diff --git a/src/main/java/com/sk89q/worldedit/EditSession.java b/src/main/java/com/sk89q/worldedit/EditSession.java index 016aa9c1f..dc0138e7f 100644 --- a/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/src/main/java/com/sk89q/worldedit/EditSession.java @@ -2808,10 +2808,15 @@ public class EditSession { final RValue typeVariable = expression.getVariable("type", false); final RValue dataVariable = expression.getVariable("data", false); + final WorldEditExpressionEnvironment environment = new WorldEditExpressionEnvironment(this, unit, zero); + expression.setEnvironment(environment); + final ArbitraryShape shape = new ArbitraryShape(region) { @Override protected BaseBlock getMaterial(int x, int y, int z, BaseBlock defaultMaterial) { - final Vector scaled = new Vector(x, y, z).subtract(zero).divide(unit); + final Vector current = new Vector(x, y, z); + environment.setCurrentBlock(current); + final Vector scaled = current.subtract(zero).divide(unit); try { if (expression.evaluate(scaled.getX(), scaled.getY(), scaled.getZ(), defaultMaterial.getType(), defaultMaterial.getData()) <= 0) { @@ -2837,7 +2842,8 @@ public class EditSession { final RValue y = expression.getVariable("y", false); final RValue z = expression.getVariable("z", false); - Vector zero2 = zero.add(0.5, 0.5, 0.5); + final WorldEditExpressionEnvironment environment = new WorldEditExpressionEnvironment(this, unit, zero); + expression.setEnvironment(environment); final DoubleArrayList queue = new DoubleArrayList(false); @@ -2848,13 +2854,11 @@ public class EditSession { // transform expression.evaluate(scaled.getX(), scaled.getY(), scaled.getZ()); - final Vector sourceScaled = new Vector(x.getValue(), y.getValue(), z.getValue()); - - // unscale, unoffset, round-nearest - final BlockVector sourcePosition = sourceScaled.multiply(unit).add(zero2).toBlockPoint(); + final BlockVector sourcePosition = environment.toWorld(scaled.getX(), scaled.getY(), scaled.getZ()); // read block from world - BaseBlock material = new BaseBlock(world.getBlockType(sourcePosition), world.getBlockData(sourcePosition)); + // TODO: use getBlock here once the reflection is out of the way + final BaseBlock material = new BaseBlock(world.getBlockType(sourcePosition), world.getBlockData(sourcePosition)); // queue operation queue.put(position, material); diff --git a/src/main/java/com/sk89q/worldedit/WorldEditExpressionEnvironment.java b/src/main/java/com/sk89q/worldedit/WorldEditExpressionEnvironment.java new file mode 100644 index 000000000..4fd46aa06 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/WorldEditExpressionEnvironment.java @@ -0,0 +1,59 @@ +package com.sk89q.worldedit; + +import com.sk89q.worldedit.expression.runtime.ExpressionEnvironment; + +class WorldEditExpressionEnvironment implements ExpressionEnvironment { + private final Vector unit; + private final Vector zero2; + private Vector current = new Vector(); + private EditSession editSession; + + public WorldEditExpressionEnvironment(EditSession editSession, Vector unit, Vector zero) { + this.editSession = editSession; + this.unit = unit; + this.zero2 = zero.add(0.5, 0.5, 0.5); + } + + public BlockVector toWorld(double x, double y, double z) { + // unscale, unoffset, round-nearest + return new Vector(x, y, z).multiply(unit).add(zero2).toBlockPoint(); + } + + public Vector toWorldRel(double x, double y, double z) { + return current.add(x, y, z); + } + + @Override + public int getBlockType(double x, double y, double z) { + return editSession.getBlockType(toWorld(x, y, z)); + } + + @Override + public int getBlockData(double x, double y, double z) { + return editSession.getBlockData(toWorld(x, y, z)); + } + + @Override + public int getBlockTypeAbs(double x, double y, double z) { + return editSession.getBlockType(new Vector(x, y, z)); + } + + @Override + public int getBlockDataAbs(double x, double y, double z) { + return editSession.getBlockData(new Vector(x, y, z)); + } + + @Override + public int getBlockTypeRel(double x, double y, double z) { + return editSession.getBlockType(toWorldRel(x, y, z)); + } + + @Override + public int getBlockDataRel(double x, double y, double z) { + return editSession.getBlockData(toWorldRel(x, y, z)); + } + + public void setCurrentBlock(Vector current) { + this.current = current; + } +} diff --git a/src/main/java/com/sk89q/worldedit/expression/Expression.java b/src/main/java/com/sk89q/worldedit/expression/Expression.java index 1b34c2032..f78ab76b3 100644 --- a/src/main/java/com/sk89q/worldedit/expression/Expression.java +++ b/src/main/java/com/sk89q/worldedit/expression/Expression.java @@ -24,9 +24,12 @@ import java.util.List; import java.util.Map; import java.util.Stack; +import com.sk89q.worldedit.BlockVector; +import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.expression.lexer.Lexer; import com.sk89q.worldedit.expression.lexer.tokens.Token; import com.sk89q.worldedit.expression.parser.Parser; +import com.sk89q.worldedit.expression.runtime.ExpressionEnvironment; import com.sk89q.worldedit.expression.runtime.Constant; import com.sk89q.worldedit.expression.runtime.EvaluationException; import com.sk89q.worldedit.expression.runtime.Functions; @@ -43,7 +46,7 @@ import com.sk89q.worldedit.expression.runtime.Variable; * Arithmetic: +, -, *, /, % (modulo), ^ (power), - (unary), --, ++ (prefix only) * Comparison: <=, >=, >, <, ==, !=, ~= (near) * - * Supported functions: abs, acos, asin, atan, atan2, cbrt, ceil, cos, cosh, exp, floor, ln, log, log10, max, max, min, min, rint, round, sin, sinh, sqrt, tan, tanh + * Supported functions: abs, acos, asin, atan, atan2, cbrt, ceil, cos, cosh, exp, floor, ln, log, log10, max, max, min, min, rint, round, sin, sinh, sqrt, tan, tanh and more. (See the Functions class or the wiki) * * Constants: e, pi * @@ -65,6 +68,7 @@ public class Expression { private final String[] variableNames; private RValue root; private final Functions functions = new Functions(); + private ExpressionEnvironment environment; public static Expression compile(String expression, String... variableNames) throws ExpressionException { return new Expression(expression, variableNames); @@ -157,4 +161,12 @@ public class Expression { public Functions getFunctions() { return functions; } + + public ExpressionEnvironment getEnvironment() { + return environment; + } + + public void setEnvironment(ExpressionEnvironment environment) { + this.environment = environment; + } } diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/ExpressionEnvironment.java b/src/main/java/com/sk89q/worldedit/expression/runtime/ExpressionEnvironment.java new file mode 100644 index 000000000..5ac316834 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/ExpressionEnvironment.java @@ -0,0 +1,16 @@ +package com.sk89q.worldedit.expression.runtime; + +import com.sk89q.worldedit.BlockVector; +import com.sk89q.worldedit.Vector; + +/** + * Represents a way to access blocks in a world. Has to accept non-rounded coordinates. + */ +public interface ExpressionEnvironment { + int getBlockType(double x, double y, double z); + int getBlockData(double x, double y, double z); + int getBlockTypeAbs(double x, double y, double z); + int getBlockDataAbs(double x, double y, double z); + int getBlockTypeRel(double x, double y, double z); + int getBlockDataRel(double x, double y, double z); +} diff --git a/src/main/java/com/sk89q/worldedit/expression/runtime/Functions.java b/src/main/java/com/sk89q/worldedit/expression/runtime/Functions.java index 0ca3198ae..018d217ea 100644 --- a/src/main/java/com/sk89q/worldedit/expression/runtime/Functions.java +++ b/src/main/java/com/sk89q/worldedit/expression/runtime/Functions.java @@ -375,4 +375,60 @@ public final class Functions { public static double randint(RValue max) throws EvaluationException { return random.nextInt((int) Math.floor(max.getValue())); } + + + private static double queryInternal(LValue type, LValue data, double typeId, double dataValue) throws EvaluationException { + // Compare to input values and determine return value + final double ret = typeId == type.getValue() && typeId == type.getValue() ? 1.0 : 0.0; + + type.assign(typeId); + data.assign(dataValue); + + return ret; + } + + @Dynamic + public static double query(RValue x, RValue y, RValue z, LValue type, LValue data) throws EvaluationException { + final double xp = x.getValue(); + final double yp = y.getValue(); + final double zp = z.getValue(); + + final ExpressionEnvironment environment = Expression.getInstance().getEnvironment(); + + // Read values from world + final double typeId = environment.getBlockType(xp, yp, zp); + final double dataValue = environment.getBlockData(xp, yp, zp); + + return queryInternal(type, data, typeId, dataValue); + } + + @Dynamic + public static double queryAbs(RValue x, RValue y, RValue z, LValue type, LValue data) throws EvaluationException { + final double xp = x.getValue(); + final double yp = y.getValue(); + final double zp = z.getValue(); + + final ExpressionEnvironment environment = Expression.getInstance().getEnvironment(); + + // Read values from world + final double typeId = environment.getBlockTypeAbs(xp, yp, zp); + final double dataValue = environment.getBlockDataAbs(xp, yp, zp); + + return queryInternal(type, data, typeId, dataValue); + } + + @Dynamic + public static double queryRel(RValue x, RValue y, RValue z, LValue type, LValue data) throws EvaluationException { + final double xp = x.getValue(); + final double yp = y.getValue(); + final double zp = z.getValue(); + + final ExpressionEnvironment environment = Expression.getInstance().getEnvironment(); + + // Read values from world + final double typeId = environment.getBlockTypeRel(xp, yp, zp); + final double dataValue = environment.getBlockDataRel(xp, yp, zp); + + return queryInternal(type, data, typeId, dataValue); + } }