
/* Generated by Interface Builder */

#import "PacManView.h"
#import <libc.h>	// event stuff, misc.
#import "FruitView.h"
#import "PreferencesBrain.h"
#import "Maze.h"
#import "Monster.h"
#import "Player.h"
#import <appkit/graphics.h>	// for drawing
#import <appkit/NXImage.h>	// for tiff rendering
#import <appkit/Application.h>	// event stuff, misc.

// decides which screen (1-6) to load for a given level.  Allows us to
// put them in an arbitrary order.
static int screens[NUMSCREENS] = {	// which maze image (1-6) to use
                      1, 1, 1, 1, 2, 2, 2, 2,
                      3, 3, 3, 4, 4, 5, 5, 5,
                      1, 2, 0, 0, 3, 4, 0, 5 };

// fruit values
static long fruitval[NUMFRUITVALS + 1] = { 0, 100, 200, 300, 300, 500, 700,
	700, 1000, 1000, 2000, 2000, 3000, 3000, 3000, 3000, 5000 };


@implementation PacManView

- initFrame:(const NXRect *)frm    // designated initializer for a view
{    
    [super initFrame:frm];
    begin = 128;
 
// initialize game variables
	dotEat = NO;
    
	// where the maze is located within our view.
	mazePos.x = 13; mazePos.y = 12;
	myorigin.x = 0; myorigin.y = 0;
	// a convenient rect; this way I don't have to keep creating a bunch of
	// rects of dimensions GHOST_SIZE by GHOST_SIZE; I re-use this one...
	NXSetRect(&eraseRect, 0, 0, GHOST_SIZE, GHOST_SIZE);
	NXSetRect(&textEraseRect, 0, 0, 3 * GHOST_SIZE, 2 * GHOST_SIZE);
	// make sure power dots get drawn
	erasePwr = YES;
	
    return self;
}

// Called by appDidInit to load up images, etc. that we need.
- loadPix
{
    int i;
	const int *gh;

	[super loadPix];
	
	[readyText setStringValue:""]; // clear out the TextField.
	[fruitText setStringValue:""]; // clear out the TextField.
	[self addSubview:readyText];
	[self addSubview:fruitText];
	fruitPointCount = 0;
	
// get textfields for displaying ghost point values
	ghostText[0] = ghost1text;
	ghostText[1] = ghost2text;
	ghostText[2] = ghost3text;
	ghostText[3] = ghost4text;
	
// build the ghost objects
	gh = [maze ghosts]; 
	for (i=0; i<=3; i++) {
		ghost[i] = [[Monster alloc]
				initGhost:i player:player maze:maze at:gh[i*2] :gh[i*2+1]];
		[ghostText[i] setStringValue:""]; // clear out the TextFields.
		// It's impossible to get a color that makes this text stand out in
		// all situations.  Turns out that whit is the best, since that's the
		// maze color.  Trouble is, the "1" in "1600" disappears into the
		// maze wall.  I suppose I could use tiffs that have white text
		// ringed with black, but I prefer the TextFields myself.  Simpler.
		//[ghostText[i] setTextColor:NX_COLORRED]; // make them all red.
		[self addSubview:ghostText[i]];
		ghostPointCount[i] = 0;
	}
	
// get the images
	fruit = [NXImage findImageNamed:"Fruit.tiff"];
	gameOver = [NXImage findImageNamed:"GameOver.tiff"];
	[gameOver getSize:&gameOverSize];

	return self;
}

- ghost:(int)i				// return ghost #i
{
	return ghost[i];
}


