local LEVEL = {}
InitNewGame()

CONFIG.backcolor_r = 0.000000
CONFIG.backcolor_g = 0.000000
CONFIG.backcolor_b = 0.000000
LoadConfig()

StopBackMusic()
SetCamUseBounds( false )

local char1
local char2

local track
local track_pos
local final
local speed = 8
local max_x = 0
local update_done = false
local track_complete = false
local visible_track_parts = {}

local finish_line

local char1, char2, camera
local c1x, c1y, c2x, c2y
local sprite_shift = -64
local char1info, char2info = { x = 0, y = 0, z = 0 }, { x = 0, y = 0, z = 0}

local good_end = false
local track_thread

local tracktype

local bike_size = { 96, 16, 32 }

local ENABLE_DUST = false

local tsets =
{
	sewer = dofile( "scripts/hb/tileset_sewers.lua" ),
	mountain = dofile( "scripts/hb/tileset_mountain.lua" ),
	lab = dofile( "scripts/hb/tileset_lab.lua" ),
}

local context = 
{
}

local tracks =
{
	sewer =
	{
		tileset = "sewer";
		map = "=======3=2=1==1=2=3==[ ]==2=2=3=3=2=2=1=1==1==3==2==3==3==1==2[  ]===[ ]====@====222===22@===~~~~~~~~~~~~";
	},
	mountain1 =
	{
		tileset = "mountain";
		map = "======2[  ]===1=2=3==1==@=3==@=1=2==#===!==[  ]=@==[ ]=1=[ ]==33322211=3=[ ]=[  ]====222@=2===~~~~~~~~~";
	},
	mountain2 =
	{
		tileset = "mountain";
		map = "======2@==[  ]===3==1=2=2=3==!===3=2@=@2@==[  ]==1==3==[  ]3=[ ]1==[]2===3=3==1=2=2=2=1==3=#==~~~~~~~~~~~";
	},
	mountain3 =
	{
		tileset = "mountain";
		map = "=======[  ]===1==#==1==@==1==!==[  ]=11122233322[ ]=[  ]=[ ]=1==3==2==3==1==[ ][ ]====2#=2!=22==~~~~~~~~~";
	},
	lab =
	{
		tileset = "lab";
		map = "=======123232[ ]=1=[ ]=3=[  ]==1=1==333==23=12=[  ]==@==!==@==#=1===3==3=@=1=[ ][ ]==2=[ ][ ]=@==~~~~~~~~~~";
	},
}

local function y_to_z( y )
	return -0.7 + 0.6 * (y-track.tset.top)/(track.tset.bottom-track.tset.top)
end

local shadow_color = { 0,0,0,0.4 }

local function cast_shadow( shadow_object, x, y, z, floor, size_x, size_y )
	local dist = 1 - ((floor - z)/256)
	SetObjPos( shadow_object, x, y + floor )
	if dist > 0 then
		SetObjRectangle( shadow_object, size_x * dist, size_y * dist )	
	end
end

local function SelectTrack()
	if mapvar.actors[1].info.track then
		tracktype = Game.actors[1].info.track
	else
		tracktype = "sewer"
	end 
end

local function SetTrackType( tr )
	mapvar.actors[1].info.track = tr
end

local max_z = 0

