
ActionEntity = inherited("ActionEntity", SimEntity)

Network.registerClass(ActionEntity)

ActionEntity.variables = {
	sync = {"x", "y", "angle", "dx", "dy", "da","health"},
	new = {"map", "owner", "x", "y", "angle", "dx", "dy","da", "health"},
}
ActionEntity.structure = {
	sync = {"f","f","f","f","f","f","f"},
	new = {"e", "e", "f", "f","f","f","f","f","f"},
}

function ActionEntity:new(map, owner, x, y, angle, dx, dy, da, health)
	local b = instance(self, map, owner, x, y, angle, dx, dy, da, health)
	return b
end

function ActionEntity:onNew(map, owner, x, y, angle, dx, dy, da, health)
	self.map = map
	self.owner = owner
	self.x = x
	self.y = y
	self.angle = angle
	self.dx = dx or 0
	self.dy = dy or 0
	self.da = da or 0
	self.health = health
end

function ActionEntity:getSpeedFactor()
	local speed = math.distance(0,0,self.dx,self.dy)
	return math.pow(speed, 2)/4000
end


function ActionEntity:stat(id)
	if self:def()[id] then
		return self:def()[id]
	else
		debug.printtrace()
		print("stat",id, "missing")
		return 1
	end
end

function ActionEntity:getOwner()
	if self.owner then
		return self.owner:getOwner()
	else
		return self
	end
end


function ActionEntity:getTrailOffset()
	return 0,0
end

function ActionEntity:createTrail()
	self.trail = {}
	self.trailDelay = 0
	self.trailTime = 0
	self.trailRadius = 0
end

function ActionEntity:renderTrailAt(x,y,scale,angle,a,r,g,b,width, interp)
	local ofx, ofy = self:getTrailOffset()
	local lx, ly, ang, lw= x+ofx*scale, y+ofy*scale, angle, width*scale
	for index, trail in ipairs(self.trail) do
		local sx,sy = self:interp("x", interp), self:interp("y", interp)
		local ox = x - sx*scale + trail.x*scale
		local oy = y - sy*scale + trail.y*scale
		
		local ttrailOp = math.clamp(1-(self.trailTime - trail.time),0,1)*0.25
		local wid = math.clamp(1-(self.trailTime - trail.time),0,1)*width*scale
		video.renderAngledSpriteLineShaped(lx, ly,ang, ox,oy, trail.angle, a*ttrailOp, r*0.1, g*0.1, b*0.1, nil, lw, wid)
		lx, ly, ang, lw = ox,oy, trail.angle, wid
	end
end

function ActionEntity:getRenderRadius()
	return (self.trailRadius or 0) + self:getRadius()
end

function ActionEntity:updateTrailRadius()
	self.trailRadius = 0
	for index, trail in pairs(self.trail) do
		self.trailRadius = math.max(self.trailRadius, math.distance(self.x, self.y, trail.x, trail.y))
	end
end

