mirror of
https://github.com/DeltaV-Station/Delta-v.git
synced 2026-09-30 08:07:28 +00:00
* Fix the sensor monitoring console forcing a GC every 3 seconds (#38146) * Optimize sensor monitoring window graph drawing * Add shared static Vector2 pool for all GraphView instances * Address requested changes * remove lock * Update submodule to 264.0.0 (#38629) * Toy/Plushie Inhands and Wearables (#38514) * Fixed dependency injection and some other issues in a few places. * More issue fixes * patchfor broken NetworkConfiguratorLinkMenu (#38632) fix * Fix solutions flickering when transferring contents (#34838) * Use Solution clones when applying SolutionComponent states * Revert "Use Solution clones when applying SolutionComponent states" This reverts commit 013fd111cf92b22562e00f98a7aaa49bc4b4ed62. * Make Solution implement ICloneable and rename Clone method. * Copy CanReact value when cloning a Solution * Convert to IRobustCloneable * Fix typos in guidebook: Buisness → Business (#38636) * Fix typo in `MinorAntagonists.xml` Buisness → Business * Fix typo in `YourFirstCharacter.xml` Buisness → Business * Various Headphones Fixes and Tweaks (#38479) * sprites, wearables * neck * icon-on sprite * Cleanup prototype instantiation in `DamageTest` (#38639) Cleanup prototype instantiation in DamageTest * Cleanup prototype instantiation in `ExplosionSystem` (#38642) Cleanup prototype instantiation in ExplosionSystem * AddBodyPartCommand localization. (#38612) commit * fix ItemSlotsSystem debug assert (#38655) * Allow the Command & Super door remotes to use the access of their user. (Re-creation of PR due to changes to game balance) (#35536) * Added directional beacons (#38284) * Added directional beacons Signed-off-by: Nox38 <nebulousnox38@gmail.com> * Fixed names Signed-off-by: Nox38 <nebulousnox38@gmail.com> --------- Signed-off-by: Nox38 <nebulousnox38@gmail.com> * Power stat and nuke codes commands get some LEC love. (#38585) * commit * requested changes. * Dsay Dirty and Follow commands converted to LEC and localized. (#38666) * commit * whoopwhoopwhoop * Retro laser sprite fix (#38676) * Fixed everything except the icon Signed-off-by: Nox38 <nebulousnox38@gmail.com> * fixed icon Signed-off-by: Nox38 <nebulousnox38@gmail.com> --------- Signed-off-by: Nox38 <nebulousnox38@gmail.com> * fix water coolers (#38681) * Monochromacy typo fix (#38686) * fixes the typo * Fixed cloning looking for the trait, not the component, RE https://github.com/space-wizards/space-station-14/pull/38686#issuecomment-3025093504 * Apply suggestions from code review --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Validate `CloningSettingsPrototype`s (#38688) * Validate CloningSettingsPrototypes * Update Content.IntegrationTests/Tests/Cloning/CloningSettingsPrototypeTest.cs Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Check EventComponents too --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Add test of objective-related console commands (#36400) * Add test of objective add/list/remove commands * Not sure why we're validating test prototypes, but sure * We don't need a map * Improvements and fixups for New Status Effect API (#38660) * Predict healing and bloodstream (#38690) * initial commit * reapply 38126 * fix rootable * someone missed an important minus sign here * try this * fix * fix * reenable crit hits * cleanup * fix status time dirtying * fix * camelCase * Healing and bloodstream prediction fixes. * Resolving Wizard casting recall on nuke disk making it impossible to disarm (#38661) * Resolving Wizard Recall on Nuke disk making it impossible to disarm - Adding a DisarmBomb case to nuke status update loop - Changing a few methods and parameters to properly follow formatting standards - Updating some names to follow camelCase * Updating missed tag * Reverting DataField change Should prevent this preventative bugfix being a breaking change. * Switch HSV to the default colorspace for character customization (#38434) * Made HSV default for character editor * Adds/fixes comments to HSV defaulting * Added dropbox fix, potentially cursed * Revert "Added dropbox fix, potentially cursed" This reverts commit a709883366fbee813e839742125e70844672af29. --------- Co-authored-by: TrixxedHeart <46364955+TrixxedBit@users.noreply.github.com> * Fix: Don't deploy foldables when clicking on items inside containers (#38709) * Fix * Apply suggestions from code review --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Pressure Relief Valve (#36708) * initial system (this math is probably WRONG) * General code cleanup and OnExamined support (holy moly this code sucks) * UICode and related events foundation TODO: - Actually write the XAML UI and the underlying system - Un-shitcode the entire thing - Actually test everything... * Working UI code TODO: Make predicted, as this certainly isn't predicted. Even though I said it was. It isn't. * Remove one TODO for unshitcoding the examine code * Add reminder yea * Make predicted (defenitely isn't) (also defenitely isn't a copypaste from pressure pump code) * It's predicted! TODO: - Give it snazzy predicted visuals! - Have a different field for pressure entry, lest it gets bulldozed every UI update. * Improve gas pressure relief valve UI TODO: Reminder to reduce amount of dirties using deltafields * Implement DirtyField prediction * Entity<T> cleanup A lot of Entity<T> conversions and lukewarm cleanup. Also got caught copy pasting code in 4K UHD but it's not like you couldn't tell. * More cleanup and comments * Remove TODO comment on bulldozing window title * """refactoring""" - Move appearance out of shared and finally fix it. Pointless to predict appearance in this instance. - More Entity<T> conversions because I like them. - Move UI creation handling over entirely to the ActivatableUI system. - Fix a hardcoded locale string (why????). * Add visuals * Revert debugging variable replacememt yea * Revert skissue * Remove unused using directives and remove TODO * Localize, cleanup, document * Fix adminlogging discrepancy * Add ability to construct, add guidebook entry * Clear up comment * Add guidebook tooltip to valve * Convert GasPressureReliefValveBoundUserInterface declaration into primary constructor * Adds more input handling and adds autofill on open * Un-deepfry input validator shitcode Genuinely what was I smoking * improve visuals logic * Refactor again - Update math to the correct implementation - Moved code that could be re-used in the future into a helper method under AtmosphereSystem.Gases.cs * I'm sorry but I hate warnings * Remove unused using directive in AtmosphereSystem.Gases.cs * Review and cleanup * Lukewarm UI glossup * Maintainer for the upstream project btw * Remove redundant state sets and messy logic * Unduplicate valve updater code * Redo UI (im sorry Slarti) * run tests * Test refactored UI messaging * Second round of UI improvements - God please find a way to improve this system. Feels bad. * Update loop implementation * Further predict UI * Clear up SetToCurrentThreshold * cleanup * Update to master + pipe layers and bug fixes want to run tests * fixes * Deploy rename pipebomb * Documentation and requested changes * Rename the method that wiggled away * Undo rounding changes * Fix comment * Rename and cleanup * Apply suggestions from code review --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Fix TextLinkTag (#32203) * Adjust uplink buy button to be under item icon (#38596) * Adjusted uplink buy button to be under item icon * Put the discount subtext under the icon * Indent fixes, added margin --------- Co-authored-by: TrixxedHeart <46364955+TrixxedBit@users.noreply.github.com> * Scurrets - Audio Improvements (#38482) * Scurret audio tuning * Add new sfx * Update sneezing sfx * YAML support * Rename a folder * make telesci wreck easier (#37569) rel * Validate `ShaderPrototype` IDs (#38728) * Convert all shader prototype string literals to protoids in overlays * Convert more shader prototype literal strings to protoids * Convert ValidatePrototypeId to ProtoId * Later * [BUGFIX] Fixed revenant malfunction ability not working properly only MediBots and Stasis bed (#38664) * fixed * clean up * orks fix smart * review fix 1 * more requested changes * less cursed * more descriptive description * better wording * Tiny Tiny Cleanup of the EyeClosingSystem. (#38734) Update EyeClosingSystem.cs * Fix human skin tone distribution (#38701) * fix: Use PredictedQueueDel for gib spell (#38729) * fix: don't default to uncharged sprite state for cells (#38730) * Fix Hristov description - remove inaccurate technical specs (#38746) - Removes inaccurate 'armor piercing 14.5mm shells' reference - Replaces it with a more funny description, matching the style of the other snipers and guns - Keeps ammunition type '.60 anti-materiel ammo' specification - Fixes issue #38590 Co-authored-by: Arthur Fiorese de Andrade <aandrade@cmcxs.gov.br> * feat: allow mopping evaporating puddles (#38743) * Validate remaining `ProtoId` strings (#38747) Validate remaining ProtoId strings * Validate `ProtoId`s in tests (#38745) * Convert string literals to protoids in Content.Tests * Convert string literals to protoids or consts in Content.IntegrationTests * Fix linter failures Tricksy static using misled me * Cleanup warning in StomachSystem (#38748) you did not see this * Vox scars (#38592) * Added vox scars n'stuff, renamed vox_tattoos.ftl to just vox.ftl * Revert "Added vox scars n'stuff, renamed vox_tattoos.ftl to just vox.ftl" This reverts commit c73da55ba3b39ddf93b493aecd85604c54dd8a15. * locale key fix * Changed top surgery scar names to be more generalized * Adjusted face scars * Formatting fixes --------- Co-authored-by: TrixxedHeart <46364955+TrixxedBit@users.noreply.github.com> * UnlockNode command to LEC. (#38751) * commit * Update UnlockNodeCommand.cs * commit * move command locale to its own file. * Update Content.Server/Xenoarchaeology/Artifact/UnlockNodeCommand.cs --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Typofixes for figurine dialogue (#38737) * Typofixes for figurine dialogue * Forgot two * Janitor Tool: Wire Brush (#38667) * Wow! It's -brush- * spacing. * Update Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Update Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * fixed changes --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Minor escape menu UX improvements (#38650) * fix: spellbooks can have infinite charges (#38376) * fix: spellbooks can have infinite charges * refactor: indicate infinite spellbook charges with null Not sure if I like this much better...
731 lines
28 KiB
C#
731 lines
28 KiB
C#
using Content.Server.Atmos.Components;
|
|
using Content.Server.Atmos.Piping.Components;
|
|
using Content.Shared.Atmos;
|
|
using Content.Shared.Atmos.Components;
|
|
using Content.Shared.Maps;
|
|
using Robust.Shared.Map;
|
|
using Robust.Shared.Map.Components;
|
|
using Robust.Shared.Physics.Components;
|
|
using Robust.Shared.Timing;
|
|
using Robust.Shared.Utility;
|
|
|
|
namespace Content.Server.Atmos.EntitySystems
|
|
{
|
|
public sealed partial class AtmosphereSystem
|
|
{
|
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
|
|
|
private readonly Stopwatch _simulationStopwatch = new();
|
|
|
|
/// <summary>
|
|
/// Check current execution time every n instances processed.
|
|
/// </summary>
|
|
private const int LagCheckIterations = 30;
|
|
|
|
/// <summary>
|
|
/// Check current execution time every n instances processed.
|
|
/// </summary>
|
|
private const int InvalidCoordinatesLagCheckIterations = 50;
|
|
|
|
private int _currentRunAtmosphereIndex;
|
|
private bool _simulationPaused;
|
|
|
|
private TileAtmosphere GetOrNewTile(EntityUid owner, GridAtmosphereComponent atmosphere, Vector2i index, bool invalidateNew = true)
|
|
{
|
|
var tile = atmosphere.Tiles.GetOrNew(index, out var existing);
|
|
if (existing)
|
|
return tile;
|
|
|
|
if (invalidateNew)
|
|
atmosphere.InvalidatedCoords.Add(index);
|
|
|
|
tile.GridIndex = owner;
|
|
tile.GridIndices = index;
|
|
return tile;
|
|
}
|
|
|
|
private readonly List<Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent>> _currentRunAtmosphere = new();
|
|
|
|
/// <summary>
|
|
/// Revalidates all invalid coordinates in a grid atmosphere.
|
|
/// I.e., process any tiles that have had their airtight blockers modified.
|
|
/// </summary>
|
|
/// <param name="ent">The grid atmosphere in question.</param>
|
|
/// <returns>Whether the process succeeded or got paused due to time constrains.</returns>
|
|
private bool ProcessRevalidate(Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent)
|
|
{
|
|
if (ent.Comp4.MapUid == null)
|
|
{
|
|
Log.Error($"Attempted to process atmosphere on a map-less grid? Grid: {ToPrettyString(ent)}");
|
|
return true;
|
|
}
|
|
|
|
var (uid, atmosphere, visuals, grid, xform) = ent;
|
|
var volume = GetVolumeForTiles(grid);
|
|
TryComp(xform.MapUid, out MapAtmosphereComponent? mapAtmos);
|
|
|
|
if (!atmosphere.ProcessingPaused)
|
|
{
|
|
atmosphere.CurrentRunInvalidatedTiles.Clear();
|
|
atmosphere.CurrentRunInvalidatedTiles.EnsureCapacity(atmosphere.InvalidatedCoords.Count);
|
|
foreach (var indices in atmosphere.InvalidatedCoords)
|
|
{
|
|
var tile = GetOrNewTile(uid, atmosphere, indices, invalidateNew: false);
|
|
atmosphere.CurrentRunInvalidatedTiles.Enqueue(tile);
|
|
|
|
// Update tile.IsSpace and tile.MapAtmosphere, and tile.AirtightData.
|
|
UpdateTileData(ent, mapAtmos, tile);
|
|
}
|
|
atmosphere.InvalidatedCoords.Clear();
|
|
|
|
if (_simulationStopwatch.Elapsed.TotalMilliseconds >= AtmosMaxProcessTime)
|
|
return false;
|
|
}
|
|
|
|
var number = 0;
|
|
while (atmosphere.CurrentRunInvalidatedTiles.TryDequeue(out var tile))
|
|
{
|
|
DebugTools.Assert(atmosphere.Tiles.GetValueOrDefault(tile.GridIndices) == tile);
|
|
UpdateAdjacentTiles(ent, tile, activate: true);
|
|
UpdateTileAir(ent, tile, volume);
|
|
InvalidateVisuals(ent, tile);
|
|
|
|
if (number++ < InvalidCoordinatesLagCheckIterations)
|
|
continue;
|
|
|
|
number = 0;
|
|
// Process the rest next time.
|
|
if (_simulationStopwatch.Elapsed.TotalMilliseconds >= AtmosMaxProcessTime)
|
|
return false;
|
|
}
|
|
|
|
TrimDisconnectedMapTiles(ent);
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method queued a tile and all of its neighbours up for processing by <see cref="TrimDisconnectedMapTiles"/>.
|
|
/// </summary>
|
|
public void QueueTileTrim(GridAtmosphereComponent atmos, TileAtmosphere tile)
|
|
{
|
|
if (!tile.TrimQueued)
|
|
{
|
|
tile.TrimQueued = true;
|
|
atmos.PossiblyDisconnectedTiles.Add(tile);
|
|
}
|
|
|
|
for (var i = 0; i < Atmospherics.Directions; i++)
|
|
{
|
|
var direction = (AtmosDirection) (1 << i);
|
|
var indices = tile.GridIndices.Offset(direction);
|
|
if (atmos.Tiles.TryGetValue(indices, out var adj)
|
|
&& adj.NoGridTile
|
|
&& !adj.TrimQueued)
|
|
{
|
|
adj.TrimQueued = true;
|
|
atmos.PossiblyDisconnectedTiles.Add(adj);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tiles in a <see cref="GridAtmosphereComponent"/> are either grid-tiles, or they they should be are tiles
|
|
/// adjacent to grid-tiles that represent the map's atmosphere. This method trims any map-tiles that are no longer
|
|
/// adjacent to any grid-tiles.
|
|
/// </summary>
|
|
private void TrimDisconnectedMapTiles(
|
|
Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent)
|
|
{
|
|
var atmos = ent.Comp1;
|
|
|
|
foreach (var tile in atmos.PossiblyDisconnectedTiles)
|
|
{
|
|
tile.TrimQueued = false;
|
|
if (!tile.NoGridTile)
|
|
continue;
|
|
|
|
var connected = false;
|
|
for (var i = 0; i < Atmospherics.Directions; i++)
|
|
{
|
|
var indices = tile.GridIndices.Offset((AtmosDirection) (1 << i));
|
|
if (_map.TryGetTile(ent.Comp3, indices, out var gridTile) && !gridTile.IsEmpty)
|
|
{
|
|
connected = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!connected)
|
|
{
|
|
RemoveActiveTile(atmos, tile);
|
|
atmos.Tiles.Remove(tile.GridIndices);
|
|
}
|
|
}
|
|
|
|
atmos.PossiblyDisconnectedTiles.Clear();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks whether a tile has a corresponding grid-tile, or whether it is a "map" tile. Also checks whether the
|
|
/// tile should be considered "space"
|
|
/// </summary>
|
|
private void UpdateTileData(
|
|
Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent,
|
|
MapAtmosphereComponent? mapAtmos,
|
|
TileAtmosphere tile)
|
|
{
|
|
var idx = tile.GridIndices;
|
|
bool mapAtmosphere;
|
|
if (_map.TryGetTile(ent.Comp3, idx, out var gTile) && !gTile.IsEmpty)
|
|
{
|
|
var contentDef = (ContentTileDefinition) _tileDefinitionManager[gTile.TypeId];
|
|
mapAtmosphere = contentDef.MapAtmosphere;
|
|
tile.ThermalConductivity = contentDef.ThermalConductivity;
|
|
tile.HeatCapacity = contentDef.HeatCapacity;
|
|
tile.NoGridTile = false;
|
|
}
|
|
else
|
|
{
|
|
mapAtmosphere = true;
|
|
tile.ThermalConductivity = 0.5f;
|
|
tile.HeatCapacity = float.PositiveInfinity;
|
|
|
|
if (!tile.NoGridTile)
|
|
{
|
|
tile.NoGridTile = true;
|
|
|
|
// This tile just became a non-grid atmos tile.
|
|
// It, or one of its neighbours, might now be completely disconnected from the grid.
|
|
QueueTileTrim(ent.Comp1, tile);
|
|
}
|
|
}
|
|
|
|
UpdateAirtightData(ent.Owner, ent.Comp1, ent.Comp3, tile);
|
|
|
|
if (mapAtmosphere)
|
|
{
|
|
if (!tile.MapAtmosphere)
|
|
{
|
|
(tile.Air, tile.Space) = GetDefaultMapAtmosphere(mapAtmos);
|
|
tile.MapAtmosphere = true;
|
|
ent.Comp1.MapTiles.Add(tile);
|
|
}
|
|
|
|
DebugTools.AssertNotNull(tile.Air);
|
|
DebugTools.Assert(tile.Air?.Immutable ?? false);
|
|
return;
|
|
}
|
|
|
|
if (!tile.MapAtmosphere)
|
|
return;
|
|
|
|
// Tile used to be exposed to the map's atmosphere, but isn't anymore.
|
|
RemoveMapAtmos(ent.Comp1, tile);
|
|
}
|
|
|
|
private void RemoveMapAtmos(GridAtmosphereComponent atmos, TileAtmosphere tile)
|
|
{
|
|
DebugTools.Assert(tile.MapAtmosphere);
|
|
DebugTools.AssertNotNull(tile.Air);
|
|
DebugTools.Assert(tile.Air?.Immutable ?? false);
|
|
tile.MapAtmosphere = false;
|
|
atmos.MapTiles.Remove(tile);
|
|
tile.Air = null;
|
|
tile.AirArchived = null;
|
|
tile.ArchivedCycle = 0;
|
|
tile.LastShare = 0f;
|
|
tile.Space = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check whether a grid-tile should have an air mixture, and give it one if it doesn't already have one.
|
|
/// </summary>
|
|
private void UpdateTileAir(
|
|
Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent,
|
|
TileAtmosphere tile,
|
|
float volume)
|
|
{
|
|
if (tile.MapAtmosphere)
|
|
{
|
|
DebugTools.AssertNotNull(tile.Air);
|
|
DebugTools.Assert(tile.Air?.Immutable ?? false);
|
|
return;
|
|
}
|
|
|
|
var data = tile.AirtightData;
|
|
var fullyBlocked = data.BlockedDirections == AtmosDirection.All;
|
|
|
|
if (fullyBlocked && data.NoAirWhenBlocked)
|
|
{
|
|
if (tile.Air == null)
|
|
return;
|
|
|
|
tile.Air = null;
|
|
tile.AirArchived = null;
|
|
tile.ArchivedCycle = 0;
|
|
tile.LastShare = 0f;
|
|
tile.Hotspot = new Hotspot();
|
|
return;
|
|
}
|
|
|
|
if (tile.Air != null)
|
|
return;
|
|
|
|
tile.Air = new GasMixture(volume){Temperature = Atmospherics.T20C};
|
|
|
|
if (data.FixVacuum)
|
|
GridFixTileVacuum(tile);
|
|
}
|
|
|
|
private void QueueRunTiles(
|
|
Queue<TileAtmosphere> queue,
|
|
HashSet<TileAtmosphere> tiles)
|
|
{
|
|
|
|
queue.Clear();
|
|
queue.EnsureCapacity(tiles.Count);
|
|
foreach (var tile in tiles)
|
|
{
|
|
queue.Enqueue(tile);
|
|
}
|
|
}
|
|
|
|
private bool ProcessTileEqualize(Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent)
|
|
{
|
|
var atmosphere = ent.Comp1;
|
|
if (!atmosphere.ProcessingPaused)
|
|
QueueRunTiles(atmosphere.CurrentRunTiles, atmosphere.ActiveTiles);
|
|
|
|
var number = 0;
|
|
while (atmosphere.CurrentRunTiles.TryDequeue(out var tile))
|
|
{
|
|
EqualizePressureInZone(ent, tile, atmosphere.UpdateCounter);
|
|
|
|
if (number++ < LagCheckIterations)
|
|
continue;
|
|
|
|
number = 0;
|
|
// Process the rest next time.
|
|
if (_simulationStopwatch.Elapsed.TotalMilliseconds >= AtmosMaxProcessTime)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool ProcessActiveTiles(
|
|
Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent)
|
|
{
|
|
var atmosphere = ent.Comp1;
|
|
if(!atmosphere.ProcessingPaused)
|
|
QueueRunTiles(atmosphere.CurrentRunTiles, atmosphere.ActiveTiles);
|
|
|
|
var number = 0;
|
|
while (atmosphere.CurrentRunTiles.TryDequeue(out var tile))
|
|
{
|
|
ProcessCell(ent, tile, atmosphere.UpdateCounter);
|
|
|
|
if (number++ < LagCheckIterations)
|
|
continue;
|
|
|
|
number = 0;
|
|
// Process the rest next time.
|
|
if (_simulationStopwatch.Elapsed.TotalMilliseconds >= AtmosMaxProcessTime)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool ProcessExcitedGroups(
|
|
Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent)
|
|
{
|
|
var gridAtmosphere = ent.Comp1;
|
|
if (!gridAtmosphere.ProcessingPaused)
|
|
{
|
|
gridAtmosphere.CurrentRunExcitedGroups.Clear();
|
|
gridAtmosphere.CurrentRunExcitedGroups.EnsureCapacity(gridAtmosphere.ExcitedGroups.Count);
|
|
foreach (var group in gridAtmosphere.ExcitedGroups)
|
|
{
|
|
gridAtmosphere.CurrentRunExcitedGroups.Enqueue(group);
|
|
}
|
|
}
|
|
|
|
var number = 0;
|
|
while (gridAtmosphere.CurrentRunExcitedGroups.TryDequeue(out var excitedGroup))
|
|
{
|
|
excitedGroup.BreakdownCooldown++;
|
|
excitedGroup.DismantleCooldown++;
|
|
|
|
if (excitedGroup.BreakdownCooldown > Atmospherics.ExcitedGroupBreakdownCycles)
|
|
ExcitedGroupSelfBreakdown(ent, excitedGroup);
|
|
else if (excitedGroup.DismantleCooldown > Atmospherics.ExcitedGroupsDismantleCycles)
|
|
DeactivateGroupTiles(gridAtmosphere, excitedGroup);
|
|
// TODO ATMOS. What is the point of this? why is this only de-exciting the group? Shouldn't it also dismantle it?
|
|
|
|
if (number++ < LagCheckIterations)
|
|
continue;
|
|
|
|
number = 0;
|
|
// Process the rest next time.
|
|
if (_simulationStopwatch.Elapsed.TotalMilliseconds >= AtmosMaxProcessTime)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool ProcessHighPressureDelta(Entity<GridAtmosphereComponent> ent)
|
|
{
|
|
var atmosphere = ent.Comp;
|
|
if (!atmosphere.ProcessingPaused)
|
|
QueueRunTiles(atmosphere.CurrentRunTiles, atmosphere.HighPressureDelta);
|
|
|
|
// Note: This is still processed even if space wind is turned off since this handles playing the sounds.
|
|
|
|
var number = 0;
|
|
var bodies = GetEntityQuery<PhysicsComponent>();
|
|
var xforms = GetEntityQuery<TransformComponent>();
|
|
var metas = GetEntityQuery<MetaDataComponent>();
|
|
var pressureQuery = GetEntityQuery<MovedByPressureComponent>();
|
|
|
|
while (atmosphere.CurrentRunTiles.TryDequeue(out var tile))
|
|
{
|
|
HighPressureMovements(ent, tile, bodies, xforms, pressureQuery, metas);
|
|
tile.PressureDifference = 0f;
|
|
tile.LastPressureDirection = tile.PressureDirection;
|
|
tile.PressureDirection = AtmosDirection.Invalid;
|
|
tile.PressureSpecificTarget = null;
|
|
atmosphere.HighPressureDelta.Remove(tile);
|
|
|
|
if (number++ < LagCheckIterations)
|
|
continue;
|
|
number = 0;
|
|
// Process the rest next time.
|
|
if (_simulationStopwatch.Elapsed.TotalMilliseconds >= AtmosMaxProcessTime)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool ProcessHotspots(
|
|
Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent)
|
|
{
|
|
var atmosphere = ent.Comp1;
|
|
if(!atmosphere.ProcessingPaused)
|
|
QueueRunTiles(atmosphere.CurrentRunTiles, atmosphere.HotspotTiles);
|
|
|
|
var number = 0;
|
|
while (atmosphere.CurrentRunTiles.TryDequeue(out var hotspot))
|
|
{
|
|
ProcessHotspot(ent, hotspot);
|
|
|
|
if (number++ < LagCheckIterations)
|
|
continue;
|
|
|
|
number = 0;
|
|
// Process the rest next time.
|
|
if (_simulationStopwatch.Elapsed.TotalMilliseconds >= AtmosMaxProcessTime)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool ProcessSuperconductivity(GridAtmosphereComponent atmosphere)
|
|
{
|
|
if(!atmosphere.ProcessingPaused)
|
|
QueueRunTiles(atmosphere.CurrentRunTiles, atmosphere.SuperconductivityTiles);
|
|
|
|
var number = 0;
|
|
while (atmosphere.CurrentRunTiles.TryDequeue(out var superconductivity))
|
|
{
|
|
Superconduct(atmosphere, superconductivity);
|
|
|
|
if (number++ < LagCheckIterations)
|
|
continue;
|
|
|
|
number = 0;
|
|
// Process the rest next time.
|
|
if (_simulationStopwatch.Elapsed.TotalMilliseconds >= AtmosMaxProcessTime)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool ProcessPipeNets(GridAtmosphereComponent atmosphere)
|
|
{
|
|
if (!atmosphere.ProcessingPaused)
|
|
{
|
|
atmosphere.CurrentRunPipeNet.Clear();
|
|
atmosphere.CurrentRunPipeNet.EnsureCapacity(atmosphere.PipeNets.Count);
|
|
foreach (var net in atmosphere.PipeNets)
|
|
{
|
|
atmosphere.CurrentRunPipeNet.Enqueue(net);
|
|
}
|
|
}
|
|
|
|
var number = 0;
|
|
while (atmosphere.CurrentRunPipeNet.TryDequeue(out var pipenet))
|
|
{
|
|
pipenet.Update();
|
|
|
|
if (number++ < LagCheckIterations)
|
|
continue;
|
|
|
|
number = 0;
|
|
// Process the rest next time.
|
|
if (_simulationStopwatch.Elapsed.TotalMilliseconds >= AtmosMaxProcessTime)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* UpdateProcessing() takes a different number of calls to go through all of atmos
|
|
* processing depending on what options are enabled. This returns the actual effective time
|
|
* between atmos updates that devices actually experience.
|
|
*/
|
|
public float RealAtmosTime()
|
|
{
|
|
int num = (int)AtmosphereProcessingState.NumStates;
|
|
if (!MonstermosEqualization)
|
|
num--;
|
|
if (!ExcitedGroups)
|
|
num--;
|
|
if (!Superconduction)
|
|
num--;
|
|
return num * AtmosTime;
|
|
}
|
|
|
|
private bool ProcessAtmosDevices(
|
|
Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent,
|
|
Entity<MapAtmosphereComponent?> map)
|
|
{
|
|
var atmosphere = ent.Comp1;
|
|
if (!atmosphere.ProcessingPaused)
|
|
{
|
|
atmosphere.CurrentRunAtmosDevices.Clear();
|
|
atmosphere.CurrentRunAtmosDevices.EnsureCapacity(atmosphere.AtmosDevices.Count);
|
|
foreach (var device in atmosphere.AtmosDevices)
|
|
{
|
|
atmosphere.CurrentRunAtmosDevices.Enqueue(device);
|
|
}
|
|
}
|
|
|
|
var time = _gameTiming.CurTime;
|
|
var number = 0;
|
|
var ev = new AtmosDeviceUpdateEvent(RealAtmosTime(), (ent, ent.Comp1, ent.Comp2), map);
|
|
while (atmosphere.CurrentRunAtmosDevices.TryDequeue(out var device))
|
|
{
|
|
RaiseLocalEvent(device, ref ev);
|
|
device.Comp.LastProcess = time;
|
|
|
|
if (number++ < LagCheckIterations)
|
|
continue;
|
|
|
|
number = 0;
|
|
// Process the rest next time.
|
|
if (_simulationStopwatch.Elapsed.TotalMilliseconds >= AtmosMaxProcessTime)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private void UpdateProcessing(float frameTime)
|
|
{
|
|
_simulationStopwatch.Restart();
|
|
|
|
if (!_simulationPaused)
|
|
{
|
|
_currentRunAtmosphereIndex = 0;
|
|
_currentRunAtmosphere.Clear();
|
|
|
|
var query = EntityQueryEnumerator<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent>();
|
|
while (query.MoveNext(out var uid, out var atmos, out var overlay, out var grid, out var xform ))
|
|
{
|
|
_currentRunAtmosphere.Add((uid, atmos, overlay, grid, xform));
|
|
}
|
|
}
|
|
|
|
// We set this to true just in case we have to stop processing due to time constraints.
|
|
_simulationPaused = true;
|
|
|
|
for (; _currentRunAtmosphereIndex < _currentRunAtmosphere.Count; _currentRunAtmosphereIndex++)
|
|
{
|
|
var ent = _currentRunAtmosphere[_currentRunAtmosphereIndex];
|
|
var (owner, atmosphere, visuals, grid, xform) = ent;
|
|
|
|
if (xform.MapUid == null
|
|
|| TerminatingOrDeleted(xform.MapUid.Value)
|
|
|| xform.MapID == MapId.Nullspace)
|
|
{
|
|
Log.Error($"Attempted to process atmos without a map? Entity: {ToPrettyString(owner)}. Map: {ToPrettyString(xform?.MapUid)}. MapId: {xform?.MapID}");
|
|
continue;
|
|
}
|
|
|
|
if (atmosphere.LifeStage >= ComponentLifeStage.Stopping || Paused(owner) || !atmosphere.Simulated)
|
|
continue;
|
|
|
|
atmosphere.Timer += frameTime;
|
|
|
|
if (atmosphere.Timer < AtmosTime)
|
|
continue;
|
|
|
|
// We subtract it so it takes lost time into account.
|
|
atmosphere.Timer -= AtmosTime;
|
|
|
|
var map = new Entity<MapAtmosphereComponent?>(xform.MapUid.Value, _mapAtmosQuery.CompOrNull(xform.MapUid.Value));
|
|
|
|
switch (atmosphere.State)
|
|
{
|
|
case AtmosphereProcessingState.Revalidate:
|
|
if (!ProcessRevalidate(ent))
|
|
{
|
|
atmosphere.ProcessingPaused = true;
|
|
return;
|
|
}
|
|
|
|
atmosphere.ProcessingPaused = false;
|
|
|
|
// Next state depends on whether monstermos equalization is enabled or not.
|
|
// Note: We do this here instead of on the tile equalization step to prevent ending it early.
|
|
// Therefore, a change to this CVar might only be applied after that step is over.
|
|
atmosphere.State = MonstermosEqualization
|
|
? AtmosphereProcessingState.TileEqualize
|
|
: AtmosphereProcessingState.ActiveTiles;
|
|
continue;
|
|
case AtmosphereProcessingState.TileEqualize:
|
|
if (!ProcessTileEqualize(ent))
|
|
{
|
|
atmosphere.ProcessingPaused = true;
|
|
return;
|
|
}
|
|
|
|
atmosphere.ProcessingPaused = false;
|
|
atmosphere.State = AtmosphereProcessingState.ActiveTiles;
|
|
continue;
|
|
case AtmosphereProcessingState.ActiveTiles:
|
|
if (!ProcessActiveTiles(ent))
|
|
{
|
|
atmosphere.ProcessingPaused = true;
|
|
return;
|
|
}
|
|
|
|
atmosphere.ProcessingPaused = false;
|
|
// Next state depends on whether excited groups are enabled or not.
|
|
atmosphere.State = ExcitedGroups ? AtmosphereProcessingState.ExcitedGroups : AtmosphereProcessingState.HighPressureDelta;
|
|
continue;
|
|
case AtmosphereProcessingState.ExcitedGroups:
|
|
if (!ProcessExcitedGroups(ent))
|
|
{
|
|
atmosphere.ProcessingPaused = true;
|
|
return;
|
|
}
|
|
|
|
atmosphere.ProcessingPaused = false;
|
|
atmosphere.State = AtmosphereProcessingState.HighPressureDelta;
|
|
continue;
|
|
case AtmosphereProcessingState.HighPressureDelta:
|
|
if (!ProcessHighPressureDelta((ent, ent)))
|
|
{
|
|
atmosphere.ProcessingPaused = true;
|
|
return;
|
|
}
|
|
|
|
atmosphere.ProcessingPaused = false;
|
|
atmosphere.State = AtmosphereProcessingState.Hotspots;
|
|
continue;
|
|
case AtmosphereProcessingState.Hotspots:
|
|
if (!ProcessHotspots(ent))
|
|
{
|
|
atmosphere.ProcessingPaused = true;
|
|
return;
|
|
}
|
|
|
|
atmosphere.ProcessingPaused = false;
|
|
// Next state depends on whether superconduction is enabled or not.
|
|
// Note: We do this here instead of on the tile equalization step to prevent ending it early.
|
|
// Therefore, a change to this CVar might only be applied after that step is over.
|
|
atmosphere.State = Superconduction
|
|
? AtmosphereProcessingState.Superconductivity
|
|
: AtmosphereProcessingState.PipeNet;
|
|
continue;
|
|
case AtmosphereProcessingState.Superconductivity:
|
|
if (!ProcessSuperconductivity(atmosphere))
|
|
{
|
|
atmosphere.ProcessingPaused = true;
|
|
return;
|
|
}
|
|
|
|
atmosphere.ProcessingPaused = false;
|
|
atmosphere.State = AtmosphereProcessingState.PipeNet;
|
|
continue;
|
|
case AtmosphereProcessingState.PipeNet:
|
|
if (!ProcessPipeNets(atmosphere))
|
|
{
|
|
atmosphere.ProcessingPaused = true;
|
|
return;
|
|
}
|
|
|
|
atmosphere.ProcessingPaused = false;
|
|
atmosphere.State = AtmosphereProcessingState.AtmosDevices;
|
|
continue;
|
|
case AtmosphereProcessingState.AtmosDevices:
|
|
if (!ProcessAtmosDevices(ent, map))
|
|
{
|
|
atmosphere.ProcessingPaused = true;
|
|
return;
|
|
}
|
|
|
|
atmosphere.ProcessingPaused = false;
|
|
atmosphere.State = AtmosphereProcessingState.Revalidate;
|
|
|
|
// We reached the end of this atmosphere's update tick. Break out of the switch.
|
|
break;
|
|
}
|
|
|
|
// And increase the update counter.
|
|
atmosphere.UpdateCounter++;
|
|
}
|
|
|
|
// We finished processing all atmospheres successfully, therefore we won't be paused next tick.
|
|
_simulationPaused = false;
|
|
}
|
|
}
|
|
|
|
public enum AtmosphereProcessingState : byte
|
|
{
|
|
Revalidate,
|
|
TileEqualize,
|
|
ActiveTiles,
|
|
ExcitedGroups,
|
|
HighPressureDelta,
|
|
Hotspots,
|
|
Superconductivity,
|
|
PipeNet,
|
|
AtmosDevices,
|
|
NumStates
|
|
}
|
|
}
|