From 4ade6f60ff2f5ced7053a9ef8ec57015f365fd96 Mon Sep 17 00:00:00 2001 From: Slava0135 <40753025+Slava0135@users.noreply.github.com> Date: Sat, 29 Apr 2023 23:05:10 +0300 Subject: [PATCH] Electrified grille sparks effect (#15178) * 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 --- .../ActivatedElectrifiedComponent.cs | 14 + .../Components/ElectrifiedComponent.cs | 105 ++- .../Components/ElectrocutionComponent.cs | 31 +- .../Electrocution/ElectrocutionSystem.cs | 630 +++++++++--------- .../Electrocution/SharedElectrocution.cs | 15 + .../Entities/Structures/Walls/grille.yml | 16 +- .../Effects/electricity.rsi/electrified.png | Bin 0 -> 1047 bytes .../Effects/electricity.rsi/meta.json | 24 + 8 files changed, 457 insertions(+), 378 deletions(-) create mode 100644 Content.Server/Electrocution/Components/ActivatedElectrifiedComponent.cs create mode 100644 Content.Shared/Electrocution/SharedElectrocution.cs create mode 100644 Resources/Textures/Effects/electricity.rsi/electrified.png create mode 100644 Resources/Textures/Effects/electricity.rsi/meta.json diff --git a/Content.Server/Electrocution/Components/ActivatedElectrifiedComponent.cs b/Content.Server/Electrocution/Components/ActivatedElectrifiedComponent.cs new file mode 100644 index 0000000000..60c3cff880 --- /dev/null +++ b/Content.Server/Electrocution/Components/ActivatedElectrifiedComponent.cs @@ -0,0 +1,14 @@ +namespace Content.Server.Electrocution; + +/// +/// Updates every frame for short duration to check if electrifed entity is powered when activated, e.g to play animation +/// +[RegisterComponent] +public sealed class ActivatedElectrifiedComponent : Component +{ + /// + /// How long electrified entity will remain active + /// + [ViewVariables] + public float TimeLeft = 1f; +} diff --git a/Content.Server/Electrocution/Components/ElectrifiedComponent.cs b/Content.Server/Electrocution/Components/ElectrifiedComponent.cs index 0d2ed0a244..97a24ffe0c 100644 --- a/Content.Server/Electrocution/Components/ElectrifiedComponent.cs +++ b/Content.Server/Electrocution/Components/ElectrifiedComponent.cs @@ -1,77 +1,76 @@ using Robust.Shared.Audio; -namespace Content.Server.Electrocution +namespace Content.Server.Electrocution; + +/// +/// Component for things that shock users on touch. +/// +[RegisterComponent] +public sealed class ElectrifiedComponent : Component { - /// - /// Component for things that shock users on touch. - /// - [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; - /// - /// Shock time, in seconds. - /// - [DataField("shockTime")] - public float ShockTime { get; } = 8f; + /// + /// Shock time, in seconds. + /// + [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; } diff --git a/Content.Server/Electrocution/Components/ElectrocutionComponent.cs b/Content.Server/Electrocution/Components/ElectrocutionComponent.cs index a93b55a525..60c752c34d 100644 --- a/Content.Server/Electrocution/Components/ElectrocutionComponent.cs +++ b/Content.Server/Electrocution/Components/ElectrocutionComponent.cs @@ -1,16 +1,21 @@ -namespace Content.Server.Electrocution +namespace Content.Server.Electrocution; + +/// +/// Component for virtual electrocution entities (representing an in-progress shock). +/// +[RegisterComponent] +[Access(typeof(ElectrocutionSystem))] +public sealed class ElectrocutionComponent : Component { - /// - /// Component for virtual electrocution entities (representing an in-progress shock). - /// - [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; } diff --git a/Content.Server/Electrocution/ElectrocutionSystem.cs b/Content.Server/Electrocution/ElectrocutionSystem.cs index 811232a86a..11db6184e3 100644 --- a/Content.Server/Electrocution/ElectrocutionSystem.cs +++ b/Content.Server/Electrocution/ElectrocutionSystem.cs @@ -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(OnElectrifiedStartCollide); + SubscribeLocalEvent(OnElectrifiedAttacked); + SubscribeLocalEvent(OnElectrifiedHandInteract); + SubscribeLocalEvent(OnElectrifiedInteractUsing); + SubscribeLocalEvent(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(); + 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(OnElectrifiedStartCollide); - SubscribeLocalEvent(OnElectrifiedAttacked); - SubscribeLocalEvent(OnElectrifiedHandInteract); - SubscribeLocalEvent(OnElectrifiedInteractUsing); - SubscribeLocalEvent(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(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(); + while (query.MoveNext(out var uid, out var activated, out var electrified, out var transform)) { - // Update "in progress" electrocutions - - RemQueue finishedElectrocutionsQueue = new(); - foreach (var (electrocution, consumer) in EntityManager - .EntityQuery()) + 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(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(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(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(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(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(id, out var tryNode) && + tryNode.NodeGroup is IBasePowerNet { NetworkNode: { LastCombinedSupply: > 0 } }) + { + return tryNode; } + return null; } + } /// Entity being electrocuted. /// Source entity of the electrocution. @@ -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(electrocutionEntity) - .GetNode("electrocution"); + var electrocutionEntity = Spawn($"VirtualElectrocutionLoad{node.NodeGroupID}", sourceTransform.Coordinates); + var electrocutionNode = Comp(electrocutionEntity).GetNode("electrocution"); + var electrocutionComponent = Comp(electrocutionEntity); - var electrocutionComponent = EntityManager.GetComponent(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(uid, StatusEffectKey, time, refresh, - statusEffects)) - return false; + if (!Resolve(uid, ref statusEffects, false) || + !_statusEffects.CanApplyEffect(uid, StatusEffectKey, statusEffects)) + { + return false; + } + + if (!_statusEffects.TryAddStatusEffect(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(DamageType), dmg), origin: sourceUid); + if (shockDamage is { } dmg) + { + var actual = _damageable.TryChangeDamage(uid, + new DamageSpecifier(_prototypeManager.Index(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(); - - 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 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(); - 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 visited, + List<(EntityUid entity, int depth)> all) + { + all.Add((entity, depth)); + visited.Add(entity); + + if (TryComp(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(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(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 index 0000000000..4060856d4d --- /dev/null +++ b/Content.Shared/Electrocution/SharedElectrocution.cs @@ -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 +} diff --git a/Resources/Prototypes/Entities/Structures/Walls/grille.yml b/Resources/Prototypes/Entities/Structures/Walls/grille.yml index b3123dc6a7..9ebacb7779 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/grille.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/grille.yml @@ -17,7 +17,13 @@ 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 @@ -71,6 +77,14 @@ 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 index 0000000000000000000000000000000000000000..ec6a863bf93c7efc5240508ec7937263d199113b GIT binary patch literal 1047 zcmV+y1nB#TP)#Uev_Jt!2u`4tD1a8&D|afO5_&*dJ>>B6HZR19Lm2~H|LvzYYy4lj zC(1fQ-l+gU`Pwc`g#gz9h|={K9FSc)6io*}QILmgN)Q0R_#xpod zpn5^{Udg2V3;?qI(?_)!9m_7v?WGi_IZg~glA$O7&=@#XL@nWA3X`0q6qJje1bc)j z05}Tp3?MaH&fL;_LRAtoyQfKek|iokLsu>VcudwI0cSZBGp?k{;{ z+OTn(wrc?AZk0Qjlma|fOs$Z}HJGkWP)fn2m?}NCh>=i&sDb`SO05fuwNeIF$(klSX#?T!AfD`u&W&=r&s^ojv zTC%tTkS0Q{3AggWX}tMY-6Vjt1Jo+R_(8 z+L&2*L;8HrAt*Mp*h}HV0IWLXR=w{f1htHl4IpkG!tRmPdtaeLM><|F06fqC$*|CS`eq#VdQoV^jK9)V1GSqRU>)bV z-mn`0ac#=8wU;2V09coFaU!7sJpVx-vPCcG2H-{cdGC?G(ZUT0v+rX803l(6!9#+x zZU8|CBSzT`AhnP!y1bT!$J2x9_>_Fa24HP@BUYyc;mSCPOEU)m1sXlZ`4{vsa7N{U R<_!P<002ovPDHLkV1ix6$mIY4 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Effects/electricity.rsi/meta.json b/Resources/Textures/Effects/electricity.rsi/meta.json new file mode 100644 index 0000000000..0b762debd0 --- /dev/null +++ b/Resources/Textures/Effects/electricity.rsi/meta.json @@ -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 -- 2.51.2