From 0e898c53392d8929b8af240993166b2064779e8f Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Fri, 29 May 2026 21:16:36 +0200 Subject: [PATCH] Fix lightning beam rotation (#42342) * fix lightning rotation * RT 272 fixed this * yaml conventions --- Content.Client/Beam/BeamSystem.cs | 5 +--- .../Beam/Components/BeamComponent.cs | 8 ----- Content.Server/Beam/BeamSystem.cs | 30 ++++++++++--------- .../Beam/Components/BeamComponent.cs | 8 ----- Content.Server/Lightning/LightningSystem.cs | 4 +-- .../LightningArcShooterComponent.cs | 7 +++++ .../EntitySystem/LightningArcShooterSystem.cs | 5 +++- ...haredBeamComponent.cs => BeamComponent.cs} | 20 +++++++------ .../Entities/Effects/admin_triggers.yml | 27 +++++++++++------ 9 files changed, 59 insertions(+), 55 deletions(-) delete mode 100644 Content.Client/Beam/Components/BeamComponent.cs delete mode 100644 Content.Server/Beam/Components/BeamComponent.cs rename Content.Shared/Beam/Components/{SharedBeamComponent.cs => BeamComponent.cs} (89%) diff --git a/Content.Client/Beam/BeamSystem.cs b/Content.Client/Beam/BeamSystem.cs index 93f5fa5b6d..379bc3af83 100644 --- a/Content.Client/Beam/BeamSystem.cs +++ b/Content.Client/Beam/BeamSystem.cs @@ -1,5 +1,4 @@ -using Content.Client.Beam.Components; -using Content.Shared.Beam; +using Content.Shared.Beam; using Content.Shared.Beam.Components; using Robust.Client.GameObjects; @@ -23,8 +22,6 @@ public sealed partial class BeamSystem : SharedBeamSystem if (TryComp(beam, out var sprites)) { - _sprite.SetRotation((beam, sprites), args.UserAngle); - if (args.BodyState != null) { _sprite.LayerSetRsiState((beam, sprites), 0, args.BodyState); diff --git a/Content.Client/Beam/Components/BeamComponent.cs b/Content.Client/Beam/Components/BeamComponent.cs deleted file mode 100644 index 58557b079c..0000000000 --- a/Content.Client/Beam/Components/BeamComponent.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Content.Shared.Beam.Components; - -namespace Content.Client.Beam.Components; -[RegisterComponent] -public sealed partial class BeamComponent : SharedBeamComponent -{ - -} diff --git a/Content.Server/Beam/BeamSystem.cs b/Content.Server/Beam/BeamSystem.cs index 08e8cef4e1..ea3839709d 100644 --- a/Content.Server/Beam/BeamSystem.cs +++ b/Content.Server/Beam/BeamSystem.cs @@ -1,16 +1,15 @@ using System.Numerics; -using Content.Server.Beam.Components; using Content.Shared.Beam; using Content.Shared.Beam.Components; using Content.Shared.Physics; using Robust.Server.GameObjects; -using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Map; using Robust.Shared.Physics; using Robust.Shared.Physics.Collision.Shapes; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Systems; +using Robust.Shared.Prototypes; namespace Content.Server.Beam; @@ -22,6 +21,8 @@ public sealed partial class BeamSystem : SharedBeamSystem [Dependency] private SharedBroadphaseSystem _broadphase = default!; [Dependency] private SharedPhysicsSystem _physics = default!; + private static readonly EntProtoId VirtualBeamEntityControllerId = "VirtualBeamEntityController"; + public override void Initialize() { base.Initialize(); @@ -77,8 +78,8 @@ public sealed partial class BeamSystem : SharedBeamSystem string shader = "unshaded") { var beamSpawnPos = beamStartPos; - var ent = Spawn(prototype, beamSpawnPos); - var shape = new EdgeShape(distanceCorrection, new Vector2(0,0)); + var ent = Spawn(prototype, beamSpawnPos, rotation: userAngle); + var shape = new EdgeShape(distanceCorrection, new Vector2(0, 0)); if (!TryComp(ent, out var physics) || !TryComp(ent, out var beam)) return; @@ -108,7 +109,7 @@ public sealed partial class BeamSystem : SharedBeamSystem else { - var controllerEnt = Spawn("VirtualBeamEntityController", beamSpawnPos); + var controllerEnt = Spawn(VirtualBeamEntityControllerId, beamSpawnPos); beam.VirtualBeamController = controllerEnt; _audio.PlayPvs(beam.Sound, ent); @@ -118,10 +119,11 @@ public sealed partial class BeamSystem : SharedBeamSystem } //Create the rest of the beam, sprites handled through the BeamVisualizerEvent - for (var i = 0; i < distanceLength-1; i++) + for (var i = 0; i < distanceLength - 1; i++) { beamSpawnPos = beamSpawnPos.Offset(calculatedDistance.Normalized()); var newEnt = Spawn(prototype, beamSpawnPos); + _transform.SetWorldRotation(newEnt, userAngle); var ev = new BeamVisualizerEvent(GetNetEntity(newEnt), distanceLength, userAngle, bodyState, shader); RaiseNetworkEvent(ev); @@ -140,7 +142,7 @@ public sealed partial class BeamSystem : SharedBeamSystem /// The prototype spawned when this beam is created /// Optional sprite state for the if a default one is not given /// Optional shader for the if a default one is not given - /// + /// The virtual beam controller entity to use. If null one will be spawned. public void TryCreateBeam(EntityUid user, EntityUid target, string bodyPrototype, string? bodyState = null, string shader = "unshaded", EntityUid? controller = null) { if (Deleted(user) || Deleted(target)) @@ -149,21 +151,21 @@ public sealed partial class BeamSystem : SharedBeamSystem var userMapPos = _transform.GetMapCoordinates(user); var targetMapPos = _transform.GetMapCoordinates(target); - //The distance between the target and the user. - var calculatedDistance = targetMapPos.Position - userMapPos.Position; - var userAngle = calculatedDistance.ToWorldAngle(); - if (userMapPos.MapId != targetMapPos.MapId) return; - //Where the start of the beam will spawn - var beamStartPos = userMapPos.Offset(calculatedDistance.Normalized()); + //The distance between the target and the user. + var calculatedDistance = targetMapPos.Position - userMapPos.Position; + var userAngle = calculatedDistance.ToWorldAngle(); //Don't divide by zero if (calculatedDistance.Length() == 0) return; - if (controller != null && TryComp(controller, out var controllerBeamComp)) + //Where the start of the beam will spawn + var beamStartPos = userMapPos.Offset(calculatedDistance.Normalized()); + + if (TryComp(controller, out var controllerBeamComp)) { controllerBeamComp.HitTargets.Add(user); controllerBeamComp.HitTargets.Add(target); diff --git a/Content.Server/Beam/Components/BeamComponent.cs b/Content.Server/Beam/Components/BeamComponent.cs deleted file mode 100644 index 175782e634..0000000000 --- a/Content.Server/Beam/Components/BeamComponent.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Content.Shared.Beam.Components; - -namespace Content.Server.Beam.Components; -[RegisterComponent] -public sealed partial class BeamComponent : SharedBeamComponent -{ - -} diff --git a/Content.Server/Lightning/LightningSystem.cs b/Content.Server/Lightning/LightningSystem.cs index ec715369e8..7a1ea116f6 100644 --- a/Content.Server/Lightning/LightningSystem.cs +++ b/Content.Server/Lightning/LightningSystem.cs @@ -1,6 +1,6 @@ using System.Linq; using Content.Server.Beam; -using Content.Server.Beam.Components; +using Content.Shared.Beam.Components; using Content.Server.Lightning.Components; using Content.Shared.Lightning; using Robust.Server.GameObjects; @@ -82,7 +82,7 @@ public sealed partial class LightningSystem : SharedLightningSystem int shootedCount = 0; int count = -1; - while(shootedCount < boltCount) + while (shootedCount < boltCount) { count++; diff --git a/Content.Server/Tesla/Components/LightningArcShooterComponent.cs b/Content.Server/Tesla/Components/LightningArcShooterComponent.cs index add7bf88e4..40577f443d 100644 --- a/Content.Server/Tesla/Components/LightningArcShooterComponent.cs +++ b/Content.Server/Tesla/Components/LightningArcShooterComponent.cs @@ -17,6 +17,13 @@ public sealed partial class LightningArcShooterComponent : Component [DataField, ViewVariables(VVAccess.ReadWrite)] public int MaxLightningArc = 1; + /// + /// Fire instantly after spawning? + /// If false this entity will fire after instead. + /// + [DataField] + public bool Instant = false; + /// /// Minimum interval between shooting. /// diff --git a/Content.Server/Tesla/EntitySystem/LightningArcShooterSystem.cs b/Content.Server/Tesla/EntitySystem/LightningArcShooterSystem.cs index da6de0fa39..5ca5f2853f 100644 --- a/Content.Server/Tesla/EntitySystem/LightningArcShooterSystem.cs +++ b/Content.Server/Tesla/EntitySystem/LightningArcShooterSystem.cs @@ -22,7 +22,10 @@ public sealed partial class LightningArcShooterSystem : EntitySystem private void OnShooterMapInit(EntityUid uid, LightningArcShooterComponent component, ref MapInitEvent args) { - component.NextShootTime = _gameTiming.CurTime + TimeSpan.FromSeconds(component.ShootMaxInterval); + if (component.Instant) + component.NextShootTime = _gameTiming.CurTime; + else + component.NextShootTime = _gameTiming.CurTime + TimeSpan.FromSeconds(component.ShootMaxInterval); } public override void Update(float frameTime) diff --git a/Content.Shared/Beam/Components/SharedBeamComponent.cs b/Content.Shared/Beam/Components/BeamComponent.cs similarity index 89% rename from Content.Shared/Beam/Components/SharedBeamComponent.cs rename to Content.Shared/Beam/Components/BeamComponent.cs index 9c5c5dd344..879a52757d 100644 --- a/Content.Shared/Beam/Components/SharedBeamComponent.cs +++ b/Content.Shared/Beam/Components/BeamComponent.cs @@ -1,48 +1,50 @@ using Robust.Shared.Audio; +using Robust.Shared.GameStates; using Robust.Shared.Serialization; namespace Content.Shared.Beam.Components; + /// /// Use this as a generic beam. Not for something like a laser gun, more for something continuous like lightning. /// -public abstract partial class SharedBeamComponent : Component +[RegisterComponent, NetworkedComponent] +public sealed partial class BeamComponent : Component { /// /// A unique list of targets that this beam collided with. /// Useful for code like Arcing in the Lightning Component. /// - [DataField("hitTargets")] + [DataField] public HashSet HitTargets = new(); /// /// The virtual entity representing a beam. /// - [DataField("virtualBeamController")] + [DataField] public EntityUid? VirtualBeamController; /// /// The first beam created, useful for keeping track of chains. /// - [DataField("originBeam")] + [DataField] public EntityUid OriginBeam; /// - /// The entity that fired the beam originally + /// The entity that fired the beam originally. /// - [DataField("beamShooter")] + [DataField] public EntityUid BeamShooter; /// /// A unique list of created beams that the controller keeps track of. /// - [DataField("createdBeams")] + [DataField] public HashSet CreatedBeams = new(); /// /// Sound played upon creation /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("sound")] + [DataField] public SoundSpecifier? Sound; } diff --git a/Resources/Prototypes/Entities/Effects/admin_triggers.yml b/Resources/Prototypes/Entities/Effects/admin_triggers.yml index df9e11bcff..d704b52e92 100644 --- a/Resources/Prototypes/Entities/Effects/admin_triggers.yml +++ b/Resources/Prototypes/Entities/Effects/admin_triggers.yml @@ -1,6 +1,6 @@ - type: entity - id: AdminInstantEffectBase abstract: true + id: AdminInstantEffectBase name: instant effect components: - type: Sprite @@ -12,26 +12,26 @@ lifetime: 5 - type: entity + parent: AdminInstantEffectBase id: AdminInstantEffectEMP suffix: EMP - parent: AdminInstantEffectBase components: - type: EmpOnTrigger range: 4 energyConsumption: 50000 - type: entity + parent: AdminInstantEffectBase id: AdminInstantEffectBluespace suffix: BluespaceFlash - parent: AdminInstantEffectBase components: - type: SpawnOnTrigger proto: EffectFlashBluespace - type: entity + parent: AdminInstantEffectBase id: AdminInstantEffectFlash suffix: Flash - parent: AdminInstantEffectBase components: - type: FlashOnTrigger range: 7 @@ -39,9 +39,9 @@ proto: GrenadeFlashEffect - type: entity + parent: AdminInstantEffectBase id: AdminInstantEffectSmoke3 suffix: Smoke (03 sec) - parent: AdminInstantEffectBase components: - type: SmokeOnTrigger duration: 3 @@ -53,9 +53,9 @@ path: /Audio/Effects/Smoke-grenade.ogg - type: entity + parent: AdminInstantEffectBase id: AdminInstantEffectSmoke10 suffix: Smoke (10 sec) - parent: AdminInstantEffectBase components: - type: SmokeOnTrigger duration: 10 @@ -67,9 +67,9 @@ path: /Audio/Effects/Smoke-grenade.ogg - type: entity + parent: AdminInstantEffectBase id: AdminInstantEffectSmoke30 suffix: Smoke (30 sec) - parent: AdminInstantEffectBase components: - type: SmokeOnTrigger duration: 30 @@ -81,9 +81,9 @@ path: /Audio/Effects/Smoke-grenade.ogg - type: entity + parent: AdminInstantEffectBase id: AdminInstantEffectTearGas suffix: Tear Gas - parent: AdminInstantEffectBase components: - type: SmokeOnTrigger duration: 10 @@ -94,9 +94,9 @@ Quantity: 50 - type: entity + parent: AdminInstantEffectBase id: AdminInstantEffectGravityWell suffix: Gravity Well - parent: AdminInstantEffectBase components: - type: EmitSoundOnTrigger sound: @@ -118,3 +118,12 @@ intensity: 10 falloffPower: 1.5 +- type: entity + parent: AdminInstantEffectBase + id: AdminInstantEffectLightning + suffix: Lightning + components: + - type: LightningArcShooter + instant: true + shootMinInterval: 6 # larger than the timed despawn so that we only shoot once + shootMaxInterval: 7