diff --git a/Content.Client/Radiation/RadiationPulseComponent.cs b/Content.Client/Radiation/RadiationPulseComponent.cs deleted file mode 100644 index b260c41147..0000000000 --- a/Content.Client/Radiation/RadiationPulseComponent.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Content.Shared.Radiation; - -namespace Content.Client.Radiation -{ - [RegisterComponent] - [ComponentReference(typeof(SharedRadiationPulseComponent))] - public sealed class RadiationPulseComponent : SharedRadiationPulseComponent - { - private bool _draw; - private bool _decay; - private float _range; - private TimeSpan _startTime; - private TimeSpan _endTime; - - public override float Range => _range; - public override TimeSpan StartTime => _startTime; - public override TimeSpan EndTime => _endTime; - public override bool Draw => _draw; - public override bool Decay => _decay; - - public override void HandleComponentState(ComponentState? curState, ComponentState? nextState) - { - base.HandleComponentState(curState, nextState); - - if (curState is not RadiationPulseState state) - { - return; - } - - _range = state.Range; - _draw = state.Draw; - _decay = state.Decay; - _startTime = state.StartTime; - _endTime = state.EndTime; - } - } -} diff --git a/Content.Client/Radiation/RadiationPulseOverlay.cs b/Content.Client/Radiation/RadiationPulseOverlay.cs index 475816ce7a..258696ec5d 100644 --- a/Content.Client/Radiation/RadiationPulseOverlay.cs +++ b/Content.Client/Radiation/RadiationPulseOverlay.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.Shared.Radiation.Components; using Robust.Client.Graphics; using Robust.Shared.Enums; using Robust.Shared.Map; @@ -18,8 +19,6 @@ namespace Content.Client.Radiation public override OverlaySpace Space => OverlaySpace.WorldSpace; public override bool RequestScreenTexture => true; - private TimeSpan _lastTick = default; - private readonly ShaderInstance _baseShader; private readonly Dictionary _pulses = new(); @@ -52,7 +51,7 @@ namespace Content.Client.Radiation shd?.SetParameter("renderScale", viewport.RenderScale); shd?.SetParameter("positionInput", tempCoords); shd?.SetParameter("range", instance.Range); - var life = (_lastTick - instance.Start) / (instance.End - instance.Start); + var life = (_gameTiming.RealTime - instance.Start).TotalSeconds / instance.Duration; shd?.SetParameter("life", (float) life); // There's probably a very good reason not to do this. @@ -75,8 +74,6 @@ namespace Content.Client.Radiation return; } - _lastTick = _gameTiming.CurTime; - var currentEyeLoc = currentEye.Position; var pulses = _entityManager.EntityQuery(); @@ -92,9 +89,9 @@ namespace Content.Client.Radiation _baseShader.Duplicate(), new RadiationShaderInstance( _entityManager.GetComponent(pulseEntity).MapPosition.Position, - pulse.Range, + pulse.VisualRange, pulse.StartTime, - pulse.EndTime + pulse.VisualDuration ) ) ); @@ -110,7 +107,7 @@ namespace Content.Client.Radiation { var shaderInstance = _pulses[pulseEntity]; shaderInstance.instance.CurrentMapCoords = _entityManager.GetComponent(pulseEntity).MapPosition.Position; - shaderInstance.instance.Range = pulse.Range; + shaderInstance.instance.Range = pulse.VisualRange; } else { _pulses[pulseEntity].shd.Dispose(); _pulses.Remove(pulseEntity); @@ -124,12 +121,12 @@ namespace Content.Client.Radiation return _entityManager.GetComponent(pulseEntity).MapID == currentEyeLoc.MapId && _entityManager.GetComponent(pulseEntity).Coordinates.InRange(_entityManager, EntityCoordinates.FromMap(_entityManager, _entityManager.GetComponent(pulseEntity).ParentUid, currentEyeLoc), MaxDist); } - private sealed record RadiationShaderInstance(Vector2 CurrentMapCoords, float Range, TimeSpan Start, TimeSpan End) + private sealed record RadiationShaderInstance(Vector2 CurrentMapCoords, float Range, TimeSpan Start, float Duration) { public Vector2 CurrentMapCoords = CurrentMapCoords; public float Range = Range; public TimeSpan Start = Start; - public TimeSpan End = End; + public float Duration = Duration; }; } } diff --git a/Content.Server/Radiation/RadiationPulseComponent.cs b/Content.Server/Radiation/RadiationPulseComponent.cs deleted file mode 100644 index 34843b0ba7..0000000000 --- a/Content.Server/Radiation/RadiationPulseComponent.cs +++ /dev/null @@ -1,103 +0,0 @@ -using Content.Shared.Radiation; -using Robust.Shared.Audio; -using Robust.Shared.Player; -using Robust.Shared.Random; -using Robust.Shared.Timing; - -namespace Content.Server.Radiation -{ - [RegisterComponent] - [ComponentReference(typeof(SharedRadiationPulseComponent))] - public sealed class RadiationPulseComponent : SharedRadiationPulseComponent - { - [Dependency] private readonly IEntityManager _entMan = default!; - [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly IRobustRandom _random = default!; - - private float _duration; - private float _range = 5f; - private TimeSpan _startTime; - private TimeSpan _endTime; - private bool _draw = true; - private bool _decay = true; - - /// - /// Whether the entity will delete itself after a certain duration defined by - /// and - /// - [DataField("decay")] - public override bool Decay - { - get => _decay; - set - { - _decay = value; - Dirty(); - } - } - - [DataField("minPulseLifespan")] - public float MinPulseLifespan { get; set; } = 0.8f; - - [DataField("maxPulseLifespan")] - public float MaxPulseLifespan { get; set; } = 2.5f; - - [DataField("sound")] public SoundSpecifier Sound { get; set; } = new SoundCollectionSpecifier("RadiationPulse"); - - [DataField("range")] - public override float Range - { - get => _range; - set - { - _range = value; - Dirty(); - } - } - - [DataField("draw")] - public override bool Draw - { - get => _draw; - set - { - _draw = value; - Dirty(); - } - } - - public override TimeSpan StartTime => _startTime; - public override TimeSpan EndTime => _endTime; - - public void DoPulse() - { - if (Decay) - { - var currentTime = _gameTiming.CurTime; - _startTime = currentTime; - _duration = _random.NextFloat() * (MaxPulseLifespan - MinPulseLifespan) + MinPulseLifespan; - _endTime = currentTime + TimeSpan.FromSeconds(_duration); - } - - SoundSystem.Play(Sound.GetSound(), Filter.Pvs(Owner), Owner); - - Dirty(); - } - - public override ComponentState GetComponentState() - { - return new RadiationPulseState(_range, Draw, Decay, _startTime, _endTime); - } - - public void Update(float frameTime) - { - if (!Decay || _entMan.Deleted(Owner)) - return; - - if (_duration <= 0f) - _entMan.QueueDeleteEntity(Owner); - - _duration -= frameTime; - } - } -} diff --git a/Content.Server/Radiation/RadiationPulseSystem.cs b/Content.Server/Radiation/RadiationPulseSystem.cs deleted file mode 100644 index 5c62879b7c..0000000000 --- a/Content.Server/Radiation/RadiationPulseSystem.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Content.Server.Radiation.Systems; -using JetBrains.Annotations; - -namespace Content.Server.Radiation -{ - [UsedImplicitly] - public sealed class RadiationPulseSystem : EntitySystem - { - [Dependency] private readonly RadiationSystem _radiation = default!; - - private const float RadiationCooldown = 1.0f; - private float _accumulator; - - public override void Update(float frameTime) - { - base.Update(frameTime); - - _accumulator += frameTime; - - while (_accumulator > RadiationCooldown) - { - _accumulator -= RadiationCooldown; - - // All code here runs effectively every RadiationCooldown seconds, so use that as the "frame time". - foreach (var comp in EntityManager.EntityQuery()) - { - comp.Update(RadiationCooldown); - var ent = comp.Owner; - - if (Deleted(ent)) continue; - - var cords = Transform(ent).MapPosition; - _radiation.IrradiateRange(cords, comp.Range, comp.RadsPerSecond, RadiationCooldown); - } - } - } - } -} diff --git a/Content.Server/Radiation/Systems/RadiationSystem.cs b/Content.Server/Radiation/Systems/RadiationSystem.cs index db0cb6e1b1..2c03ca3e4b 100644 --- a/Content.Server/Radiation/Systems/RadiationSystem.cs +++ b/Content.Server/Radiation/Systems/RadiationSystem.cs @@ -7,6 +7,31 @@ public sealed class RadiationSystem : EntitySystem { [Dependency] private readonly EntityLookupSystem _lookup = default!; + private const float RadiationCooldown = 1.0f; + private float _accumulator; + + public override void Update(float frameTime) + { + base.Update(frameTime); + _accumulator += frameTime; + + while (_accumulator > RadiationCooldown) + { + _accumulator -= RadiationCooldown; + + // All code here runs effectively every RadiationCooldown seconds, so use that as the "frame time". + foreach (var comp in EntityManager.EntityQuery()) + { + var ent = comp.Owner; + if (Deleted(ent)) + continue; + + var cords = Transform(ent).MapPosition; + IrradiateRange(cords, comp.Range, comp.RadsPerSecond, RadiationCooldown); + } + } + } + public void IrradiateRange(MapCoordinates coordinates, float range, float radsPerSecond, float time) { var lookUp = _lookup.GetEntitiesInRange(coordinates, range); diff --git a/Content.Server/Sound/Components/BaseEmitSoundComponent.cs b/Content.Server/Sound/Components/BaseEmitSoundComponent.cs index b8bcac286d..1fe5551515 100644 --- a/Content.Server/Sound/Components/BaseEmitSoundComponent.cs +++ b/Content.Server/Sound/Components/BaseEmitSoundComponent.cs @@ -12,6 +12,6 @@ namespace Content.Server.Sound.Components [ViewVariables(VVAccess.ReadWrite)] [DataField("sound", required: true)] - public SoundSpecifier Sound { get; set; } = default!; + public SoundSpecifier? Sound; } } diff --git a/Content.Server/Sound/Components/EmitSoundOnSpawnComponent.cs b/Content.Server/Sound/Components/EmitSoundOnSpawnComponent.cs new file mode 100644 index 0000000000..6535b264e6 --- /dev/null +++ b/Content.Server/Sound/Components/EmitSoundOnSpawnComponent.cs @@ -0,0 +1,9 @@ +namespace Content.Server.Sound.Components; + +/// +/// Simple sound emitter that emits sound on entity spawn. +/// +[RegisterComponent] +public sealed class EmitSoundOnSpawnComponent : BaseEmitSoundComponent +{ +} diff --git a/Content.Server/Sound/EmitSoundSystem.cs b/Content.Server/Sound/EmitSoundSystem.cs index 5b19f60ae3..58dd21186b 100644 --- a/Content.Server/Sound/EmitSoundSystem.cs +++ b/Content.Server/Sound/EmitSoundSystem.cs @@ -54,6 +54,7 @@ namespace Content.Server.Sound public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(HandleEmitSpawnOnInit); SubscribeLocalEvent(HandleEmitSoundOnLand); SubscribeLocalEvent(HandleEmitSoundOnUseInHand); SubscribeLocalEvent(HandleEmitSoundOnThrown); @@ -64,6 +65,11 @@ namespace Content.Server.Sound SubscribeLocalEvent(HandleEmitSoundOnDrop); } + private void HandleEmitSpawnOnInit(EntityUid uid, EmitSoundOnSpawnComponent component, ComponentInit args) + { + TryEmitSound(component); + } + private void HandleEmitSoundOnTrigger(EntityUid uid, EmitSoundOnTriggerComponent component, TriggerEvent args) { TryEmitSound(component); @@ -122,6 +128,8 @@ namespace Content.Server.Sound private void TryEmitSound(BaseEmitSoundComponent component) { + if (component.Sound == null) + return; _audioSystem.PlayPvs(component.Sound, component.Owner, component.Sound.Params.AddVolume(-2f)); } } diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Components/RadiateArtifactComponent.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Components/RadiateArtifactComponent.cs index d5f3d74c47..1e57e46b23 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Components/RadiateArtifactComponent.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Components/RadiateArtifactComponent.cs @@ -1,4 +1,3 @@ -using Content.Server.Radiation; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; @@ -12,7 +11,6 @@ public sealed class RadiateArtifactComponent : Component { /// /// Radiation pulse prototype to spawn. - /// Should has . /// [DataField("pulsePrototype", customTypeSerializer:typeof(PrototypeIdSerializer))] public string PulsePrototype = "RadiationPulse"; diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/RadiateArtifactSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/RadiateArtifactSystem.cs index d235c50e2f..de67aa970d 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/RadiateArtifactSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/RadiateArtifactSystem.cs @@ -15,11 +15,6 @@ public sealed class RadiateArtifactSystem : EntitySystem private void OnActivate(EntityUid uid, RadiateArtifactComponent component, ArtifactActivatedEvent args) { var transform = Transform(uid); - - var pulseUid = EntityManager.SpawnEntity(component.PulsePrototype, transform.Coordinates); - if (!TryComp(pulseUid, out RadiationPulseComponent? pulse)) - return; - - pulse.DoPulse(); + EntityManager.SpawnEntity(component.PulsePrototype, transform.Coordinates); } } diff --git a/Content.Shared/Radiation/Components/RadiationPulseComponent.cs b/Content.Shared/Radiation/Components/RadiationPulseComponent.cs new file mode 100644 index 0000000000..08f2685993 --- /dev/null +++ b/Content.Shared/Radiation/Components/RadiationPulseComponent.cs @@ -0,0 +1,30 @@ +using Content.Shared.Radiation.Systems; +using Content.Shared.Spawners.Components; + +namespace Content.Shared.Radiation.Components; + +/// +/// Create circle pulse animation of radiation around object. +/// Drawn on client after creation only once per component lifetime. +/// +[RegisterComponent] +[Access(typeof(RadiationPulseSystem))] +public sealed class RadiationPulseComponent : Component +{ + /// + /// Timestamp when component was assigned to this entity. + /// + public TimeSpan StartTime; + + /// + /// How long will animation play in seconds. + /// Can be overridden by . + /// + public float VisualDuration = 2f; + + /// + /// The range of animation. + /// Can be overridden by . + /// + public float VisualRange = 5f; +} diff --git a/Content.Shared/Radiation/Components/RadiationSourceComponent.cs b/Content.Shared/Radiation/Components/RadiationSourceComponent.cs new file mode 100644 index 0000000000..a793ee4a95 --- /dev/null +++ b/Content.Shared/Radiation/Components/RadiationSourceComponent.cs @@ -0,0 +1,20 @@ +/// +/// Irradiate all objects in range. +/// +[RegisterComponent] +public sealed class RadiationSourceComponent : Component +{ + /// + /// How many rads per second receive irradiated object. + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("radsPerSecond")] + public float RadsPerSecond = 1; + + /// + /// Radius of radiation source. + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("range")] + public float Range = 5f; +} diff --git a/Content.Shared/Radiation/SharedRadiationStorm.cs b/Content.Shared/Radiation/SharedRadiationStorm.cs deleted file mode 100644 index bde87a1bfc..0000000000 --- a/Content.Shared/Radiation/SharedRadiationStorm.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Robust.Shared.GameStates; -using Robust.Shared.Serialization; - -namespace Content.Shared.Radiation -{ - [NetworkedComponent()] - public abstract class SharedRadiationPulseComponent : Component - { - [DataField("radsPerSecond")] - public float RadsPerSecond { get; set; } = 1; - - /// - /// Radius of the pulse from its position - /// - public virtual float Range { get; set; } - - public virtual bool Decay { get; set; } - public virtual bool Draw { get; set; } - - public virtual TimeSpan StartTime { get; } - public virtual TimeSpan EndTime { get; } - } - - /// - /// For syncing the pulse's lifespan between client and server for the overlay - /// - [Serializable, NetSerializable] - public sealed class RadiationPulseState : ComponentState - { - // not networking RadsPerSecond because damage is only ever dealt by server-side systems. - - public readonly float Range; - public readonly bool Draw; - public readonly bool Decay; - public readonly TimeSpan StartTime; - public readonly TimeSpan EndTime; - - public RadiationPulseState(float range, bool draw, bool decay, TimeSpan startTime, TimeSpan endTime) - { - Range = range; - Draw = draw; - Decay = decay; - StartTime = startTime; - EndTime = endTime; - } - } -} diff --git a/Content.Shared/Radiation/Systems/RadiationPulseSystem.cs b/Content.Shared/Radiation/Systems/RadiationPulseSystem.cs new file mode 100644 index 0000000000..a2da34a63f --- /dev/null +++ b/Content.Shared/Radiation/Systems/RadiationPulseSystem.cs @@ -0,0 +1,32 @@ +using Content.Shared.Radiation.Components; +using Content.Shared.Spawners.Components; +using Robust.Shared.Timing; + +namespace Content.Shared.Radiation.Systems; + +public sealed class RadiationPulseSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnStartup); + } + + private void OnStartup(EntityUid uid, RadiationPulseComponent component, ComponentStartup args) + { + component.StartTime = _timing.RealTime; + + // try to get despawn time or keep default duration time + if (TryComp(uid, out var despawn)) + { + component.VisualDuration = despawn.Lifetime; + } + // try to get radiation range or keep default visual range + if (TryComp(uid, out var radSource)) + { + component.VisualRange = radSource.Range; + } + } +} diff --git a/Content.Shared/Singularity/SharedSingularitySystem.cs b/Content.Shared/Singularity/SharedSingularitySystem.cs index 1d9c2a53e7..39982d9b29 100644 --- a/Content.Shared/Singularity/SharedSingularitySystem.cs +++ b/Content.Shared/Singularity/SharedSingularitySystem.cs @@ -105,9 +105,9 @@ namespace Content.Shared.Singularity singularity.Level = value; - if (EntityManager.TryGetComponent(singularity.Owner, out SharedRadiationPulseComponent? pulse)) + if (EntityManager.TryGetComponent(singularity.Owner, out RadiationSourceComponent? source)) { - pulse.RadsPerSecond = singularity.RadsPerLevel * value; + source.RadsPerSecond = singularity.RadsPerLevel * value; } if (EntityManager.TryGetComponent(singularity.Owner, out AppearanceComponent? appearance)) diff --git a/Resources/Prototypes/Entities/Effects/radiation.yml b/Resources/Prototypes/Entities/Effects/radiation.yml index 7e0fbd15b4..5caa763716 100644 --- a/Resources/Prototypes/Entities/Effects/radiation.yml +++ b/Resources/Prototypes/Entities/Effects/radiation.yml @@ -4,7 +4,12 @@ noSpawn: true description: Looking at this anomaly makes you feel strange, like something is pushing at your eyes. components: - - type: RadiationPulse - minPulseLifespan: 0.8 - maxPulseLifespan: 2.5 + - type: RadiationSource radsPerSecond: 5 + - type: TimedDespawn + lifetime: 2 + - type: EmitSoundOnSpawn + sound: + collection: + RadiationPulse + - type: RadiationPulse diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml index 4abe11bf06..fddeecaffb 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml @@ -24,13 +24,10 @@ layer: - AllMask - type: Singularity - radsPerLevel: 1 # determines RadiationPulse's radiation per second. + radsPerLevel: 1 - type: SingularityDistortion - - type: RadiationPulse + - type: RadiationSource range: 10 - decay: false - minPulseLifespan: 1 - maxPulseLifespan: 1 - type: Sprite sprite: Structures/Power/Generation/Singularity/singularity_1.rsi state: singularity_1 diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/generators.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/generators.yml index 4c7c175e96..02478b07c4 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/generators.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/generators.yml @@ -243,8 +243,5 @@ layers: - state: rtg_damaged - state: rtg_glow - - type: RadiationPulse # ideally only when opened. + - type: RadiationSource # ideally only when opened. range: 2 - decay: false - minPulseLifespan: 1 - maxPulseLifespan: 1