Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
98 changes: 98 additions & 0 deletions Content.Tests/DMProject/Tests/Generator/GeneratorOperators.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Generators can be chained together with math operators, producing a new generator.
// Constant generators (low == high) are used here so the results are deterministic.

/proc/assert_num(generator/gen, expected)
var/result = gen.Rand()
ASSERT(isnum(result))
ASSERT(result == expected)

// A generator built from an operator always produces a 3D vector, even from 2D operands
/proc/assert_vec(generator/gen, x, y, z)
var/vector/result = gen.Rand()
ASSERT(istype(result, /vector))
ASSERT(result.len == 3)
ASSERT(result.x == x)
ASSERT(result.y == y)
ASSERT(result.z == z)

/proc/RunTest()
var/generator/five = generator("num", 5, 5)
var/generator/two = generator("num", 2, 2)
var/generator/vec = generator("vector", vector(3, 4), vector(3, 4))
var/generator/vec3d = generator("vector", vector(3, 4, 5), vector(3, 4, 5))

// Operators return a generator, not a value
ASSERT(istype(five + two, /generator))

// Number generators with another generator
assert_num(five + two, 7)
assert_num(five - two, 3)
assert_num(five * two, 10)

// Number generators with a plain number
assert_num(five + 3, 8)
assert_num(five - 3, 2)
assert_num(five * 3, 15)
assert_num(five / 2, 2.5)
assert_num(five ** 2, 25)
assert_num(-five, -5)

// null counts as 0
assert_num(five + null, 5)
assert_num(five - null, 5)
assert_num(five * null, 0)

// Operators can be chained
assert_num((five + 1) * 2, 12)

// A number generator reduces a vector operand down to its last component
assert_num(five + vector(1, 2), 7)
assert_num(five + vector(1, 2, 3), 8)

// An uncombined generator keeps producing 2D vectors
var/vector/plain = vec.Rand()
ASSERT(plain.len == 2)

// A number is applied to every component of a vector
assert_vec(vec + 2, 5, 6, 2)
assert_vec(vec - 2, 1, 2, -2)
assert_vec(vec * 2, 6, 8, 0)
assert_vec(vec / 2, 1.5, 2, 0)
assert_vec(vec ** 2, 9, 16, 0)
assert_vec(-vec, -3, -4, 0)
assert_vec(vec + two, 5, 6, 2)
assert_vec(vec * two, 6, 8, 0)
assert_vec(vec3d + 2, 5, 6, 7)

// Vector generators combine component-wise
assert_vec(vec + vector(1, 2), 4, 6, 0)
assert_vec(vec - vector(1, 2), 2, 2, 0)
assert_vec(vec * vector(2, 3), 6, 12, 0)
assert_vec(vec3d * vector(2, 2, 2), 6, 8, 10)

// A list of 2 or 3 numbers works in place of a vector
assert_vec(vec + list(1, 2), 4, 6, 0)
assert_vec(vec3d * list(2, 2, 2), 6, 8, 10)

// Multiplying by a matrix transforms the vector
assert_vec(vec * matrix(2, 0, 10, 0, 3, 20), 16, 32, 0)

// A color matrix does a 3D transform, with red,green,blue mapping to x,y,z
assert_vec(vec3d * list(2,0,0,0, 0,3,0,0, 0,0,4,0, 0,0,0,1, 10,20,30,0), 16, 32, 50)

// The assignment forms rebind the var to the combined generator
var/generator/combined = generator("num", 5, 5)
combined += 2
assert_num(combined, 7)
combined -= 1
assert_num(combined, 6)
combined *= 2
assert_num(combined, 12)
combined /= 4
assert_num(combined, 3)

// Operands are re-rolled on every Rand() call
var/generator/random = generator("num", 0, 1) + generator("num", 10, 20)
for(var/i in 1 to 20)
var/result = random.Rand()
ASSERT(result >= 10 && result <= 21)
15 changes: 15 additions & 0 deletions Content.Tests/DMProject/Tests/Generator/GeneratorParticles.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Generators built from operators must still be usable for particle effects
/proc/RunTest()
var/particles/P = new
P.lifespan = generator("num", 10, 20) + 5
P.fade = generator("num", 1, 2) * 2
P.position = generator("vector", vector(1, 1), vector(2, 2)) * 2
P.velocity = generator("circle", 1, 2) + vector(1, 1)
P.scale = -generator("vector", vector(1, 1), vector(2, 2))
P.drift = generator("sphere", 1, 2) * matrix(2, 0, 0, 0, 2, 0)

var/generator/spun = generator("vector", vector(1, 0), vector(1, 0))
spun.Turn(90)
P.gravity = spun

ASSERT(P.lifespan != null)
34 changes: 34 additions & 0 deletions Content.Tests/DMProject/Tests/Generator/GeneratorTurn.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// /generator/proc/Turn() rotates the vectors a generator produces around the XY plane.
// Unlike most Turn() procs it modifies the generator in place and returns it.

/proc/assert_turn(generator/gen, angle, x, y, z)
ASSERT(gen.Turn(angle) == gen) // Modified in place

