private AtmosDirection ConductivityDirections(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile) { if (tile.Air == null) { return(AtmosDirection.All); } // TODO ATMOS check if this is correct return(AtmosDirection.All); }
public bool ConsiderSuperconductivity(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile) { if (tile.ThermalConductivity == 0f || !Superconduction) { return(false); } gridAtmosphere.SuperconductivityTiles.Add(tile); return(true); }
public void FinishSuperconduction(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile) { // Conduct with air on my tile if I have it if (tile.Air != null) { tile.Temperature = TemperatureShare(tile.Air, tile.ThermalConductivity, tile.Temperature, tile.HeatCapacity); } FinishSuperconduction(gridAtmosphere, tile, tile.Air?.Temperature ?? tile.Temperature); }
private void HotspotExpose(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, float exposedTemperature, float exposedVolume, bool soh = false) { if (tile.Air == null) { return; } var oxygen = tile.Air.GetMoles(Gas.Oxygen); if (oxygen < 0.5f) { return; } var plasma = tile.Air.GetMoles(Gas.Plasma); var tritium = tile.Air.GetMoles(Gas.Tritium); if (tile.Hotspot.Valid) { if (soh) { if (plasma > 0.5f || tritium > 0.5f) { if (tile.Hotspot.Temperature < exposedTemperature) { tile.Hotspot.Temperature = exposedTemperature; } if (tile.Hotspot.Volume < exposedVolume) { tile.Hotspot.Volume = exposedVolume; } } } return; } if ((exposedTemperature > Atmospherics.PlasmaMinimumBurnTemperature) && (plasma > 0.5f || tritium > 0.5f)) { tile.Hotspot = new Hotspot { Volume = exposedVolume * 25f, Temperature = exposedTemperature, SkippedFirstProcess = tile.CurrentCycle > gridAtmosphere.UpdateCounter, Valid = true, State = 1 }; AddActiveTile(gridAtmosphere, tile); gridAtmosphere.HotspotTiles.Add(tile); } }
public TileAtmosphere(GridAtmosphereComponent atmosphereComponent, GridId gridIndex, Vector2i gridIndices, GasMixture mixture = null, bool immutable = false) { IoCManager.InjectDependencies(this); _gridAtmosphereComponent = atmosphereComponent; _gridTileLookupSystem = _entityManager.EntitySysManager.GetEntitySystem<GridTileLookupSystem>(); GridIndex = gridIndex; GridIndices = gridIndices; Air = mixture; if(immutable) Air?.MarkImmutable(); }
public void Dispose() { if (_disposed) { return; } _disposed = true; _gridAtmosphereComponent.RemoveExcitedGroup(this); Dismantle(); _gridAtmosphereComponent = null; }
private void ExcitedGroupSelfBreakdown(GridAtmosphereComponent gridAtmosphere, ExcitedGroup excitedGroup) { DebugTools.Assert(!excitedGroup.Disposed, "Excited group is disposed!"); DebugTools.Assert(gridAtmosphere.ExcitedGroups.Contains(excitedGroup), "Grid Atmosphere does not contain Excited Group!"); var combined = new GasMixture(Atmospherics.CellVolume); var tileSize = excitedGroup.Tiles.Count; if (excitedGroup.Disposed) { return; } if (tileSize == 0) { ExcitedGroupDispose(gridAtmosphere, excitedGroup); return; } foreach (var tile in excitedGroup.Tiles) { if (tile?.Air == null) { continue; } Merge(combined, tile.Air); if (!ExcitedGroupsSpaceIsAllConsuming || !tile.Air.Immutable) { continue; } combined.Clear(); break; } combined.Multiply(1 / (float)tileSize); foreach (var tile in excitedGroup.Tiles) { if (tile?.Air == null) { continue; } tile.Air.CopyFromMutable(combined); InvalidateVisuals(tile.GridIndex, tile.GridIndices); } excitedGroup.BreakdownCooldown = 0; }
private AtmosDirection ConductivityDirections(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile) { if (tile.Air == null) { if (tile.ArchivedCycle < gridAtmosphere.UpdateCounter) { Archive(tile, gridAtmosphere.UpdateCounter); } return(AtmosDirection.All); } // TODO ATMOS check if this is correct return(AtmosDirection.All); }
private void ExcitedGroupDispose(GridAtmosphereComponent gridAtmosphere, ExcitedGroup excitedGroup) { if (excitedGroup.Disposed) { return; } DebugTools.Assert(gridAtmosphere.ExcitedGroups.Contains(excitedGroup), "Grid Atmosphere does not contain Excited Group!"); excitedGroup.Disposed = true; gridAtmosphere.ExcitedGroups.Remove(excitedGroup); ExcitedGroupDismantle(gridAtmosphere, excitedGroup, false); }
private void ExcitedGroupDismantle(GridAtmosphereComponent gridAtmosphere, ExcitedGroup excitedGroup, bool unexcite = true) { foreach (var tile in excitedGroup.Tiles) { tile.ExcitedGroup = null; if (!unexcite) { continue; } RemoveActiveTile(gridAtmosphere, tile); } excitedGroup.Tiles.Clear(); }
public bool ConsiderSuperconductivity(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, bool starting) { if (!Superconduction) { return(false); } if (tile.Air == null || tile.Air.Temperature < (starting ? Atmospherics.MinimumTemperatureStartSuperConduction : Atmospherics.MinimumTemperatureForSuperconduction)) { return(false); } return(!(GetHeatCapacity(tile.Air) < Atmospherics.MCellWithRatio) && ConsiderSuperconductivity(gridAtmosphere, tile)); }
private void HighPressureMovements(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile) { // TODO ATMOS finish this if (tile.PressureDifference > 15) { if (_spaceWindSoundCooldown == 0) { var coordinates = tile.GridIndices.ToEntityCoordinates(tile.GridIndex, _mapManager); if (!string.IsNullOrEmpty(SpaceWindSound)) { SoundSystem.Play(Filter.Pvs(coordinates), SpaceWindSound, coordinates, AudioHelpers.WithVariation(0.125f).WithVolume(MathHelper.Clamp(tile.PressureDifference / 10, 10, 100))); } } } foreach (var entity in Get <GridTileLookupSystem>().GetEntitiesIntersecting(tile.GridIndex, tile.GridIndices)) { if (!entity.TryGetComponent(out IPhysBody? physics) || !entity.IsMovedByPressure(out var pressure) || entity.IsInContainer()) { continue; } var pressureMovements = physics.Owner.EnsureComponent <MovedByPressureComponent>(); if (pressure.LastHighPressureMovementAirCycle < gridAtmosphere.UpdateCounter) { pressureMovements.ExperiencePressureDifference(gridAtmosphere.UpdateCounter, tile.PressureDifference, tile.PressureDirection, 0, tile.PressureSpecificTarget?.GridIndices.ToEntityCoordinates(tile.GridIndex, _mapManager) ?? EntityCoordinates.Invalid); } } if (tile.PressureDifference > 100) { // TODO ATMOS Do space wind graphics here! } _spaceWindSoundCooldown++; if (_spaceWindSoundCooldown > 75) { _spaceWindSoundCooldown = 0; } }
private void OnGridAtmosphereInit(EntityUid uid, GridAtmosphereComponent gridAtmosphere, ComponentInit args) { base.Initialize(); gridAtmosphere.Tiles.Clear(); if (!TryComp(uid, out IMapGridComponent? mapGrid)) { return; } if (gridAtmosphere.TilesUniqueMixes != null) { foreach (var(indices, mix) in gridAtmosphere.TilesUniqueMixes) { try { gridAtmosphere.Tiles.Add(indices, new TileAtmosphere(mapGrid.Owner, indices, gridAtmosphere.UniqueMixes ![mix].Clone()));
/// <summary> /// Makes a tile become inactive and stop processing. /// </summary> /// <param name="gridAtmosphere">Grid Atmosphere where to get the tile.</param> /// <param name="tile">Tile Atmosphere to be deactivated.</param> /// <param name="disposeExcitedGroup">Whether to dispose of the tile's <see cref="ExcitedGroup"/></param> private void RemoveActiveTile(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, bool disposeExcitedGroup = true) { tile.Excited = false; gridAtmosphere.ActiveTiles.Remove(tile); if (tile.ExcitedGroup == null) { return; } if (disposeExcitedGroup) { ExcitedGroupDispose(gridAtmosphere, tile.ExcitedGroup); } else { ExcitedGroupRemoveTile(tile.ExcitedGroup, tile); } }
private void HighPressureMovements(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile) { // 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 = tile.GridIndices.ToEntityCoordinates(tile.GridIndex, _mapManager); SoundSystem.Play(Filter.Pvs(coordinates), SpaceWindSound, coordinates, AudioHelpers.WithVariation(0.125f).WithVolume(MathHelper.Clamp(tile.PressureDifference / 10, 10, 100))); } } foreach (var entity in _lookup.GetEntitiesIntersecting(tile.GridIndex, tile.GridIndices)) { if (!HasComp <IPhysBody>(entity) || !entity.IsMovedByPressure(out var pressure) || entity.IsInContainer()) { continue; } var pressureMovements = EnsureComp <MovedByPressureComponent>(entity); if (pressure.LastHighPressureMovementAirCycle < gridAtmosphere.UpdateCounter) { pressureMovements.ExperiencePressureDifference(gridAtmosphere.UpdateCounter, tile.PressureDifference, tile.PressureDirection, 0, tile.PressureSpecificTarget?.GridIndices.ToEntityCoordinates(tile.GridIndex, _mapManager) ?? EntityCoordinates.Invalid); } } if (tile.PressureDifference > 100) { // TODO ATMOS Do space wind graphics here! } if (_spaceWindSoundCooldown++ > SpaceWindSoundCooldownCycles) { _spaceWindSoundCooldown = 0; } }
private bool TryRefreshTile(GridAtmosphereComponent gam, GasOverlayData oldTile, MapIndices indices, out GasOverlayData overlayData) { var tile = gam.GetTile(indices); if (tile == null) { overlayData = default; return(false); } var tileData = new List <GasData>(); for (byte i = 0; i < Atmospherics.TotalNumberOfGases; i++) { var gas = _atmosphereSystem.GetGas(i); var overlay = _atmosphereSystem.GetOverlay(i); if (overlay == null || tile?.Air == null) { continue; } var moles = tile.Air.Gases[i]; if (moles < gas.GasMolesVisible) { continue; } var data = new GasData(i, (byte)(MathHelper.Clamp01(moles / gas.GasMolesVisibleMax) * 255)); tileData.Add(data); } overlayData = new GasOverlayData(tile !.Hotspot.State, tile.Hotspot.Temperature, tileData.Count == 0 ? null : tileData.ToArray()); if (overlayData.Equals(oldTile)) { return(false); } return(true); }
private void ProcessHotspot(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile) { if (!tile.Hotspot.Valid) { gridAtmosphere.HotspotTiles.Remove(tile); return; } if (!tile.Excited) { AddActiveTile(gridAtmosphere, tile); } if (!tile.Hotspot.SkippedFirstProcess) { tile.Hotspot.SkippedFirstProcess = true; return; } if (tile.ExcitedGroup != null) { ExcitedGroupResetCooldowns(tile.ExcitedGroup); } if ((tile.Hotspot.Temperature < Atmospherics.FireMinimumTemperatureToExist) || (tile.Hotspot.Volume <= 1f) || tile.Air == null || tile.Air.GetMoles(Gas.Oxygen) < 0.5f || (tile.Air.GetMoles(Gas.Plasma) < 0.5f && tile.Air.GetMoles(Gas.Tritium) < 0.5f)) { tile.Hotspot = new Hotspot(); InvalidateVisuals(tile.GridIndex, tile.GridIndices); return; } PerformHotspotExposure(tile); if (tile.Hotspot.Bypassing) { tile.Hotspot.State = 3; // TODO ATMOS: Burn tile here if (tile.Air.Temperature > Atmospherics.FireMinimumTemperatureToSpread) { var radiatedTemperature = tile.Air.Temperature * Atmospherics.FireSpreadRadiosityScale; foreach (var otherTile in tile.AdjacentTiles) { // TODO ATMOS: This is sus. Suss this out. if (otherTile == null) { continue; } if (!otherTile.Hotspot.Valid) { HotspotExpose(gridAtmosphere, otherTile, radiatedTemperature, Atmospherics.CellVolume / 4); } } } } else { tile.Hotspot.State = (byte)(tile.Hotspot.Volume > Atmospherics.CellVolume * 0.4f ? 2 : 1); } if (tile.Hotspot.Temperature > tile.MaxFireTemperatureSustained) { tile.MaxFireTemperatureSustained = tile.Hotspot.Temperature; } if (_hotspotSoundCooldown++ == 0 && !string.IsNullOrEmpty(HotspotSound)) { var coordinates = tile.GridIndices.ToEntityCoordinates(tile.GridIndex, _mapManager); // A few details on the audio parameters for fire. // The greater the fire state, the lesser the pitch variation. // The greater the fire state, the greater the volume. SoundSystem.Play(Filter.Pvs(coordinates), HotspotSound, coordinates, AudioHelpers.WithVariation(0.15f / tile.Hotspot.State).WithVolume(-5f + 5f * tile.Hotspot.State)); } if (_hotspotSoundCooldown > HotspotSoundCooldownCycles) { _hotspotSoundCooldown = 0; } // TODO ATMOS Maybe destroy location here? }
public async Task AirConsistencyTest() { // --- Setup var options = new ServerContentIntegrationOption { ExtraPrototypes = Prototypes }; var server = StartServer(options); await server.WaitIdleAsync(); var mapLoader = server.ResolveDependency <IMapLoader>(); var mapManager = server.ResolveDependency <IMapManager>(); var entityManager = server.ResolveDependency <IEntityManager>(); RespiratorSystem respSys = default; MetabolizerSystem metaSys = default; MapId mapId; IMapGrid grid = null; SharedBodyComponent body = default; EntityUid human = default; GridAtmosphereComponent relevantAtmos = default; float startingMoles = 0.0f; var testMapName = "Maps/Test/Breathing/3by3-20oxy-80nit.yml"; await server.WaitPost(() => { mapId = mapManager.CreateMap(); grid = mapLoader.LoadBlueprint(mapId, testMapName); }); Assert.NotNull(grid, $"Test blueprint {testMapName} not found."); float GetMapMoles() { var totalMapMoles = 0.0f; foreach (var tile in relevantAtmos.Tiles.Values) { totalMapMoles += tile.Air?.TotalMoles ?? 0.0f; } return(totalMapMoles); } await server.WaitAssertion(() => { var coords = new Vector2(0.5f, -1f); var coordinates = new EntityCoordinates(grid.GridEntityId, coords); human = entityManager.SpawnEntity("HumanBodyDummy", coordinates); respSys = EntitySystem.Get <RespiratorSystem>(); metaSys = EntitySystem.Get <MetabolizerSystem>(); relevantAtmos = entityManager.GetComponent <GridAtmosphereComponent>(grid.GridEntityId); startingMoles = GetMapMoles(); Assert.True(entityManager.TryGetComponent(human, out body)); Assert.True(entityManager.HasComponent <RespiratorComponent>(human)); }); // --- End setup var inhaleCycles = 100; for (var i = 0; i < inhaleCycles; i++) { await server.WaitAssertion(() => { // inhale respSys.Update(2.0f); Assert.That(GetMapMoles(), Is.LessThan(startingMoles)); // metabolize + exhale metaSys.Update(1.0f); metaSys.Update(1.0f); respSys.Update(2.0f); Assert.That(GetMapMoles(), Is.EqualTo(startingMoles).Within(0.0001)); }); } await server.WaitIdleAsync(); }
private void ProcessCell(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, int fireCount) { // Can't process a tile without air if (tile.Air == null) { RemoveActiveTile(gridAtmosphere, tile); return; } if (tile.ArchivedCycle < fireCount) { Archive(tile, fireCount); } tile.CurrentCycle = fireCount; var adjacentTileLength = 0; for (var i = 0; i < Atmospherics.Directions; i++) { var direction = (AtmosDirection)(1 << i); if (tile.AdjacentBits.IsFlagSet(direction)) { adjacentTileLength++; } } for (var i = 0; i < Atmospherics.Directions; i++) { var direction = (AtmosDirection)(1 << i); if (!tile.AdjacentBits.IsFlagSet(direction)) { continue; } var enemyTile = tile.AdjacentTiles[i]; // If the tile is null or has no air, we don't do anything for it. if (enemyTile?.Air == null) { continue; } if (fireCount <= enemyTile.CurrentCycle) { continue; } Archive(enemyTile, fireCount); var shouldShareAir = false; if (ExcitedGroups && tile.ExcitedGroup != null && enemyTile.ExcitedGroup != null) { if (tile.ExcitedGroup != enemyTile.ExcitedGroup) { ExcitedGroupMerge(gridAtmosphere, tile.ExcitedGroup, enemyTile.ExcitedGroup); } shouldShareAir = true; } else if (tile.Air !.Compare(enemyTile.Air !) != GasMixture.GasCompareResult.NoExchange) { if (!enemyTile.Excited) { AddActiveTile(gridAtmosphere, enemyTile); } if (ExcitedGroups) { var excitedGroup = tile.ExcitedGroup; excitedGroup ??= enemyTile.ExcitedGroup; if (excitedGroup == null) { excitedGroup = new ExcitedGroup(); gridAtmosphere.ExcitedGroups.Add(excitedGroup); } if (tile.ExcitedGroup == null) { ExcitedGroupAddTile(excitedGroup, tile); } if (enemyTile.ExcitedGroup == null) { ExcitedGroupAddTile(excitedGroup, enemyTile); } } shouldShareAir = true; } if (shouldShareAir) { var difference = Share(tile.Air !, enemyTile.Air !, adjacentTileLength); if (SpaceWind) { if (difference > 0) { ConsiderPressureDifference(gridAtmosphere, tile, enemyTile, difference); } else { ConsiderPressureDifference(gridAtmosphere, enemyTile, tile, -difference); } } LastShareCheck(tile); } } if (tile.Air != null) { React(tile.Air, tile); } InvalidateVisuals(tile.GridIndex, tile.GridIndices); var remove = true; if (tile.Air !.Temperature > Atmospherics.MinimumTemperatureStartSuperConduction) { if (ConsiderSuperconductivity(gridAtmosphere, tile, true)) { remove = false; } } if (ExcitedGroups && tile.ExcitedGroup == null && remove) { RemoveActiveTile(gridAtmosphere, tile); } }
public void Initialize(GridAtmosphereComponent gridAtmosphereComponent) { _gridAtmosphereComponent = gridAtmosphereComponent; _gridAtmosphereComponent.AddExcitedGroup(this); }
private void ProcessHotspot(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile) { if (!tile.Hotspot.Valid) { gridAtmosphere.HotspotTiles.Remove(tile); return; } if (!tile.Excited) { AddActiveTile(gridAtmosphere, tile); } if (!tile.Hotspot.SkippedFirstProcess) { tile.Hotspot.SkippedFirstProcess = true; return; } if (tile.ExcitedGroup != null) { ExcitedGroupResetCooldowns(tile.ExcitedGroup); } if ((tile.Hotspot.Temperature < Atmospherics.FireMinimumTemperatureToExist) || (tile.Hotspot.Volume <= 1f) || tile.Air == null || tile.Air.GetMoles(Gas.Oxygen) < 0.5f || (tile.Air.GetMoles(Gas.Plasma) < 0.5f && tile.Air.GetMoles(Gas.Tritium) < 0.5f)) { tile.Hotspot = new Hotspot(); InvalidateVisuals(tile.GridIndex, tile.GridIndices); return; } PerformHotspotExposure(tile); if (tile.Hotspot.Bypassing) { tile.Hotspot.State = 3; // TODO ATMOS: Burn tile here if (tile.Air.Temperature > Atmospherics.FireMinimumTemperatureToSpread) { var radiatedTemperature = tile.Air.Temperature * Atmospherics.FireSpreadRadiosityScale; foreach (var otherTile in tile.AdjacentTiles) { // TODO ATMOS: This is sus. Suss this out. if (otherTile == null) { continue; } if (!otherTile.Hotspot.Valid) { HotspotExpose(gridAtmosphere, otherTile, radiatedTemperature, Atmospherics.CellVolume / 4); } } } } else { tile.Hotspot.State = (byte)(tile.Hotspot.Volume > Atmospherics.CellVolume * 0.4f ? 2 : 1); } if (tile.Hotspot.Temperature > tile.MaxFireTemperatureSustained) { tile.MaxFireTemperatureSustained = tile.Hotspot.Temperature; } // TODO ATMOS Maybe destroy location here? }