diff --git a/OpenDreamRuntime/DreamMapManager.cs b/OpenDreamRuntime/DreamMapManager.cs index b7585478b4..89864ab959 100644 --- a/OpenDreamRuntime/DreamMapManager.cs +++ b/OpenDreamRuntime/DreamMapManager.cs @@ -139,7 +139,7 @@ public void InitializeAtoms(List? maps) { for (var z = 1; z <= Levels; ++z) { for (var y = Size.Y; y >= 1; --y) { for (var x = Size.X; x >= 1; --x) { - _levels[z - 1].Cells[x - 1, y - 1].Turf?.SpawnProc("New"); + _levels[z - 1].Cells[x - 1, y - 1].Turf.SpawnProc("New"); } } } @@ -154,30 +154,18 @@ public void InitializeAtoms(List? maps) { } } - private DreamObject SetTurf(Vector2i pos, int z, DreamObjectDefinition type, DreamProcArguments creationArguments, DreamObjectArea? area = null) { + private void SetTurf(Vector2i pos, int z, DreamObjectDefinition type, DreamProcArguments creationArguments) { if (IsInvalidCoordinate(pos, z)) throw new ArgumentException("Invalid coordinates"); - Cell cell = _levels[z - 1].Cells[pos.X - 1, pos.Y - 1]; + var cell = _levels[z - 1].Cells[pos.X - 1, pos.Y - 1]; - if(area is not null) { - cell.Area.Contents.RemoveValue(new(cell.Turf)); - area.Contents.AddValue(new(cell.Turf)); - } - - if (cell.Turf != null) { - cell.Turf.SetTurfType(type); - } else { - cell.Turf = new DreamObjectTurf(type, pos.X, pos.Y, z, cell); - // Only add the /turf to .contents when it's created. - cell.Area.Contents.AddValue(new(cell.Turf)); - } + cell.Turf.SetTurfType(type); IconAppearance turfAppearance = _atomManager.GetAppearanceFromDefinition(cell.Turf.ObjectDefinition); SetTurfAppearance(cell.Turf, turfAppearance); cell.Turf.InitSpawn(creationArguments); - return cell.Turf; } public void SetTurf(DreamObjectTurf turf, DreamObjectDefinition type, DreamProcArguments creationArguments) { @@ -215,7 +203,7 @@ public void SetAreaAppearance(DreamObjectArea area, IconAppearance appearance) { _turfAreaLookup.Clear(); int oldAppearance = area.AppearanceId; area.AppearanceId = _appearanceSystem.AddAppearance(appearance); - foreach (var turf in area.Contents.GetTurfs()) { + foreach (var turf in area.Turfs) { var turfAppearance = _atomManager.MustGetAppearance(turf); if(turfAppearance is null) continue; @@ -238,7 +226,7 @@ public bool TryGetCellAt(Vector2i pos, int z, [NotNullWhen(true)] out Cell? cell public bool TryGetTurfAt(Vector2i pos, int z, [NotNullWhen(true)] out DreamObjectTurf? turf) { if (TryGetCellAt(pos, z, out var cell)) { turf = cell.Turf; - return (turf != null); + return true; } turf = null; @@ -264,8 +252,6 @@ public void SetWorldSize(Vector2i size) { var newX = Math.Max(oldSize.X, size.X); var newY = Math.Max(oldSize.Y, size.Y); - DreamObjectArea defaultArea = GetOrCreateArea(_defaultArea); - Size = (newX, newY); if(Size.X > oldSize.X || Size.Y > oldSize.Y) { @@ -280,7 +266,10 @@ public void SetWorldSize(Vector2i size) { continue; } - existingLevel.Cells[x - 1, y - 1] = new Cell(defaultArea); + var defaultTurf = new DreamObjectTurf(_defaultTurf.ObjectDefinition, x, y, existingLevel.Z); + var cell = new Cell(DefaultArea, defaultTurf); + defaultTurf.Cell = cell; + existingLevel.Cells[x - 1, y - 1] = cell; SetTurf(new Vector2i(x, y), existingLevel.Z, _defaultTurf.ObjectDefinition, new()); } } @@ -298,7 +287,7 @@ public void SetWorldSize(Vector2i size) { for (var y = 1; y <= oldSize.Y; y++) { if (x > size.X || y > size.Y) { var deleteCell = oldCells[x - 1, y - 1]; - deleteCell.Turf?.Delete(); + deleteCell.Turf.Delete(); _mapSystem.SetTile(existingLevel.Grid, new Vector2i(x, y), Tile.Empty); foreach (var movableToDelete in deleteCell.Movables) { movableToDelete.Delete(); @@ -314,14 +303,12 @@ public void SetWorldSize(Vector2i size) { public void SetZLevels(int levels) { if (levels > Levels) { - DreamObjectArea defaultArea = GetOrCreateArea(_defaultArea); - for (int z = Levels + 1; z <= levels; z++) { MapId mapId = new(z); _mapSystem.CreateMap(mapId); var grid = _mapManager.CreateGridEntity(mapId); - Level level = new Level(z, grid, defaultArea, Size); + Level level = new Level(z, grid, _defaultTurf.ObjectDefinition, DefaultArea, Size); _levels.Add(level); for (int x = 1; x <= Size.X; x++) { @@ -358,13 +345,9 @@ private void LoadMapAreasAndTurfs(MapBlockJson block, Dictionary block.Width) { @@ -436,14 +419,18 @@ public sealed class Level { public Cell[,] Cells; public readonly Dictionary QueuedTileUpdates = new(); - public Level(int z, Entity grid, DreamObjectArea area, Vector2i size) { + public Level(int z, Entity grid, DreamObjectDefinition turfType, DreamObjectArea area, Vector2i size) { Z = z; Grid = grid; Cells = new Cell[size.X, size.Y]; for (int x = 0; x < size.X; x++) { for (int y = 0; y < size.Y; y++) { - Cells[x, y] = new Cell(area); + var turf = new DreamObjectTurf(turfType, x + 1, y + 1, z); + var cell = new Cell(area, turf); + + turf.Cell = cell; + Cells[x, y] = cell; } } } @@ -453,20 +440,24 @@ public sealed class Cell { public DreamObjectArea Area { get => _area; set { + _area.Turfs.Remove(Turf); _area.ResetCoordinateCache(); + _area = value; + _area.Turfs.Add(Turf); _area.ResetCoordinateCache(); } } - public DreamObjectTurf? Turf; + public readonly DreamObjectTurf Turf; public readonly List Movables = new(); private DreamObjectArea _area; - public Cell(DreamObjectArea area) { + public Cell(DreamObjectArea area, DreamObjectTurf turf) { + Turf = turf; _area = area; - _area.ResetCoordinateCache(); + Area = area; } } diff --git a/OpenDreamRuntime/Objects/Types/DreamList.cs b/OpenDreamRuntime/Objects/Types/DreamList.cs index ae7522364b..d3c887222c 100644 --- a/OpenDreamRuntime/Objects/Types/DreamList.cs +++ b/OpenDreamRuntime/Objects/Types/DreamList.cs @@ -416,7 +416,7 @@ public override DreamValue GetValue(DreamValue key) { if (DreamObject.TryGetVariable(varName, out var objectVar)) { return objectVar; } - + throw new Exception($"Cannot get value of undefined var \"{key}\" on type {DreamObject.ObjectDefinition.Type}"); } else { throw new Exception($"Invalid var index {key}"); @@ -1136,27 +1136,23 @@ public override int GetLength() { } // turf.contents list -public sealed class TurfContentsList : DreamList { - private readonly IDreamMapManager.Cell _cell; - - public TurfContentsList(DreamObjectDefinition listDef, IDreamMapManager.Cell cell) : base(listDef, 0) { - _cell = cell; - } +public sealed class TurfContentsList(DreamObjectDefinition listDef, DreamObjectTurf turf) : DreamList(listDef, 0) { + private IDreamMapManager.Cell Cell => turf.Cell; public override DreamValue GetValue(DreamValue key) { if (!key.TryGetValueAsInteger(out var index)) throw new Exception($"Invalid index into turf contents list: {key}"); - if (index < 1 || index > _cell.Movables.Count) + if (index < 1 || index > Cell.Movables.Count) throw new Exception($"Out of bounds index on turf contents list: {index}"); - return new DreamValue(_cell.Movables[index - 1]); + return new DreamValue(Cell.Movables[index - 1]); } // TODO: This would preferably be an IEnumerable<> method. Probably as part of #985. public override List GetValues() { - List values = new(_cell.Movables.Count); + List values = new(Cell.Movables.Count); - foreach (var movable in _cell.Movables) { + foreach (var movable in Cell.Movables) { values.Add(new(movable)); } @@ -1171,32 +1167,30 @@ public override void AddValue(DreamValue value) { if (!value.TryGetValueAsDreamObject(out var movable)) throw new Exception($"Cannot add {value} to turf contents"); - movable.SetVariable("loc", new(_cell.Turf)); + movable.SetVariable("loc", new(Cell.Turf)); } public override void Cut(int start = 1, int end = 0) { - int movableCount = _cell.Movables.Count + 1; + int movableCount = Cell.Movables.Count + 1; if (end == 0 || end > movableCount) end = movableCount; for (int i = start; i < end; i++) { - _cell.Movables[i - 1].SetVariable("loc", DreamValue.Null); + Cell.Movables[i - 1].SetVariable("loc", DreamValue.Null); } } public override int GetLength() { - return _cell.Movables.Count; + return Cell.Movables.Count; } } // area.contents list public sealed class AreaContentsList(DreamObjectDefinition listDef, DreamObjectArea area) : DreamList(listDef, 0) { - private readonly List _turfs = new(); - public override DreamValue GetValue(DreamValue key) { if (!key.TryGetValueAsInteger(out var index)) throw new Exception($"Invalid index into area contents list: {key}"); - foreach (var turf in _turfs) { + foreach (var turf in area.Turfs) { if (index < 1) break; @@ -1217,9 +1211,9 @@ public override DreamValue GetValue(DreamValue key) { } public override List GetValues() { - List values = new(_turfs.Count); + List values = new(area.Turfs.Count); - foreach (var turf in _turfs) { + foreach (var turf in area.Turfs) { values.Add(new(turf)); values.AddRange(turf.Contents.GetValues()); } @@ -1236,14 +1230,12 @@ public override void AddValue(DreamValue value) { throw new Exception($"Cannot add {value} to area contents"); turf.Cell.Area = area; - _turfs.Add(turf); } public override void RemoveValue(DreamValue value) { if (!value.TryGetValueAsDreamObject(out var turf)) throw new Exception($"Cannot remove {value} from area contents"); - _turfs.Remove(turf); // Remove first, in case the new area (default) is still this area turf.Cell.Area = DreamMapManager.DefaultArea; } @@ -1252,17 +1244,13 @@ public override void Cut(int start = 1, int end = 0) { } public override int GetLength() { - int length = _turfs.Count; + int length = area.Turfs.Count; - foreach (var turf in _turfs) + foreach (var turf in area.Turfs) length += turf.Contents.GetLength(); return length; } - - public IEnumerable GetTurfs() { - return _turfs; - } } // proc args list diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectArea.cs b/OpenDreamRuntime/Objects/Types/DreamObjectArea.cs index f36b227d37..e94f2c82d7 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectArea.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectArea.cs @@ -22,15 +22,18 @@ public int Z { } } - public readonly AreaContentsList Contents; + public readonly HashSet Turfs; public int AppearanceId; + private readonly AreaContentsList _contents; + // Iterating all our turfs to find the one with the lowest coordinates is slow business private int? _cachedX, _cachedY, _cachedZ; public DreamObjectArea(DreamObjectDefinition objectDefinition) : base(objectDefinition) { - Contents = new(ObjectTree.List.ObjectDefinition, this); + Turfs = new(); AtomManager.SetAtomAppearance(this, AtomManager.GetAppearanceFromDefinition(ObjectDefinition)); + _contents = new(ObjectTree.List.ObjectDefinition, this); } /// @@ -52,7 +55,7 @@ protected override bool TryGetVar(string varName, out DreamValue value) { value = new(Z); return true; case "contents": - value = new(Contents); + value = new(_contents); return true; default: return base.TryGetVar(varName, out value); @@ -106,7 +109,7 @@ private void UpdateCoordinateCache() { if (_cachedX != null) return; - foreach (var turf in Contents.GetTurfs()) { + foreach (var turf in Turfs) { if (_cachedX != null) { if (turf.Z > _cachedZ) continue; diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs b/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs index 3ceb6e995a..7248f524f6 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs @@ -2,16 +2,16 @@ public sealed class DreamObjectTurf : DreamObjectAtom { public readonly int X, Y, Z; - public readonly IDreamMapManager.Cell Cell; public readonly TurfContentsList Contents; + public IDreamMapManager.Cell Cell; public int AppearanceId; - public DreamObjectTurf(DreamObjectDefinition objectDefinition, int x, int y, int z, IDreamMapManager.Cell cell) : base(objectDefinition) { + public DreamObjectTurf(DreamObjectDefinition objectDefinition, int x, int y, int z) : base(objectDefinition) { X = x; Y = y; Z = z; - Cell = cell; - Contents = new TurfContentsList(ObjectTree.List.ObjectDefinition, Cell); + Cell = default!; // NEEDS to be set by DreamMapManager after creation + Contents = new TurfContentsList(ObjectTree.List.ObjectDefinition, this); } public void SetTurfType(DreamObjectDefinition objectDefinition) {