/*
  PMD.js - Pac-Man Dungeons

  (c) Norbert Landsteiner 2006
  mass:werk - media environments
  <http://www.masswerk.at>

*/

var VERSIONSTRING = 'Pac-Man Dungeons';
var REVISION = '1b6';
var SERIALNR = '000001';
var term;
var kbd;
var game;
var displayScreen=null;
var displayStatus=null;
var displayScreenOffset;
var pacdir;
var nearthings;
var ghostbot, ghostchat;

var colorThemes=new Array('termBlue', 'termDark', 'termGreen', 'termLight', 'termWhite');
var currentTheme=0;

var colorSets = {
	termDark: {
		bgColor: '#181818',
		className: 'term'
	},
	termLight: {
		bgColor: '#faefc0',
		className: 'termLight'
	},
	termGreen: {
		bgColor: '#9cb099',
		className: 'termGreen'
	},
	termWhite: {
		bgColor: '#eeeeee',
		className: 'termWhite'
	},
	termBlue: {
		bgColor: '#232e45',
		className: 'termBlue'
	}
};

function toggleTermTheme() {
	if ((!term) || (term.closed)) return;
	currentTheme=(currentTheme+1)%colorThemes.length;
	var theme=colorSets[colorThemes[currentTheme]];
	term.conf.bgColor=theme.bgColor;
	term.conf.fontClass=theme.className;
	term.rebuild();
}

var directions = {
	'w': 'west',
	'e': 'east',
	'n': 'north',
	's': 'south'
};
var shortdirections = {
	'w': 'w',
	'west': 'w',
	'e': 'e',
	'east': 'e',
	'n': 'n',
	'north': 'n',
	's': 's',
	'south': 's',
	'up': 'n',
	'upwards': 'n',
	'left': 'w',
	'right': 'e',
	'down': 's',
	'downwards': 's',
	'top': 'n',
	'bottom': 's',
	'u': 'n',
	'l': 'w',
	'r': 'e',
	'd': 's'
};

var objects = {
	'ghost': true,
	'ghosts': true,
	'teleport': true,
	'tele-port': true,
	'gate': true,
	'cave': true,
	'wall': true,
	'walls': true,
	'food': true,
	'pellet': true,
	'pill': true,
	'passage': true,
	'passages': true,
	'cavern': true,
	'caverns': true,
	'dungeon': true,
	'dungeons': true,
	'maze': true,
	'crossing': true,
	'junction': true,
	'corner': true,
	'game': true,
	'map': true,
	'sign': true
};

var compass=new Array();
compass[1]='east';
compass[2]='west';
compass[4]='north';
compass[8]='south';
var compassUC=new Array();
compassUC[1]='EAST';
compassUC[2]='WEST';
compassUC[4]='NORTH';
compassUC[8]='SOUTH';
var compassdirs= [ 4, 8, 1, 2 ];

var mapTileNames= [
	'g1', 'g2', 'g3', 'g4', 'gb', 'gw',
	'p1', 'p2', 'p4', 'p8',
	't0', 't1', 't2', 't3', 't4', 't5'
];

var mapTiles=new Array();
var mapCache=new Array();

var wdgtImgs=new Array('close_lo', 'close_hi', 'close_dn');
var wdgtImgRef=new Array();
var wdgtImgPath='images/';

function mapPreload() {
	for (var i=0; i<mapTileNames.length; i++) {
		var n=mapTileNames[i];
		mapTiles[n]=new Image();
		mapTiles[n].src='images/'+n+'.gif';
	}
}

function wdgtPreload() {
	for (var i=0; i<wdgtImgs.length; i++) {
		var n=wdgtImgs[i];
		wdgtImgRef[n]=new Image();
		wdgtImgRef[n].src=wdgtImgPath+n+'.gif';
	}
}

if (document.images) {
	mapPreload();
	wdgtPreload();
}


var splashScreen= [
	' ',
	'              Welcome to %+uP%-uac-%+uM%-uan %+uD%-uungeons - the Text-mode Pac-Man!',
	' ',
	'        #######        PPPPPPP    MM     MM   DDDDDDD           #######     ',
	'      ##       ##       PP   PP   MMM   MMM    DD   DD        ##       ##   ',
	'    ##          ###     PP   PP   MMMM MMMM    DD    DD     ##   ## ##   ## ',
	'   #         ###        PP   PP   MM MMM MM    DD    DD    #     ## ##     #',
	'   #       ##           PPPPPP    MM  M  MM    DD    DD    #               #',
	'   #         ###        PP        MM     MM    DD    DD    #     #####     #',
	'    ##          ###     PP        MM     MM    DD    DD    #               #',
	'      ##       ##       PP        MM     MM    DD   DD     #   ###   ###   #',
	'        #######        PPPP      MMMM   MMMM  DDDDDDD       ###   ###   ###',
	' ',
	'              (c) mass:werk, N. Landsteiner 2006; www.masswerk.at',
	' ',
	' PMD is a homage to the two most popular forms of computer games in the late',
	' 1970ies and early 80ies: Pac-Man and text adventures (interactive fiction).',
	' ',
	' ',
	' Hit <ENTER> to enter the Pac-Man dungeons. Type "help" for help.%n '
];

var infoScreen= [
	' ',
	'                   %+uP%-uac-%+uM%-uan %+uD%-uungeons - the Text-mode Pac-Man!',
	' ',
	'        #######        PPPPPPP    MM     MM   DDDDDDD           #######     ',
	'      ##       ##       PP   PP   MMM   MMM    DD   DD        ##       ##   ',
	'    ##          ###     PP   PP   MMMM MMMM    DD    DD     ##   ## ##   ## ',
	'   #         ###        PP   PP   MM MMM MM    DD    DD    #     ## ##     #',
	'   #       ##           PPPPPP    MM  M  MM    DD    DD    #               #',
	'   #         ###        PP        MM     MM    DD    DD    #     #####     #',
	'    ##          ###     PP        MM     MM    DD    DD    #               #',
	'      ##       ##       PP        MM     MM    DD   DD     #   ###   ###   #',
	'        #######        PPPP      MMMM   MMMM  DDDDDDD       ###   ###   ###',
	' ',
	'              (c) mass:werk, N. Landsteiner 2006; www.masswerk.at',
	' ',
	' PMD is a homage to the two most popular forms of computer games in the late',
	' 1970ies and early 80ies: Pac-Man and text adventures (interactive fiction).',
	' [Some sense of humor might help while dealing with this piece of software.]',
	' ',
	' PMD is based on JavaScript-PacMan (1997), mass:werk termlib.js (2003-2005),',
	' and elizabot.js (2005); all (c) mass:werk, N. Landsteiner, www.masswerk.at.',
	' (The original ELIZA program by Joseph Weizenbaum, 1966, at Project MAC/MIT.)'
];

var helpScreen=[
	'%CS%+r Pac-Man Dungeons Help %-r%n',
	'  %+iEnter one of the following to move through the passages:%-i',
	' ',
	'    [go|move|run|step] %+ue%-uast|%+un%-uorth|%+us%-uouth|%+uw%-uest',
	'    %+i(Underlined characters indicate abbreviations.)%-i',
	' ',
	' ',
	'  %+iOther useful commands:%-i',
	' ',
	'    say|scream|sing|shout|whisper|yell {text} - say something',
	'                                         %+up%-uass - pass time',
	'                              [hide|show] %+um%-uap - hide/show map window',
	'                                      %+ure%-ufresh - show the last step again',
	'                                         %+uq%-uuit - leave the current game',
	'                                     %+un%-uew %+ug%-uame - start a new game',
	'                                    %+uh%-uelp or ? - show this help page',
	'                                %+ua%-ubout or info - show a short info on this game',
	' ',
	' ',
	'  %+iUse cursor%-i %+r<UP>%-r %+ito show the last command(s).%-i',
	'  %+iYou may chat with a ghost that is not more than 2 steps away.%-i'
];

