package org.pokenet.server.battle.impl; import org.pokenet.server.backend.entity.PlayerChar; import org.pokenet.server.battle.BattleField; import org.pokenet.server.battle.BattleTurn; import org.pokenet.server.battle.Pokemon; import org.pokenet.server.battle.mechanics.BattleMechanics; import org.pokenet.server.battle.mechanics.MoveQueueException; import org.pokenet.server.battle.mechanics.statuses.StatusEffect; 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.feature.TimeService; import org.pokenet.server.network.TcpProtocolHandler; import org.pokenet.server.network.message.battle.BattleEndMessage; import org.pokenet.server.network.message.battle.BattleInitMessage; import org.pokenet.server.network.message.battle.BattleMessage; import org.pokenet.server.network.message.battle.BattleMoveMessage; import org.pokenet.server.network.message.battle.BattleMoveRequest; import org.pokenet.server.network.message.battle.EnemyDataMessage; import org.pokenet.server.network.message.battle.FaintMessage; import org.pokenet.server.network.message.battle.HealthChangeMessage; import org.pokenet.server.network.message.battle.NoPPMessage; import org.pokenet.server.network.message.battle.StatusChangeMessage; import org.pokenet.server.network.message.battle.SwitchMessage; import org.pokenet.server.network.message.battle.SwitchRequest; import org.pokenet.server.network.message.battle.BattleEndMessage.BattleEnd; /** * A battlefield for PvP battles * @author shadowkanji * */ public class PvPBattleField extends BattleField { private PlayerChar[] m_players; private BattleTurn[] m_turn = new BattleTurn[2]; private boolean m_finished = false; /** * Constructor * * @param mech * @param p1 * @param p2 */ public PvPBattleField(BattleMechanics mech, PlayerChar p1, PlayerChar p2) { super(mech, new Pokemon[][] { p1.getParty(), p2.getParty() }); /* * Store the players */ m_players = new PlayerChar[2]; m_players[0] = p1; m_players[1] = p2; /* *Set the player to battling */ p1.setBattling(true); p2.setBattling(true); /* * Set the battlefield for the players */ p2.setBattleField(this); /* * Set battle ids */ p1.setBattleId(0); p2.setBattleId(1); /* * Send battle initialisation packets */ TcpProtocolHandler.writeMessage(p1.getTcpSession(), new BattleInitMessage(false, p2.getPartyCount())); TcpProtocolHandler.writeMessage(p2.getTcpSession(), new BattleInitMessage(false, p1.getPartyCount())); /* Send the enemy's name to both players*/ p1.getTcpSession().write("bn" + p2.getName()); p2.getTcpSession().write("bn" + p1.getName()); /* Send pokemon data to both players */ sendPokemonData(p1, p2); sendPokemonData(p2, p1); /* Apply weather and request moves */ applyWeather(); requestMoves(); } /** * Sends pokemon data for PlayerChar p to receiver * * @param p * @param receiver */ private void sendPokemonData(PlayerChar p, PlayerChar receiver) { for (int i = 0; i < p.getParty().length; i++) { if (p.getParty()[i] != null) { TcpProtocolHandler.writeMessage(receiver.getTcpSession(), new EnemyDataMessage(i, p.getParty()[i])); } } } @Override public void applyWeather() { if (m_players[0].getMap().isWeatherForced()) { switch (m_players[0].getMap().getWeather()) { case NORMAL: return; case RAIN: this.applyEffect(new RainEffect()); return; case HAIL: this.applyEffect(new HailEffect()); return; case SANDSTORM: this.applyEffect(new SandstormEffect()); return; default: return; } } else { FieldEffect f = TimeService.getWeatherEffect(); if (f != null) { this.applyEffect(f); } } } @Override public void clearQueue() { m_turn[0] = null; m_turn[1] = null; } @Override public BattleTurn[] getQueuedTurns() { return m_turn; } @Override public String getTrainerName(int idx) { return m_players[idx].getName(); } @Override public void informPokemonFainted(int trainer, int idx) { if(m_players != null) { TcpProtocolHandler.writeMessage(m_players[0].getTcpSession(), new FaintMessage(getParty(trainer)[idx].getSpeciesName())); TcpProtocolHandler.writeMessage(m_players[1].getTcpSession(), new FaintMessage(getParty(trainer)[idx].getSpeciesName())); } } @Override public void informPokemonHealthChanged(Pokemon poke, int change) { if (m_players != null && poke != null) { if (poke.compareTo(getActivePokemon()[0]) == 0) { TcpProtocolHandler.writeMessage(m_players[0].getTcpSession(), new HealthChangeMessage(0 , change)); TcpProtocolHandler.writeMessage(m_players[1].getTcpSession(), new HealthChangeMessage(1 , change)); } else if(poke.compareTo(getActivePokemon()[1]) == 0) { TcpProtocolHandler.writeMessage(m_players[1].getTcpSession(), new HealthChangeMessage(0 , change)); TcpProtocolHandler.writeMessage(m_players[0].getTcpSession(), new HealthChangeMessage(1 , change)); } else { int index = getPokemonPartyIndex(0, poke); if(index > -1) { m_players[0].getTcpSession().write("Ph" + String.valueOf(index) + poke.getHealth()); //TODO: Add support for enemy pokemon healing for pokemon in pokeballs return; } index = getPokemonPartyIndex(1, poke); if(index > -1) { m_players[1].getTcpSession().write("Ph" + String.valueOf(index) + poke.getHealth()); //TODO: Add support for enemy pokemon healing for pokemon in pokeballs return; } } } } @Override public void informStatusApplied(Pokemon poke, StatusEffect eff) { if(m_finished) return; if (m_players != null && poke != null) { if (poke.compareTo(getActivePokemon()[0]) == 0) { TcpProtocolHandler.writeMessage(m_players[0].getTcpSession(), new StatusChangeMessage(0, poke.getSpeciesName(), eff.getName(), false)); TcpProtocolHandler.writeMessage(m_players[1].getTcpSession(), new StatusChangeMessage(1, poke.getSpeciesName(), eff.getName(), false)); } else if(poke.compareTo(getActivePokemon()[1]) == 0) { TcpProtocolHandler.writeMessage(m_players[0].getTcpSession(), new StatusChangeMessage(1, poke.getSpeciesName(), eff.getName(), false)); TcpProtocolHandler.writeMessage(m_players[1].getTcpSession(), new StatusChangeMessage(0, poke.getSpeciesName(), eff.getName(), false)); } } } @Override public void informStatusRemoved(Pokemon poke, StatusEffect eff) { if(m_finished) return; if (poke != null && m_players != null) { if (poke.compareTo(getActivePokemon()[0]) == 0 && !getActivePokemon()[0].isFainted()) { TcpProtocolHandler.writeMessage(m_players[0].getTcpSession(), new StatusChangeMessage(0, poke.getSpeciesName(), eff.getName(), true)); TcpProtocolHandler.writeMessage(m_players[1].getTcpSession(), new StatusChangeMessage(1, poke.getSpeciesName(), eff.getName(), true)); } else if(poke.compareTo(getActivePokemon()[1]) == 0 && !getActivePokemon()[1].isFainted()) { TcpProtocolHandler.writeMessage(m_players[0].getTcpSession(), new StatusChangeMessage(1, poke.getSpeciesName(), eff.getName(), true)); TcpProtocolHandler.writeMessage(m_players[1].getTcpSession(), new StatusChangeMessage(0, poke.getSpeciesName(), eff.getName(), true)); } } } @Override public void informSwitchInPokemon(int trainer, Pokemon poke) { int pokeIndex = getPokemonPartyIndex(trainer, poke); if(m_players != null) { if (trainer == 0) { TcpProtocolHandler.writeMessage(m_players[0].getTcpSession(), new SwitchMessage(m_players[0].getName(), poke.getSpeciesName(), 0, pokeIndex)); TcpProtocolHandler.writeMessage(m_players[1].getTcpSession(), new SwitchMessage(m_players[0].getName(), poke.getSpeciesName(), 1, pokeIndex)); } else { TcpProtocolHandler.writeMessage(m_players[0].getTcpSession(), new SwitchMessage(m_players[1].getName(), poke.getSpeciesName(), 1, pokeIndex)); TcpProtocolHandler.writeMessage(m_players[1].getTcpSession(), new SwitchMessage(m_players[1].getName(), poke.getSpeciesName(), 0, pokeIndex)); } } } @Override public void informUseMove(Pokemon poke, String name) { if(m_players != null) { TcpProtocolHandler.writeMessage(m_players[0].getTcpSession(), new BattleMoveMessage(poke.getSpeciesName(), name)); TcpProtocolHandler.writeMessage(m_players[1].getTcpSession(), new BattleMoveMessage(poke.getSpeciesName(), name)); } } @Override public void informVictory(int winner) { m_finished = true; m_players[0].removeTempStatusEffects(); m_players[1].removeTempStatusEffects(); if (winner == 0) { TcpProtocolHandler.writeMessage(m_players[0].getTcpSession(), new BattleEndMessage(BattleEnd.WON)); TcpProtocolHandler.writeMessage(m_players[1].getTcpSession(), new BattleEndMessage(BattleEnd.LOST)); m_players[1].lostBattle(); } else { TcpProtocolHandler.writeMessage(m_players[0].getTcpSession(), new BattleEndMessage(BattleEnd.LOST)); TcpProtocolHandler.writeMessage(m_players[1].getTcpSession(), new BattleEndMessage(BattleEnd.WON)); m_players[0].lostBattle(); } m_players[0].setBattling(false); m_players[1].setBattling(false); dispose(); if (m_dispatch != null) { /* * This very bad programming but shoddy does it and forces us to do * it */ //Thread t = m_dispatch; //m_dispatch = null; //t.stop(); let the thread manually return. } } @Override public void queueMove(int trainer, BattleTurn move) throws MoveQueueException { /* Checks the move exists */ if(move.isMoveTurn() && move.getId() != -1 && getActivePokemon()[trainer].getMove(move.getId()) == null) { requestMove(trainer); return; } /* Handle forced switches */ if(m_isWaiting && m_replace != null && m_replace[trainer]) { if(!move.isMoveTurn()) { if(getActivePokemon()[trainer].compareTo(this.getParty(trainer)[move.getId()]) != 0) { this.switchInPokemon(trainer, move.getId()); m_replace[trainer] = false; m_isWaiting = false; return; } } requestPokemonReplacement(trainer); return; } // The trainer has no turn queued. if (m_turn[trainer] == null) { /* Handle Pokemon being unhappy and ignoring you */ if(!getActivePokemon()[trainer].isFainted()) { if(getActivePokemon()[trainer].getHappiness() <= 40) { /* Pokemon is unhappy, they'll do what they feel like */ showMessage(getActivePokemon()[trainer].getSpeciesName() + " is unhappy!"); int moveID = getMechanics().getRandom().nextInt(4); while (getActivePokemon()[trainer].getMove(moveID) == null) moveID = getMechanics().getRandom().nextInt(4); move = BattleTurn.getMoveTurn(moveID); } else if(getActivePokemon()[trainer].getHappiness() < 70) { /* Pokemon is partially unhappy, 50% chance they'll listen to you */ if(getMechanics().getRandom().nextInt(2) == 1) { showMessage(getActivePokemon()[trainer].getSpeciesName() + " is unhappy!"); int moveID = getMechanics().getRandom().nextInt(4); while (getActivePokemon()[trainer].getMove(moveID) == null) moveID = getMechanics().getRandom().nextInt(4); move = BattleTurn.getMoveTurn(moveID); } } } if (move.getId() == -1) { if (m_dispatch == null && ((trainer == 0 && m_turn[1] != null) || (trainer == 1 && m_turn[0] != null))) { m_dispatch = new Thread(new Runnable() { public void run() { executeTurn(m_turn); m_dispatch = null; } }); m_dispatch.start(); return; } } else { // Handle a fainted pokemon if (this.getActivePokemon()[trainer].isFainted()) { if (!move.isMoveTurn() && this.getParty(trainer)[move.getId()] != null && this.getParty(trainer)[move.getId()].getHealth() > 0) { switchInPokemon(trainer, move.getId()); requestMoves(); return; } else { // The player still has pokemon left if (getAliveCount(trainer) > 0) { requestPokemonReplacement(trainer); return; } else { // the player has no pokemon left. Announce winner if (trainer == 0) this.informVictory(1); else this.informVictory(0); return; } } } else { // The turn was used to attack! if (move.isMoveTurn()) { // Handles Struggle if (getActivePokemon()[trainer].mustStruggle()) m_turn[trainer] = BattleTurn.getMoveTurn(-1); else { // The move has no more PP. Tell the client! if (this.getActivePokemon()[trainer].getPp(move .getId()) <= 0) { if (trainer == 0) { TcpProtocolHandler.writeMessage(m_players[0].getTcpSession(), new NoPPMessage(this.getActivePokemon()[trainer] .getMoveName(move.getId()))); } else { TcpProtocolHandler.writeMessage(m_players[1].getTcpSession(), new NoPPMessage(this.getActivePokemon()[trainer] .getMoveName(move.getId()))); } return; } else { // Assign the move to the turn m_turn[trainer] = move; } } } else { if (this.getActivePokemon()[trainer].isActive() && this.getParty(trainer)[move.getId()] != null && this.getParty(trainer)[move.getId()].getHealth() > 0) { m_turn[trainer] = move; } else { requestMove(trainer); return; } } } } } if (m_dispatch != null) return; // Both turns are ready to be performed if (m_turn[0] != null && m_turn[1] != null) { m_dispatch = new Thread(new Runnable() { public void run() { executeTurn(m_turn); for (int i = 0; i < m_participants; ++i) { m_turn[i] = null; } m_dispatch = null; } }); m_dispatch.start(); } } @Override public void refreshActivePokemon() { if(m_players != null) { m_players[0].getTcpSession().write( "bh0" + this.getActivePokemon()[0].getHealth()); m_players[0].getTcpSession().write( "bh1" + this.getActivePokemon()[1].getHealth()); m_players[1].getTcpSession().write( "bh0" + this.getActivePokemon()[1].getHealth()); m_players[1].getTcpSession().write( "bh1" + this.getActivePokemon()[0].getHealth()); } } @Override public void requestAndWaitForSwitch(int party) { requestPokemonReplacement(party); if (!m_replace[party]) { return; } m_isWaiting = true; do { synchronized (m_dispatch) { try { m_dispatch.wait(1000); } catch (InterruptedException e) { } } } while ((m_replace != null) && m_replace[party]); } @Override protected void requestMove(int trainer) { TcpProtocolHandler.writeMessage(m_players[trainer].getTcpSession(), new BattleMoveRequest()); } @Override protected void requestMoves() { clearQueue(); if (this.getActivePokemon()[0].isActive() && this.getActivePokemon()[1].isActive()) { TcpProtocolHandler.writeMessage(m_players[0].getTcpSession(), new BattleMoveRequest()); TcpProtocolHandler.writeMessage(m_players[1].getTcpSession(), new BattleMoveRequest()); } } @Override protected void requestPokemonReplacement(int i) { TcpProtocolHandler.writeMessage(m_players[i].getTcpSession(), new SwitchRequest()); } @Override public void showMessage(String message) { if(m_finished) return; if(m_players != null) { if(m_players[0] != null) TcpProtocolHandler.writeMessage(m_players[0].getTcpSession(), new BattleMessage(message)); if(m_players[1] != null) TcpProtocolHandler.writeMessage(m_players[1].getTcpSession(), new BattleMessage(message)); } } /** * Handles a disconnect from a player * Rewards other player with some money * @param trainer */ public void disconnect(int trainer) { if(m_players != null) { if(trainer == 0) { /* if(m_players[0].getMoney() >= 100) { m_players[0].setMoney(m_players[0].getMoney() - 100); m_players[1].setMoney(m_players[1].getMoney() + 100); } else { m_players[1].setMoney(m_players[1].getMoney() + m_players[0].getMoney()); m_players[0].setMoney(0); } m_players[1].updateClientMoney(); */ TcpProtocolHandler.writeMessage(m_players[1].getTcpSession(), new BattleEndMessage(BattleEnd.WON)); m_players[1].setBattling(false); } else { /* if(m_players[1].getMoney() >= 100) { m_players[1].setMoney(m_players[0].getMoney() - 100); m_players[0].setMoney(m_players[1].getMoney() + 100); } else { m_players[0].setMoney(m_players[1].getMoney() + m_players[0].getMoney()); m_players[1].setMoney(0); } m_players[0].updateClientMoney(); */ TcpProtocolHandler.writeMessage(m_players[0].getTcpSession(), new BattleEndMessage(BattleEnd.WON)); m_players[0].setBattling(false); } } } @Override public void forceExecuteTurn() { if(m_turn[0] == null) { m_turn[0] = BattleTurn.getMoveTurn(-1); } if(m_turn[1] == null) { m_turn[1] = BattleTurn.getMoveTurn(-1); } executeTurn(m_turn); } }