Package org.pokenet.server.battle.mechanics.moves

Source Code of org.pokenet.server.battle.mechanics.moves.MoveList$LeechSeedEffect

/*
* MoveList.java
*
* Created on December 16, 2006, 1:51 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.mechanics.moves;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.Set;

import org.pokenet.server.battle.BattleField;
import org.pokenet.server.battle.BattleTurn;
import org.pokenet.server.battle.Pokemon;
import org.pokenet.server.battle.PokemonSpecies;
import org.pokenet.server.battle.mechanics.BattleMechanics;
import org.pokenet.server.battle.mechanics.JewelMechanics;
import org.pokenet.server.battle.mechanics.PokemonType;
import org.pokenet.server.battle.mechanics.StatMultiplier;
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.FreezeEffect;
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.SleepEffect;
import org.pokenet.server.battle.mechanics.statuses.StatChangeEffect;
import org.pokenet.server.battle.mechanics.statuses.StatusEffect;
import org.pokenet.server.battle.mechanics.statuses.ToxicEffect;
import org.pokenet.server.battle.mechanics.statuses.abilities.IntrinsicAbility;
import org.pokenet.server.battle.mechanics.statuses.field.FieldEffect;
import org.pokenet.server.battle.mechanics.statuses.field.HailEffect;
import org.pokenet.server.battle.mechanics.statuses.field.RainEffect;
import org.pokenet.server.battle.mechanics.statuses.field.SandstormEffect;
import org.pokenet.server.battle.mechanics.statuses.field.SunEffect;
import org.pokenet.server.battle.mechanics.statuses.items.Berry;
import org.pokenet.server.battle.mechanics.statuses.items.ChoiceBandItem;
import org.pokenet.server.battle.mechanics.statuses.items.HoldItem;


/**
* This class contains static methods for maintaining the server's list of
* moves that pokemon can have.
* @author Ben
* @author Colin
*/
public class MoveList {

  private static MoveList m_inst = new MoveList(true);
  private ArrayList<MoveListEntry> m_moves;

  /**
   * Get the default MoveList.
   */
  public static MoveList getDefaultData() {
    return m_inst;
  }

  public boolean containsMove(String moveName) {
    for(int i = 0; i < m_moves.size() / 2; i++) {
      if(m_moves.get(i).getName().equalsIgnoreCase(moveName))
        return true;
      else if(m_moves.get(m_moves.size() - 1 - i).getName().equalsIgnoreCase(moveName))
        return true;
    }
    return false;
  }

  /**
   * Initialise all moves that do not have a special effect.
   */
  private void initNonStatusMoves() {
    m_moves.add(new MoveListEntry("Cut",
        new PokemonMove(PokemonType.T_NORMAL, 50, 0.95, 30)));

    m_moves.add(new MoveListEntry("Dragon Claw",
        new PokemonMove(PokemonType.T_DRAGON, 80, 1.0, 15)));

    m_moves.add(new MoveListEntry("Drill Peck",
        new PokemonMove(PokemonType.T_FLYING, 80, 1.0, 20)));

    m_moves.add(new MoveListEntry("Earthquake",
        new PokemonMove(PokemonType.T_GROUND, 100, 1.0, 10)));

    m_moves.add(new MoveListEntry("Egg Bomb",
        new PokemonMove(PokemonType.T_NORMAL, 75, 1.0, 10)));

    m_moves.add(new MoveListEntry("Gust",
        new PokemonMove(PokemonType.T_FLYING, 35, 1.0, 35)));

    m_moves.add(new MoveListEntry("Horn Attack",
        new PokemonMove(PokemonType.T_NORMAL, 65, 1.0, 25)));

    m_moves.add(new MoveListEntry("Hydro Pump",
        new PokemonMove(PokemonType.T_WATER, 120, 0.8, 5)));

    m_moves.add(new MoveListEntry("Hyper Voice",
        new PokemonMove(PokemonType.T_NORMAL, 90, 1.0, 10)));

    m_moves.add(new MoveListEntry("Megahorn",
        new PokemonMove(PokemonType.T_BUG, 120, 0.85, 10)));

    m_moves.add(new MoveListEntry("Mega Punch",
        new PokemonMove(PokemonType.T_NORMAL, 80, 0.85, 20)));

    m_moves.add(new MoveListEntry("Mega Kick",
        new PokemonMove(PokemonType.T_NORMAL, 120, 0.75, 5)));

    m_moves.add(new MoveListEntry("Peck",
        new PokemonMove(PokemonType.T_FLYING, 35, 1.0, 35)));

    m_moves.add(new MoveListEntry("Pound",
        new PokemonMove(PokemonType.T_NORMAL, 40, 1.0, 35)));

    m_moves.add(new MoveListEntry("Rock Throw",
        new PokemonMove(PokemonType.T_ROCK, 50, 0.9, 15)));

    m_moves.add(new MoveListEntry("Rolling Kick",
        new PokemonMove(PokemonType.T_NORMAL, 60, 0.85, 15)));

    m_moves.add(new MoveListEntry("Scratch",
        new PokemonMove(PokemonType.T_NORMAL, 40, 1.0, 35)));

    m_moves.add(new MoveListEntry("Sky Uppercut",
        new PokemonMove(PokemonType.T_FIGHTING, 85, 0.9, 15)));

    m_moves.add(new MoveListEntry("Slam",
        new PokemonMove(PokemonType.T_NORMAL, 80, 0.75, 20)));

    m_moves.add(new MoveListEntry("Surf",
        new PokemonMove(PokemonType.T_WATER, 95, 1.0, 15)));

    m_moves.add(new MoveListEntry("Tackle",
        new PokemonMove(PokemonType.T_NORMAL, 35, 0.95, 35)));

    m_moves.add(new MoveListEntry("Vicegrip",
        new PokemonMove(PokemonType.T_NORMAL, 55, 1.0, 30)));

    m_moves.add(new MoveListEntry("Vine Whip",
        new PokemonMove(PokemonType.T_GRASS, 35, 1.0, 10)));

    m_moves.add(new MoveListEntry("Water Gun",
        new PokemonMove(PokemonType.T_WATER, 40, 1.0, 25)));

    m_moves.add(new MoveListEntry("Waterfall",
        new PokemonMove(PokemonType.T_WATER, 80, 1.0, 15)));

    m_moves.add(new MoveListEntry("Wing Attack",
        new PokemonMove(PokemonType.T_FLYING, 60, 1.0, 35)));

    m_moves.add(new MoveListEntry("Strength",
        new PokemonMove(PokemonType.T_NORMAL, 80, 1.0, 15)));

    m_moves.add(new MoveListEntry("Return",
        new PokemonMove(PokemonType.T_NORMAL, 102, 1.0, 20)));

    m_moves.add(new MoveListEntry("Frustration",
        new PokemonMove(PokemonType.T_NORMAL, 102, 1.0, 20)));

    m_moves.add(new MoveListEntry("Weather Ball",
        new PokemonMove(PokemonType.T_NORMAL, 50, 1.0, 10)));
  }

  private void initStatusMoves() {       
    m_moves.add(new MoveListEntry("Acid", new StatusMove(
        PokemonType.T_POISON, 40, 1.0, 30, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_DEFENCE, false)
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Agility", new StatusMove(
        PokemonType.T_PSYCHIC, 0, 1.0, 30, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPEED, true, 2)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Acid Armor", new StatusMove(
        PokemonType.T_POISON, 0, 1.0, 40, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_DEFENCE, true, 2)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Amnesia", new StatusMove(
        PokemonType.T_PSYCHIC, 0, 1.0, 20, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPDEFENCE, true, 2)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Astonish", new StatusMove(
        PokemonType.T_GHOST, 30, 1.0, 15, new StatusEffect[] {
            new FlinchEffect()
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("Attract", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 15, new StatusEffect[] {
            new AttractEffect()
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Aurora Beam", new StatusMove(
        PokemonType.T_ICE, 65, 1.0, 20, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_ATTACK, false)
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Barrier", new StatusMove(
        PokemonType.T_PSYCHIC, 0, 1.0, 30, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_DEFENCE, true, 2)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Bite", new StatusMove(
        PokemonType.T_DARK, 60, 1.0, 25, new StatusEffect[] {
            new FlinchEffect()
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Blaze Kick", new StatusMove(
        PokemonType.T_FIRE, 85, 0.9, 10, new StatusEffect[] {
            new BurnEffect()
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Blizzard", new StatusMove(
        PokemonType.T_ICE, 120, 0.7, 5, new StatusEffect[] {
            new FreezeEffect()
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Body Slam", new StatusMove(
        PokemonType.T_NORMAL, 85, 1.0, 15, new StatusEffect[] {
            new ParalysisEffect()
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("Bone Club", new StatusMove(
        PokemonType.T_GROUND, 65, 0.85, 20, new StatusEffect[] {
            new FlinchEffect()
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("Bubble", new StatusMove(
        PokemonType.T_WATER, 20, 1.0, 30, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPEED, false)
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Bubblebeam", new StatusMove(
        PokemonType.T_WATER, 65, 1.0, 20, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPEED, false)
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("Bulk Up", new StatusMove(
        PokemonType.T_FIGHTING, 0, 1.0, 20, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_ATTACK, true),
            new StatChangeEffect(Pokemon.S_DEFENCE, true)
        },
        new boolean[] { true, true },
        new double[] { 1.0, 1.0 }
    )));

    m_moves.add(new MoveListEntry("Calm Mind", new StatusMove(
        PokemonType.T_PSYCHIC, 0, 1.0, 20, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPATTACK, true),
            new StatChangeEffect(Pokemon.S_SPDEFENCE, true)
        },
        new boolean[] { true, true },
        new double[] { 1.0, 1.0 }
    )));

    m_moves.add(new MoveListEntry("Charm", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 20, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_ATTACK, false, 2)
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Constrict", new StatusMove(
        PokemonType.T_NORMAL, 10, 1.0, 35, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPEED, false)
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Confuse Ray", new StatusMove(
        PokemonType.T_GHOST, 0, 1.0, 10, new StatusEffect[] {
            new ConfuseEffect()
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Confusion", new StatusMove(
        PokemonType.T_PSYCHIC, 50, 1.0, 25, new StatusEffect[] {
            new ConfuseEffect()
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Cosmic Power", new StatusMove(
        PokemonType.T_PSYCHIC, 0, 1.0, 20, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_DEFENCE, true),
            new StatChangeEffect(Pokemon.S_SPDEFENCE, true)
        },
        new boolean[] { true, true },
        new double[] { 1.0, 1.0 }
    )));

    m_moves.add(new MoveListEntry("Cotton Spore", new StatusMove(
        PokemonType.T_GRASS, 0, 0.85, 40, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPEED, false, 2)
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    // TBD: SHOULD BE SPECIAL DEFENCE IN ADVANCE
    m_moves.add(new MoveListEntry("Crunch", new StatusMove(
        PokemonType.T_DARK, 80, 1.0, 15, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_DEFENCE, false)
        },
        new boolean[] { false },
        new double[] { 0.2 }
    )));