function buildMap(divId) {
	var s='<table border="0" cellspacing="0" cellpadding="1" class="outerWin">\n';
	s+='<tr><td class="mapWinTitle" onmousedown="dragElement(\'pacMap\'); return false">Map</td></tr>\n';
	s+='<tr><td class="mapWinBody"><table border="0" cellspacing="0" cellpadding="0">\n';
	for (var r=1; r<=14; r++) {
		s+='<tr><td nowrap>';
		for (var c=1; c<=20; c++) {
			s+='<img src="images/t3.gif" width="8" height="8" alt="" id="r'+r+'c'+c+'">';
		}
		s+='</td></tr>\n';
	}
	s+='</table></td></tr>\n';
	s+='</table>';
	TermGlobals.writeElement(divId, s);
	for (var r=1; r<=14; r++) {
		var mcr=mapCache[r]=new Array();
		for (var c=1; c<=20; c++) {
			mcr[c]='t3';
		}
	}
}

function clearMap() {
	for (var r=1; r<=14; r++) {
		for (var c=1; c<=20; c++) {
			setMapTile( r, c, 't3');
		}
	}
}

function updateMap() {
	for (var r=1; r<=14; r++) {
		for (var c=1; c<=20; c++) {
			setMapTile( r, c, (game.mapMask[r][c])? 't'+game.map[r][c]: 't3' );
		}
	}
	for (var i=1; i<=4; i++) {
		var gi=game.g[i];
		setMapTile( gi.r, gi.c, ((game.pill) && (gi.s>0))? (game.pillCnt<=1)? 'gw':'gb': 'g'+i );
	}
	setMapTile( game.pac.r, game.pac.c, 'p'+game.pacDir );
}

function setMapTile(r, c, i) {
	if (mapCache[r][c]!=i) {
		mapCache[r][c]=i;
		document.images['r'+r+'c'+c].src=mapTiles[i].src;
	}
}

function showWins() {
	var x=212;
	var y=60;
	TermGlobals.setElementXY('pacTerm', x, y);
	TermGlobals.setDisplay('pacTerm', 'block');
	TermGlobals.setVisible('pacTerm', true);
	setTimeout('showMapWin()',1);
}

function showMapWin() {
	var x=212;
	var y=60;
	TermGlobals.setElementXY('pacMap', x+getElementWidth('pacTerm')+16, y+24);
	TermGlobals.setDisplay('pacMap', 'block');
	TermGlobals.setVisible('pacMap', true);
}

function getElementWidth(id) {
	var obj;
	if (document.getElementById) {
		obj=document.getElementById(id);
	}
	else if (document.all) {
		obj=document.all[id];
	}
	else if (document.layers) {
		return document.layers[id].clip.right;
	}
	return parseInt(obj.offsetWidth,10);
}

function hideWins() {
	TermGlobals.setVisible('pacTerm', false);
	TermGlobals.setVisible('pacMap', false);
	TermGlobals.setDisplay('pacTerm', 'none');
	TermGlobals.setDisplay('pacMap', 'none');
	kbd.hide();
	pageTextActivate();
}

function setClassName(id, cln) {
	var obj;
	if (document.getElementById) {
		obj=document.getElementById(id);
	}
	else if (document.all) {
		obj=document.all[id];
	}
	if (obj) {
		obj.className=cln;
	}
}

function pageTextDimm() {
	setClassName('startLink', 'termopen_dimmed');
	setClassName('mainBlock1', 'mainText_dimmed');
	setClassName('mainBlock2', 'mainText_dimmed');
	setClassName('copyrightBlock', 'copyText_dimmed');
	if (document.images.pmd) document.images.pmd.src='pmd_dimmed.gif';
	TermGlobals.keylock=false;
}

function pageTextActivate() {
	setClassName('startLink', 'termopen');
	setClassName('mainBlock1', 'mainText');
	setClassName('mainBlock2', 'mainText');
	setClassName('copyrightBlock', 'mainText');
	if (document.images.pmd) document.images.pmd.src='pmd.gif';
	TermGlobals.keylock=true;
}

function showInfoScreen(termRef, screen) {
	termRef.r--;
	termRef.c=2;
	termRef._clearLine();
	if ((termRef.history.length>0) && (termRef.histPtr)) {
		termRef.history.length--;
		termRef.histPtr--;
	}
	termCapture(termRef);
	termRef.maxLines = termRef.conf.rows;
	termRef.clear();
	termRef.write(screen);
	termRef.statusLine(' Hit any key to continue.', 1);
	termRef.maxLines--;
	termRef.env.restoreLast=true;
	termRef.charMode=true;
	termRef.lock = false;
}

function restoreFromInfoScreen(termRef) {
	termRestore(termRef);
	termRef.env.restoreLast=false;
	termRef.charMode=false;
	termRef.lock=false;
	termRef.cursorOn();
}

function showQuitPrompt(termRef, restart) {
	if (restart) {
		termRef.write('Do you wish to rerstart the game? [y|n] ');
		termRef.env.restart=true;
	}
	else {
		termRef.write('Do you wish to leave the game? [y|n] ');
		termRef.env.quit=true;
	}
	termRef.charMode=true;
	termRef.cursorOn();
	termRef.lock=false;
}

function refreshLastScreen(termRef, roomOnly) {
	if (roomOnly) {
		//termRef.clear();
		var text=new Array();
		for (var i=displayScreenOffset; i<displayScreen.length; i++) text.push(displayScreen[i]);
		termRef.write(text);
	}
	else {
		termRef.maxLines = termRef.conf.rows;
		termRef.clear();
		if (displayStatus) {
			if (displayStatus.length>1) {
				termRef.statusLine(displayStatus[0], 0, 1);
				termRef.statusLine(displayStatus[1], 1, 2);
			}
			else {
				termRef.statusLine(displayStatus[0], 1, 1);
			}
			termRef.maxLines = termRef.conf.rows-displayStatus.length;
		}
		termRef.write(displayScreen);
	}
}

function toggleMap(v) {
	TermGlobals.setVisible('pacMap', v);
	TermGlobals.setDisplay('pacMap', (v)? 'block':'none');
}

function termOpen() {
	if (!term) {
		var theme=colorSets[colorThemes[currentTheme]];
		term=new Terminal(
			{
				x: 0,
				y: 0,
				termDiv: 'termDiv',
				ps: '>',
				initHandler: termInitHandler,
				handler: commandHandler,
				exitHandler: hideWins,
				historyUnique: true,
				closeOnESC: false,
				bgColor: theme.bgColor,
				fontClass: theme.className
			}
		);
		if (term) term.open();
		showWins();
		buildMap('pacMap');
		kbd=new TermKeyboard( { optsColorHandler: toggleTermTheme } );
		kbd.showOpts('termOptsDiv');
		game=new PacEngine();
		ghostbot=new ElizaBot();
		// localize termlib's more prompt
		TermGlobals.lcMorePrompt2 = ' (Type: space to continue, \'c\' to cancel)';
		TermGlobals.lcMoreKeyAbort = 99;
	}
	else if (term.closed) {
		term.open();
		showWins();
		clearMap();
	}
	else {
		term.focus();
	}
	pageTextDimm();
}

function termInitHandler() {
	// output a start up screen
	this.write(splashScreen);
	displayScreen=splashScreen;
	displayScreenOffset=0;
	// set a status line
	displayStatus=[' Hit <ENTER> to enter the Pac-Man dungeons. Type "help" for help.'];
	this.statusLine(displayStatus[0], 1);
	this.maxLines --;
	// and leave with prompt
	this.env.isStart=true;
	this.env.mapVisible=true;
	this.prompt();
}

