Fix lightning beam rotation (#42342)

* fix lightning rotation

* RT 272 fixed this

* yaml conventions
This commit is contained in:
slarticodefast 2026-05-29 21:16:36 +02:00 • committed by GitHub
parent 249d40500e
commit 0e898c5339
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 59 additions and 55 deletions

View file

@ -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<SpriteComponent>(beam, out var sprites))
{
_sprite.SetRotation((beam, sprites), args.UserAngle);
if (args.BodyState != null)
{
_sprite.LayerSetRsiState((beam, sprites), 0, args.BodyState);

View file

@ -1,8 +0,0 @@
using Content.Shared.Beam.Components;
namespace Content.Client.Beam.Components;
[RegisterComponent]
public sealed partial class BeamComponent : SharedBeamComponent
{
}

View file

@ -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<PhysicsComponent>(ent, out var physics) || !TryComp<BeamComponent>(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
/// <param name="bodyPrototype">The prototype spawned when this beam is created</param>
/// <param name="bodyState">Optional sprite state for the <see cref="bodyPrototype"/> if a default one is not given</param>
/// <param name="shader">Optional shader for the <see cref="bodyPrototype"/> if a default one is not given</param>
/// <param name="controller"></param>
/// <param name="controller">The virtual beam controller entity to use. If null one will be spawned.</param>
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<BeamComponent>(controller, out var controllerBeamComp))
//Where the start of the beam will spawn
var beamStartPos = userMapPos.Offset(calculatedDistance.Normalized());
if (TryComp<BeamComponent>(controller, out var controllerBeamComp))
{
controllerBeamComp.HitTargets.Add(user);
controllerBeamComp.HitTargets.Add(target);

View file

@ -1,8 +0,0 @@
using Content.Shared.Beam.Components;
namespace Content.Server.Beam.Components;
[RegisterComponent]
public sealed partial class BeamComponent : SharedBeamComponent
{
}

View file

@ -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++;

View file

@ -17,6 +17,13 @@ public sealed partial class LightningArcShooterComponent : Component
[DataField, ViewVariables(VVAccess.ReadWrite)]
public int MaxLightningArc = 1;
/// <summary>
/// Fire instantly after spawning?
/// If false this entity will fire after <see cref="ShootMaxInterval"/> instead.
/// </summary>
[DataField]
public bool Instant = false;
/// <summary>
/// Minimum interval between shooting.
/// </summary>

View file

@ -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)

View file

@ -1,48 +1,50 @@
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
namespace Content.Shared.Beam.Components;
/// <summary>
/// Use this as a generic beam. Not for something like a laser gun, more for something continuous like lightning.
/// </summary>
public abstract partial class SharedBeamComponent : Component
[RegisterComponent, NetworkedComponent]
public sealed partial class BeamComponent : Component
{
/// <summary>
/// A unique list of targets that this beam collided with.
/// Useful for code like Arcing in the Lightning Component.
/// </summary>
[DataField("hitTargets")]
[DataField]
public HashSet<EntityUid> HitTargets = new();
/// <summary>
/// The virtual entity representing a beam.
/// </summary>
[DataField("virtualBeamController")]
[DataField]
public EntityUid? VirtualBeamController;
/// <summary>
/// The first beam created, useful for keeping track of chains.
/// </summary>
[DataField("originBeam")]
[DataField]
public EntityUid OriginBeam;
/// <summary>
/// The entity that fired the beam originally
/// The entity that fired the beam originally.
/// </summary>
[DataField("beamShooter")]
[DataField]
public EntityUid BeamShooter;
/// <summary>
/// A unique list of created beams that the controller keeps track of.
/// </summary>
[DataField("createdBeams")]
[DataField]
public HashSet<EntityUid> CreatedBeams = new();
/// <summary>
/// Sound played upon creation
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("sound")]
[DataField]
public SoundSpecifier? Sound;
}

View file

@ -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