package org.pokenet.server.battle.mechanics; import org.pokenet.server.battle.BattleField; import org.pokenet.server.battle.Pokemon; import org.pokenet.server.battle.mechanics.moves.MoveList; import org.pokenet.server.battle.mechanics.moves.PokemonMove; import org.pokenet.server.battle.mechanics.statuses.StatChangeEffect; import org.pokenet.server.battle.mechanics.statuses.field.FieldEffect; /** * This class represents the mechanics in the advanced generation of pokemon. * @author Colin */ public class AdvanceMechanics extends BattleMechanics { private static final long serialVersionUID = -2238204671194997172L; @SuppressWarnings("unused") private static int m_log = 0; /** Creates a new instance of AdvanceMechanics */ public AdvanceMechanics(int bytes) { super(bytes); } public int calculateStat(Pokemon p, int i) throws StatException { if ((i < 0) || (i > 5)) throw new StatException(); int common = (int)((int)(((2.0 * p.getBase(i)) + p.getIv(i) + (p.getEv(i) / 4.0))) * (p.getLevel() / 100.0)); if (i == Pokemon.S_HP) { if (p.getSpeciesName().equals("Shedinja")) { // Shedinja always has 1 hp. return 1; } else { return common + 10 + p.getLevel(); } } return (int)((common + 5) * p.getNature().getEffect(i)); } /** * Return whether the move hit. */ public boolean attemptHit(PokemonMove move, Pokemon user, Pokemon target) { BattleField field = user.getField(); double accuracy = move.getAccuracy(); boolean hit; if ((accuracy != 0.0) && (user.hasAbility("No Guard") || target.hasAbility("No Guard") || user.hasEffect(MoveList.LockOnEffect.class)) ) { hit = true; } else { double effective = (accuracy * user.getAccuracy().getMultiplier()) / target.getEvasion().getMultiplier(); if (effective > 1.0) effective = 1.0; hit = (field.getRandom().nextDouble() <= effective); } if (!hit) { field.showMessage(user.getName() + "'s attack missed!"); } return hit; } public boolean isCriticalHit(PokemonMove move, Pokemon user, Pokemon target) { if (target.isCriticalImmune()) { return false; } FieldEffect effect = user.getField().getEffectByType(MoveList.LuckyChantEffect.class); if (effect != null) { MoveList.LuckyChantEffect eff = (MoveList.LuckyChantEffect)effect; if (eff.isActive(target.getParty())) { return false; } } int moveFactor = 0; if (move.hasHighCriticalHitRate()) { moveFactor = (this instanceof JewelMechanics) ? 1 : 3; } int factor = user.getCriticalHitFactor() + (user.hasItem("Scope Lens") ? 1 : 0) /* TODO: + (FE/L * 1) */ + moveFactor; double chance = 0.0; switch (factor) { case 1: chance = 0.0625; break; case 2: chance = 0.125; break; case 3: chance = 0.25; break; case 4: chance = 0.332; break; default: chance = 0.5; break; } return (user.getField().getRandom().nextDouble() <= chance); } /** * Return whether a given move deals special damage. */ public boolean isMoveSpecial(PokemonMove move) { return move.getType().isSpecial(); } public strictfp int calculateDamage( PokemonMove move, Pokemon attacker, Pokemon defender, boolean silent) { final BattleField field = attacker.getField(); PokemonType moveType = move.getType(); final boolean special = isMoveSpecial(move); boolean isCritical = move.canCriticalHit() && isCriticalHit(move, attacker, defender); double attack = attacker.getStat(special ? Pokemon.S_SPATTACK : Pokemon.S_ATTACK); int defStat = special ? Pokemon.S_SPDEFENCE : Pokemon.S_DEFENCE; StatMultiplier mul = defender.getMultiplier(defStat); double defMultiplier = mul.getMultiplier(); if (isCritical && (defMultiplier > 1.0)) { defMultiplier = mul.getSecondaryMultiplier(); } double defence = defender.getStat(defStat, defMultiplier); final int random = field.getRandom().nextInt(16) + 85; double multiplier = move.getEffectiveness(attacker, defender); if (multiplier > 1.0) { if (!silent) { field.showMessage("It's super effective!"); } } else if (multiplier == 0.0) { if (!silent) { field.showMessage("It doesn't affect " + defender.getName() + "..."); } // Just return now to prevent a critical hit from occurring. return 0; } else if (multiplier < 1.0) { if (!silent) { field.showMessage("It's not very effective..."); } } final boolean stab = attacker.isType(moveType); double stabFactor = attacker.hasAbility("Adaptability") ? 2.0 : 1.5; int damage = (int)(((int)((int)(((int)((2 * attacker.getLevel()) / 5.0 + 2.0) * attack * move.getPower()) / defence) / 50.0) + 2) * (random / 100.0) * (stab ? stabFactor : 1.0) * multiplier); if (isCritical) { damage *= attacker.hasAbility("Sniper") ? 3 : 2; if (defender.hasAbility("Anger Point")) { if (!silent) { field.showMessage(defender.getName() + "'s Anger Point raised its attack!"); } StatChangeEffect eff = new StatChangeEffect( Pokemon.S_ATTACK,true, 12); eff.setDescription(null); defender.addStatus(defender, eff); } if (!silent) { field.showMessage("A critical hit!"); } } return ((damage < 1) ? 1 : damage); } /** * There are several conditions to validate. The total number of effort * points must be less than or equal to 510. There can be no more than 255 * effort points per stat. There can be no more than 31 individual * points per stat. The pokemon's level must be in the interval [1, 100]. */ public void validateHiddenStats(Pokemon p) throws ValidationException { int level = p.getLevel(); if ((level < 1) || (level > 100)) throw new ValidationException("Level must be between 1 and 100."); int evs = 0; for (int i = 0; i < 6; ++i) { int ev = p.getEv(i); evs += ev; if (ev > 255) throw new ValidationException( "No stat can be allocated more than 255 EVs."); if (ev < 0) { throw new ValidationException("EVs cannot be negative."); } int iv = p.getIv(i); if (iv > 31) throw new ValidationException( "No stat can be given more than 31 IVs."); if (iv < 0) { throw new ValidationException("IVs cannot be negative."); } } if (evs > 510) { throw new ValidationException( "A pokemon cannot have more than 510 EVs in total."); } } }