package carpet.script.api; import carpet.script.CarpetContext; import carpet.script.Context; import carpet.script.Expression; import carpet.script.Fluff; 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 java.util.ArrayList; import java.util.List; import java.util.Locale; import net.minecraft.core.BlockPos; import net.minecraft.core.Vec3i; import net.minecraft.server.level.ServerLevel; import net.minecraft.util.Mth; 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(final Expression expression) { // lazy cause of lazy expression expression.addLazyFunction("scan", (c, t, llv) -> { if (llv.size() < 3) { throw new InternalExpressionException("'scan' needs many more arguments"); } final List lv = Fluff.AbstractFunction.unpackLazy(llv.subList(0, llv.size() - 1), c, Context.NONE); final CarpetContext cc = (CarpetContext) c; final BlockArgument centerLocator = BlockArgument.findIn(cc, lv, 0); Vector3Argument rangeLocator = Vector3Argument.findIn(lv, centerLocator.offset); final BlockPos center = centerLocator.block.getPos(); final 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()) ); } 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(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()) ); } else { upperRange = new Vec3i(abs(rangeLocator.vec.x), abs(rangeLocator.vec.y), abs(rangeLocator.vec.z)); } } if (llv.size() != rangeLocator.offset + 1) { throw new InternalExpressionException("'scan' takes two, or three block positions, and an expression: " + lv.size() + " " + rangeLocator.offset); } final LazyValue expr = llv.get(rangeLocator.offset); final int cx = center.getX(); final int cy = center.getY(); final int cz = center.getZ(); final int xrange = range.getX(); final int yrange = range.getY(); final int zrange = range.getZ(); final int xprange = upperRange.getX(); final int yprange = upperRange.getY(); final int zprange = upperRange.getZ(); //saving outer scope final LazyValue _x = c.getVariable("_x"); final LazyValue _y = c.getVariable("_y"); final LazyValue _z = c.getVariable("_z"); final LazyValue __ = c.getVariable("_"); int sCount = 0; outer: for (int y = cy - yrange; y <= cy + yprange; y++) { final int yFinal = y; c.setVariable("_y", (c_, t_) -> new NumericValue(yFinal).bindTo("_y")); for (int x = cx - xrange; x <= cx + xprange; x++) { final int xFinal = x; c.setVariable("_x", (c_, t_) -> new NumericValue(xFinal).bindTo("_x")); for (int z = cz - zrange; z <= cz + zprange; z++) { final int zFinal = z; c.setVariable("_z", (c_, t_) -> new NumericValue(zFinal).bindTo("_z")); final 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 (final ContinueStatement notIgnored) { result = notIgnored.retval; } catch (final 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("_", __); final int finalSCount = sCount; return (c_, t_) -> new NumericValue(finalSCount); }); // must be lazy expression.addLazyFunction("volume", (c, t, llv) -> { final CarpetContext cc = (CarpetContext) c; if (llv.size() < 3) { throw new InternalExpressionException("'volume' needs many more arguments"); } final List lv = Fluff.AbstractFunction.unpackLazy(llv.subList(0, llv.size() - 1), c, Context.NONE); final BlockArgument pos1Locator = BlockArgument.findIn(cc, lv, 0); final BlockArgument pos2Locator = BlockArgument.findIn(cc, lv, pos1Locator.offset); final BlockPos pos1 = pos1Locator.block.getPos(); final BlockPos pos2 = pos2Locator.block.getPos(); final int x1 = pos1.getX(); final int y1 = pos1.getY(); final int z1 = pos1.getZ(); final int x2 = pos2.getX(); final int y2 = pos2.getY(); final int z2 = pos2.getZ(); final int minx = min(x1, x2); final int miny = min(y1, y2); final int minz = min(z1, z2); final int maxx = max(x1, x2); final int maxy = max(y1, y2); final int maxz = max(z1, z2); final LazyValue expr = llv.get(pos2Locator.offset); //saving outer scope final LazyValue _x = c.getVariable("_x"); final LazyValue _y = c.getVariable("_y"); final LazyValue _z = c.getVariable("_z"); final LazyValue __ = c.getVariable("_"); int sCount = 0; outer: for (int y = miny; y <= maxy; y++) { final int yFinal = y; c.setVariable("_y", (c_, t_) -> new NumericValue(yFinal).bindTo("_y")); for (int x = minx; x <= maxx; x++) { final int xFinal = x; c.setVariable("_x", (c_, t_) -> new NumericValue(xFinal).bindTo("_x")); for (int z = minz; z <= maxz; z++) { final int zFinal = z; c.setVariable("_z", (c_, t_) -> new NumericValue(zFinal).bindTo("_z")); final 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 (final ContinueStatement notIgnored) { result = notIgnored.retval; } catch (final 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("_", __); final int finalSCount = sCount; return (c_, t_) -> new NumericValue(finalSCount); }); expression.addContextFunction("neighbours", -1, (c, t, lv) -> { final BlockPos center = BlockArgument.findIn((CarpetContext) c, lv, 0).block.getPos(); final ServerLevel world = ((CarpetContext) c).level(); final List neighbours = new ArrayList<>(); neighbours.add(new BlockValue(null, world, center.above())); neighbours.add(new BlockValue(null, world, center.below())); 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())); return ListValue.wrap(neighbours); }); expression.addContextFunction("rect", -1, (c, t, lv) -> { final CarpetContext cc = (CarpetContext) c; final int cx; final int cy; final int cz; final int sminx; final int sminy; final int sminz; final int smaxx; final int smaxy; final int smaxz; final BlockArgument cposLocator = BlockArgument.findIn(cc, lv, 0); final BlockPos cpos = cposLocator.block.getPos(); cx = cpos.getX(); cy = cpos.getY(); cz = cpos.getZ(); if (lv.size() > cposLocator.offset) { final Vector3Argument diffLocator = Vector3Argument.findIn(lv, cposLocator.offset); if (diffLocator.fromBlock) { sminx = Mth.floor(abs(diffLocator.vec.x - cx)); sminy = Mth.floor(abs(diffLocator.vec.y - cx)); sminz = Mth.floor(abs(diffLocator.vec.z - cx)); } else { sminx = Mth.floor(abs(diffLocator.vec.x)); sminy = Mth.floor(abs(diffLocator.vec.y)); sminz = Mth.floor(abs(diffLocator.vec.z)); } if (lv.size() > diffLocator.offset) { final Vector3Argument posDiff = Vector3Argument.findIn(lv, diffLocator.offset); if (posDiff.fromBlock) { smaxx = Mth.floor(abs(posDiff.vec.x - cx)); smaxy = Mth.floor(abs(posDiff.vec.y - cx)); smaxz = Mth.floor(abs(posDiff.vec.z - cx)); } else { smaxx = Mth.floor(abs(posDiff.vec.x)); smaxy = Mth.floor(abs(posDiff.vec.y)); smaxz = Mth.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 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() { final 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.addContextFunction("diamond", -1, (c, t, lv) -> { final CarpetContext cc = (CarpetContext) c; final BlockArgument cposLocator = BlockArgument.findIn((CarpetContext) c, lv, 0); final BlockPos cpos = cposLocator.block.getPos(); final int cx; final int cy; final int cz; final int width; final int height; try { cx = cpos.getX(); cy = cpos.getY(); cz = cpos.getZ(); if (lv.size() == cposLocator.offset) { return 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) ); } else if (lv.size() == 1 + cposLocator.offset) { width = (int) ((NumericValue) lv.get(cposLocator.offset)).getLong(); height = 0; } else if (lv.size() == 2 + cposLocator.offset) { width = (int) ((NumericValue) lv.get(cposLocator.offset)).getLong(); height = (int) ((NumericValue) lv.get(cposLocator.offset + 1)).getLong(); } else { throw new InternalExpressionException("Incorrect number of arguments for 'diamond'"); } } catch (final ClassCastException ignored) { throw new InternalExpressionException("Attempted to pass a non-number to 'diamond'"); } if (height == 0) { return 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 final 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 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 final 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; } } 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); } }; } }); } }