diff --git a/DMCompiler/DMStandard/_Standard.dm b/DMCompiler/DMStandard/_Standard.dm index 410b1cd950..5eabf6be5a 100644 --- a/DMCompiler/DMStandard/_Standard.dm +++ b/DMCompiler/DMStandard/_Standard.dm @@ -112,7 +112,6 @@ proc/viewers(Depth, Center = usr) as /list proc/walk(Ref, Dir, Lag = 0, Speed = 0) proc/walk_rand(Ref,Lag = 0,Speed = 0) proc/walk_to(Ref, Trg, Min = 0, Lag = 0, Speed = 0) - set opendream_unimplemented = 1 proc/walk_towards(Ref,Trg,Lag=0,Speed=0) proc/winclone(player, window_name, clone_name) proc/winexists(player, control_id) as text diff --git a/OpenDreamRuntime/Procs/Native/DreamProcNative.cs b/OpenDreamRuntime/Procs/Native/DreamProcNative.cs index acaaac09e2..45d9737617 100644 --- a/OpenDreamRuntime/Procs/Native/DreamProcNative.cs +++ b/OpenDreamRuntime/Procs/Native/DreamProcNative.cs @@ -113,6 +113,7 @@ public static void SetupNativeProcs(DreamObjectTree objectTree) { objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_walk); objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_walk_rand); objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_walk_towards); + objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_walk_to); objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_winclone); objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_winexists); objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_winget); diff --git a/OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs b/OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs index e8c0511734..e20346626c 100644 --- a/OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs +++ b/OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs @@ -3213,6 +3213,29 @@ public static DreamValue NativeProc_walk_towards(NativeProc.Bundle bundle, Dream return DreamValue.Null; } + [DreamProc("walk_to")] + [DreamProcParameter("Ref", Type = DreamValueTypeFlag.DreamObject)] + [DreamProcParameter("Trg", Type = DreamValueTypeFlag.DreamObject)] + [DreamProcParameter("Min", Type = DreamValueTypeFlag.Float, DefaultValue = 0)] + [DreamProcParameter("Lag", Type = DreamValueTypeFlag.Float, DefaultValue = 0)] + [DreamProcParameter("Speed", Type = DreamValueTypeFlag.Float, DefaultValue = 0)] + public static DreamValue NativeProc_walk_to(NativeProc.Bundle bundle, DreamObject? src, DreamObject? usr) { + if (!bundle.GetArgument(0, "Ref").TryGetValueAsDreamObject(out var refAtom)) + return DreamValue.Null; + + if (!bundle.GetArgument(1, "Trg").TryGetValueAsDreamObject(out var trgAtom)) { + bundle.WalkManager.StopWalks(refAtom); + return DreamValue.Null; + } + + bundle.GetArgument(2, "Min").TryGetValueAsInteger(out var min); + bundle.GetArgument(3, "Lag").TryGetValueAsInteger(out var lag); + bundle.GetArgument(4, "Speed").TryGetValueAsInteger(out var speed); + + bundle.WalkManager.StartWalkTo(refAtom, trgAtom, min, lag, speed); + return DreamValue.Null; + } + [DreamProc("winclone")] [DreamProcParameter("player", Type = DreamValueTypeFlag.DreamObject)] [DreamProcParameter("window_name", Type = DreamValueTypeFlag.String)] diff --git a/OpenDreamRuntime/WalkManager.cs b/OpenDreamRuntime/WalkManager.cs index 560342d20a..ca372e6adf 100644 --- a/OpenDreamRuntime/WalkManager.cs +++ b/OpenDreamRuntime/WalkManager.cs @@ -1,3 +1,4 @@ +using System.Linq; using System.Threading; using OpenDreamRuntime.Map; using OpenDreamRuntime.Objects.Types; @@ -111,4 +112,39 @@ public void StartWalkTowards(DreamObjectMovable movable, DreamObjectAtom target, return DreamValue.Null; }); } + + /// + /// Walk towards the target with pathfinding taken into account + /// + public void StartWalkTo(DreamObjectMovable movable, DreamObjectAtom target, int min, int lag, int speed) { // TODO: Implement speed. Speed=0 uses Ref.step_size + StopWalks(movable); + + lag = Math.Max(lag, 1); // Minimum of 1 tick lag + + CancellationTokenSource cancelSource = new(); + _walkTasks[movable] = cancelSource; + + DreamThread.Run($"walk_to {movable}", async state => { + var moveProc = movable.GetProc("Move"); + + while (true) { + await _scheduler.CreateDelayTicks(lag); + if (cancelSource.IsCancellationRequested) + break; + + var currentLoc = _atomManager.GetAtomPosition(movable); + var targetLoc = _atomManager.GetAtomPosition(target); + var steps = _dreamMapManager.CalculateSteps(currentLoc, targetLoc, min); + using var enumerator = steps.GetEnumerator(); + if (!enumerator.MoveNext()) // No more steps to take + break; + + var dir = enumerator.Current; + var newLoc = DreamProcNativeHelpers.GetStep(_atomManager, _dreamMapManager, movable, dir); + await state.Call(moveProc, movable, null, new(newLoc), new((int)dir)); + } + + return DreamValue.Null; + }); + } }