/*
  JavaScript-PacMan 2
  (c) N.Landsteiner 1996-2007; www.masswerk.at

*/

// preload & setup

var elements = new Object();

var pacimages = [
	'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k', 'l', 'm', 'n',
	'o', 'p', 'q', 'r', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'pnt',
	'e0', 'e1', 'e2', 'e4', 'e8', 'ge',
	'g11', 'g12', 'g13', 'g21', 'g22', 'g23', 'g31', 'g32', 'g33',
	'g41', 'g42', 'g43', 'ga1', 'ga2', 'ga3', 'gn1', 'gn2', 'gn3', 'gx',
	'pb1', 'pf1', 'pf2', 'pf3', 'pl1', 'pl2', 'pl3', 'pr1', 'pr2', 'pr3',
	'px1', 'px2', 'px3', 'px4', 'px5', 'px6', 'px7',// 's',
	'bonus0', 'bonus1', 'bonus2', 'bonus3',
	'gameover', 'gamepaused'
];
var pacimages2 = [
	'life_hi', 'life_lo', 'xx', 'newgame_lo', 'newgame_hi', 'newgame_dn',
	'neon_lo', 'neon_hi'
];
var imgRef, imgCnt, imgTotal;
var imgPath='pacimages/';

function init() {
	elements.maze=getElement('maze');
	if ((!elements.maze) || (!document.images)) {
		if (prompt('Sorry, DOM compatible browser (Mozilla, Firefox, Internet Explorer 5+, Safari, etc) required.\n\nTry the legacy version for older browsers?\n')) {
			self.location.replace('legacy/JS-PacManPlus.htm');
		}
		return;
	}
	setVisibility(getElement('progress'), true);
	elements.scoredisplay=getElement('scoredisplay');
	elements.leveldisplay=getElement('leveldisplay');
	elements.mazedisplay=getElement('mazedisplay');
	preload();
}

function preload() {
	imgRef=new Object();
	imgCnt=0;
	imgTotal=pacimages.length+pacimages2.length;
	var i;
	for (i=0; i<pacimages.length; i++) {
		var n=pacimages[i];
		var img=imgRef[n]=new Image();
		img.src=imgPath+n+'.png';
		if (img.complete) {
			imgTotal--;
		}
		else {
			img.onload=preloadHandler;
		}
	}
	for (i=0; i<pacimages2.length; i++) {
		var n=pacimages2[i];
		var img=imgRef[n]=new Image();
		img.src=imgPath+n+'.gif';
		if (img.complete) {
			imgTotal--;
		}
		else {
			img.onload=preloadHandler;
		}
	}
	if (imgTotal==0) preloadComplete();
}

function preloadHandler() {
	imgCnt++;
	var d=getElement('progressbar');
	var v = imgCnt/imgTotal;
	d.style.width=Math.round(160*v)+'px';
	d=getElement('progressvalue');
	d.innerHTML=Math.round(v*100)+'%';
	if (imgCnt==imgTotal) setTimeout('preloadComplete()', 10);
}

function preloadComplete() {
	var d=getElement('progress');
	setVisibility(d, false);
	d.style.display='none';
	initMaze();
	initSprites();
	setVisibility(getElement('controls'), true);
	var select=getElement('mazeselect');
	select.selectedIndex=0;
	setQuality(settingsQualityValue);
	setSpeed(settingsSpeedValue);
	enableKeyboard();
	if (window.activateTouchControls) activateTouchControls();
	setTimeout('newGame()', 10);
}

function initMaze() {
	var r,c;
	var s='<table border="0" cellspacing="0" cellpadding="0">\n';
	if (msieLe6) {
		for (r=1; r<=14; r++) {
			s+='<tr><td nowrap>';
			for (c=1; c<=20; c++) {
				s+='<img src="'+imgPath+'xx.gif" width="27" height="27" alt="" id="t'+r+'_'+c+'">';
			}
			s+='</td></tr>\n';
		}
	}
	else {
		for (r=1; r<=14; r++) {
			s+='<tr><td nowrap>';
			for (c=1; c<=20; c++) {
				s+='<img src="'+imgPath+'x.png" width="27" height="27" alt="" id="t'+r+'_'+c+'">';
			}
			s+='</td></tr>\n';
		}
	}
	s+='</table>';
	elements.maze.innerHTML=s;
	if (msieLe6) {
		var f="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+imgPath+"x.png', sizingMethod='scale')";
		for (r=1; r<=14; r++) {
			for (c=1; c<=20; c++) {
				document.all['t'+r+'_'+c].style.filter=f;
			}
		}
	}
}

function initSprites() {
	var d, id;
	var xpng=imgPath+'x.png';
	var xgif=imgPath+'xx.gif';
	var imgsrc= (msieLe6)? xgif:xpng;
	var misieFilter="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+xpng+"', sizingMethod='scale')";
	id='pac';
	d = elements[id]=getElement(id);
	setVisibility(d, false);
	d.innerHTML='<img src="'+imgsrc+'" width="27" height="27" alt="" id="imgpac">';
	d.style.width=d.style.height='27px';
	d=elements.pacimg=getElement('imgpac');
	if (msieLe6) d.style.filter=misieFilter;
	for (var i=1; i<=4; i++) {
		id='g'+i;
		d=elements[id]=getElement(id);
		d.style.width=d.style.height='27px';
		setVisibility(d, false);
		id='gbody'+i;
		d = elements[id]=getElement(id);
		d.innerHTML='<img src="'+imgsrc+'" width="27" height="27" alt="" id="imgg'+i+'">';
		d.style.width=d.style.height='27px';
		id='geyes'+i;
		d = elements[id]=getElement(id);
		d.innerHTML='<img src="'+imgsrc+'" width="27" height="27" alt="" id="imgge'+i+'">';
		d.style.width=d.style.height='27px';
		id='imgg'+i;
		d=elements[id]=getElement(id);
		if (msieLe6) d.style.filter=misieFilter;
		id='imgge'+i;
		d=elements[id]=getElement(id);
		if (msieLe6) d.style.filter=misieFilter;
		
		id='bonus'+i;
		d=elements[id]=getElement(id);
		setVisibility(d, false);
		d.innerHTML='<img src="'+imgsrc+'" width="27" height="27" alt="" id="imgbonus'+i+'">';
		d.style.width=d.style.height='27px';
		id='imgbonus'+i;
		d=elements[id]=getElement(id);
		if (msieLe6) d.style.filter=misieFilter;
	}
	d=elements.gameover=getElement('gameover');
	setVisibility(d, false);
	if (msieLe6) {
		d.innerHTML='<img id="imggameover" src="'+imgPath+'xx.gif" width="184" height="35" alt="">';
		document.all.imggameover.style.filter="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+imgPath+"gameover.png', sizingMethod='scale')";
	}
	else {
		d.innerHTML='<img src="'+imgPath+'gameover.png" width="184" height="35" alt="">';
	}
	d.style.left='178px';
	d.style.top='140px';
	d=elements.gamepaused=getElement('gamepaused');
	setVisibility(d, false);
	if (msieLe6) {
		d.innerHTML='<img id="imggamepaused" src="'+xgif+'" width="227" height="35" alt="">'; document.all.imggamepaused.style.filter="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+imgPath+"gamepaused.png', sizingMethod='scale')";
	}
	else {
		d.innerHTML='<img src="'+imgPath+'gamepaused.png" width="227" height="35" alt="">';
	}
	d.style.left='156px';
	d.style.top='140px';
	if (msieLe6 && elements.imgg1 && elements.imgg1.filters && elements.imgg1.filters.item(0)) {
		setSpriteImg=setSpriteImgMsieLe6;
		setMaze=setMazeMsieLe6;
	}
	elements.restartdialog=getElement('restartdialog');
	elements.dialogscreen=getElement('dialogscreen');
	elements.dialogscreenimg=getElement('dialogscreenimg');
}

// definitions

var settingsQuality=new Array(5, 6, 7);
var settingsSpeed=new Array(282, 252, 228, 204);
var speedFactor=new Array(1.08, 1, 0.94);
var settingsQualityValue=1;
var settingsSpeedValue=1;
var aStep=6;
var aSpeed=252;
var gameDelay;

var blinkRate=4;
var blinkSteps=6;
var pillFactor=1.75;
var pillMaxLength=26;
var pillDecrement=5;
var eyesFactor=.75;
var bonusLifeScore=10000;
var useInstantTurns=true;
var pillMinLength, bonusLength;

if (typeof window.useSound == 'undefined') useSound=false;

var ghostResetDelay=20;
var ghostResetAlpha=.7;
var ghostResetDelta=.05;

var mazeColor='none';
var mazeMode ='Standard Levels';

var highscore=0;

var f1= new Array();
var f2= new Array();
var fw= new Array();
var fft= new Array();
for (var i=1; i<=14; i++) {
	f1[i]=new Array();
	f2[i]=new Array();
	fw[i]=new Array();
	fft[i]=new Array();
}

