From df4fe500ba064de46476e5f00598a6c286cd219f Mon Sep 17 00:00:00 2001 From: stfwi Date: Sun, 12 Jan 2020 17:43:56 +0100 Subject: [PATCH] 1.15: Crafting Table TER implemented. Crafting Table refab tweaks added. --- 1.12/gradle.properties | 2 +- 1.12/readme.md | 2 + 1.14/gradle.properties | 2 +- 1.14/readme.md | 2 + .../java/wile/engineersdecor/ModContent.java | 12 +- .../blocks/BlockDecorCraftingTable.java | 992 +++++++++++------- .../wile/engineersdecor/detail/ModConfig.java | 2 +- .../wile/engineersdecor/detail/ModTesrs.java | 6 +- .../engineersdecor/eapi/jei/JEIPlugin.java | 2 +- 1.15/gradle.properties | 4 +- 1.15/meta/update.json | 2 +- 1.15/readme.md | 4 +- .../java/wile/engineersdecor/ModContent.java | 18 +- .../blocks/BlockDecorCraftingTable.java | 983 ++++++++++------- .../wile/engineersdecor/detail/ModConfig.java | 2 +- .../engineersdecor/detail/ModRenderers.java | 72 +- meta/update.json | 2 +- readme.md | 4 +- 18 files changed, 1269 insertions(+), 844 deletions(-) diff --git a/1.12/gradle.properties b/1.12/gradle.properties index e7dc501..f59132f 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.18-b2 +version_engineersdecor=1.0.18-b3 diff --git a/1.12/readme.md b/1.12/readme.md index 4aa41ab..ad11cba 100644 --- a/1.12/readme.md +++ b/1.12/readme.md @@ -10,6 +10,8 @@ Mod sources for Minecraft version 1.12.2. ---- ## Version history + ~ v1.0.18-b3 + - v1.0.18-b2 [A] Added Treated Wood Crafting table tweaks (ctrl-shift moves all same stacks from the inventory, mouse wheel over crafting slot increases/decreases crafting grid stacks). [F] EN Lang file fixed (issue #76, thx Riverstar907). diff --git a/1.14/gradle.properties b/1.14/gradle.properties index ef724cc..2f645d3 100644 --- a/1.14/gradle.properties +++ b/1.14/gradle.properties @@ -5,4 +5,4 @@ version_minecraft=1.14.4 version_forge_minecraft=1.14.4-28.1.111 version_fml_mappings=20190719-1.14.3 version_jei=1.14.4:6.0.0.10 -version_engineersdecor=1.0.18-b3 +version_engineersdecor=1.0.18-b4 diff --git a/1.14/readme.md b/1.14/readme.md index d65bb40..5742822 100644 --- a/1.14/readme.md +++ b/1.14/readme.md @@ -11,6 +11,8 @@ Mod sources for Minecraft version 1.14.4. ## Version history + ~ v1.0.18-b4 + - v1.0.18-b3 [A] Added Treated Wood Crafting table tweaks (ctrl-shift moves all same stacks from the inventory, mouse wheel over crafting slot increases/decreases crafting grid stacks). [F] EN Lang file fixed (issue #76, thx Riverstar907). diff --git a/1.14/src/main/java/wile/engineersdecor/ModContent.java b/1.14/src/main/java/wile/engineersdecor/ModContent.java index 39e7980..5323bf6 100644 --- a/1.14/src/main/java/wile/engineersdecor/ModContent.java +++ b/1.14/src/main/java/wile/engineersdecor/ModContent.java @@ -371,7 +371,7 @@ public class ModContent // ------------------------------------------------------------------------------------------------------------------- - public static final BlockDecorCraftingTable TREATED_WOOD_CRAFTING_TABLE = (BlockDecorCraftingTable)(new BlockDecorCraftingTable( + public static final BlockDecorCraftingTable.CraftingTableBlock TREATED_WOOD_CRAFTING_TABLE = (BlockDecorCraftingTable.CraftingTableBlock)(new BlockDecorCraftingTable.CraftingTableBlock( BlockDecor.CFG_CUTOUT|BlockDecor.CFG_HORIZIONTAL|BlockDecor.CFG_LOOK_PLACEMENT|BlockDecor.CFG_OPPOSITE_PLACEMENT, Block.Properties.create(Material.WOOD, MaterialColor.WOOD).hardnessAndResistance(1f, 15f).sound(SoundType.WOOD), ModAuxiliaries.getPixeledAABB(1,0,1, 15,15.9,15) @@ -624,7 +624,7 @@ public class ModContent //-------------------------------------------------------------------------------------------------------------------- public static final TileEntityType TET_TREATED_WOOD_CRAFTING_TABLE = TileEntityType.Builder - .create(BlockDecorCraftingTable.BTileEntity::new, TREATED_WOOD_CRAFTING_TABLE) + .create(BlockDecorCraftingTable.CraftingTableTileEntity::new, TREATED_WOOD_CRAFTING_TABLE) .build(null) .setRegistryName(ModEngineersDecor.MODID, "te_treated_wood_crafting_table"); @@ -743,7 +743,7 @@ public class ModContent // Container registration //-------------------------------------------------------------------------------------------------------------------- - public static final ContainerType CT_TREATED_WOOD_CRAFTING_TABLE; + public static final ContainerType CT_TREATED_WOOD_CRAFTING_TABLE; public static final ContainerType CT_FACTORY_DROPPER; public static final ContainerType CT_FACTORY_PLACER; public static final ContainerType CT_FACTORY_HOPPER; @@ -752,7 +752,7 @@ public class ModContent public static final ContainerType CT_WASTE_INCINERATOR; static { - CT_TREATED_WOOD_CRAFTING_TABLE = (new ContainerType(BlockDecorCraftingTable.BContainer::new)); + CT_TREATED_WOOD_CRAFTING_TABLE = (new ContainerType(BlockDecorCraftingTable.CraftingTableContainer::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"); @@ -853,7 +853,7 @@ public class ModContent @OnlyIn(Dist.CLIENT) public static final void registerContainerGuis(final FMLClientSetupEvent event) { - ScreenManager.registerFactory(CT_TREATED_WOOD_CRAFTING_TABLE, BlockDecorCraftingTable.BGui::new); + ScreenManager.registerFactory(CT_TREATED_WOOD_CRAFTING_TABLE, BlockDecorCraftingTable.CraftingTableGui::new); ScreenManager.registerFactory(CT_FACTORY_DROPPER, BlockDecorDropper.BGui::new); ScreenManager.registerFactory(CT_FACTORY_PLACER, BlockDecorPlacer.BGui::new); ScreenManager.registerFactory(CT_FACTORY_HOPPER, BlockDecorHopper.BGui::new); @@ -865,6 +865,6 @@ public class ModContent @OnlyIn(Dist.CLIENT) public static final void registerTileEntityRenderers(final FMLClientSetupEvent event) { - ClientRegistry.bindTileEntitySpecialRenderer(BlockDecorCraftingTable.BTileEntity.class, new wile.engineersdecor.detail.ModTesrs.TesrDecorCraftingTable()); + ClientRegistry.bindTileEntitySpecialRenderer(BlockDecorCraftingTable.CraftingTableTileEntity.class, new wile.engineersdecor.detail.ModTesrs.TesrDecorCraftingTable()); } } diff --git a/1.14/src/main/java/wile/engineersdecor/blocks/BlockDecorCraftingTable.java b/1.14/src/main/java/wile/engineersdecor/blocks/BlockDecorCraftingTable.java index a3b9b28..dd9c0a0 100644 --- a/1.14/src/main/java/wile/engineersdecor/blocks/BlockDecorCraftingTable.java +++ b/1.14/src/main/java/wile/engineersdecor/blocks/BlockDecorCraftingTable.java @@ -5,6 +5,13 @@ * @license MIT (see https://opensource.org/licenses/MIT) * * Crafting table + * + * @todo: The functionality in the nested classes has become big enough to + * start librarizing, mainly the crafting-assist: + * + * - class InventoryAssist (to prevent ambiguity with InventoryHelper, InventoryUtil, etc) + * - class RecipeAssist + * - class CraftingAssist */ package wile.engineersdecor.blocks; @@ -25,13 +32,10 @@ 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.crafting.*; import net.minecraft.item.Item; import net.minecraft.item.Items; import net.minecraft.item.ItemStack; -import net.minecraft.item.crafting.ICraftingRecipe; -import net.minecraft.item.crafting.IRecipeType; -import net.minecraft.item.crafting.Ingredient; -import net.minecraft.item.crafting.IRecipe; import net.minecraft.inventory.*; import net.minecraft.nbt.CompoundNBT; import net.minecraft.util.*; @@ -56,13 +60,16 @@ import javax.annotation.Nullable; import java.util.*; import java.util.stream.Collectors; +import static java.util.Arrays.asList; -public class BlockDecorCraftingTable extends BlockDecorDirected.WaterLoggable + +public class BlockDecorCraftingTable { 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 boolean with_crafting_slot_mouse_scrolling = true; + public static boolean with_outslot_defined_refab = true; public static final void on_config(boolean without_crafting_assist, boolean with_assist_immediate_history_refab, boolean with_quickmove_buttons, boolean without_crafting_slot_mouse_scrolling) @@ -71,92 +78,100 @@ public class BlockDecorCraftingTable extends BlockDecorDirected.WaterLoggable with_assist_direct_history_refab = with_assist_immediate_history_refab; with_assist_quickmove_buttons = with_quickmove_buttons; with_crafting_slot_mouse_scrolling = !without_crafting_slot_mouse_scrolling; + with_outslot_defined_refab = with_assist; CraftingHistory.max_history_size(32); } - public BlockDecorCraftingTable(long config, Block.Properties builder, final AxisAlignedBB unrotatedAABB) - { super(config|CFG_WATERLOGGABLE, builder, unrotatedAABB); } + //-------------------------------------------------------------------------------------------------------------------- + // Block + //-------------------------------------------------------------------------------------------------------------------- - @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) + public static final class CraftingTableBlock extends BlockDecorDirected.WaterLoggable { - 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; - } + public CraftingTableBlock(long config, Block.Properties builder, final AxisAlignedBB unrotatedAABB) + { super(config|CFG_WATERLOGGABLE, builder, unrotatedAABB); } - @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 boolean hasTileEntity(BlockState state) + { return true; } - @Override - public boolean hasDynamicDropList() - { return true; } + @Override + @Nullable + public TileEntity createTileEntity(BlockState state, IBlockReader world) + { return new BlockDecorCraftingTable.CraftingTableTileEntity(); } - @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(); + @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 CraftingTableTileEntity)) 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 CraftingTableTileEntity)) return; + ((CraftingTableTileEntity)te).readnbt(inventory_nbt); + ((CraftingTableTileEntity)te).markDirty(); + } + + @Override + public boolean hasDynamicDropList() + { return true; } + + @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 CraftingTableTileEntity)) return stacks; + if(!explosion) { + ItemStack stack = new ItemStack(this, 1); + CompoundNBT inventory_nbt = new CompoundNBT(); + ItemStackHelper.saveAllItems(inventory_nbt, ((CraftingTableTileEntity)te).stacks, false); + if(!inventory_nbt.isEmpty()) { + CompoundNBT nbt = new CompoundNBT(); + nbt.put("inventory", inventory_nbt); + stack.setTag(nbt); + } + ((CraftingTableTileEntity) te).clear(); + stacks.add(stack); + } else { + for(ItemStack stack: ((CraftingTableTileEntity)te).stacks) { + if(!stack.isEmpty()) stacks.add(stack); + } + ((CraftingTableTileEntity)te).reset(); + } + return stacks; } - return stacks; } //-------------------------------------------------------------------------------------------------------------------- // Tile entity //-------------------------------------------------------------------------------------------------------------------- - public static class BTileEntity extends TileEntity implements IInventory, INameable, INamedContainerProvider + public static class CraftingTableTileEntity 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() + public CraftingTableTileEntity() { this(ModContent.TET_TREATED_WOOD_CRAFTING_TABLE); } - public BTileEntity(TileEntityType te_type) + public CraftingTableTileEntity(TileEntityType te_type) { super(te_type); } public void reset() @@ -229,7 +244,7 @@ public class BlockDecorCraftingTable extends BlockDecorDirected.WaterLoggable @Override public Container createMenu( int id, PlayerInventory inventory, PlayerEntity player ) - { return new BContainer(id, inventory, this, IWorldPosCallable.of(world, pos)); } + { return new CraftingTableContainer(id, inventory, this, IWorldPosCallable.of(world, pos)); } // IInventory ------------------------------------------------------------------------------ @@ -297,92 +312,8 @@ public class BlockDecorCraftingTable extends BlockDecorDirected.WaterLoggable // Crafting container //-------------------------------------------------------------------------------------------------------------------- - public static class BContainer extends Container implements Networking.INetworkSynchronisableContainer + public static class CraftingTableContainer extends Container implements Networking.INetworkSynchronisableContainer { - public enum PlacementResult { UNCHANGED, INCOMPLETE, PLACED } - - // 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; - } - } - - private static class SlotRange - { - public final IInventory inventory; - public int start_slot, end_slot; - public SlotRange(IInventory inv, int start, int end) { inventory=inv; start_slot=start; end_slot=end; } - } - - //------------------------------------------------------------------------------------------------------------------ - //------------------------------------------------------------------------------------------------------------------ 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; @@ -393,27 +324,28 @@ public class BlockDecorCraftingTable extends BlockDecorDirected.WaterLoggable private final IInventory inventory_; private final IWorldPosCallable wpc_; private final CraftingHistory history_; - private final BInventoryCrafting matrix_; + private final CraftingTableGrid matrix_; private final CraftResultInventory result_; private boolean has_recipe_collision_; + private boolean crafting_matrix_changed_now_; - public BContainer(int cid, PlayerInventory pinv) - { this(cid, pinv, new Inventory(BTileEntity.NUM_OF_SLOTS), IWorldPosCallable.DUMMY); } + public CraftingTableContainer(int cid, PlayerInventory pinv) + { this(cid, pinv, new Inventory(CraftingTableTileEntity.NUM_OF_SLOTS), IWorldPosCallable.DUMMY); } - private BContainer(int cid, PlayerInventory pinv, IInventory block_inventory, IWorldPosCallable wpc) + private CraftingTableContainer(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(); + if(world.isRemote && (inventory_ instanceof CraftingTableTileEntity)) world = ((CraftingTableTileEntity)inventory_).getWorld(); history_ = new CraftingHistory(world); result_ = new CraftResultInventory(); - matrix_ = new BInventoryCrafting(this, block_inventory); + matrix_ = new CraftingTableGrid(this, block_inventory); matrix_.openInventory(player_); // container slotId 0 === crafting output - addSlot(new BSlotCrafting(this, pinv.player, matrix_, result_, 0, 134, 35)); + addSlot(new CraftingOutputSlot(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 @@ -441,9 +373,9 @@ public class BlockDecorCraftingTable extends BlockDecorDirected.WaterLoggable 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_); + if((!player_.world.isRemote) && (inventory_ instanceof CraftingTableTileEntity)) { + history_.read(((CraftingTableTileEntity)inventory_).history.copy()); + syncHistory(); } CRAFTING_SLOT_COORDINATES = ImmutableList.copyOf(slotpositions); onCraftMatrixChanged(matrix_); @@ -460,7 +392,7 @@ public class BlockDecorCraftingTable extends BlockDecorDirected.WaterLoggable wpc_.consume((world,pos)->{ if(world.isRemote) return; try { - //craft(windowId, world, player_, matrix_, result_); + crafting_matrix_changed_now_ = true; ServerPlayerEntity player = (ServerPlayerEntity) player_; ItemStack stack = ItemStack.EMPTY; List recipes = world.getServer().getRecipeManager().getRecipes(IRecipeType.CRAFTING, matrix_, world); @@ -478,7 +410,7 @@ public class BlockDecorCraftingTable extends BlockDecorDirected.WaterLoggable } result_.setInventorySlotContents(0, stack); player.connection.sendPacket(new SSetSlotPacket(windowId, 0, stack)); - syncProperties(player); + syncProperties(); } catch(Throwable exc) { ModEngineersDecor.logger().error("Recipe failed:", exc); } @@ -537,6 +469,222 @@ public class BlockDecorCraftingTable extends BlockDecorDirected.WaterLoggable return stack; } + @Override + public ItemStack slotClick(int slotId, int button, ClickType clickType, PlayerEntity player) + { + crafting_matrix_changed_now_ = false; + final ItemStack stack = super.slotClick(slotId, button, clickType, player); + if((with_outslot_defined_refab) && (slotId == 0) && (clickType == ClickType.PICKUP)) { + if((!crafting_matrix_changed_now_) && (!player.world.isRemote()) && (crafting_grid_empty())) { + final ItemStack dragged = player.inventory.getItemStack(); // @todo: check, as far as seen this is actually `stack`. + if((dragged != null) && (!dragged.isEmpty())) { + try_result_stack_refab(dragged, player.world); + } else if(!history().current().isEmpty()) { + try_result_stack_refab(history().current_recipe().getRecipeOutput(), player.world); + } + } + } + return stack; + } + + // 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")); + if(nbt.contains("hascollision")) has_recipe_collision_ = nbt.getBoolean("hascollision"); + } + + 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 CraftingTableGui.BUTTON_NEXT: { + history_.next(); + syncHistory(); + // 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 CraftingTableGui.BUTTON_PREV: { + history_.prev(); + syncHistory(); + if(clear_grid_to_player(player)) { changed = true; player_inventory_changed = true; } + if(clear_grid_to_storage(player)) changed = true; + } break; + case CraftingTableGui.BUTTON_CLEAR_GRID: { + history_.reset_selection(); + syncHistory(); + if(clear_grid_to_player(player)) { changed = true; player_inventory_changed = true; } + if(clear_grid_to_storage(player)) changed = true; + } break; + case CraftingTableGui.BUTTON_TO_STORAGE: { + if(clear_grid_to_storage(player)) changed = true; + } break; + case CraftingTableGui.BUTTON_TO_PLAYER: { + if(clear_grid_to_player(player)) { changed = true; player_inventory_changed = true; } + } break; + case CraftingTableGui.BUTTON_FROM_STORAGE: { + if(place_stacks(new SlotRange[]{ + new SlotRange(inventory_, STORAGE_SLOTS_BEGIN, STORAGE_SLOTS_BEGIN+NUM_OF_STORAGE_SLOTS) + }, refab_crafting_stacks()) != PlacementResult.UNCHANGED) { + changed = true; + } + } break; + case CraftingTableGui.BUTTON_FROM_PLAYER: { + if(place_stacks(new SlotRange[]{ + new SlotRange(player.inventory, 9, 36), + new SlotRange(player.inventory, 0, 9) + }, refab_crafting_stacks()) != PlacementResult.UNCHANGED) { + changed = true; player_inventory_changed = true; + } + } break; + case CraftingTableGui.ACTION_PLACE_CURRENT_HISTORY_SEL: { + if(place_stacks(new SlotRange[]{ + new SlotRange(player.inventory, 0, 9), + new SlotRange(player.inventory, 9, 36), + new SlotRange(inventory_, STORAGE_SLOTS_BEGIN, STORAGE_SLOTS_BEGIN+NUM_OF_STORAGE_SLOTS) + }, refab_crafting_stacks()) != PlacementResult.UNCHANGED) { + changed = true; + } + } break; + case CraftingTableGui.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; + PlacementResult stat = distribute_stack(inventory_, storage_slot); + if(stat != PlacementResult.UNCHANGED) changed = true; + } else { + // from player + int player_slot = (container_slot_id >= 37) ? (container_slot_id-37) : (container_slot_id-10+9); + final ItemStack reference_stack = player.inventory.getStackInSlot(player_slot).copy(); + if((!reference_stack.isEmpty()) && (distribute_stack(player.inventory, player_slot) != PlacementResult.UNCHANGED)) { + player_inventory_changed = true; + changed = true; + if(nbt.contains("move-all")) { + for(int i=0; i < player.inventory.getSizeInventory(); ++i) { + final ItemStack stack = player.inventory.getStackInSlot(i); + if(!reference_stack.isItemEqual(stack)) continue; + if(distribute_stack(player.inventory, i) == PlacementResult.UNCHANGED) break; // grid is full + } + } + } + } + } break; + case CraftingTableGui.ACTION_MOVE_ALL_STACKS: { + final int container_slot_id = nbt.getInt("containerslot"); + if((container_slot_id < 1) || (container_slot_id > 53)) { + break; // out of range + } else if(container_slot_id < 10) { + // from crafting grid to player inventory, we clear the grid here as this is most likely + // what is wanted in the end. Saves clicking the other grid stacks. + if(clear_grid_to_player(player)) { changed = true; player_inventory_changed = true; } + if(clear_grid_to_storage(player)) changed = true; + break; + } + IInventory from_inventory; + SlotRange[] to_ranges; + int from_slot; + if(container_slot_id >= 46) { + // from storage to player inventory + from_inventory = inventory_; + from_slot = container_slot_id - 46 + STORAGE_SLOTS_BEGIN; + to_ranges = new SlotRange[] {new SlotRange(player.inventory, 9, 36), new SlotRange(player.inventory, 0, 9)}; + } else { + // from player to storage (otherwise ACTION_PLACE_SHIFTCLICKED_STACK would have been used) + from_inventory = player.inventory; + from_slot = (container_slot_id >= 37) ? (container_slot_id-37) : (container_slot_id-10+9); + to_ranges = new SlotRange[] {new SlotRange(inventory_, STORAGE_SLOTS_BEGIN, STORAGE_SLOTS_BEGIN+NUM_OF_STORAGE_SLOTS)}; + } + final ItemStack reference_stack = from_inventory.getStackInSlot(from_slot).copy(); + if(!reference_stack.isEmpty()) { + boolean abort = false; + for(int i=0; (i < from_inventory.getSizeInventory()) && (!abort); ++i) { + final ItemStack stack = from_inventory.getStackInSlot(i); + if(!reference_stack.isItemEqual(stack)) continue; + ItemStack remaining = from_inventory.getStackInSlot(i); + for(SlotRange range:to_ranges) { + remaining = move_stack_to_inventory(remaining, range, false, 0); + if(!remaining.isEmpty()) { + abort = true; // no space left + break; + } else { + changed = player_inventory_changed = true; + } + } + from_inventory.setInventorySlotContents(i, remaining); + } + } + } break; + case CraftingTableGui.BUTTON_NEXT_COLLISION_RECIPE: { + select_next_collision_recipe(inventory_); + } break; + case CraftingTableGui.ACTION_DECREASE_CRAFTING_STACKS: { + changed = player_inventory_changed = decrease_grid_stacks(new SlotRange[]{ + new SlotRange(player.inventory, 9, 36), + new SlotRange(player.inventory, 0, 9), + new SlotRange(inventory_, STORAGE_SLOTS_BEGIN, STORAGE_SLOTS_BEGIN+NUM_OF_STORAGE_SLOTS) + }, MathHelper.clamp(nbt.getInt("limit"), 1, 8)); + } break; + case CraftingTableGui.ACTION_INCREASE_CRAFTING_STACKS: { + changed = player_inventory_changed = increase_grid_stacks(new SlotRange[]{ + new SlotRange(player.inventory, 9, 36), + new SlotRange(player.inventory, 0, 9), + new SlotRange(inventory_, STORAGE_SLOTS_BEGIN, STORAGE_SLOTS_BEGIN+NUM_OF_STORAGE_SLOTS) + }, MathHelper.clamp(nbt.getInt("limit"), 1, 8)); + } 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_; } + + private void syncHistory() + { + if(!with_assist) return; + this.wpc_.consume((world,pos)->{ + if(world.isRemote()) return; + CompoundNBT hist_nbt = history_.write(); + if((inventory_ instanceof CraftingTableTileEntity)) { + ((CraftingTableTileEntity)inventory_).history = hist_nbt.copy(); + inventory_.markDirty(); + } + final CompoundNBT nbt = new CompoundNBT(); + nbt.put("history", hist_nbt); + nbt.putBoolean("hascollision", has_recipe_collision_); + Networking.PacketContainerSyncServerToClient.sendToListeners(world, this, nbt); + }); + } + + private void syncProperties() + { + this.wpc_.consume((world,pos)->{ + final CompoundNBT nbt = new CompoundNBT(); + nbt.putBoolean("hascollision", has_recipe_collision_); + Networking.PacketContainerSyncServerToClient.sendToListeners(world, this, nbt); + }); + } + // private aux methods --------------------------------------------------------------------- public boolean has_recipe_collision() @@ -572,12 +720,129 @@ public class BlockDecorCraftingTable extends BlockDecorDirected.WaterLoggable }); } + @Nullable + private ICraftingRecipe find_first_recipe_for(World world, ItemStack stack) + { + return (ICraftingRecipe)world.getServer().getRecipeManager().getRecipes().stream() + .filter(r->(r.getType()==IRecipeType.CRAFTING) && (r.getRecipeOutput().isItemEqual(stack))) + .findFirst().orElse(null); + } + + private ItemStack search_inventory(ItemStack match_stack, ItemStack not_found_value) { + SlotRange search_ranges[] = new SlotRange[]{ + new SlotRange(player_.inventory, 0, 36), + new SlotRange(inventory_, STORAGE_SLOTS_BEGIN, STORAGE_SLOTS_BEGIN+NUM_OF_STORAGE_SLOTS) + }; + for(SlotRange range: search_ranges) { + for(int i=0; i placement_stacks(ICraftingRecipe recipe) + { + final World world = player_.world; + final ArrayList grid = new ArrayList(); + if(recipe.getIngredients().size() > 9) { + return grid; + } else if(recipe instanceof ShapedRecipe) { + final int endw = ((ShapedRecipe)recipe).getWidth(); + final int endh = ((ShapedRecipe)recipe).getHeight(); + int ingredient_index = 0; + for(int i=3-endh; i>0; --i) for(int w=0; w<3; ++w) { + grid.add(ItemStack.EMPTY); + } + for(int h=3-endh; h<3; ++h) for(int w=0; w<3; ++w) { + if((w >= endw) || (ingredient_index >= recipe.getIngredients().size())) { grid.add(ItemStack.EMPTY); continue; } + ItemStack[] match_stacks = recipe.getIngredients().get(ingredient_index++).getMatchingStacks(); + if(match_stacks.length == 0) { grid.add(ItemStack.EMPTY); continue; } + ItemStack preferred = search_inventory(match_stacks, match_stacks[0]); + if(preferred.isEmpty()) { grid.add(ItemStack.EMPTY); continue; } + grid.add(preferred); + } + } else if(recipe instanceof ShapelessRecipe) { + // todo: check if a collision resolver with shaped recipes makes sense here. + for(int ingredient_index=0; ingredient_index grid_stacks) + { + boolean changed = false; + final List ingredients = recipe.getIngredients(); + for(int stack_index=0; stack_index < grid_stacks.size(); ++stack_index) { + ItemStack to_replace = grid_stacks.get(stack_index); + ItemStack replacement = to_replace; + if(to_replace.isEmpty() || (!search_inventory(to_replace, ItemStack.EMPTY).isEmpty())) continue; // no replacement needed + for(int ingredient_index=0; ingredient_indexs.isItemEqual(to_replace))) { + replacement = search_inventory(match_stacks, to_replace); + changed = true; + break; + } + } + grid_stacks.set(stack_index, replacement); + } + return changed; + } + + private void try_result_stack_refab(ItemStack output_stack, World world) + { + ICraftingRecipe recipe; + int history_index = history().find(output_stack); + if(history_index >= 0) { + history().selection(history_index); + recipe = history().current_recipe(); + List grid_stacks = history().current().subList(1, history().current().size()); + if(adapt_recipe_placement(recipe, grid_stacks)) { + history().stash(grid_stacks, recipe); + recipe = history().current_recipe(); + } + } else if((recipe=find_first_recipe_for(world, output_stack)) != null) { + ArrayList stacks = placement_stacks(recipe); + if(stacks.isEmpty()) { + recipe = null; + } else { + history().stash(stacks, recipe); + recipe = history().current_recipe(); + } + } + if(recipe != null) { + onCraftMatrixChanged(inventory_); + syncHistory(); + } + } + + private boolean crafting_grid_empty() + { for(int i=0; i<10; ++i) { if(getSlot(i).getHasStack()) return false; } return true; } + private boolean itemstack_recipe_match(ItemStack grid_stack, ItemStack history_stack) { if(history_.current_recipe()!=null) { + final NonNullList ingredients = history_.current_recipe().getIngredients(); boolean grid_match, dist_match; - for(int i=0; i 53)) { - break; // out of range - } - if(container_slot_id >= 46) { - // from storage - final int storage_slot = container_slot_id - 46 + STORAGE_SLOTS_BEGIN; - PlacementResult stat = distribute_stack(inventory_, storage_slot); - if(stat != PlacementResult.UNCHANGED) changed = true; - } else { - // from player - int player_slot = (container_slot_id >= 37) ? (container_slot_id-37) : (container_slot_id-10+9); - final ItemStack reference_stack = player.inventory.getStackInSlot(player_slot).copy(); - if((!reference_stack.isEmpty()) && (distribute_stack(player.inventory, player_slot) != PlacementResult.UNCHANGED)) { - player_inventory_changed = true; - changed = true; - if(nbt.contains("move-all")) { - for(int i=0; i < player.inventory.getSizeInventory(); ++i) { - final ItemStack stack = player.inventory.getStackInSlot(i); - if(!reference_stack.isItemEqual(stack)) continue; - if(distribute_stack(player.inventory, i) == PlacementResult.UNCHANGED) break; // grid is full - } - } - } - } - } break; - case BGui.ACTION_MOVE_ALL_STACKS: { - final int container_slot_id = nbt.getInt("containerslot"); - if((container_slot_id < 1) || (container_slot_id > 53)) { - break; // out of range - } else if(container_slot_id < 10) { - // from crafting grid to player inventory, we clear the grid here as this is most likely - // what is wanted in the end. Saves clicking the other grid stacks. - if(clear_grid_to_player(player)) { changed = true; player_inventory_changed = true; } - if(clear_grid_to_storage(player)) changed = true; - break; - } - IInventory from_inventory; - SlotRange[] to_ranges; - int from_slot; - if(container_slot_id >= 46) { - // from storage to player inventory - from_inventory = inventory_; - from_slot = container_slot_id - 46 + STORAGE_SLOTS_BEGIN; - to_ranges = new SlotRange[] {new SlotRange(player.inventory, 9, 36), new SlotRange(player.inventory, 0, 9)}; - } else { - // from player to storage (otherwise ACTION_PLACE_SHIFTCLICKED_STACK would have been used) - from_inventory = player.inventory; - from_slot = (container_slot_id >= 37) ? (container_slot_id-37) : (container_slot_id-10+9); - to_ranges = new SlotRange[] {new SlotRange(inventory_, STORAGE_SLOTS_BEGIN, STORAGE_SLOTS_BEGIN+NUM_OF_STORAGE_SLOTS)}; - } - final ItemStack reference_stack = from_inventory.getStackInSlot(from_slot).copy(); - if(!reference_stack.isEmpty()) { - boolean abort = false; - for(int i=0; (i < from_inventory.getSizeInventory()) && (!abort); ++i) { - final ItemStack stack = from_inventory.getStackInSlot(i); - if(!reference_stack.isItemEqual(stack)) continue; - ItemStack remaining = from_inventory.getStackInSlot(i); - for(SlotRange range:to_ranges) { - remaining = move_stack_to_inventory(remaining, range, false, 0); - if(!remaining.isEmpty()) { - abort = true; // no space left - break; - } else { - changed = player_inventory_changed = true; - } - } - from_inventory.setInventorySlotContents(i, remaining); - } - } - } break; - case BGui.BUTTON_NEXT_COLLISION_RECIPE: { - select_next_collision_recipe(inventory_); - } break; - case BGui.ACTION_DECREASE_CRAFTING_STACKS: { - changed = player_inventory_changed = decrease_grid_stacks(new SlotRange[]{ - new SlotRange(player.inventory, 9, 36), - new SlotRange(player.inventory, 0, 9), - new SlotRange(inventory_, STORAGE_SLOTS_BEGIN, STORAGE_SLOTS_BEGIN+NUM_OF_STORAGE_SLOTS) - }, MathHelper.clamp(nbt.getInt("limit"), 1, 8)); - } break; - case BGui.ACTION_INCREASE_CRAFTING_STACKS: { - changed = player_inventory_changed = increase_grid_stacks(new SlotRange[]{ - new SlotRange(player.inventory, 9, 36), - new SlotRange(player.inventory, 0, 9), - new SlotRange(inventory_, STORAGE_SLOTS_BEGIN, STORAGE_SLOTS_BEGIN+NUM_OF_STORAGE_SLOTS) - }, MathHelper.clamp(nbt.getInt("limit"), 1, 8)); - } 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_; } - - 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); - nbt.putBoolean("hascollision", has_recipe_collision_); - Networking.PacketContainerSyncServerToClient.sendToListeners(player.world, this, nbt); - } - - private void syncProperties(PlayerEntity player) - { - final CompoundNBT nbt = new CompoundNBT(); - nbt.putBoolean("hascollision", has_recipe_collision_); - Networking.PacketContainerSyncServerToClient.sendToPlayer(player, windowId, nbt); - } } //-------------------------------------------------------------------------------------------------------------------- @@ -1117,7 +1190,7 @@ public class BlockDecorCraftingTable extends BlockDecorDirected.WaterLoggable //-------------------------------------------------------------------------------------------------------------------- @OnlyIn(Dist.CLIENT) - public static class BGui extends ContainerScreen + public static class CraftingTableGui extends ContainerScreen { protected static final String BUTTON_NEXT = "next"; protected static final String BUTTON_PREV = "prev"; @@ -1137,7 +1210,7 @@ public class BlockDecorCraftingTable extends BlockDecorDirected.WaterLoggable protected final ArrayList