mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
Introduces cargo weight and draw argument properties to units across backend, frontend, and Python API. Adds related commands, data extraction, and registration logic, enabling setting and reading of cargo weight and custom draw arguments for units. Includes new API examples and updates to interfaces, data types, and Lua backend for full feature integration.
1766 lines
56 KiB
Lua
1766 lines
56 KiB
Lua
local version = "{{OLYMPUS_VERSION_NUMBER}}.{{OLYMPUS_COMMIT_HASH}}"
|
|
|
|
local debug = false -- True enables debug printing using DCS messages
|
|
|
|
-- .dll related variables
|
|
Olympus.OlympusDLL = nil
|
|
Olympus.DLLsloaded = false
|
|
|
|
-- Logger reference
|
|
Olympus.log = mist.Logger:new("Olympus", 'info')
|
|
|
|
-- Data structures for transfer to .dll
|
|
Olympus.missionData = {}
|
|
Olympus.unitsData = {}
|
|
Olympus.weaponsData = {}
|
|
Olympus.drawingsByLayer = {}
|
|
Olympus.executionResults = {}
|
|
|
|
-- Units data structures
|
|
Olympus.unitCounter = 1 -- Counter to generate unique names
|
|
Olympus.cloneDatabase = {} -- Database of spawn options, used for units cloning
|
|
Olympus.unitIndex = 0 -- Counter used to spread the computational load of data retrievial from DCS
|
|
Olympus.unitStep = 50 -- Max number of units that get updated each cycle
|
|
Olympus.units = {} -- Table holding references to all the currently existing units
|
|
Olympus.unitsInitialLife = {} -- getLife0 returns 0 for ships, so we need to store the initial life of units
|
|
Olympus.drawArguments = {} -- Table that sets what drawArguments to read for each unit
|
|
|
|
Olympus.weaponIndex = 0 -- Counter used to spread the computational load of data retrievial from DCS
|
|
Olympus.weaponStep = 50 -- Max number of weapons that get updated each cycle
|
|
Olympus.weapons = {} -- Table holding references to all the currently existing weapons
|
|
|
|
-- Spots (laser/IR) data
|
|
Olympus.spots = {}
|
|
Olympus.spotsCounter = 1
|
|
|
|
-- Miscellaneous initializations
|
|
Olympus.missionStartTime = DCS.getRealTime()
|
|
Olympus.napalmCounter = 1
|
|
Olympus.fireCounter = 1
|
|
|
|
-- Load the lua file system
|
|
local lfs = require('lfs')
|
|
|
|
------------------------------------------------------------------------------------------------------
|
|
-- Olympus functions
|
|
------------------------------------------------------------------------------------------------------
|
|
-- Print a debug message if the debug option is true
|
|
function Olympus.debug(message, displayFor)
|
|
if debug == true then
|
|
Olympus.log:info(message)
|
|
trigger.action.outText(message, displayFor)
|
|
end
|
|
end
|
|
|
|
-- Print a notify message
|
|
function Olympus.notify(message, displayFor)
|
|
Olympus.log:info(message)
|
|
trigger.action.outText(message, displayFor)
|
|
end
|
|
|
|
-- Loads the olympus .dll
|
|
function Olympus.loadDLLs()
|
|
-- Add the .dll paths
|
|
package.cpath = package.cpath..';'..Olympus.instancePath..'?.dll;'
|
|
|
|
local status
|
|
status, Olympus.OlympusDLL = pcall(require, 'olympus')
|
|
if status then
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
-- Gets a unit class reference from a given ObjectID (the ID used by Olympus for unit referencing)
|
|
function Olympus.getUnitByID(ID)
|
|
return Olympus.units[ID];
|
|
end
|
|
|
|
-- Gets the ID of the first country that belongs to a coalition
|
|
function Olympus.getCountryIDByCoalition(coalitionString)
|
|
for countryName, countryId in pairs(country.id) do
|
|
if coalition.getCountryCoalition(countryId) == Olympus.getCoalitionIDByCoalition(coalitionString) then
|
|
return countryId
|
|
end
|
|
end
|
|
return 0
|
|
end
|
|
|
|
-- Gets the coalition ID of a coalition
|
|
function Olympus.getCoalitionIDByCoalition(coalitionString)
|
|
local coalitionID = 0
|
|
if coalitionString == "red" then
|
|
coalitionID = 1
|
|
elseif coalitionString == "blue" then
|
|
coalitionID = 2
|
|
end
|
|
return coalitionID
|
|
end
|
|
|
|
-- Gets the coalition name from the coalition ID
|
|
function Olympus.getCoalitionByCoalitionID(coalitionID)
|
|
local coalitionString = "neutral"
|
|
if coalitionID == 1 then
|
|
coalitionString = "red"
|
|
elseif coalitionID == 2 then
|
|
coalitionString = "blue"
|
|
end
|
|
return coalitionString
|
|
end
|
|
|
|
-- Builds a valid enroute task depending on the provided options
|
|
function Olympus.buildEnrouteTask(options)
|
|
local task = nil
|
|
-- Engage specific target by ID. Checks if target exists.
|
|
if options['id'] == 'EngageUnit' and options['targetID'] then
|
|
local target = Olympus.getUnitByID(options['targetID'])
|
|
if target and target:isExist() then
|
|
task = {
|
|
id = 'EngageUnit',
|
|
params = {
|
|
unitId = options['targetID'],
|
|
}
|
|
}
|
|
end
|
|
-- Start being an active tanker
|
|
elseif options['id'] == 'Tanker' then
|
|
task = {
|
|
id = 'Tanker',
|
|
params = {},
|
|
}
|
|
-- Start being an active AWACS
|
|
elseif options['id'] == 'AWACS' then
|
|
task = {
|
|
id = 'AWACS',
|
|
params = {},
|
|
}
|
|
end
|
|
return task
|
|
end
|
|
|
|
-- Builds a valid main task depending on the provided options
|
|
function Olympus.buildTask(groupName, options)
|
|
local task = nil
|
|
|
|
local group = Group.getByName(groupName)
|
|
|
|
-- Combo tasks require nested tables
|
|
if (Olympus.isArray(options)) then
|
|
local tasks = {}
|
|
for idx, subOptions in pairs(options) do
|
|
tasks[idx] = Olympus.buildTask(groupName, subOptions) or Olympus.buildEnrouteTask(subOptions)
|
|
end
|
|
task = {
|
|
id = 'ComboTask',
|
|
params = {
|
|
tasks = tasks
|
|
}
|
|
}
|
|
Olympus.debug(Olympus.serializeTable(task), 30)
|
|
else
|
|
-- Follow a unit in formation with a given offset
|
|
if options['id'] == 'FollowUnit' and options['leaderID'] and options['offset'] then
|
|
local leader = Olympus.getUnitByID(options['leaderID'])
|
|
if leader and leader:isExist() then
|
|
task = {
|
|
id = 'Follow',
|
|
params = {
|
|
groupId = leader:getGroup():getID(),
|
|
pos = options['offset'],
|
|
lastWptIndexFlag = false,
|
|
lastWptIndex = 1
|
|
}
|
|
}
|
|
end
|
|
-- Go refuel to the nearest tanker. If the unit can't refuel it will RTB
|
|
elseif options['id'] == 'Refuel' then
|
|
task = {
|
|
id = 'Refueling',
|
|
params = {}
|
|
}
|
|
-- Orbit in place at a given altitude and with a given pattern
|
|
elseif options['id'] == 'Orbit' then
|
|
task = {
|
|
id = 'Orbit',
|
|
params = {
|
|
pattern = options['pattern'] or "Circle"
|
|
}
|
|
}
|
|
|
|
if options['pattern'] == 'Race-Track' then
|
|
local heading = options['heading'] or 0
|
|
local length = options['length'] or 20000
|
|
if group ~= nil then
|
|
local groupPos = mist.getLeadPos(group)
|
|
task['params']['point'] = {x = groupPos.x, y = groupPos.z}
|
|
task['params']['point2'] = {x = groupPos.x + math.cos(heading) * length, y = groupPos.z + math.sin(heading) * length}
|
|
end
|
|
end
|
|
|
|
-- Compute the altitude depending on the altitude type
|
|
if options['altitude'] then
|
|
if options ['altitudeType'] then
|
|
if options ['altitudeType'] == "AGL" then
|
|
local groundHeight = 0
|
|
if group ~= nil then
|
|
local groupPos = mist.getLeadPos(group)
|
|
groundHeight = land.getHeight({x = groupPos.x, y = groupPos.z})
|
|
end
|
|
task['params']['altitude'] = groundHeight + options['altitude']
|
|
else
|
|
task['params']['altitude'] = options['altitude']
|
|
end
|
|
else
|
|
task['params']['altitude'] = options['altitude']
|
|
end
|
|
end
|
|
|
|
-- Compute the speed depending on the speed type. CAS calculation is only available if the altitude is also available
|
|
if options['speed'] then
|
|
-- Simplified formula to compute CAS from GS
|
|
local speed = options['speed']
|
|
if options['speedType'] and options['speedType'] == "CAS" and task['params']['altitude'] then
|
|
speed = speed * (1 + 0.02 * task['params']['altitude'] / 0.3048 / 1000)
|
|
end
|
|
task['params']['speed'] = speed
|
|
end
|
|
-- Bomb a specific location
|
|
elseif options['id'] == 'Bombing' and options['lat'] and options['lng'] then
|
|
local point = coord.LLtoLO(options['lat'], options['lng'], 0)
|
|
task = {
|
|
id = 'Bombing',
|
|
params = {
|
|
point = {x = point.x, y = point.z},
|
|
attackQty = 1
|
|
}
|
|
}
|
|
-- Perform carpet bombing at a specific location
|
|
elseif options['id'] == 'CarpetBombing' and options['lat'] and options['lng'] then
|
|
local point = coord.LLtoLO(options['lat'], options['lng'], 0)
|
|
task = {
|
|
id = 'CarpetBombing',
|
|
params = {
|
|
x = point.x,
|
|
y = point.z,
|
|
carpetLength = 1000,
|
|
attackType = 'Carpet',
|
|
expend = "All",
|
|
attackQty = 1,
|
|
attackQtyLimit = true
|
|
}
|
|
}
|
|
-- Fire at a specific point
|
|
elseif options['id'] == 'FireAtPoint' and options['lat'] and options['lng'] and options['radius'] then
|
|
local point = coord.LLtoLO(options['lat'], options['lng'], 0)
|
|
local expendQtyEnabled = false
|
|
local expendQty = 0
|
|
|
|
if options['expendQty'] then
|
|
expendQtyEnabled = true
|
|
expendQty = options['expendQty']
|
|
end
|
|
|
|
if options['alt'] then
|
|
task = {
|
|
id = 'FireAtPoint',
|
|
params = {
|
|
point = {x = point.x, y = point.z},
|
|
radius = options['radius'],
|
|
altitude = options['alt'],
|
|
alt_type = 0, -- ASL
|
|
expendQtyEnabled = expendQtyEnabled,
|
|
expendQty = expendQty
|
|
}
|
|
}
|
|
else
|
|
task = {
|
|
id = 'FireAtPoint',
|
|
params = {
|
|
point = {x = point.x, y = point.z},
|
|
radius = options['radius'],
|
|
expendQtyEnabled = expendQtyEnabled,
|
|
expendQty = expendQty
|
|
}
|
|
}
|
|
end
|
|
-- Land at a specific point
|
|
elseif options['id'] == 'LandAtPoint' then
|
|
local point = coord.LLtoLO(options['lat'], options['lng'], 0)
|
|
task = {
|
|
id = 'Land',
|
|
params = {
|
|
point = {x = point.x, y = point.z},
|
|
}
|
|
}
|
|
-- Attack unit
|
|
elseif options['id'] == 'AttackUnit' and options['unitID'] then
|
|
task = {
|
|
id = 'AttackUnit',
|
|
params = {
|
|
unitId = options['unitID'],
|
|
}
|
|
}
|
|
end
|
|
end
|
|
return task
|
|
end
|
|
|
|
-- Move a unit. Since many tasks in DCS are Enroute tasks, this function is an important way to control the unit AI
|
|
function Olympus.move(groupName, lat, lng, altitude, altitudeType, speed, speedType, category, taskOptions)
|
|
Olympus.debug("Olympus.move " .. groupName .. " (" .. lat .. ", " .. lng ..") " .. altitude .. "m " .. altitudeType .. " ".. speed .. "m/s " .. category .. " " .. Olympus.serializeTable(taskOptions), 2)
|
|
local group = Group.getByName(groupName)
|
|
if group ~= nil then
|
|
if category == "Aircraft" then
|
|
local startPoint = mist.getLeadPos(group)
|
|
local endPoint = coord.LLtoLO(lat, lng, 0)
|
|
|
|
-- 'AGL' mode does not appear to work in the buildWP function. This is a crude approximation
|
|
if altitudeType == "AGL" then
|
|
altitude = land.getHeight({x = endPoint.x, y = endPoint.z}) + altitude
|
|
end
|
|
|
|
-- Simplified formula to compute CAS from GS
|
|
if speedType == "CAS" then
|
|
speed = speed * (1 + 0.02 * altitude / 0.3048 / 1000)
|
|
end
|
|
|
|
-- Create the path
|
|
local path = {
|
|
[1] = mist.fixedWing.buildWP(startPoint, turningPoint, speed, altitude, 'BARO'),
|
|
[2] = mist.fixedWing.buildWP(endPoint, turningPoint, speed, altitude, 'BARO')
|
|
}
|
|
|
|
-- If a task exists assign it to the controller
|
|
if taskOptions then
|
|
local task = Olympus.buildEnrouteTask(taskOptions)
|
|
if task then
|
|
path[1].task = task
|
|
path[2].task = task
|
|
end
|
|
end
|
|
|
|
-- Assign the mission task to the controller
|
|
local missionTask = {
|
|
id = 'Mission',
|
|
params = {
|
|
route = {
|
|
points = mist.utils.deepCopy(path),
|
|
},
|
|
},
|
|
}
|
|
local groupCon = group:getController()
|
|
if groupCon then
|
|
groupCon:setTask(missionTask)
|
|
end
|
|
Olympus.debug("Olympus.move executed successfully on Aircraft", 2)
|
|
elseif category == "Helicopter" then
|
|
local startPoint = mist.getLeadPos(group)
|
|
local endPoint = coord.LLtoLO(lat, lng, 0)
|
|
|
|
-- 'AGL' mode does not appear to work in the buildWP function. This is a crude approximation
|
|
if altitudeType == "AGL" then
|
|
altitude = land.getHeight({x = endPoint.x, y = endPoint.z}) + altitude
|
|
end
|
|
|
|
-- Simplified formula to compute CAS from GS
|
|
if speedType == "CAS" then
|
|
speed = speed * (1 + 0.02 * altitude / 0.3048 / 1000)
|
|
end
|
|
|
|
-- Create the path
|
|
local path = {
|
|
[1] = mist.heli.buildWP(startPoint, turningPoint, speed, altitude, 'BARO'),
|
|
[2] = mist.heli.buildWP(endPoint, turningPoint, speed, altitude, 'BARO')
|
|
}
|
|
|
|
-- If a task exists assign it to the controller
|
|
if taskOptions then
|
|
local task = Olympus.buildEnrouteTask(taskOptions)
|
|
if task then
|
|
path[1].task = task
|
|
path[2].task = task
|
|
end
|
|
end
|
|
|
|
-- Assign the mission task to the controller
|
|
local missionTask = {
|
|
id = 'Mission',
|
|
params = {
|
|
route = {
|
|
points = mist.utils.deepCopy(path),
|
|
},
|
|
},
|
|
}
|
|
local groupCon = group:getController()
|
|
if groupCon then
|
|
groupCon:setTask(missionTask)
|
|
end
|
|
Olympus.debug("Olympus.move executed successfully on Helicopter", 2)
|
|
elseif category == "GroundUnit" then
|
|
local endPoint = coord.LLtoLO(lat, lng, 0)
|
|
|
|
local action = "Off Road"
|
|
local disableRoads = true
|
|
if taskOptions and taskOptions['id'] == 'FollowRoads' and taskOptions['value'] == true then
|
|
action = "On Road"
|
|
disableRoads = false
|
|
end
|
|
|
|
missionTask = {
|
|
id = 'Mission',
|
|
params = {
|
|
route = {
|
|
points = {
|
|
[1] = {
|
|
type = "Turning Point",
|
|
action = action,
|
|
disableRoads = disableRoads,
|
|
x = endPoint.x,
|
|
y = endPoint.z,
|
|
speed = speed,
|
|
speed_locked = false,
|
|
ETA_locked = false,
|
|
name = 'Mission1',
|
|
},
|
|
[2] = {
|
|
type = "Turning Point",
|
|
action = action,
|
|
disableRoads = disableRoads,
|
|
x = endPoint.x,
|
|
y = endPoint.z,
|
|
speed = speed,
|
|
speed_locked = false,
|
|
ETA_locked = false,
|
|
name = 'Mission1',
|
|
},
|
|
}
|
|
},
|
|
}
|
|
}
|
|
local groupCon = group:getController()
|
|
if groupCon then
|
|
groupCon:setTask(missionTask)
|
|
end
|
|
Olympus.debug("Olympus.move executed successfully on GroundUnit", 2)
|
|
elseif category == "NavyUnit" then
|
|
local startPoint = mist.getLeadPos(group)
|
|
local endPoint = coord.LLtoLO(lat, lng, 0)
|
|
local bearing = math.atan2(endPoint.z - startPoint.z, endPoint.x - startPoint.x)
|
|
|
|
vars = {
|
|
group = group,
|
|
point = endPoint,
|
|
heading = bearing,
|
|
speed = speed
|
|
}
|
|
mist.groupToRandomPoint(vars)
|
|
Olympus.debug("Olympus.move executed successfully on NavyUnit", 2)
|
|
else
|
|
Olympus.debug("Olympus.move not implemented yet for " .. category, 2)
|
|
end
|
|
else
|
|
Olympus.debug("Error in Olympus.move " .. groupName, 2)
|
|
end
|
|
end
|
|
|
|
-- Creates a simple smoke on the ground
|
|
function Olympus.smoke(color, lat, lng)
|
|
Olympus.debug("Olympus.smoke " .. color .. " (" .. lat .. ", " .. lng ..")", 2)
|
|
local colorEnum = nil
|
|
if color == "green" then
|
|
colorEnum = trigger.smokeColor.Green
|
|
elseif color == "red" then
|
|
colorEnum = trigger.smokeColor.Red
|
|
elseif color == "white" then
|
|
colorEnum = trigger.smokeColor.White
|
|
elseif color == "orange" then
|
|
colorEnum = trigger.smokeColor.Orange
|
|
elseif color == "blue" then
|
|
colorEnum = trigger.smokeColor.Blue
|
|
end
|
|
trigger.action.smoke(mist.utils.makeVec3GL(coord.LLtoLO(lat, lng, 0)), colorEnum)
|
|
end
|
|
|
|
-- Creates an explosion on the ground
|
|
function Olympus.explosion(intensity, explosionType, lat, lng, alt)
|
|
Olympus.debug("Olympus.explosion " .. explosionType .. " " .. intensity .. " (" .. lat .. ", " .. lng .. ")", 2)
|
|
|
|
local vec3 = nil
|
|
if alt ~= nil then
|
|
vec3 = coord.LLtoLO(lat, lng, alt)
|
|
else
|
|
vec3 = mist.utils.makeVec3GL(coord.LLtoLO(lat, lng))
|
|
end
|
|
|
|
if explosionType == "normal" then
|
|
trigger.action.explosion(vec3, intensity)
|
|
elseif explosionType == "phosphorous" then
|
|
Olympus.phosphorous(vec3)
|
|
elseif explosionType == "napalm" then
|
|
Olympus.napalm(vec3)
|
|
elseif explosionType == "secondary" then
|
|
Olympus.secondaries(vec3)
|
|
elseif explosionType == "fire" then
|
|
Olympus.createFire(vec3)
|
|
elseif explosionType == "depthCharge" then
|
|
|
|
end
|
|
end
|
|
|
|
function Olympus.phosphorous(vec3)
|
|
trigger.action.explosion(vec3, 1)
|
|
for i = 1,math.random(3, 10) do
|
|
angle = mist.utils.toRadian((math.random(1, 360)))
|
|
local randVec = mist.utils.makeVec3GL((mist.getRandPointInCircle(vec3, 5, 1, 0, 360)))
|
|
trigger.action.signalFlare(randVec, 2, angle)
|
|
end
|
|
end
|
|
|
|
function Olympus.napalm(vec3)
|
|
local napeName = "napalmStrike" .. Olympus.napalmCounter
|
|
Olympus.napalmCounter = Olympus.napalmCounter + 1
|
|
mist.dynAddStatic(
|
|
{
|
|
country = 20,
|
|
category = 'Fortifications',
|
|
hidden = true,
|
|
name = napeName,
|
|
type ="Fuel tank",
|
|
x = vec3.x,
|
|
y = vec3.z,
|
|
heading = 0,
|
|
} -- end of function
|
|
)
|
|
timer.scheduleFunction(Olympus.explodeNapalm, vec3, timer.getTime() + 0.1)
|
|
timer.scheduleFunction(Olympus.removeNapalm, napeName, timer.getTime() + 0.12)
|
|
end
|
|
|
|
function Olympus.explodeNapalm(vec3)
|
|
trigger.action.explosion(vec3, 10)
|
|
end
|
|
|
|
function Olympus.removeNapalm(staticName)
|
|
StaticObject.getByName(staticName):destroy()
|
|
end
|
|
|
|
function Olympus.createFire(vec3)
|
|
local smokeName = "smokeName" .. Olympus.fireCounter
|
|
Olympus.fireCounter = Olympus.fireCounter + 1
|
|
trigger.action.effectSmokeBig(vec3, 2 , 1, smokeName)
|
|
trigger.action.explosion(vec3, 1) -- looks wierd to spawn in on flat land without this
|
|
timer.scheduleFunction(Olympus.removeFire, smokeName, timer.getTime() + 20)
|
|
end
|
|
|
|
function Olympus.removeFire (smokeName)
|
|
trigger.action.effectSmokeStop(smokeName)
|
|
end
|
|
|
|
function Olympus.secondaries(vec3)
|
|
Olympus.randomDebries(vec3)
|
|
--trigger.action.explosion(vec3, 1)
|
|
--for i = 1, 10 do
|
|
-- timer.scheduleFunction(Olympus.randomDebries, vec3, timer.getTime() + math.random(0, 180))
|
|
--end
|
|
end
|
|
|
|
function Olympus.randomDebries(vec3)
|
|
trigger.action.explosion(vec3, 1)
|
|
for i = 1,math.random(3, 10) do
|
|
angle = mist.utils.toRadian((math.random(1, 360)))
|
|
local randVec = mist.utils.makeVec3GL((mist.getRandPointInCircle(vec3, 5, 1, 0, 360)))
|
|
trigger.action.signalFlare(randVec, 3, angle)
|
|
end
|
|
end
|
|
|
|
-- Shines a laser from a unit to a point
|
|
function Olympus.fireLaser(ID, code, lat, lng)
|
|
Olympus.debug("Olympus.fireLaser " .. ID .. " -> (" .. lat .. ", " .. lng .. ") code " .. code, 2)
|
|
|
|
local vec3 = mist.utils.makeVec3GL(coord.LLtoLO(lat, lng))
|
|
|
|
local unit = Olympus.getUnitByID(ID)
|
|
if unit ~= nil and unit:isExist() then
|
|
local spot = Spot.createLaser(unit, {x = 0, y = 1, z = 0}, vec3, code)
|
|
Olympus.spotsCounter = Olympus.spotsCounter + 1
|
|
Olympus.spots[Olympus.spotsCounter] = {
|
|
type = "laser",
|
|
object = spot,
|
|
sourceUnitID = ID,
|
|
targetPosition = {
|
|
lat = lat,
|
|
lng = lng
|
|
},
|
|
active = true,
|
|
code = code
|
|
}
|
|
end
|
|
end
|
|
|
|
-- Shines a infrared light from a unit to a point
|
|
function Olympus.fireInfrared(ID, lat, lng)
|
|
Olympus.debug("Olympus.fireInfrared " .. ID .. " -> (" .. lat .. ", " .. lng .. ")", 2)
|
|
|
|
local vec3 = mist.utils.makeVec3GL(coord.LLtoLO(lat, lng))
|
|
|
|
local unit = Olympus.getUnitByID(ID)
|
|
if unit ~= nil and unit:isExist() then
|
|
local spot = Spot.createInfraRed(unit, {x = 0, y = 1, z = 0}, vec3)
|
|
Olympus.spotsCounter = Olympus.spotsCounter + 1
|
|
Olympus.spots[Olympus.spotsCounter] = {
|
|
type = "infrared",
|
|
object = spot,
|
|
sourceUnitID = ID,
|
|
targetPosition = {
|
|
lat = lat,
|
|
lng = lng
|
|
},
|
|
active = true
|
|
}
|
|
end
|
|
end
|
|
|
|
-- Set new laser code
|
|
function Olympus.setLaserCode(spotID, code)
|
|
Olympus.debug("Olympus.setLaserCode " .. spotID .. " -> " .. code, 2)
|
|
local spot = Olympus.spots[spotID]
|
|
if spot ~= nil and spot.type == "laser" then
|
|
spot.object:setCode(code)
|
|
spot.code = code
|
|
end
|
|
end
|
|
|
|
-- Move spot to a new location
|
|
function Olympus.moveSpot(spotID, lat, lng)
|
|
Olympus.debug("Olympus.moveSpot " .. spotID .. " -> (" .. lat .. ", " .. lng .. ")", 2)
|
|
local spot = Olympus.spots[spotID]
|
|
if spot ~= nil then
|
|
spot.object:setPoint(mist.utils.makeVec3GL(coord.LLtoLO(lat, lng, 0)))
|
|
spot.targetPosition = {lat = lat, lng = lng}
|
|
end
|
|
end
|
|
|
|
-- Remove the spot
|
|
function Olympus.deleteSpot(spotID)
|
|
Olympus.debug("Olympus.deleteSpot " .. spotID, 2)
|
|
local spot = Olympus.spots[spotID]
|
|
if spot ~= nil then
|
|
spot.object:destroy()
|
|
Olympus.spots[spotID]["active"] = false
|
|
end
|
|
end
|
|
|
|
-- Spawns a new unit or group
|
|
-- Spawn table contains the following parameters
|
|
-- category: (string), either Aircraft, Helicopter, GroundUnit or NavyUnit
|
|
-- coalition: (string)
|
|
-- country: (string)
|
|
-- airbaseName: (string, optional) only for air units
|
|
-- units: (array) Array of units to spawn. All units will be in the same group. Each unit element must contain:
|
|
-- unitType: (string) DCS Name of the unit
|
|
-- lat: (number)
|
|
-- lng: (number)
|
|
-- alt: (number, optional) only for air units
|
|
-- loadout: (string, optional) only for air units, must be one of the loadouts defined in unitPayloads.lua or mods.lua
|
|
-- payload: (table, optional) overrides loadout, specifies directly the loadout of the unit
|
|
-- liveryID: (string, optional)
|
|
function Olympus.spawnUnits(spawnTable, requestHash)
|
|
Olympus.debug("Olympus.spawnUnits " .. Olympus.serializeTable(spawnTable), 2)
|
|
|
|
local unitsTable = nil
|
|
local route = nil
|
|
local category = nil
|
|
|
|
-- Generate the units table and route as per DCS requirements
|
|
if spawnTable.category == 'Aircraft' then
|
|
unitsTable = Olympus.generateAirUnitsTable(spawnTable.units)
|
|
route = Olympus.generateAirUnitsRoute(spawnTable)
|
|
category = 'plane'
|
|
elseif spawnTable.category == 'Helicopter' then
|
|
unitsTable = Olympus.generateAirUnitsTable(spawnTable.units)
|
|
route = Olympus.generateAirUnitsRoute(spawnTable)
|
|
category = 'helicopter'
|
|
elseif spawnTable.category == 'GroundUnit' then
|
|
unitsTable = Olympus.generateGroundUnitsTable(spawnTable.units)
|
|
category = 'vehicle'
|
|
elseif spawnTable.category == 'NavyUnit' then
|
|
unitsTable = Olympus.generateNavyUnitsTable(spawnTable.units)
|
|
category = 'ship'
|
|
end
|
|
|
|
-- It the unit country is not specified, get a country that belongs to the coalition
|
|
local countryID = 0
|
|
if spawnTable.country == nil or spawnTable.country == "" then
|
|
countryID = Olympus.getCountryIDByCoalition(spawnTable.coalition)
|
|
else
|
|
countryID = country.id[spawnTable.country]
|
|
end
|
|
|
|
-- Save the units in the database, for cloning
|
|
for idx, unitTable in pairs(unitsTable) do
|
|
Olympus.addToDatabase(unitTable)
|
|
end
|
|
|
|
-- Spawn the new group
|
|
local vars =
|
|
{
|
|
units = unitsTable,
|
|
country = countryID,
|
|
category = category,
|
|
route = route,
|
|
name = "Olympus-" .. Olympus.unitCounter,
|
|
task = 'CAP'
|
|
}
|
|
Olympus.debug(Olympus.serializeTable(vars), 2)
|
|
local newGroup = mist.dynAdd(vars)
|
|
|
|
Olympus.unitCounter = Olympus.unitCounter + 1
|
|
Olympus.debug("Olympus.spawnUnits completed succesfully", 2)
|
|
|
|
if newGroup == nil then
|
|
Olympus.notify("Olympus.spawnUnits failed to spawn group: " .. Olympus.serializeTable(spawnTable), 30)
|
|
return nil
|
|
end
|
|
|
|
Olympus.executionResults[requestHash] = newGroup.groupId
|
|
end
|
|
|
|
-- Generates unit table for air units
|
|
function Olympus.generateAirUnitsTable(units)
|
|
local unitsTable = {}
|
|
for idx, unit in pairs(units) do
|
|
local loadout = unit.loadout -- loadout: a string, one of the names defined in unitPayloads.lua. Must be compatible with the unitType
|
|
local payload = unit.payload -- payload: a table, if present the unit will receive this specific payload. Overrides loadout
|
|
|
|
if unit.heading == nil then
|
|
unit.heading = 0
|
|
end
|
|
|
|
-- Define the loadout
|
|
if payload == nil then
|
|
if loadout ~= nil and loadout ~= "" and Olympus.unitPayloads[unit.unitType] and Olympus.unitPayloads[unit.unitType][loadout] then
|
|
payload = { ["pylons"] = Olympus.unitPayloads[unit.unitType][loadout], ["fuel"] = 999999, ["flare"] = 60, ["ammo_type"] = 1, ["chaff"] = 60, ["gun"] = 100 }
|
|
elseif loadout ~= nil and loadout ~= "" and Olympus.modsUnitPayloads ~= nil and Olympus.modsUnitPayloads[unit.unitType] and Olympus.modsUnitPayloads[unit.unitType][loadout] then
|
|
payload = { ["pylons"] = Olympus.modsUnitPayloads[unit.unitType][loadout], ["fuel"] = 999999, ["flare"] = 60, ["ammo_type"] = 1, ["chaff"] = 60, ["gun"] = 100 }
|
|
else
|
|
payload = { ["pylons"] = {}, ["fuel"] = 999999, ["flare"] = 60, ["ammo_type"] = 1, ["chaff"] = 60, ["gun"] = 100 }
|
|
end
|
|
end
|
|
|
|
-- Generate the unit table
|
|
local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(unit.lat, unit.lng, 0))
|
|
unitsTable[#unitsTable + 1] =
|
|
{
|
|
["type"] = unit.unitType,
|
|
["x"] = spawnLocation.x,
|
|
["y"] = spawnLocation.z,
|
|
["alt"] = unit.alt,
|
|
["alt_type"] = "BARO",
|
|
["skill"] = unit.skill,
|
|
["payload"] = payload,
|
|
["heading"] = unit.heading,
|
|
["callsign"] = { [1] = 1, [2] = 1, [3] = 1, ["name"] = "Olympus" .. Olympus.unitCounter.. "-" .. #unitsTable + 1 },
|
|
["name"] = "Olympus-" .. Olympus.unitCounter .. "-" .. #unitsTable + 1,
|
|
["livery_id"] = unit.liveryID
|
|
}
|
|
end
|
|
return unitsTable
|
|
end
|
|
|
|
function Olympus.generateAirUnitsRoute(spawnTable)
|
|
local airbaseName = spawnTable.airbaseName -- airbaseName: a string, if present the aircraft will spawn on the ground of the selected airbase
|
|
local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(spawnTable.units[1].lat, spawnTable.units[1].lng, 0))
|
|
|
|
-- If a airbase is provided the first waypoint is set as a From runway takeoff.
|
|
local route = {}
|
|
if airbaseName and airbaseName ~= "" then
|
|
local airbase = Airbase.getByName(airbaseName)
|
|
if airbase then
|
|
local airbaseID = airbase:getID()
|
|
route =
|
|
{
|
|
["points"] =
|
|
{
|
|
[1] =
|
|
{
|
|
["action"] = "From Parking Area Hot",
|
|
["tasks"] = {
|
|
[1] = {["number"] = 1, ["auto"] = true, ["id"] = "WrappedAction", ["enabled"] = true, ["params"] = {["action"] = {["id"] = "EPLRS", ["params"] = {["value"] = true}, }, }, },
|
|
[2] = {["number"] = 2, ["auto"] = false, ["id"] = "Orbit", ["enabled"] = true, ["params"] = {["pattern"] = "Circle"}, },
|
|
},
|
|
["type"] = "TakeOffParkingHot",
|
|
["ETA"] = 0,
|
|
["ETA_locked"] = true,
|
|
["x"] = spawnLocation.x,
|
|
["y"] = spawnLocation.z,
|
|
["alt_type"] = "BARO",
|
|
["formation_template"] = "",
|
|
["airdromeId"] = airbaseID,
|
|
["speed_locked"] = true,
|
|
},
|
|
},
|
|
}
|
|
end
|
|
else
|
|
route = {
|
|
["points"] =
|
|
{
|
|
[1] =
|
|
{
|
|
["alt"] = alt,
|
|
["alt_type"] = "BARO",
|
|
["tasks"] = {
|
|
[1] = {["number"] = 1, ["auto"] = true, ["id"] = "WrappedAction", ["enabled"] = true, ["params"] = {["action"] = {["id"] = "EPLRS", ["params"] = {["value"] = true}, }, }, },
|
|
[2] = {["number"] = 2, ["auto"] = false, ["id"] = "Orbit", ["enabled"] = true, ["params"] = {["pattern"] = "Circle"}, },
|
|
},
|
|
["type"] = "Turning Point",
|
|
["x"] = spawnLocation.x,
|
|
["y"] = spawnLocation.z,
|
|
},
|
|
},
|
|
}
|
|
end
|
|
return route
|
|
end
|
|
|
|
-- Generates ground units table, either single or from template
|
|
function Olympus.generateGroundUnitsTable(units)
|
|
local unitsTable = {}
|
|
for idx, unit in pairs(units) do
|
|
local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(unit.lat, unit.lng, 0))
|
|
if Olympus.hasKey(templates, unit.unitType) then
|
|
for idx, value in pairs(templates[unit.unitType].units) do
|
|
unitsTable[#unitsTable + 1] =
|
|
{
|
|
["type"] = value.name,
|
|
["x"] = spawnLocation.x + value.dx,
|
|
["y"] = spawnLocation.z + value.dy,
|
|
["heading"] = 0,
|
|
["skill"] = unit.skill,
|
|
["name"] = "Olympus-" .. Olympus.unitCounter .. "-" .. #unitsTable + 1
|
|
}
|
|
end
|
|
else
|
|
unitsTable[#unitsTable + 1] =
|
|
{
|
|
["type"] = unit.unitType,
|
|
["x"] = spawnLocation.x,
|
|
["y"] = spawnLocation.z,
|
|
["heading"] = unit.heading,
|
|
["skill"] = unit.skill,
|
|
["name"] = "Olympus-" .. Olympus.unitCounter .. "-" .. #unitsTable + 1,
|
|
["livery_id"] = unit.liveryID
|
|
}
|
|
end
|
|
end
|
|
|
|
return unitsTable
|
|
end
|
|
|
|
-- Generates navy units table, either single or from template
|
|
function Olympus.generateNavyUnitsTable(units)
|
|
local unitsTable = {}
|
|
for idx, unit in pairs(units) do
|
|
local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(unit.lat, unit.lng, 0))
|
|
if Olympus.hasKey(templates, unit.unitType) then
|
|
for idx, value in pairs(templates[unit.unitType].units) do
|
|
unitsTable[#unitsTable + 1] =
|
|
{
|
|
["type"] = value.name,
|
|
["x"] = spawnLocation.x + value.dx,
|
|
["y"] = spawnLocation.z + value.dy,
|
|
["heading"] = 0,
|
|
["skill"] = unit.skill,
|
|
["name"] = "Olympus-" .. Olympus.unitCounter .. "-" .. #unitsTable + 1,
|
|
["transportable"] = { ["randomTransportable"] = false }
|
|
}
|
|
end
|
|
else
|
|
unitsTable[#unitsTable + 1] =
|
|
{
|
|
["type"] = unit.unitType,
|
|
["x"] = spawnLocation.x,
|
|
["y"] = spawnLocation.z,
|
|
["heading"] = unit.heading,
|
|
["skill"] = unit.skill,
|
|
["name"] = "Olympus-" .. Olympus.unitCounter .. "-" .. #unitsTable + 1,
|
|
["transportable"] = { ["randomTransportable"] = false },
|
|
["livery_id"] = unit.liveryID
|
|
}
|
|
end
|
|
end
|
|
|
|
return unitsTable
|
|
end
|
|
|
|
-- Add the unit data to the database, used for unit cloning
|
|
function Olympus.addToDatabase(unitTable)
|
|
Olympus.cloneDatabase[unitTable.name] = unitTable
|
|
end
|
|
|
|
-- Find a database entry by ID
|
|
function Olympus.findInDatabase(ID)
|
|
for idx, unitRecord in pairs(Olympus.cloneDatabase) do
|
|
if unitRecord ~= nil and unitRecord["ID"] == ID then
|
|
return unitRecord
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
-- Clones a unit by ID. Will clone the unit with the same original payload as the source unit. TODO: only works on Olympus unit not ME units (TO BE VERIFIED).
|
|
-- cloneTable is an array of element, each of which contains
|
|
-- ID: (number) ID of the unit to clone
|
|
-- lat: (number)
|
|
-- lng: (number)
|
|
function Olympus.clone(cloneTable, deleteOriginal)
|
|
Olympus.debug("Olympus.clone " .. Olympus.serializeTable(cloneTable), 2)
|
|
|
|
local unitsTable = {}
|
|
local countryID = nil
|
|
local category = nil
|
|
local route = {}
|
|
|
|
-- All the units in the table will be cloned in a single group
|
|
for idx, cloneData in pairs(cloneTable) do
|
|
local ID = cloneData.ID
|
|
local unitRecord = Olympus.findInDatabase(ID)
|
|
|
|
if unitRecord ~= nil then
|
|
-- Update the data of the cloned unit
|
|
local unitTable = mist.utils.deepCopy(unitRecord)
|
|
|
|
local point = coord.LLtoLO(cloneData['lat'], cloneData['lng'], 0)
|
|
if unitTable then
|
|
unitTable["x"] = point.x
|
|
unitTable["y"] = point.z
|
|
unitTable["name"] = "Olympus-" .. Olympus.unitCounter .. "-" .. #unitsTable + 1
|
|
end
|
|
|
|
if countryID == nil and category == nil then
|
|
countryID = unitRecord["country"]
|
|
if unitRecord["category"] == Unit.Category.AIRPLANE then
|
|
category = 'plane'
|
|
route = {
|
|
["points"] =
|
|
{
|
|
[1] =
|
|
{
|
|
["alt"] = alt,
|
|
["alt_type"] = "BARO",
|
|
["tasks"] = {
|
|
[1] = {["number"] = 1, ["auto"] = true, ["id"] = "WrappedAction", ["enabled"] = true, ["params"] = {["action"] = {["id"] = "EPLRS", ["params"] = {["value"] = true}, }, }, },
|
|
[2] = {["number"] = 2, ["auto"] = false, ["id"] = "Orbit", ["enabled"] = true, ["params"] = {["pattern"] = "Circle"}, },
|
|
},
|
|
["type"] = "Turning Point",
|
|
["x"] = point.x,
|
|
["y"] = point.z,
|
|
},
|
|
},
|
|
}
|
|
elseif unitRecord["category"] == Unit.Category.HELICOPTER then
|
|
category = 'helicopter'
|
|
route = {
|
|
["points"] =
|
|
{
|
|
[1] =
|
|
{
|
|
["alt"] = alt,
|
|
["alt_type"] = "BARO",
|
|
["tasks"] = {
|
|
[1] = {["number"] = 1, ["auto"] = true, ["id"] = "WrappedAction", ["enabled"] = true, ["params"] = {["action"] = {["id"] = "EPLRS", ["params"] = {["value"] = true}, }, }, },
|
|
[2] = {["number"] = 2, ["auto"] = false, ["id"] = "Orbit", ["enabled"] = true, ["params"] = {["pattern"] = "Circle"}, },
|
|
},
|
|
["type"] = "Turning Point",
|
|
["x"] = point.x,
|
|
["y"] = point.z,
|
|
},
|
|
},
|
|
}
|
|
elseif unitRecord["category"] == Unit.Category.GROUND_UNIT then
|
|
category = 'vehicle'
|
|
elseif unitRecord["category"] == Unit.Category.SHIP then
|
|
category = 'ship'
|
|
end
|
|
end
|
|
unitsTable[#unitsTable + 1] = mist.utils.deepCopy(unitTable)
|
|
end
|
|
|
|
if deleteOriginal then
|
|
Olympus.delete(ID, false)
|
|
end
|
|
end
|
|
|
|
local vars =
|
|
{
|
|
units = unitsTable,
|
|
country = countryID,
|
|
category = category,
|
|
route = route,
|
|
name = "Olympus-" .. Olympus.unitCounter,
|
|
task = 'CAP'
|
|
}
|
|
|
|
Olympus.debug(Olympus.serializeTable(vars), 1)
|
|
|
|
-- Save the units in the database, for cloning
|
|
for idx, unitTable in pairs(unitsTable) do
|
|
Olympus.addToDatabase(unitTable)
|
|
end
|
|
|
|
Olympus.debug(Olympus.serializeTable(vars), 2)
|
|
mist.dynAdd(vars)
|
|
Olympus.unitCounter = Olympus.unitCounter + 1
|
|
|
|
Olympus.debug("Olympus.clone completed successfully", 2)
|
|
end
|
|
|
|
-- Delete a unit by ID, optionally use an explosion
|
|
function Olympus.delete(ID, explosion, explosionType)
|
|
Olympus.debug("Olympus.delete " .. ID .. " " .. tostring(explosion), 2)
|
|
local unit = Olympus.getUnitByID(ID)
|
|
if unit ~= nil and unit:isExist() then
|
|
if unit:getPlayerName() or explosion then
|
|
if explosionType == nil then
|
|
explosionType = "normal"
|
|
end
|
|
local lat, lng, alt = coord.LOtoLL(unit:getPoint())
|
|
Olympus.explosion(250, explosionType, lat, lng, alt)
|
|
Olympus.debug("Olympus.delete completed successfully", 2)
|
|
else
|
|
unit:destroy(); --works for AI units not players
|
|
Olympus.debug("Olympus.delete completed successfully", 2)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Set a DCS main task to a group
|
|
function Olympus.setTask(groupName, taskOptions)
|
|
Olympus.debug("Olympus.setTask " .. groupName .. " " .. Olympus.serializeTable(taskOptions), 2)
|
|
local group = Group.getByName(groupName)
|
|
if group ~= nil then
|
|
local task = Olympus.buildTask(groupName, taskOptions);
|
|
Olympus.debug("Olympus.setTask " .. Olympus.serializeTable(task), 20)
|
|
if task then
|
|
group:getController():setTask(task)
|
|
Olympus.debug("Olympus.setTask completed successfully", 2)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Reset the task of a group
|
|
function Olympus.resetTask(groupName)
|
|
Olympus.debug("Olympus.resetTask " .. groupName, 2)
|
|
local group = Group.getByName(groupName)
|
|
if group ~= nil then
|
|
group:getController():resetTask()
|
|
Olympus.debug("Olympus.resetTask completed successfully", 2)
|
|
end
|
|
end
|
|
|
|
-- Give a group a command
|
|
function Olympus.setCommand(groupName, command)
|
|
Olympus.debug("Olympus.setCommand " .. groupName .. " " .. Olympus.serializeTable(command), 2)
|
|
local group = Group.getByName(groupName)
|
|
if group ~= nil then
|
|
group:getController():setCommand(command)
|
|
Olympus.debug("Olympus.setCommand completed successfully", 2)
|
|
end
|
|
end
|
|
|
|
-- Set an option of a group
|
|
function Olympus.setOption(groupName, optionID, optionValue)
|
|
Olympus.debug("Olympus.setOption " .. groupName .. " " .. optionID .. " " .. tostring(optionValue), 2)
|
|
local group = Group.getByName(groupName)
|
|
if group ~= nil then
|
|
group:getController():setOption(optionID, optionValue)
|
|
Olympus.debug("Olympus.setOption completed successfully", 2)
|
|
end
|
|
end
|
|
|
|
-- Disable the AI of a group on or off entirely
|
|
function Olympus.setOnOff(groupName, onOff)
|
|
Olympus.debug("Olympus.setOnOff " .. groupName .. " " .. tostring(onOff), 2)
|
|
local group = Group.getByName(groupName)
|
|
if group ~= nil then
|
|
group:getController():setOnOff(onOff)
|
|
Olympus.debug("Olympus.setOnOff completed successfully", 2)
|
|
end
|
|
end
|
|
|
|
-- Get the unit description
|
|
function getUnitDescription(unit)
|
|
return unit:getDescr()
|
|
end
|
|
|
|
-- Set the unit cargo weight
|
|
function Olympus.setCargoWeight(ID, weight)
|
|
Olympus.debug("Olympus.setCargoWeight " .. ID .. " " .. tostring(weight), 2)
|
|
|
|
local unit = Olympus.getUnitByID(ID)
|
|
if unit ~= nil and unit:isExist() then
|
|
trigger.action.setUnitInternalCargo(unit:getName(), weight)
|
|
end
|
|
end
|
|
|
|
-- Register a drawArgument to be read for a unit
|
|
function Olympus.registerDrawArgument(ID, argument, active)
|
|
Olympus.debug("Olympus.registerDrawArgument " .. ID .. " " .. tostring(argument) .. " " .. tostring(active), 2)
|
|
|
|
-- Create the table if it does not exist
|
|
if Olympus.drawArguments[ID] == nil then
|
|
Olympus.drawArguments[ID] = {}
|
|
end
|
|
|
|
-- Set the draw argument to true or false
|
|
if active then
|
|
Olympus.drawArguments[ID][argument] = true
|
|
else
|
|
Olympus.drawArguments[ID][argument] = false
|
|
end
|
|
end
|
|
|
|
-- This function gets the navpoints from the DCS mission
|
|
function Olympus.getNavPoints()
|
|
local function extract_tag(str)
|
|
return str:match("^%[(.-)%]")
|
|
end
|
|
|
|
local navpoints = {}
|
|
if mist.DBs.navPoints ~= nil then
|
|
for coalitionName, coalitionNavpoints in pairs(mist.DBs.navPoints) do
|
|
if navpoints[coalitionName] == nil then
|
|
navpoints[coalitionName] = {}
|
|
end
|
|
|
|
for index, navpointDrawingData in pairs(coalitionNavpoints) do
|
|
local navpointCustomLayer = extract_tag(navpointDrawingData['callsignStr']);
|
|
|
|
-- Let's convert DCS coords to lat lon
|
|
local vec3 = { x = navpointDrawingData['x'], y = 0, z = navpointDrawingData['y'] }
|
|
local lat, lng = coord.LOtoLL(vec3)
|
|
navpointDrawingData['lat'] = lat
|
|
navpointDrawingData['lng'] = lng
|
|
navpointDrawingData['coalition'] = coalitionName
|
|
navpointDrawingData['tag'] = navpointCustomLayer
|
|
|
|
if navpointCustomLayer ~= nil then
|
|
if navpoints[coalitionName][navpointCustomLayer] == nil then
|
|
navpoints[coalitionName][navpointCustomLayer] = {}
|
|
end
|
|
navpoints[coalitionName][navpointCustomLayer][navpointDrawingData['callsignStr']] = navpointDrawingData
|
|
else
|
|
navpoints[coalitionName][navpointDrawingData['callsignStr']] = navpointDrawingData
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return navpoints
|
|
end
|
|
|
|
-- This function is periodically called to collect the data of all the existing drawings in the mission to be transmitted to the olympus.dll
|
|
function Olympus.initializeDrawings()
|
|
local function extract_custom_layer_name(str)
|
|
if str:match("^%[LYR:(.-)%]") then
|
|
return str:match("^%[LYR:(.-)%]")
|
|
elseif str:match("^%[(.-)%]") then
|
|
return str:match("^%[(.-)%]")
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local drawings = {}
|
|
if mist.DBs.drawingByName ~= nil then
|
|
for drawingName, drawingData in pairs(mist.DBs.drawingByName) do
|
|
local customLayer = extract_custom_layer_name(drawingName)
|
|
|
|
-- Let's convert DCS coords to lat lon
|
|
local vec3 = { x = drawingData['mapX'], y = 0, z = drawingData['mapY'] }
|
|
local lat, lng = coord.LOtoLL(vec3)
|
|
drawingData['lat'] = lat
|
|
drawingData['lng'] = lng
|
|
|
|
-- If the drawing has points, we have to convert those too
|
|
if drawingData['points'] ~= nil then
|
|
if drawingData['points']['x'] ~= nil then
|
|
-- In this case we have only one point
|
|
local point = { x = drawingData['points']['x'], y = 0, z = drawingData['points']['y'] }
|
|
local pointLat, pointLng = coord.LOtoLL(point)
|
|
drawingData['points'][0] = { lat = pointLat, lng = pointLng }
|
|
else
|
|
-- In this case we have multiple points indexed by number
|
|
for pointNumber, pointLOCoords in pairs(drawingData['points']) do
|
|
local point = { x = pointLOCoords['x'], y = 0, z = pointLOCoords['y'] }
|
|
local pointLat, pointLng = coord.LOtoLL(point)
|
|
|
|
drawingData['points'][pointNumber] = { lat = pointLat, lng = pointLng }
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Let's initialize the layers
|
|
if drawings[drawingData.layerName] == nil then
|
|
drawings[drawingData.layerName] = {}
|
|
end
|
|
|
|
if customLayer and drawings[drawingData.layerName][customLayer] == nil then
|
|
drawings[drawingData.layerName][customLayer] = {}
|
|
end
|
|
|
|
-- Let's put the drawing in the correct layer
|
|
if customLayer then
|
|
-- Let's remove the m from the drawing name
|
|
local cleanDrawingName = string.match(drawingName, "%] (.+)")
|
|
drawingData.name = cleanDrawingName
|
|
-- The drawing has the custom layer m
|
|
drawings[drawingData.layerName][customLayer][cleanDrawingName] = drawingData
|
|
else
|
|
-- The drawing is a standard drawing
|
|
drawings[drawingData.layerName][drawingName] = drawingData
|
|
end
|
|
end
|
|
|
|
local navpoints = Olympus.getNavPoints()
|
|
|
|
drawings['navpoints'] = navpoints
|
|
|
|
Olympus.drawingsByLayer["drawings"] = drawings
|
|
|
|
-- Send the drawings to the DLL
|
|
Olympus.OlympusDLL.setDrawingsData()
|
|
|
|
Olympus.notify("Olympus drawings initialized", 2)
|
|
else
|
|
Olympus.debug("MIST DBs not ready", 2)
|
|
timer.scheduleFunction(Olympus.initializeDrawings, {}, timer.getTime() + 1)
|
|
end
|
|
end
|
|
|
|
-- This function is periodically called to collect the data of all the existing units in the mission to be transmitted to the olympus.dll
|
|
function Olympus.setUnitsData(arg, time)
|
|
-- Units data
|
|
local units = {}
|
|
|
|
local startIndex = Olympus.unitIndex
|
|
local endIndex = startIndex + Olympus.unitStep
|
|
local index = 0
|
|
for ID, unit in pairs(Olympus.units) do
|
|
index = index + 1
|
|
-- Only the indexes between startIndex and endIndex are handled. This is a simple way to spread the update load over many cycles
|
|
if index > startIndex then
|
|
if unit ~= nil and unit:isExist() then
|
|
local table = {}
|
|
|
|
-- Get the object category in Olympus name
|
|
local objectCategory = Object.getCategory(unit)
|
|
if objectCategory == Object.Category.UNIT then
|
|
if unit:getDesc().category == Unit.Category.AIRPLANE then
|
|
table["category"] = "Aircraft"
|
|
elseif unit:getDesc().category == Unit.Category.HELICOPTER then
|
|
table["category"] = "Helicopter"
|
|
elseif unit:getDesc().category == Unit.Category.GROUND_UNIT then
|
|
table["category"] = "GroundUnit"
|
|
elseif unit:getDesc().category == Unit.Category.SHIP then
|
|
table["category"] = "NavyUnit"
|
|
elseif Olympus.modsList ~= nil and Olympus.modsList[unit:getDesc().typeName] ~= nil then
|
|
table["category"] = Olympus.modsList[unit:getDesc().typeName]
|
|
end
|
|
else
|
|
local status, description = pcall(getUnitDescription, unit)
|
|
if status and Olympus.modsList ~= nil and Olympus.modsList[description.typeName] ~= nil then
|
|
table["category"] = Olympus.modsList[description.typeName]
|
|
else
|
|
units[ID] = {isAlive = false}
|
|
Olympus.units[ID] = nil
|
|
end
|
|
end
|
|
|
|
-- If the category is handled by Olympus, get the data
|
|
if table["category"] ~= nil then
|
|
-- Compute unit position and heading
|
|
local lat, lng, alt = coord.LOtoLL(unit:getPoint())
|
|
local position = unit:getPosition()
|
|
local heading = math.atan2( position.x.z, position.x.x )
|
|
local velocity = unit:getVelocity();
|
|
local airborne = unit:inAir()
|
|
|
|
-- Fill the data table
|
|
table["unitID"] = unit:getID()
|
|
table["name"] = unit:getTypeName()
|
|
table["coalitionID"] = unit:getCoalition()
|
|
table["position"] = {}
|
|
table["position"]["lat"] = lat
|
|
table["position"]["lng"] = lng
|
|
table["position"]["alt"] = alt
|
|
table["speed"] = mist.vec.mag(velocity)
|
|
table["horizontalVelocity"] = math.sqrt(velocity.x * velocity.x + velocity.z * velocity.z)
|
|
table["verticalVelocity"] = velocity.y
|
|
table["heading"] = heading
|
|
table["airborne"] = airborne
|
|
|
|
-- Track angles are wrong because of weird reference systems, approximate it using latitude and longitude differences
|
|
if (table["horizontalVelocity"] > 1) then
|
|
if Olympus.unitsData["units"] ~= nil and Olympus.unitsData["units"][ID] ~= nil and Olympus.unitsData["units"][ID]["position"] ~= nil and Olympus.unitsData["units"][ID]["position"]["lat"] ~= nil and Olympus.unitsData["units"][ID]["position"]["lng"] ~= nil then
|
|
local latDifference = lat - Olympus.unitsData["units"][ID]["position"]["lat"]
|
|
local lngDifference = lng - Olympus.unitsData["units"][ID]["position"]["lng"]
|
|
table["track"] = math.atan2(lngDifference * math.cos(lat / 57.29577), latDifference)
|
|
else
|
|
table["track"] = math.atan2(velocity.z, velocity.x)
|
|
end
|
|
else
|
|
table["track"] = table["heading"]
|
|
end
|
|
|
|
table["isAlive"] = unit:isExist() and unit:isActive() and unit:getLife() >= 1
|
|
|
|
--[[ COMMENTING OUT BECAUSE OF CRASHES -- TO BE INVESTIGATED LATER ON ]]--
|
|
--[[ if unit:isActive() and unit:hasSensors(Unit.SensorType.RADAR) then
|
|
if unit:getRadar() then
|
|
table["radarState"] = true
|
|
else
|
|
table["radarState"] = false
|
|
end
|
|
end ]]
|
|
|
|
-- Read the draw arguments
|
|
local drawArguments = {}
|
|
if Olympus.drawArguments[ID] ~= nil then
|
|
for argument, active in pairs(Olympus.drawArguments[ID]) do
|
|
if active then
|
|
drawArguments[#drawArguments + 1] = {
|
|
argument = argument,
|
|
value = unit:getDrawArgumentValue(argument)
|
|
}
|
|
end
|
|
end
|
|
end
|
|
table["drawArguments"] = drawArguments
|
|
|
|
local group = unit:getGroup()
|
|
if group ~= nil then
|
|
local controller = group:getController()
|
|
if controller ~= nil then
|
|
-- Get the targets detected by the unit controller
|
|
local contacts = {}
|
|
local unitController = unit:getController()
|
|
if unitController ~= nil then
|
|
for det, enum in pairs(Controller.Detection) do
|
|
local controllerTargets = unitController:getDetectedTargets(enum)
|
|
for i, target in ipairs(controllerTargets) do
|
|
if target ~= nil and target.object ~= nil and target.visible then
|
|
target["detectionMethod"] = det
|
|
contacts[#contacts + 1] = target
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- getLife0 does not seem to work for ships, so we need to keep a reference to the initial life of the unit
|
|
if Olympus.unitsInitialLife[ID] == nil then
|
|
Olympus.unitsInitialLife[ID] = unit:getLife()
|
|
end
|
|
|
|
-- Get the initial life of the unit to compute the current health
|
|
local initialLife = 1
|
|
if Olympus.unitsInitialLife[ID] ~= nil then
|
|
initialLife = Olympus.unitsInitialLife[ID]
|
|
end
|
|
|
|
table["country"] = unit:getCountry()
|
|
if unit:getPlayerName() ~= nil then
|
|
table["unitName"] = unit:getPlayerName()
|
|
else
|
|
table["unitName"] = unit:getName()
|
|
end
|
|
-- In case of AI units the callSign and the unitName will be the same
|
|
table["callsign"] = unit:getName()
|
|
table["groupID"] = group:getID()
|
|
table["groupName"] = group:getName()
|
|
table["isHuman"] = (unit:getPlayerName() ~= nil)
|
|
table["hasTask"] = controller:hasTask()
|
|
table["ammo"] = unit:getAmmo() --TODO remove a lot of stuff we don't really need
|
|
table["fuel"] = unit:getFuel()
|
|
table["health"] = unit:getLife() / initialLife * 100
|
|
table["contacts"] = contacts
|
|
|
|
local name = unit:getName()
|
|
|
|
-- If the unit is not in the clone database it means it was not spawned by Olympus. Let's try and recover it using mist
|
|
if Olympus.cloneDatabase[name] == nil then
|
|
if mist.DBs ~= nil and mist.DBs.unitsByName ~= nil and mist.DBs.unitsByName[name] ~= nil then
|
|
-- Payloads can be copied from ME units only TODO: can we fix this?
|
|
local payload = {}
|
|
if mist.DBs.MEunitsByName[name] then
|
|
payload = mist.getPayload(name)
|
|
end
|
|
|
|
-- Create a mock spawn table to generate the database
|
|
local unitsTable = nil
|
|
local spawnTable = {}
|
|
spawnTable.units = {
|
|
[1] = {
|
|
["unitType"] = table["name"],
|
|
["lat"] = table["position"]["lat"],
|
|
["lng"] = table["position"]["lng"],
|
|
["alt"] = table["position"]["alt"],
|
|
["payload"] = payload,
|
|
["liveryID"] = mist.DBs.unitsByName[name]["livery_id"]
|
|
}
|
|
}
|
|
|
|
-- Generate the units table as per DCS requirements
|
|
if table["category"] == 'Aircraft' then
|
|
unitsTable = Olympus.generateAirUnitsTable(spawnTable.units)
|
|
elseif table["category"] == 'Helicopter' then
|
|
unitsTable = Olympus.generateAirUnitsTable(spawnTable.units)
|
|
elseif table["category"] == 'GroundUnit' then
|
|
unitsTable = Olympus.generateGroundUnitsTable(spawnTable.units)
|
|
elseif table["category"] == 'NavyUnit' then
|
|
unitsTable = Olympus.generateNavyUnitsTable(spawnTable.units)
|
|
end
|
|
|
|
-- Save the units in the database, for cloning
|
|
for idx, unitTable in pairs(unitsTable) do
|
|
-- Force the name of the unit to be equal to the original name
|
|
unitTable["name"] = name
|
|
Olympus.addToDatabase(mist.utils.deepCopy(unitTable))
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Update the database used for unit cloning
|
|
if Olympus.cloneDatabase[name] ~= nil then
|
|
Olympus.cloneDatabase[name]["ID"] = ID
|
|
Olympus.cloneDatabase[name]["category"] = unit:getDesc().category
|
|
Olympus.cloneDatabase[name]["heading"] = table["heading"]
|
|
Olympus.cloneDatabase[name]["alt"] = alt
|
|
Olympus.cloneDatabase[name]["country"] = unit:getCountry()
|
|
end
|
|
|
|
units[ID] = table
|
|
end
|
|
else
|
|
-- If the unit reference is nil it means the unit no longer exits
|
|
units[ID] = {isAlive = false}
|
|
Olympus.units[ID] = nil
|
|
end
|
|
end
|
|
else
|
|
-- If the unit reference is nil it means the unit no longer exits
|
|
units[ID] = {isAlive = false}
|
|
Olympus.units[ID] = nil
|
|
end
|
|
end
|
|
if index >= endIndex then
|
|
break
|
|
end
|
|
end
|
|
|
|
-- Reset the counter
|
|
if index ~= endIndex then
|
|
Olympus.unitIndex = 0
|
|
else
|
|
Olympus.unitIndex = endIndex
|
|
end
|
|
|
|
-- Assemble unitsData table
|
|
Olympus.unitsData["units"] = units
|
|
|
|
Olympus.OlympusDLL.setUnitsData()
|
|
return time + 0.05
|
|
end
|
|
|
|
-- This function is periodically called to collect the data of all the existing weapons in the mission to be transmitted to the olympus.dll
|
|
function Olympus.setWeaponsData(arg, time)
|
|
-- Weapons data
|
|
local weapons = {}
|
|
|
|
local startIndex = Olympus.weaponIndex
|
|
local endIndex = startIndex + Olympus.weaponStep
|
|
local index = 0
|
|
for ID, weapon in pairs(Olympus.weapons) do
|
|
index = index + 1
|
|
|
|
-- Only the indexes between startIndex and endIndex are handled. This is a simple way to spread the update load over many cycles
|
|
if index > startIndex then
|
|
if weapon ~= nil and weapon:isExist() then
|
|
local table = {}
|
|
|
|
-- Get the object category in Olympus name
|
|
local objectCategory = Object.getCategory(weapon)
|
|
if objectCategory == Object.Category.WEAPON then
|
|
if weapon:getDesc().category == Weapon.Category.MISSILE then
|
|
table["category"] = "Missile"
|
|
elseif weapon:getDesc().category == Weapon.Category.ROCKET then
|
|
table["category"] = "Missile"
|
|
elseif weapon:getDesc().category == Weapon.Category.BOMB then
|
|
table["category"] = "Bomb"
|
|
--elseif weapon:getDesc().category == Weapon.Category.SHELL then
|
|
-- table["category"] = "Shell" -- Useful for debugging but has no real use and has big impact on performance
|
|
end
|
|
else
|
|
weapons[ID] = {isAlive = false}
|
|
Olympus.weapons[ID] = nil
|
|
end
|
|
|
|
-- If the category is handled by Olympus, get the data
|
|
if table["category"] ~= nil then
|
|
-- Compute weapon position and heading
|
|
local lat, lng, alt = coord.LOtoLL(weapon:getPoint())
|
|
local position = weapon:getPosition()
|
|
local heading = math.atan2( position.x.z, position.x.x )
|
|
|
|
-- Fill the data table
|
|
table["name"] = weapon:getTypeName()
|
|
table["coalitionID"] = weapon:getCoalition()
|
|
table["position"] = {}
|
|
table["position"]["lat"] = lat
|
|
table["position"]["lng"] = lng
|
|
table["position"]["alt"] = alt
|
|
table["speed"] = mist.vec.mag(weapon:getVelocity())
|
|
table["heading"] = heading
|
|
table["isAlive"] = weapon:isExist()
|
|
|
|
weapons[ID] = table
|
|
end
|
|
else
|
|
-- If the weapon reference is nil it means the unit no longer exits
|
|
weapons[ID] = {isAlive = false}
|
|
Olympus.weapons[ID] = nil
|
|
end
|
|
end
|
|
if index >= endIndex then
|
|
break
|
|
end
|
|
end
|
|
|
|
-- Reset the counter
|
|
if index ~= endIndex then
|
|
Olympus.weaponIndex = 0
|
|
else
|
|
Olympus.weaponIndex = endIndex
|
|
end
|
|
|
|
-- Assemble weaponsData table
|
|
Olympus.weaponsData["weapons"] = weapons
|
|
|
|
Olympus.OlympusDLL.setWeaponsData()
|
|
return time + 0.25
|
|
end
|
|
|
|
function Olympus.setExecutionResults()
|
|
Olympus.OlympusDLL.setExecutionResults()
|
|
return timer.getTime() + 1
|
|
end
|
|
|
|
function Olympus.setMissionData(arg, time)
|
|
-- Bullseye data
|
|
local bullseyes = {}
|
|
for i = 0, 2 do
|
|
local bullseyeVec3 = coalition.getMainRefPoint(i)
|
|
local bullseyeLatitude, bullseyeLongitude, bullseyeAltitude = coord.LOtoLL(bullseyeVec3)
|
|
bullseyes[i] = {}
|
|
bullseyes[i]["latitude"] = bullseyeLatitude
|
|
bullseyes[i]["longitude"] = bullseyeLongitude
|
|
bullseyes[i]["coalition"] = Olympus.getCoalitionByCoalitionID(i)
|
|
end
|
|
|
|
-- Airbases data
|
|
local base = world.getAirbases()
|
|
local airbases = {}
|
|
for i = 1, #base do
|
|
local info = {}
|
|
local latitude, longitude, altitude = coord.LOtoLL(Airbase.getPoint(base[i]))
|
|
info["callsign"] = Airbase.getCallsign(base[i])
|
|
info["coalition"] = Olympus.getCoalitionByCoalitionID(Airbase.getCoalition(base[i]))
|
|
info["latitude"] = latitude
|
|
info["longitude"] = longitude
|
|
if Airbase.getUnit(base[i]) then
|
|
info["unitId"] = Airbase.getUnit(base[i]):getID()
|
|
end
|
|
airbases[i] = info
|
|
end
|
|
|
|
-- Mission
|
|
local mission = {}
|
|
mission.theatre = env.mission.theatre
|
|
mission.dateAndTime = {
|
|
["elapsedTime"] = DCS.getRealTime() - Olympus.missionStartTime,
|
|
["time"] = mist.time.getDHMS(timer.getAbsTime()),
|
|
["startTime"] = env.mission.start_time,
|
|
["date"] = env.mission.date
|
|
}
|
|
|
|
mission.coalitions = {
|
|
["red"] = {},
|
|
["blue"] = {},
|
|
["neutral"] = {}
|
|
}
|
|
for countryName, countryId in pairs(country["id"]) do
|
|
local coalitionName = Olympus.getCoalitionByCoalitionID(coalition.getCountryCoalition(countryId))
|
|
mission.coalitions[coalitionName][#mission.coalitions[coalitionName] + 1] = countryName
|
|
end
|
|
|
|
-- Spots
|
|
-- Initialize an empty table to store spots
|
|
local spots = {}
|
|
|
|
-- Iterate over each spot in Olympus.spots
|
|
for ID, spot in pairs(Olympus.spots) do
|
|
-- Create a new entry in the spots table with the same ID
|
|
spots[ID] = {
|
|
type = spot.type,
|
|
sourceUnitID = spot.sourceUnitID,
|
|
targetPosition = spot.targetPosition,
|
|
active = spot.active,
|
|
}
|
|
|
|
-- If the spot type is "laser", add the code to the spot entry
|
|
if spot.type == "laser" then
|
|
spots[ID]["code"] = spot.code
|
|
end
|
|
end
|
|
|
|
-- Assemble table
|
|
Olympus.missionData["bullseyes"] = bullseyes
|
|
Olympus.missionData["airbases"] = airbases
|
|
Olympus.missionData["mission"] = mission
|
|
Olympus.missionData["spots"] = spots
|
|
|
|
Olympus.OlympusDLL.setMissionData()
|
|
return time + 1 -- For perfomance reasons mission data is updated once every second
|
|
end
|
|
|
|
-- Initializes the units table with all the existing ME units
|
|
function Olympus.initializeUnits()
|
|
if mist and mist.DBs and mist.DBs.MEunitsById then
|
|
for id, unitsTable in pairs(mist.DBs.MEunitsById) do
|
|
local unit = Unit.getByName(unitsTable["unitName"])
|
|
if unit ~= nil and unit:isExist() then
|
|
Olympus.units[unit["id_"]] = unit
|
|
end
|
|
end
|
|
Olympus.notify("Olympus units table initialized", 2)
|
|
else
|
|
Olympus.debug("MIST DBs not ready", 2)
|
|
timer.scheduleFunction(Olympus.initializeUnits, {}, timer.getTime() + 1)
|
|
end
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------
|
|
-- Olympus utility functions
|
|
------------------------------------------------------------------------------------------------------
|
|
function Olympus.serializeTable(val, name, skipnewlines, depth)
|
|
skipnewlines = skipnewlines or false
|
|
depth = depth or 0
|
|
|
|
local tmp = string.rep(" ", depth)
|
|
if name then
|
|
if type(name) == "number" then
|
|
tmp = tmp .. "[" .. name .. "]" .. " = "
|
|
else
|
|
tmp = tmp .. name .. " = "
|
|
end
|
|
end
|
|
|
|
if type(val) == "table" then
|
|
tmp = tmp .. "{" .. (not skipnewlines and "\n" or "")
|
|
for k, v in pairs(val) do
|
|
tmp = tmp .. Olympus.serializeTable(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and "\n" or "")
|
|
end
|
|
tmp = tmp .. string.rep(" ", depth) .. "}"
|
|
elseif type(val) == "number" then
|
|
tmp = tmp .. tostring(val)
|
|
elseif type(val) == "string" then
|
|
tmp = tmp .. string.format("%q", val)
|
|
elseif type(val) == "boolean" then
|
|
tmp = tmp .. (val and "true" or "false")
|
|
else
|
|
tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\""
|
|
end
|
|
|
|
return tmp
|
|
end
|
|
|
|
function Olympus.isArray(t)
|
|
local i = 0
|
|
for _ in pairs(t) do
|
|
i = i + 1
|
|
if t[i] == nil then return false end
|
|
end
|
|
return true
|
|
end
|
|
|
|
function Olympus.hasValue(tab, val)
|
|
for index, value in ipairs(tab) do
|
|
if value == val then
|
|
return true
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function Olympus.hasKey(tab, key)
|
|
for k, value in pairs(tab) do
|
|
if k == key then
|
|
return true
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
------------------------------------------------------------------------------------------------------
|
|
-- Olympus startup script
|
|
------------------------------------------------------------------------------------------------------
|
|
Olympus.instancePath = lfs.writedir().."Mods\\Services\\Olympus\\bin\\"
|
|
Olympus.notify("Starting DCS Olympus backend session in "..Olympus.instancePath, 2)
|
|
|
|
local OlympusName = 'Olympus ' .. version .. ' C++ module';
|
|
Olympus.DLLsloaded = Olympus.loadDLLs()
|
|
if Olympus.DLLsloaded then
|
|
Olympus.notify(OlympusName..' successfully loaded.', 20)
|
|
else
|
|
Olympus.notify('Failed to load '..OlympusName, 20)
|
|
end
|
|
|
|
-- Create the handler to detect new units
|
|
if handler ~= nil then
|
|
world.removeEventHandler(handler)
|
|
Olympus.debug("Olympus handler removed" , 2)
|
|
end
|
|
handler = {}
|
|
function handler:onEvent(event)
|
|
if event.id == 1 then
|
|
local weapon = event.weapon
|
|
if Olympus ~= nil and Olympus.weapons ~= nil then
|
|
Olympus.weapons[weapon["id_"]] = weapon
|
|
Olympus.debug("New weapon created " .. weapon["id_"], 2)
|
|
end
|
|
elseif event.id == 15 then
|
|
local unit = event.initiator
|
|
if Olympus ~= nil and Olympus.units ~= nil then
|
|
Olympus.units[unit["id_"]] = unit
|
|
Olympus.debug("New unit created " .. unit["id_"], 2)
|
|
end
|
|
end
|
|
end
|
|
world.addEventHandler(handler)
|
|
|
|
-- Start the periodic functions
|
|
timer.scheduleFunction(Olympus.setUnitsData, {}, timer.getTime() + 0.05)
|
|
timer.scheduleFunction(Olympus.setWeaponsData, {}, timer.getTime() + 0.25)
|
|
timer.scheduleFunction(Olympus.setMissionData, {}, timer.getTime() + 1)
|
|
timer.scheduleFunction(Olympus.setExecutionResults, {}, timer.getTime() + 1)
|
|
|
|
-- Initialize the ME units
|
|
Olympus.initializeUnits()
|
|
|
|
-- Initialize the Drawings
|
|
Olympus.initializeDrawings()
|
|
|
|
Olympus.notify("OlympusCommand script " .. version .. " loaded successfully", 2, true)
|
|
|