//////////////////////////////////////////////////////////////////////////////
//                                                                          //
// Wii Opera SDK - Raycasting v1.8.10 2008-12-14                            //
// (c) 2007-2008 Daniel Gump. All Rights Reserved.                          //
// http://wiioperasdk.com, http://hullbreachonline.com                      //
// hullbreach@hullbreachonline.com                                          //
//                                                                          //
//  Wii is a trademark of Nintendo Co., Ltd.                                //
//  Opera is a trademark of Opera, ASA.                                     //
//  This software package is not associated with either company             //
//  but was created to support users of both.  Its alternative name         //
//  when supporting other products is the HULLBREACH SDK.                   //
//                                                                          //
//  Redistribution and use in source and binary forms, with or without      //
//  modification, are permitted provided that the following conditions      //
//  are met:                                                                //
//    * Redistributions of source code must retain the above copyright      //
//      notice, this list of conditions and the following disclaimer.       //
//    * Redistributions in binary form must reproduce the above copyright   //
//      notice, this list of conditions and the following disclaimer in     //
//      the documentation and/or other materials provided with the          //
//      distribution.                                                       //
//    * Neither the names HULLBREACH ONLINE nor WII OPERA SDK nor the names //
//      of its contributors may be used to endorse or promote products      //
//      derived from this software without specific prior written           //
//      permission.                                                         //
//    * If the explicit purpose of the software is not to support the       //
//      Nintendo Wii or the Opera Web browser, then the names of such must  //
//      not be used in any derived product. The name shall be the           //
//      HULLBREACH SDK with a reference link to http://hullbreachonline.    //
//                                                                          //
//  THIS SOFTWARE IS PROVIDED BY Daniel Gump ''AS IS'' AND ANY EXPRESS OR   //
//  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED          //
//  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE  //
//  DISCLAIMED. IN NO EVENT SHALL Daniel Gump BE LIABLE FOR ANY DIRECT,     //
//  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES      //
//  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR      //
//  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)      //
//  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,     //
//  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING   //
//  IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE      //
//  POSSIBILITY OF SUCH DAMAGE.                                             //
//////////////////////////////////////////////////////////////////////////////
function RayCast(){

	//Canvas size
	WIDTH = 320; HEIGHT = 240;

	//Player's coordinates
	X = 1.5; Y = 1.5;
	U = 0; V = 1;

	WOBBLE = 0;

	PLANEU = 1; PLANEV = 0;

	ANGLE = 0;
	RESOLUTION = 1;

	ROTATESPEED = .1; MOVESPEED = .1; DUCK = false; ALTITUDE = 0;

	WALLSCENE = [];
	TEXTURES = [];
	TELEPORTS = [];

	CANVAS = null;

	SHADESCENE = 0;
	this.FLASHLIGHT = 1; this.GOURAUD = 2; this.SHADOW = 3;

	SKYFILL = "rgb(0,0,0)"; GROUNDFILL = "rgb(0,0,0)";
	SKYIMAGE = 0; GROUNDIMAGE = 0;

	TELEPORTING = 0;
	COLLISION = 0;
}
RayCast.prototype.generateScene = function(){

	CANVAS.fillStyle=SKYFILL;
	CANVAS.fillRect(0,0, WIDTH, HEIGHT>>1);
	CANVAS.fillStyle=GROUNDFILL;
	CANVAS.fillRect(0,HEIGHT>>1, WIDTH, HEIGHT);
	if(SKYIMAGE){
		CANVAS.drawImage(TEXTURES[SKYIMAGE], -ANGLE*WIDTH/(2*Math.PI),0);
		CANVAS.drawImage(TEXTURES[SKYIMAGE], (2*Math.PI-ANGLE)*WIDTH/(2*Math.PI),0);
	}
	if(GROUNDIMAGE){
		CANVAS.drawImage(TEXTURES[GROUNDIMAGE], -ANGLE*WIDTH/(2*Math.PI),0);
		CANVAS.drawImage(TEXTURES[GROUNDIMAGE], (2*Math.PI-ANGLE)*WIDTH/(2*Math.PI),0);
	}

	//Player location
	var rayX = X;
	var rayY = Y;

	//Ray length data
	var sideDistX, sideDistY;

	//Stepping data
	var stepX, stepY;
	var side, hit;

	for(var column=WIDTH, camerastep = 2/WIDTH; column>=0; column-=RESOLUTION){
		var cameraX = column*camerastep-1;

		//Ray length
		var deltaDistX = Math.sqrt(1+((rayV=V+PLANEV*cameraX)*rayV)/((rayU=U+PLANEU*cameraX)*rayU));
		var deltaDistY = Math.sqrt(1+(rayU*rayU)/(rayV*rayV));

		//Find initial side dist

		stepX = stepY = -1;
		sideDistX = rayX-(mapX=X|0);
		sideDistY = rayY-(mapY=Y|0);

		if(rayU>=0){
			stepX = 1;
			sideDistX = 1-sideDistX;
		}
		if(rayV>=0){
			stepY = 1;
			sideDistY = 1-sideDistY;
		}
		sideDistX*=deltaDistX;
		sideDistY*=deltaDistY;

		//Step DDA
		do{
			if(sideDistX<sideDistY){
				sideDistX+=deltaDistX;
				mapX+=stepX;
				side=0;
			}else{
				sideDistY+=deltaDistY;
				mapY+=stepY;
				side=1;
			}
		}while(!(hit=WALLSCENE[mapX][mapY]));

		var drawStart = ((HEIGHT-(lineHeight=HEIGHT/((perpWallDist=(!side)?(mapX-rayX+(1-stepX)*.5)/rayU:(mapY-rayY+(1-stepY)*.5)/rayV)>>31?-perpWallDist:perpWallDist)))>>1)+(walkBounce=((DUCK?-2*HEIGHT:HEIGHT)+ALTITUDE+HEIGHT*Math.abs(Math.cos(WOBBLE)))/(20*perpWallDist));
		var drawEnd = ((lineHeight+HEIGHT)>>1)-drawStart+walkBounce;

		var wallX = side?rayX+perpWallDist*rayU:rayY+perpWallDist*rayV;

		var texture=TEXTURES[WALLSCENE[mapX][mapY]];
		var texX = ((wallX-(wallX|0))*texture.width)|0;
		if((!side && rayU>0) || (side && rayV<0)) texX = texture.width - texX - 1;

		CANVAS.drawImage(texture, texX,0, 1,texture.height, column,drawStart, RESOLUTION,drawEnd);
		if(SHADESCENE){
			var col=0;
			if(SHADESCENE==this.FLASHLIGHT)
				col = (cameraX<0?-cameraX:cameraX)*1.5;
			else if(SHADESCENE==this.GOURAUD)
				col = 0.5-column/WIDTH;
			else if(SHADESCENE==this.SHADOW)
				if(side) col=stepY*.25;

			if(col>0)
				CANVAS.fillStyle="rgba(0,0,0,"+col+")";
			else
				CANVAS.fillStyle="rgba(255,255,255,"+(-col)+")";
			if(col) CANVAS.fillRect(column,drawStart-1,RESOLUTION,drawEnd+2);
		}
	}
	if(TELEPORTING){
		CANVAS.fillStyle="rgba(255,255,255,"+(TELEPORTING/10)+")";
		CANVAS.fillRect(0,0, WIDTH, HEIGHT);
		TELEPORTING--;
	}

}
RayCast.prototype.loadScene = function(array){
	WALLSCENE = {};
	if(WALLSCENE = array.split(";")) for (i=WALLSCENE.length;--i>=0;) WALLSCENE[i] = WALLSCENE[i].split(",");
}
RayCast.prototype.duck = function(){ DUCK=true; }
RayCast.prototype.unDuck = function(){ DUCK=false; }
RayCast.prototype.moveForward = function(){
	COLLISION=1;
	if(!WALLSCENE[(X+U*MOVESPEED)|0][Y|0]){
		X+=U*MOVESPEED;
		COLLISION=0;
	}
	if(!WALLSCENE[X|0][(Y+V*MOVESPEED)|0]){
		Y+=V*MOVESPEED;
		COLLISION=0;
	}
	WOBBLE+=6*MOVESPEED%(Math.PI*2);
}
RayCast.prototype.moveBackward = function(){
	COLLISION=1;
	if(!WALLSCENE[(X-U*MOVESPEED)|0][Y|0]){
		X-=U*MOVESPEED;
		COLLISION=0;
	}
	if(!WALLSCENE[X|0][(Y-V*MOVESPEED)|0]){
		Y-=V*MOVESPEED;
		COLLISION=0;
	}
	WOBBLE+=6*MOVESPEED%(Math.PI*2);
}
RayCast.prototype.moveLeft = function(){
	COLLISION=1;
	if(!WALLSCENE[(X-V*MOVESPEED)|0][Y|0]){
		X-=V*MOVESPEED;
		COLLISION=0;
	}
	if(!WALLSCENE[X|0][(Y+U*MOVESPEED)|0]){
		Y+=U*MOVESPEED;
		COLLISION=0;
	}
	WOBBLE+=6*MOVESPEED%(Math.PI*2);
}
RayCast.prototype.moveRight = function(){
	COLLISION=1;
	if(!WALLSCENE[(X+V*MOVESPEED)|0][Y|0]){
		X+=V*MOVESPEED;
		COLLISION=0;
	}
	if(!WALLSCENE[X|0][(Y-U*MOVESPEED)|0]){
		Y-=U*MOVESPEED;
		COLLISION=0;
	}
	WOBBLE+=6*MOVESPEED%(Math.PI*2);
}

