mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Start MapGrid ECS (#4185)
This commit is contained in:
@@ -621,6 +621,7 @@ namespace Robust.Client.Console.Commands
|
||||
|
||||
internal sealed class ChunkInfoCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IMapManager _map = default!;
|
||||
[Dependency] private readonly IEyeManager _eye = default!;
|
||||
[Dependency] private readonly IInputManager _input = default!;
|
||||
@@ -631,16 +632,17 @@ namespace Robust.Client.Console.Commands
|
||||
{
|
||||
var mousePos = _eye.ScreenToMap(_input.MouseScreenPosition);
|
||||
|
||||
if (!_map.TryFindGridAt(mousePos, out _, out var grid))
|
||||
if (!_map.TryFindGridAt(mousePos, out var gridUid, out var grid))
|
||||
{
|
||||
shell.WriteLine("No grid under your mouse cursor.");
|
||||
return;
|
||||
}
|
||||
|
||||
var chunkIndex = grid.LocalToChunkIndices(grid.MapToGrid(mousePos));
|
||||
var chunk = grid.GetOrAddChunk(chunkIndex);
|
||||
var mapSystem = _entManager.System<SharedMapSystem>();
|
||||
var chunkIndex = mapSystem.LocalToChunkIndices(gridUid, grid, grid.MapToGrid(mousePos));
|
||||
var chunk = mapSystem.GetOrAddChunk(gridUid, grid, chunkIndex);
|
||||
|
||||
shell.WriteLine($"worldBounds: {grid.CalcWorldAABB(chunk)} localBounds: {chunk.CachedBounds}");
|
||||
shell.WriteLine($"worldBounds: {mapSystem.CalcWorldAABB(gridUid, grid, chunk)} localBounds: {chunk.CachedBounds}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,8 @@ public sealed class DebugEntityLookupSystem : EntitySystem
|
||||
IoCManager.Resolve<IOverlayManager>().AddOverlay(
|
||||
new EntityLookupOverlay(
|
||||
EntityManager,
|
||||
Get<EntityLookupSystem>()));
|
||||
EntityManager.System<EntityLookupSystem>(),
|
||||
EntityManager.System<SharedTransformSystem>()));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -52,31 +53,35 @@ public sealed class DebugEntityLookupSystem : EntitySystem
|
||||
|
||||
public sealed class EntityLookupOverlay : Overlay
|
||||
{
|
||||
private IEntityManager _entityManager = default!;
|
||||
private EntityLookupSystem _lookup = default!;
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly EntityLookupSystem _lookup;
|
||||
private readonly SharedTransformSystem _transform;
|
||||
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
public EntityLookupOverlay(IEntityManager entManager, EntityLookupSystem lookup)
|
||||
public EntityLookupOverlay(IEntityManager entManager, EntityLookupSystem lookup, SharedTransformSystem transform)
|
||||
{
|
||||
_entityManager = entManager;
|
||||
_lookup = lookup;
|
||||
_xformQuery = entManager.GetEntityQuery<TransformComponent>();
|
||||
_transform = transform;
|
||||
}
|
||||
|
||||
protected internal override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
var worldHandle = args.WorldHandle;
|
||||
var xformQuery = _entityManager.GetEntityQuery<TransformComponent>();
|
||||
var worldBounds = args.WorldBounds;
|
||||
|
||||
foreach (var lookup in _lookup.FindLookupsIntersecting(args.MapId, args.WorldBounds))
|
||||
// TODO: Static version
|
||||
_lookup.FindLookupsIntersecting(args.MapId, worldBounds, (uid, lookup) =>
|
||||
{
|
||||
var lookupXform = xformQuery.GetComponent(lookup.Owner);
|
||||
|
||||
var (_, rotation, matrix, invMatrix) = lookupXform.GetWorldPositionRotationMatrixWithInv();
|
||||
var (_, rotation, matrix, invMatrix) = _transform.GetWorldPositionRotationMatrixWithInv(uid);
|
||||
|
||||
worldHandle.SetTransform(matrix);
|
||||
|
||||
var lookupAABB = invMatrix.TransformBox(args.WorldBounds);
|
||||
var lookupAABB = invMatrix.TransformBox(worldBounds);
|
||||
var ents = new List<EntityUid>();
|
||||
|
||||
lookup.DynamicTree.QueryAabb(ref ents, static (ref List<EntityUid> state, in FixtureProxy value) =>
|
||||
@@ -105,20 +110,22 @@ public sealed class EntityLookupOverlay : Overlay
|
||||
|
||||
foreach (var ent in ents)
|
||||
{
|
||||
if (_entityManager.Deleted(ent)) continue;
|
||||
var xform = xformQuery.GetComponent(ent);
|
||||
if (_entityManager.Deleted(ent))
|
||||
continue;
|
||||
|
||||
var xform = _xformQuery.GetComponent(ent);
|
||||
|
||||
//DebugTools.Assert(!ent.IsInContainer(_entityManager));
|
||||
var (entPos, entRot) = xform.GetWorldPositionRotation();
|
||||
var (entPos, entRot) = _transform.GetWorldPositionRotation(ent);
|
||||
|
||||
var lookupPos = invMatrix.Transform(entPos);
|
||||
var lookupRot = entRot - rotation;
|
||||
|
||||
var aabb = _lookup.GetAABB(ent, lookupPos, lookupRot, xform, xformQuery);
|
||||
var aabb = _lookup.GetAABB(ent, lookupPos, lookupRot, xform, _xformQuery);
|
||||
|
||||
worldHandle.DrawRect(aabb, Color.Blue.WithAlpha(0.2f));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
worldHandle.SetTransform(Matrix3.Identity);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Robust.Client.Physics
|
||||
{
|
||||
if (_enableDebug == value) return;
|
||||
|
||||
Sawmill.Info($"Set grid fixture debug to {value}");
|
||||
Log.Info($"Set grid fixture debug to {value}");
|
||||
_enableDebug = value;
|
||||
|
||||
if (_enableDebug)
|
||||
@@ -59,7 +59,7 @@ namespace Robust.Client.Physics
|
||||
|
||||
private void OnDebugMessage(ChunkSplitDebugMessage ev)
|
||||
{
|
||||
Sawmill.Info($"Received grid fixture debug data");
|
||||
Log.Info($"Received grid fixture debug data");
|
||||
if (!_enableDebug) return;
|
||||
|
||||
_nodes[ev.Grid] = ev.Nodes;
|
||||
|
||||
@@ -62,18 +62,19 @@ namespace Robust.Client.UserInterface.CustomControls.DebugMonitorControls
|
||||
var screenSize = _displayManager.ScreenSize;
|
||||
var screenScale = _displayManager.MainWindow.ContentScale;
|
||||
|
||||
MapCoordinates mouseWorldMap;
|
||||
EntityCoordinates mouseGridPos;
|
||||
TileRef tile;
|
||||
|
||||
mouseWorldMap = _eyeManager.ScreenToMap(mouseScreenPos);
|
||||
var mouseWorldMap = _eyeManager.ScreenToMap(mouseScreenPos);
|
||||
if (mouseWorldMap == MapCoordinates.Nullspace)
|
||||
return;
|
||||
|
||||
if (_mapManager.TryFindGridAt(mouseWorldMap, out _, out var mouseGrid))
|
||||
var mapSystem = _entityManager.System<SharedMapSystem>();
|
||||
|
||||
if (_mapManager.TryFindGridAt(mouseWorldMap, out var mouseGridUid, out var mouseGrid))
|
||||
{
|
||||
mouseGridPos = mouseGrid.MapToGrid(mouseWorldMap);
|
||||
tile = mouseGrid.GetTileRef(mouseGridPos);
|
||||
mouseGridPos = mapSystem.MapToGrid(mouseGridUid, mouseWorldMap);
|
||||
tile = mapSystem.GetTileRef(mouseGridUid, mouseGrid, mouseGridPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -44,6 +44,7 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
private IServerEntityManagerInternal _serverEntityManager = default!;
|
||||
[Dependency] private readonly ITileDefinitionManager _tileDefManager = default!;
|
||||
[Dependency] private readonly MetaDataSystem _meta = default!;
|
||||
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
private ISawmill _logLoader = default!;
|
||||
@@ -764,11 +765,12 @@ public sealed class MapLoaderSystem : EntitySystem
|
||||
SequenceDataNode yamlGridChunks = (SequenceDataNode)yamlGrid["chunks"];
|
||||
|
||||
var grid = AllocateMapGrid(gridComp, yamlGridInfo);
|
||||
var gridUid = grid.Owner;
|
||||
|
||||
foreach (var chunkNode in yamlGridChunks.Cast<MappingDataNode>())
|
||||
{
|
||||
var (chunkOffsetX, chunkOffsetY) = _serManager.Read<Vector2i>(chunkNode["ind"]);
|
||||
_serManager.Read(chunkNode, _context, instanceProvider: () => grid.GetOrAddChunk(chunkOffsetX, chunkOffsetY), notNullableOverride: true);
|
||||
_serManager.Read(chunkNode, _context, instanceProvider: () => _mapSystem.GetOrAddChunk(gridUid, grid, chunkOffsetX, chunkOffsetY), notNullableOverride: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,15 +107,12 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
private EntityQuery<EyeComponent> _eyeQuery;
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_eyeQuery = GetEntityQuery<EyeComponent>();
|
||||
_xformQuery = GetEntityQuery<TransformComponent>();
|
||||
_sawmill = Logger.GetSawmill("PVS");
|
||||
|
||||
_entityPvsCollection = RegisterPVSCollection<EntityUid>();
|
||||
|
||||
@@ -182,7 +179,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
sb.Append($" Entity last sent: {lastSeenTick.Value}");
|
||||
}
|
||||
|
||||
_sawmill.Warning(sb.ToString());
|
||||
Log.Warning(sb.ToString());
|
||||
|
||||
sessionData.LastSeenAt.Clear();
|
||||
|
||||
@@ -231,7 +228,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
|
||||
private PVSCollection<TIndex> RegisterPVSCollection<TIndex>() where TIndex : IComparable<TIndex>, IEquatable<TIndex>
|
||||
{
|
||||
var collection = new PVSCollection<TIndex>(_sawmill, EntityManager, _transform);
|
||||
var collection = new PVSCollection<TIndex>(Log, EntityManager, _transform);
|
||||
_pvsCollections.Add(collection);
|
||||
return collection;
|
||||
}
|
||||
@@ -334,12 +331,12 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
if (e.NewStatus == SessionStatus.InGame)
|
||||
{
|
||||
if (!PlayerData.TryAdd(e.Session, new()))
|
||||
_sawmill.Error($"Attempted to add player to _playerVisibleSets, but they were already present? Session:{e.Session}");
|
||||
Log.Error($"Attempted to add player to _playerVisibleSets, but they were already present? Session:{e.Session}");
|
||||
|
||||
foreach (var pvsCollection in _pvsCollections)
|
||||
{
|
||||
if (!pvsCollection.AddPlayer(e.Session))
|
||||
_sawmill.Error($"Attempted to add player to pvsCollection, but they were already present? Session:{e.Session}");
|
||||
Log.Error($"Attempted to add player to pvsCollection, but they were already present? Session:{e.Session}");
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -353,7 +350,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
foreach (var pvsCollection in _pvsCollections)
|
||||
{
|
||||
if (!pvsCollection.RemovePlayer(e.Session))
|
||||
_sawmill.Error($"Attempted to remove player from pvsCollection, but they were already removed? Session:{e.Session}");
|
||||
Log.Error($"Attempted to remove player from pvsCollection, but they were already removed? Session:{e.Session}");
|
||||
}
|
||||
|
||||
if (data.Overflow != null)
|
||||
@@ -822,7 +819,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
#if DEBUG
|
||||
// This happens relatively frequently for the current TickBuffer value, and doesn't really provide any
|
||||
// useful info when not debugging/testing locally. Hence only enable on DEBUG.
|
||||
_sawmill.Debug($"Client {session} exceeded tick buffer.");
|
||||
Log.Debug($"Client {session} exceeded tick buffer.");
|
||||
#endif
|
||||
}
|
||||
else if (oldEntry.Value.Value != lastAcked)
|
||||
@@ -980,7 +977,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
if (metaDataComponent.EntityLifeStage >= EntityLifeStage.Terminating)
|
||||
{
|
||||
var rep = new EntityStringRepresentation(uid, metaDataComponent.EntityDeleted, metaDataComponent.EntityName, metaDataComponent.EntityPrototype?.ID);
|
||||
_sawmill.Error($"Attempted to add a deleted entity to PVS send set: '{rep}'. Trace:\n{Environment.StackTrace}");
|
||||
Log.Error($"Attempted to add a deleted entity to PVS send set: '{rep}'. Trace:\n{Environment.StackTrace}");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1110,7 +1107,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
|
||||
if (component.Deleted || !component.Initialized)
|
||||
{
|
||||
_sawmill.Error("Entity manager returned deleted or uninitialized components while sending entity data");
|
||||
Log.Error("Entity manager returned deleted or uninitialized components while sending entity data");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ internal sealed class MapChunkSerializer : ITypeSerializer<MapChunk, MappingData
|
||||
id = tileDefinitionManager[defName].TileId;
|
||||
|
||||
var tile = new Tile(id, flags, variant);
|
||||
chunk.SetTile(x, y, tile);
|
||||
chunk.TrySetTile(x, y, tile, out _, out _);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ internal sealed class MapChunkSerializer : ITypeSerializer<MapChunk, MappingData
|
||||
{
|
||||
for (ushort x = 0; x < chunk.ChunkSize; x++)
|
||||
{
|
||||
chunk.SetTile(x, y, source.GetTile(x, y));
|
||||
chunk.TrySetTile(x, y, source.GetTile(x, y), out _, out _);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,16 @@ internal sealed class RecursiveMoveSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
|
||||
bool Subscribed = false;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_xformQuery = GetEntityQuery<TransformComponent>();
|
||||
}
|
||||
|
||||
internal void AddSubscription()
|
||||
{
|
||||
if (Subscribed)
|
||||
@@ -34,14 +42,12 @@ internal sealed class RecursiveMoveSystem : EntitySystem
|
||||
DebugTools.Assert(!_mapManager.IsMap(args.Sender));
|
||||
DebugTools.Assert(!_mapManager.IsGrid(args.Sender));
|
||||
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
AnythingMovedSubHandler(args.Sender, args.Component, xformQuery);
|
||||
AnythingMovedSubHandler(args.Sender, args.Component);
|
||||
}
|
||||
|
||||
private void AnythingMovedSubHandler(
|
||||
EntityUid uid,
|
||||
TransformComponent xform,
|
||||
EntityQuery<TransformComponent> xformQuery)
|
||||
TransformComponent xform)
|
||||
{
|
||||
// TODO maybe use a c# event? This event gets raised a lot.
|
||||
// Would probably help with server performance and is also the main bottleneck for replay scrubbing.
|
||||
@@ -55,8 +61,8 @@ internal sealed class RecursiveMoveSystem : EntitySystem
|
||||
var childEnumerator = xform.ChildEnumerator;
|
||||
while (childEnumerator.MoveNext(out var child))
|
||||
{
|
||||
if (xformQuery.TryGetComponent(child.Value, out var childXform))
|
||||
AnythingMovedSubHandler(child.Value, childXform, xformQuery);
|
||||
if (_xformQuery.TryGetComponent(child.Value, out var childXform))
|
||||
AnythingMovedSubHandler(child.Value, childXform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ internal sealed class TeleportCommand : LocalizedCommands
|
||||
|
||||
if (_map.TryFindGridAt(mapId, position, out var gridUid, out var grid))
|
||||
{
|
||||
var gridPos = grid.WorldToLocal(position);
|
||||
var gridPos = xformSystem.GetInvWorldMatrix(gridUid).Transform(position);
|
||||
|
||||
xformSystem.SetCoordinates(entity, transform, new EntityCoordinates(gridUid, gridPos));
|
||||
}
|
||||
|
||||
@@ -49,8 +49,6 @@ namespace Robust.Shared.GameObjects
|
||||
private UniqueIndexHkm<EntityUid, Component> _entCompIndex =
|
||||
new(ComponentCollectionCapacity);
|
||||
|
||||
private EntityQuery<MetaDataComponent> _metaQuery;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event Action<AddedComponentEventArgs>? ComponentAdded;
|
||||
|
||||
@@ -68,7 +66,6 @@ namespace Robust.Shared.GameObjects
|
||||
FillComponentDict();
|
||||
_componentFactory.ComponentAdded += OnComponentAdded;
|
||||
_componentFactory.ComponentReferenceAdded += OnComponentReferenceAdded;
|
||||
_metaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -18,6 +18,8 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
public delegate void EntityUidQueryCallback(EntityUid uid);
|
||||
|
||||
public delegate void ComponentQueryCallback<T>(EntityUid uid, T component) where T : Component;
|
||||
|
||||
/// <inheritdoc />
|
||||
[Virtual]
|
||||
public partial class EntityManager : IEntityManager
|
||||
@@ -36,6 +38,9 @@ namespace Robust.Shared.GameObjects
|
||||
// positions on spawn....
|
||||
private SharedTransformSystem _xforms = default!;
|
||||
|
||||
private EntityQuery<MetaDataComponent> _metaQuery;
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
|
||||
#endregion Dependencies
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -76,6 +81,8 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
private string _xformName = string.Empty;
|
||||
|
||||
private SharedMapSystem _mapSystem = default!;
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
private ISawmill _resolveSawmill = default!;
|
||||
|
||||
@@ -219,7 +226,10 @@ namespace Robust.Shared.GameObjects
|
||||
_entitySystemManager.Initialize();
|
||||
Started = true;
|
||||
_eventBus.CalcOrdering();
|
||||
_xforms = _entitySystemManager.GetEntitySystem<SharedTransformSystem>();
|
||||
_mapSystem = System<SharedMapSystem>();
|
||||
_xforms = System<SharedTransformSystem>();
|
||||
_metaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
_xformQuery = GetEntityQuery<TransformComponent>();
|
||||
}
|
||||
|
||||
public virtual void Shutdown()
|
||||
@@ -306,7 +316,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
if (coordinates.IsValid(this))
|
||||
{
|
||||
_xforms.SetCoordinates(newEntity, GetComponent<TransformComponent>(newEntity), coordinates, unanchor: false);
|
||||
_xforms.SetCoordinates(newEntity, _xformQuery.GetComponent(newEntity), coordinates, unanchor: false);
|
||||
}
|
||||
|
||||
return newEntity;
|
||||
@@ -316,7 +326,7 @@ namespace Robust.Shared.GameObjects
|
||||
public virtual EntityUid CreateEntityUninitialized(string? prototypeName, MapCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
{
|
||||
var newEntity = CreateEntity(prototypeName, default, overrides);
|
||||
var transform = GetComponent<TransformComponent>(newEntity);
|
||||
var transform = _xformQuery.GetComponent(newEntity);
|
||||
|
||||
if (coordinates.MapId == MapId.Nullspace)
|
||||
{
|
||||
@@ -333,7 +343,7 @@ namespace Robust.Shared.GameObjects
|
||||
EntityCoordinates coords;
|
||||
if (transform.Anchored && _mapManager.TryFindGridAt(coordinates, out var gridUid, out var grid))
|
||||
{
|
||||
coords = new EntityCoordinates(gridUid, grid.WorldToLocal(coordinates.Position));
|
||||
coords = new EntityCoordinates(gridUid, _mapSystem.WorldToLocal(gridUid, grid, coordinates.Position));
|
||||
_xforms.SetCoordinates(newEntity, transform, coords, unanchor: false);
|
||||
}
|
||||
else
|
||||
@@ -743,7 +753,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
private protected void LoadEntity(EntityUid entity, IEntityLoadContext? context)
|
||||
{
|
||||
EntityPrototype.LoadEntity(GetComponent<MetaDataComponent>(entity).EntityPrototype, entity, ComponentFactory, this, _serManager, context);
|
||||
EntityPrototype.LoadEntity(_metaQuery.GetComponent(entity).EntityPrototype, entity, ComponentFactory, this, _serManager, context);
|
||||
}
|
||||
|
||||
private protected void LoadEntity(EntityUid entity, IEntityLoadContext? context, EntityPrototype? prototype)
|
||||
@@ -755,12 +765,12 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
try
|
||||
{
|
||||
var meta = GetComponent<MetaDataComponent>(entity);
|
||||
var meta = _metaQuery.GetComponent(entity);
|
||||
InitializeEntity(entity, meta);
|
||||
StartEntity(entity);
|
||||
|
||||
// If the map we're initializing the entity on is initialized, run map init on it.
|
||||
if (_mapManager.IsMapInitialized(mapId ?? GetComponent<TransformComponent>(entity).MapID))
|
||||
if (_mapManager.IsMapInitialized(mapId ?? _xformQuery.GetComponent(entity).MapID))
|
||||
RunMapInit(entity, meta);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
@@ -26,12 +26,10 @@ public sealed partial class EntityLookupSystem
|
||||
EntityUid lookupUid,
|
||||
HashSet<EntityUid> intersecting,
|
||||
Box2 worldAABB,
|
||||
LookupFlags flags,
|
||||
EntityQuery<BroadphaseComponent> lookupQuery,
|
||||
EntityQuery<TransformComponent> xformQuery)
|
||||
LookupFlags flags)
|
||||
{
|
||||
var lookup = lookupQuery.GetComponent(lookupUid);
|
||||
var invMatrix = _transform.GetInvWorldMatrix(lookupUid, xformQuery);
|
||||
var lookup = _broadQuery.GetComponent(lookupUid);
|
||||
var invMatrix = _transform.GetInvWorldMatrix(lookupUid);
|
||||
var localAABB = invMatrix.TransformBox(worldAABB);
|
||||
|
||||
if ((flags & LookupFlags.Dynamic) != 0x0)
|
||||
@@ -79,12 +77,10 @@ public sealed partial class EntityLookupSystem
|
||||
EntityUid lookupUid,
|
||||
HashSet<EntityUid> intersecting,
|
||||
Box2Rotated worldBounds,
|
||||
LookupFlags flags,
|
||||
EntityQuery<BroadphaseComponent> lookupQuery,
|
||||
EntityQuery<TransformComponent> xformQuery)
|
||||
LookupFlags flags)
|
||||
{
|
||||
var lookup = lookupQuery.GetComponent(lookupUid);
|
||||
var invMatrix = _transform.GetInvWorldMatrix(lookupUid, xformQuery);
|
||||
var lookup = _broadQuery.GetComponent(lookupUid);
|
||||
var invMatrix = _transform.GetInvWorldMatrix(lookupUid);
|
||||
// We don't just use CalcBoundingBox because the transformed bounds might be tighter.
|
||||
var localAABB = invMatrix.TransformBox(worldBounds);
|
||||
|
||||
@@ -132,12 +128,10 @@ public sealed partial class EntityLookupSystem
|
||||
private bool AnyEntitiesIntersecting(EntityUid lookupUid,
|
||||
Box2 worldAABB,
|
||||
LookupFlags flags,
|
||||
EntityQuery<BroadphaseComponent> lookupQuery,
|
||||
EntityQuery<TransformComponent> xformQuery,
|
||||
EntityUid? ignored = null)
|
||||
{
|
||||
var lookup = lookupQuery.GetComponent(lookupUid);
|
||||
var localAABB = xformQuery.GetComponent(lookupUid).InvWorldMatrix.TransformBox(worldAABB);
|
||||
var lookup = _broadQuery.GetComponent(lookupUid);
|
||||
var localAABB = _transform.GetInvWorldMatrix(lookupUid).TransformBox(worldAABB);
|
||||
var state = (ignored, found: false);
|
||||
|
||||
if ((flags & LookupFlags.Dynamic) != 0x0)
|
||||
@@ -197,12 +191,10 @@ public sealed partial class EntityLookupSystem
|
||||
private bool AnyEntitiesIntersecting(EntityUid lookupUid,
|
||||
Box2Rotated worldBounds,
|
||||
LookupFlags flags,
|
||||
EntityQuery<BroadphaseComponent> lookupQuery,
|
||||
EntityQuery<TransformComponent> xformQuery,
|
||||
EntityUid? ignored = null)
|
||||
{
|
||||
var lookup = lookupQuery.GetComponent(lookupUid);
|
||||
var localAABB = xformQuery.GetComponent(lookupUid).InvWorldMatrix.TransformBox(worldBounds);
|
||||
var lookup = _broadQuery.GetComponent(lookupUid);
|
||||
var localAABB = _transform.GetInvWorldMatrix(lookupUid).TransformBox(worldBounds);
|
||||
var state = (ignored, found: false);
|
||||
|
||||
if ((flags & LookupFlags.Dynamic) != 0x0)
|
||||
@@ -259,35 +251,35 @@ public sealed partial class EntityLookupSystem
|
||||
return state.found;
|
||||
}
|
||||
|
||||
private void RecursiveAdd(EntityUid uid, ref ValueList<EntityUid> toAdd, EntityQuery<TransformComponent> xformQuery)
|
||||
private void RecursiveAdd(EntityUid uid, ref ValueList<EntityUid> toAdd)
|
||||
{
|
||||
var childEnumerator = xformQuery.GetComponent(uid).ChildEnumerator;
|
||||
var childEnumerator = _xformQuery.GetComponent(uid).ChildEnumerator;
|
||||
|
||||
while (childEnumerator.MoveNext(out var child))
|
||||
{
|
||||
toAdd.Add(child.Value);
|
||||
RecursiveAdd(child.Value, ref toAdd, xformQuery);
|
||||
RecursiveAdd(child.Value, ref toAdd);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddContained(HashSet<EntityUid> intersecting, LookupFlags flags, EntityQuery<TransformComponent> xformQuery)
|
||||
private void AddContained(HashSet<EntityUid> intersecting, LookupFlags flags)
|
||||
{
|
||||
if ((flags & LookupFlags.Contained) == 0x0 || intersecting.Count == 0)
|
||||
return;
|
||||
|
||||
var conQuery = GetEntityQuery<ContainerManagerComponent>();
|
||||
var toAdd = new ValueList<EntityUid>();
|
||||
|
||||
foreach (var uid in intersecting)
|
||||
{
|
||||
if (!conQuery.TryGetComponent(uid, out var conManager)) continue;
|
||||
if (!_containerQuery.TryGetComponent(uid, out var conManager))
|
||||
continue;
|
||||
|
||||
foreach (var con in conManager.GetAllContainers())
|
||||
{
|
||||
foreach (var contained in con.ContainedEntities)
|
||||
{
|
||||
toAdd.Add(contained);
|
||||
RecursiveAdd(contained, ref toAdd, xformQuery);
|
||||
RecursiveAdd(contained, ref toAdd);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -321,11 +313,9 @@ public sealed partial class EntityLookupSystem
|
||||
float arcWidth,
|
||||
LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
|
||||
foreach (var entity in GetEntitiesInRange(coordinates, range * 2, flags))
|
||||
{
|
||||
var angle = new Angle(_transform.GetWorldPosition(entity, xformQuery) - coordinates.Position);
|
||||
var angle = new Angle(_transform.GetWorldPosition(entity) - coordinates.Position);
|
||||
if (angle.Degrees < direction.Degrees + arcWidth / 2 &&
|
||||
angle.Degrees > direction.Degrees - arcWidth / 2)
|
||||
yield return entity;
|
||||
@@ -340,48 +330,63 @@ public sealed partial class EntityLookupSystem
|
||||
{
|
||||
if (mapId == MapId.Nullspace) return false;
|
||||
|
||||
var lookupQuery = GetEntityQuery<BroadphaseComponent>();
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
|
||||
// Don't need to check contained entities as they have the same bounds as the parent.
|
||||
var found = false;
|
||||
|
||||
foreach (var grid in _mapManager.FindGridsIntersecting(mapId, worldAABB))
|
||||
{
|
||||
if (AnyEntitiesIntersecting(grid.Owner, worldAABB, flags, lookupQuery, xformQuery)) return true;
|
||||
}
|
||||
var state = (this, worldAABB, flags, found);
|
||||
|
||||
_mapManager.FindGridsIntersecting(mapId, worldAABB, ref state,
|
||||
static (EntityUid uid, MapGridComponent _, ref (EntityLookupSystem lookup, Box2 worldAABB, LookupFlags flags, bool found) tuple) =>
|
||||
{
|
||||
if (!tuple.lookup.AnyEntitiesIntersecting(uid, tuple.worldAABB, tuple.flags))
|
||||
return true;
|
||||
|
||||
tuple.found = true;
|
||||
return false;
|
||||
});
|
||||
|
||||
if (state.found)
|
||||
return true;
|
||||
|
||||
var mapUid = _mapManager.GetMapEntityId(mapId);
|
||||
return AnyEntitiesIntersecting(mapUid, worldAABB, flags, lookupQuery, xformQuery);
|
||||
return AnyEntitiesIntersecting(mapUid, worldAABB, flags);
|
||||
}
|
||||
|
||||
public HashSet<EntityUid> GetEntitiesIntersecting(MapId mapId, Box2 worldAABB, LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
if (mapId == MapId.Nullspace) return new HashSet<EntityUid>();
|
||||
|
||||
var lookupQuery = GetEntityQuery<BroadphaseComponent>();
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
|
||||
var intersecting = new HashSet<EntityUid>();
|
||||
|
||||
// Get grid entities
|
||||
foreach (var grid in _mapManager.FindGridsIntersecting(mapId, worldAABB))
|
||||
{
|
||||
AddEntitiesIntersecting(grid.Owner, intersecting, worldAABB, flags, lookupQuery, xformQuery);
|
||||
var state = (this, _map, intersecting, worldAABB, flags);
|
||||
|
||||
if ((flags & LookupFlags.Static) != 0x0)
|
||||
_mapManager.FindGridsIntersecting(mapId, worldAABB, ref state,
|
||||
static (EntityUid gridUid, MapGridComponent grid, ref (
|
||||
EntityLookupSystem lookup, SharedMapSystem _map, HashSet<EntityUid> intersecting,
|
||||
Box2 worldAABB, LookupFlags flags) tuple) =>
|
||||
{
|
||||
foreach (var uid in grid.GetAnchoredEntities(worldAABB))
|
||||
tuple.lookup.AddEntitiesIntersecting(gridUid, tuple.intersecting, tuple.worldAABB, tuple.flags);
|
||||
|
||||
if ((tuple.flags & LookupFlags.Static) != 0x0)
|
||||
{
|
||||
if (Deleted(uid)) continue;
|
||||
intersecting.Add(uid);
|
||||
// TODO: Need a struct enumerator version.
|
||||
foreach (var uid in tuple._map.GetAnchoredEntities(gridUid, grid, tuple.worldAABB))
|
||||
{
|
||||
if (tuple.lookup.Deleted(uid))
|
||||
continue;
|
||||
|
||||
tuple.intersecting.Add(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
// Get map entities
|
||||
var mapUid = _mapManager.GetMapEntityId(mapId);
|
||||
AddEntitiesIntersecting(mapUid, intersecting, worldAABB, flags, lookupQuery, xformQuery);
|
||||
AddContained(intersecting, flags, xformQuery);
|
||||
AddEntitiesIntersecting(mapUid, intersecting, worldAABB, flags);
|
||||
AddContained(intersecting, flags);
|
||||
|
||||
return intersecting;
|
||||
}
|
||||
@@ -392,39 +397,55 @@ public sealed partial class EntityLookupSystem
|
||||
|
||||
public bool AnyEntitiesIntersecting(MapId mapId, Box2Rotated worldBounds, LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
var lookupQuery = GetEntityQuery<BroadphaseComponent>();
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
|
||||
// Don't need to check contained entities as they have the same bounds as the parent.
|
||||
var worldAABB = worldBounds.CalcBoundingBox();
|
||||
|
||||
foreach (var grid in _mapManager.FindGridsIntersecting(mapId, worldBounds.CalcBoundingBox()))
|
||||
{
|
||||
if (AnyEntitiesIntersecting(grid.Owner, worldBounds, flags, lookupQuery, xformQuery)) return true;
|
||||
}
|
||||
const bool found = false;
|
||||
var state = (this, worldBounds, flags, found);
|
||||
|
||||
_mapManager.FindGridsIntersecting(mapId, worldAABB, ref state,
|
||||
static (EntityUid uid, MapGridComponent grid, ref (EntityLookupSystem lookup, Box2Rotated worldBounds, LookupFlags flags, bool found) tuple) =>
|
||||
{
|
||||
if (tuple.lookup.AnyEntitiesIntersecting(uid, tuple.worldBounds, tuple.flags))
|
||||
{
|
||||
tuple.found = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (state.found)
|
||||
return true;
|
||||
|
||||
var mapUid = _mapManager.GetMapEntityId(mapId);
|
||||
return AnyEntitiesIntersecting(mapUid, worldBounds, flags, lookupQuery, xformQuery);
|
||||
return AnyEntitiesIntersecting(mapUid, worldBounds, flags);
|
||||
}
|
||||
|
||||
public HashSet<EntityUid> GetEntitiesIntersecting(MapId mapId, Box2Rotated worldBounds, LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
if (mapId == MapId.Nullspace) return new HashSet<EntityUid>();
|
||||
|
||||
var lookupQuery = GetEntityQuery<BroadphaseComponent>();
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
|
||||
var intersecting = new HashSet<EntityUid>();
|
||||
|
||||
if (mapId == MapId.Nullspace)
|
||||
return intersecting;
|
||||
|
||||
// Get grid entities
|
||||
foreach (var grid in _mapManager.FindGridsIntersecting(mapId, worldBounds.CalcBoundingBox()))
|
||||
var state = (this, intersecting, worldBounds, flags);
|
||||
|
||||
_mapManager.FindGridsIntersecting(mapId, worldBounds.CalcBoundingBox(), ref state, static
|
||||
(EntityUid uid, MapGridComponent _,
|
||||
ref (EntityLookupSystem lookup,
|
||||
HashSet<EntityUid> intersecting,
|
||||
Box2Rotated worldBounds,
|
||||
LookupFlags flags) tuple) =>
|
||||
{
|
||||
AddEntitiesIntersecting(grid.Owner, intersecting, worldBounds, flags, lookupQuery, xformQuery);
|
||||
}
|
||||
tuple.lookup.AddEntitiesIntersecting(uid, tuple.intersecting, tuple.worldBounds, tuple.flags);
|
||||
return true;
|
||||
});
|
||||
|
||||
// Get map entities
|
||||
var mapUid = _mapManager.GetMapEntityId(mapId);
|
||||
AddEntitiesIntersecting(mapUid, intersecting, worldBounds, flags, lookupQuery, xformQuery);
|
||||
AddContained(intersecting, flags, xformQuery);
|
||||
AddEntitiesIntersecting(mapUid, intersecting, worldBounds, flags);
|
||||
AddContained(intersecting, flags);
|
||||
|
||||
return intersecting;
|
||||
}
|
||||
@@ -438,50 +459,72 @@ public sealed partial class EntityLookupSystem
|
||||
public bool AnyEntitiesIntersecting(EntityUid uid, LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
var worldAABB = GetWorldAABB(uid);
|
||||
var mapID = Transform(uid).MapID;
|
||||
var mapID = _xformQuery.GetComponent(uid).MapID;
|
||||
|
||||
if (mapID == MapId.Nullspace) return false;
|
||||
if (mapID == MapId.Nullspace)
|
||||
return false;
|
||||
|
||||
var lookupQuery = GetEntityQuery<BroadphaseComponent>();
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
const bool found = false;
|
||||
var state = (this, worldAABB, flags, found, uid);
|
||||
|
||||
_mapManager.FindGridsIntersecting(mapID, worldAABB, ref state,
|
||||
static (EntityUid gridUid, MapGridComponent grid,
|
||||
ref (EntityLookupSystem lookup, Box2 worldAABB, LookupFlags flags, bool found, EntityUid ignored) tuple) =>
|
||||
{
|
||||
if (tuple.lookup.AnyEntitiesIntersecting(gridUid, tuple.worldAABB, tuple.flags, tuple.ignored))
|
||||
{
|
||||
tuple.found = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var grid in _mapManager.FindGridsIntersecting(mapID, worldAABB))
|
||||
{
|
||||
if (AnyEntitiesIntersecting(grid.Owner, worldAABB, flags, lookupQuery, xformQuery, uid))
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
var mapUid = _mapManager.GetMapEntityId(mapID);
|
||||
return AnyEntitiesIntersecting(mapUid, worldAABB, flags, lookupQuery, xformQuery, uid);
|
||||
return AnyEntitiesIntersecting(mapUid, worldAABB, flags, uid);
|
||||
}
|
||||
|
||||
public bool AnyEntitiesInRange(EntityUid uid, float range, LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
var mapPos = Transform(uid).MapPosition;
|
||||
var mapPos = _xformQuery.GetComponent(uid).MapPosition;
|
||||
|
||||
if (mapPos.MapId == MapId.Nullspace) return false;
|
||||
if (mapPos.MapId == MapId.Nullspace)
|
||||
return false;
|
||||
|
||||
var rangeVec = new Vector2(range, range);
|
||||
|
||||
var worldAABB = new Box2(mapPos.Position - rangeVec, mapPos.Position + rangeVec);
|
||||
var lookupQuery = GetEntityQuery<BroadphaseComponent>();
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
|
||||
foreach (var grid in _mapManager.FindGridsIntersecting(mapPos.MapId, worldAABB))
|
||||
const bool found = false;
|
||||
var state = (this, worldAABB, flags, found, uid);
|
||||
|
||||
_mapManager.FindGridsIntersecting(mapPos.MapId, worldAABB, ref state, static (
|
||||
EntityUid gridUid,
|
||||
MapGridComponent _, ref (
|
||||
EntityLookupSystem lookup,
|
||||
Box2 worldAABB,
|
||||
LookupFlags flags,
|
||||
bool found,
|
||||
EntityUid ignored) tuple) =>
|
||||
{
|
||||
if (AnyEntitiesIntersecting(grid.Owner, worldAABB, flags, lookupQuery, xformQuery, uid))
|
||||
return true;
|
||||
}
|
||||
if (tuple.lookup.AnyEntitiesIntersecting(gridUid, tuple.worldAABB, tuple.flags, tuple.ignored))
|
||||
{
|
||||
tuple.found = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
var mapUid = _mapManager.GetMapEntityId(mapPos.MapId);
|
||||
return AnyEntitiesIntersecting(mapUid, worldAABB, flags, lookupQuery, xformQuery, uid);
|
||||
return AnyEntitiesIntersecting(mapUid, worldAABB, flags, uid);
|
||||
}
|
||||
|
||||
public HashSet<EntityUid> GetEntitiesInRange(EntityUid uid, float range, LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
var mapPos = Transform(uid).MapPosition;
|
||||
var mapPos = _xformQuery.GetComponent(uid).MapPosition;
|
||||
|
||||
if (mapPos.MapId == MapId.Nullspace) return new HashSet<EntityUid>();
|
||||
if (mapPos.MapId == MapId.Nullspace)
|
||||
return new HashSet<EntityUid>();
|
||||
|
||||
var intersecting = GetEntitiesInRange(mapPos, range, flags);
|
||||
intersecting.Remove(uid);
|
||||
@@ -490,12 +533,13 @@ public sealed partial class EntityLookupSystem
|
||||
|
||||
public HashSet<EntityUid> GetEntitiesIntersecting(EntityUid uid, LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
var xform = Transform(uid);
|
||||
var xform = _xformQuery.GetComponent(uid);
|
||||
var mapId = xform.MapID;
|
||||
|
||||
if (mapId == MapId.Nullspace) return new HashSet<EntityUid>();
|
||||
if (mapId == MapId.Nullspace)
|
||||
return new HashSet<EntityUid>();
|
||||
|
||||
var (worldPos, worldRot) = xform.GetWorldPositionRotation();
|
||||
var (worldPos, worldRot) = _transform.GetWorldPositionRotation(xform);
|
||||
var bounds = GetAABBNoContainer(uid, worldPos, worldRot);
|
||||
|
||||
var intersecting = GetEntitiesIntersecting(mapId, bounds, flags);
|
||||
@@ -509,7 +553,8 @@ public sealed partial class EntityLookupSystem
|
||||
|
||||
public bool AnyEntitiesIntersecting(EntityCoordinates coordinates, LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
if (!coordinates.IsValid(EntityManager)) return false;
|
||||
if (!coordinates.IsValid(EntityManager))
|
||||
return false;
|
||||
|
||||
var mapPos = coordinates.ToMap(EntityManager, _transform);
|
||||
return AnyEntitiesIntersecting(mapPos, flags);
|
||||
@@ -517,7 +562,8 @@ public sealed partial class EntityLookupSystem
|
||||
|
||||
public bool AnyEntitiesInRange(EntityCoordinates coordinates, float range, LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
if (!coordinates.IsValid(EntityManager)) return false;
|
||||
if (!coordinates.IsValid(EntityManager))
|
||||
return false;
|
||||
|
||||
var mapPos = coordinates.ToMap(EntityManager, _transform);
|
||||
return AnyEntitiesInRange(mapPos, range, flags);
|
||||
@@ -581,7 +627,8 @@ public sealed partial class EntityLookupSystem
|
||||
{
|
||||
DebugTools.Assert(range > 0, "Range must be a positive float");
|
||||
|
||||
if (mapId == MapId.Nullspace) return new HashSet<EntityUid>();
|
||||
if (mapId == MapId.Nullspace)
|
||||
return new HashSet<EntityUid>();
|
||||
|
||||
// TODO: Actual circles
|
||||
var rangeVec = new Vector2(range, range);
|
||||
@@ -601,7 +648,7 @@ public sealed partial class EntityLookupSystem
|
||||
// Technically this doesn't consider anything overlapping from outside the grid but is this an issue?
|
||||
if (!_mapManager.TryGetGrid(gridId, out var grid)) return new HashSet<EntityUid>();
|
||||
|
||||
var lookup = Comp<BroadphaseComponent>(gridId);
|
||||
var lookup = _broadQuery.GetComponent(gridId);
|
||||
var intersecting = new HashSet<EntityUid>();
|
||||
var tileSize = grid.TileSize;
|
||||
|
||||
@@ -647,8 +694,7 @@ public sealed partial class EntityLookupSystem
|
||||
}
|
||||
}
|
||||
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
AddContained(intersecting, flags, xformQuery);
|
||||
AddContained(intersecting, flags);
|
||||
|
||||
return intersecting;
|
||||
}
|
||||
@@ -656,8 +702,10 @@ public sealed partial class EntityLookupSystem
|
||||
public HashSet<EntityUid> GetEntitiesIntersecting(EntityUid gridId, Vector2i gridIndices, LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
// Technically this doesn't consider anything overlapping from outside the grid but is this an issue?
|
||||
if (!_mapManager.TryGetGrid(gridId, out var grid)) return new HashSet<EntityUid>();
|
||||
var lookup = Comp<BroadphaseComponent>(gridId);
|
||||
if (!_mapManager.TryGetGrid(gridId, out var grid))
|
||||
return new HashSet<EntityUid>();
|
||||
|
||||
var lookup = _broadQuery.GetComponent(gridId);
|
||||
var tileSize = grid.TileSize;
|
||||
var aabb = GetLocalBounds(gridIndices, tileSize);
|
||||
return GetEntitiesIntersecting(lookup, aabb, flags);
|
||||
@@ -707,22 +755,20 @@ public sealed partial class EntityLookupSystem
|
||||
}, aabb);
|
||||
}
|
||||
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
AddContained(intersecting, flags, xformQuery);
|
||||
AddContained(intersecting, flags);
|
||||
|
||||
return intersecting;
|
||||
}
|
||||
|
||||
public HashSet<EntityUid> GetEntitiesIntersecting(EntityUid gridId, Box2 worldAABB, LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
if (!_mapManager.GridExists(gridId)) return new HashSet<EntityUid>();
|
||||
if (!_mapManager.GridExists(gridId))
|
||||
return new HashSet<EntityUid>();
|
||||
|
||||
var lookupQuery = GetEntityQuery<BroadphaseComponent>();
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
var intersecting = new HashSet<EntityUid>();
|
||||
|
||||
AddEntitiesIntersecting(gridId, intersecting, worldAABB, flags, lookupQuery, xformQuery);
|
||||
AddContained(intersecting, flags, xformQuery);
|
||||
AddEntitiesIntersecting(gridId, intersecting, worldAABB, flags);
|
||||
AddContained(intersecting, flags);
|
||||
|
||||
return intersecting;
|
||||
}
|
||||
@@ -731,12 +777,10 @@ public sealed partial class EntityLookupSystem
|
||||
{
|
||||
if (!_mapManager.GridExists(gridId)) return new HashSet<EntityUid>();
|
||||
|
||||
var lookupQuery = GetEntityQuery<BroadphaseComponent>();
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
var intersecting = new HashSet<EntityUid>();
|
||||
|
||||
AddEntitiesIntersecting(gridId, intersecting, worldBounds, flags, lookupQuery, xformQuery);
|
||||
AddContained(intersecting, flags, xformQuery);
|
||||
AddEntitiesIntersecting(gridId, intersecting, worldBounds, flags);
|
||||
AddContained(intersecting, flags);
|
||||
|
||||
return intersecting;
|
||||
}
|
||||
@@ -754,8 +798,7 @@ public sealed partial class EntityLookupSystem
|
||||
public HashSet<EntityUid> GetEntitiesIntersecting(BroadphaseComponent component, ref Box2 worldAABB, LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
var intersecting = new HashSet<EntityUid>();
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
var localAABB = xformQuery.GetComponent(component.Owner).InvWorldMatrix.TransformBox(worldAABB);
|
||||
var localAABB = _transform.GetInvWorldMatrix(component.Owner).TransformBox(worldAABB);
|
||||
|
||||
if ((flags & LookupFlags.Dynamic) != 0x0)
|
||||
{
|
||||
@@ -793,7 +836,7 @@ public sealed partial class EntityLookupSystem
|
||||
}, localAABB, (flags & LookupFlags.Approximate) != 0x0);
|
||||
}
|
||||
|
||||
AddContained(intersecting, flags, xformQuery);
|
||||
AddContained(intersecting, flags);
|
||||
|
||||
return intersecting;
|
||||
}
|
||||
@@ -838,7 +881,7 @@ public sealed partial class EntityLookupSystem
|
||||
}, localAABB, (flags & LookupFlags.Approximate) != 0x0);
|
||||
}
|
||||
|
||||
AddContained(intersecting, flags, GetEntityQuery<TransformComponent>());
|
||||
AddContained(intersecting, flags);
|
||||
|
||||
return intersecting;
|
||||
}
|
||||
@@ -850,36 +893,24 @@ public sealed partial class EntityLookupSystem
|
||||
/// <summary>
|
||||
/// Gets the relevant <see cref="BroadphaseComponent"/> that intersects the specified area.
|
||||
/// </summary>
|
||||
public IEnumerable<BroadphaseComponent> FindLookupsIntersecting(MapId mapId, Box2 worldAABB)
|
||||
public void FindLookupsIntersecting(MapId mapId, Box2Rotated worldBounds, ComponentQueryCallback<BroadphaseComponent> callback)
|
||||
{
|
||||
if (mapId == MapId.Nullspace) yield break;
|
||||
if (mapId == MapId.Nullspace)
|
||||
return;
|
||||
|
||||
var lookupQuery = GetEntityQuery<BroadphaseComponent>();
|
||||
var mapUid = _mapManager.GetMapEntityId(mapId);
|
||||
callback(mapUid, _broadQuery.GetComponent(mapUid));
|
||||
|
||||
yield return lookupQuery.GetComponent(_mapManager.GetMapEntityId(mapId));
|
||||
var state = (callback, _broadQuery);
|
||||
|
||||
foreach (var grid in _mapManager.FindGridsIntersecting(mapId, worldAABB))
|
||||
{
|
||||
yield return lookupQuery.GetComponent(grid.Owner);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the relevant <see cref="BroadphaseComponent"/> that intersects the specified area.
|
||||
/// </summary>
|
||||
public IEnumerable<BroadphaseComponent> FindLookupsIntersecting(MapId mapId, Box2Rotated worldBounds)
|
||||
{
|
||||
if (mapId == MapId.Nullspace) yield break;
|
||||
|
||||
var lookupQuery = GetEntityQuery<BroadphaseComponent>();
|
||||
|
||||
yield return lookupQuery.GetComponent(_mapManager.GetMapEntityId(mapId));
|
||||
|
||||
// Copy-paste with above but the query may differ slightly internally.
|
||||
foreach (var grid in _mapManager.FindGridsIntersecting(mapId, worldBounds))
|
||||
{
|
||||
yield return lookupQuery.GetComponent(grid.Owner);
|
||||
}
|
||||
_mapManager.FindGridsIntersecting(mapId, worldBounds, ref state,
|
||||
static (EntityUid uid, MapGridComponent grid,
|
||||
ref (ComponentQueryCallback<BroadphaseComponent> callback, EntityQuery<BroadphaseComponent> _broadQuery)
|
||||
tuple) =>
|
||||
{
|
||||
tuple.callback(uid, tuple._broadQuery.GetComponent(uid));
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -904,8 +935,7 @@ public sealed partial class EntityLookupSystem
|
||||
|
||||
if (worldMatrix == null || angle == null)
|
||||
{
|
||||
var gridXform = Transform(tileRef.GridUid);
|
||||
var (_, wAng, wMat) = gridXform.GetWorldPositionRotationMatrix();
|
||||
var (_, wAng, wMat) = _transform.GetWorldPositionRotationMatrix(tileRef.GridUid);
|
||||
worldMatrix = wMat;
|
||||
angle = wAng;
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ namespace Robust.Shared.GameObjects
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly INetManager _netMan = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
private EntityQuery<BroadphaseComponent> _broadQuery;
|
||||
|
||||
@@ -10,11 +10,12 @@ using Robust.Shared.Random;
|
||||
namespace Robust.Shared.GameObjects;
|
||||
public abstract class SharedAudioSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _protoMan = default!;
|
||||
[Dependency] protected readonly IConfigurationManager CfgManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _protoMan = default!;
|
||||
[Dependency] protected readonly IRobustRandom RandMan = default!;
|
||||
[Dependency] protected readonly ISharedPlayerManager PlayerManager = default!;
|
||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Default max range at which the sound can be heard.
|
||||
@@ -292,7 +293,7 @@ public abstract class SharedAudioSystem : EntitySystem
|
||||
protected EntityCoordinates GetFallbackCoordinates(MapCoordinates mapCoordinates)
|
||||
{
|
||||
if (_mapManager.TryFindGridAt(mapCoordinates, out var gridUid, out var mapGrid))
|
||||
return new EntityCoordinates(gridUid, mapGrid.WorldToLocal(mapCoordinates.Position));
|
||||
return new EntityCoordinates(gridUid, _map.WorldToLocal(gridUid, mapGrid, mapCoordinates.Position));
|
||||
|
||||
if (_mapManager.HasMapEntity(mapCoordinates.MapId))
|
||||
return new EntityCoordinates(_mapManager.GetMapEntityId(mapCoordinates.MapId), mapCoordinates.Position);
|
||||
|
||||
@@ -7,6 +7,7 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Map.Events;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Collision.Shapes;
|
||||
@@ -21,9 +22,9 @@ namespace Robust.Shared.GameObjects
|
||||
public abstract class SharedGridFixtureSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly FixtureSystem _fixtures = default!;
|
||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
protected ISawmill Sawmill = default!;
|
||||
private bool _enabled;
|
||||
private float _fixtureEnlargement;
|
||||
|
||||
@@ -33,12 +34,17 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
base.Initialize();
|
||||
UpdatesBefore.Add(typeof(SharedBroadphaseSystem));
|
||||
Sawmill = Logger.GetSawmill("physics");
|
||||
|
||||
_cfg.OnValueChanged(CVars.GenerateGridFixtures, SetEnabled, true);
|
||||
_cfg.OnValueChanged(CVars.GridFixtureEnlargement, SetEnlargement, true);
|
||||
|
||||
SubscribeLocalEvent<GridInitializeEvent>(OnGridInit);
|
||||
SubscribeLocalEvent<RegenerateGridBoundsEvent>(OnGridBoundsRegenerate);
|
||||
}
|
||||
|
||||
private void OnGridBoundsRegenerate(ref RegenerateGridBoundsEvent ev)
|
||||
{
|
||||
RegenerateCollision(ev.Entity, ev.ChunkRectangles, ev.RemovedChunks);
|
||||
}
|
||||
|
||||
protected virtual void OnGridInit(GridInitializeEvent ev)
|
||||
@@ -48,7 +54,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
// This will also check for grid splits if applicable.
|
||||
var grid = Comp<MapGridComponent>(ev.EntityUid);
|
||||
grid.RegenerateCollision(grid.GetMapChunks().Values.ToHashSet());
|
||||
_map.RegenerateCollision(ev.EntityUid, grid, _map.GetMapChunks(ev.EntityUid, grid).Values.ToHashSet());
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
@@ -68,23 +74,24 @@ namespace Robust.Shared.GameObjects
|
||||
Dictionary<MapChunk, List<Box2i>> mapChunks,
|
||||
List<MapChunk> removedChunks)
|
||||
{
|
||||
if (!_enabled) return;
|
||||
if (!_enabled)
|
||||
return;
|
||||
|
||||
if (!EntityManager.TryGetComponent(uid, out PhysicsComponent? body))
|
||||
{
|
||||
Sawmill.Error($"Trying to regenerate collision for {uid} that doesn't have {nameof(body)}");
|
||||
Log.Error($"Trying to regenerate collision for {uid} that doesn't have {nameof(body)}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntityManager.TryGetComponent(uid, out FixturesComponent? manager))
|
||||
{
|
||||
Sawmill.Error($"Trying to regenerate collision for {uid} that doesn't have {nameof(manager)}");
|
||||
Log.Error($"Trying to regenerate collision for {uid} that doesn't have {nameof(manager)}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntityManager.TryGetComponent(uid, out TransformComponent? xform))
|
||||
{
|
||||
Sawmill.Error($"Trying to regenerate collision for {uid} that doesn't have {nameof(TransformComponent)}");
|
||||
Log.Error($"Trying to regenerate collision for {uid} that doesn't have {nameof(TransformComponent)}");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Map.Enumerators;
|
||||
using Robust.Shared.Map.Events;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -224,7 +227,7 @@ public abstract partial class SharedMapSystem
|
||||
if (chunkData.IsDeleted())
|
||||
continue;
|
||||
|
||||
var chunk = component.GetOrAddChunk(chunkData.Index);
|
||||
var chunk = GetOrAddChunk(uid, component, chunkData.Index);
|
||||
chunk.SuppressCollisionRegeneration = true;
|
||||
DebugTools.Assert(chunkData.TileData.Length == component.ChunkSize * component.ChunkSize);
|
||||
|
||||
@@ -237,7 +240,7 @@ public abstract partial class SharedMapSystem
|
||||
if (chunk.GetTile(x, y) == tile)
|
||||
continue;
|
||||
|
||||
chunk.SetTile(x, y, tile);
|
||||
SetChunkTile(uid, component, chunk, x, y, tile);
|
||||
modified.Add((new Vector2i(chunk.X * component.ChunkSize + x, chunk.Y * component.ChunkSize + y), tile));
|
||||
}
|
||||
}
|
||||
@@ -247,13 +250,13 @@ public abstract partial class SharedMapSystem
|
||||
{
|
||||
if (chunkData.IsDeleted())
|
||||
{
|
||||
component.RemoveChunk(chunkData.Index);
|
||||
RemoveChunk(uid, component, chunkData.Index);
|
||||
continue;
|
||||
}
|
||||
|
||||
var chunk = component.GetOrAddChunk(chunkData.Index);
|
||||
var chunk = GetOrAddChunk(uid, component, chunkData.Index);
|
||||
chunk.SuppressCollisionRegeneration = false;
|
||||
component.RegenerateCollision(chunk);
|
||||
RegenerateCollision(uid, component, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,12 +266,12 @@ public abstract partial class SharedMapSystem
|
||||
foreach (var index in component.Chunks.Keys)
|
||||
{
|
||||
if (!state.FullGridData.ContainsKey(index))
|
||||
component.RemoveChunk(index);
|
||||
RemoveChunk(uid, component, index);
|
||||
}
|
||||
|
||||
foreach (var (index, tiles) in state.FullGridData)
|
||||
{
|
||||
var chunk = component.GetOrAddChunk(index);
|
||||
var chunk = GetOrAddChunk(uid, component, index);
|
||||
chunk.SuppressCollisionRegeneration = true;
|
||||
DebugTools.Assert(tiles.Length == component.ChunkSize * component.ChunkSize);
|
||||
|
||||
@@ -281,13 +284,13 @@ public abstract partial class SharedMapSystem
|
||||
if (chunk.GetTile(x, y) == tile)
|
||||
continue;
|
||||
|
||||
chunk.SetTile(x, y, tile);
|
||||
SetChunkTile(uid, component, chunk, x, y, tile);
|
||||
modified.Add((new Vector2i(chunk.X * component.ChunkSize + x, chunk.Y * component.ChunkSize + y), tile));
|
||||
}
|
||||
}
|
||||
|
||||
chunk.SuppressCollisionRegeneration = false;
|
||||
component.RegenerateCollision(chunk);
|
||||
RegenerateCollision(uid, component, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,7 +303,7 @@ public abstract partial class SharedMapSystem
|
||||
{
|
||||
if (args.FromTick <= component.CreationTick)
|
||||
{
|
||||
GetFullState(component, ref args);
|
||||
GetFullState(uid, component, ref args);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -324,7 +327,7 @@ public abstract partial class SharedMapSystem
|
||||
chunkData.Add(ChunkDatum.CreateDeleted(indices));
|
||||
}
|
||||
|
||||
foreach (var (index, chunk) in component.GetMapChunks())
|
||||
foreach (var (index, chunk) in GetMapChunks(uid, component))
|
||||
{
|
||||
if (chunk.LastTileModifiedTick < fromTick)
|
||||
continue;
|
||||
@@ -348,11 +351,11 @@ public abstract partial class SharedMapSystem
|
||||
args.State = new MapGridComponentState(component.ChunkSize, chunkData);
|
||||
}
|
||||
|
||||
private void GetFullState(MapGridComponent component, ref ComponentGetState args)
|
||||
private void GetFullState(EntityUid uid, MapGridComponent component, ref ComponentGetState args)
|
||||
{
|
||||
var chunkData = new Dictionary<Vector2i, Tile[]>();
|
||||
|
||||
foreach (var (index, chunk) in component.GetMapChunks())
|
||||
foreach (var (index, chunk) in GetMapChunks(uid, component))
|
||||
{
|
||||
var tileBuffer = new Tile[component.ChunkSize * (uint)component.ChunkSize];
|
||||
|
||||
@@ -385,7 +388,6 @@ public abstract partial class SharedMapSystem
|
||||
|
||||
foreach (var chunk in component.Chunks.Values)
|
||||
{
|
||||
chunk.TileModified += component.OnTileModified;
|
||||
chunk.LastTileModifiedTick = curTick;
|
||||
}
|
||||
|
||||
@@ -445,7 +447,7 @@ public abstract partial class SharedMapSystem
|
||||
if (!Resolve(uid, ref xform))
|
||||
return new Box2();
|
||||
|
||||
var (worldPos, worldRot) = _transform.GetWorldPositionRotation(xform, GetEntityQuery<TransformComponent>());
|
||||
var (worldPos, worldRot) = _transform.GetWorldPositionRotation(xform);
|
||||
var aabb = grid.LocalAABB.Translated(worldPos);
|
||||
|
||||
return new Box2Rotated(aabb, worldRot, worldPos).CalcBoundingBox();
|
||||
@@ -456,7 +458,7 @@ public abstract partial class SharedMapSystem
|
||||
DebugTools.Assert(!EntityManager.HasComponent<MapComponent>(uid));
|
||||
var aabb = GetWorldAABB(uid, grid);
|
||||
|
||||
if (!TryComp<TransformComponent>(uid, out var xform))
|
||||
if (!_xformQuery.TryGetComponent(uid, out var xform))
|
||||
return;
|
||||
|
||||
if (TryComp<GridTreeComponent>(xform.MapUid, out var gridTree))
|
||||
@@ -486,4 +488,796 @@ public abstract partial class SharedMapSystem
|
||||
movedGrids.MovedGrids.Remove(uid);
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveChunk(EntityUid uid, MapGridComponent grid, Vector2i origin)
|
||||
{
|
||||
if (!grid.Chunks.TryGetValue(origin, out var chunk))
|
||||
return;
|
||||
|
||||
if (_netManager.IsServer)
|
||||
grid.ChunkDeletionHistory.Add((_timing.CurTick, chunk.Indices));
|
||||
|
||||
chunk.Fixtures.Clear();
|
||||
grid.Chunks.Remove(origin);
|
||||
|
||||
if (grid.Chunks.Count == 0)
|
||||
RaiseLocalEvent(uid, new EmptyGridEvent { GridId = uid }, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Regenerates the chunk local bounds of this chunk.
|
||||
/// </summary>
|
||||
private void RegenerateCollision(EntityUid uid, MapGridComponent grid, MapChunk mapChunk)
|
||||
{
|
||||
RegenerateCollision(uid, grid, new HashSet<MapChunk> { mapChunk });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Regenerate collision for multiple chunks at once; faster than doing it individually.
|
||||
/// </summary>
|
||||
internal void RegenerateCollision(EntityUid uid, MapGridComponent grid, IReadOnlySet<MapChunk> chunks)
|
||||
{
|
||||
if (HasComp<MapComponent>(uid))
|
||||
return;
|
||||
|
||||
var chunkRectangles = new Dictionary<MapChunk, List<Box2i>>(chunks.Count);
|
||||
var removedChunks = new List<MapChunk>();
|
||||
|
||||
foreach (var mapChunk in chunks)
|
||||
{
|
||||
// Even if the chunk is still removed still need to make sure bounds are updated (for now...)
|
||||
// generate collision rectangles for this chunk based on filled tiles.
|
||||
GridChunkPartition.PartitionChunk(mapChunk, out var localBounds, out var rectangles);
|
||||
mapChunk.CachedBounds = localBounds;
|
||||
|
||||
if (mapChunk.FilledTiles > 0)
|
||||
chunkRectangles.Add(mapChunk, rectangles);
|
||||
else
|
||||
{
|
||||
// Gone. Reduced to atoms
|
||||
// Need to do this before RemoveChunk because it clears fixtures.
|
||||
FixturesComponent? manager = null;
|
||||
PhysicsComponent? body = null;
|
||||
TransformComponent? xform = null;
|
||||
|
||||
foreach (var fixture in mapChunk.Fixtures)
|
||||
{
|
||||
_fixtures.DestroyFixture(uid, fixture, false, manager: manager, body: body, xform: xform);
|
||||
}
|
||||
|
||||
RemoveChunk(uid, grid, mapChunk.Indices);
|
||||
removedChunks.Add(mapChunk);
|
||||
}
|
||||
}
|
||||
|
||||
grid.LocalAABB = new Box2();
|
||||
|
||||
foreach (var chunk in grid.Chunks.Values)
|
||||
{
|
||||
var chunkBounds = chunk.CachedBounds;
|
||||
|
||||
if (chunkBounds.Size.Equals(Vector2i.Zero))
|
||||
continue;
|
||||
|
||||
if (grid.LocalAABB.Size == Vector2.Zero)
|
||||
{
|
||||
var gridBounds = chunkBounds.Translated(chunk.Indices * chunk.ChunkSize);
|
||||
grid.LocalAABB = gridBounds;
|
||||
}
|
||||
else
|
||||
{
|
||||
var gridBounds = chunkBounds.Translated(chunk.Indices * chunk.ChunkSize);
|
||||
grid.LocalAABB = grid.LocalAABB.Union(gridBounds);
|
||||
}
|
||||
}
|
||||
|
||||
// May have been deleted from the bulk update above!
|
||||
if (Deleted(uid))
|
||||
return;
|
||||
|
||||
_physics.WakeBody(uid);
|
||||
OnGridBoundsChange(uid, grid);
|
||||
var ev = new RegenerateGridBoundsEvent(uid, chunkRectangles, removedChunks);
|
||||
RaiseLocalEvent(ref ev);
|
||||
}
|
||||
|
||||
#region TileAccess
|
||||
|
||||
public TileRef GetTileRef(EntityUid uid, MapGridComponent grid, MapCoordinates coords)
|
||||
{
|
||||
return GetTileRef(uid, grid, CoordinatesToTile(uid, grid, coords));
|
||||
}
|
||||
|
||||
public TileRef GetTileRef(EntityUid uid, MapGridComponent grid, EntityCoordinates coords)
|
||||
{
|
||||
return GetTileRef(uid, grid, CoordinatesToTile(uid, grid, coords));
|
||||
}
|
||||
|
||||
public TileRef GetTileRef(EntityUid uid, MapGridComponent grid, Vector2i tileCoordinates)
|
||||
{
|
||||
var chunkIndices = GridTileToChunkIndices(uid, grid, tileCoordinates);
|
||||
|
||||
if (!grid.Chunks.TryGetValue(chunkIndices, out var output))
|
||||
{
|
||||
// Chunk doesn't exist, return a tileRef to an empty (space) tile.
|
||||
return new TileRef(uid, tileCoordinates.X, tileCoordinates.Y, default);
|
||||
}
|
||||
|
||||
var chunkTileIndices = output.GridTileToChunkTile(tileCoordinates);
|
||||
return GetTileRef(uid, grid, output, (ushort)chunkTileIndices.X, (ushort)chunkTileIndices.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the tile at the given chunk indices.
|
||||
/// </summary>
|
||||
/// <param name="mapChunk"></param>
|
||||
/// <param name="xIndex">The X tile index relative to the chunk origin.</param>
|
||||
/// <param name="yIndex">The Y tile index relative to the chunk origin.</param>
|
||||
/// <returns>A reference to a tile.</returns>
|
||||
internal TileRef GetTileRef(EntityUid uid, MapGridComponent grid, MapChunk mapChunk, ushort xIndex, ushort yIndex)
|
||||
{
|
||||
if (xIndex >= mapChunk.ChunkSize)
|
||||
throw new ArgumentOutOfRangeException(nameof(xIndex), "Tile indices out of bounds.");
|
||||
|
||||
if (yIndex >= mapChunk.ChunkSize)
|
||||
throw new ArgumentOutOfRangeException(nameof(yIndex), "Tile indices out of bounds.");
|
||||
|
||||
var indices = mapChunk.ChunkTileToGridTile(new Vector2i(xIndex, yIndex));
|
||||
return new TileRef(uid, indices, mapChunk.GetTile(xIndex, yIndex));
|
||||
}
|
||||
|
||||
public IEnumerable<TileRef> GetAllTiles(EntityUid uid, MapGridComponent grid, bool ignoreEmpty = true)
|
||||
{
|
||||
foreach (var kvChunk in grid.Chunks)
|
||||
{
|
||||
var chunk = kvChunk.Value;
|
||||
for (ushort x = 0; x < grid.ChunkSize; x++)
|
||||
{
|
||||
for (ushort y = 0; y < grid.ChunkSize; y++)
|
||||
{
|
||||
var tile = chunk.GetTile(x, y);
|
||||
|
||||
if (ignoreEmpty && tile.IsEmpty)
|
||||
continue;
|
||||
|
||||
var (gridX, gridY) = new Vector2i(x, y) + chunk.Indices * grid.ChunkSize;
|
||||
yield return new TileRef(uid, gridX, gridY, tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public GridTileEnumerator GetAllTilesEnumerator(EntityUid uid, MapGridComponent grid, bool ignoreEmpty = true)
|
||||
{
|
||||
return new GridTileEnumerator(uid, grid.Chunks.GetEnumerator(), grid.ChunkSize, ignoreEmpty);
|
||||
}
|
||||
|
||||
public void SetTile(EntityUid uid, MapGridComponent grid, EntityCoordinates coords, Tile tile)
|
||||
{
|
||||
var localTile = CoordinatesToTile(uid, grid, coords);
|
||||
SetTile(uid, grid, new Vector2i(localTile.X, localTile.Y), tile);
|
||||
}
|
||||
|
||||
public void SetTile(EntityUid uid, MapGridComponent grid, Vector2i gridIndices, Tile tile)
|
||||
{
|
||||
var (chunk, chunkTile) = ChunkAndOffsetForTile(uid, grid, gridIndices);
|
||||
SetChunkTile(uid, grid, chunk, (ushort)chunkTile.X, (ushort)chunkTile.Y, tile);
|
||||
// Ideally we'd to this here for consistency but apparently tile modified does it or something.
|
||||
// Yeah it's noodly.
|
||||
// RegenerateCollision(chunk);
|
||||
}
|
||||
|
||||
public void SetTiles(EntityUid uid, MapGridComponent grid, List<(Vector2i GridIndices, Tile Tile)> tiles)
|
||||
{
|
||||
if (tiles.Count == 0)
|
||||
return;
|
||||
|
||||
var chunks = new HashSet<MapChunk>(Math.Max(1, tiles.Count / grid.ChunkSize));
|
||||
|
||||
foreach (var (gridIndices, tile) in tiles)
|
||||
{
|
||||
var (chunk, chunkTile) = ChunkAndOffsetForTile(uid, grid, gridIndices);
|
||||
chunks.Add(chunk);
|
||||
chunk.SuppressCollisionRegeneration = true;
|
||||
SetChunkTile(uid, grid, chunk, (ushort)chunkTile.X, (ushort)chunkTile.Y, tile);
|
||||
}
|
||||
|
||||
foreach (var chunk in chunks)
|
||||
{
|
||||
chunk.SuppressCollisionRegeneration = false;
|
||||
}
|
||||
|
||||
RegenerateCollision(uid, grid, chunks);
|
||||
}
|
||||
|
||||
public IEnumerable<TileRef> GetLocalTilesIntersecting(EntityUid uid, MapGridComponent grid, Box2Rotated localArea, bool ignoreEmpty = true,
|
||||
Predicate<TileRef>? predicate = null)
|
||||
{
|
||||
var localAABB = localArea.CalcBoundingBox();
|
||||
return GetLocalTilesIntersecting(uid, grid, localAABB, ignoreEmpty, predicate);
|
||||
}
|
||||
|
||||
public IEnumerable<TileRef> GetTilesIntersecting(EntityUid uid, MapGridComponent grid, Box2Rotated worldArea, bool ignoreEmpty = true,
|
||||
Predicate<TileRef>? predicate = null)
|
||||
{
|
||||
var matrix = _transform.GetInvWorldMatrix(uid);
|
||||
var localArea = matrix.TransformBox(worldArea);
|
||||
|
||||
foreach (var tile in GetLocalTilesIntersecting(uid, grid, localArea, ignoreEmpty, predicate))
|
||||
{
|
||||
yield return tile;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<TileRef> GetTilesIntersecting(EntityUid uid, MapGridComponent grid, Box2 worldArea, bool ignoreEmpty = true,
|
||||
Predicate<TileRef>? predicate = null)
|
||||
{
|
||||
var matrix = _transform.GetInvWorldMatrix(uid);
|
||||
var localArea = matrix.TransformBox(worldArea);
|
||||
|
||||
foreach (var tile in GetLocalTilesIntersecting(uid, grid, localArea, ignoreEmpty, predicate))
|
||||
{
|
||||
yield return tile;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<TileRef> GetLocalTilesIntersecting(EntityUid uid, MapGridComponent grid, Box2 localArea, bool ignoreEmpty = true,
|
||||
Predicate<TileRef>? predicate = null)
|
||||
{
|
||||
// TODO: Should move the intersecting calls onto mapmanager system and then allow people to pass in xform / xformquery
|
||||
// that way we can avoid the GetComp here.
|
||||
var gridTileLb = new Vector2i((int)Math.Floor(localArea.Left), (int)Math.Floor(localArea.Bottom));
|
||||
// If we have 20.1 we want to include that tile but if we have 20 then we don't.
|
||||
var gridTileRt = new Vector2i((int)Math.Ceiling(localArea.Right), (int)Math.Ceiling(localArea.Top));
|
||||
|
||||
for (var x = gridTileLb.X; x < gridTileRt.X; x++)
|
||||
{
|
||||
for (var y = gridTileLb.Y; y < gridTileRt.Y; y++)
|
||||
{
|
||||
var gridChunk = GridTileToChunkIndices(uid, grid, new Vector2i(x, y));
|
||||
|
||||
if (grid.Chunks.TryGetValue(gridChunk, out var chunk))
|
||||
{
|
||||
var chunkTile = chunk.GridTileToChunkTile(new Vector2i(x, y));
|
||||
var tile = GetTileRef(uid, grid, chunk, (ushort)chunkTile.X, (ushort)chunkTile.Y);
|
||||
|
||||
if (ignoreEmpty && tile.Tile.IsEmpty)
|
||||
continue;
|
||||
|
||||
if (predicate == null || predicate(tile))
|
||||
yield return tile;
|
||||
}
|
||||
else if (!ignoreEmpty)
|
||||
{
|
||||
var tile = new TileRef(uid, x, y, Tile.Empty);
|
||||
|
||||
if (predicate == null || predicate(tile))
|
||||
yield return tile;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<TileRef> GetTilesIntersecting(EntityUid uid, MapGridComponent grid, Circle worldArea, bool ignoreEmpty = true,
|
||||
Predicate<TileRef>? predicate = null)
|
||||
{
|
||||
var aabb = new Box2(worldArea.Position.X - worldArea.Radius, worldArea.Position.Y - worldArea.Radius,
|
||||
worldArea.Position.X + worldArea.Radius, worldArea.Position.Y + worldArea.Radius);
|
||||
var circleGridPos = new EntityCoordinates(uid, WorldToLocal(uid, grid, worldArea.Position));
|
||||
|
||||
foreach (var tile in GetTilesIntersecting(uid, grid, aabb, ignoreEmpty, predicate))
|
||||
{
|
||||
var local = GridTileToLocal(uid, grid, tile.GridIndices);
|
||||
|
||||
if (!local.TryDistance(EntityManager, _transform, circleGridPos, out var distance))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (distance <= worldArea.Radius)
|
||||
{
|
||||
yield return tile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryGetTile(EntityUid uid, MapGridComponent grid, Vector2i indices, bool ignoreEmpty, [NotNullWhen(true)] out TileRef? tileRef, Predicate<TileRef>? predicate = null)
|
||||
{
|
||||
// Similar to TryGetTileRef but for the tiles intersecting iterators.
|
||||
var gridChunk = GridTileToChunkIndices(uid, grid, indices);
|
||||
|
||||
if (grid.Chunks.TryGetValue(gridChunk, out var chunk))
|
||||
{
|
||||
var chunkTile = chunk.GridTileToChunkTile(indices);
|
||||
var tile = GetTileRef(uid, grid, chunk, (ushort)chunkTile.X, (ushort)chunkTile.Y);
|
||||
|
||||
if (ignoreEmpty && tile.Tile.IsEmpty)
|
||||
{
|
||||
tileRef = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (predicate == null || predicate(tile))
|
||||
{
|
||||
tileRef = tile;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (!ignoreEmpty)
|
||||
{
|
||||
var tile = new TileRef(uid, indices.X, indices.Y, Tile.Empty);
|
||||
|
||||
if (predicate == null || predicate(tile))
|
||||
{
|
||||
tileRef = tile;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
tileRef = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion TileAccess
|
||||
|
||||
#region ChunkAccess
|
||||
|
||||
internal MapChunk GetOrAddChunk(EntityUid uid, MapGridComponent grid, int xIndex, int yIndex)
|
||||
{
|
||||
return GetOrAddChunk(uid, grid, new Vector2i(xIndex, yIndex));
|
||||
}
|
||||
|
||||
internal bool TryGetChunk(EntityUid uid, MapGridComponent grid, Vector2i chunkIndices, [NotNullWhen(true)] out MapChunk? chunk)
|
||||
{
|
||||
return grid.Chunks.TryGetValue(chunkIndices, out chunk);
|
||||
}
|
||||
|
||||
internal MapChunk GetOrAddChunk(EntityUid uid, MapGridComponent grid, Vector2i chunkIndices)
|
||||
{
|
||||
if (grid.Chunks.TryGetValue(chunkIndices, out var output))
|
||||
return output;
|
||||
|
||||
var newChunk = new MapChunk(chunkIndices.X, chunkIndices.Y, grid.ChunkSize)
|
||||
{
|
||||
LastTileModifiedTick = _timing.CurTick
|
||||
};
|
||||
|
||||
return grid.Chunks[chunkIndices] = newChunk;
|
||||
}
|
||||
|
||||
public bool HasChunk(EntityUid uid, MapGridComponent grid, Vector2i chunkIndices)
|
||||
{
|
||||
return grid.Chunks.ContainsKey(chunkIndices);
|
||||
}
|
||||
|
||||
internal IReadOnlyDictionary<Vector2i, MapChunk> GetMapChunks(EntityUid uid, MapGridComponent grid)
|
||||
{
|
||||
return grid.Chunks;
|
||||
}
|
||||
|
||||
internal ChunkEnumerator GetMapChunks(EntityUid uid, MapGridComponent grid, Box2 worldAABB)
|
||||
{
|
||||
var localAABB = _transform.GetInvWorldMatrix(uid).TransformBox(worldAABB);
|
||||
return new ChunkEnumerator(grid.Chunks, localAABB, grid.ChunkSize);
|
||||
}
|
||||
|
||||
internal ChunkEnumerator GetMapChunks(EntityUid uid, MapGridComponent grid, Box2Rotated worldArea)
|
||||
{
|
||||
var matrix = _transform.GetInvWorldMatrix(uid);
|
||||
var localArea = matrix.TransformBox(worldArea);
|
||||
return new ChunkEnumerator(grid.Chunks, localArea, grid.ChunkSize);
|
||||
}
|
||||
|
||||
internal ChunkEnumerator GetLocalMapChunks(EntityUid uid, MapGridComponent grid, Box2 localAABB)
|
||||
{
|
||||
return new ChunkEnumerator(grid.Chunks, localAABB, grid.ChunkSize);
|
||||
}
|
||||
|
||||
#endregion ChunkAccess
|
||||
|
||||
#region SnapGridAccess
|
||||
|
||||
public int AnchoredEntityCount(EntityUid uid, MapGridComponent grid, Vector2i pos)
|
||||
{
|
||||
var gridChunkPos = GridTileToChunkIndices(uid, grid, pos);
|
||||
|
||||
if (!grid.Chunks.TryGetValue(gridChunkPos, out var chunk))
|
||||
return 0;
|
||||
|
||||
var (x, y) = chunk.GridTileToChunkTile(pos);
|
||||
return chunk.GetSnapGrid((ushort)x, (ushort)y)?.Count ?? 0; // ?
|
||||
}
|
||||
|
||||
public IEnumerable<EntityUid> GetAnchoredEntities(EntityUid uid, MapGridComponent grid, MapCoordinates coords)
|
||||
{
|
||||
return GetAnchoredEntities(uid, grid, TileIndicesFor(uid, grid, coords));
|
||||
}
|
||||
|
||||
public IEnumerable<EntityUid> GetAnchoredEntities(EntityUid uid, MapGridComponent grid, EntityCoordinates coords)
|
||||
{
|
||||
return GetAnchoredEntities(uid, grid, TileIndicesFor(uid, grid, coords));
|
||||
}
|
||||
|
||||
public IEnumerable<EntityUid> GetAnchoredEntities(EntityUid uid, MapGridComponent grid, Vector2i pos)
|
||||
{
|
||||
// Because some content stuff checks neighboring tiles (which may not actually exist) we won't just
|
||||
// create an entire chunk for it.
|
||||
var gridChunkPos = GridTileToChunkIndices(uid, grid, pos);
|
||||
|
||||
if (!grid.Chunks.TryGetValue(gridChunkPos, out var chunk)) return Enumerable.Empty<EntityUid>();
|
||||
|
||||
var chunkTile = chunk.GridTileToChunkTile(pos);
|
||||
return chunk.GetSnapGridCell((ushort)chunkTile.X, (ushort)chunkTile.Y);
|
||||
}
|
||||
|
||||
public AnchoredEntitiesEnumerator GetAnchoredEntitiesEnumerator(EntityUid uid, MapGridComponent grid, Vector2i pos)
|
||||
{
|
||||
var gridChunkPos = GridTileToChunkIndices(uid, grid, pos);
|
||||
|
||||
if (!grid.Chunks.TryGetValue(gridChunkPos, out var chunk)) return AnchoredEntitiesEnumerator.Empty;
|
||||
|
||||
var chunkTile = chunk.GridTileToChunkTile(pos);
|
||||
var snapgrid = chunk.GetSnapGrid((ushort)chunkTile.X, (ushort)chunkTile.Y);
|
||||
|
||||
return snapgrid == null
|
||||
? AnchoredEntitiesEnumerator.Empty
|
||||
: new AnchoredEntitiesEnumerator(snapgrid.GetEnumerator());
|
||||
}
|
||||
|
||||
public IEnumerable<EntityUid> GetLocalAnchoredEntities(EntityUid uid, MapGridComponent grid, Box2 localAABB)
|
||||
{
|
||||
foreach (var tile in GetLocalTilesIntersecting(uid, grid, localAABB, true, null))
|
||||
{
|
||||
foreach (var ent in GetAnchoredEntities(uid, grid, tile.GridIndices))
|
||||
{
|
||||
yield return ent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<EntityUid> GetAnchoredEntities(EntityUid uid, MapGridComponent grid, Box2 worldAABB)
|
||||
{
|
||||
foreach (var tile in GetTilesIntersecting(uid, grid, worldAABB))
|
||||
{
|
||||
foreach (var ent in GetAnchoredEntities(uid, grid, tile.GridIndices))
|
||||
{
|
||||
yield return ent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<EntityUid> GetAnchoredEntities(EntityUid uid, MapGridComponent grid, Box2Rotated worldBounds)
|
||||
{
|
||||
foreach (var tile in GetTilesIntersecting(uid, grid, worldBounds))
|
||||
{
|
||||
foreach (var ent in GetAnchoredEntities(uid, grid, tile.GridIndices))
|
||||
{
|
||||
yield return ent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2i TileIndicesFor(EntityUid uid, MapGridComponent grid, EntityCoordinates coords)
|
||||
{
|
||||
#if DEBUG
|
||||
var mapId = _xformQuery.GetComponent(uid).MapID;
|
||||
DebugTools.Assert(mapId == coords.GetMapId(EntityManager));
|
||||
#endif
|
||||
|
||||
return SnapGridLocalCellFor(uid, grid, LocalToGrid(uid, grid, coords));
|
||||
}
|
||||
|
||||
public Vector2i TileIndicesFor(EntityUid uid, MapGridComponent grid, MapCoordinates worldPos)
|
||||
{
|
||||
#if DEBUG
|
||||
var mapId = _xformQuery.GetComponent(uid).MapID;
|
||||
DebugTools.Assert(mapId == worldPos.MapId);
|
||||
#endif
|
||||
|
||||
var localPos = WorldToLocal(uid, grid, worldPos.Position);
|
||||
return SnapGridLocalCellFor(uid, grid, localPos);
|
||||
}
|
||||
|
||||
private Vector2i SnapGridLocalCellFor(EntityUid uid, MapGridComponent grid, Vector2 localPos)
|
||||
{
|
||||
var x = (int)Math.Floor(localPos.X / grid.TileSize);
|
||||
var y = (int)Math.Floor(localPos.Y / grid.TileSize);
|
||||
return new Vector2i(x, y);
|
||||
}
|
||||
|
||||
public bool IsAnchored(EntityUid uid, MapGridComponent grid, EntityCoordinates coords, EntityUid euid)
|
||||
{
|
||||
var tilePos = TileIndicesFor(uid, grid, coords);
|
||||
var (chunk, chunkTile) = ChunkAndOffsetForTile(uid, grid, tilePos);
|
||||
var snapgrid = chunk.GetSnapGrid((ushort)chunkTile.X, (ushort)chunkTile.Y);
|
||||
return snapgrid?.Contains(euid) == true;
|
||||
}
|
||||
|
||||
public bool AddToSnapGridCell(EntityUid gridUid, MapGridComponent grid, Vector2i pos, EntityUid euid)
|
||||
{
|
||||
var (chunk, chunkTile) = ChunkAndOffsetForTile(gridUid, grid, pos);
|
||||
|
||||
if (chunk.GetTile((ushort)chunkTile.X, (ushort)chunkTile.Y).IsEmpty)
|
||||
return false;
|
||||
|
||||
chunk.AddToSnapGridCell((ushort)chunkTile.X, (ushort)chunkTile.Y, euid);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool AddToSnapGridCell(EntityUid gridUid, MapGridComponent grid, EntityCoordinates coords, EntityUid euid)
|
||||
{
|
||||
return AddToSnapGridCell(gridUid, grid, TileIndicesFor(gridUid, grid, coords), euid);
|
||||
}
|
||||
|
||||
public void RemoveFromSnapGridCell(EntityUid gridUid, MapGridComponent grid, Vector2i pos, EntityUid euid)
|
||||
{
|
||||
var (chunk, chunkTile) = ChunkAndOffsetForTile(gridUid, grid, pos);
|
||||
chunk.RemoveFromSnapGridCell((ushort)chunkTile.X, (ushort)chunkTile.Y, euid);
|
||||
}
|
||||
|
||||
public void RemoveFromSnapGridCell(EntityUid gridUid, MapGridComponent grid, EntityCoordinates coords, EntityUid euid)
|
||||
{
|
||||
RemoveFromSnapGridCell(gridUid, grid, TileIndicesFor(gridUid, grid, coords), euid);
|
||||
}
|
||||
|
||||
private (MapChunk, Vector2i) ChunkAndOffsetForTile(EntityUid uid, MapGridComponent grid, Vector2i pos)
|
||||
{
|
||||
var gridChunkIndices = GridTileToChunkIndices(uid, grid, pos);
|
||||
var chunk = GetOrAddChunk(uid, grid, gridChunkIndices);
|
||||
var chunkTile = chunk.GridTileToChunkTile(pos);
|
||||
return (chunk, chunkTile);
|
||||
}
|
||||
|
||||
public IEnumerable<EntityUid> GetInDir(EntityUid uid, MapGridComponent grid, EntityCoordinates position, Direction dir)
|
||||
{
|
||||
var pos = GetDirection(TileIndicesFor(uid, grid, position), dir);
|
||||
return GetAnchoredEntities(uid, grid, pos);
|
||||
}
|
||||
|
||||
public IEnumerable<EntityUid> GetOffset(EntityUid uid, MapGridComponent grid, EntityCoordinates coords, Vector2i offset)
|
||||
{
|
||||
var pos = TileIndicesFor(uid, grid, coords) + offset;
|
||||
return GetAnchoredEntities(uid, grid, pos);
|
||||
}
|
||||
|
||||
public IEnumerable<EntityUid> GetLocal(EntityUid uid, MapGridComponent grid, EntityCoordinates coords)
|
||||
{
|
||||
return GetAnchoredEntities(uid, grid, TileIndicesFor(uid, grid, coords));
|
||||
}
|
||||
|
||||
public EntityCoordinates DirectionToGrid(EntityUid uid, MapGridComponent grid, EntityCoordinates coords, Direction direction)
|
||||
{
|
||||
return GridTileToLocal(uid, grid, GetDirection(TileIndicesFor(uid, grid, coords), direction));
|
||||
}
|
||||
|
||||
public IEnumerable<EntityUid> GetCardinalNeighborCells(EntityUid uid, MapGridComponent grid, EntityCoordinates coords)
|
||||
{
|
||||
var position = TileIndicesFor(uid, grid, coords);
|
||||
foreach (var cell in GetAnchoredEntities(uid, grid, position))
|
||||
yield return cell;
|
||||
foreach (var cell in GetAnchoredEntities(uid, grid, position + new Vector2i(0, 1)))
|
||||
yield return cell;
|
||||
foreach (var cell in GetAnchoredEntities(uid, grid, position + new Vector2i(0, -1)))
|
||||
yield return cell;
|
||||
foreach (var cell in GetAnchoredEntities(uid, grid, position + new Vector2i(1, 0)))
|
||||
yield return cell;
|
||||
foreach (var cell in GetAnchoredEntities(uid, grid, position + new Vector2i(-1, 0)))
|
||||
yield return cell;
|
||||
}
|
||||
|
||||
public IEnumerable<EntityUid> GetCellsInSquareArea(EntityUid uid, MapGridComponent grid, EntityCoordinates coords, int n)
|
||||
{
|
||||
var position = TileIndicesFor(uid, grid, coords);
|
||||
|
||||
for (var y = -n; y <= n; ++y)
|
||||
for (var x = -n; x <= n; ++x)
|
||||
{
|
||||
var enumerator = GetAnchoredEntitiesEnumerator(uid, grid, position + new Vector2i(x, y));
|
||||
|
||||
while (enumerator.MoveNext(out var cell))
|
||||
{
|
||||
yield return cell.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Transforms
|
||||
|
||||
public Vector2 WorldToLocal(EntityUid uid, MapGridComponent grid, Vector2 posWorld)
|
||||
{
|
||||
var matrix = _transform.GetInvWorldMatrix(uid);
|
||||
return matrix.Transform(posWorld);
|
||||
}
|
||||
|
||||
public EntityCoordinates MapToGrid(EntityUid uid, MapCoordinates posWorld)
|
||||
{
|
||||
var mapId = _xformQuery.GetComponent(uid).MapID;
|
||||
|
||||
if (posWorld.MapId != mapId)
|
||||
throw new ArgumentException(
|
||||
$"Grid {uid} is on map {mapId}, but coords are on map {posWorld.MapId}.",
|
||||
nameof(posWorld));
|
||||
|
||||
if (!TryComp<MapGridComponent>(uid, out var grid))
|
||||
{
|
||||
return new EntityCoordinates(MapManager.GetMapEntityId(posWorld.MapId), new Vector2(posWorld.X, posWorld.Y));
|
||||
}
|
||||
|
||||
return new EntityCoordinates(uid, WorldToLocal(uid, grid, posWorld.Position));
|
||||
}
|
||||
|
||||
public Vector2 LocalToWorld(EntityUid uid, MapGridComponent grid, Vector2 posLocal)
|
||||
{
|
||||
var matrix = _transform.GetWorldMatrix(uid);
|
||||
return matrix.Transform(posLocal);
|
||||
}
|
||||
|
||||
public Vector2i WorldToTile(EntityUid uid, MapGridComponent grid, Vector2 posWorld)
|
||||
{
|
||||
var local = WorldToLocal(uid, grid, posWorld);
|
||||
var x = (int)Math.Floor(local.X / grid.TileSize);
|
||||
var y = (int)Math.Floor(local.Y / grid.TileSize);
|
||||
return new Vector2i(x, y);
|
||||
}
|
||||
|
||||
public Vector2i LocalToTile(EntityUid uid, MapGridComponent grid, EntityCoordinates coordinates)
|
||||
{
|
||||
var position = LocalToGrid(uid, grid, coordinates);
|
||||
return new Vector2i((int) Math.Floor(position.X / grid.TileSize), (int) Math.Floor(position.Y / grid.TileSize));
|
||||
}
|
||||
|
||||
public Vector2i CoordinatesToTile(EntityUid uid, MapGridComponent grid, MapCoordinates coords)
|
||||
{
|
||||
#if DEBUG
|
||||
var mapId = _xformQuery.GetComponent(uid).MapID;
|
||||
DebugTools.Assert(mapId == coords.MapId);
|
||||
#endif
|
||||
|
||||
var local = WorldToLocal(uid, grid, coords.Position);
|
||||
|
||||
var x = (int)Math.Floor(local.X / grid.TileSize);
|
||||
var y = (int)Math.Floor(local.Y / grid.TileSize);
|
||||
return new Vector2i(x, y);
|
||||
}
|
||||
|
||||
public Vector2i CoordinatesToTile(EntityUid uid, MapGridComponent grid, EntityCoordinates coords)
|
||||
{
|
||||
#if DEBUG
|
||||
var mapId = _xformQuery.GetComponent(uid).MapID;
|
||||
DebugTools.Assert(mapId == coords.GetMapId(EntityManager));
|
||||
#endif
|
||||
var local = LocalToGrid(uid, grid, coords);
|
||||
|
||||
var x = (int)Math.Floor(local.X / grid.TileSize);
|
||||
var y = (int)Math.Floor(local.Y / grid.TileSize);
|
||||
return new Vector2i(x, y);
|
||||
}
|
||||
|
||||
public Vector2i LocalToChunkIndices(EntityUid uid, MapGridComponent grid, EntityCoordinates gridPos)
|
||||
{
|
||||
var local = LocalToGrid(uid, grid, gridPos);
|
||||
|
||||
var x = (int)Math.Floor(local.X / (grid.TileSize * grid.ChunkSize));
|
||||
var y = (int)Math.Floor(local.Y / (grid.TileSize * grid.ChunkSize));
|
||||
return new Vector2i(x, y);
|
||||
}
|
||||
|
||||
public Vector2 LocalToGrid(EntityUid uid, MapGridComponent grid, EntityCoordinates position)
|
||||
{
|
||||
return position.EntityId == uid
|
||||
? position.Position
|
||||
: WorldToLocal(uid, grid, position.ToMapPos(EntityManager, _transform));
|
||||
}
|
||||
|
||||
public bool CollidesWithGrid(EntityUid uid, MapGridComponent grid, Vector2i indices)
|
||||
{
|
||||
var chunkIndices = GridTileToChunkIndices(uid, grid, indices);
|
||||
if (!grid.Chunks.TryGetValue(chunkIndices, out var chunk))
|
||||
return false;
|
||||
|
||||
var cTileIndices = chunk.GridTileToChunkTile(indices);
|
||||
return chunk.GetTile((ushort)cTileIndices.X, (ushort)cTileIndices.Y).TypeId != Tile.Empty.TypeId;
|
||||
}
|
||||
|
||||
public Vector2i GridTileToChunkIndices(EntityUid uid, MapGridComponent grid, Vector2i gridTile)
|
||||
{
|
||||
var x = (int)Math.Floor(gridTile.X / (float) grid.ChunkSize);
|
||||
var y = (int)Math.Floor(gridTile.Y / (float) grid.ChunkSize);
|
||||
|
||||
return new Vector2i(x, y);
|
||||
}
|
||||
|
||||
public EntityCoordinates GridTileToLocal(EntityUid uid, MapGridComponent grid, Vector2i gridTile)
|
||||
{
|
||||
return new(uid,
|
||||
new Vector2(gridTile.X * grid.TileSize + (grid.TileSize / 2f), gridTile.Y * grid.TileSize + (grid.TileSize / 2f)));
|
||||
}
|
||||
|
||||
public Vector2 GridTileToWorldPos(EntityUid uid, MapGridComponent grid, Vector2i gridTile)
|
||||
{
|
||||
var locX = gridTile.X * grid.TileSize + (grid.TileSize / 2f);
|
||||
var locY = gridTile.Y * grid.TileSize + (grid.TileSize / 2f);
|
||||
|
||||
return _transform.GetWorldMatrix(uid).Transform(new Vector2(locX, locY));
|
||||
}
|
||||
|
||||
public MapCoordinates GridTileToWorld(EntityUid uid, MapGridComponent grid, Vector2i gridTile)
|
||||
{
|
||||
var parentMapId = _xformQuery.GetComponent(uid).MapID;
|
||||
|
||||
return new(GridTileToWorldPos(uid, grid, gridTile), parentMapId);
|
||||
}
|
||||
|
||||
public bool TryGetTileRef(EntityUid uid, MapGridComponent grid, Vector2i indices, out TileRef tile)
|
||||
{
|
||||
var chunkIndices = GridTileToChunkIndices(uid, grid, indices);
|
||||
if (!grid.Chunks.TryGetValue(chunkIndices, out var chunk))
|
||||
{
|
||||
tile = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
var cTileIndices = chunk.GridTileToChunkTile(indices);
|
||||
tile = GetTileRef(uid, grid, chunk, (ushort)cTileIndices.X, (ushort)cTileIndices.Y);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryGetTileRef(EntityUid uid, MapGridComponent grid, EntityCoordinates coords, out TileRef tile)
|
||||
{
|
||||
return TryGetTileRef(uid, grid, CoordinatesToTile(uid, grid, coords), out tile);
|
||||
}
|
||||
|
||||
public bool TryGetTileRef(EntityUid uid, MapGridComponent grid, Vector2 worldPos, out TileRef tile)
|
||||
{
|
||||
return TryGetTileRef(uid, grid, WorldToTile(uid, grid, worldPos), out tile);
|
||||
}
|
||||
|
||||
#endregion Transforms
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the world space AABB for this chunk.
|
||||
/// </summary>
|
||||
internal Box2 CalcWorldAABB(EntityUid uid, MapGridComponent grid, MapChunk mapChunk)
|
||||
{
|
||||
var (position, rotation) =
|
||||
_transform.GetWorldPositionRotation(uid);
|
||||
|
||||
var chunkPosition = mapChunk.Indices;
|
||||
var tileScale = grid.TileSize;
|
||||
var chunkScale = mapChunk.ChunkSize;
|
||||
|
||||
var worldPos = position + rotation.RotateVec(chunkPosition * tileScale * chunkScale);
|
||||
|
||||
return new Box2Rotated(
|
||||
((Box2)mapChunk.CachedBounds
|
||||
.Scale(tileScale))
|
||||
.Translated(worldPos),
|
||||
rotation, worldPos).CalcBoundingBox();
|
||||
}
|
||||
|
||||
private void OnTileModified(EntityUid uid, MapGridComponent grid, MapChunk mapChunk, Vector2i tileIndices, Tile newTile, Tile oldTile,
|
||||
bool shapeChanged)
|
||||
{
|
||||
// As the collision regeneration can potentially delete the chunk we'll notify of the tile changed first.
|
||||
var gridTile = mapChunk.ChunkTileToGridTile(tileIndices);
|
||||
mapChunk.LastTileModifiedTick = _timing.CurTick;
|
||||
grid.LastTileModifiedTick = _timing.CurTick;
|
||||
Dirty(grid);
|
||||
|
||||
// The map serializer currently sets tiles of unbound grids as part of the deserialization process
|
||||
// It properly sets SuppressOnTileChanged so that the event isn't spammed for every tile on the grid.
|
||||
// ParentMapId is not able to be accessed on unbound grids, so we can't even call this function for unbound grids.
|
||||
if (!MapManager.SuppressOnTileChanged)
|
||||
{
|
||||
var newTileRef = new TileRef(uid, gridTile, newTile);
|
||||
_mapInternal.RaiseOnTileChanged(newTileRef, oldTile);
|
||||
}
|
||||
|
||||
if (shapeChanged && !mapChunk.SuppressCollisionRegeneration)
|
||||
{
|
||||
RegenerateCollision(uid, grid, mapChunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
public abstract partial class SharedMapSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Replaces a single tile inside of the chunk.
|
||||
/// </summary>
|
||||
/// <param name="xIndex">The X tile index relative to the chunk.</param>
|
||||
/// <param name="yIndex">The Y tile index relative to the chunk.</param>
|
||||
/// <param name="tile">The new tile to insert.</param>
|
||||
internal void SetChunkTile(EntityUid uid, MapGridComponent grid, MapChunk chunk, ushort xIndex, ushort yIndex, Tile tile)
|
||||
{
|
||||
if (!chunk.TrySetTile(xIndex, yIndex, tile, out var oldTile, out var shapeChanged))
|
||||
return;
|
||||
|
||||
var tileIndices = new Vector2i(xIndex, yIndex);
|
||||
OnTileModified(uid, grid, chunk, tileIndices, tile, oldTile, shapeChanged);
|
||||
}
|
||||
}
|
||||
@@ -6,21 +6,30 @@ using System.Collections.Generic;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Map.Components;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public abstract partial class SharedMapSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] protected readonly IMapManager MapManager = default!;
|
||||
[Dependency] private readonly IMapManagerInternal _mapInternal = default!;
|
||||
[Dependency] private readonly INetManager _netManager = default!;
|
||||
[Dependency] private readonly FixtureSystem _fixtures = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_xformQuery = GetEntityQuery<TransformComponent>();
|
||||
|
||||
InitializeMap();
|
||||
InitializeGrid();
|
||||
|
||||
|
||||
@@ -33,8 +33,8 @@ public abstract partial class SharedTransformSystem
|
||||
EntityQuery<TransformComponent> xformQuery)
|
||||
{
|
||||
// Bypass some of the expensive stuff in unanchoring / anchoring.
|
||||
oldGrid.RemoveFromSnapGridCell(tilePos, uid);
|
||||
newGrid.AddToSnapGridCell(tilePos, uid);
|
||||
_map.RemoveFromSnapGridCell(oldGridUid, oldGrid, tilePos, uid);
|
||||
_map.AddToSnapGridCell(newGridUid, newGrid, tilePos, uid);
|
||||
// TODO: Could do this re-parent way better.
|
||||
// Unfortunately we don't want any anchoring events to go out hence... this.
|
||||
xform._anchored = false;
|
||||
@@ -77,7 +77,7 @@ public abstract partial class SharedTransformSystem
|
||||
MapGridComponent grid,
|
||||
Vector2i tileIndices)
|
||||
{
|
||||
if (!grid.AddToSnapGridCell(tileIndices, uid))
|
||||
if (!_map.AddToSnapGridCell(gridUid, grid, tileIndices, uid))
|
||||
return false;
|
||||
|
||||
var wasAnchored = xform._anchored;
|
||||
@@ -94,7 +94,7 @@ public abstract partial class SharedTransformSystem
|
||||
}
|
||||
|
||||
// Anchor snapping. If there is a coordinate change, it will dirty the component for us.
|
||||
var pos = new EntityCoordinates(gridUid, grid.GridTileToLocal(tileIndices).Position);
|
||||
var pos = new EntityCoordinates(gridUid, _map.GridTileToLocal(gridUid, grid, tileIndices).Position);
|
||||
SetCoordinates(uid, xform, pos, unanchor: false);
|
||||
|
||||
return true;
|
||||
@@ -102,14 +102,14 @@ public abstract partial class SharedTransformSystem
|
||||
|
||||
public bool AnchorEntity(EntityUid uid, TransformComponent xform, MapGridComponent grid)
|
||||
{
|
||||
var tileIndices = grid.TileIndicesFor(xform.Coordinates);
|
||||
var tileIndices = _map.TileIndicesFor(grid.Owner, grid, xform.Coordinates);
|
||||
return AnchorEntity(uid, xform, grid, tileIndices);
|
||||
}
|
||||
|
||||
public bool AnchorEntity(EntityUid uid, TransformComponent xform)
|
||||
{
|
||||
return _mapManager.TryGetGrid(xform.GridUid, out var grid)
|
||||
&& AnchorEntity(uid, xform, grid, grid.TileIndicesFor(xform.Coordinates));
|
||||
&& AnchorEntity(uid, xform, grid, _map.TileIndicesFor(xform.GridUid.Value, grid, xform.Coordinates));
|
||||
}
|
||||
|
||||
public void Unanchor(EntityUid uid, TransformComponent xform, bool setPhysics = true)
|
||||
@@ -128,8 +128,8 @@ public abstract partial class SharedTransformSystem
|
||||
|
||||
if (TryComp(xform.GridUid, out MapGridComponent? grid))
|
||||
{
|
||||
var tileIndices = grid.TileIndicesFor(xform.Coordinates);
|
||||
grid.RemoveFromSnapGridCell(tileIndices, uid);
|
||||
var tileIndices = _map.TileIndicesFor(xform.GridUid.Value, grid, xform.Coordinates);
|
||||
_map.RemoveFromSnapGridCell(xform.GridUid.Value, grid, tileIndices, uid);
|
||||
}
|
||||
else if (xform.Initialized)
|
||||
{
|
||||
@@ -156,7 +156,7 @@ public abstract partial class SharedTransformSystem
|
||||
/// </summary>
|
||||
public bool ContainsEntity(TransformComponent xform, EntityUid entity)
|
||||
{
|
||||
return ContainsEntity(xform, entity, GetEntityQuery<TransformComponent>());
|
||||
return ContainsEntity(xform, entity, _xformQuery);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ContainsEntity(Robust.Shared.GameObjects.TransformComponent,Robust.Shared.GameObjects.EntityUid)"/>
|
||||
@@ -168,7 +168,7 @@ public abstract partial class SharedTransformSystem
|
||||
/// <inheritdoc cref="ContainsEntity(Robust.Shared.GameObjects.TransformComponent,Robust.Shared.GameObjects.EntityUid)"/>
|
||||
public bool ContainsEntity(TransformComponent xform, TransformComponent entityTransform)
|
||||
{
|
||||
return ContainsEntity(xform, entityTransform, GetEntityQuery<TransformComponent>());
|
||||
return ContainsEntity(xform, entityTransform, _xformQuery);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ContainsEntity(Robust.Shared.GameObjects.TransformComponent,Robust.Shared.GameObjects.EntityUid)"/>
|
||||
@@ -227,11 +227,9 @@ public abstract partial class SharedTransformSystem
|
||||
return value;
|
||||
}
|
||||
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
|
||||
if (!component._mapIdInitialized)
|
||||
{
|
||||
FindMapIdAndSet(uid, component, EntityManager, xformQuery, _mapManager);
|
||||
FindMapIdAndSet(uid, component, EntityManager, _xformQuery, _mapManager);
|
||||
component._mapIdInitialized = true;
|
||||
}
|
||||
|
||||
@@ -241,7 +239,7 @@ public abstract partial class SharedTransformSystem
|
||||
// Note that _children is a HashSet<EntityUid>,
|
||||
// so duplicate additions (which will happen) don't matter.
|
||||
|
||||
var parentXform = xformQuery.GetComponent(component.ParentUid);
|
||||
var parentXform = _xformQuery.GetComponent(component.ParentUid);
|
||||
if (parentXform.LifeStage > ComponentLifeStage.Running || LifeStage(component.ParentUid) > EntityLifeStage.MapInitialized)
|
||||
{
|
||||
var msg = $"Attempted to re-parent to a terminating object. Entity: {ToPrettyString(component.ParentUid)}, new parent: {ToPrettyString(uid)}";
|
||||
@@ -365,7 +363,7 @@ public abstract partial class SharedTransformSystem
|
||||
DebugTools.Assert(!HasComp<MapGridComponent>(uid));
|
||||
DebugTools.Assert(gridId == null || HasComp<MapGridComponent>(gridId));
|
||||
|
||||
xformQuery ??= GetEntityQuery<TransformComponent>();
|
||||
xformQuery ??= _xformQuery;
|
||||
SetGridIdRecursive(uid, xform, gridId, xformQuery.Value);
|
||||
}
|
||||
|
||||
@@ -486,8 +484,6 @@ public abstract partial class SharedTransformSystem
|
||||
// Perform parent change logic
|
||||
if (value.EntityId != xform._parent)
|
||||
{
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
|
||||
if (value.EntityId == uid)
|
||||
{
|
||||
QueueDel(uid);
|
||||
@@ -496,7 +492,7 @@ public abstract partial class SharedTransformSystem
|
||||
|
||||
if (value.EntityId.IsValid())
|
||||
{
|
||||
if (!xformQuery.Resolve(value.EntityId, ref newParent, false))
|
||||
if (!_xformQuery.Resolve(value.EntityId, ref newParent, false))
|
||||
{
|
||||
QueueDel(uid);
|
||||
throw new InvalidOperationException($"Attempted to parent entity {ToPrettyString(uid)} to non-existent entity {value.EntityId}");
|
||||
@@ -530,13 +526,13 @@ public abstract partial class SharedTransformSystem
|
||||
}
|
||||
|
||||
recursiveUid = recursiveXform.ParentUid;
|
||||
recursiveXform = xformQuery.GetComponent(recursiveUid);
|
||||
recursiveXform = _xformQuery.GetComponent(recursiveUid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (xform._parent.IsValid())
|
||||
xformQuery.Resolve(xform._parent, ref oldParent);
|
||||
_xformQuery.Resolve(xform._parent, ref oldParent);
|
||||
|
||||
oldParent?._children.Remove(uid);
|
||||
newParent?._children.Add(uid);
|
||||
@@ -546,7 +542,7 @@ public abstract partial class SharedTransformSystem
|
||||
|
||||
if (newParent != null)
|
||||
{
|
||||
xform.ChangeMapId(newParent.MapID, xformQuery);
|
||||
xform.ChangeMapId(newParent.MapID, _xformQuery);
|
||||
|
||||
if (!xform._gridInitialized)
|
||||
InitializeGridUid(uid, xform);
|
||||
@@ -559,18 +555,18 @@ public abstract partial class SharedTransformSystem
|
||||
}
|
||||
else
|
||||
{
|
||||
xform.ChangeMapId(MapId.Nullspace, xformQuery);
|
||||
xform.ChangeMapId(MapId.Nullspace, _xformQuery);
|
||||
if (!xform._gridInitialized)
|
||||
InitializeGridUid(uid, xform);
|
||||
else
|
||||
SetGridId(uid, xform, null, xformQuery);
|
||||
SetGridId(uid, xform, null, _xformQuery);
|
||||
}
|
||||
|
||||
if (xform.Initialized)
|
||||
{
|
||||
// preserve world rotation
|
||||
if (rotation == null && oldParent != null && newParent != null && !xform.NoLocalRotation)
|
||||
xform._localRotation += GetWorldRotation(oldParent, xformQuery) - GetWorldRotation(newParent, xformQuery);
|
||||
xform._localRotation += GetWorldRotation(oldParent) - GetWorldRotation(newParent);
|
||||
|
||||
DebugTools.Assert(!xform.NoLocalRotation || xform.LocalRotation == 0);
|
||||
|
||||
@@ -598,7 +594,7 @@ public abstract partial class SharedTransformSystem
|
||||
|
||||
public void ReparentChildren(EntityUid oldUid, EntityUid uid)
|
||||
{
|
||||
ReparentChildren(oldUid, uid, GetEntityQuery<TransformComponent>());
|
||||
ReparentChildren(oldUid, uid, _xformQuery);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -625,7 +621,7 @@ public abstract partial class SharedTransformSystem
|
||||
|
||||
public TransformComponent? GetParent(EntityUid uid)
|
||||
{
|
||||
return GetParent(uid, GetEntityQuery<TransformComponent>());
|
||||
return GetParent(uid, _xformQuery);
|
||||
}
|
||||
|
||||
public TransformComponent? GetParent(EntityUid uid, EntityQuery<TransformComponent> xformQuery)
|
||||
@@ -635,7 +631,7 @@ public abstract partial class SharedTransformSystem
|
||||
|
||||
public TransformComponent? GetParent(TransformComponent xform)
|
||||
{
|
||||
return GetParent(xform, GetEntityQuery<TransformComponent>());
|
||||
return GetParent(xform, _xformQuery);
|
||||
}
|
||||
|
||||
public TransformComponent? GetParent(TransformComponent xform, EntityQuery<TransformComponent> xformQuery)
|
||||
@@ -646,13 +642,12 @@ public abstract partial class SharedTransformSystem
|
||||
|
||||
public void SetParent(EntityUid uid, EntityUid parent)
|
||||
{
|
||||
var query = GetEntityQuery<TransformComponent>();
|
||||
SetParent(uid, query.GetComponent(uid), parent, query);
|
||||
SetParent(uid, _xformQuery.GetComponent(uid), parent, _xformQuery);
|
||||
}
|
||||
|
||||
public void SetParent(EntityUid uid, TransformComponent xform, EntityUid parent, TransformComponent? parentXform = null)
|
||||
{
|
||||
SetParent(uid, xform, parent, GetEntityQuery<TransformComponent>(), parentXform);
|
||||
SetParent(uid, xform, parent, _xformQuery, parentXform);
|
||||
}
|
||||
|
||||
public void SetParent(EntityUid uid, TransformComponent xform, EntityUid parent, EntityQuery<TransformComponent> xformQuery, TransformComponent? parentXform = null)
|
||||
@@ -711,8 +706,8 @@ public abstract partial class SharedTransformSystem
|
||||
// remove from any old grid lookups
|
||||
if (xform.Anchored && TryComp(xform.ParentUid, out MapGridComponent? grid))
|
||||
{
|
||||
var tileIndices = grid.TileIndicesFor(xform.Coordinates);
|
||||
grid.RemoveFromSnapGridCell(tileIndices, uid);
|
||||
var tileIndices = _map.TileIndicesFor(xform.ParentUid, grid, xform.Coordinates);
|
||||
_map.RemoveFromSnapGridCell(xform.ParentUid, grid, tileIndices, uid);
|
||||
}
|
||||
|
||||
// Set anchor state true during the move event unless the entity wasn't and isn't being anchored. This avoids unnecessary entity lookup changes.
|
||||
@@ -729,8 +724,8 @@ public abstract partial class SharedTransformSystem
|
||||
{
|
||||
if (xform.ParentUid == xform.GridUid && TryComp(xform.GridUid, out MapGridComponent? newGrid))
|
||||
{
|
||||
var tileIndices = newGrid.TileIndicesFor(xform.Coordinates);
|
||||
newGrid.AddToSnapGridCell(tileIndices, uid);
|
||||
var tileIndices = _map.TileIndicesFor(xform.GridUid.Value, newGrid, xform.Coordinates);
|
||||
_map.AddToSnapGridCell(xform.GridUid.Value, newGrid, tileIndices, uid);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -778,8 +773,7 @@ public abstract partial class SharedTransformSystem
|
||||
[Pure]
|
||||
public Matrix3 GetWorldMatrix(EntityUid uid)
|
||||
{
|
||||
var query = GetEntityQuery<TransformComponent>();
|
||||
return GetWorldMatrix(query.GetComponent(uid), query);
|
||||
return GetWorldMatrix(_xformQuery.GetComponent(uid), _xformQuery);
|
||||
}
|
||||
|
||||
// Temporary until it's moved here
|
||||
@@ -787,7 +781,7 @@ public abstract partial class SharedTransformSystem
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Matrix3 GetWorldMatrix(TransformComponent component)
|
||||
{
|
||||
return GetWorldMatrix(component, GetEntityQuery<TransformComponent>());
|
||||
return GetWorldMatrix(component, _xformQuery);
|
||||
}
|
||||
|
||||
[Pure]
|
||||
@@ -845,6 +839,12 @@ public abstract partial class SharedTransformSystem
|
||||
return GetWorldPosition(component);
|
||||
}
|
||||
|
||||
[Pure]
|
||||
public (Vector2 WorldPosition, Angle WorldRotation) GetWorldPositionRotation(EntityUid uid)
|
||||
{
|
||||
return GetWorldPositionRotation(_xformQuery.GetComponent(uid));
|
||||
}
|
||||
|
||||
[Pure]
|
||||
public (Vector2 WorldPosition, Angle WorldRotation) GetWorldPositionRotation(TransformComponent component)
|
||||
{
|
||||
@@ -947,7 +947,7 @@ public abstract partial class SharedTransformSystem
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetWorldPosition(TransformComponent component, Vector2 worldPos)
|
||||
{
|
||||
SetWorldPosition(component, worldPos, GetEntityQuery<TransformComponent>());
|
||||
SetWorldPosition(component, worldPos, _xformQuery);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -973,8 +973,7 @@ public abstract partial class SharedTransformSystem
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Angle GetWorldRotation(EntityUid uid)
|
||||
{
|
||||
var query = GetEntityQuery<TransformComponent>();
|
||||
return GetWorldRotation(query.GetComponent(uid), query);
|
||||
return GetWorldRotation(_xformQuery.GetComponent(uid), _xformQuery);
|
||||
}
|
||||
|
||||
// Temporary until it's moved here
|
||||
@@ -982,7 +981,7 @@ public abstract partial class SharedTransformSystem
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Angle GetWorldRotation(TransformComponent component)
|
||||
{
|
||||
return GetWorldRotation(component, GetEntityQuery<TransformComponent>());
|
||||
return GetWorldRotation(component, _xformQuery);
|
||||
}
|
||||
|
||||
[Pure]
|
||||
@@ -1050,7 +1049,7 @@ public abstract partial class SharedTransformSystem
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetWorldPositionRotation(TransformComponent component, Vector2 worldPos, Angle worldRot)
|
||||
{
|
||||
SetWorldPositionRotation(component, worldPos, worldRot, GetEntityQuery<TransformComponent>());
|
||||
SetWorldPositionRotation(component, worldPos, worldRot, _xformQuery);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -1115,15 +1114,14 @@ public abstract partial class SharedTransformSystem
|
||||
[Pure]
|
||||
public Matrix3 GetInvWorldMatrix(EntityUid uid)
|
||||
{
|
||||
var query = GetEntityQuery<TransformComponent>();
|
||||
return GetInvWorldMatrix(query.GetComponent(uid), query);
|
||||
return GetInvWorldMatrix(_xformQuery.GetComponent(uid), _xformQuery);
|
||||
}
|
||||
|
||||
[Pure]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Matrix3 GetInvWorldMatrix(TransformComponent component)
|
||||
{
|
||||
return GetInvWorldMatrix(component, GetEntityQuery<TransformComponent>());
|
||||
return GetInvWorldMatrix(component, _xformQuery);
|
||||
}
|
||||
|
||||
[Pure]
|
||||
@@ -1148,15 +1146,14 @@ public abstract partial class SharedTransformSystem
|
||||
public (Vector2 WorldPosition, Angle WorldRotation, Matrix3 WorldMatrix)
|
||||
GetWorldPositionRotationMatrix(EntityUid uid)
|
||||
{
|
||||
var query = GetEntityQuery<TransformComponent>();
|
||||
return GetWorldPositionRotationMatrix(query.GetComponent(uid), query);
|
||||
return GetWorldPositionRotationMatrix(_xformQuery.GetComponent(uid), _xformQuery);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public (Vector2 WorldPosition, Angle WorldRotation, Matrix3 WorldMatrix)
|
||||
GetWorldPositionRotationMatrix(TransformComponent xform)
|
||||
{
|
||||
return GetWorldPositionRotationMatrix(xform, GetEntityQuery<TransformComponent>());
|
||||
return GetWorldPositionRotationMatrix(xform, _xformQuery);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -1186,7 +1183,7 @@ public abstract partial class SharedTransformSystem
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public (Vector2 WorldPosition, Angle WorldRotation, Matrix3 InvWorldMatrix) GetWorldPositionRotationInvMatrix(TransformComponent xform)
|
||||
{
|
||||
return GetWorldPositionRotationInvMatrix(xform, GetEntityQuery<TransformComponent>());
|
||||
return GetWorldPositionRotationInvMatrix(xform, _xformQuery);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -1210,15 +1207,14 @@ public abstract partial class SharedTransformSystem
|
||||
public (Vector2 WorldPosition, Angle WorldRotation, Matrix3 WorldMatrix, Matrix3 InvWorldMatrix)
|
||||
GetWorldPositionRotationMatrixWithInv(EntityUid uid)
|
||||
{
|
||||
var query = GetEntityQuery<TransformComponent>();
|
||||
return GetWorldPositionRotationMatrixWithInv(query.GetComponent(uid), query);
|
||||
return GetWorldPositionRotationMatrixWithInv(_xformQuery.GetComponent(uid), _xformQuery);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public (Vector2 WorldPosition, Angle WorldRotation, Matrix3 WorldMatrix, Matrix3 InvWorldMatrix)
|
||||
GetWorldPositionRotationMatrixWithInv(TransformComponent xform)
|
||||
{
|
||||
return GetWorldPositionRotationMatrixWithInv(xform, GetEntityQuery<TransformComponent>());
|
||||
return GetWorldPositionRotationMatrixWithInv(xform, _xformQuery);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -1241,9 +1237,8 @@ public abstract partial class SharedTransformSystem
|
||||
#region AttachToGridOrMap
|
||||
public void AttachToGridOrMap(EntityUid uid, TransformComponent? xform = null)
|
||||
{
|
||||
var query = GetEntityQuery<TransformComponent>();
|
||||
if (query.Resolve(uid, ref xform))
|
||||
AttachToGridOrMap(uid, xform, query);
|
||||
if (_xformQuery.Resolve(uid, ref xform))
|
||||
AttachToGridOrMap(uid, xform, _xformQuery);
|
||||
}
|
||||
|
||||
public void AttachToGridOrMap(EntityUid uid, TransformComponent xform, EntityQuery<TransformComponent> query)
|
||||
@@ -1281,18 +1276,17 @@ public abstract partial class SharedTransformSystem
|
||||
|
||||
public bool TryGetMapOrGridCoordinates(EntityUid uid, [NotNullWhen(true)] out EntityCoordinates? coordinates, TransformComponent? xform = null)
|
||||
{
|
||||
var query = GetEntityQuery<TransformComponent>();
|
||||
coordinates = null;
|
||||
|
||||
if (!query.Resolve(uid, ref xform))
|
||||
if (!_xformQuery.Resolve(uid, ref xform))
|
||||
return false;
|
||||
|
||||
if (!xform.ParentUid.IsValid())
|
||||
return false;
|
||||
|
||||
EntityUid newParent;
|
||||
var oldPos = GetWorldPosition(xform, query);
|
||||
if (_mapManager.TryFindGridAt(xform.MapID, oldPos, query, out var gridUid, out _))
|
||||
var oldPos = GetWorldPosition(xform, _xformQuery);
|
||||
if (_mapManager.TryFindGridAt(xform.MapID, oldPos, _xformQuery, out var gridUid, out _))
|
||||
{
|
||||
newParent = gridUid;
|
||||
}
|
||||
@@ -1305,7 +1299,7 @@ public abstract partial class SharedTransformSystem
|
||||
return false;
|
||||
}
|
||||
|
||||
coordinates = new(newParent, GetInvWorldMatrix(newParent, query).Transform(oldPos));
|
||||
coordinates = new(newParent, GetInvWorldMatrix(newParent, _xformQuery).Transform(oldPos));
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
@@ -1342,8 +1336,8 @@ public abstract partial class SharedTransformSystem
|
||||
if (xform.Anchored && _metaQuery.TryGetComponent(xform.GridUid, out var meta) && meta.EntityLifeStage <= EntityLifeStage.MapInitialized)
|
||||
{
|
||||
var grid = Comp<MapGridComponent>(xform.GridUid.Value);
|
||||
var tileIndices = grid.TileIndicesFor(xform.Coordinates);
|
||||
grid.RemoveFromSnapGridCell(tileIndices, uid);
|
||||
var tileIndices = _map.TileIndicesFor(xform.GridUid.Value, grid, xform.Coordinates);
|
||||
_map.RemoveFromSnapGridCell(xform.GridUid.Value, grid, tileIndices, uid);
|
||||
xform._anchored = false;
|
||||
var anchorStateChangedEvent = new AnchorStateChangedEvent(xform, true);
|
||||
RaiseLocalEvent(uid, ref anchorStateChangedEvent, true);
|
||||
@@ -1362,7 +1356,7 @@ public abstract partial class SharedTransformSystem
|
||||
{
|
||||
if (LifeStage(uid) > EntityLifeStage.Initialized)
|
||||
{
|
||||
SetGridId(uid, component, uid, GetEntityQuery<TransformComponent>());
|
||||
SetGridId(uid, component, uid, _xformQuery);
|
||||
return;
|
||||
}
|
||||
component._gridInitialized = true;
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace Robust.Shared.GameObjects
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
|
||||
private EntityQuery<MapGridComponent> _gridQuery;
|
||||
@@ -254,7 +255,7 @@ namespace Robust.Shared.GameObjects
|
||||
return GetWorldPosition(xform).Floored();
|
||||
|
||||
// We're on a grid, need to convert the coordinates to grid tiles.
|
||||
return _mapManager.GetGrid(xform.GridUid.Value).CoordinatesToTile(xform.Coordinates);
|
||||
return _map.CoordinatesToTile(xform.GridUid.Value, Comp<MapGridComponent>(xform.GridUid.Value), xform.Coordinates);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Enumerators;
|
||||
using Robust.Shared.Map.Events;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -27,10 +20,8 @@ namespace Robust.Shared.Map.Components
|
||||
[NetworkedComponent]
|
||||
public sealed class MapGridComponent : Component
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IMapManagerInternal _mapManager = default!;
|
||||
[Dependency] private readonly INetManager _netManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
private SharedMapSystem MapSystem => _entManager.System<SharedMapSystem>();
|
||||
|
||||
// This field is used for deserialization internally in the map loader.
|
||||
// If you want to remove this, you would have to restructure the map save file.
|
||||
@@ -40,6 +31,9 @@ namespace Robust.Shared.Map.Components
|
||||
|
||||
[DataField("chunkSize")] internal ushort ChunkSize = 16;
|
||||
|
||||
[ViewVariables]
|
||||
public int ChunkCount => Chunks.Count;
|
||||
|
||||
/// <summary>
|
||||
/// The length of the side of a square tile in world units.
|
||||
/// </summary>
|
||||
@@ -70,7 +64,7 @@ namespace Robust.Shared.Map.Components
|
||||
internal readonly Dictionary<Vector2i, MapChunk> Chunks = new();
|
||||
|
||||
[ViewVariables]
|
||||
public Box2 LocalAABB { get; private set; }
|
||||
public Box2 LocalAABB { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set to enable or disable grid splitting.
|
||||
@@ -79,849 +73,221 @@ namespace Robust.Shared.Map.Components
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("canSplit")]
|
||||
public bool CanSplit = true;
|
||||
|
||||
internal void RemoveChunk(Vector2i origin)
|
||||
{
|
||||
if (!Chunks.TryGetValue(origin, out var chunk))
|
||||
return;
|
||||
|
||||
if (_netManager.IsServer)
|
||||
ChunkDeletionHistory.Add((_timing.CurTick, chunk.Indices));
|
||||
|
||||
chunk.Fixtures.Clear();
|
||||
Chunks.Remove(origin);
|
||||
|
||||
if (Chunks.Count == 0)
|
||||
_entMan.EventBus.RaiseLocalEvent(Owner, new EmptyGridEvent { GridId = Owner }, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Regenerate collision for multiple chunks at once; faster than doing it individually.
|
||||
/// </summary>
|
||||
internal void RegenerateCollision(IReadOnlySet<MapChunk> chunks)
|
||||
{
|
||||
if (_entMan.HasComponent<MapComponent>(Owner))
|
||||
return;
|
||||
|
||||
var chunkRectangles = new Dictionary<MapChunk, List<Box2i>>(chunks.Count);
|
||||
var removedChunks = new List<MapChunk>();
|
||||
var fixtureSystem = _entMan.EntitySysManager.GetEntitySystem<FixtureSystem>();
|
||||
_entMan.EntitySysManager.TryGetEntitySystem(out SharedGridFixtureSystem? system);
|
||||
|
||||
foreach (var mapChunk in chunks)
|
||||
{
|
||||
// Even if the chunk is still removed still need to make sure bounds are updated (for now...)
|
||||
// generate collision rectangles for this chunk based on filled tiles.
|
||||
GridChunkPartition.PartitionChunk(mapChunk, out var localBounds, out var rectangles);
|
||||
mapChunk.CachedBounds = localBounds;
|
||||
|
||||
if (mapChunk.FilledTiles > 0)
|
||||
chunkRectangles.Add(mapChunk, rectangles);
|
||||
else
|
||||
{
|
||||
// Gone. Reduced to atoms
|
||||
// Need to do this before RemoveChunk because it clears fixtures.
|
||||
FixturesComponent? manager = null;
|
||||
PhysicsComponent? body = null;
|
||||
TransformComponent? xform = null;
|
||||
|
||||
foreach (var fixture in mapChunk.Fixtures)
|
||||
{
|
||||
fixtureSystem.DestroyFixture(Owner, fixture, false, manager: manager, body: body, xform: xform);
|
||||
}
|
||||
|
||||
RemoveChunk(mapChunk.Indices);
|
||||
removedChunks.Add(mapChunk);
|
||||
}
|
||||
}
|
||||
|
||||
LocalAABB = new Box2();
|
||||
foreach (var chunk in Chunks.Values)
|
||||
{
|
||||
var chunkBounds = chunk.CachedBounds;
|
||||
|
||||
if (chunkBounds.Size.Equals(Vector2i.Zero))
|
||||
continue;
|
||||
|
||||
if (LocalAABB.Size == Vector2.Zero)
|
||||
{
|
||||
var gridBounds = chunkBounds.Translated(chunk.Indices * chunk.ChunkSize);
|
||||
LocalAABB = gridBounds;
|
||||
}
|
||||
else
|
||||
{
|
||||
var gridBounds = chunkBounds.Translated(chunk.Indices * chunk.ChunkSize);
|
||||
LocalAABB = LocalAABB.Union(gridBounds);
|
||||
}
|
||||
}
|
||||
|
||||
// May have been deleted from the bulk update above!
|
||||
if (_entMan.Deleted(Owner))
|
||||
return;
|
||||
|
||||
// TODO: Move this to the component when we combine.
|
||||
_entMan.EntitySysManager.GetEntitySystem<SharedPhysicsSystem>().WakeBody(Owner);
|
||||
_entMan.EntitySysManager.GetEntitySystem<SharedMapSystem>().OnGridBoundsChange(Owner, this);
|
||||
system?.RegenerateCollision(Owner, chunkRectangles, removedChunks);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Regenerates the chunk local bounds of this chunk.
|
||||
/// </summary>
|
||||
internal void RegenerateCollision(MapChunk mapChunk)
|
||||
{
|
||||
RegenerateCollision(new HashSet<MapChunk> { mapChunk });
|
||||
}
|
||||
|
||||
#region TileAccess
|
||||
|
||||
/// <inheritdoc />
|
||||
public TileRef GetTileRef(MapCoordinates coords)
|
||||
{
|
||||
return GetTileRef(CoordinatesToTile(coords));
|
||||
return MapSystem.GetTileRef(Owner, this, coords);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public TileRef GetTileRef(EntityCoordinates coords)
|
||||
{
|
||||
return GetTileRef(CoordinatesToTile(coords));
|
||||
return MapSystem.GetTileRef(Owner, this, coords);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public TileRef GetTileRef(Vector2i tileCoordinates)
|
||||
{
|
||||
var chunkIndices = GridTileToChunkIndices(tileCoordinates);
|
||||
|
||||
if (!Chunks.TryGetValue(chunkIndices, out var output))
|
||||
{
|
||||
// Chunk doesn't exist, return a tileRef to an empty (space) tile.
|
||||
return new TileRef(Owner, tileCoordinates.X, tileCoordinates.Y, default);
|
||||
}
|
||||
|
||||
var chunkTileIndices = output.GridTileToChunkTile(tileCoordinates);
|
||||
return GetTileRef(output, (ushort)chunkTileIndices.X, (ushort)chunkTileIndices.Y);
|
||||
return MapSystem.GetTileRef(Owner, this, tileCoordinates);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the tile at the given chunk indices.
|
||||
/// </summary>
|
||||
/// <param name="mapChunk"></param>
|
||||
/// <param name="xIndex">The X tile index relative to the chunk origin.</param>
|
||||
/// <param name="yIndex">The Y tile index relative to the chunk origin.</param>
|
||||
/// <returns>A reference to a tile.</returns>
|
||||
internal TileRef GetTileRef(MapChunk mapChunk, ushort xIndex, ushort yIndex)
|
||||
{
|
||||
if (xIndex >= mapChunk.ChunkSize)
|
||||
throw new ArgumentOutOfRangeException(nameof(xIndex), "Tile indices out of bounds.");
|
||||
|
||||
if (yIndex >= mapChunk.ChunkSize)
|
||||
throw new ArgumentOutOfRangeException(nameof(yIndex), "Tile indices out of bounds.");
|
||||
|
||||
var indices = mapChunk.ChunkTileToGridTile(new Vector2i(xIndex, yIndex));
|
||||
return new TileRef(Owner, indices, mapChunk.GetTile(xIndex, yIndex));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<TileRef> GetAllTiles(bool ignoreEmpty = true)
|
||||
{
|
||||
foreach (var kvChunk in Chunks)
|
||||
{
|
||||
var chunk = kvChunk.Value;
|
||||
for (ushort x = 0; x < ChunkSize; x++)
|
||||
{
|
||||
for (ushort y = 0; y < ChunkSize; y++)
|
||||
{
|
||||
var tile = chunk.GetTile(x, y);
|
||||
|
||||
if (ignoreEmpty && tile.IsEmpty)
|
||||
continue;
|
||||
|
||||
var (gridX, gridY) = new Vector2i(x, y) + chunk.Indices * ChunkSize;
|
||||
yield return new TileRef(Owner, gridX, gridY, tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
return MapSystem.GetAllTiles(Owner, this, ignoreEmpty);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public GridTileEnumerator GetAllTilesEnumerator(bool ignoreEmpty = true)
|
||||
{
|
||||
return new GridTileEnumerator(Owner, Chunks.GetEnumerator(), ChunkSize, ignoreEmpty);
|
||||
return MapSystem.GetAllTilesEnumerator(Owner, this, ignoreEmpty);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetTile(EntityCoordinates coords, Tile tile)
|
||||
{
|
||||
var localTile = CoordinatesToTile(coords);
|
||||
SetTile(new Vector2i(localTile.X, localTile.Y), tile);
|
||||
MapSystem.SetTile(Owner, this, coords, tile);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetTile(Vector2i gridIndices, Tile tile)
|
||||
{
|
||||
var (chunk, chunkTile) = ChunkAndOffsetForTile(gridIndices);
|
||||
chunk.SetTile((ushort)chunkTile.X, (ushort)chunkTile.Y, tile);
|
||||
// Ideally we'd to this here for consistency but apparently tile modified does it or something.
|
||||
// Yeah it's noodly.
|
||||
// RegenerateCollision(chunk);
|
||||
MapSystem.SetTile(Owner, this, gridIndices, tile);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetTiles(List<(Vector2i GridIndices, Tile Tile)> tiles)
|
||||
{
|
||||
if (tiles.Count == 0) return;
|
||||
|
||||
var chunks = new HashSet<MapChunk>(Math.Max(1, tiles.Count / ChunkSize));
|
||||
|
||||
foreach (var (gridIndices, tile) in tiles)
|
||||
{
|
||||
var (chunk, chunkTile) = ChunkAndOffsetForTile(gridIndices);
|
||||
chunks.Add(chunk);
|
||||
chunk.SuppressCollisionRegeneration = true;
|
||||
chunk.SetTile((ushort)chunkTile.X, (ushort)chunkTile.Y, tile);
|
||||
}
|
||||
|
||||
foreach (var chunk in chunks)
|
||||
{
|
||||
chunk.SuppressCollisionRegeneration = false;
|
||||
}
|
||||
|
||||
RegenerateCollision(chunks);
|
||||
MapSystem.SetTiles(Owner, this, tiles);
|
||||
}
|
||||
|
||||
public IEnumerable<TileRef> GetLocalTilesIntersecting(Box2Rotated localArea, bool ignoreEmpty = true,
|
||||
Predicate<TileRef>? predicate = null)
|
||||
{
|
||||
var localAABB = localArea.CalcBoundingBox();
|
||||
return GetLocalTilesIntersecting(localAABB, ignoreEmpty, predicate);
|
||||
return MapSystem.GetLocalTilesIntersecting(Owner, this, localArea, ignoreEmpty, predicate);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<TileRef> GetTilesIntersecting(Box2Rotated worldArea, bool ignoreEmpty = true,
|
||||
Predicate<TileRef>? predicate = null)
|
||||
{
|
||||
var matrix = _entMan.GetComponent<TransformComponent>(Owner).InvWorldMatrix;
|
||||
var localArea = matrix.TransformBox(worldArea);
|
||||
|
||||
foreach (var tile in GetLocalTilesIntersecting(localArea, ignoreEmpty, predicate))
|
||||
{
|
||||
yield return tile;
|
||||
}
|
||||
return MapSystem.GetTilesIntersecting(Owner, this, worldArea, ignoreEmpty, predicate);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<TileRef> GetTilesIntersecting(Box2 worldArea, bool ignoreEmpty = true,
|
||||
Predicate<TileRef>? predicate = null)
|
||||
{
|
||||
var matrix = _entMan.GetComponent<TransformComponent>(Owner).InvWorldMatrix;
|
||||
var localArea = matrix.TransformBox(worldArea);
|
||||
|
||||
foreach (var tile in GetLocalTilesIntersecting(localArea, ignoreEmpty, predicate))
|
||||
{
|
||||
yield return tile;
|
||||
}
|
||||
return MapSystem.GetTilesIntersecting(Owner, this, worldArea, ignoreEmpty, predicate);
|
||||
}
|
||||
|
||||
public IEnumerable<TileRef> GetLocalTilesIntersecting(Box2 localArea, bool ignoreEmpty = true,
|
||||
Predicate<TileRef>? predicate = null)
|
||||
{
|
||||
// TODO: Should move the intersecting calls onto mapmanager system and then allow people to pass in xform / xformquery
|
||||
// that way we can avoid the GetComp here.
|
||||
var gridTileLb = new Vector2i((int)Math.Floor(localArea.Left), (int)Math.Floor(localArea.Bottom));
|
||||
// If we have 20.1 we want to include that tile but if we have 20 then we don't.
|
||||
var gridTileRt = new Vector2i((int)Math.Ceiling(localArea.Right), (int)Math.Ceiling(localArea.Top));
|
||||
|
||||
for (var x = gridTileLb.X; x < gridTileRt.X; x++)
|
||||
{
|
||||
for (var y = gridTileLb.Y; y < gridTileRt.Y; y++)
|
||||
{
|
||||
var gridChunk = GridTileToChunkIndices(new Vector2i(x, y));
|
||||
|
||||
if (Chunks.TryGetValue(gridChunk, out var chunk))
|
||||
{
|
||||
var chunkTile = chunk.GridTileToChunkTile(new Vector2i(x, y));
|
||||
var tile = GetTileRef(chunk, (ushort)chunkTile.X, (ushort)chunkTile.Y);
|
||||
|
||||
if (ignoreEmpty && tile.Tile.IsEmpty)
|
||||
continue;
|
||||
|
||||
if (predicate == null || predicate(tile))
|
||||
yield return tile;
|
||||
}
|
||||
else if (!ignoreEmpty)
|
||||
{
|
||||
var tile = new TileRef(Owner, x, y, new Tile());
|
||||
|
||||
if (predicate == null || predicate(tile))
|
||||
yield return tile;
|
||||
}
|
||||
}
|
||||
}
|
||||
return MapSystem.GetLocalTilesIntersecting(Owner, this, localArea, ignoreEmpty, predicate);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<TileRef> GetTilesIntersecting(Circle worldArea, bool ignoreEmpty = true,
|
||||
Predicate<TileRef>? predicate = null)
|
||||
{
|
||||
var aabb = new Box2(worldArea.Position.X - worldArea.Radius, worldArea.Position.Y - worldArea.Radius,
|
||||
worldArea.Position.X + worldArea.Radius, worldArea.Position.Y + worldArea.Radius);
|
||||
var circleGridPos = new EntityCoordinates(Owner, WorldToLocal(worldArea.Position));
|
||||
|
||||
foreach (var tile in GetTilesIntersecting(aabb, ignoreEmpty, predicate))
|
||||
{
|
||||
var local = GridTileToLocal(tile.GridIndices);
|
||||
|
||||
if (!local.TryDistance(_entMan, circleGridPos, out var distance))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (distance <= worldArea.Radius)
|
||||
{
|
||||
yield return tile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryGetTile(Vector2i indices, bool ignoreEmpty, [NotNullWhen(true)] out TileRef? tileRef, Predicate<TileRef>? predicate = null)
|
||||
{
|
||||
// Similar to TryGetTileRef but for the tiles intersecting iterators.
|
||||
var gridChunk = GridTileToChunkIndices(indices);
|
||||
|
||||
if (Chunks.TryGetValue(gridChunk, out var chunk))
|
||||
{
|
||||
var chunkTile = chunk.GridTileToChunkTile(indices);
|
||||
var tile = GetTileRef(chunk, (ushort)chunkTile.X, (ushort)chunkTile.Y);
|
||||
|
||||
if (ignoreEmpty && tile.Tile.IsEmpty)
|
||||
{
|
||||
tileRef = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (predicate == null || predicate(tile))
|
||||
{
|
||||
tileRef = tile;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (!ignoreEmpty)
|
||||
{
|
||||
var tile = new TileRef(Owner, indices.X, indices.Y, Tile.Empty);
|
||||
|
||||
if (predicate == null || predicate(tile))
|
||||
{
|
||||
tileRef = tile;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
tileRef = null;
|
||||
return false;
|
||||
return MapSystem.GetTilesIntersecting(Owner, this, worldArea, ignoreEmpty, predicate);
|
||||
}
|
||||
|
||||
#endregion TileAccess
|
||||
|
||||
#region ChunkAccess
|
||||
|
||||
/// <summary>
|
||||
/// The total number of allocated chunks in the grid.
|
||||
/// </summary>
|
||||
public int ChunkCount => Chunks.Count;
|
||||
|
||||
/// <inheritdoc />
|
||||
internal MapChunk GetOrAddChunk(int xIndex, int yIndex)
|
||||
{
|
||||
return GetOrAddChunk(new Vector2i(xIndex, yIndex));
|
||||
}
|
||||
|
||||
internal bool TryGetChunk(Vector2i chunkIndices, [NotNullWhen(true)] out MapChunk? chunk)
|
||||
{
|
||||
return Chunks.TryGetValue(chunkIndices, out chunk);
|
||||
return MapSystem.TryGetChunk(Owner, this, chunkIndices, out chunk);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
internal MapChunk GetOrAddChunk(Vector2i chunkIndices)
|
||||
{
|
||||
if (Chunks.TryGetValue(chunkIndices, out var output))
|
||||
return output;
|
||||
|
||||
var newChunk = new MapChunk(chunkIndices.X, chunkIndices.Y, ChunkSize);
|
||||
newChunk.LastTileModifiedTick = _mapManager.GameTiming.CurTick;
|
||||
|
||||
if (Initialized)
|
||||
newChunk.TileModified += OnTileModified;
|
||||
|
||||
return Chunks[chunkIndices] = newChunk;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasChunk(Vector2i chunkIndices)
|
||||
{
|
||||
return Chunks.ContainsKey(chunkIndices);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
internal IReadOnlyDictionary<Vector2i, MapChunk> GetMapChunks()
|
||||
{
|
||||
return Chunks;
|
||||
return MapSystem.GetMapChunks(Owner, this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
internal ChunkEnumerator GetMapChunks(Box2 worldAABB)
|
||||
{
|
||||
var localAABB = _entMan.GetComponent<TransformComponent>(Owner).InvWorldMatrix
|
||||
.TransformBox(worldAABB);
|
||||
return new ChunkEnumerator(Chunks, localAABB, ChunkSize);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
internal ChunkEnumerator GetMapChunks(Box2Rotated worldArea)
|
||||
{
|
||||
var matrix = _entMan.GetComponent<TransformComponent>(Owner).InvWorldMatrix;
|
||||
var localArea = matrix.TransformBox(worldArea);
|
||||
return new ChunkEnumerator(Chunks, localArea, ChunkSize);
|
||||
}
|
||||
|
||||
internal ChunkEnumerator GetLocalMapChunks(Box2 localAABB)
|
||||
{
|
||||
return new ChunkEnumerator(Chunks, localAABB, ChunkSize);
|
||||
return MapSystem.GetMapChunks(Owner, this, worldArea);
|
||||
}
|
||||
|
||||
#endregion ChunkAccess
|
||||
|
||||
#region SnapGridAccess
|
||||
|
||||
/// <inheritdoc />
|
||||
public int AnchoredEntityCount(Vector2i pos)
|
||||
{
|
||||
var gridChunkPos = GridTileToChunkIndices(pos);
|
||||
|
||||
if (!Chunks.TryGetValue(gridChunkPos, out var chunk)) return 0;
|
||||
|
||||
var (x, y) = chunk.GridTileToChunkTile(pos);
|
||||
return chunk.GetSnapGrid((ushort)x, (ushort)y)?.Count ?? 0; // ?
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<EntityUid> GetAnchoredEntities(MapCoordinates coords)
|
||||
{
|
||||
return GetAnchoredEntities(TileIndicesFor(coords));
|
||||
return MapSystem.GetAnchoredEntities(Owner, this, coords);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<EntityUid> GetAnchoredEntities(EntityCoordinates coords)
|
||||
{
|
||||
return GetAnchoredEntities(TileIndicesFor(coords));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<EntityUid> GetAnchoredEntities(Vector2i pos)
|
||||
{
|
||||
// Because some content stuff checks neighboring tiles (which may not actually exist) we won't just
|
||||
// create an entire chunk for it.
|
||||
var gridChunkPos = GridTileToChunkIndices(pos);
|
||||
|
||||
if (!Chunks.TryGetValue(gridChunkPos, out var chunk)) return Enumerable.Empty<EntityUid>();
|
||||
|
||||
var chunkTile = chunk.GridTileToChunkTile(pos);
|
||||
return chunk.GetSnapGridCell((ushort)chunkTile.X, (ushort)chunkTile.Y);
|
||||
return MapSystem.GetAnchoredEntities(Owner, this, pos);
|
||||
}
|
||||
|
||||
public AnchoredEntitiesEnumerator GetAnchoredEntitiesEnumerator(Vector2i pos)
|
||||
{
|
||||
var gridChunkPos = GridTileToChunkIndices(pos);
|
||||
|
||||
if (!Chunks.TryGetValue(gridChunkPos, out var chunk)) return AnchoredEntitiesEnumerator.Empty;
|
||||
|
||||
var chunkTile = chunk.GridTileToChunkTile(pos);
|
||||
var snapgrid = chunk.GetSnapGrid((ushort)chunkTile.X, (ushort)chunkTile.Y);
|
||||
|
||||
return snapgrid == null
|
||||
? AnchoredEntitiesEnumerator.Empty
|
||||
: new AnchoredEntitiesEnumerator(snapgrid.GetEnumerator());
|
||||
return MapSystem.GetAnchoredEntitiesEnumerator(Owner, this, pos);
|
||||
}
|
||||
|
||||
public IEnumerable<EntityUid> GetLocalAnchoredEntities(Box2 localAABB)
|
||||
{
|
||||
foreach (var tile in GetLocalTilesIntersecting(localAABB, true, null))
|
||||
{
|
||||
foreach (var ent in GetAnchoredEntities(tile.GridIndices))
|
||||
{
|
||||
yield return ent;
|
||||
}
|
||||
}
|
||||
return MapSystem.GetLocalAnchoredEntities(Owner, this, localAABB);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<EntityUid> GetAnchoredEntities(Box2 worldAABB)
|
||||
{
|
||||
foreach (var tile in GetTilesIntersecting(worldAABB))
|
||||
{
|
||||
foreach (var ent in GetAnchoredEntities(tile.GridIndices))
|
||||
{
|
||||
yield return ent;
|
||||
}
|
||||
}
|
||||
return MapSystem.GetAnchoredEntities(Owner, this, worldAABB);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<EntityUid> GetAnchoredEntities(Box2Rotated worldBounds)
|
||||
{
|
||||
foreach (var tile in GetTilesIntersecting(worldBounds))
|
||||
{
|
||||
foreach (var ent in GetAnchoredEntities(tile.GridIndices))
|
||||
{
|
||||
yield return ent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Vector2i TileIndicesFor(EntityCoordinates coords)
|
||||
{
|
||||
#if DEBUG
|
||||
var mapId = _entMan.GetComponent<TransformComponent>(Owner).MapID;
|
||||
DebugTools.Assert(mapId == coords.GetMapId(_entMan));
|
||||
#endif
|
||||
|
||||
return SnapGridLocalCellFor(LocalToGrid(coords));
|
||||
return MapSystem.TileIndicesFor(Owner, this, coords);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Vector2i TileIndicesFor(MapCoordinates worldPos)
|
||||
{
|
||||
#if DEBUG
|
||||
var mapId = _entMan.GetComponent<TransformComponent>(Owner).MapID;
|
||||
DebugTools.Assert(mapId == worldPos.MapId);
|
||||
#endif
|
||||
|
||||
var localPos = WorldToLocal(worldPos.Position);
|
||||
return SnapGridLocalCellFor(localPos);
|
||||
return MapSystem.TileIndicesFor(Owner, this, worldPos);
|
||||
}
|
||||
|
||||
private Vector2i SnapGridLocalCellFor(Vector2 localPos)
|
||||
{
|
||||
var x = (int)Math.Floor(localPos.X / TileSize);
|
||||
var y = (int)Math.Floor(localPos.Y / TileSize);
|
||||
return new Vector2i(x, y);
|
||||
}
|
||||
|
||||
public bool IsAnchored(EntityCoordinates coords, EntityUid euid)
|
||||
{
|
||||
var tilePos = TileIndicesFor(coords);
|
||||
var (chunk, chunkTile) = ChunkAndOffsetForTile(tilePos);
|
||||
var snapgrid = chunk.GetSnapGrid((ushort)chunkTile.X, (ushort)chunkTile.Y);
|
||||
return snapgrid?.Contains(euid) == true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool AddToSnapGridCell(Vector2i pos, EntityUid euid)
|
||||
{
|
||||
var (chunk, chunkTile) = ChunkAndOffsetForTile(pos);
|
||||
|
||||
if (chunk.GetTile((ushort)chunkTile.X, (ushort)chunkTile.Y).IsEmpty)
|
||||
return false;
|
||||
|
||||
chunk.AddToSnapGridCell((ushort)chunkTile.X, (ushort)chunkTile.Y, euid);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool AddToSnapGridCell(EntityCoordinates coords, EntityUid euid)
|
||||
{
|
||||
return AddToSnapGridCell(TileIndicesFor(coords), euid);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RemoveFromSnapGridCell(Vector2i pos, EntityUid euid)
|
||||
{
|
||||
var (chunk, chunkTile) = ChunkAndOffsetForTile(pos);
|
||||
chunk.RemoveFromSnapGridCell((ushort)chunkTile.X, (ushort)chunkTile.Y, euid);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RemoveFromSnapGridCell(EntityCoordinates coords, EntityUid euid)
|
||||
{
|
||||
RemoveFromSnapGridCell(TileIndicesFor(coords), euid);
|
||||
}
|
||||
|
||||
private (MapChunk, Vector2i) ChunkAndOffsetForTile(Vector2i pos)
|
||||
{
|
||||
var gridChunkIndices = GridTileToChunkIndices(pos);
|
||||
var chunk = GetOrAddChunk(gridChunkIndices);
|
||||
var chunkTile = chunk.GridTileToChunkTile(pos);
|
||||
return (chunk, chunkTile);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<EntityUid> GetInDir(EntityCoordinates position, Direction dir)
|
||||
{
|
||||
var pos = SharedMapSystem.GetDirection(TileIndicesFor(position), dir);
|
||||
return GetAnchoredEntities(pos);
|
||||
return MapSystem.GetInDir(Owner, this, position, dir);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<EntityUid> GetOffset(EntityCoordinates coords, Vector2i offset)
|
||||
{
|
||||
var pos = TileIndicesFor(coords) + offset;
|
||||
return GetAnchoredEntities(pos);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<EntityUid> GetLocal(EntityCoordinates coords)
|
||||
{
|
||||
return GetAnchoredEntities(TileIndicesFor(coords));
|
||||
return MapSystem.GetLocal(Owner, this, coords);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public EntityCoordinates DirectionToGrid(EntityCoordinates coords, Direction direction)
|
||||
{
|
||||
return GridTileToLocal(SharedMapSystem.GetDirection(TileIndicesFor(coords), direction));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<EntityUid> GetCardinalNeighborCells(EntityCoordinates coords)
|
||||
{
|
||||
var position = TileIndicesFor(coords);
|
||||
foreach (var cell in GetAnchoredEntities(position))
|
||||
yield return cell;
|
||||
foreach (var cell in GetAnchoredEntities(position + new Vector2i(0, 1)))
|
||||
yield return cell;
|
||||
foreach (var cell in GetAnchoredEntities(position + new Vector2i(0, -1)))
|
||||
yield return cell;
|
||||
foreach (var cell in GetAnchoredEntities(position + new Vector2i(1, 0)))
|
||||
yield return cell;
|
||||
foreach (var cell in GetAnchoredEntities(position + new Vector2i(-1, 0)))
|
||||
yield return cell;
|
||||
return MapSystem.GetCardinalNeighborCells(Owner, this, coords);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<EntityUid> GetCellsInSquareArea(EntityCoordinates coords, int n)
|
||||
{
|
||||
var position = TileIndicesFor(coords);
|
||||
|
||||
for (var y = -n; y <= n; ++y)
|
||||
for (var x = -n; x <= n; ++x)
|
||||
{
|
||||
var enumerator = GetAnchoredEntitiesEnumerator(position + new Vector2i(x, y));
|
||||
|
||||
while (enumerator.MoveNext(out var cell))
|
||||
{
|
||||
yield return cell.Value;
|
||||
}
|
||||
}
|
||||
return MapSystem.GetCellsInSquareArea(Owner, this, coords, n);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Transforms
|
||||
|
||||
/// <inheritdoc />
|
||||
public Vector2 WorldToLocal(Vector2 posWorld)
|
||||
{
|
||||
var matrix = _entMan.GetComponent<TransformComponent>(Owner).InvWorldMatrix;
|
||||
return matrix.Transform(posWorld);
|
||||
return MapSystem.WorldToLocal(Owner, this, posWorld);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public EntityCoordinates MapToGrid(MapCoordinates posWorld)
|
||||
{
|
||||
var mapId = _entMan.GetComponent<TransformComponent>(Owner).MapID;
|
||||
|
||||
if (posWorld.MapId != mapId)
|
||||
throw new ArgumentException(
|
||||
$"Grid {Owner} is on map {mapId}, but coords are on map {posWorld.MapId}.",
|
||||
nameof(posWorld));
|
||||
|
||||
if (!_mapManager.TryGetGrid(Owner, out var grid))
|
||||
{
|
||||
return new EntityCoordinates(_mapManager.GetMapEntityId(posWorld.MapId), new Vector2(posWorld.X, posWorld.Y));
|
||||
}
|
||||
|
||||
return new EntityCoordinates(((Component) grid).Owner, WorldToLocal(posWorld.Position));
|
||||
return MapSystem.MapToGrid(Owner, posWorld);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Vector2 LocalToWorld(Vector2 posLocal)
|
||||
{
|
||||
var matrix = _entMan.GetComponent<TransformComponent>(Owner).WorldMatrix;
|
||||
return matrix.Transform(posLocal);
|
||||
return MapSystem.LocalToWorld(Owner, this, posLocal);
|
||||
}
|
||||
|
||||
public Vector2i WorldToTile(Vector2 posWorld)
|
||||
{
|
||||
var local = WorldToLocal(posWorld);
|
||||
var x = (int)Math.Floor(local.X / TileSize);
|
||||
var y = (int)Math.Floor(local.Y / TileSize);
|
||||
return new Vector2i(x, y);
|
||||
return MapSystem.WorldToTile(Owner, this, posWorld);
|
||||
}
|
||||
|
||||
public Vector2i LocalToTile(EntityCoordinates coordinates)
|
||||
{
|
||||
var position = LocalToGrid(coordinates);
|
||||
return new Vector2i((int) Math.Floor(position.X / TileSize), (int) Math.Floor(position.Y / TileSize));
|
||||
return MapSystem.LocalToTile(Owner, this, coordinates);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Vector2i CoordinatesToTile(MapCoordinates coords)
|
||||
{
|
||||
#if DEBUG
|
||||
var mapId = _entMan.GetComponent<TransformComponent>(Owner).MapID;
|
||||
DebugTools.Assert(mapId == coords.MapId);
|
||||
#endif
|
||||
|
||||
var local = WorldToLocal(coords.Position);
|
||||
|
||||
var x = (int)Math.Floor(local.X / TileSize);
|
||||
var y = (int)Math.Floor(local.Y / TileSize);
|
||||
return new Vector2i(x, y);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Vector2i CoordinatesToTile(EntityCoordinates coords)
|
||||
{
|
||||
#if DEBUG
|
||||
var mapId = _entMan.GetComponent<TransformComponent>(Owner).MapID;
|
||||
DebugTools.Assert(mapId == coords.GetMapId(_entMan));
|
||||
#endif
|
||||
var local = LocalToGrid(coords);
|
||||
|
||||
var x = (int)Math.Floor(local.X / TileSize);
|
||||
var y = (int)Math.Floor(local.Y / TileSize);
|
||||
return new Vector2i(x, y);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Vector2i LocalToChunkIndices(EntityCoordinates gridPos)
|
||||
{
|
||||
var local = LocalToGrid(gridPos);
|
||||
|
||||
var x = (int)Math.Floor(local.X / (TileSize * ChunkSize));
|
||||
var y = (int)Math.Floor(local.Y / (TileSize * ChunkSize));
|
||||
return new Vector2i(x, y);
|
||||
}
|
||||
|
||||
public Vector2 LocalToGrid(EntityCoordinates position)
|
||||
{
|
||||
return position.EntityId == Owner
|
||||
? position.Position
|
||||
: WorldToLocal(position.ToMapPos(_entMan));
|
||||
return MapSystem.CoordinatesToTile(Owner, this, coords);
|
||||
}
|
||||
|
||||
public bool CollidesWithGrid(Vector2i indices)
|
||||
{
|
||||
var chunkIndices = GridTileToChunkIndices(indices);
|
||||
if (!Chunks.TryGetValue(chunkIndices, out var chunk))
|
||||
return false;
|
||||
|
||||
var cTileIndices = chunk.GridTileToChunkTile(indices);
|
||||
return chunk.GetTile((ushort)cTileIndices.X, (ushort)cTileIndices.Y).TypeId != Tile.Empty.TypeId;
|
||||
return MapSystem.CollidesWithGrid(Owner, this, indices);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Vector2i GridTileToChunkIndices(Vector2i gridTile)
|
||||
{
|
||||
var x = (int)Math.Floor(gridTile.X / (float)ChunkSize);
|
||||
var y = (int)Math.Floor(gridTile.Y / (float)ChunkSize);
|
||||
|
||||
return new Vector2i(x, y);
|
||||
return MapSystem.GridTileToChunkIndices(Owner, this, gridTile);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public EntityCoordinates GridTileToLocal(Vector2i gridTile)
|
||||
{
|
||||
return new(Owner,
|
||||
new Vector2(gridTile.X * TileSize + (TileSize / 2f), gridTile.Y * TileSize + (TileSize / 2f)));
|
||||
return MapSystem.GridTileToLocal(Owner, this, gridTile);
|
||||
}
|
||||
|
||||
public Vector2 GridTileToWorldPos(Vector2i gridTile)
|
||||
{
|
||||
var locX = gridTile.X * TileSize + (TileSize / 2f);
|
||||
var locY = gridTile.Y * TileSize + (TileSize / 2f);
|
||||
var xform = _entMan.GetComponent<TransformComponent>(Owner);
|
||||
|
||||
return xform.WorldMatrix.Transform(new Vector2(locX, locY));
|
||||
return MapSystem.GridTileToWorldPos(Owner, this, gridTile);
|
||||
}
|
||||
|
||||
public MapCoordinates GridTileToWorld(Vector2i gridTile)
|
||||
{
|
||||
var parentMapId = _entMan.GetComponent<TransformComponent>(Owner).MapID;
|
||||
|
||||
return new(GridTileToWorldPos(gridTile), parentMapId);
|
||||
return MapSystem.GridTileToWorld(Owner, this, gridTile);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryGetTileRef(Vector2i indices, out TileRef tile)
|
||||
{
|
||||
var chunkIndices = GridTileToChunkIndices(indices);
|
||||
if (!Chunks.TryGetValue(chunkIndices, out var chunk))
|
||||
{
|
||||
tile = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
var cTileIndices = chunk.GridTileToChunkTile(indices);
|
||||
tile = GetTileRef(chunk, (ushort)cTileIndices.X, (ushort)cTileIndices.Y);
|
||||
return true;
|
||||
return MapSystem.TryGetTileRef(Owner, this, indices, out tile);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryGetTileRef(EntityCoordinates coords, out TileRef tile)
|
||||
{
|
||||
return TryGetTileRef(CoordinatesToTile(coords), out tile);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryGetTileRef(Vector2 worldPos, out TileRef tile)
|
||||
{
|
||||
return TryGetTileRef(WorldToTile(worldPos), out tile);
|
||||
}
|
||||
|
||||
#endregion Transforms
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the world space AABB for this chunk.
|
||||
/// </summary>
|
||||
internal Box2 CalcWorldAABB(MapChunk mapChunk)
|
||||
{
|
||||
var (position, rotation) =
|
||||
_entMan.GetComponent<TransformComponent>(Owner).GetWorldPositionRotation();
|
||||
|
||||
var chunkPosition = mapChunk.Indices;
|
||||
var tileScale = TileSize;
|
||||
var chunkScale = mapChunk.ChunkSize;
|
||||
|
||||
var worldPos = position + rotation.RotateVec(chunkPosition * tileScale * chunkScale);
|
||||
|
||||
return new Box2Rotated(
|
||||
((Box2)mapChunk.CachedBounds
|
||||
.Scale(tileScale))
|
||||
.Translated(worldPos),
|
||||
rotation, worldPos).CalcBoundingBox();
|
||||
}
|
||||
|
||||
internal void OnTileModified(MapChunk mapChunk, Vector2i tileIndices, Tile newTile, Tile oldTile,
|
||||
bool shapeChanged)
|
||||
{
|
||||
// As the collision regeneration can potentially delete the chunk we'll notify of the tile changed first.
|
||||
var gridTile = mapChunk.ChunkTileToGridTile(tileIndices);
|
||||
mapChunk.LastTileModifiedTick = _mapManager.GameTiming.CurTick;
|
||||
LastTileModifiedTick = _mapManager.GameTiming.CurTick;
|
||||
_entMan.Dirty(this);
|
||||
|
||||
// The map serializer currently sets tiles of unbound grids as part of the deserialization process
|
||||
// It properly sets SuppressOnTileChanged so that the event isn't spammed for every tile on the grid.
|
||||
// ParentMapId is not able to be accessed on unbound grids, so we can't even call this function for unbound grids.
|
||||
if (!_mapManager.SuppressOnTileChanged)
|
||||
{
|
||||
var newTileRef = new TileRef(Owner, gridTile, newTile);
|
||||
_mapManager.RaiseOnTileChanged(newTileRef, oldTile);
|
||||
}
|
||||
|
||||
if (shapeChanged && !mapChunk.SuppressCollisionRegeneration)
|
||||
{
|
||||
RegenerateCollision(mapChunk);
|
||||
}
|
||||
return MapSystem.TryGetTileRef(Owner, this, coords, out tile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,66 +24,67 @@ namespace Robust.Shared.Map
|
||||
IoCManager.Resolve(ref entityManager, ref mapManager);
|
||||
|
||||
var gridId = coords.GetGridUid(entityManager);
|
||||
var mapSystem = entityManager.System<SharedMapSystem>();
|
||||
|
||||
if (mapManager.TryGetGrid(gridId, out var mapGrid))
|
||||
{
|
||||
return mapGrid.GridTileToLocal(mapGrid.CoordinatesToTile(coords));
|
||||
return mapSystem.GridTileToLocal(gridId.Value, mapGrid, mapSystem.CoordinatesToTile(gridId.Value, mapGrid, coords));
|
||||
}
|
||||
else
|
||||
|
||||
var mapCoords = coords.ToMap(entityManager);
|
||||
|
||||
if (mapManager.TryFindGridAt(mapCoords, out var gridUid, out mapGrid))
|
||||
{
|
||||
var mapCoords = coords.ToMap(entityManager);
|
||||
|
||||
if (mapManager.TryFindGridAt(mapCoords, out _, out mapGrid))
|
||||
{
|
||||
return mapGrid.GridTileToLocal(mapGrid.CoordinatesToTile(coords));
|
||||
}
|
||||
|
||||
// create a box around the cursor
|
||||
var gridSearchBox = Box2.UnitCentered.Scale(searchBoxSize).Translated(mapCoords.Position);
|
||||
|
||||
// find grids in search box
|
||||
var gridsInArea = mapManager.FindGridsIntersecting(mapCoords.MapId, gridSearchBox);
|
||||
|
||||
// find closest grid intersecting our search box.
|
||||
MapGridComponent? closest = null;
|
||||
var distance = float.PositiveInfinity;
|
||||
var intersect = new Box2();
|
||||
var xformQuery = entityManager.GetEntityQuery<TransformComponent>();
|
||||
|
||||
foreach (var grid in gridsInArea)
|
||||
{
|
||||
var gridXform = xformQuery.GetComponent(grid.Owner);
|
||||
// TODO: Use CollisionManager to get nearest edge.
|
||||
|
||||
// figure out closest intersect
|
||||
var gridIntersect = gridSearchBox.Intersect(gridXform.WorldMatrix.TransformBox(grid.LocalAABB));
|
||||
var gridDist = (gridIntersect.Center - mapCoords.Position).LengthSquared();
|
||||
|
||||
if (gridDist >= distance)
|
||||
continue;
|
||||
|
||||
distance = gridDist;
|
||||
closest = grid;
|
||||
intersect = gridIntersect;
|
||||
}
|
||||
|
||||
if (closest != null) // stick to existing grid
|
||||
{
|
||||
// round to nearest cardinal dir
|
||||
var normal = mapCoords.Position - intersect.Center;
|
||||
|
||||
// round coords to center of tile
|
||||
var tileIndices = closest.WorldToTile(intersect.Center);
|
||||
var tileCenterWorld = closest.GridTileToWorldPos(tileIndices);
|
||||
|
||||
// move mouse one tile out along normal
|
||||
var newTilePos = tileCenterWorld + normal * closest.TileSize;
|
||||
|
||||
coords = new EntityCoordinates(closest.Owner, closest.WorldToLocal(newTilePos));
|
||||
}
|
||||
//else free place
|
||||
return mapSystem.GridTileToLocal(gridUid, mapGrid, mapSystem.CoordinatesToTile(gridUid, mapGrid, coords));
|
||||
}
|
||||
|
||||
// create a box around the cursor
|
||||
var gridSearchBox = Box2.UnitCentered.Scale(searchBoxSize).Translated(mapCoords.Position);
|
||||
|
||||
// find grids in search box
|
||||
var gridsInArea = mapManager.FindGridsIntersecting(mapCoords.MapId, gridSearchBox);
|
||||
|
||||
// find closest grid intersecting our search box.
|
||||
gridUid = EntityUid.Invalid;
|
||||
MapGridComponent? closest = null;
|
||||
var distance = float.PositiveInfinity;
|
||||
var intersect = new Box2();
|
||||
var xformQuery = entityManager.GetEntityQuery<TransformComponent>();
|
||||
|
||||
foreach (var grid in gridsInArea)
|
||||
{
|
||||
var gridXform = xformQuery.GetComponent(grid.Owner);
|
||||
// TODO: Use CollisionManager to get nearest edge.
|
||||
|
||||
// figure out closest intersect
|
||||
var gridIntersect = gridSearchBox.Intersect(gridXform.WorldMatrix.TransformBox(grid.LocalAABB));
|
||||
var gridDist = (gridIntersect.Center - mapCoords.Position).LengthSquared();
|
||||
|
||||
if (gridDist >= distance)
|
||||
continue;
|
||||
|
||||
gridUid = grid.Owner;
|
||||
distance = gridDist;
|
||||
closest = grid;
|
||||
intersect = gridIntersect;
|
||||
}
|
||||
|
||||
if (closest != null) // stick to existing grid
|
||||
{
|
||||
// round to nearest cardinal dir
|
||||
var normal = mapCoords.Position - intersect.Center;
|
||||
|
||||
// round coords to center of tile
|
||||
var tileIndices = mapSystem.WorldToTile(gridUid, closest, intersect.Center);
|
||||
var tileCenterWorld = mapSystem.GridTileToWorldPos(gridUid, closest, tileIndices);
|
||||
|
||||
// move mouse one tile out along normal
|
||||
var newTilePos = tileCenterWorld + normal * closest.TileSize;
|
||||
|
||||
coords = new EntityCoordinates(gridUid, mapSystem.WorldToLocal(gridUid, closest, newTilePos));
|
||||
}
|
||||
//else free place
|
||||
|
||||
return coords;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,10 +191,12 @@ namespace Robust.Shared.Map
|
||||
if(!IsValid(entityManager))
|
||||
return new Vector2i();
|
||||
|
||||
var mapSystem = entityManager.System<SharedMapSystem>();
|
||||
var gridIdOpt = GetGridUid(entityManager);
|
||||
if (gridIdOpt is { } gridId && gridId.IsValid())
|
||||
{
|
||||
return mapManager.GetGrid(gridId).GetTileRef(this).GridIndices;
|
||||
var grid = mapManager.GetGrid(gridId);
|
||||
return mapSystem.GetTileRef(gridId, grid, this).GridIndices;
|
||||
}
|
||||
|
||||
var vec = ToMapPos(entityManager, transformSystem);
|
||||
|
||||
21
Robust.Shared/Map/Events/RegenerateGridBoundsEvent.cs
Normal file
21
Robust.Shared/Map/Events/RegenerateGridBoundsEvent.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Shared.Map.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Raised directed on a grid to get its bounds.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Really this exists to get around test dependency creeping.
|
||||
/// </remarks>
|
||||
[ByRefEvent]
|
||||
internal readonly record struct RegenerateGridBoundsEvent(EntityUid Entity, Dictionary<MapChunk, List<Box2i>> ChunkRectangles, List<MapChunk> RemovedChunks)
|
||||
{
|
||||
public readonly EntityUid Entity = Entity;
|
||||
|
||||
public readonly Dictionary<MapChunk, List<Box2i>> ChunkRectangles = ChunkRectangles;
|
||||
|
||||
public readonly List<MapChunk> RemovedChunks = RemovedChunks;
|
||||
}
|
||||
@@ -22,15 +22,9 @@ namespace Robust.Shared.Map
|
||||
|
||||
private readonly Vector2i _gridIndices;
|
||||
|
||||
[ViewVariables]
|
||||
private readonly Tile[,] _tiles;
|
||||
[ViewVariables] internal readonly Tile[,] Tiles;
|
||||
private readonly SnapGridCell[,] _snapGrid;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a tile is modified on this chunk.
|
||||
/// </summary>
|
||||
public event TileModifiedDelegate? TileModified;
|
||||
|
||||
/// <summary>
|
||||
/// Keeps a running count of the number of filled tiles in this chunk.
|
||||
/// </summary>
|
||||
@@ -38,7 +32,7 @@ namespace Robust.Shared.Map
|
||||
/// This will always be between 1 and <see cref="ChunkSize"/>^2.
|
||||
/// </remarks>
|
||||
[ViewVariables]
|
||||
internal int FilledTiles { get; private set; }
|
||||
internal int FilledTiles { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Chunk-local AABB of this chunk.
|
||||
@@ -73,7 +67,7 @@ namespace Robust.Shared.Map
|
||||
_gridIndices = new Vector2i(x, y);
|
||||
ChunkSize = chunkSize;
|
||||
|
||||
_tiles = new Tile[ChunkSize, ChunkSize];
|
||||
Tiles = new Tile[ChunkSize, ChunkSize];
|
||||
_snapGrid = new SnapGridCell[ChunkSize, ChunkSize];
|
||||
}
|
||||
|
||||
@@ -113,58 +107,12 @@ namespace Robust.Shared.Map
|
||||
if (yIndex >= ChunkSize)
|
||||
throw new ArgumentOutOfRangeException(nameof(yIndex), "Tile indices out of bounds.");
|
||||
|
||||
return _tiles[xIndex, yIndex];
|
||||
return Tiles[xIndex, yIndex];
|
||||
}
|
||||
|
||||
public Tile GetTile(Vector2i indices)
|
||||
{
|
||||
return _tiles[indices.X, indices.Y];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces a single tile inside of the chunk.
|
||||
/// </summary>
|
||||
/// <param name="xIndex">The X tile index relative to the chunk.</param>
|
||||
/// <param name="yIndex">The Y tile index relative to the chunk.</param>
|
||||
/// <param name="tile">The new tile to insert.</param>
|
||||
public void SetTile(ushort xIndex, ushort yIndex, Tile tile)
|
||||
{
|
||||
if (xIndex >= ChunkSize)
|
||||
throw new ArgumentOutOfRangeException(nameof(xIndex), "Tile indices out of bounds.");
|
||||
|
||||
if (yIndex >= ChunkSize)
|
||||
throw new ArgumentOutOfRangeException(nameof(yIndex), "Tile indices out of bounds.");
|
||||
|
||||
// same tile, no point to continue
|
||||
if (_tiles[xIndex, yIndex] == tile)
|
||||
return;
|
||||
|
||||
var oldTile = _tiles[xIndex, yIndex];
|
||||
var oldFilledTiles = FilledTiles;
|
||||
|
||||
if (oldTile.IsEmpty != tile.IsEmpty)
|
||||
{
|
||||
if (oldTile.IsEmpty)
|
||||
{
|
||||
FilledTiles += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
FilledTiles -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
var shapeChanged = oldFilledTiles != FilledTiles;
|
||||
DebugTools.Assert(FilledTiles >= 0);
|
||||
|
||||
_tiles[xIndex, yIndex] = tile;
|
||||
|
||||
var tileIndices = new Vector2i(xIndex, yIndex);
|
||||
|
||||
// God I hate C# events sometimes.
|
||||
DebugTools.Assert(TileModified == null || TileModified.GetInvocationList().Length <= 1);
|
||||
|
||||
TileModified?.Invoke(this, tileIndices, tile, oldTile, shapeChanged);
|
||||
return Tiles[indices.X, indices.Y];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -266,6 +214,48 @@ namespace Robust.Shared.Map
|
||||
{
|
||||
public List<EntityUid>? Center;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the tile without any callbacks.
|
||||
/// Do not call this unless you know what you are doing.
|
||||
/// </summary>
|
||||
internal bool TrySetTile(ushort xIndex, ushort yIndex, Tile tile, out Tile oldTile, out bool shapeChanged)
|
||||
{
|
||||
if (xIndex >= Tiles.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(xIndex), "Tile indices out of bounds.");
|
||||
|
||||
if (yIndex >= Tiles.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(yIndex), "Tile indices out of bounds.");
|
||||
|
||||
// same tile, no point to continue
|
||||
if (Tiles[xIndex, yIndex] == tile)
|
||||
{
|
||||
oldTile = Tile.Empty;
|
||||
shapeChanged = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
oldTile = Tiles[xIndex, yIndex];
|
||||
var oldFilledTiles = FilledTiles;
|
||||
|
||||
if (oldTile.IsEmpty != tile.IsEmpty)
|
||||
{
|
||||
if (oldTile.IsEmpty)
|
||||
{
|
||||
FilledTiles += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
FilledTiles -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
shapeChanged = oldFilledTiles != FilledTiles;
|
||||
DebugTools.Assert(FilledTiles >= 0);
|
||||
|
||||
Tiles[xIndex, yIndex] = tile;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -276,5 +266,5 @@ namespace Robust.Shared.Map
|
||||
/// <param name="newTile">New version of the tile.</param>
|
||||
/// <param name="oldTile">Old version of the tile.</param>
|
||||
/// <param name="chunkShapeChanged">If changing this tile changed the shape of the chunk.</param>
|
||||
internal delegate void TileModifiedDelegate(MapChunk mapChunk, Vector2i tileIndices, Tile newTile, Tile oldTile, bool chunkShapeChanged);
|
||||
internal delegate void TileModifiedDelegate(EntityUid uid, MapGridComponent grid, MapChunk mapChunk, Vector2i tileIndices, Tile newTile, Tile oldTile, bool chunkShapeChanged);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Components;
|
||||
|
||||
namespace Robust.Shared.Map;
|
||||
|
||||
@@ -29,24 +27,20 @@ internal partial class MapManager
|
||||
return;
|
||||
}
|
||||
|
||||
var physicsQuery = EntityManager.GetEntityQuery<PhysicsComponent>();
|
||||
var xformQuery = EntityManager.GetEntityQuery<TransformComponent>();
|
||||
var xformSystem = EntityManager.System<SharedTransformSystem>();
|
||||
var state = (worldAABB, gridTree.Tree, callback, approx, physicsQuery, xformQuery, xformSystem);
|
||||
var state = (worldAABB, gridTree.Tree, callback, approx, this, _transformSystem);
|
||||
|
||||
gridTree.Tree.Query(ref state,
|
||||
static (ref (Box2 worldAABB,
|
||||
B2DynamicTree<(EntityUid Uid, MapGridComponent Grid)> gridTree,
|
||||
GridCallback callback,
|
||||
bool approx,
|
||||
EntityQuery<PhysicsComponent> physicsQuery, EntityQuery<TransformComponent> xformQuery,
|
||||
MapManager mapManager,
|
||||
SharedTransformSystem xformSystem) tuple,
|
||||
DynamicTree.Proxy proxy) =>
|
||||
{
|
||||
var data = tuple.gridTree.GetUserData(proxy);
|
||||
|
||||
if (!tuple.approx && !IsIntersecting(tuple.worldAABB, data.Uid, data.Grid,
|
||||
tuple.physicsQuery, tuple.xformQuery, tuple.xformSystem))
|
||||
if (!tuple.approx && !tuple.mapManager.IsIntersecting(tuple.worldAABB, data.Uid, data.Grid))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -77,10 +71,7 @@ internal partial class MapManager
|
||||
callback(mapUid, grid, ref state);
|
||||
}
|
||||
|
||||
var physicsQuery = EntityManager.GetEntityQuery<PhysicsComponent>();
|
||||
var xformQuery = EntityManager.GetEntityQuery<TransformComponent>();
|
||||
var xformSystem = EntityManager.System<SharedTransformSystem>();
|
||||
var state2 = (state, worldAABB, gridTree.Tree, callback, approx, physicsQuery, xformQuery, xformSystem);
|
||||
var state2 = (state, worldAABB, gridTree.Tree, callback, approx, this, _transformSystem);
|
||||
|
||||
gridTree.Tree.Query(ref state2, static (ref (
|
||||
TState state,
|
||||
@@ -88,15 +79,13 @@ internal partial class MapManager
|
||||
B2DynamicTree<(EntityUid Uid, MapGridComponent Grid)> gridTree,
|
||||
GridCallback<TState> callback,
|
||||
bool approx,
|
||||
EntityQuery<PhysicsComponent> physicsQuery,
|
||||
EntityQuery<TransformComponent> xformQuery,
|
||||
MapManager mapManager,
|
||||
SharedTransformSystem xformSystem) tuple,
|
||||
DynamicTree.Proxy proxy) =>
|
||||
{
|
||||
var data = tuple.gridTree.GetUserData(proxy);
|
||||
|
||||
if (!tuple.approx && !IsIntersecting(tuple.worldAABB, data.Uid, data.Grid,
|
||||
tuple.physicsQuery, tuple.xformQuery, tuple.xformSystem))
|
||||
if (!tuple.approx && !tuple.mapManager.IsIntersecting(tuple.worldAABB, data.Uid, data.Grid))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -119,22 +108,18 @@ internal partial class MapManager
|
||||
FindGridsIntersecting(mapId, worldBounds.CalcBoundingBox(), ref state, callback, approx, includeMap);
|
||||
}
|
||||
|
||||
private static bool IsIntersecting(
|
||||
private bool IsIntersecting(
|
||||
Box2 aabb,
|
||||
EntityUid gridUid,
|
||||
MapGridComponent grid,
|
||||
EntityQuery<PhysicsComponent> physicsQuery,
|
||||
EntityQuery<TransformComponent> xformQuery,
|
||||
SharedTransformSystem xformSystem)
|
||||
MapGridComponent grid)
|
||||
{
|
||||
var xformComp = xformQuery.GetComponent(gridUid);
|
||||
var (worldPos, worldRot, matrix, invMatrix) = xformSystem.GetWorldPositionRotationMatrixWithInv(xformComp, xformQuery);
|
||||
var (worldPos, worldRot, matrix, invMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(gridUid);
|
||||
var overlap = matrix.TransformBox(grid.LocalAABB).Intersect(aabb);
|
||||
var localAABB = invMatrix.TransformBox(overlap);
|
||||
|
||||
if (physicsQuery.HasComponent(gridUid))
|
||||
if (_physicsQuery.HasComponent(gridUid))
|
||||
{
|
||||
var enumerator = grid.GetLocalMapChunks(localAABB);
|
||||
var enumerator = _mapSystem.GetLocalMapChunks(gridUid, grid, localAABB);
|
||||
|
||||
var transform = new Transform(worldPos, worldRot);
|
||||
|
||||
@@ -186,14 +171,13 @@ internal partial class MapManager
|
||||
|
||||
uid = EntityUid.Invalid;
|
||||
grid = null;
|
||||
var xformSystem = EntityManager.System<SharedTransformSystem>();
|
||||
var state = (uid, grid, worldPos, xformQuery, xformSystem);
|
||||
var state = (uid, grid, worldPos, _mapSystem, _transformSystem);
|
||||
|
||||
FindGridsIntersecting(mapId, aabb, ref state, static (EntityUid iUid, MapGridComponent iGrid, ref (
|
||||
EntityUid uid,
|
||||
MapGridComponent? grid,
|
||||
Vector2 worldPos,
|
||||
EntityQuery<TransformComponent> xformQuery,
|
||||
SharedMapSystem mapSystem,
|
||||
SharedTransformSystem xformSystem) tuple) =>
|
||||
{
|
||||
// Turn the worldPos into a localPos and work out the relevant chunk we need to check
|
||||
@@ -201,7 +185,7 @@ internal partial class MapManager
|
||||
// (though now we need some extra calcs up front).
|
||||
|
||||
// Doesn't use WorldBounds because it's just an AABB.
|
||||
var matrix = tuple.xformSystem.GetInvWorldMatrix(iUid, tuple.xformQuery);
|
||||
var matrix = tuple.xformSystem.GetInvWorldMatrix(iUid);
|
||||
var localPos = matrix.Transform(tuple.worldPos);
|
||||
|
||||
// NOTE:
|
||||
@@ -209,9 +193,10 @@ internal partial class MapManager
|
||||
// you account for the fact that fixtures are shrunk slightly!
|
||||
var chunkIndices = SharedMapSystem.GetChunkIndices(localPos, iGrid.ChunkSize);
|
||||
|
||||
if (!iGrid.HasChunk(chunkIndices)) return true;
|
||||
if (!tuple.mapSystem.HasChunk(iUid, iGrid, chunkIndices))
|
||||
return true;
|
||||
|
||||
var chunk = iGrid.GetOrAddChunk(chunkIndices);
|
||||
var chunk = tuple.mapSystem.GetOrAddChunk(iUid, iGrid, chunkIndices);
|
||||
var chunkRelative = SharedMapSystem.GetChunkRelative(localPos, iGrid.ChunkSize);
|
||||
var chunkTile = chunk.GetTile(chunkRelative);
|
||||
|
||||
@@ -241,8 +226,7 @@ internal partial class MapManager
|
||||
/// </summary>
|
||||
public bool TryFindGridAt(MapId mapId, Vector2 worldPos, out EntityUid uid, [NotNullWhen(true)] out MapGridComponent? grid)
|
||||
{
|
||||
var xformQuery = EntityManager.GetEntityQuery<TransformComponent>();
|
||||
return TryFindGridAt(mapId, worldPos, xformQuery, out uid, out grid);
|
||||
return TryFindGridAt(mapId, worldPos, _xformQuery, out uid, out grid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -4,6 +4,7 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -20,9 +21,17 @@ internal partial class MapManager : IMapManagerInternal, IEntityEventSubscriber
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
private SharedMapSystem _mapSystem = default!;
|
||||
private SharedTransformSystem _transformSystem = default!;
|
||||
private EntityQuery<PhysicsComponent> _physicsQuery;
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Initialize()
|
||||
{
|
||||
_physicsQuery = EntityManager.GetEntityQuery<PhysicsComponent>();
|
||||
_xformQuery = EntityManager.GetEntityQuery<TransformComponent>();
|
||||
|
||||
_sawmill = Logger.GetSawmill("map");
|
||||
|
||||
#if DEBUG
|
||||
@@ -36,6 +45,9 @@ internal partial class MapManager : IMapManagerInternal, IEntityEventSubscriber
|
||||
/// <inheritdoc />
|
||||
public void Startup()
|
||||
{
|
||||
_transformSystem = EntityManager.System<SharedTransformSystem>();
|
||||
_mapSystem = EntityManager.System<SharedMapSystem>();
|
||||
|
||||
#if DEBUG
|
||||
DebugTools.Assert(_dbgGuardInit);
|
||||
_dbgGuardRunning = true;
|
||||
@@ -52,9 +64,12 @@ internal partial class MapManager : IMapManagerInternal, IEntityEventSubscriber
|
||||
#endif
|
||||
_sawmill.Debug("Stopping...");
|
||||
|
||||
foreach (var mapComp in EntityManager.EntityQuery<MapComponent>())
|
||||
// TODO: AllEntityQuery instead???
|
||||
var query = EntityManager.EntityQueryEnumerator<MapComponent>();
|
||||
|
||||
while (query.MoveNext(out var uid, out _))
|
||||
{
|
||||
EntityManager.DeleteEntity(mapComp.Owner);
|
||||
EntityManager.DeleteEntity(uid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,9 +80,11 @@ internal partial class MapManager : IMapManagerInternal, IEntityEventSubscriber
|
||||
|
||||
// Don't just call Shutdown / Startup because we don't want to touch the subscriptions on gridtrees
|
||||
// Restart can be called any time during a game, whereas shutdown / startup are typically called upon connection.
|
||||
foreach (var mapComp in EntityManager.EntityQuery<MapComponent>())
|
||||
var query = EntityManager.EntityQueryEnumerator<MapComponent>();
|
||||
|
||||
while (query.MoveNext(out var uid, out _))
|
||||
{
|
||||
EntityManager.DeleteEntity(mapComp.Owner);
|
||||
EntityManager.DeleteEntity(uid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,15 +20,19 @@ namespace Robust.Shared.Physics.Systems
|
||||
{
|
||||
public abstract class SharedBroadphaseSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly IMapManagerInternal _mapManager = default!;
|
||||
[Dependency] private readonly IParallelManager _parallel = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physicsSystem = default!;
|
||||
[Dependency] private readonly SharedGridTraversalSystem _traversal = default!;
|
||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physicsSystem = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
private ISawmill _logger = default!;
|
||||
private EntityQuery<BroadphaseComponent> _broadphaseQuery;
|
||||
private EntityQuery<MapGridComponent> _gridQuery;
|
||||
private EntityQuery<PhysicsComponent> _physicsQuery;
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
|
||||
/*
|
||||
* Okay so Box2D has its own "MoveProxy" stuff so you can easily find new contacts when required.
|
||||
@@ -53,9 +57,12 @@ namespace Robust.Shared.Physics.Systems
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_logger = Logger.GetSawmill("physics");
|
||||
UpdatesOutsidePrediction = true;
|
||||
_broadphaseQuery = GetEntityQuery<BroadphaseComponent>();
|
||||
_gridQuery = GetEntityQuery<MapGridComponent>();
|
||||
_physicsQuery = GetEntityQuery<PhysicsComponent>();
|
||||
_xformQuery = GetEntityQuery<TransformComponent>();
|
||||
|
||||
UpdatesOutsidePrediction = true;
|
||||
UpdatesAfter.Add(typeof(SharedTransformSystem));
|
||||
|
||||
_cfg.OnValueChanged(CVars.BroadphaseExpand, SetBroadphaseExpand, true);
|
||||
@@ -79,27 +86,24 @@ namespace Robust.Shared.Physics.Systems
|
||||
PhysicsMapComponent component,
|
||||
MapId mapId,
|
||||
HashSet<EntityUid> movedGrids,
|
||||
Dictionary<FixtureProxy, Box2> gridMoveBuffer,
|
||||
EntityQuery<BroadphaseComponent> broadQuery,
|
||||
EntityQuery<TransformComponent> xformQuery)
|
||||
Dictionary<FixtureProxy, Box2> gridMoveBuffer)
|
||||
{
|
||||
// None moved this tick
|
||||
if (movedGrids.Count == 0) return;
|
||||
|
||||
var mapBroadphase = broadQuery.GetComponent(_mapManager.GetMapEntityId(mapId));
|
||||
var mapBroadphase = _broadphaseQuery.GetComponent(_mapManager.GetMapEntityId(mapId));
|
||||
|
||||
// This is so that if we're on a broadphase that's moving (e.g. a grid) we need to make sure anything
|
||||
// we move over is getting checked for collisions, and putting it on the movebuffer is the easiest way to do so.
|
||||
var moveBuffer = component.MoveBuffer;
|
||||
var gridQuery = GetEntityQuery<MapGridComponent>();
|
||||
|
||||
foreach (var gridUid in movedGrids)
|
||||
{
|
||||
var grid = gridQuery.GetComponent(gridUid);
|
||||
var xform = xformQuery.GetComponent(gridUid);
|
||||
var grid = _gridQuery.GetComponent(gridUid);
|
||||
var xform = _xformQuery.GetComponent(gridUid);
|
||||
|
||||
DebugTools.Assert(xform.MapID == mapId);
|
||||
var worldAABB = _transform.GetWorldMatrix(xform, xformQuery).TransformBox(grid.LocalAABB);
|
||||
var worldAABB = _transform.GetWorldMatrix(xform).TransformBox(grid.LocalAABB);
|
||||
var enlargedAABB = worldAABB.Enlarged(_broadphaseExpand);
|
||||
var state = (moveBuffer, gridMoveBuffer);
|
||||
|
||||
@@ -111,7 +115,7 @@ namespace Robust.Shared.Physics.Systems
|
||||
{
|
||||
moveBuffer[proxy] = worldAABB;
|
||||
// If something is in our AABB then try grid traversal for it
|
||||
_traversal.CheckTraverse(proxy.Entity, xformQuery.GetComponent(proxy.Entity));
|
||||
_traversal.CheckTraverse(proxy.Entity, _xformQuery.GetComponent(proxy.Entity));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,12 +161,8 @@ namespace Robust.Shared.Physics.Systems
|
||||
var movedGrids = Comp<MovedGridsComponent>(mapUid).MovedGrids;
|
||||
var gridMoveBuffer = new Dictionary<FixtureProxy, Box2>();
|
||||
|
||||
var broadphaseQuery = GetEntityQuery<BroadphaseComponent>();
|
||||
var physicsQuery = GetEntityQuery<PhysicsComponent>();
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
|
||||
// Find any entities being driven over that might need to be considered
|
||||
FindGridContacts(component, mapId, movedGrids, gridMoveBuffer, broadphaseQuery, xformQuery);
|
||||
FindGridContacts(component, mapId, movedGrids, gridMoveBuffer);
|
||||
|
||||
// There is some mariana trench levels of bullshit going on.
|
||||
// We essentially need to re-create Box2D's FindNewContacts but in a way that allows us to check every
|
||||
@@ -174,7 +174,7 @@ namespace Robust.Shared.Physics.Systems
|
||||
// to cache a bunch of stuff to make up for it.
|
||||
|
||||
// Handle grids first as they're not stored on map broadphase at all.
|
||||
HandleGridCollisions(mapId, movedGrids, physicsQuery, xformQuery);
|
||||
HandleGridCollisions(mapId, movedGrids);
|
||||
|
||||
// EZ
|
||||
if (moveBuffer.Count == 0)
|
||||
@@ -212,7 +212,7 @@ namespace Robust.Shared.Physics.Systems
|
||||
var proxyBody = proxy.Body;
|
||||
DebugTools.Assert(!proxyBody.Deleted);
|
||||
|
||||
var state = (this, proxy, worldAABB, buffer, xformQuery, broadphaseQuery);
|
||||
var state = (this, proxy, worldAABB, buffer);
|
||||
|
||||
// Get every broadphase we may be intersecting.
|
||||
_mapManager.FindGridsIntersecting(mapId, worldAABB.Enlarged(_broadphaseExpand), ref state,
|
||||
@@ -220,18 +220,16 @@ namespace Robust.Shared.Physics.Systems
|
||||
SharedBroadphaseSystem system,
|
||||
FixtureProxy proxy,
|
||||
Box2 worldAABB,
|
||||
List<FixtureProxy> pairBuffer,
|
||||
EntityQuery<TransformComponent> xformQuery,
|
||||
EntityQuery<BroadphaseComponent> broadphaseQuery) tuple) =>
|
||||
List<FixtureProxy> pairBuffer) tuple) =>
|
||||
{
|
||||
ref var buffer = ref tuple.pairBuffer;
|
||||
tuple.system.FindPairs(tuple.proxy, tuple.worldAABB, uid, buffer, tuple.xformQuery, tuple.broadphaseQuery);
|
||||
tuple.system.FindPairs(tuple.proxy, tuple.worldAABB, uid, buffer);
|
||||
return true;
|
||||
});
|
||||
|
||||
// Struct ref moment, I have no idea what's fastest.
|
||||
buffer = state.buffer;
|
||||
FindPairs(proxy, worldAABB, _mapManager.GetMapEntityId(mapId), buffer, xformQuery, broadphaseQuery);
|
||||
FindPairs(proxy, worldAABB, _mapManager.GetMapEntityId(mapId), buffer);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -272,27 +270,23 @@ namespace Robust.Shared.Physics.Systems
|
||||
|
||||
private void HandleGridCollisions(
|
||||
MapId mapId,
|
||||
HashSet<EntityUid> movedGrids,
|
||||
EntityQuery<PhysicsComponent> physicsQuery,
|
||||
EntityQuery<TransformComponent> xformQuery)
|
||||
HashSet<EntityUid> movedGrids)
|
||||
{
|
||||
var gridQuery = GetEntityQuery<MapGridComponent>();
|
||||
|
||||
foreach (var gridUid in movedGrids)
|
||||
{
|
||||
var grid = gridQuery.GetComponent(gridUid);
|
||||
var xform = xformQuery.GetComponent(gridUid);
|
||||
var grid = _gridQuery.GetComponent(gridUid);
|
||||
var xform = _xformQuery.GetComponent(gridUid);
|
||||
DebugTools.Assert(xform.MapID == mapId);
|
||||
|
||||
var (worldPos, worldRot, worldMatrix, invWorldMatrix) = _transform.GetWorldPositionRotationMatrixWithInv(xform, xformQuery);
|
||||
var (worldPos, worldRot, worldMatrix, invWorldMatrix) = _transform.GetWorldPositionRotationMatrixWithInv(xform);
|
||||
|
||||
var aabb = new Box2Rotated(grid.LocalAABB, worldRot).CalcBoundingBox().Translated(worldPos);
|
||||
|
||||
// TODO: Need to handle grids colliding with non-grid entities with the same layer
|
||||
// (nothing in SS14 does this yet).
|
||||
|
||||
var transform = _physicsSystem.GetPhysicsTransform(gridUid, xformQuery: xformQuery);
|
||||
var state = (gridUid, grid, transform, worldMatrix, invWorldMatrix, _physicsSystem, _transform, physicsQuery, xformQuery);
|
||||
var transform = _physicsSystem.GetPhysicsTransform(gridUid, xformQuery: _xformQuery);
|
||||
var state = (gridUid, grid, transform, worldMatrix, invWorldMatrix, _map, _physicsSystem, _transform, _physicsQuery, _xformQuery);
|
||||
|
||||
_mapManager.FindGridsIntersecting(mapId, aabb, ref state,
|
||||
static (EntityUid uid, MapGridComponent component,
|
||||
@@ -301,6 +295,7 @@ namespace Robust.Shared.Physics.Systems
|
||||
Transform transform,
|
||||
Matrix3 worldMatrix,
|
||||
Matrix3 invWorldMatrix,
|
||||
SharedMapSystem _map,
|
||||
SharedPhysicsSystem _physicsSystem,
|
||||
SharedTransformSystem xformSystem,
|
||||
EntityQuery<PhysicsComponent> physicsQuery,
|
||||
@@ -320,7 +315,7 @@ namespace Robust.Shared.Physics.Systems
|
||||
var aabb1 = tuple.grid.LocalAABB.Intersect(tuple.invWorldMatrix.TransformBox(otherGridBounds));
|
||||
|
||||
// TODO: AddPair has a nasty check in there that's O(n) but that's also a general physics problem.
|
||||
var ourChunks = tuple.grid.GetLocalMapChunks(aabb1);
|
||||
var ourChunks = tuple._map.GetLocalMapChunks(tuple.gridUid, tuple.grid, aabb1);
|
||||
var physicsA = tuple.physicsQuery.GetComponent(tuple.gridUid);
|
||||
var physicsB = tuple.physicsQuery.GetComponent(uid);
|
||||
|
||||
@@ -331,7 +326,7 @@ namespace Robust.Shared.Physics.Systems
|
||||
tuple.worldMatrix.TransformBox(
|
||||
ourChunk.CachedBounds.Translated(ourChunk.Indices * tuple.grid.ChunkSize));
|
||||
var ourChunkOtherRef = otherGridInvMatrix.TransformBox(ourChunkWorld);
|
||||
var collidingChunks = component.GetLocalMapChunks(ourChunkOtherRef);
|
||||
var collidingChunks = tuple._map.GetLocalMapChunks(uid, component, ourChunkOtherRef);
|
||||
|
||||
while (collidingChunks.MoveNext(out var collidingChunk))
|
||||
{
|
||||
@@ -372,14 +367,12 @@ namespace Robust.Shared.Physics.Systems
|
||||
FixtureProxy proxy,
|
||||
Box2 worldAABB,
|
||||
EntityUid broadphase,
|
||||
List<FixtureProxy> pairBuffer,
|
||||
EntityQuery<TransformComponent> xformQuery,
|
||||
EntityQuery<BroadphaseComponent> broadphaseQuery)
|
||||
List<FixtureProxy> pairBuffer)
|
||||
{
|
||||
DebugTools.Assert(proxy.Body.CanCollide);
|
||||
|
||||
// Broadphase can't intersect with entities on itself so skip.
|
||||
if (proxy.Entity == broadphase || !xformQuery.TryGetComponent(proxy.Entity, out var xform))
|
||||
if (proxy.Entity == broadphase || !_xformQuery.TryGetComponent(proxy.Entity, out var xform))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -389,7 +382,7 @@ namespace Robust.Shared.Physics.Systems
|
||||
DebugTools.AssertNotNull(xform.Broadphase);
|
||||
if (!_lookup.TryGetCurrentBroadphase(xform, out var proxyBroad))
|
||||
{
|
||||
_logger.Error($"Found null broadphase for {ToPrettyString(proxy.Entity)}");
|
||||
Log.Error($"Found null broadphase for {ToPrettyString(proxy.Entity)}");
|
||||
DebugTools.Assert(false);
|
||||
return;
|
||||
}
|
||||
@@ -401,10 +394,10 @@ namespace Robust.Shared.Physics.Systems
|
||||
}
|
||||
else
|
||||
{
|
||||
aabb = _transform.GetInvWorldMatrix(broadphase, xformQuery).TransformBox(worldAABB);
|
||||
aabb = _transform.GetInvWorldMatrix(broadphase).TransformBox(worldAABB);
|
||||
}
|
||||
|
||||
var broadphaseComp = broadphaseQuery.GetComponent(broadphase);
|
||||
var broadphaseComp = _broadphaseQuery.GetComponent(broadphase);
|
||||
var state = (pairBuffer, proxy);
|
||||
|
||||
QueryBroadphase(broadphaseComp.DynamicTree, state, aabb);
|
||||
@@ -512,7 +505,7 @@ namespace Robust.Shared.Physics.Systems
|
||||
}
|
||||
|
||||
// Won't worry about accurate bounds checks as it's probably slower in most use cases.
|
||||
var chunkEnumerator = mapGrid.GetMapChunks(aabb);
|
||||
var chunkEnumerator = _map.GetMapChunks(bUid, mapGrid, aabb);
|
||||
|
||||
if (chunkEnumerator.MoveNext(out _))
|
||||
{
|
||||
|
||||
@@ -27,6 +27,7 @@ using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Utility;
|
||||
using EyeComponent = Robust.Server.GameObjects.EyeComponent;
|
||||
using MapSystem = Robust.Server.GameObjects.MapSystem;
|
||||
|
||||
namespace Robust.UnitTesting
|
||||
{
|
||||
@@ -86,6 +87,7 @@ namespace Robust.UnitTesting
|
||||
|
||||
var systems = deps.Resolve<IEntitySystemManager>();
|
||||
// Required systems
|
||||
systems.LoadExtraSystemType<MapSystem>();
|
||||
systems.LoadExtraSystemType<EntityLookupSystem>();
|
||||
|
||||
// uhhh so maybe these are the wrong system for the client, but I CBF adding sprite system and all the rest,
|
||||
|
||||
@@ -37,7 +37,6 @@ namespace Robust.UnitTesting.Shared
|
||||
public void TestAnchoring()
|
||||
{
|
||||
var sim = RobustServerSimulation.NewSimulation();
|
||||
// sim.RegisterEntitySystems(m => m.LoadExtraSystemType<EntityLookupSystem>());
|
||||
var server = sim.InitializeInstance();
|
||||
|
||||
var lookup = server.Resolve<IEntitySystemManager>().GetEntitySystem<EntityLookupSystem>();
|
||||
@@ -61,7 +60,7 @@ namespace Robust.UnitTesting.Shared
|
||||
// When anchoring it should still get returned.
|
||||
xform.Anchored = true;
|
||||
Assert.That(xform.Anchored);
|
||||
Assert.That(lookup.GetEntitiesIntersecting(mapId, theMapSpotBeingUsed).ToList().Count, Is.EqualTo(1));
|
||||
Assert.That(lookup.GetEntitiesIntersecting(mapId, theMapSpotBeingUsed).ToList(), Has.Count.EqualTo(1));
|
||||
|
||||
xform.Anchored = false;
|
||||
Assert.That(lookup.GetEntitiesIntersecting(mapId, theMapSpotBeingUsed).ToList().Count, Is.EqualTo(1));
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace Robust.UnitTesting.Shared.Map
|
||||
var x = i % size;
|
||||
var y = i / size;
|
||||
|
||||
chunk.SetTile((ushort)x, (ushort)y, new Tile((ushort)tiles[i]));
|
||||
chunk.TrySetTile((ushort)x, (ushort)y, new Tile((ushort)tiles[i]), out _, out _);
|
||||
}
|
||||
|
||||
return chunk;
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace Robust.UnitTesting.Shared.Map
|
||||
|
||||
await server.WaitIdleAsync();
|
||||
|
||||
var entMan = server.ResolveDependency<IEntityManager>();
|
||||
var mapMan = server.ResolveDependency<IMapManager>();
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
@@ -35,18 +36,18 @@ namespace Robust.UnitTesting.Shared.Map
|
||||
var coordinates = new EntityCoordinates(gridEnt, new Vector2(10, 0));
|
||||
|
||||
// if no rotation and 0,0 position should just be the same coordinate.
|
||||
Assert.That(IoCManager.Resolve<IEntityManager>().GetComponent<TransformComponent>(gridEnt).WorldRotation, Is.EqualTo(Angle.Zero));
|
||||
Assert.That(entMan.GetComponent<TransformComponent>(gridEnt).WorldRotation, Is.EqualTo(Angle.Zero));
|
||||
Assert.That(grid.WorldToLocal(coordinates.Position), Is.EqualTo(coordinates.Position));
|
||||
|
||||
// Rotate 180 degrees should show -10, 0 for the position in map-terms and 10, 0 for the position in entity terms (i.e. no change).
|
||||
IoCManager.Resolve<IEntityManager>().GetComponent<TransformComponent>(gridEnt).WorldRotation += new Angle(MathF.PI);
|
||||
Assert.That(IoCManager.Resolve<IEntityManager>().GetComponent<TransformComponent>(gridEnt).WorldRotation, Is.EqualTo(new Angle(MathF.PI)));
|
||||
entMan.GetComponent<TransformComponent>(gridEnt).WorldRotation += new Angle(MathF.PI);
|
||||
Assert.That(entMan.GetComponent<TransformComponent>(gridEnt).WorldRotation, Is.EqualTo(new Angle(MathF.PI)));
|
||||
// Check the map coordinate rotates correctly
|
||||
Assert.That(grid.WorldToLocal(new Vector2(10, 0)).EqualsApprox(new Vector2(-10, 0), 0.01f));
|
||||
Assert.That(grid.LocalToWorld(coordinates.Position).EqualsApprox(new Vector2(-10, 0), 0.01f));
|
||||
|
||||
// Now we'll do the same for 180 degrees.
|
||||
IoCManager.Resolve<IEntityManager>().GetComponent<TransformComponent>(gridEnt).WorldRotation += MathF.PI / 2f;
|
||||
entMan.GetComponent<TransformComponent>(gridEnt).WorldRotation += MathF.PI / 2f;
|
||||
// If grid facing down then worldpos of 10, 0 gets rotated 90 degrees CCW and hence should be 0, 10
|
||||
Assert.That(grid.WorldToLocal(new Vector2(10, 0)).EqualsApprox(new Vector2(0, 10), 0.01f));
|
||||
// If grid facing down then local 10,0 pos should just return 0, -10 given it's aligned with the rotation.
|
||||
@@ -64,6 +65,7 @@ namespace Robust.UnitTesting.Shared.Map
|
||||
|
||||
var entMan = server.ResolveDependency<IEntityManager>();
|
||||
var mapMan = server.ResolveDependency<IMapManager>();
|
||||
var mapSystem = entMan.System<SharedMapSystem>();
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
@@ -86,26 +88,26 @@ namespace Robust.UnitTesting.Shared.Map
|
||||
|
||||
Assert.That(chunks.Count, Is.EqualTo(1));
|
||||
var chunk = chunks[0];
|
||||
var aabb = grid.CalcWorldAABB(chunk);
|
||||
var aabb = mapSystem.CalcWorldAABB(gridEnt, grid, chunk);
|
||||
var bounds = new Box2(new Vector2(0, 0), new Vector2(2, 10));
|
||||
|
||||
// With all cardinal directions these should align.
|
||||
Assert.That(aabb, Is.EqualTo(bounds));
|
||||
|
||||
IoCManager.Resolve<IEntityManager>().GetComponent<TransformComponent>(gridEnt).LocalRotation = new Angle(Math.PI);
|
||||
aabb = grid.CalcWorldAABB(chunk);
|
||||
entMan.GetComponent<TransformComponent>(gridEnt).LocalRotation = new Angle(Math.PI);
|
||||
aabb = mapSystem.CalcWorldAABB(gridEnt, grid, chunk);
|
||||
bounds = new Box2(new Vector2(-2, -10), new Vector2(0, 0));
|
||||
|
||||
Assert.That(aabb.EqualsApprox(bounds), $"Expected bounds of {aabb} and got {bounds}");
|
||||
|
||||
IoCManager.Resolve<IEntityManager>().GetComponent<TransformComponent>(gridEnt).LocalRotation = new Angle(-Math.PI / 2);
|
||||
aabb = grid.CalcWorldAABB(chunk);
|
||||
entMan.GetComponent<TransformComponent>(gridEnt).LocalRotation = new Angle(-Math.PI / 2);
|
||||
aabb = mapSystem.CalcWorldAABB(gridEnt, grid, chunk);
|
||||
bounds = new Box2(new Vector2(0, -2), new Vector2(10, 0));
|
||||
|
||||
Assert.That(aabb.EqualsApprox(bounds), $"Expected bounds of {aabb} and got {bounds}");
|
||||
|
||||
IoCManager.Resolve<IEntityManager>().GetComponent<TransformComponent>(gridEnt).LocalRotation = new Angle(-Math.PI / 4);
|
||||
aabb = grid.CalcWorldAABB(chunk);
|
||||
entMan.GetComponent<TransformComponent>(gridEnt).LocalRotation = new Angle(-Math.PI / 4);
|
||||
aabb = mapSystem.CalcWorldAABB(gridEnt, grid, chunk);
|
||||
bounds = new Box2(new Vector2(0, -1.4142135f), new Vector2(8.485281f, 7.071068f));
|
||||
|
||||
Assert.That(aabb.EqualsApprox(bounds), $"Expected bounds of {aabb} and got {bounds}");
|
||||
|
||||
Reference in New Issue
Block a user