package me.corriekay.pokegoutil.windows; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.StringSelection; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.function.BiConsumer; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.ListCellRenderer; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.apache.commons.lang3.mutable.MutableInt; import com.pokegoapi.api.PokemonGo; import com.pokegoapi.api.map.pokemon.EvolutionResult; import com.pokegoapi.api.player.PlayerProfile.Currency; import com.pokegoapi.api.pokemon.Pokemon; import com.pokegoapi.exceptions.CaptchaActiveException; import com.pokegoapi.exceptions.LoginFailedException; import com.pokegoapi.exceptions.RemoteServerException; import com.pokegoapi.exceptions.hash.HashException; import me.corriekay.pokegoutil.data.enums.BatchOperation; import me.corriekay.pokegoutil.data.enums.PokeColumn; import me.corriekay.pokegoutil.utils.ConfigKey; import me.corriekay.pokegoutil.utils.ConfigNew; import me.corriekay.pokegoutil.utils.StringLiterals; import me.corriekay.pokegoutil.utils.Utilities; import me.corriekay.pokegoutil.utils.helpers.LDocumentListener; import me.corriekay.pokegoutil.utils.helpers.LocationHelper; import me.corriekay.pokegoutil.utils.pokemon.PokeHandler; import me.corriekay.pokegoutil.utils.pokemon.PokeHandler.ReplacePattern; import me.corriekay.pokegoutil.utils.pokemon.PokeNick; import me.corriekay.pokegoutil.utils.pokemon.PokemonCalculationUtils; import me.corriekay.pokegoutil.utils.pokemon.PokemonUtils; import me.corriekay.pokegoutil.utils.ui.GhostText; import me.corriekay.pokegoutil.utils.windows.PokemonTable; import me.corriekay.pokegoutil.utils.windows.PokemonTableModel; import POGOProtos.Enums.PokemonFamilyIdOuterClass.PokemonFamilyId; import POGOProtos.Enums.PokemonIdOuterClass.PokemonId; import POGOProtos.Networking.Responses.NicknamePokemonResponseOuterClass.NicknamePokemonResponse; import POGOProtos.Networking.Responses.ReleasePokemonResponseOuterClass.ReleasePokemonResponse; import POGOProtos.Networking.Responses.SetFavoritePokemonResponseOuterClass.SetFavoritePokemonResponse; import POGOProtos.Networking.Responses.UpgradePokemonResponseOuterClass.UpgradePokemonResponse; /** * The main PokemonTab. */ @SuppressWarnings("serial") public class PokemonTab extends JPanel { private final PokemonGo go; private final PokemonTable pt; private static final JTextField searchBar = new JTextField(""); private static final JTextField ivTransfer = new JTextField("", 20); private static final ConfigNew config = ConfigNew.getConfig(); // Used constants private static final int WHEN_TO_SHOW_SELECTION_TITLE = 2; private static final String GYM_SKIPPED_MESSAGE_UNFORMATTED = "%s with %d CP is in gym, skipping."; private static final int POPUP_WIDTH = 500; private static final int POPUP_HEIGHT = 400; private static final int MIN_FONT_SIZE = 2; /** * Creates an instance of the PokemonTab. * * @param go The go api class. */ public PokemonTab(final PokemonGo go) { super(); setLayout(new BorderLayout()); this.go = go; pt = new PokemonTable(go); final JPanel topPanel = new JPanel(new GridBagLayout()); final JButton refreshPkmn = new JButton("Refresh List"), renameSelected = new JButton(BatchOperation.RENAME.toString()), transferSelected = new JButton(BatchOperation.TRANSFER.toString()), evolveSelected = new JButton(BatchOperation.EVOLVE.toString()), powerUpSelected = new JButton(BatchOperation.POWER_UP.toString()), toggleFavorite = new JButton(BatchOperation.FAVORITE.toString()); pt.getSelectionModel().addListSelectionListener(event -> { if (event.getValueIsAdjusting()) { // We need a break here. Cause otherwise mouse selection would trigger twice. (Yeah, that's swing) return; } if (event.getSource() == pt.getSelectionModel() && pt.getRowSelectionAllowed()) { final int selectedRows = pt.getSelectedRowCount(); if (selectedRows >= WHEN_TO_SHOW_SELECTION_TITLE) { PokemonGoMainWindow.getInstance().setTitle(selectedRows + " Pokémon selected"); } else { PokemonGoMainWindow.getInstance().refreshTitle(); } } }); final GridBagConstraints gbc = new GridBagConstraints(); topPanel.add(refreshPkmn, gbc); refreshPkmn.addActionListener(l -> new SwingWorker<Void, Void>() { @Override protected Void doInBackground() { refreshPkmn(); return null; } }.execute()); topPanel.add(renameSelected, gbc); renameSelected.addActionListener(l -> new SwingWorker<Void, Void>() { @Override protected Void doInBackground() { renameSelected(); return null; } }.execute()); topPanel.add(transferSelected, gbc); transferSelected.addActionListener(l -> new SwingWorker<Void, Void>() { @Override protected Void doInBackground() { transferSelected(); return null; } }.execute()); topPanel.add(evolveSelected, gbc); evolveSelected.addActionListener(l -> new SwingWorker<Void, Void>() { @Override protected Void doInBackground() { evolveSelected(); return null; } }.execute()); topPanel.add(powerUpSelected, gbc); powerUpSelected.addActionListener(l -> new SwingWorker<Void, Void>() { @Override protected Void doInBackground() { powerUpSelected(); return null; } }.execute()); topPanel.add(toggleFavorite, gbc); toggleFavorite.addActionListener(l -> new SwingWorker<Void, Void>() { @Override protected Void doInBackground() { toggleFavorite(); return null; } }.execute()); ivTransfer.addKeyListener( new KeyListener() { @Override public void keyPressed(final KeyEvent event) { if (event.getKeyCode() == KeyEvent.VK_ENTER) { new SwingWorker<Void, Void>() { @Override protected Void doInBackground() { selectLessThanIv(); return null; } }.execute(); } } @Override public void keyTyped(final KeyEvent event) { // nothing here } @Override public void keyReleased(final KeyEvent event) { // nothing here } }); topPanel.add(ivTransfer, gbc); new GhostText(ivTransfer, "Pokemon IV"); final JButton transferIv = new JButton("Select Pokemon < IV"); transferIv.addActionListener(l -> new SwingWorker<Void, Void>() { @Override protected Void doInBackground() { selectLessThanIv(); return null; } }.execute()); topPanel.add(transferIv, gbc); gbc.weightx = 1.0; gbc.weighty = 1.0; gbc.gridwidth = 3; gbc.fill = GridBagConstraints.HORIZONTAL; topPanel.add(searchBar, gbc); // Pokemon name language drop down final String[] locales = {"en", "de", "fr", "ru", "zh_CN", "zh_HK", "ja"}; final JComboBox<String> pokelang = new JComboBox<>(locales); pokelang.setSelectedItem(config.getString(ConfigKey.LANGUAGE)); pokelang.addActionListener(e -> new SwingWorker<Void, Void>() { @Override protected Void doInBackground() { final String lang = (String) pokelang.getSelectedItem(); changeLanguage(lang); return null; } }.execute()); topPanel.add(pokelang); // Set font size if specified in config final Font font = pt.getFont(); final int size = Math.max(MIN_FONT_SIZE, config.getInt(ConfigKey.FONT_SIZE, font.getSize())); if (size != font.getSize()) { pt.setFont(font.deriveFont((float) size)); } // Font size dropdown final String[] sizes = {"8", "10", "11", "12", "14", "16", "18"}; final JComboBox<String> fontSize = new JComboBox<>(sizes); fontSize.setSelectedItem(String.valueOf(size)); fontSize.addActionListener(e -> new SwingWorker<Void, Void>() { @Override protected Void doInBackground() { final String size = fontSize.getSelectedItem().toString(); pt.setFont(pt.getFont().deriveFont(Float.parseFloat(size))); config.setInt(ConfigKey.FONT_SIZE, Integer.parseInt(size)); return null; } }.execute()); topPanel.add(fontSize); LDocumentListener.addChangeListener(searchBar, e -> refreshList()); new GhostText(searchBar, "Search Pokémon..."); add(topPanel, BorderLayout.NORTH); final JScrollPane sp = new JScrollPane(pt); sp.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); add(sp, BorderLayout.CENTER); } private void changeLanguage(final String langCode) { config.setString(ConfigKey.LANGUAGE, langCode); // Check if cached locations should be deleted, because they are language dependant if (LocationHelper.locationFileExists() && JOptionPane.showConfirmDialog(null, "You have changed your language." + StringLiterals.NEWLINE + "The locations queried from the Google Locations Geocode API may be language-specific (eg local city and country names)." + StringLiterals.NEWLINE + "If you want the cached languages to be deleted, click YES." + StringLiterals.NEWLINE + "But do note that querying the locations again may take some time and may bring you over the API limit.", "Local Locations may be different", JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE) == JOptionPane.YES_OPTION) { // Delete the cached locations now LocationHelper.deleteCachedLocations(); } refreshPkmn(); } private void refreshPkmn() { try { go.getInventories().updateInventories(); PokemonGoMainWindow.getInstance().refreshTitle(); } catch (final Exception e) { e.printStackTrace(); } SwingUtilities.invokeLater(this::refreshList); System.out.println("Done refreshing Pokémon list"); } private void showFinishedText(final String message, final int size, final MutableInt success, final MutableInt skipped, final MutableInt err) { final String finishText = message + "\nPokémon total: " + size + "\nSuccessful: " + success.getValue() + (skipped.getValue() > 0 ? "\nSkipped: " + skipped.getValue() : "") + (err.getValue() > 0 ? "\nErrors: " + err.getValue() : ""); if (config.getBool(ConfigKey.SHOW_BULK_POPUP)) { JOptionPane.showMessageDialog(null, finishText, "Finished Operation", JOptionPane.INFORMATION_MESSAGE); } else { System.out.println(finishText); } } private void renameSelected() { final ArrayList<Pokemon> selection = getSelectedPokemon(); if (selection.isEmpty()) { return; } final PokeHandler handler = new PokeHandler(selection); final String renamePattern = inputOperation(BatchOperation.RENAME, selection); final MutableInt err = new MutableInt(), skipped = new MutableInt(), success = new MutableInt(), total = new MutableInt(1); final BiConsumer<NicknamePokemonResponse.Result, Pokemon> perPokeCallback = (renameResult, pokemon) -> { System.out.println(String.format( "Doing Rename %d of %d", total.getValue(), selection.size())); total.increment(); final PokeNick pokeNick = PokeHandler.generatePokemonNickname(renamePattern, pokemon); // We check if the Pokemon was skipped final boolean isSkipped = pokeNick.toString().equals(pokemon.getNickname()) && renameResult.getNumber() == NicknamePokemonResponse.Result.UNSET_VALUE; if (isSkipped) { System.out.println(String.format( "Skipped renaming %s, already named \"%s\"", PokemonUtils.getLocalPokeName(pokemon), pokemon.getNickname())); skipped.increment(); return; } if (renameResult.getNumber() == NicknamePokemonResponse.Result.SUCCESS_VALUE) { success.increment(); if (pokeNick.isTooLong()) { System.out.println(String.format( "WARNING: Nickname \"%s\" is too long. Get's cut to: \"%s\"", pokeNick.fullNickname, pokeNick.toString())); } System.out.println(String.format( "Renaming %s from \"%s\" to \"%s\", Result: Success!", PokemonUtils.getLocalPokeName(pokemon), pokemon.getNickname(), PokeHandler.generatePokemonNickname(renamePattern, pokemon))); } else { err.increment(); System.out.println(String.format( "Renaming %s failed! Code: %s; Nick: \"%s\"", PokemonUtils.getLocalPokeName(pokemon), renameResult.toString(), PokeHandler.generatePokemonNickname(renamePattern, pokemon))); } // If not last element and API was queried, sleep until the next one if (!selection.get(selection.size() - 1).equals(pokemon)) { final int sleepMin = config.getInt(ConfigKey.DELAY_RENAME_MIN); final int sleepMax = config.getInt(ConfigKey.DELAY_RENAME_MAX); Utilities.sleepRandom(sleepMin, sleepMax); } }; handler.bulkRenameWithPattern(renamePattern, perPokeCallback); try { go.getInventories().updateInventories(); } catch (final Exception e) { e.printStackTrace(); } SwingUtilities.invokeLater(this::refreshList); showFinishedText("Pokémon batch rename complete!", selection.size(), success, skipped, err); } private void transferSelected() { final ArrayList<Pokemon> selection = getSelectedPokemon(); final ArrayList<Pokemon> finalSelection = new ArrayList<Pokemon>(); if (selection.isEmpty()) { return; } if (!confirmOperation(BatchOperation.TRANSFER, selection)) { return; } final MutableInt err = new MutableInt(), skipped = new MutableInt(), success = new MutableInt(), total = new MutableInt(selection.size()); selection.forEach(poke -> { if (poke.isFavorite()) { System.out.println(String.format( "%s with %d CP is favorite, skipping.", PokemonUtils.getLocalPokeName(poke), poke.getCp())); skipped.increment(); return; } if (poke.isDeployed()) { System.out.println(String.format( GYM_SKIPPED_MESSAGE_UNFORMATTED, PokemonUtils.getLocalPokeName(poke), poke.getCp())); skipped.increment(); return; } if (go.getPlayerProfile().getBuddy() != null && poke.getId() == go.getPlayerProfile().getBuddy().getPokemon().getId()) { System.out.println(String.format( "%s with %d CP is buddy, skipping.", PokemonUtils.getLocalPokeName(poke), poke.getCp())); skipped.increment(); return; } finalSelection.add(poke); }); if (finalSelection.size() > 0) { System.out.println(String.format("Multi-Transfering %d pokemons, %d skipped", finalSelection.size(), skipped.intValue())); try { final Map<PokemonFamilyId, Integer> transferResult = go.getInventories().getPokebank().releasePokemon(finalSelection.toArray(new Pokemon[1])); transferResult.forEach((pokeFamilyID, candies) -> { System.out.println(String.format( "Transferred %s (Family): Candies : %d[+%d]", StringUtils.capitalize(pokeFamilyID.toString().toLowerCase().replace("family_", "")), go.getInventories().getCandyjar().getCandies(pokeFamilyID), candies)); }); success.add(finalSelection.size()); } catch (CaptchaActiveException | LoginFailedException | RemoteServerException | HashException e) { err.add(finalSelection.size()); System.out.println(String.format( "Error transferring pokemons! %s", Utilities.getRealExceptionMessage(e))); } } SwingUtilities.invokeLater(this::refreshList); showFinishedText("Pokémon multi-transfer complete!", total.intValue(), success, skipped, err); } private void evolveSelected() { final ArrayList<Pokemon> selection = getSelectedPokemon(); if (selection.isEmpty()) { return; } if (!confirmOperation(BatchOperation.EVOLVE, selection)) { return; } final MutableInt err = new MutableInt(), skipped = new MutableInt(), success = new MutableInt(), total = new MutableInt(1); selection.forEach(poke -> { System.out.println(String.format( "Doing Evolve %d of %d", total.getValue(), selection.size())); total.increment(); if (!poke.getDeployedFortId().isEmpty()) { System.out.println(String.format( GYM_SKIPPED_MESSAGE_UNFORMATTED, PokemonUtils.getLocalPokeName(poke), +poke.getCp())); skipped.increment(); return; } try { final int candies = poke.getCandy(); final int candiesToEvolve = poke.getCandiesToEvolve(); final int cp = poke.getCp(); final int hp = poke.getMaxStamina(); boolean afterTransfer = false; // Check if user has enough candy, otherwise we don't need to call server if (candies < candiesToEvolve) { err.increment(); System.out.println(String.format( "Error. Not enough candy to evolve %s. %d available, %d needed.", PokemonUtils.getLocalPokeName(poke), candies, candiesToEvolve)); return; } // Check if pokemon is already highest evolution, otherwise we don't need to call server if (!poke.canEvolve()) { skipped.increment(); System.out.println(String.format( "Skipped evolving %s. Is already highest evolution.", PokemonUtils.getLocalPokeName(poke))); return; } final EvolutionResult evolutionResultWrapper = poke.evolve(); if (evolutionResultWrapper.isSuccessful()) { final Pokemon newPoke = evolutionResultWrapper.getEvolvedPokemon(); int newCandies = newPoke.getCandy(); final int newCp = newPoke.getCp(); final int newHp = newPoke.getStamina(); int candyRefund = 1; System.out.println(String.format( "Evolving %s. Evolve result: %s", PokemonUtils.getLocalPokeName(poke), evolutionResultWrapper.getResult().toString())); if (config.getBool(ConfigKey.TRANSFER_AFTER_EVOLVE)) { if (newPoke.isFavorite()) { System.out.println(String.format( "Skipping \"Transfer After Evolve\" for %s because favorite.", StringUtils.capitalize(newPoke.getPokemonId().toString().toLowerCase()))); System.out.println(String.format( "Stat changes: " + "(Candies: %d[%d-%d+%d], " + "CP: %d[+%d], " + "HP: %d[+%d])", newCandies, candies, candiesToEvolve, candyRefund, newCp, (newCp - cp), newHp, (newHp - hp))); } else { // Sleep before transferring final int sleepMin = config.getInt(ConfigKey.DELAY_EVOLVE_MIN); final int sleepMax = config.getInt(ConfigKey.DELAY_EVOLVE_MAX); Utilities.sleepRandom(sleepMin, sleepMax); final ReleasePokemonResponse.Result result = newPoke .transferPokemon(); afterTransfer = true; if (result == ReleasePokemonResponse.Result.SUCCESS) { newCandies = newPoke.getCandy(); candyRefund++; } System.out.println(String.format( "Transferring %s, Result: %s", StringUtils.capitalize(newPoke.getPokemonId().toString().toLowerCase()), result)); System.out.println(String.format( "Stat changes: (Candies: %d[%d-%d+%d])", newCandies, candies, candiesToEvolve, candyRefund)); } } else { System.out.println(String.format( "Stat changes: " + "(Candies: %d[%d-%d+%d], " + "CP: %d[+%d], " + "HP: %d[+%d])", newCandies, candies, candiesToEvolve, candyRefund, newCp, (newCp - cp), newHp, (newHp - hp))); } go.getInventories().updateInventories(); success.increment(); } else { err.increment(); System.out.println(String.format( "Error evolving %s, result: %s", PokemonUtils.getLocalPokeName(poke), evolutionResultWrapper.getResult().toString())); } // If not last element, sleep until the next one if (!selection.get(selection.size() - 1).equals(poke)) { int sleepMin; int sleepMax; if (afterTransfer) { sleepMin = config.getInt(ConfigKey.DELAY_TRANSFER_MIN); sleepMax = config.getInt(ConfigKey.DELAY_TRANSFER_MAX); } else { sleepMin = config.getInt(ConfigKey.DELAY_EVOLVE_MIN); sleepMax = config.getInt(ConfigKey.DELAY_EVOLVE_MAX); } Utilities.sleepRandom(sleepMin, sleepMax); } } catch (final Exception e) { err.increment(); System.out.println(String.format( "Error evolving %s! %s", PokemonUtils.getLocalPokeName(poke), Utilities.getRealExceptionMessage(e))); } }); try { go.getInventories().updateInventories(); } catch (final Exception e) { e.printStackTrace(); } SwingUtilities.invokeLater(this::refreshList); showFinishedText(String.format( "Pokémon batch evolve%s complete!", config.getBool(ConfigKey.TRANSFER_AFTER_EVOLVE) ? "/transfer" : ""), selection.size(), success, skipped, err); } private void powerUpSelected() { final ArrayList<Pokemon> selection = getSelectedPokemon(); if (selection.isEmpty()) { return; } if (!confirmOperation(BatchOperation.POWER_UP, selection)) { return; } final MutableInt err = new MutableInt(), skipped = new MutableInt(), success = new MutableInt(), total = new MutableInt(1); selection.forEach(poke -> { try { System.out.println(String.format("Doing Power Up %d of %d", total.getValue(), selection.size())); total.increment(); if (!poke.getDeployedFortId().isEmpty()) { System.out.println(String.format( GYM_SKIPPED_MESSAGE_UNFORMATTED, PokemonUtils.getLocalPokeName(poke), +poke.getCp())); skipped.increment(); return; } final int stardust = go.getPlayerProfile().getCurrency(Currency.STARDUST); final int candies = poke.getCandy(); final int cp = poke.getCp(); final int hp = poke.getMaxStamina(); final int stardustToPowerUp = poke.getStardustCostsForPowerup(); final int candiesToPowerUp = poke.getCandyCostsForPowerup(); // Check if user has enough candy and stardust, otherwise we don't need to call server if (candies < candiesToPowerUp || stardust < stardustToPowerUp) { err.increment(); System.out.println(String.format( "Error. Not enough candy/stardust to power up %s. " + "Stardust: %d/%d, " + "Candy: %d/%d", PokemonUtils.getLocalPokeName(poke), stardust, stardustToPowerUp, candies, candiesToPowerUp)); return; } // Check we aren't at max level, otherwise we don't need to call server if (poke.getCp() >= poke.getMaxCpForPlayer()) { skipped.increment(); System.out.println(String.format( "Skipping power-up of %s. It is already MaxCP: %d", PokemonUtils.getLocalPokeName(poke), poke.getCp())); return; } final UpgradePokemonResponse.Result upgradeResult = poke.powerUp(); go.getPlayerProfile().updateProfile(); if (upgradeResult == UpgradePokemonResponse.Result.SUCCESS) { final int newCandies = poke.getCandy(); final int newCp = poke.getCp(); final int newHp = poke.getMaxStamina(); System.out.println(String.format( "Powering Up %s, Result: Success!", PokemonUtils.getLocalPokeName(poke))); System.out.println(String.format( "Stat changes: " + "(Candies : %d[%d-%d], " + "CP: %d[+%d], " + "HP: %d[+%d], " + "Stardust used %d[remaining: %d])", newCandies, candies, candiesToPowerUp, newCp, newCp - cp, newHp, newHp - hp, stardustToPowerUp, go.getPlayerProfile().getCurrency(Currency.STARDUST))); success.increment(); } else { err.increment(); System.out.println(String.format( "Error powering up %s, result: %s", PokemonUtils.getLocalPokeName(poke), upgradeResult.toString())); } // If not last element, sleep until the next one if (!selection.get(selection.size() - 1).equals(poke)) { final int sleepMin = config.getInt(ConfigKey.DELAY_POWERUP_MIN); final int sleepMax = config.getInt(ConfigKey.DELAY_POWERUP_MAX); Utilities.sleepRandom(sleepMin, sleepMax); } } catch (final Exception e) { err.increment(); System.out.println(String.format( "Error powering up %s! %s", PokemonUtils.getLocalPokeName(poke), Utilities.getRealExceptionMessage(e))); } }); try { go.getInventories().updateInventories(); PokemonGoMainWindow.getInstance().refreshTitle(); } catch (final RemoteServerException | LoginFailedException | CaptchaActiveException | HashException e) { e.printStackTrace(); } SwingUtilities.invokeLater(this::refreshList); showFinishedText("Pokémon batch powerup complete!", selection.size(), success, skipped, err); } // feature added by Ben Kauffman private void toggleFavorite() { final ArrayList<Pokemon> selection = getSelectedPokemon(); if (selection.isEmpty()) { return; } if (!confirmOperation(BatchOperation.FAVORITE, selection)) { return; } final MutableInt err = new MutableInt(), skipped = new MutableInt(), success = new MutableInt(), total = new MutableInt(1); selection.forEach(poke -> { try { System.out.println(String.format( "Toggling favorite %d of %d", total.getValue(), selection.size())); total.increment(); final SetFavoritePokemonResponse.Result favoriteResult = poke .setFavoritePokemon(!poke.isFavorite()); System.out.println(String.format( "Attempting to set favorite for %s to %b...", PokemonUtils.getLocalPokeName(poke), !poke.isFavorite())); go.getPlayerProfile().updateProfile(); if (favoriteResult == SetFavoritePokemonResponse.Result.SUCCESS) { System.out.println(String.format( "Favorite for %s set to %b, Result: Success!", PokemonUtils.getLocalPokeName(poke), !poke.isFavorite())); success.increment(); } else { err.increment(); System.out.println(String.format( "Error toggling favorite for %s, result: %s", PokemonUtils.getLocalPokeName(poke), favoriteResult.toString())); } // If not last element, sleep until the next one if (!selection.get(selection.size() - 1).equals(poke)) { final int sleepMin = config.getInt(ConfigKey.DELAY_FAVORITE_MIN); final int sleepMax = config.getInt(ConfigKey.DELAY_FAVORITE_MAX); Utilities.sleepRandom(sleepMin, sleepMax); } } catch (final Exception e) { err.increment(); System.out.println(String.format( "Error toggling favorite for %s! %s", PokemonUtils.getLocalPokeName(poke), Utilities.getRealExceptionMessage(e))); } }); try { PokemonGoMainWindow.getInstance().refreshTitle(); } catch (final Exception e) { e.printStackTrace(); } SwingUtilities.invokeLater(this::refreshPkmn); showFinishedText("Pokémon batch \"toggle favorite\" complete!", selection.size(), success, skipped, err); } private void selectLessThanIv() { if (!NumberUtils.isNumber(ivTransfer.getText())) { System.out.println("Please select a valid IV value (0-100)"); return; } final double ivLessThan = Double.parseDouble(ivTransfer.getText()); if (ivLessThan > 100 || ivLessThan < 0) { System.out.println("Please select a valid IV value (0-100)"); return; } pt.clearSelection(); System.out.println("Selecting Pokemon with IV less than: " + ivTransfer.getText()); for (int i = 0; i < pt.getRowCount(); i++) { final double pIv = (double) pt.getValueAt(i, PokeColumn.IV_RATING.id) * 100; //final double pIv = Double.parseDouble((String) pt.getValueAt(i, 3)); if (pIv < ivLessThan) { pt.getSelectionModel().addSelectionInterval(i, i); } } } /** * Handles.. idk why we have this. @author Cryptic * * @param operation operation to be done * @param pokes list of pokemons * @return rename pattern */ private String inputOperation(final BatchOperation operation, final ArrayList<Pokemon> pokes) { JPanel panel; String message = ""; String savedPattern = ""; switch (operation) { case RENAME: panel = buildPanelForRename(); savedPattern = config.getString(ConfigKey.RENAME_PATTERN); message = "Renaming " + pokes.size() + " Pokémon."; break; default: panel = buildPanelForOperation(operation, pokes); break; } final String input = (String) JOptionPane.showInputDialog(null, panel, message, JOptionPane.PLAIN_MESSAGE, null, null, savedPattern); if (input != null) { switch (operation) { case RENAME: config.setString(ConfigKey.RENAME_PATTERN, input); default: break; } } return input; } /** * Prompt for confirmation before doing the operation. * * @param operation operation to be done * @param pokes list of pokemons * @return ok was selected */ private boolean confirmOperation(final BatchOperation operation, final ArrayList<Pokemon> pokes) { final JPanel panel = buildPanelForOperation(operation, pokes); final int response = JOptionPane.showConfirmDialog(null, panel, String.format( "Please confirm %s of %d Pokémon", operation, pokes.size()), JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE); return response == JOptionPane.OK_OPTION; } /** * Internal function to build the panel for a batch operation. * * @param operation The operation. * @param pokes List of Pokémon for that operation. * @return The panel. */ private JPanel buildPanelForOperation(final BatchOperation operation, final ArrayList<Pokemon> pokes) { final JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); final JPanel innerPanel = new JPanel(); innerPanel.setLayout(new BoxLayout(innerPanel, BoxLayout.Y_AXIS)); innerPanel.setAlignmentX(CENTER_ALIGNMENT); final JScrollPane scroll = new JScrollPane(innerPanel); scroll.setAlignmentX(CENTER_ALIGNMENT); // Auto-height? Resizable? Haha. Funny joke. // I hate swing. But we need to get around here some way. // So lets get dirty. // We take 20 px for each row, 5 px buffer, and cap that at may 400 // pixel. final int height = Math.min(400, pokes.size() * 20 + 5); panel.setPreferredSize(new Dimension(500, height)); pokes.forEach(p -> { String str = String.format("%s - CP: %d, IV: %s%%", PokemonUtils.getLocalPokeName(p), p.getCp(), Utilities.percentageWithTwoCharacters(PokemonCalculationUtils.ivRating(p))); switch (operation) { case EVOLVE: str += " Cost: " + p.getCandiesToEvolve(); str += p.getCandiesToEvolve() > 1 ? " Candies" : " Candy"; break; case POWER_UP: str += " Cost: " + p.getCandyCostsForPowerup(); str += p.getCandyCostsForPowerup() > 1 ? " Candies" : " Candy"; str += " " + p.getStardustCostsForPowerup() + " Stardust"; break; case RENAME: for (final PokeHandler.ReplacePattern pattern : PokeHandler.ReplacePattern.values()) { str += StringLiterals.PERCENTAGE + pattern.name().toLowerCase() + "% -> " + pattern.toString() + "\n"; } break; case TRANSFER: break; default: break; } innerPanel.add(new JLabel(str)); }); panel.add(scroll); return panel; } /** * Build the Popup panel for Renaming. * * @return The panel. */ private JPanel buildPanelForRename() { final JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); panel.setAlignmentX(LEFT_ALIGNMENT); final JPanel innerPanel = new JPanel(); innerPanel.setLayout(new BoxLayout(innerPanel, BoxLayout.Y_AXIS)); innerPanel.setAlignmentX(LEFT_ALIGNMENT); final JScrollPane scroll = new JScrollPane(innerPanel); scroll.setAlignmentX(LEFT_ALIGNMENT); panel.setPreferredSize(new Dimension(POPUP_WIDTH, POPUP_HEIGHT)); panel.add(new JLabel("You can rename with normal text and patterns, or both combined.")); panel.add(new JLabel("Patterns are going to be replaced with the Pokémons values.")); panel.add(new JLabel("Existing patterns: (double click on item to copy)")); final JList<ReplacePattern> listPattern = new JList<>(ReplacePattern.values()); listPattern.setCellRenderer(new ReplacePatternRenderer()); listPattern.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(final MouseEvent mouseEvent) { @SuppressWarnings("unchecked") final JList<ReplacePattern> theList = ((JList<ReplacePattern>) mouseEvent.getSource()); final boolean isDoubleClick = mouseEvent.getClickCount() == 2; if (isDoubleClick) { final int index = theList.locationToIndex(mouseEvent.getPoint()); if (index >= 0) { final ReplacePattern replacePattern = theList.getModel().getElementAt(index); final Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard(); cb.setContents(new StringSelection(StringLiterals.PERCENTAGE + replacePattern.name().toLowerCase() + StringLiterals.PERCENTAGE), null); } } } }); innerPanel.add(listPattern); panel.add(scroll); return panel; } public ArrayList<Pokemon> getSelectedPokemon() { final ArrayList<Pokemon> pokes = new ArrayList<>(); final PokemonTableModel model = (PokemonTableModel) pt.getModel(); for (final int i : pt.getSelectedRows()) { final Pokemon poke = model.getPokemonByIndex(i); if (poke != null) { pokes.add(poke); } } return pokes; } public void refreshList() { final List<Pokemon> pokes = new ArrayList<>(); final String search = searchBar.getText().replaceAll(StringLiterals.SPACE, "").replaceAll(StringLiterals.UNDERSCORE, "").replaceAll("snek", "ekans") .toLowerCase(); final String[] terms = search.split(";"); try { if ("".equals(search) || "searchpokémon...".equals(search)) { pokes.addAll(go.getInventories().getPokebank().getPokemons()); } else { synchronized (go.getInventories().getPokebank().getLock()) { go.getInventories().getPokebank().getPokemons().forEach(poke -> { final boolean useFamilyName = config.getBool(ConfigKey.INCLUDE_FAMILY); String familyName = ""; if (useFamilyName) { // Try translating family name try { final PokemonId familyPokemonId = PokemonId.valueOf(poke.getPokemonFamily().toString().replaceAll(StringLiterals.FAMILY_PREFIX, "")); familyName = PokemonUtils.getLocalPokeName(familyPokemonId.getNumber()); } catch (final IllegalArgumentException e) { familyName = poke.getPokemonFamily().toString(); } } String searchme = Utilities.concatString(',', PokemonUtils.getLocalPokeName(poke), ((useFamilyName) ? familyName : ""), poke.getNickname(), poke.getSettings().getType().toString(), poke.getSettings().getType2().toString(), poke.getMove1().toString(), poke.getMove2().toString(), poke.getPokeball().toString()); searchme = searchme.replaceAll("_FAST", "").replaceAll(StringLiterals.FAMILY_PREFIX, "").replaceAll("NONE", "") .replaceAll("ITEM_", "").replaceAll(StringLiterals.POKEMON_TYPE_PREFIX, "").replaceAll(StringLiterals.UNDERSCORE, "") .replaceAll(StringLiterals.SPACE, "").toLowerCase(); for (final String s : terms) { if (searchme.contains(s)) { pokes.add(poke); // Break, so that a Pokémon isn't added twice even if it // matches more than one criteria break; } } }); } } pt.constructNewTableModel(pokes); } catch (final Exception e) { e.printStackTrace(); } } /** * Provide custom formatting for the list of patterns. */ private class ReplacePatternRenderer extends JLabel implements ListCellRenderer<ReplacePattern> { /** * Constructor to create a ReplacePatternRenderer. */ ReplacePatternRenderer() { super(); setOpaque(true); } @Override public Component getListCellRendererComponent(final JList<? extends ReplacePattern> list, final ReplacePattern value, final int index, final boolean isSelected, final boolean cellHasFocus) { //Get the selected index. (The index param isn't always valid, so just use the value.) if (isSelected) { setBackground(list.getSelectionBackground()); setForeground(list.getSelectionForeground()); } else { setBackground(list.getBackground()); setForeground(list.getForeground()); } final String str = StringLiterals.PERCENTAGE + value.name().toLowerCase() + StringLiterals.PERCENTAGE + " -> " + value.toString() + StringLiterals.NEWLINE; setText(str); setFont(list.getFont()); return this; } } public List<String> getColumnErrors() { return pt.getColumnErrors(); } public void saveColumnOrder() { pt.saveColumnOrderToConfig(); } }