/**
 * La classe Road représente un circuit, ouvert ou en boucle, découpé en segments.
 * Un segment porte :
 *  - une altitude
 *  - une courbure
 *  - deux éléments de décor
 */

function RoadRetrievalHelper(road)
{
	this.road = road;
}
 
RoadRetrievalHelper.prototype = {

	/**
	 * Point d'entrée principal pour la création d'une route.
	 * Pour le niveau 1, utilise le niveau hardcodé, cf commentaire de createLevel1Road()
	 * Pour les autres, crée une requête Ajax pour demander une description du circuit,
	 * puis une fois celle-ci complétée, rappelle la Road pour exploiter les infos reçues
	 */
	invoke : function(mode, raceNumber, car, loader)
	{
		if (raceNumber == 0) {
			// niveau de test pour le design du circuit, on rebranche directement sur la callback
			this.road.createLevel1Road();
			loader.extractImages();
		} else {
			this.loader = loader;
			this.ajaxHandler = new AjaxHandler();
			this.ajaxHandler.retrieveRoad(mode, raceNumber, car, "", this);
		}
	},
	
	/**
	 * Callback de changement d'état, pour la mise à jour de la barre de progression
	 */
	updateAjaxProgress : function(value)
	{
		this.loader.ajaxProgress = value;
	},

	/**
	 * Callback de fin d'appel Ajax, récupère le résultat, s'il est valide en extrait
	 * les informations, et rappelle le RaceLoader.
	 */
	ajaxCallCompleted : function()
	{
		var trackDesc = '';
		if (this.ajaxHandler.success) {
			trackDesc = this.ajaxHandler.response;
		}
		this.road.createRoadFromDescription (trackDesc);
		var loader = this.loader;
		this.loader = null; // on évite les références circulaires
		loader.extractImages();
	}
}

function RaceTimeSubmissionHelper(road)
{
	this.road = road;
}
 
RaceTimeSubmissionHelper.prototype = {

	/**
	 * Point d'entrée principal pour l'envoi de la télémétrie d'une course et du temps effectué.
	 * Vérifie que le temps est un record pour le joueur, et initie un appel Ajax
	 * destiné à enregistrer cette course dans la base de données
	 */
	invoke : function(mode, raceNumber, car, time, recording, raceResult) 
	{
		// on envoie systématiquement la course pour validation, et non plus que
		// si le joueur a battu son propre record
		// Raison : des conditions d'obtention des voitures sont indépendantes du temps
		// de course. On peut donc les remplir sans avoir battu son record, et il faut
		// une validation serveur pour débloquer une voiture ou un mode de jeu. 
		this.raceResult = raceResult;
		this.road.lapRecordUpdateCompleted = false;
		this.ajaxHandler = new AjaxHandler();
		this.ajaxHandler.submitRaceTime(mode, raceNumber, car, time, recording, this);
	},

	/**
	 * Callback de changement d'état, pour la mise à jour de la barre de progression
	 */
	updateAjaxProgress : function(value)
	{
	},

	/**
	 * Callback de fin d'appel Ajax, récupère le résultat, s'il est valide 
	 * met à jour l'objet Road
	 */
	ajaxCallCompleted : function()
	{
		var lapRecordDesc = '';
		if (this.ajaxHandler.success) {
			lapRecordDesc = this.ajaxHandler.response;
			this.road.setRaceRecords(lapRecordDesc);
			this.raceResult.updateFromServerResponse(lapRecordDesc);
		}
		this.road.lapRecordUpdateCompleted = true;
	}
}



 
 
function Road()
{
	this.level = 0;
	this.invalidRoad = false;
	this.lapRecordUpdateCompleted = false;
	this.lapRecordCount = 5;
	this.lapRecords = new Array (this.lapRecordCount);
	for (var index=0; index<this.lapRecordCount; ++index) {
		this.lapRecords[index] = {name:'', car:0, time:0 };
	}
}