// State machine...called by the timed entry.  This state machine basically
// controls all the aspects of the game; depending upon it's state, different
// things can/will happen.  The best way to figure this one out is to sit
// down and draw a diagram show how the states transition from one to the
// next.  Note that there are several counters, etc. that function as smaller
// independent state machines that operate within the context of this larger
// state machine.  (Dots blinking, monster states, etc. all run independently
// from main state machine, although the main machine occasionally interrupts
// things in the sub-machines.)  I've not yet had time to fully document the
// logic involved in this state machine...if I ever have the time, I may do so,
// but I don't know that anyone would care if I did anyway.  Minor changes in
// here could render the entire game non-functional if you aren't careful!
// UNLESS YOU KNOW WHAT YOU'RE DOING, DON'T MESS WITH THIS CODE!  Take time
// to understand it fully before playing with it!
// This is the most ridiculously long method you'll ever see, but there's
// no really efficient way to break it up.  (Each section deals with what
// happens in a specific state, and breaking into smaller methods isn't worth
// while, since things are repeated...and it's silly to proliferate subroutines
// that only get called once and from one place.)
- autoUpdate:sender
{			// ALL animation is controlled from here!!!
	register int i, pts, lcnt;
	int x, y;
	BOOL flag = NO;
	BOOL updateFlag = NO;
	float gx, gy;

    // keep track of how many time we've been called; we can use it do
	// decide when to do certain things...
	if ([NXApp isHidden]) return self; // don't suck cycles if we're hidden
	cycles++;
	if (((![preferences speed]) || ([preferences speed] == 2))
			&& (!demoMode)) { // slow speed, so ignore 75% of entries
		if (cycles & 0x03) return self;
	}

	if (!(cycles & 0x0f)) { // power dots always blink no matter what.
		[maze blinkPowerDot];	// happens every 16 cycles.
		erasePwr = YES;
	}
	
	if (fruitPointCount) {
		if (!--fruitPointCount) {
			[fruitText setStringValue:""]; // clear out the TextField.
			fruitPointCount = WIPETEXT;
	}	}

	for (i=0; i<4; i++) {
		if (ghostPointCount[i]) {
			if (!--ghostPointCount[i]) {
				[ghostText[i] setStringValue:""]; // clear out the TextField.
				ghostPointCount[i] = WIPETEXT;
	}	}	}

	if (state == GAMEOVER) {
	// when demowait counter hits WAITFORDEMO, we start up demo mode.
	// it basically restarts the game--but with "demoMode" turned on.
		if (demoWait++ == WAITFORDEMO) {
			state = NORMALSTATE;
			[self restartGame];
			demoMode = YES;
			[controller unpause];
			[[self window] setTitle:"PacMan Demo"];
			[controller zeroScore];
	}	}
	
	// in this state, the "Get Ready!" sign has been put up; we stall for a]
	// while and then we take it away and start up the next level.
	if (state == READY) {
		for (i=0; i<=3; i++) {
			[ghost[i] move:self];
		}
		if (!(--begin)) {
			state = NORMALSTATE;
			[readyText setStringValue:""];
			[controller nextLevel:self];
			[player resetPlayer];
			updateFlag = YES;
	}	}

	// identical to the above, but we re-start on the same level without
	// re-loading the maze...if we did, all the dots would be restored.
	if (state == DIEREADY) {
		for (i=0; i<=3; i++) {
			[ghost[i] move:self];
		}
		if (!(--begin)) {
			state = NORMALSTATE;
			[readyText setStringValue:""];
			[self startScreen];
			[player resetPlayer];
			updateFlag = YES;
	}	}

	// make the maze blink on and off at the end of the level.
	if (state == BLINK_LEVEL) {
		if (begin--) {
			if (begin < 32) {
				if (!(cycles & 0x03)) { // make the maze blink
					[maze visible:(![maze isVisible])];
					updateFlag = YES;
			}	}
		} else {
			state = READY;
			[maze playerPosition:&x :&y];
			[readyText moveTo:(x - 2 * GHOST_SIZE + mazePos.x)
				:(y + mazePos.y)];
			[readyText setStringValue:"Get Ready!"];
			begin = 64;
		}
	}

	// stall.  when player dies, the player object needs time to get through
	// it's whole sequence.
	if (state == DYING_PAC) {
		if (!(--begin)) {	// stall
			if (![player newPlayer]) {	// no pacs left == game over.
				state = GAMEOVER;
				[controller gameOver];
				updateFlag = YES;
			} else { // do ready, next pac...
				state = DIEREADY;
				[maze playerPosition:&x :&y];
				[readyText moveTo:(x - 2 * GHOST_SIZE + mazePos.x)
								 :(y + mazePos.y)];
				[readyText setStringValue:"Get Ready!"];
				begin = 64;
	}	}	}

	// this state is the meat of the game.  move the player and monsters
	//  and deal with player/ghost collisions and eating dots and fruit.
    if (state == NORMALSTATE) {
		// move all the ghosts and the pac
		if (!paused) {
			if (demoMode) lcnt = 2;
			else {
				lcnt = [preferences speed] / 2 + 1;	// allow hyper speeds:
			// since 0 <= speed <= 3, we'll move one or two frames per update
			}
			while (lcnt) {
				// figure out how to move player
				[player move:self];

				// put up/take away the fruit
				if ((numFruits < 2)||demoMode) { // only two fruits per level
					if (fruitCount++ == timeToFruit) { // time for new fruit ?
						fruitOn = DRAW; numFruits++;
					}
					if (fruitCount == ERASEFRUIT) {
						// it's been there a while... so remove it
						timeToFruit = 200 + 16 * (random() & 0x0f);
						fruitOn = ERASE;
						fruitCount = 0;
					}
				}
			
				// handle eating dots
				if ([maze eatDotAt:[player xpos] :[player ypos]]) {
					dotEat = YES;
				} else dotEat = NO;
				if ([maze powerDotAt:[player xpos] :[player ypos]]) {
					flag = YES;
					dotEat = YES;
				}
				if (![maze dots]) {
					state = BLINK_LEVEL;
					begin = 64;
				}

				[maze playerPosition:&x :&y]; // get position of fruit
				// see if player ate the fruit
				if (fruitOn == YES) { // fruit's there...
					if ((abs([player ypos] - y) < GHOST_SIZE / 2) &&
						(abs([player xpos] - x) < GHOST_SIZE / 2)) { // ate it!
						fruitOn = ERASE;
						// add value of fruit to the score
						pts = (([controller level] < NUMFRUITVALS)
							? fruitval[[controller level]]
							: fruitval[NUMFRUITVALS]);
						[controller addToScore:pts];
						// tell the player how many points he/she got
						ftx = x + TEXTOFFSET + mazePos.x;
						fty = y + mazePos.y;
						[fruitText moveTo:ftx :fty];
						if (pts) [fruitText setIntValue:pts];
						// now, align coords to the maze so erase
						// does it's job right (otherwise, we end up SOVERing
						// maze parts twice, and it looks _ugly_!
						ftx -= mazePos.x; fty -= mazePos.y;
						// chop off lower four bits (floor to mult. of 16)
						ftx /= 16; ftx = (ftx << 4) + mazePos.x;
						fty /= 16; fty = (fty << 4) + mazePos.y;
						fruitPointCount = 48;
				}	}

				// check for player/ghost collision: (and deal with it)
				if ([maze dots]) {
					for (i=0; i<=3; i++) {
						// tell ghost of power dot
						if (flag) [ghost[i] powerDot:YES];
						[ghost[i] at:&gx :&gy];
						if ((abs(gx - [player xpos]) < GHOST_SIZE / 2) &&
							(abs(gy - [player ypos]) < GHOST_SIZE / 2)) {
						// collision! decide who dies...
							switch ([ghost[i] munch]) {
								case YES : {	// player got ghost
									// (-munch already added any bonus.)
									gtx[i] = gx + TEXTOFFSET + mazePos.x;
									gty[i] = gy + mazePos.y;
									[ghostText[i] moveTo:gtx[i] :gty[i]];
									[ghostText[i]
										setIntValue:[controller ateGhost]];
									// now, align to maze as above
									gtx[i] -= mazePos.x; gtx[i] /= 16;
									gty[i] -= mazePos.y; gty[i] /= 16;
									gtx[i] = (gtx[i] << 4) + mazePos.x;
									gty[i] = (gty[i] << 4) + mazePos.y;
									ghostPointCount[i] = 48;
									break;
								}
								case NO : {		// ghost got player, so die...
									state = DYING_PAC;
									begin = 48; // stall
									[player pacDie];
									fruitOn = NO;
									updateFlag = YES;
									break;
								}
								case HARMLESS : 
								default : {		// eyes do nothing.
									break;
				}	}	}	}	}

				// figure out where ghosts will go next
				for (i=0; i<=3; i++) {
					[ghost[i] move:self];
				}
				if (lcnt > 1) { // make movement take effect w/o render
					for (i=0; i<=3; i++) {
						[ghost[i] moveOneFrame];
					}
					[player moveOneFrame];
				}
				lcnt--;
	}	}	}
	// draw all the changes, if applicable.
	if (updateFlag) [self update];
    if ((state != READY) && (state != DIEREADY)) [self updateSelf:&bounds :1];
    return self;
}

