actors/Actor.java


package
other
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;

/**
other
The x-position on the map where the actor gets spawned */
protected int
spawnX;

/**
other
The y-position on the map where the actor gets spawned */
protected int
spawnY;

/**
other
The direction in which the actor is currently oriented. */
protected Direction
currentMoveDir;

/**
other
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;

/**
other
The size of the direction request buffer. */
private final static int
DIRECTION_QUEUE_SIZE =
other
6;

/** The current orientation angle of the actor. Ignored by actors that do
* not have to orient (aka ghosts). */
protected int
dirOrient;

/**
other
The x-position delta to the current map cell, caused by movement
* in pixels. */
protected float
deltaX;

/**
other
The y-position delta to the current map cell, caused by movement
* in pixels. */
protected float
deltaY;

/**
other
The movement speed of the actor */
protected float
speed;

/**
* Actor Class Constructor
*
* @param type
*
other
Object type that is an actor
* @param
color
*
other
Base color of the actor
* @param
m
*
other
Reference to the global map object
* @param
x
*
other
X coordinate to spawn the actor at
* @param
y
*
other
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);
}

//
other
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;
}

/**
*
other
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;
}

/**
*
other
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;
}

/**
*
other
Get dead status
*
* @return True if dead, false if alive
* @see Actor
#setDead(boolean)
*/
public boolean isDead() {
return isDead;
}

/**
*
other
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;
}

/**
*
other
Get the current speed of the actor
*
* @return Current speed
* @see Actor
#setSpeed(float)
*/
public float getSpeed() {
return speed;
}

/**
*
other
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);
}

//
other
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
*
other
A x coordinate to move to
* @param
y
*
other
A y coordinate to move to
* @return True if the move succeeded. False if otherwise
* @see Map
#canMove(
other
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;
}

/**
*
other
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(
other
java.awt.Graphics2D)
*/
@Override
public abstract void
paint(Graphics2D g);

}

actors/Ghost.java


package
other
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
*
other
Color of the ghost's 'body'
* @param
m
*
other
Reference to the map
* @param
x
*
other
X coordinate to spawn at
* @param
y
*
other
Y coordinate to spawn at
* @param
trap
*
other
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 =
other
false;
}

/**
* Return the fear status of the ghost
*
* @return True if fearful
*/
public boolean
isInFear() {
return inFear;
}

/**
*
other
Set fear status. AIManager interperates this setting for behavior
*
* @param
f
*
other
Fear status, true if fearful
*/
public void
setFear(boolean f) {
inFear = f;
}

/**
*
other
Get the current trapped status
*
* @return True if the ghost is currently in the spawn-jail
*/
public boolean
isTrapped() {
return trapped;
}

/**
*
other
Set the current trapped status
*
* @param
t
*
other
Trye uf the ghost is in the spawn-jail
*/
public void
setTrapped(boolean t) {
trapped = t;
}

/**
*
other
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;
}

/**
*
other
Update the Path object for the ghost to follow'
*
* @param
p
*
other
Path object generated in process() by the AIManager
* @see AIManager
#process()
*/
public void updatePath(Path p) {
nextStepIdx = 1;
path = p;
needNewPath =
other
false;
}

/**
* Direct's the paint() function to draw the current path of the ghost on
* the map
*
* @param
d
*
other
If true, debug is on and the path will be drawn
* @see AIManager#setDebugEnabled
*/
public void
setDebugDrawPath(boolean d) {
debugDrawPath = d;
}

/**
*
other
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() {
//
other
Move to the next step
if
(path != null && nextStepIdx < path.getLength()) {

//
other
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;
}

//
other
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 = deltaY - speed;
//
other
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 = deltaX + speed;
deltaY = 0;
if (Math.abs(deltaX) >= map.CELL_SIZE) {
deltaX = 0;
move(positionX + 1, positionY);
nextStepIdx++;
}
break;
case down:
deltaX = 0;
deltaY = deltaY + speed;
if (Math.abs(deltaY) >= map.CELL_SIZE) {
deltaY = 0;
move(positionX, positionY + 1);
nextStepIdx++;
}
break;
case left:
deltaX = deltaX - 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 =
other
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),
other
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


package
other
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; //
other
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;

/**
*
other
Class Constructor for Player
*
* @param
m
*
other
Reference to the map object
* @param
x
*
other
X coordiante to spawn the player at
* @param
y
*
other
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 =
other
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
*
other
Amount to increment
*/
public void
incrementScore(int amt) {
m_iScore += amt;
}

/**
*
other
Get the current level score of the player
*
* @return the score
*/
public int
getScore() {
return m_iScore;
}

/**
*
other
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;
}

/**
*
other
Set powered up state and start the expirtation time for when the powerup
* wears off
*
* @param
x
*
other
True if powered up, false if otherwise
* @see Player
#isPoweredUp()
*/
public void setPowerUp(boolean x) {
isPowered = x;
//
other
If powered up, start the timer and increase speed temporarily
if
(isPowered) {
poweredExpireTime =
other
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) {
//
other
Notify the State of the loss if pacman isn't powered up
if
(!isPowered) {
setDead(true);
return;
} else {
a.setDead(
other
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(
other
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;
}
}

//
other
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:
//
other
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 = deltaY - speed;
//
other
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 = deltaX + 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 = deltaY + 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 = deltaX - speed;
deltaY = 0;
if (Math.abs(deltaX) >= map.CELL_SIZE) {
deltaX = 0;
move(positionX - 1, positionY);
}
}
dirOrient = 180;
break;
case stop:
case none:
//
other
do not move
}
}

/**
* Draw & animate pacman
*
* @param
g
*
other
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);

//
other
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


package
other
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
*
other
Reference to the map object being used by the game
* @param
pl
*
other
Reference to the player
* @param
debug
*
other
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;
}

//
other
Getters and Setters

/**
* Direct ghosts to display diagnostic information
*
* @param
d
*
other
If true, ghosts will enter debug mode
* @see Ghost#setDebugDrawPath
*/
public void
setDebugEnabled(boolean d) {
debugEnabled = d;
}

