Added ability to delete original cloned units

Mostly useful to group units together
This commit is contained in:
Pax1601
2023-09-07 13:02:14 +02:00
parent e2f80c5788
commit 09bf361d44
6 changed files with 128 additions and 77 deletions

View File

@@ -1,39 +1,50 @@
local version = "v0.4.4-alpha"
local debug = true
local debug = true -- True enables debug printing using DCS messages
-- .dll related variables
Olympus.OlympusDLL = nil
Olympus.DLLsloaded = false
Olympus.OlympusModPath = os.getenv('DCSOLYMPUS_PATH')..'\\bin\\'
-- Logger reference
Olympus.log = mist.Logger:new("Olympus", 'info')
Olympus.unitCounter = 1
Olympus.spawnDatabase = {}
-- Data structures for transfer to .dll
Olympus.missionData = {}
Olympus.unitsData = {}
Olympus.weaponsData = {}
Olympus.unitIndex = 0
Olympus.unitStep = 50
Olympus.units = {}
-- Units data structures
Olympus.unitCounter = 1 -- Counter to generate unique names
Olympus.spawnDatabase = {} -- 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.weaponIndex = 0
Olympus.weaponStep = 50
Olympus.weapons = {}
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
-- Miscellaneous initializations
Olympus.missionStartTime = DCS.getRealTime()
------------------------------------------------------------------------------------------------------
-- Olympus functions
------------------------------------------------------------------------------------------------------
-- Print a debug message if the debug option is true
function Olympus.debug(message, displayFor)
if debug == true then
trigger.action.outText(message, displayFor)
end
end
-- Print a notify message
function Olympus.notify(message, displayFor)
trigger.action.outText(message, displayFor)
end
-- Loads the olympus .dll
function Olympus.loadDLLs()
-- Add the .dll paths
package.cpath = package.cpath..';'..Olympus.OlympusModPath..'?.dll;'
@@ -52,6 +63,7 @@ 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
@@ -61,6 +73,7 @@ function Olympus.getCountryIDByCoalition(coalitionString)
return 0
end
-- Gets the coalition ID of a coalition
function Olympus.getCoalitionIDByCoalition(coalitionString)
local coalitionID = 0
if coalitionString == "red" then
@@ -71,6 +84,7 @@ function Olympus.getCoalitionIDByCoalition(coalitionString)
return coalitionID
end
-- Gets the coalition name from the coalition ID
function Olympus.getCoalitionByCoalitionID(coalitionID)
local coalitionString = "neutral"
if coalitionID == 1 then
@@ -81,7 +95,7 @@ function Olympus.getCoalitionByCoalitionID(coalitionID)
return coalitionString
end
-- Builds a valid task depending on the provided options
-- 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.
@@ -111,11 +125,13 @@ function Olympus.buildEnrouteTask(options)
return task
end
-- Builds a valid task depending on the provided options
-- 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
@@ -129,6 +145,7 @@ function Olympus.buildTask(groupName, options)
}
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
@@ -142,11 +159,13 @@ function Olympus.buildTask(groupName, options)
}
}
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',
@@ -173,6 +192,7 @@ function Olympus.buildTask(groupName, options)
if options['speed'] then
task['params']['speed'] = options['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 = {
@@ -182,6 +202,7 @@ function Olympus.buildTask(groupName, options)
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 = {
@@ -196,14 +217,7 @@ function Olympus.buildTask(groupName, options)
attackQtyLimit = true
}
}
elseif options['id'] == 'AttackMapObject' and options['lat'] and options['lng'] then
local point = coord.LLtoLO(options['lat'], options['lng'], 0)
task = {
id = 'AttackMapObject',
params = {
point = {x = point.x, y = point.z}
}
}
-- 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)
task = {
@@ -226,22 +240,17 @@ function Olympus.move(groupName, lat, lng, altitude, altitudeType, speed, speedT
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
local path = {}
if taskOptions and taskOptions['id'] == 'Land' then
path = {
[1] = mist.fixedWing.buildWP(startPoint, turningPoint, speed, altitude, 'BARO'),
[2] = mist.fixedWing.buildWP(endPoint, landing, speed, 0, 'AGL')
}
else
path = {
[1] = mist.fixedWing.buildWP(startPoint, turningPoint, speed, altitude, 'BARO'),
[2] = mist.fixedWing.buildWP(endPoint, turningPoint, speed, altitude, 'BARO')
}
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
@@ -270,22 +279,16 @@ function Olympus.move(groupName, lat, lng, altitude, altitudeType, speed, speedT
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
local path = {}
if taskOptions and taskOptions['id'] == 'Land' then
path = {
[1] = mist.heli.buildWP(startPoint, turningPoint, speed, altitude, 'BARO'),
[2] = mist.heli.buildWP(endPoint, landing, speed, 0, 'AGL')
}
else
path = {
[1] = mist.heli.buildWP(startPoint, turningPoint, speed, altitude, 'BARO'),
[2] = mist.heli.buildWP(endPoint, turningPoint, speed, altitude, 'BARO')
}
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
@@ -315,13 +318,12 @@ function Olympus.move(groupName, lat, lng, altitude, altitudeType, speed, speedT
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
}
vars = {
group = group,
point = endPoint,
heading = bearing,
speed = speed
}
if taskOptions and taskOptions['id'] == 'FollowRoads' and taskOptions['value'] == true then
vars["disableRoads"] = false
@@ -331,21 +333,20 @@ function Olympus.move(groupName, lat, lng, altitude, altitudeType, speed, speedT
end
mist.groupToRandomPoint(vars)
Olympus.debug("Olympus.move executed succesfully on GroundUnit", 2)
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
}
vars = {
group = group,
point = endPoint,
heading = bearing,
speed = speed
}
mist.groupToRandomPoint(vars)
Olympus.debug("Olympus.move executed succesfully on NavyUnit", 2)
Olympus.debug("Olympus.move executed successfully on NavyUnit", 2)
else
Olympus.debug("Olympus.move not implemented yet for " .. category, 2)
end
@@ -379,6 +380,19 @@ function Olympus.explosion(intensity, lat, lng)
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
-- payload: (table, optional) overrides loadout, specifies directly the loadout of the unit
-- liveryID: (string, optional)
function Olympus.spawnUnits(spawnTable)
Olympus.debug("Olympus.spawnUnits " .. Olympus.serializeTable(spawnTable), 2)
@@ -386,6 +400,7 @@ function Olympus.spawnUnits(spawnTable)
local route = nil
local category = nil
-- Generate the units table and rout as per DCS requirements
if spawnTable.category == 'Aircraft' then
unitsTable = Olympus.generateAirUnitsTable(spawnTable.units)
route = Olympus.generateAirUnitsRoute(spawnTable)
@@ -402,6 +417,7 @@ function Olympus.spawnUnits(spawnTable)
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)
@@ -414,6 +430,7 @@ function Olympus.spawnUnits(spawnTable)
Olympus.addToDatabase(unitTable)
end
-- Spawn the new group
local vars =
{
units = unitsTable,
@@ -429,7 +446,7 @@ function Olympus.spawnUnits(spawnTable)
Olympus.debug("Olympus.spawnUnits completed succesfully", 2)
end
-- Generates unit table for a air unit.
-- Generates unit table for air units
function Olympus.generateAirUnitsTable(units)
local unitsTable = {}
for idx, unit in pairs(units) do
@@ -440,6 +457,7 @@ function Olympus.generateAirUnitsTable(units)
unit.heading = 0
end
-- Define the loadout
if payload == nil then
if loadout and loadout ~= "" and Olympus.unitPayloads[unit.unitType][loadout] then
payload = Olympus.unitPayloads[unit.unitType][loadout]
@@ -448,6 +466,7 @@ function Olympus.generateAirUnitsTable(units)
end
end
-- Generate the unit table
local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(unit.lat, unit.lng, 0))
unitsTable[#unitsTable + 1] =
{
@@ -593,13 +612,17 @@ function Olympus.generateNavyUnitsTable(units)
return unitsTable
end
-- Add the unit data to the database, used for unit cloning
function Olympus.addToDatabase(unitTable)
-- Add the unit data to the database, used for unit cloning
Olympus.spawnDatabase[unitTable.name] = unitTable
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).
function Olympus.clone(cloneTable)
-- 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 = {}
@@ -607,6 +630,7 @@ function Olympus.clone(cloneTable)
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 unit = Olympus.getUnitByID(ID)
@@ -676,6 +700,10 @@ function Olympus.clone(cloneTable)
unitsTable[#unitsTable + 1] = mist.utils.deepCopy(unitTable)
end
if deleteOriginal then
Olympus.delete(ID, false)
end
end
local vars =
@@ -701,6 +729,7 @@ function Olympus.clone(cloneTable)
Olympus.debug("Olympus.clone completed successfully", 2)
end
-- Delete a unit by ID, optionally use an explosion
function Olympus.delete(ID, explosion)
Olympus.debug("Olympus.delete " .. ID .. " " .. tostring(explosion), 2)
local unit = Olympus.getUnitByID(ID)
@@ -715,6 +744,7 @@ function Olympus.delete(ID, explosion)
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)
@@ -728,6 +758,7 @@ function Olympus.setTask(groupName, taskOptions)
end
end
-- Reset the dask of a group
function Olympus.resetTask(groupName)
Olympus.debug("Olympus.resetTask " .. groupName, 2)
local group = Group.getByName(groupName)
@@ -737,6 +768,7 @@ function Olympus.resetTask(groupName)
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)
@@ -746,6 +778,7 @@ function Olympus.setCommand(groupName, command)
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)
@@ -755,6 +788,7 @@ function Olympus.setOption(groupName, optionID, optionValue)
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)
@@ -764,6 +798,7 @@ function Olympus.setOnOff(groupName, onOff)
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 = {}
@@ -773,9 +808,11 @@ function Olympus.setUnitsData(arg, time)
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 then
local table = {}
local table = {}
-- Get the object category in Olympus name
local objectCategory = unit:getCategory()
if objectCategory == Object.Category.UNIT then
@@ -845,6 +882,7 @@ function Olympus.setUnitsData(arg, time)
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
@@ -853,6 +891,8 @@ function Olympus.setUnitsData(arg, time)
break
end
end
-- Reset the counter
if index ~= endIndex then
Olympus.unitIndex = 0
else
@@ -866,6 +906,7 @@ function Olympus.setUnitsData(arg, time)
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 = {}
@@ -875,6 +916,8 @@ function Olympus.setWeaponsData(arg, time)
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 then
local table = {}
@@ -915,6 +958,7 @@ function Olympus.setWeaponsData(arg, time)
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
@@ -923,6 +967,8 @@ function Olympus.setWeaponsData(arg, time)
break
end
end
-- Reset the counter
if index ~= endIndex then
Olympus.weaponIndex = 0
else
@@ -990,9 +1036,10 @@ function Olympus.setMissionData(arg, time)
Olympus.missionData["mission"] = mission
Olympus.OlympusDLL.setMissionData()
return time + 1
return time + 1 -- For perfomance reasons weapons are 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
@@ -1008,6 +1055,9 @@ function Olympus.initializeUnits()
end
end
------------------------------------------------------------------------------------------------------
-- Olympus utility functions
------------------------------------------------------------------------------------------------------
function Olympus.serializeTable(val, name, skipnewlines, depth)
skipnewlines = skipnewlines or false
depth = depth or 0