Пример #1
0
        public void CreateEffect(EffectSystemMessage message)
        {
            if (message.AttachedEntityUid != null && message.Coordinates != default)
            {
                Logger.Warning("Set both an AttachedEntityUid and EntityCoordinates on an EffectSystemMessage for sprite {0} which is not supported!", message.EffectSprite);
            }

            var gameTime = gameTiming.CurTime;

            if (gameTime > message.DeathTime) //Did we already die in transit? That's pretty troubling isn't it
            {
                Logger.Warning("Effect using sprite {0} died in transit to the client", message.EffectSprite);
                return;
            }

            //Create effect from creation message
            var effect = new Effect(message, resourceCache, _mapManager, _entityManager);

            if (effect.AttachedEntityUid != null)
            {
                effect.AttachedEntity = _entityManager.GetEntity(effect.AttachedEntityUid.Value);
            }

            //Age the effect through a single update to the previous update tick of the effect system
            //effect.Update((float)((lasttimeprocessed - effect.Age).TotalSeconds));

            _Effects.Add(effect);
        }
    protected void MuzzleFlash(EntityUid gun, AmmoComponent component, EntityUid?user = null)
    {
        var sprite = component.MuzzleFlash?.ToString();

        // TODO: AAAAA THIS MUZZLE FLASH CODE IS BAD
        // NEEDS EFFECTS TO NOT BE BAD!
        if (sprite == null)
        {
            return;
        }

        var time      = Timing.CurTime;
        var deathTime = time + TimeSpan.FromSeconds(MuzzleFlashLifetime);
        // Offset the sprite so it actually looks like it's coming from the gun
        var offset = new Vector2(0.0f, -0.5f);

        var message = new EffectSystemMessage
        {
            EffectSprite      = sprite,
            Born              = time,
            DeathTime         = deathTime,
            AttachedEntityUid = gun,
            AttachedOffset    = offset,
            //Rotated from east facing
            Rotation   = -MathF.PI / 2f,
            Color      = Vector4.Multiply(new Vector4(255, 255, 255, 255), 1.0f),
            ColorDelta = new Vector4(0, 0, 0, -1500f),
            Shaded     = false
        };

        CreateEffect(message, user);
    }
Пример #3
0
        public void MuzzleFlash(EntityUid entity, Angle angle)
        {
            if (_muzzleFlashSprite == null)
            {
                return;
            }

            var time      = _gameTiming.CurTime;
            var deathTime = time + TimeSpan.FromMilliseconds(200);
            // Offset the sprite so it actually looks like it's coming from the gun
            var offset = angle.ToVec().Normalized / 2;

            var message = new EffectSystemMessage
            {
                EffectSprite      = _muzzleFlashSprite,
                Born              = time,
                DeathTime         = deathTime,
                AttachedEntityUid = entity,
                AttachedOffset    = offset,
                //Rotated from east facing
                Rotation   = (float)angle.Theta,
                Color      = Vector4.Multiply(new Vector4(255, 255, 255, 255), 1.0f),
                ColorDelta = new Vector4(0, 0, 0, -1500f),
                Shaded     = false
            };

            EntitySystem.Get <EffectSystem>().CreateParticle(message);
        }
Пример #4
0
        public void CreateEffect(EffectSystemMessage message)
        {
            var gametime = gameTiming.CurTime;

            /*
             * // TODO: Fix this, it doesn't work. Probably because CurTime isn't synchronized with the server.
             * if (gametime > message.DeathTime) //Did we already die in transit? That's pretty troubling isn't it
             * {
             *  Logger.Warning(string.Format("Effect using sprite {0} died in transit to the client", message.EffectSprite), message);
             *  return;
             * }
             */

            //Create effect from creation message
            var effect = new Effect(message, resourceCache);

            //Remove this
            effect.Age       = gametime;
            effect.Deathtime = effect.Age + TimeSpan.FromSeconds(4);

            //Age the effect through a single update to the previous update tick of the effect system
            //effect.Update((float)((lasttimeprocessed - effect.Age).TotalSeconds));

            _Effects.Add(effect);
        }
