diff --git a/Content.Client/Light/RgbLightControllerSystem.cs b/Content.Client/Light/RgbLightControllerSystem.cs index 55dd5ab7aa..05e846c6d8 100644 --- a/Content.Client/Light/RgbLightControllerSystem.cs +++ b/Content.Client/Light/RgbLightControllerSystem.cs @@ -1,15 +1,18 @@ using System; +using System.Linq; +using Content.Shared.Item; using Content.Shared.Light; using Content.Shared.Light.Component; using Robust.Client.GameObjects; using Robust.Shared.GameObjects; +using Robust.Shared.GameStates; using Robust.Shared.IoC; using Robust.Shared.Maths; using Robust.Shared.Timing; namespace Content.Client.Light { - public class RgbLightControllerSystem : EntitySystem + public sealed class RgbLightControllerSystem : SharedRgbLightControllerSystem { [Dependency] private IGameTiming _gameTiming = default!; @@ -17,24 +20,115 @@ namespace Content.Client.Light { base.Initialize(); - UpdatesOutsidePrediction = true; + SubscribeLocalEvent(OnHandleState); + SubscribeLocalEvent(OnComponentShutdown); + SubscribeLocalEvent(OnComponentStart); } - public override void Update(float frameTime) + private void OnComponentStart(EntityUid uid, RgbLightControllerComponent rgb, ComponentStartup args) + { + if (TryComp(uid, out PointLightComponent? light)) + rgb.OriginalLightColor = light.Color; + + if (TryComp(uid, out SharedItemComponent? item)) + rgb.OriginalItemColor = item.Color; + + GetOriginalSpriteColors(uid, rgb); + } + + private void OnComponentShutdown(EntityUid uid, RgbLightControllerComponent rgb, ComponentShutdown args) + { + if (TryComp(uid, out PointLightComponent? light)) + light.Color = rgb.OriginalLightColor; + + if (TryComp(uid, out SharedItemComponent? item)) + item.Color = rgb.OriginalItemColor; + + ResetSpriteColors(uid, rgb); + } + + private void OnHandleState(EntityUid uid, RgbLightControllerComponent rgb, ref ComponentHandleState args) + { + if (args.Current is not RgbLightControllerState state) + return; + + ResetSpriteColors(uid, rgb); + rgb.CycleRate = state.CycleRate; + rgb.Layers = state.Layers; + + // get the new original sprite colors (necessary if rgb.Layers was updated). + GetOriginalSpriteColors(uid, rgb); + } + + private void GetOriginalSpriteColors(EntityUid uid, RgbLightControllerComponent? rgb = null, SpriteComponent? sprite = null) + { + if (!Resolve(uid, ref rgb, ref sprite)) + return; + + if (rgb.Layers == null) + { + rgb.OriginalSpriteColor = sprite.Color; + rgb.OriginalLayerColors = null; + return; + } + + var spriteLayerCount = sprite.AllLayers.Count(); + rgb.OriginalLayerColors = new(rgb.Layers.Count); + + foreach (var layer in rgb.Layers.ToArray()) + { + if (layer < spriteLayerCount) + rgb.OriginalLayerColors[layer] = sprite[layer].Color; + else + rgb.Layers.Remove(layer); + } + } + + private void ResetSpriteColors(EntityUid uid, RgbLightControllerComponent? rgb = null, SpriteComponent? sprite = null) + { + if (!Resolve(uid, ref rgb, ref sprite)) + return; + + if (rgb.Layers == null || rgb.OriginalLayerColors == null) + { + sprite.Color = rgb.OriginalSpriteColor; + return; + } + + foreach (var (layer, color) in rgb.OriginalLayerColors) + { + sprite.LayerSetColor(layer, color); + } + } + + public override void FrameUpdate(float frameTime) { - base.Update(frameTime); - var curTime = _gameTiming.CurTime; foreach (var (rgb, light, sprite) in EntityManager.EntityQuery()) { - light.Color = GetCurrentRgbColor(curTime, TimeSpan.FromSeconds(rgb.CreationTick.Value * _gameTiming.TickPeriod.TotalSeconds), rgb); - sprite.Color = GetCurrentRgbColor(curTime, TimeSpan.FromSeconds(rgb.CreationTick.Value * _gameTiming.TickPeriod.TotalSeconds), rgb); + var color = GetCurrentRgbColor(_gameTiming.RealTime, rgb.CreationTick.Value * _gameTiming.TickPeriod, rgb); + + light.Color = color; + + if (rgb.Layers == null) + sprite.Color = color; + else + { + foreach (var layer in rgb.Layers) + { + sprite.LayerSetColor(layer, color); + } + } + + // not all rgb is hand-held (Hence, not part of EntityQuery) + if (TryComp(rgb.Owner, out SharedItemComponent? item)) + item.Color = color; } } public static Color GetCurrentRgbColor(TimeSpan curTime, TimeSpan offset, RgbLightControllerComponent rgb) { return Color.FromHsv(new Vector4( - (float) (((curTime.TotalSeconds - offset.TotalSeconds) / rgb.CycleRate + Math.Abs(rgb.Owner.GetHashCode() * 0.1)) % 1), + (float) (((curTime.TotalSeconds - offset.TotalSeconds) * rgb.CycleRate + Math.Abs(rgb.Owner.GetHashCode() * 0.1)) % 1), 1.0f, 1.0f, 1.0f diff --git a/Content.Server/Light/EntitySystems/RgbLightControllerSystem.cs b/Content.Server/Light/EntitySystems/RgbLightControllerSystem.cs new file mode 100644 index 0000000000..bcecc42f21 --- /dev/null +++ b/Content.Server/Light/EntitySystems/RgbLightControllerSystem.cs @@ -0,0 +1,8 @@ +using Content.Shared.Light; + +namespace Content.Server.Light.EntitySystems; + +public sealed class RgbLightControllerSystem : SharedRgbLightControllerSystem +{ + // Howdy +} diff --git a/Content.Shared/Light/Component/RgbLightControllerComponent.cs b/Content.Shared/Light/Component/RgbLightControllerComponent.cs index 08c03f2fdf..9cca0c898f 100644 --- a/Content.Shared/Light/Component/RgbLightControllerComponent.cs +++ b/Content.Shared/Light/Component/RgbLightControllerComponent.cs @@ -1,21 +1,48 @@ +using Robust.Shared.Analyzers; using Robust.Shared.GameObjects; using Robust.Shared.GameStates; +using Robust.Shared.Maths; +using Robust.Shared.Serialization; using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.ViewVariables; +using System; +using System.Collections.Generic; -namespace Content.Shared.Light.Component +namespace Content.Shared.Light.Component; + +/// +/// Networked ~~solely for admemes~~ for completely legitimate reasons, like hacked energy swords. +/// +[NetworkedComponent] +[RegisterComponent] +[ComponentProtoName("RgbLightController")] +[Friend(typeof(SharedRgbLightControllerSystem))] +public sealed class RgbLightControllerComponent : Robust.Shared.GameObjects.Component { - /// - /// Networked solely for admemes. - /// - [NetworkedComponent] - [RegisterComponent] - public class RgbLightControllerComponent : Robust.Shared.GameObjects.Component - { - public override string Name => "RgbLightController"; + [DataField("cycleRate")] + public float CycleRate { get; set; } = 0.1f; - [DataField("cycleRate")] - [ViewVariables(VVAccess.ReadWrite)] - public float CycleRate { get; set; } = 10.0f; + /// + /// What layers of the sprite to modulate? If null, will affect the whole sprite. + /// + [DataField("layers")] + public List? Layers; + + // original colors when rgb was added. Used to revert Colors when removed. + public Color OriginalLightColor; + public Color OriginalItemColor; + public Color OriginalSpriteColor; + public Dictionary? OriginalLayerColors; +} + +[Serializable, NetSerializable] +public class RgbLightControllerState : ComponentState +{ + public readonly float CycleRate; + public readonly List? Layers; + + public RgbLightControllerState(float cycleRate, List? layers) + { + CycleRate = cycleRate; + Layers = layers; } } diff --git a/Content.Shared/Light/SharedRgbLightControllerSystem.cs b/Content.Shared/Light/SharedRgbLightControllerSystem.cs new file mode 100644 index 0000000000..32c1fc9841 --- /dev/null +++ b/Content.Shared/Light/SharedRgbLightControllerSystem.cs @@ -0,0 +1,39 @@ +using Content.Shared.Light.Component; +using Robust.Shared.GameObjects; +using Robust.Shared.GameStates; +using System.Collections.Generic; + +namespace Content.Shared.Light; + +public abstract class SharedRgbLightControllerSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnGetState); + } + + private void OnGetState(EntityUid uid, RgbLightControllerComponent component, ref ComponentGetState args) + { + args.State = new RgbLightControllerState(component.CycleRate, component.Layers); + } + + public void SetLayers(EntityUid uid, List? layers, RgbLightControllerComponent? rgb = null) + { + if (!Resolve(uid, ref rgb)) + return; + + rgb.Layers = layers; + rgb.Dirty(); + } + + public void SetCycleRate(EntityUid uid, float rate, RgbLightControllerComponent? rgb = null) + { + if (!Resolve(uid, ref rgb)) + return; + + rgb.CycleRate = rate; + rgb.Dirty(); + } +}