function commandHandler() {
	// check for raw mode first (should not be parsed)
	if (this.charMode) {
		if ((this.env.quit) || (this.env.restart)) {
			var c;
			if (this.inputChar) c=String.fromCharCode(this.inputChar).toLowerCase();
			if (c=='y') {
				this.write('y');
				this.charMode=false;
				this.lock=false;
				if (this.env.quit) {
					this.env.quit=false;
					this.close();
				}
				else {
					this.env.restart=false;
					this.env.isStart=true;
					TermGlobals.keyHandler({which:13});
					return;
				}
			}
			else if (c=='n') {
				this.env.quit=false;
				this.env.restart=false;
				this.write('n');
				this.write(' ');
			}
			else {
				return;
			}
		}
		else if (this.env.restoreLast) {
			restoreFromInfoScreen(this);
			return;
		}
		// leave in normal mode
		this.charMode = false;
		this.prompt();
		return;
	}
	this.newLine();
	// normal command parsing
	parseLine(this);
	// start sequence
	if ((this.env.isStart) || (game.gameStatus==1)) {
		var scm= (this.argv.length == 0)? '':this.argv[0].toLowerCase();
		scm=scm.replace(/[,;:-]+$/, '');
		switch (scm) {
			case 'help':
			case 'h':
			case '?':
				showInfoScreen(this, helpScreen);
				return;
			case 'quit':
			case 'q':
				showQuitPrompt(this, false);
				return;
			default:
				this.clear();
				if (this.env.isStart) {
					displayScreen=[
						'%+uLevel 1 - Inside the Maze%-u',
						'You find yourself in the winding passages of a labyrinth.',
						'All you can think of is an urging hunger for those delicious food pellets.',
						'A little tune, which your grandma - a yellow, ball-shaped creature like you -',
						'used to sing, is nagging at the back of your mind:',
						' ',
						'  "Red, green, orange, pink -',
						'   Never touch such a thing,',
						'   Never even dare to think.',
						'   But if it turns blue,',
						'   Sweet tastes a ghost so -',
						'   Tries not to meet you."'
					];
					nearthings = {
						ghost:false,
						food: false,
						pill: false,
						teleport: false,
						gate: false,
						cave: false
					};
					ghostchat=false;
					this.env={};
					this.env.mapVisible=true;
					ghostbot.reset();
					game.newGame();
					pacdir='';
				}
				else {
					game.gameStep();
					game.gameStep();
					displayScreen=[
						'%+uLevel '+game.nLevel+' - '+((game.nLevel>2)? 'Even ':'')+'More Twisty Little Passages%-u',
						'You find yourself in another labyrinth bursting with fresh tasty food.'
					];
				}
				makeReport(this);
				updateMap();
				return;
		}
	}
	else if (this.env.save) {
		fileAccess(this, true, this.argv[0]);
		this.env.save=false;
		this.newLine();
		this.newLine();
		this.prompt();
		return;
	}
	else if (this.env.restore) {
		fileAccess(this, false, this.argv[0]);
		this.env.restore=false;
		this.newLine();
		this.newLine();
		this.prompt();
		return;
	}
	// normal state
	if (this.argv.length == 0) {
		// no commmand line input
	}
	else {
		var cmd = cleanArg(this.argv[this.argc++]);
		/*
		  process commands now
		  1st argument: this.argv[this.argc]
		*/
		switch (cmd) {
			case 'pass':
			case 'p':
			case 'sleep':
			case 'wait':
				if (cmd=='sleep') {
					displayScreen=['Time passes as you sleep ...'];
				}
				else {
					displayScreen=['Time passes ...'];
				}
				pacMove(this, '');
				return;
			case 'help':
			case 'h':
			case '?':
				showInfoScreen(this, helpScreen);
				return;
			case 'info':
			case 'about':
			case 'a':
				showInfoScreen(this, infoScreen);
				return;
			case 'quit':
			case 'q':
				showQuitPrompt(this, false);
				return;
			case 'talk':
			case 'tell':
				cmd='say';
			case 'sing':
			case 'say':
			case 'scream':
			case 'shout':
			case 'yell':
			case 'whisper':
			case 'utter':
				var words=new Array();
				var quoted=false;
				for (var i=this.argc; i<this.argv.length; i++) {
					if (this.argQL[i]) {
						this.argc=i;
						quoted=true;
						break;
					}
				}
				if (!quoted) {
					for (var i=0; i<this.argv.length; i++) {
						if (this.argv[i].indexOf(':')>-1) {
							this.argc=i+1;
							break;
						}
					}
				}
				while (this.argc<this.argv.length) words.push(this.argv[this.argc++]);
				var what=words.join(' ');
				if (!ghostchat) {
					var str='You are '+cmd+'ing ';
					if (what) {
						if (cmd=='sing') str+='with a mellow voice ';
						what=what.charAt(0).toUpperCase()+what.substring(1);
						str+='"'+what+'"';
						if (!what.match(/[!?\.]$/)) str+='.';
					}
					else {
						str+=(cmd=='sing')? 'some tune with a sweet and mellow voice.':'something.';
					}
					str+='%n';
					if (cmd=='scream' || cmd=='shout' || cmd=='yell') {
						str+='Your words are echoing from the walls';
						var echo=what.replace(/\s*[!?\.]+$/, '');
						echo=echo.replace(/^.*\s+/, '');
						if (echo) {
							echo=echo.toLowerCase();
							str+=' "'+echo+', '+echo+' ..."';
						}
						else {
							str+='.';
						}
					}
					else if (cmd=='whisper') {
						str+='Your voice is swallowed by the thick walls of the passages.';
					}
					else if (cmd=='sing') {
						str+='There is a faint glow as your words are floating through the air.';
					}
					else if (game.pill) {
						str+='Your voice sounds firm and melodious.';
					}
					else {
						str+='Your voice sounds a bit raspy.';
					}
					this.write(str);
				}
				if (nearthings.ghost) {
					if (what) {
						if (!ghostchat) {
							this.newLine();
							this.newLine();
						}
						else {
							var str='You are '+cmd+'ing: "'+what.charAt(0).toUpperCase()+what.substring(1)+'"';
							if (!what.match(/[!?\.]$/)) str+='.';
							this.write(str+'%n');
						}
						this.write('The ghost says: "'+ghostTalk(what)+'"');
					}
					else {
						if (ghostchat) {
							this.write('You are '+cmd+'ing '+((cmd=='sing')? 'some tune':'something')+'.%n');
						}
						else {
							this.newLine();
						}
						var rn=Math.random();
						if (rn<.25) {
							this.write('The ghost is looking at you sympathetically.');
						}
						else if (rn<.5) {
							this.write('The ghost observes you with a consoling look.');
						}
						else if (rn<.75) {
							this.write('The ghost gives you an encouraging look.');
						}
						else {
							this.write('The ghost appears to assume a wait-and-see attitude.');
						}
					}
					ghostchat=true;
				}
				else if ((nearthings.ghost) && (!ghostchat)) {
					this.newLine();
					this.newLine();
					this.write('The ghost says: "'+ghostTalk('hello')+'"');
					ghostchat=true;
				}
				break;
			case 'l':
			case 'look':
				if (this.argc==this.argv.length) {
					refreshLastScreen(this, true);
					this.prompt();
					return;
				}
			case 'examine':
			case 'inspect':
				var found=false;
				var fobj='';
				while (this.argc<this.argv.length) {
					var obj=cleanArg(this.argv[this.argc++]);
					if (obj=='ghost' || obj=='ghosts') {
						if (nearthings.ghost) {
							var sp='                            ';
							this.write(sp+'     #######%n');
							this.write(sp+'   ##       ##%n');
							this.write(sp+' ##   ## ##   ##%n');
							this.write(sp+'#     ## ##     #%n');
							this.write(sp+'#               #%n');
							if (game.pill) {
								this.write(sp+'#     _   _     #    (( *whizzz* ))%n');
								this.write(sp+'#    / \\_/ \\    #%n');
							}
							else {
								this.write(sp+'#     _____     #     ((( WHIZZZzzz )))%n');
								this.write(sp+'#               #%n');
							}
							this.write(sp+'#   ###   ###   #%n');
							this.write(sp+' ###   ###   ###%n%n');
							if (game.pill) {
								this.write('The ghost is of blue color and looks as if it would be frightened of you. While%nin panic, it is moving only slowly.');
							}
							else {
								this.write('The ghost is looking at you with hungry eyes and makes a whizzing sound.%n');
								this.write('It is a nasty little fellow. You\'d better not let him come close.%n');
								this.write('(But ghosts are known to like a little chat before dinner. Some even say they%nare good doctors.)');
							}
						}
						else {
							this.write('I can\'t see a ghost nearby.');
						}
						found=true;
						break;
					}
					else if (obj=='pill' || obj=='food' || obj=='pellet') {
						if (nearthings.pill) {
							this.write('This food pellet is bigger and brighter than the others. It looks like a very%nbig pill. There seems to be some magic about it.');
						}
						else if (nearthings.food) {
							this.write('The food portion is a delicious little white pellet. There is a tasty smell%nabout it.');
						}
						else {
							this.write('I can\'t see any '+obj+' nearby.');
						}
						found=true;
						break;
					}
					else if (obj=='teleport' || obj=='tele-port') {
						if (nearthings.teleport) {
							this.write('The teleport is just an endless space of nothingness. In some distance you can%nsee an aurora '+((game.pac.r==14)? 'australis':'borealis')+' of overwhelming beauty.');
						}
						else {
							this.write('I can\'t see a teleport nearby.');
						}
						found=true;
						break;
					}
					else if (obj=='gate') {
						if (nearthings.gate) {
							this.write('The gate is made of rusty iron and richly ornamented. It looks terribly old.%nThrough the welded iron bars you can see a cave.');
						}
						else {
							this.write('I can\'t see such a thing.');
						}
						found=true;
						break;
					}
					else if (obj=='cave') {
						if (nearthings.cave) {
							this.write('The walls of the cave look as if they were chiseled out of the living rock.%nIt is a smelly and uninviting place.%n');
							this.write('The wall opposed to the gate shows an old and worn inscription. It reads:%n%n');
							this.write('  for each (var ye in who.enter) {%n    ye.abandon(/hope/gi);%n  }');
							
						}
						else {
							this.write('I can\'t see such a thing.');
						}
						found=true;
						break;
					}
					else if (obj=='sign') {
						if (nearthings.sign) {
							this.write('The sign reads "THIS WAY". There must have been an arrow pointing to any%ndirection, but some vendals stole it.');
							
						}
						else {
							this.write('I can\'t see such a thing.');
						}
						found=true;
						break;
					}
					else if (objects[obj]) {
						fobj=obj;
						break;
					}
				}
				if (!found) {
					if (fobj) {
						if (fobj=='passage' || fobj=='passages') {
							this.write('The passage is twisted and gloomy.');
						}
						else if (fobj=='wall' || fobj=='walls') {
							this.write('The walls are just ordinary walls.');
						}
						else if (fobj=='game') {
							this.write('There is nothing really interesting in this game. Sorry.');
						}
						else if (fobj=='maze' || fobj=='map') {
							if (fobj=='maze') this.write('Did you have a look at the map lately?%n');
							if (this.env.mapVisible) {
								this.write('Try the little window at the right side - or where it else might be.');
							}
							else {
								this.write('There used to be a little window ... But rude as you are, you closed it.');
							}
							this.write('%n%n(Bit of a metalepsis here. - What are we talking about here? Is there really%nanyone talking? I could go mad but north by northwest ...)');
						}
						else {
							this.write('There is nothing of interest.');
						}
					}
					else {
						this.write('I can\'t see such a thing.');
					}
				}
				break;
			case 'consider':
			case 'climb':
			case 'open':
			case 'take':
			case 'eat':
			case 'munch':
			case 'touch':
			case 'break':
			case 'catch':
			case 'bite':
			case 'try':
			case 'twist':
			case 'bind':
			case 'exit':
			case 'leave':
				var found=false;
				while (this.argc<this.argv.length) {
					var obj=cleanArg(this.argv[this.argc++]);
					if (objects[obj]) {
						if (cmd=='exit' || cmd=='close') {
							if (obj=='game' || obj=='passage' || obj=='passages') {
								this.write('Nice try. %+iUse "quit" to leave the game.%-i');
							}
							else {
								this.write('You cannot '+cmd+' the '+obj+'.%n%+iUse "quit" to leave the game.%-i');
							}
						}
						else if (obj=='game' || obj=='map') {
							this.write('What a truly interesting concept!%nHave you ever thought of inscribing for philosophy?');
						}
						else if (
							((obj=='ghost' || obj=='ghosts') && (!nearthings.ghost)) ||
							((obj=='pill' || obj=='food' || obj=='pellet') && (!nearthings.food) && (!nearthings.pill)) ||
							((obj=='teleport' || obj=='tele-port') && (!nearthings.teleport)) ||
							((obj=='gate') && (!nearthings.gate)) ||
							((obj=='cave') && (!nearthings.cave))
							) {
							if (obj=='ghost' || obj=='pill' || obj=='food' || obj=='pellet') {
								this.write('There is no '+obj+' in your reach.');
							}
							else if (obj=='ghosts') {
								this.write('There are no '+obj+' in your reach.');
							}
							else if (obj=='teleport' || obj=='tele-port') {
								this.write('There is no '+obj+' in nearby.');
							}
							else {
								this.write('You can\'t see any such thing.');
							}
						}
						else if (cmd=='eat' || cmd=='munch') {
							if (obj=='food' || obj=='pill' || obj=='pellet') {
								this.write('You are already '+cmd+'ing all the food as you move around. %+ipaku-paku!%-i');
							}
							else if ((obj=='ghost' || obj=='ghosts') && (game.pill)) {
								this.write('Try and catch it first!');
							}
							else {
								this.write('I don\'t think that the '+obj+' would agree with you.');
							}
						}
						else if ((cmd=='take') && (obj=='sign' || obj=='food' || obj=='pill' || obj=='pellet')) {
							if (obj=='sign') {
								this.write('The sign is fixed to the wall. You can\'t take it.');
							}
							else {
								this.write('You can\'t take the '+obj+'.');
							}
						}
						else {
							var rnd=Math.random();
							if (rnd<.3) {
								this.write('Not likely.');
							}
							else if (rnd<.6) {
								this.write('An interesting idea ...');
							}
							else {
								this.write('What a concept! You cannot '+cmd+' the '+obj+'.');
							}
						}
						found=true;
						break;
					}
				}
				if (!found) {
					if (cmd=='eat' || cmd=='munch') {
						if (this.argv.length==1) {
							this.write('You are already '+cmd+'ing all the food portions as you move around. %+ipaku-paku!%-i');
						}
						else {
							this.write('I don\'t know exactly what you want to eat, but I doubt that it would agree%nwith you.');
						}
					}
					else if (cmd=='exit' || cmd=='close') {
						this.write('There is nothing to '+cmd+'.%n%+iUse "quit" to leave the game.%-i');
					}
					else {
						this.write('There is nothing to '+cmd+'.');
					}
				}
				break;
			case 'read':
				var found=false;
				while (this.argc<this.argv.length) {
					var obj=cleanArg(this.argv[this.argc++]);
					if (obj=='sign') {
						if (nearthings.sign) {
							this.write('The sign reads "THIS WAY".');
							
						}
						else {
							this.write('You can\'t see such a thing.');
						}
						found=true;
						break;
					}
					else if (
						((obj=='ghost' || obj=='ghosts') && (!nearthings.ghost)) ||
						((obj=='pill' || obj=='food' || obj=='pellet') && (!nearthings.food) && (!nearthings.pill)) ||
						((obj=='teleport' || obj=='tele-port') && (!nearthings.teleport)) ||
						((obj=='gate') && (!nearthings.gate)) ||
						((obj=='cave') && (!nearthings.cave))
						) {
						if (obj=='ghost' || obj=='pill' || obj=='food' || obj=='pellet') {
							this.write('There is no '+obj+' to read. - What a concept!');
						}
						else if (obj=='ghosts') {
							this.write('There are no '+obj+' to read. - What a concept!');
						}
						else if (obj=='teleport' || obj=='tele-port') {
							this.write('There is no '+obj+' to read. - What a concept!');
						}
						else {
							this.write('You can\'t see any such thing.');
						}
						found=true;
						break;
					}
					else if (obj=='map' || obj=='screen' || obj=='help' || obj=='info') {
						this.write('We are a bit in metaleptic concepts, aren\'t we?');
						found=true;
						break;
					}
					else if (obj=='game') {
						this.write('Did you mean "read the screen"???');
						found=true;
						break;
					}
					else if (objects[obj]) {
						this.write('What a concept! You can\'t read the '+obj+'.');
						found=true;
						break;
					}
				}
				if (!found) {
					if (this.argv.length==1) {
						this.write('Read what?');
					}
					else {
						this.write('There is nothing interesting to read.');
					}
				}
				break;
			case 'think':
				this.write('You are thinking of all the marvels of interactive fiction and potential%nnarratives. You are struck by the thought that %+iPac-Man%-i hit the market just the%nsame year that Infocom published %+iZork I%-i. That was only five years after Will%nCrowther wrote %+iAdventure%-i! And the original %+iZork%-i was developed at MIT just the%nsame year that saw the start of the development of %+iPac-Man%-i. What a coincidence!%n- Nothing happens.');
				break;
			case 'fight':
			case 'beat':
			case 'kill':
				this.write('Being of peaceful nature you are unable to do any harm to anyone.');
				break;
			case 'jump':
				this.write('Sorry, you are to heavy to '+cmd+'.');
				break;
			case 'inventory':
				this.write('Sorry, there is no inventory.%nAs all of the family of Pac-Man you have no bag to carry things around.');
				break;
			case 'swim':
			case 'dance':
			case 'fly':
				this.write('It my be a pity, but you never learned to '+cmd+'.');
				break;
			case 'pray':
				this.write('You are praying to the gods of Infocom, but nothing happens.');
				break;
			case 'show':
			case 'hide':
				var what=getArgAndSkip(this, ['a', 'the', 'an']);
				if (what) {
					what=cleanArg(what);
					if (what=='map' || what=='m') {
						this.env.mapVisible=(cmd=='show')? true:false;
						toggleMap(this.env.mapVisible);
						this.write((this.env.mapVisible)? 'Showing map ...':'Map hidden.');
					}
					else {
						if (this.argc==this.argv.length) {
							if (what=='me') {
								this.write('%+iWhat a concept! I can\'t '+cmd+' you.%-i');
							}
							else if (what=='game') {
								if (cmd=='hide') {
									this.write('%+iWhat a concept! Maybe you want to use "quit" to leave the game.%-i');
								}
								else {
									this.write('%+iYou are already looking at the game. (I\'s on the screen).%-i');
								}
							}
							else if (what=='ghost' || what=='ghosts') {
								if (cmd=='hide') {
									if (nearthings.ghost) {
										this.write('%+iWhat a concept! It\'s up to you to deal with the ghost.%-i');
									}
									else {
										this.write('%+iThere is no ghost to hide.%-i');
									}
								}
								else {
									if (nearthings.ghost) {
										this.write('%+iType "look ghost" for a closer look.%-i');
									}
									else {
										this.write('%+iThere are no ghosts to show.%-i');
									}
								}
							}
							else {
								this.write('%+iI do not know how to '+cmd+' a "'+what+'".%-i');
							}
						}
						else {
							this.write('%+iI do not understand the word "'+cmd+'" in this context.%-i');
						}
					}
				}
				else {
					this.write('%+iI do not know what to "'+cmd+'".%-i');
				}
				break;
			case 'map':
			case 'm':
				this.env.mapVisible=(!this.env.mapVisible);
				toggleMap(this.env.mapVisible);
				this.write((this.env.mapVisible)? '%+iShowing map ...%-i':'%+iMap hidden.%-i');
				break;
			case 'refresh':
			case 're':
			case 'update':
				refreshLastScreen(this, false);
				this.prompt();
				return;
			case 'diagnose':
				this.write('You are alive.%n');
				var ml=(game.nLife==1)? 'no': game.nLife-1;
				this.write('You have '+((game.nLife==1)? 'no': game.nLife-1)+' more '+((game.nLife<=2)? 'life': 'lives')+' to lose.');
				if (game.pill) {
					this.write('%nYou have some extra strength that makes you invincible for '+game.pillCnt+' moves.');
				}
				break;
			case 'save':
				if (this.argv[this.argc]) {
					fileAccess(this, true, this.argv[this.argc]);
				}
				else {
					this.write('Please supply a filename for save:');
					this.env.save=true;
					this.prompt();
					return;
				}
				break;
			case 'load':
			case 'restore':
				if (this.argv[this.argc]) {
					fileAccess(this, false, this.argv[this.argc]);
				}
				else {
					this.write('Please supply a filename for restore:');
					this.env.restore=true;
					this.prompt();
					return;
				}
				break;
			case 'score':
				this.write('Your score is '+game.nScore+' in don\'t-know-how-many moves.%n');
				this.write('This gives you certainly some rank, but I do not bother what it is.');
				break;
			case 'version':
				this.write('Welcome to '+VERSIONSTRING+'.%n');
				this.write('Release '+REVISION+' / Serial number '+SERIALNR+'%n');
				this.write('Copyright (c) mass:werk - media environments 2006.%n');
				this.write('Standard JavaScript interpreter / ECMA-262, 3rd edition.');
				break;
			case 'count':
				if (this.argv[this.argc]) {
					var what=getArgAndSkip(this, ['all', 'the', 'little', 'white', 'delicious']);
					if ((what=='food') || (what=='pellets')) {
						this.write('There are '+game.food+' food pellets scattered throughout theses passages.');
						break;
					}
				}
				this.write('%+iCount what?%-i');
				break;
			case 'brief':
				this.write('%+iBrief answer: There is only verbose mode. Sorry.%-i');
				break;
			case 'superbrief':
				this.write('%+iSuperbrief answer: Verbose mode only. Sorry.%-i');
				break;
			case 'verbose':
				this.write('%+iVerbose answer:%nWe are truely sorry to have to admit, but you are already in verbose mode.%-i');
				break;
			case 'win':
				var todo=game.levels.length-game.nLevel;
				if (todo>0) {
					this.write('Nice try! - But you should really try to clear some '+(todo+1)+' levels first ...');
				}
				else {
					this.write('Nice try! - But you should really try to clear this level first.');
				}
				break;
			case 'ng':
			case 'restart':
				showQuitPrompt(this, true);
				return;
			case 'new':
				var what=this.argv[1];
				if (what && what.toLowerCase()=='game') {
					showQuitPrompt(this, true);
					return;
				}
				else {
					this.write('%+iI do not understand the word "new" in this context.%-i');
				}
				break;
			case '#finalstep#':
				// cheat: one step from final reply
				game.nLevel=game.levels.length;
				game.food=1;
				game.nScore=0;
				this.write('CHEAT-MODE: %+iSet up for %+uone step from final reply%-u.%nEat just one more food to see it.%-i');
				this.write('%n%+i(You lost your total score for using this cheat.)%n');
				this.statusLine(' Level: '+game.nLevel+'  Lives: '+game.nLife+'  Score: '+game.nScore+'  Food left: '+game.food+((game.pill)? '  Extra strength for '+game.pillCnt+' moves.':''), 0, 1);
				break;
			// movements
			case 'go':
			case 'move':
			case 'run':
			case 'step':
			case 'leap':
				var what=getArgAndSkip(this, ['to', 'the', 'in', 'into', 'quick']);
				if (what) {
					if ((what=='on') || (what=='again')) {
						if (pacdir) {
							what=pacdir;
						}
						else {
							this.write('%+iI do not know where to '+cmd+'.%-i');
							break;
						}
					}
					if (shortdirections[what]) {
						pacMove(this, what);
						return;
					}
					else {
						this.write('%+iI do not understand the word "'+what+'" in this context.%-i');
					}
				}
				else if (pacdir) {
					pacMove(this, pacdir);
					return;
				}
				else {
					this.write('%+iI do not know where to '+cmd+'.%-i');
				}
				break;
			// no simple r|u|d move commands
			case 'r':
			case 'u':
			case 'd':
				this.write('%+iI do not understand the word "'+cmd+'" in this context.%-i');
				break;
			default:
				// check for movement shortcuts
				if (shortdirections[cmd]) {
					pacMove(this, cmd);
					return;
				}
				else if (ghostchat) {
					var words=new Array();
					while (this.argc<this.argv.length) words.push(this.argv[this.argc++]);
					this.write('The ghost says: "'+ghostTalk(words.join(' '))+'"');
				}
				else if (this.argv[0]=='?') {
					showInfoScreen(this, helpScreen);
					return;
				}
				else {
					this.write('%+iI do not understand the word "'+cmd+'" in this context.%-i');
				}
				break;
		}
		this.newLine();
		this.newLine();
	}
	this.prompt();
}

