Crafting table keeps inventory and has 8 storage slots. Inset light harvest tool and strength adapted. Crafting table recipe changed.

This commit is contained in:
stfwi 2019-03-06 20:59:25 +01:00
parent 412098c960
commit 06233d25ad
13 changed files with 403 additions and 56 deletions

View file

@ -3,7 +3,7 @@ org.gradle.daemon=false
org.gradle.jvmargs=-Xmx8G
version_minecraft=1.12.2
version_forge=14.23.5.2768
version_engineersdecor=1.0.1-b3
version_engineersdecor=1.0.1-b4
#
# jar signing data loaded from signing.properties in the project root.
#

View file

@ -1,6 +1,7 @@
{
"homepage": "https://www.curseforge.com/minecraft/mc-mods/engineers-decor/",
"1.12.2": {
"1.0.1-b4": "[M] Crafting table keeps inventory and has eight storage slots.\n[M] Adapted inset light strength and harvest tool.\n[M] Crafting table recipe adapted.",
"1.0.1-b3": "[A] Added inset light (glowstone-metal, light level like torch, can be used as floor/ceiling/wall light).\n[M] Crafting table model updated (issue #7, thanks majijn).\n[M] Logo image updated.",
"1.0.1-b2": "[A] Added treated wood crafting table.\n[A] Added treated wood stool.\n[F] Fixed ladder bounding boxes to allow climbing connected trap doors (issue #6, thanks to Forgilageord).\n[M] Improved wall-block connections (wall elements only connect to other walls or gates, as well as to solid blocks if these blocks are in a straight line with at least two wall elements).\n[M] Decor walls are defined \"solid\" on top, so that e.g. torches and redstone tracks can be placed on them.",
"1.0.1-b1": "[F] Fixed missing condition for ie:stone_deco in recipe constants.\n[A] Added clinker brick wall.",
@ -12,6 +13,6 @@
},
"promos": {
"1.12.2-recommended": "1.0.0",
"1.12.2-latest": "1.0.1-b3"
"1.12.2-latest": "1.0.1-b4"
}
}

View file

@ -10,6 +10,10 @@ Mod sources for Minecraft version 1.12.2.
----
## Revision history
- v1.0.1-b4 [M] Crafting table keeps inventory and has eight storage slots.
[M] Adapted inset light strength and harvest tool.
[M] Crafting table recipe adapted.
- v1.0.1-b3 [A] Added inset light (glowstone-metal, light level like torch,
can be used as floor/ceiling/wall light).
[M] Crafting table model updated (issue #7, thanks majijn).

View file

@ -126,8 +126,7 @@ public class ModEngineersDecor
final BlockPos pos = new BlockPos(x,y,z);
final TileEntity te = world.getTileEntity(pos);
switch(guiid) {
case GUIID_CRAFTING_TABLE:
return (te instanceof BlockDecorCraftingTable.BEntity) ? (new BlockDecorCraftingTable.BContainer(player.inventory, world, pos)) : null;
case GUIID_CRAFTING_TABLE: return BlockDecorCraftingTable.getServerGuiElement(player, world, pos, te);
}
return null;
}
@ -139,8 +138,7 @@ public class ModEngineersDecor
final BlockPos pos = new BlockPos(x,y,z);
final TileEntity te = (world instanceof WorldClient) ? world.getTileEntity(pos) : null;
switch(guiid) {
case GUIID_CRAFTING_TABLE:
return (te instanceof BlockDecorCraftingTable.BEntity) ? (new BlockDecorCraftingTable.BGuiCrafting(player.inventory, world, pos)) : null;
case GUIID_CRAFTING_TABLE: return BlockDecorCraftingTable.getClientGuiElement(player, world, pos, te);
}
return null;
}

View file

@ -165,7 +165,6 @@ public class BlockDecor extends Block
{ return super.getStateForPlacement(world, pos, facing, hitX, hitY, hitZ, meta, placer, hand); }
@Override
public boolean removedByPlayer(IBlockState state, World world, BlockPos pos, EntityPlayer player, boolean willHarvest)
{ return super.removedByPlayer(state, world, pos, player, willHarvest); }
public boolean canHarvestBlock(IBlockAccess world, BlockPos pos, EntityPlayer player)
{ return true; }
}

View file

