From 363b24f9153f32627802089eee7900d5749e410e Mon Sep 17 00:00:00 2001 From: Ajarmar Date: Sun, 8 May 2022 23:52:01 +0200 Subject: add "better trainer movesets" setting --- src/com/sneed/pkrandom/Randomizer.java | 270 +++-- src/com/sneed/pkrandom/Settings.java | 13 +- .../sneed/pkrandom/constants/GlobalConstants.java | 36 +- src/com/sneed/pkrandom/newgui/Bundle.properties | 2 + .../sneed/pkrandom/newgui/NewRandomizerGUI.form | 11 + .../sneed/pkrandom/newgui/NewRandomizerGUI.java | 10 + src/com/sneed/pkrandom/pokemon/Effectiveness.java | 56 + src/com/sneed/pkrandom/pokemon/Move.java | 28 + src/com/sneed/pkrandom/pokemon/MoveSynergy.java | 1207 ++++++++++++++++++++ src/com/sneed/pkrandom/pokemon/Trainer.java | 8 + src/com/sneed/pkrandom/pokemon/TrainerPokemon.java | 2 - src/com/sneed/pkrandom/pokemon/Type.java | 15 + .../pkrandom/romhandlers/AbstractRomHandler.java | 687 ++++++++++- .../sneed/pkrandom/romhandlers/Gen1RomHandler.java | 7 + .../sneed/pkrandom/romhandlers/Gen2RomHandler.java | 7 + .../sneed/pkrandom/romhandlers/Gen3RomHandler.java | 10 +- .../sneed/pkrandom/romhandlers/Gen4RomHandler.java | 11 +- .../sneed/pkrandom/romhandlers/Gen5RomHandler.java | 14 +- .../sneed/pkrandom/romhandlers/Gen6RomHandler.java | 13 +- .../sneed/pkrandom/romhandlers/Gen7RomHandler.java | 22 +- src/com/sneed/pkrandom/romhandlers/RomHandler.java | 8 +- 21 files changed, 2262 insertions(+), 175 deletions(-) create mode 100644 src/com/sneed/pkrandom/pokemon/MoveSynergy.java (limited to 'src/com/sneed/pkrandom') diff --git a/src/com/sneed/pkrandom/Randomizer.java b/src/com/sneed/pkrandom/Randomizer.java index 41dc933..47b0c1e 100644 --- a/src/com/sneed/pkrandom/Randomizer.java +++ b/src/com/sneed/pkrandom/Randomizer.java @@ -84,6 +84,7 @@ public class Randomizer { boolean startersChanged = false; boolean evolutionsChanged = false; boolean trainersChanged = false; + boolean trainerMovesetsChanged = false; boolean staticsChanged = false; boolean totemsChanged = false; boolean wildsChanged = false; @@ -312,6 +313,118 @@ public class Randomizer { log.println("Pokemon Movesets: Unchanged." + NEWLINE); } + // TMs + + if (!(settings.getMovesetsMod() == Settings.MovesetsMod.METRONOME_ONLY) + && settings.getTmsMod() == Settings.TMsMod.RANDOM) { + romHandler.randomizeTMMoves(settings); + tmMovesChanged = true; + } + + if (tmMovesChanged) { + checkValue = logTMMoves(log, checkValue); + } else if (settings.getMovesetsMod() == Settings.MovesetsMod.METRONOME_ONLY) { + log.println("TM Moves: Metronome Only." + NEWLINE); + } else { + log.println("TM Moves: Unchanged." + NEWLINE); + } + + // TM/HM compatibility + // 1. Randomize TM/HM compatibility + // 2. Ensure levelup move sanity + // 3. Follow evolutions + // 4. Full HM compatibility + // 5. Copy to cosmetic forms + + switch (settings.getTmsHmsCompatibilityMod()) { + case COMPLETELY_RANDOM: + case RANDOM_PREFER_TYPE: + romHandler.randomizeTMHMCompatibility(settings); + tmsHmsCompatChanged = true; + break; + case FULL: + romHandler.fullTMHMCompatibility(); + tmsHmsCompatChanged = true; + break; + default: + break; + } + + if (settings.isTmLevelUpMoveSanity()) { + romHandler.ensureTMCompatSanity(); + if (settings.isTmsFollowEvolutions()) { + romHandler.ensureTMEvolutionSanity(); + } + tmsHmsCompatChanged = true; + } + + if (settings.isFullHMCompat()) { + romHandler.fullHMCompatibility(); + tmsHmsCompatChanged = true; + } + + // Copy TM/HM compatibility to cosmetic formes if it was changed at all, and log changes + if (tmsHmsCompatChanged) { + romHandler.copyTMCompatibilityToCosmeticFormes(); + logTMHMCompatibility(log); + } + + // Move Tutors + if (romHandler.hasMoveTutors()) { + + List oldMtMoves = romHandler.getMoveTutorMoves(); + + if (!(settings.getMovesetsMod() == Settings.MovesetsMod.METRONOME_ONLY) + && settings.getMoveTutorMovesMod() == Settings.MoveTutorMovesMod.RANDOM) { + + romHandler.randomizeMoveTutorMoves(settings); + moveTutorMovesChanged = true; + } + + if (moveTutorMovesChanged) { + checkValue = logMoveTutorMoves(log, checkValue, oldMtMoves); + } else if (settings.getMovesetsMod() == Settings.MovesetsMod.METRONOME_ONLY) { + log.println("Move Tutor Moves: Metronome Only." + NEWLINE); + } else { + log.println("Move Tutor Moves: Unchanged." + NEWLINE); + } + + // Move Tutor Compatibility + // 1. Randomize MT compatibility + // 2. Ensure levelup move sanity + // 3. Follow evolutions + // 4. Copy to cosmetic forms + + switch (settings.getMoveTutorsCompatibilityMod()) { + case COMPLETELY_RANDOM: + case RANDOM_PREFER_TYPE: + romHandler.randomizeMoveTutorCompatibility(settings); + tutorCompatChanged = true; + break; + case FULL: + romHandler.fullMoveTutorCompatibility(); + tutorCompatChanged = true; + break; + default: + break; + } + + if (settings.isTutorLevelUpMoveSanity()) { + romHandler.ensureMoveTutorCompatSanity(); + if (settings.isTutorFollowEvolutions()) { + romHandler.ensureMoveTutorEvolutionSanity(); + } + tutorCompatChanged = true; + } + + // Copy move tutor compatibility to cosmetic formes if it was changed at all + if (tutorCompatChanged) { + romHandler.copyMoveTutorCompatibilityToCosmeticFormes(); + logTutorCompatibility(log); + } + + } + // Trainer Pokemon // 1. Add extra Trainer Pokemon // 2. Set trainers to be double battles and add extra Pokemon if necessary @@ -361,6 +474,11 @@ public class Randomizer { trainersChanged = true; } + if (settings.isBetterTrainerMovesets()) { + romHandler.pickTrainerMovesets(settings); + trainerMovesetsChanged = true; + } + if (settings.isRandomizeHeldItemsForBossTrainerPokemon() || settings.isRandomizeHeldItemsForImportantTrainerPokemon() || settings.isRandomizeHeldItemsForRegularTrainerPokemon()) { @@ -387,7 +505,7 @@ public class Randomizer { } if (trainersChanged) { - maybeLogTrainerChanges(log, originalTrainerNames, trainerNamesChanged); + maybeLogTrainerChanges(log, originalTrainerNames, trainerNamesChanged, trainerMovesetsChanged); } else { log.println("Trainers: Unchanged." + NEWLINE); } @@ -486,117 +604,6 @@ public class Randomizer { } } - // TMs - - if (!(settings.getMovesetsMod() == Settings.MovesetsMod.METRONOME_ONLY) - && settings.getTmsMod() == Settings.TMsMod.RANDOM) { - romHandler.randomizeTMMoves(settings); - tmMovesChanged = true; - } - - if (tmMovesChanged) { - checkValue = logTMMoves(log, checkValue); - } else if (settings.getMovesetsMod() == Settings.MovesetsMod.METRONOME_ONLY) { - log.println("TM Moves: Metronome Only." + NEWLINE); - } else { - log.println("TM Moves: Unchanged." + NEWLINE); - } - - // TM/HM compatibility - // 1. Randomize TM/HM compatibility - // 2. Ensure levelup move sanity - // 3. Follow evolutions - // 4. Full HM compatibility - // 5. Copy to cosmetic forms - - switch (settings.getTmsHmsCompatibilityMod()) { - case COMPLETELY_RANDOM: - case RANDOM_PREFER_TYPE: - romHandler.randomizeTMHMCompatibility(settings); - tmsHmsCompatChanged = true; - break; - case FULL: - romHandler.fullTMHMCompatibility(); - tmsHmsCompatChanged = true; - break; - default: - break; - } - - if (settings.isTmLevelUpMoveSanity()) { - romHandler.ensureTMCompatSanity(); - if (settings.isTmsFollowEvolutions()) { - romHandler.ensureTMEvolutionSanity(); - } - tmsHmsCompatChanged = true; - } - - if (settings.isFullHMCompat()) { - romHandler.fullHMCompatibility(); - tmsHmsCompatChanged = true; - } - - // Copy TM/HM compatibility to cosmetic formes if it was changed at all, and log changes - if (tmsHmsCompatChanged) { - romHandler.copyTMCompatibilityToCosmeticFormes(); - logTMHMCompatibility(log); - } - - // Move Tutors - if (romHandler.hasMoveTutors()) { - - List oldMtMoves = romHandler.getMoveTutorMoves(); - - if (!(settings.getMovesetsMod() == Settings.MovesetsMod.METRONOME_ONLY) - && settings.getMoveTutorMovesMod() == Settings.MoveTutorMovesMod.RANDOM) { - - romHandler.randomizeMoveTutorMoves(settings); - moveTutorMovesChanged = true; - } - - if (moveTutorMovesChanged) { - checkValue = logMoveTutorMoves(log, checkValue, oldMtMoves); - } else if (settings.getMovesetsMod() == Settings.MovesetsMod.METRONOME_ONLY) { - log.println("Move Tutor Moves: Metronome Only." + NEWLINE); - } else { - log.println("Move Tutor Moves: Unchanged." + NEWLINE); - } - - // Move Tutor Compatibility - // 1. Randomize MT compatibility - // 2. Ensure levelup move sanity - // 3. Follow evolutions - // 4. Copy to cosmetic forms - - switch (settings.getMoveTutorsCompatibilityMod()) { - case COMPLETELY_RANDOM: - case RANDOM_PREFER_TYPE: - romHandler.randomizeMoveTutorCompatibility(settings); - tutorCompatChanged = true; - break; - case FULL: - romHandler.fullMoveTutorCompatibility(); - tutorCompatChanged = true; - break; - default: - break; - } - - if (settings.isTutorLevelUpMoveSanity()) { - romHandler.ensureMoveTutorCompatSanity(); - if (settings.isTutorFollowEvolutions()) { - romHandler.ensureMoveTutorEvolutionSanity(); - } - tutorCompatChanged = true; - } - - // Copy move tutor compatibility to cosmetic formes if it was changed at all - if (tutorCompatChanged) { - romHandler.copyMoveTutorCompatibilityToCosmeticFormes(); - logTutorCompatibility(log); - } - - } // In-game trades @@ -1116,8 +1123,7 @@ public class Randomizer { log.println(); } - private void maybeLogTrainerChanges(final PrintStream log, List originalTrainerNames, boolean trainerNamesChanged) { - + private void maybeLogTrainerChanges(final PrintStream log, List originalTrainerNames, boolean trainerNamesChanged, boolean logTrainerMovesets) { log.println("--Trainers Pokemon--"); List trainers = romHandler.getTrainers(); for (Trainer t : trainers) { @@ -1139,15 +1145,37 @@ public class Randomizer { if (t.offset != 0) { log.printf("@%X", t.offset); } - log.print(" - "); - boolean first = true; + String[] itemNames = romHandler.getItemNames(); - for (TrainerPokemon tpk : t.pokemon) { - if (!first) { - log.print(", "); + if (logTrainerMovesets) { + log.println(); + for (TrainerPokemon tpk : t.pokemon) { + List moves = romHandler.getMoves(); + log.printf(tpk.toString(), itemNames[tpk.heldItem]); + log.print(", Ability: " + romHandler.abilityName(romHandler.getAbilityForTrainerPokemon(tpk))); + log.print(" - "); + boolean first = true; + for (int move : tpk.moves) { + if (move != 0) { + if (!first) { + log.print(", "); + } + log.print(moves.get(move).name); + first = false; + } + } + log.println(); + } + } else { + log.print(" - "); + boolean first = true; + for (TrainerPokemon tpk : t.pokemon) { + if (!first) { + log.print(", "); + } + log.printf(tpk.toString(), itemNames[tpk.heldItem]); + first = false; } - log.printf(tpk.toString(), itemNames[tpk.heldItem]); - first = false; } log.println(); } diff --git a/src/com/sneed/pkrandom/Settings.java b/src/com/sneed/pkrandom/Settings.java index f6bf1c9..cba8808 100644 --- a/src/com/sneed/pkrandom/Settings.java +++ b/src/com/sneed/pkrandom/Settings.java @@ -188,6 +188,7 @@ public class Settings { private boolean highestLevelOnlyGetsItemsForTrainerPokemon; private boolean doubleBattleMode; private boolean shinyChance; + private boolean betterTrainerMovesets; public enum WildPokemonMod { UNCHANGED, RANDOM, AREA_MAPPING, GLOBAL_MAPPING @@ -483,7 +484,8 @@ public class Settings { trainersBlockLegendaries, trainersBlockEarlyWonderGuard, swapTrainerMegaEvos, - shinyChance)); + shinyChance, + betterTrainerMovesets)); // 28 - 31: pokemon restrictions try { @@ -788,6 +790,7 @@ public class Settings { settings.setTrainersBlockEarlyWonderGuard(restoreState(data[27], 4)); settings.setSwapTrainerMegaEvos(restoreState(data[27], 5)); settings.setShinyChance(restoreState(data[27], 6)); + settings.setBetterTrainerMovesets(restoreState(data[27], 7)); // gen restrictions int genLimit = FileFunctions.readFullIntBigEndian(data, 28); @@ -1743,6 +1746,14 @@ public class Settings { this.shinyChance = shinyChance; } + public boolean isBetterTrainerMovesets() { + return betterTrainerMovesets; + } + + public void setBetterTrainerMovesets(boolean betterTrainerMovesets) { + this.betterTrainerMovesets = betterTrainerMovesets; + } + public WildPokemonMod getWildPokemonMod() { return wildPokemonMod; } diff --git a/src/com/sneed/pkrandom/constants/GlobalConstants.java b/src/com/sneed/pkrandom/constants/GlobalConstants.java index b1506be..e9046ca 100644 --- a/src/com/sneed/pkrandom/constants/GlobalConstants.java +++ b/src/com/sneed/pkrandom/constants/GlobalConstants.java @@ -25,13 +25,9 @@ package com.sneed.pkrandom.constants; /*-- along with this program. If not, see . --*/ /*----------------------------------------------------------------------------*/ -import com.sneed.pkrandom.pokemon.Stat; -import com.sneed.pkrandom.pokemon.StatChange; +import com.sneed.pkrandom.pokemon.*; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -77,7 +73,7 @@ public class GlobalConstants { Moves.armThrust, Moves.barrage, Moves.boneRush, Moves.bulletSeed, Moves.cometPunch, Moves.doubleSlap, Moves.furyAttack, Moves.furySwipes, Moves.icicleSpear, Moves.pinMissile, Moves.rockBlast, Moves.spikeCannon, Moves.tailSlap, Moves.waterShuriken); - + public static final List doubleHitMoves = Arrays.asList( Moves.bonemerang, Moves.doubleHit, Moves.doubleIronBash, Moves.doubleKick, Moves.dragonDarts, Moves.dualChop, Moves.gearGrind, Moves.twineedle); @@ -217,6 +213,32 @@ public class GlobalConstants { Moves.magnitude, Moves.mirrorCoat, Moves.beatUp, Moves.spitUp, Moves.sheerCold ); + public static final List cannotBeObsoletedMoves = Arrays.asList( + Moves.returnTheMoveNotTheKeyword, Moves.frustration, Moves.endeavor, Moves.flail, Moves.reversal, + Moves.hiddenPower, Moves.storedPower, Moves.smellingSalts, Moves.fling, Moves.powerTrip, Moves.counter, + Moves.mirrorCoat, Moves.superFang + ); + + public static final List cannotObsoleteMoves = Arrays.asList( + Moves.gearUp, Moves.magneticFlux, Moves.focusPunch, Moves.explosion, Moves.selfDestruct, Moves.geomancy, + Moves.venomDrench + ); + + public static final List doubleBattleMoves = Arrays.asList( + Moves.followMe, Moves.helpingHand, Moves.ragePowder, Moves.afterYou, Moves.allySwitch, Moves.healPulse, + Moves.quash, Moves.ionDeluge, Moves.matBlock, Moves.aromaticMist, Moves.electrify, Moves.instruct, + Moves.spotlight, Moves.decorate, Moves.lifeDew, Moves.coaching + ); + + public static final List uselessMoves = Arrays.asList( + Moves.splash, Moves.celebrate, Moves.holdHands, Moves.teleport, + Moves.reflectType // the AI does not know how to use this move properly + ); + + public static final List requiresOtherMove = Arrays.asList( + Moves.spitUp, Moves.swallow, Moves.dreamEater, Moves.nightmare + ); + public static final int MIN_DAMAGING_MOVE_POWER = 50; public static final int HIGHEST_POKEMON_GEN = 8; diff --git a/src/com/sneed/pkrandom/newgui/Bundle.properties b/src/com/sneed/pkrandom/newgui/Bundle.properties index 80bae63..a594026 100644 --- a/src/com/sneed/pkrandom/newgui/Bundle.properties +++ b/src/com/sneed/pkrandom/newgui/Bundle.properties @@ -627,4 +627,6 @@ GUI.pbsAssignEvoStatsRandomlyCheckBox.text=Randomize Added Stats on Evolution GUI.pbsAssignEvoStatsRandomlyCheckBox.tooltipText=When a Pokemon evolves, the additional BST it gains will be assigned randomly on top of the pre-evo's stats
instead of the evolved Pokemon having the exact same ratio between its stats as the pre-evo.

Applies to both regular Evolutions and Mega Evolutions (only if "Follow Evolutions"/"Follow Mega Evolutions" is selected). GUI.tpEliteFourUniquePokemonCheckBox.text=Elite Four Have Unique Pokemon: GUI.tpEliteFourUniquePokemonCheckBox.toolTipText=Enabling this setting will ensure that Elite Four Trainers (including Champions and BW1 N/Ghetsis)
are given the specified number of unique Pokemon that will not appear on any other Trainer.
The Elite Four Trainers' highest level Pokemon are the ones that will be unique;
in the case of a level tie, it will be the Pokemon further back in the party.
Does not apply to postgame Elite Four battles. +GUI.tpBetterMovesetsCheckBox.text=Better Movesets +GUI.tpBetterMovesetsCheckBox.toolTipText=Attempts to give Trainer Pokemon better movesets by including TM moves/tutor moves/egg moves/pre-evolution moves,
and picking moves that synergize with the Pokemon's ability/stats/other moves. diff --git a/src/com/sneed/pkrandom/newgui/NewRandomizerGUI.form b/src/com/sneed/pkrandom/newgui/NewRandomizerGUI.form index 5f2abda..3aeba23 100644 --- a/src/com/sneed/pkrandom/newgui/NewRandomizerGUI.form +++ b/src/com/sneed/pkrandom/newgui/NewRandomizerGUI.form @@ -1913,6 +1913,17 @@ + + + + + + + + + + + diff --git a/src/com/sneed/pkrandom/newgui/NewRandomizerGUI.java b/src/com/sneed/pkrandom/newgui/NewRandomizerGUI.java index fdb05ad..7521dd9 100644 --- a/src/com/sneed/pkrandom/newgui/NewRandomizerGUI.java +++ b/src/com/sneed/pkrandom/newgui/NewRandomizerGUI.java @@ -293,6 +293,7 @@ public class NewRandomizerGUI { private JRadioButton peRandomEveryLevelRadioButton; private JCheckBox miscFastDistortionWorldCheckBox; private JComboBox tpComboBox; + private JCheckBox tpBetterMovesetsCheckBox; private static JFrame frame; @@ -1372,6 +1373,7 @@ public class NewRandomizerGUI { tpHighestLevelGetsItemCheckBox.setSelected(settings.isHighestLevelGetsItemsForTrainers()); tpRandomShinyTrainerPokemonCheckBox.setSelected(settings.isShinyChance()); + tpBetterMovesetsCheckBox.setSelected(settings.isBetterTrainerMovesets()); totpUnchangedRadioButton.setSelected(settings.getTotemPokemonMod() == Settings.TotemPokemonMod.UNCHANGED); totpRandomRadioButton.setSelected(settings.getTotemPokemonMod() == Settings.TotemPokemonMod.RANDOM); @@ -1600,6 +1602,7 @@ public class NewRandomizerGUI { settings.setAdditionalImportantTrainerPokemon(tpImportantTrainersCheckBox.isVisible() && tpImportantTrainersCheckBox.isSelected() ? (int)tpImportantTrainersSpinner.getValue() : 0); settings.setAdditionalRegularTrainerPokemon(tpRegularTrainersCheckBox.isVisible() && tpRegularTrainersCheckBox.isSelected() ? (int)tpRegularTrainersSpinner.getValue() : 0); settings.setShinyChance(tpRandomShinyTrainerPokemonCheckBox.isVisible() && tpRandomShinyTrainerPokemonCheckBox.isSelected()); + settings.setBetterTrainerMovesets(tpBetterMovesetsCheckBox.isVisible() && tpBetterMovesetsCheckBox.isSelected()); settings.setRandomizeHeldItemsForBossTrainerPokemon(tpBossTrainersItemsCheckBox.isVisible() && tpBossTrainersItemsCheckBox.isSelected()); settings.setRandomizeHeldItemsForImportantTrainerPokemon(tpImportantTrainersItemsCheckBox.isVisible() && tpImportantTrainersItemsCheckBox.isSelected()); settings.setRandomizeHeldItemsForRegularTrainerPokemon(tpRegularTrainersItemsCheckBox.isVisible() && tpRegularTrainersItemsCheckBox.isSelected()); @@ -2180,6 +2183,9 @@ public class NewRandomizerGUI { tpHighestLevelGetsItemCheckBox.setSelected(false); tpRandomShinyTrainerPokemonCheckBox.setVisible(true); tpRandomShinyTrainerPokemonCheckBox.setEnabled(false); + tpBetterMovesetsCheckBox.setVisible(true); + tpBetterMovesetsCheckBox.setEnabled(false); + tpBetterMovesetsCheckBox.setSelected(false); totpPanel.setVisible(true); totpAllyPanel.setVisible(true); totpAuraPanel.setVisible(true); @@ -2669,6 +2675,7 @@ public class NewRandomizerGUI { tpRandomizeTrainerClassNamesCheckBox.setEnabled(true); tpNoEarlyWonderGuardCheckBox.setVisible(pokemonGeneration >= 3); tpRandomShinyTrainerPokemonCheckBox.setVisible(pokemonGeneration >= 7); + tpBetterMovesetsCheckBox.setVisible(pokemonGeneration >= 3); totpPanel.setVisible(pokemonGeneration == 7); if (totpPanel.isVisible()) { @@ -3112,6 +3119,8 @@ public class NewRandomizerGUI { tpSwapMegaEvosCheckBox.setSelected(false); tpRandomShinyTrainerPokemonCheckBox.setEnabled(false); tpRandomShinyTrainerPokemonCheckBox.setSelected(false); + tpBetterMovesetsCheckBox.setEnabled(false); + tpBetterMovesetsCheckBox.setSelected(false); tpDoubleBattleModeCheckBox.setEnabled(false); tpDoubleBattleModeCheckBox.setSelected(false); tpBossTrainersCheckBox.setEnabled(false); @@ -3147,6 +3156,7 @@ public class NewRandomizerGUI { tpSwapMegaEvosCheckBox.setSelected(false); } tpRandomShinyTrainerPokemonCheckBox.setEnabled(true); + tpBetterMovesetsCheckBox.setEnabled(true); tpDoubleBattleModeCheckBox.setEnabled(tpDoubleBattleModeCheckBox.isVisible()); tpBossTrainersCheckBox.setEnabled(tpBossTrainersCheckBox.isVisible()); tpImportantTrainersCheckBox.setEnabled(tpImportantTrainersCheckBox.isVisible()); diff --git a/src/com/sneed/pkrandom/pokemon/Effectiveness.java b/src/com/sneed/pkrandom/pokemon/Effectiveness.java index bc8f8e3..362ca60 100644 --- a/src/com/sneed/pkrandom/pokemon/Effectiveness.java +++ b/src/com/sneed/pkrandom/pokemon/Effectiveness.java @@ -28,6 +28,7 @@ package com.sneed.pkrandom.pokemon; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; public enum Effectiveness { ZERO, HALF, NEUTRAL, DOUBLE, QUARTER, QUADRUPLE; @@ -64,7 +65,62 @@ public enum Effectiveness { return result; } + public static List notVeryEffective(Type attackingType, int generation, boolean effectivenessUpdated) { + Effectiveness[][] effectivenesses; + if (generation == 1) { + effectivenesses = effectivenessUpdated ? gen2Through5Table : gen1Table; + } else if (generation >= 2 && generation <= 5) { + effectivenesses = effectivenessUpdated ? gen6PlusTable : gen2Through5Table; + } else { + effectivenesses = gen6PlusTable; + } + List allTypes = Type.getAllTypes(generation); + + return allTypes + .stream() + .filter(defendingType -> + effectivenesses[attackingType.ordinal()][defendingType.ordinal()] == Effectiveness.HALF || + effectivenesses[attackingType.ordinal()][defendingType.ordinal()] == Effectiveness.ZERO) + .collect(Collectors.toList()); + } + + public static List superEffective(Type attackingType, int generation, boolean effectivenessUpdated) { + Effectiveness[][] effectivenesses; + if (generation == 1) { + effectivenesses = effectivenessUpdated ? gen2Through5Table : gen1Table; + } else if (generation >= 2 && generation <= 5) { + effectivenesses = effectivenessUpdated ? gen6PlusTable : gen2Through5Table; + } else { + effectivenesses = gen6PlusTable; + } + List allTypes = Type.getAllTypes(generation); + + return allTypes + .stream() + .filter(defendingType -> + effectivenesses[attackingType.ordinal()][defendingType.ordinal()] == Effectiveness.DOUBLE) + .collect(Collectors.toList()); + } + // Attacking type is the row, Defending type is the column. This corresponds to the ordinal of types. + private static final Effectiveness[][] gen1Table = { + /* NORMAL,FIGHTING, FLYING, GRASS , WATER, FIRE , ROCK , GROUND, PSYCHIC, BUG , DRAGON,ELECTRIC, GHOST , POISON, ICE */ + /*NORMAL */ {NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL, HALF, NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL, ZERO, NEUTRAL, NEUTRAL}, + /*FIGHTING*/{ DOUBLE, NEUTRAL, HALF, NEUTRAL, NEUTRAL, NEUTRAL, DOUBLE, NEUTRAL, NEUTRAL, HALF, NEUTRAL, NEUTRAL, ZERO, HALF, DOUBLE}, + /*FLYING */ {NEUTRAL, DOUBLE, NEUTRAL, DOUBLE, NEUTRAL, NEUTRAL, HALF, NEUTRAL, NEUTRAL, DOUBLE, NEUTRAL, HALF, NEUTRAL, NEUTRAL, NEUTRAL}, + /*GRASS */ {NEUTRAL, NEUTRAL, HALF, HALF, DOUBLE, HALF, DOUBLE, DOUBLE, NEUTRAL, HALF, HALF, NEUTRAL, NEUTRAL, HALF, NEUTRAL}, + /*WATER */ {NEUTRAL, NEUTRAL, NEUTRAL, HALF, HALF, DOUBLE, DOUBLE, DOUBLE, NEUTRAL, NEUTRAL, HALF, NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL}, + /*FIRE */ {NEUTRAL, NEUTRAL, NEUTRAL, DOUBLE, HALF, HALF, HALF, NEUTRAL, NEUTRAL, DOUBLE, HALF, NEUTRAL, NEUTRAL, NEUTRAL, DOUBLE}, + /*ROCK */ {NEUTRAL, HALF, DOUBLE, NEUTRAL, NEUTRAL, DOUBLE, NEUTRAL, HALF, NEUTRAL, DOUBLE, NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL, DOUBLE}, + /*GROUND */ {NEUTRAL, NEUTRAL, ZERO, HALF, NEUTRAL, DOUBLE, DOUBLE, NEUTRAL, NEUTRAL, HALF, NEUTRAL, DOUBLE, NEUTRAL, DOUBLE, NEUTRAL}, + /*PSYCHIC*/ {NEUTRAL, DOUBLE, NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL, HALF, NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL, DOUBLE, NEUTRAL}, + /*BUG */ {NEUTRAL, HALF, HALF, DOUBLE, NEUTRAL, HALF, NEUTRAL, NEUTRAL, DOUBLE, NEUTRAL, NEUTRAL, NEUTRAL, HALF, DOUBLE, NEUTRAL}, + /*DRAGON */ {NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL, DOUBLE, NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL}, + /*ELECTRIC*/{NEUTRAL, NEUTRAL, DOUBLE, HALF, DOUBLE, NEUTRAL, NEUTRAL, ZERO, NEUTRAL, NEUTRAL, HALF, HALF, NEUTRAL, NEUTRAL, NEUTRAL}, + /*GHOST */ { ZERO, NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL, ZERO, NEUTRAL, NEUTRAL, NEUTRAL, DOUBLE, NEUTRAL, NEUTRAL}, + /*POISON */ {NEUTRAL, NEUTRAL, NEUTRAL, DOUBLE, NEUTRAL, NEUTRAL, HALF, HALF, NEUTRAL, DOUBLE, NEUTRAL, NEUTRAL, HALF, HALF, NEUTRAL}, + /*ICE */ {NEUTRAL, NEUTRAL, DOUBLE, DOUBLE, HALF, NEUTRAL, NEUTRAL, DOUBLE, NEUTRAL, NEUTRAL, DOUBLE, NEUTRAL, NEUTRAL, NEUTRAL, HALF}, + }; private static final Effectiveness[][] gen2Through5Table = { /* NORMAL,FIGHTING, FLYING, GRASS , WATER, FIRE , ROCK , GROUND, PSYCHIC, BUG , DRAGON,ELECTRIC, GHOST , POISON, ICE , STEEL , DARK */ /*NORMAL */ {NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL, HALF, NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL, NEUTRAL, ZERO, NEUTRAL, NEUTRAL, HALF, NEUTRAL}, diff --git a/src/com/sneed/pkrandom/pokemon/Move.java b/src/com/sneed/pkrandom/pokemon/Move.java index 930440f..51cef26 100755 --- a/src/com/sneed/pkrandom/pokemon/Move.java +++ b/src/com/sneed/pkrandom/pokemon/Move.java @@ -24,11 +24,20 @@ package com.sneed.pkrandom.pokemon; /*-- along with this program. If not, see . --*/ /*----------------------------------------------------------------------------*/ +import com.dabomstew.pkrandom.constants.GlobalConstants; + public class Move { public class StatChange { public StatChangeType type; public int stages; public double percentChance; + + @Override + public boolean equals(Object obj) { + StatChange other = (StatChange)obj; + return this.type == other.type && this.stages == other.stages && this.percentChance == other.percentChance; + } + } public String name; @@ -68,6 +77,25 @@ public class Move { } } + public boolean hasSpecificStatChange(StatChangeType type, boolean isPositive) { + for (StatChange sc: this.statChanges) { + if (sc.type == type && (isPositive ^ sc.stages < 0)) { + return true; + } + } + return false; + } + + public boolean hasBeneficialStatChange() { + return (statChangeMoveType == StatChangeMoveType.DAMAGE_TARGET && statChanges[0].stages < 0) || + statChangeMoveType == StatChangeMoveType.DAMAGE_USER && statChanges[0].stages > 0; + } + + public boolean isGoodDamaging(int perfectAccuracy) { + return (power * hitCount) >= 2 * GlobalConstants.MIN_DAMAGING_MOVE_POWER + || ((power * hitCount) >= GlobalConstants.MIN_DAMAGING_MOVE_POWER && (hitratio >= 90 || hitratio == perfectAccuracy)); + } + public String toString() { return "#" + number + " " + name + " - Power: " + power + ", Base PP: " + pp + ", Type: " + type + ", Hit%: " + (hitratio) + ", Effect: " + effectIndex + ", Priority: " + priority; diff --git a/src/com/sneed/pkrandom/pokemon/MoveSynergy.java b/src/com/sneed/pkrandom/pokemon/MoveSynergy.java new file mode 100644 index 0000000..c612679 --- /dev/null +++ b/src/com/sneed/pkrandom/pokemon/MoveSynergy.java @@ -0,0 +1,1207 @@ +package com.sneed.pkrandom.pokemon; + +/*----------------------------------------------------------------------------*/ +/*-- MoveSynergy.java - synergies between moves, or between --*/ +/*-- abilities/stats and moves --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer ZX" by the UPR-ZX team --*/ +/*-- Originally part of "Universal Pokemon Randomizer" by sneed --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2020. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- 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 . --*/ +/*----------------------------------------------------------------------------*/ + +import com.sneed.pkrandom.constants.Abilities; +import com.sneed.pkrandom.constants.Moves; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class MoveSynergy { + + public static List getSoftAbilityMoveSynergy(int ability, List moveList, Type pkType1, Type pkType2) { + List synergisticMoves = new ArrayList<>(); + + switch(ability) { + case Abilities.drizzle: + case Abilities.primordialSea: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.type == Type.WATER && mv.category != MoveCategory.STATUS) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.drought: + case Abilities.desolateLand: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.type == Type.FIRE && mv.category != MoveCategory.STATUS) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.refrigerate: + if (pkType1 == Type.ICE || pkType2 == Type.ICE) { + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.type == Type.NORMAL && mv.category != MoveCategory.STATUS) + .map(mv -> mv.number) + .collect(Collectors.toList())); + } + break; + case Abilities.galeWings: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.type == Type.FLYING) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.pixilate: + if (pkType1 == Type.FAIRY || pkType2 == Type.FAIRY) { + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.type == Type.NORMAL && mv.category != MoveCategory.STATUS) + .map(mv -> mv.number) + .collect(Collectors.toList())); + } + break; + case Abilities.aerilate: + if (pkType1 == Type.FLYING || pkType2 == Type.FLYING) { + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.type == Type.NORMAL && mv.category != MoveCategory.STATUS) + .map(mv -> mv.number) + .collect(Collectors.toList())); + } + break; + case Abilities.darkAura: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.type == Type.DARK && mv.category != MoveCategory.STATUS) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.fairyAura: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.type == Type.FAIRY && mv.category != MoveCategory.STATUS) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.steelworker: + case Abilities.steelySpirit: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.type == Type.STEEL && mv.category != MoveCategory.STATUS) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.galvanize: + if (pkType1 == Type.ELECTRIC || pkType2 == Type.ELECTRIC) { + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.type == Type.NORMAL && mv.category != MoveCategory.STATUS) + .map(mv -> mv.number) + .collect(Collectors.toList())); + } + break; + case Abilities.electricSurge: + case Abilities.transistor: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.type == Type.ELECTRIC && mv.category != MoveCategory.STATUS) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.psychicSurge: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.type == Type.PSYCHIC && mv.category != MoveCategory.STATUS) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.grassySurge: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.type == Type.GRASS && mv.category != MoveCategory.STATUS) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.dragonsMaw: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.type == Type.DRAGON && mv.category != MoveCategory.STATUS) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + } + + return moveList + .stream() + .filter(mv -> synergisticMoves.contains(mv.number)) + .distinct() + .collect(Collectors.toList()); + } + + public static List getSoftAbilityMoveAntiSynergy(int ability, List moveList) { + List antiSynergisticMoves = new ArrayList<>(); + + switch(ability) { + case Abilities.drizzle: + antiSynergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.type == Type.FIRE && mv.category != MoveCategory.STATUS) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.drought: + antiSynergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.type == Type.WATER && mv.category != MoveCategory.STATUS) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.mistySurge: + antiSynergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.type == Type.DRAGON && mv.category != MoveCategory.STATUS) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + } + + return moveList + .stream() + .filter(mv -> antiSynergisticMoves.contains(mv.number)) + .distinct() + .collect(Collectors.toList()); + } + + public static List getHardAbilityMoveSynergy(int ability, Type pkType1, Type pkType2, List moveList, + int generation, int perfectAccuracy) { + List synergisticMoves = new ArrayList<>(); + + switch(ability) { + case Abilities.drizzle: + case Abilities.primordialSea: + synergisticMoves.add(Moves.thunder); + synergisticMoves.add(Moves.hurricane); + if (pkType1 == Type.WATER || pkType2 == Type.WATER) { + synergisticMoves.add(Moves.weatherBall); + } + break; + case Abilities.speedBoost: + synergisticMoves.add(Moves.batonPass); + synergisticMoves.add(Moves.storedPower); + synergisticMoves.add(Moves.powerTrip); + break; + case Abilities.sturdy: + if (generation >= 5) { + synergisticMoves.add(Moves.endeavor); + synergisticMoves.add(Moves.counter); + synergisticMoves.add(Moves.mirrorCoat); + synergisticMoves.add(Moves.flail); + synergisticMoves.add(Moves.reversal); + } + break; + case Abilities.sandVeil: + case Abilities.sandRush: + synergisticMoves.add(Moves.sandstorm); + break; + case Abilities.staticTheAbilityNotTheKeyword: + synergisticMoves.add(Moves.smellingSalts); + synergisticMoves.add(Moves.hex); + break; + case Abilities.compoundEyes: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.hitratio > 0 && mv.hitratio <= 80) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.ownTempo: + case Abilities.tangledFeet: + synergisticMoves.add(Moves.petalDance); + synergisticMoves.add(Moves.thrash); + synergisticMoves.add(Moves.outrage); + break; + case Abilities.shadowTag: + case Abilities.arenaTrap: + synergisticMoves.add(Moves.perishSong); + break; + case Abilities.poisonPoint: + synergisticMoves.add(Moves.venoshock); + // fallthrough + case Abilities.effectSpore: + case Abilities.flameBody: + synergisticMoves.add(Moves.hex); + break; + case Abilities.sereneGrace: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> ((mv.statChangeMoveType == StatChangeMoveType.DAMAGE_TARGET || + mv.statChangeMoveType == StatChangeMoveType.DAMAGE_USER) && + mv.statChanges[0].percentChance < 100) || + (mv.statusMoveType == StatusMoveType.DAMAGE && mv.statusPercentChance < 100) || + mv.flinchPercentChance > 0) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.swiftSwim: + case Abilities.rainDish: + case Abilities.drySkin: + case Abilities.hydration: + synergisticMoves.add(Moves.rainDance); + break; + case Abilities.chlorophyll: + case Abilities.harvest: + case Abilities.leafGuard: + synergisticMoves.add(Moves.sunnyDay); + break; + case Abilities.soundproof: + synergisticMoves.add(Moves.perishSong); + break; + case Abilities.sandStream: + if (pkType1 == Type.ROCK || pkType2 == Type.ROCK) { + synergisticMoves.add(Moves.weatherBall); + } + break; + case Abilities.earlyBird: + case Abilities.shedSkin: + synergisticMoves.add(Moves.rest); + break; + case Abilities.truant: + synergisticMoves.add(Moves.transform); + break; + case Abilities.hustle: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> (mv.statChangeMoveType == StatChangeMoveType.DAMAGE_USER || + mv.statChangeMoveType == StatChangeMoveType.NO_DAMAGE_USER) && + mv.hasSpecificStatChange(StatChangeType.ACCURACY, true)) + .map(mv -> mv.number) + .collect(Collectors.toList())); + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.category == MoveCategory.PHYSICAL && mv.hitratio == perfectAccuracy) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.guts: + synergisticMoves.add(Moves.facade); + break; + case Abilities.rockHead: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.recoilPercent > 0) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.drought: + case Abilities.desolateLand: + synergisticMoves.add(Moves.solarBeam); + synergisticMoves.add(Moves.solarBlade); + synergisticMoves.add(Moves.morningSun); + synergisticMoves.add(Moves.synthesis); + synergisticMoves.add(Moves.moonlight); + if (generation >= 5) { + synergisticMoves.add(Moves.growth); + } + if (pkType1 == Type.FIRE || pkType2 == Type.FIRE) { + synergisticMoves.add(Moves.weatherBall); + } + break; + case Abilities.ironFist: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.isPunchMove) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.snowCloak: + case Abilities.iceBody: + case Abilities.slushRush: + synergisticMoves.add(Moves.hail); + break; + case Abilities.unburden: + synergisticMoves.add(Moves.fling); + synergisticMoves.add(Moves.acrobatics); + break; + case Abilities.simple: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> (mv.statChangeMoveType == StatChangeMoveType.DAMAGE_USER || + mv.statChangeMoveType == StatChangeMoveType.NO_DAMAGE_USER) && + mv.statChanges[0].stages > 0) + .map(mv -> mv.number) + .collect(Collectors.toList())); + synergisticMoves.add(Moves.acupressure); + break; + case Abilities.adaptability: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.category != MoveCategory.STATUS && (mv.type == pkType1 || mv.type == pkType2)) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.skillLink: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.hitCount >= 3) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.sniper: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.criticalChance == CriticalChance.INCREASED || + mv.criticalChance == CriticalChance.GUARANTEED) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.magicGuard: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.recoilPercent > 0) + .map(mv -> mv.number) + .collect(Collectors.toList())); + synergisticMoves.add(Moves.mindBlown); + break; + case Abilities.stall: + synergisticMoves.add(Moves.metalBurst); + synergisticMoves.add(Moves.payback); + break; + case Abilities.superLuck: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.criticalChance == CriticalChance.INCREASED) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.analytic: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.power > 0 && mv.priority < 0) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.noGuard: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.hitratio > 0 && mv.hitratio <= 70) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.technician: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> (mv.power >= 40 && mv.power <= 60) || mv.hitCount > 1) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.slowStart: + synergisticMoves.add(Moves.transform); + synergisticMoves.add(Moves.protect); + synergisticMoves.add(Moves.detect); + synergisticMoves.add(Moves.kingsShield); + synergisticMoves.add(Moves.banefulBunker); + synergisticMoves.add(Moves.fly); + synergisticMoves.add(Moves.dig); + synergisticMoves.add(Moves.bounce); + synergisticMoves.add(Moves.dive); + break; + case Abilities.snowWarning: + synergisticMoves.add(Moves.auroraVeil); + synergisticMoves.add(Moves.blizzard); + if (pkType1 == Type.ICE || pkType2 == Type.ICE) { + synergisticMoves.add(Moves.weatherBall); + } + break; + case Abilities.reckless: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.recoilPercent > 0) + .map(mv -> mv.number) + .collect(Collectors.toList())); + synergisticMoves.add(Moves.jumpKick); + synergisticMoves.add(Moves.highJumpKick); + break; + case Abilities.badDreams: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.statusType == StatusType.SLEEP) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.sheerForce: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.statChangeMoveType == StatChangeMoveType.DAMAGE_TARGET || + (mv.statChangeMoveType == StatChangeMoveType.DAMAGE_USER && + mv.statChanges[0].stages > 0) || + mv.statusMoveType == StatusMoveType.DAMAGE || + mv.flinchPercentChance > 0) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.contrary: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.statChangeMoveType == StatChangeMoveType.DAMAGE_USER && + mv.statChanges[0].stages < 0) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.heavyMetal: + synergisticMoves.add(Moves.heatCrash); + synergisticMoves.add(Moves.heavySlam); + break; + case Abilities.moody: + synergisticMoves.add(Moves.storedPower); + synergisticMoves.add(Moves.powerTrip); + break; + case Abilities.poisonTouch: + case Abilities.toughClaws: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.makesContact) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.regenerator: + synergisticMoves.add(Moves.uTurn); + synergisticMoves.add(Moves.voltSwitch); + synergisticMoves.add(Moves.partingShot); + break; + case Abilities.prankster: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.statusMoveType == StatusMoveType.NO_DAMAGE) + .map(mv -> mv.number) + .collect(Collectors.toList())); + synergisticMoves.add(Moves.destinyBond); + synergisticMoves.add(Moves.encore); + synergisticMoves.add(Moves.reflect); + synergisticMoves.add(Moves.lightScreen); + synergisticMoves.add(Moves.grudge); + synergisticMoves.add(Moves.painSplit); + synergisticMoves.add(Moves.substitute); + break; + case Abilities.strongJaw: + synergisticMoves.add(Moves.bite); + synergisticMoves.add(Moves.crunch); + synergisticMoves.add(Moves.fireFang); + synergisticMoves.add(Moves.fishiousRend); + synergisticMoves.add(Moves.hyperFang); + synergisticMoves.add(Moves.iceFang); + synergisticMoves.add(Moves.jawLock); + synergisticMoves.add(Moves.poisonFang); + synergisticMoves.add(Moves.psychicFangs); + synergisticMoves.add(Moves.thunderFang); + break; + case Abilities.megaLauncher: + synergisticMoves.add(Moves.auraSphere); + synergisticMoves.add(Moves.darkPulse); + synergisticMoves.add(Moves.dragonPulse); + synergisticMoves.add(Moves.originPulse); + synergisticMoves.add(Moves.terrainPulse); + synergisticMoves.add(Moves.waterPulse); + break; + case Abilities.wimpOut: + case Abilities.emergencyExit: + synergisticMoves.add(Moves.fakeOut); + synergisticMoves.add(Moves.firstImpression); + break; + case Abilities.merciless: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.statusType == StatusType.POISON || mv.statusType == StatusType.TOXIC_POISON) + .map(mv -> mv.number) + .collect(Collectors.toList())); + synergisticMoves.add(Moves.banefulBunker); + break; + case Abilities.liquidVoice: + if (pkType1 == Type.WATER || pkType2 == Type.WATER) { + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.isSoundMove && mv.power > 0) + .map(mv -> mv.number) + .collect(Collectors.toList())); + } + break; + case Abilities.triage: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.absorbPercent > 0) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.surgeSurfer: + synergisticMoves.add(Moves.electricTerrain); + break; + case Abilities.corrosion: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.category == MoveCategory.STATUS && + (mv.statusType == StatusType.POISON || mv.statusType == StatusType.TOXIC_POISON)) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + } + + return moveList + .stream() + .filter(mv -> synergisticMoves.contains(mv.number)) + .distinct() + .collect(Collectors.toList()); + } + + public static List getHardAbilityMoveAntiSynergy(int ability, List moveList) { + List antiSynergisticMoves = new ArrayList<>(); + + switch(ability) { + case Abilities.primordialSea: + antiSynergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.type == Type.FIRE && + mv.category != MoveCategory.STATUS) + .map(mv -> mv.number) + .collect(Collectors.toList())); + // fallthrough + case Abilities.drizzle: + case Abilities.sandStream: + case Abilities.snowWarning: + antiSynergisticMoves.add(Moves.solarBeam); + antiSynergisticMoves.add(Moves.solarBlade); + antiSynergisticMoves.add(Moves.morningSun); + antiSynergisticMoves.add(Moves.synthesis); + antiSynergisticMoves.add(Moves.moonlight); + antiSynergisticMoves.add(Moves.rainDance); + antiSynergisticMoves.add(Moves.sunnyDay); + antiSynergisticMoves.add(Moves.hail); + antiSynergisticMoves.add(Moves.sandstorm); + break; + case Abilities.speedBoost: + antiSynergisticMoves.addAll(moveList + .stream() + .filter(mv -> (mv.statChangeMoveType == StatChangeMoveType.NO_DAMAGE_USER) && + mv.statChanges[0].type == StatChangeType.SPEED && + mv.statChanges[0].stages > 0) + .map(mv -> mv.number) + .collect(Collectors.toList())); + antiSynergisticMoves.add(Moves.psychUp); + antiSynergisticMoves.add(Moves.haze); + break; + case Abilities.desolateLand: + antiSynergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.type == Type.WATER && + mv.category != MoveCategory.STATUS) + .map(mv -> mv.number) + .collect(Collectors.toList())); + // fallthrough + case Abilities.drought: + antiSynergisticMoves.add(Moves.thunder); + antiSynergisticMoves.add(Moves.hurricane); + antiSynergisticMoves.add(Moves.rainDance); + antiSynergisticMoves.add(Moves.sunnyDay); + antiSynergisticMoves.add(Moves.hail); + antiSynergisticMoves.add(Moves.sandstorm); + break; + case Abilities.noGuard: + antiSynergisticMoves.add(Moves.lockOn); + antiSynergisticMoves.add(Moves.mindReader); + antiSynergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.hasSpecificStatChange(StatChangeType.ACCURACY, true) || + mv.hasSpecificStatChange(StatChangeType.EVASION, true) || + mv.hasSpecificStatChange(StatChangeType.EVASION, false)) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.damp: + antiSynergisticMoves.add(Moves.selfDestruct); + antiSynergisticMoves.add(Moves.explosion); + antiSynergisticMoves.add(Moves.mindBlown); + antiSynergisticMoves.add(Moves.mistyExplosion); + break; + case Abilities.insomnia: + case Abilities.vitalSpirit: + case Abilities.comatose: + case Abilities.sweetVeil: + antiSynergisticMoves.add(Moves.rest); + break; + case Abilities.airLock: + case Abilities.cloudNine: + case Abilities.deltaStream: + antiSynergisticMoves.add(Moves.rainDance); + antiSynergisticMoves.add(Moves.sunnyDay); + antiSynergisticMoves.add(Moves.sandstorm); + antiSynergisticMoves.add(Moves.hail); + break; + case Abilities.simple: + antiSynergisticMoves.addAll(moveList + .stream() + .filter(mv -> (mv.statChangeMoveType == StatChangeMoveType.DAMAGE_USER || + mv.statChangeMoveType == StatChangeMoveType.NO_DAMAGE_USER) && + mv.statChanges[0].stages < 0) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.contrary: + antiSynergisticMoves.addAll(moveList + .stream() + .filter(mv -> (mv.statChangeMoveType == StatChangeMoveType.DAMAGE_USER || + mv.statChangeMoveType == StatChangeMoveType.NO_DAMAGE_USER) && + mv.statChanges[0].stages > 0) + .map(mv -> mv.number) + .collect(Collectors.toList())); + antiSynergisticMoves.add(Moves.shellSmash); + break; + case Abilities.lightMetal: + antiSynergisticMoves.add(Moves.heatCrash); + antiSynergisticMoves.add(Moves.heavySlam); + break; + case Abilities.electricSurge: + antiSynergisticMoves.addAll(moveList + .stream() + .filter(mv -> (mv.category == MoveCategory.STATUS && mv.statusType == StatusType.SLEEP)) + .map(mv -> mv.number) + .collect(Collectors.toList())); + antiSynergisticMoves.add(Moves.rest); + break; + case Abilities.psychicSurge: + antiSynergisticMoves.addAll(moveList + .stream() + .filter(mv -> (mv.priority > 0)) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Abilities.mistySurge: + antiSynergisticMoves.addAll(moveList + .stream() + .filter(mv -> (mv.category == MoveCategory.STATUS && + (mv.statusType == StatusType.BURN || + mv.statusType == StatusType.FREEZE || + mv.statusType == StatusType.PARALYZE || + mv.statusType == StatusType.SLEEP || + mv.statusType == StatusType.POISON || + mv.statusType == StatusType.TOXIC_POISON))) + .map(mv -> mv.number) + .collect(Collectors.toList())); + antiSynergisticMoves.add(Moves.rest); + break; + case Abilities.grassySurge: + antiSynergisticMoves.add(Moves.earthquake); + antiSynergisticMoves.add(Moves.magnitude); + antiSynergisticMoves.add(Moves.bulldoze); + break; + + + } + + return moveList + .stream() + .filter(mv -> antiSynergisticMoves.contains(mv.number)) + .distinct() + .collect(Collectors.toList()); + } + + public static List getStatMoveSynergy(Pokemon pk, List moveList) { + List synergisticMoves = new ArrayList<>(); + + if ((double)pk.hp / (double)pk.bst() < 1.0/8) { + synergisticMoves.add(Moves.painSplit); + synergisticMoves.add(Moves.endeavor); + } + + if ((double)pk.hp / (double)pk.bst() >= 1.0/4) { + synergisticMoves.add(Moves.waterSpout); + synergisticMoves.add(Moves.eruption); + synergisticMoves.add(Moves.counter); + synergisticMoves.add(Moves.mirrorCoat); + } + + if (pk.attack * 2 < pk.defense) { + synergisticMoves.add(Moves.powerTrick); + } + + if ((double)(pk.attack + pk.spatk) / (double)pk.bst() < 1.0/4) { + synergisticMoves.add(Moves.powerSplit); + } + + if ((double)(pk.defense + pk.spdef) / (double)pk.bst() < 1.0/4) { + synergisticMoves.add(Moves.guardSplit); + } + + if ((double)pk.speed / (double)pk.bst() < 1.0/8) { + synergisticMoves.add(Moves.gyroBall); + } + + if ((double)pk.speed / (double)pk.bst() >= 1.0/4) { + synergisticMoves.add(Moves.electroBall); + } + + return moveList + .stream() + .filter(mv -> synergisticMoves.contains(mv.number)) + .distinct() + .collect(Collectors.toList()); + } + + public static List getStatMoveAntiSynergy(Pokemon pk, List moveList) { + List antiSynergisticMoves = new ArrayList<>(); + + if ((double)pk.hp / (double)pk.bst() >= 1.0/4) { + antiSynergisticMoves.add(Moves.painSplit); + antiSynergisticMoves.add(Moves.endeavor); + } + + if (pk.defense * 2 < pk.attack) { + antiSynergisticMoves.add(Moves.powerTrick); + } + + if ((double)(pk.attack + pk.spatk) / (double)pk.bst() >= 1.0/3) { + antiSynergisticMoves.add(Moves.powerSplit); + } + + if ((double)(pk.defense + pk.spdef) / (double)pk.bst() >= 1.0/3) { + antiSynergisticMoves.add(Moves.guardSplit); + } + + if ((double)pk.speed / (double)pk.bst() >= 1.0/4) { + antiSynergisticMoves.add(Moves.gyroBall); + } + + if ((double)pk.speed / (double)pk.bst() < 1.0/8) { + antiSynergisticMoves.add(Moves.electroBall); + } + + return moveList + .stream() + .filter(mv -> antiSynergisticMoves.contains(mv.number)) + .distinct() + .collect(Collectors.toList()); + } + + public static List getMoveSynergy(Move mv1, List moveList, int generation) { + List synergisticMoves = new ArrayList<>(); + + if ((mv1.statChangeMoveType == StatChangeMoveType.DAMAGE_TARGET && + mv1.hasSpecificStatChange(StatChangeType.SPEED, false)) || + ((mv1.statChangeMoveType == StatChangeMoveType.DAMAGE_USER || + mv1.statChangeMoveType == StatChangeMoveType.NO_DAMAGE_USER) && + mv1.hasSpecificStatChange(StatChangeType.SPEED, true))) { + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.flinchPercentChance > 0 && mv.priority == 0) + .map(mv -> mv.number) + .collect(Collectors.toList())); + } + + if (mv1.flinchPercentChance > 0 && mv1.priority == 0) { + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> (mv.statChangeMoveType == StatChangeMoveType.DAMAGE_TARGET && + mv.hasSpecificStatChange(StatChangeType.SPEED, false)) || + ((mv.statChangeMoveType == StatChangeMoveType.DAMAGE_USER || + mv.statChangeMoveType == StatChangeMoveType.NO_DAMAGE_USER) && + mv.hasSpecificStatChange(StatChangeType.SPEED, true))) + .map(mv -> mv.number) + .collect(Collectors.toList())); + } + + if (mv1.statChanges[0].stages >= 2 || mv1.statChanges[1].type != StatChangeType.NONE) { + synergisticMoves.add(Moves.batonPass); + synergisticMoves.add(Moves.storedPower); + synergisticMoves.add(Moves.powerTrip); + } + + if (mv1.statusType == StatusType.SLEEP) { + synergisticMoves.add(Moves.dreamEater); + synergisticMoves.add(Moves.nightmare); + synergisticMoves.add(Moves.hex); + } + + switch(mv1.number) { + case Moves.toxic: + synergisticMoves.add(Moves.protect); + synergisticMoves.add(Moves.detect); + synergisticMoves.add(Moves.kingsShield); + synergisticMoves.add(Moves.dig); + synergisticMoves.add(Moves.fly); + synergisticMoves.add(Moves.bounce); + synergisticMoves.add(Moves.dive); + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> (mv.isTrapMove)) + .map(mv -> mv.number) + .collect(Collectors.toList())); + // fallthrough + case Moves.poisonPowder: + case Moves.poisonGas: + case Moves.banefulBunker: + case Moves.toxicThread: + synergisticMoves.add(Moves.venoshock); + synergisticMoves.add(Moves.hex); + break; + case Moves.venoshock: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.category == MoveCategory.STATUS && + (mv.statusType == StatusType.POISON || mv.statusType == StatusType.TOXIC_POISON)) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Moves.protect: + case Moves.detect: + case Moves.kingsShield: + synergisticMoves.add(Moves.toxic); + synergisticMoves.add(Moves.leechSeed); + synergisticMoves.add(Moves.willOWisp); + break; + case Moves.batonPass: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.statChanges[0].stages >= 2 || mv.statChanges[1].type != StatChangeType.NONE) + .map(mv -> mv.number) + .collect(Collectors.toList())); + synergisticMoves.add(Moves.shellSmash); + break; + case Moves.willOWisp: + synergisticMoves.add(Moves.hex); + break; + case Moves.lockOn: + case Moves.mindReader: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> (mv.hitratio <= 50)) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Moves.sunnyDay: + synergisticMoves.add(Moves.solarBlade); + synergisticMoves.add(Moves.solarBeam); + break; + case Moves.rainDance: + synergisticMoves.add(Moves.thunder); + synergisticMoves.add(Moves.hurricane); + break; + case Moves.powerSwap: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.statChangeMoveType == StatChangeMoveType.DAMAGE_USER && + (mv.hasSpecificStatChange(StatChangeType.ATTACK, false) || + mv.hasSpecificStatChange(StatChangeType.SPECIAL_ATTACK, false))) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Moves.endure: + synergisticMoves.add(Moves.reversal); + synergisticMoves.add(Moves.flail); + synergisticMoves.add(Moves.endeavor); + break; + case Moves.endeavor: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.category != MoveCategory.STATUS && mv.priority > 0) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Moves.thunderWave: + case Moves.glare: + case Moves.stunSpore: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.flinchPercentChance > 0) + .map(mv -> mv.number) + .collect(Collectors.toList())); + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.category == MoveCategory.STATUS && mv.statusType == StatusType.CONFUSION) + .map(mv -> mv.number) + .collect(Collectors.toList())); + synergisticMoves.add(Moves.hex); + break; + case Moves.hail: + synergisticMoves.add(Moves.blizzard); + synergisticMoves.add(Moves.auroraVeil); + break; + case Moves.stockpile: + synergisticMoves.add(Moves.spitUp); + synergisticMoves.add(Moves.swallow); + break; + case Moves.spitUp: + synergisticMoves.add(Moves.stockpile); + break; + case Moves.swallow: + synergisticMoves.add(Moves.stockpile); + break; + case Moves.leechSeed: + case Moves.perishSong: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> (mv.isTrapMove)) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Moves.spikes: + case Moves.stealthRock: + case Moves.toxicSpikes: + synergisticMoves.add(Moves.roar); + synergisticMoves.add(Moves.whirlwind); + synergisticMoves.add(Moves.dragonTail); + synergisticMoves.add(Moves.circleThrow); + break; + case Moves.rest: + synergisticMoves.add(Moves.sleepTalk); + break; + case Moves.focusEnergy: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.criticalChance == CriticalChance.INCREASED) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Moves.focusPunch: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.statusMoveType == StatusMoveType.NO_DAMAGE && + mv.statusType == StatusType.SLEEP) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Moves.torment: + synergisticMoves.add(Moves.encore); + break; + case Moves.encore: + synergisticMoves.add(Moves.torment); + break; + case Moves.hex: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.statusMoveType == StatusMoveType.NO_DAMAGE && + mv.statusType != StatusType.CONFUSION) + .map(mv -> mv.number) + .collect(Collectors.toList())); + synergisticMoves.add(Moves.banefulBunker); + break; + case Moves.dreamEater: + case Moves.nightmare: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.statusMoveType == StatusMoveType.NO_DAMAGE && + mv.statusType == StatusType.SLEEP) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Moves.storedPower: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.statChanges[0].stages > 1 || mv.statChanges[1].type != StatChangeType.NONE) + .map(mv -> mv.number) + .collect(Collectors.toList())); + synergisticMoves.add(Moves.acupressure); + synergisticMoves.add(Moves.shellSmash); + break; + case Moves.swagger: + synergisticMoves.add(Moves.punishment); + break; + case Moves.punishment: + synergisticMoves.add(Moves.swagger); + break; + case Moves.shellSmash: + synergisticMoves.add(Moves.storedPower); + break; + } + + return moveList + .stream() + .filter(mv -> synergisticMoves.contains(mv.number)) + .distinct() + .collect(Collectors.toList()); + } + + public static List getSoftMoveSynergy(Move mv1, List moveList, int generation, + boolean effectivenessUpdated) { + List synergisticMoves = new ArrayList<>(); + + if (mv1.category != MoveCategory.STATUS) { + List notVeryEffective = Effectiveness.notVeryEffective(mv1.type, generation, effectivenessUpdated); + for (Type nveType: notVeryEffective) { + List superEffectiveAgainstNVE = + Effectiveness.against(nveType, null, generation, effectivenessUpdated) + .entrySet() + .stream() + .filter(entry -> entry.getValue() == Effectiveness.DOUBLE) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.category != MoveCategory.STATUS && + superEffectiveAgainstNVE.contains(mv.type)) + .map(mv -> mv.number) + .collect(Collectors.toList())); + } + } + + switch(mv1.number) { + case Moves.swordsDance: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.category == MoveCategory.PHYSICAL) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Moves.nastyPlot: + case Moves.tailGlow: + synergisticMoves.addAll(moveList + .stream() + .filter(mv -> mv.category == MoveCategory.SPECIAL) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + } + + return moveList + .stream() + .filter(mv -> synergisticMoves.contains(mv.number)) + .distinct() + .collect(Collectors.toList()); + } + + public static List getHardMoveAntiSynergy(Move mv1, List moveList) { + List antiSynergisticMoves = new ArrayList<>(); + + + if (mv1.category == MoveCategory.STATUS && mv1.statusType != StatusType.NONE) { + antiSynergisticMoves.addAll(moveList + .stream() + .filter(mv -> (mv.category == MoveCategory.STATUS && mv.statusType != StatusType.NONE && + (mv.statusType == mv1.statusType || + (mv1.statusType != StatusType.CONFUSION && mv.statusType != StatusType.CONFUSION)))) + .map(mv -> mv.number) + .collect(Collectors.toList())); + } + + switch(mv1.number) { + case Moves.protect: + antiSynergisticMoves.add(Moves.detect); + antiSynergisticMoves.add(Moves.banefulBunker); + antiSynergisticMoves.add(Moves.kingsShield); + break; + case Moves.detect: + antiSynergisticMoves.add(Moves.protect); + antiSynergisticMoves.add(Moves.banefulBunker); + antiSynergisticMoves.add(Moves.kingsShield); + break; + case Moves.kingsShield: + antiSynergisticMoves.add(Moves.protect); + antiSynergisticMoves.add(Moves.detect); + antiSynergisticMoves.add(Moves.banefulBunker); + break; + case Moves.banefulBunker: + antiSynergisticMoves.add(Moves.protect); + antiSynergisticMoves.add(Moves.detect); + antiSynergisticMoves.add(Moves.kingsShield); + break; + case Moves.returnTheMoveNotTheKeyword: + antiSynergisticMoves.add(Moves.frustration); + break; + case Moves.frustration: + antiSynergisticMoves.add(Moves.returnTheMoveNotTheKeyword); + break; + } + + if (mv1.type != null) { + switch (mv1.type) { + case FIRE: + if (mv1.category != MoveCategory.STATUS) { + antiSynergisticMoves.add(Moves.waterSport); + } + break; + case ELECTRIC: + if (mv1.category != MoveCategory.STATUS) { + antiSynergisticMoves.add(Moves.mudSport); + } + break; + } + } + + return moveList + .stream() + .filter(mv -> antiSynergisticMoves.contains(mv.number)) + .distinct() + .collect(Collectors.toList()); + } + + public static List getSoftMoveAntiSynergy(Move mv1, List moveList) { + List antiSynergisticMoves = new ArrayList<>(); + + + if (mv1.category != MoveCategory.STATUS) { + antiSynergisticMoves.addAll(moveList + .stream() + .filter(mv -> (mv.category != MoveCategory.STATUS && mv.type == mv1.type)) + .map(mv -> mv.number) + .collect(Collectors.toList())); + } + + switch (mv1.number) { + case Moves.waterSport: + antiSynergisticMoves.addAll(moveList + .stream() + .filter(mv -> (mv.category != MoveCategory.STATUS && mv.type == Type.FIRE)) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + case Moves.mudSport: + antiSynergisticMoves.addAll(moveList + .stream() + .filter(mv -> (mv.category != MoveCategory.STATUS && mv.type == Type.ELECTRIC)) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + } + + return moveList + .stream() + .filter(mv -> antiSynergisticMoves.contains(mv.number)) + .distinct() + .collect(Collectors.toList()); + } + + public static List requiresOtherMove(Move mv1, List moveList) { + List requiresMove = new ArrayList<>(); + switch(mv1.number) { + case Moves.spitUp: + case Moves.swallow: + requiresMove.add(Moves.stockpile); + break; + case Moves.dreamEater: + case Moves.nightmare: + requiresMove.addAll(moveList + .stream() + .filter(mv -> (mv.category == MoveCategory.STATUS && mv.statusType == StatusType.SLEEP)) + .map(mv -> mv.number) + .collect(Collectors.toList())); + break; + } + return moveList.stream().filter(mv -> requiresMove.contains(mv.number)).distinct().collect(Collectors.toList()); + } +} diff --git a/src/com/sneed/pkrandom/pokemon/Trainer.java b/src/com/sneed/pkrandom/pokemon/Trainer.java index 8e8a76e..7509628 100755 --- a/src/com/sneed/pkrandom/pokemon/Trainer.java +++ b/src/com/sneed/pkrandom/pokemon/Trainer.java @@ -124,6 +124,14 @@ public class Trainer implements Comparable { return (this.poketype & 2) == 2; } + public void setPokemonHaveCustomMoves(boolean haveCustomMoves) { + if (haveCustomMoves) { + this.poketype |= 1; + } else { + this.poketype = poketype & ~1; + } + } + public boolean pokemonHaveCustomMoves() { // This flag seems consistent for all gens return (this.poketype & 1) == 1; diff --git a/src/com/sneed/pkrandom/pokemon/TrainerPokemon.java b/src/com/sneed/pkrandom/pokemon/TrainerPokemon.java index f483621..9188c20 100755 --- a/src/com/sneed/pkrandom/pokemon/TrainerPokemon.java +++ b/src/com/sneed/pkrandom/pokemon/TrainerPokemon.java @@ -37,7 +37,6 @@ public class TrainerPokemon { public int abilitySlot; public int forme; public String formeSuffix = ""; - public int absolutePokeNumber = 0; public int forcedGenderFlag; public byte nature; @@ -94,7 +93,6 @@ public class TrainerPokemon { tpk.abilitySlot = abilitySlot; tpk.forme = forme; tpk.formeSuffix = formeSuffix; - tpk.absolutePokeNumber = absolutePokeNumber; tpk.resetMoves = resetMoves; diff --git a/src/com/sneed/pkrandom/pokemon/Type.java b/src/com/sneed/pkrandom/pokemon/Type.java index 0836e81..d919047 100755 --- a/src/com/sneed/pkrandom/pokemon/Type.java +++ b/src/com/sneed/pkrandom/pokemon/Type.java @@ -49,9 +49,24 @@ public enum Type { private static final List VALUES = Collections.unmodifiableList(Arrays.asList(values())); private static final int SIZE = VALUES.size(); + public static final List GEN1 = Collections.unmodifiableList(Arrays.asList(values()).subList(0, ICE.ordinal()+1)); public static final List GEN2THROUGH5 = Collections.unmodifiableList(Arrays.asList(values()).subList(0, DARK.ordinal()+1)); public static final List GEN6PLUS = Collections.unmodifiableList(Arrays.asList(values()).subList(0, FAIRY.ordinal()+1)); + public static List getAllTypes(int generation) { + switch (generation) { + case 1: + return GEN1; + case 2: + case 3: + case 4: + case 5: + return GEN2THROUGH5; + default: + return GEN6PLUS; + } + } + public static Type randomType(Random random) { return VALUES.get(random.nextInt(SIZE)); } diff --git a/src/com/sneed/pkrandom/romhandlers/AbstractRomHandler.java b/src/com/sneed/pkrandom/romhandlers/AbstractRomHandler.java index 966a071..4fb7902 100755 --- a/src/com/sneed/pkrandom/romhandlers/AbstractRomHandler.java +++ b/src/com/sneed/pkrandom/romhandlers/AbstractRomHandler.java @@ -1868,7 +1868,6 @@ public abstract class AbstractRomHandler implements RomHandler { if (distributionSetting || (mainPlaythroughSetting && mainPlaythroughTrainers.contains(t.index))) { setPlacementHistory(newPK); } - tp.absolutePokeNumber = newPK.number; tp.pokemon = newPK; setFormeForTrainerPokemon(tp, newPK); tp.abilitySlot = getRandomAbilitySlot(newPK); @@ -1928,6 +1927,7 @@ public abstract class AbstractRomHandler implements RomHandler { boolean giveToImportantPokemon = settings.isRandomizeHeldItemsForImportantTrainerPokemon(); boolean giveToRegularPokemon = settings.isRandomizeHeldItemsForRegularTrainerPokemon(); boolean highestLevelOnly = settings.isHighestLevelGetsItemsForTrainers(); + boolean betterMovesets = settings.isBetterTrainerMovesets(); List moves = this.getMoves(); Map> movesets = this.getMovesLearnt(); @@ -1958,13 +1958,25 @@ public abstract class AbstractRomHandler implements RomHandler { if (highestLevelPoke == null) { continue; // should never happen - trainer had zero pokes } - randomizeHeldItem(highestLevelPoke, settings, moves, movesets); + int[] moveset = highestLevelPoke.resetMoves ? + RomFunctions.getMovesAtLevel(getAltFormeOfPokemon( + highestLevelPoke.pokemon, highestLevelPoke.forme).number, + movesets, + highestLevelPoke.level) : + highestLevelPoke.moves; + randomizeHeldItem(highestLevelPoke, settings, moves, moveset); } else { for (TrainerPokemon tp : t.pokemon) { - randomizeHeldItem(tp, settings, moves, movesets); + int[] moveset = tp.resetMoves ? + RomFunctions.getMovesAtLevel(getAltFormeOfPokemon( + tp.pokemon, tp.forme).number, + movesets, + tp.level) : + tp.moves; + randomizeHeldItem(tp, settings, moves, moveset); if (t.requiresUniqueHeldItems) { while (!t.pokemonHaveUniqueHeldItems()) { - randomizeHeldItem(tp, settings, moves, movesets); + randomizeHeldItem(tp, settings, moves, moveset); } } } @@ -1973,7 +1985,7 @@ public abstract class AbstractRomHandler implements RomHandler { this.setTrainers(currentTrainers, false); } - private void randomizeHeldItem(TrainerPokemon tp, Settings settings, List moves, Map> movesets) { + private void randomizeHeldItem(TrainerPokemon tp, Settings settings, List moves, int[] moveset) { boolean sensibleItemsOnly = settings.isSensibleItemsOnlyForTrainers(); boolean consumableItemsOnly = settings.isConsumableItemsOnlyForTrainers(); boolean swapMegaEvolutions = settings.isSwapTrainerMegaEvos(); @@ -1985,7 +1997,7 @@ public abstract class AbstractRomHandler implements RomHandler { } List toChooseFrom; if (sensibleItemsOnly) { - toChooseFrom = getSensibleHeldItemsFor(tp, consumableItemsOnly, moves, movesets); + toChooseFrom = getSensibleHeldItemsFor(tp, consumableItemsOnly, moves, moveset); } else if (consumableItemsOnly) { toChooseFrom = getAllConsumableHeldItems(); } else { @@ -2020,7 +2032,6 @@ public abstract class AbstractRomHandler implements RomHandler { Pokemon newPokemon = fullyEvolve(tp.pokemon, t.index); if (newPokemon != tp.pokemon) { tp.pokemon = newPokemon; - tp.absolutePokeNumber = newPokemon.number; setFormeForTrainerPokemon(tp, newPokemon); tp.abilitySlot = getValidAbilitySlotFromOriginal(newPokemon, tp.abilitySlot); tp.resetMoves = true; @@ -2114,6 +2125,654 @@ public abstract class AbstractRomHandler implements RomHandler { this.setTrainers(currentTrainers, true); } + private Map> allLevelUpMoves; + private Map> allEggMoves; + private Map allTMCompat, allTutorCompat; + private List allTMMoves, allTutorMoves; + + @Override + public List getMoveSelectionPoolAtLevel(TrainerPokemon tp, boolean cyclicEvolutions) { + + List moves = getMoves(); + double eggMoveProbability = 0.1; + double preEvoMoveProbability = 0.5; + double tmMoveProbability = 0.6; + double tutorMoveProbability = 0.6; + + if (allLevelUpMoves == null) { + allLevelUpMoves = getMovesLearnt(); + } + + if (allEggMoves == null) { + allEggMoves = getEggMoves(); + } + + if (allTMCompat == null) { + allTMCompat = getTMHMCompatibility(); + } + + if (allTMMoves == null) { + allTMMoves = getTMMoves(); + } + + if (allTutorCompat == null && hasMoveTutors()) { + allTutorCompat = getMoveTutorCompatibility(); + } + + if (allTutorMoves == null) { + allTutorMoves = getMoveTutorMoves(); + } + + // Level-up Moves + List moveSelectionPoolAtLevel = allLevelUpMoves.get(getAltFormeOfPokemon(tp.pokemon, tp.forme).number) + .stream() + .filter(ml -> (ml.level <= tp.level && ml.level != 0) || (ml.level == 0 && tp.level >= 30)) + .map(ml -> moves.get(ml.move)) + .distinct() + .collect(Collectors.toList()); + + // Pre-Evo Moves + if (!cyclicEvolutions) { + Pokemon preEvo; + if (altFormesCanHaveDifferentEvolutions()) { + preEvo = getAltFormeOfPokemon(tp.pokemon, tp.forme); + } else { + preEvo = tp.pokemon; + } + while (!preEvo.evolutionsTo.isEmpty()) { + preEvo = preEvo.evolutionsTo.get(0).from; + moveSelectionPoolAtLevel.addAll(allLevelUpMoves.get(preEvo.number) + .stream() + .filter(ml -> ml.level <= tp.level) + .filter(ml -> this.random.nextDouble() < preEvoMoveProbability) + .map(ml -> moves.get(ml.move)) + .distinct() + .collect(Collectors.toList())); + } + } + + // TM Moves + boolean[] tmCompat = allTMCompat.get(getAltFormeOfPokemon(tp.pokemon, tp.forme)); + for (int tmMove: allTMMoves) { + if (tmCompat[allTMMoves.indexOf(tmMove) + 1]) { + Move thisMove = moves.get(tmMove); + if (thisMove.power > 1 && tp.level * 3 > thisMove.power * thisMove.hitCount && + this.random.nextDouble() < tmMoveProbability) { + moveSelectionPoolAtLevel.add(thisMove); + } else if ((thisMove.power <= 1 && this.random.nextInt(100) < tp.level) || + this.random.nextInt(200) < tp.level) { + moveSelectionPoolAtLevel.add(thisMove); + } + } + } + + // Move Tutor Moves + if (hasMoveTutors()) { + boolean[] tutorCompat = allTutorCompat.get(getAltFormeOfPokemon(tp.pokemon, tp.forme)); + for (int tutorMove: allTutorMoves) { + if (tutorCompat[allTutorMoves.indexOf(tutorMove) + 1]) { + Move thisMove = moves.get(tutorMove); + if (thisMove.power > 1 && tp.level * 3 > thisMove.power * thisMove.hitCount && + this.random.nextDouble() < tutorMoveProbability) { + moveSelectionPoolAtLevel.add(thisMove); + } else if ((thisMove.power <= 1 && this.random.nextInt(100) < tp.level) || + this.random.nextInt(200) < tp.level) { + moveSelectionPoolAtLevel.add(thisMove); + } + } + } + } + + // Egg Moves + if (!cyclicEvolutions) { + Pokemon firstEvo; + if (altFormesCanHaveDifferentEvolutions()) { + firstEvo = getAltFormeOfPokemon(tp.pokemon, tp.forme); + } else { + firstEvo = tp.pokemon; + } + while (!firstEvo.evolutionsTo.isEmpty()) { + firstEvo = firstEvo.evolutionsTo.get(0).from; + } + if (allEggMoves.get(firstEvo.number) != null) { + moveSelectionPoolAtLevel.addAll(allEggMoves.get(firstEvo.number) + .stream() + .filter(egm -> this.random.nextDouble() < eggMoveProbability) + .map(moves::get) + .collect(Collectors.toList())); + } + } + + + + return moveSelectionPoolAtLevel.stream().distinct().collect(Collectors.toList()); + } + + @Override + public void pickTrainerMovesets(Settings settings) { + boolean isCyclicEvolutions = settings.getEvolutionsMod() == Settings.EvolutionsMod.RANDOM_EVERY_LEVEL; + boolean doubleBattleMode = settings.isDoubleBattleMode(); + + List trainers = getTrainers(); + + for (Trainer t: trainers) { + t.setPokemonHaveCustomMoves(true); + + for (TrainerPokemon tp: t.pokemon) { + tp.resetMoves = false; + + List movesAtLevel = getMoveSelectionPoolAtLevel(tp, isCyclicEvolutions); + + movesAtLevel = trimMoveList(tp, movesAtLevel, doubleBattleMode); + + if (movesAtLevel.isEmpty()) { + continue; + } + + double trainerTypeModifier = 1; + if (t.isImportant()) { + trainerTypeModifier = 1.5; + } else if (t.isBoss()) { + trainerTypeModifier = 2; + } + double movePoolSizeModifier = movesAtLevel.size() / 10.0; + double bonusModifier = trainerTypeModifier * movePoolSizeModifier; + + double atkSpatkRatioModifier = 0.75; + double stabMoveBias = 0.25 * bonusModifier; + double hardAbilityMoveBias = 1 * bonusModifier; + double softAbilityMoveBias = 0.5 * bonusModifier; + double statBias = 0.5 * bonusModifier; + double softMoveBias = 0.25 * bonusModifier; + double hardMoveBias = 1 * bonusModifier; + double softMoveAntiBias = 0.5; + + // Add bias for STAB + + Pokemon pk = getAltFormeOfPokemon(tp.pokemon, tp.forme); + + List stabMoves = new ArrayList<>(movesAtLevel) + .stream() + .filter(mv -> mv.type == pk.primaryType && mv.category != MoveCategory.STATUS) + .collect(Collectors.toList()); + Collections.shuffle(stabMoves, this.random); + + for (int i = 0; i < stabMoveBias * stabMoves.size(); i++) { + int j = i % stabMoves.size(); + movesAtLevel.add(stabMoves.get(j)); + } + + if (pk.secondaryType != null) { + stabMoves = new ArrayList<>(movesAtLevel) + .stream() + .filter(mv -> mv.type == pk.secondaryType && mv.category != MoveCategory.STATUS) + .collect(Collectors.toList()); + Collections.shuffle(stabMoves, this.random); + + for (int i = 0; i < stabMoveBias * stabMoves.size(); i++) { + int j = i % stabMoves.size(); + movesAtLevel.add(stabMoves.get(j)); + } + } + + // Hard ability/move synergy + + List abilityMoveSynergyList = MoveSynergy.getHardAbilityMoveSynergy( + getAbilityForTrainerPokemon(tp), + pk.primaryType, + pk.secondaryType, + movesAtLevel, + generationOfPokemon(), + perfectAccuracy); + Collections.shuffle(abilityMoveSynergyList, this.random); + for (int i = 0; i < hardAbilityMoveBias * abilityMoveSynergyList.size(); i++) { + int j = i % abilityMoveSynergyList.size(); + movesAtLevel.add(abilityMoveSynergyList.get(j)); + } + + // Soft ability/move synergy + + List softAbilityMoveSynergyList = MoveSynergy.getSoftAbilityMoveSynergy( + getAbilityForTrainerPokemon(tp), + movesAtLevel, + pk.primaryType, + pk.secondaryType); + + Collections.shuffle(softAbilityMoveSynergyList, this.random); + for (int i = 0; i < softAbilityMoveBias * softAbilityMoveSynergyList.size(); i++) { + int j = i % softAbilityMoveSynergyList.size(); + movesAtLevel.add(softAbilityMoveSynergyList.get(j)); + } + + // Soft ability/move anti-synergy + + List softAbilityMoveAntiSynergyList = MoveSynergy.getSoftAbilityMoveAntiSynergy( + getAbilityForTrainerPokemon(tp), movesAtLevel); + List withoutSoftAntiSynergy = new ArrayList<>(movesAtLevel); + for (Move mv: softAbilityMoveAntiSynergyList) { + withoutSoftAntiSynergy.remove(mv); + } + if (withoutSoftAntiSynergy.size() > 0) { + movesAtLevel = withoutSoftAntiSynergy; + } + + List distinctMoveList = movesAtLevel.stream().distinct().collect(Collectors.toList()); + int movesLeft = distinctMoveList.size(); + + if (movesLeft <= 4) { + + for (int i = 0; i < 4; i++) { + if (i < movesLeft) { + tp.moves[i] = distinctMoveList.get(i).number; + } else { + tp.moves[i] = 0; + } + } + continue; + } + + // Stat/move synergy + + List statSynergyList = MoveSynergy.getStatMoveSynergy(pk, movesAtLevel); + Collections.shuffle(statSynergyList, this.random); + for (int i = 0; i < statBias * statSynergyList.size(); i++) { + int j = i % statSynergyList.size(); + movesAtLevel.add(statSynergyList.get(j)); + } + + // Stat/move anti-synergy + + List statAntiSynergyList = MoveSynergy.getStatMoveAntiSynergy(pk, movesAtLevel); + List withoutStatAntiSynergy = new ArrayList<>(movesAtLevel); + for (Move mv: statAntiSynergyList) { + withoutStatAntiSynergy.remove(mv); + } + if (withoutStatAntiSynergy.size() > 0) { + movesAtLevel = withoutStatAntiSynergy; + } + + distinctMoveList = movesAtLevel.stream().distinct().collect(Collectors.toList()); + movesLeft = distinctMoveList.size(); + + if (movesLeft <= 4) { + + for (int i = 0; i < 4; i++) { + if (i < movesLeft) { + tp.moves[i] = distinctMoveList.get(i).number; + } else { + tp.moves[i] = 0; + } + } + continue; + } + + // Add bias for atk/spatk ratio + + double atkSpatkRatio = (double)pk.attack / (double)pk.spatk; + switch(getAbilityForTrainerPokemon(tp)) { + case Abilities.hugePower: + case Abilities.purePower: + atkSpatkRatio *= 2; + break; + case Abilities.hustle: + case Abilities.gorillaTactics: + atkSpatkRatio *= 1.5; + break; + case Abilities.moxie: + atkSpatkRatio *= 1.1; + break; + case Abilities.soulHeart: + atkSpatkRatio *= 0.9; + break; + } + + List physicalMoves = new ArrayList<>(movesAtLevel) + .stream() + .filter(mv -> mv.category == MoveCategory.PHYSICAL) + .collect(Collectors.toList()); + List specialMoves = new ArrayList<>(movesAtLevel) + .stream() + .filter(mv -> mv.category == MoveCategory.SPECIAL) + .collect(Collectors.toList()); + + if (atkSpatkRatio < 1 && specialMoves.size() > 0) { + atkSpatkRatio = 1 / atkSpatkRatio; + double acceptedRatio = atkSpatkRatioModifier * atkSpatkRatio; + int additionalMoves = (int)(physicalMoves.size() * acceptedRatio) - specialMoves.size(); + for (int i = 0; i < additionalMoves; i++) { + Move mv = specialMoves.get(this.random.nextInt(specialMoves.size())); + movesAtLevel.add(mv); + } + } else if (physicalMoves.size() > 0) { + double acceptedRatio = atkSpatkRatioModifier * atkSpatkRatio; + int additionalMoves = (int)(specialMoves.size() * acceptedRatio) - physicalMoves.size(); + for (int i = 0; i < additionalMoves; i++) { + Move mv = physicalMoves.get(this.random.nextInt(physicalMoves.size())); + movesAtLevel.add(mv); + } + } + + // Pick moves + + List pickedMoves = new ArrayList<>(); + + for (int i = 1; i <= 4; i++) { + Move move; + List pickFrom; + + if (i == 1) { + pickFrom = movesAtLevel + .stream() + .filter(mv -> mv.isGoodDamaging(perfectAccuracy)) + .collect(Collectors.toList()); + if (pickFrom.isEmpty()) { + pickFrom = movesAtLevel; + } + } else { + pickFrom = movesAtLevel; + } + + if (i == 4) { + List requiresOtherMove = movesAtLevel + .stream() + .filter(mv -> GlobalConstants.requiresOtherMove.contains(mv.number)) + .distinct() + .collect(Collectors.toList()); + + for (Move dependentMove: requiresOtherMove) { + boolean hasRequiredMove = false; + for (Move requiredMove: MoveSynergy.requiresOtherMove(dependentMove, movesAtLevel)) { + if (pickedMoves.contains(requiredMove)) { + hasRequiredMove = true; + break; + } + } + if (!hasRequiredMove) { + movesAtLevel.removeAll(Collections.singletonList(dependentMove)); + } + } + } + + move = pickFrom.get(this.random.nextInt(pickFrom.size())); + pickedMoves.add(move); + + if (i == 4) { + break; + } + + movesAtLevel.removeAll(Collections.singletonList(move)); + + movesAtLevel.removeAll(MoveSynergy.getHardMoveAntiSynergy(move, movesAtLevel)); + + distinctMoveList = movesAtLevel.stream().distinct().collect(Collectors.toList()); + movesLeft = distinctMoveList.size(); + + if (movesLeft <= (4 - i)) { + pickedMoves.addAll(distinctMoveList); + break; + } + + List hardMoveSynergyList = MoveSynergy.getMoveSynergy( + move, + movesAtLevel, + generationOfPokemon()); + Collections.shuffle(hardMoveSynergyList, this.random); + for (int j = 0; j < hardMoveBias * hardMoveSynergyList.size(); j++) { + int k = j % hardMoveSynergyList.size(); + movesAtLevel.add(hardMoveSynergyList.get(k)); + } + + List softMoveSynergyList = MoveSynergy.getSoftMoveSynergy( + move, + movesAtLevel, + generationOfPokemon(), + isEffectivenessUpdated()); + Collections.shuffle(softMoveSynergyList, this.random); + for (int j = 0; j < softMoveBias * softMoveSynergyList.size(); j++) { + int k = j % softMoveSynergyList.size(); + movesAtLevel.add(softMoveSynergyList.get(k)); + } + + List softMoveAntiSynergyList = MoveSynergy.getSoftMoveAntiSynergy(move, movesAtLevel); + Collections.shuffle(softMoveAntiSynergyList, this.random); + for (int j = 0; j < softMoveAntiBias * softMoveAntiSynergyList.size(); j++) { + distinctMoveList = movesAtLevel.stream().distinct().collect(Collectors.toList()); + if (distinctMoveList.size() <= (4 - i)) { + break; + } + int k = j % softMoveAntiSynergyList.size(); + movesAtLevel.remove(softMoveAntiSynergyList.get(k)); + } + + distinctMoveList = movesAtLevel.stream().distinct().collect(Collectors.toList()); + movesLeft = distinctMoveList.size(); + + if (movesLeft <= (4 - i)) { + pickedMoves.addAll(distinctMoveList); + break; + } + } + + int movesPicked = pickedMoves.size(); + + for (int i = 0; i < 4; i++) { + if (i < movesPicked) { + tp.moves[i] = pickedMoves.get(i).number; + } else { + tp.moves[i] = 0; + } + } + } + } + setTrainers(trainers, false); + } + + private List trimMoveList(TrainerPokemon tp, List movesAtLevel, boolean doubleBattleMode) { + int movesLeft = movesAtLevel.size(); + + if (movesLeft <= 4) { + for (int i = 0; i < 4; i++) { + if (i < movesLeft) { + tp.moves[i] = movesAtLevel.get(i).number; + } else { + tp.moves[i] = 0; + } + } + return new ArrayList<>(); + } + + movesAtLevel = movesAtLevel + .stream() + .filter(mv -> !GlobalConstants.uselessMoves.contains(mv.number) && + (doubleBattleMode || !GlobalConstants.doubleBattleMoves.contains(mv.number))) + .collect(Collectors.toList()); + + movesLeft = movesAtLevel.size(); + + if (movesLeft <= 4) { + for (int i = 0; i < 4; i++) { + if (i < movesLeft) { + tp.moves[i] = movesAtLevel.get(i).number; + } else { + tp.moves[i] = 0; + } + } + return new ArrayList<>(); + } + + List obsoletedMoves = getObsoleteMoves(movesAtLevel); + + // Remove obsoleted moves + + movesAtLevel.removeAll(obsoletedMoves); + + movesLeft = movesAtLevel.size(); + + if (movesLeft <= 4) { + for (int i = 0; i < 4; i++) { + if (i < movesLeft) { + tp.moves[i] = movesAtLevel.get(i).number; + } else { + tp.moves[i] = 0; + } + } + return new ArrayList<>(); + } + + List requiresOtherMove = movesAtLevel + .stream() + .filter(mv -> GlobalConstants.requiresOtherMove.contains(mv.number)) + .collect(Collectors.toList()); + + for (Move dependentMove: requiresOtherMove) { + if (MoveSynergy.requiresOtherMove(dependentMove, movesAtLevel).isEmpty()) { + movesAtLevel.remove(dependentMove); + } + } + + movesLeft = movesAtLevel.size(); + + if (movesLeft <= 4) { + for (int i = 0; i < 4; i++) { + if (i < movesLeft) { + tp.moves[i] = movesAtLevel.get(i).number; + } else { + tp.moves[i] = 0; + } + } + return new ArrayList<>(); + } + + // Remove hard ability anti-synergy moves + + List withoutHardAntiSynergy = new ArrayList<>(movesAtLevel); + withoutHardAntiSynergy.removeAll(MoveSynergy.getHardAbilityMoveAntiSynergy( + getAbilityForTrainerPokemon(tp), + movesAtLevel)); + + if (withoutHardAntiSynergy.size() > 0) { + movesAtLevel = withoutHardAntiSynergy; + } + + movesLeft = movesAtLevel.size(); + + if (movesLeft <= 4) { + for (int i = 0; i < 4; i++) { + if (i < movesLeft) { + tp.moves[i] = movesAtLevel.get(i).number; + } else { + tp.moves[i] = 0; + } + } + return new ArrayList<>(); + } + return movesAtLevel; + } + + private List getObsoleteMoves(List movesAtLevel) { + List obsoletedMoves = new ArrayList<>(); + for (Move mv: movesAtLevel) { + if (GlobalConstants.cannotObsoleteMoves.contains(mv.number)) { + continue; + } + if (mv.power > 0) { + List obsoleteThis = movesAtLevel + .stream() + .filter(mv2 -> !GlobalConstants.cannotBeObsoletedMoves.contains(mv2.number) && + mv.type == mv2.type && + ((((mv.statChangeMoveType == mv2.statChangeMoveType && + mv.statChanges[0].equals(mv2.statChanges[0])) || + (mv2.statChangeMoveType == StatChangeMoveType.NONE_OR_UNKNOWN && + mv.hasBeneficialStatChange())) && + mv.absorbPercent >= mv2.absorbPercent && + !mv.isChargeMove && + !mv.isRechargeMove) || + mv2.power * mv2.hitCount <= 30) && + mv.hitratio >= mv2.hitratio && + mv.category == mv2.category && + mv.priority >= mv2.priority && + mv2.power > 0 && + mv.power * mv.hitCount > mv2.power * mv2.hitCount) + .collect(Collectors.toList()); + for (Move obsoleted: obsoleteThis) { + //System.out.println(obsoleted.name + " obsoleted by " + mv.name); + } + obsoletedMoves.addAll(obsoleteThis); + } else if (mv.statChangeMoveType == StatChangeMoveType.NO_DAMAGE_USER || + mv.statChangeMoveType == StatChangeMoveType.NO_DAMAGE_TARGET) { + List obsoleteThis = new ArrayList<>(); + List statChanges1 = new ArrayList<>(); + for (Move.StatChange sc: mv.statChanges) { + if (sc.type != StatChangeType.NONE) { + statChanges1.add(sc); + } + } + for (Move mv2: movesAtLevel + .stream() + .filter(otherMv -> !otherMv.equals(mv) && + otherMv.power <= 0 && + otherMv.statChangeMoveType == mv.statChangeMoveType && + (otherMv.statusType == mv.statusType || + otherMv.statusType == StatusType.NONE)) + .collect(Collectors.toList())) { + List statChanges2 = new ArrayList<>(); + for (Move.StatChange sc: mv2.statChanges) { + if (sc.type != StatChangeType.NONE) { + statChanges2.add(sc); + } + } + if (statChanges2.size() > statChanges1.size()) { + continue; + } + List statChanges1Filtered = statChanges1 + .stream() + .filter(sc -> !statChanges2.contains(sc)) + .collect(Collectors.toList()); + statChanges2.removeAll(statChanges1); + if (!statChanges1Filtered.isEmpty() && statChanges2.isEmpty()) { + if (!GlobalConstants.cannotBeObsoletedMoves.contains(mv2.number)) { + obsoleteThis.add(mv2); + } + continue; + } + if (statChanges1Filtered.isEmpty() && statChanges2.isEmpty()) { + continue; + } + boolean maybeBetter = false; + for (Move.StatChange sc1: statChanges1Filtered) { + boolean canStillBeBetter = false; + for (Move.StatChange sc2: statChanges2) { + if (sc1.type == sc2.type) { + canStillBeBetter = true; + if ((mv.statChangeMoveType == StatChangeMoveType.NO_DAMAGE_USER && sc1.stages > sc2.stages) || + (mv.statChangeMoveType == StatChangeMoveType.NO_DAMAGE_TARGET && sc1.stages < sc2.stages)) { + maybeBetter = true; + } else { + canStillBeBetter = false; + } + } + } + if (!canStillBeBetter) { + maybeBetter = false; + break; + } + } + if (maybeBetter) { + if (!GlobalConstants.cannotBeObsoletedMoves.contains(mv2.number)) { + obsoleteThis.add(mv2); + } + } + } + for (Move obsoleted: obsoleteThis) { + //System.out.println(obsoleted.name + " obsoleted by " + mv.name); + } + obsoletedMoves.addAll(obsoleteThis); + } + } + + return obsoletedMoves.stream().distinct().collect(Collectors.toList()); + } + private boolean trainerShouldNotGetBuffs(Trainer t) { return t.tag != null && (t.tag.startsWith("RIVAL1-") || t.tag.startsWith("FRIEND1-") || t.tag.endsWith("NOTSTRONG")); } @@ -2998,8 +3657,7 @@ public abstract class AbstractRomHandler implements RomHandler { } if (!GlobalConstants.bannedForDamagingMove[mv.number]) { - if ((mv.power * mv.hitCount) >= 2 * GlobalConstants.MIN_DAMAGING_MOVE_POWER - || ((mv.power * mv.hitCount) >= GlobalConstants.MIN_DAMAGING_MOVE_POWER && (mv.hitratio >= 90 || mv.hitratio == perfectAccuracy))) { + if (mv.isGoodDamaging(perfectAccuracy)) { validDamagingMoves.add(mv); if (mv.type != null) { if (!validTypeDamagingMoves.containsKey(mv.type)) { @@ -3749,9 +4407,7 @@ public abstract class AbstractRomHandler implements RomHandler { if (GlobalConstants.bannedRandomMoves[mv.number] || GlobalConstants.zMoves.contains(mv.number) || hms.contains(mv.number) || banned.contains(mv.number)) { unusableMoves.add(mv); - } else if (GlobalConstants.bannedForDamagingMove[mv.number] - || (mv.power * mv.hitCount) < GlobalConstants.MIN_DAMAGING_MOVE_POWER - || ((mv.power * mv.hitCount) < GlobalConstants.MIN_DAMAGING_MOVE_POWER * 2 && mv.hitratio < 90 && mv.hitratio != perfectAccuracy)) { + } else if (GlobalConstants.bannedForDamagingMove[mv.number] || !mv.isGoodDamaging(perfectAccuracy)) { unusableDamagingMoves.add(mv); } } @@ -4014,9 +4670,7 @@ public abstract class AbstractRomHandler implements RomHandler { if (GlobalConstants.bannedRandomMoves[mv.number] || tms.contains(mv.number) || hms.contains(mv.number) || banned.contains(mv.number) || GlobalConstants.zMoves.contains(mv.number)) { unusableMoves.add(mv); - } else if (GlobalConstants.bannedForDamagingMove[mv.number] - || (mv.power * mv.hitCount) < GlobalConstants.MIN_DAMAGING_MOVE_POWER - || ((mv.power * mv.hitCount) < GlobalConstants.MIN_DAMAGING_MOVE_POWER * 2 && mv.hitratio < 90 && mv.hitratio != perfectAccuracy)) { + } else if (GlobalConstants.bannedForDamagingMove[mv.number] || !mv.isGoodDamaging(perfectAccuracy)) { unusableDamagingMoves.add(mv); } } @@ -5993,7 +6647,6 @@ public abstract class AbstractRomHandler implements RomHandler { } } bestPoke.pokemon = starter; - bestPoke.absolutePokeNumber = starter.number; setFormeForTrainerPokemon(bestPoke,starter); bestPoke.resetMoves = true; bestPoke.abilitySlot = abilitySlot; @@ -6792,7 +7445,7 @@ public abstract class AbstractRomHandler implements RomHandler { } @Override - public List getSensibleHeldItemsFor(TrainerPokemon tp, boolean consumableOnly, List moves, Map> movesets) { + public List getSensibleHeldItemsFor(TrainerPokemon tp, boolean consumableOnly, List moves, int[] pokeMoves) { return Arrays.asList(0); } diff --git a/src/com/sneed/pkrandom/romhandlers/Gen1RomHandler.java b/src/com/sneed/pkrandom/romhandlers/Gen1RomHandler.java index 7074073..3ba0183 100755 --- a/src/com/sneed/pkrandom/romhandlers/Gen1RomHandler.java +++ b/src/com/sneed/pkrandom/romhandlers/Gen1RomHandler.java @@ -331,6 +331,7 @@ public class Gen1RomHandler extends AbstractGBCRomHandler { private SubMap[] maps; private boolean xAccNerfed; private long actualCRC32; + private boolean effectivenessUpdated; @Override public boolean detectRom(byte[] rom) { @@ -1445,6 +1446,7 @@ public class Gen1RomHandler extends AbstractGBCRomHandler { } logBlankLine(); writeTypeEffectivenessTable(typeEffectivenessTable); + effectivenessUpdated = true; } private List readTypeEffectivenessTable() { @@ -2109,6 +2111,11 @@ public class Gen1RomHandler extends AbstractGBCRomHandler { } } + @Override + public boolean isEffectivenessUpdated() { + return effectivenessUpdated; + } + private void applyBWEXPPatch() { genericIPSPatch("BWXPTweak"); } diff --git a/src/com/sneed/pkrandom/romhandlers/Gen2RomHandler.java b/src/com/sneed/pkrandom/romhandlers/Gen2RomHandler.java index 7bc9f13..1c47661 100755 --- a/src/com/sneed/pkrandom/romhandlers/Gen2RomHandler.java +++ b/src/com/sneed/pkrandom/romhandlers/Gen2RomHandler.java @@ -280,6 +280,7 @@ public class Gen2RomHandler extends AbstractGBCRomHandler { private boolean isVietCrystal; private ItemList allowedItems, nonBadItems; private long actualCRC32; + private boolean effectivenessUpdated; @Override public boolean detectRom(byte[] rom) { @@ -2201,6 +2202,11 @@ public class Gen2RomHandler extends AbstractGBCRomHandler { } } + @Override + public boolean isEffectivenessUpdated() { + return effectivenessUpdated; + } + private void randomizeCatchingTutorial() { if (romEntry.arrayEntries.containsKey("CatchingTutorialOffsets")) { // Pick a pokemon @@ -2255,6 +2261,7 @@ public class Gen2RomHandler extends AbstractGBCRomHandler { } logBlankLine(); writeTypeEffectivenessTable(typeEffectivenessTable); + effectivenessUpdated = true; } private List readTypeEffectivenessTable() { diff --git a/src/com/sneed/pkrandom/romhandlers/Gen3RomHandler.java b/src/com/sneed/pkrandom/romhandlers/Gen3RomHandler.java index 387db02..513c549 100755 --- a/src/com/sneed/pkrandom/romhandlers/Gen3RomHandler.java +++ b/src/com/sneed/pkrandom/romhandlers/Gen3RomHandler.java @@ -364,6 +364,7 @@ public class Gen3RomHandler extends AbstractGBRomHandler { private ItemList allowedItems, nonBadItems; private int pickupItemsTableOffset; private long actualCRC32; + private boolean effectivenessUpdated; @Override public boolean detectRom(byte[] rom) { @@ -4120,6 +4121,11 @@ public class Gen3RomHandler extends AbstractGBRomHandler { } } + @Override + public boolean isEffectivenessUpdated() { + return effectivenessUpdated; + } + private void randomizeCatchingTutorial() { if (romEntry.getValue("CatchingTutorialOpponentMonOffset") > 0) { int oppOffset = romEntry.getValue("CatchingTutorialOpponentMonOffset"); @@ -4235,6 +4241,7 @@ public class Gen3RomHandler extends AbstractGBRomHandler { } logBlankLine(); writeTypeEffectivenessTable(typeEffectivenessTable); + effectivenessUpdated = true; } private List readTypeEffectivenessTable() { @@ -4344,13 +4351,12 @@ public class Gen3RomHandler extends AbstractGBRomHandler { } @Override - public List getSensibleHeldItemsFor(TrainerPokemon tp, boolean consumableOnly, List moves, Map> movesets) { + public List getSensibleHeldItemsFor(TrainerPokemon tp, boolean consumableOnly, List moves, int[] pokeMoves) { List items = new ArrayList<>(); items.addAll(Gen3Constants.generalPurposeConsumableItems); if (!consumableOnly) { items.addAll(Gen3Constants.generalPurposeItems); } - int[] pokeMoves = RomFunctions.getMovesAtLevel(tp.pokemon.number, movesets, tp.level); for (int moveIdx : pokeMoves) { Move move = moves.get(moveIdx); if (move == null) { diff --git a/src/com/sneed/pkrandom/romhandlers/Gen4RomHandler.java b/src/com/sneed/pkrandom/romhandlers/Gen4RomHandler.java index 2613a4e..7632c8f 100755 --- a/src/com/sneed/pkrandom/romhandlers/Gen4RomHandler.java +++ b/src/com/sneed/pkrandom/romhandlers/Gen4RomHandler.java @@ -2844,7 +2844,6 @@ public class Gen4RomHandler extends AbstractDSRomHandler { tpk.abilitySlot = abilitySlot; tpk.forme = formnum; tpk.formeSuffix = Gen4Constants.getFormeSuffixByBaseForme(species,formnum); - tpk.absolutePokeNumber = Gen4Constants.getAbsolutePokeNumByBaseForme(species,formnum); pokeOffs += 6; if (tr.pokemonHaveItems()) { tpk.heldItem = readWord(trpoke, pokeOffs); @@ -2965,7 +2964,7 @@ public class Gen4RomHandler extends AbstractDSRomHandler { } if (tr.pokemonHaveCustomMoves()) { if (tp.resetMoves) { - int[] pokeMoves = RomFunctions.getMovesAtLevel(tp.absolutePokeNumber, movesets, tp.level); + int[] pokeMoves = RomFunctions.getMovesAtLevel(getAltFormeOfPokemon(tp.pokemon, tp.forme).number, movesets, tp.level); for (int m = 0; m < 4; m++) { writeWord(trpoke, pokeOffs + m * 2, pokeMoves[m]); } @@ -5262,6 +5261,11 @@ public class Gen4RomHandler extends AbstractDSRomHandler { } } + @Override + public boolean isEffectivenessUpdated() { + return effectivenessUpdated; + } + private void randomizeCatchingTutorial() { int opponentOffset = romEntry.getInt("CatchingTutorialOpponentMonOffset"); @@ -5685,7 +5689,7 @@ public class Gen4RomHandler extends AbstractDSRomHandler { } @Override - public List getSensibleHeldItemsFor(TrainerPokemon tp, boolean consumableOnly, List moves, Map> movesets) { + public List getSensibleHeldItemsFor(TrainerPokemon tp, boolean consumableOnly, List moves, int[] pokeMoves) { List items = new ArrayList<>(); items.addAll(Gen4Constants.generalPurposeConsumableItems); int frequencyBoostCount = 6; // Make some very good items more common, but not too common @@ -5693,7 +5697,6 @@ public class Gen4RomHandler extends AbstractDSRomHandler { frequencyBoostCount = 8; // bigger to account for larger item pool. items.addAll(Gen4Constants.generalPurposeItems); } - int[] pokeMoves = RomFunctions.getMovesAtLevel(tp.pokemon.number, movesets, tp.level); for (int moveIdx : pokeMoves) { Move move = moves.get(moveIdx); if (move == null) { diff --git a/src/com/sneed/pkrandom/romhandlers/Gen5RomHandler.java b/src/com/sneed/pkrandom/romhandlers/Gen5RomHandler.java index 26a3aee..075088c 100755 --- a/src/com/sneed/pkrandom/romhandlers/Gen5RomHandler.java +++ b/src/com/sneed/pkrandom/romhandlers/Gen5RomHandler.java @@ -1440,7 +1440,6 @@ public class Gen5RomHandler extends AbstractDSRomHandler { tpk.forcedGenderFlag = (abilityAndFlag & 0xF); tpk.forme = formnum; tpk.formeSuffix = Gen5Constants.getFormeSuffixByBaseForme(species,formnum); - tpk.absolutePokeNumber = Gen5Constants.getAbsolutePokeNumByBaseForme(species,formnum); pokeOffs += 8; if (tr.pokemonHaveItems()) { tpk.heldItem = readWord(trpoke, pokeOffs); @@ -1484,7 +1483,6 @@ public class Gen5RomHandler extends AbstractDSRomHandler { for (int move = 0; move < 4; move++) { tpk.moves[move] = readWord(pkmndata, 2 + (move*2)); } - tpk.absolutePokeNumber = Gen5Constants.getAbsolutePokeNumByBaseForme(species,0); tr.pokemon.add(tpk); currentFile++; } @@ -1585,7 +1583,7 @@ public class Gen5RomHandler extends AbstractDSRomHandler { } if (tr.pokemonHaveCustomMoves()) { if (tp.resetMoves) { - int[] pokeMoves = RomFunctions.getMovesAtLevel(tp.absolutePokeNumber, movesets, tp.level); + int[] pokeMoves = RomFunctions.getMovesAtLevel(getAltFormeOfPokemon(tp.pokemon, tp.forme).number, movesets, tp.level); for (int m = 0; m < 4; m++) { writeWord(trpoke, pokeOffs + m * 2, pokeMoves[m]); } @@ -1705,7 +1703,7 @@ public class Gen5RomHandler extends AbstractDSRomHandler { writeWord(pkmndata, 12, tp.heldItem); // handle moves if (tp.resetMoves) { - int[] pokeMoves = RomFunctions.getMovesAtLevel(tp.absolutePokeNumber, movesets, tp.level); + int[] pokeMoves = RomFunctions.getMovesAtLevel(tp.pokemon.number, movesets, tp.level); for (int m = 0; m < 4; m++) { writeWord(pkmndata, 2 + m * 2, pokeMoves[m]); } @@ -2461,6 +2459,11 @@ public class Gen5RomHandler extends AbstractDSRomHandler { } } + @Override + public boolean isEffectivenessUpdated() { + return effectivenessUpdated; + } + // Removes the free lucky egg you receive from Professor Juniper and replaces it with a gooey mulch. private void removeFreeLuckyEgg() { int scriptFileGifts = romEntry.getInt("LuckyEggScriptOffset"); @@ -4238,7 +4241,7 @@ public class Gen5RomHandler extends AbstractDSRomHandler { } @Override - public List getSensibleHeldItemsFor(TrainerPokemon tp, boolean consumableOnly, List moves, Map> movesets) { + public List getSensibleHeldItemsFor(TrainerPokemon tp, boolean consumableOnly, List moves, int[] pokeMoves) { List items = new ArrayList<>(); items.addAll(Gen5Constants.generalPurposeConsumableItems); int frequencyBoostCount = 6; // Make some very good items more common, but not too common @@ -4246,7 +4249,6 @@ public class Gen5RomHandler extends AbstractDSRomHandler { frequencyBoostCount = 8; // bigger to account for larger item pool. items.addAll(Gen5Constants.generalPurposeItems); } - int[] pokeMoves = RomFunctions.getMovesAtLevel(tp.pokemon.number, movesets, tp.level); for (int moveIdx : pokeMoves) { Move move = moves.get(moveIdx); if (move == null) { diff --git a/src/com/sneed/pkrandom/romhandlers/Gen6RomHandler.java b/src/com/sneed/pkrandom/romhandlers/Gen6RomHandler.java index 9783679..b314667 100644 --- a/src/com/sneed/pkrandom/romhandlers/Gen6RomHandler.java +++ b/src/com/sneed/pkrandom/romhandlers/Gen6RomHandler.java @@ -1920,9 +1920,6 @@ public class Gen6RomHandler extends Abstract3DSRomHandler { tpk.forcedGenderFlag = (abilityAndFlag & 0xF); tpk.forme = formnum; tpk.formeSuffix = Gen6Constants.getFormeSuffixByBaseForme(species,formnum); - tpk.absolutePokeNumber = absolutePokeNumByBaseForme - .getOrDefault(species,dummyAbsolutePokeNums) - .getOrDefault(formnum,species); pokeOffs += 8; if (tr.pokemonHaveItems()) { tpk.heldItem = readWord(trpoke, pokeOffs); @@ -2026,7 +2023,7 @@ public class Gen6RomHandler extends Abstract3DSRomHandler { } if (tr.pokemonHaveCustomMoves()) { if (tp.resetMoves) { - int[] pokeMoves = RomFunctions.getMovesAtLevel(tp.absolutePokeNumber, movesets, tp.level); + int[] pokeMoves = RomFunctions.getMovesAtLevel(getAltFormeOfPokemon(tp.pokemon, tp.forme).number, movesets, tp.level); for (int m = 0; m < 4; m++) { writeWord(trpoke, pokeOffs + m * 2, pokeMoves[m]); } @@ -2604,6 +2601,11 @@ public class Gen6RomHandler extends Abstract3DSRomHandler { } } + @Override + public boolean isEffectivenessUpdated() { + return false; + } + private void applyFastestText() { int offset = find(code, Gen6Constants.fastestTextPrefixes[0]); if (offset > 0) { @@ -4097,7 +4099,7 @@ public class Gen6RomHandler extends Abstract3DSRomHandler { } @Override - public List getSensibleHeldItemsFor(TrainerPokemon tp, boolean consumableOnly, List moves, Map> movesets) { + public List getSensibleHeldItemsFor(TrainerPokemon tp, boolean consumableOnly, List moves, int[] pokeMoves) { List items = new ArrayList<>(); items.addAll(Gen6Constants.generalPurposeConsumableItems); int frequencyBoostCount = 6; // Make some very good items more common, but not too common @@ -4105,7 +4107,6 @@ public class Gen6RomHandler extends Abstract3DSRomHandler { frequencyBoostCount = 8; // bigger to account for larger item pool. items.addAll(Gen6Constants.generalPurposeItems); } - int[] pokeMoves = RomFunctions.getMovesAtLevel(tp.pokemon.number, movesets, tp.level); int numDamagingMoves = 0; for (int moveIdx : pokeMoves) { Move move = moves.get(moveIdx); diff --git a/src/com/sneed/pkrandom/romhandlers/Gen7RomHandler.java b/src/com/sneed/pkrandom/romhandlers/Gen7RomHandler.java index d41116e..4faac9b 100644 --- a/src/com/sneed/pkrandom/romhandlers/Gen7RomHandler.java +++ b/src/com/sneed/pkrandom/romhandlers/Gen7RomHandler.java @@ -1107,7 +1107,7 @@ public class Gen7RomHandler extends Abstract3DSRomHandler { @Override public Pokemon getAltFormeOfPokemon(Pokemon pk, int forme) { int pokeNum = absolutePokeNumByBaseForme.getOrDefault(pk.number,dummyAbsolutePokeNums).getOrDefault(forme,0); - return pokeNum != 0 && !pokes[pokeNum].actuallyCosmetic ? pokes[pokeNum] : pk; + return pokeNum != 0 ? !pokes[pokeNum].actuallyCosmetic ? pokes[pokeNum] : pokes[pokeNum].baseForme : pk; } @Override @@ -1637,9 +1637,6 @@ public class Gen7RomHandler extends Abstract3DSRomHandler { tpk.pokemon = pokes[species]; tpk.forme = formnum; tpk.formeSuffix = Gen7Constants.getFormeSuffixByBaseForme(species,formnum); - tpk.absolutePokeNumber = absolutePokeNumByBaseForme - .getOrDefault(species,dummyAbsolutePokeNums) - .getOrDefault(formnum,species); pokeOffs += 20; tpk.heldItem = readWord(trpoke, pokeOffs); tpk.hasMegaStone = Gen6Constants.isMegaStone(tpk.heldItem); @@ -1727,12 +1724,12 @@ public class Gen7RomHandler extends Abstract3DSRomHandler { writeWord(trpoke, pokeOffs, tp.heldItem); pokeOffs += 4; if (tp.resetMoves) { - int[] pokeMoves = RomFunctions.getMovesAtLevel(tp.absolutePokeNumber, movesets, tp.level); + int[] pokeMoves = RomFunctions.getMovesAtLevel(getAltFormeOfPokemon(tp.pokemon, tp.forme).number, movesets, tp.level); for (int m = 0; m < 4; m++) { writeWord(trpoke, pokeOffs + m * 2, pokeMoves[m]); } if (Gen7Constants.heldZCrystals.contains(tp.heldItem)) { // Choose a new Z-Crystal at random based on the types of the Pokemon's moves - int chosenMove = this.random.nextInt(Arrays.stream(pokeMoves).filter(pk -> pk != 0).toArray().length); + int chosenMove = this.random.nextInt(Arrays.stream(pokeMoves).filter(mv -> mv != 0).toArray().length); int newZCrystal = Gen7Constants.heldZCrystals.get((int)Gen7Constants.typeToByte(moves[pokeMoves[chosenMove]].type)); writeWord(trpoke, pokeOffs - 4, newZCrystal); } @@ -1741,6 +1738,11 @@ public class Gen7RomHandler extends Abstract3DSRomHandler { writeWord(trpoke, pokeOffs + 2, tp.moves[1]); writeWord(trpoke, pokeOffs + 4, tp.moves[2]); writeWord(trpoke, pokeOffs + 6, tp.moves[3]); + if (Gen7Constants.heldZCrystals.contains(tp.heldItem)) { // Choose a new Z-Crystal at random based on the types of the Pokemon's moves + int chosenMove = this.random.nextInt(Arrays.stream(tp.moves).filter(mv -> mv != 0).toArray().length); + int newZCrystal = Gen7Constants.heldZCrystals.get((int)Gen7Constants.typeToByte(moves[tp.moves[chosenMove]].type)); + writeWord(trpoke, pokeOffs - 4, newZCrystal); + } } pokeOffs += 8; } @@ -2383,6 +2385,11 @@ public class Gen7RomHandler extends Abstract3DSRomHandler { } } + @Override + public boolean isEffectivenessUpdated() { + return false; + } + private void applyFastestText() { int offset = find(code, Gen7Constants.fastestTextPrefixes[0]); if (offset > 0) { @@ -3647,7 +3654,7 @@ public class Gen7RomHandler extends Abstract3DSRomHandler { } @Override - public List getSensibleHeldItemsFor(TrainerPokemon tp, boolean consumableOnly, List moves, Map> movesets) { + public List getSensibleHeldItemsFor(TrainerPokemon tp, boolean consumableOnly, List moves, int[] pokeMoves) { List items = new ArrayList<>(); items.addAll(Gen7Constants.generalPurposeConsumableItems); int frequencyBoostCount = 6; // Make some very good items more common, but not too common @@ -3655,7 +3662,6 @@ public class Gen7RomHandler extends Abstract3DSRomHandler { frequencyBoostCount = 8; // bigger to account for larger item pool. items.addAll(Gen7Constants.generalPurposeItems); } - int[] pokeMoves = RomFunctions.getMovesAtLevel(tp.pokemon.number, movesets, tp.level); int numDamagingMoves = 0; for (int moveIdx : pokeMoves) { Move move = moves.get(moveIdx); diff --git a/src/com/sneed/pkrandom/romhandlers/RomHandler.java b/src/com/sneed/pkrandom/romhandlers/RomHandler.java index d66cafd..aa067e7 100755 --- a/src/com/sneed/pkrandom/romhandlers/RomHandler.java +++ b/src/com/sneed/pkrandom/romhandlers/RomHandler.java @@ -251,7 +251,7 @@ public interface RomHandler { void randomizeTrainerHeldItems(Settings settings); - List getSensibleHeldItemsFor(TrainerPokemon tp, boolean consumableOnly, List moves, Map> movesets); + List getSensibleHeldItemsFor(TrainerPokemon tp, boolean consumableOnly, List moves, int[] pokeMoves); List getAllConsumableHeldItems(); @@ -269,6 +269,10 @@ public interface RomHandler { void doubleBattleMode(); + List getMoveSelectionPoolAtLevel(TrainerPokemon tp, boolean cyclicEvolutions); + + void pickTrainerMovesets(Settings settings); + // ========= // Move Data // ========= @@ -635,6 +639,8 @@ public interface RomHandler { void applyMiscTweak(MiscTweak tweak); + boolean isEffectivenessUpdated(); + void renderPlacementHistory(); // ========================== -- cgit v1.2.3