function getArgAndSkip(termRef, args) {
	var toSkip=new Object();
	for (var i=0; i<args.length; i++) {
		toSkip[args[i]]=true;
	}
	if (termRef.argc<termRef.argv.length) {
		var what=cleanArg(termRef.argv[termRef.argc++]);
		while ((toSkip[what]) && (termRef.argc<termRef.argv.length)) {
			what=cleanArg(termRef.argv[termRef.argc++]);
		}
		return what;
	}
	else {
		return '';
	}
}

function cleanArg(v) {
	v=v.replace(/[!?,;:\.\-]+$/, '');
	return v.toLowerCase();
}

function fileAccess(termRef, save, fname) {
	if (save) {
		var msg=(fname)? 'DISK ERROR, NO WRITE PERMISSION.': 'SAVE FAILED, REASON UNKNOWN.';
		termRef.write(' There appears a sinister figure before you in the gloom of the passages.%n');
		termRef.write(' He raises his shiny trident and speaks in a firm, but sorrowfull voice,%n');
		termRef.write(' "'+msg+' You shall not pass."%n');
		termRef.write(' As he fades into the spreading darkness there is to be heard a hollow%n');
		termRef.write(' laughter rolling through the passages of the labyrinth.');
	}
	else {
		var msg=(fname)? 'DISK ERROR, FILE NOT FOUND.': 'RESTORE FAILED, UNABLE TO OPEN FILE.';
		termRef.write(' A sinister figure appears before you seeming to float in the air.%n');
		termRef.write(' In a low, sorrowful voice he says, "Alas, the very thing you asked for%n');
		termRef.write(' is unknown in these dungeons." As he fades into the spreading darkness%n');
		termRef.write(' there appears in his place a tastefully lettered sign reading:%n');
		termRef.write(' '+msg+'%n');
		termRef.write(' In some distance there is a hollow voice to be heard laughing.');
	}
}

