diff --git a/src/Core/Worlds/World.cs b/src/Core/Worlds/World.cs index d88a12b..ba52cec 100644 --- a/src/Core/Worlds/World.cs +++ b/src/Core/Worlds/World.cs @@ -40,6 +40,37 @@ public abstract class World : ISerializable /// public event NotifyActorMoved? ActorMoved; + /// + /// The world bounds. If this value is null, the world is unbounded. If any of the values are 0 or lower, they will be set to a very small value. + /// + public Vector3? WorldBounds + { + get => _worldBounds; + set + { + if (value != null) + { + float minimum = 0.000000001f; + + _worldBounds = new Vector3( + Math.Max(value.Value.X, minimum), + Math.Max(value.Value.Y, minimum), + Math.Max(value.Value.Z, minimum) + ); + } + else + { + _worldBounds = null; + } + } + } + private Vector3? _worldBounds = null; + + /// + /// Whether the world wraps around. + /// + public bool WorldWrap { get; set; } = false; + /// /// Get the current step. /// @@ -254,17 +285,41 @@ public void MoveTo(Actor a, Vector3 v) throw new ArgumentException("Vector3 cannot contain NaN values"); } + Vector3 newPosition = v; + + if (WorldBounds != null) + { + float x = newPosition.X; + float y = newPosition.Y; + float z = newPosition.Z; + + if (WorldWrap) + { + x -= (float)Math.Floor(x / WorldBounds.Value.X) * WorldBounds.Value.X; + y -= (float)Math.Floor(y / WorldBounds.Value.Y) * WorldBounds.Value.Y; + z -= (float)Math.Floor(z / WorldBounds.Value.Z) * WorldBounds.Value.Z; + } + else + { + x = Math.Clamp(x, 0, WorldBounds.Value.X); + y = Math.Clamp(y, 0, WorldBounds.Value.Y); + z = Math.Clamp(z, 0, WorldBounds.Value.Z); + } + + newPosition = new Vector3(x, y, z); + } + Vector3 oldPosition = GetPosition(a); lock (_positionsLock) { if (positions.ContainsKey(a)) { - positions[a] = v; + positions[a] = newPosition; } } - ActorMoved?.Invoke(a, oldPosition, v); + ActorMoved?.Invoke(a, oldPosition, newPosition); } ///