Пример #5
0
        public void CreateParticle(EffectSystemMessage effect)
        {
            _CurrentEffects.Add(effect);

            //For now we will use this which sends to ALL clients
            //TODO: Client bubbling
            RaiseNetworkEvent(effect);
        }
Пример #6
0
 protected override void CreateEffect(EffectSystemMessage message, EntityUid?user = null)
 {
     // TODO: F*****g bad
     if (TryComp <ActorComponent>(user, out var actor))
     {
         _effects.CreateParticle(message, actor.PlayerSession);
     }
     else
     {
         _effects.CreateParticle(message);
     }
 }
Пример #7
0
    public void MuzzleFlash(EntityUid entity, AmmoComponent component, Angle angle)
    {
        if (component.MuzzleFlashSprite == null)
        {
            return;
        }

        var time      = _gameTiming.CurTime;
        var deathTime = time + TimeSpan.FromMilliseconds(200);
        // Offset the sprite so it actually looks like it's coming from the gun
        var offset = new Vector2(0.0f, -0.5f);

        var message = new EffectSystemMessage
        {
            EffectSprite      = component.MuzzleFlashSprite.ToString(),
            Born              = time,
            DeathTime         = deathTime,
            AttachedEntityUid = entity,
            AttachedOffset    = offset,
            //Rotated from east facing
            Rotation   = -MathF.PI / 2f,
            Color      = Vector4.Multiply(new Vector4(255, 255, 255, 255), 1.0f),
            ColorDelta = new Vector4(0, 0, 0, -1500f),
            Shaded     = false
        };

        /* TODO: Fix rotation when shooting sideways. This was the closest I got but still had issues.
         * var time = _gameTiming.CurTime;
         * var deathTime = time + TimeSpan.FromMilliseconds(200);
         * var entityRotation = EntityManager.GetComponent<TransformComponent>(entity).WorldRotation;
         * var localAngle = entityRotation - (angle + MathF.PI / 2f);
         * // Offset the sprite so it actually looks like it's coming from the gun
         * var offset = localAngle.RotateVec(new Vector2(0.0f, -0.5f));
         *
         * var message = new EffectSystemMessage
         * {
         *  EffectSprite = component.MuzzleFlashSprite.ToString(),
         *  Born = time,
         *  DeathTime = deathTime,
         *  AttachedEntityUid = entity,
         *  AttachedOffset = offset,
         *  //Rotated from east facing
         *  Rotation = (float) (localAngle - MathF.PI / 2),
         *  Color = Vector4.Multiply(new Vector4(255, 255, 255, 255), 1.0f),
         *  ColorDelta = new Vector4(0, 0, 0, -1500f),
         *  Shaded = false
         * };
         */

        _effects.CreateParticle(message);
    }
Пример #8
0
        /// <summary>
        ///     Creates a particle effect and sends it to clients.
        /// </summary>
        /// <param name="effect"></param>
        /// <param name="excludedSession">Session to be excluded for prediction</param>
        public void CreateParticle(EffectSystemMessage effect, IPlayerSession?excludedSession = null)
        {
            _CurrentEffects.Add(effect);

            //For now we will use this which sends to ALL clients
            //TODO: Client bubbling
            foreach (var player in _playerManager.GetAllPlayers())
            {
                if (player.Status != SessionStatus.InGame || player == excludedSession)
                {
                    continue;
                }

                RaiseNetworkEvent(effect, player.ConnectedClient);
            }
        }
Пример #9
0
        public void CreateEffect(EffectSystemMessage message)
        {
            var gameTime = gameTiming.CurTime;

            if (gameTime > message.DeathTime) //Did we already die in transit? That's pretty troubling isn't it
            {
                Logger.Warning("Effect using sprite {0} died in transit to the client", message.EffectSprite);
                return;
            }

            //Create effect from creation message
            var effect = new Effect(message, resourceCache, _mapManager);

            //Age the effect through a single update to the previous update tick of the effect system
            //effect.Update((float)((lasttimeprocessed - effect.Age).TotalSeconds));

            _Effects.Add(effect);
        }