function pacMove(termRef, d) {
	var dir = shortdirections[d];
	var scx=game.setDir(dir);
	//termRef.clear();
	termRef.newLine();
	if (d) {
		displayScreen=new Array();
		if (scx) {
			displayScreen.push('You move '+directions[dir]+'.');
		}
		else {
			displayScreen.push('Trying to move '+directions[dir]+' you bang your head against a wall.');
			displayScreen.push('You\'re feeling a bit dizzy.');
		}
	}
	game.gameStep();
	if (scx) {
		var pr=game.pac.r;
		var pc=game.pac.c;
		var pd=game.pacDir;
		if (((pc==1) && (pd==1)) || ((pc==20) && (pd==2)) || ((pr==1) && (pd==8)) || ((pr==14) && (pd==4))) {
			displayScreen.push('Entering the teleport you glide through space and time to the opposite side of');
			displayScreen.push('the maze.');
		}
		pacdir=dir;
	}
	else {
		pacdir='';
	}
	makeReport(termRef);
	if (game.gameStatus<0) game.pacDir=4;
	updateMap();
}

function makeReport(termRef) {
	var rep=game.report;
	var brep=game.bonusReport;
	var bri=0;
	for (var i=0; i<rep.length; i++) {
		var ri=rep[i];
		var gate=false;
		switch (ri) {
			case 'food':
				if (Math.random()<.05) {
					displayScreen.push('As you move you are eating the food pellet in your way. It tastes exceptional.');
				}
				else {
					displayScreen.push('As you move you are eating the food pellet in your way. It tastes delicious.');
				}
				break;
			case 'pill':
				displayScreen.push('As you move you eat the big white pill lying in your way.');
				displayScreen.push('An uncommon strength pulsates through your ball-shaped body. You feel bold');
				displayScreen.push('and daring, just as if you were invicible.');
				if (ghostchat) {
					var rd=Math.random();
					if (rd<.33) {
						displayScreen.push('The ghost says: "Sorry my friend, but I\'ve to leave now ..."');
					}
					else if (rd<.66) {
						displayScreen.push('The ghost says: "Rrrr - We stay friends anyway, don\'t we?"');
					}
					else {
						displayScreen.push('The ghost says: "This could well be the end to a beginning friendship."');
					}
				}
				break;
			case 'ghostcaught':
				displayScreen.push('As you get a grip on the ghost there is a bitter-sweet taste in your mouth');
				displayScreen.push('before the ghost evaporates to its cave.');
				displayScreen.push('A bonus of %+i'+brep[bri++]+' points%-i is added to your score.');
				break;
			case 'endlife':
				var rd=Math.random();
				if (ghostchat) {
					if (rd<.33) {
						displayScreen.push('The ghost says: "Sorry my little friend ...", as it comes down on you. The');
						displayScreen.push('last you feel, are the ghost\'s sharp teeth in your neck. As you regain');
						displayScreen.push('consciousness you are in the exact place, where you entered the maze first.');
					}
					else if (rd<.66) {
						displayScreen.push('The ghost says warmly: "Wait my little friend ...", and suddenly attacks you');
						displayScreen.push('with deadly force. The agonizing pain lets you slip into a merciful darkness.');
						displayScreen.push('As you regain consciousness you are in the exact place, where you entered the');
						displayScreen.push('maze first.');
					}
					else {
						displayScreen.push('The ghost mumbles: "Do unto others before ...", and rippes you to pieces.');
						displayScreen.push('There is an agonizing pain before you pass out into a merciful darkness. As');
						displayScreen.push('you regain consciousness you are in the exact place, where you entered the');
						displayScreen.push('maze first.');
					}
				}
				else {
					if (rd<.25) {
						displayScreen.push('The air is painfully infused with a whizzing sound as the ghost comes down on');
						displayScreen.push('you. The last you feel, are the ghost\'s sharp teeth in your neck. As you regain');
						displayScreen.push('consciousness you are in the exact place, where you entered the maze first');
					}
					else if (rd<.5) {
						displayScreen.push('Suddenly you are grabbed by the ghost and ripped to pieces. The agonizing pain');
						displayScreen.push('lets you slip into a merciful darkness. As you regain consciousness you are in');
						displayScreen.push('the exact place, where you entered the maze first.');
					}
					else {
						displayScreen.push('As the ghost touches you there is an agonizing pain before you pass out into a');
						displayScreen.push('merciful darkness. As you regain consciousness you are in the exact place,');
						displayScreen.push('where you entered the maze first.');
					}
				}
				displayScreen.push(' ');
				displayScreen.push('                                      ****');
				break;
			case 'bonuslife':
				displayScreen.push('Good news: As you are roaming the maze so bravely, you earn an extra life.');
				break;
			case 'gate':
				if (!gate) {
					gate=true;
					displayScreen.push('There is a shrieking noise echoing through the maze.');
				}
				break;
			case 'gameover':
				var rd=Math.random();
				if (rd<.33) {
					displayScreen.push('Suddenly you are grabbed by the ghost and ripped to pieces. As you exhausted');
					displayScreen.push('the last of your lives the game is ended.');
				}
				else if (rd<.66) {
					displayScreen.push('There is smacking sound as you are hit by the ghost\'s teeth. As you have no');
					displayScreen.push('more life to lose the game is ended.');
				}
				else {
					displayScreen.push('As the ghost touches you there is an agonizing pain. As you are losing the');
					displayScreen.push('last of your lives the game is ended.');
				}
				displayScreen.push(' ');
				displayScreen.push(' ');
				displayScreen.push('                           **** G A M E  O V E R ****');
				displayScreen.push(' ');
				displayScreen.push(' ');
				displayScreen.push('             %+iHit <ENTER> to restart the game, type "quit" to quit.%-i');
				displayScreen.push(' ');
				displayStatus=[
					' Level: '+game.nLevel+'  Lives: '+game.nLife+'  Score: '+game.nScore+'  Food left: '+game.food,
					' Hit <ENTER> to restart the game, type "quit" to quit.'
				];
				termRef.statusLine(displayStatus[0], 0, 1);
				termRef.statusLine(displayStatus[1], 1, 2);
				termRef.maxLines = termRef.conf.rows-2;
				termRef.write(displayScreen, true);
				termRef.env.isStart=true;
				return;
			case 'newlevel':
				displayScreen.push(' ');
				if (game.nLevel==game.levels.length) {
					// final reply
					displayScreen.push(' *** As you cleared the '+game.nLevel+'th maze you\'ve reached the final level of wisdom. ***');
					displayScreen.push(' ');
					displayScreen.push('Out of the gloom of a distant passage appears an old man tripping over his');
					displayScreen.push('long white beard. It is the Dungeon Master, the bearer of all wisdom. With');
					displayScreen.push('some circumstantialities he presents an ancient scroll made of precious');
					displayScreen.push('parchment to you. As it magically unfolds it reads:');
					displayScreen.push(' ');
					displayScreen.push('                                    "XZYYZX"');
					displayScreen.push(' ');
					displayScreen.push('"This, my jolly friend," he says, "is the omnipotent spell that rules these');
					displayScreen.push('dungeons. For pity\'s sake the parser does not understand it."');
					displayScreen.push(' ');
					displayScreen.push('Having said this, he evaporates into the darkness leaving a small cloud of a');
					displayScreen.push('peculiar smell behind, which keeps sailing softly in the gloom of the passages.');
				}
				else {
					displayScreen.push('*** As you have cleared the whole maze you\'ve reached the end of level '+game.nLevel+'. ***');
					displayScreen.push(' ');
					displayScreen.push('The walls of the passages are blinking in a shiny color while you hear the');
					if (game.nLevel==1) {
						displayScreen.push('tunes of a merry melody. (The instruments sound a bit squeaky and out of tune,');
						displayScreen.push('but everyone would tell you it\'s being big fun.)');
					}
					else {
						displayScreen.push('tunes of a merry melody.');
					}
				}
				displayScreen.push(' ');
				displayScreen.push(' ');
				displayScreen.push('                     %+iHit <ENTER> to enter the next level.%-i');
				displayScreen.push(' ');
				displayStatus=[
					' Level: '+game.nLevel+'  Lives: '+game.nLife+'  Score: '+game.nScore+'  Food left: '+game.food,
					' Hit <ENTER> to start the next level.'
				];
				termRef.statusLine(displayStatus[0], 0, 1);
				termRef.statusLine(displayStatus[1], 1, 2);
				termRef.maxLines = termRef.conf.rows-2;
				termRef.write(displayScreen, true);
				return;
		}
	}
	displayScreen.push(' ');
	displayScreenOffset=displayScreen.length;
	var d=game.pac.dir;
	var ds=new Array();
	var dsUC=new Array();
	var dss=new Array();
	var pr=game.pac.r;
	var pc=game.pac.c;
	
	var f1=game.f1;
	var f2=game.f2;
	var ftt=game.fft;
	var ghosts=new Array();
	var ghostdirs=0;
	nearthings = {
		ghost:false,
		food: false,
		pill: false,
		teleport: false,
		gate: false,
		cave: false,
		sign: false
	};
	if ((game.pac.r==7) && (game.pac.c==13)) nearthings.gate=nearthings.cave=true;
	for (var i=0; i<compassdirs.length; i++) {
		var di=compassdirs[i];
		if (d&di) {
			ds.push(compass[di]);
			dsUC.push(compassUC[di]);
			var dx=game.tx[di];
			var dy=game.ty[di];
			var r=pr+dy;
			var c=pc+dx;
			var steps=1;
			var food=0;
			var pill=0;
			var tp=false;
			var crossing=0;
			var crosstype='';
			while ((r>0) && (r<15) && (c>0) && (c<21)) {
				if (f1[r][c]==8) {
					if (!pill) pill=steps;
					if (!food) food=steps;
					if (steps==1) nearthings.pill=true;
				}
				else if (f1[r][c]) {
					if (!food) food=steps;
					if (steps==1) nearthings.food=true;
				}
				if (ftt[r][c]) {
					for (var k=1; k<=4; k++) {
						if (ftt[r][c]&game.g[k].bid) {
							ghosts.push({ dir:compass[di], steps: steps, color: game.gColor[k], bound: compass[game.g[k].d] });
							ghostdirs|=di;
						}
					}
				}
				if (((r==1) || (c==1) || (r==14) || (c==20)) && (!crossing)) tp=true;
				var f2v=f2[r][c];
				if (f2v) {
					if (!crossing) {
						crossing=steps;
						var f2n=game.t2[f2v].length;
						crosstype= (f2n==2)? 'corner' : (f2n==3)? 'junction' : 'crossing';
					}
					if (!(f2[r][c]&di)) break;
				}
				r+=dy;
				c+=dx;
				steps++;
			}
			dss.push({ dir:compass[di], dirUC:compassUC[di], food:food, pill:pill, teleport:tp, crossing: crossing, dircode: di, crosstype:crosstype });
		}
	}
	
	var l=game.t2[d].length;
	if ((d==3) || (d==12)) {
		displayScreen.push('You are in a passage leading from '+((d==3)? 'west to east.':'north to south.'));
	}
	else if (l==2) {
		displayScreen.push('You are at a corner leading '+ds.join(' and ')+'.');
	}
	else if (l==3) {
		displayScreen.push('You are at a junction leading '+ds[0]+', '+ds[1]+', and to the '+ds[2]+'.');
	}
	else if (l==4) {
		displayScreen.push('You are at a crossing leading north, south, east, west.');
	}
	if ((pr==7) && (pc==13)) {
		displayScreen.push('There is an old rusty iron gate in the western wall. Through the gate you can');
		var gg=new Array();
		for (var i=1; i<=4; i++) {
			var gi=game.g[i];
			if (gi.s<2) gg.push(game.gColor[i]);
		}
		if (gg.length) {
			var pl=(gg.length>1);
			displayScreen.push('see a cave. Inside there '+((pl)? 'are':'is')+' the '+gg.join((gg.length>2)? ', the ':' and the ')+' ghost.');
			displayScreen.push('The ghost'+((pl)? 's are':' is')+' whizzing angrily.');
		}
		else {
			displayScreen.push('an empty cave.');
		}
	}
	else if ((pr==9) && (pc==10)) {
		displayScreen.push('There is a sign on the wall.');
		nearthings.sign=true;
	}
	var teleport;
	if (pc==1) {
		teleport='west';
	}
	else if (pc==20) {
		teleport='east';
	}
	else if (pr==1) {
		teleport='north';
	}
	else if (pr==14) {
		teleport='south';
	}
	if (teleport) {
		displayScreen.push('In the '+teleport.toUpperCase()+' you are facing the vast emptiness of a teleport. The look into the');
		displayScreen.push('abyss is a bit nauseating.');
		nearthings.teleport=true;
	}
	
	displayScreen.push(' ');
	for (var i=0; i<dss.length; i++) {
		var dsi=dss[i];
		if (dsi.food) {
			displayScreen.push('In the '+dsi.dirUC+' you can see some delicious food '+((dsi.food==1)? 'just 1 step':dsi.food+' steps')+' away.');
		}
		else if (teleport!=dsi.dir) {
			if (ghostdirs&dsi.dircode) {
				displayScreen.push('There is no food to the '+dsi.dirUC+'.');
			}
			else {
				displayScreen.push('In the '+dsi.dirUC+' you can see an empty passage.');
			}
		}
		if (dsi.pill) {
			displayScreen.push('One of the pellets is bigger and brighter, it is '+dsi.food+' step'+((dsi.food>1)? 's':'')+' away.');
		}
		if (dsi.crossing) {
			displayScreen.push('There is a '+dsi.crosstype+' to the '+dsi.dir+' '+dsi.crossing+' step'+((dsi.crossing>1)? 's':'')+' away.');
		}
		if (dsi.teleport) {
			displayScreen.push('At the end of the '+dsi.dir+'ern passage there is the vast emptiness of a teleport.');
		}
	}
	if (ghosts.length) {
		displayScreen.push(' ');
		displayScreen.push('There is a whizzing sound in the air.');
	}
	for (var i=0; i<ghosts.length; i++) {
		var gi=ghosts[i];
		if (i==0) {
			if (game.pill) {
				displayScreen.push('In the '+gi.dir+'ern passage you can see a blue ghost going '+gi.bound+' slowly. The ghost');
				displayScreen.push('is '+gi.steps+' step'+((gi.steps>1)? 's':'')+' away. It looks anxious.');
			}
			else {
				displayScreen.push('In the '+gi.dir+'ern passage you can see the '+gi.color+' ghost. It is '+gi.steps+' step'+((gi.steps>1)? 's':'')+' away moving');
				displayScreen.push(gi.bound+'. The ghost is looking quite angry.');
			}
		}
		else {
			if (game.pill) {
				displayScreen.push('Another blue ghost is in the '+gi.dir+'ern passage '+gi.steps+' step'+((gi.steps>1)? 's':'')+' away moving '+gi.bound+'.');
			}
			else {
				displayScreen.push('There is also the '+gi.color+' ghost in the '+gi.dir+'ern passage '+gi.steps+' step'+((gi.steps>1)? 's':'')+' away moving '+gi.bound+'.');
			}
		}
		if (gi.steps<=2) nearthings.ghost=true;
	}
	if (!nearthings.ghost) ghostchat=false;
	
	displayScreen.push(' ');
	displayStatus=[
		' Level: '+game.nLevel+'  Lives: '+game.nLife+'  Score: '+game.nScore+'  Food left: '+game.food+((game.pill)? '  Extra strength for '+game.pillCnt+' moves.':''),
		' Possible directions: '+dsUC.join(', ')+'.  Type "?" or "help" for help.'
	];
	termRef.statusLine(displayStatus[0], 0, 1);
	termRef.statusLine(displayStatus[1], 1, 2);
	termRef.maxLines = termRef.conf.rows-2;
	termRef.write(displayScreen);
	termRef.prompt();
}

