diff --git a/src/api/java/baritone/api/Settings.java b/src/api/java/baritone/api/Settings.java index 1812fe486..d9cb501ef 100644 --- a/src/api/java/baritone/api/Settings.java +++ b/src/api/java/baritone/api/Settings.java @@ -29,6 +29,9 @@ import net.minecraft.network.chat.Component; import net.minecraft.world.item.Item; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -1089,6 +1092,28 @@ public final class Settings { */ public final Setting schematicOrientationZ = new Setting<>(false); + /** + * Rotates the schematic before building it. + * Possible values are + * + */ + public final Setting buildSchematicRotation = new Setting<>(Rotation.NONE); + + /** + * Mirrors the schematic before building it. + * Possible values are + * + */ + public final Setting buildSchematicMirror = new Setting<>(Mirror.NONE); + /** * The fallback used by the build command when no extension is specified. This may be useful if schematics of a * particular format are used often, and the user does not wish to have to specify the extension with every usage. diff --git a/src/api/java/baritone/api/schematic/MirroredSchematic.java b/src/api/java/baritone/api/schematic/MirroredSchematic.java new file mode 100644 index 000000000..b9e10808c --- /dev/null +++ b/src/api/java/baritone/api/schematic/MirroredSchematic.java @@ -0,0 +1,114 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.schematic; + +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.Mirror; + +import java.util.List; +import java.util.stream.Collectors; + +public class MirroredSchematic implements ISchematic { + + private final ISchematic schematic; + private final Mirror mirror; + + public MirroredSchematic(ISchematic schematic, Mirror mirror) { + this.schematic = schematic; + this.mirror = mirror; + } + + @Override + public boolean inSchematic(int x, int y, int z, BlockState currentState) { + return schematic.inSchematic( + mirrorX(x, widthX(), mirror), + y, + mirrorZ(z, lengthZ(), mirror), + mirror(currentState, mirror) + ); + } + + @Override + public BlockState desiredState(int x, int y, int z, BlockState current, List approxPlaceable) { + return mirror(schematic.desiredState( + mirrorX(x, widthX(), mirror), + y, + mirrorZ(z, lengthZ(), mirror), + mirror(current, mirror), + mirror(approxPlaceable, mirror) + ), mirror); + } + + @Override + public void reset() { + schematic.reset(); + } + + @Override + public int widthX() { + return schematic.widthX(); + } + + @Override + public int heightY() { + return schematic.heightY(); + } + + @Override + public int lengthZ() { + return schematic.lengthZ(); + } + + private static int mirrorX(int x, int sizeX, Mirror mirror) { + switch (mirror) { + case NONE: + case LEFT_RIGHT: + return x; + case FRONT_BACK: + return sizeX - x - 1; + } + throw new IllegalArgumentException("Unknown mirror"); + } + + private static int mirrorZ(int z, int sizeZ, Mirror mirror) { + switch (mirror) { + case NONE: + case FRONT_BACK: + return z; + case LEFT_RIGHT: + return sizeZ - z - 1; + } + throw new IllegalArgumentException("Unknown mirror"); + } + + private static BlockState mirror(BlockState state, Mirror mirror) { + if (state == null) { + return null; + } + return state.mirror(mirror); + } + + private static List mirror(List states, Mirror mirror) { + if (states == null) { + return null; + } + return states.stream() + .map(s -> mirror(s, mirror)) + .collect(Collectors.toList()); + } +} diff --git a/src/api/java/baritone/api/schematic/RotatedSchematic.java b/src/api/java/baritone/api/schematic/RotatedSchematic.java new file mode 100644 index 000000000..e3c9ed7aa --- /dev/null +++ b/src/api/java/baritone/api/schematic/RotatedSchematic.java @@ -0,0 +1,136 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.schematic; + +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.Rotation; + +import java.util.List; +import java.util.stream.Collectors; + +public class RotatedSchematic implements ISchematic { + + private final ISchematic schematic; + private final Rotation rotation; + private final Rotation inverseRotation; + + public RotatedSchematic(ISchematic schematic, Rotation rotation) { + this.schematic = schematic; + this.rotation = rotation; + // I don't think a 14 line switch would improve readability + this.inverseRotation = rotation.getRotated(rotation).getRotated(rotation); + } + + @Override + public boolean inSchematic(int x, int y, int z, BlockState currentState) { + return schematic.inSchematic( + rotateX(x, z, widthX(), lengthZ(), inverseRotation), + y, + rotateZ(x, z, widthX(), lengthZ(), inverseRotation), + rotate(currentState, inverseRotation) + ); + } + + @Override + public BlockState desiredState(int x, int y, int z, BlockState current, List approxPlaceable) { + return rotate(schematic.desiredState( + rotateX(x, z, widthX(), lengthZ(), inverseRotation), + y, + rotateZ(x, z, widthX(), lengthZ(), inverseRotation), + rotate(current, inverseRotation), + rotate(approxPlaceable, inverseRotation) + ), rotation); + } + + @Override + public void reset() { + schematic.reset(); + } + + @Override + public int widthX() { + return flipsCoordinates(rotation) ? schematic.lengthZ() : schematic.widthX(); + } + + @Override + public int heightY() { + return schematic.heightY(); + } + + @Override + public int lengthZ() { + return flipsCoordinates(rotation) ? schematic.widthX() : schematic.lengthZ(); + } + + /** + * Wether {@code rotation} swaps the x and z components + */ + private static boolean flipsCoordinates(Rotation rotation) { + return rotation == Rotation.CLOCKWISE_90 || rotation == Rotation.COUNTERCLOCKWISE_90; + } + + /** + * The x component of x,y after applying the rotation + */ + private static int rotateX(int x, int z, int sizeX, int sizeZ, Rotation rotation) { + switch (rotation) { + case NONE: + return x; + case CLOCKWISE_90: + return sizeZ - z - 1; + case CLOCKWISE_180: + return sizeX - x - 1; + case COUNTERCLOCKWISE_90: + return z; + } + throw new IllegalArgumentException("Unknown rotation"); + } + + /** + * The z component of x,y after applying the rotation + */ + private static int rotateZ(int x, int z, int sizeX, int sizeZ, Rotation rotation) { + switch (rotation) { + case NONE: + return z; + case CLOCKWISE_90: + return x; + case CLOCKWISE_180: + return sizeZ - z - 1; + case COUNTERCLOCKWISE_90: + return sizeX - x - 1; + } + throw new IllegalArgumentException("Unknown rotation"); + } + + private static BlockState rotate(BlockState state, Rotation rotation) { + if (state == null) { + return null; + } + return state.rotate(rotation); + } + + private static List rotate(List states, Rotation rotation) { + if (states == null) { + return null; + } + return states.stream() + .map(s -> rotate(s, rotation)) + .collect(Collectors.toList()); + } +} diff --git a/src/api/java/baritone/api/utils/SettingsUtil.java b/src/api/java/baritone/api/utils/SettingsUtil.java index 23c2acddb..7a18b61c3 100644 --- a/src/api/java/baritone/api/utils/SettingsUtil.java +++ b/src/api/java/baritone/api/utils/SettingsUtil.java @@ -27,6 +27,9 @@ import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; + import java.awt.*; import java.io.BufferedReader; import java.io.BufferedWriter; @@ -220,7 +223,8 @@ public class SettingsUtil { FLOAT(Float.class, Float::parseFloat), LONG(Long.class, Long::parseLong), STRING(String.class, String::new), - DIRECTION(Direction.class, Direction::byName), + MIRROR(Mirror.class, Mirror::valueOf, Mirror::name), + ROTATION(Rotation.class, Rotation::valueOf, Rotation::name), COLOR( Color.class, str -> new Color(Integer.parseInt(str.split(",")[0]), Integer.parseInt(str.split(",")[1]), Integer.parseInt(str.split(",")[2])), diff --git a/src/main/java/baritone/process/BuilderProcess.java b/src/main/java/baritone/process/BuilderProcess.java index 33f3db3f1..cb6eb9d8a 100644 --- a/src/main/java/baritone/process/BuilderProcess.java +++ b/src/main/java/baritone/process/BuilderProcess.java @@ -30,6 +30,8 @@ import baritone.api.schematic.ISchematic; import baritone.api.schematic.IStaticSchematic; import baritone.api.schematic.MaskSchematic; import baritone.api.schematic.SubstituteSchematic; +import baritone.api.schematic.RotatedSchematic; +import baritone.api.schematic.MirroredSchematic; import baritone.api.schematic.format.ISchematicFormat; import baritone.api.utils.*; import baritone.api.utils.input.Input; @@ -118,6 +120,12 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil if (!Baritone.settings().buildSubstitutes.value.isEmpty()) { this.schematic = new SubstituteSchematic(this.schematic, Baritone.settings().buildSubstitutes.value); } + if (Baritone.settings().buildSchematicMirror.value != net.minecraft.world.level.block.Mirror.NONE) { + this.schematic = new MirroredSchematic(this.schematic, Baritone.settings().buildSchematicMirror.value); + } + if (Baritone.settings().buildSchematicRotation.value != net.minecraft.world.level.block.Rotation.NONE) { + this.schematic = new RotatedSchematic(this.schematic, Baritone.settings().buildSchematicRotation.value); + } // TODO this preserves the old behavior, but maybe we should bake the setting value right here this.schematic = new MaskSchematic(this.schematic) { @Override