dofile "scripts/demohighscores.lua"

dofile "scripts/options.lua"
--game

if gamestats then gamestats:delete() gamestats = nil end
local w,h = window.refsize()
local ww,wh = 200,40
gamestats = GroupFrame:new((w-200)*.5,h-wh+10,ww,wh)
--Container:getRootContainer():add(gamestats)
gamestats.text = gamestats:add(Label:new(5,5,ww-10,20))

local function addglow(actor,size,type,x,y,z,facing,noisesize,noiseforce)
	local glow = l3dprimitive.newquadcentered("qc",l3dset.default():layer(8),size,size)
	glow:rfBlend(true)
	glow:rsBlendmode(blendmode.add())
	glow:rfNodepthmask(true)
	glow:matsurface(tex["galaxy.star"..(type or "02")])
	glow:linkinterface(actor)
	local ns = noisesize or .5
	glow.noise = l3dprimitive.newquadcentered("qc",l3dset.default():layer(13),size*ns,size*ns)
	glow.noise:parent(glow)
	glow.noise:rfNodepthmask(true)
	glow.noise:matsurface(tex["galaxy.star01"])
	glow.noise:rfBlend(true)
	glow.noise:rsBlendmode(blendmode.add())
	local n = noiseforce or .5
	glow.noise:color(n,n,n,1)
	if x then
		glow:uselocal(true)
		glow:localpos(x,y,z)
	end
	if facing then
		glow:uselocal(true)
		glow:uselookat(true)
		glow:rfNocull(true)
		glow:localrotdeg(90,90,0)
	end
	return glow
end

class "Player"

function Player : shoot (which)
	for i,w in ipairs(self.weapon[which]) do
		if w.reload<self.age and self.energy>=w.energyuse and self.extracounter >= w.extrause then 
			for i,p in ipairs(w.pos) do
				local x,y = self.body:local2world(unpack(p))
				self.game:fireprojectile(w.type,x,y,unpack(w.speed))
			end
			self.energy = self.energy - w.energyuse
			self.extracounter = self.extracounter - w.extrause
			w.reload = self.age + w.pause
		end
	end
end

function Player : think ()
	if self.destroyed then return 1 end
	self.age = self.age + 1
	self.energy = math.min(100,self.energy + 5 + math.min(math.max(0,self.energy*.2),10))
	
	local px,py,pz = self.body:pos()
	local x,y,z = self.game.cam:pos()
	px = math.min(math.max(-levelw,px),levelw)
	py = math.max(math.min(y+400,py),y-220) + 1
	y = y + 1
	
	
	local dis = .75
	local sx,sy = self.game.camshakex or 0,self.game.camshakey or 0
	self.game.sun:pos(x-9000,y-2000,20000)
	self.game.cam:pos(self.game.head*levelw*dis+sx,y+sy,(2000-1000*self.game.pitch)*dis)
	self.game.cam:lookat(sx,y+100*dis+sy,0,0,0,1)

	
	for i,fn in pairs(self.keys) do fn() end
	self.x,self.y = self.x*.5,self.y*.5
	--self.player.y = self.player.y + .1
	local fx,fy =  self.x,  self.y
	local s = .1
	self.body:addforce(fx*s,fy*s,-pz*.1)
	
	self.body:pos(px,py,pz)
	local vx = self.body:velocity()
	--print(self.body:rotdeg())
	local rx,ry,rz = self.body:rotdeg()
	
	self.body:rotdeg(rx*.5,vx*6,rz*.5)
	self.body:angvel(0,0,0)
	
	
	for i,t in ipairs(self.trails) do
		local x,y,z = self.body:local2world(t.x,t.y,t.z)
		t:localtime(self.game.simtime)
		t:spawn(x,y,z,0,0,1,0,-500,0)
	end
	
	self.shield = math.min(100,self.shield + .25)
end

function Player : explode ()
	self.geom : delete()
	self.destroyed = true
	self.game:spawnexplosion(100,self.body:pos())
	self.body:delete()
	self.player:delete()
	
	
end

function Player : takedamage (dmg)
	self.shield = self.shield - dmg
	if self.shield < 0 then
		self.hp = self.hp + self.shield
		self.shield = 0
		if self.hp < 0 then
			self : explode()
		end
	end
end