/**
*
other
Set the global map and player references. Ghosts being tracked (in the
* 'ghosts' ArrayList) will be updated
*
* @param
m
*
other
Reference to the map
* @param
pl
*
other
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);
}
}
}

/**
*
other
Run all logic required for AI operation; fear, ghost release, path
* updates. Ghost act() functions are called here
*/
public void
process() {
//
other
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 =
other
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 =
other
System.currentTimeMillis() + 8000;
break;
}
}
}

// Go through a list of all AI on the map
for (final Ghost
ghost : ghosts) {
//
other
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 >
other
4) {
break;
}
}

// Clear path and move to jail

ghost.updatePath(null);
ghost.move(x, y);
ghost.setTrapped(true);
ghost.setDead(
other
false);
}

// Any ghost not trapped is given the current fear status
if
(!ghost.isTrapped()) {
//
other
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(
other
false);
}

// Develop path for ghost
if
(!ghost.isTrapped() && ghost.needsNewPath()) {
int randx = player.getX();
int randy = player.getY();
//
other
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();

//
other
If debug is enabled, force ghost to draw it's path

ghost.setDebugDrawPath(debugEnabled);
}
}
}

ai/AStarHeuristic.java


package
other
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
*
other
The map on which the path is being found
* @param
mover
*
other
The entity that is moving along the path
* @param
x
*
other
The x coordinate of the tile being evaluated
* @param
y
*
other
The y coordinate of the tile being evaluated
* @param
tx
*
other
The x coordinate of the target location
* @param
ty
*
other
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


package
other
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() {
other
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.
other
setBounds(12, 218, 59, 23
);
btnWall.
other
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent
evt) {
editor.setMarkerObjectType(GameObject.OBJECT_WALL);
}
});
}
{
btnDot = new JButton();
getContentPane().add(btnDot);
btnDot.setText("Dot");
btnDot.
other
setBounds(12, 36, 59, 23
);
btnDot.
other
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent
evt) {
editor.setMarkerObjectType(GameObject.OBJECT_DOT);
}
});
}
{
btnPacman = new JButton();
getContentPane().add(btnPacman);
btnPacman.setText("Pacman");
btnPacman.
other
setBounds(136, 36, 110, 23
);
btnPacman.
other
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent
evt) {
editor.setMarkerObjectType(GameObject.OBJECT_PLAYER);
}
});
}
{
jSeparator1 = new JSeparator();
getContentPane().add(jSeparator1);
jSeparator1.
other
setBounds(12, 301, 360, 10
);
}
{
lblPlaceableObjs = new JLabel();
getContentPane().add(lblPlaceableObjs);
lblPlaceableObjs.setText("Placeable Objects");
lblPlaceableObjs.
other
setBounds(12, 12, 129, 16
);
}
{
jWallTypeLabel = new JLabel();
getContentPane().add(jWallTypeLabel);
jWallTypeLabel.setText("Wall Type");
jWallTypeLabel.
other
setBounds(12, 196, 82, 16);
}
{
final ComboBoxModel
jWallTypeComboModel =
other
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.
other
setBounds(12, 246, 153, 23
);
jWallTypeCombo.
other
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.
other
setBounds(12, 317, 70, 23
);
btnSave.
other
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent
evt) {
editor.saveMap(txtFilename.getText());
}
});
}
{
btnLoad = new JButton();
getContentPane().add(btnLoad);
btnLoad.setText("Load");
btnLoad.
other
setBounds(87, 317, 68, 23
);
btnLoad.
other
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent
evt) {
editor.loadMap(txtFilename.getText());
}
});
}
{
txtFilename = new JTextField();
getContentPane().add(txtFilename);
txtFilename.
other
setBounds(12, 345, 225, 23
);
txtFilename.setText("test.map");
}
{
btnNew = new JButton();
getContentPane().add(btnNew);
btnNew.setText("New");
btnNew.
other
setBounds(160, 317, 71, 23
);
btnNew.
other
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent
evt) {
editor.newMap(28, 31);
}
});
}
{
btnTeleport = new JButton();
getContentPane().add(btnTeleport);
btnTeleport.setText("Teleport");
btnTeleport.
other
setBounds(237, 218, 110, 23
);
btnTeleport.
other
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.
other
setBounds(237, 196, 123, 16
);
}
{
lblTeleportX = new JLabel();
getContentPane().add(lblTeleportX);
lblTeleportX.setText("Dest X:");
lblTeleportX.
other
setBounds(237, 249, 60, 16
);
}
{
lblTeleportY = new JLabel();
getContentPane().add(lblTeleportY);
lblTeleportY.setText("Dest Y: ");
lblTeleportY.
other
setBounds(235, 279, 52, 16
);
}
{
txtTeleportX = new JTextField();
getContentPane().add(txtTeleportX);
txtTeleportX.setText("13");
txtTeleportX.
other
setBounds(280, 246, 85, 23
);
}
{
txtTeleportY = new JTextArea();
getContentPane().add(txtTeleportY);
txtTeleportY.setText("17");
txtTeleportY.
other
setBounds(280, 275, 82, 20
);
}
{
btnPowerup = new JButton();
getContentPane().add(btnPowerup);
btnPowerup.setText("Powerup");
btnPowerup.
other
setBounds(12, 65, 102, 23
);
btnPowerup.
other
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.
other
setBounds(272, 12, 76, 16
);
}
{
chkGhostTrapped = new JCheckBox();
getContentPane().add(chkGhostTrapped);
chkGhostTrapped.setText("Trapped");
chkGhostTrapped.
other
setBounds(360, 10, 100, 20
);
chkGhostTrapped.
other
addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent
evt) {
editor.setMarkerGhostTrapped(!editor.getMarkerGhostTrapped());
System.out.println(editor.getMarkerGhostTrapped());
}
});
}
{
final ComboBoxModel comboGhostModel =
other
new DefaultComboBoxModel(new String[] {
"Blinky", "Pinky", "Inky", "Clyde
" });
comboGhost = new JComboBox();
getContentPane().add(comboGhost);
comboGhost.setModel(comboGhostModel);
comboGhost.
other
setBounds(272, 65, 146, 23
);
comboGhost.
other
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.
other
setBounds(272, 36, 146, 23
);
btnGhost.
other
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent
evt) {
editor.setMarkerObjectType(GameObject.OBJECT_GHOST);
}
});
}
other
pack();
this.setSize(451, 547);
} catch (final Exception
e) {
//
other
add your error handling code here

e.printStackTrace();
}
}

}

