package it.paspiz85.nanobot.game; import it.paspiz85.nanobot.exception.BotBadBaseException; import it.paspiz85.nanobot.exception.BotException; import it.paspiz85.nanobot.util.Area; import it.paspiz85.nanobot.util.Point; import it.paspiz85.nanobot.util.Utils; import java.awt.Color; import java.awt.Polygon; import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.logging.Level; import java.util.stream.Stream; import javax.imageio.ImageIO; import org.sikuli.core.search.RegionMatch; import org.sikuli.core.search.algorithm.TemplateMatcher; /** * Attack mode screen. * * @author paspiz85 * */ public class AttackScreen extends Screen { private static final Area AREA_NEXT_BUTTON = Area.byEdges(692, 488, 739, 547); private static final Point BUTTON_END_BATTLE = getPoint("point.button.end_battle"); private static final Point BUTTON_END_BATTLE_QUESTION_OK = getPoint("point.button.end_battle.question_ok"); private static final Color DARKCHECK_COLOR_NO = getColor("darkcheck.color.no"); private static final Color DARKCHECK_COLOR_YES = getColor("darkcheck.color.yes"); private static final Point DARKCHECK_POINT = getPoint("darkcheck.point"); private static final Area ENEMY_BASE = Area.byEdges(31, 0, 831, 510); private static final Point ENEMY_BASE_BOTTOM = new Point(400, 597); private static final Point ENEMY_BASE_LEFT = new Point(13, 313); private static final Polygon ENEMY_BASE_POLY = new Polygon(); private static final Point ENEMY_BASE_RIGHT = new Point(779, 312); private static final Point ENEMY_BASE_TOP = new Point(401, 16); protected static final Area ENEMY_LOOT = Area.byEdges(17, 68, 138, 240); private static final Point POINT_DARK_ELIXIR = new Point(33, 57 + 2); private static final Point POINT_ELIXIR_HAS_DARK = new Point(33, 29 + 2); private static final Point POINT_ELIXIR_HASNT_DARK = new Point(33, 30); private static final Point POINT_GOLD_HAS_DARK = getPoint("point.gold.hasdark"); private static final Point POINT_GOLD_HASNT_DARK = new Point(33, 1); private static final Point POINT_TROPHY_WIN_HAS_DARK = new Point(33, 90); private static final Point POINT_TROPHY_WIN_HASNT_DARK = new Point(33, 62); private Point buttonNext; AttackScreen() { ENEMY_BASE_POLY.addPoint(ENEMY_BASE_LEFT.x(), ENEMY_BASE_LEFT.y()); ENEMY_BASE_POLY.addPoint(ENEMY_BASE_TOP.x(), ENEMY_BASE_TOP.y()); ENEMY_BASE_POLY.addPoint(ENEMY_BASE_RIGHT.x(), ENEMY_BASE_RIGHT.y()); ENEMY_BASE_POLY.addPoint(ENEMY_BASE_BOTTOM.x(), ENEMY_BASE_BOTTOM.y()); } private int countAttackableElixirs(final List<RegionMatch> doFindAll, final List<Rectangle> matchedElixirs, final Path next) { int attackableElixirs = 0; int c = 0; RECT_LOOP: for (final RegionMatch i : doFindAll) { // if matched area is out of enemy poly if (!ENEMY_BASE_POLY.contains(i.x, i.y)) { continue; } // check if it's an existing match for (final Rectangle r : matchedElixirs) { if (r.intersects(i.getBounds())) { break RECT_LOOP; } } c++; matchedElixirs.add(i.getBounds()); if (next.getFileName().toString().startsWith("empty")) { attackableElixirs--; } else if (next.getFileName().toString().startsWith("full")) { attackableElixirs++; } logger.finest("\t" + i.getBounds() + " score: " + i.getScore()); } if (c > 0) { logger.finest(String.format("\tfound %d elixirs matching %s\n", c, next.getFileName().toString())); } return attackableElixirs; } public Point getButtonAttackUnit(final int x) { Point result; switch (x) { case 1: result = new Point(72, 600); break; case 2: result = new Point(145, 600); break; case 3: result = new Point(217, 600); break; case 4: result = new Point(288, 600); break; case 5: result = new Point(361, 600); break; case 6: result = new Point(435, 600); break; case 7: result = new Point(505, 600); break; default: throw new IllegalArgumentException(x + ""); } return result; } public Point getButtonEndBattle() { return BUTTON_END_BATTLE; } public Point getButtonEndBattleQuestionOK() { return BUTTON_END_BATTLE_QUESTION_OK; } public Point getButtonNext() { if (buttonNext == null) { searchButtonNext(true); } return buttonNext; } private boolean hasDE(final BufferedImage image) throws BotBadBaseException { final int rgb = image.getRGB(DARKCHECK_POINT.x(), DARKCHECK_POINT.y()); final Color deCheck = new Color(rgb); boolean result; if (platform.compareColor(deCheck, DARKCHECK_COLOR_YES, 7)) { result = true; } else if (platform.compareColor(deCheck, DARKCHECK_COLOR_NO, 7)) { result = false; } else { throw new BotBadBaseException("de: " + Integer.toHexString(deCheck.getRGB())); } return result; } public Boolean isCollectorFullBase() throws BotException { final int[] attackableElixirs = { 0 }; final BufferedImage image = platform.screenshot(ENEMY_BASE); try { final URI uri = getClass().getResource("elixirs").toURI(); Utils.withClasspathFolder( uri, (path) -> { final List<Rectangle> matchedElixirs = new ArrayList<>(); try (Stream<Path> walk = Files.walk(path, 1)) { for (final Iterator<Path> it = walk.iterator(); it.hasNext();) { final Path next = it.next(); if (Files.isDirectory(next)) { continue; } final BufferedImage tar = ImageIO.read(Files.newInputStream(next, StandardOpenOption.READ)); final List<RegionMatch> doFindAll = TemplateMatcher .findMatchesByGrayscaleAtOriginalResolution(image, tar, 7, 0.8); attackableElixirs[0] += countAttackableElixirs(doFindAll, matchedElixirs, next); } } catch (final IOException e) { logger.log(Level.SEVERE, e.getMessage(), e); } }); } catch (final URISyntaxException e) { logger.log(Level.SEVERE, e.getMessage(), e); } return attackableElixirs[0] >= 0; } @Override public boolean isDisplayed() { return isDisplayedByImageSearch(this::searchButtonNext); } protected final Integer parseDarkElixir(final BufferedImage image) throws BotBadBaseException { Integer result = null; if (hasDE(image)) { result = parseNumber(image, 2, POINT_DARK_ELIXIR, image.getWidth() - 43); } return result; } protected final Integer parseElixir(final BufferedImage image) throws BotBadBaseException { return parseNumber(image, 1, hasDE(image) ? POINT_ELIXIR_HAS_DARK : POINT_ELIXIR_HASNT_DARK, image.getWidth() - 43); } public EnemyInfo parseEnemyInfo() throws BotBadBaseException { final BufferedImage image = platform.screenshot(ENEMY_LOOT); final EnemyInfo info = new EnemyInfo(); info.setGold(parseGold(image)); info.setElixir(parseElixir(image)); info.setDarkElixir(parseDarkElixir(image)); info.setTrophyWin(parseTrophyWin(image)); info.setTrophyDefeat(parseTrophyDefeat(image)); return info; } protected final Integer parseGold(final BufferedImage image) throws BotBadBaseException { return parseNumber(image, 0, hasDE(image) ? POINT_GOLD_HAS_DARK : POINT_GOLD_HASNT_DARK, image.getWidth() - 43); } protected final Integer parseTrophyDefeat(final BufferedImage image) throws BotBadBaseException { // TODO implement trophyDefeat return null; } protected final Integer parseTrophyWin(final BufferedImage image) throws BotBadBaseException { // TODO implement trophyWin return null; /* * return parseNumber(image, 3, hasDE(image) ? POINT_TROPHY_WIN_HAS_DARK * : POINT_TROPHY_WIN_HASNT_DARK, image.getWidth() - 43); */ } private void searchButtonNext(boolean debug) { buttonNext = searchImage(getClass().getResource("button_next.png"), AREA_NEXT_BUTTON, debug); } }