Skip to content

Commit

Permalink
Remove a turf from an area's contents when it's added to another (#2099)
Browse files Browse the repository at this point in the history
  • Loading branch information
wixoaGit authored Nov 19, 2024
1 parent 6062c18 commit 3b37b1c
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 72 deletions.
63 changes: 27 additions & 36 deletions OpenDreamRuntime/DreamMapManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ public void InitializeAtoms(List<DreamMapJson>? 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");
}
}
}
Expand All @@ -154,30 +154,18 @@ public void InitializeAtoms(List<DreamMapJson>? 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) {
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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) {
Expand All @@ -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());
}
}
Expand All @@ -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();
Expand All @@ -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++) {
Expand Down Expand Up @@ -358,13 +345,9 @@ private void LoadMapAreasAndTurfs(MapBlockJson block, Dictionary<string, CellDef
DreamObjectArea area = GetOrCreateArea(cellDefinition.Area ?? _defaultArea);

Vector2i pos = (block.X + blockX - 1, block.Y + block.Height - blockY);
Level level = _levels[block.Z - 1];

var turf = SetTurf(pos, block.Z, CreateMapObjectDefinition(cellDefinition.Turf), new(), area);
// The following calls level.SetArea via an event on the area's `contents` var.
if (level.Cells[pos.X - 1, pos.Y - 1].Area != area) {
area.Contents.AddValue(new(turf));
}
_levels[block.Z - 1].Cells[pos.X - 1, pos.Y - 1].Area = area;
SetTurf(pos, block.Z, CreateMapObjectDefinition(cellDefinition.Turf), new());

blockX++;
if (blockX > block.Width) {
Expand Down Expand Up @@ -436,14 +419,18 @@ public sealed class Level {
public Cell[,] Cells;
public readonly Dictionary<Vector2i, Tile> QueuedTileUpdates = new();

public Level(int z, Entity<MapGridComponent> grid, DreamObjectArea area, Vector2i size) {
public Level(int z, Entity<MapGridComponent> 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;
}
}
}
Expand All @@ -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<DreamObjectMovable> Movables = new();

private DreamObjectArea _area;

public Cell(DreamObjectArea area) {
public Cell(DreamObjectArea area, DreamObjectTurf turf) {
Turf = turf;
_area = area;
_area.ResetCoordinateCache();
Area = area;
}
}

Expand Down
44 changes: 16 additions & 28 deletions OpenDreamRuntime/Objects/Types/DreamList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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}");
Expand Down Expand Up @@ -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<DreamValue> GetValues() {
List<DreamValue> values = new(_cell.Movables.Count);
List<DreamValue> values = new(Cell.Movables.Count);

foreach (var movable in _cell.Movables) {
foreach (var movable in Cell.Movables) {
values.Add(new(movable));
}

Expand All @@ -1171,32 +1167,30 @@ public override void AddValue(DreamValue value) {
if (!value.TryGetValueAsDreamObject<DreamObjectMovable>(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<DreamObjectTurf> _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;

Expand All @@ -1217,9 +1211,9 @@ public override DreamValue GetValue(DreamValue key) {
}

public override List<DreamValue> GetValues() {
List<DreamValue> values = new(_turfs.Count);
List<DreamValue> 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());
}
Expand All @@ -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<DreamObjectTurf>(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;
}

Expand All @@ -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<DreamObjectTurf> GetTurfs() {
return _turfs;
}
}

// proc args list
Expand Down
11 changes: 7 additions & 4 deletions OpenDreamRuntime/Objects/Types/DreamObjectArea.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,18 @@ public int Z {
}
}

public readonly AreaContentsList Contents;
public readonly HashSet<DreamObjectTurf> 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);
}

/// <summary>
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
8 changes: 4 additions & 4 deletions OpenDreamRuntime/Objects/Types/DreamObjectTurf.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down

0 comments on commit 3b37b1c

Please sign in to comment.