
function ENTITY_CREATE_player( params )
{
	Player =
	{
		position = vec3( @params.x || 0, @params.y || 0, @params.z || 0 ),
		direction = @params.direction.normalized || vec3( 0, 1, 0 ),
		camera = null,
		cam_angle = 0.0,
		cam_zangle = 0.0,
		cam_ignoreOnce = true,
		move_noclip = false,
		
		portal_h0 = null,
		portal_h1 = null,
		portal_movement = 0,
		can_switch_portals = 0,
		portal_orig_fogcolor = null,
		portal_orig_fogdensity = null,
		portal_orig_zfar = null,
		
		body = null,
		
		health = 20,
		death_timeout = 1,
		hit_timeout = 0.0,
	};
	Player.cam_angle = vec2(Player.direction.x,Player.direction.y).angle;
	
	sh_capsuleZ = Bullet_CreateCapsuleShapeZ( 0.25, 0.75 );
	rbci = Bullet_CreateRigidBodyCreationInfo();
	rbci.collisionShape = sh_capsuleZ;
	rbci.mass = 80;
	rbci.position = Player.position;
	body = Bullet_CreateRigidBody( rbci );
	body.activationState = Bullet_DISABLE_DEACTIVATION;
	body.linearDamping = 0.96;
	gSGR_BulletWorld.addRigidBody( body, 0x02 );
	Player.body = body;
	
	gSGR_Actions.add_action( "move_fwd" );
	gSGR_Actions.add_action( "move_lft" );
	gSGR_Actions.add_action( "move_bwd" );
	gSGR_Actions.add_action( "move_rgt" );
	gSGR_Actions.add_action( "enable_noclip" );
	gSGR_Actions.add_action( "use" );
	gSGR_Actions.add_action( "switch_worlds" );
	
	gSGR_Actions.add_default_binding( "move_fwd", SDLK_W );
	gSGR_Actions.add_default_binding( "move_lft", SDLK_A );
	gSGR_Actions.add_default_binding( "move_bwd", SDLK_S );
	gSGR_Actions.add_default_binding( "move_rgt", SDLK_D );
	gSGR_Actions.add_default_binding( "use", SDLK_E );
	gSGR_Actions.add_default_binding( "enable_noclip", SDLK_N );
	gSGR_Actions.add_default_binding( "switch_worlds", SDLK_T );
	
	Player.ui_font = SS_CreateFont( "fonts/gauge-heavy.ttf", gSGR_Window.height / 30 );
	
	function Player.added()
	{
		this.camera = camera = SS3D_CreateCamera();
		gSGR_Scene.camera = camera;
		global PLAYER = this;
		
		this.camera.angle = 90;
		this.camera.znear = 0.01;
		this.camera.zfar = 1000;
	}
	
	function Player.removed()
	{
		gSGR_BulletWorld.removeRigidBody( this.body );
		global PLAYER = null;
	}
	
	function Player.serialize()
	{
		innerdata =
		{
			cam_angle = this.cam_angle,
			cam_zangle = this.cam_zangle,
			cam_ignoreOnce = this.cam_ignoreOnce,
			move_noclip = this.move_noclip,
			
			portal_h0 = this.portal_h0,
			portal_h1 = this.portal_h1,
			portal_movement = this.portal_movement,
			can_switch_portals = this.can_switch_portals,
			portal_orig_fogcolor = this.portal_orig_fogcolor,
			portal_orig_fogdensity = this.portal_orig_fogdensity,
			portal_orig_zfar = this.portal_orig_zfar,
			
			health = this.health,
			death_timeout = this.death_timeout,
			hit_timeout = this.hit_timeout,
			
			b_pos = this.body.position,
			b_lvel = this.body.linearVelocity,
		};
		p = this.position;
		return "player", { x = p.x, y = p.y, z = p.z, direction = this.direction }, innerdata;
	}
	
	function Player.can_reuse( type, params, data )
	{
		return type == "player";
	}
	
	function Player.unserialize( data )
	{
		this.
		{
			cam_angle = data.cam_angle,
			cam_zangle = data.cam_zangle,
			cam_ignoreOnce = data.cam_ignoreOnce,
			move_noclip = data.move_noclip,
			
			portal_h0 = data.portal_h0,
			portal_h1 = data.portal_h1,
			portal_movement = data.portal_movement,
			can_switch_portals = data.can_switch_portals,
			portal_orig_fogcolor = data.portal_orig_fogcolor,
			portal_orig_fogdensity = data.portal_orig_fogdensity,
			portal_orig_zfar = data.portal_orig_zfar,
			
			health = data.health,
			death_timeout = data.death_timeout,
			hit_timeout = data.hit_timeout,
		};
		
		this.body.position = data.b_pos;
		this.body.linearVelocity = data.b_lvel;
	}
	
	function Player.getUseInfo()
	{
		if( this.health <= 0 || this.portal_movement )
			return null;
		
		cur_obj = null;
		cur_act = null;
		cur_dst = 100;
		
		foreach( ent : gSGR_World.entities )
		{
			if( @ent._use_test )
			{
				(act,dst) = ent._use_test( this.position );
				if( act && dst < cur_dst )
				{
					cur_obj = ent;
					cur_act = act;
					cur_dst = dst;
				}
			}
		}
		
		return cur_act, cur_obj;
	}
	
	function Player.updateDirection( mmspeed )
	{
		if( gSGR_MouseMove.length && !current_cutscene )
		{
			if( !this.cam_ignoreOnce && !Game.PrevPaused )
			{
				this.cam_angle -= gSGR_MouseMove.x * mmspeed;
				this.cam_zangle -= gSGR_MouseMove.y * mmspeed;
			}
			else this.cam_ignoreOnce = false;
		}
		this.cam_zangle = clamp( this.cam_zangle, -M_PI*0.49, M_PI*0.49 );
		
		cos_angle = cos( this.cam_angle );
		sin_angle = sin( this.cam_angle );
		cos_zangle = cos( this.cam_zangle );
		sin_zangle = sin( this.cam_zangle );
		this.direction = vec3( cos_angle * cos_zangle, sin_angle * cos_zangle, sin_zangle );
	}
	
	function Player.updateNoclip( dt )
	{
		this.updateDirection( 0.02 );
		cos_angle = cos( this.cam_angle );
		sin_angle = sin( this.cam_angle );
		cos_zangle = cos( this.cam_zangle );
		sin_zangle = sin( this.cam_zangle );
		dir = this.direction;
		right = vec3( sin_angle, -cos_angle, 0 );
		speed = 10;
	//	if( gSGR_Actions.get( "move_faster" ) ) speed *= 5;
		if( gSGR_Actions.get( "move_fwd" ) ) this.position += dir * dt * speed;
		if( gSGR_Actions.get( "move_bwd" ) ) this.position -= dir * dt * speed;
		if( gSGR_Actions.get( "move_rgt" ) ) this.position += right * dt * speed;
		if( gSGR_Actions.get( "move_lft" ) ) this.position -= right * dt * speed;
	}
	
	function Player.updateMovement( dt )
	{
		if( this.health <= 0 )
			return;
		
		this.updateDirection( 0.01 );
		
		speed = 2000;
		
		cos_angle = cos( this.cam_angle );
		sin_angle = sin( this.cam_angle );
		fwdir = vec2( cos_angle, sin_angle );
		right = vec2( sin_angle, -cos_angle );
		
		if( !this.portal_movement && !current_cutscene )
		{
			movedir = vec2(0,0);
			if( gSGR_Actions.get( "move_fwd" ) ) movedir += fwdir;
			if( gSGR_Actions.get( "move_bwd" ) ) movedir -= fwdir;
			if( gSGR_Actions.get( "move_rgt" ) ) movedir += right;
			if( gSGR_Actions.get( "move_lft" ) ) movedir -= right;
			movedir = movedir.normalized;
			
			invcvf = 1 - this.body.linearVelocity.length / speed * 500;
			this.body.applyCentralImpulse( vec3(movedir.x,movedir.y,0) * dt * speed * invcvf );
			bpos = this.body.position;
			
			(rr,cs,rn,rp) = gSGR_BulletWorld.rayTestClosest( bpos, bpos + vec3(0,0,-20) );
			if( rr === null )
			{
				// ?
			}
			else
			{
				bpos.z = rp.z + 1;
				this.body.position = bpos;
				if( abs(rn.z) < 0.8 )
				{
					rightdir = vec3_cross( rn, vec3(0,0,1) );
					downdir = vec3_cross( rn, rightdir ).normalized;
			//		println("IMP:"$rn.z$"/"$downdir$"/"$ftime());
					this.body.linearVelocity += downdir * 10 * dt;
				}
			}
			this.position = this.body.position;
		}
	}
	
	function Player.updateCamera()
	{
		if( current_cutscene )
			return;
		
		this.camera.position = this.position;
		this.camera.direction = this.direction;
		this.camera.aspect = gSGR_Window.width / gSGR_Window.height;
	}
	
	function Player.hit()
	{
		this.health -= 2;
		this.hit_timeout = 0.2;
	}
	
	function Player.tick( dt )
	{
		if( gSGR_Actions.is_pressed( "enable_noclip" ) )
			this.move_noclip = !this.move_noclip;
		
		if( gSGR_Actions.is_pressed( "use" ) )
		{
			(act,obj) = this.getUseInfo();
			if( act )
				obj._use_act();
		}
		
		if( this.move_noclip )
			this.updateNoclip( dt );
		else
			this.updateMovement( dt );
		
		p = this.position;
		this.can_switch_portals = INSIDE_PORTAL( p.x, p.y, p.z );
		if( gSGR_Actions.get( "switch_worlds" ) && !this.portal_movement )
		{
			this.portal_movement = this.can_switch_portals;
			this.portal_h0 = this.position.z;
			this.portal_h1 = this.position.z + this.portal_movement;
			this.portal_orig_fogcolor = gSGR_Scene.fogColor;
			this.portal_orig_fogdensity = gSGR_Scene.fogDensity;
			this.portal_orig_zfar = gSGR_Scene.camera.zfar;
		}
		if( this.portal_movement )
		{
			this.position.z += this.portal_movement * dt;
			this.body.position = this.position;
			
			ff = ( this.position.z - this.portal_h0 ) / this.portal_movement;
			ff = pow( clamp( 1 - abs( ff * 2 - 1 ), 0, 1 ), 0.5 );
			cf = this.portal_orig_fogcolor;
			ct = vec3(0.6,0.05,0.02);
			df = this.portal_orig_fogdensity;
			dto = 5;
			zf = 100;
			zt = 2;
			gSGR_Scene.fogColor = lerp( cf, ct, ff );
			gSGR_Scene.fogDensity = lerp( df, dto, ff );
			gSGR_Scene.camera.zfar = lerp( zf, zt, pow(ff,0.02) );
			
			if( ( this.position.z - this.portal_h0 ) / this.portal_movement > 1 )
			{
				this.position.z = this.portal_h1;
				this.body.position = this.position;
				gSGR_BulletWorld.removeRigidBody( this.body );
				gSGR_BulletWorld.addRigidBody( this.body, 0x02 );
				this.portal_movement = 0;
				gSGR_Scene.fogColor = this.portal_orig_fogcolor;
				gSGR_Scene.fogDensity = this.portal_orig_fogdensity;
				gSGR_Scene.camera.zfar = this.portal_orig_zfar;
			}
		}
		
		if( @gSGR_Keys[ SDLK_0 ] )
			this.health = 0;
		
		if( this.health <= 0 )
		{
			this.death_timeout -= dt;
			if( this.death_timeout <= 0 )
			{
				Game.SetPaused( true );
				Game.OverlayScreens.push( PauseScreen );
			}
			this.portal_movement = -0.01;
			this.portal_h0 = this.position.z;
			this.portal_h1 = this.portal_h0 - 1;
			this.portal_orig_fogcolor = gSGR_Scene.fogColor;
			this.portal_orig_fogdensity = gSGR_Scene.fogDensity;
		}
		
		this.hit_timeout -= dt;
		
		this.updateCamera();
	}
	
	function Player.draw_ui()
	{
		if( current_cutscene || mission_complete )
			return;
		
		unit = gSGR_Window.height / 30;
		SS_DrawTextLine( "HEALTH: " $ string_repeat( "|", max( 0, this.health ) ), this.ui_font, unit*3, unit*3, color(1,1) );
		
	//	rr = gSGR_BulletWorld.rayTestClosest( this.position, this.position + this.direction * 5 );
	//	SS_DrawTextLine( "RAYCAST: "$rr, gSGR_DefaultFont, 100, 100, color(1,1) );
		
		if( time_since_saved < 3 )
		{
			SS_DrawTextLine( "Game saved!", this.ui_font, gSGR_Window.width / 3, unit, color(1,1-time_since_saved/3) );
		}
		
		if( act = this.getUseInfo() )
		{
			SS_DrawTextLine( "Press E to "$act, this.ui_font, gSGR_Window.width - unit*20, unit*3, color(1,1) );
		}
		if( this.can_switch_portals && !this.portal_movement )
		{
			SS_DrawTextLine( "Press T to switch worlds", this.ui_font, gSGR_Window.width - unit*20, unit*3, color(1,1) );
		}
		
		if( max_see_time > 0 )
		{
			text = if( max_see_time >= 1, "TURRET ALERT!", "TURRET ALERT" );
			SS_DrawTextLine( text, this.ui_font, gSGR_Window.width / 2 - unit * 4, unit * 3, color(1,0.1,0.05,1) );
		}
		
		if( this.hit_timeout > 0 )
			SS_DrawColorRect( 0, 0, gSGR_Window.width, gSGR_Window.height, 0.9, 0.1, 0.05, this.hit_timeout );
	}
	
	return Player;
}