local function ShowResults()
	Resume( NewMapThread( function()
		GlobalSetKeyDownProc( nil )
		GlobalSetKeyReleaseProc( nil )
		Wait( 3000 )
		StopBackMusic()
		PlaySnd( iff( good_end, "music/iie_win_jingle.it", "music/iie_lose_jingle.it" ), true )

		local text =
		{
			dictionary_string( "PLAYER 1" ),
			dictionary_string( "PLAYER 2" ),
			dictionary_string( "Not present" ),
			dictionary_string( "%i out of %i" ),
			dictionary_string( "TOTAL" ),
			dictionary_string( "PERFECT!" ),
			dictionary_string( "BONUS SCORE" ),
			dictionary_string( "PERFECT BONUS" ),
			dictionary_string( "FULL SCORE" )
		}

		local sx, sy, w
		local master_widget = CreateWidget( constants.wt_Widget, "", nil, -1, -1, CONFIG.scr_width+2, CONFIG.scr_height+2 )
		WidgetSetColorBox( master_widget, {0,0,0,0.25} )
		WidgetSetZ( master_widget, 0.5 )
		WidgetSetSpriteBlendingMode( master_widget, constants.bmSrcA_OneMinusSrcA )

		local y = 130
		wait = 750

		sx, sy = GetCaptionSize( "default", text[1] )
		w = CreateWidget( constants.wt_Label, "", master_widget, CONFIG.scr_width/4 - sx/2, y, 1, 1 )
		WidgetSetCaptionColor( w, {1,1,1,1}, false, {0,0,0,1} )
		WidgetSetCaption(  w, text[1] )
		sx, sy = GetCaptionSize( "default", text[2] )
		w = CreateWidget( constants.wt_Label, "", master_widget, 3*CONFIG.scr_width/4 - sx/2, y, 1, 1 )
		WidgetSetCaptionColor( w, {1,1,1,1}, false, {0,0,0,1} )
		WidgetSetCaption(  w, text[2] )
		y = y + 20

		Wait( wait )

		local pl1score = string.format( text[4], (char1info.score or 0), (track.vegetables or 0) )
		sx, sy = GetCaptionSize( "dialogue", pl1score )
		w = CreateWidget( constants.wt_Label, "", master_widget, CONFIG.scr_width/4 - sx/2, y, 1, 1 )
		WidgetSetCaptionFont( w, "dialogue" )
		WidgetSetCaptionColor( w, {1,1,1,1}, false, {0,0,0,1} )
		WidgetSetCaption(  w, pl1score )
		if Game.actors[2] then
			local pl2score = string.format( text[4], (char2info.score or 0), (track.vegetables or 0) )
			sx, sy = GetCaptionSize( "dialogue", pl2score )
			w = CreateWidget( constants.wt_Label, "", master_widget, 3*CONFIG.scr_width/4 - sx/2, y, 1, 1 )
			WidgetSetCaptionFont( w, "dialogue" )
			WidgetSetCaptionColor( w, {1,1,1,1}, false, {0,0,0,1} )
			WidgetSetCaption(  w, pl2score )
		else
			sx, sy = GetCaptionSize( "dialogue", text[3] )
			w = CreateWidget( constants.wt_Label, "", master_widget, 3*CONFIG.scr_width/4 - sx/2, y, 1, 1 )
			WidgetSetCaptionFont( w, "dialogue" )
			WidgetSetCaptionColor( w, {1,1,1,1}, false, {0,0,0,1} )
			WidgetSetCaption(  w, text[3] )
		end
		y = y + 30

		Wait( wait )

		local sc = (char1info.score or 0) + (char2info.score or 0)
		if Game.actors[2] then
			sx, sy = GetCaptionSize( "default", text[5] )
			w = CreateWidget( constants.wt_Label, "", master_widget, CONFIG.scr_width/2 - sx/2, y, 1, 1 )
			WidgetSetCaptionColor( w, {1,1,1,1}, false, {0,0,0,1} )
			WidgetSetCaption(  w, text[5] )
			y = y + 30
			
			Wait( wait )
			
			local totalscore = string.format( text[4], sc, (track.vegetables or 0) )
			sx, sy = GetCaptionSize( "dialogue", totalscore )
			w = CreateWidget( constants.wt_Label, "", master_widget, 3*CONFIG.scr_width/4 - sx/2, 50, 1, 1 )
			WidgetSetCaptionFont( w, "dialogue" )
			WidgetSetCaptionColor( w, {1,1,1,1}, false, {0,0,0,1} )
			WidgetSetCaption(  w, totalscore )
			y = y + 20

			Wait( wait )
		end

		local perfect = false
		if sc == (track.vegetables or 0) then
			perfect = true
			sx, sy = GetCaptionSize( "default", text[6] )
			w = CreateWidget( constants.wt_Label, "", master_widget, CONFIG.scr_width/2 - sx/2, y, 1, 1 )
			WidgetSetCaptionColor( w, {1,1,1,1}, false, {0,0,0,1} )
			WidgetSetCaption(  w, text[6] )
			y = y + 30

			Wait( wait )
		end

		sx, sy = GetCaptionSize( "default", text[7] )
		w = CreateWidget( constants.wt_Label, "", master_widget, CONFIG.scr_width/4 - sx/2, y, 1, 1 )
		WidgetSetCaptionColor( w, {1,1,1,1}, false, {0,0,0,1} )
		WidgetSetCaption(  w, text[7] )
		sx, sy = GetCaptionSize( "default", text[7] )
		w = CreateWidget( constants.wt_Label, "", master_widget, 3*CONFIG.scr_width/4 - sx/2, y, 1, 1 )
		WidgetSetCaptionColor( w, {1,1,1,1}, false, {0,0,0,1} )
		WidgetSetCaption(  w, text[7] )
		y = y + 20

		Wait( wait )

		if ( char1info.score or 0 ) > 0 or perfect then
			Game.GainScore( GetPlayerCharacter(1), (char1info.score or 0) * 400 + iff( perfect, 1500, 0), x, y )
		end
		if (( char2info.score or 0 ) > 0 or perfect) and Game.actors[2] then
			Game.GainScore( GetPlayerCharacter(2), (char2info.score or 0) * 400 + iff( perfect, 1500, 0), x, y )
		end
		local score = tostring( (char1info.score or 0) * 400 )
		sx, sy = GetCaptionSize( "default", score )
		w = CreateWidget( constants.wt_Label, "", master_widget, CONFIG.scr_width/4 - sx/2, y, 1, 1 )
		WidgetSetCaptionColor( w, {1,1,1,1}, false, {0,0,0,1} )
		WidgetSetCaption(  w, score )
		score = tostring( (char2info.score or 0) * 400 )
		sx, sy = GetCaptionSize( "default", score )
		w = CreateWidget( constants.wt_Label, "", master_widget, 3*CONFIG.scr_width/4 - sx/2, y, 1, 1 )
		WidgetSetCaptionColor( w, {1,1,1,1}, false, {0,0,0,1} )
		WidgetSetCaption(  w, score )
		y = y + 40

		Wait( wait )

		if perfect then
			sx, sy = GetCaptionSize( "default", text[8] )
			w = CreateWidget( constants.wt_Label, "", master_widget, CONFIG.scr_width/2 - sx/2, y, 1, 1 )
			WidgetSetCaptionColor( w, {1,1,1,1}, false, {0,0,0,1} )
			WidgetSetCaption(  w, text[8] )
			y = y + 30

			Wait( wait )

			sx, sy = GetCaptionSize( "default", "1500" )
			w = CreateWidget( constants.wt_Label, "", master_widget, CONFIG.scr_width/2 - sx/2, y, 1, 1 )
			WidgetSetCaptionColor( w, {1,1,1,1}, false, {0,0,0,1} )
			WidgetSetCaption(  w, "1500" )
			y = y + 60

			Wait( wait )
		end

		if Game.custom_map then
			sx, sy = GetCaptionSize( "default", text[9] )
			w = CreateWidget( constants.wt_Label, "", master_widget, CONFIG.scr_width/4 - sx/2, y, 1, 1 )
			WidgetSetCaptionColor( w, {1,1,1,1}, false, {0,0,0,1} )
			WidgetSetCaption(  w, text[9] )
			sx, sy = GetCaptionSize( "default", text[9] )
			w = CreateWidget( constants.wt_Label, "", master_widget, 3*CONFIG.scr_width/4 - sx/2, y, 1, 1 )
			WidgetSetCaptionColor( w, {1,1,1,1}, false, {0,0,0,1} )
			WidgetSetCaption(  w, text[9] )
			y = y + 20

			Wait( wait )

			local score = tostring( (mapvar.actors[1].info.score or 0) )
			sx, sy = GetCaptionSize( "default", score )
			w = CreateWidget( constants.wt_Label, "", master_widget, CONFIG.scr_width/4 - sx/2, y, 1, 1 )
			WidgetSetCaptionColor( w, {1,1,1,1}, false, {0,0,0,1} )
			WidgetSetCaption(  w, score )
			score = tostring( ((mapvar.actors[2] or {info ={score=0}}).info.score or 0) )
			sx, sy = GetCaptionSize( "default", score )
			w = CreateWidget( constants.wt_Label, "", master_widget, 3*CONFIG.scr_width/4 - sx/2, y, 1, 1 )
			WidgetSetCaptionColor( w, {1,1,1,1}, false, {0,0,0,1} )
			WidgetSetCaption(  w, score )
			y = y + 40

			Wait( wait )
		end

		Wait( 3000 )
		
		RestoreKeyProcessors()
		DestroyWidget( master_widget )
		local obj1 = CreateItem( "generic-trigger", -1000, -1000 )
		ObjectPushInt( obj1, 28 )
		SetObjAnim( obj1, "script", false )
	end ))
