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.GuiAutoAnvil; import openblocks.common.LiquidXpUtils; import openblocks.common.container.ContainerAutoAnvil; import openblocks.common.tileentity.TileEntityAutoAnvil.AutoSlots; import openmods.api.IHasGui; import openmods.api.INeighbourAwareTile; import openmods.api.IValueProvider; import openmods.api.IValueReceiver; 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.SyncableSides; import openmods.sync.SyncableTank; import openmods.tileentity.SyncedTileEntity; import openmods.utils.EnchantmentUtils; import openmods.utils.MiscUtils; import openmods.utils.SidedInventoryAdapter; import openmods.utils.VanillaAnvilLogic; import openmods.utils.bitmap.BitMapUtils; import openmods.utils.bitmap.IRpcDirectionBitMap; import openmods.utils.bitmap.IRpcIntBitMap; import openmods.utils.bitmap.IWriteableBitMap; public class TileEntityAutoAnvil extends SyncedTileEntity implements IHasGui, IInventoryProvider, IConfigurableGuiSlots<AutoSlots>, INeighbourAwareTile { protected static final int TOTAL_COOLDOWN = 40; public static final int TANK_CAPACITY = LiquidXpUtils.getLiquidForLevel(45); private int cooldown = 0; private boolean needsTankUpdate; /** * The 3 slots in the inventory */ public enum Slots { tool, modifier, output } /** * The keys of the things that can be auto injected/extracted */ public enum AutoSlots { tool, modifier, output, xp } /** * The shared/syncable objects */ private SyncableSides toolSides; private SyncableSides modifierSides; private SyncableSides outputSides; private SyncableSides xpSides; private SyncableTank tank; private SyncableFlags automaticSlots; private final GenericInventory inventory = registerInventoryCallback(new TileEntityInventory(this, "autoanvil", true, 3) { @Override public boolean isItemValidForSlot(int i, ItemStack itemstack) { if (i == 0 && (!itemstack.getItem().isItemTool(itemstack) && itemstack.getItem() != Items.enchanted_book)) { return false; } if (i == 2) { return false; } return super.isItemValidForSlot(i, itemstack); } }); @IncludeInterface(ISidedInventory.class) private final SidedInventoryAdapter slotSides = new SidedInventoryAdapter(inventory); @IncludeInterface private final IFluidHandler tankWrapper = new SidedFluidHandler.Drain(xpSides, tank); public TileEntityAutoAnvil() { slotSides.registerSlot(Slots.tool, toolSides, true, false); slotSides.registerSlot(Slots.modifier, modifierSides, true, false); slotSides.registerSlot(Slots.output, outputSides, false, true); } @Override protected void createSyncedFields() { toolSides = new SyncableSides(); modifierSides = new SyncableSides(); outputSides = new SyncableSides(); xpSides = new SyncableSides(); tank = new SyncableTank(TANK_CAPACITY, OpenBlocks.Fluids.xpJuice); automaticSlots = SyncableFlags.create(AutoSlots.values().length); } @Override public void updateEntity() { super.updateEntity(); if (!worldObj.isRemote) { // if we should auto-drink liquid, do it! if (automaticSlots.get(AutoSlots.xp)) { if (needsTankUpdate) { tank.updateNeighbours(worldObj, getPosition()); needsTankUpdate = false; } tank.fillFromSides(100, worldObj, getPosition(), xpSides.getValue()); } if (shouldAutoOutput() && hasOutput()) { ItemDistribution.moveItemsToOneOfSides(this, inventory, Slots.output.ordinal(), 1, outputSides.getValue(), true); } // if we should auto input the tool and we don't currently have one if (shouldAutoInputTool() && !hasTool()) { ItemDistribution.moveItemsFromOneOfSides(this, inventory, 1, Slots.tool.ordinal(), toolSides.getValue(), true); } // if we should auto input the modifier if (shouldAutoInputModifier()) { ItemDistribution.moveItemsFromOneOfSides(this, inventory, 1, Slots.modifier.ordinal(), modifierSides.getValue(), true); } if (cooldown-- < 0 && !hasOutput()) { repairItem(); cooldown = TOTAL_COOLDOWN; } if (tank.isDirty()) sync(); } } private void repairItem() { final VanillaAnvilLogic helper = new VanillaAnvilLogic(inventory.getStackInSlot(Slots.tool), inventory.getStackInSlot(Slots.modifier)); final ItemStack output = helper.getOutputStack(); if (output != null) { int levelCost = helper.getLevelCost(); int xpCost = EnchantmentUtils.getExperienceForLevel(levelCost); int liquidXpCost = LiquidXpUtils.xpToLiquidRatio(xpCost); FluidStack drained = tank.drain(liquidXpCost, false); if (drained != null && drained.amount == liquidXpCost) { tank.drain(liquidXpCost, true); removeModifiers(helper.getModifierCost()); inventory.setInventorySlotContents(Slots.tool.ordinal(), null); inventory.setInventorySlotContents(Slots.output.ordinal(), output); worldObj.playSoundEffect(xCoord + 0.5, yCoord + 0.5, zCoord + 0.5, "random.anvil_use", 0.3f, 1f); } } } private void removeModifiers(int modifierCost) { if (modifierCost > 0) { ItemStack modifierStack = inventory.getStackInSlot(Slots.modifier); if (modifierStack != null) { modifierStack.stackSize -= modifierCost; if (modifierStack.stackSize <= 0) inventory.setInventorySlotContents(Slots.modifier.ordinal(), null); } } else { inventory.setInventorySlotContents(Slots.modifier.ordinal(), null); } } @Override public boolean canOpenGui(EntityPlayer player) { return true; } @Override public Object getServerGui(EntityPlayer player) { return new ContainerAutoAnvil(player.inventory, this); } @Override public Object getClientGui(EntityPlayer player) { return new GuiAutoAnvil(new ContainerAutoAnvil(player.inventory, this)); } public IValueProvider<FluidStack> getFluidProvider() { return tank; } private boolean shouldAutoInputModifier() { return automaticSlots.get(AutoSlots.modifier); } public boolean shouldAutoOutput() { return automaticSlots.get(AutoSlots.output); } private boolean hasTool() { return inventory.getStackInSlot(0) != null; } private boolean shouldAutoInputTool() { return automaticSlots.get(AutoSlots.tool); } private boolean hasOutput() { return inventory.getStackInSlot(2) != null; } @IncludeOverride public boolean canDrain(ForgeDirection from, Fluid fluid) { return false; } @Override public IInventory getInventory() { return slotSides; } @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 modifier: return modifierSides; case output: return outputSides; case tool: return toolSides; 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; } }