package carpet.script.api; import carpet.script.CarpetContext; import carpet.script.Context; import carpet.script.Expression; import carpet.script.LazyValue; import carpet.script.argument.BlockArgument; import carpet.script.argument.Vector3Argument; import carpet.script.exception.BreakStatement; import carpet.script.exception.ContinueStatement; import carpet.script.exception.InternalExpressionException; import carpet.script.value.BlockValue; import carpet.script.value.LazyListValue; import carpet.script.value.ListValue; import carpet.script.value.NumericValue; import carpet.script.value.Value; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3i; import java.util.ArrayList; import java.util.List; import java.util.Locale; import static java.lang.Math.abs; import static java.lang.Math.max; import static java.lang.Math.min; public class BlockIterators { public static void apply(Expression expression) { expression.addLazyFunction("scan", -1, (c, t, lv) -> { CarpetContext cc = (CarpetContext)c; BlockArgument centerLocator = BlockArgument.findIn(cc, lv, 0); Vector3Argument rangeLocator = Vector3Argument.findIn(cc, lv, centerLocator.offset); BlockPos center = centerLocator.block.getPos(); Vec3i range; if (rangeLocator.fromBlock) { range = new Vec3i( abs(rangeLocator.vec.x - center.getX()), abs(rangeLocator.vec.y - center.getY()), abs(rangeLocator.vec.z - center.getZ()) ); //if (lv.size() > rangeLocator.offset+1) throw new InternalExpressionException("'scan' takes two block positions, and the expression") } else { range = new Vec3i(abs(rangeLocator.vec.x), abs(rangeLocator.vec.y), abs(rangeLocator.vec.z)); } Vec3i upperRange = range; if (lv.size() > rangeLocator.offset+1) // +1 cause we still need the expression { rangeLocator = Vector3Argument.findIn(cc, lv, rangeLocator.offset); if (rangeLocator.fromBlock) { upperRange = new Vec3i( abs(rangeLocator.vec.x - center.getX()), abs(rangeLocator.vec.y - center.getY()), abs(rangeLocator.vec.z - center.getZ()) ); //if (lv.size() > rangeLocator.offset+1) throw new InternalExpressionException("'scan' takes two block positions, and the expression") } else { upperRange = new Vec3i(abs(rangeLocator.vec.x), abs(rangeLocator.vec.y), abs(rangeLocator.vec.z)); } } if (lv.size() != rangeLocator.offset+1) { throw new InternalExpressionException("'scan' takes two, or three block positions, and an expression: "+lv.size()+" "+rangeLocator.offset); } LazyValue expr = lv.get(rangeLocator.offset); int cx = center.getX(); int cy = center.getY(); int cz = center.getZ(); int xrange = range.getX(); int yrange = range.getY(); int zrange = range.getZ(); int xprange = upperRange.getX(); int yprange = upperRange.getY(); int zprange = upperRange.getZ(); //saving outer scope LazyValue _x = c.getVariable("_x"); LazyValue _y = c.getVariable("_y"); LazyValue _z = c.getVariable("_z"); LazyValue __ = c.getVariable("_"); int sCount = 0; outer:for (int y=cy-yrange; y <= cy+yprange; y++) { int yFinal = y; c.setVariable("_y", (c_, t_) -> new NumericValue(yFinal).bindTo("_y")); for (int x=cx-xrange; x <= cx+xprange; x++) { int xFinal = x; c.setVariable("_x", (c_, t_) -> new NumericValue(xFinal).bindTo("_x")); for (int z=cz-zrange; z <= cz+zprange; z++) { int zFinal = z; c.setVariable("_z", (c_, t_) -> new NumericValue(zFinal).bindTo("_z")); Value blockValue = BlockValue.fromCoords(((CarpetContext)c), xFinal,yFinal,zFinal).bindTo("_"); c.setVariable( "_", (cc_, t_c) -> blockValue); Value result; try { result = expr.evalValue(c, t); } catch (ContinueStatement notIgnored) { result = notIgnored.retval; } catch (BreakStatement notIgnored) { break outer; } if (t != Context.VOID && result.getBoolean()) { sCount += 1; } } } } //restoring outer scope c.setVariable("_x", _x); c.setVariable("_y", _y); c.setVariable("_z", _z); c.setVariable("_", __); int finalSCount = sCount; return (c_, t_) -> new NumericValue(finalSCount); }); expression.addLazyFunction("volume", -1, (c, t, lv) -> { CarpetContext cc = (CarpetContext)c; BlockArgument pos1Locator = BlockArgument.findIn(cc, lv, 0); BlockArgument pos2Locator = BlockArgument.findIn(cc, lv, pos1Locator.offset); BlockPos pos1 = pos1Locator.block.getPos(); BlockPos pos2 = pos2Locator.block.getPos(); int x1 = pos1.getX(); int y1 = pos1.getY(); int z1 = pos1.getZ(); int x2 = pos2.getX(); int y2 = pos2.getY(); int z2 = pos2.getZ(); int minx = min(x1, x2); int miny = min(y1, y2); int minz = min(z1, z2); int maxx = max(x1, x2); int maxy = max(y1, y2); int maxz = max(z1, z2); LazyValue expr = lv.get(pos2Locator.offset); //saving outer scope LazyValue _x = c.getVariable("_x"); LazyValue _y = c.getVariable("_y"); LazyValue _z = c.getVariable("_z"); LazyValue __ = c.getVariable("_"); int sCount = 0; outer:for (int y=miny; y <= maxy; y++) { int yFinal = y; c.setVariable("_y", (c_, t_) -> new NumericValue(yFinal).bindTo("_y")); for (int x=minx; x <= maxx; x++) { int xFinal = x; c.setVariable("_x", (c_, t_) -> new NumericValue(xFinal).bindTo("_x")); for (int z=minz; z <= maxz; z++) { int zFinal = z; c.setVariable("_z", (c_, t_) -> new NumericValue(zFinal).bindTo("_z")); Value blockValue = BlockValue.fromCoords(((CarpetContext)c), xFinal,yFinal,zFinal).bindTo("_"); c.setVariable( "_", (cc_, t_c) -> blockValue); Value result; try { result = expr.evalValue(c, t); } catch (ContinueStatement notIgnored) { result = notIgnored.retval; } catch (BreakStatement notIgnored) { break outer; } if (t != Context.VOID && result.getBoolean()) { sCount += 1; } } } } //restoring outer scope c.setVariable("_x", _x); c.setVariable("_y", _y); c.setVariable("_z", _z); c.setVariable("_", __); int finalSCount = sCount; return (c_, t_) -> new NumericValue(finalSCount); }); expression.addLazyFunction("neighbours", -1, (c, t, lv)-> { BlockPos center = BlockArgument.findIn((CarpetContext) c, lv,0).block.getPos(); ServerWorld world = ((CarpetContext) c).s.getWorld(); List neighbours = new ArrayList<>(); neighbours.add(new BlockValue(null, world, center.up())); neighbours.add(new BlockValue(null, world, center.down())); neighbours.add(new BlockValue(null, world, center.north())); neighbours.add(new BlockValue(null, world, center.south())); neighbours.add(new BlockValue(null, world, center.east())); neighbours.add(new BlockValue(null, world, center.west())); Value retval = ListValue.wrap(neighbours); return (c_, t_) -> retval; }); expression.addLazyFunction("rect", -1, (c, t, lv)-> { CarpetContext cc = (CarpetContext) c; int cx, cy, cz; int sminx, sminy, sminz; int smaxx, smaxy, smaxz; BlockArgument cposLocator = BlockArgument.findIn(cc, lv, 0); BlockPos cpos = cposLocator.block.getPos(); cx = cpos.getX(); cy = cpos.getY(); cz = cpos.getZ(); if (lv.size() > cposLocator.offset) { Vector3Argument diffLocator = Vector3Argument.findIn(cc, lv, cposLocator.offset); if (diffLocator.fromBlock) { sminx = MathHelper.floor(abs(diffLocator.vec.x - cx)); sminy = MathHelper.floor(abs(diffLocator.vec.y - cx)); sminz = MathHelper.floor(abs(diffLocator.vec.z - cx)); } else { sminx = MathHelper.floor(abs(diffLocator.vec.x)); sminy = MathHelper.floor(abs(diffLocator.vec.y)); sminz = MathHelper.floor(abs(diffLocator.vec.z)); } if (lv.size() > diffLocator.offset) { Vector3Argument posDiff = Vector3Argument.findIn(cc, lv, diffLocator.offset); if (posDiff.fromBlock) { smaxx = MathHelper.floor(abs(posDiff.vec.x - cx)); smaxy = MathHelper.floor(abs(posDiff.vec.y - cx)); smaxz = MathHelper.floor(abs(posDiff.vec.z - cx)); } else { smaxx = MathHelper.floor(abs(posDiff.vec.x)); smaxy = MathHelper.floor(abs(posDiff.vec.y)); smaxz = MathHelper.floor(abs(posDiff.vec.z)); } } else { smaxx = sminx; smaxy = sminy; smaxz = sminz; } } else { sminx = 1; sminy = 1; sminz = 1; smaxx = 1; smaxy = 1; smaxz = 1; } return (c_, t_) -> new LazyListValue() { final int minx = cx-sminx; final int miny = cy-sminy; final int minz = cz-sminz; final int maxx = cx+smaxx; final int maxy = cy+smaxy; final int maxz = cz+smaxz; int x; int y; int z; { reset(); } @Override public boolean hasNext() { return y <= maxy; } @Override public Value next() { Value r = BlockValue.fromCoords(cc, x,y,z); //possibly reroll context x++; if (x > maxx) { x = minx; z++; if (z > maxz) { z = minz; y++; // hasNext should fail if we went over } } return r; } @Override public void fatality() { // possibly return original x, y, z super.fatality(); } @Override public void reset() { x = minx; y = miny; z = minz; } @Override public String getString() { return String.format(Locale.ROOT, "rect[(%d,%d,%d),..,(%d,%d,%d)]",minx, miny, minz, maxx, maxy, maxz); } }; }); expression.addLazyFunction("diamond", -1, (c, t, lv)-> { CarpetContext cc = (CarpetContext)c; BlockArgument cposLocator=BlockArgument.findIn((CarpetContext)c,lv,0); BlockPos cpos = cposLocator.block.getPos(); int cx; int cy; int cz; int width; int height; try { cx = cpos.getX(); cy = cpos.getY(); cz = cpos.getZ(); if (lv.size()==cposLocator.offset) { Value retval = ListValue.of( BlockValue.fromCoords(cc, cx, cy-1, cz), BlockValue.fromCoords(cc, cx, cy, cz), BlockValue.fromCoords(cc, cx-1, cy, cz), BlockValue.fromCoords(cc, cx, cy, cz-1), BlockValue.fromCoords(cc, cx+1, cy, cz), BlockValue.fromCoords(cc, cx, cy, cz+1), BlockValue.fromCoords(cc, cx, cy+1, cz) ); return (_c, _t ) -> retval; } else if (lv.size()==1+cposLocator.offset) { width = (int) ((NumericValue) lv.get(cposLocator.offset).evalValue(c)).getLong(); height = 0; } else if(lv.size()==2+cposLocator.offset) { width = (int) ((NumericValue) lv.get(cposLocator.offset).evalValue(c)).getLong(); height = (int) ((NumericValue) lv.get(cposLocator.offset+1).evalValue(c)).getLong(); } else{ throw new InternalExpressionException("Incorrect number of arguments for 'diamond'"); } } catch (ClassCastException exc) { throw new InternalExpressionException("Attempted to pass a non-number to 'diamond'"); } if (height == 0) { return (c_, t_) -> new LazyListValue() { int curradius; int curpos; { reset(); } @Override public boolean hasNext() { return curradius <= width; } @Override public Value next() { if (curradius == 0) { curradius = 1; return BlockValue.fromCoords(cc, cx, cy, cz); } // x = 3-|i-6| // z = |( (i-3)%12-6|-3 Value block = BlockValue.fromCoords(cc, cx+(curradius-abs(curpos-2*curradius)), cy, cz-curradius+abs( abs(curpos-curradius)%(4*curradius) -2*curradius )); curpos++; if (curpos>=curradius*4) { curradius++; curpos = 0; } return block; } @Override public void reset() { curradius = 0; curpos = 0; } @Override public String getString() { return String.format(Locale.ROOT, "diamond[(%d,%d,%d),%d,0]",cx, cy, cz, width); } }; } else { return (c_, t_) -> new LazyListValue() { int curradius; int curpos; int curheight; { reset(); } @Override public boolean hasNext() { return curheight <= height; } @Override public Value next() { if (curheight == -height || curheight == height) { return BlockValue.fromCoords(cc, cx, cy+curheight++, cz); } if (curradius == 0) { curradius++; return BlockValue.fromCoords(cc, cx, cy+curheight, cz); } // x = 3-|i-6| // z = |( (i-3)%12-6|-3 Value block = BlockValue.fromCoords(cc, cx+(curradius-abs(curpos-2*curradius)), cy+curheight, cz-curradius+abs( abs(curpos-curradius)%(4*curradius) -2*curradius )); curpos++; if (curpos>=curradius*4) { curradius++; curpos = 0; if (curradius>width -abs(width*curheight/height)) { curheight++; curradius = 0; curpos = 0; } } return block; } @Override public void reset() { curradius = 0; curpos = 0; curheight = -height; } @Override public String getString() { return String.format(Locale.ROOT, "diamond[(%d,%d,%d),%d,%d]",cx, cy, cz, width, height); } }; } }); } }