diff --git a/src/main/java/com/pokegoapi/api/pokemon/Pokemon.java b/src/main/java/com/pokegoapi/api/pokemon/Pokemon.java index 1aaffa24..b08b9027 100644 --- a/src/main/java/com/pokegoapi/api/pokemon/Pokemon.java +++ b/src/main/java/com/pokegoapi/api/pokemon/Pokemon.java @@ -30,7 +30,6 @@ import POGOProtos.Networking.Requests.RequestTypeOuterClass.RequestType; import POGOProtos.Networking.Responses.EvolvePokemonResponseOuterClass.EvolvePokemonResponse; import POGOProtos.Networking.Responses.NicknamePokemonResponseOuterClass.NicknamePokemonResponse; -import POGOProtos.Networking.Responses.RecycleInventoryItemResponseOuterClass; import POGOProtos.Networking.Responses.ReleasePokemonResponseOuterClass.ReleasePokemonResponse; import POGOProtos.Networking.Responses.ReleasePokemonResponseOuterClass.ReleasePokemonResponse.Result; import POGOProtos.Networking.Responses.SetFavoritePokemonResponseOuterClass.SetFavoritePokemonResponse; @@ -42,6 +41,7 @@ import com.pokegoapi.api.inventory.Item; import com.pokegoapi.api.map.pokemon.EvolutionResult; import com.pokegoapi.exceptions.LoginFailedException; +import com.pokegoapi.exceptions.NoSuchItemException; import com.pokegoapi.exceptions.RemoteServerException; import com.pokegoapi.main.ServerRequest; import com.pokegoapi.util.Log; @@ -189,7 +189,7 @@ public UpgradePokemonResponse.Result powerUp() throws LoginFailedException, Remo } } - /** + /**dus * Evolve evolution result. * * @return the evolution result @@ -390,6 +390,48 @@ public double getBaseFleeRate() { return getMeta().getBaseFleeRate(); } + public float getLevel() { + return PokemonCpUtils.getLevelFromCpMultiplier(proto.getCpMultiplier() + proto.getAdditionalCpMultiplier()); + } + + /** + * @return The maximum CP for this pokemon + */ + public int getMaxCp() throws NoSuchItemException { + PokemonMeta pokemonMeta = PokemonMetaRegistry.getMeta(proto.getPokemonId()); + if (pokemonMeta == null) { + throw new NoSuchItemException("Cannot find meta data for " + proto.getPokemonId().name()); + } + int attack = proto.getIndividualAttack() + pokemonMeta.getBaseAttack(); + int defense = proto.getIndividualDefense() + pokemonMeta.getBaseDefense(); + int stamina = proto.getIndividualStamina() + pokemonMeta.getBaseStamina(); + return PokemonCpUtils.getMaxCp(attack, defense, stamina); + } + + /** + * @return The CP for this pokemon after powerup + */ + public int getCpAfterPowerup() { + return PokemonCpUtils.getCpAfterPowerup(proto.getCp(), + proto.getCpMultiplier() + proto.getAdditionalCpMultiplier()); + } + + /** + * @return Cost of candy for a powerup + */ + public int getCandyCostsForPowerup() { + return PokemonCpUtils.getCandyCostsForPowerup(proto.getCpMultiplier() + proto.getAdditionalCpMultiplier(), + proto.getNumUpgrades()); + } + + /** + * @return Cost of stardust for a powerup + */ + public int getStardustCostsForPowerup() { + return PokemonCpUtils.getStartdustCostsForPowerup(proto.getCpMultiplier() + proto.getAdditionalCpMultiplier(), + proto.getNumUpgrades()); + } + public PokemonIdOuterClass.PokemonId getParent() { return getMeta().getParentId(); } diff --git a/src/main/java/com/pokegoapi/api/pokemon/PokemonCpUtils.java b/src/main/java/com/pokegoapi/api/pokemon/PokemonCpUtils.java new file mode 100644 index 00000000..4bebdba6 --- /dev/null +++ b/src/main/java/com/pokegoapi/api/pokemon/PokemonCpUtils.java @@ -0,0 +1,256 @@ +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.pokegoapi.api.pokemon; + +import java.util.HashMap; +import java.util.Map; + +/** + * Information in this class is based on: + * http://pokemongo.gamepress.gg/cp-multiplier + * and + * http://pokemongo.gamepress.gg/pokemon-stats-advanced + */ +class PokemonCpUtils { + private static final Map LEVEL_CPMULTIPLIER = new HashMap<>(); + + static { + LEVEL_CPMULTIPLIER.put(1f, 0.094f); + LEVEL_CPMULTIPLIER.put(1.5f, 0.135137432f); + LEVEL_CPMULTIPLIER.put(2f, 0.16639787f); + LEVEL_CPMULTIPLIER.put(2.5f, 0.192650919f); + LEVEL_CPMULTIPLIER.put(3f, 0.21573247f); + LEVEL_CPMULTIPLIER.put(3.5f, 0.236572661f); + LEVEL_CPMULTIPLIER.put(4f, 0.25572005f); + LEVEL_CPMULTIPLIER.put(4.5f, 0.273530381f); + LEVEL_CPMULTIPLIER.put(5f, 0.29024988f); + LEVEL_CPMULTIPLIER.put(5.5f, 0.306057377f); + LEVEL_CPMULTIPLIER.put(6f, 0.3210876f); + LEVEL_CPMULTIPLIER.put(6.5f, 0.335445036f); + LEVEL_CPMULTIPLIER.put(7f, 0.34921268f); + LEVEL_CPMULTIPLIER.put(7.5f, 0.362457751f); + LEVEL_CPMULTIPLIER.put(8f, 0.37523559f); + LEVEL_CPMULTIPLIER.put(8.5f, 0.387592406f); + LEVEL_CPMULTIPLIER.put(9f, 0.39956728f); + LEVEL_CPMULTIPLIER.put(9.5f, 0.411193551f); + LEVEL_CPMULTIPLIER.put(10f, 0.42250001f); + LEVEL_CPMULTIPLIER.put(10.5f, 0.432926419f); + LEVEL_CPMULTIPLIER.put(11f, 0.44310755f); + LEVEL_CPMULTIPLIER.put(11.5f, 0.453059958f); + LEVEL_CPMULTIPLIER.put(12f, 0.46279839f); + LEVEL_CPMULTIPLIER.put(12.5f, 0.472336083f); + LEVEL_CPMULTIPLIER.put(13f, 0.48168495f); + LEVEL_CPMULTIPLIER.put(13.5f, 0.4908558f); + LEVEL_CPMULTIPLIER.put(14f, 0.49985844f); + LEVEL_CPMULTIPLIER.put(14.5f, 0.508701765f); + LEVEL_CPMULTIPLIER.put(15f, 0.51739395f); + LEVEL_CPMULTIPLIER.put(15.5f, 0.525942511f); + LEVEL_CPMULTIPLIER.put(16f, 0.53435433f); + LEVEL_CPMULTIPLIER.put(16.5f, 0.542635767f); + LEVEL_CPMULTIPLIER.put(17f, 0.55079269f); + LEVEL_CPMULTIPLIER.put(17.5f, 0.558830576f); + LEVEL_CPMULTIPLIER.put(18f, 0.56675452f); + LEVEL_CPMULTIPLIER.put(18.5f, 0.574569153f); + LEVEL_CPMULTIPLIER.put(19f, 0.58227891f); + LEVEL_CPMULTIPLIER.put(19.5f, 0.589887917f); + LEVEL_CPMULTIPLIER.put(20f, 0.59740001f); + LEVEL_CPMULTIPLIER.put(20.5f, 0.604818814f); + LEVEL_CPMULTIPLIER.put(21f, 0.61215729f); + LEVEL_CPMULTIPLIER.put(21.5f, 0.619399365f); + LEVEL_CPMULTIPLIER.put(22f, 0.62656713f); + LEVEL_CPMULTIPLIER.put(22.5f, 0.633644533f); + LEVEL_CPMULTIPLIER.put(23f, 0.64065295f); + LEVEL_CPMULTIPLIER.put(23.5f, 0.647576426f); + LEVEL_CPMULTIPLIER.put(24f, 0.65443563f); + LEVEL_CPMULTIPLIER.put(24.5f, 0.661214806f); + LEVEL_CPMULTIPLIER.put(25f, 0.667934f); + LEVEL_CPMULTIPLIER.put(25.5f, 0.674577537f); + LEVEL_CPMULTIPLIER.put(26f, 0.68116492f); + LEVEL_CPMULTIPLIER.put(26.5f, 0.687680648f); + LEVEL_CPMULTIPLIER.put(27f, 0.69414365f); + LEVEL_CPMULTIPLIER.put(27.5f, 0.700538673f); + LEVEL_CPMULTIPLIER.put(28f, 0.70688421f); + LEVEL_CPMULTIPLIER.put(28.5f, 0.713164996f); + LEVEL_CPMULTIPLIER.put(29f, 0.71939909f); + LEVEL_CPMULTIPLIER.put(29.5f, 0.725571552f); + LEVEL_CPMULTIPLIER.put(30f, 0.7317f); + LEVEL_CPMULTIPLIER.put(30.5f, 0.734741009f); + LEVEL_CPMULTIPLIER.put(31f, 0.73776948f); + LEVEL_CPMULTIPLIER.put(31.5f, 0.740785574f); + LEVEL_CPMULTIPLIER.put(32f, 0.74378943f); + LEVEL_CPMULTIPLIER.put(32.5f, 0.746781211f); + LEVEL_CPMULTIPLIER.put(33f, 0.74976104f); + LEVEL_CPMULTIPLIER.put(33.5f, 0.752729087f); + LEVEL_CPMULTIPLIER.put(34f, 0.75568551f); + LEVEL_CPMULTIPLIER.put(34.5f, 0.758630378f); + LEVEL_CPMULTIPLIER.put(35f, 0.76156384f); + LEVEL_CPMULTIPLIER.put(35.5f, 0.764486065f); + LEVEL_CPMULTIPLIER.put(36f, 0.76739717f); + LEVEL_CPMULTIPLIER.put(36.5f, 0.770297266f); + LEVEL_CPMULTIPLIER.put(37f, 0.7731865f); + LEVEL_CPMULTIPLIER.put(37.5f, 0.776064962f); + LEVEL_CPMULTIPLIER.put(38f, 0.77893275f); + LEVEL_CPMULTIPLIER.put(38.5f, 0.781790055f); + LEVEL_CPMULTIPLIER.put(39f, 0.78463697f); + LEVEL_CPMULTIPLIER.put(39.5f, 0.787473578f); + LEVEL_CPMULTIPLIER.put(40f, 0.79030001f); + } + + private static float getLevel(float cpMuliplier) { + float level; + if (cpMuliplier < 0.734f) { + // compute polynomial approximation obtained by regression + level = 58.35178527f * cpMuliplier * cpMuliplier - 2.838007664f * cpMuliplier + 0.8539209906f; + } else { + // compute linear approximation obtained by regression + level = 171.0112688f * cpMuliplier - 95.20425243f; + } + // round to nearest .5 value and return + return Math.round((level) * 2) / 2.0f; + } + + /** + * Get the level from the cp multiplier + * @param cpMultiplier All CP multiplier values combined + * @return Level + */ + static float getLevelFromCpMultiplier(float cpMultiplier) { + return getLevel(cpMultiplier); + } + + /** + * Get the maximum CP from the values + * @param attack All attack values combined + * @param defense All defense values combined + * @param stamina All stamina values combined + * @return Maximum CP for these levels + */ + static int getMaxCp(int attack, int defense, int stamina) { + float maxCpMultplier = LEVEL_CPMULTIPLIER.get(40f); + return (int)(attack * Math.pow(defense, 0.5) * Math.pow(stamina, 0.5) * Math.pow(maxCpMultplier,2) / 10f); + } + + /** + * Get the CP after powerup + * @param cp Current CP level + * @param cpMultiplier All CP multiplier values combined + * @return New CP level + */ + static int getCpAfterPowerup(float cp, float cpMultiplier) { + // Based on http://pokemongo.gamepress.gg/power-up-costs + float level = getLevelFromCpMultiplier(cpMultiplier); + if (level <= 10) { + return (int)((cp * 0.009426125469) / Math.pow(cpMultiplier, 2)); + } + if (level <= 20) { + return (int)((cp * 0.008919025675) / Math.pow(cpMultiplier, 2)); + } + if (level <= 30) { + return (int)((cp * 0.008924905903) / Math.pow(cpMultiplier, 2)); + } + return (int)((cp * 0.00445946079) / Math.pow(cpMultiplier, 2)); + } + + /** + * Get the amount of stardust required to do a powerup + * @param cpMultiplier All CP multiplier values combined + * @param powerups Number of previous powerups + * @return Amount of stardust + */ + static int getStartdustCostsForPowerup(float cpMultiplier, int powerups) { + // Based on http://pokemongo.gamepress.gg/power-up-costs + float level = getLevelFromCpMultiplier(cpMultiplier); + if (level <= 3 && powerups <= 4) { + return 200; + } + if (level <= 4 && powerups <= 8) { + return 400; + } + if (level <= 7 && powerups <= 12) { + return 600; + } + if (level <= 8 && powerups <= 16) { + return 800; + } + if (level <= 11 && powerups <= 20) { + return 1000; + } + if (level <= 13 && powerups <= 24) { + return 1300; + } + if (level <= 15 && powerups <= 28) { + return 1600; + } + if (level <= 17 && powerups <= 32) { + return 1900; + } + if (level <= 19 && powerups <= 36) { + return 2200; + } + if (level <= 21 && powerups <= 40) { + return 2500; + } + if (level <= 23 && powerups <= 44) { + return 3000; + } + if (level <= 25 && powerups <= 48) { + return 3500; + } + if (level <= 27 && powerups <= 52) { + return 4000; + } + if (level <= 29 && powerups <= 56) { + return 4500; + } + if (level <= 31 && powerups <= 60) { + return 5000; + } + if (level <= 33 && powerups <= 64) { + return 6000; + } + if (level <= 35 && powerups <= 68) { + return 7000; + } + if (level <= 37 && powerups <= 72) { + return 8000; + } + if (level <= 39 && powerups <= 76) { + return 9000; + } + return 10000; + } + + /** + * Get the amount of candy required to do a powerup + * @param cpMultiplier All CP multiplier values combined + * @param powerups Number of previous powerups + * @return Amount of candy + */ + static int getCandyCostsForPowerup(float cpMultiplier, int powerups) { + // Based on http://pokemongo.gamepress.gg/power-up-costs + float level = getLevelFromCpMultiplier(cpMultiplier); + if (level <= 13 && powerups <= 20 ) { + return 1; + } + if (level <= 21 && powerups <= 36 ) { + return 2; + } + if (level <= 31 && powerups <= 60 ) { + return 3; + } + return 4; + } +}