package com.sneed.pkrandom;
/*----------------------------------------------------------------------------*/
/*-- Randomizer.java - Can randomize a file based on settings. --*/
/*-- Output varies by seed. --*/
/*-- --*/
/*-- Part of "Universal Pokemon Randomizer ZX" by the UPR-ZX team --*/
/*-- Originally part of "Universal Pokemon Randomizer" by sneed --*/
/*-- Pokemon and any associated names and the like are --*/
/*-- trademark and (C) Nintendo 1996-2020. --*/
/*-- --*/
/*-- The custom code written here is licensed under the terms of the GPL: --*/
/*-- --*/
/*-- This program is free software: you can redistribute it and/or modify --*/
/*-- it under the terms of the GNU General Public License as published by --*/
/*-- the Free Software Foundation, either version 3 of the License, or --*/
/*-- (at your option) any later version. --*/
/*-- --*/
/*-- This program is distributed in the hope that it will be useful, --*/
/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/
/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/
/*-- GNU General Public License for more details. --*/
/*-- --*/
/*-- You should have received a copy of the GNU General Public License --*/
/*-- along with this program. If not, see . --*/
/*----------------------------------------------------------------------------*/
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.*;
import com.sneed.pkrandom.pokemon.*;
import com.sneed.pkrandom.romhandlers.Gen1RomHandler;
import com.sneed.pkrandom.romhandlers.RomHandler;
// Can randomize a file based on settings. Output varies by seed.
public class Randomizer {
private static final String NEWLINE = System.getProperty("line.separator");
private final Settings settings;
private final RomHandler romHandler;
private final ResourceBundle bundle;
private final boolean saveAsDirectory;
public Randomizer(Settings settings, RomHandler romHandler, ResourceBundle bundle, boolean saveAsDirectory) {
this.settings = settings;
this.romHandler = romHandler;
this.bundle = bundle;
this.saveAsDirectory = saveAsDirectory;
}
public int randomize(final String filename) {
return randomize(filename, new PrintStream(new OutputStream() {
@Override
public void write(int b) {
}
}));
}
public int randomize(final String filename, final PrintStream log) {
long seed = RandomSource.pickSeed();
// long seed = 123456789; // TESTING
return randomize(filename, log, seed);
}
public int randomize(final String filename, final PrintStream log, long seed) {
final long startTime = System.currentTimeMillis();
RandomSource.seed(seed);
int checkValue = 0;
log.println("Randomizer Version: " + Version.VERSION_STRING);
log.println("Random Seed: " + seed);
log.println("Settings String: " + Version.VERSION + settings.toString());
log.println();
// All possible changes that can be logged
boolean movesUpdated = false;
boolean movesChanged = false;
boolean movesetsChanged = false;
boolean pokemonTraitsChanged = false;
boolean startersChanged = false;
boolean evolutionsChanged = false;
boolean trainersChanged = false;
boolean trainerMovesetsChanged = false;
boolean staticsChanged = false;
boolean totemsChanged = false;
boolean wildsChanged = false;
boolean tmMovesChanged = false;
boolean moveTutorMovesChanged = false;
boolean tradesChanged = false;
boolean tmsHmsCompatChanged = false;
boolean tutorCompatChanged = false;
boolean shopsChanged = false;
// Limit Pokemon
// 1. Set Pokemon pool according to limits (or lack thereof)
// 2. If limited, remove evolutions that are outside of the pool
romHandler.setPokemonPool(settings);
if (settings.isLimitPokemon()) {
romHandler.removeEvosForPokemonPool();
}
// Move updates & data changes
// 1. Update moves to a future generation
// 2. Randomize move stats
if (settings.isUpdateMoves()) {
romHandler.initMoveUpdates();
romHandler.updateMoves(settings);
movesUpdated = true;
}
if (movesUpdated) {
logMoveUpdates(log);
}
if (settings.isRandomizeMovePowers()) {
romHandler.randomizeMovePowers();
movesChanged = true;
}
if (settings.isRandomizeMoveAccuracies()) {
romHandler.randomizeMoveAccuracies();
movesChanged = true;
}
if (settings.isRandomizeMovePPs()) {
romHandler.randomizeMovePPs();
movesChanged = true;
}
if (settings.isRandomizeMoveTypes()) {
romHandler.randomizeMoveTypes();
movesChanged = true;
}
if (settings.isRandomizeMoveCategory() && romHandler.hasPhysicalSpecialSplit()) {
romHandler.randomizeMoveCategory();
movesChanged = true;
}
// Misc Tweaks
if (settings.getCurrentMiscTweaks() != MiscTweak.NO_MISC_TWEAKS) {
romHandler.applyMiscTweaks(settings);
}
// Update base stats to a future generation
if (settings.isUpdateBaseStats()) {
romHandler.updatePokemonStats(settings);
pokemonTraitsChanged = true;
}
// Standardize EXP curves
if (settings.isStandardizeEXPCurves()) {
romHandler.standardizeEXPCurves(settings);
}
// Pokemon Types
if (settings.getTypesMod() != Settings.TypesMod.UNCHANGED) {
romHandler.randomizePokemonTypes(settings);
pokemonTraitsChanged = true;
}
// Wild Held Items
if (settings.isRandomizeWildPokemonHeldItems()) {
romHandler.randomizeWildHeldItems(settings);
pokemonTraitsChanged = true;
}
// Random Evos
// Applied after type to pick new evos based on new types.
if (settings.getEvolutionsMod() == Settings.EvolutionsMod.RANDOM) {
romHandler.randomizeEvolutions(settings);
evolutionsChanged = true;
} else if (settings.getEvolutionsMod() == Settings.EvolutionsMod.RANDOM_EVERY_LEVEL) {
romHandler.randomizeEvolutionsEveryLevel(settings);
evolutionsChanged = true;
}
if (evolutionsChanged) {
logEvolutionChanges(log);
}
// Base stat randomization
switch (settings.getBaseStatisticsMod()) {
case SHUFFLE:
romHandler.shufflePokemonStats(settings);
pokemonTraitsChanged = true;
break;
case RANDOM:
romHandler.randomizePokemonStats(settings);
pokemonTraitsChanged = true;
break;
default:
break;
}
// Abilities
if (settings.getAbilitiesMod() == Settings.AbilitiesMod.RANDOMIZE) {
romHandler.randomizeAbilities(settings);
pokemonTraitsChanged = true;
}
// Log Pokemon traits (stats, abilities, etc) if any have changed
if (pokemonTraitsChanged) {
logPokemonTraitChanges(log);
} else {
log.println("Pokemon base stats & type: unchanged" + NEWLINE);
}
for (Pokemon pkmn : romHandler.getPokemon()) {
if (pkmn != null) {
checkValue = addToCV(checkValue, pkmn.hp, pkmn.attack, pkmn.defense, pkmn.speed, pkmn.spatk,
pkmn.spdef, pkmn.ability1, pkmn.ability2, pkmn.ability3);
}
}
// Trade evolutions removal
if (settings.isChangeImpossibleEvolutions()) {
romHandler.removeImpossibleEvolutions(settings);
}
// Easier evolutions
if (settings.isMakeEvolutionsEasier()) {
romHandler.condenseLevelEvolutions(40, 30);
romHandler.makeEvolutionsEasier(settings);
}
// Remove time-based evolutions
if (settings.isRemoveTimeBasedEvolutions()) {
romHandler.removeTimeBasedEvolutions();
}
// Log everything afterwards, so that "impossible evolutions" can account for "easier evolutions"
if (settings.isChangeImpossibleEvolutions()) {
log.println("--Removing Impossible Evolutions--");
logUpdatedEvolutions(log, romHandler.getImpossibleEvoUpdates(), romHandler.getEasierEvoUpdates());
}
if (settings.isMakeEvolutionsEasier()) {
log.println("--Making Evolutions Easier--");
if (!(romHandler instanceof Gen1RomHandler)) {
log.println("Friendship evolutions now take 160 happiness (was 220).");
}
logUpdatedEvolutions(log, romHandler.getEasierEvoUpdates(), null);
}
if (settings.isRemoveTimeBasedEvolutions()) {
log.println("--Removing Timed-Based Evolutions--");
logUpdatedEvolutions(log, romHandler.getTimeBasedEvoUpdates(), null);
}
// Starter Pokemon
// Applied after type to update the strings correctly based on new types
switch(settings.getStartersMod()) {
case CUSTOM:
romHandler.customStarters(settings);
startersChanged = true;
break;
case COMPLETELY_RANDOM:
romHandler.randomizeStarters(settings);
startersChanged = true;
break;
case RANDOM_WITH_TWO_EVOLUTIONS:
romHandler.randomizeBasicTwoEvosStarters(settings);
startersChanged = true;
break;
default:
break;
}
if (settings.isRandomizeStartersHeldItems() && !(romHandler instanceof Gen1RomHandler)) {
romHandler.randomizeStarterHeldItems(settings);
}
if (startersChanged) {
logStarters(log);
}
// Move Data Log
// Placed here so it matches its position in the randomizer interface
if (movesChanged) {
logMoveChanges(log);
} else if (!movesUpdated) {
log.println("Move Data: Unchanged." + NEWLINE);
}
// Movesets
// 1. Randomize movesets
// 2. Reorder moves by damage
// Note: "Metronome only" is handled after trainers instead
if (settings.getMovesetsMod() != Settings.MovesetsMod.UNCHANGED &&
settings.getMovesetsMod() != Settings.MovesetsMod.METRONOME_ONLY) {
romHandler.randomizeMovesLearnt(settings);
romHandler.randomizeEggMoves(settings);
movesetsChanged = true;
}
if (settings.isReorderDamagingMoves()) {
romHandler.orderDamagingMovesByDamage();
movesetsChanged = true;
}
// Show the new movesets if applicable
if (movesetsChanged) {
logMovesetChanges(log);
} else if (settings.getMovesetsMod() == Settings.MovesetsMod.METRONOME_ONLY) {
log.println("Pokemon Movesets: Metronome Only." + NEWLINE);
} else {
log.println("Pokemon Movesets: Unchanged." + NEWLINE);
}
// TMs
if (!(settings.getMovesetsMod() == Settings.MovesetsMod.METRONOME_ONLY)
&& settings.getTmsMod() == Settings.TMsMod.RANDOM) {
romHandler.randomizeTMMoves(settings);
tmMovesChanged = true;
}
if (tmMovesChanged) {
checkValue = logTMMoves(log, checkValue);
} else if (settings.getMovesetsMod() == Settings.MovesetsMod.METRONOME_ONLY) {
log.println("TM Moves: Metronome Only." + NEWLINE);
} else {
log.println("TM Moves: Unchanged." + NEWLINE);
}
// TM/HM compatibility
// 1. Randomize TM/HM compatibility
// 2. Ensure levelup move sanity
// 3. Follow evolutions
// 4. Full HM compatibility
// 5. Copy to cosmetic forms
switch (settings.getTmsHmsCompatibilityMod()) {
case COMPLETELY_RANDOM:
case RANDOM_PREFER_TYPE:
romHandler.randomizeTMHMCompatibility(settings);
tmsHmsCompatChanged = true;
break;
case FULL:
romHandler.fullTMHMCompatibility();
tmsHmsCompatChanged = true;
break;
default:
break;
}
if (settings.isTmLevelUpMoveSanity()) {
romHandler.ensureTMCompatSanity();
if (settings.isTmsFollowEvolutions()) {
romHandler.ensureTMEvolutionSanity();
}
tmsHmsCompatChanged = true;
}
if (settings.isFullHMCompat()) {
romHandler.fullHMCompatibility();
tmsHmsCompatChanged = true;
}
// Copy TM/HM compatibility to cosmetic formes if it was changed at all, and log changes
if (tmsHmsCompatChanged) {
romHandler.copyTMCompatibilityToCosmeticFormes();
logTMHMCompatibility(log);
}
// Move Tutors
if (romHandler.hasMoveTutors()) {
List oldMtMoves = romHandler.getMoveTutorMoves();
if (!(settings.getMovesetsMod() == Settings.MovesetsMod.METRONOME_ONLY)
&& settings.getMoveTutorMovesMod() == Settings.MoveTutorMovesMod.RANDOM) {
romHandler.randomizeMoveTutorMoves(settings);
moveTutorMovesChanged = true;
}
if (moveTutorMovesChanged) {
checkValue = logMoveTutorMoves(log, checkValue, oldMtMoves);
} else if (settings.getMovesetsMod() == Settings.MovesetsMod.METRONOME_ONLY) {
log.println("Move Tutor Moves: Metronome Only." + NEWLINE);
} else {
log.println("Move Tutor Moves: Unchanged." + NEWLINE);
}
// Move Tutor Compatibility
// 1. Randomize MT compatibility
// 2. Ensure levelup move sanity
// 3. Follow evolutions
// 4. Copy to cosmetic forms
switch (settings.getMoveTutorsCompatibilityMod()) {
case COMPLETELY_RANDOM:
case RANDOM_PREFER_TYPE:
romHandler.randomizeMoveTutorCompatibility(settings);
tutorCompatChanged = true;
break;
case FULL:
romHandler.fullMoveTutorCompatibility();
tutorCompatChanged = true;
break;
default:
break;
}
if (settings.isTutorLevelUpMoveSanity()) {
romHandler.ensureMoveTutorCompatSanity();
if (settings.isTutorFollowEvolutions()) {
romHandler.ensureMoveTutorEvolutionSanity();
}
tutorCompatChanged = true;
}
// Copy move tutor compatibility to cosmetic formes if it was changed at all
if (tutorCompatChanged) {
romHandler.copyMoveTutorCompatibilityToCosmeticFormes();
logTutorCompatibility(log);
}
}
// Trainer Pokemon
// 1. Add extra Trainer Pokemon
// 2. Set trainers to be double battles and add extra Pokemon if necessary
// 3. Randomize Trainer Pokemon
// 4. Modify rivals to carry starters
// 5. Force Trainer Pokemon to be fully evolved
if (settings.getAdditionalRegularTrainerPokemon() > 0
|| settings.getAdditionalImportantTrainerPokemon() > 0
|| settings.getAdditionalBossTrainerPokemon() > 0) {
romHandler.addTrainerPokemon(settings);
trainersChanged = true;
}
if (settings.isDoubleBattleMode()) {
romHandler.doubleBattleMode();
trainersChanged = true;
}
switch(settings.getTrainersMod()) {
case RANDOM:
case DISTRIBUTED:
case MAINPLAYTHROUGH:
case TYPE_THEMED:
case TYPE_THEMED_ELITE4_GYMS:
romHandler.randomizeTrainerPokes(settings);
trainersChanged = true;
break;
default:
if (settings.isTrainersLevelModified()) {
romHandler.onlyChangeTrainerLevels(settings);
trainersChanged = true;
}
break;
}
if ((settings.getTrainersMod() != Settings.TrainersMod.UNCHANGED
|| settings.getStartersMod() != Settings.StartersMod.UNCHANGED)
&& settings.isRivalCarriesStarterThroughout()) {
romHandler.rivalCarriesStarter();
trainersChanged = true;
}
if (settings.isTrainersForceFullyEvolved()) {
romHandler.forceFullyEvolvedTrainerPokes(settings);
trainersChanged = true;
}
if (settings.isBetterTrainerMovesets()) {
romHandler.pickTrainerMovesets(settings);
trainerMovesetsChanged = true;
}
if (settings.isRandomizeHeldItemsForBossTrainerPokemon()
|| settings.isRandomizeHeldItemsForImportantTrainerPokemon()
|| settings.isRandomizeHeldItemsForRegularTrainerPokemon()) {
romHandler.randomizeTrainerHeldItems(settings);
trainersChanged = true;
}
List originalTrainerNames = getTrainerNames();
boolean trainerNamesChanged = false;
// Trainer names & class names randomization
if (romHandler.canChangeTrainerText()) {
if (settings.isRandomizeTrainerClassNames()) {
romHandler.randomizeTrainerClassNames(settings);
trainersChanged = true;
trainerNamesChanged = true;
}
if (settings.isRandomizeTrainerNames()) {
romHandler.randomizeTrainerNames(settings);
trainersChanged = true;
trainerNamesChanged = true;
}
}
if (trainersChanged) {
maybeLogTrainerChanges(log, originalTrainerNames, trainerNamesChanged, trainerMovesetsChanged);
} else {
log.println("Trainers: Unchanged." + NEWLINE);
}
// Apply metronome only mode now that trainers have been dealt with
if (settings.getMovesetsMod() == Settings.MovesetsMod.METRONOME_ONLY) {
romHandler.metronomeOnlyMode();
}
List trainers = romHandler.getTrainers();
for (Trainer t : trainers) {
for (TrainerPokemon tpk : t.pokemon) {
checkValue = addToCV(checkValue, tpk.level, tpk.pokemon.number);
}
}
// Static Pokemon
if (romHandler.canChangeStaticPokemon()) {
List oldStatics = romHandler.getStaticPokemon();
if (settings.getStaticPokemonMod() != Settings.StaticPokemonMod.UNCHANGED) { // Legendary for L
romHandler.randomizeStaticPokemon(settings);
staticsChanged = true;
} else if (settings.isStaticLevelModified()) {
romHandler.onlyChangeStaticLevels(settings);
staticsChanged = true;
}
if (staticsChanged) {
checkValue = logStaticPokemon(log, checkValue, oldStatics);
} else {
log.println("Static Pokemon: Unchanged." + NEWLINE);
}
}
// Totem Pokemon
if (romHandler.generationOfPokemon() == 7) {
List oldTotems = romHandler.getTotemPokemon();
if (settings.getTotemPokemonMod() != Settings.TotemPokemonMod.UNCHANGED ||
settings.getAllyPokemonMod() != Settings.AllyPokemonMod.UNCHANGED ||
settings.getAuraMod() != Settings.AuraMod.UNCHANGED ||
settings.isRandomizeTotemHeldItems() ||
settings.isTotemLevelsModified()) {
romHandler.randomizeTotemPokemon(settings);
totemsChanged = true;
}
if (totemsChanged) {
checkValue = logTotemPokemon(log, checkValue, oldTotems);
} else {
log.println("Totem Pokemon: Unchanged." + NEWLINE);
}
}
// Wild Pokemon
// 1. Update catch rates
// 2. Randomize Wild Pokemon
if (settings.isUseMinimumCatchRate()) {
romHandler.changeCatchRates(settings);
}
switch (settings.getWildPokemonMod()) {
case RANDOM:
romHandler.randomEncounters(settings);
wildsChanged = true;
break;
case AREA_MAPPING:
romHandler.area1to1Encounters(settings);
wildsChanged = true;
break;
case GLOBAL_MAPPING:
romHandler.game1to1Encounters(settings);
wildsChanged = true;
break;
default:
if (settings.isWildLevelsModified()) {
romHandler.onlyChangeWildLevels(settings);
wildsChanged = true;
}
break;
}
if (wildsChanged) {
logWildPokemonChanges(log);
} else {
log.println("Wild Pokemon: Unchanged." + NEWLINE);
}
boolean useTimeBasedEncounters = settings.isUseTimeBasedEncounters() ||
(settings.getWildPokemonMod() == Settings.WildPokemonMod.UNCHANGED && settings.isWildLevelsModified());
List encounters = romHandler.getEncounters(useTimeBasedEncounters);
for (EncounterSet es : encounters) {
for (Encounter e : es.encounters) {
checkValue = addToCV(checkValue, e.level, e.pokemon.number);
}
}
// In-game trades
List oldTrades = romHandler.getIngameTrades();
switch(settings.getInGameTradesMod()) {
case RANDOMIZE_GIVEN:
case RANDOMIZE_GIVEN_AND_REQUESTED:
romHandler.randomizeIngameTrades(settings);
tradesChanged = true;
break;
default:
break;
}
if (tradesChanged) {
logTrades(log, oldTrades);
}
// Field Items
switch(settings.getFieldItemsMod()) {
case SHUFFLE:
romHandler.shuffleFieldItems();
break;
case RANDOM:
case RANDOM_EVEN:
romHandler.randomizeFieldItems(settings);
break;
default:
break;
}
// Shops
switch(settings.getShopItemsMod()) {
case SHUFFLE:
romHandler.shuffleShopItems();
shopsChanged = true;
break;
case RANDOM:
romHandler.randomizeShopItems(settings);
shopsChanged = true;
break;
default:
break;
}
if (shopsChanged) {
logShops(log);
}
// Pickup Items
if (settings.getPickupItemsMod() == Settings.PickupItemsMod.RANDOM) {
romHandler.randomizePickupItems(settings);
logPickupItems(log);
}
// Test output for placement history
// romHandler.renderPlacementHistory();
// Intro Pokemon...
romHandler.randomizeIntroPokemon();
// Record check value?
romHandler.writeCheckValueToROM(checkValue);
// Save
if (saveAsDirectory) {
romHandler.saveRomDirectory(filename);
} else {
romHandler.saveRomFile(filename, seed);
}
// Log tail
String gameName = romHandler.getROMName();
if (romHandler.hasGameUpdateLoaded()) {
gameName = gameName + " (" + romHandler.getGameUpdateVersion() + ")";
}
log.println("------------------------------------------------------------------");
log.println("Randomization of " + gameName + " completed.");
log.println("Time elapsed: " + (System.currentTimeMillis() - startTime) + "ms");
log.println("RNG Calls: " + RandomSource.callsSinceSeed());
log.println("------------------------------------------------------------------");
log.println();
// Diagnostics
log.println("--ROM Diagnostics--");
if (!romHandler.isRomValid()) {
log.println(bundle.getString("Log.InvalidRomLoaded"));
}
romHandler.printRomDiagnostics(log);
return checkValue;
}
private int logMoveTutorMoves(PrintStream log, int checkValue, List oldMtMoves) {
log.println("--Move Tutor Moves--");
List newMtMoves = romHandler.getMoveTutorMoves();
List moves = romHandler.getMoves();
for (int i = 0; i < newMtMoves.size(); i++) {
log.printf("%-10s -> %-10s" + NEWLINE, moves.get(oldMtMoves.get(i)).name,
moves.get(newMtMoves.get(i)).name);
checkValue = addToCV(checkValue, newMtMoves.get(i));
}
log.println();
return checkValue;
}
private int logTMMoves(PrintStream log, int checkValue) {
log.println("--TM Moves--");
List tmMoves = romHandler.getTMMoves();
List moves = romHandler.getMoves();
for (int i = 0; i < tmMoves.size(); i++) {
log.printf("TM%02d %s" + NEWLINE, i + 1, moves.get(tmMoves.get(i)).name);
checkValue = addToCV(checkValue, tmMoves.get(i));
}
log.println();
return checkValue;
}
private void logTrades(PrintStream log, List oldTrades) {
log.println("--In-Game Trades--");
List newTrades = romHandler.getIngameTrades();
int size = oldTrades.size();
for (int i = 0; i < size; i++) {
IngameTrade oldT = oldTrades.get(i);
IngameTrade newT = newTrades.get(i);
log.printf("Trade %-11s -> %-11s the %-11s -> %-11s -> %-15s the %s" + NEWLINE,
oldT.requestedPokemon != null ? oldT.requestedPokemon.fullName() : "Any",
oldT.nickname, oldT.givenPokemon.fullName(),
newT.requestedPokemon != null ? newT.requestedPokemon.fullName() : "Any",
newT.nickname, newT.givenPokemon.fullName());
}
log.println();
}
private void logMovesetChanges(PrintStream log) {
log.println("--Pokemon Movesets--");
List movesets = new ArrayList<>();
Map> moveData = romHandler.getMovesLearnt();
Map> eggMoves = romHandler.getEggMoves();
List moves = romHandler.getMoves();
List pkmnList = romHandler.getPokemonInclFormes();
int i = 1;
for (Pokemon pkmn : pkmnList) {
if (pkmn == null || pkmn.actuallyCosmetic) {
continue;
}
StringBuilder evoStr = new StringBuilder();
try {
evoStr.append(" -> ").append(pkmn.evolutionsFrom.get(0).to.fullName());
} catch (Exception e) {
evoStr.append(" (no evolution)");
}
StringBuilder sb = new StringBuilder();
if (romHandler instanceof Gen1RomHandler) {
sb.append(String.format("%03d %s", i, pkmn.fullName()))
.append(evoStr).append(System.getProperty("line.separator"))
.append(String.format("HP %-3d", pkmn.hp)).append(System.getProperty("line.separator"))
.append(String.format("ATK %-3d", pkmn.attack)).append(System.getProperty("line.separator"))
.append(String.format("DEF %-3d", pkmn.defense)).append(System.getProperty("line.separator"))
.append(String.format("SPEC %-3d", pkmn.special)).append(System.getProperty("line.separator"))
.append(String.format("SPE %-3d", pkmn.speed)).append(System.getProperty("line.separator"));
} else {
sb.append(String.format("%03d %s", i, pkmn.fullName()))
.append(evoStr).append(System.getProperty("line.separator"))
.append(String.format("HP %-3d", pkmn.hp)).append(System.getProperty("line.separator"))
.append(String.format("ATK %-3d", pkmn.attack)).append(System.getProperty("line.separator"))
.append(String.format("DEF %-3d", pkmn.defense)).append(System.getProperty("line.separator"))
.append(String.format("SPA %-3d", pkmn.spatk)).append(System.getProperty("line.separator"))
.append(String.format("SPD %-3d", pkmn.spdef)).append(System.getProperty("line.separator"))
.append(String.format("SPE %-3d", pkmn.speed)).append(System.getProperty("line.separator"));
}
i++;
List data = moveData.get(pkmn.number);
for (MoveLearnt ml : data) {
try {
if (ml.level == 0) {
sb.append("Learned upon evolution: ")
.append(moves.get(ml.move).name).append(System.getProperty("line.separator"));
} else {
sb.append("Level ")
.append(String.format("%-2d", ml.level))
.append(": ")
.append(moves.get(ml.move).name).append(System.getProperty("line.separator"));
}
} catch (NullPointerException ex) {
sb.append("invalid move at level").append(ml.level);
}
}
List eggMove = eggMoves.get(pkmn.number);
if (eggMove != null && eggMove.size() != 0) {
sb.append("Egg Moves:").append(System.getProperty("line.separator"));
for (Integer move : eggMove) {
sb.append(" - ").append(moves.get(move).name).append(System.getProperty("line.separator"));
}
}
movesets.add(sb.toString());
}
Collections.sort(movesets);
for (String moveset : movesets) {
log.println(moveset);
}
log.println();
}
private void logMoveUpdates(PrintStream log) {
log.println("--Move Updates--");
List moves = romHandler.getMoves();
Map moveUpdates = romHandler.getMoveUpdates();
for (int moveID : moveUpdates.keySet()) {
boolean[] changes = moveUpdates.get(moveID);
Move mv = moves.get(moveID);
List nonTypeChanges = new ArrayList<>();
if (changes[0]) {
nonTypeChanges.add(String.format("%d power", mv.power));
}
if (changes[1]) {
nonTypeChanges.add(String.format("%d PP", mv.pp));
}
if (changes[2]) {
nonTypeChanges.add(String.format("%.00f%% accuracy", mv.hitratio));
}
String logStr = "Made " + mv.name;
// type or not?
if (changes[3]) {
logStr += " be " + mv.type + "-type";
if (nonTypeChanges.size() > 0) {
logStr += " and";
}
}
if (changes[4]) {
if (mv.category == MoveCategory.PHYSICAL) {
logStr += " a Physical move";
} else if (mv.category == MoveCategory.SPECIAL) {
logStr += " a Special move";
} else if (mv.category == MoveCategory.STATUS) {
logStr += " a Status move";
}
}
if (nonTypeChanges.size() > 0) {
logStr += " have ";
if (nonTypeChanges.size() == 3) {
logStr += nonTypeChanges.get(0) + ", " + nonTypeChanges.get(1) + " and " + nonTypeChanges.get(2);
} else if (nonTypeChanges.size() == 2) {
logStr += nonTypeChanges.get(0) + " and " + nonTypeChanges.get(1);
} else {
logStr += nonTypeChanges.get(0);
}
}
log.println(logStr);
}
log.println();
}
private void logEvolutionChanges(PrintStream log) {
log.println("--Randomized Evolutions--");
List allPokes = romHandler.getPokemonInclFormes();
for (Pokemon pk : allPokes) {
if (pk != null && !pk.actuallyCosmetic) {
int numEvos = pk.evolutionsFrom.size();
if (numEvos > 0) {
StringBuilder evoStr = new StringBuilder(pk.evolutionsFrom.get(0).toFullName());
for (int i = 1; i < numEvos; i++) {
if (i == numEvos - 1) {
evoStr.append(" and ").append(pk.evolutionsFrom.get(i).toFullName());
} else {
evoStr.append(", ").append(pk.evolutionsFrom.get(i).toFullName());
}
}
log.printf("%-15s -> %-15s" + NEWLINE, pk.fullName(), evoStr.toString());
}
}
}
log.println();
}
private void logPokemonTraitChanges(final PrintStream log) {
List allPokes = romHandler.getPokemonInclFormes();
String[] itemNames = romHandler.getItemNames();
// Log base stats & types
log.println("--Pokemon Base Stats & Types--");
if (romHandler instanceof Gen1RomHandler) {
log.println("NUM|NAME |TYPE | HP| ATK| DEF| SPE|SPEC");
for (Pokemon pkmn : allPokes) {
if (pkmn != null) {
String typeString = pkmn.primaryType == null ? "???" : pkmn.primaryType.toString();
if (pkmn.secondaryType != null) {
typeString += "/" + pkmn.secondaryType.toString();
}
log.printf("%3d|%-10s|%-17s|%4d|%4d|%4d|%4d|%4d" + NEWLINE, pkmn.number, pkmn.fullName(), typeString,
pkmn.hp, pkmn.attack, pkmn.defense, pkmn.speed, pkmn.special );
}
}
} else {
String nameSp = " ";
String nameSpFormat = "%-13s";
String abSp = " ";
String abSpFormat = "%-12s";
if (romHandler.generationOfPokemon() == 5) {
nameSp = " ";
} else if (romHandler.generationOfPokemon() == 6) {
nameSp = " ";
nameSpFormat = "%-16s";
abSp = " ";
abSpFormat = "%-14s";
} else if (romHandler.generationOfPokemon() >= 7) {
nameSp = " ";
nameSpFormat = "%-16s";
abSp = " ";
abSpFormat = "%-16s";
}
log.print("NUM|NAME" + nameSp + "|TYPE | HP| ATK| DEF|SATK|SDEF| SPD");
int abils = romHandler.abilitiesPerPokemon();
for (int i = 0; i < abils; i++) {
log.print("|ABILITY" + (i + 1) + abSp);
}
log.print("|ITEM");
log.println();
int i = 0;
for (Pokemon pkmn : allPokes) {
if (pkmn != null && !pkmn.actuallyCosmetic) {
i++;
String typeString = pkmn.primaryType == null ? "???" : pkmn.primaryType.toString();
if (pkmn.secondaryType != null) {
typeString += "/" + pkmn.secondaryType.toString();
}
log.printf("%3d|" + nameSpFormat + "|%-17s|%4d|%4d|%4d|%4d|%4d|%4d", i, pkmn.fullName(), typeString,
pkmn.hp, pkmn.attack, pkmn.defense, pkmn.spatk, pkmn.spdef, pkmn.speed);
if (abils > 0) {
log.printf("|" + abSpFormat + "|" + abSpFormat, romHandler.abilityName(pkmn.ability1),
pkmn.ability1 == pkmn.ability2 ? "--" : romHandler.abilityName(pkmn.ability2));
if (abils > 2) {
log.printf("|" + abSpFormat, romHandler.abilityName(pkmn.ability3));
}
}
log.print("|");
if (pkmn.guaranteedHeldItem > 0) {
log.print(itemNames[pkmn.guaranteedHeldItem] + " (100%)");
} else {
int itemCount = 0;
if (pkmn.commonHeldItem > 0) {
itemCount++;
log.print(itemNames[pkmn.commonHeldItem] + " (common)");
}
if (pkmn.rareHeldItem > 0) {
if (itemCount > 0) {
log.print(", ");
}
itemCount++;
log.print(itemNames[pkmn.rareHeldItem] + " (rare)");
}
if (pkmn.darkGrassHeldItem > 0) {
if (itemCount > 0) {
log.print(", ");
}
log.print(itemNames[pkmn.darkGrassHeldItem] + " (dark grass only)");
}
}
log.println();
}
}
}
log.println();
}
private void logTMHMCompatibility(final PrintStream log) {
log.println("--TM Compatibility--");
Map compat = romHandler.getTMHMCompatibility();
List tmHMs = new ArrayList<>(romHandler.getTMMoves());
tmHMs.addAll(romHandler.getHMMoves());
List moveData = romHandler.getMoves();
logCompatibility(log, compat, tmHMs, moveData, true);
}
private void logTutorCompatibility(final PrintStream log) {
log.println("--Move Tutor Compatibility--");
Map compat = romHandler.getMoveTutorCompatibility();
List tutorMoves = romHandler.getMoveTutorMoves();
List moveData = romHandler.getMoves();
logCompatibility(log, compat, tutorMoves, moveData, false);
}
private void logCompatibility(final PrintStream log, Map compat, List moveList,
List moveData, boolean includeTMNumber) {
int tmCount = romHandler.getTMCount();
for (Map.Entry entry : compat.entrySet()) {
Pokemon pkmn = entry.getKey();
if (pkmn.actuallyCosmetic) continue;
boolean[] flags = entry.getValue();
String nameSpFormat = "%-14s";
if (romHandler.generationOfPokemon() >= 6) {
nameSpFormat = "%-17s";
}
log.printf("%3d " + nameSpFormat, pkmn.number, pkmn.fullName() + " ");
for (int i = 1; i < flags.length; i++) {
String moveName = moveData.get(moveList.get(i - 1)).name;
if (moveName.length() == 0) {
moveName = "(BLANK)";
}
int moveNameLength = moveName.length();
if (flags[i]) {
if (includeTMNumber) {
if (i <= tmCount) {
log.printf("|TM%02d %" + moveNameLength + "s ", i, moveName);
} else {
log.printf("|HM%02d %" + moveNameLength + "s ", i-tmCount, moveName);
}
} else {
log.printf("|%" + moveNameLength + "s ", moveName);
}
} else {
if (includeTMNumber) {
log.printf("| %" + (moveNameLength+4) + "s ", "-");
} else {
log.printf("| %" + (moveNameLength-1) + "s ", "-");
}
}
}
log.println("|");
}
log.println("");
}
private void logUpdatedEvolutions(final PrintStream log, Set updatedEvolutions,
Set otherUpdatedEvolutions) {
for (EvolutionUpdate evo: updatedEvolutions) {
if (otherUpdatedEvolutions != null && otherUpdatedEvolutions.contains(evo)) {
log.println(evo.toString() + " (Overwritten by \"Make Evolutions Easier\", see below)");
} else {
log.println(evo.toString());
}
}
log.println();
}
private void logStarters(final PrintStream log) {
switch(settings.getStartersMod()) {
case CUSTOM:
log.println("--Custom Starters--");
break;
case COMPLETELY_RANDOM:
log.println("--Random Starters--");
break;
case RANDOM_WITH_TWO_EVOLUTIONS:
log.println("--Random 2-Evolution Starters--");
break;
default:
break;
}
List starters = romHandler.getPickedStarters();
int i = 1;
for (Pokemon starter: starters) {
log.println("Set starter " + i + " to " + starter.fullName());
i++;
}
log.println();
}
private void logWildPokemonChanges(final PrintStream log) {
log.println("--Wild Pokemon--");
boolean useTimeBasedEncounters = settings.isUseTimeBasedEncounters() ||
(settings.getWildPokemonMod() == Settings.WildPokemonMod.UNCHANGED && settings.isWildLevelsModified());
List encounters = romHandler.getEncounters(useTimeBasedEncounters);
int idx = 0;
for (EncounterSet es : encounters) {
idx++;
log.print("Set #" + idx + " ");
if (es.displayName != null) {
log.print("- " + es.displayName + " ");
}
log.print("(rate=" + es.rate + ")");
log.println();
for (Encounter e : es.encounters) {
StringBuilder sb = new StringBuilder();
if (e.isSOS) {
String stringToAppend;
switch (e.sosType) {
case RAIN:
stringToAppend = "Rain SOS: ";
break;
case HAIL:
stringToAppend = "Hail SOS: ";
break;
case SAND:
stringToAppend = "Sand SOS: ";
break;
default:
stringToAppend = " SOS: ";
break;
}
sb.append(stringToAppend);
}
sb.append(e.pokemon.fullName()).append(" Lv");
if (e.maxLevel > 0 && e.maxLevel != e.level) {
sb.append("s ").append(e.level).append("-").append(e.maxLevel);
} else {
sb.append(e.level);
}
String whitespaceFormat = romHandler.generationOfPokemon() == 7 ? "%-31s" : "%-25s";
log.print(String.format(whitespaceFormat, sb));
StringBuilder sb2 = new StringBuilder();
if (romHandler instanceof Gen1RomHandler) {
sb2.append(String.format("HP %-3d ATK %-3d DEF %-3d SPECIAL %-3d SPEED %-3d", e.pokemon.hp, e.pokemon.attack, e.pokemon.defense, e.pokemon.special, e.pokemon.speed));
} else {
sb2.append(String.format("HP %-3d ATK %-3d DEF %-3d SPATK %-3d SPDEF %-3d SPEED %-3d", e.pokemon.hp, e.pokemon.attack, e.pokemon.defense, e.pokemon.spatk, e.pokemon.spdef, e.pokemon.speed));
}
log.print(sb2);
log.println();
}
log.println();
}
log.println();
}
private void maybeLogTrainerChanges(final PrintStream log, List originalTrainerNames, boolean trainerNamesChanged, boolean logTrainerMovesets) {
log.println("--Trainers Pokemon--");
List trainers = romHandler.getTrainers();
for (Trainer t : trainers) {
log.print("#" + t.index + " ");
String originalTrainerName = originalTrainerNames.get(t.index);
String currentTrainerName = "";
if (t.fullDisplayName != null) {
currentTrainerName = t.fullDisplayName;
} else if (t.name != null) {
currentTrainerName = t.name;
}
if (!currentTrainerName.isEmpty()) {
if (trainerNamesChanged) {
log.printf("(%s => %s)", originalTrainerName, currentTrainerName);
} else {
log.printf("(%s)", currentTrainerName);
}
}
if (t.offset != 0) {
log.printf("@%X", t.offset);
}
String[] itemNames = romHandler.getItemNames();
if (logTrainerMovesets) {
log.println();
for (TrainerPokemon tpk : t.pokemon) {
List moves = romHandler.getMoves();
log.printf(tpk.toString(), itemNames[tpk.heldItem]);
log.print(", Ability: " + romHandler.abilityName(romHandler.getAbilityForTrainerPokemon(tpk)));
log.print(" - ");
boolean first = true;
for (int move : tpk.moves) {
if (move != 0) {
if (!first) {
log.print(", ");
}
log.print(moves.get(move).name);
first = false;
}
}
log.println();
}
} else {
log.print(" - ");
boolean first = true;
for (TrainerPokemon tpk : t.pokemon) {
if (!first) {
log.print(", ");
}
log.printf(tpk.toString(), itemNames[tpk.heldItem]);
first = false;
}
}
log.println();
}
log.println();
}
private int logStaticPokemon(final PrintStream log, int checkValue, List oldStatics) {
List newStatics = romHandler.getStaticPokemon();
log.println("--Static Pokemon--");
Map seenPokemon = new TreeMap<>();
for (int i = 0; i < oldStatics.size(); i++) {
StaticEncounter oldP = oldStatics.get(i);
StaticEncounter newP = newStatics.get(i);
checkValue = addToCV(checkValue, newP.pkmn.number);
String oldStaticString = oldP.toString(settings.isStaticLevelModified());
log.print(oldStaticString);
if (seenPokemon.containsKey(oldStaticString)) {
int amount = seenPokemon.get(oldStaticString);
log.print("(" + (++amount) + ")");
seenPokemon.put(oldStaticString, amount);
} else {
seenPokemon.put(oldStaticString, 1);
}
log.println(" => " + newP.toString(settings.isStaticLevelModified()));
}
log.println();
return checkValue;
}
private int logTotemPokemon(final PrintStream log, int checkValue, List oldTotems) {
List newTotems = romHandler.getTotemPokemon();
String[] itemNames = romHandler.getItemNames();
log.println("--Totem Pokemon--");
for (int i = 0; i < oldTotems.size(); i++) {
TotemPokemon oldP = oldTotems.get(i);
TotemPokemon newP = newTotems.get(i);
checkValue = addToCV(checkValue, newP.pkmn.number);
log.println(oldP.pkmn.fullName() + " =>");
log.printf(newP.toString(),itemNames[newP.heldItem]);
}
log.println();
return checkValue;
}
private void logMoveChanges(final PrintStream log) {
log.println("--Move Data--");
log.print("NUM|NAME |TYPE |POWER|ACC.|PP");
if (romHandler.hasPhysicalSpecialSplit()) {
log.print(" |CATEGORY");
}
log.println();
List allMoves = romHandler.getMoves();
for (Move mv : allMoves) {
if (mv != null) {
String mvType = (mv.type == null) ? "???" : mv.type.toString();
log.printf("%3d|%-15s|%-8s|%5d|%4d|%3d", mv.internalId, mv.name, mvType, mv.power,
(int) mv.hitratio, mv.pp);
if (romHandler.hasPhysicalSpecialSplit()) {
log.printf("| %s", mv.category.toString());
}
log.println();
}
}
log.println();
}
private void logShops(final PrintStream log) {
String[] itemNames = romHandler.getItemNames();
log.println("--Shops--");
Map shopsDict = romHandler.getShopItems();
for (int shopID : shopsDict.keySet()) {
Shop shop = shopsDict.get(shopID);
log.printf("%s", shop.name);
log.println();
List shopItems = shop.items;
for (int shopItemID : shopItems) {
log.printf("- %5s", itemNames[shopItemID]);
log.println();
}
log.println();
}
log.println();
}
private void logPickupItems(final PrintStream log) {
List pickupItems = romHandler.getPickupItems();
String[] itemNames = romHandler.getItemNames();
log.println("--Pickup Items--");
for (int levelRange = 0; levelRange < 10; levelRange++) {
int startingLevel = (levelRange * 10) + 1;
int endingLevel = (levelRange + 1) * 10;
log.printf("Level %s-%s", startingLevel, endingLevel);
log.println();
TreeMap> itemListPerProbability = new TreeMap<>();
for (PickupItem pickupItem : pickupItems) {
int probability = pickupItem.probabilities[levelRange];
if (itemListPerProbability.containsKey(probability)) {
itemListPerProbability.get(probability).add(itemNames[pickupItem.item]);
} else if (probability > 0) {
List itemList = new ArrayList<>();
itemList.add(itemNames[pickupItem.item]);
itemListPerProbability.put(probability, itemList);
}
}
for (Map.Entry> itemListPerProbabilityEntry : itemListPerProbability.descendingMap().entrySet()) {
int probability = itemListPerProbabilityEntry.getKey();
List itemList = itemListPerProbabilityEntry.getValue();
String itemsString = String.join(", ", itemList);
log.printf("%d%%: %s", probability, itemsString);
log.println();
}
log.println();
}
log.println();
}
private List getTrainerNames() {
List trainerNames = new ArrayList<>();
trainerNames.add(""); // for index 0
List trainers = romHandler.getTrainers();
for (Trainer t : trainers) {
if (t.fullDisplayName != null) {
trainerNames.add(t.fullDisplayName);
} else if (t.name != null) {
trainerNames.add(t.name);
} else {
trainerNames.add("");
}
}
return trainerNames;
}
private static int addToCV(int checkValue, int... values) {
for (int value : values) {
checkValue = Integer.rotateLeft(checkValue, 3);
checkValue ^= value;
}
return checkValue;
}
}