mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
Improve the consistency of the module intros to the most commonly used version (single dash). Add missing module information (abbreviated where none existed previously). Fix broken documentation links Make module names correspond to filenames (and fix links). Fix typos.
2359 lines
75 KiB
Lua
2359 lines
75 KiB
Lua
--- Various routines
|
|
-- @module Utilities.Routines
|
|
-- @image MOOSE.JPG
|
|
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.round = function( number, decimals )
|
|
local power = 10 ^ decimals
|
|
return math.floor( number * power ) / power
|
|
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
|
|
|
|
lookup_table = {}
|
|
|
|
local function _Serialize( tbl )
|
|
|
|
if type( tbl ) == 'table' then -- function only works for tables!
|
|
|
|
if lookup_table[tbl] then
|
|
return lookup_table[object]
|
|
end
|
|
|
|
local tbl_str = {}
|
|
|
|
lookup_table[tbl] = tbl_str
|
|
|
|
tbl_str[#tbl_str + 1] = '{'
|
|
|
|
for ind, val in pairs( tbl ) do -- serialize its fields
|
|
local ind_str = {}
|
|
if type( ind ) == "number" then
|
|
ind_str[#ind_str + 1] = '['
|
|
ind_str[#ind_str + 1] = tostring( ind )
|
|
ind_str[#ind_str + 1] = ']='
|
|
else -- must be a string
|
|
ind_str[#ind_str + 1] = '['
|
|
ind_str[#ind_str + 1] = routines.utils.basicSerialize( ind )
|
|
ind_str[#ind_str + 1] = ']='
|
|
end
|
|
|
|
local val_str = {}
|
|
if ((type( val ) == 'number') or (type( val ) == 'boolean')) then
|
|
val_str[#val_str + 1] = tostring( val )
|
|
val_str[#val_str + 1] = ','
|
|
tbl_str[#tbl_str + 1] = table.concat( ind_str )
|
|
tbl_str[#tbl_str + 1] = table.concat( val_str )
|
|
elseif type( val ) == 'string' then
|
|
val_str[#val_str + 1] = routines.utils.basicSerialize( val )
|
|
val_str[#val_str + 1] = ','
|
|
tbl_str[#tbl_str + 1] = table.concat( ind_str )
|
|
tbl_str[#tbl_str + 1] = table.concat( val_str )
|
|
elseif type( val ) == 'nil' then -- won't ever happen, right?
|
|
val_str[#val_str + 1] = 'nil,'
|
|
tbl_str[#tbl_str + 1] = table.concat( ind_str )
|
|
tbl_str[#tbl_str + 1] = table.concat( val_str )
|
|
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
|
|
|
|
val_str[#val_str + 1] = _Serialize( val )
|
|
val_str[#val_str + 1] = ',' -- I think this is right, I just added it
|
|
tbl_str[#tbl_str + 1] = table.concat( ind_str )
|
|
tbl_str[#tbl_str + 1] = table.concat( val_str )
|
|
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
|
|
if type( tbl ) == 'string' then
|
|
return tbl
|
|
else
|
|
return tostring( tbl )
|
|
end
|
|
end
|
|
end
|
|
|
|
local objectreturn = _Serialize( tbl )
|
|
return objectreturn
|
|
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( '%s', s:gsub( "%%", "%%%%" ) )
|
|
return s
|
|
end
|
|
end
|
|
end
|
|
|
|
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
|
|
|
|
-- 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
|
|
|
|
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.IsUnitNearZonesRadius( TransportUnit, LandingZones, ZoneRadius )
|
|
-- 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 <= ZoneRadius) 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 <= ZoneRadius) 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, ReferencePosition, 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 ReferenceP = ReferencePosition.p
|
|
|
|
if (((CargoPos.x - ReferenceP.x) ^ 2 + (CargoPos.z - ReferenceP.z) ^ 2) ^ 0.5 <= Radius) then
|
|
else
|
|
Valid = false
|
|
end
|
|
|
|
return Valid
|
|
end
|
|
|
|
function routines.IsPartOfGroupInRadius( CargoGroup, ReferencePosition, Radius )
|
|
-- trace.f()
|
|
|
|
local Valid = true
|
|
|
|
Valid = routines.ValidateGroup( CargoGroup, "CargoGroup", 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 ReferenceP = ReferencePosition.p
|
|
-- env.info( 'routines.IsPartOfGroupInRadius: ReferenceGroupPos.x = ' .. ReferenceGroupPos.x .. ' ReferenceGroupPos.z = ' .. ReferenceGroupPos.z )
|
|
|
|
if (((CargoUnitPos.x - ReferenceP.x) ^ 2 + (CargoUnitPos.z - ReferenceP.z) ^ 2) ^ 0.5 <= Radius) then
|
|
else
|
|
Valid = false
|
|
break
|
|
end
|
|
end
|
|
|
|
return Valid
|
|
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.Templates.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 env.mission.version > 7 then
|
|
routeData.name = env.getValueDictByKey( point.name )
|
|
else
|
|
routeData.name = point.name
|
|
end
|
|
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 )
|
|
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 )
|
|
end
|
|
|
|
function routines.GetUnitHeight( CheckUnit )
|
|
-- trace.f( "routines" )
|
|
|
|
local UnitPoint = CheckUnit:getPoint()
|
|
local UnitPosition = { x = UnitPoint.x, y = UnitPoint.z }
|
|
local UnitHeight = UnitPoint.y
|
|
|
|
local LandHeight = land.getHeight( UnitPosition )
|
|
|
|
-- env.info(( 'CarrierHeight: LandHeight = ' .. LandHeight .. ' CarrierHeight = ' .. CarrierHeight ))
|
|
|
|
-- trace.f( "routines", "Unit Height = " .. UnitHeight - LandHeight )
|
|
|
|
return UnitHeight - LandHeight
|
|
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, MsgTime, "Message" ):ToCoalition( coalition.side.RED ):ToCoalition( coalition.side.BLUE )
|
|
end
|
|
|
|
function MessageToRed( MsgText, MsgTime, MsgName )
|
|
-- trace.f()
|
|
|
|
MESSAGE:New( MsgText, MsgTime, "To Red Coalition" ):ToCoalition( coalition.side.RED )
|
|
end
|
|
|
|
function MessageToBlue( MsgText, MsgTime, MsgName )
|
|
-- trace.f()
|
|
|
|
MESSAGE:New( MsgText, MsgTime, "To Blue Coalition" ):ToCoalition( coalition.side.BLUE )
|
|
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
|
|
|
|
function GetUnitHeight( CheckUnit )
|
|
-- trace.f()
|
|
|
|
local UnitPoint = CheckUnit:getPoint()
|
|
local UnitPosition = { x = CurrentPoint.x, y = CurrentPoint.z }
|
|
local UnitHeight = CurrentPoint.y
|
|
|
|
local LandHeight = land.getHeight( CurrentPosition )
|
|
|
|
-- env.info(( 'CarrierHeight: LandHeight = ' .. LandHeight .. ' CarrierHeight = ' .. CarrierHeight ))
|
|
|
|
return UnitHeight - LandHeight
|
|
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') )
|