// This renders the whole screen, much like updateSelf:: below, but since
// it always redraws the _entire_ screen, it is unnaceptably inefficient for
// handling individual animation frames.
- drawSelf:(NXRect *)rects :(int)rectCount	// redraws the screen.
{		// right now, it's stupid and always redraws the whole view.
	register int i, f;
	int x, y;
    NXPoint pos;
	NXRect from, bezel, mazeRect;

    if ([self window] == nil)
        return self;	// exit if no window to draw in
    
	[self lockFocus];
    	
	// draw bezel
	NXSetRect(&bezel, bounds.origin.x + 5, bounds.origin.y + 5,
		bounds.size.width - 10, bounds.size.height - 10);
	NXDrawGrayBezel(&bezel, &bounds);
	NXFrameRectWithWidth(&bounds, 5);
	bezel.size.width -= 6;
	bezel.size.height -= 6;
	bezel.origin.x += 3;
	bezel.origin.y += 3;
	[self drawBackground:&bezel]; // put background inside the bezel
	NXSetRect(&mazeRect, 0, 0,
		GHOST_SIZE * BLOCK_WIDTH, GHOST_SIZE * BLOCK_HEIGHT);
	pos.x = 13; pos.y = 12;
	[maze render:&mazeRect at:&mazePos]; // draw the maze
	if ((fruitOn && (fruitOn != ERASE)) && (state != BLINK_LEVEL)) {
		// render the fruit if necessary
		// first, get coordinates of where to draw the fruit
		[maze playerPosition:&x :&y];
		pos.x = x + mazePos.x;
		pos.y = y + mazePos.y;
		// decide which fruit to draw
		f = fruits[(([controller level] > FRUIT_LEVELS) ?
			FRUIT_LEVELS : [controller level])];
		// draw the fruit
		NXSetRect(&from,
			(f % FRUIT_PER_ROW) * FRUIT_SIZE,
			(f / FRUIT_PER_ROW) * FRUIT_SIZE,
			FRUIT_SIZE, FRUIT_SIZE);
		[fruit composite:NX_SOVER fromRect:&from toPoint:&pos];
		fruitOn = YES;
	}
	if (fruitOn == ERASE) fruitOn = NO;
	if ((state != BLINK_LEVEL) || ((state == BLINK_LEVEL) && (begin > 31)))
		[player renderAt:mazePos.x :mazePos.y move:NO];	// draw the pacman
	if ((state != DYING_PAC) && (state != BLINK_LEVEL) &&
			(state != GAMEOVER)) {	// draw the ghosts
		for (i=0; i<=3; i++) {
			[ghost[i] renderAt:mazePos.x :mazePos.y move:NO];
	}	}

	[self unlockFocus];
	if (fruitPointCount) [fruitText display];
	for (i=0; i<4; i++) if (ghostPointCount[i]) [ghostText[i] display];
	[self lockFocus];
	if (state == GAMEOVER) {
		pos.x = (NX_WIDTH(&bounds) - gameOverSize.width) / 2;
		pos.y = (NX_HEIGHT(&bounds) - gameOverSize.height) / 2;
		[gameOver composite:NX_SOVER toPoint:&pos];
	}
	[self unlockFocus];
	NXPing();
	return self;
}

