Package org.pokenet.server.battle

Source Code of org.pokenet.server.battle.Pokemon

/*
* 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;
  }
}
TOP

Related Classes of org.pokenet.server.battle.Pokemon

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.