end

local function CheckDeath()
	if char1info.dead and char2info.dead then
		camera:own_vel{ x = 0, y = 0 }
		track.ended = true
		ShowResults()
	end
end

function context.TheVegetable( veg, z )
	track.vegetables = (track.vegetables or 0) + 1
	local z = z or -32
	local pos = veg:aabb().p
	local the_shadow = GetObjectUserdata( CreateSprite( "circle", pos.x, pos.y ) )
	local sp_z = y_to_z( pos.y )
	SetObjPos( veg, pos.x, pos.y + z )
	veg:sprite_z( sp_z )
	SetObjSpriteRenderMethod( the_shadow, constants.rsmStretch )
	SetObjSpriteBlendingMode( the_shadow, constants.bmSrcA_OneMinusSrcA )
	SetObjSpriteColor( the_shadow, shadow_color )
	SetObjRectangle( the_shadow, 16, 8 )
	cast_shadow( the_shadow, pos.x, pos.y, z, 0, 24, 12 )
--	SetObjPos( the_shadow, pos.x, pos.y )
	the_shadow:sprite_z( sp_z )
	SetObjProcessor( veg, function( this )
		if ObjectOnScreen( this ) then
			local pos = this:aabb().p
			local dist1, dist2, dist3 = math.abs(pos.y - z - char1info.y), math.abs(pos.x - char1info.x), math.abs( z + 32 - char1info.z )
			if dist1 < 16 and dist2 < 32 and dist3 <= 48 then
				char1info.score = (char1info.score or 0) + 1
				PlaySnd( "health-pickup.ogg", true )
				SetObjDead( this )
				SetObjDead( the_shadow )
				Game.FlashObject( char1info.fake_player )
				return
			end
			dist1, dist2, dist3 = math.abs(pos.y - z - char2info.y), math.abs(pos.x - char2info.x), math.abs( z + 32 - char1info.z )
			if dist1 < 16 and dist2 < 32  and dist3 <= 48 then
				char2info.score = (char2info.score or 0) + 1
				PlaySnd( "health-pickup.ogg", true )
				SetObjDead( this )
				SetObjDead( the_shadow )
				Game.FlashObject( char2info.fake_player )
				return
			end
		end
	end )