var standardLevels = [
{
	name: "Standard Level 1",
	color: "gold",
	maze: [
		"ahhhhhgxbhhdxehhhhhc",
		"vp....o......o....pv",
		"v.lhm...lhhm...lhm.v",
		"v.....n......n.....v",
		"v.n.n.v.ahhc.v.n.n.v",
		"d.v.o.v.vxxq.v.o.v.b",
		"x.v...v.vxxt.v...v.x",
		"c.bhm.o.bhhr.o.lhd.a",
		"v........x.........v",
		"em.lc.am.lm.lc.am.lg",
		"v...v.v......v.v...v",
		"v.k.o.o.lhhm.o.o.k.v",
		"vp................pv",
		"bhhhhhcxahhcxahhhhhd"
	]
},
{
	name: "Standard Level 2",
	color: "gold",
	maze: [
		"ahhhdxbhhhhhhdxbhhhc",
		"v.p..............p.v",
		"v.k.n.lm.lm.lm.n.k.v",
		"v...v..........v...v",
		"ehm.bhm.ahhc.lhd.lhg",
		"d.......vxxq.......b",
		"x.lhm.n.vxxt.n.lhm.x",
		"c.....v.bhhr.v.....a",
		"ehhm.lg..x...em.lhhg",
		"v.....o.n.lc.o.....v",
		"v.ahc...v..v...ahc.v",
		"v.bhd.n.bm.o.n.bhd.v",
		"v.p...v......v...p.v",
		"bhhhcxehhhhhhgxahhhd"
	]
},
{
	name: "Standard Level 3",
	color: "cyan",
	maze: [
		"ahdxbhihhhhhhihdxbhc",
		"vp....o......o....pv",
		"d.aim...lhhm...lic.b",
		"x.bg..n......n..ed.x",
		"c..o.ag.ahhc.ec.o..a",
		"em...bg.vxxq.ed...lg",
		"v..n..o.vxxt.o..n..v",
		"v.lfm...bhhr...lfm.v",
		"v.....n..x...n.....v",
		"d.lhhid.lhhm.bihhm.b",
		"x....o........o....x",
		"c.lm...k.lm.k...lm.a",
		"vp...n........n...pv",
		"bhcxafhhhhhhhhfcxahd"
	]
},
{
	name: "Standard Level 4",
	color: "cyan",
	maze: [
		"nxaidxbhhgxbdxbhmxlc",
		"v.bdp....v.........v",
		"v.....lm.o.lhm.ahcpv",
		"v.lhm..........vkv.v",
		"d.....n.ahhihm.bhd.b",
		"x.ahc.v.vxxq.......x",
		"c.vkv.v.vxxt.am.lc.a",
		"d.bhd.o.bhhr.v...o.b",
		"x........x...o.n...x",
		"c.lhc.lhhm.n...o.n.a",
		"v...o......v.n..pv.v",
		"v.n...n.lm.o.bm.ld.v",
		"v.ecp.v............v",
		"oxbfcxehhcxacxahmxld"
	]
},
{
	name: "Standard Level 5",
	color: "greenneon",
	maze: [
		"ahhhdxbhhhhhhdxoxbhc",
		"v.p................v",
		"v.ahc.ahhm.lhc.ahm.v",
		"v.bhd.v......v.v...v",
		"d.....v.ahhc.o.opn.b",
		"x.ahm.o.vxxq.....v.x",
		"c.v.....vxxt.n.lhd.a",
		"d.o.ahc.bhhr.o.....b",
		"x...vkv..x.....n.n.x",
		"c.n.bhd.lhhihm.v.o.a",
		"v.vp.......v...v...v",
		"v.bhm.lhhm.o.k.ehm.v",
		"v..............v...v",
		"bhhhcxahhhhhhcxvxahd"
	]
}
];

var AztecLevels = [
{
	name: "Aztec Level 1",
	color: "desert",
	maze: [
		"ahhhihdxbhhdxbhihhhc",
		"v.p.v..........v.p.v",
		"d.z.o.lhhhhhhm.o.z.b",
		"x..................x",
		"c.ahhhc.ahhc.ahhhc.a",
		"v.vahcv.vxxq.vahcv.v",
		"v.vvldv.vxxt.vbmvv.v",
		"v.vbhhd.bhhr.bhhdv.v",
		"d.bhhhm..x...lhhhd.b",
		"x.......ahhc.......x",
		"ic.ac.lhgzzehm.ac.ai",
		"ed.bd...bhhd...bd.bg",
		"ecp...z......z...pag",
		"bfhhhhcxahhcxahhhhfd"
	]
},
{
	name: "Aztec Level 2",
	color: "pueblored",
	maze: [
		"ahdxbhhhhhhhhhhdxbhc",
		"vp................pv",
		"d.ahhhm.ahhc.lhhhc.b",
		"x.v.....vxxv.....v.x",
		"c.v.lhihyhhyhihm.v.a",
		"v.v...bhgxxuhd...v.v",
		"v.v.n...vxxt...n.v.v",
		"d.o.v.n.bhhr.n.v.o.b",
		"x...v.v..x...v.v...x",
		"m.ahg.o.ahhc.o.ehc.l",
		"x.vzv...vzzv...vzv.x",
		"c.bhd.z.bhhd.z.bhd.a",
		"v...p..........p...v",
		"bhcxahhhhhhhhhhcxahd"
	]
},
{
	name: "Aztec Level 3",
	color: "forest",
	maze: [
		"ahhhhhdxbhhdxbhhhhhc",
		"vp................pv",
		"v.ahhhm.acac.lhhhc.v",
		"v.v.....edbg.....v.v",
		"v.o.lihhyhhyhhim.o.v",
		"v....bhhgxxuhhd....v",
		"fm.n....vxxt....n.lf",
		"xx.v.ac.bhhr.ac.v.xx",
		"im.o.bd..x...bd.o.li",
		"v.......ahhc.......v",
		"v.ahc.lhdlmbhm.ahc.v",
		"v.bhd..........bhd.v",
		"v.p...n.lhhm.n...p.v",
		"bhhhhhgxahhcxehhhhhd"
	]
},
{
	name: "Aztec Level 4",
	color: "lemon",
	maze: [
		"ahhdxbhhhhhhhhdxbhhc",
		"vp................pv",
		"d.ac.am.ahhc.lc.ac.b",
		"x.ed.v..vxxv..v.bg.x",
		"c.v..v.ayhhyc.v..v.a",
		"v.v.ld.bgxxud.bm.v.v",
		"v.v.....vxxt.....v.v",
		"v.v.n.n.bhhr.n.n.v.v",
		"d.v.o.v..x...v.o.v.b",
		"x.v.p.v.lhhm.v.p.v.x",
		"c.ehhhg......ehhhg.a",
		"v.bhhhfm.zz.lfhhhd.v",
		"v..................v",
		"bhhcxahhhhhhhhcxahhd"
	]
},
{
	name: "Aztec Level 5",
	color: "gold",
	maze: [
		"ahhhhdxbhhhhdxbhhhhc",
		"d..................b",
		"x.n.n.ahhhhhhc.n.n.x",
		"c.v.v.v......v.v.v.a",
		"v.v.v.v.ahhc.v.v.v.v",
		"v.v.v.v.vxxq.v.v.v.v",
		"v.v.vpv.vxxt.vpv.v.v",
		"v.o.v.v.bhhr.v.v.o.v",
		"v...v.v..x...v.v...v",
		"v.z.v.bm.ac.ld.v.z.v",
		"d.p.v....vv....v.p.b",
		"x.z.bm.n.bd.n.ld.z.x",
		"c......v....v......a",
		"bhhhhcxehhhhgxahhhhd"
	]
}
];

var LegacyLevels= [
{
	name: "Remember Pac-Man?",
	color: "blueneon",
	maze: [
		'ahhhhhhhhiihhhhhhhhc',
		'v........vv........v',
		'vplm.lhm.bd.lhm.lmpv',
		'v..................v',
		'bhhc.ac.ahhc.ac.ahhd',
		'lhhd.bd.vxxq.bd.bhhm',
		'xxxx....vxxt....xxxx',
		'lhhc.lm.bhhr.lm.ahhm',
		'ahhd.....x......bhhc',
		'v....ac.liim.ac....v',
		'vpn.adv..vv..vbc.npv',
		'v.o.bhfm.bd.lfhd.o.v',
		'v..................v',
		'bhhhhhhhhhhhhhhhhhhd'
	]
},
{
	name: "Meeting Ms Pac-Man",
	color: "plum",
	maze: [
		'ahhhhhihhhhhhihhhhhc',
		'v.....v......v.....v',
		'vplhm.o.lhhm.o.lhmpv',
		'v..................v',
		'bhm.n.n.ahhc.n.n.lhd',
		'xxx.v.o.vxxq.o.v.xxx',
		'lhc.v...vxxt...v.ahm',
		'ahd.bhm.bhhr.lhd.bhc',
		'v........x.........v',
		'v.ac.ahm.ac.lhc.ac.v',
		'v.vv.v...vv...v.vv.v',
		'vpbd.o.lhffhm.o.bdpv',
		'v..................v',
		'bhhhhhhhhhhhhhhhhhhd'
	]
},
{
	name: "Ms P. for tea",
	color: "cyan",
	maze: [
		'lhhhhhihhhhhhihhhhhm',
		'xxxxxxv......vxxxxxx',
		'ahhhmxo.lhhm.oxlhhhc',
		'vp................pv',
		'v.lihhm.ahhc.lhhim.v',
		'v..o....vxxq....o..v',
		'bc...lm.vxxt.lm...ad',
		'ld.lhim.bhhr.lihm.bm',
		'xx...v...x....v...xx',
		'am.n.o.lhiihm.o.n.lc',
		'vp.v.....vv.....v.pv',
		'v.ld.lhm.bd.lhm.bm.v',
		'v..................v',
		'bhhhhhhhhhhhhhhhhhhd'
	]
},
{
	name: "A promenade with Ms P.",
	color: "gold",
	maze: [
		'ahhhhhhihhhhihhhhhhc',
		'v......v....v......v',
		'vplhhm.o.lm.o.lhhmpv',
		'v..................v',
		'bhm.n.z.ahhc.z.n.lhd',
		'x...o...vxxq...o...x',
		'n.n...n.vxxt.n...n.n',
		'v.o.lhd.bhhr.bhm.o.v',
		'v........x.........v',
		'em.lm.am.ac.lc.lm.lg',
		'vp....v..vv..v....pv',
		'v.lhm.v.lffm.v.lhm.v',
		'v.....v......v.....v',
		'bhhhhhfhhhhhhfhhhhhd'
	]
},
{
	name: "Ms Pac-Man's day out",
	color: "darkneon",
	maze: [
		'ahhhhhhhhhhhhhhhhhhc',
		'v..................v',
		'vpn.z.ahhhhhhc.z.npv',
		'v.o...o......o...o.v',
		'v...n...ahhc...n...v',
		'bhm.ehm.vxxq.lhg.lhd',
		'xxx.oxx.vxxt.xxo.xxx',
		'hhm...k.bhhr.k...lhh',
		'xxx.n....x.....n.xxx',
		'ahm.o.n.lhhm.n.o.lhc',
		'v.....v......v.....v',
		'vplm.lfm.ac.lfm.lmpv',
		'v........vv........v',
		'bhhhhhhhhdbhhhhhhhhd'
	]
}
];

