diff --git a/build.gradle.kts b/build.gradle.kts index 6d60124..8327c06 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -44,7 +44,7 @@ dependencies { implementation("org.luaj:luaj-jse:3.0.1") implementation("net.luckperms:api:5.4") implementation("org.jetbrains.kotlin:kotlin-reflect") - implementation("dev.znci:twine:v1.0.4") + implementation("dev.znci:twine:1.0.7") implementation("org.reflections:reflections:0.10.2") testImplementation(kotlin("test")) diff --git a/src/main/kotlin/dev/znci/rocket/scripting/globals/tables/Math.kt b/src/main/kotlin/dev/znci/rocket/scripting/globals/tables/Math.kt new file mode 100644 index 0000000..70254eb --- /dev/null +++ b/src/main/kotlin/dev/znci/rocket/scripting/globals/tables/Math.kt @@ -0,0 +1,171 @@ +package dev.znci.rocket.scripting.globals.tables + +import dev.znci.rocket.scripting.annotations.Global +import dev.znci.rocket.scripting.api.RocketError +import dev.znci.twine.TwineNative +import dev.znci.twine.annotations.TwineNativeFunction +import dev.znci.twine.annotations.TwineOverload +import kotlin.math.roundToInt + +@Global +class LuaMath : TwineNative("math") { + @TwineNativeFunction + @TwineOverload + fun random(min: Double, max: Double): Double { + if (min >= max) { + throw RocketError("min value should be less than max value") + } + + return (min + (max - min) * Math.random()) + } + + @TwineNativeFunction + @TwineOverload + fun random(): Double { + return random(0.0, 1.0) + } + + @TwineNativeFunction + @TwineOverload + fun randomInt(min: Double, max: Double): Int { + if (min >= max) { + throw RocketError("min value should be less than max value") + } + + return (min + (max - min) * Math.random()).roundToInt() + } + + @TwineNativeFunction + @TwineOverload + fun randomInt(): Int { + return randomInt(0.0, 1.0) + } + + @TwineNativeFunction + fun cos(x: Double): Double { + return kotlin.math.cos(x) + } + + @TwineNativeFunction + fun sin(x: Double): Double { + return kotlin.math.sin(x) + } + + @TwineNativeFunction + fun tan(x: Double): Double { + return kotlin.math.tan(x) + } + + @TwineNativeFunction + fun acos(x: Double): Double { + return kotlin.math.acos(x) + } + + @TwineNativeFunction + fun asin(x: Double): Double { + return kotlin.math.asin(x) + } + + @TwineNativeFunction + fun atan(x: Double): Double { + return kotlin.math.atan(x) + } + + @TwineNativeFunction + fun atan2(y: Double, x: Double): Double { + return kotlin.math.atan2(y, x) + } + + @TwineNativeFunction + fun sqrt(x: Double): Double { + return kotlin.math.sqrt(x) + } + + @TwineNativeFunction + fun ceil(x: Double): Double { + return kotlin.math.ceil(x) + } + + @TwineNativeFunction + @TwineOverload + fun floor(x: Double): Double { + return kotlin.math.floor(x) + } + + @TwineNativeFunction + @TwineOverload + fun floor(vector3: LuaVector3): LuaVector3 { + return LuaVector3( + kotlin.math.floor(vector3.xProperty), + kotlin.math.floor(vector3.yProperty), + kotlin.math.floor(vector3.zProperty) + ) + } + + @TwineNativeFunction + @TwineOverload + fun abs(x: Double): Double { + return kotlin.math.abs(x) + } + + @TwineNativeFunction + @TwineOverload + fun abs(vector3: LuaVector3): LuaVector3 { + return LuaVector3( + kotlin.math.abs(vector3.xProperty), + kotlin.math.abs(vector3.yProperty), + kotlin.math.abs(vector3.zProperty) + ) + } + + @TwineNativeFunction + @TwineOverload + fun max(vararg values: Double): Double { + return values.maxOrNull() ?: 0.0 + } + + @TwineNativeFunction + @TwineOverload + fun max(vector3: LuaVector3): Double { + return kotlin.math.max( + kotlin.math.max(vector3.xProperty, vector3.yProperty), + vector3.zProperty + ) + } + + @TwineNativeFunction + @TwineOverload + fun min(vararg values: Double): Double { + return values.minOrNull() ?: 0.0 + } + + @TwineNativeFunction + @TwineOverload + fun min(vector3: LuaVector3): Double { + return kotlin.math.min( + kotlin.math.min(vector3.xProperty, vector3.yProperty), + vector3.zProperty + ) + } + + @TwineNativeFunction + @TwineOverload + fun round(x: Double): Double { + return kotlin.math.round(x) + } + + @TwineNativeFunction + @TwineOverload + fun round(vector3: LuaVector3): LuaVector3 { + return LuaVector3( + kotlin.math.round(vector3.xProperty), + kotlin.math.round(vector3.yProperty), + kotlin.math.round(vector3.zProperty) + ) + } + + @TwineNativeFunction + fun toDegrees(radians: Double): Double { + return Math.toDegrees(radians) + } +} \ No newline at end of file diff --git a/src/main/kotlin/dev/znci/rocket/scripting/globals/tables/Vector3.kt b/src/main/kotlin/dev/znci/rocket/scripting/globals/tables/Vector3.kt new file mode 100644 index 0000000..e42bf2b --- /dev/null +++ b/src/main/kotlin/dev/znci/rocket/scripting/globals/tables/Vector3.kt @@ -0,0 +1,234 @@ +package dev.znci.rocket.scripting.globals.tables + +import dev.znci.rocket.scripting.annotations.Global +import dev.znci.twine.TwineNative +import dev.znci.twine.annotations.TwineNativeFunction +import dev.znci.twine.annotations.TwineNativeProperty +import dev.znci.twine.annotations.TwineOverload +import org.bukkit.util.Vector + +@Global +class LuaVectors3 : TwineNative("vector3") { + @TwineNativeFunction + fun new(x: Double, y: Double, z: Double): LuaVector3 { + return LuaVector3(x, y, z) + } +} + +class LuaVector3( + x: Double, + y: Double, + z: Double +) : TwineNative("") { + private var bukkitVector = Vector(x, y, z) + + @TwineNativeFunction + @TwineOverload + fun add(vector3: LuaVector3): LuaVector3 { + bukkitVector.add(vector3.bukkitVector) + return this + } + + @TwineNativeFunction + @TwineOverload + fun add(x: Double, y: Double, z: Double): LuaVector3 { + bukkitVector.add(Vector(x, y, z)) + return this + } + + @TwineNativeFunction + @TwineOverload + fun set(vector3: LuaVector3): LuaVector3 { + bukkitVector = vector3.bukkitVector + return this + } + + @TwineNativeFunction + @TwineOverload + fun set(x: Double, y: Double, z: Double): LuaVector3 { + bukkitVector = Vector(x, y, z) + return this + } + + @TwineNativeFunction + fun clone(): LuaVector3 { + return bukkitVector.toLuaVector3() + } + + @TwineNativeFunction + fun crossProduct(comparedVector: LuaVector3): LuaVector3 { + // TODO: Evaluate whether getCrossProduct or crossProduct is better here + return bukkitVector.getCrossProduct( + comparedVector.bukkitVector + ).toLuaVector3() + } + + @TwineNativeFunction + fun cross(comparedVector: LuaVector3): LuaVector3 { + return crossProduct(comparedVector) + } + + @TwineNativeFunction + fun distance(comparedVector: LuaVector3): Double { + return bukkitVector.distance( + comparedVector.bukkitVector + ) + } + + @TwineNativeFunction + @TwineOverload + fun divide(comparedVector: LuaVector3): LuaVector3 { + return bukkitVector.divide( + comparedVector.bukkitVector + ).toLuaVector3() + } + + @TwineNativeFunction + @TwineOverload + fun divide(x: Double, y: Double, z: Double): LuaVector3 { + return bukkitVector.divide(Vector(x, y, z)).toLuaVector3() + } + + @TwineNativeFunction + @TwineOverload + fun div(comparedVector: LuaVector3): LuaVector3 { + return divide(comparedVector) + } + + @TwineNativeFunction + @TwineOverload + fun div(x: Double, y: Double, z: Double): LuaVector3 { + return divide(x, y, z) + } + + @TwineNativeFunction + @TwineOverload + fun multiply(comparedVector: LuaVector3): LuaVector3 { + return bukkitVector.multiply( + comparedVector.bukkitVector + ).toLuaVector3() + } + + @TwineNativeFunction + @TwineOverload + fun multiply(x: Double, y: Double, z: Double): LuaVector3 { + return bukkitVector.multiply(Vector(x, y, z)).toLuaVector3() + } + + @TwineNativeFunction + @TwineOverload + fun mul(comparedVector: LuaVector3): LuaVector3 { + return multiply(comparedVector) + } + + @TwineNativeFunction + @TwineOverload + fun mul(x: Double, y: Double, z: Double): LuaVector3 { + return multiply(x, y, z) + } + + @TwineNativeFunction + @TwineOverload + fun subtract(comparedVector: LuaVector3): LuaVector3 { + return bukkitVector.subtract( + comparedVector.bukkitVector + ).toLuaVector3() + } + + @TwineNativeFunction + @TwineOverload + fun subtract(x: Double, y: Double, z: Double): LuaVector3 { + return bukkitVector.subtract(Vector(x, y, z)).toLuaVector3() + } + + @TwineNativeFunction + @TwineOverload + fun sub(comparedVector: LuaVector3): LuaVector3 { + return subtract(comparedVector) + } + + @TwineNativeFunction + @TwineOverload + fun sub(x: Double, y: Double, z: Double): LuaVector3 { + return subtract(x, y, z) + } + + @TwineNativeFunction + fun dotProduct(comparedVector: LuaVector3): Double { + return bukkitVector.dot( + comparedVector.bukkitVector + ) + } + + @TwineNativeFunction + fun dot(comparedVector: LuaVector3): Double { + return dotProduct(comparedVector) + } + + @TwineNativeFunction + fun midpoint(comparedVector: LuaVector3): LuaVector3 { + return bukkitVector.getMidpoint( + comparedVector.bukkitVector + ).toLuaVector3() + } + + @TwineNativeFunction + fun inAABB(min: LuaVector3, max: LuaVector3): Boolean { + return bukkitVector.isInAABB(min.bukkitVector, max.bukkitVector) + } + + @TwineNativeFunction + fun inSphere(center: LuaVector3, radius: Double): Boolean { + return bukkitVector.isInSphere(center.bukkitVector, radius) + } + + @TwineNativeFunction + fun rotateAround(comparedVector: LuaVector3, angle: Double): LuaVector3 { + return this.bukkitVector.rotateAroundAxis( + comparedVector.bukkitVector, angle + ).toLuaVector3() + } + + @TwineNativeFunction + fun normalize(): LuaVector3 { + return bukkitVector.normalize().toLuaVector3() + } + + @TwineNativeProperty("magnitude") + val magnitude: Double + get() = bukkitVector.length() + + fun Vector.toLuaVector3(): LuaVector3 { + return LuaVector3( + this.x, + this.y, + this.z + ) + } + + @TwineNativeProperty("x") + var xProperty: Double + get() = bukkitVector.x + set(value) { + bukkitVector.x = value + } + + @TwineNativeProperty("y") + var yProperty: Double + get() = bukkitVector.y + set(value) { + bukkitVector.y = value + } + + @TwineNativeProperty("z") + var zProperty: Double + get() = bukkitVector.z + set(value) { + bukkitVector.z = value + } + + @TwineNativeFunction + override fun toString(): String { + return "vector3($xProperty, $yProperty, $zProperty)" + } +} \ No newline at end of file diff --git a/src/test/kotlin/dev/znci/rocket/scripting/globals/tables/LuaCommandsTest.kt b/src/test/kotlin/dev/znci/rocket/scripting/globals/tables/LuaCommandsTest.kt index d04280b..5a907d5 100644 --- a/src/test/kotlin/dev/znci/rocket/scripting/globals/tables/LuaCommandsTest.kt +++ b/src/test/kotlin/dev/znci/rocket/scripting/globals/tables/LuaCommandsTest.kt @@ -14,7 +14,6 @@ import org.mockbukkit.mockbukkit.ServerMock * a new instance of `LuaCommand`. */ class LuaCommandsTest { - private lateinit var mockServer: ServerMock private lateinit var plugin: Rocket diff --git a/src/test/kotlin/dev/znci/rocket/scripting/globals/tables/LuaLocationTest.kt b/src/test/kotlin/dev/znci/rocket/scripting/globals/tables/LuaLocationTest.kt new file mode 100644 index 0000000..4757c8c --- /dev/null +++ b/src/test/kotlin/dev/znci/rocket/scripting/globals/tables/LuaLocationTest.kt @@ -0,0 +1,43 @@ +package dev.znci.rocket.scripting.globals.tables + +import dev.znci.rocket.Rocket +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.luaj.vm2.Globals +import org.luaj.vm2.lib.jse.JsePlatform +import org.mockbukkit.mockbukkit.MockBukkit +import org.mockbukkit.mockbukkit.ServerMock +import kotlin.test.assertEquals + +class LuaLocationTest { + private lateinit var mockServer: ServerMock + private lateinit var plugin: Rocket + + @BeforeEach + fun setUp() { + mockServer = MockBukkit.mock() + plugin = MockBukkit.load(Rocket::class.java) + mockServer.worlds.add(mockServer.addSimpleWorld("world")) + } + + fun run(script: String): Any { + val globals: Globals = JsePlatform.standardGlobals() + globals.set("location", LuaLocations().table) + + val newScript = """ + local loc = location.new(1, 2, 3, "world", 0, 0) + $script + """.trimIndent() + + val result = globals.load(newScript, "test.lua").call() + + return result + } + + @Test + fun `new should create a new LuaLocation instance`() { + val result = run("return loc.x") + + assertEquals(1.0, result.toString().toDouble()) + } +} \ No newline at end of file diff --git a/src/test/kotlin/dev/znci/rocket/scripting/globals/tables/LuaMathTest.kt b/src/test/kotlin/dev/znci/rocket/scripting/globals/tables/LuaMathTest.kt new file mode 100644 index 0000000..d7a92fb --- /dev/null +++ b/src/test/kotlin/dev/znci/rocket/scripting/globals/tables/LuaMathTest.kt @@ -0,0 +1,141 @@ +package dev.znci.rocket.scripting.globals.tables + +import org.junit.jupiter.api.Test +import org.luaj.vm2.Globals +import org.luaj.vm2.LuaTable +import org.luaj.vm2.lib.jse.JsePlatform +import kotlin.test.assertEquals + +class LuaMathTest { + fun run(script: String): Any { + val globals: Globals = JsePlatform.standardGlobals() + globals.set("vector3", LuaVectors3().table) + globals.set("math", LuaMath().table) + + val result = globals.load(script, "test.lua").call() + + return result + } + + @Test + fun `acos should return the correct value`() { + val result = run("return math.acos(0.5)") + + assertEquals(1.0471976, result.toString().toDouble()) + } + + @Test + fun `asin should return the correct value`() { + val result = run("return math.asin(0.5)") + + assertEquals(0.5235988, result.toString().toDouble()) + } + + @Test + fun `atan should return the correct value`() { + val result = run("return math.atan(1)") + + assertEquals(0.7853982, result.toString().toDouble()) + } + + @Test + fun `atan2 should return the correct value`() { + val result = run("return math.atan2(1, 1)") + + assertEquals(0.7853982, result.toString().toDouble()) + } + + @Test + fun `random should return a value between 0 and 1`() { + val result = run("return math.random()") + + assert(result.toString().toDouble() in 0.0..1.0) + } + + @Test + fun `random(1, 100) should return a value between 1 and 100`() { + val result = run("return math.random(1, 100)") + + assert(result.toString().toDouble() in 1.0..100.0) + } + + @Test + fun `randomInt should return a value between 1 and 100`() { + val result = run("return math.randomInt(1, 100)") + + assert(result.toString().toDouble() in 1.0..100.0) + } + + @Test + fun `randomInt should return a value between 0 and 1`() { + val result = run("return math.randomInt()") + + assert(result.toString().toDouble() in 0.0..1.0) + } + + @Test + fun `cos should return the correct value`() { + val result = run("return math.cos(0)") + + assertEquals(1.0, result.toString().toDouble()) + } + + @Test + fun `sin should return the correct value`() { + val result = run("return math.sin(0)") + + assertEquals(0.0, result.toString().toDouble()) + } + + @Test + fun `tan should return the correct value`() { + val result = run("return math.tan(0)") + + assertEquals(0.0, result.toString().toDouble()) + } + + @Test + fun `min should return the correct value`() { + val result = run("return math.min(1, 2)") + + assertEquals(1.0, result.toString().toDouble()) + } + + @Test + fun `min with a vector should return the correct value`() { + val result = run("return math.min(vector3.new(1, 2, 3))") + + assertEquals(1.0, result.toString().toDouble()) + } + + @Test + fun `max should return the correct value`() { + val result = run("return math.max(1, 2)") + + assertEquals(2.0, result.toString().toDouble()) + } + + @Test + fun `max with a vector should return the correct value`() { + val result = run("return math.max(vector3.new(1, 2, 3))") + + assertEquals(3.0, result.toString().toDouble()) + } + + @Test + fun `abs should return the correct value`() { + val result = run("return math.abs(-1)") + + assertEquals(1.0, result.toString().toDouble()) + } + + @Test + fun `abs with a vector should return the correct value`() { + val result = run("return math.abs(vector3.new(-1, -2, -3))") + val table = result as LuaTable + + assertEquals(1.0, table.get("x").todouble()) + assertEquals(2.0, table.get("y").todouble()) + assertEquals(3.0, table.get("z").todouble()) + } +} \ No newline at end of file diff --git a/src/test/kotlin/dev/znci/rocket/scripting/globals/tables/LuaVector3Test.kt b/src/test/kotlin/dev/znci/rocket/scripting/globals/tables/LuaVector3Test.kt new file mode 100644 index 0000000..5684688 --- /dev/null +++ b/src/test/kotlin/dev/znci/rocket/scripting/globals/tables/LuaVector3Test.kt @@ -0,0 +1,369 @@ +package dev.znci.rocket.scripting.globals.tables + +import org.junit.jupiter.api.Test +import org.luaj.vm2.Globals +import org.luaj.vm2.LuaTable +import org.luaj.vm2.lib.jse.JsePlatform +import kotlin.test.assertEquals + +class LuaVector3Test { + fun run(script: String): Any { + val globals: Globals = JsePlatform.standardGlobals() + globals.set("vector3", LuaVectors3().table) + + val newScript = """ + local vector = vector3.new(1, 2, 3) + local vector2 = vector3.new(4, 5, 6) + $script + """.trimIndent() + + val result = globals.load(newScript, "test.lua").call() + + return result + } + + @Test + fun `vector3 addition should return the correct value`() { + val result = run(""" + vector.add(vector3.new(1, 2, 3)) + return vector + """.trimIndent()) + val table = result as LuaTable + + assertEquals(2.0, table.get("x").todouble()) + assertEquals(4.0, table.get("y").todouble()) + assertEquals(6.0, table.get("z").todouble()) + } + + @Test + fun `vector3 set should return the correct value`() { + val result = run(""" + vector.set(vector3.new(5, 6, 7)) + return vector + """.trimIndent()) + val table = result as LuaTable + + assertEquals(5.0, table.get("x").todouble()) + assertEquals(6.0, table.get("y").todouble()) + assertEquals(7.0, table.get("z").todouble()) + } + + @Test + fun `vector3 crossProduct should return the correct value`() { + val result = run(""" + local crossProduct = vector.crossProduct(vector2) + return crossProduct + """.trimIndent()) + val table = result as LuaTable + + assertEquals(-3.0, table.get("x").todouble()) + assertEquals(6.0, table.get("y").todouble()) + assertEquals(-3.0, table.get("z").todouble()) + } + + @Test + fun `vector3 cross should return the correct value`() { + val result = run(""" + local crossProduct = vector.cross(vector2) + return crossProduct + """.trimIndent()) + val table = result as LuaTable + + assertEquals(-3.0, table.get("x").todouble()) + assertEquals(6.0, table.get("y").todouble()) + assertEquals(-3.0, table.get("z").todouble()) + } + + @Test + fun `vector3 clone should return the correct value`() { + val result = run(""" + local clone = vector.clone() + return clone + """.trimIndent()) + val table = result as LuaTable + + assertEquals(1.0, table.get("x").todouble()) + assertEquals(2.0, table.get("y").todouble()) + assertEquals(3.0, table.get("z").todouble()) + } + + @Test + fun `vector3 distance should return the correct value`() { + val result = run(""" + local distance = vector.distance(vector2) + return distance + """.trimIndent()) + + assertEquals(5.196152, result.toString().toDouble()) + } + + @Test + fun `vector3 divide should return the correct value`() { + val result = run(""" + local divided = vector.divide(vector2) + return divided + """.trimIndent()) + val table = result as LuaTable + + assertEquals(0.25, table.get("x").todouble()) + assertEquals(0.4, table.get("y").todouble()) + assertEquals(0.5, table.get("z").todouble()) + } + + @Test + fun `vector3 div should return the correct value`() { + val result = run(""" + local divided = vector.div(vector2) + return divided + """.trimIndent()) + val table = result as LuaTable + + assertEquals(0.25, table.get("x").todouble()) + assertEquals(0.4, table.get("y").todouble()) + assertEquals(0.5, table.get("z").todouble()) + } + + @Test + fun `vector3 multiply should return the correct value`() { + val result = run(""" + local multiplied = vector.multiply(vector2) + return multiplied + """.trimIndent()) + val table = result as LuaTable + + assertEquals(4.0, table.get("x").todouble()) + assertEquals(10.0, table.get("y").todouble()) + assertEquals(18.0, table.get("z").todouble()) + } + + @Test + fun `vector3 multiply using non-vector parameters should return the correct value`() { + val result = run(""" + local multiplied = vector.multiply(4, 5, 6) + return multiplied + """.trimIndent()) + val table = result as LuaTable + + assertEquals(4.0, table.get("x").todouble()) + assertEquals(10.0, table.get("y").todouble()) + assertEquals(18.0, table.get("z").todouble()) + } + + @Test + fun `vector3 mul should return the correct value`() { + val result = run(""" + local multiplied = vector.mul(vector2) + return multiplied + """.trimIndent()) + val table = result as LuaTable + + assertEquals(4.0, table.get("x").todouble()) + assertEquals(10.0, table.get("y").todouble()) + assertEquals(18.0, table.get("z").todouble()) + } + + @Test + fun `vector3 mul using non-vector parameters should return the correct value`() { + val result = run(""" + local multiplied = vector.mul(4, 5, 6) + return multiplied + """.trimIndent()) + val table = result as LuaTable + + assertEquals(4.0, table.get("x").todouble()) + assertEquals(10.0, table.get("y").todouble()) + assertEquals(18.0, table.get("z").todouble()) + } + + @Test + fun `vector3 subtract should return the correct value`() { + val result = run(""" + local subtracted = vector.subtract(vector2) + return subtracted + """.trimIndent()) + val table = result as LuaTable + + assertEquals(-3.0, table.get("x").todouble()) + assertEquals(-3.0, table.get("y").todouble()) + assertEquals(-3.0, table.get("z").todouble()) + } + + @Test + fun `vector3 subtract using non-vector parameters should return the correct value`() { + val result = run(""" + local subtracted = vector.subtract(4, 5, 6) + return subtracted + """.trimIndent()) + val table = result as LuaTable + + assertEquals(-3.0, table.get("x").todouble()) + assertEquals(-3.0, table.get("y").todouble()) + assertEquals(-3.0, table.get("z").todouble()) + } + + @Test + fun `vector3 sub should return the correct value`() { + val result = run(""" + local subtracted = vector.sub(vector2) + return subtracted + """.trimIndent()) + val table = result as LuaTable + + assertEquals(-3.0, table.get("x").todouble()) + assertEquals(-3.0, table.get("y").todouble()) + assertEquals(-3.0, table.get("z").todouble()) + } + + @Test + fun `vector3 sub using non-vector parameters should return the correct value`() { + val result = run(""" + local subtracted = vector.sub(4, 5, 6) + return subtracted + """.trimIndent()) + val table = result as LuaTable + + assertEquals(-3.0, table.get("x").todouble()) + assertEquals(-3.0, table.get("y").todouble()) + assertEquals(-3.0, table.get("z").todouble()) + } + + @Test + fun `vector3 dotProduct should return the correct value`() { + val result = run(""" + local dotProduct = vector.dotProduct(vector2) + return dotProduct + """.trimIndent()) + + assertEquals(32.0, result.toString().toDouble()) + } + + @Test + fun `vector3 dot should return the correct value`() { + val result = run(""" + local dotProduct = vector.dot(vector2) + return dotProduct + """.trimIndent()) + + assertEquals(32.0, result.toString().toDouble()) + } + + @Test + fun `vector3 midpoint should return the correct value`() { + val result = run(""" + local midpoint = vector.midpoint(vector2) + return midpoint + """.trimIndent()) + val table = result as LuaTable + + assertEquals(2.5, table.get("x").todouble()) + assertEquals(3.5, table.get("y").todouble()) + assertEquals(4.5, table.get("z").todouble()) + } + + @Test + fun `vector3 inAABB should return the correct value`() { + val result = run(""" + local inAABB = vector.inAABB(vector3.new(0, 0, 0), vector3.new(5, 6, 7)) + return inAABB + """.trimIndent()) + + assertEquals(true, result.toString().toBoolean()) + } + + @Test + fun `vector3 inAABB with invalid coordinates should return the correct value`() { + val result = run(""" + local inAABB = vector.inAABB(vector3.new(0, 0, 0), vector3.new(1, 1, 1)) + return inAABB + """.trimIndent()) + + assertEquals(false, result.toString().toBoolean()) + } + + @Test + fun `vector3 inSphere should return the correct value`() { + val result = run(""" + local inSphere = vector.inSphere(vector3.new(0, 0, 0), 5) + return inSphere + """.trimIndent()) + + assertEquals(true, result.toString().toBoolean()) + } + + @Test + fun `vector3 inSphere with invalid coordinates should return the correct value`() { + val result = run(""" + local inSphere = vector.inSphere(vector3.new(0, 0, 0), 1) + return inSphere + """.trimIndent()) + + assertEquals(false, result.toString().toBoolean()) + } + + @Test + fun `vector3 rotateAround should return the correct value`() { + val result = run(""" + local rotated = vector.rotateAround(vector3.new(10, 10, 10), 90) + return rotated + """.trimIndent()) + val table = result as LuaTable + + assertEquals(2.9642228305135787, table.get("x").todouble()) + assertEquals(0.967701571231181, table.get("y").todouble()) + assertEquals(2.0680755982552386, table.get("z").todouble()) + } + + @Test + fun `vector3 normalize should return the correct value`() { + val result = run(""" + local normalized = vector.normalize() + return normalized + """.trimIndent()) + val table = result as LuaTable + + assertEquals(0.2672612419124244, table.get("x").todouble()) + assertEquals(0.5345224838248488, table.get("y").todouble()) + assertEquals(0.8017837257372732, table.get("z").todouble()) + } + + @Test + fun `vector3 magnitude should return the correct value`() { + val result = run(""" + local magnitude = vector.magnitude + return magnitude + """.trimIndent()) + + assertEquals(3.7416575, result.toString().toDouble()) + } + + @Test + fun `vector3 toString should return the correct value`() { + val result = run(""" + local str = vector.toString() + return str + """.trimIndent()) + + assertEquals("vector3(1.0, 2.0, 3.0)", result.toString()) + } + + @Test + fun `vector3 x property should return the correct value`() { + val result = run("return vector.x") + + assertEquals(1.0, result.toString().toDouble()) + } + + @Test + fun `vector3 y property should return the correct value`() { + val result = run("return vector.y") + + assertEquals(2.0, result.toString().toDouble()) + } + + @Test + fun `vector3 z property should return the correct value`() { + val result = run("return vector.z") + + assertEquals(3.0, result.toString().toDouble()) + } +} \ No newline at end of file