diff --git a/Content.Client/Effects/ColorFlashEffectSystem.cs b/Content.Client/Effects/ColorFlashEffectSystem.cs new file mode 100644 index 0000000000..b14c11a39e --- /dev/null +++ b/Content.Client/Effects/ColorFlashEffectSystem.cs @@ -0,0 +1,107 @@ +using Content.Shared.Effects; +using Robust.Client.Animations; +using Robust.Client.GameObjects; +using Robust.Shared.Animations; + +namespace Content.Client.Effects; + +public sealed class ColorFlashEffectSystem : EntitySystem +{ + [Dependency] private readonly AnimationPlayerSystem _animation = default!; + + /// + /// It's a little on the long side but given we use multiple colours denoting what happened it makes it easier to register. + /// + private const float AnimationLength = 0.30f; + private const string AnimationKey = "color-flash-effect"; + + public override void Initialize() + { + base.Initialize(); + + SubscribeAllEvent(OnColorFlashEffect); + SubscribeLocalEvent(OnEffectAnimationCompleted); + } + + private void OnEffectAnimationCompleted(EntityUid uid, ColorFlashEffectComponent component, AnimationCompletedEvent args) + { + if (args.Key != AnimationKey) + return; + + if (TryComp(uid, out var sprite)) + { + sprite.Color = component.Color; + } + + RemCompDeferred(uid); + } + + private Animation? GetDamageAnimation(EntityUid uid, Color color, SpriteComponent? sprite = null) + { + if (!Resolve(uid, ref sprite, false)) + return null; + + // 90% of them are going to be this so why allocate a new class. + return new Animation + { + Length = TimeSpan.FromSeconds(AnimationLength), + AnimationTracks = + { + new AnimationTrackComponentProperty + { + ComponentType = typeof(SpriteComponent), + Property = nameof(SpriteComponent.Color), + InterpolationMode = AnimationInterpolationMode.Linear, + KeyFrames = + { + new AnimationTrackProperty.KeyFrame(color, 0f), + new AnimationTrackProperty.KeyFrame(sprite.Color, AnimationLength) + } + } + } + }; + } + + private void OnColorFlashEffect(ColorFlashEffectEvent ev) + { + var color = ev.Color; + + foreach (var ent in ev.Entities) + { + if (Deleted(ent)) + { + continue; + } + + var player = EnsureComp(ent); + player.NetSyncEnabled = false; + + // Need to stop the existing animation first to ensure the sprite color is fixed. + // Otherwise we might lerp to a red colour instead. + if (_animation.HasRunningAnimation(ent, player, AnimationKey)) + { + _animation.Stop(ent, player, AnimationKey); + } + + if (!TryComp(ent, out var sprite)) + { + continue; + } + + if (TryComp(ent, out var effect)) + { + sprite.Color = effect.Color; + } + + var animation = GetDamageAnimation(ent, color, sprite); + + if (animation == null) + continue; + + var comp = EnsureComp(ent); + comp.NetSyncEnabled = false; + comp.Color = sprite.Color; + _animation.Play(player, animation, AnimationKey); + } + } +} diff --git a/Content.Client/Replay/ContentReplayPlaybackManager.cs b/Content.Client/Replay/ContentReplayPlaybackManager.cs index 55491429ea..a96752383a 100644 --- a/Content.Client/Replay/ContentReplayPlaybackManager.cs +++ b/Content.Client/Replay/ContentReplayPlaybackManager.cs @@ -5,13 +5,13 @@ using Content.Client.Replay.Spectator; using Content.Client.Replay.UI.Loading; using Content.Client.UserInterface.Systems.Chat; using Content.Shared.Chat; +using Content.Shared.Effects; using Content.Shared.GameTicking; using Content.Shared.GameWindow; using Content.Shared.Hands; using Content.Shared.Instruments; using Content.Shared.Popups; using Content.Shared.Projectiles; -using Content.Shared.Weapons.Melee; using Content.Shared.Weapons.Melee.Events; using Content.Shared.Weapons.Ranged.Events; using Content.Shared.Weapons.Ranged.Systems; @@ -24,9 +24,7 @@ using Robust.Client.Replays.Playback; using Robust.Client.State; using Robust.Client.Timing; using Robust.Client.UserInterface; -using Robust.Shared.ContentPack; using Robust.Shared.Serialization.Markdown.Mapping; -using Robust.Shared.Utility; namespace Content.Client.Replay; @@ -66,7 +64,7 @@ public sealed class ContentReplayPlaybackManager private void LoadOverride(IReplayFileReader fileReader) { var screen = _stateMan.RequestStateChange>(); - screen.Job = new ContentLoadReplayJob(1/60f, fileReader, _loadMan, screen); + screen.Job = new ContentLoadReplayJob(1 / 60f, fileReader, _loadMan, screen); screen.OnJobFinished += (_, e) => OnFinishedLoading(e); } @@ -141,7 +139,7 @@ public sealed class ContentReplayPlaybackManager case SharedGunSystem.HitscanEvent: case ImpactEffectEvent: case MuzzleFlashEvent: - case DamageEffectEvent: + case ColorFlashEffectEvent: case InstrumentStartMidiEvent: case InstrumentMidiEventEvent: case InstrumentStopMidiEvent: @@ -159,7 +157,7 @@ public sealed class ContentReplayPlaybackManager private void OnReplayPlaybackStopped() { - _conGrp.Implementation = (IClientConGroupImplementation)_adminMan; + _conGrp.Implementation = (IClientConGroupImplementation) _adminMan; ReturnToDefaultState(); } } diff --git a/Content.Client/Weapons/Melee/MeleeWeaponSystem.Effects.cs b/Content.Client/Weapons/Melee/MeleeWeaponSystem.Effects.cs index f841c9bff5..7dce555817 100644 --- a/Content.Client/Weapons/Melee/MeleeWeaponSystem.Effects.cs +++ b/Content.Client/Weapons/Melee/MeleeWeaponSystem.Effects.cs @@ -1,7 +1,5 @@ using System.Numerics; using Content.Client.Weapons.Melee.Components; -using Content.Shared.Weapons; -using Content.Shared.Weapons.Melee; using Robust.Client.Animations; using Robust.Client.GameObjects; using Robust.Shared.Animations; @@ -11,106 +9,10 @@ namespace Content.Client.Weapons.Melee; public sealed partial class MeleeWeaponSystem { - /// - /// It's a little on the long side but given we use multiple colours denoting what happened it makes it easier to register. - /// - private const float DamageAnimationLength = 0.30f; - - private const string DamageAnimationKey = "damage-effect"; private const string FadeAnimationKey = "melee-fade"; private const string SlashAnimationKey = "melee-slash"; private const string ThrustAnimationKey = "melee-thrust"; - private void InitializeEffect() - { - SubscribeLocalEvent(OnEffectAnimation); - } - - private void OnEffectAnimation(EntityUid uid, DamageEffectComponent component, AnimationCompletedEvent args) - { - if (args.Key != DamageAnimationKey) - return; - - if (TryComp(uid, out var sprite)) - { - sprite.Color = component.Color; - } - - RemCompDeferred(uid); - } - - /// - /// Gets the red effect animation whenever the server confirms something is hit - /// - private Animation? GetDamageAnimation(EntityUid uid, Color color, SpriteComponent? sprite = null) - { - if (!Resolve(uid, ref sprite, false)) - return null; - - // 90% of them are going to be this so why allocate a new class. - return new Animation - { - Length = TimeSpan.FromSeconds(DamageAnimationLength), - AnimationTracks = - { - new AnimationTrackComponentProperty - { - ComponentType = typeof(SpriteComponent), - Property = nameof(SpriteComponent.Color), - InterpolationMode = AnimationInterpolationMode.Linear, - KeyFrames = - { - new AnimationTrackProperty.KeyFrame(color, 0f), - new AnimationTrackProperty.KeyFrame(sprite.Color, DamageAnimationLength) - } - } - } - }; - } - - private void OnDamageEffect(DamageEffectEvent ev) - { - var color = ev.Color; - - foreach (var ent in ev.Entities) - { - if (Deleted(ent)) - { - continue; - } - - var player = EnsureComp(ent); - player.NetSyncEnabled = false; - - // Need to stop the existing animation first to ensure the sprite color is fixed. - // Otherwise we might lerp to a red colour instead. - if (_animation.HasRunningAnimation(ent, player, DamageAnimationKey)) - { - _animation.Stop(ent, player, DamageAnimationKey); - } - - if (!TryComp(ent, out var sprite)) - { - continue; - } - - if (TryComp(ent, out var effect)) - { - sprite.Color = effect.Color; - } - - var animation = GetDamageAnimation(ent, color, sprite); - - if (animation == null) - continue; - - var comp = EnsureComp(ent); - comp.NetSyncEnabled = false; - comp.Color = sprite.Color; - _animation.Play(player, animation, DamageAnimationKey); - } - } - /// /// Does all of the melee effects for a player that are predicted, i.e. character lunge and weapon animation. /// diff --git a/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs b/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs index 573c6c7265..d77537c051 100644 --- a/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs +++ b/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs @@ -1,6 +1,7 @@ using System.Linq; using Content.Client.Gameplay; using Content.Shared.CombatMode; +using Content.Shared.Effects; using Content.Shared.Hands.Components; using Content.Shared.Mobs.Components; using Content.Shared.StatusEffect; @@ -37,9 +38,7 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem public override void Initialize() { base.Initialize(); - InitializeEffect(); _overlayManager.AddOverlay(new MeleeWindupOverlay(EntityManager, _timing, _player, _protoManager)); - SubscribeAllEvent(OnDamageEffect); SubscribeNetworkEvent(OnMeleeLunge); UpdatesOutsidePrediction = true; } @@ -227,7 +226,7 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem { // Server never sends the event to us for predictiveeevent. if (_timing.IsFirstTimePredicted) - RaiseLocalEvent(new DamageEffectEvent(Color.Red, targets)); + RaiseLocalEvent(new ColorFlashEffectEvent(Color.Red, targets)); } protected override bool DoDisarm(EntityUid user, DisarmAttackEvent ev, EntityUid meleeUid, MeleeWeaponComponent component, ICommonSession? session) diff --git a/Content.Server/Projectiles/ProjectileSystem.cs b/Content.Server/Projectiles/ProjectileSystem.cs index 5529a5f4f6..e8a7bc0e45 100644 --- a/Content.Server/Projectiles/ProjectileSystem.cs +++ b/Content.Server/Projectiles/ProjectileSystem.cs @@ -5,12 +5,10 @@ using Content.Shared.Damage; using Content.Shared.Database; using Content.Shared.FixedPoint; using Content.Shared.Projectiles; -using Content.Shared.Weapons.Melee; -using JetBrains.Annotations; using Robust.Server.GameObjects; -using Robust.Shared.GameStates; using Robust.Shared.Player; using Robust.Shared.Physics.Events; +using Content.Shared.Effects; namespace Content.Server.Projectiles; @@ -52,7 +50,7 @@ public sealed class ProjectileSystem : SharedProjectileSystem { if (modifiedDamage.Total > FixedPoint2.Zero && !deleted) { - RaiseNetworkEvent(new DamageEffectEvent(Color.Red, new List {otherEntity}), Filter.Pvs(otherEntity, entityManager: EntityManager)); + RaiseNetworkEvent(new ColorFlashEffectEvent(Color.Red, new List { otherEntity }), Filter.Pvs(otherEntity, entityManager: EntityManager)); } _adminLogger.Add(LogType.BulletHit, diff --git a/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs b/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs index bb0442b671..2e3104eea2 100644 --- a/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs +++ b/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs @@ -9,11 +9,9 @@ using Content.Server.CombatMode.Disarm; using Content.Server.Contests; using Content.Server.Examine; using Content.Server.Movement.Systems; -using Content.Server.Popups; using Content.Shared.Administration.Components; using Content.Shared.Actions.Events; using Content.Shared.CombatMode; -using Content.Shared.Damage; using Content.Shared.Database; using Content.Shared.FixedPoint; using Content.Shared.Hands.Components; @@ -33,6 +31,7 @@ using Robust.Shared.Player; using Robust.Shared.Players; using Robust.Shared.Random; using Robust.Shared.Utility; +using Content.Shared.Effects; namespace Content.Server.Weapons.Melee; @@ -193,7 +192,7 @@ public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem var eventArgs = new DisarmedEvent { Target = target, Source = user, PushProbability = 1 - chance }; RaiseLocalEvent(target, eventArgs); - RaiseNetworkEvent(new DamageEffectEvent(Color.Aqua, new List() { target })); + RaiseNetworkEvent(new ColorFlashEffectEvent(Color.Aqua, new List() { target })); return true; } @@ -219,7 +218,7 @@ public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem protected override void DoDamageEffect(List targets, EntityUid? user, TransformComponent targetXform) { var filter = Filter.Pvs(targetXform.Coordinates, entityMan: EntityManager).RemoveWhereAttachedEntity(o => o == user); - RaiseNetworkEvent(new DamageEffectEvent(Color.Red, targets), filter); + RaiseNetworkEvent(new ColorFlashEffectEvent(Color.Red, targets), filter); } private float CalculateDisarmChance(EntityUid disarmer, EntityUid disarmed, EntityUid? inTargetHand, CombatModeComponent disarmerComp) diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs index 90f67c2890..be24ef4eee 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs @@ -10,6 +10,7 @@ using Content.Server.Weapons.Ranged.Components; using Content.Shared.Damage; using Content.Shared.Damage.Systems; using Content.Shared.Database; +using Content.Shared.Effects; using Content.Shared.FixedPoint; using Content.Shared.Interaction.Components; using Content.Shared.Projectiles; @@ -225,7 +226,7 @@ public sealed partial class GunSystem : SharedGunSystem { var hitEntity = lastHit.Value; if (hitscan.StaminaDamage > 0f) - _stamina.TakeStaminaDamage(hitEntity, hitscan.StaminaDamage, source:user); + _stamina.TakeStaminaDamage(hitEntity, hitscan.StaminaDamage, source: user); var dmg = hitscan.Damage; @@ -239,7 +240,7 @@ public sealed partial class GunSystem : SharedGunSystem if (!Deleted(hitEntity)) { if (dmg.Total > FixedPoint2.Zero) - RaiseNetworkEvent(new DamageEffectEvent(Color.Red, new List {hitEntity}), Filter.Pvs(hitEntity, entityManager: EntityManager)); + RaiseNetworkEvent(new ColorFlashEffectEvent(Color.Red, new List { hitEntity }), Filter.Pvs(hitEntity, entityManager: EntityManager)); // TODO get fallback position for playing hit sound. PlayImpactSound(hitEntity, dmg, hitscan.Sound, hitscan.ForceSound); @@ -343,7 +344,7 @@ public sealed partial class GunSystem : SharedGunSystem return angle; } - protected override void Popup(string message, EntityUid? uid, EntityUid? user) {} + protected override void Popup(string message, EntityUid? uid, EntityUid? user) { } protected override void CreateEffect(EntityUid uid, MuzzleFlashEvent message, EntityUid? user = null) { diff --git a/Content.Shared/Cuffs/SharedCuffableSystem.cs b/Content.Shared/Cuffs/SharedCuffableSystem.cs index 470256c9a6..8de24c3c1f 100644 --- a/Content.Shared/Cuffs/SharedCuffableSystem.cs +++ b/Content.Shared/Cuffs/SharedCuffableSystem.cs @@ -8,6 +8,7 @@ using Content.Shared.Cuffs.Components; using Content.Shared.Damage; using Content.Shared.Database; using Content.Shared.DoAfter; +using Content.Shared.Effects; using Content.Shared.Hands; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; @@ -26,7 +27,6 @@ using Content.Shared.Pulling.Events; using Content.Shared.Rejuvenate; using Content.Shared.Stunnable; using Content.Shared.Verbs; -using Content.Shared.Weapons.Melee; using Content.Shared.Weapons.Melee.Events; using Robust.Shared.Containers; using Robust.Shared.Network; @@ -592,7 +592,7 @@ namespace Content.Shared.Cuffs if (target == user) { - RaiseNetworkEvent(new DamageEffectEvent(Color.Red, new List() { user })); + RaiseNetworkEvent(new ColorFlashEffectEvent(Color.Red, new List() { user })); _popup.PopupEntity(Loc.GetString("cuffable-component-start-uncuffing-self"), user, user); } else diff --git a/Content.Shared/Effects/ColorFlashEffectComponent.cs b/Content.Shared/Effects/ColorFlashEffectComponent.cs new file mode 100644 index 0000000000..3a9895e9c0 --- /dev/null +++ b/Content.Shared/Effects/ColorFlashEffectComponent.cs @@ -0,0 +1,11 @@ +namespace Content.Shared.Effects; + +/// +/// Stores the original sprite color for flashing entity to be able to restore it later. +/// +[RegisterComponent] +public sealed class ColorFlashEffectComponent : Component +{ + [ViewVariables] + public Color Color = Color.White; +} diff --git a/Content.Shared/Effects/ColorFlashEffectEvent.cs b/Content.Shared/Effects/ColorFlashEffectEvent.cs new file mode 100644 index 0000000000..7dca9c5f41 --- /dev/null +++ b/Content.Shared/Effects/ColorFlashEffectEvent.cs @@ -0,0 +1,23 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared.Effects; + +/// +/// Raised on the server and sent to a client to play the color flash animation. +/// +[Serializable, NetSerializable] +public sealed class ColorFlashEffectEvent : EntityEventArgs +{ + /// + /// Color to play for the flash. + /// + public Color Color; + + public List Entities; + + public ColorFlashEffectEvent(Color color, List entities) + { + Color = color; + Entities = entities; + } +} \ No newline at end of file diff --git a/Content.Shared/Weapons/DamageEffectComponent.cs b/Content.Shared/Weapons/DamageEffectComponent.cs deleted file mode 100644 index 8574e30065..0000000000 --- a/Content.Shared/Weapons/DamageEffectComponent.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Content.Shared.Weapons; - -/// -/// Stores the original sprite color for a damaged entity to be able to restore it later. -/// -[RegisterComponent] -public sealed class DamageEffectComponent : Component -{ - [ViewVariables] - public Color Color = Color.White; -} diff --git a/Content.Shared/Weapons/Melee/DamageEffectEvent.cs b/Content.Shared/Weapons/Melee/DamageEffectEvent.cs deleted file mode 100644 index ed5eb15a02..0000000000 --- a/Content.Shared/Weapons/Melee/DamageEffectEvent.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Robust.Shared.Serialization; - -namespace Content.Shared.Weapons.Melee; - -/// -/// Raised on the server and sent to a client to play the damage animation. -/// -[Serializable, NetSerializable] -public sealed class DamageEffectEvent : EntityEventArgs -{ - /// - /// Color to play for the damage flash. - /// - public Color Color; - - public List Entities; - - public DamageEffectEvent(Color color, List entities) - { - Color = color; - Entities = entities; - } -}