diff --git a/Content.Tests/DMProject/Tests/Stdlib/generator_rand.dm b/Content.Tests/DMProject/Tests/Stdlib/generator_rand.dm
new file mode 100644
index 0000000000..3b56e5177b
--- /dev/null
+++ b/Content.Tests/DMProject/Tests/Stdlib/generator_rand.dm
@@ -0,0 +1,5 @@
+/proc/RunTest()
+ var/generator/gen = generator("num", -1, 1)
+ var/result = gen.Rand()
+ ASSERT(isnum(result))
+ ASSERT((result >= -1 && result <= 1))
\ No newline at end of file
diff --git a/DMCompiler/DMStandard/Types/Generator.dm b/DMCompiler/DMStandard/Types/Generator.dm
index 12c2bcedb8..6b476b75f9 100644
--- a/DMCompiler/DMStandard/Types/Generator.dm
+++ b/DMCompiler/DMStandard/Types/Generator.dm
@@ -5,7 +5,6 @@
var/_binobj as opendream_unimplemented
/generator/proc/Rand()
- set opendream_unimplemented = TRUE
/*
Generator Theory
diff --git a/OpenDreamRuntime/Objects/Types/DreamObjectVector.cs b/OpenDreamRuntime/Objects/Types/DreamObjectVector.cs
index 0506b6645a..22f18e3f4f 100644
--- a/OpenDreamRuntime/Objects/Types/DreamObjectVector.cs
+++ b/OpenDreamRuntime/Objects/Types/DreamObjectVector.cs
@@ -338,6 +338,24 @@ public static DreamObjectVector CreateFromValue(DreamValue value, DreamObjectTre
return vector;
}
+ ///
+ /// Creates a from a
+ ///
+ public static DreamObjectVector CreateFromValue(Vector3 value, DreamObjectTree tree) {
+ var vector = tree.CreateObject(tree.Vector);
+ vector.Initialize(new(new(value.X), new(value.Y), new(value.Z)));
+ return vector;
+ }
+
+ ///
+ /// Creates a from a
+ ///
+ public static DreamObjectVector CreateFromValue(Vector2 value, DreamObjectTree tree) {
+ var vector = tree.CreateObject(tree.Vector);
+ vector.Initialize(new(new(value.X), new(value.Y)));
+ return vector;
+ }
+
// TODO: Operators, supports indexing and "most math"
// TODO: For loop support
}
diff --git a/OpenDreamRuntime/Procs/Native/DreamProcNative.cs b/OpenDreamRuntime/Procs/Native/DreamProcNative.cs
index b202da6bab..e5e22c96ba 100644
--- a/OpenDreamRuntime/Procs/Native/DreamProcNative.cs
+++ b/OpenDreamRuntime/Procs/Native/DreamProcNative.cs
@@ -195,6 +195,8 @@ public static void SetupNativeProcs(DreamObjectTree objectTree) {
objectTree.SetNativeProc(objectTree.DatabaseQuery, DreamProcNativeDatabaseQuery.NativeProc_NextRow);
objectTree.SetNativeProc(objectTree.DatabaseQuery, DreamProcNativeDatabaseQuery.NativeProc_RowsAffected);
+ objectTree.SetNativeProc(objectTree.Generator, DreamProcNativeGenerator.NativeProc_Rand);
+
SetOverridableNativeProc(objectTree, objectTree.World, DreamProcNativeWorld.NativeProc_Error);
SetOverridableNativeProc(objectTree, objectTree.World, DreamProcNativeWorld.NativeProc_Reboot);
}
diff --git a/OpenDreamRuntime/Procs/Native/DreamProcNativeGenerator.cs b/OpenDreamRuntime/Procs/Native/DreamProcNativeGenerator.cs
new file mode 100644
index 0000000000..8dc321ebaa
--- /dev/null
+++ b/OpenDreamRuntime/Procs/Native/DreamProcNativeGenerator.cs
@@ -0,0 +1,30 @@
+using OpenDreamRuntime.Objects;
+using OpenDreamRuntime.Objects.Types;
+using OpenDreamShared.Dream;
+using Robust.Shared.Random;
+
+namespace OpenDreamRuntime.Procs.Native;
+
+internal static class DreamProcNativeGenerator {
+ [DreamProc("Rand")]
+ public static DreamValue NativeProc_Rand(NativeProc.Bundle bundle, DreamObject? src, DreamObject? usr) {
+ var genObj = (DreamObjectGenerator)src!;
+
+ switch (genObj.Generator) {
+ case IGeneratorNum numGen: {
+ var result = numGen.Generate(IoCManager.Resolve());
+ return new DreamValue(result);
+ }
+ case IGeneratorVector vecGen: {
+ var rand = IoCManager.Resolve();
+ var resultObj = vecGen.PrefersVector3
+ ? DreamObjectVector.CreateFromValue(vecGen.GenerateVector3(rand), bundle.ObjectTree)
+ : DreamObjectVector.CreateFromValue(vecGen.GenerateVector2(rand), bundle.ObjectTree);
+
+ return new DreamValue(resultObj);
+ }
+ default:
+ throw new Exception($"Invalid generator for Rand: {genObj}");
+ }
+ }
+}
diff --git a/OpenDreamShared/Dream/Generator.cs b/OpenDreamShared/Dream/Generator.cs
index f994235916..62e2e002b7 100644
--- a/OpenDreamShared/Dream/Generator.cs
+++ b/OpenDreamShared/Dream/Generator.cs
@@ -31,12 +31,14 @@ public interface IGeneratorNum : IGenerator {
}
public interface IGeneratorVector : IGenerator {
+ bool PrefersVector3 { get; set; }
public Vector2 GenerateVector2(IRobustRandom random);
public Vector3 GenerateVector3(IRobustRandom random);
}
[Serializable, NetSerializable]
public sealed class GeneratorNum(float low, float high, GeneratorDistribution distribution) : IGeneratorNum, IGeneratorVector {
+ public bool PrefersVector3 { get; set; } = false;
public GeneratorNum(float value) : this(value, value, GeneratorDistribution.Constant) { }
public float Generate(IRobustRandom random) {
@@ -58,6 +60,7 @@ public override string ToString() {
[Serializable, NetSerializable]
public sealed class GeneratorVector2(Vector2 low, Vector2 high, GeneratorDistribution distribution) : IGeneratorVector {
+ public bool PrefersVector3 { get; set; } = false;
public GeneratorVector2(Vector2 value) : this(value, value, GeneratorDistribution.Constant) { }
public Vector2 GenerateVector2(IRobustRandom random) {
@@ -77,6 +80,7 @@ public override string ToString() {
[Serializable, NetSerializable]
public sealed class GeneratorVector3(Vector3 low, Vector3 high, GeneratorDistribution distribution) : IGeneratorVector {
+ public bool PrefersVector3 { get; set; } = true;
public GeneratorVector3(Vector3 value) : this(value, value, GeneratorDistribution.Constant) { }
public Vector2 GenerateVector2(IRobustRandom random) {
@@ -96,6 +100,8 @@ public override string ToString() {
[Serializable, NetSerializable]
public sealed class GeneratorBox2(Vector2 low, Vector2 high, GeneratorDistribution distribution) : IGeneratorVector {
+ public bool PrefersVector3 { get; set; } = false;
+
public Vector2 GenerateVector2(IRobustRandom random) {
var x = IGenerator.GenerateNum(random, low.X, high.X, distribution);
var y = IGenerator.GenerateNum(random, low.Y, high.Y, distribution);
@@ -116,6 +122,8 @@ public override string ToString() {
[Serializable, NetSerializable]
public sealed class GeneratorBox3(Vector3 low, Vector3 high, GeneratorDistribution distribution) : IGeneratorVector {
+ public bool PrefersVector3 { get; set; } = true;
+
public Vector2 GenerateVector2(IRobustRandom random) {
var vector = GenerateVector3(random);
@@ -137,6 +145,8 @@ public override string ToString() {
[Serializable, NetSerializable]
public sealed class GeneratorCircle(float low, float high, GeneratorDistribution distribution) : IGeneratorVector {
+ public bool PrefersVector3 { get; set; } = false;
+
public Vector2 GenerateVector2(IRobustRandom random) {
var theta = random.NextFloat(0f, 360f);
var r = IGenerator.GenerateNum(random, low, high, distribution);
@@ -157,6 +167,8 @@ public override string ToString() {
[Serializable, NetSerializable]
public sealed class GeneratorSphere(float low, float high, GeneratorDistribution distribution) : IGeneratorVector {
+ public bool PrefersVector3 { get; set; } = true;
+
public Vector2 GenerateVector2(IRobustRandom random) {
var vector = GenerateVector3(random);
@@ -182,6 +194,8 @@ public override string ToString() {
[Serializable, NetSerializable]
public sealed class GeneratorSquare(Vector2 low, Vector2 high, GeneratorDistribution distribution) : IGeneratorVector {
+ public bool PrefersVector3 { get; set; } = false;
+
public Vector2 GenerateVector2(IRobustRandom random) {
var x = IGenerator.GenerateNum(random, -high.X, high.X, distribution);
var y = IGenerator.GenerateNum(random, -high.Y, high.Y, distribution);
@@ -207,6 +221,8 @@ public override string ToString() {
[Serializable, NetSerializable]
public sealed class GeneratorCube(Vector3 low, Vector3 high, GeneratorDistribution distribution) : IGeneratorVector {
+ public bool PrefersVector3 { get; set; } = true;
+
public Vector2 GenerateVector2(IRobustRandom random) {
var vector = GenerateVector3(random);