package com.pokemongo; import java.awt.AWTException; import java.awt.MouseInfo; import java.awt.Point; import java.awt.PointerInfo; import java.awt.Rectangle; import java.awt.Robot; import java.awt.event.InputEvent; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.imageio.ImageIO; /** * Automation tool for Pokémon GO. * Finds running application, selects all Pokémon and clicks Transfer button. */ public class PokemonGoAutomation { private Robot robot; private Rectangle windowBounds; private BufferedImage t1Template; // TRANSFER button template private BufferedImage t2Template; // Confirmation button template private BufferedImage t3Template; private BufferedImage pok1Template; // Pokemon detection template private BufferedImage includeTemplate; // Include button template private static final int DELAY_BETWEEN_CLICKS = 100; // ms private static final int DELAY_AFTER_ACTION = 100; // ms private int transferredPokemonCount = 0; // Count of transferred pokemon private Point initialMousePosition = null; // Cursor position before start of automation private volatile boolean stopRequested = false; public PokemonGoAutomation() throws AWTException { this.robot = new Robot(); this.robot.setAutoDelay(50); this.robot.setAutoWaitForIdle(true); loadButtonTemplates(); } /** * Returns count of pokemon transferred so far */ public int getTransferredCount() { return transferredPokemonCount; } /** * Resets counter of transferred pokemon */ public void resetTransferredCount() { transferredPokemonCount = 0; } public void requestStop() { stopRequested = true; } private boolean shouldStop() { return stopRequested || Thread.currentThread().isInterrupted(); } /** * Takes screenshot of window area */ private BufferedImage captureScreen(Rectangle area) { return robot.createScreenCapture(area); } /** * Loads templates for TRANSFER button (t1.png) and confirmation (t2.png) */ private void loadButtonTemplates() { try { String t1Paths = "/t1.png"; t1Template = ImageIO.read(getClass().getResourceAsStream(t1Paths)); System.out.println("✅ Template TRANSFER loaded from: " + t1Paths); String t2Paths = "/t2.png"; t2Template = ImageIO.read(getClass().getResourceAsStream(t2Paths)); System.out.println("✅ Template Confirmation loaded from: " + t2Paths); String t3Paths = "/t3.png"; t3Template = ImageIO.read(getClass().getResourceAsStream(t3Paths)); System.out.println("✅ Template Confirmation loaded from: " + t3Paths); String pok1Paths = "/pok1.png"; pok1Template = ImageIO.read(getClass().getResourceAsStream(pok1Paths)); System.out.println("✅ Template Pokémon loaded from: " + pok1Paths); String includePaths = "/include.png"; includeTemplate = ImageIO.read(getClass().getResourceAsStream(includePaths)); System.out.println("✅ Template INCLUDE loaded from: " + includePaths); if (t1Template == null) { System.out.println("⚠️ Template t1.png not found"); } if (t2Template == null) { System.out.println("⚠️ Template t2.png not found"); } if (pok1Template == null) { System.out.println("⚠️ Template pok1.png not found"); } if (includeTemplate == null) { System.out.println("⚠️ Template include.png not found"); } } catch (IOException e) { System.err.println("⚠️ Error loading templates: " + e.getMessage()); } } /** * Compares template with area in image - simple pixel comparison with * optimization */ private double templateMatch(BufferedImage screenshot, int startX, int startY, BufferedImage template, int tolerance) { if (template == null) return 0.0; if (shouldStop()) { return 0.0; } int tWidth = template.getWidth(); int tHeight = template.getHeight(); // Kontrola hranic if (startX + tWidth > screenshot.getWidth() || startY + tHeight > screenshot.getHeight()) { return 0.0; } if (startX < 0 || startY < 0) { return 0.0; } int matchingPixels = 0; int totalPixels = tWidth * tHeight; int requiredMatch = (int) (totalPixels * 0.75); // 75% shody for (int y = 0; y < tHeight; y++) { if (shouldStop()) { return 0.0; } for (int x = 0; x < tWidth; x++) { int templateRGB = template.getRGB(x, y); int screenRGB = screenshot.getRGB(startX + x, startY + y); // Split RGB values int tR = (templateRGB >> 16) & 0xFF; int tG = (templateRGB >> 8) & 0xFF; int tB = templateRGB & 0xFF; int sR = (screenRGB >> 16) & 0xFF; int sG = (screenRGB >> 8) & 0xFF; int sB = screenRGB & 0xFF; // Compare colors - increased tolerance for better detection int rDiff = Math.abs(tR - sR); int gDiff = Math.abs(tG - sG); int bDiff = Math.abs(tB - sB); if (rDiff < tolerance && gDiff < tolerance && bDiff < tolerance) { matchingPixels++; // Early exit - if we have not reached minimum, stop if (matchingPixels < requiredMatch / 2 && (y * tWidth + x) > (totalPixels / 2)) { return 0.0; } } } } // Return percentage match (0.0-1.0) return (double) matchingPixels / totalPixels; } /** * Finds and returns position of confirmation button without clicking */ private Point findConfirmTransferButtonPosition(BufferedImage template, int tolerance) { System.out.println("Looking for confirm TRANSFER button using template matching..."); if (shouldStop()) { return null; } BufferedImage screenshot = captureScreen(windowBounds); int tWidth = template.getWidth(); int tHeight = template.getHeight(); // Dialog is usually in upper half after TRANSFER click int searchStartY = (int) (windowBounds.height * 0.40); int searchEndY = (int) (windowBounds.height * 0.75); double bestScore = 0.0; int bestX = 0; int bestY = 0; // Scan Y positions with larger step - 15px instead of 5px for speed for (int y = searchStartY; y <= searchEndY - tHeight; y += 15) { if (shouldStop()) { return null; } // Also scan X positions to find center of match for (int x = 0; x <= windowBounds.width - tWidth; x += 30) { double score = templateMatch(screenshot, x, y, template, tolerance); if (score > bestScore) { bestScore = score; bestX = x; bestY = y; } } } if (bestScore > 0.85) { Point bestMatch = new Point( windowBounds.x + bestX + (tWidth / 2), windowBounds.y + bestY + (tHeight / 2)); System.out.println("✅ Found confirmation button (template match) at: " + bestMatch + " (match: " + String.format("%.1f", bestScore * 100) + "%)"); return bestMatch; } System.out.println("Template not found"); return null; } private Point findIncludeButtonPosition(int tolerance) { System.out.println("Looking for INCLUDE button using template matching (include.png)..."); if (shouldStop()) { return null; } BufferedImage screenshot = captureScreen(windowBounds); int tWidth = includeTemplate.getWidth(); int tHeight = includeTemplate.getHeight(); // Dialog is usually in upper half after TRANSFER click int searchStartY = (int) (windowBounds.height * 0.40); int searchEndY = (int) (windowBounds.height * 0.75); double bestScore = 0.0; int bestX = 0; int bestY = 0; // Scan Y positions with larger step - 15px instead of 5px for speed for (int y = searchStartY; y <= searchEndY - tHeight; y += 15) { if (shouldStop()) { return null; } // Also scan X positions to find center of match for (int x = 0; x <= windowBounds.width - tWidth; x += 30) { double score = templateMatch(screenshot, x, y, includeTemplate, tolerance); if (score > bestScore) { bestScore = score; bestX = x; bestY = y; } } } if (bestScore > 0.80) { Point bestMatch = new Point( windowBounds.x + bestX + (tWidth / 2), windowBounds.y + bestY + (tHeight / 2)); System.out.println("✅ Found INCLUDE button (template match) at: " + bestMatch + " (match: " + String.format("%.1f", bestScore * 100) + "%)"); return bestMatch; } System.out.println("Template not found"); return null; } /** * Finds window with name containing "Pokémon" or "Pokemon" */ public boolean findPokemonGoWindow() { try { Rectangle bounds = WindowFinder.findWindowByName("Lenovo"); if (bounds != null && bounds.width > 0 && bounds.height > 0) { windowBounds = bounds; System.out.println("Window found: " + windowBounds); return true; } System.out.println("Pokémon GO window not found."); return false; } catch (Exception e) { System.err.println("Error finding window: " + e.getMessage()); e.printStackTrace(); return false; } } /** * Aktivuje okno aplikace */ public void activateWindow() { try { // Click on top panel of window (title bar) for activation // Title bar is usually 30-40px high at top of window int centerX = windowBounds.x + windowBounds.width / 2; int titleBarY = windowBounds.y - 15; // 15px from top of window = top panel System.out.println("Activating window by clicking title bar: (" + centerX + ", " + titleBarY + ")"); robot.mouseMove(centerX, titleBarY); robot.delay(200); robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); robot.delay(DELAY_AFTER_ACTION); System.out.println("Window activated"); } catch (Exception e) { System.err.println("Error activating window: " + e.getMessage()); } } /** * Clicks Transfer button at bottom of screen */ public void clickTransferButton() { if (shouldStop()) { return; } // Use button detection // Point buttonPos = findTransferButtonPosition(); Point buttonPos = getAbsolutePoint(300, 1156); if (buttonPos == null) { System.err.println("TRANSFER button not found!"); return; } robot.mouseMove(buttonPos.x, buttonPos.y); robot.delay(DELAY_AFTER_ACTION); robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); System.out.println("TRANSFER button clicked!"); robot.delay(DELAY_AFTER_ACTION); } /** * Confirms transfer (if confirmation dialog is needed) */ public void clickConfirmTransferButton(BufferedImage template, int tolerance) { System.out.println("Confirming transfer - looking for confirmation button..."); if (shouldStop()) { return; } robot.delay(100); // Wait for dialog to appear // Find green TRANSFER button in confirmation dialog Point buttonPos = findConfirmTransferButtonPosition(template, tolerance); if (buttonPos == null) { System.out.println("Confirmation button not found, skipping confirmation."); return; } robot.mouseMove(buttonPos.x, buttonPos.y); robot.delay(DELAY_BETWEEN_CLICKS); robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); System.out.println("Transfer finally confirmed!"); robot.delay(DELAY_AFTER_ACTION); } public boolean clickIncludeButton(int tolerance) { System.out.println("Confirming transfer - looking for include.png (confirmation button)..."); if (shouldStop()) { return false; } robot.delay(100); // Wait for dialog to appear // Find green INCLUDE button in confirmation dialog Point buttonPos = findIncludeButtonPosition(tolerance); if (buttonPos == null) { System.out.println("INCLUDE button not found, skipping confirmation."); return false; } robot.mouseMove(buttonPos.x, buttonPos.y); robot.delay(DELAY_BETWEEN_CLICKS); robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); System.out.println("Transfer finally confirmed!"); robot.delay(DELAY_AFTER_ACTION); return true; } /** * Starts Transfer automation with specific number of pokemon * * @param totalPokemonCount Total number of pokemon to transfer * @param delaySeconds Delay between iterations (in seconds) */ public void runWithCount(int totalPokemonCount, int delaySeconds) { System.out.println("Starting Transfer automation - Pokémon count: " + totalPokemonCount + ", Delay: " + delaySeconds + "s"); // Save current cursor position PointerInfo pointerInfo = MouseInfo.getPointerInfo(); initialMousePosition = pointerInfo != null ? pointerInfo.getLocation() : null; if (initialMousePosition != null) { System.out.println("Initial cursor position saved: " + initialMousePosition); } if (!findPokemonGoWindow()) { System.err.println("Failed to find Pokémon GO window!"); return; } activateWindow(); System.out.println("Waiting 1 second before start..."); robot.delay(1000); int transferredCount = 0; // Transfer until we reach desired count try { while (transferredCount < totalPokemonCount) { // Check interruption if (shouldStop()) { System.out.println("\n⚠️ Automation was interrupted!"); return; } int pokemonThisRound = Math.min(12, totalPokemonCount - transferredCount); System.out.println( "\n=== Iteration " + (transferredCount / 9 + 1) + " - Transferring max " + pokemonThisRound + " pokemon ==="); // Select pokemon and get actual number selected int actualTransferredCount = selectAllPokemonCount(pokemonThisRound); if (shouldStop()) { System.out.println("\n⚠️ Automation was interrupted during selection!"); return; } System.out.println("Waiting for TRANSFER..."); clickTransferButton(); if (shouldStop()) { System.out.println("\n⚠️ Automation was interrupted after clicking TRANSFER!"); return; } System.out.println("Waiting for INCLUDE dialog..."); robot.delay(100); if (clickIncludeButton(110)) { System.out.println("Waiting for confirmation dialog t3..."); robot.delay(100); clickConfirmTransferButton(t3Template, 70); } else { System.out.println("Waiting for confirmation dialog t2..."); robot.delay(100); clickConfirmTransferButton(t2Template, 70); } transferredCount += actualTransferredCount; transferredPokemonCount += actualTransferredCount; System.out.println("Transferred in this iteration: " + actualTransferredCount + "pokemon (total: " + transferredCount + ")"); // If there is still something to transfer, wait if (transferredCount < totalPokemonCount) { System.out.println("Break: " + delaySeconds + " seconds..."); // Wait with checking interruption long endTime = System.currentTimeMillis() + (delaySeconds * 1000L); while (System.currentTimeMillis() < endTime) { if (shouldStop()) { System.out.println("\n⚠️ Automation was interrupted during break!"); break; } robot.delay(100); } } } } finally { // Obnovit pozici kurzoru if (initialMousePosition != null) { try { robot.mouseMove(initialMousePosition.x, initialMousePosition.y); System.out.println("Cursor returned to initial position: " + initialMousePosition); } catch (Exception e) { System.err.println("Error restoring cursor position: " + e.getMessage()); } } } System.out.println("\n✅ Transfer automation complete! Transferred: " + transferredCount + "pokemon"); } /** * Selects specific number of pokemon * * @return Actual number of selected pokemon */ private int selectAllPokemonCount(int count) { System.out.println("Starting to select " + count + "pokemon (template matching only)..."); List positions = getPossitionsAbsolute(); int maxPokemon = Math.min(count, positions.size()); System.out.println("Will select " + maxPokemon + "pokemon..."); for (int i = 0; i < maxPokemon; i++) { if (shouldStop()) { System.out.println("Selection interrupted."); return i; } Point pos = positions.get(i); System.out.println("Clicking on Pokémon " + (i + 1) + "/" + maxPokemon + " at position: " + pos); robot.mouseMove(pos.x, pos.y); if (i == 0) { System.out.println(" -> Long press (activate multi-select)"); robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); robot.delay(700); robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); robot.delay(DELAY_AFTER_ACTION); } else { System.out.println(" -> Normal click"); robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); } robot.delay(DELAY_BETWEEN_CLICKS); } System.out.println("Selected " + maxPokemon + "pokemon!"); System.out.println("Waiting before clicking TRANSFER..."); robot.delay(800); return maxPokemon; } private List getPossitionsAbsolute() { List possitions = new ArrayList<>(); possitions.add(getAbsolutePoint(115, 290)); possitions.add(getAbsolutePoint(310, 290)); possitions.add(getAbsolutePoint(504, 290)); possitions.add(getAbsolutePoint(115, 510)); possitions.add(getAbsolutePoint(310, 510)); possitions.add(getAbsolutePoint(504, 510)); possitions.add(getAbsolutePoint(115, 730)); possitions.add(getAbsolutePoint(310, 730)); possitions.add(getAbsolutePoint(504, 730)); possitions.add(getAbsolutePoint(115, 950)); possitions.add(getAbsolutePoint(310, 950)); possitions.add(getAbsolutePoint(504, 950)); return possitions; } /** * Converts relative position in window (0.0-1.0) to absolute screen coordinates * * @param relativeX Relative X position (0.0 = left edge, 1.0 = right edge) * @param relativeY Relative Y position (0.0 = top edge, 1.0 = bottom edge) * @return Absolute position on screen */ private Point getAbsolutePoint(int relativeX, int relativeY) { int xCorrection = 0; int yCorrection = 0; int absoluteX = windowBounds.x + relativeX + xCorrection; int absoluteY = windowBounds.y + relativeY + yCorrection; return new Point(absoluteX, absoluteY); } /** * Cleans up all resources used by automation * Call after automation finishes */ public void cleanup() { try { System.out.println("Cleaning up automation resources..."); // Release references to images t1Template = null; t2Template = null; t3Template = null; pok1Template = null; includeTemplate = null; windowBounds = null; // Explicitly invoke garbage collector System.gc(); System.out.println("Automation resources cleaned"); } catch (Exception e) { System.err.println("Error cleaning up resources: " + e.getMessage()); } } public static void main(String[] args) { System.out.println("=== Pokémon GO Automation Tool ==="); System.out.println("Make sure Pokémon GO is running..."); } }