/* * This file is part of LanternServer, licensed under the MIT License (MIT). * * Copyright (c) LanternPowered <https://www.lanternpowered.org> * Copyright (c) SpongePowered <https://www.spongepowered.org> * Copyright (c) contributors * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the Software), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package org.lanternpowered.server.game.registry.type.block; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import static org.lanternpowered.server.block.PropertyProviderCollections.INSTANT_BROKEN; import static org.lanternpowered.server.block.PropertyProviderCollections.PASSABLE; import static org.lanternpowered.server.block.PropertyProviders.blastResistance; import static org.lanternpowered.server.block.PropertyProviders.flammableInfo; import static org.lanternpowered.server.block.PropertyProviders.hardness; import static org.lanternpowered.server.block.PropertyProviders.lightEmission; import static org.lanternpowered.server.block.PropertyProviders.replaceable; import static org.lanternpowered.server.item.PropertyProviders.equipmentType; import static org.lanternpowered.server.text.translation.TranslationHelper.tr; import it.unimi.dsi.fastutil.bytes.Byte2ObjectMap; import it.unimi.dsi.fastutil.bytes.Byte2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ShortMap; import it.unimi.dsi.fastutil.objects.Object2ShortOpenHashMap; import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap; import org.lanternpowered.server.block.BlockTypeBuilder; import org.lanternpowered.server.block.BlockTypeBuilderImpl; import org.lanternpowered.server.block.LanternBlockType; import org.lanternpowered.server.block.PropertyProviderCollections; import org.lanternpowered.server.block.TranslationProvider; import org.lanternpowered.server.block.aabb.BoundingBoxes; import org.lanternpowered.server.block.behavior.simple.BlockSnapshotProviderPlaceBehavior; import org.lanternpowered.server.block.behavior.simple.SimpleBlockDropsProviderBehavior; import org.lanternpowered.server.block.behavior.simple.SimpleBreakBehavior; import org.lanternpowered.server.block.behavior.simple.SimplePlacementBehavior; import org.lanternpowered.server.block.behavior.vanilla.ChestInteractionBehavior; import org.lanternpowered.server.block.behavior.vanilla.ChestPlacementBehavior; import org.lanternpowered.server.block.behavior.vanilla.EnderChestInteractionBehavior; import org.lanternpowered.server.block.behavior.vanilla.HopperPlacementBehavior; import org.lanternpowered.server.block.behavior.vanilla.HorizontalRotationPlacementBehavior; import org.lanternpowered.server.block.behavior.vanilla.LogAxisRotationPlacementBehavior; import org.lanternpowered.server.block.behavior.vanilla.OpeneableContainerInteractionBehavior; import org.lanternpowered.server.block.behavior.vanilla.OppositeFaceDirectionalPlacementBehavior; import org.lanternpowered.server.block.behavior.vanilla.QuartzLinesRotationPlacementBehavior; import org.lanternpowered.server.block.behavior.vanilla.RotationPlacementBehavior; import org.lanternpowered.server.block.behavior.vanilla.TorchPlacementBehavior; import org.lanternpowered.server.block.extended.SnowyExtendedBlockStateProvider; import org.lanternpowered.server.block.state.LanternBlockState; import org.lanternpowered.server.block.trait.LanternBooleanTraits; import org.lanternpowered.server.block.trait.LanternEnumTraits; import org.lanternpowered.server.block.trait.LanternIntegerTraits; import org.lanternpowered.server.block.translation.SpongeTranslationProvider; import org.lanternpowered.server.data.key.LanternKeys; import org.lanternpowered.server.data.property.LanternPropertyRegistry; import org.lanternpowered.server.data.type.LanternBedPart; import org.lanternpowered.server.data.type.LanternDirtType; import org.lanternpowered.server.data.type.LanternDyeColor; import org.lanternpowered.server.data.type.LanternPlantType; import org.lanternpowered.server.data.type.LanternPortionType; import org.lanternpowered.server.data.type.LanternQuartzType; import org.lanternpowered.server.data.type.LanternRailDirection; import org.lanternpowered.server.data.type.LanternSandType; import org.lanternpowered.server.data.type.LanternSandstoneType; import org.lanternpowered.server.data.type.LanternShrubType; import org.lanternpowered.server.data.type.LanternSlabType; import org.lanternpowered.server.data.type.LanternStoneType; import org.lanternpowered.server.data.type.LanternTreeType; import org.lanternpowered.server.game.Lantern; import org.lanternpowered.server.game.registry.AdditionalPluginCatalogRegistryModule; import org.lanternpowered.server.game.registry.type.data.KeyRegistryModule; import org.lanternpowered.server.game.registry.type.item.ItemRegistryModule; import org.lanternpowered.server.game.registry.type.item.inventory.equipment.EquipmentTypeRegistryModule; import org.lanternpowered.server.inventory.InventorySnapshot; import org.lanternpowered.server.item.behavior.vanilla.SlabItemInteractionBehavior; import org.spongepowered.api.block.BlockState; import org.spongepowered.api.block.BlockType; import org.spongepowered.api.block.BlockTypes; import org.spongepowered.api.block.tileentity.TileEntityTypes; import org.spongepowered.api.block.trait.EnumTrait; import org.spongepowered.api.data.key.Keys; import org.spongepowered.api.data.type.SlabType; import org.spongepowered.api.data.type.TreeType; import org.spongepowered.api.item.inventory.equipment.EquipmentTypes; import org.spongepowered.api.registry.util.RegistrationDependency; import org.spongepowered.api.util.Direction; import java.util.Optional; import java.util.function.Supplier; @RegistrationDependency({ KeyRegistryModule.class, EquipmentTypeRegistryModule.class }) public final class BlockRegistryModule extends AdditionalPluginCatalogRegistryModule<BlockType> implements BlockRegistry { private static final BlockRegistryModule INSTANCE = new BlockRegistryModule(); public static BlockRegistryModule get() { return INSTANCE; } private final Short2ObjectMap<BlockType> blockTypeByInternalId = new Short2ObjectOpenHashMap<>(); private final Object2ShortMap<BlockType> internalIdByBlockType = new Object2ShortOpenHashMap<>(); private final Short2ObjectMap<BlockState> blockStateByPackedType = new Short2ObjectOpenHashMap<>(); private final Object2ShortMap<BlockState> packedTypeByBlockState = new Object2ShortOpenHashMap<>(); // The counter for custom block ids. (Non vanilla ones.) private int blockIdCounter = 1024; public BlockRegistryModule() { super(BlockTypes.class); } @Override public int getBlockStatesCount() { return this.blockStateByPackedType.size(); } private void register0(int internalId, LanternBlockType blockType, BlockState2DataFunction stateToDataConverter) { checkNotNull(stateToDataConverter, "stateToDataConverter"); checkState(internalId >= 0, "The internal id cannot be negative: %s", internalId); checkState(internalId <= 0xfff, "The internal id exceeded the internal id limit: %s > %s", internalId, 0xfff); final short internalId0 = (short) internalId; checkState(!this.blockTypeByInternalId.containsKey(internalId0), "The internal id is already used: %s", internalId); super.register(blockType); this.blockTypeByInternalId.put(internalId0, blockType); this.internalIdByBlockType.put(blockType, internalId0); Byte2ObjectMap<BlockState> usedValues = new Byte2ObjectOpenHashMap<>(); int internalStateIdBase = (internalId & 0xfff) << 4; for (BlockState blockState : blockType.getBlockStateBase().getBlockStates()) { if (((LanternBlockState) blockState).isExtended()) { continue; } byte value = checkNotNull(stateToDataConverter.apply(blockState)); if (usedValues.containsKey(value)) { throw new IllegalStateException("The data value " + value + " for state '" + blockState.getId() + "' is already used by '" + usedValues.get(value).getId() + "'"); } usedValues.put(value, blockState); final short internalStateId = (short) (internalStateIdBase | value & 0xf); this.blockStateByPackedType.put(internalStateId, blockState); this.packedTypeByBlockState.put(blockState, internalStateId); } final BlockState defaultBlockState = blockType.getDefaultState(); for (byte b = 0; b <= 0xf; b++) { if (!usedValues.containsKey(b)) { final short internalStateId = (short) (internalStateIdBase | b & 0xf); this.blockStateByPackedType.put(internalStateId, defaultBlockState); } } for (BlockState blockState : blockType.getBlockStateBase().getBlockStates()) { if (!((LanternBlockState) blockState).isExtended()) { continue; } blockState = blockType.getExtendedBlockStateProvider().remove(blockState); this.packedTypeByBlockState.put(blockState, checkNotNull(this.packedTypeByBlockState.get(blockState))); } final BlockStateRegistryModule blockStateRegistryModule = Lantern.getRegistry() .getRegistryModule(BlockStateRegistryModule.class).get(); blockType.getAllStates().forEach(blockStateRegistryModule::registerState); blockType.getItem().ifPresent(itemType -> ItemRegistryModule.get().register(internalId, itemType)); LanternPropertyRegistry.getInstance().registerBlockPropertyStores(blockType.getPropertyProviderCollection()); } @Override public void register(int internalId, BlockType blockType, BlockState2DataFunction stateToDataConverter) { register0(internalId, (LanternBlockType) blockType, stateToDataConverter); } @Override public void register(int internalId, BlockType blockType) { LanternBlockType blockType0 = (LanternBlockType) checkNotNull(blockType, "blockType"); checkState(blockType0.getBlockStateBase().getBlockStates().stream() .filter(s -> !((LanternBlockState) s).isExtended()).count() <= 1, "You cannot register a blockType with more then one state with this method."); register0(internalId, blockType0, blockState -> (byte) 0); } @Override public void register(BlockType blockType, BlockState2DataFunction stateToDataConverter) { register(this.nextInternalId(), blockType, stateToDataConverter); } @Override public void register(BlockType catalogType) { register(nextInternalId(), catalogType); } private int nextInternalId() { int internalId; do { internalId = this.blockIdCounter++; } while (this.blockTypeByInternalId.containsKey((short) internalId)); return internalId; } @Override public Optional<BlockState> getStateByInternalId(int internalId) { return Optional.ofNullable(this.blockStateByPackedType.get((short) ((internalId & 0xfff) << 4))); } @Override public Optional<BlockState> getStateByInternalIdAndData(int internalId, byte data) { return Optional.ofNullable(this.blockStateByPackedType.get((short) (((internalId & 0xfff) << 4) | (data & 0xf)))); } @Override public Optional<BlockState> getStateByTypeAndData(BlockType blockType, byte data) { return Optional.ofNullable(this.blockStateByPackedType.get( (short) ((this.packedTypeByBlockState.get(blockType.getDefaultState()) & 0xfff0) | (data & 0xf)))); } @Override public Optional<BlockState> getStateByInternalIdAndData(int internalIdAndData) { return Optional.ofNullable(this.blockStateByPackedType.get((short) (internalIdAndData & 0xffff))); } @Override public byte getStateData(BlockState blockState) { return (byte) (this.packedTypeByBlockState.get(checkNotNull(blockState, "blockState")) & 0xf); } @Override public short getStateInternalId(BlockState blockState) { return this.internalIdByBlockType.getShort(checkNotNull(blockState, "blockState").getType()); } @Override public short getStateInternalIdAndData(BlockState blockState) { return this.packedTypeByBlockState.get(checkNotNull(blockState, "blockState")); } @Override public int getPackedVersion(int internalId, byte data) { return ((internalId & 0xfff) << 4) | (data & 0xf); } @Override public void registerDefaults() { // @formatter:off /////////////////// /// Air /// /////////////////// register(0, builder() .properties(PropertyProviderCollections.DEFAULT_GAS) .translation("tile.air.name") .build("minecraft", "air")); /////////////////// /// Stone /// /////////////////// register(1, simpleBuilder() .trait(LanternEnumTraits.STONE_TYPE) .defaultState(state -> state.withTrait(LanternEnumTraits.STONE_TYPE, LanternStoneType.STONE).get()) .itemType(builder -> builder .keysProvider(valueContainer -> valueContainer .registerKey(Keys.STONE_TYPE, LanternStoneType.STONE) ) ) .properties(builder -> builder .add(hardness(1.5)) .add(blastResistance(30.0))) .translation(TranslationProvider.of(LanternEnumTraits.STONE_TYPE)) .build("minecraft", "stone"), blockState -> (byte) blockState.getTraitValue(LanternEnumTraits.STONE_TYPE).get().getInternalId()); /////////////////// /// Grass /// /////////////////// register(2, simpleBuilder() .trait(LanternBooleanTraits.SNOWY) .extendedStateProvider(new SnowyExtendedBlockStateProvider()) .defaultState(state -> state.withTrait(LanternBooleanTraits.SNOWY, false).get()) .itemType() .properties(builder -> builder .add(hardness(0.6)) .add(blastResistance(3.0))) .translation("tile.grass.name") .build("minecraft", "grass")); /////////////////// /// Dirt /// /////////////////// register(3, simpleBuilder() .traits(LanternEnumTraits.DIRT_TYPE, LanternBooleanTraits.SNOWY) .defaultState(state -> state .withTrait(LanternEnumTraits.DIRT_TYPE, LanternDirtType.DIRT).get() .withTrait(LanternBooleanTraits.SNOWY, false).get()) .extendedStateProvider(new SnowyExtendedBlockStateProvider()) .itemType(builder -> builder .keysProvider(valueContainer -> valueContainer .registerKey(Keys.DIRT_TYPE, LanternDirtType.DIRT) ) ) .properties(builder -> builder .add(hardness(0.5)) .add(blastResistance(2.5))) .translation(TranslationProvider.of(LanternEnumTraits.DIRT_TYPE)) .build("minecraft", "dirt"), blockState -> (byte) blockState.getTraitValue(LanternEnumTraits.DIRT_TYPE).get().getInternalId()); /////////////////// /// Cobblestone /// /////////////////// register(4, simpleBuilder() .itemType() .properties(builder -> builder .add(hardness(2.0)) .add(blastResistance(3.0))) .translation("tile.stonebrick.name") .build("minecraft", "cobblestone")); /////////////////// /// Planks /// /////////////////// register(5, simpleBuilder() .trait(LanternEnumTraits.TREE_TYPE) .defaultState(state -> state.withTrait(LanternEnumTraits.TREE_TYPE, LanternTreeType.OAK).get()) .itemType(builder -> builder .keysProvider(valueContainer -> valueContainer .registerKey(Keys.TREE_TYPE, LanternTreeType.OAK) ) ) .properties(builder -> builder .add(hardness(2.0)) .add(blastResistance(5.0)) .add(flammableInfo(5, 20))) .translation(TranslationProvider.of(LanternEnumTraits.TREE_TYPE, type -> tr("tile.planks." + type.getTranslationKeyBase() + ".name"))) .build("minecraft", "planks"), blockState -> (byte) blockState.getTraitValue(LanternEnumTraits.TREE_TYPE).get().getInternalId()); //////////////////// /// Sapling /// //////////////////// register(6, simpleBuilder() .traits(LanternEnumTraits.TREE_TYPE, LanternIntegerTraits.SAPLING_GROWTH_STAGE) .defaultState(state -> state .withTrait(LanternEnumTraits.TREE_TYPE, LanternTreeType.OAK).get() .withTrait(LanternIntegerTraits.SAPLING_GROWTH_STAGE, 0).get()) .itemType(builder -> builder .keysProvider(valueContainer -> valueContainer .registerKey(Keys.TREE_TYPE, LanternTreeType.OAK) ) ) .properties(builder -> builder .add(PASSABLE) .add(INSTANT_BROKEN)) .translation(TranslationProvider.of(LanternEnumTraits.TREE_TYPE, type -> tr("tile.sapling." + type.getTranslationKeyBase() + ".name"))) .build("minecraft", "sapling"), blockState -> { final int type = blockState.getTraitValue(LanternEnumTraits.TREE_TYPE).get().getInternalId(); final int stage = blockState.getTraitValue(LanternIntegerTraits.SAPLING_GROWTH_STAGE).get(); return (byte) (stage << 3 | type); }); //////////////////// /// Bedrock /// //////////////////// register(7, simpleBuilder() .itemType() .properties(builder -> builder .add(PropertyProviderCollections.UNBREAKABLE)) .translation("tile.bedrock.name") .build("minecraft", "bedrock")); //////////////////// /// Sand /// //////////////////// register(12, simpleBuilder() .trait(LanternEnumTraits.SAND_TYPE) .defaultState(state -> state.withTrait(LanternEnumTraits.SAND_TYPE, LanternSandType.NORMAL).get()) .itemType(builder -> builder .keysProvider(valueContainer -> valueContainer .registerKey(Keys.SAND_TYPE, LanternSandType.NORMAL) ) ) .properties(builder -> builder .add(hardness(0.5)) .add(blastResistance(2.5))) .translation(TranslationProvider.of(LanternEnumTraits.SAND_TYPE)) .build("minecraft", "sand"), blockState -> (byte) blockState.getTraitValue(LanternEnumTraits.SAND_TYPE).get().getInternalId()); // TODO: Sand physics behavior //////////////////// /// Gravel /// //////////////////// register(13, simpleBuilder() .itemType() .properties(builder -> builder .add(hardness(0.6)) .add(blastResistance(3.0))) .translation("tile.gravel.name") .build("minecraft", "gravel")); // TODO: Gravel physics behavior //////////////////// /// Gold Ore /// //////////////////// register(14, simpleBuilder() .itemType() .properties(builder -> builder .add(hardness(3.0)) .add(blastResistance(15.0))) .translation("tile.oreGold.name") .build("minecraft", "gold_ore")); //////////////////// /// Iron Ore /// //////////////////// register(15, simpleBuilder() .itemType() .properties(builder -> builder .add(hardness(3.0)) .add(blastResistance(15.0))) .translation("tile.oreIron.name") .build("minecraft", "iron_ore")); //////////////////// /// Coal Ore /// //////////////////// register(16, simpleBuilder() .itemType() .properties(builder -> builder .add(hardness(3.0)) .add(blastResistance(15.0))) .translation("tile.oreCoal.name") .build("minecraft", "coal_ore")); //////////////////// /// Log 1 /// //////////////////// register(17, logBuilder(LanternEnumTraits.LOG1_TYPE, LanternTreeType.OAK) .build("minecraft", "log"), blockState -> logData(blockState, blockState.getTraitValue(LanternEnumTraits.LOG1_TYPE).get().getInternalId())); //////////////////// /// Leaves 1 /// //////////////////// register(18, leavesBuilder(LanternEnumTraits.LEAVES1_TYPE, LanternTreeType.OAK) .build("minecraft", "leaves"), blockState -> leavesData(blockState, blockState.getTraitValue(LanternEnumTraits.LEAVES1_TYPE).get().getInternalId())); //////////////////// /// Sponge /// //////////////////// register(19, simpleBuilder() .trait(LanternBooleanTraits.IS_WET) .defaultState(state -> state.withTrait(LanternBooleanTraits.IS_WET, false).get()) .itemType(builder -> builder .keysProvider(valueContainer -> valueContainer .registerKey(Keys.IS_WET, false) ) ) .properties(builder -> builder .add(hardness(0.6)) .add(blastResistance(3.0))) .translation(new SpongeTranslationProvider()) .build("minecraft", "sponge"), blockState -> (byte) (blockState.getTraitValue(LanternBooleanTraits.IS_WET).get() ? 1 : 0)); //////////////////// /// Glass /// //////////////////// register(20, simpleBuilder() .itemType() .properties(builder -> builder .add(hardness(0.3)) .add(blastResistance(1.5))) .translation("tile.glass.name") .build("minecraft", "glass")); //////////////////// /// Lapis Ore /// //////////////////// register(21, simpleBuilder() .itemType() .properties(builder -> builder .add(hardness(3.0)) .add(blastResistance(15.0))) .translation("tile.oreLapis.name") .build("minecraft", "lapis_ore")); //////////////////// /// Lapis Block /// //////////////////// register(22, simpleBuilder() .itemType() .properties(builder -> builder .add(hardness(3.0)) .add(blastResistance(15.0))) .translation("tile.blockLapis.name") .build("minecraft", "lapis_block")); //////////////////// /// Dispenser /// //////////////////// register(23, simpleBuilder() .traits(LanternEnumTraits.FACING, LanternBooleanTraits.TRIGGERED) .defaultState(state -> state .withTrait(LanternEnumTraits.FACING, Direction.NORTH).get() .withTrait(LanternBooleanTraits.TRIGGERED, false).get()) .itemType() .properties(builder -> builder .add(hardness(3.5)) .add(blastResistance(17.5))) // .tileEntityType(() -> TileEntityTypes.DISPENSER) .translation("tile.dispenser.name") .behaviors(pipeline -> pipeline .add(new RotationPlacementBehavior())) .build("minecraft", "dispenser"), blockState -> { int data = directionData(blockState.getTraitValue(LanternEnumTraits.FACING).get()); if (blockState.getTraitValue(LanternBooleanTraits.TRIGGERED).get()) { data |= 0x8; } return (byte) data; }); //////////////////// /// Sandstone /// //////////////////// register(24, simpleBuilder() .trait(LanternEnumTraits.SANDSTONE_TYPE) .defaultState(state -> state.withTrait(LanternEnumTraits.SANDSTONE_TYPE, LanternSandstoneType.DEFAULT).get()) .itemType(builder -> builder .keysProvider(valueContainer -> valueContainer .registerKey(Keys.SANDSTONE_TYPE, LanternSandstoneType.DEFAULT) ) ) .properties(builder -> builder .add(hardness(0.8)) .add(blastResistance(4.0))) .translation(TranslationProvider.of(LanternEnumTraits.SANDSTONE_TYPE)) .build("minecraft", "sandstone"), blockState -> (byte) blockState.getTraitValue(LanternEnumTraits.SANDSTONE_TYPE).get().getInternalId()); //////////////////// /// Noteblock /// //////////////////// register(25, simpleBuilder() .itemType() .properties(builder -> builder .add(hardness(0.8)) .add(blastResistance(4.0))) .translation("tile.musicBlock.name") .build("minecraft", "noteblock")); //////////////////// /// Bed /// //////////////////// register(26, simpleBuilder() .traits(LanternEnumTraits.HORIZONTAL_FACING, LanternEnumTraits.BED_PART, LanternBooleanTraits.OCCUPIED) .defaultState(state -> state .withTrait(LanternEnumTraits.HORIZONTAL_FACING, Direction.NORTH).get() .withTrait(LanternEnumTraits.BED_PART, LanternBedPart.FOOT).get() .withTrait(LanternBooleanTraits.OCCUPIED, false).get()) .properties(builder -> builder .add(hardness(0.2)) .add(blastResistance(1.0))) .translation("tile.bed.name") .build("minecraft", "bed"), blockState -> { final Direction facing = blockState.getTraitValue(LanternEnumTraits.HORIZONTAL_FACING).get(); int type = facing == Direction.SOUTH ? 0 : facing == Direction.WEST ? 1 : facing == Direction.NORTH ? 2 : facing == Direction.EAST ? 3 : -1; checkArgument(type != -1); if (blockState.getTraitValue(LanternBooleanTraits.OCCUPIED).get()) { type |= 0x4; } if (blockState.getTraitValue(LanternEnumTraits.BED_PART).get() == LanternBedPart.HEAD) { type |= 0x8; } return (byte) type; }); ////////////////////// /// Golden Rail /// ////////////////////// register(27, simpleBuilder() .traits(LanternEnumTraits.STRAIGHT_RAIL_DIRECTION, LanternBooleanTraits.POWERED) .defaultState(state -> state .withTrait(LanternEnumTraits.STRAIGHT_RAIL_DIRECTION, LanternRailDirection.NORTH_SOUTH).get() .withTrait(LanternBooleanTraits.POWERED, false).get()) .itemType() .boundingBox(BoundingBoxes::rail) .properties(builder -> builder .add(PASSABLE) .add(hardness(0.7)) .add(blastResistance(3.5))) .translation("tile.goldenRail.name") .build("minecraft", "golden_rail"), blockState -> { int type = blockState.getTraitValue(LanternEnumTraits.STRAIGHT_RAIL_DIRECTION).get().getInternalId(); if (blockState.getTraitValue(LanternBooleanTraits.POWERED).get()) { type |= 0x8; } return (byte) type; }); //////////////////////// /// Detector Rail /// //////////////////////// register(28, simpleBuilder() .traits(LanternEnumTraits.STRAIGHT_RAIL_DIRECTION, LanternBooleanTraits.POWERED) .defaultState(state -> state .withTrait(LanternEnumTraits.STRAIGHT_RAIL_DIRECTION, LanternRailDirection.NORTH_SOUTH).get() .withTrait(LanternBooleanTraits.POWERED, false).get()) .itemType() .boundingBox(BoundingBoxes::rail) .properties(builder -> builder .add(PASSABLE) .add(hardness(0.7)) .add(blastResistance(3.5))) .translation("tile.detectorRail.name") .build("minecraft", "detector_rail"), blockState -> { int type = blockState.getTraitValue(LanternEnumTraits.STRAIGHT_RAIL_DIRECTION).get().getInternalId(); if (blockState.getTraitValue(LanternBooleanTraits.POWERED).get()) { type |= 0x8; } return (byte) type; }); // TODO: 29 /////////////// /// Web /// /////////////// register(30, simpleBuilder() .itemType() .properties(builder -> builder .add(PASSABLE) .add(hardness(4.0)) .add(blastResistance(20.0))) .translation("tile.web.name") .build("minecraft", "web")); ////////////////////// /// Tall Grass /// ////////////////////// register(31, simpleBuilder() .traits(LanternEnumTraits.SHRUB_TYPE) .defaultState(state -> state .withTrait(LanternEnumTraits.SHRUB_TYPE, LanternShrubType.DEAD_BUSH).get()) .itemType(builder -> builder .keysProvider(valueContainer -> valueContainer .registerKey(Keys.SHRUB_TYPE, LanternShrubType.DEAD_BUSH))) .boundingBox(BoundingBoxes.bush()) .properties(builder -> builder .add(INSTANT_BROKEN) .add(PASSABLE) .add(replaceable(true))) .translation("tile.tallgrass.name") .build("minecraft", "tallgrass"), blockState -> (byte) blockState.getTraitValue(LanternEnumTraits.SHRUB_TYPE).get().getInternalId()); ///////////////////// /// Dead Bush /// ///////////////////// register(32, simpleBuilder() .properties(builder -> builder .add(INSTANT_BROKEN) .add(PASSABLE) .add(replaceable(true))) .boundingBox(BoundingBoxes.bush()) .itemType() .translation("tile.deadbush.name") .build("minecraft", "deadbush")); // TODO: 33 // TODO: 34 /////////////////// /// Wool /// /////////////////// register(35, dyedBuilder("tile.wool.%s.name") .properties(builder -> builder .add(hardness(0.8)) .add(blastResistance(4.0))) .build("minecraft", "wool"), this::dyedData); // TODO: 36 ///////////////////////// /// Yellow Flower /// ///////////////////////// register(37, simpleBuilder() .traits(LanternEnumTraits.YELLOW_FLOWER_TYPE) .defaultState(state -> state .withTrait(LanternEnumTraits.YELLOW_FLOWER_TYPE, LanternPlantType.DANDELION).get()) .itemType(builder -> builder .keysProvider(valueContainer -> valueContainer .registerKey(Keys.PLANT_TYPE, LanternPlantType.DANDELION))) .boundingBox(BoundingBoxes.bush()) .properties(builder -> builder .add(INSTANT_BROKEN) .add(PASSABLE)) .translation(TranslationProvider.of(LanternEnumTraits.YELLOW_FLOWER_TYPE)) .build("minecraft", "yellow_flower"), blockState -> (byte) blockState.getTraitValue(LanternEnumTraits.YELLOW_FLOWER_TYPE).get().getInternalId()); ////////////////////// /// Red Flower /// ////////////////////// register(38, simpleBuilder() .traits(LanternEnumTraits.RED_FLOWER_TYPE) .defaultState(state -> state .withTrait(LanternEnumTraits.RED_FLOWER_TYPE, LanternPlantType.POPPY).get()) .itemType(builder -> builder .keysProvider(valueContainer -> valueContainer .registerKey(Keys.PLANT_TYPE, LanternPlantType.POPPY))) .boundingBox(BoundingBoxes.bush()) .properties(builder -> builder .add(INSTANT_BROKEN) .add(PASSABLE)) .translation(TranslationProvider.of(LanternEnumTraits.RED_FLOWER_TYPE)) .build("minecraft", "red_flower"), blockState -> (byte) blockState.getTraitValue(LanternEnumTraits.RED_FLOWER_TYPE).get().getInternalId()); ////////////////////////// /// Brown Mushroom /// ////////////////////////// register(39, simpleBuilder() .boundingBox(BoundingBoxes.bush()) .properties(builder -> builder .add(INSTANT_BROKEN) .add(PASSABLE) .add(lightEmission(1))) .translation("tile.mushroom.name") .build("minecraft", "brown_mushroom")); //////////////////////// /// Red Mushroom /// //////////////////////// register(40, simpleBuilder() .boundingBox(BoundingBoxes.bush()) .properties(builder -> builder .add(INSTANT_BROKEN) .add(PASSABLE)) .translation("tile.mushroom.name") .build("minecraft", "red_mushroom")); ////////////////////// /// Gold Block /// ////////////////////// register(41, simpleBuilder() .itemType() .properties(builder -> builder .add(hardness(3.0)) .add(blastResistance(10.0))) .translation("tile.blockGold.name") .build("minecraft", "gold_block")); ////////////////////// /// Iron Block /// ////////////////////// register(42, simpleBuilder() .itemType() .properties(builder -> builder .add(hardness(5.0)) .add(blastResistance(10.0))) .translation("tile.blockIron.name") .build("minecraft", "iron_block")); /////////////////////////// /// Double Stone Slab 1 /// /////////////////////////// register(43, doubleStoneSlab(LanternEnumTraits.STONE_SLAB1_TYPE, LanternSlabType.STONE) .translation("tile.stoneSlab.name") .build("minecraft", "double_stone_slab"), blockState -> doubleStoneSlabData(blockState, blockState.getTraitValue(LanternEnumTraits.STONE_SLAB1_TYPE).get().getInternalId())); //////////////////////// /// Stone Slab 1 /// //////////////////////// register(44, stoneSlab(LanternEnumTraits.STONE_SLAB1_TYPE, LanternSlabType.STONE, () -> BlockTypes.STONE_SLAB, () -> BlockTypes.DOUBLE_STONE_SLAB) .translation("tile.stoneSlab.name") .boundingBox(BoundingBoxes::slab) .build("minecraft", "stone_slab"), blockState -> stoneSlabData(blockState, blockState.getTraitValue(LanternEnumTraits.STONE_SLAB1_TYPE).get().getInternalId())); /////////////////////// /// Brick Block /// /////////////////////// register(45, simpleBuilder() .itemType() .properties(builder -> builder .add(hardness(2.0)) .add(blastResistance(10.0))) .translation("tile.brick.name") .build("minecraft", "brick_block")); /////////////// /// TNT /// /////////////// register(46, simpleBuilder() .trait(LanternBooleanTraits.EXPLODE) .defaultState(state -> state .withTrait(LanternBooleanTraits.EXPLODE, false).get()) .itemType() .properties(builder -> builder .add(INSTANT_BROKEN)) .translation("tile.tnt.name") .build("minecraft", "tnt"), blockState -> (byte) (blockState.getTraitValue(LanternBooleanTraits.EXPLODE).get() ? 1 : 0)); ///////////////////// /// Bookshelf /// ///////////////////// register(47, simpleBuilder() .itemType() .properties(builder -> builder .add(hardness(1.5)) .add(blastResistance(7.5))) .translation("tile.bookshelf.name") .build("minecraft", "bookshelf")); ///////////////////////////// /// Mossy Cobblestone /// ///////////////////////////// register(48, simpleBuilder() .itemType() .properties(builder -> builder .add(hardness(2.0)) .add(blastResistance(10.0))) .translation("tile.stoneMoss.name") .build("minecraft", "mossy_cobblestone")); //////////////////// /// Obsidian /// //////////////////// register(49, simpleBuilder() .itemType() .properties(builder -> builder .add(hardness(50.0)) .add(blastResistance(2000.0))) .translation("tile.obsidian.name") .build("minecraft", "obsidian")); ///////////////// /// Torch /// ///////////////// register(50, builder() .trait(LanternEnumTraits.TORCH_FACING) .defaultState(state -> state .withTrait(LanternEnumTraits.TORCH_FACING, Direction.UP).get()) .itemType() .properties(builder -> builder .add(INSTANT_BROKEN)) .translation("tile.torch.name") .boundingBox(BoundingBoxes::torch) .behaviors(pipeline -> pipeline .add(new BlockSnapshotProviderPlaceBehavior()) .add(new TorchPlacementBehavior()) .add(new SimpleBreakBehavior())) .build("minecraft", "torch"), blockState -> { final Direction direction = blockState.getTraitValue(LanternEnumTraits.TORCH_FACING).get(); switch (direction) { case EAST: return (byte) 1; case WEST: return (byte) 2; case SOUTH: return (byte) 3; case NORTH: return (byte) 4; case UP: return (byte) 5; default: throw new IllegalArgumentException(); } }); //////////////////// /// Chest /// //////////////////// register(54, chestBuilder() .translation("tile.chest.name") .build("minecraft", "chest"), this::chestData); //////////////////// /// Pumpkin /// //////////////////// register(86, pumpkinBuilder() .itemType(builder -> builder .properties(properties -> properties .add(equipmentType(EquipmentTypes.HEADWEAR)))) .translation("tile.pumpkin.name") .build("minecraft", "pumpkin"), this::pumpkinData); //////////////////// /// Lit Pumpkin /// //////////////////// register(91, pumpkinBuilder() .properties(builder -> builder .add(lightEmission(15))) .translation("tile.litpumpkin.name") .build("minecraft", "lit_pumpkin"), this::pumpkinData); ///////////////////// /// Stained Glass /// ///////////////////// register(95, dyedBuilder("tile.stainedGlass.%s.name") .properties(builder -> builder .add(hardness(0.3)) .add(blastResistance(1.5))) .build("minecraft", "stained_glass"), this::dyedData); ////////////////////////// /// Double Wooden Slab /// ////////////////////////// register(125, simpleBuilder() .traits(LanternEnumTraits.TREE_TYPE) .defaultState(state -> state .withTrait(LanternEnumTraits.TREE_TYPE, LanternTreeType.OAK).get()) .translation(TranslationProvider.of(LanternEnumTraits.TREE_TYPE, type -> tr("tile.woodSlab." + type.getTranslationKeyBase() + ".name"))) .itemType(builder -> builder .keysProvider(valueContainer -> valueContainer .registerKey(Keys.TREE_TYPE, LanternTreeType.OAK) ) ) .properties(builder -> builder .add(hardness(2.0)) .add(blastResistance(5.0))) .build("minecraft", "double_wooden_slab"), blockState -> (byte) blockState.getTraitValue(LanternEnumTraits.TREE_TYPE).get().getInternalId()); ////////////////////////// /// Wooden Slab /// ////////////////////////// register(126, simpleBuilder() .traits(LanternEnumTraits.PORTION_TYPE, LanternEnumTraits.TREE_TYPE) .defaultState(state -> state .withTrait(LanternEnumTraits.PORTION_TYPE, LanternPortionType.BOTTOM).get() .withTrait(LanternEnumTraits.TREE_TYPE, LanternTreeType.OAK).get()) .translation(TranslationProvider.of(LanternEnumTraits.TREE_TYPE, type -> tr("tile.woodSlab." + type.getTranslationKeyBase() + ".name"))) .itemType(builder -> builder .behaviors(pipeline -> pipeline .add(new SlabItemInteractionBehavior<>(LanternEnumTraits.TREE_TYPE, () -> BlockTypes.WOODEN_SLAB, () -> BlockTypes.DOUBLE_WOODEN_SLAB))) .keysProvider(valueContainer -> valueContainer .registerKey(Keys.TREE_TYPE, LanternTreeType.OAK) ) ) .boundingBox(BoundingBoxes::slab) .properties(builder -> builder .add(hardness(2.0)) .add(blastResistance(5.0))) .build("minecraft", "wooden_slab"), blockState -> { final int type = blockState.getTraitValue(LanternEnumTraits.TREE_TYPE).get().getInternalId(); final int portion = (byte) blockState.getTraitValue(LanternEnumTraits.PORTION_TYPE).get().getInternalId(); return (byte) (portion << 3 | type); }); ///////////////////// /// Ender Chest /// ///////////////////// register(130, simpleBuilder() .trait(LanternEnumTraits.HORIZONTAL_FACING) .defaultState(state -> state.withTrait(LanternEnumTraits.HORIZONTAL_FACING, Direction.NORTH).get()) .itemType() .tileEntityType(() -> TileEntityTypes.ENDER_CHEST) .properties(builder -> builder .add(hardness(22.5)) .add(blastResistance(3000.0)) .add(lightEmission(7))) .translation("tile.enderChest.name") .boundingBox(BoundingBoxes.chest()) .behaviors(pipeline -> pipeline .add(new HorizontalRotationPlacementBehavior()) .add(new EnderChestInteractionBehavior())) .build("minecraft", "ender_chest"), this::chestData); ///////////////////// /// Trapped Chest /// ///////////////////// register(146, chestBuilder() .translation("tile.chestTrap.name") .build("minecraft", "trapped_chest"), this::chestData); /////////////////////// /// Redstone Block /// /////////////////////// register(152, simpleBuilder() .itemType() .properties(builder -> builder .add(hardness(5.0)) .add(blastResistance(30.0))) .translation("tile.blockRedstone.name") .build("minecraft", "redstone_block")); //////////////////// /// Quartz Ore /// //////////////////// register(153, simpleBuilder() .itemType() .properties(builder -> builder .add(hardness(3.0)) .add(blastResistance(15.0))) .translation("tile.netherquartz.name") .build("minecraft", "quartz_ore")); //////////////////// /// Hopper /// //////////////////// register(154, simpleBuilder() .traits(LanternEnumTraits.HOPPER_FACING, LanternBooleanTraits.ENABLED) .defaultState(state -> state .withTrait(LanternEnumTraits.HOPPER_FACING, Direction.DOWN).get() .withTrait(LanternBooleanTraits.ENABLED, false).get()) .itemType() .properties(builder -> builder .add(hardness(3.0)) .add(blastResistance(8.0))) .translation("tile.hopper.name") .behaviors(pipeline -> pipeline .add(new HopperPlacementBehavior())) .build("minecraft", "hopper"), blockState -> { int data = directionData(blockState.getTraitValue(LanternEnumTraits.HOPPER_FACING).get()); if (!blockState.getTraitValue(LanternBooleanTraits.ENABLED).get()) { data |= 0x8; } return (byte) data; }); ////////////////////// /// Quartz Block /// ////////////////////// register(155, simpleBuilder() .trait(LanternEnumTraits.QUARTZ_TYPE) .defaultState(state -> state .withTrait(LanternEnumTraits.QUARTZ_TYPE, LanternQuartzType.DEFAULT).get()) .itemType(builder -> builder .keysProvider(valueContainer -> valueContainer .registerKey(Keys.QUARTZ_TYPE, LanternQuartzType.DEFAULT) ) ) .properties(builder -> builder .add(hardness(0.8)) .add(blastResistance(2.4))) .translation(TranslationProvider.of(LanternEnumTraits.QUARTZ_TYPE)) .behaviors(pipeline -> pipeline .add(new QuartzLinesRotationPlacementBehavior())) .build("minecraft", "quartz_block"), blockState -> (byte) blockState.getTraitValue(LanternEnumTraits.QUARTZ_TYPE).get().getInternalId()); //////////////////// /// Dropper /// //////////////////// register(158, simpleBuilder() .traits(LanternEnumTraits.FACING, LanternBooleanTraits.TRIGGERED) .defaultState(state -> state .withTrait(LanternEnumTraits.FACING, Direction.NORTH).get() .withTrait(LanternBooleanTraits.TRIGGERED, false).get()) .itemType() .properties(builder -> builder .add(hardness(3.5)) .add(blastResistance(17.5))) // .tileEntityType(() -> TileEntityTypes.DROPPER) .translation("tile.dropper.name") .behaviors(pipeline -> pipeline .add(new RotationPlacementBehavior())) .build("minecraft", "dropper"), blockState -> { int data = directionData(blockState.getTraitValue(LanternEnumTraits.FACING).get()); if (blockState.getTraitValue(LanternBooleanTraits.TRIGGERED).get()) { data |= 0x8; } return (byte) data; }); ////////////////////////////// /// Stained Hardended Clay /// ////////////////////////////// register(159, dyedBuilder("tile.clayHardenedStained.%s.name") .properties(builder -> builder .add(hardness(1.25)) .add(blastResistance(7.0))) .build("minecraft", "stained_hardened_clay"), this::dyedData); ////////////////////////// /// Stained Glass Pane /// ////////////////////////// register(160, dyedBuilder("tile.thinStainedGlass.%s.name") .properties(builder -> builder .add(hardness(0.3)) .add(blastResistance(1.5))) .build("minecraft", "stained_glass_pane"), this::dyedData); //////////////////// /// Leaves 2 /// //////////////////// register(161, leavesBuilder(LanternEnumTraits.LEAVES2_TYPE, LanternTreeType.ACACIA) .build("minecraft", "leaves2"), blockState -> leavesData(blockState, blockState.getTraitValue(LanternEnumTraits.LEAVES2_TYPE).get().getInternalId() - 4)); //////////////////// /// Log 2 /// //////////////////// register(162, logBuilder(LanternEnumTraits.LOG2_TYPE, LanternTreeType.ACACIA) .build("minecraft", "log2"), blockState -> logData(blockState, blockState.getTraitValue(LanternEnumTraits.LOG2_TYPE).get().getInternalId() - 4)); //////////////////// /// Barrier /// //////////////////// register(166, simpleBuilder() .itemType() .properties(builder -> builder .add(PropertyProviderCollections.UNBREAKABLE)) .translation("tile.barrier.name") .build("minecraft", "barrier")); ///////////////////// /// Red Sandstone /// ///////////////////// register(179, simpleBuilder() .trait(LanternEnumTraits.SANDSTONE_TYPE) .defaultState(state -> state.withTrait(LanternEnumTraits.SANDSTONE_TYPE, LanternSandstoneType.DEFAULT).get()) .itemType(builder -> builder .keysProvider(valueContainer -> valueContainer .registerKey(Keys.SANDSTONE_TYPE, LanternSandstoneType.DEFAULT) ) ) .properties(builder -> builder .add(hardness(0.8)) .add(blastResistance(4.0))) .translation(TranslationProvider.of(LanternEnumTraits.SANDSTONE_TYPE)) .build("minecraft", "red_sandstone"), blockState -> (byte) blockState.getTraitValue(LanternEnumTraits.SANDSTONE_TYPE).get().getInternalId()); /////////////////////////// /// Double Stone Slab 2 /// /////////////////////////// register(181, doubleStoneSlab(LanternEnumTraits.STONE_SLAB2_TYPE, LanternSlabType.RED_SAND) .translation("tile.stoneSlab2.name") .build("minecraft", "double_stone_slab2"), blockState -> doubleStoneSlabData(blockState, blockState.getTraitValue(LanternEnumTraits.STONE_SLAB2_TYPE).get().getInternalId() - 8)); //////////////////////// /// Stone Slab 2 /// //////////////////////// register(182, stoneSlab(LanternEnumTraits.STONE_SLAB2_TYPE, LanternSlabType.RED_SAND, () -> BlockTypes.STONE_SLAB2, () -> BlockTypes.DOUBLE_STONE_SLAB2) .translation("tile.stoneSlab2.name") .boundingBox(BoundingBoxes::slab) .build("minecraft", "stone_slab2"), blockState -> stoneSlabData(blockState, blockState.getTraitValue(LanternEnumTraits.STONE_SLAB2_TYPE).get().getInternalId() - 8)); /////////////////////////// /// White Shulker Box /// /////////////////////////// register(219, shulkerBox() .translation("tile.shulkerBoxWhite.name") .build("minecraft", "white_shulker_box"), this::shulkerBoxData); /////////////////////////// /// Orange Shulker Box /// /////////////////////////// register(220, shulkerBox() .translation("tile.shulkerBoxOrange.name") .build("minecraft", "orange_shulker_box"), this::shulkerBoxData); //////////////////////////// /// Magenta Shulker Box /// //////////////////////////// register(221, shulkerBox() .translation("tile.shulkerBoxMagenta.name") .build("minecraft", "magenta_shulker_box"), this::shulkerBoxData); /////////////////////////////// /// Light Blue Shulker Box /// /////////////////////////////// register(222, shulkerBox() .translation("tile.shulkerBoxLightBlue.name") .build("minecraft", "light_blue_shulker_box"), this::shulkerBoxData); /////////////////////////// /// Yellow Shulker Box /// /////////////////////////// register(223, shulkerBox() .translation("tile.shulkerBoxYellow.name") .build("minecraft", "yellow_shulker_box"), this::shulkerBoxData); ///////////////////////// /// Lime Shulker Box /// ///////////////////////// register(224, shulkerBox() .translation("tile.shulkerBoxLime.name") .build("minecraft", "lime_shulker_box"), this::shulkerBoxData); ///////////////////////// /// Pink Shulker Box /// ///////////////////////// register(225, shulkerBox() .translation("tile.shulkerBoxPink.name") .build("minecraft", "pink_shulker_box"), this::shulkerBoxData); ///////////////////////// /// Gray Shulker Box /// ///////////////////////// register(226, shulkerBox() .translation("tile.shulkerBoxGray.name") .build("minecraft", "gray_shulker_box"), this::shulkerBoxData); ///////////////////////// /// Gray Shulker Box /// ///////////////////////// register(227, shulkerBox() .translation("tile.shulkerBoxSilver.name") .build("minecraft", "silver_shulker_box"), this::shulkerBoxData); ///////////////////////// /// Cyan Shulker Box /// ///////////////////////// register(228, shulkerBox() .translation("tile.shulkerBoxCyan.name") .build("minecraft", "cyan_shulker_box"), this::shulkerBoxData); /////////////////////////// /// Purple Shulker Box /// /////////////////////////// register(229, shulkerBox() .translation("tile.shulkerBoxPurple.name") .build("minecraft", "purple_shulker_box"), this::shulkerBoxData); ///////////////////////// /// Blue Shulker Box /// ///////////////////////// register(230, shulkerBox() .translation("tile.shulkerBoxBlue.name") .build("minecraft", "blue_shulker_box"), this::shulkerBoxData); ////////////////////////// /// Brown Shulker Box /// ////////////////////////// register(231, shulkerBox() .translation("tile.shulkerBoxBrown.name") .build("minecraft", "brown_shulker_box"), this::shulkerBoxData); ////////////////////////// /// Green Shulker Box /// ////////////////////////// register(232, shulkerBox() .translation("tile.shulkerBoxGreen.name") .build("minecraft", "green_shulker_box"), this::shulkerBoxData); //////////////////////// /// Red Shulker Box /// //////////////////////// register(233, shulkerBox() .translation("tile.shulkerBoxRed.name") .build("minecraft", "red_shulker_box"), this::shulkerBoxData); ////////////////////////// /// Black Shulker Box /// ////////////////////////// register(234, shulkerBox() .translation("tile.shulkerBoxBlack.name") .build("minecraft", "black_shulker_box"), this::shulkerBoxData); // @formatter:on } private BlockTypeBuilder simpleBuilder() { return builder() .behaviors(pipeline -> pipeline .add(new BlockSnapshotProviderPlaceBehavior()) .add(new SimplePlacementBehavior()) .add(new SimpleBreakBehavior())); // TODO: Item drops? } private BlockTypeBuilder builder() { return new BlockTypeBuilderImpl(); } private int directionData(Direction direction) { switch (direction) { case DOWN: return 0; case UP: return 1; case NORTH: return 2; case SOUTH: return 3; case WEST: return 4; case EAST: return 5; default: throw new IllegalArgumentException(); } } private int horizontalDirectionData(Direction direction) { switch (direction) { case SOUTH: return 0; case WEST: return 1; case NORTH: return 2; case EAST: return 3; default: throw new IllegalArgumentException(); } } private BlockTypeBuilder pumpkinBuilder() { return simpleBuilder() .itemType() .traits(LanternEnumTraits.HORIZONTAL_FACING) .defaultState(state -> state .withTrait(LanternEnumTraits.HORIZONTAL_FACING, Direction.NORTH).get()) .properties(builder -> builder .add(hardness(1.0)) .add(blastResistance(5.0))) .behaviors(pipeline -> pipeline .add(new HorizontalRotationPlacementBehavior())); } private byte pumpkinData(BlockState blockState) { return (byte) horizontalDirectionData(blockState.getTraitValue(LanternEnumTraits.HORIZONTAL_FACING).get()); } private BlockTypeBuilder dyedBuilder(String translationKey) { return simpleBuilder() .traits(LanternEnumTraits.DYE_COLOR) .defaultState(state -> state .withTrait(LanternEnumTraits.DYE_COLOR, LanternDyeColor.WHITE).get()) .itemType(builder -> builder .keysProvider(valueContainer -> valueContainer .registerKey(Keys.DYE_COLOR, LanternDyeColor.WHITE) ) ) .properties(builder -> builder .add(hardness(0.8)) .add(blastResistance(4.0))) .translation(TranslationProvider.of(LanternEnumTraits.DYE_COLOR, color -> tr(String.format(translationKey, color.getTranslationPart())))); } private byte dyedData(BlockState blockState) { return (byte) blockState.getTraitValue(LanternEnumTraits.DYE_COLOR).get().getInternalId(); } /** * Generates a leaves block builder. * * @param enumTrait The tree type enum trait * @param <E> The enum value type * @return The block type builder */ private <E extends Enum<E> & TreeType> BlockTypeBuilder leavesBuilder(EnumTrait<E> enumTrait, E defaultTreeType) { return simpleBuilder() .traits(LanternBooleanTraits.DECAYABLE, LanternBooleanTraits.CHECK_DECAY, enumTrait) .defaultState(state -> state.withTrait(enumTrait, defaultTreeType).get()) .itemType(builder -> builder .keysProvider(valueContainer -> valueContainer .registerKey(Keys.TREE_TYPE, defaultTreeType) ) ) .properties(builder -> builder .add(hardness(0.2)) .add(blastResistance(1.0)) .add(flammableInfo(30, 60))) .translation(TranslationProvider.of(enumTrait, type -> tr("tile.leaves." + ((LanternTreeType) type).getTranslationKeyBase() + ".name"))); } private byte leavesData(BlockState blockState, int type) { if (blockState.getTraitValue(LanternBooleanTraits.DECAYABLE).get()) { type |= 0x4; } if (blockState.getTraitValue(LanternBooleanTraits.CHECK_DECAY).get()) { type |= 0x8; } return (byte) type; } /** * Generates a log block builder. * * @param enumTrait The tree type enum trait * @param <E> The enum value type * @return The block type builder */ private <E extends Enum<E> & TreeType> BlockTypeBuilder logBuilder(EnumTrait<E> enumTrait, E defaultTreeType) { return simpleBuilder() .traits(LanternEnumTraits.LOG_AXIS, enumTrait) .defaultState(state -> state.withTrait(enumTrait, defaultTreeType).get()) .itemType(builder -> builder .keysProvider(valueContainer -> valueContainer .registerKey(Keys.TREE_TYPE, defaultTreeType) ) ) .properties(builder -> builder .add(hardness(2.0)) .add(blastResistance(5.0)) .add(flammableInfo(5, 5))) .translation(TranslationProvider.of(enumTrait, type -> tr("tile.log." + ((LanternTreeType) type).getTranslationKeyBase() + ".name"))) .behaviors(pipeline -> pipeline .add(new LogAxisRotationPlacementBehavior()) .add(new SimpleBlockDropsProviderBehavior(/* No items yet? */))); } private byte logData(BlockState blockState, int type) { final int axis = blockState.getTraitValue(LanternEnumTraits.LOG_AXIS).get().getInternalId(); return (byte) (axis << 2 | type); } private byte chestData(BlockState blockState) { final Direction facing = blockState.getTraitValue(LanternEnumTraits.HORIZONTAL_FACING).get(); return (byte) directionData(facing); } private BlockTypeBuilder chestBuilder() { return builder() .trait(LanternEnumTraits.HORIZONTAL_FACING) .defaultState(state -> state.withTrait(LanternEnumTraits.HORIZONTAL_FACING, Direction.NORTH).get()) .itemType() .tileEntityType(() -> TileEntityTypes.CHEST) .boundingBox(BoundingBoxes::doubleChest) .properties(builder -> builder .add(hardness(2.5)) .add(blastResistance(12.5))) .behaviors(pipeline -> pipeline .add(new BlockSnapshotProviderPlaceBehavior()) .add(new ChestPlacementBehavior()) .add(new ChestInteractionBehavior()) .add(new SimpleBreakBehavior())); // TODO: Item drops? } private BlockTypeBuilder shulkerBox() { return builder() .trait(LanternEnumTraits.FACING) .defaultState(state -> state.withTrait(LanternEnumTraits.FACING, Direction.UP).get()) .itemType(builder -> builder .keysProvider(valueContainer -> valueContainer .registerKey(LanternKeys.INVENTORY_SNAPSHOT, InventorySnapshot.EMPTY) ) .maxStackQuantity(1) ) .tileEntityType(() -> TileEntityTypes.SHULKER_BOX) .properties(builder -> builder .add(hardness(2.0)) .add(blastResistance(10.0))) .behaviors(pipeline -> pipeline .add(new BlockSnapshotProviderPlaceBehavior()) .add(new SimplePlacementBehavior()) .add(new OppositeFaceDirectionalPlacementBehavior()) .add(new OpeneableContainerInteractionBehavior()) .add(new SimpleBreakBehavior())); // TODO: Item drops? } private byte shulkerBoxData(BlockState blockState) { final Direction facing = blockState.getTraitValue(LanternEnumTraits.FACING).get(); return (byte) directionData(facing); } private <E extends Enum<E> & SlabType> BlockTypeBuilder stoneSlab(EnumTrait<E> enumTrait, E defaultValue, Supplier<BlockType> halfSlabType, Supplier<BlockType> doubleSlabType) { return simpleBuilder() .traits(LanternEnumTraits.PORTION_TYPE, enumTrait) .defaultState(state -> state .withTrait(enumTrait, defaultValue).get() .withTrait(LanternEnumTraits.PORTION_TYPE, LanternPortionType.BOTTOM).get()) .translation(TranslationProvider.of(enumTrait)) .itemType(builder -> builder .behaviors(pipeline -> pipeline .add(new SlabItemInteractionBehavior<>(enumTrait, halfSlabType, doubleSlabType))) .keysProvider(valueContainer -> valueContainer .registerKey(Keys.SLAB_TYPE, defaultValue) ) ) .properties(builder -> builder .add(hardness(2.0)) .add(blastResistance(10.0))); } private byte stoneSlabData(BlockState blockState, int type) { final int portion = (byte) blockState.getTraitValue(LanternEnumTraits.PORTION_TYPE).get().getInternalId(); return (byte) (portion << 3 | type); } private <E extends Enum<E> & SlabType> BlockTypeBuilder doubleStoneSlab(EnumTrait<E> enumTrait, E defaultValue) { return simpleBuilder() .traits(LanternBooleanTraits.SEAMLESS, enumTrait) .defaultState(state -> state .withTrait(enumTrait, defaultValue).get() .withTrait(LanternBooleanTraits.SEAMLESS, false).get()) .translation(TranslationProvider.of(enumTrait)) .itemType(builder -> builder .keysProvider(valueContainer -> valueContainer .registerKey(Keys.SLAB_TYPE, defaultValue) ) ) .properties(builder -> builder .add(hardness(2.0)) .add(blastResistance(10.0))); } private byte doubleStoneSlabData(BlockState blockState, int type) { final byte seamless = (byte) (blockState.getTraitValue(LanternBooleanTraits.SEAMLESS).get() ? 1 : 0); return (byte) (seamless << 3 | type); } }