// This is the main drawing here.  It works like drawSelf, but only
// _changes_ stuff; it doesn't re-draw the whole view each time.  This
// has been done to speed things up.  We erase the old, and then redraw
// all the spots we erased after calculating where things have moved to.
- updateSelf:(NXRect *)rects :(int)rectCount	// redraws the screen.
{		// it redraws only what has changed since last redraw.
    register int f, i;
	int x, y;
	const int *pd;	// pointer to array of power dot coords
	NXRect from;
	NXPoint pos;

    if ([self window] == nil) return self;	// exit if no window to draw in
        
	[self lockFocus];
	pd = [maze powerDot];	// get power dot coords
	// erase ghosts and power dots by drawing background image on top.
	for (i=0; i<=3; i++) {
		if ((ghostPointCount[i] == WIPETEXT) && (state != GAME_OVER)) {
			ghostPointCount[i] = 0;
			ZAPRECT(textEraseRect, gtx[i], gty[i]);
			NX_X(&textEraseRect) -= mazePos.x;
			NX_Y(&textEraseRect) -= mazePos.y;
			[maze render:&textEraseRect at:&mazePos];	// render maze there
		}
		if (state != GAME_OVER) {
			[ghost[i] lastAt:&NX_X(&eraseRect) :&NX_Y(&eraseRect)];
			NX_X(&eraseRect) += mazePos.x;
			NX_Y(&eraseRect) += mazePos.y;
			[self drawBackground:&eraseRect];
		} if (erasePwr) {	// erase power dot every time it blinks.
			NX_X(&eraseRect) = pd[i * 2 ]    * GHOST_SIZE + mazePos.x;
			NX_Y(&eraseRect) = pd[i * 2 + 1] * GHOST_SIZE + mazePos.y;
			[self drawBackground:&eraseRect];
	}	}
	
	// erase fruit text
	if ((fruitPointCount == WIPETEXT) && (state != GAME_OVER)) {
		fruitPointCount = 0;
		ZAPRECT(textEraseRect, ftx, fty);
		NX_X(&textEraseRect) -= mazePos.x;
		NX_Y(&textEraseRect) -= mazePos.y;
		[maze render:&textEraseRect at:&mazePos];	// render maze there
	}
	
	// erase fruit if needed
	if ((fruitOn) && (state != GAME_OVER)) {
		// inefficient -- we always re draw it when it's on...
		[maze playerPosition:&x :&y];
		NX_X(&eraseRect) = x + mazePos.x;
		NX_Y(&eraseRect) = y + mazePos.y; 
		[self drawBackground:&eraseRect];
	}

	// erase the player's PacMan
	if (state != GAME_OVER) {
		[player lastAt:&NX_X(&eraseRect) :&NX_Y(&eraseRect)];
		NX_X(&eraseRect) += mazePos.x;
		NX_Y(&eraseRect) += mazePos.y;
		[self drawBackground:&eraseRect];
		[player lastAt:&NX_X(&eraseRect) :&NX_Y(&eraseRect)];
		[maze render:&eraseRect at:&mazePos];	// render maze
	} if (dotEat) {	// makes sure that the eaten dot was erased.
		// if I don't do this, when a dot is eaten, if the player quickly
		// reverses direction, the dot will remain--looking half eaten, even
		// though we internally register it as eaten.
		[maze lastDot:&x :&y];
		NX_X(&eraseRect) = x + mazePos.x;
		NX_Y(&eraseRect) = y + mazePos.y; 
		[self drawBackground:&eraseRect];	// draw background over it
		NX_X(&eraseRect) = x;
		NX_Y(&eraseRect) = y; 
		[maze render:&eraseRect at:&mazePos];	// render maze there
		dotEat = NO;
	}
	
	// render maze underneath the fruit if necessary
	if ((fruitOn) && (state != GAME_OVER)) {
		// inefficient -- we always re draw it when it's on...
		[maze playerPosition:&x :&y];
		NX_X(&eraseRect) = x;
		NX_Y(&eraseRect) = y; 
		[maze render:&eraseRect at:&mazePos];	// render maze there
	}
	
	// draw the maze over where the ghosts were.
	for (i=0; i<=3; i++) {
		[ghost[i] lastAt:&NX_X(&eraseRect) :&NX_Y(&eraseRect)];
		[maze render:&eraseRect at:&mazePos];
		if (erasePwr) { // will put power dot on with blinks.
			NX_X(&eraseRect) = pd[i * 2 ]    * GHOST_SIZE;
			NX_Y(&eraseRect) = pd[i * 2 + 1] * GHOST_SIZE;
			[maze render:&eraseRect at:&mazePos];
	}	}

	// render the fruit if necessary
	if (fruitOn) {
		if ((fruitOn != ERASE) && (state != BLINK_LEVEL)) {
			// inefficient -- we always re draw it when it's on...
			// first, get coordinates of where to draw the fruit
			[maze playerPosition:&x :&y];
			pos.x = x + mazePos.x;
			pos.y = y + mazePos.y;
			// decide which fruit to draw
			f = fruits[(([controller level] > FRUIT_LEVELS) ?
				FRUIT_LEVELS : [controller level])];
			// draw the fruit
			NXSetRect(&from,
				(f % FRUIT_PER_ROW) * FRUIT_SIZE,
				(f / FRUIT_PER_ROW) * FRUIT_SIZE,
				FRUIT_SIZE, FRUIT_SIZE);
			[fruit composite:NX_SOVER fromRect:&from toPoint:&pos];
			fruitOn = YES;
		} else { fruitOn = NO; }
	}
	
	// put the player's Pac on the screen if in a state where it's visible.
	if ((state != BLINK_LEVEL) || ((state == BLINK_LEVEL) && (begin > 31)))
		[player renderAt:mazePos.x :mazePos.y
			move:((!paused) && (state == NORMALSTATE))];
		
	// put ghosts on screen if we're in a state where they are visible.
	if ((state != DYING_PAC) && (state != BLINK_LEVEL) &&
			(state != GAMEOVER)) {
		for (i=0; i<=3; i++) {
			[ghost[i] renderAt:mazePos.x :mazePos.y move:
				((!paused) && ((state == READY) || (state == NORMALSTATE)))];
	}	}
	
	/*if (state == GAMEOVER) {
		pos.x = (NX_WIDTH(&bounds) - gameOverSize.width) / 2;
		pos.y = (NX_HEIGHT(&bounds) - gameOverSize.height) / 2;
		[gameOver composite:NX_SOVER toPoint:&pos];
	}*/
	// housekeeping for the graphics:
	[self unlockFocus];
	if (fruitOn == ERASE) fruitOn = NO;
	if (fruitPointCount) [fruitText display];
	for (i=0; i<4; i++) if (ghostPointCount[i]) [ghostText[i] display];
	[[self window] flushWindow];
	NXPing();
	erasePwr = NO;	// we've listened to this flag, so turn it off now.
	return self;
}

