Noble Master Games
Noble Master Games
Navigation
Categories:

Search:

Meta:

Archives:
Dev. Blog
Forums
 

Noble Master Developer's Blog

August 31, 2010

Star Rating Panel for Java Swing

Filed under: Development — noblemaster @ 09:32
Star Rating

Star Rating

I couldn’t find a good star rating component for Java Swing, so I rolled my own. The star rating component displays the current current rating (yellow stars), as well as a “selected” rating (reddish stars). The “selected” rating can be changed by a user. If the “selected” rating is changed, all the listeners will be notified of the change.

Example (5 stars, 3.78 average rating, 3 stars initial selection):

  StarRater starRater = new StarRater(5, 3.61, 3);
  starRater.addStarListener(new StarRater.StarListener() {
    public void handleSelection(int selection) {
      // a new number of stars has been selected
      do something...
    }
  });
  add(starRater);   // add the component to the container...

The star rating component will initially be used to rate maps in Age of Conquest.

Here is the download for both source code & star images (Photoshop). Note that I included the star images as byte array in the source code for your convenience. You are obviously free to use your own as well. Please consider both source code as well as images in the Public Domain. If you would like to credit me for the work that’s great, otherwise, no problem either:

August 24, 2010

King of the Hill for Age of Conquest

Filed under: Development — noblemaster @ 10:55
England Zone

England Zone

Age of Conquest will have a new game type available. King of the Hill will require a player or a team to hold a certain area on the map for a given time to win the game. Target flags will define the zone.

For additional suggestions, please post your ideas here in the blog!

August 19, 2010

Color Swatch & Color Selector for Java

Filed under: Development — noblemaster @ 15:44
Color Swatch (Java)

Color Swatch (Java)

The color selector (JColorChooser) for Java is rather ugly, so I decided to write my own color chooser. The color chooser is made up of two components:

  • ColorSelector: A button which displays the currently selected color and pops up the ColorSwatch when clicked.
  • ColorSwatch:  Displays a color swatch with clickable colors. The color swatch expects a two-dimensional array of colors as input that will be displayed in the swatch.

The color selector and swatch will be used for Age of Conquest to make it more convenient to select a color in the map editor (less cumbersome). Please note, the five columns to the left are specifically designed colors for Age of Conquest to give maps a medieval look & feel. Although other colors can be chosen as well, it is recommended to use the medieval colors when creating new scenarios for the game.

Here is the code for both components. Please consider the code Public Domain. Feel free to use and modify it any way you please. Although, I would appreciate to be credited, it is not necessary to do so. Enjoy!

ColorSelector.java

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.ButtonModel;
import javax.swing.JButton;
import javax.swing.JPopupMenu;

/**
 * Represents a color selector.
 *
 * @author noblemaster
 * @since August 16, 2010
 */
public class ColorSelector extends JButton {