end

local function BuildTrack()
	track.tiles = {}
	track.tset = tsets[track.tileset]
	context.x = 0
	track.tset.InitTrack(context)
	for i = 1, #track.map do
		context.x = context.x + track.tset.BuildTrackPart( track, string.sub( track.map, i, i ), context )
	end
end

local input = {
	{ false, false, false, false, false },
	{ false, false, false, false, false },
}

function context.GetTileType( x )
	for i=1,#track.tiles do
		if x < track.tiles[i][1] then
			return track.tiles[i-1][2], track.tiles[i-1][1]
		end
	end
	return "=", 0
end

local race_started = false

local function ShadowCaster( t )
	return function(this)
		local aabb = this:aabb()
		t.x = aabb.p.x
		t.y = aabb.p.y
		local vel = { x = speed, y = 0 }
		local floor = 0
		local sp_z = y_to_z( t.y )
		if race_started and not mapvar.tmp.race_finished then
			if t.input[1] then
				vel.y = -1
			elseif t.input[2] then
				vel.y = 1
			end
			if t.input[3] then
				vel.x = speed - 2
			elseif t.input[4] then
				vel.x = speed + 2
			end
			if t.input[5] then
				if (not t.jump_complete) and t.z_vel <= 0 and t.jump_reserve > 0 and GetCurTime() - t.last_jump_press > 10 then
					t.last_jump_press = GetCurTime()
					t.z_vel = -3
					t.jump_reserve = t.jump_reserve - 1
				end
			else
				if t.z_vel < 0 then
					t.jump_complete = true
				end
			end
			floor = track.tset.GetFloorZ( t.x, t.y, context )
			--local tile = GetTileType( t.x )
			if t.z_vel < 0 then
				t.z = t.z + math.min( t.z_vel, floor - t.z )
			else
				t.z = t.z + math.max(0, math.min( t.z_vel, floor - t.z ))
			end
			if t.jump_reserve == 0 or t.z_vel > 0 or not input[5] then
				t.z_vel = iff( t.z >= floor, 0, t.z_vel + 0.1 )
				t.jump_complete = false
			end
			if t.z >= floor and t.jump_reserve < 10 then
				t.jump_reserve = 10
				local dust = CreateSpriteCenter( "dust-land", t.x-30, t.y-65 )
				dust:sprite_z(sp_z+0.01)
			end
			if t.z > floor and not boom then
				CreateEnemy( "big_explosion", t.x, t.y )
				t.z = 0
				t.dead = true
				SetObjDead( t.fake_player )
				SetObjDead( t.shadow_object )
				if ENABLE_DUST then
					SetObjDead( t.dust )
				end
				local dead_player = GetObjectUserdata( CreateEnemy( t.prop, t.x, t.y, "die" ) )
				SetObjPos( dead_player, t.x, t.y )
				t.x = 0
				t.y = 0
				dead_player:vel{ x = 0, y = -10 }
				dead_player:own_vel{ x = speed, y = 0 }
				CheckDeath()
				SetObjDead( this )
				return 
			end
			this:own_vel(vel)
			if not track.ended then
				local cam = camera:aabb()
				if aabb.p.x - aabb.W < cam.p.x - CONFIG.scr_width/2 then
					aabb.p.x = cam.p.x - CONFIG.scr_width/2 + aabb.W
					SetObjPos( this, aabb.p.x, aabb.p.y )
				end
				if aabb.p.x + aabb.W > cam.p.x + CONFIG.scr_width/2 then
					aabb.p.x = cam.p.x + CONFIG.scr_width/2 - aabb.W
					SetObjPos( this, aabb.p.x, aabb.p.y )
				end
				if aabb.p.y < track.tset.top+bike_size[2]/2 then
					aabb.p.y = track.tset.top+bike_size[2]/2
					SetObjPos( this, aabb.p.x, aabb.p.y )
				end
				if aabb.p.y > track.tset.bottom-bike_size[2]/2 then
					aabb.p.y = track.tset.bottom-bike_size[2]/2
					SetObjPos( this, aabb.p.x, aabb.p.y )
				end
			end
		end
		t.fake_player:sprite_z( sp_z )
		t.shadow_object:sprite_z( sp_z )
		if ENABLE_DUST and race_started then
			if t.z == floor then
				t.dust:sprite_z( sp_z+0.001 )
				SetObjPos( t.dust, aabb.p.x, aabb.p.y - 60 )
				SetObjInvisible( t.dust, false )
			else
				SetObjInvisible( t.dust, true )
			end	
		end
		SetObjPos( t.fake_player, aabb.p.x, aabb.p.y + t.z + sprite_shift )
		cast_shadow( t.shadow_object, t.x, t.y, t.z, track.tset.GetFloorZ( t.x, t.y, context ), bike_size[1], bike_size[2] )
