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

Initial vector implementation #2221

Merged
merged 4 commits into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions DMCompiler/DM/DMObjectTree.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ public DMObject GetOrCreateDMObject(DreamPath path) {
case "client":
case "datum":
case "list":
case "vector":
case "savefile":
case "world":
parent = GetOrCreateDMObject(DreamPath.Root);
Expand Down
26 changes: 26 additions & 0 deletions DMCompiler/DMStandard/Types/Vector.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/vector
var/len as num
var/size as num
var/x as num
var/y as num
var/z as num

proc/New(x, y, z)

proc/Cross(vector/B)
set opendream_unimplemented = TRUE

proc/Dot(vector/B)
set opendream_unimplemented = TRUE

proc/Interpolate(vector/B, t)
set opendream_unimplemented = TRUE

proc/Normalize()
set opendream_unimplemented = TRUE

proc/Turn(angle)
set opendream_unimplemented = TRUE

/proc/vector(x, y, z) as /vector
return new /vector(x, y, z)
1 change: 1 addition & 0 deletions DMCompiler/DMStandard/_Standard.dm
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ proc/winset(player, control_id, params)
#include "Types\Regex.dm"
#include "Types\Savefile.dm"
#include "Types\Sound.dm"
#include "Types\Vector.dm"
#include "Types\World.dm"
#include "Types\Atoms\_Atom.dm"
#include "Types\Atoms\Area.dm"
Expand Down
4 changes: 4 additions & 0 deletions OpenDreamRuntime/Objects/DreamObjectTree.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
public TreeEntry DatabaseQuery { get; private set; }
public TreeEntry Regex { get; private set; }
public TreeEntry Filter { get; private set; }
public TreeEntry Vector { get; private set; }
public TreeEntry Icon { get; private set; }
public TreeEntry Image { get; private set; }
public TreeEntry MutableAppearance { get; private set; }
Expand Down Expand Up @@ -177,6 +178,8 @@
throw new Exception("New turfs must be created by the map manager");
if (type.ObjectDefinition.IsSubtypeOf(Exception))
return new DreamObjectException(type.ObjectDefinition);
if (type.ObjectDefinition.IsSubtypeOf(Vector))
return new DreamObjectVector(type.ObjectDefinition);

return new DreamObject(type.ObjectDefinition);
}
Expand Down Expand Up @@ -294,6 +297,7 @@
DatabaseQuery = GetTreeEntry("/database/query");
Regex = GetTreeEntry("/regex");
Filter = GetTreeEntry("/dm_filter");
Vector = GetTreeEntry("/vector");
Icon = GetTreeEntry("/icon");
Image = GetTreeEntry("/image");
MutableAppearance = GetTreeEntry("/mutable_appearance");
Expand Down
147 changes: 147 additions & 0 deletions OpenDreamRuntime/Objects/Types/DreamObjectVector.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
using System.Linq;
using OpenDreamRuntime.Procs;

namespace OpenDreamRuntime.Objects.Types;