  /** The default colors. */
  private static final int[][] DEFAULT_COLORS = new int[][] {
      { 0xFFFFFF, 0xEEEEEE, 0xDDDDDD, 0xCCCCCC
       , 0xBBBBBB, 0xAAAAAA, 0xFFCC00, 0xFF9900
       , 0xFF6600, 0xFF3300, 0x999999, 0x888888
       , 0x666666, 0x444444, 0x222222, 0x000000 },
      { 0x99CC00, 0x000000, 0x000000, 0x000000
       , 0x000000, 0xCC9900, 0xFFCC33, 0xFFCC66
       , 0xFF9966, 0xFF6633, 0xCC3300, 0x000000
       , 0x000000, 0x000000, 0x000000, 0xCC0033 },
      { 0xCCFF00, 0xCCFF33, 0x333300, 0x666600
       , 0x999900, 0xCCCC00, 0xFFFF00, 0xCC9933
       , 0xCC6633, 0x330000, 0x660000, 0x990000
       , 0xCC0000, 0xFF0000, 0xFF3366, 0xFF0033 },
      { 0x99FF00, 0xCCFF66, 0x99CC33, 0x666633
       , 0x999933, 0xCCCC33, 0xFFFF33, 0x996600
       , 0x993300, 0x663333, 0x993333, 0xCC3333
       , 0xFF3333, 0xCC3366, 0xFF6699, 0xFF0066 },
      { 0x66FF00, 0x99FF66, 0x66CC33, 0x669900
       , 0x999966, 0xCCCC66, 0xFFFF66, 0x996633
       , 0x663300, 0x996666, 0xCC6666, 0xFF6666
       , 0x990033, 0xCC3399, 0xFF66CC, 0xFF0099 },
      { 0x33FF00, 0x66FF33, 0x339900, 0x66CC00
       , 0x99FF33, 0xCCCC99, 0xFFFF99, 0xCC9966
       , 0xCC6600, 0xCC9999, 0xFF9999, 0xFF3399
       , 0xCC0066, 0x990066, 0xFF33CC, 0xFF00CC },
      { 0x00CC00, 0x33CC00, 0x336600, 0x669933
       , 0x99CC66, 0xCCFF99, 0xFFFFCC, 0xFFCC99
       , 0xFF9933, 0xFFCCCC, 0xFF99CC, 0xCC6699
       , 0x993366, 0x660033, 0xCC0099, 0x330033 },
      { 0x33CC33, 0x66CC66, 0x00FF00, 0x33FF33
       , 0x66FF66, 0x99FF99, 0xCCFFCC, 0xFFFFE3
       , 0xFFFFFF, 0xFFE3FF, 0xCC99CC, 0x996699
       , 0x993399, 0x990099, 0x663366, 0x660066 },
      { 0x006600, 0x336633, 0x009900, 0x339933
       , 0x669966, 0x99CC99, 0xE3FFFF, 0xFFFFFF
       , 0xFFFFFF, 0xFFCCFF, 0xFF99FF, 0xFF66FF
       , 0xFF33FF, 0xFF00FF, 0xCC66CC, 0xCC33CC },
      { 0x003300, 0x00CC33, 0x006633, 0x339966
       , 0x66CC99, 0x99FFCC, 0xCCFFFF, 0x3399FF
       , 0x99CCFF, 0xCCCCFF, 0xCC99FF, 0x9966CC
       , 0x663399, 0x330066, 0x9900CC, 0xCC00CC },
      { 0x00FF33, 0x33FF66, 0x009933, 0x00CC66
       , 0x33FF99, 0x99FFFF, 0x99CCCC, 0x0066CC
       , 0x6699CC, 0x9999FF, 0x9999CC, 0x9933FF
       , 0x6600CC, 0x660099, 0xCC33FF, 0xCC00FF },
      { 0x00FF66, 0x66FF99, 0x33CC66, 0x009966
       , 0x66FFFF, 0x66CCCC, 0x669999, 0x003366
       , 0x336699, 0x6666FF, 0x6666CC, 0x666699
       , 0x330099, 0x9933CC, 0xCC66FF, 0x9900FF },
      { 0x00FF99, 0x66FFCC, 0x33CC99, 0x33FFFF
       , 0x33CCCC, 0x339999, 0x336666, 0x006699
       , 0x003399, 0x3333FF, 0x3333CC, 0x333399
       , 0x333366, 0x6633CC, 0x9966FF, 0x6600FF },
      { 0x00FFCC, 0x33FFCC, 0x00FFFF, 0x00CCCC
       , 0x009999, 0x006666, 0x003333, 0x3399CC
       , 0x3366CC, 0x0000FF, 0x0000CC, 0x000099
       , 0x000066, 0x000033, 0x6633FF, 0x3300FF },
      { 0x00CC99, 0x000000, 0x000000, 0x000000
       , 0x000000, 0x0099CC, 0x33CCFF, 0x66CCFF
       , 0x6699FF, 0x3366FF, 0x0033CC, 0x000000
       , 0x000000, 0x000000, 0x000000, 0x3300CC },
      { 0x000000, 0x222222, 0x444444, 0x666666
       , 0x888888, 0x999999, 0x00CCFF, 0x0099FF
       , 0x0066FF, 0x0033FF, 0xAAAAAA, 0xBBBBBB
       , 0xCCCCCC, 0xDDDDDD, 0xEEEEEE, 0xFFFFFF },
  };