--		SetObjPos( t.shadow_object, t.x, aabb.p.y + GetFloorZ( t.x, t.y ) )
	end
end

LEVEL.NO_RESTART = true

local function InputPress(key)
	if IsConfKey(key, config_keys.gui_nav_menu) then
		Menu.mn = not Menu.mn
		if Menu.mn then
			Loader.showMenu()
		else
			Loader.hideMenu()
		end
		return
	end
	if Game.actors[2] then
		if key == CONFIG.key_conf[1].up then
			input[1][1] = true
		elseif key == CONFIG.key_conf[1].down then
			input[1][2] = true
		elseif key == CONFIG.key_conf[1].left then
			input[1][3] = true		
		elseif key == CONFIG.key_conf[1].right then
			input[1][4] = true
		elseif key == CONFIG.key_conf[1].jump then
			input[1][5] = true
		elseif key == CONFIG.key_conf[2].up then
			input[2][1] = true
		elseif key == CONFIG.key_conf[2].down then
			input[2][2] = true
		elseif key == CONFIG.key_conf[2].left then
			input[2][3] = true		
		elseif key == CONFIG.key_conf[2].right then
			input[2][4] = true
		elseif key == CONFIG.key_conf[2].jump then
			input[2][5] = true
		end
	else
		if IsConfKey(key, config_keys.up) then
			input[1][1] = true
		elseif IsConfKey(key, config_keys.down) then
			input[1][2] = true
		elseif IsConfKey(key, config_keys.left) then
			input[1][3] = true		
		elseif IsConfKey(key, config_keys.right) then
			input[1][4] = true
		elseif IsConfKey(key, config_keys.jump) then
			input[1][5] = true
		end
	end
end

