diff --git a/Content.Server/Radiation/RadiationPulseSystem.cs b/Content.Server/Radiation/RadiationPulseSystem.cs index a6f5a78c69..efdcbc425c 100644 --- a/Content.Server/Radiation/RadiationPulseSystem.cs +++ b/Content.Server/Radiation/RadiationPulseSystem.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Content.Server.Radiation.Systems; using Content.Shared.Radiation; using Content.Shared.Sound; using JetBrains.Annotations; @@ -12,8 +13,7 @@ namespace Content.Server.Radiation [UsedImplicitly] public sealed class RadiationPulseSystem : EntitySystem { - [Dependency] private readonly IEntityManager _entMan = default!; - [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly RadiationSystem _radiation = default!; private const float RadiationCooldown = 1.0f; private float _accumulator; @@ -36,18 +36,8 @@ namespace Content.Server.Radiation if (Deleted(ent)) continue; - foreach (var entity in _lookup.GetEntitiesInRange(_entMan.GetComponent(ent).Coordinates, comp.Range)) - { - // For now at least still need this because it uses a list internally then returns and this may be deleted before we get to it. - if ((!_entMan.EntityExists(entity) ? EntityLifeStage.Deleted : _entMan.GetComponent(entity).EntityLifeStage) >= EntityLifeStage.Deleted) continue; - - // Note: Radiation is liable for a refactor (stinky Sloth coding a basic version when he did StationEvents) - // so this ToArray doesn't really matter. - foreach (var radiation in _entMan.GetComponents(entity).ToArray()) - { - radiation.RadiationAct(RadiationCooldown, comp); - } - } + 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 new file mode 100644 index 0000000000..20432f9f5c --- /dev/null +++ b/Content.Server/Radiation/Systems/RadiationSystem.cs @@ -0,0 +1,27 @@ +using Content.Shared.Radiation.Events; +using Robust.Shared.Map; + +namespace Content.Server.Radiation.Systems; + +public sealed class RadiationSystem : EntitySystem +{ + [Dependency] private readonly EntityLookupSystem _lookup = default!; + + public void IrradiateRange(MapCoordinates coordinates, float range, float radsPerSecond, float time) + { + var lookUp = _lookup.GetEntitiesInRange(coordinates, range); + foreach (var uid in lookUp) + { + if (Deleted(uid)) + continue; + + IrradiateEntity(uid, radsPerSecond, time); + } + } + + public void IrradiateEntity(EntityUid uid, float radsPerSecond, float time) + { + var msg = new OnIrradiatedEvent(time, radsPerSecond); + RaiseLocalEvent(uid, msg); + } +} diff --git a/Content.Server/Singularity/Components/RadiationCollectorComponent.cs b/Content.Server/Singularity/Components/RadiationCollectorComponent.cs index df18b5617e..67f7f6db27 100644 --- a/Content.Server/Singularity/Components/RadiationCollectorComponent.cs +++ b/Content.Server/Singularity/Components/RadiationCollectorComponent.cs @@ -1,52 +1,37 @@ -using Content.Server.Power.Components; -using Content.Shared.Radiation; -using Content.Shared.Singularity.Components; +using Content.Server.Singularity.EntitySystems; namespace Content.Server.Singularity.Components { + /// + /// Generates electricity from radiation. + /// [RegisterComponent] - public sealed class RadiationCollectorComponent : Component, IRadiationAct + [Friend(typeof(RadiationCollectorSystem))] + public sealed class RadiationCollectorComponent : Component { - [Dependency] private readonly IEntityManager _entMan = default!; - - public bool Enabled; - public TimeSpan CoolDownEnd; - - [ViewVariables(VVAccess.ReadWrite)] - public bool Collecting { - get => Enabled; - set - { - if (Enabled == value) return; - Enabled = value; - SetAppearance(Enabled ? RadiationCollectorVisualState.Activating : RadiationCollectorVisualState.Deactivating); - } - } - + /// + /// How much joules will collector generate for each rad. + /// + [DataField("chargeModifier")] [ViewVariables(VVAccess.ReadWrite)] public float ChargeModifier = 30000f; - void IRadiationAct.RadiationAct(float frameTime, SharedRadiationPulseComponent radiation) - { - if (!Enabled) return; + /// + /// Cooldown time between users interaction. + /// + [DataField("cooldown")] + [ViewVariables(VVAccess.ReadWrite)] + public TimeSpan Cooldown = TimeSpan.FromSeconds(0.81f); - // No idea if this is even vaguely accurate to the previous logic. - // The maths is copied from that logic even though it works differently. - // But the previous logic would also make the radiation collectors never ever stop providing energy. - // And since frameTime was used there, I'm assuming that this is what the intent was. - // This still won't stop things being potentially hilarously unbalanced though. - if (_entMan.TryGetComponent(Owner, out var batteryComponent)) - { - batteryComponent.CurrentCharge += frameTime * radiation.RadsPerSecond * ChargeModifier; - } - } + /// + /// Was machine activated by user? + /// + [ViewVariables(VVAccess.ReadOnly)] + public bool Enabled; - public void SetAppearance(RadiationCollectorVisualState state) - { - if (IoCManager.Resolve().TryGetComponent(Owner, out var appearance)) - { - appearance.SetData(RadiationCollectorVisuals.VisualState, state); - } - } + /// + /// Timestamp when machine can be deactivated again. + /// + public TimeSpan CoolDownEnd; } } diff --git a/Content.Server/Singularity/EntitySystems/RadiationCollectorSystem.cs b/Content.Server/Singularity/EntitySystems/RadiationCollectorSystem.cs index 18a0c4a390..b3b6a054be 100644 --- a/Content.Server/Singularity/EntitySystems/RadiationCollectorSystem.cs +++ b/Content.Server/Singularity/EntitySystems/RadiationCollectorSystem.cs @@ -2,6 +2,8 @@ using Content.Server.Singularity.Components; using Content.Shared.Interaction; using Content.Shared.Singularity.Components; using Content.Server.Popups; +using Content.Server.Power.Components; +using Content.Shared.Radiation.Events; using Robust.Shared.Timing; using Robust.Shared.Player; @@ -15,6 +17,7 @@ namespace Content.Server.Singularity.EntitySystems { base.Initialize(); SubscribeLocalEvent(OnInteractHand); + SubscribeLocalEvent(OnRadiation); } private void OnInteractHand(EntityUid uid, RadiationCollectorComponent component, InteractHandEvent args) @@ -24,18 +27,58 @@ namespace Content.Server.Singularity.EntitySystems if(curTime < component.CoolDownEnd) return; - if (!component.Enabled) + ToggleCollector(uid, args.User, component); + component.CoolDownEnd = curTime + component.Cooldown; + } + + private void OnRadiation(EntityUid uid, RadiationCollectorComponent component, OnIrradiatedEvent args) + { + if (!component.Enabled) return; + + // No idea if this is even vaguely accurate to the previous logic. + // The maths is copied from that logic even though it works differently. + // But the previous logic would also make the radiation collectors never ever stop providing energy. + // And since frameTime was used there, I'm assuming that this is what the intent was. + // This still won't stop things being potentially hilariously unbalanced though. + if (TryComp(uid, out var batteryComponent)) { - _popupSystem.PopupEntity(Loc.GetString("radiation-collector-component-use-on"), uid, Filter.Pvs(args.User)); - component.Collecting = true; + var charge = args.TotalRads * component.ChargeModifier; + batteryComponent.CurrentCharge += charge; } - else + } + + public void ToggleCollector(EntityUid uid, EntityUid? user = null, RadiationCollectorComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + SetCollectorEnabled(uid, !component.Enabled, user, component); + } + + public void SetCollectorEnabled(EntityUid uid, bool enabled, EntityUid? user = null, RadiationCollectorComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + component.Enabled = enabled; + + // Show message to the player + if (user != null) { - _popupSystem.PopupEntity(Loc.GetString("radiation-collector-component-use-off"), uid, Filter.Pvs(args.User)); - component.Collecting = false; + var msg = component.Enabled ? "radiation-collector-component-use-on" : "radiation-collector-component-use-off"; + _popupSystem.PopupEntity(Loc.GetString(msg), uid, Filter.Pvs(user.Value)); + } - component.CoolDownEnd = curTime + TimeSpan.FromSeconds(0.81f); + // Update appearance + UpdateAppearance(uid, component); + } + + private void UpdateAppearance(EntityUid uid, RadiationCollectorComponent? component, AppearanceComponent? appearance = null) + { + if (!Resolve(uid, ref component, ref appearance)) + return; + + var state = component.Enabled ? RadiationCollectorVisualState.Active : RadiationCollectorVisualState.Deactive; + appearance.SetData(RadiationCollectorVisuals.VisualState, state); } } } diff --git a/Content.Server/Singularity/StartSingularityEngineCommand.cs b/Content.Server/Singularity/StartSingularityEngineCommand.cs index 041301c192..4d11154392 100644 --- a/Content.Server/Singularity/StartSingularityEngineCommand.cs +++ b/Content.Server/Singularity/StartSingularityEngineCommand.cs @@ -32,7 +32,7 @@ namespace Content.Server.Singularity } foreach (var comp in entityManager.EntityQuery()) { - comp.Collecting = true; + EntitySystem.Get().SetCollectorEnabled(comp.Owner, true, null, comp); } foreach (var comp in entityManager.EntityQuery()) { diff --git a/Content.Shared/Damage/Components/DamageableComponent.cs b/Content.Shared/Damage/Components/DamageableComponent.cs index e130c9889f..a0ee05628c 100644 --- a/Content.Shared/Damage/Components/DamageableComponent.cs +++ b/Content.Shared/Damage/Components/DamageableComponent.cs @@ -1,17 +1,9 @@ -using System; -using System.Collections.Generic; -using Content.Shared.Acts; using Content.Shared.Damage.Prototypes; using Content.Shared.FixedPoint; -using Content.Shared.Radiation; -using Robust.Shared.Analyzers; -using Robust.Shared.GameObjects; using Robust.Shared.GameStates; using Robust.Shared.Serialization; -using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; -using Robust.Shared.ViewVariables; namespace Content.Shared.Damage { @@ -25,7 +17,7 @@ namespace Content.Shared.Damage [RegisterComponent] [NetworkedComponent()] [Friend(typeof(DamageableSystem))] - public sealed class DamageableComponent : Component, IRadiationAct + public sealed class DamageableComponent : Component { /// /// This specifies what damage types are supported by this component. @@ -77,21 +69,6 @@ namespace Content.Shared.Damage [ViewVariables] [DataField("explosionDamageTypes", customTypeSerializer: typeof(PrototypeIdListSerializer))] public List ExplosionDamageTypeIDs = new() { "Piercing", "Heat" }; - - // TODO RADIATION Remove this. - void IRadiationAct.RadiationAct(float frameTime, SharedRadiationPulseComponent radiation) - { - var damageValue = FixedPoint2.New(MathF.Max((frameTime * radiation.RadsPerSecond), 1)); - - // Radiation should really just be a damage group instead of a list of types. - DamageSpecifier damage = new(); - foreach (var typeID in RadiationDamageTypeIDs) - { - damage.DamageDict.Add(typeID, damageValue); - } - - EntitySystem.Get().TryChangeDamage(Owner, damage); - } } [Serializable, NetSerializable] diff --git a/Content.Shared/Damage/Systems/DamageableSystem.cs b/Content.Shared/Damage/Systems/DamageableSystem.cs index 958b45cd97..f3354241e6 100644 --- a/Content.Shared/Damage/Systems/DamageableSystem.cs +++ b/Content.Shared/Damage/Systems/DamageableSystem.cs @@ -2,6 +2,7 @@ using System.Linq; using Content.Shared.Damage.Prototypes; using Content.Shared.FixedPoint; using Content.Shared.Inventory; +using Content.Shared.Radiation.Events; using Robust.Shared.GameObjects; using Robust.Shared.GameStates; using Robust.Shared.IoC; @@ -19,6 +20,7 @@ namespace Content.Shared.Damage SubscribeLocalEvent(DamageableInit); SubscribeLocalEvent(DamageableHandleState); SubscribeLocalEvent(DamageableGetState); + SubscribeLocalEvent(OnIrradiated); } /// @@ -195,12 +197,26 @@ namespace Content.Shared.Damage Dirty(comp); } - + private void DamageableGetState(EntityUid uid, DamageableComponent component, ref ComponentGetState args) { args.State = new DamageableComponentState(component.Damage.DamageDict, component.DamageModifierSetId); } + private void OnIrradiated(EntityUid uid, DamageableComponent component, OnIrradiatedEvent args) + { + var damageValue = FixedPoint2.New(args.TotalRads); + + // Radiation should really just be a damage group instead of a list of types. + DamageSpecifier damage = new(); + foreach (var typeId in component.RadiationDamageTypeIDs) + { + damage.DamageDict.Add(typeId, damageValue); + } + + TryChangeDamage(uid, damage); + } + private void DamageableHandleState(EntityUid uid, DamageableComponent component, ref ComponentHandleState args) { if (args.Current is not DamageableComponentState state) diff --git a/Content.Shared/Radiation/Events/OnIrradiatedEvent.cs b/Content.Shared/Radiation/Events/OnIrradiatedEvent.cs new file mode 100644 index 0000000000..ee35304e2a --- /dev/null +++ b/Content.Shared/Radiation/Events/OnIrradiatedEvent.cs @@ -0,0 +1,20 @@ +namespace Content.Shared.Radiation.Events; + +/// +/// Raised on entity when it was irradiated +/// by some radiation source. +/// +public sealed class OnIrradiatedEvent : EntityEventArgs +{ + public readonly float FrameTime; + + public readonly float RadsPerSecond; + + public float TotalRads => RadsPerSecond * FrameTime; + + public OnIrradiatedEvent(float frameTime, float radsPerSecond) + { + FrameTime = frameTime; + RadsPerSecond = radsPerSecond; + } +} diff --git a/Content.Shared/Radiation/IRadiationAct.cs b/Content.Shared/Radiation/IRadiationAct.cs deleted file mode 100644 index d681f2a015..0000000000 --- a/Content.Shared/Radiation/IRadiationAct.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Robust.Shared.Analyzers; -using Robust.Shared.GameObjects; - -namespace Content.Shared.Radiation -{ - [RequiresExplicitImplementation] - public interface IRadiationAct : IComponent - { - void RadiationAct(float frameTime, SharedRadiationPulseComponent radiation); - } -}