editor/EditorMarker.java


package
other
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
*
other
Color of the marker
* @param
m
*
other
Reference to the map object
* @param
x
*
other
X coordinate to initially place the marker
* @param
y
*
other
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);
}

//
other
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
*
other
Amount to change the current X coordinate by
* @param
dy
*
other
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;
}

/**
*
other
EditorMarker has a blank act() method
*
* @see GameObject
#act()
*/
@Override
public void act() {
//
other
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


package
other
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 =
other
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;

/**
*
other
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();
}

/**
*
other
Startup functionality for the program called by the constructor
*/
private void
init() {
// Debug vars
debugEnabled = false;

startMap = "test.map";
changeStateRequested =
other
false;

// Setup the game frame

frame = new Frame("Pacman");
frame.
other
setLayout(null);
setBounds(0, 0
, RES_X, RES_Y);
frame.add(this);
frame.setSize(RES_X, RES_Y);
frame.setResizable(false);
frame.
other
setVisible(true);

// Set the exit handler with an anonymous class

frame.
other
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent
e) {
// Exit main thread
runMainThread =
other
false;
}
});

// Setup double buffering
setIgnoreRepaint(true); // We'll handle repainting
createBufferStrategy(2
);
m_gBuffer = getBufferStrategy();

runMainThread =
other
true;
}

// Getter and Setter methods

/**
* Get the Frame object encapsulating the program
*
* @return The frame
*/
public Frame
getFrame() {
return frame;
}

/**
*
other
Get a 'handle' of the current graphics buffer for drawing
*
* @return The Graphics2D buffer
*/
public Graphics2D
getGraphicsContext() {
return (Graphics2D) m_gBuffer.
other
getDrawGraphics();
}

/**
* Get the name of the map to be loaded in StateGame
*
* @return Map name (with .map extension)
*/
public String
getStartMap() {
return startMap;
}

/**
*
other
Set the default starting map (set by menu)
*
* @param
m
*
other
The name of the map to load (with the .map extension)
*/
public void
setStartMap(String m) {
startMap = m;
}

/**
*
other
Return the current debug setting
*
* @return True if debug setting is on
* @see Game
#toggleDebug()
*/
public boolean isDebugEnabled() {
return debugEnabled;
}

/**
*
other
Toggle debugging. Facilities like AIManager use this flag to display
* diagnostic information like AI paths
*/
public void
toggleDebug() {
debugEnabled = !debugEnabled;
}

//
other
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 =
other
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 =
other
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);

//
other
Run the logic of the current game state here

currentState.logic();

// Show the new buffer
g.dispose();
m_gBuffer.
other
show();

try {
Thread.sleep(10);
} catch (InterruptedException
e) {
//
other
TODO Auto-generated catch block

e.
other
printStackTrace();
}

}
}

// Private Methods

/**
* Change the state of the game. Called in mainThreadLogic()
*
* @param
state
*
other
The state to set. Must match the static vars above
* @see Game
#requestChangeState(int)
* @see Game#mainThreadLoop()
*/
private void changeState(int state) {
//
other
Cleanup for the outgoing state
if
(currentState != null) {
frame.removeKeyListener(currentState);
removeKeyListener(currentState);
currentState.end();
}

//
other
Set the new state type

stateId = state;

//
other
Instance the new state (reset() is called in the construtor)
switch
(stateId) {
case State.STATE_GAME:
currentState =
other
new StateGame(this);
break;
case State
.STATE_SCOREBOARD:
currentState =
other
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 =
other
new StateEditor(this);
break;
case State
.STATE_MENU:
currentState =
other
new StateMenu(this);
break;
case State
.STATE_EXITING:
currentState = null;
runMainThread =
other
false;
break;
default:
break;
}

// Setup input handler and reset()
if
(currentState != null) {
frame.addKeyListener(currentState);
addKeyListener(currentState);
}
}
}

game/GameObject.java


package
other
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 =
other
1;
public static final int
OBJECT_POWERUP =
other
2;
public static final int
OBJECT_CHERRY =
other
4;
public static final int
OBJECT_PLAYER =
other
8;
public static final int
OBJECT_GHOST =
other
16;
public static final int
OBJECT_MARKER =
other
32; // Virtual
public static final int
OBJECT_WALL =
other
64; // Virtual
public static final int
OBJECT_TELEPORT =
other
128;

// Wall types (Walls aren't instanced GameObject's)
public static final byte
WALL_VERTICAL =
other
1;
public static final byte
WALL_HORIZONTAL =
other
2;
public static final byte
WALL_TOPLEFT =
other
3;
public static final byte
WALL_TOPRIGHT =
other
4;
public static final byte
WALL_BOTTOMLEFT =
other
5;
public static final byte
WALL_BOTTOMRIGHT =
other
6;
public static final byte
WALL_GHOSTBARRIER =
other
7;

// Generic object attributes
protected int
objType;
protected Color objColor;
protected int positionX;
protected int positionY;

//
other
Outside refereneces
protected final Map
map; //
other
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;
}

/**
*
other
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;
}

/**
*
other
Set the current base color used when rendering the object
*
* @param
c
*
other
java.awt.Color Color of object
*/
public void
setColor(Color c) {
objColor = c;
}

/**
*
other
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;
}

/**
*
other
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;
}

//
other
Public & Protected Abstract methods

/**
* Class Constructor for a game object
*
* @param
type
*
other
Type of game object (see static types above)
* @param
color
* Standard java Color
* @param m
*
other
Reference to the global Map
* @param
x
* Initial x coordinate
* @param y
*
other
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;
}

/**
*
other
Perform a "Think" cycle for the Object This includes things like self
* maintenance and movement
*/
public abstract void
act();

