mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
2547 lines
75 KiB
Lua
2547 lines
75 KiB
Lua
--- Various routines
|
|
-- @module routines
|
|
-- @author Flightcontrol
|
|
|
|
Include.File( "Trace" )
|
|
--Include.File( "Message" )
|
|
|
|
|
|
env.setErrorMessageBoxEnabled(false)
|
|
|
|
--- Extract of MIST functions.
|
|
-- @author Grimes
|
|
|
|
routines = {}
|
|
|
|
|
|
-- don't change these
|
|
routines.majorVersion = 3
|
|
routines.minorVersion = 3
|
|
routines.build = 22
|
|
|
|
-----------------------------------------------------------------------------------------------------------------
|
|
|
|
----------------------------------------------------------------------------------------------
|
|
-- Utils- conversion, Lua utils, etc.
|
|
routines.utils = {}
|
|
|
|
|
|
|
|
routines.utils.toDegree = function(angle)
|
|
return angle*180/math.pi
|
|
end
|
|
|
|
routines.utils.toRadian = function(angle)
|
|
return angle*math.pi/180
|
|
end
|
|
|
|
routines.utils.metersToNM = function(meters)
|
|
return meters/1852
|
|
end
|
|
|
|
routines.utils.metersToFeet = function(meters)
|
|
return meters/0.3048
|
|
end
|
|
|
|
routines.utils.NMToMeters = function(NM)
|
|
return NM*1852
|
|
end
|
|
|
|
routines.utils.feetToMeters = function(feet)
|
|
return feet*0.3048
|
|
end
|
|
|
|
routines.utils.mpsToKnots = function(mps)
|
|
return mps*3600/1852
|
|
end
|
|
|
|
routines.utils.mpsToKmph = function(mps)
|
|
return mps*3.6
|
|
end
|
|
|
|
routines.utils.knotsToMps = function(knots)
|
|
return knots*1852/3600
|
|
end
|
|
|
|
routines.utils.kmphToMps = function(kmph)
|
|
return kmph/3.6
|
|
end
|
|
|
|
function routines.utils.makeVec2(Vec3)
|
|
if Vec3.z then
|
|
return {x = Vec3.x, y = Vec3.z}
|
|
else
|
|
return {x = Vec3.x, y = Vec3.y} -- it was actually already vec2.
|
|
end
|
|
end
|
|
|
|
function routines.utils.makeVec3(Vec2, y)
|
|
if not Vec2.z then
|
|
if not y then
|
|
y = 0
|
|
end
|
|
return {x = Vec2.x, y = y, z = Vec2.y}
|
|
else
|
|
return {x = Vec2.x, y = Vec2.y, z = Vec2.z} -- it was already Vec3, actually.
|
|
end
|
|
end
|
|
|
|
function routines.utils.makeVec3GL(Vec2, offset)
|
|
local adj = offset or 0
|
|
|
|
if not Vec2.z then
|
|
return {x = Vec2.x, y = (land.getHeight(Vec2) + adj), z = Vec2.y}
|
|
else
|
|
return {x = Vec2.x, y = (land.getHeight({x = Vec2.x, y = Vec2.z}) + adj), z = Vec2.z}
|
|
end
|
|
end
|
|
|
|
routines.utils.zoneToVec3 = function(zone)
|
|
local new = {}
|
|
if type(zone) == 'table' and zone.point then
|
|
new.x = zone.point.x
|
|
new.y = zone.point.y
|
|
new.z = zone.point.z
|
|
return new
|
|
elseif type(zone) == 'string' then
|
|
zone = trigger.misc.getZone(zone)
|
|
if zone then
|
|
new.x = zone.point.x
|
|
new.y = zone.point.y
|
|
new.z = zone.point.z
|
|
return new
|
|
end
|
|
end
|
|
end
|
|
|
|
-- gets heading-error corrected direction from point along vector vec.
|
|
function routines.utils.getDir(vec, point)
|
|
local dir = math.atan2(vec.z, vec.x)
|
|
dir = dir + routines.getNorthCorrection(point)
|
|
if dir < 0 then
|
|
dir = dir + 2*math.pi -- put dir in range of 0 to 2*pi
|
|
end
|
|
return dir
|
|
end
|
|
|
|
-- gets distance in meters between two points (2 dimensional)
|
|
function routines.utils.get2DDist(point1, point2)
|
|
point1 = routines.utils.makeVec3(point1)
|
|
point2 = routines.utils.makeVec3(point2)
|
|
return routines.vec.mag({x = point1.x - point2.x, y = 0, z = point1.z - point2.z})
|
|
end
|
|
|
|
-- gets distance in meters between two points (3 dimensional)
|
|
function routines.utils.get3DDist(point1, point2)
|
|
return routines.vec.mag({x = point1.x - point2.x, y = point1.y - point2.y, z = point1.z - point2.z})
|
|
end
|
|
|
|
|
|
--from http://lua-users.org/wiki/CopyTable
|
|
routines.utils.deepCopy = function(object)
|
|
local lookup_table = {}
|
|
local function _copy(object)
|
|
if type(object) ~= "table" then
|
|
return object
|
|
elseif lookup_table[object] then
|
|
return lookup_table[object]
|
|
end
|
|
local new_table = {}
|
|
lookup_table[object] = new_table
|
|
for index, value in pairs(object) do
|
|
new_table[_copy(index)] = _copy(value)
|
|
end
|
|
return setmetatable(new_table, getmetatable(object))
|
|
end
|
|
local objectreturn = _copy(object)
|
|
return objectreturn
|
|
end
|
|
|
|
|
|
-- porting in Slmod's serialize_slmod2
|
|
routines.utils.oneLineSerialize = function(tbl) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function
|
|
if type(tbl) == 'table' then --function only works for tables!
|
|
|
|
local tbl_str = {}
|
|
|
|
tbl_str[#tbl_str + 1] = '{'
|
|
|
|
for ind,val in pairs(tbl) do -- serialize its fields
|
|
if type(ind) == "number" then
|
|
tbl_str[#tbl_str + 1] = '['
|
|
tbl_str[#tbl_str + 1] = tostring(ind)
|
|
tbl_str[#tbl_str + 1] = ']='
|
|
else --must be a string
|
|
tbl_str[#tbl_str + 1] = '['
|
|
tbl_str[#tbl_str + 1] = routines.utils.basicSerialize(ind)
|
|
tbl_str[#tbl_str + 1] = ']='
|
|
end
|
|
|
|
if ((type(val) == 'number') or (type(val) == 'boolean')) then
|
|
tbl_str[#tbl_str + 1] = tostring(val)
|
|
tbl_str[#tbl_str + 1] = ','
|
|
elseif type(val) == 'string' then
|
|
tbl_str[#tbl_str + 1] = routines.utils.basicSerialize(val)
|
|
tbl_str[#tbl_str + 1] = ','
|
|
elseif type(val) == 'nil' then -- won't ever happen, right?
|
|
tbl_str[#tbl_str + 1] = 'nil,'
|
|
elseif type(val) == 'table' then
|
|
if ind == "__index" then
|
|
tbl_str[#tbl_str + 1] = "__index"
|
|
tbl_str[#tbl_str + 1] = ',' --I think this is right, I just added it
|
|
else
|
|
tbl_str[#tbl_str + 1] = routines.utils.oneLineSerialize(val)
|
|
tbl_str[#tbl_str + 1] = ',' --I think this is right, I just added it
|
|
end
|
|
elseif type(val) == 'function' then
|
|
tbl_str[#tbl_str + 1] = "function " .. tostring(ind)
|
|
tbl_str[#tbl_str + 1] = ',' --I think this is right, I just added it
|
|
else
|
|
env.info('unable to serialize value type ' .. routines.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind))
|
|
env.info( debug.traceback() )
|
|
end
|
|
|
|
end
|
|
tbl_str[#tbl_str + 1] = '}'
|
|
return table.concat(tbl_str)
|
|
else
|
|
return tostring(tbl)
|
|
end
|
|
end
|
|
|
|
--porting in Slmod's "safestring" basic serialize
|
|
routines.utils.basicSerialize = function(s)
|
|
if s == nil then
|
|
return "\"\""
|
|
else
|
|
if ((type(s) == 'number') or (type(s) == 'boolean') or (type(s) == 'function') or (type(s) == 'table') or (type(s) == 'userdata') ) then
|
|
return tostring(s)
|
|
elseif type(s) == 'string' then
|
|
s = string.format('%q', s)
|
|
return s
|
|
end
|
|
end
|
|
end
|
|
|
|
-- From http://lua-users.org/wiki/SimpleRound
|
|
-- use negative idp for rounding ahead of decimal place, positive for rounding after decimal place
|
|
routines.utils.round = function(num, idp)
|
|
local mult = 10^(idp or 0)
|
|
return math.floor(num * mult + 0.5) / mult
|
|
end
|
|
|
|
-- porting in Slmod's dostring
|
|
routines.utils.dostring = function(s)
|
|
local f, err = loadstring(s)
|
|
if f then
|
|
return true, f()
|
|
else
|
|
return false, err
|
|
end
|
|
end
|
|
|
|
|
|
--3D Vector manipulation
|
|
routines.vec = {}
|
|
|
|
routines.vec.add = function(vec1, vec2)
|
|
return {x = vec1.x + vec2.x, y = vec1.y + vec2.y, z = vec1.z + vec2.z}
|
|
end
|
|
|
|
routines.vec.sub = function(vec1, vec2)
|
|
return {x = vec1.x - vec2.x, y = vec1.y - vec2.y, z = vec1.z - vec2.z}
|
|
end
|
|
|
|
routines.vec.scalarMult = function(vec, mult)
|
|
return {x = vec.x*mult, y = vec.y*mult, z = vec.z*mult}
|
|
end
|
|
|
|
routines.vec.scalar_mult = routines.vec.scalarMult
|
|
|
|
routines.vec.dp = function(vec1, vec2)
|
|
return vec1.x*vec2.x + vec1.y*vec2.y + vec1.z*vec2.z
|
|
end
|
|
|
|
routines.vec.cp = function(vec1, vec2)
|
|
return { x = vec1.y*vec2.z - vec1.z*vec2.y, y = vec1.z*vec2.x - vec1.x*vec2.z, z = vec1.x*vec2.y - vec1.y*vec2.x}
|
|
end
|
|
|
|
routines.vec.mag = function(vec)
|
|
return (vec.x^2 + vec.y^2 + vec.z^2)^0.5
|
|
end
|
|
|
|
routines.vec.getUnitVec = function(vec)
|
|
local mag = routines.vec.mag(vec)
|
|
return { x = vec.x/mag, y = vec.y/mag, z = vec.z/mag }
|
|
end
|
|
|
|
routines.vec.rotateVec2 = function(vec2, theta)
|
|
return { x = vec2.x*math.cos(theta) - vec2.y*math.sin(theta), y = vec2.x*math.sin(theta) + vec2.y*math.cos(theta)}
|
|
end
|
|
---------------------------------------------------------------------------------------------------------------------------
|
|
|
|
-- acc- the accuracy of each easting/northing. 0, 1, 2, 3, 4, or 5.
|
|
routines.tostringMGRS = function(MGRS, acc)
|
|
if acc == 0 then
|
|
return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph
|
|
else
|
|
return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph .. ' ' .. string.format('%0' .. acc .. 'd', routines.utils.round(MGRS.Easting/(10^(5-acc)), 0))
|
|
.. ' ' .. string.format('%0' .. acc .. 'd', routines.utils.round(MGRS.Northing/(10^(5-acc)), 0))
|
|
end
|
|
end
|
|
|
|
--[[acc:
|
|
in DM: decimal point of minutes.
|
|
In DMS: decimal point of seconds.
|
|
position after the decimal of the least significant digit:
|
|
So:
|
|
42.32 - acc of 2.
|
|
]]
|
|
routines.tostringLL = function(lat, lon, acc, DMS)
|
|
|
|
local latHemi, lonHemi
|
|
if lat > 0 then
|
|
latHemi = 'N'
|
|
else
|
|
latHemi = 'S'
|
|
end
|
|
|
|
if lon > 0 then
|
|
lonHemi = 'E'
|
|
else
|
|
lonHemi = 'W'
|
|
end
|
|
|
|
lat = math.abs(lat)
|
|
lon = math.abs(lon)
|
|
|
|
local latDeg = math.floor(lat)
|
|
local latMin = (lat - latDeg)*60
|
|
|
|
local lonDeg = math.floor(lon)
|
|
local lonMin = (lon - lonDeg)*60
|
|
|
|
if DMS then -- degrees, minutes, and seconds.
|
|
local oldLatMin = latMin
|
|
latMin = math.floor(latMin)
|
|
local latSec = routines.utils.round((oldLatMin - latMin)*60, acc)
|
|
|
|
local oldLonMin = lonMin
|
|
lonMin = math.floor(lonMin)
|
|
local lonSec = routines.utils.round((oldLonMin - lonMin)*60, acc)
|
|
|
|
if latSec == 60 then
|
|
latSec = 0
|
|
latMin = latMin + 1
|
|
end
|
|
|
|
if lonSec == 60 then
|
|
lonSec = 0
|
|
lonMin = lonMin + 1
|
|
end
|
|
|
|
local secFrmtStr -- create the formatting string for the seconds place
|
|
if acc <= 0 then -- no decimal place.
|
|
secFrmtStr = '%02d'
|
|
else
|
|
local width = 3 + acc -- 01.310 - that's a width of 6, for example.
|
|
secFrmtStr = '%0' .. width .. '.' .. acc .. 'f'
|
|
end
|
|
|
|
return string.format('%02d', latDeg) .. ' ' .. string.format('%02d', latMin) .. '\' ' .. string.format(secFrmtStr, latSec) .. '"' .. latHemi .. ' '
|
|
.. string.format('%02d', lonDeg) .. ' ' .. string.format('%02d', lonMin) .. '\' ' .. string.format(secFrmtStr, lonSec) .. '"' .. lonHemi
|
|
|
|
else -- degrees, decimal minutes.
|
|
latMin = routines.utils.round(latMin, acc)
|
|
lonMin = routines.utils.round(lonMin, acc)
|
|
|
|
if latMin == 60 then
|
|
latMin = 0
|
|
latDeg = latDeg + 1
|
|
end
|
|
|
|
if lonMin == 60 then
|
|
lonMin = 0
|
|
lonDeg = lonDeg + 1
|
|
end
|
|
|
|
local minFrmtStr -- create the formatting string for the minutes place
|
|
if acc <= 0 then -- no decimal place.
|
|
minFrmtStr = '%02d'
|
|
else
|
|
local width = 3 + acc -- 01.310 - that's a width of 6, for example.
|
|
minFrmtStr = '%0' .. width .. '.' .. acc .. 'f'
|
|
end
|
|
|
|
return string.format('%02d', latDeg) .. ' ' .. string.format(minFrmtStr, latMin) .. '\'' .. latHemi .. ' '
|
|
.. string.format('%02d', lonDeg) .. ' ' .. string.format(minFrmtStr, lonMin) .. '\'' .. lonHemi
|
|
|
|
end
|
|
end
|
|
|
|
--[[ required: az - radian
|
|
required: dist - meters
|
|
optional: alt - meters (set to false or nil if you don't want to use it).
|
|
optional: metric - set true to get dist and alt in km and m.
|
|
precision will always be nearest degree and NM or km.]]
|
|
routines.tostringBR = function(az, dist, alt, metric)
|
|
az = routines.utils.round(routines.utils.toDegree(az), 0)
|
|
|
|
if metric then
|
|
dist = routines.utils.round(dist/1000, 2)
|
|
else
|
|
dist = routines.utils.round(routines.utils.metersToNM(dist), 2)
|
|
end
|
|
|
|
local s = string.format('%03d°', az) .. ' for ' .. dist
|
|
|
|
if alt then
|
|
if metric then
|
|
s = s .. ' at ' .. routines.utils.round(alt, 0)
|
|
else
|
|
s = s .. ' at ' .. routines.utils.round(routines.utils.metersToFeet(alt), 0)
|
|
end
|
|
end
|
|
return s
|
|
end
|
|
|
|
routines.getNorthCorrection = function(point) --gets the correction needed for true north
|
|
if not point.z then --Vec2; convert to Vec3
|
|
point.z = point.y
|
|
point.y = 0
|
|
end
|
|
local lat, lon = coord.LOtoLL(point)
|
|
local north_posit = coord.LLtoLO(lat + 1, lon)
|
|
return math.atan2(north_posit.z - point.z, north_posit.x - point.x)
|
|
end
|
|
|
|
|
|
-- the main area
|
|
do
|
|
-- THE MAIN FUNCTION -- Accessed 100 times/sec.
|
|
routines.main = function()
|
|
timer.scheduleFunction(routines.main, {}, timer.getTime() + 0.1) --reschedule first in case of Lua error
|
|
----------------------------------------------------------------------------------------------------------
|
|
--area to add new stuff in
|
|
|
|
routines.do_scheduled_functions()
|
|
end -- end of routines.main
|
|
|
|
timer.scheduleFunction(routines.main, {}, timer.getTime() + 0.1)
|
|
|
|
end
|
|
|
|
|
|
do
|
|
local Tasks = {}
|
|
local task_id = 0
|
|
--[[ routines.scheduleFunction:
|
|
int id = routines.schedule_task(f function, vars table, t number, rep number, st number)
|
|
id - integer id of this function task
|
|
f - function to run
|
|
vars - table of vars for that function
|
|
t - time to run function
|
|
rep - time between repetitions of this function (OPTIONAL)
|
|
st - time when repetitions of this function will stop automatically (OPTIONAL)
|
|
]]
|
|
routines.scheduleFunction = function(f, vars, t, rep, st)
|
|
--verify correct types
|
|
assert(type(f) == 'function', 'variable 1, expected function, got ' .. type(f))
|
|
assert(type(vars) == 'table' or vars == nil, 'variable 2, expected table or nil, got ' .. type(f))
|
|
assert(type(t) == 'number', 'variable 3, expected number, got ' .. type(t))
|
|
assert(type(rep) == 'number' or rep == nil, 'variable 4, expected number or nil, got ' .. type(rep))
|
|
assert(type(st) == 'number' or st == nil, 'variable 5, expected number or nil, got ' .. type(st))
|
|
if not vars then
|
|
vars = {}
|
|
end
|
|
task_id = task_id + 1
|
|
table.insert(Tasks, {f = f, vars = vars, t = t, rep = rep, st = st, id = task_id})
|
|
return task_id
|
|
end
|
|
|
|
-- removes a scheduled function based on the function's id. returns true if successful, false if not successful.
|
|
routines.removeFunction = function(id)
|
|
local i = 1
|
|
while i <= #Tasks do
|
|
if Tasks[i].id == id then
|
|
table.remove(Tasks, i)
|
|
else
|
|
i = i + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
routines.errhandler = function(errmsg)
|
|
|
|
env.info( "Error in scheduled function:" .. errmsg )
|
|
env.info( debug.traceback() )
|
|
|
|
return errmsg
|
|
end
|
|
|
|
--------------------------------------------------------------------------------------------------------------------
|
|
-- not intended for users to use this function.
|
|
routines.do_scheduled_functions = function()
|
|
local i = 1
|
|
while i <= #Tasks do
|
|
if not Tasks[i].rep then -- not a repeated process
|
|
if Tasks[i].t <= timer.getTime() then
|
|
local Task = Tasks[i] -- local reference
|
|
--env.info("do_scheduled_functions:call function " .. i )
|
|
table.remove(Tasks, i)
|
|
local err, errmsg = xpcall(function() Task.f( unpack(Task.vars, 1, table.maxn(Task.vars))) end, routines.errhandler )
|
|
if not err then
|
|
--env.info('routines.scheduleFunction, error in scheduled function: ' .. errmsg)
|
|
end
|
|
--Task.f(unpack(Task.vars, 1, table.maxn(Task.vars))) -- do the task, do not increment i
|
|
else
|
|
i = i + 1
|
|
end
|
|
else
|
|
if Tasks[i].st and Tasks[i].st <= timer.getTime() then --if a stoptime was specified, and the stop time exceeded
|
|
--env.info("do_scheduled_functions:remove repeated")
|
|
table.remove(Tasks, i) -- stop time exceeded, do not execute, do not increment i
|
|
elseif Tasks[i].t <= timer.getTime() then
|
|
local Task = Tasks[i] -- local reference
|
|
Task.t = timer.getTime() + Task.rep --schedule next run
|
|
--env.info("do_scheduled_functions:call function " .. i )
|
|
local err, errmsg = xpcall(function() Task.f( unpack(Task.vars, 1, table.maxn(Task.vars))) end, routines.errhandler )
|
|
if not err then
|
|
--env.info('routines.scheduleFunction, error in scheduled function: ' .. errmsg)
|
|
end
|
|
--Tasks[i].f(unpack(Tasks[i].vars, 1, table.maxn(Tasks[i].vars))) -- do the task
|
|
i = i + 1
|
|
else
|
|
i = i + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
do
|
|
local idNum = 0
|
|
|
|
--Simplified event handler
|
|
routines.addEventHandler = function(f) --id is optional!
|
|
local handler = {}
|
|
idNum = idNum + 1
|
|
handler.id = idNum
|
|
handler.f = f
|
|
handler.onEvent = function(self, event)
|
|
self.f(event)
|
|
end
|
|
world.addEventHandler(handler)
|
|
end
|
|
|
|
routines.removeEventHandler = function(id)
|
|
for key, handler in pairs(world.eventHandlers) do
|
|
if handler.id and handler.id == id then
|
|
world.eventHandlers[key] = nil
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
end
|
|
|
|
-- need to return a Vec3 or Vec2?
|
|
function routines.getRandPointInCircle(point, radius, innerRadius)
|
|
local theta = 2*math.pi*math.random()
|
|
local rad = math.random() + math.random()
|
|
if rad > 1 then
|
|
rad = 2 - rad
|
|
end
|
|
|
|
local radMult
|
|
if innerRadius and innerRadius <= radius then
|
|
radMult = (radius - innerRadius)*rad + innerRadius
|
|
else
|
|
radMult = radius*rad
|
|
end
|
|
|
|
if not point.z then --might as well work with vec2/3
|
|
point.z = point.y
|
|
end
|
|
|
|
local rndCoord
|
|
if radius > 0 then
|
|
rndCoord = {x = math.cos(theta)*radMult + point.x, y = math.sin(theta)*radMult + point.z}
|
|
else
|
|
rndCoord = {x = point.x, y = point.z}
|
|
end
|
|
return rndCoord
|
|
end
|
|
|
|
routines.goRoute = function(group, path)
|
|
local misTask = {
|
|
id = 'Mission',
|
|
params = {
|
|
route = {
|
|
points = routines.utils.deepCopy(path),
|
|
},
|
|
},
|
|
}
|
|
if type(group) == 'string' then
|
|
group = Group.getByName(group)
|
|
end
|
|
local groupCon = group:getController()
|
|
if groupCon then
|
|
groupCon:setTask(misTask)
|
|
return true
|
|
end
|
|
|
|
Controller.setTask(groupCon, misTask)
|
|
return false
|
|
end
|
|
|
|
|
|
-- Useful atomic functions from mist, ported.
|
|
|
|
routines.ground = {}
|
|
routines.fixedWing = {}
|
|
routines.heli = {}
|
|
|
|
routines.ground.buildWP = function(point, overRideForm, overRideSpeed)
|
|
|
|
local wp = {}
|
|
wp.x = point.x
|
|
|
|
if point.z then
|
|
wp.y = point.z
|
|
else
|
|
wp.y = point.y
|
|
end
|
|
local form, speed
|
|
|
|
if point.speed and not overRideSpeed then
|
|
wp.speed = point.speed
|
|
elseif type(overRideSpeed) == 'number' then
|
|
wp.speed = overRideSpeed
|
|
else
|
|
wp.speed = routines.utils.kmphToMps(20)
|
|
end
|
|
|
|
if point.form and not overRideForm then
|
|
form = point.form
|
|
else
|
|
form = overRideForm
|
|
end
|
|
|
|
if not form then
|
|
wp.action = 'Cone'
|
|
else
|
|
form = string.lower(form)
|
|
if form == 'off_road' or form == 'off road' then
|
|
wp.action = 'Off Road'
|
|
elseif form == 'on_road' or form == 'on road' then
|
|
wp.action = 'On Road'
|
|
elseif form == 'rank' or form == 'line_abrest' or form == 'line abrest' or form == 'lineabrest'then
|
|
wp.action = 'Rank'
|
|
elseif form == 'cone' then
|
|
wp.action = 'Cone'
|
|
elseif form == 'diamond' then
|
|
wp.action = 'Diamond'
|
|
elseif form == 'vee' then
|
|
wp.action = 'Vee'
|
|
elseif form == 'echelon_left' or form == 'echelon left' or form == 'echelonl' then
|
|
wp.action = 'EchelonL'
|
|
elseif form == 'echelon_right' or form == 'echelon right' or form == 'echelonr' then
|
|
wp.action = 'EchelonR'
|
|
else
|
|
wp.action = 'Cone' -- if nothing matched
|
|
end
|
|
end
|
|
|
|
wp.type = 'Turning Point'
|
|
|
|
return wp
|
|
|
|
end
|
|
|
|
routines.fixedWing.buildWP = function(point, WPtype, speed, alt, altType)
|
|
|
|
local wp = {}
|
|
wp.x = point.x
|
|
|
|
if point.z then
|
|
wp.y = point.z
|
|
else
|
|
wp.y = point.y
|
|
end
|
|
|
|
if alt and type(alt) == 'number' then
|
|
wp.alt = alt
|
|
else
|
|
wp.alt = 2000
|
|
end
|
|
|
|
if altType then
|
|
altType = string.lower(altType)
|
|
if altType == 'radio' or 'agl' then
|
|
wp.alt_type = 'RADIO'
|
|
elseif altType == 'baro' or 'asl' then
|
|
wp.alt_type = 'BARO'
|
|
end
|
|
else
|
|
wp.alt_type = 'RADIO'
|
|
end
|
|
|
|
if point.speed then
|
|
speed = point.speed
|
|
end
|
|
|
|
if point.type then
|
|
WPtype = point.type
|
|
end
|
|
|
|
if not speed then
|
|
wp.speed = routines.utils.kmphToMps(500)
|
|
else
|
|
wp.speed = speed
|
|
end
|
|
|
|
if not WPtype then
|
|
wp.action = 'Turning Point'
|
|
else
|
|
WPtype = string.lower(WPtype)
|
|
if WPtype == 'flyover' or WPtype == 'fly over' or WPtype == 'fly_over' then
|
|
wp.action = 'Fly Over Point'
|
|
elseif WPtype == 'turningpoint' or WPtype == 'turning point' or WPtype == 'turning_point' then
|
|
wp.action = 'Turning Point'
|
|
else
|
|
wp.action = 'Turning Point'
|
|
end
|
|
end
|
|
|
|
wp.type = 'Turning Point'
|
|
return wp
|
|
end
|
|
|
|
routines.heli.buildWP = function(point, WPtype, speed, alt, altType)
|
|
|
|
local wp = {}
|
|
wp.x = point.x
|
|
|
|
if point.z then
|
|
wp.y = point.z
|
|
else
|
|
wp.y = point.y
|
|
end
|
|
|
|
if alt and type(alt) == 'number' then
|
|
wp.alt = alt
|
|
else
|
|
wp.alt = 500
|
|
end
|
|
|
|
if altType then
|
|
altType = string.lower(altType)
|
|
if altType == 'radio' or 'agl' then
|
|
wp.alt_type = 'RADIO'
|
|
elseif altType == 'baro' or 'asl' then
|
|
wp.alt_type = 'BARO'
|
|
end
|
|
else
|
|
wp.alt_type = 'RADIO'
|
|
end
|
|
|
|
if point.speed then
|
|
speed = point.speed
|
|
end
|
|
|
|
if point.type then
|
|
WPtype = point.type
|
|
end
|
|
|
|
if not speed then
|
|
wp.speed = routines.utils.kmphToMps(200)
|
|
else
|
|
wp.speed = speed
|
|
end
|
|
|
|
if not WPtype then
|
|
wp.action = 'Turning Point'
|
|
else
|
|
WPtype = string.lower(WPtype)
|
|
if WPtype == 'flyover' or WPtype == 'fly over' or WPtype == 'fly_over' then
|
|
wp.action = 'Fly Over Point'
|
|
elseif WPtype == 'turningpoint' or WPtype == 'turning point' or WPtype == 'turning_point' then
|
|
wp.action = 'Turning Point'
|
|
else
|
|
wp.action = 'Turning Point'
|
|
end
|
|
end
|
|
|
|
wp.type = 'Turning Point'
|
|
return wp
|
|
end
|
|
|
|
routines.groupToRandomPoint = function(vars)
|
|
local group = vars.group --Required
|
|
local point = vars.point --required
|
|
local radius = vars.radius or 0
|
|
local innerRadius = vars.innerRadius
|
|
local form = vars.form or 'Cone'
|
|
local heading = vars.heading or math.random()*2*math.pi
|
|
local headingDegrees = vars.headingDegrees
|
|
local speed = vars.speed or routines.utils.kmphToMps(20)
|
|
|
|
|
|
local useRoads
|
|
if not vars.disableRoads then
|
|
useRoads = true
|
|
else
|
|
useRoads = false
|
|
end
|
|
|
|
local path = {}
|
|
|
|
if headingDegrees then
|
|
heading = headingDegrees*math.pi/180
|
|
end
|
|
|
|
if heading >= 2*math.pi then
|
|
heading = heading - 2*math.pi
|
|
end
|
|
|
|
local rndCoord = routines.getRandPointInCircle(point, radius, innerRadius)
|
|
|
|
local offset = {}
|
|
local posStart = routines.getLeadPos(group)
|
|
|
|
offset.x = routines.utils.round(math.sin(heading - (math.pi/2)) * 50 + rndCoord.x, 3)
|
|
offset.z = routines.utils.round(math.cos(heading + (math.pi/2)) * 50 + rndCoord.y, 3)
|
|
path[#path + 1] = routines.ground.buildWP(posStart, form, speed)
|
|
|
|
|
|
if useRoads == true and ((point.x - posStart.x)^2 + (point.z - posStart.z)^2)^0.5 > radius * 1.3 then
|
|
path[#path + 1] = routines.ground.buildWP({['x'] = posStart.x + 11, ['z'] = posStart.z + 11}, 'off_road', speed)
|
|
path[#path + 1] = routines.ground.buildWP(posStart, 'on_road', speed)
|
|
path[#path + 1] = routines.ground.buildWP(offset, 'on_road', speed)
|
|
else
|
|
path[#path + 1] = routines.ground.buildWP({['x'] = posStart.x + 25, ['z'] = posStart.z + 25}, form, speed)
|
|
end
|
|
|
|
path[#path + 1] = routines.ground.buildWP(offset, form, speed)
|
|
path[#path + 1] = routines.ground.buildWP(rndCoord, form, speed)
|
|
|
|
routines.goRoute(group, path)
|
|
|
|
return
|
|
end
|
|
|
|
routines.groupRandomDistSelf = function(gpData, dist, form, heading, speed)
|
|
local pos = routines.getLeadPos(gpData)
|
|
local fakeZone = {}
|
|
fakeZone.radius = dist or math.random(300, 1000)
|
|
fakeZone.point = {x = pos.x, y, pos.y, z = pos.z}
|
|
routines.groupToRandomZone(gpData, fakeZone, form, heading, speed)
|
|
|
|
return
|
|
end
|
|
|
|
routines.groupToRandomZone = function(gpData, zone, form, heading, speed)
|
|
if type(gpData) == 'string' then
|
|
gpData = Group.getByName(gpData)
|
|
end
|
|
|
|
if type(zone) == 'string' then
|
|
zone = trigger.misc.getZone(zone)
|
|
elseif type(zone) == 'table' and not zone.radius then
|
|
zone = trigger.misc.getZone(zone[math.random(1, #zone)])
|
|
end
|
|
|
|
if speed then
|
|
speed = routines.utils.kmphToMps(speed)
|
|
end
|
|
|
|
local vars = {}
|
|
vars.group = gpData
|
|
vars.radius = zone.radius
|
|
vars.form = form
|
|
vars.headingDegrees = heading
|
|
vars.speed = speed
|
|
vars.point = routines.utils.zoneToVec3(zone)
|
|
|
|
routines.groupToRandomPoint(vars)
|
|
|
|
return
|
|
end
|
|
|
|
routines.isTerrainValid = function(coord, terrainTypes) -- vec2/3 and enum or table of acceptable terrain types
|
|
if coord.z then
|
|
coord.y = coord.z
|
|
end
|
|
local typeConverted = {}
|
|
|
|
if type(terrainTypes) == 'string' then -- if its a string it does this check
|
|
for constId, constData in pairs(land.SurfaceType) do
|
|
if string.lower(constId) == string.lower(terrainTypes) or string.lower(constData) == string.lower(terrainTypes) then
|
|
table.insert(typeConverted, constId)
|
|
end
|
|
end
|
|
elseif type(terrainTypes) == 'table' then -- if its a table it does this check
|
|
for typeId, typeData in pairs(terrainTypes) do
|
|
for constId, constData in pairs(land.SurfaceType) do
|
|
if string.lower(constId) == string.lower(typeData) or string.lower(constData) == string.lower(typeId) then
|
|
table.insert(typeConverted, constId)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
for validIndex, validData in pairs(typeConverted) do
|
|
if land.getSurfaceType(coord) == land.SurfaceType[validData] then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
routines.groupToPoint = function(gpData, point, form, heading, speed, useRoads)
|
|
if type(point) == 'string' then
|
|
point = trigger.misc.getZone(point)
|
|
end
|
|
if speed then
|
|
speed = routines.utils.kmphToMps(speed)
|
|
end
|
|
|
|
local vars = {}
|
|
vars.group = gpData
|
|
vars.form = form
|
|
vars.headingDegrees = heading
|
|
vars.speed = speed
|
|
vars.disableRoads = useRoads
|
|
vars.point = routines.utils.zoneToVec3(point)
|
|
routines.groupToRandomPoint(vars)
|
|
|
|
return
|
|
end
|
|
|
|
|
|
routines.getLeadPos = function(group)
|
|
if type(group) == 'string' then -- group name
|
|
group = Group.getByName(group)
|
|
end
|
|
|
|
local units = group:getUnits()
|
|
|
|
local leader = units[1]
|
|
if not leader then -- SHOULD be good, but if there is a bug, this code future-proofs it then.
|
|
local lowestInd = math.huge
|
|
for ind, unit in pairs(units) do
|
|
if ind < lowestInd then
|
|
lowestInd = ind
|
|
leader = unit
|
|
end
|
|
end
|
|
end
|
|
if leader and Unit.isExist(leader) then -- maybe a little too paranoid now...
|
|
return leader:getPosition().p
|
|
end
|
|
end
|
|
|
|
--[[ vars for routines.getMGRSString:
|
|
vars.units - table of unit names (NOT unitNameTable- maybe this should change).
|
|
vars.acc - integer between 0 and 5, inclusive
|
|
]]
|
|
routines.getMGRSString = function(vars)
|
|
local units = vars.units
|
|
local acc = vars.acc or 5
|
|
local avgPos = routines.getAvgPos(units)
|
|
if avgPos then
|
|
return routines.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(avgPos)), acc)
|
|
end
|
|
end
|
|
|
|
--[[ vars for routines.getLLString
|
|
vars.units - table of unit names (NOT unitNameTable- maybe this should change).
|
|
vars.acc - integer, number of numbers after decimal place
|
|
vars.DMS - if true, output in degrees, minutes, seconds. Otherwise, output in degrees, minutes.
|
|
|
|
|
|
]]
|
|
routines.getLLString = function(vars)
|
|
local units = vars.units
|
|
local acc = vars.acc or 3
|
|
local DMS = vars.DMS
|
|
local avgPos = routines.getAvgPos(units)
|
|
if avgPos then
|
|
local lat, lon = coord.LOtoLL(avgPos)
|
|
return routines.tostringLL(lat, lon, acc, DMS)
|
|
end
|
|
end
|
|
|
|
--[[
|
|
vars.zone - table of a zone name.
|
|
vars.ref - vec3 ref point, maybe overload for vec2 as well?
|
|
vars.alt - boolean, if used, includes altitude in string
|
|
vars.metric - boolean, gives distance in km instead of NM.
|
|
]]
|
|
routines.getBRStringZone = function(vars)
|
|
local zone = trigger.misc.getZone( vars.zone )
|
|
local ref = routines.utils.makeVec3(vars.ref, 0) -- turn it into Vec3 if it is not already.
|
|
local alt = vars.alt
|
|
local metric = vars.metric
|
|
if zone then
|
|
local vec = {x = zone.point.x - ref.x, y = zone.point.y - ref.y, z = zone.point.z - ref.z}
|
|
local dir = routines.utils.getDir(vec, ref)
|
|
local dist = routines.utils.get2DDist(zone.point, ref)
|
|
if alt then
|
|
alt = zone.y
|
|
end
|
|
return routines.tostringBR(dir, dist, alt, metric)
|
|
else
|
|
env.info( 'routines.getBRStringZone: error: zone is nil' )
|
|
end
|
|
end
|
|
|
|
--[[
|
|
vars.units- table of unit names (NOT unitNameTable- maybe this should change).
|
|
vars.ref - vec3 ref point, maybe overload for vec2 as well?
|
|
vars.alt - boolean, if used, includes altitude in string
|
|
vars.metric - boolean, gives distance in km instead of NM.
|
|
]]
|
|
routines.getBRString = function(vars)
|
|
local units = vars.units
|
|
local ref = routines.utils.makeVec3(vars.ref, 0) -- turn it into Vec3 if it is not already.
|
|
local alt = vars.alt
|
|
local metric = vars.metric
|
|
local avgPos = routines.getAvgPos(units)
|
|
if avgPos then
|
|
local vec = {x = avgPos.x - ref.x, y = avgPos.y - ref.y, z = avgPos.z - ref.z}
|
|
local dir = routines.utils.getDir(vec, ref)
|
|
local dist = routines.utils.get2DDist(avgPos, ref)
|
|
if alt then
|
|
alt = avgPos.y
|
|
end
|
|
return routines.tostringBR(dir, dist, alt, metric)
|
|
end
|
|
end
|
|
|
|
|
|
-- Returns the Vec3 coordinates of the average position of the concentration of units most in the heading direction.
|
|
--[[ vars for routines.getLeadingPos:
|
|
vars.units - table of unit names
|
|
vars.heading - direction
|
|
vars.radius - number
|
|
vars.headingDegrees - boolean, switches heading to degrees
|
|
]]
|
|
routines.getLeadingPos = function(vars)
|
|
local units = vars.units
|
|
local heading = vars.heading
|
|
local radius = vars.radius
|
|
if vars.headingDegrees then
|
|
heading = routines.utils.toRadian(vars.headingDegrees)
|
|
end
|
|
|
|
local unitPosTbl = {}
|
|
for i = 1, #units do
|
|
local unit = Unit.getByName(units[i])
|
|
if unit and unit:isExist() then
|
|
unitPosTbl[#unitPosTbl + 1] = unit:getPosition().p
|
|
end
|
|
end
|
|
if #unitPosTbl > 0 then -- one more more units found.
|
|
-- first, find the unit most in the heading direction
|
|
local maxPos = -math.huge
|
|
|
|
local maxPosInd -- maxPos - the furthest in direction defined by heading; maxPosInd =
|
|
for i = 1, #unitPosTbl do
|
|
local rotatedVec2 = routines.vec.rotateVec2(routines.utils.makeVec2(unitPosTbl[i]), heading)
|
|
if (not maxPos) or maxPos < rotatedVec2.x then
|
|
maxPos = rotatedVec2.x
|
|
maxPosInd = i
|
|
end
|
|
end
|
|
|
|
--now, get all the units around this unit...
|
|
local avgPos
|
|
if radius then
|
|
local maxUnitPos = unitPosTbl[maxPosInd]
|
|
local avgx, avgy, avgz, totNum = 0, 0, 0, 0
|
|
for i = 1, #unitPosTbl do
|
|
if routines.utils.get2DDist(maxUnitPos, unitPosTbl[i]) <= radius then
|
|
avgx = avgx + unitPosTbl[i].x
|
|
avgy = avgy + unitPosTbl[i].y
|
|
avgz = avgz + unitPosTbl[i].z
|
|
totNum = totNum + 1
|
|
end
|
|
end
|
|
avgPos = { x = avgx/totNum, y = avgy/totNum, z = avgz/totNum}
|
|
else
|
|
avgPos = unitPosTbl[maxPosInd]
|
|
end
|
|
|
|
return avgPos
|
|
end
|
|
end
|
|
|
|
|
|
--[[ vars for routines.getLeadingMGRSString:
|
|
vars.units - table of unit names
|
|
vars.heading - direction
|
|
vars.radius - number
|
|
vars.headingDegrees - boolean, switches heading to degrees
|
|
vars.acc - number, 0 to 5.
|
|
]]
|
|
routines.getLeadingMGRSString = function(vars)
|
|
local pos = routines.getLeadingPos(vars)
|
|
if pos then
|
|
local acc = vars.acc or 5
|
|
return routines.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(pos)), acc)
|
|
end
|
|
end
|
|
|
|
--[[ vars for routines.getLeadingLLString:
|
|
vars.units - table of unit names
|
|
vars.heading - direction, number
|
|
vars.radius - number
|
|
vars.headingDegrees - boolean, switches heading to degrees
|
|
vars.acc - number of digits after decimal point (can be negative)
|
|
vars.DMS - boolean, true if you want DMS.
|
|
]]
|
|
routines.getLeadingLLString = function(vars)
|
|
local pos = routines.getLeadingPos(vars)
|
|
if pos then
|
|
local acc = vars.acc or 3
|
|
local DMS = vars.DMS
|
|
local lat, lon = coord.LOtoLL(pos)
|
|
return routines.tostringLL(lat, lon, acc, DMS)
|
|
end
|
|
end
|
|
|
|
|
|
|
|
--[[ vars for routines.getLeadingBRString:
|
|
vars.units - table of unit names
|
|
vars.heading - direction, number
|
|
vars.radius - number
|
|
vars.headingDegrees - boolean, switches heading to degrees
|
|
vars.metric - boolean, if true, use km instead of NM.
|
|
vars.alt - boolean, if true, include altitude.
|
|
vars.ref - vec3/vec2 reference point.
|
|
]]
|
|
routines.getLeadingBRString = function(vars)
|
|
local pos = routines.getLeadingPos(vars)
|
|
if pos then
|
|
local ref = vars.ref
|
|
local alt = vars.alt
|
|
local metric = vars.metric
|
|
|
|
local vec = {x = pos.x - ref.x, y = pos.y - ref.y, z = pos.z - ref.z}
|
|
local dir = routines.utils.getDir(vec, ref)
|
|
local dist = routines.utils.get2DDist(pos, ref)
|
|
if alt then
|
|
alt = pos.y
|
|
end
|
|
return routines.tostringBR(dir, dist, alt, metric)
|
|
end
|
|
end
|
|
|
|
--[[ vars for routines.message.add
|
|
vars.text = 'Hello World'
|
|
vars.displayTime = 20
|
|
vars.msgFor = {coa = {'red'}, countries = {'Ukraine', 'Georgia'}, unitTypes = {'A-10C'}}
|
|
|
|
]]
|
|
|
|
--[[ vars for routines.msgMGRS
|
|
vars.units - table of unit names (NOT unitNameTable- maybe this should change).
|
|
vars.acc - integer between 0 and 5, inclusive
|
|
vars.text - text in the message
|
|
vars.displayTime - self explanatory
|
|
vars.msgFor - scope
|
|
]]
|
|
routines.msgMGRS = function(vars)
|
|
local units = vars.units
|
|
local acc = vars.acc
|
|
local text = vars.text
|
|
local displayTime = vars.displayTime
|
|
local msgFor = vars.msgFor
|
|
|
|
local s = routines.getMGRSString{units = units, acc = acc}
|
|
local newText
|
|
if string.find(text, '%%s') then -- look for %s
|
|
newText = string.format(text, s) -- insert the coordinates into the message
|
|
else -- else, just append to the end.
|
|
newText = text .. s
|
|
end
|
|
|
|
routines.message.add{
|
|
text = newText,
|
|
displayTime = displayTime,
|
|
msgFor = msgFor
|
|
}
|
|
end
|
|
|
|
--[[ vars for routines.msgLL
|
|
vars.units - table of unit names (NOT unitNameTable- maybe this should change) (Yes).
|
|
vars.acc - integer, number of numbers after decimal place
|
|
vars.DMS - if true, output in degrees, minutes, seconds. Otherwise, output in degrees, minutes.
|
|
vars.text - text in the message
|
|
vars.displayTime - self explanatory
|
|
vars.msgFor - scope
|
|
]]
|
|
routines.msgLL = function(vars)
|
|
local units = vars.units -- technically, I don't really need to do this, but it helps readability.
|
|
local acc = vars.acc
|
|
local DMS = vars.DMS
|
|
local text = vars.text
|
|
local displayTime = vars.displayTime
|
|
local msgFor = vars.msgFor
|
|
|
|
local s = routines.getLLString{units = units, acc = acc, DMS = DMS}
|
|
local newText
|
|
if string.find(text, '%%s') then -- look for %s
|
|
newText = string.format(text, s) -- insert the coordinates into the message
|
|
else -- else, just append to the end.
|
|
newText = text .. s
|
|
end
|
|
|
|
routines.message.add{
|
|
text = newText,
|
|
displayTime = displayTime,
|
|
msgFor = msgFor
|
|
}
|
|
|
|
end
|
|
|
|
|
|
--[[
|
|
vars.units- table of unit names (NOT unitNameTable- maybe this should change).
|
|
vars.ref - vec3 ref point, maybe overload for vec2 as well?
|
|
vars.alt - boolean, if used, includes altitude in string
|
|
vars.metric - boolean, gives distance in km instead of NM.
|
|
vars.text - text of the message
|
|
vars.displayTime
|
|
vars.msgFor - scope
|
|
]]
|
|
routines.msgBR = function(vars)
|
|
local units = vars.units -- technically, I don't really need to do this, but it helps readability.
|
|
local ref = vars.ref -- vec2/vec3 will be handled in routines.getBRString
|
|
local alt = vars.alt
|
|
local metric = vars.metric
|
|
local text = vars.text
|
|
local displayTime = vars.displayTime
|
|
local msgFor = vars.msgFor
|
|
|
|
local s = routines.getBRString{units = units, ref = ref, alt = alt, metric = metric}
|
|
local newText
|
|
if string.find(text, '%%s') then -- look for %s
|
|
newText = string.format(text, s) -- insert the coordinates into the message
|
|
else -- else, just append to the end.
|
|
newText = text .. s
|
|
end
|
|
|
|
routines.message.add{
|
|
text = newText,
|
|
displayTime = displayTime,
|
|
msgFor = msgFor
|
|
}
|
|
|
|
end
|
|
|
|
|
|
--------------------------------------------------------------------------------------------
|
|
-- basically, just sub-types of routines.msgBR... saves folks the work of getting the ref point.
|
|
--[[
|
|
vars.units- table of unit names (NOT unitNameTable- maybe this should change).
|
|
vars.ref - string red, blue
|
|
vars.alt - boolean, if used, includes altitude in string
|
|
vars.metric - boolean, gives distance in km instead of NM.
|
|
vars.text - text of the message
|
|
vars.displayTime
|
|
vars.msgFor - scope
|
|
]]
|
|
routines.msgBullseye = function(vars)
|
|
if string.lower(vars.ref) == 'red' then
|
|
vars.ref = routines.DBs.missionData.bullseye.red
|
|
routines.msgBR(vars)
|
|
elseif string.lower(vars.ref) == 'blue' then
|
|
vars.ref = routines.DBs.missionData.bullseye.blue
|
|
routines.msgBR(vars)
|
|
end
|
|
end
|
|
|
|
--[[
|
|
vars.units- table of unit names (NOT unitNameTable- maybe this should change).
|
|
vars.ref - unit name of reference point
|
|
vars.alt - boolean, if used, includes altitude in string
|
|
vars.metric - boolean, gives distance in km instead of NM.
|
|
vars.text - text of the message
|
|
vars.displayTime
|
|
vars.msgFor - scope
|
|
]]
|
|
|
|
routines.msgBRA = function(vars)
|
|
if Unit.getByName(vars.ref) then
|
|
vars.ref = Unit.getByName(vars.ref):getPosition().p
|
|
if not vars.alt then
|
|
vars.alt = true
|
|
end
|
|
routines.msgBR(vars)
|
|
end
|
|
end
|
|
--------------------------------------------------------------------------------------------
|
|
|
|
--[[ vars for routines.msgLeadingMGRS:
|
|
vars.units - table of unit names
|
|
vars.heading - direction
|
|
vars.radius - number
|
|
vars.headingDegrees - boolean, switches heading to degrees (optional)
|
|
vars.acc - number, 0 to 5.
|
|
vars.text - text of the message
|
|
vars.displayTime
|
|
vars.msgFor - scope
|
|
]]
|
|
routines.msgLeadingMGRS = function(vars)
|
|
local units = vars.units -- technically, I don't really need to do this, but it helps readability.
|
|
local heading = vars.heading
|
|
local radius = vars.radius
|
|
local headingDegrees = vars.headingDegrees
|
|
local acc = vars.acc
|
|
local text = vars.text
|
|
local displayTime = vars.displayTime
|
|
local msgFor = vars.msgFor
|
|
|
|
local s = routines.getLeadingMGRSString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc}
|
|
local newText
|
|
if string.find(text, '%%s') then -- look for %s
|
|
newText = string.format(text, s) -- insert the coordinates into the message
|
|
else -- else, just append to the end.
|
|
newText = text .. s
|
|
end
|
|
|
|
routines.message.add{
|
|
text = newText,
|
|
displayTime = displayTime,
|
|
msgFor = msgFor
|
|
}
|
|
|
|
|
|
end
|
|
--[[ vars for routines.msgLeadingLL:
|
|
vars.units - table of unit names
|
|
vars.heading - direction, number
|
|
vars.radius - number
|
|
vars.headingDegrees - boolean, switches heading to degrees (optional)
|
|
vars.acc - number of digits after decimal point (can be negative)
|
|
vars.DMS - boolean, true if you want DMS. (optional)
|
|
vars.text - text of the message
|
|
vars.displayTime
|
|
vars.msgFor - scope
|
|
]]
|
|
routines.msgLeadingLL = function(vars)
|
|
local units = vars.units -- technically, I don't really need to do this, but it helps readability.
|
|
local heading = vars.heading
|
|
local radius = vars.radius
|
|
local headingDegrees = vars.headingDegrees
|
|
local acc = vars.acc
|
|
local DMS = vars.DMS
|
|
local text = vars.text
|
|
local displayTime = vars.displayTime
|
|
local msgFor = vars.msgFor
|
|
|
|
local s = routines.getLeadingLLString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc, DMS = DMS}
|
|
local newText
|
|
if string.find(text, '%%s') then -- look for %s
|
|
newText = string.format(text, s) -- insert the coordinates into the message
|
|
else -- else, just append to the end.
|
|
newText = text .. s
|
|
end
|
|
|
|
routines.message.add{
|
|
text = newText,
|
|
displayTime = displayTime,
|
|
msgFor = msgFor
|
|
}
|
|
|
|
end
|
|
|
|
--[[
|
|
vars.units - table of unit names
|
|
vars.heading - direction, number
|
|
vars.radius - number
|
|
vars.headingDegrees - boolean, switches heading to degrees (optional)
|
|
vars.metric - boolean, if true, use km instead of NM. (optional)
|
|
vars.alt - boolean, if true, include altitude. (optional)
|
|
vars.ref - vec3/vec2 reference point.
|
|
vars.text - text of the message
|
|
vars.displayTime
|
|
vars.msgFor - scope
|
|
]]
|
|
routines.msgLeadingBR = function(vars)
|
|
local units = vars.units -- technically, I don't really need to do this, but it helps readability.
|
|
local heading = vars.heading
|
|
local radius = vars.radius
|
|
local headingDegrees = vars.headingDegrees
|
|
local metric = vars.metric
|
|
local alt = vars.alt
|
|
local ref = vars.ref -- vec2/vec3 will be handled in routines.getBRString
|
|
local text = vars.text
|
|
local displayTime = vars.displayTime
|
|
local msgFor = vars.msgFor
|
|
|
|
local s = routines.getLeadingBRString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, metric = metric, alt = alt, ref = ref}
|
|
local newText
|
|
if string.find(text, '%%s') then -- look for %s
|
|
newText = string.format(text, s) -- insert the coordinates into the message
|
|
else -- else, just append to the end.
|
|
newText = text .. s
|
|
end
|
|
|
|
routines.message.add{
|
|
text = newText,
|
|
displayTime = displayTime,
|
|
msgFor = msgFor
|
|
}
|
|
end
|
|
|
|
|
|
function spairs(t, order)
|
|
-- collect the keys
|
|
local keys = {}
|
|
for k in pairs(t) do keys[#keys+1] = k end
|
|
|
|
-- if order function given, sort by it by passing the table and keys a, b,
|
|
-- otherwise just sort the keys
|
|
if order then
|
|
table.sort(keys, function(a,b) return order(t, a, b) end)
|
|
else
|
|
table.sort(keys)
|
|
end
|
|
|
|
-- return the iterator function
|
|
local i = 0
|
|
return function()
|
|
i = i + 1
|
|
if keys[i] then
|
|
return keys[i], t[keys[i]]
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
function routines.IsPartOfGroupInZones( CargoGroup, LandingZones )
|
|
trace.f()
|
|
|
|
local CurrentZoneID = nil
|
|
|
|
if CargoGroup then
|
|
local CargoUnits = CargoGroup:getUnits()
|
|
for CargoUnitID, CargoUnit in pairs( CargoUnits ) do
|
|
if CargoUnit and CargoUnit:getLife() >= 1.0 then
|
|
CurrentZoneID = routines.IsUnitInZones( CargoUnit, LandingZones )
|
|
if CurrentZoneID then
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
trace.r( "", "", { CurrentZoneID } )
|
|
return CurrentZoneID
|
|
end
|
|
|
|
|
|
|
|
function routines.IsUnitInZones( TransportUnit, LandingZones )
|
|
trace.f("", "routines.IsUnitInZones" )
|
|
|
|
local TransportZoneResult = nil
|
|
local TransportZonePos = nil
|
|
local TransportZone = nil
|
|
|
|
-- fill-up some local variables to support further calculations to determine location of units within the zone.
|
|
if TransportUnit then
|
|
local TransportUnitPos = TransportUnit:getPosition().p
|
|
if type( LandingZones ) == "table" then
|
|
for LandingZoneID, LandingZoneName in pairs( LandingZones ) do
|
|
TransportZone = trigger.misc.getZone( LandingZoneName )
|
|
if TransportZone then
|
|
TransportZonePos = {radius = TransportZone.radius, x = TransportZone.point.x, y = TransportZone.point.y, z = TransportZone.point.z}
|
|
if ((( TransportUnitPos.x - TransportZonePos.x)^2 + (TransportUnitPos.z - TransportZonePos.z)^2)^0.5 <= TransportZonePos.radius) then
|
|
TransportZoneResult = LandingZoneID
|
|
break
|
|
end
|
|
end
|
|
end
|
|
else
|
|
TransportZone = trigger.misc.getZone( LandingZones )
|
|
TransportZonePos = {radius = TransportZone.radius, x = TransportZone.point.x, y = TransportZone.point.y, z = TransportZone.point.z}
|
|
if ((( TransportUnitPos.x - TransportZonePos.x)^2 + (TransportUnitPos.z - TransportZonePos.z)^2)^0.5 <= TransportZonePos.radius) then
|
|
TransportZoneResult = 1
|
|
end
|
|
end
|
|
if TransportZoneResult then
|
|
trace.i( "routines", "TransportZone:" .. TransportZoneResult )
|
|
else
|
|
trace.i( "routines", "TransportZone:nil logic" )
|
|
end
|
|
return TransportZoneResult
|
|
else
|
|
trace.i( "routines", "TransportZone:nil hard" )
|
|
return nil
|
|
end
|
|
end
|
|
|
|
function routines.IsStaticInZones( TransportStatic, LandingZones )
|
|
trace.f()
|
|
|
|
local TransportZoneResult = nil
|
|
local TransportZonePos = nil
|
|
local TransportZone = nil
|
|
|
|
-- fill-up some local variables to support further calculations to determine location of units within the zone.
|
|
local TransportStaticPos = TransportStatic:getPosition().p
|
|
if type( LandingZones ) == "table" then
|
|
for LandingZoneID, LandingZoneName in pairs( LandingZones ) do
|
|
TransportZone = trigger.misc.getZone( LandingZoneName )
|
|
if TransportZone then
|
|
TransportZonePos = {radius = TransportZone.radius, x = TransportZone.point.x, y = TransportZone.point.y, z = TransportZone.point.z}
|
|
if ((( TransportStaticPos.x - TransportZonePos.x)^2 + (TransportStaticPos.z - TransportZonePos.z)^2)^0.5 <= TransportZonePos.radius) then
|
|
TransportZoneResult = LandingZoneID
|
|
break
|
|
end
|
|
end
|
|
end
|
|
else
|
|
TransportZone = trigger.misc.getZone( LandingZones )
|
|
TransportZonePos = {radius = TransportZone.radius, x = TransportZone.point.x, y = TransportZone.point.y, z = TransportZone.point.z}
|
|
if ((( TransportStaticPos.x - TransportZonePos.x)^2 + (TransportStaticPos.z - TransportZonePos.z)^2)^0.5 <= TransportZonePos.radius) then
|
|
TransportZoneResult = 1
|
|
end
|
|
end
|
|
|
|
trace.r( "", "", { TransportZoneResult } )
|
|
return TransportZoneResult
|
|
end
|
|
|
|
|
|
function routines.IsUnitInRadius( CargoUnit, ReferenceGroup, Radius )
|
|
trace.f()
|
|
|
|
local Valid = true
|
|
|
|
-- fill-up some local variables to support further calculations to determine location of units within the zone.
|
|
local CargoPos = CargoUnit:getPosition().p
|
|
local ReferenceGroupPos = ReferenceGroup:getUnits()[1]:getPosition().p
|
|
|
|
if (((CargoPos.x - ReferenceGroupPos.x)^2 + (CargoPos.z - ReferenceGroupPos.z)^2)^0.5 <= Radius) then
|
|
else
|
|
Valid = false
|
|
end
|
|
|
|
trace.r( "", "", { Valid } )
|
|
return Valid
|
|
end
|
|
|
|
function routines.IsPartOfGroupInRadius( CargoGroup, ReferenceGroup, Radius )
|
|
trace.f()
|
|
|
|
local Valid = true
|
|
|
|
Valid = routines.ValidateGroup( CargoGroup, "CargoGroup", Valid )
|
|
Valid = routines.ValidateGroup( ReferenceGroup, "ReferenceGroup", Valid )
|
|
|
|
-- fill-up some local variables to support further calculations to determine location of units within the zone
|
|
local CargoUnits = CargoGroup:getUnits()
|
|
for CargoUnitId, CargoUnit in pairs( CargoUnits ) do
|
|
local CargoUnitPos = CargoUnit:getPosition().p
|
|
-- env.info( 'routines.IsPartOfGroupInRadius: CargoUnitPos.x = ' .. CargoUnitPos.x .. ' CargoUnitPos.z = ' .. CargoUnitPos.z )
|
|
local ReferenceGroupPos = ReferenceGroup:getUnits()[1]:getPosition().p
|
|
-- env.info( 'routines.IsPartOfGroupInRadius: ReferenceGroupPos.x = ' .. ReferenceGroupPos.x .. ' ReferenceGroupPos.z = ' .. ReferenceGroupPos.z )
|
|
|
|
if ((( CargoUnitPos.x - ReferenceGroupPos.x)^2 + (CargoUnitPos.z - ReferenceGroupPos.z)^2)^0.5 <= Radius) then
|
|
else
|
|
Valid = false
|
|
break
|
|
end
|
|
end
|
|
|
|
trace.r( "", "", { Valid } )
|
|
return Valid
|
|
end
|
|
|
|
function routines.DestroyGroupInRadiusFromGroup( CargoGroup, ReferenceGroup, Radius )
|
|
trace.f()
|
|
|
|
local Valid = true
|
|
|
|
Valid = routines.ValidateGroup( CargoGroup, "CargoGroup", Valid )
|
|
Valid = routines.ValidateGroup( ReferenceGroup, "ReferenceGroup", Valid )
|
|
|
|
if Valid then
|
|
-- fill-up some local variables to support further calculations to determine location of units within the zone
|
|
local CargoUnits = CargoGroup:getUnits()
|
|
local AliveCargoUnits = #CargoUnits
|
|
for CargoUnitId, CargoUnit in pairs( CargoUnits ) do
|
|
local CargoUnitPos = CargoUnit:getPosition().p
|
|
-- env.info( 'routines.DestroyGroupInRadiusFromGroup: CargoUnitPos.x = ' .. CargoUnitPos.x .. ' CargoUnitPos.z = ' .. CargoUnitPos.z )
|
|
local ReferenceGroupPos = ReferenceGroup:getUnits()[1]:getPosition().p
|
|
-- env.info( 'routines.DestroyGroupInRadiusFromGroup: ReferenceGroupPos.x = ' .. ReferenceGroupPos.x .. ' ReferenceGroupPos.z = ' .. ReferenceGroupPos.z )
|
|
|
|
if ((( CargoUnitPos.x - ReferenceGroupPos.x)^2 + (CargoUnitPos.z - ReferenceGroupPos.z)^2)^0.5 <= Radius) then
|
|
CargoUnit:destroy()
|
|
AliveCargoUnits = AliveCargoUnits - 1
|
|
else
|
|
Valid = false
|
|
break
|
|
end
|
|
end
|
|
else
|
|
AliveCargoUnits = -1
|
|
end
|
|
|
|
trace.r( "", "", { AliveCargoUnits } )
|
|
return AliveCargoUnits
|
|
end
|
|
|
|
|
|
function routines.ValidateString( Variable, VariableName, Valid )
|
|
trace.f()
|
|
|
|
if type( Variable ) == "string" then
|
|
if Variable == "" then
|
|
error( "routines.ValidateString: error: " .. VariableName .. " must be filled out!" )
|
|
Valid = false
|
|
end
|
|
else
|
|
error( "routines.ValidateString: error: " .. VariableName .. " is not a string." )
|
|
Valid = false
|
|
end
|
|
|
|
trace.r( "", "", { Valid } )
|
|
return Valid
|
|
end
|
|
|
|
function routines.ValidateNumber( Variable, VariableName, Valid )
|
|
trace.f()
|
|
|
|
if type( Variable ) == "number" then
|
|
else
|
|
error( "routines.ValidateNumber: error: " .. VariableName .. " is not a number." )
|
|
Valid = false
|
|
end
|
|
|
|
trace.r( "", "", { Valid } )
|
|
return Valid
|
|
|
|
end
|
|
|
|
function routines.ValidateGroup( Variable, VariableName, Valid )
|
|
trace.f()
|
|
|
|
if Variable == nil then
|
|
error( "routines.ValidateGroup: error: " .. VariableName .. " is a nil value!" )
|
|
Valid = false
|
|
end
|
|
|
|
trace.r( "", "", { Valid } )
|
|
return Valid
|
|
end
|
|
|
|
function routines.ValidateZone( LandingZones, VariableName, Valid )
|
|
trace.f()
|
|
|
|
if LandingZones == nil then
|
|
error( "routines.ValidateGroup: error: " .. VariableName .. " is a nil value!" )
|
|
Valid = false
|
|
end
|
|
|
|
if type( LandingZones ) == "table" then
|
|
for LandingZoneID, LandingZoneName in pairs( LandingZones ) do
|
|
if trigger.misc.getZone( LandingZoneName ) == nil then
|
|
error( "routines.ValidateGroup: error: Zone " .. LandingZoneName .. " does not exist!" )
|
|
Valid = false
|
|
break
|
|
end
|
|
end
|
|
else
|
|
if trigger.misc.getZone( LandingZones ) == nil then
|
|
error( "routines.ValidateGroup: error: Zone " .. LandingZones .. " does not exist!" )
|
|
Valid = false
|
|
end
|
|
end
|
|
|
|
trace.r( "", "", { Valid } )
|
|
return Valid
|
|
end
|
|
|
|
function routines.ValidateEnumeration( Variable, VariableName, Enum, Valid )
|
|
trace.f()
|
|
|
|
local ValidVariable = false
|
|
|
|
for EnumId, EnumData in pairs( Enum ) do
|
|
if Variable == EnumData then
|
|
ValidVariable = true
|
|
break
|
|
end
|
|
end
|
|
|
|
if ValidVariable then
|
|
else
|
|
error( 'TransportValidateEnum: " .. VariableName .. " is not a valid type.' .. Variable )
|
|
Valid = false
|
|
end
|
|
|
|
trace.r( "", "", { Valid } )
|
|
return Valid
|
|
end
|
|
|
|
function routines.getGroupRoute(groupIdent, task) -- same as getGroupPoints but returns speed and formation type along with vec2 of point}
|
|
-- refactor to search by groupId and allow groupId and groupName as inputs
|
|
local gpId = groupIdent
|
|
if type(groupIdent) == 'string' and not tonumber(groupIdent) then
|
|
gpId = _Database.Groups[groupIdent].groupId
|
|
end
|
|
|
|
for coa_name, coa_data in pairs(env.mission.coalition) do
|
|
if (coa_name == 'red' or coa_name == 'blue') and type(coa_data) == 'table' then
|
|
if coa_data.country then --there is a country table
|
|
for cntry_id, cntry_data in pairs(coa_data.country) do
|
|
for obj_type_name, obj_type_data in pairs(cntry_data) do
|
|
if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" then -- only these types have points
|
|
if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's a group!
|
|
for group_num, group_data in pairs(obj_type_data.group) do
|
|
if group_data and group_data.groupId == gpId then -- this is the group we are looking for
|
|
if group_data.route and group_data.route.points and #group_data.route.points > 0 then
|
|
local points = {}
|
|
|
|
for point_num, point in pairs(group_data.route.points) do
|
|
local routeData = {}
|
|
if not point.point then
|
|
routeData.x = point.x
|
|
routeData.y = point.y
|
|
else
|
|
routeData.point = point.point --it's possible that the ME could move to the point = Vec2 notation.
|
|
end
|
|
routeData.form = point.action
|
|
routeData.speed = point.speed
|
|
routeData.alt = point.alt
|
|
routeData.alt_type = point.alt_type
|
|
routeData.airdromeId = point.airdromeId
|
|
routeData.helipadId = point.helipadId
|
|
routeData.type = point.type
|
|
routeData.action = point.action
|
|
if task then
|
|
routeData.task = point.task
|
|
end
|
|
points[point_num] = routeData
|
|
end
|
|
|
|
return points
|
|
end
|
|
return
|
|
end --if group_data and group_data.name and group_data.name == 'groupname'
|
|
end --for group_num, group_data in pairs(obj_type_data.group) do
|
|
end --if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then
|
|
end --if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then
|
|
end --for obj_type_name, obj_type_data in pairs(cntry_data) do
|
|
end --for cntry_id, cntry_data in pairs(coa_data.country) do
|
|
end --if coa_data.country then --there is a country table
|
|
end --if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then
|
|
end --for coa_name, coa_data in pairs(mission.coalition) do
|
|
end
|
|
routines.ground.patrolRoute = function(vars)
|
|
|
|
|
|
local tempRoute = {}
|
|
local useRoute = {}
|
|
local gpData = vars.gpData
|
|
if type(gpData) == 'string' then
|
|
gpData = Group.getByName(gpData)
|
|
end
|
|
|
|
local useGroupRoute
|
|
if not vars.useGroupRoute then
|
|
useGroupRoute = vars.gpData
|
|
else
|
|
useGroupRoute = vars.useGroupRoute
|
|
end
|
|
local routeProvided = false
|
|
if not vars.route then
|
|
if useGroupRoute then
|
|
tempRoute = routines.getGroupRoute(useGroupRoute)
|
|
end
|
|
else
|
|
useRoute = vars.route
|
|
local posStart = routines.getLeadPos(gpData)
|
|
useRoute[1] = routines.ground.buildWP(posStart, useRoute[1].action, useRoute[1].speed)
|
|
routeProvided = true
|
|
end
|
|
|
|
|
|
local overRideSpeed = vars.speed or 'default'
|
|
local pType = vars.pType
|
|
local offRoadForm = vars.offRoadForm or 'default'
|
|
local onRoadForm = vars.onRoadForm or 'default'
|
|
|
|
if routeProvided == false and #tempRoute > 0 then
|
|
local posStart = routines.getLeadPos(gpData)
|
|
|
|
|
|
useRoute[#useRoute + 1] = routines.ground.buildWP(posStart, offRoadForm, overRideSpeed)
|
|
for i = 1, #tempRoute do
|
|
local tempForm = tempRoute[i].action
|
|
local tempSpeed = tempRoute[i].speed
|
|
|
|
if offRoadForm == 'default' then
|
|
tempForm = tempRoute[i].action
|
|
end
|
|
if onRoadForm == 'default' then
|
|
onRoadForm = 'On Road'
|
|
end
|
|
if (string.lower(tempRoute[i].action) == 'on road' or string.lower(tempRoute[i].action) == 'onroad' or string.lower(tempRoute[i].action) == 'on_road') then
|
|
tempForm = onRoadForm
|
|
else
|
|
tempForm = offRoadForm
|
|
end
|
|
|
|
if type(overRideSpeed) == 'number' then
|
|
tempSpeed = overRideSpeed
|
|
end
|
|
|
|
|
|
useRoute[#useRoute + 1] = routines.ground.buildWP(tempRoute[i], tempForm, tempSpeed)
|
|
end
|
|
|
|
if pType and string.lower(pType) == 'doubleback' then
|
|
local curRoute = routines.utils.deepCopy(useRoute)
|
|
for i = #curRoute, 2, -1 do
|
|
useRoute[#useRoute + 1] = routines.ground.buildWP(curRoute[i], curRoute[i].action, curRoute[i].speed)
|
|
end
|
|
end
|
|
|
|
useRoute[1].action = useRoute[#useRoute].action -- make it so the first WP matches the last WP
|
|
end
|
|
|
|
local cTask3 = {}
|
|
local newPatrol = {}
|
|
newPatrol.route = useRoute
|
|
newPatrol.gpData = gpData:getName()
|
|
cTask3[#cTask3 + 1] = 'routines.ground.patrolRoute('
|
|
cTask3[#cTask3 + 1] = routines.utils.oneLineSerialize(newPatrol)
|
|
cTask3[#cTask3 + 1] = ')'
|
|
cTask3 = table.concat(cTask3)
|
|
local tempTask = {
|
|
id = 'WrappedAction',
|
|
params = {
|
|
action = {
|
|
id = 'Script',
|
|
params = {
|
|
command = cTask3,
|
|
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
|
|
useRoute[#useRoute].task = tempTask
|
|
routines.goRoute(gpData, useRoute)
|
|
|
|
return
|
|
end
|
|
|
|
routines.ground.patrol = function(gpData, pType, form, speed)
|
|
local vars = {}
|
|
|
|
if type(gpData) == 'table' and gpData:getName() then
|
|
gpData = gpData:getName()
|
|
end
|
|
|
|
vars.useGroupRoute = gpData
|
|
vars.gpData = gpData
|
|
vars.pType = pType
|
|
vars.offRoadForm = form
|
|
vars.speed = speed
|
|
|
|
routines.ground.patrolRoute(vars)
|
|
|
|
return
|
|
end
|
|
|
|
|
|
|
|
Su34Status = { status = {} }
|
|
boardMsgRed = { statusMsg = "" }
|
|
boardMsgAll = { timeMsg = "" }
|
|
SpawnSettings = {}
|
|
Su34MenuPath = {}
|
|
Su34Menus = 0
|
|
|
|
|
|
function Su34AttackCarlVinson(groupName)
|
|
trace.menu("", "Su34AttackCarlVinson")
|
|
local groupSu34 = Group.getByName( groupName )
|
|
local controllerSu34 = groupSu34.getController(groupSu34)
|
|
local groupCarlVinson = Group.getByName("US Carl Vinson #001")
|
|
controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.OPEN_FIRE )
|
|
controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE )
|
|
if groupCarlVinson ~= nil then
|
|
controllerSu34.pushTask(controllerSu34,{id = 'AttackGroup', params = { groupId = groupCarlVinson:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = true}})
|
|
end
|
|
Su34Status.status[groupName] = 1
|
|
MessageToRed( string.format('%s: ',groupName) .. 'Attacking carrier Carl Vinson. ', 10, 'RedStatus' .. groupName )
|
|
end
|
|
|
|
function Su34AttackWest(groupName)
|
|
trace.f("","Su34AttackWest")
|
|
local groupSu34 = Group.getByName( groupName )
|
|
local controllerSu34 = groupSu34.getController(groupSu34)
|
|
local groupShipWest1 = Group.getByName("US Ship West #001")
|
|
local groupShipWest2 = Group.getByName("US Ship West #002")
|
|
controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.OPEN_FIRE )
|
|
controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE )
|
|
if groupShipWest1 ~= nil then
|
|
controllerSu34.pushTask(controllerSu34,{id = 'AttackGroup', params = { groupId = groupShipWest1:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = true}})
|
|
end
|
|
if groupShipWest2 ~= nil then
|
|
controllerSu34.pushTask(controllerSu34,{id = 'AttackGroup', params = { groupId = groupShipWest2:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = true}})
|
|
end
|
|
Su34Status.status[groupName] = 2
|
|
MessageToRed( string.format('%s: ',groupName) .. 'Attacking invading ships in the west. ', 10, 'RedStatus' .. groupName )
|
|
end
|
|
|
|
function Su34AttackNorth(groupName)
|
|
trace.menu("","Su34AttackNorth")
|
|
local groupSu34 = Group.getByName( groupName )
|
|
local controllerSu34 = groupSu34.getController(groupSu34)
|
|
local groupShipNorth1 = Group.getByName("US Ship North #001")
|
|
local groupShipNorth2 = Group.getByName("US Ship North #002")
|
|
local groupShipNorth3 = Group.getByName("US Ship North #003")
|
|
controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.OPEN_FIRE )
|
|
controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE )
|
|
if groupShipNorth1 ~= nil then
|
|
controllerSu34.pushTask(controllerSu34,{id = 'AttackGroup', params = { groupId = groupShipNorth1:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = false}})
|
|
end
|
|
if groupShipNorth2 ~= nil then
|
|
controllerSu34.pushTask(controllerSu34,{id = 'AttackGroup', params = { groupId = groupShipNorth2:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = false}})
|
|
end
|
|
if groupShipNorth3 ~= nil then
|
|
controllerSu34.pushTask(controllerSu34,{id = 'AttackGroup', params = { groupId = groupShipNorth3:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = false}})
|
|
end
|
|
Su34Status.status[groupName] = 3
|
|
MessageToRed( string.format('%s: ',groupName) .. 'Attacking invading ships in the north. ', 10, 'RedStatus' .. groupName )
|
|
end
|
|
|
|
function Su34Orbit(groupName)
|
|
trace.menu("","Su34Orbit")
|
|
local groupSu34 = Group.getByName( groupName )
|
|
local controllerSu34 = groupSu34:getController()
|
|
controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_HOLD )
|
|
controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE )
|
|
controllerSu34:pushTask( {id = 'ControlledTask', params = { task = { id = 'Orbit', params = { pattern = AI.Task.OrbitPattern.RACE_TRACK } }, stopCondition = { duration = 600 } } } )
|
|
Su34Status.status[groupName] = 4
|
|
MessageToRed( string.format('%s: ',groupName) .. 'In orbit and awaiting further instructions. ', 10, 'RedStatus' .. groupName )
|
|
end
|
|
|
|
function Su34TakeOff(groupName)
|
|
trace.menu("","Su34TakeOff")
|
|
local groupSu34 = Group.getByName( groupName )
|
|
local controllerSu34 = groupSu34:getController()
|
|
controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_HOLD )
|
|
controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.BYPASS_AND_ESCAPE )
|
|
Su34Status.status[groupName] = 8
|
|
MessageToRed( string.format('%s: ',groupName) .. 'Take-Off. ', 10, 'RedStatus' .. groupName )
|
|
end
|
|
|
|
function Su34Hold(groupName)
|
|
trace.menu("","Su34Hold")
|
|
local groupSu34 = Group.getByName( groupName )
|
|
local controllerSu34 = groupSu34:getController()
|
|
controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_HOLD )
|
|
controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.BYPASS_AND_ESCAPE )
|
|
Su34Status.status[groupName] = 5
|
|
MessageToRed( string.format('%s: ',groupName) .. 'Holding Weapons. ', 10, 'RedStatus' .. groupName )
|
|
end
|
|
|
|
function Su34RTB(groupName)
|
|
trace.menu("","Su34RTB")
|
|
Su34Status.status[groupName] = 6
|
|
MessageToRed( string.format('%s: ',groupName) .. 'Return to Krasnodar. ', 10, 'RedStatus' .. groupName )
|
|
end
|
|
|
|
function Su34Destroyed(groupName)
|
|
trace.menu("","Su34Destroyed")
|
|
Su34Status.status[groupName] = 7
|
|
MessageToRed( string.format('%s: ',groupName) .. 'Destroyed. ', 30, 'RedStatus' .. groupName )
|
|
end
|
|
|
|
function GroupAlive( groupName )
|
|
trace.menu("","GroupAlive")
|
|
local groupTest = Group.getByName( groupName )
|
|
|
|
local groupExists = false
|
|
|
|
if groupTest then
|
|
groupExists = groupTest:isExist()
|
|
end
|
|
|
|
trace.r( "", "", { groupExists } )
|
|
return groupExists
|
|
end
|
|
|
|
function Su34IsDead()
|
|
trace.f()
|
|
|
|
end
|
|
|
|
function Su34OverviewStatus()
|
|
trace.menu("","Su34OverviewStatus")
|
|
local msg = ""
|
|
local currentStatus = 0
|
|
local Exists = false
|
|
|
|
for groupName, currentStatus in pairs(Su34Status.status) do
|
|
|
|
env.info(('Su34 Overview Status: GroupName = ' .. groupName ))
|
|
Alive = GroupAlive( groupName )
|
|
|
|
if Alive then
|
|
if currentStatus == 1 then
|
|
msg = msg .. string.format("%s: ",groupName)
|
|
msg = msg .. "Attacking carrier Carl Vinson. "
|
|
elseif currentStatus == 2 then
|
|
msg = msg .. string.format("%s: ",groupName)
|
|
msg = msg .. "Attacking supporting ships in the west. "
|
|
elseif currentStatus == 3 then
|
|
msg = msg .. string.format("%s: ",groupName)
|
|
msg = msg .. "Attacking invading ships in the north. "
|
|
elseif currentStatus == 4 then
|
|
msg = msg .. string.format("%s: ",groupName)
|
|
msg = msg .. "In orbit and awaiting further instructions. "
|
|
elseif currentStatus == 5 then
|
|
msg = msg .. string.format("%s: ",groupName)
|
|
msg = msg .. "Holding Weapons. "
|
|
elseif currentStatus == 6 then
|
|
msg = msg .. string.format("%s: ",groupName)
|
|
msg = msg .. "Return to Krasnodar. "
|
|
elseif currentStatus == 7 then
|
|
msg = msg .. string.format("%s: ",groupName)
|
|
msg = msg .. "Destroyed. "
|
|
elseif currentStatus == 8 then
|
|
msg = msg .. string.format("%s: ",groupName)
|
|
msg = msg .. "Take-Off. "
|
|
end
|
|
else
|
|
if currentStatus == 7 then
|
|
msg = msg .. string.format("%s: ",groupName)
|
|
msg = msg .. "Destroyed. "
|
|
else
|
|
Su34Destroyed(groupName)
|
|
end
|
|
end
|
|
end
|
|
|
|
boardMsgRed.statusMsg = msg
|
|
end
|
|
|
|
|
|
function UpdateBoardMsg()
|
|
trace.f()
|
|
Su34OverviewStatus()
|
|
MessageToRed( boardMsgRed.statusMsg, 15, 'RedStatus' )
|
|
end
|
|
|
|
function MusicReset( flg )
|
|
trace.f()
|
|
trigger.action.setUserFlag(95,flg)
|
|
end
|
|
|
|
function PlaneActivate(groupNameFormat, flg)
|
|
trace.f()
|
|
local groupName = groupNameFormat .. string.format("#%03d", trigger.misc.getUserFlag(flg))
|
|
--trigger.action.outText(groupName,10)
|
|
trigger.action.activateGroup(Group.getByName(groupName))
|
|
end
|
|
|
|
function Su34Menu(groupName)
|
|
trace.f()
|
|
|
|
--env.info(( 'Su34Menu(' .. groupName .. ')' ))
|
|
local groupSu34 = Group.getByName( groupName )
|
|
|
|
if Su34Status.status[groupName] == 1 or
|
|
Su34Status.status[groupName] == 2 or
|
|
Su34Status.status[groupName] == 3 or
|
|
Su34Status.status[groupName] == 4 or
|
|
Su34Status.status[groupName] == 5 then
|
|
if Su34MenuPath[groupName] == nil then
|
|
if planeMenuPath == nil then
|
|
planeMenuPath = missionCommands.addSubMenuForCoalition(
|
|
coalition.side.RED,
|
|
"SU-34 anti-ship flights",
|
|
nil
|
|
)
|
|
end
|
|
Su34MenuPath[groupName] = missionCommands.addSubMenuForCoalition(
|
|
coalition.side.RED,
|
|
"Flight " .. groupName,
|
|
planeMenuPath
|
|
)
|
|
|
|
missionCommands.addCommandForCoalition(
|
|
coalition.side.RED,
|
|
"Attack carrier Carl Vinson",
|
|
Su34MenuPath[groupName],
|
|
Su34AttackCarlVinson,
|
|
groupName
|
|
)
|
|
|
|
missionCommands.addCommandForCoalition(
|
|
coalition.side.RED,
|
|
"Attack ships in the west",
|
|
Su34MenuPath[groupName],
|
|
Su34AttackWest,
|
|
groupName
|
|
)
|
|
|
|
missionCommands.addCommandForCoalition(
|
|
coalition.side.RED,
|
|
"Attack ships in the north",
|
|
Su34MenuPath[groupName],
|
|
Su34AttackNorth,
|
|
groupName
|
|
)
|
|
|
|
missionCommands.addCommandForCoalition(
|
|
coalition.side.RED,
|
|
"Hold position and await instructions",
|
|
Su34MenuPath[groupName],
|
|
Su34Orbit,
|
|
groupName
|
|
)
|
|
|
|
missionCommands.addCommandForCoalition(
|
|
coalition.side.RED,
|
|
"Report status",
|
|
Su34MenuPath[groupName],
|
|
Su34OverviewStatus
|
|
)
|
|
end
|
|
else
|
|
if Su34MenuPath[groupName] then
|
|
missionCommands.removeItemForCoalition(coalition.side.RED, Su34MenuPath[groupName])
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Obsolete function, but kept to rework in framework.
|
|
|
|
function ChooseInfantry ( TeleportPrefixTable, TeleportMax )
|
|
trace.f("Spawn")
|
|
--env.info(( 'ChooseInfantry: ' ))
|
|
|
|
TeleportPrefixTableCount = #TeleportPrefixTable
|
|
TeleportPrefixTableIndex = math.random( 1, TeleportPrefixTableCount )
|
|
|
|
--env.info(( 'ChooseInfantry: TeleportPrefixTableIndex = ' .. TeleportPrefixTableIndex .. ' TeleportPrefixTableCount = ' .. TeleportPrefixTableCount .. ' TeleportMax = ' .. TeleportMax ))
|
|
|
|
local TeleportFound = false
|
|
local TeleportLoop = true
|
|
local Index = TeleportPrefixTableIndex
|
|
local TeleportPrefix = ''
|
|
|
|
while TeleportLoop do
|
|
TeleportPrefix = TeleportPrefixTable[Index]
|
|
if SpawnSettings[TeleportPrefix] then
|
|
if SpawnSettings[TeleportPrefix]['SpawnCount'] - 1 < TeleportMax then
|
|
SpawnSettings[TeleportPrefix]['SpawnCount'] = SpawnSettings[TeleportPrefix]['SpawnCount'] + 1
|
|
TeleportFound = true
|
|
else
|
|
TeleportFound = false
|
|
end
|
|
else
|
|
SpawnSettings[TeleportPrefix] = {}
|
|
SpawnSettings[TeleportPrefix]['SpawnCount'] = 0
|
|
TeleportFound = true
|
|
end
|
|
if TeleportFound then
|
|
TeleportLoop = false
|
|
else
|
|
if Index < TeleportPrefixTableCount then
|
|
Index = Index + 1
|
|
else
|
|
TeleportLoop = false
|
|
end
|
|
end
|
|
--env.info(( 'ChooseInfantry: Loop 1 - TeleportPrefix = ' .. TeleportPrefix .. ' Index = ' .. Index ))
|
|
end
|
|
|
|
if TeleportFound == false then
|
|
TeleportLoop = true
|
|
Index = 1
|
|
while TeleportLoop do
|
|
TeleportPrefix = TeleportPrefixTable[Index]
|
|
if SpawnSettings[TeleportPrefix] then
|
|
if SpawnSettings[TeleportPrefix]['SpawnCount'] - 1 < TeleportMax then
|
|
SpawnSettings[TeleportPrefix]['SpawnCount'] = SpawnSettings[TeleportPrefix]['SpawnCount'] + 1
|
|
TeleportFound = true
|
|
else
|
|
TeleportFound = false
|
|
end
|
|
else
|
|
SpawnSettings[TeleportPrefix] = {}
|
|
SpawnSettings[TeleportPrefix]['SpawnCount'] = 0
|
|
TeleportFound = true
|
|
end
|
|
if TeleportFound then
|
|
TeleportLoop = false
|
|
else
|
|
if Index < TeleportPrefixTableIndex then
|
|
Index = Index + 1
|
|
else
|
|
TeleportLoop = false
|
|
end
|
|
end
|
|
--env.info(( 'ChooseInfantry: Loop 2 - TeleportPrefix = ' .. TeleportPrefix .. ' Index = ' .. Index ))
|
|
end
|
|
end
|
|
|
|
local TeleportGroupName = ''
|
|
if TeleportFound == true then
|
|
TeleportGroupName = TeleportPrefix .. string.format("#%03d", SpawnSettings[TeleportPrefix]['SpawnCount'] )
|
|
else
|
|
TeleportGroupName = ''
|
|
end
|
|
|
|
--env.info(('ChooseInfantry: TeleportGroupName = ' .. TeleportGroupName ))
|
|
--env.info(('ChooseInfantry: return'))
|
|
|
|
return TeleportGroupName
|
|
end
|
|
|
|
SpawnedInfantry = 0
|
|
|
|
function LandCarrier ( CarrierGroup, LandingZonePrefix )
|
|
trace.f()
|
|
--env.info(( 'LandCarrier: ' ))
|
|
--env.info(( 'LandCarrier: CarrierGroup = ' .. CarrierGroup:getName() ))
|
|
--env.info(( 'LandCarrier: LandingZone = ' .. LandingZonePrefix ))
|
|
|
|
local controllerGroup = CarrierGroup:getController()
|
|
|
|
local LandingZone = trigger.misc.getZone(LandingZonePrefix)
|
|
local LandingZonePos = {}
|
|
LandingZonePos.x = LandingZone.point.x + math.random(LandingZone.radius * -1, LandingZone.radius)
|
|
LandingZonePos.y = LandingZone.point.z + math.random(LandingZone.radius * -1, LandingZone.radius)
|
|
|
|
controllerGroup:pushTask( { id = 'Land', params = { point = LandingZonePos, durationFlag = true, duration = 10 } } )
|
|
|
|
--env.info(( 'LandCarrier: end' ))
|
|
end
|
|
|
|
EscortCount = 0
|
|
function EscortCarrier ( CarrierGroup, EscortPrefix, EscortLastWayPoint, EscortEngagementDistanceMax, EscortTargetTypes )
|
|
trace.f()
|
|
--env.info(( 'EscortCarrier: ' ))
|
|
--env.info(( 'EscortCarrier: CarrierGroup = ' .. CarrierGroup:getName() ))
|
|
--env.info(( 'EscortCarrier: EscortPrefix = ' .. EscortPrefix ))
|
|
|
|
local CarrierName = CarrierGroup:getName()
|
|
|
|
local EscortMission = {}
|
|
local CarrierMission = {}
|
|
|
|
local EscortMission = SpawnMissionGroup( EscortPrefix )
|
|
local CarrierMission = SpawnMissionGroup( CarrierGroup:getName() )
|
|
|
|
if EscortMission ~= nil and CarrierMission ~= nil then
|
|
|
|
EscortCount = EscortCount + 1
|
|
EscortMissionName = string.format( EscortPrefix .. '#Escort %s', CarrierName )
|
|
EscortMission.name = EscortMissionName
|
|
EscortMission.groupId = nil
|
|
EscortMission.lateActivation = false
|
|
EscortMission.taskSelected = false
|
|
|
|
local EscortUnits = #EscortMission.units
|
|
for u = 1, EscortUnits do
|
|
EscortMission.units[u].name = string.format( EscortPrefix .. '#Escort %s %02d', CarrierName, u )
|
|
EscortMission.units[u].unitId = nil
|
|
end
|
|
|
|
|
|
EscortMission.route.points[1].task = { id = "ComboTask",
|
|
params =
|
|
{
|
|
tasks =
|
|
{
|
|
[1] =
|
|
{
|
|
enabled = true,
|
|
auto = false,
|
|
id = "Escort",
|
|
number = 1,
|
|
params =
|
|
{
|
|
lastWptIndexFlagChangedManually = false,
|
|
groupId = CarrierGroup:getID(),
|
|
lastWptIndex = nil,
|
|
lastWptIndexFlag = false,
|
|
engagementDistMax = EscortEngagementDistanceMax,
|
|
targetTypes = EscortTargetTypes,
|
|
pos =
|
|
{
|
|
y = 20,
|
|
x = 20,
|
|
z = 0,
|
|
} -- end of ["pos"]
|
|
} -- end of ["params"]
|
|
} -- end of [1]
|
|
} -- end of ["tasks"]
|
|
} -- end of ["params"]
|
|
} -- end of ["task"]
|
|
|
|
SpawnGroupAdd( EscortPrefix, EscortMission )
|
|
|
|
end
|
|
end
|
|
|
|
function SendMessageToCarrier( CarrierGroup, CarrierMessage )
|
|
trace.f()
|
|
|
|
if CarrierGroup ~= nil then
|
|
MessageToGroup( CarrierGroup, CarrierMessage, 30, 'Carrier/' .. CarrierGroup:getName() )
|
|
end
|
|
|
|
end
|
|
|
|
function MessageToGroup( MsgGroup, MsgText, MsgTime, MsgName )
|
|
trace.f()
|
|
|
|
if type(MsgGroup) == 'string' then
|
|
--env.info( 'MessageToGroup: Converted MsgGroup string "' .. MsgGroup .. '" into a Group structure.' )
|
|
MsgGroup = Group.getByName( MsgGroup )
|
|
end
|
|
|
|
if MsgGroup ~= nil then
|
|
local MsgTable = {}
|
|
MsgTable.text = MsgText
|
|
MsgTable.displayTime = MsgTime
|
|
MsgTable.msgFor = { units = { MsgGroup:getUnits()[1]:getName() } }
|
|
MsgTable.name = MsgName
|
|
--routines.message.add( MsgTable )
|
|
--env.info(('MessageToGroup: Message sent to ' .. MsgGroup:getUnits()[1]:getName() .. ' -> ' .. MsgText ))
|
|
end
|
|
end
|
|
|
|
function MessageToUnit( UnitName, MsgText, MsgTime, MsgName )
|
|
trace.f()
|
|
|
|
if UnitName ~= nil then
|
|
local MsgTable = {}
|
|
MsgTable.text = MsgText
|
|
MsgTable.displayTime = MsgTime
|
|
MsgTable.msgFor = { units = { UnitName } }
|
|
MsgTable.name = MsgName
|
|
--routines.message.add( MsgTable )
|
|
end
|
|
end
|
|
|
|
function MessageToAll( MsgText, MsgTime, MsgName )
|
|
trace.f()
|
|
|
|
MESSAGE:New( MsgText, "Message", MsgTime, MsgName ):ToCoalition( coalition.side.RED ):ToCoalition( coalition.side.BLUE )
|
|
end
|
|
|
|
function MessageToRed( MsgText, MsgTime, MsgName )
|
|
trace.f()
|
|
|
|
MESSAGE:New( MsgText, "To Red Coalition", MsgTime, MsgName ):ToCoalition( coalition.side.RED )
|
|
end
|
|
|
|
function MessageToBlue( MsgText, MsgTime, MsgName )
|
|
trace.f()
|
|
|
|
MESSAGE:New( MsgText, "To Blue Coalition", MsgTime, MsgName ):ToCoalition( coalition.side.RED )
|
|
end
|
|
|
|
function getCarrierHeight( CarrierGroup )
|
|
trace.f()
|
|
|
|
if CarrierGroup ~= nil then
|
|
if table.getn(CarrierGroup:getUnits()) == 1 then
|
|
local CarrierUnit = CarrierGroup:getUnits()[1]
|
|
local CurrentPoint = CarrierUnit:getPoint()
|
|
|
|
local CurrentPosition = { x = CurrentPoint.x, y = CurrentPoint.z }
|
|
local CarrierHeight = CurrentPoint.y
|
|
|
|
local LandHeight = land.getHeight( CurrentPosition )
|
|
|
|
--env.info(( 'CarrierHeight: LandHeight = ' .. LandHeight .. ' CarrierHeight = ' .. CarrierHeight ))
|
|
|
|
return CarrierHeight - LandHeight
|
|
else
|
|
return 999999
|
|
end
|
|
else
|
|
return 999999
|
|
end
|
|
|
|
end
|
|
|
|
_MusicTable = {}
|
|
_MusicTable.Files = {}
|
|
_MusicTable.Queue = {}
|
|
_MusicTable.FileCnt = 0
|
|
|
|
|
|
function MusicRegister( SndRef, SndFile, SndTime )
|
|
trace.f()
|
|
|
|
env.info(( 'MusicRegister: SndRef = ' .. SndRef ))
|
|
env.info(( 'MusicRegister: SndFile = ' .. SndFile ))
|
|
env.info(( 'MusicRegister: SndTime = ' .. SndTime ))
|
|
|
|
|
|
_MusicTable.FileCnt = _MusicTable.FileCnt + 1
|
|
|
|
_MusicTable.Files[_MusicTable.FileCnt] = {}
|
|
_MusicTable.Files[_MusicTable.FileCnt].Ref = SndRef
|
|
_MusicTable.Files[_MusicTable.FileCnt].File = SndFile
|
|
_MusicTable.Files[_MusicTable.FileCnt].Time = SndTime
|
|
|
|
if not _MusicTable.Function then
|
|
_MusicTable.Function = routines.scheduleFunction( MusicScheduler, { }, timer.getTime() + 10, 10)
|
|
end
|
|
|
|
end
|
|
|
|
function MusicToPlayer( SndRef, PlayerName, SndContinue )
|
|
trace.f()
|
|
|
|
--env.info(( 'MusicToPlayer: SndRef = ' .. SndRef ))
|
|
|
|
local PlayerUnits = AlivePlayerUnits()
|
|
for PlayerUnitIdx, PlayerUnit in pairs(PlayerUnits) do
|
|
local PlayerUnitName = PlayerUnit:getPlayerName()
|
|
--env.info(( 'MusicToPlayer: PlayerUnitName = ' .. PlayerUnitName ))
|
|
if PlayerName == PlayerUnitName then
|
|
PlayerGroup = PlayerUnit:getGroup()
|
|
if PlayerGroup then
|
|
--env.info(( 'MusicToPlayer: PlayerGroup = ' .. PlayerGroup:getName() ))
|
|
MusicToGroup( SndRef, PlayerGroup, SndContinue )
|
|
end
|
|
break
|
|
end
|
|
end
|
|
|
|
--env.info(( 'MusicToPlayer: end' ))
|
|
|
|
end
|
|
|
|
function MusicToGroup( SndRef, SndGroup, SndContinue )
|
|
trace.f()
|
|
|
|
--env.info(( 'MusicToGroup: SndRef = ' .. SndRef ))
|
|
|
|
if SndGroup ~= nil then
|
|
if _MusicTable and _MusicTable.FileCnt > 0 then
|
|
if SndGroup:isExist() then
|
|
if MusicCanStart(SndGroup:getUnit(1):getPlayerName()) then
|
|
--env.info(( 'MusicToGroup: OK for Sound.' ))
|
|
local SndIdx = 0
|
|
if SndRef == '' then
|
|
--env.info(( 'MusicToGroup: SndRef as empty. Queueing at random.' ))
|
|
SndIdx = math.random( 1, _MusicTable.FileCnt )
|
|
else
|
|
for SndIdx = 1, _MusicTable.FileCnt do
|
|
if _MusicTable.Files[SndIdx].Ref == SndRef then
|
|
break
|
|
end
|
|
end
|
|
end
|
|
--env.info(( 'MusicToGroup: SndIdx = ' .. SndIdx ))
|
|
--env.info(( 'MusicToGroup: Queueing Music ' .. _MusicTable.Files[SndIdx].File .. ' for Group ' .. SndGroup:getID() ))
|
|
trigger.action.outSoundForGroup( SndGroup:getID(), _MusicTable.Files[SndIdx].File )
|
|
MessageToGroup( SndGroup, 'Playing ' .. _MusicTable.Files[SndIdx].File, 15, 'Music-' .. SndGroup:getUnit(1):getPlayerName() )
|
|
|
|
local SndQueueRef = SndGroup:getUnit(1):getPlayerName()
|
|
if _MusicTable.Queue[SndQueueRef] == nil then
|
|
_MusicTable.Queue[SndQueueRef] = {}
|
|
end
|
|
_MusicTable.Queue[SndQueueRef].Start = timer.getTime()
|
|
_MusicTable.Queue[SndQueueRef].PlayerName = SndGroup:getUnit(1):getPlayerName()
|
|
_MusicTable.Queue[SndQueueRef].Group = SndGroup
|
|
_MusicTable.Queue[SndQueueRef].ID = SndGroup:getID()
|
|
_MusicTable.Queue[SndQueueRef].Ref = SndIdx
|
|
_MusicTable.Queue[SndQueueRef].Continue = SndContinue
|
|
_MusicTable.Queue[SndQueueRef].Type = Group
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function MusicCanStart(PlayerName)
|
|
trace.f()
|
|
|
|
--env.info(( 'MusicCanStart:' ))
|
|
|
|
local MusicOut = false
|
|
|
|
if _MusicTable['Queue'] ~= nil and _MusicTable.FileCnt > 0 then
|
|
--env.info(( 'MusicCanStart: PlayerName = ' .. PlayerName ))
|
|
local PlayerFound = false
|
|
local MusicStart = 0
|
|
local MusicTime = 0
|
|
for SndQueueIdx, SndQueue in pairs( _MusicTable.Queue ) do
|
|
if SndQueue.PlayerName == PlayerName then
|
|
PlayerFound = true
|
|
MusicStart = SndQueue.Start
|
|
MusicTime = _MusicTable.Files[SndQueue.Ref].Time
|
|
break
|
|
end
|
|
end
|
|
if PlayerFound then
|
|
--env.info(( 'MusicCanStart: MusicStart = ' .. MusicStart ))
|
|
--env.info(( 'MusicCanStart: MusicTime = ' .. MusicTime ))
|
|
--env.info(( 'MusicCanStart: timer.getTime() = ' .. timer.getTime() ))
|
|
|
|
if MusicStart + MusicTime <= timer.getTime() then
|
|
MusicOut = true
|
|
end
|
|
else
|
|
MusicOut = true
|
|
end
|
|
end
|
|
|
|
if MusicOut then
|
|
--env.info(( 'MusicCanStart: true' ))
|
|
else
|
|
--env.info(( 'MusicCanStart: false' ))
|
|
end
|
|
|
|
return MusicOut
|
|
end
|
|
|
|
function MusicScheduler()
|
|
trace.scheduled("", "MusicScheduler")
|
|
|
|
--env.info(( 'MusicScheduler:' ))
|
|
if _MusicTable['Queue'] ~= nil and _MusicTable.FileCnt > 0 then
|
|
--env.info(( 'MusicScheduler: Walking Sound Queue.'))
|
|
for SndQueueIdx, SndQueue in pairs( _MusicTable.Queue ) do
|
|
if SndQueue.Continue then
|
|
if MusicCanStart(SndQueue.PlayerName) then
|
|
--env.info(('MusicScheduler: MusicToGroup'))
|
|
MusicToPlayer( '', SndQueue.PlayerName, true )
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
|
|
env.info(( 'Init: Scripts Loaded v1.1' ))
|
|
|