mirror of
https://github.com/WWhiteDreamProject/wwdpublic.git
synced 2026-04-29 19:47:48 +03:00
## Mirror of PR #22521: [Partial atmos refactor](https://github.com/space-wizards/space-station-14/pull/22521) from <img src="https://avatars.githubusercontent.com/u/10567778?v=4" alt="space-wizards" width="22"/> [space-wizards](https://github.com/space-wizards)/[space-station-14](https://github.com/space-wizards/space-station-14) ###### `18a35e7e83b2b71ee84b054d44d9ed5e595dd618` PR opened by <img src="https://avatars.githubusercontent.com/u/60421075?v=4" width="16"/><a href="https://github.com/ElectroJr"> ElectroJr</a> at 2023-12-15 03:45:42 UTC --- PR changed 43 files with 891 additions and 635 deletions. The PR had the following labels: - Status: Needs Review --- <details open="true"><summary><h1>Original Body</h1></summary> > This PR reworks how some parts of atmos code work. Originally it was just meant to be a performance and bugfix PR, but it has ballooned in scope. I'm not sure about some of my changes largely because I'm not sure if some things were an oversight or an intentional decision for some reason. > > List of changes: > - The `MolesArchived float[]` field is now read-only > - It simply gets zeroed whenever the `GasMixture` is set to null instead of constantly reallocating > - Airtight query information is now cached in `TileAtmosphere` > - This means that it should only iterate over anchored entities once per update. > - Previously an invalidated atmos tile would cause `ProcessRevalidate()` to query airtight entities on the same tile six times by calling a combination of `GridIsTileAirBlocked()`, `NeedsVacuumFixing()`, and `GridIsTileAirBlocked()`. So this should help significantly reduce component lookups & entity enumeration. > - This does change some behaviour. In particular blocked directions are now only updated if the tile was invalidated prior to the current atmos-update, and will only ever be updated once per atmos-update. > - AFAIK this only has an effect if the invalid tile processing is deferred over multiple ticks, and I don't think it should cause any issues? > - Fixes a potential bug, where tiles might not dispose of their excited group if their direction flags changed. > - `MapAtmosphereComponent.Mixture` is now always immutable and no longer nullable > - I'm not sure why the mixture was nullable before? AFAICT the component is meaningless if its null? > - Space "gas" was always immutable, but there was nothing that required planet atmospheres to be immutable. Requiring that it be immutable gets rid of the constant gas mixture cloning. > - I don't know if there was a reason for why they weren't immutable to begin with. > - Fixes lungs removing too much air from a gas mixture, resulting in negative moles. > - `GasMixture.Moles` is now `[Access]` restricted to the atmosphere system. > - This is to prevent people from improperly modifying the gas mixtures (e.g., lungs), or accidentally modifying immutable mixtures. > - Fixes an issue where non-grid atmosphere tiles would fail to update their adjacent tiles, resulting in null reference exception spam > - Fixes #21732 > - Fixes #21210 (probably) > - Disconnected atmosphere tiles, i.e., tiles that aren't on or adjacent to a grid tile, will now get removed from the tile set. Previously the tile set would just always increase, with tiles never getting removed. > - Removes various redundant component and tile-definition queries. > - Removes some method events in favour of just using methods. > - Map-exposded tiles now get updated when a map's atmosphere changes (or the grid moves across maps). > - Adds a `setmapatmos` command for adding map-wide atmospheres. > - Fixed (non-planet) map atmospheres rendering over grids. > > ## Media > > This PR also includes changes to the atmos debug overlay, though I've also split that off into a separate PR to make reviewing easier (#22520). > > Below is a video showing that atmos still seems to work, and that trimming of disconnected tiles works: > > https://github.com/space-wizards/space-station-14/assets/60421075/4da46992-19e6-4354-8ecd-3cd67be4d0ed > > For comparison, here is a video showing how current master works (disconnected tiles never get removed): > > https://github.com/space-wizards/space-station-14/assets/60421075/54590777-e11c-41dc-b49d-fd7e53bfeed7 > > 🆑 > - fix: Fixed a bug where partially airtight entities (e.g., thin windows or doors) could let air leak out into space. > </details> Co-authored-by: SimpleStation14 <Unknown>
254 lines
11 KiB
C#
254 lines
11 KiB
C#
using Content.Server.Atmos.Components;
|
|
using Content.Shared.Atmos;
|
|
using Content.Shared.Mobs.Components;
|
|
using Content.Shared.Physics;
|
|
using Robust.Shared.Audio;
|
|
using Robust.Shared.Map;
|
|
using Robust.Shared.Physics;
|
|
using Robust.Shared.Physics.Components;
|
|
using Robust.Shared.Random;
|
|
using Robust.Shared.Utility;
|
|
|
|
namespace Content.Server.Atmos.EntitySystems
|
|
{
|
|
public sealed partial class AtmosphereSystem
|
|
{
|
|
private const int SpaceWindSoundCooldownCycles = 75;
|
|
|
|
private int _spaceWindSoundCooldown = 0;
|
|
|
|
[ViewVariables(VVAccess.ReadWrite)]
|
|
public string? SpaceWindSound { get; private set; } = "/Audio/Effects/space_wind.ogg";
|
|
|
|
private readonly HashSet<Entity<MovedByPressureComponent>> _activePressures = new(8);
|
|
|
|
private void UpdateHighPressure(float frameTime)
|
|
{
|
|
var toRemove = new RemQueue<Entity<MovedByPressureComponent>>();
|
|
|
|
foreach (var ent in _activePressures)
|
|
{
|
|
var (uid, comp) = ent;
|
|
MetaDataComponent? metadata = null;
|
|
|
|
if (Deleted(uid, metadata))
|
|
{
|
|
toRemove.Add((uid, comp));
|
|
continue;
|
|
}
|
|
|
|
if (Paused(uid, metadata))
|
|
continue;
|
|
|
|
comp.Accumulator += frameTime;
|
|
|
|
if (comp.Accumulator < 2f)
|
|
continue;
|
|
|
|
// Reset it just for VV reasons even though it doesn't matter
|
|
comp.Accumulator = 0f;
|
|
toRemove.Add(ent);
|
|
|
|
if (HasComp<MobStateComponent>(uid) &&
|
|
TryComp<PhysicsComponent>(uid, out var body))
|
|
{
|
|
_physics.SetBodyStatus(body, BodyStatus.OnGround);
|
|
}
|
|
|
|
if (TryComp<FixturesComponent>(uid, out var fixtures))
|
|
{
|
|
foreach (var (id, fixture) in fixtures.Fixtures)
|
|
{
|
|
_physics.AddCollisionMask(uid, id, fixture, (int) CollisionGroup.TableLayer, manager: fixtures);
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (var comp in toRemove)
|
|
{
|
|
_activePressures.Remove(comp);
|
|
}
|
|
}
|
|
|
|
private void AddMobMovedByPressure(EntityUid uid, MovedByPressureComponent component, PhysicsComponent body)
|
|
{
|
|
if (!TryComp<FixturesComponent>(uid, out var fixtures))
|
|
return;
|
|
|
|
_physics.SetBodyStatus(body, BodyStatus.InAir);
|
|
|
|
foreach (var (id, fixture) in fixtures.Fixtures)
|
|
{
|
|
_physics.RemoveCollisionMask(uid, id, fixture, (int) CollisionGroup.TableLayer, manager: fixtures);
|
|
}
|
|
|
|
// TODO: Make them dynamic type? Ehh but they still want movement so uhh make it non-predicted like weightless?
|
|
// idk it's hard.
|
|
|
|
component.Accumulator = 0f;
|
|
_activePressures.Add((uid, component));
|
|
}
|
|
|
|
private void HighPressureMovements(Entity<GridAtmosphereComponent> gridAtmosphere, TileAtmosphere tile, EntityQuery<PhysicsComponent> bodies, EntityQuery<TransformComponent> xforms, EntityQuery<MovedByPressureComponent> pressureQuery, EntityQuery<MetaDataComponent> metas)
|
|
{
|
|
// TODO ATMOS finish this
|
|
|
|
// Don't play the space wind sound on tiles that are on fire...
|
|
if(tile.PressureDifference > 15 && !tile.Hotspot.Valid)
|
|
{
|
|
if(_spaceWindSoundCooldown == 0 && !string.IsNullOrEmpty(SpaceWindSound))
|
|
{
|
|
var coordinates = _mapSystem.ToCenterCoordinates(tile.GridIndex, tile.GridIndices);
|
|
_audio.PlayPvs(SpaceWindSound, coordinates, AudioParams.Default.WithVariation(0.125f).WithVolume(MathHelper.Clamp(tile.PressureDifference / 10, 10, 100)));
|
|
}
|
|
}
|
|
|
|
|
|
if (tile.PressureDifference > 100)
|
|
{
|
|
// TODO ATMOS Do space wind graphics here!
|
|
}
|
|
|
|
if (_spaceWindSoundCooldown++ > SpaceWindSoundCooldownCycles)
|
|
_spaceWindSoundCooldown = 0;
|
|
|
|
// No atmos yeets, return early.
|
|
if (!SpaceWind)
|
|
return;
|
|
|
|
// Used by ExperiencePressureDifference to correct push/throw directions from tile-relative to physics world.
|
|
var gridWorldRotation = xforms.GetComponent(gridAtmosphere).WorldRotation;
|
|
|
|
// If we're using monstermos, smooth out the yeet direction to follow the flow
|
|
if (MonstermosEqualization)
|
|
{
|
|
// We step through tiles according to the pressure direction on the current tile.
|
|
// The goal is to get a general direction of the airflow in the area.
|
|
// 3 is the magic number - enough to go around corners, but not U-turns.
|
|
var curTile = tile;
|
|
for (var i = 0; i < 3; i++)
|
|
{
|
|
if (curTile.PressureDirection == AtmosDirection.Invalid
|
|
|| !curTile.AdjacentBits.IsFlagSet(curTile.PressureDirection))
|
|
break;
|
|
curTile = curTile.AdjacentTiles[curTile.PressureDirection.ToIndex()]!;
|
|
}
|
|
|
|
if (curTile != tile)
|
|
tile.PressureSpecificTarget = curTile;
|
|
}
|
|
|
|
_entSet.Clear();
|
|
_lookup.GetLocalEntitiesIntersecting(tile.GridIndex, tile.GridIndices, _entSet, 0f);
|
|
|
|
foreach (var entity in _entSet)
|
|
{
|
|
// Ideally containers would have their own EntityQuery internally or something given recursively it may need to slam GetComp<T> anyway.
|
|
// Also, don't care about static bodies (but also due to collisionwakestate can't query dynamic directly atm).
|
|
if (!bodies.TryGetComponent(entity, out var body) ||
|
|
!pressureQuery.TryGetComponent(entity, out var pressure) ||
|
|
!pressure.Enabled)
|
|
continue;
|
|
|
|
if (_containers.IsEntityInContainer(entity, metas.GetComponent(entity))) continue;
|
|
|
|
var pressureMovements = EnsureComp<MovedByPressureComponent>(entity);
|
|
if (pressure.LastHighPressureMovementAirCycle < gridAtmosphere.Comp.UpdateCounter)
|
|
{
|
|
// tl;dr YEET
|
|
ExperiencePressureDifference(
|
|
(entity, pressureMovements),
|
|
gridAtmosphere.Comp.UpdateCounter,
|
|
tile.PressureDifference,
|
|
tile.PressureDirection, 0,
|
|
tile.PressureSpecificTarget != null ? _mapSystem.ToCenterCoordinates(tile.GridIndex, tile.PressureSpecificTarget.GridIndices) : EntityCoordinates.Invalid,
|
|
gridWorldRotation,
|
|
xforms.GetComponent(entity),
|
|
body);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Called from AtmosphereSystem.LINDA.cs with SpaceWind CVar check handled there.
|
|
private void ConsiderPressureDifference(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, AtmosDirection differenceDirection, float difference)
|
|
{
|
|
gridAtmosphere.HighPressureDelta.Add(tile);
|
|
|
|
if (difference <= tile.PressureDifference)
|
|
return;
|
|
|
|
tile.PressureDifference = difference;
|
|
tile.PressureDirection = differenceDirection;
|
|
}
|
|
|
|
public void ExperiencePressureDifference(
|
|
Entity<MovedByPressureComponent> ent,
|
|
int cycle,
|
|
float pressureDifference,
|
|
AtmosDirection direction,
|
|
float pressureResistanceProbDelta,
|
|
EntityCoordinates throwTarget,
|
|
Angle gridWorldRotation,
|
|
TransformComponent? xform = null,
|
|
PhysicsComponent? physics = null)
|
|
{
|
|
var (uid, component) = ent;
|
|
if (!Resolve(uid, ref physics, false))
|
|
return;
|
|
|
|
if (!Resolve(uid, ref xform))
|
|
return;
|
|
|
|
// TODO ATMOS stuns?
|
|
|
|
var maxForce = MathF.Sqrt(pressureDifference) * 2.25f;
|
|
var moveProb = 100f;
|
|
|
|
if (component.PressureResistance > 0)
|
|
moveProb = MathF.Abs((pressureDifference / component.PressureResistance * MovedByPressureComponent.ProbabilityBasePercent) -
|
|
MovedByPressureComponent.ProbabilityOffset);
|
|
|
|
// Can we yeet the thing (due to probability, strength, etc.)
|
|
if (moveProb > MovedByPressureComponent.ProbabilityOffset && _robustRandom.Prob(MathF.Min(moveProb / 100f, 1f))
|
|
&& !float.IsPositiveInfinity(component.MoveResist)
|
|
&& (physics.BodyType != BodyType.Static
|
|
&& (maxForce >= (component.MoveResist * MovedByPressureComponent.MoveForcePushRatio)))
|
|
|| (physics.BodyType == BodyType.Static && (maxForce >= (component.MoveResist * MovedByPressureComponent.MoveForceForcePushRatio))))
|
|
{
|
|
if (HasComp<MobStateComponent>(uid))
|
|
{
|
|
AddMobMovedByPressure(uid, component, physics);
|
|
}
|
|
|
|
if (maxForce > MovedByPressureComponent.ThrowForce)
|
|
{
|
|
var moveForce = maxForce;
|
|
moveForce /= (throwTarget != EntityCoordinates.Invalid) ? SpaceWindPressureForceDivisorThrow : SpaceWindPressureForceDivisorPush;
|
|
moveForce *= MathHelper.Clamp(moveProb, 0, 100);
|
|
|
|
// Apply a sanity clamp to prevent being thrown through objects.
|
|
var maxSafeForceForObject = SpaceWindMaxVelocity * physics.Mass;
|
|
moveForce = MathF.Min(moveForce, maxSafeForceForObject);
|
|
|
|
// Grid-rotation adjusted direction
|
|
var dirVec = (direction.ToAngle() + gridWorldRotation).ToWorldVec();
|
|
|
|
// TODO: Technically these directions won't be correct but uhh I'm just here for optimisations buddy not to fix my old bugs.
|
|
if (throwTarget != EntityCoordinates.Invalid)
|
|
{
|
|
var pos = ((throwTarget.ToMap(EntityManager).Position - xform.WorldPosition).Normalized() + dirVec).Normalized();
|
|
_physics.ApplyLinearImpulse(uid, pos * moveForce, body: physics);
|
|
}
|
|
else
|
|
{
|
|
moveForce = MathF.Min(moveForce, SpaceWindMaxPushForce);
|
|
_physics.ApplyLinearImpulse(uid, dirVec * moveForce, body: physics);
|
|
}
|
|
|
|
component.LastHighPressureMovementAirCycle = cycle;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|