local function InputRelease(key)
	if Game.actors[2] then
		if key == CONFIG.key_conf[1].up then
			input[1][1] = false
		elseif key == CONFIG.key_conf[1].down then
			input[1][2] = false
		elseif key == CONFIG.key_conf[1].left then
			input[1][3] = false		
		elseif key == CONFIG.key_conf[1].right then
			input[1][4] = false
		elseif key == CONFIG.key_conf[1].jump then
			input[1][5] = false
		elseif key == CONFIG.key_conf[2].up then
			input[2][1] = false
		elseif key == CONFIG.key_conf[2].down then
			input[2][2] = false
		elseif key == CONFIG.key_conf[2].left then
			input[2][3] = false		
		elseif key == CONFIG.key_conf[2].right then
			input[2][4] = false
		elseif key == CONFIG.key_conf[2].jump then
			input[2][5] = false
		end
	else
		if IsConfKey(key, config_keys.up) then
			input[1][1] = false
		elseif IsConfKey(key, config_keys.down) then
			input[1][2] = false
		elseif IsConfKey(key, config_keys.left) then
			input[1][3] = false		
		elseif IsConfKey(key, config_keys.right) then
			input[1][4] = false
		elseif IsConfKey(key, config_keys.jump) then
			input[1][5] = false
		end
	end
end

local function PreparePlayer( num, tab, char )
	local obj = GetObjectUserdata( CreateEnemy( "camera-focus", 64, 350 + iff(num == 1, 32, -32) ) ) 
	obj:own_vel{ x = 0, y = 0 }
	tab.shadow_object = GetObjectUserdata( CreateSprite( "circle", 0, 0 ) )
	SetObjSpriteRenderMethod( tab.shadow_object, constants.rsmStretch )
	SetObjSpriteBlendingMode( tab.shadow_object, constants.bmSrcA_OneMinusSrcA )
	SetObjSpriteColor( tab.shadow_object, shadow_color )
	SetObjRectangle( tab.shadow_object, 96, 16 )
	tab.input = input[num]
	tab.z = 0
	tab.z_vel = 0
	tab.jump_reserve = 10
	tab.last_jump_press = 0
	tab.jump_complete = false
	tab.fake_player = GetObjectUserdata( CreateEnemy( iff( char == "pear15soh", "hoverbike", "hoverbike2" ), 0, 0 ) )
	tab.fake_player:gravity{ x = 0, y = 0 }
	tab.prop = iff( char == "pear15soh", "characters/pear15soh", "characters/pear15unyl" )
	SetObjRectangle( obj, 96, 16 )
	SetObjProcessor( obj, ShadowCaster( tab ) )
	if ENABLE_DUST then
		tab.dust = GetObjectUserdata( CreateSprite( "effects/dust-hoverbike", 0, 0 ) )
	end
	if num == 1 then
		char1 = obj
	else
		char2 = obj
	end
	SetObjInvisible( tab.fake_player, true )
	SetObjInvisible( tab.shadow_object, true )
	if ENABLE_DUST then
		SetObjInvisible( tab.dust, true )
	end
end

local function CameraProcessor( this )
	local pos = this:aabb().p
	if pos.x >= (track.finish_line or 0) then
		mapvar.tmp.race_finished = true
		if char1 then
			local vel = char1:own_vel()
			vel.y = 0
			char1:own_vel( vel )
		end
		if char2 then
			local vel = char2:own_vel()
			vel.y = 0
			char2:own_vel( vel )
		end
		good_end = true
		this:own_vel{ x = 0, y = 0 }
		track.ended = true
		ShowResults()
		SetObjProcessor( this, function() end )
	end
end

local function PrepareTrack()
	CamMoveToPos( CONFIG.scr_width/2, CONFIG.scr_height/2 )
	local x = 0
	track_pos = 1
	BuildTrack()
	RemoveKeyProcessors()
	EnablePlayerControl( false )
	GlobalSetKeyDownProc( InputPress )
	GlobalSetKeyReleaseProc( InputRelease )
	camera = GetObjectUserdata( CreateEnemy( "camera-focus", CONFIG.scr_width/2, CONFIG.scr_height/2 ) )
	SetCamAttachedObj( camera:id() )
	SetCamUseBounds( false )
	SetCamObjOffset(0, 0)
	SetObjInvisible( camera, true )
	camera:own_vel{ x = 0, y = 0 }
	SetObjProcessor( camera, CameraProcessor )
	if GetPlayerCharacter(1) then
		PreparePlayer( 1, char1info, Game.actors[1].info.character )
	end
	if GetPlayerCharacter(2) then
		PreparePlayer( 2, char2info, Game.actors[2].info.character )
	else
		char2info.dead = true
	end
end

local function UpdateCharacters()
end

local function TrackThread()
	while not track_complete do
		UpdateCharacters()
		Wait(1)
	end
end