Пример #10
0
        private EffectSystemMessage AfterEffects(EntityCoordinates origin, Angle angle, float distance, float offset = 0.0f)
        {
            var midPointOffset = angle.ToVec() * distance / 2;
            var message        = new EffectSystemMessage
            {
                EffectSprite = _spriteName,
                Born         = _startTime,
                DeathTime    = _deathTime,
                Size         = new Vector2(distance - offset, 1f),
                Coordinates  = origin.Offset(midPointOffset),
                //Rotated from east facing
                Rotation   = (float)angle.Theta,
                Color      = Vector4.Multiply(new Vector4(255, 255, 255, 750), ColorModifier),
                ColorDelta = new Vector4(0, 0, 0, -1500f),

                Shaded = false
            };

            return(message);
        }
        protected virtual void AfterEffects(IEntity user, RayCastResults ray, Angle angle)
        {
            var time   = IoCManager.Resolve <IGameTiming>().CurTime;
            var offset = angle.ToVec() * ray.Distance / 2;

            EffectSystemMessage message = new EffectSystemMessage
            {
                EffectSprite = spritename,
                Born         = time,
                DeathTime    = time + TimeSpan.FromSeconds(1),
                Size         = new Vector2(ray.Distance, 1f),
                Coordinates  = user.GetComponent <TransformComponent>().LocalPosition.Translated(offset),
                //Rotated from east facing
                Rotation   = (float)angle.Theta,
                ColorDelta = new Vector4(0, 0, 0, -1500f),
                Color      = new Vector4(255, 255, 255, 750),
                Shaded     = false
            };

            IoCManager.Resolve <IEntitySystemManager>().GetEntitySystem <EffectSystem>().CreateParticle(message);
        }
Пример #12
0
 public Effect(EffectSystemMessage effectcreation, IResourceCache resourceCache)
 {
     EffectSprite           = resourceCache.GetResource <TextureResource>(new ResourcePath("/Textures/") / effectcreation.EffectSprite).Texture;
     Coordinates            = effectcreation.Coordinates;
     EmitterCoordinates     = effectcreation.EmitterCoordinates;
     Velocity               = effectcreation.Velocity;
     Acceleration           = effectcreation.Acceleration;
     RadialVelocity         = effectcreation.RadialVelocity;
     RadialAcceleration     = effectcreation.RadialAcceleration;
     TangentialVelocity     = effectcreation.TangentialVelocity;
     TangentialAcceleration = effectcreation.TangentialAcceleration;
     Age          = effectcreation.Born;
     Deathtime    = effectcreation.DeathTime;
     Rotation     = effectcreation.Rotation;
     RotationRate = effectcreation.RotationRate;
     Size         = effectcreation.Size;
     SizeDelta    = effectcreation.SizeDelta;
     Color        = effectcreation.Color;
     ColorDelta   = effectcreation.ColorDelta;
     Shaded       = effectcreation.Shaded;
 }