// Handle player movement.  We respond to the arrow keys.  If we don't
// recognize the key, we pass it on.  This method just shunts the key
// off to the player object, which is what really deals with it. 
- keyDown:(NXEvent *)myevent
{
	if (!myevent) return self; // if no event when coalescing, go away
	if (!(myevent->flags&(NX_CONTROLMASK|NX_ALTERNATEMASK|NX_COMMANDMASK)) ) {
		if		  (myevent->data.key.keyCode == 0x16) { // Up Arrow
			[player newDirection:PAC_UP];
			[controller unpause];
			return self;
		} else if (myevent->data.key.keyCode == 0x0f) { // Down Arrow
			[player newDirection:PAC_DOWN];
			[controller unpause];
			return self;
		} else if (myevent->data.key.keyCode == 0x09) { // Left Arrow
			[player newDirection:PAC_LEFT];
			[controller unpause];
			return self;
		} else if (myevent->data.key.keyCode == 0x10) { // Right Arrow
			[player newDirection:PAC_RIGHT];
			[controller unpause];
			return self;
		} else [super keyDown:myevent];
	} else [super keyDown:myevent];
	[self keyDown:[NXApp peekAndGetNextEvent:NX_KEYDOWN]]; // coalesce keydowns
	// this is done recursively...primitive, but easy to implement :-)
    return self;
}