  /** The rollover color. */
  private static final Color ROLLOVER_COLOR = new Color(0x20ffffff, true);

  /** The current color. */
  private Color color;
  /** The available colors. */
  private Color[][] colors;

  /**
   * The constructor.
   */
  public ColorSelector() {
    this(Color.RED, getDefaultColors());
  }

  /**
   * The constructor.
   *
   * @param color  The active color.
   * @param colors  The colors to select from.
   */
  public ColorSelector(Color color, Color[][] colors) {
    this.color = color;
    this.colors = colors;

    // set background
    setOpaque(false);

    // listen to clicks and display popup as needed
    addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent arg0) {
        // create and show swatch
        final JPopupMenu popup = new JPopupMenu();
        popup.setOpaque(false);
        ColorSwatch swatch = new ColorSwatch(ColorSelector.this.colors);
        swatch.addColorListener(new ColorSwatch.ColorListener() {
          public void handleColor(Color color) {
            // set the new color
            if (color != null) {
              ColorSelector.this.setColor(color);
            }

            // hide the popup
            popup.setVisible(false);
          }
        });
        popup.add(swatch);
        popup.show(ColorSelector.this, getWidth() / 2, getHeight() / 2);
      }
    });
  }

  /**
   * Returns the color.
   *
   * @return  The color.
   */
  public Color getColor() {
    return color;
  }

  /**
   * Sets the color.
   *
   * @param color  The color.
   */
  public void setColor(Color color) {
    this.color = color;
  }

  /**
   * Returns the colors.
   *
   * @return  The colors.
   */
  public Color[][] getColors() {
    return colors;
  }

  /**
   * Sets the colors.
   *
   * @param colors  The colors.
   */
  public void setColors(Color[][] colors) {
    this.colors = colors;
  }

  /**
   * Returns the base colors.
   *
   * @return  The base colors.
   */
  public static Color[][] getDefaultColors() {
    Color[][] colors = new Color[DEFAULT_COLORS.length][DEFAULT_COLORS[0].length];
    for (int y = 0; y < colors.length; y++) {
      for (int x = 0; x < colors[0].length; x++) {
        colors[y][x] = new Color(DEFAULT_COLORS[y][x]);
      }
    }
    return colors;
  }

  /**
   * Returns the preferred size.
   *
   * @return  The preferred size.
   */
  @Override
  public Dimension getPreferredSize() {
    return new Dimension(60, 30);
  }

  /**
   * Returns the margin.
   *
   * @return  The margin.
   */
  private int margin() {
    return 3;
  }

  /**
   * Draws this component.
   *
   * @param g  Where to draw to.
   */
  @Override
  public void paint(Graphics g) {
    // use antialiasing
    Graphics2D g2 = (Graphics2D)g;
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                        RenderingHints.VALUE_ANTIALIAS_ON);
    int width = getWidth();
    int height = getHeight();
    int x = 0;
    int y = 0;
    int margin = margin();

    // draw border
    getBorder().paintBorder(this, g, 0, 0, width - 1, height - 1); 

    // draw the color
    g.setColor(color);
    g.fillRoundRect(x + margin, y + margin
                 , width - (2 * margin), height - (2 * margin), 5, 5);

    // draw effect as need
    ButtonModel model = getModel();
    if (model.isPressed()) {
      g.setColor(ROLLOVER_COLOR);
      g.fillRoundRect(x + margin, y + margin
                 , width - (2 * margin), height - (2 * margin), 5, 5);
      g.fillRoundRect(x + margin, y + margin
                , width - (2 * margin), height - (2 * margin), 5, 5);
    }
    else if (model.isRollover()) {
      g.setColor(ROLLOVER_COLOR);
      g.fillRoundRect(x + margin, y + margin
               , width - (2 * margin), height - (2 * margin), 5, 5);
    }
  }
}

ColorSwatch.java:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.event.MouseInputListener;

/**
 * Displays a color swatch.
 *
 * @author noblemaster
 * @since August 16, 2010
 */
public class ColorSwatch extends JPanel {

  /** The rollover color. */
  private static final Color ROLLOVER_COLOR = new Color(0x80ffffff, true);