function Player : Player (game)
	self.game = game
	self.player = actornode.new("player")
	self.mdl = l3dmodel.new("player",l3dset.default():layer(4),mdl.player)
	self.mdl:rfLitSun(true)
	self.mdl:rfLitFX(true)
	self.mdl:linkinterface(self.player)
	self.mdl:uselocal(true)
	self.mdl:localrotdeg(0,0,180)
	self.age = 0
	
	self.weapon = {}
	self.weapon.primary = {
		{type = "laser", reload = 0, pos = {{-10,5,0},{10,5,0}}, energyuse = 30, extrause = 0, pause = 2, speed = {0,15,0}},
		{type = "laser", reload = 0, pos = {{-14,3,0},{14,3,0}}, energyuse = 30, extrause = 0, pause = 2, speed = {0,15,0}},
	}
	
	self.weapon.secondary = {
		{type = "bomb", reload = 0, pos = {{0,0,0}}, energyuse = 0, extrause = 1, pause = 50,speed = {0,5,0}},
	}
	
	self.energy = 0
	
	self.shield = 100
	self.hp = 100
		
	local function kdf (which,fn,jaxis,jbutton) 
		return function () 
			local j = {input.joystick(0)}
			local axis,btn
			if j[1] and jaxis then 
				axis = j[jaxis+5]
			end
			if j[1] and jbutton then
				btn = j[j[3]+4+jbutton]
			end
			axis = (axis and math.abs(axis)>.1) and axis
			if axis then print(axis) end
			return (Keyboard.isKeyDown(which) or axis or btn) and fn(10 * (axis or 1)) or fn(0) 
		end
	end
	self.game.pausetimeout = 0
	self.keys = {
		up = kdf("UP", function (v) self.y = self.y + v end,1),
		down = kdf("DOWN",function(v) self.y = self.y - v end),
		left = kdf("LEFT",function(v) self.x = self.x - v end),
		right = kdf("RIGHT", function(v) self.x = self.x + v end,0),
		shootpri = kdf("LCTRL", function(v) if v~=0 then self:shoot("primary") end end,nil,1),
		shootsec = kdf("SHIFT", function(v) if v~=0 then self:shoot("secondary") end end,nil,2),
		pause = kdf("ENTER", function(v) 
			if v~=0 and self.game.pausetimeout>30 then  
				self.game.pausetimeout = 0 self.game : showLastMessage () end self.game.pausetimeout = self.game.pausetimeout + 1 end),
	}
	
	self.body = dbody.new()
	function self.body.takedamage(body,dmg)
		self:takedamage(dmg)
	end
	local pw,ph,pd = 30,35,10
	self.geom = dgeomsphere.new(20,game.globalspace)
	self.extracounter = 0
	function self.geom.onextra(g,extra)
		self.extracounter = self.extracounter + 1
	end
	self.geom:category(0,0,false)
	self.geom:category(1,0,false)
	self.geom:body(self.body)
	self.geom.l3d = l3dprimitive.newbox("db",pw,ph,pd)
	--self.player.geom.l3d:linkinterface(self.player)
	self.geom.l3d:rfWireframe(true)
	
	self.player:link(self.body)
	self.x,self.y = 0,0
	self.body:masssphere(1,20,true)
	self.body:veldrag(.9,.9,.5)
	self.trails = 
	{
		l3dtrail.new("p1",l3dset.default():layer(6),6),
		l3dtrail.new("p2",l3dset.default():layer(6),6),
		l3dtrail.new("p2",l3dset.default():layer(6),6),
	}
	local pos = {-10,-20,0, 10,-20,0, 0,-20,0}
	for i,t in ipairs(self.trails) do 
		t:linkinterface(self.player) t:rfNocull(true) 
		t.x,t.y,t.z = pos[i*3-2],pos[i*3-1],pos[i*3]
		t:uselocaltime(true)
		--t:setlayer(l3dset.default():layer(4))
		t:size(-1,3)
		t:rfBlend(true)
		t:rsBlendmode(blendmode.add())
		t:rfNodepthmask(true)
		t:usevelocity(true)
		t:color(0,1,1,1,1)
		t:color(1,0,0,1,1)
		t:color(2,0,0,0,1)
		t:matsurface(tex.trail1)
		
		t.glow = addglow(self.player,50,"02",t.x,t.y,t.z)
	end
	
	game : addthink(self)
end

class "Projectile"

function Projectile : Projectile (game,typeinit,x,y,vx,vy,collidebit)
	self.game = game
	self.actor = actornode.new("projectile")
	self.body = dbody.new()
	self.actor:link(self.body)
	
	
	self.age = 0
	self.maxage = 40
	self.dmg = 10
	
	typeinit(self,collidebit)
	
	self.body:pos(x,y,0)
	self.body:velocity(vx,vy,0)
	
	
	self.game : addthink(self)
end

function Projectile : onhit()
end

function Projectile : tick()
end

function Projectile:think ()
	if not self.hashit and self.body:jointcount()>0 then
		local cnt = 0 
		for i=0,self.body:jointcount()-1 do
			local j = self.body:joint(i)
			if j:geom1() and j:geom2() then cnt = cnt + 1 end
			if j:body1() and j:body1().takedamage then
				j:body1():takedamage(self.dmg)
			end
			if j:body2() and j:body2().takedamage then
				j:body2():takedamage(self.dmg)
			end
		end
		if cnt > 0 and not self.multihit then
			self.hashit = true
			self:onhit()
		end
	end
	
	self:tick()
	
	self.age = self.age + 1
	
	if self.age > self.maxage then
		self.actor:delete()
		self.geom:delete()
		self.body:delete()
		return true
	end
end



class "Game"

function Game : fireprojectile (type,x,y, vx,vy,collidebit)
	assert(type and self.projectiletypes[type])
	local p = new(Projectile, self, self.projectiletypes[type], x,y,vx,vy,collidebit)
end

function Game : thinkall()
	for i=#self.thinklist,1,-1 do 
		if self.thinklist[i]:think() then
			table.remove(self.thinklist,i)
		end
	end
end