function ActionEntity:updateTrail(time, duration)
	local interp = self:getInterpolation(self.map.sim.time)
	if interp then
		local x,y = self:interp("x", interp), self:interp("y", interp)
		self.trailTime = self.trailTime + time/duration
		local speed = math.distance(0,0,self.dx, self.dy)
		self.trailDelay = self.trailDelay - time*math.min(10,speed/100)
		if self.trailDelay <= 0 then
			local ofx, ofy = self:getTrailOffset()
			table.insert(self.trail,1, {x = x +ofx, y = y + ofy, time = self.trailTime, angle = self.angle})
			self.trailDelay = 1
			self:updateTrailRadius()
		end
		if #self.trail > 0 then
			local step = self.trail[#self.trail]
			while step and step.time < self.trailTime-1 do
				table.remove(self.trail, #self.trail)
				
				self:updateTrailRadius()
				if #self.trail > 0 then
					step = self.trail[#self.trail]
				else
					step = nil
				end
			end
		end
	end
end

function ActionEntity:hurt(h)
	self.health = self.health -h
	if self.health <= 0 then
		return true
	end
end



function ActionEntity:getEffectRadius(angle)
	return self:getRadius(angle)
end

function ActionEntity:explode()
	if self:def().explosionDamage then
		for index, ent in pairs(self.map.ents) do
			if ent ~= self then
				if ent:isTarget() and ent:canBeHurtBy(self) then
					local relAngle = ent:getAngleTo(self)
					local relDist = math.max(0,self:getDistanceTo(ent) - ent:getEffectRadius(relAngle))
					if relDist < self:stat("explosionRadius") then

						local protected = false
						for index, pent in pairs(self.map.ents) do
							if ent ~= pent and pent:isProtective() and not self:isInside(pent) and ent:isInside(pent) then
								protected = true
								break
							end
						end

						if not protected then
							local factor = (self:stat("explosionRadius") - relDist)/self:stat("explosionRadius")

							local damFact = math.pow(factor,self:stat("explosionDamageExp"))
							ent:hurt(self:stat("explosionDamage")*damFact)
							local relAngle = self:getAngleTo(ent)
							ent:addSpeed(relAngle, factor * self:stat("explosionForce"))
						end
					end
				end
			end
		end
	end
end

function ActionEntity:onDestroy()

end

function ActionEntity:remove()
	if not self._remove then
		self._remove = true
		if self.owner.removeEnt then
			self.owner:removeEnt(self)
		end
		self.map:removeEnt(self)
	end
	if self.resources then
		for index, res in pairs(self.resources) do
			res:setOwner(self.map)
		end
	end
end

function ActionEntity:applyHail(time)
	local speedEffect = self:getSpeedFactor()
	local hailAmount = self.map:getHailAmount(self.x, self.y)
	if hailAmount > 0 then
		self:hurt(hailAmount*time*speedEffect)
	end
end

function ActionEntity:applyGravity(time)
	self.dy = self.dy + time*self.map:getGravity(self.x, self.y)
end

function ActionEntity:applyFrictionDelta(time)
	local airFriction = self.map:getAtmosphere(self.x, self.y)
	local frictionDelta = self.lastFriction and (airFriction - self.lastFriction) or 0
	self.lastFriction = airFriction
	if frictionDelta > 0 then
		self.dy = self.dy - frictionDelta*time*math.abs(math.cos(self.angle))*100 - frictionDelta*time*10
	end
end

function ActionEntity:applyAngleAdjustment(time)
	local airFriction = self.map:getAtmosphere(self.x, self.y)
	local speedAngle = math.angleBetweenPoints(0,0,self.dx, self.dy)
	local speed = math.distance(0,0,self.dx, self.dy)
	self.da = math.approach(self.da, math.angleDifference(speedAngle, self.angle), airFriction*time*speed*self:stat("angleAdjustment"))
end

function ActionEntity:applyFriction(time)
	local airFriction = self.map:getAtmosphere(self.x, self.y)
	self.dx = math.approach(self.dx, 0, math.pow(self.dx*self.map:getFriction(),2)*airFriction*time*self:stat("airFriction"))
	self.dy = math.approach(self.dy, 0, math.pow(self.dy*self.map:getFriction(),2)*airFriction*time*self:stat("airFriction"))
	self.da = math.approach(self.da, 0, math.pow(self.da*self.map:getFriction(),2)*airFriction*time*self:stat("angleFriction"))
end

function ActionEntity:hasCollision()
	return true
end

function ActionEntity:step(time)
	self.dy = self.dy + time*self.map:getGravity(self.x, self.y)
	self.x = self.x + self.dx*time
	self.y = self.y + self.dy*time
	self.angle = self.angle + self.da*time

	if self:hasCollision() then
		local hit, ent, angle = self.map:isInEnt(self,self.x, self.y, self.dx, self.dy)
		if hit and self:canCollideWith(ent) and ent:canCollideWith(self) then
			local moveAng = math.angleBetweenPoints(0,0,self.dx, self.dy)
			local impulse = math.abs(math.cos(math.angleDifference(moveAng, angle)))*math.distance(0,0,self.dx, self.dy)*self:getMass()

			local counterImpulse = nil
			if not ent:isBuilding() then
				local counterMoveAng = math.angleBetweenPoints(0,0,ent.dx, ent.dy)
				counterImpulse = math.abs(math.cos(math.angleDifference(counterMoveAng, angle+math.pi)))*math.distance(0,0,ent.dx, ent.dy)*ent:getMass()
			else
				counterImpulse = impulse
			end

			self.x = self.x - self.dx*time
			self.y = self.y - self.dy*time
			self:onHit(ent, angle, impulse, counterImpulse)
			ent:onHit(self, angle+math.pi, counterImpulse, impulse)
		else
			local collide, normal = self.map:isInTerrain(self.x, self.y, self.dx, self.dy, self:getRadius())
			if collide then
				local moveAng = math.angleBetweenPoints(0,0,self.dx, self.dy)
				local impulse = math.abs(math.cos(math.angleDifference(moveAng, normal)))*math.distance(0,0,self.dx, self.dy)*self:getMass()
				local torque = math.sin(math.angleDifference(moveAng, normal))*math.distance(0,0,self.dx, self.dy)/self:getRadius()/self:getMass()
				local speed = math.distance(0,0,self.dx, self.dy)
				self.x = self.x - self.dx*time
				self.y = self.y - self.dy*time

				self:onCollided(normal+math.pi, impulse, torque)
			end
		end
	else
		local collide, normal = self.map:isInTerrain(self.x, self.y, self.dx, self.dy, self:getRadius())
		if collide then
			self.ground = true
		end
	end

	if self.health <= 0 then
		self:onDestroy()
		self:remove()
	end
end

function ActionEntity:onFakeEvent(event)
	if self:def().events and self:def().events[event] then
		self:def().events[event](self)
	end
end

function ActionEntity:getMass()
	return 1
end

function ActionEntity:bounce(normal, restitution)
	local normal = normal
	local restitution = restitution or 1
	local moveAng = math.angleBetweenPoints(0,0,self.dx, self.dy)
	local moveDist = math.distance(0,0,self.dx, self.dy)

	moveAng = normal - math.angleDifference(moveAng + math.pi, normal)
	local dx = math.cos(moveAng)*moveDist
	local dy = math.sin(moveAng)*moveDist

	local upx, upy = vectorMath.project(dx, dy, math.cos(normal), math.sin(normal))
	local rightx, righty = vectorMath.project(dx, dy, math.cos(normal + math.pi*0.5), math.sin(normal + math.pi*0.5))
	self.dx = upx*restitution + rightx
	self.dy = upy*restitution + righty
end

function ActionEntity:rotate(amt)
	self.da = self.da + amt
end

function ActionEntity:addSpeed(angle, amt)
	self.dx = self.dx + math.cos(angle)*amt
	self.dy = self.dy + math.sin(angle)*amt
end

function ActionEntity:onCollided(normal, impulse, torque)
	self:bounce(normal, 0.75, impulse/self:getMass())
	self:rotate(torque)
	self:addSpeed(normal, impulse*0.5/self:getMass())
end	

function ActionEntity:onHit(ent, angle, impulse, counterImpulse)
	self:bounce(angle, 0.75, impulse)
	self:addSpeed(angle, impulse*0.5/self:getMass())
end	

function ActionEntity:getRadius()
	return 1
end

function ActionEntity:renderAt(x,y,scale,angle,a,r,g,b)
	video.renderSpriteState(SPRITES.arrow, x, y, scale, self.angle + angle, a, r, g, b)
	--self.map:renderCollision(self.x, self.y, self.dx, self.dy, 32, x,y,scale,angle, a, 255, 0, 0)
end


SpaceShip = inherited("SpaceShip", ActionEntity)

Network.registerClass(SpaceShip)

SpaceShip.variables = {
	sync = {"x", "y", "angle", "rudder", "dx", "dy", "da", "roll", "health", "energy", "engine", "heat"},
	new = {"map", "owner", "x", "y", "angle", "dx", "dy", "da", "type"},
}
SpaceShip.structure = {
	sync = {"f","f","r","f","f","f","f","r","f","f", "f", "f"},
	new = {"e", "e", "f", "f","f","f","f", "f", "i"},
}

SpaceShip.interpolation = {
	angle = INTERPOLATORS.angle,
	--x = INTERPOLATORS.xpos,
	--y = INTERPOLATORS.ypos,
}

function SpaceShip:new(map, owner, x, y, angle, dx, dy, da, type)
	local b = instance(self, map, owner, x, y, angle, dx, dy, da, SHIP_TYPES[type].health)
	b.type = type
	b.desiredAmount = 0
	b.desiredAngle = 0
	b.action = false
	b.alt = false
	b.time = 0

	b.energy = SHIP_TYPES[type].energy

	b.dr = 0
	b.roll = 0
	b.rollDir = 1
	b.desiredRoll = 0

	b.thrust = 0
	b.engine = 0
	b.rudder = 0
	b.engineRudder = 0
	b.resources = {}

	b.heat = 0
	map:addEnt(b)
	
	return b
end

function SpaceShip:def()
	return SHIP_TYPES[self.type]
end

function SpaceShip:getMass()
	return self:stat("mass")
end

function SpaceShip:addResource(res)
	table.insertUnique(self.resources, res)
end

function SpaceShip:removeResource(res)
	table.removeUnique(self.resources, res)
end

function SpaceShip:getRadius()
	return self:stat("radius")
end

function SpaceShip:isTarget()
	return true
end

function SpaceShip:isCarrier()
	return true
end

function SpaceShip:onDestroy()
	self:explode()	
end

function SpaceShip:onKilled()
	if not self.killed then
		self.killed = true
		self.map.sim:create(Resource, self.map, self.map, self.x, self.y, self.dx*0.75, self.dy*0.75 - math.random()*500 - 500,RESOURCE_TYPES.plutonium)
	end
end

function SpaceShip:onCollided(normal, impulse, torque)
	self:bounce(normal, 0.6, impulse/self:getMass())
	local diff = math.sin(math.angleDifference(self.angle, normal+math.pi))
		--print(diff, normal/math.pi)
	if diff > 0.25 then
		self:rotate(torque * math.abs(math.sin(math.angleDifference(self.angle, normal))))
	else
		self.da = math.approach(self.da, math.angleDifference(normal+math.pi, self.angle), impulse/self:getMass()*math.abs(torque)*100)
	end

	self.ground = true
	
	--self:addSpeed(normal+math.pi, impulse*0.5)

	local impulseDamage = impulse/self:getMass()*self:stat("collisionDamageFactor")
	if impulseDamage > self:stat("collisionThreshold") then
		local damage = impulseDamage - self:stat("collisionThreshold")
		if self:hurt(damage) then
			self.dx = math.cos(normal)*impulse/self:getMass()*0.25
			self.dy = math.sin(normal)*impulse/self:getMass()*0.25
		end
	end
end	

function SpaceShip:onHit(ent, angle, impulse, counterImpulse)
	--ent:onCollided(angle, impulse, 0)
	self:addSpeed(angle, impulse/self:getMass())
	if ent:canBeHurtBy(self) then
		ent:hurt(impulse*self:stat("impulseDamageFactor"))
	end
end	

function SpaceShip:hurt(h)
	local damage = h
	if self.energy > 0 then
		local subHurt = math.min(h,self.energy)
		damage = damage - subHurt
		self.energy = self.energy - subHurt
	end
	if damage > 0 then
		self.health = math.max(0, self.health - damage)
		if self.health <= 0 then
			return true
		end
	end
end

function SpaceShip:step(time)
	local desiredThrust = math.min(1,math.max(0,(self.desiredAmount - 0.25))/0.65)
	self.thrust = math.approach(self.thrust,desiredThrust, time * self:stat("engineResponsiveness"))

	local using = false

	if self.desiredAmount > 1 then
		self.boost = math.min(self.energy/self:stat("boostDrain"), 1)
	else
		self.boost = 0
	end
	local desiredRudder = -math.angleDifference(self.angle, self.desiredAngle)

	self.da = math.approach(self.da, desiredRudder, self:stat("angleThrusters")*time)

	self.rudder = math.clamp(math.approach(self.rudder,desiredRudder, time * self:stat("rudderResponsiveness")), -self:stat("rudderArcUp"), self:stat("rudderArcDown"))

	local flatAmount = (1-self:stat("flatFactor")) + self:stat("flatFactor")*math.abs(math.cos(self.roll))
	local flatFactor = math.cos(self.roll)

	if math.abs(desiredRudder) > math.pi*0.5 then
		local base = math.round(math.normalizeAngle(self.roll)/(math.pi*0.5))*math.pi*0.5
		self.desiredRoll = math.pi*0.5*math.sign(desiredRudder)
		self.rollDir = math.sign(desiredRudder)
	else
		local targetRoll = math.round(math.normalizeAngle(self.angle)/(math.pi))*math.pi
		local last = math.angleDifference(self.desiredRoll, targetRoll)
		if self.rollDir ~= 0 then
			self.desiredRoll = math.normalizeAngle(self.desiredRoll + time*self:stat("rollRestoration")*self.rollDir)
			--print()
			if math.sign(last) ~= math.sign(math.angleDifference(self.desiredRoll, targetRoll)) then
				self.rollDir = 0
			end
			--math.approachAngle(self.desiredRoll,math.approachAngle(self.desiredRoll,math.round(math.normalizeAngle(self.angle)/(math.pi))*math.pi, self.rollDir*math.pi*0.5), time*self:stat("rollRestoration") ) 
		else
			self.desiredRoll = math.approachAngle(self.desiredRoll,targetRoll, time*self:stat("rollRestoration") ) 
		end
	end

	if not self.ground then
		self.roll = self.roll + self.dr*time*self:stat("rollSpeed")
	end
	--apply thrust
	self.engine = self.thrust

	--boost 
	if self.boost > 0 then
		self.engine = self.engine + self.boost*self:stat("boostBonus")
		self.energy = math.approach(self.energy, 0, time*self:stat("boostDrain"))
		using = true
	end

	--engine
	self.engineRudder = math.clamp(math.approach(self.engineRudder, desiredRudder, time * self:stat("engineRudderResponsiveness")), -self:stat("engineRudderArc"), self:stat("engineRudderArc"))
	self.da = self.da + self.engineRudder*self.engine*time*self:stat("engineRudderPower")* self:stat("enginePower") * math.abs(math.sin(self.engineRudder))

	local engineAngle = self.angle + self.engineRudder
	self.dx = self.dx + math.cos(engineAngle)*self.engine*time* self:stat("enginePower")
	self.dy = self.dy + math.sin(engineAngle)*self.engine*time* self:stat("enginePower")
	

	local speedAngle = math.angleBetweenPoints(0,0,self.dx, self.dy)
	local speed = math.distance(0,0,self.dx, self.dy)
	local speedEffect = self:getSpeedFactor()
	local rudderEffectFactor = math.cos(math.angleDifference(self.angle + self.rudder*flatFactor, speedAngle))*speedEffect
	
	--anglespeed control
	local airFriction = self.map:getAtmosphere(self.x, self.y)
	local frictionDelta = self.lastFriction and (airFriction - self.lastFriction) or 0
	self.lastFriction = airFriction

	self.da = self.da + self.rudder*rudderEffectFactor*time*self:stat("rudderFlowSize")*airFriction

	if frictionDelta > 0 then
		self.dy = self.dy - frictionDelta*10*speedEffect*time*math.pow(math.abs(math.cos(speedAngle)),2)
		self.heat = frictionDelta*5500
		self:hurt(time*self.heat)
	else
		self.heat = 0
	end

	self:applyHail(time)
	local rudderdiff = math.difference(0,self.rudder)
	self.rudder = math.approach(self.rudder, 0, rudderdiff*rudderEffectFactor*time*self:stat("rudderFlowSize")*airFriction*self:stat("rudderNullify"))


	self.dr = math.approach(self.dr, math.angleDifference(self.desiredRoll,self.roll), time*self:stat("rollResponsiveness")*airFriction*speedEffect)

	--carving
	local speedAngle = math.angleBetweenPoints(0,0,self.dx, self.dy)
	local speed = math.distance(0,0,self.dx, self.dy)
	local speedEffect = self:getSpeedFactor()

	local speedAngleDiff = math.abs(math.cos(math.clamp(math.angleDifference(self.angle, speedAngle), -math.pi*0.5, math.pi*0.5)))

	local carveFactor = flatAmount*math.pow(speedAngleDiff,self:stat("carvingExp"))*speedEffect

	local speedf = 1-(self:stat("speedThresholdDamping")/(self:stat("speedThresholdDamping")+speed))
	local cf = self:stat("maxCarvingLoss") * speedf + (1-self:stat("maxCarvingLoss"))

	local diff = math.abs(math.angleDifference(speedAngle, self.angle))/math.pi*2
	
	local newSpeedAngle = math.approachAngle(speedAngle, self.angle, cf*diff*time*carveFactor*self:stat("bodyCarving")*airFriction)
	
	local carvedAmount = math.abs(math.angleDifference(speedAngle, newSpeedAngle))

	

	local carvedAmountFract = math.min(1,carvedAmount/math.pi/0.5)
	local speed = speed*self:stat("carvingSpeedDamping")*carvedAmountFract + (1-carvedAmountFract)*speed
	self.dx = math.cos(newSpeedAngle)*speed
	self.dy = math.sin(newSpeedAngle)*speed

	
	
	
	local frictionAmount = (1-self:stat("aerodynamicness"))*airFriction + flatAmount*self:stat("aerodynamicness")*math.pow(speedAngleDiff, self:stat("aerodynamicExp"))*airFriction

	--friction
	self.da = math.approach(self.da, math.angleDifference(speedAngle, self.angle), frictionAmount*flatAmount*diff*time*speed*self:stat("angleAdjustment"))


	self.da = math.approach(self.da, 0, speedf*math.abs(self.da)*carvedAmount*self:stat("carvingAngleDamping"))

	--local speed = math.distance(0,0,self.dx, self.dy)
	self.dx = math.approach(self.dx, 0, math.pow(self.dx*self.map:getFriction(),2)*frictionAmount*time*self:stat("airFriction"))
	self.dy = math.approach(self.dy, 0, math.pow(self.dy*self.map:getFriction(),2)*frictionAmount*time*self:stat("airFriction"))
	local daDiff = math.difference(self.da, 0)
	self.da = math.approach(self.da, 0, math.pow(self.da*self.map:getFriction(),2)*frictionAmount*flatFactor*time*self:stat("angleFriction") + flatFactor*daDiff*frictionAmount*time*self:stat("angleForwardFriction")/100)

	--gravity
	self.dy = self.dy + time*self.map:getGravity(self.x, self.y)

	if self.delay and self.delay > 0 then
		self.using = true
		self.delay = self.delay - time
		if self.delay <= 0 then

		end
	else
		local weap = nil
		if self.alt then
			weap = self:def().weapons[2]
		elseif self.action then
			weap = self:def().weapons[1]
		end
		if weap then
			if self.energy > weap.energy then
				if weap.projectile then
					local proj = self.map.sim:create(Projectile, self.map, self, self.x, self.y, self.angle, self.dx + math.cos(self.angle)*weap.launchSpeed, self.dy + math.sin(self.angle)*weap.launchSpeed, 0, weap.projectile)
				end
				if weap.missile then
					local miss = self.map.sim:create(Missile, self.map, self, self.x, self.y, self.angle, self.dx + math.cos(self.angle + weap.launchAngle*flatFactor)*weap.launchSpeed, self.dy + math.sin(self.angle+ weap.launchAngle*flatFactor)*weap.launchSpeed, 0, weap.missile)
				end
				self.delay = weap.delay
				self.energy = self.energy - weap.energy
			end
		end
	end

	if not using then
		self.energy = math.approach(self.energy, self:stat("energy") * self:getHealthPercent(), time*self:stat("recharge"))
	end

	

	self.ground = false

	if #self.resources > 0 then
		local lastRes = self
		for index, res in ipairs(self.resources) do
			if not res.suckInto then
				local ang = lastRes:getAngleTo(res)

				local targetX = lastRes.x + math.cos(ang)*self:stat("resourceSpacing")
				local targetY = lastRes.y + math.sin(ang)*self:stat("resourceSpacing")

				local relX = res:getRelativeXPosition(targetX)
				local relY = res:getRelativeYPosition(targetY)

				local distTo = math.distance(0,0,relX,relY)
				local angleTo = math.angleBetweenPoints(0,0,relX,relY)

				local pull = 1-(self:stat("resourceRubberDist") / (self:stat("resourceRubberDist") + distTo))
				res:addSpeed(angleTo, self:stat("resourceForce") *pull)

				res.dx = math.approach(res.dx, 0, math.difference(res.dx, 0)*self:stat("resourceFrictionAmount")*time*index)
				res.dy = math.approach(res.dy, 0, math.difference(res.dy, 0)*self:stat("resourceFrictionAmount")*time*index)

				res.dx = math.approach(res.dx, self.dx, math.difference(res.dx, self.dx)*self:stat("resourceMatchAmount")*time)
				res.dy = math.approach(res.dy, self.dy, math.difference(res.dy, self.dy)*self:stat("resourceMatchAmount")*time)

				lastRes = res
			end
		end
	end

	self:super().step(self, time)
	if self.health > 0 then
		self.health = math.approach(self.health, self:stat("health"), time*self:stat("healthRegain"))
	end
end

local warnings = {
	pullUp = {
		{sound = "indicatorWarningA", volume = 0.1, delay = 0.25},
		{sound = "pullUp", volume = 0.5, delay = 1},
	},
	criticalHealth = {
		{sound = "indicatorWarningB", volume = 0.1, delay = 0.75},
		{sound = "criticalDamage", volume = 0.5, delay = 2},
	},
	incoming = {
		{sound = "indicatorWarningC", volume = 0.2, delay = 0.7},
	}
}

function SpaceShip:initProxy()
	self:createTrail()
	self.warnings = {}
	for index, warning in pairs(warnings) do
		self.warnings[index] = {}
		for ind, val in pairs(warning) do
			self.warnings[index][ind] = 0
		end
	end
end

function SpaceShip:updateHudSounds(time)
	local groundY = self.map:getYAt(self.x, self.y)
	local diff = groundY-self.y

	local ang = math.abs(math.angleDifference(self.angle, math.pi*0.5))
	self.pullUp = diff < self.dy and ang < math.pi*0.5

	self.criticalHealth = self:getHealthPercent() < 0.5
	
	self.incoming = false
	for index, ent in pairs(self.map.ents) do
		if ent:isHoming() and ent:getOwner() ~= self:getOwner() then
			local distTo = ent:getDistanceTo(self)
			if distTo < ent:stat("targetDistance") then
				local angTo = ent:getAngleTo(self)
				local angDiff = math.abs(math.angleDifference(angTo, ent.angle))
				if angDiff < ent:stat("targetArc")*0.5 then
					self.incoming = true
				end
			end
		end
	end

	for index, warning in pairs(warnings) do
		if self[index] then
			for ind, def in pairs(warning) do
				if self.warnings[index][ind] <= 0 then
					sound.playSound(def.sound, def.volume or 1, 0, 1)
					self.warnings[index][ind] = def.delay
				else
					self.warnings[index][ind] = self.warnings[index][ind] - time
				end
			end
		end
	end


end

function SpaceShip:getEnergyPercent(interp)
	if interp then
		return self:interp("energy", interp)/self:stat("energy")
	else
		return self.energy/self:stat("energy")
	end
end

function SpaceShip:getHealthPercent(interp)
	if interp then
		return self:interp("health", interp)/self:stat("health")
	else
		return self.health/self:stat("health")
	end
end

function SpaceShip:getHeatPercent(interp)
	if interp then
		return math.min(1,self:interp("heat", interp)/self:stat("recharge")/5)
	else
		return math.min(1,self.heat/self:stat("recharge")/5)
	end
end



function SpaceShip:getTrailOffset()
	return math.cos(self.angle + math.pi)*self:def().trailDist,  math.sin(self.angle + math.pi)*self:def().trailDist
end

function SpaceShip:fakeStep(time)
	self.time = self.time + time
	self:updateTrail(time, 3)
	local groundY = self.map:getYAt(self.x- self.dx/10)
	local prox = (groundY-self.y)
	local dustRange = 400

	if prox < dustRange then
		local amt = (dustRange-prox)/dustRange
		local groundY = self.map:getYAt(self.x- self.dx/10*(1-amt))
		if math.random() < amt *time*100 then
			local speed = math.abs(self.dx)
			local fx = Fx:new(FX_TYPES.smoke, self.map, self.x+math.randomGaussian()*15 - self.dx/10*(1-amt), groundY-math.random()*15, -self.dx*0.2*amt+math.randomGaussian()*0.1*self.dx*amt, -speed*0.3*amt + math.randomGaussian()*speed*0.1*amt)
		end
	end

	if self.engine > 0 then
		if self.engine > 1 then
			local amt = math.sqrt(self.engine-1)
			self:emitLoop("afterburner", amt, 0.9 + 0.2*amt)
		else
			local amt =self.engine
			self:emitLoop("engine", amt, 0.9 + 0.2*amt)
		end
	end
	if self.heat > 0 then
		local amt = self:getHeatPercent()
		self:emitLoop("athmosphere-reentry", amt, 0.9 + 0.2*amt)
	end

	if self:getHealthPercent() < 0.5 then
		if math.random() < time*(10-self:getHealthPercent()*10) then
			local fx = Fx:new(FX_TYPES.smoke, self.map, self.x+math.randomGaussian()*15, self.y+math.randomGaussian()*15, self.dx*0.5, -self.dy*0.5)
			local fx = Fx:new(FX_TYPES.explosion, self.map, self.x+math.randomGaussian()*15, self.y+math.randomGaussian()*15, -self.dx*0.5, -self.dy*0.5)
		end
	end
end

function SpaceShip:renderRolledAt(pieceName, x,y,scale,angle,a,r,g,b, interp)
--print(self:def().sprites[spriteName.."Top"])
	local offsets = self:def().pieces[pieceName]

	local rudder = self:interp("rudder", interp)
	local roll = self:interp("roll", interp)
	local rollY = math.cos(roll)
	local rollZ = math.cos(roll-math.pi*0.5)
	local rollFlip = math.sign(math.cos(roll)) >= 0

	local x = x + math.cos(angle) * offsets.x * scale + math.cos(angle+math.pi*0.5) * offsets.y * scale * rollY + math.cos(angle+math.pi*0.5) * offsets.z * scale * rollZ --* (rollFlip and -1 or 1)
	local y = y + math.sin(angle) * offsets.x * scale + math.sin(angle+math.pi*0.5) * offsets.y * scale * rollY + math.sin(angle+math.pi*0.5) * offsets.z * scale * rollZ --* (rollFlip and -1 or 1)
	
	local angle = angle + (offsets.angle or 0)
	local flipped = true
	local topFlipped = offsets.flip or false

	local sideAngle = angle
	if offsets.rudder then
		sideAngle = sideAngle  + rudder * rollY * offsets.rudder
	end

	local sprite = offsets.sprite
	
	if sprite then
		if math.abs(rollY) > 0 then
			video.renderScaledSpriteState(self:def().sprites[sprite.."Side"], x, y, 1, rollY, scale, sideAngle, a, r, g, b, flipped)
		end

		if math.abs(rollZ) > 0 then
			video.renderScaledSpriteState(self:def().sprites[sprite.."Top"], x, y, 1, rollZ, scale, angle, a, r, g, b, topFlipped)
		end
	end
	if offsets.flame then
		local engine = self:interp("engine", interp)
		local def = nil
		local amt = nil
		if engine > 1 then
			amt = math.sqrt(engine-1)*2
			def = FX_TYPES[FX_TYPES.afterburn]
		else
			amt = engine
			def = FX_TYPES[FX_TYPES.engine]
		end
		for index, spr in pairs(def.sprites) do
			local scale = scale*amt + math.safeGaussian()*scale*amt*0.5
			video.renderSpriteState(spr,x,y,scale,sideAngle, a, 255, 255, 255, flipped)
		end
	end
end

function SpaceShip:renderAt(x,y,scale,angle,a,r,g,b,interp)

	--video.renderSpriteLine(x, y, x+ math.cos(angle - self.rudder*math.cos(self.roll) + math.pi)*150*scale, y+ math.sin(angle- self.rudder*math.cos(self.roll) + math.pi)*150*scale, 255, 0, 255, 255)
--
--
	--video.renderSpriteLine(x, y, x+ math.cos(angle + math.pi)*150*scale*self.engine, y+ math.sin(angle + math.pi)*150*scale*self.engine, 128, 255, 0, 255)
	--video.renderSpriteLine(x, y, x+ math.cos(angle - math.pi*0.5)*150*scale*math.cos(self.roll), y+ math.sin(angle - math.pi*0.5)*150*scale*math.cos(self.roll), 128, 255, 255, 0)
	--
	--if self.engine > 1 then
	--	video.renderSpriteLine(x, y, x+ math.cos(angle + math.pi)*250*scale*self.engine, y+ math.sin(angle + math.pi)*250*scale*self.engine, 255, 255, 128, 0, nil, 10)
	--end

	local angle = angle + self:interp("angle", interp)

	if self.trail then
		self:renderTrailAt(x,y,scale,angle,a,r,g,b,32, interp)
	end
	
	if self.heat > 0 then
		local h = self:getHeatPercent()
		g = g - g*h*0.4
		b = b - b*h*0.9
	end

	local order = math.normalize(math.round(math.normalizeAngle(self:interp("roll", interp))/math.pi*2)+1, 1, 4) 
	for index, ren in pairs(self:def().renderOrders[order]) do
		self:renderRolledAt(ren, x,y,scale,angle,a,r,g,b, interp)
	end

	if self.heat > 0 then
		local h = self:getHeatPercent()
		local engine = self:interp("engine", interp)
		local amt = h
		local def = FX_TYPES[FX_TYPES.heat]
		for index, spr in pairs(def.sprites) do
			local scale = scale*(0.8 + 0.2*amt) + math.safeGaussian()*scale*amt*0.2
			local alpha = (a * 0.8 + 0.2*math.safeGaussian())*amt
			video.renderSpriteState(spr,x ,y ,scale,angle-math.pi*0.5, alpha, 255, 255, 255, flipped)
		end
	end
end

function SpaceShip:renderHudAt(x,y,scale,angle,a,r,g,b,interp)
	local gs = states.get("game")

	local lplayer = gs.sync:getLocalPlayer()

	for index, player in pairs(gs.sync.players) do
		if player.pilot and player.pilot.ship and player.pilot.ship.eid == self.eid and player.team then
			if lplayer ~= player then
				local col = player.team:def().color
				video.renderTextSprites(player.nick, x, y + 200*scale, 1, "small", 64, col.r,col.g,col.b)
			end
		end
	end

	
	if lplayer.pilot and lplayer.pilot.ship then
		if gs.fake:hasEid(lplayer.pilot.ship.eid) then
			local ship = gs.fake:getProxy(lplayer.pilot.ship)
			if ship and ship ~= self then
				local x = math.clamp(x, canvas.w*0.05, canvas.w*0.95)
				local y = math.clamp(y, canvas.h*0.05, canvas.h*0.95)
				if ship:getOwner() == self:getOwner() then
					video.renderSpriteState(SPRITES.hud_ally, x,y, 0.5, 0, a,r*0.5,g*0.5,b)
				else
					video.renderSpriteState(SPRITES.hud_enemy, x,y, 0.5, 0, a,r,g*0.5,b*0.5)
				end
			end
		end
	end
end


function SpaceShip:hasTargetHud()
	return true
end


function SpaceShip:renderHudBars(x,y, a, r, g, b, interp)
	local width = canvas.w*0.6
	video.renderSpriteLine(x -width*0.5*self:getHealthPercent(interp), y,    x+width*0.5*self:getHealthPercent(interp),  y,    a, r, g, b, nil, nil, 5)
	video.renderSpriteLine(x -width*0.5*self:getEnergyPercent(interp), y+15, x+width*0.5*self:getEnergyPercent(interp),  y+15, a, r, g, b, nil, nil,  20)
	video.renderSpriteLine(x -width*0.5*self:getHeatPercent(interp), y+30, x+width*0.5*self:getHeatPercent(interp),  y+30, 255- 255*0.95*math.abs(math.cos(self.time*10*math.pi)), 255, 64, 0, nil, nil,  5)
end

function SpaceShip:renderHudAltitude(x,y,a,r,g,b, interp)
	local he = canvas.h

	local ey = self:interp("y", interp)
	for i=1, 20 do
		local thin = (math.ceil(ey/self.map.height + i)%2) == 0 and SPRITES.hud_altometerSmall or SPRITES.hud_altometerBig
		local hy = math.normalize(-ey *0.25 + i/20*he, 0, he)
		video.renderSpriteState(thin, x, y - he*0.5 + hy, 0.5, 0, a, r, g, b)
	end
end
	
