diff --git a/Content.Server/Atmos/Components/MovedByPressureComponent.cs b/Content.Server/Atmos/Components/MovedByPressureComponent.cs index b92d2bd3ea..bae12d67bd 100644 --- a/Content.Server/Atmos/Components/MovedByPressureComponent.cs +++ b/Content.Server/Atmos/Components/MovedByPressureComponent.cs @@ -1,30 +1,22 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using Content.Shared.Atmos; -using Content.Shared.MobState.Components; -using Content.Shared.Physics; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Map; -using Robust.Shared.Maths; -using Robust.Shared.Physics; -using Robust.Shared.Random; -using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.ViewVariables; +using Content.Server.Atmos.EntitySystems; namespace Content.Server.Atmos.Components { + // Unfortunately can't be friends yet due to magboots. [RegisterComponent] public sealed class MovedByPressureComponent : Component { - [Dependency] private readonly IRobustRandom _robustRandom = default!; - [Dependency] private readonly IEntityManager _entMan = default!; + public const float MoveForcePushRatio = 1f; + public const float MoveForceForcePushRatio = 1f; + public const float ProbabilityOffset = 25f; + public const float ProbabilityBasePercent = 10f; + public const float ThrowForce = 100f; - private const float MoveForcePushRatio = 1f; - private const float MoveForceForcePushRatio = 1f; - private const float ProbabilityOffset = 25f; - private const float ProbabilityBasePercent = 10f; - private const float ThrowForce = 100f; + /// + /// Accumulates time when yeeted by high pressure deltas. + /// + [ViewVariables] + public float Accumulator = 0f; [ViewVariables(VVAccess.ReadWrite)] [DataField("enabled")] @@ -37,75 +29,5 @@ namespace Content.Server.Atmos.Components public float MoveResist { get; set; } = 100f; [ViewVariables(VVAccess.ReadWrite)] public int LastHighPressureMovementAirCycle { get; set; } = 0; - - public void ExperiencePressureDifference(int cycle, float pressureDifference, AtmosDirection direction, - float pressureResistanceProbDelta, EntityCoordinates throwTarget) - { - if (!_entMan.TryGetComponent(Owner, out PhysicsComponent? physics)) - return; - if (!_entMan.TryGetComponent(Owner, out FixturesComponent? fixtureComponent)) - return; - - // TODO ATMOS stuns? - - var transform = _entMan.GetComponent(physics.Owner); - var maxForce = MathF.Sqrt(pressureDifference) * 2.25f; - var moveProb = 100f; - - if (PressureResistance > 0) - moveProb = MathF.Abs((pressureDifference / PressureResistance * ProbabilityBasePercent) - - ProbabilityOffset); - - if (moveProb > ProbabilityOffset && _robustRandom.Prob(MathF.Min(moveProb / 100f, 1f)) - && !float.IsPositiveInfinity(MoveResist) - && (physics.BodyType != BodyType.Static - && (maxForce >= (MoveResist * MoveForcePushRatio))) - || (physics.BodyType == BodyType.Static && (maxForce >= (MoveResist * MoveForceForcePushRatio)))) - { - if (_entMan.HasComponent(physics.Owner)) - { - physics.BodyStatus = BodyStatus.InAir; - foreach (var fixture in fixtureComponent.Fixtures.Values) - { - fixture.CollisionMask &= ~(int) CollisionGroup.VaultImpassable; - } - - Owner.SpawnTimer(2000, () => - { - if (Deleted || !_entMan.TryGetComponent(Owner, out PhysicsComponent? physicsComponent)) return; - - // Uhh if you get race conditions good luck buddy. - if (_entMan.HasComponent(physicsComponent.Owner)) - { - physicsComponent.BodyStatus = BodyStatus.OnGround; - } - - foreach (var fixture in physics.Fixtures) - { - fixture.CollisionMask |= (int) CollisionGroup.VaultImpassable; - } - }); - } - - if (maxForce > ThrowForce) - { - // Vera please fix ;-; - if (throwTarget != EntityCoordinates.Invalid) - { - var moveForce = maxForce * MathHelper.Clamp(moveProb, 0, 100) / 15f; - var pos = ((throwTarget.Position - transform.Coordinates.Position).Normalized + direction.ToDirection().ToVec()).Normalized; - physics.ApplyLinearImpulse(pos * moveForce); - } - - else - { - var moveForce = MathF.Min(maxForce * MathHelper.Clamp(moveProb, 0, 100) / 2500f, 20f); - physics.ApplyLinearImpulse(direction.ToDirection().ToVec() * moveForce); - } - - LastHighPressureMovementAirCycle = cycle; - } - } - } } } diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Grid.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Grid.cs index 23331cf089..4c6746dfc7 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Grid.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Grid.cs @@ -1493,6 +1493,11 @@ namespace Content.Server.Atmos.EntitySystems #region Position Helpers + private TileRef? GetTile(TileAtmosphere tile) + { + return tile.GridIndices.GetTileRef(tile.GridIndex, _mapManager); + } + public bool TryGetGridAndTile(MapCoordinates coordinates, [NotNullWhen(true)] out (GridId Grid, Vector2i Tile)? tuple) { if (!_mapManager.TryFindGridAt(coordinates, out var grid)) diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.HighPressureDelta.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.HighPressureDelta.cs index 5d8382500d..4a05623129 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.HighPressureDelta.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.HighPressureDelta.cs @@ -1,11 +1,16 @@ using Content.Server.Atmos.Components; using Content.Shared.Atmos; using Content.Shared.Audio; +using Content.Shared.MobState.Components; +using Content.Shared.Physics; using Robust.Shared.Audio; using Robust.Shared.GameObjects; using Robust.Shared.Map; using Robust.Shared.Maths; +using Robust.Shared.Physics; using Robust.Shared.Player; +using Robust.Shared.Random; +using Robust.Shared.Utility; using Robust.Shared.ViewVariables; namespace Content.Server.Atmos.EntitySystems @@ -19,6 +24,71 @@ namespace Content.Server.Atmos.EntitySystems [ViewVariables(VVAccess.ReadWrite)] public string? SpaceWindSound { get; private set; } = "/Audio/Effects/space_wind.ogg"; + private HashSet _activePressures = new(8); + + private void UpdateHighPressure(float frameTime) + { + var toRemove = new RemQueue(); + + foreach (var comp in _activePressures) + { + MetaDataComponent? metadata = null; + + if (Deleted(comp.Owner, metadata)) + { + toRemove.Add(comp); + continue; + } + + if (Paused(comp.Owner, 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(comp); + + if (HasComp(comp.Owner) && + TryComp(comp.Owner, out var body)) + { + body.BodyStatus = BodyStatus.OnGround; + } + + if (TryComp(comp.Owner, out var fixtures)) + { + foreach (var (_, fixture) in fixtures.Fixtures) + { + _physics.AddCollisionMask(fixtures, fixture, (int) CollisionGroup.VaultImpassable); + } + } + } + + foreach (var comp in toRemove) + { + _activePressures.Remove(comp); + } + } + + private void AddMobMovedByPressure(MovedByPressureComponent component, PhysicsComponent body) + { + if (!TryComp(component.Owner, out var fixtures)) return; + + body.BodyStatus = BodyStatus.InAir; + + foreach (var fixture in fixtures.Fixtures.Values) + { + _physics.RemoveCollisionMask(fixtures, fixture, (int) CollisionGroup.VaultImpassable); + } + + // 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(component); + } + private void HighPressureMovements(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, EntityQuery bodies, EntityQuery xforms, EntityQuery pressureQuery) { // TODO ATMOS finish this @@ -37,15 +107,28 @@ namespace Content.Server.Atmos.EntitySystems foreach (var entity in _lookup.GetEntitiesIntersecting(tile.GridIndex, tile.GridIndices)) { // Ideally containers would have their own EntityQuery internally or something given recursively it may need to slam GetComp anyway. - if (!bodies.HasComponent(entity) - || !pressureQuery.TryGetComponent(entity, out var pressure) || !pressure.Enabled - || _containers.IsEntityInContainer(entity, xforms.GetComponent(entity))) + // 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; + var xform = xforms.GetComponent(entity); + + if (_containers.IsEntityInContainer(entity, xform)) continue; + var pressureMovements = EnsureComp(entity); if (pressure.LastHighPressureMovementAirCycle < gridAtmosphere.UpdateCounter) { - pressureMovements.ExperiencePressureDifference(gridAtmosphere.UpdateCounter, tile.PressureDifference, tile.PressureDirection, 0, tile.PressureSpecificTarget?.GridIndices.ToEntityCoordinates(tile.GridIndex, _mapManager) ?? EntityCoordinates.Invalid); + // tl;dr YEET + ExperiencePressureDifference( + pressureMovements, + gridAtmosphere.UpdateCounter, + tile.PressureDifference, + tile.PressureDirection, 0, + tile.PressureSpecificTarget?.GridIndices.ToEntityCoordinates(tile.GridIndex, _mapManager) ?? EntityCoordinates.Invalid, + xform, + body); } } @@ -68,5 +151,62 @@ namespace Content.Server.Atmos.EntitySystems tile.PressureDirection = (tile.GridIndices - other.GridIndices).GetDir().ToAtmosDirection(); } } + + public void ExperiencePressureDifference( + MovedByPressureComponent component, + int cycle, + float pressureDifference, + AtmosDirection direction, + float pressureResistanceProbDelta, + EntityCoordinates throwTarget, + TransformComponent? xform = null, + PhysicsComponent? physics = null) + { + if (!Resolve(component.Owner, ref physics, false)) + return; + + if (!Resolve(component.Owner, 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(physics.Owner)) + { + AddMobMovedByPressure(component, physics); + } + + if (maxForce > MovedByPressureComponent.ThrowForce) + { + // 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 moveForce = maxForce * MathHelper.Clamp(moveProb, 0, 100) / 15f; + var pos = ((throwTarget.Position - xform.Coordinates.Position).Normalized + direction.ToDirection().ToVec()).Normalized; + physics.ApplyLinearImpulse(pos * moveForce); + } + + else + { + var moveForce = MathF.Min(maxForce * MathHelper.Clamp(moveProb, 0, 100) / 2500f, 20f); + physics.ApplyLinearImpulse(direction.ToDirection().ToVec() * moveForce); + } + + component.LastHighPressureMovementAirCycle = cycle; + } + } + } } } diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Hotspot.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Hotspot.cs index 6fbb47c574..9e78f4acb6 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Hotspot.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Hotspot.cs @@ -170,7 +170,7 @@ namespace Content.Server.Atmos.EntitySystems foreach (var entity in _lookup.GetEntitiesIntersecting(tile.GridIndex, tile.GridIndices)) { - RaiseLocalEvent(entity, fireEvent, false); + RaiseLocalEvent(entity, ref fireEvent, false); } } } diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs index bb13f8a423..bae1c044b8 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs @@ -112,7 +112,7 @@ namespace Content.Server.Atmos.EntitySystems AddActiveTile(atmosphere, tile); // TODO ATMOS: Query all the contents of this tile (like walls) and calculate the correct thermal conductivity and heat capacity - var tileDef = tile.Tile?.Tile.GetContentTileDefinition(); + var tileDef = GetTile(tile)?.Tile.GetContentTileDefinition(_tileDefinitionManager); tile.ThermalConductivity = tileDef?.ThermalConductivity ?? 0.5f; tile.HeatCapacity = tileDef?.HeatCapacity ?? float.PositiveInfinity; InvalidateVisuals(mapGrid.Index, indices); diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs index cef954fe00..d8c34f1ce6 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs @@ -1,18 +1,10 @@ -using Content.Server.Administration; using Content.Server.Administration.Logs; using Content.Server.Atmos.Components; -using Content.Server.Database; using Content.Server.NodeContainer.EntitySystems; -using Content.Server.Temperature.Components; -using Content.Server.Temperature.Systems; -using Content.Shared.Administration; using Content.Shared.Atmos.EntitySystems; using Content.Shared.Maps; using JetBrains.Annotations; -using Robust.Shared.Console; using Robust.Shared.Containers; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; using Robust.Shared.Map; namespace Content.Server.Atmos.EntitySystems @@ -26,6 +18,7 @@ namespace Content.Server.Atmos.EntitySystems [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly AdminLogSystem _adminLog = default!; [Dependency] private readonly SharedContainerSystem _containers = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; private const float ExposedUpdateDelay = 1f; private float _exposedTimer = 0f; @@ -77,6 +70,7 @@ namespace Content.Server.Atmos.EntitySystems base.Update(frameTime); UpdateProcessing(frameTime); + UpdateHighPressure(frameTime); _exposedTimer += frameTime; diff --git a/Content.Server/Atmos/EntitySystems/FlammableSystem.cs b/Content.Server/Atmos/EntitySystems/FlammableSystem.cs index d657b0d081..a92a95294d 100644 --- a/Content.Server/Atmos/EntitySystems/FlammableSystem.cs +++ b/Content.Server/Atmos/EntitySystems/FlammableSystem.cs @@ -111,7 +111,7 @@ namespace Content.Server.Atmos.EntitySystems args.IsHot = flammable.OnFire; } - private void OnTileFireEvent(EntityUid uid, FlammableComponent flammable, TileFireEvent args) + private void OnTileFireEvent(EntityUid uid, FlammableComponent flammable, ref TileFireEvent args) { var tempDelta = args.Temperature - MinIgnitionTemperature; diff --git a/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs b/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs index 17f222922f..28a3d05539 100644 --- a/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs +++ b/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs @@ -237,7 +237,7 @@ namespace Content.Server.Atmos.Monitor.Systems } } - private void OnFireEvent(EntityUid uid, AtmosMonitorComponent component, TileFireEvent args) + private void OnFireEvent(EntityUid uid, AtmosMonitorComponent component, ref TileFireEvent args) { if (!TryComp(uid, out var powerReceiverComponent) || !powerReceiverComponent.Powered) diff --git a/Content.Server/Atmos/TileFireEvent.cs b/Content.Server/Atmos/TileFireEvent.cs index d9085ce015..5dad4e8fc0 100644 --- a/Content.Server/Atmos/TileFireEvent.cs +++ b/Content.Server/Atmos/TileFireEvent.cs @@ -1,14 +1,13 @@ -using Robust.Shared.GameObjects; - -namespace Content.Server.Atmos +namespace Content.Server.Atmos { /// /// Event raised directed to an entity when it is standing on a tile that's on fire. /// - public sealed class TileFireEvent : EntityEventArgs + [ByRefEvent] + public readonly struct TileFireEvent { - public float Temperature { get; } - public float Volume { get; } + public readonly float Temperature; + public readonly float Volume; public TileFireEvent(float temperature, float volume) {