var HackerLevels= [
{
	name: "Bourne Again ...",
	color: "emerald",
	maze: [
	'lhhhhhmxlhhmxlhhhhhm',
	'x.p..............p.x',
	'ac.ahcn.lhhm.lhhm.ac',
	'vv.bcvv...........vv',
	'vv.nvvv.ahhc.ahhc.vv',
	'bd.ovvo.vxxq.vacv.bd',
	'x..adbc.vxxt.vbdv..x',
	'ac.bhhd.bhhr.bhcv.ac',
	'vv.......x...ahdv.vv',
	'vv.ahhc.ahhc.bhhd.vv',
	'vv.vxxv.vacv......vv',
	'bd.vxxv.vvvv.ahhc.bd',
	'x.pbhhd.bdbd.bhhdp.x',
	'lhhhhhmxlhhmxlhhhhhm'
	]
},
{
	name: "Binary Delights",
	color: "grass",
	maze: [
	'ahhhhdxbhhhhdxbhhhhc',
	'vp................pv',
	'v.lm.lhm.lm.lhm.lm.v',
	'v..................v',
	'v.ahc.n.ahhc.n.ahc.v',
	'd.vxv.v.vxxq.v.vxv.b',
	'x.vxv.v.vxxt.v.vxv.x',
	'c.bhd.o.bhhr.o.bhd.a',
	'v........x.........v',
	'v.n.ac.n.ac.n.ac.n.v',
	'v.v.vv.v.vv.v.vv.v.v',
	'v.o.bd.o.bd.o.bd.o.v',
	'vp................pv',
	'bhhhhcxahhhhcxahhhhd'
	]
},
{
	name: "Solid State",
	color: "lemon",
	maze: [
	'ahhhhhhdxvvxbhhhhhhc',
	'vp.......eg.......pv',
	'ec.lm.lc.bd.am.lm.ag',
	'ed.....o....o.....bg',
	'v..ahc..ahhc..ahc..v',
	'v.lgxem.vxxq.lgxem.v',
	'v.lgxem.vxxt.lgxem.v',
	'v.lgxem.bhhr.lgxem.v',
	'v..bhd...x....bhd..v',
	'fm.....z.lm.z.....lf',
	'x..ahc........ahc..x',
	'c.lfhfm.liim.lfhfm.a',
	'vp......xegx......pv',
	'bhhhhhhcxvvxahhhhhhd'
	]
},
{
	name: "Compelling Logic",
	color: "greenneon",
	maze: [
	'ahhhhhhdxvvxbhhhhhhc',
	'v........vv........v',
	'vpac.n.n.bd.n.n.acpv',
	'v.vv.o.v....v.o.vv.v',
	'v.bd...oahhco...bd.v',
	'v....n..vxxq..n....v',
	'biic.ec.vxxt.ag.aiid',
	'affd.ed.bhhr.bg.bffc',
	'v....o...x....o....v',
	'o.ac...ahhhhc...ac.o',
	'x.vv.ahg....ehc.vv.x',
	'n.bd.bhd.ac.bhd.bd.n',
	'vp.......vv.......pv',
	'bhhhhhhcxvvxahhhhhhd'
	]
},
{
	name: "Pong!",
	color: "white",
	maze: [
	'ahhhhhhhhhhhhhhhhhhc',
	'em.p............p..v',
	'em.ahhc.lhhm.ac.lm.v',
	'em.vacv......vv.lm.v',
	'em.vbdv.ahhc.vv....v',
	'em.bhcv.vxxq.vv.ac.v',
	'v....vv.vxxt.vv.vv.v',
	'o.ac.bd.bhhr.bd.vv.o',
	'x.vv.....x......vv.x',
	'n.vv.ac.lhhm.lm.bd.n',
	'v.vv.bd......lm.lm.v',
	'v.bd....lhhm.lm.lm.v',
	'vp...lm......lm...pv',
	'bhhhhhhhhhhhhhhhhhhd'
	]
},
{
	name: "/dev/null",
	color: "none",
	maze: [
	'ahhhhhhhhhhhhhhhhhhc',
	'vxxxxxxxxxxxxxxxxxxv',
	'vxxxxxxxxxxxxx.xx.xv',
	'vxxxxxxxxxxxxx.xx.xv',
	'vx....x.ahhc.x.xx.xv',
	'vx.xx.x.vxxq.x.xx.xv',
	'vx.xx.x.vxxt.x.xx.xv',
	'vx.xx.x.bhhr.x.xx.xv',
	'vx.xx.x..x...x.xx.xv',
	'vxxxxxxxxxxxxxxxxxxv',
	'vxxxxxxxxxxxxxxxxxxv',
	'vxpxxxxxxxxxxxxxxpxv',
	'vxxxxxxxxxxxxxxxxxxv',
	'bhhhhhhhhhhhhhhhhhhd'
	]
}
];

var levels=standardLevels;

var tx= new Array();
var ty= new Array();

var t1= new Array();
t1[0]=0;
t1[1]= 9; // rd
t1[2]=10; // ld
t1[3]= 5; // ru
t1[4]= 6; // lu
t1[5]=13; // rdu
t1[6]=14; // ldu
t1[7]=11; // rld
t1[8]= 7; // rlu
t1[9]=15; // rlud
tx[0]=0;  ty[0]=0;
tx[1]=1;  ty[1]=0;  //r
tx[2]=-1; ty[2]=0;  //l
tx[4]=0;  ty[4]=-1; //u
tx[8]=0;  ty[8]=1;  //d


var t2 = new Array();
t2[0]= [0];
t2[1]= [1];
t2[2]= [2];
t2[4]= [4];
t2[8]= [8];
t2[3]= [1, 2];
t2[9]= [1, 8];
t2[10]=[2, 8];
t2[12]=[4, 8];
t2[5]= [1, 4];
t2[6]= [2, 4];
t2[7]= [1, 2, 4];
t2[11]=[1, 2, 8];
t2[13]=[1, 4, 8];
t2[14]=[2, 4, 8];
t2[15]=[1, 2, 4, 8];

var tdx=[2, 0, 1];
var tdy=[4, 0, 8];

var t3 = new Array();
t3[0]=0; t3[1]=2; t3[2]=1; t3[4]=8; t3[8]=4;

var nextNodes= new Array();
var pacNext=null;
var pacLast=null;

var gHomePos= new Array( [7,11], [7,10], [6,11], [6,10] );
var gHomeOffset= new Array( [0,1], [-2, 1], [0,-1], [-2,-1]);
var gbid= new Array(1,2,4,8);

var g= new Array();
for (var i=1; i<=4; i++) {
	g[i]=new Ghost();
	g[i].bid=gbid[i-1];
	g[i].bm=15^gbid[i-1];
}
var pac= new Pacman();
var bonus=new Array();
	for (var i=1; i<=4; i++) bonus[i]=0;
var movedir=0;
var runThru=false;
var gameOn= false;
var pill= false;
var pillCnt= 0;
var food= 0;
var nLevel= 0;
var nLife=0;
var gStep, gStep2;
var phaseSet=true;
var bonusLifeCnt=0;
var pacInitDir=0;
var ghostInitDir=0;
var pGstrat, pGsLookahead;
var pillPeriode, pillCnt;
var isPause=false;
var restartDialogOn=false;
var gameTimer=null;
var enterTime=0;

var aSpan= new Array();
var gSpan= new Array();
var gSpan2= new Array();
var pacStr= new Array();

pacStr[1]='pr'; pacStr[2]='pl';
pacStr[4]='pb'; pacStr[8]='pf';
pacStr[0]='pf';

var gameStatus=0;
var statusCnt=0;
var ghostCnt=0;

var ghostBonus=new Array(200,400,800,1600);

var isSpaceTile= {
	pnt:true, x:true, p:true, s:true
};

// colors

var colorDefs = {
	none: '#d3d3d3',
	gold: '#ecd4a4',
	white: '#ffffff',
	cyan: '#93dcdc',
	blueneon: '#79c1cd',
	darkneon: '#6bc6e4',
	greenneon: '#93dcac',
	emerald: '#95e995',
	grass: '#bfe390',
	red: '#ec5c81',
	pueblored: '#e9708f',
	desert: '#cbb383',
	reddesert: '#fa9fb1',
	cranberry: '#ed99bb',
	velvet: '#c880f9',
	ice: '#b3e5e5',
	forest: '#74b092',
	lemon: '#e5e5af',
	plum: '#d8a8f0',
	midnight: '#8f8fcb'
};

var randomcolors= [
	'gold',
	'blueneon',
	'greenneon',
	'darkneon',
	'cyan',
	'grass',
	'pueblored',
	'velvet',
	'ice',
	'lemon',
	'plum',
	'midnight'
];

var randomNamesByColor= {
	gold: [0,"The golden", "The amber"],
	blueneon: [0,"The forbidden", "The liquid", "The gleaming"],
	greenneon: [0,"The misty", "The oozy", "The Venutian"],
	darkneon: [0,"The electric", "The Mercurian", "The subterranean"],
	cyan: [1,"Inky's", "The mystic", "The doomed"],
	grass: [0,"The loomy", "The alien", "The ivy", "The jade"],
	pueblored: [2,"Blinky's", "Dracula's", "The bloody", "The Martian"],
	velvet: [0,"The velvet", "The glowing", "The secret"],
	ice: [0,"The icy", "The frozen", "The cristal"],
	lemon: [1,"Clyde's", "The glinting"],
	plum: [1,"Pinky's", "The haunted", "The forgotten"],
	midnight: [0,"The gloomy", "The hidden", "The dark"]
};

var randomNounsPers= ["revenge", "home", "cave", "place", "manor"];
var randomNounsSym= ["palace", "mansion", "abbey", "cave", "alley", "castle", "dungeon"];
var randomNounsAsym= ["passages", "grounds", "caverns", "alley", "dungeons", "hall"];
var randomNames={};
var lastRandomNoun='';

function getRandomColor() {
	return randomcolors[Math.floor(Math.random()*randomcolors.length)];
}

function getRandomName(clr, sym) {
	var n, n1,n2;
	var cn=randomNamesByColor[clr];
	n1=Math.floor(Math.random()*(cn.length-1))+1;
	for (var i=0; i<10; i++) {
		if ((cn[0]>0) && (n1<=cn[0])) {
			n2=randomNounsPers[Math.floor(Math.random()*randomNounsPers.length)];
		}
		else if (sym) {
			n2=randomNounsSym[Math.floor(Math.random()*randomNounsSym.length)];
		}
		else {
			n2=randomNounsAsym[Math.floor(Math.random()*randomNounsAsym.length)];
		}
		if (n2!=lastRandomNoun) break;
	}
	lastRandomNoun=n2;
	n=cn[n1]+' '+n2;
	if (randomNames[n]) {
		randomNames[n]++;
		n+=', Vol. '+randomNames[n];
	}
	else {
		randomNames[n]=1;
	}
	return n;
}


// constructors

function Ghost() {
	this.r= 0;
	this.c= 0;
	this.s= 0;
	this.d= 0;
	this.p= 0;
	this.z= 0;
	this.osx=0;
	this.osy=0;
	this.posx=0;
	this.posy=0;
	this.lastn=null;
	this.bid=0;
	this.bm=0;
	this.alpha=1;
	this.e=-1;
	this.lc='';
}

