/* * Copyright 2013 MovingBlocks * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.terasology.world.block.family; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import gnu.trove.iterator.TByteObjectIterator; import gnu.trove.map.TByteObjectMap; import gnu.trove.map.hash.TByteObjectHashMap; import org.terasology.math.Rotation; import org.terasology.math.Side; import org.terasology.math.SideBitFlag; import org.terasology.naming.Name; import org.terasology.world.block.Block; import org.terasology.world.block.BlockBuilderHelper; import org.terasology.world.block.BlockUri; import org.terasology.world.block.loader.BlockFamilyDefinition; import java.util.Collection; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; public abstract class UpdatesWithNeighboursFamilyFactory implements BlockFamilyFactory { public static final String NO_CONNECTIONS = "no_connections"; public static final String ONE_CONNECTION = "one_connection"; public static final String TWO_CONNECTIONS_LINE = "line_connection"; public static final String TWO_CONNECTIONS_CORNER = "2d_corner"; public static final String THREE_CONNECTIONS_CORNER = "3d_corner"; public static final String THREE_CONNECTIONS_T = "2d_t"; public static final String FOUR_CONNECTIONS_CROSS = "cross"; public static final String FOUR_CONNECTIONS_SIDE = "3d_side"; public static final String FIVE_CONNECTIONS = "five_connections"; public static final String SIX_CONNECTIONS = "all"; private static final ImmutableSet<String> BLOCK_NAMES = ImmutableSet.of( NO_CONNECTIONS, ONE_CONNECTION, TWO_CONNECTIONS_LINE, TWO_CONNECTIONS_CORNER, THREE_CONNECTIONS_CORNER, THREE_CONNECTIONS_T, FOUR_CONNECTIONS_CROSS, FOUR_CONNECTIONS_SIDE, FIVE_CONNECTIONS, SIX_CONNECTIONS); private static final Map<String, Byte> DEFAULT_SHAPE_MAPPING = ImmutableMap.<String, Byte>builder() .put(NO_CONNECTIONS, (byte) 0) .put(ONE_CONNECTION, SideBitFlag.getSides(Side.BACK)) .put(TWO_CONNECTIONS_LINE, SideBitFlag.getSides(Side.BACK, Side.FRONT)) .put(TWO_CONNECTIONS_CORNER, SideBitFlag.getSides(Side.LEFT, Side.BACK)) .put(THREE_CONNECTIONS_CORNER, SideBitFlag.getSides(Side.LEFT, Side.BACK, Side.TOP)) .put(THREE_CONNECTIONS_T, SideBitFlag.getSides(Side.LEFT, Side.BACK, Side.FRONT)) .put(FOUR_CONNECTIONS_CROSS, SideBitFlag.getSides(Side.RIGHT, Side.LEFT, Side.BACK, Side.FRONT)) .put(FOUR_CONNECTIONS_SIDE, SideBitFlag.getSides(Side.LEFT, Side.BACK, Side.FRONT, Side.TOP)) .put(FIVE_CONNECTIONS, SideBitFlag.getSides(Side.LEFT, Side.BACK, Side.FRONT, Side.TOP, Side.BOTTOM)) .put(SIX_CONNECTIONS, (byte) 63) .build(); private ConnectionCondition connectionCondition; private byte connectionSides; private boolean horizontalOnly; private Map<String, Byte> shapeMapping; protected UpdatesWithNeighboursFamilyFactory(ConnectionCondition connectionCondition, byte connectionSides) { this(connectionCondition, connectionSides, DEFAULT_SHAPE_MAPPING, false); } protected UpdatesWithNeighboursFamilyFactory(ConnectionCondition connectionCondition, byte connectionSides, Map<String, Byte> shapeMapping, boolean horizontalOnly) { this.connectionCondition = connectionCondition; this.connectionSides = connectionSides; this.shapeMapping = shapeMapping; this.horizontalOnly = horizontalOnly; } @Override public BlockFamily createBlockFamily(BlockFamilyDefinition definition, BlockBuilderHelper blockBuilder) { TByteObjectMap<String>[] basicBlocks = new TByteObjectMap[7]; TByteObjectMap<Block> blocksForConnections = new TByteObjectHashMap<>(); addConnections(basicBlocks, 0, NO_CONNECTIONS); addConnections(basicBlocks, 1, ONE_CONNECTION); addConnections(basicBlocks, 2, TWO_CONNECTIONS_LINE); addConnections(basicBlocks, 2, TWO_CONNECTIONS_CORNER); addConnections(basicBlocks, 3, THREE_CONNECTIONS_CORNER); addConnections(basicBlocks, 3, THREE_CONNECTIONS_T); addConnections(basicBlocks, 4, FOUR_CONNECTIONS_CROSS); addConnections(basicBlocks, 4, FOUR_CONNECTIONS_SIDE); addConnections(basicBlocks, 5, FIVE_CONNECTIONS); addConnections(basicBlocks, 6, SIX_CONNECTIONS); BlockUri blockUri = new BlockUri(definition.getUrn()); // Now make sure we have all combinations based on the basic set (above) and rotations for (byte connections = 0; connections < 64; connections++) { // Only the allowed connections should be created if ((connections & connectionSides) == connections) { Block block = constructBlockForConnections(connections, blockBuilder, definition, basicBlocks); if (block == null) { throw new IllegalStateException("Unable to find correct block definition for connections: " + connections); } block.setUri(new BlockUri(blockUri, new Name(String.valueOf(connections)))); blocksForConnections.put(connections, block); } } final Block archetypeBlock = blocksForConnections.get(SideBitFlag.getSides(Side.RIGHT, Side.LEFT)); return new UpdatesWithNeighboursFamily(connectionCondition, blockUri, definition.getCategories(), archetypeBlock, blocksForConnections, connectionSides); } private void addConnections(TByteObjectMap<String>[] basicBlocks, int index, String connections) { if (basicBlocks[index] == null) { basicBlocks[index] = new TByteObjectHashMap<>(); } Byte val = shapeMapping.get(connections); if (val != null) { basicBlocks[index].put(shapeMapping.get(connections), connections); } } private Block constructBlockForConnections(final byte connections, final BlockBuilderHelper blockBuilder, BlockFamilyDefinition definition, TByteObjectMap<String>[] basicBlocks) { int connectionCount = SideBitFlag.getSides(connections).size(); TByteObjectMap<String> possibleBlockDefinitions = basicBlocks[connectionCount]; final TByteObjectIterator<String> blockDefinitionIterator = possibleBlockDefinitions.iterator(); while (blockDefinitionIterator.hasNext()) { blockDefinitionIterator.advance(); final byte originalConnections = blockDefinitionIterator.key(); final String section = blockDefinitionIterator.value(); Rotation rot = getRotationToAchieve(originalConnections, connections); if (rot != null) { return blockBuilder.constructTransformedBlock(definition, section, rot); } } return null; } private Rotation getRotationToAchieve(byte source, byte target) { Collection<Side> originalSides = SideBitFlag.getSides(source); Iterable<Rotation> rotations = horizontalOnly ? Rotation.horizontalRotations() : Rotation.values(); for (Rotation rot : rotations) { Set<Side> transformedSides = Sets.newHashSet(); transformedSides.addAll(originalSides.stream().map(rot::rotate).collect(Collectors.toList())); byte transformedSide = SideBitFlag.getSides(transformedSides); if (transformedSide == target) { return rot; } } return null; } @Override public Set<String> getSectionNames() { return BLOCK_NAMES; } }