/**
*
other
Draw the object. Subclasses should define how they are to be drawn. This
* is called in StateGame's logic()
*
* @param
g
*
other
The graphics context
* @see StateGame
#logic()
*/
public abstract void paint(Graphics2D g);
}

game/Item.java


package
other
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;

/**
*
other
Class constructor for Item
*
* @param
type
* Object type
* @param color
*
other
Base color of the item
* @param
m
*
other
Reference to the map object
* @param
x
*
other
X coordinate the item will occupy on the map
* @param
y
*
other
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 =
other
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
*
other
Y destination coordinate
*/
public void
setTeleport(int x, int y) {
teleportDestX = x;
teleportDestY = y;
}

/**
*
other
Retrieve the teleport destination X coordinate
*
* @return X destination coordinate
* @see Item
#setTeleport(int, int)
*/
public int getTeleportX() {
return teleportDestX;
}

/**
*
other
Retrieve the teleport destination Y coordinate
*
* @return Y destination coordinate
* @see Item
#setTeleport(int, int)
*/
public int getTeleportY() {
return teleportDestY;
}

/**
*
other
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
*
other
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 =
other
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;
}

/**
*
other
Item's have no "think" process. Blank method
*
* @see GameObject
#act()
*/
@Override
public void act() {
//
other
do nothing
}

/**
* Draw the item based on it's type
*
* @see GameObject
#paint(
other
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 /
other
2;

// Render item based on type
switch
(objType) {
case OBJECT_DOT:
g.fillArc(center_x - 4, center_y -
other
4, 8, 8, 0, 360);
break;
case
OBJECT_POWERUP:
g.fillArc(center_x - 8, center_y -
other
8, 16, 16, 0, 360);
break;
case
OBJECT_TELEPORT:
g.fillOval(center_x - 6, center_y -
other
8, 12, 16);
break;
default:
break
;
}
}

}

game/JPacmanGame.java


package
other
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


package
other
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;

/**
*
other
Class constructor, inits a blank map based on a width, height, and cell
* size Used in the editor
*
* @param
w
*
other
Width of the map
* @param
h
*
other
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 =
other
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];

//
other
Initialize itemMap, a 2D array that contains items (dots, powerups,
// cherry) on the map

itemMap = new Item[mapWidth][mapHeight];

//
other
Create m_objects, an arraylist with all actorList

actorList =
other
new ArrayList<Actor>();
}

/**
* Class Constructor that reads the map data from filename
*
* @param
filename
*
other
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);

//
other
Read contents of the map file

read(filename);
}

/**
*
other
The width of the map originally set in the constructor
*
* @return The width of the map
*/
public int
getWidth() {
return mapWidth;
}

/**
*
other
The height of the map originally set in the constructor
*
* @return The height of the map
*/
public int
getHeight() {
return mapHeight;
}

/**
*
other
Get the number of actorList on the map (the size of the actorList
* ArrayList)
*
* @return Number of actorList
*/
public int
getNumActors() {
return actorList.
other
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;
}

/**
*
other
Return the item map (a 2D array of Item objects)
*
* @return item map (itemMap)
*/
public Item
[][] getItemMap() {
return itemMap;
}

/**
*
other
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;
}

/**
*
other
Add a collidable (by type) to the collideMap
*
* @param
x
* X coordinate
* @param y
* Y coordinate
* @param t
*
other
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) {
other
return false;
}

// Check if theres already something there
if
(collideMap[x][y] >
other
0) {
return false;
}

// Add to the collideMap

collideMap[x][y] = t;
other
return true;
}

/**
* Put a new item to the item map
*
* @param
item
*
other
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) {
other
return false;
}

// Add to the itemMap
if
(item.getType() == GameObject.OBJECT_DOT) {
dotsRemaining++;
}
itemMap[x][y] = item;
other
return true;
}

/**
* Put a new actor in the map (actorList ArrayList)
*
* @param
act
*
other
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) {
other
return false;
}

// Add to the array list

actorList.add(act);
other
return true;
}

/**
* Return a value at (x,y) in the collision map
*
* @param
x
* X Coordinate
* @param y
*
other
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];
}

/**
*
other
Return an item at coordinate (x,y) from within the item map (itemMap)
*
* @param
x
* X Coordinate
* @param y
*
other
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];
}

/**
*
other
Return an actor at index in the actorList ArrayList
*
* @param
idx
*
other
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;
}

/**
*
other
Find and return the player object within the local actorList ArrayList
*
* @return The player object. null if not found
*/
public Player
getPlayer() {
//
other
Get from the object map
for (final Actor
g : actorList) {
if (g.getType() == GameObject.OBJECT_PLAYER) {
return (Player) g;
}
}

other
return null;
}

/**
* Return an actor at coordinate (x,y)
*
* @param
x
* X Coordinate
* @param y
* Y Coordinate
* @param notPlayer
*
other
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) {
other
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;
}
}

other
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
*
other
Index of the actor
*/
public void
removeActor(int idx) {
actorList.remove(idx);
}

/**
*
other
Remove an item from the item array by coordinate (x, y)
*
* @param
x
*
other
X coordinate of the item
* @param
y
*
other
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] =
other
null;
}

/**
* Remove everything at coordiante (x,y) Used by the editor only
*
* @param
x
* X coordinate
* @param y
*
other
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) {
other
return false;
}

// Remove any collidable
if
(collideMap[x][y] != 0) {
collideMap[x][y] = 0;
rm =
other
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;
}

/**
*
other
Find the distance (Manhattan) between two objects
*
* @param
start
*
other
GameObject at the initial position
* @param
end
*
other
GameObject at the end position
* @return Distance (integer)
*/
public int
findDistance(GameObject start, GameObject end) {
other
return (int) Math.sqrt(Math.pow(Math.abs
(start.getX() - end.getX()),
other
2)
+ Math.pow(Math.abs
(start.getY() - end.getY()),
other
2));
}