    m_moves.add(new MoveListEntry("Crush Claw", new StatusMove(
        PokemonType.T_NORMAL, 75, 0.95, 10, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_DEFENCE, false)
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Defense Curl", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 40, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_DEFENCE, true),
            new DefenseCurlEffect()          
        },
        new boolean[] { true, true },
        new double[] { 1.0, 1.0 }
    )));

    m_moves.add(new MoveListEntry("Dizzy Punch", new StatusMove(
        PokemonType.T_NORMAL, 70, 1.0, 10, new StatusEffect[] {
            new ConfuseEffect()
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("Double Team", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 15, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_EVASION, true)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Dragonbreath", new StatusMove(
        PokemonType.T_DRAGON, 60, 1.0, 20, new StatusEffect[] {
            new ParalysisEffect()
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Dragon Dance", new StatusMove(
        PokemonType.T_DRAGON, 0, 1.0, 20, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_ATTACK, true),
            new StatChangeEffect(Pokemon.S_SPEED, true)
        },
        new boolean[] { true, true },
        new double[] { 1.0, 1.0 }
    )));

    m_moves.add(new MoveListEntry("Dynamicpunch", new StatusMove(
        PokemonType.T_FIGHTING, 100, 0.5, 5, new StatusEffect[] {
            new ConfuseEffect()
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Ember", new StatusMove(
        PokemonType.T_FIRE, 40, 1.0, 25, new StatusEffect[] {
            new BurnEffect()
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Extrasensory", new StatusMove(
        PokemonType.T_PSYCHIC, 80, 1.0, 30, new StatusEffect[] {
            new FlinchEffect()
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Fake Tears", new StatusMove(
        PokemonType.T_DARK, 0, 1.0, 20, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPDEFENCE, false, 2)
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Featherdance", new StatusMove(
        PokemonType.T_FLYING, 0, 1.0, 15, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_ATTACK, false, 2)
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Fire Blast", new StatusMove(
        PokemonType.T_FIRE, 120, 0.85, 5, new StatusEffect[] {
            new BurnEffect()
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("Fire Punch", new StatusMove(
        PokemonType.T_FIRE, 75, 1.0, 15, new StatusEffect[] {
            new BurnEffect()
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Flamethrower", new StatusMove(
        PokemonType.T_FIRE, 95, 1.0, 15, new StatusEffect[] {
            new BurnEffect()
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Flame Wheel", new StatusMove(
        PokemonType.T_FIRE, 60, 1.0, 25, new StatusEffect[] {
            new BurnEffect()
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Flash", new StatusMove(
        PokemonType.T_NORMAL, 0, 0.7, 20, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_ACCURACY, false)
        },
        new boolean[] { false },
        new double[] { 1.0 }
    ) {
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        setAccuracy((mech instanceof JewelMechanics) ? 1.0 : 0.7);
        return super.attemptHit(mech, user, target);
      }
    }));

    m_moves.add(new MoveListEntry("Flatter", new StatusMove(
        PokemonType.T_DARK, 0, 1.0, 15, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPATTACK, true, 2),
            new ConfuseEffect()
        },
        new boolean[] { false, false },
        new double[] { 1.0, 1.0 }
    )));

    m_moves.add(new MoveListEntry("Glare", new StatusMove(
        PokemonType.T_NORMAL, 0, 0.75, 30, new StatusEffect[] {
            new ParalysisEffect()
        },
        new boolean[] { false },
        new double[] { 1.0 }
    ) {
      public boolean isEffective(Pokemon target) {
        if (!(target.getField().getMechanics() instanceof JewelMechanics))
          return super.isEffective(target);
        /** Believe it or not, in D/P, this move completely ignores
         *  type immunities!
         */
        return true;
      }
    }));

    m_moves.add(new MoveListEntry("Grasswhistle", new StatusMove(
        PokemonType.T_GRASS, 0, 0.55, 15, new StatusEffect[] {
            new SleepEffect()
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Growl", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 40, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_ATTACK, false)
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Growth", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 40, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPATTACK, true)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Harden", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 30, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_DEFENCE, true)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Headbutt", new StatusMove(
        PokemonType.T_NORMAL, 70, 1.0, 15, new StatusEffect[] {
            new FlinchEffect()
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("Heat Wave", new StatusMove(
        PokemonType.T_FIRE, 100, 0.9, 10, new StatusEffect[] {
            new BurnEffect()
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Howl", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 40, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_ATTACK, true)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Hyper Fang", new StatusMove(
        PokemonType.T_NORMAL, 80, 0.9, 15, new StatusEffect[] {
            new FlinchEffect()
        },
        new boolean[] { true },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Hypnosis", new StatusMove(
        PokemonType.T_PSYCHIC, 0, 0.6, 20, new StatusEffect[] {
            new SleepEffect()
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Ice Beam", new StatusMove(
        PokemonType.T_ICE, 95, 1.0, 10, new StatusEffect[] {
            new FreezeEffect()
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Ice Punch", new StatusMove(
        PokemonType.T_ICE, 75, 1.0, 15, new StatusEffect[] {
            new FreezeEffect()
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Icy Wind", new StatusMove(
        PokemonType.T_ICE, 55, 0.95, 15, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPEED, false)
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Iron Defense", new StatusMove(
        PokemonType.T_STEEL, 0, 1.0, 15, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_DEFENCE, true, 2)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Iron Tail", new StatusMove(
        PokemonType.T_STEEL, 100, 0.75, 15, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_DEFENCE, false)
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("Kinesis", new StatusMove(
        PokemonType.T_PSYCHIC, 0, 0.8, 15, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_ACCURACY, false)
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Leer", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 30, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_DEFENCE, false)
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Lick", new StatusMove(
        PokemonType.T_GHOST, 20, 1.0, 20, new StatusEffect[] {
            new ParalysisEffect()
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("Lovely Kiss", new StatusMove(
        PokemonType.T_NORMAL, 0, 0.75, 10, new StatusEffect[] {
            new SleepEffect()
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Luster Purge", new StatusMove(
        PokemonType.T_PSYCHIC, 70, 1.0, 5, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPDEFENCE, false)
        },
        new boolean[] { false },
        new double[] { 0.5 }
    )));

    m_moves.add(new MoveListEntry("Meditate", new StatusMove(
        PokemonType.T_PSYCHIC, 0, 1.0, 40, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_ATTACK, true)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Metal Claw", new StatusMove(
        PokemonType.T_STEEL, 50, 1.0, 40, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_ATTACK, true)
        },
        new boolean[] { true },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Metal Sound", new StatusMove(
        PokemonType.T_STEEL, 0, 0.85, 40, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPDEFENCE, false, 2)
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Meteor Mash", new StatusMove(
        PokemonType.T_STEEL, 100, 0.85, 10, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_ATTACK, true)
        },
        new boolean[] { true },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Minimize", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 20, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_EVASION, true),
            new MinimizeEffect()
        },
        new boolean[] { true, true },
        new double[] { 1.0, 1.0 }
    )));

    m_moves.add(new MoveListEntry("Mist Ball", new StatusMove(
        PokemonType.T_PSYCHIC, 70, 1.0, 5, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPDEFENCE, false)
        },
        new boolean[] { false },
        new double[] { 0.5 }
    )));

    m_moves.add(new MoveListEntry("Mud Shot", new StatusMove(
        PokemonType.T_GROUND, 55, 0.95, 15, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPEED, false)
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Mud-Slap", new StatusMove(
        PokemonType.T_GROUND, 20, 1.0, 10, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_ACCURACY, false)
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Muddy Water", new StatusMove(
        PokemonType.T_WATER, 95, 0.85, 10, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_ACCURACY, false)
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("Mud-Slap", new StatusMove(
        PokemonType.T_GROUND, 20, 1.0, 10, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_ACCURACY, false)
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Needle Arm", new StatusMove(
        PokemonType.T_GRASS, 60, 1.0, 15, new StatusEffect[] {
            new FlinchEffect()
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("Octazooka", new StatusMove(
        PokemonType.T_WATER, 65, 0.85, 10, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_ACCURACY, false)
        },
        new boolean[] { false },
        new double[] { 0.5 }
    )));

    m_moves.add(new MoveListEntry("Overheat", new StatusMove(
        PokemonType.T_FIRE, 140, 0.90, 5, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPATTACK, false, 2)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Poison Gas", new StatusMove(
        PokemonType.T_POISON, 0, 0.55, 40, new StatusEffect[] {
            new PoisonEffect()
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Poison Sting", new StatusMove(
        PokemonType.T_POISON, 15, 1.0, 35, new StatusEffect[] {
            new PoisonEffect()
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("Poisonpowder", new StatusMove(
        PokemonType.T_POISON, 0, 0.75, 35, new StatusEffect[] {
            new PoisonEffect()
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Toxic", new StatusMove(
        PokemonType.T_POISON, 0, 0.85, 10, new StatusEffect[] {
            new ToxicEffect()
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Poison Fang", new StatusMove(
        PokemonType.T_POISON, 50, 1.0, 15, new StatusEffect[] {
            new ToxicEffect()
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("Powder Snow", new StatusMove(
        PokemonType.T_ICE, 40, 1.0, 25, new StatusEffect[] {
            new FreezeEffect()
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Psybeam", new StatusMove(
        PokemonType.T_PSYCHIC, 65, 1.0, 20, new StatusEffect[] {
            new ConfuseEffect()
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Psychic", new StatusMove(
        PokemonType.T_PSYCHIC, 90, 1.0, 10, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPDEFENCE, false)
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Psycho Boost", new StatusMove(
        PokemonType.T_PSYCHIC, 140, 0.9, 5, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPATTACK, false, 2)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Rock Slide", new StatusMove(
        PokemonType.T_ROCK, 75, 0.9, 10, new StatusEffect[] {
            new FlinchEffect()
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("Rock Smash", new StatusMove(
        PokemonType.T_FIGHTING, 20, 1.0, 15, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_DEFENCE, false)
        },
        new boolean[] { false },
        new double[] { 0.3 }
    ) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        setPower(mech instanceof JewelMechanics ? 40 : 20);
        return super.use(mech, user, target);
      }
    }
    ));

    m_moves.add(new MoveListEntry("Rock Tomb", new StatusMove(
        PokemonType.T_ROCK, 50, 0.8, 10, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPEED, false)
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Sand-Attack", new StatusMove(
        PokemonType.T_GROUND, 0, 1.0, 15, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_ACCURACY, false)
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Sacred Fire", new StatusMove(
        PokemonType.T_FIRE, 100, 0.95, 5, new StatusEffect[] {
            new BurnEffect()
        },
        new boolean[] { false },
        new double[] { 0.5 }
    )));

    m_moves.add(new MoveListEntry("Scary Face", new StatusMove(
        PokemonType.T_NORMAL, 0, 0.9, 10, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPEED, false, 2)
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Screech", new StatusMove(
        PokemonType.T_NORMAL, 0, 0.85, 40, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_DEFENCE, false, 2)
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Shadow Ball", new StatusMove(
        PokemonType.T_GHOST, 80, 1.0, 15, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPDEFENCE, false)
        },
        new boolean[] { false },
        new double[] { 0.2 }
    )));

    m_moves.add(new MoveListEntry("Sharpen", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 30, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_ATTACK, true, 1)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Signal Beam", new StatusMove(
        PokemonType.T_BUG, 75, 1.0, 15, new StatusEffect[] {
            new ConfuseEffect()
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Sing", new StatusMove(
        PokemonType.T_NORMAL, 0, 0.55, 15, new StatusEffect[] {
            new SleepEffect()
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Sleep Powder", new StatusMove(
        PokemonType.T_GRASS, 0, 0.75, 15, new StatusEffect[] {
            new SleepEffect()
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Sludge", new StatusMove(
        PokemonType.T_POISON, 65, 1.0, 20, new StatusEffect[] {
            new PoisonEffect()
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("Sludge Bomb", new StatusMove(
        PokemonType.T_POISON, 90, 1.0, 10, new StatusEffect[] {
            new PoisonEffect()
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Smog", new StatusMove(
        PokemonType.T_POISON, 20, 0.7, 20, new StatusEffect[] {
            new PoisonEffect()
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("Smokescreen", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 20, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_ACCURACY, false)
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Spark", new StatusMove(
        PokemonType.T_ELECTRIC, 65, 1.0, 20, new StatusEffect[] {
            new ParalysisEffect()
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("Spore", new StatusMove(
        PokemonType.T_GRASS, 0, 1.0, 15, new StatusEffect[] {
            new SleepEffect()
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Steel Wing", new StatusMove(
        PokemonType.T_STEEL, 70, 0.9, 25, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_DEFENCE, true)
        },
        new boolean[] { true },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Stomp", new StatusMove(
        PokemonType.T_NORMAL, 65, 1.0, 20, new StatusEffect[] {
            new FlinchEffect()
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("String Shot", new StatusMove(
        PokemonType.T_BUG, 0, 0.95, 40, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPEED, false)
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Stun Spore", new StatusMove(
        PokemonType.T_GRASS, 0, 0.75, 30, new StatusEffect[] {
            new ParalysisEffect()
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Superpower", new StatusMove(
        PokemonType.T_FIGHTING, 120, 1.0, 5, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_ATTACK, false),
            new StatChangeEffect(Pokemon.S_DEFENCE, false)
        },
        new boolean[] { true, true },
        new double[] { 1.0, 1.0 }
    )));

    m_moves.add(new MoveListEntry("Supersonic", new StatusMove(
        PokemonType.T_NORMAL, 0, 0.55, 30, new StatusEffect[] {
            new ConfuseEffect()
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Swagger", new StatusMove(
        PokemonType.T_NORMAL, 0, 0.9, 15, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_ATTACK, true, 2),
            new ConfuseEffect()
        },
        new boolean[] { false, false },
        new double[] { 1.0, 1.0 }
    )));

    m_moves.add(new MoveListEntry("Sweet Kiss", new StatusMove(
        PokemonType.T_NORMAL, 0, 0.75, 10, new StatusEffect[] {
            new ConfuseEffect()
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Sweet Scent", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 20, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_EVASION, false)
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Swords Dance", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 30, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_ATTACK, true, 2)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Tail Glow", new StatusMove(
        PokemonType.T_BUG, 0, 1.0, 20, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPATTACK, true, 2)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Tail Whip", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 30, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_DEFENCE, false)
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Teeter Dance", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 20, new StatusEffect[] {
            new ConfuseEffect()
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Thunder", new StatusMove(
        PokemonType.T_ELECTRIC, 120, 0.7, 10, new StatusEffect[] {
            new ParalysisEffect()
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("Thunder Wave", new StatusMove(
        PokemonType.T_ELECTRIC, 0, 1.0, 20, new StatusEffect[] {
            new ParalysisEffect()
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Thunderbolt", new StatusMove(
        PokemonType.T_ELECTRIC, 95, 1.0, 15, new StatusEffect[] {
            new ParalysisEffect()
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Thunderpunch", new StatusMove(
        PokemonType.T_ELECTRIC, 75, 1.0, 15, new StatusEffect[] {
            new ParalysisEffect()
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Thundershock", new StatusMove(
        PokemonType.T_ELECTRIC, 40, 1.0, 30, new StatusEffect[] {
            new ParalysisEffect()
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    // TODO: Hits through substitutes in Advance, but not in D/P.
    m_moves.add(new MoveListEntry("Tickle", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 20, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_ATTACK, false),
            new StatChangeEffect(Pokemon.S_DEFENCE, false)
        },
        new boolean[] { false, false },
        new double[] { 1.0, 1.0 }
    )));

    m_moves.add(new MoveListEntry("Tri Attack", new StatusMove(
        PokemonType.T_NORMAL, 80, 1.0, 10, new StatusEffect[] {
            new ParalysisEffect(),
            new BurnEffect(),
            new FreezeEffect()
        },
        new boolean[] { false, false, false },
        new double[] { 0.1, 0.1, 0.1 }
    )));

    m_moves.add(new MoveListEntry("Twister", new StatusMove(
        PokemonType.T_DRAGON, 40, 1.0, 20, new StatusEffect[] {
            new FlinchEffect()
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("Water Pulse", new StatusMove(
        PokemonType.T_WATER, 60, 1.0, 20, new StatusEffect[] {
            new ConfuseEffect()
        },
        new boolean[] { false },
        new double[] { 0.2 }
    )));

    m_moves.add(new MoveListEntry("Will-o-wisp", new StatusMove(
        PokemonType.T_FIRE, 0, 0.75, 15, new StatusEffect[] {
            new BurnEffect()
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Withdraw", new StatusMove(
        PokemonType.T_WATER, 0, 1.0, 40, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_DEFENCE, true)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Zap Cannon", new StatusMove(
        PokemonType.T_ELECTRIC, 100, 0.5, 5, new StatusEffect[] {
            new ParalysisEffect()
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Milk Drink", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 10, new StatusEffect[] {
            new PercentEffect(0.5, false, -1, null)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    //todo: this should have 20pp in advance and 10pp in d/p
    m_moves.add(new MoveListEntry("Recover", new StatusMove(
        PokemonType.T_PSYCHIC, 0, 1.0, 10, new StatusEffect[] {
            new PercentEffect(0.5, false, -1, null)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Softboiled", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 10, new StatusEffect[] {
            new PercentEffect(0.5, false, -1, null)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    {
      StatusMove healing = new StatusMove(
          PokemonType.T_NORMAL, 0, 1.0, 5, new StatusEffect[] {
              new PercentEffect(0.5, false, -1, null)
          },
          new boolean[] { true },
          new double[] { 1.0 }
      );
      m_moves.add(new MoveListEntry("Moonlight", (PokemonMove)healing.clone()));
      m_moves.add(new MoveListEntry("Morning Sun", (PokemonMove)healing.clone()));
      m_moves.add(new MoveListEntry("Synthesis", (PokemonMove)healing.clone()));
    }

    m_moves.add(new MoveListEntry("Solarbeam", new StatusMove(
        PokemonType.T_GRASS, 0, 1.0, 10, new StatusEffect[] {
            new ChargeEffect(1, "takes in sunlight!", new MoveListEntry(
                "Solarbeam",
                new PokemonMove(
                    PokemonType.T_GRASS, 120, 1.0, 10
                )
            )
            )
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )   {
      public boolean isAttack() {
        return true;
      }
      public boolean isDamaging() {
        return true;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Sunny Day", new WeatherMove(
        PokemonType.T_FIRE, 5,
        new Class[] { SunEffect.class },
        "Heat Rock"
    )));

    m_moves.add(new MoveListEntry("Rain Dance", new WeatherMove(
        PokemonType.T_WATER, 5,
        new Class[] { RainEffect.class },
        "Damp Rock"
    )));

    m_moves.add(new MoveListEntry("Hail", new WeatherMove(
        PokemonType.T_ICE, 5,
        new Class[] { HailEffect.class },
        "Icy Rock"
    )));

    m_moves.add(new MoveListEntry("Sandstorm", new WeatherMove(
        PokemonType.T_ROCK, 5,
        new Class[] { SandstormEffect.class },
        "Smooth Rock"
    )));

    m_moves.add(new MoveListEntry("Seismic Toss",
        new PokemonMove(PokemonType.T_FIGHTING, 0, 1.0, 20) {
      public boolean isAttack() {
        return true;
      }
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (getEffectiveness(user, target) == 0.0) {
          user.getField().showMessage("It doesn't affect "
              + target.getName() + "...");
          return 0;
        }
        int damage = user.getLevel();
        target.changeHealth(-damage);
        return damage;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Night Shade",
        new PokemonMove(PokemonType.T_GHOST, 0, 1.0, 15) {
      public boolean isAttack() {
        return true;
      }
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (getEffectiveness(user, target) == 0.0) {
          user.getField().showMessage("It doesn't affect "
              + target.getName() + "...");
          return 0;
        }
        int damage = user.getLevel();
        target.changeHealth(-damage);
        return damage;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Sonicboom",
        new PokemonMove(PokemonType.T_NORMAL, 0, 0.9, 20) {
      public boolean isAttack() {
        return true;
      }
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (getEffectiveness(user, target) == 0.0) {
          user.getField().showMessage("It doesn't affect "
              + target.getName() + "...");
          return 0;
        }
        final int damage = 20;
        target.changeHealth(-damage);
        return damage;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Dragon Rage",
        new PokemonMove(PokemonType.T_DRAGON, 0, 1.0, 10) {
      public boolean isAttack() {
        return true;
      }
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        final int damage = 40;
        target.changeHealth(-damage);
        return damage;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Double-edge",
        new RecoilMove(PokemonType.T_NORMAL, 120, 1.0, 20, 0.125)));

    m_moves.add(new MoveListEntry("Submission",
        new RecoilMove(PokemonType.T_FIGHTING, 80, 0.8, 25, 0.25)));

    m_moves.add(new MoveListEntry("Take Down",
        new RecoilMove(PokemonType.T_NORMAL, 90, 0.85, 20, 0.25)));

    m_moves.add(new MoveListEntry("Volt Tackle",
        new RecoilMove(PokemonType.T_ELECTRIC, 120, 1.0, 15, 1.0/3.0)));

    m_moves.add(new MoveListEntry("Struggle",
        new RecoilMove(PokemonType.T_TYPELESS, 50, 1.0, 1, 0.50) {
      public boolean isProtected(Pokemon p) {
        return false;
      }
      public int getRecoil(Pokemon p, int damage) {
        if (p.getField().getMechanics() instanceof JewelMechanics) {
          return p.getStat(Pokemon.S_HP) / 4;
        }
        return super.getRecoil(p, damage);
      }
    }));

    m_moves.add(new MoveListEntry("Arm Thrust",
        new MultipleHitMove(PokemonType.T_FIGHTING, 15, 1.0, 20)));

    m_moves.add(new MoveListEntry("Barrage",
        new MultipleHitMove(PokemonType.T_NORMAL, 15, 0.85, 20)));

    m_moves.add(new MoveListEntry("Bone Rush",
        new MultipleHitMove(PokemonType.T_GROUND, 25, 0.8, 10)));

    m_moves.add(new MoveListEntry("Bullet Seed",
        new MultipleHitMove(PokemonType.T_GRASS, 10, 1.0, 30)));

    m_moves.add(new MoveListEntry("Comet Punch",
        new MultipleHitMove(PokemonType.T_NORMAL, 18, 0.85, 15)));

    m_moves.add(new MoveListEntry("Doubleslap",
        new MultipleHitMove(PokemonType.T_NORMAL, 15, 0.85, 10)));

    m_moves.add(new MoveListEntry("Fury Attack",
        new MultipleHitMove(PokemonType.T_NORMAL, 15, 0.85, 20)));

    m_moves.add(new MoveListEntry("Fury Swipes",
        new MultipleHitMove(PokemonType.T_NORMAL, 18, 0.8, 15)));

    m_moves.add(new MoveListEntry("Icicle Spear",
        new MultipleHitMove(PokemonType.T_ICE, 10, 1.0, 30)));

    m_moves.add(new MoveListEntry("Pin Missile",
        new MultipleHitMove(PokemonType.T_BUG, 14, 0.85, 20)));

    m_moves.add(new MoveListEntry("Rock Blast",
        new MultipleHitMove(PokemonType.T_ROCK, 25, 0.8, 10)));

    m_moves.add(new MoveListEntry("Spike Cannon",
        new MultipleHitMove(PokemonType.T_NORMAL, 20, 1.0, 15)));

    m_moves.add(new MoveListEntry("Slack Off", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 10, new StatusEffect[] {
            new PercentEffect(0.5, false, -1, null)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Absorb",
        new AbsorbMove(PokemonType.T_GRASS, 20, 1.0, 20, 0.5)));

    /** TBD (TODO): Has only 5 PP in Advance, but 10 in D/P. */
    m_moves.add(new MoveListEntry("Giga Drain",
        new AbsorbMove(PokemonType.T_GRASS, 60, 1.0, 10, 0.5)));

    m_moves.add(new MoveListEntry("Leech Life",
        new AbsorbMove(PokemonType.T_BUG, 20, 1.0, 15, 0.5)));

    m_moves.add(new MoveListEntry("Mega Drain",
        new AbsorbMove(PokemonType.T_GRASS, 40, 1.0, 10, 0.5)));

    m_moves.add(new MoveListEntry("Ancientpower", new StatusMove(
        PokemonType.T_ROCK, 60, 1.0, 5, new StatusEffect[] {
            new MultipleStatChangeEffect(new int[] {
                Pokemon.S_ATTACK,
                Pokemon.S_DEFENCE,
                Pokemon.S_SPEED,
                Pokemon.S_SPATTACK,
                Pokemon.S_SPDEFENCE
            }
            )
        },
        new boolean[] { true },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Silver Wind", new StatusMove(
        PokemonType.T_BUG, 60, 1.0, 5, new StatusEffect[] {
            new MultipleStatChangeEffect(new int[] {
                Pokemon.S_ATTACK,
                Pokemon.S_DEFENCE,
                Pokemon.S_SPEED,
                Pokemon.S_SPATTACK,
                Pokemon.S_SPDEFENCE
            }
            )
        },
        new boolean[] { true },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Belly Drum", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 10, new StatusEffect[] {
            // new PercentEffect(-0.5, false, -1, null),
            new StatChangeEffect(Pokemon.S_ATTACK, true, 12)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    ) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        int damage = (user.getStat(Pokemon.S_HP) / 2);
        if (user.getHealth() <= damage) {
          user.getField().showMessage("But it failed!");
          return 0;
        }
        IntrinsicAbility ability = user.getAbility();
        if (user.hasAbility("Magic Guard")) {
          ability.deactivate();
        } else {
          ability = null;
        }
        user.changeHealth(-damage, true);
        if (ability != null) {
          ability.activate();
        }
        return super.use(mech, user, target);
      }
    }
    ));

    m_moves.add(new MoveListEntry("Reversal",
        new HealthBasedMove(PokemonType.T_FIGHTING, 1.0, 15, false)));

    m_moves.add(new MoveListEntry("Flail",
        new HealthBasedMove(PokemonType.T_NORMAL, 1.0, 15, false)));

    m_moves.add(new MoveListEntry("Eruption",
        new HealthBasedMove(PokemonType.T_FIRE, 1.0, 5, true)));

    m_moves.add(new MoveListEntry("Water Spout",
        new HealthBasedMove(PokemonType.T_WATER, 1.0, 5, true)));

    m_moves.add(new MoveListEntry("Aeroblast",
        new HighCriticalHitMove(PokemonType.T_FLYING, 120, 0.95, 5)));

    m_moves.add(new MoveListEntry("Air Cutter",
        new HighCriticalHitMove(PokemonType.T_FLYING, 55, 0.95, 25)));

    m_moves.add(new MoveListEntry("Crabhammer",
        new HighCriticalHitMove(PokemonType.T_WATER, 90, 0.85, 10)));

    m_moves.add(new MoveListEntry("Cross Chop",
        new HighCriticalHitMove(PokemonType.T_FIGHTING, 100, 0.8, 5)));

    m_moves.add(new MoveListEntry("Karate Chop",
        new HighCriticalHitMove(PokemonType.T_FIGHTING, 501.0, 25)));

    m_moves.add(new MoveListEntry("Leaf Blade",
        new HighCriticalHitMove(PokemonType.T_GRASS, 70, 1.0, 15) {
      public int use(BattleMechanics mech, Pokemon source, Pokemon target) {
        setPower((mech instanceof JewelMechanics) ? 90 : 70);
        return super.use(mech, source, target);
      }
    }));

    m_moves.add(new MoveListEntry("Poison Tail",
        new StatusMove(PokemonType.T_POISON, 50, 1.0, 25, new StatusEffect[] {
            new PoisonEffect()
        },
        new boolean[] { false },
        new double[] { 0.1 }
        ) {
      public boolean hasHighCriticalHitRate() { return true; }
    }
    ));

    m_moves.add(new MoveListEntry("Razor Leaf",
        new HighCriticalHitMove(PokemonType.T_GRASS, 55, 0.95, 25)));

    m_moves.add(new MoveListEntry("Slash",
        new HighCriticalHitMove(PokemonType.T_NORMAL, 70, 1.0, 20)));        

    m_moves.add(new MoveListEntry("Aerial Ace",
        new PerfectAccuracyMove(PokemonType.T_FLYING, 60, 20)));

    m_moves.add(new MoveListEntry("Faint Attack",
        new PerfectAccuracyMove(PokemonType.T_DARK, 60, 20)));

    m_moves.add(new MoveListEntry("Magical Leaf",
        new PerfectAccuracyMove(PokemonType.T_GRASS, 60, 20)));

    m_moves.add(new MoveListEntry("Shadow Punch",
        new PerfectAccuracyMove(PokemonType.T_GHOST, 60, 20)));

    m_moves.add(new MoveListEntry("Swift",
        new PerfectAccuracyMove(PokemonType.T_NORMAL, 60, 20)));

    m_moves.add(new MoveListEntry("Shock Wave",
        new PerfectAccuracyMove(PokemonType.T_ELECTRIC, 60, 20)));

    m_moves.add(new MoveListEntry("Bonemerang",
        new PokemonMove(PokemonType.T_GROUND, 50, 0.9, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        int damage = 0;
        for (int i = 0; i < 2; ++i) {
          final int partial = mech.calculateDamage(this, user, target);
          target.changeHealth(-partial);
          damage += partial;
        }
        user.getField().showMessage("Hit 2 time(s)!");
        return damage;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Double Kick",
        new PokemonMove(PokemonType.T_FIGHTING, 30, 1.0, 30) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        int damage = 0;
        for (int i = 0; i < 2; ++i) {
          final int partial = mech.calculateDamage(this, user, target);
          target.changeHealth(-partial);
          damage += partial;
        }
        user.getField().showMessage("Hit 2 time(s)!");
        return damage;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Twineedle",
        new StatusMove(PokemonType.T_BUG, 25, 1.0, 15, new StatusEffect[] {
            new PoisonEffect()
        },
        new boolean[] { false },
        new double[] { 0.1 }
        )
    {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        int damage = 0;
        for (int i = 0; i < 2; ++i) {
          damage += super.use(mech, user, target);
        }
        return damage;
      }
    }
    ));

    PokemonMove healBell = new PokemonMove(PokemonType.T_NORMAL, 0, 1.0, 5) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        boolean sound = getMoveListEntry().getName().equals("Heal Bell");
        Pokemon[] teammates = user.getTeammates();
        for (int i = 0; i < teammates.length; ++i) {
          Pokemon p = teammates[i];
          if (p == null || p.isFainted() || (sound && p.hasAbility("Soundproof")))
            continue;
          p.removeStatus(StatusEffect.SPECIAL_EFFECT_LOCK);
        }
        return 0;
      }
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        return true;
      }
    };

    PokemonMove aromatherapy = (PokemonMove)healBell.clone();
    aromatherapy.setType(PokemonType.T_GRASS);

    m_moves.add(new MoveListEntry("Aromatherapy", aromatherapy));

    m_moves.add(new MoveListEntry("Heal Bell", healBell));

    m_moves.add(new MoveListEntry("Assist",
        new PokemonMove(PokemonType.T_NORMAL, 0, 1.0, 15) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        Pokemon[] teammates = user.getTeammates();
        if (teammates.length < 2) {
          user.getField().showMessage("But it failed!");
          return 0;
        }
        Random random = mech.getRandom();
        Pokemon teammate;
        do {
          teammate = teammates[random.nextInt(teammates.length)];
        } while (teammate == user);
        MoveListEntry move;
        do {
          move = teammate.getMove(random.nextInt(4));
        } while (move == null);
        if (move.getName().equals("Focus Punch")) {
          user.getField().showMessage("But it failed!");
          return 0;
        }
        return user.useMove(move, target);
      }
    }
    ));

    m_moves.add(new MoveListEntry("Facade",
        new PokemonMove(PokemonType.T_NORMAL, 70, 1.0, 20) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        int power = getPower();
        if (user.hasEffect(StatusEffect.SPECIAL_EFFECT_LOCK)) {
          setPower(2 * power);
        }
        int damage = mech.calculateDamage(this, user, target);
        setPower(power);
        target.changeHealth(-damage);
        return damage;
      }
    }
    ));

    m_moves.add(new MoveListEntry("False Swipe",
        new PokemonMove(PokemonType.T_NORMAL, 40, 1.0, 40) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        int targetHealth = target.getHealth();
        int damage = mech.calculateDamage(this, user, target);
        if (damage >= targetHealth) {
          damage = targetHealth - 1;
        }
        target.changeHealth(-damage);
        return damage;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Psywave",
        new PokemonMove(PokemonType.T_PSYCHIC, 0, 0.8, 15) {
      public boolean isAttack() {
        return true;
      }
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        Random random = mech.getRandom();
        int damage = user.getLevel() * (random.nextInt(11) * 10 + 50) / 100;
        target.changeHealth(-damage);
        return damage;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Jump Kick",
        new JumpKickMove(PokemonType.T_FIGHTING, 70, 85, 0.95, 20)
    ));

    m_moves.add(new MoveListEntry("Hi Jump Kick",
        new JumpKickMove(PokemonType.T_FIGHTING, 85, 100, 0.90, 20)
    ));

    m_moves.add(new MoveListEntry("Refresh",
        new PokemonMove(PokemonType.T_NORMAL, 0, 1.0, 20) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        user.removeStatus(StatusEffect.SPECIAL_EFFECT_LOCK);
        return 0;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Smellingsalt",
        new PokemonMove(PokemonType.T_NORMAL, 60, 1.0, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        int power = getPower();
        if (!target.hasSubstitute() && target.hasEffect(ParalysisEffect.class)) {
          setPower(2 * power);
          target.removeStatus(StatusEffect.SPECIAL_EFFECT_LOCK);
          user.getField().showMessage(target.getName() + " was cured of paralysis!");
        }
        int damage = mech.calculateDamage(this, user, target);
        setPower(power);
        target.changeHealth(-damage);
        return damage;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Splash",
        new PokemonMove(PokemonType.T_NORMAL, 0, 1.0, 40) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        user.getField().showMessage("But nothing happened!");
        return 0;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Teleport",
        new PokemonMove(PokemonType.T_PSYCHIC, 0, 1.0, 20) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        user.getField().showMessage("But it failed!");
        return 0;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Triple Kick",
        new PokemonMove(PokemonType.T_FIGHTING, 60, 0.9, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        int damage = 0;
        PokemonMove move = new PokemonMove(PokemonType.T_FIGHTING, 0, 1.0, 1);
        for (int i = 1; i < 4; ++i) {
          move.setPower(i * 10);
          damage += user.useMove(move, target);
        }
        user.getField().showMessage("Hit 3 time(s)!");
        return damage;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Razor Wind", new StatusMove(
        PokemonType.T_FLYING, 0, 1.0, 10, new StatusEffect[] {
            new ChargeEffect(1, "created a whirlwind!", new MoveListEntry(
                "Razor Wind",
                new HighCriticalHitMove(
                    PokemonType.T_FLYING, 80, 1.0, 10
                )
            )
            )
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )   {
      public boolean isAttack() {
        return true;
      }
      public boolean isDamaging() {
        return true;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Skull Bash", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 15, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_DEFENCE, true, 1),
            new ChargeEffect(1, "lowered its head!", new MoveListEntry(
                "Skull Bash",
                new PokemonMove(
                    PokemonType.T_NORMAL, 100, 1.0, 15
                )
            )
            )
        },
        new boolean[] { true, true },
        new double[] { 1.0, 1.0 }
    )   {
      public boolean isAttack() {
        return true;
      }
      public boolean isDamaging() {
        return true;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Sky Attack", new StatusMove(
        PokemonType.T_FLYING, 0, 1.0, 5, new StatusEffect[] {
            new ChargeEffect(1, "is glowing!", new MoveListEntry(
                "Sky Attack",
                new PokemonMove(
                    PokemonType.T_FLYING, 140, 0.9, 5
                )
            )
            )
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )   {
      public boolean isAttack() {
        return true;
      }
      public boolean isDamaging() {
        return true;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Pay Day",
        new PokemonMove(PokemonType.T_NORMAL, 40, 1.0, 20) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        user.getField().showMessage("Coins scattered everywhere!");
        return super.use(mech, user, target);
      }
    }
    ));

    m_moves.add(new MoveListEntry("Present",
        new PokemonMove(PokemonType.T_NORMAL, 0, 0.9, 15) {
      public boolean isAttack() {
        return true;
      }
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        double random = mech.getRandom().nextDouble();
        int power;
        if (random <= 0.4) {
          power = 40;
        } else if (random <= 0.7) {
          power = 80;
        } else if (random <= 0.8) {
          power = 120;
        } else {
          double maximum = (double)target.getStat(Pokemon.S_HP);
          int restore = (int)(maximum * 0.2);
          target.changeHealth(restore);
          return restore;
        }
        setPower(power);
        int damage = mech.calculateDamage(this, user, target);
        setPower(0);
        target.changeHealth(-damage);
        return damage;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Dream Eater",
        new AbsorbMove(PokemonType.T_PSYCHIC, 100, 1.0, 15, 0.5) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (target.hasEffect(SleepEffect.class) && !target.hasSubstitute()) {
          return super.use(mech, user, target);
        }
        user.getField().showMessage("But it failed!");
        return 0;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Haze",
        new PokemonMove(PokemonType.T_ICE, 0, 1.0, 30) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        user.removeStatus(StatChangeEffect.class);
        target.removeStatus(StatChangeEffect.class);
        return 0;
      }
    }
    ));

    PokemonMove foresight = new PokemonMove(PokemonType.T_NORMAL, 0, 1.0, 40) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        List<StatusEffect> statuses = target.getNormalStatuses(0);
        Iterator<StatusEffect> i = statuses.iterator();
        while (i.hasNext()) {
          StatusEffect effect = (StatusEffect)i.next();
          if (!(effect instanceof StatChangeEffect)) continue;
          StatChangeEffect eff = (StatChangeEffect)effect;
          if (eff.getStat() == Pokemon.S_EVASION) {
            target.removeStatus(eff);
          }
        }
        statuses = user.getNormalStatuses(0);
        i = statuses.iterator();
        while (i.hasNext()) {
          StatusEffect effect = (StatusEffect)i.next();
          if (!(effect instanceof StatChangeEffect)) continue;
          StatChangeEffect eff = (StatChangeEffect)effect;
          if (eff.getStat() == Pokemon.S_ACCURACY) {
            user.removeStatus(eff);
          }
        }
        user.getAccuracy().setSecondaryMultiplier(1);
        target.getEvasion().setSecondaryMultiplier(1);
        return 0;
      }
    };

    m_moves.add(new MoveListEntry("Foresight", (PokemonMove)foresight.clone()));
    m_moves.add(new MoveListEntry("Odor Sleuth", (PokemonMove)foresight.clone()));

    m_moves.add(new MoveListEntry("Fissure",
        new OneHitKillMove(PokemonType.T_GROUND, 5)));

    m_moves.add(new MoveListEntry("Horn Drill",
        new OneHitKillMove(PokemonType.T_NORMAL, 5)));

    m_moves.add(new MoveListEntry("Guillotine",
        new OneHitKillMove(PokemonType.T_NORMAL, 5)));

    m_moves.add(new MoveListEntry("Sheer Cold",
        new OneHitKillMove(PokemonType.T_ICE, 5)));

    m_moves.add(new MoveListEntry("Memento",
        new StatusMove(PokemonType.T_DARK, 0, 1.0, 10, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_ATTACK, false, 2),
            new StatChangeEffect(Pokemon.S_SPATTACK, false, 2)
        },
        new boolean[] { false, false },
        new double[] { 1.0, 1.0 }
        ) {
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        return true;
      }
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (mech.attemptHit(this, user, target)) {
          super.use(mech, user, target);
        }
        user.faint();
        return 0;
      }            
    }
    ));

    PokemonMove explosion = new PokemonMove(PokemonType.T_NORMAL, 250, 1.0, 5) {
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        return true;
      }
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (target.hasAbility("Damp")) {
          user.getField().showMessage(target.getName() + "'s Damp prevents explosions!");
          return 0;
        }
        int damage = 0;
        if (target.hasEffect(ProtectEffect.class)) {
          List<StatusEffect> list = target.getNormalStatuses(0);
          Iterator<StatusEffect> i = list.iterator();
          while (i.hasNext()) {
            Object o = i.next();
            if (o instanceof ProtectEffect) {
              ProtectEffect eff = (ProtectEffect)o;
              user.getField().showMessage(target.getName() + eff.getDescription());
              break;
            }
          }
        } else if (mech.attemptHit(this, user, target)) {
          StatMultiplier mul = target.getMultiplier(Pokemon.S_DEFENCE);
          double value = mul.getSecondaryMultiplier();
          mul.setSecondaryMultiplier(value / 2.0);
          damage = mech.calculateDamage(this, user, target);
          mul.setSecondaryMultiplier(value);
          target.changeHealth(-damage);
        }
        user.faint();
        return damage;
      }
    };

    PokemonMove selfDestruct = (PokemonMove)explosion.clone();
    selfDestruct.setPower(200);
    m_moves.add(new MoveListEntry("Selfdestruct", selfDestruct));
    m_moves.add(new MoveListEntry("Explosion", explosion));

    m_moves.add(new MoveListEntry("Nightmare",
        new StatusMove(PokemonType.T_GHOST, 0, 1.0, 15, new StatusEffect[] {
            new StatusEffect() {
              public String getName() {
                return "Nightmare";
              }
              public boolean apply(Pokemon p) {
                if (!p.hasEffect(SleepEffect.class)) {
                  p.getField().showMessage("But it failed!");
                  return false;
                }
                return true;
              }
              public String getDescription() {
                return " fell into a nightmare!";
              }
              public int getTier() {
                return 4;
              }
              public boolean tick(Pokemon p) {
                if (!p.hasEffect(SleepEffect.class)) {
                  p.removeStatus(this);
                  return true;
                }
                double maximum = (double)p.getStat(Pokemon.S_HP);
                int loss = (int)(maximum / 4.0);
                if (loss < 1) loss = 1;
                p.getField().showMessage(p.getName() + " is having a nightmare!");
                p.changeHealth(-loss);
                return false;
              }
            }
        },
        new boolean[] { false },
        new double[] { 1.0 }
        )));

    m_moves.add(new MoveListEntry("Snore",
        new PokemonMove(PokemonType.T_NORMAL, 40, 1.0, 15) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (!user.hasEffect(SleepEffect.class)) {
          user.getField().showMessage("But it failed!");
          return 0;
        }
        int damage = mech.calculateDamage(this, user, target);
        target.changeHealth(-damage);
        return damage;
      }
      public Class<?> getStatusException() {
        return SleepEffect.class;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Sleep Talk",
        new PokemonMove(PokemonType.T_NORMAL, 0, 1.0, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (!user.hasEffect(SleepEffect.class)) {
          user.getField().showMessage("But it failed!");
          return 0;
        }
        Random random = mech.getRandom();
        MoveListEntry move;

        boolean hasOtherMoves = false;
        for (int i = 0; i < 4; ++i) {
          if ((move = user.getMove(i)) != null) {
            if (!move.getName().equals("Sleep Talk")) {
              hasOtherMoves = true;
              break;
            }
          }
        }

        if (!hasOtherMoves) {
          user.getField().showMessage("But it failed!");
          return 0;
        }

        String name = null;
        do {
          move = user.getMove(random.nextInt(4));
          if (move != null) {
            if ((name = move.getName()) == null)
              continue;
          } else {
            continue;
          }
        } while (name.equals("Sleep Talk"));

        if (name.equals("Focus Punch")) {
          user.getField().showMessage("But it failed!");
          return 0;
        }

        return user.useMove(move, target);
      }

      public Class<?> getStatusException() {
        return SleepEffect.class;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Hidden Power", new HiddenPowerMove()));

    m_moves.add(new MoveListEntry("Endeavor",
        new PokemonMove(PokemonType.T_NORMAL, 0, 1.0, 5) {
      public boolean isAttack() {
        return true;
      }
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (getEffectiveness(user, target) == 0.0) {
          user.getField().showMessage("It doesn't affect "
              + target.getName() + "...");
          return 0;
        }
        if (user.getHealth() >= target.getHealth()) {
          user.getField().showMessage("But it failed!");
          return 0;
        }
        int damage = target.getHealth() - user.getHealth();
        target.changeHealth(-damage);
        return damage;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Pain Split",
        new PokemonMove(PokemonType.T_NORMAL, 0, 1.0, 20) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (target.hasSubstitute()) {
          user.getField().showMessage("But it failed!");
          return 0;
        }
        final int userHp = user.getHealth();
        final int targetHp = target.getHealth();
        final int newHealth = (userHp + targetHp) / 2;
        user.changeHealth(newHealth - userHp, true);
        target.changeHealth(newHealth - targetHp, true);
        return 0;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Super Fang",
        new PokemonMove(PokemonType.T_NORMAL, 0, 0.9, 10) {
      public boolean isAttack() {
        return true;
      }
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        int damage = target.getHealth() / 2;
        if (damage == 0) damage = 1;
        target.changeHealth(-damage);
        return damage;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Extremespeed",
        new PriorityMove(PokemonType.T_NORMAL, 80, 1.0, 5, 1)));

    m_moves.add(new MoveListEntry("Mach Punch",
        new PriorityMove(PokemonType.T_FIGHTING, 40, 1.0, 30, 1)));

    m_moves.add(new MoveListEntry("Quick Attack",
        new PriorityMove(PokemonType.T_NORMAL, 40, 1.0, 30, 1)));

    m_moves.add(new MoveListEntry("Vital Throw",
        new PerfectAccuracyMove(PokemonType.T_FIGHTING, 70, 10) {
      public int getPriority() {
        return -1;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Leech Seed", new StatusMove(
        PokemonType.T_GRASS, 0, 0.9, 10, new StatusEffect[] {
            new LeechSeedEffect()
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Metronome", new PokemonMove(
        PokemonType.T_NORMAL, 0, 1.0, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        ArrayList<MoveListEntry> moves = getMoveList();
        int size = moves.size();
        Random random = mech.getRandom();
        MoveListEntry entry;
        do {
          entry = (MoveListEntry)moves.get(random.nextInt(size));
          String name = entry.getName();
          if (name.equals("Focus Punch") || (name.equals("Metronome"))) continue;
        } while (false);
        return user.useMove(entry, target);
      }
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        return true;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Perish Song", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 5, new StatusEffect[] {
            new PerishSongEffect(true),
            new PerishSongEffect(true)
        },
        new boolean[] { true, false },
        new double[] { 1.0, 1.0 }
    ) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        user.getField().showMessage("Both Pokemon will faint in 3 turn(s)");
        return super.use(mech, user, target);
      }
    }
    ));

    m_moves.add(new MoveListEntry("Beat Up",
        new PokemonMove(PokemonType.T_NORMAL, 10, 1.0, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        Pokemon[] team = user.getTeammates();
        final int baseDefence = target.getBase(Pokemon.S_DEFENCE);
        final int power = getPower();
        int totalDamage = 0;
        for (int i = 0; i < team.length; ++i) {
          Pokemon pokemon = team[i];
          if (pokemon.isFainted() || pokemon.hasEffect(StatusEffect.SPECIAL_EFFECT_LOCK)) {
            continue;
          }
          final int baseAttack = pokemon.getBase(Pokemon.S_ATTACK);
          boolean stab = pokemon.isType(getType());             
          int damage = (int)(((int)((int)(((int)((2 * pokemon.getLevel()) / 5.0 + 2.0)
              * baseAttack
              * power)
              / baseDefence)
              / 50.0)
              + 2)
              * (stab ? 1.5 : 1.0));
          user.getField().showMessage(pokemon.getName() + "'s attack!");
          target.changeHealth(-damage);
          totalDamage += damage;
        }
        return totalDamage;   
      }
    }
    ));

    m_moves.add(new MoveListEntry("Curse",
        new PokemonMove(PokemonType.T_TYPELESS, 0, 1.0, 10) {
      public boolean attemptHit(BattleMechanics mech, Pokemon source, Pokemon target) {
        return true;
      }
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (user.isType(PokemonType.T_GHOST)) {
          user.addStatus(user, new PercentEffect(-0.5, false, -1, null));
          target.addStatus(user, new PercentEffect(-0.25, false, 3, " is afflicted by the curse!"));
          user.getField().showMessage(user.getName() + " cut its health"
              + " and laid a curse on " + target.getName() + "!");
        } else {
          user.addStatus(user, new StatChangeEffect(Pokemon.S_SPEED, false, 1));
          user.addStatus(user, new StatChangeEffect(Pokemon.S_ATTACK, true, 1));
          user.addStatus(user, new StatChangeEffect(Pokemon.S_DEFENCE, true, 1));
        }
        return 0;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Conversion", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 30, new StatusEffect[] {
            new StatusEffect() {
              private PokemonType[] m_types;

              public String getName() {
                return "Conversion";
              }
              public String getDescription() {
                return null;
              }
              public int getTier() {
                return -1;
              }
              public boolean tick(Pokemon p) {
                return false;
              }
              public boolean apply(Pokemon p) {
                m_types = p.getTypes();
                Random random = p.getField().getRandom();
                PokemonType moveType = null;
                MoveListEntry move;
                boolean fail = true;
                for (int i = 0; i < 4; ++i) {
                  move = p.getMove(i);
                  if ((move != null)
                      && !move.getMove().getType().equals(PokemonType.T_TYPELESS)
                      && !move.getName().equals("Conversion")) {
                    fail = false;
                    break;
                  }
                }
                if (fail) {
                  return false;
                }
                do {
                  move = p.getMove(random.nextInt(4));
                  if ((move == null) || move.getName().equals("Conversion")) {
                    continue;
                  }
                  moveType = move.getMove().getType();
                } while ((moveType == null) || moveType.equals(PokemonType.T_TYPELESS));
                p.setType(new PokemonType[] { moveType });
                p.getField().showMessage(p.getName() + " became the "
                    + moveType + " type!");
                return true;
              }
              public void unapply(Pokemon p) {
                p.setType(m_types);
              }
              public boolean switchOut(Pokemon p) {
                p.setType(m_types);
                return true;
              }
            }
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Mud Sport", new TypeCutMove(
        PokemonType.T_GROUND, 15, PokemonType.T_ELECTRIC, "Mud Sport"
    )));

    m_moves.add(new MoveListEntry("Water Sport", new TypeCutMove(
        PokemonType.T_GROUND, 15, PokemonType.T_FIRE, "Water Sport"
    )));

    PokemonMove thief = new PokemonMove(PokemonType.T_DARK, 40, 1.0, 40) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (target.hasSubstitute()) {
          return super.use(mech, user, target);
        }
        HoldItem enemyItem = target.getItem();                 
        if ((enemyItem != null) && (user.getItem() == null) && !(target.hasAbility("Sticky Hold"))) {
          user.setItem(enemyItem);
          target.setItem(null);
          user.getField().showMessage(user.getName() + " stole " + target.getName()
              + "'s " + enemyItem.getName() + "!");
        } else if (target.hasAbility("Sticky Hold")) {
          user.getField().showMessage(target.getName() + " held on with its Sticky Hold!");
        }
        return super.use(mech, user, target);
      }
    };

    PokemonMove covet = (PokemonMove)thief.clone();
    covet.setType(PokemonType.T_NORMAL);

    m_moves.add(new MoveListEntry("Thief", thief));
    m_moves.add(new MoveListEntry("Covet", covet));

    m_moves.add(new MoveListEntry("Knock Off",
        new PokemonMove(PokemonType.T_DARK, 20, 1.0, 20) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (target.hasSubstitute()) {
          return super.use(mech, user, target);
        }
        HoldItem item = target.getItem();
        if ((item != null) && !(target.hasAbility("Sticky Hold"))) {
          target.setItem(null);
          user.getField().showMessage(user.getName() + " knocked off foe "
              + target.getName() + "'s " + item.getName() + "!");
        } else if (target.hasAbility("Sticky Hold")) {
          user.getField().showMessage(target.getName() + " hung on with its Sticky Hold!");
        }

        return super.use(mech, user, target);
      }
    }
    ));

    m_moves.add(new MoveListEntry("Trick",
        new PokemonMove(PokemonType.T_PSYCHIC, 0, 1.0, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (target.hasSubstitute()) {
          user.getField().showMessage("But it failed!");
          return 0;
        }

        if ((target.hasAbility("Sticky Hold")) || (user.hasAbility("Sticky Hold"))) {
          if (user.hasAbility("Sticky Hold")) {
            user.getField().showMessage(user.getName() + " hung on with its Sticky Hold!");
          }
          if (target.hasAbility("Sticky Hold")) {
            user.getField().showMessage(target.getName() + " hung on with its Sticky Hold!");
          }
          return 0;
        }

        HoldItem targetItem = target.getItem();
        HoldItem item = user.getItem();
        /**if (item == null) {
                        user.getField().showMessage("But it failed!");
                        return 0;
                    }**/

        HoldItem userItem = (item == null) ? null : (HoldItem)item.clone();
        user.setItem(targetItem);
        target.setItem(userItem);
        if (targetItem != null) {
          user.getField().showMessage(user.getName() + " obtained " + targetItem.getName() + "!");
        }
        if (userItem != null) {
          user.getField().showMessage(target.getName() + " obtained " + userItem.getName() + "!");
        }
        return 0;
      }
      public boolean isAttack() {
        return true;
      }
      public boolean isDamaging() {
        return false;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Dig", new StatusMove(
        PokemonType.T_GROUND, 0, 1.0, 10, new StatusEffect[] {
            new InvulnerableStateEffect(new String[] { "Earthquake", "Fissure", "Magnitude" } ),
            new ChargeEffect(1, "dug a hole!", new MoveListEntry(
                "Dig",
                new PokemonMove(
                    PokemonType.T_GROUND, 60, 1.0, 10
                ) {
                  public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
                    setPower(mech instanceof JewelMechanics ? 80 : 60);
                    return super.use(mech, user, target);
                  }
                }
            )
            )
        },
        new boolean[] { true, true },
        new double[] { 1.0, 1.0 }
    )   {
      public boolean isAttack() {
        return true;
      }
      public boolean isDamaging() {
        return true;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Fly", new StatusMove(
        PokemonType.T_FLYING, 0, 0.95, 15, new StatusEffect[] {
            new InvulnerableStateEffect(new String[] { "Thunder", "Twister", "Gust", "Sky Uppercut" } ),
            new ChargeEffect(1, "flew up high!", new MoveListEntry(
                "Fly",
                new PokemonMove(
                    PokemonType.T_FLYING, 70, 0.95, 15
                )
            )
            )
        },
        new boolean[] { true, true },
        new double[] { 1.0, 1.0 }
    )   {
      public boolean isAttack() {
        return true;
      }
      public boolean isDamaging() {
        return true;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Dive", new StatusMove(
        PokemonType.T_WATER, 0, 1.0, 10, new StatusEffect[] {
            new InvulnerableStateEffect(new String[] { "Surf" } ),
            new ChargeEffect(1, "dove underwater!", new MoveListEntry(
                "Dive",
                new PokemonMove(
                    PokemonType.T_WATER, 60, 1.0, 10
                )
            )
            )
        },
        new boolean[] { true, true },
        new double[] { 1.0, 1.0 }
    )   {
      public boolean isAttack() {
        return true;
      }
      public boolean isDamaging() {
        return true;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Bounce", new StatusMove(
        PokemonType.T_FLYING, 0, 0.85, 5, new StatusEffect[] {
            new InvulnerableStateEffect(new String[0]),
            new ChargeEffect(1, "bounced up!", new MoveListEntry(
                "Bounce",
                new StatusMove(
                    PokemonType.T_FLYING, 85, 0.85, 5, new StatusEffect[] {
                        new ParalysisEffect()
                    },
                    new boolean[] { false },
                    new double[] { 0.3 }
                )
            )
            )
        },
        new boolean[] { true, true },
        new double[] { 1.0, 1.0 }
    )   {
      public boolean isAttack() {
        return true;
      }
      public boolean isDamaging() {
        return true;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Ingrain", new StatusMove(
        PokemonType.T_GRASS, 0, 1.0, 20, new StatusEffect[] {
            new IngrainEffect()
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Magnitude",
        new PokemonMove(PokemonType.T_GROUND, 0, 1.0, 30) {
      public boolean isAttack() {
        return true;
      }
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        double chance = mech.getRandom().nextDouble();
        int power;
        int magnitude;
        if (chance <= 0.05) {
          power = 10;
          magnitude = 4;
        } else if (chance <= 0.15) {
          power = 30;
          magnitude = 5;
        } else if (chance <= 0.35) {
          power = 50;
          magnitude = 6;
        } else if (chance <= 0.65) {
          power = 70;
          magnitude = 7;
        } else if (chance <= 0.85) {
          power = 90;
          magnitude = 8;
        } else if (chance <= 0.95) {
          power = 110;
          magnitude = 9;
        } else {
          power = 150;
          magnitude = 10;
        }
        user.getField().showMessage("Magnitude " + magnitude + "!");
        setPower(power);
        int damage = mech.calculateDamage(this, user, target);
        setPower(0);
        target.changeHealth(-damage);
        return damage;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Bind", new StatusMove(
        PokemonType.T_NORMAL, 15, 0.75, 20, new StatusEffect[] {
            new RestrainingEffect("Bind", "bound")
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Clamp", new StatusMove(
        PokemonType.T_WATER, 35, 0.75, 10, new StatusEffect[] {
            new RestrainingEffect("Clamp", "clamped")
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Fire Spin", new StatusMove(
        PokemonType.T_FIRE, 15, 0.70, 15, new StatusEffect[] {
            new RestrainingEffect("Fire Spin", "trapped in a vortex")
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Sand Tomb", new StatusMove(
        PokemonType.T_GROUND, 15, 0.70, 10, new StatusEffect[] {
            new RestrainingEffect("Sand Tomb", "trapped in a vortex")
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Whirlpool", new StatusMove(
        PokemonType.T_WATER, 15, 0.70, 15, new StatusEffect[] {
            new RestrainingEffect("Whirlpool", "trapped in a vortex")
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Wrap", new StatusMove(
        PokemonType.T_NORMAL, 15, 0.85, 20, new StatusEffect[] {
            new RestrainingEffect("Wrap", "wrapped")
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Block", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 5, new StatusEffect[] {
            new TrappingEffect("Block")
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Mean Look", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 5, new StatusEffect[] {
            new TrappingEffect("Mean Look")
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Spider Web", new StatusMove(
        PokemonType.T_BUG, 0, 1.0, 5, new StatusEffect[] {
            new TrappingEffect("Spider Web")
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Yawn", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 10, new StatusEffect[] {
            new StatusEffect() {
              private int m_turns = 1;
              public String getName() {
                return "Yawn";
              }
              public String getDescription() {
                return " became drowsy!";
              }
              public int getTier() {
                return 5;
              }
              public boolean hitsThroughSubstitute() {
                return false;
              }
              public boolean isPassable() {
                /** According to the smogon research thread,
                 *  this is not passed. This was also confirmed
                 *  was an additional in game test.
                 */
                return false;
              }
              public boolean tick(Pokemon p) {
                if (m_turns-- <= 0) {
                  p.addStatus(p.getOpponent(), new SleepEffect() {
                    public boolean hitsThroughSubstitute() {
                      return true;
                    }
                  });
                  p.removeStatus(this);
                  return false;
                }
                return true;
              }
            }
        },
        new boolean[] { false },
        new double[] { 1.0 }
    ) {
      public boolean attemptHit(BattleMechanics mech, Pokemon source, Pokemon target) {
        return true;
      }
    }));

    m_moves.add(new MoveListEntry("Skill Swap",
        new PokemonMove(PokemonType.T_PSYCHIC, 0, 1.0, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        IntrinsicAbility userAbility = (IntrinsicAbility)user.getAbility().clone();
        IntrinsicAbility targetAbility = target.getAbility();
        if (!userAbility.isSwappable() || !targetAbility.isSwappable()) {
          user.getField().showMessage("But it failed!");
          return 0;
        }
        user.setAbility(targetAbility, false);
        target.setAbility(userAbility, false);
        user.getField().showMessage(user.getName() + " swapped abilities with its opponent!");
        return 0;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Role Play",
        new PokemonMove(PokemonType.T_PSYCHIC, 0, 1.0, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        IntrinsicAbility abl = target.getAbility();
        if (!abl.isSwappable()) {
          user.getField().showMessage("But it failed!");
          return 0;
        }
        user.setAbility(abl, false);
        user.getField().showMessage(user.getName() + " copied its opponent's ability!");
        return 0;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Psych Up",
        new PokemonMove(PokemonType.T_PSYCHIC, 0, 1.0, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        user.removeStatus(StatChangeEffect.class);

        List<?> statuses = target.getNormalStatuses(0);
        Iterator<?> i = statuses.iterator();
        while (i.hasNext()) {
          StatusEffect effect = (StatusEffect)i.next();
          if (effect instanceof StatChangeEffect) {
            StatChangeEffect effectClone = (StatChangeEffect)effect.clone();
            effectClone.setDescription(null);
            // TODO: Open question: does Clear Body protect
            // against lowering stats by using this move?
                user.addStatus(target, effectClone);
          }
        }
        user.getField().showMessage(user.getName() + " copied the opponent's stat changes!");
        return 0;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Hyper Beam", new StatusMove(
        PokemonType.T_NORMAL, 150, 0.9, 5, new StatusEffect[] {
            new RechargeEffect(1)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Frenzy Plant", new StatusMove(
        PokemonType.T_GRASS, 150, 0.9, 5, new StatusEffect[] {
            new RechargeEffect(1)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Blast Burn", new StatusMove(
        PokemonType.T_FIRE, 150, 0.9, 5, new StatusEffect[] {
            new RechargeEffect(1)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Hydro Cannon", new StatusMove(
        PokemonType.T_WATER, 150, 0.9, 5, new StatusEffect[] {
            new RechargeEffect(1)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Spikes",
        new PokemonMove(PokemonType.T_GROUND, 0, 1.0, 20) {
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        return true;
      }
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        BattleField field = user.getField();
        SpikesEffect spikes = SpikesEffect.getSpikes(field, SpikesEffect.class);
        if (spikes == null) {
          spikes = new SpikesEffect();
          field.applyEffect(spikes);
        }
        spikes.addSpikes(target);
        return 0;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Rapid Spin",
        new PokemonMove(PokemonType.T_NORMAL, 20, 1.0, 40) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (getEffectiveness(user, target) == 0.0) {
          user.getField().showMessage("It doesn't affect "
              + target.getName() + "...");
          return 0;
        }
        if (user.hasEffect(RestrainingEffect.class) || user.hasEffect(LeechSeedEffect.class)) {
          user.removeStatus(RestrainingEffect.class);
          user.removeStatus(LeechSeedEffect.class);
          user.getField().showMessage(user.getName() + " was released!");
        }
        ArrayList<FieldEffect> spikes = user.getField().getEffectsByType(SpikesEffect.class);
        Iterator<FieldEffect> i = spikes.iterator();
        boolean blewAway = false;
        while (i.hasNext()) {
          SpikesEffect eff = (SpikesEffect)i.next();
          if (eff != null && eff.getLayers(user) >= 1) {
            eff.removeSpikes(user);
            blewAway = true;
          }
        }
        if (blewAway) {
          user.getField().showMessage(user.getName() + " blew away the spikes!");
        }
        return super.use(mech, user, target);                 
      }
    }
    ));

    m_moves.add(new MoveListEntry("Charge", new StatusMove(
        PokemonType.T_ELECTRIC, 0, 1.0, 20, new StatusEffect[] {
            new StatusEffect() {
              private int m_turns = 2;
              public String getName() {
                return "Charge";
              }
              public boolean apply(Pokemon p) {
                if (p.getField().getMechanics() instanceof JewelMechanics) {
                  p.addStatus(p, new StatChangeEffect(Pokemon.S_SPDEFENCE, true, 1));
                }
                return true;
              }
              //tier is not very important
              public int getTier() {
                return 1;
              }
              public String getDescription() {
                return " is charging power!";
              }
              public boolean tick(Pokemon p) {
                if (--m_turns <= 0) {
                  p.removeStatus(this);
                  return true;
                }
                return false;
              }
              public boolean isMoveTransformer(boolean enemy) {
                return !enemy;
              }
              public MoveListEntry getTransformedMove(Pokemon p, MoveListEntry entry) {
                PokemonMove move = entry.getMove();
                if (move.getType().equals(PokemonType.T_ELECTRIC)) {
                  int power = move.getPower();
                  move.setPower(power * 2);
                }
                return entry;
              }
            }
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Rest",
        new PokemonMove(PokemonType.T_PSYCHIC, 0, 1.0, 10) {
      public boolean attemptHit(BattleMechanics mech, Pokemon source, Pokemon target) {
        return true;
      }
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (user.hasAbility("Insomnia")
            || user.hasAbility("Vital Spirit")
            || user.hasEffect(SleepEffect.class)
            || (user.getHealth() == user.getStat(Pokemon.S_HP))) {
          user.getField().showMessage("But it failed!");
          return 0;
        }
        user.removeStatus(StatusEffect.SPECIAL_EFFECT_LOCK);
        // The turn it is applied still counts as a turn
        user.addStatus(user, new SleepEffect(3));                   
        int change = user.getStat(Pokemon.S_HP) - user.getHealth();
        user.changeHealth(change);
        return 0;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Reflect",
        new PokemonMove(PokemonType.T_PSYCHIC, 0, 1.0, 20) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (!user.getField().applyEffect(new StatCutEffect(Pokemon.S_DEFENCE,
            user.getParty(), "reflect"))) {
          user.getField().showMessage("But it failed!");
        }
        return 0;
      }
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        return true;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Light Screen",
        new PokemonMove(PokemonType.T_PSYCHIC, 0, 1.0, 20) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (!user.getField().applyEffect(new StatCutEffect(Pokemon.S_SPDEFENCE,
            user.getParty(), "light screen"))) {
          user.getField().showMessage("But it failed!");
        }
        return 0;
      }
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        return true;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Brick Break",
        new PokemonMove(PokemonType.T_FIGHTING, 75, 1.0, 15) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        ArrayList<FieldEffect> effects = user.getField().getEffectsByType(StatCutEffect.class);
        Iterator<FieldEffect> i = effects.iterator();
        while (i.hasNext()) {
          StatCutEffect eff = (StatCutEffect)i.next();
          if (eff.getParty() == target.getParty()) {
            user.getField().removeEffect(eff);
            user.getField().showMessage("The wall shattered!");
          }
        }
        return super.use(mech, user, target);
      }
    }
    ));

    m_moves.add(new MoveListEntry("Camouflage", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 20, new StatusEffect[] {
            new StatusEffect() {
              private PokemonType[] types;

              public String getName() {
                return "Camouflage";
              }
              public String getDescription() {
                return null;
              }
              public int getTier() {
                return -1;
              }
              public boolean tick(Pokemon p) {
                return false;
              }
              public boolean apply(Pokemon p) {
                types = p.getTypes();

                //todo: this should be based on the terrain but it isn't implemented yet
                PokemonType type = PokemonType.T_NORMAL;

                p.setType(new PokemonType[] { type });
                p.getField().showMessage(p.getName() + " became the "
                    + type + " type!");
                return true;
              }
              public boolean switchOut(Pokemon p) {
                p.setType(types);
                return true;
              }
            }
        },
        new boolean[] { true },
        new double[] { 1.0 }
    ) {
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        return true;
      }
    }));

    m_moves.add(new MoveListEntry("Nature Power",
        new PokemonMove(PokemonType.T_NORMAL, 0, 0.95, 20) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        //todo: this should also be based on terrain
        if (mech instanceof JewelMechanics) {
          return user.useMove(getMove("Swift"), target);
        } else {
          return user.useMove(getMove("Tri Attack"), target);
        }
      }
      public boolean isAttack() {
        return false;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Secret Power",
        new PokemonMove(PokemonType.T_NORMAL, 70, 1.0, 20) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        int damage = mech.calculateDamage(this, user, target);
        target.changeHealth(-damage);

        double random = mech.getRandom().nextDouble();
        //todo: this should also be based on terrain
        if (random <= 0.3) {
          target.addStatus(user, new ParalysisEffect());
        }
        return damage;
      }
    }
    ));

    //Doesn't do anything in 1 vs 1
    m_moves.add(new MoveListEntry("Follow Me",
        new PokemonMove(PokemonType.T_NORMAL, 0, 1.0, 20) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        user.getField().showMessage(user.getName() + " became the centre of attention!");
        return 0;
      }
      public int getPriority() {
        return 4;
      }
    }
    ));

    //Doesn't do anything in 1 vs 1
    m_moves.add(new MoveListEntry("Helping Hand",
        new PokemonMove(PokemonType.T_NORMAL, 0, 1.0, 20) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        user.getField().showMessage("But it failed!");
        return 0;
      }
      public int getPriority() {
        return 6;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Doom Desire",
        new PokemonMove(PokemonType.T_STEEL, 120, 0.85, 5) {
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        return true;
      }
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        int damage;
        if (!super.attemptHit(mech, user, target)) {
          damage = 0;
        } else {
          PokemonMove move = new PokemonMove(PokemonType.T_TYPELESS, this.getPower(), 1.0, 1);
          move.setMoveListEntry(getMoveListEntry());
          damage = mech.calculateDamage(move, user, target);
        }

        user.getField().applyEffect(
            new DelayedDamageEffect(damage, target.getParty(), 3));
        return 0;                   
      }
    }
    ));

    m_moves.add(new MoveListEntry("Future Sight",
        new PokemonMove(PokemonType.T_PSYCHIC, 80, 0.9, 15) {
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        return true;
      }
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        int damage;
        if (!super.attemptHit(mech, user, target)) {
          damage = 0;
        } else {
          damage = mech.calculateDamage(
              new PokemonMove(PokemonType.T_TYPELESS, this.getPower(), 1.0, 1), user, target);
        }

        user.getField().applyEffect(
            new DelayedDamageEffect(damage, target.getParty(), 3));
        return 0;                   
      }
    }
    ));

    m_moves.add(new MoveListEntry("Focus Punch",
        new DamageListenerMove(PokemonType.T_FIGHTING, 150, 1.0, 20) {          
      public void beginTurn(BattleTurn[] turn, int index, Pokemon source) {
        if (!source.hasEffect(SleepEffect.class) && !source.hasEffect(FreezeEffect.class)) {
          source.getField().showMessage(source.getName() + " is tightening its focus!");
        }
        super.beginTurn(turn, index, source);
      }
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        DamageListenerEffect listener = getListener(user);
        if (listener == null) return 0;
        if (listener.getDamage() > 0) {
          user.getField().showMessage(user.getName() + " lost its focus and couldn't move!");
          return 0;
        }
        return super.use(mech, user, target);
      }
      public int getPriority() {
        return -2;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Low Kick",
        new MassBasedMove(PokemonType.T_FIGHTING, 1.0, 20)));

    m_moves.add(new MoveListEntry("Baton Pass",
        new PokemonMove(PokemonType.T_NORMAL, 0, 1.0, 40) {
      @SuppressWarnings("unchecked")
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        BattleField field = user.getField();
        int party = user.getParty();
        if (field.getAliveCount(party) == 1) {
          user.getField().showMessage("But it failed!");
          return 0;
        }
        List<StatusEffect> list = user.getNormalStatuses(0);
        List applied = new ArrayList();
        Iterator<StatusEffect>  i = list.iterator();
        while (i.hasNext()) {
          StatusEffect effect = (StatusEffect)i.next();
          if (effect.getLock() != StatusEffect.SPECIAL_EFFECT_LOCK) {
            if (effect.isPassable()) {
              if (effect instanceof LeechSeedEffect) {
                applied.add(new LeechSeedEffect(false));
              } else if (effect instanceof PerishSongEffect) {
                applied.add(new PerishSongEffect(false));
              } else {
                applied.add(effect.clone());
              }
              if (effect instanceof CoEffect) {
                CoEffect coeffect = (CoEffect)effect;
                if (!coeffect.getType().equals(AttractEffect.class)) {
                  effect.disable();
                }
              }
            }
          }
        }
        int substitute = user.getSubstitute();
        field.requestAndWaitForSwitch(party);
        target = field.getActivePokemon()[party];
        target.setSubstitute(substitute);
        i = applied.iterator();
        field.setNarrationEnabled(false);
        while (i.hasNext()) {
          StatusEffect effect = (StatusEffect)i.next();
          target.addStatus(target, effect);
        }
        field.setNarrationEnabled(true);
        return 0;
      }
      public boolean attemptHit(BattleMechanics mech, Pokemon source, Pokemon target) {
        return true;
      }
    }));

    PokemonMove roar = new PokemonMove(PokemonType.T_NORMAL, 0, 1.0, 20) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (target.hasAbility("Suction Cups") || target.hasEffect(IngrainEffect.class)) {
          user.getField().showMessage("But it failed!");
          return 0;
        }

        if (target.isFainted())
          return 0;

        ArrayList<Pokemon> party = new ArrayList<Pokemon>(Arrays.asList(target.getTeammates()));
        Iterator<Pokemon> i = party.iterator();
        while (i.hasNext()) {
          Pokemon p = (Pokemon)i.next();
          if (p == null || p.isFainted() || (p == target)) {
            i.remove();
          }
        }
        if (party.size() == 0) {
          user.getField().showMessage("But it failed!");
          return 0;
        }
        Pokemon p = (Pokemon)party.get(mech.getRandom().nextInt(party.size()));
        p.getField().switchInPokemon(p.getParty(), p.getId());
        p.addStatus(user, new StatusEffect() {
          public int getTier() {
            return 1;
          }
          public boolean tick(Pokemon p) {
            p.removeStatus(this);
            return true;
          }
          public boolean isMoveTransformer(boolean enemy) {
            return !enemy;
          }
          public MoveListEntry getTransformedMove(Pokemon p, MoveListEntry entry) {
            return null;
          }
          public String getDescription() {
            return null;
          }
          public String getName() {
            return null;
          }
        });
        return 0;
      }
      public int getPriority() {
        return -5;
      }

    };

    PokemonMove whirlwind = (PokemonMove)roar.clone();
    m_moves.add(new MoveListEntry("Roar", roar));
    m_moves.add(new MoveListEntry("Whirlwind", whirlwind));

    m_moves.add(new MoveListEntry("Wish",
        new PokemonMove(PokemonType.T_NORMAL, 0, 1.0, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        user.getField().applyEffect(new DelayedStatusEffect(
            new PercentEffect(0.5, false, -1, "The wish came true!"),
            user.getParty(), 2, " made a wish!"));
        return 0;
      }
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        return true;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Counter",
        new CounterMove(PokemonType.T_FIGHTING, 1.0, 20, 2)));
    m_moves.add(new MoveListEntry("Mirror Coat",
        new CounterMove(PokemonType.T_PSYCHIC, 1.0, 20, 1)));

    m_moves.add(new MoveListEntry("Pursuit",
        new PokemonMove(PokemonType.T_DARK, 40, 1.0, 20) {
      public void beginTurn(BattleTurn[] turn, int index, Pokemon source) {
        // Note: assumes two pokemon.
        BattleTurn opp = turn[1 - index];
        Pokemon target = source.getOpponent();
        boolean damageNow = false;
        if (!opp.isMoveTurn()) {
          damageNow = true;
        } else {
          MoveListEntry entry = target.getMove(opp.getId());
          if (entry.getName().equals("U-turn")) {
            if (target.getStat(Pokemon.S_SPEED) > source.getStat(Pokemon.S_SPEED)) {
              damageNow = true;
            }
          }
        }

        if (!damageNow)
          return;

        // Prevent this attack from occurring later in the turn.
        turn[index] = null;

        if (source.isImmobilised(null))
          return;

        int power = getPower();
        setPower(power * 2);
        source.useMove(new MoveListEntry("Pursuit", (PokemonMove)clone()), target);
        setPower(power);
      }
    }
    ));

    m_moves.add(new MoveListEntry("Revenge",
        new DamageListenerMove(PokemonType.T_FIGHTING, 60, 1.0, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        DamageListenerEffect listener = getListener(user);
        if (listener == null) return 0;
        if (listener.getDamage() <= 0) {
          return super.use(mech, user, target);
        }
        int power = 60;
        setPower(power * 2);
        int damage = super.use(mech, user, target);
        setPower(power);
        return damage;
      }
      public int getPriority() {
        return -3;
      }
    }
    ));

    /********************************************************************
     * DP moves start
     ********************************************************************/

    m_moves.add(new MoveListEntry("U-turn",
        new PokemonMove(PokemonType.T_BUG, 70, 1.0, 20) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        int damage = mech.calculateDamage(this, user, target);
        target.changeHealth(-damage);

        int party = user.getParty();
        BattleField field = user.getField();
        if (field.getAliveCount(party) > 1) {
          field.requestAndWaitForSwitch(party);
          target = field.getActivePokemon()[party];
          HoldItem item = target.getItem();
          if (item instanceof ChoiceBandItem) {
            for (int i = 0; i < 4; ++i) {
              MoveListEntry entry = target.getMove(i);
              if ((entry != null) && entry.getName().equals("U-turn")) {
                ((ChoiceBandItem)item).setChoice(target, mech, entry);
                break;
              }
            }
          }
        }
        return damage;
      }
    }));

    m_moves.add(new MoveListEntry("Lunar Dance",
        new PokemonMove(PokemonType.T_PSYCHIC, 0, 0, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        int party = user.getParty();
        BattleField field = user.getField();
        if (field.getAliveCount(party) == 1) {
          user.getField().showMessage("But it failed!");
          return 0;
        }
        user.faint();
        field.requestAndWaitForSwitch(party);
        target = field.getActivePokemon()[party];
        target.changeHealth(target.getStat(Pokemon.S_HP));
        for (int i = 0; i < 4; ++i) {
          MoveListEntry entry = target.getMove(i);
          if (entry != null) {
            PokemonMove move = entry.getMove();
            if (move != null) {
              target.setPp(i, target.getMaxPp(i));
            }
          }
        }
        target.removeStatus(StatusEffect.SPECIAL_EFFECT_LOCK);
        return 0;
      }
      public boolean attemptHit(BattleMechanics mech, Pokemon source, Pokemon target) {
        return true;
      }
    }));

    m_moves.add(new MoveListEntry("Worry Seed",
        new PokemonMove(PokemonType.T_GRASS, 0, 1.0, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (target.hasAbility("Multitype") || target.hasSubstitute()) {
          user.getField().showMessage("But it failed!");
          return 0;
        }
        target.setAbility(IntrinsicAbility.getInstance("Insomnia"), false);
        return 0;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Psycho Shift",
        new PokemonMove(PokemonType.T_NORMAL, 0, 1.0, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (target.hasSubstitute()) {
          user.getField().showMessage("But it failed!");
          return 0;
        }
        StatusEffect eff = user.getEffect(StatusEffect.SPECIAL_EFFECT_LOCK);
        if (eff == null)
          return 0;
        StatusEffect clone = (StatusEffect)eff.clone();
        if (!target.hasEffect(StatusEffect.SPECIAL_EFFECT_LOCK)) {
          user.removeStatus(eff);
          target.addStatus(user, clone);
        }
        return 0;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Trick Room",
        new PokemonMove(PokemonType.T_PSYCHIC, 0, 1.0, 5) {
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        return true;
      }
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        BattleField field = user.getField();
        FieldEffect effect = field.getEffectByType(SpeedSwapEffect.class);
        if (effect != null) {
          field.removeEffect(effect);
        } else {
          field.showMessage(user.getName() + " twisted the dimensions!");
          field.applyEffect(new SpeedSwapEffect());
        }
        return 0;
      }
      public int getPriority() {
        return -5;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Gyro Ball",
        new PokemonMove(PokemonType.T_STEEL, 100, 1.0, 5) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        int power = 51 * target.getStat(Pokemon.S_SPEED) /
        user.getStat(Pokemon.S_SPEED) / 2;
        if (power > 150) power = 150;
        setPower(getPower() * power / 100);
        int damage = mech.calculateDamage(this, user, target);
        target.changeHealth(-damage);
        setPower(100);
        return damage;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Wake-up Slap",
        new PokemonMove(PokemonType.T_FIGHTING, 60, 1.0, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        int power = getPower();
        if (!target.hasSubstitute() && target.hasEffect(SleepEffect.class)) {
          setPower(2 * power);
          target.removeStatus(StatusEffect.SPECIAL_EFFECT_LOCK);
          user.getField().showMessage(target.getName() + " woke up!");
        }
        int damage = mech.calculateDamage(this, user, target);
        setPower(power);
        target.changeHealth(-damage);
        return damage;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Hammer Arm", new StatusMove(
        PokemonType.T_FIGHTING, 100, 0.9, 10, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPEED, false)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Brine",
        new PokemonMove(PokemonType.T_WATER, 65, 1.0, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        int power = getPower();
        if (target.getHealth() < (target.getStat(Pokemon.S_HP) / 2)) {
          setPower(power * 2);
        }
        int damage = super.use(mech, user, target);
        setPower(power);
        return damage;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Acupressure",
        new PokemonMove(PokemonType.T_NORMAL, 0, 1.0, 30) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        int random = mech.getRandom().nextInt(7) + 1;
        user.addStatus(user, new StatChangeEffect(random, true, 2));
        return 0;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Close Combat", new StatusMove(
        PokemonType.T_FIGHTING, 120, 1.0, 5, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_DEFENCE, false),
            new StatChangeEffect(Pokemon.S_SPDEFENCE, false)
        },
        new boolean[] { true, true },
        new double[] { 1.0, 1.0 }
    )));

    //todo: get right tier   
    m_moves.add(new MoveListEntry("Aqua Ring", new StatusMove(
        PokemonType.T_WATER, 0, 1.0, 20, new StatusEffect[] {
            new PercentEffect(0.0625, false, 3, "'s Aqua Ring restored health!") {
              public boolean isSingleton() {
                return true;
              }
            }
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    //todo: find correct rate for all of these
    m_moves.add(new MoveListEntry("Flare Blitz",
        new RecoilMove(PokemonType.T_FIRE, 120, 1.0, 15, 1.0/3.0)));

    m_moves.add(new MoveListEntry("Brave Bird",
        new RecoilMove(PokemonType.T_FLYING, 120, 1.0, 15, 1.0/3.0)));

    m_moves.add(new MoveListEntry("Wood Hammer",
        new RecoilMove(PokemonType.T_GRASS, 120, 1.0, 15, 1.0/3.0)));

    m_moves.add(new MoveListEntry("Head Smash",
        new RecoilMove(PokemonType.T_ROCK, 150, 0.8, 5, 0.5)));

    m_moves.add(new MoveListEntry("Force Palm", new StatusMove(
        PokemonType.T_FIGHTING, 60, 1.0, 10, new StatusEffect[] {
            new ParalysisEffect()
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("Aura Sphere",
        new PerfectAccuracyMove(PokemonType.T_FIGHTING, 90, 20)));

    m_moves.add(new MoveListEntry("Magnet Bomb",
        new PerfectAccuracyMove(PokemonType.T_STEEL, 60, 20)));

    m_moves.add(new MoveListEntry("Rock Polish", new StatusMove(
        PokemonType.T_ROCK, 0, 1.0, 30, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPEED, true, 2)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Poison Jab", new StatusMove(
        PokemonType.T_POISON, 80, 1.0, 20, new StatusEffect[] {
            new PoisonEffect()
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("Dark Pulse", new StatusMove(
        PokemonType.T_DARK, 80, 1.0, 15, new StatusEffect[] {
            new FlinchEffect()
        },
        new boolean[] { false },
        new double[] { 0.2 }
    )));

    m_moves.add(new MoveListEntry("Night Slash",
        new HighCriticalHitMove(PokemonType.T_DARK, 70, 1.0, 15)));

    m_moves.add(new MoveListEntry("Shadow Claw",
        new HighCriticalHitMove(PokemonType.T_GHOST, 70, 1.0, 15)));

    m_moves.add(new MoveListEntry("Psycho Cut",
        new HighCriticalHitMove(PokemonType.T_PSYCHIC, 70, 1.0, 20)));

    m_moves.add(new MoveListEntry("Stone Edge",
        new HighCriticalHitMove(PokemonType.T_ROCK, 100, 0.8, 5)));

    m_moves.add(new MoveListEntry("Attack Order",
        new HighCriticalHitMove(PokemonType.T_BUG, 90, 1.0, 15)));

    m_moves.add(new MoveListEntry("Spacial Rend",
        new HighCriticalHitMove(PokemonType.T_DRAGON, 100, 0.95, 5)));

    m_moves.add(new MoveListEntry("Aqua Tail",
        new PokemonMove(PokemonType.T_WATER, 90, 0.9, 10)));

    m_moves.add(new MoveListEntry("Seed Bomb",
        new PokemonMove(PokemonType.T_GRASS, 80, 1.0, 15)));

    m_moves.add(new MoveListEntry("X-Scissor",
        new PokemonMove(PokemonType.T_BUG, 80, 1.0, 15)));

    m_moves.add(new MoveListEntry("Dragon Pulse",
        new PokemonMove(PokemonType.T_DRAGON, 90, 1.0, 10)));

    m_moves.add(new MoveListEntry("Power Gem",
        new PokemonMove(PokemonType.T_ROCK, 70, 1.0, 20)));

    m_moves.add(new MoveListEntry("Power Whip",
        new PokemonMove(PokemonType.T_GRASS, 120, 0.85, 10)));

    m_moves.add(new MoveListEntry("Air Slash", new StatusMove(
        PokemonType.T_FLYING, 75, 0.95, 20, new StatusEffect[] {
            new FlinchEffect()
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("Zen Headbutt", new StatusMove(
        PokemonType.T_PSYCHIC, 80, 0.9, 15, new StatusEffect[] {
            new FlinchEffect()
        },
        new boolean[] { false },
        new double[] { 0.2 }
    )));

    m_moves.add(new MoveListEntry("Dragon Rush", new StatusMove(
        PokemonType.T_DRAGON, 100, 0.75, 10, new StatusEffect[] {
            new FlinchEffect()
        },
        new boolean[] { false },
        new double[] { 0.2 }
    )));

    m_moves.add(new MoveListEntry("Iron Head", new StatusMove(
        PokemonType.T_STEEL, 80, 1.0, 15, new StatusEffect[] {
            new FlinchEffect()
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("Bug Buzz", new StatusMove(
        PokemonType.T_BUG, 90, 1.0, 10, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPDEFENCE, false)
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Drain Punch",
        new AbsorbMove(PokemonType.T_FIGHTING, 60, 1.0, 5, 0.5)));

    m_moves.add(new MoveListEntry("Vacuum Wave",
        new PriorityMove(PokemonType.T_FIGHTING, 40, 1.0, 30, 1)));

    m_moves.add(new MoveListEntry("Bullet Punch",
        new PriorityMove(PokemonType.T_STEEL, 40, 1.0, 30, 1)));

    m_moves.add(new MoveListEntry("Ice Shard",
        new PriorityMove(PokemonType.T_ICE, 40, 1.0, 30, 1)));

    m_moves.add(new MoveListEntry("Shadow Sneak",
        new PriorityMove(PokemonType.T_GHOST, 40, 1.0, 30, 1)));

    m_moves.add(new MoveListEntry("Aqua Jet",
        new PriorityMove(PokemonType.T_WATER, 40, 1.0, 30, 1)));

    m_moves.add(new MoveListEntry("Focus Blast", new StatusMove(
        PokemonType.T_FIGHTING, 120, 0.7, 5, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPDEFENCE, false)
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Energy Ball", new StatusMove(
        PokemonType.T_GRASS, 80, 1.0, 10, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPDEFENCE, false)
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Earth Power", new StatusMove(
        PokemonType.T_GROUND, 90, 1.0, 10, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPDEFENCE, false)
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Mirror Shot", new StatusMove(
        PokemonType.T_STEEL, 65, 0.85, 10, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_ACCURACY, false)
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("Flash Cannon", new StatusMove(
        PokemonType.T_STEEL, 80, 1.0, 10, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPATTACK, false)
        },
        new boolean[] { false },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Rock Climb", new StatusMove(
        PokemonType.T_NORMAL, 90, 0.85, 20, new StatusEffect[] {
            new ConfuseEffect()
        },
        new boolean[] { false },
        new double[] { 0.2 }
    )));

    m_moves.add(new MoveListEntry("Switcheroo",
        new PokemonMove(PokemonType.T_DARK, 0, 1.0, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (target.hasSubstitute()) {
          user.getField().showMessage("But it failed!");
          return 0;
        }
        if ((target.hasAbility("Sticky Hold")) || (user.hasAbility("Sticky Hold"))) {
          if (user.hasAbility("Sticky Hold")) {
            user.getField().showMessage(user.getName() + " hung on with its Sticky Hold!");
          }
          if (target.hasAbility("Sticky Hold")) {
            user.getField().showMessage(target.getName() + " hung on with its Sticky Hold!");
          }
          return 0;
        }

        HoldItem targetItem = target.getItem();
        HoldItem item = user.getItem();
        /**if (item == null) {
                        user.getField().showMessage("But it failed!");
                        return 0;
                    }**/

        HoldItem userItem = (item == null) ? null : (HoldItem)item.clone();
        user.setItem(targetItem);
        target.setItem(userItem);
        if (targetItem != null) {
          user.getField().showMessage(user.getName() + " obtained " + targetItem.getName() + "!");
        }
        if (userItem != null) {
          user.getField().showMessage(target.getName() + " obtained " + userItem.getName() + "!");
        }
        return 0;
      }
      public boolean isAttack() {
        return true;
      }
      public boolean isDamaging() {
        return false;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Giga Impact", new StatusMove(
        PokemonType.T_NORMAL, 150, 0.9, 5, new StatusEffect[] {
            new RechargeEffect(1)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Rock Wrecker", new StatusMove(
        PokemonType.T_ROCK, 150, 0.9, 5, new StatusEffect[] {
            new RechargeEffect(1)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Roar Of Time", new StatusMove(
        PokemonType.T_DRAGON, 150, 0.9, 5, new StatusEffect[] {
            new RechargeEffect(1)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Nasty Plot", new StatusMove(
        PokemonType.T_DARK, 0, 1.0, 20, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPATTACK, true, 2)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    //todo: correct probabilities?
    m_moves.add(new MoveListEntry("Thunder Fang", new StatusMove(
        PokemonType.T_ELECTRIC, 65, 0.95, 15, new StatusEffect[] {
            new FlinchEffect(),
            new ParalysisEffect()
        },
        new boolean[] { false, false },
        new double[] { 0.1, 0.1 }
    )));

    m_moves.add(new MoveListEntry("Ice Fang", new StatusMove(
        PokemonType.T_ICE, 65, 0.95, 15, new StatusEffect[] {
            new FlinchEffect(),
            new FreezeEffect()
        },
        new boolean[] { false, false },
        new double[] { 0.1, 0.1 }
    )));

    m_moves.add(new MoveListEntry("Fire Fang", new StatusMove(
        PokemonType.T_FIRE, 65, 0.95, 15, new StatusEffect[] {
            new FlinchEffect(),
            new BurnEffect()
        },
        new boolean[] { false, false },
        new double[] { 0.1, 0.1 }
    )));

    m_moves.add(new MoveListEntry("Mud Bomb", new StatusMove(
        PokemonType.T_GROUND, 65, 0.85, 10, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_ACCURACY, false)
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("Defog", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 15, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_EVASION, false)
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Draco Meteor", new StatusMove(
        PokemonType.T_DRAGON, 140, 0.90, 5, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPATTACK, false, 2)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Leaf Storm", new StatusMove(
        PokemonType.T_GRASS, 140, 0.90, 5, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPATTACK, false, 2)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Discharge", new StatusMove(
        PokemonType.T_ELECTRIC, 80, 1.0, 15, new StatusEffect[] {
            new ParalysisEffect()
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("Lava Plume", new StatusMove(
        PokemonType.T_FIRE, 80, 1.0, 15, new StatusEffect[] {
            new BurnEffect()
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("Cross Poison",
        new StatusMove(PokemonType.T_POISON, 70, 1.0, 20, new StatusEffect[] {
            new PoisonEffect()
        },
        new boolean[] { false },
        new double[] { 0.1 }
        ) {
      public boolean hasHighCriticalHitRate() { return true; }
    }
    ));

    m_moves.add(new MoveListEntry("Gunk Shot", new StatusMove(
        PokemonType.T_POISON, 120, 0.7, 5, new StatusEffect[] {
            new PoisonEffect()
        },
        new boolean[] { false },
        new double[] { 0.3 }
    )));

    m_moves.add(new MoveListEntry("Captivate",
        new StatusMove(PokemonType.T_NORMAL, 0, 1.0, 20, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPATTACK, false, 2)
        },
        new boolean[] { false },
        new double[] { 1.0 }
        ) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        int userGender = user.getGender();
        int targetGender = target.getGender();
        if ((userGender == targetGender) || (userGender == PokemonSpecies.GENDER_NONE)
            || (targetGender == PokemonSpecies.GENDER_NONE)) {
          user.getField().showMessage("But it failed!");
          return 0;
        }
        return super.use(mech, user, target);
      }
    }
    ));

    m_moves.add(new MoveListEntry("Chatter", new StatusMove(
        PokemonType.T_FLYING, 60, 1.0, 20, new StatusEffect[] {
            new ConfuseEffect()
        },
        new boolean[] { false },
        new double[] { 0.31 }
    )));

    m_moves.add(new MoveListEntry("Charge Beam", new StatusMove(
        PokemonType.T_ELECTRIC, 50, 0.9, 10, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPATTACK, true)
        },
        new boolean[] { true },
        new double[] { 0.7 }
    )));

    m_moves.add(new MoveListEntry("Defend Order", new StatusMove(
        PokemonType.T_BUG, 0, 1.0, 10, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_DEFENCE, true),
            new StatChangeEffect(Pokemon.S_SPDEFENCE, true)
        },
        new boolean[] { true, true },
        new double[] { 1.0, 1.0 }
    )));

    m_moves.add(new MoveListEntry("Substitute",
        new PokemonMove(PokemonType.T_NORMAL, 0, 1.0, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (!user.createSubstitute()) {
          user.getField().showMessage("But it failed!");
        } else {
          user.addStatus(user, new SubstituteEffect());
        }
        return 0;
      }
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        return true;
      }
    }));

    m_moves.add(new MoveListEntry("Heal Order", new StatusMove(
        PokemonType.T_BUG, 0, 1.0, 10, new StatusEffect[] {
            new PercentEffect(0.5, false, -1, null)
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Double Hit",
        new PokemonMove(PokemonType.T_NORMAL, 35, 0.9, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        int damage = 0;
        for (int i = 0; i < 2; ++i) {
          final int partial = mech.calculateDamage(this, user, target);
          target.changeHealth(-partial);
          damage += partial;
        }
        user.getField().showMessage("Hit 2 time(s)!");
        return damage;
      }
    }
    ));

    PokemonMove crushGrip = new PokemonMove(PokemonType.T_NORMAL, 0, 1.0, 5) {
      //todo: this formula may not be exactly correct
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        int power = (int)(110.0 *
            (((double)target.getHealth())
                / ((double)target.getStat(Pokemon.S_HP))));

        setPower(power);
        int damage = mech.calculateDamage(this, user, target);
        target.changeHealth(-damage);
        return damage;
      }
      public boolean isAttack() {
        return true;
      }
    };

    PokemonMove wringOut = (PokemonMove)crushGrip.clone();
    m_moves.add(new MoveListEntry("Crush Grip", crushGrip));
    m_moves.add(new MoveListEntry("Wring Out", wringOut));

    m_moves.add(new MoveListEntry("Feint",
        new PokemonMove(PokemonType.T_NORMAL, 50, 1.0, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (!target.hasEffect(CounterEffect.class)) {
          user.getField().showMessage("But it failed!");
          return 0;
        }
        return super.use(mech, user, target);                   
      }
    }
    ));

    m_moves.add(new MoveListEntry("Trump Card",
        new PokemonMove(PokemonType.T_NORMAL, 0, 1.0, 5) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        //todo: these are estimated values for the powers
        int pp = 5;
        for (int i = 0; i < 4; ++i) {
          MoveListEntry entry = user.getMove(i);
          if (entry == null) continue;
          if (entry.getName().equals("Trump Card")) {
            pp = user.getPp(i);
            break;
          }
        }
        if (pp > 4) {
          setPower(35);
        } else if (pp == 4) {
          setPower(50);
        } else if (pp == 3) {
          setPower(60);
        } else if (pp == 2) {
          setPower(75);
        } else {
          setPower(190);
        }
        int damage = super.use(mech, user, target);
        setPower(0);
        return damage;
      }
      public boolean isAttack() {
        return true;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Punishment",
        new PokemonMove(PokemonType.T_DARK, 0, 1.0, 5) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        int raises = 0;
        List<StatusEffect> statuses = target.getNormalStatuses(0);
        Iterator<StatusEffect> i = statuses.iterator();
        while (i.hasNext()) {
          StatusEffect effect = (StatusEffect)i.next();
          if (!(effect instanceof StatChangeEffect)) continue;
          if (((StatChangeEffect)effect).isRaise()) {
            raises++;
          }
        }
        setPower(60 + 20 * raises);
        int damage = super.use(mech, user, target);
        setPower(0);
        return damage;
      }
      public boolean isAttack() {
        return true;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Last Resort",
        new PokemonMove(PokemonType.T_NORMAL, 130, 1.0, 5) {
      class LastResortEffect extends StatusEffect {
        int[] m_pp;
        public LastResortEffect(int[] pp) {
          m_pp = pp;
        }
        public int getPp(int i) {
          return m_pp[i];
        }
      }
      public void switchIn(Pokemon p) {
        int[] pp = new int[4];
        for (int i = 0; i < 4; ++i) {
          pp[i] = p.getPp(i);
        }
        p.addStatus(p, new LastResortEffect(pp));
      }
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        LastResortEffect effect = (LastResortEffect)
        user.getEffect(LastResortEffect.class);
        if (effect == null) {
          user.getField().showMessage("But it failed!");
          return 0;
        }
        boolean hasOtherMoves = false;
        for (int i = 0; i < 4; ++i) {
          MoveListEntry entry = user.getMove(i);
          if (entry == null) continue;
          if (!entry.getName().equals("Last Resort")) {
            hasOtherMoves = true;
            if (user.getPp(i) >= effect.getPp(i)) {
              hasOtherMoves = false;
              break;
            }
          }
        }
        if (!hasOtherMoves) {
          user.getField().showMessage("But it failed!");
          return 0;
        }
        return super.use(mech, user, target);
      }
    }
    ));

    m_moves.add(new MoveListEntry("Magma Storm", new StatusMove(
        PokemonType.T_FIRE, 120, 0.70, 5, new StatusEffect[] {
            new RestrainingEffect("Magma Storm", "trapped in a vortex!")
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Dark Void", new StatusMove(
        PokemonType.T_DARK, 0, 0.8, 10, new StatusEffect[] {
            new SleepEffect()
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Seed Flare", new StatusMove(
        PokemonType.T_GRASS, 120, 0.85, 5, new StatusEffect[] {
            new StatChangeEffect(Pokemon.S_SPDEFENCE, false)
        },
        new boolean[] { false },
        new double[] { 0.4 }
    )));

    m_moves.add(new MoveListEntry("Protect", new ProtectMove(
        PokemonType.T_NORMAL, 10, new ProtectEffect() {
          public String getDescription() {
            return " protected itself!";
          }
        })));

    m_moves.add(new MoveListEntry("Endure", new ProtectMove(
        PokemonType.T_NORMAL, 10, new EndureEffect()
    )));

    m_moves.add(new MoveListEntry("Detect", new ProtectMove(
        PokemonType.T_FIGHTING, 5, new ProtectEffect() {
          public String getDescription() {
            return " braced itself!";
          }
        })));

    m_moves.add(new MoveListEntry("Taunt", new StatusMove(
        PokemonType.T_DARK, 0, 1.0, 20, new StatusEffect[] {
            new StatusEffect() {
              private int m_turns;
              public boolean apply(Pokemon p) {
                Random r = p.getField().getMechanics().getRandom();
                m_turns = r.nextInt(3) + 3;
                return true;
              }
              public String getDescription() {
                return " fell for the taunt!";
              }
              public String getName() {
                return "Taunt";
              }
              public int getTier() {
                return 1;
              }
              public boolean tick(Pokemon p) {
                if (--m_turns == 0) {
                  p.removeStatus(this);
                  p.getField().showMessage(p.getName() + "'s taunt wore off!");
                  return true;
                }
                return false;
              }
              public boolean isMoveTransformer(boolean enemy) {
                return !enemy;
              }
              public boolean hitsThroughSubstitute() {
                // TODO: NOTE: Does not hit through in advance!
                return true;
              }
              public boolean vetoesMove(Pokemon p, MoveListEntry entry) {
                String name = entry.getName();
                if (name.equals("Struggle")) {
                  return false;
                }
                HashSet<String> set = new HashSet<String>(Arrays.asList(new String[] {
                    "Nature Power", "Sleep Talk", "Assist", "Metronome"
                }));
                if (set.contains(name)) {
                  return true;
                }
                return !entry.getMove().isDamaging();
              }
              public MoveListEntry getTransformedMove(Pokemon p, MoveListEntry entry) {
                if (vetoesMove(p, entry)) {
                  BattleField field = p.getField();
                  String move = entry.getName();
                  field.informUseMove(p, move);
                  field.showMessage(p.getName() + " can't use " + move + " after the taunt!");
                  return null;
                }
                return entry;
              }
            }
        },
        new boolean[] { false },
        new double[] { 1.0 }
    ) {
      public boolean attemptHit(BattleMechanics mech, Pokemon source, Pokemon target) {
        return true;
      }
    }));

    m_moves.add(new MoveListEntry("Shadow Force", new StatusMove(
        PokemonType.T_GHOST, 0, 1.0, 5, new StatusEffect[] {
            new InvulnerableStateEffect(new String[0]),
            new ChargeEffect(1, "dissappeared from sight!", new MoveListEntry(
                "Shadow Force",
                new PokemonMove(PokemonType.T_GHOST, 120, 1.0, 5)
            )
            )
        },
        new boolean[] { true, true },
        new double[] { 1.0, 1.0 }
    )   {
      public boolean isAttack() {
        return true;
      }
      public boolean isDamaging() {
        return true;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Ominous Wind", new StatusMove(
        PokemonType.T_GHOST, 60, 1.0, 5, new StatusEffect[] {
            new MultipleStatChangeEffect(new int[] {
                Pokemon.S_ATTACK,
                Pokemon.S_DEFENCE,
                Pokemon.S_SPEED,
                Pokemon.S_SPATTACK,
                Pokemon.S_SPDEFENCE
            }
            )
        },
        new boolean[] { true },
        new double[] { 0.1 }
    )));

    m_moves.add(new MoveListEntry("Toxic Spikes",
        new PokemonMove(PokemonType.T_POISON, 0, 1.0, 20) {
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        return true;
      }
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        BattleField field = user.getField();
        ToxicSpikesEffect spikes = (ToxicSpikesEffect)SpikesEffect.getSpikes(field, ToxicSpikesEffect.class);
        if (spikes == null) {
          spikes = new ToxicSpikesEffect();
          field.applyEffect(spikes);
        }
        spikes.addSpikes(target);
        return 0;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Stealth Rock",
        new PokemonMove(PokemonType.T_ROCK, 0, 1.0, 20) {
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        return true;
      }
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        BattleField field = user.getField();
        StealthRockEffect spikes = (StealthRockEffect)SpikesEffect.getSpikes(field, StealthRockEffect.class);
        if (spikes == null) {
          spikes = new StealthRockEffect();
          field.applyEffect(spikes);
        }
        spikes.addSpikes(target);
        return 0;
      }
    }
    ));

    class MeFirstEffect extends StatusEffect {
      private MoveListEntry m_move;
      public MeFirstEffect(MoveListEntry move) {
        m_move = move;
      }
      public boolean isMoveTransformer(boolean enemy) {
        return !enemy;
      }
      public MoveListEntry getTransformedMove(Pokemon p, MoveListEntry entry) {
        p.getField().informUseMove(p, "Me First");
        return m_move;
      }
      public int getTier() {
        return 1;
      }
      public boolean tick(Pokemon p) {
        p.removeStatus(this);
        return true;
      }
      public String getName() {
        return null;
      }
      public String getDescription() {
        return null;
      }
    }

    m_moves.add(new MoveListEntry("Me First",
        new PokemonMove(PokemonType.T_NORMAL, 0, 1.0, 20) {
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        return true;
      }
      public void beginTurn(BattleTurn[] turn, int index, Pokemon source) {
        // Assume two pokemon
        /** Note: You cannot give PokemonMoves states
         * outside of their nature as a move - there is only one
         * copy for the whole program, not for each pokemon who
         * has the move! The latter would be a massive waste of
         * memory.
         */
        if ((index == 1) || source.hasEffect(SleepEffect.class) || source.hasEffect(FreezeEffect.class)) {
          return;
        }
        BattleTurn opp = turn[1 - index];
        Pokemon target = source.getOpponent();
        if (!opp.isMoveTurn()) return;
        MoveListEntry entry = (MoveListEntry)target.getMove(opp.getId()).clone();
        PokemonMove move = entry.getMove();
        int power = move.getPower();
        if (!move.isDamaging()) return;
        move.setPower(power * 3 / 2);
        source.addStatus(source, new MeFirstEffect(entry));
      }
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        user.getField().showMessage("But it failed!");
        return 0;
      }
    }
    ));

    class SuckerPunchEffect extends StatusEffect {
      public int getTier() {
        return 1;
      }
      public boolean tick(Pokemon p) {
        p.removeStatus(this);
        return true;
      }
      public String getName() {
        return null;
      }
      public String getDescription() {
        return null;
      }
    }

    m_moves.add(new MoveListEntry("Sucker Punch",
        new PokemonMove(PokemonType.T_DARK, 80, 1.0, 5) {
      public void beginTurn(BattleTurn[] turn, int index, Pokemon source) {
        // Assume two pokemon
        if (index == 1) {
          // User must be going first.
          return;
        }
        BattleTurn opp = turn[1 - index];
        if (opp.isMoveTurn() && opp.getMove(source.getOpponent()).isDamaging()) {
          source.addStatus(source, new SuckerPunchEffect());
        }
      }
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (!user.hasEffect(SuckerPunchEffect.class)) {
          user.getField().showMessage("But it failed!");
          return 0;
        }
        return super.use(mech, user, target);
      }
      public int getPriority() {
        return 1;
      }
    }
    ));

    class AssuranceEffect extends StatusEffect {
      private int m_health;
      public boolean apply(Pokemon p) {
        m_health = p.getOpponent().getHealth();
        return true;
      }
      public int getHealth() {
        return m_health;
      }
      public int getTier() {
        return 5;
      }
      public boolean tick(Pokemon p) {
        p.removeStatus(this);
        return true;
      }
    }

    m_moves.add(new MoveListEntry("Assurance",
        new PokemonMove(PokemonType.T_DARK, 50, 1.0, 10) {
      public void beginTurn(BattleTurn[] turn, int index, Pokemon source) {
        source.getOpponent().addStatus(source, new AssuranceEffect());
      }
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        int power = getPower();
        if (!target.hasEffect(AssuranceEffect.class)) {
          StatusEffect effect = target.getEffect(AssuranceEffect.class);
          AssuranceEffect eff = (AssuranceEffect) effect;
          if (target.getHealth() < eff.getHealth()) {
            setPower(power * 2);
          }
          int damage = super.use(mech, user, target);
          setPower(power);
          return damage;
        } else {
          return super.use(mech, user, target);
        }
      }
    }
    ));



    m_moves.add(new MoveListEntry("Judgement",
        new PokemonMove(PokemonType.T_NORMAL, 100, 1.0, 10)));

    m_moves.add(new MoveListEntry("Metal Burst",
        new CounterMove(PokemonType.T_STEEL, 1.0, 10, 3) {
      public int getPriority() {
        return 0;
      }
    }
    ));

    DamageListenerMove payback = new DamageListenerMove(PokemonType.T_DARK, 50, 1.0, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        DamageListenerEffect listener = getListener(user);
        if ((listener == null) || (listener.getDamage() <= 0)) {
          return super.use(mech, user, target);
        }
        int power = getPower();
        setPower(power * 2);
        int damage = super.use(mech, user, target);
        setPower(power);
        return damage;
      }
    };

    DamageListenerMove avalanche = (DamageListenerMove)payback.clone();
    avalanche.setType(PokemonType.T_ICE);
    avalanche.setPower(60);
    avalanche.setPriority(-3);
    m_moves.add(new MoveListEntry("Payback", payback));
    m_moves.add(new MoveListEntry("Avalanche", avalanche));

    m_moves.add(new MoveListEntry("Roost", new StatusMove(
        PokemonType.T_FLYING, 0, 1.0, 10, new StatusEffect[] {
            new StatusEffect() {
              private PokemonType[] m_types;
              public boolean apply(Pokemon p) {
                m_types = p.getTypes();
                ArrayList<PokemonType> types = new ArrayList<PokemonType>(Arrays.asList(m_types));
                Iterator<PokemonType> i = types.iterator();
                while (i.hasNext()) {
                  PokemonType type = (PokemonType)i.next();
                  if (type.equals(PokemonType.T_FLYING)) {
                    i.remove();
                  }
                }
                p.setType((PokemonType[])types.toArray(new PokemonType[types.size()]));
                return true;
              }
              public String getName() {
                return "Roosting";
              }
              public int getTier() {
                return 5;
              }
              public String getDescription() {
                return null;
              }
              public void unapply(Pokemon p) {
                p.setType(m_types);
              }
              public boolean tick(Pokemon p) {
                p.removeStatus(this);
                return true;
              }
            }, new PercentEffect(0.5, false, -1, null)
        },
        new boolean[] { true, true },
        new double[] { 1.0, 1.0 }
    )));

    m_moves.add(new MoveListEntry("Grass Knot",
        new MassBasedMove(PokemonType.T_GRASS, 1.0, 20)));

    m_moves.add(new MoveListEntry("Guard Swap",
        new StatChangeSwapMove(PokemonType.T_PSYCHIC, 10, new int[] {
            Pokemon.S_DEFENCE,
            Pokemon.S_SPDEFENCE
        }
        )));

    m_moves.add(new MoveListEntry("Power Swap",
        new StatChangeSwapMove(PokemonType.T_PSYCHIC, 10, new int[] {
            Pokemon.S_ATTACK,
            Pokemon.S_SPATTACK
        }
        )));

    m_moves.add(new MoveListEntry("Heart Swap",
        new StatChangeSwapMove(PokemonType.T_PSYCHIC, 10, new int[] {
            Pokemon.S_DEFENCE,
            Pokemon.S_SPDEFENCE,
            Pokemon.S_ATTACK,
            Pokemon.S_SPATTACK,
            Pokemon.S_SPEED,
            Pokemon.S_ACCURACY,
            Pokemon.S_EVASION
        }
        )));

    m_moves.add(new MoveListEntry("Outrage",
        new RampageMove(PokemonType.T_DRAGON, 120, 1.0, 15) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        setPower((int)(((double)getPower()) / 120.0 * ((mech instanceof JewelMechanics) ? 120.0 : 90.0)));
        return super.use(mech, user, target);
      }
    }));

    m_moves.add(new MoveListEntry("Petal Dance",
        new RampageMove(PokemonType.T_GRASS, 70, 1.0, 20)
    ));

    m_moves.add(new MoveListEntry("Thrash",
        new RampageMove(PokemonType.T_NORMAL, 90, 1.0, 20)
    ));

    m_moves.add(new MoveListEntry("Fake Out",
        new PokemonMove(PokemonType.T_NORMAL, 40, 1.0, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (!user.isFirstTurn()) {
          user.getField().showMessage("But it failed!");
          return 0;
        }
        if ((getEffectiveness(user, target) != 0.0) && !target.hasSubstitute()) {
          target.addStatus(user, new FlinchEffect());
        }
        return super.use(mech, user, target);
      }
      public int getPriority() {
        return 1;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Magnet Rise",
        new PokemonMove(PokemonType.T_ELECTRIC, 0, 0, 10) {
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        return true;
      }
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        StatusEffect eff = user.addStatus(user, new MagnetRiseEffect());
        if (eff == null) {
          user.getField().showMessage("But it failed!");
        }
        return 0;
      }
    }
    ));

    PokemonMove mirrorMove = new PokemonMove(PokemonType.T_PSYCHIC, 0, 1.0, 20) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (target.getLastMove() == null) {
          user.getField().showMessage("But it failed!");
          return 0;
        }
        return user.useMove(target.getLastMove(), target);
      }
    };

    m_moves.add(new MoveListEntry("Mirror Move", (PokemonMove)mirrorMove.clone()));
    m_moves.add(new MoveListEntry("Copycat", (PokemonMove)mirrorMove.clone()));

    m_moves.add(new MoveListEntry("Spite",
        new PokemonMove(PokemonType.T_GHOST, 0, 1.0, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        MoveListEntry move = target.getLastMove();
        if (move == null) {
          user.getField().showMessage("But it failed!");
          return 0;
        }
        for (int i = 0; i < 4; ++i) {
          if (move.equals(target.getMove(i))) {
            int number = (mech instanceof JewelMechanics) ? 4 :
              mech.getRandom().nextInt(3) + 2;
            target.setPp(i, target.getPp(i) - number);
            return 0;
          }
        }
        return 0;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Destiny Bond", new StatusMove(
        PokemonType.T_GHOST, 0, 1.0, 5, new StatusEffect[] {
            new StatusEffect() {
              public String getName() {
                return "Destiny bond";
              }
              public String getDescription() {
                return " is trying to take its foe with it!";
              }
              public int getTier() {
                return -1;
              }
              public boolean tick(Pokemon p) {
                return false;
              }
              public void executeTurn(Pokemon p, BattleTurn turn) {
                p.removeStatus(this);
              }
              public boolean isListener() {
                return true;
              }
              public boolean hitsThroughSubstitute() {
                return true;
              }
              public void informDamaged(Pokemon source, Pokemon target, MoveListEntry move, int damage) {
                if (target.getHealth() <= 0) {
                  target.getField().showMessage(target.getName() + " took " +
                      source.getName() + " with it!");
                  source.faint();
                }
              }
            }
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Grudge", new StatusMove(
        PokemonType.T_GHOST, 0, 1.0, 5, new StatusEffect[] {
            new StatusEffect() {
              public String getName() {
                return "Grudge";
              }
              public String getDescription() {
                return " is bearing a Grudge!";
              }
              public int getTier() {
                return -1;
              }
              public boolean tick(Pokemon p) {
                return false;
              }
              public boolean isListener() {
                return true;
              }
              public boolean hitsThroughSubstitute() {
                return true;
              }
              public void informDamaged(Pokemon source, Pokemon target, MoveListEntry move, int damage) {
                if (target.getHealth() <= 0) {
                  for (int i = 0; i < 4; i++) {
                    if (move.equals(source.getMove(i))) {
                      source.getField().showMessage(
                          move.getName() + " lost its PP due to the Grudge!");
                      source.setPp(i, 0);
                      break;
                    }
                  }
                }
              }
            }
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Miracle Eye", new StatusMove(
        PokemonType.T_PSYCHIC, 0, 1.0, 40, new StatusEffect[] {
            new StatusEffect() {
              public String getName() {
                return "Miracle";
              }
              public String getDescription() {
                return " indentified the enemy Pokemon!";
              }
              public int getTier() {
                return -1;
              }
              public boolean tick(Pokemon p) {
                return false;
              }
              public boolean isEffectivenessTransformer(boolean enemy) {
                return !enemy;
              }
              public double getTransformedEffectiveness(PokemonType move, PokemonType pokemon) {
                if (move.equals(PokemonType.T_PSYCHIC) && pokemon.equals(PokemonType.T_DARK)) {
                  return 1.0;
                }
                return super.getTransformedEffectiveness(move, pokemon);
              }
            }
        },
        new boolean[] { false },
        new double[] { 1.0 }
    ) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        List<StatusEffect> statuses = target.getNormalStatuses(0);
        Iterator<StatusEffect> i = statuses.iterator();
        while (i.hasNext()) {
          StatusEffect effect = (StatusEffect)i.next();
          if (!(effect instanceof StatChangeEffect)) continue;
          StatChangeEffect eff = (StatChangeEffect)effect;
          if (eff.getStat() == Pokemon.S_EVASION) {
            target.removeStatus(eff);
          }
        }
        target.getEvasion().setSecondaryMultiplier(1);
        return super.use(mech, user, target);
      }
    }
    ));

    m_moves.add(new MoveListEntry("Torment", new StatusMove(
        PokemonType.T_DARK, 0, 1.0, 15, new StatusEffect[] {
            new StatusEffect() {
              private MoveListEntry m_entry;
              public String getName() {
                return "Torment";
              }
              public String getDescription() {
                return " was subjected to Torment!";
              }
              public int getTier() {
                return -1;
              }
              public boolean tick(Pokemon p) {
                return false;
              }
              public boolean apply(Pokemon p) {
                m_entry = p.getLastMove();
                return super.apply(p);
              }
              public boolean isMoveTransformer(boolean enemy) {
                return !enemy;
              }
              public MoveListEntry getTransformedMove(Pokemon p, MoveListEntry entry) {
                if (entry.equals(m_entry)) {
                  p.getField().showMessage(p.getName() + " couldn't use the move after" +
                      " the torment!");
                  return null;
                }
                m_entry = entry;
                return entry;
              }
              public boolean vetoesMove(Pokemon p, MoveListEntry entry) {
                if (m_entry == null) {
                  return false;
                }
                return m_entry.equals(entry);
              }
            }
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Encore", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 5, new StatusEffect[] {
            new StatusEffect() {
              private int m_turns;
              private boolean m_transform = true;
              private MoveListEntry m_entry;
              public String getName() {
                return "Encore";
              }
              public String getDescription() {
                return " got an encore!";
              }
              public int getTier() {
                return 5;
              }
              public boolean hitsThroughSubstitute() {
                return true;
              }
              public boolean apply(Pokemon p) {
                MoveListEntry entry = p.getLastMove();
                if (entry == null) {
                  p.getField().showMessage("But it failed!");
                  return false;
                }
                m_entry = entry;
                m_turns = p.getField().getRandom().nextInt(5) + 5;
                return true;
              }

              public boolean tick(Pokemon p) {
                if (--m_turns <= 0) {
                  p.getField().showMessage(p.getName() + "'s encore ended.");
                  p.removeStatus(this);
                  return true;
                }
                return false;
              }
              public boolean isMoveTransformer(boolean enemy) {
                return !enemy;
              }
              public MoveListEntry getTransformedMove(Pokemon p, MoveListEntry entry) {
                if (m_transform) {
                  m_transform = false;
                  return m_entry;
                }
                return entry;
              }
              public boolean vetoesMove(Pokemon p, MoveListEntry entry) {
                return !entry.equals(m_entry);
              }
            }
        },
        new boolean[] { false },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Disable", new StatusMove(
        PokemonType.T_NORMAL, 0, 0.55, 20, new StatusEffect[] {
            new StatusEffect() {
              private int m_turns;
              private MoveListEntry m_entry;
              public String getName() {
                return "Disabled: " + m_entry.getName();
              }
              public String getDescription() {
                return " was disabled!";
              }
              public int getTier() {
                return 5;
              }
              public boolean apply(Pokemon p) {
                MoveListEntry entry = p.getLastMove();
                if (entry == null) {
                  p.getField().showMessage("But it failed!");
                  return false;
                }
                m_entry = entry;
                m_turns = p.getField().getRandom().nextInt(3) + 2;
                return true;
              }
              public boolean isMoveTransformer(boolean enemy) {
                return !enemy;
              }
              public MoveListEntry getEnemyTransformedMove(Pokemon p, MoveListEntry entry) {
                if (entry.equals(m_entry)) {
                  p.getField().showMessage(p.getName() + "'s " + entry.getName() +
                  " is disabled!");
                  return null;
                }
                return entry;
              }
              public boolean tick(Pokemon p) {
                if (--m_turns <= 0) {
                  p.removeStatus(this);
                  return true;
                }
                return false;
              }
              public boolean vetoesMove(Pokemon p, MoveListEntry entry) {
                return entry.equals(m_entry);
              }
              public boolean hitsThroughSubstitute() {
                return true;
              }
            }
        },
        new boolean[] { false },
        new double[] { 1.0 }
    ) {
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        setAccuracy(mech instanceof JewelMechanics ? 80 : 55);
        return super.attemptHit(mech, user, target);
      }
    }
    ));

    m_moves.add(new MoveListEntry("Imprison", new StatusMove(
        PokemonType.T_PSYCHIC, 0, 1.0, 15, new StatusEffect[] {
            new StatusEffect() {
              public String getName() {
                return "Imprison";
              }
              public String getDescription() {
                return "'s moves were sealed!";
              }
              public int getTier() {
                return -1;
              }
              public boolean tick(Pokemon p) {
                return false;
              }
              public boolean vetoesMove(Pokemon p, MoveListEntry entry) {
                Pokemon target = p.getOpponent();
                for (int i = 0; i < 4; ++i) {
                  MoveListEntry move = target.getMove(i);
                  if ((move != null) && move.equals(entry)) {
                    return true;
                  }
                }
                return false;
              }
              public boolean hitsThroughSubstitute() {
                return true;
              }
            }
        },
        new boolean[] { false },
        new double[] { 1.0 }
    ) {
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        return true;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Stockpile", new PokemonMove(
        PokemonType.T_NORMAL, 0, 1.0, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        StockpileEffect eff = (StockpileEffect)(user.getEffect(StockpileEffect.class));
        if (eff == null) {
          eff = (StockpileEffect)user.addStatus(user, new StockpileEffect());
        }
        eff.incrementLevel(user);
        return 0;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Spit Up",
        new StockpileMove(PokemonType.T_NORMAL, 0, 1.0, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        int levels = getLevels(user);
        if (levels <= 0) {
          user.getField().showMessage("But it failed to spit up anything!");
          return 0;
        }
        setPower(100 * levels);
        int damage = super.use(mech, user, target);
        setPower(0);
        user.removeStatus(getStockpileEffect(user));
        return damage;
      }
      public boolean isAttack() {
        return true;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Swallow",
        new StockpileMove(PokemonType.T_NORMAL, 0, 1.0, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        int levels = getLevels(user);
        if (levels <= 0) {
          user.getField().showMessage("But it failed to swallow anything!");
          return 0;
        }
        double[] percents = new double[] {0.25, 0.5, 1.0};
        double percent = percents[levels];

        user.addStatus(user, new PercentEffect(percent, false, -1, null));
        user.removeStatus(getStockpileEffect(user));
        return 0;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Tailwind",
        new PokemonMove(PokemonType.T_FLYING, 0, 1.0, 30) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        user.getField().applyEffect(new TailwindEffect(user.getParty()));
        return 0;
      }
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        return true;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Bide", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 10, new StatusEffect[] {
            new StatusEffect() {
              private int m_damage = 0;
              private int m_turns = 2;
              public String getName() {
                return "Bide";
              }
              public String getDescription() {
                return " is storing energy!";
              }
              public int getTier() {
                return -1;
              }
              public boolean deactivates(Pokemon p) {
                return true;
              }
              public boolean isListener() {
                return true;
              }
              public void informDamaged(Pokemon source, Pokemon target, MoveListEntry move, int damage) {
                m_damage += damage;
              }
              public boolean immobilises(Pokemon p) {
                if (--m_turns <= 0) {
                  p.getField().showMessage(p.getName() + " unleashed energy!");
                  p.useMove(new PokemonMove(PokemonType.T_TYPELESS, 0, 1.0, 1) {
                    public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
                      int change = 2 * m_damage;
                      target.changeHealth(-change);
                      return change;
                    }
                  }, p.getOpponent());
                  p.removeStatus(this);
                }
                return true;
              }
            }
        },
        new boolean[] { true },
        new double[] { 1.0 }
    ) {
      public int getPriority() {
        // todo: This should also only be in D/P.
        return 1;
      }
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        return true;
      }
    }
    ));

    class MimicEffect extends StatusEffect {
      private MoveListEntry m_entry;
      public MimicEffect(MoveListEntry entry) {
        m_entry = entry;
      }
      public String getName() {
        return "Mimicking " + m_entry.getName();
      }
      public String getDescription() {
        return " mimicked the foe's move!";
      }
      public boolean isMoveTransformer(boolean enemy) {
        return !enemy;
      }
      public MoveListEntry getTransformedMove(Pokemon p, MoveListEntry entry) {
        if (entry.getName().equals("Mimic")) {
          return m_entry;
        }
        return entry;
      }
    }

    m_moves.add(new MoveListEntry("Mimic",
        new PokemonMove(PokemonType.T_NORMAL, 0, 1.0, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        MoveListEntry move = target.getLastMove();
        if (move == null) {
          user.getField().showMessage("But it failed!");
          return 0;
        }
        user.addStatus(user, new MimicEffect(move));
        return 0;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Mist",
        new PokemonMove(PokemonType.T_ICE, 0, 1.0, 30) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        StatusEffect eff = user.getField().getEffectByType(MistEffect.class);
        if (eff == null) {
          user.getField().applyEffect(new MistEffect());
        }
        eff = user.getField().getEffectByType(MistEffect.class);
        if (eff != null) {
          MistEffect effect = (MistEffect)eff;
          effect.activateParty(user);
        }
        return 0;
      }
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        return true;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Safeguard",
        new PokemonMove(PokemonType.T_NORMAL, 0, 1.0, 25) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        StatusEffect eff = user.getField().getEffectByType(SafeguardEffect.class);
        if (eff == null) {
          user.getField().applyEffect(new SafeguardEffect());
        }
        eff = user.getField().getEffectByType(SafeguardEffect.class);
        if (eff != null) {
          SafeguardEffect effect = (SafeguardEffect)eff;
          effect.activateParty(user);
        }
        return 0;
      }
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        return true;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Gastro Acid",
        new PokemonMove(PokemonType.T_POISON, 0, 1.0, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (target.hasSubstitute()) {
          user.getField().showMessage("But it failed!");
          return 0;
        }
        IntrinsicAbility ability = target.getAbility();
        if ((ability != null) && ability.isActive()) {
          ability.unapply(target);
          ability.deactivate();
        }
        user.getField().showMessage(target.getName() + "'s ability was nullified.");
        target.addStatus(user, new StatusEffect() {
          public String getName() {
            return "Gastro Acid";
          }
        });
        return 0;
      }
    }));

    m_moves.add(new MoveListEntry("Gravity",
        new PokemonMove(PokemonType.T_PSYCHIC, 0, 1.0, 5) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        user.getField().applyEffect(new FieldEffect() {
          private int m_turns = 5;
          public int getTier() {
            return 1;
          }
          public boolean applyToField(BattleField field) {
            // Completely made up message.
            field.showMessage("Gravity intensified!");
            return true;
          }
          public boolean apply(Pokemon p) {
            if (p.hasAbility("Levitate")) {
              p.getAbility().deactivate();
            }
            BattleField field = p.getField();
            field.setNarrationEnabled(false);
            p.removeStatus(MagnetRiseEffect.class);
            field.setNarrationEnabled(true);
            p.getMultiplier(Pokemon.S_ACCURACY).multiplyBy(1.7);
            return true;
          }
          public void unapply(Pokemon p) {
            /** This will interfere with mold breaker and gastro acid,
             *  but we will address that later. */
            IntrinsicAbility ability = p.getAbility();
            if ((ability != null) && ability.getName().equals("Levitate")) {
              ability.activate();
            }
            p.getMultiplier(Pokemon.S_ACCURACY).divideBy(1.7);
          }
          public boolean tickPokemon(Pokemon p) {
            return false;
          }
          public boolean isEffectivenessTransformer(boolean enemy) {
            return !enemy;
          }
          public double getTransformedEffectiveness(PokemonType move, PokemonType defender) {
            if (PokemonType.T_GROUND.equals(move) && PokemonType.T_FLYING.equals(defender)) {
              // Ground is neutral aganist flying under the effects of gravity.
              return 1.0;
            }
            return super.getTransformedEffectiveness(move, defender);
          }
          public boolean isMoveTransformer(boolean enemy) {
            return !enemy;
          }
          public boolean vetoesMove(Pokemon p, MoveListEntry entry) {
            return !canUseMove(entry.getName());
          }
          private boolean canUseMove(String move) {
            // Moves that involve going up into the air are forbidden.
            Set<String> forbidden = new HashSet<String>(Arrays.asList(new String[] {
                "Fly", "Bounce", "Hi Jump Kick", "Jump Kick", "Magnet Rise"
            }));
            return !forbidden.contains(move);
          }
          public MoveListEntry getTransformedMove(Pokemon p, MoveListEntry entry) {
            String name = entry.getName();
            if (!canUseMove(name)) {
              BattleField field = p.getField();
              field.showMessage(p.getName() + " can't use " + name + " because of the Gravity.");
              return null;
            }
            return entry;
          }

          public boolean tickField(BattleField field) {
            if (--m_turns == 0) {
              field.removeEffect(this);
              return true;
            }
            return false;
          }
          public String getName() {
            return "Gravity";
          }
          public void unapplyToField(BattleField field) {
            field.showMessage("Gravity returned to normal!");
          }
        });
        return 0;
      }
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        return true;
      }
    }));

    m_moves.add(new MoveListEntry("Embargo",
        new PokemonMove(PokemonType.T_DARK, 0, 1.0, 15) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (target.hasSubstitute()) {
          user.getField().showMessage("But it failed!");
          return 0;
        }
        target.addStatus(user, new StatusEffect() {
          public boolean apply(Pokemon p) {
            HoldItem item = p.getItem();
            if ((item != null) && item.isActive()) {
              item.unapply(p);
              item.deactivate();
            }
            return true;
          }
          public String getDescription() {
            return "'s item was nullified.";
          }
          public void unapply(Pokemon p) {
            HoldItem item = p.getItem();
            if ((item != null) && !item.isRemovable()) {
              item.activate();
              item.apply(p);
            }
          }
          public String getName() {
            return "Embargo";
          }
        });
        return 0;
      }
    }));

    class SnatchEffect extends StatusEffect {
      private StatusEffect[] m_effects;
      public SnatchEffect(StatusEffect[] effects) {
        m_effects = effects;
      }
      public String getName() {
        return "Snatch";
      }
      public String getDescription() {
        return null;
      }
      public boolean tick(Pokemon p) {
        p.removeStatus(this);
        return true;
      }
      public boolean isMoveTransformer(boolean enemy) {
        return enemy;
      }
      public MoveListEntry getEnemyTransformedMove(Pokemon p, MoveListEntry entry) {
        return null;
      }
      public StatusEffect[] getEffects() {
        return m_effects;
      }
    }

    m_moves.add(new MoveListEntry("Snatch",
        new PokemonMove(PokemonType.T_DARK, 0, 1.0, 10) {
      @SuppressWarnings("unused")
      public void beginTurn(BattleTurn[] turn, Pokemon p, int index) {
        if (p.hasEffect(SleepEffect.class) || p.hasEffect(FreezeEffect.class)) {
          return;
        }
        BattleTurn opp = turn[1 - index];
        if (!opp.isMoveTurn()) return;
        MoveListEntry entry = p.getOpponent().getMove(opp.getId());
        PokemonMove move = entry.getMove();
        if (!(move instanceof StatusMove)) return;
        StatusMove statusMove = (StatusMove)move;
        if (statusMove.isAttack()) return;
        StatusEffect[] effects = statusMove.getEffects();
        p.addStatus(p, new SnatchEffect(effects));
      }
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        user.getField().showMessage(user.getName() + " awaits the foe's move!");
        SnatchEffect eff = (SnatchEffect)user.getEffect(SnatchEffect.class);
        if (eff == null) {
          return 0;
        }
        StatusEffect[] effects = eff.getEffects();
        if (effects.length > 0) {
          user.getField().showMessage(user.getName() + " snatched the foe's effects!");
          for (int i = 0; i < effects.length; ++i) {
            user.addStatus(user, effects[i]);
          }
        }
        return 0;
      }
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        return true;
      }
      public int getPriority() {
        return 3;
      }
    }
    ));

    PokemonMove lockOn = new PokemonMove(PokemonType.T_NORMAL, 0, 1.0, 5) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        user.addStatus(user, new LockOnEffect());
        return 0;
      }
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        return true;
      }
    };
    m_moves.add(new MoveListEntry("Lock-on", (PokemonMove)lockOn.clone()));
    m_moves.add(new MoveListEntry("Mind Reader", (PokemonMove)lockOn.clone()));

    m_moves.add(new MoveListEntry("Conversion 2", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 30, new StatusEffect[] {
            new StatusEffect() {
              private PokemonType[] m_types;

              public String getName() {
                return "Conversion 2";
              }
              public String getDescription() {
                return null;
              }
              public int getTier() {
                return -1;
              }
              public boolean tick(Pokemon p) {
                return false;
              }
              public boolean apply(Pokemon p) {
                m_types = p.getTypes();
                MoveListEntry entry = p.getOpponent().getLastMove();
                if (entry == null) {
                  p.getField().showMessage("But it failed!");
                  return false;
                }
                PokemonType moveType = entry.getMove().getType();
                ArrayList<PokemonType> types = new ArrayList<PokemonType>(Arrays.asList(PokemonType.getTypes()));
                Iterator<PokemonType> i = types.iterator();
                while (i.hasNext()) {
                  PokemonType type = (PokemonType)i.next();
                  if (moveType.getMultiplier(type) >= 1) {
                    i.remove();
                  }
                }
                int random = p.getField().getRandom().nextInt(types.size());
                p.setType(new PokemonType[] { (PokemonType)types.get(random) });
                return true;                           
              }
              public void unapply(Pokemon p) {
                p.setType(m_types);
              }
              public boolean switchOut(Pokemon p) {
                p.setType(m_types);
                return true;
              }
            }
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Rage", new StatusMove(
        PokemonType.T_NORMAL, 20, 1.0, 20, new StatusEffect[] {
            new StatusEffect() {                       
              public String getName() {
                return "Rage";
              }
              public String getDescription() {
                return " went into a rage!";
              }
              public int getTier() {
                return -1;
              }
              public boolean tick(Pokemon p) {
                return false;
              }
              public void executeTurn(Pokemon p, BattleTurn turn) {
                p.removeStatus(this);
              }
              public boolean isListener() {
                return true;
              }
              public void informDamaged(Pokemon source, Pokemon target, MoveListEntry move, int damage) {
                target.getField().showMessage(target.getName() + " is angry!");
                target.addStatus(target, new StatChangeEffect(Pokemon.S_ATTACK, true));
              }
            }
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    class FuryCutterEffect extends StatusEffect {
      public int m_turns = 0;
      public String getName() {
        return "Fury cutter";
      }
      public String getDescription() {
        return null;
      }
      public int getTier() {
        return -1;
      }
      public boolean tick(Pokemon p) {
        m_turns++;
        return false;
      }
      public void executeTurn(Pokemon p, BattleTurn turn) {
        if (p.getField().getMechanics() instanceof JewelMechanics) {
          return;
        }
        if (!p.getMove(turn.getId()).getName().equals("Fury Cutter")) {
          p.removeStatus(this);
        }
      }
      public boolean isMoveTransformer(boolean enemy) {
        return !enemy;
      }
      public MoveListEntry getTransformedMove(Pokemon p, MoveListEntry entry) {
        if (!entry.getName().equals("Fury Cuttter")) return entry;
        PokemonMove move = entry.getMove();
        int power = move.getPower() * (1 << m_turns);
        if (power > 160) power = 160;
        move.setPower(power);
        return entry;
      }
      public boolean isSingleton() {
        return true;
      }
      public void informDuplicateEffect(Pokemon p) {
      }
    }

    m_moves.add(new MoveListEntry("Fury Cutter", new StatusMove(
        PokemonType.T_BUG, 10, 0.95, 20, new StatusEffect[] {
            new FuryCutterEffect()
        },
        new boolean[] { true },
        new double[] { 1.0 }
    ) {
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        return true;
      }
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        if (!super.attemptHit(mech, user, target)) {
          user.removeStatus(FuryCutterEffect.class);
        }
        return super.use(mech, user, target);
      }
    }
    ));

    m_moves.add(new MoveListEntry("Power Trick", new StatusMove(
        PokemonType.T_PSYCHIC, 0, 1.0, 10, new StatusEffect[] {
            new StatusEffect() {                       
              private int m_attack;
              private int m_defence;
              public String getName() {
                return "Power Trick";
              }
              public String getDescription() {
                return " swapped its stats!";
              }
              public int getTier() {
                return -1;
              }
              public boolean tick(Pokemon p) {
                return false;
              }
              public boolean apply(Pokemon p) {
                m_attack = p.getRawStat(Pokemon.S_ATTACK);
                m_defence = p.getRawStat(Pokemon.S_DEFENCE);
                p.setRawStat(Pokemon.S_ATTACK, m_defence);
                p.setRawStat(Pokemon.S_DEFENCE, m_attack);
                return super.apply(p);
              }
              public void unapply(Pokemon p) {
                p.setRawStat(Pokemon.S_ATTACK, m_attack);
                p.setRawStat(Pokemon.S_DEFENCE, m_defence);
              }
            }
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Uproar", new FixedAttackMove(
        PokemonType.T_NORMAL, 50, 1.0, 20, new FixedAttackEffect("Uproar",
            " caused an uproar!", " calmed down."))));

    m_moves.add(new MoveListEntry("Rollout", new FixedAttackMove(
        PokemonType.T_ROCK, 30, 0.9, 20, new RolloutEffect("Rollout"))));

    m_moves.add(new MoveListEntry("Ice Ball", new FixedAttackMove(
        PokemonType.T_ICE, 30, 0.9, 20, new RolloutEffect("Ice Ball"))));

    /** If you had bothered to read the Smogon analysis, you would have learned that
     *  this is actually nearly a clone of Lunar Dance (except that it does not heal
     *  PP); it is not at all like Destiny Bond. Fortunately the move is rare
     *  enough that it has yet to matter.
     */
    m_moves.add(new MoveListEntry("Healing Wish",
        new PokemonMove(PokemonType.T_PSYCHIC, 0, 1.0, 10) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        int party = user.getParty();
        BattleField field = user.getField();
        if (field.getAliveCount(party) == 1) {
          user.getField().showMessage("But it failed!");
          return 0;
        }
        user.faint();
        field.requestAndWaitForSwitch(party);
        target = field.getActivePokemon()[party];
        target.changeHealth(target.getStat(Pokemon.S_HP));
        target.removeStatus(StatusEffect.SPECIAL_EFFECT_LOCK);
        return 0;
      }
      public boolean attemptHit(BattleMechanics mech, Pokemon source, Pokemon target) {
        return true;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Bug Bite",
        new PokemonMove(PokemonType.T_BUG, 60, 1.0, 20) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        HoldItem item = target.getItem();
        if (item instanceof Berry) {
          Berry berry = (Berry)item;
          berry.executeEffects(user);
          target.setItem(null);
        }
        return super.use(mech, user, target);
      }
    }
    ));

    m_moves.add(new MoveListEntry("Pluck",
        new PokemonMove(PokemonType.T_FLYING, 60, 1.0, 20) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        int power = getPower();
        HoldItem item = target.getItem();
        if (item instanceof Berry) {
          setPower(power * 2);
        }
        int damage = super.use(mech, user, target);
        setPower(power);
        return damage;
      }
    }
    ));
    m_moves.add(new MoveListEntry("Lucky Chant",
        new PokemonMove(PokemonType.T_NORMAL, 0, 1.0, 30) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        FieldEffect effect = user.getField().getEffectByType(LuckyChantEffect.class);
        if (effect == null) {
          user.getField().applyEffect(
              new LuckyChantEffect(" is feeling lucky!", "'s luck wore off..."));
        }
        effect = user.getField().getEffectByType(LuckyChantEffect.class);
        LuckyChantEffect eff = (LuckyChantEffect)effect;
        eff.activateParty(user);
        return 0;
      }
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        return true;
      }
    }
    ));

    m_moves.add(new MoveListEntry("Heal Block",
        new PokemonMove(PokemonType.T_PSYCHIC, 0, 1.0, 15) {
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        target.addStatus(user, new HealBlockEffect());
        return 0;
      }
      public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
        return true;
      }
    }
    ));
    m_moves.add(new MoveListEntry("Focus Energy", new StatusMove(
        PokemonType.T_NORMAL, 0, 1.0, 30, new StatusEffect[] {
            new StatusEffect() {
              public String getName() {
                return "Focus energy";
              }
              public String getDescription() {
                return " tightened its focus!";
              }
              public boolean switchOut(Pokemon p) {
                return true;
              }
            }
        },
        new boolean[] { true },
        new double[] { 1.0 }
    )));

    m_moves.add(new MoveListEntry("Magic Coat", new PokemonMove(
        PokemonType.T_PSYCHIC, 0, 1.0, 15) {
      public int getPriority() {
        return 5;
      }
      public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
        user.getField().showMessage(user.getName() + " created a barrier!");
        target.addStatus(user, new StatusEffect() {
          public boolean isMoveTransformer(boolean enemy) {
            return !enemy;
          }
          public MoveListEntry getTransformedMove(Pokemon p, MoveListEntry entry) {
            PokemonMove move = entry.getMove();
            if (!(move instanceof StatusMove)) return entry;
            StatusMove statusMove = (StatusMove)move;
            if (!statusMove.isAttack() || statusMove.isDamaging()) {
              return entry;
            }
            int max = statusMove.getEffects().length;
            for (int i = 0; i < max; ++i) {
              statusMove.setAttacker(i, true);
            }
            return entry;
          }
          public int getTier() {
            return 5;
          }
          public boolean tick(Pokemon p) {
            p.removeStatus(this);
            return true;
          }
        });
        return 0;
      }
    }
    ));


  }
  /**
   * An effect that is applied to a team for 5 turns.
   */
  public abstract static class PartyEffect extends FieldEffect {
    private int[] m_turns = { 5, 5 };
    private boolean[] m_active = { false, false };
    private String m_endMessage;
    private String m_startMessage;

    public PartyEffect(String start, String end) {
      m_startMessage = start;
      m_endMessage = end;
    }
    public boolean applyToField(BattleField field) {
      return true;
    }
    public int getTier() {
      return 0;
    }
    public boolean tickField(BattleField field) {
      boolean stillActive = false;
      for (int i = 0; i < m_turns.length; ++i) {
        if (!m_active[i]) continue;
        if (--m_turns[i] <= 0) {
          if (m_endMessage != null) {
            field.showMessage(
                field.getActivePokemon()[i].getName() + m_endMessage);
          }
          m_active[i] = false;
        }
        if (m_active[i]) {
          stillActive = true;
        }
      }
      if (!stillActive) {
        field.removeEffect(this);
        return true;
      }
      return false;
    }
    public boolean isSingleton() {
      return true;
    }
    public void activateParty(Pokemon p) {
      int party = p.getParty();
      BattleField field = p.getField();

      if (m_active[party]) {
        informDuplicateEffect(p);
        return;
      }
      m_active[party] = true;
      m_turns[party] = 5;
      if (m_startMessage != null) {
        field.showMessage(
            field.getActivePokemon()[party].getName() + m_startMessage);
      }
    }
    public boolean isActive(int party) {
      if ((party < 0) || (party > m_active.length)) return false;
      return m_active[party];
    }
  }
  /**
   * The party cannot be hit by critical hits for 5 turns.
   */
  public static class LuckyChantEffect extends PartyEffect {
    public LuckyChantEffect(String start, String end) {
      super(start, end);
    }
    public String getName() {
      return "Lucky chant";
    }
  }
  /**
   * Prevents the affected pokemon from using recovery moves for 5 turns.
   */
  public static class HealBlockEffect extends StatusEffect {
    private int m_turns = 5;
    public String getName() {
      return "Heal block";
    }
    public String getDescription() {
      return " was prevented from healing!";
    }
    public boolean isVetoed(MoveListEntry entry) {
      String name= entry.getName();
      if (name.equals("Rest") || name.equals("Wish")) return true;
      PokemonMove move = entry.getMove();
      if (!(move instanceof StatusMove)) return false;
      StatusMove statusMove = (StatusMove)move;
      StatusEffect[] effects = statusMove.getEffects();
      StatusEffect eff = null;
      for (int i = 0; i < effects.length; ++i) {
        if (effects[i] instanceof PercentEffect) {
          eff = effects[i];
          PercentEffect effect = (PercentEffect)eff;
          if ((effect.getTier() == -1) && (effect.getPercent() > 0)) {
            return true;
          }
        }
      }
      return false;
    }
    public boolean isMoveTransformer(boolean enemy) {
      return enemy;
    }
    public MoveListEntry getEnemyTransformedMove(Pokemon p, MoveListEntry entry) {
      if (isVetoed(entry)) {
        p.getField().showMessage(p.getName() + " can't use " + entry.getName() +
        " because of Heal Block!");
        return null;
      }
      return entry;
    }
    //todo: find actual tier
    public int getTier() {
      return 3;
    }
    public boolean tick(Pokemon p) {
      if (m_turns-- <= 0) {
        p.removeStatus(this);
        p.getField().showMessage(p.getName() + "'s Heal Block wore off!");
        return true;
      }
      return false;
    }
    public boolean vetoesMove(Pokemon p, MoveListEntry entry) {
      return isVetoed(entry);
    }
    public void informDuplicateEffect(Pokemon p) {
      p.getField().showMessage("It failed to affect " + p.getName());
    }
  }

  /**
   * A move that prevents the user from using other moves until the move misses
   * or the number of turns is reached.
   */
  public static class FixedAttackMove extends StatusMove {
    public FixedAttackMove(PokemonType type, int power, double accuracy, int pp, FixedAttackEffect eff) {
      super(type, power, accuracy, pp, new StatusEffect[] { eff },
          new boolean[] { true }, new double[] { 1.0 });
    }
    public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
      return true;
    }
    public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
      if (super.attemptHit(mech, user, target)) {
        return super.use(mech, user, target);
      }
      user.removeStatus(FixedAttackEffect.class);
      return 0;
    }
  }

  /**
   * A status that forces the user to continue to use the same move.
   */
  public static class FixedAttackEffect extends StatusEffect {
    protected int m_turns;
    private int m_pp;
    private String m_name;
    private String m_description;
    private String m_message;

    public FixedAttackEffect(String name, String description, String message) {
      m_name = name;
      m_description = description;           
      m_message = message;
    }
    public String getName() {
      return m_name;
    }
    public String getDescription() {
      return m_description;
    }
    public int getTier() {
      return 3;
    }
    private int getMoveIndex(Pokemon p) {
      for (int i = 0; i < 4; ++i) {
        MoveListEntry move = p.getMove(i);
        if (move == null)
          continue;
        if (move.getName().equals(getName())) {
          return i;
        }
      }
      return -1;
    }
    public boolean apply(Pokemon p) {
      m_turns = p.getField().getRandom().nextInt(4) + 2;
      int idx = getMoveIndex(p);
      m_pp = p.getPp(idx);
      return super.apply(p);
    }
    public boolean tick(Pokemon p) {
      int idx = getMoveIndex(p);
      p.setPp(idx, m_pp);
      if (m_turns-- <= 0) {
        if (m_message != null) {
          p.getField().showMessage(p.getName() + m_message);
        }
        p.removeStatus(this);
        return true;
      }
      return false;                           
    }
    public boolean canSwitch(Pokemon p) {
      return false;
    }
    public boolean vetoesMove(Pokemon p, MoveListEntry entry) {
      return !entry.getName().equals(getName());
    }
    public boolean isSingleton() {
      return true;
    }
    public void informDuplicateEffect(Pokemon p) {
    }
    public void executeTurn(Pokemon p, BattleTurn turn) {
      m_pp = p.getPp(turn.getId());
    }
  }

  private static class RolloutEffect extends FixedAttackEffect {
    public RolloutEffect(String name) {
      super(name, null, null);
    }
    public boolean apply(Pokemon p) {
      super.apply(p);
      m_turns = 5;
      return true;
    }
    public boolean isMoveTransformer(boolean enemy) {
      return !enemy;
    }
    public MoveListEntry getTransformedMove(Pokemon p, MoveListEntry move) {
      move.getMove().setPower(move.getMove().getPower() * (1 << (5 - m_turns)));
      return move;
    }
  }

  /**
   * Ensures that the next move will hit unless the opponenet has an ISE.
   * The logic is located in AdvanceMechanics.
   */   
  public static class LockOnEffect extends StatusEffect {
    @SuppressWarnings("unused")
    private int m_turns = 1;
    public String getName() {
      return "Locked-on";
    }
    public String getDescription() {
      return " took aim at the foe!";
    }
    public int getTier() {
      return 0;
    }
    public boolean isMoveTransformer(boolean enemy) {
      return !enemy;
    }
    public MoveListEntry getTransformedMove(Pokemon p, MoveListEntry entry) {
      if (entry.getMove().isAttack()) {
        p.removeStatus(this);
      }
      return entry;
    }
  }
  /**
   * A move that attacks for 2-3 turns and then confuses.
   */
  private static class RampageMove extends PokemonMove {
    public RampageMove(PokemonType type, int power, double accuracy, int pp) {
      super(type, power, accuracy, pp);
    }
    public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
      user.addStatus(user, new StatusEffect() {
        private int m_turns = 0;
        public boolean apply(Pokemon p) {
          m_turns = 2 + p.getField().getMechanics().getRandom().nextInt(2);
          return true;
        }
        public String getName() {
          return getMoveListEntry().getName();
        }
        public String getDescription() {
          return " went on a rampage!";
        }
        public int getTier() {
          return 1;
        }
        public boolean canSwitch(Pokemon p) {
          return false;
        }
        public boolean vetoesMove(Pokemon p, MoveListEntry move) {
          return !getMoveListEntry().equals(move);
        }
        public boolean tick(Pokemon p) {
          if (--m_turns == 0) {
            p.getField().showMessage(p.getName() + "'s rampage ended.");
            p.removeStatus(this);
            p.addStatus(p, new ConfuseEffect());
          }
          return true;
        }
      });
      return super.use(mech, user, target);
    }
  }

  /**
   * A move that guards against an enemy attack.
   */
  private static class ProtectMove extends StatusMove {
    private double m_failure = 0.0;
    public ProtectMove(PokemonType type, int pp, DelegationEffect effect) {
      super(type, 0, 1.0, pp, new StatusEffect[] {
          effect,
          new CounterEffect()
      },
      new boolean[] { true, true },
      new double[] { 1.0, 1.0 }
      );
    }
    public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
      if (mech.getRandom().nextDouble() < m_failure) {
        user.getField().showMessage("But it failed!");
        user.removeStatus(CounterEffect.class);
        return 0;
      }
      return super.use(mech, user, target);
    }
    public void setFailure(double failure) {
      m_failure = failure;
    }
    public int getPriority() {
      return 2;
    }
    public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
      return true;
    }
  }

  /**
   * Makes pokemon immune to ground attacks for five turns.
   */
  static class MagnetRiseEffect extends StatusEffect {
    private int m_turns = 5;
    public String getDescription() {
      return " became immune to ground attacks!";
    }
    public String getName() {
      return "Magnet Rise";
    }
    public boolean tick(Pokemon p) {
      if (--m_turns == 0) {
        p.removeStatus(this);
        p.getField().showMessage(p.getName()
            + " is no longer immune to ground attacks!");
        return true;
      }
      return false;
    }
    public int getTier() {
      // Does not really matter.
      return 1;
    }
    public boolean isEffectivenessTransformer(boolean enemy) {
      return enemy;
    }
    public double getEnemyTransformedEffectiveness(
        PokemonType move, PokemonType target) {
      if (move.equals(PokemonType.T_GROUND)) {
        return 0.0;
      }
      return super.getEnemyTransformedEffectiveness(move, target);
    }
  }

  /**
   * A move with a high chance of striking critical.
   */
  static class HighCriticalHitMove extends PokemonMove {
    public HighCriticalHitMove(PokemonType type, int power, double accuracy, int pp) {
      super(type, power, accuracy, pp);
    }
    public boolean hasHighCriticalHitRate() {
      return true;
    }
  }

  /**
   * A move that always hits unless the opponent is underground or flying.
   */
  public static class PerfectAccuracyMove extends PokemonMove {
    public PerfectAccuracyMove(PokemonType type, int power, int pp) {
      super(type, power, 0, pp);
    }
    public static boolean isHit(BattleMechanics mech, Pokemon user, Pokemon target) {
      return !target.hasEffect(InvulnerableStateEffect.class);
    }
    public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
      return isHit(mech, user, target);
    }
  }

  private static class JumpKickMove extends PokemonMove {
    private int m_jewelPower, m_power;
    private static class CalculationEffect extends StatusEffect {
      public boolean isEffectivenessTransformer(boolean enemy) {
        return !enemy;
      }
      public double getTransformedEffectiveness(PokemonType move, PokemonType defender) {
        double expected = super.getTransformedEffectiveness(move, defender);
        if (expected == 0.0)
          return 1.0;
        return expected;
      }
    }
    public JumpKickMove(PokemonType type, int power,int jewelPower,
        double accuracy, int pp) {
      super(type, power, accuracy, pp);
      m_jewelPower = jewelPower;
      m_power = power;
    }
    public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
      return true;
    }
    public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
      setPower((mech instanceof JewelMechanics) ? m_jewelPower : m_power);
      boolean ineffective = (getEffectiveness(user, target) == 0.0);
      boolean protect = target.hasEffect(ProtectEffect.class);
      if (ineffective) {
        user.addStatus(user, new CalculationEffect());
      }
      int damage = mech.calculateDamage(this, user, target, protect);
      if (ineffective) {
        user.removeStatus(CalculationEffect.class);
      }
      BattleField field = user.getField();
      if (!ineffective && !protect
          && super.attemptHit(mech, user, target)) {
        target.changeHealth(-damage);
        return damage;
      } else if (protect) {
        StatusEffect eff = target.getEffect(ProtectEffect.class);
        field.showMessage(target.getName() + eff.getDescription());
      }
      field.showMessage(user.getName() + " kept going and crashed!");
      int recoil = (int)((double)damage / 2.0);
      int max = target.getStat(Pokemon.S_HP) / 2;
      if (recoil > max) {
        recoil = max;
      }
      user.changeHealth(-recoil);
      return damage;
    }
  }

  /**
   * A move that kills the target in one hit.
   */
   public static class OneHitKillMove extends PokemonMove {
    public OneHitKillMove(PokemonType type, int pp) {
      super(type, 0, 0, pp);
    }
    public boolean isAttack() {
      return true;
    }
    public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
      if (target.hasEffect(InvulnerableStateEffect.class)) {
        return false;
      }
      double ratio = ((double)(user.getLevel() - target.getLevel())) / 128.0;
      if (ratio < 0) {
        return false;
      }
      setAccuracy(0.234 + ratio);
      return super.attemptHit(mech, user, target);
    }
    public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
      if (target.hasAbility("Sturdy")) {
        user.getField().showMessage(target.getName() + " held sturdy!");
      } else {
        user.getField().showMessage("It's a OHKO!");
        if (target.hasSubstitute()) {
          target.setSubstitute(1);
          target.changeHealth(-1);
        } else {
          target.faint();
        }
      }
      return 0;
    }
   }

   /**
    * A move with a modified priority.
    */
   static class PriorityMove extends PokemonMove {
     private int m_priority;
     public PriorityMove(PokemonType type, int power, double accuracy, int pp, int priority) {
       super(type, power, accuracy, pp);
       m_priority = priority;
     }
     public int getPriority() {
       return m_priority;
     }
   }

   /**
    * A move whos power is based on the mass of the opponent.
    */   
   private static class MassBasedMove extends PokemonMove {
     public MassBasedMove(PokemonType type, double accuracy, int pp) {
       super(type, 0, accuracy, pp);
     }
     public int use(BattleMechanics mech, Pokemon user, Pokemon target) {
       final double mass = PokemonSpecies.getDefaultData().getPokemonByName(target.getSpeciesName()).getWeight();
       if (mass <= 10.0) {
         setPower(20);
       } else if (mass <= 25.0) {
         setPower(40);
       } else if (mass <= 100.0) {
         setPower(80);
       } else if (mass <= 200.0) {
         setPower(100);
       } else {
         setPower(120);
       }
       int damage = super.use(mech, user, target);
       setPower(0);
       return damage;
     }
     public boolean isAttack() {
       return true;
     }
   }

   /**
    * Cuts the power of the opponent's moves of a certain type.
    */
   private static class TypeCutMove extends PokemonMove {      
     private PokemonType m_cut;
     private String m_name;

     public TypeCutMove(PokemonType type, int pp, PokemonType cut, String name) {
       super(type, 0, 1.0, pp);
       m_cut = cut;
       m_name = name;
     }

     public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
       return true;
     }

     public int use(BattleMechanics mech, Pokemon user, final Pokemon target) {
       final String name = m_name;
       BattleField field = user.getField();
       if (!field.applyEffect(new TypeCutStatus(m_cut, user) {
         public String getName() {
           return name;
         }
       })) {
         field.showMessage("But it failed!");
       }
       return 0;
     }
   }

   private static class CounterMove extends PokemonMove {      
     // 1 is special, 2 is physical, 3 is both
     private int m_special;
     public boolean isDamaging() {
       return true;
     }
     public boolean isAttack() {
       return true;
     }
     public CounterMove(PokemonType type, double accuracy, int pp, int special) {
       super(type, 0, accuracy, pp);
       m_special = special;
     }
     public int use(BattleMechanics mech, Pokemon user, final Pokemon target) {
       if (getEffectiveness(m_type, user, target) == 0.0) {
         user.getField().showMessage("It doesn't affect " + target.getName() + "...");
         return 0;
       }
       DamageListenerEffect listener = null;
       List<StatusEffect> effects = user.getNormalStatuses(0);
       Iterator<StatusEffect> i = effects.iterator();
       while (i.hasNext()) {
         StatusEffect eff = (StatusEffect)i.next();
         if (eff instanceof DamageListenerEffect) {
           listener = (DamageListenerEffect)eff;
           break;
         }
       }
       if (listener == null) {
         user.getField().showMessage("But it failed!");
         return 0;
       }
       int damage = listener.getDamage() * 2;
       boolean special = listener.isSpecial();
       boolean good = (m_special == 3 || (m_special == 1 && special) || (m_special == 2 && !special));
       if (damage <= 0 || !good) {
         user.getField().showMessage("But it failed!");
         return 0;
       }
       target.changeHealth(-damage);
       return damage;
     }
     public int getPriority() {
       return -4;
     }
     public void beginTurn(BattleTurn[] turn, int index, Pokemon source) {
       source.addStatus(source, new DamageListenerEffect());
     }
   }

   /**
    * A move that swaps the StatChangeEffects of certain stats between two Pokemon.
    */

   private static class StatChangeSwapMove extends PokemonMove {
     private int[] m_stats;

     public StatChangeSwapMove(PokemonType type, int pp, int[] stats) {
       super(type, 0, 1.0, pp);
       m_stats = stats;
     }
     public boolean attemptHit(BattleMechanics mech, Pokemon user, Pokemon target) {
       return true;
     }
     /**
      * Removes statuses from the list if they are not StatChangeEffects of the expected variety.
      */
     private List<StatusEffect> cleanList(List<StatusEffect> list) {
       Iterator<StatusEffect> i = list.iterator();
       while (i.hasNext()) {
         StatusEffect eff = (StatusEffect)i.next();
         if (eff == null) continue;
         if (!(eff instanceof StatChangeEffect)) {
           i.remove();
           continue;
         }
         StatChangeEffect effect = (StatChangeEffect)eff;
         int stat = effect.getStat();
         boolean good = false;
         for (int j = 0; j < m_stats.length; ++j) {
           if (stat == m_stats[j]) {
             good = true;
           }
         }
         if (!good) i.remove();
       }
       return list;
     }
     /**
      * Removes a list of effects from a Pokemon.
      */
     private void removeStatuses(Pokemon p, List<StatusEffect> effects) {
       Iterator<StatusEffect> i = effects.iterator();
       while (i.hasNext()) {
         StatChangeEffect eff = (StatChangeEffect)i.next();
         p.removeStatus(eff);
       }
     }
     /**
      * Applies a list of effects from a Pokemon.
      */
     private void addStatuses(Pokemon p, List<StatusEffect> effects) {
       Iterator<StatusEffect> i = effects.iterator();
       while (i.hasNext()) {
         StatChangeEffect eff = (StatChangeEffect)i.next();
         eff.setDescription(null);
         p.addStatus(p, eff);
       }
     }
     public int use(BattleMechanics mech, Pokemon user, final Pokemon target) {
       List<StatusEffect> userStatuses = cleanList(user.getNormalStatuses(0));
       List<StatusEffect> targetStatuses = cleanList(target.getNormalStatuses(0));
       removeStatuses(user, userStatuses);
       removeStatuses(target, targetStatuses);
       addStatuses(user, targetStatuses);
       addStatuses(target, userStatuses);
       user.getField().showMessage("The Pokemon swapped stats!");
       return 0;
     }
   }

   private static class DamageListenerMove extends PokemonMove {
     private int m_priority = 0;
     public DamageListenerMove(PokemonType type, int power, double accuracy, int pp) {
       super(type, power, accuracy, pp);
     }
     public void beginTurn(BattleTurn[] turn, int index, Pokemon source) {
       source.addStatus(source, new DamageListenerEffect());
     }
     public DamageListenerEffect getListener(Pokemon p) {
       DamageListenerEffect listener = null;
       List<StatusEffect> effects = p.getNormalStatuses(0);
       Iterator<StatusEffect> i = effects.iterator();
       while (i.hasNext()) {
         StatusEffect eff = (StatusEffect)i.next();
         if (eff instanceof DamageListenerEffect) {
           listener = (DamageListenerEffect)eff;
           break;
         }
       }
       return listener;
     }
     public int getPriority() {
       return m_priority;
     }
     public void setPriority(int priority) {
       m_priority = priority;
     }
   }

   private static class PerishSongEffect extends StatusEffect {
     private int m_turns = 3;
     private boolean m_soundImmune = true;
     public PerishSongEffect(boolean soundImmune) {
       m_soundImmune = soundImmune;
     }
     public String getDescription() {
       return null;
     }
     public boolean apply(Pokemon p) {
       // No need to reference Cacophony here.
       return !(m_soundImmune && p.hasAbility("Soundproof"));
     }
     public boolean tick(Pokemon p) {
       p.getField().showMessage(p.getName() + "'s perish count fell to " + m_turns + "!");
       if (m_turns-- == 0) {
         p.faint();
       }
       return true;
     }
     public int getTier() {
       return 5;
     }
     public boolean hitsThroughSubstitute() {
       return true;
     }
     public String getName() {
       return "Perish count";
     }
   }

   private abstract static class TypeCutStatus extends FieldEffect {

     private PokemonType m_type;
     private Pokemon m_user;

     public TypeCutStatus(PokemonType type, Pokemon user) {
       m_type = type;
       m_user = user;
     }

     public boolean switchOut(Pokemon p) {
       if (p == m_user) {
         p.getField().removeEffect(this);
       }
       return super.switchOut(p);
     }

     public boolean equals(Object obj) {
       if (!(obj instanceof TypeCutStatus)) {
         return false;
       }
       TypeCutStatus rhs = (TypeCutStatus)obj;
       return m_type.equals(rhs.m_type);
     }

     public boolean applyToField(BattleField field) {
       field.showMessage(m_type + " attacks were weakened!");
       return true;
     }

     public String getDescription() {
       return null;
     }

     public boolean tickField(BattleField field) {
       return false;
     }

     public int getTier() {
       return -1;
     }

     public boolean isMoveTransformer(boolean enemy) {
       return !enemy;
     }

     protected MoveListEntry getTransformedMove(Pokemon p, MoveListEntry entry) {
       PokemonMove move = entry.getMove();
       if (move.getType().equals(m_type)) {
         move.setPower(move.getPower() / 2);
       }
       return entry;
     }

   }

   public static class SubstituteEffect extends StatusEffect {
     public String getName() {
       return "Substitute";
     }
     public String getDescription() {
       return " made a substitute!";
     }
     public int getTier() {
       return -1;
     }
     public boolean tick(Pokemon p) {
       return false;
     }
     public boolean apply(Pokemon p) {
       p.getField().refreshActivePokemon();
       return true;
     }
     public void unapply(Pokemon p) {
       p.getField().refreshActivePokemon();
     }
     public boolean switchOut(Pokemon p) {
       p.setSubstitute(0);
       return super.switchOut(p);
     }
   }

   private static class InvulnerableStateEffect extends StatusEffect {
     @SuppressWarnings("unused")
     private int m_turns = 0;
     private String[] m_effectiveMoves;

     /**
      * Creates an effect that makes a Pokemon invulnerable, except against certain moves.
      * @param effectiveMoves moves that are doubly effective against a Pokemon in this state
      */
     public InvulnerableStateEffect(String[] effectiveMoves) {
       m_effectiveMoves = effectiveMoves;
     }
     public String getName() {
       return "Invulnerable state";
     }
     public String getDescription() {
       return null;
     }
     public boolean isMoveTransformer(boolean enemy) {
       return enemy;
     }
     protected MoveListEntry getEnemyTransformedMove(Pokemon p, MoveListEntry entry) {
       String moveName = entry.getName();
       PokemonMove move = entry.getMove();
       for (int i = 0; i < m_effectiveMoves.length; ++i) {
         if (moveName.equals(m_effectiveMoves[i])) {
           move.setPower(move.getPower() * 2);
           return entry;
         }
       }
       if (!p.hasAbility("No Guard") && !p.getOpponent().hasAbility("No Guard")) {
         move.setAccuracy(0);
       }
       return entry;
     }
     public int getTier() {
       return -1;
     }
     public boolean tick(Pokemon p) {
       return false;
     }
     public boolean immobilises(Pokemon poke) {
       poke.removeStatus(this);
       return false;
     }
   }

   static class LeechSeedEffect extends StatusEffect {
     private boolean m_grassImmune = true;
     public LeechSeedEffect(boolean grassImmune) {
       m_grassImmune = grassImmune;
     }
     public LeechSeedEffect() {

     }
     public String getName() {
       return "Leech seed";
     }
     public int getTier() {
       return 3;
     }
     public String getDescription() {
       return " was seeded!";
     }
     public boolean tick(Pokemon p) {
       final Pokemon opponent = p.getOpponent();
       if (p.isFainted() || opponent.isFainted()) return false;
       int damage = p.getStat(Pokemon.S_HP) / 8;
       if (damage == 0) {
         damage = 1;
       }
       if (!p.hasAbility("Magic Guard")) {
         p.getField().showMessage(p.getName() + "'s health was sapped by leech seed!");
         p.changeHealth(-damage, true);

         if (p.hasAbility("Liquid Ooze")) {
           p.getField().showMessage(opponent.getName() + " sucked up liquid ooze!");
           opponent.changeHealth(-damage);
         } else {
           p.getField().showMessage(opponent.getName() + " regained health!");
           opponent.changeHealth(damage);
         }
       }
       return false;
     }
     public boolean apply(Pokemon p) {
       if (m_grassImmune && p.isType(PokemonType.T_GRASS)) {
         return false;
       }
       return true;
     }
   }   

   //tbd - remove this effect if the user switches out
   static class RestrainingEffect extends StatusEffect {
     private int m_turns;
     private String m_description;
     private String m_name;

     public RestrainingEffect(String name, String description) {
       m_name = name;
       m_description = description;
     }
     public String getName() {
       return m_name;
     }
     public String getDescription() {
       return " was " + m_description + "!";
     }
     public int getTier() {
       return 3;
     }
     public boolean tick(Pokemon p) {
       if (m_turns-- <= 0) {
         p.getField().showMessage(p.getName() + " was released from " + m_name + "!");
         p.removeStatus(this);
         return false;
       }
       int maximum = p.getStat(Pokemon.S_HP);
       int damage = maximum / 16;
       if (damage == 0) damage = 1;
       p.getField().showMessage(p.getName() + " is hurt by " + m_name + "!");
       p.changeHealth(-damage);
       return true;
     }
     public boolean apply(Pokemon p) {
       if (p.hasEffect(RestrainingEffect.class)) {
         p.removeStatus(RestrainingEffect.class);
       }
       m_turns = p.getField().getRandom().nextInt(4) + 1;
       p.getOpponent().addStatus(p, new CoEffect(getClass()));
       return true;
     }
     public boolean canSwitch(Pokemon p) {
       return p.hasItem("Shed Shell");
     }
   }

   private static interface ProtectEffectEvent {
     public int use(
         PokemonMove move,
         BattleMechanics mech,
         Pokemon source, Pokemon target);
   }

   private abstract static class ProtectEffect extends DelegationEffect {
     public ProtectEffect() {
       m_event = new ProtectEffectEvent() {
         public int use(
             PokemonMove move,
             BattleMechanics mech,
             Pokemon source, Pokemon target) {
           source.getField().showMessage(target.getName()
               + ProtectEffect.this.getDescription());
           return 0;
         }
       };
     }
   }

   public static class EndureEffect extends DelegationEffect {
     public String getDescription() {
       return " became ready to endure!";
     }
     public boolean isMoveTransformer(boolean enemy) {
       return false;
     }
   }

   private abstract static class DelegationEffect extends StatusEffect {
     protected ProtectEffectEvent m_event;

     public String getName() {
       return null;
     }
     public int getTier() {
       return 1;
     }
     public boolean isMoveTransformer(boolean enemy) {
       return enemy;
     }
     public boolean tick(Pokemon p) {
       p.removeStatus(this);
       return true;
     }
     public MoveListEntry getEnemyTransformedMove(Pokemon p, MoveListEntry entry) {
       final PokemonMove move = entry.getMove();
       String name = entry.getName();
       if (name.equals("Feint")
           || name.equals("Shadow Force")
           || (move instanceof JumpKickMove)
           || name.equals("Explosion")
           || name.equals("Selfdestruct")) return entry;
       if (!p.hasEffect(ChargeEffect.class) && (move instanceof StatusMove)) {
         StatusMove smove = (StatusMove)move;
         StatusEffect[] effects = smove.getEffects();
         for (int i = 0; i < effects.length; ++i) {
           if (effects[i] instanceof ChargeEffect) return entry;
         }
       }
       BattleField field = p.getField();
       if ((field.getMechanics() instanceof JewelMechanics)
           && (field.getEffectByType(RainEffect.class) != null)
           && name.equals("Thunder")) {
         return MoveList.getDefaultData().getMove("Thunder");
       }
       if (move.isAttack() || name.equals("Roar") || name.equals("Whirlwind")) {
         PokemonMove replace =
           new PokemonMove(PokemonType.T_TYPELESS, 0, move.getAccuracy(), 1) {
           public int use(BattleMechanics mech, Pokemon source, Pokemon target) {
             return m_event.use(move, mech, source, target);
           }
         };
         return new MoveListEntry(entry.getName(), replace);
       }
       return entry;
     }
   }

   private static class IngrainEffect extends StatusEffect {
     public String getName() {
       return "Ingrain";
     }
     public int getTier() {
       return 3;
     }
     public String getDescription() {
       return " planted its roots!";
     }
     public boolean tick(Pokemon p) {
       int absorb = p.getStat(Pokemon.S_HP) / 16;
       if (absorb == 0) {
         absorb = 1;
       }
       p.getField().showMessage(p.getName() + " absorbed health!");
       p.changeHealth(absorb);
       return true;
     }
     public boolean canSwitch(Pokemon p) {
       /** Research from AA confirms that Shed Shell allows you to switch
        *  out of ingrain.
        */
       return p.hasItem("Shed Shell");
     }
   }

   /**
    * An effect that _effectively_ swaps the speeds of the pokemon in play.
    */
   public static class SpeedSwapEffect extends FieldEffect {
     private int m_turns = 5;
     public boolean applyToField(BattleField field) {  
       return true;
     }
     public boolean tickField(BattleField field) {
       if (--m_turns == 0) {
         field.removeEffect(this);
         return true;
       }
       return false;
     }
     public void unapplyToField(BattleField field) {
       field.showMessage("The twisted dimensions returned to normal!");
     }
     public int getTier() {
       return 1;
     }
     public String getDescription() {
       return null;
     }
     public String getName() {
       return "Trick room";
     }
   }

   /**
    * A counter used for protect, detect, and endure.
    */
   private static class CounterEffect extends StatusEffect {
     private int m_count = 1;

     public boolean tick(Pokemon p) {
       return false;
     }

     public String getName() {
       return null;
     }

     public String getDescription() {
       return null;
     }

     public int getTier() {
       return -1;
     }

     public boolean isMoveTransformer(boolean enemy) {
       return !enemy;
     }

     public MoveListEntry getTransformedMove(Pokemon p, MoveListEntry entry) {
       PokemonMove move = entry.getMove();

       if ((move instanceof ProtectMove) && (p.getLastMove() != null)) {
         ++m_count;
         ((ProtectMove)move).setFailure(1.0 - 1.0 / (double)(1 << m_count));
       } else {
         p.removeStatus(this);
       }
       return entry;
     }

     public void informDuplicateEffect(Pokemon p) {
       // Swallow the error.
     }
   }

   public static class TrappingEffect extends StatusEffect {
     private String m_name;

     public TrappingEffect(String name) {
       m_name = name;
     }
     public String getName() {
       return m_name;
     }
     public boolean apply(Pokemon p) {
       p.getOpponent().addStatus(p, new CoEffect(getClass()));
       return super.apply(p);
     }
     public String getDescription() {
       return " was trapped!";
     }
     public int getTier() {
       return -1;
     }
     public boolean tick(Pokemon p) {
       return false;
     }
     public boolean isSingleton() {
       return false;
     }
     public boolean canSwitch(Pokemon p) {
       return p.hasItem("Shed Shell");
     }
   }

   private static class StockpileEffect extends StatusEffect {
     private int m_levels = 0;
     private List<StatusEffect> m_effects = new ArrayList<StatusEffect>();

     public String getName() {
       return "Stockpile";
     }
     public String getDescription() {
       return null;
     }
     public int getTier() {
       return -1;
     }
     public boolean tick(Pokemon p) {
       return false;
     }
     public boolean incrementLevel(Pokemon p) {
       if (m_levels < 3) {
         m_levels++;
         p.getField().showMessage(p.getName() + " stockpiled " + m_levels + "!");
         if (p.getField().getMechanics() instanceof JewelMechanics) {
           m_effects.add(
               p.addStatus(p, new StatChangeEffect(Pokemon.S_DEFENCE, true)));
           m_effects.add(
               p.addStatus(p, new StatChangeEffect(Pokemon.S_SPDEFENCE, true)));
         }
         return true;
       }
       p.getField().showMessage(p.getName() + " couldn't stockpile any more!");
       return false;
     }
     public int getLevels() {
       return m_levels;
     }
     public boolean isSingleton() {
       return true;
     }
     public boolean isPassable() {
       return false;
     }
     public void informDuplicateEffect(Pokemon p) {
       //Do nothing
     }
     public void unapply(Pokemon p) {
       Iterator<StatusEffect> i = m_effects.iterator();
       while (i.hasNext()) {
         p.removeStatus((StatusEffect)i.next());
       }
       super.unapply(p);
     }
   }

   /**
    * A move that is based upon the number of levels stored in Stockpile
    */
   public static class StockpileMove extends PokemonMove {
     public StockpileMove(PokemonType type, int power, double accuracy, int pp) {
       super(type, power, accuracy, pp);
     }
     public StockpileEffect getStockpileEffect(Pokemon p) {
       List<StatusEffect> statuses = p.getNormalStatuses(0);
       Iterator<StatusEffect> i = statuses.iterator();
       while (i.hasNext()) {
         StatusEffect eff = (StatusEffect)i.next();
         if (eff instanceof StockpileEffect) {
           return (StockpileEffect)eff;
         }
       }
       return null;
     }
     public int getLevels(Pokemon p) {
       StockpileEffect eff = getStockpileEffect(p);
       if (eff == null) return -1;
       return eff.getLevels();
     }
   }
   //Removes an effect from the opponent if the user switches out.   
   public static class CoEffect extends StatusEffect {
     private Class<?> m_type;
     public CoEffect(Class<?> type) {
       m_type = type;
     }
     public Class<?> getType() {
       return m_type;
     }
     public String getName() {
       return null;
     }
     public String getDescription() {
       return null;
     }
     public int getTier() {
       return -1;
     }
     public boolean tick(Pokemon p) {
       return false;
     }
     public boolean switchOut(Pokemon p) {
       p.getOpponent().removeStatus(m_type);
       return true;
     }
   }

   static class DefenseCurlEffect extends StatusEffect {
     public String getName() {
       return "Defense Curl";
     }
     public String getDescription() {
       return null;
     }
     public int getTier() {
       return -1;
     }
     public boolean tick(Pokemon p) {
       return false;
     }
     public boolean isMoveTransformer(boolean enemy) {
       return !enemy;
     }
     public MoveListEntry getTransformedMove(Pokemon p, MoveListEntry entry) {
       if (entry.getName().equals("Rollout")) {
         PokemonMove move = entry.getMove();
         move.setPower(move.getPower() * 2);
       }
       return entry;
     }
     public boolean isPassable() {
       return false;
     }
     public void informDuplicateEffect(Pokemon p) {
     }
   }

   static class MinimizeEffect extends StatusEffect {
     public String getName() {
       return "Minimize";
     }
     public String getDescription() {
       return null;
     }
     public int getTier() {
       return -1;
     }
     public boolean tick(Pokemon p) {
       return false;
     }
     public boolean isMoveTransformer(boolean enemy) {
       return enemy;
     }
     public MoveListEntry getEnemyTransformedMove(Pokemon p, MoveListEntry entry) {
       if (entry.getName().equals("Stomp") || entry.getName().equals("Extrasensory")) {
         PokemonMove move = entry.getMove();
         move.setPower(move.getPower() * 2);
       }
       return entry;
     }
     public boolean isPassable() {
       return false;
     }
     public void informDuplicateEffect(Pokemon p) {
     }
   }

   static class RechargeEffect extends StatusEffect {
     private int m_turns;

     public RechargeEffect(int turns) {
       m_turns = turns;
     }
     public String getName() {
       return "Recharge Effect";
     }
     public String getDescription() {
       return null;
     }
     public int getTier() {
       return 0;
     }
     public boolean tick(Pokemon p) {
       if (m_turns-- <= 0) {
         p.removeStatus(this);
       }
       return false;
     }
     public boolean canSwitch(Pokemon p) {
       return false;
     }
     public boolean isMoveTransformer(boolean enemy) {
       return !enemy;
     }
     public MoveListEntry getTransformedMove(Pokemon p, MoveListEntry entry) {
       p.getField().showMessage(p.getName() + " must recharge!");
       return null;
     }
   }

   /**
    * Records the amount of damage done to a Pokemon in a turn. Should be applied in
    * a beginTurn event.
    */
   static class DamageListenerEffect extends StatusEffect {
     private int m_damage = 0;
     private boolean m_special = true;

     public String getName() {
       return null;
     }
     public String getDescription() {
       return null;
     }
     public int getTier() {
       return 1;
     }
     public boolean tick(Pokemon p) {
       p.removeStatus(this);
       return true;
     }
     public boolean isListener() {
       return true;
     }
     public void informDamaged(Pokemon source, Pokemon target, MoveListEntry move, int damage) {
       m_damage += damage;
       m_special = move.getMove().isSpecial(source.getField().getMechanics());
     }
     public int getDamage() {
       return m_damage;
     }
     public boolean isSpecial() {
       return m_special;
     }
   }

   public static class SpikesEffect extends FieldEffect {
     protected int m_layers[] = { 0, 0 };
     protected int m_maxLayers = 3;
     protected String m_message = "Spikes were scattered around the foe's team!";

     public String getName() {
       return "Spikes";
     }
     public String getDescription() {
       return null;
     }
     public int getTier() {
       return -1;
     }
     /**
      * Returns the instance of a certain class of spikes on the field, if one is present.
      */
     public static SpikesEffect getSpikes(BattleField field, Class<?> type) {
       List<?> effects = field.getEffectsByType(SpikesEffect.class);
       if (effects.size() == 0) return null;
       Iterator<?> i = effects.iterator();
       while (i.hasNext()) {
         SpikesEffect eff = (SpikesEffect)i.next();
         if (eff.getClass().equals(type)) {
           return eff;
         }
       }
       return null;
     }
     /**
      * Returns the number of layers of spikes that are applied to a Pokemon.
      */
     public int getLayers(Pokemon p) {
       int team = p.getParty();
       return m_layers[team];
     }
     /**
      * Adds a layer of spikes to a Pokemon's team.
      */
     public void addSpikes(Pokemon p) {
       int team = p.getParty();
       int layers = getLayers(p);
       if (layers >= m_maxLayers) {
         p.getField().showMessage("But it failed!");
         return;
       }
       m_layers[team]++;
       if (m_message != null) {
         p.getField().showMessage(m_message);
       }
     }
     /**
      * Removes spikes from a Pokemon's team.
      */
     public void removeSpikes(Pokemon p) {
       int team = p.getParty();
       m_layers[team] = 0;
     }
     public void switchIn(Pokemon p) {
       BattleField field = p.getField();
       if ((PokemonMove.getEffectiveness(PokemonType.T_GROUND, null, p) == 0.0) || p.hasAbility("Levitate")
           || (getLayers(p) <= 0)) {
         return;
       }
       int layers = getLayers(p);
       int maximum = p.getStat(Pokemon.S_HP);
       double factor = new double[] { 0.125, 0.1875, 0.25 }[layers - 1];
       int damage = (int)(((double)maximum) * factor);
       if (damage < 1) damage = 1;
       field.showMessage(p.getName() + " was hurt by Spikes!");
       p.changeHealth(-damage, true);
     }
     public boolean applyToField(BattleField field) {
       field.showMessage("Spikes were scattered everywhere!");
       return true;
     }
     public boolean tickField(BattleField field) {
       return false;
     }
   }

   public static class ToxicSpikesEffect extends SpikesEffect {
     public ToxicSpikesEffect() {
       m_maxLayers = 2;
       m_message = "Toxic Spikes were scattered around the foe's team!";
     }
     public String getName() {
       return "Toxic spikes";
     }
     public void switchIn(Pokemon p) {
       BattleField field = p.getField();
       if ((PokemonMove.getEffectiveness(PokemonType.T_GROUND, null, p) == 0.0)
           || p.isType(PokemonType.T_STEEL)
           || p.hasAbility("Levitate")
           || (getLayers(p) <= 0)) {
         return;
       }
       if (p.isType(PokemonType.T_POISON)) {
         field.showMessage(p.getName() + " absorbed the Toxic Spikes!");
         removeSpikes(p);
         return;
       }
       int layers = getLayers(p);
       if (layers == 1) {
         if (p.addStatus(null, new PoisonEffect()) != null) {
           field.showMessage(p.getName() + " was poisoned by the Toxic Spikes!");
         }
       } else if (layers == 2) {
         if (p.addStatus(null, new ToxicEffect()) != null) {
           field.showMessage(p.getName() + " was badly poisoned by the Toxic Spikes!");
         }
       }
     }
   }

   public static class StealthRockEffect extends SpikesEffect {
     public StealthRockEffect() {
       m_maxLayers = 1;
       m_message = null;
     }
     public String getName() {
       return "Stealth rock";
     }
     public void switchIn(Pokemon p) {
       if (getLayers(p) <= 0) {
         return;
       }
       double effectiveness = PokemonMove.getEffectiveness(PokemonType.T_ROCK, null, p);
       double baseDamage = p.getStat(Pokemon.S_HP) / 8.0;
       int damage = (int)(baseDamage * effectiveness);
       if (damage <= 1) damage = 1;
       p.getField().showMessage("Pointed stones dug into " + p.getName() + ".");
       p.changeHealth(-damage, true);
     }
     public boolean applyToField(BattleField field) {
       field.showMessage("Pointed stones float in the air around your foe's team!");
       return true;
     }
   }

   public static class AttractEffect extends StatusEffect {
     public String getName() {
       return "Attract";
     }
     public boolean isPassable() {
       return false;
     }
     public boolean hitsThroughSubstitute() {
       return true;
     }
     public String getDescription() {
       return " fell in love!";
     }
     public int getTier() {
       return -1;
     }
     public boolean tick(Pokemon p) {
       return false;
     }
     public boolean apply(Pokemon p) {          
       int g1 = p.getGender();
       int g2 = p.getOpponent().getGender();
       if ((g1 == g2) ||
           (g1 == PokemonSpecies.GENDER_NONE) ||
           (g2 == PokemonSpecies.GENDER_NONE)) {
         p.getField().showMessage("But it failed!");
         return false;
       }
       if (p.hasAbility("Oblivious")) {
         p.getField().showMessage(p.getName() + "'s Oblivious prevents attraction!");
         return false;
       }
       p.getOpponent().addStatus(p, new CoEffect(getClass()));
       return true;
     }
     public boolean immobilises(Pokemon p) {
       p.getField().showMessage(p.getName() + " is in love with foe " + p.getOpponent().getName() + "!");
       if (p.getField().getRandom().nextBoolean()) {
         p.getField().showMessage(p.getName() + " is immobilised by love!");
         return true;
       }
       return false;
     }
   }

   /**
    * Applys an effect after a certain number of turns.
    */
   static class DelayedStatusEffect extends FieldEffect {
     protected int m_party;
     protected int m_turns;
     private StatusEffect m_effect;
     private String m_message;

     public DelayedStatusEffect(StatusEffect effect, int party, int turns, String message) {
       m_effect = effect;
       m_party = party;
       m_turns = turns;
       m_message = message;
     }
     public boolean isSingleton() {
       return false;
     }
     public String getName() {
       return null;
     }
     public String getDescription() {
       return null;
     }
     public int getTier() {
       return 1;
     }
     public boolean applyToField(BattleField field) {
       field.showMessage(field.getActivePokemon()[m_party].getName() + m_message);
       return true;
     }
     public boolean tickField(BattleField field) {
       if (--m_turns == 0) {
         Pokemon poke = field.getActivePokemon()[m_party];
         poke.addStatus(poke.getOpponent(), m_effect);
         field.removeEffect(this);
         return true;
       }
       return false;
     }
   }

   /**
    * Deals an amount of damage to the active Pokemon a specified number of turns later.
    */   
   static class DelayedDamageEffect extends DelayedStatusEffect {
     private int m_damage;

     public DelayedDamageEffect(int damage, int party, int turns) {
       super(null, party, turns, " foresaw an attack!");
       m_damage = damage;
     }
     public boolean tickField(BattleField field) {
       if (--m_turns == 0) {
         if (m_damage <= 0) {
           field.showMessage("But it failed!");
           field.removeEffect(this);
           return true;
         }
         Pokemon poke = field.getActivePokemon()[m_party];
         field.showMessage(poke.getName() + " took the attack!");
         poke.changeHealth(-m_damage);
         field.removeEffect(this);
         return true;
       }
       return false;
     }
     public int getTier() {
       return 4;
     }
   }

   /**
    * Cuts a certain stat of an entire team for 5 turns.
    */
   static class StatCutEffect extends FieldEffect {
     private int m_stat;
     private String m_name;
     private int m_party;
     private int m_turns;

     public StatCutEffect(int stat, int party, String name) {
       m_stat = stat;
       m_party = party;
       m_name = name;
     }
     public String getName() {
       return m_name;
     }
     public String getDescription() {
       return null;
     }
     public int getTier() {
       return 0;
     }
     public boolean isSingleton() {
       return false;
     }
     public boolean isMoveTransformer(boolean enemy) {
       return enemy;
     }
     protected MoveListEntry getEnemyTransformedMove(Pokemon p, MoveListEntry entry) {
       if (p.getParty() != m_party) return entry;
       PokemonMove move = entry.getMove();
       boolean special = (m_stat == Pokemon.S_SPDEFENCE);
       if (move.isSpecial(p.getField().getMechanics()) == special) {
         move.setPower(move.getPower() / 2);
       }
       return entry;
     }
     public boolean equals(Object o2) {
       if (!(o2 instanceof StatCutEffect)) return false;
       StatCutEffect eff = (StatCutEffect)o2;
       return ((m_stat == eff.m_stat) && (m_party == eff.m_party));
     }
     public boolean applyToField(BattleField field) {
       Pokemon user = field.getActivePokemon()[m_party];
       m_turns = user.hasItem("Light Clay") ? 8 : 5;
       field.showMessage("A barrier was formed!");
       return true;
     }
     public boolean tickField(BattleField field) {
       if (m_turns-- <= 0) {
         Pokemon[] pokes = field.getActivePokemon();
         field.showMessage(pokes[m_party].getName() + "'s " + m_name + " wore off!");
         field.removeEffect(this);
         return true;
       }
       return false;
     }

     public int getParty() {
       return m_party;
     }
   }

   /**
    * Cuts a certain stat of an entire team for 5 turns.
    */
   static class TailwindEffect extends FieldEffect {
     private int m_party;
     private int m_turns = 3;
     public TailwindEffect(int party) {
       m_party = party;
     }
     public String getName() {
       return "Tailwind";
     }
     public String getDescription() {
       return null;
     }
     public int getTier() {
       //todo: no idea about tier
       return 4;
     }
     public boolean isSingleton() {
       return true;
     }
     public boolean equals(Object o2) {
       if (!(o2 instanceof TailwindEffect)) return false;
       TailwindEffect eff = (TailwindEffect)o2;
       return m_party == eff.m_party;
     }
     public boolean applyToField(BattleField field) {
       field.showMessage("A tailwind picked up!");
       return true;
     }
     public boolean tickField(BattleField field) {
       if (m_turns-- <= 0) {
         field.showMessage("The tailwind died down.");
         field.removeEffect(this);
         return true;
       }
       return false;
     }
     public boolean apply(Pokemon p) {
       if (p.getParty() == m_party) {
         p.getMultiplier(Pokemon.S_SPEED).multiplyBy(2.0);
       }
       return super.apply(p);
     }
     public void unapply(Pokemon p) {
       if (p.getParty() == m_party) {
         p.getMultiplier(Pokemon.S_SPEED).divideBy(2.0);
       }
       super.unapply(p);
     }
   }

   public static class MistEffect extends PartyEffect {
     public MistEffect() {
       super(" was shrouded in Mist!", "'s mist wore off.");
     }
     public String getName() {
       return "Mist";
     }
   }

   public static class SafeguardEffect extends PartyEffect {
     public SafeguardEffect() {
       super(" is protected by a veil!", "'s Safeguard wore off.");
     }
     public String getName() {
       return "Safeguard";
     }
     public boolean allowsStatus(StatusEffect eff, Pokemon source, Pokemon target) {
       if (!isActive(target.getParty())) return true;
       if (source == target) return true;
       return !((eff instanceof BurnEffect) || (eff instanceof FreezeEffect) ||
           (eff instanceof ParalysisEffect) || (eff instanceof PoisonEffect) ||
           (eff instanceof SleepEffect) || (eff instanceof ConfuseEffect));
     }
   }

   public MoveList(boolean initialise) {
     if (!initialise) {
       return;
     }
     m_moves = new ArrayList<MoveListEntry>();
     initNonStatusMoves();
     initStatusMoves();
   }

   /**
    * Get the move list.
    */
   public ArrayList<MoveListEntry> getMoveList() {
     return m_moves;
   }

   /**
    * Get a move by its name.
    */
   public MoveListEntry getMove(String name) {
     if (name == null)
       return null;
     Iterator<MoveListEntry> i = m_moves.iterator();
     while (i.hasNext()) {
       MoveListEntry move = (MoveListEntry)i.next();
       if (name.equalsIgnoreCase(move.getName())) {
         return move;
       }
     }
     return null;
   }

   /**
    * Write data on all moves to an ouput stream.
    */
   public void saveMoveList(OutputStream output) throws IOException {
     ObjectOutputStream stream = new ObjectOutputStream(output);
     ArrayList<MoveListEntry> moves = getMoveList();
     stream.writeInt(moves.size());
     Iterator<MoveListEntry> i = moves.iterator();
     while (i.hasNext()) {
       MoveListEntry entry = (MoveListEntry)i.next();
       stream.writeObject(entry.getName());
       PokemonMove move = entry.getMove();
       if (move instanceof StatusMove) {
         StatusMove statusMove = (StatusMove)move;
         StatusEffect[] effects = statusMove.getEffects();
         for (int j = 0; j < effects.length; ++j) {
           StatusEffect effect = effects[j];
           if (effect instanceof ChargeEffect) {
             ChargeEffect charge = (ChargeEffect)effect;
             MoveListEntry chargeEntry = charge.getMove();
             if (chargeEntry != null) {
               PokemonMove chargeMove = chargeEntry.getMove();
               if (chargeMove != null) {
                 move = chargeMove;
               }
             }
           }
         }
       }

       stream.writeObject(move.getType());
       stream.writeInt(move.getPower());
       stream.writeDouble(move.getAccuracy());
       stream.writeInt(move.getPp());
     }
     stream.flush();
   }

   /**
    * Read data on all moves from an input stream.
    */
   public void loadMoveList(InputStream input) throws IOException {
     ObjectInputStream stream = new ObjectInputStream(input);
     m_moves = new ArrayList<MoveListEntry>();
     int size = stream.readInt();
     m_moves.ensureCapacity(size);
     for (int i = 0; i < size; ++i) {
       try {
         String name = (String)stream.readObject();
         PokemonType type = (PokemonType)stream.readObject();
         int power = stream.readInt();
         double accuracy = stream.readDouble();
         int pp = stream.readInt();
         m_moves.add(new MoveListEntry(name,
             new PokemonMove(type, power, accuracy, pp)));
       } catch (ClassNotFoundException e) {
         throw new InternalError();
       }
     }
   }

   /*
    * Get a list of all move names.
    */
   public String[] getMoveNames() {
     ArrayList<MoveListEntry> moves = m_moves;
     String[] names = new String[moves.size()];
     for (int i = 0; i < moves.size(); ++i) {
       names[i] = ((MoveListEntry)moves.get(i)).getName();
     }
     return names;
   }

}
TOP

Related Classes of org.pokenet.server.battle.mechanics.moves.MoveList$LeechSeedEffect

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.
-20639858-1', 'auto'); ga('send', 'pageview');