@ -8,25 +8,33 @@
*/
package wile.engineersdecor.blocks;
import wile.engineersdecor.ModEngineersDecor;
import net.minecraft.block.Block;
import net.minecraft.block.SoundType;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.world.Explosion;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.inventory.*;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.NonNullList;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.World;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import wile.engineersdecor.ModEngineersDecor;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -34,6 +42,7 @@ import javax.annotation.Nullable;
public class BlockDecorCraftingTable extends BlockDecorDirected
{
public BlockDecorCraftingTable(@Nonnull String registryName, long config, @Nullable Material material, float hardness, float resistance, @Nullable SoundType sound, @Nonnull AxisAlignedBB unrotatedAABB)
{
super(registryName, config, material, hardness, resistance, sound, unrotatedAABB);
@ -51,7 +60,7 @@ public class BlockDecorCraftingTable extends BlockDecorDirected
@Nullable
public TileEntity createTileEntity(World world, IBlockState state)
{ return new BlockDecorCraftingTable.BEntity(); }
{ return new BlockDecorCraftingTable.BTileEntity(); }
@Override
public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ)
@ -61,45 +70,71 @@ public class BlockDecorCraftingTable extends BlockDecorDirected
return true;
}
//--------------------------------------------------------------------------------------------------------------------
// TE
//--------------------------------------------------------------------------------------------------------------------
public static class BEntity extends TileEntity
@Override
public void onBlockPlacedBy(World world, BlockPos pos, IBlockState state, EntityLivingBase placer, ItemStack stack)
{
if(world.isRemote) return;
if((!stack.hasTagCompound()) || (!stack.getTagCompound().hasKey("inventory"))) return;
NBTTagCompound inventory_nbt = stack.getTagCompound().getCompoundTag("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();
}
private ItemStack itemize_with_inventory(World world, BlockPos pos)
{
TileEntity te = world.getTileEntity(pos);
if(!(te instanceof BTileEntity)) return ItemStack.EMPTY;
ItemStack stack = new ItemStack(this, 1);
NBTTagCompound inventory_nbt = new NBTTagCompound();
ItemStackHelper.saveAllItems(inventory_nbt, ((BTileEntity)te).stacks, false);
if(!inventory_nbt.isEmpty()) {
NBTTagCompound nbt = new NBTTagCompound();
nbt.setTag("inventory", inventory_nbt);
stack.setTagCompound(nbt);
}
return stack;
}
@Override
public boolean removedByPlayer(IBlockState state, World world, BlockPos pos, EntityPlayer player, boolean willHarvest)
{
if(world.isRemote) return true;
final ItemStack stack = itemize_with_inventory(world, pos);
if(stack != ItemStack.EMPTY) {
world.spawnEntity(new EntityItem(world, pos.getX()+0.5, pos.getY()+0.5, pos.getZ()+0.5, stack));
world.setBlockToAir(pos);
world.removeTileEntity(pos);
return false;
} else {
return super.removedByPlayer(state, world, pos, player, willHarvest);
}
}
@Override
public void onBlockExploded(World world, BlockPos pos, Explosion explosion)
{
if(world.isRemote) return;
TileEntity te = world.getTileEntity(pos);
if(!(te instanceof BTileEntity)) return;
for(ItemStack stack: ((BTileEntity)te).stacks) {
if(!stack.isEmpty()) world.spawnEntity(new EntityItem(world, pos.getX(), pos.getY(), pos.getZ(), stack));
}
((BTileEntity)te).reset();
super.onBlockExploded(world, pos, explosion);
}
//--------------------------------------------------------------------------------------------------------------------
// Container
// ModEngineersDecor.GuiHandler connectors
//--------------------------------------------------------------------------------------------------------------------
public static class BContainer extends ContainerWorkbench
{
private World world;
private BlockPos pos;
private EntityPlayer player;
public static Object getServerGuiElement(final EntityPlayer player, final World world, final BlockPos pos, final TileEntity te)
{ return (te instanceof BTileEntity) ? (new BContainer(player.inventory, world, pos, (BTileEntity)te)) : null; }
public BContainer(InventoryPlayer inv, World world, BlockPos pos)
{
super(inv, world, pos);
this.world = world;
this.pos = pos;
this.player = inv.player;
}
@Override
public boolean canInteractWith(EntityPlayer playerIn)
{ return (world.getBlockState(this.pos).getBlock() instanceof BlockDecorCraftingTable) && (playerIn.getDistanceSq(this.pos) <= 8*8); }
@Override
public void onCraftMatrixChanged(IInventory inv)
{
try {
slotChangedCraftingGrid(this.world, player, this.craftMatrix, this.craftResult);
} catch(Throwable exc) {
ModEngineersDecor.logger.error("Recipe failed:", exc);
}
}
}
public static Object getClientGuiElement(final EntityPlayer player, final World world, final BlockPos pos, final TileEntity te)
{ return (te instanceof BTileEntity) ? (new BGuiCrafting(player.inventory, world, pos, (BTileEntity)te)) : null; }
//--------------------------------------------------------------------------------------------------------------------
// GUI
@ -108,8 +143,8 @@ public class BlockDecorCraftingTable extends BlockDecorDirected
@SideOnly(Side.CLIENT)
public static class BGuiCrafting extends GuiContainer
{
public BGuiCrafting(InventoryPlayer playerInventory, World world, BlockPos pos)
{ super(new BContainer(playerInventory, world, pos)); }
public BGuiCrafting(InventoryPlayer playerInventory, World world, BlockPos pos, BTileEntity te)
{ super(new BContainer(playerInventory, world, pos, te)); }
@Override
public void drawScreen(int mouseX, int mouseY, float partialTicks)
@ -128,4 +163,302 @@ public class BlockDecorCraftingTable extends BlockDecorDirected
}
}
//--------------------------------------------------------------------------------------------------------------------
// Crafting container
//--------------------------------------------------------------------------------------------------------------------
public static class BContainer extends Container
{
private final World world;
private final BlockPos pos;
private final EntityPlayer player;
private final BTileEntity te;
public BInventoryCrafting craftMatrix;
public InventoryCraftResult craftResult = new InventoryCraftResult();
public BContainer(InventoryPlayer playerInventory, World world, BlockPos pos, BTileEntity te)
{
this.player = playerInventory.player;
this.world = world;
this.pos = pos;
this.te = te;
this.craftMatrix = new BInventoryCrafting(this, te);
this.craftMatrix.openInventory(player);
this.addSlotToContainer(new SlotCrafting(playerInventory.player, this.craftMatrix, this.craftResult, 0, 124+14, 35));
for(int y=0; y<3; ++y) {
for(int x=0; x<3; ++x) {
addSlotToContainer(new Slot(this.craftMatrix, x+y*3, 28+30+x*18, 17+y*18)); // block slots 0..8
}
}
for(int y=0; y<3; ++y) {
for (int x=0; x<9; ++x) {
this.addSlotToContainer(new Slot(playerInventory, x+y*9+9, 8+x*18, 86+y*18)); // player slots: 9..35
}
}
for (int x=0; x<9; ++x) {
this.addSlotToContainer(new Slot(playerInventory, x, 8+x*18, 144)); // player slots: 0..8
}
for(int y=0; y<4; ++y) {
for (int x=0; x<2; ++x) {
this.addSlotToContainer(new Slot(this.craftMatrix, x+y*2+9, 8+x*18, 9+y*18)); // block slots 9..17
}
}
this.onCraftMatrixChanged(this.craftMatrix);
}
@Override
public boolean canInteractWith(EntityPlayer player)
{ return (world.getBlockState(pos).getBlock() instanceof BlockDecorCraftingTable) && (player.getDistanceSq(pos) <= 64); }
@Override
public void onCraftMatrixChanged(IInventory inv)
{
try {
slotChangedCraftingGrid(this.world, this.player, this.craftMatrix, this.craftResult);
} catch(Throwable exc) {
ModEngineersDecor.logger.error("Recipe failed:", exc);
}
}
@Override
public void onContainerClosed(EntityPlayer player)
{
craftMatrix.closeInventory(player);
craftResult.clear();
craftResult.closeInventory(player);
if(player!=null) {
for(Slot e:player.inventoryContainer.inventorySlots) {
if(e instanceof SlotCrafting) {
((SlotCrafting)e).putStack(ItemStack.EMPTY);
}
}
}
}
@Override
public boolean canMergeSlot(ItemStack stack, Slot slot)
{ return (slot.inventory != this.craftResult) && (super.canMergeSlot(stack, slot)); }
@Override
public ItemStack transferStackInSlot(EntityPlayer playerIn, int index)
{
ItemStack stack = ItemStack.EMPTY;
Slot slot = this.inventorySlots.get(index);
if((slot == null) || (!slot.getHasStack())) return stack;
ItemStack slotstack = slot.getStack();
stack = slotstack.copy();
if(index == 0) {
slotstack.getItem().onCreated(slotstack, this.world, playerIn);
if(!this.mergeItemStack(slotstack, 10, 46, true)) return ItemStack.EMPTY;
slot.onSlotChange(slotstack, stack);
} else if (index >= 10 && (index < 37)) {
if(!this.mergeItemStack(slotstack, 37, 46, false)) return ItemStack.EMPTY;
} else if((index >= 37) && (index < 46)) {
if(!this.mergeItemStack(slotstack, 10, 37, 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(playerIn, slotstack);
if(index == 0) {
playerIn.dropItem(itemstack2, false);
}
return stack;
}
}
//--------------------------------------------------------------------------------------------------------------------
// Crafting inventory (needed to allow SlotCrafting to have a InventoryCrafting)
//--------------------------------------------------------------------------------------------------------------------
public static class BInventoryCrafting extends InventoryCrafting
{
protected final Container container;
protected final IInventory inventory;
public BInventoryCrafting(Container container_, IInventory inventory_te) {
super(container_, 3, 3);
container = container_;
inventory = inventory_te;
}
@Override
public int getSizeInventory()
{ return 9; }
@Override
public void openInventory(EntityPlayer player)
{ inventory.openInventory(player); }
@Override
public void closeInventory(EntityPlayer 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;
}
}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
public static class BTileEntity extends TileEntity implements IInventory
{
public static final int NUM_OF_CRAFTING_SLOTS = 9;
public static final int NUM_OF_STORAGE_SLOTS = 9;
public static final int NUM_OF_SLOTS = NUM_OF_CRAFTING_SLOTS+NUM_OF_STORAGE_SLOTS;
protected NonNullList<ItemStack> stacks;
public BTileEntity()
{ stacks = NonNullList.<ItemStack>withSize(NUM_OF_SLOTS, ItemStack.EMPTY); }
public void reset()
{ stacks = NonNullList.<ItemStack>withSize(NUM_OF_SLOTS, ItemStack.EMPTY); }
public void readnbt(NBTTagCompound compound)
{
reset();
ItemStackHelper.loadAllItems(compound, this.stacks);
while(this.stacks.size() < NUM_OF_SLOTS) this.stacks.add(ItemStack.EMPTY);
}
private void writenbt(NBTTagCompound compound)
{ ItemStackHelper.saveAllItems(compound, this.stacks); }
// TileEntity ------------------------------------------------------------------------------
@Override
public boolean shouldRefresh(World world, BlockPos pos, IBlockState os, IBlockState ns)
{ return (os.getBlock() != ns.getBlock()) || (!(ns.getBlock() instanceof BlockDecorCraftingTable)); }
@Override
public void readFromNBT(NBTTagCompound compound)
{ super.readFromNBT(compound); readnbt(compound); }
@Override
public NBTTagCompound writeToNBT(NBTTagCompound compound)
{ super.writeToNBT(compound); writenbt(compound); return compound; }
//@Override
//public NBTTagCompound getUpdateTag()
//{ return writeToNBT(new NBTTagCompound()); }
//@Override
//public SPacketUpdateTileEntity getUpdatePacket()
//{ return new SPacketUpdateTileEntity(getPos(), 0x1, getUpdateTag()); }
//
//@Override
//public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt)
//{ readFromNBT(pkt.getNbtCompound()); super.onDataPacket(net, pkt); }
// IWorldNamable ---------------------------------------------------------------------------
@Override
public String getName()
{ final Block block=getBlockType(); return (block!=null) ? (block.getTranslationKey() + ".name") : (""); }
@Override
public boolean hasCustomName()
{ return false; }
@Override
public ITextComponent getDisplayName()
{ return new TextComponentTranslation(getName(), new Object[0]); }
// IInventory ------------------------------------------------------------------------------
// @see net.minecraft.inventory.InventoryCrafting
@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(EntityPlayer player)
{ return true; }
@Override
public void openInventory(EntityPlayer player)
{}
@Override
public void closeInventory(EntityPlayer player)
{ this.markDirty(); }
@Override
public boolean isItemValidForSlot(int index, ItemStack stack)
{ return true; }
@Override
public int getField(int id)
{ return 0; }
@Override
public void setField(int id, int value)
{}
@Override
public int getFieldCount()
{ return 0; }
@Override
public void clear()
{ stacks.clear(); }
}
}

View file

@ -175,4 +175,9 @@ public class BlockDecorWall extends BlockDecor
private boolean canWallConnectTo(IBlockAccess world, BlockPos pos, EnumFacing facing)
{ return canConnectTo(world, pos, pos.offset(facing), facing.getOpposite()); }
@Override
public boolean canPlaceTorchOnTop(IBlockState state, IBlockAccess world, BlockPos pos)
{ return true; }
}

View file

@ -84,7 +84,7 @@ public class ModBlocks
public static final BlockDecorDirected INSET_LIGHT_IRON = new BlockDecorDirected(
"iron_inset_light",
BlockDecor.CFG_CUTOUT|BlockDecor.CFG_OPPOSITE_PLACEMENT|(14<<BlockDecor.CFG_LIGHT_VALUE_SHIFT),
Material.IRON, 1.0f, 15f, SoundType.METAL,
Material.IRON, 0.3f, 15f, SoundType.METAL,
ModAuxiliaries.getPixeledAABB(5.2,5.2,15.7, 10.8,10.8,16.0)
);
@ -138,7 +138,7 @@ public class ModBlocks
for(Block e:registeredBlocks) event.getRegistry().register(e);
ModEngineersDecor.logger.info("Registered " + Integer.toString(registeredBlocks.size()) + " blocks.");
// TEs
GameRegistry.registerTileEntity(BlockDecorCraftingTable.BEntity.class, new ResourceLocation(ModEngineersDecor.MODID, "te_crafting_table"));
GameRegistry.registerTileEntity(BlockDecorCraftingTable.BTileEntity.class, new ResourceLocation(ModEngineersDecor.MODID, "te_crafting_table"));
}
// Invoked from ClientProxy.registerModels()