/**
* Check if a coordinate is completely empty (void of actorList, items, and
* collissions) Used by the editor
*
* @param
x
*
other
A x coordinate to move to
* @param
y
*
other
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) {
other
return false;
}

// Check if the Object is hitting something on the collideMap
if
(getCollidable(x, y) !=
other
0) {
return false;
}

// Check if object is hitting something on the itemMap
if
(getItem(x, y) !=
other
null) {
return false;
}

// Actor collission
if
(getActor(x, y,
other
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
*
other
The actor object trying to move
* @param
x
*
other
A x coordinate to move to
* @param
y
*
other
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 ==
other
null) {
return false;
}

// Check bounds
if
(!isInBounds(x, y)) {
other
return false;
}

// Check if the Object is hitting something on the collideMap
if
(getCollidable(x, y) !=
other
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;
}

/**
*
other
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
*
other
The mover that is trying to move across the tile
* @param
sx
*
other
The x coordinate of the tile we're moving from
* @param
sy
*
other
The y coordinate of the tile we're moving from
* @param
tx
*
other
The x coordinate of the tile we're moving to
* @param
ty
*
other
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) {
other
return 1;
}

/**
* Write the contents of this map to a file in the correct format
*
* @param
filename
*
other
File name of the map
*/
public void
write(String filename) {
FileOutputStream fout;
DataOutputStream data;

try {
fout = new FileOutputStream(filename);
data = new DataOutputStream(fout);

//
other
Write the map file magic

data.
other
writeUTF("RKPACMAP");

// Write map width & height

data.writeInt(mapWidth);
data.writeInt(mapHeight);

//
other
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];

//
other
If an item doesnt exist at (x,y), write 'false' for
// nonexistant and continue
if
(item == null) {
data.writeBoolean(false);
continue;
}
data.
other
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());
}
}
}

//
other
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) {
other
System.out.println("Failed to write map file
: " + e.
other
getMessage());
}
}

/**
* Read a file with map contents and set the properties in this map Called
* by the constructor.
*
* @param
filename
*
other
File name of the map
*/
private void
read(String filename) {

FileInputStream fin;
DataInputStream data;

try {
fin = new FileInputStream(filename);
data = new DataInputStream(fin);

//
other
Check for the magic
if
(!data.
other
readUTF().equals("RKPACMAP")) {
System.out.println("Not a map file!");
return;
}

// Read map width & height

mapWidth = data.readInt();
mapHeight = data.readInt();
dotsRemaining =
other
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];

//
other
Initialize itemMap, a 2D array that contains items (dots,
// powerups, cherry) on the map

itemMap = new Item[mapWidth][mapHeight];

//
other
Create m_objects, an arraylist with all actorList

actorList = new ArrayList<Actor>();

//
other
Read the collision map
for
(int x = 0; x < mapWidth; x++) {
for (int y = 0; y < mapHeight; y++) {
addCollidable(x, y, data.readByte());
}
}

//
other
Read the item map
for
(int x = 0; x < mapWidth; x++) {
for (int y = 0; y < mapHeight; y++) {
//
other
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));
} /*
*
other
else { addActor(new Actor(t, c, this, ix, iy
)); }
*/
}

data.close();
fin.close();
} catch (final IOException e) {
other
System.out.println("Failed to read map file
: " + e.getMessage());
}
}

}

map/Path.java


package
other
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;

/**
*
other
Create an empty path
*/
public Path
() {
steps =
other
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.
other
size();
}

/**
* Get the step at a given index in the path
*
* @param
index
*
other
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);
}

/**
*
other
Get the x coordinate for the step at the given index
*
* @param
index
*
other
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;
}

/**
*
other
Get the y coordinate for the step at the given index
*
* @param
index
*
other
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;
}

/**
*
other
Append a step to the path.
*
* @param
x
*
other
The x coordinate of the new step
* @param
y
*
other
The y coordinate of the new step
*/
public void
appendStep(int x, int y) {
steps.add(new Step(x, y));
}

/**
*
other
Prepend a step to the path.
*
* @param
x
*
other
The x coordinate of the new step
* @param
y
*
other
The y coordinate of the new step
*/
public void
prependStep(int x, int y) {
steps.add(0, new Step(x, y));
}

