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: 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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
|
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 |