diff --git a/1.12/gradle.properties b/1.12/gradle.properties index aff41d6..d9c4b97 100644 --- a/1.12/gradle.properties +++ b/1.12/gradle.properties @@ -4,4 +4,4 @@ org.gradle.jvmargs=-Xmx8G version_minecraft=1.12.2 version_forge=14.23.5.2768 version_jei=4.10.0.198 -version_engineersdecor=1.0.9 +version_engineersdecor=1.0.10 diff --git a/1.12/meta/update.json b/1.12/meta/update.json index 3e70560..9d41106 100644 --- a/1.12/meta/update.json +++ b/1.12/meta/update.json @@ -1,6 +1,9 @@ { "homepage": "https://www.curseforge.com/minecraft/mc-mods/engineers-decor/", "1.12.2": { + "1.0.10": "[R] Release based on v1.0.10-b2. Release-to-release changes: * Steel table added. * Steel floor grating added. * Treated wood side table added. * Exit Sign added. * Recipe fixes.", + "1.0.10-b2": "[A] Steel table added.\n[A] Steel floor grating added.", + "1.0.10-b1": "[A] Treated wood side table added.\n[F] Fixed recipe collision of Metal Rung Ladder (issue #37, thx ProsperCraft for reporting).\n[A] Added Exit Sign (texture design by J. Carver).", "1.0.9": "[R] Release based on v1.0.9-b3. Release-to-release changes: * Slabs for clinker, concrete, slag bricks. * Slab slices for sheet metals, treated wood, and concretes. * Language updates. * Block hardness adaptions. * 1st/3rd person item model fixes. * Furnace initialisation issue fixed.", "1.0.9-b3": "[A] Added missing recipes for slabs.\n[A] Added slab slices for IE sheet metals, treated wood, and concretes (stackable \"quater-slabs\").\n[M] Updated 1st/3rd person item model rotations/translations.\n[M] Hardness of valves and furni slightly increased.", "1.0.9-b2": "[A] Added slabs for Clinker Brick, Slag Brick, Rebar Concrete, and Stained Clinker. Texture variations like the base blocks. Allow fast pick-up (see tooltip help or config).\n[F] Fixed lab/electrical furnace initialisation issue (first item inserted was smelted directly).", @@ -47,7 +50,7 @@ "1.0.0-b1": "[A] Initial structure.\n[A] Added clinker bricks and clinker brick stairs.\n[A] Added slag bricks and slag brick stairs.\n[A] Added metal rung ladder.\n[A] Added staggered metal steps ladder.\n[A] Added treated wood ladder.\n[A] Added treated wood pole.\n[A] Added treated wood table." }, "promos": { - "1.12.2-recommended": "1.0.9", - "1.12.2-latest": "1.0.9" + "1.12.2-recommended": "1.0.10", + "1.12.2-latest": "1.0.10" } } \ No newline at end of file diff --git a/1.12/readme.md b/1.12/readme.md index 6fef5a9..38b57af 100644 --- a/1.12/readme.md +++ b/1.12/readme.md @@ -10,6 +10,23 @@ Mod sources for Minecraft version 1.12.2. ---- ## Version history + ------------------------------------------------------------------- + - v1.0.10 [R] Release based on v1.0.10-b2. Release-to-release changes: + * Steel table added. + * Steel floor grating added. + * Treated wood side table added. + * Exit Sign added. + * Recipe fixes. + ------------------------------------------------------------------- + + - v1.0.10-b2 [A] Steel table added. + [A] Steel floor grating added. + + - v1.0.10-b1 [A] Treated wood side table added. + [F] Fixed recipe collision of Metal Rung Ladder (issue #37, + thx ProsperCraft for reporting). + [A] Added Exit Sign (texture design by J. Carver). + ------------------------------------------------------------------- - v1.0.9 [R] Release based on v1.0.9-b3. Release-to-release changes: * Slabs for clinker, concrete, slag bricks. diff --git a/1.12/src/main/java/wile/engineersdecor/blocks/BlockDecor.java b/1.12/src/main/java/wile/engineersdecor/blocks/BlockDecor.java index ad0877d..462e498 100644 --- a/1.12/src/main/java/wile/engineersdecor/blocks/BlockDecor.java +++ b/1.12/src/main/java/wile/engineersdecor/blocks/BlockDecor.java @@ -31,7 +31,6 @@ import net.minecraft.util.*; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.entity.Entity; -import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import javax.annotation.Nonnull; diff --git a/1.12/src/main/java/wile/engineersdecor/blocks/BlockDecorFloorGrating.java b/1.12/src/main/java/wile/engineersdecor/blocks/BlockDecorFloorGrating.java new file mode 100644 index 0000000..a6b6125 --- /dev/null +++ b/1.12/src/main/java/wile/engineersdecor/blocks/BlockDecorFloorGrating.java @@ -0,0 +1,44 @@ +/* + * @file BlockDecorFloorGrating.java + * @author Stefan Wilhelm (wile) + * @copyright (C) 2019 Stefan Wilhelm + * @license MIT (see https://opensource.org/licenses/MIT) + * + * Floor gratings. + */ +package wile.engineersdecor.blocks; + +import net.minecraft.block.SoundType; +import net.minecraft.block.material.Material; +import net.minecraft.block.state.BlockFaceShape; +import net.minecraft.block.state.IBlockState; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockAccess; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class BlockDecorFloorGrating extends BlockDecor +{ + public BlockDecorFloorGrating(@Nonnull String registryName, long config, @Nullable Material material, float hardness, float resistance, @Nullable SoundType sound, @Nullable AxisAlignedBB boundingbox) + { super(registryName, config, material, hardness, resistance, sound, (boundingbox==null) ? (new AxisAlignedBB[]{FULL_BLOCK_AABB}) : (new AxisAlignedBB[]{boundingbox})); } + + @Override + public boolean isFullCube(IBlockState state) + { return false; } + + @Override + public boolean isNormalCube(IBlockState state) + { return false; } + + @Override + public boolean isOpaqueCube(IBlockState state) + { return false; } + + @Override + public BlockFaceShape getBlockFaceShape(IBlockAccess world, IBlockState state, BlockPos pos, EnumFacing face) + { return BlockFaceShape.SOLID; } + +} diff --git a/1.12/src/main/java/wile/engineersdecor/blocks/ModBlocks.java b/1.12/src/main/java/wile/engineersdecor/blocks/ModBlocks.java index 3865179..523c0bd 100644 --- a/1.12/src/main/java/wile/engineersdecor/blocks/ModBlocks.java +++ b/1.12/src/main/java/wile/engineersdecor/blocks/ModBlocks.java @@ -73,6 +73,86 @@ public class ModBlocks public static final BlockDecorGlassBlock PANZERGLASS_BLOCK = new BlockDecorGlassBlock("panzerglass_block", 0, Material.GLASS, 1f, 2000f, SoundType.GLASS); public static final BlockDecorSlab PANZERGLASS_SLAB = new BlockDecorSlab("panzerglass_slab", BlockDecor.CFG_TRANSLUCENT, Material.GLASS, 1f, 2000f, SoundType.GLASS); + //-------------------------------------------------------------------------------------------------------------------- + + public static final BlockDecorCraftingTable TREATED_WOOD_CRAFTING_TABLE = new BlockDecorCraftingTable( + "treated_wood_crafting_table", + BlockDecor.CFG_CUTOUT|BlockDecor.CFG_HORIZIONTAL|BlockDecor.CFG_LOOK_PLACEMENT|BlockDecor.CFG_OPPOSITE_PLACEMENT, + Material.WOOD, 1.0f, 15f, SoundType.WOOD, + ModAuxiliaries.getPixeledAABB(1,0,1, 15,15.9,15) + ); + + public static final BlockDecorFurnace SMALL_LAB_FURNACE = new BlockDecorFurnace( + "small_lab_furnace", + BlockDecor.CFG_CUTOUT|BlockDecor.CFG_HORIZIONTAL|BlockDecor.CFG_LOOK_PLACEMENT|BlockDecor.CFG_OPPOSITE_PLACEMENT| + BlockDecor.CFG_ELECTRICAL, + Material.IRON, 0.5f, 15f, SoundType.METAL, + ModAuxiliaries.getPixeledAABB(1,0,1, 15,15,16) + ); + + public static final BlockDecorFurnaceElectrical SMALL_ELECTRICAL_FURNACE = new BlockDecorFurnaceElectrical( + "small_electrical_furnace", + BlockDecor.CFG_CUTOUT|BlockDecor.CFG_HORIZIONTAL|BlockDecor.CFG_LOOK_PLACEMENT|BlockDecor.CFG_ELECTRICAL, + Material.IRON, 0.5f, 15f, SoundType.METAL, + ModAuxiliaries.getPixeledAABB(0,0,0, 16,16,16) + ); + + public static final BlockDecorDropper FACTORY_DROPPER = new BlockDecorDropper( + "factory_dropper", + BlockDecor.CFG_LOOK_PLACEMENT|BlockDecor.CFG_REDSTONE_CONTROLLED, + Material.IRON, 1f, 15f, SoundType.METAL, + ModAuxiliaries.getPixeledAABB(0,0,0, 16,16,15) + ); + + public static final BlockDecorWasteIncinerator SMALL_WASTE_INCINERATOR = new BlockDecorWasteIncinerator( + "small_waste_incinerator", + BlockDecor.CFG_DEFAULT|BlockDecor.CFG_ELECTRICAL, + Material.IRON, 1f, 15f, SoundType.METAL, + ModAuxiliaries.getPixeledAABB(0,0,0, 16,16,16) + ); + + public static final BlockDecorMineralSmelter SMALL_MINERAL_SMELTER = new BlockDecorMineralSmelter( + "small_mineral_smelter", + BlockDecor.CFG_LOOK_PLACEMENT, + Material.IRON, 1f, 15f, SoundType.METAL, + ModAuxiliaries.getPixeledAABB(1.1,0,1.1, 14.9,16,14.9) + ); + + //-------------------------------------------------------------------------------------------------------------------- + + public static final BlockDecorPipeValve STRAIGHT_CHECK_VALVE = new BlockDecorPipeValve( + "straight_pipe_valve", + BlockDecor.CFG_FACING_PLACEMENT|BlockDecor.CFG_OPPOSITE_PLACEMENT|BlockDecor.CFG_FLIP_PLACEMENT_SHIFTCLICK| + BlockDecor.CFG_CUTOUT, + Material.IRON, 0.7f, 15f, SoundType.METAL, + ModAuxiliaries.getPixeledAABB(4,4,0, 12,12,16) + ); + + public static final BlockDecorPipeValve STRAIGHT_REDSTONE_VALVE = new BlockDecorPipeValve( + "straight_pipe_valve_redstone", + BlockDecor.CFG_FACING_PLACEMENT|BlockDecor.CFG_OPPOSITE_PLACEMENT|BlockDecor.CFG_FLIP_PLACEMENT_SHIFTCLICK| + BlockDecor.CFG_CUTOUT|BlockDecor.CFG_REDSTONE_CONTROLLED, + Material.IRON, 0.7f, 15f, SoundType.METAL, + ModAuxiliaries.getPixeledAABB(4,4,0, 12,12,16) + ); + + public static final BlockDecorPipeValve STRAIGHT_REDSTONE_ANALOG_VALVE = new BlockDecorPipeValve( + "straight_pipe_valve_redstone_analog", + BlockDecor.CFG_FACING_PLACEMENT|BlockDecor.CFG_OPPOSITE_PLACEMENT|BlockDecor.CFG_FLIP_PLACEMENT_SHIFTCLICK| + BlockDecor.CFG_CUTOUT|BlockDecor.CFG_REDSTONE_CONTROLLED|BlockDecor.CFG_ANALOG, + Material.IRON, 0.7f, 15f, SoundType.METAL, + ModAuxiliaries.getPixeledAABB(4,4,0, 12,12,16) + ); + + public static final BlockDecorPassiveFluidAccumulator PASSIVE_FLUID_ACCUMULATOR = new BlockDecorPassiveFluidAccumulator( + "passive_fluid_accumulator", + BlockDecor.CFG_FACING_PLACEMENT|BlockDecor.CFG_OPPOSITE_PLACEMENT|BlockDecor.CFG_FLIP_PLACEMENT_SHIFTCLICK| + BlockDecor.CFG_CUTOUT, + Material.IRON, 0.7f, 15f, SoundType.METAL, + ModAuxiliaries.getPixeledAABB(0,0,0, 16,16,16) + ); + + //-------------------------------------------------------------------------------------------------------------------- public static final BlockDecorStraightPole TREATED_WOOD_POLE = new BlockDecorStraightPole( "treated_wood_pole", @@ -123,6 +203,15 @@ public class ModBlocks ModAuxiliaries.getPixeledAABB(5,5,0, 11,11,16) ); + public static final BlockDecorHorizontalSupport STEEL_DOUBLE_T_SUPPORT = new BlockDecorHorizontalSupport( + "steel_double_t_support", + BlockDecor.CFG_CUTOUT|BlockDecor.CFG_HORIZIONTAL|BlockDecor.CFG_LOOK_PLACEMENT, + Material.IRON, 2.0f, 15f, SoundType.METAL, + ModAuxiliaries.getPixeledAABB(5,11,0, 11,16,16) + ); + + //-------------------------------------------------------------------------------------------------------------------- + public static final BlockDecor TREATED_WOOD_TABLE = new BlockDecor( "treated_wood_table", BlockDecor.CFG_CUTOUT|BlockDecor.CFG_HORIZIONTAL|BlockDecor.CFG_LOOK_PLACEMENT, @@ -144,11 +233,11 @@ public class ModBlocks ModAuxiliaries.getPixeledAABB(2,0,2, 14,15.9,14) ); - public static final BlockDecorCraftingTable TREATED_WOOD_CRAFTING_TABLE = new BlockDecorCraftingTable( - "treated_wood_crafting_table", - BlockDecor.CFG_CUTOUT|BlockDecor.CFG_HORIZIONTAL|BlockDecor.CFG_LOOK_PLACEMENT|BlockDecor.CFG_OPPOSITE_PLACEMENT, - Material.WOOD, 1.0f, 15f, SoundType.WOOD, - ModAuxiliaries.getPixeledAABB(1,0,1, 15,15.9,15) + public static final BlockDecorDirected TREATED_WOOD_WINDOWSILL = new BlockDecorDirected( + "treated_wood_windowsill", + BlockDecor.CFG_CUTOUT|BlockDecor.CFG_HORIZIONTAL|BlockDecor.CFG_FACING_PLACEMENT, + Material.WOOD, 1.0f, 10f, SoundType.WOOD, + ModAuxiliaries.getPixeledAABB(0.5,15,10.5, 15.5,16,16) ); public static final BlockDecorDirected INSET_LIGHT_IRON = new BlockDecorDirected( @@ -158,13 +247,22 @@ public class ModBlocks ModAuxiliaries.getPixeledAABB(5.2,5.2,15.7, 10.8,10.8,16.0) ); - public static final BlockDecorDirected TREATED_WOOD_WINDOWSILL = new BlockDecorDirected( - "treated_wood_windowsill", - BlockDecor.CFG_CUTOUT|BlockDecor.CFG_HORIZIONTAL|BlockDecor.CFG_FACING_PLACEMENT, - Material.WOOD, 1.0f, 10f, SoundType.WOOD, - ModAuxiliaries.getPixeledAABB(0.5,15,10.5, 15.5,16,16) + public static final BlockDecor STEEL_TABLE = new BlockDecor( + "steel_table", + BlockDecor.CFG_CUTOUT|BlockDecor.CFG_HORIZIONTAL|BlockDecor.CFG_LOOK_PLACEMENT, + Material.IRON, 1.0f, 15f, SoundType.METAL, + ModAuxiliaries.getPixeledAABB(0,0,0, 16,15.9,16) ); + public static final BlockDecorFloorGrating STEEL_FLOOR_GRATING = new BlockDecorFloorGrating( + "steel_floor_grating", + BlockDecor.CFG_CUTOUT, + Material.IRON, 1.0f, 15f, SoundType.METAL, + ModAuxiliaries.getPixeledAABB(0,14,0, 16,16,16) + ); + + //-------------------------------------------------------------------------------------------------------------------- + public static final BlockDecorWindow TREATED_WOOD_WINDOW = new BlockDecorWindow( "treated_wood_window", BlockDecor.CFG_TRANSLUCENT|BlockDecor.CFG_LOOK_PLACEMENT, @@ -179,20 +277,7 @@ public class ModBlocks ModAuxiliaries.getPixeledAABB(0,0,7.5, 16,16,8.5) ); - public static final BlockDecorFurnace SMALL_LAB_FURNACE = new BlockDecorFurnace( - "small_lab_furnace", - BlockDecor.CFG_CUTOUT|BlockDecor.CFG_HORIZIONTAL|BlockDecor.CFG_LOOK_PLACEMENT|BlockDecor.CFG_OPPOSITE_PLACEMENT| - BlockDecor.CFG_ELECTRICAL, - Material.IRON, 0.5f, 15f, SoundType.METAL, - ModAuxiliaries.getPixeledAABB(1,0,1, 15,15,16) - ); - - public static final BlockDecorFurnaceElectrical SMALL_ELECTRICAL_FURNACE = new BlockDecorFurnaceElectrical( - "small_electrical_furnace", - BlockDecor.CFG_CUTOUT|BlockDecor.CFG_HORIZIONTAL|BlockDecor.CFG_LOOK_PLACEMENT|BlockDecor.CFG_ELECTRICAL, - Material.IRON, 0.5f, 15f, SoundType.METAL, - ModAuxiliaries.getPixeledAABB(0,0,0, 16,16,16) - ); + //-------------------------------------------------------------------------------------------------------------------- public static final BlockDecorDirected SIGN_MODLOGO = new BlockDecorDirected( "sign_decor", @@ -201,66 +286,6 @@ public class ModBlocks ModAuxiliaries.getPixeledAABB(0,0,15.6, 16,16,16) ); - public static final BlockDecorHorizontalSupport STEEL_DOUBLE_T_SUPPORT = new BlockDecorHorizontalSupport( - "steel_double_t_support", - BlockDecor.CFG_CUTOUT|BlockDecor.CFG_HORIZIONTAL|BlockDecor.CFG_LOOK_PLACEMENT, - Material.IRON, 2.0f, 15f, SoundType.METAL, - ModAuxiliaries.getPixeledAABB(5,11,0, 11,16,16) - ); - - public static final BlockDecorPipeValve STRAIGHT_CHECK_VALVE = new BlockDecorPipeValve( - "straight_pipe_valve", - BlockDecor.CFG_FACING_PLACEMENT|BlockDecor.CFG_OPPOSITE_PLACEMENT|BlockDecor.CFG_FLIP_PLACEMENT_SHIFTCLICK| - BlockDecor.CFG_CUTOUT, - Material.IRON, 0.7f, 15f, SoundType.METAL, - ModAuxiliaries.getPixeledAABB(4,4,0, 12,12,16) - ); - - public static final BlockDecorPipeValve STRAIGHT_REDSTONE_VALVE = new BlockDecorPipeValve( - "straight_pipe_valve_redstone", - BlockDecor.CFG_FACING_PLACEMENT|BlockDecor.CFG_OPPOSITE_PLACEMENT|BlockDecor.CFG_FLIP_PLACEMENT_SHIFTCLICK| - BlockDecor.CFG_CUTOUT|BlockDecor.CFG_REDSTONE_CONTROLLED, - Material.IRON, 0.7f, 15f, SoundType.METAL, - ModAuxiliaries.getPixeledAABB(4,4,0, 12,12,16) - ); - - public static final BlockDecorPipeValve STRAIGHT_REDSTONE_ANALOG_VALVE = new BlockDecorPipeValve( - "straight_pipe_valve_redstone_analog", - BlockDecor.CFG_FACING_PLACEMENT|BlockDecor.CFG_OPPOSITE_PLACEMENT|BlockDecor.CFG_FLIP_PLACEMENT_SHIFTCLICK| - BlockDecor.CFG_CUTOUT|BlockDecor.CFG_REDSTONE_CONTROLLED|BlockDecor.CFG_ANALOG, - Material.IRON, 0.7f, 15f, SoundType.METAL, - ModAuxiliaries.getPixeledAABB(4,4,0, 12,12,16) - ); - - public static final BlockDecorPassiveFluidAccumulator PASSIVE_FLUID_ACCUMULATOR = new BlockDecorPassiveFluidAccumulator( - "passive_fluid_accumulator", - BlockDecor.CFG_FACING_PLACEMENT|BlockDecor.CFG_OPPOSITE_PLACEMENT|BlockDecor.CFG_FLIP_PLACEMENT_SHIFTCLICK| - BlockDecor.CFG_CUTOUT, - Material.IRON, 0.7f, 15f, SoundType.METAL, - ModAuxiliaries.getPixeledAABB(0,0,0, 16,16,16) - ); - - public static final BlockDecorWasteIncinerator SMALL_WASTE_INCINERATOR = new BlockDecorWasteIncinerator( - "small_waste_incinerator", - BlockDecor.CFG_DEFAULT|BlockDecor.CFG_ELECTRICAL, - Material.IRON, 1f, 15f, SoundType.METAL, - ModAuxiliaries.getPixeledAABB(0,0,0, 16,16,16) - ); - - public static final BlockDecorDropper FACTORY_DROPPER = new BlockDecorDropper( - "factory_dropper", - BlockDecor.CFG_LOOK_PLACEMENT|BlockDecor.CFG_REDSTONE_CONTROLLED, - Material.IRON, 1f, 15f, SoundType.METAL, - ModAuxiliaries.getPixeledAABB(0,0,0, 16,16,15) - ); - - public static final BlockDecorMineralSmelter SMALL_MINERAL_SMELTER = new BlockDecorMineralSmelter( - "small_mineral_smelter", - BlockDecor.CFG_LOOK_PLACEMENT, - Material.IRON, 1f, 15f, SoundType.METAL, - ModAuxiliaries.getPixeledAABB(1.1,0,1.1, 14.9,16,14.9) - ); - public static final BlockDecorDirected SIGN_HOTWIRE = new BlockDecorDirected( "sign_hotwire", BlockDecor.CFG_CUTOUT|BlockDecor.CFG_OPPOSITE_PLACEMENT|(1< TET_TREATED_WOOD_CRAFTING_TABLE = TileEntityType.Builder + .create(BlockDecorCraftingTable.BTileEntity::new, TREATED_WOOD_CRAFTING_TABLE) + .build(null) + .setRegistryName(ModEngineersDecor.MODID, "te_treated_wood_crafting_table"); + + public static final TileEntityType TET_SMALL_LAB_FURNACE = TileEntityType.Builder + .create(BlockDecorFurnace.BTileEntity::new, SMALL_LAB_FURNACE) + .build(null) + .setRegistryName(ModEngineersDecor.MODID, "te_small_lab_furnace"); + + public static final TileEntityType TET_SMALL_ELECTRICAL_FURNACE = TileEntityType.Builder + .create(BlockDecorFurnaceElectrical.BTileEntity::new, SMALL_ELECTRICAL_FURNACE) + .build(null) + .setRegistryName(ModEngineersDecor.MODID, "te_small_electrical_furnace"); + + public static final TileEntityType TET_FACTORY_DROPPER = TileEntityType.Builder + .create(BlockDecorDropper.BTileEntity::new, FACTORY_DROPPER) + .build(null) + .setRegistryName(ModEngineersDecor.MODID, "te_factory_dropper"); + + public static final TileEntityType TET_WASTE_INCINERATOR = TileEntityType.Builder + .create(BlockDecorWasteIncinerator.BTileEntity::new, SMALL_WASTE_INCINERATOR) + .build(null) + .setRegistryName(ModEngineersDecor.MODID, "te_small_waste_incinerator"); + + public static final TileEntityType TET_STRAIGHT_PIPE_VALVE = TileEntityType.Builder + .create(BlockDecorPipeValve.BTileEntity::new, STRAIGHT_CHECK_VALVE, STRAIGHT_REDSTONE_VALVE, STRAIGHT_REDSTONE_ANALOG_VALVE) + .build(null) + .setRegistryName(ModEngineersDecor.MODID, "te_pipe_valve"); + + public static final TileEntityType TET_PASSIVE_FLUID_ACCUMULATOR = TileEntityType.Builder + .create(BlockDecorPassiveFluidAccumulator.BTileEntity::new, PASSIVE_FLUID_ACCUMULATOR) + .build(null) + .setRegistryName(ModEngineersDecor.MODID, "te_passive_fluid_accumulator"); + + public static final TileEntityType TET_MINERAL_SMELTER = TileEntityType.Builder + .create(BlockDecorMineralSmelter.BTileEntity::new, SMALL_MINERAL_SMELTER) + .build(null) + .setRegistryName(ModEngineersDecor.MODID, "te_small_mineral_smelter"); + + + private static final TileEntityType tile_entity_types[] = { + TET_TREATED_WOOD_CRAFTING_TABLE, + TET_SMALL_LAB_FURNACE, + TET_FACTORY_DROPPER, + TET_SMALL_ELECTRICAL_FURNACE, + TET_WASTE_INCINERATOR, + TET_STRAIGHT_PIPE_VALVE, + TET_PASSIVE_FLUID_ACCUMULATOR, + TET_MINERAL_SMELTER + }; + + //-------------------------------------------------------------------------------------------------------------------- + // Entities bound exclusively to the blocks above + //-------------------------------------------------------------------------------------------------------------------- + + public static final EntityType ET_CHAIR = EntityType.Builder + .create(BlockDecorChair.EntityChair::new, EntityClassification.MISC) + .immuneToFire().size(1e-3f, 1e-3f).disableSerialization() + .setShouldReceiveVelocityUpdates(false).setUpdateInterval(4) + .setCustomClientFactory(BlockDecorChair.EntityChair::customClientFactory) + .build(new ResourceLocation(ModEngineersDecor.MODID, "et_chair").toString()) + .setRegistryName(new ResourceLocation(ModEngineersDecor.MODID, "et_chair")) + ; + + private static final EntityType entity_types[] = { + ET_CHAIR + }; + + //-------------------------------------------------------------------------------------------------------------------- + // Container registration + //-------------------------------------------------------------------------------------------------------------------- + + public static final ContainerType CT_TREATED_WOOD_CRAFTING_TABLE; + public static final ContainerType CT_FACTORY_DROPPER; + public static final ContainerType CT_SMALL_LAB_FURNACE; + public static final ContainerType CT_SMALL_ELECTRICAL_FURNACE; + public static final ContainerType CT_WASTE_INCINERATOR; + + static { + CT_TREATED_WOOD_CRAFTING_TABLE = (new ContainerType(BlockDecorCraftingTable.BContainer::new)); + CT_TREATED_WOOD_CRAFTING_TABLE.setRegistryName(ModEngineersDecor.MODID,"ct_treated_wood_crafting_table"); + CT_FACTORY_DROPPER = (new ContainerType(BlockDecorDropper.BContainer::new)); + CT_FACTORY_DROPPER.setRegistryName(ModEngineersDecor.MODID,"ct_factory_dropper"); + CT_SMALL_LAB_FURNACE = (new ContainerType(BlockDecorFurnace.BContainer::new)); + CT_SMALL_LAB_FURNACE.setRegistryName(ModEngineersDecor.MODID,"ct_small_lab_furnace"); + CT_SMALL_ELECTRICAL_FURNACE = (new ContainerType(BlockDecorFurnaceElectrical.BContainer::new)); + CT_SMALL_ELECTRICAL_FURNACE.setRegistryName(ModEngineersDecor.MODID,"ct_small_electrical_furnace"); + CT_WASTE_INCINERATOR = (new ContainerType(BlockDecorWasteIncinerator.BContainer::new)); + CT_WASTE_INCINERATOR.setRegistryName(ModEngineersDecor.MODID,"ct_small_waste_incinerator"); + } + + // DON'T FORGET TO REGISTER THE GUI in registerContainerGuis(), no list/map format found yet for that. + private static final ContainerType container_types[] = { + CT_TREATED_WOOD_CRAFTING_TABLE, + CT_FACTORY_DROPPER, + CT_SMALL_LAB_FURNACE, + CT_SMALL_ELECTRICAL_FURNACE, + CT_WASTE_INCINERATOR + }; + + //-------------------------------------------------------------------------------------------------------------------- + // Initialisation events + //-------------------------------------------------------------------------------------------------------------------- + + private static ArrayList registeredBlocks = new ArrayList<>(); + + public static boolean isExperimentalBlock(Block block) + { return ArrayUtils.contains(devBlocks, block); } + + @Nonnull + public static List getRegisteredBlocks() + { return Collections.unmodifiableList(registeredBlocks); } + + public static final void registerBlocks(final RegistryEvent.Register event) + { + ArrayList allBlocks = new ArrayList<>(); + Collections.addAll(allBlocks, modBlocks); + if(ModAuxiliaries.isModLoaded("immersiveengineering")) ModAuxiliaries.logInfo("Immersive Engineering also installed ..."); + // @todo: config not available yet, other registration control for experimental features needed. + Collections.addAll(allBlocks, devBlocks); + registeredBlocks.addAll(allBlocks); + for(Block e:registeredBlocks) event.getRegistry().register(e); + ModAuxiliaries.logInfo("Registered " + Integer.toString(registeredBlocks.size()) + " blocks."); + } + + public static final void registerItemBlocks(final RegistryEvent.Register event) + { + int n = 0; + for(Block e:registeredBlocks) { + ResourceLocation rl = e.getRegistryName(); + if(rl == null) continue; + event.getRegistry().register(new BlockItem(e, (new BlockItem.Properties().group(ModEngineersDecor.ITEMGROUP))).setRegistryName(rl)); + ++n; + } + } + + public static final void registerTileEntities(final RegistryEvent.Register> event) + { + int n_registered = 0; + for(final TileEntityType e:tile_entity_types) { + event.getRegistry().register(e); + ++n_registered; + } + ModAuxiliaries.logInfo("Registered " + Integer.toString(n_registered) + " tile entities."); + } + + public static final void registerEntities(final RegistryEvent.Register> event) + { + int n_registered = 0; + for(final EntityType e:entity_types) { + if((e==ET_CHAIR) && (!registeredBlocks.contains(TREATED_WOOD_STOOL))) continue; + event.getRegistry().register(e); + ++n_registered; + } + ModAuxiliaries.logInfo("Registered " + Integer.toString(n_registered) + " entities bound to blocks."); + } + + public static final void registerContainers(final RegistryEvent.Register> event) + { + int n_registered = 0; + for(final ContainerType e:container_types) { + event.getRegistry().register(e); + ++n_registered; + } + ModAuxiliaries.logInfo("Registered " + Integer.toString(n_registered) + " containers bound to tile entities."); + } + + @OnlyIn(Dist.CLIENT) + public static final void registerContainerGuis(final FMLClientSetupEvent event) + { + ScreenManager.registerFactory(CT_TREATED_WOOD_CRAFTING_TABLE, BlockDecorCraftingTable.BGui::new); + ScreenManager.registerFactory(CT_FACTORY_DROPPER, BlockDecorDropper.BGui::new); + ScreenManager.registerFactory(CT_SMALL_LAB_FURNACE, BlockDecorFurnace.BGui::new); + ScreenManager.registerFactory(CT_SMALL_ELECTRICAL_FURNACE, BlockDecorFurnaceElectrical.BGui::new); + ScreenManager.registerFactory(CT_WASTE_INCINERATOR, BlockDecorWasteIncinerator.BGui::new); + } + +} diff --git a/1.14/src/main/java/wile/engineersdecor/ModEngineersDecor.java b/1.14/src/main/java/wile/engineersdecor/ModEngineersDecor.java new file mode 100644 index 0000000..ed1c480 --- /dev/null +++ b/1.14/src/main/java/wile/engineersdecor/ModEngineersDecor.java @@ -0,0 +1,152 @@ +package wile.engineersdecor; + +import wile.engineersdecor.detail.ModConfig; +import wile.engineersdecor.detail.RecipeCondModSpecific; +import wile.engineersdecor.detail.Networking; +import wile.engineersdecor.blocks.*; +import net.minecraft.client.Minecraft; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.inventory.container.ContainerType; +import net.minecraft.item.ItemGroup; +import net.minecraft.tileentity.TileEntityType; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; +import net.minecraft.block.Block; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraftforge.common.crafting.CraftingHelper; +import net.minecraftforge.event.entity.living.LivingEvent; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.DistExecutor; +import net.minecraftforge.fml.ModLoadingContext; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; +import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; +import net.minecraftforge.fml.event.lifecycle.InterModEnqueueEvent; +import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent; +import net.minecraftforge.fml.event.server.FMLServerStartingEvent; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.annotation.Nullable; + + +@Mod("engineersdecor") +public class ModEngineersDecor +{ + public static final String MODID = "engineersdecor"; + public static final int VERSION_DATAFIXER = 0; + private static final Logger LOGGER = LogManager.getLogger(); + + public ModEngineersDecor() + { + FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onSetup); + FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onSendImc); + FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onRecvImc); + FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onClientSetup); + ModLoadingContext.get().registerConfig(net.minecraftforge.fml.config.ModConfig.Type.COMMON, ModConfig.COMMON_CONFIG_SPEC); + ModLoadingContext.get().registerConfig(net.minecraftforge.fml.config.ModConfig.Type.CLIENT, ModConfig.CLIENT_CONFIG_SPEC); + MinecraftForge.EVENT_BUS.register(this); + } + + public static final Logger logger() { return LOGGER; } + + // + // Events + // + + private void onSetup(final FMLCommonSetupEvent event) + { + LOGGER.info("Registering recipe condition processor ..."); + CraftingHelper.register(new ResourceLocation(MODID, "grc"), new RecipeCondModSpecific()); + Networking.init(); + } + + private void onClientSetup(final FMLClientSetupEvent event) + { ModContent.registerContainerGuis(event); } + + private void onSendImc(final InterModEnqueueEvent event) + {} + + private void onRecvImc(final InterModProcessEvent event) + {} + + @SubscribeEvent + public void onServerStarting(FMLServerStartingEvent event) + {} + + @Mod.EventBusSubscriber(bus=Mod.EventBusSubscriber.Bus.MOD) + public static class RegistryEvents + { + @SubscribeEvent + public static void onBlocksRegistry(final RegistryEvent.Register event) + { ModContent.registerBlocks(event); } + + @SubscribeEvent + public static void onItemRegistry(final RegistryEvent.Register event) + { ModContent.registerItemBlocks(event); } + + @SubscribeEvent + public static void onTileEntityRegistry(final RegistryEvent.Register> event) + { ModContent.registerTileEntities(event); } + + @SubscribeEvent + public static void onRegisterEntityTypes(final RegistryEvent.Register> event) + { ModContent.registerEntities(event); } + + @SubscribeEvent + public static void onRegisterContainerTypes(final RegistryEvent.Register> event) + { ModContent.registerContainers(event); } + } + + // + // Sided proxy functionality (skel) + // + public static ISidedProxy proxy = DistExecutor.runForDist(()->ClientProxy::new, ()->ServerProxy::new); + public interface ISidedProxy + { + default @Nullable PlayerEntity getPlayerClientSide() { return null; } + default @Nullable World getWorldClientSide() { return null; } + default @Nullable Minecraft mc() { return null; } + } + public static final class ClientProxy implements ISidedProxy + { + public @Nullable PlayerEntity getPlayerClientSide() { return Minecraft.getInstance().player; } + public @Nullable World getWorldClientSide() { return Minecraft.getInstance().world; } + public @Nullable Minecraft mc() { return Minecraft.getInstance(); } + } + public static final class ServerProxy implements ISidedProxy + { + public @Nullable PlayerEntity getPlayerClientSide() { return null; } + public @Nullable World getWorldClientSide() { return null; } + public @Nullable Minecraft mc() { return null; } + } + + // + // Item group / creative tab + // + public static final ItemGroup ITEMGROUP = (new ItemGroup("tab" + MODID) { + @OnlyIn(Dist.CLIENT) + public ItemStack createIcon() + { return new ItemStack(ModContent.SIGN_MODLOGO); } + }); + + // + // Player update event + // + @SubscribeEvent + public void onPlayerEvent(final LivingEvent.LivingUpdateEvent event) + { + if(!(event.getEntity() instanceof PlayerEntity)) return; + final PlayerEntity player = (PlayerEntity)event.getEntity(); + if(player.world == null) return; + if(player.isOnLadder()) BlockDecorLadder.onPlayerUpdateEvent(player); + } + +} diff --git a/1.14/src/main/java/wile/engineersdecor/blocks/BlockDecor.java b/1.14/src/main/java/wile/engineersdecor/blocks/BlockDecor.java new file mode 100644 index 0000000..0374051 --- /dev/null +++ b/1.14/src/main/java/wile/engineersdecor/blocks/BlockDecor.java @@ -0,0 +1,133 @@ +/* + * @file BlockDecorFull.java + * @author Stefan Wilhelm (wile) + * @copyright (C) 2019 Stefan Wilhelm + * @license MIT (see https://opensource.org/licenses/MIT) + * + * Common functionality class for decor blocks. + * Mainly needed for: + * - MC block defaults. + * - Tooltip functionality + * - Model initialisation + */ +package wile.engineersdecor.blocks; + +import wile.engineersdecor.detail.ModAuxiliaries; +import net.minecraft.entity.item.ItemEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.fluid.IFluidState; +import net.minecraft.world.Explosion; +import net.minecraft.world.World; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.shapes.ISelectionContext; +import net.minecraft.util.math.shapes.VoxelShape; +import net.minecraft.util.math.shapes.VoxelShapes; +import net.minecraft.block.Block; +import net.minecraft.block.material.PushReaction; +import net.minecraft.block.BlockState; +import net.minecraft.item.ItemStack; +import net.minecraft.world.IBlockReader; +import net.minecraft.world.storage.loot.LootContext; +import net.minecraft.client.util.ITooltipFlag; +import net.minecraft.util.*; +import net.minecraft.util.text.ITextComponent; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.List; + + +public class BlockDecor extends Block implements IDecorBlock +{ + public static final long CFG_DEFAULT = 0x0000000000000000L; // no special config + public static final long CFG_CUTOUT = 0x0000000000000001L; // cutout rendering + public static final long CFG_HORIZIONTAL = 0x0000000000000002L; // horizontal block, affects bounding box calculation at construction time and placement + public static final long CFG_LOOK_PLACEMENT = 0x0000000000000004L; // placed in direction the player is looking when placing. + public static final long CFG_FACING_PLACEMENT = 0x0000000000000008L; // placed on the facing the player has clicked. + public static final long CFG_OPPOSITE_PLACEMENT = 0x0000000000000010L; // placed placed in the opposite direction of the face the player clicked. + public static final long CFG_FLIP_PLACEMENT_IF_SAME = 0x0000000000000020L; // placement direction flipped if an instance of the same class was clicked + public static final long CFG_FLIP_PLACEMENT_SHIFTCLICK = 0x0000000000000040L; // placement direction flipped if player is sneaking + public static final long CFG_TRANSLUCENT = 0x0000000000000080L; // indicates a block/pane is glass like (transparent, etc) + public static final long CFG_ELECTRICAL = 0x0000000000010000L; // Denotes if a component is mainly flux driven. + public static final long CFG_REDSTONE_CONTROLLED = 0x0000000000020000L; // Denotes if a component has somehow a redstone control input + public static final long CFG_ANALOG = 0x0000000000040000L; // Denotes if a component has analog behaviour + public static final long CFG_HARD_IE_DEPENDENT = 0x8000000000000000L; // The block is implicitly opt'ed out if IE is not installed + + public final long config; + public final VoxelShape vshape; + + public BlockDecor(long config, Block.Properties properties) + { this(config, properties, ModAuxiliaries.getPixeledAABB(0, 0, 0, 16, 16,16 )); } + + public BlockDecor(long config, Block.Properties properties, AxisAlignedBB aabb) + { super(properties); this.config = config; this.vshape = VoxelShapes.create(aabb); } + + public BlockDecor(long config, Block.Properties properties, VoxelShape voxel_shape) + { super(properties); this.config = config; this.vshape = voxel_shape; } + + @Override + @OnlyIn(Dist.CLIENT) + public void addInformation(ItemStack stack, @Nullable IBlockReader world, List tooltip, ITooltipFlag flag) + { ModAuxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true); } + + @Override + @OnlyIn(Dist.CLIENT) + public BlockRenderLayer getRenderLayer() + { return ((config & CFG_CUTOUT)!=0) ? BlockRenderLayer.CUTOUT : BlockRenderLayer.SOLID; } + + @Override + @SuppressWarnings("deprecation") + public VoxelShape getShape(BlockState state, IBlockReader source, BlockPos pos, ISelectionContext selectionContext) + { return vshape; } + + @Override + @SuppressWarnings("deprecation") + public VoxelShape getCollisionShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext selectionContext) + { return vshape; } + + @Override + public boolean canSpawnInBlock() + { return false; } + + @Override + @SuppressWarnings("deprecation") + public PushReaction getPushReaction(BlockState state) + { return PushReaction.NORMAL; } + + @Override + @SuppressWarnings("deprecation") + public void onReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean isMoving) + { + if(state.hasTileEntity() && (state.getBlock() != newState.getBlock())) { + world.removeTileEntity(pos); + world.updateComparatorOutputLevel(pos, this); + } + } + + public static boolean dropBlock(BlockState state, World world, BlockPos pos, boolean explosion) + { + if(!(state.getBlock() instanceof IDecorBlock)) { world.removeBlock(pos, false); return true; } + if(!world.isRemote()) { + ((IDecorBlock)state.getBlock()).dropList(state, world, pos, explosion).forEach(stack->world.addEntity(new ItemEntity(world, pos.getX()+0.5, pos.getY()+0.5, pos.getZ()+0.5, stack))); + } + if(state.getBlock().hasTileEntity(state)) world.removeTileEntity(pos); + world.removeBlock(pos, false); + return true; + } + + @Override + public boolean removedByPlayer(BlockState state, World world, BlockPos pos, PlayerEntity player, boolean willHarvest, IFluidState fluid) + { return dropBlock(state, world, pos, false); } + + @Override + public void onExplosionDestroy(World world, BlockPos pos, Explosion explosion) + { dropBlock(world.getBlockState(pos), world, pos, false); } + + @Override + @SuppressWarnings("deprecation") + public List getDrops(BlockState state, LootContext.Builder builder) + { return Collections.singletonList(ItemStack.EMPTY); } // { return Collections.singletonList(new ItemStack(this.asItem())); } // + +} diff --git a/1.14/src/main/java/wile/engineersdecor/blocks/BlockDecorChair.java b/1.14/src/main/java/wile/engineersdecor/blocks/BlockDecorChair.java new file mode 100644 index 0000000..c5d39bf --- /dev/null +++ b/1.14/src/main/java/wile/engineersdecor/blocks/BlockDecorChair.java @@ -0,0 +1,190 @@ +/* + * @file BlockDecorFull.java + * @author Stefan Wilhelm (wile) + * @copyright (C) 2019 Stefan Wilhelm + * @license MIT (see https://opensource.org/licenses/MIT) + * + * Full block characteristics class. + */ +package wile.engineersdecor.blocks; + +import wile.engineersdecor.ModContent; +import wile.engineersdecor.ModEngineersDecor; +import net.minecraft.entity.*; +import net.minecraft.entity.monster.*; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.network.IPacket; +import net.minecraft.util.math.*; +import net.minecraft.world.IWorldReader; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.util.*; +import net.minecraft.world.World; +import net.minecraftforge.fml.network.FMLPlayMessages; +import net.minecraftforge.fml.network.NetworkHooks; + +import java.util.List; +import java.util.Random; + + +public class BlockDecorChair extends BlockDecorDirected +{ + private static boolean sitting_enabled = true; + private static double sitting_probability = 0.1; + private static double standup_probability = 0.01; + + public static void on_config(boolean without_sitting, boolean without_mob_sitting, double sitting_probability_percent, double standup_probability_percent) + { + sitting_enabled = (!without_sitting); + sitting_probability = (without_sitting||without_mob_sitting) ? 0.0 : MathHelper.clamp(sitting_probability_percent/100, 0, 0.9); + standup_probability = (without_sitting||without_mob_sitting) ? 1.0 : MathHelper.clamp(standup_probability_percent/100, 1e-6, 1e-2); + ModEngineersDecor.logger().info("Config chairs: " + sitting_enabled + ", sit: " + sitting_probability, ", stand up: " + standup_probability); + } + + public BlockDecorChair(long config, Block.Properties builder, final AxisAlignedBB unrotatedAABB) + { super(config, builder.tickRandomly(), unrotatedAABB); } + + @Override + @SuppressWarnings("deprecation") + public boolean onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult rayTraceResult) + { + if(sitting_enabled && (!world.isRemote)) { EntityChair.sit(world, player, pos); } + return true; + } + + @Override + @SuppressWarnings("deprecation") + public void onEntityCollision(BlockState state, World world, BlockPos pos, Entity entity) + { + if(sitting_enabled && (Math.random() < sitting_probability) && (entity instanceof MobEntity)) EntityChair.sit(world, (LivingEntity)entity, pos); + } + + @Override + public int tickRate(IWorldReader world) + { return 10; } + + @Override + @SuppressWarnings("deprecation") + public void tick(BlockState state, World world, BlockPos pos, Random rnd) + { + if((!sitting_enabled) || (sitting_probability < 1e-6)) return; + final List entities = world.getEntitiesWithinAABB(MobEntity.class, new AxisAlignedBB(pos).grow(2,1,2).expand(0,1,0), e->true); + if(entities.isEmpty()) return; + int index = rnd.nextInt(entities.size()); + if((index < 0) || (index >= entities.size())) return; + EntityChair.sit(world, entities.get(index), pos); + } + + //-------------------------------------------------------------------------------------------------------------------- + // Riding entity for sitting + //-------------------------------------------------------------------------------------------------------------------- + + public static class EntityChair extends Entity + { + public static final double x_offset = 0.5d; + public static final double y_offset = 0.4d; + public static final double z_offset = 0.5d; + private int t_sit = 0; + public BlockPos chair_pos = new BlockPos(0,0,0); + + public EntityChair(EntityType entityType, World world) + { + super(entityType, world); + preventEntitySpawning=true; + setMotion(Vec3d.ZERO); + canUpdate(true); + noClip=true; + } + + public EntityChair(World world) + { this(ModContent.ET_CHAIR, world); } + + public static EntityChair customClientFactory(FMLPlayMessages.SpawnEntity spkt, World world) + { return new EntityChair(world); } + + public IPacket createSpawnPacket() + { return NetworkHooks.getEntitySpawningPacket(this); } + + public static boolean accepts_mob(LivingEntity entity) + { + if(!(entity instanceof net.minecraft.entity.monster.MonsterEntity)) return false; + if((entity.getType().getSize().height > 2.5) || (entity.getType().getSize().height > 2.0)) return false; + if(entity instanceof ZombieEntity) return true; + if(entity instanceof ZombieVillagerEntity) return true; + if(entity instanceof ZombiePigmanEntity) return true; + if(entity instanceof HuskEntity) return true; + if(entity instanceof StrayEntity) return true; + if(entity instanceof SkeletonEntity) return true; + if(entity instanceof WitherSkeletonEntity) return true; + return false; + } + + public static void sit(World world, LivingEntity sitter, BlockPos pos) + { + if(!sitting_enabled) return; + if((world==null) || (world.isRemote) || (sitter==null) || (pos==null)) return; + if((!(sitter instanceof PlayerEntity)) && (!accepts_mob(sitter))) return; + if(!world.getEntitiesWithinAABB(EntityChair.class, new AxisAlignedBB(pos)).isEmpty()) return; + if(sitter.isBeingRidden() || (!sitter.isAlive()) || (sitter.isPassenger()) ) return; + if((!world.isAirBlock(pos.up())) || (!world.isAirBlock(pos.up(2)))) return; + boolean on_top_of_block_position = true; + boolean use_next_negative_y_position = false; + EntityChair chair = new EntityChair(world); + chair.chair_pos = pos; + chair.t_sit = 5; + chair.prevPosX = chair.posX; + chair.prevPosY = chair.posY; + chair.prevPosZ = chair.posZ; + chair.setPosition(pos.getX()+x_offset,pos.getY()+y_offset,pos.getZ()+z_offset); + world.addEntity(chair); + sitter.startRiding(chair, true); + } + + @Override + protected void registerData() {} + + @Override + protected void readAdditional(CompoundNBT compound) {} + + @Override + protected void writeAdditional(CompoundNBT compound) {} + + @Override + protected boolean canTriggerWalking() + { return false; } + + @Override + public boolean canBePushed() + { return false; } + + @Override + public double getMountedYOffset() + { return 0.0; } + + @Override + public void tick() + { + if(world.isRemote) return; + super.tick(); + if(--t_sit > 0) return; + Entity sitter = getPassengers().isEmpty() ? null : getPassengers().get(0); + if((sitter==null) || (!sitter.isAlive())) { + this.remove(); + return; + } + boolean abort = (!sitting_enabled); + final BlockState state = world.getBlockState(chair_pos); + if((state==null) || (!(state.getBlock() instanceof BlockDecorChair))) abort = true; + if(!world.isAirBlock(chair_pos.up())) abort = true; + if((!(sitter instanceof PlayerEntity)) && (Math.random() < standup_probability)) abort = true; + if(abort) { + for(Entity e:getPassengers()) { + if(e.isAlive()) e.stopRiding(); + } + this.remove(); + } + } + } + +} diff --git a/1.14/src/main/java/wile/engineersdecor/blocks/BlockDecorCraftingTable.java b/1.14/src/main/java/wile/engineersdecor/blocks/BlockDecorCraftingTable.java new file mode 100644 index 0000000..0d28e3a --- /dev/null +++ b/1.14/src/main/java/wile/engineersdecor/blocks/BlockDecorCraftingTable.java @@ -0,0 +1,1237 @@ +/* + * @file BlockDecorDirected.java + * @author Stefan Wilhelm (wile) + * @copyright (C) 2019 Stefan Wilhelm + * @license MIT (see https://opensource.org/licenses/MIT) + * + * Crafting table + */ +package wile.engineersdecor.blocks; + +import wile.engineersdecor.ModContent; +import wile.engineersdecor.ModEngineersDecor; +import wile.engineersdecor.detail.Networking; +import net.minecraft.client.gui.screen.inventory.ContainerScreen; +import net.minecraft.client.gui.widget.button.ImageButton; +import net.minecraft.inventory.container.*; +import net.minecraft.item.Items; +import net.minecraft.item.crafting.ICraftingRecipe; +import net.minecraft.item.crafting.IRecipeType; +import net.minecraft.item.crafting.Ingredient; +import net.minecraft.network.play.server.SSetSlotPacket; +import net.minecraft.util.math.BlockRayTraceResult; +import net.minecraft.util.text.StringTextComponent; +import net.minecraft.world.*; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.tileentity.TileEntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.IRecipe; +import net.minecraft.inventory.*; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.util.*; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.client.gui.widget.button.Button; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraftforge.fml.network.NetworkHooks; +import net.minecraftforge.common.util.FakePlayer; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import com.mojang.blaze3d.platform.GlStateManager; +import com.google.common.collect.ImmutableList; +import javax.annotation.Nullable; +import java.util.*; + + +public class BlockDecorCraftingTable extends BlockDecorDirected +{ + public static boolean with_assist = true; + public static boolean with_assist_direct_history_refab = false; + public static boolean with_assist_quickmove_buttons = false; + + public static final void on_config(boolean without_crafting_assist, boolean with_assist_immediate_history_refab, boolean with_quickmove_buttons) + { + with_assist = !without_crafting_assist; + with_assist_direct_history_refab = with_assist_immediate_history_refab; + with_assist_quickmove_buttons = with_quickmove_buttons; + CraftingHistory.max_history_size(32); + } + + public BlockDecorCraftingTable(long config, Block.Properties builder, final AxisAlignedBB unrotatedAABB) + { super(config, builder, unrotatedAABB); } + + @Override + public boolean hasTileEntity(BlockState state) + { return true; } + + @Override + @Nullable + public TileEntity createTileEntity(BlockState state, IBlockReader world) + { return new BlockDecorCraftingTable.BTileEntity(); } + + @Override + @SuppressWarnings("deprecation") + public boolean onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult rayTraceResult) + { + if(world.isRemote) return true; + final TileEntity te = world.getTileEntity(pos); + if(!(te instanceof BTileEntity)) return true; + if((!(player instanceof ServerPlayerEntity) && (!(player instanceof FakePlayer)))) return true; + NetworkHooks.openGui((ServerPlayerEntity)player,(INamedContainerProvider)te); + return true; + } + + @Override + public void onBlockPlacedBy(World world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) + { + if(world.isRemote) return; + if(!stack.hasTag()) return; + if((!stack.hasTag()) || (!stack.getTag().contains("inventory"))) return; + CompoundNBT inventory_nbt = stack.getTag().getCompound("inventory"); + if(inventory_nbt.isEmpty()) return; + final TileEntity te = world.getTileEntity(pos); + if(!(te instanceof BTileEntity)) return; + ((BTileEntity)te).readnbt(inventory_nbt); + ((BTileEntity)te).markDirty(); + } + + @Override + public List dropList(BlockState state, World world, BlockPos pos, boolean explosion) + { + final List stacks = new ArrayList(); + if(world.isRemote) return stacks; + final TileEntity te = world.getTileEntity(pos); + if(!(te instanceof BTileEntity)) return stacks; + if(!explosion) { + ItemStack stack = new ItemStack(this, 1); + CompoundNBT inventory_nbt = new CompoundNBT(); + ItemStackHelper.saveAllItems(inventory_nbt, ((BTileEntity)te).stacks, false); + if(!inventory_nbt.isEmpty()) { + CompoundNBT nbt = new CompoundNBT(); + nbt.put("inventory", inventory_nbt); + stack.setTag(nbt); + } + ((BTileEntity) te).clear(); + stacks.add(stack); + } else { + for(ItemStack stack: ((BTileEntity)te).stacks) { + if(!stack.isEmpty()) stacks.add(stack); + } + ((BTileEntity)te).reset(); + } + return stacks; + } + + //-------------------------------------------------------------------------------------------------------------------- + // Tile entity + //-------------------------------------------------------------------------------------------------------------------- + + public static class BTileEntity extends TileEntity implements IInventory, INameable, INamedContainerProvider + { + public static final int NUM_OF_SLOTS = 9+8; + protected NonNullList stacks = NonNullList.withSize(NUM_OF_SLOTS, ItemStack.EMPTY); + protected CompoundNBT history = new CompoundNBT(); + + public BTileEntity() + { this(ModContent.TET_TREATED_WOOD_CRAFTING_TABLE); } + + public BTileEntity(TileEntityType te_type) + { super(te_type); } + + public void reset() + { stacks = NonNullList.withSize(NUM_OF_SLOTS, ItemStack.EMPTY); } + + public void readnbt(CompoundNBT nbt) + { + reset(); + ItemStackHelper.loadAllItems(nbt, this.stacks); + while(this.stacks.size() < NUM_OF_SLOTS) this.stacks.add(ItemStack.EMPTY); + history = nbt.getCompound("history"); + } + + private void writenbt(CompoundNBT nbt) + { + ItemStackHelper.saveAllItems(nbt, this.stacks); + if(!history.isEmpty()) nbt.put("history", history); + } + + // TileEntity ------------------------------------------------------------------------------ + + @Override + public void read(CompoundNBT compound) + { super.read(compound); readnbt(compound); } + + @Override + public CompoundNBT write(CompoundNBT compound) + { super.write(compound); writenbt(compound); return compound; } + + @Override + public CompoundNBT getUpdateTag() + { CompoundNBT nbt = super.getUpdateTag(); writenbt(nbt); return nbt; } + + @Override + public void onLoad() + {} + + // INameable --------------------------------------------------------------------------- + + @Override + public ITextComponent getName() + { final Block block=getBlockState().getBlock(); return new StringTextComponent((block!=null) ? block.getTranslationKey() : "Treated wood crafting table"); } + + @Override + public boolean hasCustomName() + { return false; } + + @Override + public ITextComponent getCustomName() + { return getName(); } + + // INamedContainerProvider ------------------------------------------------------------------------------ + + @Override + public ITextComponent getDisplayName() + { return INameable.super.getDisplayName(); } + + @Override + public Container createMenu( int id, PlayerInventory inventory, PlayerEntity player ) + { return new BContainer(id, inventory, this, IWorldPosCallable.of(world, pos)); } + + // IInventory ------------------------------------------------------------------------------ + + @Override + public int getSizeInventory() + { return stacks.size(); } + + @Override + public boolean isEmpty() + { for(ItemStack stack: stacks) { if(!stack.isEmpty()) return false; } return true; } + + @Override + public ItemStack getStackInSlot(int index) + { return (index < getSizeInventory()) ? stacks.get(index) : ItemStack.EMPTY; } + + @Override + public ItemStack decrStackSize(int index, int count) + { return ItemStackHelper.getAndSplit(stacks, index, count); } + + @Override + public ItemStack removeStackFromSlot(int index) + { return ItemStackHelper.getAndRemove(stacks, index); } + + @Override + public void setInventorySlotContents(int index, ItemStack stack) + { stacks.set(index, stack); } + + @Override + public int getInventoryStackLimit() + { return 64; } + + @Override + public void markDirty() + { super.markDirty(); } + + @Override + public boolean isUsableByPlayer(PlayerEntity player) + { return getPos().distanceSq(player.getPosition()) < 36; } + + @Override + public void openInventory(PlayerEntity player) + {} + + @Override + public void closeInventory(PlayerEntity player) + { this.markDirty(); } + + @Override + public boolean isItemValidForSlot(int index, ItemStack stack) + { return true; } + + @Override + public void clear() + { stacks.clear(); } + + } + + //-------------------------------------------------------------------------------------------------------------------- + // Crafting container + //-------------------------------------------------------------------------------------------------------------------- + + public static class BContainer extends Container implements Networking.INetworkSynchronisableContainer + { + // Crafting slot of container -------------------------------------------------------------------------------------- + public static class BSlotCrafting extends CraftingResultSlot + { + private final BContainer container; + private final PlayerEntity player; + + public BSlotCrafting(BContainer container, PlayerEntity player, CraftingInventory craftingInventory, IInventory inventoryIn, int slotIndex, int xPosition, int yPosition) + { super(player, craftingInventory, inventoryIn, slotIndex, xPosition, yPosition); this.container = container; this.player=player; } + + @Override + protected void onCrafting(ItemStack stack) + { + if((with_assist) && ((player.world!=null) && (!(player.world.isRemote))) && (!stack.isEmpty())) { + final IRecipe recipe = ((CraftResultInventory)this.inventory).getRecipeUsed(); + final ArrayList grid = new ArrayList(); + grid.add(stack); + for(int i = 0; i < 9; ++i) grid.add(container.inventory_.getStackInSlot(i)); + container.history().add(grid, recipe); + container.history().reset_current(); + container.syncHistory(player); + } + super.onCrafting(stack); + } + } + + // Crafting inventory (needed to allow SlotCrafting to have a InventoryCrafting) ----------------------------------- + public static class BInventoryCrafting extends CraftingInventory + { + protected final Container container; + protected final IInventory inventory; + + public BInventoryCrafting(Container container_, IInventory block_inventory) { + super(container_, 3, 3); + container = container_; + inventory = block_inventory; + } + + @Override + public int getSizeInventory() + { return 9; } + + @Override + public void openInventory(PlayerEntity player) + { inventory.openInventory(player); } + + @Override + public void closeInventory(PlayerEntity player) + { inventory.closeInventory(player); } + + @Override + public void markDirty() + { inventory.markDirty(); } + + @Override + public void setInventorySlotContents(int index, ItemStack stack) + { + inventory.setInventorySlotContents(index, stack); + container.onCraftMatrixChanged(this); + } + + @Override + public ItemStack getStackInSlot(int index) + { return inventory.getStackInSlot(index); } + + @Override + public ItemStack decrStackSize(int index, int count) + { + final ItemStack stack = inventory.decrStackSize(index, count); + if(!stack.isEmpty()) container.onCraftMatrixChanged(this); + return stack; + } + } + + //------------------------------------------------------------------------------------------------------------------ + //------------------------------------------------------------------------------------------------------------------ + public static final int CRAFTING_SLOTS_BEGIN = 0; + public static final int NUM_OF_CRAFTING_SLOTS = 9; + public static final int STORAGE_SLOTS_BEGIN = NUM_OF_CRAFTING_SLOTS; + public static final int NUM_OF_STORAGE_SLOTS = 8; + + public final ImmutableList> CRAFTING_SLOT_COORDINATES; + private final PlayerEntity player_; + private final IInventory inventory_; + private final IWorldPosCallable wpc_; + private final CraftingHistory history_; + private final BInventoryCrafting matrix_; + private final CraftResultInventory result_; + + public BContainer(int cid, PlayerInventory pinv) + { this(cid, pinv, new Inventory(BTileEntity.NUM_OF_SLOTS), IWorldPosCallable.DUMMY); } + + private BContainer(int cid, PlayerInventory pinv, IInventory block_inventory, IWorldPosCallable wpc) + { + super(ModContent.CT_TREATED_WOOD_CRAFTING_TABLE, cid); + wpc_ = wpc; + player_ = pinv.player; + inventory_ = block_inventory; + World world = player_.world; + if(world.isRemote && (inventory_ instanceof BTileEntity)) world = ((BTileEntity)inventory_).getWorld(); + history_ = new CraftingHistory(world); + result_ = new CraftResultInventory(); + matrix_ = new BInventoryCrafting(this, block_inventory); + matrix_.openInventory(player_); + // container slotId 0 === crafting output + addSlot(new BSlotCrafting(this, pinv.player, matrix_, result_, 0, 134, 35)); + ArrayList> slotpositions = new ArrayList>(); + slotpositions.add(new Tuple<>(134, 35)); + // container slotId 1..9 === TE slots 0..8 + for(int y=0; y<3; ++y) { + for(int x=0; x<3; ++x) { + int xpos = 60+x*18; + int ypos = 17+y*18; + addSlot(new Slot(matrix_, x+y*3, xpos, ypos)); + slotpositions.add(new Tuple<>(xpos, ypos)); + } + } + // container slotId 10..36 === player slots: 9..35 + for(int y=0; y<3; ++y) { + for(int x=0; x<9; ++x) { + addSlot(new Slot(pinv, x+y*9+9, 8+x*18, 86+y*18)); + } + } + // container slotId 37..45 === player slots: 0..8 + for(int x=0; x<9; ++x) { + addSlot(new Slot(pinv, x, 8+x*18, 144)); + } + // container slotId 46..53 === TE slots 9..17 (storage) + for(int y=0; y<4; ++y) { + for(int x=0; x<2; ++x) { + addSlot(new Slot(matrix_, x+y*2+9, 8+x*18, 9+y*18)); + } + } + if((!player_.world.isRemote) && (inventory_ instanceof BTileEntity)) { + history_.read(((BTileEntity)inventory_).history.copy()); + syncHistory(player_); + } + CRAFTING_SLOT_COORDINATES = ImmutableList.copyOf(slotpositions); + onCraftMatrixChanged(matrix_); + } + + @Override + public boolean canInteractWith(PlayerEntity player) + { return inventory_.isUsableByPlayer(player); } + + @Override + public void onCraftMatrixChanged(IInventory inv) + { + detectAndSendChanges(); + wpc_.consume((world,pos)->{ + if(world.isRemote) return; + try { + //craft(windowId, world, player_, matrix_, result_); + ServerPlayerEntity player = (ServerPlayerEntity) player_; + ItemStack stack = ItemStack.EMPTY; + Optional optional = world.getServer().getRecipeManager().getRecipe(IRecipeType.CRAFTING, matrix_, world); + if(optional.isPresent()) { + ICraftingRecipe icraftingrecipe = optional.get(); + if(result_.canUseRecipe(world, player, icraftingrecipe)) { + stack = icraftingrecipe.getCraftingResult(matrix_); + } + } + result_.setInventorySlotContents(0, stack); + player.connection.sendPacket(new SSetSlotPacket(windowId, 0, stack)); + } catch(Throwable exc) { + ModEngineersDecor.logger().error("Recipe failed:", exc); + } + }); + } + + @Override + public void onContainerClosed(PlayerEntity player) + { + matrix_.closeInventory(player); + result_.clear(); + result_.closeInventory(player); + if(player!=null) { + for(Slot e:player.container.inventorySlots) { + if(e instanceof CraftingResultSlot) { + ((CraftingResultSlot)e).putStack(ItemStack.EMPTY); + } + } + } + } + + @Override + public boolean canMergeSlot(ItemStack stack, Slot slot) + { return (slot.inventory != result_) && (super.canMergeSlot(stack, slot)); } + + @Override + public ItemStack transferStackInSlot(PlayerEntity player, int index) + { + Slot slot = inventorySlots.get(index); + if((slot == null) || (!slot.getHasStack())) return ItemStack.EMPTY; + ItemStack slotstack = slot.getStack(); + ItemStack stack = slotstack.copy(); + if(index == 0) { + wpc_.consume((world, pos)->slotstack.getItem().onCreated(slotstack, world, player)); + if(!this.mergeItemStack(slotstack, 10, 46, true)) return ItemStack.EMPTY; + slot.onSlotChange(slotstack, stack); + } else if(index >= 10 && (index < 46)) { + if(!this.mergeItemStack(slotstack, 46, 54, false)) return ItemStack.EMPTY; + } else if((index >= 46) && (index < 54)) { + if(!this.mergeItemStack(slotstack, 10, 46, false)) return ItemStack.EMPTY; + } else if(!this.mergeItemStack(slotstack, 10, 46, false)) { + return ItemStack.EMPTY; + } + if(slotstack.isEmpty()) { + slot.putStack(ItemStack.EMPTY); + } else { + slot.onSlotChanged(); + } + if(slotstack.getCount() == stack.getCount()) { + return ItemStack.EMPTY; + } + ItemStack itemstack2 = slot.onTake(player, slotstack); + if(index == 0) { + player.dropItem(itemstack2, false); + } + return stack; + } + + // private aux methods --------------------------------------------------------------------- + + private boolean itemstack_recipe_match(ItemStack grid_stack, ItemStack history_stack) + { + if(history_.current_recipe()!=null) { + boolean grid_match, dist_match; + for(int i=0; i crafting_slot_stacks_to_add() + { + final ArrayList slots = new ArrayList(); + final List tocraft = history_.current(); + final int stack_sizes[] = {-1,-1,-1,-1,-1,-1,-1,-1,-1}; + if(tocraft.isEmpty()) return slots; + for(int i=0; i<9; ++i) { + if((i+CraftingHistory.INPUT_STACKS_BEGIN) >= tocraft.size()) break; + final ItemStack needed = tocraft.get(i+CraftingHistory.INPUT_STACKS_BEGIN); + final ItemStack palced = inventory_.getStackInSlot(i+CRAFTING_SLOTS_BEGIN); + if(needed.isEmpty() && (!palced.isEmpty())) return slots; // history vs grid mismatch. + if((!palced.isEmpty()) && (!itemstack_recipe_match(needed, palced))) return slots; // also mismatch + if(!needed.isEmpty()) stack_sizes[i] = palced.getCount(); + } + int min_placed = 64, max_placed=0; + for(int i=0; i<9; ++i) { + if(stack_sizes[i] < 0) continue; + min_placed = Math.min(min_placed, stack_sizes[i]); + max_placed = Math.max(max_placed, stack_sizes[i]); + } + int fillup_size = (max_placed <= min_placed) ? (min_placed + 1) : max_placed; + for(int i=0; i<9; ++i) { + if(stack_sizes[i] < 0) continue; + if(fillup_size > inventory_.getStackInSlot(i+CRAFTING_SLOTS_BEGIN).getMaxStackSize()) return slots; // can't fillup all + } + for(int i=0; i<9; ++i) { + if(stack_sizes[i] < 0) { + slots.add(ItemStack.EMPTY); + } else { + ItemStack st = inventory_.getStackInSlot(i+CRAFTING_SLOTS_BEGIN).copy(); + if(st.isEmpty()) { + st = tocraft.get(i+CraftingHistory.INPUT_STACKS_BEGIN).copy(); + st.setCount(Math.min(st.getMaxStackSize(), fillup_size)); + } else { + st.setCount(MathHelper.clamp(fillup_size-st.getCount(), 0, st.getMaxStackSize())); + } + slots.add(st); + } + } + return slots; + } + + /** + * Moves as much items from the stack to the slots in range [first_slot, last_slot] of the inventory, + * filling up existing stacks first, then (player inventory only) checks appropriate empty slots next + * to stacks that have that item already, and last uses any empty slot that can be found. + * Returns the stack that is still remaining in the referenced `stack`. + */ + private ItemStack move_stack_to_inventory(final ItemStack stack_to_move, IInventory inventory, final int slot_begin, final int slot_end, boolean only_fillup) + { + final ItemStack mvstack = stack_to_move.copy(); + if((mvstack.isEmpty()) || (slot_begin < 0) || (slot_end > inventory.getSizeInventory())) return mvstack; + // first iteration: fillup existing stacks + for(int i = slot_begin; i < slot_end; ++i) { + final ItemStack stack = inventory.getStackInSlot(i); + if((stack.isEmpty()) || (!stack.isItemEqual(mvstack))) continue; + int nmax = stack.getMaxStackSize() - stack.getCount(); + if(mvstack.getCount() <= nmax) { + stack.setCount(stack.getCount()+mvstack.getCount()); + mvstack.setCount(0); + inventory.setInventorySlotContents(i, stack); + return mvstack; + } else { + stack.setCount(stack.getMaxStackSize()); + mvstack.shrink(nmax); + inventory.setInventorySlotContents(i, stack); + } + } + if(only_fillup) return mvstack; + if(inventory instanceof PlayerInventory) { + // second iteration: use appropriate empty slots + for(int i = slot_begin+1; i < slot_end-1; ++i) { + final ItemStack stack = inventory.getStackInSlot(i); + if(!stack.isEmpty()) continue; + if((!inventory.getStackInSlot(i+1).isItemEqual(mvstack)) && (!inventory.getStackInSlot(i-1).isItemEqual(mvstack))) continue; + inventory.setInventorySlotContents(i, mvstack.copy()); + mvstack.setCount(0); + return mvstack; + } + } + // third iteration: use any empty slots + for(int i = slot_begin; i < slot_end; ++i) { + final ItemStack stack = inventory.getStackInSlot(i); + if(!stack.isEmpty()) continue; + inventory.setInventorySlotContents(i, mvstack.copy()); + mvstack.setCount(0); + return mvstack; + } + return mvstack; + } + + /** + * Moves as much items from the slots in range [first_slot, last_slot] of the inventory into a new stack. + * Implicitly shrinks the inventory stacks and the `request_stack`. + */ + private ItemStack move_stack_from_inventory(IInventory inventory, final ItemStack request_stack, final int slot_begin, final int slot_end) + { + ItemStack fetched_stack = request_stack.copy(); + fetched_stack.setCount(0); + int n_left = request_stack.getCount(); + while(n_left > 0) { + int smallest_stack_size = 0; + int smallest_stack_index = -1; + for(int i = slot_begin; i < slot_end; ++i) { + final ItemStack stack = inventory.getStackInSlot(i); + if((!stack.isEmpty()) && (stack.isItemEqual(request_stack))) { + // Never automatically place stuff with nbt (except a few allowed like "Damage"), + // as this could be a full crate, a valuable tool item, etc. For these recipes + // the user has to place this item manually. + if(stack.hasTag()) { + final CompoundNBT nbt = stack.getTag(); + int n = stack.getTag().size(); + if((n > 0) && (stack.getTag().contains("Damage"))) --n; + if(n > 0) continue; + } + fetched_stack = stack.copy(); // copy exact stack with nbt and tool damage, otherwise we have an automagical repair of items. + fetched_stack.setCount(0); + int n = stack.getCount(); + if((n < smallest_stack_size) || (smallest_stack_size <= 0)) { + smallest_stack_size = n; + smallest_stack_index = i; + } + } + } + if(smallest_stack_index < 0) { + break; // no more items available + } else { + int n = Math.min(n_left, smallest_stack_size); + n_left -= n; + fetched_stack.grow(n); + ItemStack st = inventory.getStackInSlot(smallest_stack_index); + st.shrink(n); + inventory.setInventorySlotContents(smallest_stack_index, st); + } + } + return fetched_stack; + } + + private boolean clear_grid_to_storage(PlayerEntity player) + { + boolean changed = false; + for(int grid_i = CRAFTING_SLOTS_BEGIN; grid_i < (CRAFTING_SLOTS_BEGIN+NUM_OF_CRAFTING_SLOTS); ++grid_i) { + ItemStack stack = inventory_.getStackInSlot(grid_i); + if(stack.isEmpty()) continue; + ItemStack remaining = move_stack_to_inventory(stack, inventory_, STORAGE_SLOTS_BEGIN, STORAGE_SLOTS_BEGIN+NUM_OF_STORAGE_SLOTS, false); + inventory_.setInventorySlotContents(grid_i, remaining); + changed = true; + } + return changed; + } + + private boolean clear_grid_to_player(PlayerEntity player) + { + boolean changed = false; + for(int grid_i = CRAFTING_SLOTS_BEGIN; grid_i < (CRAFTING_SLOTS_BEGIN+NUM_OF_CRAFTING_SLOTS); ++grid_i) { + ItemStack remaining = inventory_.getStackInSlot(grid_i); + if(remaining.isEmpty()) continue; + remaining = move_stack_to_inventory(remaining, player.inventory,9, 36, true); // prefer filling up inventory stacks + remaining = move_stack_to_inventory(remaining, player.inventory,0, 9, true); // then fill up the hotbar stacks + remaining = move_stack_to_inventory(remaining, player.inventory,9, 36, false); // then allow empty stacks in inventory + remaining = move_stack_to_inventory(remaining, player.inventory,0, 9, false); // then new stacks in the hotbar + inventory_.setInventorySlotContents(grid_i, remaining); + changed = true; + } + return changed; + } + + enum EnumRefabPlacement { UNCHANGED, INCOMPLETE, PLACED } + private EnumRefabPlacement place_refab_stacks(IInventory inventory, final int slot_begin, final int slot_end) + { + List to_fill = crafting_slot_stacks_to_add(); + boolean slots_changed = false; + boolean missing_item = false; + int num_slots_placed = 0; + if(!to_fill.isEmpty()) { + for(int it_guard=63; it_guard>=0; --it_guard) { + boolean slots_updated = false; + for(int i = 0; i < 9; ++i) { + final ItemStack req_stack = to_fill.get(i).copy(); + if(req_stack.isEmpty()) continue; + req_stack.setCount(1); + to_fill.get(i).shrink(1); + final ItemStack mv_stack = move_stack_from_inventory(inventory, req_stack, slot_begin, slot_end); + if(mv_stack.isEmpty()) { missing_item=true; continue; } + // sizes already checked + ItemStack grid_stack = inventory_.getStackInSlot(i + CRAFTING_SLOTS_BEGIN).copy(); + if(grid_stack.isEmpty()) { + grid_stack = mv_stack.copy(); + } else { + grid_stack.grow(mv_stack.getCount()); + } + inventory_.setInventorySlotContents(i + CRAFTING_SLOTS_BEGIN, grid_stack); + slots_changed = true; + slots_updated = true; + } + if(!slots_updated) break; + } + } + if(!slots_changed) { + return EnumRefabPlacement.UNCHANGED; + } else if(missing_item) { + return EnumRefabPlacement.INCOMPLETE; + } else { + return EnumRefabPlacement.PLACED; + } + } + + private EnumRefabPlacement distribute_stack(IInventory inventory, final int slotno) + { + List to_refab = crafting_slot_stacks_to_add(); + ItemStack to_distribute = inventory.getStackInSlot(slotno).copy(); + if(to_distribute.isEmpty()) return EnumRefabPlacement.UNCHANGED; + int matching_grid_stack_sizes[] = {-1,-1,-1,-1,-1,-1,-1,-1,-1}; + int max_matching_stack_size = -1; + int min_matching_stack_size = 65; + int total_num_missing_stacks = 0; + for(int i=0; i<9; ++i) { + final ItemStack grid_stack = inventory_.getStackInSlot(i+CRAFTING_SLOTS_BEGIN); + final ItemStack refab_stack = to_refab.isEmpty() ? ItemStack.EMPTY : to_refab.get(i).copy(); + if((!grid_stack.isEmpty()) && (grid_stack.isItemEqual(to_distribute))) { + matching_grid_stack_sizes[i] = grid_stack.getCount(); + total_num_missing_stacks += grid_stack.getMaxStackSize()-grid_stack.getCount(); + if(max_matching_stack_size < matching_grid_stack_sizes[i]) max_matching_stack_size = matching_grid_stack_sizes[i]; + if(min_matching_stack_size > matching_grid_stack_sizes[i]) min_matching_stack_size = matching_grid_stack_sizes[i]; + } else if((!refab_stack.isEmpty()) && (refab_stack.isItemEqual(to_distribute))) { + matching_grid_stack_sizes[i] = 0; + total_num_missing_stacks += grid_stack.getMaxStackSize(); + if(max_matching_stack_size < matching_grid_stack_sizes[i]) max_matching_stack_size = matching_grid_stack_sizes[i]; + if(min_matching_stack_size > matching_grid_stack_sizes[i]) min_matching_stack_size = matching_grid_stack_sizes[i]; + } else if(grid_stack.isEmpty() && (!refab_stack.isEmpty())) { + if(itemstack_recipe_match(to_distribute, refab_stack)) { + matching_grid_stack_sizes[i] = 0; + total_num_missing_stacks += grid_stack.getMaxStackSize(); + if(max_matching_stack_size < matching_grid_stack_sizes[i]) max_matching_stack_size = matching_grid_stack_sizes[i]; + if(min_matching_stack_size > matching_grid_stack_sizes[i]) min_matching_stack_size = matching_grid_stack_sizes[i]; + } + } + } + if(min_matching_stack_size < 0) return EnumRefabPlacement.UNCHANGED; + final int stack_limit_size = Math.min(to_distribute.getMaxStackSize(), inventory_.getInventoryStackLimit()); + if(min_matching_stack_size >= stack_limit_size) return EnumRefabPlacement.UNCHANGED; + int n_to_distribute = to_distribute.getCount(); + for(int it_guard=63; it_guard>=0; --it_guard) { + if(n_to_distribute <= 0) break; + for(int i=0; i<9; ++i) { + if(n_to_distribute <= 0) break; + if(matching_grid_stack_sizes[i] == min_matching_stack_size) { + ++matching_grid_stack_sizes[i]; + --n_to_distribute; + } + } + if(min_matching_stack_size < max_matching_stack_size) { + ++min_matching_stack_size; // distribute short stacks + } else { + ++min_matching_stack_size; // stacks even, increase all + max_matching_stack_size = min_matching_stack_size; + } + if(min_matching_stack_size >= stack_limit_size) break; // all full + } + if(n_to_distribute == to_distribute.getCount()) return EnumRefabPlacement.UNCHANGED; // was already full + if(n_to_distribute <= 0) { + inventory.setInventorySlotContents(slotno, ItemStack.EMPTY); + } else { + to_distribute.setCount(n_to_distribute); + inventory.setInventorySlotContents(slotno, to_distribute); + } + for(int i=0; i<9; ++i) { + if(matching_grid_stack_sizes[i] < 0) continue; + ItemStack grid_stack = inventory_.getStackInSlot(i + CRAFTING_SLOTS_BEGIN).copy(); + if(grid_stack.isEmpty()) grid_stack = to_distribute.copy(); + grid_stack.setCount(matching_grid_stack_sizes[i]); + inventory_.setInventorySlotContents(i + CRAFTING_SLOTS_BEGIN, grid_stack); + } + return EnumRefabPlacement.PLACED; + } + + // Container client/server synchronisation -------------------------------------------------- + + @OnlyIn(Dist.CLIENT) + public void onGuiAction(String message, CompoundNBT nbt) + { + nbt.putString("action", message); + Networking.PacketContainerSyncClientToServer.sendToServer(windowId, nbt); + } + + public void onServerPacketReceived(int windowId, CompoundNBT nbt) + { + if(nbt.contains("history")) { + history_.read(nbt.getCompound("history")); + } + } + + public void onClientPacketReceived(int windowId, PlayerEntity player, CompoundNBT nbt) + { + boolean changed = false; + boolean player_inventory_changed = false; + if(with_assist && nbt.contains("action")) { + switch(nbt.getString("action")) { + case BGui.BUTTON_NEXT: { + history_.next(); + syncHistory(player); + // implicitly clear the grid, so that the player can see the refab, and that no recipe is active. + if(clear_grid_to_player(player)) { changed = true; player_inventory_changed = true; } + if(clear_grid_to_storage(player)) changed = true; + } break; + case BGui.BUTTON_PREV: { + history_.prev(); + syncHistory(player); + if(clear_grid_to_player(player)) { changed = true; player_inventory_changed = true; } + if(clear_grid_to_storage(player)) changed = true; + } break; + case BGui.BUTTON_CLEAR_GRID: { + history_.reset_selection(); + syncHistory(player); + if(clear_grid_to_player(player)) { changed = true; player_inventory_changed = true; } + if(clear_grid_to_storage(player)) changed = true; + } break; + case BGui.BUTTON_TO_STORAGE: { + if(clear_grid_to_storage(player)) changed = true; + } break; + case BGui.BUTTON_TO_PLAYER: { + if(clear_grid_to_player(player)) { changed = true; player_inventory_changed = true; } + } break; + case BGui.BUTTON_FROM_STORAGE: { + EnumRefabPlacement from_storage = place_refab_stacks(inventory_, STORAGE_SLOTS_BEGIN, STORAGE_SLOTS_BEGIN+NUM_OF_STORAGE_SLOTS); + if(from_storage != EnumRefabPlacement.UNCHANGED) changed = true; + } break; + case BGui.BUTTON_FROM_PLAYER: { + EnumRefabPlacement from_player_inv = place_refab_stacks(player.inventory, 9, 36); + if(from_player_inv != EnumRefabPlacement.UNCHANGED) { changed = true; player_inventory_changed = true; } + if(from_player_inv != EnumRefabPlacement.PLACED) { + EnumRefabPlacement from_hotbar = place_refab_stacks(player.inventory, 0, 9); + if(from_hotbar != EnumRefabPlacement.UNCHANGED) { changed = true; player_inventory_changed = true; } + } + } break; + case BGui.ACTION_PLACE_CURRENT_HISTORY_SEL: { + EnumRefabPlacement from_storage = place_refab_stacks(inventory_, STORAGE_SLOTS_BEGIN, STORAGE_SLOTS_BEGIN+NUM_OF_STORAGE_SLOTS); + if(from_storage != EnumRefabPlacement.UNCHANGED) changed = true; + if(from_storage != EnumRefabPlacement.PLACED) { + EnumRefabPlacement from_player_inv = place_refab_stacks(player.inventory, 9, 36); + if(from_player_inv != EnumRefabPlacement.UNCHANGED) { changed = true; player_inventory_changed = true; } + if(from_player_inv != EnumRefabPlacement.PLACED) { + EnumRefabPlacement from_hotbar = place_refab_stacks(player.inventory, 0, 9); + if(from_hotbar != EnumRefabPlacement.UNCHANGED) { changed = true; player_inventory_changed = true; } + } + } + } break; + case BGui.ACTION_PLACE_SHIFTCLICKED_STACK: { + final int container_slot_id = nbt.getInt("containerslot"); + if((container_slot_id < 10) || (container_slot_id > 53)) break; // out of range + if(container_slot_id >= 46) { + // from storage + final int storage_slot = container_slot_id - 46 + STORAGE_SLOTS_BEGIN; + EnumRefabPlacement stat = distribute_stack(inventory_, storage_slot); + if(stat != EnumRefabPlacement.UNCHANGED) changed = true; + } else { + // from player + int player_slot = (container_slot_id >= 37) ? (container_slot_id-37) : (container_slot_id-10+9); + EnumRefabPlacement stat = distribute_stack(player.inventory, player_slot); + if(stat != EnumRefabPlacement.UNCHANGED) { player_inventory_changed = true; changed = true; } + } + } break; + } + } + if(changed) inventory_.markDirty(); + if(player_inventory_changed) player.inventory.markDirty(); + if(changed || player_inventory_changed) { + this.onCraftMatrixChanged(inventory_); + this.detectAndSendChanges(); + } + } + + public CraftingHistory history() + { return history_; } + + // todo: somehow hook into the container listeners for syncing all clients having that container open. + private void syncHistory(PlayerEntity player) + { + if((!with_assist) || (player.world.isRemote)) return; + CompoundNBT hist_nbt = history_.write(); + if((inventory_ instanceof BTileEntity)) { + ((BTileEntity)inventory_).history = hist_nbt.copy(); + inventory_.markDirty(); + } + final CompoundNBT nbt = new CompoundNBT(); + nbt.put("history", hist_nbt); + Networking.PacketContainerSyncServerToClient.sendToPlayer(player, windowId, nbt); + } + } + + //-------------------------------------------------------------------------------------------------------------------- + // GUI + //-------------------------------------------------------------------------------------------------------------------- + + @OnlyIn(Dist.CLIENT) + public static class BGui extends ContainerScreen + { + protected static final String BUTTON_NEXT = "next"; + protected static final String BUTTON_PREV = "prev"; + protected static final String BUTTON_CLEAR_GRID = "clear"; + protected static final String BUTTON_FROM_STORAGE = "from-storage"; + protected static final String BUTTON_TO_STORAGE = "to-storage"; + protected static final String BUTTON_FROM_PLAYER = "from-player"; + protected static final String BUTTON_TO_PLAYER = "to-player"; + protected static final String ACTION_PLACE_CURRENT_HISTORY_SEL = "place-refab"; + protected static final String ACTION_PLACE_SHIFTCLICKED_STACK = "place-stack"; + protected static final ResourceLocation BACKGROUND = new ResourceLocation(ModEngineersDecor.MODID, "textures/gui/treated_wood_crafting_table.png"); + protected final PlayerEntity player; + protected final ArrayList