function Game : addthink(o)
	self.thinklist[#self.thinklist+1] = o
end

Game.pexplode = particlesys.load("explo.prt")	
Game.pfire = particlesys.load("damage.prt")	
Game.pbomb = particlesys.load("bomb.prt")	

function Game : spawnfire (x,y,z)
end

function Game : spawnextra (x,y,z)
	local s = 15
	local extra = dbody.new()
	extra.geom = dgeombox.new(s,s,s,self.globalspace)
	--extra.geom:category(0,0,false)
	--extra.geom:category(1,0,false)
	--extra.geom:category(0,1,false)
	--extra.geom:category(1,1,false)
	extra.geom:body(extra)
	extra:pos(x,y,z)
	extra:massbox(.75,s,s,s,true)
	--extra:veldrag(.99,.99,.0)
	--extra:rotdrag(.9999,.9999,.9999)
	extra.l3d = l3dprimitive.newbox("extra",l3dset.default():layer(4),s,s,s)
	extra.l3d:matsurface(tex.crate)
	extra.actor = actornode.new("extra")
	extra.glow = addglow(extra.actor,s*6,nil,nil,nil,nil,true)
	extra:addforce(0,-1,0)
	extra.actor:link(extra)
	extra.l3d:linkinterface(extra.actor)
	extra.l3d:rfLitSun(true)
	extra.l3d:rfLitFX(true)
	extra.age = 0
	extra.maxage = 400
	extra.trail = l3dtrail.new("track",l3dset.default():layer(5),2)
	extra.trail:linkinterface(extra.actor)
	extra.trail:spawnstep(0)
	extra.trail:rfNodepthmask(true)
	extra.trail:rfBlend(true)
	extra.trail:size(-1,16)
	extra.trail:matsurface(tex.tractor)
	extra.trail:rsBlendmode(blendmode.add())
	local game = self
	function extra:think()
		for i=0,self:jointcount()-1 do
			local j = self:joint(i)
			local g1,g2 = j:geom1(),j:geom2()
			if g2 and g2.onextra then g1 = g2 end
			if g1 and g1.onextra then 
				g1:onextra(extra)
				extra:delete()
				extra.actor:delete()
				extra.geom:delete()
				return true
			end
		end
		if not game.player.destroyed then
			
			local x,y,z =self:pos()
			local px,py,pz = game.player.body:pos()
			local dx,dy,dz = x-px,y-py,z-pz
			local d = math.sqrt(dx*dx+dy*dy+dz*dz)
			local maxd = 300
			local c = 1-math.min(d,maxd)/maxd
			extra.trail:spawn(x,y,z,0,0,1)
			extra.trail:spawn(px,py,pz,0,0,1)
			extra.trail:color(-1,c,c,c,1)
			if d<maxd then
				local f = .5
				extra:addforce(-dx/d*f,-dy/d*f,0)
				extra:veldrag(.96,.96,0)
				self.age = 0
			else
				extra:veldrag(1,1,0)
			end
		else
			extra.trail:delete()
		end
	
		self.age = self.age + 1
		local x,y,z = self:pos()
		self:pos(x,y,0)
		if self.age>self.maxage then
			self:delete()
			self.actor:delete()
			self.geom:delete()
			return true
		end
	end
	
	self:addthink(extra)
end

function Game:playsnd(name,gain,pitch)
	if type(name)=="table" then name = name[math.random(#name)] end
	assert(snd[name])
	local node = soundnode.new(snd[name])
	
	node:autodelete(false)
	node:play()
	node:gain(gain or 1)
	node:pitch(pitch or 1)
	function node:think()
		return not self:isplaying()
	end
	self:addthink(node)
end

function Game : spawnexplosion(size,x,y,z)
	
	self:playsnd("explode1",math.random()+.5,math.random()+.25)
	local pushgeom = dgeomsphere.new (0.01,self.globalspace)
	pushgeom:pos(x,y,z)
	pushgeom:setcategory(0,2^13)
	pushgeom:setcategory(1,2^13)
	
	self.camshake = (self.camshake or 0) + size
	local actor = actornode.new("aexp",x,y,z)
	actor.explo = l3dpemitter.new("exp",Game.pexplode,l3dset.default():layer(8))
	actor.explo:prtsize (size*.8)
	actor.explo:prtsize ("0",size*2.5)
	
	
	local lactor = actornode.new("light")
	lactor:pos(x,y,z+400)
	lactor.light = l3dlight.new("fx")
	lactor.light:linkinterface(lactor)
	lactor.light:activate (0,700,1750)	-- was 2 seconds
	lactor.light:diffuse(4,4,0,1)
	lactor.light:ambient(0,0,0,1)
	--actor.explo:prtsize ("1",size*.05)
	--actor.explo:prtsize ("10",size*.02)
	--_print("create",self.explo)
	actor.explo:linkinterface(actor)
	actor.explo:start(0)
	
	actor.age = 0
	actor.maxage = 35	-- was 100
	pushgeom:r(size*5)
	pushgeom:surfaceid(5)
	function actor:think()
		
		if actor.age==0 then pushgeom:delete() end
		actor.age = actor.age + 1
		local c = 1 - actor.age / actor.maxage
		--actor.quad:color(c,c,c,1)
		lactor.light:diffuse(c*3,c*3,0,1)
		if actor.age>actor.maxage then
			--pushgeom:delete()
			actor:delete()
			lactor:delete()
			lactor.light:delete()
			actor.explo:delete()
			return true
		end
	end
	self:addthink(actor)
end

dworld.surfacecombination(0,5,5)
dworld.surfacesoftcfm(5,0)
dworld.surfacesofterp(5,0.01)
dworld.surfacecombination(0,6,6)
dworld.surfacesoftcfm(6,0)
dworld.surfacesofterp(6,0.05)

local tempmatrix = matrix4x4.new()

local function bomb (projectile, collidebit)
	
	projectile.geom =dgeomsphere.new(.15,projectile.game.projectilespace)
	projectile.geom:setcategory(0,0)
	projectile.geom:setcategory(1,0)
	projectile.geom:body(projectile.body)
	projectile.geom:surfaceid(6)
	projectile.body:masssphere(15,15,true)

	
	projectile.glow = addglow(projectile.actor,90,nil,nil,nil,nil,true,1,.6)
		
	projectile.age = 0
	projectile.maxage = 90
	projectile.dmg = 1000
	projectile.multihit = true
	projectile.rotval = 0
	
	

	local function addtrail(ox,oy,oz)
		local l3d = l3dtrail.new("p",l3dset.default():layer(8),20)
		l3d:spawnstep(0)
		local r,g,b = 0,0,1
		l3d:color(0,1,1,1,1)
		l3d:color(1,r,g,b,1)
		l3d:color(2,0,0,0,1)
		l3d:rfBlend(true)
		l3d:rsBlendmode(blendmode.add())
		l3d:rfNodepthmask(true)
		l3d:facecamera(true)
		l3d:size(0,5)
		l3d:size(1,4)
		l3d:size(2,3)
		l3d:matsurface(tex.laser)
		l3d:linkinterface(projectile.actor)
		l3d.off = {ox,oy,oz}
		return l3d
	end
	
	projectile.l3ds = {addtrail(15,0,0),addtrail(-15,0,0)}
	
	
	
	function projectile : tick()		
		local x,y,z = self.body:pos()
		local vx,vy,vz = self.body:velocity()
		self.rotval = self.rotval + 0.2
		if self.l3ds then
			
			tempmatrix:identity()
			tempmatrix:rotrad(0,self.rotval,0)
			
			
			for i,l in pairs(self.l3ds) do
				local ox,oy,oz = tempmatrix:transform(unpack(l.off))
				l:spawn(x+vx+ox,y+vy+oy,z+vz+oz,0,0,1)
			end
		end
		
		local c = math.min(1,(1-math.min(1,self.age/self.maxage))*2)*.6
		if self.age == self.maxage-15 then
			self.game:playsnd("explode3",1.5,1.1)
			self.geom:setcategory(1,2^collidebit)
			self.body:veldrag(0,0,0)
			local bomb = l3dpemitter.new("bomb",Game.pbomb,l3dset.default():layer(8))
			bomb:linkinterface(self.actor)
			bomb:start(0)
			self.bomb = bomb
			self.glow:delete()
			self.geom:r(180)
			for i,l in pairs(self.l3ds) do
				l:delete()
			end
			self.l3ds = nil
			self.body:masssphere(150,15,true)
			
			local lactor = actornode.new("light")
			lactor:pos(x,y,z+400)
			lactor.light = l3dlight.new("fx")
			lactor.light:linkinterface(lactor)
			lactor.light:activate (0,1000,1750)	-- was 2 seconds
			lactor.light:diffuse(0,4,4,1)
			lactor.light:ambient(0,0,0,1)
			lactor.age = self.age
			
			self.lactor = lactor
		end
		
		if (self.lactor) then
			local c = (self.age-self.lactor.age)/(self.maxage-self.lactor.age)
			c = 1-c
			self.lactor.light:diffuse(0,c*0.5*3,c*3,1)
		end
		--self.glow:color(c*.05,c*.05,c*.05,1)
	end
	
	function projectile : onhit()
		self.age = 0
		self.maxage = 20
		self.geom:setcategory(1,0) -- no collide anymore
	end
	
end

local function laser (r,g,b,maxage,len,gain)
	r,g,b = r or 1, g or 1, b or 0
	len = len or 5 
	maxage = maxage or 40
	return function (projectile,collidebit)
		projectile.game:playsnd("laser",gain,1.1)
		projectile.geom =dgeomsphere.new(15,projectile.game.projectilespace)
		projectile.geom:setcategory(0,0)
		projectile.geom:setcategory(1,2^collidebit)
		projectile.geom:body(projectile.body)
		projectile.body:masssphere(.01,15,true)
		
		projectile.l3d = l3dtrail.new("p",l3dset.default():layer(8),len)
		projectile.l3d:spawnstep(0)
		projectile.l3d:color(0,r,g,b,1)
		projectile.l3d:color(1,r,g,b,1)
		projectile.l3d:color(2,0,0,0,1)
		projectile.l3d:rfBlend(true)
		projectile.l3d:rsBlendmode(blendmode.add())
		projectile.l3d:rfNodepthmask(true)
		projectile.l3d:facecamera(true)
		projectile.l3d:size(0,3)
		projectile.l3d:size(1,2)
		projectile.l3d:size(2,1)
		
		projectile.l3d:matsurface(tex.laser)
		projectile.l3d:linkinterface(projectile.actor)
		
		projectile.glow = addglow(projectile.actor,10,nil,nil,nil,nil,true,1,.6)
		
		projectile.age = 0
		projectile.maxage = maxage
	
		function projectile : tick()		
			local x,y,z = self.body:pos()
			local vx,vy,vz = self.body:velocity()
			self.l3d:spawn(x+vx,y+vy,z+vz,0,0,1)
			
			local c = math.min(1,(1-math.min(1,self.age/self.maxage))*2)*.6
			
			self.l3d:color(0,1*c,1*c,1*c,1)
			self.l3d:color(1,r*c,g*c,b*c,1)
			self.l3d:color(2,0,0,0,1)
			--self.glow:color(c*.05,c*.05,c*.05,1)
		end
		
		function projectile : onhit()
			projectile.game:playsnd({"hit1","hit2"},1.0,1.1)
			self.age = 0
			self.maxage = 20
			self.geom:setcategory(1,0) -- no collide anymore
		end
	end
end

function Game : Game (levelfile)	
	print("Loading game world: ",levelfile)
	local tab = assert(dofile(levelfile))

	local w,h = window.refsize()
	local tw,th = tex.power:dimension()
	local x,y = (w-tw)*.5,h-th
	self.gui = l2dnode.new("gui")
	self.gui:parent(l2dlist.getroot())
	self.screen = l2dimage.new("screen", matsurface.vertexcolor())
	self.screen:pos(x+4,y+4,0)
	self.screen:scale(tw-8,th-4,1)
	self.screen:color(0,.2,0,1)
	self.screen:parent(self.gui)
	
	local function barset(self,pwr)
		pwr = math.min(20,math.max(0,pwr/5))
		local x = self:pos()
		self:pos(x,4+20-pwr,0)
		self:scale(12,pwr,1)
		if self.frame then
			self.frame:scale(12,pwr,1)
		end
	end
	
	
	self.dmgbar = l2dimage.new("dmgbar", matsurface.vertexcolor())
	self.dmgbar:parent(self.screen)
	self.dmgbar:color(1,.5,0,1)
	self.dmgbar:pos(20,4,0)
	self.dmgbar:scale(12,20,1)
	self.dmgbar.set = barset
	
	self.shieldbar = l2dimage.new("dmgbar", matsurface.vertexcolor())
	self.shieldbar:parent(self.screen)
	self.shieldbar:color(0,1,0,1)
	self.shieldbar:pos(4,4,0)
	self.shieldbar:scale(12,20,1)
	
	self.shieldbar.set = barset
	
	self.pwrbar = l2dimage.new("dmgbar", matsurface.vertexcolor())
	self.pwrbar:parent(self.screen)
	self.pwrbar:color(1,1,0,1)
	self.pwrbar:pos(36,4,0)
	self.pwrbar:scale(12,20,1)
	self.pwrbar.set = barset
	
	self.extracnt = l2dtext.new("extracnt","0")
	self.extracnt:parent(self.screen)
	self.extracnt:color(0,1,0,1)
	self.extracnt:pos(48,4,0)
	self.extracnt:size(24)
	self.extracnt:spacing(8)
	function self.extracnt:set(n)
		self:text(tostring(n))
	end
	
	self.display = l2dimage.new("display",tex.power)
	self.display:pos(x,y,0)
	self.display:scale(tw,th,1)
	self.display:parent(self.gui)
	self.display:rfBlend(true)
	self.display:rsBlendmode(blendmode.decal())
	
	
	self.simtime = 0
	self.list = {}
	self.entity = {}
	self.thinklist = {}
	self.activatespace = dspacehash.new () -- space for objects 
	  -- to be activated
	self.globalspace = dspacehash.new()
	self.projectilespace = dspacehash.new(self.globalspace)
	self.projectilespace:collidetest(false)
	
	
	self.projectiletypes = {}
	self.projectiletypes.laser = laser(nil,nil,nil,nil,nil,1)
	self.projectiletypes.enemylaser = laser(0,1,0,90,10,.4)
	self.projectiletypes.bomb = bomb
		
	
	
	self.player = new (Player,self)
	
	for i,props in ipairs(tab) do
		local node
		local x,y,z = unpack(props["location.position"])
		local rx,ry,rz = unpack(props["location.rotation"])
		

		local function setl3dstuff ()
			
			node.l3d:rfBlend(true)
			local mode = props["rendering.blendmode"]
			if mode == "additive" then
				node.l3d:rsBlendmode(blendmode.add())
			elseif mode == "amodadd" then
				node.l3d:rsBlendmode(blendmode.amodadd())
			elseif mode == "decal" then
				node.l3d:rsBlendmode(blendmode.decal())
			elseif mode == "modulate" then
				node.l3d:rsBlendmode(blendmode.modulate())
			else
				--assert(mode=="<no blendmode>","wrong blend: "..tostring(mode))
				node.l3d:rsBlendmode(blendmode.disable())
				node.l3d:rfBlend(false)
			end
		
			
			node.l3d:linkinterface(node)
			node.l3d:rfNocull(props["rendering.no backgraceculling"]==true)
			node.l3d:rfNodepthmask(props["rendering.no depthmask"]==true)
			node.l3d:rfNodepthtest(props["rendering.no depthtest"]==true)
			
			node.l3d:rfLitSun(props["rendering.lit sun"]==true)
			node.l3d:rfLitFX(props["rendering.lit fx"]==true)
			node.l3d:color(unpack(props["rendering.color"] or {1,1,1,1}))
			
			if node.localrotdeg then 
				node:localrotdeg(rx,ry,rz)
			else
				node:rotdeg(rx,ry,rz)
			end
		end
		if props["trigger.type"] == "Scriptfile" then
		
			node = dgeomsphere.new(1,self.activatespace)
			function node.activate()
				local activator = {}
				function activator.think()
					if self.player.destroyed then return 1 end
					local px,py = self.cam:pos()
					if py>y then
						dofile ("ingame/"..props["trigger.file"])
						return 1
					end
				end
				self:addthink(activator)
			end
			node:pos(0,y,0)
		end
		if props["game.enemytype"] and props["game.enemytype"]~="<none>" then
			-- its an enemy
			node = actornode.new(props.name)
			local info = {
				["Small fighter"] = {mdl = mdl.enemy1, size = {16}, 
					trails = {
						{x= 8,y=30,z=5,r=0,g=1,b=0.33,size=4},
						{x=-8,y=30,z=5,r=0,g=1,b=0.33,size=4}
					},
					extra = 1,
					health = 100,
					code = "smallfighter.lua",
					mass = .75
				},
				["Medium fighter"] = {mdl = mdl.enemy4, size = {25},
					health = 260,
					trails = {
						{x= 27,y=10,z=4,r=0,g=1,b=0.33,size=2},
						{x=-27,y=10,z=4,r=0,g=1,b=0.33,size=2},
						{x= 14,y=20,z=4,r=0,g=1,b=0.33,size=2},
						{x=-14,y=20,z=4,r=0,g=1,b=0.33,size=2}
					},
					extra = 2,
					code = "mediumfighter.lua",
					mass = 1,
				},
				["Heavy fighter"] = {mdl = mdl.enemy3, size = {40},
					health = 300,
					trails = {
						{x= 0,y=35,z=10,r=0,g=1,b=0.33,size=8},
					},
					extra = 3,
					code = "heavyfighter.lua",
					mass = 2,
				},
				["Bomber"] = {mdl = mdl.enemy2, size = {35},
					health = 460,
					trails = {
						{x= 30,y=32,z=6,r=0,g=1,b=0.33,size=8},
						{x=-30,y=32,z=6,r=0,g=1,b=0.33,size=8}
					},
					extra = 5, 
					code = "bomber.lua",
					mass = 5,
				},
					
				["Transporter"] = {mdl = mdl.enemy5, size = {30},
					health = 600,
					trails = {
						{x= 14,y=35,z=9,r=0,g=1,b=0.33,size=2},
						{x=-14,y=35,z=9,r=0,g=1,b=0.33,size=2}
					},
					extra = 8,
					code = "transporter.lua",
					mass = 10,
				}
			}
			local info = info[props["game.enemytype"] ]
			local layer = l3dset.default():layer(4)
			node.trails = {}
			for i,trail in ipairs(info.trails or {}) do
				local t = l3dtrail.new("et",l3dset.default():layer(6),6)
				t:linkinterface(node) t:rfNocull(true) 
				t.x,t.y,t.z = trail.x,trail.y,trail.z
				t:uselocaltime(true)
				t:size(-1,trail.size)
				t:rfBlend(true)
				t:rsBlendmode(blendmode.add())
				t:rfNodepthmask(true)
				t:usevelocity(true)
				local r,g,b = trail.r or 0,trail.g or 0,trail.b or 0
				t:color(0,1,1,1,1)
				t:color(1,r,g,b,1)
				t:color(2,0,0,0,1)
				t:matsurface(tex.trail1)
				t.glow = addglow(node,100,"06",t.x,t.y,t.z)
				t.glow:color(.9,.9,.9,.5)
				node.trails[i] = t
			end
			
			node.mdl = l3dmodel.new("g",layer,info.mdl)
			--self.game.l3d.mdl:setlayer(layer)
			node.mdl:linkinterface(node)
			node.mdl:rfLitSun(true)
			node.mdl:rfLitFX(true)
			node:rotdeg(rx,ry,rz)
			node.activator = dgeomsphere.new(1,self.activatespace)
			
			function node.activator.activate()
				self:addthink(node)
			end
			
			node.body = dbody.new()
			node.body:veldrag(.92,.92,.92)
			node.body:rotdrag(.95,.95,.95)
			node:link(node.body)
			local r = unpack(info.size)
			node.body:masssphere(info.mass,r,true)
			node.geom = dgeomsphere.new(r,self.globalspace)
			node.geom:category(0,1,false)
			node.geom:category(1,1,false)
			node.geom:body(node.body)
			node.geom.l3d = l3dprimitive.newbox("db",unpack(info.size))
			--node.geom.l3d:linkinterface(node)
			node.geom.l3d:rfWireframe(true)
			node.health = info.health
			node.nextfire = node.health*.8
			node.fires = {}
			function node.body:takedamage(dmg)
				node.health = node.health - dmg
				if node.health<node.nextfire then
					node.nextfire = node.nextfire - 10
					local explo = l3dpemitter.new("fir",Game.pfire,l3dset.default():layer(8))
					--explo:prtsize (size*.5)
					--explo:prtsize ("0",size*1.5)
					--actor.explo:prtsize ("1",size*.05)
					--actor.explo:prtsize ("10",size*.02)
					--_print("create",self.explo)
					explo:uselocal(true)
					local mid = info.mdl:meshid(0)
					
					explo:localpos(mid:vertexPos(math.random(mid:vertexCount())-1))
					explo:parent(node.mdl)
					explo:start(0)
					table.insert(node.fires,explo)
				end
			end
			node.body:pos(x,y,z)
			node.body:rotdeg(rx,ry,rz)
			node.rx,node.ry,node.rz = rx,ry,rz
			node.activator:pos(x,y,0)
			node.body:lock(3,0,0,.9)
			
			if not info.code then
				function node:think()
				
				end
			else
				local fn,env = assert(loadfile("enemies/"..info.code)),{}
				setmetatable(env,{__index = _G})
				setfenv(fn,env)
				fn()
				node.age = 0
				node.game = self
				function node:explode()
					node.geom:category(0,13,false)
					node.geom:category(1,13,false)
					self.game:spawnexplosion(100,self.body:pos())
					
				end
				
				function node:think()
					local x,y,z = self.body:pos()
					local dx,dy,dz = self.body:local2world(0,1,0)
					dx,dy,dz = dx-x, dy-y, dz - z
					for i,t in ipairs(self.trails) do
						local x,y,z = self.body:local2world(t.x,t.y,t.z)
						t:localtime(self.game.simtime)
						local f = node.timeout and math.max(0,500-node.timeout*20) or 500
						t:spawn(x,y,z,0,0,1,dx*f,dy*f,dz*f)
					end
					if node.health<0 then
						node.timeout = (node.timeout or 0) + 1
						if node.timeout==8 then
							node:explode()
							self.body:rotdrag(1,1,1)
							self.body:veldrag(1,1,1)
						end		
						if node.timeout>30 then 
							local x,y,z = self.body:pos()
							self.body:delete()
							self.geom:delete()
							self:delete()
							for i=1,info.extra do 
								self.game:spawnextra(x+math.random()*20-10,y+math.random()*20-10,z)
							end
							return true
						else return false end
					end
					self.age = self.age + 1
					local x,y,z = self.body:pos()
					local cx,cy,cz = self.game.cam:pos()
					if y+levelw*2<cy then 
						self.game.list[node] = nil
						return true end
						
					return env.think(node,self.player,{})
				end
			end
		elseif props["rendering.model"] and props["rendering.model"]~='<none>' then 
			local l = tonumber((props["rendering.layer"] or "Layer 0"):match("%d+"))
			local layer = l3dset.default():layer(l)
			node = scenenode.new(props.name,true)
			node:localpos(x,y,z)
			local m = mdl[props["rendering.model"] ]
			node.l3d = l3dmodel.new(props.name,layer,m)
			node.l3d:setlayer(layer)
			node.l3d:linkinterface(node)
			local sx,sy,sz = unpack(props["rendering.scale"] or {1,1,1})
			node.l3d:renderscale(sx,sy,sz)
			setl3dstuff()
			
			local a,b,c,d,e,f = m:bbox()
			a,b,c,d,e,f = a*sx,b*sy,c*sz,d*sx,e*sy,f*sz
			a,b,c,d,e,f = math.min(a,-100),math.min(b,-100),math.min(c,-100),
			math.max(d,100),math.max(e,100),math.max(f,100)
			node:vistestbbox(a,b,c,d,e,f)
		elseif props["rendering.billboard"] and props["rendering.billboard"]~='<none>' then
			local l = tonumber((props["rendering.layer"] or "Layer 0"):match("%d+"))
			local layer = l3dset.default():layer(l)
			local sx,sy,sz = unpack(props["rendering.scale"] or {1,1,1})
			node = scenenode.new(props.name,true)
			node:localpos(x,y,z)
			node.l3d = l3dprimitive.newquadcentered(props.name,layer,sx,sy,sz)
			node.l3d:setlayer(layer)
			node.l3d:matsurface(tex[props["rendering.billboard"] ])
			setl3dstuff()
		
			node:vistestbbox(math.min(-sx,-100),math.min(-sy,-100),math.min(-sz,-100),
			math.max(sx,100),math.max(sy,100),math.max(sz,100))
		end
		
		
		if props["light.active"] then
			local l = tonumber((props["rendering.layer"] or "Layer 0"):match("%d+"))
			local layer = l3dset.default():layer(l)
			if not node then 
				node = scenenode.new(props.name,layer,true)
				node:localpos(x,y,z)
			end
			node.light = l3dlight.new(props.name,layer)
			node.light:linkinterface(node)
			
			local r = (props["light.range"] or {10000})[1]
			local R,G,B,A = unpack(props["light.color"] or {1,1,1,1})
			node.light:activate(0,r*4,0)
			local c = r^3
			node.light:diffuse(c*R,c*G,c*B,c*A)
			node.light:attenuateconst(0)
			node.light:attenuatelinear(r)
			node.light:attenuatequadratic(r*4)
		end

		if node then self.list[node] = true end
	end
	MouseCursor.showMouse(false)
	
	local function kdf (which) -- returns a function which 
		-- returns the state of the given key
		return function () return Keyboard.isKeyDown(which) end 
	end

	local up,down,del,ctrl,alt,shift =  
		kdf "PAGEUP",kdf "PAGEDOWN", kdf "DEL", kdf "CTRL",kdf "alt",kdf "shift"
	
	self.sun = actornode.new("sun",1000,200,10000)
	self.sun.l3d = l3dlight.new("sun")
	self.sun.l3d:makesun()
	self.sun.l3d:linkinterface(self.sun)
	self.sun.l3d:ambient(.35,.35,.35)
	self.sun.l3d:diffuse(.55,.55,.55)
	
	self.cam = actornode.new("cam")
	--self.con = Container.getRootContainer():add(GroupFrame:new(0,0,100,30))
	self.cam.camera = l3dcamera.default()
	self.cam.camera:fov(60)
	self.cam.camera:linkinterface(self.cam)
	self.cam:pos(200,-400,2000)
	self.cam:lookat(0,0,0,0,0,1)
	self.cam.camera:backplane(2048*16)
	self.cam.camera:fov(40)
	self.cam.activationblock = dgeombox.new(levelw*4,1200,200)
	self.pitch,self.head = 0,0
	
	self.simtime = 0
	
	self.player:think()
	
end

function Game : showMenu (comp,onok)
	self.pausetimeout = 0
	MouseCursor.showMouse(true)
	local bw,bh = comp:getSize()
	bw,bh = bw + 20, bh + 40
	local w,h = window.refsize()
	local box = GroupFrame:new(w*.5,(h-bh)*.5,0,bh)
	box:moveToRect((w-bw)*.5,(h-bh)*.5,bw,bh,.1,20)
	comp:setLocation(10,10)
	box:add(comp)
	box:getSkin():setSkinColor(0,0,0,.5)
	--text:getSkin():setFontColor(1,0,0,1)
	local btn = box:add(Button:new(bw-45,bh-25,40,20,"OK"))
	btn:getSkin():setSkinColor(0,1,0,.3)
	btn:getSkin():setFontColor(0,1,0,1)
	Container.getRootContainer():add(box)
	function btn.onClicked()
		self.displayingmessage = false
		self.pause = false
		box:moveToRect(w*.5,(h-bh)*.5,0,bh,.25,20,function()box:delete()end)
		if onok then onok() end
	end
	self.displayingmessage = true
	if (postfx and postfx.distort) then
		postfx.distort.scale(0,0)
	end
	render.pauseparticles(true)
end

function Game : showLastMessage ()
	if self.lastmsg then
		self:showMessage(self.lastmsg)
	end
end

function Game : showMessage (tx,onok)
	
	self.lastmsg = tx
	bw,bh = 480,360
	local text = Label:new(0,0,bw,bh)
	text.colors = {}
	setmetatable(text.colors,{__index = Label.colors})
	text.colors.text = {0,1,0,1}
	text:setAlignment(nil,Label.LABEL_ALIGNTOP)
	tx = text:wrapLine(tx)
	TimerTask.new(function ()
		for i=1,#tx do 
			text:setText(tx:sub(1,i).."_")
			coroutine.yield(20)
		end
		text:setText(tx)
	end,1000)
	self:showMenu(text,onok)
end

local collresult = dcollresult.new()
function Game:think(donthidemouse)
	while self.pause or self.displayingmessage do return end
	render.pauseparticles(false)
	if (postfx and postfx.distort) then
		local t = system.time()
		t = t * 0.00008
		local w,h = window.size()
		postfx.distort.scale(16*w/640,16*h/480)
		postfx.distort.offset(t,math.sin(t))
	end
	if not donthidemouse then
		MouseCursor.showMouse(console.active())
	end
	self.shieldbar:set(self.player.shield or 0)
	self.pwrbar:set(self.player.energy or 0)
	self.dmgbar:set(self.player.hp or 0)
	self.extracnt:set(self.player.extracounter or 0)
	
	if self.player.destroyed then
		
		local x,y = self.gui:pos()
		self.gui.y = (self.gui.y or y)+.1
		self.gui:pos(x,math.floor(self.gui.y),0)
		self.deathout = (self.deathout or 0)+1
		if self.deathout==300 then
			if self.onPlayerDeath then 
				self:onPlayerDeath()
			end
		end
	end
	
	if self.simtime < 20 then collectgarbage"collect" end
	self.camshake = (self.camshake or 0)*.9
	self.camshakex = math.sin(self.simtime)*self.camshake*.05
	self.camshakey = math.cos(self.simtime)*self.camshake*.05
	local x,y = self.cam:pos()
	self.cam.activationblock:pos(x,y,0)
	
	local cnt = collresult:test(self.cam.activationblock,self.activatespace)
	
	
	if cnt>0 then
		for i=0,cnt-1 do
			local g1,g2 = collresult:geoms(i)
			local g = (g1 == self.cam.activationblock and g2 or g1)
			g:activate()
			g:delete()
		end
	end

	self.simtime = self.simtime + 20
	
	
	dworld.collidetest(self.globalspace)
	dworld.makecontacts()
	dworld.quickstep(1)
	
	self:thinkall()
	
end