diff --git a/Content.IntegrationTests/Tests/Fluids/FluidSpillTest.cs b/Content.IntegrationTests/Tests/Fluids/FluidSpillTest.cs new file mode 100644 index 0000000000..cfba1ee3e5 --- /dev/null +++ b/Content.IntegrationTests/Tests/Fluids/FluidSpillTest.cs @@ -0,0 +1,184 @@ +#nullable enable +using System; +using System.Threading.Tasks; +using Content.Server.Fluids.Components; +using Content.Server.Fluids.EntitySystems; +using Content.Shared.Chemistry.Components; +using Content.Shared.FixedPoint; +using NUnit.Framework; +using Robust.Server.Maps; +using Robust.Shared.GameObjects; +using Robust.Shared.Map; +using Robust.Shared.Maths; +using Robust.Shared.Timing; + +namespace Content.IntegrationTests.Tests.Fluids; + +[TestFixture] +[TestOf(typeof(FluidSpreaderSystem))] +public sealed class FluidSpill : ContentIntegrationTest +{ + private const string SpillMapsYml = "Maps/Test/floor3x3.yml"; + + private static PuddleComponent? GetPuddle(IEntityManager entityManager, IMapGrid mapGrid, Vector2i pos) + { + foreach (var uid in mapGrid.GetAnchoredEntities(pos)) + { + if (entityManager.TryGetComponent(uid, out PuddleComponent puddleComponent)) + return puddleComponent; + } + + return null; + } + + private readonly Direction[] _dirs = + { + Direction.East, + Direction.SouthEast, + Direction.South, + Direction.SouthWest, + Direction.West, + Direction.NorthWest, + Direction.North, + Direction.NorthEast, + }; + + + private readonly Vector2i _origin = new(-1, -1); + + [Test] + public async Task SpillEvenlyTest() + { + // --- Setup + var server = StartServer(); + await server.WaitIdleAsync(); + + var mapManager = server.ResolveDependency(); + var mapLoader = server.ResolveDependency(); + var entityManager = server.ResolveDependency(); + var spillSystem = server.ResolveDependency().GetEntitySystem(); + var gameTiming = server.ResolveDependency(); + MapId mapId; + IMapGrid? grid = null; + + await server.WaitPost(() => + { + mapId = mapManager.CreateMap(); + grid = mapLoader.LoadBlueprint(mapId, SpillMapsYml)!; + }); + + if (grid == null) + { + Assert.Fail($"Test blueprint {SpillMapsYml} not found."); + return; + } + + await server.WaitAssertion(() => + { + var solution = new Solution("Water", FixedPoint2.New(100)); + var tileRef = grid.GetTileRef(_origin); + var puddle = spillSystem.SpillAt(tileRef, solution, "PuddleSmear"); + Assert.That(puddle, Is.Not.Null); + Assert.That(GetPuddle(entityManager, grid, _origin), Is.Not.Null); + }); + + var sTimeToWait = (int) Math.Ceiling(2f * gameTiming.TickRate); + await server.WaitRunTicks(sTimeToWait); + + server.Assert(() => + { + var puddle = GetPuddle(entityManager, grid, _origin); + + Assert.That(puddle, Is.Not.Null); + Assert.That(puddle!.CurrentVolume, Is.EqualTo(FixedPoint2.New(20))); + + foreach (var direction in _dirs) + { + var newPos = _origin.Offset(direction); + var sidePuddle = GetPuddle(entityManager, grid, newPos); + Assert.That(sidePuddle, Is.Not.Null); + Assert.That(sidePuddle!.CurrentVolume, Is.EqualTo(FixedPoint2.New(10))); + } + }); + + await server.WaitIdleAsync(); + } + + + [Test] + public async Task SpillSmallOverflowTest() + { + // --- Setup + var server = StartServer(); + await server.WaitIdleAsync(); + + var mapManager = server.ResolveDependency(); + var mapLoader = server.ResolveDependency(); + var entityManager = server.ResolveDependency(); + var spillSystem = server.ResolveDependency().GetEntitySystem(); + var gameTiming = server.ResolveDependency(); + MapId mapId; + IMapGrid? grid = null; + + await server.WaitPost(() => + { + mapId = mapManager.CreateMap(); + grid = mapLoader.LoadBlueprint(mapId, SpillMapsYml)!; + }); + + if (grid == null) + { + Assert.Fail($"Test blueprint {SpillMapsYml} not found."); + return; + } + + await server.WaitAssertion(() => + { + var solution = new Solution("Water", FixedPoint2.New(20.01)); + + var tileRef = grid.GetTileRef(_origin); + var puddle = spillSystem.SpillAt(tileRef, solution, "PuddleSmear"); + + Assert.That(puddle, Is.Not.Null); + }); + + if (grid == null) + { + Assert.Fail($"Test blueprint {SpillMapsYml} not found."); + return; + } + + var sTimeToWait = (int) Math.Ceiling(2f * gameTiming.TickRate); + await server.WaitRunTicks(sTimeToWait); + + server.Assert(() => + { + var puddle = GetPuddle(entityManager, grid, _origin); + Assert.That(puddle, Is.Not.Null); + Assert.That(puddle!.CurrentVolume, Is.EqualTo(FixedPoint2.New(20))); + + // we don't know where a spill would happen + // but there should be only one + var emptyField = 0; + var fullField = 0; + foreach (var direction in _dirs) + { + var newPos = _origin.Offset(direction); + var sidePuddle = GetPuddle(entityManager, grid, newPos); + if (sidePuddle == null) + { + emptyField++; + } + else if (sidePuddle.CurrentVolume == FixedPoint2.Epsilon) + { + fullField++; + } + } + + Assert.That(emptyField, Is.EqualTo(7)); + Assert.That(fullField, Is.EqualTo(1)); + }); + + await server.WaitIdleAsync(); + } +} diff --git a/Content.Server/Fluids/EntitySystems/FluidSpreaderSystem.cs b/Content.Server/Fluids/EntitySystems/FluidSpreaderSystem.cs index df66569460..31b81a5855 100644 --- a/Content.Server/Fluids/EntitySystems/FluidSpreaderSystem.cs +++ b/Content.Server/Fluids/EntitySystems/FluidSpreaderSystem.cs @@ -37,9 +37,6 @@ public sealed class FluidSpreaderSystem : EntitySystem puddleComponent.SolutionName, out puddleSolution)) return; - if (puddleSolution.CurrentVolume <= puddleComponent.OverflowVolume) - return; - var spreaderComponent = EntityManager.EnsureComponent(puddleComponent.Owner); spreaderComponent.OverflownSolution = puddleSolution; spreaderComponent.Enabled = true; @@ -88,6 +85,13 @@ public sealed class FluidSpreaderSystem : EntitySystem private void SpreadFluid(EntityUid suid) { + EntityUid GetOrCreate(EntityUid uid, string prototype, IMapGrid grid, Vector2i pos) + { + return uid == EntityUid.Invalid + ? EntityManager.SpawnEntity(prototype, grid.GridTileToWorld(pos)) + : uid; + } + PuddleComponent? puddleComponent = null; MetaDataComponent? metadataOriginal = null; TransformComponent? transformOrig = null; @@ -97,22 +101,31 @@ public sealed class FluidSpreaderSystem : EntitySystem return; var prototypeName = metadataOriginal.EntityPrototype!.ID; - - var puddles = new List { puddleComponent }; var visitedTiles = new HashSet(); if (!_mapManager.TryGetGrid(transformOrig.GridID, out var mapGrid)) return; - while (puddles.Count > 0 + // skip origin puddle + var nextToExpand = new List(9); + ExpandPuddle(suid, visitedTiles, mapGrid, nextToExpand); + + while (nextToExpand.Count > 0 && spreader.OverflownSolution.CurrentVolume > FixedPoint2.Zero) { - var nextToExpand = new List<(Vector2i, EntityUid?)>(); + // we need to clamp to prevent spreading 0u fluids, while never going over spill limit + var divided = FixedPoint2.Clamp(spreader.OverflownSolution.CurrentVolume / nextToExpand.Count, + FixedPoint2.Epsilon, puddleComponent.OverflowVolume); - var divided = spreader.OverflownSolution.CurrentVolume / puddles.Count; - - foreach (var puddle in puddles) + foreach (var posAndUid in nextToExpand) { + var puddleUid = GetOrCreate(posAndUid.Uid, prototypeName, mapGrid, posAndUid.Pos); + + if (!TryComp(puddleUid, out PuddleComponent? puddle)) + continue; + + posAndUid.Uid = puddleUid; + if (puddle.CurrentVolume >= puddle.OverflowVolume) continue; // -puddle.OverflowLeft is guaranteed to be >= 0 @@ -122,52 +135,56 @@ public sealed class FluidSpreaderSystem : EntitySystem puddle.Owner, spreader.OverflownSolution.SplitSolution(split), false, false, puddle); - } - // if solution is spent do not explore - if (spreader.OverflownSolution.CurrentVolume <= FixedPoint2.Zero) - continue; + // if solution is spent do not explore + if (spreader.OverflownSolution.CurrentVolume <= FixedPoint2.Zero) + return; + } // find edges - foreach (var puddle in puddles) - { - TransformComponent? transform = null; + nextToExpand = ExpandPuddles(nextToExpand, visitedTiles, mapGrid); + } + } - if (!Resolve(puddle.Owner, ref transform, false)) - continue; + private List ExpandPuddles(List toExpand, + HashSet visitedTiles, + IMapGrid mapGrid) + { + var nextToExpand = new List(9); + foreach (var puddlePlacer in toExpand) + { + ExpandPuddle(puddlePlacer.Uid, visitedTiles, mapGrid, nextToExpand, puddlePlacer.Pos); + } - // prepare next set of puddles to be expanded - var puddlePos = transform.Coordinates.ToVector2i(EntityManager, _mapManager); - foreach (var direction in SharedDirectionExtensions.RandomDirections().ToArray()) - { - var newPos = puddlePos.Offset(direction); - if (visitedTiles.Contains(newPos)) - continue; + return nextToExpand; + } - visitedTiles.Add(newPos); + private void ExpandPuddle(EntityUid puddle, + HashSet visitedTiles, + IMapGrid mapGrid, + List nextToExpand, + Vector2i? pos = null) + { + TransformComponent? transform = null; - if (CanExpand(newPos, mapGrid, out var uid)) - nextToExpand.Add((newPos, uid)); - } - } + if (pos == null && !Resolve(puddle, ref transform, false)) + { + return; + } - puddles = new List(); + var puddlePos = pos ?? transform!.Coordinates.ToVector2i(EntityManager, _mapManager); - // prepare edges for next iteration - foreach (var (pos, uid) in nextToExpand) - { - if (spreader.OverflownSolution.CurrentVolume <= FixedPoint2.Zero) - continue; + // prepare next set of puddles to be expanded + foreach (var direction in SharedDirectionExtensions.RandomDirections().ToArray()) + { + var newPos = puddlePos.Offset(direction); + if (visitedTiles.Contains(newPos)) + continue; - var puddleUid = uid!.Value; - var coordinate = mapGrid.GridTileToWorld(pos); - if (uid == EntityUid.Invalid) - { - puddleUid = EntityManager.SpawnEntity(prototypeName, coordinate); - } + visitedTiles.Add(newPos); - puddles.Add(EntityManager.GetComponent(puddleUid)); - } + if (CanExpand(newPos, mapGrid, out var uid)) + nextToExpand.Add(new PuddlePlacer(newPos, (EntityUid) uid)); } } @@ -205,3 +222,16 @@ public sealed class FluidSpreaderSystem : EntitySystem return true; } } + +// Helper to allow mutable pair of (Pos, Uid) +internal sealed class PuddlePlacer +{ + internal Vector2i Pos; + internal EntityUid Uid; + + public PuddlePlacer(Vector2i pos, EntityUid uid) + { + Pos = pos; + Uid = uid; + } +} diff --git a/Resources/Maps/Test/floor3x3.yml b/Resources/Maps/Test/floor3x3.yml new file mode 100644 index 0000000000..d5ff197658 --- /dev/null +++ b/Resources/Maps/Test/floor3x3.yml @@ -0,0 +1,128 @@ +meta: + format: 2 + name: DemoStation + author: Space-Wizards + postmapinit: false +tilemap: + 0: space + 1: floor_asteroid_coarse_sand0 + 2: floor_asteroid_coarse_sand1 + 3: floor_asteroid_coarse_sand2 + 4: floor_asteroid_coarse_sand_dug + 5: floor_asteroid_sand + 6: floor_asteroid_tile + 7: floor_bar + 8: floor_blue + 9: floor_blue_circuit + 10: floor_clown + 11: floor_dark + 12: floor_elevator_shaft + 13: floor_freezer + 14: floor_glass + 15: floor_gold + 16: floor_grass + 17: floor_green_circuit + 18: floor_hydro + 19: floor_kitchen + 20: floor_laundry + 21: floor_lino + 22: floor_mime + 23: floor_mono + 24: floor_reinforced + 25: floor_rglass + 26: floor_rock_vault + 27: floor_showroom + 28: floor_silver + 29: floor_snow + 30: floor_steel + 31: floor_steel_dirty + 32: floor_techmaint + 33: floor_white + 34: floor_wood + 35: lattice + 36: plating + 37: underplating +grids: +- settings: + chunksize: 16 + tilesize: 1 + chunks: + - ind: "-1,-1" + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAHgAAAA== + - ind: "-1,0" + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + - ind: "0,0" + tiles: BgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + - ind: "0,-1" + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== +entities: +- uid: 0 + components: + - parent: null + type: Transform + - index: 0 + type: MapGrid + - linearDamping: 0.1 + fixedRotation: False + bodyType: Dynamic + type: Physics + - fixtures: + - shape: !type:PolygonShape + vertices: + - -0.01,0.01 + - -0.01,0.99 + - -1.99,0.99 + - -1.99,0.01 + id: grid_chunk--1.99-0.01 + mask: + - MapGrid + layer: + - MapGrid + mass: 7.7616 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 0.99,0.01 + - 0.99,0.99 + - 0.01,0.99 + - 0.01,0.01 + id: grid_chunk-0.01-0.01 + mask: + - MapGrid + layer: + - MapGrid + mass: 3.8416002 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 0.99,-1.99 + - 0.99,-0.01 + - 0.01,-0.01 + - 0.01,-1.99 + id: grid_chunk-0.01--1.99 + mask: + - MapGrid + layer: + - MapGrid + mass: 7.7616 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -0.01,-1.99 + - -0.01,-0.01 + - -1.99,-0.01 + - -1.99,-1.99 + id: grid_chunk--1.99--1.99 + mask: + - MapGrid + layer: + - MapGrid + mass: 15.681601 + restitution: 0.1 + type: Fixtures + - gravityShakeSound: !type:SoundPathSpecifier + path: /Audio/Effects/alert.ogg + type: Gravity + - chunkCollection: {} + type: DecalGrid +...