diff --git a/Content.Client/Light/Components/ExpendableLightComponent.cs b/Content.Client/Light/Components/ExpendableLightComponent.cs
index d2cc535cf4..6d22bdf161 100644
--- a/Content.Client/Light/Components/ExpendableLightComponent.cs
+++ b/Content.Client/Light/Components/ExpendableLightComponent.cs
@@ -1,14 +1,55 @@
+using Content.Client.Light.EntitySystems;
using Content.Shared.Light.Component;
using Robust.Shared.Audio;
-namespace Content.Client.Light.Components
+namespace Content.Client.Light.Components;
+
+///
+/// Component that represents a handheld expendable light which can be activated and eventually dies over time.
+///
+[RegisterComponent]
+public sealed class ExpendableLightComponent : SharedExpendableLightComponent
{
///
- /// Component that represents a handheld expendable light which can be activated and eventually dies over time.
+ /// The icon state used by expendable lights when the they have been completely expended.
///
- [RegisterComponent]
- public sealed class ExpendableLightComponent : SharedExpendableLightComponent
- {
- public IPlayingAudioStream? PlayingStream { get; set; }
- }
+ [DataField("iconStateSpent")]
+ public string? IconStateSpent;
+
+ ///
+ /// The icon state used by expendable lights while they are lit.
+ ///
+ [DataField("iconStateLit")]
+ public string? IconStateLit;
+
+ ///
+ /// The sprite layer shader used while the expendable light is lit.
+ ///
+ [DataField("spriteShaderLit")]
+ public string? SpriteShaderLit = null;
+
+ ///
+ /// The sprite layer shader used after the expendable light has burnt out.
+ ///
+ [DataField("spriteShaderSpent")]
+ public string? SpriteShaderSpent = null;
+
+ ///
+ /// The sprite layer shader used after the expendable light has burnt out.
+ ///
+ [DataField("glowColorLit")]
+ public Color? GlowColorLit = null;
+
+ ///
+ /// The sound that plays when the expendable light is lit.
+ ///
+ [Access(typeof(ExpendableLightSystem))]
+ public IPlayingAudioStream? PlayingStream;
+}
+
+public enum ExpendableLightVisualLayers : byte
+{
+ Base = 0,
+ Glow = 1,
+ Overlay = 2,
}
diff --git a/Content.Client/Light/EntitySystems/ExpendableLightSystem.cs b/Content.Client/Light/EntitySystems/ExpendableLightSystem.cs
index c982f5c463..d180f430db 100644
--- a/Content.Client/Light/EntitySystems/ExpendableLightSystem.cs
+++ b/Content.Client/Light/EntitySystems/ExpendableLightSystem.cs
@@ -1,9 +1,15 @@
using Content.Client.Light.Components;
+using Content.Shared.Light.Component;
+using Robust.Client.GameObjects;
+using Robust.Client.Graphics;
namespace Content.Client.Light.EntitySystems;
-public sealed class ExpendableLightSystem : EntitySystem
+public sealed class ExpendableLightSystem : VisualizerSystem
{
+ [Dependency] private readonly PointLightSystem _pointLightSystem = default!;
+ [Dependency] private readonly SharedAudioSystem _audioSystem = default!;
+
public override void Initialize()
{
base.Initialize();
@@ -15,4 +21,71 @@ public sealed class ExpendableLightSystem : EntitySystem
{
component.PlayingStream?.Stop();
}
+
+ protected override void OnAppearanceChange(EntityUid uid, ExpendableLightComponent comp, ref AppearanceChangeEvent args)
+ {
+ if (args.Sprite == null)
+ return;
+
+ if (AppearanceSystem.TryGetData(uid, ExpendableLightVisuals.Behavior, out var lightBehaviourID, args.Component)
+ && TryComp(uid, out var lightBehaviour))
+ {
+ lightBehaviour.StopLightBehaviour();
+
+ if (!string.IsNullOrEmpty(lightBehaviourID))
+ {
+ lightBehaviour.StartLightBehaviour(lightBehaviourID);
+ }
+ else if (TryComp(uid, out var light))
+ {
+ _pointLightSystem.SetEnabled(uid, false, light);
+ }
+ }
+
+ if (!AppearanceSystem.TryGetData(uid, ExpendableLightVisuals.State, out var state, args.Component))
+ return;
+
+ switch (state)
+ {
+ case ExpendableLightState.Lit:
+ comp.PlayingStream?.Stop();
+ comp.PlayingStream = _audioSystem.PlayPvs(
+ comp.LoopedSound,
+ uid,
+ SharedExpendableLightComponent.LoopedSoundParams
+ );
+ if (args.Sprite.LayerMapTryGet(ExpendableLightVisualLayers.Overlay, out var layerIdx, true))
+ {
+ if (!string.IsNullOrWhiteSpace(comp.IconStateLit))
+ args.Sprite.LayerSetState(layerIdx, comp.IconStateLit);
+ if (!string.IsNullOrWhiteSpace(comp.SpriteShaderLit))
+ args.Sprite.LayerSetShader(layerIdx, comp.SpriteShaderLit);
+ else
+ args.Sprite.LayerSetShader(layerIdx, null, null);
+ if (comp.GlowColorLit.HasValue)
+ args.Sprite.LayerSetColor(layerIdx, comp.GlowColorLit.Value);
+ args.Sprite.LayerSetVisible(layerIdx, true);
+ }
+
+ if (comp.GlowColorLit.HasValue)
+ args.Sprite.LayerSetColor(ExpendableLightVisualLayers.Glow, comp.GlowColorLit.Value);
+ args.Sprite.LayerSetVisible(ExpendableLightVisualLayers.Glow, true);
+
+ break;
+ case ExpendableLightState.Dead:
+ comp.PlayingStream?.Stop();
+ if (args.Sprite.LayerMapTryGet(ExpendableLightVisualLayers.Overlay, out layerIdx, true))
+ {
+ if (!string.IsNullOrWhiteSpace(comp.IconStateSpent))
+ args.Sprite.LayerSetState(layerIdx, comp.IconStateSpent);
+ if (!string.IsNullOrWhiteSpace(comp.SpriteShaderSpent))
+ args.Sprite.LayerSetShader(layerIdx, comp.SpriteShaderSpent);
+ else
+ args.Sprite.LayerSetShader(layerIdx, null, null);
+ }
+
+ args.Sprite.LayerSetVisible(ExpendableLightVisualLayers.Glow, false);
+ break;
+ }
+ }
}
diff --git a/Content.Client/Light/Visualizers/ExpendableLightVisualizer.cs b/Content.Client/Light/Visualizers/ExpendableLightVisualizer.cs
deleted file mode 100644
index 012e918e36..0000000000
--- a/Content.Client/Light/Visualizers/ExpendableLightVisualizer.cs
+++ /dev/null
@@ -1,80 +0,0 @@
-using Content.Client.Light.Components;
-using Content.Shared.Light.Component;
-using JetBrains.Annotations;
-using Robust.Client.GameObjects;
-
-namespace Content.Client.Light.Visualizers
-{
- [UsedImplicitly]
- public sealed class ExpendableLightVisualizer : AppearanceVisualizer
- {
- [DataField("iconStateSpent")]
- public string? IconStateSpent { get; set; }
-
- [DataField("iconStateOn")]
- public string? IconStateLit { get; set; }
-
- [Obsolete("Subscribe to AppearanceChangeEvent instead.")]
- public override void OnChangeData(AppearanceComponent component)
- {
- base.OnChangeData(component);
-
- var entities = IoCManager.Resolve();
-
- if (!entities.TryGetComponent(component.Owner, out ExpendableLightComponent? expendableLight))
- return;
-
- if (!entities.TryGetComponent(component.Owner, out SpriteComponent? sprite))
- return;
-
- if (component.TryGetData(ExpendableLightVisuals.Behavior, out string lightBehaviourID))
- {
- if (entities.TryGetComponent(component.Owner, out LightBehaviourComponent? lightBehaviour))
- {
- lightBehaviour.StopLightBehaviour();
-
- if (lightBehaviourID != string.Empty)
- {
- lightBehaviour.StartLightBehaviour(lightBehaviourID);
- }
- else if (entities.TryGetComponent(component.Owner, out PointLightComponent? light))
- {
- light.Enabled = false;
- }
- }
- }
-
- if (!component.TryGetData(ExpendableLightVisuals.State, out ExpendableLightState state))
- return;
-
- switch (state)
- {
- case ExpendableLightState.Lit:
- expendableLight.PlayingStream?.Stop();
- expendableLight.PlayingStream = entities.EntitySysManager.GetEntitySystem().PlayPvs(
- expendableLight.LoopedSound,
- expendableLight.Owner,
- SharedExpendableLightComponent.LoopedSoundParams);
- if (!string.IsNullOrWhiteSpace(IconStateLit))
- {
- sprite.LayerSetState(2, IconStateLit);
- sprite.LayerSetShader(2, "shaded");
- }
-
- sprite.LayerSetVisible(1, true);
-
- break;
- case ExpendableLightState.Dead:
- expendableLight.PlayingStream?.Stop();
- if (!string.IsNullOrWhiteSpace(IconStateSpent))
- {
- sprite.LayerSetState(0, IconStateSpent);
- sprite.LayerSetShader(0, "shaded");
- }
-
- sprite.LayerSetVisible(1, false);
- break;
- }
- }
- }
-}
diff --git a/Resources/Prototypes/Entities/Objects/Misc/torch.yml b/Resources/Prototypes/Entities/Objects/Misc/torch.yml
index 7cff2072d2..c014d72ee0 100644
--- a/Resources/Prototypes/Entities/Objects/Misc/torch.yml
+++ b/Resources/Prototypes/Entities/Objects/Misc/torch.yml
@@ -18,8 +18,10 @@
- type: Sprite
sprite: Objects/Misc/torch.rsi
layers:
- - state: torch_unlit
- - state: lit_overlay
+ - map: [ enum.ExpendableLightVisualLayers.Base ]
+ state: torch_unlit
+ - map: [ enum.ExpendableLightVisualLayers.Glow ]
+ state: lit_overlay
color: "#FFFFFF"
visible: false
shader: unshaded
@@ -33,8 +35,6 @@
graph: LightTorch
node: torch
- type: Appearance
- visuals:
- - type: ExpendableLightVisualizer
- type: PointLight
enabled: false
color: "#E25822"
diff --git a/Resources/Prototypes/Entities/Objects/Tools/flare.yml b/Resources/Prototypes/Entities/Objects/Tools/flare.yml
index e8fb33e73e..169cc911db 100644
--- a/Resources/Prototypes/Entities/Objects/Tools/flare.yml
+++ b/Resources/Prototypes/Entities/Objects/Tools/flare.yml
@@ -24,12 +24,15 @@
- type: Sprite
sprite: Objects/Misc/flare.rsi
layers:
- - state: flare_base
- - state: flare_burn
+ - map: [ enum.ExpendableLightVisualLayers.Base ]
+ state: flare_base
+ - map: [ enum.ExpendableLightVisualLayers.Glow ]
+ state: flare_burn
color: "#FFFFFF"
visible: false
shader: unshaded
- - state: flare_unlit
+ - map: [ enum.ExpendableLightVisualLayers.Overlay ]
+ state: flare_unlit
color: "#FF0000"
- type: Icon
sprite: Objects/Misc/flare.rsi
@@ -38,8 +41,6 @@
sprite: Objects/Misc/flare.rsi
heldPrefix: unlit
- type: Appearance
- visuals:
- - type: ExpendableLightVisualizer
- type: PointLight
enabled: false
color: "#FF8080"
diff --git a/Resources/Prototypes/Entities/Objects/Tools/glowstick.yml b/Resources/Prototypes/Entities/Objects/Tools/glowstick.yml
index 41919ddce6..3d7041829c 100644
--- a/Resources/Prototypes/Entities/Objects/Tools/glowstick.yml
+++ b/Resources/Prototypes/Entities/Objects/Tools/glowstick.yml
@@ -9,29 +9,29 @@
spentName: expendable-light-spent-green-glowstick-name
spentDesc: expendable-light-spent-glowstick-desc
glowDuration: 900 # time in seconds
+ glowColorLit: "#00FF00"
fadeOutDuration: 300
turnOnBehaviourID: turn_on
fadeOutBehaviourID: fade_out
+ iconStateLit: glowstick_lit
+ iconStateSpent: glowstick_unlit
litSound:
path: /Audio/Items/Handcuffs/rope_breakout.ogg
- type: Sprite
sprite: Objects/Misc/glowstick.rsi
layers:
- - state: glowstick_base
- - state: glowstick_glow
- color: "#00FF00"
- visible: false
- shader: unshaded
- - state: glowstick_unlit
- color: "#00FF00"
+ - map: [ enum.ExpendableLightVisualLayers.Base ]
+ state: glowstick_base
+ - map: [ enum.ExpendableLightVisualLayers.Glow ]
+ state: glowstick_glow
+ visible: false
+ shader: unshaded
+ - map: [ enum.ExpendableLightVisualLayers.Overlay ]
+ state: glowstick_unlit
- type: Item
sprite: Objects/Misc/glowstick.rsi
heldPrefix: unlit
- type: Appearance
- visuals:
- - type: ExpendableLightVisualizer
- iconStateOn: glowstick_lit
- iconStateSpent: glowstick_unlit
- type: PointLight
enabled: false
color: "#00FF00"
@@ -70,21 +70,10 @@
- type: ExpendableLight
spentName: expendable-light-spent-red-glowstick-name
glowDuration: 10 # time in seconds
+ glowColorLit: "#FF0000"
fadeOutDuration: 5
- - type: Sprite
- layers:
- - state: glowstick_base
- - state: glowstick_glow
- color: "#FF0000"
- visible: false
- shader: unshaded
- - state: glowstick_unlit
- color: "#FF0000"
- type: PointLight
- enabled: false
color: "#FF0000"
- radius: 5
- energy: 0
- type: entity
name: purple glowstick
@@ -93,20 +82,9 @@
components:
- type: ExpendableLight
spentName: expendable-light-spent-purple-glowstick-name
- - type: Sprite
- layers:
- - state: glowstick_base
- - state: glowstick_glow
- color: "#FF00FF"
- visible: false
- shader: unshaded
- - state: glowstick_unlit
- color: "#FF00FF"
+ glowColorLit: "#FF00FF"
- type: PointLight
- enabled: false
color: "#FF00FF"
- radius: 5
- energy: 0
- type: entity
name: yellow glowstick
@@ -115,21 +93,9 @@
components:
- type: ExpendableLight
spentName: expendable-light-spent-yellow-glowstick-name
- - type: Sprite
- sprite: Objects/Misc/glowstick.rsi
- layers:
- - state: glowstick_base
- - state: glowstick_glow
- color: "#FFFF00"
- visible: false
- shader: unshaded
- - state: glowstick_unlit
- color: "#FFFF00"
+ glowColorLit: "#FFFF00"
- type: PointLight
- enabled: false
color: "#FFFF00"
- radius: 5
- energy: 0
- type: entity
name: blue glowstick
@@ -138,22 +104,9 @@
components:
- type: ExpendableLight
spentName: expendable-light-spent-blue-glowstick-name
- - type: Sprite
- sprite: Objects/Misc/glowstick.rsi
- layers:
- - state: glowstick_base
- - state: glowstick_glow
- color: "#0000FF"
- visible: false
- shader: unshaded
- - state: glowstick_unlit
- color: "#0000FF"
- heldPrefix: unlit
+ glowColorLit: "#0000FF"
- type: PointLight
- enabled: false
color: "#0000FF"
- radius: 5
- energy: 0
# ----------------------------------------------------------------------------
# THE FOLLOWING ARE ALL DUMMY ENTITIES USED TO TEST THE LIGHT BEHAVIOUR SYSTEM