/*name			: Tank-o-Furry - Game Engineupdate			: 20080630author			: Maurice van Creijdependencies	: classbehaviour.jsinfo			: http://www.woollymittens.nl/*/

	// create the classBehaviour object if it doesn't already
	if(typeof(classBehaviour)=='undefined'){
		function ClassBehaviour(){
			this.handlers = new Array();
		}
		classBehaviour = new ClassBehaviour();
	}

		// game engine and interface mechanics for a tank game		// define this class behaviour		function TankOFurry(){			// properties
			this.delay			=	4000;
			this.heightMap		=	new Array(
										2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,										2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,										2,3,2,2,2,2,2,2,2,2,2,2,2,0,0,2,										2,4,4,2,2,2,0,0,0,2,2,2,0,0,2,2,										2,4,4,4,2,0,0,2,0,0,2,0,0,2,2,2,										2,4,4,4,2,0,2,4,4,4,2,2,0,2,2,2,										2,3,2,4,2,0,3,4,4,4,2,2,2,2,2,2,										2,2,2,9,2,2,3,4,4,2,2,0,0,2,2,2,										2,2,2,2,2,0,2,9,9,2,2,0,2,2,2,2,										2,3,9,9,2,0,2,2,2,2,0,0,2,9,9,2,										2,4,4,4,2,0,2,2,0,0,0,2,2,4,4,9,										2,9,3,9,2,0,0,0,0,2,2,2,3,4,4,4,										2,2,2,2,2,2,0,2,2,2,2,2,2,2,4,4,										2,2,2,2,2,0,0,2,2,4,3,2,2,2,4,4,										2,0,0,2,0,0,2,2,4,4,2,2,2,2,2,3,										0,0,2,2,2,2,2,2,4,4,4,4,4,3,2,2									);
			this.mapXml			=	null;			this.name 			= 	'tankOFurry';
			this.node			=	null;
			this.rankImage		=	null;
			this.timeout		=	null;
			this.updateUrl		=	null;
			this.updateTime		=	null;
			this.updateSet		=	null;
			// objects
			this.mouseX			=	0;
			this.mouseY			=	0;
			this.tankFilter		=	new TankFilter;
			this.tankGrid		=	new TankGrid;
			this.tankInfo		=	new TankInfo;
			this.tankOptions	=	new TankOptions;
			this.tankShop		=	new TankShop;
			this.tankStatus		=	new TankStatus;
			this.tankProgress	=	new TankProgress;
			this.tankReward		=	new TankReward;
			this.tankUser		=	new TankUser;
			this.tankBot		=	new TankBot;			// methods			this.start			=	function(node){
										// store the playing field node
										this.node = node;
										// hide it while it loads
										this.node.parentNode.onsubmit = this.returnFalse;
										this.node.parentNode.style.visibility = 'hidden';
										// keep track of the mouse position
										this.node.onmousemove = this.mouseTracker;
										// get the web-service path
										this.updateUrl = document.getElementById('tankUpdateUrl').value;
										// get the rank image path
										this.rankImage = unescape(document.getElementById('playerRank').innerHTML);
										// get the object's root nodes
										this.tankGrid.node = document.getElementById('tankGrid');
										this.tankInfo.node = document.getElementById('tankInfo');
										this.tankOptions.node = document.getElementById('tankOptions');
										this.tankShop.node = document.getElementById('tankShop');
										this.tankStatus.node = document.getElementById('tankStatus');
										this.tankProgress.node = document.getElementById('tankProgress');
										this.tankReward.node = document.getElementById('tankReward');
										this.tankUser.node = document.getElementById('tankUser');										// trigger the start-up functions in all modules
										this.tankFilter.startUp();
										this.tankGrid.startUp();
										this.tankInfo.startUp();
										this.tankOptions.startUp();
										this.tankShop.startUp();
										this.tankStatus.startUp();
										this.tankProgress.startUp();
										this.tankReward.startUp();
										this.tankUser.startUp();
										// set an update cycle for the modules
										this.reload();									}
			this.reload			=	function(updateSet){
										// store the update set or get an idle bot move
										this.updateSet = (updateSet!=null) ? updateSet : this.tankBot.getUpdateSet() ;
										// store the time of the update
										this.updateTime = '&upd=' + new Date().getTime();
										// call for an update
										classBehaviour.ajax.addRequest(this.updateUrl + this.updateTime + this.updateSet, this.update, this.tankProgress.update, null, null);
									}			this.update			=	function(updateXml, updateReferer, updateText){
										var tof = classBehaviour.tankOFurry;										// get a new version of the xml file
										tof.mapXml = updateXml;
										// trigger the update functions in all modules, but stop if anything goes wrong
										checkOkay = true;
										if(checkOkay) checkOkay = tof.tankFilter.update();
										if(checkOkay) checkOkay = tof.tankGrid.update();
										if(checkOkay) checkOkay = tof.tankStatus.update();
										if(checkOkay) checkOkay = tof.tankReward.update();
										// show the playing field
										if(checkOkay) tof.node.parentNode.style.visibility = 'visible';
										// set a timeout for the next reload if it wasn't set before
										if(checkOkay) tof.timeout = setTimeout("classBehaviour.tankOFurry.reload()", tof.delay);									}
			this.centerWindow	=	function(node){
										var tof = classBehaviour.tankOFurry;
										// how far is the map scrolled
										scrolledCanvas = document.getElementById('tankCanvas');
										// where is the mouse
										xPos = tof.mouseX;
										yPos = tof.mouseY;
										// compensate for the scrolling
										xPos = (xPos>scrolledCanvas.offsetWidth-128) ? xPos-128 : xPos ;
										yPos = (yPos>scrolledCanvas.offsetHeight-128) ? yPos-128 : yPos ;
										// show the tank info interface
										node.style.left = xPos + 'px' ;
										node.style.top =  yPos + 'px' ;
										node.style.display = 'block';
									}
			this.closeAll		=	function(){
										var tof = classBehaviour.tankOFurry;
										// remove the square highlights
										allGridButtons = tof.tankGrid.node.getElementsByTagName('button');
										for(var a=0; a<allGridButtons.length; a++) allGridButtons[a].className = ''; 
										// shut all interface windows
										tof.tankInfo.node.style.display = 'none';
										tof.tankOptions.node.style.display = 'none';
										tof.tankShop.node.style.display = 'none';
									}
			/* events */
			this.mouseTracker	=	function(that){
										var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
										var tof = classBehaviour.tankOFurry;
										// store the mouse position
										tof.mouseX = (typeof(event)!='undefined') ? event.clientX : that.clientX ;										tof.mouseY = (typeof(event)!='undefined') ? event.clientY : that.clientY ;
									}
			this.returnFalse	=	function(){
										return false;
									}		}
			function TankFilter(){
				// properties
				this.node		=	null;
				this.turn		=	0;
				this.shots		=	new Array();
				this.hits		=	new Array();
				// methods
				this.startUp 	=	function(){
										var tof = classBehaviour.tankOFurry;
										// for all the tiles on the map
										for(var a=0; a<tof.tankGrid.mapSize; a++){
											// note the turn number
											this.hits[a] = new Array(this.turn, '-');
											// note the turn number
											this.shots[a] = new Array(this.turn, '-');
										}
									}
				this.update		=	function(){
										var tof = classBehaviour.tankOFurry;
										// update the turn counter
										this.turn += 1;
										// for all the tiles in the map
										ids = ',';
										anythingHit = '';
										squares = tof.mapXml.getElementsByTagName('square');
										for(var square=0; square<tof.tankGrid.mapSize; square++){
											// check if the id is already on the map
											id = squares[square].getElementsByTagName('id')[0].firstChild.nodeValue;
											if(ids.indexOf(','+id+',')>-1 && id!='-'){
												// remove the clone
												updateSet = '&root=map&parent=square';
												updateSetA = '&indexa='+square+'&valuesa=bearing;-;cammo;-;drive;-;exp;-;heading;-;hit;-;id;-;key;-;name;-;shot;-;turret;-';
												updateSetB = '';
												tof.reload(updateSet + updateSetA + updateSetB);
												// cancel the current update
												return false;
											}else{
												// add it to the list
												ids += id + ',';
											}
											// is there's a hit on the map
											hit = squares[square].getElementsByTagName('hit')[0].firstChild.nodeValue;
											if(hit!='-'){
												// if the hit was noted in an earlier turn
												if(this.hits[square][1]==hit && this.hits[square][0]<this.turn){
													// remove it
													squares[square].getElementsByTagName('hit')[0].firstChild.nodeValue = '-';
												// else
												}else{
													// remember it
													this.hits[square][0] = this.turn;
													this.hits[square][1] = hit;
													anythingHit = hit;
												}
											}
											// is there's a shot on the map
											shot = squares[square].getElementsByTagName('shot')[0].firstChild.nodeValue;
											if(shot!='-'){
												// if the hit was noted in an earlier turn
												if(this.shots[square][1]==shot && this.shots[square][0]<this.turn){
													// remove it
													squares[square].getElementsByTagName('shot')[0].firstChild.nodeValue = '-';
												// else
												}else{
													// remember it
													this.shots[square][0] = this.turn;
													this.shots[square][1] = shot;
												}
											}
										}
										// produce a shooting or hitting sound
										if(anythingHit=='hit') classBehaviour.flashTitle.setFlashVar('soundPlayer1','strPlayUrl','./sounds/hit.mp3');
										if(anythingHit=='block') classBehaviour.flashTitle.setFlashVar('soundPlayer1','strPlayUrl','./sounds/block.mp3');
										if(anythingHit=='miss') classBehaviour.flashTitle.setFlashVar('soundPlayer1','strPlayUrl','./sounds/miss.mp3');
										// confirm success
										return true;
									}
				this.clear		=	function(square){
										if(square!=null){
											this.hits[square][0] = 0;
											this.hits[square][1] = '-';
											this.shots[square][0] = 0;
											this.shots[square][1] = '-';
										}
									}
			}
			function TankGrid(){
				// properties
				this.node			=	null;
				this.mapSize		=	256;
				this.mapRow			=	16;
				this.mapFocus		=	null;
				// methods
				this.startUp 		=	function(){
											var tof = classBehaviour.tankOFurry;
											// get the prototype node
											firstNode = this.node.getElementsByTagName('li')[0];
											// for all expected map squares
											for(var a=0; a<this.mapSize-1; a++){
												// clone the prototype node
												clonedNode = firstNode.cloneNode(true);
												firstNode.parentNode.appendChild(clonedNode);
											}
											// when the map is scrolled the windows should hide
											this.node.parentNode.onscroll = tof.closeAll;
										}
				this.update			=	function(){
											var tof = classBehaviour.tankOFurry;
											// adjust the size of the playing field to the screen
											if(document.documentElement.clientHeight){
												this.node.parentNode.style.height = (document.documentElement.clientHeight - 185) + 'px';
												this.node.parentNode.parentNode.style.height = (document.documentElement.clientHeight - 185) + 'px';
											}
											// for all map squares
											allMapTiles = this.node.getElementsByTagName('li');
											allMapNodes = tof.mapXml.getElementsByTagName('square');
											for(var a=0; a<allMapNodes.length; a++){
												// number the tile
												allMapTiles.id = 'tankTile' + a;
												// get the node information
												bearing = allMapNodes[a].getElementsByTagName('bearing')[0].firstChild.nodeValue;
												cammo = allMapNodes[a].getElementsByTagName('cammo')[0].firstChild.nodeValue;
												drive = allMapNodes[a].getElementsByTagName('drive')[0].firstChild.nodeValue;
												heading = allMapNodes[a].getElementsByTagName('heading')[0].firstChild.nodeValue;
												turret = allMapNodes[a].getElementsByTagName('turret')[0].firstChild.nodeValue;
												experience = parseInt(allMapNodes[a].getElementsByTagName('exp')[0].firstChild.nodeValue);
												shot = allMapNodes[a].getElementsByTagName('shot')[0].firstChild.nodeValue;
												hit = allMapNodes[a].getElementsByTagName('hit')[0].firstChild.nodeValue;
												pname = allMapNodes[a].getElementsByTagName('name')[0].firstChild.nodeValue;
												key = allMapNodes[a].getElementsByTagName('key')[0].firstChild.nodeValue;
												// attenuate the experience
												experience = tof.tankUser.attenuate(experience);
												// if the item is not scenery
												if(drive!='wreck' && drive!='trail' && drive!='-'){
													// if the item is camouflaged change its settings
													if(cammo!='-'){
														drive = 'cammo';
														turret = '-';
													}
													// if the item is not from the player, display the item with a modifier
													if(pname!=tof.tankUser.getPlayer() && key!=tof.tankUser.getKey()){
														drive += '_a';
														turret += '_a';
													}
												}
												// update tile classes
												allTileLayers = allMapTiles[a].getElementsByTagName('div');
												allTileLayers[0].className = drive + '_' + bearing;
												allTileLayers[1].className = turret + '_' + heading;
												allTileLayers[2].className = 'rank_' + parseInt(Math.log(experience+1)/Math.LN2);
												allTileLayers[3].className = hit;
												allTileLayers[4].className = shot + '_' + heading;
												// update the button
												button = allMapTiles[a].getElementsByTagName('button')[0];
												button.id = 'tankTileButton' + a;
												// if the square is not highlighted for moving or shooting
												if(button.className!='move' && button.className!='shoot'){
													// show the tank menu if this square contains the player's tank
													if(pname==tof.tankUser.getPlayer() && key==tof.tankUser.getKey()) button.onclick = tof.tankOptions.show
													// or show the info on the enemy tank
													else if(pname!='-') button.onclick = tof.tankInfo.show
													// or show the tank shop if the square contains nothing
													else button.onclick = tof.tankShop.show;
												}
											}
											// confirm success
											return true;
										}
				this.getMapIndex	=	function(node){
											// use the tile with the focus if no prefered one was passed
											if(node==null) node = this.mapFocus;
											// return its id
											return (node==null) ? null : parseInt(node.id.replace('tankTileButton',''));
										}
			}
			function TankInfo(){
				// properties
				this.node		=	null;
				// methods
				this.startUp 	=	function(){}
				// events
				this.show		=	function(that){
										var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
										var tof = classBehaviour.tankOFurry;
										if(tof.tankUser.loggedIn){
											// if the square is highlighted allready
											if(objNode.className == 'focus'){
												// clear it
												tof.closeAll();
											// else show the interface
											}else{
												// hide any other interface windows
												tof.closeAll();
												// highlight the selected square
												objNode.className = 'focus';
												tof.tankGrid.mapFocus = objNode;
												// fill the enemy player name
												squareIndex = tof.tankGrid.getMapIndex(objNode);
												squareName = tof.mapXml.getElementsByTagName('name')[squareIndex].firstChild.nodeValue;
												document.getElementById('enemyPlayer').firstChild.nodeValue = squareName;
												// fill in the enemy player rank
												squareRank = tof.tankUser.getStats(squareName).rank;
												document.getElementById('enemyRank').innerHTML = tof.rankImage.replace(/{rankNumber}/gi, squareRank).replace(/{rankName}/gi, tof.tankStatus.rankNames[squareRank]);
												// show the tank info interface
												tof.centerWindow(tof.tankInfo.node);
											}
										}
										// don't submit the page
										return false;
									}
			}
			function TankOptions(){
				// properties
				this.node		=	null;
				// methods
				this.startUp 	=	function(){
										// put event handlers on the buttons
										document.getElementById('tankMove').onclick = this.moveRange;
										document.getElementById('tankShoot').onclick = this.shootRange;
										document.getElementById('tankCammo').onclick = this.applyCammo;
									}
				this.findRange	=	function(indexA, indexB){
										var tof = classBehaviour.tankOFurry;
										// get the coordinates
										xPosA = indexA % tof.tankGrid.mapRow;
										yPosA = parseInt(indexA / tof.tankGrid.mapRow);
										xPosB = indexB % tof.tankGrid.mapRow;
										yPosB = parseInt(indexB / tof.tankGrid.mapRow);
										// return the distance
										return Math.sqrt(Math.pow(xPosA-xPosB, 2) + Math.pow(yPosA-yPosB, 2));
									}
				this.findAngle	=	function(indexA, indexB){
										var tof = classBehaviour.tankOFurry;
										// get the coordinates
										xPosA = indexA % tof.tankGrid.mapRow;
										yPosA = parseInt(indexA / tof.tankGrid.mapRow);
										xPosB = indexB % tof.tankGrid.mapRow;
										yPosB = parseInt(indexB / tof.tankGrid.mapRow);
										// translate it into clockwise octant numbers
										octant = 0;
										if(xPosA==xPosB && yPosA>yPosB) octant = 0;
										if(xPosA<xPosB && yPosA>yPosB) octant = 1;
										if(xPosA<xPosB && yPosA==yPosB) octant = 2;
										if(xPosA<xPosB && yPosA<yPosB) octant = 3;
										if(xPosA==xPosB && yPosA<yPosB) octant = 4;
										if(xPosA>xPosB && yPosA<yPosB) octant = 5;
										if(xPosA>xPosB && yPosA==yPosB) octant = 6;
										if(xPosA>xPosB && yPosA>yPosB) octant = 7;
										// return the direction
										return octant;
									}
				this.findHeight	=	function(indexA, indexB){
										var tof = classBehaviour.tankOFurry;
										// get the coordinates
									//	heightA = parseInt(tof.mapXml.getElementsByTagName('terrain')[indexA].firstChild.nodeValue);
									//	heightB = parseInt(tof.mapXml.getElementsByTagName('terrain')[indexB].firstChild.nodeValue);
										heightA = tof.heightMap[indexA];
										heightB = tof.heightMap[indexB];
										// return the distance
										return Math.abs(heightA-heightB);
									}
				// events
				this.show		=	function(that){
										var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
										var tof = classBehaviour.tankOFurry;
										if(tof.tankUser.loggedIn){
											// if the square has the focus
											if(objNode.className == 'focus'){
												// clear it
												tof.closeAll();
											// else show the interface
											}else{
												// hide any other interface windows
												tof.closeAll();
												// highlight the selected square
												objNode.className = 'focus';
												tof.tankGrid.mapFocus = objNode;
												// show the tank info interface
												tof.centerWindow(tof.tankOptions.node);
											}
										}
										// don't submit the page
										return false;
									}
				this.moveRange	=	function(that){
										var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
										var tof = classBehaviour.tankOFurry;
										// what kind of vehicle is this
										focusIndex = tof.tankGrid.getMapIndex();
										driveKind = tof.mapXml.getElementsByTagName('drive')[focusIndex].firstChild.nodeValue;
										// for all tiles
										allGridButtons = tof.tankGrid.node.getElementsByTagName('button');
										for(var tileIndex=0; tileIndex<allGridButtons.length; tileIndex++){
											// exception perks for certain drive types
											exception = false;
											if(driveKind=='track' && (tof.heightMap[tileIndex]==4 || tof.heightMap[focusIndex]==4)) exception = true;
											if(driveKind=='hover' && (tof.heightMap[tileIndex]==0 || tof.heightMap[focusIndex]==0)) exception = true;
											// if the tile is not the one in focus
											// and the tile is in an area surrounding the focus
											// and the tile is no more that 1 level of height difference, or an exception
											// and the tile contains no other player
											if(
												tileIndex!=focusIndex &&
												tof.tankOptions.findRange(tileIndex, focusIndex) < 2 &&
												(tof.tankOptions.findHeight(tileIndex, focusIndex) < 2 || exception) &&
												tof.mapXml.getElementsByTagName('name')[tileIndex].firstChild.nodeValue == '-'
											){
												// mark the tile
												allGridButtons[tileIndex].className = 'move';
												allGridButtons[tileIndex].onclick = tof.tankOptions.move;
											}
										}
										// hide the option window
										tof.tankOptions.node.style.display = 'none';
										// don't submit the page
										return false;
									}
				this.move		=	function(that){
										var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
										var tof = classBehaviour.tankOFurry;
										// cancel the next idle update
										clearTimeout(tof.timeout);
										
										// INPUT
										// get the properties of the source square
										sourceIndex = tof.tankGrid.getMapIndex();
										sourceBearing = tof.mapXml.getElementsByTagName('bearing')[sourceIndex].firstChild.nodeValue;
										sourceCammo = tof.mapXml.getElementsByTagName('cammo')[sourceIndex].firstChild.nodeValue;
										sourceDrive = tof.mapXml.getElementsByTagName('drive')[sourceIndex].firstChild.nodeValue;
										sourceExp = parseInt(tof.mapXml.getElementsByTagName('exp')[sourceIndex].firstChild.nodeValue);
										sourceHeading = tof.mapXml.getElementsByTagName('heading')[sourceIndex].firstChild.nodeValue;
										sourceHit = tof.mapXml.getElementsByTagName('hit')[sourceIndex].firstChild.nodeValue;
										sourceId = tof.mapXml.getElementsByTagName('id')[sourceIndex].firstChild.nodeValue;
										sourceKey = tof.mapXml.getElementsByTagName('key')[sourceIndex].firstChild.nodeValue;
										sourceName = tof.mapXml.getElementsByTagName('name')[sourceIndex].firstChild.nodeValue;
										sourceShot = tof.mapXml.getElementsByTagName('shot')[sourceIndex].firstChild.nodeValue;
										sourceTerrain = tof.heightMap[sourceIndex]; // tof.mapXml.getElementsByTagName('terrain')[sourceIndex].firstChild.nodeValue;
										sourceTurret = tof.mapXml.getElementsByTagName('turret')[sourceIndex].firstChild.nodeValue;
									   
										// PROCESSING
										// clear the filter for this item
										tof.tankFilter.clear(sourceIndex);
										// get the properties of the destination square
										destinationIndex = tof.tankGrid.getMapIndex(objNode);
										destinationName = tof.mapXml.getElementsByTagName('name')[destinationIndex].firstChild.nodeValue;
										destinationDrive = tof.mapXml.getElementsByTagName('drive')[destinationIndex].firstChild.nodeValue;
										// remove the camouflage (but the wheeled vehicle has a perk that keeps it on)
										if(sourceDrive!='wheel') sourceCammo = '-';
										// what cosmetic trail to leave
										sourceTrail = (sourceDrive!='hover') ? 'trail' : '-' ;
										// adjust the direction of the drive
										sourceBearing = tof.tankOptions.findAngle(sourceIndex, destinationIndex);
										// if we drive over wrecks we get bonus points for cleaning
										if(destinationDrive=='wreck') sourceExp += 1;
										
										// DISPLAY
										// play a move sound relevant to the drive
										if(sourceDrive=='track') 	classBehaviour.flashTitle.setFlashVar('soundPlayer0','strPlayUrl','./sounds/track.mp3');
										if(sourceDrive=='wheel') 	classBehaviour.flashTitle.setFlashVar('soundPlayer0','strPlayUrl','./sounds/wheel.mp3');
										if(sourceDrive=='hover') 	classBehaviour.flashTitle.setFlashVar('soundPlayer0','strPlayUrl','./sounds/hover.mp3');
										// adjust the visual before the next update
										tof.mapXml.getElementsByTagName('bearing')[sourceIndex].firstChild.nodeValue = sourceBearing ;
										tof.mapXml.getElementsByTagName('cammo')[sourceIndex].firstChild.nodeValue = sourceCammo ;
										tof.tankGrid.update();
										
										// OUTPUT
										// construct the update string
										updateSet = '&root=map&parent=square';
										updateSetA = '&indexa='+destinationIndex+'&valuesa=bearing;'+sourceBearing+';cammo;'+sourceCammo+';drive;'+sourceDrive+';exp;'+sourceExp+';heading;'+sourceHeading+';hit;'+sourceHit+';id;'+sourceId+';key;'+sourceKey+';name;'+sourceName+';shot;'+sourceShot+';turret;'+sourceTurret;
										updateSetB = '&indexb='+sourceIndex+'&valuesb=bearing;'+sourceBearing+';cammo;-;drive;'+sourceTrail+';exp;-;heading;-;hit;-;id;-;key;-;name;-;shot;-;terrain;'+sourceTerrain+';turret;-';
										// send the requested changes if the destination square is still free
										if(destinationName=='-') tof.reload(updateSet + updateSetA + updateSetB);
										// reset the interface
										tof.closeAll();
										// don't submit the page
										return false;
									}
				this.shootRange	=	function(that){
										var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
										var tof = classBehaviour.tankOFurry;
										// if the focus is above a certain level, the range is increased
										focusIndex = tof.tankGrid.getMapIndex();
										rangeLimit = (tof.heightMap[focusIndex]>3) ? 4 : 3 ;
										// for all tiles
										allGridButtons = tof.tankGrid.node.getElementsByTagName('button');
										for(var tileIndex=0; tileIndex<allGridButtons.length; tileIndex++){
											// if the tile is not the one in focus
											// and the tile is in an area surrounding the focus
											if(
												tileIndex!=focusIndex &&
												tof.tankOptions.findRange(tileIndex, focusIndex) < rangeLimit
											){
												// mark the tile
												allGridButtons[tileIndex].className = 'shoot';
												allGridButtons[tileIndex].onclick = tof.tankOptions.shoot;
											}
										}
										// hide the option window
										tof.tankOptions.node.style.display = 'none';
										// don't submit the page
										return false;
									}
				this.shoot		=	function(that){
										var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
										var tof = classBehaviour.tankOFurry;
										// cancel the next idle update
										clearTimeout(tof.timeout);
										
										// INPUT
										// properties of the source
										sourceIndex = tof.tankGrid.getMapIndex();
										sourceBearing = tof.mapXml.getElementsByTagName('bearing')[sourceIndex].firstChild.nodeValue;
										sourceCammo = tof.mapXml.getElementsByTagName('cammo')[sourceIndex].firstChild.nodeValue;
										sourceDrive = tof.mapXml.getElementsByTagName('drive')[sourceIndex].firstChild.nodeValue;
										rawSourceExp = parseInt(tof.mapXml.getElementsByTagName('exp')[sourceIndex].firstChild.nodeValue);
										sourceExp = (isNaN(rawSourceExp)) ? 0 : rawSourceExp;
										sourceHeading = tof.mapXml.getElementsByTagName('heading')[sourceIndex].firstChild.nodeValue;
										sourceHit = tof.mapXml.getElementsByTagName('hit')[sourceIndex].firstChild.nodeValue;
										sourceId = tof.mapXml.getElementsByTagName('id')[sourceIndex].firstChild.nodeValue;
										sourceKey = tof.mapXml.getElementsByTagName('key')[sourceIndex].firstChild.nodeValue;
										sourceName = tof.mapXml.getElementsByTagName('name')[sourceIndex].firstChild.nodeValue;
										sourceShot = tof.mapXml.getElementsByTagName('shot')[sourceIndex].firstChild.nodeValue;
										sourceTerrain = tof.mapXml.getElementsByTagName('terrain')[sourceIndex].firstChild.nodeValue;
										sourceTurret = tof.mapXml.getElementsByTagName('turret')[sourceIndex].firstChild.nodeValue;
										sourceRank = tof.tankUser.getStats(sourceName).rank;
										// clear the filter for this item
										tof.tankFilter.clear(sourceIndex);
										// properties of the target
										destinationIndex = tof.tankGrid.getMapIndex(objNode);
										destinationBearing = tof.mapXml.getElementsByTagName('bearing')[destinationIndex].firstChild.nodeValue;
										destinationCammo = tof.mapXml.getElementsByTagName('cammo')[destinationIndex].firstChild.nodeValue;
										destinationDrive = tof.mapXml.getElementsByTagName('drive')[destinationIndex].firstChild.nodeValue;
										rawDestinationExp = parseInt(tof.mapXml.getElementsByTagName('exp')[destinationIndex].firstChild.nodeValue);
										destinationExp = (isNaN(rawDestinationExp)) ? 0 : rawDestinationExp;
										destinationHeading = tof.mapXml.getElementsByTagName('heading')[destinationIndex].firstChild.nodeValue;
										destinationHit = tof.mapXml.getElementsByTagName('hit')[destinationIndex].firstChild.nodeValue;
										destinationId = tof.mapXml.getElementsByTagName('id')[destinationIndex].firstChild.nodeValue;
										destinationKey = tof.mapXml.getElementsByTagName('key')[destinationIndex].firstChild.nodeValue;
										destinationName = tof.mapXml.getElementsByTagName('name')[destinationIndex].firstChild.nodeValue;
										destinationShot = tof.mapXml.getElementsByTagName('shot')[destinationIndex].firstChild.nodeValue;
										destinationTerrain = tof.mapXml.getElementsByTagName('terrain')[destinationIndex].firstChild.nodeValue;
										destinationTurret = tof.mapXml.getElementsByTagName('turret')[destinationIndex].firstChild.nodeValue;
										destinationRank = tof.tankUser.getStats(destinationName).rank;
									   
										// PROCESSING
										// clear the filter for this item
										tof.tankFilter.clear(destinationIndex);
										// remove the camouflage
										sourceCammo = '-';
										// adjust the direction of the turret
										sourceHeading = tof.tankOptions.findAngle(sourceIndex, destinationIndex);
										// determine the outcome of the shot
										if(destinationName!=sourceName){
											// if we were shooting at the landscape
											if(destinationName=='-'){
												// set the shoot graphic for the source
												sourceShot = 'shot';
												// set the miss graphic for the target
												destinationHit = 'miss';
											}
											// win
											if(
										   		(sourceTurret=='cannon' && destinationDrive=='wheel') ||
										   		(sourceTurret=='missile' && destinationDrive=='track') ||
												(sourceTurret=='gun' && destinationDrive=='hover')
											){
												// set the shoot graphic for the source
												sourceShot = 'shot';
												// set the destroy graphic for the target
												destinationHit = 'hit';
												// update the rank bonusses
												sourceExp += destinationRank + 1;
												destinationExp = '-';
												// remove the losing tank
												destinationName = '-';
												destinationKey = '-';
												destinationTurret = '-';
												destinationDrive = 'wreck';
											}
											// draw
											if(
										   		(sourceTurret=='cannon' && destinationDrive=='track') ||
										   		(sourceTurret=='missile' && destinationDrive=='hover') ||
												(sourceTurret=='gun' && destinationDrive=='wheel')
											){
												// set the shoot graphic for the source
												sourceShot = 'shot';
												// set the deflect graphic for the target
												destinationHit = 'block';
												// update neither bonus
											}
											// lose
											if(
										   		(sourceTurret=='cannon' && destinationDrive=='hover') ||
										   		(sourceTurret=='missile' && destinationDrive=='wheel') ||
												(sourceTurret=='gun' && destinationDrive=='track')
											){
												/* LOST SHOTS MAKE THE ENEMY RETURN FIRE */
												// set the shoot graphic for the target
												sourceShot = 'shot';
												destinationShot = 'shot';
												// set the destroy graphic for the source
												sourceHit = 'hit';
												destinationHit = 'miss';
												// update the rank bonusses
												sourceExp = '-';
												destinationExp += sourceRank + 1;
												// remove the losing tank
												sourceName = '-';
												sourceKey = '-';
												sourceTurret = '-';
												sourceDrive = 'wreck';
												// adjust the winning tank
												destinationHeading = tof.tankOptions.findAngle(destinationIndex, sourceIndex);
												
												/* LOST SHOTS ONLY COST XP
												// set the shoot graphic for the source
												sourceShot = 'shot';
												// set the miss graphic for the target
												destinationHit = 'miss';
												// update the rank bonusses
												destinationExp += 1;
												sourceExp -= (sourceExp>1) ? 1 : 0 ;
												*/
											}
										   	// play a move sound relevant to the turret
											if(sourceTurret=='cannon') 	classBehaviour.flashTitle.setFlashVar('soundPlayer0','strPlayUrl','./sounds/cannon.mp3');
											if(sourceTurret=='missile') classBehaviour.flashTitle.setFlashVar('soundPlayer0','strPlayUrl','./sounds/missile.mp3');
											if(sourceTurret=='gun') 	classBehaviour.flashTitle.setFlashVar('soundPlayer0','strPlayUrl','./sounds/gun.mp3');
										}else{
											classBehaviour.flashTitle.setFlashVar('soundPlayer0','strPlayUrl','./sounds/wrong.mp3')
										}
										
										// DISPLAY
										// adjust the visual before the next update
										tof.mapXml.getElementsByTagName('heading')[sourceIndex].firstChild.nodeValue = sourceHeading ;
										tof.mapXml.getElementsByTagName('shot')[sourceIndex].firstChild.nodeValue = sourceShot ;
										tof.mapXml.getElementsByTagName('cammo')[sourceIndex].firstChild.nodeValue = sourceCammo ;
										tof.tankGrid.update();
										
										// OUTPUT
										// construct the update string
										updateSet = '&root=map&parent=square';
										updateSetA = '&indexa='+sourceIndex+'&valuesa=cammo;'+sourceCammo+';drive;'+sourceDrive+';exp;'+sourceExp+';heading;'+sourceHeading+';hit;'+sourceHit+';id;'+sourceId+';key;'+sourceKey+';name;'+sourceName+';shot;'+destinationShot+';turret;'+sourceTurret;
										updateSetB = '&indexb='+destinationIndex+'&valuesb=drive;'+destinationDrive+';exp;'+destinationExp+';heading;'+destinationHeading+';hit;'+destinationHit+';id;'+destinationId+';key;'+destinationKey+';name;'+destinationName+';shot;'+destinationShot+';turret;'+destinationTurret;
										// send the changes to the server
										tof.reload(updateSet + updateSetA + updateSetB);
										// reset the interface
										tof.closeAll();
										// don't submit the page
										return false;
									}
				this.applyCammo	=	function(that){
										var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
										var tof = classBehaviour.tankOFurry;
										// cancel the next idle update
										clearTimeout(tof.timeout);
										
										// INPUT
										// properties of the source
										sourceIndex = tof.tankGrid.getMapIndex();
										sourceBearing = tof.mapXml.getElementsByTagName('bearing')[sourceIndex].firstChild.nodeValue;
										sourceCammo = tof.mapXml.getElementsByTagName('cammo')[sourceIndex].firstChild.nodeValue;
										sourceDrive = tof.mapXml.getElementsByTagName('drive')[sourceIndex].firstChild.nodeValue;
										sourceExp = parseInt(tof.mapXml.getElementsByTagName('exp')[sourceIndex].firstChild.nodeValue);
										sourceHeading = tof.mapXml.getElementsByTagName('heading')[sourceIndex].firstChild.nodeValue;
										sourceHit = tof.mapXml.getElementsByTagName('hit')[sourceIndex].firstChild.nodeValue;
										sourceId = tof.mapXml.getElementsByTagName('id')[sourceIndex].firstChild.nodeValue;
										sourceKey = tof.mapXml.getElementsByTagName('key')[sourceIndex].firstChild.nodeValue;
										sourceName = tof.mapXml.getElementsByTagName('name')[sourceIndex].firstChild.nodeValue;
										sourceShot = tof.mapXml.getElementsByTagName('shot')[sourceIndex].firstChild.nodeValue;
										sourceTerrain = tof.mapXml.getElementsByTagName('terrain')[sourceIndex].firstChild.nodeValue;
										sourceTurret = tof.mapXml.getElementsByTagName('turret')[sourceIndex].firstChild.nodeValue;
									   
										// PROCESSING
										// apply the cammo value
										sourceCammo = (sourceCammo=='-') ? '1' : '-' ;
										
										// DISPLAY
										// adjust the visual before the next update
										tof.mapXml.getElementsByTagName('cammo')[sourceIndex].firstChild.nodeValue = '1' ;
										tof.tankGrid.update();
										
										// OUTPUT
										// construct the update string
										updateSet = '&root=map&parent=square';
										updateSetA = '&indexa='+sourceIndex+'&valuesa=cammo;'+sourceCammo;
										updateSetB = '';
										// send the changes to the server
										tof.reload(updateSet + updateSetA + updateSetB);
										// reset the interface
										tof.closeAll();
										// don't submit the page
										return false;
									}
			}
			function TankShop(){
				// properties
				this.node		=	null;
				// methods
				this.startUp 	=	function(){
										// put event handlers on the buttons
										document.getElementById('shopBuy').onclick = this.addTank;
										document.getElementById('shopCancel').onclick = classBehaviour.tankOFurry.closeAll;
									}
				// events
				this.show		=	function(that){
										var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
										var tof = classBehaviour.tankOFurry;
										if(tof.tankUser.loggedIn){
											// if the square is highlighted allready
											if(objNode.className == 'focus'){
												// clear it
												tof.closeAll();
											// else show the interface
											}else{
												// hide any other interface windows
												tof.closeAll();
												// highlight the selected square
												objNode.className = 'focus';
												tof.tankGrid.mapFocus = objNode;
												rankStats = tof.tankUser.getStats();
												// does the rank and terain allow for the placement of a new tank
												allowLimit = (rankStats.tanks<rankStats.tanksLimit);
												allowTerrain = (parseInt(tof.mapXml.getElementsByTagName('terrain')[tof.tankGrid.getMapIndex()].firstChild.nodeValue)>-1);
												// show the error if there are too many tanks already
												document.getElementById('shopLimitError').style.display = (allowLimit) ? 'none' : 'block' ;
												// show the error if the terrain is unsuitable
												document.getElementById('shopTerrainError').style.display = (allowTerrain) ? 'none' : 'block' ;
												// show the tank shop if the player has too little tanks
												document.getElementById('shopInterface').style.display = (allowLimit && allowTerrain) ? 'block' : 'none' ;
												// show the shop interface
												tof.centerWindow(tof.tankShop.node);
											}
										}
										// don't submit the page
										return false;
									}
				this.addTank	=	function(that){
										var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
										var tof = classBehaviour.tankOFurry;
										// cancel the next idle update
										clearTimeout(tof.timeout);
										
										// INPUT
										// gather the properties of this square
										destinationIndex = tof.tankGrid.getMapIndex();
										destinationBearing = '0';
										destinationCammo = '1';
										destinationDrive = document.getElementById('shopDrive').value;
										destinationExp = 0;
										destinationHeading = '0';
										destinationHit = '-';
										destinationId = new Date().getTime();
										destinationKey = tof.tankUser.getKey();
										destinationName = tof.tankUser.getPlayer();
										destinationShot = '-';
										destinationTerrain = tof.heightMap[destinationIndex];
										destinationTurret = document.getElementById('shopTurret').value;
										
										// DISPLAY
										// play a sound
										if(destinationDrive=='track') 	classBehaviour.flashTitle.setFlashVar('soundPlayer0','strPlayUrl','./sounds/track.mp3');
										if(destinationDrive=='wheel') 	classBehaviour.flashTitle.setFlashVar('soundPlayer0','strPlayUrl','./sounds/wheel.mp3');
										if(destinationDrive=='hover') 	classBehaviour.flashTitle.setFlashVar('soundPlayer0','strPlayUrl','./sounds/hover.mp3');
										
										// OUTPUT
										// construct the update string
										updateSet = '&root=map&parent=square';
										updateSetA = '&indexa='+destinationIndex+'&valuesa=bearing;'+destinationBearing+';cammo;'+destinationCammo+';drive;'+destinationDrive+';exp;'+destinationExp+';heading;'+destinationHeading+';hit;'+destinationHit+';id;'+destinationId+';key;'+destinationKey+';name;'+destinationName+';shot;'+destinationShot+';terrain;'+destinationTerrain+';turret;'+destinationTurret;
										updateSetB = '';
										// send the requested changes
										tof.reload(updateSet + updateSetA + updateSetB);
										// close the shop window
										tof.closeAll();
										// don't submit the page
										return false;
									}
			}
			function TankStatus(){
				// properties
				this.node		=	null;
				this.oldRank	=	0;
				this.rankNames	=	new Array('Private', 'Corporal', 'Sergeant', 'Lieutenant', 'Captain','Major', 'Colonel', 'General');
				// methods
				this.startUp 	=	function(){
										// make the status panel clickable
										this.node.onclick = this.move;
										this.node.style.left = 'auto';
									}
				this.update		=	function(){
										var tof = classBehaviour.tankOFurry;
										// set player name
										document.getElementById('playerName').innerHTML = tof.tankUser.getPlayer();
										// get the player's experience count
										stats = tof.tankUser.getStats();
										// display the rank
										document.getElementById('playerRank').innerHTML = tof.rankImage.replace(/{rankNumber}/gi, stats.rank).replace(/{rankName}/gi, this.rankNames[stats.rank]);
										// display the experience and the next promotion
										document.getElementById('playerBonus').style.backgroundPosition = (100-stats.experience/stats.experienceLimit*100) +'% 0%' ;
										document.getElementById('playerBonus').innerHTML = 'XP: ' + Math.round(stats.experience/stats.experienceLimit*100) + ' %';
										// display the amount of tanks and the limit
										document.getElementById('playerTanks').innerHTML = stats.tanks + ' of ' + stats.tanksLimit + ' tanks';
										// update the remembered rank
										this.oldRank = stats.rank;
										// update the highscore table
										this.highscores();
										// confirm success
										return true;
									}
				this.highscores	=	function(){
										var tof = classBehaviour.tankOFurry;
										// create an empty list of players
										var playerScores = new Array();
										// for all squares on the map
										var allSquareNames = tof.mapXml.getElementsByTagName('name');
										for(var a=0; a<allSquareNames.length; a++){
											// fetch the player name
											var squareName = allSquareNames[a].firstChild.nodeValue;
											// if this square is a player
											if(squareName!='-' && squareName.length == squareName.replace(/^\s+|\s+$/g, '').length){
												// check if the name is not already in the list
												if((','+playerScores.join(',')+',').indexOf(','+squareName+',')<0){
													// get the player's stats
													var statsName = squareName + '';
													var squareStats = tof.tankUser.getStats(statsName);
													// make a new entry in the list
													playerScores[playerScores.length] = new Array(squareStats.score, squareStats.rank, squareName);
												}
											}
										}
										// sort the list
										playerScores = playerScores.sort(function(a,b){return parseInt(b)-parseInt(a)});
										playerScores[playerScores.length] = new Array(0,0,'-');
										playerScores[playerScores.length] = new Array(0,0,'-');
										playerScores[playerScores.length] = new Array(0,0,'-');
										playerScores[playerScores.length] = new Array(0,0,'-');
										playerScores[playerScores.length] = new Array(0,0,'-');
										// for every item in the highscore list
										allHighscores = document.getElementById('playerHighscores').getElementsByTagName('li');
										for(var a=0; a<allHighscores.length; a++){
											// write the player name
											allHighscores[a].innerHTML = '<em>' + Math.round(playerScores[a][0]) + '</em>' + playerScores[a][2];
											// add the player rank
											allHighscores[a].className = 'rank_' + playerScores[a][1];
										}
									}
				// events
				this.move		=	function(that){
										var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
										var tof = classBehaviour.tankOFurry;
										// the progress indicator should move to
										tof.tankProgress.node.style.right = (objNode.style.left!='auto') ? '16px' : 'auto' ;
										tof.tankProgress.node.style.left = (objNode.style.left!='auto') ? 'auto' : '-1px' ;
										// toggle the menu position
										objNode.style.right = (objNode.style.left!='auto') ? '16px' : 'auto' ;
										objNode.style.left = (objNode.style.left!='auto') ? 'auto' : '-1px' ;
									}
			}
			function TankProgress(){
				// properties
				this.node		=	null;
				// methods
				this.startUp 	=	function(){}
				this.update		=	function(progressStatus, progressReferer, progressError){
										var tof = classBehaviour.tankOFurry;
										var tofp = classBehaviour.tankOFurry.tankProgress;
										if(progressStatus!=null){
											tofp.node.style.backgroundPosition = (progressStatus<0) ? '0% 100%' : (100-progressStatus*100) +'% 0%' ;
											tofp.node.innerHTML = (progressStatus<0) ? 'error: ' + progressError : 'update: ' + Math.round(progressStatus*100) + '%' ;
											tofp.node.style.display = 'block';
										}
										if(progressStatus<0){
											tof.timeout = setTimeout("classBehaviour.tankOFurry.reload()", tof.delay);
										}
									}
			}
			function TankReward(){
				// properties
				this.node		=	null;
				this.showed		=	-1;
				// methods
				this.startUp 	=	function(){
										this.node.onclick = this.hide;
									}
				this.update		=	function(){
										var tof = classBehaviour.tankOFurry;
										// if a player is logged in
										if(tof.tankUser.loggedIn){
											// show the rank's message if a new rank was reached, but not shown before
											rank = tof.tankUser.getStats().rank;
											if(rank>this.showed) this.show(rank);
											// remember the last rank shown
											this.showed = rank;
										}
										// confirm success
										return true;
									}
				// events
				this.show		=	function(rank){
										var tof = classBehaviour.tankOFurry;
										// hide any other interface windows
										tof.closeAll();
										// show the right promotion message
										messages = this.node.getElementsByTagName('li');
										for(var message=0; message<messages.length; message++) messages[message].style.display = (rank==message) ? 'block' : 'none' ;
										this.node.style.display = 'block';
									}
				this.hide		=	function(that){
										var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
										// hide the promotion message
										objNode.style.display = 'none';
									}
			}
			function TankUser(){
				// properties
				this.node		=	null;
				this.loggedIn	=	false;
				// methods
				this.startUp 	=	function(){
										// show the login interface
										this.node.style.display = 'block';
										// add the event handler to the button
										document.getElementById('tankLogIn').onclick = this.logIn;
									}
				this.getPlayer	=	function(){
										// take the name the player filled in
										var playerName = (this.loggedIn) ? document.getElementById('tankName').value : '' ;
										// pass it back
										return playerName;
									}
				this.getKey		=	function(){
										// take the key the player filled in
										playerKey = document.getElementById('tankKey').value;
										// process it ireversably
										hashedKey = 0;
										for (var a=0; a<playerKey.length; a++){
											switch(playerKey.charAt(a)){
												case '0' : hashedKey += 1; break;	case '1' : hashedKey += 1; break;
												case '2' : hashedKey += 2; break;	case '3' : hashedKey += 3; break;
												case '4' : hashedKey += 4; break;	case '5' : hashedKey += 5; break;
												case '6' : hashedKey += 6; break;	case '7' : hashedKey += 7; break;
												case '8' : hashedKey += 8; break;	case '9' : hashedKey += 9; break;
												case '0' : hashedKey += 0; break;
												case 'a' : hashedKey += 1; break;	case 'b' : hashedKey += 2; break;
												case 'c' : hashedKey += 3; break;	case 'd' : hashedKey += 4; break;
												case 'e' : hashedKey += 5; break;	case 'f' : hashedKey += 6; break;
												case 'g' : hashedKey += 7; break;	case 'h' : hashedKey += 8; break;
												case 'i' : hashedKey += 9; break;	case 'j' : hashedKey += 10; break;
												case 'k' : hashedKey += 11; break;	case 'l' : hashedKey += 12; break;
												case 'm' : hashedKey += 13; break;	case 'n' : hashedKey += 14; break;
												case 'o' : hashedKey += 15; break;	case 'p' : hashedKey += 16; break;
												case 'q' : hashedKey += 17; break;	case 'r' : hashedKey += 18; break;
												case 's' : hashedKey += 19; break;	case 't' : hashedKey += 20; break;
												case 'u' : hashedKey += 21; break;	case 'v' : hashedKey += 22; break;
												case 'w' : hashedKey += 23; break;	case 'x' : hashedKey += 24; break;
												case 'y' : hashedKey += 25; break;	case 'z' : hashedKey += 26; break;
												default : hashedKey += 0;
											}
										}
										// pass it back
										return (this.loggedIn) ? hashedKey : 0 ;
									}
				this.getStats	=	function(playerName){
										var tof = classBehaviour.tankOFurry;
										// for all the squares
										playerName = (playerName!=null) ? playerName : tof.tankUser.getPlayer();
										tanks = 0;
										score = 1;
										squares = tof.mapXml.getElementsByTagName('square');
										for(var square=0; square<squares.length; square++){
											// if the tank belongs to the player
											squareName = squares[square].getElementsByTagName('name')[0].firstChild.nodeValue;
											if(playerName==squareName){
												// count the tank
												tanks += 1;
												// count its experience
												rawScore = parseInt(squares[square].getElementsByTagName('exp')[0].firstChild.nodeValue)
												score += (isNaN(rawScore)) ? 0 : rawScore ;
											}
										}
										// attenuate the experience
										experience = this.attenuate(score);
										experience = (experience>Math.pow(2,7)) ? Math.pow(2,7) : experience ;
										// look up the rank that goes with it
										rank = parseInt(Math.log(experience)/Math.LN2);
										// calculate the next rank level
										rankLimit = rank + 1;
										// calculate the amount of tanks allowed
										tanksLimit = rank + 1;
										// calculate the next experience level
										previousLimit = Math.pow(2, rank);
										experienceLimit = Math.pow(2, rankLimit);
										// return the rank
										var stats = new Stats;
										stats.tanks = tanks;
										stats.tanksLimit = tanksLimit; 
										stats.experience = experience - previousLimit; 
										stats.experienceLimit = experienceLimit - previousLimit;
										stats.rank = rank;
										stats.rankLimit = rankLimit;
										stats.score = score - 1;
										return stats;
									}
				this.attenuate	=	function(experience){
										return (experience - 1)/2 + 1;
									}
				// events
				this.logIn		=	function(that){
										var objNode = (typeof(this.nodeName)=='undefined') ? that : this ;
										var tof = classBehaviour.tankOFurry;
										// if both fields were filled in
										if(document.getElementById('tankName').value!='' && document.getElementById('tankKey').value!=''){
											// trim the name
											document.getElementById('tankName').value = document.getElementById('tankName').value.replace(/^\s+|\s+$/g, '');
											// accept the login
											tof.tankUser.loggedIn = true;
											// hide the login interface
											tof.tankUser.node.style.left = '-1000px';
											// show the interface if there is a login
											tof.tankStatus.node.style.display = (tof.tankUser.loggedIn) ? 'block' : 'none' ;
											tof.tankProgress.node.style.display = (tof.tankUser.loggedIn) ? 'block' : 'none' ;
										}
										// don't submit the page
										return false;
									}
			}
				function Stats(){
					this.tanks = null; 
					this.tanksLimit = null; 
					this.experience = null; 
					this.experienceLimit = null; 
					this.rank = null;
					this.rankLimit = null;
					this.score = null;
				}
			function TankBot(){
				this.getUpdateSet	=	function(){
											var tof = classBehaviour.tankOFurry;
											// EMPTY DATA SET
											// update strings
											updateSet = '&root=map&parent=square';
											updateSetA = '';
											updateSetB = '';
											// if the map xml exists
											if(tof.mapXml!=null && tof.tankUser.getPlayer()!='' && tof.tankUser.getKey()!=0){
												// BOT LOGIC
												// It will scan the entire map and for each square
												playerNemesis = tof.tankUser.getPlayer().split('').reverse().join('').toLowerCase() + ' ';
												allEnemies = new Array();
												cannonTank = null;
												missileTank = null;
												gunTank = null;
												allSquares = tof.mapXml.getElementsByTagName('square');
												for(var a=0; a<allSquares.length; a++){
													squareName = allSquares[a].getElementsByTagName('name')[0].firstChild.nodeValue;
													// Add any enemies to an array.
													if(allSquares[a].getElementsByTagName('name')[0].firstChild.nodeValue==playerNemesis){
														squareTurret = allSquares[a].getElementsByTagName('turret')[0].firstChild.nodeValue;
														// Add the cannon tank its variable.
														if(squareTurret=='cannon') cannonTank = a;
														// Add the missile tank to its variable.
														if(squareTurret=='missile') missileTank = a;
														// Add the machine gun tank to its variable.
														if(squareTurret=='gun') gunTank = a;
													}else if(squareName!='-'){
														allEnemies[allEnemies.length] = a;
													}
												}
												// If it doesn't find its cannon tank, it will order a new one in a random spot if it's free.
												if(cannonTank==null){
													// pick a random spot
													randomSpot = Math.round((allSquares.length - 1) * Math.random());
													randomIndex = Math.round(Math.random()*2.9-0.49);
													randomDrives = new Array('track', 'wheel', 'hover');
													// construct the update string if there's nothing there
													if(updateSetA=='' && tof.mapXml.getElementsByTagName('name')[randomSpot].firstChild.nodeValue=='-' && tof.heightMap[randomSpot]>0 && tof.heightMap[randomSpot]<9)
														updateSetA = '&indexa='+randomSpot+'&valuesa=bearing;0;cammo;-;drive;'+randomDrives[0]+';exp;1;heading;0;hit;-;id;'+(new Date().getTime())+'a;key;666;name;'+playerNemesis+';shot;-;turret;cannon';
												}
												// If it doesn't find its machine gun tank, it will order a new one in a random spot if it's free.
												if(gunTank==null){
													// pick a random spot
													randomSpot = Math.round((allSquares.length - 1) * Math.random());
													randomIndex = Math.round(Math.random()*2.9-0.49);
													randomDrives = new Array('track', 'wheel', 'hover');
													// construct the update string if there's nothing there
													if(updateSetA=='' && tof.mapXml.getElementsByTagName('name')[randomSpot].firstChild.nodeValue=='-' && tof.heightMap[randomSpot]>0 && tof.heightMap[randomSpot]<9) 
														updateSetA = '&indexa='+randomSpot+'&valuesa=bearing;0;cammo;-;drive;'+randomDrives[1]+';exp;1;heading;0;hit;-;id;'+(new Date().getTime())+'b;key;666;name;'+playerNemesis+';shot;-;turret;gun';
												}
												// If it doesn't find its missile tank, it will order a new one in a random spot if it's free.
												if(missileTank==null){
													// pick a random spot
													randomSpot = Math.round((allSquares.length - 1) * Math.random());
													randomIndex = Math.round(Math.random()*2.9-0.49);
													randomDrives = new Array('track', 'wheel', 'hover');
													// construct the update string if there's nothing there
													if(updateSetA=='' && tof.mapXml.getElementsByTagName('name')[randomSpot].firstChild.nodeValue=='-' && tof.heightMap[randomSpot]>0 && tof.heightMap[randomSpot]<9) 
														updateSetA = '&indexa='+randomSpot+'&valuesa=bearing;0;cammo;-;drive;'+randomDrives[2]+';exp;1;heading;0;hit;-;id;'+(new Date().getTime())+'c;key;666;name;'+playerNemesis+';shot;-;turret;missile';
												}
												// stop processing if an update set was already made
												if(updateSetA!='') updateSet + updateSetA + updateSetB;
												// It will scan all enemies to find if any are in shooting range
												for(var a=0; a<allEnemies.length; a++){
													enemyTank = allEnemies[a];
													enemyDrive = allSquares[enemyTank].getElementsByTagName('drive')[0].firstChild.nodeValue;
													enemyCammo = allSquares[enemyTank].getElementsByTagName('cammo')[0].firstChild.nodeValue;
													enemyBearing = allSquares[enemyTank].getElementsByTagName('bearing')[0].firstChild.nodeValue;
													enemyName = allSquares[enemyTank].getElementsByTagName('name')[0].firstChild.nodeValue;
													enemyStats = tof.tankUser.getStats(enemyName);
													enemyRankz = enemyStats.rank;
													// If the enemy is wheeled and in range of the cannon tank, shoot it.
													if(tof.tankOptions.findRange(cannonTank, enemyTank)<3 && enemyDrive=='wheel' && enemyCammo=='-'){
														if(updateSetA=='' && allSquares[cannonTank]){
															updateExp = parseInt(allSquares[cannonTank].getElementsByTagName('exp')[0].firstChild.nodeValue);
															updateSetA = '&indexa='+cannonTank+'&valuesa=cammo;-;drive;track;exp;'+(updateExp+enemyRankz+1)+';heading;'+tof.tankOptions.findAngle(cannonTank, enemyTank)+';key;666;name;'+playerNemesis+';shot;shot;';
															updateSetB = '&indexb='+enemyTank+'&valuesb=bearing;'+enemyBearing+';cammo;-;drive;wreck;exp;-;heading;-;hit;hit;key;-;name;-;shot;-;turret;-';
														}
													}
													// If the enemy is tracked and in range of the missile gun tank, shoot it.
													if(tof.tankOptions.findRange(missileTank, enemyTank)<3 && enemyDrive=='track' && enemyCammo=='-'){
														if(updateSetA=='' && allSquares[missileTank]){
															updateExp = parseInt(allSquares[missileTank].getElementsByTagName('exp')[0].firstChild.nodeValue);
															updateSetA = '&indexa='+missileTank+'&valuesa=cammo;-;drive;track;exp;'+(updateExp+enemyRankz+1)+';heading;'+tof.tankOptions.findAngle(missileTank, enemyTank)+';key;666;name;'+playerNemesis+';shot;shot;';
															updateSetB = '&indexb='+enemyTank+'&valuesb=bearing;'+enemyBearing+';cammo;-;drive;wreck;exp;-;heading;-;hit;hit;key;-;name;-;shot;-;turret;-';
														}
													}
													// If the enemy is hovered and in range of the gun tank, shoot it.
													if(tof.tankOptions.findRange(gunTank, enemyTank)<3 && enemyDrive=='hover' && enemyCammo=='-'){
														if(updateSetA=='' && allSquares[gunTank]){
															updateExp = parseInt(allSquares[gunTank].getElementsByTagName('exp')[0].firstChild.nodeValue);
															updateSetA = '&indexa='+gunTank+'&valuesa=cammo;-;drive;track;exp;'+(updateExp+enemyRankz+1)+';heading;'+tof.tankOptions.findAngle(gunTank, enemyTank)+';key;666;name;'+playerNemesis+';shot;shot;';
															updateSetB = '&indexb='+enemyTank+'&valuesb=bearing;'+enemyBearing+';cammo;-;drive;wreck;exp;-;heading;-;hit;hit;key;-;name;-;shot;-;turret;-';
														}
													}
												}
												// stop processing if an update set was already made
												if(updateSetA!='') updateSet + updateSetA + updateSetB;
												// In a new array of possible movements.
												allMovements = new Array();
												// It will add a new spot randomly to the cannon tank's north/south/neither and east/west/neither if its free.
												newLocation = cannonTank + (Math.round(Math.random()*2-1)) + (Math.round(Math.random()*2-1))*16;
												if(newLocation>=0 && newLocation<tof.tankGrid.mapSize)
													if(tof.mapXml.getElementsByTagName('name')[newLocation].firstChild.nodeValue=='-' && tof.tankOptions.findHeight(cannonTank, newLocation)<2)
														allMovements[allMovements.length] = new Array(cannonTank, newLocation);
												// It will add a new spot randomly to the machine gun tank's north/south/neither and east/west/neither if its free.
												newLocation = gunTank + (Math.round(Math.random()*2-1)) + (Math.round(Math.random()*2-1))*16;
												if(newLocation>=0 && newLocation<tof.tankGrid.mapSize)
													if(tof.mapXml.getElementsByTagName('name')[newLocation].firstChild.nodeValue=='-' && tof.tankOptions.findHeight(gunTank, newLocation)<2)
														allMovements[allMovements.length] = new Array(gunTank, newLocation);
												// It will add a new spot randomly to the missile tank's north/south/neither and east/west/neither if its free.
												newLocation = missileTank + (Math.round(Math.random()*2-1)) + (Math.round(Math.random()*2-1))*16;
												if(newLocation>=0 && newLocation<tof.tankGrid.mapSize)
													if(tof.mapXml.getElementsByTagName('name')[newLocation].firstChild.nodeValue=='-' && tof.tankOptions.findHeight(missileTank, newLocation)<2)
														allMovements[allMovements.length] = new Array(missileTank, newLocation);
												// If will pick a random available movement from the list if there's any entries.
												if(updateSetA=='' && allMovements.length>0){
													newMovement = allMovements[Math.round(Math.random()*(allMovements.length-1))];
													// get the properties of the source square
													sourceIndex = newMovement[0];
													sourceBearing = tof.mapXml.getElementsByTagName('bearing')[sourceIndex].firstChild.nodeValue;
													sourceCammo = tof.mapXml.getElementsByTagName('cammo')[sourceIndex].firstChild.nodeValue;
													sourceDrive = tof.mapXml.getElementsByTagName('drive')[sourceIndex].firstChild.nodeValue;
													sourceExp = tof.mapXml.getElementsByTagName('exp')[sourceIndex].firstChild.nodeValue;
													sourceHeading = tof.mapXml.getElementsByTagName('heading')[sourceIndex].firstChild.nodeValue;
													sourceHit = tof.mapXml.getElementsByTagName('hit')[sourceIndex].firstChild.nodeValue;
													sourceId = tof.mapXml.getElementsByTagName('id')[sourceIndex].firstChild.nodeValue;
													sourceKey = tof.mapXml.getElementsByTagName('key')[sourceIndex].firstChild.nodeValue;
													sourceName = tof.mapXml.getElementsByTagName('name')[sourceIndex].firstChild.nodeValue;
													sourceShot = tof.mapXml.getElementsByTagName('shot')[sourceIndex].firstChild.nodeValue;
													sourceTerrain = tof.heightMap[sourceIndex]; // tof.mapXml.getElementsByTagName('terrain')[sourceIndex].firstChild.nodeValue;
													sourceTurret = tof.mapXml.getElementsByTagName('turret')[sourceIndex].firstChild.nodeValue;
													// get the properties of the destination square
													destinationIndex = newMovement[1];
													// remove the camouflage
													sourceCammo = '-';
													// what cosmetic trail to leave
													sourceTrail = (sourceDrive!='hover') ? 'trail' : '-' ;
													// adjust the direction of the drive
													sourceBearing = tof.tankOptions.findAngle(newMovement[0], newMovement[1]);
													// build the update set
													updateSetA = '&indexa='+destinationIndex+'&valuesa=bearing;'+sourceBearing+';cammo;'+sourceCammo+';drive;'+sourceDrive+';exp;'+sourceExp+';heading;'+sourceHeading+';hit;'+sourceHit+';id;'+sourceId+';key;'+sourceKey+';name;'+sourceName+';shot;'+sourceShot+';turret;'+sourceTurret;
													updateSetB = '&indexb='+sourceIndex+'&valuesb=bearing;'+sourceBearing+';cammo;-;drive;'+sourceTrail+';exp;-;heading;-;hit;-;id;-;key;-;name;-;shot;-;terrain;'+sourceTerrain+';turret;-';
												}
											}
											// UPDATE STRING
											// send the requested changes
											return (updateSetA!='') ? updateSet + updateSetA + updateSetB : '' ;
										}
			}
				// add this function to the classbehaviour object		classBehaviour.tankOFurry = new TankOFurry;		classBehaviour.handlers[classBehaviour.handlers.length] = classBehaviour.tankOFurry;