View file

@ -184,6 +184,15 @@
},
"name": "paneGlass"
},
{
"ingredient": {
"item": "minecraft:crafting_table",
"data": 0
},
"name": "itemCraftingTable"
},
{
"ingredient": [
{

View file

@ -8,20 +8,16 @@
],
"type": "minecraft:crafting_shaped",
"pattern": [
"SS",
"PP",
"PC"
],
"key": {
"S": {
"item": "#slabTreatedWood",
"data": 0
},
"P": {
"item": "#plankTreatedWood",
"data": 0
},
"C": {
"item": "#crateTreatedWood",
"item": "#itemCraftingTable",
"data": 0
}
},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

Before After
Before After

View file

@ -1,6 +1,7 @@
{
"homepage": "https://www.curseforge.com/minecraft/mc-mods/engineers-decor/",
"1.12.2": {
"1.0.1-b4": "[M] Crafting table keeps inventory and has eight storage slots.\n[M] Adapted inset light strength and harvest tool.\n[M] Crafting table recipe adapted.",
"1.0.1-b3": "[A] Added inset light (glowstone-metal, light level like torch, can be used as floor/ceiling/wall light).\n[M] Crafting table model updated (issue #7, thanks majijn).\n[M] Logo image updated.",
"1.0.1-b2": "[A] Added treated wood crafting table.\n[A] Added treated wood stool.\n[F] Fixed ladder bounding boxes to allow climbing connected trap doors (issue #6, thanks to Forgilageord).\n[M] Improved wall-block connections (wall elements only connect to other walls or gates, as well as to solid blocks if these blocks are in a straight line with at least two wall elements).\n[M] Decor walls are defined \"solid\" on top, so that e.g. torches and redstone tracks can be placed on them.",
"1.0.1-b1": "[F] Fixed missing condition for ie:stone_deco in recipe constants.\n[A] Added clinker brick wall.",
@ -15,7 +16,7 @@
},
"promos": {
"1.12.2-recommended": "1.0.0",
"1.12.2-latest": "1.0.1-b3",
"1.12.2-latest": "1.0.1-b4",
"1.13.2-recommended": "",
"1.13.2-latest": "1.0.0-a1"
}

View file

@ -42,8 +42,9 @@ no tile entities or user interactions are used. Current feature set:
with reverse recipe.
- *Treated wood crafting table*: 3x3 crafting table with IE style GUI and a model
fitting better in the engineer's workshop. Crafted 2x2 with two treated wood
slabs, one crate, and one treated wood plank.
fitting better in the engineer's workshop. Keeps its inventory, has eight additional
storage slots on the left side of the crafting grid. Crafted 2x2 with three
treated wood planks and one vanilla crafting table.
- *Treated wood ladder*: Crafted 3x3 with the known ladder pattern, items are
treated wood sticks.