package net.sf.colossus.gui; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.util.logging.Level; import java.util.logging.Logger; import net.sf.colossus.client.Client; import net.sf.colossus.common.Constants; import net.sf.colossus.game.BattleUnit; import net.sf.colossus.game.PlayerColor; import net.sf.colossus.util.HTMLColor; public class GUIBattleChit extends Chit { private static final Logger LOGGER = Logger.getLogger(GUIBattleChit.class .getName()); private final BattleUnit battleUnit; private static Font font; private static Font oldFont; private static int fontHeight; private int hits = 0; private final Color color; private static BasicStroke borderStroke; private Rectangle midRect; private Rectangle outerRect; private int strikeNumber; // Number required for successful strike. private int numDice; // modifier for number of Dice rolled. private StrikeDie strikeDie; // Graphical representation of strikeNumber. private StrikeDie strikeAdjDie; // representation of dice gained or lost. private final int scale; // inner scale divided by border thickness private static final int borderRatio = 20; private static boolean useColoredBorders = false; public GUIBattleChit(int scale, String id, boolean inverted, PlayerColor playerColor, Client client, BattleUnit battleUnit) { super(scale, id, inverted, client); if (id == null) { LOGGER.log(Level.WARNING, "Created GUIBattleChit with null id!"); } this.battleUnit = battleUnit; battleUnit.addListener(battleUnit.new Listener() { @Override public void actOnHitOrDeadChanged() { // TODO Auto-generated method stub updateAndRepaint(); } }); this.scale = scale; this.color = HTMLColor.stringToColor(playerColor.getName() + "Colossus"); setBackground(Color.WHITE); } // TODO does asking from BattleUnit / BattleCreature give same result? public String getCreatureName() { String id = getId(); if (id.startsWith(Constants.titan)) { id = Constants.titan; } String buName = battleUnit.getType().getName(); if (!buName.equals(id)) { LOGGER.warning("own name is " + id + " but battleUnit gave us " + buName + "!"); } return id; } public String getDescription() { return battleUnit.getType().getName() + " in " + battleUnit.getCurrentHex().getLabel(); } @Override public String toString() { return getDescription(); } public int getTag() { return battleUnit.getTag(); } public BattleUnit getBattleUnit() { return battleUnit; } public void updateAndRepaint() { this.hits = battleUnit.getHits(); setDead(battleUnit.isDead()); } @Override public void setDead(boolean dead) { if (dead) { // TODO this looks like a bad trick: set chit's hit to 0 so that // no number is painted. Shouldn't that "paint nr or not" rather // in chit itself decide based on "hits" whether to paint the // number? this.hits = 0; } // Chit.setDead() triggers repaint super.setDead(dead); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2 = (Graphics2D)g; if (!isDead()) { // The power and skill are drawn with a font that is one fifth the // size of the Chit. // Construct a font that is two fifths the size of the chit // for drawing Hits and strike die adjustments. // All overlays (hits, dice) have gaps of 2. oldFont = g2.getFont(); String name = oldFont.getName(); int fifthChitSize = (rect.height > rect.width ? (rect.width - 8) / 5 : (rect.height - 8) / 5); int style = oldFont.getStyle(); font = new Font(name, style, fifthChitSize * 2); g2.setFont(font); FontMetrics fontMetrics = g2.getFontMetrics(); fontHeight = fontMetrics.getAscent(); String hitString = Integer.toString(hits); int hitsFontWidth = fontMetrics.stringWidth(hitString); // Setup spaces to show Hits and Strike Target. if (strikeNumber > 0) { Rectangle strikeRect = strikeDie.getBounds(); Point point = new Point(rect.x + rect.width - strikeRect.width - 2, (inverted ? rect.y + fifthChitSize + 4 : rect.y + 2)); strikeDie.setLocation(point); strikeDie.paintComponent(g2); if (numDice != 0) { String diceString = numDice < 0 ? Integer .toString(numDice) : "+" + Integer.toString(numDice); Point dicePoint = new Point(point.x, point.y + (fifthChitSize * 2) + 2); strikeAdjDie.setLocation(dicePoint); strikeAdjDie.paintComponent(g2); g2.setColor(Color.GREEN); g2.drawString(diceString, dicePoint.x, dicePoint.y + strikeRect.height - 2); } } if (hits > 0) { Rectangle hitRect = new Rectangle(rect.x + 2, rect.y + 2 + (inverted ? fifthChitSize + 2 : 0), hitsFontWidth, fontHeight); // Provide a high-contrast background for the number. g2.setColor(Color.WHITE); g2.fillRect(hitRect.x, hitRect.y, hitRect.width, hitRect.height); // Show number of hits taken in red. g2.setColor(Color.RED); g2.drawString(hitString, hitRect.x, hitRect.y + fontHeight); } // Restore the font. g2.setFont(oldFont); } if (useColoredBorders) { // Draw border using player color. g2.setColor(color); g2.setStroke(borderStroke); g2.drawRect(midRect.x, midRect.y, midRect.width, midRect.height); g2.setColor(Color.BLACK); g2.setStroke(oneWide); g2.drawRect(outerRect.x, outerRect.y, outerRect.width, outerRect.height); } } @Override public void setLocation(Point point) { outerRect.setLocation(point); setBounds(outerRect); } @Override public boolean contains(Point point) { return outerRect.contains(point); } @Override public Rectangle getBounds() { return outerRect; } @Override public void setBounds(Rectangle outerRect) { this.outerRect = outerRect; int innerScale = (int)(outerRect.width / (1.0 + 2.0 / borderRatio)); // avoid rescaling if possible if (innerScale > 50 && innerScale < 70) { innerScale = 60; } borderStroke = new BasicStroke( (int)Math.ceil((outerRect.width - innerScale) / 2.0)); Point center = new Point(outerRect.x + outerRect.width / 2, outerRect.y + outerRect.height / 2); rect = new Rectangle(center.x - innerScale / 2, center.y - innerScale / 2, innerScale, innerScale); int midScale = (int)(Math.round((scale + innerScale) / 2.0)); midRect = new Rectangle(center.x - midScale / 2, center.y - midScale / 2, midScale, midScale); } public void setStrikeNumber(int strikeNumber) { this.strikeNumber = strikeNumber; if (strikeNumber > 0) { int fifthChitSize = (rect.height > rect.width ? (rect.width - 8) / 5 : (rect.height - 8) / 5); strikeDie = new StrikeDie(fifthChitSize * 2, strikeNumber, "Hit"); strikeDie.setToolTipText("Test"); this.add(strikeDie); strikeAdjDie = new StrikeDie(fifthChitSize * 2, strikeNumber, "RedBlue"); } else { strikeDie = null; strikeAdjDie = null; } } public void setStrikeDice(int numDice) { this.numDice = numDice; } protected static void setUseColoredBorders(boolean bval) { useColoredBorders = bval; } }