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}