]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Electrified grille sparks effect (#15178)
authorSlava0135 <40753025+Slava0135@users.noreply.github.com>
Sat, 29 Apr 2023 20:05:10 +0000 (23:05 +0300)
committerGitHub <noreply@github.com>
Sat, 29 Apr 2023 20:05:10 +0000 (16:05 -0400)
* use file namespace

* shorter systems name

* replace SoundSystem with AudioSystem

* refactor update function

* refactor

* refactor 2

* remove setters

* uh oh

* remove getters

* active checks

* refactor 3

* better way

* update state

* have to remove this for now

* move electrified component to shared

* forgot this

* fix airlocks

* add effect

* Revert "move electrified component to shared"

This reverts commit 6457e8fc9c3b674a705a61034831ce6f084e2b01.

* Revert "forgot this"

This reverts commit ed361cee2d5b8b958830ba0af07fcc2627eb7845.

* functioning effects

* use animation by Aleksh

* make effect part of grille

* optimisation?

* remove timing

* file name

* only activate when touched

* refactor electrocution comp too

* make it 1 sec

* formatting

* replace all entity query with enumerator

* queue del

Content.Server/Electrocution/Components/ActivatedElectrifiedComponent.cs [new file with mode: 0644]
Content.Server/Electrocution/Components/ElectrifiedComponent.cs
Content.Server/Electrocution/Components/ElectrocutionComponent.cs
Content.Server/Electrocution/ElectrocutionSystem.cs
Content.Shared/Electrocution/SharedElectrocution.cs [new file with mode: 0644]
Resources/Prototypes/Entities/Structures/Walls/grille.yml
Resources/Textures/Effects/electricity.rsi/electrified.png [new file with mode: 0644]
Resources/Textures/Effects/electricity.rsi/meta.json [new file with mode: 0644]

diff --git a/Content.Server/Electrocution/Components/ActivatedElectrifiedComponent.cs b/Content.Server/Electrocution/Components/ActivatedElectrifiedComponent.cs
new file mode 100644 (file)
index 0000000..60c3cff
--- /dev/null
@@ -0,0 +1,14 @@
+namespace Content.Server.Electrocution;
+
+/// <summary>
+/// Updates every frame for short duration to check if electrifed entity is powered when activated, e.g to play animation
+/// </summary>
+[RegisterComponent]
+public sealed class ActivatedElectrifiedComponent : Component
+{
+    /// <summary>
+    /// How long electrified entity will remain active
+    /// </summary>
+    [ViewVariables]
+    public float TimeLeft = 1f;
+}
index 0d2ed0a2445fba0dcd67afbfc20ea0e0cfb942dc..97a24ffe0c5b5108bb5c8e7762942dbb7d92af10 100644 (file)
@@ -1,77 +1,76 @@
 using Robust.Shared.Audio;
 
-namespace Content.Server.Electrocution
+namespace Content.Server.Electrocution;
+
+/// <summary>
+///     Component for things that shock users on touch.
+/// </summary>
+[RegisterComponent]
+public sealed class ElectrifiedComponent : Component
 {
-    /// <summary>
-    ///     Component for things that shock users on touch.
-    /// </summary>
-    [RegisterComponent]
-    public sealed class ElectrifiedComponent : Component
-    {
-        [DataField("enabled")]
-        public bool Enabled { get; set; } = true;
+    [DataField("enabled")]
+    public bool Enabled = true;
 
-        [DataField("onBump")]
-        public bool OnBump { get; set; } = true;
+    [DataField("onBump")]
+    public bool OnBump = true;
 
-        [DataField("onAttacked")]
-        public bool OnAttacked { get; set; } = true;
+    [DataField("onAttacked")]
+    public bool OnAttacked = true;
 
-        [DataField("noWindowInTile")]
-        public bool NoWindowInTile { get; set; } = false;
+    [DataField("noWindowInTile")]
+    public bool NoWindowInTile = false;
 
-        [DataField("onHandInteract")]
-        public bool OnHandInteract { get; set; } = true;
+    [DataField("onHandInteract")]
+    public bool OnHandInteract = true;
 
-        [DataField("onInteractUsing")]
-        public bool OnInteractUsing { get; set; } = true;
+    [DataField("onInteractUsing")]
+    public bool OnInteractUsing = true;
 
-        [DataField("requirePower")]
-        public bool RequirePower { get; } = true;
+    [DataField("requirePower")]
+    public bool RequirePower = true;
 
-        [DataField("usesApcPower")]
-        public bool UsesApcPower { get; } = false;
+    [DataField("usesApcPower")]
+    public bool UsesApcPower = false;
 
-        [DataField("highVoltageNode")]
-        public string? HighVoltageNode { get; }
+    [DataField("highVoltageNode")]
+    public string? HighVoltageNode;
 
-        [DataField("mediumVoltageNode")]
-        public string? MediumVoltageNode { get; }
+    [DataField("mediumVoltageNode")]
+    public string? MediumVoltageNode;
 
-        [DataField("lowVoltageNode")]
-        public string? LowVoltageNode { get; }
+    [DataField("lowVoltageNode")]
+    public string? LowVoltageNode;
 
-        [DataField("highVoltageDamageMultiplier")]
-        public float HighVoltageDamageMultiplier { get; } = 3f;
+    [DataField("highVoltageDamageMultiplier")]
+    public float HighVoltageDamageMultiplier = 3f;
 
-        [DataField("highVoltageTimeMultiplier")]
-        public float HighVoltageTimeMultiplier { get; } = 1.5f;
+    [DataField("highVoltageTimeMultiplier")]
+    public float HighVoltageTimeMultiplier = 1.5f;
 
-        [DataField("mediumVoltageDamageMultiplier")]
-        public float MediumVoltageDamageMultiplier { get; } = 2f;
+    [DataField("mediumVoltageDamageMultiplier")]
+    public float MediumVoltageDamageMultiplier = 2f;
 
-        [DataField("mediumVoltageTimeMultiplier")]
-        public float MediumVoltageTimeMultiplier { get; } = 1.25f;
+    [DataField("mediumVoltageTimeMultiplier")]
+    public float MediumVoltageTimeMultiplier = 1.25f;
 
-        [DataField("shockDamage")]
-        public int ShockDamage { get; } = 20;
+    [DataField("shockDamage")]
+    public int ShockDamage = 20;
 
-        /// <summary>
-        ///     Shock time, in seconds.
-        /// </summary>
-        [DataField("shockTime")]
-        public float ShockTime { get; } = 8f;
+    /// <summary>
+    ///     Shock time, in seconds.
+    /// </summary>
+    [DataField("shockTime")]
+    public float ShockTime = 8f;
 
-        [DataField("siemensCoefficient")]
-        public float SiemensCoefficient { get; } = 1f;
+    [DataField("siemensCoefficient")]
+    public float SiemensCoefficient = 1f;
 
-        [DataField("shockNoises")]
-        public SoundSpecifier ShockNoises { get; } = new SoundCollectionSpecifier("sparks");
+    [DataField("shockNoises")]
+    public SoundSpecifier ShockNoises = new SoundCollectionSpecifier("sparks");
 
-        [DataField("playSoundOnShock")]
-        public bool PlaySoundOnShock { get; } = true;
+    [DataField("playSoundOnShock")]
+    public bool PlaySoundOnShock = true;
 
-        [DataField("shockVolume")]
-        public float ShockVolume { get; } = 20;
-    }
+    [DataField("shockVolume")]
+    public float ShockVolume = 20;
 }
index a93b55a5259ad2594cd00fc3d975e91058bdc83e..60c752c34d6386c33fc9b148e904dba31ed3e3c1 100644 (file)
@@ -1,16 +1,21 @@
-namespace Content.Server.Electrocution
+namespace Content.Server.Electrocution;
+
+/// <summary>
+/// Component for virtual electrocution entities (representing an in-progress shock).
+/// </summary>
+[RegisterComponent]
+[Access(typeof(ElectrocutionSystem))]
+public sealed class ElectrocutionComponent : Component
 {
-    /// <summary>
-    /// Component for virtual electrocution entities (representing an in-progress shock).
-    /// </summary>
-    [RegisterComponent]
-    [Access(typeof(ElectrocutionSystem))]
-    public sealed class ElectrocutionComponent : Component
-    {
-        [DataField("timeLeft")] public float TimeLeft { get; set; }
-        [DataField("electrocuting")] public EntityUid Electrocuting { get; set; }
-        [DataField("accumDamage")] public float AccumulatedDamage { get; set; }
-        [DataField("source")] public EntityUid Source { get; set; }
+    [DataField("timeLeft")]
+    public float TimeLeft;
+
+    [DataField("electrocuting")]
+    public EntityUid Electrocuting;
+
+    [DataField("accumDamage")]
+    public float AccumulatedDamage;
 
-    }
+    [DataField("source")]
+    public EntityUid Source;
 }
index 811232a86a166e1e05d4dfe99086638863506855..11db6184e3a3a5b1a36cae21df3ffdfc240ff629 100644 (file)
@@ -23,107 +23,138 @@ using Content.Shared.Tag;
 using Content.Shared.Weapons.Melee;
 using Content.Shared.Weapons.Melee.Events;
 using Robust.Shared.Audio;
-using Robust.Shared.Physics.Dynamics;
 using Robust.Shared.Physics.Events;
 using Robust.Shared.Player;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Random;
-using Robust.Shared.Utility;
 
-namespace Content.Server.Electrocution
+namespace Content.Server.Electrocution;
+
+public sealed class ElectrocutionSystem : SharedElectrocutionSystem
 {
-    public sealed class ElectrocutionSystem : SharedElectrocutionSystem
+    [Dependency] private readonly EntityLookupSystem _entityLookup = default!;
+    [Dependency] private readonly IRobustRandom _random = default!;
+    [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+    [Dependency] private readonly StatusEffectsSystem _statusEffects = default!;
+    [Dependency] private readonly SharedJitteringSystem _jittering = default!;
+    [Dependency] private readonly SharedStunSystem _stun = default!;
+    [Dependency] private readonly SharedStutteringSystem _stuttering = default!;
+    [Dependency] private readonly SharedPopupSystem _popup = default!;
+    [Dependency] private readonly DamageableSystem _damageable = default!;
+    [Dependency] private readonly NodeGroupSystem _nodeGroup = default!;
+    [Dependency] private readonly IAdminLogManager _adminLogger = default!;
+    [Dependency] private readonly TagSystem _tag = default!;
+    [Dependency] private readonly SharedAudioSystem _audio = default!;
+    [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
+
+    private const string StatusEffectKey = "Electrocution";
+    private const string DamageType = "Shock";
+
+    // Yes, this is absurdly small for a reason.
+    private const float ElectrifiedDamagePerWatt = 0.0015f;
+
+    private const float RecursiveDamageMultiplier = 0.75f;
+    private const float RecursiveTimeMultiplier = 0.8f;
+
+    private const float ParalyzeTimeMultiplier = 1f;
+
+    private const float StutteringTimeMultiplier = 1.5f;
+
+    private const float JitterTimeMultiplier = 0.75f;
+    private const float JitterAmplitude = 80f;
+    private const float JitterFrequency = 8f;
+
+    public override void Initialize()
     {
-        [Dependency] private readonly EntityLookupSystem _entityLookup = default!;
-        [Dependency] private readonly IRobustRandom _random = default!;
-        [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
-        [Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!;
-        [Dependency] private readonly SharedJitteringSystem _jitteringSystem = default!;
-        [Dependency] private readonly SharedStunSystem _stunSystem = default!;
-        [Dependency] private readonly SharedStutteringSystem _stutteringSystem = default!;
-        [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
-        [Dependency] private readonly DamageableSystem _damageableSystem = default!;
-        [Dependency] private readonly NodeGroupSystem _nodeGroupSystem = default!;
-        [Dependency] private readonly IAdminLogManager _adminLogger= default!;
-        [Dependency] private readonly TagSystem _tagSystem = default!;
-
-        private const string StatusEffectKey = "Electrocution";
-        private const string DamageType = "Shock";
+        base.Initialize();
 
-        // Yes, this is absurdly small for a reason.
-        private const float ElectrifiedDamagePerWatt = 0.0015f;
+        SubscribeLocalEvent<ElectrifiedComponent, StartCollideEvent>(OnElectrifiedStartCollide);
+        SubscribeLocalEvent<ElectrifiedComponent, AttackedEvent>(OnElectrifiedAttacked);
+        SubscribeLocalEvent<ElectrifiedComponent, InteractHandEvent>(OnElectrifiedHandInteract);
+        SubscribeLocalEvent<ElectrifiedComponent, InteractUsingEvent>(OnElectrifiedInteractUsing);
+        SubscribeLocalEvent<RandomInsulationComponent, MapInitEvent>(OnRandomInsulationMapInit);
 
-        private const float RecursiveDamageMultiplier = 0.75f;
-        private const float RecursiveTimeMultiplier = 0.8f;
+        UpdatesAfter.Add(typeof(PowerNetSystem));
+    }
 
-        private const float ParalyzeTimeMultiplier = 1f;
+    public override void Update(float frameTime)
+    {
+        UpdateElectrocutions(frameTime);
+        UpdateState(frameTime);
+    }
 
-        private const float StutteringTimeMultiplier = 1.5f;
+    private void UpdateElectrocutions(float frameTime)
+    {
+        var query = EntityQueryEnumerator<ElectrocutionComponent, PowerConsumerComponent>();
+        while (query.MoveNext(out var uid, out var electrocution, out var consumer))
+        {
+            var timePassed = Math.Min(frameTime, electrocution.TimeLeft);
 
-        private const float JitterTimeMultiplier = 0.75f;
-        private const float JitterAmplitude = 80f;
-        private const float JitterFrequency = 8f;
+            electrocution.TimeLeft -= timePassed;
+            electrocution.AccumulatedDamage += consumer.ReceivedPower * ElectrifiedDamagePerWatt * timePassed;
 
-        public override void Initialize()
-        {
-            base.Initialize();
+            if (!MathHelper.CloseTo(electrocution.TimeLeft, 0))
+                continue;
 
-            SubscribeLocalEvent<ElectrifiedComponent, StartCollideEvent>(OnElectrifiedStartCollide);
-            SubscribeLocalEvent<ElectrifiedComponent, AttackedEvent>(OnElectrifiedAttacked);
-            SubscribeLocalEvent<ElectrifiedComponent, InteractHandEvent>(OnElectrifiedHandInteract);
-            SubscribeLocalEvent<ElectrifiedComponent, InteractUsingEvent>(OnElectrifiedInteractUsing);
-            SubscribeLocalEvent<RandomInsulationComponent, MapInitEvent>(OnRandomInsulationMapInit);
+            if (EntityManager.EntityExists(electrocution.Electrocuting))
+            {
+                // TODO: damage should be scaled by shock damage multiplier
+                // TODO: better paralyze/jitter timing
+                var damage = new DamageSpecifier(_prototypeManager.Index<DamageTypePrototype>(DamageType), (int) electrocution.AccumulatedDamage);
 
-            UpdatesAfter.Add(typeof(PowerNetSystem));
+                var actual = _damageable.TryChangeDamage(electrocution.Electrocuting, damage, origin: electrocution.Source);
+                if (actual != null)
+                {
+                    _adminLogger.Add(LogType.Electrocution,
+                        $"{ToPrettyString(electrocution.Electrocuting):entity} received {actual.Total:damage} powered electrocution damage from {ToPrettyString(electrocution.Source):source}");
+                }
+            }
+            QueueDel(uid);
         }
+    }
 
-        public override void Update(float frameTime)
+    private void UpdateState(float frameTime)
+    {
+        var query = EntityQueryEnumerator<ActivatedElectrifiedComponent, ElectrifiedComponent, TransformComponent>();
+        while (query.MoveNext(out var uid, out var activated, out var electrified, out var transform))
         {
-            // Update "in progress" electrocutions
-
-            RemQueue<ElectrocutionComponent> finishedElectrocutionsQueue = new();
-            foreach (var (electrocution, consumer) in EntityManager
-                .EntityQuery<ElectrocutionComponent, PowerConsumerComponent>())
+            activated.TimeLeft -= frameTime;
+            if (activated.TimeLeft <= 0 || !IsPowered(uid, electrified, transform))
             {
-                var ftAdjusted = Math.Min(frameTime, electrocution.TimeLeft);
-
-                electrocution.TimeLeft -= ftAdjusted;
-                electrocution.AccumulatedDamage += consumer.ReceivedPower * ElectrifiedDamagePerWatt * ftAdjusted;
-
-                if (MathHelper.CloseTo(electrocution.TimeLeft, 0))
-                    finishedElectrocutionsQueue.Add(electrocution);
+                _appearance.SetData(uid, ElectrifiedVisuals.IsPowered, false);
+                RemComp<ActivatedElectrifiedComponent>(uid);
             }
+        }
+    }
 
-            foreach (var finished in finishedElectrocutionsQueue)
+    private bool IsPowered(EntityUid uid, ElectrifiedComponent electrified, TransformComponent transform)
+    {
+        if (!electrified.Enabled)
+            return false;
+        if (electrified.NoWindowInTile)
+        {
+            foreach (var entity in transform.Coordinates.GetEntitiesInTile(LookupFlags.Approximate | LookupFlags.Static, _entityLookup))
             {
-                var uid = finished.Owner;
-                if (EntityManager.EntityExists(finished.Electrocuting))
-                {
-                    // TODO: damage should be scaled by shock damage multiplier
-                    // TODO: better paralyze/jitter timing
-                    var damage = new DamageSpecifier(
-                        _prototypeManager.Index<DamageTypePrototype>(DamageType),
-                        (int) finished.AccumulatedDamage);
-
-                    var actual = _damageableSystem.TryChangeDamage(finished.Electrocuting, damage, origin: finished.Source);
-                    if (actual != null)
-                    {
-                        _adminLogger.Add(LogType.Electrocution,
-                            $"{ToPrettyString(finished.Electrocuting):entity} received {actual.Total:damage} powered electrocution damage from {ToPrettyString(finished.Source):source}");
-                    }
-                }
-
-                EntityManager.DeleteEntity(uid);
+                if (_tag.HasTag(entity, "Window"))
+                    return false;
             }
         }
-
-        private void OnElectrifiedStartCollide(EntityUid uid, ElectrifiedComponent electrified, ref StartCollideEvent args)
+        if (electrified.UsesApcPower)
         {
-            if (!electrified.OnBump)
-                return;
+            if (!this.IsPowered(uid, EntityManager))
+                return false;
+        }
+        else if (electrified.RequirePower && PoweredNode(uid, electrified) == null)
+            return false;
+
+        return true;
+    }
 
+    private void OnElectrifiedStartCollide(EntityUid uid, ElectrifiedComponent electrified, ref StartCollideEvent args)
+    {
+        if (electrified.OnBump)
             TryDoElectrifiedAct(uid, args.OtherFixture.Body.Owner, 1, electrified);
-        }
+    }
 
         private void OnElectrifiedAttacked(EntityUid uid, ElectrifiedComponent electrified, AttackedEvent args)
         {
@@ -138,129 +169,112 @@ namespace Content.Server.Electrocution
             }
 
             TryDoElectrifiedAct(uid, args.User, 1, electrified);
-        }
-
-        private void OnElectrifiedHandInteract(EntityUid uid, ElectrifiedComponent electrified, InteractHandEvent args)
-        {
-            if (!electrified.OnHandInteract)
-                return;
+    }
 
+    private void OnElectrifiedHandInteract(EntityUid uid, ElectrifiedComponent electrified, InteractHandEvent args)
+    {
+        if (electrified.OnHandInteract)
             TryDoElectrifiedAct(uid, args.User, 1, electrified);
-        }
+    }
 
-        private void OnElectrifiedInteractUsing(EntityUid uid, ElectrifiedComponent electrified, InteractUsingEvent args)
-        {
-            if (!electrified.OnInteractUsing)
-                return;
+    private void OnElectrifiedInteractUsing(EntityUid uid, ElectrifiedComponent electrified, InteractUsingEvent args)
+    {
+        if (!electrified.OnInteractUsing)
+            return;
 
-            var siemens = TryComp(args.Used, out InsulatedComponent? insulation)
-                ? insulation.SiemensCoefficient
-                : 1;
+        var siemens = TryComp<InsulatedComponent>(args.Used, out var insulation)
+            ? insulation.SiemensCoefficient
+            : 1;
 
-            TryDoElectrifiedAct(uid, args.User, siemens, electrified);
-        }
+        TryDoElectrifiedAct(uid, args.User, siemens, electrified);
+    }
 
-        public bool TryDoElectrifiedAct(EntityUid uid, EntityUid targetUid,
-            float siemens = 1,
-            ElectrifiedComponent? electrified = null,
-            NodeContainerComponent? nodeContainer = null,
-            TransformComponent? transform = null)
-        {
-            if (!Resolve(uid, ref electrified, ref transform, false))
-                return false;
+    public bool TryDoElectrifiedAct(EntityUid uid, EntityUid targetUid,
+        float siemens = 1,
+        ElectrifiedComponent? electrified = null,
+        NodeContainerComponent? nodeContainer = null,
+        TransformComponent? transform = null)
+    {
+        if (!Resolve(uid, ref electrified, ref transform, false))
+            return false;
 
-            if (!electrified.Enabled)
-                return false;
+        if (!IsPowered(uid, electrified, transform))
+            return false;
 
-            if (electrified.NoWindowInTile)
-            {
-                foreach (var entity in transform.Coordinates.GetEntitiesInTile(
-                    LookupFlags.Approximate | LookupFlags.Static, _entityLookup))
-                {
-                    if (_tagSystem.HasTag(entity, "Window"))
-                        return false;
-                }
-            }
+        EnsureComp<ActivatedElectrifiedComponent>(uid);
+        _appearance.SetData(uid, ElectrifiedVisuals.IsPowered, true);
 
-            siemens *= electrified.SiemensCoefficient;
-            if (!DoCommonElectrocutionAttempt(targetUid, uid, ref siemens) || siemens <= 0)
-                return false; // If electrocution would fail, do nothing.
+        siemens *= electrified.SiemensCoefficient;
+        if (siemens <= 0 || !DoCommonElectrocutionAttempt(targetUid, uid, ref siemens))
+            return false; // If electrocution would fail, do nothing.
 
-            var targets = new List<(EntityUid entity, int depth)>();
-            GetChainedElectrocutionTargets(targetUid, targets);
-            if (!electrified.RequirePower || electrified.UsesApcPower)
+        var targets = new List<(EntityUid entity, int depth)>();
+        GetChainedElectrocutionTargets(targetUid, targets);
+        if (!electrified.RequirePower || electrified.UsesApcPower)
+        {
+            var lastRet = true;
+            for (var i = targets.Count - 1; i >= 0; i--)
             {
-                // Does it use APC power for its electrification check? Check if it's powered, and then
-                // attempt an electrocution if all the checks succeed.
-
-                if (electrified.UsesApcPower && !this.IsPowered(uid, EntityManager))
-                {
-                    return false;
-                }
-
-                var lastRet = true;
-                for (var i = targets.Count - 1; i >= 0; i--)
-                {
-                    var (entity, depth) = targets[i];
-                    lastRet = TryDoElectrocution(
-                        entity,
-                        uid,
-                        (int) (electrified.ShockDamage * MathF.Pow(RecursiveDamageMultiplier, depth)),
-                        TimeSpan.FromSeconds(electrified.ShockTime * MathF.Pow(RecursiveTimeMultiplier, depth)), true,
-                        electrified.SiemensCoefficient);
-                }
-
-                return lastRet;
+                var (entity, depth) = targets[i];
+                lastRet = TryDoElectrocution(
+                    entity,
+                    uid,
+                    (int) (electrified.ShockDamage * MathF.Pow(RecursiveDamageMultiplier, depth)),
+                    TimeSpan.FromSeconds(electrified.ShockTime * MathF.Pow(RecursiveTimeMultiplier, depth)), 
+                    true,
+                    electrified.SiemensCoefficient
+                );
             }
+            return lastRet;
+        }
 
-            if (!Resolve(uid, ref nodeContainer, false))
-                return false;
-
-            var node = TryNode(electrified.HighVoltageNode) ??
-                       TryNode(electrified.MediumVoltageNode) ??
-                       TryNode(electrified.LowVoltageNode);
-
-            if (node == null)
-                return false;
+        var node = PoweredNode(uid, electrified, nodeContainer);
+        if (node == null)
+            return false;
 
-            var (damageMult, timeMult) = node.NodeGroupID switch
-            {
-                NodeGroupID.HVPower => (electrified.HighVoltageDamageMultiplier, electrified.HighVoltageTimeMultiplier),
-                NodeGroupID.MVPower => (electrified.MediumVoltageDamageMultiplier,
-                    electrified.MediumVoltageTimeMultiplier),
-                _ => (1f, 1f)
-            };
+        var (damageMult, timeMult) = node.NodeGroupID switch
+        {
+            NodeGroupID.HVPower => (electrified.HighVoltageDamageMultiplier, electrified.HighVoltageTimeMultiplier),
+            NodeGroupID.MVPower => (electrified.MediumVoltageDamageMultiplier, electrified.MediumVoltageTimeMultiplier),
+            _ => (1f, 1f)
+        };
 
+        {
+            var lastRet = true;
+            for (var i = targets.Count - 1; i >= 0; i--)
             {
-                var lastRet = true;
-                for (var i = targets.Count - 1; i >= 0; i--)
-                {
-                    var (entity, depth) = targets[i];
-                    lastRet = TryDoElectrocutionPowered(
-                        entity,
-                        uid,
-                        node,
-                        (int) (electrified.ShockDamage * MathF.Pow(RecursiveDamageMultiplier, depth) * damageMult),
-                        TimeSpan.FromSeconds(electrified.ShockTime * MathF.Pow(RecursiveTimeMultiplier, depth) *
-                                             timeMult), true,
-                        electrified.SiemensCoefficient);
-                }
-
-                return lastRet;
+                var (entity, depth) = targets[i];
+                lastRet = TryDoElectrocutionPowered(
+                    entity,
+                    uid,
+                    node,
+                    (int) (electrified.ShockDamage * MathF.Pow(RecursiveDamageMultiplier, depth) * damageMult),
+                    TimeSpan.FromSeconds(electrified.ShockTime * MathF.Pow(RecursiveTimeMultiplier, depth) * timeMult), 
+                    true,
+                    electrified.SiemensCoefficient);
             }
+            return lastRet;
+        }
+    }
 
+    private Node? PoweredNode(EntityUid uid, ElectrifiedComponent electrified, NodeContainerComponent? nodeContainer = null)
+    {
+        if (!Resolve(uid, ref nodeContainer, false))
+            return null;
 
-            Node? TryNode(string? id)
-            {
-                if (id != null && nodeContainer.TryGetNode<Node>(id, out var tryNode)
-                               && tryNode.NodeGroup is IBasePowerNet { NetworkNode: { LastCombinedSupply: >0 } })
-                {
-                    return tryNode;
-                }
+        return TryNode(electrified.HighVoltageNode) ?? TryNode(electrified.MediumVoltageNode) ?? TryNode(electrified.LowVoltageNode);
 
-                return null;
+        Node? TryNode(string? id)
+        {
+            if (id != null &&
+                nodeContainer.TryGetNode<Node>(id, out var tryNode) &&
+                tryNode.NodeGroup is IBasePowerNet { NetworkNode: { LastCombinedSupply: > 0 } })
+            {
+                return tryNode;
             }
+            return null;
         }
+    }
 
         /// <param name="uid">Entity being electrocuted.</param>
         /// <param name="sourceUid">Source entity of the electrocution.</param>
@@ -283,184 +297,178 @@ namespace Content.Server.Electrocution
             return true;
         }
 
-        private bool TryDoElectrocutionPowered(
-            EntityUid uid,
-            EntityUid sourceUid,
-            Node node,
-            int shockDamage,
-            TimeSpan time,
-            bool refresh,
-            float siemensCoefficient = 1f,
-            StatusEffectsComponent? statusEffects = null,
-            TransformComponent? sourceTransform = null)
-        {
-            if (!DoCommonElectrocutionAttempt(uid, sourceUid, ref siemensCoefficient))
-                return false;
-
-            // Coefficient needs to be higher than this to do a powered electrocution!
-            if (siemensCoefficient <= 0.5f)
-                return DoCommonElectrocution(uid, sourceUid, shockDamage, time, refresh, siemensCoefficient, statusEffects);
+    private bool TryDoElectrocutionPowered(
+        EntityUid uid,
+        EntityUid sourceUid,
+        Node node,
+        int shockDamage,
+        TimeSpan time,
+        bool refresh,
+        float siemensCoefficient = 1f,
+        StatusEffectsComponent? statusEffects = null,
+        TransformComponent? sourceTransform = null)
+    {
+        if (!DoCommonElectrocutionAttempt(uid, sourceUid, ref siemensCoefficient))
+            return false;
 
-            if (!DoCommonElectrocution(uid, sourceUid, null, time, refresh, siemensCoefficient, statusEffects))
-                return false;
+        // Coefficient needs to be higher than this to do a powered electrocution!
+        if (siemensCoefficient <= 0.5f)
+            return DoCommonElectrocution(uid, sourceUid, shockDamage, time, refresh, siemensCoefficient, statusEffects);
 
-            if (!Resolve(sourceUid, ref sourceTransform)) // This shouldn't really happen, but just in case...
-                return true;
+        if (!DoCommonElectrocution(uid, sourceUid, null, time, refresh, siemensCoefficient, statusEffects))
+            return false;
 
-            var electrocutionEntity = EntityManager.SpawnEntity(
-                $"VirtualElectrocutionLoad{node.NodeGroupID}", sourceTransform.Coordinates);
+        if (!Resolve(sourceUid, ref sourceTransform)) // This shouldn't really happen, but just in case...
+            return true;
 
-            var electrocutionNode = EntityManager.GetComponent<NodeContainerComponent>(electrocutionEntity)
-                .GetNode<ElectrocutionNode>("electrocution");
+        var electrocutionEntity = Spawn($"VirtualElectrocutionLoad{node.NodeGroupID}", sourceTransform.Coordinates);
+        var electrocutionNode = Comp<NodeContainerComponent>(electrocutionEntity).GetNode<ElectrocutionNode>("electrocution");
+        var electrocutionComponent = Comp<ElectrocutionComponent>(electrocutionEntity);
 
-            var electrocutionComponent = EntityManager.GetComponent<ElectrocutionComponent>(electrocutionEntity);
+        electrocutionNode.CableEntity = sourceUid;
+        electrocutionNode.NodeName = node.Name;
 
-            electrocutionNode.CableEntity = sourceUid;
-            electrocutionNode.NodeName = node.Name;
+        _nodeGroup.QueueReflood(electrocutionNode);
 
-            _nodeGroupSystem.QueueReflood(electrocutionNode);
+        electrocutionComponent.TimeLeft = 1f;
+        electrocutionComponent.Electrocuting = uid;
+        electrocutionComponent.Source = sourceUid;
 
-            electrocutionComponent.TimeLeft = 1f;
-            electrocutionComponent.Electrocuting = uid;
-            electrocutionComponent.Source = sourceUid;
+        RaiseLocalEvent(uid, new ElectrocutedEvent(uid, sourceUid, siemensCoefficient), true);
 
-            RaiseLocalEvent(uid, new ElectrocutedEvent(uid, sourceUid, siemensCoefficient), true);
+        return true;
+    }
 
-            return true;
-        }
+    private bool DoCommonElectrocutionAttempt(EntityUid uid, EntityUid? sourceUid, ref float siemensCoefficient, bool ignoreInsulation = false)
+    {
 
-        private bool DoCommonElectrocutionAttempt(EntityUid uid, EntityUid? sourceUid, ref float siemensCoefficient, bool ignoreInsulation = false)
-        {
+        var attemptEvent = new ElectrocutionAttemptEvent(uid, sourceUid, siemensCoefficient,
+            ignoreInsulation ? SlotFlags.NONE : ~SlotFlags.POCKET);
+        RaiseLocalEvent(uid, attemptEvent, true);
 
-            var attemptEvent = new ElectrocutionAttemptEvent(uid, sourceUid, siemensCoefficient,
-                ignoreInsulation ? SlotFlags.NONE : ~SlotFlags.POCKET);
-            RaiseLocalEvent(uid, attemptEvent, true);
+        // Cancel the electrocution early, so we don't recursively electrocute anything.
+        if (attemptEvent.Cancelled)
+            return false;
 
-            // Cancel the electrocution early, so we don't recursively electrocute anything.
-            if (attemptEvent.Cancelled)
-                return false;
+        siemensCoefficient = attemptEvent.SiemensCoefficient;
+        return true;
+    }
 
-            siemensCoefficient = attemptEvent.SiemensCoefficient;
-            return true;
-        }
+    private bool DoCommonElectrocution(EntityUid uid, EntityUid? sourceUid,
+        int? shockDamage, TimeSpan time, bool refresh, float siemensCoefficient = 1f,
+        StatusEffectsComponent? statusEffects = null)
+    {
+        if (siemensCoefficient <= 0)
+            return false;
 
-        private bool DoCommonElectrocution(EntityUid uid, EntityUid? sourceUid,
-            int? shockDamage, TimeSpan time, bool refresh, float siemensCoefficient = 1f,
-            StatusEffectsComponent? statusEffects = null)
+        if (shockDamage != null)
         {
-            if (siemensCoefficient <= 0)
-                return false;
-
-            if (shockDamage != null)
-            {
-                shockDamage = (int) (shockDamage * siemensCoefficient);
+            shockDamage = (int) (shockDamage * siemensCoefficient);
 
-                if (shockDamage.Value <= 0)
-                    return false;
-            }
-
-            if (!Resolve(uid, ref statusEffects, false) ||
-                !_statusEffectsSystem.CanApplyEffect(uid, StatusEffectKey, statusEffects))
+            if (shockDamage.Value <= 0)
                 return false;
+        }
 
-            if (!_statusEffectsSystem.TryAddStatusEffect<ElectrocutedComponent>(uid, StatusEffectKey, time, refresh,
-                statusEffects))
-                return false;
+        if (!Resolve(uid, ref statusEffects, false) ||
+            !_statusEffects.CanApplyEffect(uid, StatusEffectKey, statusEffects))
+        {
+            return false;
+        }
+        
+        if (!_statusEffects.TryAddStatusEffect<ElectrocutedComponent>(uid, StatusEffectKey, time, refresh, statusEffects))
+            return false;
 
-            var shouldStun = siemensCoefficient > 0.5f;
+        var shouldStun = siemensCoefficient > 0.5f;
 
-            if (shouldStun)
-                _stunSystem.TryParalyze(uid, time * ParalyzeTimeMultiplier, refresh, statusEffects);
+        if (shouldStun)
+            _stun.TryParalyze(uid, time * ParalyzeTimeMultiplier, refresh, statusEffects);
 
-            // TODO: Sparks here.
+        // TODO: Sparks here.
 
-            if(shockDamage is {} dmg)
-            {
-                var actual = _damageableSystem.TryChangeDamage(uid,
-                    new DamageSpecifier(_prototypeManager.Index<DamageTypePrototype>(DamageType), dmg), origin: sourceUid);
+        if (shockDamage is { } dmg)
+        {
+            var actual = _damageable.TryChangeDamage(uid,
+                new DamageSpecifier(_prototypeManager.Index<DamageTypePrototype>(DamageType), dmg), origin: sourceUid);
 
-                if (actual != null)
-                {
-                    _adminLogger.Add(LogType.Electrocution,
-                        $"{ToPrettyString(statusEffects.Owner):entity} received {actual.Total:damage} powered electrocution damage{(sourceUid != null ? " from " + ToPrettyString(sourceUid.Value) : ""):source}");
-                }
+            if (actual != null)
+            {
+                _adminLogger.Add(LogType.Electrocution,
+                    $"{ToPrettyString(uid):entity} received {actual.Total:damage} powered electrocution damage{(sourceUid != null ? " from " + ToPrettyString(sourceUid.Value) : ""):source}");
             }
+        }
 
-            _stutteringSystem.DoStutter(uid, time * StutteringTimeMultiplier, refresh, statusEffects);
-            _jitteringSystem.DoJitter(uid, time * JitterTimeMultiplier, refresh, JitterAmplitude, JitterFrequency, true,
-                statusEffects);
-
-            _popupSystem.PopupEntity(Loc.GetString("electrocuted-component-mob-shocked-popup-player"), uid, uid);
+        _stuttering.DoStutter(uid, time * StutteringTimeMultiplier, refresh, statusEffects);
+        _jittering.DoJitter(uid, time * JitterTimeMultiplier, refresh, JitterAmplitude, JitterFrequency, true, statusEffects);
 
-            var filter = Filter.PvsExcept(uid, entityManager: EntityManager);
+        _popup.PopupEntity(Loc.GetString("electrocuted-component-mob-shocked-popup-player"), uid, uid);
 
-            // TODO: Allow being able to pass EntityUid to Loc...
-            if (sourceUid != null)
-            {
-                _popupSystem.PopupEntity(Loc.GetString("electrocuted-component-mob-shocked-by-source-popup-others",
-                        ("mob", uid), ("source", (sourceUid.Value))), uid, filter, true);
-                PlayElectrocutionSound(uid, sourceUid.Value);
-            }
-            else
-            {
-                _popupSystem.PopupEntity(Loc.GetString("electrocuted-component-mob-shocked-popup-others",
-                    ("mob", uid)), uid, filter, true);
-            }
+        var filter = Filter.PvsExcept(uid, entityManager: EntityManager);
 
-            return true;
+        // TODO: Allow being able to pass EntityUid to Loc...
+        if (sourceUid != null)
+        {
+            _popup.PopupEntity(Loc.GetString("electrocuted-component-mob-shocked-by-source-popup-others",
+                ("mob", uid), ("source", (sourceUid.Value))), uid, filter, true);
+            PlayElectrocutionSound(uid, sourceUid.Value);
         }
-
-        private void GetChainedElectrocutionTargets(EntityUid source, List<(EntityUid entity, int depth)> all)
+        else
         {
-            var visited = new HashSet<EntityUid>();
-
-            GetChainedElectrocutionTargetsRecurse(source, 1, visited, all);
+            _popup.PopupEntity(Loc.GetString("electrocuted-component-mob-shocked-popup-others",
+                ("mob", uid)), uid, filter, true);
         }
 
-        private void GetChainedElectrocutionTargetsRecurse(
-            EntityUid entity,
-            int depth,
-            HashSet<EntityUid> visited,
-            List<(EntityUid entity, int depth)> all)
-        {
-            all.Add((entity, depth));
-            visited.Add(entity);
+        return true;
+    }
 
-            if (EntityManager.TryGetComponent(entity, out SharedPullableComponent? pullable)
-                && pullable.Puller is {Valid: true} pullerId
-                && !visited.Contains(pullerId))
-            {
-                GetChainedElectrocutionTargetsRecurse(pullerId, depth + 1, visited, all);
-            }
+    private void GetChainedElectrocutionTargets(EntityUid source, List<(EntityUid entity, int depth)> all)
+    {
+        var visited = new HashSet<EntityUid>();
 
-            if (EntityManager.TryGetComponent(entity, out SharedPullerComponent? puller)
-                && puller.Pulling is {Valid: true} pullingId
-                && !visited.Contains(pullingId))
-            {
-                GetChainedElectrocutionTargetsRecurse(pullingId, depth + 1, visited, all);
-            }
+        GetChainedElectrocutionTargetsRecurse(source, 1, visited, all);
+    }
+
+    private void GetChainedElectrocutionTargetsRecurse(
+        EntityUid entity,
+        int depth,
+        HashSet<EntityUid> visited,
+        List<(EntityUid entity, int depth)> all)
+    {
+        all.Add((entity, depth));
+        visited.Add(entity);
+
+        if (TryComp<SharedPullableComponent>(entity, out var pullable) &&
+            pullable.Puller is { Valid: true } pullerId &&
+            !visited.Contains(pullerId))
+        {
+            GetChainedElectrocutionTargetsRecurse(pullerId, depth + 1, visited, all);
         }
 
-        private void OnRandomInsulationMapInit(EntityUid uid, RandomInsulationComponent randomInsulation,
-            MapInitEvent args)
+        if (TryComp<SharedPullerComponent>(entity, out var puller) &&
+            puller.Pulling is { Valid: true } pullingId &&
+            !visited.Contains(pullingId))
         {
-            if (!EntityManager.TryGetComponent(uid, out InsulatedComponent? insulated))
-                return;
+            GetChainedElectrocutionTargetsRecurse(pullingId, depth + 1, visited, all);
+        }
+    }
 
-            if (randomInsulation.List.Length == 0)
-                return;
+    private void OnRandomInsulationMapInit(EntityUid uid, RandomInsulationComponent randomInsulation,
+        MapInitEvent args)
+    {
+        if (!TryComp<InsulatedComponent>(uid, out var insulated))
+            return;
 
-            SetInsulatedSiemensCoefficient(uid, _random.Pick(randomInsulation.List), insulated);
-        }
+        if (randomInsulation.List.Length == 0)
+            return;
 
-        private void PlayElectrocutionSound(EntityUid targetUid, EntityUid sourceUid, ElectrifiedComponent? electrified = null)
-        {
-            if (!Resolve(sourceUid, ref electrified) || !electrified.PlaySoundOnShock)
-            {
-                return;
-            }
+        SetInsulatedSiemensCoefficient(uid, _random.Pick(randomInsulation.List), insulated);
+    }
 
-            SoundSystem.Play(electrified.ShockNoises.GetSound(), Filter.Pvs(targetUid), targetUid, AudioParams.Default.WithVolume(electrified.ShockVolume));
+    private void PlayElectrocutionSound(EntityUid targetUid, EntityUid sourceUid, ElectrifiedComponent? electrified = null)
+    {
+        if (!Resolve(sourceUid, ref electrified) || !electrified.PlaySoundOnShock)
+        {
+            return;
         }
+        _audio.PlayPvs(electrified.ShockNoises, targetUid, AudioParams.Default.WithVolume(electrified.ShockVolume));
     }
 }
diff --git a/Content.Shared/Electrocution/SharedElectrocution.cs b/Content.Shared/Electrocution/SharedElectrocution.cs
new file mode 100644 (file)
index 0000000..4060856
--- /dev/null
@@ -0,0 +1,15 @@
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Electrocution;
+
+[Serializable, NetSerializable]
+public enum ElectrifiedLayers : byte
+{
+    Powered
+}
+
+[Serializable, NetSerializable]
+public enum ElectrifiedVisuals : byte
+{
+    IsPowered
+}
index b3123dc6a71cb1543cf57d7455958a62d689c6d8..9ebacb77796ecbdd6186199f1f0f9ea546c460f3 100644 (file)
       netsync: false
       drawdepth: Walls
       sprite: Structures/Walls/grille.rsi
-      state: grille
+      layers:
+        - state: grille
+        - state: electrified
+          sprite: Effects/electricity.rsi
+          map: ["enum.ElectrifiedLayers.Powered"]
+          shader: unshaded
+          visible: false
     - type: Icon
       sprite: Structures/Walls/grille.rsi
       state: grille
               node: grilleBroken
             - !type:DoActsBehavior
               acts: ["Breakage"]
+    - type: Appearance
+    - type: GenericVisualizer
+      visuals:
+        enum.ElectrifiedVisuals.IsPowered:
+          enum.ElectrifiedLayers.Powered:
+            True: { visible: True }
+            False: { visible: False }
+    - type: AnimationPlayer
 
 - type: entity
   id: GrilleBroken
diff --git a/Resources/Textures/Effects/electricity.rsi/electrified.png b/Resources/Textures/Effects/electricity.rsi/electrified.png
new file mode 100644 (file)
index 0000000..ec6a863
Binary files /dev/null and b/Resources/Textures/Effects/electricity.rsi/electrified.png differ
diff --git a/Resources/Textures/Effects/electricity.rsi/meta.json b/Resources/Textures/Effects/electricity.rsi/meta.json
new file mode 100644 (file)
index 0000000..0b762de
--- /dev/null
@@ -0,0 +1,24 @@
+{
+  "version": 1,
+  "license": "CC-BY-SA-3.0",
+  "copyright": "made by Aleksh#7552 (Discord) / Alekshhh (Github) for SS14",
+  "size": {
+    "x": 32,
+    "y": 32
+  },
+  "states": [
+    {
+      "name": "electrified",
+      "delays": [
+        [
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1
+        ]
+      ]
+    }
+  ]
+}
\ No newline at end of file