diff --git a/Content.Server/Electrocution/ElectrocutionSystem.cs b/Content.Server/Electrocution/ElectrocutionSystem.cs index f887db9d38..f6c32b99f6 100644 --- a/Content.Server/Electrocution/ElectrocutionSystem.cs +++ b/Content.Server/Electrocution/ElectrocutionSystem.cs @@ -51,7 +51,7 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem private const string DamageType = "Shock"; // Yes, this is absurdly small for a reason. - public const float ElectrifiedDamagePerWatt = 0.0015f; // Parkstation-IPC + public const float ElectrifiedDamagePerWatt = 0.0015f; // Parkstation-IPC // This information is allowed to be public, and was needed in BatteryElectrocuteChargeSystem.cs private const float RecursiveDamageMultiplier = 0.75f; private const float RecursiveTimeMultiplier = 0.8f; diff --git a/Content.Server/Nyanotrasen/Borgs/CyborgComponent.cs b/Content.Server/Nyanotrasen/Borgs/CyborgComponent.cs index 4311e27b89..d2f74ff00c 100644 --- a/Content.Server/Nyanotrasen/Borgs/CyborgComponent.cs +++ b/Content.Server/Nyanotrasen/Borgs/CyborgComponent.cs @@ -4,4 +4,4 @@ namespace Content.Server.Borgs [RegisterComponent] public sealed class CyborgComponent : Component {} -} \ No newline at end of file +} diff --git a/Content.Server/Nyanotrasen/Borgs/CyborgSystem.cs b/Content.Server/Nyanotrasen/Borgs/CyborgSystem.cs index c82fdf6215..fbac68189a 100644 --- a/Content.Server/Nyanotrasen/Borgs/CyborgSystem.cs +++ b/Content.Server/Nyanotrasen/Borgs/CyborgSystem.cs @@ -16,7 +16,7 @@ public override void Initialize() base.Initialize(); SubscribeLocalEvent(OnChangeState); } - + private void OnChangeState(EntityUid uid, CyborgComponent component, MobStateChangedEvent args) { if (args.NewMobState == MobState.Dead){ @@ -28,7 +28,7 @@ private void OnChangeState(EntityUid uid, CyborgComponent component, MobStateCha // Stop dead borg from being movable AA cards by removing their ability to bump doors. _tagSystem.RemoveTag(uid, "DoorBumpOpener"); - + } else if(args.NewMobState == MobState.Alive && args.OldMobState == MobState.Dead) { @@ -39,4 +39,4 @@ private void OnChangeState(EntityUid uid, CyborgComponent component, MobStateCha return; } } -} \ No newline at end of file +} diff --git a/Content.Server/SimpleStation14/Power/Components/BatteryDrinkerComponent.cs b/Content.Server/SimpleStation14/Power/Components/BatteryDrinkerComponent.cs index ea254dfbdc..8587f6a89c 100644 --- a/Content.Server/SimpleStation14/Power/Components/BatteryDrinkerComponent.cs +++ b/Content.Server/SimpleStation14/Power/Components/BatteryDrinkerComponent.cs @@ -1,9 +1,7 @@ -using Robust.Shared.Audio; - namespace Content.Server.SimpleStation14.Power; [RegisterComponent] -public class BatteryDrinkerComponent : Component +public sealed class BatteryDrinkerComponent : Component { /// /// Is this drinker allowed to drink batteries not tagged as ? @@ -26,16 +24,8 @@ public class BatteryDrinkerComponent : Component public float DrinkMultiplier = 5f; /// - /// The sound to override the standard drink sound with. - /// Uses per source sound if null. - /// - [DataField("drinkSoundOverride")] - public SoundSpecifier? DrinkSound = new SoundPathSpecifier("/Audio/Items/drink.ogg"); - - /// - /// The localised string to display when drinking from a battery. - /// Doesn't _need_ to be localised, but come on, man. + /// The multiplier for how long it takes to drink a non-source battery, if is true. /// - [DataField("drinkText")] - public string DrinkText = "aaaaaaaaaa"; + [DataField("drinkAllMultiplier"), ViewVariables(VVAccess.ReadWrite)] + public float DrinkAllMultiplier = 2.5f; } diff --git a/Content.Server/SimpleStation14/Power/Components/RandomBatteryChargeComponent.cs b/Content.Server/SimpleStation14/Power/Components/RandomBatteryChargeComponent.cs index 505ff3176c..6f5f601675 100644 --- a/Content.Server/SimpleStation14/Power/Components/RandomBatteryChargeComponent.cs +++ b/Content.Server/SimpleStation14/Power/Components/RandomBatteryChargeComponent.cs @@ -1,7 +1,7 @@ namespace Content.Server.SimpleStation14.Power.Components; [RegisterComponent] -public class RandomBatteryChargeComponent : Component +public sealed class RandomBatteryChargeComponent : Component { /// /// The minimum and maximum max charge the battery can have. diff --git a/Content.Server/SimpleStation14/Power/Systems/BatteryDrinkerSystem.cs b/Content.Server/SimpleStation14/Power/Systems/BatteryDrinkerSystem.cs index e9eb0e6a57..3e92f7effe 100644 --- a/Content.Server/SimpleStation14/Power/Systems/BatteryDrinkerSystem.cs +++ b/Content.Server/SimpleStation14/Power/Systems/BatteryDrinkerSystem.cs @@ -2,15 +2,13 @@ using Content.Server.Power.Components; using Content.Shared.Containers.ItemSlots; using Content.Shared.DoAfter; -using Content.Shared.Interaction.Helpers; using Content.Shared.PowerCell.Components; using Content.Shared.SimpleStation14.Silicon; using Content.Shared.Verbs; -using Robust.Shared.Audio; -using Robust.Shared.Serialization; using Robust.Shared.Utility; using Content.Server.SimpleStation14.Silicon.Charge; using Content.Server.Power.EntitySystems; +using Content.Server.Popups; namespace Content.Server.SimpleStation14.Power; @@ -21,6 +19,7 @@ public sealed class BatteryDrinkerSystem : EntitySystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly BatterySystem _battery = default!; [Dependency] private readonly SiliconChargeSystem _silicon = default!; + [Dependency] private readonly PopupSystem _popup = default!; public override void Initialize() { @@ -28,7 +27,7 @@ public override void Initialize() SubscribeLocalEvent>(AddAltVerb); - SubscribeLocalEvent(OnDoAfter); + SubscribeLocalEvent(OnDoAfter); } private void AddAltVerb(EntityUid uid, BatteryComponent batteryComponent, GetVerbsEvent args) @@ -36,16 +35,16 @@ private void AddAltVerb(EntityUid uid, BatteryComponent batteryComponent, GetVer if (!args.CanAccess || !args.CanInteract) return; - if (!EntityManager.TryGetComponent(args.User, out var drinkerComp) || + if (!TryComp(args.User, out var drinkerComp) || !TestDrinkableBattery(uid, drinkerComp) || - !TryGetFillableBattery(args.User, out var drinkerBattery)) + !TryGetFillableBattery(args.User, out var drinkerBattery, out _)) return; AlternativeVerb verb = new() { Act = () => DrinkBattery(uid, args.User, drinkerComp), Text = "system-battery-drinker-verb-drink", - Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/drink.svg.192dpi.png")), + Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/smite.svg.192dpi.png")), }; args.Verbs.Add(verb); @@ -59,19 +58,22 @@ private bool TestDrinkableBattery(EntityUid target, BatteryDrinkerComponent drin return true; } - private bool TryGetFillableBattery(EntityUid uid, [NotNullWhen(true)] out BatteryComponent? battery) + private bool TryGetFillableBattery(EntityUid uid, [NotNullWhen(true)] out BatteryComponent? battery, [NotNullWhen(true)] out EntityUid batteryUid) { - if (_silicon.TryGetSiliconBattery(uid, out battery)) + if (_silicon.TryGetSiliconBattery(uid, out battery, out batteryUid)) return true; - if (EntityManager.TryGetComponent(uid, out battery)) + if (TryComp(uid, out battery)) return true; - if (EntityManager.TryGetComponent(uid, out var powerCellSlot) && + if (TryComp(uid, out var powerCellSlot) && _slots.TryGetSlot(uid, powerCellSlot.CellSlotId, out var slot) && slot.Item != null && - EntityManager.TryGetComponent(slot.Item.Value, out battery)) + TryComp(slot.Item.Value, out battery)) + { + batteryUid = slot.Item.Value; return true; + } return false; } @@ -80,12 +82,12 @@ private void DrinkBattery(EntityUid target, EntityUid user, BatteryDrinkerCompon { var doAfterTime = drinkerComp.DrinkSpeed; - if (EntityManager.TryGetComponent(target, out var sourceComp)) + if (TryComp(target, out var sourceComp)) doAfterTime *= sourceComp.DrinkSpeedMulti; else - doAfterTime *= 2.5f; + doAfterTime *= drinkerComp.DrinkAllMultiplier; - var args = new DoAfterArgs(user, doAfterTime, new BatteryDrinkerEvent(), user, target) + var args = new DoAfterArgs(user, doAfterTime, new BatteryDrinkerDoAfterEvent(), user, target) // TODO: Make this doafter loop, once we merge Upstream. { BreakOnDamage = true, BreakOnTargetMove = true, @@ -105,11 +107,11 @@ private void OnDoAfter(EntityUid uid, BatteryDrinkerComponent drinkerComp, DoAft var source = args.Target.Value; var drinker = uid; - var sourceBattery = EntityManager.GetComponent(source); + var sourceBattery = Comp(source); - TryGetFillableBattery(drinker, out var drinkerBattery); + TryGetFillableBattery(drinker, out var drinkerBattery, out var drinkerBatteryUid); - EntityManager.TryGetComponent(source, out var sourceComp); + TryComp(source, out var sourceComp); DebugTools.AssertNotNull(drinkerBattery); @@ -126,26 +128,19 @@ private void OnDoAfter(EntityUid uid, BatteryDrinkerComponent drinkerComp, DoAft if (amountToDrink <= 0) { - // Do empty stuff + _popup.PopupEntity(Loc.GetString("battery-drinker-empty", ("target", source)), drinker, drinker); return; } if (_battery.TryUseCharge(source, amountToDrink, sourceBattery)) - { - _battery.SetCharge(source, drinkerBattery.Charge + amountToDrink, sourceBattery); - } + _battery.SetCharge(drinkerBatteryUid, drinkerBattery.Charge + amountToDrink, drinkerBattery); else { - _battery.SetCharge(drinker, sourceBattery.Charge, drinkerBattery); + _battery.SetCharge(drinker, sourceBattery.Charge + drinkerBattery.Charge, drinkerBattery); _battery.SetCharge(source, 0, sourceBattery); } - var sound = drinkerComp.DrinkSound ?? sourceComp?.DrinkSound; - - if (sound != null) - _audio.PlayPvs(sound, source); - - // if (sourceBattery.CurrentCharge > 0) // Make use proper looping doafters when we merge Upstream. - // DrinkBattery(source, drinker, sourceBattery, drinkerBattery, drinkerComp); + if (sourceComp != null && sourceComp.DrinkSound != null) + _audio.PlayPvs(sourceComp.DrinkSound, source); } } diff --git a/Content.Server/SimpleStation14/Power/Systems/BatteryElectrocuteChargeSystem.cs b/Content.Server/SimpleStation14/Power/Systems/BatteryElectrocuteChargeSystem.cs index 03504e5afb..86238aab6c 100644 --- a/Content.Server/SimpleStation14/Power/Systems/BatteryElectrocuteChargeSystem.cs +++ b/Content.Server/SimpleStation14/Power/Systems/BatteryElectrocuteChargeSystem.cs @@ -1,6 +1,7 @@ using Content.Server.Electrocution; using Content.Server.Popups; using Content.Server.Power.Components; +using Content.Server.Power.EntitySystems; using Content.Shared.Electrocution; using Robust.Shared.Random; using Robust.Shared.Timing; @@ -11,7 +12,7 @@ public sealed class BatteryElectrocuteChargeSystem : EntitySystem { [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly PopupSystem _popup = default!; - [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly BatterySystem _battery = default!; public override void Initialize() { @@ -30,7 +31,7 @@ private void OnElectrocuted(EntityUid uid, BatteryComponent battery, Electrocute var damage = args.ShockDamage.Value * args.SiemensCoefficient; var charge = Math.Min(damage / damagePerWatt, battery.MaxCharge * 0.25f) * _random.NextFloat(0.75f, 1.25f); - battery.CurrentCharge += charge; + _battery.SetCharge(uid, battery.CurrentCharge + charge); _popup.PopupEntity(Loc.GetString("battery-electrocute-charge"), uid, uid); } diff --git a/Content.Server/SimpleStation14/Radio/IntrinsicRadioKeySystem.cs b/Content.Server/SimpleStation14/Radio/IntrinsicRadioKeySystem.cs index 6ea9c30d90..32dad9f078 100644 --- a/Content.Server/SimpleStation14/Radio/IntrinsicRadioKeySystem.cs +++ b/Content.Server/SimpleStation14/Radio/IntrinsicRadioKeySystem.cs @@ -24,7 +24,7 @@ private void OnReceiverChannelsChanged(EntityUid uid, ActiveRadioComponent compo UpdateChannels(uid, args.Component, ref component.Channels); } - private void UpdateChannels(EntityUid uid, EncryptionKeyHolderComponent keyHolderComp, ref HashSet channels) + private void UpdateChannels(EntityUid _, EncryptionKeyHolderComponent keyHolderComp, ref HashSet channels) { channels.Clear(); channels.UnionWith(keyHolderComp.Channels); diff --git a/Content.Server/SimpleStation14/Silicon/Charge/Components/BatteryDrinkerSourceComponent.cs b/Content.Server/SimpleStation14/Silicon/Charge/Components/BatteryDrinkerSourceComponent.cs index 807f08f003..f4abfa392e 100644 --- a/Content.Server/SimpleStation14/Silicon/Charge/Components/BatteryDrinkerSourceComponent.cs +++ b/Content.Server/SimpleStation14/Silicon/Charge/Components/BatteryDrinkerSourceComponent.cs @@ -3,10 +3,11 @@ namespace Content.Server.SimpleStation14.Silicon.Charge; [RegisterComponent] -public class BatteryDrinkerSourceComponent : Component +public sealed class BatteryDrinkerSourceComponent : Component { /// - /// The max amount of power to give when drunk from. + /// The max amount of power this source can provide in one sip. + /// No limit if null. /// [DataField("maxAmount"), ViewVariables(VVAccess.ReadWrite)] public int? MaxAmount = null; diff --git a/Content.Server/SimpleStation14/Silicon/Charge/Components/SiliconDownOnDeadComponent.cs b/Content.Server/SimpleStation14/Silicon/Charge/Components/SiliconDownOnDeadComponent.cs index 17fa934df0..434a4f6df2 100644 --- a/Content.Server/SimpleStation14/Silicon/Charge/Components/SiliconDownOnDeadComponent.cs +++ b/Content.Server/SimpleStation14/Silicon/Charge/Components/SiliconDownOnDeadComponent.cs @@ -1,7 +1,16 @@ namespace Content.Server.SimpleStation14.Silicon.Death; +/// +/// Marks a Silicon as becoming incapacitated when they run out of battery charge. +/// +/// +/// Uses the Silicon System's charge states to do so, so make sure they're a battery powered Silicon. +/// [RegisterComponent] public sealed class SiliconDownOnDeadComponent : Component { + /// + /// Is this Silicon currently dead? + /// public bool Dead { get; set; } = false; } diff --git a/Content.Server/SimpleStation14/Silicon/Charge/Systems/SiliconChargeDeathSystem.cs b/Content.Server/SimpleStation14/Silicon/Charge/Systems/SiliconChargeDeathSystem.cs index d69e65f4f7..37c6fe09f7 100644 --- a/Content.Server/SimpleStation14/Silicon/Charge/Systems/SiliconChargeDeathSystem.cs +++ b/Content.Server/SimpleStation14/Silicon/Charge/Systems/SiliconChargeDeathSystem.cs @@ -4,7 +4,6 @@ using Content.Shared.Bed.Sleep; using Content.Server.Sound.Components; using Content.Server.SimpleStation14.Silicon.Charge; -using Serilog; namespace Content.Server.SimpleStation14.Silicon.Death; @@ -22,20 +21,17 @@ public override void Initialize() private void OnSiliconChargeStateUpdate(EntityUid uid, SiliconDownOnDeadComponent siliconDeadComp, SiliconChargeStateUpdateEvent args) { - _silicon.TryGetSiliconBattery(uid, out var batteryComp); - - Logger.Debug($"Silicon charge state update: {args.ChargeState}"); - Logger.Debug($"Silicon battery: {batteryComp?.CurrentCharge}"); + _silicon.TryGetSiliconBattery(uid, out var batteryComp, out var batteryUid); if (args.ChargeState == ChargeState.Dead && !siliconDeadComp.Dead) - SiliconDead(uid, siliconDeadComp, batteryComp); + SiliconDead(uid, siliconDeadComp, batteryComp, batteryUid); else if (args.ChargeState != ChargeState.Dead && siliconDeadComp.Dead) - SiliconUnDead(uid, siliconDeadComp, batteryComp); + SiliconUnDead(uid, siliconDeadComp, batteryComp, batteryUid); } - private void SiliconDead(EntityUid uid, SiliconDownOnDeadComponent siliconDeadComp, BatteryComponent? batteryComp) + private void SiliconDead(EntityUid uid, SiliconDownOnDeadComponent siliconDeadComp, BatteryComponent? batteryComp, EntityUid batteryUid) { - var deadEvent = new SiliconChargeDyingEvent(uid, batteryComp); + var deadEvent = new SiliconChargeDyingEvent(uid, batteryComp, batteryUid); RaiseLocalEvent(uid, deadEvent); if (deadEvent.Cancelled) @@ -47,60 +43,70 @@ private void SiliconDead(EntityUid uid, SiliconDownOnDeadComponent siliconDeadCo siliconDeadComp.Dead = true; - RaiseLocalEvent(uid, new SiliconChargeDeathEvent(uid, batteryComp)); + RaiseLocalEvent(uid, new SiliconChargeDeathEvent(uid, batteryComp, batteryUid)); } - private void SiliconUnDead(EntityUid uid, SiliconDownOnDeadComponent siliconDeadComp, BatteryComponent? batteryComp) + private void SiliconUnDead(EntityUid uid, SiliconDownOnDeadComponent siliconDeadComp, BatteryComponent? batteryComp, EntityUid batteryUid) { _sleep.TryWaking(uid, null, true); siliconDeadComp.Dead = false; - RaiseLocalEvent(uid, new SiliconChargeAliveEvent(uid, batteryComp)); + RaiseLocalEvent(uid, new SiliconChargeAliveEvent(uid, batteryComp, batteryUid)); } } /// /// A cancellable event raised when a Silicon is about to go down due to charge. -/// +/// +/// +/// This probably shouldn't be modified unless you intend to fill the Silicon's battery, +/// as otherwise it'll just be triggered again next frame. +/// public sealed class SiliconChargeDyingEvent : CancellableEntityEventArgs { public EntityUid SiliconUid { get; } public BatteryComponent? BatteryComp { get; } + public EntityUid BatteryUid { get; } - public SiliconChargeDyingEvent(EntityUid siliconUid, BatteryComponent? batteryComp) + public SiliconChargeDyingEvent(EntityUid siliconUid, BatteryComponent? batteryComp, EntityUid batteryUid) { SiliconUid = siliconUid; BatteryComp = batteryComp; + BatteryUid = batteryUid; } } /// /// An event raised after a Silicon has gone down due to charge. -/// +/// public sealed class SiliconChargeDeathEvent : EntityEventArgs { public EntityUid SiliconUid { get; } public BatteryComponent? BatteryComp { get; } + public EntityUid BatteryUid { get; } - public SiliconChargeDeathEvent(EntityUid siliconUid, BatteryComponent? batteryComp) + public SiliconChargeDeathEvent(EntityUid siliconUid, BatteryComponent? batteryComp, EntityUid batteryUid) { SiliconUid = siliconUid; BatteryComp = batteryComp; + BatteryUid = batteryUid; } } /// /// An event raised after a Silicon has reawoken due to an increase in charge. -/// +/// public sealed class SiliconChargeAliveEvent : EntityEventArgs { public EntityUid SiliconUid { get; } public BatteryComponent? BatteryComp { get; } + public EntityUid BatteryUid { get; } - public SiliconChargeAliveEvent(EntityUid siliconUid, BatteryComponent? batteryComp) + public SiliconChargeAliveEvent(EntityUid siliconUid, BatteryComponent? batteryComp, EntityUid batteryUid) { SiliconUid = siliconUid; BatteryComp = batteryComp; + BatteryUid = batteryUid; } } diff --git a/Content.Server/SimpleStation14/Silicon/Charge/Systems/SiliconChargeSystem.cs b/Content.Server/SimpleStation14/Silicon/Charge/Systems/SiliconChargeSystem.cs index 4cd2e0c1ef..c0aac19c47 100644 --- a/Content.Server/SimpleStation14/Silicon/Charge/Systems/SiliconChargeSystem.cs +++ b/Content.Server/SimpleStation14/Silicon/Charge/Systems/SiliconChargeSystem.cs @@ -13,6 +13,11 @@ using Content.Server.Power.EntitySystems; using Robust.Shared.Containers; using System.Diagnostics.CodeAnalysis; +using Robust.Shared.Timing; +using Content.Shared.SimpleStation14.CCVar; +using Robust.Shared.Configuration; +using System.Diagnostics; +using Robust.Shared.Utility; namespace Content.Server.SimpleStation14.Silicon.Charge; @@ -25,6 +30,8 @@ public sealed class SiliconChargeSystem : EntitySystem [Dependency] private readonly MovementSpeedModifierSystem _moveMod = default!; [Dependency] private readonly BatterySystem _battery = default!; [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly IConfigurationManager _config = default!; public override void Initialize() { @@ -33,21 +40,23 @@ public override void Initialize() SubscribeLocalEvent(OnSiliconStartup); } - public bool TryGetSiliconBattery(EntityUid uid, [NotNullWhen(true)] out BatteryComponent? batteryComp) + public bool TryGetSiliconBattery(EntityUid silicon, [NotNullWhen(true)] out BatteryComponent? batteryComp, out EntityUid batteryUid) { batteryComp = null; + batteryUid = silicon; - if (!EntityManager.TryGetComponent(uid, out SiliconComponent? siliconComp)) + if (!EntityManager.TryGetComponent(silicon, out SiliconComponent? siliconComp)) return false; if (siliconComp.BatteryContainer != null && siliconComp.BatteryContainer.ContainedEntities.Count > 0 && TryComp(siliconComp.BatteryContainer.ContainedEntities[0], out batteryComp)) { + batteryUid = siliconComp.BatteryContainer.ContainedEntities[0]; return true; } - if (TryComp(uid, out batteryComp)) + if (TryComp(silicon, out batteryComp)) return true; return false; @@ -58,8 +67,11 @@ private void OnSiliconStartup(EntityUid uid, SiliconComponent component, Compone if (component.BatterySlot == null) return; - var container = _container.EnsureContainer(uid, component.BatterySlot); + var container = _container.GetContainer(uid, component.BatterySlot); component.BatteryContainer = container; + + if (component.EntityType.GetType() != typeof(SiliconType)) + DebugTools.Assert("SiliconComponent.EntityType is not a SiliconType enum."); } public override void Update(float frameTime) @@ -70,14 +82,31 @@ public override void Update(float frameTime) var query = EntityQueryEnumerator(); while (query.MoveNext(out var silicon, out var siliconComp)) { - if (!siliconComp.BatteryPowered || !TryGetSiliconBattery(silicon, out var batteryComp)) + if (!siliconComp.BatteryPowered) + continue; + + // Check if the Silicon is an NPC, and if so, follow the delay as specified in the CVAR. + if (siliconComp.EntityType.Equals(SiliconType.Npc)) + { + var updateTime = _config.GetCVar(SimpleStationCCVars.SiliconNpcUpdateTime); + if (_timing.CurTime - siliconComp.LastDrainTime < TimeSpan.FromSeconds(updateTime)) + continue; + + siliconComp.LastDrainTime = _timing.CurTime; + } + + // If you can't find a battery, set the indicator and skip it. + if (!TryGetSiliconBattery(silicon, out var batteryComp, out var battery)) + { + UpdateChargeState(battery, ChargeState.Invalid, siliconComp); continue; + } // If the silicon is dead, skip it. if (_mobState.IsDead(silicon)) continue; - var drainRate = 10 * siliconComp.DrainRateMulti; + var drainRate = siliconComp.DrainPerSecond; // All multipliers will be subtracted by 1, and then added together, and then multiplied by the drain rate. This is then added to the base drain rate. // This is to stop exponential increases, while still allowing for less-than-one multipliers. @@ -86,15 +115,15 @@ public override void Update(float frameTime) // TODO: Devise a method of adding multis where other systems can alter the drain rate. // Maybe use something similar to refreshmovespeedmodifiers, where it's stored in the component. // Maybe it doesn't matter, and stuff should just use static drain? - - drainRateFinalAddi += SiliconHeatEffects(silicon, frameTime) - 1; + if (!siliconComp.EntityType.Equals(SiliconType.Npc)) // Don't bother checking heat if it's an NPC. It's a waste of time, and it'd be delayed due to the update time. + drainRateFinalAddi += SiliconHeatEffects(silicon, frameTime) - 1; // This will need to be changed at some point if we allow external batteries, since the heat of the Silicon might not be applicable. // Ensures that the drain rate is at least 10% of normal, // and would allow at least 4 minutes of life with a max charge, to prevent cheese. drainRate += Math.Clamp(drainRateFinalAddi, drainRate * -0.9f, batteryComp.MaxCharge / 240); // Drain the battery. - _battery.UseCharge(silicon, frameTime * drainRate, batteryComp); + _battery.UseCharge(battery, frameTime * drainRate, batteryComp); // Figure out the current state of the Silicon. var chargePercent = batteryComp.CurrentCharge / batteryComp.MaxCharge; @@ -108,16 +137,23 @@ public override void Update(float frameTime) _ => ChargeState.Dead, }; - // Check if anything needs to be updated. - if (currentState != siliconComp.ChargeState) - { - siliconComp.ChargeState = currentState; + UpdateChargeState(silicon, currentState, siliconComp); + } + } - RaiseLocalEvent(silicon, new SiliconChargeStateUpdateEvent(currentState)); + /// + /// Checks if anything needs to be updated, and updates it. + /// + public void UpdateChargeState(EntityUid uid, ChargeState state, SiliconComponent component) + { + if (component.ChargeState == state) + return; - _moveMod.RefreshMovementSpeedModifiers(silicon); - } - } + component.ChargeState = state; + + RaiseLocalEvent(uid, new SiliconChargeStateUpdateEvent(state)); + + _moveMod.RefreshMovementSpeedModifiers(uid); } private float SiliconHeatEffects(EntityUid silicon, float frameTime) diff --git a/Content.Server/SimpleStation14/Silicon/Charge/Systems/SiliconChargerSystem.cs b/Content.Server/SimpleStation14/Silicon/Charge/Systems/SiliconChargerSystem.cs index 9e92177b8d..cdb54599c0 100644 --- a/Content.Server/SimpleStation14/Silicon/Charge/Systems/SiliconChargerSystem.cs +++ b/Content.Server/SimpleStation14/Silicon/Charge/Systems/SiliconChargerSystem.cs @@ -150,49 +150,50 @@ private void HandleChargingEntity(EntityUid entity, float chargeRate, SiliconCha // Now we charge the entities we found. chargeRate /= entitiesToCharge.Count; - foreach (var entityToCharge in entitiesToCharge.ToList()) + foreach (var (entityToCharge, batteryComp) in entitiesToCharge.ToList()) { - if (_silicon.TryGetSiliconBattery(entityToCharge, out var batteryComp)) + if (batteryComp != null) ChargeBattery(entityToCharge, batteryComp, chargeRate, chargerComp, chargerUid); else if (TryComp(entityToCharge, out var damageComp)) BurnEntity(entityToCharge, damageComp, frameTime, chargerComp, chargerUid); } } - private List SearchThroughEntities(EntityUid entity, bool burn = true) + private List<(EntityUid, BatteryComponent?)> SearchThroughEntities(EntityUid entity, bool burn = true) { - var entitiesToCharge = new List(); + var entitiesToCharge = new List<(EntityUid, BatteryComponent?)>(); - if (_silicon.TryGetSiliconBattery(entity, out var siliconBatteryComp)) + // If the given entity is a silicon, charge their respective battery. + if (_silicon.TryGetSiliconBattery(entity, out var siliconBatteryComp, out var siliconBatteryUid)) { if (siliconBatteryComp.CurrentCharge < siliconBatteryComp.MaxCharge) - entitiesToCharge.Add(entity); + entitiesToCharge.Add((siliconBatteryUid, siliconBatteryComp)); } - // If the given entity has a battery, charge it. + // Or if the given entity has a battery, charge it. else if (!HasComp(entity) && // Should probably be charged by the entity holding it. Might be too small to be safe. TryComp(entity, out var batteryComp)) { if (batteryComp.CurrentCharge < batteryComp.MaxCharge) - entitiesToCharge.Add(entity); + entitiesToCharge.Add((entity, batteryComp)); } - // If the given entity contains a battery, charge it. + // Or if the given entity contains a battery, charge it. else if (!HasComp(entity) && // Should probably be charged by the entity holding it. Might be too small to be safe. TryComp(entity, out var cellSlotComp) && _itemSlots.TryGetSlot(entity, cellSlotComp.CellSlotId, out var slot) && TryComp(slot.Item, out var cellBattComp)) { if (cellBattComp.CurrentCharge < cellBattComp.MaxCharge) - entitiesToCharge.Add(slot.Item.Value); + entitiesToCharge.Add((slot.Item.Value, cellBattComp)); } - // If the given entity is fleshy, burn the fucker. + // Or if the given entity is fleshy, burn the fucker. else if (burn && TryComp(entity, out var damageComp) && damageComp.DamageContainerID == "Biological") { - entitiesToCharge.Add(entity); + entitiesToCharge.Add((entity, null)); } // Now the weird part, we check for any inventories the entities contained may have, and run this function on any entities contained, for a recursive charging effect. diff --git a/Content.Server/SimpleStation14/Silicon/Systems/SiliconMiscSystem.cs b/Content.Server/SimpleStation14/Silicon/Systems/SiliconMiscSystem.cs index 117fc331f7..d331d8908e 100644 --- a/Content.Server/SimpleStation14/Silicon/Systems/SiliconMiscSystem.cs +++ b/Content.Server/SimpleStation14/Silicon/Systems/SiliconMiscSystem.cs @@ -4,7 +4,6 @@ namespace Content.Server.SimpleStation14.Silicon.Misc; // This entire thing is fucking stupid and I hate it. -// I will never forgive them for what they did with sleeping... public sealed class SiliconMiscSystem : EntitySystem { public override void Initialize() diff --git a/Content.Shared/Radio/EntitySystems/EncryptionKeySystem.cs b/Content.Shared/Radio/EntitySystems/EncryptionKeySystem.cs index ac8507bd13..bd80b6aecb 100644 --- a/Content.Shared/Radio/EntitySystems/EncryptionKeySystem.cs +++ b/Content.Shared/Radio/EntitySystems/EncryptionKeySystem.cs @@ -62,7 +62,7 @@ private void OnKeyRemoval(EntityUid uid, EncryptionKeyHolderComponent component, // TODO add predicted pop-up overrides. if (_net.IsServer) _popup.PopupEntity(Loc.GetString("encryption-keys-all-extracted"), uid, args.User); - + _audio.PlayPredicted(component.KeyExtractionSound, uid, args.User); } diff --git a/Content.Shared/SimpleStation14/CCVar/CCVars.cs b/Content.Shared/SimpleStation14/CCVar/CCVars.cs index 91fdb6529a..1baaad6ec5 100644 --- a/Content.Shared/SimpleStation14/CCVar/CCVars.cs +++ b/Content.Shared/SimpleStation14/CCVar/CCVars.cs @@ -30,7 +30,7 @@ public sealed class SimpleStationCCVars /// public static readonly CVarDef BloodLostThreshold = CVarDef.Create("eorstats.bloodlost_threshold", 300f, CVar.SERVERONLY); - #endregion + #endregion BloodLost #region CuffedTime /// @@ -41,7 +41,7 @@ public sealed class SimpleStationCCVars /// public static readonly CVarDef CuffedTimeThreshold = CVarDef.Create("eorstats.cuffedtime_threshold", 8, CVar.SERVERONLY); - #endregion + #endregion CuffedTime #region EmitSound /// @@ -52,7 +52,7 @@ public sealed class SimpleStationCCVars /// public static readonly CVarDef EmitSoundThreshold = CVarDef.Create("eorstats.emitsound_threshold", 80, CVar.SERVERONLY); - #endregion + #endregion EmitSound #region InstrumentPlayed /// @@ -63,7 +63,7 @@ public sealed class SimpleStationCCVars /// public static readonly CVarDef InstrumentPlayedThreshold = CVarDef.Create("eorstats.instrumentplayed_threshold", 8, CVar.SERVERONLY); - #endregion + #endregion InstrumentPlayed #region MopUsed /// @@ -89,7 +89,7 @@ public sealed class SimpleStationCCVars /// public static readonly CVarDef MopUsedTopMopperCount = CVarDef.Create("eorstats.mopused_topmoppercount", 3, CVar.SERVERONLY); - #endregion + #endregion MopUsed #region ShotsFired /// @@ -106,7 +106,7 @@ public sealed class SimpleStationCCVars /// public static readonly CVarDef ShotsFiredDisplayNone = CVarDef.Create("eorstats.shotsfired_displaynone", true, CVar.SERVERONLY); - #endregion + #endregion ShotsFired #region SlippedCount /// @@ -129,6 +129,17 @@ public sealed class SimpleStationCCVars /// public static readonly CVarDef SlippedCountTopSlipper = CVarDef.Create("eorstats.slippedcount_topslipper", true, CVar.SERVERONLY); - #endregion - #endregion + #endregion SlippedCount + #endregion EndOfRoundStats + + /* + * Silicons + */ + #region Silicons + /// + /// The amount of time between NPC Silicons draining their battery in seconds. + /// + public static readonly CVarDef SiliconNpcUpdateTime = + CVarDef.Create("silicon.npcupdatetime", 1.5f, CVar.SERVERONLY); + #endregion Silicons } diff --git a/Content.Shared/SimpleStation14/Silicon/BatteryDrinkerEvent.cs b/Content.Shared/SimpleStation14/Silicon/BatteryDrinkerEvent.cs index 738a7a34dd..adf9570401 100644 --- a/Content.Shared/SimpleStation14/Silicon/BatteryDrinkerEvent.cs +++ b/Content.Shared/SimpleStation14/Silicon/BatteryDrinkerEvent.cs @@ -4,9 +4,9 @@ namespace Content.Shared.SimpleStation14.Silicon; [Serializable, NetSerializable] -public sealed class BatteryDrinkerEvent : SimpleDoAfterEvent +public sealed class BatteryDrinkerDoAfterEvent : SimpleDoAfterEvent { - public BatteryDrinkerEvent() + public BatteryDrinkerDoAfterEvent() { } } diff --git a/Content.Shared/SimpleStation14/Silicon/Components/SiliconComponent.cs b/Content.Shared/SimpleStation14/Silicon/Components/SiliconComponent.cs index c8ef4e95ae..2a4683dc31 100644 --- a/Content.Shared/SimpleStation14/Silicon/Components/SiliconComponent.cs +++ b/Content.Shared/SimpleStation14/Silicon/Components/SiliconComponent.cs @@ -18,21 +18,31 @@ public sealed class SiliconComponent : Component public float OverheatAccumulator = 0.0f; /// - /// The owner of this component. + /// The last time the Silicon was drained. + /// Used for NPC Silicons to avoid over updating. /// - [ViewVariables(VVAccess.ReadOnly)] - public new EntityUid Owner = EntityUid.Invalid; + /// + /// Time between drains can be specified in + /// + /// + public TimeSpan LastDrainTime = TimeSpan.Zero; /// /// The Silicon's battery slot, if it has one. /// - public Container? BatteryContainer = null; + public IContainer? BatteryContainer = null; /// /// Is the Silicon currently dead? /// public bool Dead = false; + // BatterySystem took issue with how this was used, so I'm coming back to it at a later date, when more foundational Silicon stuff is implemented. + // /// + // /// The entity to get the battery from. + // /// + // public EntityUid BatteryOverride? = EntityUid.Invalid; + /// /// The type of silicon this is. @@ -41,7 +51,7 @@ public sealed class SiliconComponent : Component /// Any new types of Silicons should be added to the enum. /// [DataField("entityType", customTypeSerializer: typeof(EnumSerializer))] - public Enum EntityType = SiliconType.NPC; + public Enum EntityType = SiliconType.Npc; /// /// Is this silicon battery powered? @@ -60,10 +70,10 @@ public sealed class SiliconComponent : Component public string? BatterySlot = null; /// - /// Multiplier for the drain rate of the silicon. + /// How much power is drained by this Silicon every second by default. /// - [DataField("drainRateMulti"), ViewVariables(VVAccess.ReadWrite)] - public float DrainRateMulti = 5.0f; + [DataField("drainPerSecond"), ViewVariables(VVAccess.ReadWrite)] + public float DrainPerSecond = 50f; /// diff --git a/Content.Shared/SimpleStation14/Silicon/Systems/SharedSiliconSystem.cs b/Content.Shared/SimpleStation14/Silicon/Systems/SharedSiliconSystem.cs index beb4246f65..8ddce7550b 100644 --- a/Content.Shared/SimpleStation14/Silicon/Systems/SharedSiliconSystem.cs +++ b/Content.Shared/SimpleStation14/Silicon/Systems/SharedSiliconSystem.cs @@ -18,6 +18,7 @@ public sealed class SharedSiliconChargeSystem : EntitySystem {ChargeState.Low, 2}, {ChargeState.Critical, 1}, {ChargeState.Dead, 0}, + {ChargeState.Invalid, -1}, }; public override void Initialize() @@ -31,17 +32,13 @@ public override void Initialize() private void OnSiliconInit(EntityUid uid, SiliconComponent component, ComponentInit args) { - component.Owner = uid; - if (component.BatteryPowered) - { - _alertsSystem.ShowAlert(uid, AlertType.Charge, ChargeStateAlert[component.ChargeState]); - } + _alertsSystem.ShowAlert(uid, AlertType.Charge, (short) component.ChargeState); } private void OnSiliconChargeStateUpdate(EntityUid uid, SiliconComponent component, SiliconChargeStateUpdateEvent ev) { - _alertsSystem.ShowAlert(uid, AlertType.Charge, ChargeStateAlert[component.ChargeState]); + _alertsSystem.ShowAlert(uid, AlertType.Charge, (short) ev.ChargeState); } private void OnRefreshMovespeed(EntityUid uid, SiliconComponent component, RefreshMovementSpeedModifiersEvent args) @@ -51,7 +48,7 @@ private void OnRefreshMovespeed(EntityUid uid, SiliconComponent component, Refre var speedModThresholds = component.SpeedModifierThresholds; - var closest = -0.5f; + var closest = 0f; foreach (var state in speedModThresholds) { @@ -70,16 +67,17 @@ public enum SiliconType { Player, GhostRole, - NPC + Npc, } public enum ChargeState { - Full, - Mid, - Low, + Invalid = -1, + Dead, Critical, - Dead + Low, + Mid, + Full, } diff --git a/Resources/Locale/en-US/SimpleStation14/Content/Power/batteries.ftl b/Resources/Locale/en-US/SimpleStation14/Content/Power/batteries.ftl new file mode 100644 index 0000000000..15ebafae77 --- /dev/null +++ b/Resources/Locale/en-US/SimpleStation14/Content/Power/batteries.ftl @@ -0,0 +1 @@ +battery-electrocute-charge = The battery surges with energy! diff --git a/Resources/Locale/en-US/SimpleStation14/Content/Power/batteryDrinker.ftl b/Resources/Locale/en-US/SimpleStation14/Content/Power/batteryDrinker.ftl new file mode 100644 index 0000000000..1f6425d943 --- /dev/null +++ b/Resources/Locale/en-US/SimpleStation14/Content/Power/batteryDrinker.ftl @@ -0,0 +1,2 @@ +battery-drinker-drink = Drain +battery-drinker-empty = {CAPATALIZE(THE($target))} is already empty! diff --git a/Resources/Locale/en-US/SimpleStation14/Content/Silicons/silicons.ftl b/Resources/Locale/en-US/SimpleStation14/Content/Silicons/silicons.ftl index 7f53b2a89d..b6c8c5dc4d 100644 --- a/Resources/Locale/en-US/SimpleStation14/Content/Silicons/silicons.ftl +++ b/Resources/Locale/en-US/SimpleStation14/Content/Silicons/silicons.ftl @@ -1 +1,4 @@ silicon-overheating = You feel your circuits overheating! + +alerts-charge-name = Charge +alerts-charge-desk = Your current battery level. diff --git a/Resources/Locale/en-US/devices/device-network.ftl b/Resources/Locale/en-US/devices/device-network.ftl index 9e5f241eb4..0dc214da78 100644 --- a/Resources/Locale/en-US/devices/device-network.ftl +++ b/Resources/Locale/en-US/devices/device-network.ftl @@ -24,14 +24,14 @@ device-address-prefix-vent = Vnt- device-address-prefix-scrubber = Scr- device-address-prefix-sensor = Sns- -#PDAs and terminals +# PDAs and terminals device-address-prefix-console = Cls- device-address-prefix-fire-alarm = Fir- device-address-prefix-air-alarm = Air- device-address-examine-message = The device's address is {$address}. -#Device net ID names +# Device net ID names device-net-id-private = Private device-net-id-wired = Wired device-net-id-wireless = Wireless diff --git a/Resources/Locale/en-US/job/department-desc.ftl b/Resources/Locale/en-US/job/department-desc.ftl index 194a02d9be..bc1ed8b081 100644 --- a/Resources/Locale/en-US/job/department-desc.ftl +++ b/Resources/Locale/en-US/job/department-desc.ftl @@ -6,6 +6,6 @@ department-Medical-description = Keep the crew healthy. department-Security-description = Keep the peace around the station. department-Science-description = Research new technologies and dangerous artifacts. -#Nyano +# Nyano department-Epistemics-description = Discover what lies beyond the fabric of reality. department-Silicon-description = Follow your laws in service to the humans. diff --git a/Resources/Locale/en-US/revenant/revenant.ftl b/Resources/Locale/en-US/revenant/revenant.ftl index 3c7841b778..1380f73936 100644 --- a/Resources/Locale/en-US/revenant/revenant.ftl +++ b/Resources/Locale/en-US/revenant/revenant.ftl @@ -18,7 +18,7 @@ revenant-soul-finish-harvest = {CAPITALIZE(THE($target))} slumps onto the ground revenant-psionic-power = a spirit power -#UI +# UI revenant-user-interface-title = Ability Shop revenant-user-interface-essence-amount = [color=plum]{$amount}[/color] Stolen Essence diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml b/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml index 684e981d61..963a91e4e4 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml @@ -97,6 +97,31 @@ - type: Alerts - type: TypingIndicator proto: robot + - type: PowerCellSlot + cellSlotId: cell_slot + - type: ItemSlots + slots: + cell_slot: + name: power-cell-slot-component-slot-name-default + startingItem: PowerCellHyper + - type: ContainerContainer + containers: + cell_slot: !type:ContainerSlot + - type: Silicon + batterySlot: cell_slot + entityType: enum.SiliconType.Npc + batteryPowered: true + drainPerSecond: 0.67 + chargeThresholdMid: 0 + chargeThresholdLow: 0 + chargeThresholdCritical: 0.12 + speedModifierThresholds: + 4: 1 + 3: 1 + 2: 1 + 1: 0.20 + 0: 0.00 + - type: SiliconDownOnDead - type: entity parent: MobSiliconBase diff --git a/Resources/Prototypes/Entities/Structures/Power/apc.yml b/Resources/Prototypes/Entities/Structures/Power/apc.yml index e0369bdda7..014ddbe81a 100644 --- a/Resources/Prototypes/Entities/Structures/Power/apc.yml +++ b/Resources/Prototypes/Entities/Structures/Power/apc.yml @@ -185,6 +185,8 @@ - type: Battery maxCharge: 50000 startingCharge: 50000 + - type: BatteryDrinkerSource + maxAmount: 5000 - type: entity parent: BaseAPC @@ -194,6 +196,8 @@ - type: Battery maxCharge: 100000 startingCharge: 100000 + - type: BatteryDrinkerSource + maxAmount: 12000 - type: entity parent: BaseAPC @@ -203,6 +207,8 @@ - type: Battery maxCharge: 150000 startingCharge: 150000 + - type: BatteryDrinkerSource + maxAmount: 18000 - type: entity parent: BaseAPC @@ -212,3 +218,5 @@ - type: Battery maxCharge: 200000 startingCharge: 200000 + - type: BatteryDrinkerSource + maxAmount: 26000 diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Mobs/Player/robots.yml b/Resources/Prototypes/Nyanotrasen/Entities/Mobs/Player/robots.yml index 8793f6b02b..bfe821c69c 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Mobs/Player/robots.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Mobs/Player/robots.yml @@ -191,16 +191,15 @@ - type: Silicon entityType: enum.SiliconType.Player batteryPowered: true - drainRateMulti: 4 - chargeRateMulti: 6 + drainPerSecond: 15 chargeThresholdMid: 0.60 chargeThresholdLow: 0.30 chargeThresholdCritical: 0 speedModifierThresholds: - 0: 1 - 1: 1 + 4: 1 + 3: 1 2: 0.75 - 3: 0.15 + 1: 0.15 - type: entity parent: PlayerRobotBase diff --git a/Resources/Prototypes/SimpleStation14/Alerts/alerts.yml b/Resources/Prototypes/SimpleStation14/Alerts/alerts.yml index 5e729c2532..24515f1e88 100644 --- a/Resources/Prototypes/SimpleStation14/Alerts/alerts.yml +++ b/Resources/Prototypes/SimpleStation14/Alerts/alerts.yml @@ -1,6 +1,8 @@ - type: alert id: Charge icons: + - sprite: /Textures/SimpleStation14/Interface/Alerts/charge.rsi + state: charge-empty - sprite: /Textures/SimpleStation14/Interface/Alerts/charge.rsi state: charge0 - sprite: /Textures/SimpleStation14/Interface/Alerts/charge.rsi @@ -13,5 +15,5 @@ state: charge4 name: alerts-charge-name description: alerts-charge-desc - minSeverity: 0 + minSeverity: -1 maxSeverity: 4 diff --git a/Resources/Prototypes/SimpleStation14/Entities/Mobs/Player/ipc.yml b/Resources/Prototypes/SimpleStation14/Entities/Mobs/Player/ipc.yml index 8a7e71b2ad..5012c26623 100644 --- a/Resources/Prototypes/SimpleStation14/Entities/Mobs/Player/ipc.yml +++ b/Resources/Prototypes/SimpleStation14/Entities/Mobs/Player/ipc.yml @@ -15,16 +15,16 @@ - type: Silicon entityType: enum.SiliconType.Player batteryPowered: true - drainRateMulti: 5 + drainPerSecond: 60 chargeThresholdMid: 0.80 chargeThresholdLow: 0.35 chargeThresholdCritical: 0.10 speedModifierThresholds: - 0: 1 - 1: 1 + 4: 1 + 3: 1 2: 0.80 - 3: 0.45 - 4: 0.00 + 1: 0.45 + 0: 0.00 - type: Carriable - type: BatteryDrinker - type: EncryptionKeyHolder diff --git a/Resources/Prototypes/SimpleStation14/Entities/Mobs/Player/silicon_base.yml b/Resources/Prototypes/SimpleStation14/Entities/Mobs/Player/silicon_base.yml index 91cce030d4..e6a2618ffe 100644 --- a/Resources/Prototypes/SimpleStation14/Entities/Mobs/Player/silicon_base.yml +++ b/Resources/Prototypes/SimpleStation14/Entities/Mobs/Player/silicon_base.yml @@ -63,17 +63,15 @@ - type: Silicon entityType: enum.SiliconType.Player batteryPowered: false # Needs to also have a battery! - drainRateMulti: 4.5 - chargeRateMulti: 6 chargeThresholdMid: 0.60 chargeThresholdLow: 0.30 chargeThresholdCritical: 0 speedModifierThresholds: - 0: 1 - 1: 1 + 4: 1 + 3: 1 2: 0.80 - 3: 0.45 - 4: 0.00 + 1: 0.45 + 0: 0.00 - type: RadiationReceiver - type: AtmosExposed - type: Temperature