001/*
002 * This file is part of Baritone.
003 *
004 * Baritone is free software: you can redistribute it and/or modify
005 * it under the terms of the GNU Lesser General Public License as published by
006 * the Free Software Foundation, either version 3 of the License, or
007 * (at your option) any later version.
008 *
009 * Baritone is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012 * GNU Lesser General Public License for more details.
013 *
014 * You should have received a copy of the GNU Lesser General Public License
015 * along with Baritone.  If not, see <https://www.gnu.org/licenses/>.
016 */
017
018package baritone.api.schematic;
019
020import net.minecraft.world.level.block.state.BlockState;
021import net.minecraft.world.level.block.Rotation;
022
023import java.util.List;
024import java.util.stream.Collectors;
025
026public class RotatedSchematic implements ISchematic {
027
028    private final ISchematic schematic;
029    private final Rotation rotation;
030    private final Rotation inverseRotation;
031
032    public RotatedSchematic(ISchematic schematic, Rotation rotation) {
033        this.schematic = schematic;
034        this.rotation = rotation;
035        // I don't think a 14 line switch would improve readability
036        this.inverseRotation = rotation.getRotated(rotation).getRotated(rotation);
037    }
038
039    @Override
040    public boolean inSchematic(int x, int y, int z, BlockState currentState) {
041        return schematic.inSchematic(
042                rotateX(x, z, widthX(), lengthZ(), inverseRotation),
043                y,
044                rotateZ(x, z, widthX(), lengthZ(), inverseRotation),
045                rotate(currentState, inverseRotation)
046        );
047    }
048
049    @Override
050    public BlockState desiredState(int x, int y, int z, BlockState current, List<BlockState> approxPlaceable) {
051        return rotate(schematic.desiredState(
052                rotateX(x, z, widthX(), lengthZ(), inverseRotation),
053                y,
054                rotateZ(x, z, widthX(), lengthZ(), inverseRotation),
055                rotate(current, inverseRotation),
056                rotate(approxPlaceable, inverseRotation)
057        ), rotation);
058    }
059
060    @Override
061    public void reset() {
062        schematic.reset();
063    }
064
065    @Override
066    public int widthX() {
067        return flipsCoordinates(rotation) ? schematic.lengthZ() : schematic.widthX();
068    }
069
070    @Override
071    public int heightY() {
072        return schematic.heightY();
073    }
074
075    @Override
076    public int lengthZ() {
077        return flipsCoordinates(rotation) ? schematic.widthX() : schematic.lengthZ();
078    }
079
080    /**
081     * Wether {@code rotation} swaps the x and z components
082     */
083    private static boolean flipsCoordinates(Rotation rotation) {
084        return rotation == Rotation.CLOCKWISE_90 || rotation == Rotation.COUNTERCLOCKWISE_90;
085    }
086
087    /**
088     * The x component of x,y after applying the rotation
089     */
090    private static int rotateX(int x, int z, int sizeX, int sizeZ, Rotation rotation) {
091        switch (rotation) {
092            case NONE:
093                return x;
094            case CLOCKWISE_90:
095                return sizeZ - z - 1;
096            case CLOCKWISE_180:
097                return sizeX - x - 1;
098            case COUNTERCLOCKWISE_90:
099                return z;
100        }
101        throw new IllegalArgumentException("Unknown rotation");
102    }
103
104    /**
105     * The z component of x,y after applying the rotation
106     */
107    private static int rotateZ(int x, int z, int sizeX, int sizeZ, Rotation rotation) {
108        switch (rotation) {
109            case NONE:
110                return z;
111            case CLOCKWISE_90:
112                return x;
113            case CLOCKWISE_180:
114                return sizeZ - z - 1;
115            case COUNTERCLOCKWISE_90:
116                return sizeX - x - 1;
117        }
118        throw new IllegalArgumentException("Unknown rotation");
119    }
120
121    private static BlockState rotate(BlockState state, Rotation rotation) {
122        if (state == null) {
123            return null;
124        }
125        return state.rotate(rotation);
126    }
127
128    private static List<BlockState> rotate(List<BlockState> states, Rotation rotation) {
129        if (states == null) {
130            return null;
131        }
132        return states.stream()
133                .map(s -> rotate(s, rotation))
134                .collect(Collectors.toList());
135    }
136}