package org.pokenet.server.battle.mechanics;
import java.io.Serializable;
import java.security.SecureRandom;
import java.util.Random;
import org.pokenet.server.battle.Pokemon;
import org.pokenet.server.battle.mechanics.moves.PokemonMove;
/**
* This class represents the mechanics for a battle in a particular generation
* of pokemon. Derive classes from this class to implement a desired generation.
*
* @author Colin
*/
public abstract class BattleMechanics implements Serializable {
/**
* Constant version of this class.
*/
private static final long serialVersionUID = 2907773868045621558L;
/**
* One universal random number generator is defined here for the whole
* server so that there are not several identical streams of random
* numbers kicking around.
*/
@SuppressWarnings("unused")
private static final Random m_masterRandom;
/**
* A random number generator specific to this instance of the mechanics.
*/
private final Random m_random;
/**
* Calculate the initial value of a stat from a pokemon's base stats and
* hidden stats.
*
* @param p the pokemon whose stats to calculate
* @param i the stat to calculate (use the constants Pokemon.S_HP, etc.)
*/
abstract public int calculateStat(Pokemon p, int i) throws StatException;
/**
* Validate the hidden stats of a pokemon.
*/
abstract public void validateHiddenStats(Pokemon p)
throws ValidationException;
/**
* Randomly decide whether a move hits.
*/
abstract public boolean attemptHit(PokemonMove move,
Pokemon user,
Pokemon target);
/**
* Calculate the damage done by a move.
* Does not actually inflict damage to pokemon.
* Optionally do not display any messages.
*/
abstract public int calculateDamage(PokemonMove move,
Pokemon attacker,
Pokemon defender,
boolean silent);
/**
* Calculate the damage done by a move.
* Does not actually inflict damage to pokemon.
*/
public int calculateDamage(PokemonMove move,
Pokemon attacker,
Pokemon defender) {
return calculateDamage(move, attacker, defender, false);
}
/**
* Return whether a given move deals special damage.
*/
public abstract boolean isMoveSpecial(PokemonMove move);
/**
* Get an instance of the Random class.
*/
public final Random getRandom() {
return m_random;
}
/**
* Initialise an instance of the mechanics.
*/
public BattleMechanics(int bytes) {
if (bytes == 4) {
m_random = new Random();
} else {
m_random = getRandomSource(bytes);
}
}
static {
m_masterRandom = getRandomSource(25);
}
/**
* Returns the amount of EXP required to reach a level
* based on a pokemon's EXP type
* @param poke
* @param level
* @return
*/
public double getExpForLevel(Pokemon poke, int level){
double exp = 0;
switch (poke.getExpType()){
case MEDIUM:
exp = (int)java.lang.Math.pow((double)level, 3);
break;
case ERRATIC:
double p = 0;
switch (level % 3){
case 0:
p = 0;
break;
case 1:
p = 0.008;
break;
case 2:
p = 0.014;
break;
}
if (level <= 50){
exp = java.lang.Math.pow((double)level, 3) * ((100 - (double)level) / 50);
} else if (level <= 68){
exp = java.lang.Math.pow((double)level, 3) * ((150 - (double)level) / 50);
} else if (level <= 98){
exp = (java.lang.Math.pow((double)level, 3) *
(1.274 - ((1/50) * ((double)level / 3)) - p));
} else {
exp = java.lang.Math.pow((double)level, 3) * ((160 - (double)level) / 50);
}
break;
case FAST:
exp = 4 * java.lang.Math.pow((double)level, 3) / 5;
break;
case FLUCTUATING:
if (level <= 15){
exp = java.lang.Math.pow((double)level, 3) *
((24 + (((double)level + 1) / 3)) / 50);
} else if ((double)level <= 35){
exp = java.lang.Math.pow((double)level, 3) *
((14 + (double)level) / 50);
} else {
exp = java.lang.Math.pow((double)level, 3) *
((32 + ((double)level / 2)) / 50);
}
break;
case PARABOLIC:
exp = (6 * (java.lang.Math.pow((double)level, 3) / 5)) -
(15 * java.lang.Math.pow((double)level, 2)) + (100 * (double)level) - 140;
break;
case SLOW:
exp = 5 * java.lang.Math.pow((double)level, 3) / 4;;
break;
}
return exp;
}
/**
* Initialise the battle mechanics. Try to use the SecureRandom class for
* the random number generator, with a seed from /dev/random. However, if
* /dev/random is unavailable (e.g. if we are running on Windows) then an
* instance of Random, seeded from the time, is used instead.
*
* For best results, use an operating system that supports /dev/random.
*/
public static Random getRandomSource(int bytes) {
try {
return new SecureRandom(/*seed*/);
} catch (Exception e) {
System.out.println("Could not use SecureRandom: " + e.getMessage());
return new Random();
}
}
/**
* Calcultes the level of a Pokemon based on their EXP amount
* @param a
* @return
*/
public int calculateLevel(Pokemon a){
double result = 0;
switch (a.getExpType()) {
case MEDIUM:
{
for(double i = 1; i <= 100; i++)
{
if(i < 100 && a.getExp() >= (i * i * i) && a.getExp() < ((i + 1) * (i + 1) * (i + 1)))
{
result = i;
break;
}
else if(i == 100 && a.getExp() >= (i * i * i))
{
result = 100;
break;
}
else if(a.getExp() >= 1000000)
{
result = 100;
a.setExp(1000000);
break;
}
}
}
break;
case ERRATIC:
{
for(double i = 1; i < 101; i++)
{
if(i < 50)
{
if((a.getExp()) >= ((i * i * i)*((100 - i)/50)) && (a.getExp()) < (((i + 1) * (i + 1) * (i + 1))*((100 - (i + 1))/50)))
{
result = i;
break;
}
}
else if(i == 50)
result = i;
else if(i >= 51 && i < 68)
{
if((a.getExp()) >= ((i * i * i)*((150 - i)/50)) && (a.getExp()) < (((i + 1) * (i + 1) * (i + 1))*((150 - (i + 1))/50)))
{
result = i;
break;
}
}
else if(i == 68)
result = i;
else if(i >= 69 && i < 98)
{
double temp = a.getExp();
double funt = i % 3;
double funt1 = (i + 1) % 3;
if(funt == 1)
funt = 0.008;
else if(funt == 2)
funt = 0.014;
if(funt1 == 1)
funt1 = 0.008;
else if(funt1 == 2)
funt1 = 0.014;
if(temp >= ((i * i * i) * (1.274 - ((1 / 50)*(i / 3)) - funt)) && temp < (((i + 1) * (i + 1) * (i + 1)) * (1.274 - ((1 / 50)*((i + 1) / 3)) - funt1)))
{
result = i;
break;
}
}
else if(i == 98)
result = i;
else if(i == 99)
{
if((a.getExp()) >= ((i * i * i)*((160 - i)/50)) && (a.getExp()) < (((i + 1) * (i + 1) * (i + 1))*((160 - (i + 1))/50)))
{
result = i;
break;
}
}
else if(i == 100)
{
if((a.getExp()) >= ((i * i * i)*((160 - i)/50)))
{
result = i;
break;
}
}
else if(a.getExp() >= 600000)
{
result = 100;
a.setExp(600000);
break;
}
}
}
break;
case FLUCTUATING:
{
for(double i = 101; i > 36; i--)
{
if((a.getExp()) < ((i * i * i)*((32 + (i / 2))/50)))
result = i - 1;
}
for(double i = 36; i > 15; i--)
{
System.out.println(i);
if((a.getExp()) < ((i * i * i)*((14 + i)/50)))
result = i - 1;
}
for(double i = 15; i > 1; i--)
{
double reqExp = (i * i * i)*((24 + ((i + 1) / 3))/50);
if((a.getExp()) < reqExp)
result = i - 1;
}
if(a.getExp() >= 1640000)
{
result = 100;
a.setExp(1640000);
break;
}
}
break;
case PARABOLIC:
{
for(double i = 101; i > 1; i--)
{
if(a.getExp() < (((6 * (i * i * i))/5) - (15 * (i * i)) + (100 * i) - 140))
{
result = i - 1;
}
else if(a.getExp() >= 1059860)
{
result = 100;
a.setExp(1059860);
break;
}
}
}
break;
case FAST:
{
for(double i = 101; i > 1; i--)
{
if(a.getExp() < ((4 * (i * i * i))/5))
{
result = i - 1;
}
else if(a.getExp() >= 800000)
{
result = 100;
a.setExp(800000);
break;
}
}
}
break;
case SLOW:
{
for(double i = 101; i > 1; i--)
{
if(a.getExp() < ((5 * (i * i * i))/4))
{
result = i - 1;
}
else if(a.getExp() >= 1250000)
{
result = 100;
a.setExp(1250000);
break;
}
}
}
}
return (int) result;
}
/**
* Calculates the EXP gained (per Pokemon who defeated it) from defeating a Pokemon
* @param a - The defeated Pokemon
* @param u - How many Pokemon defeated it
* @return
*/
public double calculateExpGain(Pokemon a, int u){
double result = (((((a.getLevel() * a.getBaseExp())/7))/u));
return result / 2;
}
/**
* Returns true if a Pokemon was successfully caught
* @param pokemon
* @param rate
* @param ball
* @param status
* @return
*/
public boolean isCaught(Pokemon pokemon, int rate, double ball, int status){
int maxHP = pokemon.getStat(Pokemon.S_HP);
int currentHP = pokemon.getHealth();
//maxHP represents the max HP of the wild pokemon
//currentHP represents the current HP of the wild pokemon
//rate represents the pokemons catch rate by default
//ball is the thrown ball's catch rate(1 for Pokeball; 1.5 for Great Ball; 2 for Ultra Ball; 255 for Masterball)
//status represents any status ailments the Pokemon has (2 for sleep and freeze, 1.5 for paralyze, poison and burn), if none set to 1
//The following calculation recalculates the Pokemon's catch rate with status ailments/currentHP/pokeball used/etc. taken into account
double a = 0;
a = ((((((3 * maxHP) - (2 * currentHP)) * rate) * ball) / (3 * maxHP)) * status);
if(a >= 255){
return true;
} else {
double b = 1048560 / (Math.sqrt(Math.sqrt((16711680 / a))));
double po = getRandom().nextInt(65536);
double pt = getRandom().nextInt(65536);
double pth = getRandom().nextInt(65536);
double pf = getRandom().nextInt(65536);
if(po <= b)
if(pt <= b)
if(pth <= b)
if(pf <= b)
return true;
else
return false;
else
return false;
else
return false;
else
return false;
}
}
}