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,
|
|
} |