function Pacman() {
	this.r= 0;
	this.c= 0;
	this.p= 0;
	this.pn= 0;
	this.dir= pacInitDir;
	this.md= 0;
	this.dx= 0;
	this.dy= 0;
	this.osx=0;
	this.osy=0;
	this.posx=0;
	this.posy=0;
	this.reversed=false;
	this.lc='';
}


// basics

function setMazeColor(clr) {
	elements.maze.style.backgroundColor=colorDefs[clr];
}

function getElement(id) {
	if (document.getElementById) return document.getElementById(id);
	if (document.all) return doucument.all[id];
	return null;
}

function setVisibility(obj, v) {
	obj.style.visibility= (v)? 'visible' : 'hidden';
}

function setOpacity(obj,v) {
	if (typeof obj.style.opacity != 'undefined') {
		obj.style.opacity=v;
	}
	else if (typeof obj.style.filter != 'undefined') {
		if (!obj.style.filter) {
			if (v<1) obj.style.filter='progid:DXImageTransform.Microsoft.Alpha(Opacity='+Math.floor(v*100)+')';
		}
		else {
			if (v<1) {
				var f=obj.filters.item('DXImageTransform.Microsoft.Alpha');
				f.opacity = Math.floor(v*100);
				f.enabled = true;
			}
			else {
				obj.filters.item('DXImageTransform.Microsoft.Alpha').enabled = false;
			}
		}
	}
	else if (typeof obj.style.MozOpacity != 'undefined') {
		obj.style.MozOpacity=v;
	}
	else if (typeof obj.style.KhtmlOpacity != 'undefined') {
		obj.style.KhtmlOpacity=v;
	}
}

function setImage(id, img) {
	document.images[id].src=imgRef[img].src;
}

function setSpriteImg(obj, img) {
	obj.src=imgRef[img].src;
}

function moveElement(obj,x,y) {
	obj.style.left=x+'px';
	obj.style.top=y+'px';
}

function setMaze(r,c,s) {
	document.images['t'+r+'_'+c].src=imgRef[s].src;
}

// msie-pre-7 png fixes

function setSpriteImgMsieLe6(obj, img) {
	obj.filters.item(0).src=imgRef[img].src;
}

function setMazeMsieLe6(r,c,s) {
	document.images['t'+r+'_'+c].filters.item(0).src=imgRef[s].src;
}


// settings

function setQuality(v) {
	var s1=aStep;
	var s2=gStep;
	var s3=gStep2;
	aStep=settingsQuality[v];
	setSpan();
	pillMinLength=pillMaxLength*aStep-8*pillDecrement;
	pillPeriode=(nLevel<6)? pillMaxLength*aStep : Math.max(pillMinLength, pillMaxLength*aStep-(nLevel-5)*pillDecrement);
	bonusLength=10*aStep;
	setImage('neon_q'+settingsQualityValue, 'neon_lo');
	setImage('neon_q'+v, 'neon_hi');
	settingsQualityValue=v;
	var cf1=aStep/s1;
	var cf2=gStep/s2;
	var cf3=gStep2/s3;
	pac.osx=Math.round(pac.osx*cf1);
	pac.osy=Math.round(pac.osy*cf1);
	for (var i=1; i<=4; i++) {
		if (g[i].s==0) continue;
		if (pill && g[i].s==2) {
			g[i].osx=Math.round(g[i].osx*cf2);
			g[i].osy=Math.round(g[i].osy*cf2);
		}
		else if (g[i].s==3) {
			g[i].osx=Math.round(g[i].osx*cf3);
			g[i].osy=Math.round(g[i].osy*cf3);
		}
		else {
			g[i].osx=Math.round(g[i].osx*cf1);
			g[i].osy=Math.round(g[i].osy*cf1);
		}
	}
	aSpeed=settingsSpeed[settingsSpeedValue]*speedFactor[settingsQualityValue];
	gameDelay=Math.round(aSpeed/aStep);
}

function setSpeed(v) {
	aSpeed=settingsSpeed[v]*speedFactor[settingsQualityValue];
	setImage('neon_s'+settingsSpeedValue, 'neon_lo');
	setImage('neon_s'+v, 'neon_hi');
	settingsSpeedValue=v;
	gameDelay=Math.round(aSpeed/aStep);
}

// maze & game setup

function buildMaze(lvl) {
	var r, c, d, f1r, fwr, z, m;
	if (lvl.color) {
		setMazeColor(lvl.color);
		mazeColor=lvl.color;
	}
	else {
		setMazeColor('none');
		mazeColor='none';
	}
	if (lvl.name) {
		displayMazeName(lvl.name, false);
	}
	else {
		displayMazeName('', false);
	}
	m=lvl.maze;
	food=0;
	for (r=1; r<=14; r++) {
		fwr=fw[r];
		f1r=f1[r];
		for (c=1; c<=20; c++) {
			f1r[c]=0;
			z=m[r-1].charAt(c-1);
			if (z=='.') {
				setMaze(r,c,"pnt");
				f1r[c]=9;
				food++;
				fwr[c]=true;
			}
			else {
				setMaze(r,c,z);
				if ((z=='p') || (z=='s')) {
					f1r[c]=8;
					food++;
				}
				fwr[c]=isSpaceTile[z];
			}
		}
	}
	pacInitDir=0;
	if (fw[9][9]) pacInitDir|=2;
	if (fw[9][11]) pacInitDir|=1;
	if (fw[10][10]) pacInitDir|=8;
	ghostInitDir=0;
	if (fw[6][13]) ghostInitDir|=4;
	if (fw[8][13]) ghostInitDir|=8;
	if (fw[7][14]) ghostInitDir|=1;
	
	// calc directions
	f2=new Array();
	for (r=1; r<=14; r++) {
		f2[r]=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
	}
	for (r=2; r<14; r++) {
		for (c=2; c<20; c++) {
			if (fw[r][c] && (r<6 || r>7 || c<10 || c>11))  {
				d=0;
				if (fw[r-1][c]) d|=4;
				if (fw[r+1][c]) d|=8;
				if (fw[r][c-1]) d|=2;
				if (fw[r][c+1]) d|=1;
				if ((d==3) || (d==12)) d=0;
				f2[r][c]=d;
			}
		}
	}
}

function calcNextNodes() {
	var dirs=t2[15];
	var fm,fr,dx,dy,x,y;
	for (var m=0; m<dirs.length; m++) {
		var d=dirs[m];
		fd=nextNodes[d]=new Array();
		for (var r=1; r<=14; r++) {
			fr=fd[r]=new Array();
			for (var c=1; c<=20; c++) {
				if (f2[r][c] & d) {
					dx=tx[d];
					dy=ty[d];
					y=r;
					x=c;
					while (true) {
						if (y==1) {fr[c]=[13,c]; break;}
						if (y==14) {fr[c]=[2,c]; break;}
						if (x==1) {fr[c]=[r,19]; break;}
						if (x==20) {fr[c]=[r,2]; break;}
						x+=dx;
						y+=dy;
						if (f2[y][x]) {fr[c]=[y,x]; break;}
					}
				}
				else {
					fr[c]=null;
				}
			}
		}
	}
}


// general


function pHome() {
	pill=false; pillCnt=0;
	movedir=0;
	with (pac) {
		r=9; c=10;
		osx=0; osy=0;
		md=0; dir= pacInitDir;
		dx=0; dy=0;
		pn=1; p=1;
		reversed=false;
		pacLast=[r,c];
		pacNext=null;
		lc='';
	}
	setPac(pac.r,pac.c,"pr1");
	setOpacity(elements.pac, 1);
}

function gHome(i) {
	with (g[i]) {
		for (var k=0; k<gHomePos.length; k++) {
			if (tileFree(i,gHomePos[k][0],gHomePos[k][1])) {
				r=gHomePos[k][0];
				c=gHomePos[k][1];
				osx=gHomeOffset[k][0];
				osy=gHomeOffset[k][1];
				break
			}
		};
		s=0; p=1;
		z=0;
		d=0;
		e=-1;
		lc='';
		alpha=1;
		setGhost(i,r,c);
	}
	setOpacity(elements['gbody'+i], 1);
	setOpacity(elements['geyes'+i], 1);
	setVisibility(elements['g'+i], true);
}

function newGame() {
	if (gameTimer) clearTimeout(gameTimer);
	isPause=false;
	setRestartDialogVisible(false);
	setVisibility(elements.gameover, false);
	setVisibility(elements.gamepaused, false);
	nLife=3;
	setImage('life1','life_lo');
	setImage('life2','life_lo');
	setImage('life3','life_hi');
	setImage('life4','xx');
	setImage('life5','xx');
	nScore=0;
	nLevel=0;
	displayLevel();
	displayScore();
	bonusLifeCnt=0;
	startLevel();
	gameTimer=setTimeout('gameStep()', gameDelay);
}

function startLevel() {
	gameOn=false;
	setVisibility(elements.pac, false);
	for (i=1; i<=4; i++) {
		setVisibility(elements['g'+i], false);
		setVisibility(elements['bonus'+i], false);
		bonus[i]=0;
	}
	runThru=false;
	nLevel++;
	displayLevel();
	displayScore();
	gameStatus=0;
	statusCnt=0;
	/*
	pGstrat=Math.min(0.7, 0.2+nLevel/10);
	pGsLookahead=Math.min(0.5, 0.1+nLevel/10);
	*/
	pGstrat=Math.min(0.75, 0.25+nLevel/10);
	pGsLookahead=Math.min(0.6, 0.2+nLevel/10);
	pillPeriode=(nLevel<6)? pillMaxLength*aStep : Math.max(pillMinLength, pillMaxLength*aStep-(nLevel-5)*pillDecrement);
	switch (mazeMode) {
		case 'Standard Levels':
			levels=standardLevels;
			break;
		case 'Aztec Levels':
			levels=AztecLevels;
			break;
		case 'Mini-Pac Levels':
			levels=LegacyLevels;
			break;
		case 'Hacker Levels':
			levels=HackerLevels;
			break;
	}
	if ((mazeMode=='Random Levels') || (nLevel>levels.length)) buildRandomMaze()
	else buildMaze(levels[nLevel-1]);
	calcNextNodes();
	for (var r=1; r<=14; r++) {
		for (var c=1; c<=20; c++) {
			fft[r][c]=0;
		}
	}
	for (var i=1; i<=4; i++) gHome(i);
	pHome();
	pac.lfx=pac.lfy=0;
	setVisibility(elements.pac, true);
	phaseSet=true;
	gameOn=true;
}