public sealed class DreamObjectVector(DreamObjectDefinition definition) : DreamObject(definition) {
public float X, Y;

public float Z {
get => Is3D ? _z : 0;
set {
if (!Is3D)
return;
_z = value;
}
}

public bool Is3D { get; private set; }

public float Size {
get => MathF.Sqrt(X * X + Y * Y + Z * Z);
set {
if (X == 0 && Y == 0 && Z == 0)
return;

var magnitude = Size;
X = X / magnitude * value;
Y = Y / magnitude * value;
Z = Z / magnitude * value;
}
}

private float _z;

public override void Initialize(DreamProcArguments args) {
base.Initialize(args);

var arg1 = args.GetArgument(0);
if (arg1.TryGetValueAsFloat(out var x) && args.Count is 2 or 3) { // X, Y, optionally Z
X = x;
Y = args.GetArgument(1).UnsafeGetValueAsFloat();
if (args.Count == 3) {
Is3D = true;
Z = args.GetArgument(2).UnsafeGetValueAsFloat();
}

return;
} else if (arg1.TryGetValueAsString(out var vectorStr)) { // Numbers with a comma or 'x' as a delimiter
var components = vectorStr.Split(',', 'x');

if (components.Length is 2 or 3) {
X = float.Parse(components[0]);
Y = float.Parse(components[1]);
if (components.Length == 3) {
Is3D = true;
Z = float.Parse(components[2]);
}

return;
}
} else if (arg1.TryGetValueAsDreamList(out var vectorList)) { // list(X, Y) or list(X, Y, Z)
var components = vectorList.GetValues();

if (components.Count is 2 or 3 && components.All(v => v.Type == DreamValue.DreamValueType.Float)) {
X = components[0].UnsafeGetValueAsFloat();
Y = components[1].UnsafeGetValueAsFloat();
if (components.Count == 3) {
Is3D = true;
Z = components[2].UnsafeGetValueAsFloat();
}

return;
}
} else if (arg1.TryGetValueAsDreamObject<DreamObjectVector>(out var vectorCopy)) { // new /vector(vector)
Is3D = vectorCopy.Is3D;
X = vectorCopy.X;
Y = vectorCopy.Y;
Z = vectorCopy.Z;

return;
}

// TODO: Allow pixloc as an arg
throw new Exception($"Bad vector arguments {args.ToString()}");
}

protected override bool TryGetVar(string varName, out DreamValue value) {
switch (varName) {
case "type":
value = new(ObjectDefinition.TreeEntry);
return true;
case "len":
value = new(Is3D ? 3 : 2);
return true;
case "size":
value = new(Size);
return true;
case "x":
value = new(X);
return true;
case "y":
value = new(Y);
return true;
case "z":
value = new(Z);
return true;
default:
// Hide the base vars
throw new Exception($"Invalid vector variable \"{varName}\"");
}
}

protected override void SetVar(string varName, DreamValue value) {
switch (varName) {
case "type":
throw new Exception("Cannot set type var");
case "len":
var newLen = value.UnsafeGetValueAsFloat();

// Something like 2.3 actually isn't valid here; it doesn't cast to an int
if (!newLen.Equals(2f) && !newLen.Equals(3f)) {
throw new Exception($"Invalid vector len {value}");
}

Is3D = newLen.Equals(3f);
break;
case "size":
Size = value.UnsafeGetValueAsFloat();
break;
case "x":
X = value.UnsafeGetValueAsFloat();
break;
case "y":
Y = value.UnsafeGetValueAsFloat();
break;
case "z":
Z = value.UnsafeGetValueAsFloat();
break;
default:
// Hide the base vars
throw new Exception($"Invalid vector variable \"{varName}\"");
}
}

// TODO: Operators, supports indexing and "most math"
// TODO: For loop support
}
15 changes: 14 additions & 1 deletion OpenDreamRuntime/Procs/DreamProcArguments.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Runtime.CompilerServices;
using System.Text;

namespace OpenDreamRuntime.Procs;

Expand Down Expand Up @@ -29,6 +30,18 @@ public DreamValue GetArgument(int argumentPosition) {
}

public override string ToString() {
return $"<Arguments {Count}>";
var strBuilder = new StringBuilder((Count * 2 - 1) + 4);

strBuilder.Append("<Arguments ");
strBuilder.Append(Count);
strBuilder.Append(">(");
for (int i = 0; i < Count; i++) {
strBuilder.Append(Values[i]);
if (i != Count - 1)
strBuilder.Append(", ");
}

strBuilder.Append(')');
return strBuilder.ToString();
}
}
37 changes: 25 additions & 12 deletions OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1448,20 +1448,33 @@ private static void JsonEncode(Utf8JsonWriter writer, DreamValue value) {
writer.WriteEndArray();
}
} else if (value.TryGetValueAsDreamObject(out var dreamObject)) {
if (dreamObject == null)
writer.WriteNullValue();
else if (dreamObject is DreamObjectMatrix matrix) { // Special behaviour for /matrix values
writer.WriteStartArray();
switch (dreamObject) {
case null:
writer.WriteNullValue();
break;
case DreamObjectMatrix matrix: {
writer.WriteStartArray();

foreach (var f in DreamObjectMatrix.EnumerateMatrix(matrix)) {
writer.WriteNumberValue(f);
}
foreach (var f in DreamObjectMatrix.EnumerateMatrix(matrix)) {
writer.WriteNumberValue(f);
}

writer.WriteEndArray();
// This doesn't have any corresponding snowflaking in CreateValueFromJsonElement()
// because BYOND actually just forgets that this was a matrix after doing json encoding.
} else
writer.WriteStringValue(value.Stringify());
writer.WriteEndArray();
// This doesn't have any corresponding snowflaking in CreateValueFromJsonElement()
// because BYOND actually just forgets that this was a matrix after doing json encoding.
break;
}
case DreamObjectVector vector: { // Special behaviour for /vector values
if (vector.Is3D)
writer.WriteStringValue($"vector({vector.X},{vector.Y},{vector.Z})");
else
writer.WriteStringValue($"vector({vector.X},{vector.Y})");
break;
}
default:
writer.WriteStringValue(value.Stringify());
break;
}
} else if (value.TryGetValueAsDreamResource(out var dreamResource)) {
writer.WriteStringValue(dreamResource.ResourcePath);
} else {
Expand Down
Loading