diff --git a/OpenDreamRuntime/Objects/Types/DreamList.cs b/OpenDreamRuntime/Objects/Types/DreamList.cs index 31d66392a5..7344a9e0c7 100644 --- a/OpenDreamRuntime/Objects/Types/DreamList.cs +++ b/OpenDreamRuntime/Objects/Types/DreamList.cs @@ -1263,6 +1263,91 @@ public IEnumerable GetTurfs() { } } +// mob.contents, obj.contents list +public sealed class MovableContentsList(DreamObjectDefinition listDef, DreamObjectMovable movable) : DreamList(listDef, 0) { + private readonly DreamObjectMovable _movable = movable; + public override DreamValue GetValue(DreamValue key) { + if (!key.TryGetValueAsInteger(out var index)) + throw new Exception($"Invalid index into movable contents list: {key}"); + + + if (index < 1 || index > _movable.ChildCount) + throw new Exception($"Out of bounds index on movable contents list: {index}"); + + + using (var childEnumerator = _movable.ChildEnumerator) { + while (index >= 1) { + var current = childEnumerator.MoveNext(out EntityUid child); + + if (index == 1) { + if (AtomManager.TryGetMovableFromEntity(child, out var childObject)) + return new DreamValue(childObject); + else + throw new Exception($"Invalid child in movable contents list: {child}"); + } + + index--; + } + } + + throw new Exception($"Out of bounds index on movable contents list after iterating: {key}"); + } + + public override List GetValues() { + List values = []; + + using (var childEnumerator = _movable.ChildEnumerator) { + while (childEnumerator.MoveNext(out EntityUid child)) { + if (!AtomManager.TryGetMovableFromEntity(child, out var childObject)) + continue; + + values.Add(new DreamValue(childObject)); + } + } + + return values; + } + + public override void SetValue(DreamValue key, DreamValue value, bool allowGrowth = false) { + throw new Exception("Cannot set an index of movable contents list"); + } + + public override void AddValue(DreamValue value) { + if (!value.TryGetValueAsDreamObject(out var movable)) + throw new Exception($"Cannot add {value} to movable contents"); + + movable.SetVariable("loc", new (_movable)); + } + + public override void RemoveValue(DreamValue value) { + if (!value.TryGetValueAsDreamObject(out var movable)) + throw new Exception($"Cannot remove {value} from movable contents"); + + movable.SetVariable("loc", DreamValue.Null); + } + + public override bool ContainsValue(DreamValue value) { + if (!value.TryGetValueAsDreamObject(out var movable)) + return false; + + if (!movable.TryGetVariable("loc", out var _locVariable)) + return false; + + if (!_locVariable.TryGetValueAsDreamObject(out var _loc)) + return false; + + return _loc == _movable; + } + + public override void Cut(int start = 1, int end = 0) { + // TODO + } + + public override int GetLength() { + return _movable.ChildCount; + } +} + // proc args list sealed class ProcArgsList : DreamList { private readonly DMProcState _state; diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectMovable.cs b/OpenDreamRuntime/Objects/Types/DreamObjectMovable.cs index 9b38720942..dceae7995a 100644 --- a/OpenDreamRuntime/Objects/Types/DreamObjectMovable.cs +++ b/OpenDreamRuntime/Objects/Types/DreamObjectMovable.cs @@ -1,4 +1,4 @@ -using OpenDreamRuntime.Procs; +using OpenDreamRuntime.Procs; using OpenDreamRuntime.Rendering; using OpenDreamShared.Dream; using Robust.Shared.Map; @@ -19,6 +19,10 @@ public class DreamObjectMovable : DreamObjectAtom { private readonly TransformComponent _transformComponent; + public readonly MovableContentsList Contents; + + public TransformChildrenEnumerator ChildEnumerator => _transformComponent.ChildEnumerator; + public int ChildCount => _transformComponent.ChildCount; private string? ScreenLoc { get => _screenLoc; @@ -39,6 +43,8 @@ public DreamObjectMovable(DreamObjectDefinition objectDefinition) : base(objectD Entity = AtomManager.CreateMovableEntity(this); SpriteComponent = EntityManager.GetComponent(Entity); _transformComponent = EntityManager.GetComponent(Entity); + + Contents = new MovableContentsList(ObjectTree.List.ObjectDefinition, this); } public override void Initialize(DreamProcArguments args) { @@ -87,18 +93,7 @@ protected override bool TryGetVar(string varName, out DreamValue value) { value = (ScreenLoc != null) ? new(ScreenLoc) : DreamValue.Null; return true; case "contents": - DreamList contents = ObjectTree.CreateList(); - - using (var childEnumerator = _transformComponent.ChildEnumerator) { - while (childEnumerator.MoveNext(out EntityUid child)) { - if (!AtomManager.TryGetMovableFromEntity(child, out var childAtom)) - continue; - - contents.AddValue(new DreamValue(childAtom)); - } - } - - value = new(contents); + value = new(Contents); return true; case "locs": // Unimplemented; just return a list containing src.loc diff --git a/OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs b/OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs index 405491c8f5..c468c7414f 100644 --- a/OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs +++ b/OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs @@ -1,4 +1,4 @@ -using System.Buffers; +using System.Buffers; using OpenDreamRuntime.Objects; using OpenDreamRuntime.Resources; using OpenDreamShared.Dream; @@ -3023,6 +3023,14 @@ public static DreamValue NativeProc_view(NativeProc.Bundle bundle, DreamObject? if (center is null) return new(view); + if (center.TryGetVariable("contents", out var centerContents) && centerContents.TryGetValueAsDreamList(out var centerContentsList)) { + foreach (var content in centerContentsList.GetValues()) { + view.AddValue(content); + } + } + + // Center gets included during the walk through the tiles + var eyePos = bundle.AtomManager.GetAtomPosition(center); var viewData = DreamProcNativeHelpers.CollectViewData(bundle.AtomManager, bundle.MapManager, eyePos, range); diff --git a/TestGame/code.dm b/TestGame/code.dm index 42c94dda80..67cd018e65 100644 --- a/TestGame/code.dm +++ b/TestGame/code.dm @@ -12,18 +12,26 @@ /obj/plane_master appearance_flags = PLANE_MASTER - + /obj/plane_master/turf screen_loc = "1,1" plane = TURF_PLANE New() - src.filters = filter(type="displace", size=100, icon=icon('icons/displace.dmi',"lense")) + src.filters = filter(type="displace", size=100, icon=icon('icons/displace.dmi',"lense")) /mob/verb/examine(atom/thing as obj|mob in world) set category = null usr << "This is [thing]. [thing.desc]" +/mob/verb/poke(mob/someone as obj|mob in world) + set category = null + usr << "You poke [someone]!" + for(var/x in view(someone, 0)) + usr << "They see: [x]" + for(var/obj/item/item in someone) + usr << "They have: [item]" + /turf icon = 'icons/turf.dmi' icon_state = "turf" @@ -45,9 +53,32 @@ icon = null else icon = 'icons/objects.dmi' - spawn(20) + spawn(20) toggleBlink() +/obj/item/bandoleer + icon = 'icons/objects.dmi' + icon_state = "bandoleer" + layer = OBJ_LAYER + + name = "Bandoleer" + desc = "Stylish and comes with a gun!" + + New() + ..() + contents += new /obj/item/gun + +/obj/item/gun + icon = 'icons/objects.dmi' + icon_state = "gun" + layer = OBJ_LAYER + + name = "Testing Gun" + desc = "Non-functional, but it takes up space" + + New() + ..() + /mob icon = 'icons/mob.dmi' icon_state = "mob" @@ -61,6 +92,11 @@ New() ..() + contents += new /obj/item/bandoleer + var/obj/item/gun = new /obj/item/gun + gun.name = "Testing Gun Outer" // Keep it distinct + contents += gun + loc = locate(5, 5, 1) Login() @@ -118,12 +154,12 @@ set name = "Walk North" usr << "Walking north. Use the 'Walk Stop' verb to cease." walk(src, NORTH) - + verb/start_walk_rand() set name = "Walk Randomly" usr << "Walking randomly. Use the 'Walk Stop' verb to cease." walk_rand(src) - + verb/stop_walk() set name = "Walk Stop" usr << "Walking stopped." @@ -210,7 +246,7 @@ if("drop_shadow") src.filters = filter(type="drop_shadow", size=2) if("displace") - src.client.screen += new /obj/plane_master/turf + src.client.screen += new /obj/plane_master/turf usr << "Applied [selected] filter" verb/toggle_see_invisibility() @@ -225,7 +261,7 @@ var/image/i = image(icon = 'icons/hanoi.dmi', icon_state="8") i.loc = src i.override = 1 - + src.client.images += i usr << "override added" for(var/turf/T in range(src, 2)) @@ -243,10 +279,10 @@ spawn(20) src << "showing main window" winset(src,"mainwindow","is-visible=true") - + verb/winget_text_verb(var/rawtext as command_text) set name = "wingettextverb" - world << "recieved: [rawtext]" + world << "recieved: [rawtext]" verb/test_hot_reload_interface() set category = "Test"