218 lines
5.2 KiB
Lua
218 lines
5.2 KiB
Lua
--[[
|
|
|
|
Airshuttle
|
|
==========
|
|
|
|
Copyright (C) 2018 Joachim Stolberg
|
|
|
|
See LICENSE.txt for more information
|
|
|
|
]]--
|
|
|
|
local P = minetest.pos_to_string
|
|
local F = function(val) return string.format("%2.1f", val) end
|
|
local DBG = function(...) end
|
|
--local DBG = print
|
|
|
|
local MissingPlayer = {}
|
|
|
|
local MAX_DISTANCE = 5000 -- per hop
|
|
local TELEPORT_DIST = 200
|
|
|
|
-- Speed horizontal [m/s]
|
|
local SH_MAX = 8
|
|
local SH_CHECKP = 2.5
|
|
local SH_MIN = 0.1
|
|
local SH_STEP = 0.5
|
|
|
|
-- Speed vertival [m/s]
|
|
local SV_MAX = 4
|
|
local SV_MIN = 0.1
|
|
local SV_STEP = 0.2
|
|
|
|
-- Rotation steps [radiant]
|
|
local ROT_STEP = 0.2
|
|
|
|
-- decrease value if shuttle does not hit the target
|
|
local MAGIC_FACTOR = 0.18
|
|
|
|
local function get_sign(i)
|
|
if i == 0 then
|
|
return 0
|
|
else
|
|
return i / math.abs(i)
|
|
end
|
|
end
|
|
|
|
local function yaw_offset(old, new)
|
|
local d = (new - old) % (2*math.pi)
|
|
if d > math.pi then
|
|
return d - (2*math.pi)
|
|
end
|
|
return d
|
|
end
|
|
|
|
|
|
local function dest_position_reached(self, distH, distV)
|
|
DBG(self.wp_number, self.dest_approach, F(distH), F(distV), F(self.speedH))
|
|
if self.dest_approach then
|
|
return distH < 0.2 and math.abs(distV) < 0.1
|
|
else
|
|
return distH < 3 and math.abs(distV) < 2
|
|
end
|
|
end
|
|
|
|
-- Calculation of distH, distV and yaw_offs based on predefined route
|
|
function airshuttle.remote_control(self)
|
|
local pos = self.object:get_pos()
|
|
if self.dest_pos == nil then
|
|
self.dest_approach = false
|
|
self.start_pos = pos
|
|
self.wp_number = nil
|
|
self.wp_number, self.dest_pos, self.sh_max =
|
|
airshuttle.get_next_waypoint(self.owner, self.route_id, self.wp_number)
|
|
if not self.wp_number then return false end
|
|
end
|
|
-- calculate yaw and distance H/V
|
|
local distH = vector.distance(pos, self.dest_pos)
|
|
local distV = self.dest_pos.y - pos.y
|
|
local dir = vector.subtract(self.dest_pos, pos)
|
|
local new_yaw = minetest.dir_to_yaw(dir)
|
|
local yaw_offs = yaw_offset(self.object:get_yaw(), new_yaw)
|
|
|
|
-- teleport distance?
|
|
if distH > TELEPORT_DIST then
|
|
self.object:set_pos(self.dest_pos)
|
|
distH = 0
|
|
distV = 0
|
|
end
|
|
|
|
-- waypoint missed due lag?
|
|
-- if self.oldDist and distH > self.oldDist then
|
|
-- self.object:set_pos(self.dest_pos)
|
|
-- end
|
|
-- self.oldDist = distH
|
|
|
|
-- horizontal speed
|
|
if self.dest_approach then
|
|
if distH < 0.1 then
|
|
self.speedH = 0
|
|
else
|
|
self.speedH = math.min(self.sh_max, distH + SH_MIN, self.speedH + SH_STEP)
|
|
end
|
|
else -- normal check point
|
|
self.speedH = math.min(self.sh_max, distH + SH_CHECKP, self.speedH + SH_STEP)
|
|
end
|
|
|
|
-- vertical speed
|
|
if distV > 0 then
|
|
self.speedV = math.min(SV_MAX, distV/4 + SV_MIN, self.speedV + SV_STEP)
|
|
elseif distV < 0 then
|
|
self.speedV = math.max(-SV_MAX, distV/4 - SV_MIN, self.speedV - SV_STEP)
|
|
end
|
|
if math.abs(distV) < 0.1 then self.speedV = 0 end
|
|
|
|
if self.speedH == 0 and yaw_offs == 0 and self.speedV == 0 then
|
|
return false
|
|
end
|
|
|
|
-- H/V speed ratio correction
|
|
if self.speedH > 0 and math.abs(self.speedV) > 0 then
|
|
local ratio = distH / math.abs(distV)
|
|
local maxV = (self.speedH / ratio) * 1
|
|
local maxH = (math.abs(self.speedV) * ratio) * 1
|
|
|
|
if maxV < math.abs(self.speedV) then
|
|
DBG("correction1")
|
|
self.speedV = maxV * get_sign(self.speedV)
|
|
elseif maxH < self.speedH then
|
|
DBG("correction2")
|
|
self.speedH = maxH
|
|
end
|
|
end
|
|
|
|
-- speed/rotation correction
|
|
local max_speed = distH * MAGIC_FACTOR / math.abs(yaw_offs)
|
|
if self.speedH > max_speed then
|
|
self.speedH = max_speed
|
|
end
|
|
|
|
-- yaw_offs limitation
|
|
self.yaw_offs = math.min(math.abs(yaw_offs), ROT_STEP) * get_sign(yaw_offs)
|
|
|
|
if dest_position_reached(self, distH, distV) then
|
|
self.oldDist = nil
|
|
self.wp_number, self.dest_pos, self.sh_max =
|
|
airshuttle.get_next_waypoint(self.owner, self.route_id, self.wp_number)
|
|
if self.wp_number and not self.dest_approach then
|
|
DBG("checkpoint hit")
|
|
elseif not self.dest_approach then -- destination approach
|
|
self.dest_pos = table.copy(self.start_pos)
|
|
self.sh_max = 3
|
|
self.dest_approach = true
|
|
DBG("destination approach")
|
|
else
|
|
self.on_trip = false
|
|
self.object:set_velocity({x = 0, y = 0, z = 0})
|
|
self.object:set_pos(self.object:get_pos())
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
function airshuttle.player_gone(player_name)
|
|
if not player_name then
|
|
return true
|
|
end
|
|
|
|
local player = minetest.get_player_by_name(player_name)
|
|
if not player then
|
|
return true
|
|
end
|
|
|
|
if MissingPlayer[player_name] then
|
|
MissingPlayer[player_name] = nil
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Place the player back to the start point
|
|
local function reset_player(player)
|
|
if player then
|
|
local spos = player:get_attribute("airshuttle_start_pos")
|
|
if spos then
|
|
local pos = minetest.string_to_pos(spos)
|
|
if pos then
|
|
player:set_detach()
|
|
default.player_attached[player:get_player_name()] = false
|
|
default.player_set_animation(player, "stand" , 30)
|
|
minetest.after(0.1, function()
|
|
player:set_pos(pos)
|
|
end)
|
|
end
|
|
player:set_attribute("airshuttle_start_pos", nil)
|
|
DBG("player reset")
|
|
end
|
|
end
|
|
end
|
|
|
|
-- fly is canceled
|
|
minetest.register_on_joinplayer(function(player)
|
|
reset_player(player)
|
|
end)
|
|
|
|
minetest.register_on_leaveplayer(function(player)
|
|
MissingPlayer[player:get_player_name()] = true
|
|
end)
|
|
|
|
minetest.register_on_dieplayer(function(player)
|
|
MissingPlayer[player:get_player_name()] = true
|
|
reset_player(player)
|
|
end)
|
|
|
|
|
|
|
|
|