diff --git a/Content.Client/Singularity/Systems/EmitterSystem.cs b/Content.Client/Singularity/Systems/EmitterSystem.cs new file mode 100644 index 0000000000..599b133326 --- /dev/null +++ b/Content.Client/Singularity/Systems/EmitterSystem.cs @@ -0,0 +1,59 @@ +using Content.Client.Storage.Visualizers; +using Content.Shared.Singularity.Components; +using Content.Shared.Singularity.EntitySystems; +using Content.Shared.Storage; +using Robust.Client.GameObjects; + +namespace Content.Client.Singularity.Systems; + +public sealed class EmitterSystem : SharedEmitterSystem +{ + [Dependency] private readonly AppearanceSystem _appearance = default!; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnAppearanceChange); + } + + private void OnAppearanceChange(EntityUid uid, EmitterComponent component, ref AppearanceChangeEvent args) + { + if (args.Sprite == null) + return; + + if (args.Sprite.LayerMapTryGet(StorageVisualLayers.Lock, out var lockLayer)) + { + if (!_appearance.TryGetData(uid, StorageVisuals.Locked, out bool locked, args.Component)) + locked = false; + + args.Sprite.LayerSetVisible(lockLayer, locked); + } + + if (!_appearance.TryGetData(uid, EmitterVisuals.VisualState, out EmitterVisualState state, args.Component)) + state = EmitterVisualState.Off; + + if (!args.Sprite.LayerMapTryGet(EmitterVisualLayers.Lights, out var layer)) + return; + + switch (state) + { + case EmitterVisualState.On: + if (component.OnState == null) + break; + args.Sprite.LayerSetVisible(layer, true); + args.Sprite.LayerSetState(layer, component.OnState); + break; + case EmitterVisualState.Underpowered: + if (component.UnderpoweredState == null) + break; + args.Sprite.LayerSetVisible(layer, true); + args.Sprite.LayerSetState(layer, component.UnderpoweredState); + break; + case EmitterVisualState.Off: + args.Sprite.LayerSetVisible(layer, false); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } +} diff --git a/Content.Client/Singularity/Visualizers/EmitterVisualizer.cs b/Content.Client/Singularity/Visualizers/EmitterVisualizer.cs deleted file mode 100644 index f9c094641d..0000000000 --- a/Content.Client/Singularity/Visualizers/EmitterVisualizer.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using Content.Shared.Singularity.Components; -using Content.Shared.Storage; -using JetBrains.Annotations; -using Robust.Client.GameObjects; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; - -namespace Content.Client.Singularity.Visualizers -{ - [UsedImplicitly] - public sealed class EmitterVisualizer : AppearanceVisualizer - { - private const string OverlayBeam = "beam"; - private const string OverlayUnderPowered = "underpowered"; - - [Obsolete("Subscribe to AppearanceChangeEvent instead.")] - public override void OnChangeData(AppearanceComponent component) - { - base.OnChangeData(component); - - var entities = IoCManager.Resolve(); - if (!entities.TryGetComponent(component.Owner, out ISpriteComponent? sprite)) - { - return; - } - - if (!component.TryGetData(StorageVisuals.Locked, out bool locked)) - locked = false; - - - if (!component.TryGetData(EmitterVisuals.VisualState, out EmitterVisualState state)) - state = EmitterVisualState.Off; - - switch (state) - { - case EmitterVisualState.On: - sprite.LayerSetVisible(1, true); - sprite.LayerSetState(1, OverlayBeam); - break; - case EmitterVisualState.Underpowered: - sprite.LayerSetVisible(1, true); - sprite.LayerSetState(1, OverlayUnderPowered); - break; - case EmitterVisualState.Off: - sprite.LayerSetVisible(1, false); - break; - default: - throw new ArgumentOutOfRangeException(); - } - - sprite.LayerSetVisible(2, locked); - } - } -} diff --git a/Content.Server/Singularity/Components/EmitterComponent.cs b/Content.Server/Singularity/Components/EmitterComponent.cs deleted file mode 100644 index 817725e5ec..0000000000 --- a/Content.Server/Singularity/Components/EmitterComponent.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System.Threading; -using Content.Shared.Construction.Prototypes; -using Robust.Shared.Audio; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Server.Singularity.Components -{ - [RegisterComponent] - public sealed class EmitterComponent : Component - { - public CancellationTokenSource? TimerCancel; - - // whether the power switch is in "on" - [ViewVariables] public bool IsOn; - // Whether the power switch is on AND the machine has enough power (so is actively firing) - [ViewVariables] public bool IsPowered; - - /// - /// counts the number of consecutive shots fired. - /// - [ViewVariables] - public int FireShotCounter; - - /// - /// The entity that is spawned when the emitter fires. - /// - [DataField("boltType")] - public string BoltType = "EmitterBolt"; - - /// - /// The current amount of power being used. - /// - [DataField("powerUseActive")] - public int PowerUseActive = 600; - - /// - /// The base amount of power that is consumed. - /// Used in machine part rating calculations. - /// - [DataField("basePowerUseActive"), ViewVariables(VVAccess.ReadWrite)] - public int BasePowerUseActive = 600; - - /// - /// Multiplier that is applied to the basePowerUseActive - /// to get the actual power use. - /// - [DataField("powerUseMultiplier")] - public float PowerUseMultiplier = 0.75f; - - /// - /// The machine part used to reduce the power use of the machine. - /// - [DataField("machinePartPowerUse", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string MachinePartPowerUse = "Capacitor"; - - /// - /// The amount of shots that are fired in a single "burst" - /// - [DataField("fireBurstSize")] - public int FireBurstSize = 3; - - /// - /// The time between each shot during a burst. - /// - [DataField("fireInterval")] - public TimeSpan FireInterval = TimeSpan.FromSeconds(2); - - /// - /// The base amount of time between each shot during a burst. - /// - [DataField("baseFireInterval"), ViewVariables(VVAccess.ReadWrite)] - public TimeSpan BaseFireInterval = TimeSpan.FromSeconds(2); - - /// - /// The current minimum delay between bursts. - /// - [DataField("fireBurstDelayMin")] - public TimeSpan FireBurstDelayMin = TimeSpan.FromSeconds(4); - - /// - /// The current maximum delay between bursts. - /// - [DataField("fireBurstDelayMax")] - public TimeSpan FireBurstDelayMax = TimeSpan.FromSeconds(10); - - /// - /// The base minimum delay between shot bursts. - /// Used for machine part rating calculations. - /// - [DataField("baseFireBurstDelayMin")] - public TimeSpan BaseFireBurstDelayMin = TimeSpan.FromSeconds(4); - - /// - /// The base maximum delay between shot bursts. - /// Used for machine part rating calculations. - /// - [DataField("baseFireBurstDelayMax")] - public TimeSpan BaseFireBurstDelayMax = TimeSpan.FromSeconds(10); - - /// - /// The multiplier for the base delay between shot bursts as well as - /// the fire interval - /// - [DataField("fireRateMultiplier"), ViewVariables(VVAccess.ReadWrite)] - public float FireRateMultiplier = 0.8f; - - /// - /// The machine part that affects burst delay. - /// - [DataField("machinePartFireRate", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string MachinePartFireRate = "Laser"; - } -} diff --git a/Content.Server/Singularity/EntitySystems/EmitterSystem.cs b/Content.Server/Singularity/EntitySystems/EmitterSystem.cs index 862c31f987..44986ab518 100644 --- a/Content.Server/Singularity/EntitySystems/EmitterSystem.cs +++ b/Content.Server/Singularity/EntitySystems/EmitterSystem.cs @@ -4,7 +4,6 @@ using Content.Server.Construction; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Server.Projectiles; -using Content.Server.Singularity.Components; using Content.Server.Storage.Components; using Content.Server.Weapons.Ranged.Systems; using Content.Shared.Database; @@ -12,6 +11,7 @@ using Content.Shared.Interaction; using Content.Shared.Popups; using Content.Shared.Projectiles; using Content.Shared.Singularity.Components; +using Content.Shared.Singularity.EntitySystems; using Content.Shared.Weapons.Ranged.Components; using JetBrains.Annotations; using Robust.Shared.Map; @@ -24,7 +24,7 @@ using Timer = Robust.Shared.Timing.Timer; namespace Content.Server.Singularity.EntitySystems { [UsedImplicitly] - public sealed class EmitterSystem : EntitySystem + public sealed class EmitterSystem : SharedEmitterSystem { [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!; diff --git a/Content.Shared/Singularity/Components/SharedEmitterComponent.cs b/Content.Shared/Singularity/Components/SharedEmitterComponent.cs index 2fca9d4d0a..f2362cae85 100644 --- a/Content.Shared/Singularity/Components/SharedEmitterComponent.cs +++ b/Content.Shared/Singularity/Components/SharedEmitterComponent.cs @@ -1,18 +1,145 @@ -using Robust.Shared.Serialization; +using System.Threading; +using Content.Shared.Construction.Prototypes; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -namespace Content.Shared.Singularity.Components +namespace Content.Shared.Singularity.Components; + +[RegisterComponent, NetworkedComponent] +public sealed class EmitterComponent : Component { - [NetSerializable, Serializable] - public enum EmitterVisuals - { - VisualState - } + public CancellationTokenSource? TimerCancel; - [NetSerializable, Serializable] - public enum EmitterVisualState - { - On, - Underpowered, - Off - } + // whether the power switch is in "on" + [ViewVariables] public bool IsOn; + // Whether the power switch is on AND the machine has enough power (so is actively firing) + [ViewVariables] public bool IsPowered; + + /// + /// counts the number of consecutive shots fired. + /// + [ViewVariables] + public int FireShotCounter; + + /// + /// The entity that is spawned when the emitter fires. + /// + [DataField("boltType")] + public string BoltType = "EmitterBolt"; + + /// + /// The current amount of power being used. + /// + [DataField("powerUseActive")] + public int PowerUseActive = 600; + + /// + /// The base amount of power that is consumed. + /// Used in machine part rating calculations. + /// + [DataField("basePowerUseActive"), ViewVariables(VVAccess.ReadWrite)] + public int BasePowerUseActive = 600; + + /// + /// Multiplier that is applied to the basePowerUseActive + /// to get the actual power use. + /// + [DataField("powerUseMultiplier")] + public float PowerUseMultiplier = 0.75f; + + /// + /// The machine part used to reduce the power use of the machine. + /// + [DataField("machinePartPowerUse", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string MachinePartPowerUse = "Capacitor"; + + /// + /// The amount of shots that are fired in a single "burst" + /// + [DataField("fireBurstSize")] + public int FireBurstSize = 3; + + /// + /// The time between each shot during a burst. + /// + [DataField("fireInterval")] + public TimeSpan FireInterval = TimeSpan.FromSeconds(2); + + /// + /// The base amount of time between each shot during a burst. + /// + [DataField("baseFireInterval"), ViewVariables(VVAccess.ReadWrite)] + public TimeSpan BaseFireInterval = TimeSpan.FromSeconds(2); + + /// + /// The current minimum delay between bursts. + /// + [DataField("fireBurstDelayMin")] + public TimeSpan FireBurstDelayMin = TimeSpan.FromSeconds(4); + + /// + /// The current maximum delay between bursts. + /// + [DataField("fireBurstDelayMax")] + public TimeSpan FireBurstDelayMax = TimeSpan.FromSeconds(10); + + /// + /// The base minimum delay between shot bursts. + /// Used for machine part rating calculations. + /// + [DataField("baseFireBurstDelayMin")] + public TimeSpan BaseFireBurstDelayMin = TimeSpan.FromSeconds(4); + + /// + /// The base maximum delay between shot bursts. + /// Used for machine part rating calculations. + /// + [DataField("baseFireBurstDelayMax")] + public TimeSpan BaseFireBurstDelayMax = TimeSpan.FromSeconds(10); + + /// + /// The multiplier for the base delay between shot bursts as well as + /// the fire interval + /// + [DataField("fireRateMultiplier"), ViewVariables(VVAccess.ReadWrite)] + public float FireRateMultiplier = 0.8f; + + /// + /// The machine part that affects burst delay. + /// + [DataField("machinePartFireRate", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string MachinePartFireRate = "Laser"; + + /// + /// The visual state that is set when the emitter is turned on + /// + [DataField("onState")] + public string? OnState = "beam"; + + /// + /// The visual state that is set when the emitter doesn't have enough power. + /// + [DataField("underpoweredState")] + public string? UnderpoweredState = "underpowered"; +} + +[NetSerializable, Serializable] +public enum EmitterVisuals : byte +{ + VisualState +} + +[Serializable, NetSerializable] +public enum EmitterVisualLayers : byte +{ + Lights +} + +[NetSerializable, Serializable] +public enum EmitterVisualState +{ + On, + Underpowered, + Off } diff --git a/Content.Shared/Singularity/EntitySystems/SharedEmitterSystem.cs b/Content.Shared/Singularity/EntitySystems/SharedEmitterSystem.cs new file mode 100644 index 0000000000..bba6629988 --- /dev/null +++ b/Content.Shared/Singularity/EntitySystems/SharedEmitterSystem.cs @@ -0,0 +1,6 @@ +namespace Content.Shared.Singularity.EntitySystems; + +public abstract class SharedEmitterSystem : EntitySystem +{ + +} diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/emitter.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/emitter.yml index 2838bc8eaa..0a7bffc42a 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/emitter.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/emitter.yml @@ -30,9 +30,11 @@ - state: beam shader: unshaded visible: false + map: ["enum.EmitterVisualLayers.Lights"] - state: lock shader: unshaded visible: false + map: ["enum.StorageVisualLayers.Lock"] - type: Emitter - type: Gun fireRate: 10 #just has to be fast enough to keep up with upgrades @@ -72,8 +74,6 @@ - type: Pullable - type: Rotatable - type: Appearance - visuals: - - type: EmitterVisualizer - type: Lock locked: false - type: AccessReader