/**
*
other
Check if this path contains the given step
*
* @param
x
*
other
The x coordinate of the step to check for
* @param
y
*
other
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));
}

/**
*
other
A single step within the path
*
* @author Kevin Glass
*/
public class Step {
/** The x coordinate at the given step */
private final int
x;
/**
other
The y coordinate at the given step */
private final int
y;

/**
*
other
Create a new step
*
* @param
x
*
other
The x coordinate of the new step
* @param
y
*
other
The y coordinate of the new step
*/
public Step(int
x, int y) {
this.x = x;
this.y = y;
}

/**
*
other
Get the x coordinate of the new step
*
* @return The x coodindate of the new step
*/
public int
getX() {
return x;
}

/**
*
other
Get the y coordinate of the new step
*
* @return The y coodindate of the new step
*/
public int
getY() {
return y;
}

/**
* @
other
see Object#hashCode()
*/
@Override
public int hashCode() {
return
x * y;
}

/**
* @
other
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


package
other
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;
/**
other
The set of nodes that we do not yet consider fully searched */
private final SortedNodeList
open =
other
new SortedNodeList();

/** The map being searched */
private final Map
map;
/**
other
The maximum depth of search we're willing to accept before giving up */
private final int
maxSearchDistance;

/**
other
The complete set of nodes across the map */
private final Node
[][] nodes;
/**
other
True if we allow diaganol movement */
private final boolean
allowDiagMovement;
/**
other
The heuristic we're applying to determine which nodes to search first */
private final AStarHeuristic
heuristic;

/**
*
other
Create a path finder with the default heuristic - closest to target.
*
* @param
map
*
other
The map to be searched
* @param
maxSearchDistance
*
other
The maximum depth we'll search before giving up
* @param
allowDiagMovement
*
other
True if the search should try diaganol movement
*/
public PathFinder(Map
map, int maxSearchDistance, boolean allowDiagMovement) {
this(map, maxSearchDistance, allowDiagMovement,
other
new AStarHeuristic());
}

/**
* Create a path finder
*
* @param
heuristic
*
other
The heuristic used to determine the search order of the map
* @param
map
*
other
The map to be searched
* @param
maxSearchDistance
*
other
The maximum depth we'll search before giving up
* @param
allowDiagMovement
*
other
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);
}
}
}

/**
*
other
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
*
other
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
*
other
The x coordinate of the start location
* @param
sy
*
other
The y coordinate of the start location
* @param
tx
*
other
The x coordinate of the target location
* @param
ty
*
other
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) {
//
other
easy first check, if the destination is blocked, we can't get there

if
(!map.canMove(mover, tx, ty)) {
other
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() !=
other
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);

//
other
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++) {
//
other
not a neighbour, its the current tile

if
((x == 0) && (y ==
other
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 !=
other
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)) {
//
other
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);

//
other
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);
}
}

//
other
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);
}
}
}
}
}

//
other
since we'e've run out of search
// there was no path. Just return null

if
(nodes[tx][ty].parent ==
other
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);

//
other
thats it, we have our path

return
path;
}

/**
*
other
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();
}

/**
*
other
Add a node to the open list
*
* @param
node
*
other
The node to be added to the open list
*/
protected void
addToOpen(Node node) {
open.add(node);
}

/**
*
other
Check if a node is in the open list
*
* @param
node
*
other
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);
}

/**
*
other
Remove a node from the open list
*
* @param
node
*
other
The node to remove from the open list
*/
protected void
removeFromOpen(Node node) {
open.remove(node);
}

/**
*
other
Add a node to the closed list
*
* @param
node
*
other
The node to add to the closed list
*/
protected void
addToClosed(Node node) {
closed.add(node);
}

/**
*
other
Check if the node supplied is in the closed list
*
* @param
node
*
other
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);
}

/**
*
other
Remove a node from the closed list
*
* @param
node
*
other
The node to remove from the closed list
*/
protected void
removeFromClosed(Node node) {
closed.remove(node);
}

/**
*
other
Check if a given location is valid for the supplied mover
*
* @param
mover
*
other
The mover that would hold a given location
* @param
sx
*
other
The starting x coordinate
* @param
sy
*
other
The starting y coordinate
* @param
x
*
other
The x coordinate of the location to check
* @param
y
*
other
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;
}

/**
*
other
Get the cost to move through a given location
*
* @param
mover
*
other
The entity that is being moved
* @param
sx
*
other
The x coordinate of the tile whose cost is being determined
* @param
sy
*
other
The y coordiante of the tile whose cost is being determined
* @param
tx
*
other
The x coordinate of the target location
* @param
ty
*
other
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);
}

/**
*
other
Get the heuristic cost for the given location. This determines in which
* order the locations are processed.
*
* @param
mover
*
other
The entity that is being moved
* @param
x
*
other
The x coordinate of the tile whose cost is being determined
* @param
y
*
other
The y coordiante of the tile whose cost is being determined
* @param
tx
*
other
The x coordinate of the target location
* @param
ty
*
other
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);
}

/**
*
other
A simple sorted list
*
* @author kevin
*/
private class SortedNodeList {
/** The list of elements */
private final ArrayList<Node
> list =
other
new ArrayList<Node>();

/**
* Retrieve the first element from the list
*
* @return The first element from the list
*/
public Object
first() {
return list.
other
get(0);
}

/**
* Empty the list
*/
public void
clear() {
list.
other
clear();
}

/**
* Add an element to the list - causes sorting
*
* @param
o
*
other
The element to add
*/
public void
add(Node o) {
list.add(o);
Collections.sort(list);
}

/**
*
other
Remove an element from the list
*
* @param
o
*
other
The element to remove
*/
public void
remove(Object o) {
list.remove(o);
}

/**
*
other
Get the number of elements in the list
*
* @return The number of element in the list
*/
public int
size() {
return list.
other
size();
}

/**
* Check if an element is in the list
*
* @param
o
*
other
The element to search for
* @return True if the element is in the list
*/
public boolean
contains(Object o) {
return list.contains(o);
}
}

/**
*
other
A single node in the search graph
*/
private class Node implements Comparable<Object> {
/** The x coordinate of the node */
private final int
x;
/**
other
The y coordinate of the node */
private final int
y;
/**
other
The path cost for this node */
private float
cost;
/**
other
The parent of this node, how we reached it in the search */
private Node
parent;
/**
other
The heuristic cost of this node */
private float
nodeHeuristic;
/**
other
The search depth of this node */
private int
depth;

/**
*
other
Create a new node
*
* @param
x
*
other
The x coordinate of the node
* @param
y
*
other
The y coordinate of the node
*/
public Node(int
x, int y) {
this.x = x;
this.y = y;
}

/**
*
other
Set the parent of this node
*
* @param
parent
*
other
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;
}

/**
* @
other
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) {
other
return 1;
} else {
return 0
;
}
}
}
}

state/State.java


package
other
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 =
other
1;
public static final int
STATE_SCOREBOARD =
other
2;
public static final int
STATE_GAME =
other
4;
public static final int
STATE_DEAD =
other
8;
// public static final int STATE_GAMEOVER = 16;
public static final int
STATE_EDITOR =
other
32;
public static final int
STATE_EXITING = 64;

protected Game game;

/**
* Class Constructor
*
* @param g
*
other
Reference to the game
*/
public State(Game
g) {
game = g;
reset();
}

/**
*
other
Return the reference to the game object
*
* @return Reference to the game object
*/
public Game
getGame() {
return game;
}

/**
*
other
Start or reset the state
*
* Can be called either by the Supervisor or the state itself
*/
public abstract void
reset();

/**
*
other
Primary logic function called in the mainThreadLoop
*
* Called only by the Supervisor
*/
public abstract void
logic();

/**
*
other
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();

/*
*
other
Human Input default
*/

@Override
public void keyReleased(KeyEvent
e) {
//
other
do nothing
}

@Override
public void keyTyped(KeyEvent
e) {
// Esc
switch (e.getKeyChar()) {
case 27:
game.requestChangeState(STATE_EXITING);
break;
default:
break;
}
}
}

state/StateEditor.java


package
other
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;

//
other
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);

//
other
If true, remove all editor helpers like grid lines

gameView =
other
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 =
other
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
*
other
Type of object (from GameObject statics)
*/
public void
setMarkerObjectType(int t) {
markerObjectType = t;
}

