From 8960987643cbddb16aa74ed887d0edd5a7de19c5 Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sat, 13 Jul 2024 00:49:44 +1000 Subject: [PATCH 01/32] it works --- .../Effects/ElectricityAnomalySystem.cs | 6 +- .../Kitchen/EntitySystems/MicrowaveSystem.cs | 5 +- .../Components/LightningTargetComponent.cs | 11 +- Content.Server/Lightning/LightningSystem.cs | 249 +++++++++++++++--- .../Psionics/Glimmer/GlimmerReactiveSystem.cs | 8 +- .../EntitySystem/LightningArcShooterSystem.cs | 6 +- 6 files changed, 227 insertions(+), 58 deletions(-) diff --git a/Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs b/Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs index 8b5a72449f3..afae42075c9 100644 --- a/Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs @@ -32,7 +32,7 @@ private void OnPulse(Entity anomaly, ref AnomalyPul int boltCount = (int)MathF.Floor(MathHelper.Lerp((float)anomaly.Comp.MinBoltCount, (float)anomaly.Comp.MaxBoltCount, args.Severity)); - _lightning.ShootRandomLightnings(anomaly, range, boltCount); + _lightning.ShootRandomLightnings(anomaly, range, boltCount, 50000f); } private void OnSupercritical(Entity anomaly, ref AnomalySupercriticalEvent args) @@ -40,7 +40,9 @@ private void OnSupercritical(Entity anomaly, ref An var range = anomaly.Comp.MaxElectrocuteRange * 3; _emp.EmpPulse(_transform.GetMapCoordinates(anomaly), range, anomaly.Comp.EmpEnergyConsumption, anomaly.Comp.EmpDisabledDuration); - _lightning.ShootRandomLightnings(anomaly, range, anomaly.Comp.MaxBoltCount * 3, arcDepth: 3); + _lightning.ShootRandomLightnings(anomaly, range, anomaly.Comp.MaxBoltCount * 3, 500000f, + maxArcs: 3 + ); } public override void Update(float frameTime) diff --git a/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs b/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs index 212383c463a..80eef020b8d 100644 --- a/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs @@ -403,7 +403,10 @@ private void RollMalfunction(Entity diff --git a/Content.Server/Lightning/Components/LightningTargetComponent.cs b/Content.Server/Lightning/Components/LightningTargetComponent.cs index 6d806b3fe7e..aa454b15775 100644 --- a/Content.Server/Lightning/Components/LightningTargetComponent.cs +++ b/Content.Server/Lightning/Components/LightningTargetComponent.cs @@ -13,17 +13,10 @@ namespace Content.Server.Lightning.Components; public sealed partial class LightningTargetComponent : Component { /// - /// The probability that this target will not be ignored by a lightning strike. This is necessary for Tesla's balance. + /// The probability weighting of being stuck by lightning, compared against other nearby lightning targets. /// [DataField, ViewVariables(VVAccess.ReadWrite)] - public float HitProbability = 1f; - - /// - /// Priority level for selecting a lightning target. - /// - [DataField, ViewVariables(VVAccess.ReadWrite)] - public int Priority; - + public float Weighting = 1f; /// /// Lightning has a number of bounces into neighboring targets. /// This number controls how many bounces the lightning bolt has left after hitting that target. diff --git a/Content.Server/Lightning/LightningSystem.cs b/Content.Server/Lightning/LightningSystem.cs index 4f975a60fda..4fdb9f00240 100644 --- a/Content.Server/Lightning/LightningSystem.cs +++ b/Content.Server/Lightning/LightningSystem.cs @@ -3,7 +3,11 @@ using Content.Server.Beam.Components; using Content.Server.Lightning.Components; using Content.Shared.Lightning; +using Content.Shared.Random.Helpers; +using FastAccessors; +using Robust.Server.GameObjects; using Robust.Shared.Random; +using Robust.Shared.Utility; namespace Content.Server.Lightning; @@ -20,6 +24,16 @@ public sealed class LightningSystem : SharedLightningSystem [Dependency] private readonly BeamSystem _beam = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly TransformSystem _transform = default!; + + // a priority queue is required to iterate through Arcs of various depth + private PriorityQueue _lightningQueue = new PriorityQueue(); + + // a dictionary allows insertation / removal of new lightning bolt data 'infinitely' without throwing errors + // don't worry, the data gets deleted afterwards + private Dictionary _lightningDict = new Dictionary(); + private int _lightningId = 0; + private int NextId() { return _lightningId++; } public override void Initialize() { @@ -39,71 +53,220 @@ private void OnRemove(EntityUid uid, LightningComponent component, ComponentRemo } /// - /// Fires lightning from user to target + /// Fires a lightning bolt from one entity to another + /// + public void ShootLightning(EntityUid user, EntityUid target, float totalCharge, + int maxArcs = 1, + float arcRange = 5f, + int arcForks = 1, + bool allowLooping = false, + string lightningPrototype = "Lightning", + float damage = 15, + bool electrocute = true, + bool triggerLightningEvents = true, + ExplosionState explosionState = ExplosionState.AlwaysExplosion + ) + { + int id = NextId(); + LightningContext context = new LightningContext + { + Id = id, + Arcs = [], + Charge = totalCharge, + MaxArcs = maxArcs, + ArcRange = arcRange, + ArcForks = arcForks, + AllowLooping = allowLooping, + History = [], + LightningPrototype = lightningPrototype, + Damage = damage, + Electrocute = electrocute, + TriggerLightningEvents = triggerLightningEvents, + ExplosionState = explosionState + }; + _lightningDict[id] = context; + + LightningArc lightningArc = new LightningArc + { + User = user, + Target = target, + ContextId = id, + }; + StageLightningArc(lightningArc); + } + + /// + /// Looks for objects with a LightningTarget component in the radius, and fire lightning at (weighted) random targets + /// + public void ShootRandomLightnings(EntityUid user, float lightningRange, int lightningCount, float lightningChargePer, + int maxArcs = 1, + float arcRange = 5f, + int arcForks = 1, + bool allowLooping = false, + string lightningPrototype = "Lightning", + float damage = 15, + bool electrocute = true, + bool triggerLightningEvents = true, + ExplosionState explosionState = ExplosionState.AlwaysExplosion + ) + { + var targets = _lookup.GetComponentsInRange(_transform.GetMapCoordinates(user), lightningRange).ToList(); // TODO - use collision groups + Dictionary weights = targets.ToDictionary(x => x.Owner.Id.ToString() ?? "", x => x.Weighting); + weights.Remove(user.ToString()); + + for (int i = 0; i < lightningCount; i++) + { + EntityUid target = EntityUid.Parse(_random.Pick(weights)); + + ShootLightning(user, target, lightningChargePer, + maxArcs: maxArcs, + arcRange: arcRange, + arcForks: arcForks, + allowLooping: allowLooping, + lightningPrototype: lightningPrototype, + damage: damage, + electrocute: electrocute, + triggerLightningEvents: triggerLightningEvents, + explosionState: explosionState + ); + } + } + + /// + /// Loads a LightningArc to be fired, and then checks for chain targets /// - /// Where the lightning fires from - /// Where the lightning fires to - /// The prototype for the lightning to be created - /// if the lightnings being fired should trigger lightning events. - public void ShootLightning(EntityUid user, EntityUid target, string lightningPrototype = "Lightning", bool triggerLightningEvents = true) + private void StageLightningArc(LightningArc lightningArc, int arcDepth = 0) { - var spriteState = LightningRandomizer(); - _beam.TryCreateBeam(user, target, lightningPrototype, spriteState); + var user = lightningArc.User; + var target = lightningArc.Target; + var contextId = lightningArc.ContextId; + + // get the context and check to see if there are any arcs remaining + if (!_lightningDict.TryGetValue(contextId, out LightningContext context) + || context.Arcs.Count() >= context.MaxArcs) + { + NextLightningArc(); + return; + } + + // add this arc to the pool of arcs + context.Arcs.Add(lightningArc); + + // check for any more targets + var targets = _lookup.GetComponentsInRange(_transform.GetMapCoordinates(user), context.ArcRange).ToList(); // TODO - use collision groups + Dictionary weights = targets.ToDictionary(x => x.Owner.Id.ToString() ?? "", x => x.Weighting); - if (triggerLightningEvents) // we don't want certain prototypes to trigger lightning level events + if (!context.History.Contains(user)) + context.History.Add(user); + + if (!context.AllowLooping) { - var ev = new HitByLightningEvent(user, target); - RaiseLocalEvent(target, ref ev); + Dictionary exception = context.History.ToDictionary(x => x.ToString(), x => 0f); + weights.Except(exception); } + else + weights.Remove(user.ToString()); + + // do the bounce + for (int i = 0; i < context.ArcForks; i++) + { + EntityUid nextTarget = EntityUid.Parse(_random.Pick(weights)); + LightningArc nextArc = new LightningArc { User = target, Target = nextTarget, ContextId = context.Id, ArcDepth = arcDepth + 1 }; + _lightningQueue.Enqueue(nextArc, arcDepth + 1); + } + + // update the context and select the next lightning arc + _lightningDict[context.Id] = context; + NextLightningArc(); } + /// + /// Dequeues a LightningArc from the priority queue and stages it + /// If the priority queue is empty, fire lightning effects instead + /// + private void NextLightningArc() + { + if (_lightningQueue.Count <= 0) + { + foreach (KeyValuePair entry in _lightningDict) + { + DoLightning(entry.Value); + } + _lightningDict.Clear(); + return; + } + + LightningArc lightningArc = _lightningQueue.Dequeue(); + //if (!_lightningDict.TryGetValue(lightningArc.ContextId, out LightningContext context)) + //{ + // NextLightningArc(); + // return; + //} + StageLightningArc(lightningArc, lightningArc.ArcDepth); + } /// - /// Looks for objects with a LightningTarget component in the radius, prioritizes them, and hits the highest priority targets with lightning. + /// Fires all loaded LightningArcs /// - /// Where the lightning fires from - /// Targets selection radius - /// Number of lightning bolts - /// The prototype for the lightning to be created - /// how many times to recursively fire lightning bolts from the target points of the first shot. - /// if the lightnings being fired should trigger lightning events. - public void ShootRandomLightnings(EntityUid user, float range, int boltCount, string lightningPrototype = "Lightning", int arcDepth = 0, bool triggerLightningEvents = true) + private void DoLightning(LightningContext context) { - //To Do: add support to different priority target tablem for different lightning types - //To Do: Remove Hardcode LightningTargetComponent (this should be a parameter of the SharedLightningComponent) - //To Do: This is still pretty bad for perf but better than before and at least it doesn't re-allocate - // several hashsets every time - - var targets = _lookup.GetComponentsInRange(Transform(user).MapPosition, range).ToList(); - _random.Shuffle(targets); - targets.Sort((x, y) => y.Priority.CompareTo(x.Priority)); - - int shootedCount = 0; - int count = -1; - while(shootedCount < boltCount) + for (int i = 0; i < context.Arcs.Count; i++) { - count++; + LightningArc lightningArc = context.Arcs[i]; - if (count >= targets.Count) { break; } + // use up charge from the total pool + float discharge = context.Charge * (1f / (context.Arcs.Count - i)); + context.Charge -= discharge; - var curTarget = targets[count]; - if (!_random.Prob(curTarget.HitProbability)) //Chance to ignore target - continue; + // zap + var spriteState = LightningRandomizer(); + _beam.TryCreateBeam(lightningArc.User, lightningArc.Target, context.LightningPrototype, spriteState); - ShootLightning(user, targets[count].Owner, lightningPrototype, triggerLightningEvents); - if (arcDepth - targets[count].LightningResistance > 0) + // we may not want to trigger certain lightning events + if (context.TriggerLightningEvents) { - ShootRandomLightnings(targets[count].Owner, range, 1, lightningPrototype, arcDepth - targets[count].LightningResistance, triggerLightningEvents); + var ev = new HitByLightningEvent(discharge, context); + RaiseLocalEvent(lightningArc.Target, ref ev); } - shootedCount++; + + if (context.Charge <= 0f) + break; } } } +public record struct LightningArc( + EntityUid User, + EntityUid Target, + int ContextId, + int ArcDepth +); +public record struct LightningContext( + int Id, + List Arcs, + float Charge, + int MaxArcs, + float ArcRange, + int ArcForks, + bool AllowLooping, + List History, + string LightningPrototype, + float Damage, + bool Electrocute, + bool TriggerLightningEvents, + ExplosionState ExplosionState +); /// /// Raised directed on the target when an entity becomes the target of a lightning strike (not when touched) /// -/// The entity that created the lightning -/// The entity that was struck by lightning. +/// The energy (J) that was discharged by the lightning bolt. +/// The field that encapsulates the data used to make the lightning bolt. [ByRefEvent] -public readonly record struct HitByLightningEvent(EntityUid Source, EntityUid Target); +public readonly record struct HitByLightningEvent(float Discharge, LightningContext Context); + +public enum ExplosionState : sbyte +{ + NoExplosion = 0, + ChargeExplosion = 1, + AlwaysExplosion = 2 +} diff --git a/Content.Server/Nyanotrasen/Psionics/Glimmer/GlimmerReactiveSystem.cs b/Content.Server/Nyanotrasen/Psionics/Glimmer/GlimmerReactiveSystem.cs index da3b07d6dab..0fc0e261b58 100644 --- a/Content.Server/Nyanotrasen/Psionics/Glimmer/GlimmerReactiveSystem.cs +++ b/Content.Server/Nyanotrasen/Psionics/Glimmer/GlimmerReactiveSystem.cs @@ -287,7 +287,7 @@ private void Beam(EntityUid prober, EntityUid target, GlimmerTier tier, bool obe } - _lightning.ShootLightning(prober, target, beamproto); + _lightning.ShootLightning(prober, target, 500000, lightningPrototype: beamproto); BeamCooldown += 3f; } @@ -328,7 +328,11 @@ private void OnMeleeThrowOnHitAttempt(Entity ent args.Cancelled = true; args.Handled = true; - _lightning.ShootRandomLightnings(uid, 10, 2, "SuperchargedLightning", 2, false); + _lightning.ShootRandomLightnings(uid, 10f, 2, 500000f, + lightningPrototype: "SuperchargedLightning", + maxArcs: 2, + triggerLightningEvents: false + ); // Check if the parent of the user is alive, which will be the case if the user is an item and is being held. var zapTarget = _transformSystem.GetParentUid(args.User); diff --git a/Content.Server/Tesla/EntitySystem/LightningArcShooterSystem.cs b/Content.Server/Tesla/EntitySystem/LightningArcShooterSystem.cs index 9948537c3c9..ccabd568c40 100644 --- a/Content.Server/Tesla/EntitySystem/LightningArcShooterSystem.cs +++ b/Content.Server/Tesla/EntitySystem/LightningArcShooterSystem.cs @@ -44,6 +44,10 @@ public override void Update(float frameTime) private void ArcShoot(EntityUid uid, LightningArcShooterComponent component) { var arcs = _random.Next(1, component.MaxLightningArc); - _lightning.ShootRandomLightnings(uid, component.ShootRange, arcs, component.LightningPrototype, component.ArcDepth); + _lightning.ShootRandomLightnings(uid, component.ShootRange, arcs, 500000, + lightningPrototype: component.LightningPrototype, + maxArcs: component.ArcDepth, + arcForks: 2 + ); } } From e5c2f55b4c5602f05dfb1b85040a48fb54b351dc Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sat, 13 Jul 2024 09:51:21 +1000 Subject: [PATCH 02/32] add initial weightings --- Resources/Prototypes/Entities/Mobs/base.yml | 1 - .../Structures/Doors/Airlocks/base_structureairlocks.yml | 2 +- .../Structures/Machines/Computers/base_structurecomputers.yml | 2 +- .../Entities/Structures/Machines/base_structuremachines.yml | 4 ++-- .../Entities/Structures/Power/Generation/Tesla/coil.yml | 4 ++-- Resources/Prototypes/Entities/Structures/Power/apc.yml | 2 +- 6 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Resources/Prototypes/Entities/Mobs/base.yml b/Resources/Prototypes/Entities/Mobs/base.yml index 40cb0da95a1..5319d35419d 100644 --- a/Resources/Prototypes/Entities/Mobs/base.yml +++ b/Resources/Prototypes/Entities/Mobs/base.yml @@ -93,7 +93,6 @@ path: /Audio/Effects/hit_kick.ogg - type: Pullable - type: LightningTarget - priority: 2 lightningExplode: false # Used for mobs that can enter combat mode and can attack. diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml index 476e715175e..73aa59c320d 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml @@ -137,7 +137,7 @@ - type: StaticPrice price: 150 - type: LightningTarget - priority: 1 + weighting: 2 - type: Tag tags: - Airlock diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/base_structurecomputers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/base_structurecomputers.yml index 204e06c8600..6470c64e592 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/base_structurecomputers.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/base_structurecomputers.yml @@ -59,5 +59,5 @@ board: !type:Container ents: [] - type: LightningTarget - priority: 1 + weighting: 2 - type: RequireProjectileTarget diff --git a/Resources/Prototypes/Entities/Structures/Machines/base_structuremachines.yml b/Resources/Prototypes/Entities/Structures/Machines/base_structuremachines.yml index fb5ed4440a9..5da577f89d2 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/base_structuremachines.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/base_structuremachines.yml @@ -51,7 +51,7 @@ powerLoad: 1000 - type: ExtensionCableReceiver - type: LightningTarget - priority: 1 + weighting: 2 - type: entity abstract: true @@ -69,7 +69,7 @@ - machine_parts - machine_board - type: LightningTarget - priority: 1 + weighting: 2 - type: entity abstract: true diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/coil.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/coil.yml index f236bb8a41e..aa5b8047b73 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/coil.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/coil.yml @@ -63,7 +63,7 @@ - type: TeslaCoil chargeFromLightning: 500000 - type: LightningTarget - priority: 4 + weighting: 100 hitProbability: 0.5 lightningResistance: 10 lightningExplode: false @@ -149,7 +149,7 @@ False: { state: grounding_rod_open } - type: LightningSparking - type: LightningTarget - priority: 3 + weighting: 300 lightningResistance: 10 lightningExplode: false damageFromLightning: 0 diff --git a/Resources/Prototypes/Entities/Structures/Power/apc.yml b/Resources/Prototypes/Entities/Structures/Power/apc.yml index 7efa53e2542..81860ad459d 100644 --- a/Resources/Prototypes/Entities/Structures/Power/apc.yml +++ b/Resources/Prototypes/Entities/Structures/Power/apc.yml @@ -138,7 +138,7 @@ mediumVoltageNode: input lowVoltageNode: output - type: LightningTarget - priority: 1 + weighting: 2 - type: StaticPrice price: 500 From 4270666fd523a0fd5e4dccb730fb63d6d9f129b7 Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sat, 13 Jul 2024 10:39:37 +1000 Subject: [PATCH 03/32] tesla coils absorb lightning charge, reconsolidate lightning charge values --- Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs | 2 +- .../Nyanotrasen/Psionics/Glimmer/GlimmerReactiveSystem.cs | 2 +- Content.Server/Tesla/Components/TeslaCoilComponent.cs | 2 +- Content.Server/Tesla/EntitySystem/LightningArcShooterSystem.cs | 2 +- Content.Server/Tesla/EntitySystem/TeslaCoilSystem.cs | 2 +- .../Entities/Structures/Power/Generation/Tesla/coil.yml | 1 - 6 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs b/Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs index afae42075c9..c44269cb19b 100644 --- a/Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs @@ -40,7 +40,7 @@ private void OnSupercritical(Entity anomaly, ref An var range = anomaly.Comp.MaxElectrocuteRange * 3; _emp.EmpPulse(_transform.GetMapCoordinates(anomaly), range, anomaly.Comp.EmpEnergyConsumption, anomaly.Comp.EmpDisabledDuration); - _lightning.ShootRandomLightnings(anomaly, range, anomaly.Comp.MaxBoltCount * 3, 500000f, + _lightning.ShootRandomLightnings(anomaly, range, anomaly.Comp.MaxBoltCount * 3, 100000f, maxArcs: 3 ); } diff --git a/Content.Server/Nyanotrasen/Psionics/Glimmer/GlimmerReactiveSystem.cs b/Content.Server/Nyanotrasen/Psionics/Glimmer/GlimmerReactiveSystem.cs index 0fc0e261b58..a71f6a12e4d 100644 --- a/Content.Server/Nyanotrasen/Psionics/Glimmer/GlimmerReactiveSystem.cs +++ b/Content.Server/Nyanotrasen/Psionics/Glimmer/GlimmerReactiveSystem.cs @@ -328,7 +328,7 @@ private void OnMeleeThrowOnHitAttempt(Entity ent args.Cancelled = true; args.Handled = true; - _lightning.ShootRandomLightnings(uid, 10f, 2, 500000f, + _lightning.ShootRandomLightnings(uid, 10f, 2, 50000f, lightningPrototype: "SuperchargedLightning", maxArcs: 2, triggerLightningEvents: false diff --git a/Content.Server/Tesla/Components/TeslaCoilComponent.cs b/Content.Server/Tesla/Components/TeslaCoilComponent.cs index d9c7be6fe4d..aea381d5647 100644 --- a/Content.Server/Tesla/Components/TeslaCoilComponent.cs +++ b/Content.Server/Tesla/Components/TeslaCoilComponent.cs @@ -13,5 +13,5 @@ public sealed partial class TeslaCoilComponent : Component /// // To Do: Different lightning bolts have different powers and generate different amounts of energy [DataField, ViewVariables(VVAccess.ReadWrite)] - public float ChargeFromLightning = 50000f; + public float LightningChargeEfficiency = 0.9f; } diff --git a/Content.Server/Tesla/EntitySystem/LightningArcShooterSystem.cs b/Content.Server/Tesla/EntitySystem/LightningArcShooterSystem.cs index ccabd568c40..31bd3ce914c 100644 --- a/Content.Server/Tesla/EntitySystem/LightningArcShooterSystem.cs +++ b/Content.Server/Tesla/EntitySystem/LightningArcShooterSystem.cs @@ -44,7 +44,7 @@ public override void Update(float frameTime) private void ArcShoot(EntityUid uid, LightningArcShooterComponent component) { var arcs = _random.Next(1, component.MaxLightningArc); - _lightning.ShootRandomLightnings(uid, component.ShootRange, arcs, 500000, + _lightning.ShootRandomLightnings(uid, component.ShootRange, arcs, 100000, lightningPrototype: component.LightningPrototype, maxArcs: component.ArcDepth, arcForks: 2 diff --git a/Content.Server/Tesla/EntitySystem/TeslaCoilSystem.cs b/Content.Server/Tesla/EntitySystem/TeslaCoilSystem.cs index 4fd2f9b6ed0..9a2bc429e3d 100644 --- a/Content.Server/Tesla/EntitySystem/TeslaCoilSystem.cs +++ b/Content.Server/Tesla/EntitySystem/TeslaCoilSystem.cs @@ -24,7 +24,7 @@ private void OnHitByLightning(Entity coil, ref HitByLightnin { if (TryComp(coil, out var batteryComponent)) { - _battery.SetCharge(coil, batteryComponent.CurrentCharge + coil.Comp.ChargeFromLightning); + _battery.SetCharge(coil, batteryComponent.CurrentCharge + args.Discharge * coil.Comp.LightningChargeEfficiency); } } } diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/coil.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/coil.yml index aa5b8047b73..ca6f790d5e4 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/coil.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/coil.yml @@ -61,7 +61,6 @@ - type: BatteryDischarger activeSupplyRate: 15000 - type: TeslaCoil - chargeFromLightning: 500000 - type: LightningTarget weighting: 100 hitProbability: 0.5 From aa378672586e048adafb6035c39a5b6f985c5674 Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sat, 13 Jul 2024 10:48:25 +1000 Subject: [PATCH 04/32] remove electrified component from lightning --- Resources/Prototypes/Entities/Effects/lightning.yml | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/Resources/Prototypes/Entities/Effects/lightning.yml b/Resources/Prototypes/Entities/Effects/lightning.yml index 7afd1c07a0c..6f5a5bb5d87 100644 --- a/Resources/Prototypes/Entities/Effects/lightning.yml +++ b/Resources/Prototypes/Entities/Effects/lightning.yml @@ -11,8 +11,6 @@ shader: unshaded - type: Physics canCollide: false - - type: Electrified - requirePower: false - type: Lightning - type: PointLight enabled: true @@ -74,9 +72,6 @@ layers: - state: "blue_lightning" shader: unshaded - - type: Electrified - requirePower: false - shockDamage: 40 - type: Lightning canArc: true lightningPrototype: ChargedLightning @@ -93,8 +88,6 @@ layers: - state: "lightning_1" shader: unshaded - - type: Electrified - shockDamage: 0 - type: Lightning lightningPrototype: Spark - type: PointLight @@ -120,9 +113,6 @@ layers: - state: "yellow_lightning" shader: unshaded - - type: Electrified - requirePower: false - shockDamage: 50 - type: Lightning canArc: true lightningPrototype: SuperchargedLightning @@ -146,9 +136,6 @@ layers: - state: "red_lightning" shader: unshaded - - type: Electrified - requirePower: false - shockDamage: 60 - type: Lightning canArc: true lightningPrototype: HyperchargedLightning From cd93f054e2f382c8e69e94bb4f1866ce8dd07dca Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sat, 13 Jul 2024 12:17:06 +1000 Subject: [PATCH 05/32] allow parsing functions into lightning parameters --- Content.Server/Lightning/LightningSystem.cs | 72 +++++++++------------ 1 file changed, 31 insertions(+), 41 deletions(-) diff --git a/Content.Server/Lightning/LightningSystem.cs b/Content.Server/Lightning/LightningSystem.cs index 4fdb9f00240..88a7d2c0e82 100644 --- a/Content.Server/Lightning/LightningSystem.cs +++ b/Content.Server/Lightning/LightningSystem.cs @@ -63,26 +63,24 @@ public void ShootLightning(EntityUid user, EntityUid target, float totalCharge, string lightningPrototype = "Lightning", float damage = 15, bool electrocute = true, - bool triggerLightningEvents = true, - ExplosionState explosionState = ExplosionState.AlwaysExplosion + bool explode = true ) { int id = NextId(); LightningContext context = new LightningContext { Id = id, - Arcs = [], Charge = totalCharge, + Arcs = [], MaxArcs = maxArcs, - ArcRange = arcRange, - ArcForks = arcForks, - AllowLooping = allowLooping, History = [], - LightningPrototype = lightningPrototype, - Damage = damage, - Electrocute = electrocute, - TriggerLightningEvents = triggerLightningEvents, - ExplosionState = explosionState + ArcRange = (LightningContext context) => arcRange, + ArcForks = (LightningContext context) => arcForks, + AllowLooping = (LightningContext context) => allowLooping, + LightningPrototype = (float discharge, LightningContext context) => lightningPrototype, + Damage = (float discharge, LightningContext context) => damage, + Electrocute = (float discharge, LightningContext context) => electrocute, + Explode = (float discharge, LightningContext context) => explode, }; _lightningDict[id] = context; @@ -106,8 +104,7 @@ public void ShootRandomLightnings(EntityUid user, float lightningRange, int ligh string lightningPrototype = "Lightning", float damage = 15, bool electrocute = true, - bool triggerLightningEvents = true, - ExplosionState explosionState = ExplosionState.AlwaysExplosion + bool explode = true ) { var targets = _lookup.GetComponentsInRange(_transform.GetMapCoordinates(user), lightningRange).ToList(); // TODO - use collision groups @@ -126,8 +123,7 @@ public void ShootRandomLightnings(EntityUid user, float lightningRange, int ligh lightningPrototype: lightningPrototype, damage: damage, electrocute: electrocute, - triggerLightningEvents: triggerLightningEvents, - explosionState: explosionState + explode: explode ); } } @@ -153,13 +149,13 @@ private void StageLightningArc(LightningArc lightningArc, int arcDepth = 0) context.Arcs.Add(lightningArc); // check for any more targets - var targets = _lookup.GetComponentsInRange(_transform.GetMapCoordinates(user), context.ArcRange).ToList(); // TODO - use collision groups + var targets = _lookup.GetComponentsInRange(_transform.GetMapCoordinates(user), context.ArcRange(context)).ToList(); // TODO - use collision groups Dictionary weights = targets.ToDictionary(x => x.Owner.Id.ToString() ?? "", x => x.Weighting); if (!context.History.Contains(user)) context.History.Add(user); - if (!context.AllowLooping) + if (!context.AllowLooping(context)) { Dictionary exception = context.History.ToDictionary(x => x.ToString(), x => 0f); weights.Except(exception); @@ -168,7 +164,7 @@ private void StageLightningArc(LightningArc lightningArc, int arcDepth = 0) weights.Remove(user.ToString()); // do the bounce - for (int i = 0; i < context.ArcForks; i++) + for (int i = 0; i < context.ArcForks(context); i++) { EntityUid nextTarget = EntityUid.Parse(_random.Pick(weights)); LightningArc nextArc = new LightningArc { User = target, Target = nextTarget, ContextId = context.Id, ArcDepth = arcDepth + 1 }; @@ -220,14 +216,11 @@ private void DoLightning(LightningContext context) // zap var spriteState = LightningRandomizer(); - _beam.TryCreateBeam(lightningArc.User, lightningArc.Target, context.LightningPrototype, spriteState); + _beam.TryCreateBeam(lightningArc.User, lightningArc.Target, context.LightningPrototype(discharge, context), spriteState); // we may not want to trigger certain lightning events - if (context.TriggerLightningEvents) - { - var ev = new HitByLightningEvent(discharge, context); - RaiseLocalEvent(lightningArc.Target, ref ev); - } + var ev = new HitByLightningEvent(discharge, context); + RaiseLocalEvent(lightningArc.Target, ref ev); if (context.Charge <= 0f) break; @@ -241,19 +234,23 @@ public record struct LightningArc( int ArcDepth ); public record struct LightningContext( + // Core data used by the LightningContext, do not touch! int Id, - List Arcs, float Charge, + List Arcs, int MaxArcs, - float ArcRange, - int ArcForks, - bool AllowLooping, List History, - string LightningPrototype, - float Damage, - bool Electrocute, - bool TriggerLightningEvents, - ExplosionState ExplosionState + + // Staging data before charge is even considered + Func ArcRange, + Func ArcForks, + Func AllowLooping, + + // Effect data which can take discharge into account + Func LightningPrototype, + Func Damage, + Func Electrocute, + Func Explode ); /// @@ -262,11 +259,4 @@ ExplosionState ExplosionState /// The energy (J) that was discharged by the lightning bolt. /// The field that encapsulates the data used to make the lightning bolt. [ByRefEvent] -public readonly record struct HitByLightningEvent(float Discharge, LightningContext Context); - -public enum ExplosionState : sbyte -{ - NoExplosion = 0, - ChargeExplosion = 1, - AlwaysExplosion = 2 -} +public readonly record struct HitByLightningEvent(float Discharge, LightningContext Context); \ No newline at end of file From 2064a49e8766b93815103528d9f2475e66d9f62c Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sat, 13 Jul 2024 13:51:06 +1000 Subject: [PATCH 06/32] add LightningContext constructor --- .../Kitchen/EntitySystems/MicrowaveSystem.cs | 3 +- Content.Server/Lightning/LightningSystem.cs | 120 +++++++++++++----- .../Psionics/Glimmer/GlimmerReactiveSystem.cs | 3 +- 3 files changed, 89 insertions(+), 37 deletions(-) diff --git a/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs b/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs index 80eef020b8d..7644e74e758 100644 --- a/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs @@ -405,7 +405,8 @@ private void RollMalfunction(Entity _lightningDict = new Dictionary(); - private int _lightningId = 0; + private int _lightningId = -1; private int NextId() { return _lightningId++; } public override void Initialize() @@ -66,14 +66,10 @@ public void ShootLightning(EntityUid user, EntityUid target, float totalCharge, bool explode = true ) { - int id = NextId(); - LightningContext context = new LightningContext + LightningContext context = new LightningContext() { - Id = id, Charge = totalCharge, - Arcs = [], MaxArcs = maxArcs, - History = [], ArcRange = (LightningContext context) => arcRange, ArcForks = (LightningContext context) => arcForks, AllowLooping = (LightningContext context) => allowLooping, @@ -82,13 +78,24 @@ public void ShootLightning(EntityUid user, EntityUid target, float totalCharge, Electrocute = (float discharge, LightningContext context) => electrocute, Explode = (float discharge, LightningContext context) => explode, }; - _lightningDict[id] = context; + + ShootLightning(user, target, context); + } + + /// + /// Fires a lightning bolt from one entity to another + /// + public void ShootLightning(EntityUid user, EntityUid target, LightningContext context) + { + int id = NextId(); + context.Id = id; + _lightningDict[context.Id] = context; LightningArc lightningArc = new LightningArc { User = user, Target = target, - ContextId = id, + ContextId = context.Id, }; StageLightningArc(lightningArc); } @@ -106,6 +113,27 @@ public void ShootRandomLightnings(EntityUid user, float lightningRange, int ligh bool electrocute = true, bool explode = true ) + { + LightningContext context = new LightningContext() + { + Charge = lightningChargePer, + MaxArcs = maxArcs, + ArcRange = (LightningContext context) => arcRange, + ArcForks = (LightningContext context) => arcForks, + AllowLooping = (LightningContext context) => allowLooping, + LightningPrototype = (float discharge, LightningContext context) => lightningPrototype, + Damage = (float discharge, LightningContext context) => damage, + Electrocute = (float discharge, LightningContext context) => electrocute, + Explode = (float discharge, LightningContext context) => explode, + }; + + ShootRandomLightnings(user, lightningRange, lightningCount, context); + } + + /// + /// Looks for objects with a LightningTarget component in the radius, and fire lightning at (weighted) random targets + /// + public void ShootRandomLightnings(EntityUid user, float lightningRange, int lightningCount, LightningContext context) { var targets = _lookup.GetComponentsInRange(_transform.GetMapCoordinates(user), lightningRange).ToList(); // TODO - use collision groups Dictionary weights = targets.ToDictionary(x => x.Owner.Id.ToString() ?? "", x => x.Weighting); @@ -115,16 +143,7 @@ public void ShootRandomLightnings(EntityUid user, float lightningRange, int ligh { EntityUid target = EntityUid.Parse(_random.Pick(weights)); - ShootLightning(user, target, lightningChargePer, - maxArcs: maxArcs, - arcRange: arcRange, - arcForks: arcForks, - allowLooping: allowLooping, - lightningPrototype: lightningPrototype, - damage: damage, - electrocute: electrocute, - explode: explode - ); + ShootLightning(user, target, context); } } @@ -139,7 +158,7 @@ private void StageLightningArc(LightningArc lightningArc, int arcDepth = 0) // get the context and check to see if there are any arcs remaining if (!_lightningDict.TryGetValue(contextId, out LightningContext context) - || context.Arcs.Count() >= context.MaxArcs) + || context.Arcs.Count >= context.MaxArcs) { NextLightningArc(); return; @@ -150,11 +169,21 @@ private void StageLightningArc(LightningArc lightningArc, int arcDepth = 0) // check for any more targets var targets = _lookup.GetComponentsInRange(_transform.GetMapCoordinates(user), context.ArcRange(context)).ToList(); // TODO - use collision groups - Dictionary weights = targets.ToDictionary(x => x.Owner.Id.ToString() ?? "", x => x.Weighting); + // if there aren't any more targets, update the context and exit + if (targets.Count == 0) + { + _lightningDict[context.Id] = context; + NextLightningArc(); + return; + } + + // otherwise get the weighting of every target to use _random.Pick(Dictionary) + Dictionary weights = targets.ToDictionary(x => x.Owner.Id.ToString() ?? "", x => x.Weighting); if (!context.History.Contains(user)) context.History.Add(user); + // depending on AllowLooping, remove previously visited entities from the targeting list if (!context.AllowLooping(context)) { Dictionary exception = context.History.ToDictionary(x => x.ToString(), x => 0f); @@ -233,25 +262,46 @@ public record struct LightningArc( int ContextId, int ArcDepth ); -public record struct LightningContext( - // Core data used by the LightningContext, do not touch! - int Id, - float Charge, - List Arcs, - int MaxArcs, - List History, +public record struct LightningContext +{ + // These are not parameters, and are handled by the LightningSystem + public int Id; + public List Arcs; + public List History; + + // Initial data that shouldn't be changed by staging + public float Charge; + public int MaxArcs; // Staging data before charge is even considered - Func ArcRange, - Func ArcForks, - Func AllowLooping, + public Func ArcRange; + public Func ArcForks; + public Func AllowLooping; // Effect data which can take discharge into account - Func LightningPrototype, - Func Damage, - Func Electrocute, - Func Explode -); + public Func LightningPrototype; + public Func Damage; + public Func Electrocute; + public Func Explode; + + public LightningContext() + { + Arcs = []; + History = []; + + Charge = 50000f; + MaxArcs = 1; + + ArcRange = (LightningContext context) => 3.5f; + ArcForks = (LightningContext context) => 1; + AllowLooping = (LightningContext context) => true; + + LightningPrototype = (float discharge, LightningContext context) => "Lightning"; + Damage = (float discharge, LightningContext context) => 15f; + Electrocute = (float discharge, LightningContext context) => true; + Explode = (float discharge, LightningContext context) => true; + } +}; /// /// Raised directed on the target when an entity becomes the target of a lightning strike (not when touched) diff --git a/Content.Server/Nyanotrasen/Psionics/Glimmer/GlimmerReactiveSystem.cs b/Content.Server/Nyanotrasen/Psionics/Glimmer/GlimmerReactiveSystem.cs index a71f6a12e4d..118bb8eda98 100644 --- a/Content.Server/Nyanotrasen/Psionics/Glimmer/GlimmerReactiveSystem.cs +++ b/Content.Server/Nyanotrasen/Psionics/Glimmer/GlimmerReactiveSystem.cs @@ -331,7 +331,8 @@ private void OnMeleeThrowOnHitAttempt(Entity ent _lightning.ShootRandomLightnings(uid, 10f, 2, 50000f, lightningPrototype: "SuperchargedLightning", maxArcs: 2, - triggerLightningEvents: false + electrocute: false, + explode: false ); // Check if the parent of the user is alive, which will be the case if the user is an item and is being held. From a3e2b4cbcc185953ed20cc380798b79275383202 Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sat, 13 Jul 2024 15:18:52 +1000 Subject: [PATCH 07/32] fixes various Invalid Weight errors --- Content.Server/Lightning/LightningSystem.cs | 41 ++++++++++++++++----- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/Content.Server/Lightning/LightningSystem.cs b/Content.Server/Lightning/LightningSystem.cs index 73dd42b5d46..10aacda03d1 100644 --- a/Content.Server/Lightning/LightningSystem.cs +++ b/Content.Server/Lightning/LightningSystem.cs @@ -1,4 +1,6 @@ +using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Runtime.InteropServices; using Content.Server.Beam; using Content.Server.Beam.Components; using Content.Server.Lightning.Components; @@ -6,6 +8,7 @@ using Content.Shared.Random.Helpers; using FastAccessors; using Robust.Server.GameObjects; +using Robust.Shared.Map; using Robust.Shared.Random; using Robust.Shared.Utility; @@ -25,6 +28,7 @@ public sealed class LightningSystem : SharedLightningSystem [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly IEntityManager _entMan = default!; // a priority queue is required to iterate through Arcs of various depth private PriorityQueue _lightningQueue = new PriorityQueue(); @@ -103,7 +107,7 @@ public void ShootLightning(EntityUid user, EntityUid target, LightningContext co /// /// Looks for objects with a LightningTarget component in the radius, and fire lightning at (weighted) random targets /// - public void ShootRandomLightnings(EntityUid user, float lightningRange, int lightningCount, float lightningChargePer, + public void ShootRandomLightnings(EntityUid user, float lightningRange, int lightningCount, float lightningChargePer, EntityCoordinates? queryPosition = null, int maxArcs = 1, float arcRange = 5f, int arcForks = 1, @@ -133,10 +137,15 @@ public void ShootRandomLightnings(EntityUid user, float lightningRange, int ligh /// /// Looks for objects with a LightningTarget component in the radius, and fire lightning at (weighted) random targets /// - public void ShootRandomLightnings(EntityUid user, float lightningRange, int lightningCount, LightningContext context) + public void ShootRandomLightnings(EntityUid user, float lightningRange, int lightningCount, LightningContext context, EntityCoordinates? queryPosition = null) { - var targets = _lookup.GetComponentsInRange(_transform.GetMapCoordinates(user), lightningRange).ToList(); // TODO - use collision groups - Dictionary weights = targets.ToDictionary(x => x.Owner.Id.ToString() ?? "", x => x.Weighting); + // default the query location to the user's position + if (!queryPosition.HasValue) + queryPosition = Transform(user).Coordinates; + + if (!TryGetLightningTargets(queryPosition.Value, lightningRange, out var weights)) + return; + weights.Remove(user.ToString()); for (int i = 0; i < lightningCount; i++) @@ -147,6 +156,23 @@ public void ShootRandomLightnings(EntityUid user, float lightningRange, int ligh } } + private bool TryGetLightningTargets(EntityCoordinates queryPosition, float radius, [NotNullWhen(true)] out Dictionary? weights) + { + weights = null; + + var targets = _lookup.GetComponentsInRange( + //_transform.GetMapCoordinates(Transform(user).MapUid ?? EntityUid.Invalid, Transform(user).Coordinates), + queryPosition.ToMap(_entMan, _transform), + radius + ).ToList(); // TODO - use collision groups + + if (targets.Count == 0) + return false; + + weights = targets.ToDictionary(x => x.Owner.Id.ToString() ?? "", x => x.Weighting); + return true; + } + /// /// Loads a LightningArc to be fired, and then checks for chain targets /// @@ -168,18 +194,13 @@ private void StageLightningArc(LightningArc lightningArc, int arcDepth = 0) context.Arcs.Add(lightningArc); // check for any more targets - var targets = _lookup.GetComponentsInRange(_transform.GetMapCoordinates(user), context.ArcRange(context)).ToList(); // TODO - use collision groups - - // if there aren't any more targets, update the context and exit - if (targets.Count == 0) + if (!TryGetLightningTargets(Transform(target).Coordinates, context.ArcRange(context), out var weights)) { _lightningDict[context.Id] = context; NextLightningArc(); return; } - // otherwise get the weighting of every target to use _random.Pick(Dictionary) - Dictionary weights = targets.ToDictionary(x => x.Owner.Id.ToString() ?? "", x => x.Weighting); if (!context.History.Contains(user)) context.History.Add(user); From 51006c6edbef95bf10c9b2f0a8da7e3e43da21e3 Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sat, 13 Jul 2024 15:30:41 +1000 Subject: [PATCH 08/32] consolidations --- Content.Server/Lightning/LightningSystem.cs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/Content.Server/Lightning/LightningSystem.cs b/Content.Server/Lightning/LightningSystem.cs index 10aacda03d1..3744a86f1d8 100644 --- a/Content.Server/Lightning/LightningSystem.cs +++ b/Content.Server/Lightning/LightningSystem.cs @@ -107,7 +107,7 @@ public void ShootLightning(EntityUid user, EntityUid target, LightningContext co /// /// Looks for objects with a LightningTarget component in the radius, and fire lightning at (weighted) random targets /// - public void ShootRandomLightnings(EntityUid user, float lightningRange, int lightningCount, float lightningChargePer, EntityCoordinates? queryPosition = null, + public void ShootRandomLightnings(EntityUid user, float lightningRadius, int lightningCount, float lightningChargePer, EntityCoordinates? queryPosition = null, int maxArcs = 1, float arcRange = 5f, int arcForks = 1, @@ -131,21 +131,22 @@ public void ShootRandomLightnings(EntityUid user, float lightningRange, int ligh Explode = (float discharge, LightningContext context) => explode, }; - ShootRandomLightnings(user, lightningRange, lightningCount, context); + ShootRandomLightnings(user, lightningRadius, lightningCount, context); } /// /// Looks for objects with a LightningTarget component in the radius, and fire lightning at (weighted) random targets /// - public void ShootRandomLightnings(EntityUid user, float lightningRange, int lightningCount, LightningContext context, EntityCoordinates? queryPosition = null) + public void ShootRandomLightnings(EntityUid user, float lightningRadius, int lightningCount, LightningContext context, EntityCoordinates? queryPosition = null) { // default the query location to the user's position if (!queryPosition.HasValue) queryPosition = Transform(user).Coordinates; - if (!TryGetLightningTargets(queryPosition.Value, lightningRange, out var weights)) + if (!TryGetLightningTargets(queryPosition.Value, lightningRadius, out var weights)) return; + // remove the user to prevent lightning striking self weights.Remove(user.ToString()); for (int i = 0; i < lightningCount; i++) @@ -156,15 +157,17 @@ public void ShootRandomLightnings(EntityUid user, float lightningRange, int ligh } } + /// + /// Helper function that gets entities with LightningTarget component in a radius + /// private bool TryGetLightningTargets(EntityCoordinates queryPosition, float radius, [NotNullWhen(true)] out Dictionary? weights) { weights = null; var targets = _lookup.GetComponentsInRange( - //_transform.GetMapCoordinates(Transform(user).MapUid ?? EntityUid.Invalid, Transform(user).Coordinates), queryPosition.ToMap(_entMan, _transform), radius - ).ToList(); // TODO - use collision groups + ).ToList(); // TODO - use collision groups? if (targets.Count == 0) return false; @@ -211,6 +214,7 @@ private void StageLightningArc(LightningArc lightningArc, int arcDepth = 0) weights.Except(exception); } else + // remove the user regardless to prevent lightning striking self weights.Remove(user.ToString()); // do the bounce @@ -243,11 +247,6 @@ private void NextLightningArc() } LightningArc lightningArc = _lightningQueue.Dequeue(); - //if (!_lightningDict.TryGetValue(lightningArc.ContextId, out LightningContext context)) - //{ - // NextLightningArc(); - // return; - //} StageLightningArc(lightningArc, lightningArc.ArcDepth); } From adfe78b61386cb2c82426680d9c50a122108af59 Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sat, 13 Jul 2024 15:50:16 +1000 Subject: [PATCH 09/32] reimplement lightning shock --- Content.Server/Lightning/LightningSystem.cs | 6 ++-- .../Lightning/LightningTargetSystem.cs | 30 ++++++++++++------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/Content.Server/Lightning/LightningSystem.cs b/Content.Server/Lightning/LightningSystem.cs index 3744a86f1d8..bd8016fa56c 100644 --- a/Content.Server/Lightning/LightningSystem.cs +++ b/Content.Server/Lightning/LightningSystem.cs @@ -93,6 +93,7 @@ public void ShootLightning(EntityUid user, EntityUid target, LightningContext co { int id = NextId(); context.Id = id; + context.Invoker = user; _lightningDict[context.Id] = context; LightningArc lightningArc = new LightningArc @@ -268,7 +269,7 @@ private void DoLightning(LightningContext context) _beam.TryCreateBeam(lightningArc.User, lightningArc.Target, context.LightningPrototype(discharge, context), spriteState); // we may not want to trigger certain lightning events - var ev = new HitByLightningEvent(discharge, context); + var ev = new HitByLightningEvent(discharge, lightningArc.Target, context); RaiseLocalEvent(lightningArc.Target, ref ev); if (context.Charge <= 0f) @@ -286,6 +287,7 @@ public record struct LightningContext { // These are not parameters, and are handled by the LightningSystem public int Id; + public EntityUid Invoker; public List Arcs; public List History; @@ -329,4 +331,4 @@ public LightningContext() /// The energy (J) that was discharged by the lightning bolt. /// The field that encapsulates the data used to make the lightning bolt. [ByRefEvent] -public readonly record struct HitByLightningEvent(float Discharge, LightningContext Context); \ No newline at end of file +public readonly record struct HitByLightningEvent(float Discharge, EntityUid Target, LightningContext Context); \ No newline at end of file diff --git a/Content.Server/Lightning/LightningTargetSystem.cs b/Content.Server/Lightning/LightningTargetSystem.cs index ccaa74e9e26..b9178367c9c 100644 --- a/Content.Server/Lightning/LightningTargetSystem.cs +++ b/Content.Server/Lightning/LightningTargetSystem.cs @@ -1,3 +1,4 @@ +using Content.Server.Electrocution; using Content.Server.Explosion.EntitySystems; using Content.Server.Lightning; using Content.Server.Lightning.Components; @@ -12,6 +13,7 @@ public sealed class LightningTargetSystem : EntitySystem { [Dependency] private readonly DamageableSystem _damageable = default!; [Dependency] private readonly ExplosionSystem _explosionSystem = default!; + [Dependency] private readonly ElectrocutionSystem _electrocutionSystem = default!; public override void Initialize() { @@ -22,18 +24,26 @@ public override void Initialize() private void OnHitByLightning(Entity uid, ref HitByLightningEvent args) { - DamageSpecifier damage = new(); - damage.DamageDict.Add("Structural", uid.Comp.DamageFromLightning); - _damageable.TryChangeDamage(uid, damage, true); + if (args.Context.Electrocute(args.Discharge, args.Context)) + { + _electrocutionSystem.TryDoElectrocution(uid, args.Context.Invoker, (int) Math.Round(args.Context.Damage(args.Discharge, args.Context), 0), TimeSpan.FromSeconds(5f), true); + } - if (uid.Comp.LightningExplode) + if (args.Context.Explode(args.Discharge, args.Context)) { - _explosionSystem.QueueExplosion( - Transform(uid).MapPosition, - uid.Comp.ExplosionPrototype, - uid.Comp.TotalIntensity, uid.Comp.Dropoff, - uid.Comp.MaxTileIntensity, - canCreateVacuum: false); + DamageSpecifier damage = new(); + damage.DamageDict.Add("Structural", uid.Comp.DamageFromLightning); + _damageable.TryChangeDamage(uid, damage, true); + + if (uid.Comp.LightningExplode) + { + _explosionSystem.QueueExplosion( + Transform(uid).MapPosition, + uid.Comp.ExplosionPrototype, + uid.Comp.TotalIntensity, uid.Comp.Dropoff, + uid.Comp.MaxTileIntensity, + canCreateVacuum: false); + } } } } From f52115b927feb61bb3528bf3ac93dfe1b8f437a2 Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sat, 13 Jul 2024 16:15:31 +1000 Subject: [PATCH 10/32] lightning damage scales with charge --- Content.Server/Lightning/LightningSystem.cs | 11 +++++------ Content.Server/Lightning/LightningTargetSystem.cs | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Content.Server/Lightning/LightningSystem.cs b/Content.Server/Lightning/LightningSystem.cs index bd8016fa56c..769360f6f90 100644 --- a/Content.Server/Lightning/LightningSystem.cs +++ b/Content.Server/Lightning/LightningSystem.cs @@ -78,7 +78,6 @@ public void ShootLightning(EntityUid user, EntityUid target, float totalCharge, ArcForks = (LightningContext context) => arcForks, AllowLooping = (LightningContext context) => allowLooping, LightningPrototype = (float discharge, LightningContext context) => lightningPrototype, - Damage = (float discharge, LightningContext context) => damage, Electrocute = (float discharge, LightningContext context) => electrocute, Explode = (float discharge, LightningContext context) => explode, }; @@ -114,9 +113,8 @@ public void ShootRandomLightnings(EntityUid user, float lightningRadius, int lig int arcForks = 1, bool allowLooping = false, string lightningPrototype = "Lightning", - float damage = 15, bool electrocute = true, - bool explode = true + bool explode = false ) { LightningContext context = new LightningContext() @@ -127,7 +125,6 @@ public void ShootRandomLightnings(EntityUid user, float lightningRadius, int lig ArcForks = (LightningContext context) => arcForks, AllowLooping = (LightningContext context) => allowLooping, LightningPrototype = (float discharge, LightningContext context) => lightningPrototype, - Damage = (float discharge, LightningContext context) => damage, Electrocute = (float discharge, LightningContext context) => electrocute, Explode = (float discharge, LightningContext context) => explode, }; @@ -302,8 +299,9 @@ public record struct LightningContext // Effect data which can take discharge into account public Func LightningPrototype; - public Func Damage; public Func Electrocute; + public Func ElectrocuteDamage; + public Func ElectrocuteIgnoreInsulation; public Func Explode; public LightningContext() @@ -319,8 +317,9 @@ public LightningContext() AllowLooping = (LightningContext context) => true; LightningPrototype = (float discharge, LightningContext context) => "Lightning"; - Damage = (float discharge, LightningContext context) => 15f; Electrocute = (float discharge, LightningContext context) => true; + ElectrocuteDamage = (float discharge, LightningContext context) => discharge * 0.0002f; // damage increases by 1 for every 5000J + ElectrocuteIgnoreInsulation = (float discharge, LightningContext context) => false; Explode = (float discharge, LightningContext context) => true; } }; diff --git a/Content.Server/Lightning/LightningTargetSystem.cs b/Content.Server/Lightning/LightningTargetSystem.cs index b9178367c9c..0c6dfa671ec 100644 --- a/Content.Server/Lightning/LightningTargetSystem.cs +++ b/Content.Server/Lightning/LightningTargetSystem.cs @@ -26,7 +26,7 @@ private void OnHitByLightning(Entity uid, ref HitByLig { if (args.Context.Electrocute(args.Discharge, args.Context)) { - _electrocutionSystem.TryDoElectrocution(uid, args.Context.Invoker, (int) Math.Round(args.Context.Damage(args.Discharge, args.Context), 0), TimeSpan.FromSeconds(5f), true); + _electrocutionSystem.TryDoElectrocution(uid, args.Context.Invoker, (int) Math.Round(args.Context.ElectrocuteDamage(args.Discharge, args.Context), 0), TimeSpan.FromSeconds(5f), true, ignoreInsulation: args.Context.ElectrocuteIgnoreInsulation(args.Discharge, args.Context)); } if (args.Context.Explode(args.Discharge, args.Context)) From 907a6895a006995e3dc92155587d4c52f09d8d14 Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sat, 13 Jul 2024 17:17:08 +1000 Subject: [PATCH 11/32] fix lightning stacking --- Content.Server/Lightning/LightningSystem.cs | 24 +++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/Content.Server/Lightning/LightningSystem.cs b/Content.Server/Lightning/LightningSystem.cs index 769360f6f90..7d943f4aea9 100644 --- a/Content.Server/Lightning/LightningSystem.cs +++ b/Content.Server/Lightning/LightningSystem.cs @@ -149,9 +149,12 @@ public void ShootRandomLightnings(EntityUid user, float lightningRadius, int lig for (int i = 0; i < lightningCount; i++) { - EntityUid target = EntityUid.Parse(_random.Pick(weights)); + string stringTarget = _random.Pick(weights); + weights.Remove(stringTarget); + EntityUid target = EntityUid.Parse(stringTarget); - ShootLightning(user, target, context); + LightningContext clone = context.Clone(); + ShootLightning(user, target, clone); } } @@ -194,6 +197,9 @@ private void StageLightningArc(LightningArc lightningArc, int arcDepth = 0) // add this arc to the pool of arcs context.Arcs.Add(lightningArc); + if (!context.History.Contains(user)) + context.History.Add(user); + // check for any more targets if (!TryGetLightningTargets(Transform(target).Coordinates, context.ArcRange(context), out var weights)) { @@ -202,9 +208,6 @@ private void StageLightningArc(LightningArc lightningArc, int arcDepth = 0) return; } - if (!context.History.Contains(user)) - context.History.Add(user); - // depending on AllowLooping, remove previously visited entities from the targeting list if (!context.AllowLooping(context)) { @@ -280,7 +283,7 @@ public record struct LightningArc( int ContextId, int ArcDepth ); -public record struct LightningContext +public struct LightningContext { // These are not parameters, and are handled by the LightningSystem public int Id; @@ -322,6 +325,15 @@ public LightningContext() ElectrocuteIgnoreInsulation = (float discharge, LightningContext context) => false; Explode = (float discharge, LightningContext context) => true; } + + public LightningContext Clone() + { + LightningContext other = (LightningContext) MemberwiseClone(); + other.Arcs = new List(Arcs); + other.History = new List(History); + + return other; + } }; /// From 06a013281bf42f188b90d6ea1df6aab710926afb Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sat, 13 Jul 2024 17:18:16 +1000 Subject: [PATCH 12/32] another weighted pick fix --- Content.Server/Lightning/LightningSystem.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Content.Server/Lightning/LightningSystem.cs b/Content.Server/Lightning/LightningSystem.cs index 7d943f4aea9..fa871acb708 100644 --- a/Content.Server/Lightning/LightningSystem.cs +++ b/Content.Server/Lightning/LightningSystem.cs @@ -149,6 +149,9 @@ public void ShootRandomLightnings(EntityUid user, float lightningRadius, int lig for (int i = 0; i < lightningCount; i++) { + if (weights.Count <= 0) + break; + string stringTarget = _random.Pick(weights); weights.Remove(stringTarget); EntityUid target = EntityUid.Parse(stringTarget); From 6df2db88e752092d980d573c19468625898fb291 Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sat, 13 Jul 2024 17:45:16 +1000 Subject: [PATCH 13/32] fix tesla being shaded --- .../Entities/Structures/Power/Generation/Tesla/energyball.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/energyball.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/energyball.yml index 4567c6d0440..0676bac713d 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/energyball.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/energyball.yml @@ -92,9 +92,9 @@ - type: Sprite drawdepth: Effects sprite: Structures/Power/Generation/Tesla/energy_ball.rsi - shader: unshaded layers: - state: energy_ball + shader: unshaded - type: EmitSoundOnSpawn sound: path: /Audio/Effects/tesla_collapse.ogg @@ -135,8 +135,8 @@ - type: Sprite drawdepth: Effects sprite: Structures/Power/Generation/Tesla/energy_miniball.rsi - shader: unshaded layers: - state: tesla_projectile + shader: unshaded - type: Electrified requirePower: false From 722cb4b3bc323fab518658fc0aa5cdeb2e5f9c3e Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sat, 13 Jul 2024 18:22:12 +1000 Subject: [PATCH 14/32] cull exploding behaviour from LightningTarget, use Explosive instead --- .../Components/LightningTargetComponent.cs | 39 +------------------ Content.Server/Lightning/LightningSystem.cs | 2 +- .../Lightning/LightningTargetSystem.cs | 16 ++++---- 3 files changed, 9 insertions(+), 48 deletions(-) diff --git a/Content.Server/Lightning/Components/LightningTargetComponent.cs b/Content.Server/Lightning/Components/LightningTargetComponent.cs index aa454b15775..a7a976a684c 100644 --- a/Content.Server/Lightning/Components/LightningTargetComponent.cs +++ b/Content.Server/Lightning/Components/LightningTargetComponent.cs @@ -17,6 +17,7 @@ public sealed partial class LightningTargetComponent : Component /// [DataField, ViewVariables(VVAccess.ReadWrite)] public float Weighting = 1f; + /// /// Lightning has a number of bounces into neighboring targets. /// This number controls how many bounces the lightning bolt has left after hitting that target. @@ -24,42 +25,4 @@ public sealed partial class LightningTargetComponent : Component /// [DataField, ViewVariables(VVAccess.ReadWrite)] public int LightningResistance = 1; //by default, reduces the number of bounces from this target by 1 - - // BOOM PART - - /// - /// Will the entity explode after being struck by lightning? - /// - [DataField, ViewVariables(VVAccess.ReadWrite)] - public bool LightningExplode = true; - - /// - /// The explosion prototype to spawn - /// - [DataField, ViewVariables(VVAccess.ReadWrite)] - public ProtoId ExplosionPrototype = "Default"; - - /// - /// The total amount of intensity an explosion can achieve - /// - [DataField, ViewVariables(VVAccess.ReadWrite)] - public float TotalIntensity = 25f; - - /// - /// How quickly does the explosion's power slope? Higher = smaller area and more concentrated damage, lower = larger area and more spread out damage - /// - [DataField, ViewVariables(VVAccess.ReadWrite)] - public float Dropoff = 2f; - - /// - /// How much intensity can be applied per tile? - /// - [DataField, ViewVariables(VVAccess.ReadWrite)] - public float MaxTileIntensity = 5f; - - /// - /// how much structural damage the object takes from a lightning strike - /// - [DataField] - public FixedPoint2 DamageFromLightning = 1; } diff --git a/Content.Server/Lightning/LightningSystem.cs b/Content.Server/Lightning/LightningSystem.cs index fa871acb708..8dd3de9651e 100644 --- a/Content.Server/Lightning/LightningSystem.cs +++ b/Content.Server/Lightning/LightningSystem.cs @@ -114,7 +114,7 @@ public void ShootRandomLightnings(EntityUid user, float lightningRadius, int lig bool allowLooping = false, string lightningPrototype = "Lightning", bool electrocute = true, - bool explode = false + bool explode = true ) { LightningContext context = new LightningContext() diff --git a/Content.Server/Lightning/LightningTargetSystem.cs b/Content.Server/Lightning/LightningTargetSystem.cs index 0c6dfa671ec..8f2cfb5a89c 100644 --- a/Content.Server/Lightning/LightningTargetSystem.cs +++ b/Content.Server/Lightning/LightningTargetSystem.cs @@ -1,4 +1,5 @@ using Content.Server.Electrocution; +using Content.Server.Explosion.Components; using Content.Server.Explosion.EntitySystems; using Content.Server.Lightning; using Content.Server.Lightning.Components; @@ -31,19 +32,16 @@ private void OnHitByLightning(Entity uid, ref HitByLig if (args.Context.Explode(args.Discharge, args.Context)) { + /* DamageSpecifier damage = new(); damage.DamageDict.Add("Structural", uid.Comp.DamageFromLightning); _damageable.TryChangeDamage(uid, damage, true); + */ - if (uid.Comp.LightningExplode) - { - _explosionSystem.QueueExplosion( - Transform(uid).MapPosition, - uid.Comp.ExplosionPrototype, - uid.Comp.TotalIntensity, uid.Comp.Dropoff, - uid.Comp.MaxTileIntensity, - canCreateVacuum: false); - } + if (!TryComp(args.Target, out var comp)) + return; + + _explosionSystem.TriggerExplosive(args.Target, comp, true); } } } From 89218fd7254efa17ec6ca73499811fce0ecf5f3a Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sat, 13 Jul 2024 21:30:30 +1000 Subject: [PATCH 15/32] reimplement lightning resistance --- .../Components/LightningTargetComponent.cs | 20 +++++++++++-- Content.Server/Lightning/LightningSystem.cs | 28 +++++++++++++++++-- .../Lightning/LightningTargetSystem.cs | 18 +++++++++--- .../EntitySystem/LightningSparkingSystem.cs | 4 +-- .../Tesla/EntitySystem/TeslaCoilSystem.cs | 4 +-- 5 files changed, 60 insertions(+), 14 deletions(-) diff --git a/Content.Server/Lightning/Components/LightningTargetComponent.cs b/Content.Server/Lightning/Components/LightningTargetComponent.cs index a7a976a684c..a6573702621 100644 --- a/Content.Server/Lightning/Components/LightningTargetComponent.cs +++ b/Content.Server/Lightning/Components/LightningTargetComponent.cs @@ -15,7 +15,7 @@ public sealed partial class LightningTargetComponent : Component /// /// The probability weighting of being stuck by lightning, compared against other nearby lightning targets. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public float Weighting = 1f; /// @@ -23,6 +23,20 @@ public sealed partial class LightningTargetComponent : Component /// This number controls how many bounces the lightning bolt has left after hitting that target. /// At high values, the lightning will not travel farther than that entity. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] - public int LightningResistance = 1; //by default, reduces the number of bounces from this target by 1 + [DataField] + public int LightningArcReduction = 1; //by default, reduces the number of bounces from this target by 1 + + /// + /// Lightning has a charge that gauges its energy. + /// This number subtracts residual charge after absorption. + /// + [DataField] + public float LightningChargeReduction = 0f; + + /// + /// Lightning has a charge that gauges its energy. + /// This number multiplies residual charge after absorption. + /// + [DataField] + public float LightningChargeMultiplier = 1f; } diff --git a/Content.Server/Lightning/LightningSystem.cs b/Content.Server/Lightning/LightningSystem.cs index 8dd3de9651e..f5468aad43a 100644 --- a/Content.Server/Lightning/LightningSystem.cs +++ b/Content.Server/Lightning/LightningSystem.cs @@ -203,6 +203,10 @@ private void StageLightningArc(LightningArc lightningArc, int arcDepth = 0) if (!context.History.Contains(user)) context.History.Add(user); + // send an event to the staged target to be influenced by resistance + var ev = new LightningStageEvent(lightningArc.Target, context); + RaiseLocalEvent(lightningArc.Target, ref ev); + // check for any more targets if (!TryGetLightningTargets(Transform(target).Coordinates, context.ArcRange(context), out var weights)) { @@ -272,7 +276,7 @@ private void DoLightning(LightningContext context) _beam.TryCreateBeam(lightningArc.User, lightningArc.Target, context.LightningPrototype(discharge, context), spriteState); // we may not want to trigger certain lightning events - var ev = new HitByLightningEvent(discharge, lightningArc.Target, context); + var ev = new LightningEffectEvent(discharge, lightningArc.Target, context); RaiseLocalEvent(lightningArc.Target, ref ev); if (context.Charge <= 0f) @@ -340,9 +344,27 @@ public LightningContext Clone() }; /// -/// Raised directed on the target when an entity becomes the target of a lightning strike (not when touched) +/// Raised on an entity when it becomes the target of a lightning strike +/// +/// The potential target of the lightning strike. +/// The field that encapsulates the data used to make the lightning bolt. +[ByRefEvent] +public struct LightningStageEvent(EntityUid target, LightningContext context) +{ + public EntityUid Target = target; + public LightningContext Context = context; +} + +/// +/// Raised on a target when it is affected by the lightning strike /// /// The energy (J) that was discharged by the lightning bolt. +/// The target of the lightning strike. /// The field that encapsulates the data used to make the lightning bolt. [ByRefEvent] -public readonly record struct HitByLightningEvent(float Discharge, EntityUid Target, LightningContext Context); \ No newline at end of file +public struct LightningEffectEvent(float discharge, EntityUid target, LightningContext context) +{ + public float Discharge = discharge; + public EntityUid Target = target; + public LightningContext Context = context; +} \ No newline at end of file diff --git a/Content.Server/Lightning/LightningTargetSystem.cs b/Content.Server/Lightning/LightningTargetSystem.cs index 8f2cfb5a89c..8d8754c41c2 100644 --- a/Content.Server/Lightning/LightningTargetSystem.cs +++ b/Content.Server/Lightning/LightningTargetSystem.cs @@ -20,11 +20,21 @@ public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnHitByLightning); + SubscribeLocalEvent(OnLightningStage); + SubscribeLocalEvent(OnLightningEffect); } - private void OnHitByLightning(Entity uid, ref HitByLightningEvent args) + private void OnLightningStage(Entity uid, ref LightningStageEvent args) { + // Reduce the number of lightning jumps based on lightning modifiers + args.Context.MaxArcs -= uid.Comp.LightningArcReduction; + } + private void OnLightningEffect(Entity uid, ref LightningEffectEvent args) + { + // Reduce the residual charge of lighting based on lightning modifiers + args.Context.Charge -= uid.Comp.LightningChargeReduction; + args.Context.Charge *= uid.Comp.LightningChargeMultiplier; + if (args.Context.Electrocute(args.Discharge, args.Context)) { _electrocutionSystem.TryDoElectrocution(uid, args.Context.Invoker, (int) Math.Round(args.Context.ElectrocuteDamage(args.Discharge, args.Context), 0), TimeSpan.FromSeconds(5f), true, ignoreInsulation: args.Context.ElectrocuteIgnoreInsulation(args.Discharge, args.Context)); @@ -38,10 +48,10 @@ private void OnHitByLightning(Entity uid, ref HitByLig _damageable.TryChangeDamage(uid, damage, true); */ - if (!TryComp(args.Target, out var comp)) + if (!TryComp(args.Target, out var bomb)) return; - _explosionSystem.TriggerExplosive(args.Target, comp, true); + _explosionSystem.TriggerExplosive(args.Target, bomb, true); } } } diff --git a/Content.Server/Tesla/EntitySystem/LightningSparkingSystem.cs b/Content.Server/Tesla/EntitySystem/LightningSparkingSystem.cs index c07472c8b3b..921e0cf6d59 100644 --- a/Content.Server/Tesla/EntitySystem/LightningSparkingSystem.cs +++ b/Content.Server/Tesla/EntitySystem/LightningSparkingSystem.cs @@ -17,10 +17,10 @@ public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnHitByLightning); + SubscribeLocalEvent(OnLightningEffect); } - private void OnHitByLightning(Entity uid, ref HitByLightningEvent args) + private void OnLightningEffect(Entity uid, ref LightningEffectEvent args) { _appearance.SetData(uid.Owner, TeslaCoilVisuals.Lightning, true); uid.Comp.LightningEndTime = _gameTiming.CurTime + TimeSpan.FromSeconds(uid.Comp.LightningTime); diff --git a/Content.Server/Tesla/EntitySystem/TeslaCoilSystem.cs b/Content.Server/Tesla/EntitySystem/TeslaCoilSystem.cs index 9a2bc429e3d..41d4e3a3400 100644 --- a/Content.Server/Tesla/EntitySystem/TeslaCoilSystem.cs +++ b/Content.Server/Tesla/EntitySystem/TeslaCoilSystem.cs @@ -16,11 +16,11 @@ public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnHitByLightning); + SubscribeLocalEvent(OnLightningEffect); } //When struck by lightning, charge the internal battery - private void OnHitByLightning(Entity coil, ref HitByLightningEvent args) + private void OnLightningEffect(Entity coil, ref LightningEffectEvent args) { if (TryComp(coil, out var batteryComponent)) { From fb470dbc57593dda8df93686acee86ba21ab64fc Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sat, 13 Jul 2024 21:39:25 +1000 Subject: [PATCH 16/32] name consolidation + stacking options --- Content.Server/Lightning/LightningSystem.cs | 35 +++++++++++++-------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/Content.Server/Lightning/LightningSystem.cs b/Content.Server/Lightning/LightningSystem.cs index f5468aad43a..bb3ab5a6362 100644 --- a/Content.Server/Lightning/LightningSystem.cs +++ b/Content.Server/Lightning/LightningSystem.cs @@ -63,7 +63,7 @@ public void ShootLightning(EntityUid user, EntityUid target, float totalCharge, int maxArcs = 1, float arcRange = 5f, int arcForks = 1, - bool allowLooping = false, + bool arcStacking = false, string lightningPrototype = "Lightning", float damage = 15, bool electrocute = true, @@ -76,7 +76,7 @@ public void ShootLightning(EntityUid user, EntityUid target, float totalCharge, MaxArcs = maxArcs, ArcRange = (LightningContext context) => arcRange, ArcForks = (LightningContext context) => arcForks, - AllowLooping = (LightningContext context) => allowLooping, + ArcStacking = (LightningContext context) => arcStacking, LightningPrototype = (float discharge, LightningContext context) => lightningPrototype, Electrocute = (float discharge, LightningContext context) => electrocute, Explode = (float discharge, LightningContext context) => explode, @@ -107,11 +107,11 @@ public void ShootLightning(EntityUid user, EntityUid target, LightningContext co /// /// Looks for objects with a LightningTarget component in the radius, and fire lightning at (weighted) random targets /// - public void ShootRandomLightnings(EntityUid user, float lightningRadius, int lightningCount, float lightningChargePer, EntityCoordinates? queryPosition = null, + public void ShootRandomLightnings(EntityUid user, float lightningRadius, int lightningCount, float lightningChargePer, EntityCoordinates? queryPosition = null, bool lightningStacking = true, int maxArcs = 1, float arcRange = 5f, int arcForks = 1, - bool allowLooping = false, + bool arcStacking = false, string lightningPrototype = "Lightning", bool electrocute = true, bool explode = true @@ -123,19 +123,19 @@ public void ShootRandomLightnings(EntityUid user, float lightningRadius, int lig MaxArcs = maxArcs, ArcRange = (LightningContext context) => arcRange, ArcForks = (LightningContext context) => arcForks, - AllowLooping = (LightningContext context) => allowLooping, + ArcStacking = (LightningContext context) => arcStacking, LightningPrototype = (float discharge, LightningContext context) => lightningPrototype, Electrocute = (float discharge, LightningContext context) => electrocute, Explode = (float discharge, LightningContext context) => explode, }; - ShootRandomLightnings(user, lightningRadius, lightningCount, context); + ShootRandomLightnings(user, lightningRadius, lightningCount, context, queryPosition, lightningStacking); } /// /// Looks for objects with a LightningTarget component in the radius, and fire lightning at (weighted) random targets /// - public void ShootRandomLightnings(EntityUid user, float lightningRadius, int lightningCount, LightningContext context, EntityCoordinates? queryPosition = null) + public void ShootRandomLightnings(EntityUid user, float lightningRadius, int lightningCount, LightningContext context, EntityCoordinates? queryPosition = null, bool lightningStacking = true) { // default the query location to the user's position if (!queryPosition.HasValue) @@ -153,11 +153,13 @@ public void ShootRandomLightnings(EntityUid user, float lightningRadius, int lig break; string stringTarget = _random.Pick(weights); - weights.Remove(stringTarget); EntityUid target = EntityUid.Parse(stringTarget); LightningContext clone = context.Clone(); ShootLightning(user, target, clone); + + if (!lightningStacking) + weights.Remove(stringTarget); } } @@ -215,8 +217,8 @@ private void StageLightningArc(LightningArc lightningArc, int arcDepth = 0) return; } - // depending on AllowLooping, remove previously visited entities from the targeting list - if (!context.AllowLooping(context)) + // depending on ArcStacking, remove previously visited entities from the targeting list + if (!context.ArcStacking(context)) { Dictionary exception = context.History.ToDictionary(x => x.ToString(), x => 0f); weights.Except(exception); @@ -228,9 +230,16 @@ private void StageLightningArc(LightningArc lightningArc, int arcDepth = 0) // do the bounce for (int i = 0; i < context.ArcForks(context); i++) { - EntityUid nextTarget = EntityUid.Parse(_random.Pick(weights)); + if (weights.Count <= 0) + break; + + string stringTarget = _random.Pick(weights); + EntityUid nextTarget = EntityUid.Parse(stringTarget); LightningArc nextArc = new LightningArc { User = target, Target = nextTarget, ContextId = context.Id, ArcDepth = arcDepth + 1 }; _lightningQueue.Enqueue(nextArc, arcDepth + 1); + + if (!context.ArcStacking(context)) + weights.Remove(stringTarget); } // update the context and select the next lightning arc @@ -305,7 +314,7 @@ public struct LightningContext // Staging data before charge is even considered public Func ArcRange; public Func ArcForks; - public Func AllowLooping; + public Func ArcStacking; // Effect data which can take discharge into account public Func LightningPrototype; @@ -324,7 +333,7 @@ public LightningContext() ArcRange = (LightningContext context) => 3.5f; ArcForks = (LightningContext context) => 1; - AllowLooping = (LightningContext context) => true; + ArcStacking = (LightningContext context) => true; LightningPrototype = (float discharge, LightningContext context) => "Lightning"; Electrocute = (float discharge, LightningContext context) => true; From 37da556e53391d1f87ef89c3c2f7dce641b19ac4 Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sat, 13 Jul 2024 22:06:08 +1000 Subject: [PATCH 17/32] fix LightningTarget not being able to influence LightningContext --- .../Lightning/Components/LightningTargetComponent.cs | 3 ++- Content.Server/Lightning/LightningSystem.cs | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Content.Server/Lightning/Components/LightningTargetComponent.cs b/Content.Server/Lightning/Components/LightningTargetComponent.cs index a6573702621..c1115b12306 100644 --- a/Content.Server/Lightning/Components/LightningTargetComponent.cs +++ b/Content.Server/Lightning/Components/LightningTargetComponent.cs @@ -22,9 +22,10 @@ public sealed partial class LightningTargetComponent : Component /// Lightning has a number of bounces into neighboring targets. /// This number controls how many bounces the lightning bolt has left after hitting that target. /// At high values, the lightning will not travel farther than that entity. + /// For the love of god, do not make this number negative. /// [DataField] - public int LightningArcReduction = 1; //by default, reduces the number of bounces from this target by 1 + public int LightningArcReduction = 0; /// /// Lightning has a charge that gauges its energy. diff --git a/Content.Server/Lightning/LightningSystem.cs b/Content.Server/Lightning/LightningSystem.cs index bb3ab5a6362..3ef79f6345d 100644 --- a/Content.Server/Lightning/LightningSystem.cs +++ b/Content.Server/Lightning/LightningSystem.cs @@ -208,6 +208,7 @@ private void StageLightningArc(LightningArc lightningArc, int arcDepth = 0) // send an event to the staged target to be influenced by resistance var ev = new LightningStageEvent(lightningArc.Target, context); RaiseLocalEvent(lightningArc.Target, ref ev); + context = ev.Context; // check for any more targets if (!TryGetLightningTargets(Transform(target).Coordinates, context.ArcRange(context), out var weights)) @@ -284,9 +285,10 @@ private void DoLightning(LightningContext context) var spriteState = LightningRandomizer(); _beam.TryCreateBeam(lightningArc.User, lightningArc.Target, context.LightningPrototype(discharge, context), spriteState); - // we may not want to trigger certain lightning events + // send an event to the target to be affected by lightning, also inherit information var ev = new LightningEffectEvent(discharge, lightningArc.Target, context); RaiseLocalEvent(lightningArc.Target, ref ev); + context = ev.Context; if (context.Charge <= 0f) break; @@ -333,7 +335,7 @@ public LightningContext() ArcRange = (LightningContext context) => 3.5f; ArcForks = (LightningContext context) => 1; - ArcStacking = (LightningContext context) => true; + ArcStacking = (LightningContext context) => false; LightningPrototype = (float discharge, LightningContext context) => "Lightning"; Electrocute = (float discharge, LightningContext context) => true; From ac5c8fefd78d272fd4fcbb837a060aa8f86d515e Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sat, 13 Jul 2024 22:37:56 +1000 Subject: [PATCH 18/32] adjusts prototypes, electrical devices are explosive again --- Content.Server/Lightning/LightningTargetSystem.cs | 5 +++-- Resources/Prototypes/Entities/Mobs/base.yml | 1 - .../Structures/Doors/Airlocks/base_structureairlocks.yml | 2 +- .../Machines/Computers/base_structurecomputers.yml | 7 ++++++- .../Structures/Machines/base_structuremachines.yml | 9 +++++++-- .../Entities/Structures/Power/Generation/Tesla/coil.yml | 7 +------ Resources/Prototypes/Entities/Structures/Power/apc.yml | 7 ++++++- Resources/Prototypes/Entities/Structures/Power/smes.yml | 7 +++++++ .../Prototypes/Entities/Structures/Power/substation.yml | 4 ++++ 9 files changed, 35 insertions(+), 14 deletions(-) diff --git a/Content.Server/Lightning/LightningTargetSystem.cs b/Content.Server/Lightning/LightningTargetSystem.cs index 8d8754c41c2..a3c4f2dfc1c 100644 --- a/Content.Server/Lightning/LightningTargetSystem.cs +++ b/Content.Server/Lightning/LightningTargetSystem.cs @@ -37,7 +37,7 @@ private void OnLightningEffect(Entity uid, ref Lightni if (args.Context.Electrocute(args.Discharge, args.Context)) { - _electrocutionSystem.TryDoElectrocution(uid, args.Context.Invoker, (int) Math.Round(args.Context.ElectrocuteDamage(args.Discharge, args.Context), 0), TimeSpan.FromSeconds(5f), true, ignoreInsulation: args.Context.ElectrocuteIgnoreInsulation(args.Discharge, args.Context)); + _electrocutionSystem.TryDoElectrocution(uid, args.Context.Invoker, 0, TimeSpan.FromSeconds(5f), true, ignoreInsulation: args.Context.ElectrocuteIgnoreInsulation(args.Discharge, args.Context)); } if (args.Context.Explode(args.Discharge, args.Context)) @@ -51,7 +51,8 @@ private void OnLightningEffect(Entity uid, ref Lightni if (!TryComp(args.Target, out var bomb)) return; - _explosionSystem.TriggerExplosive(args.Target, bomb, true); + // don't delete the target because it looks jarring, the explosion destroys most things anyhow + _explosionSystem.TriggerExplosive(args.Target, bomb, false); } } } diff --git a/Resources/Prototypes/Entities/Mobs/base.yml b/Resources/Prototypes/Entities/Mobs/base.yml index 5319d35419d..10667a5042d 100644 --- a/Resources/Prototypes/Entities/Mobs/base.yml +++ b/Resources/Prototypes/Entities/Mobs/base.yml @@ -93,7 +93,6 @@ path: /Audio/Effects/hit_kick.ogg - type: Pullable - type: LightningTarget - lightningExplode: false # Used for mobs that can enter combat mode and can attack. - type: entity diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml index 73aa59c320d..b024ec63a4b 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml @@ -137,7 +137,7 @@ - type: StaticPrice price: 150 - type: LightningTarget - weighting: 2 + weighting: 10 - type: Tag tags: - Airlock diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/base_structurecomputers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/base_structurecomputers.yml index 6470c64e592..3e05126e0fb 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/base_structurecomputers.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/base_structurecomputers.yml @@ -59,5 +59,10 @@ board: !type:Container ents: [] - type: LightningTarget - weighting: 2 + weighting: 10 + - type: Explosive + explosionType: Default + maxIntensity: 1 + intensitySlope: 5 + totalIntensity: 5 - type: RequireProjectileTarget diff --git a/Resources/Prototypes/Entities/Structures/Machines/base_structuremachines.yml b/Resources/Prototypes/Entities/Structures/Machines/base_structuremachines.yml index 5da577f89d2..25c391ab074 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/base_structuremachines.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/base_structuremachines.yml @@ -51,7 +51,12 @@ powerLoad: 1000 - type: ExtensionCableReceiver - type: LightningTarget - weighting: 2 + weighting: 20 + - type: Explosive + explosionType: Default + maxIntensity: 2 + intensitySlope: 5 + totalIntensity: 10 - type: entity abstract: true @@ -69,7 +74,7 @@ - machine_parts - machine_board - type: LightningTarget - weighting: 2 + weighting: 10 - type: entity abstract: true diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/coil.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/coil.yml index ca6f790d5e4..2828198b55e 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/coil.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/coil.yml @@ -63,9 +63,6 @@ - type: TeslaCoil - type: LightningTarget weighting: 100 - hitProbability: 0.5 - lightningResistance: 10 - lightningExplode: false - type: PowerNetworkBattery maxSupply: 1000000 supplyRampTolerance: 1000000 @@ -149,9 +146,7 @@ - type: LightningSparking - type: LightningTarget weighting: 300 - lightningResistance: 10 - lightningExplode: false - damageFromLightning: 0 + lightningChargeMultiplier: 0.5 # consume charge when struck - type: Anchorable - type: Rotatable - type: Pullable diff --git a/Resources/Prototypes/Entities/Structures/Power/apc.yml b/Resources/Prototypes/Entities/Structures/Power/apc.yml index 81860ad459d..19f89480ab7 100644 --- a/Resources/Prototypes/Entities/Structures/Power/apc.yml +++ b/Resources/Prototypes/Entities/Structures/Power/apc.yml @@ -138,7 +138,12 @@ mediumVoltageNode: input lowVoltageNode: output - type: LightningTarget - weighting: 2 + weighting: 25 + - type: Explosive + explosionType: Default + maxIntensity: 5 + intensitySlope: 5 + totalIntensity: 25 - type: StaticPrice price: 500 diff --git a/Resources/Prototypes/Entities/Structures/Power/smes.yml b/Resources/Prototypes/Entities/Structures/Power/smes.yml index c2170a127ad..0410922d809 100644 --- a/Resources/Prototypes/Entities/Structures/Power/smes.yml +++ b/Resources/Prototypes/Entities/Structures/Power/smes.yml @@ -92,6 +92,13 @@ sendBroadcastAttemptEvent: true examinableAddress: true - type: WiredNetworkConnection + - type: LightningTarget + weighting: 75 + - type: Explosive + explosionType: Default + maxIntensity: 5 + intensitySlope: 5 + totalIntensity: 25 # SMES' in use diff --git a/Resources/Prototypes/Entities/Structures/Power/substation.yml b/Resources/Prototypes/Entities/Structures/Power/substation.yml index 94de12be185..8b51972b45e 100644 --- a/Resources/Prototypes/Entities/Structures/Power/substation.yml +++ b/Resources/Prototypes/Entities/Structures/Power/substation.yml @@ -111,6 +111,8 @@ requirePower: true highVoltageNode: input mediumVoltageNode: output + - type: LightningTarget + weighting: 50 # Compact Wall Substation Base - type: entity @@ -216,6 +218,8 @@ totalIntensity: 100 - type: StationInfiniteBatteryTarget - type: WallMount + - type: LightningTarget + weighting: 50 # Substations in use From f8d23b35eb365c89c9e84cae61dd4d9fc4f82cd2 Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sat, 13 Jul 2024 23:11:27 +1000 Subject: [PATCH 19/32] change system to use damage specifiers --- Content.Server/Lightning/LightningSystem.cs | 7 +++++-- .../Lightning/LightningTargetSystem.cs | 17 +++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Content.Server/Lightning/LightningSystem.cs b/Content.Server/Lightning/LightningSystem.cs index 3ef79f6345d..ccb60f134e2 100644 --- a/Content.Server/Lightning/LightningSystem.cs +++ b/Content.Server/Lightning/LightningSystem.cs @@ -4,6 +4,7 @@ using Content.Server.Beam; using Content.Server.Beam.Components; using Content.Server.Lightning.Components; +using Content.Shared.Damage; using Content.Shared.Lightning; using Content.Shared.Random.Helpers; using FastAccessors; @@ -320,8 +321,9 @@ public struct LightningContext // Effect data which can take discharge into account public Func LightningPrototype; + public Func Damage; + public Func DamageIgnoreInsulation; public Func Electrocute; - public Func ElectrocuteDamage; public Func ElectrocuteIgnoreInsulation; public Func Explode; @@ -338,8 +340,9 @@ public LightningContext() ArcStacking = (LightningContext context) => false; LightningPrototype = (float discharge, LightningContext context) => "Lightning"; + Damage = (float discharge, LightningContext context) => { DamageSpecifier _Damage = new DamageSpecifier(); _Damage.DamageDict["Shock"] = discharge * 0.0002f; return _Damage; }; // damage increases by 1 for every 5000J + DamageIgnoreInsulation = (float discharge, LightningContext context) => false; Electrocute = (float discharge, LightningContext context) => true; - ElectrocuteDamage = (float discharge, LightningContext context) => discharge * 0.0002f; // damage increases by 1 for every 5000J ElectrocuteIgnoreInsulation = (float discharge, LightningContext context) => false; Explode = (float discharge, LightningContext context) => true; } diff --git a/Content.Server/Lightning/LightningTargetSystem.cs b/Content.Server/Lightning/LightningTargetSystem.cs index a3c4f2dfc1c..c061540342b 100644 --- a/Content.Server/Lightning/LightningTargetSystem.cs +++ b/Content.Server/Lightning/LightningTargetSystem.cs @@ -4,6 +4,7 @@ using Content.Server.Lightning; using Content.Server.Lightning.Components; using Content.Shared.Damage; +using Content.Shared.Electrocution; namespace Content.Server.Tesla.EntitySystems; @@ -35,24 +36,24 @@ private void OnLightningEffect(Entity uid, ref Lightni args.Context.Charge -= uid.Comp.LightningChargeReduction; args.Context.Charge *= uid.Comp.LightningChargeMultiplier; + // Deal damage as specified by lightning + if (!args.Context.DamageIgnoreInsulation(args.Discharge, args.Context) || !TryComp(uid, out var _)) + _damageable.TryChangeDamage(uid, args.Context.Damage(args.Discharge, args.Context), true); + + // Attempt to electrocute the target if (args.Context.Electrocute(args.Discharge, args.Context)) { _electrocutionSystem.TryDoElectrocution(uid, args.Context.Invoker, 0, TimeSpan.FromSeconds(5f), true, ignoreInsulation: args.Context.ElectrocuteIgnoreInsulation(args.Discharge, args.Context)); } + // Attempt to explode the target, provided that they are explosives if (args.Context.Explode(args.Discharge, args.Context)) { - /* - DamageSpecifier damage = new(); - damage.DamageDict.Add("Structural", uid.Comp.DamageFromLightning); - _damageable.TryChangeDamage(uid, damage, true); - */ - - if (!TryComp(args.Target, out var bomb)) + if (!TryComp(uid, out var bomb)) return; // don't delete the target because it looks jarring, the explosion destroys most things anyhow - _explosionSystem.TriggerExplosive(args.Target, bomb, false); + _explosionSystem.TriggerExplosive(uid, bomb, false); } } } From c90278570039acc44dde38078c4b1f53fb37b2bf Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sat, 13 Jul 2024 23:15:29 +1000 Subject: [PATCH 20/32] oops --- Content.Server/Lightning/LightningTargetSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Server/Lightning/LightningTargetSystem.cs b/Content.Server/Lightning/LightningTargetSystem.cs index c061540342b..14b595e6a31 100644 --- a/Content.Server/Lightning/LightningTargetSystem.cs +++ b/Content.Server/Lightning/LightningTargetSystem.cs @@ -37,7 +37,7 @@ private void OnLightningEffect(Entity uid, ref Lightni args.Context.Charge *= uid.Comp.LightningChargeMultiplier; // Deal damage as specified by lightning - if (!args.Context.DamageIgnoreInsulation(args.Discharge, args.Context) || !TryComp(uid, out var _)) + if (!args.Context.DamageIgnoreInsulation(args.Discharge, args.Context) && !TryComp(uid, out var _)) _damageable.TryChangeDamage(uid, args.Context.Damage(args.Discharge, args.Context), true); // Attempt to electrocute the target From 6cda0e0543dd101537644822b289287752a54710 Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sat, 13 Jul 2024 23:48:20 +1000 Subject: [PATCH 21/32] increase odds of hitting mobs --- Resources/Prototypes/Entities/Mobs/base.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Mobs/base.yml b/Resources/Prototypes/Entities/Mobs/base.yml index 10667a5042d..80c13109718 100644 --- a/Resources/Prototypes/Entities/Mobs/base.yml +++ b/Resources/Prototypes/Entities/Mobs/base.yml @@ -93,7 +93,7 @@ path: /Audio/Effects/hit_kick.ogg - type: Pullable - type: LightningTarget - + - weighting: 10 # Used for mobs that can enter combat mode and can attack. - type: entity save: false From af55fa7ffe3540bbc0f1dfe5164e4f9d08811c31 Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sat, 13 Jul 2024 23:54:45 +1000 Subject: [PATCH 22/32] uncrash --- Resources/Prototypes/Entities/Mobs/base.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Mobs/base.yml b/Resources/Prototypes/Entities/Mobs/base.yml index 80c13109718..bbbae2608d5 100644 --- a/Resources/Prototypes/Entities/Mobs/base.yml +++ b/Resources/Prototypes/Entities/Mobs/base.yml @@ -93,7 +93,7 @@ path: /Audio/Effects/hit_kick.ogg - type: Pullable - type: LightningTarget - - weighting: 10 + weighting: 10 # Used for mobs that can enter combat mode and can attack. - type: entity save: false From 4e56c753ec49a8af5a7cd92eed923ea0588e1f89 Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sun, 14 Jul 2024 00:36:53 +1000 Subject: [PATCH 23/32] stopgap stuff --- Content.Server/Lightning/LightningSystem.cs | 2 ++ .../Lightning/LightningTargetSystem.cs | 22 ++++++++++++------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/Content.Server/Lightning/LightningSystem.cs b/Content.Server/Lightning/LightningSystem.cs index ccb60f134e2..3f38c5d58b9 100644 --- a/Content.Server/Lightning/LightningSystem.cs +++ b/Content.Server/Lightning/LightningSystem.cs @@ -324,6 +324,7 @@ public struct LightningContext public Func Damage; public Func DamageIgnoreInsulation; public Func Electrocute; + public Func ElectrocuteDamage; //TODO - make ElectrocutionSystem not parse an !INT! for shock damage public Func ElectrocuteIgnoreInsulation; public Func Explode; @@ -343,6 +344,7 @@ public LightningContext() Damage = (float discharge, LightningContext context) => { DamageSpecifier _Damage = new DamageSpecifier(); _Damage.DamageDict["Shock"] = discharge * 0.0002f; return _Damage; }; // damage increases by 1 for every 5000J DamageIgnoreInsulation = (float discharge, LightningContext context) => false; Electrocute = (float discharge, LightningContext context) => true; + ElectrocuteDamage = (float discharge, LightningContext context) => 1; ElectrocuteIgnoreInsulation = (float discharge, LightningContext context) => false; Explode = (float discharge, LightningContext context) => true; } diff --git a/Content.Server/Lightning/LightningTargetSystem.cs b/Content.Server/Lightning/LightningTargetSystem.cs index 14b595e6a31..9f15599fdc9 100644 --- a/Content.Server/Lightning/LightningTargetSystem.cs +++ b/Content.Server/Lightning/LightningTargetSystem.cs @@ -25,25 +25,31 @@ public override void Initialize() SubscribeLocalEvent(OnLightningEffect); } - private void OnLightningStage(Entity uid, ref LightningStageEvent args) + private void OnLightningStage(EntityUid uid, LightningTargetComponent comp, LightningStageEvent args) { // Reduce the number of lightning jumps based on lightning modifiers - args.Context.MaxArcs -= uid.Comp.LightningArcReduction; + args.Context.MaxArcs -= comp.LightningArcReduction; } - private void OnLightningEffect(Entity uid, ref LightningEffectEvent args) + + private void OnLightningEffect(EntityUid uid, LightningTargetComponent comp, LightningEffectEvent args) { // Reduce the residual charge of lighting based on lightning modifiers - args.Context.Charge -= uid.Comp.LightningChargeReduction; - args.Context.Charge *= uid.Comp.LightningChargeMultiplier; + args.Context.Charge -= comp.LightningChargeReduction; + args.Context.Charge *= comp.LightningChargeMultiplier; + // TODO - Make checking for insulation actually work // Deal damage as specified by lightning - if (!args.Context.DamageIgnoreInsulation(args.Discharge, args.Context) && !TryComp(uid, out var _)) - _damageable.TryChangeDamage(uid, args.Context.Damage(args.Discharge, args.Context), true); + float damageCoefficient = 1f; + /* + if (!args.Context.DamageIgnoreInsulation(args.Discharge, args.Context) && TryComp(uid, out var insulated)) + coefficient = insulated.Coefficient; + */ + _damageable.TryChangeDamage(uid, args.Context.Damage(args.Discharge, args.Context) * damageCoefficient, true); // Attempt to electrocute the target if (args.Context.Electrocute(args.Discharge, args.Context)) { - _electrocutionSystem.TryDoElectrocution(uid, args.Context.Invoker, 0, TimeSpan.FromSeconds(5f), true, ignoreInsulation: args.Context.ElectrocuteIgnoreInsulation(args.Discharge, args.Context)); + _electrocutionSystem.TryDoElectrocution(uid, args.Context.Invoker, args.Context.ElectrocuteDamage(args.Discharge, args.Context), TimeSpan.FromSeconds(5f), true, ignoreInsulation: args.Context.ElectrocuteIgnoreInsulation(args.Discharge, args.Context)); } // Attempt to explode the target, provided that they are explosives From a9b712619131128bf293fc22a5a03f270755eb95 Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sun, 14 Jul 2024 11:20:11 +1000 Subject: [PATCH 24/32] improves electrocution damage logic --- .../Electrocution/ElectrocutionSystem.cs | 46 +++++++++++++------ Content.Server/Lightning/LightningSystem.cs | 8 +--- .../Lightning/LightningTargetSystem.cs | 9 +--- .../SharedElectrocutionSystem.cs | 3 +- 4 files changed, 36 insertions(+), 30 deletions(-) diff --git a/Content.Server/Electrocution/ElectrocutionSystem.cs b/Content.Server/Electrocution/ElectrocutionSystem.cs index f71f8b69742..69c7e568cd5 100644 --- a/Content.Server/Electrocution/ElectrocutionSystem.cs +++ b/Content.Server/Electrocution/ElectrocutionSystem.cs @@ -12,6 +12,7 @@ using Content.Shared.Damage.Prototypes; using Content.Shared.Database; using Content.Shared.Electrocution; +using Content.Shared.FixedPoint; using Content.Shared.IdentityManagement; using Content.Shared.Interaction; using Content.Shared.Inventory; @@ -212,7 +213,7 @@ private void OnElectrifiedInteractUsing(EntityUid uid, ElectrifiedComponent elec TryDoElectrifiedAct(uid, args.User, siemens, electrified); } - private float CalculateElectrifiedDamageScale(float power) + public float CalculateElectrifiedDamageScale(float power) { // A logarithm allows a curve of damage that grows quickly, but slows down dramatically past a value. This keeps the damage to a reasonable range. const float DamageShift = 1.67f; // Shifts the curve for an overall higher or lower damage baseline @@ -313,13 +314,26 @@ public bool TryDoElectrifiedAct(EntityUid uid, EntityUid targetUid, } } + public bool TryDoElectrocution( + EntityUid uid, EntityUid? sourceUid, FixedPoint2 shockDamage, TimeSpan time, bool refresh, float siemensCoefficient = 1f, + StatusEffectsComponent? statusEffects = null, bool ignoreInsulation = false) + { + var damage = new DamageSpecifier(); + damage.DamageDict.Add("Shock", shockDamage.Value); + + return TryDoElectrocution( + uid, sourceUid, damage, time, refresh, siemensCoefficient, + statusEffects, ignoreInsulation + ); + } + /// public override bool TryDoElectrocution( - EntityUid uid, EntityUid? sourceUid, int shockDamage, TimeSpan time, bool refresh, float siemensCoefficient = 1f, + EntityUid uid, EntityUid? sourceUid, DamageSpecifier damageSpecifier, TimeSpan time, bool refresh, float siemensCoefficient = 1f, StatusEffectsComponent? statusEffects = null, bool ignoreInsulation = false) { if (!DoCommonElectrocutionAttempt(uid, sourceUid, ref siemensCoefficient, ignoreInsulation) - || !DoCommonElectrocution(uid, sourceUid, shockDamage, time, refresh, siemensCoefficient, statusEffects)) + || !DoCommonElectrocution(uid, sourceUid, damageSpecifier, time, refresh, siemensCoefficient, statusEffects)) return false; RaiseLocalEvent(uid, new ElectrocutedEvent(uid, sourceUid, siemensCoefficient), true); @@ -393,20 +407,22 @@ private bool DoCommonElectrocutionAttempt(EntityUid uid, EntityUid? sourceUid, r } private bool DoCommonElectrocution(EntityUid uid, EntityUid? sourceUid, - int? shockDamage, TimeSpan time, bool refresh, float siemensCoefficient = 1f, + FixedPoint2? shockDamage, TimeSpan time, bool refresh, float siemensCoefficient = 1f, + StatusEffectsComponent? statusEffects = null) + { + var damage = new DamageSpecifier(); + if (shockDamage.HasValue) + damage.DamageDict.Add("Shock", shockDamage.Value); + + return DoCommonElectrocution(uid, sourceUid, damage, time, refresh, siemensCoefficient, statusEffects); + } + private bool DoCommonElectrocution(EntityUid uid, EntityUid? sourceUid, + DamageSpecifier? damageSpecifier, TimeSpan time, bool refresh, float siemensCoefficient = 1f, StatusEffectsComponent? statusEffects = null) { if (siemensCoefficient <= 0) return false; - if (shockDamage != null) - { - shockDamage = (int) (shockDamage * siemensCoefficient); - - if (shockDamage.Value <= 0) - return false; - } - if (!Resolve(uid, ref statusEffects, false) || !_statusEffects.CanApplyEffect(uid, StatusEffectKey, statusEffects)) { @@ -423,10 +439,10 @@ private bool DoCommonElectrocution(EntityUid uid, EntityUid? sourceUid, // TODO: Sparks here. - if (shockDamage is { } dmg) + if (damageSpecifier != null) { - var actual = _damageable.TryChangeDamage(uid, - new DamageSpecifier(_prototypeManager.Index(DamageType), dmg), origin: sourceUid); + damageSpecifier *= siemensCoefficient; + var actual = _damageable.TryChangeDamage(uid, damageSpecifier, origin: sourceUid); if (actual != null) { diff --git a/Content.Server/Lightning/LightningSystem.cs b/Content.Server/Lightning/LightningSystem.cs index 3f38c5d58b9..493176b8f52 100644 --- a/Content.Server/Lightning/LightningSystem.cs +++ b/Content.Server/Lightning/LightningSystem.cs @@ -321,10 +321,8 @@ public struct LightningContext // Effect data which can take discharge into account public Func LightningPrototype; - public Func Damage; - public Func DamageIgnoreInsulation; public Func Electrocute; - public Func ElectrocuteDamage; //TODO - make ElectrocutionSystem not parse an !INT! for shock damage + public Func ElectrocuteDamage; public Func ElectrocuteIgnoreInsulation; public Func Explode; @@ -341,10 +339,8 @@ public LightningContext() ArcStacking = (LightningContext context) => false; LightningPrototype = (float discharge, LightningContext context) => "Lightning"; - Damage = (float discharge, LightningContext context) => { DamageSpecifier _Damage = new DamageSpecifier(); _Damage.DamageDict["Shock"] = discharge * 0.0002f; return _Damage; }; // damage increases by 1 for every 5000J - DamageIgnoreInsulation = (float discharge, LightningContext context) => false; Electrocute = (float discharge, LightningContext context) => true; - ElectrocuteDamage = (float discharge, LightningContext context) => 1; + ElectrocuteDamage = (float discharge, LightningContext context) => { DamageSpecifier _Damage = new DamageSpecifier(); _Damage.DamageDict["Shock"] = discharge * 0.0002f; return _Damage; }; // damage increases by 1 for every 5000J ElectrocuteIgnoreInsulation = (float discharge, LightningContext context) => false; Explode = (float discharge, LightningContext context) => true; } diff --git a/Content.Server/Lightning/LightningTargetSystem.cs b/Content.Server/Lightning/LightningTargetSystem.cs index 9f15599fdc9..1fd46f208a8 100644 --- a/Content.Server/Lightning/LightningTargetSystem.cs +++ b/Content.Server/Lightning/LightningTargetSystem.cs @@ -37,14 +37,7 @@ private void OnLightningEffect(EntityUid uid, LightningTargetComponent comp, Lig args.Context.Charge -= comp.LightningChargeReduction; args.Context.Charge *= comp.LightningChargeMultiplier; - // TODO - Make checking for insulation actually work - // Deal damage as specified by lightning - float damageCoefficient = 1f; - /* - if (!args.Context.DamageIgnoreInsulation(args.Discharge, args.Context) && TryComp(uid, out var insulated)) - coefficient = insulated.Coefficient; - */ - _damageable.TryChangeDamage(uid, args.Context.Damage(args.Discharge, args.Context) * damageCoefficient, true); + // TODO - Add a damage specifier for machines // Attempt to electrocute the target if (args.Context.Electrocute(args.Discharge, args.Context)) diff --git a/Content.Shared/Electrocution/SharedElectrocutionSystem.cs b/Content.Shared/Electrocution/SharedElectrocutionSystem.cs index 5031d8a9115..1db82da9203 100644 --- a/Content.Shared/Electrocution/SharedElectrocutionSystem.cs +++ b/Content.Shared/Electrocution/SharedElectrocutionSystem.cs @@ -1,3 +1,4 @@ +using Content.Shared.Damage; using Content.Shared.Inventory; using Content.Shared.StatusEffect; @@ -33,7 +34,7 @@ public void SetInsulatedSiemensCoefficient(EntityUid uid, float siemensCoefficie /// Should the electrocution bypass the Insulated component? /// Whether the entity was stunned by the shock. public virtual bool TryDoElectrocution( - EntityUid uid, EntityUid? sourceUid, int shockDamage, TimeSpan time, bool refresh, float siemensCoefficient = 1f, + EntityUid uid, EntityUid? sourceUid, DamageSpecifier damageSpecifier, TimeSpan time, bool refresh, float siemensCoefficient = 1f, StatusEffectsComponent? statusEffects = null, bool ignoreInsulation = false) { // only done serverside From de543cb4f1929d5532356af1ca51c0e5b60898ef Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sun, 14 Jul 2024 12:33:24 +1000 Subject: [PATCH 25/32] allow machines to take electrocution damage --- .../Electrocution/ElectrocutionSystem.cs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Content.Server/Electrocution/ElectrocutionSystem.cs b/Content.Server/Electrocution/ElectrocutionSystem.cs index 69c7e568cd5..ce8dac89676 100644 --- a/Content.Server/Electrocution/ElectrocutionSystem.cs +++ b/Content.Server/Electrocution/ElectrocutionSystem.cs @@ -416,6 +416,7 @@ private bool DoCommonElectrocution(EntityUid uid, EntityUid? sourceUid, return DoCommonElectrocution(uid, sourceUid, damage, time, refresh, siemensCoefficient, statusEffects); } + private bool DoCommonElectrocution(EntityUid uid, EntityUid? sourceUid, DamageSpecifier? damageSpecifier, TimeSpan time, bool refresh, float siemensCoefficient = 1f, StatusEffectsComponent? statusEffects = null) @@ -423,22 +424,6 @@ private bool DoCommonElectrocution(EntityUid uid, EntityUid? sourceUid, if (siemensCoefficient <= 0) 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; - - if (shouldStun) - _stun.TryParalyze(uid, time * ParalyzeTimeMultiplier, refresh, statusEffects); - - // TODO: Sparks here. - if (damageSpecifier != null) { damageSpecifier *= siemensCoefficient; @@ -451,6 +436,21 @@ private bool DoCommonElectrocution(EntityUid uid, EntityUid? sourceUid, } } + var checkStun = false; + if (siemensCoefficient > 0.5f + && Resolve(uid, ref statusEffects, false) + && _statusEffects.CanApplyEffect(uid, StatusEffectKey, statusEffects) + && _statusEffects.TryAddStatusEffect(uid, StatusEffectKey, time, refresh, statusEffects)) + { + _stun.TryParalyze(uid, time * ParalyzeTimeMultiplier, refresh, statusEffects); + checkStun = true; + } + + if (damageSpecifier == null || !checkStun) + return false; + + // TODO: Sparks here. + _stuttering.DoStutter(uid, time * StutteringTimeMultiplier, refresh, statusEffects); _jittering.DoJitter(uid, time * JitterTimeMultiplier, refresh, JitterAmplitude, JitterFrequency, true, statusEffects); From 40b32d4ce61e7764fae08319f26aee2b16a52fd2 Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sun, 14 Jul 2024 13:24:21 +1000 Subject: [PATCH 26/32] stop giving a damage popup when 0 electrocute damage is dealt --- Content.Server/Electrocution/ElectrocutionSystem.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Content.Server/Electrocution/ElectrocutionSystem.cs b/Content.Server/Electrocution/ElectrocutionSystem.cs index ce8dac89676..2522154766a 100644 --- a/Content.Server/Electrocution/ElectrocutionSystem.cs +++ b/Content.Server/Electrocution/ElectrocutionSystem.cs @@ -424,15 +424,18 @@ private bool DoCommonElectrocution(EntityUid uid, EntityUid? sourceUid, if (siemensCoefficient <= 0) return false; + var checkDamage = false; if (damageSpecifier != null) { damageSpecifier *= siemensCoefficient; var actual = _damageable.TryChangeDamage(uid, damageSpecifier, origin: sourceUid); - if (actual != null) + if (actual != null && actual.DamageDict.Values.Sum() > FixedPoint2.Zero) { _adminLogger.Add(LogType.Electrocution, $"{ToPrettyString(uid):entity} received {actual.GetTotal():damage} powered electrocution damage{(sourceUid != null ? " from " + ToPrettyString(sourceUid.Value) : ""):source}"); + + checkDamage = true; } } @@ -443,10 +446,11 @@ private bool DoCommonElectrocution(EntityUid uid, EntityUid? sourceUid, && _statusEffects.TryAddStatusEffect(uid, StatusEffectKey, time, refresh, statusEffects)) { _stun.TryParalyze(uid, time * ParalyzeTimeMultiplier, refresh, statusEffects); + checkStun = true; } - if (damageSpecifier == null || !checkStun) + if (!checkDamage && !checkStun) return false; // TODO: Sparks here. From b6b4b72f528f79048ec3e5dcd2f6d59da93029d1 Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sun, 14 Jul 2024 13:39:14 +1000 Subject: [PATCH 27/32] coils/rods no longer take shock damage, weighting buffs --- .../Entities/Structures/Power/Generation/Tesla/coil.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/coil.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/coil.yml index 2828198b55e..47fde1fb2c7 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/coil.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/coil.yml @@ -62,7 +62,7 @@ activeSupplyRate: 15000 - type: TeslaCoil - type: LightningTarget - weighting: 100 + weighting: 200 - type: PowerNetworkBattery maxSupply: 1000000 supplyRampTolerance: 1000000 @@ -73,6 +73,7 @@ - type: InteractionOutline - type: Damageable damageContainer: StructuralInorganic + damageModifierSet: ShockAbsorber - type: ExaminableDamage messages: WindowMessages - type: Repairable @@ -145,7 +146,8 @@ False: { state: grounding_rod_open } - type: LightningSparking - type: LightningTarget - weighting: 300 + weighting: 400 + lightningArcReduction: 1 lightningChargeMultiplier: 0.5 # consume charge when struck - type: Anchorable - type: Rotatable @@ -157,6 +159,7 @@ - type: Repairable - type: Damageable damageContainer: StructuralInorganic + damageModifierSet: ShockAbsorber - type: DamageVisuals thresholds: [8, 16, 25] damageDivisor: 3.333 From 02db056d4ab39e362241bb2cb1cbea2ebedd3607 Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sun, 14 Jul 2024 22:16:35 +1000 Subject: [PATCH 28/32] consolidation --- Content.Server/Lightning/LightningTargetSystem.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Content.Server/Lightning/LightningTargetSystem.cs b/Content.Server/Lightning/LightningTargetSystem.cs index 1fd46f208a8..0ed0d6a5785 100644 --- a/Content.Server/Lightning/LightningTargetSystem.cs +++ b/Content.Server/Lightning/LightningTargetSystem.cs @@ -3,8 +3,6 @@ using Content.Server.Explosion.EntitySystems; using Content.Server.Lightning; using Content.Server.Lightning.Components; -using Content.Shared.Damage; -using Content.Shared.Electrocution; namespace Content.Server.Tesla.EntitySystems; @@ -13,7 +11,6 @@ namespace Content.Server.Tesla.EntitySystems; /// public sealed class LightningTargetSystem : EntitySystem { - [Dependency] private readonly DamageableSystem _damageable = default!; [Dependency] private readonly ExplosionSystem _explosionSystem = default!; [Dependency] private readonly ElectrocutionSystem _electrocutionSystem = default!; @@ -37,8 +34,6 @@ private void OnLightningEffect(EntityUid uid, LightningTargetComponent comp, Lig args.Context.Charge -= comp.LightningChargeReduction; args.Context.Charge *= comp.LightningChargeMultiplier; - // TODO - Add a damage specifier for machines - // Attempt to electrocute the target if (args.Context.Electrocute(args.Discharge, args.Context)) { From 228590a24b5d08893678229f8486f6f8b59a0761 Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Mon, 15 Jul 2024 14:07:07 +1000 Subject: [PATCH 29/32] allow wall lights to be targeted by lightning --- .../Prototypes/Entities/Structures/Lighting/base_lighting.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml b/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml index 59def55d952..fcff8cab3df 100644 --- a/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml +++ b/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml @@ -66,6 +66,8 @@ - !type:PlaySoundBehavior sound: collection: GlassBreak + - type: LightningTarget + weighting: 2 placement: mode: SnapgridCenter snap: From 3584d28e01ded27deb41c9324128589e27b21204 Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Mon, 15 Jul 2024 20:50:56 +1000 Subject: [PATCH 30/32] update LightningArcShooter to use math --- Content.Server/Lightning/LightningSystem.cs | 11 +++- .../LightningArcShooterComponent.cs | 50 +++++++++++++------ .../EntitySystem/LightningArcShooterSystem.cs | 44 +++++++++++++--- .../Power/Generation/Tesla/energyball.yml | 15 ++---- 4 files changed, 87 insertions(+), 33 deletions(-) diff --git a/Content.Server/Lightning/LightningSystem.cs b/Content.Server/Lightning/LightningSystem.cs index 493176b8f52..5a63a6d4c8c 100644 --- a/Content.Server/Lightning/LightningSystem.cs +++ b/Content.Server/Lightning/LightningSystem.cs @@ -91,6 +91,9 @@ public void ShootLightning(EntityUid user, EntityUid target, float totalCharge, /// public void ShootLightning(EntityUid user, EntityUid target, LightningContext context) { + if (context.MaxArcs <= 0) + return; + int id = NextId(); context.Id = id; context.Invoker = user; @@ -136,7 +139,10 @@ public void ShootRandomLightnings(EntityUid user, float lightningRadius, int lig /// /// Looks for objects with a LightningTarget component in the radius, and fire lightning at (weighted) random targets /// - public void ShootRandomLightnings(EntityUid user, float lightningRadius, int lightningCount, LightningContext context, EntityCoordinates? queryPosition = null, bool lightningStacking = true) + public void ShootRandomLightnings(EntityUid user, float lightningRadius, int lightningCount, LightningContext context, EntityCoordinates? queryPosition = null, bool lightningStacking = true, + Func? dynamicCharge = null, + Func? dynamicArcs = null + ) { // default the query location to the user's position if (!queryPosition.HasValue) @@ -157,6 +163,9 @@ public void ShootRandomLightnings(EntityUid user, float lightningRadius, int lig EntityUid target = EntityUid.Parse(stringTarget); LightningContext clone = context.Clone(); + if (dynamicCharge != null) clone.Charge = dynamicCharge(context); + if (dynamicArcs != null) clone.MaxArcs = dynamicArcs(context); + ShootLightning(user, target, clone); if (!lightningStacking) diff --git a/Content.Server/Tesla/Components/LightningArcShooterComponent.cs b/Content.Server/Tesla/Components/LightningArcShooterComponent.cs index add7bf88e43..3a9b546b1de 100644 --- a/Content.Server/Tesla/Components/LightningArcShooterComponent.cs +++ b/Content.Server/Tesla/Components/LightningArcShooterComponent.cs @@ -9,42 +9,62 @@ namespace Content.Server.Tesla.Components; [RegisterComponent, Access(typeof(LightningArcShooterSystem)), AutoGenerateComponentPause] public sealed partial class LightningArcShooterComponent : Component { + /// + /// The total number of arcs that an energy ball can create from any one shot + /// Increasing this value can lead to increasingly unpredictable ranges + [DataField] + public int MaxArcs = 4; + + /// + /// The target selection radius for a lightning bolt 'arc' bounce/chain. + /// + [DataField] + public float ArcRadius = 3.5f; + /// /// The number of lightning bolts that are fired at the same time. From 0 to N /// Important balance value: if there aren't a N number of coils or grounders around the tesla, /// the tesla will have a chance to shoot into something important and break. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] - public int MaxLightningArc = 1; + [DataField] + public int MaxBolts = 1; /// - /// Minimum interval between shooting. + /// The target selection radius for lightning bolts. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] - public float ShootMinInterval = 0.5f; + [DataField] + public float BoltRadius = 3.5f; /// - /// Maximum interval between shooting. + /// The chance that any given lightning arc will fork instead of chain /// - [DataField, ViewVariables(VVAccess.ReadWrite)] - public float ShootMaxInterval = 8.0f; + [DataField] + public float ForkChance = 0.5f; /// - /// the target selection radius for lightning bolts. + /// The max number of forks for any given lighting arc + /// Must be 2 or higher /// - [DataField, ViewVariables(VVAccess.ReadWrite)] - public float ShootRange = 5f; + [DataField] + public int MaxForks = 2; + /// - /// How many times after a hit the lightning bolt will bounce into an adjacent target + /// Minimum interval between shooting. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] - public int ArcDepth = 1; + [DataField] + public float ShootMinInterval = 0.5f; + + /// + /// Maximum interval between shooting. + /// + [DataField] + public float ShootMaxInterval = 8.0f; /// /// The time, upon reaching which the next batch of lightning bolts will be fired. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] [AutoPausedField] public TimeSpan NextShootTime; diff --git a/Content.Server/Tesla/EntitySystem/LightningArcShooterSystem.cs b/Content.Server/Tesla/EntitySystem/LightningArcShooterSystem.cs index 31bd3ce914c..9972573a490 100644 --- a/Content.Server/Tesla/EntitySystem/LightningArcShooterSystem.cs +++ b/Content.Server/Tesla/EntitySystem/LightningArcShooterSystem.cs @@ -1,3 +1,4 @@ +using Content.Server.Administration.Commands; using Content.Server.Lightning; using Content.Server.Tesla.Components; using Robust.Shared.Random; @@ -43,11 +44,42 @@ public override void Update(float frameTime) private void ArcShoot(EntityUid uid, LightningArcShooterComponent component) { - var arcs = _random.Next(1, component.MaxLightningArc); - _lightning.ShootRandomLightnings(uid, component.ShootRange, arcs, 100000, - lightningPrototype: component.LightningPrototype, - maxArcs: component.ArcDepth, - arcForks: 2 - ); + int lightningBolts = _random.Next(1, component.MaxBolts); + int boltIterator = lightningBolts; + int lightningArcs = _random.Next(lightningBolts, component.MaxArcs); + + int DynamicArcs(LightningContext context) + { + boltIterator -= 1; + + if (boltIterator > 0) + { + int diff = _random.Next(1, lightningArcs - boltIterator); + lightningArcs -= diff; + return diff; + }; + + return lightningArcs; + } + + float DynamicCharge(LightningContext context) + { + return _random.Next(context.MaxArcs, (int) Math.Round((decimal) component.MaxArcs / lightningBolts, 0)) * 10000f; + } + + LightningContext lightningContext = new LightningContext + { + ArcRange = (context) => component.ArcRadius, + ArcForks = (context) => + { + if (_random.NextFloat(0f, 1f) < component.ForkChance) + return 1; + + return _random.Next(2, Math.Max(2, component.MaxForks)); + }, + LightningPrototype = (discharge, context) => component.LightningPrototype.ToString(), + }; + + _lightning.ShootRandomLightnings(uid, component.BoltRadius, lightningBolts, lightningContext, dynamicArcs: DynamicArcs, dynamicCharge: DynamicCharge); } } diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/energyball.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/energyball.yml index 0676bac713d..22bc242e6f9 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/energyball.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/energyball.yml @@ -24,11 +24,6 @@ color: "#4080FF" - type: Appearance - type: LightningArcShooter - arcDepth: 2 - maxLightningArc: 1 - shootMinInterval: 4 - shootMaxInterval: 10 - shootRange: 3 lightningPrototype: Lightning - type: entity @@ -73,12 +68,10 @@ params: variation: 0.3 - type: LightningArcShooter - arcDepth: 3 - maxLightningArc: 4 - shootMinInterval: 3 - shootMaxInterval: 5 - shootRange: 7 - lightningPrototype: Lightning #To do: change to HyperchargedLightning, after fix beam system + maxArcs: 16 + arcRadius: 6 + maxBolts: 4 + boltRadius: 7 - type: ChasingWalk minSpeed: 1 maxSpeed: 3 From 07fcfe3975dd66cc0529d822224776b495d1831e Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sat, 24 Aug 2024 12:56:06 +1000 Subject: [PATCH 31/32] migrate types to shared --- Content.Server/Lightning/LightningSystem.cs | 58 --------- .../EntitySystem/LightningArcShooterSystem.cs | 1 + .../Lightning/SharedLightningSystem.cs | 120 +++++++++++++++++- 3 files changed, 120 insertions(+), 59 deletions(-) diff --git a/Content.Server/Lightning/LightningSystem.cs b/Content.Server/Lightning/LightningSystem.cs index 5a63a6d4c8c..d042cb8e722 100644 --- a/Content.Server/Lightning/LightningSystem.cs +++ b/Content.Server/Lightning/LightningSystem.cs @@ -305,64 +305,6 @@ private void DoLightning(LightningContext context) } } } -public record struct LightningArc( - EntityUid User, - EntityUid Target, - int ContextId, - int ArcDepth -); -public struct LightningContext -{ - // These are not parameters, and are handled by the LightningSystem - public int Id; - public EntityUid Invoker; - public List Arcs; - public List History; - - // Initial data that shouldn't be changed by staging - public float Charge; - public int MaxArcs; - - // Staging data before charge is even considered - public Func ArcRange; - public Func ArcForks; - public Func ArcStacking; - - // Effect data which can take discharge into account - public Func LightningPrototype; - public Func Electrocute; - public Func ElectrocuteDamage; - public Func ElectrocuteIgnoreInsulation; - public Func Explode; - - public LightningContext() - { - Arcs = []; - History = []; - - Charge = 50000f; - MaxArcs = 1; - - ArcRange = (LightningContext context) => 3.5f; - ArcForks = (LightningContext context) => 1; - ArcStacking = (LightningContext context) => false; - - LightningPrototype = (float discharge, LightningContext context) => "Lightning"; - Electrocute = (float discharge, LightningContext context) => true; - ElectrocuteDamage = (float discharge, LightningContext context) => { DamageSpecifier _Damage = new DamageSpecifier(); _Damage.DamageDict["Shock"] = discharge * 0.0002f; return _Damage; }; // damage increases by 1 for every 5000J - ElectrocuteIgnoreInsulation = (float discharge, LightningContext context) => false; - Explode = (float discharge, LightningContext context) => true; - } - - public LightningContext Clone() - { - LightningContext other = (LightningContext) MemberwiseClone(); - other.Arcs = new List(Arcs); - other.History = new List(History); - - return other; - } -}; /// /// Raised on an entity when it becomes the target of a lightning strike diff --git a/Content.Server/Tesla/EntitySystem/LightningArcShooterSystem.cs b/Content.Server/Tesla/EntitySystem/LightningArcShooterSystem.cs index 9972573a490..ccbd8222777 100644 --- a/Content.Server/Tesla/EntitySystem/LightningArcShooterSystem.cs +++ b/Content.Server/Tesla/EntitySystem/LightningArcShooterSystem.cs @@ -1,5 +1,6 @@ using Content.Server.Administration.Commands; using Content.Server.Lightning; +using Content.Shared.Lightning; using Content.Server.Tesla.Components; using Robust.Shared.Random; using Robust.Shared.Timing; diff --git a/Content.Shared/Lightning/SharedLightningSystem.cs b/Content.Shared/Lightning/SharedLightningSystem.cs index dc79a81c320..71d594e09ed 100644 --- a/Content.Shared/Lightning/SharedLightningSystem.cs +++ b/Content.Shared/Lightning/SharedLightningSystem.cs @@ -1,4 +1,6 @@ -using Robust.Shared.Random; +using Content.Shared.Damage; +using Robust.Shared.Map; +using Robust.Shared.Random; namespace Content.Shared.Lightning; @@ -16,4 +18,120 @@ public string LightningRandomizer() var spriteStateNumber = _random.Next(1, 12); return ("lightning_" + spriteStateNumber); } + + /// + /// Fires a lightning bolt from one entity to another + /// Only done serverside + /// + public virtual void ShootLightning(EntityUid user, EntityUid target, float totalCharge, + int maxArcs = 1, + float arcRange = 5f, + int arcForks = 1, + bool arcStacking = false, + string lightningPrototype = "Lightning", + float damage = 15, + bool electrocute = true, + bool explode = true + ) + { + return; + } + + /// + /// Fires a lightning bolt from one entity to another + /// Only done serverside + /// + public virtual void ShootLightning(EntityUid user, EntityUid target, LightningContext context) + { + return; + } + + /// + /// Looks for objects with a LightningTarget component in the radius, and fire lightning at (weighted) random targets + /// Only done serverside + /// + public virtual void ShootRandomLightnings(EntityUid user, float lightningRadius, int lightningCount, float lightningChargePer, EntityCoordinates? queryPosition = null, bool lightningStacking = true, + int maxArcs = 1, + float arcRange = 5f, + int arcForks = 1, + bool arcStacking = false, + string lightningPrototype = "Lightning", + bool electrocute = true, + bool explode = true + ) + { + return; + } + + /// + /// Looks for objects with a LightningTarget component in the radius, and fire lightning at (weighted) random targets + /// Only done serverside + /// + public virtual void ShootRandomLightnings(EntityUid user, float lightningRadius, int lightningCount, LightningContext context, EntityCoordinates? queryPosition = null, bool lightningStacking = true, + Func? dynamicCharge = null, + Func? dynamicArcs = null + ) + { + return; + } } + +public record struct LightningArc( + EntityUid User, + EntityUid Target, + int ContextId, + int ArcDepth +); + +public struct LightningContext +{ + // These are not parameters, and are handled by the LightningSystem + public int Id; + public EntityUid Invoker; + public List Arcs; + public List History; + + // Initial data that shouldn't be changed by staging + public float Charge; + public int MaxArcs; + + // Staging data before charge is even considered + public Func ArcRange; + public Func ArcForks; + public Func ArcStacking; + + // Effect data which can take discharge into account + public Func LightningPrototype; + public Func Electrocute; + public Func ElectrocuteDamage; + public Func ElectrocuteIgnoreInsulation; + public Func Explode; + + public LightningContext() + { + Arcs = []; + History = []; + + Charge = 50000f; + MaxArcs = 1; + + ArcRange = (LightningContext context) => 3.5f; + ArcForks = (LightningContext context) => 1; + ArcStacking = (LightningContext context) => false; + + LightningPrototype = (float discharge, LightningContext context) => "Lightning"; + Electrocute = (float discharge, LightningContext context) => true; + ElectrocuteDamage = (float discharge, LightningContext context) => { DamageSpecifier _Damage = new DamageSpecifier(); _Damage.DamageDict["Shock"] = discharge * 0.0002f; return _Damage; }; // damage increases by 1 for every 5000J + ElectrocuteIgnoreInsulation = (float discharge, LightningContext context) => false; + Explode = (float discharge, LightningContext context) => true; + } + + public LightningContext Clone() + { + LightningContext other = (LightningContext) MemberwiseClone(); + other.Arcs = new List(Arcs); + other.History = new List(History); + + return other; + } +}; From 301f95107e5760a0aeccec4c27637ba6984e5bd6 Mon Sep 17 00:00:00 2001 From: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Date: Sat, 24 Aug 2024 13:03:41 +1000 Subject: [PATCH 32/32] fix LightningArcShooter fork chance being inverted --- Content.Server/Tesla/EntitySystem/LightningArcShooterSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Server/Tesla/EntitySystem/LightningArcShooterSystem.cs b/Content.Server/Tesla/EntitySystem/LightningArcShooterSystem.cs index ccbd8222777..33ba7a21772 100644 --- a/Content.Server/Tesla/EntitySystem/LightningArcShooterSystem.cs +++ b/Content.Server/Tesla/EntitySystem/LightningArcShooterSystem.cs @@ -73,7 +73,7 @@ float DynamicCharge(LightningContext context) ArcRange = (context) => component.ArcRadius, ArcForks = (context) => { - if (_random.NextFloat(0f, 1f) < component.ForkChance) + if (_random.NextFloat(0f, 1f) > component.ForkChance) return 1; return _random.Next(2, Math.Max(2, component.MaxForks));