diff --git a/Content.Server/Singularity/Components/EmitterComponent.cs b/Content.Server/Singularity/Components/EmitterComponent.cs index eab75d81fe..a99cf7c32e 100644 --- a/Content.Server/Singularity/Components/EmitterComponent.cs +++ b/Content.Server/Singularity/Components/EmitterComponent.cs @@ -1,5 +1,7 @@ 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 { @@ -18,14 +20,102 @@ namespace Content.Server.Singularity.Components public const float Volume = 0.5f; public const float Distance = 6f; - [ViewVariables] public int FireShotCounter; + /// + /// counts the number of consecutive shots fired. + /// + [ViewVariables] + public int FireShotCounter; - [ViewVariables] [DataField("fireSound")] public SoundSpecifier FireSound = new SoundPathSpecifier("/Audio/Weapons/emitter.ogg"); - [ViewVariables] [DataField("boltType")] public string BoltType = "EmitterBolt"; - [ViewVariables] [DataField("powerUseActive")] public int PowerUseActive = 500; - [ViewVariables] [DataField("fireBurstSize")] public int FireBurstSize = 3; - [ViewVariables] [DataField("fireInterval")] public TimeSpan FireInterval = TimeSpan.FromSeconds(2); - [ViewVariables] [DataField("fireBurstDelayMin")] public TimeSpan FireBurstDelayMin = TimeSpan.FromSeconds(2); - [ViewVariables] [DataField("fireBurstDelayMax")] public TimeSpan FireBurstDelayMax = TimeSpan.FromSeconds(10); + [DataField("fireSound"), ViewVariables] + public SoundSpecifier FireSound = new SoundPathSpecifier("/Audio/Weapons/emitter.ogg"); + + /// + /// The entity that is spawned when the emitter fires. + /// + [DataField("boltType"), ViewVariables] + public string BoltType = "EmitterBolt"; + + /// + /// The current amount of power being used. + /// + [DataField("powerUseActive"), ViewVariables] + 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"), ViewVariables] + public int FireBurstSize = 3; + + /// + /// The time between each shot during a burst. + /// + [DataField("fireInterval"), ViewVariables] + 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"), ViewVariables] + public TimeSpan FireBurstDelayMin = TimeSpan.FromSeconds(4); + + /// + /// The current maximum delay between bursts. + /// + [DataField("fireBurstDelayMax"), ViewVariables] + public TimeSpan FireBurstDelayMax = TimeSpan.FromSeconds(10); + + /// + /// The base minimum delay between shot bursts. + /// Used for machine part rating calculations. + /// + [DataField("baseFireBurstDelayMin"), ViewVariables] + public TimeSpan BaseFireBurstDelayMin = TimeSpan.FromSeconds(4); + + /// + /// The base maximum delay between shot bursts. + /// Used for machine part rating calculations. + /// + [DataField("baseFireBurstDelayMax"), ViewVariables] + 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 91f68b8346..93b6b78b6b 100644 --- a/Content.Server/Singularity/EntitySystems/EmitterSystem.cs +++ b/Content.Server/Singularity/EntitySystems/EmitterSystem.cs @@ -1,12 +1,12 @@ using System.Threading; using Content.Server.Administration.Logs; +using Content.Server.Construction; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Server.Projectiles; using Content.Server.Projectiles.Components; using Content.Server.Singularity.Components; using Content.Server.Storage.Components; -using Content.Shared.Audio; using Content.Shared.Database; using Content.Shared.Interaction; using Content.Shared.Popups; @@ -27,6 +27,10 @@ namespace Content.Server.Singularity.EntitySystems { [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly ProjectileSystem _projectile = default!; public override void Initialize() { @@ -34,6 +38,7 @@ namespace Content.Server.Singularity.EntitySystems SubscribeLocalEvent(ReceivedChanged); SubscribeLocalEvent(OnInteractHand); + SubscribeLocalEvent(OnRefreshParts); } private void OnInteractHand(EntityUid uid, EmitterComponent component, InteractHandEvent args) @@ -41,7 +46,8 @@ namespace Content.Server.Singularity.EntitySystems args.Handled = true; if (EntityManager.TryGetComponent(uid, out LockComponent? lockComp) && lockComp.Locked) { - component.Owner.PopupMessage(args.User, Loc.GetString("comp-emitter-access-locked", ("target", component.Owner))); + _popup.PopupEntity(Loc.GetString("comp-emitter-access-locked", + ("target", component.Owner)), uid, Filter.Entities(args.User)); return; } @@ -50,12 +56,14 @@ namespace Content.Server.Singularity.EntitySystems if (!component.IsOn) { SwitchOn(component); - component.Owner.PopupMessage(args.User, Loc.GetString("comp-emitter-turned-on", ("target", component.Owner))); + _popup.PopupEntity(Loc.GetString("comp-emitter-turned-on", + ("target", component.Owner)), uid, Filter.Entities(args.User)); } else { SwitchOff(component); - component.Owner.PopupMessage(args.User, Loc.GetString("comp-emitter-turned-off", ("target", component.Owner))); + _popup.PopupEntity(Loc.GetString("comp-emitter-turned-off", + ("target", component.Owner)), uid, Filter.Entities(args.User)); } _adminLogger.Add(LogType.Emitter, @@ -64,7 +72,8 @@ namespace Content.Server.Singularity.EntitySystems } else { - component.Owner.PopupMessage(args.User, Loc.GetString("comp-emitter-not-anchored", ("target", component.Owner))); + _popup.PopupEntity(Loc.GetString("comp-emitter-not-anchored", + ("target", component.Owner)), uid, Filter.Entities(args.User)); } } @@ -88,10 +97,23 @@ namespace Content.Server.Singularity.EntitySystems } } + private void OnRefreshParts(EntityUid uid, EmitterComponent component, RefreshPartsEvent args) + { + var powerUseRating = args.PartRatings[component.MachinePartPowerUse]; + var fireRateRating = args.PartRatings[component.MachinePartFireRate]; + + component.PowerUseActive = (int) (component.BasePowerUseActive * MathF.Pow(component.PowerUseMultiplier, powerUseRating - 1)); + + component.FireInterval = component.BaseFireInterval * MathF.Pow(component.FireRateMultiplier, fireRateRating - 1); + component.FireBurstDelayMin = component.BaseFireBurstDelayMin * MathF.Pow(component.FireRateMultiplier, fireRateRating - 1); + component.FireBurstDelayMax = component.BaseFireBurstDelayMax * MathF.Pow(component.FireRateMultiplier, fireRateRating - 1); + } + public void SwitchOff(EmitterComponent component) { component.IsOn = false; - if (TryComp(component.Owner, out var powerConsumer)) powerConsumer.DrawRate = 0; + if (TryComp(component.Owner, out var powerConsumer)) + powerConsumer.DrawRate = 0; PowerOff(component); UpdateAppearance(component); } @@ -99,7 +121,8 @@ namespace Content.Server.Singularity.EntitySystems public void SwitchOn(EmitterComponent component) { component.IsOn = true; - if (TryComp(component.Owner, out var powerConsumer)) powerConsumer.DrawRate = component.PowerUseActive; + if (TryComp(component.Owner, out var powerConsumer)) + powerConsumer.DrawRate = component.PowerUseActive; // Do not directly PowerOn(). // OnReceivedPowerChanged will get fired due to DrawRate change which will turn it on. UpdateAppearance(component); @@ -140,7 +163,8 @@ namespace Content.Server.Singularity.EntitySystems private void ShotTimerCallback(EmitterComponent component) { - if (component.Deleted) return; + if (component.Deleted) + return; // Any power-off condition should result in the timer for this method being cancelled // and thus not firing @@ -189,7 +213,7 @@ namespace Content.Server.Singularity.EntitySystems return; } - Get().SetShooter(projectileComponent, component.Owner); + _projectile.SetShooter(projectileComponent, component.Owner); physicsComponent .LinearVelocity = EntityManager.GetComponent(component.Owner).WorldRotation.ToWorldVec() * 20f; @@ -198,17 +222,12 @@ namespace Content.Server.Singularity.EntitySystems // TODO: Move to projectile's code. Timer.Spawn(3000, () => EntityManager.DeleteEntity(projectile)); - SoundSystem.Play(component.FireSound.GetSound(), Filter.Pvs(component.Owner), - component.Owner, AudioHelpers.WithVariation(EmitterComponent.Variation).WithVolume(EmitterComponent.Volume).WithMaxDistance(EmitterComponent.Distance)); + _audio.PlayPvs(component.FireSound, component.Owner, + AudioParams.Default.WithVariation(EmitterComponent.Variation).WithVolume(EmitterComponent.Volume).WithMaxDistance(EmitterComponent.Distance)); } private void UpdateAppearance(EmitterComponent component) { - if (!TryComp(component.Owner, out var appearanceComponent)) - { - return; - } - EmitterVisualState state; if (component.IsPowered) { @@ -222,8 +241,7 @@ namespace Content.Server.Singularity.EntitySystems { state = EmitterVisualState.Off; } - - appearanceComponent.SetData(EmitterVisuals.VisualState, state); + _appearance.SetData(component.Owner, EmitterVisuals.VisualState, state); } } }