Пример #13
0
            public Effect(EffectSystemMessage effectcreation, IResourceCache resourceCache, IMapManager mapManager, IEntityManager entityManager)
            {
                if (effectcreation.RsiState != null)
                {
                    var rsi = resourceCache
                              .GetResource <RSIResource>(new ResourcePath("/Textures/") / effectcreation.EffectSprite)
                              .RSI;
                    RsiState     = rsi[effectcreation.RsiState];
                    EffectSprite = RsiState.Frame0;
                }
                else
                {
                    EffectSprite = resourceCache
                                   .GetResource <TextureResource>(new ResourcePath("/Textures/") / effectcreation.EffectSprite)
                                   .Texture;
                }

                AnimationLoops         = effectcreation.AnimationLoops;
                AttachedEntityUid      = effectcreation.AttachedEntityUid;
                AttachedOffset         = effectcreation.AttachedOffset;
                Coordinates            = effectcreation.Coordinates;
                EmitterCoordinates     = effectcreation.EmitterCoordinates;
                Velocity               = effectcreation.Velocity;
                Acceleration           = effectcreation.Acceleration;
                RadialVelocity         = effectcreation.RadialVelocity;
                RadialAcceleration     = effectcreation.RadialAcceleration;
                TangentialVelocity     = effectcreation.TangentialVelocity;
                TangentialAcceleration = effectcreation.TangentialAcceleration;
                Age            = effectcreation.Born;
                Deathtime      = effectcreation.DeathTime;
                Rotation       = effectcreation.Rotation;
                RotationRate   = effectcreation.RotationRate;
                Size           = effectcreation.Size;
                SizeDelta      = effectcreation.SizeDelta;
                Color          = effectcreation.Color;
                ColorDelta     = effectcreation.ColorDelta;
                Shaded         = effectcreation.Shaded;
                _mapManager    = mapManager;
                _entityManager = entityManager;
            }
Пример #14
0
        private EffectSystemMessage?ImpactFlash(float distance, Angle angle)
        {
            if (_impactFlash == null)
            {
                return(null);
            }

            var message = new EffectSystemMessage
            {
                EffectSprite = _impactFlash,
                Born         = _startTime,
                DeathTime    = _deathTime,
                Coordinates  = Owner.Transform.Coordinates.Offset(angle.ToVec() * distance),
                //Rotated from east facing
                Rotation   = (float)angle.FlipPositive(),
                Color      = Vector4.Multiply(new Vector4(255, 255, 255, 750), ColorModifier),
                ColorDelta = new Vector4(0, 0, 0, -1500f),
                Shaded     = false
            };

            return(message);
        }
Пример #15
0
        protected virtual void AfterEffects(IEntity user, float distance, Angle angle, float energyModifier)
        {
            var time    = IoCManager.Resolve <IGameTiming>().CurTime;
            var offset  = angle.ToVec() * distance / 2;
            var message = new EffectSystemMessage
            {
                EffectSprite = _spritename,
                Born         = time,
                DeathTime    = time + TimeSpan.FromSeconds(1),
                Size         = new Vector2(distance, 1f),
                Coordinates  = user.Transform.GridPosition.Translated(offset),
                //Rotated from east facing
                Rotation   = (float)angle.Theta,
                ColorDelta = new Vector4(0, 0, 0, -1500f),
                Color      = Vector4.Multiply(new Vector4(255, 255, 255, 750), energyModifier),

                Shaded = false
            };

            EntitySystem.Get <EffectSystem>().CreateParticle(message);
            EntitySystem.Get <AudioSystem>().PlayFromEntity(_fireSound, Owner, AudioParams.Default.WithVolume(-5));
        }
        protected virtual void AfterEffects(IEntity user, RayCastResults ray, Angle angle)
        {
            var time    = IoCManager.Resolve <IGameTiming>().CurTime;
            var dist    = ray.DidHitObject ? ray.Distance : MaxLength;
            var offset  = angle.ToVec() * dist / 2;
            var message = new EffectSystemMessage
            {
                EffectSprite = Spritename,
                Born         = time,
                DeathTime    = time + TimeSpan.FromSeconds(1),
                Size         = new Vector2(dist, 1f),
                Coordinates  = user.Transform.GridPosition.Translated(offset),
                //Rotated from east facing
                Rotation   = (float)angle.Theta,
                ColorDelta = new Vector4(0, 0, 0, -1500f),
                Color      = new Vector4(255, 255, 255, 750),
                Shaded     = false
            };
            var mgr = IoCManager.Resolve <IEntitySystemManager>();

            mgr.GetEntitySystem <EffectSystem>().CreateParticle(message);
            mgr.GetEntitySystem <AudioSystem>().Play("/Audio/laser.ogg", Owner, AudioParams.Default.WithVolume(-5));
        }