  /** The listener. */
  public static interface ColorListener {

    /**
     * Called if a color has been activated.
     *
     * @param color  The color.
     */
    void handleColor(Color color);
  }

  /** The listeners. */
  private transient List
 listeners = new ArrayList();

  /** The colors. */
  private Color[][] colors;

  /** The rollover color. */
  private Color rolloverColor = null;

  /**
   * The constructor.
   */
  public ColorSwatch() {
    this(new Color[0][0]);
  }

  /**
   * The constructor.
   *
   * @param colors  The colors to select from.
   */
  public ColorSwatch(Color[][] colors) {
    this.colors = colors;

    // set look
    setOpaque(true);
    setBackground(Color.WHITE);
    setBorder(BorderFactory.createLineBorder(Color.BLACK, 1));

    // add listener
    MouseInputListener mouseListener = new MouseInputListener() {
      public void mousePressed(MouseEvent event) {
        Color color = getColor(event.getX(), event.getY());

        // notify about selection
        for (int i = 0; i < listeners.size(); i++) {
          listeners.get(i).handleColor(color);
        }
      }
      public void mouseMoved(MouseEvent event) {
        rolloverColor = getColor(event.getX(), event.getY());
        repaint();
      }
      public void mouseDragged(MouseEvent event) {
        // not used
      }
      public void mouseReleased(MouseEvent event) {
        // not used
      }
      public void mouseClicked(MouseEvent arg0) {
        // not used
      }
      public void mouseEntered(MouseEvent arg0) {
        // not used
      }
      public void mouseExited(MouseEvent arg0) {
        rolloverColor = null;
        repaint();
      }
    };
    addMouseListener(mouseListener);
    addMouseMotionListener(mouseListener);
  }

  /**
   * Returns the colors.
   *
   * @return  The colors.
   */
  public Color[][] getColors() {
    return colors;
  }

  /**
   * Sets the colors.
   *
   * @param colors  The colors.
   */
  public void setColors(Color[][] colors) {
    this.colors = colors;
  }

  /**
   * Returns the preferred size.
   *
   * @return  The preferred size.
   */
  @Override
  public Dimension getPreferredSize() {
    int colorSize = getColorSize();
    int colorSpacing = getColorSpacing();
    int margin = getMargin();
    return new Dimension((colorSize + colorSpacing) * colors[0].length 
                              + (2 * margin) - colorSpacing
                       , (colorSize + colorSpacing) * colors.length 
                              + (2 * margin) - colorSpacing);
  }

  /**
   * Returns the margin/insets from where the colors are painted. Override this method to paint the colors
   * further inside.
   *
   * @return  The margin.
   */
  public int getMargin() {
    return 2;
  }

  /**
   * Returns the color for the given coordinate.
   *
   * @param x  The x coordinate.
   * @param y  The y coordinate.
   * @return  The color or null if not found.
   */
  private Color getColor(int x, int y) {
    int margin = getMargin();
    int colorSize = getColorSize();
    int colorSpacing = getColorSpacing();
    x -= margin;
    y -= margin;
    int col = x / (colorSize + colorSpacing);
    int row = y / (colorSize + colorSpacing);
    if ((col < 0) || (col >= colors[0].length) || (row < 0) 
                       || (row >= colors.length)) {
      return null;
    }
    else {
      return colors[row][col];
    }
  }

  /**
   * Returns the spacing.
   *
   * @return  The spacing.
   */
  private int getColorSpacing() {
    return getColorSize() > 2 ? 1 : 0;
  }

  /**
   * Returns the size of a color thingy.
   *
   * @return  The color size in pixels.
   */
  private int getColorSize() {
    if ((colors[0].length <= 12) && (colors.length <= 8)) {
      return 15;
    }
    else if ((colors[0].length <= 24) && (colors.length <= 16)) {
      return 7;
    }
    else if ((colors[0].length <= 48) && (colors.length <= 32)) {
      return 5;
    }
    else if ((colors[0].length <= 96) && (colors.length <= 64)) {
      return 3;
    }
    else {
      return 1;
    }
  }