function setSpan() {
	for (var n=1-aStep; n<aStep; n++) aSpan[n]= (n==0)? 0 : Math.round(n*27/aStep);
	gStep=Math.round(aStep*pillFactor);
	for (n=1-gStep; n<gStep; n++) gSpan[n]= (n==0)? 0 : Math.round(n*27/gStep);
	gStep2=Math.round(aStep*eyesFactor);
	for (n=1-gStep2; n<gStep2; n++) gSpan2[n]= (n==0)? 0 : Math.round(n*27/gStep2);
}

function limitOffsets(obj,limit) {
	if (obj.osx>=limit) obj.osx=limit-1;
	if (obj.osy>=limit) obj.osy=limit-1;
	if (obj.osx<=-limit) obj.osx=1-limit;
	if (obj.osy<=-limit) obj.osy=1-limit;
}

function getRand(x) {
	return Math.floor(Math.random() * x);
}

function trunkBy(v,m) {
	v%=m;
	return (v==0)? m:v;
}

function tileFree(n, r,c) {
	if (n) {
		return (fft[r][c]&g[n].bm)? false:true;
	}
	else {
		return (fft[r][c])? false:true;
	}
}

function isCollision(x1,y1,x2,y2, radius) {
	return (Math.abs(x1-x2)+Math.abs(y1-y2)<radius);
}

function addScore(n) {
	nScore+=n;
	bonusLifeCnt+=n;
	if (bonusLifeCnt>=bonusLifeScore) {
		bonusLife();
		bonusLifeCnt-=bonusLifeScore;
	}
	displayScore();
}

function displayLevel() {
	elements.leveldisplay.innerHTML=nLevel;
}

function displayScore() {
	elements.scoredisplay.innerHTML=nScore;
}

function displayMazeName(t, r) {
	if (t) {
		var s='Now playing: &raquo;'+t+'&laquo;';
		if (r) s+='<br><small>(random generated level)</small>';
		elements.mazedisplay.innerHTML=s;
	}
	else {
		elements.mazedisplay.innerHTML='';
	}
}


// main

function gameStep() {
	if (gameTimer) clearTimeout(gameTimer);
	var now=new Date();
	enterTime=now.getTime();
	if (gameOn) {
		phaseSet=(!phaseSet);
		switch(gameStatus) {
			case 0: doMove(); break;
			case 1: resetGhost(); break;
			case 2: resetPac(); return;
			case 3: endLevel(); break;
			case 4: startLevel(); break;
		}
	}
	if (gameOn || isPause) gameTimer=setTimeout('reLoop()', 1);
}

function reLoop() {
	if (gameTimer) clearTimeout(gameTimer);
	var now=new Date();
	var delay = gameDelay + enterTime - now.getTime();
	gameTimer=setTimeout('gameStep()', (delay>0)? delay:1);
}

function doMove() {
	for (var i=1; i<=4; i++) {
		if (bonus[i]>0) {
			bonus[i]--;
			if (bonus[i]==0) {
				setVisibility(elements['bonus'+i], false);
			}
			else {
				setOpacity(elements['bonus'+i], bonus[i]/bonusLength);
			}
		}
	}
	with (pac) {
		if (osx+osy==0) {
			pMove();
		}
		else if (useInstantTurns && reversed==false && movedir && movedir==t3[md] && f1[r][c]==0) {
			if (dx) dx=-dx;
			if (dy) dy=-dy;
			md=t3[md];
			movedir=0;
			reversed=true;
			var temp=pacNext;
			pacNext=pacLast;
			pacLast=temp;
			touchX=window.touchLastX;
			touchY=window.touchLastY;
		}
		if (runThru) {
			osx+=dx;
			osy+=dy;
			if ((c+dx)%21==0) osx=0;
			if ((r+dy)%15==0) osy=0;
			osx%=aStep;
			osy%=aStep;
			if (reversed==false) {
				if (osx==0) c=trunkBy(c+dx,20);
				if (osy==0) r=trunkBy(r+dy,14);
			};
			if (phaseSet) {
				p+=pn;
				if (p==3) pn=-1;
				if (p==1) pn=1;
			};
			if (md==4) setPac(r,c,'pb1')
			else setPac(r,c,pacStr[md]+p);
			if (osx+osy==0 && f2[r][c]) dir=f2[r][c];
		}
	}
	var r=pac.r+Math.round(pac.osy/aStep*.75);
	var c=pac.c+Math.round(pac.osx/aStep*.75);
	if (f1[r][c]>=8) {
		setMaze(r,c,'x');
		if (f1[r][c]==8) {
			if (!pill) {
				pill=true;
				for (var i=1; i<=4; i++) translateGPos(i,false);
			};
			pillCnt=pillPeriode;
			addScore(40);
			ghostCnt=0;
			if (useSound) playSound('pillbeam');
		}
		else {
			addScore(15);
			if (useSound) playSound('doteat');
		}
		f1[r][c]=0;
		food--;
		if (food==0) {
			statusCnt=blinkSteps*blinkRate;
			gameStatus=3;
			g[1].alpha=.75;
			for (i=1; i<=4; i++) {
				setOpacity(elements['gbody'+i], .75);
				setOpacity(elements['geyes'+i], .75);
			}
			if (useSound) playSound('endlevel');
			return;
		}
	}
	gmove();
	checkHits();
	if (gameStatus>0) return;
	if (pill) {
		pillCnt--;
		if (pillCnt==0) {
			pill=false;
			for (i=1; i<=4; i++) {
				var gi=g[i];
				if (gi.s!=4) {
					translateGPos(i,true);
				}
			}
		}
	}
}


// pacman

function setPac(r,c,s) {
	pac.posx=27*(c-1)+aSpan[pac.osx];
	pac.posy=27*(r-1)+aSpan[pac.osy];
	if (s!=pac.lc) {
		setSpriteImg(elements.pacimg, s);
		pac.lc=s;
	}
	moveElement(elements.pac,pac.posx, pac.posy);
}

function pMove() {
	with (pac) {
		if (dir&movedir) {
			md= dir&movedir;
			touchX=window.touchLastX;
			touchY=window.touchLastY;
		}
		else {
			md&=dir;
			if (md==0) runThru=false;
		}
		if (md) {
			runThru=true;
			dx=(md&3);
			dy=(md&12);
			if (dx) {
				dir=3;
				dx=tx[dx];
			}
			if (dy) {
				dir=12;
				dy=ty[dy];
			}
		}
		reversed=false;
		if (f2[r][c]) {
			setPacNextNode();
			pacLast=[r,c];
		}
	}
}

function setPacNextNode() {
	with (pac) {
		if (md) {
			var n=nextNodes[md][r][c];
			if (n) {
				var d=f2[n[0]][n[1]] & (15^t3[md]);
				while (d && t2[d].length<2) {
					var nn=nextNodes[d][n[0]][n[1]];
					if (nn) {
						n=nn;
						d=f2[n[0]][n[1]] & (15^t3[d]);
					}
					else {
						break;
					}
				}
				pacNext=n;
				return;
			}
		}
		pacNext=null;
	}
}


// ghosts

function setGhost(i,r,c) {
	var eyes;
	var gi=g[i];
	var s=gi.s;
	var gc;
	if (pill && (s==2 || s==1)) {
		gi.posx=27*(c-1)+gSpan[gi.osx];
		gi.posy=27*(r-1)+gSpan[gi.osy];
		if (pillCnt>4*aStep) {
			gc='ga';
		}
		else {
			gc=(pillCnt%6<3)? 'gn':'ga';
		}
	}
	else if (s==0) {
		gi.posx=27*(c-1)+gi.osx;
		gi.posy=27*(r-1)+gi.osy;
		gc='g'+i;
	}
	else if (s==3) {
		gi.posx=27*(c-1)+gSpan2[gi.osx];
		gi.posy=27*(r-1)+gSpan2[gi.osy];
		gc='g'+i;
	}
	else {
		gi.posx=27*(c-1)+aSpan[gi.osx];
		gi.posy=27*(r-1)+aSpan[gi.osy];
		gc='g'+i;
	}
	if (s==3) {
		gc='ge'
	}
	else {
		gc+=gi.p;
	}
	if (s<3) fft[gi.r][gi.c]|=gi.bid;
	//if (gc!=gi.lc) {
		setSpriteImg(elements['imgg'+i], gc);
	//	gi.lc=gc;
	//}
	if (gi.d!=gi.e) {
		setSpriteImg(elements['imgge'+i], 'e'+gi.d);
		gi.e=gi.d;
	}
	moveElement(elements['g'+i], gi.posx, gi.posy);
}

function gmove() {
	for (i=1; i<=4; i++) {
		if (gameOn) {
			with (g[i]) {
				fft[r][c]&=bm;
				if (phaseSet) {
					p++;
					if (p==4) p=1;
				}
				if (s==2) gMove2(i)
				else if (s==3) gMove3(i)
				else if (s==1) gMove1(i)
				else if (s==4) gMove4(i)
				else gMove0(i);
			}
		}
		if (g[i].s!=4) displayGhost(i)
	}
}

function displayGhost(i) {
	with (g[i]) {
		setGhost(i,r,c);
	}
}

function gMoveEyes(i) {
	if (Math.random()<.01) {
		g[i].d=(Math.random()<.5)? 0 : t2[15][getRand(t2[15].length)];
	}
}

function gMove0(i) {
	with (g[i]) {
		if (tileFree(i,7,12) && pac.dx+pac.dy!=0) {
			if ((z>40) || (getRand(30)<1)) {
				s++;
				r=7; c=12;
				osx=osy=0;
				d=2;
			}
			else {
				gMoveEyes(i);
				z++;
			}
		}
		else gMoveEyes(i);
	}
}

function gMove1(i) {
	with (g[i]) {
		if (tileFree(i,7,13)) {
			if (osx==0) osx++
			else {
				osx++;
				if (osx==aStep) {
					c=13;
					osx=0;
					d=t2[ghostInitDir][getRand(t2[ghostInitDir].length)];
					s++;
				}
			}
		}
	}
}

function gMove2(i) {
	with (g[i]) {
		if (osx+osy==0) {
			var x= f2[r][c];
			if (x>0) {
				var gdm= x&(15^t3[d]);
				if (Math.random()<pGstrat) gmoveStrat(i,gdm)
				else gmoveRand(i,gdm);
			}
			if (d>0) {
				if (tileFree(i,trunkBy(r+ty[d],14),trunkBy(c+tx[d],20))) gSet(i)
				else reverseStuck(i,x);
			}
		}
		else gSet(i);
	}
}