local function ShowIntro()
	Resume( NewMapThread( function()
		Game.fade_out()
		Wait(1)
		local sx, sy = GetCaptionSize( "default", "Бонусный Уровень" )
		mapvar.tmp.text1 = CreateWidget( constants.wt_Label, "TEXT", mapvar.tmp.fade_widget, (CONFIG.scr_width - sx)/2, CONFIG.scr_height/2 - 1.5 * sy, 1, 1 )
		WidgetSetCaptionColor( mapvar.tmp.text1, {1,1,1,1}, false )
		WidgetSetCaption( mapvar.tmp.text1, "Бонусный Уровень" )
		WidgetSetZ( mapvar.tmp.text1, 1.05 )
		sx, sy = GetCaptionSize( "default", "Собирайте овощи!" )
		mapvar.tmp.text2 = CreateWidget( constants.wt_Label, "TEXT", mapvar.tmp.fade_widget, (CONFIG.scr_width - sx)/2, CONFIG.scr_height/2 + 1.5 * sy, 1, 1 )
		WidgetSetCaptionColor( mapvar.tmp.text2, {1,1,1,1}, false )
		WidgetSetCaption( mapvar.tmp.text2, "Собирайте овощи!" )
		WidgetSetZ( mapvar.tmp.text2, 1.05 )
		local hb1, hb2
		local pl1, pl2
		local hs1, hs2
		local ch1, ch2 = "characters/pear15soh", "characters/pear15unyl"
		if Game.actors[1].info.character == "pear15unyl" then
			ch1, ch2 = ch2, ch1
			hb1, hb2 = hb2, hb1
			hs1, hs2 = hs2, hs1
		end
		if GetPlayerCharacter( 1 ) then --96 16
			local aabb
			hb1 = GetObjectUserdata( CreateEnemy( "hoverbike-empty", 200, 350 - 64 ) )
			aabb = hb1:aabb()
			SetObjPos( hb1, 200, 380-aabb.H, y_to_z(380) )
			hs1 = GetObjectUserdata( CreateSprite( "circle", 200, 350 - 64 ) )
			SetObjSpriteRenderMethod( hs1, constants.rsmStretch )
			SetObjSpriteBlendingMode( hs1, constants.bmSrcA_OneMinusSrcA )
			SetObjSpriteColor( hs1, shadow_color )
			cast_shadow( hs1, 200-10, 380, 32, 0, 96, 16 )
			SetObjSpriteMirrored( hb1, false )
			pl1 = GetObjectUserdata( CreateEnemy( ch1, 0, 0 ) )
			aabb = pl1:aabb()
			pl1:gravity{ x=0, y=0 }
			SetObjPos( pl1, -128, 370-aabb.H, y_to_z(370) )
			SetObjAnim( pl1, "walk", false )
		end
		if GetPlayerCharacter( 2 ) then
			local aabb
			hb2 = GetObjectUserdata( CreateEnemy( "hoverbike-empty", 200, 350 + 64 ) )
			aabb = hb2:aabb()
			hb2:sprite_z( y_to_z(346) )
			SetObjPos( hb2, 250, 400-aabb.H, y_to_z(400) )
			hs2 = GetObjectUserdata( CreateSprite( "circle", 200, 350 - 64 ) )
			SetObjSpriteRenderMethod( hs2, constants.rsmStretch )
			SetObjSpriteBlendingMode( hs2, constants.bmSrcA_OneMinusSrcA )
			SetObjSpriteColor( hs2, shadow_color )
			cast_shadow( hs2, 250, 400, 32, 0, 96, 16 )
			SetObjSpriteMirrored( hb2, false )
			pl2 = GetObjectUserdata( CreateEnemy( ch2, 0, 0 ) )
			aabb = pl2:aabb()
			pl2:gravity{ x=0, y=0 }
			SetObjPos( pl2, -148, 400-aabb.H, y_to_z(400) )
			SetObjAnim( pl2, "walk", false )
		end
		Wait(200)
		mapvar.tmp.fade_complete = false
		Game.fade_in( false, "FADE_COMPLETE" )
		WaitForEvent( "FADE_COMPLETE" )
		if pl1 then
			pl1:vel{ x = 3, y = 0 }
			SetObjProcessor( pl1, function( obj )
				local pos = obj:aabb().p
				if pos.x >= 200 then
					local pos = hb1:aabb().p
					SetObjPos( char1, pos.x-10, pos.y+64 )
					SetObjPos( char1info.fake_player, pos.x-10, pos.y+64 + sprite_shift )
					cast_shadow( char1info.shadow_object, pos.x-10, pos.y+64, 0, 0, 96, 16 )
					SetObjInvisible( char1info.fake_player, false )
					SetObjInvisible( char1info.shadow_object, false )
					SetObjDead( hb1 )
					SetObjDead( hs1 )
					SetObjDead( obj )
					FireEvent( "PLAYER_SEATED" )
					return
				end
			end )
		end
		if pl2 then
			pl2:vel{ x = 3, y = 0 }
			SetObjProcessor( pl2, function( obj )
				local pos = obj:aabb().p
				if pos.x >= 250 then
					local pos = hb2:aabb().p
					SetObjPos( char2, pos.x-10, pos.y+64 )
					SetObjPos( char2info.fake_player, pos.x-10, pos.y+64 + sprite_shift )
					cast_shadow( char2info.shadow_object, pos.x-10, pos.y+64, 0, 0, 96, 16 )
					SetObjInvisible( char2info.fake_player, false )
					SetObjInvisible( char2info.shadow_object, false )
					SetObjDead( hb2 )
					SetObjDead( hs2 )
					SetObjDead( obj )
					FireEvent( "PLAYER_SEATED" )
					return
				end
			end )
		end
		if pl1 then
			WaitForEvent( "PLAYER_SEATED" )
		end
		if pl2 then
			WaitForEvent( "PLAYER_SEATED" )
		end
		PlayBackMusic( "music/nsmpr_nwplc.it" )
		local target_speed = speed
		speed = 0
		race_started = true
		Resume( NewMapThread( function()
			while speed < target_speed do
				if target_speed - speed < 0.1 then
					speed = target_speed
				else
					speed = speed + ( target_speed - speed ) * 0.1
				end
				camera:own_vel{ x = speed, y = 0 }
				Wait( 100 )
			end
		end ))
		track_thread = NewMapThread( TrackThread )
		Resume( track_thread )
	end ))