/**
*
other
Set the type of wall to be placed by the marker Called by the EditorFrame
* Wall button
*
* @param
t
*
other
Type of wall (from GameObject statics)
*/
public void
setMarkerWallType(byte t) {
markerWallType = t;
}

/**
*
other
Set the type of wall to be placed by the marker Called by the EditorFrame
* Add Ghost button
*
* @param
t
*
other
Type of wall (from GameObject statics)
*/
public void
setMarkerGhostType(String t) {
markerGhostType = t;
}

/**
*
other
Toggle the trapped status of the next ghost to be added
*
* @param
t
*
other
True if trapped in the spawn-jail
*/
public void
setMarkerGhostTrapped(boolean t) {
markerGhostTrapped = t;
}

/**
*
other
Get the current trapped status
*
* @return True if new ghosts will be created as trapped
*/
public boolean
getMarkerGhostTrapped() {
return markerGhostTrapped;
}

/**
*
other
Set the teleport destination, used to aid a teleport drop Called in
* EditorFrame by the Teleport add button
*
* @param
x
*
other
destination coordinate X of the next teleport
* @param
y
*
other
destination coordinate Y of the next teleport
*/
public void
setMarkerTeleport(int x, int y) {
markerTeleportX = x;
markerTeleportY = y;
}

/**
*
other
Reset the StateEditor objects like the Marker
*
* @see State
#reset()
*/
@Override
public void reset() {
//
other
Force previous references out of scope

marker = null;
map = null;

markerObjectType = GameObject.OBJECT_DOT;
}

/**
*
other
Setup and render a new blank map
*
* @param
width
*
other
The width of the map to be created
* @param
height
*
other
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 =
other
new Map(28, 31, 32);

// Create the marker (but don't put it "in" the map
)
marker = new EditorMarker(Color.GREEN, map,
other
0, 0);
}

/**
* Save the map
*
* @param
filename
*/
public void saveMap(String filename) {
map.write(System.getProperty("user.dir") + "\\" + filename);
}

/**
*
other
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 =
other
new Map(System.getProperty("user.dir
") + "\\" + filename, 32);
mapWidth = map.getWidth();
mapHeight = map.getHeight();

//
other
Create the marker (but don't put it "in" the map
)
marker = new EditorMarker(Color.GREEN, map,
other
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();

//
other
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);

//
other
Switch based on wall type and paint

g.setColor(Color.BLUE);
switch (sType) {
other
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,
other
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,
other
12, 0, 0);
break;
case GameObject
.WALL_TOPLEFT:
//
other
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 /
other
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 /
other
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 /
other
2, 12, 0, 0); // hori

g.fillRoundRect(x * map.CELL_SIZE + 10, y * map.CELL_SIZE, 12,
map.CELL_SIZE /
other
2, 0, 0); // vert
break;
case GameObject
.WALL_BOTTOMRIGHT:
g.fillRoundRect(x * map.CELL_SIZE, y * map.CELL_SIZE + 10,
map.CELL_SIZE /
other
2, 12, 0, 0); // hori

g.fillRoundRect(x * map.CELL_SIZE + 10, y * map.CELL_SIZE, 12,
map.CELL_SIZE /
other
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,
other
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);

//
other
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);
}

//
other
Player X,Y coordinates bottom right

g.drawString("X: " + marker.getX() + ", Y: " + marker.getY(),
other
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 =
other
null;
}

/**
* Input processing for the Editor
*
* @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent)
*/
@Override
public void keyPressed(KeyEvent
e) {
switch (e.
other
getKeyCode()) {
case KeyEvent.VK_UP
:
marker.changeTile(
other
0, -1);
break;
case KeyEvent.VK_RIGHT
:
marker.changeTile(
other
1, 0);
break;
case KeyEvent.VK_DOWN
:
marker.changeTile(
other
0, +1);
break;
case KeyEvent.VK_LEFT
:
marker.changeTile(-
other
1, 0);
break;
case KeyEvent.VK_ENTER:
if
(marker ==
other
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:
//
other
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);
other
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())) {
other
return;
}

// Remove anything (collidable, actor, or item) at (x,y
)
map.removeAnyAt(marker.getX(), marker.getY());
other
break;
case KeyEvent.VK_V
:
gameView = !gameView;
other
break;
case KeyEvent.VK_0:
// editorFrame.setEnabled(false);
// game.changeState(STATE_MENU);
break;
default:
break
;
}
}

}

state/StateGame.java


package
other
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; //
other
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;

//
other
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
*
other
Reference to the game supervisor
*/
public StateGame(Game
g) {
super(g);
}

/**
*
other
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;
}

//
other
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 =
other
0;

// Respawn (start level 1
)
respawn(
other
true);
}

/**
* Respawns the player after a death or win. Similar to reset() except
* running vars are saved
*
* @param
nextLevel
*
other
Moves to the next level and corresponding map
*/
public void
respawn(boolean nextLevel) {
gamePaused = true;
pauseTime =
other
System.currentTimeMillis() + 3000;

// If we're jumping to the next level, reset everything
if
(nextLevel) {
currentLevel++;

//
other
Force previous references out of scope

player = null;
map = null;
ai =
other
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());

//
other
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(
other
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();

//
other
Offset the buffer so object's arent clipped by the window borders

g.
other
translate(10, 30);

// Paint right UI with lives remaining, score, highscore etc

g.setColor(Color.WHITE);
g.
other
setFont(new Font("Comic Sans MS", Font.BOLD, 24
));
g.
other
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,
other
750, 250);

// Execute game logic for all entites on the map
if
(!gamePaused) {
ai.process();
player.act();
}

//
other
Check for player death. End the round if the player is dead
if
(player.isDead()) {
lose();
other
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);

//
other
Switch based on wall type and paint

g.setColor(Color.BLUE);
switch (sType) {
other
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,
other
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,
other
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 /
other
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 /
other
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 /
other
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 /
other
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 /
other
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.
other
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(),
other
750, 700);
}

