commit 5450f0e92f0657f589991c0fab03f824548cbe18 Author: Radek Davidek Date: Thu Dec 4 16:18:59 2025 +0100 initial commit diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..7b12add --- /dev/null +++ b/.gitignore @@ -0,0 +1,50 @@ +### Eclipse +.classpath +.project +.settings/ + +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/** +!**/src/test/** + +### STS ### +.mvn +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +mvnw +mvnw.cmd + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ + +### VS Code ### +.vscode/ + +### Log4j2 ### +logs +*.log + +# Ignore Mac DS_Store files +.DS_Store + +dependency-reduced-pom.xml + +cert.pem \ No newline at end of file diff --git a/pgo-automat-settings.properties b/pgo-automat-settings.properties new file mode 100644 index 0000000..a2f9984 --- /dev/null +++ b/pgo-automat-settings.properties @@ -0,0 +1,12 @@ +#Pokmon GO Automatizace - Nastaven +#Thu Dec 04 16:03:55 CET 2025 +autoklik.count=500 +window.width=807 +transfer.delay=0 +window.height=743 +autoklik.x=2380 +autoklik.y=1124 +transfer.count=30 +window.x=930 +autoklik.interval=100 +window.y=22 diff --git a/pgo.png b/pgo.png new file mode 100644 index 0000000..f2801e6 Binary files /dev/null and b/pgo.png differ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..5807d9d --- /dev/null +++ b/pom.xml @@ -0,0 +1,74 @@ + + + 4.0.0 + + com.pokemongo + pgo-automat + 1.0.0 + + + 11 + 11 + UTF-8 + + + + + + net.java.dev.jna + jna + 5.13.0 + + + net.java.dev.jna + jna-platform + 5.13.0 + + + + + org.openpnp + opencv + 4.7.0-0 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 11 + 11 + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.4.1 + + + package + + shade + + + + + com.pokemongo.PokemonGoGUI + + + pgo-automat-gui + + + + + + + diff --git a/src/main/java/com/pokemongo/GlobalHotkey.java b/src/main/java/com/pokemongo/GlobalHotkey.java new file mode 100644 index 0000000..c367840 --- /dev/null +++ b/src/main/java/com/pokemongo/GlobalHotkey.java @@ -0,0 +1,64 @@ +package com.pokemongo; + +import java.awt.event.KeyEvent; +import java.awt.KeyboardFocusManager; +import java.util.HashSet; +import java.util.Set; +import javax.swing.JLabel; + +/** + * Globální hotkey listener pro detekci CTRL+ALT+Z kombinace + * Funguje i když není okno fokusované + */ +public class GlobalHotkey extends JLabel { + private static GlobalHotkey instance; + private GlobalKeyListener globalKeyListener; + + public GlobalHotkey(Runnable onHotkey) { + this.globalKeyListener = new GlobalKeyListener(onHotkey); + + // Registrace globálního key listeneru + KeyboardFocusManager.getCurrentKeyboardFocusManager() + .addKeyEventDispatcher(globalKeyListener); + } + + public static GlobalHotkey create(Runnable onHotkey) { + if (instance == null) { + instance = new GlobalHotkey(onHotkey); + } + return instance; + } + + /** + * Interní třída pro globální naslouchání na klávesnici + */ + private static class GlobalKeyListener implements java.awt.KeyEventDispatcher { + private Runnable callback; + private Set pressedKeys = new HashSet<>(); + + public GlobalKeyListener(Runnable callback) { + this.callback = callback; + } + + @Override + public boolean dispatchKeyEvent(KeyEvent e) { + if (e.getID() == KeyEvent.KEY_PRESSED) { + pressedKeys.add(e.getKeyCode()); + + // Detekce CTRL+ALT+X + if (e.getKeyCode() == KeyEvent.VK_X && + e.isControlDown() && e.isAltDown()) { + System.out.println("🔌 Globální hotkey detekován: CTRL+ALT+X"); + if (callback != null) { + callback.run(); + } + return true; // Konzumovat event + } + } else if (e.getID() == KeyEvent.KEY_RELEASED) { + pressedKeys.remove(e.getKeyCode()); + } + + return false; // Nepropagovat ostatní eventy + } + } +} diff --git a/src/main/java/com/pokemongo/PokemonGoAutomation.java b/src/main/java/com/pokemongo/PokemonGoAutomation.java new file mode 100644 index 0000000..ac71572 --- /dev/null +++ b/src/main/java/com/pokemongo/PokemonGoAutomation.java @@ -0,0 +1,651 @@ +package com.pokemongo; + +import java.awt.AWTException; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Point; +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; + +/** + * Automatizační nástroj pro Pokémon GO. + * Najde běžící aplikaci, označí všechny Pokémony a stiskne tlačítko Transfer. + */ +public class PokemonGoAutomation { + + private Robot robot; + private Rectangle windowBounds; + private BufferedImage t1Template; // TRANSFER button template + private BufferedImage t2Template; // Confirmation button template + private BufferedImage pok1Template; // Pokemon detection template + private BufferedImage includeTemplate; // Include button template + private static final int DELAY_BETWEEN_CLICKS = 200; // ms + private static final int DELAY_AFTER_ACTION = 500; // ms + private int transferredPokemonCount = 0; // Počet transfernutých pokémonů + + public PokemonGoAutomation() throws AWTException { + this.robot = new Robot(); + this.robot.setAutoDelay(50); + this.robot.setAutoWaitForIdle(true); + loadButtonTemplates(); + } + + /** + * Vrátí počet dosud transfernutých pokémonů + */ + public int getTransferredCount() { + return transferredPokemonCount; + } + + /** + * Resetuje počítadlo transfernutých pokémonů + */ + public void resetTransferredCount() { + transferredPokemonCount = 0; + } + + /** + * Pořídí screenshot oblasti okna + */ + private BufferedImage captureScreen(Rectangle area) { + return robot.createScreenCapture(area); + } + + /** + * Načte templaty pro tlačítka TRANSFER (t1.png) a confirmation (t2.png) + */ + private void loadButtonTemplates() { + try { + String t1Paths = "/t1.png"; + + t1Template = ImageIO.read(getClass().getResourceAsStream(t1Paths)); + System.out.println("✅ Template TRANSFER načten z: " + t1Paths); + + String t2Paths = "/t2.png"; + + t2Template = ImageIO.read(getClass().getResourceAsStream(t2Paths)); + System.out.println("✅ Template Potvrzení načten z: " + t2Paths); + + String pok1Paths = "/pok1.png"; + + pok1Template = ImageIO.read(getClass().getResourceAsStream(pok1Paths)); + System.out.println("✅ Template Pokémon načten z: " + pok1Paths); + + String includePaths = "/include.png"; + + includeTemplate = ImageIO.read(getClass().getResourceAsStream(includePaths)); + System.out.println("✅ Template INCLUDE načten z: " + includePaths); + + if (t1Template == null) { + System.out.println("⚠️ Template t1.png nebyl nalezen"); + } + if (t2Template == null) { + System.out.println("⚠️ Template t2.png nebyl nalezen"); + } + if (pok1Template == null) { + System.out.println("⚠️ Template pok1.png nebyl nalezen"); + } + if (includeTemplate == null) { + System.out.println("⚠️ Template include.png nebyl nalezen"); + } + } catch (IOException e) { + System.err.println("⚠️ Chyba při načítání templates: " + e.getMessage()); + } + } + + /** + * Porovnává template s oblastí v obrázku - jednoduché pixelové porovnání s + * optimalizací + */ + private double templateMatch(BufferedImage screenshot, int startX, int startY, BufferedImage template, int tolerance) { + if (template == null) + 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++) { + for (int x = 0; x < tWidth; x++) { + int templateRGB = template.getRGB(x, y); + int screenRGB = screenshot.getRGB(startX + x, startY + y); + + // Rozdělit RGB hodnoty + 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; + + // Porovnat barvy - zvýšená tolerance pro lepší detekci + 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 - pokud jsme nedosáhli minima, skončit + if (matchingPixels < requiredMatch / 2 && (y * tWidth + x) > (totalPixels / 2)) { + return 0.0; + } + } + } + } + + // Vrátit procentuální shodu (0.0-1.0) + return (double) matchingPixels / totalPixels; + } + + /** + * Detekuje pozice Pokémonů pomocí template matchingu s pok1.png + * Hledá v oblasti od vrchu obrazovky do 75% výšky + * Může najít až 9 Pokémonů + */ + private List detectPokemonsByTemplateMatching() { + List positions = new ArrayList<>(); + + System.out.println("\n=== Detekce Pokémonů pomocí template matchingu (pok1.png) ==="); + + if (pok1Template == null) { + System.out.println("⚠️ Template pok1.png není dostupný"); + return positions; + } + + BufferedImage screenshot = captureScreen(windowBounds); + + int tWidth = pok1Template.getWidth(); + int tHeight = pok1Template.getHeight(); + System.out.println("Template velikost: " + tWidth + "x" + tHeight); + + // Grid parametry - 3x3 (9 Pokémonů) - pevné pozice podle layoutu aplikace + int gridStartY = (int) (windowBounds.height * 0.15); + int gridEndY = (int) (windowBounds.height * 0.75); + int gridHeight = gridEndY - gridStartY; + + int gridMargin = (int) (windowBounds.width * 0.05); + int gridStartX = gridMargin; + int gridWidth = windowBounds.width - (2 * gridMargin); + + int columns = 3; + int rows = 3; // 3x3 grid = 9 Pokémonů + + int columnWidth = gridWidth / columns; + int rowHeight = gridHeight / rows; + + System.out.println("Hledám Pokémony v gridu 3x3 (9 pozic)"); + + // Pro každou buňku v gridu hledat template + for (int row = 0; row < rows; row++) { + for (int col = 0; col < columns; col++) { + int cellX = gridStartX + (col * columnWidth); + int cellY = gridStartY + (row * rowHeight); + + double bestScore = 0.0; + int bestX = cellX + columnWidth / 2; + int bestY = cellY + rowHeight / 2; + + // Skenovat buňku s jemností + for (int sy = cellY; sy < cellY + rowHeight - tHeight; sy += 3) { + for (int sx = cellX; sx < cellX + columnWidth - tWidth; sx += 3) { + double score = templateMatch(screenshot, sx, sy, pok1Template, 115); // Zvýšená tolerance + if (score > bestScore) { + bestScore = score; + bestX = sx + tWidth / 2; + bestY = sy + tHeight / 2; + } + } + } + + // Pokud jsme našli match (skóre > 75%), považovat za Pokémona + if (bestScore > 0.98) { + int x = windowBounds.x + bestX; + int y = windowBounds.y + bestY; + positions.add(new Point(x, y)); + System.out.println(" Detekován Pokémon na [" + row + "," + col + "] -> (" + x + ", " + y + + ") skóre=" + String.format("%.1f", bestScore * 100) + "%"); + } + } + } + + System.out.println("Celkem detekováno: " + positions.size() + " Pokémonů"); + System.out.println("=================================\n"); + + return positions; + } + + + /** + * Najde a vrátí pozici transferu tlačítka bez kliknutí + */ + private Point findTransferButtonPosition() { + System.out.println("Hledám tlačítko TRANSFER pomocí template matchingu (t1.png)..."); + + BufferedImage screenshot = captureScreen(windowBounds); + + int tWidth = t1Template.getWidth(); + int tHeight = t1Template.getHeight(); + + // Template je celý řádek - zkusíme matchovat od 75% do 95% (kde by měl být + // TRANSFER řádek) + int searchStartY = (int) (windowBounds.height * 0.75); + int searchEndY = (int) (windowBounds.height * 0.95); + + double bestScore = 0.0; + int bestX = 0; + int bestY = 0; + + // Skenovat Y-ové pozice s větším krokem - 15px místo 5px pro zrychlení + for (int y = searchStartY; y <= searchEndY - tHeight; y += 15) { + // Skenovat také X pozice pro nalezení středu shody + for (int x = 0; x <= windowBounds.width - tWidth; x += 30) { + double score = templateMatch(screenshot, x, y, t1Template, 90); + + if (score > bestScore) { + bestScore = score; + bestX = x; + bestY = y; + } + } + } + + if (bestScore > 0.92) { + Point bestMatch = new Point( + windowBounds.x + bestX + (tWidth / 2), + windowBounds.y + bestY + (tHeight / 2)); + System.out.println("✅ Nalezeno TRANSFER tlačítko (template match) na: " + bestMatch + " (shoda: " + + String.format("%.1f", bestScore * 100) + "%)"); + return bestMatch; + } + + System.out.println("Template nenalezen"); + return null; + } + + /** + * Najde a vrátí pozici potvrzovacího tlačítka bez kliknutí + */ + private Point findConfirmTransferButtonPosition() { + System.out.println("Hledám potvrzovací TRANSFER tlačítko pomocí template matchingu (t2.png)..."); + + BufferedImage screenshot = captureScreen(windowBounds); + + int tWidth = t2Template.getWidth(); + int tHeight = t2Template.getHeight(); + + // Dialog je obvykle v horní polovině po TRANSFER kliknutí + int searchStartY = (int) (windowBounds.height * 0.40); + int searchEndY = (int) (windowBounds.height * 0.75); + + double bestScore = 0.0; + int bestX = 0; + int bestY = 0; + + // Skenovat Y-ové pozice s větším krokem - 15px místo 5px pro zrychlení + for (int y = searchStartY; y <= searchEndY - tHeight; y += 15) { + // Skenovat také X pozice pro nalezení středu shody + for (int x = 0; x <= windowBounds.width - tWidth; x += 30) { + double score = templateMatch(screenshot, x, y, t2Template, 90); + + 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("✅ Nalezeno potvrzovací tlačítko (template match) na: " + bestMatch + " (shoda: " + + String.format("%.1f", bestScore * 100) + "%)"); + return bestMatch; + } + + System.out.println("Template nenalezen"); + return null; + } + + private Point findIncludeButtonPosition() { + System.out.println("Hledám tlačítko INCLUDE pomocí template matchingu (include.png)..."); + + BufferedImage screenshot = captureScreen(windowBounds); + + int tWidth = includeTemplate.getWidth(); + int tHeight = includeTemplate.getHeight(); + + // Dialog je obvykle v horní polovině po TRANSFER kliknutí + int searchStartY = (int) (windowBounds.height * 0.40); + int searchEndY = (int) (windowBounds.height * 0.75); + + double bestScore = 0.0; + int bestX = 0; + int bestY = 0; + + // Skenovat Y-ové pozice s větším krokem - 15px místo 5px pro zrychlení + for (int y = searchStartY; y <= searchEndY - tHeight; y += 15) { + // Skenovat také X pozice pro nalezení středu shody + for (int x = 0; x <= windowBounds.width - tWidth; x += 30) { + double score = templateMatch(screenshot, x, y, includeTemplate, 90); + + if (score > bestScore) { + bestScore = score; + bestX = x; + bestY = y; + } + } + } + + if (bestScore > 0.92) { + Point bestMatch = new Point( + windowBounds.x + bestX + (tWidth / 2), + windowBounds.y + bestY + (tHeight / 2)); + System.out.println("✅ Nalezeno tlačítko INCLUDE (template match) na: " + bestMatch + " (shoda: " + + String.format("%.1f", bestScore * 100) + "%)"); + return bestMatch; + } + + System.out.println("Template nenalezen"); + return null; + } + + /** + * Najde okno s názvem obsahujícím "Pokémon" nebo "Pokemon" + */ + public boolean findPokemonGoWindow() { + try { + // Pro Linux - hledání okna pomocí xdotool + // Nejdříve najít ID okna, pak získat jeho geometrii + Process searchProcess = Runtime.getRuntime().exec( + new String[] { "bash", "-c", "xdotool search --name \"Lenovo|L78032\" | head -1" }); + + java.io.BufferedReader searchReader = new java.io.BufferedReader( + new java.io.InputStreamReader(searchProcess.getInputStream())); + + String windowId = searchReader.readLine(); + searchProcess.waitFor(); + + if (windowId != null && !windowId.trim().isEmpty()) { + System.out.println("Nalezeno okno s ID: " + windowId); + + // Získat geometrii okna + Process geomProcess = Runtime.getRuntime().exec( + new String[] { "bash", "-c", "xdotool getwindowgeometry " + windowId.trim() }); + + java.io.BufferedReader geomReader = new java.io.BufferedReader( + new java.io.InputStreamReader(geomProcess.getInputStream())); + + String line; + int x = 0, y = 0, width = 0, height = 0; + + while ((line = geomReader.readLine()) != null) { + System.out.println("xdotool výstup: " + line); + if (line.contains("Position:")) { + String[] parts = line.split("Position: ")[1].split(","); + x = Integer.parseInt(parts[0].trim()); + y = Integer.parseInt(parts[1].trim().split(" ")[0]); + } else if (line.contains("Geometry:")) { + String[] parts = line.split("Geometry: ")[1].split("x"); + width = Integer.parseInt(parts[0].trim()); + height = Integer.parseInt(parts[1].trim()); + } + } + + geomProcess.waitFor(); + + if (width > 0 && height > 0) { + windowBounds = new Rectangle(x, y, width, height); + System.out.println("Nalezeno okno: " + windowBounds); + return true; + } + } + + // Fallback - použití celé obrazovky + System.out.println("Okno nenalezeno, použiji celou obrazovku"); + GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); + windowBounds = gd.getDefaultConfiguration().getBounds(); + return true; + + } catch (Exception e) { + System.err.println("Chyba při hledání okna: " + e.getMessage()); + e.printStackTrace(); + // Použití celé obrazovky jako záložní varianta + GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); + windowBounds = gd.getDefaultConfiguration().getBounds(); + return true; + } + } + + /** + * Aktivuje okno aplikace + */ + public void activateWindow() { + try { + // Kliknutí na horní panel okna (title bar) pro aktivaci + // Title bar je obvykle 30-40px vysoký v horní části okna + int centerX = windowBounds.x + windowBounds.width / 2; + int titleBarY = windowBounds.y + 15; // 15px od vrcholu okna = horní panel + + System.out.println("Aktivuji okno kliknutím na 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("Okno aktivováno"); + } catch (Exception e) { + System.err.println("Chyba při aktivaci okna: " + e.getMessage()); + } + } + + /** + * Stiskne tlačítko Transfer na spodní části obrazovky + */ + public void clickTransferButton() { + // Použít detekci tlačítka + Point buttonPos = findTransferButtonPosition(); + + if (buttonPos == null) { + System.err.println("Tlačítko TRANSFER nebylo nalezeno!"); + 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("Tlačítko TRANSFER stisknuto!"); + robot.delay(DELAY_AFTER_ACTION); + } + + /** + * Potvrdí transfer (pokud je potřeba potvrzovací dialog) + */ + public void clickConfirmTransferButton() { + System.out.println("Potvrzuji transfer - hledám t2.png (confirmation button)..."); + + robot.delay(100); // Počkat na zobrazení dialogu + + // Najít zelené TRANSFER tlačítko v potvrzovacím dialogu + Point buttonPos = findConfirmTransferButtonPosition(); + if (buttonPos == null) { + System.out.println("Potvrzovací tlačítko nebylo nalezeno, přeskočím potvrzení."); + 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 finálně potvrzen!"); + robot.delay(DELAY_AFTER_ACTION); + } + + public void clickIncludeButton() { + System.out.println("Potvrzuji transfer - hledám include.png (confirmation button)..."); + + robot.delay(100); // Počkat na zobrazení dialogu + + // Najít zelené INCLUDE tlačítko v potvrzovacím dialogu + Point buttonPos = findIncludeButtonPosition(); + if (buttonPos == null) { + System.out.println("INCLUDE tlačítko nebylo nalezeno, přeskočím potvrzení."); + 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 finálně potvrzen!"); + robot.delay(DELAY_AFTER_ACTION); + } + + /** + * Spustí Transfer automatizaci s konkrétním počtem pokémonů + * + * @param totalPokemonCount Celkový počet pokémonů k transferu + * @param delaySeconds Čekání mezi iteracemi (v sekundách) + */ + public void runWithCount(int totalPokemonCount, int delaySeconds) { + System.out.println("Spouštím Transfer automatizaci - Počet pokémonů: " + totalPokemonCount + ", Čekání: " + + delaySeconds + "s"); + + if (!findPokemonGoWindow()) { + System.err.println("Nepodařilo se najít okno Pokémon GO!"); + return; + } + + activateWindow(); + + System.out.println("Čekám 1 sekundu před začátkem..."); + robot.delay(1000); + + int transferredCount = 0; + + // Transferovat dokud nedosáhneme požadovaného počtu + while (transferredCount < totalPokemonCount) { + // Kontrola přerušení + if (Thread.currentThread().isInterrupted()) { + System.out.println("\n⚠️ Automatizace byla přerušena!"); + return; + } + + int pokemonThisRound = Math.min(9, totalPokemonCount - transferredCount); + + System.out.println("\n=== Iterace " + (transferredCount / 9 + 1) + " - Transferuji maximálně " + pokemonThisRound + + " pokémonů ==="); + + // Vybrat pokémony a získat skutečný počet vybraných + int actualTransferredCount = selectAllPokemonCount(pokemonThisRound); + + System.out.println("Čekám na TRANSFER..."); + clickTransferButton(); + + System.out.println("Čekám na INCLUDE dialog..."); + robot.delay(100); + clickIncludeButton(); + + System.out.println("Čekám na potvrzovací dialog..."); + robot.delay(100); + clickConfirmTransferButton(); + + transferredCount += actualTransferredCount; + transferredPokemonCount += actualTransferredCount; + + System.out.println("Transferováno v této iteraci: " + actualTransferredCount + " pokémonů (celkem: " + transferredCount + ")"); + + // Pokud ještě zbývá co transferovat, počkat + if (transferredCount < totalPokemonCount) { + System.out.println("Přestávka: " + delaySeconds + " sekund..."); + // Čekat s kontrolou přerušení + long endTime = System.currentTimeMillis() + (delaySeconds * 1000L); + while (System.currentTimeMillis() < endTime) { + if (Thread.currentThread().isInterrupted()) { + System.out.println("\n⚠️ Automatizace byla přerušena během přestávky!"); + return; + } + robot.delay(100); + } + } + } + + System.out.println("\n✅ Transfer automatizace dokončena! Transferováno: " + transferredCount + " pokémonů"); + } + + /** + * Označí konkrétní počet pokémonů + * @return Skutečný počet vybraných pokémonů + */ + private int selectAllPokemonCount(int count) { + System.out.println("Začínám označovat " + count + " pokémonů (pouze template matching)..."); + + List positions = detectPokemonsByTemplateMatching(); + + int maxPokemon = Math.min(count, positions.size()); + System.out.println("Budu označovat " + maxPokemon + " pokémonů..."); + + for (int i = 0; i < maxPokemon; i++) { + Point pos = positions.get(i); + System.out.println("Klikám na Pokémona " + (i + 1) + "/" + maxPokemon + " na pozici: " + pos); + + robot.mouseMove(pos.x, pos.y); + + if (i == 0) { + robot.delay(500); + System.out.println(" -> Dlouhé podržení (aktivace multi-select)"); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.delay(800); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.delay(DELAY_AFTER_ACTION); + } else { + System.out.println(" -> Normální klik"); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + } + + robot.delay(DELAY_BETWEEN_CLICKS); + } + + System.out.println("Označeno " + maxPokemon + " pokémonů!"); + System.out.println("Čekám před kliknutím na TRANSFER..."); + robot.delay(800); + + return maxPokemon; + } + + public static void main(String[] args) { + System.out.println("=== Pokémon GO Automatizační Nástroj ==="); + System.out.println("Ujistěte se, že je aplikace Pokémon GO spuštěná..."); + } +} diff --git a/src/main/java/com/pokemongo/PokemonGoGUI.java b/src/main/java/com/pokemongo/PokemonGoGUI.java new file mode 100644 index 0000000..f4e92e7 --- /dev/null +++ b/src/main/java/com/pokemongo/PokemonGoGUI.java @@ -0,0 +1,830 @@ +package com.pokemongo; + +import javax.swing.*; +import javax.swing.KeyStroke; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.WindowEvent; +import java.io.*; +import java.util.Properties; + +/** + * Jednoduchá GUI aplikace pro Pokémon GO automatizaci + * S kartami pro jednotlivé automatizace + */ +public class PokemonGoGUI extends JFrame { + + private JButton stopButton; + private JTextArea logArea; + private JLabel statusLabel; + private volatile boolean isRunning = false; + private volatile boolean shouldStop = false; + private PokemonGoAutomation automation; + private Thread automationThread; + private int totalTransferredCount = 0; // Celkový počet transfernutých pokémonů + + private volatile boolean autoClickRunning = false; + private GlobalHotkey globalHotkey; + + private static final String CONFIG_FILE = "pgo-automat-settings.properties"; + private static final String VERSION = "1.0.2"; + private Properties settings; + private JSpinner countSpinner; + private JSpinner delaySpinner; + + private static final Font TITLE_FONT = new Font("Segoe UI", Font.BOLD, 18); + private static final Font HEADER_FONT = new Font("Segoe UI", Font.BOLD, 12); + private static final Font BODY_FONT = new Font("Segoe UI", Font.PLAIN, 11); + private static final Font SMALL_FONT = new Font("Segoe UI", Font.PLAIN, 10); + private static final Color PANEL_BACKGROUND = new Color(248, 248, 252); + private static final Color CARD_BACKGROUND = Color.WHITE; + private static final Color CARD_BORDER_COLOR = new Color(210, 210, 210); + private static final Color PRIMARY_GREEN = new Color(76, 175, 80); + private static final Color PRIMARY_RED = new Color(244, 67, 54); + private static final Color ACCENT_BLUE = new Color(33, 150, 243); + private static final Color LOG_BORDER_COLOR = new Color(200, 200, 200); + private static final Color STATUS_DEFAULT = new Color(33, 150, 243); + private static final int CARD_MAX_WIDTH = 760; + + public PokemonGoGUI() { + setTitle("Pokémon GO Automatizace v" + VERSION); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + // Načtení uložených nastavení + loadSettings(); + + // Obnovení velikosti a pozice okna + int width = Integer.parseInt(settings.getProperty("window.width", "900")); + int height = Integer.parseInt(settings.getProperty("window.height", "680")); + int x = Integer.parseInt(settings.getProperty("window.x", "-1")); + int y = Integer.parseInt(settings.getProperty("window.y", "-1")); + + setSize(width, height); + + if (x >= 0 && y >= 0) { + setLocation(x, y); + } else { + setLocationRelativeTo(null); + } + + setResizable(true); + setBackground(PANEL_BACKGROUND); + + // Uložení nastavení při zavření okna + addWindowListener(new java.awt.event.WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + saveSettings(); + } + }); + + // Hlavní panel + JPanel mainPanel = new JPanel(new BorderLayout(10, 10)); + mainPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15)); + mainPanel.setBackground(PANEL_BACKGROUND); + + // Horní panel se stavem + JLabel stateLabel = new JLabel("Stav:"); + stateLabel.setFont(HEADER_FONT); + stateLabel.setForeground(new Color(70, 70, 70)); + statusLabel = new JLabel("Připraven k spuštění"); + statusLabel.setFont(BODY_FONT); + statusLabel.setForeground(STATUS_DEFAULT); + JLabel versionLabel = new JLabel("verze " + VERSION); + versionLabel.setFont(SMALL_FONT); + versionLabel.setForeground(new Color(120, 120, 120)); + + stopButton = new JButton("⏹ ZASTAVIT (CTRL+ALT+X)"); + stopButton.setPreferredSize(new Dimension(150, 30)); + styleButton(stopButton, PRIMARY_RED, Color.WHITE, SMALL_FONT); + stopButton.setEnabled(false); + stopButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + stopAutomation(); + } + }); + + JPanel stopPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + stopPanel.setOpaque(false); + stopPanel.add(stopButton); + + JLabel titleLabel = new JLabel("Pokémon GO Automatizace"); + titleLabel.setFont(TITLE_FONT); + titleLabel.setForeground(new Color(30, 30, 30)); + + JPanel titleRow = new JPanel(new BorderLayout()); + titleRow.setBackground(PANEL_BACKGROUND); + titleRow.add(titleLabel, BorderLayout.WEST); + titleRow.add(stopPanel, BorderLayout.EAST); + + JPanel statusRow = new JPanel(new FlowLayout(FlowLayout.LEFT, 8, 0)); + statusRow.setBackground(PANEL_BACKGROUND); + statusRow.add(stateLabel); + statusRow.add(statusLabel); + statusRow.add(versionLabel); + + JPanel headerPanel = new JPanel(); + headerPanel.setLayout(new BoxLayout(headerPanel, BoxLayout.Y_AXIS)); + headerPanel.setBackground(PANEL_BACKGROUND); + headerPanel.add(titleRow); + headerPanel.add(Box.createVerticalStrut(6)); + headerPanel.add(statusRow); + + // Panel s kartami automatizací + JPanel cardsContainer = new JPanel(new GridBagLayout()); + cardsContainer.setBackground(PANEL_BACKGROUND); + cardsContainer.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.weightx = 1.0; + gbc.anchor = GridBagConstraints.NORTH; + gbc.insets = new Insets(0, 0, 12, 0); + + // Karta 1: Transfer automatizace + JPanel transferCard = createAutomationCard( + "🔄 TRANSFER AUTOMATIZACE", + "Hledá Pokémony, označí je a stiskne Transfer", + null + ); + + // Najít START tlačítko - je v EAST pozici BorderLayoutu + JButton startBtnTransfer = findStartButton(transferCard); + + if (startBtnTransfer != null) { + startBtnTransfer.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + startTransferAutomation(transferCard); + } + }); + } + + gbc.gridy = 0; + cardsContainer.add(transferCard, gbc); + gbc.gridy++; + gbc.insets = new Insets(0, 0, 12, 0); + + // Karta 2: Autoklik + JPanel autoClickCard = createAutomationCard( + "🖱️ AUTOKLIK", + "Automaticky klikuje na zadanou pozici", + null + ); + + // Najít START tlačítko + JButton startBtnAutoClick = findStartButton(autoClickCard); + + if (startBtnAutoClick != null) { + startBtnAutoClick.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + startAutoClick(autoClickCard); + } + }); + } + + gbc.gridy++; + gbc.insets = new Insets(0, 0, 0, 0); + cardsContainer.add(autoClickCard, gbc); + gbc.gridy++; + gbc.weighty = 1.0; + JPanel spacer = new JPanel(); + spacer.setOpaque(false); + cardsContainer.add(spacer, gbc); + + JPanel automationPanel = new JPanel(new BorderLayout()); + automationPanel.setBackground(PANEL_BACKGROUND); + automationPanel.add(cardsContainer, BorderLayout.NORTH); + + JScrollPane automationScrollPane = new JScrollPane(automationPanel); + automationScrollPane.setBorder(null); + automationScrollPane.setBackground(PANEL_BACKGROUND); + automationScrollPane.getViewport().setBackground(PANEL_BACKGROUND); + + // Log area + logArea = new JTextArea(); + logArea.setEditable(false); + logArea.setFont(new Font("Consolas", Font.PLAIN, 11)); + logArea.setBackground(new Color(248, 248, 248)); + logArea.setForeground(new Color(45, 45, 45)); + logArea.setLineWrap(true); + logArea.setWrapStyleWord(true); + logArea.setMargin(new Insets(5, 5, 5, 5)); + + JScrollPane logScrollPane = new JScrollPane(logArea); + logScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); + logScrollPane.setBorder(BorderFactory.createTitledBorder( + BorderFactory.createLineBorder(LOG_BORDER_COLOR), + "Log", + 0, + 0, + HEADER_FONT, + new Color(85, 85, 85) + )); + logScrollPane.getViewport().setBackground(new Color(247, 247, 247)); + + JButton clearLogButton = new JButton("🗑 Vyčistit log"); + clearLogButton.setPreferredSize(new Dimension(140, 28)); + styleButton(clearLogButton, ACCENT_BLUE, Color.WHITE, SMALL_FONT); + clearLogButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + logArea.setText(""); + } + }); + + JPanel logButtonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 5, 5)); + logButtonPanel.setBackground(PANEL_BACKGROUND); + logButtonPanel.add(clearLogButton); + + JPanel logPanel = new JPanel(new BorderLayout(5, 5)); + logPanel.setBackground(new Color(247, 247, 247)); + logPanel.add(logScrollPane, BorderLayout.CENTER); + logPanel.add(logButtonPanel, BorderLayout.SOUTH); + + // Splitter mezi kartami a logem + JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, automationScrollPane, logPanel); + splitPane.setDividerSize(6); + splitPane.setResizeWeight(0.6); + splitPane.setContinuousLayout(true); + splitPane.setBorder(null); + splitPane.setBackground(PANEL_BACKGROUND); + + // Přidání do hlavního panelu + mainPanel.add(headerPanel, BorderLayout.NORTH); + mainPanel.add(splitPane, BorderLayout.CENTER); + + add(mainPanel); + + // Klávesová zkratka: Ctrl+Alt+Z pro zastavení + KeyStroke hotkey = KeyStroke.getKeyStroke(KeyEvent.VK_X, KeyEvent.CTRL_DOWN_MASK | KeyEvent.ALT_DOWN_MASK); + getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(hotkey, "stopAutomationAction"); + getRootPane().getActionMap().put("stopAutomationAction", new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + if (isRunning) { + stopAutomation(); + } + } + }); + + // Globální hotkey pro ESC (bonus, když okno není fokusované) + globalHotkey = new GlobalHotkey(new Runnable() { + @Override + public void run() { + if (isRunning) { + stopAutomation(); + } + } + }); + mainPanel.add(globalHotkey, BorderLayout.SOUTH); + + // Přesměrování System.out a System.err do GUI + redirectSystemStreams(); + + logArea.append("=== Pokémon GO Automatizace ===\n\n"); + logArea.append("Vyberte jednu z automatizací a klikněte na tlačítko START.\n"); + logArea.append("Ujistěte se, že je aplikace Pokémon GO spuštěná.\n"); + logArea.append("Pro zastavení stiskněte Ctrl+Alt+X nebo klikněte ZASTAVIT.\n\n"); + } + + /** + * Vytvoří kartu automatizace s nastavením + */ + private JPanel createAutomationCard(String title, String description, ActionListener startAction) { + JPanel card = new JPanel(new BorderLayout(12, 12)); + card.setBackground(CARD_BACKGROUND); + card.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createLineBorder(CARD_BORDER_COLOR, 1), + BorderFactory.createEmptyBorder(8, 8, 8, 8) + )); + card.setMaximumSize(new Dimension(CARD_MAX_WIDTH, 160)); + card.setPreferredSize(new Dimension(CARD_MAX_WIDTH, 160)); + card.setAlignmentX(Component.CENTER_ALIGNMENT); + JPanel infoPanel = new JPanel(); + infoPanel.setLayout(new BoxLayout(infoPanel, BoxLayout.Y_AXIS)); + infoPanel.setBackground(CARD_BACKGROUND); + infoPanel.setOpaque(true); + infoPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + + JLabel titleLabel = new JLabel(title); + titleLabel.setFont(HEADER_FONT); + titleLabel.setForeground(new Color(28, 28, 28)); + + JLabel descLabel = new JLabel("" + description + ""); + descLabel.setFont(SMALL_FONT); + descLabel.setForeground(new Color(115, 115, 115)); + + infoPanel.add(titleLabel); + infoPanel.add(Box.createVerticalStrut(3)); + infoPanel.add(descLabel); + infoPanel.add(Box.createVerticalGlue()); + + JPanel centerPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 12, 6)); + centerPanel.setBackground(CARD_BACKGROUND); + centerPanel.setOpaque(true); + + if (title.contains("TRANSFER")) { + countSpinner = new JSpinner(new SpinnerNumberModel( + Integer.parseInt(settings.getProperty("transfer.count", "9")), + 1, 999, 1 + )); + countSpinner.setPreferredSize(new Dimension(64, 26)); + delaySpinner = new JSpinner(new SpinnerNumberModel( + Integer.parseInt(settings.getProperty("transfer.delay", "2")), + 0, 60, 1 + )); + delaySpinner.setPreferredSize(new Dimension(64, 26)); + + card.putClientProperty("countSpinner", countSpinner); + card.putClientProperty("delaySpinner", delaySpinner); + + centerPanel.add(createFieldPanel("Počet:", countSpinner)); + centerPanel.add(createFieldPanel("Čekání (s):", delaySpinner)); + } else if (title.contains("AUTOKLIK")) { + JSpinner xSpinner = new JSpinner(new SpinnerNumberModel( + Integer.parseInt(settings.getProperty("autoklik.x", "960")), + 0, 9999, 1 + )); + xSpinner.setPreferredSize(new Dimension(70, 26)); + card.putClientProperty("xSpinner", xSpinner); + + JSpinner ySpinner = new JSpinner(new SpinnerNumberModel( + Integer.parseInt(settings.getProperty("autoklik.y", "540")), + 0, 9999, 1 + )); + ySpinner.setPreferredSize(new Dimension(70, 26)); + card.putClientProperty("ySpinner", ySpinner); + + JSpinner intervalSpinner = new JSpinner(new SpinnerNumberModel( + Integer.parseInt(settings.getProperty("autoklik.interval", "100")), + 10, 10000, 10 + )); + intervalSpinner.setPreferredSize(new Dimension(70, 26)); + card.putClientProperty("intervalSpinner", intervalSpinner); + + JSpinner clickCountSpinner = new JSpinner(new SpinnerNumberModel( + Integer.parseInt(settings.getProperty("autoklik.count", "10")), + 1, 10000, 1 + )); + clickCountSpinner.setPreferredSize(new Dimension(70, 26)); + card.putClientProperty("clickCountSpinner", clickCountSpinner); + + centerPanel.add(createFieldPanel("X:", xSpinner)); + centerPanel.add(createFieldPanel("Y:", ySpinner)); + centerPanel.add(createFieldPanel("Interval (ms):", intervalSpinner)); + centerPanel.add(createFieldPanel("Počet kliknutí:", clickCountSpinner)); + + JButton pickPositionBtn = new JButton("Nastavit"); + pickPositionBtn.setPreferredSize(new Dimension(80, 26)); + styleButton(pickPositionBtn, ACCENT_BLUE, Color.WHITE, SMALL_FONT); + pickPositionBtn.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + PositionPicker picker = new PositionPicker(null); + + new Thread(new Runnable() { + @Override + public void run() { + picker.setVisible(true); + + while (picker.isVisible()) { + try { + Thread.sleep(50); + } catch (InterruptedException ex) { + break; + } + } + + if (picker.isPositionSelected()) { + Point pos = picker.getSelectedPosition(); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + xSpinner.setValue(pos.x); + ySpinner.setValue(pos.y); + logArea.append("✅ (" + pos.x + ", " + pos.y + ")\n"); + } + }); + } + } + }).start(); + } + }); + centerPanel.add(createFieldPanel("Pozice:", pickPositionBtn)); + } + + // Pravý panel - START tlačítko + JPanel rightPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 2, 0)); + rightPanel.setBackground(CARD_BACKGROUND); + rightPanel.setBorder(BorderFactory.createEmptyBorder(4, 0, 0, 0)); + + JButton startBtn = new JButton("▶ START"); + startBtn.setPreferredSize(new Dimension(90, 32)); + styleButton(startBtn, PRIMARY_GREEN, Color.WHITE, SMALL_FONT); + startBtn.addActionListener(startAction); + + rightPanel.add(startBtn); + + // Komponování karty: info nahoře, nastavení uprostřed, tlačítko dole + card.add(infoPanel, BorderLayout.NORTH); + card.add(centerPanel, BorderLayout.CENTER); + card.add(rightPanel, BorderLayout.SOUTH); + + return card; + } + + private JPanel createFieldPanel(String labelText, JComponent field) { + JLabel label = new JLabel(labelText); + label.setFont(BODY_FONT); + label.setForeground(new Color(96, 96, 96)); + field.setFont(BODY_FONT); + if (field instanceof JSpinner) { + ((JSpinner) field).setBorder(BorderFactory.createLineBorder(CARD_BORDER_COLOR)); + field.setBackground(new Color(252, 252, 252)); + } + JPanel wrapper = new JPanel(new FlowLayout(FlowLayout.LEFT, 4, 2)); + wrapper.setBackground(CARD_BACKGROUND); + wrapper.add(label); + wrapper.add(field); + return wrapper; + } + + private void styleButton(JButton button, Color background, Color foreground, Font font) { + button.setFont(font); + button.setBackground(background); + button.setForeground(foreground); + button.setFocusPainted(false); + button.setBorder(BorderFactory.createEmptyBorder(4, 10, 4, 10)); + } + + private JButton findStartButton(Container container) { + for (Component comp : container.getComponents()) { + if (comp instanceof JButton) { + JButton button = (JButton) comp; + if ("▶ START".equals(button.getText())) { + return button; + } + } + if (comp instanceof Container) { + JButton nested = findStartButton((Container) comp); + if (nested != null) { + return nested; + } + } + } + return null; + } + + /** + * Přesměruje System.out a System.err do log area + */ + private void redirectSystemStreams() { + System.setOut(new java.io.PrintStream(System.out) { + @Override + public void println(String x) { + super.println(x); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + logArea.append(x + "\n"); + logArea.setCaretPosition(logArea.getDocument().getLength()); + } + }); + } + }); + + System.setErr(new java.io.PrintStream(System.err) { + @Override + public void println(String x) { + super.println(x); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + logArea.append("[CHYBA] " + x + "\n"); + logArea.setCaretPosition(logArea.getDocument().getLength()); + } + }); + } + }); + } + + /** + * Spustí Transfer automatizaci + */ + private void startTransferAutomation(JPanel transferCard) { + if (isRunning) { + JOptionPane.showMessageDialog(this, "Automatizace již běží!", "Upozornění", JOptionPane.WARNING_MESSAGE); + return; + } + + // Získat počet pokémonů a čekání z GUI + JSpinner countSpinner = (JSpinner) transferCard.getClientProperty("countSpinner"); + JSpinner delaySpinner = (JSpinner) transferCard.getClientProperty("delaySpinner"); + + int pokemonCount = (Integer) countSpinner.getValue(); + int delaySeconds = (Integer) delaySpinner.getValue(); + + stopButton.setEnabled(true); + statusLabel.setText("Transfer automatizace běží..."); + statusLabel.setForeground(new Color(255, 152, 0)); + isRunning = true; + + logArea.append("\n========================================\n"); + logArea.append("SPOUŠTĚNÍ TRANSFER AUTOMATIZACE\n"); + logArea.append("Počet pokémonů: " + pokemonCount + ", Čekání: " + delaySeconds + "s\n"); + logArea.append("========================================\n\n"); + + shouldStop = false; + automationThread = new Thread(new Runnable() { + @Override + public void run() { + try { + automation = new PokemonGoAutomation(); + automation.resetTransferredCount(); + + // Spustit vlákno pro aktualizaci statusu + Thread statusUpdateThread = new Thread(new Runnable() { + @Override + public void run() { + while (isRunning && !shouldStop && !Thread.currentThread().isInterrupted()) { + try { + Thread.sleep(500); // Aktualizovat každých 500ms + final int transferred = automation.getTransferredCount(); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + if (isRunning) { + statusLabel.setText("Transfer běží... Transferováno: " + transferred + "/" + pokemonCount); + } + } + }); + } catch (InterruptedException e) { + break; + } + } + } + }); + statusUpdateThread.setDaemon(true); + statusUpdateThread.start(); + + if (!shouldStop) { + automation.runWithCount(pokemonCount, delaySeconds); + totalTransferredCount += automation.getTransferredCount(); + } + + if (!shouldStop) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + statusLabel.setText("Automatizace dokončena! Celkem transferováno: " + totalTransferredCount); + statusLabel.setForeground(new Color(76, 175, 80)); + logArea.append("\n✅ Transfer automatizace úspěšně dokončena! Transferováno: " + automation.getTransferredCount() + " pokémonů\n"); + } + }); + } + } catch (Exception e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + statusLabel.setText("Chyba!"); + statusLabel.setForeground(new Color(244, 67, 54)); + logArea.append("\n❌ Chyba: " + e.getMessage() + "\n"); + e.printStackTrace(); + } + }); + } finally { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + isRunning = false; + stopButton.setEnabled(false); + } + }); + } + } + }); + + automationThread.start(); + } + + /** + * Spustí autoklik + */ + private void startAutoClick(JPanel autoClickCard) { + if (isRunning) { + JOptionPane.showMessageDialog(this, "Automatizace již běží!", "Upozornění", JOptionPane.WARNING_MESSAGE); + return; + } + + // Načtení nastavení + JSpinner xSpinner = (JSpinner) autoClickCard.getClientProperty("xSpinner"); + JSpinner ySpinner = (JSpinner) autoClickCard.getClientProperty("ySpinner"); + JSpinner intervalSpinner = (JSpinner) autoClickCard.getClientProperty("intervalSpinner"); + JSpinner clickCountSpinner = (JSpinner) autoClickCard.getClientProperty("clickCountSpinner"); + + int x = (Integer) xSpinner.getValue(); + int y = (Integer) ySpinner.getValue(); + int interval = (Integer) intervalSpinner.getValue(); + int clickCount = (Integer) clickCountSpinner.getValue(); + + // Uložení nastavení + settings.setProperty("autoklik.x", String.valueOf(x)); + settings.setProperty("autoklik.y", String.valueOf(y)); + settings.setProperty("autoklik.interval", String.valueOf(interval)); + settings.setProperty("autoklik.count", String.valueOf(clickCount)); + saveSettings(); + + stopButton.setEnabled(true); + statusLabel.setText("Autoklik běží... (" + x + ", " + y + ")"); + statusLabel.setForeground(new Color(255, 152, 0)); + isRunning = true; + autoClickRunning = true; + shouldStop = false; + + logArea.append("\n========================================\n"); + logArea.append("SPOUŠTĚNÍ AUTOKLIKERU\n"); + logArea.append("Pozice: (" + x + ", " + y + ")\n"); + logArea.append("Interval: " + interval + "ms, Počet kliknutí: " + clickCount + "\n"); + logArea.append("========================================\n\n"); + + automationThread = new Thread(new Runnable() { + @Override + public void run() { + try { + java.awt.Robot robot = new java.awt.Robot(); + + logArea.append("Spouštím autoklik...\n"); + + for (int i = 0; i < clickCount && autoClickRunning && !shouldStop; i++) { + // Přesunutí kurzoru na pozici + robot.mouseMove(x, y); + Thread.sleep(10); + + // Klik + robot.mousePress(java.awt.event.InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(java.awt.event.InputEvent.BUTTON1_DOWN_MASK); + + // Čekání mezi kliknutími + if (i < clickCount - 1 && !shouldStop) { + Thread.sleep(interval); + } + + // Update progress každých 10 kliknutí + if ((i + 1) % 10 == 0 && !shouldStop) { + int progress = i + 1; + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + logArea.append(" Kliknutí: " + progress + "/" + clickCount + "\n"); + } + }); + } + } + + if (!shouldStop) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + statusLabel.setText("Autoklik úspěšně dokončen!"); + statusLabel.setForeground(new Color(76, 175, 80)); + logArea.append("\n✅ Autoklik úspěšně dokončen! Celkem kliknutí: " + clickCount + "\n"); + } + }); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } catch (Exception e) { + if (!shouldStop) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + statusLabel.setText("Chyba!"); + statusLabel.setForeground(new Color(244, 67, 54)); + logArea.append("\n❌ Chyba: " + e.getMessage() + "\n"); + e.printStackTrace(); + } + }); + } + } finally { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + isRunning = false; + autoClickRunning = false; + stopButton.setEnabled(false); + } + }); + } + } + }); + + automationThread.start(); + } + + /** + * Zastaví automatizaci + */ + private void stopAutomation() { + shouldStop = true; + autoClickRunning = false; + isRunning = false; + stopButton.setEnabled(false); + + if (automationThread != null && automationThread.isAlive()) { + automationThread.interrupt(); + // Čekej max 2 sekundy na ukončení + try { + automationThread.join(2000); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + logArea.append("\n⚠️ Automatizace byla přerušena uživatelem\n"); + statusLabel.setText("Přerušeno"); + statusLabel.setForeground(new Color(244, 67, 54)); + } + }); + } + + /** + * Načte uložená nastavení ze souboru + */ + private void loadSettings() { + settings = new Properties(); + File configFile = new File(CONFIG_FILE); + + if (configFile.exists()) { + try (java.io.FileInputStream fis = new java.io.FileInputStream(configFile)) { + settings.load(fis); + } catch (IOException e) { + System.err.println("Chyba při načítání nastavení: " + e.getMessage()); + } + } + + // Výchozí hodnoty, pokud nastavení neexistují + if (!settings.containsKey("transfer.count")) { + settings.setProperty("transfer.count", "9"); + } + if (!settings.containsKey("transfer.delay")) { + settings.setProperty("transfer.delay", "2"); + } + if (!settings.containsKey("window.width")) { + settings.setProperty("window.width", "700"); + } + if (!settings.containsKey("window.height")) { + settings.setProperty("window.height", "600"); + } + if (!settings.containsKey("window.x")) { + settings.setProperty("window.x", "-1"); + } + if (!settings.containsKey("window.y")) { + settings.setProperty("window.y", "-1"); + } + } + + /** + * Uloží aktuální nastavení do souboru + */ + private void saveSettings() { + // Uložení hodnot z spinnerů + if (countSpinner != null) { + settings.setProperty("transfer.count", countSpinner.getValue().toString()); + } + if (delaySpinner != null) { + settings.setProperty("transfer.delay", delaySpinner.getValue().toString()); + } + + // Uložení velikosti a pozice okna + settings.setProperty("window.width", String.valueOf(getWidth())); + settings.setProperty("window.height", String.valueOf(getHeight())); + settings.setProperty("window.x", String.valueOf(getX())); + settings.setProperty("window.y", String.valueOf(getY())); + + try (java.io.FileOutputStream fos = new java.io.FileOutputStream(CONFIG_FILE)) { + settings.store(fos, "Pokémon GO Automatizace - Nastavení"); + } catch (IOException e) { + System.err.println("Chyba při ukládání nastavení: " + e.getMessage()); + } + } + + + public static void main(String[] args) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + PokemonGoGUI frame = new PokemonGoGUI(); + frame.setVisible(true); + } + }); + } +} diff --git a/src/main/java/com/pokemongo/PositionPicker.java b/src/main/java/com/pokemongo/PositionPicker.java new file mode 100644 index 0000000..c92cb3c --- /dev/null +++ b/src/main/java/com/pokemongo/PositionPicker.java @@ -0,0 +1,95 @@ +package com.pokemongo; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; + +public class PositionPicker extends JWindow { + private Point selectedPosition = null; + private boolean positionSelected = false; + private PositionPickerListener listener = null; + + public interface PositionPickerListener { + void onPositionSelected(Point position); + void onCancelled(); + } + + public PositionPicker(PositionPickerListener listener) { + this.listener = listener; + GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); + Rectangle bounds = gd.getDefaultConfiguration().getBounds(); + setBounds(bounds); + + System.out.println("PositionPicker vytvořen: " + bounds); + + setFocusable(true); + setBackground(new Color(0, 0, 0, 0)); + + // Přidáme panel pro vykreslování + JPanel panel = new JPanel() { + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + Graphics2D g2d = (Graphics2D) g; + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + // Lehké šedé pozadí + g2d.setColor(new Color(100, 100, 100, 10)); + g2d.fillRect(0, 0, getWidth(), getHeight()); + + // Instrukce + g2d.setColor(new Color(0, 0, 0, 200)); + g2d.fillRect(10, 10, 400, 60); + + g2d.setColor(new Color(255, 255, 0, 255)); + g2d.setFont(new Font("Monospaced", Font.BOLD, 12)); + g2d.drawString("VYBERTE POZICI - Klikněte na místo kde chcete klikat", 20, 30); + g2d.drawString("ESC: Zrušit", 20, 50); + } + }; + panel.setOpaque(false); + panel.setBackground(new Color(0, 0, 0, 0)); + setContentPane(panel); + + panel.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + selectedPosition = new Point(e.getX(), e.getY()); + positionSelected = true; + System.out.println("✅ Pozice vybrána: " + selectedPosition); + if (listener != null) { + listener.onPositionSelected(selectedPosition); + } + dispose(); + } + }); + + panel.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { + System.out.println("ESC - Výběr zrušen"); + positionSelected = false; + if (listener != null) { + listener.onCancelled(); + } + dispose(); + } + } + }); + + panel.setFocusable(true); + panel.requestFocus(); + } + + public Point getSelectedPosition() { + return selectedPosition; + } + + public boolean isPositionSelected() { + return positionSelected; + } +} diff --git a/src/main/resources/include.png b/src/main/resources/include.png new file mode 100644 index 0000000..c86caa4 Binary files /dev/null and b/src/main/resources/include.png differ diff --git a/src/main/resources/pok1.png b/src/main/resources/pok1.png new file mode 100644 index 0000000..e4a27c1 Binary files /dev/null and b/src/main/resources/pok1.png differ diff --git a/src/main/resources/ps-end/ps-end.png b/src/main/resources/ps-end/ps-end.png new file mode 100644 index 0000000..7796029 Binary files /dev/null and b/src/main/resources/ps-end/ps-end.png differ diff --git a/src/main/resources/ps-end/ps-end2.png b/src/main/resources/ps-end/ps-end2.png new file mode 100644 index 0000000..7b417d4 Binary files /dev/null and b/src/main/resources/ps-end/ps-end2.png differ diff --git a/src/main/resources/ps-end/ps-end3.png b/src/main/resources/ps-end/ps-end3.png new file mode 100644 index 0000000..2e72775 Binary files /dev/null and b/src/main/resources/ps-end/ps-end3.png differ diff --git a/src/main/resources/ps/Screenshot 2025-10-03 164033.png b/src/main/resources/ps/Screenshot 2025-10-03 164033.png new file mode 100755 index 0000000..ba36887 Binary files /dev/null and b/src/main/resources/ps/Screenshot 2025-10-03 164033.png differ diff --git a/src/main/resources/ps/Screenshot 2025-10-03 164048.png b/src/main/resources/ps/Screenshot 2025-10-03 164048.png new file mode 100755 index 0000000..b2fc897 Binary files /dev/null and b/src/main/resources/ps/Screenshot 2025-10-03 164048.png differ diff --git a/src/main/resources/ps/Screenshot 2025-10-03 164057.png b/src/main/resources/ps/Screenshot 2025-10-03 164057.png new file mode 100755 index 0000000..d69ed70 Binary files /dev/null and b/src/main/resources/ps/Screenshot 2025-10-03 164057.png differ diff --git a/src/main/resources/ps/Screenshot 2025-10-03 164107.png b/src/main/resources/ps/Screenshot 2025-10-03 164107.png new file mode 100755 index 0000000..fce42f1 Binary files /dev/null and b/src/main/resources/ps/Screenshot 2025-10-03 164107.png differ diff --git a/src/main/resources/ps/Screenshot 2025-10-03 164119.png b/src/main/resources/ps/Screenshot 2025-10-03 164119.png new file mode 100755 index 0000000..216c22b Binary files /dev/null and b/src/main/resources/ps/Screenshot 2025-10-03 164119.png differ diff --git a/src/main/resources/ps/Screenshot 2025-10-03 164207.png b/src/main/resources/ps/Screenshot 2025-10-03 164207.png new file mode 100755 index 0000000..3a9ce12 Binary files /dev/null and b/src/main/resources/ps/Screenshot 2025-10-03 164207.png differ diff --git a/src/main/resources/ps/Screenshot 2025-10-03 164638.png b/src/main/resources/ps/Screenshot 2025-10-03 164638.png new file mode 100755 index 0000000..3c25c11 Binary files /dev/null and b/src/main/resources/ps/Screenshot 2025-10-03 164638.png differ diff --git a/src/main/resources/ps/Screenshot 2025-10-03 164654.png b/src/main/resources/ps/Screenshot 2025-10-03 164654.png new file mode 100755 index 0000000..11bc693 Binary files /dev/null and b/src/main/resources/ps/Screenshot 2025-10-03 164654.png differ diff --git a/src/main/resources/t1.png b/src/main/resources/t1.png new file mode 100644 index 0000000..c34f2ec Binary files /dev/null and b/src/main/resources/t1.png differ diff --git a/src/main/resources/t2.png b/src/main/resources/t2.png new file mode 100644 index 0000000..72041a1 Binary files /dev/null and b/src/main/resources/t2.png differ