Road.prototype = {

	/**
	 * Crée et invoque un RoadRetrievalHelper pour remplir les champs décrivant la route
	 * (altitude, virages, décors).
	 * Paramètres :
	 *  - mode : 1 simu, 2 arcade, 3 drift
	 *  - raceNumber : 1-based
	 *  - car : 1-based
	 *  - raceDirectory : répertoire des images de la course
	 *  - loader : contexte d'exécution pour les callbacks
	 */
	createRoad : function(mode, raceNumber, car, raceDirectory, loader)
	{
		this.folder = raceDirectory;
		this.level = raceNumber;
		var helper = new RoadRetrievalHelper(this);
		helper.invoke(mode, raceNumber, car, loader);
	},
	
	/**
	 * Point d'entrée pour renvoyer un résultat de course au serveur
	 * Paramètres :
	 *  - mode : 1 simu, 2 arcade, 3 drift
	 *  - car : 1-based
	 *  - time : temps de course en 1/100 s
	 *  - recording : télémétrie de la course (format selon le mode)
	 */
	validateRace : function(mode, car, time, recording, raceResult) 
	{
		var helper = new RaceTimeSubmissionHelper(this);
		helper.invoke(mode, this.level, car, time, recording, raceResult);
	},
	

	/**
	 * Ajoute un virage sur la route :
	 *  - offset : distance du début du virage (en nombre de segments)
	 *  - length : nombre de segments composant le virage
	 *  - minAngle : angle du virage au premier et dernier segment
	 *  - maxAngle : angle au milieu (généralement plus élevé, mais pour un effet, inverser le signe)
	 *  - warnSignLength : longueur (nombre de segments) des panneaux d'avertissement de virage
	 *  - warnSignType : type (champ du scenery) de panneau d'avertissement (peut être n'importe quoi)
	 *  - innerSignLength : longueur (nombre de segments) des panneaux dans le virage (toujours à l'extérieur)
	 *  - innerSignType : type (champ du scenery) de panneau dans le virage
	 *
	 * Les panneaux d'avertissement sont plantés des deux côtés de la route
	 * Les panneaux dans le virage sont uniquement du côté extérieur
	 * Les altitudes restent inchangées. Si la route formait déja un virage à cet endroit,
	 * il est remplacé, il n'y a pas d'addition des courbures.
	 *
	 * Aucune vérification n'est faite sur la longueur du tableau
	 */
	addBend : function (offset, length, minAngle, maxAngle, 
						warnSignLength, warnSignType, innerSignLength, innerSignType) 
	{
		for (var index = 0; index<length; ++index)
		{
			var alpha = 1.0;
			if (index*3<length) {
				alpha = index*3/length;
			} else if (index*3>2*length) {
				alpha = (length-index)*3/length;
			}
			var angle = alpha*maxAngle+(1.0-alpha)*minAngle;
			this.roadAngle[offset + index] = angle;
		}
		
		var bendDirection = (minAngle > 0 ? -1 : (minAngle < 0 ? 1 : (maxAngle > 0 ? -1 : 1 )));
		

		for (var index = 0; index<innerSignLength; ++index)
		{
			if (bendDirection>0) {
				this.sceneryLeft[offset+index] = { x:-1.3, type:innerSignType };
			} else {
				this.sceneryRight[offset+index] = { x:1.3, type:innerSignType };
			}
		}

		for (var index = 1; index<=warnSignLength; ++index)
		{
			this.sceneryLeft[offset-index] = { x:-1.1, type:warnSignType };
			this.sceneryRight[offset-index] = { x:1.1, type:warnSignType };
		}
	},
	
	/**
	 * Ajoute un plateau ou une colline.
	 * Les virages et décors ne sont pas modifiés.
	 *  - offset : distance du début du virage (en nombre de segments)
	 *  - upLength : nombre de segments de la montée jusqu'au début du plateau
	 *  - upTaper : coefficient d'arrondi : 0 pour un plan, 1 pour une sinusoïde
	 *  - plateauLength : nombre de segments du plateau
	 *  - plateauHeight : altitude relative du plateau. Negative pour un creux.
	 *  - downLength : nombre de segments de la descente depuis la fin du plateau
	 *  - downTaper : coefficient d'arrondi : 0 pour un plan, 1 pour une sinusoïde
	 *
	 * Pour une colline, mettre plateauLength=0 : on redescend aussitôt
	 * A noter que les "up" et "down" signifient plutôt "avant" et "après", car
	 * leur signification s'inverse si plateauHeight<0, on forme un creux.
	 * Les altitudes s'additionnent : on peut rajouter un plateau sur un autre plateau
	 * avec un effet cumulatif.
	 */
	addPlateau : function(offset, upLength, upTaper, plateauLength, plateauHeight, downLength, downTaper)
	{
		for (var index=0; index<upLength; ++index) {
			var alpha = index/upLength;
			var sineValue = 0.5+0.5*Math.sin(alpha*3.1416-1.5708); // entre 0 et 1
			this.roadAltitude[index+offset] += (alpha*(1-upTaper)+upTaper*sineValue)*plateauHeight;
		}
		offset+=upLength;
		
		for (var index=0; index<plateauLength; ++index) {
			this.roadAltitude[index+offset] += plateauHeight;
		}
		offset+=plateauLength;

		for (var index=0; index<downLength; ++index) {
			var alpha = 1.0-index/downLength;
			var sineValue = 0.5+0.5*Math.sin(alpha*3.1416-1.5708); // entre 0 et 1
			this.roadAltitude[index+offset] += (alpha*(1-upTaper)+upTaper*sineValue)*plateauHeight;
		}
	},

	/**
	 * Ajoute ou modifie le décor de la route, sur un ou plusieurs segments, consécutifs
	 * ou séparés par un intervalle constant. Sur les segments concernés, les deux éléments de décor
	 * (gauche et droit) dont remplacés par ceux fournis en paramètres.
	 * Remplace le décor existant s'il était déja défini. Les valeurs suivantes pour les éléments
	 * de décor ont des significations spéciales :
	 *  - -1 pour l'absence de décor (supprime le décor pré-existant)
	 *  - -2 pour ne pas modifier le décor de ce côté
	 *
	 * Paramètres :
	 *  - offset : début de la zone à éditer (en nombre de segments)
	 *  - length : longueur de la zone à éditer (en nombre de segments)
	 *  - step : intervalle entre deux segments dont le décor sera modifié (entier > 0)
	 *  - leftX : abscisse du premier élément de décor
	 *  - leftType : index de type (pris dans this.sceneryMaster) du premier élément de décor (-2 pour ignorer)
	 *  - rightX : abscisse du deuxième élément de décor
	 *  - rightType : index de type (pris dans this.sceneryMaster) du deuxième élément de décor (-2 pour ignorer)
	 *
	 * A noter que les noms <i>left</i> et <i>right</i> des paramètres présument de leur positionnement relatif
	 * mais pas du côté de la route où il se trouvent, les deux peuvent être sur le bas-côté gauche ou inversement.
	 * Il n'y a pas de préconisation si on souhaite un seul élément de décor sur le segment, on peut utiliser
	 * indifféremment left ou right.
	 *
	 */
	editSceneryRaw : function(offset, length, step, leftX, leftType, rightX, rightType)
	{
		// pour éviter les étourderies, boucles infinies et accès illégaux
		step = (step < 1 ? 1 : Math.round(step));
		for (var index=offset ; index<offset+length; index+=step) {
			if (leftType > -2) {
				this.sceneryLeft[index] = { x:leftX, type:leftType };
			}
			if (rightType > -2) {
				this.sceneryRight[index] = { x:rightX, type:rightType };
			}
		}
	},
	
	/**
	 * Ajoute une pente simple (montante ou descendante)
	 * Les virages et décors ne sont pas modifiés.
	 *  - offset : distance du début du virage (en nombre de segments)
	 *  - length : nombre de segments de la pente
	 *  - taper : coefficient d'arrondi : 0 pour un plan, 1 pour une sinusoïde
	 *  - slopeHeight : altitude relative en fin de pente, positive pour une montée, négative pour une descente
	 *
	 * La différence d'altitude s'additionne, on peut rajouter une pente par-dessus une autre.
	 * La valeur slopeHeight est également ajoutée à l'altitude de tous les segments à l'issue de la pente
	 * (pour éviter un décrochage). 
	 */
	addSlope : function(offset, length, taper, slopeHeight)
	{
		for (var index=0; index<length; ++index) {
			var alpha = index/length;
			var sineValue = 0.5+0.5*Math.sin(alpha*3.1416-1.5708); // entre 0 et 1
			this.roadAltitude[index+offset] += (alpha*(1-taper)+taper*sineValue)*slopeHeight;
		}
		
		for (var index=offset+length; index<this.roadLength; ++index) {
			this.roadAltitude[index] += slopeHeight;
		}
	},
	
	/**
	 * Crée la route à partir d'une description XML fournie par le serveur.
	 *
	 * Cette dernière contient :
	 *  - une chaîne de caractères qui décrit la route en tant qu'une série d'appels de méthodes.
	 *  - un ensemble de champs XML décrivant l'objet sceneryMaster (objets graphiques du décor)
	 *
	 * Le début de la chaîne de caractères est un élément commun à tous les circuits :
	 *
	 * +---------+--------------------------------
	 * |  octets | coef * | intervalle | 
	 * +---------+--------------------------------
	 * |   0-1   |      1 |    0-4095  | nombre de segments
	 * |   2-3   |    500 |  0-209750  | ligne d'arrivée (en cm)
	 * 
	 * La suite est traitée par blocs de longueur variable.
	 * Le premier octet d'un bloc définit la méthode à appeler et 
	 * la nature des autres octets composant le bloc, qui représentent les 
	 * paramètres de la méthode.
	 * Le premier bloc commence à l'offset 4. Il n'y a pas de séparateur entre blocs.
	 * Dans les tableaux suivants, l'offset indiqué est relatif au début du bloc.
	 *
	 * +---------+------------------------------------------------------------------------------
	 * |  octets | coef * |      intervalle | 
	 * +---------+------------------------------------------------------------------------------
	 * |      0  |        |               1 | addBend()
	 * |    1-2  |      1 |        0 - 4095 | segment de départ
	 * |      3  |      1 |          0 - 63 | longueur, en nombre de segments
	 * |      4  |  0.005 |   -0.16 - 0.155 | courbure initiale et finale du virage
	 * |      5  |   0.01 |    -0.32 - 0.31 | courbure en milieu de virage
	 * |      6  |      1 |          0 - 63 | nombre de segments des panneaux d'avertissement
	 * |      7  |      1 |         -2 - 61 | type (index du scenery) du panneau d'avertissement
	 * |      8  |      1 |          0 - 63 | nombre de segments des panneaux dans le virage
	 * |      9  |      1 |         -2 - 61 | type (index du scenery) du panneau dans le virage
	 *
	 * +---------+------------------------------------------------------------------------------
	 * |  octets | coef * |      intervalle | 
	 * +---------+------------------------------------------------------------------------------
	 * |      0  |        |               2 | addPlateau()
	 * |    1-2  |      1 |        0 - 4095 | segment de départ
	 * |      3  |      5 |         0 - 315 | longueur de la montée, en nombre de segments
	 * |      4  |   1/63 |           0 - 1 | coefficient d'arrondi de la montée : 0 pour un plan, 1 pour une sinusoïde
	 * |      5  |      5 |         0 - 315 | longueur du plateau, en nombre de segments
	 * |      6  |    100 |    -3200 - 3100 | altitude relative du plateau
	 * |      7  |      5 |         0 - 315 | longueur de la descente, en nombre de segments
	 * |      8  |   1/63 |           0 - 1 | coefficient d'arrondi de la descente : 0 pour un plan, 1 pour une sinusoïde
	 *
	 * +---------+------------------------------------------------------------------------------
	 * |  octets | coef * |      intervalle | 
	 * +---------+------------------------------------------------------------------------------
	 * |      0  |        |               3 | editSceneryRaw()
	 * |    1-2  |      1 |        0 - 4095 | segment de départ
	 * |    3-4  |      1 |        0 - 4095 | longueur de la zone concernée
	 * |      5  |      1 |          1 - 63 | intervalle entre deux segments à éditer
	 * |    6-7  |   0.01 |  -20.48 - 20.47 | abscisse du premier élément de décor
	 * |      8  |      1 |         -2 - 61 | type du premier élément de décor
	 * |   9-10  |   0.01 |  -20.48 - 20.47 | abscisse du deuxième élément de décor
	 * |     11  |      1 |         -2 - 61 | type du deuxième élément de décor
	 *
	 * +---------+------------------------------------------------------------------------------
	 * |  octets | coef * |      intervalle | 
	 * +---------+------------------------------------------------------------------------------
	 * |      0  |        |               4 | addSlope()
	 * |    1-2  |      1 |        0 - 4095 | segment de départ
	 * |      3  |      5 |         0 - 315 | longueur de la pente, en nombre de segments
	 * |      4  |   1/63 |           0 - 1 | coefficient d'arrondi de la montée : 0 pour un plan, 1 pour une sinusoïde
	 * |      5  |    100 |    -3200 - 3100 | altitude relative à l'extrémité de la pente
	 */
	createRoadFromDescription : function(contents) 
	{
		this.invalidRoad = true;

		// extraction XML
		var roadDesc = contents.match(/<track><road>\"([-_A-Za-z0-9]*)\"<\/road>/); // regExp <track><road>...</road>
		var groundDesc = contents.match(/<ground count="(\d*)">(.*)<\/ground>/); // regExp <ground count="...">...</ground>
		var weatherDesc = contents.match(/<weather count="(\d*)">(.*)<\/weather>/); // regExp <weather count="...">...</weather>
		var sceneryDesc = contents.match(/<scenery>(.*)<\/scenery>/); // regExp <scenery>...</scenery>
		var lapRecordsDesc = contents.match(/<lapRecords>(.*)<\/lapRecords>/); // regExp <lapRecords>...</lapRecords>
		if (roadDesc == null || groundDesc == null || sceneryDesc == null || lapRecordsDesc == null) {
			return;
		}
		roadDesc = roadDesc[1];
		this.groundSectionCount = parseInt(groundDesc[1]);
		groundDesc = groundDesc[2];
		// weatherDesc peut être null : dans ce cas il n'y a pas d'événement météo
		sceneryDesc = sceneryDesc[1];
		lapRecordsDesc = lapRecordsDesc[1];
	
		this.invalidRoad = false;
		this.roadLength = this.decodeUnsigned16(roadDesc, 0, 1);
		this.distanceStep = 500; // longueur d'un segment (500cm = 5m)
		this.roadAltitude = new Array(this.roadLength);
		this.roadAngle = new Array(this.roadLength);
		this.sceneryLeft = new Array(this.roadLength);
		this.sceneryRight = new Array(this.roadLength);
		this.finishLine = this.decodeUnsigned16(roadDesc, 2, 500);

		for (var index = 0; index<this.roadLength; ++index)
		{
			this.roadAltitude[index] = 0;
			this.roadAngle[index] = 0;
			this.sceneryLeft[index] = { x:-1.1, type:-1 };
			this.sceneryRight[index] = { x:1.1, type:-1 };
		}
		
		var offset = 4;
		while (offset < roadDesc.length && !this.invalidRoad) {
			var action = this.decodeUnsigned8(roadDesc, offset, 1);
			switch (action) {
				case 1 : 
					var startAt = this.decodeUnsigned16(roadDesc, offset+1, 1);
					var length = this.decodeUnsigned8(roadDesc, offset+3, 1);
					var minAngle = this.decodeSigned8(roadDesc, offset+4, 0.005);
					var maxAngle = this.decodeSigned8(roadDesc, offset+5, 0.01);
					var warnSignLength = this.decodeUnsigned8(roadDesc, offset+6, 1);
					var warnSignType = -2+this.decodeUnsigned8(roadDesc, offset+7, 1);
					var innerSignLength = this.decodeUnsigned8(roadDesc, offset+8, 1);
					var innerSignType = -2+this.decodeUnsigned8(roadDesc, offset+9, 1);
					this.addBend(startAt, length, minAngle, maxAngle, 
						warnSignLength, warnSignType, innerSignLength, innerSignType);
					offset+=10;
					break;
				
				case 2 : 
					var startAt = this.decodeUnsigned16(roadDesc, offset+1, 1);
					var upLength = this.decodeUnsigned8(roadDesc, offset+3, 5);
					var upTaper = this.decodeUnsigned8(roadDesc, offset+4, 1/63.0);
					var plateauLength = this.decodeUnsigned8(roadDesc, offset+5, 5);
					var plateauHeight = this.decodeSigned8(roadDesc, offset+6, 100);
					var downLength = this.decodeUnsigned8(roadDesc, offset+7, 5);
					var downTaper = this.decodeUnsigned8(roadDesc, offset+8, 1/63.0);
					this.addPlateau(startAt, upLength, upTaper, plateauLength, plateauHeight, downLength, downTaper);
					offset+=9;
					break;
					
				case 3 : 
					var startAt = this.decodeUnsigned16(roadDesc, offset+1, 1);
					var length = this.decodeUnsigned16(roadDesc, offset+3, 1);
					var step = this.decodeUnsigned8(roadDesc, offset+5, 1);
					var leftX = this.decodeSigned16(roadDesc, offset+6, 0.01);
					var leftType = -2+this.decodeUnsigned8(roadDesc, offset+8, 1);
					var rightX = this.decodeSigned16(roadDesc, offset+9, 0.01);
					var rightType = -2+this.decodeUnsigned8(roadDesc, offset+11, 1);
					this.editSceneryRaw(startAt, length, step, leftX, leftType, rightX, rightType);
					offset+=12;
					break;
					
				case 4 : 
					var startAt = this.decodeUnsigned16(roadDesc, offset+1, 1);
					var length = this.decodeUnsigned8(roadDesc, offset+3, 5);
					var taper = this.decodeUnsigned8(roadDesc, offset+4, 1/63.0);
					var slopeHeight = this.decodeSigned8(roadDesc, offset+5, 100);
					this.addSlope(startAt, length, taper, slopeHeight);
					offset+=6;
					break;
					
				default :
					this.invalidRoad = true;
			}
		}
		
		this.phaseCount = 0;
		this.groundSection = new Array(this.groundSectionCount+1);
		var groundLines = groundDesc.match(/<section (.*?)\/>/g); // regExp  <section [attributes] /> pour chaque ligne
		if (groundLines == null || groundLines.length != this.groundSectionCount) {
			this.invalidRoad = true;
		}
		for (var sectionIndex = 0; sectionIndex<this.groundSectionCount; ++sectionIndex) {
			var lineExp = groundLines[sectionIndex].match(/offset="(\d*)" leftWidth="(\d*.?\d*)" rightWidth="(\d*.?\d*)" gripInside="(\d*.?\d*)" gripOutside="(\d*.?\d*)" patternDistance="(\d*)" patternSize="(\d*)" groundColor="([\d\w ,#\(\)]*)" groundLine="([0-9 ]*)"( groundLeft="([-.0-9 ]*)")?( groundRight="([-.0-9 ]*)")?( tunnelInnerHeight="([-.0-9 ]*)" tunnelOuterHeight="([-.0-9 ]*)" tunnelInnerColor="([\d\w,#\(\)]*)" tunnelOuterColor="([\d\w,#\(\)]*)")?/);
			if (lineExp == null) {
				this.invalidRoad = true;
			} else {
				var offset = parseInt(lineExp[1]);
				var leftWidth = parseFloat(lineExp[2]);
				var rightWidth = parseFloat(lineExp[3]);
				var gripInside = parseFloat(lineExp[4]);
				var gripOutside = parseFloat(lineExp[5]);
				var distance = parseInt(lineExp[6]);
				var size = parseInt(lineExp[7]);
				var color = lineExp[8].split(' ');
				var line = lineExp[9].split(' ');
				for (var index=0; index<size; ++index) {
					line[index]=parseInt(line[index]);
					this.phaseCount = (line[index]>=this.phaseCount ? line[index]+1 : this.phaseCount);
				}
				var groundLeft = false;
				if (lineExp[11])  {
					groundLeft = lineExp[11].split(' ');
					for (var index=0; index<groundLeft.length; ++index) {
						groundLeft[index]=parseFloat(groundLeft[index]);
					}
				}
				var groundRight = false;
				if (lineExp[13])  {
					groundRight = lineExp[13].split(' ');
					for (var index=0; index<groundRight.length; ++index) {
						groundRight[index]=parseFloat(groundRight[index]);
					}
				}
				var tunnelInnerHeight=false, tunnelOuterHeight=false, tunnelInnerColor=false, tunnelOuterColor=false, isTunnel=false;
				if (lineExp[15]) {
					tunnelInnerHeight = parseInt(lineExp[15]);
					tunnelOuterHeight = parseInt(lineExp[16]);
					tunnelInnerColor = lineExp[17];
					tunnelOuterColor = lineExp[18];
					isTunnel = true;
				}
				
				this.groundSection[sectionIndex] = {	offset:offset,
														leftWidth:leftWidth,
														rightWidth:rightWidth,
														gripInside:gripInside,
														gripOutside:gripOutside,
														patternDistance:distance,
														patternSize:size,
														groundColor:color,
														groundLine:line,
														groundLeft:groundLeft,
														groundRight:groundRight,
														tunnelInnerHeight:tunnelInnerHeight,
														tunnelOuterHeight:tunnelOuterHeight,
														tunnelInnerColor:tunnelInnerColor,
														tunnelOuterColor:tunnelOuterColor,
														isTunnel:isTunnel
													};
			}
		}
		// on ajoute un élément en fin de tronçon
		this.groundSection[this.groundSectionCount] = {	offset:this.roadLength*2,
														leftWidth:this.groundSection[0].leftWidth,
														rightWidth:this.groundSection[0].rightWidth,
														gripInside:this.groundSection[0].gripInside,
														gripOutside:this.groundSection[0].gripOutside,
														patternDistance:this.groundSection[0].patternDistance,
														patternSize:this.groundSection[0].patternSize,
														groundColor:this.groundSection[0].groundColor,
														groundLine:this.groundSection[0].groundLine
													};
		
		// Import des événements météo
		if (weatherDesc==null) {
			this.weatherAreaCount = 1;
			this.weatherArea = new Array(1);
			this.weatherArea[0] = {	offset:0,
									image:"",
									color:"#BBC0D0",
									opacity:0
								  };
		} else {
			this.weatherAreaCount = parseInt(weatherDesc[1]);
			weatherDesc = weatherDesc[2];
			this.weatherArea = new Array(this.weatherAreaCount);
			
			var weatherAreaDesc = weatherDesc.match(/<area (.*?)\/>/g); // regExp  <area [attributes] /> pour chaque ligne
			if (weatherAreaDesc == null || weatherAreaDesc.length != this.weatherAreaCount) {
				this.invalidRoad = true;
			}
			for (var areaIndex = 0; areaIndex<this.weatherAreaCount; ++areaIndex) {
				var lineExp = weatherAreaDesc[areaIndex].match(/offset="(\d*)" image="([A-Za-z0-9.]*)" color="([\d\w ,#\(\)]*)" opacity="(\d*.?\d*)" /);
				if (lineExp == null) {
					this.invalidRoad = true;
				} else {
					var offset = parseInt(lineExp[1]);
					var image = lineExp[2];
					var color = lineExp[3];
					var opacity = parseFloat(lineExp[4]);
					
					this.weatherArea[areaIndex] = {	offset:offset,
													image:image,
													color:color,
													opacity:opacity
												  };
				}
			}
		}
		
		this.sceneryTypeCount = 0; // default is no scenery -> invalid road if nothing is read
		var sceneryCount = sceneryDesc.match(/<count>(\d*)<\/count>/); // regExp <count> </count>
		if (sceneryCount) {
			this.sceneryTypeCount = parseInt(sceneryCount[1]);
		} 
		if (this.sceneryTypeCount>0 && this.sceneryTypeCount<64) {
			this.sceneryMaster = new Array(this.sceneryTypeCount);
		} else {
			this.invalidRoad = true;
		}

		var sceneryLines = sceneryDesc.match(/<item>(.*?)<\/item>/g); // regExp  <item>...</item> pour chaque ligne
		if (sceneryLines == null || sceneryLines.length != this.sceneryTypeCount) {
			this.invalidRoad = true;
		}
		for (var sceneryIndex = 0; sceneryIndex<this.sceneryTypeCount && !this.invalidRoad ; ++sceneryIndex) {
			var lineExp = sceneryLines[sceneryIndex].match(/<w>(\d*)<\/w><h>(\d*)<\/h><src>(\w*\.(?:png|gif))<\/src><cl>(\d*)<\/cl><cw>(\d*)<\/cw>/);
			if (lineExp == null) {
				this.invalidRoad = true;
			} else {
				var w = parseInt(lineExp[1]); // regExp <w>...</w>
				var h = parseInt(lineExp[2]); // regExp <h>...</h>
				var src = lineExp[3]; // regExp <src>...</src>
				var collisionLeft = parseInt(lineExp[4]); // regExp <cl>...</cl>
				var collisionWidth = parseInt(lineExp[5]); // regExp <cw>...</cw>
				var door = 0; // pour les éléments de décor de type porte où on doit passer à gauche (-1) ou à droite (+1)
				var line = 0; // éléments de départ (1) ou d'arrivée (-1) d'un slalom dans la course
				var collisionEffect = 0; // comportement lors d'une collision : choc, déplacement, récupération ..
				
				var optionalDoorExp = sceneryLines[sceneryIndex].match(/<door>(-?\d*)<\/door>/);
				if (optionalDoorExp != null) {
					door = parseInt(optionalDoorExp[1]); // regExp <door>...</door>
				}
				var optionalLineExp = sceneryLines[sceneryIndex].match(/<line>(-?\d*)<\/line>/);
				if (optionalLineExp != null) {
					line = parseInt(optionalLineExp[1]); // regExp <line>...</line>
				}
				var optionalCollisionEffectExp = sceneryLines[sceneryIndex].match(/<collisionFx>(-?\d*)<\/collisionFx>/);
				if (optionalCollisionEffectExp != null) {
					collisionEffect = parseInt(optionalCollisionEffectExp[1]); // regExp <collisionFx>...</collisionFx>
				}
				var optionalSideWidthExp = sceneryLines[sceneryIndex].match(/<sidew>(-?\d*)<\/sidew>/);
				if (optionalSideWidthExp != null) {
					sideW = parseInt(optionalSideWidthExp[1]); // regExp <sidew>...</sidew>
				}
				
				this.invalidRoad = this.invalidRoad ||  (collisionLeft+collisionWidth>w);
				this.sceneryMaster[sceneryIndex] = { w:w, 
													 h:h, 
													 src:this.folder+src,
													 collisionLeft:collisionLeft, 
													 collisionWidth:collisionWidth,
													 door:door,
													 line:line,
													 collisionEffect:collisionEffect,
													 sideW:sideW};
			}
		}
		
		var horizonDesc = sceneryDesc.match(/<horizon><w>(\d*)<\/w><h>(\d*)<\/h><src>(\w*\.(?:png|gif))<\/src>(<reflection>(\w*\.(?:png|gif))<\/reflection>)?<\/horizon>/);
		if (horizonDesc == null) {
			this.invalidRoad = true;
		} else {
			var w = horizonDesc[1]; // regExp <w>...</w>
			var h = horizonDesc[2]; // regExp <h>...</h>
			var src = horizonDesc[3]; // regExp <src>...</src>
			var reflection = horizonDesc[5]; // regExp <reflection>...</reflection>
			this.horizon = { w:w, h:h, src:this.folder+src, reflection:reflection?this.folder+reflection:false }
		}
		
		this.setRaceRecords(lapRecordsDesc);
	},
	
	/**
	 * Remplit les différents records à partir de la description XML fournie :
	 *  - 5 meilleurs temps du circuit
	 *  - record personnel du joueur
	 *  - temps de qualification
	 *
	 * Cet appel est prévu une fois au chargement du circuit, et une seconde fois
	 * après avoir fait valider le temps de course par le serveur
	 */
	setRaceRecords : function(lapRecordsDesc)
	{
		var lapTimesDesc = lapRecordsDesc.match(/<lapTime>(.*?)<\/lapTime>/g); // regExp  <lapTime>...</lapTime> pour chaque ligne
		if (lapTimesDesc != null) {
			for (var lapTimeIndex = 0; lapTimeIndex<lapTimesDesc.length && lapTimeIndex<this.lapRecordCount  ; ++lapTimeIndex) {
				var lineExp = lapTimesDesc[lapTimeIndex].match(/<name>([^<]+)<\/name><car>(\d*)<\/car><time>(\d*)<\/time>/);
				if (lineExp != null) {
				var name = lineExp[1]; // regExp <name>...</name>
				var car = parseInt(lineExp[2]); // regExp <name>...</name>
				var time = parseInt(lineExp[3]); // regExp <name>...</name>
				this.lapRecords[lapTimeIndex] = { name:name, car:car, time:time };
				}
			}
		}
		
		var playerLapTimesDesc = lapRecordsDesc.match(/<playerTime><name>([^<]+)<\/name><car>(\d*)<\/car><time>(\d*)<\/time><\/playerTime>/); 
		if (playerLapTimesDesc == null) {
			this.playerLapRecord = { name:'', car:0, time:0 };
		} else {
			var name = playerLapTimesDesc[1]; // regExp <name>...</name>
			var car = parseInt(playerLapTimesDesc[2]); // regExp <car>...</car>
			var time = parseInt(playerLapTimesDesc[3]); // regExp <time>...</time>
			this.playerLapRecord = { name:name, car:car, time:time };
		}
		
		var qualificationTimeDesc = lapRecordsDesc.match(/<qualifyingTime>(\d*)<\/qualifyingTime>/);
		if (qualificationTimeDesc == null) {
			this.qualifyingTime = 0;
		} else {
			this.qualifyingTime = parseInt(qualificationTimeDesc[1]);
		}
	},
	
	/**
	 * Crée de manière hardcodée la description d'une course
	 * Cette méthode sert lors du design du circuit, pour la conception
	 * des virages et du relief, ainsi que pour le placement du décor.
	 * 	 
	 * Une fois la réalisation terminée, le code est basculé vers le RoadEncoder
	 * qui produit le XML correspondant à recopier côté serveur.
	 */
	createLevel1Road : function()
	{
		this.invalidRoad = false;
		this.roadLength = 1000; // nombre de segments 
		this.distanceStep = 500; // longueur d'un segment (500cm = 5m)
		this.roadAltitude = new Array(this.roadLength);
		this.roadAngle = new Array(this.roadLength);
		this.sceneryLeft = new Array(this.roadLength);
		this.sceneryRight = new Array(this.roadLength);
		this.finishLine = 670*500;

		for (var index = 0; index<this.roadLength; ++index)
		{
			this.roadAltitude[index] = 0;
			this.roadAngle[index] = 0;
			this.sceneryLeft[index] = { x:-1.1, type:-1 };
			this.sceneryRight[index] = { x:1.1, type:-1 };
		}

		this.addBend (   35, 20, 0.02, 0.02, 0, 0, 0, 0);
		this.addBend (   65, 30, 0.02, 0.02, 0, 0, 0, 0);
		this.addBend (   95, 30, -0.03, -0.03, 0, 0, 0, 0);
		this.addBend (  135, 30, 0.02, 0.04, 0, 0, 0, 0);
		this.addBend (  165, 30, -0.03, -0.05, 0, 0, 0, 0);
		this.addPlateau ( 140, 10, 1, 30, -200, 10, 1);
		this.addPlateau ( 200, 10, 1, 10, 400, 10, 1);
		this.addBend (  215, 20, 0.03, 0.05, 0, 0, 0, 0);
		this.addBend (  235, 20, -0.01, 0.05, 0, 0, 0, 0);
		this.addPlateau ( 240, 20, 1, 100, -400, 20, 1);
		this.addBend (  275, 10, 0.01, 0.02, 0, 0, 0, 0);
		this.addPlateau ( 280, 10, 1, 40, 300, 25, 1);
		this.addBend (  290, 20, 0.02, 0.02, 0, 0, 0, 0);
		this.addBend (  305, 20, -0.01, -0.05, 0, 0, 0, 0);
		this.addBend (  330, 20, -0.03, -0.06, 0, 0, 0, 0);
		this.addBend (  345, 15, 0.02, 0.02, 0, 0, 0, 0);
		this.addBend (  375, 22, 0.01, 0.02, 0, 0, 0, 0);
		this.addPlateau ( 385, 10, 1, 10, 100, 10, 1);
		this.addBend (  400, 5, 0.01, 0.03, 0, 0, 0, 0);
		this.addBend (  405, 15, 0.01, -0.04, 0, 0, 0, 0);
		this.addBend (  435, 25, 0.01, -0.03, 0, 0, 0, 0);
		this.addPlateau ( 440, 25, 1, 10, 200, 20, 1);
		this.addBend (  450, 25, 0.01, -0.03, 0, 0, 0, 0);
		this.addBend (  480, 30, 0.01, 0.04, 0, 0, 0, 0);
		this.addBend (  505, 10, -0.02, -0.03, 0, 0, 0, 0);
		this.addBend (  515, 20, 0, 0.01, 0, 0, 0, 0);
		this.addPlateau ( 510, 40, 1, 100, -200, 40, 1);
		this.addPlateau ( 520, 15, 1, 0, 50, 20, 1);
		this.addPlateau ( 530, 20, 1, 10, 100, 20, 1);
		this.addBend (  535, 20, 0, -0.03, 0, 0, 0, 0);
		this.addBend (  545, 20, 0, 0.03, 0, 0, 0, 0);
		this.addBend (  555, 20, 0, -0.04, 0, 0, 0, 0);
		this.addBend (  565, 20, 0, 0.05, 0, 0, 0, 0);
		this.addBend (  575, 20, 0, -0.06, 0, 0, 0, 0);
		this.addBend (  585, 20, 0, 0.07, 0, 0, 0, 0);
		this.addBend (  595, 20, 0, -0.01, 0, 0, 0, 0);
		this.addPlateau ( 640, 30, 1, 0, -200, 30, 1);
		this.addBend (  630, 30, 0, 0.02, 0, 0, 0, 0);
		this.addBend (  650, 20, 0.01, 0.01, 0, 0, 0, 0);

		
		// portique au départ
		this.editSceneryRaw(4, 1, 1, 0, 2, 1.08, 4);
		// portique à l'arrivée
		var finishSegment = this.finishLine/this.distanceStep;
		this.editSceneryRaw(finishSegment, 1, 1, 0, 3, 1.08, 4);		

		this.editSceneryRaw(10, 15, 3, -1.3, 13, 1.3, 14);
		this.editSceneryRaw(25, 2, 1, -4, 11, 0, -2);
		this.editSceneryRaw(30, 15, 3, -1.3, 13, 1.3, 14);
		this.editSceneryRaw(40, 1, 1, 0, -2, 4, 11);
		this.editSceneryRaw(60, 30, 3, -1.3, 13, 1.3, 14);
		this.editSceneryRaw(110, 2, 1, -4, 11, 0, -2);
		this.editSceneryRaw(135, 1, 1, -1.1, 0, 1.1, 0);
		this.editSceneryRaw(120, 1, 1, 0, 5, 0, -2);
		this.editSceneryRaw(142, 49, 4, -.96, 6, .96, 7);
		this.editSceneryRaw(210, 1, 1, 0, 5, 0, -2);
		this.editSceneryRaw(215, 20, 3, -1.3, 13, 1.3, 14);
		this.editSceneryRaw(242, 118, 4, -.96, 6, .96, 7);
		this.editSceneryRaw(360, 30, 3, -1.3, 13, 1.3, 14);
		this.editSceneryRaw(400, 1, 1, 0, 5, 0, -2);
		this.editSceneryRaw(432, 30, 4, -.96, 6, .96, 7);
		this.editSceneryRaw(480, 1, 1, 0, 5, 0, -2);
		this.editSceneryRaw(502, 18, 4, -.96, 6, .96, 7);
		this.editSceneryRaw(525, 20, 3, -1.3, 13, 1.3, 14);
		this.editSceneryRaw(552, 22, 4, -.96, 6, .96, 7);
		this.editSceneryRaw(586, 54, 4, -.96, 6, .96, 7);
		this.editSceneryRaw(690, 1, 1, 0, 5, 0, -2);
		this.editSceneryRaw(702, 58, 4, -.96, 6, .96, 7);
		this.editSceneryRaw(780, 1, 1, 0, 5, 0, -2);
		this.editSceneryRaw(802, 58, 4, -.96, 6, .96, 7);
		this.editSceneryRaw(890, 30, 4, -.96, 6, .96, 7);
		
		this.sceneryTypeCount = 16; // types d'objet
		this.sceneryMaster = new Array(this.sceneryTypeCount);
		this.sceneryMaster[0] = { w:52, h:101, src:"level000/tunnel_sign.png", collisionLeft:24, collisionWidth:4};
		this.sceneryMaster[1] = { w:40, h:80, src:"level000/100.png", collisionLeft:18, collisionWidth:4 };
		this.sceneryMaster[2] = { w:504, h:288, src:"level000/portique_start.png", collisionLeft:0, collisionWidth:28};
		this.sceneryMaster[3] = { w:504, h:288, src:"level000/portique_finish.png", collisionLeft:0, collisionWidth:28 };
		this.sceneryMaster[4] = { w:28, h:216, src:"level000/portique_d.png", collisionLeft:0, collisionWidth:28 };
		this.sceneryMaster[5] = { w:704, h:247, src:"level000/overhead_sign_downtown.png", collisionLeft:0, collisionWidth:20 };
		this.sceneryMaster[6] = { w:13, h:126, src:"level000/tunnel_light_g.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[7] = { w:13, h:126, src:"level000/tunnel_light_d.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[8] = { w:81, h:43, src:"level000/barrier.png", collisionLeft:0, collisionWidth:1, sideW:40 };
		this.sceneryMaster[9] = { w:240, h:100, src:"level000/100.png", collisionLeft:0, collisionWidth:160, sideW:40 };
		this.sceneryMaster[10] = { w:100, h:50, src:"level000/100.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[11] = { w:1005, h:672, src:"level000/building2_part1.png", collisionLeft:0, collisionWidth:483, sideW:261 };
		this.sceneryMaster[12] = { w:927, h:672, src:"level000/building2_part2.png", collisionLeft:0, collisionWidth:2, sideW:222 };
		this.sceneryMaster[13] = { w:80, h:200, src:"level000/streetlight_g.png", collisionLeft:13, collisionWidth:8 };
		this.sceneryMaster[14] = { w:80, h:200, src:"level000/streetlight_d.png", collisionLeft:61, collisionWidth:8 };
		this.sceneryMaster[15] = { w:100, h:50, src:"level000/100.png", collisionLeft:0, collisionWidth:0 };

		
		this.horizon = {w:1720, h:520, src:"level000/bg_vancouver_256.png"};
		
		this.playerLapRecord = { name:'', car:0, time:0 };
		this.qualifyingTime = 6500; // 1mn 5s 

		this.groundSection = new Array(20);
		this.groundSection[0] =  {	offset:0,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:0.8,
									gripOutside:0.6,
									patternDistance:1000,
									patternSize:10,
									groundColor:["#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0", "#A0A0A0", "#A0A0A0", "#A0A0A0", "#A0A0A0", "#A0A0A0"],
									groundLine:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
								};
		this.groundSection[1] =  {	offset:140,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:0.8,
									gripOutside:0.6,
									patternDistance:2000,
									patternSize:20,
									groundColor:["black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black"],
									groundLine:[10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
									tunnelInnerHeight:120,
									tunnelOuterHeight:240,
									tunnelInnerColor:"black",
									tunnelOuterColor:"#808080",
									isTunnel:true
								};
		this.groundSection[2] =  {	offset:192,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:0.8,
									gripOutside:0.6,
									patternDistance:1000,
									patternSize:10,
									groundColor:["#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0", "#A0A0A0", "#A0A0A0", "#A0A0A0", "#A0A0A0", "#A0A0A0"],
									groundLine:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
								};
		this.groundSection[3] =  {	offset:240,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:0.8,
									gripOutside:0.6,
									patternDistance:2000,
									patternSize:20,
									groundColor:["black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black"],
									groundLine:[10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
									tunnelInnerHeight:120,
									tunnelOuterHeight:240,
									tunnelInnerColor:"black",
									tunnelOuterColor:"#808080",
									isTunnel:true
								};
		this.groundSection[4] =  {	offset:360,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:0.8,
									gripOutside:0.6,
									patternDistance:1000,
									patternSize:10,
									groundColor:["#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0", "#A0A0A0", "#A0A0A0", "#A0A0A0", "#A0A0A0", "#A0A0A0"],
									groundLine:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
								};
		this.groundSection[5] =  {	offset:430,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:0.8,
									gripOutside:0.6,
									patternDistance:2000,
									patternSize:20,
									groundColor:["black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black"],
									groundLine:[10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
									tunnelInnerHeight:120,
									tunnelOuterHeight:240,
									tunnelInnerColor:"black",
									tunnelOuterColor:"#808080",
									isTunnel:true
								};
		this.groundSection[6] =  {	offset:462,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:0.8,
									gripOutside:0.6,
									patternDistance:1000,
									patternSize:10,
									groundColor:["#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0", "#A0A0A0", "#A0A0A0", "#A0A0A0", "#A0A0A0", "#A0A0A0"],
									groundLine:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
								};
		this.groundSection[7] =  {	offset:500,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:0.8,
									gripOutside:0.6,
									patternDistance:2000,
									patternSize:20,
									groundColor:["black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black"],
									groundLine:[10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
									tunnelInnerHeight:120,
									tunnelOuterHeight:240,
									tunnelInnerColor:"black",
									tunnelOuterColor:"#808080",
									isTunnel:true
								};
		this.groundSection[8] =  {	offset:520,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:0.8,
									gripOutside:0.6,
									patternDistance:1000,
									patternSize:10,
									groundColor:["#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0", "#A0A0A0", "#A0A0A0", "#A0A0A0", "#A0A0A0", "#A0A0A0"],
									groundLine:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
								};
		this.groundSection[9] =  {	offset:550,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:0.8,
									gripOutside:0.6,
									patternDistance:2000,
									patternSize:20,
									groundColor:["black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black"],
									groundLine:[10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
									tunnelInnerHeight:120,
									tunnelOuterHeight:240,
									tunnelInnerColor:"black",
									tunnelOuterColor:"#808080",
									isTunnel:true
								};
		this.groundSection[10] =  {	offset:574,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:0.8,
									gripOutside:0.6,
									patternDistance:1000,
									patternSize:10,
									groundColor:["#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0", "#A0A0A0", "#A0A0A0", "#A0A0A0", "#A0A0A0", "#A0A0A0"],
									groundLine:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
								};
		this.groundSection[11] =  {	offset:584,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:0.8,
									gripOutside:0.6,
									patternDistance:2000,
									patternSize:20,
									groundColor:["black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black"],
									groundLine:[10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
									tunnelInnerHeight:120,
									tunnelOuterHeight:240,
									tunnelInnerColor:"black",
									tunnelOuterColor:"#808080",
									isTunnel:true
								};
		this.groundSection[12] =  {	offset:640,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:0.8,
									gripOutside:0.6,
									patternDistance:1000,
									patternSize:10,
									groundColor:["#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0", "#A0A0A0", "#A0A0A0", "#A0A0A0", "#A0A0A0", "#A0A0A0"],
									groundLine:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
								};
		this.groundSection[13] =  {	offset:700,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:0.8,
									gripOutside:0.6,
									patternDistance:2000,
									patternSize:20,
									groundColor:["black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black"],
									groundLine:[10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
									tunnelInnerHeight:120,
									tunnelOuterHeight:240,
									tunnelInnerColor:"black",
									tunnelOuterColor:"#808080",
									isTunnel:true
								};
		this.groundSection[14] =  {	offset:760,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:0.8,
									gripOutside:0.6,
									patternDistance:1000,
									patternSize:10,
									groundColor:["#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0", "#A0A0A0", "#A0A0A0", "#A0A0A0", "#A0A0A0", "#A0A0A0"],
									groundLine:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
								};
		this.groundSection[15] =  {	offset:800,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:0.8,
									gripOutside:0.6,
									patternDistance:2000,
									patternSize:20,
									groundColor:["black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black"],
									groundLine:[10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
									tunnelInnerHeight:120,
									tunnelOuterHeight:240,
									tunnelInnerColor:"black",
									tunnelOuterColor:"#808080",
									isTunnel:true
								};
		this.groundSection[16] =  {	offset:860,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:0.8,
									gripOutside:0.6,
									patternDistance:1000,
									patternSize:10,
									groundColor:["#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0", "#A0A0A0", "#A0A0A0", "#A0A0A0", "#A0A0A0", "#A0A0A0"],
									groundLine:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
								};
		this.groundSection[17] =  {	offset:888,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:0.8,
									gripOutside:0.6,
									patternDistance:2000,
									patternSize:20,
									groundColor:["black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black", "black"],
									groundLine:[10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
									tunnelInnerHeight:120,
									tunnelOuterHeight:240,
									tunnelInnerColor:"black",
									tunnelOuterColor:"#808080",
									isTunnel:true
								};
		this.groundSection[18] =  {	offset:920,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:0.8,
									gripOutside:0.6,
									patternDistance:1000,
									patternSize:10,
									groundColor:["#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0", "#A0A0A0", "#A0A0A0", "#A0A0A0", "#A0A0A0", "#A0A0A0"],
									groundLine:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
								};
		this.groundSection[19] =  {	offset:2*this.roadLength,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:0.8,
									gripOutside:0.6,
									patternDistance:1000,
									patternSize:10,
									groundColor:["#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0", "#C0C0C0", "#A0A0A0", "#A0A0A0", "#A0A0A0", "#A0A0A0", "#A0A0A0"],
									groundLine:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
								};
		this.phaseCount = 30;
		
		this.weatherArea = new Array(1);
		this.weatherArea[0] = {	offset:0,
								image:"",
								color:"#BBC0D0",
								opacity:0
							  };
							  
		for (i=0; i<this.sceneryTypeCount; ++i) {
			this.sceneryMaster[i].door=this.sceneryMaster[i].door||0;
			this.sceneryMaster[i].line=this.sceneryMaster[i].line||0;
			this.sceneryMaster[i].collisionEffect=this.sceneryMaster[i].collisionEffect||0;
			this.sceneryMaster[i].sideW=this.sceneryMaster[i].sideW||0;
			this.sceneryMaster[i].frontW=this.sceneryMaster[i].w-2*this.sceneryMaster[i].sideW;
		}
		
	},
	
	/**
	 * Crée de manière hardcodée la description d'une course
	 * Cette méthode sert lors du design du circuit, pour la conception
	 * des virages et du relief, ainsi que pour le placement du décor.
	 * 	 
	 * Une fois la réalisation terminée, le code est basculé vers le RoadEncoder
	 * qui produit le XML correspondant à recopier côté serveur.
	 */
	createLevel4Road : function()
	{
		this.invalidRoad = false;
		this.roadLength = 1140; // nombre de segments 
		this.distanceStep = 500; // longueur d'un segment (500cm = 5m)
		this.roadAltitude = new Array(this.roadLength);
		this.roadAngle = new Array(this.roadLength);
		this.sceneryLeft = new Array(this.roadLength);
		this.sceneryRight = new Array(this.roadLength);
		this.finishLine = 550000;

		for (var index = 0; index<this.roadLength; ++index)
		{
			this.roadAltitude[index] = 0;
			this.roadAngle[index] = 0;
			this.sceneryLeft[index] = { x:-1.1, type:-1 };
			this.sceneryRight[index] = { x:1.1, type:-1 };
		}


		// description de la course : 1100 segments (+40 pour éviter de revoir le début)
		// 6 étapes
		//  - 1 : lac à droite, relief plat
		//  - 2 : collines
		//  - 3 : lac à gauche, relief plat
		//  - 4 : collines
		//  - 5 : lac à droite et à gauche
		//  - 6 : collines, route de graviers
		
		
		// d'abord le décor
		
		
		// puis les virages et le relief
		// étape 1
		this.addBend (   6, 3, 0.02, 0.04, 0, 0, 0, 0);
		this.addBend (  10, 4, 0.01, 0.02, 0, 0, 0, 0);
		this.addBend (  14, 2, -0.01, -0.01, 0, 0, 0, 0);
		this.addBend (  17, 3, 0.02, 0.02, 0, 0, 0, 0);
		this.addBend (  21, 5, -0.02, 0.05, 0, 0, 0, 0);
		this.addBend (  30, 4, 0.03, 0.05, 0, 0, 0, 0);
		this.addBend (  37, 4, 0.02, 0.03, 0, 0, 0, 0);
		this.addBend (  41, 2, -0.02, -0.02, 0, 0, 0, 0);
		this.addBend (  45, 3, -0.02, -0.02, 0, 0, 0, 0);
		this.addBend (  50, 2, 0.02, 0.02, 0, 0, 0, 0);
		this.addBend (  55, 8, -0.06, -0.09, 1, 6, 4, 2);
		
		// étape 2
		this.addPlateau (  60, 15, 1, 5, 500, 12, 1);
		this.addPlateau (  70, 20, 1, 100, 300, 40, 1);
		this.addPlateau (  85, 20, 1, 0, -600, 20, 1);
		this.addPlateau ( 110, 12, 1, 10, 400, 40, 1);
		this.addPlateau ( 120, 40, 1, 10, -500, 20, 1);
		this.addPlateau ( 160, 15, 1, 30, 400, 10, 1);
		this.addPlateau ( 165, 50, 1, 50, 500, 15, 1);
		this.addPlateau ( 170, 20, 1, 20, 100, 10, 1);
		this.addPlateau ( 190, 30, 1, 0, 700, 40, 1);
		this.addPlateau ( 195, 20, 1, 65, -200, 20, 1);
		this.addPlateau ( 220, 30, 1, 20, 300, 25, 1);
		this.addPlateau ( 260, 10, 1, 5, 200, 15, 1);
		this.addPlateau ( 270, 15, 1, 0, 200, 15, 1);
		
		this.addBend (  75, 10, 0.02, 0.05, 0, 0, 0, 0);
		this.addBend (  88, 12, 0.02, -0.05, 0, 0, 0, 0);
		this.addBend (  95, 15, 0, 0.02, 0, 0, 0, 0);
		this.addBend ( 120, 15, 0, -0.03, 0, 0, 0, 0);
		this.addBend ( 130, 15, 0, 0.04, 0, 0, 0, 0);
		this.addBend ( 140, 15, 0, -0.05, 0, 0, 0, 0);
		this.addBend ( 150, 15, 0, 0.06, 0, 0, 0, 0);
		this.addBend ( 180, 10, 0.02, 0.06, 0, 0, 0, 0);
		this.addBend ( 195, 20, 0.01, 0.04, 0, 0, 0, 0);
		this.addBend ( 205, 15, -0.01, -0.04, 0, 0, 0, 0);
		this.addBend ( 215, 10, 0.01, 0.04, 0, 0, 0, 0);
		this.addBend ( 230, 5, 0.06, 0.06, 0, 0, 0, 0);
		this.addBend ( 245, 10, 0.01, 0.02, 0, 0, 0, 0);
		this.addBend ( 260, 20, 0.04, 0.01, 0, 0, 0, 0);
		this.addBend ( 290, 5, -0.05, -0.07, 0, 0, 4, 2);

		// étape 3
		this.addBend ( 310, 30, 0.003, 0.009, 0, 0, 0, 0);
		this.addBend ( 345, 10, 0.01, 0.02, 0, 0, 0, 0);
		this.addBend ( 357, 8, 0.01, -0.04, 0, 0, 0, 0);
		this.addBend ( 365, 8, 0.01, 0.04, 0, 0, 0, 0);
		this.addBend ( 380, 5, 0.03, 0.05, 0, 0, 0, 0);
		this.addBend ( 389, 8, 0, 0.06, 1, 5, 4, 1);
		
		// étape 4
		this.addPlateau ( 400, 40, 1, 10, 700, 60, 1);
		this.addPlateau ( 410, 50, 1, 100, 1200, 60, 1);
		this.addPlateau ( 440, 20, 1, 10, -300, 30, 1);
		this.addPlateau ( 460, 15, 1, 10, -100, 15, 1);
		this.addPlateau ( 495, 30, 1, 40, 800, 40, 1);
		this.addPlateau ( 500, 40, .8, 20, 800, 30, .8);
		this.addPlateau ( 520, 30, 1, 40, 600, 40, 1);
		this.addPlateau ( 560, 40, 1, 20, -500, 30, 1);
		this.addPlateau ( 600, 10, 1, 0, 500, 12, 1);
		this.addPlateau ( 630, 40, 1, 20, 200, 15, 1);
		this.addPlateau ( 635, 20, 1, 0, 500, 25, 1);
		this.addPlateau ( 660, 40, 1, 0, -100, 30, 1);
		this.addPlateau ( 680, 20, 1, 10, 200, 30, 1);
		this.addPlateau ( 710, 10, 1, 10, 200, 10, 1);
		
		this.addBend (398, 12, 0.02, 0.04, 0, 0, 0, 0);
		this.addBend (412, 8, 0.00, 0.05, 0, 0, 0, 0);
		this.addBend (416, 8, 0.00, -0.05, 0, 0, 0, 0);
		this.addBend (420, 8, 0.00, 0.05, 0, 0, 0, 0);
		this.addBend (435, 30, 0.00, 0.03, 0, 0, 0, 0);
		this.addBend (465, 20, 0.00, -0.04, 0, 0, 0, 0);
		this.addBend (485, 10, 0.00, 0.05, 0, 0, 0, 0);
		this.addBend (498, 6, 0.02, 0.06, 0, 0, 0, 0);
		this.addBend (510, 30, 0.04, -0.04, 0, 0, 0, 0);
		this.addBend (550, 10, 0.02, 0.07, 0, 0, 0, 0);
		this.addBend (557, 10, -0.01, 0.03, 0, 0, 0, 0);
		this.addBend (579, 25, -0.01, 0.08, 0, 0, 0, 0);
		this.addBend (599, 10, -0.07, -0.07, 0, 0, 0, 0);
		this.addBend (615, 12, -0.03, -0.09, 0, 0, 0, 0);
		this.addBend (628, 10, 0.03, 0.04, 0, 0, 0, 0);
		this.addBend (640, 20, 0.02, -0.06, 0, 0, 0, 0);
		this.addBend (660, 12, 0.06, -0.06, 0, 0, 0, 0);
		this.addBend (680, 30, 0.02, 0.01, 0, 0, 0, 0);
		this.addBend (688, 6, 0.02, 0.04, 0, 0, 0, 0);
		this.addBend (699, 6, -0.02, -0.04, 0, 0, 0, 0);
		this.addBend (720, 25, 0.02, 0.07, 0, 0, 0, 0);

		// étape 5
		this.addBend (749, 15, 0.02, 0.04, 0, 0, 0, 0);
		this.addBend (760, 8, 0.00, -0.06, 0, 0, 0, 0);
		this.addBend (770, 20, 0.00, 0.07, 0, 0, 0, 0);
		this.addBend (795, 10, 0.00, -0.03, 0, 0, 0, 0);
		this.addBend (807, 5, 0.015, 0.05, 0, 0, 0, 0);
		this.addBend (820, 10, -0.015, -0.04, 0, 0, 0, 0);
		this.addBend (828, 14, 0, 0.06, 0, 0, 0, 0);
		this.addBend (850, 20, 0, -0.10, 0, 0, 4, 2);
		this.addBend (875, 10, -0.02, -0.04, 0, 0, 0, 0);
		
		// étape 6
		this.addPlateau ( 880, 60, 1, 100, 1200, 40, 1);
		this.addPlateau ( 900, 20, 1, 40, -400, 30, 1);
		this.addPlateau ( 940, 20, 1, 20, 600, 20, 1);
		this.addPlateau ( 945, 10, 1, 10, -100, 10, 1);
		this.addPlateau ( 980, 15, 1, 5, 400, 15, 1);
		this.addPlateau (1010, 15, 1, 0, 450, 15, 1);
		this.addPlateau (1030, 10, 1, 0, 100, 10, 1);
		this.addPlateau (1060, 15, 1, 0, 450, 15, 1);
		this.addPlateau (1080, 5, 1, 10, 100, 10, 1);
		
		this.addBend ( 885, 6, -0.02, -0.06, 0, 0, 0, 0);
		this.addBend ( 893, 6, 0.02, 0.07, 0, 0, 0, 0);
		this.addBend ( 901, 6, -0.02, -0.05, 0, 0, 0, 0);
		this.addBend ( 909, 6, 0.02, 0.04, 0, 0, 0, 0);
		this.addBend ( 917, 6, -0.02, -0.04, 0, 0, 0, 0);
		this.addBend ( 931, 12, 0, 0.05, 0, 0, 0, 0);
		this.addBend ( 940, 20, 0, -0.03, 0, 0, 0, 0);
		this.addBend ( 948, 20, 0, 0.03, 0, 0, 0, 0);
		this.addBend ( 960, 12, 0.05, 0.06, 0, 0, 0, 0);
		this.addBend ( 990, 25, 0.07, 0.02, 0, 0, 0, 0);
		this.addBend (1000, 20, -0.04, 0.03, 0, 0, 0, 0);
		this.addBend (1025, 15, -0.04, 0.04, 0, 0, 0, 0);
		this.addBend (1030, 30, 0, 0.09, 0, 0, 5, 1);
		this.addBend (1055, 20, 0, -0.02, 0, 0, 0, 0);
		this.addBend (1080, 30, 0.02, 0.04, 0, 0, 0, 0);

		this.editSceneryRaw(592, 1, 1, -1.1, 7, 0, -2);
		this.editSceneryRaw(812, 1, 1, -1.1, 7, 0, -2);
		
		// editSceneryRaw : function(offset, length, step, leftX, leftType, rightX, rightType)
		// addBend : function (offset, length, minAngle, maxAngle, warnSignLength, warnSignType, innerSignLength, innerSignType) 
		// addPlateau : function(offset, upLength, upTaper, plateauLength, plateauHeight, downLength, downTaper)
		
		
		// portique au départ
		this.editSceneryRaw(4, 1, 1, 0, 3, 1.08, 4);
		// portique à l'arrivée
		var finishSegment = this.finishLine/this.distanceStep;
		this.editSceneryRaw(finishSegment, 1, 1, 0, 3, 1.08, 4);
		
		this.editSceneryRaw(100, 1, 1, -1.5, 30, 1.5, 30);
		this.editSceneryRaw(200, 1, 1, -1.5, 31, 1.5, 31);
		this.editSceneryRaw(300, 1, 1, -1.5, 32, 1.5, 32);
		this.editSceneryRaw(400, 1, 1, -1.5, 33, 1.5, 33);
		this.editSceneryRaw(500, 1, 1, -1.5, 34, 1.5, 34);
		this.editSceneryRaw(600, 1, 1, -1.5, 35, 1.5, 35);
		this.editSceneryRaw(700, 1, 1, -1.5, 36, 1.5, 36);
		this.editSceneryRaw(800, 1, 1, -1.5, 37, 1.5, 37);
		this.editSceneryRaw(900, 1, 1, -1.5, 38, 1.5, 38);
		this.editSceneryRaw(1000, 1, 1, -1.5, 39, 1.5, 39);

		
		this.sceneryTypeCount = 41; // types d'objet
		this.sceneryMaster = new Array(this.sceneryTypeCount);
		this.sceneryMaster[0] = { w:14, h:48, src:"level000/picket.png", collisionLeft:1, collisionWidth:9};
		this.sceneryMaster[1] = { w:40, h:80, src:"level000/left_turn_sign.png", collisionLeft:18, collisionWidth:4 };
		this.sceneryMaster[2] = { w:40, h:80, src:"level000/right_turn_sign.png", collisionLeft:19, collisionWidth:4};
		this.sceneryMaster[3] = { w:504, h:216, src:"level000/portique.png", collisionLeft:0, collisionWidth:28 };
		this.sceneryMaster[4] = { w:28, h:216, src:"level000/portique_d.png", collisionLeft:0, collisionWidth:28 };
		this.sceneryMaster[5] = { w:40, h:82, src:"level000/left_warning_sign.png", collisionLeft:18, collisionWidth:4 };
		this.sceneryMaster[6] = { w:40, h:82, src:"level000/right_warning_sign.png", collisionLeft:18, collisionWidth:4 };
		this.sceneryMaster[7] = { w:40, h:105, src:"level000/s_curve_sign.png", collisionLeft:18, collisionWidth:4 };
		this.sceneryMaster[8] = { w:100, h:50, src:"level000/100.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[9] = { w:100, h:50, src:"level000/100.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[10] = { w:100, h:50, src:"level000/100.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[11] = { w:100, h:50, src:"level000/100.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[12] = { w:100, h:50, src:"level000/100.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[13] = { w:100, h:50, src:"level000/100.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[14] = { w:100, h:50, src:"level000/100.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[15] = { w:100, h:50, src:"level000/100.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[16] = { w:100, h:50, src:"level000/100.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[17] = { w:100, h:50, src:"level000/100.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[18] = { w:100, h:50, src:"level000/100.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[19] = { w:100, h:50, src:"level000/100.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[20] = { w:100, h:50, src:"level000/100.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[21] = { w:100, h:50, src:"level000/100.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[22] = { w:100, h:50, src:"level000/100.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[23] = { w:100, h:50, src:"level000/100.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[24] = { w:100, h:50, src:"level000/100.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[25] = { w:100, h:50, src:"level000/100.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[26] = { w:100, h:50, src:"level000/100.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[27] = { w:100, h:50, src:"level000/100.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[28] = { w:100, h:50, src:"level000/100.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[29] = { w:100, h:50, src:"level000/100.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[30] = { w:100, h:50, src:"level000/100.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[31] = { w:100, h:50, src:"level000/200.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[32] = { w:100, h:50, src:"level000/300.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[33] = { w:100, h:50, src:"level000/400.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[34] = { w:100, h:50, src:"level000/500.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[35] = { w:100, h:50, src:"level000/600.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[36] = { w:100, h:50, src:"level000/700.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[37] = { w:100, h:50, src:"level000/800.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[38] = { w:100, h:50, src:"level000/900.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[39] = { w:100, h:50, src:"level000/1000.png", collisionLeft:0, collisionWidth:0 };
		this.sceneryMaster[40] = { w:100, h:50, src:"level000/1100.png", collisionLeft:0, collisionWidth:0 };

		
		this.horizon = {w:1720, h:520, src:"level000/bg_boussole.png", reflection:"level000/bg_bluewater.png" };
		
		this.playerLapRecord = { name:'', car:0, time:0 };
		this.qualifyingTime = 6500; // 1mn 5s 

		this.groundSection = new Array(7);
		this.groundSection[0] =  {	offset:0,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:0.7,
									gripOutside:0.6,
									patternDistance:1000,
									patternSize:10,
									groundColor:["#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020"],
									groundLine:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
									groundRight:[9, 3, 2.5, 1.5, 1.6, 1.9, 4, 9]
								};
		this.groundSection[1] =  {	offset:60,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:0.7,
									gripOutside:0.6,
									patternDistance:1000,
									patternSize:10,
									groundColor:["#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020"],
									groundLine:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
								};
		this.groundSection[2] =  {	offset:300,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:0.7,
									gripOutside:0.6,
									patternDistance:1000,
									patternSize:10,
									groundColor:["#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020"],
									groundLine:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
									groundLeft:[-9, -3, -2, -1.1, -1.2, -1.9, -4, -9]
								};
		this.groundSection[3] =  {	offset:400,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:0.7,
									gripOutside:0.6,
									patternDistance:1000,
									patternSize:10,
									groundColor:["#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020"],
									groundLine:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
								};
		this.groundSection[4] =  {	offset:740,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:0.7,
									gripOutside:0.6,
									patternDistance:1000,
									patternSize:10,
									groundColor:["#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020"],
									groundLine:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
									groundLeft:[-9, -3, -2.5, -1.5, -1.6, -1.9, -4, -9, -40, -40, -40, -40, -40],
									groundRight:[40, 40, 40, 40, 40, 9, 3, 2.5, 1.5, 1.6, 1.9, 4, 9]
								};
		this.groundSection[5] =  {	offset:880,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:0.7,
									gripOutside:0.6,
									patternDistance:1000,
									patternSize:10,
									groundColor:["#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020"],
									groundLine:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
								};
		this.groundSection[6] =  {	offset:2*this.roadLength,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:0.9,
									gripOutside:0.4,
									patternDistance:1000,
									patternSize:10,
									groundColor:["#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020", "#80C020"],
									groundLine:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
								};
		this.phaseCount = 10;
		
		this.weatherArea = new Array(1);
		this.weatherArea[0] = {	offset:0,
								image:"",
								color:"#BBC0D0",
								opacity:0
							  };
							  
		for (i=0; i<this.sceneryTypeCount; ++i) {
			this.sceneryMaster[i].door=this.sceneryMaster[i].door||0;
			this.sceneryMaster[i].line=this.sceneryMaster[i].line||0;
			this.sceneryMaster[i].collisionEffect=this.sceneryMaster[i].collisionEffect||0;
		}
	},

	createDemoRoad1 : function()
	{
		this.roadLength = 24; // nombre de segments 
		this.distanceStep = 500; // longueur d'un segment (500cm = 5m)
		this.roadAltitude = new Array(this.roadLength);
		this.roadAngle = new Array(this.roadLength);
		this.sceneryLeft = new Array(this.roadLength);
		this.sceneryRight = new Array(this.roadLength);
		this.finishLine = this.roadLength*this.distanceStep;
		
		for (var index = 0; index<this.roadLength; ++index)
		{
			this.roadAltitude[index] = 0;
			this.roadAngle[index] = 0;
			this.sceneryLeft[index] = { x:-1.1, type:-1 };
			this.sceneryRight[index] = { x:1.1, type:-1 };
		}

		
		// arbres
		this.editSceneryRaw(0, this.roadLength, 2, -1.4, 0, 1.4, 0);

		// virage
		this.addBend(4, 6, -0.08, -0.08, 0, 0, 4, 1) ;

		// couleur du sol
		this.groundSection = new Array(2);
		this.groundSection[0] =  {	offset:0,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:1.0,
									gripOutside:0.5,
									patternDistance:1000,
									patternSize:5,
									groundColor:["#658A70", "#658A70", "#658A70", "#3B6150", "#3B6150"],
									groundLine:[0, 0, 0, 1, 1]
								};
		this.groundSection[1] =  {	offset:72,
									leftWidth:1.0,
									rightWidth:1.0,
									gripInside:1.0,
									gripOutside:0.5,
									patternDistance:1000,
									patternSize:5,
									groundColor:["#658A70", "#658A70", "#658A70", "#3B6150", "#3B6150"],
									groundLine:[0, 0, 0, 1, 1]
								};
		this.phaseCount = 3;

		this.weatherArea = new Array(1);
		this.weatherArea[0] = {	offset:0,
								image:"",
								color:"",
								opacity:0
							  };
		
		this.folder = "level001/";
		
		this.sceneryTypeCount = 2; // types d'objet
		this.sceneryMaster = new Array(this.sceneryTypeCount);
		this.sceneryMaster[0] = { w:191, h:220, src:this.folder+"chataignier.png", collisionLeft:89, collisionWidth:13, sideW:0, frontW:191};
		this.sceneryMaster[1] = { w:78, h:40, src:this.folder+"panneau_virage_droite.gif", collisionLeft:0, collisionWidth:78, sideW:0, frontW:78 };		
		this.horizon = {w:1280, h:470, src:this.folder+"bg_cevennes.png" };
	},
	
	/**
	 * Renvoie un champ non signé encodé sur 2 octets
	 * Encodage de type base64 : 0-9,A-Z,a-Z,-,_ pour 64 valeurs
	 * Si un caractère différent est rencontré, le flag this.invalidRoad est basculé à true
	 * Paramètres :
	 *  - text : la chaine de caractères décrivant la route
	 *  - offset : l'offset du premier octet à lire
	 *  - factor : le facteur multiplicatif (pas) à appliquer au résultat
	 */
	decodeUnsigned16 : function(text, offset, factor) 
	{
		var code0 = text.charCodeAt(offset);
		var value0 = (code0 == 45 ? 62 : (code0 < 58 ? code0-48 : (code0 < 91 ? code0-55 : (code0 == 95 ? 63 : (code0 < 123 ? code0 - 61 : 64)))));
		var code1 = text.charCodeAt(offset+1);
		var value1 = (code1 == 45 ? 62 : (code1 < 58 ? code1-48 : (code1 < 91 ? code1-55 : (code1 == 95 ? 63 : (code1 < 123 ? code1 - 61 : 64)))));
		this.invalidRoad = this.invalidRoad || (value0 < 0) || (value0 > 63) || (value1 < 0) || (value1 > 63);
		return factor*(value0*64+value1);
	},

    /**
	 * Renvoie un champ signé encodé sur 2 octets
	 * Encodage de type base64 : 0-9,A-Z,a-Z,-,_ pour 64 valeurs
	 * Les valeurs négatives sont codées par décalage, non par complément à 2
	 * Si un caractère différent est rencontré, le flag this.invalidRoad est basculé à true
	 * Paramètres :
	 *  - text : la chaine de caractères décrivant la route
	 *  - offset : l'offset du premier octet à lire
	 *  - factor : le facteur multiplicatif (pas) à appliquer au résultat
	 */
	decodeSigned16 : function(text, offset, factor)
	{
		var code0 = text.charCodeAt(offset);
		var value0 = (code0 == 45 ? 62 : (code0 < 58 ? code0-48 : (code0 < 91 ? code0-55 : (code0 == 95 ? 63 : (code0 < 123 ? code0 - 61 : 64)))));
		var code1 = text.charCodeAt(offset+1);
		var value1 = (code1 == 45 ? 62 : (code1 < 58 ? code1-48 : (code1 < 91 ? code1-55 : (code1 == 95 ? 63 : (code1 < 123 ? code1 - 61 : 64)))));
		this.invalidRoad = this.invalidRoad || (value0 < 0) || (value0 > 63) || (value1 < 0) || (value1 > 63);
		return factor*(value0*64+value1-2048);
	},

	
	/**
	 * Renvoie un champ non signé encodé sur 1 octet
	 * Encodage de type base64 : 0-9,A-Z,a-Z,-,_ pour 64 valeurs
	 * Si un caractère différent est rencontré, le flag this.invalidRoad est basculé à true
	 * Paramètres :
	 *  - text : la chaine de caractères décrivant la route
	 *  - offset : l'offset de l'octet à lire
	 *  - factor : le facteur multiplicatif (pas) à appliquer au résultat
	 */
	decodeUnsigned8 : function(text, offset, factor) 
	{
		var code = text.charCodeAt(offset);
		var value = (code == 45 ? 62 : (code < 58 ? code-48 : (code < 91 ? code-55 : (code == 95 ? 63 : (code < 123 ? code - 61 : 64)))));
		this.invalidRoad = this.invalidRoad || (value < 0) || (value > 63);
		return value*factor;
	},

	/**
	 * Renvoie un champ signé encodé sur 1 octet
	 * Encodage de type base64 : 0-9,A-Z,a-Z,-,_ pour 64 valeurs
	 * Les valeurs négatives sont codées par décalage, non par complément à 2
	 * Si un caractère différent est rencontré, le flag this.invalidRoad est basculé à true
	 * Paramètres :
	 *  - text : la chaine de caractères décrivant la route
	 *  - offset : l'offset de l'octet à lire
	 *  - factor : le facteur multiplicatif (pas) à appliquer au résultat
	 */
	decodeSigned8 : function(text, offset, factor) 
	{
		var code = text.charCodeAt(offset);
		var value = (code == 45 ? 62 : (code < 58 ? code-48 : (code < 91 ? code-55 : (code == 95 ? 63 : (code < 123 ? code - 61 : 64)))));
		this.invalidRoad = this.invalidRoad || (value < 0) || (value > 63);
		return (value-32)*factor;
	}
	
}


