Atmos Air Grenades (#37531)
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Content.Shared.Explosion.EntitySystems;
|
||||
|
||||
public abstract partial class SharedReleaseGasOnTriggerSystem : EntitySystem;
|
||||
|
||||
// I have dreams of Atmos in shared.
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
After Width: | Height: | Size: 905 B |
|
After Width: | Height: | Size: 229 B |
BIN
Resources/Textures/Objects/Weapons/Grenades/airboom.rsi/icon.png
Normal file
|
After Width: | Height: | Size: 306 B |
|
After Width: | Height: | Size: 363 B |
|
After Width: | Height: | Size: 378 B |
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 299 B |
|
After Width: | Height: | Size: 350 B |