package net.sf.colossus.gui; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.FlowLayout; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.border.LineBorder; import javax.swing.border.TitledBorder; import net.sf.colossus.util.HTMLColor; /** * Class BattleDice displays dice rolls during a battle. * * @author David Ripton * @author Romain Dolbeau */ final class BattleDice extends Box { private static class DiceEntry { String battlePhaseDesc = ""; String attackerDesc = ""; String strikerDesc = ""; String targetDesc = ""; int numDice = 0; int targetNumber = 0; final List<String> rolls = new ArrayList<String>(); int averageMiss = -1; } private Chit[] dice; private final JPanel diceBox, missBox, hitBox; private final JLabel attackerText; private final TitledBorder diceBoxTitledBorder; private final List<DiceEntry> diceResults = new ArrayList<DiceEntry>(); private int currentEntry = 0; BattleDice() { super(BoxLayout.Y_AXIS); setVisible(false); setBackground(Color.lightGray); attackerText = new JLabel(); attackerText.setAlignmentX(Component.CENTER_ALIGNMENT); add(attackerText); diceBox = new JPanel(); diceBox.setLayout(new FlowLayout()); diceBoxTitledBorder = new TitledBorder(""); diceBoxTitledBorder.setTitleJustification(TitledBorder.CENTER); diceBox.setBorder(diceBoxTitledBorder); missBox = new JPanel(); hitBox = new JPanel(); missBox.setLayout(new FlowLayout()); missBox.setBorder(new LineBorder(HTMLColor.blue)); hitBox.setBorder(new LineBorder(HTMLColor.red)); hitBox.setLayout(new FlowLayout()); diceBox.add(missBox); diceBox.add(hitBox); add(diceBox); diceResults.add(new DiceEntry()); setVisible(true); showLastRoll(); } void addValues(String battlePhaseDesc, String attackerDesc, String strikerDesc, String targetDesc, int targetNumber, List<String> rolls) { DiceEntry entry = new DiceEntry(); entry.battlePhaseDesc = battlePhaseDesc; entry.attackerDesc = attackerDesc; entry.strikerDesc = strikerDesc; entry.targetDesc = targetDesc; entry.targetNumber = targetNumber; entry.rolls.addAll(rolls); Collections.sort(entry.rolls); entry.numDice = entry.rolls.size(); // for average miss number, let's assume 6-sided roll :-) entry.averageMiss = Math .round((entry.numDice * (targetNumber - 1) / 6.0f)); diceResults.add(entry); } private String getDieImageName(String rollString) { int roll; try { roll = Integer.parseInt(rollString); } catch (NumberFormatException ex) { return null; } StringBuilder basename = new StringBuilder(); if (roll >= targetNumber()) { basename.append("Hit"); } else { basename.append("Miss"); } basename.append(roll); return basename.toString(); } /** Initialize and layout the components, in response to new data. */ void showLastRoll() { currentEntry = diceResults.size() - 1; showCurrentRoll(); } void showCurrentRoll() { attackerText.setText(attackerDesc() + " " + battlePhaseDesc()); diceBox.setVisible(false); hitBox.removeAll(); hitBox.setVisible(false); missBox.removeAll(); missBox.setVisible(false); if (strikerDesc().equals("")) { diceBoxTitledBorder.setTitle("Attack results"); } else { diceBoxTitledBorder.setTitle("(" + currentEntry + " of " + (diceResults.size() - 1) + ") " + strikerDesc() + " attacks " + targetDesc() + " (target number is " + targetNumber() + ")"); } if (numDice() > 0) { dice = new Chit[numDice()]; for (int i = 0; i < numDice(); i++) { String imageName = getDieImageName(rolls().get(i)); if (imageName != null) { dice[i] = Chit.newDiceChit(2 * Scale.get(), imageName); if (averageMiss() > i) { missBox.add(dice[i]); } else { hitBox.add(dice[i]); } } } if (averageMiss() < numDice()) { hitBox.setVisible(true); } if (averageMiss() > 0) { missBox.setVisible(true); } } else { dice = null; } diceBox.invalidate(); diceBox.setVisible(true); } private int averageMiss() { return getCurrentResults().averageMiss; } private List<String> rolls() { return getCurrentResults().rolls; } private int numDice() { return getCurrentResults().numDice; } private int targetNumber() { return getCurrentResults().targetNumber; } private DiceEntry getCurrentResults() { assert diceResults.size() > 0; return diceResults.get(currentEntry); } private String targetDesc() { return getCurrentResults().targetDesc; } private String attackerDesc() { return getCurrentResults().attackerDesc; } private String battlePhaseDesc() { return getCurrentResults().battlePhaseDesc; } private String strikerDesc() { return getCurrentResults().strikerDesc; } void rescale() { showLastRoll(); } @Override public Dimension getMinimumSize() { return getPreferredSize(); } @Override public Dimension getPreferredSize() { int scale = Scale.get(); return new Dimension(60 * scale, 6 * scale); } /** * Use to set the current roll in the history. Updates the control. * * @param pValue */ public void setCurrentRoll(int pValue) { if (pValue != currentEntry && pValue < diceResults.size()) { currentEntry = pValue; showCurrentRoll(); } } /** * @return number of dice rolls stored in history */ public int getHistoryLength() { return diceResults.size(); } }