/* * Copyright (C) 2017 - 2019 | Wurst-Imperium | All rights reserved. * * This source code is subject to the terms of the GNU General Public * License, version 3. If a copy of the GPL was not distributed with this * file, You can obtain one at: https://www.gnu.org/licenses/gpl-3.0.txt */ package net.wurstclient.forge.hacks; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import org.lwjgl.opengl.GL11; import com.mojang.realmsclient.gui.ChatFormatting; import net.minecraft.block.Block; import net.minecraft.block.BlockFalling; import net.minecraft.block.BlockLiquid; import net.minecraft.block.BlockTorch; import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher; import net.minecraft.client.settings.GameSettings; import net.minecraft.client.settings.KeyBinding; import net.minecraft.init.Blocks; import net.minecraft.item.ItemBlock; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3i; import net.minecraftforge.client.event.RenderWorldLastEvent; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.wurstclient.fmlevents.WUpdateEvent; import net.wurstclient.forge.Category; import net.wurstclient.forge.Hack; import net.wurstclient.forge.HackList; import net.wurstclient.forge.compatibility.WItem; import net.wurstclient.forge.compatibility.WMinecraft; import net.wurstclient.forge.settings.CheckboxSetting; import net.wurstclient.forge.settings.EnumSetting; import net.wurstclient.forge.settings.SliderSetting; import net.wurstclient.forge.utils.BlockUtils; import net.wurstclient.forge.utils.ChatUtils; import net.wurstclient.forge.utils.KeyBindingUtils; import net.wurstclient.forge.utils.PlayerControllerUtils; import net.wurstclient.forge.utils.RenderUtils; import net.wurstclient.forge.utils.RotationUtils; @Hack.DontSaveState public final class TunnellerHack extends Hack { private final EnumSetting<TunnelSize> size = new EnumSetting<>( "Tunnel size", TunnelSize.values(), TunnelSize.SIZE_3X3); private final SliderSetting limit = new SliderSetting("Limit", "Automatically stops once the tunnel\n" + "has reached the given length.\n\n" + "0 = no limit", 0, 0, 1000, 1, v -> v == 0 ? "disabled" : v == 1 ? "1 block" : (int)v + " blocks"); private final CheckboxSetting torches = new CheckboxSetting( "Place torches", "Places just enough torches\n" + "to prevent mobs from\n" + "spawning inside the tunnel.", false); private BlockPos start; private EnumFacing direction; private int length; private Task[] tasks; private int[] displayLists = new int[5]; private BlockPos currentBlock; private float progress; private float prevProgress; public TunnellerHack() { super("Tunneller", "Automatically digs a tunnel.\n\n" + ChatFormatting.RED + ChatFormatting.BOLD + "WARNING:" + ChatFormatting.RESET + " Although this bot will try to avoid\n" + "lava and other dangers, there is no guarantee\n" + "that it won't die. Only send it out with gear\n" + "that you don't mind losing."); setCategory(Category.BLOCKS); addSetting(size); addSetting(limit); addSetting(torches); } @Override public String getRenderName() { if(limit.getValueI() == 0) return getName(); else return getName() + " [" + length + "/" + limit.getValueI() + "]"; } @Override protected void onEnable() { MinecraftForge.EVENT_BUS.register(this); for(int i = 0; i < displayLists.length; i++) displayLists[i] = GL11.glGenLists(1); EntityPlayerSP player = WMinecraft.getPlayer(); start = new BlockPos(player); direction = player.getHorizontalFacing(); length = 0; tasks = new Task[]{new DodgeLiquidTask(), new FillInFloorTask(), new PlaceTorchTask(), new DigTunnelTask(), new WalkForwardTask()}; updateCyanList(); } @Override protected void onDisable() { MinecraftForge.EVENT_BUS.unregister(this); if(currentBlock != null) try { PlayerControllerUtils.setIsHittingBlock(true); mc.playerController.resetBlockRemoving(); currentBlock = null; }catch(ReflectiveOperationException e) { throw new RuntimeException(e); } for(int displayList : displayLists) GL11.glDeleteLists(displayList, 1); } @SubscribeEvent public void onUpdate(WUpdateEvent event) { HackList hax = wurst.getHax(); Hack[] incompatibleHax = {hax.autoToolHack, hax.autoWalkHack, hax.blinkHack, hax.flightHack, hax.nukerHack, hax.sneakHack}; for(Hack hack : incompatibleHax) hack.setEnabled(false); if(hax.freecamHack.isEnabled()) return; GameSettings gs = mc.gameSettings; KeyBinding[] bindings = {gs.keyBindForward, gs.keyBindBack, gs.keyBindLeft, gs.keyBindRight, gs.keyBindJump, gs.keyBindSneak}; for(KeyBinding binding : bindings) KeyBindingUtils.setPressed(binding, false); for(Task task : tasks) { if(!task.canRun()) continue; task.run(); break; } } @SubscribeEvent public void onRenderWorldLast(RenderWorldLastEvent event) { // GL settings GL11.glEnable(GL11.GL_BLEND); GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); GL11.glEnable(GL11.GL_LINE_SMOOTH); GL11.glLineWidth(2); GL11.glDisable(GL11.GL_TEXTURE_2D); GL11.glEnable(GL11.GL_CULL_FACE); GL11.glDisable(GL11.GL_DEPTH_TEST); GL11.glPushMatrix(); GL11.glTranslated(-TileEntityRendererDispatcher.staticPlayerX, -TileEntityRendererDispatcher.staticPlayerY, -TileEntityRendererDispatcher.staticPlayerZ); for(int displayList : displayLists) GL11.glCallList(displayList); if(currentBlock != null) { float p = prevProgress + (progress - prevProgress) * event.getPartialTicks(); float red = p * 2F; float green = 2 - red; GL11.glTranslated(currentBlock.getX(), currentBlock.getY(), currentBlock.getZ()); if(p < 1) { GL11.glTranslated(0.5, 0.5, 0.5); GL11.glScaled(p, p, p); GL11.glTranslated(-0.5, -0.5, -0.5); } AxisAlignedBB box2 = new AxisAlignedBB(BlockPos.ORIGIN); GL11.glColor4f(red, green, 0, 0.25F); GL11.glBegin(GL11.GL_QUADS); RenderUtils.drawSolidBox(box2); GL11.glEnd(); GL11.glColor4f(red, green, 0, 0.5F); GL11.glBegin(GL11.GL_LINES); RenderUtils.drawOutlinedBox(box2); GL11.glEnd(); } GL11.glPopMatrix(); // GL resets GL11.glColor4f(1, 1, 1, 1); GL11.glEnable(GL11.GL_DEPTH_TEST); GL11.glEnable(GL11.GL_TEXTURE_2D); GL11.glDisable(GL11.GL_BLEND); GL11.glDisable(GL11.GL_LINE_SMOOTH); } private void updateCyanList() { GL11.glNewList(displayLists[0], GL11.GL_COMPILE); GL11.glPushMatrix(); GL11.glTranslated(start.getX(), start.getY(), start.getZ()); GL11.glTranslated(0.5, 0.5, 0.5); GL11.glColor4f(0, 1, 1, 0.5F); GL11.glBegin(GL11.GL_LINES); RenderUtils .drawNode(new AxisAlignedBB(-0.25, -0.25, -0.25, 0.25, 0.25, 0.25)); GL11.glEnd(); RenderUtils.drawArrow( new Vec3d(direction.getDirectionVec()).scale(0.25), new Vec3d(direction.getDirectionVec()) .scale(Math.max(0.5, length))); GL11.glPopMatrix(); GL11.glEndList(); } private BlockPos offset(BlockPos pos, Vec3i vec) { return pos.offset(direction.rotateYCCW(), vec.getX()).up(vec.getY()); } private int getDistance(BlockPos pos1, BlockPos pos2) { return Math.abs(pos1.getX() - pos2.getX()) + Math.abs(pos1.getY() - pos2.getY()) + Math.abs(pos1.getZ() - pos2.getZ()); } private static abstract class Task { public abstract boolean canRun(); public abstract void run(); } private class DigTunnelTask extends Task { private int requiredDistance; @Override public boolean canRun() { BlockPos player = new BlockPos(WMinecraft.getPlayer()); BlockPos base = start.offset(direction, length); int distance = getDistance(player, base); if(distance <= 1) requiredDistance = size.getSelected().maxRange; else if(distance > size.getSelected().maxRange) requiredDistance = 1; return distance <= requiredDistance; } @Override public void run() { BlockPos base = start.offset(direction, length); BlockPos from = offset(base, size.getSelected().from); BlockPos to = offset(base, size.getSelected().to); ArrayList<BlockPos> blocks = new ArrayList<>(); BlockPos.getAllInBox(from, to).forEach(blocks::add); Collections.reverse(blocks); GL11.glNewList(displayLists[1], GL11.GL_COMPILE); AxisAlignedBB box = new AxisAlignedBB(0.1, 0.1, 0.1, 0.9, 0.9, 0.9); GL11.glColor4f(0, 1, 0, 0.5F); for(BlockPos pos : blocks) { GL11.glPushMatrix(); GL11.glTranslated(pos.getX(), pos.getY(), pos.getZ()); GL11.glBegin(GL11.GL_LINES); RenderUtils.drawOutlinedBox(box); GL11.glEnd(); GL11.glPopMatrix(); } GL11.glEndList(); currentBlock = null; for(BlockPos pos : blocks) { if(!BlockUtils.canBeClicked(pos)) continue; currentBlock = pos; break; } if(currentBlock == null) { mc.playerController.resetBlockRemoving(); progress = 1; prevProgress = 1; length++; if(limit.getValueI() == 0 || length < limit.getValueI()) updateCyanList(); else { ChatUtils.message("Tunnel completed."); setEnabled(false); } return; } wurst.getHax().autoToolHack.equipBestTool(currentBlock, false, true, false); BlockUtils.breakBlockSimple(currentBlock); if(WMinecraft.getPlayer().capabilities.isCreativeMode || BlockUtils.getHardness(currentBlock) >= 1) { progress = 1; prevProgress = 1; return; } try { prevProgress = progress; progress = PlayerControllerUtils.getCurBlockDamageMP(); if(progress < prevProgress) prevProgress = progress; }catch(ReflectiveOperationException e) { setEnabled(false); throw new RuntimeException(e); } } } private class WalkForwardTask extends Task { @Override public boolean canRun() { BlockPos player = new BlockPos(WMinecraft.getPlayer()); BlockPos base = start.offset(direction, length); return getDistance(player, base) > 1; } @Override public void run() { BlockPos base = start.offset(direction, length); Vec3d vec = new Vec3d(base).addVector(0.5, 0.5, 0.5); RotationUtils.faceVectorForWalking(vec); KeyBindingUtils.setPressed(mc.gameSettings.keyBindForward, true); } } private class FillInFloorTask extends Task { private final ArrayList<BlockPos> blocks = new ArrayList<>(); @Override public boolean canRun() { BlockPos player = new BlockPos(WMinecraft.getPlayer()); BlockPos from = offsetFloor(player, size.getSelected().from); BlockPos to = offsetFloor(player, size.getSelected().to); blocks.clear(); for(BlockPos pos : BlockPos.getAllInBox(from, to)) if(!BlockUtils.getState(pos).isFullBlock()) blocks.add(pos); GL11.glNewList(displayLists[2], GL11.GL_COMPILE); AxisAlignedBB box = new AxisAlignedBB(0.1, 0.1, 0.1, 0.9, 0.9, 0.9); GL11.glColor4f(1, 1, 0, 0.5F); for(BlockPos pos : blocks) { GL11.glPushMatrix(); GL11.glTranslated(pos.getX(), pos.getY(), pos.getZ()); GL11.glBegin(GL11.GL_LINES); RenderUtils.drawOutlinedBox(box); GL11.glEnd(); GL11.glPopMatrix(); } GL11.glEndList(); return !blocks.isEmpty(); } private BlockPos offsetFloor(BlockPos pos, Vec3i vec) { return pos.offset(direction.rotateYCCW(), vec.getX()).down(); } @Override public void run() { KeyBindingUtils.setPressed(mc.gameSettings.keyBindSneak, true); WMinecraft.getPlayer().motionX = 0; WMinecraft.getPlayer().motionZ = 0; Vec3d eyes = RotationUtils.getEyesPos().addVector(-0.5, -0.5, -0.5); Comparator<BlockPos> comparator = Comparator.<BlockPos> comparingDouble( p -> eyes.squareDistanceTo(new Vec3d(p))); BlockPos pos = blocks.stream().max(comparator).get(); if(!equipSolidBlock(pos)) { ChatUtils.error( "Found a hole in the tunnel's floor but don't have any blocks to fill it with."); setEnabled(false); return; } if(BlockUtils.getMaterial(pos).isReplaceable()) BlockUtils.placeBlockSimple(pos); else { wurst.getHax().autoToolHack.equipBestTool(pos, false, true, false); BlockUtils.breakBlockSimple(pos); } } private boolean equipSolidBlock(BlockPos pos) { for(int slot = 0; slot < 9; slot++) { // filter out non-block items ItemStack stack = WMinecraft.getPlayer().inventory.getStackInSlot(slot); if(WItem.isNullOrEmpty(stack) || !(stack.getItem() instanceof ItemBlock)) continue; Block block = Block.getBlockFromItem(stack.getItem()); // filter out non-solid blocks if(!block.getDefaultState().isFullBlock()) continue; // filter out blocks that would fall if(block instanceof BlockFalling && BlockFalling .canFallThrough(BlockUtils.getState(pos.down()))) continue; WMinecraft.getPlayer().inventory.currentItem = slot; return true; } return false; } } private class DodgeLiquidTask extends Task { private final HashSet<BlockPos> liquids = new HashSet<>(); private int disableTimer = 60; @Override public boolean canRun() { if(!liquids.isEmpty()) return true; BlockPos base = start.offset(direction, length); BlockPos from = offset(base, size.getSelected().from); BlockPos to = offset(base, size.getSelected().to); int maxY = Math.max(from.getY(), to.getY()); for(BlockPos pos : BlockPos.getAllInBox(from, to)) { // check current & previous blocks int maxOffset = Math.min(size.getSelected().maxRange, length); for(int i = 0; i <= maxOffset; i++) { BlockPos pos2 = pos.offset(direction.getOpposite(), i); if(BlockUtils.getBlock(pos2) instanceof BlockLiquid) liquids.add(pos2); } if(BlockUtils.getState(pos).isFullBlock()) continue; // check next blocks BlockPos pos3 = pos.offset(direction); if(BlockUtils.getBlock(pos3) instanceof BlockLiquid) liquids.add(pos3); // check ceiling blocks if(pos.getY() == maxY) { BlockPos pos4 = pos.up(); if(BlockUtils.getBlock(pos4) instanceof BlockLiquid) liquids.add(pos4); } } if(liquids.isEmpty()) return false; ChatUtils.error("The tunnel is flooded, cannot continue."); GL11.glNewList(displayLists[3], GL11.GL_COMPILE); AxisAlignedBB box = new AxisAlignedBB(0.1, 0.1, 0.1, 0.9, 0.9, 0.9); GL11.glColor4f(1, 0, 0, 0.5F); for(BlockPos pos : liquids) { GL11.glPushMatrix(); GL11.glTranslated(pos.getX(), pos.getY(), pos.getZ()); GL11.glBegin(GL11.GL_LINES); RenderUtils.drawOutlinedBox(box); GL11.glEnd(); GL11.glPopMatrix(); } GL11.glEndList(); return true; } @Override public void run() { BlockPos player = new BlockPos(WMinecraft.getPlayer()); KeyBinding forward = mc.gameSettings.keyBindForward; Vec3d diffVec = new Vec3d(player.subtract(start)); Vec3d dirVec = new Vec3d(direction.getDirectionVec()); double dotProduct = diffVec.dotProduct(dirVec); BlockPos pos1 = start.offset(direction, (int)dotProduct); if(!player.equals(pos1)) { RotationUtils.faceVectorForWalking(toVec3d(pos1)); KeyBindingUtils.setPressed(forward, true); return; } BlockPos pos2 = start.offset(direction, Math.max(0, length - 10)); if(!player.equals(pos2)) { RotationUtils.faceVectorForWalking(toVec3d(pos2)); KeyBindingUtils.setPressed(forward, true); WMinecraft.getPlayer().setSprinting(true); return; } BlockPos pos3 = start.offset(direction, length + 1); RotationUtils.faceVectorForWalking(toVec3d(pos3)); KeyBindingUtils.setPressed(forward, false); WMinecraft.getPlayer().setSprinting(false); if(disableTimer > 0) { disableTimer--; return; } setEnabled(false); } private Vec3d toVec3d(BlockPos pos) { return new Vec3d(pos).addVector(0.5, 0.5, 0.5); } } private class PlaceTorchTask extends Task { private BlockPos lastTorch; private BlockPos nextTorch = start; @Override public boolean canRun() { if(!torches.isChecked()) { lastTorch = null; nextTorch = new BlockPos(WMinecraft.getPlayer()); GL11.glNewList(displayLists[4], GL11.GL_COMPILE); GL11.glEndList(); return false; } if(lastTorch != null) nextTorch = lastTorch.offset(direction, size.getSelected().torchDistance); GL11.glNewList(displayLists[4], GL11.GL_COMPILE); GL11.glColor4f(1, 1, 0, 0.5F); Vec3d torchVec = new Vec3d(nextTorch).addVector(0.5, 0, 0.5); RenderUtils.drawArrow(torchVec, torchVec.addVector(0, 0.5, 0)); GL11.glEndList(); BlockPos base = start.offset(direction, length); if(getDistance(start, base) <= getDistance(start, nextTorch)) return false; return Blocks.TORCH.canPlaceBlockAt(WMinecraft.getWorld(), nextTorch); } @Override public void run() { if(!equipTorch()) { ChatUtils.error("Out of torches."); setEnabled(false); return; } KeyBindingUtils.setPressed(mc.gameSettings.keyBindSneak, true); BlockUtils.placeBlockSimple(nextTorch); if(BlockUtils.getBlock(nextTorch) instanceof BlockTorch) lastTorch = nextTorch; } private boolean equipTorch() { for(int slot = 0; slot < 9; slot++) { // filter out non-block items ItemStack stack = WMinecraft.getPlayer().inventory.getStackInSlot(slot); if(WItem.isNullOrEmpty(stack) || !(stack.getItem() instanceof ItemBlock)) continue; // filter out non-torch blocks Block block = Block.getBlockFromItem(stack.getItem()); if(!(block instanceof BlockTorch)) continue; WMinecraft.getPlayer().inventory.currentItem = slot; return true; } return false; } } private enum TunnelSize { SIZE_1X2("1x2", new Vec3i(0, 1, 0), new Vec3i(0, 0, 0), 4, 13), SIZE_3X3("3x3", new Vec3i(1, 2, 0), new Vec3i(-1, 0, 0), 4, 11); private final String name; private final Vec3i from; private final Vec3i to; private final int maxRange; private final int torchDistance; private TunnelSize(String name, Vec3i from, Vec3i to, int maxRange, int torchDistance) { this.name = name; this.from = from; this.to = to; this.maxRange = maxRange; this.torchDistance = torchDistance; } @Override public String toString() { return name; } } }