Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement /area appearances #1868

Merged
merged 13 commits into from
Jul 8, 2024
2 changes: 2 additions & 0 deletions Content.Tests/DummyDreamMapManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public void SetTurf(DreamObjectTurf turf, DreamObjectDefinition type, DreamProcA

public void SetTurfAppearance(DreamObjectTurf turf, IconAppearance appearance) { }

public void SetAreaAppearance(DreamObjectArea area, IconAppearance appearance) { }

public void SetArea(DreamObjectTurf turf, DreamObjectArea area) { }

public bool TryGetCellFromTransform(TransformComponent transform, [NotNullWhen(true)] out IDreamMapManager.Cell? cell) {
Expand Down
6 changes: 5 additions & 1 deletion OpenDreamRuntime/AtomManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ public DreamValue GetAppearanceVar(IconAppearance appearance, string varName) {
return atom switch {
DreamObjectTurf turf => AppearanceSystem.MustGetAppearance(turf.AppearanceId),
DreamObjectMovable movable => movable.SpriteComponent.Appearance,
DreamObjectArea => new IconAppearance(),
DreamObjectArea area => AppearanceSystem.MustGetAppearance(area.AppearanceId),
DreamObjectImage image => image.Appearance,
_ => throw new Exception($"Cannot get appearance of {atom}")
};
Expand All @@ -464,6 +464,8 @@ public bool TryGetAppearance(DreamObject atom, [NotNullWhen(true)] out IconAppea
appearance = movable.SpriteComponent.Appearance;
else if (atom is DreamObjectImage image)
appearance = image.Appearance;
else if (atom is DreamObjectArea area)
appearance = AppearanceSystem.MustGetAppearance(area.AppearanceId);
else
appearance = null;

Expand All @@ -485,6 +487,8 @@ public void SetAtomAppearance(DreamObject atom, IconAppearance appearance) {
movable.SpriteComponent.SetAppearance(appearance);
} else if (atom is DreamObjectImage image) {
image.Appearance = appearance;
} else if (atom is DreamObjectArea area) {
_dreamMapManager.SetAreaAppearance(area, appearance);
}
}

Expand Down
44 changes: 42 additions & 2 deletions OpenDreamRuntime/DreamMapManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,11 +154,18 @@ public void InitializeAtoms(List<DreamMapJson>? maps) {
}
}

private DreamObject SetTurf(Vector2i pos, int z, DreamObjectDefinition type, DreamProcArguments creationArguments) {
private DreamObject SetTurf(Vector2i pos, int z, DreamObjectDefinition type, DreamProcArguments creationArguments, DreamObjectArea? area = null) {
if (IsInvalidCoordinate(pos, z))
throw new ArgumentException("Invalid coordinates");

Cell cell = _levels[z - 1].Cells[pos.X - 1, pos.Y - 1];

if(area is not null) {
cell.Area.Contents.RemoveValue(new(cell.Turf));
cell.Area = area;
cell.Area.Contents.AddValue(new(cell.Turf));
}

if (cell.Turf != null) {
cell.Turf.SetTurfType(type);
} else {
Expand All @@ -178,7 +185,24 @@ public void SetTurf(DreamObjectTurf turf, DreamObjectDefinition type, DreamProcA
SetTurf((turf.X, turf.Y), turf.Z, type, creationArguments);
}

/// <summary>
/// Caches the turf/area appearance pair instead of recreating and re-registering it for every turf in the game.
/// This is cleared out when an area appearance changes
/// </summary>
private readonly Dictionary<ValueTuple<IconAppearance, int>, IconAppearance> _turfAreaLookup = new();

public void SetTurfAppearance(DreamObjectTurf turf, IconAppearance appearance) {
if(turf.Cell.Area.AppearanceId != 0)
amylizzle marked this conversation as resolved.
Show resolved Hide resolved
if(!appearance.Overlays.Contains(turf.Cell.Area.AppearanceId)) {
if(!_turfAreaLookup.TryGetValue((appearance, turf.Cell.Area.AppearanceId), out var newAppearance)) {
newAppearance = new(appearance);
newAppearance.Overlays.Add(turf.Cell.Area.AppearanceId);
_turfAreaLookup.Add((appearance, turf.Cell.Area.AppearanceId), newAppearance);
}

appearance = newAppearance;
Fixed Show fixed Hide fixed
amylizzle marked this conversation as resolved.
Show resolved Hide resolved
}

int appearanceId = _appearanceSystem.AddAppearance(appearance);

var level = _levels[turf.Z - 1];
Expand All @@ -187,6 +211,21 @@ public void SetTurfAppearance(DreamObjectTurf turf, IconAppearance appearance) {
turf.AppearanceId = appearanceId;
}

public void SetAreaAppearance(DreamObjectArea area, IconAppearance appearance) {
//if an area changes appearance, invalidate the lookup
_turfAreaLookup.Clear();
int oldAppearance = area.AppearanceId;
area.AppearanceId = _appearanceSystem.AddAppearance(appearance);
foreach (var turf in area.Contents.GetTurfs()) {
var turfAppearance = _atomManager.MustGetAppearance(turf);

if(turfAppearance is null) continue;

turfAppearance.Overlays.Remove(oldAppearance);
SetTurfAppearance(turf, turfAppearance);
}
}
Fixed Show fixed Hide fixed

public bool TryGetCellAt(Vector2i pos, int z, [NotNullWhen(true)] out Cell? cell) {
if (IsInvalidCoordinate(pos, z) || !_levels.TryGetValue(z - 1, out var level)) {
cell = null;
Expand Down Expand Up @@ -323,7 +362,7 @@ private void LoadMapAreasAndTurfs(MapBlockJson block, Dictionary<string, CellDef
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());
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));
Expand Down Expand Up @@ -431,6 +470,7 @@ public Cell(DreamObjectArea area) {

public void SetTurf(DreamObjectTurf turf, DreamObjectDefinition type, DreamProcArguments creationArguments);
public void SetTurfAppearance(DreamObjectTurf turf, IconAppearance appearance);
public void SetAreaAppearance(DreamObjectArea area, IconAppearance appearance);
public bool TryGetCellAt(Vector2i pos, int z, [NotNullWhen(true)] out Cell? cell);
public bool TryGetTurfAt(Vector2i pos, int z, [NotNullWhen(true)] out DreamObjectTurf? turf);
public void SetZLevels(int levels);
Expand Down
4 changes: 4 additions & 0 deletions OpenDreamRuntime/Objects/Types/DreamList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1251,6 +1251,10 @@ public override int GetLength() {

return length;
}

public IEnumerable<DreamObjectTurf> GetTurfs() {
return _turfs;
}
}

// proc args list
Expand Down
2 changes: 2 additions & 0 deletions OpenDreamRuntime/Objects/Types/DreamObjectArea.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
public sealed class DreamObjectArea : DreamObjectAtom {
public int X, Y, Z;
public readonly AreaContentsList Contents;
public int AppearanceId;

public DreamObjectArea(DreamObjectDefinition objectDefinition) : base(objectDefinition) {
Contents = new(ObjectTree.List.ObjectDefinition, this);
AtomManager.SetAtomAppearance(this, AtomManager.GetAppearanceFromDefinition(ObjectDefinition));
}

protected override bool TryGetVar(string varName, out DreamValue value) {
Expand Down
4 changes: 4 additions & 0 deletions OpenDreamRuntime/Rendering/ServerAppearanceSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ public sealed class ServerAppearanceSystem : SharedAppearanceSystem {
[Dependency] private readonly IPlayerManager _playerManager = default!;

public override void Initialize() {
//register empty appearance as ID 0
_appearanceToId.Add(IconAppearance.Default, 0);
_idToAppearance.Add(0, IconAppearance.Default);
_appearanceIdCounter = 1;
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
}

Expand Down
15 changes: 15 additions & 0 deletions TestGame/code.dm
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,21 @@
/turf/blue
icon_state = "turf_blue"

/area/withicon
icon = 'icons/objects.dmi'
icon_state = "overlay"

New()
toggleBlink()

proc/toggleBlink()
if (icon == 'icons/objects.dmi')
icon = null
else
icon = 'icons/objects.dmi'
spawn(20)
toggleBlink()

/mob
icon = 'icons/mob.dmi'
icon_state = "mob"
Expand Down
6 changes: 4 additions & 2 deletions TestGame/map_z1.dmm
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
"K" = (/obj/complex_overlay_test,/turf,/area)
"L" = (/obj/float_layer_test,/turf,/area)
"O" = (/obj/plaque/animation_test,/turf,/area)
"R" = (/turf/blue,/area/withicon)
"X" = (/turf,/area/withicon)
"Z" = (/obj/button/animation_test,/turf,/area)

(1,1,1) = {"
Expand All @@ -58,8 +60,8 @@ baaaaaaaaaaaaaaaaaaaab
baaaaaaaaaaaaaaaaaaaab
baaaaaaaaaaaaaaaaaaaab
baaaaaaaaaaaaaaaaaaaab
baaaaaaaaaaaaaaaaaaaab
baaaaaaaaaaaaaaaaaaaab
bXaaaaaaaaaaaaaaaaaaab
Raaaaaaaaaaaaaaaaaaaab
bCJKLaaaaaaaaaaaaaaaab
bbbbbbbbbbbbbbbbbbbbbb
"}
Loading