Пример #17
0
        private EffectSystemMessage?MuzzleFlash(EntityCoordinates grid, Angle angle)
        {
            if (_muzzleFlash == null)
            {
                return(null);
            }

            var offset = angle.ToVec().Normalized / 2;

            var message = new EffectSystemMessage
            {
                EffectSprite = _muzzleFlash,
                Born         = _startTime,
                DeathTime    = _deathTime,
                Coordinates  = grid.Offset(offset),
                //Rotated from east facing
                Rotation   = (float)angle.Theta,
                Color      = Vector4.Multiply(new Vector4(255, 255, 255, 750), ColorModifier),
                ColorDelta = new Vector4(0, 0, 0, -1500f),
                Shaded     = false
            };

            return(message);
        }
        private bool Explosion()
        {
            var maxRange = MathHelper.Max(DevastationRange, HeavyImpactRange, LightImpactRange, 0f);
            //Entity damage calculation
            var entitiesAll = _serverEntityManager.GetEntitiesInRange(Owner.Transform.GridPosition, maxRange).ToList();

            foreach (var entity in entitiesAll)
            {
                Owner.Delete();
                if (entity == Owner)
                {
                    continue;
                }
                if (!entity.Transform.IsMapTransform)
                {
                    continue;
                }
                var distanceFromEntity = (int)entity.Transform.GridPosition.Distance(_mapManager, Owner.Transform.GridPosition);
                var exAct    = _entitySystemManager.GetEntitySystem <ActSystem>();
                var severity = ExplosionSeverity.Destruction;
                if (distanceFromEntity < DevastationRange)
                {
                    severity = ExplosionSeverity.Destruction;
                }
                else if (distanceFromEntity < HeavyImpactRange)
                {
                    severity = ExplosionSeverity.Heavy;
                }
                else if (distanceFromEntity < LightImpactRange)
                {
                    severity = ExplosionSeverity.Light;
                }
                else
                {
                    continue;
                }
                exAct.HandleExplosion(Owner, entity, severity);
            }

            //Tile damage calculation mockup
            //TODO: make it into some sort of actual damage component or whatever the boys think is appropriate
            var mapGrid = _mapManager.GetGrid(Owner.Transform.GridPosition.GridID);
            var circle  = new Circle(Owner.Transform.GridPosition.Position, maxRange);
            var tiles   = mapGrid.GetTilesIntersecting(circle);

            foreach (var tile in tiles)
            {
                var tileLoc          = mapGrid.GridTileToLocal(tile.GridIndices);
                var tileDef          = (ContentTileDefinition)_tileDefinitionManager[tile.Tile.TypeId];
                var distanceFromTile = (int)tileLoc.Distance(_mapManager, Owner.Transform.GridPosition);
                if (!string.IsNullOrWhiteSpace(tileDef.SubFloor))
                {
                    if (distanceFromTile < DevastationRange)
                    {
                        mapGrid.SetTile(tileLoc, new Tile(_tileDefinitionManager["space"].TileId));
                    }
                    if (distanceFromTile < HeavyImpactRange)
                    {
                        if (new Random().Prob(80))
                        {
                            mapGrid.SetTile(tileLoc, new Tile(_tileDefinitionManager[tileDef.SubFloor].TileId));
                        }
                        else
                        {
                            mapGrid.SetTile(tileLoc, new Tile(_tileDefinitionManager["space"].TileId));
                        }
                    }
                    if (distanceFromTile < LightImpactRange)
                    {
                        if (new Random().Prob(50))
                        {
                            mapGrid.SetTile(tileLoc, new Tile(_tileDefinitionManager[tileDef.SubFloor].TileId));
                        }
                    }
                }
            }

            //Effects and sounds
            var time    = IoCManager.Resolve <IGameTiming>().CurTime;
            var message = new EffectSystemMessage
            {
                EffectSprite = "Effects/explosion.rsi",
                RsiState     = "explosionfast",
                Born         = time,
                DeathTime    = time + TimeSpan.FromSeconds(5),
                Size         = new Vector2(FlashRange / 2, FlashRange / 2),
                Coordinates  = Owner.Transform.GridPosition,
                //Rotated from east facing
                Rotation   = 0f,
                ColorDelta = new Vector4(0, 0, 0, -1500f),
                Color      = Vector4.Multiply(new Vector4(255, 255, 255, 750), 0.5f),
                Shaded     = false
            };

            _entitySystemManager.GetEntitySystem <EffectSystem>().CreateParticle(message);
            _entitySystemManager.GetEntitySystem <AudioSystem>().Play("/Audio/effects/explosion.ogg", Owner);

            // Knock back cameras of all players in the area.

            var playerManager = IoCManager.Resolve <IPlayerManager>();
            var selfPos       = Owner.Transform.WorldPosition;

            foreach (var player in playerManager.GetAllPlayers())
            {
                if (player.AttachedEntity == null ||
                    player.AttachedEntity.Transform.MapID != mapGrid.ParentMapId ||
                    !player.AttachedEntity.TryGetComponent(out CameraRecoilComponent recoil))
                {
                    continue;
                }

                var playerPos = player.AttachedEntity.Transform.WorldPosition;
                var delta     = selfPos - playerPos;
                var distance  = delta.LengthSquared;

                var effect = 1 / (1 + 0.2f * distance);
                if (effect > 0.01f)
                {
                    var kick = -delta.Normalized * effect;
                    recoil.Kick(kick);
                }
            }

            return(true);
        }
 protected abstract void CreateEffect(EffectSystemMessage message, EntityUid?user = null);
        private void PlayWeaponArc(PlayMeleeWeaponAnimationMessage msg)
        {
            if (!_prototypeManager.TryIndex(msg.ArcPrototype, out MeleeWeaponAnimationPrototype? weaponArc))
            {
                Logger.Error("Tried to play unknown weapon arc prototype '{0}'", msg.ArcPrototype);
                return;
            }

            var attacker = msg.Attacker;

            if (!EntityManager.EntityExists(msg.Attacker))
            {
                // FIXME: This should never happen.
                Logger.Error($"Tried to play a weapon arc {msg.ArcPrototype}, but the attacker does not exist. attacker={msg.Attacker}, source={msg.Source}");
                return;
            }

            if (!Deleted(attacker))
            {
                var lunge = attacker.EnsureComponent <MeleeLungeComponent>();
                lunge.SetData(msg.Angle);

                var entity = EntityManager.SpawnEntity(weaponArc.Prototype, EntityManager.GetComponent <TransformComponent>(attacker).Coordinates);
                EntityManager.GetComponent <TransformComponent>(entity).LocalRotation = msg.Angle;

                var weaponArcAnimation = EntityManager.GetComponent <MeleeWeaponArcAnimationComponent>(entity);
                weaponArcAnimation.SetData(weaponArc, msg.Angle, attacker, msg.ArcFollowAttacker);

                // Due to ISpriteComponent limitations, weapons that don't use an RSI won't have this effect.
                if (EntityManager.EntityExists(msg.Source) &&
                    msg.TextureEffect &&
                    EntityManager.TryGetComponent(msg.Source, out ISpriteComponent? sourceSprite) &&
                    sourceSprite.BaseRSI?.Path is { } path)
                {
                    var curTime = _gameTiming.CurTime;
                    var effect  = new EffectSystemMessage
                    {
                        EffectSprite = path.ToString(),
                        RsiState     = sourceSprite.LayerGetState(0).Name,
                        Coordinates  = EntityManager.GetComponent <TransformComponent>(attacker).Coordinates,
                        Color        = Vector4.Multiply(new Vector4(255, 255, 255, 125), 1.0f),
                        ColorDelta   = Vector4.Multiply(new Vector4(0, 0, 0, -10), 1.0f),
                        Velocity     = msg.Angle.ToWorldVec(),
                        Acceleration = msg.Angle.ToWorldVec() * 5f,
                        Born         = curTime,
                        DeathTime    = curTime.Add(TimeSpan.FromMilliseconds(300f)),
                    };

                    _effectSystem.CreateEffect(effect);
                }
            }

            foreach (var hit in msg.Hits)
            {
                if (!EntityManager.EntityExists(hit))
                {
                    continue;
                }

                if (!EntityManager.TryGetComponent(hit, out ISpriteComponent? sprite))
                {
                    continue;
                }

                var originalColor = sprite.Color;
                var newColor      = Color.Red * originalColor;
                sprite.Color = newColor;

                hit.SpawnTimer(100, () =>
                {
                    // Only reset back to the original color if something else didn't change the color in the mean time.
                    if (sprite.Color == newColor)
                    {
                        sprite.Color = originalColor;
                    }
                });
            }
        }