function gMove3(i) {
	with (g[i]) {
		if (osx+osy==0) {
			if (r==7 && c==13) {
				setVisibility(elements['g'+i], false);
				gHome(i);
				return;
			}
			var x= f2[r][c];
			if (x>0) {
				var gdm= x&(15^t3[d]);
				var node='r'+r+'c'+c;
				if (lastn[node] && t2[gdm].length>1 && Math.random()>.3) gdm &= 15^lastn[node];
				var desx=13-g[i].c;
				var desy=7-g[i].r;
				d=0;
				if (desx!=0) d= (desx>0)? 1:2;
				if (desy!=0) d|= (desy>0)? 8:4;
				d&=gdm;
				if (d>0) gmoveRand(i,d)
				else {
					var adx=(desx>0)? gdm&(15^2):gdm&(15^1);
					var ady=(desy>0)? gdm&(15^4):gdm&(15^1);
					if (adx) gmoveRand(i,adx)
					else if (ady) gmoveRand(i,ady)
					else gmoveRand(i,gdm);
				}
				lastn[node]=d;
			}
		}
		gSet(i);
	}
}

function gMove4(i) {
	var gi=g[i];
	gi.z--;
	if (gi.z==0) {
		gi.s=3;
		var cf=gStep2/gStep;
		gi.osx=Math.round(gi.osx*cf);
		gi.osy=Math.round(gi.osy*cf);
		gi.lastn=new Array();
		setGhost(i,gi.r,gi.c);
		setOpacity(elements['gbody'+i],1);
		setOpacity(elements['geyes'+i],1);
	}
	else if (gi.z<ghostResetDelay/2) {
		gi.alpha-=ghostResetDelta*2;
		setOpacity(elements['gbody'+i], gi.alpha);
	}
	else {
		gi.alpha-=ghostResetDelta;
		setOpacity(elements['gbody'+i], gi.alpha);
	}
}

function gSet(i) {
	with (g[i]) {
		osy+=ty[d];
		osx+=tx[d];
		if (s==3) {
			osx%=gStep2;
			osy%=gStep2;
		}
		else if (pill && s==2) {
			osx%=gStep;
			osy%=gStep;
		}
		else {
			osx%=aStep;
			osy%=aStep;
		}
		if ((c==1 && tx[d]==-1) || (c==20 && tx[d]==1)) osx=0;
		if ((r==1 && ty[d]==-1) || (r==14 && ty[d]==1)) osy=0;
		if (osx==0) c=trunkBy(c+tx[d],20);
		if (osy==0) r=trunkBy(r+ty[d],14);
	}
}

function gmoveRand(i,k) {
	g[i].d=t2[k][getRand(t2[k].length)];
}

function gmoveStrat(i,gdm) {
	var desx, desy, px, py;
	var gi=g[i];
	if (pill) {
		desy=gi.r-pac.r;
		desx=gi.c-pac.c;
	}
	else if (pacNext && Math.random()<pGsLookahead) {
		py=pacNext[0];
		px=pacNext[1];
		desy=py-gi.r;
		desx=px-gi.c;
		if (desx==0 && desy==0) {
			desy=pac.r-gi.r;
			desx=pac.c-gi.c;
		}
	}
	else {
		desy=pac.r-gi.r;
		desx=pac.c-gi.c;
	}
	gi.d=0;
	if (desx!=0) gi.d= (desx>0)? 1:2;
	if (desy!=0) gi.d|= (desy>0)? 8:4;
	gi.d&=gdm;
	if (gi.d>0) gmoveRand(i,gi.d)
	else gmoveRand(i,gdm);
}

function reverseStuck(i,x) {
	with (g[i]) {
		if (x==0) {
			if (tileFree(i,trunkBy(r-ty[d],14),trunkBy(c-tx[d],20))) d=t3[d];
		}
		else {
			var vd= t2[x][getRand(t2[x].length)];
			if (tileFree(i,r+ty[vd],c+tx[vd])) {
				d=vd;
				gSet(i);
			}
		}
	}
}

function translateGPos(i,reset) {
	with (g[i]) {
		if (s==3) return;
		if ((reset) && (s==2)) {
			osx= Math.round(osx/pillFactor-tx[d]*.1);
			osy= Math.round(osy/pillFactor-ty[d]*.1);
			limitOffsets(g[i], aStep);
		}
		else if (s==2) {
			osx= Math.round(osx*pillFactor+tx[d]*.1);
			osy= Math.round(osy*pillFactor+ty[d]*.1);
			limitOffsets(g[i], gStep);
		}
	}
}

// hit-test

function checkHits() {
	var crash=0;
	for (i=1; i<=4; i++) {
		if (g[i].s==2 && isCollision(g[i].posx,g[i].posy, pac.posx,pac.posy, 26)) {
			crash=i;
			if (pill) {
				bonus[i]=bonusLength;
				var b=elements['bonus'+i];
				moveElement(b, g[i].posx, g[i].posy);
				setSpriteImg(elements['imgbonus'+i], 'bonus'+ghostCnt);
				setOpacity(b, 1);
				setVisibility(b, true);
				addScore(ghostBonus[ghostCnt]);
				if (ghostCnt<3) ghostCnt++;
				var gi=g[i];
				translateGPos(i,true);
				setSpriteImg(elements['imgg'+i], 'gx');
				setOpacity(elements['gbody'+i], ghostResetAlpha);
				setOpacity(elements['geyes'+i], ghostResetAlpha);
				gi.alpha=ghostResetAlpha;
				gi.z=ghostResetDelay;
				gi.s=4;
				fft[gi.r][gi.c]&=gi.bm;
			}
			else {
				setOpacity(elements['gbody'+i], .5);
			}
		}
	}
	if (crash>0) {
		if (pill) {
			gameStatus=1;
			statusCnt=2;
			if (useSound) playSound('ghostcatch');
		}
		else {
			setSpriteImg(elements.pacimg, 'px1');
			pac.p=1;
			gameStatus=2;
			if (useSound) playSound('pacend');
		}
	}
}

function resetGhost() {
	statusCnt--;
	if (statusCnt==0) gameStatus=0;
}

function resetPac() {
	if (gameTimer) clearTimeout(gameTimer);
	if (pac.p<7) {
		pac.p++;
		setSpriteImg(elements.pacimg, 'px'+pac.p);
		setOpacity(elements.pac, 1-pac.p/18);
		gameTimer=setTimeout('resetPac()', 174-pac.p*2);
	}
	else {
		if (nLife<6) setImage('life'+nLife,'xx');
		nLife--;
		if (nLife>0) {
			for (var i=1; i<=4; i++) {
				var gi=g[i];
				fft[gi.r][gi.c]&=gi.bm;
				gHome(i);
			}
			if (nLife<6) setImage('life'+nLife,'life_hi');
			pHome();
			gameStatus=0;
			gameTimer=setTimeout('gameStep()', 100);
		}
		else gameOver();
	}
}

function bonusLife() {
	if (nLife<6) {
		setImage('life'+nLife,'life_lo');
		nLife++;
		if (nLife<6) setImage('life'+nLife,'life_hi');
		if (useSound) playSound('bonuslife');
	}
}

function gameOver() {
	gameOn=false;
	gameStatus=-1;
	setOpacity(elements.gameover, .1);
	setVisibility(elements.gameover, true);
	setVisibility(elements.pac, false);
	setVisibility(elements.gamepaused, false);
	for (var i=1; i<=4; i++) {
		setOpacity(elements['gbody'+i], .6);
		setOpacity(elements['geyes'+i], .7);
	}
	statusCnt=.1
	gameTimer=setTimeout('showGameOver()', 8);
}

function showGameOver() {
	if (statusCnt<1) {
		statusCnt+=.01;
		setOpacity(elements.gameover, statusCnt);
		gameTimer=setTimeout('showGameOver()', 8);
	}
}

function endLevel() {
	var a=g[1].alpha-=.015;
	for (var i=1; i<=4; i++) {
		setOpacity(elements['gbody'+i], a);
	}
	if (statusCnt%blinkRate==0) {
		if ((statusCnt/blinkRate)%2==blinkSteps%2) {
			setMazeColor((mazeColor!='white')? 'white':'none');
		}
		else {
			setMazeColor(mazeColor);
		}
	}
	statusCnt--;
	if (statusCnt==0) {
		setMazeColor(mazeColor);
		gameStatus=4;
	}
}

function doPause() {
	if (gameStatus>=0) {
		if (gameOn) {
			setVisibility(elements.gamepaused, true);
			gameOn=false;
			isPause=true;
		}
		else {
			setVisibility(elements.gamepaused, false);
			gameOn=true;
			isPause=false;
		}
	}
}

function showRestartDialog() {
	if (gameTimer) clearTimeout(gameTimer);
	setRestartDialogVisible(true);
}

function hideRestartDialog(ok) {
	setRestartDialogVisible(false);
	if (ok) {
		setTimeout('newGame()', 10);
	}
	else {
		setTimeout('reLoop()', 10);
	}
}

function setRestartDialogVisible(v) {
	setVisibility(elements.restartdialog, v);
	setVisibility(elements.dialogscreen, v);
	elements.dialogscreen.display= (v)? 'block':'none';
	if (v) setOpacity(elements.dialogscreenimg, .25);
	restartDialogOn=v;
}


// random maze

var rf= new Array();
for (var r=0; r<=15; r++) {
	rf[r]=new Array();
}

var rCageFrame= new Array(
	[5,9], [5,10], [5,11], [5,12],
	[6,9], [6,12], [7,9], [7,12],
	[8,9], [8,10], [8,11], [8,12]
);
var rStandardFree= new Array([9,9], [7,13], [8,13], [9,11], [9,12], [9,13]);
var rForceUpperGateFree= false;

var rOrdC=[2,19,3,18,4,17,5,16,6,15,7,14,8,13,9,12,10,11];
var rOrdR=[2,13,3,12,4,11,5,10,6,9,7,8];

var rSymLine= [2,3,4,10,11,12];

var tileRef=new Array(
	'x', 'l', 'm', 'h', 'o',
	'b', 'd', 'f', 'n', 'a',
	'c', 'i', 'v', 'e', 'g',
	'y', 'k', 'q', 't', 'r',
	'z', 'u'
);