// set up the screen at the start of a new level.  Loads in the maze.
- setUpScreen
{	    
    [super setUpScreen];
	[maze makeMaze:screens[(([controller level] >= NUMSCREENS) ?
		(NUMSCREENS - 1) : [controller level]) - 1]];
	[self startScreen];
	timeToFruit = 200 + 16 * (random() & 0x0f);
	fruitOn = NO;
	fruitCount = 0;
	numFruits = 0;
	
    return self;
}

// This let's us put the ghosts back where they are at the start of the level.
// This is separate from above because the above also resets the dots, and if
// the player dies, we only want to reset the ghosts, not the dots, too.
- startScreen
{
	const int *gh; int i;
	    
	gh = [maze ghosts]; // pointer to array of ghost coordinates
	for (i=0; i<=3; i++) {	// re-initialize each ghost
		[ghost[i] initGhost:i player:player maze:maze at:gh[i*2] :gh[i*2+1]];
	}
	// don't let fruit come out too quickly
	if (timeToFruit - fruitCount < 60)
		timeToFruit = 200 + 16 * (random() & 0x0f);
	
    return self;
}

- restartGame
{
	int x, y;
	
	// go to READY state to start game; but want DIEREADY so we don't advance
	// the level; the controller, by virtue of calling this method, has already
	// advance the level.
	state = DIEREADY;
	begin = 96;
	
	// put up the "Get Ready!" sign.
	[maze playerPosition:&x :&y];
	[readyText moveTo:(x - 2 * GHOST_SIZE + mazePos.x) :(y + mazePos.y)];
	[readyText setStringValue:"Get Ready!"];
	
	// make sure that all artifacts of demo mode are gone
    demoWait = 0;
    if (demoMode) {
		demoMode = NO;
		[[self window] setTitle:"PacMan"];
    }
	
	[self setUpScreen];		// load maze, etc.
    [controller zeroScore];	// clear the score
    [self getPreferences];	// make sure we're up to date
	[player newPlayer];		// get a new pac to play with
	// (above always sets up the pac, but in demoMode, the gameBrain hasn't
	// given up 3 pacs, so we end up taking a negative # of pacs, which means
	// demo mode will end as soon as the pac dies.)
	[self update];			// redraw screen
    return self;
}


// These three methods load the three hardcoded background images.
// To work, the images must be in the same directory as the executable.
- back1:sender
{
    char *tempStr;
    
    tempStr = malloc(256); sprintf(tempStr, "%sGradation.eps", appPath);
    [self setBackgroundFile:tempStr andRemember:YES];
    free(tempStr); [self display];
    return self;
}

- back2:sender
{
    char *tempStr;
    
    tempStr = malloc(256); sprintf(tempStr, "%slush.tiff", appPath);
    [self setBackgroundFile:tempStr andRemember:YES];
    free(tempStr); [self display];
    return self;
}

- back3:sender
{
    char *tempStr;
    
    tempStr = malloc(256); sprintf(tempStr, "%sSunset.tiff", appPath);
    [self setBackgroundFile:tempStr andRemember:YES];
    free(tempStr); [self display];
    return self;
}


@end
