From: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> Date: Sun, 18 May 2025 04:32:51 +0000 (-0700) Subject: Atmos Air Grenades (#37531) X-Git-Url: https://git.smokeofanarchy.ru/gitweb.cgi?a=commitdiff_plain;h=95cc36c41d1a01ce1568945e2734200c6add57a3;p=space-station-14.git Atmos Air Grenades (#37531) --- diff --git a/Content.Server/Explosion/EntitySystems/ReleaseGasOnTriggerSystem.cs b/Content.Server/Explosion/EntitySystems/ReleaseGasOnTriggerSystem.cs new file mode 100644 index 0000000000..09b6e1bf24 --- /dev/null +++ b/Content.Server/Explosion/EntitySystems/ReleaseGasOnTriggerSystem.cs @@ -0,0 +1,92 @@ +using Content.Server.Atmos.EntitySystems; +using Content.Shared.Explosion.Components.OnTrigger; +using Content.Shared.Explosion.EntitySystems; +using Robust.Shared.Timing; + +namespace Content.Server.Explosion.EntitySystems; + +/// +/// Releases a gas mixture to the atmosphere when triggered. +/// Can also release gas over a set timespan to prevent trolling people +/// with the instant-wall-of-pressure-inator. +/// +public sealed partial class ReleaseGasOnTriggerSystem : SharedReleaseGasOnTriggerSystem +{ + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; + [Dependency] private readonly IGameTiming _timing = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnTrigger); + } + + /// + /// Shrimply sets the component to active when triggered, allowing it to release over time. + /// + private void OnTrigger(Entity ent, ref TriggerEvent args) + { + ent.Comp.Active = true; + ent.Comp.NextReleaseTime = _timing.CurTime; + ent.Comp.StartingTotalMoles = ent.Comp.Air.TotalMoles; + UpdateAppearance(ent.Owner, true); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var curTime = _timing.CurTime; + var query = EntityQueryEnumerator(); + + while (query.MoveNext(out var uid, out var comp)) + { + if (!comp.Active || comp.NextReleaseTime > curTime) + continue; + + var giverGasMix = comp.Air.Remove(comp.StartingTotalMoles * comp.RemoveFraction); + var environment = _atmosphereSystem.GetContainingMixture(uid, false, true); + + if (environment == null) + { + UpdateAppearance(uid, false); + RemCompDeferred(uid); + continue; + } + + _atmosphereSystem.Merge(environment, giverGasMix); + comp.NextReleaseTime += comp.ReleaseInterval; + + if (comp.PressureLimit != 0 && environment.Pressure >= comp.PressureLimit || + comp.Air.TotalMoles <= 0) + { + UpdateAppearance(uid, false); + RemCompDeferred(uid); + continue; + } + + if (comp.ExponentialRise) + UpdateExponentialRise(comp, comp.RemoveFraction); + } + } + + /// + /// Updates the RemoveFraction for exponential rise. + /// + /// See https://www.desmos.com/calculator/fx9gfrhoim + private static void UpdateExponentialRise(ReleaseGasOnTriggerComponent comp, float baseFraction) + { + comp.TimesReleased++; + comp.RemoveFraction = 1f - MathF.Pow(1f - baseFraction, comp.TimesReleased); + } + + private void UpdateAppearance(Entity entity, bool state) + { + if (!Resolve(entity, ref entity.Comp, false)) + return; + + _appearance.SetData(entity, ReleaseGasOnTriggerComponent.ReleaseGasOnTriggerVisuals.Key, state); + } +} diff --git a/Content.Shared/Explosion/Components/OnTrigger/ReleaseGasOnTriggerComponent.cs b/Content.Shared/Explosion/Components/OnTrigger/ReleaseGasOnTriggerComponent.cs new file mode 100644 index 0000000000..5072b89dd2 --- /dev/null +++ b/Content.Shared/Explosion/Components/OnTrigger/ReleaseGasOnTriggerComponent.cs @@ -0,0 +1,93 @@ +using Content.Shared.Atmos; +using Content.Shared.Explosion.EntitySystems; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; + +namespace Content.Shared.Explosion.Components.OnTrigger; + +/// +/// Contains a GasMixture that will release its contents to the atmosphere when triggered. +/// +[RegisterComponent, NetworkedComponent] +[AutoGenerateComponentPause] +[Access(typeof(SharedReleaseGasOnTriggerSystem))] +public sealed partial class ReleaseGasOnTriggerComponent : Component +{ + /// + /// Represents visual states for whatever visuals that need to be applied + /// on state changes. + /// + [Serializable] [NetSerializable] + public enum ReleaseGasOnTriggerVisuals : byte + { + Key, + } + + /// + /// Whether this grenade is active and releasing gas. + /// Set to true when triggered, which starts gas release. + /// + [DataField] + public bool Active; + + /// + /// The gas mixture that will be released to the current tile atmosphere when triggered. + /// + [DataField] + public GasMixture Air; + + /// + /// If true, the gas will be released in an exponential manner. + /// + [DataField] + public bool ExponentialRise; + + /// + /// Time at which the next release will occur. + /// This is automatically set when the grenade activates. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + [AutoPausedField] + public TimeSpan NextReleaseTime = TimeSpan.Zero; + + /// + /// The cap at which this grenade can fill the exposed atmosphere to. + /// The grenade automatically deletes itself when the pressure is reached. + /// + /// If set to 101.325, the grenade will only fill the exposed + /// atmosphere up to 101.325 kPa. + /// If zero, this limit won't be respected. + [DataField] + public float PressureLimit; + + /// + /// How often the grenade will release gas. + /// + [DataField] + public TimeSpan ReleaseInterval = TimeSpan.FromSeconds(1); + + /// + /// A float from 0 to 1, representing a partial portion of the moles + /// of the gas mixture that will be + /// released to the current tile atmosphere when triggered. + /// + /// If undefined on the prototype, the entire molar amount will be transferred. + [DataField] + public float RemoveFraction = 1; + + /// + /// Stores the total moles initially in the grenade upon activation. + /// Used to calculate the moles released over time. + /// + /// Set when the grenade is activated. + [DataField(readOnly: true)] + public float StartingTotalMoles; + + /// + /// Stores the number of times the grenade has been released, + /// for exponential rise calculations. + /// + [DataField] + public int TimesReleased; +} diff --git a/Content.Shared/Explosion/EntitySystems/SharedReleaseGasOnTriggerSystem.cs b/Content.Shared/Explosion/EntitySystems/SharedReleaseGasOnTriggerSystem.cs new file mode 100644 index 0000000000..5027b04517 --- /dev/null +++ b/Content.Shared/Explosion/EntitySystems/SharedReleaseGasOnTriggerSystem.cs @@ -0,0 +1,5 @@ +namespace Content.Shared.Explosion.EntitySystems; + +public abstract partial class SharedReleaseGasOnTriggerSystem : EntitySystem; + +// I have dreams of Atmos in shared. diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/engineer.yml b/Resources/Prototypes/Catalog/Fills/Lockers/engineer.yml index cac3eab043..b1c5b07aa6 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/engineer.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/engineer.yml @@ -115,6 +115,7 @@ - id: HolofanProjector - id: RCD - id: RCDAmmo + - id: AirGrenade - type: entity id: LockerAtmosphericsFilled @@ -131,6 +132,7 @@ - id: HolofanProjector - id: RCD - id: RCDAmmo + - id: AirGrenade - type: entity id: LockerEngineerFilledHardsuit diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml index 66dc50c5c6..6cc8eb82c7 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml @@ -461,6 +461,38 @@ - type: StaticPrice price: 350 +- type: entity + parent: [ BaseEngineeringContraband, GrenadeBase ] # Prevent inheriting DeleteOnTrigger from SmokeGrenade + id: AirGrenade + name: air grenade + description: A special solid state chemical grenade used for quickly releasing standard air into a spaced area. Fills up to 30 tiles! + components: + - type: Sprite + sprite: Objects/Weapons/Grenades/airboom.rsi + - type: SoundOnTrigger + sound: /Audio/Items/smoke_grenade_smoke.ogg + - type: TimerTriggerVisuals + primingSound: + path: /Audio/Items/smoke_grenade_prime.ogg + - type: OnUseTimerTrigger + delay: 3 + - type: ReleaseGasOnTrigger + removeFraction: 0.25 + air: + volume: 1000 + moles: # Target is 3117.84 mols total for filling 30 tiles (goal is 101.325 kPa @ 20C) + - 654.7464 # oxygen + - 2463.0936 # nitrogen + temperature: 293.15 + - type: StaticPrice + price: 350 + - type: GenericVisualizer + visuals: + enum.ReleaseGasOnTriggerVisuals.Key: + enabled: + True: { state: active } + False: { state: spent } + # Non-explosive "dummy" grenades to use as a distraction. - type: entity diff --git a/Resources/Textures/Objects/Weapons/Grenades/airboom.rsi/active.png b/Resources/Textures/Objects/Weapons/Grenades/airboom.rsi/active.png new file mode 100644 index 0000000000..69d3caf891 Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Grenades/airboom.rsi/active.png differ diff --git a/Resources/Textures/Objects/Weapons/Grenades/airboom.rsi/equipped-BELT.png b/Resources/Textures/Objects/Weapons/Grenades/airboom.rsi/equipped-BELT.png new file mode 100644 index 0000000000..c0ec51c1cb Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Grenades/airboom.rsi/equipped-BELT.png differ diff --git a/Resources/Textures/Objects/Weapons/Grenades/airboom.rsi/icon.png b/Resources/Textures/Objects/Weapons/Grenades/airboom.rsi/icon.png new file mode 100644 index 0000000000..c3c7f6fbe0 Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Grenades/airboom.rsi/icon.png differ diff --git a/Resources/Textures/Objects/Weapons/Grenades/airboom.rsi/inhand-left.png b/Resources/Textures/Objects/Weapons/Grenades/airboom.rsi/inhand-left.png new file mode 100644 index 0000000000..3ba282d6c1 Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Grenades/airboom.rsi/inhand-left.png differ diff --git a/Resources/Textures/Objects/Weapons/Grenades/airboom.rsi/inhand-right.png b/Resources/Textures/Objects/Weapons/Grenades/airboom.rsi/inhand-right.png new file mode 100644 index 0000000000..84e4dc7578 Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Grenades/airboom.rsi/inhand-right.png differ diff --git a/Resources/Textures/Objects/Weapons/Grenades/airboom.rsi/meta.json b/Resources/Textures/Objects/Weapons/Grenades/airboom.rsi/meta.json new file mode 100644 index 0000000000..b2739f6b31 --- /dev/null +++ b/Resources/Textures/Objects/Weapons/Grenades/airboom.rsi/meta.json @@ -0,0 +1,44 @@ +{ + "version": 1, + "license": "CC0-1.0", + "copyright": "Created by EmoGarbage404 (github) for Space Station 14", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "primed" + }, + { + "name": "active", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "spent" + }, + { + "name": "equipped-BELT", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Objects/Weapons/Grenades/airboom.rsi/primed.png b/Resources/Textures/Objects/Weapons/Grenades/airboom.rsi/primed.png new file mode 100644 index 0000000000..7acc406dba Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Grenades/airboom.rsi/primed.png differ diff --git a/Resources/Textures/Objects/Weapons/Grenades/airboom.rsi/spent.png b/Resources/Textures/Objects/Weapons/Grenades/airboom.rsi/spent.png new file mode 100644 index 0000000000..c261eff5d1 Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Grenades/airboom.rsi/spent.png differ