var calcPhraseExpr=/^(calculate|calc|evaluate|eval|what makes)?([0-9\.,eE+\-\/\*xX:\ \(\)]+)(\s*[=?])?$/;

function ghostTalk(phrase) {
	if (phrase.match(/[0-9]/) && (phrase.match(/[+\-\/\*xX:]/))) {
		var r=calcPhraseExpr.exec(phrase);
		if (r) {
			try {
				return 'Makes '+eval(r[2].replace(/x/gi, '*').replace(/:/g, '/').replace(/,/g, '.'))+'.';
			}
			catch(e) {
				return 'Sorry, this does not calculate."%nThe ghost engages busily behind his back and produces a big sign reading:%n"SYNTAX ERROR: NOT A VALID EXPRESSION.';
			}
		}
	}
	return ghostbot.transform(phrase);
}


// termlib screen cupture/restore

var screenBuffer;

function termCapture(termRef) {
	var rl=termRef.conf.rows;
	var cl=termRef.conf.cols;
	screenBuffer=new Object();
	screenBuffer.cbuf=new Array();
	screenBuffer.sbuf=new Array();
	screenBuffer.rows=rl;
	screenBuffer.cols=cl;
	screenBuffer.maxCols=termRef.maxCols;
	screenBuffer.maxLines=termRef.maxLines;
	screenBuffer.r=termRef.r;
	screenBuffer.c=termRef.c;
	screenBuffer.cursoractive=termRef.cursoractive;
	if (termRef.cursoractive) termRef.cursorOff();
	for (var r=0; r<rl; r++) {
		var cbr=termRef.charBuf[r];
		var sbr=termRef.styleBuf[r];
		var tcbr=screenBuffer.cbuf[r]=new Array();
		var tsbr=screenBuffer.sbuf[r]=new Array();
		for (var c=0; c<cl; c++) {
			tcbr[c]=cbr[c];
			tsbr[c]=sbr[c];
		}
	}
}

