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;
}
}