389 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			389 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
| function getHeliControllerIndexByOwner(p)
 | |
| 	if global.heliControllers then
 | |
| 		for i, curController in ipairs(global.heliControllers) do
 | |
| 			if curController.owner == p then
 | |
| 				return i
 | |
| 			end
 | |
| 		end
 | |
| 	end
 | |
| end
 | |
| 
 | |
| function getHeliControllerByOwner(p)
 | |
| 	local i = getHeliControllerIndexByowner(p)
 | |
| 	if i then return global.heliControllers[i] end
 | |
| end
 | |
| 
 | |
| function getHeliControllerIndexByHeli(heli)
 | |
| 	if global.heliControllers then
 | |
| 		for i, curController in ipairs(global.heliControllers) do
 | |
| 			if curController.heli == heli then
 | |
| 				return i
 | |
| 			end
 | |
| 		end
 | |
| 	end
 | |
| end
 | |
| 
 | |
| function getHeliControllerByHeli(heli)
 | |
| 	local i = getHeliControllerIndexByHeli(heli)
 | |
| 	if i then return global.heliControllers[i] end
 | |
| end
 | |
| 
 | |
| function assignHeliController(owner, heli, target, targetIsPlayer)
 | |
| 	local oldControllerIndex = searchIndexInTable(global.heliControllers, heli, "heli")
 | |
| 	
 | |
| 	if oldControllerIndex then
 | |
| 		global.heliControllers[oldControllerIndex]:destroy()
 | |
| 		table.remove(global.heliControllers, oldControllerIndex)
 | |
| 	end
 | |
| 
 | |
| 	insertInGlobal("heliControllers", heliController.new(owner, heli, target, targetIsPlayer))
 | |
| end
 | |
| 
 | |
| heliControllerState = {
 | |
| 	getUp = 1,
 | |
| 	orientToTarget = 2,
 | |
| 	moveToTarget = 3,
 | |
| 	creepToPosition = 4,
 | |
| 	land = 5,
 | |
| 	stop = 6,
 | |
| }
 | |
| 
 | |
| heliController = 
 | |
