Java tutorial
/** * Copyright 2013 Mark Browning, StellaArtois * Licensed under the LGPL 3.0 or later (See LICENSE.md for details) * * Contains code from Minecraft, copyright Mojang AB */ package com.mtbs3d.minecrift.control; import java.io.IOException; import java.lang.reflect.Field; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import com.mtbs3d.minecrift.settings.VRSettings; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiIngameMenu; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.GuiSlot; import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.inventory.Slot; import org.apache.commons.lang3.tuple.Pair; import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; import org.lwjgl.opengl.Display; public class GuiScreenNavigator { public ArrayList<Pair<Integer, Integer>> points = new ArrayList<Pair<Integer, Integer>>(); private GuiSlot slot; private int slotIndex = -1; private boolean onSlot = false; public GuiScreen screen; private GuiScreen parentScreen; private Minecraft mc; public static float aimPitchRate = 0; public static float aimYawRate = 0; public static float aimPitchAdd = 0; public static float aimYawAdd = 0; private int lastGuiCursorOffsetX = -1; private int lastGuiCursorOffsetY = -1; private Pair<Integer, Integer> curPoint; private static Field guiLeft = null; private static Field guiTop = null; private static Field keyDownField = null; public static boolean selectDepressed = false; public static boolean altselectDepressed = false; private boolean shiftDepressed; static final int AXIS_PREFERENCE = 5; private static GuiScreenNavigator nav; static abstract class GuiControlBinding extends ControlBinding { public GuiControlBinding(String desc) { super("GUI " + desc, "gui." + desc); } public GuiControlBinding(String desc, String key) { super("GUI " + desc, "gui." + key); } boolean floatActive = false; @Override public boolean isGUI() { return true; }; @Override public void setValue(float value) { if (Math.abs(value) > 0.5) { if (!floatActive) setState(true); floatActive = true; } else { setState(false); floatActive = false; } } } static class GuiUpBinding extends GuiControlBinding { public GuiUpBinding() { super("Up"); } @Override public void setState(boolean state) { if (state) nav.up(); } } static class GuiDownBinding extends GuiControlBinding { public GuiDownBinding() { super("Down"); } @Override public void setState(boolean state) { if (state) nav.down(); } } static class GuiRightBinding extends GuiControlBinding { public GuiRightBinding() { super("Right"); } @Override public void setState(boolean state) { if (state) nav.right(); } } static class GuiLeftBinding extends GuiControlBinding { public GuiLeftBinding() { super("Left"); } @Override public void setState(boolean state) { if (state) nav.left(); } } static class GuiSelectBinding extends GuiControlBinding { public GuiSelectBinding() { super("Select"); } @Override public void setState(boolean state) { nav.select(state); } } static class GuiAltSelectBinding extends GuiControlBinding { public GuiAltSelectBinding() { super("Alt. Select"); } @Override public void setState(boolean state) { nav.altselect(state); } } static class GuiBackBinding extends GuiControlBinding { public GuiBackBinding() { super("Back"); } @Override public void setState(boolean state) { if (state) nav.back(); } } static class GuiShiftBinding extends GuiControlBinding { public GuiShiftBinding() { super("Shift"); } @Override public void setState(boolean state) { nav.shift(state); } } public static class GuiCursorAimPitchBinding extends GuiScreenNavigator.GuiControlBinding { @Override public boolean isBiAxis() { return true; } public GuiCursorAimPitchBinding() { super("Cursor Up/Down", "axis.updown"); } @Override public void setValue(float value) { aimPitchRate = value; } @Override public void setState(boolean state) { } } public static class GuiCursorAimYawBinding extends GuiScreenNavigator.GuiControlBinding { @Override public boolean isBiAxis() { return true; } public GuiCursorAimYawBinding() { super("Cursor Left/Right", "axis.leftright"); } @Override public void setValue(float value) { aimYawRate = value; } @Override public void setState(boolean state) { } } public GuiScreenNavigator(GuiScreen screen) { mc = Minecraft.getMinecraft(); nav = this; if (guiLeft == null) { try { keyDownField = Keyboard.class.getDeclaredField("keyDownBuffer"); keyDownField.setAccessible(true); guiLeft = GuiContainer.class.getDeclaredField("field_147003_i"); // was guiLeft guiTop = GuiContainer.class.getDeclaredField("field_147009_r"); // was guiTop System.out.println("[Minecrift] GuiScreenNavigator: Reflected guiLeft/guiTop"); } catch (NoSuchFieldException e) { try { guiLeft = GuiContainer.class.getDeclaredField("i"); //obfuscated name was p guiTop = GuiContainer.class.getDeclaredField("r"); //obfuscated name was q System.out.println("[Minecrift] GuiScreenNavigator: Reflected obfuscated guiLeft/guiTop (i/r)"); } catch (NoSuchFieldException e1) { System.out.println( "[Minecrift] GuiScreenNavigator: Couldn't get guiLeft/guiTop via reflection! Joystick navigation of inventories may be inaccurate."); } ; } if (guiLeft != null) guiLeft.setAccessible(true); if (guiTop != null) guiTop.setAccessible(true); } this.screen = screen; for (Field field : screen.getClass().getDeclaredFields()) { if (field.getType().getSuperclass() == GuiSlot.class) { field.setAccessible(true); try { slot = (GuiSlot) field.get(screen); break; } catch (Exception e) { e.printStackTrace(); } } } Class<?> screenclazz = screen.getClass(); parentScreen = null; while (parentScreen == null && screenclazz != null) { for (Field field : screenclazz.getDeclaredFields()) { if (GuiScreen.class.isAssignableFrom(field.getType())) { field.setAccessible(true); try { //Assume the first declared GuiScreen object is the "parent" //Might not always work. parentScreen = (GuiScreen) field.get(screen); break; } catch (Exception e) { e.printStackTrace(); } } } screenclazz = screenclazz.getSuperclass(); } updateCursorPos(); if (slot != null && slot.publicGetSize() > 0) { slotIndex = 0; slot.select(0, false); onSlot = true; } } public void back() { if (screen instanceof GuiIngameMenu) mc.displayGuiScreen(null); else if (parentScreen != null) mc.displayGuiScreen(parentScreen); } public void select(boolean state) { if (state && onSlot) { slot.select(slotIndex, true); return; } selectDepressed = state; updateCursorPos(); if (curPoint != null) { if (keyDownField != null) try { ((ByteBuffer) keyDownField.get(null)).put(Keyboard.KEY_RSHIFT, (byte) (shiftDepressed ? 1 : 0)); } catch (Exception e) { } if (state) try { mc.currentScreen.mouseGuiDown(curPoint.getLeft(), curPoint.getRight(), 0); //Left click } catch (IOException e) { } else mc.currentScreen.mouseGuiUp(curPoint.getLeft(), curPoint.getRight(), 0); //Left click } } public void altselect(boolean state) { altselectDepressed = state; updateCursorPos(); if (curPoint != null) { if (keyDownField != null) try { ((ByteBuffer) keyDownField.get(null)).put(Keyboard.KEY_RSHIFT, (byte) (shiftDepressed ? 1 : 0)); } catch (Exception e) { } if (state) try { mc.currentScreen.mouseGuiDown(curPoint.getLeft(), curPoint.getRight(), 1); //Right click } catch (IOException e) { } else mc.currentScreen.mouseGuiUp(curPoint.getLeft(), curPoint.getRight(), 1); //Right click } } public void shift(boolean state) { shiftDepressed = state; } private float dist(Pair<Integer, Integer> a, Pair<Integer, Integer> b, float xScale, float yScale) { float x = xScale * (a.getLeft() - b.getLeft()); float y = yScale * (a.getRight() - b.getRight()); return (float) Math.sqrt(x * x + y * y); } public void left() { onSlot = false; updateCursorPos(); parsePoints(); if (curPoint != null) { Pair<Integer, Integer> nextBest = null; for (Pair<Integer, Integer> point : points) { if (point.getLeft() < curPoint.getLeft()) { if (nextBest == null || dist(nextBest, curPoint, 1, AXIS_PREFERENCE) > dist(point, curPoint, 1, AXIS_PREFERENCE)) nextBest = point; } } if (nextBest != null) { mc.currentScreen.mouseGuiDrag(curPoint.getLeft(), curPoint.getRight()); curPoint = nextBest; mouseto(); } } } public void right() { onSlot = false; updateCursorPos(); parsePoints(); if (curPoint != null) { Pair<Integer, Integer> nextBest = null; for (Pair<Integer, Integer> point : points) { if (point.getLeft() > curPoint.getLeft()) { if (nextBest == null || dist(nextBest, curPoint, 1, AXIS_PREFERENCE) > dist(point, curPoint, 1, AXIS_PREFERENCE)) nextBest = point; } } if (nextBest != null) { mc.currentScreen.mouseGuiDrag(curPoint.getLeft(), curPoint.getRight()); curPoint = nextBest; mouseto(); } } } public void down() { if (onSlot && slot != null && slotIndex != slot.publicGetSize() - 1) { slotIndex++; int slotY = slot.select(slotIndex, false); curPoint = Pair.of(screen.width / 2, slotY); mouseto(); return; } onSlot = false; updateCursorPos(); parsePoints(); if (curPoint != null) { Pair<Integer, Integer> nextBest = null; for (Pair<Integer, Integer> point : points) { if (point.getRight() > curPoint.getRight()) { if (nextBest == null || dist(nextBest, curPoint, AXIS_PREFERENCE, 1) > dist(point, curPoint, AXIS_PREFERENCE, 1)) nextBest = point; } } if (nextBest != null) { mc.currentScreen.mouseGuiDrag(curPoint.getLeft(), curPoint.getRight()); curPoint = nextBest; mouseto(); } } } public void up() { updateCursorPos(); parsePoints(); if (curPoint != null) { Pair<Integer, Integer> nextBest = null; for (Pair<Integer, Integer> point : points) { if (point.getRight() < curPoint.getRight()) { if (nextBest == null || dist(nextBest, curPoint, AXIS_PREFERENCE, 1) > dist(point, curPoint, AXIS_PREFERENCE, 1)) nextBest = point; } } if (nextBest != null) { mc.currentScreen.mouseGuiDrag(curPoint.getLeft(), curPoint.getRight()); curPoint = nextBest; mouseto(); onSlot = false; } else if (slot != null) { if (onSlot && slotIndex != 0) slotIndex--; int slotY = slot.select(slotIndex, false); curPoint = Pair.of(screen.width / 2, slotY); mouseto(); onSlot = true; } } } private void mouseto() { int mouseFromX = screen.getMouseX(); int mouseFromY = screen.getMouseY(); int mouseToX = curPoint.getLeft(); int mouseToY = curPoint.getRight(); int diffX = mouseToX - mouseFromX; int diffY = mouseToY - mouseFromY; // Don't use Mouse.setCursorPosition() here - it will screw up second // instances of Minecraft on the same PC mc.currentScreen.mouseOffsetX += diffX; mc.currentScreen.mouseOffsetY += diffY; updateCursorPos(); if (altselectDepressed || selectDepressed) { if (keyDownField != null) try { ((ByteBuffer) keyDownField.get(null)).put(Keyboard.KEY_RSHIFT, (byte) (shiftDepressed ? 1 : 0)); } catch (Exception e) { } mc.currentScreen.mouseGuiDrag(mouseToX, mouseToY); } } @SuppressWarnings("unchecked") protected void parsePoints() { points.clear(); for (GuiButton button : (List<GuiButton>) screen.buttonList) { if (button.enabled) points.add(Pair.of(button.xPosition + 5, button.yPosition + 5)); } if (screen instanceof GuiContainer) { GuiContainer container = (GuiContainer) screen; int xOffset = 125; //These are the offsets for at least the inventory screen and chest int yOffset = 48; if (guiLeft != null) { try { xOffset = guiLeft.getInt(container); yOffset = guiTop.getInt(container); } catch (Exception e) { e.printStackTrace(); } } for (Slot slot : (List<Slot>) container.inventorySlots.inventorySlots) { points.add(Pair.of(xOffset + slot.xDisplayPosition + 8, yOffset + slot.yDisplayPosition + 8)); } } } protected void updateCursorPos() { if (screen != null) curPoint = Pair.of(screen.getMouseX(), screen.getMouseY()); } public void guiCursor() { if (this.mc.currentScreen != null && Display.isActive()) { getInput(1f); int lastx = lastGuiCursorOffsetX; int lasty = lastGuiCursorOffsetY; // Start in center of screen the first time through if (mc.currentScreen.mouseOffsetX == -1) lastGuiCursorOffsetX = 0; if (mc.currentScreen.mouseOffsetY == -1) lastGuiCursorOffsetY = 0; // Increment offset by deltas lastGuiCursorOffsetX += aimYawAdd; lastGuiCursorOffsetY += aimPitchAdd * (this.mc.gameSettings.invertMouse ? 1f : -1f); if (lastx == lastGuiCursorOffsetX && lasty == lastGuiCursorOffsetY) return; onSlot = false; // Get mouse pos in guiscreen space int x = Mouse.getX() * this.mc.currentScreen.width / this.mc.displayWidth; int y = this.mc.currentScreen.height - Mouse.getY() * this.mc.currentScreen.height / this.mc.displayHeight - 1; // Get offset limit values int maxX = (this.mc.currentScreen.width - 1) - x; int maxY = (this.mc.currentScreen.height - 1) - y; int minX = -x; int minY = -y; // Restrict values to size of gui screen if (lastGuiCursorOffsetX > maxX) { lastGuiCursorOffsetX = maxX; } else if (lastGuiCursorOffsetX < minX) { lastGuiCursorOffsetX = minX; } if (lastGuiCursorOffsetY > maxY) { lastGuiCursorOffsetY = maxY; } else if (lastGuiCursorOffsetY < minY) { lastGuiCursorOffsetY = minY; } // Set emulated mouse offsets mc.currentScreen.mouseOffsetX = lastGuiCursorOffsetX; mc.currentScreen.mouseOffsetY = lastGuiCursorOffsetY; updateCursorPos(); if (selectDepressed || altselectDepressed) { if (keyDownField != null) try { ((ByteBuffer) keyDownField.get(null)).put(Keyboard.KEY_RSHIFT, (byte) (shiftDepressed ? 1 : 0)); } catch (Exception e) { } mc.currentScreen.mouseGuiDrag(curPoint.getLeft(), curPoint.getRight()); } } } protected void getInput(float partialTicks) { aimYawAdd = 2 * aimYawRate * VRSettings.inst.joystickSensitivity * partialTicks; aimPitchAdd = 2 * aimPitchRate * VRSettings.inst.joystickSensitivity * partialTicks; } }