end

function LEVEL.InitScript()
--$(INIT_SCRIPT)+
	Game.info.can_join = false
	SelectTrack()
	track = tracks[tracktype]
	PrepareTrack()

	ShowIntro()

	--[[track_thread = NewMapThread( TrackThread )
	Resume( track_thread )]]
--$(INIT_SCRIPT)-
	Loader.level.SpecialCreation()
end

function LEVEL.MapScript(player)
--$(MAP_SCRIPT)+
--$(MAP_SCRIPT)-
end

function LEVEL.WeaponBonus()
--$(WEAPON_BONUS)+
--$(WEAPON_BONUS)-
end

function LEVEL.placeSecrets()
--$(PLACE_SECRETS)+
--$(PLACE_SECRETS)-
end

function LEVEL.removeSecrets()
--$(REMOVE_SECRETS)+
--$(REMOVE_SECRETS)-
end

function LEVEL.SpecialCreation()
--$(SPECIAL_CREATION)+
	local object
--$(SPECIAL_CREATION)-
end

function map_trigger(id, trigger)
--$(MAP_TRIGGER)+
	SetObjDead( id )
	if Game.custom_map then
		if tracktype == "sewer" then
			SetTrackType( "mountain1" )
		elseif tracktype == "mountain1" then
			SetTrackType( "mountain2" )
		elseif tracktype == "mountain2" then
			SetTrackType( "mountain3" )
		elseif tracktype == "mountain3" then
			SetTrackType( "lab" )
		else
			good_end = false
		end
		if good_end then
			Loader.ChangeLevel( "pear15hoverbike" )
		else
			StopThread( track_thread )
			Menu.showMainMenu()
		end
	else
		if tracktype == "sewer" then
			Loader.ChangeLevel( "pear15house_opening" )
		elseif tracktype == "mountain1" then
			Loader.ChangeLevel( "pear15fm" )
		elseif tracktype == "mountain2" then
			Loader.ChangeLevel( "pear15mountains" )
		elseif tracktype == "mountain3" then
			Loader.ChangeLevel( "pear15tower" )
		elseif tracktype == "lab" then
			Loader.ChangeLevel( "pear15lab" )
		else
			Loader.exitGame()
		end
	end
--$(MAP_TRIGGER)-
end

LEVEL.spawnpoints = {{-1000, -1000}}
--LEVEL.spawnpoints = {{243, 150}}

function LEVEL.SetLoader(l)
	LEVEL.missionStarter=l
end

LEVEL.name = 'Cutscene'

return LEVEL