function buildRandomMaze() {
	var edgelines=false;
	var sym=(Math.random()>.5);
	var i, r, c, x, y;
	rMazeSetUp();
	var n=getRand(28)+18;
	// edge lines
	if (Math.random()>.5) {
		var k=getRand(10)+2;
		if (sym && k>6) k=6;
		for (i=2; i<k; i++) {
			var z=getRand(4);
			if (z==0) {
				rEdgeLine(2, (sym)? getRand(3)+13:getRand(12)+4, 8);
			}
			else if (z==1) {
				rEdgeLine(13, (sym)? getRand(3)+13:getRand(12)+4, 4);
			}
			else if ((sym) || (z==2)) {
				rEdgeLine(getRand(6)+4,19,2);
			}
			else if (z==3) {
				rEdgeLine(getRand(6)+4,2,1);
			}
		}
		edgelines=true;
		n-=k;
	}
	// inner lines
	if (sym) {
		for (i=0; i<n; i+=2) rLine(getRand(10)+3, getRand(9)+10, (Math.random()>.5)? 1:8);
		for (i=0; i<rSymLine.length; i++) {
			r=rSymLine[i];
			rf[r][10]=-1;
			if (rf[r+1][10]>=0) rf[r+1][10]&=14;
		}
		for (r=2; r<14; r++) {
			for (c=2; c<11; c++) {
				if (rf[r][21-c]>=0) rSet(r,c);
			}
		}
	}
	else {
		for (i=0; i<n; i++) rLine(getRand(10)+3, getRand(16)+3, (Math.random()>.5)? 1:8);
	}
	if (edgelines) {
		for (i=2; i<4; i++) {
			r=rOrdR[i];
			for (c=3; c<19; c++) rFillBag(r,c);
		}
		for (r=3; r<13; r++) {
			for (i=2; i<4; i++) rFillBag(r,rOrdC[i]);
		}
	}
	// fill grid
	rGrow(2);
	if (sym) {
		for (r=2; r<14; r++) {
			for (c=12; c<21; c++) {
				if ((r>5) && (r<9) && (c<13)) continue;
				var sc=21-c;
				if (rf[r][sc]>=0) rClear(r,sc);
				if (rf[r][c]>=0) rSet(r,sc);
			}
		}
	}
	rGrow(0);
	
	// teleports
	var m=getRand(2)+1;
	for (i=0; i<m; i++) {
		x=(sym)? getRand(8)+2 : getRand(18)+2;
		if (rf[2][x]<0 && rf[13][x]<0 && rf[1][x-1]>=0 && rf[1][x+1]>=0) {
			if (sym) {
				var x2=21-x;
				if (rf[2][x2]>=0 || rf[13][x2]>=0 || rf[1][x2-1]<0 || rf[1][x2+1]<0) return;
				rf[1][x2]=-3;
				rf[14][x2]=-3;
				rf[1][x2-1]&=14; rf[1][x2-1]|=4;
				rf[1][x2+1]&=13; rf[1][x2+1]|=4;
				rf[14][x2-1]&=14; rf[14][x2-1]|=8;
				rf[14][x2+1]&=13; rf[14][x2+1]|=8;
			}
			rf[1][x]=-3;
			rf[14][x]=-3;
			rf[1][x-1]&=14; rf[1][x-1]|=4;
			rf[1][x+1]&=13; rf[1][x+1]|=4;
			rf[14][x-1]&=14; rf[14][x-1]|=8;
			rf[14][x+1]&=13; rf[14][x+1]|=8;
			if (sym) break;
		}
	}
	m=getRand(3)+1;
	for (i=0; i<m; i++) {
		x=getRand(12)+2;
		if ((rf[x][2]<0) && (rf[x][19]<0) && (rf[x-1][1]>=0) && (rf[x+1][1]>=0)) {
			rf[x][1]=-3;
			rf[x][20]=-3;
			rf[x-1][1]&=7; rf[x-1][1]|=2;
			rf[x+1][1]&=11; rf[x+1][1]|=2;
			rf[x-1][20]&=7; rf[x-1][20]|=1;
			rf[x+1][20]&=11; rf[x+1][20]|=1;
		}
	}
	
	rClearWalls(sym);
	// parse grid & set tiles
	rParseGrid();
	// pill placement
	if (sym) {
		rSetPill(2,2,true);
		rSetPill(13,2,true);
	}
	else {
		rSetPill(2,2,false);
		rSetPill(2,19,false);
		rSetPill(13,2,false);
		rSetPill(13,19,false);
	}
	if (nLevel<12) {
		x=getRand(12)+4;
		y=getRand(6)+4;
		if (rf[y][x]<0 && f1[y][x]==9) {
			f1[y][x]=8;
			setMaze(y,x,'p');
			fw[y][x]=true;
		}
	}
	//pacHome 9/10
	pacInitDir=0;
	if (rf[9][11]<0) pacInitDir|=1;
	if (rf[9][9]<0) pacInitDir|=2;
	if (rf[10][10]<0) pacInitDir|=8;
	//gStart 7/13
	ghostInitDir=0;
	if (rf[7][14]<0) ghostInitDir|=1;
	if (rf[6][13]<0) ghostInitDir|=4;
	if (rf[8][13]<0) ghostInitDir|=8;
	var rclr;
	for (var i=0; i<100; i++) {
		rclr=getRandomColor();
		if (rclr!=mazeColor) break;
	}
	displayMazeName(getRandomName(rclr, sym), true);
	setMazeColor(rclr);
	mazeColor=rclr;
}

function rMazeSetUp() {
	var r, c, i;
	for (r=0; r<=15; r++) {
		for (c=0; c<=21; c++) rf[r][c]=-1;
	}
	for (c=1; c<21; c++) { rSet(1,c); rSet(14,c) }
	for (r=2; r<14; r++) { rSet(r,1); rSet(r,20) }
	for (i=0; i<gHomePos.length; i++) {
		var p=gHomePos[i];
		rf[p[0]][p[1]]=-3;
	}
	for (i=0; i<rCageFrame.length; i++) {
		rSet(rCageFrame[i][0],rCageFrame[i][1]);
	}
	for (i=0; i<rStandardFree.length; i++) {
		rf[rStandardFree[i][0]][rStandardFree[i][1]]=-2;
	}
	if (rForceUpperGateFree || Math.random()<.3) rf[6][13]=-2;
	rf[9][10]=-3;
}

function rSet(r,c) {
	var d=0;
	if (rf[r+1][c]>=0) { rf[r+1][c]|=4; d|=8 }
	if (rf[r-1][c]>=0) { rf[r-1][c]|=8; d|=4 }
	if (rf[r][c+1]>=0) { rf[r][c+1]|=2; d|=1 }
	if (rf[r][c-1]>=0) { rf[r][c-1]|=1; d|=2 }
	rf[r][c]=d;
}

function rClear(r,c) {
	if (rf[r+1][c]>=0) rf[r+1][c]&=255^4;
	if (rf[r-1][c]>=0) rf[r-1][c]&=255^8;
	if (rf[r][c+1]>=0) rf[r][c+1]&=255^2;
	if (rf[r][c-1]>=0) rf[r][c-1]&=255^1;
	rf[r][c]=-1;
}

function rLine(r,c,d) {
	if (rf[r][c]!=-1) return;
	if (rf[r+1][c]>=0 || rf[r-1][c]>=0 || rf[r][c+1]>=0 || rf[r][c-1]>=0) return;
	if (rf[r+1][c+1]>=0 || rf[r+1][c-1]>=0 || rf[r-1][c+1]>=0 || rf[r-1][c-1]>=0) return;
	rSet(r,c);
	rLineDraw(r,c,d);
}

function rEdgeLine(r,c,d) {
	if (rf[r][c]!=-1) return;
	if (d&3) {
		if (rf[r-1][c]>=0 || rf[r-2][c]>=0 || rf[r+1][c]>=0 || rf[r+2][c]>=0 || rf[r+3][c]>=0 || rf[r-3][c]>=0) return;
		if (d==1 && (rf[r][c+1]>=0 || rf[r+1][c+1]>=0 || rf[r-1][c+1]>=0)) return;
		if (d==2 && (rf[r][-1]>=0 || rf[r+1][c-1]>=0 || rf[r-1][c-1]>=0)) return;
	}
	else {
		if (rf[r][c+1]>=0 || rf[r][c+2]>=0 || rf[r][c-1]>=0 || rf[r][c-2]>=0 || rf[r][c+3]>=0 || rf[r][c-3]>=0) return;
		if (d==8 && (rf[r+1][c]>=0 || rf[r+1][c-1]>=0 || rf[r+1][c+1]>=0)) return;
		if (d==4 && (rf[r-1][c]>=0 || rf[r-1][c-1]>=0 || rf[r-1][c+1]>=0)) return;
	}
	rSet(r,c);
	rLineDraw(r,c,d, true);
}

function rLineDraw(r,c,d, isEdge) {
	var l=(d&3)? getRand(12-r)+1 : getRand(18-r)+1;
	if (isEdge  && (d&12) && l>6) l==6;
	for (var k=0; k<l; k++) {
		r+=ty[d];
		c+=tx[d];
		if (rf[r][c]!=-1) return;
		if ((d&3) && (rf[r+1][c]>=0 || rf[r-1][c]>=0)) return;
		if ((d&12) && (rf[r][c+1]>=0 || rf[r][c-1]>=0)) return;
		var r2=r+ty[d];
		var c2=c+tx[d];
		if (rf[r2][c2]>=0) return;
		if ((d&3) && (rf[r2+1][c2]>=0 || rf[r2-1][c2]>=0)) return;
		if ((d&12) && (rf[r2][c2+1]>=0 || rf[r2][c2-1]>=0)) return;
		rSet(r,c);
	}
}

function rGrow(ofs) {
	for (var ri=ofs; ri<rOrdR.length; ri++) {
		var r=rOrdR[ri];
		for (var ci=ofs; ci<rOrdC.length; ci++) {
			var c=rOrdC[ci];
			var x=rf[r][c];
			if ((x>=0) && (x<15)) {
				if (c<11) {
					if ((x&1)==0) rGrowLine(r,c,1);
					if ((x&2)==0) rGrowLine(r,c,2);
				}
				else {
					if ((x&2)==0) rGrowLine(r,c,2);
					if ((x&1)==0) rGrowLine(r,c,1);
				}
				if (r<8) {
					if ((x&4)==0) rGrowLine(r,c,4);
					if ((x&8)==0) rGrowLine(r,c,8);
				}
				else {
					if ((x&4)==0) rGrowLine(r,c,4);
					if ((x&8)==0) rGrowLine(r,c,8);
				}
			}
		}
	}
}

