actors/Actor.java


other
package pacman.actors; import java.awt.Color; import java.awt.Graphics2D; import pacman.ai.AIManager; import pacman.game.GameObject; import pacman.map.Map; import pacman.state.StateGame; import pacman.util.Direction; import pacman.util.RequestedDirectionBuffer; /** * An actor is any object that has a degree of autonomy or intelligent input * (Human / AIManager) dictating the object's behavior ingame Subclass of * GameObject * * @author Ramsey Kant */ public abstract class Actor extends GameObject { /** Whether the actor is alive or not. */ protected boolean isDead; /** The x-position on the map where the actor gets spawned */ protected int spawnX; /** The y-position on the map where the actor gets spawned */ protected int spawnY; /** The direction in which the actor is currently oriented. */ protected Direction currentMoveDir; /** The actor's direction requested by user input. Uses a buffer to * remember requests over some steps to simplify timing for the user. * Computer-controlled actors (ghosts) ignore this. */ protected RequestedDirectionBuffer requestedMoveDirBuffer; /** The size of the direction request buffer. */ private final static int DIRECTION_QUEUE_SIZE = 6; /** The current orientation angle of the actor. Ignored by actors that do * not have to orient (aka ghosts). */ protected int dirOrient; /** The x-position delta to the current map cell, caused by movement * in pixels. */ protected float deltaX; /** The y-position delta to the current map cell, caused by movement * in pixels. */ protected float deltaY; /** The movement speed of the actor */ protected float speed; /** * Actor Class Constructor * * @param type * Object type that is an actor * @param color * Base color of the actor * @param m * Reference to the global map object * @param x * X coordinate to spawn the actor at * @param y * Y coordinate to spawn the actor at * @see GameObject */ public Actor(int type, Color color, Map m, int x, int y) { super(type, color, m, x, y); isDead = false; // Movement spawnX = x; spawnY = y; currentMoveDir = Direction.none; requestedMoveDirBuffer = new RequestedDirectionBuffer(DIRECTION_QUEUE_SIZE); dirOrient = 0; deltaX = 0; deltaY = 0; speed = (float) (5d * map.SCALE); } // Getters and Setters /** * Returns the original X coordinate the Actor was given in the constructor * * @return the X coordinate of the spawn point */ public int getSpawnX() { return spawnX; } /** * Returns the original Y coordinate the Actor was given in the constructor * * @return the Y coordinate of the spawn point */ public int getSpawnY() { return spawnY; } /** * Set the death status of the actor. Used by StateGame and AIManager to * determine if the player / ghost has died */ public void setDead(boolean s) { isDead = s; } /** * Get dead status * * @return True if dead, false if alive * @see Actor#setDead(boolean) */ public boolean isDead() { return isDead; } /** * Speed is the number of pixels an actor moves across the screen in a given * cycle. A full position change is the number of pixels defined in * Map.CELL_SIZE * * @param s * New Speed */ public void setSpeed(float s) { speed = s; } /** * Get the current speed of the actor * * @return Current speed * @see Actor#setSpeed(float) */ public float getSpeed() { return speed; } /** * Set the direction actor should travel in. Player uses this to determine * the direction to "auto-move" to Ghosts ignore what is set by this * function because their direction is determined within act() based on the * path */ public void setMoveDirection(Direction dir) { requestedMoveDirBuffer.setRequestedDirection(dir); } // Public Methods /** * Attempt to move the actor to the given x,y location. This method will * check if a coordinate is valid with the Map class method canMove(). It is * not necessary to call canMove() before this function * * @param x * A x coordinate to move to * @param y * A y coordinate to move to * @return True if the move succeeded. False if otherwise * @see Map#canMove(Actor, int, int) */ public boolean move(int x, int y) { final boolean res = map.canMove(this, x, y); if (res) { positionX = x; positionY = y; } return res; } /** * The primary logic function for actors. StateGame calls this for players * directly in logic() and the AIManager calls this for ghosts in process() * * @see GameObject#act() * @see StateGame#logic() * @see AIManager#process() */ @Override public abstract void act(); /** * * @see GameObject#paint(java.awt.Graphics2D) */ @Override public abstract void paint(Graphics2D g); }

actors/Ghost.java


other
package pacman.actors; import java.awt.Color; import java.awt.Graphics2D; import pacman.ai.AIManager; import pacman.game.GameObject; import pacman.map.Map; import pacman.map.Path; import pacman.util.Direction; /** * The Ghost class is the primary enemy in Pacman. Intelligent decisions of * ghosts are made by the AIManager class Ghost is a subclass of Actor * * @author Ramsey Kant */ public class Ghost extends Actor { // Movement private Path path; private int nextStepIdx; private boolean needNewPath; // State private boolean trapped; private boolean inFear; private boolean debugDrawPath; /** * Class constructor * * @param color * Color of the ghost's 'body' * @param m * Reference to the map * @param x * X coordinate to spawn at * @param y * Y coordinate to spawn at * @param trap * Set trapped status */ public Ghost(Color color, Map m, int x, int y, boolean trap) { super(GameObject.OBJECT_GHOST, color, m, x, y); needNewPath = true; inFear = false; trapped = trap; debugDrawPath = false; } /** * Return the fear status of the ghost * * @return True if fearful */ public boolean isInFear() { return inFear; } /** * Set fear status. AIManager interperates this setting for behavior * * @param f * Fear status, true if fearful */ public void setFear(boolean f) { inFear = f; } /** * Get the current trapped status * * @return True if the ghost is currently in the spawn-jail */ public boolean isTrapped() { return trapped; } /** * Set the current trapped status * * @param t * Trye uf the ghost is in the spawn-jail */ public void setTrapped(boolean t) { trapped = t; } /** * Flag that is set to true when the path reaches the last possible step * * @return True if the AIManager needs to assign a new path */ public boolean needsNewPath() { return needNewPath; } /** * Update the Path object for the ghost to follow' * * @param p * Path object generated in process() by the AIManager * @see AIManager#process() */ public void updatePath(Path p) { nextStepIdx = 1; path = p; needNewPath = false; } /** * Direct's the paint() function to draw the current path of the ghost on * the map * * @param d * If true, debug is on and the path will be drawn * @see AIManager#setDebugEnabled */ public void setDebugDrawPath(boolean d) { debugDrawPath = d; } /** * Run a think cycle for the AI. Major decisions are made by the AIManager * (pathing), this just determines movement and screen draw deltas * * @see Actor#act() */ @Override public void act() { // Move to the next step if (path != null && nextStepIdx < path.getLength()) { // Figure out the direction if ((path.getY(nextStepIdx) - positionY) < 0) { currentMoveDir = Direction.up; } else if ((path.getY(nextStepIdx) - positionY) > 0) { currentMoveDir = Direction.down; } else if ((path.getX(nextStepIdx) - positionX) > 0) { currentMoveDir = Direction.right; } else { currentMoveDir = Direction.left; } // Based on the direction, move the screen delta's and the X,Y // coordinates if the # of pixels for the cell have been surpassed switch (currentMoveDir) { case up: deltaX = 0; deltaY -= 0 - speed; // If the movement delta has surpassed the number of pixels for // the cell, set him to the map cell he has reached by his movement. if (Math.abs(deltaY) >= map.CELL_SIZE) { deltaY = 0; move(positionX, positionY - 1); nextStepIdx++; } break; case right: deltaX += 0 + speed; deltaY = 0; if (Math.abs(deltaX) >= map.CELL_SIZE) { deltaX = 0; move(positionX + 1, positionY); nextStepIdx++; } break; case down: deltaX = 0; deltaY += 0 + speed; if (Math.abs(deltaY) >= map.CELL_SIZE) { deltaY = 0; move(positionX, positionY + 1); nextStepIdx++; } break; case left: deltaX -= 0 - speed; deltaY = 0; if (Math.abs(deltaX) >= map.CELL_SIZE) { deltaX = 0; move(positionX - 1, positionY); nextStepIdx++; } break; case stop: case none: // do not move } } else { needNewPath = true; } } /** * Draw the ghost * * @see GameObject#paint(Graphics2D) */ @Override public void paint(Graphics2D g) { // Change the position of pacman on screen by the offsets m_fDelta final int screenX = (int) ((map.CELL_SIZE * positionX) + deltaX); final int screenY = (int) ((map.CELL_SIZE * positionY) + deltaY); g.setColor(objColor); // Body if (inFear) { g.setColor(Color.WHITE); } g.fillArc(screenX, screenY, map.CELL_SIZE, map.CELL_SIZE, 0, 360); g.fillRect((int) ((map.CELL_SIZE * positionX) + deltaX), (int) ((map.CELL_SIZE * positionY) + (map.CELL_SIZE / 2) + deltaY), map.CELL_SIZE, map.CELL_SIZE / 2); // Eyes if (inFear) { g.setColor(Color.BLACK); } else { g.setColor(Color.WHITE); } g.fillOval((int) ((map.CELL_SIZE * positionX) + 4 + deltaX), (int) ((map.CELL_SIZE * positionY) + 3 + deltaY), 8, 10); g.fillOval((int) ((map.CELL_SIZE * positionX) + 12 + deltaX), (int) ((map.CELL_SIZE * positionY) + 3 + deltaY), 8, 10); // Eyeballs g.setColor(Color.BLUE); g.fillOval((int) ((map.CELL_SIZE * positionX) + 7 + deltaX), (int) ((map.CELL_SIZE * positionY) + 6 + deltaY), 4, 4); g.fillOval((int) ((map.CELL_SIZE * positionX) + 13 + deltaX), (int) ((map.CELL_SIZE * positionY) + 6 + deltaY), 4, 4); // Debug draw path if (debugDrawPath && path != null) { for (int i = 0; i < path.getLength(); i++) { final Path.Step s = path.getStep(i); g.setColor(objColor); g.drawLine(map.CELL_SIZE * s.getX(), map.CELL_SIZE * s.getY(), (map.CELL_SIZE * s.getX()) + map.CELL_SIZE, (map.CELL_SIZE * s.getY()) + map.CELL_SIZE); } } } }

actors/Player.java


other
package pacman.actors; import java.awt.Color; import java.awt.Graphics2D; import pacman.game.GameObject; import pacman.game.Item; import pacman.map.Map; import pacman.util.Direction; /** * Player (pacman) is the object controlled by the human playing the game Player * is a subclass of Actor * * @author Ramsey Kant */ public class Player extends Actor { // State private int m_iScore; // Current score - Only valid for the current life / // level. StateGame will pull this on death or on // level change private boolean isPowered; // Powered up private long poweredExpireTime; /** * Class Constructor for Player * * @param m * Reference to the map object * @param x * X coordiante to spawn the player at * @param y * Y coordinate to spawn the player at */ public Player(Map m, int x, int y) { super(OBJECT_PLAYER, Color.yellow, m, x, y); // State m_iScore = 0; isPowered = false; poweredExpireTime = 0; } // Getters and Setters /** * Increment score by amount. The is the current level score, not the entire * session score This function is typically called inside an Item's use() * function when the player picks up an item like a dot * * @param amt * Amount to increment */ public void incrementScore(int amt) { m_iScore += amt; } /** * Get the current level score of the player * * @return the score */ public int getScore() { return m_iScore; } /** * Returns the isPowered flag which determines whether or not the player is * powered up and invicible to ghosts * * @return True if the player is powered up */ public boolean isPoweredUp() { return isPowered; } /** * Set powered up state and start the expirtation time for when the powerup * wears off * * @param x * True if powered up, false if otherwise * @see Player#isPoweredUp() */ public void setPowerUp(boolean x) { isPowered = x; // If powered up, start the timer and increase speed temporarily if (isPowered) { poweredExpireTime = System.currentTimeMillis() + 10000; } } /** * Player act() method This should evaluate if there is: - a collission with * a ghost and how to handle that interaction - a dot or cherry being eaten * (call use() on the item) - a next movement */ @Override public void act() { // If there is a ghost at the players location, this player is dead // unless the player is powered up (then the ghost dies) final Actor a = map.getActor(positionX, positionY, true); if (a != null && a.getType() == GameObject.OBJECT_GHOST) { // Notify the State of the loss if pacman isn't powered up if (!isPowered) { setDead(true); return; } else { a.setDead(true); } } // Check for powerup expire if (System.currentTimeMillis() > poweredExpireTime) { setPowerUp(false); } // Use item at current location boolean itemDestroy = false; final Item item = map.getItem(positionX, positionY); if (item != null) { itemDestroy = item.use(this); } // Update the item's state in the map (remove if itemDestroy is true) if (itemDestroy) { map.removeItem(positionX, positionY); } // Check if a change in direction was requested and if we can move // there. If yes that will be the new direction, if no, keep // the old direction. final Direction requestedDir = requestedMoveDirBuffer.getRequestedDirection(); if (requestedDir != Direction.none) { if (map.canMove(this, requestedDir)) { currentMoveDir = requestedDir; } } // Based on the direction, increment the movement delta and set the // appropriate orientation // The delta's represent the screen position (in pixels) since the last // official change in position on the grid // When a delta in a certain direction passes the CELL_SIZE, the object // can change position in the map grid. This makes for smooth // transitions between tiles switch (currentMoveDir) { case up: // Move in the direction only if the next map cell in this // direction is reachable (not occupied by a wall) if (map.canMove(this, positionX, positionY - 1)) { deltaX = 0; deltaY -= 0 - speed; // If the movement delta has surpassed the number of pixels for // the cell, set him to the map cell he has reached by his movement if (Math.abs(deltaY) >= map.CELL_SIZE) { deltaY = 0; move(positionX, positionY - 1); } } dirOrient = 90; break; case right: if (map.canMove(this, positionX + 1, positionY)) { deltaX += 0 + speed; deltaY = 0; if (Math.abs(deltaX) >= map.CELL_SIZE) { deltaX = 0; move(positionX + 1, positionY); } } dirOrient = 0; break; case down: if (map.canMove(this, positionX, positionY + 1)) { deltaX = 0; deltaY += 0 + speed; if (Math.abs(deltaY) >= map.CELL_SIZE) { deltaY = 0; move(positionX, positionY + 1); } } dirOrient = -90; break; case left: if (map.canMove(this, positionX - 1, positionY)) { deltaX -= 0 - speed; deltaY = 0; if (Math.abs(deltaX) >= map.CELL_SIZE) { deltaX = 0; move(positionX - 1, positionY); } } dirOrient = 180; break; case stop: case none: // do not move } } /** * Draw & animate pacman * * @param g * The graphics context * @see Actor#act() */ @Override public void paint(Graphics2D g) { // Change the position of pacman on screen by the offsets m_fDelta final int screenX = (int) ((map.CELL_SIZE * positionX) + deltaX); final int screenY = (int) ((map.CELL_SIZE * positionY) + deltaY); g.setColor(objColor); // Animate Pacman's mouth // When the player is half-way through a tile, close the flap. Open it // back up when the flap clears a tile. // This essentially creates an eating animation if ((Math.abs(deltaX) >= map.CELL_SIZE / 2) || Math.abs(deltaY) >= map.CELL_SIZE / 2) { g.fillArc(screenX, screenY, map.CELL_SIZE, map.CELL_SIZE, 0 + dirOrient, 360); // flap // closed } else { g.fillArc(screenX, screenY, map.CELL_SIZE, map.CELL_SIZE, 35 + dirOrient, 270); } } }

ai/AIManager.java


other
package pacman.ai; import java.util.ArrayList; import pacman.actors.Actor; import pacman.actors.Ghost; import pacman.actors.Player; import pacman.game.GameObject; import pacman.map.Map; import pacman.map.Path; import pacman.map.PathFinder; /** * Strategy management behind the AI (Ghost objects) * * @author Ramsey Kant */ public class AIManager { // References private Map map; private Player player; // Logic private boolean debugEnabled; private PathFinder finder; private final ArrayList<Ghost> ghosts; private long nextReleaseTime; /** * Class Constructor * * @param m * Reference to the map object being used by the game * @param pl * Reference to the player * @param debug * Set the debug flag allowing the AI manager to direct ghosts to * exibit diagnostic behavior */ public AIManager(Map m, Player pl, boolean debug) { // Set vars ghosts = new ArrayList<Ghost>(); setReferences(m, pl); nextReleaseTime = System.currentTimeMillis() + 10000; debugEnabled = debug; } // Getters and Setters /** * Direct ghosts to display diagnostic information * * @param d * If true, ghosts will enter debug mode * @see Ghost#setDebugDrawPath */ public void setDebugEnabled(boolean d) { debugEnabled = d; } /** * Set the global map and player references. Ghosts being tracked (in the * 'ghosts' ArrayList) will be updated * * @param m * Reference to the map * @param pl * Reference to the player object */ public void setReferences(Map m, Player pl) { ghosts.clear(); map = m; player = pl; finder = new PathFinder(m, 500, false); // Get a list of all AI on the map final int nActors = map.getNumActors(); for (int i = 0; i < nActors; i++) { final Actor a = map.getActor(i); if (a.getType() == GameObject.OBJECT_GHOST) { ghosts.add((Ghost) a); } } } /** * Run all logic required for AI operation; fear, ghost release, path * updates. Ghost act() functions are called here */ public void process() { // Make sure the game is still running and there is a map if (map == null) { return; } // Determine if ghosts are fearful of the player boolean fear = false; if (map.getPlayer().isPoweredUp()) { fear = true; } // Release the next ghost if (System.currentTimeMillis() > nextReleaseTime) { for (final Ghost g : ghosts) { if (g.isTrapped()) { g.setTrapped(false); g.move(13, 11); nextReleaseTime = System.currentTimeMillis() + 8000; break; } } } // Go through a list of all AI on the map for (final Ghost ghost : ghosts) { // If a ghost just died, send them to jail if (ghost.isDead()) { // Find an empty spot in the jail final int x = 11; final int y = 13; int z = 0; while (!map.isEmpty(x + z, y)) { z++; if (z > 4) { break; } } // Clear path and move to jail ghost.updatePath(null); ghost.move(x, y); ghost.setTrapped(true); ghost.setDead(false); } // Any ghost not trapped is given the current fear status if (!ghost.isTrapped()) { // If fear switches from false to true for this ghost, abandon // their current (and likely) chase path if (!ghost.isInFear() && fear) { ghost.updatePath(null); } ghost.setFear(fear); } else { ghost.setFear(false); } // Develop path for ghost if (!ghost.isTrapped() && ghost.needsNewPath()) { int randx = player.getX(); int randy = player.getY(); // 45% chance of randomizing a destination, or if they are // fearful if (fear || Math.random() < 0.45) { randx = (int) (Math.random() * map.getWidth()); randy = (int) (Math.random() * map.getHeight()); } final Path p = finder.findPath(ghost, ghost.getX(), ghost.getY(), randx, randy); ghost.updatePath(p); } // Run an act() ghost.act(); // If debug is enabled, force ghost to draw it's path ghost.setDebugDrawPath(debugEnabled); } } }

ai/AStarHeuristic.java


other
package pacman.ai; import pacman.actors.Actor; import pacman.map.Map; /** * A heuristic that uses the tile that is closest to the target as the next best * tile. * * @author Kevin Glass */ public class AStarHeuristic { /** * Get the additional heuristic cost of the given tile. This controls the * order in which tiles are searched while attempting to find a path to the * target location. The lower the cost the more likely the tile will be * searched. * * @param map * The map on which the path is being found * @param mover * The entity that is moving along the path * @param x * The x coordinate of the tile being evaluated * @param y * The y coordinate of the tile being evaluated * @param tx * The x coordinate of the target location * @param ty * Teh y coordinate of the target location * @return The cost associated with the given tile */ public float getCost(Map map, Actor mover, int x, int y, int tx, int ty) { final float dx = tx - x; final float dy = ty - y; final float result = (float) (Math.sqrt((dx * dx) + (dy * dy))); return result; } }

editor/EditorFrame.java


other
package pacman.editor; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.ComboBoxModel; import javax.swing.DefaultComboBoxModel; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JSeparator; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.WindowConstants; import pacman.game.GameObject; import pacman.state.State; import pacman.state.StateEditor; /** * This code was edited or generated using CloudGarden's Jigloo SWT/Swing GUI * Builder, which is free for non-commercial use. If Jigloo is being used * commercially (ie, by a corporation, company or business for any purpose * whatever) then you should purchase a license for each developer using Jigloo. * Please visit www.cloudgarden.com for details. Use of Jigloo implies * acceptance of these licensing terms. A COMMERCIAL LICENSE HAS NOT BEEN * PURCHASED FOR THIS MACHINE, SO JIGLOO OR THIS CODE CANNOT BE USED LEGALLY FOR * ANY CORPORATE OR COMMERCIAL PURPOSE. */ public class EditorFrame extends javax.swing.JFrame { private static final long serialVersionUID = 1L; private final StateEditor editor; private JMenuItem jItemSaveAs; private JTextArea txtTeleportY; private JTextField txtTeleportX; private JLabel lblTeleportY; private JLabel lblTeleportX; private JLabel lblTeleportSettings; private JButton btnTeleport; private JButton btnNew; private JTextField txtFilename; private JButton btnLoad; private JButton btnSave; private JComboBox jWallTypeCombo; private JButton btnGhost; private JComboBox comboGhost; private JCheckBox chkGhostTrapped; private JLabel lblGhosts; private JButton btnPowerup; private JLabel jWallTypeLabel; private JMenuItem jItemExit; private JSeparator jSeperatorFile; private JMenuItem jItemSave; private JMenuItem jItemLoad; private JMenu jMenuFile; private JMenuBar jMenuBar1; private JLabel lblPlaceableObjs; private JSeparator jSeparator1; private JButton btnPacman; private JButton btnDot; private JButton btnWall; public EditorFrame(StateEditor e) { super(); editor = e; initGUI(); } private void initGUI() { try { setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); getContentPane().setLayout(null); this.setTitle("Pacman Map Editor - Ramsey Kant"); this.addWindowListener(new WindowAdapter() { @Override public void windowClosed(WindowEvent evt) { editor.getGame().requestChangeState(State.STATE_EXITING); } }); { jMenuBar1 = new JMenuBar(); setJMenuBar(jMenuBar1); { jMenuFile = new JMenu(); jMenuBar1.add(jMenuFile); jMenuFile.setText("File"); { jItemLoad = new JMenuItem(); jMenuFile.add(jItemLoad); jItemLoad.setText("Load"); } { jItemSave = new JMenuItem(); jMenuFile.add(jItemSave); jItemSave.setText("Save"); } { jItemSaveAs = new JMenuItem(); jMenuFile.add(jItemSaveAs); jItemSaveAs.setText("Save As.."); } { jSeperatorFile = new JSeparator(); jMenuFile.add(jSeperatorFile); } { jItemExit = new JMenuItem(); jMenuFile.add(jItemExit); jItemExit.setText("Exit"); } } } { btnWall = new JButton(); getContentPane().add(btnWall); btnWall.setText("Wall"); btnWall.setBounds(12, 218, 59, 23); btnWall.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent evt) { editor.setMarkerObjectType(GameObject.OBJECT_WALL); } }); } { btnDot = new JButton(); getContentPane().add(btnDot); btnDot.setText("Dot"); btnDot.setBounds(12, 36, 59, 23); btnDot.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent evt) { editor.setMarkerObjectType(GameObject.OBJECT_DOT); } }); } { btnPacman = new JButton(); getContentPane().add(btnPacman); btnPacman.setText("Pacman"); btnPacman.setBounds(136, 36, 110, 23); btnPacman.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent evt) { editor.setMarkerObjectType(GameObject.OBJECT_PLAYER); } }); } { jSeparator1 = new JSeparator(); getContentPane().add(jSeparator1); jSeparator1.setBounds(12, 301, 360, 10); } { lblPlaceableObjs = new JLabel(); getContentPane().add(lblPlaceableObjs); lblPlaceableObjs.setText("Placeable Objects"); lblPlaceableObjs.setBounds(12, 12, 129, 16); } { jWallTypeLabel = new JLabel(); getContentPane().add(jWallTypeLabel); jWallTypeLabel.setText("Wall Type"); jWallTypeLabel.setBounds(12, 196, 82, 16); } { final ComboBoxModel jWallTypeComboModel = new DefaultComboBoxModel(new String[] { "Vertical", "Horizontal", "Top Left", "Top Right", "Bottom Left", "Bottom Right", "Ghost Barrier" }); jWallTypeCombo = new JComboBox(); getContentPane().add(jWallTypeCombo); jWallTypeCombo.setModel(jWallTypeComboModel); jWallTypeCombo.setBounds(12, 246, 153, 23); jWallTypeCombo.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent evt) { final String sType = (String) jWallTypeCombo.getSelectedItem(); if (sType.equals("Vertical")) { editor.setMarkerWallType(GameObject.WALL_VERTICAL); } else if (sType.equals("Horizontal")) { editor.setMarkerWallType(GameObject.WALL_HORIZONTAL); } else if (sType.equals("Top Left")) { editor.setMarkerWallType(GameObject.WALL_TOPLEFT); } else if (sType.equals("Top Right")) { editor.setMarkerWallType(GameObject.WALL_TOPRIGHT); } else if (sType.equals("Bottom Left")) { editor.setMarkerWallType(GameObject.WALL_BOTTOMLEFT); } else if (sType.equals("Bottom Right")) { editor.setMarkerWallType(GameObject.WALL_BOTTOMRIGHT); } else if (sType.equals("Ghost Barrier")) { editor.setMarkerWallType(GameObject.WALL_GHOSTBARRIER); } else { editor.setMarkerWallType(GameObject.WALL_HORIZONTAL); } } }); } { btnSave = new JButton(); getContentPane().add(btnSave); btnSave.setText("Save"); btnSave.setBounds(12, 317, 70, 23); btnSave.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent evt) { editor.saveMap(txtFilename.getText()); } }); } { btnLoad = new JButton(); getContentPane().add(btnLoad); btnLoad.setText("Load"); btnLoad.setBounds(87, 317, 68, 23); btnLoad.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent evt) { editor.loadMap(txtFilename.getText()); } }); } { txtFilename = new JTextField(); getContentPane().add(txtFilename); txtFilename.setBounds(12, 345, 225, 23); txtFilename.setText("test.map"); } { btnNew = new JButton(); getContentPane().add(btnNew); btnNew.setText("New"); btnNew.setBounds(160, 317, 71, 23); btnNew.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent evt) { editor.newMap(28, 31); } }); } { btnTeleport = new JButton(); getContentPane().add(btnTeleport); btnTeleport.setText("Teleport"); btnTeleport.setBounds(237, 218, 110, 23); btnTeleport.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent evt) { editor.setMarkerObjectType(GameObject.OBJECT_TELEPORT); editor.setMarkerTeleport(Integer.parseInt(txtTeleportX.getText()), Integer.parseInt(txtTeleportY.getText())); } }); } { lblTeleportSettings = new JLabel(); getContentPane().add(lblTeleportSettings); lblTeleportSettings.setText("Teleport Settings"); lblTeleportSettings.setBounds(237, 196, 123, 16); } { lblTeleportX = new JLabel(); getContentPane().add(lblTeleportX); lblTeleportX.setText("Dest X:"); lblTeleportX.setBounds(237, 249, 60, 16); } { lblTeleportY = new JLabel(); getContentPane().add(lblTeleportY); lblTeleportY.setText("Dest Y: "); lblTeleportY.setBounds(235, 279, 52, 16); } { txtTeleportX = new JTextField(); getContentPane().add(txtTeleportX); txtTeleportX.setText("13"); txtTeleportX.setBounds(280, 246, 85, 23); } { txtTeleportY = new JTextArea(); getContentPane().add(txtTeleportY); txtTeleportY.setText("17"); txtTeleportY.setBounds(280, 275, 82, 20); } { btnPowerup = new JButton(); getContentPane().add(btnPowerup); btnPowerup.setText("Powerup"); btnPowerup.setBounds(12, 65, 102, 23); btnPowerup.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent evt) { editor.setMarkerObjectType(GameObject.OBJECT_POWERUP); } }); } { lblGhosts = new JLabel(); getContentPane().add(lblGhosts); lblGhosts.setText("Ghost Settings"); lblGhosts.setBounds(272, 12, 76, 16); } { chkGhostTrapped = new JCheckBox(); getContentPane().add(chkGhostTrapped); chkGhostTrapped.setText("Trapped"); chkGhostTrapped.setBounds(360, 10, 100, 20); chkGhostTrapped.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent evt) { editor.setMarkerGhostTrapped(!editor.getMarkerGhostTrapped()); System.out.println(editor.getMarkerGhostTrapped()); } }); } { final ComboBoxModel comboGhostModel = new DefaultComboBoxModel(new String[] { "Blinky", "Pinky", "Inky", "Clyde" }); comboGhost = new JComboBox(); getContentPane().add(comboGhost); comboGhost.setModel(comboGhostModel); comboGhost.setBounds(272, 65, 146, 23); comboGhost.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent evt) { final String sType = (String) comboGhost.getSelectedItem(); editor.setMarkerGhostType(sType); } }); } { btnGhost = new JButton(); getContentPane().add(btnGhost); btnGhost.setText("Add Ghost"); btnGhost.setBounds(272, 36, 146, 23); btnGhost.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent evt) { editor.setMarkerObjectType(GameObject.OBJECT_GHOST); } }); } pack(); this.setSize(451, 547); } catch (final Exception e) { // add your error handling code here e.printStackTrace(); } } }

editor/EditorMarker.java


other
package pacman.editor; import java.awt.Color; import java.awt.Graphics2D; import java.awt.event.KeyEvent; import pacman.game.GameObject; import pacman.map.Map; import pacman.state.StateEditor; /** * The EditorMarker is used by the StateEditor for navigation and selecting * tiles on the map EditorMarker is NOT tracked inside the Map EditorMarker is a * subclass of GameObject * * @author Ramsey Kant */ public class EditorMarker extends GameObject { /** * Class constructor for EditorMarker * * @param color * Color of the marker * @param m * Reference to the map object * @param x * X coordinate to initially place the marker * @param y * Y coordinate to initially place the marker */ public EditorMarker(Color color, Map m, int x, int y) { super(GameObject.OBJECT_MARKER, color, m, x, y); } // Public Methods /** * Change tile is the EditorMarker's version of Actor's move() method. * Called by keyPressed in StateEditor Moves the Marker on the screen * * @param dx * Amount to change the current X coordinate by * @param dy * Amount to change the current Y coordinate by * @see StateEditor#keyPressed(KeyEvent) */ public void changeTile(int dx, int dy) { // Check bounds if (positionX + dx < 0 || positionY + dy < 0 || positionX + dx >= map.getWidth() || positionY + dy >= map.getHeight()) { return; } positionX += dx; positionY += dy; } /** * EditorMarker has a blank act() method * * @see GameObject#act() */ @Override public void act() { // do nothing } /** * EditorMarker appears as a circle around the tile being edited. The color * is set in the constructor * * @see GameObject#paint(Graphics2D) */ @Override public void paint(Graphics2D g) { final int screenX = (map.CELL_SIZE * positionX); final int screenY = (map.CELL_SIZE * positionY); g.setColor(objColor); g.drawOval(screenX, screenY, map.CELL_SIZE, map.CELL_SIZE); } }

game/Game.java


other
package pacman.game; import java.awt.Canvas; import java.awt.Color; import java.awt.Frame; import java.awt.Graphics2D; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.image.BufferStrategy; import pacman.state.State; import pacman.state.StateEditor; import pacman.state.StateGame; import pacman.state.StateMenu; import pacman.state.StateScoreboard; /** * The Game Supervisor. This class implements that core program logic, state * management, and graphics. * * @author Ramsey Kant */ public class Game extends Canvas { private static final long serialVersionUID = 1L; // Debug vars private boolean debugEnabled; // Threading private boolean runMainThread; // Graphics variables private Frame frame; public final int RES_X; public final int RES_Y; private BufferStrategy m_gBuffer; // State private int stateId; private State currentState; private boolean changeStateRequested; private int requestedState; private String startMap; /** * Class Constructor Set's up graphics and put's game logic into a startup * state by calling init() * * @param x * Resolution X * @param y * Resolution Y * @see Game#init() */ public Game(int x, int y) { // Set resolution settings RES_X = x; RES_Y = y; // Init game init(); } /** * Startup functionality for the program called by the constructor */ private void init() { // Debug vars debugEnabled = false; startMap = "test.map"; changeStateRequested = false; // Setup the game frame frame = new Frame("Pacman"); frame.setLayout(null); setBounds(0, 0, RES_X, RES_Y); frame.add(this); frame.setSize(RES_X, RES_Y); frame.setResizable(false); frame.setVisible(true); // Set the exit handler with an anonymous class frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { // Exit main thread runMainThread = false; } }); // Setup double buffering setIgnoreRepaint(true); // We'll handle repainting createBufferStrategy(2); m_gBuffer = getBufferStrategy(); runMainThread = true; } // Getter and Setter methods /** * Get the Frame object encapsulating the program * * @return The frame */ public Frame getFrame() { return frame; } /** * Get a 'handle' of the current graphics buffer for drawing * * @return The Graphics2D buffer */ public Graphics2D getGraphicsContext() { return (Graphics2D) m_gBuffer.getDrawGraphics(); } /** * Get the name of the map to be loaded in StateGame * * @return Map name (with .map extension) */ public String getStartMap() { return startMap; } /** * Set the default starting map (set by menu) * * @param m * The name of the map to load (with the .map extension) */ public void setStartMap(String m) { startMap = m; } /** * Return the current debug setting * * @return True if debug setting is on * @see Game#toggleDebug() */ public boolean isDebugEnabled() { return debugEnabled; } /** * Toggle debugging. Facilities like AIManager use this flag to display * diagnostic information like AI paths */ public void toggleDebug() { debugEnabled = !debugEnabled; } // Public Methods /** * Called by other states to safely change currentState. This is done so the * currentState's logic can finish * * @see Game#mainThreadLoop() */ public void requestChangeState(int state) { requestedState = state; changeStateRequested = true; } /** * The main game loop that handles graphics and game state determination */ public void mainThreadLoop() { final long loopTime = 20; long lastLoopStart = 0; while (runMainThread) { if ((lastLoopStart + loopTime) > System.currentTimeMillis()){ continue; } lastLoopStart = System.currentTimeMillis(); // If a state change was requested, execute it now if (changeStateRequested) { changeStateRequested = false; changeState(requestedState); continue; } final Graphics2D g = getGraphicsContext(); // Wipe the screen g.setColor(Color.black); g.fillRect(0, 0, RES_X, RES_Y); // Run the logic of the current game state here currentState.logic(); // Show the new buffer g.dispose(); m_gBuffer.show(); try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } // Private Methods /** * Change the state of the game. Called in mainThreadLogic() * * @param state * The state to set. Must match the static vars above * @see Game#requestChangeState(int) * @see Game#mainThreadLoop() */ private void changeState(int state) { // Cleanup for the outgoing state if (currentState != null) { frame.removeKeyListener(currentState); removeKeyListener(currentState); currentState.end(); } // Set the new state type stateId = state; // Instance the new state (reset() is called in the construtor) switch (stateId) { case State.STATE_GAME: currentState = new StateGame(this); break; case State.STATE_SCOREBOARD: currentState = new StateScoreboard(this); /* * StateGame sb = new StateScoreboard(); int newScore = 0; * * // If the previous state was STATE_GAME, pull the session * score and pass it to the scoreboard if(currentState * instanceof StateGame) * sb.addScore((int)((StateGame)currentState * ).getSessionScore())); * * currentState = sb; */ break; case State.STATE_EDITOR: currentState = new StateEditor(this); break; case State.STATE_MENU: currentState = new StateMenu(this); break; case State.STATE_EXITING: currentState = null; runMainThread = false; break; default: break; } // Setup input handler and reset() if (currentState != null) { frame.addKeyListener(currentState); addKeyListener(currentState); } } }

game/GameObject.java


other
package pacman.game; import java.awt.Color; import java.awt.Graphics2D; import pacman.actors.Actor; import pacman.map.Map; import pacman.state.StateGame; /** * A game object is anything on the pacman grid (wall, cherry, ghost, player). * GameObject is the base class of almost everything within the Map * * @author Ramsey Kant */ public abstract class GameObject { // Static type vars public static final int OBJECT_DOT = 1; public static final int OBJECT_POWERUP = 2; public static final int OBJECT_CHERRY = 4; public static final int OBJECT_PLAYER = 8; public static final int OBJECT_GHOST = 16; public static final int OBJECT_MARKER = 32; // Virtual public static final int OBJECT_WALL = 64; // Virtual public static final int OBJECT_TELEPORT = 128; // Wall types (Walls aren't instanced GameObject's) public static final byte WALL_VERTICAL = 1; public static final byte WALL_HORIZONTAL = 2; public static final byte WALL_TOPLEFT = 3; public static final byte WALL_TOPRIGHT = 4; public static final byte WALL_BOTTOMLEFT = 5; public static final byte WALL_BOTTOMRIGHT = 6; public static final byte WALL_GHOSTBARRIER = 7; // Generic object attributes protected int objType; protected Color objColor; protected int positionX; protected int positionY; // Outside refereneces protected final Map map; // Can only be set once. Object only exists within // the map. If the map changes, new objects are // created // Getters and Setters /** * Return the type of the object set in the constructor. See static types * defined in GameObject * * @return type of object */ public int getType() { return objType; } /** * Grab the current java.awt.Color (base color) the object is being rendered * in * * @return Base Color of the object */ public Color getColor() { return objColor; } /** * Set the current base color used when rendering the object * * @param c * java.awt.Color Color of object */ public void setColor(Color c) { objColor = c; } /** * Grab the current X coordinate of the object on the map. This property is * frequently modified by the Map class and move() method * * @see Actor#move(int, int) */ public int getX() { return positionX; } /** * Grab the current Y coordinate of the object on the map. This property is * frequently modified by the Map class and move() method * * @see Actor#move(int, int) */ public int getY() { return positionY; } // Public & Protected Abstract methods /** * Class Constructor for a game object * * @param type * Type of game object (see static types above) * @param color * Standard java Color * @param m * Reference to the global Map * @param x * Initial x coordinate * @param y * Initial y coordinate */ public GameObject(int type, Color color, Map m, int x, int y) { objType = type; objColor = color; map = m; positionX = x; positionY = y; } /** * Perform a "Think" cycle for the Object This includes things like self * maintenance and movement */ public abstract void act(); /** * Draw the object. Subclasses should define how they are to be drawn. This * is called in StateGame's logic() * * @param g * The graphics context * @see StateGame#logic() */ public abstract void paint(Graphics2D g); }

game/Item.java


other
package pacman.game; import java.awt.Color; import java.awt.Graphics2D; import pacman.actors.Player; import pacman.map.Map; /** * Item objects are GameObject's that can be manipulated by the Player on the * map (teleports, dots, powerups, fruit) Item is a subclass of GameObject * * @author Ramsey Kant */ public class Item extends GameObject { // Teleportation vars private int teleportDestX; private int teleportDestY; /** * Class constructor for Item * * @param type * Object type * @param color * Base color of the item * @param m * Reference to the map object * @param x * X coordinate the item will occupy on the map * @param y * Y coordinate the item with occupy on the map * @see GameObject */ public Item(int type, Color color, Map m, int x, int y) { super(type, color, m, x, y); teleportDestX = 13; teleportDestY = 17; } /** * Set the destination coordinates for teleportation. This isn't useful to * any item other than a teleport * * @param x * X destination coordinate * @param y * Y destination coordinate */ public void setTeleport(int x, int y) { teleportDestX = x; teleportDestY = y; } /** * Retrieve the teleport destination X coordinate * * @return X destination coordinate * @see Item#setTeleport(int, int) */ public int getTeleportX() { return teleportDestX; } /** * Retrieve the teleport destination Y coordinate * * @return Y destination coordinate * @see Item#setTeleport(int, int) */ public int getTeleportY() { return teleportDestY; } /** * Called when the item is picked up / used by the player (in the player's * act() function) Add point values or trigger powerup modifiers here (using * the pl object) * * @param pl * Player that uses the item * @return True->Destroy the item. False->Keep the item on the map * @see Player#act() */ public boolean use(Player pl) { boolean destroy = false; // Perform action based on type switch (objType) { case OBJECT_DOT: pl.incrementScore(10); destroy = true; break; case OBJECT_POWERUP: pl.incrementScore(50); pl.setPowerUp(true); destroy = true; break; case OBJECT_TELEPORT: pl.move(teleportDestX, teleportDestY); break; default: break; } return destroy; } /** * Item's have no "think" process. Blank method * * @see GameObject#act() */ @Override public void act() { // do nothing } /** * Draw the item based on it's type * * @see GameObject#paint(java.awt.Graphics2D) */ @Override public void paint(Graphics2D g) { g.setColor(objColor); // Item's are placed in the center of a cell final int center_x = (positionX * map.CELL_SIZE) + map.CELL_SIZE / 2; final int center_y = (positionY * map.CELL_SIZE) + map.CELL_SIZE / 2; // Render item based on type switch (objType) { case OBJECT_DOT: g.fillArc(center_x - 4, center_y - 4, 8, 8, 0, 360); break; case OBJECT_POWERUP: g.fillArc(center_x - 8, center_y - 8, 16, 16, 0, 360); break; case OBJECT_TELEPORT: g.fillOval(center_x - 6, center_y - 8, 12, 16); break; default: break; } } }

game/JPacmanGame.java


other
package pacman.game; import pacman.state.State; /** * The entry point of the program * * @author Ramsey Kant */ public class JPacmanGame { public static void main(String[] arg) { final Game g = new Game(1024, 768); g.requestChangeState(State.STATE_MENU); g.mainThreadLoop(); System.exit(0); } }

map/Map.java


other
package pacman.map; import java.awt.Color; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import pacman.actors.Actor; import pacman.actors.Ghost; import pacman.actors.Player; import pacman.game.GameObject; import pacman.game.Item; import pacman.util.Direction; /** * Map class keeps track objects on the playing grid, helper methods to make * movement decisions, and export/import methods for the editor * * @author Ramsey Kant */ public class Map { // Map parameters (width & height represent # of cells) private int mapWidth; private int mapHeight; public final int CELL_SIZE; public final int WALL_THICKNESS; public final int WALL_OVERLAP; public final double SCALE; // Instance vars private byte collideMap[][]; private Item itemMap[][]; private ArrayList<Actor> actorList; private int dotsRemaining; /** * Class constructor, inits a blank map based on a width, height, and cell * size Used in the editor * * @param w * Width of the map * @param h * Height of the map * @param cs * Size of individual cells in pixels */ public Map(int w, int h, double scale) { // Set map parameters mapWidth = w; mapHeight = h; SCALE = scale; CELL_SIZE = (int) (32 * scale); WALL_THICKNESS = (int) (12 * scale); WALL_OVERLAP = (int) (10 * scale); dotsRemaining = 0; // Initialize collideMap, a 2D array that contains all static collidable // GameObjects // We use this for fast lookup during collision detection and AI // movement paths collideMap = new byte[mapWidth][mapHeight]; // Initialize itemMap, a 2D array that contains items (dots, powerups, // cherry) on the map itemMap = new Item[mapWidth][mapHeight]; // Create m_objects, an arraylist with all actorList actorList = new ArrayList<Actor>(); } /** * Class Constructor that reads the map data from filename * * @param filename * The file name of the map to read contents from * @param cs * Size of individual cells in pixels. This is something that * should be deteremined by graphics, not the mapfile */ public Map(String filename, double scale) { // Set the cell size SCALE = scale; CELL_SIZE = (int) (32 * scale); WALL_THICKNESS = (int) (12 * scale); WALL_OVERLAP = (int) (10 * scale); // Read contents of the map file read(filename); } /** * The width of the map originally set in the constructor * * @return The width of the map */ public int getWidth() { return mapWidth; } /** * The height of the map originally set in the constructor * * @return The height of the map */ public int getHeight() { return mapHeight; } /** * Get the number of actorList on the map (the size of the actorList * ArrayList) * * @return Number of actorList */ public int getNumActors() { return actorList.size(); } /** * Return the collidable map (a 2d array of bytes which correspond to the * collidable types defined in GameObject) * * @return collidable map (collideMap) */ public byte[][] getCollidableMap() { return collideMap; } /** * Return the item map (a 2D array of Item objects) * * @return item map (itemMap) */ public Item[][] getItemMap() { return itemMap; } /** * Return the number of dots remaining on the map. This is tracked by the * dotsRemaining local var (not a loop and count in itemMap) * * @return dots remaining */ public int getDotsRemaining() { return dotsRemaining; } /** * Add a collidable (by type) to the collideMap * * @param x * X coordinate * @param y * Y coordinate * @param t * Type of collidable * @return True if successful */ public boolean addCollidable(int x, int y, byte t) { // Check bounds if (x < 0 || y < 0 || x >= mapWidth || y >= mapHeight) { return false; } // Check if theres already something there if (collideMap[x][y] > 0) { return false; } // Add to the collideMap collideMap[x][y] = t; return true; } /** * Put a new item to the item map * * @param item * Item * @return True if successful */ public boolean addItem(Item item) { if (item == null) { return false; } // Check bounds final int x = item.getX(); final int y = item.getY(); if (x < 0 || y < 0 || x >= mapWidth || y >= mapHeight) { return false; } // Add to the itemMap if (item.getType() == GameObject.OBJECT_DOT) { dotsRemaining++; } itemMap[x][y] = item; return true; } /** * Put a new actor in the map (actorList ArrayList) * * @param act * Actor * @return True if successful */ public boolean addActor(Actor act) { if (act == null) { return false; } // Check bounds final int x = act.getX(); final int y = act.getY(); if (x < 0 || y < 0 || x >= mapWidth || y >= mapHeight) { return false; } // Add to the array list actorList.add(act); return true; } /** * Return a value at (x,y) in the collision map * * @param x * X Coordinate * @param y * Y Coordinate * @return Integer that represents the collision object */ public byte getCollidable(int x, int y) { // Check bounds if (x < 0 || y < 0 || x >= mapWidth || y >= mapHeight) { return -1; } return collideMap[x][y]; } /** * Return an item at coordinate (x,y) from within the item map (itemMap) * * @param x * X Coordinate * @param y * Y Coordinate * @return Item the item that is found at (x,y) */ public Item getItem(int x, int y) { // Check bounds if (x < 0 || y < 0 || x >= mapWidth || y >= mapHeight) { return null; } return itemMap[x][y]; } /** * Return an actor at index in the actorList ArrayList * * @param idx * Index in actorList * @return Actor (null if non-existant) */ public Actor getActor(int idx) { Actor act = null; try { act = actorList.get(idx); } catch (final IndexOutOfBoundsException e) { e.printStackTrace(); } return act; } /** * Find and return the player object within the local actorList ArrayList * * @return The player object. null if not found */ public Player getPlayer() { // Get from the object map for (final Actor g : actorList) { if (g.getType() == GameObject.OBJECT_PLAYER) { return (Player) g; } } return null; } /** * Return an actor at coordinate (x,y) * * @param x * X Coordinate * @param y * Y Coordinate * @param notPlayer * If true, ignore a "Player" actor at (x,y) * @return Actor (null if an actor doesn't exist at the position) */ public Actor getActor(int x, int y, boolean notPlayer) { // Check bounds if (x < 0 || y < 0 || x >= mapWidth || y >= mapHeight) { return null; } // Get from the object map for (final Actor g : actorList) { if (notPlayer && g.getType() == GameObject.OBJECT_PLAYER) { continue; } if (g.getX() == x && g.getY() == y) { return g; } } return null; } /** * Remove an actor from actorList based on index. Be careful when using * this! Just because an actor isn't in the map doesn't mean it's not * 'alive' This is primarily for the editor * * @param idx * Index of the actor */ public void removeActor(int idx) { actorList.remove(idx); } /** * Remove an item from the item array by coordinate (x, y) * * @param x * X coordinate of the item * @param y * Y coordinate of the item */ public void removeItem(int x, int y) { // Check bounds if (x < 0 || y < 0 || x >= mapWidth || y >= mapHeight) { return; } if (itemMap[x][y].getType() == GameObject.OBJECT_DOT) { dotsRemaining--; } itemMap[x][y] = null; } /** * Remove everything at coordiante (x,y) Used by the editor only * * @param x * X coordinate * @param y * Y coordinate * @return boolean True if something was removed, false if otherwise */ public boolean removeAnyAt(int x, int y) { boolean rm = false; // Check bounds if (x < 0 || y < 0 || x >= mapWidth || y >= mapHeight) { return false; } // Remove any collidable if (collideMap[x][y] != 0) { collideMap[x][y] = 0; rm = true; } // Remove any item if (itemMap[x][y] != null) { itemMap[x][y] = null; rm = true; } // Remove any actor for (int i = 0; i < actorList.size(); i++) { Actor a = actorList.get(i); if (a.getX() == x && a.getY() == y) { actorList.remove(i); a = null; i--; rm = true; } } return rm; } /** * Find the distance (Manhattan) between two objects * * @param start * GameObject at the initial position * @param end * GameObject at the end position * @return Distance (integer) */ public int findDistance(GameObject start, GameObject end) { return (int) Math.sqrt(Math.pow(Math.abs(start.getX() - end.getX()), 2) + Math.pow(Math.abs(start.getY() - end.getY()), 2)); } /** * Check if a coordinate is completely empty (void of actorList, items, and * collissions) Used by the editor * * @param x * A x coordinate to move to * @param y * A y coordinate to move to * @return True if empty. False if otherwise */ public boolean isEmpty(int x, int y) { // Check bounds if (x < 0 || y < 0 || x >= mapWidth || y >= mapHeight) { return false; } // Check if the Object is hitting something on the collideMap if (getCollidable(x, y) != 0) { return false; } // Check if object is hitting something on the itemMap if (getItem(x, y) != null) { return false; } // Actor collission if (getActor(x, y, false) != null) { return false; } return true; } /** * Move attempt method. Changes the position the map of the game object if * there are no obstructions * * @param act * The actor object trying to move * @param x * A x coordinate to move to * @param y * A y coordinate to move to * @return True if the move succeeded. False if otherwise */ public boolean canMove(Actor act, int x, int y) { if (act == null) { return false; } // Check bounds if (!isInBounds(x, y)) { return false; } // Check if the Object is hitting something on the collideMap if (getCollidable(x, y) != 0) { return false; } // Allow the Actor to move return true; } public boolean canMove(Actor act, Direction dir) { int x = act.getX(); int y = act.getY(); switch (dir) { case up: y--; break; case right: x++; break; case down: y++; break; case left: x--; break; case none: return true; } return canMove(act, x, y); } private boolean isInBounds(int x, int y) { return x > 0 && y > 0 && x <= mapWidth && y <= mapHeight; } /** * Get the cost of moving through the given tile. This can be used to make * certain areas more desirable. A simple and valid implementation of this * method would be to return 1 in all cases. * * @param mover * The mover that is trying to move across the tile * @param sx * The x coordinate of the tile we're moving from * @param sy * The y coordinate of the tile we're moving from * @param tx * The x coordinate of the tile we're moving to * @param ty * The y coordinate of the tile we're moving to * @return The relative cost of moving across the given tile */ public float getCost(Actor mover, int sx, int sy, int tx, int ty) { return 1; } /** * Write the contents of this map to a file in the correct format * * @param filename * File name of the map */ public void write(String filename) { FileOutputStream fout; DataOutputStream data; try { fout = new FileOutputStream(filename); data = new DataOutputStream(fout); // Write the map file magic data.writeUTF("RKPACMAP"); // Write map width & height data.writeInt(mapWidth); data.writeInt(mapHeight); // Write the collision map for (int x = 0; x < mapWidth; x++) { for (int y = 0; y < mapHeight; y++) { data.write(collideMap[x][y]); } } // Write the item map Item item = null; for (int x = 0; x < mapWidth; x++) { for (int y = 0; y < mapHeight; y++) { item = itemMap[x][y]; // If an item doesnt exist at (x,y), write 'false' for // nonexistant and continue if (item == null) { data.writeBoolean(false); continue; } data.writeBoolean(true); // Write properties of the item data.writeInt(item.getType()); data.writeInt(item.getX()); data.writeInt(item.getY()); data.writeInt(item.getColor().getRGB()); if (item.getType() == GameObject.OBJECT_TELEPORT) { data.writeInt(item.getTeleportX()); data.writeInt(item.getTeleportY()); } } } // Write the number of actorList, then all actor data data.writeInt(actorList.size()); for (final Actor a : actorList) { data.writeInt(a.getType()); data.writeInt(a.getX()); data.writeInt(a.getY()); data.writeInt(a.getColor().getRGB()); if (a.getType() == GameObject.OBJECT_GHOST) { data.writeBoolean(((Ghost) a).isTrapped()); } } data.close(); fout.close(); } catch (final IOException e) { System.out.println("Failed to write map file: " + e.getMessage()); } } /** * Read a file with map contents and set the properties in this map Called * by the constructor. * * @param filename * File name of the map */ private void read(String filename) { FileInputStream fin; DataInputStream data; try { fin = new FileInputStream(filename); data = new DataInputStream(fin); // Check for the magic if (!data.readUTF().equals("RKPACMAP")) { System.out.println("Not a map file!"); return; } // Read map width & height mapWidth = data.readInt(); mapHeight = data.readInt(); dotsRemaining = 0; // Initialize collideMap, a 2D array that contains all static // collidable GameObjects // We use this for fast lookup during collision detection and AI // movement paths collideMap = new byte[mapWidth][mapHeight]; // Initialize itemMap, a 2D array that contains items (dots, // powerups, cherry) on the map itemMap = new Item[mapWidth][mapHeight]; // Create m_objects, an arraylist with all actorList actorList = new ArrayList<Actor>(); // Read the collision map for (int x = 0; x < mapWidth; x++) { for (int y = 0; y < mapHeight; y++) { addCollidable(x, y, data.readByte()); } } // Read the item map for (int x = 0; x < mapWidth; x++) { for (int y = 0; y < mapHeight; y++) { // If an item doesnt exist at (x,y), continue if (!data.readBoolean()) { continue; } // Read and set properties of the item final int t = data.readInt(); final int ix = data.readInt(); final int iy = data.readInt(); final Color c = new Color(data.readInt()); addItem(new Item(t, c, this, ix, iy)); if (t == GameObject.OBJECT_TELEPORT) { final int teleX = data.readInt(); final int teleY = data.readInt(); itemMap[ix][iy].setTeleport(teleX, teleY); } } } // Read the number of actorList, then all actor data final int nActorsSize = data.readInt(); for (int i = 0; i < nActorsSize; i++) { final int t = data.readInt(); final int ix = data.readInt(); final int iy = data.readInt(); final Color c = new Color(data.readInt()); if (t == GameObject.OBJECT_PLAYER) { addActor(new Player(this, ix, iy)); } else if (t == GameObject.OBJECT_GHOST) { final boolean trap = data.readBoolean(); addActor(new Ghost(c, this, ix, iy, trap)); } /* * else { addActor(new Actor(t, c, this, ix, iy)); } */ } data.close(); fin.close(); } catch (final IOException e) { System.out.println("Failed to read map file: " + e.getMessage()); } } }

map/Path.java


other
package pacman.map; import java.util.ArrayList; /** * A path determined by some path finding algorithm. A series of steps from the * starting location to the target location. This includes a step for the * initial location. * * @author Kevin Glass */ public class Path { /** The list of steps building up this path */ private final ArrayList<Step> steps; /** * Create an empty path */ public Path() { steps = new ArrayList<Step>(); } /** * Get the length of the path, i.e. the number of steps * * @return The number of steps in this path */ public int getLength() { return steps.size(); } /** * Get the step at a given index in the path * * @param index * The index of the step to retrieve. Note this should be >= 0 * and < getLength(); * @return The step information, the position on the map. */ public Step getStep(int index) { return steps.get(index); } /** * Get the x coordinate for the step at the given index * * @param index * The index of the step whose x coordinate should be retrieved * @return The x coordinate at the step */ public int getX(int index) { return getStep(index).x; } /** * Get the y coordinate for the step at the given index * * @param index * The index of the step whose y coordinate should be retrieved * @return The y coordinate at the step */ public int getY(int index) { return getStep(index).y; } /** * Append a step to the path. * * @param x * The x coordinate of the new step * @param y * The y coordinate of the new step */ public void appendStep(int x, int y) { steps.add(new Step(x, y)); } /** * Prepend a step to the path. * * @param x * The x coordinate of the new step * @param y * The y coordinate of the new step */ public void prependStep(int x, int y) { steps.add(0, new Step(x, y)); } /** * Check if this path contains the given step * * @param x * The x coordinate of the step to check for * @param y * The y coordinate of the step to check for * @return True if the path contains the given step */ public boolean contains(int x, int y) { return steps.contains(new Step(x, y)); } /** * A single step within the path * * @author Kevin Glass */ public class Step { /** The x coordinate at the given step */ private final int x; /** The y coordinate at the given step */ private final int y; /** * Create a new step * * @param x * The x coordinate of the new step * @param y * The y coordinate of the new step */ public Step(int x, int y) { this.x = x; this.y = y; } /** * Get the x coordinate of the new step * * @return The x coodindate of the new step */ public int getX() { return x; } /** * Get the y coordinate of the new step * * @return The y coodindate of the new step */ public int getY() { return y; } /** * @see Object#hashCode() */ @Override public int hashCode() { return x * y; } /** * @see Object#equals(Object) */ @Override public boolean equals(Object other) { if (other instanceof Step) { final Step o = (Step) other; return (o.x == x) && (o.y == y); } return false; } } }

map/PathFinder.java


other
package pacman.map; import java.util.ArrayList; import java.util.Collections; import pacman.actors.Actor; import pacman.ai.AStarHeuristic; /** * A path finder implementation that uses the AStar heuristic based algorithm to * determine a path. * * @author Kevin Glass */ public class PathFinder { /** The set of nodes that have been searched through */ private final ArrayList<Node> closed; /** The set of nodes that we do not yet consider fully searched */ private final SortedNodeList open = new SortedNodeList(); /** The map being searched */ private final Map map; /** The maximum depth of search we're willing to accept before giving up */ private final int maxSearchDistance; /** The complete set of nodes across the map */ private final Node[][] nodes; /** True if we allow diaganol movement */ private final boolean allowDiagMovement; /** The heuristic we're applying to determine which nodes to search first */ private final AStarHeuristic heuristic; /** * Create a path finder with the default heuristic - closest to target. * * @param map * The map to be searched * @param maxSearchDistance * The maximum depth we'll search before giving up * @param allowDiagMovement * True if the search should try diaganol movement */ public PathFinder(Map map, int maxSearchDistance, boolean allowDiagMovement) { this(map, maxSearchDistance, allowDiagMovement, new AStarHeuristic()); } /** * Create a path finder * * @param heuristic * The heuristic used to determine the search order of the map * @param map * The map to be searched * @param maxSearchDistance * The maximum depth we'll search before giving up * @param allowDiagMovement * True if the search should try diaganol movement */ public PathFinder(Map map, int maxSearchDistance, boolean allowDiagMovement, AStarHeuristic heuristic) { this.heuristic = heuristic; this.map = map; this.maxSearchDistance = maxSearchDistance; this.allowDiagMovement = allowDiagMovement; closed = new ArrayList<Node>(); nodes = new Node[map.getWidth()][map.getHeight()]; for (int x = 0; x < map.getWidth(); x++) { for (int y = 0; y < map.getHeight(); y++) { nodes[x][y] = new Node(x, y); } } } /** * Find a path from the starting location provided (sx,sy) to the target * location (tx,ty) avoiding blockages and attempting to honour costs * provided by the tile map. * * @param mover * The entity that will be moving along the path. This provides a * place to pass context information about the game entity doing * the moving, e.g. can it fly? can it swim etc. * * @param sx * The x coordinate of the start location * @param sy * The y coordinate of the start location * @param tx * The x coordinate of the target location * @param ty * Teh y coordinate of the target location * @return The path found from start to end, or null if no path can be * found. */ public Path findPath(Actor mover, int sx, int sy, int tx, int ty) { // easy first check, if the destination is blocked, we can't get there if (!map.canMove(mover, tx, ty)) { return null; } // initial state for A*. The closed group is empty. Only the starting // tile is in the open list and it'e're already there nodes[sx][sy].cost = 0; nodes[sx][sy].depth = 0; closed.clear(); open.clear(); open.add(nodes[sx][sy]); nodes[tx][ty].parent = null; // while we haven'n't exceeded our max search depth int maxDepth = 0; while ((maxDepth < maxSearchDistance) && (open.size() != 0)) { // pull out the first node in our open list, this is determined to // be the most likely to be the next step based on our heuristic final Node current = getFirstInOpen(); if (current == nodes[tx][ty]) { break; } removeFromOpen(current); addToClosed(current); // search through all the neighbours of the current node evaluating // them as next steps for (int x = -1; x < 2; x++) { for (int y = -1; y < 2; y++) { // not a neighbour, its the current tile if ((x == 0) && (y == 0)) { continue; } // if we're not allowing diaganol movement then only // one of x or y can be set if (!allowDiagMovement) { if ((x != 0) && (y != 0)) { continue; } } // determine the location of the neighbour and evaluate it final int xp = x + current.x; final int yp = y + current.y; if (isValidLocation(mover, sx, sy, xp, yp)) { // the cost to get to this node is cost the current plus // the movement // cost to reach this node. Note that the heursitic // value is only used // in the sorted open list final float nextStepCost = current.cost + getMovementCost(mover, current.x, current.y, xp, yp); final Node neighbour = nodes[xp][yp]; // map.pathFinderVisited(xp, yp); // if the new cost we've determined for this node is // lower than // it has been previously makes sure the node hasn'e've // determined that there might have been a better path // to get to // this node so it needs to be re-evaluated if (nextStepCost < neighbour.cost) { if (inOpenList(neighbour)) { removeFromOpen(neighbour); } if (inClosedList(neighbour)) { removeFromClosed(neighbour); } } // if the node hasn't already been processed and // discarded then // reset it's cost to our current cost and add it as a // next possible // step (i.e. to the open list) if (!inOpenList(neighbour) && !(inClosedList(neighbour))) { neighbour.cost = nextStepCost; neighbour.nodeHeuristic = getHeuristicCost(mover, xp, yp, tx, ty); maxDepth = Math.max(maxDepth, neighbour.setParent(current)); addToOpen(neighbour); } } } } } // since we'e've run out of search // there was no path. Just return null if (nodes[tx][ty].parent == null) { return null; } // At this point we've definitely found a path so we can uses the parent // references of the nodes to find out way from the target location back // to the start recording the nodes on the way. final Path path = new Path(); Node target = nodes[tx][ty]; while (target != nodes[sx][sy]) { path.prependStep(target.x, target.y); target = target.parent; } path.prependStep(sx, sy); // thats it, we have our path return path; } /** * Get the first element from the open list. This is the next one to be * searched. * * @return The first element in the open list */ protected Node getFirstInOpen() { return (Node) open.first(); } /** * Add a node to the open list * * @param node * The node to be added to the open list */ protected void addToOpen(Node node) { open.add(node); } /** * Check if a node is in the open list * * @param node * The node to check for * @return True if the node given is in the open list */ protected boolean inOpenList(Node node) { return open.contains(node); } /** * Remove a node from the open list * * @param node * The node to remove from the open list */ protected void removeFromOpen(Node node) { open.remove(node); } /** * Add a node to the closed list * * @param node * The node to add to the closed list */ protected void addToClosed(Node node) { closed.add(node); } /** * Check if the node supplied is in the closed list * * @param node * The node to search for * @return True if the node specified is in the closed list */ protected boolean inClosedList(Node node) { return closed.contains(node); } /** * Remove a node from the closed list * * @param node * The node to remove from the closed list */ protected void removeFromClosed(Node node) { closed.remove(node); } /** * Check if a given location is valid for the supplied mover * * @param mover * The mover that would hold a given location * @param sx * The starting x coordinate * @param sy * The starting y coordinate * @param x * The x coordinate of the location to check * @param y * The y coordinate of the location to check * @return True if the location is valid for the given mover */ protected boolean isValidLocation(Actor mover, int sx, int sy, int x, int y) { boolean invalid = (x < 0) || (y < 0) || (x >= map.getWidth()) || (y >= map.getHeight()); if ((!invalid) && ((sx != x) || (sy != y))) { invalid = map.canMove(mover, x, y) == false; } return !invalid; } /** * Get the cost to move through a given location * * @param mover * The entity that is being moved * @param sx * The x coordinate of the tile whose cost is being determined * @param sy * The y coordiante of the tile whose cost is being determined * @param tx * The x coordinate of the target location * @param ty * The y coordinate of the target location * @return The cost of movement through the given tile */ public float getMovementCost(Actor mover, int sx, int sy, int tx, int ty) { return map.getCost(mover, sx, sy, tx, ty); } /** * Get the heuristic cost for the given location. This determines in which * order the locations are processed. * * @param mover * The entity that is being moved * @param x * The x coordinate of the tile whose cost is being determined * @param y * The y coordiante of the tile whose cost is being determined * @param tx * The x coordinate of the target location * @param ty * The y coordinate of the target location * @return The heuristic cost assigned to the tile */ public float getHeuristicCost(Actor mover, int x, int y, int tx, int ty) { return heuristic.getCost(map, mover, x, y, tx, ty); } /** * A simple sorted list * * @author kevin */ private class SortedNodeList { /** The list of elements */ private final ArrayList<Node> list = new ArrayList<Node>(); /** * Retrieve the first element from the list * * @return The first element from the list */ public Object first() { return list.get(0); } /** * Empty the list */ public void clear() { list.clear(); } /** * Add an element to the list - causes sorting * * @param o * The element to add */ public void add(Node o) { list.add(o); Collections.sort(list); } /** * Remove an element from the list * * @param o * The element to remove */ public void remove(Object o) { list.remove(o); } /** * Get the number of elements in the list * * @return The number of element in the list */ public int size() { return list.size(); } /** * Check if an element is in the list * * @param o * The element to search for * @return True if the element is in the list */ public boolean contains(Object o) { return list.contains(o); } } /** * A single node in the search graph */ private class Node implements Comparable<Object> { /** The x coordinate of the node */ private final int x; /** The y coordinate of the node */ private final int y; /** The path cost for this node */ private float cost; /** The parent of this node, how we reached it in the search */ private Node parent; /** The heuristic cost of this node */ private float nodeHeuristic; /** The search depth of this node */ private int depth; /** * Create a new node * * @param x * The x coordinate of the node * @param y * The y coordinate of the node */ public Node(int x, int y) { this.x = x; this.y = y; } /** * Set the parent of this node * * @param parent * The parent node which lead us to this node * @return The depth we have no reached in searching */ public int setParent(Node parent) { depth = parent.depth + 1; this.parent = parent; return depth; } /** * @see Comparable#compareTo(Object) */ @Override public int compareTo(Object other) { final Node o = (Node) other; final float f = nodeHeuristic + cost; final float of = o.nodeHeuristic + o.cost; if (f < of) { return -1; } else if (f > of) { return 1; } else { return 0; } } } }

state/State.java


other
package pacman.state; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import pacman.game.Game; /** * A State is a mode of the program where input and functionality are radically * different from others portions of the program. We can then effectively * separate different logical facilities into their own State subclasses * * @author Ramsey Kant */ public abstract class State implements KeyListener { // Game States public static final int STATE_MENU = 1; public static final int STATE_SCOREBOARD = 2; public static final int STATE_GAME = 4; public static final int STATE_DEAD = 8; // public static final int STATE_GAMEOVER = 16; public static final int STATE_EDITOR = 32; public static final int STATE_EXITING = 64; protected Game game; /** * Class Constructor * * @param g * Reference to the game */ public State(Game g) { game = g; reset(); } /** * Return the reference to the game object * * @return Reference to the game object */ public Game getGame() { return game; } /** * Start or reset the state * * Can be called either by the Supervisor or the state itself */ public abstract void reset(); /** * Primary logic function called in the mainThreadLoop * * Called only by the Supervisor */ public abstract void logic(); /** * Signals the state to terminate. Any final updates should be performed * here THIS IS ONLY CALLED INSIDE CHANGESTATE() - DO NOT CALL THIS ANYWHERE * ELSE */ public abstract void end(); /* * Human Input default */ @Override public void keyReleased(KeyEvent e) { // do nothing } @Override public void keyTyped(KeyEvent e) { // Esc switch (e.getKeyChar()) { case 27: game.requestChangeState(STATE_EXITING); break; default: break; } } }

state/StateEditor.java


other
package pacman.state; import java.awt.Color; import java.awt.Graphics2D; import java.awt.event.KeyEvent; import pacman.actors.Actor; import pacman.actors.Ghost; import pacman.actors.Player; import pacman.editor.EditorFrame; import pacman.editor.EditorMarker; import pacman.game.Game; import pacman.game.GameObject; import pacman.game.Item; import pacman.map.Map; /** * The StateEditor is a mode of the program that allows the user to create and * modify map files that can be played in StateGame. StateEditor is a subclass * of State * * @author Ramsey Kant */ public class StateEditor extends State { // Logic object references private final EditorFrame editorFrame; private EditorMarker marker; private boolean gameView; private Map map; // Placement variables private int markerObjectType; private byte markerWallType; private String markerGhostType; private boolean markerGhostTrapped; private int markerTeleportX; private int markerTeleportY; // Map vars. Store them as class member vars to eliminate function call // overhead for getHeight/getWidth private int mapWidth; private int mapHeight; public StateEditor(Game g) { super(g); // If true, remove all editor helpers like grid lines gameView = false; // Create the editor toolpane game.getFrame().setSize(1024, game.RES_Y); editorFrame = new EditorFrame(this); editorFrame.setVisible(true); // Defaults markerObjectType = GameObject.OBJECT_WALL; markerWallType = GameObject.WALL_VERTICAL; markerGhostType = "Blinky"; markerGhostTrapped = false; markerTeleportX = 13; markerTeleportY = 17; } // Getters and Setters /** * Set the type of object to be placed by the marker Called by the * EditorFrame (Dot, Powerup, Teleport buttons) * * @param t * Type of object (from GameObject statics) */ public void setMarkerObjectType(int t) { markerObjectType = t; } /** * Set the type of wall to be placed by the marker Called by the EditorFrame * Wall button * * @param t * Type of wall (from GameObject statics) */ public void setMarkerWallType(byte t) { markerWallType = t; } /** * Set the type of wall to be placed by the marker Called by the EditorFrame * Add Ghost button * * @param t * Type of wall (from GameObject statics) */ public void setMarkerGhostType(String t) { markerGhostType = t; } /** * Toggle the trapped status of the next ghost to be added * * @param t * True if trapped in the spawn-jail */ public void setMarkerGhostTrapped(boolean t) { markerGhostTrapped = t; } /** * Get the current trapped status * * @return True if new ghosts will be created as trapped */ public boolean getMarkerGhostTrapped() { return markerGhostTrapped; } /** * Set the teleport destination, used to aid a teleport drop Called in * EditorFrame by the Teleport add button * * @param x * destination coordinate X of the next teleport * @param y * destination coordinate Y of the next teleport */ public void setMarkerTeleport(int x, int y) { markerTeleportX = x; markerTeleportY = y; } /** * Reset the StateEditor objects like the Marker * * @see State#reset() */ @Override public void reset() { // Force previous references out of scope marker = null; map = null; markerObjectType = GameObject.OBJECT_DOT; } /** * Setup and render a new blank map * * @param width * The width of the map to be created * @param height * The height of the map to be created */ public void newMap(int width, int height) { // Setup the game map game.getGraphicsContext().setBackground(Color.BLACK); mapWidth = width; mapHeight = height; map = new Map(28, 31, 32); // Create the marker (but don't put it "in" the map) marker = new EditorMarker(Color.GREEN, map, 0, 0); } /** * Save the map * * @param filename */ public void saveMap(String filename) { map.write(System.getProperty("user.dir") + "\\" + filename); } /** * Setup and render a map loaded from the file system * * @param filename */ public void loadMap(String filename) { // Setup the game map game.getGraphicsContext().setBackground(Color.BLACK); map = new Map(System.getProperty("user.dir") + "\\" + filename, 32); mapWidth = map.getWidth(); mapHeight = map.getHeight(); // Create the marker (but don't put it "in" the map) marker = new EditorMarker(Color.GREEN, map, 0, 0); } /** * Logic of the editor processed here: Rendering, input, and object * placement. Called in the mainThreadLoop * * @see State#logic() */ @Override public void logic() { if (map == null) { return; } final Graphics2D g = game.getGraphicsContext(); // Offset the buffer so object's arent clipped by the window borders g.translate(10, 30); // Now run render logic // Paint boundaries and items Item item = null; for (int x = 0; x < mapWidth; x++) { for (int y = 0; y < mapHeight; y++) { // Get and paint the static object here final byte sType = map.getCollidable(x, y); // Switch based on wall type and paint g.setColor(Color.BLUE); switch (sType) { case 0: // Nothing break; case GameObject.WALL_VERTICAL: // Vertical wall, no edges g.fillRoundRect(x * map.CELL_SIZE + 10, y * map.CELL_SIZE, 12, map.CELL_SIZE, 0, 0); // 2x+12 = map.CELL_SIZE. // x = 10 break; case GameObject.WALL_HORIZONTAL: // Horizontal wall, no edges g.fillRoundRect(x * map.CELL_SIZE, y * map.CELL_SIZE + 10, map.CELL_SIZE, 12, 0, 0); break; case GameObject.WALL_TOPLEFT: // g.fillArc(x*map.CELL_SIZE+10, y*map.CELL_SIZE, // map.CELL_SIZE, map.CELL_SIZE, 90, 90); g.fillRoundRect(x * map.CELL_SIZE + (map.CELL_SIZE / 2), y * map.CELL_SIZE + 10, map.CELL_SIZE / 2, 12, 0, 0); g.fillRoundRect(x * map.CELL_SIZE + 10, y * map.CELL_SIZE + (map.CELL_SIZE / 2), 12, map.CELL_SIZE / 2, 0, 0); break; case GameObject.WALL_TOPRIGHT: g.fillRoundRect(x * map.CELL_SIZE, y * map.CELL_SIZE + 10, map.CELL_SIZE / 2, 12, 0, 0); g.fillRoundRect(x * map.CELL_SIZE + 10, y * map.CELL_SIZE + (map.CELL_SIZE / 2), 12, map.CELL_SIZE / 2, 0, 0); break; case GameObject.WALL_BOTTOMLEFT: g.fillRoundRect(x * map.CELL_SIZE + (map.CELL_SIZE / 2), y * map.CELL_SIZE + 10, map.CELL_SIZE / 2, 12, 0, 0); // hori g.fillRoundRect(x * map.CELL_SIZE + 10, y * map.CELL_SIZE, 12, map.CELL_SIZE / 2, 0, 0); // vert break; case GameObject.WALL_BOTTOMRIGHT: g.fillRoundRect(x * map.CELL_SIZE, y * map.CELL_SIZE + 10, map.CELL_SIZE / 2, 12, 0, 0); // hori g.fillRoundRect(x * map.CELL_SIZE + 10, y * map.CELL_SIZE, 12, map.CELL_SIZE / 2, 0, 0); // vert break; case GameObject.WALL_GHOSTBARRIER: g.setColor(Color.PINK); g.fillRoundRect(x * map.CELL_SIZE, y * map.CELL_SIZE + 10, map.CELL_SIZE, 6, 0, 0); break; default: break; } // Paint any of the items here item = map.getItem(x, y); if (item != null) { item.paint(g); } } } // Paint actors ontop final int nActors = map.getNumActors(); for (int i = 0; i < nActors; i++) { final Actor a = map.getActor(i); if (a != null) { a.paint(g); } } // Paint the marker marker.paint(g); // Paint gridline overlay if in editor view if (!gameView) { g.setColor(Color.RED); for (int i = 0; i < mapWidth; i++) { g.drawLine(i * map.CELL_SIZE, 0, i * map.CELL_SIZE, mapHeight * map.CELL_SIZE); } for (int i = 0; i < mapHeight; i++) { g.drawLine(0, i * map.CELL_SIZE, mapWidth * map.CELL_SIZE, i * map.CELL_SIZE); } // Player X,Y coordinates bottom right g.drawString("X: " + marker.getX() + ", Y: " + marker.getY(), 900, 700); } } /** * Termination of the StateEdtior. Set references stored by the StateEditor * as null * * @see State#end() */ @Override public void end() { // Cleanup marker = null; map = null; } /** * Input processing for the Editor * * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent) */ @Override public void keyPressed(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_UP: marker.changeTile(0, -1); break; case KeyEvent.VK_RIGHT: marker.changeTile(1, 0); break; case KeyEvent.VK_DOWN: marker.changeTile(0, +1); break; case KeyEvent.VK_LEFT: marker.changeTile(-1, 0); break; case KeyEvent.VK_ENTER: if (marker == null) { return; } // If not empty, bail if (!map.isEmpty(marker.getX(), marker.getY())) { return; } switch (markerObjectType) { case GameObject.OBJECT_WALL: map.addCollidable(marker.getX(), marker.getY(), markerWallType); break; case GameObject.OBJECT_DOT: map.addItem(new Item(GameObject.OBJECT_DOT, Color.WHITE, map, marker.getX(), marker.getY())); break; case GameObject.OBJECT_POWERUP: map.addItem(new Item(GameObject.OBJECT_POWERUP, Color.WHITE, map, marker .getX(), marker.getY())); break; case GameObject.OBJECT_GHOST: if (markerGhostType.equals("Blinky")) { map.addActor(new Ghost(Color.RED, map, marker.getX(), marker.getY(), markerGhostTrapped)); } else if (markerGhostType.equals("Pinky")) { map.addActor(new Ghost(Color.PINK, map, marker.getX(), marker.getY(), markerGhostTrapped)); } else if (markerGhostType.equals("Inky")) { map.addActor(new Ghost(Color.CYAN, map, marker.getX(), marker.getY(), markerGhostTrapped)); } else { map.addActor(new Ghost(Color.ORANGE, map, marker.getX(), marker.getY(), markerGhostTrapped)); } break; case GameObject.OBJECT_PLAYER: // If there is already a player in the actors list, // remove it for (int i = 0; i < map.getNumActors(); i++) { if (map.getActor(i).getType() == GameObject.OBJECT_PLAYER) { map.removeActor(i); i--; } } // Add the new player map.addActor(new Player(map, marker.getX(), marker.getY())); break; case GameObject.OBJECT_TELEPORT: final Item tel = new Item(GameObject.OBJECT_TELEPORT, Color.LIGHT_GRAY, map, marker.getX(), marker.getY()); tel.setTeleport(markerTeleportX, markerTeleportY); map.addItem(tel); break; default: break; } break; case KeyEvent.VK_DELETE: // Delete a placed object. Will reduce excessive memory // consumption if the user cant just replace a tile with a new // object // If empty, bail if (map.isEmpty(marker.getX(), marker.getY())) { return; } // Remove anything (collidable, actor, or item) at (x,y) map.removeAnyAt(marker.getX(), marker.getY()); break; case KeyEvent.VK_V: gameView = !gameView; break; case KeyEvent.VK_0: // editorFrame.setEnabled(false); // game.changeState(STATE_MENU); break; default: break; } } }

state/StateGame.java


other
package pacman.state; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.event.KeyEvent; import pacman.actors.Actor; import pacman.actors.Ghost; import pacman.actors.Player; import pacman.ai.AIManager; import pacman.game.Game; import pacman.game.GameObject; import pacman.game.Item; import pacman.map.Map; import pacman.util.Direction; /** * StateGame is a mode of the program where the user can play the game of Pacman * various maps StateGame is a subclass of State * * @author Ramsey Kant */ public class StateGame extends State { // Logic object references private Player player; private Map map; private AIManager ai; // Game vars private String mapName; private int currentLevel; private int sessionScore; // Overall score for the game session. The player // object score is only the score for that life / // level private int livesRemaining; private boolean gamePaused; private long pauseTime; // Map vars. Store them as class member vars to eliminate function call // overhead for getHeight/getWidth private int mapWidth; private int mapHeight; /** * StateGame Constructor * * @param g * Reference to the game supervisor */ public StateGame(Game g) { super(g); } /** * Get the current session score. A session score is the total score of the * entire 'game' contained by the limited number of lives This is compounded * at game over and in win() * * @see StateGame#win() * @see StateGame#lose() */ public int getSessionScore() { return sessionScore; } // Public Methods /** * Reset the state of the game entirely (level 1) * * @see State#reset() */ @Override public void reset() { // Set game vars mapName = game.getStartMap(); currentLevel = 0; sessionScore = 0; livesRemaining = 99; pauseTime = 0; // Respawn (start level 1) respawn(true); } /** * Respawns the player after a death or win. Similar to reset() except * running vars are saved * * @param nextLevel * Moves to the next level and corresponding map */ public void respawn(boolean nextLevel) { gamePaused = true; pauseTime = System.currentTimeMillis() + 3000; // If we're jumping to the next level, reset everything if (nextLevel) { currentLevel++; // Force previous references out of scope player = null; map = null; ai = null; // Setup the game map game.getGraphicsContext().setBackground(Color.BLACK); map = new Map(mapName, 0.75); mapWidth = map.getWidth(); mapHeight = map.getHeight(); // Spawn the player player = map.getPlayer(); // Setup AI ai = new AIManager(map, player, game.isDebugEnabled()); // Slighly increase the game speed } else { // Player died, reset the map // Move all actors back to their spawn positions final int nActors = map.getNumActors(); for (int i = 0; i < nActors; i++) { final Actor a = map.getActor(i); if (a != null) { a.move(a.getSpawnX(), a.getSpawnY()); a.setDead(false); if (a.getType() == GameObject.OBJECT_GHOST) { ((Ghost) a).updatePath(null); } } } } } /** * Main game logic for rendering and processing. Called by mainThreadLoop * * @see Game#mainThreadLoop() * @see State#logic() */ @Override public void logic() { if (map == null) { return; } final Graphics2D g = game.getGraphicsContext(); // Offset the buffer so object's arent clipped by the window borders g.translate(10, 30); // Paint right UI with lives remaining, score, highscore etc g.setColor(Color.WHITE); g.setFont(new Font("Comic Sans MS", Font.BOLD, 24)); g.drawString("PACMAN by Ramsey Kant", 680, 50); g.drawString("Score: " + player.getScore(), 750, 100); g.drawString("Total: " + sessionScore, 750, 150); g.drawString("Lives: " + livesRemaining, 750, 200); g.drawString("Level: " + currentLevel, 750, 250); // Execute game logic for all entites on the map if (!gamePaused) { ai.process(); player.act(); } // Check for player death. End the round if the player is dead if (player.isDead()) { lose(); return; } // Check for a win (all dots collected) if (map.getDotsRemaining() <= 0) { win(); return; } // Now run render logic // Paint boundaries and items Item item = null; for (int x = 0; x < mapWidth; x++) { for (int y = 0; y < mapHeight; y++) { // Get and paint the static object here final byte sType = map.getCollidable(x, y); // Switch based on wall type and paint g.setColor(Color.BLUE); switch (sType) { case 0: // Nothing break; case GameObject.WALL_VERTICAL: // Vertical wall, no edges g.fillRoundRect(x * map.CELL_SIZE + map.WALL_OVERLAP, y * map.CELL_SIZE, map.WALL_THICKNESS, map.CELL_SIZE, 0, 0); // 2x+12 = map.CELL_SIZE. // x = 10 break; case GameObject.WALL_HORIZONTAL: // Horizontal wall, no edges g.fillRoundRect(x * map.CELL_SIZE, y * map.CELL_SIZE + map.WALL_OVERLAP, map.CELL_SIZE, map.WALL_THICKNESS, 0, 0); break; case GameObject.WALL_TOPLEFT: g.fillRoundRect(x * map.CELL_SIZE + (map.CELL_SIZE / 2), y * map.CELL_SIZE + map.WALL_OVERLAP, map.CELL_SIZE / 2, map.WALL_THICKNESS, 0, 0); g.fillRoundRect(x * map.CELL_SIZE + map.WALL_OVERLAP, y * map.CELL_SIZE + (map.CELL_SIZE / 2), map.WALL_THICKNESS, map.CELL_SIZE / 2, 0, 0); break; case GameObject.WALL_TOPRIGHT: g.fillRoundRect(x * map.CELL_SIZE, y * map.CELL_SIZE + map.WALL_OVERLAP, map.CELL_SIZE / 2, map.WALL_THICKNESS, 0, 0); g.fillRoundRect(x * map.CELL_SIZE + map.WALL_OVERLAP, y * map.CELL_SIZE + (map.CELL_SIZE / 2), map.WALL_THICKNESS, map.CELL_SIZE / 2, 0, 0); break; case GameObject.WALL_BOTTOMLEFT: g.fillRoundRect(x * map.CELL_SIZE + (map.CELL_SIZE / 2), y * map.CELL_SIZE + map.WALL_OVERLAP, map.CELL_SIZE / 2, map.WALL_THICKNESS, 0, 0); // hori g.fillRoundRect(x * map.CELL_SIZE + map.WALL_OVERLAP, y * map.CELL_SIZE, 12, map.CELL_SIZE / 2, 0, 0); // vert break; case GameObject.WALL_BOTTOMRIGHT: g.fillRoundRect(x * map.CELL_SIZE, y * map.CELL_SIZE + map.WALL_OVERLAP, map.CELL_SIZE / 2, map.WALL_THICKNESS, 0, 0); // hori g.fillRoundRect(x * map.CELL_SIZE + map.WALL_OVERLAP, y * map.CELL_SIZE, map.WALL_THICKNESS, map.CELL_SIZE / 2, 0, 0); // vert break; case GameObject.WALL_GHOSTBARRIER: g.setColor(Color.PINK); g.fillRoundRect(x * map.CELL_SIZE, y * map.CELL_SIZE + map.WALL_OVERLAP, map.CELL_SIZE, map.WALL_THICKNESS / 2, 0, 0); break; default: break; } // Paint any of the items here item = map.getItem(x, y); if (item != null) { item.paint(g); } } } // Paint actors ontop final int nActors = map.getNumActors(); for (int i = 0; i < nActors; i++) { final Actor a = map.getActor(i); if (a != null) { a.paint(g); } } // Debug if (game.isDebugEnabled()) { g.setColor(Color.RED); g.drawString("DEBUG ON", 750, 650); /* * // Paint gridline overlay for(int i = 0; i < mapWidth; i++) * g.drawLine(i*map.CELL_SIZE, 0, i*map.CELL_SIZE, * mapHeight*map.CELL_SIZE); for(int i = 0; i < mapHeight; i++) * g.drawLine(0, i*map.CELL_SIZE, mapWidth*map.CELL_SIZE, * i*map.CELL_SIZE); */ // Player X,Y coordinates bottom right g.drawString("positionX: " + player.getX(), 750, 675); g.drawString("positionY: " + player.getY(), 750, 700); } // Check for game pause and print pause status if (gamePaused) { g.setColor(Color.RED); g.setFont(new Font("Comic Sans MS", Font.BOLD, 24)); g.drawString("PAUSED", 750, 500); if (pauseTime > System.currentTimeMillis()) { g.drawString( "Pause ends in..." + ((pauseTime - System.currentTimeMillis()) / 1000), 750, 550); } if (pauseTime != 0 && System.currentTimeMillis() > pauseTime) { pauseTime = 0; gamePaused = false; } return; } } /** * Player has died or the Supervisor has decided to change the state * abruptly * * @see State#end() */ @Override public void end() { // Cleanup player = null; map = null; } /** * Player has won, move to the next level Called by logic() * * @see StateGame#logic() */ public void win() { sessionScore += player.getScore(); respawn(true); } /** * Player has died, reset() if lives remain. Otherwise, request a state * change thereby end()ing this state Called by logic() * * @see StateGame#logic() */ public void lose() { livesRemaining--; if (livesRemaining > 0) { respawn(false); } else { if (currentLevel == 1) { sessionScore = player.getScore(); // win() never called, so // score is the 1st level // score } game.requestChangeState(State.STATE_SCOREBOARD); } } /** * Start automove in certain direction * * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent) */ @Override public void keyPressed(KeyEvent e) { if (player == null) { return; } switch (e.getKeyCode()) { case KeyEvent.VK_UP: player.setMoveDirection(Direction.up); break; case KeyEvent.VK_RIGHT: player.setMoveDirection(Direction.right); break; case KeyEvent.VK_DOWN: player.setMoveDirection(Direction.down); break; case KeyEvent.VK_LEFT: player.setMoveDirection(Direction.left); break; case KeyEvent.VK_SPACE: player.setMoveDirection(Direction.stop); break; case KeyEvent.VK_P: // Don't interupt system pauses if (pauseTime < System.currentTimeMillis()) { gamePaused = !gamePaused; } break; case KeyEvent.VK_V: game.toggleDebug(); // AI debug ai.setDebugEnabled(game.isDebugEnabled()); break; case KeyEvent.VK_0: // game.changeState(STATE_MENU); break; default: break; } } }

state/StateMenu.java


other
package pacman.state; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.event.KeyEvent; import java.io.File; import java.io.FilenameFilter; import pacman.game.Game; /** * StateMenu is a graphical representation that allows users to switch into * other States of the program like the Game and editor * * @author Ramsey Kant */ public class StateMenu extends State { // Private instance private int cursorX; private int cursorY; private byte currentOption; private byte currentMapOption; // Corresponds to the index in mapList private String[] mapList; public StateMenu(Game g) { super(g); } @Override public void reset() { // Set cursor & menu position cursorX = 380; cursorY = 310; currentOption = 0; currentMapOption = 0; // Load the map list final File dir = new File(System.getProperty("user.dir")); // It is also possible to filter the list of returned files. // This example does not return any files that start with `.'. final FilenameFilter filter = new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.endsWith(".map"); } }; // Apply the filter mapList = dir.list(filter); if (mapList == null) { System.out.println("No maps exist!"); game.requestChangeState(STATE_EXITING); return; } } /** * Cleanup Menu objects * * @see State#end() */ @Override public void end() { // do nothing } /** * Logic processing for the Menu. Rendering, Input, screen pointer * manipulation * * @see State#logic() */ @Override public void logic() { final Graphics2D g = game.getGraphicsContext(); // Draw title g.setColor(Color.YELLOW); g.setFont(new Font("Comic Sans MS", Font.BOLD, 50)); g.fillArc(56, 92, 100, 100, 35, 270); // First pacman g.drawString("PACMAN", 350, 180); g.fillArc(780, 92, 100, 100, 35, 270); // Draw menu options g.setFont(new Font("Comic Sans MS", Font.BOLD, 24)); g.drawString("Play Game", 380, 300); //g.drawString("Map Editor", 525, 340); g.drawString("Scoreboard", 380, 340); g.drawString("Exit", 380, 380); if (mapList.length > 0) { g.drawString("Current Map: " + mapList[currentMapOption], 380, 600); } else { g.drawString( "No maps detected. Have you placed the maps file in the same directory as the program?", 100, 600); } // Draw underline cursor g.setColor(Color.RED); g.fillRect(cursorX, cursorY, 150, 5); } @Override public void keyPressed(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_RIGHT: if (currentMapOption >= 0 && currentMapOption < (mapList.length - 1)) { currentMapOption++; } break; case KeyEvent.VK_LEFT: if (currentMapOption > 0 && currentMapOption <= (mapList.length - 1)) { currentMapOption--; } break; case KeyEvent.VK_DOWN: if (currentOption >= 0 && currentOption < 2) { currentOption++; cursorY += 38; } break; case KeyEvent.VK_UP: if (currentOption > 0 && currentOption <= 2) { currentOption--; cursorY -= 38; } break; case KeyEvent.VK_ENTER: // Execute the appropriate state change switch (currentOption) { case 0: // Play game if (mapList.length > 0) { game.setStartMap(mapList[currentMapOption]); game.requestChangeState(STATE_GAME); } break; case 1: // Scoreboard game.requestChangeState(STATE_SCOREBOARD); break; case 2: // Exit game.requestChangeState(STATE_EXITING); break; default: break; } break; default: break; } } }

state/StateScoreboard.java


other
package pacman.state; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.event.KeyEvent; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import pacman.game.Game; /** * StateScoreboard is a mode of the program that allows the user to view (and * sometimes modify) scores set in StateGame StateScoreboard is a subclass of * State * * @author Ramsey Kant */ public class StateScoreboard extends State { private String[] names; private int[] scores; private int numScores; /** * Class Constructor * * @param g * Reference to the Game class */ public StateScoreboard(Game g) { super(g); } // Public Functions /** * Setup the scoreboard by loading the current score file * * @see State#reset() */ @Override public void reset() { // Only the top 10 scores will be displayed names = new String[10]; scores = new int[10]; numScores = 0; // Read in the scores // readScores(); } /** * Cleanup objects and write back the scores * * @see State#end() */ @Override public void end() { // saveScores(); } /** * Render the Scoreboard and perform any updates */ @Override public void logic() { final Graphics2D g = game.getGraphicsContext(); // Draw title g.setColor(Color.YELLOW); g.setFont(new Font("Comic Sans MS", Font.BOLD, 72)); g.fillArc(156, 92, 100, 100, 35, 270); // First pacman g.drawString("Scores", 450, 180); g.fillArc(960, 92, 100, 100, 35, 270); g.fillRect(150, 200, 910, 5); g.setFont(new Font("Comic Sans MS", Font.BOLD, 24)); // Draw scores for (int i = 0; i < names.length; i++) { if (names[i] == null) { continue; } g.drawString(names[i], 150, 210); g.drawString(scores[i] + " ", 960, 210); } } /** * Output names and corresponding scores to the pacman.scores file */ public void saveScores() { FileOutputStream fout; DataOutputStream data; try { fout = new FileOutputStream("pacman.scores"); data = new DataOutputStream(fout); // Write the score file magic data.writeUTF("RKPACSCORES"); // Write # of scores in the file, then the actual scores data.writeInt(numScores); for (int i = 0; i < numScores; i++) { if (names[i] == null) { break; } data.writeUTF(names[i]); data.writeInt(scores[i]); } data.close(); fout.close(); } catch (final IOException e) { System.out.println("Failed to write score file: " + e.getMessage()); } } /** * Populate names and scores from the pacman.scores file */ public void readScores() { FileInputStream fin; DataInputStream data; try { fin = new FileInputStream("pacman.scores"); data = new DataInputStream(fin); // Check for the magic if (!data.readUTF().equals("RKPACSCORES")) { System.out.println("Not a score file!"); return; } // Read in scores numScores = data.readInt(); if (numScores > 10) { numScores = 10; } for (int i = 0; i < numScores; i++) { names[i] = data.readUTF(); scores[i] = data.readInt(); } data.close(); fin.close(); } catch (final IOException e) { System.out.println("Failed to read score file: " + e.getMessage()); } } // Input functions /** * Process input on the scoreboard (exit) * * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent) */ @Override public void keyPressed(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_0: game.requestChangeState(STATE_MENU); break; default: break; } } }

util/RequestedDirectionBuffer.java


other
package pacman.util; import java.util.LinkedList; /** * This buffer is used to remember direction changes requested by the user for * some game turns. If a direction change was requested too early (move not * possible yet, because the crossing has not been reached) the direction change * can be retired in the next steps. The size parameter of the constructor * contols how long the user input will be remembered. If another direction is * set, all previous directions will be forgotten. * * By default the buffer will return the neutral Direction.none direction. If * another direction is set, the buffer will return this direction for the next * n calls to getRequestedDirection(), where n = size. When a new direction is * set, all previously set directions are forgotten. * */ public class RequestedDirectionBuffer { private final int size; private final LinkedList<Direction> queue; public RequestedDirectionBuffer(int size) { super(); this.size = size; queue = new LinkedList<Direction>(); fill(Direction.none); } /** * Get the currently requested direction. * * @return the currently requested direction. */ public Direction getRequestedDirection() { final Direction dir = queue.poll(); queue.add(Direction.none); return dir; } /** * Set the requested direction * * @param dir * the requested direction */ public void setRequestedDirection(Direction dir) { fill(dir); } /** * Fill the queue with a direction * * @param dir * the direction. */ private void fill(Direction dir) { queue.clear(); for (int i = 0; i < size; i++) { queue.add(dir); } } }