using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using Robust.Shared.GameStates; using Robust.Shared.IoC; using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Maths; using Robust.Shared.Network; using Robust.Shared.Physics; using Robust.Shared.Physics.Collision; using Robust.Shared.Physics.Systems; using Robust.Shared.Timing; using Robust.Shared.Utility; namespace Robust.Shared.GameObjects { /// /// Manages all the grids and maps in the ECS, providing methods to create and modify them. /// public abstract partial class SharedMapSystem : EntitySystem { [Dependency] private ITileDefinitionManager _tileMan = default!; [Dependency] private IGameTiming _timing = default!; [Dependency] private IManifoldManager _manifolds = default!; [Dependency] private INetManager _netManager = default!; [Dependency] private FixtureSystem _fixtures = default!; [Dependency] private SharedPhysicsSystem _physics = default!; [Dependency] private SharedTransformSystem _transform = default!; [Dependency] private MetaDataSystem _meta = default!; private EntityQuery _fixturesQuery; private EntityQuery _mapQuery; private EntityQuery _gridQuery; private EntityQuery _metaQuery; private EntityQuery _xformQuery; [Dependency] EntityQuery _gridTreeQuery; internal Dictionary Maps { get; } = new(); /// /// If set, this prevents the from being raised when modifying grids. /// /// /// Useful if you want to create a new grid, delete an existing grid, or bulk-modify tiles and don't want to spam ten billion individual tile-changed events. /// internal bool SuppressOnTileChanged { get; set; } /// /// This hashset is used to try prevent MapId re-use. This is mainly for auto-assigned map ids. /// Loading a map with a specific id (e.g., the various mapping commands) may still result in an id being /// reused. /// protected HashSet UsedIds = new(); public override void Initialize() { base.Initialize(); _fixturesQuery = GetEntityQuery(); _mapQuery = GetEntityQuery(); _gridQuery = GetEntityQuery(); _metaQuery = GetEntityQuery(); _xformQuery = GetEntityQuery(); InitializeMap(); InitializeGrid(); SubscribeLocalEvent(OnMapLightGetState); SubscribeLocalEvent(OnMapLightHandleState); } /// /// Converts the specified index to a bitmask with the specified chunksize. /// [Pure] public static ulong ToBitmask(Vector2i index, byte chunkSize = 8) { DebugTools.Assert(chunkSize <= 8); DebugTools.Assert((index.X + index.Y * chunkSize) < 64); return (ulong) 1 << (index.X + index.Y * chunkSize); } /// True if the specified bitflag is set for this index. [Pure] public static bool FromBitmask(Vector2i index, ulong bitmask, byte chunkSize = 8) { var flag = ToBitmask(index, chunkSize); return (flag & bitmask) == flag; } } /// /// Arguments for when a map is created or deleted. /// [Obsolete("Use map creation or deletion events")] public sealed class MapChangedEvent : EntityEventArgs { public EntityUid Uid; /// /// Creates a new instance of this class. /// public MapChangedEvent(EntityUid uid, MapId map, bool created) { Uid = uid; Map = map; Created = created; } /// /// Map that is being modified. /// public MapId Map { get; } /// /// The map is being created. /// public bool Created { get; } /// /// The map is being destroyed (not ). /// public bool Destroyed => !Created; } /// /// Event raised whenever a map is created. /// public readonly record struct MapCreatedEvent(EntityUid Uid, MapId MapId); /// /// Event raised whenever a map is removed. /// public readonly record struct MapRemovedEvent(EntityUid Uid, MapId MapId); #pragma warning disable CS0618 public sealed class GridStartupEvent : EntityEventArgs { public EntityUid EntityUid { get; } public GridStartupEvent(EntityUid uid) { EntityUid = uid; } } public sealed class GridRemovalEvent : EntityEventArgs { public EntityUid EntityUid { get; } public GridRemovalEvent(EntityUid uid) { EntityUid = uid; } } /// /// Raised whenever a grid is being initialized. /// public sealed class GridInitializeEvent : EntityEventArgs { public EntityUid EntityUid { get; } public MapGridComponent Grid { get; } public GridInitializeEvent(EntityUid uid, MapGridComponent grid) { EntityUid = uid; Grid = grid; } } #pragma warning restore CS0618 /// /// Raised whenever a grid is Added /// public sealed class GridAddEvent : EntityEventArgs { public EntityUid EntityUid { get; } public GridAddEvent(EntityUid uid) { EntityUid = uid; } } /// /// Raised directed at the grid when tiles are changed locally or remotely. /// [ByRefEvent] public readonly record struct TileChangedEvent { /// public TileChangedEvent(Entity entity, TileRef newTile, Tile oldTile, Vector2i chunkIndex) : this(entity, newTile.Tile, oldTile, chunkIndex, newTile.GridIndices) { } /// /// Creates a new instance of this event for a single changed tile. /// /// The grid entity containing the changed tile(s) /// New tile that replaced the old one. /// Old tile that was replaced. /// The index of the grid-chunk that this tile belongs to. /// The positional indices of this tile on the grid. public TileChangedEvent(Entity entity, Tile newTile, Tile oldTile, Vector2i chunkIndex, Vector2i gridIndices) { Entity = entity; Changes = [new TileChangedEntry(newTile, oldTile, chunkIndex, gridIndices)]; } /// /// Creates a new instance of this event for multiple changed tiles. /// public TileChangedEvent(Entity entity, TileChangedEntry[] changes) { Entity = entity; Changes = changes; } /// /// Entity of the grid with the tile-change. TileRef stores the GridId. /// public readonly Entity Entity; /// /// An array of all the tiles that were changed. /// public readonly TileChangedEntry[] Changes; } /// /// Data about a single tile that was changed as part of a . /// /// New tile that replaced the old one. /// Old tile that was replaced. /// The index of the grid-chunk that this tile belongs to. /// The positional indices of this tile on the grid. public readonly record struct TileChangedEntry(Tile NewTile, Tile OldTile, Vector2i ChunkIndex, Vector2i GridIndices) { /// /// Was the tile previously empty or is it now empty. /// public bool EmptyChanged => OldTile.IsEmpty != NewTile.IsEmpty; } }