// Check for game pause and print pause status
if
(gamePaused) {
g.setColor(Color.RED);
g.
other
setFont(new Font("Comic Sans MS", Font.BOLD, 24
));
g.
other
drawString("PAUSED", 750, 500);
if
(pauseTime > System.currentTimeMillis()) {
g.drawString(
"Pause ends in..." + ((pauseTime -
other
System.currentTimeMillis()) / 1000),
750, 550);
}
if
(pauseTime != 0 && System.currentTimeMillis() > pauseTime) {
pauseTime = 0;
gamePaused =
other
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 =
other
null;
}

/**
* Player has won, move to the next level Called by logic()
*
* @see StateGame
#logic()
*/
public void win() {
sessionScore += player.getScore();

respawn(
other
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(); //
other
win() never called, so
// score is the 1st level
// score

}
game.requestChangeState(State.STATE_SCOREBOARD);
}
}

/**
*
other
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.
other
getKeyCode()) {
case KeyEvent.VK_UP
:
player.setMoveDirection(Direction.up);
other
break;
case KeyEvent.VK_RIGHT
:
player.setMoveDirection(Direction.right);
other
break;
case KeyEvent.VK_DOWN
:
player.setMoveDirection(Direction.down);
other
break;
case KeyEvent.VK_LEFT
:
player.setMoveDirection(Direction.left);
other
break;
case KeyEvent.VK_SPACE
:
player.setMoveDirection(Direction.stop);
other
break;
case KeyEvent.VK_P:
// Don't interupt system pauses
if
(pauseTime < System.currentTimeMillis()) {
gamePaused = !gamePaused;
}
other
break;
case KeyEvent.VK_V
:
game.toggleDebug();
// AI debug
ai.setDebugEnabled(game.isDebugEnabled());
other
break;
case KeyEvent.VK_0:
// game.changeState(STATE_MENU);
break;
default:
break
;
}
}
}

state/StateMenu.java


package
other
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; //
other
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 =
other
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 =
other
new FilenameFilter() {
@Override
public boolean accept(File
dir, String name) {
return name.
other
endsWith(".map");
}
};

// Apply the filter

mapList = dir.list(filter);

if (mapList ==
other
null) {
System.out.println("No maps exist
!");
game.requestChangeState(STATE_EXITING);
other
return;
}
}

/**
* Cleanup Menu objects
*
* @see State
#end()
*/
@Override
public void end() {
//
other
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.
other
setFont(new Font("Comic Sans MS", Font.BOLD, 50
));
g.
other
fillArc(56, 92, 100, 100, 35, 270); // First pacman

g.drawString("PACMAN", 350, 180);
g.
other
fillArc(780, 92, 100, 100, 35, 270);

// Draw menu options

g.
other
setFont(new Font("Comic Sans MS", Font.BOLD, 24
));
g.
other
drawString("Play Game", 380, 300);
//g.drawString("Map Editor", 525, 340
);
g.drawString("Scoreboard", 380, 340);
g.
other
drawString("Exit", 380, 380);
if
(mapList.length > 0) {
g.drawString("Current Map: " + mapList[currentMapOption], 380, 600);
} else {
g.
other
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,
other
150, 5);
}

@Override
public void keyPressed(KeyEvent
e) {
switch (e.
other
getKeyCode()) {
case KeyEvent.VK_RIGHT:
if
(currentMapOption >= 0 && currentMapOption < (mapList.length - 1)) {
currentMapOption++;
}
other
break;
case KeyEvent.VK_LEFT:
if
(currentMapOption > 0 && currentMapOption <= (mapList.length - 1)) {
currentMapOption--;
}
other
break;
case KeyEvent.VK_DOWN:
if
(currentOption >= 0 && currentOption < 2) {
currentOption++;
cursorY +=
other
38;
}
break;
case KeyEvent.VK_UP:
if
(currentOption > 0 && currentOption <= 2) {
currentOption--;
cursorY -=
other
38;
}
break;
case KeyEvent.VK_ENTER:
// Execute the appropriate state change
switch
(currentOption) {
other
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);
other
break;
default:
break;
}
break;
default:
break
;
}
}

}

state/StateScoreboard.java


package
other
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
*
other
Reference to the Game class
*/
public StateScoreboard(Game
g) {
super(g);
}

//
other
Public Functions

/**
* Setup the scoreboard by loading the current score file
*
* @see State
#reset()
*/
@Override
public void reset() {
//
other
Only the top 10 scores will be displayed

names = new String[10];
scores = new int[10];
numScores =
other
0;

// Read in the scores
// readScores();
}

/**
* Cleanup objects and write back the scores
*
* @see State
#end()
*/
@Override
public void end() {
//
other
saveScores();
}

/**
* Render the Scoreboard and perform any updates
*/
@Override
public void
logic() {
final Graphics2D g = game.getGraphicsContext();

// Draw title
g.setColor(Color.YELLOW);
g.
other
setFont(new Font("Comic Sans MS", Font.BOLD, 72
));
g.
other
fillArc(156, 92, 100, 100, 35, 270); // First pacman

g.drawString("Scores", 450, 180);
g.
other
fillArc(960, 92, 100, 100, 35, 270
);
g.
other
fillRect(150, 200, 910, 5
);

g.
other
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] + " ",
other
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);

//
other
Write the score file magic

data.
other
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) {
other
System.out.println("Failed to write score file
: " + e.
other
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);

//
other
Check for the magic
if
(!data.
other
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) {
other
System.out.println("Failed to read score file
: " + e.
other
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.
other
getKeyCode()) {
case KeyEvent.VK_0
:
game.requestChangeState(STATE_MENU);
break;
default:
break;
}
}

}

util/Direction.java


package
other
pacman.util;
public enum Direction
{
up, right, down, left, none, stop;
}

util/RequestedDirectionBuffer.java


package
other
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);
}

/**
*
other
Get the currently requested direction.
*
* @return the currently requested direction.
*/
public Direction
getRequestedDirection() {
final Direction dir = queue.poll();
queue.add(Direction.none);
return dir;
}

/**
*
other
Set the requested direction
*
* @param
dir
*
other
the requested direction
*/
public void
setRequestedDirection(Direction dir) {
fill(dir);
}

/**
*
other
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);
}
}
}