summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAjarmar <axel.jarmar@gmail.com>2022-05-08 23:52:01 +0200
committerrafa_99 <raroma09@gmail.com>2022-06-12 19:28:15 +0100
commit363b24f9153f32627802089eee7900d5749e410e (patch)
treebf170e51c43828f8030e0c15e56c30de496e3495 /src
parent76431f3f79463ecdf3e46031d596e12996d6e471 (diff)
add "better trainer movesets" setting
Diffstat (limited to 'src')
-rw-r--r--src/com/sneed/pkrandom/Randomizer.java270
-rw-r--r--src/com/sneed/pkrandom/Settings.java13
-rw-r--r--src/com/sneed/pkrandom/constants/GlobalConstants.java36
-rw-r--r--src/com/sneed/pkrandom/newgui/Bundle.properties2
-rw-r--r--src/com/sneed/pkrandom/newgui/NewRandomizerGUI.form11
-rw-r--r--src/com/sneed/pkrandom/newgui/NewRandomizerGUI.java10
-rw-r--r--src/com/sneed/pkrandom/pokemon/Effectiveness.java56
-rwxr-xr-xsrc/com/sneed/pkrandom/pokemon/Move.java28
-rw-r--r--src/com/sneed/pkrandom/pokemon/MoveSynergy.java1207
-rwxr-xr-xsrc/com/sneed/pkrandom/pokemon/Trainer.java8
-rwxr-xr-xsrc/com/sneed/pkrandom/pokemon/TrainerPokemon.java2
-rwxr-xr-xsrc/com/sneed/pkrandom/pokemon/Type.java15
-rwxr-xr-xsrc/com/sneed/pkrandom/romhandlers/AbstractRomHandler.java687
-rwxr-xr-xsrc/com/sneed/pkrandom/romhandlers/Gen1RomHandler.java7
-rwxr-xr-xsrc/com/sneed/pkrandom/romhandlers/Gen2RomHandler.java7
-rwxr-xr-xsrc/com/sneed/pkrandom/romhandlers/Gen3RomHandler.java10
-rwxr-xr-xsrc/com/sneed/pkrandom/romhandlers/Gen4RomHandler.java11
-rwxr-xr-xsrc/com/sneed/pkrandom/romhandlers/Gen5RomHandler.java14
-rw-r--r--src/com/sneed/pkrandom/romhandlers/Gen6RomHandler.java13
-rw-r--r--src/com/sneed/pkrandom/romhandlers/Gen7RomHandler.java22
-rwxr-xr-xsrc/com/sneed/pkrandom/romhandlers/RomHandler.java8
21 files changed, 2262 insertions, 175 deletions
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<Integer> 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<Integer> 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<String> originalTrainerNames, boolean trainerNamesChanged) {
-
+ private void maybeLogTrainerChanges(final PrintStream log, List<String> originalTrainerNames, boolean trainerNamesChanged, boolean logTrainerMovesets) {
log.println("--Trainers Pokemon--");
List<Trainer> 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<Move> 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 <http://www.gnu.org/licenses/>. --*/
/*----------------------------------------------------------------------------*/
-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<Integer> 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<Integer> 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<Integer> cannotObsoleteMoves = Arrays.asList(
+ Moves.gearUp, Moves.magneticFlux, Moves.focusPunch, Moves.explosion, Moves.selfDestruct, Moves.geomancy,
+ Moves.venomDrench
+ );
+
+ public static final List<Integer> 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<Integer> 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<Integer> 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=<html>When a Pokemon evolves, the additional BST it gains will be assigned randomly on top of the pre-evo's stats <br />instead of the evolved Pokemon having the exact same ratio between its stats as the pre-evo.<br /><br />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=<html>Enabling this setting will ensure that Elite Four Trainers (including Champions and BW1 N/Ghetsis) <br />are given the specified number of unique Pokemon that will not appear on any other Trainer.<br />The Elite Four Trainers' highest level Pokemon are the ones that will be unique;<br/>in the case of a level tie, it will be the Pokemon further back in the party.<br />Does not apply to postgame Elite Four battles.
+GUI.tpBetterMovesetsCheckBox.text=Better Movesets
+GUI.tpBetterMovesetsCheckBox.toolTipText=<html>Attempts to give Trainer Pokemon better movesets by including TM moves/tutor moves/egg moves/pre-evolution moves,<br />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 @@
<toolTipText resource-bundle="com/sneed/pkrandom/newgui/Bundle" key="GUI.tpHighestLevelGetsItemCheckBox.tooltip"/>
</properties>
</component>
+ <component id="21090" class="javax.swing.JCheckBox" binding="tpBetterMovesetsCheckBox">
+ <constraints>
+ <grid row="3" column="1" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ <gridbag weightx="0.0" weighty="0.0"/>
+ </constraints>
+ <properties>
+ <enabled value="false"/>
+ <text resource-bundle="com/dabomstew/pkrandom/newgui/Bundle" key="GUI.tpBetterMovesetsCheckBox.text"/>
+ <toolTipText resource-bundle="com/dabomstew/pkrandom/newgui/Bundle" key="GUI.tpBetterMovesetsCheckBox.toolTipText"/>
+ </properties>
+ </component>
</children>
</grid>
<hspacer id="66d2">
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<Type> 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<Type> 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<Type> 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<Type> 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 <http://www.gnu.org/licenses/>. --*/
/*----------------------------------------------------------------------------*/
+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 <http://www.gnu.org/licenses/>. --*/
+/*----------------------------------------------------------------------------*/
+
+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<Move> getSoftAbilityMoveSynergy(int ability, List<Move> moveList, Type pkType1, Type pkType2) {
+ List<Integer> 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<Move> getSoftAbilityMoveAntiSynergy(int ability, List<Move> moveList) {
+ List<Integer> 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<Move> getHardAbilityMoveSynergy(int ability, Type pkType1, Type pkType2, List<Move> moveList,
+ int generation, int perfectAccuracy) {
+ List<Integer> 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<Move> getHardAbilityMoveAntiSynergy(int ability, List<Move> moveList) {
+ List<Integer> 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<Move> getStatMoveSynergy(Pokemon pk, List<Move> moveList) {
+ List<Integer> 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<Move> getStatMoveAntiSynergy(Pokemon pk, List<Move> moveList) {
+ List<Integer> 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<Move> getMoveSynergy(Move mv1, List<Move> moveList, int generation) {
+ List<Integer> 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<Move> getSoftMoveSynergy(Move mv1, List<Move> moveList, int generation,
+ boolean effectivenessUpdated) {
+ List<Integer> synergisticMoves = new ArrayList<>();
+
+ if (mv1.category != MoveCategory.STATUS) {
+ List<Type> notVeryEffective = Effectiveness.notVeryEffective(mv1.type, generation, effectivenessUpdated);
+ for (Type nveType: notVeryEffective) {
+ List<Type> 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<Move> getHardMoveAntiSynergy(Move mv1, List<Move> moveList) {
+ List<Integer> 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<Move> getSoftMoveAntiSynergy(Move mv1, List<Move> moveList) {
+ List<Integer> 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<Move> requiresOtherMove(Move mv1, List<Move> moveList) {
+ List<Integer> 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<Trainer> {
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<Type> VALUES = Collections.unmodifiableList(Arrays.asList(values()));
private static final int SIZE = VALUES.size();
+ public static final List<Type> GEN1 = Collections.unmodifiableList(Arrays.asList(values()).subList(0, ICE.ordinal()+1));
public static final List<Type> GEN2THROUGH5 = Collections.unmodifiableList(Arrays.asList(values()).subList(0, DARK.ordinal()+1));
public static final List<Type> GEN6PLUS = Collections.unmodifiableList(Arrays.asList(values()).subList(0, FAIRY.ordinal()+1));
+ public static List<Type> 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<Move> moves = this.getMoves();
Map<Integer, List<MoveLearnt>> 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<Move> moves, Map<Integer, List<MoveLearnt>> movesets) {
+ private void randomizeHeldItem(TrainerPokemon tp, Settings settings, List<Move> 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<Integer> 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<Integer, List<MoveLearnt>> allLevelUpMoves;
+ private Map<Integer, List<Integer>> allEggMoves;
+ private Map<Pokemon, boolean[]> allTMCompat, allTutorCompat;
+ private List<Integer> allTMMoves, allTutorMoves;
+
+ @Override
+ public List<Move> getMoveSelectionPoolAtLevel(TrainerPokemon tp, boolean cyclicEvolutions) {
+
+ List<Move> 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<Move> 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<Trainer> trainers = getTrainers();
+
+ for (Trainer t: trainers) {
+ t.setPokemonHaveCustomMoves(true);
+
+ for (TrainerPokemon tp: t.pokemon) {
+ tp.resetMoves = false;
+
+ List<Move> 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<Move> 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<Move> 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<Move> 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<Move> softAbilityMoveAntiSynergyList = MoveSynergy.getSoftAbilityMoveAntiSynergy(
+ getAbilityForTrainerPokemon(tp), movesAtLevel);
+ List<Move> withoutSoftAntiSynergy = new ArrayList<>(movesAtLevel);
+ for (Move mv: softAbilityMoveAntiSynergyList) {
+ withoutSoftAntiSynergy.remove(mv);
+ }
+ if (withoutSoftAntiSynergy.size() > 0) {
+ movesAtLevel = withoutSoftAntiSynergy;
+ }
+
+ List<Move> 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<Move> 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<Move> statAntiSynergyList = MoveSynergy.getStatMoveAntiSynergy(pk, movesAtLevel);
+ List<Move> 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<Move> physicalMoves = new ArrayList<>(movesAtLevel)
+ .stream()
+ .filter(mv -> mv.category == MoveCategory.PHYSICAL)
+ .collect(Collectors.toList());
+ List<Move> 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<Move> pickedMoves = new ArrayList<>();
+
+ for (int i = 1; i <= 4; i++) {
+ Move move;
+ List<Move> 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<Move> 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<Move> 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<Move> 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<Move> 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<Move> trimMoveList(TrainerPokemon tp, List<Move> 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<Move> 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<Move> 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<Move> 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<Move> getObsoleteMoves(List<Move> movesAtLevel) {
+ List<Move> obsoletedMoves = new ArrayList<>();
+ for (Move mv: movesAtLevel) {
+ if (GlobalConstants.cannotObsoleteMoves.contains(mv.number)) {
+ continue;
+ }
+ if (mv.power > 0) {
+ List<Move> 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<Move> obsoleteThis = new ArrayList<>();
+ List<Move.StatChange> 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<Move.StatChange> statChanges2 = new ArrayList<>();
+ for (Move.StatChange sc: mv2.statChanges) {
+ if (sc.type != StatChangeType.NONE) {
+ statChanges2.add(sc);
+ }
+ }
+ if (statChanges2.size() > statChanges1.size()) {
+ continue;
+ }
+ List<Move.StatChange> 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<Integer> getSensibleHeldItemsFor(TrainerPokemon tp, boolean consumableOnly, List<Move> moves, Map<Integer, List<MoveLearnt>> movesets) {
+ public List<Integer> getSensibleHeldItemsFor(TrainerPokemon tp, boolean consumableOnly, List<Move> 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<TypeRelationship> 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<TypeRelationship> 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<TypeRelationship> readTypeEffectivenessTable() {
@@ -4344,13 +4351,12 @@ public class Gen3RomHandler extends AbstractGBRomHandler {
}
@Override
- public List<Integer> getSensibleHeldItemsFor(TrainerPokemon tp, boolean consumableOnly, List<Move> moves, Map<Integer, List<MoveLearnt>> movesets) {
+ public List<Integer> getSensibleHeldItemsFor(TrainerPokemon tp, boolean consumableOnly, List<Move> moves, int[] pokeMoves) {
List<Integer> 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<Integer> getSensibleHeldItemsFor(TrainerPokemon tp, boolean consumableOnly, List<Move> moves, Map<Integer, List<MoveLearnt>> movesets) {
+ public List<Integer> getSensibleHeldItemsFor(TrainerPokemon tp, boolean consumableOnly, List<Move> moves, int[] pokeMoves) {
List<Integer> 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<Integer> getSensibleHeldItemsFor(TrainerPokemon tp, boolean consumableOnly, List<Move> moves, Map<Integer, List<MoveLearnt>> movesets) {
+ public List<Integer> getSensibleHeldItemsFor(TrainerPokemon tp, boolean consumableOnly, List<Move> moves, int[] pokeMoves) {
List<Integer> 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<Integer> getSensibleHeldItemsFor(TrainerPokemon tp, boolean consumableOnly, List<Move> moves, Map<Integer, List<MoveLearnt>> movesets) {
+ public List<Integer> getSensibleHeldItemsFor(TrainerPokemon tp, boolean consumableOnly, List<Move> moves, int[] pokeMoves) {
List<Integer> 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<Integer> getSensibleHeldItemsFor(TrainerPokemon tp, boolean consumableOnly, List<Move> moves, Map<Integer, List<MoveLearnt>> movesets) {
+ public List<Integer> getSensibleHeldItemsFor(TrainerPokemon tp, boolean consumableOnly, List<Move> moves, int[] pokeMoves) {
List<Integer> 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<Integer> getSensibleHeldItemsFor(TrainerPokemon tp, boolean consumableOnly, List<Move> moves, Map<Integer, List<MoveLearnt>> movesets);
+ List<Integer> getSensibleHeldItemsFor(TrainerPokemon tp, boolean consumableOnly, List<Move> moves, int[] pokeMoves);
List<Integer> getAllConsumableHeldItems();
@@ -269,6 +269,10 @@ public interface RomHandler {
void doubleBattleMode();
+ List<Move> 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();
// ==========================