package net.sf.colossus.gui; import java.awt.Color; import java.awt.Container; import java.awt.Dimension; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.util.Iterator; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.KeyStroke; import javax.swing.WindowConstants; import net.sf.colossus.client.LegionClientSide; import net.sf.colossus.common.Options; import net.sf.colossus.game.EntrySide; import net.sf.colossus.game.Legion; import net.sf.colossus.guiutil.KDialog; import net.sf.colossus.guiutil.SaveWindow; import net.sf.colossus.server.LegionServerSide; import net.sf.colossus.variant.MasterHex; /** * Class Concede allows a player to flee or concede before starting a Battle. * * @author David Ripton */ final class Concede extends KDialog { private static final Logger LOGGER = Logger.getLogger(Concede.class .getName()); private final boolean flee; private Point location; private final ClientGUI gui; private final Legion ally; private final Legion attacker; private final Legion defender; private final SaveWindow saveWindow; private final JButton showMapButton; private Concede(ClientGUI clientGui, JFrame parentFrame, Legion ally, Legion enemy, boolean flee) { super(parentFrame, (flee ? "Flee" : "Concede") + " with Legion " + LegionServerSide.getLongMarkerName(ally.getMarkerId()) + " in " + ally.getCurrentHex().getDescription() + "?", false); Container contentPane = getContentPane(); contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.PAGE_AXIS)); this.flee = flee; this.gui = clientGui; this.ally = ally; if (ally.hasMoved()) { this.attacker = ally; this.defender = enemy; } else { this.attacker = enemy; this.defender = ally; } setBackground(Color.lightGray); // Don't allow closing without explicit decision: setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); MasterHex hex = defender.getCurrentHex(); EntrySide entrySide = attacker.getEntrySide(); int direction = hex.findDirectionForEntrySide(entrySide); StringBuffer infoText = new StringBuffer(); try { MasterHex neighborHex = hex.getNeighbor(direction); String neighborHexInfo; if (neighborHex != null) { neighborHexInfo = neighborHex.getDescription(); } else { neighborHexInfo = "[No hex there - perhaps teleport?]"; } infoText.append(attacker.getMarkerId() + " attacks "); infoText.append(defender.getMarkerId() + " in "); infoText.append(hex.getDescription() + ", entering from "); infoText.append(attacker.getEntrySide().getLabel() + " ("); infoText.append(neighborHexInfo + ")"); } catch (Exception e) { LOGGER .log(Level.WARNING, "Exception during infoTextCreation: ", e); } JLabel label1 = new JLabel(infoText.toString()); contentPane.add(label1); label1.setAlignmentX(CENTER_ALIGNMENT); Box allyPane = showLegion(ally, true); Box enemyPane = showLegion(enemy, false); Box legionsPane = new Box(BoxLayout.PAGE_AXIS); legionsPane.setAlignmentX(CENTER_ALIGNMENT); legionsPane.add(allyPane); legionsPane.add(enemyPane); getContentPane().add(legionsPane); int resultingPoints = ((LegionClientSide)ally).getPointValue(); if (flee) { resultingPoints /= 2; } int score = enemy.getPlayer().getScore(); int newScore = score + resultingPoints; StringBuffer infoText2 = new StringBuffer(""); infoText2.append("This will give player " + enemy.getPlayer().getName() + " " + resultingPoints + " points, " + "i.e. increase the score from " + score + " to " + newScore + "."); JLabel label2 = new JLabel(infoText2.toString()); contentPane.add(label2); contentPane.add(label2); label2.setAlignmentX(CENTER_ALIGNMENT); JPanel buttonPane = new JPanel(); buttonPane.setAlignmentX(CENTER_ALIGNMENT); contentPane.add(buttonPane); showMapButton = new JButton("Show Battle Map"); showMapButton.setMnemonic(KeyEvent.VK_M); getRootPane().setDefaultButton(showMapButton); buttonPane.add(showMapButton); showMapButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { GUIMasterHex hex = gui.getBoard().getGUIHexByMasterHex( attacker.getCurrentHex()); gui.getBoard().showBattleMap(hex); } }); JButton doItButton = new JButton(flee ? "Flee" : "Concede"); doItButton.setMnemonic(flee ? KeyEvent.VK_F : KeyEvent.VK_C); buttonPane.add(doItButton); doItButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { processAnswer(true); } }); JButton dontDoitButton = new JButton(flee ? "Don't Flee" : "Don't Concede"); dontDoitButton.setMnemonic(KeyEvent.VK_D); buttonPane.add(dontDoitButton); dontDoitButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { processAnswer(false); } }); Action doInstead = new AbstractAction() { public void actionPerformed(ActionEvent e) { Concede.this.notifyConcede(); } }; // Prevent that space triggers the buttons. This frequently happened // when players type in the chat while the concede/flee dialog becomes // displayed, causing them accidentally to flee or concede. preventSpaceAction(doItButton, doInstead); preventSpaceAction(dontDoitButton, doInstead); preventSpaceAction(showMapButton, doInstead); pack(); saveWindow = new SaveWindow(gui.getOptions(), "Concede"); if (location == null) { location = saveWindow.loadLocation(); } if (location == null) { centerOnScreen(); location = getLocation(); } else { setLocation(location); } setVisible(true); repaint(); } /** * Prevent Space from triggering the firing of the action normally * associated to the given button; trigger the given action instead. * The "normal" action of the button can still be activated with mouse * click or with the mnemonic key (on windows with Alt + the underlined * character) * @param button * @param newAction */ private void preventSpaceAction(JButton button, Action newAction) { button.getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "doInstead"); button.getActionMap().put("doInstead", newAction); } /** * Make the user aware that there is a concede or flee dialog waiting * for response. This is called by the doInsteadAction (which is fired * when SPACE or ENTER is pressed). * Make a beep, requests focus and puts it to front, and sets as default * action the show Battle Map button. */ public void notifyConcede() { this.toFront(); this.requestFocus(); this.showMapButton.requestFocusInWindow(); gui.getBoard().getToolkit().beep(); } private Box showLegion(Legion legion, boolean dead) { Box pane = new Box(BoxLayout.X_AXIS); pane.setAlignmentX(LEFT_ALIGNMENT); int scale = 4 * Scale.get(); Marker marker = new Marker(legion, scale, legion.getLongMarkerId(), gui.getClient(), true); pane.add(marker); pane.add(Box.createRigidArea(new Dimension(scale / 4, 0))); int points = ((LegionClientSide)legion).getPointValue(); Box pointsPanel = new Box(BoxLayout.PAGE_AXIS); pointsPanel.setSize(marker.getSize()); pointsPanel.add(new JLabel("" + points)); pointsPanel.add(new JLabel("points")); pane.add(pointsPanel); pane.add(Box.createRigidArea(new Dimension(scale / 4, 0))); List<String> imageNames = gui.getGameClientSide().getLegionImageNames( legion); Iterator<String> it = imageNames.iterator(); while (it.hasNext()) { String imageName = it.next(); Chit chit = Chit.newCreatureChit(scale, imageName); chit.setDead(dead); pane.add(chit); } return pane; } private static Concede currentDialog = null; static void concede(ClientGUI gui, JFrame parentFrame, Legion ally, Legion enemy) { currentDialog = new Concede(gui, parentFrame, ally, enemy, false); } static void flee(ClientGUI gui, JFrame parentFrame, Legion ally, Legion enemy) { currentDialog = new Concede(gui, parentFrame, ally, enemy, true); } /* * the argument is the yes or no; which type it is (flee or concede) * is based on which type the last opened dialog was */ static void inactivityAutoFleeOrConcede(boolean reply) { if (currentDialog == null) { LOGGER .severe("inactivityAutoFleeOrConcede called, but currentDialog is null???"); } Concede tmp = currentDialog; currentDialog = null; tmp.finishUp(reply); } public Legion getAttacker() { return this.attacker; } private void processAnswer(boolean answer) { // Feature Request #223, confirm before allowing the player to concede // if their Titan is in the legion if (!flee && ally.hasTitan() && answer == true && gui.getOptions().getOption(Options.confirmConcedeWithTitan, true)) { String message = "Are you sure you want to concede? This legion " + "contains your Titan, and conceding will cause you to lose " + "the game!"; String[] options = new String[] { "Yes", "No", "Don't ask again" }; int confirmAnswer = JOptionPane.showOptionDialog(this, message, "Confirm Concession With Titan", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[1]); if (confirmAnswer == 1 || confirmAnswer == -1) { // answered "No", abort concede return; } if (confirmAnswer == 2) { // don't ask again gui.getClient().setPreferencesCheckBoxValue( Options.confirmConcedeWithTitan, false); } } finishUp(answer); } private void finishUp(boolean answer) { location = getLocation(); saveWindow.saveLocation(location); dispose(); if (flee) { gui.getCallbackHandler().answerFlee(ally, answer); } else { gui.getCallbackHandler().answerConcede(ally, answer); } } }