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.AirBlock;
021import net.minecraft.world.level.block.Block;
022import net.minecraft.world.level.block.Blocks;
023import net.minecraft.world.level.block.state.BlockState;
024import net.minecraft.world.level.block.state.properties.Property;
025
026import java.util.Collection;
027import java.util.HashMap;
028import java.util.List;
029import java.util.Map;
030
031public class SubstituteSchematic extends AbstractSchematic {
032
033    private final ISchematic schematic;
034    private final Map<Block, List<Block>> substitutions;
035    private final Map<BlockState, Map<Block, BlockState>> blockStateCache = new HashMap<>();
036
037    public SubstituteSchematic(ISchematic schematic, Map<Block, List<Block>> substitutions) {
038        super(schematic.widthX(), schematic.heightY(), schematic.lengthZ());
039        this.schematic = schematic;
040        this.substitutions = substitutions;
041    }
042
043    @Override
044    public boolean inSchematic(int x, int y, int z, BlockState currentState) {
045        return schematic.inSchematic(x, y, z, currentState);
046    }
047
048    @Override
049    public BlockState desiredState(int x, int y, int z, BlockState current, List<BlockState> approxPlaceable) {
050        BlockState desired = schematic.desiredState(x, y, z, current, approxPlaceable);
051        Block desiredBlock = desired.getBlock();
052        if (!substitutions.containsKey(desiredBlock)) {
053            return desired;
054        }
055        List<Block> substitutes = substitutions.get(desiredBlock);
056        if (substitutes.contains(current.getBlock()) && !(current.getBlock() instanceof AirBlock)) {// don't preserve air, it's almost always there and almost never wanted
057            return withBlock(desired, current.getBlock());
058        }
059        for (Block substitute : substitutes) {
060            if (substitute instanceof AirBlock) {
061                return current.getBlock() instanceof AirBlock ? current : Blocks.AIR.defaultBlockState(); // can always "place" air
062            }
063            for (BlockState placeable : approxPlaceable) {
064                if (substitute.equals(placeable.getBlock())) {
065                    return withBlock(desired, placeable.getBlock());
066                }
067            }
068        }
069        return substitutes.get(0).defaultBlockState();
070    }
071
072    private BlockState withBlock(BlockState state, Block block) {
073        if (blockStateCache.containsKey(state) && blockStateCache.get(state).containsKey(block)) {
074            return blockStateCache.get(state).get(block);
075        }
076        Collection<Property<?>> properties = state.getProperties();
077        BlockState newState = block.defaultBlockState();
078        for (Property<?> property : properties) {
079            try {
080                newState = copySingleProp(state, newState, property);
081            } catch (IllegalArgumentException e) { //property does not exist for target block
082            }
083        }
084        blockStateCache.computeIfAbsent(state, s -> new HashMap<Block, BlockState>()).put(block, newState);
085        return newState;
086    }
087
088    private <T extends Comparable<T>> BlockState copySingleProp(BlockState fromState, BlockState toState, Property<T> prop) {
089        return toState.setValue(prop, fromState.getValue(prop));
090    }
091}