package openblocks.common.tileentity; import java.util.Random; import java.util.Set; import net.minecraft.block.Block; import net.minecraft.entity.player.EntityPlayer; 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.GuiAutoEnchantmentTable; import openblocks.common.LiquidXpUtils; import openblocks.common.container.ContainerAutoEnchantmentTable; import openblocks.common.tileentity.TileEntityAutoEnchantmentTable.AutoSlots; import openblocks.rpc.ILevelChanger; import openmods.api.IHasGui; import openmods.api.IInventoryCallback; 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.SyncableInt; 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.bitmap.BitMapUtils; import openmods.utils.bitmap.IRpcDirectionBitMap; import openmods.utils.bitmap.IRpcIntBitMap; import openmods.utils.bitmap.IWriteableBitMap; public class TileEntityAutoEnchantmentTable extends SyncedTileEntity implements IInventoryProvider, IHasGui, IConfigurableGuiSlots<AutoSlots>, ILevelChanger, IInventoryCallback, INeighbourAwareTile { public static final int TANK_CAPACITY = LiquidXpUtils.getLiquidForLevel(30); public static enum Slots { input, output } public static enum AutoSlots { input, output, xp } private SyncableTank tank; private SyncableSides inputSides; private SyncableSides outputSides; private SyncableSides xpSides; private SyncableInt targetLevel; private SyncableFlags automaticSlots; private SyncableInt maxLevel; private boolean needsTankUpdate; private final GenericInventory inventory = new TileEntityInventory(this, "autoenchant", true, 2) { @Override public boolean isItemValidForSlot(int i, ItemStack itemstack) { return (i == Slots.input.ordinal()) && !itemstack.isItemEnchanted(); } }; @IncludeInterface(ISidedInventory.class) private final SidedInventoryAdapter slotSides = new SidedInventoryAdapter(inventory); @IncludeInterface private final IFluidHandler tankWrapper = new SidedFluidHandler.Drain(xpSides, tank); /** * grotesque book turning stuff taken from the main enchantment table */ public int tickCount; public float pageFlip; public float pageFlipPrev; public float field_70373_d; public float field_70374_e; public float bookSpread; public float bookSpreadPrev; public float bookRotation2; public float bookRotationPrev; public float bookRotation; private static Random rand = new Random(); public TileEntityAutoEnchantmentTable() { slotSides.registerSlot(Slots.input, inputSides, true, false); slotSides.registerSlot(Slots.output, outputSides, false, true); inventory.addCallback(this); } @Override protected void createSyncedFields() { tank = new SyncableTank(TANK_CAPACITY, OpenBlocks.Fluids.xpJuice); inputSides = new SyncableSides(); outputSides = new SyncableSides(); xpSides = new SyncableSides(); targetLevel = new SyncableInt(1); maxLevel = new SyncableInt(); automaticSlots = SyncableFlags.create(AutoSlots.values().length); } @Override public void updateEntity() { super.updateEntity(); handleBookRotation(); if (!worldObj.isRemote) { if (automaticSlots.get(AutoSlots.xp)) { if (needsTankUpdate) { tank.updateNeighbours(worldObj, getPosition()); needsTankUpdate = false; } tank.fillFromSides(80, worldObj, getPosition(), xpSides.getValue()); } if (shouldAutoOutput() && hasStack(Slots.output)) { 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 (shouldAutoInput() && !hasStack(Slots.input)) { ItemDistribution.moveItemsFromOneOfSides(this, inventory, 1, Slots.input.ordinal(), inputSides.getValue(), true); } if (hasStack(Slots.input) && inventory.isItemValidForSlot(Slots.input.ordinal(), getStack(Slots.input)) && !hasStack(Slots.output)) { int xpRequired = LiquidXpUtils.getLiquidForLevel(targetLevel.get()); if (xpRequired > 0 && tank.getFluidAmount() >= xpRequired) { float power = EnchantmentUtils.getPower(worldObj, xCoord, yCoord, zCoord); int enchantability = EnchantmentUtils.calcEnchantability(getStack(Slots.input), (int)power, true); if (enchantability >= targetLevel.get()) { ItemStack inputStack = getStack(Slots.input); if (inputStack == null) return; ItemStack resultingStack = inputStack.copy(); resultingStack.stackSize = 1; if (EnchantmentUtils.enchantItem(resultingStack, targetLevel.get(), worldObj.rand)) { tank.drain(xpRequired, true); inputStack.stackSize--; if (inputStack.stackSize < 1) { setStack(Slots.input, null); } setStack(Slots.output, resultingStack); sync(); } } } } if (tank.isDirty()) sync(); } } private void handleBookRotation() { this.bookSpreadPrev = this.bookSpread; this.bookRotationPrev = this.bookRotation2; EntityPlayer entityplayer = this.worldObj.getClosestPlayer(this.xCoord + 0.5F, this.yCoord + 0.5F, this.zCoord + 0.5F, 3.0D); if (entityplayer != null) { double d0 = entityplayer.posX - (this.xCoord + 0.5F); double d1 = entityplayer.posZ - (this.zCoord + 0.5F); this.bookRotation = (float)Math.atan2(d1, d0); this.bookSpread += 0.1F; if (this.bookSpread < 0.5F || rand.nextInt(40) == 0) { float f = this.field_70373_d; do { this.field_70373_d += rand.nextInt(4) - rand.nextInt(4); } while (f == this.field_70373_d); } } else { this.bookRotation += 0.02F; this.bookSpread -= 0.1F; } while (this.bookRotation2 >= (float)Math.PI) { this.bookRotation2 -= ((float)Math.PI * 2F); } while (this.bookRotation2 < -(float)Math.PI) { this.bookRotation2 += ((float)Math.PI * 2F); } while (this.bookRotation >= (float)Math.PI) { this.bookRotation -= ((float)Math.PI * 2F); } while (this.bookRotation < -(float)Math.PI) { this.bookRotation += ((float)Math.PI * 2F); } float f1 = this.bookRotation - this.bookRotation2; while (f1 >= (float)Math.PI) f1 -= ((float)Math.PI * 2F); while (f1 < -(float)Math.PI) { f1 += ((float)Math.PI * 2F); } this.bookRotation2 += f1 * 0.4F; if (this.bookSpread < 0.0F) { this.bookSpread = 0.0F; } if (this.bookSpread > 1.0F) { this.bookSpread = 1.0F; } ++this.tickCount; this.pageFlipPrev = this.pageFlip; float f2 = (this.field_70373_d - this.pageFlip) * 0.4F; float f3 = 0.2F; if (f2 < -f3) { f2 = -f3; } if (f2 > f3) { f2 = f3; } this.field_70374_e += (f2 - this.field_70374_e) * 0.9F; this.pageFlip += this.field_70374_e; } private boolean shouldAutoInput() { return automaticSlots.get(AutoSlots.input); } private boolean shouldAutoOutput() { return automaticSlots.get(AutoSlots.output); } private boolean hasStack(Enum<?> slot) { return getStack(slot) != null; } public void setStack(Enum<?> slot, ItemStack stack) { inventory.setInventorySlotContents(slot.ordinal(), stack); } private ItemStack getStack(Enum<?> slot) { return inventory.getStackInSlot(slot); } @Override public Object getServerGui(EntityPlayer player) { return new ContainerAutoEnchantmentTable(player.inventory, this); } @Override public Object getClientGui(EntityPlayer player) { return new GuiAutoEnchantmentTable(new ContainerAutoEnchantmentTable(player.inventory, this)); } @Override public boolean canOpenGui(EntityPlayer player) { return true; } @IncludeOverride public boolean canDrain(ForgeDirection from, Fluid fluid) { return false; } public IValueProvider<FluidStack> getFluidProvider() { return tank; } @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 input: return inputSides; case output: return outputSides; 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 changeLevel(int level) { targetLevel.set(level); sync(); } public IValueProvider<Integer> getLevelProvider() { return targetLevel; } public IValueProvider<Integer> getMaxLevelProvider() { return maxLevel; } @Override public void onInventoryChanged(IInventory inventory, int slotNumber) { if (worldObj.isRemote) return; float power = EnchantmentUtils.getPower(worldObj, xCoord, yCoord, zCoord); ItemStack stack = getStack(Slots.input); int enchantability = stack == null? 30 : stack.getItem().getItemEnchantability(); int maxLevel = EnchantmentUtils.calcEnchantability(enchantability, (int)power, true); this.maxLevel.set(maxLevel); sync(); } @Override public void validate() { super.validate(); this.needsTankUpdate = true; } @Override public void onNeighbourChanged(Block block) { this.needsTankUpdate = true; } }