diff --git a/src/main/java/com/hbm/blocks/network/BlockCraneBase.java b/src/main/java/com/hbm/blocks/network/BlockCraneBase.java index b0ae95010..d2840e41c 100644 --- a/src/main/java/com/hbm/blocks/network/BlockCraneBase.java +++ b/src/main/java/com/hbm/blocks/network/BlockCraneBase.java @@ -1,7 +1,9 @@ package com.hbm.blocks.network; import com.hbm.lib.RefStrings; +import com.hbm.main.MainRegistry; +import cpw.mods.fml.common.network.internal.FMLNetworkHandler; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import net.minecraft.block.BlockContainer; @@ -9,6 +11,7 @@ import net.minecraft.block.BlockPistonBase; import net.minecraft.block.material.Material; import net.minecraft.client.renderer.texture.IIconRegister; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.util.IIcon; import net.minecraft.world.World; @@ -41,6 +44,18 @@ public abstract class BlockCraneBase extends BlockContainer { this.iconSideOut = iconRegister.registerIcon(RefStrings.MODID + ":crane_side_out"); } + @Override + public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int side, float hitX, float hitY, float hitZ) { + if(world.isRemote) { + return true; + } else if(!player.isSneaking()) { + FMLNetworkHandler.openGui(player, MainRegistry.instance, 0, world, x, y, z); + return true; + } else { + return false; + } + } + public void onBlockPlacedBy(World world, int x, int y, int z, EntityLivingBase player, ItemStack stack) { int l = BlockPistonBase.determineOrientation(world, x, y, z, player); world.setBlockMetadataWithNotify(x, y, z, l, 2); diff --git a/src/main/java/com/hbm/blocks/network/CraneExtractor.java b/src/main/java/com/hbm/blocks/network/CraneExtractor.java index e5c7b08ee..c7bf7dce7 100644 --- a/src/main/java/com/hbm/blocks/network/CraneExtractor.java +++ b/src/main/java/com/hbm/blocks/network/CraneExtractor.java @@ -26,7 +26,7 @@ public class CraneExtractor extends BlockCraneBase { public void registerBlockIcons(IIconRegister iconRegister) { super.registerBlockIcons(iconRegister); this.iconDirectional = iconRegister.registerIcon(RefStrings.MODID + ":crane_out_top"); - this.iconDirectionalUp = iconRegister.registerIcon(RefStrings.MODID + ":crane_out_side_up"); - this.iconDirectionalDown = iconRegister.registerIcon(RefStrings.MODID + ":crane_out_side_down"); + this.iconDirectionalUp = iconRegister.registerIcon(RefStrings.MODID + ":crane_out_side_down"); + this.iconDirectionalDown = iconRegister.registerIcon(RefStrings.MODID + ":crane_out_side_up"); } } diff --git a/src/main/java/com/hbm/blocks/network/CraneInserter.java b/src/main/java/com/hbm/blocks/network/CraneInserter.java index a617aaa10..69b3b6a5c 100644 --- a/src/main/java/com/hbm/blocks/network/CraneInserter.java +++ b/src/main/java/com/hbm/blocks/network/CraneInserter.java @@ -1,18 +1,15 @@ package com.hbm.blocks.network; import com.hbm.lib.RefStrings; -import com.hbm.main.MainRegistry; import com.hbm.tileentity.network.TileEntityCraneInserter; import api.hbm.conveyor.IConveyorItem; import api.hbm.conveyor.IEnterableBlock; -import cpw.mods.fml.common.network.internal.FMLNetworkHandler; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import net.minecraft.block.material.Material; import net.minecraft.client.renderer.texture.IIconRegister; import net.minecraft.entity.item.EntityItem; -import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.IInventory; import net.minecraft.inventory.ISidedInventory; import net.minecraft.item.ItemStack; @@ -39,18 +36,6 @@ public class CraneInserter extends BlockCraneBase implements IEnterableBlock { this.iconDirectionalUp = iconRegister.registerIcon(RefStrings.MODID + ":crane_in_side_up"); this.iconDirectionalDown = iconRegister.registerIcon(RefStrings.MODID + ":crane_in_side_down"); } - - @Override - public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int side, float hitX, float hitY, float hitZ) { - if(world.isRemote) { - return true; - } else if(!player.isSneaking()) { - FMLNetworkHandler.openGui(player, MainRegistry.instance, 0, world, x, y, z); - return true; - } else { - return false; - } - } @Override public boolean canEnter(World world, int x, int y, int z, ForgeDirection dir, IConveyorItem entity) { diff --git a/src/main/java/com/hbm/inventory/container/ContainerCraneExtractor.java b/src/main/java/com/hbm/inventory/container/ContainerCraneExtractor.java new file mode 100644 index 000000000..c12d09b57 --- /dev/null +++ b/src/main/java/com/hbm/inventory/container/ContainerCraneExtractor.java @@ -0,0 +1,130 @@ +package com.hbm.inventory.container; + +import com.hbm.inventory.SlotUpgrade; +import com.hbm.items.ModItems; +import com.hbm.tileentity.network.TileEntityCraneExtractor; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; + +public class ContainerCraneExtractor extends Container { + + protected TileEntityCraneExtractor inserter; + + public ContainerCraneExtractor(InventoryPlayer invPlayer, TileEntityCraneExtractor inserter) { + this.inserter = inserter; + + //filter + for(int i = 0; i < 3; i++) { + for(int j = 0; j < 3; j++) { + this.addSlotToContainer(new Slot(inserter, j + i * 3, 71 + j * 18, 17 + i * 18)); + } + } + + //buffer + for(int i = 0; i < 3; i++) { + for(int j = 0; j < 3; j++) { + this.addSlotToContainer(new Slot(inserter, 9 + j + i * 3, 8 + j * 18, 17 + i * 18)); + } + } + + //upgrades + this.addSlotToContainer(new SlotUpgrade(inserter, 18, 152, 23)); + this.addSlotToContainer(new SlotUpgrade(inserter, 19, 152, 47)); + + for(int i = 0; i < 3; i++) { + for(int j = 0; j < 9; j++) { + this.addSlotToContainer(new Slot(invPlayer, j + i * 9 + 9, 8 + j * 18, 103 + i * 18)); + } + } + + for(int i = 0; i < 9; i++) { + this.addSlotToContainer(new Slot(invPlayer, i, 8 + i * 18, 161)); + } + } + + @Override + public ItemStack transferStackInSlot(EntityPlayer player, int slot) { + ItemStack var3 = null; + Slot var4 = (Slot) this.inventorySlots.get(slot); + + if(var4 != null && var4.getHasStack()) { + ItemStack var5 = var4.getStack(); + var3 = var5.copy(); + + if(slot < 9) { //filters + return null; + } + + if(slot <= inserter.getSizeInventory() - 1) { + if(!this.mergeItemStack(var5, inserter.getSizeInventory(), this.inventorySlots.size(), true)) { + return null; + } + } else { + + if(var3.getItem() == ModItems.upgrade_5g) { + if(!this.mergeItemStack(var5, 18, 19, false)) + return null; + } else if(var3.getItem() == ModItems.upgrade_crystallizer) { + if(!this.mergeItemStack(var5, 19, 20, false)) + return null; + } else if(!this.mergeItemStack(var5, 9, inserter.getSizeInventory(), false)) { + return null; + } + + return null; + } + + if(var5.stackSize == 0) { + var4.putStack((ItemStack) null); + } else { + var4.onSlotChanged(); + } + + var4.onPickupFromSlot(player, var5); + } + + return var3; + } + + @Override + public boolean canInteractWith(EntityPlayer player) { + return inserter.isUseableByPlayer(player); + } + + @Override + public ItemStack slotClick(int index, int button, int mode, EntityPlayer player) { + + //L/R: 0 + //M3: 3 + //SHIFT: 1 + //DRAG: 5 + //System.out.println("Mode " + mode); + //System.out.println("Slot " + index); + + if(index < 0 || index > 8) { + return super.slotClick(index, button, mode, player); + } + + Slot slot = this.getSlot(index); + + ItemStack ret = null; + ItemStack held = player.inventory.getItemStack(); + + if(slot.getHasStack()) + ret = slot.getStack().copy(); + + slot.putStack(held != null ? held.copy() : null); + + if(slot.getHasStack()) { + slot.getStack().stackSize = 1; + } + + slot.onSlotChanged(); + + return ret; + } +} diff --git a/src/main/java/com/hbm/inventory/gui/GUICraneExtractor.java b/src/main/java/com/hbm/inventory/gui/GUICraneExtractor.java new file mode 100644 index 000000000..04ccad591 --- /dev/null +++ b/src/main/java/com/hbm/inventory/gui/GUICraneExtractor.java @@ -0,0 +1,63 @@ +package com.hbm.inventory.gui; + +import org.lwjgl.opengl.GL11; + +import com.hbm.inventory.container.ContainerCraneExtractor; +import com.hbm.lib.RefStrings; +import com.hbm.packet.NBTControlPacket; +import com.hbm.packet.PacketDispatcher; +import com.hbm.tileentity.network.TileEntityCraneExtractor; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.audio.PositionedSoundRecord; +import net.minecraft.client.resources.I18n; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.ResourceLocation; + +public class GUICraneExtractor extends GuiInfoContainer { + + private static ResourceLocation texture = new ResourceLocation(RefStrings.MODID + ":textures/gui/storage/gui_crane_ejector.png"); + private TileEntityCraneExtractor inserter; + + public GUICraneExtractor(InventoryPlayer invPlayer, TileEntityCraneExtractor tedf) { + super(new ContainerCraneExtractor(invPlayer, tedf)); + inserter = tedf; + + this.xSize = 176; + this.ySize = 185; + } + + @Override + protected void mouseClicked(int x, int y, int i) { + super.mouseClicked(x, y, i); + + if(guiLeft + 128 <= x && guiLeft + 128 + 14 > x && guiTop + 30 < y && guiTop + 30 + 26 >= y) { + + mc.getSoundHandler().playSound(PositionedSoundRecord.func_147674_a(new ResourceLocation("gui.button.press"), 1.0F)); + NBTTagCompound data = new NBTTagCompound(); + data.setBoolean("whitelist", true); + PacketDispatcher.wrapper.sendToServer(new NBTControlPacket(data, inserter.xCoord, inserter.yCoord, inserter.zCoord)); + } + } + + @Override + protected void drawGuiContainerForegroundLayer(int i, int j) { + String name = this.inserter.hasCustomInventoryName() ? this.inserter.getInventoryName() : I18n.format(this.inserter.getInventoryName()); + this.fontRendererObj.drawString(name, this.xSize / 2 - this.fontRendererObj.getStringWidth(name) / 2, 6, 4210752); + this.fontRendererObj.drawString(I18n.format("container.inventory"), 8, this.ySize - 96 + 2, 4210752); + } + + @Override + protected void drawGuiContainerBackgroundLayer(float p_146976_1_, int p_146976_2_, int p_146976_3_) { + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + Minecraft.getMinecraft().getTextureManager().bindTexture(texture); + drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize); + + if(inserter.isWhitelist) { + drawTexturedModalRect(guiLeft + 139, guiTop + 33, 176, 0, 3, 6); + } else { + drawTexturedModalRect(guiLeft + 139, guiTop + 47, 176, 0, 3, 6); + } + } +} diff --git a/src/main/java/com/hbm/main/CraftingManager.java b/src/main/java/com/hbm/main/CraftingManager.java index c35eae0b3..bd8110eec 100644 --- a/src/main/java/com/hbm/main/CraftingManager.java +++ b/src/main/java/com/hbm/main/CraftingManager.java @@ -919,7 +919,19 @@ public class CraftingManager { addRecipeAuto(DictFrame.fromOne(ModItems.part_generic, EnumPartTpe.PISTON_PNEUMATIC, 4), new Object[] { " I ", "CPC", " I ", 'I', IRON.ingot(), 'C', CU.ingot(), 'P', IRON.plate() }); addRecipeAuto(DictFrame.fromOne(ModItems.part_generic, EnumPartTpe.PISTON_HYDRAULIC, 4), new Object[] { " I ", "CPC", " I ", 'I', STEEL.ingot(), 'C', TI.ingot(), 'P', Fluids.LUBRICANT.getDict(1000) }); addRecipeAuto(DictFrame.fromOne(ModItems.part_generic, EnumPartTpe.PISTON_ELECTRIC, 4), new Object[] { " I ", "CPC", " I ", 'I', TCALLOY.ingot(), 'C', ANY_PLASTIC.ingot(), 'P', ModItems.motor }); - + + Object[] craneCasing = new Object[] { + Blocks.stonebrick, 1, + IRON.ingot(), 2, + STEEL.ingot(), 4 + }; + + for(int i = 0; i < craneCasing.length / 2; i++) { + Object casing = craneCasing[i * 2]; + int amount = (int) craneCasing[i * 2 + 1]; + addRecipeAuto(new ItemStack(ModBlocks.crane_inserter, amount), new Object[] { "CCC", "C C", "CBC", 'C', casing, 'B', ModBlocks.conveyor }); + addRecipeAuto(new ItemStack(ModBlocks.crane_extractor, amount), new Object[] { "CCC", "CPC", "CBC", 'C', casing, 'B', ModBlocks.conveyor, 'P', DictFrame.fromOne(ModItems.part_generic, EnumPartTpe.PISTON_PNEUMATIC) }); + } addShapelessAuto(DictFrame.fromOne(ModItems.parts_legendary, EnumLegendaryType.TIER1), new Object[] { ModItems.ingot_chainsteel, ASBESTOS.ingot(), ModItems.gem_alexandrite }); addShapelessAuto(DictFrame.fromOne(ModItems.parts_legendary, EnumLegendaryType.TIER1, 3), new Object[] { DictFrame.fromOne(ModItems.parts_legendary, EnumLegendaryType.TIER2) }); diff --git a/src/main/java/com/hbm/tileentity/network/TileEntityCraneExtractor.java b/src/main/java/com/hbm/tileentity/network/TileEntityCraneExtractor.java index 4d1d5f2f2..0072fd325 100644 --- a/src/main/java/com/hbm/tileentity/network/TileEntityCraneExtractor.java +++ b/src/main/java/com/hbm/tileentity/network/TileEntityCraneExtractor.java @@ -1,18 +1,31 @@ package com.hbm.tileentity.network; import com.hbm.entity.item.EntityMovingItem; +import com.hbm.interfaces.IControlReceiver; +import com.hbm.inventory.container.ContainerCraneExtractor; +import com.hbm.inventory.gui.GUICraneExtractor; +import com.hbm.tileentity.IGUIProvider; import com.hbm.tileentity.TileEntityMachineBase; import api.hbm.conveyor.IConveyorBelt; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; import net.minecraft.block.Block; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.Container; import net.minecraft.inventory.IInventory; import net.minecraft.inventory.ISidedInventory; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.Vec3; +import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; -public class TileEntityCraneExtractor extends TileEntityMachineBase { +public class TileEntityCraneExtractor extends TileEntityMachineBase implements IGUIProvider, IControlReceiver { + + public boolean isWhitelist = false; public TileEntityCraneExtractor() { super(20); @@ -26,46 +39,114 @@ public class TileEntityCraneExtractor extends TileEntityMachineBase { @Override public void updateEntity() { - if(!worldObj.isRemote && worldObj.getTotalWorldTime() % 20 == 0) { + if(!worldObj.isRemote) { - int amount = 16; - - ForgeDirection dir = ForgeDirection.getOrientation(this.blockMetadata); - TileEntity te = worldObj.getTileEntity(xCoord - dir.offsetX, yCoord - dir.offsetY, zCoord - dir.offsetZ); - Block b = worldObj.getBlock(xCoord + dir.offsetX, yCoord + dir.offsetY, zCoord + dir.offsetZ); - - int[] access = null; - ISidedInventory sided = null; - - if(te instanceof ISidedInventory) { - sided = (ISidedInventory) te; - access = sided.getAccessibleSlotsFromSide(dir.ordinal()); - } - - if(b instanceof IConveyorBelt && te instanceof IInventory) { - IInventory inv = (IInventory) te; - int size = access == null ? inv.getSizeInventory() : access.length; + if(worldObj.getTotalWorldTime() % 20 == 0) { + int amount = 16; + + ForgeDirection dir = ForgeDirection.getOrientation(this.blockMetadata); + TileEntity te = worldObj.getTileEntity(xCoord - dir.offsetX, yCoord - dir.offsetY, zCoord - dir.offsetZ); + Block b = worldObj.getBlock(xCoord + dir.offsetX, yCoord + dir.offsetY, zCoord + dir.offsetZ); - for(int i = 0; i < size; i++) { - int index = access == null ? i : access[i]; - ItemStack stack = inv.getStackInSlot(index); + int[] access = null; + ISidedInventory sided = null; + + if(te instanceof ISidedInventory) { + sided = (ISidedInventory) te; + access = sided.getAccessibleSlotsFromSide(dir.ordinal()); + } + + if(b instanceof IConveyorBelt && te instanceof IInventory) { + IInventory inv = (IInventory) te; + int size = access == null ? inv.getSizeInventory() : access.length; - if(stack != null && (sided == null || sided.canExtractItem(index, stack, dir.ordinal()))){ - stack = stack.copy(); - int toSend = Math.min(amount, stack.stackSize); - inv.decrStackSize(index, toSend); - stack.stackSize = toSend; + for(int i = 0; i < size; i++) { + int index = access == null ? i : access[i]; + ItemStack stack = inv.getStackInSlot(index); - EntityMovingItem moving = new EntityMovingItem(worldObj); - Vec3 pos = Vec3.createVectorHelper(xCoord + 0.5 + dir.offsetX * 0.55, yCoord + 0.5 + dir.offsetY * 0.55, zCoord + 0.5 + dir.offsetZ * 0.55); - Vec3 snap = ((IConveyorBelt) b).getClosestSnappingPosition(worldObj, xCoord + dir.offsetX, yCoord + dir.offsetY, zCoord + dir.offsetZ, pos); - moving.setPosition(snap.xCoord, snap.yCoord, snap.zCoord); - moving.setItemStack(stack); - worldObj.spawnEntityInWorld(moving); - break; + if(stack != null && (sided == null || sided.canExtractItem(index, stack, dir.ordinal()))){ + + boolean match = this.matchesFilter(stack); + + if((isWhitelist && match) || (!isWhitelist && !match)) { + stack = stack.copy(); + int toSend = Math.min(amount, stack.stackSize); + inv.decrStackSize(index, toSend); + stack.stackSize = toSend; + + EntityMovingItem moving = new EntityMovingItem(worldObj); + Vec3 pos = Vec3.createVectorHelper(xCoord + 0.5 + dir.offsetX * 0.55, yCoord + 0.5 + dir.offsetY * 0.55, zCoord + 0.5 + dir.offsetZ * 0.55); + Vec3 snap = ((IConveyorBelt) b).getClosestSnappingPosition(worldObj, xCoord + dir.offsetX, yCoord + dir.offsetY, zCoord + dir.offsetZ, pos); + moving.setPosition(snap.xCoord, snap.yCoord, snap.zCoord); + moving.setItemStack(stack); + worldObj.spawnEntityInWorld(moving); + break; + } + } } } } + + NBTTagCompound data = new NBTTagCompound(); + data.setBoolean("isWhitelist", isWhitelist); + this.networkPack(data, 15); + } + } + + public void networkUnpack(NBTTagCompound nbt) { + this.isWhitelist = nbt.getBoolean("isWhitelist"); + } + + public boolean matchesFilter(ItemStack stack) { + + for(int i = 0; i < 9; i++) { + ItemStack filter = slots[i]; + + if(filter != null && filter.isItemEqual(stack)) { + return true; + } + } + + return false; + } + + @Override + public boolean isItemValidForSlot(int i, ItemStack itemStack) { + return true; + } + + @Override + public Container provideContainer(int ID, EntityPlayer player, World world, int x, int y, int z) { + return new ContainerCraneExtractor(player.inventory, this); + } + + @Override + @SideOnly(Side.CLIENT) + public GuiScreen provideGUI(int ID, EntityPlayer player, World world, int x, int y, int z) { + return new GUICraneExtractor(player.inventory, this); + } + + @Override + public void readFromNBT(NBTTagCompound nbt) { + super.readFromNBT(nbt); + this.isWhitelist = nbt.getBoolean("isWhitelist"); + } + + @Override + public void writeToNBT(NBTTagCompound nbt) { + super.writeToNBT(nbt); + nbt.setBoolean("isWhitelist", this.isWhitelist); + } + + @Override + public boolean hasPermission(EntityPlayer player) { + return Vec3.createVectorHelper(xCoord - player.posX, yCoord - player.posY, zCoord - player.posZ).lengthVector() < 20; + } + + @Override + public void receiveControl(NBTTagCompound data) { + if(data.hasKey("whitelist")) { + this.isWhitelist = !this.isWhitelist; } } } diff --git a/src/main/java/com/hbm/tileentity/network/TileEntityCraneInserter.java b/src/main/java/com/hbm/tileentity/network/TileEntityCraneInserter.java index e5e3e90ff..c6fbbce03 100644 --- a/src/main/java/com/hbm/tileentity/network/TileEntityCraneInserter.java +++ b/src/main/java/com/hbm/tileentity/network/TileEntityCraneInserter.java @@ -19,6 +19,8 @@ import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; public class TileEntityCraneInserter extends TileEntityMachineBase implements IGUIProvider { + + public static final int[] access = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }; public TileEntityCraneInserter() { super(21); @@ -62,6 +64,11 @@ public class TileEntityCraneInserter extends TileEntityMachineBase implements IG } } + @Override + public int[] getAccessibleSlotsFromSide(int side) { + return access; + } + @Override public boolean isItemValidForSlot(int i, ItemStack itemStack) { return true; diff --git a/src/main/resources/assets/hbm/lang/de_DE.lang b/src/main/resources/assets/hbm/lang/de_DE.lang index 28ff298e5..b7e48229b 100644 --- a/src/main/resources/assets/hbm/lang/de_DE.lang +++ b/src/main/resources/assets/hbm/lang/de_DE.lang @@ -3114,6 +3114,7 @@ tile.concrete_pillar.name=Stahlbetonsäule tile.concrete_smooth.name=Beton tile.concrete_super.name=Super Beton tile.concrete_super_broken.name=Schimmliger Schutt +tile.conveyor.name=Förderband tile.corium_block.name=Corium tile.crashed_bomb.name=Blindgänger tile.crate.name=Vorratskiste diff --git a/src/main/resources/assets/hbm/lang/en_US.lang b/src/main/resources/assets/hbm/lang/en_US.lang index f0fb54273..c02675eb4 100644 --- a/src/main/resources/assets/hbm/lang/en_US.lang +++ b/src/main/resources/assets/hbm/lang/en_US.lang @@ -3506,6 +3506,7 @@ tile.concrete_pillar.name=Rebar Reinforced Concrete Pillar tile.concrete_smooth.name=Concrete tile.concrete_super.name=Über Concrete tile.concrete_super_broken.name=Moldy Debris +tile.conveyor.name=Conveyor Belt tile.corium_block.name=Corium tile.crashed_bomb.name=Dud tile.crate.name=Supply Crate