| {
 | |
| 	new = function(player, heli, target, targetIsPlayer)
 | |
| 		local obj = 
 | |
| 		{
 | |
| 			valid = true,
 | |
| 
 | |
| 			owner = player,
 | |
| 			heli = heli,
 | |
| 			targetIsPlayer = targetIsPlayer,
 | |
| 
 | |
| 			curStateId = heliControllerState.getUp,
 | |
| 			stateChanged = true,
 | |
| 		}
 | |
| 
 | |
| 		if targetIsPlayer then
 | |
| 			obj.targetPlayer = target
 | |
| 			obj.targetPos = target.position
 | |
| 		else
 | |
| 			obj.targetPos = target
 | |
| 		end
 | |
| 
 | |
| 		if not heli.baseEnt.get_driver() then
 | |
| 			obj.driverIsBot = true
 | |
| 			obj.driver = heli.surface.create_entity{name = "character", force = player.force, position = player.position}
 | |
| 			heli.baseEnt.set_driver(obj.driver)
 | |
| 		else
 | |
| 			obj.driverIsBot = false
 | |
| 			obj.driver = heli.baseEnt.get_driver()
 | |
| 		end
 | |
| 
 | |
| 		heli.hasRemoteController = true
 | |
| 		heli.remoteController = obj
 | |
| 
 | |
| 		setmetatable(obj, {__index = heliController})
 | |
| 
 | |
| 		OnHeliControllerCreated(obj)
 | |
| 		return obj
 | |
| 	end,
 | |
| 
 | |
| 	curState = function(self)
 | |
| 		return heliControllerStates[self.curStateId](self)
 | |
| 	end,
 | |
| 
 | |
| 	destroy = function(self)
 | |
| 		self.valid = false
 | |
| 		self.heli.hasRemoteController = nil
 | |
| 		self.heli.remoteController = nil
 | |
| 
 | |
| 		if self.driverIsBot and self.driver and self.driver.valid then
 | |
| 			self.driver.destroy()
 | |
| 		end
 | |
| 
 | |
| 		OnHeliControllerDestroyed(self)
 | |
| 	end,
 | |
| 
 | |
| 	stopAndDestroy = function(self)
 | |
| 		if self.driverIsBot then
 | |
|             -- Told to explicitly stop, make sure to clear the flag
 | |
|             self.targetIsPlayer = false
 | |
| 			self:changeState(heliControllerState.stop)
 | |
| 		else
 | |
| 			self:destroy()
 | |
| 		end
 | |
| 	end,
 | |
| 
 | |
| 	OnTick = function(self)		
 | |
| 		if not (self.heli.valid and self.heli.baseEnt.valid and self.owner.valid) then
 | |
| 			self:destroy()
 | |
| 			return
 | |
| 		end
 | |
| 
 | |
| 		local curDriver = self.heli.baseEnt.get_driver()
 | |
| 		local curPassenger = self.heli.baseEnt.get_passenger()
 | |
| 
 | |
| 		if not (curDriver and curDriver.valid) then
 | |
| 			if self.driverIsBot then
 | |
| 				self:destroy()
 | |
| 			else
 | |
| 				self.driverIsBot = true
 | |
| 				self.driver = self.heli.surface.create_entity{name = "character", force = self.owner.force, position = self.owner.position}
 | |
| 				self.heli.baseEnt.set_driver(self.driver)
 | |
| 				self.heli:OnUp()
 | |
| 			end
 | |
| 
 | |
| 		elseif self.driverIsBot and curDriver ~= self.driver then
 | |
| 			if self.driver and self.driver.valid then
 | |
| 				self.driver.destroy()
 | |
| 				self.driver = curDriver
 | |
| 				self.driverIsBot = false
 | |
| 			end
 | |
| 			if self.targetIsPlayer and curDriver == self.targetPlayer.character then
 | |
| 				self:destroy()
 | |
| 				return
 | |
| 			end
 | |
| 
 | |
| 		elseif self.driverIsBot and curPassenger and curPassenger.valid then
 | |
| 			if self.driver and self.driver.valid then
 | |
| 				self.driver.destroy()
 | |
| 				self.driver = curPassenger
 | |
| 				self.driverIsBot = false
 | |
| 			end
 | |
| 			self.heli.baseEnt.set_driver(curPassenger)
 | |
| 			
 | |
| 			if self.targetIsPlayer and curPassenger == self.targetPlayer.character then
 | |
| 				self:destroy()
 | |
| 				return
 | |
| 			end
 | |
| 
 | |
| 		else
 | |
| 			if self.targetIsPlayer then
 | |
| 				if self.targetPlayer.valid then
 | |
| 					self.targetPos = {x = self.targetPlayer.position.x, y = self.targetPlayer.position.y - 1}
 | |
| 				else
 | |
|                     -- Invalid player target, clear the flag
 | |
|                     self.targetIsPlayer = false
 | |
| 					self:stopAndDestroy()
 | |
| 					return
 | |
| 				end
 | |
| 			end
 | |
| 
 | |
| 			local old = self.curStateId
 | |
| 			self:curState()
 | |
| 
 | |
| 			if old == self.curStateId then
 | |
| 				self.stateChanged = false
 | |
| 			else
 | |
| 				self.stateChanged = true
 | |
| 			end
 | |
| 		end
 | |
| 	end,
 | |
| 
 | |
| 	changeState = function(self, newState)
 | |
| 		self.curStateId = newState
 | |
| 	end,
 | |
| 
 | |
| 	setRidingState = function(self, acc, dir)
 | |
| 		if not acc then
 | |
| 			acc = self.driver.riding_state.acceleration
 | |
| 		end
 | |
| 
 | |
| 		if not dir then
 | |
| 			dir = self.driver.riding_state.direction
 | |
| 		end
 | |
| 
 | |
| 		self.driver.riding_state = {acceleration = acc, direction = dir}
 | |
| 	end,
 | |
| 
 | |
| 	holdSpeed = function(self, speed)
 | |
| 		local dir = self.driver.riding_state.direction
 | |
| 
 | |
| 		if math.abs(1 - (self.heli.baseEnt.speed / speed)) < 0.05 then
 | |
| 			self.heli.baseEnt.speed = speed
 | |
| 		else
 | |
| 
 | |
| 			if self.heli.baseEnt.speed > speed then
 | |
| 				self:setRidingState(defines.riding.acceleration.braking)
 | |
| 			else
 | |
| 				self:setRidingState(defines.riding.acceleration.accelerating)
 | |
| 			end
 | |
| 		end
 | |
| 	end,
 | |
| 
 | |
| 	getTargetOrientation = function(self)
 | |
| 		local curPos = self.heli.childs.bodyEntShadow.position
 | |
| 
 | |
| 		local vec = {x = self.targetPos.x - curPos.x, y = curPos.y - self.targetPos.y}
 | |
| 		local len = math.sqrt(vec.x ^ 2 + vec.y ^ 2)
 | |
| 
 | |
| 		vec.x = vec.x / len
 | |
| 		vec.y = vec.y / len
 | |
| 
 | |
| 
 | |
| 		local angle = math.atan2(vec.y, vec.x)
 | |
| 		self.targetOrient = 1.25 - (angle / (2 * math.pi))
 | |
| 		if self.targetOrient > 1 then self.targetOrient = self.targetOrient - 1 end
 | |
| 	end,
 | |
| 
 | |
| 	getSteeringToTargetOrientation = function(self)
 | |
| 		local curOrient = self.heli.baseEnt.orientation
 | |
| 
 | |
| 		if math.abs(self.targetOrient - curOrient) < 0.02 then
 | |
| 			return defines.riding.direction.straight
 | |
| 		else
 | |
| 			local deltaLeft = 0
 | |
| 			local deltaRight = 0
 | |
| 
 | |
| 			if self.targetOrient < curOrient then
 | |
| 				deltaLeft = curOrient - self.targetOrient
 | |
| 				deltaRight = 1 - curOrient + self.targetOrient
 | |
| 			else
 | |
| 				deltaLeft = curOrient + 1 - self.targetOrient
 | |
| 				deltaRight = self.targetOrient - curOrient
 | |
| 			end
 | |
| 
 | |
| 			if deltaLeft < deltaRight then
 | |
| 				return defines.riding.direction.left
 | |
| 			else
 | |
| 				return defines.riding.direction.right
 | |
| 			end
 | |
| 		end
 | |
| 	end,
 | |
| 
 | |
| 	------------- states ---------------
 | |
| 	getUp = function(self)
 | |
| 		if self.stateChanged then
 | |
| 			self.heli:OnUp()
 | |
| 		elseif self.heli.height >= maxCollisionHeight then
 | |
| 			self:changeState(heliControllerState.orientToTarget)
 | |
| 		end
 | |
| 	end,
 | |
| 
 | |
| 	orientToTarget = function(self)
 | |
| 		if self.stateChanged then
 | |
| 			self.targetOrientation = self:getTargetOrientation()
 | |
| 		else
 | |
| 			local dir = self:getSteeringToTargetOrientation()
 | |
| 
 | |
| 			self:setRidingState(defines.riding.acceleration.nothing, dir)
 | |
| 
 | |
| 			if dir == defines.riding.direction.straight then
 | |
| 				self:changeState(heliControllerState.moveToTarget)
 | |
| 			end
 | |
| 		end
 | |
| 	end,
 | |
| 
 | |
| 	moveToTarget = function(self)
 | |
| 		local dist = getDistance(self.heli.childs.bodyEntShadow.position, self.targetPos)
 | |
| 
 | |
| 		if self.stateChanged then
 | |
| 			self.updateOrientationCooldown = 30
 | |
| 			self.oldDist = -1
 | |
| 		end
 | |
| 
 | |
| 		self.updateOrientationCooldown = self.updateOrientationCooldown - 1
 | |
| 		if self.updateOrientationCooldown <  3 or dist < 10 then
 | |
| 			if self.updateOrientationCooldown == 0 then
 | |
| 				self.updateOrientationCooldown = 30
 | |
| 				if dist >= 10 then
 | |
| 					self:setRidingState(nil, defines.riding.direction.straight)
 | |
| 				end
 | |
| 			end
 | |
| 
 | |
| 			self.targetOrientation = self:getTargetOrientation()
 | |
| 			self:setRidingState(nil, self:getSteeringToTargetOrientation())
 | |
| 		else
 | |
| 			--self:setRidingState(nil, defines.riding.direction.straight)
 | |
| 		end
 | |
| 
 | |
| 		if dist < 150 then
 | |
| 			local creepZone = 0.5
 | |
| 			local landingZone = 1
 | |
| 			local desiredSpeed = 1.5 - (1.2247448 - (dist - creepZone) / 150)^2
 | |
| 
 | |
| 			self:holdSpeed(desiredSpeed)
 | |
| 
 | |
| 			if dist <= landingZone and (dist <= creepZone or dist == self.oldDist) then
 | |
| 				self:setRidingState(defines.riding.acceleration.braking)
 | |
| 				self:changeState(heliControllerState.creepToPosition)
 | |
| 			end
 | |
| 
 | |
| 			self.oldDist = dist
 | |
| 		else
 | |
| 			self:setRidingState(defines.riding.acceleration.accelerating)
 | |
| 		end
 | |
| 	end,
 | |
| 
 | |
| 	creepToPosition = function(self)
 | |
| 		local curPos = self.heli.childs.bodyEntShadow.position
 | |
| 		
 | |
| 		if self.stateChanged then
 | |
| 			self:setRidingState(defines.riding.acceleration.braking, defines.riding.direction.straight)
 | |
| 			self.heli.baseEnt.speed = 0
 | |
|             --targetIsPlayer will be updated else where
 | |
| 			--self.targetIsPlayer = false
 | |
| 
 | |
| 			local curDist = getDistance(curPos, self.targetPos)
 | |
| 			local alignFactor = 1.1
 | |
| 			local alignVec = {x = (self.targetPos.x - curPos.x) / curDist * alignFactor,  y = (self.targetPos.y - curPos.y) / curDist * alignFactor}
 | |
| 			self.targetPos = {x = self.targetPos.x + alignVec.x, y = self.targetPos.y + alignVec.y}
 | |
| 
 | |
| 			local creepFrames = 60
 | |
| 			self.creepVec = {x = (self.targetPos.x - curPos.x) / creepFrames, y = (self.targetPos.y - curPos.y) / creepFrames}
 | |
| 
 | |
| 			self.oldDist = 100
 | |
| 		end
 | |
| 
 | |
| 		self.heli.baseEnt.teleport({x = self.heli.baseEnt.position.x + self.creepVec.x, y = self.heli.baseEnt.position.y + self.creepVec.y})
 | |
| 		self.heli:updateEntityPositions()
 | |
| 
 | |
| 		local dist = getDistance(self.heli.childs.bodyEntShadow.position, self.targetPos)
 | |
| 		if dist < 0.2 or dist > self.oldDist then
 | |
|             -- If targeting the player and we shouldn't land when we catch up, then just stop, otherwise land
 | |
|             if( self.targetIsPlayer and self.targetPlayer.mod_settings["heli-remote-dont-land-following-player"].value )then
 | |
|                 self:changeState(heliControllerState.stop)
 | |
|             else
 | |
|                 self:changeState(heliControllerState.land)
 | |
|             end
 | |
| 		end
 | |
| 
 | |
| 		self.oldDist = dist
 | |
| 	end,
 | |
| 
 | |
| 	land = function(self)
 | |
| 		local d = extractPlayer(self.driver)
 | |
|         
 | |
|         if not ((not self.driverIsBot and d and d.valid) and d.mod_settings["heli-remote-dont-auto-land-player"].value) then
 | |
|             self.heli:OnDown()
 | |
|         end
 | |
| 
 | |
| 		self:destroy()
 | |
| 	end,
 | |
| 
 | |
| 	stop = function(self)
 | |
| 		self:setRidingState(defines.riding.acceleration.braking, defines.riding.direction.straight)
 | |
| 		
 | |
| 		if self.heli.baseEnt.speed == 0 then
 | |
|             if( self.targetIsPlayer and self.targetPlayer.mod_settings["heli-remote-dont-land-following-player"].value )then
 | |
|                 local dist = getDistance(self.heli.childs.bodyEntShadow.position, self.targetPos)
 | |
|                 if dist > 0.2 then
 | |
|                     -- Player moved off, orient on them
 | |
|                     self:changeState(heliControllerState.orientToTarget)
 | |
|                 end
 | |
|             else
 | |
|                 self:changeState(heliControllerState.land)
 | |
|             end
 | |
| 		end
 | |
| 	end,
 | |
| 	------------------------------------
 | |
| }
 | |
| 
 | |
| heliControllerStates = {
 | |
| 	heliController.getUp,
 | |
| 	heliController.orientToTarget,
 | |
| 	heliController.moveToTarget,
 | |
| 	heliController.creepToPosition,
 | |
| 	heliController.land,
 | |
| 	heliController.stop,
 | |
| } |