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