function termRestore(termRef) {
	if (!screenBuffer) return;
	termRef.maxLines=termRef.conf.rows=screenBuffer.rows;
	termRef.maxCols=termRef.conf.cols=screenBuffer.cols;
	for (var r=0; r<screenBuffer.rows; r++) {
		termRef.charBuf[r]=screenBuffer.cbuf[r];
		termRef.styleBuf[r]=screenBuffer.sbuf[r];
		termRef.redraw(r);
	}
	termRef.r=screenBuffer.r;
	termRef.c=screenBuffer.c;
	termRef.maxCols=screenBuffer.maxCols;
	termRef.maxLines=screenBuffer.maxLines;
	termRef.cursoractive=screenBuffer.cursoractive;
	if (termRef.cursoractive) termRef.cursorOn();
	screenBuffer=null;
}

// widget handlers

function wdgtCloseHandler(s) {
	document.images.wdgtClose.src=wdgtImgRef['close_'+s].src;
	if (s=='dn') {
		dragRelease();
		dragLock=true;
		return false;
	}
	dragLock=(s=='hi')? true:false;
	return true;
}

function wdgtCloseRelease() {
	document.images.wdgtClose.src=wdgtImgRef.close_lo.src;
	setTimeout('closeApp()',3);
	dragLock=false;
}

function closeApp() {
	if ((term) && (!term.closed)) term.close();
}

// Mac MSIE fix
if (!Array.prototype.push) Array.prototype.push = function(v) { this[this.length]=v; }

// eof