/*
* Pokemon.java Created on December 13, 2006, 5:38 PM This file is a part of
* Shoddy Battle. Copyright (C) 2006 Colin Fitzpatrick 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 2 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, visit the Free Software Foundation, Inc. online at
* http://gnu.org.
*/
package org.pokenet.server.battle;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.SortedSet;
import org.pokenet.server.backend.entity.PlayerChar;
import org.pokenet.server.battle.mechanics.BattleMechanics;
import org.pokenet.server.battle.mechanics.ModData;
import org.pokenet.server.battle.mechanics.MoveQueueException;
import org.pokenet.server.battle.mechanics.PokemonNature;
import org.pokenet.server.battle.mechanics.PokemonType;
import org.pokenet.server.battle.mechanics.StatException;
import org.pokenet.server.battle.mechanics.StatMultiplier;
import org.pokenet.server.battle.mechanics.ValidationException;
import org.pokenet.server.battle.mechanics.clauses.Clause.PendanticDamageClause;
import org.pokenet.server.battle.mechanics.moves.MoveList;
import org.pokenet.server.battle.mechanics.moves.MoveListEntry;
import org.pokenet.server.battle.mechanics.moves.PokemonMove;
import org.pokenet.server.battle.mechanics.statuses.AwesomeEffect;
import org.pokenet.server.battle.mechanics.statuses.BurnEffect;
import org.pokenet.server.battle.mechanics.statuses.ChargeEffect;
import org.pokenet.server.battle.mechanics.statuses.ConfuseEffect;
import org.pokenet.server.battle.mechanics.statuses.FlinchEffect;
import org.pokenet.server.battle.mechanics.statuses.MultipleStatChangeEffect;
import org.pokenet.server.battle.mechanics.statuses.ParalysisEffect;
import org.pokenet.server.battle.mechanics.statuses.PercentEffect;
import org.pokenet.server.battle.mechanics.statuses.PoisonEffect;
import org.pokenet.server.battle.mechanics.statuses.StatChangeEffect;
import org.pokenet.server.battle.mechanics.statuses.StatusEffect;
import org.pokenet.server.battle.mechanics.statuses.StatusListener;
import org.pokenet.server.battle.mechanics.statuses.ToxicEffect;
import org.pokenet.server.battle.mechanics.statuses.abilities.IntrinsicAbility;
import org.pokenet.server.battle.mechanics.statuses.items.HoldItem;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.ElementArray;
/**
* This class represents a pokemon in a battle. Its stats are automatically
* modified as it attacks and is attacked, so no method for directly modifying
* its stats are provided. Using this class requires a BattleMechanics object to
* initialise its stats.
*
* @author Colin
*/
public class Pokemon extends PokemonSpecies {
private static final long serialVersionUID = 2636950446169268200L;
// Transient statistics.
@Element
transient private String m_dateCaught;
transient private int m_hp;
transient private final int[] m_contestStat = new int[5];
@ElementArray
transient private int[] m_stat;
@ElementArray
transient private StatMultiplier[] m_multiplier;
transient private StatMultiplier m_accuracy;
transient private StatMultiplier m_evasion;
transient private ArrayList<StatusEffect> m_statuses;
@ElementArray
transient private int[] m_pp;
@ElementArray
transient private int[] m_maxPp;
@Element
transient private boolean m_fainted;
transient private BattleField m_field;
transient private int m_party;
private ArrayList<String> m_movesLearning;
@Element
private double m_exp;
@Element
private int m_baseExp;
@Element
transient private int m_id;
transient private IntrinsicAbility m_originalAbility;
transient private IntrinsicAbility m_ability;
@Element(required = false)
transient private HoldItem m_item;
transient private MoveListEntry m_lastMove;
transient private boolean m_firstTurn = false;
/**
* The health of a substitute, or zero if no substitute is out.
*/
transient private int m_substitute;
/* Stores the EXP growth rate of the Pokemon */
@Element
transient private ExpTypes m_expType;
@Element
private int m_happiness;
/* Stores the evolution this Pokemon is waiting to evolve to */
private PokemonEvolution m_evolution = null;
// Intrinsic statistics.
@Element
private int m_level = -1;
@Element
private PokemonNature m_nature;
private MoveListEntry[] m_move;
@ElementArray
private int[] m_ppUp; // Number
// of
// PP
// Ups
// applied
// to
// each
// move.
@Element
private String m_abilityName; // Intrinsic
// ability.
@Element
private String m_itemName; // Item
// initially
// held
// by
// the
// pokemon.
@Element
private boolean m_shiny = false;
@Element
private int m_gender = GENDER_MALE;
@Element
private String m_nickname;
// Hidden statistics.
@ElementArray
private int m_iv[];
@ElementArray
private int m_ev[];
@Element
private String m_originalTrainer;
@SuppressWarnings("unused")
@Element
private long m_originalNo;
private int m_databaseID = -1;
// Battle mechanics.
private BattleMechanics m_mech;
/* Constants representing each statistic. */
public static final int S_HP = 0;
public static final int S_ATTACK = 1;
public static final int S_DEFENCE = 2;
public static final int S_SPEED = 3;
public static final int S_SPATTACK = 4;
public static final int S_SPDEFENCE = 5;
public static final int S_ACCURACY = 6;
public static final int S_EVASION = 7;
/* Content stats */
public static final int S_BEAUTY = 0;
public static final int S_CUTE = 1;
public static final int S_SMART = 2;
public static final int S_COOL = 3;
public static final int S_TOUGH = 4;
public enum ExpTypes {
MEDIUM, ERRATIC, FLUCTUATING, PARABOLIC, FAST, SLOW
}
/**
* Returns this pokemon's contest stats in string format NOTE: Only used for
* saving MySQL
*
* @return
*/
public String getContestStatsAsString() {
return m_contestStat[0] + "," + m_contestStat[1] + "," + m_contestStat[2]
+ "," + m_contestStat[3] + "," + m_contestStat[4];
}
/**
* Sets the contest stat of the pokemon. NOTE: Use S_BEAUTY, S_CUTE, etc. for
* i
*
* @param i
* @param amount
*/
public void setContestStat(int i, int amount) {
m_contestStat[i] = amount <= 255 ? amount : 255;
}
/**
* Returns the contest of the pokemon. NOTE: Use S_BEAUTY, S_CUTE, etc.
*
* @param i
*/
public int getContestStat(int i) {
return m_contestStat[i];
}
/**
* Returns true if this Pokemon is weak against Pokemon b
*
* @param b
* @return
*/
public boolean hasTypeWeakness(Pokemon b) {
switch (this.getTypes()[0].getType()) {
case 0:
/* NORMAL - Weak against Fighting */
if (b.getTypes()[0].getType() == 6) return true;
break;
case 1:
/* FIRE - Weak against Ground, Rock, Water */
if (b.getTypes()[0].getType() == 8 || b.getTypes()[0].getType() == 12
|| b.getTypes()[0].getType() == 2) return true;
break;
case 2:
/* WATER - Weak against Electric, Grass */
if (b.getTypes()[0].getType() == 3 || b.getTypes()[0].getType() == 4)
return true;
break;
case 3:
/* ELECTRIC - Weak against Ground */
if (b.getTypes()[0].getType() == 8) return true;
break;
case 4:
/* GRASS - Weak against Bug, Fire, Flying, Ice, Poison */
if (b.getTypes()[0].getType() == 11 || b.getTypes()[0].getType() == 1
|| b.getTypes()[0].getType() == 9 || b.getTypes()[0].getType() == 5
|| b.getTypes()[0].getType() == 7) return true;
break;
case 5:
/* ICE - Weak against Fighting, Fire, Rock, Steel */
if (b.getTypes()[0].getType() == 6 || b.getTypes()[0].getType() == 1
|| b.getTypes()[0].getType() == 12 || b.getTypes()[0].getType() == 16)
return true;
break;
case 6:
/* FIGHTING - Weak against Flying, Psychic */
if (b.getTypes()[0].getType() == 9 || b.getTypes()[0].getType() == 10)
return true;
break;
case 7:
/* POISON - Weak against Ground, Psychic */
if (b.getTypes()[0].getType() == 8 || b.getTypes()[0].getType() == 10)
return true;
break;
case 8:
/* GROUND - Weak against Ice, Grass, Water */
if (b.getTypes()[0].getType() == 5 || b.getTypes()[0].getType() == 4
|| b.getTypes()[0].getType() == 2) return true;
break;
case 9:
/* FLYING - Weak against Electric, Ice, Rock */
if (b.getTypes()[0].getType() == 3 || b.getTypes()[0].getType() == 5
|| b.getTypes()[0].getType() == 12) return true;
break;
case 10:
/* PSYCHIC - Weak against Bug, Dark, Ghost */
if (b.getTypes()[0].getType() == 11 || b.getTypes()[0].getType() == 15
|| b.getTypes()[0].getType() == 13) return true;
break;
case 11:
/* BUG - Weak against Flying, Fire, Rock */
if (b.getTypes()[0].getType() == 9 || b.getTypes()[0].getType() == 1
|| b.getTypes()[0].getType() == 12) return true;
break;
case 12:
/* ROCK - Weak against Fighting, Grass, Ground, Steel, Water */
if (b.getTypes()[0].getType() == 6 || b.getTypes()[0].getType() == 4
|| b.getTypes()[0].getType() == 8 || b.getTypes()[0].getType() == 16
|| b.getTypes()[0].getType() == 2) return true;
break;
case 13:
/* GHOST - Weak against Dark, Ghost */
if (b.getTypes()[0].getType() == 13 || b.getTypes()[0].getType() == 15)
return true;
break;
case 14:
/* DRAGON - Weak against Dragon, Ice */
if (b.getTypes()[0].getType() == 14 || b.getTypes()[0].getType() == 5)
return true;
break;
case 15:
/* DARK - Weak against Bug, Fighting */
if (b.getTypes()[0].getType() == 6 || b.getTypes()[0].getType() == 11)
return true;
break;
case 16:
/* STEEL - Weak against Fire, Fighting, Ground */
if (b.getTypes()[0].getType() == 1 || b.getTypes()[0].getType() == 6
|| b.getTypes()[0].getType() == 8) return true;
break;
}
return false;
}
/**
* Returns true if this Pokemon is waiting to evolve
*
* @return
*/
public boolean isWaitToEvolve() {
return m_evolution != null;
}
/**
* Sets the Pokemon's HP
*
* @param h
*/
public void setHealth(int h) {
m_hp = h;
}
/**
* Sets if this Pokemon is waiting to evolve and the evolution it is waiting
* to go to
*
* @param e
*/
public void setEvolution(PokemonEvolution e) {
m_evolution = e;
}
/**
* Returns true if a pokemon knows the move
*
* @param move
* @return
*/
public boolean hasMove(String move) {
for (int i = 0; i < m_move.length; i++) {
if (m_move[i] != null && m_move[i].getName() != null
&& m_move[i].getName().equalsIgnoreCase(move)) { return true; }
}
return false;
}
/**
* Handles the response from the client, whether they allowed evolution or not
*
* @param allow
* - If the evolution is allowed
* @param p
* - The player that owns the Pokemon
*/
public void evolutionResponse(boolean allow, PlayerChar p) {
if (m_evolution != null) {
/* Get the index of the Pokemon in the player's party */
int index = p.getPokemonIndex(this);
if (allow) {
/* The player is allowing evolution, evolve the Pokemon */
this.evolve(PokemonSpecies.getDefaultData().
getPokemonByName(m_evolution.getEvolveTo()));
}
/* Retrieve the Pokemon data */
PokemonSpecies pokeData = PokemonSpecies.getDefaultData().
getPokemonByName(getSpeciesName());
setHappiness(m_happiness + 2);
calculateStats(false);
/* Now learn any moves that need learning */
int level = DataService.getBattleMechanics().calculateLevel(this);
int oldLevel = getLevel();
String move = "";
/* Generate a list of moves this Pokemon wants to learn */
m_movesLearning.clear();
for (int i = oldLevel + 1; i <= level; i++) {
if (pokeData.getLevelMoves().get(i) != null) {
move = pokeData.getLevelMoves().get(i);
if (move != null && !move.equalsIgnoreCase("") && !hasMove(move))
m_movesLearning.add(move);
}
}
/* Save the Pokemon's level */
setLevel(level);
/* Update the client with new Pokemon information */
p.updateClientParty(index);
/* Inform the client this Pokemon wants to learn new moves */
for (int i = 0; i < m_movesLearning.size(); i++) {
p.getTcpSession().write("Pm" + index + m_movesLearning.get(i));
}
p.updateClientPokemonStats(index);
}
}
/**
* Sets if this pokemon is fainted
*
* @param b
*/
public void setIsFainted(boolean b) {
m_fainted = b;
}
/**
* Sets the database id
*
* @param id
*/
public void setDatabaseID(int id) {
m_databaseID = id;
}
/**
* Returns the database id
*
* @return
*/
public int getDatabaseID() {
return m_databaseID;
}
/**
* Create a substitute to take hits for this pokemon.
*/
public boolean createSubstitute() {
if (hasSubstitute()) { return false; }
int quarter = m_stat[S_HP] / 4;
if (quarter >= m_hp) { return false; }
changeHealth(-quarter);
m_substitute = quarter;
return true;
}
/**
* Set the health of the substitute.
*/
public void setSubstitute(int hp) {
m_substitute = hp;
}
/**
* Get the health of the substitute.
*/
public int getSubstitute() {
return m_substitute;
}
/**
* Return whether this pokemon has a substitute.
*/
public boolean hasSubstitute() {
return (m_substitute != 0);
}
/**
* Dispose of this object.
*/
public void dispose() {
m_multiplier = null;
m_accuracy = null;
m_evasion = null;
m_statuses = null;
m_field = null;
m_nature = null;
m_move = null;
m_abilityName = null;
m_itemName = null;
m_mech = null;
}
/**
* Get the name of a stat.
*/
public static String getStatName(int stat) {
switch (stat) {
case S_HP:
return "HP";
case S_ATTACK:
return "attack";
case S_DEFENCE:
return "defence";
case S_SPEED:
return "speed";
case S_SPATTACK:
return "special attack";
case S_SPDEFENCE:
return "special defence";
case S_ACCURACY:
return "accuracy";
case S_EVASION:
return "evasion";
}
return "";
}
/**
* Get the shortened name of a stat.
*/
public static String getStatShortName(int stat) {
switch (stat) {
case S_HP:
return "HP";
case S_ATTACK:
return "Atk";
case S_DEFENCE:
return "Def";
case S_SPEED:
return "Spd";
case S_SPATTACK:
return "SAtk";
case S_SPDEFENCE:
return "SDef";
case S_ACCURACY:
return "Acc";
case S_EVASION:
return "Evas";
}
return "";
}
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
}
/**
* Unserialises a Pokemon.
*/
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
in.defaultReadObject();
if (m_ppUp == null) {
m_ppUp = new int[m_move.length];
Arrays.fill(m_ppUp, 3);
}
if (m_nickname == null) {
m_nickname = getSpeciesName();
} else {
m_nickname = m_nickname.trim();
if (m_nickname.length() == 0) {
m_nickname = getSpeciesName();
}
}
try {
initialise();
} catch (StatException e) {
throw new IOException();
}
}
/** Creates a new instance of Pokemon */
public Pokemon(BattleMechanics mech, PokemonSpecies species,
PokemonNature nature, String ability, String item, int gender, int level,
int[] ivs, int[] evs, MoveListEntry[] moves, int[] ppUps, boolean validate)
throws StatException {
super(species);
m_mech = mech;
m_iv = ivs;
m_ev = evs;
m_nature = nature;
m_gender = gender;
m_level = level;
m_move = moves;
m_abilityName = ability;
if (m_ability == null) {
m_ability = IntrinsicAbility.getInstance(ability);
}
m_itemName = item;
m_ppUp = ppUps;
m_name = species.getName();
initialise();
}
/**
* Create and validate a new pokemon.
*/
public Pokemon(BattleMechanics mech, PokemonSpecies species,
PokemonNature nature, String ability, String item, int gender, int level,
int[] ivs, int[] evs, MoveListEntry[] moves, int[] ppUps)
throws StatException {
this(mech, species, nature, ability, item, gender, level, ivs, evs, moves,
ppUps, true);
}
public BattleMechanics getMech() {
return m_mech;
}
public MoveListEntry[] getMoves() {
return m_move;
}
public Pokemon(Pokemon p) {
super(p);
m_mech = p.getMech();
Random gen = new Random();
m_iv = new int[] {
gen.nextInt(32), // IVs
gen.nextInt(32), gen.nextInt(32), gen.nextInt(32), gen.nextInt(32),
gen.nextInt(32) };
m_ev = new int[] { 0, 0, 0, 0, 0, 0 };
m_nature = p.getNature();
m_gender = p.getGender();
m_level = p.getLevel();
m_move = p.getMoves();
m_abilityName = p.getAbilityName();
m_itemName = p.getItemName();
m_ppUp = new int[] { 0, 0, 0, 0 };
m_nickname = getSpeciesName();
initialise();
}
/**
* Get a random Pokemon object.
*/
public static Pokemon getRandomPokemon(ModData data, BattleMechanics mech) {
Random random = mech.getRandom();
int[] ivs = new int[6];
for (int i = 0; i < ivs.length; ++i) {
ivs[i] = random.nextInt(32);
}
int[] evs = new int[6];
int evTotal = 0;
final int inc = 16;
while ((evTotal + inc) <= 510) {
evs[random.nextInt(evs.length)] += inc;
evTotal += inc;
}
PokemonNature nature = PokemonNature.getNature(random.nextInt(25));
PokemonSpeciesData speciesData = data.getSpeciesData();
PokemonSpecies species = new PokemonSpecies(speciesData, random
.nextInt(speciesData.getSpeciesCount()));
String [] moveset = species.getStarterMoves();
if ((moveset == null) || (moveset.length == 0)) { return null; }
int moveCount = moveset.length;
String[] moves = (String [] ) species.getLevelMoves().values().toArray();
MoveListEntry[] entries = new MoveListEntry[(moveCount >= 4) ? 4
: moveCount];
Set<String> moveSet = new HashSet<String>();
int[] ppUp = new int[entries.length];
for (int i = 0; i < entries.length; ++i) {
String move;
do {
move = moves[random.nextInt(moves.length)];
} while (moveSet.contains(move));
moveSet.add(move);
entries[i] = data.getMoveData().getMove(move);
ppUp[i] = random.nextInt(4);
}
String ability = null;
SortedSet<Object> set;
String[] itemes = species.getPossibleAbilities(speciesData);
if ((itemes != null)) {
ability = itemes[random.nextInt(itemes.length)];
}
set = data.getHoldItemData().getItemSet(species.getName());
String[] items = set.toArray(new String[set.size()]);
String item = items[random.nextInt(items.length)];
int genders = species.getPossibleGenders();
int gender = GENDER_NONE;
if (genders != GENDER_NONE) {
int[] choices = { GENDER_MALE, GENDER_FEMALE };
while (true) {
gender = choices[random.nextBoolean() ? 0 : 1];
if ((genders & gender) != 0) break;
}
}
Pokemon p = new Pokemon(mech, species, nature, ability, item, gender, 100,
ivs, evs, entries, ppUp);
// Give it a 5% chance of being shiny.
if (random.nextDouble() < 0.05) {
p.setShiny(true);
}
return p;
}
/**
* Returns a random Pokemon based on a species name and a level
*
* @param speciesName
* @param level
* @return
*/
public static Pokemon getRandomPokemon(String species, int level) {
Pokemon p;
Random random = DataService.getBattleMechanics().getRandom();
/*
* First obtain species data
*/
PokemonSpecies ps = PokemonSpecies.getDefaultData().getPokemonByName(species);
MoveListEntry[] moves = new MoveListEntry[4];
/*
* Generate a list of possible moves this Pokemon could have at this level
*/
ArrayList<MoveListEntry> possibleMoves = new ArrayList<MoveListEntry>();
MoveList moveList = MoveList.getDefaultData();
/*
* Get all starter moves
*/
for (int i = 0; i < ps.getStarterMoves().length; i++) {
possibleMoves.add(moveList.getMove(ps.getStarterMoves()[i]));
}
/*
* Get moves learned by levelling up
*/
for (int i = 1; i <= level; i++) {
if (ps.getLevelMoves().containsKey(i)) {
MoveListEntry m = moveList.getMove(ps.getLevelMoves().get(i));
boolean exists = false;
/* Check if this move is already in the list of possible moves */
for (int j = 0; j < possibleMoves.size(); j++) {
if (possibleMoves.get(j) != null
&& possibleMoves.get(j).getName() != null && m != null
&& m.getName() != null
&& possibleMoves.get(j).getName().equalsIgnoreCase(m.getName())) {
exists = true;
break;
}
}
/* If the move is not already in the list, add it to the list */
if (!exists) possibleMoves.add(m);
}
}
/*
* possibleMoves sometimes has null moves stored in it, get rid of them
*/
for (int i = 0; i < possibleMoves.size(); i++) {
if (possibleMoves.get(i) == null) possibleMoves.remove(i);
}
possibleMoves.trimToSize();
/*
* Now the store the final set of moves for the Pokemon
*/
if (possibleMoves.size() <= 4) {
for (int i = 0; i < possibleMoves.size(); i++) {
moves[i] = possibleMoves.get(i);
}
} else {
MoveListEntry m = null;
for (int i = 0; i < 4; i++) {
if (possibleMoves.size() == 0) {
moves[i] = null;
} else {
m = possibleMoves.get(random.nextInt(possibleMoves.size()));
moves[i] = m;
possibleMoves.remove(m);
possibleMoves.trimToSize();
m = null;
}
}
}
/*
* Get all possible abilities
*/
String[] abilities = PokemonSpecies.getDefaultData().getPokemonByName(
species).getAbilities();
/* First select an ability randomly */
String ab = abilities[random.nextInt(abilities.length)];
/*
* Now lets create the pokemon itself
*/
p = new Pokemon(DataService.getBattleMechanics(), ps, PokemonNature
.getNature(random.nextInt(PokemonNature.getNatureNames().length)), ab,
null, Pokemon.generateGender(ps.getPossibleGenders()), level, new int[] {
random.nextInt(32), // IVs
random.nextInt(32), random.nextInt(32), random.nextInt(32),
random.nextInt(32), random.nextInt(32) },
new int[] { 0, 0, 0, 0, 0, 0 }, // EVs
moves, new int[] { 0, 0, 0, 0 });
p.setBaseExp(ps.getBaseEXP());
p.setExpType(ps.getGrowthRate());
p.setExp(DataService.getBattleMechanics().getExpForLevel(p, level));
p.setHappiness(ps.getHappiness());
p.setRareness(ps.getRareness());
return p;
}
public static int generateGender(int possibleGenders) {
switch (possibleGenders) {
case 0:
return 0;
case 1:
return 1;
case 2:
return 2;
case 3:
if (DataService.getBattleMechanics().getRandom().nextBoolean()) return 1;
else
return 2;
default:
return -1;
}
}
/**
* Load a team from a file and return the ModData used by the team.
*/
public static ModData loadTeam(File f, Pokemon[] team) {
ModData modData = null;
try {
FileInputStream file = new FileInputStream(f);
ObjectInputStream obj = new ObjectInputStream(file);
// First thing in file is a UUID identifying the server.
String uuid = (String) obj.readObject();
modData = ModData.getModData(uuid);
if (modData == null) {
modData = ModData.getDefaultData();
}
Pokemon[] pokemon = null;
synchronized (PokemonSpecies.class) {
PokemonSpeciesData data = PokemonSpecies.getDefaultData();
PokemonSpecies.setDefaultData(modData.getSpeciesData());
try {
pokemon = (Pokemon[]) obj.readObject();
} finally {
PokemonSpecies.setDefaultData(data);
}
}
if (pokemon != null) {
System.arraycopy(pokemon, 0, team, 0, team.length);
}
obj.close();
} catch (IOException e) {
System.out.println(e.getMessage());
return null;
} catch (ClassNotFoundException e) {
System.out.println(e.getMessage());
return null;
}
return modData;
}
/**
* Set whether this pokemon is shiny.
*/
public void setShiny(boolean shiny) {
m_shiny = shiny;
}
/**
* Return whether this pokemon is shiny.
*/
public boolean isShiny() {
return m_shiny;
}
/**
* Return this Pokemon's gender.
*/
public int getGender() {
return m_gender;
}
/**
* Return the name of this pokemon's ability.
*/
public String getAbilityName() {
if ((m_ability == null) || m_ability.isRemovable()) { return ""; }
return m_ability.getName();
}
/**
* Return this pokemon's ability.
*/
public IntrinsicAbility getAbility() {
if ((m_ability == null) && (m_abilityName != null)
&& (m_abilityName.length() != 0)) { return IntrinsicAbility
.getInstance(m_abilityName); }
return m_ability;
}
/**
* Set this pokemon's ability. If ignoreTransferability is true then the
* isTransferrable() method of the ability is ignored. Otherwise it is
* respected.
*/
public void setAbility(IntrinsicAbility abl, boolean ignoreTransferability) {
removeStatus(m_ability);
if (abl != null) {
m_abilityName = abl.getName();
if (ignoreTransferability || abl.isEffectTransferrable()) {
m_ability = (IntrinsicAbility) addStatus(this, abl);
} else {
m_ability = null;
}
} else {
m_abilityName = null;
}
}
/**
* Return the name of this pokemon's item.
*/
public String getItemName() {
if ((m_item == null) || m_item.isRemovable()) { return ""; }
return m_item.getName();
}
/**
* Get this pokemon's item.
*/
public HoldItem getItem() {
if ((m_item != null) && m_item.isRemovable()) { return null; }
return m_item;
}
/**
* Set this pokemon's item.
*/
public void setItem(HoldItem item) {
removeStatus(m_item);
if (item != null) {
m_item = (HoldItem) addStatus(this, item);
}
m_itemName = getItemName();
}
/**
* Validate this pokemon.
*/
public void validate(ModData data) throws ValidationException {
m_mech.validateHiddenStats(this);
PokemonSpeciesData speciesData = data.getSpeciesData();
Set<String> set = new HashSet<String>();
int moveCount = 0;
for (int i = 0; i < m_move.length; ++i) {
MoveListEntry move = m_move[i];
if (move != null) {
++moveCount;
String name = move.getName();
if (set.contains(name)) { throw new ValidationException(
"This pokemon learns two of the same move."); }
set.add(name);
if (!canLearn(speciesData, name)) { throw new ValidationException(
"This pokemon cannot learn " + name + "."); }
if ((m_ppUp[i] > 3) || (m_ppUp[i] < 0)) { throw new ValidationException(
"Each move must have between zero and "
+ "three PP ups applied to it."); }
}
}
if (moveCount == 0) {
// Pokemon must have at least one move.
throw new ValidationException("This pokemon learns no moves.");
} else if (moveCount > 4) { throw new ValidationException(
"This pokemon learns move than four moves."); }
int genders = getPossibleGenders();
if (((genders & m_gender) == 0) && ((genders != 0) || (m_gender != 0))) { throw new ValidationException(
"This pokemon has an invalid gender."); }
if (!canUseAbility(speciesData, m_abilityName)) {
String[] possibilities = getPossibleAbilities(speciesData);
if ((possibilities != null)) {
m_abilityName = possibilities[0];
}
}
if ((m_itemName != null)
&& !data.getHoldItemData().canUseItem(getSpeciesName(), m_itemName)) { throw new ValidationException(
"This pokemon's item is invalid."); }
}
/**
* Get the number of PP Ups that have been applied to the given move slot.
*/
public int getPpUpCount(int i) {
if ((i < 0) || (i >= m_ppUp.length)) { return -1; }
return m_ppUp[i];
}
/**
* Calculate stats from a given set of IVs and EVs. The data given are assumed
* to be valid; no checking is done for illegal values in this function.
*/
public void calculateStats(int base[], int[] ivs, int[] evs) {
m_iv = ivs;
m_ev = evs;
m_base = base;
for (int i = 0; i < m_stat.length; ++i) {
m_stat[i] = m_mech.calculateStat(this, i);
}
}
/**
* Recalculates this Pokemon's stats. If reset is true, the Pokemon is also
* healed fully
*
* @param reset
*/
public void calculateStats(boolean reset) {
m_stat = new int[6];
m_multiplier = new StatMultiplier[m_stat.length];
if (reset) removeStatusEffects(true);
for (int i = 0; i < m_stat.length; ++i) {
m_stat[i] = m_mech.calculateStat(this, i);
m_multiplier[i] = new StatMultiplier(false);
}
if (reset) m_hp = m_stat[S_HP];
}
/**
* Removes temporary or all status effects
*
* @param all
*/
public void removeStatusEffects(boolean all) {
if (all) {
removeStatus(AwesomeEffect.class);
removeStatus(BurnEffect.class);
removeStatus(ChargeEffect.class);
removeStatus(ConfuseEffect.class);
removeStatus(FlinchEffect.class);
removeStatus(MultipleStatChangeEffect.class);
removeStatus(ParalysisEffect.class);
removeStatus(PercentEffect.class);
removeStatus(PoisonEffect.class);
removeStatus(ToxicEffect.class);
removeStatus(ConfuseEffect.class);
removeStatus(StatusEffect.class);
removeStatus(StatChangeEffect.class);
} else {
removeStatus(ConfuseEffect.class);
removeStatus(StatChangeEffect.class);
}
}
/**
* Calculate this pokemon's stats.
*/
private void initialise() throws StatException {
// Recreate transient members.
m_movesLearning = new ArrayList<String>();
m_accuracy = new StatMultiplier(true);
m_evasion = new StatMultiplier(true);
m_statuses = new ArrayList<StatusEffect>();
m_pp = new int[4];
m_maxPp = new int[m_pp.length];
m_fainted = false;
m_field = null;
m_substitute = 0;
m_ability = IntrinsicAbility.getInstance(m_abilityName);
calculateStats(true);
for (int i = 0; i < m_move.length; ++i) {
if (m_move[i] != null) {
m_move[i] = (MoveListEntry) m_move[i].clone();
PokemonMove move = m_move[i].getMove();
if (move != null) {
m_maxPp[i] = m_pp[i] = move.getPp() * (5 + m_ppUp[i]) / 5;
}
}
}
}
/**
* Get this pokemon's teammates, including this pokemon.
*/
public Pokemon[] getTeammates() {
if (m_field == null) { return null; }
return m_field.getParty(m_party);
}
/**
* Get the name of this pokemon's trainer.
*/
public String getTrainerName() {
if (m_field == null) { return null; }
return m_field.getTrainerName(m_party);
}
/**
* Get the Pokemon that this Pokemon is fighting in a battle.
*/
public Pokemon getOpponent() {
if (m_field == null) { return null; }
Pokemon[] active = m_field.getActivePokemon();
return active[(m_party == 0) ? 1 : 0];
}
/**
* Return whether a pokemon is a particular type.
*/
public boolean isType(PokemonType type) {
for (int i = 0; i < m_type.length; ++i) {
if (m_type[i].equals(type)) { return true; }
}
return false;
}
/**
* Get the (additive) critical hit ability of this Pokemon.
*/
public int getCriticalHitFactor() {
return hasAbility("Super Luck") ? 2 : 1;
}
/**
* Return whether this Pokemon is immune to critical hits.
*/
public boolean isCriticalImmune() {
return (hasAbility("Battle Armor") || hasAbility("Shell Armor"));
}
/**
* Set a move's pp.
*/
public void setPp(int i, int value) {
if ((i < 0) || (i >= m_pp.length)) return;
m_pp[i] = value;
}
/**
* Get a move's pp.
*/
public int getPp(int i) {
if ((i < 0) || (i >= m_move.length) || (m_move[i] == null)) return -1;
return m_pp[i];
}
/**
* Get a move's max pp.
*/
public int getMaxPp(int i) {
if ((i < 0) || (i >= m_move.length) || (m_move[i] == null)) return -1;
return m_maxPp[i];
}
/**
* Sets the max pp of a move
*
* @param index
* @param value
*/
public void setMaxPP(int i, int value) {
if ((i < 0) || (i >= m_maxPp.length)) return;
m_maxPp[i] = value;
}
/**
* Sets a pp up
*
* @param i
* @param value
*/
public void setPpUp(int i, int value) {
if ((i < 0) || (i >= m_ppUp.length)) return;
m_ppUp[i] = value;
}
/**
* Get one of this pokemon's moves.
*/
public MoveListEntry getMove(int i) {
if (i == -1) return BattleField.getStruggle();
if ((i < -1) || (i >= m_move.length) || (m_move[i] == null)) return null;
return m_move[i];
}
/**
* This method is called when the pokemon is just about to execute its turn.
*
* @param turn
* the turn that is about to be executed
*/
public void executeTurn(BattleTurn turn) {
Iterator<StatusEffect> i = m_statuses.iterator();
while (i.hasNext()) {
StatusEffect j = i.next();
if ((j == null) || !j.isActive()) {
continue;
}
j.executeTurn(this, turn);
}
}
/**
* Return whether it is the pokemon's first turn out.
*/
public boolean isFirstTurn() {
return m_firstTurn;
}
/**
* Switch in this pokemon.
*/
public void switchIn() {
// No iterator - it will freak out if switchIn() adds new statuses.
m_lastMove = null;
m_firstTurn = true;
// Inform PokemonMoves that their potential user is switching in.
for (int i = 0; i < m_move.length; ++i) {
MoveListEntry entry = m_move[i];
if (entry != null) {
PokemonMove move = entry.getMove();
if (move != null) {
move.switchIn(this);
}
}
}
// Inform status effects.
int size = m_statuses.size();
for (int i = 0; i < size; ++i) {
(m_statuses.get(i)).switchIn(this);
}
}
/**
* Return the original ability of this pokemon.
*/
public IntrinsicAbility getOriginalAbility() {
return m_originalAbility;
}
/**
* Switch out this pokemon.
*/
public void switchOut() {
ArrayList<StatusEffect> list = new ArrayList<StatusEffect>(m_statuses);
Iterator<StatusEffect> i = list.iterator();
while (i.hasNext()) {
StatusEffect effect = i.next();
if (effect.isActive() && effect.switchOut(this)) {
unapplyEffect(effect, false);
i.remove();
}
}
m_statuses = list;
setAbility(m_originalAbility, true);
synchroniseStatuses();
}
/**
* Return the effect that vetoes the use of a particular one of this pokemon's
* moves.
*/
public StatusEffect getVetoingEffect(int idx) throws MoveQueueException {
if ((idx < 0) || (idx >= m_move.length)) { throw new MoveQueueException(
"No such move."); }
MoveListEntry entry = m_move[idx];
if (entry == null) { throw new MoveQueueException("No such move."); }
synchronized (m_statuses) {
Iterator<StatusEffect> i = m_statuses.iterator();
while (i.hasNext()) {
StatusEffect j = i.next();
if ((j == null) || !j.isActive()) {
continue;
}
if (j.vetoesMove(this, entry)) { return j; }
}
}
return null;
}
/**
* Return whether this pokemon has a particular effect.
*/
public boolean hasEffect(StatusEffect eff) {
if (eff == null) { return false; }
Iterator<StatusEffect> i = m_statuses.iterator();
while (i.hasNext()) {
StatusEffect j = i.next();
if ((j == null) || !j.isActive()) {
continue;
}
if (eff.equals(j)) { return true; }
}
return false;
}
/**
* Return whether this Pokemon has a particular class of effect.
*/
public boolean hasEffect(int lock) {
return (getEffect(lock) != null);
}
/**
* Return the effect applied to this pokemon of a particular lock or null if
* there is no such effect applied.
*/
public StatusEffect getEffect(int lock) {
synchronized (m_statuses) {
Iterator<StatusEffect> i = m_statuses.iterator();
while (i.hasNext()) {
StatusEffect eff = i.next();
if ((eff == null) || !eff.isActive()) {
continue;
}
if (eff.getLock() == lock) { return eff; }
}
}
return null;
}
/**
* Return the effect of a particular class applied to this pokemon, or null if
* there is no such effect.
*/
public StatusEffect getEffect(Class<?> type) {
synchronized (m_statuses) {
Iterator<StatusEffect> i = m_statuses.iterator();
while (i.hasNext()) {
StatusEffect eff = i.next();
if ((eff == null) || !eff.isActive()) {
continue;
}
if (type.isAssignableFrom(eff.getClass())) { return eff; }
}
}
return null;
}
/**
* Return whether this Pokemon has a particular class of effect.
*/
public boolean hasEffect(Class<?> type) {
return (getEffect(type) != null);
}
/**
* Return whether this pokemon has a particular ability.
*/
public boolean hasAbility(String name) {
if (m_ability == null) { return false; }
return (m_ability.isActive() && m_ability.getName().equals(name));
}
/**
* Return whether this pokemon has a particular item.
*/
public boolean hasItem(String name) {
if (m_item == null) { return false; }
return (m_item.isActive() && m_item.getName().equals(name));
}
/**
* Return whether this pokemon is active (able to choose moves and switch).
*/
public boolean isActive() {
synchronized (m_statuses) {
Iterator<StatusEffect> i = m_statuses.iterator();
while (i.hasNext()) {
StatusEffect eff = i.next();
if (eff.isActive() && eff.deactivates(this)) { return false; }
}
}
return true;
}
/**
* Get the name of this species.
*/
public String getSpeciesName() {
return super.getName();
}
/**
* Set this pokemon's name.
*/
@Override
public void setName(String name) {
m_nickname = name;
}
/**
* Get the display name of this pokemon (i.e. its nickname).
*/
@Override
public String getName() {
return super.getName();
}
/**
* Get all status effects of a certain tier.
*/
public List<StatusEffect> getStatusesByTier(int tier) {
List<StatusEffect> ret = new ArrayList<StatusEffect>();
synchronized (m_statuses) {
Iterator<StatusEffect> i = m_statuses.iterator();
while (i.hasNext()) {
StatusEffect effect = i.next();
if (effect.isActive() && (effect.getTier() == tier)) {
ret.add(effect);
}
}
}
return ret;
}
/**
* Get a list of statuses that are not special, weather, abilities, or items.
*
* @param lock
* status lock to allow
*/
public List<StatusEffect> getNormalStatuses(int lock) {
List<StatusEffect> ret = new ArrayList<StatusEffect>();
synchronized (m_statuses) {
Iterator<StatusEffect> i = m_statuses.iterator();
while (i.hasNext()) {
StatusEffect effect = i.next();
if (!effect.isActive()) continue;
// Note: HoldItem is a subclass of IntrinsicAbility.
if (!(effect instanceof IntrinsicAbility)) {
int effLock = effect.getLock();
if ((effLock == 0) || (effLock == lock)) {
ret.add(effect);
}
}
}
}
return ret;
}
/**
* Return whether this pokemon can switch.
*/
public boolean canSwitch() {
synchronized (m_statuses) {
Iterator<StatusEffect> i = m_statuses.iterator();
while (i.hasNext()) {
StatusEffect effect = i.next();
if (effect.isActive() && !effect.canSwitch(this)) { return false; }
}
}
return true;
}
/**
* Begin ticking effects.
*/
public void beginStatusTicks() {
synchronized (m_statuses) {
Iterator<StatusEffect> i = m_statuses.iterator();
while (i.hasNext()) {
StatusEffect effect = i.next();
effect.beginTick();
}
}
}
/**
* Remove status effects that have ended.
*/
public void synchroniseStatuses() {
synchronized (m_statuses) {
Iterator<StatusEffect> i = m_statuses.iterator();
while (i.hasNext()) {
StatusEffect effect = i.next();
if (effect.isRemovable()) {
i.remove();
}
}
}
}
/**
* Invoke unapply on a status effect, optionally disabling it.
*/
private void unapplyEffect(StatusEffect eff, boolean disable) {
if (eff.isActive()) {
eff.unapply(this);
}
if (disable) {
eff.disable();
}
if (m_field != null) {
m_field.informStatusRemoved(this, eff);
}
informStatusListeners(null, eff, false);
}
/**
* Invoke unapply on a status effect and disable it as well.
*/
private void unapplyEffect(StatusEffect eff) {
unapplyEffect(eff, true);
}
/**
* Remove a status effect from this pokemon.
*/
public void removeStatus(StatusEffect eff) {
synchronized (m_statuses) {
Iterator<StatusEffect> i = m_statuses.iterator();
while (i.hasNext()) {
StatusEffect effect = i.next();
if (effect == eff) {
unapplyEffect(eff);
return;
}
}
}
}
/**
* Remove a class of statuses from this pokemon.
*/
public void removeStatus(int lock) {
synchronized (m_statuses) {
Iterator<StatusEffect> i = m_statuses.iterator();
while (i.hasNext()) {
StatusEffect effect = i.next();
if ((effect.getLock() == lock) && !effect.isRemovable()) {
unapplyEffect(effect);
}
}
}
}
/**
* Remove statuses by class type.
*/
public void removeStatus(Class<?> type) {
synchronized (m_statuses) {
Iterator<StatusEffect> i = m_statuses.iterator();
while (i.hasNext()) {
StatusEffect effect = i.next();
if (!effect.isRemovable() && type.isAssignableFrom(effect.getClass())) {
unapplyEffect(effect);
}
}
}
}
/**
* Attach this pokemon to a battle field.
*/
public void attachToField(BattleField field, int party, int position) {
m_field = field;
m_mech = m_field.getMechanics();
m_party = party;
m_id = position;
if ((m_abilityName != null) && (m_abilityName.length() != 0)) {
m_originalAbility = IntrinsicAbility.getInstance(m_abilityName);
if (m_originalAbility != null) {
m_ability = (IntrinsicAbility) addStatus(this, m_originalAbility);
}
}
if ((m_itemName != null) && (m_itemName.length() != 0)) {
IntrinsicAbility item = IntrinsicAbility.getInstance(m_itemName);
if ((item != null) && (item instanceof HoldItem)) {
m_item = (HoldItem) addStatus(this, item);
}
}
}
/**
* Detaches a battlefield from the Pokemon
*/
public void detachField() {
m_field = null;
m_mech = DataService.getBattleMechanics();
}
/**
* Get this pokemon's party. This will be in the range [0, <b>parties</b> - 1]
* where <b>parties</b> is the number of parties on the battle field (probably
* two).
*/
public int getParty() {
return m_party;
}
/**
* Get this pokemon's position on the field to which it is attached.
*/
public int getId() {
return m_id;
}
/**
* Get the field to which this pokemon is attached.
*/
public BattleField getField() {
return m_field;
}
/**
* Get the name of this pokemon's moves.
*/
public String getMoveName(int i) {
if (!(i < m_move.length) || (m_move[i] == null)) { return null; }
return m_move[i].getName();
}
/**
* Determine whether this pokemon has fainted.
*/
public boolean isFainted() {
return m_fainted;
}
/**
* Get the effectiveness of this pokemon attacking a particular type.
*/
public static double getEffectiveness(List<?> statuses, PokemonType move,
PokemonType pokemon, boolean enemy) {
double expected = move.getMultiplier(pokemon);
synchronized (statuses) {
Iterator<?> i = statuses.iterator();
while (i.hasNext()) {
StatusEffect eff = (StatusEffect) i.next();
if (eff.isActive() && eff.isEffectivenessTransformer(enemy)) {
double actual = eff.getEffectiveness(move, pokemon, enemy);
if (actual != expected) return actual;
}
}
}
return expected;
}
public double getEffectiveness(PokemonType move, PokemonType pokemon,
boolean enemy) {
return getEffectiveness(m_statuses, move, pokemon, enemy);
}
/**
* Is this pokemon immobilised?
*
* @param exception
* status not to check for
*/
public boolean isImmobilised(Class<?> exception) {
synchronized (m_statuses) {
Collections.sort(m_statuses, new Comparator<Object>() {
public int compare(Object o1, Object o2) {
StatusEffect e1 = (StatusEffect) o1;
StatusEffect e2 = (StatusEffect) o2;
return e1.getTier() - e2.getTier();
}
});
Iterator<StatusEffect> i = m_statuses.iterator();
while (i.hasNext()) {
StatusEffect eff = i.next();
if (eff.isActive() && eff.immobilises(this)) {
if ((exception == null)
|| !exception.isAssignableFrom(eff.getClass())) {
m_lastMove = null;
m_firstTurn = false;
return true;
}
}
}
}
return false;
}
/**
* Transform a move based on the status effects applied to the pokemon.
*
* @param enemy
* whether this Pokemon is an enemy
*/
protected MoveListEntry getTransformedMove(MoveListEntry move, boolean enemy) {
// For now, do this in no particular order.
synchronized (m_statuses) {
Iterator<StatusEffect> i = m_statuses.iterator();
while (i.hasNext()) {
StatusEffect eff = i.next();
if (eff.isActive() && eff.isMoveTransformer(enemy)) {
move = eff.getMove(this, (MoveListEntry) move.clone(), enemy);
if (move == null) { return null; }
}
}
}
return move;
}
/**
* Get the last move used by this pokemon, or null if the pokemon has not used
* a move since it has been out.
*/
public MoveListEntry getLastMove() {
return m_lastMove;
}
/**
* Use one of this pokemon's intrinsic moves.
*/
public int useMove(int i, Pokemon target) {
if (i == -1) {
MoveListEntry move = BattleField.getStruggle();
int ret = useMove(move, target);
m_lastMove = move;
m_firstTurn = false;
return ret;
}
if ((i >= m_move.length) || (m_move[i] == null)) return 0;
if (m_pp[i] == 0) return 0;
MoveListEntry entry = m_move[i];
PokemonMove move = m_move[i].getMove();
final int cost = (target.hasAbility("Pressure") && move.isAttack()) ? 2 : 1;
m_pp[i] -= cost;
if (m_pp[i] < 0) m_pp[i] = 0;
int ret = useMove(entry, target);
m_lastMove = entry;
m_firstTurn = false;
return ret;
}
/**
* Use a a move from the move list (so its name is known and displayed).
*/
public int useMove(MoveListEntry move, Pokemon target) {
PokemonMove pmove = move.getMove();
move = getTransformedMove(move, false);
if (move != null) {
if (target != this) {
if ((move = target.getTransformedMove(move, true)) == null) { return 0; }
}
m_field.informUseMove(this, move.getName());
int hp = target.getHealth();
pmove = move.getMove();
useMove(pmove, target);
int damage = hp - target.getHealth();
if (damage > 0) {
target.informDamaged(this, move, damage);
}
return damage;
}
return 0;
}
/**
* Check for accuracy and then use an arbitrary move.
*/
public int useMove(PokemonMove move, Pokemon target) {
if (!move.attemptHit(m_mech, this, target)) { return 0; }
return move.use(m_mech, this, target);
}
/**
* Inform that this pokemon was damaged.
*/
private void informDamaged(Pokemon source, MoveListEntry entry, int damage) {
int size = m_statuses.size();
for (int i = 0; i < size; ++i) {
StatusEffect eff = m_statuses.get(i);
if (eff.isActive() && eff.isListener()) {
eff.informDamaged(source, this, entry, damage);
}
}
}
/**
* Change the health of this pokemon, doing damage to a substitute if one is
* present.
*/
public void changeHealth(int hp) {
changeHealth(hp, false);
}
/**
* Change the health of this pokemon, optionally hitting through a substitute.
*/
public void changeHealth(int hp, boolean throughSubstitute) {
if (m_fainted) return;
if (!hasSubstitute() || throughSubstitute || (hp > 0)) {
if (throughSubstitute && (hp < 0) && hasAbility("Magic Guard")) return;
int max = m_stat[S_HP];
int display = hp;
int result = m_hp + hp;
if (hasEffect(PendanticDamageClause.class)) {
if (result > max) {
display = max - m_hp;
} else if (result < 0) {
display = -m_hp;
}
}
if (m_field != null) {
m_field.informPokemonHealthChanged(this, display);
if ((result <= 0) && !throughSubstitute) {
boolean live = false;
if (hasEffect(MoveList.EndureEffect.class)) {
m_field.showMessage(getName() + " endured the attack!");
live = true;
} else if ((m_hp == max) && hasItem("Focus Sash")) {
m_field.showMessage(getName() + " hung on using its Focus Sash!");
live = true;
setItem(null);
} else if (hasItem("Focus Band")) {
if (m_field.getRandom().nextDouble() <= 0.1) {
m_field.showMessage(getName() + " hung on using its Focus Band!");
live = true;
}
}
if (live) {
hp = -m_hp + 1;
}
}
}
m_hp += hp;
if (m_hp <= 0) {
faint();
} else if (m_hp > max) {
m_hp = max;
}
} else {
m_substitute += hp;
String name = getName();
m_field.showMessage("The substitute took damage for " + name + "!");
if (m_substitute <= 0) {
m_field.showMessage(name + "'s substitute faded!");
m_substitute = 0;
removeStatus(MoveList.SubstituteEffect.class);
}
}
}
/**
* Cause this pokemon to faint.
*/
public void faint() {
m_hp = 0;
m_fainted = true;
if (m_field != null) {
m_field.informPokemonFainted(m_party, getId());
m_field.checkBattleEnd(m_party);
}
}
/**
* Get the health of this pokemon.
*/
public int getHealth() {
return m_hp;
}
/**
* Inform listeners that a status effect was applied this pokemon.
*/
private void informStatusListeners(Pokemon source, StatusEffect eff,
boolean applied) {
synchronized (m_statuses) {
int size = m_statuses.size();
for (int i = 0; i < size; ++i) {
StatusEffect j = m_statuses.get(i);
if (j.isActive() && (j instanceof StatusListener)) {
StatusListener k = (StatusListener) j;
if (applied) {
k.informStatusApplied(source, this, eff);
} else {
k.informStatusRemoved(this, eff);
}
}
}
}
}
/**
* Return whether this pokemon must struggle if it wants to use a move.
*/
public boolean mustStruggle() {
for (int i = 0; i < m_move.length; ++i) {
try {
if (getVetoingEffect(i) != null) {
continue;
}
} catch (MoveQueueException e) {
continue;
}
if (getPp(i) > 0) { return false; }
}
return true;
}
/**
* Check whether the effects present on this pokemon permit the application of
* the given status effect to this pokemon.
*/
public boolean allowsStatus(StatusEffect eff, Pokemon source) {
Iterator<StatusEffect> i = m_statuses.iterator();
while (i.hasNext()) {
StatusEffect clause = i.next();
if ((clause == null) || !clause.isActive()) continue;
if (!clause.allowsStatus(eff, source, this)) return false;
}
return true;
}
/**
* Add a status effect to this pokemon.
*/
public StatusEffect addStatus(Pokemon source, StatusEffect eff) {
if (m_fainted) return null;
// Make sure there isn't another copy of this effect applied already.
synchronized (m_statuses) {
Iterator<StatusEffect> i = m_statuses.iterator();
while (i.hasNext()) {
Object o = i.next();
if (o != null) {
StatusEffect j = (StatusEffect) o;
if (!j.isRemovable() && (eff.equals(j) || eff.isExclusiveWith(j))) { return null; }
}
}
}
StatusEffect applied = (StatusEffect) eff.clone();
applied.activate();
applied.setInducer(source);
if ((m_field != null) && !allowsStatus(applied, source)) return null;
if (applied.apply(this)) {
m_statuses.add(applied);
if (m_field != null) {
m_field.informStatusApplied(this, applied);
}
informStatusListeners(source, applied, true);
}
return applied;
}
/**
* Add a status effect to this pokemon caused by item.
*/
public StatusEffect addStatus(StatusEffect eff) {
if (m_fainted) return null;
// Make sure there isn't another copy of this effect applied already.
synchronized (m_statuses) {
Iterator<StatusEffect> i = m_statuses.iterator();
while (i.hasNext()) {
Object o = i.next();
if (o != null) {
StatusEffect j = (StatusEffect) o;
if (!j.isRemovable() && (eff.equals(j) || eff.isExclusiveWith(j))) { return null; }
}
}
}
StatusEffect applied = (StatusEffect) eff.clone();
applied.activate();
if (applied.apply(this)) {
m_statuses.add(applied);
if (m_field != null) {
m_field.informStatusApplied(this, applied);
}
}
return applied;
}
public PokemonNature getNature() {
return m_nature;
}
public StatMultiplier getAccuracy() {
return m_accuracy;
}
public StatMultiplier getEvasion() {
return m_evasion;
}
public void setLevel(int level) {
m_level = level;
}
public int getLevel() {
return m_level;
}
public int getIv(int i) throws StatException {
if ((i < 0) || (i > 5)) throw new StatException();
return m_iv[i];
}
public int getEv(int i) throws StatException {
if ((i < 0) || (i > 5)) throw new StatException();
return m_ev[i];
}
/**
* Get a stat multiplier, including the ones for accuracy and evasion.
*/
public StatMultiplier getMultiplier(int i) throws StatException {
if (i < m_multiplier.length) {
if (m_multiplier == null) {
m_multiplier = new StatMultiplier[m_stat.length];
}
if (m_multiplier[i] == null) m_multiplier[i] = new StatMultiplier(false);
if (i < 0) throw new StatException();
if (i < 6) return m_multiplier[i];
if (i == S_ACCURACY) return m_accuracy;
if (i == S_EVASION) return m_evasion;
}
throw new StatException();
}
public int getRawStat(int i) {
if ((i < 0) || (i > 5)) throw new StatException();
return m_stat[i];
}
public void setRawStat(int i, int newStat) {
if ((i < 0) || (i > 5)) throw new StatException();
m_stat[i] = newStat;
}
public int getStat(int i, double multiplier) {
if ((i < 0) || (i > 5)) throw new StatException();
if (m_stat == null) {
calculateStats(false);
}
return (int) ((m_stat[i]) * multiplier);
}
public int getStat(int i) {
if (m_multiplier == null) {
m_multiplier = new StatMultiplier[m_stat.length];
}
if (m_multiplier[i] == null) m_multiplier[i] = new StatMultiplier(false);
if ((i < 0) || (i > 5)) throw new StatException();
// Consider stat modifications.
return getStat(i, m_multiplier[i].getMultiplier());
}
/**
* Returns an arraylist of moves waiting to be learned
*
* @return
*/
public ArrayList<String> getMovesLearning() {
return m_movesLearning;
}
/**
* Returns 0 if they are the same Pokemon
*
* @param p
* @return
*/
public int compareTo(Pokemon p) {
if (this.getDateCaught() != null && p.getDateCaught() != null
&& p.getDateCaught().equalsIgnoreCase(this.getDateCaught())) return 0;
if (this.getDatabaseID() != -1 && p.getDatabaseID() != -1
&& p.getDatabaseID() == this.getDatabaseID()) return 0;
if (p.getSpeciesName() == this.getSpeciesName()
&& p.getStat(0) == this.getStat(0) && p.getStat(1) == this.getStat(1))
return 0;
return -1;
}
/**
* Reinitialises the Pokemon
*
* @param b
*/
public void reinitialise() {
boolean hasNeg = false;
for (int i = 0; i < 6; i++) {
if (getEv(i) < 0) hasNeg = true;
}
if (hasNeg || getEvTotal() > 510) {
for (int i = 0; i < 6; i++) {
setEv(i, 0);
}
}
m_accuracy = new StatMultiplier(true);
m_evasion = new StatMultiplier(true);
m_statuses = new ArrayList<StatusEffect>();
m_movesLearning = new ArrayList<String>();
/*
* m_pp = new int[4]; m_maxPp = new int[4]; m_ppUp = new int[4];
*/
/* Check validity of moves */
if(m_move[0] != null && !PokemonSpecies.getDefaultData().
canLearn(PokemonSpecies.getDefaultData().
getPokemonByName(this.getSpeciesName()), getMoveName(0))) {
m_move[0] = null;
}
if(m_move[1] != null && !PokemonSpecies.getDefaultData().
canLearn(PokemonSpecies.getDefaultData().
getPokemonByName(this.getSpeciesName()), getMoveName(1))) {
m_move[1] = null;
}
if(m_move[2] != null && !PokemonSpecies.getDefaultData().
canLearn(PokemonSpecies.getDefaultData().
getPokemonByName(this.getSpeciesName()), getMoveName(2))) {
m_move[2] = null;
}
if(m_move[3] != null && !PokemonSpecies.getDefaultData().
canLearn(PokemonSpecies.getDefaultData().
getPokemonByName(this.getSpeciesName()), getMoveName(3))) {
m_move[3] = null;
}
}
/**
* Get EV total.
*/
public int getEvTotal() {
int total = 0;
for (int i = 0; i < 6; i++) {
total += getEv(i);
}
return total;
}
/**
* Sets the name of the original trainer
*
* @param name
*/
public void setOriginalTrainer(String name) {
m_originalTrainer = name;
}
public void setOriginalNo(int m_no) {
m_originalNo = m_no;
}
/**
* Evolves this pokemon into the new species
*
* @param species
*/
private void evolve(PokemonSpecies species) {
m_species = species.getSpeciesNumber();
m_name = species.getName();
m_base = species.getBaseStats();
m_genders = species.m_genders;
m_type = species.getTypes();
try {
String[] abilities = PokemonSpecies.getDefaultData()
.getPossibleAbilities(getSpeciesName());
m_ability = IntrinsicAbility.getInstance(abilities[DataService
.getBattleMechanics().getRandom().nextInt(abilities.length)]);
} catch (Exception e) {
e.printStackTrace();
}
calculateStats(m_base, m_iv, m_ev);
m_evolution = null;
}
/**
* Returns the EXP of the Pokemon
*
* @return
*/
public double getExp() {
return m_exp;
}
/**
* Sets the base EXP of the Pokemon
*
* @param baseEXP
*/
public void setBaseExp(int baseEXP) {
m_baseExp = baseEXP;
}
/**
* Sets the happiness of the Pokemon
*
* @param happiness
*/
public void setHappiness(int happiness) {
m_happiness = happiness <= 255 ? happiness : 255;
}
/**
* Sets the exp of the Pokemon
*
* @param exp
*/
public void setExp(double exp) {
DecimalFormat form = new DecimalFormat("#.##");
if (exp > 100000000) {
m_exp = 100000000;
return;
}
m_exp = Double.valueOf(form.format(exp));
}
/**
* Returns the happiness of the Pokemon
*
* @return
*/
public int getHappiness() {
return m_happiness;
}
/**
* Sets the EV of the pokemon
*
* @param i
* @param j
*/
public void setEv(int i, int j) {
if (j < 256) m_ev[i] = j;
else
m_ev[i] = 255;
}
/**
* Sets the EXP growth rate of the Pokemon
*
* @param growthRate
*/
public void setExpType(ExpTypes growthRate) {
m_expType = growthRate;
}
/**
* Returns the EXP growth rate of the Pokemon
*
* @return
*/
public ExpTypes getExpType() {
return m_expType;
}
/**
* Returns the base EXP of the Pokemon
*
* @return
*/
public int getBaseExp() {
return m_baseExp;
}
/**
* Learn a new move.
*
* @param idx
* @param moveName
*/
public void learnMove(int idx, String moveName) {
if (idx >= 0 && idx <= 3) {
if (MoveList.getDefaultData().containsMove(moveName)) {
m_move[idx] = MoveList.getDefaultData().getMove(moveName);
m_maxPp[idx] = m_move[idx].getMove().getPp();
setPp(idx, m_move[idx].getMove().getPp());
}
}
}
/**
* Get the date this pokemon was caught
*/
public String getDateCaught() {
return m_dateCaught;
}
/**
* Set the date this pokemon was caught
*
* @param date
*/
public void setDateCaught(String date) {
m_dateCaught = date;
}
/**
* Returns the original trainer's name
*
* @return
*/
public String getOriginalTrainer() {
return m_originalTrainer;
}
}