/*
  JavaScript-PacMan 3D
  (c) Norbert Landsteiner 1996-2008; www.masswerk.at
  (based on "JavaScript-PacMan" and "JavaScript-PacMan 2", all (c) 1996-2009 N. Landsteiner)
*/

// preload & setup

var elements = new Object();

var pacimages = [
	'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k', 'l', 'm', 'n',
	'o', 'p', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'pnt',
	'e0', 'e1', 'e2', 'ea0', 'ea1', 'ea2', 'eh1', 'eh2', 'eh4', 'eh8',
	'ex0', 'ex1', 'ex2', 'ex4', 'ex8',
	'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', 'px8',
	'bonus0', 'bonus1', 'bonus2', 'bonus3',
	'f1', 'f2', 'f3', 'f4', 'f5', 'f6',
	'bonus4', 'bonus5', 'bonus6', 'bonus7', 'bonus8', 'bonus9', 'bonus12',
	'gameover', 'gamepaused'
];
var pacimages2 = [
	'life', 'xx', 'newgame_lo', 'newgame_hi', 'newgame_dn',
	'neon_lo', 'neon_hi', 'f1s', 'f2s', 'f3s', 'f4s', 'f5s', 'f6s'
];

var mspacimages = [
	'mpb1', 'mpf1', 'mpf2', 'mpf3', 'mpl1', 'mpl2', 'mpl3', 'mpr1', 'mpr2', 'mpr3',
	'mpx1', 'mpx2', 'mpx3', 'mpx4', 'mpx5', 'mpx6', 'mpx7', 'mpx8',
	'f7', 'f8', 'f9', 'f10', 'bonus10', 'bonus11'
];
var mspacimages2 = [
	'mlife', 'f7s', 'f8s', 'f9s', 'f10s'
];

var imgRef, imgCnt, imgTotal;
var imgPath='pacimages3d/';
var sprites;
var mspacImgsLoaded=false;

function init() {
	elements.maze=getElement('maze');
	if ((!elements.maze) || (!document.images) || (!document.createElement)) {
		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.hiscoredisplay=getElement('hiscoredisplay');
	elements.leveldisplay=getElement('leveldisplay');
	elements.mazedisplay=getElement('mazedisplay');
	elements.pacmodemessage=getElement('pacmodemessage');
	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);
	getStoredSettings();
	var select=getElement('mazeselect');
	select.selectedIndex=0;
	for (var i=0; i<select.options.length; i++) {
		if (select.options[i].value==mazeMode) {
			select.selectedIndex=i;
			break;
		}
	}
	select=getElement('pacmodeselect');
	select.selectedIndex=0;
	for (i=0; i<select.options.length; i++) {
		if (select.options[i].value==settingsPacmode) {
			select.selectedIndex=i;
			break;
		}
	}
	setQuality(settingsQualityValue);
	setSpeed(settingsSpeedValue);
	setPacMode(settingsPacmode, false);
	setMazeMode(mazeMode, false);
	enableKeyboard();
	if (window.activateTouchControls) activateTouchControls();
	setTimeout('newGame()', 10);
}

function preloadMsP() {
	imgCnt=0;
	imgTotal=mspacimages.length+mspacimages2.length;
	var i;
	for (i=0; i<mspacimages.length; i++) {
		var n=mspacimages[i];
		var img=imgRef[n]=new Image();
		img.src=imgPath+n+'.png';
		if (img.complete) {
			imgTotal--;
		}
		else {
			img.onload=preloadHandlerMsP;
		}
	}
	for (i=0; i<mspacimages2.length; i++) {
		var n=mspacimages2[i];
		var img=imgRef[n]=new Image();
		img.src=imgPath+n+'.gif';
		if (img.complete) {
			imgTotal--;
		}
		else {
			img.onload=preloadHandlerMsP;
		}
	}
	if (imgTotal==0) {
		preloadCompleteMsP();
	}
	else {
		var d=getElement('progress');
		setVisibility(d, true);
		d.style.display='block';
		d=getElement('progressbar');
		d.style.width='0px';
		d=getElement('progressvalue');
		d.innerHTML='0%';
	}
}

function preloadHandlerMsP() {
	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('preloadCompleteMsP()', 10);
}

function preloadCompleteMsP() {
	var d=getElement('progress');
	setVisibility(d, false);
	d.style.display='none';
	mspacImgsLoaded=true;
	newGame();
}

function initMaze() {
	var r,c, imgsrc, i, id, x, y, z, mpr;
	var maze = elements.maze=document.getElementById('maze');
	var misieFilter="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+imgPath+"x.png', sizingMethod='crop')";
	for (r=0; r<22; r++) {
		mpr = mazePos[r+1] = new Array();
		for (c=0; c<19; c++) {
			x= (21-r)*14+c*24;
			y= 20+r*11+c*6;
			z= 1+c*6+r*114;
			mpr[c+1]= {x: x-2, y: y-10, z:z};
			id='t'+(r+1)+'_'+(c+1);
			i=document.createElement('img');
			i.id=id;
			if (msieLe6) {
				i.src=imgPath+'xx.gif';
				i.style.filter=misieFilter;
			}
			else {
				i.src=imgPath+'x.png';
			}
			i.width=28; i.height=17;
			i.style.display='block';
			i.style.position='absolute';
			i.style.left=x+'px';
			i.style.top=y+'px';
			i.style.zIndex=z;
			maze.appendChild(i);
			elements[id]=i;
		}
	}
}

