package openblocks.common.tileentity; import java.util.Set; import net.minecraft.block.Block; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Items; import net.minecraft.inventory.IInventory; import net.minecraft.inventory.ISidedInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraftforge.common.util.ForgeDirection; import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.IFluidHandler; import openblocks.OpenBlocks; import openblocks.client.gui.GuiXPBottler; import openblocks.common.LiquidXpUtils; import openblocks.common.container.ContainerXPBottler; import openblocks.common.tileentity.TileEntityXPBottler.AutoSlots; import openmods.api.IHasGui; import openmods.api.INeighbourAwareTile; import openmods.api.IValueProvider; import openmods.api.IValueReceiver; import openmods.gamelogic.WorkerLogic; import openmods.gui.misc.IConfigurableGuiSlots; import openmods.include.IncludeInterface; import openmods.include.IncludeOverride; import openmods.inventory.GenericInventory; import openmods.inventory.IInventoryProvider; import openmods.inventory.TileEntityInventory; import openmods.inventory.legacy.ItemDistribution; import openmods.liquids.SidedFluidHandler; import openmods.sync.SyncableFlags; import openmods.sync.SyncableInt; import openmods.sync.SyncableSides; import openmods.sync.SyncableTank; import openmods.tileentity.SyncedTileEntity; import openmods.utils.MiscUtils; import openmods.utils.SidedInventoryAdapter; import openmods.utils.bitmap.BitMapUtils; import openmods.utils.bitmap.IRpcDirectionBitMap; import openmods.utils.bitmap.IRpcIntBitMap; import openmods.utils.bitmap.IWriteableBitMap; public class TileEntityXPBottler extends SyncedTileEntity implements IInventoryProvider, IHasGui, IConfigurableGuiSlots<AutoSlots>, INeighbourAwareTile { public static final int TANK_CAPACITY = LiquidXpUtils.xpToLiquidRatio(LiquidXpUtils.XP_PER_BOTTLE); public static final int PROGRESS_TICKS = 40; protected static final ItemStack GLASS_BOTTLE = new ItemStack(Items.glass_bottle, 1); protected static final ItemStack XP_BOTTLE = new ItemStack(Items.experience_bottle, 1); private boolean needsTankUpdate; public static enum Slots { input, output } public static enum AutoSlots { input, output, xp } private final GenericInventory inventory = registerInventoryCallback(new TileEntityInventory(this, "xpbottler", true, 2) { @Override public boolean isItemValidForSlot(int slot, ItemStack itemstack) { if (slot != Slots.input.ordinal()) return false; return itemstack.getItem() == Items.glass_bottle; } }); @IncludeInterface(ISidedInventory.class) private final SidedInventoryAdapter sided = new SidedInventoryAdapter(inventory); /** synced data objects **/ private SyncableInt progress; private SyncableSides glassSides; private SyncableSides xpBottleSides; private SyncableSides xpSides; private SyncableFlags automaticSlots; private SyncableTank tank; private final WorkerLogic logic = new WorkerLogic(progress, PROGRESS_TICKS); @IncludeInterface private final IFluidHandler tankWrapper = new SidedFluidHandler.Drain(xpSides, tank); @Override protected void createSyncedFields() { progress = new SyncableInt(); glassSides = new SyncableSides(); xpBottleSides = new SyncableSides(); xpSides = new SyncableSides(); automaticSlots = SyncableFlags.create(AutoSlots.values().length); tank = new SyncableTank(TANK_CAPACITY, OpenBlocks.Fluids.xpJuice); } public TileEntityXPBottler() { sided.registerSlot(Slots.input, glassSides, true, false); sided.registerSlot(Slots.output, xpBottleSides, false, true); } @Override public void updateEntity() { super.updateEntity(); if (!worldObj.isRemote) { // if we should, we'll autofill the tank if (automaticSlots.get(AutoSlots.xp)) { if (needsTankUpdate) { tank.updateNeighbours(worldObj, getPosition()); needsTankUpdate = false; } tank.fillFromSides(10, worldObj, getPosition(), xpSides.getValue()); } // if they've ticked auto output, and we have something to output if (shouldAutoOutput() && hasOutputStack()) { ItemDistribution.moveItemsToOneOfSides(this, inventory, Slots.output.ordinal(), 1, xpBottleSides.getValue(), true); } // if we should auto input and we don't have any glass in the slot if (shouldAutoInput() && !hasGlassInInput()) { ItemDistribution.moveItemsFromOneOfSides(this, inventory, GLASS_BOTTLE, 1, Slots.input.ordinal(), glassSides.getValue(), true); } logic.checkWorkCondition(hasSpaceInOutput() && hasGlassInInput() && isTankFull()); if (logic.update()) { // this happens when the progress has completed worldObj.playSoundEffect(xCoord + 0.5, yCoord + 0.5, zCoord + 0.5, "openblocks:bottler.signal", .5f, .8f); inventory.decrStackSize(Slots.input.ordinal(), 1); tank.setFluid(null); ItemStack outputStack = inventory.getStackInSlot(Slots.output.ordinal()); if (outputStack == null) { inventory.setInventorySlotContents(Slots.output.ordinal(), XP_BOTTLE.copy()); } else { outputStack.stackSize++; } inventory.onInventoryChanged(Slots.output.ordinal()); } sync(); } } @Override public Object getServerGui(EntityPlayer player) { return new ContainerXPBottler(player.inventory, this); } @Override public Object getClientGui(EntityPlayer player) { return new GuiXPBottler(new ContainerXPBottler(player.inventory, this)); } @Override public boolean canOpenGui(EntityPlayer player) { return true; } public IValueProvider<Integer> getProgress() { return progress; } public boolean hasOutputStack() { ItemStack outputStack = inventory.getStackInSlot(1); return outputStack != null && outputStack.stackSize > 0; } public boolean shouldAutoInput() { return automaticSlots.get(AutoSlots.input); } public boolean shouldAutoOutput() { return automaticSlots.get(AutoSlots.output); } public boolean hasGlassInInput() { ItemStack inputStack = inventory.getStackInSlot(Slots.input.ordinal()); return inputStack != null && inputStack.isItemEqual(GLASS_BOTTLE); } public boolean hasSpaceInOutput() { ItemStack outputStack = inventory.getStackInSlot(Slots.output.ordinal()); return outputStack == null || (outputStack.isItemEqual(XP_BOTTLE) && outputStack.stackSize < outputStack.getMaxStackSize()); } public boolean isTankFull() { return tank.getFluidAmount() == tank.getCapacity(); } public IValueProvider<FluidStack> getFluidProvider() { return tank; } @IncludeOverride public boolean canDrain(ForgeDirection from, Fluid fluid) { return false; } @Override public IInventory getInventory() { return inventory; } @Override public void writeToNBT(NBTTagCompound tag) { super.writeToNBT(tag); inventory.writeToNBT(tag); } @Override public void readFromNBT(NBTTagCompound tag) { super.readFromNBT(tag); inventory.readFromNBT(tag); } private SyncableSides selectSlotMap(AutoSlots slot) { switch (slot) { case input: return glassSides; case output: return xpBottleSides; case xp: return xpSides; default: throw MiscUtils.unhandledEnum(slot); } } @Override public IValueProvider<Set<ForgeDirection>> createAllowedDirectionsProvider(AutoSlots slot) { return selectSlotMap(slot); } @Override public IWriteableBitMap<ForgeDirection> createAllowedDirectionsReceiver(AutoSlots slot) { SyncableSides dirs = selectSlotMap(slot); return BitMapUtils.createRpcAdapter(createRpcProxy(dirs, IRpcDirectionBitMap.class)); } @Override public IValueProvider<Boolean> createAutoFlagProvider(AutoSlots slot) { return BitMapUtils.singleBitProvider(automaticSlots, slot.ordinal()); } @Override public IValueReceiver<Boolean> createAutoSlotReceiver(AutoSlots slot) { IRpcIntBitMap bits = createRpcProxy(automaticSlots, IRpcIntBitMap.class); return BitMapUtils.singleBitReceiver(bits, slot.ordinal()); } @Override public void validate() { super.validate(); this.needsTankUpdate = true; } @Override public void onNeighbourChanged(Block block) { this.needsTankUpdate = true; } }