package net.sf.colossus.gui; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.GridLayout; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.ArrayList; import java.util.List; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.WindowConstants; import net.sf.colossus.client.IOracle; import net.sf.colossus.common.Constants; import net.sf.colossus.common.IOptions; import net.sf.colossus.common.Options; import net.sf.colossus.game.Legion; import net.sf.colossus.guiutil.KDialog; import net.sf.colossus.guiutil.SaveWindow; import net.sf.colossus.server.VariantSupport; /** * Post-engagement status dialog. * * It collects the results of all battles that are send by 'addData()`. * * * @author Towi * @author David Ripton */ final class EngagementResults extends KDialog { private IOracle oracle; private IOptions options; private int current = -1; private int lastSeen = -1; private final List<Engagement> engagementLog = new ArrayList<Engagement>(); private final SaveWindow saveWindow; private JButton firstButton; private JButton prevButton; private JButton nextButton; private JButton lastButton; private JLabel summaryLabel; private JLabel resultLabel; private JLabel attackerIdLabel; private JLabel defenderIdLabel; private JPanel panelCenter; private boolean moveNext; private boolean advanceToLast = false; /** * Inits the dialog, not opens it. * * @param frame is the parent window * @param oracle gives us information */ EngagementResults(final JFrame frame, final IOracle oracle, final IOptions options) { super(frame, "Engagement Status", false); setFocusable(false); this.oracle = oracle; this.options = options; setBackground(Color.lightGray); setupGUI(); setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE); this.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { options.setOption(Options.showEngagementResults, false); } }); this.saveWindow = new SaveWindow(options, "EngagementResultView"); saveWindow.restore(this, new Point(0, 0)); maybeShow(); } /** * Adds a log record to the list of logged engagements. * * Now the dialog moves to the new engagement if either * - the engagement happens in the attacker's turn * - it is the first one after the attacker's turn * The idea behind this design is that the dialog content moves along with * the player when the player is in charge of the game tempo, but if the player * is only passive the engagements stop moving until the player takes control * by either continuing to play or by clicking the next button. * * TODO: see if xxxStartingCertainities can somehow get values * of better quality. * * @param attackerStartingContents - imagew names, * result from oracle.getLegionImageNames * @param defenderStartingContents - imagew names, * result from oracle.getLegionImageNames * @param attackerStartingCertainities - list of Booleans, * for overlay ?-marks * @param defenderStartingCertainities - list of Booleans, * for overlay ?-marks * @param attackersTurn should be set to true if the engagement happened * in the attackers master board turn. The engagement dialog will be moved * to this engagement, the same will happen with the next */ void addData( Legion winner, // null on mutual elim, flee, concede, negotiate String method, int points, int turns, List<String> attackerStartingContents, List<String> defenderStartingContents, List<Boolean> attackerStartingCertainities, List<Boolean> defenderStartingCertainities, boolean attackersTurn) { Engagement result = new Engagement(winner, method, points, turns, attackerStartingContents, defenderStartingContents, attackerStartingCertainities, defenderStartingCertainities, oracle); this.engagementLog.add(result); if (this.current <= -1) { this.current = 0; } if (attackersTurn || this.moveNext || advanceToLast) { this.current = this.engagementLog.size() - 1; } // iff we are in the attackers turn, the next engagement // should be placed automatically, too this.moveNext = attackersTurn; showCurrent(); maybeShow(); } /** like toString into a swing component. * the current rough layout is: * <pre> * ### Content:BorderLayout ######################## * # +--North:GridLayout(n,1)--------------------+ # * # | Label_1 | # * # | Label_2 | # * # | ... | # * # | Label_n | # * # +-------------------------------------------+ # * #===============================================# * # +West:Grid(4,1)-+ % +-Center:Grid(4,1)----+ # * # | Label_bef_att | % | ImageList_bef_att | # * # | Label_bef_def | % | ImageList_bef_def | # * # | Label_aft_att | % | ImageList_aft_att | # * # | Label_aft_def | % | ImageList_aft_def | # * # +---------------+ % +---------------------+ # * #===============================================# * # +-South:FlowLayout(left)--------------------+ # * # | -buttons- | # * # +-------------------------------------------+ # * ################################################# * </pre> */ private void setupGUI() { Container contentPane = this.getContentPane(); contentPane.setLayout(new BorderLayout()); // space for Labels JPanel panelNorth = new JPanel(); panelNorth.setLayout(new GridLayout(2, 1, 0, 2)); panelNorth.setBackground(Color.GRAY); contentPane.add(panelNorth, BorderLayout.NORTH); // space for imagelists this.panelCenter = new JPanel(); panelCenter.setLayout(new GridLayout(3, 1, 0, 2)); contentPane.add(panelCenter, BorderLayout.CENTER); // space for list labels JPanel panelWest = new JPanel(); panelWest.setLayout(new GridLayout(6, 1, 0, 2)); contentPane.add(panelWest, BorderLayout.WEST); // space for navigate buttons JPanel panelSouth = new JPanel(); panelSouth.setLayout(new FlowLayout(FlowLayout.LEFT)); contentPane.add(panelSouth, BorderLayout.SOUTH); panelSouth.setBackground(Color.GRAY); // // add elements // // north this.summaryLabel = new JLabel(); this.resultLabel = new JLabel(); panelNorth.add(this.summaryLabel); panelNorth.add(this.resultLabel); // west this.attackerIdLabel = new JLabel(); this.attackerIdLabel.setForeground(Color.BLUE); this.defenderIdLabel = new JLabel(); this.defenderIdLabel.setForeground(Color.BLUE); panelWest.add(new JLabel("Attacker")); panelWest.add(this.attackerIdLabel); panelWest.add(new JLabel("Defender")); panelWest.add(this.defenderIdLabel); panelWest.add(new JLabel("Winner")); // center gets only prepared for the chits // south this.firstButton = new JButton("First"); firstButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { current = 0; advanceToLast = false; showCurrent(); } }); panelSouth.add(firstButton); this.prevButton = new JButton("Previous"); prevButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { current--; advanceToLast = false; showCurrent(); } }); panelSouth.add(prevButton); this.nextButton = new JButton("Next"); nextButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { current++; advanceToLast = false; showCurrent(); } }); panelSouth.add(nextButton); this.lastButton = new JButton("Last"); lastButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { current = engagementLog.size() - 1; advanceToLast = true; showCurrent(); } }); panelSouth.add(lastButton); JButton hideButton = new JButton("Hide"); hideButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setVisible(false); } }); panelSouth.add(hideButton); this.firstButton.setEnabled(false); this.prevButton.setEnabled(false); this.nextButton.setEnabled(false); this.lastButton.setEnabled(false); } private Component createLegionComponent(Legion legion, List<String> imageNames, List<Boolean> certainList, boolean isDefender) { // prepare my box Box panel = Box.createHorizontalBox(); if (isDefender) { panel.setBackground(Color.WHITE); } int scale = 3 * Scale.get(); // or 3 or 4? // add marker Marker marker = new Marker(legion, scale, legion.getLongMarkerId()); panel.add(marker); panel.add(Box.createHorizontalStrut(5)); // towi: you want it upside down or not? // if yes, then then use the "isDefender" line instead. final boolean inverse = false; // final boolean inverse = isDefender; // add chits int idx = 0; for (String imageName : imageNames) { final Boolean chitCertain = certainList.get(idx); final boolean showDubious = !chitCertain.booleanValue(); Chit chit = new Chit(scale, imageName, inverse, showDubious); panel.add(chit); idx += 1; } // make list always 7 wide with invisible chits. not perfect. for (int i = idx; i < 7; i++) { panel.add(Box.createRigidArea(new Dimension(scale, scale))); } // expand space panel.add(Box.createHorizontalGlue()); return panel; } private void showCurrent() { if (engagementLog.size() == 0) { // TODO: this shrinks the dialog to a tiny size. // for this reason we disallowed dropping // the last in the action handler. // Container contentPane = getContentPane(); // contentPane.removeAll(); this.setTitle("no Engagements"); this.firstButton.setEnabled(false); this.prevButton.setEnabled(false); this.nextButton.setEnabled(false); this.lastButton.setEnabled(false); } else { Engagement result = engagementLog.get(current); this.setTitle("Engagement " + (current + 1) + " of " + engagementLog.size()); this.summaryLabel.setText(result.getSummary()); this.resultLabel.setText(result.getResultText()); this.attackerIdLabel.setText(result.attacker.getMarkerId()); this.defenderIdLabel.setText(result.defender.getMarkerId()); this.firstButton.setEnabled(current != 0); this.prevButton.setEnabled(current != 0); this.nextButton.setEnabled(current != engagementLog.size() - 1); this.lastButton.setEnabled(current != engagementLog.size() - 1); this.panelCenter.removeAll(); this.panelCenter.add(createLegionComponent(result.attacker, result.attackerStartingContents, result.attackerStartingCertainities, false)); this.panelCenter.add(createLegionComponent(result.defender, result.defenderStartingContents, result.defenderStartingCertainities, true)); if (result.attacker.equals(result.winner)) { this.panelCenter.add(createLegionComponent(result.attacker, result.attackerEndingContents, result.attackerEndingCertainties, false)); } else if (result.defender.equals(result.winner)) { this.panelCenter.add(createLegionComponent(result.defender, result.defenderEndingContents, result.defenderEndingCertainties, true)); } else { this.panelCenter.add(new JPanel()); } this.lastSeen = Math.max(this.lastSeen, this.current); } } void maybeShow() { if (options.getOption(Options.showEngagementResults)) { pack(); if (!isVisible()) { setVisible(true); } } else { if (isVisible()) { setVisible(false); } } } @Override public void dispose() { saveWindow.save(this); super.dispose(); this.options = null; this.oracle = null; } @Override public void setVisible(boolean visible) { saveWindow.save(this); super.setVisible(visible); } /** * Stores information about an engagement. * * TODO this should probably be a class in the game package */ private class Engagement { Legion winner; // null on mutual elim, flee, concede, negotiate private Legion loser; final Legion attacker; private final Legion defender; private final String method; private final int points; private final int turns; private final List<String> attackerStartingContents; private final List<String> defenderStartingContents; final List<Boolean> attackerStartingCertainities; final List<Boolean> defenderStartingCertainities; private final String hexLabel; private final int gameTurn; private final List<String> attackerEndingContents; private final List<String> defenderEndingContents; final List<Boolean> attackerEndingCertainties; final List<Boolean> defenderEndingCertainties; public Engagement(Legion winner, String method, int points, int turns, List<String> attackerStartingContents, List<String> defenderStartingContents, List<Boolean> attackerStartingCertainities, List<Boolean> defenderStartingCertainities, IOracle oracle) { this.winner = winner; this.method = method; this.points = points; this.turns = turns; this.attackerStartingContents = attackerStartingContents; this.defenderStartingContents = defenderStartingContents; this.attackerStartingCertainities = attackerStartingCertainities; this.defenderStartingCertainities = defenderStartingCertainities; this.hexLabel = oracle.getEngagement().getLocation().getLabel(); this.attacker = oracle.getEngagement().getAttackingLegion(); this.defender = oracle.getEngagement().getDefendingLegion(); this.gameTurn = oracle.getTurnNumber(); this.attackerEndingContents = oracle .getLegionImageNames(this.attacker); this.defenderEndingContents = oracle .getLegionImageNames(this.defender); this.attackerEndingCertainties = oracle .getLegionCreatureCertainties(this.attacker); this.defenderEndingCertainties = oracle .getLegionCreatureCertainties(this.defender); this.setWinnerAndLoserId(); } public String getSummary() { return "On turn " + this.gameTurn + ", " + this.attacker + " attacked " + this.defender + " in " + VariantSupport.getCurrentVariant().getMasterBoard() .getHexByLabel(this.hexLabel).getDescription(); } private void setWinnerAndLoserId() { this.loser = null; if (this.winner != null) { if (this.winner.equals(this.attacker)) { this.loser = this.defender; } else if (this.winner.equals(this.defender)) { this.loser = this.attacker; } else { // TODO is this case possible at all? What does it mean? this.winner = null; } } } public String getResultText() { String result = "bogus method"; if (method.equals(Constants.erMethodFlee)) { result = winner + " won when " + loser + " fled and earned " + this.points + " points"; } else if (method.equals(Constants.erMethodConcede)) { result = winner + " won when " + loser + " conceded and earned " + this.points + " points"; } else if (method.equals(Constants.erMethodConcede)) { if (winner != null) { result = winner + " won a negotiated settlement and earned " + this.points + " points"; } else { result = "Negotiated mutual elimination"; } } else if (method.equals(Constants.erMethodFight)) { if (winner != null) { if (turns > 7) { result = winner + " won the battle by time loss" + " and earned " + this.points + " points"; } else { result = winner + " won the battle in " + this.turns + " turns and earned " + this.points + " points"; } } else { result = "Mutual elimination in " + this.turns + " turns"; } } return result; } } }