function initSprites() {
	var img, id, d, gd, i;
	var maze=elements.maze;
	var xpng=imgPath+'x.png';
	var xgif= imgPath+'xx.gif';
	if (msieLe6) {
		var misieFilter="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+xpng+"', sizingMethod='crop')";
		for (i=1; i<=4; i++) {
			id='g'+i;
			d = elements[id]=document.createElement('div');
			d.style.visibilty='hidden';
			d.style.position='absolute';
			
			id='gbody'+i;
			gd = elements[id]=document.createElement('div');
			gd.style.visibilty='hidden';
			gd.style.position='absolute';
			gd.style.zIndex=1;
			id='igbody'+i;
			img=elements[id]=document.createElement('img');
			img.id=id;
			img.src=xgif;
			img.width=32; img.height=31;
			img.style.filter=misieFilter;
			gd.appendChild(img);
			d.appendChild(gd);
			
			id='geyes'+i;
			gd = elements[id]=document.createElement('div');
			gd.style.visibilty='hidden';
			gd.style.position='absolute';
			gd.style.zIndex=2;
			id='igeyes'+i;
			img=elements[id]=document.createElement('img');
			img.id=id;
			img.src=xgif;
			img.width=32; img.height=31;
			img.style.filter=misieFilter;
			gd.appendChild(img);
			d.appendChild(gd);
			maze.appendChild(d);
			
			id='bonus'+i;
			gd = elements[id]=document.createElement('div');
			gd.style.visibilty='hidden';
			gd.style.position='absolute';
			id='ibonus'+i;
			img=elements[id]=document.createElement('img');
			img.id=id;
			img.src=xgif;
			img.width=32; img.height=31;
			img.style.filter=misieFilter;
			gd.appendChild(img);
			maze.appendChild(gd);
		}
		d = elements.pac=document.createElement('div');
		d.id='pac';
		d.style.visibilty='hidden';
		d.style.position='absolute';
		img = elements.ipac=document.createElement('img');
		img.id='ipac';
		img.src=xgif;
		img.width=32; img.height=31;
		img.style.filter=misieFilter;
		d.appendChild(img);
		maze.appendChild(d);
		d = elements.pac=document.createElement('div');
		d.id='fruit';
		d.style.visibilty='hidden';
		d.style.position='absolute';
		img = elements.fruit=document.createElement('img');
		img.id='ifruit';
		img.src=xgif;
		img.width=32; img.height=31;
		img.style.filter=misieFilter;
		d.appendChild(img);
		maze.appendChild(d);
		d = elements.bonus5=document.createElement('div');
		d.id='bonus5';
		d.style.visibilty='hidden';
		d.style.position='absolute';
		img = elements.ibonus5=document.createElement('img');
		img.id='ibonus5';
		img.src=xgif;
		img.width=32; img.height=31;
		img.style.filter=misieFilter;
		d.appendChild(img);
		maze.appendChild(d);
		sprites = {
			pac: {'div': elements.pac, 'img': elements.ipac},
			g: {
				'1': { 'div': elements.g1, 'body': elements.gbody1, 'eyes': elements.geyes1, 'bodyimg': elements.igbody1, 'eyesimg': elements.igeyes1 },
				'2': { 'div': elements.g2, 'body': elements.gbody2, 'eyes': elements.geyes2, 'bodyimg': elements.igbody2, 'eyesimg': elements.igeyes2 },
				'3': { 'div': elements.g3, 'body': elements.gbody3, 'eyes': elements.geyes3, 'bodyimg': elements.igbody3, 'eyesimg': elements.igeyes3 },
				'4': { 'div': elements.g4, 'body': elements.gbody4, 'eyes': elements.geyes4, 'bodyimg': elements.igbody4, 'eyesimg': elements.igeyes4 }
			},
			bonus: {
				'1': {'div': elements.bonus1, 'img': elements.ibonus1},
				'2': {'div': elements.bonus2, 'img': elements.ibonus2},
				'3': {'div': elements.bonus3, 'img': elements.ibonus3},
				'4': {'div': elements.bonus4, 'img': elements.ibonus4},
				'5': {'div': elements.bonus5, 'img': elements.ibonus5}
			},
			fruit: {'div': elements.fruit, 'img': elements.ifruit}
		};
	}
	else {
		for (var i=1; i<=4; i++) {
			id='g'+i;
			var d = elements[id]=document.createElement('div');
			d.style.visibilty='hidden';
			d.style.position='absolute';
			
			id='gbody'+i;
			img=elements[id]=document.createElement('img');
			img.id=id;
			img.src=xpng;
			img.width=32; img.height=31;
			img.style.position='absolute';
			img.style.display='block';
			img.style.zIndex=1;
			d.appendChild(img);
			
			id='geyes'+i;
			img=elements[id]=document.createElement('img');
			img.id=id;
			img.src=xpng;
			img.width=32; img.height=31;
			img.style.position='absolute';
			img.style.display='block';
			img.style.zIndex=2;
			d.appendChild(img);
			maze.appendChild(d);
			
			id='bonus'+i;
			img=elements[id]=document.createElement('img');
			img.id=id;
			img.src=xpng;
			img.width=32; img.height=31;
			img.style.visibilty='hidden';
			img.style.position='absolute';
			img.style.display='block';
			maze.appendChild(img);
		}
		img = elements.pac=elements.ipac=document.createElement('img');
		img.id='pac';
		img.src=xpng;
		img.width=32; img.height=31;
		img.style.visibilty='hidden';
		img.style.position='absolute';
		img.style.display='block';
		maze.appendChild(img);
		img = elements.fruit=elements.ifruit=document.createElement('img');
		img.id='fruit';
		img.src=xpng;
		img.width=32; img.height=31;
		img.style.visibilty='hidden';
		img.style.position='absolute';
		img.style.display='block';
		maze.appendChild(img);
		img = elements.bonus5=document.createElement('img');
		img.id='bonus5';
		img.src=xpng;
		img.width=32; img.height=31;
		img.style.visibilty='hidden';
		img.style.position='absolute';
		img.style.display='block';
		maze.appendChild(img);
		sprites = {
			pac: {'div': elements.pac, 'img': elements.pac},
			g: {
				'1': { 'div': elements.g1, 'body': elements.gbody1, 'eyes': elements.geyes1, 'bodyimg': elements.gbody1, 'eyesimg': elements.geyes1 },
				'2': { 'div': elements.g2, 'body': elements.gbody2, 'eyes': elements.geyes2, 'bodyimg': elements.gbody2, 'eyesimg': elements.geyes2 },
				'3': { 'div': elements.g3, 'body': elements.gbody3, 'eyes': elements.geyes3, 'bodyimg': elements.gbody3, 'eyesimg': elements.geyes3 },
				'4': { 'div': elements.g4, 'body': elements.gbody4, 'eyes': elements.geyes4, 'bodyimg': elements.gbody4, 'eyesimg': elements.geyes4 }
			},
			bonus: {
				'1': {'div': elements.bonus1, 'img': elements.bonus1},
				'2': {'div': elements.bonus2, 'img': elements.bonus2},
				'3': {'div': elements.bonus3, 'img': elements.bonus3},
				'4': {'div': elements.bonus4, 'img': elements.bonus4},
				'5': {'div': elements.bonus5, 'img': elements.bonus5}
			},
			fruit: {'div': elements.fruit, 'img': elements.fruit}
		};
	}
	
	d=elements.gameover=getElement('gameover');
	setVisibility(d, false);
	if (msieLe6) {
		d.innerHTML='<img id="imggameover" src="'+xgif+'" width="166" height="33" 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="166" height="33" alt="">';
	}
	d.style.left='304px';
	d.style.top='152px';
	d.style.zIndex=2998;
	d=elements.gamepaused=getElement('gamepaused');
	setVisibility(d, false);
	if (msieLe6) {
		d.innerHTML='<img id="imggamepaused" src="'+xgif+'" width="205" height="31" 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="205" height="31" alt="">';
	}
	d.style.left='284px';
	d.style.top='152px';
	d.style.zIndex=2999;
	if (msieLe6 && elements.g1 && elements.g1.filters /*&& elements.g1.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(258, 235, 212, 190);
var settingsSpeed=new Array(238, 222, 206, 180);
var settingsPillBlinkDelay=new Array(8,9,11,13);
var settingsQualityValue=1;
var settingsSpeedValue=1;
var settingsPacmode=0;
var aStep=6;
var aSpeed=252;
var gameDelay;

var blinkRate=4;
var blinkSteps=6;
var pillFactor=1.75;
var pillMaxLength=26;
var pillMinLength=10;
var eyesFactor=.75;
var bonusLifeScore=10000;
var useInstantTurns=true;
var allwaysStartInScatterMode=true;
var fruitFactor1 = 0.287;
var fruitFactor2 = 0.697;
var fruitFactorMsP1 = 0.286;
var fruitFactorMsP2 = 0.705;
var useFruitSideEnter = false;
var usePillBlink=false;
var pillBlinkDelay=9;
var bonusLength, dotsound1;
var pacseed=0;
var hiScores = new Object();
var hiScore=0;
var nScore=0;

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

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

var mazeMode ='Sequentially';
var lastMazeMode ='';
var effectivePacmode=0;

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

var pacLevels = [
{
name: "Remember Pac-Man?",
pacman: true,
maze: [
	'ahhhhhhhhihhhhhhhhc',
	'v........v........v',
	'vpac.ahc.v.ahc.acpv',
	'v.bd.bhd.o.bhd.bd.v',
	'v.................v',
	'v.lm.n.lhihm.n.lm.v',
	'v....v...v...v....v',
	'bhhc.ehmxoxlhg.ahhd',
	'xxxv.vxxxxxxxv.vxxx',
	'lhhd.oxautwcxo.bhhm',
	'XXXX.xxvxxxvxx.XXXX',
	'lhhc.nxbhhhdxn.ahhm',
	'xxxv.vxxxxxxxv.vxxx',
	'ahhd.oxlhihmxo.bhhc',
	'v........v........v',
	'v.lc.lhm.o.lhm.am.v',
	'vp.v.....x.....v.pv',
	'em.o.n.lhihm.n.o.lg',
	'v....v...v...v....v',
	'v.lhhfhm.o.lhfhhm.v',
	'v.................v',
	'bhhhhhhhhhhhhhhhhhd'
]
},
{
name: "Meeting Ms Pac-Man &ndash; Part 1",
alternateName: "Remember Ms. Pac-Man? &ndash; Part  1",
maze: [
	'ahhhhihhhhhhhihhhhc',
	'v....v.......v....v',
	'vplm.o.lhhhm.o.lmpv',
	'v.................v',
	'bc.n.ahc.n.ahc.n.ad',
	'ld.v.bhd.v.bhd.v.bm',
	'XX.v.....v.....v.XX',
	'lc.bhmxlhfhmxlhd.am',
	'xv.xxxxxxxxxxxxx.vx',
	'xv.ahmxautwcxlhc.vx',
	'xv.vxxxvxxxvxxxv.vx',
	'ld.oxnxbhhhdxnxo.bm',
	'XX.xxvxxxxxxxvxx.XX',
	'lc.lhfhmxnxlhfhm.am',
	'xv.....xxvxx.....vx',
	'ad.lhm.lhfhm.lhm.bc',
	'v........x........v',
	'v.ac.ahm.n.lhc.ac.v',
	'vpvv.v...v...v.vvpv',
	'v.bd.o.lhfhm.o.bd.v',
	'v.................v',
	'bhhhhhhhhhhhhhhhhhd'
]
},
{
name: "Meeting Ms P. &ndash; Part 2",
alternateName: "Remember Ms. Pac-Man? &ndash; Part  2",
scatterPos: [[0,0], [4,2], [22,17], [22,2], [4,17]],
maze: [
	'lhhhhihhhhhhhihhhhm',
	'XXXXXv.......vXXXXX',
	'ahhmxo.lhihm.oxlhhc',
	'vp.......v.......pv',
	'v.ahhm.n.v.n.lhhc.v',
	'v.v....v.o.v....v.v',
	'v.o.lcxv...vxam.o.v',
	'v....vxbhhhdxv....v',
	'ehhm.vxxxxxxxv.lhhg',
	'v....oxautwcxo....v',
	'v.lc.xxvxxxvxx.am.v',
	'v..v.nxbhhhdxn.v..v',
	'bc.v.vxxxxxxxv.v.ad',
	'xv.o.bmxahcxld.o.vx',
	'xv......vxv......vx',
	'ld.lhim.bhd.lihm.bm',
	'XX...v..xxx..v...XX',
	'am.n.o.lhihm.o.n.lc',
	'vp.v.....v.....v.pv',
	'v.ld.lhm.o.lhm.bm.v',
	'v.................v',
	'bhhhhhhhhhhhhhhhhhd'
]
},
{
name: "Meeting Ms P. &ndash; Part 3",
alternateName: "Remember Ms. Pac-Man? &ndash; Part  3",
maze: [
	'ahhhhhhihhhihhhhhhc',
	'v......v...v......v',
	'vpahhm.o.n.o.lhhcpv',
	'v.o......v......o.v',
	'v...n.ac.v.ac.n...v',
	'bhm.v.bd.o.bd.v.lhd',
	'X...v.........v...X',
	'n.nxbmxlhhhmxldxn.n',
	'v.vxxxxxxxxxxxxxv.v',
	'v.bmxnxautwcxnxld.v',
	'v.xxxvxvxxxvxvxxx.v',
	'v.nxldxbhhhdxbmxn.v',
	'v.vxxxxxxxxxxxxxv.v',
	'v.bmxahmxnxlhcxld.v',
	'v....v...v...v....v',
	'em.n.o.lhfhm.o.n.lg',
	'vp.v.....x.....v.pv',
	'v.ld.ahm.n.lhc.bm.v',
	'v....v...v...v....v',
	'v.lm.v.lhfhm.v.lm.v',
	'v....v.......v....v',
	'bhhhhfhhhhhhhfhhhhd'
]
},
{
name: "Meeting Ms P. &ndash; Part 4",
alternateName: "Remember Ms. Pac-Man? &ndash; Part  4",
maze: [
	'ahhhhhhhhhhhhhhhhhc',
	'v.................v',
	'v.n.ac.ahhhc.ac.n.v',
	'vpv.bd.v...v.bd.vpv',
	'v.v....v.n.v....v.v',
	'v.bm.n.o.v.o.n.ld.v',
	'v....v...v...v....v',
	'bc.lhyhmxoxlhyhm.ad',
	'ld...vxxxxxxxv...bm',
	'XXxn.oxautwcxo.nxXX',
	'lhhg.xxvxxxvxx.ehhm',
	'XXxo.nxbhhhdxn.oxXX',
	'lc...vxxxxxxxv...am',
	'xv.n.bhmxnxlhd.n.vx',
	'xv.v...xxvxx...v.vx',
	'ad.bhm.nxoxn.lhd.bc',
	'v......vxxxv......v',
	'v.am.n.bhhhd.n.lc.v',
	'vpv..v.......v..vpv',
	'v.o.lfhm.n.lhfm.o.v',
	'v........v........v',
	'bhhhhhhhhfhhhhhhhhd'
]
}
];

var alternateLevels= [
{
	name: 'Starry Night',
	maze: [
	'ahhhhhhhhihhhhhhhhc',
	'v........o........v',
	'vpac.ahc...ahc.acpv',
	'v.bd.bhd.n.bhd.bd.v',
	'v........v........v',
	'v.lm.n.lhyhm.n.lm.v',
	'v....v...v...v....v',
	'bc.lhyhmxoxlhyhm.ad',
	'xv...vxxxxxxxv...vx',
	'ldxn.oxautwcxo.nxbm',
	'XXXv.xxvxxxvxx.vXXX',
	'lcxo.nxbhhhdxn.oxam',
	'xv...vxxxxxxxv...vx',
	'xv.lhyhmxnxlhyhm.vx',
	'xv...vxxxvxxxv...vx',
	'afhm.oxlhfhmxo.lhfc',
	'v........x........v',
	'v.ac.n.lhihm.n.ac.v',
	'vpvv.v...v...v.vvpv',
	'v.bd.bhm.o.lhd.bd.v',
	'v.................v',
	'bhhhhhhhhhhhhhhhhhd'
]
},
{
	name: 'Classic Style',
	maze: [
	'ahhhhhihhhhhihhhhhc',
	'v.....v.....v.....v',
	'vplhc.v.ahc.v.ahmpv',
	'v...o.o.bhd.o.o...v',
	'v.n.............n.v',
	'v.bm.n.lhihm.n.ld.v',
	'v....v...v...v....v',
	'bhhc.bhmxoxlhd.ahhd',
	'xxxv.xxxxxxxxx.vxxx',
	'xxxv.nxautwcxn.vxxx',
	'lhhd.vxvxxxvxv.bhhm',
	'XXXX.oxbhhhdxo.XXXX',
	'lhhc.xxxxxxxxx.ahhm',
	'xxxv.nxlhihmxn.vxxx',
	'xxxv.v...v...v.vxxx',
	'ahhd.bhm.o.lhd.bhhc',
	'v........x........v',
	'v.am.n.lhihm.n.lc.v',
	'vpv..v...v...v..vpv',
	'v.o.ld.n.o.n.bm.o.v',
	'v......v...v......v',
	'bhhhhhhfhhhfhhhhhhd'
]
},
{
	name: 'Modern Times',
	maze: [
	'ahhhhhhhhhhhhhhhhhc',
	'v.................v',
	'vpahc.ahhhhhc.ahcpv',
	'v.bhd.o.....o.bhd.v',
	'v.......ahc.......v',
	'v.lhc.n.bhd.n.ahm.v',
	'v...v.v.....v.v...v',
	'bim.o.bhmxlhd.o.lid',
	'xv....xxxxxxx....vx',
	'ld.lhcxautwcxahm.bm',
	'XX.xxoxvxxxvxoxx.XX',
	'lc.nxxxbhhhdxxxn.am',
	'xv.bhmxxxxxxxlhd.vx',
	'ad.....lhihm.....bc',
	'v..n.n...v...n.n..v',
	'v.ld.ehm.o.lhg.bm.v',
	'v....o...x...o....v',
	'v.ac...lhihm...ac.v',
	'vpvv.n...v...n.vvpv',
	'v.bd.bhm.o.lhd.bd.v',
	'v.................v',
	'bhhhhhhhhhhhhhhhhhd'
]
},
{
	name: 'Duck Soup',
	maze: [
	'ahhhhhhhhhhhhhhhhhc',
	'v.................v',
	'vplm.lhc.n.ahm.lmpv',
	'v......o.o.o......v',
	'v.lc.n.......n.am.v',
	'v..v.bhm.n.lhd.v..v',
	'bc.o.....v.....o.ad',
	'xv...nxlhfhmxn...vx',
	'xv.nxoxxxxxxxoxn.vx',
	'ld.vxxxautwcxxxv.bm',
	'XX.ehmxvxxxvxlhg.XX',
	'lc.vxxxbhhhdxxxv.am',
	'xv.oxnxxxxxxxnxo.vx',
	'xv...vxlhihmxv...vx',
	'xv.n.o...v...o.n.vx',
	'ad.o...n.o.n...o.bc',
	'v....lhg.x.ehm....v',
	'v.am...o.n.o...lc.v',
	'v.v..n...v...n..v.v',
	'vpo.ld.lhfhm.bm.opv',
	'v.................v',
	'bhhhhhhhhhhhhhhhhhd'
]
},
{
	name: 'Yet Another Rorschach-Test',
	maze: [
	'ahhhhhhcxxxahhhhhhc',
	'v......ehhhg......v',
	'vpn.ac.v...v.ac.npv',
	'v.o.bd.o.n.o.bd.o.v',
	'v........v........v',
	'bc.lhm.n.o.n.lhm.ad',
	'xv.....v...v.....vx',
	'ld.lhcxbmxldxahm.bm',
	'XX.xxoxxxxxxxoxx.XX',
	'lc.nxxxautwcxxxn.am',
	'xv.ehmxvxxxvxlhg.vx',
	'ld.oxxxbhhhdxxxo.bm',
	'XX.xxnxxxxxxxnxx.XX',
	'lc.lhdxacxacxbhm.am',
	'xv.....vvxvv.....vx',
	'ad.lim.bdxbd.lim.bc',
	'v...v....x....v...v',
	'v.n.o.lhc.ahm.o.n.v',
	'vpv.....v.v.....vpv',
	'v.bm.lm.o.o.lm.ld.v',
	'v.................v',
	'bhhhhhhhhhhhhhhhhhd'
]
}
];

var ghostsLevels= [
{
	name: 'Blinky\'s Home',
	maze: [
	'ahhhhhhhhihhhhhhhhc',
	'v........v........v',
	'v.n.ahhc.o.ahhc.n.v',
	'vpv.bhhd...bhhd.vpv',
	'v.v......n......v.v',
	'v.bm.n.lhfhm.n.ld.v',
	'v....v.......v....v',
	'bhhc.emxlhmxlg.ahhd',
	'xxxv.vxxxxxxxv.vxxx',
	'lhhd.oxautwcxo.bhhm',
	'XXXX.xxvxxxvxx.XXXX',
	'lhhc.nxbhhhdxn.ahhm',
	'xxxv.vxxxxxxxv.vxxx',
	'ahhd.emxlimxlg.bhhc',
	'v....v...v...v....v',
	'v.lm.o.n.o.n.o.lm.v',
	'v......v.x.v......v',
	'v.am.n.bhhhd.n.lc.v',
	'vpv..v.......v..vpv',
	'v.o.lfhm.n.lhfm.o.v',
	'v........v........v',
	'bhhhhhhhhfhhhhhhhhd'
]
},
{
	name: 'Pinky\'s Place',
	maze: [
	'ahhhhhhhhhhhhhhhhhc',
	'v.................v',
	'vpac.n.ahhhc.n.acpv',
	'v.bd.o.o...o.o.bd.v',
	'v........n........v',
	'bc.ahm.lhyhm.lhc.ad',
	'xv.v.....v.....v.vx',
	'ld.o.ahmxoxlhc.o.bm',
	'XX...oxxxxxxxo...XX',
	'lc.nxxxautwcxxxn.am',
	'xv.ehmxvxxxvxlhg.vx',
	'ld.oxxxbhhhdxxxo.bm',
	'XX...nxxxxxxxn...XX',
	'lc.n.bhmxnxlhd.n.am',
	'xv.v...xxvxx...v.vx',
	'ad.bhm.lhfhm.lhd.bc',
	'v........x........v',
	'v.am.n.lhihm.n.lc.v',
	'vpv..v...v...v..vpv',
	'v.o.lfhm.o.lhfm.o.v',
	'v.................v',
	'bhhhhhhhhhhhhhhhhhd'
]
},
{
	name: 'Inky\'s Home',
	maze: [
	'ahhhhhhhhhhhhhhhhhc',
	'v.................v',
	'o.lhc.ahhhhhc.ahm.o',
	'X..po.o.....o.op..X',
	'n.n.....ahc.....n.n',
	'v.bm.lm.bhd.lm.ld.v',
	'v.................v',
	'bc.lhcxlhhhmxahm.ad',
	'xv...oxxxxxxxo...vx',
	'xv.n.xxautwcxx.n.vx',
	'xv.bhmxvxxxvxlhd.vx',
	'xv.xxxxbhhhdxxxx.vx',
	'xv.ahmxxxxxxxlhc.vx',
	'xv.o...lhihm...o.vx',
	'xv...n...v...n...vx',
	'ad.lhfhm.o.lhfhm.bc',
	'vp.......x.......pv',
	'o.am.n.lhihm.n.lc.o',
	'X.v..v...v...v..v.X',
	'n.o.lfhm.o.lhfm.o.n',
	'v.................v',
	'bhhhhhhhhhhhhhhhhhd'
]
},
{
	name: 'Clyde\'s Place',
	maze: [
	'ahhhhhihhhhhihhhhhc',
	'v.....v.....v.....v',
	'vplhm.o.ahc.o.lhmpv',
	'v.......bhd.......v',
	'v.n.n.n.....n.n.n.v',
	'v.o.o.bhm.lhd.o.o.v',
	'v.................v',
	'bhhc.kxlhhhmxk.ahhd',
	'xxxv.xxxxxxxxx.vxxx',
	'lhhd.nxautwcxn.bhhm',
	'XXXX.vxvxxxvxv.XXXX',
	'lhhc.oxbhhhdxo.ahhm',
	'xxxv.xxxxxxxxx.vxxx',
	'ahhd.kxlhhhmxk.bhhc',
	'v.................v',
	'v.lhm.lhc.ahm.lhm.v',
	'v.......vxv.......v',
	'v.ahc.n.o.o.n.ahc.v',
	'vpvxv.v.....v.vxvpv',
	'v.bhd.bhm.lhd.bhd.v',
	'v.................v',
	'bhhhhhhhhhhhhhhhhhd'
]
},
{
	name: 'Hide &amp; Seek',
	maze: [
	'ahhhhhihhhhhihhhhhc',
	'v.....o.....o.....v',
	'v.ahc...n.n...ahc.v',
	'vpvafc.ld.bm.afcvpv',
	'v.bgxv.......vxed.v',
	'v..bhd.lhihm.bhd..v',
	'bc.....xxvxx.....ad',
	'xv.lhihmxoxlhihm.vx',
	'xv.xxoxxxxxxxoxx.vx',
	'ld.nxxxautwcxxxn.bm',
	'XX.ehmxvxxxvxlhg.XX',
	'lc.oxxxbhhhdxxxo.am',
	'xv...nxxxxxxxn...vx',
	'xv.n.bhmxnxlhd.n.vx',
	'xv.v...xxvxx...v.vx',
	'ad.bhm.lhfhm.lhd.bc',
	'v........x........v',
	'v.ac.ahm.n.lhc.ac.v',
	'vpvv.v...v...v.vvpv',
	'v.bd.o.lhfhm.o.bd.v',
	'v.................v',
	'bhhhhhhhhhhhhhhhhhd'
]
}
];

var msPacPlusLevels = [
{
name: "Ms. Pac-Man Plus, Part 1",
maze: [
	'xxxxxxahhhhhcxxxxxx',
	'ahhhhhd.....bhhhhhc',
	'vp......ahc......pv',
	'v.n.ac.afhfc.ac.n.v',
	'v.o.bd.o...o.bd.o.v',
	'v........n........v',
	'vxahhmxahfhcxlhhcxv',
	'vxv..xxbhhhdxx..vxv',
	'vxv.lmxxxxxxxlm.vxv',
	'vxv..xxautwcxx..vxv',
	'oxbhicxvxxxvxaihdxo',
	'Xx..bdxbhhhdxbd..xX',
	'nxn...xxxxxxx...nxn',
	'vxbhm.ac.z.ac.lhdxv',
	'v.....bg...ed.....v',
	'v.lhc..bhhhd..ahm.v',
	'v...bm...x...ld...v',
	'v.n....ahhhc....n.v',
	'v.bhic.v...v.aihd.v',
	'vp..bd.o.n.o.bd..pv',
	'bhc......v......ahd',
	'xxbhhhhhhfhhhhhhdxx'
]
},
{
name: "Ms. Pac-Man Plus, Part 2",
maze: [
	'ahhhhhhihhhihhhhhhc',
	'v......v...v......v',
	'v.lhhm.v.n.v.lhhm.v',
	'vp.....v.v.v.....pv',
	'bhhm.n.v.v.v.n.lhhd',
	'XXXX.v.o.v.o.v.XXXX',
	'ahhm.v...v...v.lhhc',
	'v....ehmxoxlhg....v',
	'v.ac.vxxxxxxxv.ac.v',
	'v.vv.vxautwcxv.vv.v',
	'v.vv.vxvxxxvxv.vv.v',
	'v.bd.oxbhhhdxo.bd.v',
	'v....xxxxxxxxx....v',
	'bhm.ahhmxnxlhhc.lhd',
	'XXX.v...xvx...v.XXX',
	'ahm.v.lhhfhhm.v.lhc',
	'v...v....x....v...v',
	'v.n.ehhm.n.lhhg.n.v',
	'vpv.v....v....v.vpv',
	'v.o.o.lm.v.lm.o.o.v',
	'v........v........v',
	'bhhhhhhhhfhhhhhhhhd'
]
},
{
name: "Ms. Pac-Man Plus, Part 3",
maze: [
	'ahhhhhhhhhhhhhhhhhc',
	'v.................v',
	'v.n.ac.ahhhc.ac.n.v',
	'vpv.bd.v...v.bd.vpv',
	'v.v....v.n.v....v.v',
	'v.bm.n.o.v.o.n.ld.v',
	'v....v...v...v....v',
	'ec.lhgxlhfhmxehm.ag',
	'vv...vxxxxxxxv...vv',
	'vvXn.oxautwcxo.vXvv',
	'bdXv.xxvxxxvxx.vXbd',
	'XXXv.nxbhhhdxn.vXXX',
	'aihd.vxxxxxxxv.bhic',
	'vv...bhmxnxlhd...vv',
	'vv.n...xxvxx...n.vv',
	'ed.bhm.lhfhm.lhd.bg',
	'v......xxxxx......v',
	'v.am.n.lhhhm.n.lc.v',
	'vpv..v.......v..vpv',
	'v.o.lfhm.n.lhfm.o.v',
	'v........v........v',
	'bhhhhhhhhfhhhhhhhhd'
]
},
{
name: "Ms. Pac-Man Plus, Part 4",
maze: [
	'ahhhhhhhhihhhhhhhhc',
	'v........v........v',
	'vplhm.lm.v.lm.lhmpv',
	'v........o........v',
	'v.lhm.lm...lm.lhm.v',
	'v........n........v',
	'v.acxnxahfhcxnxac.v',
	'o.bdxoxbhhhdxoxbd.o',
	'X...xxxxxxxxxxx...X',
	'ahm.lmxautwcxlm.lhc',
	'v...xxxvxxxvxxx...v',
	'v.acxnxbhhhdxnxac.v',
	'v.vvxvxxxxxxxvxvv.v',
	'o.bdxoxlhhhmxoxbd.o',
	'X.....xxxxxxx.....X',
	'ahhhm.nxlhmxn.lhhhc',
	'vp....oxxxxxo....pv',
	'v.n.n...lhm...n.n.v',
	'v.v.v.n..x..n.v.v.v',
	'v.o.o.bm.n.ld.o.o.v',
	'v........v........v',
	'bhhhhhhhhfhhhhhhhhd'
]
}
];

var levels, allLevels, msPacLevels;

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 tpf = [4,2,8,1,1,2,4,8,8,4,1,2,2,4,1,8];
var tpd = {
	4: 0,
	2: 1,
	8: 2,
	1: 3
};

var mazePos = new Array();

var nextNodes;
var pacNext=null;
var pacLast=null;

var gHomePos= new Array(null, [11,10], [11,9], [11,11],  [10,10] );
var gbid= new Array(1,2,4,8);
var scatterPos1=new Array( [0,0], [3,3], [20,16], [20,3], [3,16]);
var scatterPos2=new Array( [0,0], [1,2], [22,17], [22,2], [1,17]);
var scatterPos;

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 pillPeriode, pillCnt;
var isPause=false;
var restartDialogOn=false;
var gameTimer=null;
var enterTime=0;
var scatterMode, stepCnt, scatterCnt, moved, gLeaveLeft;

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

var aSpan3D = new Array();
var gSpan3D= new Array();
var gSpan3D2= new Array();

var pacmanStr= {
	'0': ['pb1', 'pb1', 'pb1'],
	'1': ['pr1', 'pr2', 'pr3'],
	'2': ['pl1', 'pl2', 'pl3'],
	'4': ['pb1', 'pb1', 'pb1'],
	'8': ['pf1', 'pf2', 'pf3'],
	'x': ['px1', 'px2', 'px3', 'px4', 'px5', 'px6', 'px7', 'px8']
};

var msPacmanStr= {
	'0': ['mpl1', 'mpl1', 'mpl1'],
	'1': ['mpr1', 'mpr2', 'mpr3'],
	'2': ['mpl1', 'mpl2', 'mpl3'],
	'4': ['mpb1', 'mpb1', 'mpb1'],
	'8': ['mpf1', 'mpf2', 'mpf3'],
	'x': ['mpx1', 'mpx2', 'mpx3', 'mpx4', 'mpx5', 'mpx6', 'mpx7', 'mpx8']
};

var ghostStr= {
	'1': ['g11', 'g12', 'g13'],
	'2': ['g21', 'g22', 'g23'],
	'3': ['g31', 'g32', 'g33'],
	'4': ['g41', 'g42', 'g43'],
	'a': ['ga1', 'ga2', 'ga3'],
	'n': ['gn1', 'gn2', 'gn3']
};

var eyeRef= {
	'normal': { '0': 'e0', '1': 'e1', '2': 'e2', '4': 'x', '8': 'e0' },
	'pill': { '0': 'ea0', '1': 'ea1', '2': 'ea2', '4': 'x', '8': 'ea0' },
	'homebound': { '0': 'ea0', '1': 'eh1', '2': 'eh2', '4': 'eh4', '8': 'eh8' },
	'home': { '0': 'ex0', '1': 'ex1', '2': 'ex2', '4': 'ex4', '8': 'ex8' }
};

var fruitRef1 = ['f1', 'f2', 'f3', 'f3', 'f4', 'f4', 'f5', 'f5', 'f6', 'f6', 'f6'];
var fruitBonusRef1=['bonus4', 'bonus5', 'bonus6', 'bonus6', 'bonus7', 'bonus7', 'bonus8', 'bonus8', 'bonus9', 'bonus9', 'bonus12'];
var fruitBonusVal1=[100, 300, 500, 500, 700, 700, 1000, 1000, 3000, 3000, 5000];
var fruitBonusVal3=[100, 300, 500, 500, 700, 700, 1000, 1000, 3000];
var fruitRef2 = ['f1', 'f2', 'f3', 'f7', 'f8', 'f9', 'f10'];
var fruitBonusRef2=['bonus4', 'bonus10', 'bonus6', 'bonus7', 'bonus8', 'bonus11', 'bonus12'];
var fruitBonusVal2=[100, 200, 500, 700, 1000, 2000, 5000];
var fruit, fruitImg, fruitCnt, fruitLimit, fruitLimits, fruitPorts, fruitMoving, fruitBonus, fruiticons, fruitBonusVal, fruitBonusRef;

var scatterPhase, scatterSet;
var scatterSet1= [
	[0, 25],
	[60, 30],
	[60, 25],
	[100, 20]
];
var scatterSet2= [
	[
		[0, 33],
		[94, 33],
		[94, 24],
		[94, 24],
		[10000, 2]
	],
	[
		[0, 33],
		[100, 33],
		[100, 24],
		[4870, 2],
		[10000, 2]
	],
	[
		[0, 24],
		[98, 24],
		[98, 24],
		[4889, 2],
		[10000, 2]
	]
];

var pillPhases = [0,28,24,19,14,9,24,9,9,5,24,9,5,5,14,5,5,0,5];

var pacStr, lifeStr, pacOffsetY, mspacmode, pacmode, trueMspacmode, truePaclevel;
var pacmodeOverride=false;

var scatterPhaseCnt, mspacScatterOffset, mspacScatterPos, pillPos, pillsVisible, pillBlinkCnt;

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

var nextGhost, foodTimer,useGlobalHomeCnt,maxFoodDelay;
var globalHomeCnt=0;
var ghostPenCounts = [0,5,11,21,0];

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

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


// 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.bid=0;
	this.bm=0;
	this.alpha=1;
	this.e=-1;
	this.cMovestrat=0;
	this.cMoveahead=0;
	this.joh=0;
	this.pill=false;
	this.scatter=false;
	this.scnt=0;
	this.tunnel=false;
	this.reverse=false;
	this.step=0;
	this.turn=0;
	this.homeCnt=0;
}

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 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 (!obj) return;
	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,z) {
	obj.style.left=x+'px';
	obj.style.top=y+'px';
	obj.style.zIndex=z;
}

function setMaze(r,c,s) {
	elements['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) {
	elements['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();
	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;
	if (cf1>1 || Math.abs(pac.osx)>1) pac.osx=Math.round(pac.osx*cf1);
	if (cf1>1 || Math.abs(pac.osy)>1) pac.osy=Math.round(pac.osy*cf1);
	limitOffsets(pac, aStep);
	for (var i=1; i<=4; i++) {
		var gi=g[i];
		if (gi.osx+gi.osy==0) continue;
		if (gi.s==0 || (gi.s==2 && (gi.pill || gi.tunnel))) {
			if (cf2>1 || Math.abs(gi.osx)>1) gi.osx=Math.round(gi.osx*cf2);
			if (cf2>1 || Math.abs(gi.osy)>1) gi.osy=Math.round(gi.osy*cf2);
			limitOffsets(gi, gStep);
		}
		else if (gi.s==3) {
			if (cf3>1 || Math.abs(gi.osx)>1) gi.osx=Math.round(gi.osx*cf3);
			if (cf3>1 || Math.abs(gi.osy)>1) gi.osy=Math.round(gi.osy*cf3);
			limitOffsets(gi, gStep2);
		}
		else {
			if (cf1>1 || Math.abs(gi.osx)>1) gi.osx=Math.round(gi.osx*cf1);
			if (cf1>1 || Math.abs(gi.osy)>1) gi.osy=Math.round(gi.osy*cf1);
			limitOffsets(gi, aStep);
		}
	}
	if (fruit) {
		if (cf2>1 || Math.abs(fruit.osx)>1) fruit.osx=Math.round(fruit.osx*cf2);
		if (cf2>1 || Math.abs(fruit.osy)>1) fruit.osy=Math.round(fruit.osy*cf2);
		limitOffsets(fruit, gStep);
	}
	aSpeed=settingsSpeed[settingsSpeedValue];
	gameDelay=Math.round(aSpeed/aStep);
}

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

function setPacMode(v, restart) {
	settingsPacmode=v;
	if (!pacmodeOverride) {
		switch (v) {
			case 1:
				pacmode=true;
				mspacmode=false;
				effectivePacmode=1;
				break;
			case 2:
				pacmode=true;
				mspacmode=true;
				effectivePacmode=2;
				break;
			default:
				pacmode=false;
				mspacmode=false;
				effectivePacmode=0;
				break;
		}
		setHiScore();
		if (restart) {
			nScore=0;
			newGame();
		}
	}
}

function setMazeMode(v, restart) {
	mazeMode=v;
	setHiScore();
	if (restart) {
		nScore=0;
		newGame();
	}
}

function setHiScore() {
	hiScore=hiScores[mazeMode][effectivePacmode];
	var v=(hiScore)? hiScore:'00';
	elements.hiscoredisplay.innerHTML=v;
}

// maze & game setup

function buildMaze(lvl) {
	var r, c, d, f1r, fwr, z, m;
	if (mspacmode && lvl.alternateName) {
		displayMazeName(lvl.alternateName);
	}
	else if (lvl.name) {
		displayMazeName(lvl.name);
	}
	else {
		displayMazeName('');
	}
	if (lvl.scatterPos) {
		scatterPos=lvl.scatterPos;
	}
	else {
		scatterPos=(pacmode)? scatterPos2:scatterPos1;
	}
	if (mspacmode) mspacScatterPos = [ scatterPos[3], scatterPos[1], scatterPos[2], scatterPos[4] ];
	truePaclevel = (pacmode && lvl.pacman && !mspacmode)? true:false;
	if (usePillBlink) pillPos=new Array();
	m=lvl.maze;
	food=0;
	for (r=1; r<=22; r++) {
		fwr=fw[r];
		f1r=f1[r];
		for (c=1; c<=19; 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 if (z=='X') {
				setMaze(r,c,"x");
				f1r[c]=-1;
				fwr[c]=true;
			}
			else {
				setMaze(r,c,z);
				if (z=='p') {
					f1r[c]=8;
					food++;
					if (usePillBlink) pillPos.push([r,c,true]);
				}
				fwr[c]=isSpaceTile[z];
			}
		}
	}
	pacInitDir=0;
	if (fw[17][9]) pacInitDir|=2;
	if (fw[17][11]) pacInitDir|=1;
	if (fw[18][10]) pacInitDir|=8;
	if (fw[16][10]) pacInitDir|=4;
	ghostInitDir=0;
	if (fw[9][9]) ghostInitDir|=2;
	if (fw[9][11]) ghostInitDir|=1;
	if (fw[8][10]) ghostInitDir|=4;
	
	// calc directions
	if (lvl.mazedirs) {
		f2=lvl.mazedirs;
	}
	else {
		f2=new Array(23);
		for (r=1; r<=22; r++) {
			f2[r]=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
		}
		for (r=2; r<22; r++) {
			for (c=2; c<19; c++) {
				if (fw[r][c] && (r<10 || r>12 || c<8 || c>12))  {
					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;
				}
			}
		}
		lvl.mazedirs=f2;
	}
	
	if (!pacmode) nextNodes=getNextNodes();
	
	if (lvl.homepaths) {
		fgh=lvl.homepaths;
		fruitPorts = lvl.fruitports;
	}
	else {
		fruitPorts=new Array();
		fgh=lvl.homepaths=getHomePaths();
		lvl.fruitports=fruitPorts;
	}
}

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

function getHomePaths() {
	var i, d, d2, d3, r, c, p, n;
	var f = new Array(23);
	for (r=1; r<=22; r++) {
		f[r]=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
	}
	var paths=new Array();
	var a=t2[ghostInitDir];
	for (i=0; i<a.length; i++) {
		paths.push([9, 10, a[i]]);
	}
	while (paths.length) {
		var newpaths=new Array();
		for (i=0; i<paths.length; i++) {
			p=paths[i];
			d=p[2];
			r=p[0]+ty[d];
			c=p[1]+tx[d];
			if (r<1) {
				r=22;
			}
			else if (r>22) {
				r=1;
			}
			if (c<1) {
				c=19;
				fruitPorts.push(r);
			}
			else if (c>19) {
				c=1;
			}
			if (!f[r][c]) {
				d2=f2[r][c];
				d3=t3[d];
				f[r][c]=d3;
				if (d2) {
					d2&=(15^d3);
					if (d2) {
						a=t2[d2];
						for (n=0; n<a.length; n++) {
							newpaths.push([r, c, a[n]]);
						}
					}
				}
				else {
					newpaths.push([r, c, d]);
				}
			}
		}
		paths=newpaths;
	}
	return f;
}


// general


function pHome(initial, initscatter) {
	pill=false; pillCnt=0;
	movedir=0;
	with (pac) {
		r=17; c=10;
		osx=0; osy=0;
		md=0; dir= pacInitDir;
		dx=0; dy=0;
		pn=1; p=0;
		reversed=false;
		pacLast=[r,c];
		pacNext=null;
		lc='';
	}
	if (initscatter) {
		stepCnt=0;
		scatterMode=true;
		if (!initial && scatterPhaseCnt==0) scatterPhaseCnt++;
		if (pacmode) {
			scatterCnt=scatterPhase[0][1];
			if (mspacmode) setupMspacScatter();
		}
		else {
			scatterCnt=(initial)? scatterPhase[0][1] : Math.max(15, scatterPhase[0][1]-nLevel*2);
		}
	}
	else if (moved) resetScatter();
	moved=false;
	dotsound1=true;
	setPac(pac.r,pac.c, pacStr[0][0]);
	setOpacity(sprites.pac.div, 1);
	if (fruit) hideFruit();
	if (bonus[5]) {
		setVisibility(sprites.bonus[5].div, false);
		bonus[5]=0;
	}
	setOpacity(sprites.fruit.div, 1);
	globalHomeCnt=0;
}

function gHome(i, initial) {
	with (g[i]) {
		if (pacmode) {
			var hp, ghid;
			if (i==4) {
				hp=(initial || nextGhost==1)? gHomePos[4]:gHomePos[1];
			}
			else {
				hp=gHomePos[i];
			}
			for (var k=i%4+1; k<4; k++) {
				if (g[k].s==0) reallocateGhost(k);
			}
			r=hp[0];
			c=hp[1];
			fft[r][c]=0;
			z=1;
			if (i==4 || nextGhost==0 || nextGhost%4>i) nextGhost=i;
		}
		else {
			for (var k=1; k<=4; k++) {
				if (tileFree(i,gHomePos[k][0],gHomePos[k][1])) {
					r=gHomePos[k][0];
					c=gHomePos[k][1];
					break
				}
			}
			z= (initial)? Math.max(3, 8-nLevel):2+getRand(3);
		}
		osx=osy=0;
		s=0; p=0;
		d=0;
		e=-1;
		pill=false;
		joh=3;
		alpha=1;
		scatter=scatterMode;
		scnt=0;
		step=0;
		turn=0;
		tunnel=false;
		reverse=false;
		setGhost(i,r,c);
	}
	var gs=sprites.g[i];
	setOpacity(gs.body, 1);
	setOpacity(gs.eyes, 1);
	setVisibility(gs.div, true);
}

function reallocateGhost(i) {
	with (g[i]) {
		turn=d=osx=osy=0;
		r=gHomePos[i][0];
		c=gHomePos[i][1];
		setGhost(i,r,c);
		fft[11][10]&=bm;
		fft[10][10]&=bm;
		fft[9][10]&=bm;
		fft[r][c]=bid;
	}
}

function resetHomeCnts() {
	for (var i=1; i<=4; i++) {
		if (i==2 && nLevel==1) {
			g[i].homeCnt=19;
		}
		else if (i==3 && nLevel<3) {
			g[i].homeCnt=(nLevel==1)? 38:32;
		}
		else {
			g[i].homeCnt=0;
		}
	}
}

function fHome() {
	with (fruit) {
		if (mspacmode || useFruitSideEnter) {
			posy=r=fruitPorts[Math.floor(Math.random()*fruitPorts.length)];
			if (Math.random()>=.5) {
				posx=c=1;
				d=1;
			}
			else {
				posx=c=19;
				d=2;
			}
		}
		else {
			posx=c=10;
			posy=r=13;
			d=(Math.random()>=.5)? 2:1;
		}
		osx=0;
		osy=0;
		s=1;
		p=0; pn=1;
	}
	setFruit();
	setVisibility(sprites.fruit.div, true);
	if (useSound) playSound('assetenter');
}

function newGame() {
	if (gameTimer) clearTimeout(gameTimer);
	isPause=false;
	setRestartDialogVisible(false);
	setVisibility(elements.gameover, false);
	setVisibility(elements.gamepaused, false);
	if (msie) {
		setVisibility(elements.maze,true);
	}
	else {
		setOpacity(elements.maze, 1);
	}
	if (nScore && nScore==hiScore && hiScores[mazeMode][effectivePacmode]<nScore) {
		hiScores[mazeMode]=hiScore;
		saveHiScores();
	}
	var img=sprites.pac.img;
	if (lastMazeMode != mazeMode) {
		if (mazeMode == 'MsPacman' || mazeMode == 'Pacman' || mazeMode == 'MsPacmanPlus') {
			if (lastMazeMode != 'MsPacman' || lastMazeMode != 'Pacman' || lastMazeMode != 'MsPacmanPlus') {
				settingsPacmode = 0;
				if (pacmode) settingsPacmode++;
				if (mspacmode) settingsPacmode++;
				pacmodeOverride=true;
				if (window.dimmPacmodeSelect) dimmPacmodeSelect(true);
			}
			displayPacmodeMessage('override: '+((mazeMode == 'Pacman')? 'Pac-Man':'Ms. Pac-Man'));
		}
		else {
			if (lastMazeMode == 'MsPacman' || lastMazeMode == 'Pacman' || lastMazeMode == 'MsPacmanPlus') {
				pacmodeOverride=false;
				setPacMode(settingsPacmode, false);
				displayPacmodeMessage('');
				if (window.dimmPacmodeSelect) dimmPacmodeSelect(false);
			}
		}
		lastMazeMode=mazeMode;
	}
	if (mazeMode == 'MsPacman') {
		if (!msPacLevels) {
			msPacLevels=new Array();
			for (var i=1; i<=4; i++) msPacLevels.push(pacLevels[i]);
		}
		levels=msPacLevels;
		mspacmode=true;
		pacmode=true;
		trueMspacmode=true;
		fruitBonusRef=fruitBonusRef2;
		fruitBonusVal=fruitBonusVal2;
	}
	else if (mazeMode == 'MsPacmanPlus') {
		levels=msPacPlusLevels;
		mspacmode=true;
		pacmode=true;
		trueMspacmode=true;
		fruitBonusRef=fruitBonusRef2;
		fruitBonusVal=fruitBonusVal2;
	}
	else {
		switch (mazeMode) {
			case 'PacLevels':
				levels=pacLevels;
				break;
			case 'AlternateLevels':
				levels=alternateLevels;
				break;
			case 'GhostsLevels':
				levels=ghostsLevels;
				break;
			case 'Pacman':
				levels=new Array();
				levels[0]=pacLevels[0];
				pacmode=true;
				mspacmode=false;
				break;
			case 'Shuffle':
			case 'Sequentially':
			default:
				if (!allLevels) allLevels=pacLevels.concat(alternateLevels, ghostsLevels);
				if (mazeMode=='Shuffle') {
					var b=new Array();
					for (var i=0; i<allLevels.length; i++) b.push({ r: Math.random(), i:i });
					b.sort(levelSorter);
					levels=new Array();
					for (var i=0; i<b.length; i++) levels.push( allLevels[b[i].i] );
				}
				else {
					levels=allLevels;
				}
				break;
		}
		trueMspacmode=false;
		fruitBonusRef=fruitBonusRef1;
		fruitBonusVal=(pacmode  && !mspacmode)? fruitBonusVal1:fruitBonusVal3;
	}
	if (mspacmode) {
		if (!mspacImgsLoaded) {
			preloadMsP();
			return;
		}
		pacStr=msPacmanStr;
		lifeStr='mlife';
		pacOffsetY=5;
		img.width=36;
		img.height=35;
	}
	else {
		pacStr=pacmanStr;
		lifeStr='life';
		pacOffsetY=0;
		img.width=32;
		img.height=31;
	}
	if (mspacmode) {
		scatterSet=scatterSet2;
		fruitMoving=true;
		effectivePacmode=2;
	}
	else if (pacmode) {
		scatterSet=scatterSet2;
		fruitMoving=false;
		effectivePacmode=1;
	}
	else {
		scatterPhase=scatterSet1;
		fruitMoving=true;
		effectivePacmode=0;
	}
	nLife=3;
	setImage('life1',lifeStr);
	setImage('life2',lifeStr);
	setImage('life3','xx');
	setImage('life4','xx');
	setImage('life5','xx');
	nScore=0;
	nLevel=0;
	hiScore=hiScores[mazeMode][effectivePacmode];
	displayLevel();
	displayScore();
	bonusLifeCnt=0;
	fruitCnt=-1;
	fruiticons=new Array();
	startLevel();
	gameTimer=setTimeout('gameStep()', gameDelay);
}

function levelSorter(a,b) {
	return a.r-b.r;
}

function startLevel() {
	gameOn=false;
	moved=false;
	setVisibility(sprites.pac.div, false);
	for (i=1; i<=4; i++) {
		setVisibility(sprites.g[i].div, false);
		setVisibility(sprites.bonus[i].div, false);
		bonus[i]=0;
	}
	setVisibility(sprites.bonus[5].div, false);
	setVisibility(sprites.fruit.div, false);
	bonus[5]=0;
	runThru=false;
	nLevel++;
	displayLevel();
	displayScore();
	gameStatus=0;
	statusCnt=0;
	if (pacmode) {
		switch (nLevel) {
			case 1:
				scatterPhase=scatterSet[0];
				break;
			case 2:
			case 3:
			case 4:
				scatterPhase=scatterSet[1];
				break;
			default:
				scatterPhase=scatterSet[2];
				break;
		}
		pillPeriode = (nLevel<pillPhases.length)? pillPhases[nLevel]:0;
		if (!mspacmode) pacseed=0;
	}
	else {
		var cl=nLevel/10;
		g[1].cMovestrat=Math.min(0.85, 0.55+cl);
		g[1].cMoveahead=1;
		g[2].cMovestrat=Math.min(0.75, 0.25+cl);
		g[2].cMoveahead=Math.min(0.65, 0.15+cl);
		g[3].cMovestrat=Math.min(0.7, 0.2+cl);
		g[3].cMoveahead=Math.min(0.5, 0.1+cl);
		g[4].cMovestrat=Math.min(0.85, 0.6+cl);
		g[4].cMoveahead=-1;
		
		pGstrat=Math.min(0.75, 0.25+nLevel/10);
		pGstrat2=Math.min(0.85, 0.5+nLevel/5);
		pGsLookahead=Math.min(0.6, 0.2+nLevel/10);
		pillPeriode=(nLevel<6)? pillMaxLength : Math.max(pillMinLength, pillMaxLength-(nLevel-5));
	}
	if (trueMspacmode) {
		buildMaze( (nLevel<9)? levels[Math.floor((nLevel-1)/2)] : levels[2+Math.floor(((nLevel-1)%4)/2)] );
	}
	else {
		buildMaze(levels[(nLevel-1)%levels.length]);
	}
	for (var r=1; r<=22; r++) {
		for (var c=1; c<=19; c++) {
			fft[r][c]=0;
		}
	}
	
	fruitCnt++;
	var fimg;
	if (trueMspacmode) {
		fruitBonus = (fruitCnt<7)? fruitCnt:Math.floor(Math.random()*7);
		fimg = fruitRef2[fruitBonus];
	}
	else {
		if (fruitCnt<fruitBonusVal.length) fruitBonus=fruitCnt;
		fimg = fruitRef1[fruitBonus];
	}
	fruitLimits = (mspacmode)?
		[ food-Math.round(food*fruitFactorMsP1), food-Math.round(food*fruitFactorMsP2) ]:
		[ food-Math.round(food*fruitFactor1), food-Math.round(food*fruitFactor2) ];
	if (!trueMspacmode || nLevel<=7) setLevelIcon(fimg);
	setSpriteImg(sprites.fruit.img, fimg);
	fruitLimit = fruitLimits[0];
	fruit=null;
	
	gLeaveLeft=true;
	scatterPhaseCnt=0;
	phaseSet=true;
	pHome(true, true);
	for (var i=1; i<=4; i++) gHome(i, true);
	if (pacmode) {
		resetHomeCnts();
		useGlobalHomeCnt=false;
		maxFoodDelay = (nLevel==1)? 4000:3000;
	}
	setVisibility(sprites.pac.div, true);
	pillBlinkCnt=pillBlinkDelay;
	pillsVisible=true;
	gameOn=true;
}

function setSpan() {
	for (var n=1-aStep; n<aStep; n++) aSpan[n]= (n==0)? 0 : n/aStep;
	gStep=Math.round(aStep*pillFactor);
	for (n=1-gStep; n<gStep; n++) gSpan[n]= (n==0)? 0 : n/gStep;
	gStep2=Math.round(aStep*eyesFactor);
	for (n=1-gStep2; n<gStep2; n++) gSpan2[n]= (n==0)? 0 : n/gStep2;
	
	var min, a, r, c, dx, dy;
	min=1-aStep;
	for (r=min; r<aStep; r++) {
		a=aSpan3D[r]=new Array();
		dx= (r==0)? 0:-14*r/aStep;
		dy= (r==0)? 0:11*r/aStep;
		for (c=min; c<aStep; c++) {
			if (c==0) {
				a[c]= { x: Math.round(dx), y: Math.round(dy) };
			}
			else {
				a[c] = { x: Math.round(dx + 24*c/aStep), y: Math.round(dy+6*c/aStep) };
			}
		}
	}
	min=1-gStep;
	for (r=min; r<gStep; r++) {
		a=gSpan3D[r]=new Array();
		dx= (r==0)? 0:-14*r/gStep;
		dy= (r==0)? 0:11*r/gStep;
		for (c=min; c<gStep; c++) {
			if (c==0) {
				a[c]= { x: Math.round(dx), y: Math.round(dy) };
			}
			else {
				a[c] = { x: Math.round(dx + 24*c/gStep), y: Math.round(dy+6*c/gStep) };
			}
		}
	}
	min=1-gStep2;
	for (r=min; r<gStep2; r++) {
		a=gSpan3D2[r]=new Array();
		dx= (r==0)? 0:-14*r/gStep2;
		dy= (r==0)? 0:11*r/gStep2;
		for (c=min; c<gStep2; c++) {
			if (c==0) {
				a[c]= { x: Math.round(dx), y: Math.round(dy) };
			}
			else {
				a[c] = { x: Math.round(dx + 24*c/gStep2), y: Math.round(dy+6*c/gStep2) };
			}
		}
	}
	tSpan=new Array(3);
	var steps=[aStep, gStep, gStep, aStep, gStep, gStep2];
	var m=0;
	for (r=0; r<steps.length; r+=2) {
		var s1=steps[r];
		var s2=steps[r+1];
		var f=s2/s1;
		var max=s2-1;
		a=tSpan[m++]=new Array(s1);
		for (var c=0; c<s1; c++) {
			a[c]=(c==0)? 0: Math.min(max, Math.max(1, Math.round(c*f)));
		}
	}
}

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 getPacRand() {
	pacseed = (pacseed*5+1)&0x1fff;
	return pacseed;
}

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;
	if (nScore>hiScore) {
		hiScore=nScore;
		elements.hiscoredisplay.innerHTML=nScore;
	}
}

function displayMazeName(t) {
	elements.mazedisplay.innerHTML=(t)? 'Now playing: &raquo;'+t+'&laquo;' : '';
}

function displayPacmodeMessage(t) {
	elements.pacmodemessage.innerHTML=t;
}

function setLevelIcon(icn) {
	if (icn) {
		fruiticons.push(icn);
		var n=1;
		var i;
		for (i=1; i<8-fruiticons.length; i++) {
			setImage('lvlicon'+n, 'xx');
			n++;
		}
		for (i=fruiticons.length-1; i>=0; i--) {
			if (n>7) break;
			setImage('lvlicon'+n, fruiticons[i]+'s');
			n++;
		}
	}
}


// main

function gameStep() {
	if (gameTimer) clearTimeout(gameTimer);
	var now=new Date();
	enterTime=now.getTime();
	if (gameOn) {
		phaseSet=(!phaseSet);
		if (moved) {
			if (!pill && gameStatus==0 && ++stepCnt>=aStep) {
				stepCnt=0;
				if (--scatterCnt<0) setScatter();
			}
			if (pacmode && nextGhost) {
				var t=new Date().getTime();
				if (t-foodTimer > maxFoodDelay) {
					if (useGlobalHomeCnt) {
						globalHomeCnt=ghostPenCounts[nextGhost];
					}
					else {
						g[nextGhost].homeCnt=0;
					}
					foodTimer=t;
				}
			}
		}
		switch(gameStatus) {
			case 0: doMove(); break;
			case 1: resetGhost(); break;
			case 2: resetPac(); return;
			case 3: endLevel(); break;
			case 4: startLevel(); break;
		}
		if (usePillBlink && --pillBlinkCnt<=0) {
			pillBlinkCnt=pillBlinkDelay;
			pillsVisible=!pillsVisible;
			var img=(pillsVisible)? 'p':'x';
			for (var i=pillPos.length-1; i>=0; i--) {
				var p=pillPos[i];
				if (p[2]) setMaze(p[0],p[1],img);
			}
		}
	}
	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 setScatter() {
	if (pacmode) {
		if (scatterMode) {
			scatterMode=false;
			if (scatterPhaseCnt < scatterPhase.length-1) scatterPhaseCnt++;
			scatterCnt=scatterPhase[scatterPhaseCnt][0];
		}
		else {
			scatterMode=true;
			scatterCnt=scatterPhase[scatterPhaseCnt][1];
			if (mspacmode) setupMspacScatter();
		}
		
	}
	else {
		if (scatterMode) {
			scatterMode=false;
			if (scatterPhaseCnt < scatterPhase.length-1) scatterPhaseCnt++;
			scatterCnt=scatterPhase[scatterPhaseCnt][0]+nLevel*10;
		}
		else {
			scatterMode=true;
			scatterCnt=Math.max(10, scatterPhase[scatterPhaseCnt][1] - (nLevel-1));
		}
	}
	for (var i=1; i<=4; i++) {
		var gi=g[i];
		gi.scnt=0;
		gi.scatter=scatterMode;
		if (pacmode && gi.s==2) gi.reverse=true;
	}
}

function resetScatter() {
	if (scatterPhaseCnt==0) scatterPhaseCnt++;
	if (pacmode) {
		scatterMode=false;
		scatterCnt=scatterPhase[scatterPhaseCnt][0];
	}
	else {
		if (scatterMode) {
			setScatter();
		}
		else {
			scatterCnt=scatterPhase[scatterPhaseCnt][0]+nLevel*10;
		}
	}
}

function setupMspacScatter() {
	mspacScatterOffset = getPacRand()&3;
}

function getMspacScatterPos(i) {
	mspacScatterOffset%=4;
	return (i==2 || i==3)? scatterPos[i]:mspacScatterPos[mspacScatterOffset++];
}

function doMove() {
	var sb=sprites.bonus;
	for (var i=1; i<=5; i++) {
		if (bonus[i]>0) {
			bonus[i]--;
			if (bonus[i]==0) {
				setVisibility(sb[i].div, false);
			}
			else {
				setOpacity(sb[i].div, bonus[i]/bonusLength);
			}
		}
	}
	if (fruit) {
		if (fruitMoving) {
			fMove();
		}
		else {
			if (--fruit.z<=0) hideFruit();
		}
	}
	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;
			if (!pacmode) {
				var temp=pacNext;
				pacNext=pacLast;
				pacLast=temp;
			}
			touchX=window.touchLastX;
			touchY=window.touchLastY;
		}
		if (runThru) {
			osx+=dx;
			osy+=dy;
			if ((c+dx)%20==0) osx=0;
			if ((r+dy)%23==0) osy=0;
			osx%=aStep;
			osy%=aStep;
			if (reversed==false) {
				if (osx==0) c=trunkBy(c+dx,19);
				if (osy==0) r=trunkBy(r+dy,22);
			};
			if (phaseSet) {
				p+=pn;
				if (p==2) pn=-1;
				if (p==0) pn=1;
			};
			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 (pillPeriode) {
				pill=true;
				for (var i=1; i<=4; i++) {
					var gi=g[i];
					if (!gi.pill && gi.s<3) {
						gi.pill=true;
						translateGPos(i,gStep);
						if (pacmode) gi.reverse=true;
					}
				}
				pillCnt=pillPeriode*aStep;
				ghostCnt=0;
			}
			else {
				for (var i=1; i<=4; i++) {
					var gi=g[i];
					if (gi.s<3) gi.reverse=true;
				}
			}
			addScore(40);
			if (useSound) playSound('pillbeam');
			if (scatterMode) setScatter();
			stopSound=false;
			if (usePillBlink) {
				for (var i=pillPos.length-1; i>=0; i--) {
					var p=pillPos[i];
					if (p[0]==r && p[1]==c) {
						p[2]=false;
						break;
					}
				}
			}
		}
		else {
			addScore(15);
			if (useSound) {
				if (mspacmode) {
					playMsPacSound();
				}
				else {
					playSound((dotsound1)?'doteat1':'doteat2');
					dotsound1=!dotsound1;
				}
			}
		}
		f1[r][c]=0;
		food--;
		if (food==0) {
			statusCnt=blinkSteps*blinkRate;
			gameStatus=3;
			g[1].alpha=.75;
			for (i=1; i<=4; i++) {
				var gs=sprites.g[i];
				setOpacity(gs.body, .75);
				setOpacity(gs.eyes, .75);
			}
			if (useSound) playSound('endlevel');
			return;
		}
		else if (food==fruitLimit) {
			fruit=new Ghost();
			if (fruitMoving) {
				fHome();
			}
			else {
				fruit.r=fruit.posy=13;
				fruit.c=fruit.posx=10;
				fruit.z=44*aStep;
				var p = mazePos[13][10];
				moveElement(sprites.fruit.div, p.x, p.y, p.z);
				setVisibility(sprites.fruit.div, true);
			}
		}
		if (pacmode) manageGhostPen();
	}
	gmove();
	checkHits();
	if (gameStatus>0) return;
	if (pill) {
		pillCnt--;
		if (pillCnt<=0) {
			pillCnt=0;
			pill=false;
			for (i=1; i<=4; i++) {
				var gi=g[i];
				if (gi.pill) {
					gi.pill=false;
					if (gi.s!=4) translateGPos(i,aStep);
				}
			}
		}
	}
}


// pacman

function setPac(r,c,s) {
	pac.posx=c+aSpan[pac.osx];
	pac.posy=r+aSpan[pac.osy];
	if (s!=pac.lc) {
		setSpriteImg(sprites.pac.img, s);
		pac.lc=s;
	}
	var p = mazePos[r][c];
	var ofs = aSpan3D[pac.osy][pac.osx];
	moveElement(sprites.pac.div, p.x+ofs.x, p.y+ofs.y-pacOffsetY, p.z+5);
}

function pMove() {
	with (pac) {
		if (dir&movedir) {
			md= dir&movedir;
			touchX=window.touchLastX;
			touchY=window.touchLastY;
			if (!moved) {
				moved=true;
				if (pacmode) foodTimer=new Date().getTime();
			}
		}
		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 (!pacmode && 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;
	}
}


// fruit

function fMove() {
	with (fruit) {
		if (osx+osy==0) {
			var x= f2[r][c];
			if (x>0) {
				var k= x&(15^t3[d]);
				d=t2[k][getRand(t2[k].length)];
			}
		}
		osy=(osy+ty[d])%gStep;
		osx=(osx+tx[d])%gStep;
		if (osx==0) c+=tx[d];
		if (osy==0) r+=ty[d];
		if (c==0 || c==20 || r==0 || r==23) {
			hideFruit();
			if (useSound) playSound('assetleave');
		}
		else {
			setFruit();
		}
		if (phaseSet) {
			p+=pn;
			if (p==3) pn=-1;
			if (p==0) pn=1;
		}
	}
}

function setFruit() {
	fruit.posx=fruit.c+gSpan[fruit.osx];
	fruit.posy=fruit.r+gSpan[fruit.osy];
	var p = mazePos[fruit.r][fruit.c];
	var ofs = gSpan3D[fruit.osy][fruit.osx];
	moveElement(sprites.fruit.div, p.x+ofs.x, p.y+ofs.y-fruit.p, p.z);
}

function hideFruit() {
	if (fruit) {
		setVisibility(sprites.fruit.div, false);
		fruit=null;
	}
	if (fruitLimit>food && fruitLimit==fruitLimits[0]) {
		fruitLimit=fruitLimits[1];
		if (fruitMoving && fruitLimit>food-5) {
			fruitLimit=Math.max(1, food-5, Math.floor(food/2));
		}
	}
}


// ghosts

function setGhost(i,r,c) {
	var eyes;
	var gi=g[i];
	var s=gi.s;
	var gc, ofs;
	var p = mazePos[gi.r][gi.c];
	if (gi.pill && s<3) {
		if (s==0) {
			gi.posx=c;
			gi.posy=r;
			ofs = {x:0,y:0};
		}
		else {
			gi.posx=c+gSpan[gi.osx];
			gi.posy=r+gSpan[gi.osy];
			ofs=gSpan3D[gi.osy][gi.osx];
		}
		if (pillCnt>4*aStep) {
			gc=ghostStr.a;
		}
		else {
			gc=(pillCnt%6<3)? ghostStr.n:ghostStr.a;
		}
	}
	else if (s==3) {
		gi.posx=c+gSpan2[gi.osx];
		gi.posy=r+gSpan2[gi.osy];
		ofs=gSpan3D2[gi.osy][gi.osx];
		gc='x';
	}
	else if (gi.tunnel || s==0) {
		gi.posx=c+gSpan[gi.osx];
		gi.posy=r+gSpan[gi.osy];
		ofs=gSpan3D[gi.osy][gi.osx];
		gc=ghostStr[i];
	}
	else {
		gi.posx=c+aSpan[gi.osx];
		gi.posy=r+aSpan[gi.osy];
		ofs=aSpan3D[gi.osy][gi.osx];
		gc=ghostStr[i];
	}
	var eyes;
	var gs=sprites.g[i];
	if (s==3) {
		eyes=eyeRef.homebound;
	}
	else {
		setSpriteImg(gs.bodyimg, gc[gi.p]);
		if (s==0) {
			eyes=eyeRef.home;
		}
		else {
			eyes=(gi.pill)? eyeRef.pill:eyeRef.normal;
		}
	}
	if (s<3) fft[gi.r][gi.c]|=gi.bid;
	if (gi.d!=gi.e) {
		setSpriteImg(gs.eyesimg, eyes[gi.d]);
		gi.e=gi.d;
	}
	moveElement(gs.div, p.x+ofs.x, p.y+ofs.y, p.z+i);
}

function gmove() {
	for (i=1; i<=4; i++) {
		if (gameOn) {
			with (g[i]) {
				fft[r][c]&=bm;
				if (phaseSet) {
					p=(++p)%3;
				}
				if (s==2) {
					if (pacmode) gMove2pacmode(i)
					else 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 (moved && (!pacmode || nextGhost==i)) {
			if (r==10 && (pacmode || tileFree(i,9,10))) {
				s++;
				d=4;
				e=-1;
				z=0;
				if (pacmode) {
					nextGhost=0;
					if (i==3) {
						resetHomeCnts();
						useGlobalHomeCnt=false;
					}
					else {
						for (var k=1; k<4; k++) {
							if (g[k].s==0) {
								nextGhost=k;
								break;
							}
						}
					}
				}
			}
			if (r==11 && c==10) {
				if (osx) {
					osx+=tx[turn];
					if (osx==0) d=0;
				}
				else if (tileFree(i,10,10)) {
					if (z==0 || (pacmode && ((useGlobalHomeCnt && globalHomeCnt>=ghostPenCounts[i]) || (!useGlobalHomeCnt && homeCnt<=0)))) {
						d=4;
						e=-1;
						if (--osy==-aStep) {
							osy=0;
							r=10;
						}
					}
					else {
						if (!pacmode && stepCnt==0) z--;
						gMoveEyes(i);
					}
				}
			}
			else if (r==11 && c==9 && tileFree(i,11,10)) {
				turn=d=1;
				c=10;
				osx=1-gStep;
				e=-1;
			}
			else if (r==11 && c==11 && tileFree(i,11,10)) {
				turn=d=2;
				c=10;
				osx=gStep-1;
				e=-1;
			}
			else {
				gMoveEyes(i);
			}
		}
		else {
			gMoveEyes(i);
		}
	}
}

function manageGhostPen() {
	globalHomeCnt++;
	foodTimer=new Date().getTime();
	if (nextGhost) {
		g[nextGhost].homeCnt--;
	}
}

function gMove1(i) {
	with (g[i]) {
		if (pacmode || tileFree(i,9,10)) {
			osy--;
			if (osy==-aStep) {
				r=9;
				c=10;
				osx=osy=0;
				if (pacmode) {
					d=(i==4 || gLeaveLeft)? 2:1; reverse=pill;
					if (pacseed%256>240) gLeaveLeft=!gLeaveLeft;
					gMove2pacmode(i);
				}
				else if (scatter) {
					gmoveScatter(i, ghostInitDir);
				}
				else {
					d=t2[ghostInitDir][getRand(t2[ghostInitDir].length)];
				}
				step = (pill)? gStep:aStep;
				s++;
				e=-1;
			}
		}
	}
}

function gMove2(i) {
	with (g[i]) {
		if (osx+osy==0) {
			var x = f2[r][c];
			if (joh) {
				joh--;
				x&=11;
			}
			if (x>0) {
				var gdm= x&(15^t3[d]);
				if (scatter && !pill) {
					gmoveScatter(i,gdm);
				}
				else if (i==3 && !pill && scatterCnt%4) {
					var delta=Math.abs(pac.r-r)+Math.abs(pac.c-c);
					if (delta>10 || Math.random()>=cMovestrat) {
						gmoveRand(i,gdm);
					}
					else {
						gmoveStrat(i,gdm);
					}
				}
				else if (Math.random()<cMovestrat) {
					gmoveStrat(i,gdm);
				}
				else {
					gmoveRand(i,gdm);
				}
			}
			if (d>0) {
				if (tileFree(i,trunkBy(r+ty[d],22),trunkBy(c+tx[d],19))) {
					gSet(i);
				}
				else {
					reverseStuck(i,x);
				}
			}
		}
		else {
			gSet(i);
		}
	}
}

function gMove2pacmode(i) {
	with (g[i]) {
		if (osx+osy==0) {
			var xtnl=false;
			if (reverse) {
				turn=t3[d];
				reverse=false;
				return;
			}
			else {
				if (turn) d=turn;
				turn=0;
				var xr=trunkBy(r+ty[d],22);
				var xc=trunkBy(c+tx[d],19)
				var x = f2[xr][xc];
				if (x>0) {
					x&=(15^t3[d]);
					if (pill) {
						//var rd=tpf[Math.floor(Math.random()*4)];
						var rd=tpf[getPacRand()&0xf];
						if (x&rd) {
							turn=rd;
						}
						else {
							for (var k=0; k<4; k++) {
								if (x&tpf[k]) {
									turn=tpf[k];
									break;
								}
							}
						}
					}
					else {
						if ((xr==9 || (truePaclevel && xr==17)) && (xc>=9 && xc<=11)) x&=11;
						var tr, tc;
						if (scatter) {
							var sp = (mspacmode)? getMspacScatterPos(i) : scatterPos[i];
							tr=sp[0];
							tc=sp[1];
						}
						else {
							switch (i) {
								case 1: // Pinky
									var pd=pac.md;
									tr=pac.r+4*ty[pd];
									tc=pac.c+4*tx[pd];
									if (pd==4) tc-=4;
									break;
								case 2: // Inky
									var pd=pac.md;
									tr=pac.r+2*ty[pd];
									tc=pac.c+2*tx[pd];
									if (pd==4) tc-=2;
									tr += tr-g[4].r;
									tc += tc-g[4].c;
									break;
								case 3: // Clyde (treshold 8 becomes 7 for smaller maze)
									if (Math.abs(pac.r-xr)+Math.abs(pac.c-xc) < 7) {
										tr=scatterPos[3][0];
										tc=scatterPos[3][1];
										if (truePaclevel && xr==11 && xc==5) x&=13; // do not enter tunnel
									}
									else {
										tr=pac.r;
										tc=pac.c;
									}
									break;
								case 4: // Blinky
									tr=pac.r;
									tc=pac.c;
									break;
							}
						}
						if (x) {
							var dirs=t2[x];
							var dsy = xr-tr;
							var dsx = xc-tc;
							var delta=pd=100000000;
							for (var k=0, l=dirs.length; k<l; k++) {
								var n=dirs[k];
								var fr=dsy+ty[n];
								var fc=dsx+tx[n];
								var fd=fr*fr+fc*fc;
								if (fd<delta || (fd==delta && tpd[n]<pd)) {
									turn=n;
									delta=fd;
									pd=tpd[n];
								}
							}
						}
					}
				}
				if (f1[xr][xc]<0) xtnl=true;
			}
			tunnel = (xtnl || f1[r][c]<0)? true:false;
			if (d>0) gSet(i);
		}
		else {
			gSet(i);
		}
	}
}

function gMove3(i) {
	with (g[i]) {
		if (osx+osy==0) {
			if (r==9 && c==10) {
				if (pacmode) {
					d=8;
					z=1;
				}
				else {
					gHome(i, false);
					return;
				}
			}
			else if (z) {
				if (i==4 && nextGhost==1 && r==10 && c==9) {
					gHome(i, false);
					return;
				}
				if (r==11 && c==10) {
					if (i==1 || i==4) {
						gHome(i, false);
						return;
					}
					d=(i==2)? 2:1;
				}
				else if (r==11 && c==gHomePos[i][1]) {
					gHome(i, false);
					return;
				}
			}
			else {	
				d= fgh[r][c];
			}
		}
		gSet(i);
	}
}

function gMove4(i) {
	var gi=g[i];
	gi.z--;
	var gs=sprites.g[i];
	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.e=-1;
		var hd = fgh[gi.r][gi.c];
		if (((gi.d & 3) && (hd & 3)) || ((gi.d & 12) && (hd & 12))) gi.d=hd;
		setGhost(i,gi.r,gi.c);
		setSpriteImg(gs.bodyimg, 'x');
		setOpacity(gs.body,1);
		setOpacity(gs.eyes,1);
	}
	else {
		gi.alpha-= (gi.z<ghostResetDelay/2)? ghostResetDelta*2 : ghostResetDelta;
		setOpacity(gs.body, gi.alpha);
	}
}

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

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 (gi.pill) {
		desy=gi.r-pac.r;
		desx=gi.c-pac.c;
	}
	else if (pacNext && Math.random()<gi.cMoveahead) {
		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;
	}
	var nd=0;
	if (desx!=0) nd= (desx>0)? 1:2;
	if (desy!=0) nd|= (desy>0)? 8:4;
	nd&=gdm;
	gmoveRand(i, (nd)? nd:gdm);
}

function gmoveScatter(i,gdm) {
	var gi=g[i];
	var p=scatterPos[i];
	var desy=p[0]-gi.r;
	var desx=p[1]-gi.c;
	if (gi.scnt) {
		if (--gi.scnt==0) gi.scatter=false;
	}
	else if (Math.abs(desx)+Math.abs(desy)<3) {
		gi.scnt=4;
	}
	var nd=0;
	if (desx!=0) nd= (desx>0)? 1:2;
	if (desy!=0) nd|= (desy>0)? 8:4;
	var dxy=Math.abs(desx)-Math.abs(desy);
	nd&=gdm;
	if (dxy<-5 && nd&12) {
		nd&=12;
	}
	else if (dxy>5 && nd&3) {
		nd&=3;
	}
	gmoveRand(i,(nd)? nd:gdm);
}

function reverseStuck(i,x) {
	with (g[i]) {
		if (x==0) {
			if (tileFree(i,trunkBy(r-ty[d],22),trunkBy(c-tx[d],19))) 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, nStep) {
	with (g[i]) {
		if (s==2 && nStep!=step) {
			if (tunnel) {
				limitOffsets(g[i], gStep);
				step=gStep;
			}
			else {
				var ts;
				if (step==aStep && nStep==gStep) {
					ts=tSpan[0]
				}
				else if (step==gStep && nStep==aStep) {
					ts=tSpan[1]
				}
				else if (step==gStep && nStep==gStep2) {
					ts=tSpan[2]
				}
				if (ts) {
					osx= (osx<0)? -ts[-osx]:ts[osx];
					osy= (osy<0)? -ts[-osy]:ts[osy];
				}
				step=nStep;
			}
		}
		if (s!=3) e=-1;
	}
}

// hit-test

function checkHits() {
	var crash=false;
	var ghostcatch=false;
	if (fruit && isCollision(fruit.posx,fruit.posy, pac.posx,pac.posy, 0.825)) {
		var pos = mazePos[fruit.r][fruit.c];
		var ofs=gSpan3D[fruit.osy][fruit.osx];
		hideFruit();
		bonus[5]=bonusLength;
		var b=sprites.bonus[5];
		moveElement(b.div, pos.x+ofs.x, pos.y+ofs.y, pos.z+5);
		setSpriteImg(b.img, fruitBonusRef[fruitBonus]);
		setOpacity(b.div, 1);
		setVisibility(b.div, true);
		addScore(fruitBonusVal[fruitBonus]);
		if (useSound) playSound( (mspacmode)? 'asseteat':'assetpick');
	}
	for (i=1; i<=4; i++) {
		var gi=g[i];
		if (gi.s==2 && isCollision(gi.posx,gi.posy, pac.posx,pac.posy, 0.95)) {
			var gs=sprites.g[i];
			if (gi.pill) {
				bonus[i]=bonusLength;
				var b=sprites.bonus[i];
				var pos = mazePos[gi.r][gi.c];
				var ofs=gSpan3D[gi.osy][gi.osx];
				var ofs2 = i*3;
				moveElement(b.div, pos.x+ofs.x+ofs2, pos.y+ofs.y-ofs2, pos.z+5);
				setSpriteImg(b.img, 'bonus'+ghostCnt);
				setOpacity(b.div, 1);
				setVisibility(b.div, true);
				addScore(ghostBonus[ghostCnt]);
				if (ghostCnt<3) ghostCnt++;
				gi.tunnel=false;
				translateGPos(i,gStep2);
				var hd = fgh[gi.r][gi.c];
				if ((hd&3 && gi.d&3) || (hd&12 && gi.d&12)) g.d=hd;
				setSpriteImg(gs.bodyimg, 'gx');
				setSpriteImg(gs.eyesimg, eyeRef.homebound[gi.d]);
				setOpacity(gs.body, ghostResetAlpha);
				setOpacity(gs.eyes, ghostResetAlpha);
				gi.alpha=ghostResetAlpha;
				gi.z=ghostResetDelay;
				gi.s=4;
				fft[gi.r][gi.c]&=gi.bm;
				ghostcatch=true;
			}
			else {
				crash=true;
				setOpacity(gs.body, .5);
			}
		}
	}
	if (crash) {
		setSpriteImg(sprites.pac.img, pacStr.x[0]);
		pac.p=0;
		gameStatus=2;
		if (useSound) playSound('pacend');
	}
	else if (ghostcatch) {
		gameStatus=1;
		statusCnt=2;
		if (useSound) playSound( (mspacmode)? 'mspghostcatch':'ghostcatch');
	}
}

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

function resetPac() {
	if (gameTimer) clearTimeout(gameTimer);
	if (pac.p<7) {
		pac.p++;
		setSpriteImg(sprites.pac.img, pacStr.x[pac.p]);
		setOpacity(sprites.pac.div, 1-pac.p/18);
		gameTimer=setTimeout('resetPac()', 174-pac.p*2);
	}
	else {
		nLife--;
		if (nLife>0) {
			setImage('life'+nLife,'xx');
			pHome(false, (pacmode)? true:allwaysStartInScatterMode);
			for (var i=1; i<=4; i++) {
				var gi=g[i];
				fft[gi.r][gi.c]&=gi.bm;
				gHome(i, false);
			}
			if (pacmode) {
				//resetHomeCnts();
				useGlobalHomeCnt=true;
			}
			gameStatus=0;
			gameTimer=setTimeout('gameStep()', 100);
		}
		else gameOver();
	}
}

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

function gameOver() {
	gameOn=false;
	gameStatus=-1;
	setOpacity(elements.gameover, .1);
	setVisibility(elements.gameover, true);
	setVisibility(sprites.pac.div, false);
	setVisibility(elements.gamepaused, false);
	for (var i=1; i<=4; i++) {
		var gs=sprites.g[i];
		setOpacity(gs.body, .6);
		if (!msie) setOpacity(gs.eyes, .7);
	}
	if (fruit) setOpacity(sprites.fruit.div, .6);
	if (nScore==hiScore) {
		hiScores[mazeMode][effectivePacmode]=nScore;
		nScore=0;
		saveHiScores();
	}
	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++) {
		var gs=sprites.g[i];
		setOpacity(gs.body, a);
		//setOpacity(gs.eyes, a);
	}
	if (fruit) setOpacity(sprites.fruit.div, a);
	if (statusCnt%blinkRate==0) {
		if (msie || window.opera) {
			setVisibility(elements.maze, ((statusCnt/blinkRate)%2==blinkSteps%2)? false:true);
		}
		else {
			if ((statusCnt/blinkRate)%2==blinkSteps%2) {
				setOpacity(elements.maze, 0.6);
			}
			else {
				setOpacity(elements.maze, 1);
			}
		}
	}
	statusCnt--;
	if (statusCnt==0) {
		if (msie || window.opera) {
			setVisibility(elements.maze,true);
		}
		else {
			setOpacity(elements.maze, 1);
		}
		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;
			if (pacmode) foodTimer=new Date().getTime();
		}
	}
}

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, .2);
	restartDialogOn=v;
}


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

// high scores & cookies

var hskeys=['Sequentially','PacLevels', 'AlternateLevels', 'GhostsLevels', 'Pacman', 'MsPacman', 'MsPacmanPlus', 'Shuffle'];
var hsprefix='pac3dHS';
var settingsprefix='pac3dSettings';
var cookieLifeSpan = 366 * 24 * 60 * 60 * 1000;

function getStoredSettings() {
	var vars=getCookieVars();
	var hsstring=vars[hsprefix];
	var hsvals=new Object();
	if (hsstring) {
		var parts=hsstring.split(',');
		for (var i=0; i<parts.length; i++) {
			var p=parts[i].split(':');
			if (p.length==4) {
				hsvals[p[0]] = [parseInt(p[1],10), parseInt(p[2],10), parseInt(p[3],10)];
			}
		}
	}
	for (var i=0; i<hskeys.length; i++) {
		var k=hskeys[i];
		hiScores[k]= hsvals[k] || [0,0,0];
	}
	var v=parseInt(vars[settingsprefix+'Quality'],10);
	if (v && !isNaN(v)) settingsQualityValue = v;
	v=parseInt(vars[settingsprefix+'Speed'],10);
	if (v && !isNaN(v)) settingsSpeedValue = v;
	v=parseInt(vars[settingsprefix+'Mode'],10);
	if (v && !isNaN(v)) settingsPacmode = v;
	v=vars[settingsprefix+'Maze'];
	if (v) {
		for (var i=0; i<hskeys.length; i++) {
			if (v==hskeys[i]) {
				mazeMode = v;
				break;
			}
		}
	}
}

function resetHiScores() {
	if (!confirm('Sure to reset the high scores on this machine?')) return;
	for (var i=0; i<hskeys.length; i++) {
		hiScores[hskeys[i]]=[0,0,0];
	}
	hiScore=0;
	saveHiScores();
	setMazeMode(mazeMode, false);
}

function saveHiScores() {
	var now = new Date();
	now.setTime( now.getTime() + cookieLifeSpan );
	var datestring = ';expires=' + now.toGMTString();
	var parts=new Array();
	for (var i=0; i<hskeys.length; i++) {
		var k=hskeys[i];
		parts.push(k+':'+ ((hiScores[k])? hiScores[k].join(':') : '0:0:0'));
	}
	document.cookie=hsprefix+'='+escape(parts.join(','))+datestring;
}

function saveSettings() {
	var now = new Date();
	now.setTime( now.getTime() + cookieLifeSpan );
	var datestring = ';expires=' + now.toGMTString();
	document.cookie=settingsprefix+'Quality='+settingsQualityValue+datestring;
	document.cookie=settingsprefix+'Speed='+settingsSpeedValue+datestring;
	document.cookie=settingsprefix+'Mode='+settingsPacmode+datestring;
	document.cookie=settingsprefix+'Maze='+mazeMode+datestring;
}

function getCookieVars() {
	var obj = new Object();
	var parts = document.cookie.split(';');
	for ( var i = 0; i < parts.length; i++ ) {
		var temp = parts[i].split('=');
		if (temp.length>=2) {
			var key = temp[0].replace(/^\s+|\s+$/g, '');
			var val = unescape( temp[1].replace(/^\s+|\s+$/g, '') );
			obj[key]=val;
		}
	}
	return obj;
}

// trigger

setTimeout('init()', 500);


onunload=function() {
	elements=sprites=null;
	try {
		saveSettings();
		if (gameOn && nScore && nScore==hiScore && hiScores[mazeMode][effectivePacmode]!=nScore) {
			hiScores[mazeMode][effectivePacmode]=nScore;
			saveHiScores();
		}
	}
	catch(e) {}
}

// eof