RayCast.prototype.rotateLeft = function(){
	if((ANGLE-=ROTATESPEED)<0) ANGLE+=Math.PI*2;

	U = Math.sin(ANGLE);
	V = Math.cos(ANGLE);
	PLANEU = Math.sin(ANGLE+Math.PI/2);
	PLANEV = Math.cos(ANGLE+Math.PI/2);
}
RayCast.prototype.rotateRight = function(){
	if((ANGLE+=ROTATESPEED)>Math.PI*2) ANGLE-=Math.PI*2;

	U = Math.sin(ANGLE);
	V = Math.cos(ANGLE);
	PLANEU = Math.sin(ANGLE+Math.PI/2);
	PLANEV = Math.cos(ANGLE+Math.PI/2);
}
RayCast.prototype.checkTeleports = function(){
	for(tele=TELEPORTS.length;--tele>=0;){
		if(TELEPORTS[tele][0]==.5+(Y|0) && TELEPORTS[tele][1]==.5+(X|0)){
			if(TELEPORTS[tele][4]) document.location.href = TELEPORTS[tele][4];
			TELEPORTING=10;
			Y = TELEPORTS[tele][2];
			X = TELEPORTS[tele][3];
		}
	}
}
RayCast.prototype.isCollided = function(){ return COLLISION; }
RayCast.prototype.setAltitude = function(alt){ ALTITUDE=alt>=-5*HEIGHT?(alt<=5*HEIGHT?alt:5*HEIGHT):-5*HEIGHT; }
RayCast.prototype.setDirection = function(angle){
	U = Math.sin(ANGLE=angle*Math.PI/180);
	V = Math.cos(ANGLE);
	PLANEU = Math.sin(ANGLE+Math.PI/2);
	PLANEV = Math.cos(ANGLE+Math.PI/2);
}
RayCast.prototype.setGroundColor = function(r,g,b){ GROUNDFILL="rgb("+(r<0?0:(r>255?255:r|0))+","+(g<0?0:(g>255?255:g|0))+","+(b<0?0:(b>255?255:b|0))+")"; }
RayCast.prototype.setGroundImage = function(img){ GROUNDIMAGE=img; }
RayCast.prototype.setLocation = function(x,y){ X=y; Y=x; }
RayCast.prototype.setResolution = function(res){ RESOLUTION=res; }
RayCast.prototype.setShade = function(s){ SHADESCENE=s; }
RayCast.prototype.setSize = function(w,h){ WIDTH=w; HEIGHT=h; }
RayCast.prototype.setSkyColor = function(r,g,b){ SKYFILL="rgb("+(r<0?0:(r>255?255:r|0))+","+(g<0?0:(g>255?255:g|0))+","+(b<0?0:(b>255?255:b|0))+")"; }
RayCast.prototype.setSkyImage = function(img){ SKYIMAGE=img; }
RayCast.prototype.setTeleports = function(teleports){ TELEPORTS=teleports; }
RayCast.prototype.setTextures = function(textures){ TEXTURES=textures; }
RayCast.prototype.setTurningSpeed = function(speed){ ROTATESPEED=(speed<=.2?speed:(speed>=.05?speed:.05)); }
RayCast.prototype.setWalkingSpeed = function(speed){ MOVESPEED=(speed<=.25?speed:(speed>=.05?speed:.05)); }
RayCast.prototype.setWalls = function(Walls){ WALLSCENE = Walls; }
RayCast.prototype.initialize = function(canvas){ CANVAS = canvas.getContext("2d"); }

RayCaster = new RayCast();