var/vector/result = gen.Rand()
ASSERT(result.len == 3)
ASSERT(abs(result.x - x) < 0.0001)
ASSERT(abs(result.y - y) < 0.0001)
ASSERT(abs(result.z - z) < 0.0001)

/proc/unit_vector_generator()
return generator("vector", vector(1, 0), vector(1, 0))

/proc/RunTest()
assert_turn(unit_vector_generator(), 90, 0, 1, 0)
assert_turn(unit_vector_generator(), -90, 0, -1, 0)
assert_turn(unit_vector_generator(), 180, -1, 0, 0)
assert_turn(unit_vector_generator(), 45, 0.707107, 0.707107, 0)

// A full rotation comes back around
assert_turn(unit_vector_generator(), 360, 1, 0, 0)

// The Z component is left alone
assert_turn(generator("vector", vector(1, 0, 7), vector(1, 0, 7)), 90, 0, 1, 7)

// Turning something already combined with an operator works too
assert_turn(generator("vector", vector(3, 4), vector(3, 4)) * 2, 90, -8, 6, 0)

// Turning a number generator does nothing
var/generator/num = generator("num", 5, 5)
ASSERT(num.Turn(90) == num)
ASSERT(num.Rand() == 5)
22 changes: 22 additions & 0 deletions Content.Tests/DMProject/Tests/Generator/GeneratorVectorArgs.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Building generators out of a vector that's held in a var must leave that var alone
/proc/RunTest()
var/vector/v = vector(1, 2)

var/generator/first = generator("vector", v, v)
var/generator/second = generator("box", v, v)
var/generator/third = generator("square", v, v)

var/vector/result = first.Rand()
ASSERT(result.x == 1)
ASSERT(result.y == 2)
ASSERT(second.Rand() != null)
ASSERT(third.Rand() != null)

// The vector is unharmed and still usable
ASSERT(v.len == 2)
ASSERT(v.x == 1)
ASSERT(v.y == 2)

var/vector/sum = v + vector(1, 1)
ASSERT(sum.x == 2)
ASSERT(sum.y == 3)
1 change: 1 addition & 0 deletions Content.Tests/DMProject/Tests/Vector/VectorAdd.dm
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@
ASSERT(A.x == 7)
ASSERT(A.y == 7)
ASSERT(A.z == 4)
ASSERT(A.len == 3) // Combining with a 3D vector makes it 3D
1 change: 1 addition & 0 deletions Content.Tests/DMProject/Tests/Vector/VectorDivide.dm
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@
ASSERT(A.x == 0.75)
ASSERT(A.y == 0.75)
ASSERT(A.z == 0)
ASSERT(A.len == 3) // Combining with a 3D vector makes it 3D
16 changes: 16 additions & 0 deletions Content.Tests/DMProject/Tests/Vector/VectorLen.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// A vector is only 3D when a third component is actually given
/proc/RunTest()
var/vector/two = vector(3, 4)
ASSERT(two.len == 2)
ASSERT(two.z == 0)

var/vector/three = vector(3, 4, 5)
ASSERT(three.len == 3)

// An explicit null still counts as a third component
var/vector/nullZ = vector(3, 4, null)
ASSERT(nullZ.len == 3)
ASSERT(nullZ.z == 0)

var/vector/direct = new /vector(3, 4)
ASSERT(direct.len == 2)
1 change: 1 addition & 0 deletions Content.Tests/DMProject/Tests/Vector/VectorMultiply.dm
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@
ASSERT(A.x == 12)
ASSERT(A.y == 12)
ASSERT(A.z == 0)
ASSERT(A.len == 3) // Combining with a 3D vector makes it 3D
1 change: 1 addition & 0 deletions Content.Tests/DMProject/Tests/Vector/VectorSubtract.dm
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@
ASSERT(A.x == -1)
ASSERT(A.y == -1)
ASSERT(A.z == -4)
ASSERT(A.len == 3) // Combining with a 3D vector makes it 3D
1 change: 1 addition & 0 deletions DMCompiler/DMStandard/Types/Generator.dm
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
var/_binobj as opendream_unimplemented

/generator/proc/Rand()
/generator/proc/Turn(angle)

/*
Generator Theory
Expand Down
1 change: 0 additions & 1 deletion DMCompiler/DMStandard/Types/Vector.dm
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,3 @@
set opendream_unimplemented = TRUE

/proc/vector(x, y, z)
return new /vector(x, y, z)
12 changes: 12 additions & 0 deletions OpenDreamRuntime/Objects/DreamObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,18 @@ public virtual DreamValue OperatorDivideRef(DreamValue b, DMProcState state) {
throw new InvalidOperationException($"Division cannot be done between {this} and {b}");
}

// **
[MustDisposeResource]
public virtual DreamValue OperatorPower(DreamValue b, DMProcState state) {
throw new InvalidOperationException($"Power cannot be done between {this} and {b}");
}

// - (unary)
[MustDisposeResource]
public virtual DreamValue OperatorNegate(DMProcState state) {
return new DreamValue(0); // BYOND evaluates most objects as 0 here
}

// |
[MustDisposeResource]
public virtual DreamValue OperatorOr(DreamValue b, DMProcState state) {
Expand Down
Loading
Loading