From 01fe85f77e1b64f8d03e7a57b37887c5de7b1de3 Mon Sep 17 00:00:00 2001 From: William Lemon Date: Fri, 26 Sep 2025 20:03:43 +1000 Subject: [PATCH] Fix Flares, Give Skia Spacewalk (#4417) * Fix various light sources counting as Light, Give Skia spacewalk * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Replace the Ignore Gravity with Jetpack * Clean up event code + Avoid touching upstream code where possible * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Double cone, strategic untouches * Additional strategic untouch --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../ExpandableLightEnergySystem.cs | 22 +++++++++ .../_DV/Light/LightReactiveSystem.cs | 2 +- .../ExpendableLightEnergySystem.cs | 37 +++++++++++++++ .../_DV/Light/LightReactiveSystem.cs | 5 +- .../SharedExpendableLightComponent.cs | 11 +++++ .../ItemTogglePointLightEnergySystem.cs | 27 +++++++++++ .../_DV/Light/SharedLightReactiveSystem.cs | 47 +++++++++++++++++-- .../Entities/Objects/Misc/torch.yml | 3 ++ .../Entities/Objects/Tools/flare.yml | 3 ++ .../Entities/Objects/Tools/glowstick.yml | 2 + .../_DV/Entities/Mobs/NPCs/skia.yml | 4 ++ 11 files changed, 157 insertions(+), 6 deletions(-) create mode 100644 Content.Client/_DV/Light/EntitySystems/ExpandableLightEnergySystem.cs create mode 100644 Content.Server/_DV/Light/EntitySystems/ExpendableLightEnergySystem.cs create mode 100644 Content.Shared/_DV/Light/EntitySystems/ItemTogglePointLightEnergySystem.cs diff --git a/Content.Client/_DV/Light/EntitySystems/ExpandableLightEnergySystem.cs b/Content.Client/_DV/Light/EntitySystems/ExpandableLightEnergySystem.cs new file mode 100644 index 0000000000..24b66b9898 --- /dev/null +++ b/Content.Client/_DV/Light/EntitySystems/ExpandableLightEnergySystem.cs @@ -0,0 +1,22 @@ +using Content.Client.Light.Components; +using Content.Shared._DV.Light; +using Robust.Client.GameObjects; + +namespace Content.Client._DV.Light.EntitySystems; + +public sealed partial class ExpendableLightEnergySystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(GetLightEnergy); + } + private void GetLightEnergy(Entity ent, ref OnGetLightEnergyEvent args) + { + if (!TryComp(ent, out var pointLight)) + return; + args.LightEnergy = pointLight.Energy; + args.LightRadius = pointLight.Radius; + } +} diff --git a/Content.Client/_DV/Light/LightReactiveSystem.cs b/Content.Client/_DV/Light/LightReactiveSystem.cs index 420b3d027f..cf8a93b023 100644 --- a/Content.Client/_DV/Light/LightReactiveSystem.cs +++ b/Content.Client/_DV/Light/LightReactiveSystem.cs @@ -16,7 +16,7 @@ public sealed partial class LightReactiveSystem : SharedLightReactiveSystem _validLightsInRange.Clear(); foreach (var light in _lightsInRange) { - if(light.Comp.Enabled && !light.Comp.Deleted && light.Comp.NetSyncEnabled) + if (light.Comp.Enabled && !light.Comp.Deleted) _validLightsInRange.Add(new(light.Owner, light.Comp)); } return _validLightsInRange; diff --git a/Content.Server/_DV/Light/EntitySystems/ExpendableLightEnergySystem.cs b/Content.Server/_DV/Light/EntitySystems/ExpendableLightEnergySystem.cs new file mode 100644 index 0000000000..f4727e7264 --- /dev/null +++ b/Content.Server/_DV/Light/EntitySystems/ExpendableLightEnergySystem.cs @@ -0,0 +1,37 @@ +using Content.Server.Light.Components; +using Content.Shared._DV.Light; +using Content.Shared.Light.Components; + +namespace Content.Server._DV.Light.EntitySystems; + +public sealed partial class ExpendableLightEnergySystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(GetLightEnergy); + } + + private void GetLightEnergy(Entity ent, ref OnGetLightEnergyEvent args) + { + // This isn't a perfect clone of the Clientside code, as it relies on animation behaviours + // and thus is by nature different per-client (Especially those with random flickers) + // This same code is not used clientside, because the client doesn't actually have access to the start time(??) + float lightFactor; + switch (ent.Comp.CurrentState) + { + case ExpendableLightState.Lit: + float timeElapsed = ent.Comp.GlowDuration.Seconds - ent.Comp.StateExpiryTime; + lightFactor = MathF.Min(1.0f, 1.0f - timeElapsed / ent.Comp.FadeInDuration); + break; + case ExpendableLightState.Fading: + lightFactor = MathF.Min(1.0f, ent.Comp.StateExpiryTime / ent.Comp.FadeOutDuration.Seconds); + break; + default: // Other states aren't lit. + return; + } + args.LightEnergy = ent.Comp.LitEnergy * lightFactor; + args.LightRadius = ent.Comp.LitRadius * lightFactor; + } +} diff --git a/Content.Server/_DV/Light/LightReactiveSystem.cs b/Content.Server/_DV/Light/LightReactiveSystem.cs index f2a062c054..18469bd5fe 100644 --- a/Content.Server/_DV/Light/LightReactiveSystem.cs +++ b/Content.Server/_DV/Light/LightReactiveSystem.cs @@ -16,7 +16,10 @@ public sealed partial class LightReactiveSystem : SharedLightReactiveSystem _validLightsInRange.Clear(); foreach (var light in _lightsInRange) { - if(light.Comp.Enabled && !light.Comp.Deleted && light.Comp.NetSyncEnabled) + // On the server, we check if it's Enabled OR if netSyncEnabled is false + // Because sometimes the server doesn't actually know if it should be enabled or not. + // The Client however, can be assumed to always be right. + if ((light.Comp.Enabled || !light.Comp.NetSyncEnabled) && !light.Comp.Deleted) _validLightsInRange.Add(new(light.Owner, light.Comp)); } return _validLightsInRange; diff --git a/Content.Shared/Light/Components/SharedExpendableLightComponent.cs b/Content.Shared/Light/Components/SharedExpendableLightComponent.cs index ad7a0edc4e..d9ffdfd668 100644 --- a/Content.Shared/Light/Components/SharedExpendableLightComponent.cs +++ b/Content.Shared/Light/Components/SharedExpendableLightComponent.cs @@ -42,6 +42,17 @@ public abstract partial class SharedExpendableLightComponent : Component [DataField] public SoundSpecifier? DieSound; + + // Begin DeltaV additions + [DataField(required: true)] + public float LitRadius = 0.0f; + + [DataField(required: true)] + public float LitEnergy = 0.0f; + + [DataField] + public float FadeInDuration = 0.0f; + // End DeltaV additions } [Serializable, NetSerializable] diff --git a/Content.Shared/_DV/Light/EntitySystems/ItemTogglePointLightEnergySystem.cs b/Content.Shared/_DV/Light/EntitySystems/ItemTogglePointLightEnergySystem.cs new file mode 100644 index 0000000000..15cea65e4e --- /dev/null +++ b/Content.Shared/_DV/Light/EntitySystems/ItemTogglePointLightEnergySystem.cs @@ -0,0 +1,27 @@ +using Content.Shared._DV.Light; +using Content.Shared.Light.Components; + +namespace Content.Shared._DV.Light.EntitySystems; + +public sealed partial class ItemTogglePointLightEnergySystem : EntitySystem +{ + [Dependency] private readonly SharedPointLightSystem _light = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(GetLightEnergy); + } + private void GetLightEnergy(Entity ent, ref OnGetLightEnergyEvent args) + { + if (!_light.TryGetLight(ent.Owner, out var light)) + return; + + if (!light.Enabled) + return; + + args.LightEnergy = light.Energy; + args.LightRadius = light.Radius; + } +} diff --git a/Content.Shared/_DV/Light/SharedLightReactiveSystem.cs b/Content.Shared/_DV/Light/SharedLightReactiveSystem.cs index 468c6f203c..3e42402902 100644 --- a/Content.Shared/_DV/Light/SharedLightReactiveSystem.cs +++ b/Content.Shared/_DV/Light/SharedLightReactiveSystem.cs @@ -77,6 +77,21 @@ public abstract class SharedLightReactiveSystem : EntitySystem foreach (var (lightUid, lightComp) in GetLights(uid)) { + var energy = lightComp.Energy; + var radius = lightComp.Radius; + if (!lightComp.NetSyncEnabled) + { + // Try to use the GetLightEnergyEvent if we can't rely on it being network synced. + var lightEnergyEvnt = new OnGetLightEnergyEvent(); + RaiseLocalEvent(lightUid, ref lightEnergyEvnt); + energy = lightEnergyEvnt.LightEnergy; + radius = lightEnergyEvnt.LightRadius; + if (MathHelper.CloseTo(energy, 0f)) + continue; // No light, no problem. + } + + energy = MathF.Min(energy, 2f); // Clamp energy, to normalize strange values. + // Ensure we're on the same grid as the light source if (_transform.GetMap(lightUid) != map) continue; @@ -84,26 +99,50 @@ public abstract class SharedLightReactiveSystem : EntitySystem // Ensure we're within the light's radius. var lightPos = _transform.GetWorldPosition(lightUid); var sqrDistance = Vector2.DistanceSquared(pos, lightPos); - if (sqrDistance > lightComp.Radius * lightComp.Radius) + if (sqrDistance > radius * radius) continue; if (sqrDistance < 0.01f) { // If we're right on top of the light, just add its full energy value. - val += lightComp.Energy; + val += energy; continue; } // Collision ray check from the entity to the light source var ray = new CollisionRay(pos, (lightPos - pos).Normalized(), (int)CollisionGroup.Opaque); - var hit = _physics.IntersectRay(_transform.GetMapId(uid), ray, MathF.Sqrt(sqrDistance) - 0.5f, returnOnFirstHit: true); + var hit = _physics.IntersectRay(_transform.GetMapId(uid), ray, MathF.Sqrt(sqrDistance) - 0.5f, ignoredEnt: lightUid, returnOnFirstHit: true); if (hit.Any() && hit.First().Distance != 0) continue; + + // Manual hack for cones. + if (lightComp.MaskPath == "/Textures/Effects/LightMasks/cone.png") + { + var forward = _transform.GetWorldRotation(lightUid).RotateVec(new Vector2(0.0f, -1.0f)); + energy *= MathF.Max(0f, Vector2.Dot((pos - lightPos).Normalized(), forward)); + } + else if (lightComp.MaskPath == "/Textures/Effects/LightMasks/double_cone.png") + { + var forward = _transform.GetWorldRotation(lightUid).RotateVec(new Vector2(0.0f, -1.0f)); + energy *= MathF.Abs(Vector2.Dot((pos - lightPos).Normalized(), forward)); + } + // If we reach here, the light is unobstructed and within range, calculate a light value to add. - val += lightComp.Energy * (1.0f - sqrDistance / (lightComp.Radius * lightComp.Radius)); + val += energy * (1.0f - sqrDistance / (radius * radius)); } return val; } } + +/// +/// Passed to unsync'd light sources to get the expected light energy. +/// ONLY called when NetSync is not enabled. Otherwise, uses the light directly. +/// +[ByRefEvent] +public record struct OnGetLightEnergyEvent() +{ + public float LightEnergy = 0f; + public float LightRadius = 0f; +} diff --git a/Resources/Prototypes/Entities/Objects/Misc/torch.yml b/Resources/Prototypes/Entities/Objects/Misc/torch.yml index da4886912c..dfac6f7f7b 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/torch.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/torch.yml @@ -12,6 +12,9 @@ iconStateSpent: torch_spent turnOnBehaviourID: turn_on fadeOutBehaviourID: fade_out + litRadius: 6.0 # DeltaV + litEnergy: 5.0 # DeltaV + fadeInDuration: 8.0 # DeltaV # Sounds legit nuff litSound: path: /Audio/Items/Flare/flare_on.ogg diff --git a/Resources/Prototypes/Entities/Objects/Tools/flare.yml b/Resources/Prototypes/Entities/Objects/Tools/flare.yml index 34c233921e..415b125dcb 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/flare.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/flare.yml @@ -16,6 +16,9 @@ iconStateSpent: flare_spent turnOnBehaviourID: turn_on fadeOutBehaviourID: fade_out + litRadius: 10.0 # DeltaV + litEnergy: 9.0 # DeltaV + fadeInDuration: 45.0 # DeltaV litSound: path: /Audio/Items/Flare/flare_on.ogg loopedSound: diff --git a/Resources/Prototypes/Entities/Objects/Tools/glowstick.yml b/Resources/Prototypes/Entities/Objects/Tools/glowstick.yml index c577cd8e58..e5b26053d0 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/glowstick.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/glowstick.yml @@ -13,6 +13,8 @@ fadeOutBehaviourID: fade_out iconStateLit: glowstick_lit iconStateSpent: glowstick_unlit + litRadius: 5.0 # DeltaV + litEnergy: 3.0 # DeltaV litSound: path: /Audio/Items/Handcuffs/rope_breakout.ogg - type: Sprite diff --git a/Resources/Prototypes/_DV/Entities/Mobs/NPCs/skia.yml b/Resources/Prototypes/_DV/Entities/Mobs/NPCs/skia.yml index b188b74697..f43e6f7ec9 100644 --- a/Resources/Prototypes/_DV/Entities/Mobs/NPCs/skia.yml +++ b/Resources/Prototypes/_DV/Entities/Mobs/NPCs/skia.yml @@ -44,6 +44,10 @@ - type: MovementSpeedModifier baseWalkSpeed: 2.25 baseSprintSpeed: 3.75 + - type: NoSlip + - type: MovedByPressure + enabled: false + - type: JetpackUser - type: CombatMode - type: MeleeWeapon soundHit: