/** * This class was created by <Vazkii>. It's distributed as * part of the Botania Mod. Get the Source Code in github: * https://github.com/Vazkii/Botania * * Botania is Open Source and distributed under the * Botania License: http://botaniamod.net/license.php * * File Created @ [Jan 25, 2014, 9:40:57 PM (GMT)] */ package vazkii.botania.common.block.tile.mana; import java.util.List; import java.util.UUID; import javax.annotation.Nonnull; import org.lwjgl.opengl.GL11; import com.google.common.base.Predicates; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.ScaledResolution; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.RenderHelper; import net.minecraft.client.resources.I18n; import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.play.server.SPacketUpdateTileEntity; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.ITickable; import net.minecraft.util.SoundCategory; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import vazkii.botania.api.internal.IManaBurst; import vazkii.botania.api.internal.VanillaPacketDispatcher; import vazkii.botania.api.mana.BurstProperties; import vazkii.botania.api.mana.IDirectioned; import vazkii.botania.api.mana.IKeyLocked; import vazkii.botania.api.mana.ILens; import vazkii.botania.api.mana.ILensControl; import vazkii.botania.api.mana.ILensEffect; import vazkii.botania.api.mana.IManaCollector; import vazkii.botania.api.mana.IManaPool; import vazkii.botania.api.mana.IManaReceiver; import vazkii.botania.api.mana.IManaSpreader; import vazkii.botania.api.mana.IThrottledPacket; import vazkii.botania.api.mana.ManaNetworkEvent; import vazkii.botania.api.sound.BotaniaSoundEvents; import vazkii.botania.api.state.BotaniaStateProps; import vazkii.botania.api.state.enums.SpreaderVariant; import vazkii.botania.api.wand.IWandBindable; import vazkii.botania.client.core.handler.HUDHandler; import vazkii.botania.client.lib.LibResources; import vazkii.botania.common.block.ModBlocks; import vazkii.botania.common.block.tile.TileSimpleInventory; import vazkii.botania.common.core.handler.ConfigHandler; import vazkii.botania.common.core.handler.ManaNetworkHandler; import vazkii.botania.common.core.helper.Vector3; import vazkii.botania.common.entity.EntityManaBurst; import vazkii.botania.common.entity.EntityManaBurst.PositionProperties; public class TileSpreader extends TileSimpleInventory implements IManaCollector, IWandBindable, IKeyLocked, IThrottledPacket, IManaSpreader, IDirectioned, ITickable { private static final int MAX_MANA = 1000; private static final int ULTRA_MAX_MANA = 6400; private static final int TICKS_ALLOWED_WITHOUT_PINGBACK = 20; private static final double PINGBACK_EXPIRED_SEARCH_DISTANCE = 0.5; private static final String TAG_HAS_IDENTITY = "hasIdentity"; private static final String TAG_UUID_MOST = "uuidMost"; private static final String TAG_UUID_LEAST = "uuidLeast"; private static final String TAG_MANA = "mana"; private static final String TAG_KNOWN_MANA = "knownMana"; private static final String TAG_REQUEST_UPDATE = "requestUpdate"; private static final String TAG_ROTATION_X = "rotationX"; private static final String TAG_ROTATION_Y = "rotationY"; private static final String TAG_PADDING_COLOR = "paddingColor"; private static final String TAG_CAN_SHOOT_BURST = "canShootBurst"; private static final String TAG_PINGBACK_TICKS = "pingbackTicks"; private static final String TAG_LAST_PINGBACK_X = "lastPingbackX"; private static final String TAG_LAST_PINGBACK_Y = "lastPingbackY"; private static final String TAG_LAST_PINGBACK_Z = "lastPingbackZ"; private static final String TAG_FORCE_CLIENT_BINDING_X = "forceClientBindingX"; private static final String TAG_FORCE_CLIENT_BINDING_Y = "forceClientBindingY"; private static final String TAG_FORCE_CLIENT_BINDING_Z = "forceClientBindingZ"; // Map Maker Tags private static final String TAG_INPUT_KEY = "inputKey"; private static final String TAG_OUTPUT_KEY = "outputKey"; private static final String TAG_MAPMAKER_OVERRIDE = "mapmakerOverrideEnabled"; private static final String TAG_FORCED_COLOR = "mmForcedColor"; private static final String TAG_FORCED_MANA_PAYLOAD = "mmForcedManaPayload"; private static final String TAG_FORCED_TICKS_BEFORE_MANA_LOSS = "mmForcedTicksBeforeManaLoss"; private static final String TAG_FORCED_MANA_LOSS_PER_TICK = "mmForcedManaLossPerTick"; private static final String TAG_FORCED_GRAVITY = "mmForcedGravity"; private static final String TAG_FORCED_VELOCITY_MULTIPLIER = "mmForcedVelocityMultiplier"; boolean mapmakerOverride = false; int mmForcedColor = 0x20FF20; int mmForcedManaPayload = 160; int mmForcedTicksBeforeManaLoss = 60; float mmForcedManaLossPerTick = 4F; float mmForcedGravity = 0F; float mmForcedVelocityMultiplier = 1F; String inputKey = ""; final String outputKey = ""; // End Map Maker Tags public static final boolean staticRedstone = false; public static final boolean staticDreamwood = false; public static final boolean staticUltra = false; UUID identity; int mana; int knownMana = -1; public float rotationX, rotationY; public int paddingColor = -1; boolean requestsClientUpdate = false; boolean hasReceivedInitialPacket = false; IManaReceiver receiver = null; IManaReceiver receiverLastTick = null; boolean redstoneLastTick = true; public boolean canShootBurst = true; public int lastBurstDeathTick = -1; public int burstParticleTick = 0; public int pingbackTicks = 0; public double lastPingbackX = 0; public double lastPingbackY = -1; public double lastPingbackZ = 0; List<PositionProperties> lastTentativeBurst; boolean invalidTentativeBurst = false; @Override public boolean shouldRefresh(World world, BlockPos pos, @Nonnull IBlockState oldState, @Nonnull IBlockState newState) { if(oldState.getBlock() != newState.getBlock()) return true; if(oldState.getBlock() != ModBlocks.spreader || newState.getBlock() != ModBlocks.spreader) return true; return oldState.getValue(BotaniaStateProps.SPREADER_VARIANT) != newState.getValue(BotaniaStateProps.SPREADER_VARIANT); } @Override public boolean isFull() { return mana >= getMaxMana(); } @Override public void recieveMana(int mana) { this.mana = Math.min(this.mana + mana, getMaxMana()); } @Override public void invalidate() { super.invalidate(); ManaNetworkEvent.removeCollector(this); } @Override public void onChunkUnload() { super.onChunkUnload(); invalidate(); } @Override public void update() { boolean inNetwork = ManaNetworkHandler.instance.isCollectorIn(this); boolean wasInNetwork = inNetwork; if(!inNetwork && !isInvalid()) { ManaNetworkEvent.addCollector(this); } boolean redstone = false; for(EnumFacing dir : EnumFacing.VALUES) { TileEntity tileAt = world.getTileEntity(pos.offset(dir)); if(world.isBlockLoaded(pos.offset(dir), false) && tileAt instanceof IManaPool) { IManaPool pool = (IManaPool) tileAt; if(wasInNetwork && (pool != receiver || isRedstone())) { if(pool instanceof IKeyLocked && !((IKeyLocked) pool).getOutputKey().equals(getInputKey())) continue; int manaInPool = pool.getCurrentMana(); if(manaInPool > 0 && !isFull()) { int manaMissing = getMaxMana() - mana; int manaToRemove = Math.min(manaInPool, manaMissing); pool.recieveMana(-manaToRemove); recieveMana(manaToRemove); } } } int redstoneSide = world.getRedstonePower(pos.offset(dir), dir); if(redstoneSide > 0) redstone = true; } if(needsNewBurstSimulation()) checkForReceiver(); if(!canShootBurst) if(pingbackTicks <= 0) { double x = lastPingbackX; double y = lastPingbackY; double z = lastPingbackZ; AxisAlignedBB aabb = new AxisAlignedBB(x, y, z, x, y, z).expand(PINGBACK_EXPIRED_SEARCH_DISTANCE, PINGBACK_EXPIRED_SEARCH_DISTANCE, PINGBACK_EXPIRED_SEARCH_DISTANCE); List bursts = world.getEntitiesWithinAABB(Entity.class, aabb, Predicates.instanceOf(IManaBurst.class)); IManaBurst found = null; UUID identity = getIdentifier(); for(IManaBurst burst : (List<IManaBurst>) bursts) if(burst != null && identity.equals(burst.getShooterUUID())) { found = burst; break; } if(found != null) found.ping(); else setCanShoot(true); } else pingbackTicks--; boolean shouldShoot = !redstone; boolean isredstone = isRedstone(); if(isredstone) shouldShoot = redstone && !redstoneLastTick; if(shouldShoot && receiver != null && receiver instanceof IKeyLocked) shouldShoot = ((IKeyLocked) receiver).getInputKey().equals(getOutputKey()); ItemStack lens = itemHandler.getStackInSlot(0); ILensControl control = getLensController(lens); if(control != null) { if(isredstone) { if(shouldShoot) control.onControlledSpreaderPulse(lens, this, redstone); } else control.onControlledSpreaderTick(lens, this, redstone); shouldShoot &= control.allowBurstShooting(lens, this, redstone); } if(shouldShoot) tryShootBurst(); if(receiverLastTick != receiver && !world.isRemote) { requestsClientUpdate = true; VanillaPacketDispatcher.dispatchTEToNearbyPlayers(world, pos); } redstoneLastTick = redstone; receiverLastTick = receiver; } @Override public void writePacketNBT(NBTTagCompound cmp) { super.writePacketNBT(cmp); UUID identity = getIdentifier(); cmp.setBoolean(TAG_HAS_IDENTITY, true); cmp.setLong(TAG_UUID_MOST, identity.getMostSignificantBits()); cmp.setLong(TAG_UUID_LEAST, identity.getLeastSignificantBits()); cmp.setInteger(TAG_MANA, mana); cmp.setFloat(TAG_ROTATION_X, rotationX); cmp.setFloat(TAG_ROTATION_Y, rotationY); cmp.setBoolean(TAG_REQUEST_UPDATE, requestsClientUpdate); cmp.setInteger(TAG_PADDING_COLOR, paddingColor); cmp.setBoolean(TAG_CAN_SHOOT_BURST, canShootBurst); cmp.setInteger(TAG_PINGBACK_TICKS, pingbackTicks); cmp.setDouble(TAG_LAST_PINGBACK_X, lastPingbackX); cmp.setDouble(TAG_LAST_PINGBACK_Y, lastPingbackY); cmp.setDouble(TAG_LAST_PINGBACK_Z, lastPingbackZ); cmp.setString(TAG_INPUT_KEY, inputKey); cmp.setString(TAG_OUTPUT_KEY, outputKey); cmp.setInteger(TAG_FORCE_CLIENT_BINDING_X, receiver == null ? 0 : ((TileEntity) receiver).getPos().getX()); cmp.setInteger(TAG_FORCE_CLIENT_BINDING_Y, receiver == null ? -1 : ((TileEntity) receiver).getPos().getY()); cmp.setInteger(TAG_FORCE_CLIENT_BINDING_Z, receiver == null ? 0 : ((TileEntity) receiver).getPos().getZ()); cmp.setBoolean(TAG_MAPMAKER_OVERRIDE, mapmakerOverride); cmp.setInteger(TAG_FORCED_COLOR, mmForcedColor); cmp.setInteger(TAG_FORCED_MANA_PAYLOAD, mmForcedManaPayload); cmp.setInteger(TAG_FORCED_TICKS_BEFORE_MANA_LOSS, mmForcedTicksBeforeManaLoss); cmp.setFloat(TAG_FORCED_MANA_LOSS_PER_TICK, mmForcedManaLossPerTick); cmp.setFloat(TAG_FORCED_GRAVITY, mmForcedGravity); cmp.setFloat(TAG_FORCED_VELOCITY_MULTIPLIER, mmForcedVelocityMultiplier); requestsClientUpdate = false; } @Override public void readPacketNBT(NBTTagCompound cmp) { super.readPacketNBT(cmp); if(cmp.getBoolean(TAG_HAS_IDENTITY)) { long most = cmp.getLong(TAG_UUID_MOST); long least = cmp.getLong(TAG_UUID_LEAST); UUID identity = getIdentifierUnsafe(); if(identity == null || most != identity.getMostSignificantBits() || least != identity.getLeastSignificantBits()) this.identity = new UUID(most, least); } else getIdentifier(); mana = cmp.getInteger(TAG_MANA); rotationX = cmp.getFloat(TAG_ROTATION_X); rotationY = cmp.getFloat(TAG_ROTATION_Y); requestsClientUpdate = cmp.getBoolean(TAG_REQUEST_UPDATE); if(cmp.hasKey(TAG_INPUT_KEY)) inputKey = cmp.getString(TAG_INPUT_KEY); if(cmp.hasKey(TAG_OUTPUT_KEY)) inputKey = cmp.getString(TAG_OUTPUT_KEY); mapmakerOverride = cmp.getBoolean(TAG_MAPMAKER_OVERRIDE); mmForcedColor = cmp.getInteger(TAG_FORCED_COLOR); mmForcedManaPayload = cmp.getInteger(TAG_FORCED_MANA_PAYLOAD); mmForcedTicksBeforeManaLoss = cmp.getInteger(TAG_FORCED_TICKS_BEFORE_MANA_LOSS); mmForcedManaLossPerTick = cmp.getFloat(TAG_FORCED_MANA_LOSS_PER_TICK); mmForcedGravity = cmp.getFloat(TAG_FORCED_GRAVITY); mmForcedVelocityMultiplier = cmp.getFloat(TAG_FORCED_VELOCITY_MULTIPLIER); if(cmp.hasKey(TAG_KNOWN_MANA)) knownMana = cmp.getInteger(TAG_KNOWN_MANA); if(cmp.hasKey(TAG_PADDING_COLOR)) paddingColor = cmp.getInteger(TAG_PADDING_COLOR); if(cmp.hasKey(TAG_CAN_SHOOT_BURST)) canShootBurst = cmp.getBoolean(TAG_CAN_SHOOT_BURST); pingbackTicks = cmp.getInteger(TAG_PINGBACK_TICKS); lastPingbackX = cmp.getDouble(TAG_LAST_PINGBACK_X); lastPingbackY = cmp.getDouble(TAG_LAST_PINGBACK_Y); lastPingbackZ = cmp.getDouble(TAG_LAST_PINGBACK_Z); if(requestsClientUpdate && world != null) { int x = cmp.getInteger(TAG_FORCE_CLIENT_BINDING_X); int y = cmp.getInteger(TAG_FORCE_CLIENT_BINDING_Y); int z = cmp.getInteger(TAG_FORCE_CLIENT_BINDING_Z); if(y != -1) { TileEntity tile = world.getTileEntity(new BlockPos(x, y, z)); if(tile instanceof IManaReceiver) receiver = (IManaReceiver) tile; else receiver = null; } else receiver = null; } if(world != null && world.isRemote) hasReceivedInitialPacket = true; } @Override public boolean canRecieveManaFromBursts() { return true; } @Override public int getCurrentMana() { return mana; } public void onWanded(EntityPlayer player, ItemStack wand) { if(player == null) return; if(!player.isSneaking()) { if(!world.isRemote) { NBTTagCompound nbttagcompound = new NBTTagCompound(); writePacketNBT(nbttagcompound); nbttagcompound.setInteger(TAG_KNOWN_MANA, mana); if(player instanceof EntityPlayerMP) ((EntityPlayerMP) player).connection.sendPacket(new SPacketUpdateTileEntity(pos, -999, nbttagcompound)); } world.playSound(null, player.posX, player.posY, player.posZ, BotaniaSoundEvents.ding, SoundCategory.PLAYERS, 0.1F, 1); } else { RayTraceResult pos = raytraceFromEntity(world, player, true, 5); if(pos != null && pos.hitVec != null && !world.isRemote) { double x = pos.hitVec.xCoord - getPos().getX() - 0.5; double y = pos.hitVec.yCoord - getPos().getY() - 0.5; double z = pos.hitVec.zCoord - getPos().getZ() - 0.5; if(pos.sideHit != EnumFacing.DOWN && pos.sideHit != EnumFacing.UP) { Vector3 clickVector = new Vector3(x, 0, z); Vector3 relative = new Vector3(-0.5, 0, 0); double angle = Math.acos(clickVector.dotProduct(relative) / (relative.mag() * clickVector.mag())) * 180D / Math.PI; rotationX = (float) angle + 180F; if(clickVector.z < 0) rotationX = 360 - rotationX; } double angle = y * 180; rotationY = -(float) angle; checkForReceiver(); requestsClientUpdate = true; VanillaPacketDispatcher.dispatchTEToNearbyPlayers(world, getPos()); } } } private boolean needsNewBurstSimulation() { if(world.isRemote && !hasReceivedInitialPacket) return false; if(lastTentativeBurst == null) return true; for(PositionProperties props : lastTentativeBurst) if(!props.contentsEqual(world)) { invalidTentativeBurst = props.invalid; return !invalidTentativeBurst; } return false; } public void tryShootBurst() { if((receiver != null || isRedstone()) && !invalidTentativeBurst) { if(canShootBurst && (isRedstone() || receiver.canRecieveManaFromBursts() && !receiver.isFull())) { EntityManaBurst burst = getBurst(false); if(burst != null) { if(!world.isRemote) { mana -= burst.getStartingMana(); burst.setShooterUUID(getIdentifier()); world.spawnEntity(burst); burst.ping(); if(!ConfigHandler.silentSpreaders) world.playSound(null, pos, BotaniaSoundEvents.spreaderFire, SoundCategory.BLOCKS, 0.05F * (paddingColor != -1 ? 0.2F : 1F), 0.7F + 0.3F * (float) Math.random()); } } } } } public boolean isRedstone() { updateContainingBlockInfo(); return world == null ? staticRedstone : getBlockMetadata() == SpreaderVariant.REDSTONE.ordinal(); } public boolean isDreamwood() { updateContainingBlockInfo(); int variant = getBlockMetadata(); return world == null ? staticDreamwood : variant == SpreaderVariant.ELVEN.ordinal() || variant == SpreaderVariant.GAIA.ordinal(); } public boolean isULTRA_SPREADER() { updateContainingBlockInfo(); return world == null ? staticUltra : getBlockMetadata() == SpreaderVariant.GAIA.ordinal(); } public void checkForReceiver() { ItemStack stack = itemHandler.getStackInSlot(0); ILensControl control = getLensController(stack); if(control != null && !control.allowBurstShooting(stack, this, false)) return; EntityManaBurst fakeBurst = getBurst(true); fakeBurst.setScanBeam(); TileEntity receiver = fakeBurst.getCollidedTile(true); if(receiver != null && receiver instanceof IManaReceiver && receiver.hasWorld() && receiver.getWorld().isBlockLoaded(receiver.getPos(), !receiver.getWorld().isRemote)) this.receiver = (IManaReceiver) receiver; else this.receiver = null; lastTentativeBurst = fakeBurst.propsList; } @Override public IManaBurst runBurstSimulation() { EntityManaBurst fakeBurst = getBurst(true); fakeBurst.setScanBeam(); fakeBurst.getCollidedTile(true); return fakeBurst; } public EntityManaBurst getBurst(boolean fake) { EntityManaBurst burst = new EntityManaBurst(this, fake); boolean dreamwood = isDreamwood(); boolean ultra = isULTRA_SPREADER(); int maxMana = ultra ? 640 : dreamwood ? 240 : 160; int color = isRedstone() ? 0xFF2020 : dreamwood ? 0xFF45C4 : 0x20FF20; int ticksBeforeManaLoss = ultra ? 120 : dreamwood ? 80 : 60; float manaLossPerTick = ultra ? 20F : 4F; float motionModifier = ultra ? 2F : dreamwood ? 1.25F : 1F; float gravity = 0F; BurstProperties props = new BurstProperties(maxMana, ticksBeforeManaLoss, manaLossPerTick, gravity, motionModifier, color); ItemStack lens = itemHandler.getStackInSlot(0); if(!lens.isEmpty() && lens.getItem() instanceof ILensEffect) ((ILensEffect) lens.getItem()).apply(lens, props); burst.setSourceLens(lens); if(getCurrentMana() >= props.maxMana || fake) { if(mapmakerOverride) { burst.setColor(mmForcedColor); burst.setMana(mmForcedManaPayload); burst.setStartingMana(mmForcedManaPayload); burst.setMinManaLoss(mmForcedTicksBeforeManaLoss); burst.setManaLossPerTick(mmForcedManaLossPerTick); burst.setGravity(mmForcedGravity); burst.setMotion(burst.motionX * mmForcedVelocityMultiplier, burst.motionY * mmForcedVelocityMultiplier, burst.motionZ * mmForcedVelocityMultiplier); } else { burst.setColor(props.color); burst.setMana(props.maxMana); burst.setStartingMana(props.maxMana); burst.setMinManaLoss(props.ticksBeforeManaLoss); burst.setManaLossPerTick(props.manaLossPerTick); burst.setGravity(props.gravity); burst.setMotion(burst.motionX * props.motionModifier, burst.motionY * props.motionModifier, burst.motionZ * props.motionModifier); } return burst; } return null; } public ILensControl getLensController(ItemStack stack) { if(!stack.isEmpty() && stack.getItem() instanceof ILensControl) { ILensControl control = (ILensControl) stack.getItem(); if(control.isControlLens(stack)) return control; } return null; } public static RayTraceResult raytraceFromEntity(World world, Entity player, boolean par3, double range) { float f = 1.0F; float f1 = player.prevRotationPitch + (player.rotationPitch - player.prevRotationPitch) * f; float f2 = player.prevRotationYaw + (player.rotationYaw - player.prevRotationYaw) * f; double d0 = player.prevPosX + (player.posX - player.prevPosX) * f; double d1 = player.prevPosY + (player.posY - player.prevPosY) * f; if (!world.isRemote && player instanceof EntityPlayer) d1 += 1.62D; double d2 = player.prevPosZ + (player.posZ - player.prevPosZ) * f; Vec3d vec3 = new Vec3d(d0, d1, d2); float f3 = MathHelper.cos(-f2 * 0.017453292F - (float) Math.PI); float f4 = MathHelper.sin(-f2 * 0.017453292F - (float) Math.PI); float f5 = -MathHelper.cos(-f1 * 0.017453292F); float f6 = MathHelper.sin(-f1 * 0.017453292F); float f7 = f4 * f5; float f8 = f3 * f5; double d3 = range; if (player instanceof EntityPlayerMP) d3 = ((EntityPlayerMP) player).interactionManager.getBlockReachDistance(); Vec3d vec31 = vec3.addVector(f7 * d3, f6 * d3, f8 * d3); return world.rayTraceBlocks(vec3, vec31, par3, !par3, par3); } @SideOnly(Side.CLIENT) public void renderHUD(Minecraft mc, ScaledResolution res) { String name = I18n.format(new ItemStack(ModBlocks.spreader, 1, world.getBlockState(getPos()).getValue(BotaniaStateProps.SPREADER_VARIANT).ordinal()).getUnlocalizedName().replaceAll("tile.", "tile." + LibResources.PREFIX_MOD) + ".name"); int color = isRedstone() ? 0xFF0000 : isDreamwood() ? 0xFF00AE : 0x00FF00; HUDHandler.drawSimpleManaHUD(color, knownMana, getMaxMana(), name, res); ItemStack lens = itemHandler.getStackInSlot(0); if(!lens.isEmpty()) { GlStateManager.enableBlend(); GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); String lensName = lens.getDisplayName(); int width = 16 + mc.fontRendererObj.getStringWidth(lensName) / 2; int x = res.getScaledWidth() / 2 - width; int y = res.getScaledHeight() / 2 + 50; mc.fontRendererObj.drawStringWithShadow(lensName, x + 20, y + 5, color); RenderHelper.enableGUIStandardItemLighting(); mc.getRenderItem().renderItemAndEffectIntoGUI(lens, x, y); RenderHelper.disableStandardItemLighting(); GlStateManager.disableLighting(); GlStateManager.disableBlend(); } if(receiver != null) { TileEntity receiverTile = (TileEntity) receiver; ItemStack recieverStack = new ItemStack(world.getBlockState(receiverTile.getPos()).getBlock(), 1, receiverTile.getBlockMetadata()); GlStateManager.enableBlend(); GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); if(!recieverStack.isEmpty()) { String stackName = recieverStack.getDisplayName(); int width = 16 + mc.fontRendererObj.getStringWidth(stackName) / 2; int x = res.getScaledWidth() / 2 - width; int y = res.getScaledHeight() / 2 + 30; mc.fontRendererObj.drawStringWithShadow(stackName, x + 20, y + 5, color); RenderHelper.enableGUIStandardItemLighting(); mc.getRenderItem().renderItemAndEffectIntoGUI(recieverStack, x, y); RenderHelper.disableStandardItemLighting(); } GlStateManager.disableLighting(); GlStateManager.disableBlend(); } GlStateManager.color(1F, 1F, 1F, 1F); } @Override public void onClientDisplayTick() { if(world != null) { EntityManaBurst burst = getBurst(true); burst.getCollidedTile(false); } } @Override public float getManaYieldMultiplier(IManaBurst burst) { return 1F; } @Override public int getSizeInventory() { return 1; } @Override protected SimpleItemStackHandler createItemHandler() { return new SimpleItemStackHandler(this, true) { @Override protected int getStackLimit(int slot, @Nonnull ItemStack stack) { return 1; } @Nonnull @Override public ItemStack insertItem(int slot, @Nonnull ItemStack stack, boolean simulate) { if(!stack.isEmpty() && stack.getItem() instanceof ILens) return super.insertItem(slot, stack, simulate); else return stack; } }; } @Override public void markDirty() { checkForReceiver(); VanillaPacketDispatcher.dispatchTEToNearbyPlayers(world, pos); } @Override public BlockPos getBinding() { if(receiver == null) return null; TileEntity tile = (TileEntity) receiver; return tile.getPos(); } @Override public int getMaxMana() { return isULTRA_SPREADER() ? ULTRA_MAX_MANA : MAX_MANA; } @Override public String getInputKey() { return inputKey; } @Override public String getOutputKey() { return outputKey; } @Override public boolean canSelect(EntityPlayer player, ItemStack wand, BlockPos pos, EnumFacing side) { return true; } @Override public boolean bindTo(EntityPlayer player, ItemStack wand, BlockPos pos, EnumFacing side) { Vector3 thisVec = Vector3.fromTileEntityCenter(this); Vector3 blockVec = new Vector3(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5); AxisAlignedBB axis = player.world.getBlockState(pos).getCollisionBoundingBox(player.world, pos); if(axis != null) axis = axis.offset(pos); else axis = new AxisAlignedBB(pos, pos.add(1, 1, 1)); if(!blockVec.isInside(axis)) blockVec = new Vector3(axis.minX + (axis.maxX - axis.minX) / 2, axis.minY + (axis.maxY - axis.minY) / 2, axis.minZ + (axis.maxZ - axis.minZ) / 2); Vector3 diffVec = blockVec.subtract(thisVec); Vector3 diffVec2D = new Vector3(diffVec.x, diffVec.z, 0); Vector3 rotVec = new Vector3(0, 1, 0); double angle = rotVec.angle(diffVec2D) / Math.PI * 180.0; if(blockVec.x < thisVec.x) angle = -angle; rotationX = (float) angle + 90; rotVec = new Vector3(diffVec.x, 0, diffVec.z); angle = diffVec.angle(rotVec) * 180F / Math.PI; if(blockVec.y < thisVec.y) angle = -angle; rotationY = (float) angle; checkForReceiver(); return true; } @Override public void markDispatchable() {} @Override public float getRotationX() { return rotationX; } @Override public float getRotationY() { return rotationY; } @Override public void setRotationX(float rot) { rotationX = rot; } @Override public void setRotationY(float rot) { rotationY = rot; } @Override public void commitRedirection() { checkForReceiver(); } @Override public void setCanShoot(boolean canShoot) { canShootBurst = canShoot; } @Override public int getBurstParticleTick() { return burstParticleTick; } @Override public void setBurstParticleTick(int i) { burstParticleTick = i; } @Override public int getLastBurstDeathTick() { return lastBurstDeathTick; } @Override public void setLastBurstDeathTick(int i) { lastBurstDeathTick = i; } @Override public void pingback(IManaBurst burst, UUID expectedIdentity) { if(getIdentifier().equals(expectedIdentity)) { pingbackTicks = TICKS_ALLOWED_WITHOUT_PINGBACK; Entity e = (Entity) burst; lastPingbackX = e.posX; lastPingbackY = e.posY; lastPingbackZ = e.posZ; setCanShoot(false); } } @Override public UUID getIdentifier() { if(identity == null) identity = UUID.randomUUID(); return identity; } private UUID getIdentifierUnsafe() { return identity; } }