Atmos Air Grenades (#37531)

This commit is contained in:
ArtisticRoomba
2025-05-17 21:32:51 -07:00
committed by GitHub
parent 9c791b66c6
commit 95cc36c41d
13 changed files with 268 additions and 0 deletions

View File

@@ -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;
/// <summary>
/// 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.
/// </summary>
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<ReleaseGasOnTriggerComponent, TriggerEvent>(OnTrigger);
}
/// <summary>
/// Shrimply sets the component to active when triggered, allowing it to release over time.
/// </summary>
private void OnTrigger(Entity<ReleaseGasOnTriggerComponent> 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<ReleaseGasOnTriggerComponent>();
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<ReleaseGasOnTriggerComponent>(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<ReleaseGasOnTriggerComponent>(uid);
continue;
}
if (comp.ExponentialRise)
UpdateExponentialRise(comp, comp.RemoveFraction);
}
}
/// <summary>
/// Updates the RemoveFraction for exponential rise.
/// </summary>
/// <remarks>See https://www.desmos.com/calculator/fx9gfrhoim</remarks>
private static void UpdateExponentialRise(ReleaseGasOnTriggerComponent comp, float baseFraction)
{
comp.TimesReleased++;
comp.RemoveFraction = 1f - MathF.Pow(1f - baseFraction, comp.TimesReleased);
}
private void UpdateAppearance(Entity<AppearanceComponent?> entity, bool state)
{
if (!Resolve(entity, ref entity.Comp, false))
return;
_appearance.SetData(entity, ReleaseGasOnTriggerComponent.ReleaseGasOnTriggerVisuals.Key, state);
}
}

View File

@@ -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;
/// <summary>
/// Contains a GasMixture that will release its contents to the atmosphere when triggered.
/// </summary>
[RegisterComponent, NetworkedComponent]
[AutoGenerateComponentPause]
[Access(typeof(SharedReleaseGasOnTriggerSystem))]
public sealed partial class ReleaseGasOnTriggerComponent : Component
{
/// <summary>
/// Represents visual states for whatever visuals that need to be applied
/// on state changes.
/// </summary>
[Serializable] [NetSerializable]
public enum ReleaseGasOnTriggerVisuals : byte
{
Key,
}
/// <summary>
/// Whether this grenade is active and releasing gas.
/// Set to true when triggered, which starts gas release.
/// </summary>
[DataField]
public bool Active;
/// <summary>
/// The gas mixture that will be released to the current tile atmosphere when triggered.
/// </summary>
[DataField]
public GasMixture Air;
/// <summary>
/// If true, the gas will be released in an exponential manner.
/// </summary>
[DataField]
public bool ExponentialRise;
/// <summary>
/// Time at which the next release will occur.
/// This is automatically set when the grenade activates.
/// </summary>
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
[AutoPausedField]
public TimeSpan NextReleaseTime = TimeSpan.Zero;
/// <summary>
/// The cap at which this grenade can fill the exposed atmosphere to.
/// The grenade automatically deletes itself when the pressure is reached.
/// </summary>
/// <example>If set to 101.325, the grenade will only fill the exposed
/// atmosphere up to 101.325 kPa.</example>
/// <remarks>If zero, this limit won't be respected.</remarks>
[DataField]
public float PressureLimit;
/// <summary>
/// How often the grenade will release gas.
/// </summary>
[DataField]
public TimeSpan ReleaseInterval = TimeSpan.FromSeconds(1);
/// <summary>
/// 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.
/// </summary>
/// <remarks>If undefined on the prototype, the entire molar amount will be transferred.</remarks>
[DataField]
public float RemoveFraction = 1;
/// <summary>
/// Stores the total moles initially in the grenade upon activation.
/// Used to calculate the moles released over time.
/// </summary>
/// <remarks>Set when the grenade is activated.</remarks>
[DataField(readOnly: true)]
public float StartingTotalMoles;
/// <summary>
/// Stores the number of times the grenade has been released,
/// for exponential rise calculations.
/// </summary>
[DataField]
public int TimesReleased;
}

View File

@@ -0,0 +1,5 @@
namespace Content.Shared.Explosion.EntitySystems;
public abstract partial class SharedReleaseGasOnTriggerSystem : EntitySystem;
// I have dreams of Atmos in shared.

View File

@@ -115,6 +115,7 @@
- id: HolofanProjector - id: HolofanProjector
- id: RCD - id: RCD
- id: RCDAmmo - id: RCDAmmo
- id: AirGrenade
- type: entity - type: entity
id: LockerAtmosphericsFilled id: LockerAtmosphericsFilled
@@ -131,6 +132,7 @@
- id: HolofanProjector - id: HolofanProjector
- id: RCD - id: RCD
- id: RCDAmmo - id: RCDAmmo
- id: AirGrenade
- type: entity - type: entity
id: LockerEngineerFilledHardsuit id: LockerEngineerFilledHardsuit

View File

@@ -461,6 +461,38 @@
- type: StaticPrice - type: StaticPrice
price: 350 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. # Non-explosive "dummy" grenades to use as a distraction.
- type: entity - type: entity

Binary file not shown.

After

Width:  |  Height:  |  Size: 905 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 B

View File

@@ -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
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 B