package example7_2.game; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Point; import java.awt.Toolkit; import java.awt.image.BufferedImage; import java.awt.image.FilteredImageSource; import java.awt.image.ImageFilter; import java.awt.image.ImageProducer; import java.awt.image.RGBImageFilter; import java.io.File; import java.util.List; import java.util.Random; import javax.imageio.ImageIO; import javax.swing.JComponent; import example7_2.wavplayer.WavPlayer; public abstract class GameCharacter implements MoveBehavior { // These fields are visible to other classes in the same package (game) /*package*/ GameCharacterImage image; // image of this GameCharacter /*package*/ private Point currentPos; // the GameCharacter's current position /*package*/ int score; // the current number of points this character has /*package*/ boolean moveValid; // used to indicate whether this GameCharacter's planned move is valid /*package*/ Point targetPos; // the GameCharacter's intended position after the next move is completed /*package*/ MoveBehavior moveBehavior; public Point getCurrentPosition() { return this.currentPos; } public void setCurrentPosition(Point currentPos) { this.currentPos = currentPos; } // These fields are visible to derived classes and to classes in the same package (game) protected String name; // a name to give to the class instance // These fields are only visible to this class (GameCharacter) private WavPlayer player; // used for creating sounds private Thread soundThread; // thread for running WavPlayer sounds private int serialNumber; // a unique serial number for each GameCharacter private static int serialNumberSequence=1; // used to generate a unique serial number for each class instance private static final String IMAGE_DIR = ProjectRoot.DIRECTORY+"/other"; private Runnable r = new Runnable() { // A Runnable object that can execute on a separate thread... public void run() { player.play(); } }; /** * This method is called by the Game framework at the end of every move to give the GameCharacter * the chance to take some action. * @param list a list of GameCharacter in the same board cell as this GameCharacter */ public abstract void finishMove(List list); /** * ctor - creates an instance of a GameCharacter, which adds itself to the game board * in a random position. * @param name the name of this GameCharacter * @param imageFile image to display * @param soundFile sound to make when makeSound() is called */ public GameCharacter( String name, String imageFile, String soundFile ) { this.name = name; score = Game.INITIAL_POINTS; // assign some initial points serialNumber = serialNumberSequence++; // assign a unique serial number moveValid = false; // initially false, this flag is set to true if validateMove() approves a planned move player = new WavPlayer(soundFile); // for playing sounds the character makes soundThread = new Thread(r); // for playing sounds on a separate thread to avoid blocking the animation image = new GameCharacterImage( IMAGE_DIR + "/" + imageFile ); // create the graphical representation of the character // Add this GameCharacter to the game board. Note that implementing this logic here (rather than in the Game class) // increases coupling between Game and GameCharacter. For instance, GameCharacter has some knowledge of the Game's BOARDSIZE // and GRIDSIZE, and how many pixels the character occupies, etc - the character doesn't really have to know this. // However, doing so makes it easier to create GameCharacters in the Game // class by delegating the creation to the character itself. Random r = new Random(System.currentTimeMillis()); // create the random number generator with a semi-random seed // initial position of this GameCharacter - random row, col coordinates currentPos = new Point(r.nextInt(Game.GRIDSIZE), r.nextInt(Game.GRIDSIZE) ); targetPos = new Point(); // create a Point to hold the target position for next move, to be computed in planMove() int pixelSize = Game.BOARDSIZE/Game.GRIDSIZE; // the size of each board cell in pixels Point imagePos = new Point(getCurrentColumn()*pixelSize, getCurrentRow()*pixelSize); image.setLocation(imagePos); // set the GameCharacter's initial position (in pixel coordinates) on the board GameBoard.addCharacter(this, getCurrentRow(), getCurrentColumn()); // adds the character to the game board score = Game.INITIAL_POINTS/2+r.nextInt(Game.INITIAL_POINTS/2); // sets an semi-random initial score } /** * retrieve the chacater's name * @return the current GameCharacter's name */ public final String getName() { return name; } /** * retrieve the serial number * @return the current GameCharacter's serial number */ public final int getSerialNumber() { return serialNumber; } /** * retrieve the score * @return the current score */ public final int getScore() { return score; } /** * set the score * @param newScore new score to set */ protected final void setScore( int newScore ) { score = newScore; } /** * retrieves the GameCharacterImage from this GameCharacter * @return this GameCharacter's GameCharacterImage */ protected final GameCharacterImage getImage() { return image; } /** * Sets the GameCharacter's current position, in grid coordinates * @param row the current row position * @param column the current column position */ protected final void setCurrentPosition(int row, int column) { currentPos.y = row; currentPos.x = column; } /** * Sets the GameCharacter's target position, in grid coordinates * @param row the target row position * @param column the target column position */ protected final void setTargetPosition(int row, int column) { targetPos.y = row; targetPos.x = column; } /** * Retrieves the GameCharacter's current row location * @return row location, in grid increments */ public final int getCurrentRow() { return currentPos.y; } /** * Retrieves the GameCharacter's current column location * @return column location, in grid increments */ public final int getCurrentColumn() { return currentPos.x; } /** * Retrieves the GameCharacter's target row location * @return row location, in grid increments */ protected final int getTargetRow() { return targetPos.y; } /** * Retrieves the GameCharacter's target column location * @return column location, in grid increments */ protected final int getTargetColumn() { return targetPos.x; } /** * Plays the sound for this GameCharacter */ public final void makeSound() { if( !soundThread.isAlive() ) { soundThread = new Thread(r); soundThread.start(); } } public void setMoveBehavior(MoveBehavior.behaviorType moveBehaviorType) { switch (moveBehaviorType) { case GREEDY: this.moveBehavior = new GreedyMoveBehavior(this); break; case NONE: this.moveBehavior = new StationaryMoveBehavior(this); break; case RANDOM: this.moveBehavior = new RandomMoveBehavior(this); break; case SMART: this.moveBehavior = new SmartMoveBehavior(this); break; case SUPER_SMART: this.moveBehavior = new SuperSmartMoveBehavior(this); break; } } public void planMove(List list) { this.moveBehavior.planMove(list); } /** * Calculates the distance from this character to the other character. * @param otherCharacter the character to find the distance to * @return the distance from this bee to the other character */ public double distanceTo(GameCharacter otherCharacter) { // Distance formula... SQRT(ΔX^2 + ΔY^2) return Math.sqrt(Math.pow(otherCharacter.getCurrentRow() - this.getCurrentRow(), 2) + Math.pow(otherCharacter.getCurrentColumn() - this.getCurrentColumn(), 2)); } /** * This class represents the image of a GameCharacter * @author hornick */ protected class GameCharacterImage extends JComponent { private static final long serialVersionUID = 1L; public static final int IMAGESIZE = Game.BOARDSIZE/Game.GRIDSIZE; // jpg height/width in pixels private BufferedImage image; // jpg image of this GameCharacter /** * ctor - creates an instance of a GameCharacterImage * @param imageFile image to display */ public GameCharacterImage( String imageFile ) { loadImage(imageFile); setSize(IMAGESIZE, IMAGESIZE); // set the size of this GameCharacter's image } // This method filters an image; specifically, it converts all pixels that // are nearly WHITE (whose rgb values are > than 0xF0) to transparent private Image transformImage(BufferedImage image) { ImageFilter filter = new RGBImageFilter() { public final int filterRGB(int x, int y, int rgb) { int r = (rgb & 0xFF0000) >> 16; // mask and shift red bits low int g = (rgb & 0xFF00) >> 8; // mask and shift green bits low int b = rgb & 0xFF; // mask blue bits if (r >= 0xF0 && g >= 0xF0 && b >= 0xF0 ) { // if the pixel value is "nearly" white... // Set fully transparent rgb = 0; } else { rgb &= 0xBBFFFFFF; } return rgb; } }; ImageProducer ip = new FilteredImageSource(image.getSource(), filter); return Toolkit.getDefaultToolkit().createImage(ip); } /** * This method reads a specified file containing an image of * a GameCharacter and loads the content into an Image that can be displayed * @param imageFile the file containing the GameCharacter's image * @note if the file can't be loaded, an Image will be automatically * be generated containing an error message. */ private void loadImage(String imageFile) { try{ // try to load the specified image file Image temp = ImageIO.read( new File(imageFile));// load raw file temp = temp.getScaledInstance(IMAGESIZE, IMAGESIZE, BufferedImage.SCALE_SMOOTH); // scale to fixed size if( temp == null ) throw new RuntimeException("image problem"); // create an image with alpha blending image = new BufferedImage(IMAGESIZE, IMAGESIZE, BufferedImage.TYPE_INT_ARGB); Graphics2D g = (Graphics2D)image.getGraphics(); g.drawImage(temp, 0, 0, null); g.dispose(); temp = transformImage(image); // use the above helper method to create a transparent background if( temp == null ) throw new RuntimeException("image problem"); image = new BufferedImage(IMAGESIZE, IMAGESIZE, BufferedImage.TYPE_INT_ARGB); g = (Graphics2D)image.getGraphics(); g.drawImage(temp, 0, 0, null); g.dispose(); } catch( Exception e ) { // Aw, crud. Can't load the image file. // The following code demonstrates how to dynamically generate // an image in memory as a BufferedImage, to be displayed later. image = new BufferedImage(IMAGESIZE, IMAGESIZE, BufferedImage.TYPE_INT_ARGB); Graphics2D g = (Graphics2D)image.getGraphics(); Font f = new Font("Courier New", Font.ITALIC, 10); g.setFont(f); g.setColor(Color.RED); g.drawString(imageFile, 20, 20); g.drawString("not found", 20, 30); // note we can also draw lines etc in the BufferedImage // - maybe to create an outline of a generic GameCharacter - // here, we're just displaying a simple message. } } @Override // Override of JComponent.paintComponent method, which is called automatically // by the JFrame containing this component whenever it needs to be painted. // Here, we're repainting by drawing the JPG image we loaded earlier. public void paintComponent(Graphics graphics) { Graphics2D g = (Graphics2D)graphics; super.paintComponent(g); g.drawImage(image, 0, 0, IMAGESIZE, IMAGESIZE, null); Font f = new Font("Arial", Font.BOLD, 12); g.setFont(f); g.setColor(Color.GRAY); String msg = ""+score; if( score <= 0 ) { msg = "dead!"; } g.drawString(msg, 0,IMAGESIZE); } } }