function rGrowLine(r,c,d) {
	var dx=tx[d];
	var dy=ty[d];
	var dr=t3[d];
	r+=dy;
	c+=dx;
	while (rf[r][c]==-1 && rf[r+dy][c+dx]<0) {
		if (d&3) {
			if (rf[r+1][c]>=0 && (rf[r+1][c]&dr)==0) return;
			if (rf[r-1][c]>=0 && (rf[r-1][c]&dr)==0) return;
			var c1=c+dx;
			if (rf[r+1][c1]>=0 && (rf[r+1][c1]&dr)==0) return;
			if (rf[r-1][c1]>=0 && (rf[r-1][c1]&dr)==0) return;
		}
		else {
			if (rf[r][c+1]>=0 && (rf[r][c+1]&dr)==0) return;
			if (rf[r][c-1]>=0 && (rf[r][c-1]&dr)==0) return;
			var r1=r+dy;
			if (rf[r1][c+1]>=0 && (rf[r1][c+1]&dr)==0) return;
			if (rf[r1][c-1]>=0 && (rf[r1][c-1]&dr)==0) return;
		}
		rSet(r,c);
		r+=dy;
		c+=dx;
	}
}

function rFillBag(r,c) {
	if (rf[r][c]==-1 && rf[r-1][c-1]<0 && rf[r-1][c]<0 && rf[r-1][c+1]<0 && rf[r][c-1]<0 && rf[r][c+1]<0 && rf[r+1][c-1]<0 && rf[r+1][c]<0 && rf[r+1][c+1]<0) {
		rSet(r,c);
		if (Math.round()>.7) return;
		var r1=r;
		var c1=c+1;
		while (rf[r1][c1]==-1 && c1<18) {
			if (rf[r1][c1+1]>=0 || rf[r1-1][c1+1]>=0 || rf[r1+1][c1+1]>=0) break;
			rSet(r1,c1);
			c1++;
		}
		r1=r+1;
		c1=c;
		while (rf[r1][c1]==-1 && r1<12) {
			if (rf[r1+1][c1]>=0 || rf[r1+1][c1-1]>=0 || rf[r1+1][c1+1]>=0) break;
			rSet(r1,c1);
			r1++;
		}
	}
}

function rSetPill(r,c,sym) {
	if (rf[r][c]==-1 && rf[r][1]>0 && rf[1][c]>0) {
		f1[r][c]=8;
		setMaze(r,c,'p');
		fw[r][c]=true;
		var sc=21-c;
		if (sym && rf[r][sc]==-1) {
			f1[r][sc]=8;
			setMaze(r,sc,'p');
			fw[r][sc]=true;
		}
	}
	else {
		var dx=(c==2)? +1:-1;
		var dy=(r==2)? +1:-1;
		for (var i=0; i<50; i++) {
			var x=c+getRand(4)*dx;
			var y=r+getRand(4)*dy;
			if (rf[y][x]<0) {
				f1[y][x]=8;
				setMaze(y,x,'p');
				fw[y][x]=true;
				var sx=21-x;
				if (sym && rf[y][sx]==-1) {
					f1[y][sx]=8;
					setMaze(y,sx,'p');
					fw[y][sx]=true;
				}
				break;
			}
		}
	}
}

function rClearWalls(sym) {
	var r, c;
	for (c=3; c<18; c++) {
		var passages=0;
		var lpr=0;
		for (var r=2; r<=13; r++) {
			if (rf[r][c]<=0) {
				lpr=r;
				if (++passages==2) break;
			}
		}
		if (passages<2) {
			var posPassages=new Array();
			for (r=2; r<=13; r++) {
				if (Math.abs(lpr-r)>6 && rf[r][c]>0 && rf[r][c-1]<=0 && rf[r][c+1]<=0 && rf[r-1][c]>0 && rf[r+1][c]>0) posPassages[posPassages.length]=r;
			}
			if (posPassages.length==0) {
				for (r=2; r<=13; r++) {
					if (rf[r][c]>0 && rf[r][c-1]<=0 && rf[r][c+1]<=0 && rf[r-1][c]>0 && rf[r+1][c]>0) posPassages[posPassages.length]=r;
				}
			}
			if (posPassages.length) {
				var pr=posPassages[Math.floor(Math.random()*posPassages.length)];
				rClear(pr,c);
				if ((sym) && (c<11)) {
					var sc=21-c;
					if (rf[pr][sc-1]<=0 && rf[pr][sc+1]<=0 && rf[pr-1][sc]>0 && rf[pr+1][sc]>0) rClear(pr,sc);
				}
			}
		}
	}
}

function rParseGrid() {
	food=0;
	for (var r=1; r<=14; r++) {
		for (var c=1; c<=20; c++) {
			f1[r][c]=0;
			var v=rf[r][c];
			if (v<-2) {
				setMaze(r,c,'x');
				fw[r][c]=true;
			}
			else if (v<0) {
				setMaze(r,c,"pnt")
				f1[r][c]=9;
				fw[r][c]=true;
				food++;
			}
			else if (v==0) {
				setMaze(r,c,'z');
				fw[r][c]=false;
			}
			else if (v>0) {
				if ((v&4) && r>1) {
					if (rf[r-1][c]>0 && (rf[r-1][c]&8) && c>1 && rf[r][c-1]>0 && (rf[r][c-1]&4) && c<20 && rf[r][c+1]>0 && (rf[r][c+1]&4)) v&=11;
				}
				if ((v&8) && r<14) {
					if (rf[r+1][c]>0 && (rf[r+1][c]&4) && c>1 && rf[r][c-1]>0 && (rf[r][c-1]&8) && c<20 && rf[r][c+1]>0 && (rf[r][c+1]&8)) v&=7;
				}
				if ((v&1) && c<20) {
					if (rf[r][c+1]>0 && (rf[r][c+1]&2) && r>1 && rf[r-1][c]>0 && (rf[r-1][c]&1) && r<14 && rf[r+1][c]>0 && (rf[r+1][c]&1)) v&=14;
				}
				if ((v&2) && c>1) {
					if (rf[r][c-1]>0 && (rf[r][c-1]&1) && r>1 && rf[r-1][c]>0 && (rf[r-1][c]&2) && r<14 && rf[r+1][c]>0 && (rf[r+1][c]&2)) v&=13;
				}
				setMaze(r,c,tileRef[v]);
				fw[r][c]=false;
			}
			var d=0;
			if (v<0 && r>1 && r<14 && c>1 && c<20) {
				if (rf[r+1][c]<0) d|=8;
				if (rf[r-1][c]<0) d|=4;
				if (rf[r][c+1]<0) d|=1;
				if (rf[r][c-1]<0) d|=2;
				if (d==12 || d==3) d=0;
			}
			f2[r][c]=d;
		}
	}
	setMaze(6,12,tileRef[ (rf[6][13]>0 && (rf[6][13]&2))? 21:17 ]);
	setMaze(7,12,tileRef[18]);
	setMaze(8,12,tileRef[19]);
}

// keyboard

function enableKeyboard() {
	if (document.addEventListener) document.addEventListener("keypress", keyHandler, true)
	else {
		if (self.Event && self.Event.KEYPRESS) document.captureEvents(Event.KEYPRESS);
		document.onkeypress = keyHandler;
	}
	window.document.onkeydown=keyFix;
}

function keyFix(e) {
	// remap some keyCodes
	if (window.event) {
		if (window.event.ctrlKey || window.event.metaKey || window.event.altKey) return true;
		var k=window.event.keyCode;
		if (k==8) keyHandler({which:8})
		else if (k==37) keyHandler({which:28})
		else if (k==39) keyHandler({which:29})
		else if (k==38) keyHandler({which:30})
		else if (k==40) keyHandler({which:31})
		//else if (k==27) keyHandler({which:27})
		else if (k>=57373 && k<=57376) {
			if (k==57373) keyHandler({which:30})
			else if (k==57374) keyHandler({which:31})
			else if (k==57375) keyHandler({which:28})
			else if (k==57376) keyHandler({which:29});
		}
		else {
			window.event.cancleBubble=false;
			return true;
		}
		if (window.event.preventDefault) window.event.preventDefault();
		if (window.event.stopPropagation) window.event.stopPropagation();
		window.event.cancleBubble=true;
		window.event.returnValue=false;
		return false;
	}
	return true;
}

var keyLock=false;

function keyHandler(e) {
	if (keyLock) return;
	var k;
	var ctrl=false;
	if (e) {
		k=e.which;
		ctrl=(e.ctrlKey || e.metaKey || e.altKey || e.metaKey || e.modifiers);
	}
	else if (window.event) {
		k=window.event.keyCode;
		ctrl= (window.event.ctrlKey || window.event.altKey || window.event.metaKey);
	}
	else return true;
	if (ctrl)  return true;
	if (k=='') {
		// map specials
		if (e==null) e=window.event;
		if (e.charCode==0 && e.keyCode) {
			if (e.keyCode==37) k=28
			else if (e.keyCode==39) k=29
			else if (e.keyCode==38) k=30
			else if (e.keyCode==40) k=31
			else if (e.keyCode==27) k=27;
		}
	}
	var ch= (k>=32 && k<127)? String.fromCharCode(k):'';
	ch=ch.toLowerCase();
	
	if (restartDialogOn) {
		if (ch=='n' || k==27) {
			hideRestartDialog(false);
		}
		else if (ch=='y' || k==13) {
			hideRestartDialog(true);
		}
	}
	else {
		if (ch=="8" || ch=="w" || ch=="i" || k==30) {
			movedir=4;
		}
		else if (ch=="4" || ch=="a" || ch=="j" || k==28) {
			movedir=2;
		}
		else if (ch=="6" || ch=="d" || ch=="l" || k==29) {
			movedir=1;
		}
		else if (ch=="5" || ch=="s" || ch=="k" || k==31 || ch=="2" || k==98) {
			movedir=8;
		}
		else if (ch=="p" || k==27) {
			doPause();
		}
		else if (ch=="n") {
			if ((gameOn || isPause) && nScore>0) {
				showRestartDialog();
			}
			else {
				newGame();
			}
		}
	}
	
	if (window.event) e=window.event;
	if (e.preventDefault) e.preventDefault();
	if (e.stopPropagation) e.stopPropagation();
	e.cancleBubble=true;
	e.returnValue=false;
	return false;
}

// trigger

setTimeout('init()', 500);


// eof