diff --git a/src/main/java/ch/njol/skript/expressions/ExprMidpoint.java b/src/main/java/ch/njol/skript/expressions/ExprMidpoint.java new file mode 100644 index 00000000000..fadaf50b083 --- /dev/null +++ b/src/main/java/ch/njol/skript/expressions/ExprMidpoint.java @@ -0,0 +1,127 @@ +package ch.njol.skript.expressions; + +import ch.njol.skript.Skript; +import ch.njol.skript.config.Node; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Example; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.ExpressionType; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.SyntaxStringBuilder; +import ch.njol.skript.lang.util.SimpleExpression; +import ch.njol.skript.registrations.Classes; +import ch.njol.util.Kleenean; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.event.Event; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.log.runtime.SyntaxRuntimeErrorProducer; + +@Name("Midpoint") +@Description("Get the midpoint between two vectors or two locations in the same world.") +@Example(""" + set {_center} to the midpoint between location(0, 0, 0) and location(10, 10, 10) + set {_centerBlock} to the block at {_center} + """) +@Example("set {_midpoint} to the mid-point of vector(20, 10, 5) and vector(3, 6, 9)") +@Since("INSERT VERSION") +public class ExprMidpoint extends SimpleExpression implements SyntaxRuntimeErrorProducer { + + static { + Skript.registerExpression(ExprMidpoint.class, Object.class, ExpressionType.COMBINED, + "[the] mid[-]point (of|between) %object% and %object%"); + } + + private Expression object1; + private Expression object2; + private Class[] classTypes = null; + private Class superType; + private Node node; + + @Override + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + object1 = exprs[0]; + object2 = exprs[1]; + Class[] type1 = checkExpressionType(object1); + Class[] type2 = checkExpressionType(object2); + if (type1.length == 1 && type2.length == 1) { + if (type1[0] != type2[0]) { + Skript.error("You can only get the midpoint between two locations or two vectors."); + return false; + } + classTypes = type1; + superType = type1[0]; + } else { + classTypes = type1.length > type2.length ? type1 : type2; + superType = Classes.getSuperClassInfo(classTypes).getC(); + } + node = getParser().getNode(); + return true; + } + + @Override + protected Object @Nullable [] get(Event event) { + Object object1 = this.object1.getSingle(event); + Object object2 = this.object2.getSingle(event); + if (object1 == null || object2 == null) { + return null; + } else if (object1 instanceof Location loc1 && object2 instanceof Location loc2) { + if (loc1.getWorld() != loc2.getWorld()) { + error("Cannot get the midpoint of two locations in different worlds."); + return null; + } + World world = loc1.getWorld(); + Vector vector = loc1.toVector().getMidpoint(loc2.toVector()); + return new Location[] {vector.toLocation(world)}; + } else if (object1 instanceof Vector vector1 && object2 instanceof Vector vector2) { + return new Vector[] {vector1.getMidpoint(vector2)}; + } else { + error("You can only get the midpoint between two locations or two vectors."); + return null; + } + } + + private Class[] checkExpressionType(Expression expr) { + if (expr.canReturn(Location.class)) { + if (!expr.canReturn(Vector.class)) + return new Class[] {Location.class}; + } else if (expr.canReturn(Vector.class)) { + return new Class[] {Vector.class}; + } + return new Class[] {Location.class, Vector.class}; + } + + @Override + public boolean isSingle() { + return true; + } + + @Override + public Class getReturnType() { + return superType; + } + + @Override + public Class[] possibleReturnTypes() { + return classTypes; + } + + @Override + public Node getNode() { + return node; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return new SyntaxStringBuilder(event, debug) + .append("the midpoint between") + .append(object1) + .append("and") + .append(object2) + .toString(); + } + +} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprMidpoint.sk b/src/test/skript/tests/syntaxes/expressions/ExprMidpoint.sk new file mode 100644 index 00000000000..cd700bf2d5b --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprMidpoint.sk @@ -0,0 +1,105 @@ + +using error catching + +test "midpoint locations - simple": + set {_loc1} to location(0, 0, 0) + set {_loc2} to {_loc1} ~ vector(10, 10, 10) + set {_midpoint} to the midpoint between {_loc1} and {_loc2} + assert {_midpoint} is location(5, 5, 5) with "Simple midpoint loc1 -> loc2 is incorrect" + set {_midpoint2} to the midpoint between {_loc2} and {_loc1} + assert {_midpoint2} is location(5, 5, 5) with "Simple midpoint loc2 -> loc1 is incorrect" + +test "midpoint locations - complex": + set {_loc1} to location(10.3, 38.6, 72.9) + set {_loc2} to location(-24.2, 63.8, 598.1) + set {_midpoint} to the midpoint between {_loc1} and {_loc2} + assert {_midpoint} is location(-6.95, 51.2, 335.5) with "Complex midpoint loc1 -> loc2 is incorrect" + set {_midpoint2} to the midpoint between {_loc2} and {_loc1} + assert {_midpoint2} is location(-6.95, 51.2, 335.5) with "Complex midpoint loc2 -> loc1 is incorrect" + +test "midpoint locations - negative": + set {_loc1} to location(-48.2, -33.6, -97.8) + set {_loc2} to location(-257.3, -59.3, -327.4) + set {_midpoint} to the midpoint between {_loc1} and {_loc2} + assert {_midpoint} is location(-152.75, -46.45, -212.6) with "Negative midpoint loc1 -> loc2 is incorrect" + set {_midpoint2} to the midpoint between {_loc2} and {_loc1} + assert {_midpoint2} is location(-152.75, -46.45, -212.6) with "Negative midpoint loc2 -> loc1 is incorrect" + +test "midpoint locations - function/variable": + set {_loc} to location(20, 30, 40) + set {_midpoint} to the midpoint between location(50, 60, 70) and {_loc} + assert {_midpoint} is location(35, 45, 55) with "Midpoint locations function -> variable is incorrect" + set {_midpoint2} to the midpoint between {_loc} and location(50, 60, 70) + assert {_midpoint2} is location(35, 45, 55) with "Midpoint locations variable -> function is incorrect" + +test "midpoint location - world error": + set {_loc1} to location(0, 0, 0) + set {_loc2} to location(0, 0, 0, "world_the_end") + catch runtime errors: + set {_midpoint} to the midpoint between {_loc1} and {_loc2} + assert last caught errors is "Cannot get the midpoint of two locations in different worlds." with "ExprMidpoint should error when getting midpoint of locations in different worlds" + +test "midpoint vectors - simple": + set {_vector1} to vector(0, 0, 0) + set {_vector2} to vector(10, 10, 10) + set {_midpoint} to the midpoint between {_vector1} and {_vector2} + assert {_midpoint} is vector(5, 5, 5) with "Simple midpoint vector1 -> vector2 is incorrect" + set {_midpoint2} to the midpoint between {_vector2} and {_vector1} + assert {_midpoint2} is vector(5, 5, 5) with "Simple midpoint vector2 -> vector1 is incorrect" + +test "midpoint vectors - complex": + set {_vector1} to vector(10.3, 38.6, 72.9) + set {_vector2} to vector(-24.2, 63.8, 598.1) + set {_midpoint} to the midpoint between {_vector1} and {_vector2} + assert {_midpoint} is vector(-6.95, 51.2, 335.5) with "Complex midpoint vector1 -> vector2 is incorrect" + set {_midpoint2} to the midpoint between {_vector2} and {_vector1} + assert {_midpoint2} is vector(-6.95, 51.2, 335.5) with "Complex midpoint vector2 -> vector1 is incorrect" + +test "midpoint vectors - negative": + set {_vector1} to vector(-48.2, -33.6, -97.8) + set {_vector2} to vector(-257.3, -59.3, -327.4) + set {_midpoint} to the midpoint between {_vector1} and {_vector2} + assert {_midpoint} is vector(-152.75, -46.45, -212.6) with "Negative midpoint vector1 -> vector2 is incorrect" + set {_midpoint2} to the midpoint between {_vector2} and {_vector1} + assert {_midpoint2} is vector(-152.75, -46.45, -212.6) with "Negative midpoint vector2 -> vector1 is incorrect" + +test "midpoint vectors - function/variable": + set {_vec} to vector(20, 30, 40) + set {_midpoint} to the midpoint between vector(50, 60, 70) and {_vec} + assert {_midpoint} is vector(35, 45, 55) with "Midpoint vectors function -> variable is incorrect" + set {_midpoint2} to the midpoint between {_vec} and vector(50, 60, 70) + assert {_midpoint2} is vector(35, 45, 55) with "Midpoint vectors variable -> function is incorrect" + +test "midpoint type error": + parse: + set {_midpoint} to the midpoint between location(0, 0, 0) and vector(0, 0, 0) + assert last parse logs is set with "Midpoint between location and vector should error" + assert last parse logs is "You can only get the midpoint between two locations or two vectors." with "Incorrect error of midpoint between location and vector" + + set {_loc} to location(0, 0, 0) + set {_vector} to vector(0, 0, 0) + + set {_error} to "You can only get the midpoint between two locations or two vectors." + catch runtime errors: + set {_midpoint} to the midpoint between {_loc} and {_vector} + assert last caught errors is {_error} with "Midpoint of location variable -> vector variable should error" + + catch runtime errors: + set {_midpoint2} to the midpoint between {_vector} and {_loc} + assert last caught errors is {_error} with "Midpoint of vector variable -> location variable should error" + + catch runtime errors: + set {_midpoint3} to the midpoint between location(0, 0, 0) and {_vector} + assert last caught errors is {_error} with "Midpoint of location function -> vector variable should error" + + catch runtime errors: + set {_midpoint4} to the midpoint between {_vector} and location(0, 0, 0) + assert last caught errors is {_error} with "Midpoint of vector variable -> location function should error" + + catch runtime errors: + set {_midpoint5} to the midpoint between vector(0, 0, 0) and {_loc} + assert last caught errors is {_error} with "Midpoint of vector function -> location variable should error" + + catch runtime errors: + set {_midpoint6} to the midpoint between {_loc} and vector(0, 0, 0) + assert last caught errors is {_error} with "Midpoint of location variable -> vector function should error"