  /**
   * Draws this component.
   *
   * @param g  Where to draw to.
   */
  @Override
  protected void paintComponent(Graphics g) {
    // use antialiasing
    Graphics2D g2 = (Graphics2D)g;
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                        RenderingHints.VALUE_ANTIALIAS_ON);
    int width = getWidth();
    int height = getHeight();
    int margin = getMargin();
    int colorSize = getColorSize();
    int colorSpacing = getColorSpacing();

    // background fill
    g.setColor(getBackground());
    g.fillRect(0, 0, width, height);

    // draw the colors
    for (int y = 0; y < colors.length; y++) {
      for (int x = 0; x < colors[0].length; x++) {         Color color = colors[y][x];         g.setColor(color);         g.fillRect(x * (colorSize + colorSpacing) + margin                  , y * (colorSize + colorSpacing) + margin                  , colorSize                  , colorSize);                  // draw rollover?         if (colorSpacing > 0) {
          if (color == rolloverColor) {
            g.setColor(ROLLOVER_COLOR);
            g.fillRect(x * (colorSize + colorSpacing) + margin
                , y * (colorSize + colorSpacing) + margin
                , colorSize
                , colorSize);
          }
        }
      }
    }
  }

  /**
   * Adds a listener.
   *
   * @param listener  The listener.
   */
  public void addColorListener(ColorListener listener) {
    listeners.add(listener);
  }

  /**
   * Removes a listener.
   *
   * @param listener  The listener.
   */
  public void removeColorListener(ColorListener listener) {
    listeners.remove(listener);
  }
}

August 16, 2010

PRESS RELEASE: Age of Conquest for iPad

Filed under: Business — noblemaster @ 18:41

NOBLE MASTER GAMES RELEASES AGE OF CONQUEST FOR APPLE IPAD

Honolulu, HI – August 17, 2010 — Noble Master Games releases their medieval strategy game “Age of Conquest: World Conquest” for Apple’s iPad platform.

Age of Conquest a risk-like strategy game where you take the reign of a budding empire and struggle for domination of the world. The game includes sixteen scenarios with a total of over hundred empires to choose from including Spain, Germany, France, Egypt, Saudi Arabia, India, China, Japan, Australia, Brazil, Columbia, Cuba and the United States.

Age of Conquest features several AI difficulty levels from easy to extreme keeping you entertained for hours. Options include Fog of War and a God Mode to see all the units on the map. Features include:
– Play with any of the empires available.
– Random placement option.
– Basic population and economy management.
– Background music matching the game you are playing.
– Auto-save option to resume games in progress.

The iPad application “Age of Conquest: World Conquest” is available in the Apple iTunes Store for US$4.99. A free “Lite” version can be downloaded for try out before purchase. Age of Conquest is also available for iPhone and iPod Touch. iTunes URL:
http://itunes.apple.com/us/app/age-conquest-world-conquest/id386104577

Noble Master Games is a professionally established company to serves as a resource for the global gaming community at large.

August 13, 2010

GUI Components: Slider + Progress Bar

Filed under: Development — noblemaster @ 14:21
Slider and Progress Bar

Slider and Progress Bar

Age of Conquest III will feature two new GUI components: slider and progress bar. Both components have been designed by Sergey “Serjio” Churbanov who created most other graphics for Age of Conquest III as well.

August 11, 2010

Splash Screen – Noble Master

Filed under: Development — noblemaster @ 08:56

Spent some time creating a splash screen…

Animated Splash Screen (Noble Master)

Splash Screen (Noble Master)

August 5, 2010

Age of Conquest Lite for iPad

Filed under: Announcement — noblemaster @ 13:11

An update for Age of Conquest Lite (iOS devices) is now available in Apple’s iTunes Store. The update features improved graphics as well as improved AI difficulty levels and difficulty in general. The AI difficulty ranges from easy to extreme. Most notable, the game has now full iPad support. See screenshot below for details.

All other paid Age of Conquest applications including Europe, N. America, S. America, Asia and Africa will receive full iPad support free of charge as soon as Apple approves the latest update (submitted, pending review). The new World Conquest edition featuring a map of the world will be released around August 11.

Download Age of Conquest Lite (free) now via the iTunes Store:

Age of Conquest Lite for iPad