Пример #21
0
        private void PlayWeaponArc(PlayMeleeWeaponAnimationMessage msg)
        {
            if (!_prototypeManager.TryIndex(msg.ArcPrototype, out MeleeWeaponAnimationPrototype weaponArc))
            {
                Logger.Error("Tried to play unknown weapon arc prototype '{0}'", msg.ArcPrototype);
                return;
            }

            var attacker = EntityManager.GetEntity(msg.Attacker);

            if (!attacker.Deleted)
            {
                var lunge = attacker.EnsureComponent <MeleeLungeComponent>();
                lunge.SetData(msg.Angle);

                var entity = EntityManager.SpawnEntity(weaponArc.Prototype, attacker.Transform.Coordinates);
                entity.Transform.LocalRotation = msg.Angle;

                var weaponArcAnimation = entity.GetComponent <MeleeWeaponArcAnimationComponent>();
                weaponArcAnimation.SetData(weaponArc, msg.Angle, attacker, msg.ArcFollowAttacker);

                // Due to ISpriteComponent limitations, weapons that don't use an RSI won't have this effect.
                if (EntityManager.TryGetEntity(msg.Source, out var source) && msg.TextureEffect && source.TryGetComponent(out ISpriteComponent sourceSprite) &&
                    sourceSprite.BaseRSI?.Path != null)
                {
                    var sys     = Get <EffectSystem>();
                    var curTime = _gameTiming.CurTime;
                    var effect  = new EffectSystemMessage
                    {
                        EffectSprite = sourceSprite.BaseRSI.Path.ToString(),
                        RsiState     = sourceSprite.LayerGetState(0).Name,
                        Coordinates  = attacker.Transform.Coordinates,
                        Color        = Vector4.Multiply(new Vector4(255, 255, 255, 125), 1.0f),
                        ColorDelta   = Vector4.Multiply(new Vector4(0, 0, 0, -10), 1.0f),
                        Velocity     = msg.Angle.ToVec(),
                        Acceleration = msg.Angle.ToVec() * 5f,
                        Born         = curTime,
                        DeathTime    = curTime.Add(TimeSpan.FromMilliseconds(300f)),
                    };
                    sys.CreateEffect(effect);
                }
            }

            foreach (var uid in msg.Hits)
            {
                if (!EntityManager.TryGetEntity(uid, out var hitEntity) || hitEntity.Deleted)
                {
                    continue;
                }

                if (!hitEntity.TryGetComponent(out ISpriteComponent sprite))
                {
                    continue;
                }

                var originalColor = sprite.Color;
                var newColor      = Color.Red * originalColor;
                sprite.Color = newColor;

                hitEntity.SpawnTimer(100, () =>
                {
                    // Only reset back to the original color if something else didn't change the color in the mean time.
                    if (sprite.Color == newColor)
                    {
                        sprite.Color = originalColor;
                    }
                });
            }
        }