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