From f80eb3ed10c1ad024525f18cfc22cc860c75c417 Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Mon, 4 Jan 2016 19:24:56 +0100 Subject: [PATCH 01/42] fixed mixed-indention, removed trailing spaces --- mist.lua | 10309 +++++++++++++++++++++++++++-------------------------- 1 file changed, 5156 insertions(+), 5153 deletions(-) diff --git a/mist.lua b/mist.lua index 204bf74..c05653d 100644 --- a/mist.lua +++ b/mist.lua @@ -20,863 +20,863 @@ mist.build = 60 -------------------------------------------------------------------------------------------------------------- -- the main area do - local coroutines = {} - - local tempSpawnedUnits = {} -- birth events added here - local mistAddedObjects = {} -- mist.dynAdd unit data added here - local mistAddedGroups = {} -- mist.dynAdd groupdata added here - local writeGroups = {} - local lastUpdateTime = 0 - - local function update_alive_units() -- coroutine function - local lalive_units = mist.DBs.aliveUnits -- local references for faster execution - local lunits = mist.DBs.unitsByNum - local ldeepcopy = mist.utils.deepCopy - local lUnit = Unit - local lremovedAliveUnits = mist.DBs.removedAliveUnits - local updatedUnits = {} - - if #lunits > 0 then - local units_per_run = math.ceil(#lunits/20) - if units_per_run < 5 then - units_per_run = 5 - end - - for i = 1, #lunits do - if lunits[i].category ~= 'static' then -- can't get statics with Unit.getByName :( - local unit = lUnit.getByName(lunits[i].unitName) - if unit then - --print('unit named ' .. lunits[i].unitName .. ' alive!') - local pos = unit:getPosition() - local newtbl = ldeepcopy(lunits[i]) - if pos then - newtbl['pos'] = pos.p - end - newtbl['unit'] = unit - --newtbl['rt_id'] = unit.id_ - lalive_units[unit.id_] = newtbl - updatedUnits[unit.id_] = true - end - end - if i%units_per_run == 0 then - --print('yielding at: ' .. tostring(i)) - coroutine.yield() - --print('resuming at: ' .. tostring(i)) - end - end - -- All units updated, remove any "alive" units that were not updated- they are dead! - for unit_id, unit in pairs(lalive_units) do - if not updatedUnits[unit_id] then - lremovedAliveUnits[unit_id] = unit - lalive_units[unit_id] = nil - end - end - end - end - - local function dbUpdate(event) - local newTable = {} - - newTable['startTime'] = 0 - - if type(event) == 'string' then -- if name of an object. - local newObject - local newType = 'group' - if Group.getByName(event) then - newObject = Group.getByName(event) - elseif StaticObject.getByName(event) then - newObject = StaticObject.getByName(event) - newType = 'static' - -- env.info('its static') - else - env.info('WTF') - return false - end - + local coroutines = {} - - newTable.name = newObject:getName() - newTable.groupId = tonumber(newObject:getID()) - newTable.groupName = newObject:getName() - local unitOneRef - if newType == 'static' then - unitOneRef = newObject - newTable.countryId = tonumber(newObject:getCountry()) - newTable.coalitionId = tonumber(newObject:getCoalition()) - newTable.category = 'static' - else - unitOneRef = newObject:getUnits() - newTable.countryId = tonumber(unitOneRef[1]:getCountry()) - newTable.coalitionId = tonumber(unitOneRef[1]:getCoalition()) - newTable.category = tonumber(newObject:getCategory()) - end - for countryData, countryId in pairs(country.id) do - if newTable.country and string.upper(countryData) == string.upper(newTable.country) or countryId == newTable.countryId then - newTable['countryId'] = countryId - newTable['country'] = string.lower(countryData) - for coaData, coaId in pairs(coalition.side) do - if coaId == coalition.getCountryCoalition(countryId) then - newTable['coalition'] = string.lower(coaData) - end - end - end - end - for catData, catId in pairs(Unit.Category) do - if newType == 'group' and Group.getByName(newTable.groupName):isExist() then - if catId == Group.getByName(newTable.groupName):getCategory() then - newTable['category'] = string.lower(catData) - end - elseif newType == 'static' and StaticObject.getByName(newTable.groupName):isExist() then - if catId == StaticObject.getByName(newTable.groupName):getCategory() then - newTable['category'] = string.lower(catData) - end - - end - end - local gfound = false - for index, data in pairs(mistAddedGroups) do - if mist.stringMatch(data.name, newTable.groupName) == true then - gfound = true - newTable.task = data.task - newTable.modulation = data.modulation - newTable.uncontrolled = data.uncontrolled - newTable.radioSet = data.radioSet - newTable.hidden = data.hidden - newTable.startTime = data.start_time - mistAddedGroups[index] = nil - end - end - - if gfound == false then - newTable.uncontrolled = false - newTable.hidden = false - end - - newTable.units = {} - if newType == 'group' then - for unitId, unitData in pairs(unitOneRef) do - newTable.units[unitId] = {} - newTable.units[unitId].unitName = unitData:getName() - - newTable.units[unitId].x = mist.utils.round(unitData:getPosition().p.x) - newTable.units[unitId].y = mist.utils.round(unitData:getPosition().p.z) - newTable.units[unitId].point = {} - newTable.units[unitId].point.x = newTable.units[unitId].x - newTable.units[unitId].point.y = newTable.units[unitId].y - newTable.units[unitId].alt = mist.utils.round(unitData:getPosition().p.y) - newTable.units[unitId].speed = mist.vec.mag(unitData:getVelocity()) - - newTable.units[unitId].heading = mist.getHeading(unitData, true) + local tempSpawnedUnits = {} -- birth events added here + local mistAddedObjects = {} -- mist.dynAdd unit data added here + local mistAddedGroups = {} -- mist.dynAdd groupdata added here + local writeGroups = {} + local lastUpdateTime = 0 - newTable.units[unitId].type = unitData:getTypeName() - newTable.units[unitId].unitId = tonumber(unitData:getID()) - - - newTable.units[unitId].groupName = newTable.groupName - newTable.units[unitId].groupId = newTable.groupId - newTable.units[unitId].countryId = newTable.countryId - newTable.units[unitId].coalitionId = newTable.coalitionId - newTable.units[unitId].coalition = newTable.coalition - newTable.units[unitId].country = newTable.country - local found = false - for index, data in pairs(mistAddedObjects) do - if mist.stringMatch(data.name, newTable.units[unitId].unitName) == true then - found = true - newTable.units[unitId].livery_id = data.livery_id - newTable.units[unitId].skill = data.skill - newTable.units[unitId].alt_type = data.alt_type - newTable.units[unitId].callsign = data.callsign - newTable.units[unitId].psi = data.psi - mistAddedObjects[index] = nil - end - if found == false then - newTable.units[unitId].skill = "High" - newTable.units[unitId].alt_type = "BARO" - end - end - - end - else -- its a static - newTable.category = 'static' - newTable.units[1] = {} - newTable.units[1].unitName = newObject:getName() - newTable.units[1].category = 'static' - newTable.units[1].x = mist.utils.round(newObject:getPosition().p.x) - newTable.units[1].y = mist.utils.round(newObject:getPosition().p.z) - newTable.units[1].point = {} - newTable.units[1].point.x = newTable.units[1].x - newTable.units[1].point.y = newTable.units[1].y - newTable.units[1].alt = mist.utils.round(newObject:getPosition().p.y) - newTable.units[1].heading = mist.getHeading(newObject, true) - newTable.units[1].type = newObject:getTypeName() - newTable.units[1].unitId = tonumber(newObject:getID()) - newTable.units[1].groupName = newTable.name - newTable.units[1].groupId = newTable.groupId - newTable.units[1].countryId = newTable.countryId - newTable.units[1].country = newTable.country - newTable.units[1].coalitionId = newTable.coalitionId - newTable.units[1].coalition = newTable.coalition - if newObject:getCategory() == 6 and newObject:getCargoDisplayName() then - local mass = newObject:getCargoDisplayName() - mass = string.gsub(mass, ' ', '') - mass = string.gsub(mass, 'kg', '') - newTable.units[1].mass = tonumber(mass) - newTable.units[1].categoryStatic = 'Cargos' - newTable.units[1].canCargo = true - newTable.units[1].shape_name = 'ab-212_cargo' - end - - ----- search mist added objects for extra data if applicable - for index, data in pairs(mistAddedObjects) do - if mist.stringMatch(data.name, newTable.units[1].unitName) == true then - newTable.units[1].shape_name = data.shape_name -- for statics - newTable.units[1].livery_id = data.livery_id - newTable.units[1].airdromeId = data.airdromeId - newTable.units[1].mass = data.mass - newTable.units[1].canCargo = data.canCargo - newTable.units[1].categoryStatic = data.categoryStatic - newTable.units[1].type = 'cargo1' - mistAddedObjects[index] = nil - end - end - end - end - --mist.debug.writeData(mist.utils.serialize,{'msg', newTable}, timer.getAbsTime() ..'Group.lua') - newTable['timeAdded'] = timer.getAbsTime() -- only on the dynGroupsAdded table. For other reference, see start time - --mist.debug.dumpDBs() - --end - - return newTable - end - + local function update_alive_units() -- coroutine function + local lalive_units = mist.DBs.aliveUnits -- local references for faster execution + local lunits = mist.DBs.unitsByNum + local ldeepcopy = mist.utils.deepCopy + local lUnit = Unit + local lremovedAliveUnits = mist.DBs.removedAliveUnits + local updatedUnits = {} - - local function checkSpawnedEvents() - if #tempSpawnedUnits > 0 then - local groupsToAdd = {} - local added = false - local ltemp = tempSpawnedUnits - local ltable = table + if #lunits > 0 then + local units_per_run = math.ceil(#lunits/20) + if units_per_run < 5 then + units_per_run = 5 + end - local updatesPerRun = math.ceil(#tempSpawnedUnits/20) - if updatesPerRun < 5 then - updatesPerRun = 5 - end - for x = 1, #tempSpawnedUnits do - local spawnedObj = ltemp[x] - if spawnedObj and spawnedObj:isExist() then - local found = false - for name, val in pairs(groupsToAdd) do - if spawnedObj:getCategory() == 1 then -- normal groups - if mist.stringMatch(spawnedObj:getGroup():getName(), name) == true then - found = true - break - end - elseif spawnedObj:getCategory() == 3 or spawnedObj:getCategory() == 6 then -- static objects - if mist.stringMatch(spawnedObj:getName(), name) == true then - found = true - break - end - end - end - -- for some reason cargo objects are returning as category == 6. - if found == false then - added = true - if spawnedObj:getCategory() == 1 then -- normal groups - groupsToAdd[spawnedObj:getGroup():getName()] = true - elseif spawnedObj:getCategory() == 3 or spawnedObj:getCategory() == 6 then -- static objects - groupsToAdd[spawnedObj:getName()] = true - end - - end - end + for i = 1, #lunits do + if lunits[i].category ~= 'static' then -- can't get statics with Unit.getByName :( + local unit = lUnit.getByName(lunits[i].unitName) + if unit then + --print('unit named ' .. lunits[i].unitName .. ' alive!') + local pos = unit:getPosition() + local newtbl = ldeepcopy(lunits[i]) + if pos then + newtbl['pos'] = pos.p + end + newtbl['unit'] = unit + --newtbl['rt_id'] = unit.id_ + lalive_units[unit.id_] = newtbl + updatedUnits[unit.id_] = true + end + end + if i%units_per_run == 0 then + --print('yielding at: ' .. tostring(i)) + coroutine.yield() + --print('resuming at: ' .. tostring(i)) + end + end + -- All units updated, remove any "alive" units that were not updated- they are dead! + for unit_id, unit in pairs(lalive_units) do + if not updatedUnits[unit_id] then + lremovedAliveUnits[unit_id] = unit + lalive_units[unit_id] = nil + end + end + end + end - table.remove(ltemp, x) - if x%updatesPerRun == 0 then - coroutine.yield() - end - end + local function dbUpdate(event) + local newTable = {} - if added == true then - for groupName, val in pairs(groupsToAdd) do - local dataChanged = false - if mist.DBs.groupsByName[groupName] then - for _index, data in pairs(mist.DBs.groupsByName[groupName]) do - if data.unitName ~= spawnedObj:getName() and data.unitId ~= spawnedObj:getID() and data.type ~= spawnedObj:getTypeName() then - dataChanged = true - break - end - end - if dataChanged == false then - groupsToAdd[groupName] = false - end - end - if groupsToAdd[groupName] == true or not mist.DBs.groupsByName[groupName] then - writeGroups[#writeGroups + 1] = dbUpdate(groupName) - end - end - end - end - end - - - local function updateDBTables() - - local i = 0 - for index, newTable in pairs(writeGroups) do - i = i + 1 - end - local savesPerRun = math.ceil(i/10) - if savesPerRun < 5 then - savesPerRun = 5 - end - if i > 0 then - local ldeepCopy = mist.utils.deepCopy - for x = 1, i do - local newTable = writeGroups[x] - local mistCategory - if type(newTable.category) == 'string' then - mistCategory = string.lower(newTable.category) - end + newTable['startTime'] = 0 - if string.upper(newTable['category']) == 'GROUND_UNIT' then - mistCategory = 'vehicle' - newTable['category'] = mistCategory - elseif string.upper(newTable['category']) == 'AIRPLANE' then - mistCategory = 'plane' - newTable['category'] = mistCategory - elseif string.upper(newTable['category']) == 'HELICOPTER' then - mistCategory = 'helicopter' - newTable['category'] = mistCategory - elseif string.upper(newTable['category']) == 'SHIP' then - mistCategory = 'ship' - newTable['category'] = mistCategory - end - for newId, newUnitData in pairs(newTable.units) do - newUnitData.category = mistCategory - if newUnitData.unitId then - mist.DBs.unitsById[tonumber(newUnitData.unitId)] = ldeepCopy(newUnitData) - end - - mist.DBs.unitsByName[newUnitData.unitName] = ldeepCopy(newUnitData) - mist.DBs.unitsByCat[mistCategory][#mist.DBs.unitsByCat[mistCategory] + 1] = ldeepCopy(newUnitData) - mist.DBs.unitsByNum[#mist.DBs.unitsByNum + 1] = ldeepCopy(newUnitData) - end - -- this is a really annoying DB to populate. Gotta create new tables in case its missing - if not mist.DBs.units[newTable.coalition] then - mist.DBs.units[newTable.coalition] = {} - end + if type(event) == 'string' then -- if name of an object. + local newObject + local newType = 'group' + if Group.getByName(event) then + newObject = Group.getByName(event) + elseif StaticObject.getByName(event) then + newObject = StaticObject.getByName(event) + newType = 'static' + -- env.info('its static') + else + env.info('WTF') + return false + end - if not mist.DBs.units[newTable.coalition][newTable.country] then - mist.DBs.units[newTable.coalition][(newTable.country)] = {} - mist.DBs.units[newTable.coalition][(newTable.country)]['countryId'] = newTable.countryId - end - if not mist.DBs.units[newTable.coalition][newTable.country][mistCategory] then - mist.DBs.units[newTable.coalition][(newTable.country)][mistCategory] = {} - end - mist.DBs.units[newTable.coalition][(newTable.country)][mistCategory][#mist.DBs.units[newTable.coalition][(newTable.country)][mistCategory] + 1] = ldeepCopy(newTable) - - if newTable.groupId then - mist.DBs.groupsById[newTable.groupId] = ldeepCopy(newTable) - end - - mist.DBs.groupsByName[newTable.name] = ldeepCopy(newTable) - mist.DBs.dynGroupsAdded[#mist.DBs.dynGroupsAdded + 1] = ldeepCopy(newTable) - - writeGroups[x] = nil - if x%savesPerRun == 0 then - coroutine.yield() - end - end - if timer.getTime() > lastUpdateTime then - lastUpdateTime = timer.getTime() - end - end - end - - local update_alive_units_counter = 0 - local write_DB_table_counter = 0 - local check_spawn_events_counter = 0 - - -- THE MAIN FUNCTION -- Accessed 100 times/sec. - mist.main = function() - timer.scheduleFunction(mist.main, {}, timer.getTime() + 0.01) --reschedule first in case of Lua error - ---------------------------------------------------------------------------------------------------------- - --area to add new stuff in - write_DB_table_counter = write_DB_table_counter + 1 - if write_DB_table_counter == 10 then - - write_DB_table_counter = 0 - - if not coroutines.updateDBTables then - coroutines['updateDBTables'] = coroutine.create(updateDBTables) - end - - coroutine.resume(coroutines.updateDBTables) - - if coroutine.status(coroutines.updateDBTables) == 'dead' then - coroutines.updateDBTables = nil - end - end - check_spawn_events_counter = check_spawn_events_counter + 1 - if check_spawn_events_counter == 10 then - - check_spawn_events_counter = 0 - - if not coroutines.checkSpawnedEvents then - coroutines['checkSpawnedEvents'] = coroutine.create(checkSpawnedEvents) - end - - coroutine.resume(coroutines.checkSpawnedEvents) - - if coroutine.status(coroutines.checkSpawnedEvents) == 'dead' then - coroutines.checkSpawnedEvents = nil - end - end - - ----------------------------------------------------------------------------------------------------------- - --updating alive units - update_alive_units_counter = update_alive_units_counter + 1 - if update_alive_units_counter == 5 then - update_alive_units_counter = 0 - - if not coroutines.update_alive_units then - coroutines['update_alive_units'] = coroutine.create(update_alive_units) - end - - coroutine.resume(coroutines.update_alive_units) - - if coroutine.status(coroutines.update_alive_units) == 'dead' then - coroutines.update_alive_units = nil - end - end - - mist.do_scheduled_functions() - end -- end of mist.main - -------------------------------------------- - ------------ mist dyn add stuff for coroutines - local mistGpId = 7000 - local mistUnitId = 7000 - local mistDynAddIndex = 1 - - - mist.nextGroupId = 1 - mist.nextUnitId = 1 - - mist.getNextUnitId = function() - mist.nextUnitId = mist.nextUnitId + 1 - if mist.nextUnitId > 6900 then - mist.nextUnitId = 14000 - end - return mist.nextUnitId - end - - mist.getNextGroupId = function() - mist.nextGroupId = mist.nextGroupId + 1 - if mist.nextGroupId > 6900 then - mist.nextGroupId = 14000 - end - return mist.nextGroupId - end - - mist.getLastDBUpdateTime = function() - return lastUpdateTime - end - - local function groupSpawned(event) - if event.id == world.event.S_EVENT_BIRTH and timer.getTime0() < timer.getAbsTime()then -- dont need to add units spawned in at the start of the mission if mist is loaded in init line - table.insert(tempSpawnedUnits,(event.initiator)) - end - end - - - - mist.dynAddStatic = function(staticObj) - local newObj = {} - newObj.groupId = staticObj.groupId - newObj.category = staticObj.category - newObj.type = staticObj.type - newObj.unitId = staticObj.unitId - newObj.y = staticObj.y - newObj.x = staticObj.x - newObj.heading = staticObj.heading - newObj.name = staticObj.name - newObj.dead = staticObj.dead - newObj.country = staticObj.country - newObj.countryId = staticObj.countryId - newObj.clone = staticObj.clone - newObj.shape_name = staticObj.shape_name - newObj.canCargo = staticObj.canCargo - newObj.mass = staticObj.mass - newObj.categoryStatic = staticObj.categoryStatic - if staticObj.units then -- if its mist format - newObj.groupId = staticObj.units[1].groupId - newObj.category = staticObj.units[1].category - newObj.type = staticObj.units[1].type - newObj.unitId = staticObj.units[1].unitId - newObj.y = staticObj.units[1].y - newObj.x = staticObj.units[1].x - newObj.heading = staticObj.units[1].heading - newObj.name = staticObj.units[1].name - newObj.dead = staticObj.units[1].dead - newObj.country = staticObj.units[1].country - newObj.countryId = staticObj.units[1].countryId - newObj.shape_name = staticObj.units[1].shape_name - newObj.canCargo = staticObj.units[1].canCargo - newObj.mass = staticObj.units[1].mass - newObj.categoryStatic = staticObj.units[1].categoryStatic - end - - if not newObj.country then - return false - end - - local newCountry = newObj.country - if newObj.countryId then - newCountry = newObj.countryId - end - for countryId, countryName in pairs(country.name) do - if type(newObj.country) == 'string' then - if tostring(countryName) == string.upper(newObj.country) then - newCountry = countryName - end - elseif type(newObj.country) == 'number' then - if countryId == newObj.country then - newCountry = countryName - end - end - end - - if newObj.clone or not newObj.groupId then - mistGpId = mistGpId + 1 - newObj.groupId = mistGpId - end - - if newObj.clone or not newObj.unitId then - mistUnitId = mistUnitId + 1 - newObj.unitId = mistUnitId - end - - if newObj.clone or not newObj.name then - mistDynAddIndex = mistDynAddIndex + 1 - newObj.name = (country.name[newCountry] .. ' static ' .. mistDynAddIndex) - end - - if not newObj.dead then - newObj.dead = false - end - - if not newObj.heading then - newObj.heading = math.random(360) - end - - if newObj.categoryStatic then - newObj.category = newObj.categoryStatic - end - if newObj.mass then - newObj.category = 'Cargos' - end - mistAddedObjects[#mistAddedObjects + 1] = mist.utils.deepCopy(newObj) - if newObj.x and newObj.y and newObj.type and type(newObj.x) == 'number' and type(newObj.y) == 'number' and type(newObj.type) == 'string' then - coalition.addStaticObject(country.id[newCountry], newObj) + newTable.name = newObject:getName() + newTable.groupId = tonumber(newObject:getID()) + newTable.groupName = newObject:getName() + local unitOneRef + if newType == 'static' then + unitOneRef = newObject + newTable.countryId = tonumber(newObject:getCountry()) + newTable.coalitionId = tonumber(newObject:getCoalition()) + newTable.category = 'static' + else + unitOneRef = newObject:getUnits() + newTable.countryId = tonumber(unitOneRef[1]:getCountry()) + newTable.coalitionId = tonumber(unitOneRef[1]:getCoalition()) + newTable.category = tonumber(newObject:getCategory()) + end + for countryData, countryId in pairs(country.id) do + if newTable.country and string.upper(countryData) == string.upper(newTable.country) or countryId == newTable.countryId then + newTable['countryId'] = countryId + newTable['country'] = string.lower(countryData) + for coaData, coaId in pairs(coalition.side) do + if coaId == coalition.getCountryCoalition(countryId) then + newTable['coalition'] = string.lower(coaData) + end + end + end + end + for catData, catId in pairs(Unit.Category) do + if newType == 'group' and Group.getByName(newTable.groupName):isExist() then + if catId == Group.getByName(newTable.groupName):getCategory() then + newTable['category'] = string.lower(catData) + end + elseif newType == 'static' and StaticObject.getByName(newTable.groupName):isExist() then + if catId == StaticObject.getByName(newTable.groupName):getCategory() then + newTable['category'] = string.lower(catData) + end - return newObj - end - return false - end + end + end + local gfound = false + for index, data in pairs(mistAddedGroups) do + if mist.stringMatch(data.name, newTable.groupName) == true then + gfound = true + newTable.task = data.task + newTable.modulation = data.modulation + newTable.uncontrolled = data.uncontrolled + newTable.radioSet = data.radioSet + newTable.hidden = data.hidden + newTable.startTime = data.start_time + mistAddedGroups[index] = nil + end + end - mist.dynAdd = function(newGroup) -- same as coalition.add function in SSE. checks the passed data to see if its valid. ---Will generate groupId, groupName, unitId, and unitName if needed --- - - + if gfound == false then + newTable.uncontrolled = false + newTable.hidden = false + end - --mist.debug.writeData(mist.utils.serialize,{'msg', newGroup}, 'newGroupOrig.lua') - local cntry = newGroup.country - if newGroup.countryId then - cntry = newGroup.countryId - end - - local groupType = newGroup.category - local newCountry = '' - -- validate data - for countryId, countryName in pairs(country.name) do - if type(cntry) == 'string' then - cntry = cntry:gsub("%s+", "_") - if tostring(countryName) == string.upper(cntry) then - newCountry = countryName - end - elseif type(cntry) == 'number' then - if countryId == cntry then - newCountry = countryName - end - end - end - - if newCountry == '' then - return false - end - - local newCat = '' - for catName, catId in pairs(Unit.Category) do - if type(groupType) == 'string' then - if tostring(catName) == string.upper(groupType) then - newCat = catName - end - elseif type(groupType) == 'number' then - if catId == groupType then - newCat = catName - end - end - - if catName == 'GROUND_UNIT' and (string.upper(groupType) == 'VEHICLE' or string.upper(groupType) == 'GROUND') then - newCat = 'GROUND_UNIT' - elseif catName == 'AIRPLANE' and string.upper(groupType) == 'PLANE' then - newCat = 'AIRPLANE' - end - end - local typeName - if newCat == 'GROUND_UNIT' then - typeName = ' gnd ' - elseif newCat == 'AIRPLANE' then - typeName = ' air ' - elseif newCat == 'HELICOPTER' then - typeName = ' hel ' - elseif newCat == 'SHIP' then - typeName = ' shp ' - elseif newCat == 'BUILDING' then - typeName = ' bld ' - end - if newGroup.clone or not newGroup.groupId then - mistDynAddIndex = mistDynAddIndex + 1 - mistGpId = mistGpId + 1 - newGroup.groupId = mistGpId - end - if newGroup.groupName or newGroup.name then - if newGroup.groupName then - newGroup['name'] = newGroup.groupName - elseif newGroup.name then - newGroup['name'] = newGroup.name - end - end - - if newGroup.clone and mist.DBs.groupsByName[newGroup.name] or not newGroup.name then - newGroup['name'] = tostring(tostring(country.name[cntry]) .. tostring(typeName) .. mistDynAddIndex) - end - - if not newGroup.hidden then - newGroup.hidden = false - end - - if not newGroup.visible then - newGroup.visible = false - end - - if (newGroup.start_time and type(newGroup.start_time) ~= 'number') or not newGroup.start_time then - if newGroup.startTime then - newGroup.start_time = mist.utils.round(newGroup.startTime) - else - newGroup.start_time = 0 - end - end - - - for unitIndex, unitData in pairs(newGroup.units) do - local originalName = newGroup.units[unitIndex].unitName or newGroup.units[unitIndex].name - if newGroup.clone or not unitData.unitId then - mistUnitId = mistUnitId + 1 - newGroup.units[unitIndex]['unitId'] = mistUnitId - end - if newGroup.units[unitIndex].unitName or newGroup.units[unitIndex].name then - if newGroup.units[unitIndex].unitName then - newGroup.units[unitIndex].name = newGroup.units[unitIndex].unitName - elseif newGroup.units[unitIndex].name then - newGroup.units[unitIndex].name = newGroup.units[unitIndex].name - end - end - if newGroup.clone or not unitData.name then - newGroup.units[unitIndex].name = tostring(newGroup.name .. ' unit' .. unitIndex) - end - - if not unitData.skill then - newGroup.units[unitIndex].skill = 'Random' - end - - if not unitData.alt then - if newCat == 'AIRPLANE' then - newGroup.units[unitIndex].alt = 2000 - newGroup.units[unitIndex].alt_type = 'RADIO' - newGroup.units[unitIndex].speed = 150 - elseif newCat == 'HELICOPTER' then - newGroup.units[unitIndex].alt = 500 - newGroup.units[unitIndex].alt_type = 'RADIO' - newGroup.units[unitIndex].speed = 60 - else - --[[env.info('check height') - newGroup.units[unitIndex].alt = land.getHeight({x = newGroup.units[unitIndex].x, y = newGroup.units[unitIndex].y}) - newGroup.units[unitIndex].alt_type = 'BARO']] - end - + newTable.units = {} + if newType == 'group' then + for unitId, unitData in pairs(unitOneRef) do + newTable.units[unitId] = {} + newTable.units[unitId].unitName = unitData:getName() - end - - if newCat == 'AIRPLANE' or newCat == 'HELICOPTER' then - if newGroup.units[unitIndex].alt_type and newGroup.units[unitIndex].alt_type ~= 'BARO' or not newGroup.units[unitIndex].alt_type then - newGroup.units[unitIndex].alt_type = 'RADIO' - end - if not unitData.speed then - if newCat == 'AIRPLANE' then - newGroup.units[unitIndex].speed = 150 - elseif newCat == 'HELICOPTER' then - newGroup.units[unitIndex].speed = 60 - end - end - if not unitData.payload then - newGroup.units[unitIndex].payload = mist.getPayload(originalName) - end - end - mistAddedObjects[#mistAddedObjects + 1] = mist.utils.deepCopy(newGroup.units[unitIndex]) - end - mistAddedGroups[#mistAddedGroups + 1] = mist.utils.deepCopy(newGroup) - if newGroup.route and not newGroup.route.points then - if not newGroup.route.points and newGroup.route[1] then - local copyRoute = newGroup.route - newGroup.route = {} - newGroup.route.points = copyRoute - end - end - newGroup.country = newCountry - - - --mist.debug.writeData(mist.utils.serialize,{'msg', newGroup}, 'newGroup.lua') - - -- sanitize table - newGroup.groupName = nil - newGroup.clone = nil - newGroup.category = nil - newGroup.country = nil - - newGroup.tasks = {} - - for unitIndex, unitData in pairs(newGroup.units) do - newGroup.units[unitIndex].unitName = nil - end - - coalition.addGroup(country.id[newCountry], Unit.Category[newCat], newGroup) - - return newGroup - - end + newTable.units[unitId].x = mist.utils.round(unitData:getPosition().p.x) + newTable.units[unitId].y = mist.utils.round(unitData:getPosition().p.z) + newTable.units[unitId].point = {} + newTable.units[unitId].point.x = newTable.units[unitId].x + newTable.units[unitId].point.y = newTable.units[unitId].y + newTable.units[unitId].alt = mist.utils.round(unitData:getPosition().p.y) + newTable.units[unitId].speed = mist.vec.mag(unitData:getVelocity()) ---------------------------------------------------------------------------------------------- ---Modified Slmod task scheduler, superior to timer.scheduleFunction - - local Tasks = {} - local task_id = 0 - --[[ mist.scheduleFunction: - int id = mist.schedule_task(f function, vars table, t number, rep number, st number) - id - integer id of this function task - f - function to run - vars - table of vars for that function - t - time to run function - rep - time between repetitions of this function (OPTIONAL) - st - time when repetitions of this function will stop automatically (OPTIONAL) - ]] - mist.scheduleFunction = function(f, vars, t, rep, st) - --verify correct types - assert(type(f) == 'function', 'variable 1, expected function, got ' .. type(f)) - assert(type(vars) == 'table' or vars == nil, 'variable 2, expected table or nil, got ' .. type(f)) - assert(type(t) == 'number', 'variable 3, expected number, got ' .. type(t)) - assert(type(rep) == 'number' or rep == nil, 'variable 4, expected number or nil, got ' .. type(rep)) - assert(type(st) == 'number' or st == nil, 'variable 5, expected number or nil, got ' .. type(st)) - if not vars then - vars = {} - end - task_id = task_id + 1 - table.insert(Tasks, {f = f, vars = vars, t = t, rep = rep, st = st, id = task_id}) - return task_id - end - - -- removes a scheduled function based on the function's id. returns true if successful, false if not successful. - mist.removeFunction = function(id) - local i = 1 - while i <= #Tasks do - if Tasks[i].id == id then - table.remove(Tasks, i) - else - i = i + 1 - end - end - end + newTable.units[unitId].heading = mist.getHeading(unitData, true) - -------------------------------------------------------------------------------------------------------------------- - -- not intended for users to use this function. - mist.do_scheduled_functions = function() - local i = 1 - while i <= #Tasks do - if not Tasks[i].rep then -- not a repeated process - if Tasks[i].t <= timer.getTime() then - local Task = Tasks[i] -- local reference - table.remove(Tasks, i) - local err, errmsg = pcall(Task.f, unpack(Task.vars, 1, table.maxn(Task.vars))) - if not err then - env.info('mist.scheduleFunction, error in scheduled function: ' .. errmsg) - end - --Task.f(unpack(Task.vars, 1, table.maxn(Task.vars))) -- do the task, do not increment i - else - i = i + 1 - end - else - if Tasks[i].st and Tasks[i].st <= timer.getTime() then --if a stoptime was specified, and the stop time exceeded - table.remove(Tasks, i) -- stop time exceeded, do not execute, do not increment i - elseif Tasks[i].t <= timer.getTime() then - local Task = Tasks[i] -- local reference - Task.t = timer.getTime() + Task.rep --schedule next run - local err, errmsg = pcall(Task.f, unpack(Task.vars, 1, table.maxn(Task.vars))) - if not err then - env.info('mist.scheduleFunction, error in scheduled function: ' .. errmsg) - end - --Tasks[i].f(unpack(Tasks[i].vars, 1, table.maxn(Tasks[i].vars))) -- do the task - i = i + 1 - else - i = i + 1 - end - end - end - end + newTable.units[unitId].type = unitData:getTypeName() + newTable.units[unitId].unitId = tonumber(unitData:getID()) - - local idNum = 0 + newTable.units[unitId].groupName = newTable.groupName + newTable.units[unitId].groupId = newTable.groupId + newTable.units[unitId].countryId = newTable.countryId + newTable.units[unitId].coalitionId = newTable.coalitionId + newTable.units[unitId].coalition = newTable.coalition + newTable.units[unitId].country = newTable.country + local found = false + for index, data in pairs(mistAddedObjects) do + if mist.stringMatch(data.name, newTable.units[unitId].unitName) == true then + found = true + newTable.units[unitId].livery_id = data.livery_id + newTable.units[unitId].skill = data.skill + newTable.units[unitId].alt_type = data.alt_type + newTable.units[unitId].callsign = data.callsign + newTable.units[unitId].psi = data.psi + mistAddedObjects[index] = nil + end + if found == false then + newTable.units[unitId].skill = "High" + newTable.units[unitId].alt_type = "BARO" + end + end - --Simplified event handler - mist.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) - return handler.id - end + end + else -- its a static + newTable.category = 'static' + newTable.units[1] = {} + newTable.units[1].unitName = newObject:getName() + newTable.units[1].category = 'static' + newTable.units[1].x = mist.utils.round(newObject:getPosition().p.x) + newTable.units[1].y = mist.utils.round(newObject:getPosition().p.z) + newTable.units[1].point = {} + newTable.units[1].point.x = newTable.units[1].x + newTable.units[1].point.y = newTable.units[1].y + newTable.units[1].alt = mist.utils.round(newObject:getPosition().p.y) + newTable.units[1].heading = mist.getHeading(newObject, true) + newTable.units[1].type = newObject:getTypeName() + newTable.units[1].unitId = tonumber(newObject:getID()) + newTable.units[1].groupName = newTable.name + newTable.units[1].groupId = newTable.groupId + newTable.units[1].countryId = newTable.countryId + newTable.units[1].country = newTable.country + newTable.units[1].coalitionId = newTable.coalitionId + newTable.units[1].coalition = newTable.coalition + if newObject:getCategory() == 6 and newObject:getCargoDisplayName() then + local mass = newObject:getCargoDisplayName() + mass = string.gsub(mass, ' ', '') + mass = string.gsub(mass, 'kg', '') + newTable.units[1].mass = tonumber(mass) + newTable.units[1].categoryStatic = 'Cargos' + newTable.units[1].canCargo = true + newTable.units[1].shape_name = 'ab-212_cargo' + end - mist.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 + ----- search mist added objects for extra data if applicable + for index, data in pairs(mistAddedObjects) do + if mist.stringMatch(data.name, newTable.units[1].unitName) == true then + newTable.units[1].shape_name = data.shape_name -- for statics + newTable.units[1].livery_id = data.livery_id + newTable.units[1].airdromeId = data.airdromeId + newTable.units[1].mass = data.mass + newTable.units[1].canCargo = data.canCargo + newTable.units[1].categoryStatic = data.categoryStatic + newTable.units[1].type = 'cargo1' + mistAddedObjects[index] = nil + end + end + end + end + --mist.debug.writeData(mist.utils.serialize,{'msg', newTable}, timer.getAbsTime() ..'Group.lua') + newTable['timeAdded'] = timer.getAbsTime() -- only on the dynGroupsAdded table. For other reference, see start time + --mist.debug.dumpDBs() + --end + + return newTable + end + + + + local function checkSpawnedEvents() + if #tempSpawnedUnits > 0 then + local groupsToAdd = {} + local added = false + local ltemp = tempSpawnedUnits + local ltable = table + + local updatesPerRun = math.ceil(#tempSpawnedUnits/20) + if updatesPerRun < 5 then + updatesPerRun = 5 + end + for x = 1, #tempSpawnedUnits do + local spawnedObj = ltemp[x] + if spawnedObj and spawnedObj:isExist() then + local found = false + for name, val in pairs(groupsToAdd) do + if spawnedObj:getCategory() == 1 then -- normal groups + if mist.stringMatch(spawnedObj:getGroup():getName(), name) == true then + found = true + break + end + elseif spawnedObj:getCategory() == 3 or spawnedObj:getCategory() == 6 then -- static objects + if mist.stringMatch(spawnedObj:getName(), name) == true then + found = true + break + end + end + end + -- for some reason cargo objects are returning as category == 6. + if found == false then + added = true + if spawnedObj:getCategory() == 1 then -- normal groups + groupsToAdd[spawnedObj:getGroup():getName()] = true + elseif spawnedObj:getCategory() == 3 or spawnedObj:getCategory() == 6 then -- static objects + groupsToAdd[spawnedObj:getName()] = true + end + + end + end + + table.remove(ltemp, x) + if x%updatesPerRun == 0 then + coroutine.yield() + end + end + + if added == true then + for groupName, val in pairs(groupsToAdd) do + local dataChanged = false + if mist.DBs.groupsByName[groupName] then + for _index, data in pairs(mist.DBs.groupsByName[groupName]) do + if data.unitName ~= spawnedObj:getName() and data.unitId ~= spawnedObj:getID() and data.type ~= spawnedObj:getTypeName() then + dataChanged = true + break + end + end + if dataChanged == false then + groupsToAdd[groupName] = false + end + end + if groupsToAdd[groupName] == true or not mist.DBs.groupsByName[groupName] then + writeGroups[#writeGroups + 1] = dbUpdate(groupName) + end + end + end + end + end + + + local function updateDBTables() + + local i = 0 + for index, newTable in pairs(writeGroups) do + i = i + 1 + end + local savesPerRun = math.ceil(i/10) + if savesPerRun < 5 then + savesPerRun = 5 + end + if i > 0 then + local ldeepCopy = mist.utils.deepCopy + for x = 1, i do + local newTable = writeGroups[x] + local mistCategory + if type(newTable.category) == 'string' then + mistCategory = string.lower(newTable.category) + end + + if string.upper(newTable['category']) == 'GROUND_UNIT' then + mistCategory = 'vehicle' + newTable['category'] = mistCategory + elseif string.upper(newTable['category']) == 'AIRPLANE' then + mistCategory = 'plane' + newTable['category'] = mistCategory + elseif string.upper(newTable['category']) == 'HELICOPTER' then + mistCategory = 'helicopter' + newTable['category'] = mistCategory + elseif string.upper(newTable['category']) == 'SHIP' then + mistCategory = 'ship' + newTable['category'] = mistCategory + end + for newId, newUnitData in pairs(newTable.units) do + newUnitData.category = mistCategory + if newUnitData.unitId then + mist.DBs.unitsById[tonumber(newUnitData.unitId)] = ldeepCopy(newUnitData) + end + + mist.DBs.unitsByName[newUnitData.unitName] = ldeepCopy(newUnitData) + mist.DBs.unitsByCat[mistCategory][#mist.DBs.unitsByCat[mistCategory] + 1] = ldeepCopy(newUnitData) + mist.DBs.unitsByNum[#mist.DBs.unitsByNum + 1] = ldeepCopy(newUnitData) + end + -- this is a really annoying DB to populate. Gotta create new tables in case its missing + if not mist.DBs.units[newTable.coalition] then + mist.DBs.units[newTable.coalition] = {} + end + + if not mist.DBs.units[newTable.coalition][newTable.country] then + mist.DBs.units[newTable.coalition][(newTable.country)] = {} + mist.DBs.units[newTable.coalition][(newTable.country)]['countryId'] = newTable.countryId + end + if not mist.DBs.units[newTable.coalition][newTable.country][mistCategory] then + mist.DBs.units[newTable.coalition][(newTable.country)][mistCategory] = {} + end + mist.DBs.units[newTable.coalition][(newTable.country)][mistCategory][#mist.DBs.units[newTable.coalition][(newTable.country)][mistCategory] + 1] = ldeepCopy(newTable) + + if newTable.groupId then + mist.DBs.groupsById[newTable.groupId] = ldeepCopy(newTable) + end + + mist.DBs.groupsByName[newTable.name] = ldeepCopy(newTable) + mist.DBs.dynGroupsAdded[#mist.DBs.dynGroupsAdded + 1] = ldeepCopy(newTable) + + writeGroups[x] = nil + if x%savesPerRun == 0 then + coroutine.yield() + end + end + if timer.getTime() > lastUpdateTime then + lastUpdateTime = timer.getTime() + end + end + end + + local update_alive_units_counter = 0 + local write_DB_table_counter = 0 + local check_spawn_events_counter = 0 + + -- THE MAIN FUNCTION -- Accessed 100 times/sec. + mist.main = function() + timer.scheduleFunction(mist.main, {}, timer.getTime() + 0.01) --reschedule first in case of Lua error + ---------------------------------------------------------------------------------------------------------- + --area to add new stuff in + write_DB_table_counter = write_DB_table_counter + 1 + if write_DB_table_counter == 10 then + + write_DB_table_counter = 0 + + if not coroutines.updateDBTables then + coroutines['updateDBTables'] = coroutine.create(updateDBTables) + end + + coroutine.resume(coroutines.updateDBTables) + + if coroutine.status(coroutines.updateDBTables) == 'dead' then + coroutines.updateDBTables = nil + end + end + + check_spawn_events_counter = check_spawn_events_counter + 1 + if check_spawn_events_counter == 10 then + + check_spawn_events_counter = 0 + + if not coroutines.checkSpawnedEvents then + coroutines['checkSpawnedEvents'] = coroutine.create(checkSpawnedEvents) + end + + coroutine.resume(coroutines.checkSpawnedEvents) + + if coroutine.status(coroutines.checkSpawnedEvents) == 'dead' then + coroutines.checkSpawnedEvents = nil + end + end + + ----------------------------------------------------------------------------------------------------------- + --updating alive units + update_alive_units_counter = update_alive_units_counter + 1 + if update_alive_units_counter == 5 then + update_alive_units_counter = 0 + + if not coroutines.update_alive_units then + coroutines['update_alive_units'] = coroutine.create(update_alive_units) + end + + coroutine.resume(coroutines.update_alive_units) + + if coroutine.status(coroutines.update_alive_units) == 'dead' then + coroutines.update_alive_units = nil + end + end + + mist.do_scheduled_functions() + end -- end of mist.main + -------------------------------------------- + ------------ mist dyn add stuff for coroutines + local mistGpId = 7000 + local mistUnitId = 7000 + local mistDynAddIndex = 1 + + + mist.nextGroupId = 1 + mist.nextUnitId = 1 + + mist.getNextUnitId = function() + mist.nextUnitId = mist.nextUnitId + 1 + if mist.nextUnitId > 6900 then + mist.nextUnitId = 14000 + end + return mist.nextUnitId + end + + mist.getNextGroupId = function() + mist.nextGroupId = mist.nextGroupId + 1 + if mist.nextGroupId > 6900 then + mist.nextGroupId = 14000 + end + return mist.nextGroupId + end + + mist.getLastDBUpdateTime = function() + return lastUpdateTime + end + + local function groupSpawned(event) + if event.id == world.event.S_EVENT_BIRTH and timer.getTime0() < timer.getAbsTime()then -- dont need to add units spawned in at the start of the mission if mist is loaded in init line + table.insert(tempSpawnedUnits,(event.initiator)) + end + end + + + + + mist.dynAddStatic = function(staticObj) + local newObj = {} + newObj.groupId = staticObj.groupId + newObj.category = staticObj.category + newObj.type = staticObj.type + newObj.unitId = staticObj.unitId + newObj.y = staticObj.y + newObj.x = staticObj.x + newObj.heading = staticObj.heading + newObj.name = staticObj.name + newObj.dead = staticObj.dead + newObj.country = staticObj.country + newObj.countryId = staticObj.countryId + newObj.clone = staticObj.clone + newObj.shape_name = staticObj.shape_name + newObj.canCargo = staticObj.canCargo + newObj.mass = staticObj.mass + newObj.categoryStatic = staticObj.categoryStatic + if staticObj.units then -- if its mist format + newObj.groupId = staticObj.units[1].groupId + newObj.category = staticObj.units[1].category + newObj.type = staticObj.units[1].type + newObj.unitId = staticObj.units[1].unitId + newObj.y = staticObj.units[1].y + newObj.x = staticObj.units[1].x + newObj.heading = staticObj.units[1].heading + newObj.name = staticObj.units[1].name + newObj.dead = staticObj.units[1].dead + newObj.country = staticObj.units[1].country + newObj.countryId = staticObj.units[1].countryId + newObj.shape_name = staticObj.units[1].shape_name + newObj.canCargo = staticObj.units[1].canCargo + newObj.mass = staticObj.units[1].mass + newObj.categoryStatic = staticObj.units[1].categoryStatic + end + + if not newObj.country then + return false + end + + local newCountry = newObj.country + if newObj.countryId then + newCountry = newObj.countryId + end + for countryId, countryName in pairs(country.name) do + if type(newObj.country) == 'string' then + if tostring(countryName) == string.upper(newObj.country) then + newCountry = countryName + end + elseif type(newObj.country) == 'number' then + if countryId == newObj.country then + newCountry = countryName + end + end + end + + if newObj.clone or not newObj.groupId then + mistGpId = mistGpId + 1 + newObj.groupId = mistGpId + end + + if newObj.clone or not newObj.unitId then + mistUnitId = mistUnitId + 1 + newObj.unitId = mistUnitId + end + + if newObj.clone or not newObj.name then + mistDynAddIndex = mistDynAddIndex + 1 + newObj.name = (country.name[newCountry] .. ' static ' .. mistDynAddIndex) + end + + if not newObj.dead then + newObj.dead = false + end + + if not newObj.heading then + newObj.heading = math.random(360) + end + + if newObj.categoryStatic then + newObj.category = newObj.categoryStatic + end + if newObj.mass then + newObj.category = 'Cargos' + end + mistAddedObjects[#mistAddedObjects + 1] = mist.utils.deepCopy(newObj) + if newObj.x and newObj.y and newObj.type and type(newObj.x) == 'number' and type(newObj.y) == 'number' and type(newObj.type) == 'string' then + coalition.addStaticObject(country.id[newCountry], newObj) + + return newObj + end + return false + end + + mist.dynAdd = function(newGroup) -- same as coalition.add function in SSE. checks the passed data to see if its valid. + --Will generate groupId, groupName, unitId, and unitName if needed + -- + + + + --mist.debug.writeData(mist.utils.serialize,{'msg', newGroup}, 'newGroupOrig.lua') + local cntry = newGroup.country + if newGroup.countryId then + cntry = newGroup.countryId + end + + local groupType = newGroup.category + local newCountry = '' + -- validate data + for countryId, countryName in pairs(country.name) do + if type(cntry) == 'string' then + cntry = cntry:gsub("%s+", "_") + if tostring(countryName) == string.upper(cntry) then + newCountry = countryName + end + elseif type(cntry) == 'number' then + if countryId == cntry then + newCountry = countryName + end + end + end + + if newCountry == '' then + return false + end + + local newCat = '' + for catName, catId in pairs(Unit.Category) do + if type(groupType) == 'string' then + if tostring(catName) == string.upper(groupType) then + newCat = catName + end + elseif type(groupType) == 'number' then + if catId == groupType then + newCat = catName + end + end + + if catName == 'GROUND_UNIT' and (string.upper(groupType) == 'VEHICLE' or string.upper(groupType) == 'GROUND') then + newCat = 'GROUND_UNIT' + elseif catName == 'AIRPLANE' and string.upper(groupType) == 'PLANE' then + newCat = 'AIRPLANE' + end + end + local typeName + if newCat == 'GROUND_UNIT' then + typeName = ' gnd ' + elseif newCat == 'AIRPLANE' then + typeName = ' air ' + elseif newCat == 'HELICOPTER' then + typeName = ' hel ' + elseif newCat == 'SHIP' then + typeName = ' shp ' + elseif newCat == 'BUILDING' then + typeName = ' bld ' + end + if newGroup.clone or not newGroup.groupId then + mistDynAddIndex = mistDynAddIndex + 1 + mistGpId = mistGpId + 1 + newGroup.groupId = mistGpId + end + if newGroup.groupName or newGroup.name then + if newGroup.groupName then + newGroup['name'] = newGroup.groupName + elseif newGroup.name then + newGroup['name'] = newGroup.name + end + end + + if newGroup.clone and mist.DBs.groupsByName[newGroup.name] or not newGroup.name then + newGroup['name'] = tostring(tostring(country.name[cntry]) .. tostring(typeName) .. mistDynAddIndex) + end + + if not newGroup.hidden then + newGroup.hidden = false + end + + if not newGroup.visible then + newGroup.visible = false + end + + if (newGroup.start_time and type(newGroup.start_time) ~= 'number') or not newGroup.start_time then + if newGroup.startTime then + newGroup.start_time = mist.utils.round(newGroup.startTime) + else + newGroup.start_time = 0 + end + end + + + for unitIndex, unitData in pairs(newGroup.units) do + local originalName = newGroup.units[unitIndex].unitName or newGroup.units[unitIndex].name + if newGroup.clone or not unitData.unitId then + mistUnitId = mistUnitId + 1 + newGroup.units[unitIndex]['unitId'] = mistUnitId + end + if newGroup.units[unitIndex].unitName or newGroup.units[unitIndex].name then + if newGroup.units[unitIndex].unitName then + newGroup.units[unitIndex].name = newGroup.units[unitIndex].unitName + elseif newGroup.units[unitIndex].name then + newGroup.units[unitIndex].name = newGroup.units[unitIndex].name + end + end + if newGroup.clone or not unitData.name then + newGroup.units[unitIndex].name = tostring(newGroup.name .. ' unit' .. unitIndex) + end + + if not unitData.skill then + newGroup.units[unitIndex].skill = 'Random' + end + + if not unitData.alt then + if newCat == 'AIRPLANE' then + newGroup.units[unitIndex].alt = 2000 + newGroup.units[unitIndex].alt_type = 'RADIO' + newGroup.units[unitIndex].speed = 150 + elseif newCat == 'HELICOPTER' then + newGroup.units[unitIndex].alt = 500 + newGroup.units[unitIndex].alt_type = 'RADIO' + newGroup.units[unitIndex].speed = 60 + else + --[[env.info('check height') + newGroup.units[unitIndex].alt = land.getHeight({x = newGroup.units[unitIndex].x, y = newGroup.units[unitIndex].y}) + newGroup.units[unitIndex].alt_type = 'BARO']] + end + + + end + + if newCat == 'AIRPLANE' or newCat == 'HELICOPTER' then + if newGroup.units[unitIndex].alt_type and newGroup.units[unitIndex].alt_type ~= 'BARO' or not newGroup.units[unitIndex].alt_type then + newGroup.units[unitIndex].alt_type = 'RADIO' + end + if not unitData.speed then + if newCat == 'AIRPLANE' then + newGroup.units[unitIndex].speed = 150 + elseif newCat == 'HELICOPTER' then + newGroup.units[unitIndex].speed = 60 + end + end + if not unitData.payload then + newGroup.units[unitIndex].payload = mist.getPayload(originalName) + end + end + mistAddedObjects[#mistAddedObjects + 1] = mist.utils.deepCopy(newGroup.units[unitIndex]) + end + mistAddedGroups[#mistAddedGroups + 1] = mist.utils.deepCopy(newGroup) + if newGroup.route and not newGroup.route.points then + if not newGroup.route.points and newGroup.route[1] then + local copyRoute = newGroup.route + newGroup.route = {} + newGroup.route.points = copyRoute + end + end + newGroup.country = newCountry + + + --mist.debug.writeData(mist.utils.serialize,{'msg', newGroup}, 'newGroup.lua') + + -- sanitize table + newGroup.groupName = nil + newGroup.clone = nil + newGroup.category = nil + newGroup.country = nil + + newGroup.tasks = {} + + for unitIndex, unitData in pairs(newGroup.units) do + newGroup.units[unitIndex].unitName = nil + end + + coalition.addGroup(country.id[newCountry], Unit.Category[newCat], newGroup) + + return newGroup + + end + + --------------------------------------------------------------------------------------------- + --Modified Slmod task scheduler, superior to timer.scheduleFunction + + local Tasks = {} + local task_id = 0 + --[[ mist.scheduleFunction: + int id = mist.schedule_task(f function, vars table, t number, rep number, st number) + id - integer id of this function task + f - function to run + vars - table of vars for that function + t - time to run function + rep - time between repetitions of this function (OPTIONAL) + st - time when repetitions of this function will stop automatically (OPTIONAL) + ]] + mist.scheduleFunction = function(f, vars, t, rep, st) + --verify correct types + assert(type(f) == 'function', 'variable 1, expected function, got ' .. type(f)) + assert(type(vars) == 'table' or vars == nil, 'variable 2, expected table or nil, got ' .. type(f)) + assert(type(t) == 'number', 'variable 3, expected number, got ' .. type(t)) + assert(type(rep) == 'number' or rep == nil, 'variable 4, expected number or nil, got ' .. type(rep)) + assert(type(st) == 'number' or st == nil, 'variable 5, expected number or nil, got ' .. type(st)) + if not vars then + vars = {} + end + task_id = task_id + 1 + table.insert(Tasks, {f = f, vars = vars, t = t, rep = rep, st = st, id = task_id}) + return task_id + end + + -- removes a scheduled function based on the function's id. returns true if successful, false if not successful. + mist.removeFunction = function(id) + local i = 1 + while i <= #Tasks do + if Tasks[i].id == id then + table.remove(Tasks, i) + else + i = i + 1 + end + end + end + + -------------------------------------------------------------------------------------------------------------------- + -- not intended for users to use this function. + mist.do_scheduled_functions = function() + local i = 1 + while i <= #Tasks do + if not Tasks[i].rep then -- not a repeated process + if Tasks[i].t <= timer.getTime() then + local Task = Tasks[i] -- local reference + table.remove(Tasks, i) + local err, errmsg = pcall(Task.f, unpack(Task.vars, 1, table.maxn(Task.vars))) + if not err then + env.info('mist.scheduleFunction, error in scheduled function: ' .. errmsg) + end + --Task.f(unpack(Task.vars, 1, table.maxn(Task.vars))) -- do the task, do not increment i + else + i = i + 1 + end + else + if Tasks[i].st and Tasks[i].st <= timer.getTime() then --if a stoptime was specified, and the stop time exceeded + table.remove(Tasks, i) -- stop time exceeded, do not execute, do not increment i + elseif Tasks[i].t <= timer.getTime() then + local Task = Tasks[i] -- local reference + Task.t = timer.getTime() + Task.rep --schedule next run + local err, errmsg = pcall(Task.f, unpack(Task.vars, 1, table.maxn(Task.vars))) + if not err then + env.info('mist.scheduleFunction, error in scheduled function: ' .. errmsg) + end + --Tasks[i].f(unpack(Tasks[i].vars, 1, table.maxn(Tasks[i].vars))) -- do the task + i = i + 1 + else + i = i + 1 + end + end + end + end + + + + local idNum = 0 + + --Simplified event handler + mist.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) + return handler.id + end + + mist.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 + + mist.addEventHandler(groupSpawned) + -- mist.scheduleFunction(checkSpawnedEvents, {}, timer.getTime() + 5, 1) - mist.addEventHandler(groupSpawned) --- mist.scheduleFunction(checkSpawnedEvents, {}, timer.getTime() + 5, 1) - end ------------------------------------------------------------------------------------------------------------ @@ -886,166 +886,166 @@ end mist.utils = {} mist.utils.toDegree = function(angle) - return angle*180/math.pi + return angle*180/math.pi end mist.utils.toRadian = function(angle) - return angle*math.pi/180 + return angle*math.pi/180 end mist.utils.metersToNM = function(meters) - return meters/1852 + return meters/1852 end mist.utils.metersToFeet = function(meters) - return meters/0.3048 + return meters/0.3048 end mist.utils.NMToMeters = function(NM) - return NM*1852 + return NM*1852 end mist.utils.feetToMeters = function(feet) - return feet*0.3048 + return feet*0.3048 end mist.utils.mpsToKnots = function(mps) - return mps*3600/1852 + return mps*3600/1852 end mist.utils.mpsToKmph = function(mps) - return mps*3.6 + return mps*3.6 end mist.utils.knotsToMps = function(knots) - return knots*1852/3600 + return knots*1852/3600 end mist.utils.kmphToMps = function(kmph) - return kmph/3.6 + return kmph/3.6 end function mist.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 + 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 mist.utils.makeVec3(Vec2, y) - if not Vec2.z then - if Vec2.alt and not y then - y = Vec2.alt - elseif 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 + if not Vec2.z then + if Vec2.alt and not y then + y = Vec2.alt + elseif 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 mist.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 + 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 mist.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 + 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 mist.utils.getDir(vec, point) - local dir = math.atan2(vec.z, vec.x) - if point then - dir = dir + mist.getNorthCorrection(point) - end - if dir < 0 then - dir = dir + 2*math.pi -- put dir in range of 0 to 2*pi - end - return dir + local dir = math.atan2(vec.z, vec.x) + if point then + dir = dir + mist.getNorthCorrection(point) + end + 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 mist.utils.get2DDist(point1, point2) - point1 = mist.utils.makeVec3(point1) - point2 = mist.utils.makeVec3(point2) - return mist.vec.mag({x = point1.x - point2.x, y = 0, z = point1.z - point2.z}) + point1 = mist.utils.makeVec3(point1) + point2 = mist.utils.makeVec3(point2) + return mist.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 mist.utils.get3DDist(point1, point2) - return mist.vec.mag({x = point1.x - point2.x, y = point1.y - point2.y, z = point1.z - point2.z}) + return mist.vec.mag({x = point1.x - point2.x, y = point1.y - point2.y, z = point1.z - point2.z}) end function mist.utils.vecToWP(vec) - local newWP = {} - newWP.x = vec.x - newWP.y = vec.y - if vec.z then - newWP.alt = vec.y - newWP.y = vec.z - else - newWP.alt = land.getHeight({x = vec.x, y = vec.y}) - end - return newWP + local newWP = {} + newWP.x = vec.x + newWP.y = vec.y + if vec.z then + newWP.alt = vec.y + newWP.y = vec.z + else + newWP.alt = land.getHeight({x = vec.x, y = vec.y}) + end + return newWP end function mist.utils.unitToWP(unit) - if type(unit) == 'string' then - if Unit.getByName(unit) then - unit = Unit.getByName(unit) - end - end - if unit:isExist() == true then - local new = mist.utils.vecToWP(unit:getPosition().p) - new.speed = mist.vec.mag(unit:getVelocity()) - new.alt_type = "BARO" - - return new - end - return false + if type(unit) == 'string' then + if Unit.getByName(unit) then + unit = Unit.getByName(unit) + end + end + if unit:isExist() == true then + local new = mist.utils.vecToWP(unit:getPosition().p) + new.speed = mist.vec.mag(unit:getVelocity()) + new.alt_type = "BARO" + + return new + end + return false end --from http://lua-users.org/wiki/CopyTable mist.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 - return _copy(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 + return _copy(object) end -- From http://lua-users.org/wiki/SimpleRound @@ -1056,22 +1056,22 @@ mist.utils.round = function(num, idp) end mist.utils.roundTbl = function(tbl, idp) - for id, val in pairs(tbl) do - if type(val) == 'number' then - tbl[id] = mist.utils.round(val, idp) - end - end - return tbl + for id, val in pairs(tbl) do + if type(val) == 'number' then + tbl[id] = mist.utils.round(val, idp) + end + end + return tbl end -- porting in Slmod's dostring mist.utils.dostring = function(s) - local f, err = loadstring(s) - if f then - return true, f() - else - return false, err - end + local f, err = loadstring(s) + if f then + return true, f() + else + return false, err + end end @@ -1079,340 +1079,340 @@ end Type-checking function: Checks a var_tbl to a type_tbl. Returns true if the var_tbl passes the type check, returns false plus an error message if the var_tbl fails. -type_tbl examples: -type_tbl = { {'table', 'number'}, 'string', 'number', 'number', {'string', 'nil'}, {'number', 'nil'} } +type_tbl examples: +type_tbl = { {'table', 'number'}, 'string', 'number', 'number', {'string', 'nil'}, {'number', 'nil'} } Compare to a var_tbl with up to six entries; var_tbl index 1 must be a table or a number; index 2, a string; index 3, a number; index 4, a number; index 5, either a string or nil; and index 6, either a number or nil. Another example: type_tbl = { {'text', 'msg', 'text_out'} = 'string', display_time = 'number', display_mode = {'string', 'nil'} coa = {'string', 'nil'}} -var_tbl must have a string at one of the following table keys: "text", "msg", or "text_out". var_tbl must have a number at table key "display_time", +var_tbl must have a string at one of the following table keys: "text", "msg", or "text_out". var_tbl must have a number at table key "display_time", the table key "display_mode" must be either a string or nil, and the table key "coa" must be either a string or nil. ]] function mist.utils.typeCheck(fname, type_tbl, var_tbl) - --env.info('type check') - for type_key, type_val in pairs(type_tbl) do - --print('type_key:') - --print(type_key) - --print('type_val:') - --print(type_val) - - --type_key can be a table of accepted keys- so try to find one that is not nil - local type_key_str = '' - local act_key = type_key -- actual key within var_tbl - necessary to use for multiple possible key variables. Initialize to type_key - if type(type_key) == 'table' then - - for i = 1, #type_key do - if i ~= 1 then - type_key_str = type_key_str .. '/' - end - type_key_str = type_key_str .. tostring(type_key[i]) - if var_tbl[type_key[i]] ~= nil then - act_key = type_key[i] -- found a non-nil entry, make act_key now this val. - end - end - else - type_key_str = tostring(type_key) - end - - local err_msg = 'Error in function ' .. fname .. ', parameter "' .. type_key_str .. '", expected: ' - local passed_check = false - - if type(type_tbl[type_key]) == 'table' then - --print('err_msg, before and after:') - --print(err_msg) - for j = 1, #type_tbl[type_key] do - - if j == 1 then - err_msg = err_msg .. type_tbl[type_key][j] - else - err_msg = err_msg .. ' or ' .. type_tbl[type_key][j] - end - - if type(var_tbl[act_key]) == type_tbl[type_key][j] then - passed_check = true - end - end - --print(err_msg) - else - --print('err_msg, before and after:') - --print(err_msg) - err_msg = err_msg .. type_tbl[type_key] - --print(err_msg) - if type(var_tbl[act_key]) == type_tbl[type_key] then - passed_check = true - - end - - end - - if not passed_check then - err_msg = err_msg .. ', got ' .. type(var_tbl[act_key]) - return false, err_msg - end - end - return true + --env.info('type check') + for type_key, type_val in pairs(type_tbl) do + --print('type_key:') + --print(type_key) + --print('type_val:') + --print(type_val) + + --type_key can be a table of accepted keys- so try to find one that is not nil + local type_key_str = '' + local act_key = type_key -- actual key within var_tbl - necessary to use for multiple possible key variables. Initialize to type_key + if type(type_key) == 'table' then + + for i = 1, #type_key do + if i ~= 1 then + type_key_str = type_key_str .. '/' + end + type_key_str = type_key_str .. tostring(type_key[i]) + if var_tbl[type_key[i]] ~= nil then + act_key = type_key[i] -- found a non-nil entry, make act_key now this val. + end + end + else + type_key_str = tostring(type_key) + end + + local err_msg = 'Error in function ' .. fname .. ', parameter "' .. type_key_str .. '", expected: ' + local passed_check = false + + if type(type_tbl[type_key]) == 'table' then + --print('err_msg, before and after:') + --print(err_msg) + for j = 1, #type_tbl[type_key] do + + if j == 1 then + err_msg = err_msg .. type_tbl[type_key][j] + else + err_msg = err_msg .. ' or ' .. type_tbl[type_key][j] + end + + if type(var_tbl[act_key]) == type_tbl[type_key][j] then + passed_check = true + end + end + --print(err_msg) + else + --print('err_msg, before and after:') + --print(err_msg) + err_msg = err_msg .. type_tbl[type_key] + --print(err_msg) + if type(var_tbl[act_key]) == type_tbl[type_key] then + passed_check = true + + end + + end + + if not passed_check then + err_msg = err_msg .. ', got ' .. type(var_tbl[act_key]) + return false, err_msg + end + end + return true end --porting in Slmod's "safestring" basic serialize mist.utils.basicSerialize = function(s) - if s == nil then - return "\"\"" - else - if ((type(s) == 'number') or (type(s) == 'boolean') or (type(s) == 'function') or (type(s) == 'table') or (type(s) == 'userdata') ) then - return tostring(s) - elseif type(s) == 'string' then - s = string.format('%q', s) - return s - end - end + if s == nil then + return "\"\"" + else + if ((type(s) == 'number') or (type(s) == 'boolean') or (type(s) == 'function') or (type(s) == 'table') or (type(s) == 'userdata') ) then + return tostring(s) + elseif type(s) == 'string' then + s = string.format('%q', s) + return s + end + end end --porting in Slmod's serialize_slmod mist.utils.serialize = function(name, value, level) - -----Based on ED's serialize_simple2 - local basicSerialize = function (o) - if type(o) == "number" then - return tostring(o) - elseif type(o) == "boolean" then - return tostring(o) - else -- assume it is a string - return mist.utils.basicSerialize(o) - end - end - - local serialize_to_t = function (name, value, level) - ----Based on ED's serialize_simple2 + -----Based on ED's serialize_simple2 + local basicSerialize = function (o) + if type(o) == "number" then + return tostring(o) + elseif type(o) == "boolean" then + return tostring(o) + else -- assume it is a string + return mist.utils.basicSerialize(o) + end + end + + local serialize_to_t = function (name, value, level) + ----Based on ED's serialize_simple2 - local var_str_tbl = {} - if level == nil then level = "" end - if level ~= "" then level = level.." " end - - table.insert(var_str_tbl, level .. name .. " = ") - - if type(value) == "number" or type(value) == "string" or type(value) == "boolean" then - table.insert(var_str_tbl, basicSerialize(value) .. ",\n") - elseif type(value) == "table" then - table.insert(var_str_tbl, "\n"..level.."{\n") - - for k,v in pairs(value) do -- serialize its fields - local key - if type(k) == "number" then - key = string.format("[%s]", k) - else - key = string.format("[%q]", k) - end + local var_str_tbl = {} + if level == nil then level = "" end + if level ~= "" then level = level.." " end - table.insert(var_str_tbl, mist.utils.serialize(key, v, level.." ")) + table.insert(var_str_tbl, level .. name .. " = ") - end - if level == "" then - table.insert(var_str_tbl, level.."} -- end of "..name.."\n") + if type(value) == "number" or type(value) == "string" or type(value) == "boolean" then + table.insert(var_str_tbl, basicSerialize(value) .. ",\n") + elseif type(value) == "table" then + table.insert(var_str_tbl, "\n"..level.."{\n") - else - table.insert(var_str_tbl, level.."}, -- end of "..name.."\n") + for k,v in pairs(value) do -- serialize its fields + local key + if type(k) == "number" then + key = string.format("[%s]", k) + else + key = string.format("[%q]", k) + end - end - else - env.info("Cannot serialize a "..type(value)) - end - return var_str_tbl - end - - local t_str = serialize_to_t(name, value, level) - - return table.concat(t_str) + table.insert(var_str_tbl, mist.utils.serialize(key, v, level.." ")) + + end + if level == "" then + table.insert(var_str_tbl, level.."} -- end of "..name.."\n") + + else + table.insert(var_str_tbl, level.."}, -- end of "..name.."\n") + + end + else + env.info("Cannot serialize a "..type(value)) + end + return var_str_tbl + end + + local t_str = serialize_to_t(name, value, level) + + return table.concat(t_str) end -- porting in slmod's serialize_wcycles mist.utils.serializeWithCycles = function(name, value, saved) - --mostly straight out of Programming in Lua - local basicSerialize = function (o) - if type(o) == "number" then - return tostring(o) - elseif type(o) == "boolean" then - return tostring(o) - else -- assume it is a string - return mist.utils.basicSerialize(o) - end - end - - local t_str = {} - saved = saved or {} -- initial value - if ((type(value) == 'string') or (type(value) == 'number') or (type(value) == 'table') or (type(value) == 'boolean')) then - table.insert(t_str, name .. " = ") - if type(value) == "number" or type(value) == "string" or type(value) == "boolean" then - table.insert(t_str, basicSerialize(value) .. "\n") - else + --mostly straight out of Programming in Lua + local basicSerialize = function (o) + if type(o) == "number" then + return tostring(o) + elseif type(o) == "boolean" then + return tostring(o) + else -- assume it is a string + return mist.utils.basicSerialize(o) + end + end - if saved[value] then -- value already saved? - table.insert(t_str, saved[value] .. "\n") - else - saved[value] = name -- save name for next time - table.insert(t_str, "{}\n") - for k,v in pairs(value) do -- save its fields - local fieldname = string.format("%s[%s]", name, basicSerialize(k)) - table.insert(t_str, mist.utils.serializeWithCycles(fieldname, v, saved)) - end - end - end - return table.concat(t_str) - else - return "" - end + local t_str = {} + saved = saved or {} -- initial value + if ((type(value) == 'string') or (type(value) == 'number') or (type(value) == 'table') or (type(value) == 'boolean')) then + table.insert(t_str, name .. " = ") + if type(value) == "number" or type(value) == "string" or type(value) == "boolean" then + table.insert(t_str, basicSerialize(value) .. "\n") + else + + if saved[value] then -- value already saved? + table.insert(t_str, saved[value] .. "\n") + else + saved[value] = name -- save name for next time + table.insert(t_str, "{}\n") + for k,v in pairs(value) do -- save its fields + local fieldname = string.format("%s[%s]", name, basicSerialize(k)) + table.insert(t_str, mist.utils.serializeWithCycles(fieldname, v, saved)) + end + end + end + return table.concat(t_str) + else + return "" + end end -- porting in Slmod's serialize_slmod2 mist.utils.oneLineSerialize = function(tbl) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function - if type(tbl) == 'table' then --function only works for tables! + if type(tbl) == 'table' then --function only works for tables! - local tbl_str = {} + local tbl_str = {} - tbl_str[#tbl_str + 1] = '{ ' - - for ind,val in pairs(tbl) do -- serialize its fields - if type(ind) == "number" then - tbl_str[#tbl_str + 1] = '[' - tbl_str[#tbl_str + 1] = tostring(ind) - tbl_str[#tbl_str + 1] = '] = ' - else --must be a string - tbl_str[#tbl_str + 1] = '[' - tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(ind) - tbl_str[#tbl_str + 1] = '] = ' - end - - if ((type(val) == 'number') or (type(val) == 'boolean')) then - tbl_str[#tbl_str + 1] = tostring(val) - tbl_str[#tbl_str + 1] = ', ' - elseif type(val) == 'string' then - tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(val) - tbl_str[#tbl_str + 1] = ', ' - elseif type(val) == 'nil' then -- won't ever happen, right? - tbl_str[#tbl_str + 1] = 'nil, ' - elseif type(val) == 'table' then - tbl_str[#tbl_str + 1] = mist.utils.oneLineSerialize(val) - tbl_str[#tbl_str + 1] = ', ' --I think this is right, I just added it - else - env.info('unable to serialize value type ' .. mist.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind)) - end - - end - tbl_str[#tbl_str + 1] = '}' - return table.concat(tbl_str) - end + tbl_str[#tbl_str + 1] = '{ ' + + for ind,val in pairs(tbl) do -- serialize its fields + if type(ind) == "number" then + tbl_str[#tbl_str + 1] = '[' + tbl_str[#tbl_str + 1] = tostring(ind) + tbl_str[#tbl_str + 1] = '] = ' + else --must be a string + tbl_str[#tbl_str + 1] = '[' + tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(ind) + tbl_str[#tbl_str + 1] = '] = ' + end + + if ((type(val) == 'number') or (type(val) == 'boolean')) then + tbl_str[#tbl_str + 1] = tostring(val) + tbl_str[#tbl_str + 1] = ', ' + elseif type(val) == 'string' then + tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(val) + tbl_str[#tbl_str + 1] = ', ' + elseif type(val) == 'nil' then -- won't ever happen, right? + tbl_str[#tbl_str + 1] = 'nil, ' + elseif type(val) == 'table' then + tbl_str[#tbl_str + 1] = mist.utils.oneLineSerialize(val) + tbl_str[#tbl_str + 1] = ', ' --I think this is right, I just added it + else + env.info('unable to serialize value type ' .. mist.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind)) + end + + end + tbl_str[#tbl_str + 1] = '}' + return table.concat(tbl_str) + end end --Function to create string for viewing the contents of a table -NOT for serialization mist.utils.tableShow = function(tbl, loc, indent, tableshow_tbls) --based on serialize_slmod, this is a _G serialization - tableshow_tbls = tableshow_tbls or {} --create table of tables - loc = loc or "" - indent = indent or "" - if type(tbl) == 'table' then --function only works for tables! - tableshow_tbls[tbl] = loc - - local tbl_str = {} + tableshow_tbls = tableshow_tbls or {} --create table of tables + loc = loc or "" + indent = indent or "" + if type(tbl) == 'table' then --function only works for tables! + tableshow_tbls[tbl] = loc - tbl_str[#tbl_str + 1] = indent .. '{\n' - - for ind,val in pairs(tbl) do -- serialize its fields - if type(ind) == "number" then - tbl_str[#tbl_str + 1] = indent - tbl_str[#tbl_str + 1] = loc .. '[' - tbl_str[#tbl_str + 1] = tostring(ind) - tbl_str[#tbl_str + 1] = '] = ' - else - tbl_str[#tbl_str + 1] = indent - tbl_str[#tbl_str + 1] = loc .. '[' - tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(ind) - tbl_str[#tbl_str + 1] = '] = ' - end - - if ((type(val) == 'number') or (type(val) == 'boolean')) then - tbl_str[#tbl_str + 1] = tostring(val) - tbl_str[#tbl_str + 1] = ',\n' - elseif type(val) == 'string' then - tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(val) - tbl_str[#tbl_str + 1] = ',\n' - elseif type(val) == 'nil' then -- won't ever happen, right? - tbl_str[#tbl_str + 1] = 'nil,\n' - elseif type(val) == 'table' then - if tableshow_tbls[val] then - tbl_str[#tbl_str + 1] = tostring(val) .. ' already defined: ' .. tableshow_tbls[val] .. ',\n' - else - tableshow_tbls[val] = loc .. '[' .. mist.utils.basicSerialize(ind) .. ']' - tbl_str[#tbl_str + 1] = tostring(val) .. ' ' - tbl_str[#tbl_str + 1] = mist.utils.tableShow(val, loc .. '[' .. mist.utils.basicSerialize(ind).. ']', indent .. ' ', tableshow_tbls) - tbl_str[#tbl_str + 1] = ',\n' - end - elseif type(val) == 'function' then - if debug and debug.getinfo then - local fcnname = tostring(val) - local info = debug.getinfo(val, "S") - if info.what == "C" then - tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', C function') .. ',\n' - else - if (string.sub(info.source, 1, 2) == [[./]]) then - tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', defined in (' .. info.linedefined .. '-' .. info.lastlinedefined .. ')' .. info.source) ..',\n' - else - tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', defined in (' .. info.linedefined .. '-' .. info.lastlinedefined .. ')') ..',\n' - end - end - - else - tbl_str[#tbl_str + 1] = 'a function,\n' - end - else - tbl_str[#tbl_str + 1] = 'unable to serialize value type ' .. mist.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind) - end - end - - tbl_str[#tbl_str + 1] = indent .. '}' - return table.concat(tbl_str) - end + local tbl_str = {} + + tbl_str[#tbl_str + 1] = indent .. '{\n' + + for ind,val in pairs(tbl) do -- serialize its fields + if type(ind) == "number" then + tbl_str[#tbl_str + 1] = indent + tbl_str[#tbl_str + 1] = loc .. '[' + tbl_str[#tbl_str + 1] = tostring(ind) + tbl_str[#tbl_str + 1] = '] = ' + else + tbl_str[#tbl_str + 1] = indent + tbl_str[#tbl_str + 1] = loc .. '[' + tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(ind) + tbl_str[#tbl_str + 1] = '] = ' + end + + if ((type(val) == 'number') or (type(val) == 'boolean')) then + tbl_str[#tbl_str + 1] = tostring(val) + tbl_str[#tbl_str + 1] = ',\n' + elseif type(val) == 'string' then + tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(val) + tbl_str[#tbl_str + 1] = ',\n' + elseif type(val) == 'nil' then -- won't ever happen, right? + tbl_str[#tbl_str + 1] = 'nil,\n' + elseif type(val) == 'table' then + if tableshow_tbls[val] then + tbl_str[#tbl_str + 1] = tostring(val) .. ' already defined: ' .. tableshow_tbls[val] .. ',\n' + else + tableshow_tbls[val] = loc .. '[' .. mist.utils.basicSerialize(ind) .. ']' + tbl_str[#tbl_str + 1] = tostring(val) .. ' ' + tbl_str[#tbl_str + 1] = mist.utils.tableShow(val, loc .. '[' .. mist.utils.basicSerialize(ind).. ']', indent .. ' ', tableshow_tbls) + tbl_str[#tbl_str + 1] = ',\n' + end + elseif type(val) == 'function' then + if debug and debug.getinfo then + local fcnname = tostring(val) + local info = debug.getinfo(val, "S") + if info.what == "C" then + tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', C function') .. ',\n' + else + if (string.sub(info.source, 1, 2) == [[./]]) then + tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', defined in (' .. info.linedefined .. '-' .. info.lastlinedefined .. ')' .. info.source) ..',\n' + else + tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', defined in (' .. info.linedefined .. '-' .. info.lastlinedefined .. ')') ..',\n' + end + end + + else + tbl_str[#tbl_str + 1] = 'a function,\n' + end + else + tbl_str[#tbl_str + 1] = 'unable to serialize value type ' .. mist.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind) + end + end + + tbl_str[#tbl_str + 1] = indent .. '}' + return table.concat(tbl_str) + end end mist.debug = {} mist.debug.dump_G = function(fname) - if lfs and io then - local fdir = lfs.writedir() .. [[Logs\]] .. fname - local f = io.open(fdir, 'w') - f:write(mist.utils.tableShow(_G)) - f:close() - local errmsg = 'mist.debug.dump_G wrote data to ' .. fdir - env.info(errmsg) - trigger.action.outText(errmsg, 10) - else - local errmsg = 'Error: insufficient libraries to run mist.debug.dump_G, you must disable the sanitization of the io and lfs libraries in ./Scripts/MissionScripting.lua' - env.info(errmsg) - trigger.action.outText(errmsg, 10) - end + if lfs and io then + local fdir = lfs.writedir() .. [[Logs\]] .. fname + local f = io.open(fdir, 'w') + f:write(mist.utils.tableShow(_G)) + f:close() + local errmsg = 'mist.debug.dump_G wrote data to ' .. fdir + env.info(errmsg) + trigger.action.outText(errmsg, 10) + else + local errmsg = 'Error: insufficient libraries to run mist.debug.dump_G, you must disable the sanitization of the io and lfs libraries in ./Scripts/MissionScripting.lua' + env.info(errmsg) + trigger.action.outText(errmsg, 10) + end end mist.debug.writeData = function(fcn, fcnVars, fname) - if lfs and io then - local fdir = lfs.writedir() .. [[Logs\]] .. fname - local f = io.open(fdir, 'w') - f:write(fcn(unpack(fcnVars, 1, table.maxn(fcnVars)))) - f:close() - local errmsg = 'mist.debug.writeData wrote data to ' .. fdir - env.info(errmsg) - trigger.action.outText(errmsg, 10) - else - local errmsg = 'Error: insufficient libraries to run mist.debug.writeData, you must disable the sanitization of the io and lfs libraries in ./Scripts/MissionScripting.lua' - env.info(errmsg) - trigger.action.outText(errmsg, 10) - end + if lfs and io then + local fdir = lfs.writedir() .. [[Logs\]] .. fname + local f = io.open(fdir, 'w') + f:write(fcn(unpack(fcnVars, 1, table.maxn(fcnVars)))) + f:close() + local errmsg = 'mist.debug.writeData wrote data to ' .. fdir + env.info(errmsg) + trigger.action.outText(errmsg, 10) + else + local errmsg = 'Error: insufficient libraries to run mist.debug.writeData, you must disable the sanitization of the io and lfs libraries in ./Scripts/MissionScripting.lua' + env.info(errmsg) + trigger.action.outText(errmsg, 10) + end end mist.debug.dumpDBs = function() - for DBname, DB in pairs(mist.DBs) do - if type(DB) == 'table' and type(DBname) == 'string' then - mist.debug.writeData(mist.utils.serialize, {DBname, DB}, 'mist_DBs_' .. DBname .. '.lua') - end - end + for DBname, DB in pairs(mist.DBs) do + if type(DB) == 'table' and type(DBname) == 'string' then + mist.debug.writeData(mist.utils.serialize, {DBname, DB}, 'mist_DBs_' .. DBname .. '.lua') + end + end end ----------------------------------------------------------------------------------------------------------------- @@ -1420,432 +1420,432 @@ end mist.vec = {} mist.vec.add = function(vec1, vec2) - return {x = vec1.x + vec2.x, y = vec1.y + vec2.y, z = vec1.z + vec2.z} + return {x = vec1.x + vec2.x, y = vec1.y + vec2.y, z = vec1.z + vec2.z} end mist.vec.sub = function(vec1, vec2) - return {x = vec1.x - vec2.x, y = vec1.y - vec2.y, z = vec1.z - vec2.z} + return {x = vec1.x - vec2.x, y = vec1.y - vec2.y, z = vec1.z - vec2.z} end mist.vec.scalarMult = function(vec, mult) - return {x = vec.x*mult, y = vec.y*mult, z = vec.z*mult} + return {x = vec.x*mult, y = vec.y*mult, z = vec.z*mult} end mist.vec.scalar_mult = mist.vec.scalarMult mist.vec.dp = function(vec1, vec2) - return vec1.x*vec2.x + vec1.y*vec2.y + vec1.z*vec2.z + return vec1.x*vec2.x + vec1.y*vec2.y + vec1.z*vec2.z end mist.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} + 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 mist.vec.mag = function(vec) - return (vec.x^2 + vec.y^2 + vec.z^2)^0.5 + return (vec.x^2 + vec.y^2 + vec.z^2)^0.5 end -mist.vec.getUnitVec = function(vec) - local mag = mist.vec.mag(vec) - return { x = vec.x/mag, y = vec.y/mag, z = vec.z/mag } +mist.vec.getUnitVec = function(vec) + local mag = mist.vec.mag(vec) + return { x = vec.x/mag, y = vec.y/mag, z = vec.z/mag } end mist.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)} + 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. -mist.tostringMGRS = function(MGRS, acc) - if acc == 0 then - return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph - else - return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph .. ' ' .. string.format('%0' .. acc .. 'd', mist.utils.round(MGRS.Easting/(10^(5-acc)), 0)) - .. ' ' .. string.format('%0' .. acc .. 'd', mist.utils.round(MGRS.Northing/(10^(5-acc)), 0)) - end +mist.tostringMGRS = function(MGRS, acc) + if acc == 0 then + return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph + else + return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph .. ' ' .. string.format('%0' .. acc .. 'd', mist.utils.round(MGRS.Easting/(10^(5-acc)), 0)) + .. ' ' .. string.format('%0' .. acc .. 'd', mist.utils.round(MGRS.Northing/(10^(5-acc)), 0)) + end end ---[[acc: -in DM: decimal point of minutes. +--[[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. ]] mist.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 = mist.utils.round((oldLatMin - latMin)*60, acc) - - local oldLonMin = lonMin - lonMin = math.floor(lonMin) - local lonSec = mist.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 = mist.utils.round(latMin, acc) - lonMin = mist.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 + 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 = mist.utils.round((oldLatMin - latMin)*60, acc) + + local oldLonMin = lonMin + lonMin = math.floor(lonMin) + local lonSec = mist.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 = mist.utils.round(latMin, acc) + lonMin = mist.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.]] + 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.]] mist.tostringBR = function(az, dist, alt, metric) - az = mist.utils.round(mist.utils.toDegree(az), 0) - - if metric then - dist = mist.utils.round(dist/1000, 0) - else - dist = mist.utils.round(mist.utils.metersToNM(dist), 0) - end - - local s = string.format('%03d', az) .. ' for ' .. dist - - if alt then - if metric then - s = s .. ' at ' .. mist.utils.round(alt, 0) - else - s = s .. ' at ' .. mist.utils.round(mist.utils.metersToFeet(alt), 0) - end - end - return s + az = mist.utils.round(mist.utils.toDegree(az), 0) + + if metric then + dist = mist.utils.round(dist/1000, 0) + else + dist = mist.utils.round(mist.utils.metersToNM(dist), 0) + end + + local s = string.format('%03d', az) .. ' for ' .. dist + + if alt then + if metric then + s = s .. ' at ' .. mist.utils.round(alt, 0) + else + s = s .. ' at ' .. mist.utils.round(mist.utils.metersToFeet(alt), 0) + end + end + return s end mist.getNorthCorrection = function(gPoint) --gets the correction needed for true north - local point = mist.utils.deepCopy(gPoint) - 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) + local point = mist.utils.deepCopy(gPoint) + 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 mist.getUnitSkill = function(unitName) - if Unit.getByName(unitName) and Unit.getByName(unitName):isExist() == true then - local lunit = Unit.getByName(unitName) - for name, data in pairs(mist.DBs.unitsByName) do - if name == unitName and data.type == lunit:getTypeName() and data.unitId == lunit:getID() and data.skill then - return data.skill - end - end - end - return false + if Unit.getByName(unitName) and Unit.getByName(unitName):isExist() == true then + local lunit = Unit.getByName(unitName) + for name, data in pairs(mist.DBs.unitsByName) do + if name == unitName and data.type == lunit:getTypeName() and data.unitId == lunit:getID() and data.skill then + return data.skill + end + end + end + return false end function mist.getGroupPoints(groupIdent) -- if groupname exists in env.mission, then returns table of the group's points in numerical order, such as: { [1] = {x = 299435.224, y = -1146632.6773}, [2] = { x = 663324.6563, y = 322424.1112}} - -- 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 = mist.DBs.MEgroupsByName[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 - if not point.point then - points[point_num] = { x = point.x, y = point.y } - else - points[point_num] = point.point --it's possible that the ME could move to the point = Vec2 notation. - end - 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 + -- 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 = mist.DBs.MEgroupsByName[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 + if not point.point then + points[point_num] = { x = point.x, y = point.y } + else + points[point_num] = point.point --it's possible that the ME could move to the point = Vec2 notation. + end + 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 --[[ table attitude = getAttitude(string unitname) -- will work on any unit, even if not an aircraft. -attitude = { - Heading = number, -- in radians, range of 0 to 2*pi, relative to true north - Pitch = number, -- in radians, range of -pi/2 to pi/2 - Roll = number, -- in radians, range of 0 to 2*pi, right roll is positive direction - - --Yaw, AoA, ClimbAngle - relative to earth reference- DOES NOT TAKE INTO ACCOUNT WIND. - Yaw = number, -- in radians, range of -pi to pi, right yaw is positive direction - AoA = number, -- in radians, range of -pi to pi, rotation of aircraft to the right in comparison to flight direction being positive - ClimbAngle = number, -- in radians, range of -pi/2 to pi/2 - - --Maybe later? - AxialVel = table, velocity of the aircraft transformed into directions of aircraft axes - Speed = number -- absolute velocity in meters/sec +attitude = { + Heading = number, -- in radians, range of 0 to 2*pi, relative to true north + Pitch = number, -- in radians, range of -pi/2 to pi/2 + Roll = number, -- in radians, range of 0 to 2*pi, right roll is positive direction - } + --Yaw, AoA, ClimbAngle - relative to earth reference- DOES NOT TAKE INTO ACCOUNT WIND. + Yaw = number, -- in radians, range of -pi to pi, right yaw is positive direction + AoA = number, -- in radians, range of -pi to pi, rotation of aircraft to the right in comparison to flight direction being positive + ClimbAngle = number, -- in radians, range of -pi/2 to pi/2 + + --Maybe later? + AxialVel = table, velocity of the aircraft transformed into directions of aircraft axes + Speed = number -- absolute velocity in meters/sec + + } ]] function mist.getAttitude(unit) - local unitpos = unit:getPosition() - if unitpos then - - local Heading = math.atan2(unitpos.x.z, unitpos.x.x) - - Heading = Heading + mist.getNorthCorrection(unitpos.p) - - if Heading < 0 then - Heading = Heading + 2*math.pi -- put heading in range of 0 to 2*pi - end - ---- heading complete.---- - - local Pitch = math.asin(unitpos.x.y) - ---- pitch complete.---- - - -- now get roll: - --maybe not the best way to do it, but it works. - - --first, make a vector that is perpendicular to y and unitpos.x with cross product - local cp = mist.vec.cp(unitpos.x, {x = 0, y = 1, z = 0}) - - --now, get dot product of of this cross product with unitpos.z - local dp = mist.vec.dp(cp, unitpos.z) - - --now get the magnitude of the roll (magnitude of the angle between two vectors is acos(vec1.vec2/|vec1||vec2|) - local Roll = math.acos(dp/(mist.vec.mag(cp)*mist.vec.mag(unitpos.z))) + local unitpos = unit:getPosition() + if unitpos then - --now, have to get sign of roll. - -- by convention, making right roll positive - -- to get sign of roll, use the y component of unitpos.z. For right roll, y component is negative. - - if unitpos.z.y > 0 then -- left roll, flip the sign of the roll - Roll = -Roll - end - ---- roll complete. ---- - - --now, work on yaw, AoA, climb, and abs velocity - local Yaw - local AoA - local ClimbAngle - - -- get unit velocity - local unitvel = unit:getVelocity() - if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! - local AxialVel = {} --unit velocity transformed into aircraft axes directions - - --transform velocity components in direction of aircraft axes. - AxialVel.x = mist.vec.dp(unitpos.x, unitvel) - AxialVel.y = mist.vec.dp(unitpos.y, unitvel) - AxialVel.z = mist.vec.dp(unitpos.z, unitvel) - - --Yaw is the angle between unitpos.x and the x and z velocities - --define right yaw as positive - Yaw = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = 0, z = AxialVel.z})/mist.vec.mag({x = AxialVel.x, y = 0, z = AxialVel.z})) - - --now set correct direction: - if AxialVel.z > 0 then - Yaw = -Yaw - end - - -- AoA is angle between unitpos.x and the x and y velocities - AoA = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = AxialVel.y, z = 0})/mist.vec.mag({x = AxialVel.x, y = AxialVel.y, z = 0})) - - --now set correct direction: - if AxialVel.y > 0 then - AoA = -AoA - end - - ClimbAngle = math.asin(unitvel.y/mist.vec.mag(unitvel)) - end - return { Heading = Heading, Pitch = Pitch, Roll = Roll, Yaw = Yaw, AoA = AoA, ClimbAngle = ClimbAngle} - else - env.info('unit:getPosition() is nil!') - end + local Heading = math.atan2(unitpos.x.z, unitpos.x.x) + + Heading = Heading + mist.getNorthCorrection(unitpos.p) + + if Heading < 0 then + Heading = Heading + 2*math.pi -- put heading in range of 0 to 2*pi + end + ---- heading complete.---- + + local Pitch = math.asin(unitpos.x.y) + ---- pitch complete.---- + + -- now get roll: + --maybe not the best way to do it, but it works. + + --first, make a vector that is perpendicular to y and unitpos.x with cross product + local cp = mist.vec.cp(unitpos.x, {x = 0, y = 1, z = 0}) + + --now, get dot product of of this cross product with unitpos.z + local dp = mist.vec.dp(cp, unitpos.z) + + --now get the magnitude of the roll (magnitude of the angle between two vectors is acos(vec1.vec2/|vec1||vec2|) + local Roll = math.acos(dp/(mist.vec.mag(cp)*mist.vec.mag(unitpos.z))) + + --now, have to get sign of roll. + -- by convention, making right roll positive + -- to get sign of roll, use the y component of unitpos.z. For right roll, y component is negative. + + if unitpos.z.y > 0 then -- left roll, flip the sign of the roll + Roll = -Roll + end + ---- roll complete. ---- + + --now, work on yaw, AoA, climb, and abs velocity + local Yaw + local AoA + local ClimbAngle + + -- get unit velocity + local unitvel = unit:getVelocity() + if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! + local AxialVel = {} --unit velocity transformed into aircraft axes directions + + --transform velocity components in direction of aircraft axes. + AxialVel.x = mist.vec.dp(unitpos.x, unitvel) + AxialVel.y = mist.vec.dp(unitpos.y, unitvel) + AxialVel.z = mist.vec.dp(unitpos.z, unitvel) + + --Yaw is the angle between unitpos.x and the x and z velocities + --define right yaw as positive + Yaw = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = 0, z = AxialVel.z})/mist.vec.mag({x = AxialVel.x, y = 0, z = AxialVel.z})) + + --now set correct direction: + if AxialVel.z > 0 then + Yaw = -Yaw + end + + -- AoA is angle between unitpos.x and the x and y velocities + AoA = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = AxialVel.y, z = 0})/mist.vec.mag({x = AxialVel.x, y = AxialVel.y, z = 0})) + + --now set correct direction: + if AxialVel.y > 0 then + AoA = -AoA + end + + ClimbAngle = math.asin(unitvel.y/mist.vec.mag(unitvel)) + end + return { Heading = Heading, Pitch = Pitch, Roll = Roll, Yaw = Yaw, AoA = AoA, ClimbAngle = ClimbAngle} + else + env.info('unit:getPosition() is nil!') + end end function mist.getHeading(unit, rawHeading) - local unitpos = unit:getPosition() - if unitpos then - local Heading = math.atan2(unitpos.x.z, unitpos.x.x) - if not rawHeading then - Heading = Heading + mist.getNorthCorrection(unitpos.p) - end - if Heading < 0 then - Heading = Heading + 2*math.pi -- put heading in range of 0 to 2*pi - end - return Heading - end + local unitpos = unit:getPosition() + if unitpos then + local Heading = math.atan2(unitpos.x.z, unitpos.x.x) + if not rawHeading then + Heading = Heading + mist.getNorthCorrection(unitpos.p) + end + if Heading < 0 then + Heading = Heading + 2*math.pi -- put heading in range of 0 to 2*pi + end + return Heading + end end function mist.getPitch(unit) - local unitpos = unit:getPosition() - if unitpos then - return math.asin(unitpos.x.y) - end + local unitpos = unit:getPosition() + if unitpos then + return math.asin(unitpos.x.y) + end end function mist.getRoll(unit) - local unitpos = unit:getPosition() - if unitpos then - -- now get roll: - --maybe not the best way to do it, but it works. - - --first, make a vector that is perpendicular to y and unitpos.x with cross product - local cp = mist.vec.cp(unitpos.x, {x = 0, y = 1, z = 0}) - - --now, get dot product of of this cross product with unitpos.z - local dp = mist.vec.dp(cp, unitpos.z) - - --now get the magnitude of the roll (magnitude of the angle between two vectors is acos(vec1.vec2/|vec1||vec2|) - local Roll = math.acos(dp/(mist.vec.mag(cp)*mist.vec.mag(unitpos.z))) + local unitpos = unit:getPosition() + if unitpos then + -- now get roll: + --maybe not the best way to do it, but it works. - --now, have to get sign of roll. - -- by convention, making right roll positive - -- to get sign of roll, use the y component of unitpos.z. For right roll, y component is negative. - - if unitpos.z.y > 0 then -- left roll, flip the sign of the roll - Roll = -Roll - end - return Roll - end + --first, make a vector that is perpendicular to y and unitpos.x with cross product + local cp = mist.vec.cp(unitpos.x, {x = 0, y = 1, z = 0}) + + --now, get dot product of of this cross product with unitpos.z + local dp = mist.vec.dp(cp, unitpos.z) + + --now get the magnitude of the roll (magnitude of the angle between two vectors is acos(vec1.vec2/|vec1||vec2|) + local Roll = math.acos(dp/(mist.vec.mag(cp)*mist.vec.mag(unitpos.z))) + + --now, have to get sign of roll. + -- by convention, making right roll positive + -- to get sign of roll, use the y component of unitpos.z. For right roll, y component is negative. + + if unitpos.z.y > 0 then -- left roll, flip the sign of the roll + Roll = -Roll + end + return Roll + end end function mist.getYaw(unit) - local unitpos = unit:getPosition() - if unitpos then - -- get unit velocity - local unitvel = unit:getVelocity() - if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! - local AxialVel = {} --unit velocity transformed into aircraft axes directions - - --transform velocity components in direction of aircraft axes. - AxialVel.x = mist.vec.dp(unitpos.x, unitvel) - AxialVel.y = mist.vec.dp(unitpos.y, unitvel) - AxialVel.z = mist.vec.dp(unitpos.z, unitvel) - - --Yaw is the angle between unitpos.x and the x and z velocities - --define right yaw as positive - local Yaw = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = 0, z = AxialVel.z})/mist.vec.mag({x = AxialVel.x, y = 0, z = AxialVel.z})) - - --now set correct direction: - if AxialVel.z > 0 then - Yaw = -Yaw - end - return Yaw - end - end + local unitpos = unit:getPosition() + if unitpos then + -- get unit velocity + local unitvel = unit:getVelocity() + if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! + local AxialVel = {} --unit velocity transformed into aircraft axes directions + + --transform velocity components in direction of aircraft axes. + AxialVel.x = mist.vec.dp(unitpos.x, unitvel) + AxialVel.y = mist.vec.dp(unitpos.y, unitvel) + AxialVel.z = mist.vec.dp(unitpos.z, unitvel) + + --Yaw is the angle between unitpos.x and the x and z velocities + --define right yaw as positive + local Yaw = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = 0, z = AxialVel.z})/mist.vec.mag({x = AxialVel.x, y = 0, z = AxialVel.z})) + + --now set correct direction: + if AxialVel.z > 0 then + Yaw = -Yaw + end + return Yaw + end + end end function mist.getAoA(unit) - local unitpos = unit:getPosition() - if unitpos then - local unitvel = unit:getVelocity() - if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! - local AxialVel = {} --unit velocity transformed into aircraft axes directions - - --transform velocity components in direction of aircraft axes. - AxialVel.x = mist.vec.dp(unitpos.x, unitvel) - AxialVel.y = mist.vec.dp(unitpos.y, unitvel) - AxialVel.z = mist.vec.dp(unitpos.z, unitvel) - - -- AoA is angle between unitpos.x and the x and y velocities - local AoA = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = AxialVel.y, z = 0})/mist.vec.mag({x = AxialVel.x, y = AxialVel.y, z = 0})) - - --now set correct direction: - if AxialVel.y > 0 then - AoA = -AoA - end - return AoA - end - end + local unitpos = unit:getPosition() + if unitpos then + local unitvel = unit:getVelocity() + if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! + local AxialVel = {} --unit velocity transformed into aircraft axes directions + + --transform velocity components in direction of aircraft axes. + AxialVel.x = mist.vec.dp(unitpos.x, unitvel) + AxialVel.y = mist.vec.dp(unitpos.y, unitvel) + AxialVel.z = mist.vec.dp(unitpos.z, unitvel) + + -- AoA is angle between unitpos.x and the x and y velocities + local AoA = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = AxialVel.y, z = 0})/mist.vec.mag({x = AxialVel.x, y = AxialVel.y, z = 0})) + + --now set correct direction: + if AxialVel.y > 0 then + AoA = -AoA + end + return AoA + end + end end function mist.getClimbAngle(unit) - local unitpos = unit:getPosition() - if unitpos then - local unitvel = unit:getVelocity() - if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! - return math.asin(unitvel.y/mist.vec.mag(unitvel)) - end - end + local unitpos = unit:getPosition() + if unitpos then + local unitvel = unit:getVelocity() + if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! + return math.asin(unitvel.y/mist.vec.mag(unitvel)) + end + end end ----------------------------------------------------------------------------------------------------------- -- Database building @@ -1853,23 +1853,23 @@ mist.DBs = {} mist.DBs.missionData = {} ----------------------------------------- -if env.mission then - - mist.DBs.missionData['startTime'] = env.mission.start_time - mist.DBs.missionData['theatre'] = env.mission.theatre - mist.DBs.missionData['version'] = env.mission.version - mist.DBs.missionData['files'] = {} - if type(env.mission.resourceCounter) == 'table' then - for fIndex, fData in pairs (env.mission.resourceCounter) do - mist.DBs.missionData.files[#mist.DBs.missionData.files + 1] = mist.utils.deepCopy(fIndex) - end - end - mist.DBs.missionData['bullseye'] = {['red'] = {}, ['blue'] = {}} -- if we add more coalition specific data then bullsye should be categorized by coaliton. For now its just the bullseye table - mist.DBs.missionData.bullseye.red['x'] = env.mission.coalition.red.bullseye.x --should it be point.x? - mist.DBs.missionData.bullseye.red['y'] = env.mission.coalition.red.bullseye.y - mist.DBs.missionData.bullseye.blue['x'] = env.mission.coalition.blue.bullseye.x - mist.DBs.missionData.bullseye.blue['y'] = env.mission.coalition.blue.bullseye.y - +if env.mission then + + mist.DBs.missionData['startTime'] = env.mission.start_time + mist.DBs.missionData['theatre'] = env.mission.theatre + mist.DBs.missionData['version'] = env.mission.version + mist.DBs.missionData['files'] = {} + if type(env.mission.resourceCounter) == 'table' then + for fIndex, fData in pairs (env.mission.resourceCounter) do + mist.DBs.missionData.files[#mist.DBs.missionData.files + 1] = mist.utils.deepCopy(fIndex) + end + end + mist.DBs.missionData['bullseye'] = {['red'] = {}, ['blue'] = {}} -- if we add more coalition specific data then bullsye should be categorized by coaliton. For now its just the bullseye table + mist.DBs.missionData.bullseye.red['x'] = env.mission.coalition.red.bullseye.x --should it be point.x? + mist.DBs.missionData.bullseye.red['y'] = env.mission.coalition.red.bullseye.y + mist.DBs.missionData.bullseye.blue['x'] = env.mission.coalition.blue.bullseye.x + mist.DBs.missionData.bullseye.blue['y'] = env.mission.coalition.blue.bullseye.y + end ---------------------------------------- @@ -1879,160 +1879,160 @@ mist.DBs.zonesByNum = {} if env.mission.triggers and env.mission.triggers.zones then - for zone_ind, zone_data in pairs(env.mission.triggers.zones) do - if type(zone_data) == 'table' then - local zone = mist.utils.deepCopy(zone_data) - zone['point'] = {} -- point is used by SSE - zone['point']['x'] = zone_data.x - zone['point']['y'] = 0 - zone['point']['z'] = zone_data.y - - mist.DBs.zonesByName[zone_data.name] = zone - mist.DBs.zonesByNum[#mist.DBs.zonesByNum + 1] = mist.utils.deepCopy(zone) --[[deepcopy so that the zone in zones_by_name and the zone in - zones_by_num se are different objects.. don't want them linked.]] - end - end + for zone_ind, zone_data in pairs(env.mission.triggers.zones) do + if type(zone_data) == 'table' then + local zone = mist.utils.deepCopy(zone_data) + zone['point'] = {} -- point is used by SSE + zone['point']['x'] = zone_data.x + zone['point']['y'] = 0 + zone['point']['z'] = zone_data.y + + mist.DBs.zonesByName[zone_data.name] = zone + mist.DBs.zonesByNum[#mist.DBs.zonesByNum + 1] = mist.utils.deepCopy(zone) --[[deepcopy so that the zone in zones_by_name and the zone in + zones_by_num se are different objects.. don't want them linked.]] + end + end end mist.DBs.navPoints = {} mist.DBs.units = {} - --Build mist.db.units and mist.DBs.navPoints +--Build mist.db.units and mist.DBs.navPoints 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 - mist.DBs.units[coa_name] = {} - - ---------------------------------------------- - -- build nav points DB - mist.DBs.navPoints[coa_name] = {} - if coa_data.nav_points then --navpoints - --mist.debug.writeData (mist.utils.serialize,{'NavPoints',coa_data.nav_points}, 'NavPoints.txt') - for nav_ind, nav_data in pairs(coa_data.nav_points) do - - if type(nav_data) == 'table' then - mist.DBs.navPoints[coa_name][nav_ind] = mist.utils.deepCopy(nav_data) + if (coa_name == 'red' or coa_name == 'blue') and type(coa_data) == 'table' then + mist.DBs.units[coa_name] = {} - mist.DBs.navPoints[coa_name][nav_ind]['name'] = nav_data.callsignStr -- name is a little bit more self-explanatory. - mist.DBs.navPoints[coa_name][nav_ind]['point'] = {} -- point is used by SSE, support it. - mist.DBs.navPoints[coa_name][nav_ind]['point']['x'] = nav_data.x - mist.DBs.navPoints[coa_name][nav_ind]['point']['y'] = 0 - mist.DBs.navPoints[coa_name][nav_ind]['point']['z'] = nav_data.y - end - end - end - ------------------------------------------------- - if coa_data.country then --there is a country table - for cntry_id, cntry_data in pairs(coa_data.country) do - - local countryName = string.lower(cntry_data.name) - mist.DBs.units[coa_name][countryName] = {} - mist.DBs.units[coa_name][countryName]["countryId"] = cntry_data.id + ---------------------------------------------- + -- build nav points DB + mist.DBs.navPoints[coa_name] = {} + if coa_data.nav_points then --navpoints + --mist.debug.writeData (mist.utils.serialize,{'NavPoints',coa_data.nav_points}, 'NavPoints.txt') + for nav_ind, nav_data in pairs(coa_data.nav_points) do - if type(cntry_data) == 'table' then --just making sure - - 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" or obj_type_name == "static" then --should be an unncessary check - - local category = obj_type_name - - 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! - - mist.DBs.units[coa_name][countryName][category] = {} - - for group_num, group_data in pairs(obj_type_data.group) do - - if group_data and group_data.units and type(group_data.units) == 'table' then --making sure again- this is a valid group - - mist.DBs.units[coa_name][countryName][category][group_num] = {} - local groupName = group_data.name - if env.mission.version > 7 then - groupName = env.getValueDictByKey(groupName) - end - mist.DBs.units[coa_name][countryName][category][group_num]["groupName"] = groupName - mist.DBs.units[coa_name][countryName][category][group_num]["groupId"] = group_data.groupId - mist.DBs.units[coa_name][countryName][category][group_num]["category"] = category - mist.DBs.units[coa_name][countryName][category][group_num]["coalition"] = coa_name - mist.DBs.units[coa_name][countryName][category][group_num]["country"] = countryName - mist.DBs.units[coa_name][countryName][category][group_num]["countryId"] = cntry_data.id - mist.DBs.units[coa_name][countryName][category][group_num]["startTime"] = group_data.start_time - mist.DBs.units[coa_name][countryName][category][group_num]["task"] = group_data.task - mist.DBs.units[coa_name][countryName][category][group_num]["hidden"] = group_data.hidden - - mist.DBs.units[coa_name][countryName][category][group_num]["units"] = {} - - mist.DBs.units[coa_name][countryName][category][group_num]["radioSet"] = group_data.radioSet - mist.DBs.units[coa_name][countryName][category][group_num]["uncontrolled"] = group_data.uncontrolled - mist.DBs.units[coa_name][countryName][category][group_num]["frequency"] = group_data.frequency - mist.DBs.units[coa_name][countryName][category][group_num]["modulation"] = group_data.modulation + if type(nav_data) == 'table' then + mist.DBs.navPoints[coa_name][nav_ind] = mist.utils.deepCopy(nav_data) - for unit_num, unit_data in pairs(group_data.units) do - local units_tbl = mist.DBs.units[coa_name][countryName][category][group_num]["units"] --pointer to the units table for this group - - units_tbl[unit_num] = {} - if env.mission.version > 7 then - units_tbl[unit_num]["unitName"] = env.getValueDictByKey(unit_data.name) - else - units_tbl[unit_num]["unitName"] = unit_data.name - end - units_tbl[unit_num]["type"] = unit_data.type - units_tbl[unit_num]["skill"] = unit_data.skill --will be nil for statics - units_tbl[unit_num]["unitId"] = unit_data.unitId - units_tbl[unit_num]["category"] = category - units_tbl[unit_num]["coalition"] = coa_name - units_tbl[unit_num]["country"] = countryName - units_tbl[unit_num]["countryId"] = cntry_data.id - units_tbl[unit_num]["heading"] = unit_data.heading - units_tbl[unit_num]["playerCanDrive"] = unit_data.playerCanDrive - units_tbl[unit_num]["alt"] = unit_data.alt - units_tbl[unit_num]["alt_type"] = unit_data.alt_type - units_tbl[unit_num]["speed"] = unit_data.speed - units_tbl[unit_num]["livery_id"] = unit_data.livery_id - if unit_data.point then --ME currently does not work like this, but it might one day - units_tbl[unit_num]["point"] = unit_data.point - else - units_tbl[unit_num]["point"] = {} - units_tbl[unit_num]["point"]["x"] = unit_data.x - units_tbl[unit_num]["point"]["y"] = unit_data.y - end - units_tbl[unit_num]['x'] = unit_data.x - units_tbl[unit_num]['y'] = unit_data.y - - units_tbl[unit_num]["callsign"] = unit_data.callsign - units_tbl[unit_num]["onboard_num"] = unit_data.onboard_num - units_tbl[unit_num]["hardpoint_racks"] = unit_data.hardpoint_racks - units_tbl[unit_num]["psi"] = unit_data.psi + mist.DBs.navPoints[coa_name][nav_ind]['name'] = nav_data.callsignStr -- name is a little bit more self-explanatory. + mist.DBs.navPoints[coa_name][nav_ind]['point'] = {} -- point is used by SSE, support it. + mist.DBs.navPoints[coa_name][nav_ind]['point']['x'] = nav_data.x + mist.DBs.navPoints[coa_name][nav_ind]['point']['y'] = 0 + mist.DBs.navPoints[coa_name][nav_ind]['point']['z'] = nav_data.y + end + end + end + ------------------------------------------------- + if coa_data.country then --there is a country table + for cntry_id, cntry_data in pairs(coa_data.country) do - - units_tbl[unit_num]["groupName"] = groupName - units_tbl[unit_num]["groupId"] = group_data.groupId - - if unit_data.AddPropAircraft then - units_tbl[unit_num]["AddPropAircraft"] = unit_data.AddPropAircraft - end - - if category == 'static' then - units_tbl[unit_num]["categoryStatic"] = unit_data.category - units_tbl[unit_num]["shape_name"] = unit_data.shape_name - if unit_data.mass then - units_tbl[unit_num]["mass"] = unit_data.mass - end - - if unit_data.canCargo then - units_tbl[unit_num]["canCargo"] = unit_data.canCargo - end - end - - end --for unit_num, unit_data in pairs(group_data.units) do - end --if group_data and group_data.units then - 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 --if type(cntry_data) == 'table' then - 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 + local countryName = string.lower(cntry_data.name) + mist.DBs.units[coa_name][countryName] = {} + mist.DBs.units[coa_name][countryName]["countryId"] = cntry_data.id + + if type(cntry_data) == 'table' then --just making sure + + 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" or obj_type_name == "static" then --should be an unncessary check + + local category = obj_type_name + + 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! + + mist.DBs.units[coa_name][countryName][category] = {} + + for group_num, group_data in pairs(obj_type_data.group) do + + if group_data and group_data.units and type(group_data.units) == 'table' then --making sure again- this is a valid group + + mist.DBs.units[coa_name][countryName][category][group_num] = {} + local groupName = group_data.name + if env.mission.version > 7 then + groupName = env.getValueDictByKey(groupName) + end + mist.DBs.units[coa_name][countryName][category][group_num]["groupName"] = groupName + mist.DBs.units[coa_name][countryName][category][group_num]["groupId"] = group_data.groupId + mist.DBs.units[coa_name][countryName][category][group_num]["category"] = category + mist.DBs.units[coa_name][countryName][category][group_num]["coalition"] = coa_name + mist.DBs.units[coa_name][countryName][category][group_num]["country"] = countryName + mist.DBs.units[coa_name][countryName][category][group_num]["countryId"] = cntry_data.id + mist.DBs.units[coa_name][countryName][category][group_num]["startTime"] = group_data.start_time + mist.DBs.units[coa_name][countryName][category][group_num]["task"] = group_data.task + mist.DBs.units[coa_name][countryName][category][group_num]["hidden"] = group_data.hidden + + mist.DBs.units[coa_name][countryName][category][group_num]["units"] = {} + + mist.DBs.units[coa_name][countryName][category][group_num]["radioSet"] = group_data.radioSet + mist.DBs.units[coa_name][countryName][category][group_num]["uncontrolled"] = group_data.uncontrolled + mist.DBs.units[coa_name][countryName][category][group_num]["frequency"] = group_data.frequency + mist.DBs.units[coa_name][countryName][category][group_num]["modulation"] = group_data.modulation + + for unit_num, unit_data in pairs(group_data.units) do + local units_tbl = mist.DBs.units[coa_name][countryName][category][group_num]["units"] --pointer to the units table for this group + + units_tbl[unit_num] = {} + if env.mission.version > 7 then + units_tbl[unit_num]["unitName"] = env.getValueDictByKey(unit_data.name) + else + units_tbl[unit_num]["unitName"] = unit_data.name + end + units_tbl[unit_num]["type"] = unit_data.type + units_tbl[unit_num]["skill"] = unit_data.skill --will be nil for statics + units_tbl[unit_num]["unitId"] = unit_data.unitId + units_tbl[unit_num]["category"] = category + units_tbl[unit_num]["coalition"] = coa_name + units_tbl[unit_num]["country"] = countryName + units_tbl[unit_num]["countryId"] = cntry_data.id + units_tbl[unit_num]["heading"] = unit_data.heading + units_tbl[unit_num]["playerCanDrive"] = unit_data.playerCanDrive + units_tbl[unit_num]["alt"] = unit_data.alt + units_tbl[unit_num]["alt_type"] = unit_data.alt_type + units_tbl[unit_num]["speed"] = unit_data.speed + units_tbl[unit_num]["livery_id"] = unit_data.livery_id + if unit_data.point then --ME currently does not work like this, but it might one day + units_tbl[unit_num]["point"] = unit_data.point + else + units_tbl[unit_num]["point"] = {} + units_tbl[unit_num]["point"]["x"] = unit_data.x + units_tbl[unit_num]["point"]["y"] = unit_data.y + end + units_tbl[unit_num]['x'] = unit_data.x + units_tbl[unit_num]['y'] = unit_data.y + + units_tbl[unit_num]["callsign"] = unit_data.callsign + units_tbl[unit_num]["onboard_num"] = unit_data.onboard_num + units_tbl[unit_num]["hardpoint_racks"] = unit_data.hardpoint_racks + units_tbl[unit_num]["psi"] = unit_data.psi + + + units_tbl[unit_num]["groupName"] = groupName + units_tbl[unit_num]["groupId"] = group_data.groupId + + if unit_data.AddPropAircraft then + units_tbl[unit_num]["AddPropAircraft"] = unit_data.AddPropAircraft + end + + if category == 'static' then + units_tbl[unit_num]["categoryStatic"] = unit_data.category + units_tbl[unit_num]["shape_name"] = unit_data.shape_name + if unit_data.mass then + units_tbl[unit_num]["mass"] = unit_data.mass + end + + if unit_data.canCargo then + units_tbl[unit_num]["canCargo"] = unit_data.canCargo + end + end + + end --for unit_num, unit_data in pairs(group_data.units) do + end --if group_data and group_data.units then + 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 --if type(cntry_data) == 'table' then + 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 mist.DBs.unitsByName = {} @@ -2060,64 +2060,64 @@ mist.DBs.aliveUnits = {} -- will be filled in by the "update_alive_units" corou mist.DBs.removedAliveUnits = {} -- will be filled in by the "update_alive_units" coroutine in mist.main. -- create mist.DBs.oldAliveUnits -- do - -- local intermediate_alive_units = {} -- between 0 and 0.5 secs old - -- local function make_old_alive_units() -- called every 0.5 secs, makes the old_alive_units DB which is just a copy of alive_units that is 0.5 to 1 sec old - -- if intermediate_alive_units then - -- mist.DBs.oldAliveUnits = mist.utils.deepCopy(intermediate_alive_units) - -- end - -- intermediate_alive_units = mist.utils.deepCopy(mist.DBs.aliveUnits) - -- timer.scheduleFunction(make_old_alive_units, nil, timer.getTime() + 0.5) - -- end - - -- make_old_alive_units() +-- local intermediate_alive_units = {} -- between 0 and 0.5 secs old +-- local function make_old_alive_units() -- called every 0.5 secs, makes the old_alive_units DB which is just a copy of alive_units that is 0.5 to 1 sec old +-- if intermediate_alive_units then +-- mist.DBs.oldAliveUnits = mist.utils.deepCopy(intermediate_alive_units) +-- end +-- intermediate_alive_units = mist.utils.deepCopy(mist.DBs.aliveUnits) +-- timer.scheduleFunction(make_old_alive_units, nil, timer.getTime() + 0.5) +-- end + +-- make_old_alive_units() -- end --Build DBs for coa_name, coa_data in pairs(mist.DBs.units) do - for cntry_name, cntry_data in pairs(coa_data) do - for category_name, category_data in pairs(cntry_data) do - if type(category_data) == 'table' then - for group_ind, group_data in pairs(category_data) do - if type(group_data) == 'table' and group_data.units and type(group_data.units) == 'table' and #group_data.units > 0 then -- OCD paradigm programming - mist.DBs.groupsByName[group_data.groupName] = mist.utils.deepCopy(group_data) - mist.DBs.groupsById[group_data.groupId] = mist.utils.deepCopy(group_data) - for unit_ind, unit_data in pairs(group_data.units) do - mist.DBs.unitsByName[unit_data.unitName] = mist.utils.deepCopy(unit_data) - mist.DBs.unitsById[unit_data.unitId] = mist.utils.deepCopy(unit_data) - - mist.DBs.unitsByCat[unit_data.category] = mist.DBs.unitsByCat[unit_data.category] or {} -- future-proofing against new categories... - table.insert(mist.DBs.unitsByCat[unit_data.category], mist.utils.deepCopy(unit_data)) - --print('inserting ' .. unit_data.unitName) - table.insert(mist.DBs.unitsByNum, mist.utils.deepCopy(unit_data)) - - if unit_data.skill and (unit_data.skill == "Client" or unit_data.skill == "Player") then - mist.DBs.humansByName[unit_data.unitName] = mist.utils.deepCopy(unit_data) - mist.DBs.humansById[unit_data.unitId] = mist.utils.deepCopy(unit_data) - --if Unit.getByName(unit_data.unitName) then - -- mist.DBs.activeHumans[unit_data.unitName] = mist.utils.deepCopy(unit_data) - -- mist.DBs.activeHumans[unit_data.unitName].playerName = Unit.getByName(unit_data.unitName):getPlayerName() - --end - end - end - end - end - end - end - end + for cntry_name, cntry_data in pairs(coa_data) do + for category_name, category_data in pairs(cntry_data) do + if type(category_data) == 'table' then + for group_ind, group_data in pairs(category_data) do + if type(group_data) == 'table' and group_data.units and type(group_data.units) == 'table' and #group_data.units > 0 then -- OCD paradigm programming + mist.DBs.groupsByName[group_data.groupName] = mist.utils.deepCopy(group_data) + mist.DBs.groupsById[group_data.groupId] = mist.utils.deepCopy(group_data) + for unit_ind, unit_data in pairs(group_data.units) do + mist.DBs.unitsByName[unit_data.unitName] = mist.utils.deepCopy(unit_data) + mist.DBs.unitsById[unit_data.unitId] = mist.utils.deepCopy(unit_data) + + mist.DBs.unitsByCat[unit_data.category] = mist.DBs.unitsByCat[unit_data.category] or {} -- future-proofing against new categories... + table.insert(mist.DBs.unitsByCat[unit_data.category], mist.utils.deepCopy(unit_data)) + --print('inserting ' .. unit_data.unitName) + table.insert(mist.DBs.unitsByNum, mist.utils.deepCopy(unit_data)) + + if unit_data.skill and (unit_data.skill == "Client" or unit_data.skill == "Player") then + mist.DBs.humansByName[unit_data.unitName] = mist.utils.deepCopy(unit_data) + mist.DBs.humansById[unit_data.unitId] = mist.utils.deepCopy(unit_data) + --if Unit.getByName(unit_data.unitName) then + -- mist.DBs.activeHumans[unit_data.unitName] = mist.utils.deepCopy(unit_data) + -- mist.DBs.activeHumans[unit_data.unitName].playerName = Unit.getByName(unit_data.unitName):getPlayerName() + --end + end + end + end + end + end + end + end end -------------- --------- mist unitID funcs +-------- mist unitID funcs do - for id, idData in pairs(mist.DBs.unitsById) do - if idData.unitId > mist.nextUnitId then - mist.nextUnitId = mist.utils.deepCopy(idData.unitId) - end - if idData.groupId > mist.nextGroupId then - mist.nextGroupId = mist.utils.deepCopy(idData.groupId) - end - end + for id, idData in pairs(mist.DBs.unitsById) do + if idData.unitId > mist.nextUnitId then + mist.nextUnitId = mist.utils.deepCopy(idData.unitId) + end + if idData.groupId > mist.nextGroupId then + mist.nextGroupId = mist.utils.deepCopy(idData.groupId) + end + end end --DynDBs @@ -2134,159 +2134,159 @@ mist.DBs.MEgroupsById = mist.utils.deepCopy(mist.DBs.groupsById) mist.DBs.deadObjects = {} do - local mt = {} - - mt.__newindex = function(t, key, val) - --------------------------------------------------------------- - local original_key = key --only for duplicate runtime IDs. - local key_ind = 1 - while mist.DBs.deadObjects[key] do - --print('duplicate runtime id of previously dead object- key: ' .. tostring(key)) - key = tostring(original_key) .. ' #' .. tostring(key_ind) - key_ind = key_ind + 1 - end - --------------------------------------------------------------- - - if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then - --print('object found in alive_units') - val['objectData'] = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) - local pos = Object.getPosition(val.object) - if pos then - val['objectPos'] = pos.p - end - val['objectType'] = mist.DBs.aliveUnits[val.object.id_].category - - elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units - --print('object found in old_alive_units') - val['objectData'] = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) - local pos = Object.getPosition(val.object) - if pos then - val['objectPos'] = pos.p - end - val['objectType'] = mist.DBs.removedAliveUnits[val.object.id_].category - - else --attempt to determine if static object... - --print('object not found in alive units or old alive units') - local pos = Object.getPosition(val.object) - if pos then - local static_found = false - for ind, static in pairs(mist.DBs.unitsByCat['static']) do - if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... - --print('correlated dead static object to position') - val['objectData'] = static - val['objectPos'] = pos.p - val['objectType'] = 'static' - static_found = true - break - end - end - if not static_found then - val['objectPos'] = pos.p - val['objectType'] = 'building' - end - else - val['objectType'] = 'unknown' - end - end - rawset(t, key, val) - end + local mt = {} - setmetatable(mist.DBs.deadObjects, mt) + mt.__newindex = function(t, key, val) + --------------------------------------------------------------- + local original_key = key --only for duplicate runtime IDs. + local key_ind = 1 + while mist.DBs.deadObjects[key] do + --print('duplicate runtime id of previously dead object- key: ' .. tostring(key)) + key = tostring(original_key) .. ' #' .. tostring(key_ind) + key_ind = key_ind + 1 + end + --------------------------------------------------------------- + + if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then + --print('object found in alive_units') + val['objectData'] = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) + local pos = Object.getPosition(val.object) + if pos then + val['objectPos'] = pos.p + end + val['objectType'] = mist.DBs.aliveUnits[val.object.id_].category + + elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units + --print('object found in old_alive_units') + val['objectData'] = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) + local pos = Object.getPosition(val.object) + if pos then + val['objectPos'] = pos.p + end + val['objectType'] = mist.DBs.removedAliveUnits[val.object.id_].category + + else --attempt to determine if static object... + --print('object not found in alive units or old alive units') + local pos = Object.getPosition(val.object) + if pos then + local static_found = false + for ind, static in pairs(mist.DBs.unitsByCat['static']) do + if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... + --print('correlated dead static object to position') + val['objectData'] = static + val['objectPos'] = pos.p + val['objectType'] = 'static' + static_found = true + break + end + end + if not static_found then + val['objectPos'] = pos.p + val['objectType'] = 'building' + end + else + val['objectType'] = 'unknown' + end + end + rawset(t, key, val) + end + + setmetatable(mist.DBs.deadObjects, mt) end -- Event handler to start creating the dead_objects table -do +do - local function addDeadObject(event) - if event.id == world.event.S_EVENT_DEAD or event.id == world.event.S_EVENT_CRASH then - if event.initiator and event.initiator.id_ and event.initiator.id_ > 0 then - - local id = event.initiator.id_ -- initial ID, could change if there is a duplicate id_ already dead. - local val = {object = event.initiator} -- the new entry in mist.DBs.deadObjects. - - --------------------------------------------------------------- - local original_id = id --only for duplicate runtime IDs. - local id_ind = 1 - while mist.DBs.deadObjects[id] do - --print('duplicate runtime id of previously dead object- id: ' .. tostring(id)) - id = tostring(original_id) .. ' #' .. tostring(id_ind) - id_ind = id_ind + 1 - end - --------------------------------------------------------------- - - if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then - --print('object found in alive_units') - val['objectData'] = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) - local pos = Object.getPosition(val.object) - if pos then - val['objectPos'] = pos.p - end - val['objectType'] = mist.DBs.aliveUnits[val.object.id_].category - --[[if mist.DBs.activeHumans[Unit.getName(val.object)] then - --trigger.action.outText('remove via death: ' .. Unit.getName(val.object),20) - mist.DBs.activeHumans[Unit.getName(val.object)] = nil - end]] - elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units - --print('object found in old_alive_units') - val['objectData'] = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) - local pos = Object.getPosition(val.object) - if pos then - val['objectPos'] = pos.p - end - val['objectType'] = mist.DBs.removedAliveUnits[val.object.id_].category - - else --attempt to determine if static object... - --print('object not found in alive units or old alive units') - local pos = Object.getPosition(val.object) - if pos then - local static_found = false - for ind, static in pairs(mist.DBs.unitsByCat['static']) do - if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... - --print('correlated dead static object to position') - val['objectData'] = static - val['objectPos'] = pos.p - val['objectType'] = 'static' - static_found = true - break - end - end - if not static_found then - val['objectPos'] = pos.p - val['objectType'] = 'building' - end - else - val['objectType'] = 'unknown' - end - end - mist.DBs.deadObjects[id] = val + local function addDeadObject(event) + if event.id == world.event.S_EVENT_DEAD or event.id == world.event.S_EVENT_CRASH then + if event.initiator and event.initiator.id_ and event.initiator.id_ > 0 then - end - end - end + local id = event.initiator.id_ -- initial ID, could change if there is a duplicate id_ already dead. + local val = {object = event.initiator} -- the new entry in mist.DBs.deadObjects. - - mist.addEventHandler(addDeadObject) - - --[[ - local function addClientsToActive(event) - if event.id == world.event.S_EVENT_PLAYER_ENTER_UNIT or event.id == world.event.S_EVENT_BIRTH then - env.info(mist.utils.tableShow(event)) - if Unit.getPlayerName(event.initiator) then - env.info(Unit.getPlayerName(event.initiator)) - local newU = mist.utils.deepCopy(mist.DBs.unitsByName[Unit.getName(event.initiator)]) - newU.playerName = Unit.getPlayerName(event.initiator) - mist.DBs.activeHumans[Unit.getName(event.initiator)] = newU - --trigger.action.outText('added: ' .. Unit.getName(event.initiator), 20) - end - elseif event.id == world.event.S_EVENT_PLAYER_LEAVE_UNIT and event.initiator then - if mist.DBs.activeHumans[Unit.getName(event.initiator)] then - mist.DBs.activeHumans[Unit.getName(event.initiator)] = nil - -- trigger.action.outText('removed via control: ' .. Unit.getName(event.initiator), 20) - end - end - end - - mist.addEventHandler(addClientsToActive)]] + --------------------------------------------------------------- + local original_id = id --only for duplicate runtime IDs. + local id_ind = 1 + while mist.DBs.deadObjects[id] do + --print('duplicate runtime id of previously dead object- id: ' .. tostring(id)) + id = tostring(original_id) .. ' #' .. tostring(id_ind) + id_ind = id_ind + 1 + end + --------------------------------------------------------------- + + if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then + --print('object found in alive_units') + val['objectData'] = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) + local pos = Object.getPosition(val.object) + if pos then + val['objectPos'] = pos.p + end + val['objectType'] = mist.DBs.aliveUnits[val.object.id_].category + --[[if mist.DBs.activeHumans[Unit.getName(val.object)] then + --trigger.action.outText('remove via death: ' .. Unit.getName(val.object),20) + mist.DBs.activeHumans[Unit.getName(val.object)] = nil + end]] + elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units + --print('object found in old_alive_units') + val['objectData'] = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) + local pos = Object.getPosition(val.object) + if pos then + val['objectPos'] = pos.p + end + val['objectType'] = mist.DBs.removedAliveUnits[val.object.id_].category + + else --attempt to determine if static object... + --print('object not found in alive units or old alive units') + local pos = Object.getPosition(val.object) + if pos then + local static_found = false + for ind, static in pairs(mist.DBs.unitsByCat['static']) do + if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... + --print('correlated dead static object to position') + val['objectData'] = static + val['objectPos'] = pos.p + val['objectType'] = 'static' + static_found = true + break + end + end + if not static_found then + val['objectPos'] = pos.p + val['objectType'] = 'building' + end + else + val['objectType'] = 'unknown' + end + end + mist.DBs.deadObjects[id] = val + + end + end + end + + + mist.addEventHandler(addDeadObject) + + --[[ + local function addClientsToActive(event) + if event.id == world.event.S_EVENT_PLAYER_ENTER_UNIT or event.id == world.event.S_EVENT_BIRTH then + env.info(mist.utils.tableShow(event)) + if Unit.getPlayerName(event.initiator) then + env.info(Unit.getPlayerName(event.initiator)) + local newU = mist.utils.deepCopy(mist.DBs.unitsByName[Unit.getName(event.initiator)]) + newU.playerName = Unit.getPlayerName(event.initiator) + mist.DBs.activeHumans[Unit.getName(event.initiator)] = newU + --trigger.action.outText('added: ' .. Unit.getName(event.initiator), 20) + end + elseif event.id == world.event.S_EVENT_PLAYER_LEAVE_UNIT and event.initiator then + if mist.DBs.activeHumans[Unit.getName(event.initiator)] then + mist.DBs.activeHumans[Unit.getName(event.initiator)] = nil + -- trigger.action.outText('removed via control: ' .. Unit.getName(event.initiator), 20) + end + end + end + + mist.addEventHandler(addClientsToActive)]] end @@ -2295,7 +2295,7 @@ end function mist.makeUnitTable(tbl) ---[[ + --[[ Prefixes: "[-u]" - subtract this unit if its in the table "[g]" - add this group to the table @@ -2350,7 +2350,7 @@ Compound Identifiers: Country names to be used in [c] and [-c] short-cuts: -"Turkey" +"Turkey" "Norway" "The Netherlands" "Spain" @@ -2370,347 +2370,347 @@ Country names to be used in [c] and [-c] short-cuts: "Italy" ]] - --Assumption: will be passed a table of strings, sequential - local units_by_name = {} + --Assumption: will be passed a table of strings, sequential + local units_by_name = {} - local l_munits = mist.DBs.units --local reference for faster execution - for i = 1, #tbl do - local unit = tbl[i] - if unit:sub(1,4) == '[-u]' then --subtract a unit - if units_by_name[unit:sub(5)] then -- 5 to end - units_by_name[unit:sub(5)] = nil --remove - end - elseif unit:sub(1,3) == '[g]' then -- add a group - for coa, coa_tbl in pairs(l_munits) do - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' and group_tbl.groupName == unit:sub(4) then -- index 4 to end - for unit_ind, unit in pairs(group_tbl.units) do - units_by_name[unit.unitName] = true --add - end - end - end - end - end - end - end - elseif unit:sub(1,4) == '[-g]' then -- subtract a group - for coa, coa_tbl in pairs(l_munits) do - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' and group_tbl.groupName == unit:sub(5) then -- index 5 to end - for unit_ind, unit in pairs(group_tbl.units) do - if units_by_name[unit.unitName] then - units_by_name[unit.unitName] = nil --remove - end - end - end - end - end - end - end - end - elseif unit:sub(1,3) == '[c]' then -- add a country - local category = '' - local country_start = 4 - if unit:sub(4,15) == '[helicopter]' then - category = 'helicopter' - country_start = 16 - elseif unit:sub(4,10) == '[plane]' then - category = 'plane' - country_start = 11 - elseif unit:sub(4,9) == '[ship]' then - category = 'ship' - country_start = 10 - elseif unit:sub(4,12) == '[vehicle]' then - category = 'vehicle' - country_start = 13 - end - for coa, coa_tbl in pairs(l_munits) do - for country, country_table in pairs(coa_tbl) do - if country == string.lower(unit:sub(country_start)) then -- match - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - units_by_name[unit.unitName] = true --add - end - end - end - end - end - end - end - end - elseif unit:sub(1,4) == '[-c]' then -- subtract a country - local category = '' - local country_start = 5 - if unit:sub(5,16) == '[helicopter]' then - category = 'helicopter' - country_start = 17 - elseif unit:sub(5,11) == '[plane]' then - category = 'plane' - country_start = 12 - elseif unit:sub(5,10) == '[ship]' then - category = 'ship' - country_start = 11 - elseif unit:sub(5,13) == '[vehicle]' then - category = 'vehicle' - country_start = 14 - end - for coa, coa_tbl in pairs(l_munits) do - for country, country_table in pairs(coa_tbl) do - if country == string.lower(unit:sub(country_start)) then -- match - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - if units_by_name[unit.unitName] then - units_by_name[unit.unitName] = nil --remove - end - end - end - end - end - end - end - end - end - elseif unit:sub(1,6) == '[blue]' then -- add blue coalition - local category = '' - if unit:sub(7) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(7) == '[plane]' then - category = 'plane' - elseif unit:sub(7) == '[ship]' then - category = 'ship' - elseif unit:sub(7) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - if coa == 'blue' then - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - units_by_name[unit.unitName] = true --add - end - end - end - end - end - end - end - end - elseif unit:sub(1,7) == '[-blue]' then -- subtract blue coalition - local category = '' - if unit:sub(8) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(8) == '[plane]' then - category = 'plane' - elseif unit:sub(8) == '[ship]' then - category = 'ship' - elseif unit:sub(8) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - if coa == 'blue' then - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - if units_by_name[unit.unitName] then - units_by_name[unit.unitName] = nil --remove - end - end - end - end - end - end - end - end - end - elseif unit:sub(1,5) == '[red]' then -- add red coalition - local category = '' - if unit:sub(6) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(6) == '[plane]' then - category = 'plane' - elseif unit:sub(6) == '[ship]' then - category = 'ship' - elseif unit:sub(6) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - if coa == 'red' then - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - units_by_name[unit.unitName] = true --add - end - end - end - end - end - end - end - end - elseif unit:sub(1,6) == '[-red]' then -- subtract red coalition - local category = '' - if unit:sub(7) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(7) == '[plane]' then - category = 'plane' - elseif unit:sub(7) == '[ship]' then - category = 'ship' - elseif unit:sub(7) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - if coa == 'red' then - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - if units_by_name[unit.unitName] then - units_by_name[unit.unitName] = nil --remove - end - end - end - end - end - end - end - end - end - elseif unit:sub(1,5) == '[all]' then -- add all of a certain category (or all categories) - local category = '' - if unit:sub(6) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(6) == '[plane]' then - category = 'plane' - elseif unit:sub(6) == '[ship]' then - category = 'ship' - elseif unit:sub(6) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - units_by_name[unit.unitName] = true --add - end - end - end - end - end - end - end - elseif unit:sub(1,6) == '[-all]' then -- subtract all of a certain category (or all categories) - local category = '' - if unit:sub(7) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(7) == '[plane]' then - category = 'plane' - elseif unit:sub(7) == '[ship]' then - category = 'ship' - elseif unit:sub(7) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - if units_by_name[unit.unitName] then - units_by_name[unit.unitName] = nil --remove - end - end - end - end - end - end - end - end - else -- just a regular unit - units_by_name[unit] = true --add - end - end - - local units_tbl = {} -- indexed sequentially - for unit_name, val in pairs(units_by_name) do - if val then - units_tbl[#units_tbl + 1] = unit_name -- add all the units to the table - end - end - - - units_tbl['processed'] = timer.getTime() --add the processed flag - return units_tbl + local l_munits = mist.DBs.units --local reference for faster execution + for i = 1, #tbl do + local unit = tbl[i] + if unit:sub(1,4) == '[-u]' then --subtract a unit + if units_by_name[unit:sub(5)] then -- 5 to end + units_by_name[unit:sub(5)] = nil --remove + end + elseif unit:sub(1,3) == '[g]' then -- add a group + for coa, coa_tbl in pairs(l_munits) do + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' and group_tbl.groupName == unit:sub(4) then -- index 4 to end + for unit_ind, unit in pairs(group_tbl.units) do + units_by_name[unit.unitName] = true --add + end + end + end + end + end + end + end +elseif unit:sub(1,4) == '[-g]' then -- subtract a group + for coa, coa_tbl in pairs(l_munits) do + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' and group_tbl.groupName == unit:sub(5) then -- index 5 to end + for unit_ind, unit in pairs(group_tbl.units) do + if units_by_name[unit.unitName] then + units_by_name[unit.unitName] = nil --remove + end + end + end + end + end + end + end +end + elseif unit:sub(1,3) == '[c]' then -- add a country + local category = '' + local country_start = 4 + if unit:sub(4,15) == '[helicopter]' then + category = 'helicopter' + country_start = 16 + elseif unit:sub(4,10) == '[plane]' then + category = 'plane' + country_start = 11 + elseif unit:sub(4,9) == '[ship]' then + category = 'ship' + country_start = 10 + elseif unit:sub(4,12) == '[vehicle]' then + category = 'vehicle' + country_start = 13 + end + for coa, coa_tbl in pairs(l_munits) do + for country, country_table in pairs(coa_tbl) do + if country == string.lower(unit:sub(country_start)) then -- match + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + units_by_name[unit.unitName] = true --add + end + end + end + end + end + end + end + end + elseif unit:sub(1,4) == '[-c]' then -- subtract a country + local category = '' + local country_start = 5 + if unit:sub(5,16) == '[helicopter]' then + category = 'helicopter' + country_start = 17 + elseif unit:sub(5,11) == '[plane]' then + category = 'plane' + country_start = 12 + elseif unit:sub(5,10) == '[ship]' then + category = 'ship' + country_start = 11 + elseif unit:sub(5,13) == '[vehicle]' then + category = 'vehicle' + country_start = 14 + end + for coa, coa_tbl in pairs(l_munits) do + for country, country_table in pairs(coa_tbl) do + if country == string.lower(unit:sub(country_start)) then -- match + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + if units_by_name[unit.unitName] then + units_by_name[unit.unitName] = nil --remove + end + end + end + end + end + end + end + end + end + elseif unit:sub(1,6) == '[blue]' then -- add blue coalition + local category = '' + if unit:sub(7) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(7) == '[plane]' then + category = 'plane' + elseif unit:sub(7) == '[ship]' then + category = 'ship' + elseif unit:sub(7) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + if coa == 'blue' then + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + units_by_name[unit.unitName] = true --add + end + end + end + end + end + end + end + end + elseif unit:sub(1,7) == '[-blue]' then -- subtract blue coalition + local category = '' + if unit:sub(8) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(8) == '[plane]' then + category = 'plane' + elseif unit:sub(8) == '[ship]' then + category = 'ship' + elseif unit:sub(8) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + if coa == 'blue' then + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + if units_by_name[unit.unitName] then + units_by_name[unit.unitName] = nil --remove + end + end + end + end + end + end + end + end + end + elseif unit:sub(1,5) == '[red]' then -- add red coalition + local category = '' + if unit:sub(6) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(6) == '[plane]' then + category = 'plane' + elseif unit:sub(6) == '[ship]' then + category = 'ship' + elseif unit:sub(6) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + if coa == 'red' then + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + units_by_name[unit.unitName] = true --add + end + end + end + end + end + end + end + end + elseif unit:sub(1,6) == '[-red]' then -- subtract red coalition + local category = '' + if unit:sub(7) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(7) == '[plane]' then + category = 'plane' + elseif unit:sub(7) == '[ship]' then + category = 'ship' + elseif unit:sub(7) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + if coa == 'red' then + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + if units_by_name[unit.unitName] then + units_by_name[unit.unitName] = nil --remove + end + end + end + end + end + end + end + end + end + elseif unit:sub(1,5) == '[all]' then -- add all of a certain category (or all categories) + local category = '' + if unit:sub(6) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(6) == '[plane]' then + category = 'plane' + elseif unit:sub(6) == '[ship]' then + category = 'ship' + elseif unit:sub(6) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + units_by_name[unit.unitName] = true --add + end + end + end + end + end + end + end + elseif unit:sub(1,6) == '[-all]' then -- subtract all of a certain category (or all categories) + local category = '' + if unit:sub(7) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(7) == '[plane]' then + category = 'plane' + elseif unit:sub(7) == '[ship]' then + category = 'ship' + elseif unit:sub(7) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + if units_by_name[unit.unitName] then + units_by_name[unit.unitName] = nil --remove + end + end + end + end + end + end + end + end + else -- just a regular unit + units_by_name[unit] = true --add + end + end + + local units_tbl = {} -- indexed sequentially + for unit_name, val in pairs(units_by_name) do + if val then + units_tbl[#units_tbl + 1] = unit_name -- add all the units to the table + end + end + + + units_tbl['processed'] = timer.getTime() --add the processed flag + return units_tbl end -mist.getDeadMapObjsInZones = function(zone_names) --- zone_names: table of zone names --- returns: table of dead map objects (indexed numerically) - local map_objs = {} - local zones = {} - for i = 1, #zone_names do - if mist.DBs.zonesByName[zone_names[i]] then - zones[#zones + 1] = mist.DBs.zonesByName[zone_names[i]] - end - end - for obj_id, obj in pairs(mist.DBs.deadObjects) do - if obj.objectType and obj.objectType == 'building' then --dead map object - for i = 1, #zones do - if ((zones[i].point.x - obj.objectPos.x)^2 + (zones[i].point.z - obj.objectPos.z)^2)^0.5 <= zones[i].radius then - map_objs[#map_objs + 1] = mist.utils.deepCopy(obj) - end - end - end - end - return map_objs +mist.getDeadMapObjsInZones = function(zone_names) + -- zone_names: table of zone names + -- returns: table of dead map objects (indexed numerically) + local map_objs = {} + local zones = {} + for i = 1, #zone_names do + if mist.DBs.zonesByName[zone_names[i]] then + zones[#zones + 1] = mist.DBs.zonesByName[zone_names[i]] + end + end + for obj_id, obj in pairs(mist.DBs.deadObjects) do + if obj.objectType and obj.objectType == 'building' then --dead map object + for i = 1, #zones do + if ((zones[i].point.x - obj.objectPos.x)^2 + (zones[i].point.z - obj.objectPos.z)^2)^0.5 <= zones[i].radius then + map_objs[#map_objs + 1] = mist.utils.deepCopy(obj) + end + end + end + end + return map_objs end -mist.getDeadMapObjsInPolygonZone = function(zone) --- zone_names: table of zone names --- returns: table of dead map objects (indexed numerically) - local map_objs = {} - for obj_id, obj in pairs(mist.DBs.deadObjects) do - if obj.objectType and obj.objectType == 'building' then --dead map object - if mist.pointInPolygon(obj.objectPos, zone) then - map_objs[#map_objs + 1] = mist.utils.deepCopy(obj) - end - end - end - return map_objs +mist.getDeadMapObjsInPolygonZone = function(zone) + -- zone_names: table of zone names + -- returns: table of dead map objects (indexed numerically) + local map_objs = {} + for obj_id, obj in pairs(mist.DBs.deadObjects) do + if obj.objectType and obj.objectType == 'building' then --dead map object + if mist.pointInPolygon(obj.objectPos, zone) then + map_objs[#map_objs + 1] = mist.utils.deepCopy(obj) + end + end + end + return map_objs end mist.flagFunc = {} mist.flagFunc.mapobjs_dead_zones = function(vars) ---[[vars needs to be: + --[[vars needs to be: zones = table or string, flag = number, stopflag = number or nil, @@ -2720,44 +2720,44 @@ AND used by function, initial_number ]] --- type_tbl - local type_tbl = { - [{'zones', 'zone'}] = {'table', 'string'}, - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - [{'req_num', 'reqnum'}] = {'number', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.mapobjs_dead_zones', type_tbl, vars) - assert(err, errmsg) - local zones = vars.zones or vars.zone - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local req_num = vars.req_num or vars.reqnum or 1 - local initial_number = vars.initial_number - - if type(zones) == 'string' then - zones = {zones} - end - - if not initial_number then - initial_number = #mist.getDeadMapObjsInZones(zones) - end - - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - if (#mist.getDeadMapObjsInZones(zones) - initial_number) >= req_num and trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - return - else - mist.scheduleFunction(mist.flagFunc.mapobjs_dead_zones, {{zones = zones, flag = flag, stopflag = stopflag, req_num = req_num, initial_number = initial_number}}, timer.getTime() + 1) - end - end + -- type_tbl + local type_tbl = { + [{'zones', 'zone'}] = {'table', 'string'}, + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + [{'req_num', 'reqnum'}] = {'number', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.mapobjs_dead_zones', type_tbl, vars) + assert(err, errmsg) + local zones = vars.zones or vars.zone + local flag = vars.flag + local stopflag = vars.stopflag or -1 + local req_num = vars.req_num or vars.reqnum or 1 + local initial_number = vars.initial_number + + if type(zones) == 'string' then + zones = {zones} + end + + if not initial_number then + initial_number = #mist.getDeadMapObjsInZones(zones) + end + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + if (#mist.getDeadMapObjsInZones(zones) - initial_number) >= req_num and trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + return + else + mist.scheduleFunction(mist.flagFunc.mapobjs_dead_zones, {{zones = zones, flag = flag, stopflag = stopflag, req_num = req_num, initial_number = initial_number}}, timer.getTime() + 1) + end + end end mist.flagFunc.mapobjs_dead_polygon = function(vars) ---[[vars needs to be: + --[[vars needs to be: zone = table, flag = number, stopflag = number or nil, @@ -2767,95 +2767,95 @@ AND used by function, initial_number ]] --- type_tbl - local type_tbl = { - [{'zone', 'polyzone'}] = 'table', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - [{'req_num', 'reqnum'}] = {'number', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.mapobjs_dead_polygon', type_tbl, vars) - assert(err, errmsg) - local zone = vars.zone or vars.polyzone - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local req_num = vars.req_num or vars.reqnum or 1 - local initial_number = vars.initial_number - - if not initial_number then - initial_number = #mist.getDeadMapObjsInPolygonZone(zone) - end - - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - if (#mist.getDeadMapObjsInPolygonZone(zone) - initial_number) >= req_num and trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - return - else - mist.scheduleFunction(mist.flagFunc.mapobjs_dead_polygon, {{zone = zone, flag = flag, stopflag = stopflag, req_num = req_num, initial_number = initial_number}}, timer.getTime() + 1) - end - end + -- type_tbl + local type_tbl = { + [{'zone', 'polyzone'}] = 'table', + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + [{'req_num', 'reqnum'}] = {'number', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.mapobjs_dead_polygon', type_tbl, vars) + assert(err, errmsg) + local zone = vars.zone or vars.polyzone + local flag = vars.flag + local stopflag = vars.stopflag or -1 + local req_num = vars.req_num or vars.reqnum or 1 + local initial_number = vars.initial_number + + if not initial_number then + initial_number = #mist.getDeadMapObjsInPolygonZone(zone) + end + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + if (#mist.getDeadMapObjsInPolygonZone(zone) - initial_number) >= req_num and trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + return + else + mist.scheduleFunction(mist.flagFunc.mapobjs_dead_polygon, {{zone = zone, flag = flag, stopflag = stopflag, req_num = req_num, initial_number = initial_number}}, timer.getTime() + 1) + end + end end function mist.pointInPolygon(point, poly, maxalt) --raycasting point in polygon. Code from http://softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm - --[[local type_tbl = { - point = {'table'}, - poly = {'table'}, - maxalt = {'number', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.pointInPolygon', type_tbl, {point, poly, maxalt}) - assert(err, errmsg) - ]] - point = mist.utils.makeVec3(point) - local px = point.x - local pz = point.z - local cn = 0 - local newpoly = mist.utils.deepCopy(poly) - - if not maxalt or (point.y <= maxalt) then - local polysize = #newpoly - newpoly[#newpoly + 1] = newpoly[1] - - newpoly[1] = mist.utils.makeVec3(newpoly[1]) - - for k = 1, polysize do - newpoly[k+1] = mist.utils.makeVec3(newpoly[k+1]) - if ((newpoly[k].z <= pz) and (newpoly[k+1].z > pz)) or ((newpoly[k].z > pz) and (newpoly[k+1].z <= pz)) then - local vt = (pz - newpoly[k].z) / (newpoly[k+1].z - newpoly[k].z) - if (px < newpoly[k].x + vt*(newpoly[k+1].x - newpoly[k].x)) then - cn = cn + 1 - end - end - end - - return cn%2 == 1 - else - return false - end + --[[local type_tbl = { + point = {'table'}, + poly = {'table'}, + maxalt = {'number', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.pointInPolygon', type_tbl, {point, poly, maxalt}) + assert(err, errmsg) + ]] + point = mist.utils.makeVec3(point) + local px = point.x + local pz = point.z + local cn = 0 + local newpoly = mist.utils.deepCopy(poly) + + if not maxalt or (point.y <= maxalt) then + local polysize = #newpoly + newpoly[#newpoly + 1] = newpoly[1] + + newpoly[1] = mist.utils.makeVec3(newpoly[1]) + + for k = 1, polysize do + newpoly[k+1] = mist.utils.makeVec3(newpoly[k+1]) + if ((newpoly[k].z <= pz) and (newpoly[k+1].z > pz)) or ((newpoly[k].z > pz) and (newpoly[k+1].z <= pz)) then + local vt = (pz - newpoly[k].z) / (newpoly[k+1].z - newpoly[k].z) + if (px < newpoly[k].x + vt*(newpoly[k+1].x - newpoly[k].x)) then + cn = cn + 1 + end + end + end + + return cn%2 == 1 + else + return false + end end mist.getUnitsInPolygon = function (unit_names, polyZone, max_alt) - local units = {} - - for i = 1, #unit_names do - units[#units + 1] = Unit.getByName(unitNames[i]) - end - - local inZoneUnits = {} - for i =1, #units do - if units[i]:isActive() and mist.pointInPolygon(units[i]:getPosition().p, polyZone, max_alt) then - inZoneUnits[inZoneUnits + 1] = units[i] - end - end - - return inZoneUnits + local units = {} + + for i = 1, #unit_names do + units[#units + 1] = Unit.getByName(unitNames[i]) + end + + local inZoneUnits = {} + for i =1, #units do + if units[i]:isActive() and mist.pointInPolygon(units[i]:getPosition().p, polyZone, max_alt) then + inZoneUnits[inZoneUnits + 1] = units[i] + end + end + + return inZoneUnits end function mist.flagFunc.units_in_polygon(vars) ---[[vars needs to be: + --[[vars needs to be: units = table, zone = table, flag = number, @@ -2866,368 +2866,368 @@ req_num = number or nil toggle = boolean or nil unitTableDef = table or nil ]] --- type_tbl - local type_tbl = { - [{'units', 'unit'}] = 'table', - [{'zone', 'polyzone'}] = 'table', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - [{'maxalt', 'alt'}] = {'number', 'nil'}, - interval = {'number', 'nil'}, - [{'req_num', 'reqnum'}] = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - unitTableDef = {'table', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_in_polygon', type_tbl, vars) - assert(err, errmsg) - local units = vars.units or vars.unit - local zone = vars.zone or vars.polyzone - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local interval = vars.interval or 1 - local maxalt = vars.maxalt or vars.alt - local req_num = vars.req_num or vars.reqnum or 1 - local toggle = vars.toggle or nil - local unitTableDef = vars.unitTableDef - - if not units.processed then - unitTableDef = mist.utils.deepCopy(units) - end - - if (units.processed and units.processed < mist.getLastDBUpdateTime()) or not units.processed then -- run unit table short cuts - units = mist.makeUnitTable(unitTableDef) - end + -- type_tbl + local type_tbl = { + [{'units', 'unit'}] = 'table', + [{'zone', 'polyzone'}] = 'table', + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + [{'maxalt', 'alt'}] = {'number', 'nil'}, + interval = {'number', 'nil'}, + [{'req_num', 'reqnum'}] = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + unitTableDef = {'table', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_in_polygon', type_tbl, vars) + assert(err, errmsg) + local units = vars.units or vars.unit + local zone = vars.zone or vars.polyzone + local flag = vars.flag + local stopflag = vars.stopflag or -1 + local interval = vars.interval or 1 + local maxalt = vars.maxalt or vars.alt + local req_num = vars.req_num or vars.reqnum or 1 + local toggle = vars.toggle or nil + local unitTableDef = vars.unitTableDef + + if not units.processed then + unitTableDef = mist.utils.deepCopy(units) + end + + if (units.processed and units.processed < mist.getLastDBUpdateTime()) or not units.processed then -- run unit table short cuts + units = mist.makeUnitTable(unitTableDef) + end + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == 0) then + local num_in_zone = 0 + for i = 1, #units do + local unit = Unit.getByName(units[i]) + if unit then + local pos = unit:getPosition().p + if mist.pointInPolygon(pos, zone, maxalt) then + num_in_zone = num_in_zone + 1 + if num_in_zone >= req_num and trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + break + end + end + end + end + if toggle and (num_in_zone < req_num) and trigger.misc.getUserFlag(flag) > 0 then + trigger.action.setUserFlag(flag, false) + end + -- do another check in case stopflag was set true by this function + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == 0) then + mist.scheduleFunction(mist.flagFunc.units_in_polygon, {{units = units, zone = zone, flag = flag, stopflag = stopflag, interval = interval, req_num = req_num, maxalt = maxalt, toggle = toggle, unitTableDef = unitTableDef}}, timer.getTime() + interval) + end + end - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == 0) then - local num_in_zone = 0 - for i = 1, #units do - local unit = Unit.getByName(units[i]) - if unit then - local pos = unit:getPosition().p - if mist.pointInPolygon(pos, zone, maxalt) then - num_in_zone = num_in_zone + 1 - if num_in_zone >= req_num and trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - break - end - end - end - end - if toggle and (num_in_zone < req_num) and trigger.misc.getUserFlag(flag) > 0 then - trigger.action.setUserFlag(flag, false) - end - -- do another check in case stopflag was set true by this function - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == 0) then - mist.scheduleFunction(mist.flagFunc.units_in_polygon, {{units = units, zone = zone, flag = flag, stopflag = stopflag, interval = interval, req_num = req_num, maxalt = maxalt, toggle = toggle, unitTableDef = unitTableDef}}, timer.getTime() + interval) - end - end - end function mist.getUnitsInZones(unit_names, zone_names, zone_type) - - zone_type = zone_type or 'cylinder' - if zone_type == 'c' or zone_type == 'cylindrical' or zone_type == 'C' then - zone_type = 'cylinder' - end - if zone_type == 's' or zone_type == 'spherical' or zone_type == 'S' then - zone_type = 'sphere' - end - - assert(zone_type == 'cylinder' or zone_type == 'sphere', 'invalid zone_type: ' .. tostring(zone_type)) - local units = {} - local zones = {} + zone_type = zone_type or 'cylinder' + if zone_type == 'c' or zone_type == 'cylindrical' or zone_type == 'C' then + zone_type = 'cylinder' + end + if zone_type == 's' or zone_type == 'spherical' or zone_type == 'S' then + zone_type = 'sphere' + end - for k = 1, #unit_names do - local unit = Unit.getByName(unit_names[k]) - if unit then - units[#units + 1] = unit - end - end - - - for k = 1, #zone_names do - local zone = trigger.misc.getZone(zone_names[k]) - if zone then - zones[#zones + 1] = {radius = zone.radius, x = zone.point.x, y = zone.point.y, z = zone.point.z} - end - end - - local in_zone_units = {} - - for units_ind = 1, #units do - for zones_ind = 1, #zones do - if zone_type == 'sphere' then --add land height value for sphere zone type - local alt = land.getHeight({x = zones[zones_ind].x, y = zones[zones_ind].z}) - if alt then - zones[zones_ind].y = alt - end - end - local unit_pos = units[units_ind]:getPosition().p - if unit_pos and units[units_ind]:isActive() == true then - if zone_type == 'cylinder' and (((unit_pos.x - zones[zones_ind].x)^2 + (unit_pos.z - zones[zones_ind].z)^2)^0.5 <= zones[zones_ind].radius) then - in_zone_units[#in_zone_units + 1] = units[units_ind] - break - elseif zone_type == 'sphere' and (((unit_pos.x - zones[zones_ind].x)^2 + (unit_pos.y - zones[zones_ind].y)^2 + (unit_pos.z - zones[zones_ind].z)^2)^0.5 <= zones[zones_ind].radius) then - in_zone_units[#in_zone_units + 1] = units[units_ind] - break - end - end - end - end - return in_zone_units + assert(zone_type == 'cylinder' or zone_type == 'sphere', 'invalid zone_type: ' .. tostring(zone_type)) + + local units = {} + local zones = {} + + for k = 1, #unit_names do + local unit = Unit.getByName(unit_names[k]) + if unit then + units[#units + 1] = unit + end + end + + + for k = 1, #zone_names do + local zone = trigger.misc.getZone(zone_names[k]) + if zone then + zones[#zones + 1] = {radius = zone.radius, x = zone.point.x, y = zone.point.y, z = zone.point.z} + end + end + + local in_zone_units = {} + + for units_ind = 1, #units do + for zones_ind = 1, #zones do + if zone_type == 'sphere' then --add land height value for sphere zone type + local alt = land.getHeight({x = zones[zones_ind].x, y = zones[zones_ind].z}) + if alt then + zones[zones_ind].y = alt + end + end + local unit_pos = units[units_ind]:getPosition().p + if unit_pos and units[units_ind]:isActive() == true then + if zone_type == 'cylinder' and (((unit_pos.x - zones[zones_ind].x)^2 + (unit_pos.z - zones[zones_ind].z)^2)^0.5 <= zones[zones_ind].radius) then + in_zone_units[#in_zone_units + 1] = units[units_ind] + break + elseif zone_type == 'sphere' and (((unit_pos.x - zones[zones_ind].x)^2 + (unit_pos.y - zones[zones_ind].y)^2 + (unit_pos.z - zones[zones_ind].z)^2)^0.5 <= zones[zones_ind].radius) then + in_zone_units[#in_zone_units + 1] = units[units_ind] + break + end + end + end + end + return in_zone_units end function mist.flagFunc.units_in_zones(vars) - --[[vars needs to be: - units = table, - zones = table, - flag = number, - stopflag = number or nil, - zone_type = string or nil, - req_num = number or nil, - interval = number or nil - toggle = boolean or nil - ]] - -- type_tbl - local type_tbl = { - units = 'table', - zones = 'table', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - [{'zone_type', 'zonetype'}] = {'string', 'nil'}, - [{'req_num', 'reqnum'}] = {'number', 'nil'}, - interval = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - unitTableDef = {'table', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_in_zones', type_tbl, vars) - assert(err, errmsg) - local units = vars.units - local zones = vars.zones - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local zone_type = vars.zone_type or vars.zonetype or 'cylinder' - local req_num = vars.req_num or vars.reqnum or 1 - local interval = vars.interval or 1 - local toggle = vars.toggle or nil - local unitTableDef = vars.unitTableDef - - if not units.processed then - unitTableDef = mist.utils.deepCopy(units) - end - - if (units.processed and units.processed < mist.getLastDBUpdateTime()) or not units.processed then -- run unit table short cuts - units = mist.makeUnitTable(unitTableDef) - end - - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - - local in_zone_units = mist.getUnitsInZones(units, zones, zone_type) - - if #in_zone_units >= req_num and trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - elseif #in_zone_units < req_num and toggle then - trigger.action.setUserFlag(flag, false) - end - -- do another check in case stopflag was set true by this function - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - mist.scheduleFunction(mist.flagFunc.units_in_zones, {{units = units, zones = zones, flag = flag, stopflag = stopflag, zone_type = zone_type, req_num = req_num, interval = interval, toggle = toggle, unitTableDef = unitTableDef}}, timer.getTime() + interval) - end - end - + --[[vars needs to be: + units = table, + zones = table, + flag = number, + stopflag = number or nil, + zone_type = string or nil, + req_num = number or nil, + interval = number or nil + toggle = boolean or nil + ]] + -- type_tbl + local type_tbl = { + units = 'table', + zones = 'table', + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + [{'zone_type', 'zonetype'}] = {'string', 'nil'}, + [{'req_num', 'reqnum'}] = {'number', 'nil'}, + interval = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + unitTableDef = {'table', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_in_zones', type_tbl, vars) + assert(err, errmsg) + local units = vars.units + local zones = vars.zones + local flag = vars.flag + local stopflag = vars.stopflag or -1 + local zone_type = vars.zone_type or vars.zonetype or 'cylinder' + local req_num = vars.req_num or vars.reqnum or 1 + local interval = vars.interval or 1 + local toggle = vars.toggle or nil + local unitTableDef = vars.unitTableDef + + if not units.processed then + unitTableDef = mist.utils.deepCopy(units) + end + + if (units.processed and units.processed < mist.getLastDBUpdateTime()) or not units.processed then -- run unit table short cuts + units = mist.makeUnitTable(unitTableDef) + end + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + + local in_zone_units = mist.getUnitsInZones(units, zones, zone_type) + + if #in_zone_units >= req_num and trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + elseif #in_zone_units < req_num and toggle then + trigger.action.setUserFlag(flag, false) + end + -- do another check in case stopflag was set true by this function + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + mist.scheduleFunction(mist.flagFunc.units_in_zones, {{units = units, zones = zones, flag = flag, stopflag = stopflag, zone_type = zone_type, req_num = req_num, interval = interval, toggle = toggle, unitTableDef = unitTableDef}}, timer.getTime() + interval) + end + end + end function mist.getUnitsInMovingZones(unit_names, zone_unit_names, radius, zone_type) - - zone_type = zone_type or 'cylinder' - if zone_type == 'c' or zone_type == 'cylindrical' or zone_type == 'C' then - zone_type = 'cylinder' - end - if zone_type == 's' or zone_type == 'spherical' or zone_type == 'S' then - zone_type = 'sphere' - end - - assert(zone_type == 'cylinder' or zone_type == 'sphere', 'invalid zone_type: ' .. tostring(zone_type)) - local units = {} - local zone_units = {} + zone_type = zone_type or 'cylinder' + if zone_type == 'c' or zone_type == 'cylindrical' or zone_type == 'C' then + zone_type = 'cylinder' + end + if zone_type == 's' or zone_type == 'spherical' or zone_type == 'S' then + zone_type = 'sphere' + end - for k = 1, #unit_names do - local unit = Unit.getByName(unit_names[k]) - if unit then - units[#units + 1] = unit - end - end - - for k = 1, #zone_unit_names do - local unit = Unit.getByName(zone_unit_names[k]) - if unit then - zone_units[#zone_units + 1] = unit - end - end + assert(zone_type == 'cylinder' or zone_type == 'sphere', 'invalid zone_type: ' .. tostring(zone_type)) - local in_zone_units = {} - - for units_ind = 1, #units do - for zone_units_ind = 1, #zone_units do - local unit_pos = units[units_ind]:getPosition().p - local zone_unit_pos = zone_units[zone_units_ind]:getPosition().p - if unit_pos and zone_unit_pos and units[units_ind]:isActive() == true then - if zone_type == 'cylinder' and (((unit_pos.x - zone_unit_pos.x)^2 + (unit_pos.z - zone_unit_pos.z)^2)^0.5 <= radius) then - in_zone_units[#in_zone_units + 1] = units[units_ind] - break - elseif zone_type == 'sphere' and (((unit_pos.x - zone_unit_pos.x)^2 + (unit_pos.y - zone_unit_pos.y)^2 + (unit_pos.z - zone_unit_pos.z)^2)^0.5 <= radius) then - in_zone_units[#in_zone_units + 1] = units[units_ind] - break - end - end - end - end - return in_zone_units + local units = {} + local zone_units = {} + + for k = 1, #unit_names do + local unit = Unit.getByName(unit_names[k]) + if unit then + units[#units + 1] = unit + end + end + + for k = 1, #zone_unit_names do + local unit = Unit.getByName(zone_unit_names[k]) + if unit then + zone_units[#zone_units + 1] = unit + end + end + + local in_zone_units = {} + + for units_ind = 1, #units do + for zone_units_ind = 1, #zone_units do + local unit_pos = units[units_ind]:getPosition().p + local zone_unit_pos = zone_units[zone_units_ind]:getPosition().p + if unit_pos and zone_unit_pos and units[units_ind]:isActive() == true then + if zone_type == 'cylinder' and (((unit_pos.x - zone_unit_pos.x)^2 + (unit_pos.z - zone_unit_pos.z)^2)^0.5 <= radius) then + in_zone_units[#in_zone_units + 1] = units[units_ind] + break + elseif zone_type == 'sphere' and (((unit_pos.x - zone_unit_pos.x)^2 + (unit_pos.y - zone_unit_pos.y)^2 + (unit_pos.z - zone_unit_pos.z)^2)^0.5 <= radius) then + in_zone_units[#in_zone_units + 1] = units[units_ind] + break + end + end + end + end + return in_zone_units end function mist.flagFunc.units_in_moving_zones(vars) - --[[vars needs to be: - units = table, - zone_units = table, - radius = number, - flag = number, - stopflag = number or nil, - zone_type = string or nil, - req_num = number or nil, - interval = number or nil - toggle = boolean or nil - ]] - -- type_tbl - local type_tbl = { - units = 'table', - [{'zone_units', 'zoneunits'}] = 'table', - radius = 'number', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - [{'zone_type', 'zonetype'}] = {'string', 'nil'}, - [{'req_num', 'reqnum'}] = {'number', 'nil'}, - interval = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - unitTableDef = {'table', 'nil'}, - zUnitTableDef = {'table', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_in_moving_zones', type_tbl, vars) - assert(err, errmsg) - local units = vars.units - local zone_units = vars.zone_units or vars.zoneunits - local radius = vars.radius - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local zone_type = vars.zone_type or vars.zonetype or 'cylinder' - local req_num = vars.req_num or vars.reqnum or 1 - local interval = vars.interval or 1 - local toggle = vars.toggle or nil - local unitTableDef = vars.unitTableDef - local zUnitTableDef = vars.zUnitTableDef - - if not units.processed then - unitTableDef = mist.utils.deepCopy(units) - end - - if not zone_units.processed then - zUnitTableDef = mist.utils.deepCopy(zone_units) - end - - if (units.processed and units.processed < mist.getLastDBUpdateTime()) or not units.processed then -- run unit table short cuts - units = mist.makeUnitTable(unitTableDef) - end - - if (zone_units.processed and zone_units.processed < mist.getLastDBUpdateTime()) or not zone_units.processed then -- run unit table short cuts - zone_units = mist.makeUnitTable(zUnitTableDef) - end + --[[vars needs to be: + units = table, + zone_units = table, + radius = number, + flag = number, + stopflag = number or nil, + zone_type = string or nil, + req_num = number or nil, + interval = number or nil + toggle = boolean or nil + ]] + -- type_tbl + local type_tbl = { + units = 'table', + [{'zone_units', 'zoneunits'}] = 'table', + radius = 'number', + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + [{'zone_type', 'zonetype'}] = {'string', 'nil'}, + [{'req_num', 'reqnum'}] = {'number', 'nil'}, + interval = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + unitTableDef = {'table', 'nil'}, + zUnitTableDef = {'table', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_in_moving_zones', type_tbl, vars) + assert(err, errmsg) + local units = vars.units + local zone_units = vars.zone_units or vars.zoneunits + local radius = vars.radius + local flag = vars.flag + local stopflag = vars.stopflag or -1 + local zone_type = vars.zone_type or vars.zonetype or 'cylinder' + local req_num = vars.req_num or vars.reqnum or 1 + local interval = vars.interval or 1 + local toggle = vars.toggle or nil + local unitTableDef = vars.unitTableDef + local zUnitTableDef = vars.zUnitTableDef + + if not units.processed then + unitTableDef = mist.utils.deepCopy(units) + end + + if not zone_units.processed then + zUnitTableDef = mist.utils.deepCopy(zone_units) + end + + if (units.processed and units.processed < mist.getLastDBUpdateTime()) or not units.processed then -- run unit table short cuts + units = mist.makeUnitTable(unitTableDef) + end + + if (zone_units.processed and zone_units.processed < mist.getLastDBUpdateTime()) or not zone_units.processed then -- run unit table short cuts + zone_units = mist.makeUnitTable(zUnitTableDef) + end + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + + local in_zone_units = mist.getUnitsInMovingZones(units, zone_units, radius, zone_type) + + if #in_zone_units >= req_num and trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + elseif #in_zone_units < req_num and toggle then + trigger.action.setUserFlag(flag, false) + end + -- do another check in case stopflag was set true by this function + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + mist.scheduleFunction(mist.flagFunc.units_in_moving_zones, {{units = units, zone_units = zone_units, radius = radius, flag = flag, stopflag = stopflag, zone_type = zone_type, req_num = req_num, interval = interval, toggle = toggle, unitTableDef = unitTableDef, zUnitTableDef = zUnitTableDef}}, timer.getTime() + interval) + end + end - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - - local in_zone_units = mist.getUnitsInMovingZones(units, zone_units, radius, zone_type) - - if #in_zone_units >= req_num and trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - elseif #in_zone_units < req_num and toggle then - trigger.action.setUserFlag(flag, false) - end - -- do another check in case stopflag was set true by this function - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - mist.scheduleFunction(mist.flagFunc.units_in_moving_zones, {{units = units, zone_units = zone_units, radius = radius, flag = flag, stopflag = stopflag, zone_type = zone_type, req_num = req_num, interval = interval, toggle = toggle, unitTableDef = unitTableDef, zUnitTableDef = zUnitTableDef}}, timer.getTime() + interval) - end - end - end mist.getUnitsLOS = function(unitset1, altoffset1, unitset2, altoffset2, radius) - radius = radius or math.huge - - local unit_info1 = {} - local unit_info2 = {} - - -- get the positions all in one step, saves execution time. - for unitset1_ind = 1, #unitset1 do - local unit1 = Unit.getByName(unitset1[unitset1_ind]) - if unit1 and unit1:isActive() == true then - unit_info1[#unit_info1 + 1] = {} - unit_info1[#unit_info1]["unit"] = unit1 - unit_info1[#unit_info1]["pos"] = unit1:getPosition().p - end - end - - for unitset2_ind = 1, #unitset2 do - local unit2 = Unit.getByName(unitset2[unitset2_ind]) - if unit2 and unit2:isActive() == true then - unit_info2[#unit_info2 + 1] = {} - unit_info2[#unit_info2]["unit"] = unit2 - unit_info2[#unit_info2]["pos"] = unit2:getPosition().p - end - end + radius = radius or math.huge - local LOS_data = {} - -- now compute los - for unit1_ind = 1, #unit_info1 do - local unit_added = false - for unit2_ind = 1, #unit_info2 do - if radius == math.huge or (mist.vec.mag(mist.vec.sub(unit_info1[unit1_ind].pos, unit_info2[unit2_ind].pos)) < radius) then -- inside radius - local point1 = { x = unit_info1[unit1_ind].pos.x, y = unit_info1[unit1_ind].pos.y + altoffset1, z = unit_info1[unit1_ind].pos.z} - local point2 = { x = unit_info2[unit2_ind].pos.x, y = unit_info2[unit2_ind].pos.y + altoffset2, z = unit_info2[unit2_ind].pos.z} - if land.isVisible(point1, point2) then - if unit_added == false then - unit_added = true - LOS_data[#LOS_data + 1] = {} - LOS_data[#LOS_data]['unit'] = unit_info1[unit1_ind].unit - LOS_data[#LOS_data]['vis'] = {} - LOS_data[#LOS_data]['vis'][#LOS_data[#LOS_data]['vis'] + 1] = unit_info2[unit2_ind].unit - else - LOS_data[#LOS_data]['vis'][#LOS_data[#LOS_data]['vis'] + 1] = unit_info2[unit2_ind].unit - end - end - end - end - end - - return LOS_data + local unit_info1 = {} + local unit_info2 = {} + + -- get the positions all in one step, saves execution time. + for unitset1_ind = 1, #unitset1 do + local unit1 = Unit.getByName(unitset1[unitset1_ind]) + if unit1 and unit1:isActive() == true then + unit_info1[#unit_info1 + 1] = {} + unit_info1[#unit_info1]["unit"] = unit1 + unit_info1[#unit_info1]["pos"] = unit1:getPosition().p + end + end + + for unitset2_ind = 1, #unitset2 do + local unit2 = Unit.getByName(unitset2[unitset2_ind]) + if unit2 and unit2:isActive() == true then + unit_info2[#unit_info2 + 1] = {} + unit_info2[#unit_info2]["unit"] = unit2 + unit_info2[#unit_info2]["pos"] = unit2:getPosition().p + end + end + + local LOS_data = {} + -- now compute los + for unit1_ind = 1, #unit_info1 do + local unit_added = false + for unit2_ind = 1, #unit_info2 do + if radius == math.huge or (mist.vec.mag(mist.vec.sub(unit_info1[unit1_ind].pos, unit_info2[unit2_ind].pos)) < radius) then -- inside radius + local point1 = { x = unit_info1[unit1_ind].pos.x, y = unit_info1[unit1_ind].pos.y + altoffset1, z = unit_info1[unit1_ind].pos.z} + local point2 = { x = unit_info2[unit2_ind].pos.x, y = unit_info2[unit2_ind].pos.y + altoffset2, z = unit_info2[unit2_ind].pos.z} + if land.isVisible(point1, point2) then + if unit_added == false then + unit_added = true + LOS_data[#LOS_data + 1] = {} + LOS_data[#LOS_data]['unit'] = unit_info1[unit1_ind].unit + LOS_data[#LOS_data]['vis'] = {} + LOS_data[#LOS_data]['vis'][#LOS_data[#LOS_data]['vis'] + 1] = unit_info2[unit2_ind].unit + else + LOS_data[#LOS_data]['vis'][#LOS_data[#LOS_data]['vis'] + 1] = unit_info2[unit2_ind].unit + end + end + end + end + end + + return LOS_data end mist.flagFunc.units_LOS = function(vars) ---[[vars needs to be: -unitset1 = table, -altoffset1 = number, -unitset2 = table, + --[[vars needs to be: +unitset1 = table, +altoffset1 = number, +unitset2 = table, altoffset2 = number, flag = number, stopflag = number or nil, @@ -3236,72 +3236,72 @@ interval = number or nil, req_num = number or nil toggle = boolean or nil ]] --- type_tbl - local type_tbl = { - [{'unitset1', 'units1'}] = 'table', - [{'altoffset1', 'alt1'}] = 'number', - [{'unitset2', 'units2'}] = 'table', - [{'altoffset2', 'alt2'}] = 'number', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - [{'req_num', 'reqnum'}] = {'number', 'nil'}, - interval = {'number', 'nil'}, - radius = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - unitTableDef1 = {'table', 'nil'}, - unitTableDef2 = {'table', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_LOS', type_tbl, vars) - assert(err, errmsg) - local unitset1 = vars.unitset1 or vars.units1 - local altoffset1 = vars.altoffset1 or vars.alt1 - local unitset2 = vars.unitset2 or vars.units2 - local altoffset2 = vars.altoffset2 or vars.alt2 - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local interval = vars.interval or 1 - local radius = vars.radius or math.huge - local req_num = vars.req_num or vars.reqnum or 1 - local toggle = vars.toggle or nil - local unitTableDef1 = vars.unitTableDef1 - local unitTableDef2 = vars.unitTableDef2 - - if not unitset1.processed then - unitTableDef1 = mist.utils.deepCopy(unitset1) - end - - if not unitset2.processed then - unitTableDef2 = mist.utils.deepCopy(unitset2) - end - - if (unitset1.processed and unitset1.processed < mist.getLastDBUpdateTime()) or not unitset1.processed then -- run unit table short cuts - unitset1 = mist.makeUnitTable(unitTableDef1) - end - - if (unitset2.processed and unitset2.processed < mist.getLastDBUpdateTime()) or not unitset2.processed then -- run unit table short cuts - unitset2 = mist.makeUnitTable(unitTableDef2) - end - - - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - - local unitLOSdata = mist.getUnitsLOS(unitset1, altoffset1, unitset2, altoffset2, radius) - - if #unitLOSdata >= req_num and trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - elseif #unitLOSdata < req_num and toggle then - trigger.action.setUserFlag(flag, false) - end - -- do another check in case stopflag was set true by this function - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - mist.scheduleFunction(mist.flagFunc.units_LOS, {{unitset1 = unitset1, altoffset1 = altoffset1, unitset2 = unitset2, altoffset2 = altoffset2, flag = flag, stopflag = stopflag, radius = radius, req_num = req_num, interval = interval, toggle = toggle, unitTableDef1 = unitTableDef1, unitTableDef2 = unitTableDef2}}, timer.getTime() + interval) - end - end + -- type_tbl + local type_tbl = { + [{'unitset1', 'units1'}] = 'table', + [{'altoffset1', 'alt1'}] = 'number', + [{'unitset2', 'units2'}] = 'table', + [{'altoffset2', 'alt2'}] = 'number', + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + [{'req_num', 'reqnum'}] = {'number', 'nil'}, + interval = {'number', 'nil'}, + radius = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + unitTableDef1 = {'table', 'nil'}, + unitTableDef2 = {'table', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_LOS', type_tbl, vars) + assert(err, errmsg) + local unitset1 = vars.unitset1 or vars.units1 + local altoffset1 = vars.altoffset1 or vars.alt1 + local unitset2 = vars.unitset2 or vars.units2 + local altoffset2 = vars.altoffset2 or vars.alt2 + local flag = vars.flag + local stopflag = vars.stopflag or -1 + local interval = vars.interval or 1 + local radius = vars.radius or math.huge + local req_num = vars.req_num or vars.reqnum or 1 + local toggle = vars.toggle or nil + local unitTableDef1 = vars.unitTableDef1 + local unitTableDef2 = vars.unitTableDef2 + + if not unitset1.processed then + unitTableDef1 = mist.utils.deepCopy(unitset1) + end + + if not unitset2.processed then + unitTableDef2 = mist.utils.deepCopy(unitset2) + end + + if (unitset1.processed and unitset1.processed < mist.getLastDBUpdateTime()) or not unitset1.processed then -- run unit table short cuts + unitset1 = mist.makeUnitTable(unitTableDef1) + end + + if (unitset2.processed and unitset2.processed < mist.getLastDBUpdateTime()) or not unitset2.processed then -- run unit table short cuts + unitset2 = mist.makeUnitTable(unitTableDef2) + end + + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + + local unitLOSdata = mist.getUnitsLOS(unitset1, altoffset1, unitset2, altoffset2, radius) + + if #unitLOSdata >= req_num and trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + elseif #unitLOSdata < req_num and toggle then + trigger.action.setUserFlag(flag, false) + end + -- do another check in case stopflag was set true by this function + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + mist.scheduleFunction(mist.flagFunc.units_LOS, {{unitset1 = unitset1, altoffset1 = altoffset1, unitset2 = unitset2, altoffset2 = altoffset2, flag = flag, stopflag = stopflag, radius = radius, req_num = req_num, interval = interval, toggle = toggle, unitTableDef1 = unitTableDef1, unitTableDef2 = unitTableDef2}}, timer.getTime() + interval) + end + end end mist.flagFunc.group_alive = function(vars) ---[[vars + --[[vars groupName flag toggle @@ -3309,218 +3309,218 @@ interval stopFlag ]] - local type_tbl = { - [{'group', 'groupname', 'gp', 'groupName'}] = 'string', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - interval = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_alive', type_tbl, vars) - assert(err, errmsg) - - local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local interval = vars.interval or 1 - local toggle = vars.toggle or nil - - - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true and #Group.getByName(groupName):getUnits() > 0 then - if trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - end - else - if toggle then - trigger.action.setUserFlag(flag, false) - end - end - end - - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - mist.scheduleFunction(mist.flagFunc.group_alive, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle}}, timer.getTime() + interval) - end + local type_tbl = { + [{'group', 'groupname', 'gp', 'groupName'}] = 'string', + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + interval = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_alive', type_tbl, vars) + assert(err, errmsg) + + local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname + local flag = vars.flag + local stopflag = vars.stopflag or -1 + local interval = vars.interval or 1 + local toggle = vars.toggle or nil + + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true and #Group.getByName(groupName):getUnits() > 0 then + if trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + end + else + if toggle then + trigger.action.setUserFlag(flag, false) + end + end + end + + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + mist.scheduleFunction(mist.flagFunc.group_alive, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle}}, timer.getTime() + interval) + end end mist.flagFunc.group_dead = function(vars) - local type_tbl = { - [{'group', 'groupname', 'gp', 'groupName'}] = 'string', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - interval = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_dead', type_tbl, vars) - assert(err, errmsg) - - local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local interval = vars.interval or 1 - local toggle = vars.toggle or nil - - - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - if (Group.getByName(groupName) and Group.getByName(groupName):isExist() == false) or (Group.getByName(groupName) and #Group.getByName(groupName):getUnits() < 1) or not Group.getByName(groupName) then - if trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - end - else - if toggle then - trigger.action.setUserFlag(flag, false) - end - end - end - - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - mist.scheduleFunction(mist.flagFunc.group_dead, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle}}, timer.getTime() + interval) - end + local type_tbl = { + [{'group', 'groupname', 'gp', 'groupName'}] = 'string', + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + interval = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_dead', type_tbl, vars) + assert(err, errmsg) + + local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname + local flag = vars.flag + local stopflag = vars.stopflag or -1 + local interval = vars.interval or 1 + local toggle = vars.toggle or nil + + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + if (Group.getByName(groupName) and Group.getByName(groupName):isExist() == false) or (Group.getByName(groupName) and #Group.getByName(groupName):getUnits() < 1) or not Group.getByName(groupName) then + if trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + end + else + if toggle then + trigger.action.setUserFlag(flag, false) + end + end + end + + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + mist.scheduleFunction(mist.flagFunc.group_dead, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle}}, timer.getTime() + interval) + end end mist.flagFunc.group_alive_less_than = function(vars) - local type_tbl = { - [{'group', 'groupname', 'gp', 'groupName'}] = 'string', - percent = 'number', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - interval = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_alive_less_than', type_tbl, vars) - assert(err, errmsg) - - local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname - local flag = vars.flag - local percent = vars.percent - local stopflag = vars.stopflag or -1 - local interval = vars.interval or 1 - local toggle = vars.toggle or nil - - - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then - if Group.getByName(groupName):getSize()/Group.getByName(groupName):getInitialSize() < percent/100 then - if trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - end - else - if toggle then - trigger.action.setUserFlag(flag, false) - end - end - else - if trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - end - end - end - - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - mist.scheduleFunction(mist.flagFunc.group_alive_less_than, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle, percent = percent}}, timer.getTime() + interval) - end + local type_tbl = { + [{'group', 'groupname', 'gp', 'groupName'}] = 'string', + percent = 'number', + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + interval = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_alive_less_than', type_tbl, vars) + assert(err, errmsg) + + local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname + local flag = vars.flag + local percent = vars.percent + local stopflag = vars.stopflag or -1 + local interval = vars.interval or 1 + local toggle = vars.toggle or nil + + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then + if Group.getByName(groupName):getSize()/Group.getByName(groupName):getInitialSize() < percent/100 then + if trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + end + else + if toggle then + trigger.action.setUserFlag(flag, false) + end + end + else + if trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + end + end + end + + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + mist.scheduleFunction(mist.flagFunc.group_alive_less_than, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle, percent = percent}}, timer.getTime() + interval) + end end mist.flagFunc.group_alive_more_than = function(vars) - local type_tbl = { - [{'group', 'groupname', 'gp', 'groupName'}] = 'string', - percent = 'number', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - interval = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_alive_more_than', type_tbl, vars) - assert(err, errmsg) - - local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname - local flag = vars.flag - local percent = vars.percent - local stopflag = vars.stopflag or -1 - local interval = vars.interval or 1 - local toggle = vars.toggle or nil - - - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then - if Group.getByName(groupName):getSize()/Group.getByName(groupName):getInitialSize() > percent/100 then - if trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - end - else - if toggle and trigger.misc.getUserFlag(flag) == 1 then - trigger.action.setUserFlag(flag, false) - end - end - else --- just in case - if toggle and trigger.misc.getUserFlag(flag) == 1 then - trigger.action.setUserFlag(flag, false) - end - end - end - - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - mist.scheduleFunction(mist.flagFunc.group_alive_more_than, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle, percent = percent}}, timer.getTime() + interval) - end + local type_tbl = { + [{'group', 'groupname', 'gp', 'groupName'}] = 'string', + percent = 'number', + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + interval = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_alive_more_than', type_tbl, vars) + assert(err, errmsg) + + local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname + local flag = vars.flag + local percent = vars.percent + local stopflag = vars.stopflag or -1 + local interval = vars.interval or 1 + local toggle = vars.toggle or nil + + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then + if Group.getByName(groupName):getSize()/Group.getByName(groupName):getInitialSize() > percent/100 then + if trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + end + else + if toggle and trigger.misc.getUserFlag(flag) == 1 then + trigger.action.setUserFlag(flag, false) + end + end + else --- just in case + if toggle and trigger.misc.getUserFlag(flag) == 1 then + trigger.action.setUserFlag(flag, false) + end + end + end + + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + mist.scheduleFunction(mist.flagFunc.group_alive_more_than, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle, percent = percent}}, timer.getTime() + interval) + end end mist.getAvgPoint = function(points) - local avgX, avgY, avgZ, totNum = 0, 0, 0, 0 - for i = 1, #points do - local nPoint = mist.utils.makeVec3(points[i]) - if nPoint.z then - avgX = avgX + nPoint.x - avgY = avgY + nPoint.y - avgZ = avgZ + nPoint.z - totNum = totNum + 1 - end - end - if totNum ~= 0 then - return {x = avgX/totNum, y = avgY/totNum, z = avgZ/totNum} - end + local avgX, avgY, avgZ, totNum = 0, 0, 0, 0 + for i = 1, #points do + local nPoint = mist.utils.makeVec3(points[i]) + if nPoint.z then + avgX = avgX + nPoint.x + avgY = avgY + nPoint.y + avgZ = avgZ + nPoint.z + totNum = totNum + 1 + end + end + if totNum ~= 0 then + return {x = avgX/totNum, y = avgY/totNum, z = avgZ/totNum} + end end --Gets the average position of a group of units (by name) mist.getAvgPos = function(unitNames) - local avgX, avgY, avgZ, totNum = 0, 0, 0, 0 - for i = 1, #unitNames do - local unit - if Unit.getByName(unitNames[i]) then - unit = Unit.getByName(unitNames[i]) - elseif StaticObject.getByName(unitNames[i]) then - unit = StaticObject.getByName(unitNames[i]) - end - if unit then - local pos = unit:getPosition().p - if pos then -- you never know O.o - avgX = avgX + pos.x - avgY = avgY + pos.y - avgZ = avgZ + pos.z - totNum = totNum + 1 - end - end - end - if totNum ~= 0 then - return {x = avgX/totNum, y = avgY/totNum, z = avgZ/totNum} - end + local avgX, avgY, avgZ, totNum = 0, 0, 0, 0 + for i = 1, #unitNames do + local unit + if Unit.getByName(unitNames[i]) then + unit = Unit.getByName(unitNames[i]) + elseif StaticObject.getByName(unitNames[i]) then + unit = StaticObject.getByName(unitNames[i]) + end + if unit then + local pos = unit:getPosition().p + if pos then -- you never know O.o + avgX = avgX + pos.x + avgY = avgY + pos.y + avgZ = avgZ + pos.z + totNum = totNum + 1 + end + end + end + if totNum ~= 0 then + return {x = avgX/totNum, y = avgY/totNum, z = avgZ/totNum} + end end mist.getAvgGroupPos = function(groupName) - if type(groupName) == 'string' and Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then - groupName = Group.getByName(groupName) - end - local units = {} - for i = 1, #groupName:getSize() do - table.insert(units, groupName.getUnit(i):getName()) - end - - return mist.getAvgPos(units) + if type(groupName) == 'string' and Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then + groupName = Group.getByName(groupName) + end + local units = {} + for i = 1, #groupName:getSize() do + table.insert(units, groupName.getUnit(i):getName()) + end + + return mist.getAvgPos(units) end @@ -3529,115 +3529,115 @@ end mist.demos = {} mist.demos.printFlightData = function(unit) - if unit:isExist() then - local function printData(unit, prevVel, prevE, prevTime) - local angles = mist.getAttitude(unit) - if angles then - local Heading = angles.Heading - local Pitch = angles.Pitch - local Roll = angles.Roll - local Yaw = angles.Yaw - local AoA = angles.AoA - local ClimbAngle = angles.ClimbAngle - - if not Heading then - Heading = 'NA' - else - Heading = string.format('%12.2f', mist.utils.toDegree(Heading)) - end - - if not Pitch then - Pitch = 'NA' - else - Pitch = string.format('%12.2f', mist.utils.toDegree(Pitch)) - end - - if not Roll then - Roll = 'NA' - else - Roll = string.format('%12.2f', mist.utils.toDegree(Roll)) - end - - local AoAplusYaw = 'NA' - if AoA and Yaw then - AoAplusYaw = string.format('%12.2f', mist.utils.toDegree((AoA^2 + Yaw^2)^0.5)) - end - - if not Yaw then - Yaw = 'NA' - else - Yaw = string.format('%12.2f', mist.utils.toDegree(Yaw)) - end - - if not AoA then - AoA = 'NA' - else - AoA = string.format('%12.2f', mist.utils.toDegree(AoA)) - end - - if not ClimbAngle then - ClimbAngle = 'NA' - else - ClimbAngle = string.format('%12.2f', mist.utils.toDegree(ClimbAngle)) - end - local unitPos = unit:getPosition() - local unitVel = unit:getVelocity() - local curTime = timer.getTime() - local absVel = string.format('%12.2f', mist.vec.mag(unitVel)) - - - local unitAcc = 'NA' - local Gs = 'NA' - local axialGs = 'NA' - local transGs = 'NA' - if prevVel and prevTime then - local xAcc = (unitVel.x - prevVel.x)/(curTime - prevTime) - local yAcc = (unitVel.y - prevVel.y)/(curTime - prevTime) - local zAcc = (unitVel.z - prevVel.z)/(curTime - prevTime) - - unitAcc = string.format('%12.2f', mist.vec.mag({x = xAcc, y = yAcc, z = zAcc})) - Gs = string.format('%12.2f', mist.vec.mag({x = xAcc, y = yAcc + 9.81, z = zAcc})/9.81) - axialGs = string.format('%12.2f', mist.vec.dp({x = xAcc, y = yAcc + 9.81, z = zAcc}, unitPos.x)/9.81) - transGs = string.format('%12.2f', mist.vec.mag(mist.vec.cp({x = xAcc, y = yAcc + 9.81, z = zAcc}, unitPos.x))/9.81) - end - - local E = 0.5*mist.vec.mag(unitVel)^2 + 9.81*unitPos.p.y - - local energy = string.format('%12.2e', E) - - local dEdt = 'NA' - if prevE and prevTime then - dEdt = string.format('%12.2e', (E - prevE)/(curTime - prevTime)) - end - - trigger.action.outText(string.format('%-25s', 'Heading: ') .. Heading .. ' degrees\n' .. string.format('%-25s', 'Roll: ') .. Roll .. ' degrees\n' .. string.format('%-25s', 'Pitch: ') .. Pitch - .. ' degrees\n' .. string.format('%-25s', 'Yaw: ') .. Yaw .. ' degrees\n' .. string.format('%-25s', 'AoA: ') .. AoA .. ' degrees\n' .. string.format('%-25s', 'AoA plus Yaw: ') .. AoAplusYaw .. ' degrees\n' .. string.format('%-25s', 'Climb Angle: ') .. - ClimbAngle .. ' degrees\n' .. string.format('%-25s', 'Absolute Velocity: ') .. absVel .. ' m/s\n' .. string.format('%-25s', 'Absolute Acceleration: ') .. unitAcc ..' m/s^2\n' - .. string.format('%-25s', 'Axial G loading: ') .. axialGs .. ' g\n' .. string.format('%-25s', 'Transverse G loading: ') .. transGs .. ' g\n' .. string.format('%-25s', 'Absolute G loading: ') .. Gs .. ' g\n' .. string.format('%-25s', 'Energy: ') .. energy .. ' J/kg\n' .. string.format('%-25s', 'dE/dt: ') .. dEdt .. - ' J/(kg*s)', 1) + if unit:isExist() then + local function printData(unit, prevVel, prevE, prevTime) + local angles = mist.getAttitude(unit) + if angles then + local Heading = angles.Heading + local Pitch = angles.Pitch + local Roll = angles.Roll + local Yaw = angles.Yaw + local AoA = angles.AoA + local ClimbAngle = angles.ClimbAngle + + if not Heading then + Heading = 'NA' + else + Heading = string.format('%12.2f', mist.utils.toDegree(Heading)) + end + + if not Pitch then + Pitch = 'NA' + else + Pitch = string.format('%12.2f', mist.utils.toDegree(Pitch)) + end + + if not Roll then + Roll = 'NA' + else + Roll = string.format('%12.2f', mist.utils.toDegree(Roll)) + end + + local AoAplusYaw = 'NA' + if AoA and Yaw then + AoAplusYaw = string.format('%12.2f', mist.utils.toDegree((AoA^2 + Yaw^2)^0.5)) + end + + if not Yaw then + Yaw = 'NA' + else + Yaw = string.format('%12.2f', mist.utils.toDegree(Yaw)) + end + + if not AoA then + AoA = 'NA' + else + AoA = string.format('%12.2f', mist.utils.toDegree(AoA)) + end + + if not ClimbAngle then + ClimbAngle = 'NA' + else + ClimbAngle = string.format('%12.2f', mist.utils.toDegree(ClimbAngle)) + end + local unitPos = unit:getPosition() + local unitVel = unit:getVelocity() + local curTime = timer.getTime() + local absVel = string.format('%12.2f', mist.vec.mag(unitVel)) + + + local unitAcc = 'NA' + local Gs = 'NA' + local axialGs = 'NA' + local transGs = 'NA' + if prevVel and prevTime then + local xAcc = (unitVel.x - prevVel.x)/(curTime - prevTime) + local yAcc = (unitVel.y - prevVel.y)/(curTime - prevTime) + local zAcc = (unitVel.z - prevVel.z)/(curTime - prevTime) + + unitAcc = string.format('%12.2f', mist.vec.mag({x = xAcc, y = yAcc, z = zAcc})) + Gs = string.format('%12.2f', mist.vec.mag({x = xAcc, y = yAcc + 9.81, z = zAcc})/9.81) + axialGs = string.format('%12.2f', mist.vec.dp({x = xAcc, y = yAcc + 9.81, z = zAcc}, unitPos.x)/9.81) + transGs = string.format('%12.2f', mist.vec.mag(mist.vec.cp({x = xAcc, y = yAcc + 9.81, z = zAcc}, unitPos.x))/9.81) + end + + local E = 0.5*mist.vec.mag(unitVel)^2 + 9.81*unitPos.p.y + + local energy = string.format('%12.2e', E) + + local dEdt = 'NA' + if prevE and prevTime then + dEdt = string.format('%12.2e', (E - prevE)/(curTime - prevTime)) + end + + trigger.action.outText(string.format('%-25s', 'Heading: ') .. Heading .. ' degrees\n' .. string.format('%-25s', 'Roll: ') .. Roll .. ' degrees\n' .. string.format('%-25s', 'Pitch: ') .. Pitch + .. ' degrees\n' .. string.format('%-25s', 'Yaw: ') .. Yaw .. ' degrees\n' .. string.format('%-25s', 'AoA: ') .. AoA .. ' degrees\n' .. string.format('%-25s', 'AoA plus Yaw: ') .. AoAplusYaw .. ' degrees\n' .. string.format('%-25s', 'Climb Angle: ') .. + ClimbAngle .. ' degrees\n' .. string.format('%-25s', 'Absolute Velocity: ') .. absVel .. ' m/s\n' .. string.format('%-25s', 'Absolute Acceleration: ') .. unitAcc ..' m/s^2\n' + .. string.format('%-25s', 'Axial G loading: ') .. axialGs .. ' g\n' .. string.format('%-25s', 'Transverse G loading: ') .. transGs .. ' g\n' .. string.format('%-25s', 'Absolute G loading: ') .. Gs .. ' g\n' .. string.format('%-25s', 'Energy: ') .. energy .. ' J/kg\n' .. string.format('%-25s', 'dE/dt: ') .. dEdt .. + ' J/(kg*s)', 1) + + return unitVel, E, curTime + end + end + + local function frameFinder(unit, prevVel, prevE, prevTime) + if unit:isExist() then + local currVel = unit:getVelocity() + if prevVel and (prevVel.x ~= currVel.x or prevVel.y ~= currVel.y or prevVel.z ~= currVel.z) or (prevTime and (timer.getTime() - prevTime) > 0.25) then + prevVel, prevE, prevTime = printData(unit, prevVel, prevE, prevTime) + end + mist.scheduleFunction(frameFinder, {unit, prevVel, prevE, prevTime}, timer.getTime() + 0.005) -- it can't go this fast, limited to the 100 times a sec check right now. + end + end + + + local curVel = unit:getVelocity() + local curTime = timer.getTime() + local curE = 0.5*mist.vec.mag(curVel)^2 + 9.81*unit:getPosition().p.y + frameFinder(unit, curVel, curE, curTime) + + end - return unitVel, E, curTime - end - end - - local function frameFinder(unit, prevVel, prevE, prevTime) - if unit:isExist() then - local currVel = unit:getVelocity() - if prevVel and (prevVel.x ~= currVel.x or prevVel.y ~= currVel.y or prevVel.z ~= currVel.z) or (prevTime and (timer.getTime() - prevTime) > 0.25) then - prevVel, prevE, prevTime = printData(unit, prevVel, prevE, prevTime) - end - mist.scheduleFunction(frameFinder, {unit, prevVel, prevE, prevTime}, timer.getTime() + 0.005) -- it can't go this fast, limited to the 100 times a sec check right now. - end - end - - - local curVel = unit:getVelocity() - local curTime = timer.getTime() - local curE = 0.5*mist.vec.mag(curVel)^2 + 9.81*unit:getPosition().p.y - frameFinder(unit, curVel, curE, curTime) - - end - end -------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------- @@ -3652,82 +3652,82 @@ mist.air.fixedWing = {} mist.air.heli = {} mist.goRoute = function(group, path) - local misTask = { - id = 'Mission', - params = { - route = { - points = mist.utils.deepCopy(path), - }, - }, - } - if type(group) == 'string' then - group = Group.getByName(group) - end - local groupCon = nil - if group then - groupCon = group:getController() - if groupCon then - groupCon:setTask(misTask) - return true - end - end - --Controller.setTask(groupCon, misTask) - return false + local misTask = { + id = 'Mission', + params = { + route = { + points = mist.utils.deepCopy(path), + }, + }, + } + if type(group) == 'string' then + group = Group.getByName(group) + end + local groupCon = nil + if group then + groupCon = group:getController() + if groupCon then + groupCon:setTask(misTask) + return true + end + end + --Controller.setTask(groupCon, misTask) + return false end function mist.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 = mist.DBs.MEgroupsByName[groupIdent].groupId - end - - for coa_name, coa_data in pairs(env.mission.coalition) do - if (coa_name == 'red' or coa_name == 'blue') and type(coa_data) == 'table' then - if coa_data.country then --there is a country table - for cntry_id, cntry_data in pairs(coa_data.country) do - for obj_type_name, obj_type_data in pairs(cntry_data) do - if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" then -- only these types have points - if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's a group! - for group_num, group_data in pairs(obj_type_data.group) do - if group_data and group_data.groupId == gpId then -- this is the group we are looking for - if group_data.route and group_data.route.points and #group_data.route.points > 0 then - local points = {} - - for point_num, point in pairs(group_data.route.points) do - local routeData = {} - if not point.point then - routeData.x = point.x - routeData.y = point.y - else - routeData.point = point.point --it's possible that the ME could move to the point = Vec2 notation. - end - routeData.form = point.action - routeData.speed = point.speed - routeData.alt = point.alt - routeData.alt_type = point.alt_type - routeData.airdromeId = point.airdromeId - routeData.helipadId = point.helipadId - routeData.type = point.type - routeData.action = point.action - if task then - routeData.task = point.task - end - points[point_num] = routeData - end - - return points - end - return - end --if group_data and group_data.name and group_data.name == 'groupname' - end --for group_num, group_data in pairs(obj_type_data.group) do - end --if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then - end --if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then - end --for obj_type_name, obj_type_data in pairs(cntry_data) do - end --for cntry_id, cntry_data in pairs(coa_data.country) do - end --if coa_data.country then --there is a country table - end --if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then - end --for coa_name, coa_data in pairs(mission.coalition) do + -- 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 = mist.DBs.MEgroupsByName[groupIdent].groupId + end + + for coa_name, coa_data in pairs(env.mission.coalition) do + if (coa_name == 'red' or coa_name == 'blue') and type(coa_data) == 'table' then + if coa_data.country then --there is a country table + for cntry_id, cntry_data in pairs(coa_data.country) do + for obj_type_name, obj_type_data in pairs(cntry_data) do + if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" then -- only these types have points + if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's a group! + for group_num, group_data in pairs(obj_type_data.group) do + if group_data and group_data.groupId == gpId then -- this is the group we are looking for + if group_data.route and group_data.route.points and #group_data.route.points > 0 then + local points = {} + + for point_num, point in pairs(group_data.route.points) do + local routeData = {} + if not point.point then + routeData.x = point.x + routeData.y = point.y + else + routeData.point = point.point --it's possible that the ME could move to the point = Vec2 notation. + end + routeData.form = point.action + routeData.speed = point.speed + routeData.alt = point.alt + routeData.alt_type = point.alt_type + routeData.airdromeId = point.airdromeId + routeData.helipadId = point.helipadId + routeData.type = point.type + routeData.action = point.action + if task then + routeData.task = point.task + end + points[point_num] = routeData + end + + return points + end + return + end --if group_data and group_data.name and group_data.name == 'groupname' + end --for group_num, group_data in pairs(obj_type_data.group) do + end --if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then + end --if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then + end --for obj_type_name, obj_type_data in pairs(cntry_data) do + end --for cntry_id, cntry_data in pairs(coa_data.country) do + end --if coa_data.country then --there is a country table + end --if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then + end --for coa_name, coa_data in pairs(mission.coalition) do end @@ -3738,177 +3738,177 @@ mist.ground.buildPath = function() end -- ???? -- No longer accepts path mist.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 = mist.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 + local wp = {} + wp.x = point.x - wp.type = 'Turning Point' + if point.z then + wp.y = point.z + else + wp.y = point.y + end + local form, speed - return wp + if point.speed and not overRideSpeed then + wp.speed = point.speed + elseif type(overRideSpeed) == 'number' then + wp.speed = overRideSpeed + else + wp.speed = mist.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 mist.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 altType == 'agl' then - wp.alt_type = 'RADIO' - elseif altType == 'baro' or altType == '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 = mist.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 + 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 altType == 'agl' then + wp.alt_type = 'RADIO' + elseif altType == 'baro' or altType == '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 = mist.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 mist.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 altType == 'agl' then - wp.alt_type = 'RADIO' - elseif altType == 'baro' or altType == '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 = mist.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 + 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 altType == 'agl' then + wp.alt_type = 'RADIO' + elseif altType == 'baro' or altType == '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 = mist.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 @@ -3920,234 +3920,234 @@ end -- need to return a Vec3 or Vec2? function mist.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 + 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 mist.getRandomPointInZone = function(zoneName, innerRadius) - if type(zoneName) == 'string' and type(trigger.misc.getZone(zoneName)) == 'table' then - return mist.getRandPointInCircle(trigger.misc.getZone(zoneName).point, trigger.misc.getZone(zoneName).radius, innerRadius) - end - return false + if type(zoneName) == 'string' and type(trigger.misc.getZone(zoneName)) == 'table' then + return mist.getRandPointInCircle(trigger.misc.getZone(zoneName).point, trigger.misc.getZone(zoneName).radius, innerRadius) + end + return false end mist.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 mist.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 = mist.getRandPointInCircle(point, radius, innerRadius) - - local offset = {} - local posStart = mist.getLeadPos(group) + 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 mist.utils.kmphToMps(20) - offset.x = mist.utils.round(math.sin(heading - (math.pi/2)) * 50 + rndCoord.x, 3) - offset.z = mist.utils.round(math.cos(heading + (math.pi/2)) * 50 + rndCoord.y, 3) - path[#path + 1] = mist.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] = mist.ground.buildWP({['x'] = posStart.x + 11, ['z'] = posStart.z + 11}, 'off_road', speed) - path[#path + 1] = mist.ground.buildWP(posStart, 'on_road', speed) - path[#path + 1] = mist.ground.buildWP(offset, 'on_road', speed) - else - path[#path + 1] = mist.ground.buildWP({['x'] = posStart.x + 25, ['z'] = posStart.z + 25}, form, speed) - end - - path[#path + 1] = mist.ground.buildWP(offset, form, speed) - path[#path + 1] = mist.ground.buildWP(rndCoord, form, speed) - - mist.goRoute(group, path) - - return + 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 = mist.getRandPointInCircle(point, radius, innerRadius) + + local offset = {} + local posStart = mist.getLeadPos(group) + + offset.x = mist.utils.round(math.sin(heading - (math.pi/2)) * 50 + rndCoord.x, 3) + offset.z = mist.utils.round(math.cos(heading + (math.pi/2)) * 50 + rndCoord.y, 3) + path[#path + 1] = mist.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] = mist.ground.buildWP({['x'] = posStart.x + 11, ['z'] = posStart.z + 11}, 'off_road', speed) + path[#path + 1] = mist.ground.buildWP(posStart, 'on_road', speed) + path[#path + 1] = mist.ground.buildWP(offset, 'on_road', speed) + else + path[#path + 1] = mist.ground.buildWP({['x'] = posStart.x + 25, ['z'] = posStart.z + 25}, form, speed) + end + + path[#path + 1] = mist.ground.buildWP(offset, form, speed) + path[#path + 1] = mist.ground.buildWP(rndCoord, form, speed) + + mist.goRoute(group, path) + + return end mist.groupRandomDistSelf = function(gpData, dist, form, heading, speed) - local pos = mist.getLeadPos(gpData) - local fakeZone = {} - fakeZone.radius = dist or math.random(300, 1000) - fakeZone.point = {x = pos.x, y = pos.y, z = pos.z} - mist.groupToRandomZone(gpData, fakeZone, form, heading, speed) + local pos = mist.getLeadPos(gpData) + local fakeZone = {} + fakeZone.radius = dist or math.random(300, 1000) + fakeZone.point = {x = pos.x, y = pos.y, z = pos.z} + mist.groupToRandomZone(gpData, fakeZone, form, heading, speed) - return + return end mist.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 type(gpData) == 'string' then + gpData = Group.getByName(gpData) + end - if speed then - speed = mist.utils.kmphToMps(speed) - end - - local vars = {} - vars.group = gpData - vars.radius = zone.radius - vars.form = form - vars.headingDegrees = heading - vars.speed = speed - vars.point = mist.utils.zoneToVec3(zone) - - mist.groupToRandomPoint(vars) + 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 - return + if speed then + speed = mist.utils.kmphToMps(speed) + end + + local vars = {} + vars.group = gpData + vars.radius = zone.radius + vars.form = form + vars.headingDegrees = heading + vars.speed = speed + vars.point = mist.utils.zoneToVec3(zone) + + mist.groupToRandomPoint(vars) + + return end mist.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 + 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 mist.terrainHeightDiff = function(coord, searchSize) - local samples = {} - local searchRadius = 5 - if searchSize then - searchRadius = searchSize - end - if type(coord) == 'string' then - coord = mist.utils.zoneToVec3(coord) - end - - coord = mist.utils.makeVec2(coord) - - samples[#samples + 1] = land.getHeight(coord) - for i = 0, 360, 30 do - samples[#samples + 1] = land.getHeight({x = (coord.x + (math.sin(math.rad(i))*searchRadius)), y = (coord.y + (math.cos(math.rad(i))*searchRadius))}) - if searchRadius >= 20 then -- if search radius is sorta large, take a sample halfway between center and outer edge - samples[#samples + 1] = land.getHeight({x = (coord.x + (math.sin(math.rad(i))*(searchRadius/2))), y = (coord.y + (math.cos(math.rad(i))*(searchRadius/2)))}) - end - end - local tMax, tMin = 0, 1000000 - for index, height in pairs(samples) do - if height > tMax then - tMax = height - end - if height < tMin then - tMin = height - end - end - return mist.utils.round(tMax - tMin, 2) + local samples = {} + local searchRadius = 5 + if searchSize then + searchRadius = searchSize + end + if type(coord) == 'string' then + coord = mist.utils.zoneToVec3(coord) + end + + coord = mist.utils.makeVec2(coord) + + samples[#samples + 1] = land.getHeight(coord) + for i = 0, 360, 30 do + samples[#samples + 1] = land.getHeight({x = (coord.x + (math.sin(math.rad(i))*searchRadius)), y = (coord.y + (math.cos(math.rad(i))*searchRadius))}) + if searchRadius >= 20 then -- if search radius is sorta large, take a sample halfway between center and outer edge + samples[#samples + 1] = land.getHeight({x = (coord.x + (math.sin(math.rad(i))*(searchRadius/2))), y = (coord.y + (math.cos(math.rad(i))*(searchRadius/2)))}) + end + end + local tMax, tMin = 0, 1000000 + for index, height in pairs(samples) do + if height > tMax then + tMax = height + end + if height < tMin then + tMin = height + end + end + return mist.utils.round(tMax - tMin, 2) end mist.groupToPoint = function(gpData, point, form, heading, speed, useRoads) - if type(point) == 'string' then - point = trigger.misc.getZone(point) - end - if speed then - speed = mist.utils.kmphToMps(speed) - end - - local vars = {} - vars.group = gpData - vars.form = form - vars.headingDegrees = heading - vars.speed = speed - vars.disableRoads = useRoads - vars.point = mist.utils.zoneToVec3(point) - mist.groupToRandomPoint(vars) - - return + if type(point) == 'string' then + point = trigger.misc.getZone(point) + end + if speed then + speed = mist.utils.kmphToMps(speed) + end + + local vars = {} + vars.group = gpData + vars.form = form + vars.headingDegrees = heading + vars.speed = speed + vars.disableRoads = useRoads + vars.point = mist.utils.zoneToVec3(point) + mist.groupToRandomPoint(vars) + + return end mist.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 Unit.isExist(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 Unit.isExist(unit) and ind < lowestInd then - lowestInd = ind - return unit:getPosition().p - end - end - end - if leader and Unit.isExist(leader) then -- maybe a little too paranoid now... - return leader:getPosition().p - end + if type(group) == 'string' then -- group name + group = Group.getByName(group) + end + + local units = group:getUnits() + + local leader = units[1] + if not Unit.isExist(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 Unit.isExist(unit) and ind < lowestInd then + lowestInd = ind + return unit:getPosition().p + end + end + end + if leader and Unit.isExist(leader) then -- maybe a little too paranoid now... + return leader:getPosition().p + end end -- end of Mission task functions @@ -4163,348 +4163,348 @@ end -------MESAGGES------ do - local messageList = {} - local messageDisplayRate = 0.1 -- this defines the max refresh rate of the message box it honestly only needs to go faster than this for precision timing stuff (which could be its own function) - local messageID = 0 - local displayActive = false - local displayFuncId = 0 - - local caSlots = false - local caMSGtoGroup = false - - if env.mission.groundControl then -- just to be sure? - for index, value in pairs(env.mission.groundControl) do - if type(value) == 'table' then - for roleName, roleVal in pairs(value) do - for rIndex, rVal in pairs(roleVal) do - if rIndex == 'red' or rIndex == 'blue' then - if env.mission.groundControl[index][roleName][rIndex] > 0 then - caSlots = true - break - end - end - end - end - elseif type(value) == 'boolean' and value == true then - caSlots = true - break - end - end - end - - local function mistdisplayV5() - --[[thoughts to improve upon - event handler based activeClients table. - display messages only when there is an update - possibly co-routine it. + local messageList = {} + local messageDisplayRate = 0.1 -- this defines the max refresh rate of the message box it honestly only needs to go faster than this for precision timing stuff (which could be its own function) + local messageID = 0 + local displayActive = false + local displayFuncId = 0 + + local caSlots = false + local caMSGtoGroup = false + + if env.mission.groundControl then -- just to be sure? + for index, value in pairs(env.mission.groundControl) do + if type(value) == 'table' then + for roleName, roleVal in pairs(value) do + for rIndex, rVal in pairs(roleVal) do + if rIndex == 'red' or rIndex == 'blue' then + if env.mission.groundControl[index][roleName][rIndex] > 0 then + caSlots = true + break + end + end + end + end + elseif type(value) == 'boolean' and value == true then + caSlots = true + break + end + end + end + + local function mistdisplayV5() + --[[thoughts to improve upon + event handler based activeClients table. + display messages only when there is an update + possibly co-routine it. ]] - end - - local function mistdisplayV4() - local activeClients = {} + end - for clientId, clientData in pairs(mist.DBs.humansById) do - if Unit.getByName(clientData.unitName) and Unit.getByName(clientData.unitName):isExist() == true then - activeClients[clientData.groupId] = clientData.groupName - end - end - - --[[if caSlots == true and caMSGtoGroup == true then - - end]] + local function mistdisplayV4() + local activeClients = {} - - if #messageList > 0 then - if displayActive == false then - displayActive = true - end - --mist.debug.writeData(mist.utils.serialize,{'msg', messageList}, 'messageList.lua') - local msgTableText = {} - local msgTableSound = {} - - for messageId, messageData in pairs(messageList) do - if messageData.displayedFor > messageData.displayTime then - messageData:remove() -- now using the remove/destroy function. - else - if messageData.displayedFor then - messageData.displayedFor = messageData.displayedFor + messageDisplayRate - end - local nextSound = 1000 - local soundIndex = 0 - - if messageData.multSound and #messageData.multSound > 0 then - for index, sData in pairs(messageData.multSound) do - if sData.time <= messageData.displayedFor and sData.played == false and sData.time < nextSound then -- find index of the next sound to be played - nextSound = sData.time - soundIndex = index - end - end - if soundIndex ~= 0 then - messageData.multSound[soundIndex].played = true - end - end - - for recIndex, recData in pairs(messageData.msgFor) do -- iterate recipiants - if recData == 'RED' or recData == 'BLUE' or activeClients[recData] then -- rec exists - if messageData.text then -- text - if not msgTableText[recData] then -- create table entry for text - msgTableText[recData] = {} - msgTableText[recData].text = {} - if recData == 'RED' or recData == 'BLUE' then - msgTableText[recData].text[1] = '-------Combined Arms Message-------- \n' - end - msgTableText[recData].text[#msgTableText[recData].text + 1] = messageData.text - msgTableText[recData].displayTime = messageData.displayTime - messageData.displayedFor - else -- add to table entry and adjust display time if needed - if recData == 'RED' or recData == 'BLUE' then - msgTableText[recData].text[#msgTableText[recData].text + 1] = '\n ---------------- Combined Arms Message: \n' - else - msgTableText[recData].text[#msgTableText[recData].text + 1] = '\n ---------------- \n' - end - msgTableText[recData].text[#msgTableText[recData].text + 1] = messageData.text - if msgTableText[recData].displayTime < messageData.displayTime - messageData.displayedFor then - msgTableText[recData].displayTime = messageData.displayTime - messageData.displayedFor - else - msgTableText[recData].displayTime = 1 - end - end - end - if soundIndex ~= 0 then - msgTableSound[recData] = messageData.multSound[soundIndex].file - end - end - end - - - end - end - ------- new display - - if caSlots == true and caMSGtoGroup == false then - if msgTableText['RED'] then - trigger.action.outTextForCoalition(coalition.side.RED, table.concat(msgTableText['RED'].text), msgTableText['RED'].displayTime, true) + for clientId, clientData in pairs(mist.DBs.humansById) do + if Unit.getByName(clientData.unitName) and Unit.getByName(clientData.unitName):isExist() == true then + activeClients[clientData.groupId] = clientData.groupName + end + end - end - if msgTableText['BLUE'] then - trigger.action.outTextForCoalition(coalition.side.BLUE, table.concat(msgTableText['BLUE'].text), msgTableText['BLUE'].displayTime, true) - end - end - - for index, msgData in pairs(msgTableText) do - if type(index) == 'number' then -- its a groupNumber - trigger.action.outTextForGroup(index, table.concat(msgData.text), msgData.displayTime, true) - end - end - --- new audio - if msgTableSound['RED'] then - trigger.action.outSoundForCoalition(coalition.side.RED, msgTableSound['RED']) - end - if msgTableSound['BLUE'] then - trigger.action.outSoundForCoalition(coalition.side.BLUE, msgTableSound['BLUE']) - end - - - for index, file in pairs(msgTableSound) do - if type(index) == 'number' then -- its a groupNumber - trigger.action.outSoundForGroup(index, file) - end - end - else - mist.removeFunction(displayFuncId) - displayActive = false - end - - end - - local typeBase = { - ['Mi-8MT'] = {'Mi-8MTV2', 'Mi-8MTV', 'Mi-8'}, - ['MiG-21Bis'] = {'Mig-21'}, - ['MiG-15bis'] = {'Mig-15'}, - ['FW-190D9'] = {'FW-190'}, - ['Bf-109K-4'] = {'Bf-109'}, - } - - --[[mist.setCAGroupMSG = function(val) - if type(val) == 'boolean' then - caMSGtoGroup = val - return true - end - return false - end]] + --[[if caSlots == true and caMSGtoGroup == true then - mist.message = { - + end]] - add = function(vars) - local function msgSpamFilter(recList, spamBlockOn) - for id, name in pairs(recList) do - if name == spamBlockOn then - -- env.info('already on recList') - return recList - end - end - --env.info('add to recList') - table.insert(recList, spamBlockOn) - return recList - end - - --[[ - local vars = {} - vars.text = 'Hello World' - vars.displayTime = 20 - vars.msgFor = {coa = {'red'}, countries = {'Ukraine', 'Georgia'}, unitTypes = {'A-10C'}} - mist.message.add(vars) - Displays the message for all red coalition players. Players belonging to Ukraine and Georgia, and all A-10Cs on the map - - ]] - - - local new = {} - new.text = vars.text -- The actual message - new.displayTime = vars.displayTime -- How long will the message appear for - new.displayedFor = 0 -- how long the message has been displayed so far - new.name = vars.name -- ID to overwrite the older message (if it exists) Basically it replaces a message that is displayed with new text. - new.addedAt = timer.getTime() - new.update = true - - if vars.multSound and vars.multSound[1] then - new.multSound = vars.multSound - else - new.multSound = {} - end - - if vars.sound or vars.fileName then -- converts old sound file system into new multSound format - local sound = vars.sound - if vars.fileName then - sound = vars.fileName - end - new.multSound[#new.multSound+1] = {time = 0.1, file = sound} - end - - if #new.multSound > 0 then - for i, data in pairs(new.multSound) do - data.played = false - end - end - - local newMsgFor = {} -- list of all groups message displays for - for forIndex, forData in pairs(vars.msgFor) do - for list, listData in pairs(forData) do - for clientId, clientData in pairs(mist.DBs.humansById) do - forIndex = string.lower(forIndex) - if type(listData) == 'string' then - listData = string.lower(listData) - end - if (forIndex == 'coa' and (listData == string.lower(clientData.coalition) or listData == 'all')) or (forIndex == 'countries' and string.lower(clientData.country) == listData) or (forIndex == 'units' and string.lower(clientData.unitName) == listData) then -- - newMsgFor = msgSpamFilter(newMsgFor, clientData.groupId) -- so units dont get the same message twice if complex rules are given - --table.insert(newMsgFor, clientId) - elseif forIndex == 'unittypes' then - for typeId, typeData in pairs(listData) do - local found = false - for clientDataEntry, clientDataVal in pairs(clientData) do - if type(clientDataVal) == 'string' then - if mist.matchString(list, clientDataVal) == true or list == 'all' then - local sString = typeData - for rName, pTbl in pairs(typeBase) do -- just a quick check to see if the user may have meant something and got the specific type of the unit wrong - for pIndex, pName in pairs(pTbl) do - if mist.stringMatch(sString, pName) then - sString = rName - end - end - end - if sString == clientData.type then - found = true - newMsgFor = msgSpamFilter(newMsgFor, clientData.groupId) -- sends info oto other function to see if client is already recieving the current message. - --table.insert(newMsgFor, clientId) - end - end - end - if found == true then -- shouldn't this be elsewhere too? - break - end - end - end + if #messageList > 0 then + if displayActive == false then + displayActive = true + end + --mist.debug.writeData(mist.utils.serialize,{'msg', messageList}, 'messageList.lua') + local msgTableText = {} + local msgTableSound = {} - end - end - for coaData, coaId in pairs(coalition.side) do - if string.lower(forIndex) == 'coa' or string.lower(forIndex) == 'ca' then - if listData == string.lower(coaData) or listData == 'all' then - newMsgFor = msgSpamFilter(newMsgFor, coaData) - end - end - end - end - end - - if #newMsgFor > 0 then - new.msgFor = newMsgFor -- I swear its not confusing - - else - return false - end - - - if vars.name and type(vars.name) == 'string' then - for i = 1, #messageList do - if messageList[i].name then - if messageList[i].name == vars.name then - --env.info('updateMessage') - messageList[i].displayedFor = 0 - messageList[i].addedAt = timer.getTime() - messageList[i].sound = new.sound - messageList[i].text = new.text - messageList[i].msgFor = new.msgFor - messageList[i].multSound = new.multSound - messageList[i].update = true - return messageList[i].messageID - end - end - end - end - - messageID = messageID + 1 - new.messageID = messageID + for messageId, messageData in pairs(messageList) do + if messageData.displayedFor > messageData.displayTime then + messageData:remove() -- now using the remove/destroy function. + else + if messageData.displayedFor then + messageData.displayedFor = messageData.displayedFor + messageDisplayRate + end + local nextSound = 1000 + local soundIndex = 0 - --mist.debug.writeData(mist.utils.serialize,{'msg', new}, 'newMsg.lua') + if messageData.multSound and #messageData.multSound > 0 then + for index, sData in pairs(messageData.multSound) do + if sData.time <= messageData.displayedFor and sData.played == false and sData.time < nextSound then -- find index of the next sound to be played + nextSound = sData.time + soundIndex = index + end + end + if soundIndex ~= 0 then + messageData.multSound[soundIndex].played = true + end + end - - messageList[#messageList + 1] = new - - local mt = { __index = mist.message} - setmetatable(new, mt) + for recIndex, recData in pairs(messageData.msgFor) do -- iterate recipiants + if recData == 'RED' or recData == 'BLUE' or activeClients[recData] then -- rec exists + if messageData.text then -- text + if not msgTableText[recData] then -- create table entry for text + msgTableText[recData] = {} + msgTableText[recData].text = {} + if recData == 'RED' or recData == 'BLUE' then + msgTableText[recData].text[1] = '-------Combined Arms Message-------- \n' + end + msgTableText[recData].text[#msgTableText[recData].text + 1] = messageData.text + msgTableText[recData].displayTime = messageData.displayTime - messageData.displayedFor + else -- add to table entry and adjust display time if needed + if recData == 'RED' or recData == 'BLUE' then + msgTableText[recData].text[#msgTableText[recData].text + 1] = '\n ---------------- Combined Arms Message: \n' + else + msgTableText[recData].text[#msgTableText[recData].text + 1] = '\n ---------------- \n' + end + msgTableText[recData].text[#msgTableText[recData].text + 1] = messageData.text + if msgTableText[recData].displayTime < messageData.displayTime - messageData.displayedFor then + msgTableText[recData].displayTime = messageData.displayTime - messageData.displayedFor + else + msgTableText[recData].displayTime = 1 + end + end + end + if soundIndex ~= 0 then + msgTableSound[recData] = messageData.multSound[soundIndex].file + end + end + end + + + end + end + ------- new display + + if caSlots == true and caMSGtoGroup == false then + if msgTableText['RED'] then + trigger.action.outTextForCoalition(coalition.side.RED, table.concat(msgTableText['RED'].text), msgTableText['RED'].displayTime, true) + + end + if msgTableText['BLUE'] then + trigger.action.outTextForCoalition(coalition.side.BLUE, table.concat(msgTableText['BLUE'].text), msgTableText['BLUE'].displayTime, true) + end + end + + for index, msgData in pairs(msgTableText) do + if type(index) == 'number' then -- its a groupNumber + trigger.action.outTextForGroup(index, table.concat(msgData.text), msgData.displayTime, true) + end + end + --- new audio + if msgTableSound['RED'] then + trigger.action.outSoundForCoalition(coalition.side.RED, msgTableSound['RED']) + end + if msgTableSound['BLUE'] then + trigger.action.outSoundForCoalition(coalition.side.BLUE, msgTableSound['BLUE']) + end + + + for index, file in pairs(msgTableSound) do + if type(index) == 'number' then -- its a groupNumber + trigger.action.outSoundForGroup(index, file) + end + end + else + mist.removeFunction(displayFuncId) + displayActive = false + end + + end + + local typeBase = { + ['Mi-8MT'] = {'Mi-8MTV2', 'Mi-8MTV', 'Mi-8'}, + ['MiG-21Bis'] = {'Mig-21'}, + ['MiG-15bis'] = {'Mig-15'}, + ['FW-190D9'] = {'FW-190'}, + ['Bf-109K-4'] = {'Bf-109'}, + } + + --[[mist.setCAGroupMSG = function(val) + if type(val) == 'boolean' then + caMSGtoGroup = val + return true + end + return false +end]] + + mist.message = { + + + add = function(vars) + local function msgSpamFilter(recList, spamBlockOn) + for id, name in pairs(recList) do + if name == spamBlockOn then + -- env.info('already on recList') + return recList + end + end + --env.info('add to recList') + table.insert(recList, spamBlockOn) + return recList + end + + --[[ + local vars = {} + vars.text = 'Hello World' + vars.displayTime = 20 + vars.msgFor = {coa = {'red'}, countries = {'Ukraine', 'Georgia'}, unitTypes = {'A-10C'}} + mist.message.add(vars) + + Displays the message for all red coalition players. Players belonging to Ukraine and Georgia, and all A-10Cs on the map + + ]] + + + local new = {} + new.text = vars.text -- The actual message + new.displayTime = vars.displayTime -- How long will the message appear for + new.displayedFor = 0 -- how long the message has been displayed so far + new.name = vars.name -- ID to overwrite the older message (if it exists) Basically it replaces a message that is displayed with new text. + new.addedAt = timer.getTime() + new.update = true + + if vars.multSound and vars.multSound[1] then + new.multSound = vars.multSound + else + new.multSound = {} + end + + if vars.sound or vars.fileName then -- converts old sound file system into new multSound format + local sound = vars.sound + if vars.fileName then + sound = vars.fileName + end + new.multSound[#new.multSound+1] = {time = 0.1, file = sound} + end + + if #new.multSound > 0 then + for i, data in pairs(new.multSound) do + data.played = false + end + end + + local newMsgFor = {} -- list of all groups message displays for + for forIndex, forData in pairs(vars.msgFor) do + for list, listData in pairs(forData) do + for clientId, clientData in pairs(mist.DBs.humansById) do + forIndex = string.lower(forIndex) + if type(listData) == 'string' then + listData = string.lower(listData) + end + if (forIndex == 'coa' and (listData == string.lower(clientData.coalition) or listData == 'all')) or (forIndex == 'countries' and string.lower(clientData.country) == listData) or (forIndex == 'units' and string.lower(clientData.unitName) == listData) then -- + newMsgFor = msgSpamFilter(newMsgFor, clientData.groupId) -- so units dont get the same message twice if complex rules are given + --table.insert(newMsgFor, clientId) + elseif forIndex == 'unittypes' then + for typeId, typeData in pairs(listData) do + local found = false + for clientDataEntry, clientDataVal in pairs(clientData) do + if type(clientDataVal) == 'string' then + if mist.matchString(list, clientDataVal) == true or list == 'all' then + local sString = typeData + for rName, pTbl in pairs(typeBase) do -- just a quick check to see if the user may have meant something and got the specific type of the unit wrong + for pIndex, pName in pairs(pTbl) do + if mist.stringMatch(sString, pName) then + sString = rName + end + end + end + if sString == clientData.type then + found = true + newMsgFor = msgSpamFilter(newMsgFor, clientData.groupId) -- sends info oto other function to see if client is already recieving the current message. + --table.insert(newMsgFor, clientId) + end + end + end + if found == true then -- shouldn't this be elsewhere too? + break + end + end + end + + end + end + for coaData, coaId in pairs(coalition.side) do + if string.lower(forIndex) == 'coa' or string.lower(forIndex) == 'ca' then + if listData == string.lower(coaData) or listData == 'all' then + newMsgFor = msgSpamFilter(newMsgFor, coaData) + end + end + end + end + end + + if #newMsgFor > 0 then + new.msgFor = newMsgFor -- I swear its not confusing + + else + return false + end + + + if vars.name and type(vars.name) == 'string' then + for i = 1, #messageList do + if messageList[i].name then + if messageList[i].name == vars.name then + --env.info('updateMessage') + messageList[i].displayedFor = 0 + messageList[i].addedAt = timer.getTime() + messageList[i].sound = new.sound + messageList[i].text = new.text + messageList[i].msgFor = new.msgFor + messageList[i].multSound = new.multSound + messageList[i].update = true + return messageList[i].messageID + end + end + end + end + + messageID = messageID + 1 + new.messageID = messageID + + --mist.debug.writeData(mist.utils.serialize,{'msg', new}, 'newMsg.lua') + + + messageList[#messageList + 1] = new + + local mt = { __index = mist.message} + setmetatable(new, mt) + + if displayActive == false then + displayActive = true + displayFuncId = mist.scheduleFunction(mistdisplayV4, {}, timer.getTime() + messageDisplayRate, messageDisplayRate) + end + + return messageID + + end, + + remove = function(self) -- Now a self variable; the former functionality taken up by mist.message.removeById. + for i, msgData in pairs(messageList) do + if messageList[i] == self then + table.remove(messageList, i) + return true --removal successful + end + end + return false -- removal not successful this script fails at life! + end, + + removeById = function(id) -- This function is NOT passed a self variable; it is the remove by id function. + for i, msgData in pairs(messageList) do + if messageList[i].messageID == id then + table.remove(messageList, i) + return true --removal successful + end + end + return false -- removal not successful this script fails at life! + end, + } - if displayActive == false then - displayActive = true - displayFuncId = mist.scheduleFunction(mistdisplayV4, {}, timer.getTime() + messageDisplayRate, messageDisplayRate) - end - - return messageID - - end, - - remove = function(self) -- Now a self variable; the former functionality taken up by mist.message.removeById. - for i, msgData in pairs(messageList) do - if messageList[i] == self then - table.remove(messageList, i) - return true --removal successful - end - end - return false -- removal not successful this script fails at life! - end, - - removeById = function(id) -- This function is NOT passed a self variable; it is the remove by id function. - for i, msgData in pairs(messageList) do - if messageList[i].messageID == id then - table.remove(messageList, i) - return true --removal successful - end - end - return false -- removal not successful this script fails at life! - end, - } - end -- End of message system -------------------------------------------------------------------------------------------------------------------------------------------- @@ -4557,12 +4557,12 @@ vars.units - table of unit names (NOT unitNameTable- maybe this should change). vars.acc - integer between 0 and 5, inclusive ]] mist.getMGRSString = function(vars) - local units = vars.units - local acc = vars.acc or 5 - local avgPos = mist.getAvgPos(units) - if avgPos then - return mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(avgPos)), acc) - end + local units = vars.units + local acc = vars.acc or 5 + local avgPos = mist.getAvgPos(units) + if avgPos then + return mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(avgPos)), acc) + end end --[[ vars for mist.getLLString @@ -4573,14 +4573,14 @@ vars.DMS - if true, output in degrees, minutes, seconds. Otherwise, output in d ]] mist.getLLString = function(vars) - local units = vars.units - local acc = vars.acc or 3 - local DMS = vars.DMS - local avgPos = mist.getAvgPos(units) - if avgPos then - local lat, lon = coord.LOtoLL(avgPos) - return mist.tostringLL(lat, lon, acc, DMS) - end + local units = vars.units + local acc = vars.acc or 3 + local DMS = vars.DMS + local avgPos = mist.getAvgPos(units) + if avgPos then + local lat, lon = coord.LOtoLL(avgPos) + return mist.tostringLL(lat, lon, acc, DMS) + end end @@ -4591,20 +4591,20 @@ vars.alt - boolean, if used, includes altitude in string vars.metric - boolean, gives distance in km instead of NM. ]] mist.getBRString = function(vars) - local units = vars.units - local ref = mist.utils.makeVec3(vars.ref, 0) -- turn it into Vec3 if it is not already. - local alt = vars.alt - local metric = vars.metric - local avgPos = mist.getAvgPos(units) - if avgPos then - local vec = {x = avgPos.x - ref.x, y = avgPos.y - ref.y, z = avgPos.z - ref.z} - local dir = mist.utils.getDir(vec, ref) - local dist = mist.utils.get2DDist(avgPos, ref) - if alt then - alt = avgPos.y - end - return mist.tostringBR(dir, dist, alt, metric) - end + local units = vars.units + local ref = mist.utils.makeVec3(vars.ref, 0) -- turn it into Vec3 if it is not already. + local alt = vars.alt + local metric = vars.metric + local avgPos = mist.getAvgPos(units) + if avgPos then + local vec = {x = avgPos.x - ref.x, y = avgPos.y - ref.y, z = avgPos.z - ref.z} + local dir = mist.utils.getDir(vec, ref) + local dist = mist.utils.get2DDist(avgPos, ref) + if alt then + alt = avgPos.y + end + return mist.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. @@ -4615,53 +4615,53 @@ vars.radius - number vars.headingDegrees - boolean, switches heading to degrees ]] mist.getLeadingPos = function(vars) - local units = vars.units - local heading = vars.heading - local radius = vars.radius - if vars.headingDegrees then - heading = mist.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 = mist.vec.rotateVec2(mist.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 mist.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 + local units = vars.units + local heading = vars.heading + local radius = vars.radius + if vars.headingDegrees then + heading = mist.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 = mist.vec.rotateVec2(mist.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 mist.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 @@ -4673,11 +4673,11 @@ vars.headingDegrees - boolean, switches heading to degrees vars.acc - number, 0 to 5. ]] mist.getLeadingMGRSString = function(vars) - local pos = mist.getLeadingPos(vars) - if pos then - local acc = vars.acc or 5 - return mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(pos)), acc) - end + local pos = mist.getLeadingPos(vars) + if pos then + local acc = vars.acc or 5 + return mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(pos)), acc) + end end --[[ vars for mist.getLeadingLLString: @@ -4689,13 +4689,13 @@ vars.acc - number of digits after decimal point (can be negative) vars.DMS - boolean, true if you want DMS. ]] mist.getLeadingLLString = function(vars) - local pos = mist.getLeadingPos(vars) - if pos then - local acc = vars.acc or 3 - local DMS = vars.DMS - local lat, lon = coord.LOtoLL(pos) - return mist.tostringLL(lat, lon, acc, DMS) - end + local pos = mist.getLeadingPos(vars) + if pos then + local acc = vars.acc or 3 + local DMS = vars.DMS + local lat, lon = coord.LOtoLL(pos) + return mist.tostringLL(lat, lon, acc, DMS) + end end @@ -4710,26 +4710,26 @@ vars.alt - boolean, if true, include altitude. vars.ref - vec3/vec2 reference point. ]] mist.getLeadingBRString = function(vars) - local pos = mist.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 = mist.utils.getDir(vec, ref) - local dist = mist.utils.get2DDist(pos, ref) - if alt then - alt = pos.y - end - return mist.tostringBR(dir, dist, alt, metric) - end + local pos = mist.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 = mist.utils.getDir(vec, ref) + local dist = mist.utils.get2DDist(pos, ref) + if alt then + alt = pos.y + end + return mist.tostringBR(dir, dist, alt, metric) + end end --[[ vars for mist.message.add - vars.text = 'Hello World' - vars.displayTime = 20 - vars.msgFor = {coa = {'red'}, countries = {'Ukraine', 'Georgia'}, unitTypes = {'A-10C'}} + vars.text = 'Hello World' + vars.displayTime = 20 + vars.msgFor = {coa = {'red'}, countries = {'Ukraine', 'Georgia'}, unitTypes = {'A-10C'}} ]] @@ -4741,28 +4741,28 @@ vars.displayTime - self explanatory vars.msgFor - scope ]] mist.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 = mist.getMGRSString{units = units, acc = acc} - local newText - if text then - 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 - else - newText = s - end - mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } + local units = vars.units + local acc = vars.acc + local text = vars.text + local displayTime = vars.displayTime + local msgFor = vars.msgFor + + local s = mist.getMGRSString{units = units, acc = acc} + local newText + if text then + 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 +else + newText = s +end +mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor +} end --[[ vars for mist.msgLL @@ -4774,30 +4774,30 @@ vars.displayTime - self explanatory vars.msgFor - scope ]] mist.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 = mist.getLLString{units = units, acc = acc, DMS = DMS} - local newText - if text then - 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 - else - newText = s - end - - mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } + 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 = mist.getLLString{units = units, acc = acc, DMS = DMS} + local newText + if text then + 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 +else + newText = s +end + +mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor +} end @@ -4812,31 +4812,31 @@ vars.displayTime vars.msgFor - scope ]] mist.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 mist.getBRString - local alt = vars.alt - local metric = vars.metric - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor - - local s = mist.getBRString{units = units, ref = ref, alt = alt, metric = metric} - local newText - if text then - 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 - else - newText = s - end - - mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } + 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 mist.getBRString + local alt = vars.alt + local metric = vars.metric + local text = vars.text + local displayTime = vars.displayTime + local msgFor = vars.msgFor + + local s = mist.getBRString{units = units, ref = ref, alt = alt, metric = metric} + local newText + if text then + 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 +else + newText = s +end + +mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor +} end @@ -4853,13 +4853,13 @@ vars.displayTime vars.msgFor - scope ]] mist.msgBullseye = function(vars) - if string.lower(vars.ref) == 'red' then - vars.ref = mist.DBs.missionData.bullseye.red - mist.msgBR(vars) - elseif string.lower(vars.ref) == 'blue' then - vars.ref = mist.DBs.missionData.bullseye.blue - mist.msgBR(vars) - end + if string.lower(vars.ref) == 'red' then + vars.ref = mist.DBs.missionData.bullseye.red + mist.msgBR(vars) + elseif string.lower(vars.ref) == 'blue' then + vars.ref = mist.DBs.missionData.bullseye.blue + mist.msgBR(vars) + end end --[[ @@ -4873,13 +4873,13 @@ vars.msgFor - scope ]] mist.msgBRA = function(vars) - if Unit.getByName(vars.ref) and Unit.getByName(vars.ref):isExist() == true then - vars.ref = Unit.getByName(vars.ref):getPosition().p - if not vars.alt then - vars.alt = true - end - mist.msgBR(vars) - end + if Unit.getByName(vars.ref) and Unit.getByName(vars.ref):isExist() == true then + vars.ref = Unit.getByName(vars.ref):getPosition().p + if not vars.alt then + vars.alt = true + end + mist.msgBR(vars) + end end -------------------------------------------------------------------------------------------- @@ -4894,32 +4894,32 @@ vars.displayTime vars.msgFor - scope ]] mist.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 = mist.getLeadingMGRSString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc} - local newText - if text then - 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 - else - newText = s - end - - mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } + 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 = mist.getLeadingMGRSString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc} + local newText + if text then + 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 +else + newText = s +end + +mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor +} end @@ -4935,34 +4935,34 @@ vars.displayTime vars.msgFor - scope ]] mist.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 = mist.getLeadingLLString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc, DMS = DMS} - local newText - - if text then - 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 - else - newText = s - end - - mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } + 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 = mist.getLeadingLLString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc, DMS = DMS} + local newText + + if text then + 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 +else + newText = s +end + +mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor +} end @@ -4979,35 +4979,35 @@ vars.displayTime vars.msgFor - scope ]] mist.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 mist.getBRString - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor - - local s = mist.getLeadingBRString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, metric = metric, alt = alt, ref = ref} - local newText - - if text then - 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 - else - newText = s - end - - mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } + 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 mist.getBRString + local text = vars.text + local displayTime = vars.displayTime + local msgFor = vars.msgFor + + local s = mist.getLeadingBRString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, metric = metric, alt = alt, ref = ref} + local newText + + if text then + 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 +else + newText = s +end + +mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor +} end @@ -5018,1088 +5018,1088 @@ end -- start of sct Merge do -- all function uses of group and unit Ids must be in this do statement - - -mist.groupTableCheck = function(groupData) - local isOk = false - - if groupData.country then - isOk = true - end - if groupData.category then - isOk = true - else - isOk = false - end - if groupData.units then - for unitId, unitData in pairs(groupData.units) do - if unitData.x and unitData.y and unitData.type then - isOk = true - end - end - else - isOk = false - end - - return isOk -end - -mist.getCurrentGroupData = function(gpName) - local dbData = mist.getGroupData(gpName) - - if Group.getByName(gpName) and Group.getByName(gpName):isExist() == true then - local newGroup = Group.getByName(gpName) - local newData = {} - newData.name = gpName - newData.groupId = tonumber(newGroup:getID()) - newData.category = newGroup:getCategory() - newData.groupName = gpName - newData.hidden = dbData.hidden - - if newData.category == 2 then - newData.category = 'vehicle' - elseif newData.category == 3 then - newData.category = 'ship' - end - - newData.units = {} - local newUnits = newGroup:getUnits() - for unitNum, unitData in pairs(newGroup:getUnits()) do - newData.units[unitNum] = {} - newData.units[unitNum]["unitId"] = tonumber(unitData:getID()) - newData.units[unitNum]['point'] = unitData.point - newData.units[unitNum]['x'] = unitData:getPosition().p.x - newData.units[unitNum]['y'] = unitData:getPosition().p.z - newData.units[unitNum]["type"] = unitData:getTypeName() - newData.units[unitNum]["skill"] = mist.getUnitSkill(unitData:getName()) - - -- get velocity needed - newData.units[unitNum]["unitName"] = unitData:getName() - newData.units[unitNum]["heading"] = mist.getHeading(unitData, true) -- added to DBs - newData.units[unitNum]['alt'] = unitData:getPosition().p.y - newData.country = string.lower(country.name[unitData:getCountry()]) - newData.units[unitNum]['callsign'] = unitData:getCallsign() - end - - return newData - elseif StaticObject.getByName(gpName) and StaticObject.getByName(gpName):isExist() == true then - local staticObj = StaticObject.getByName(gpName) - dbData.units[1].x = staticObj:getPosition().p.x - dbData.units[1].y = staticObj:getPosition().p.z - dbData.units[1].alt = staticObj:getPosition().p.y - dbData.units[1].heading = mist.getHeading(staticObj, true) - - return dbData - end - -end - -mist.getGroupData = function(gpName) - local found = false - local newData = {} - if mist.DBs.groupsByName[gpName] then - newData = mist.utils.deepCopy(mist.DBs.groupsByName[gpName]) - found = true - end - - if found == false then - for groupName, groupData in pairs(mist.DBs.groupsByName) do - if mist.stringMatch(groupName, gpName) == true then - newData = mist.utils.deepCopy(groupData) - newData.groupName = groupName - found = true - break - end - end - end - - local payloads - if newData.category == 'plane' or newData.category == 'helicopter' then - payloads = mist.getGroupPayload(newData.groupName) - end - if found == true then - --newData.hidden = false -- maybe add this to DBs - - for unitNum, unitData in pairs(newData.units) do - newData.units[unitNum] = {} - - newData.units[unitNum]["unitId"] = unitData.unitId - --newData.units[unitNum]['point'] = unitData.point - newData.units[unitNum]['x'] = unitData.point.x - newData.units[unitNum]['y'] = unitData.point.y - newData.units[unitNum]['alt'] = unitData.alt - newData.units[unitNum]['alt_type'] = unitData.alt_type - newData.units[unitNum]['speed'] = unitData.speed - newData.units[unitNum]["type"] = unitData.type - newData.units[unitNum]["skill"] = unitData.skill - newData.units[unitNum]["unitName"] = unitData.unitName - newData.units[unitNum]["heading"] = unitData.heading -- added to DBs - newData.units[unitNum]["playerCanDrive"] = unitData.playerCanDrive -- added to DBs - - - if newData.category == 'plane' or newData.category == 'helicopter' then - newData.units[unitNum]["payload"] = payloads[unitNum] - newData.units[unitNum]['livery_id'] = unitData.livery_id - newData.units[unitNum]['onboard_num'] = unitData.onboard_num - newData.units[unitNum]['callsign'] = unitData.callsign - newData.units[unitNum]['AddPropAircraft'] = unitData.AddPropAircraft - end - if newData.category == 'static' then - newData.units[unitNum]['categoryStatic'] = unitData.categoryStatic - newData.units[unitNum]['mass'] = unitData.mass - newData.units[unitNum]['canCargo'] = unitData.canCargo - newData.units[unitNum]['shape_name'] = unitData.shape_name - end - end - - return newData - else - env.info(gpName .. ' not found in mist.getGroupData') - return - end -end - -mist.getPayload = function(unitIdent) - -- refactor to search by groupId and allow groupId and groupName as inputs - local unitId = unitIdent - if type(unitIdent) == 'string' and not tonumber(unitIdent) then - unitId = mist.DBs.MEunitsByName[unitIdent].unitId - end - local gpId = mist.DBs.MEunitsById[unitId].groupId - - if gpId and unitId then - 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 - for unitIndex, unitData in pairs(group_data.units) do --group index - if unitData.unitId == unitId then - return unitData.payload - end - end - end - end - end - end - end - end - end - end - end - else - env.info('mist.getPayload got ' .. type(unitIdent)) - return false - end - env.info('mist.getPayload, payload not found') - return -end - -mist.getGroupPayload = function(groupIdent) - local gpId = groupIdent - if type(groupIdent) == 'string' and not tonumber(groupIdent) then - gpId = mist.DBs.MEgroupsByName[groupIdent].groupId - end - - if gpId then - 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 - local payloads = {} - for unitIndex, unitData in pairs(group_data.units) do --group index - payloads[unitIndex] = unitData.payload - end - return payloads - end - end - end - end - end - end - end - end - end - else - env.info('mist.getGroupPayload got ' .. type(groupName)) - return false - end - env.info('mist.getGroupPayload, payload not found') - return - -end - -mist.teleportToPoint = function(vars) -- main teleport function that all of teleport/respawn functions call - local point = vars.point - - local gpName - if vars.gpName then - gpName = vars.gpName - elseif vars.groupName then - gpName = vars.groupName - else - env.info('teleportToPoint missing either vars.groupName or vars.gpName') - end - - local action = vars.action - - local disperse = vars.disperse or false - local maxDisp = vars.maxDisp - if not vars.maxDisp then - maxDisp = 200 - else - maxDisp = vars.maxDisp - end - local radius = vars.radius or 0 - local innerRadius = vars.innerRadius - - local route = vars.route - local dbData = false - - local newGroupData - if gpName and not vars.groupData then - if string.lower(action) == 'teleport' or string.lower(action) == 'tele' then - newGroupData = mist.getCurrentGroupData(gpName) - elseif string.lower(action) == 'respawn' then - newGroupData = mist.getGroupData(gpName) - dbData = true - elseif string.lower(action) == 'clone' then - newGroupData = mist.getGroupData(gpName) - newGroupData.clone = 'order66' - dbData = true - else - action = 'tele' - newGroupData = mist.getCurrentGroupData(gpName) - end - else - action = 'tele' - newGroupData = vars.groupData - end - - local diff = {['x'] = 0, ['y'] = 0} - local newCoord, origCoord - if point then - local valid = false - - local validTerrain - if string.lower(newGroupData.category) == 'ship' then - validTerrain = {'SHALLOW_WATER' , 'WATER'} - elseif string.lower(newGroupData.category) == 'vehicle' then - validTerrain = {'LAND', 'ROAD'} - else - validTerrain = {'LAND', 'ROAD', 'SHALLOW_WATER', 'WATER', 'RUNWAY'} - end - - for i = 1, 100 do - newCoord = mist.getRandPointInCircle(point, radius, innerRadius) - if mist.isTerrainValid(newCoord, validTerrain) then - origCoord = mist.utils.deepCopy(newCoord) - diff = {['x'] = (newCoord.x - newGroupData.units[1].x), ['y'] = (newCoord.y - newGroupData.units[1].y)} - valid = true - break - end - end - if valid == false then - env.info('mist.teleportToPoint; vars.point not a valid coordinate') - return false - end - end - if not newGroupData.country and mist.DBs.groupsByName[newGroupData.groupName].country then - newGroupData.country = mist.DBs.groupsByName[newGroupData.groupName].country - end - if not newGroupData.category and mist.DBs.groupsByName[newGroupData.groupName].category then - newGroupData.category = mist.DBs.groupsByName[newGroupData.groupName].category - end - - for unitNum, unitData in pairs(newGroupData.units) do - if disperse then - if maxDisp and type(maxDisp) == 'number' and unitNum ~= 1 then - newCoord = mist.getRandPointInCircle(origCoord, maxDisp) - --else - --newCoord = mist.getRandPointInCircle(zone.point, zone.radius) - end - - newGroupData.units[unitNum]['x'] = newCoord.x - newGroupData.units[unitNum]['y'] = newCoord.y - else - newGroupData.units[unitNum]["x"] = unitData.x + diff.x - newGroupData.units[unitNum]["y"] = unitData.y + diff.y - end - if point then - if (newGroupData.category == 'plane' or newGroupData.category == 'helicopter') then - if point.z and point.y > 0 and point.y > land.getHeight({newGroupData.units[unitNum]["x"], newGroupData.units[unitNum]["y"]}) + 10 then - newGroupData.units[unitNum]["alt"] = point.y - else - if newGroupData.category == 'plane' then - newGroupData.units[unitNum]["alt"] = land.getHeight({newGroupData.units[unitNum]["x"], newGroupData.units[unitNum]["y"]}) + math.random(300, 9000) - else - newGroupData.units[unitNum]["alt"] = land.getHeight({newGroupData.units[unitNum]["x"], newGroupData.units[unitNum]["y"]}) + math.random(200, 3000) - end - end - end - end - end - - if newGroupData.start_time then - newGroupData.startTime = newGroupData.start_time - end - - if newGroupData.startTime and newGroupData.startTime ~= 0 and dbData == true then - local timeDif = timer.getAbsTime() - timer.getTime0() - if timeDif > newGroupData.startTime then - newGroupData.startTime = 0 - else - newGroupData.startTime = newGroupData.startTime - timeDif - end - - end - - if route then - newGroupData.route = route - end - --mist.debug.writeData(mist.utils.serialize,{'teleportToPoint', newGroupData}, 'newGroupData.lua') - if string.lower(newGroupData.category) == 'static' then - return mist.dynAddStatic(newGroupData) - end - return mist.dynAdd(newGroupData) - -end - -mist.respawnInZone = function(gpName, zone, disperse, maxDisp) - - if type(gpName) == 'table' and gpName:getName() then - gpName = gpName:getName() - elseif type(gpName) == 'table' and gpName[1]:getName() then - gpName = math.random(#gpName) - else - gpName = tostring(gpName) - 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 - local vars = {} - vars.gpName = gpName - vars.action = 'respawn' - vars.point = zone.point - vars.radius = zone.radius - vars.disperse = disperse - vars.maxDisp = maxDisp - return mist.teleportToPoint(vars) -end - -mist.cloneInZone = function(gpName, zone, disperse, maxDisp) - - if type(gpName) == 'table' then - gpName = gpName:getName() - else - gpName = tostring(gpName) - 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 - local vars = {} - vars.gpName = gpName - vars.action = 'clone' - vars.point = zone.point - vars.radius = zone.radius - vars.disperse = disperse - vars.maxDisp = maxDisp - return mist.teleportToPoint(vars) -end - -mist.teleportInZone = function(gpName, zone, disperse, maxDisp) -- groupName, zoneName or table of Zone Names, keepForm is a boolean - if type(gpName) == 'table' and gpName:getName() then - gpName = gpName:getName() - else - gpName = tostring(gpName) - 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 - - local vars = {} - vars.gpName = gpName - vars.action = 'tele' - vars.point = zone.point - vars.radius = zone.radius - vars.disperse = disperse - vars.maxDisp = maxDisp - return mist.teleportToPoint(vars) -end - -mist.respawnGroup = function(gpName, task) - local vars = {} - vars.gpName = gpName - vars.action = 'respawn' - if task and type(task) ~= 'number' then - vars.route = mist.getGroupRoute(gpName, 'task') - end - local newGroup = mist.teleportToPoint(vars) - if task and type(task) == 'number' then - local newRoute = mist.getGroupRoute(gpName, 'task') - mist.scheduleFunction(mist.goRoute, {newGroup, newRoute}, timer.getTime() + task) - end - return newGroup -end - -mist.cloneGroup = function(gpName, task) - local vars = {} - vars.gpName = gpName - vars.action = 'clone' - if task and type(task) ~= 'number' then - vars.route = mist.getGroupRoute(gpName, 'task') - end - local newGroup = mist.teleportToPoint(vars) - if task and type(task) == 'number' then - local newRoute = mist.getGroupRoute(gpName, 'task') - mist.scheduleFunction(mist.goRoute, {newGroup, newRoute}, timer.getTime() + task) - end - return newGroup -end - -mist.teleportGroup = function(gpName, task) - local vars = {} - vars.gpName = gpName - vars.action = 'teleport' - if task and type(task) ~= 'number' then - vars.route = mist.getGroupRoute(gpName, 'task') - end - local newGroup = mist.teleportToPoint(vars) - if task and type(task) == 'number' then - local newRoute = mist.getGroupRoute(gpName, 'task') - mist.scheduleFunction(mist.goRoute, {newGroup, newRoute}, timer.getTime() + task) - end - return newGroup -end - -mist.spawnRandomizedGroup = function(groupName, vars) -- need to debug - if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then - local gpData = mist.getGroupData(groupName) - gpData.units = mist.randomizeGroupOrder(gpData.units, vars) - gpData.route = mist.getGroupRoute(groupName, 'task') - - mist.dynAdd(gpData) - end - - return true -end - -mist.randomizeNumTable = function(vars) - local newTable = {} - - local excludeIndex = {} - local randomTable = {} - - if vars and vars.exclude and type(vars.exclude) == 'table' then - for index, data in pairs(vars.exclude) do - excludeIndex[data] = true - end - end - - local low, hi, size - - if vars.size then - size = vars.size - end - - if vars and vars.lowerLimit and type(vars.lowerLimit) == 'number' then - low = mist.utils.round(vars.lowerLimit) - else - low = 1 - end - - if vars and vars.upperLimit and type(vars.upperLimit) == 'number' then - hi = mist.utils.round(vars.upperLimit) - else - hi = size - end - - local choices = {} - -- add to exclude list and create list of what to randomize - for i = 1, size do - if not (i >= low and i <= hi) then - - excludeIndex[i] = true - end - if not excludeIndex[i] then - table.insert(choices, i) - else - newTable[i] = i - end - end - - for ind, num in pairs(choices) do - local found = false - local x = 0 - while found == false do - x = mist.random(size) -- get random number from list - local addNew = true - for index, _ in pairs(excludeIndex) do - if index == x then - addNew = false - break - end - end - if addNew == true then - excludeIndex[x] = true - found = true - end - excludeIndex[x] = true - - end - newTable[num] = x - end - --[[ - for i = 1, #newTable do - env.info(newTable[i]) - end - ]] - return newTable -end - -mist.randomizeGroupOrder = function(passedUnits, vars) - -- figure out what to exclude, and send data to other func - local units = passedUnits - - if passedUnits.units then - units = passUnits.units - end - - local exclude = {} - local excludeNum = {} - if vars and vars.excludeType and type(vars.excludeType) == 'table' then - exclude = vars.excludeType - end - - if vars and vars.excludeNum and type(vars.excludeNum) == 'table' then - excludeNum = vars.excludeNum - end - - local low, hi - - if vars and vars.lowerLimit and type(vars.lowerLimit) == 'number' then - low = mist.utils.round(vars.lowerLimit) - else - low = 1 - end - - if vars and vars.upperLimit and type(vars.upperLimit) == 'number' then - hi = mist.utils.round(vars.upperLimit) - else - hi = #units - end - - - local excludeNum = {} - for unitIndex, unitData in pairs(units) do - if unitIndex >= low and unitIndex <= hi then -- if within range - local found = false - if #exclude > 0 then - for excludeType, index in pairs(exclude) do -- check if excluded - if mist.stringMatch(excludeType, unitData.type) then -- if excluded - excludeNum[unitIndex] = unitIndex - found = true - end - end - end - else -- unitIndex is either to low, or to high: added to exclude list - excludeNum[unitIndex] = unitId - end - end - - local newGroup = {} - local newOrder = mist.randomizeNumTable({exclude = excludeNum, size = #units}) - - for unitIndex, unitData in pairs(units) do - for i = 1, #newOrder do - if newOrder[i] == unitIndex then - newGroup[i] = mist.utils.deepCopy(units[i]) -- gets all of the unit data - newGroup[i].type = mist.utils.deepCopy(unitData.type) - newGroup[i].skill = mist.utils.deepCopy(unitData.skill) - newGroup[i].unitName = mist.utils.deepCopy(unitData.unitName) - newGroup[i].unitIndex = mist.utils.deepCopy(unitData.unitIndex) -- replaces the units data with a new type - end - end - end - return newGroup -end - -mist.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 = mist.getGroupRoute(useGroupRoute) - end - else - useRoute = vars.route - local posStart = mist.getLeadPos(gpData) - useRoute[1] = mist.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 = mist.getLeadPos(gpData) - - - useRoute[#useRoute + 1] = mist.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] = mist.ground.buildWP(tempRoute[i], tempForm, tempSpeed) - end - - if pType and string.lower(pType) == 'doubleback' then - local curRoute = mist.utils.deepCopy(useRoute) - for i = #curRoute, 2, -1 do - useRoute[#useRoute + 1] = mist.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] = 'mist.ground.patrolRoute(' - cTask3[#cTask3 + 1] = mist.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 - mist.goRoute(gpData, useRoute) - - return -end - -mist.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 - - mist.ground.patrolRoute(vars) - - return -end -mist.random = function(firstNum, secondNum) -- no support for decimals - local lowNum, highNum - if not secondNum then - highNum = firstNum - lowNum = 1 - else - lowNum = firstNum - highNum = secondNum - end - local total = 1 - if math.abs(highNum - lowNum + 1) < 50 then -- if total values is less than 50 - total = math.modf(50/math.abs(highNum - lowNum + 1)) -- make x copies required to be above 50 - end - local choices = {} - for i = 1, total do -- iterate required number of times - for x = lowNum, highNum do -- iterate between the range - choices[#choices +1] = x -- add each entry to a table - end - end - local rtnVal = math.random(#choices) -- will now do a math.random of at least 50 choices - for i = 1, 10 do - rtnVal = math.random(#choices) -- iterate a few times for giggles - end - return choices[rtnVal] -end + mist.groupTableCheck = function(groupData) + local isOk = false + + if groupData.country then + isOk = true + end + if groupData.category then + isOk = true + else + isOk = false + end + if groupData.units then + for unitId, unitData in pairs(groupData.units) do + if unitData.x and unitData.y and unitData.type then + isOk = true + end + end + else + isOk = false + end + + return isOk + end + + mist.getCurrentGroupData = function(gpName) + local dbData = mist.getGroupData(gpName) + + if Group.getByName(gpName) and Group.getByName(gpName):isExist() == true then + local newGroup = Group.getByName(gpName) + local newData = {} + newData.name = gpName + newData.groupId = tonumber(newGroup:getID()) + newData.category = newGroup:getCategory() + newData.groupName = gpName + newData.hidden = dbData.hidden + + if newData.category == 2 then + newData.category = 'vehicle' + elseif newData.category == 3 then + newData.category = 'ship' + end + + newData.units = {} + local newUnits = newGroup:getUnits() + for unitNum, unitData in pairs(newGroup:getUnits()) do + newData.units[unitNum] = {} + newData.units[unitNum]["unitId"] = tonumber(unitData:getID()) + newData.units[unitNum]['point'] = unitData.point + newData.units[unitNum]['x'] = unitData:getPosition().p.x + newData.units[unitNum]['y'] = unitData:getPosition().p.z + newData.units[unitNum]["type"] = unitData:getTypeName() + newData.units[unitNum]["skill"] = mist.getUnitSkill(unitData:getName()) + + -- get velocity needed + newData.units[unitNum]["unitName"] = unitData:getName() + newData.units[unitNum]["heading"] = mist.getHeading(unitData, true) -- added to DBs + newData.units[unitNum]['alt'] = unitData:getPosition().p.y + newData.country = string.lower(country.name[unitData:getCountry()]) + newData.units[unitNum]['callsign'] = unitData:getCallsign() + end + + return newData + elseif StaticObject.getByName(gpName) and StaticObject.getByName(gpName):isExist() == true then + local staticObj = StaticObject.getByName(gpName) + dbData.units[1].x = staticObj:getPosition().p.x + dbData.units[1].y = staticObj:getPosition().p.z + dbData.units[1].alt = staticObj:getPosition().p.y + dbData.units[1].heading = mist.getHeading(staticObj, true) + + return dbData + end + + end + + mist.getGroupData = function(gpName) + local found = false + local newData = {} + if mist.DBs.groupsByName[gpName] then + newData = mist.utils.deepCopy(mist.DBs.groupsByName[gpName]) + found = true + end + + if found == false then + for groupName, groupData in pairs(mist.DBs.groupsByName) do + if mist.stringMatch(groupName, gpName) == true then + newData = mist.utils.deepCopy(groupData) + newData.groupName = groupName + found = true + break + end + end + end + + local payloads + if newData.category == 'plane' or newData.category == 'helicopter' then + payloads = mist.getGroupPayload(newData.groupName) + end + if found == true then + --newData.hidden = false -- maybe add this to DBs + + for unitNum, unitData in pairs(newData.units) do + newData.units[unitNum] = {} + + newData.units[unitNum]["unitId"] = unitData.unitId + --newData.units[unitNum]['point'] = unitData.point + newData.units[unitNum]['x'] = unitData.point.x + newData.units[unitNum]['y'] = unitData.point.y + newData.units[unitNum]['alt'] = unitData.alt + newData.units[unitNum]['alt_type'] = unitData.alt_type + newData.units[unitNum]['speed'] = unitData.speed + newData.units[unitNum]["type"] = unitData.type + newData.units[unitNum]["skill"] = unitData.skill + newData.units[unitNum]["unitName"] = unitData.unitName + newData.units[unitNum]["heading"] = unitData.heading -- added to DBs + newData.units[unitNum]["playerCanDrive"] = unitData.playerCanDrive -- added to DBs + + + if newData.category == 'plane' or newData.category == 'helicopter' then + newData.units[unitNum]["payload"] = payloads[unitNum] + newData.units[unitNum]['livery_id'] = unitData.livery_id + newData.units[unitNum]['onboard_num'] = unitData.onboard_num + newData.units[unitNum]['callsign'] = unitData.callsign + newData.units[unitNum]['AddPropAircraft'] = unitData.AddPropAircraft + end + if newData.category == 'static' then + newData.units[unitNum]['categoryStatic'] = unitData.categoryStatic + newData.units[unitNum]['mass'] = unitData.mass + newData.units[unitNum]['canCargo'] = unitData.canCargo + newData.units[unitNum]['shape_name'] = unitData.shape_name + end + end + + return newData + else + env.info(gpName .. ' not found in mist.getGroupData') + return + end + end + + mist.getPayload = function(unitIdent) + -- refactor to search by groupId and allow groupId and groupName as inputs + local unitId = unitIdent + if type(unitIdent) == 'string' and not tonumber(unitIdent) then + unitId = mist.DBs.MEunitsByName[unitIdent].unitId + end + local gpId = mist.DBs.MEunitsById[unitId].groupId + + if gpId and unitId then + 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 + for unitIndex, unitData in pairs(group_data.units) do --group index + if unitData.unitId == unitId then + return unitData.payload + end + end + end + end + end + end + end + end + end + end + end + else + env.info('mist.getPayload got ' .. type(unitIdent)) + return false + end + env.info('mist.getPayload, payload not found') + return + end + + mist.getGroupPayload = function(groupIdent) + local gpId = groupIdent + if type(groupIdent) == 'string' and not tonumber(groupIdent) then + gpId = mist.DBs.MEgroupsByName[groupIdent].groupId + end + + if gpId then + 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 + local payloads = {} + for unitIndex, unitData in pairs(group_data.units) do --group index + payloads[unitIndex] = unitData.payload + end + return payloads + end + end + end + end + end + end + end + end + end + else + env.info('mist.getGroupPayload got ' .. type(groupName)) + return false + end + env.info('mist.getGroupPayload, payload not found') + return + + end + + mist.teleportToPoint = function(vars) -- main teleport function that all of teleport/respawn functions call + local point = vars.point + + local gpName + if vars.gpName then + gpName = vars.gpName + elseif vars.groupName then + gpName = vars.groupName + else + env.info('teleportToPoint missing either vars.groupName or vars.gpName') + end + + local action = vars.action + + local disperse = vars.disperse or false + local maxDisp = vars.maxDisp + if not vars.maxDisp then + maxDisp = 200 + else + maxDisp = vars.maxDisp + end + local radius = vars.radius or 0 + local innerRadius = vars.innerRadius + + local route = vars.route + local dbData = false + + local newGroupData + if gpName and not vars.groupData then + if string.lower(action) == 'teleport' or string.lower(action) == 'tele' then + newGroupData = mist.getCurrentGroupData(gpName) + elseif string.lower(action) == 'respawn' then + newGroupData = mist.getGroupData(gpName) + dbData = true + elseif string.lower(action) == 'clone' then + newGroupData = mist.getGroupData(gpName) + newGroupData.clone = 'order66' + dbData = true + else + action = 'tele' + newGroupData = mist.getCurrentGroupData(gpName) + end + else + action = 'tele' + newGroupData = vars.groupData + end + + local diff = {['x'] = 0, ['y'] = 0} + local newCoord, origCoord + if point then + local valid = false + + local validTerrain + if string.lower(newGroupData.category) == 'ship' then + validTerrain = {'SHALLOW_WATER' , 'WATER'} + elseif string.lower(newGroupData.category) == 'vehicle' then + validTerrain = {'LAND', 'ROAD'} + else + validTerrain = {'LAND', 'ROAD', 'SHALLOW_WATER', 'WATER', 'RUNWAY'} + end + + for i = 1, 100 do + newCoord = mist.getRandPointInCircle(point, radius, innerRadius) + if mist.isTerrainValid(newCoord, validTerrain) then + origCoord = mist.utils.deepCopy(newCoord) + diff = {['x'] = (newCoord.x - newGroupData.units[1].x), ['y'] = (newCoord.y - newGroupData.units[1].y)} + valid = true + break + end + end + if valid == false then + env.info('mist.teleportToPoint; vars.point not a valid coordinate') + return false + end + end + if not newGroupData.country and mist.DBs.groupsByName[newGroupData.groupName].country then + newGroupData.country = mist.DBs.groupsByName[newGroupData.groupName].country + end + if not newGroupData.category and mist.DBs.groupsByName[newGroupData.groupName].category then + newGroupData.category = mist.DBs.groupsByName[newGroupData.groupName].category + end + + for unitNum, unitData in pairs(newGroupData.units) do + if disperse then + if maxDisp and type(maxDisp) == 'number' and unitNum ~= 1 then + newCoord = mist.getRandPointInCircle(origCoord, maxDisp) + --else + --newCoord = mist.getRandPointInCircle(zone.point, zone.radius) + end + + newGroupData.units[unitNum]['x'] = newCoord.x + newGroupData.units[unitNum]['y'] = newCoord.y + else + newGroupData.units[unitNum]["x"] = unitData.x + diff.x + newGroupData.units[unitNum]["y"] = unitData.y + diff.y + end + if point then + if (newGroupData.category == 'plane' or newGroupData.category == 'helicopter') then + if point.z and point.y > 0 and point.y > land.getHeight({newGroupData.units[unitNum]["x"], newGroupData.units[unitNum]["y"]}) + 10 then + newGroupData.units[unitNum]["alt"] = point.y + else + if newGroupData.category == 'plane' then + newGroupData.units[unitNum]["alt"] = land.getHeight({newGroupData.units[unitNum]["x"], newGroupData.units[unitNum]["y"]}) + math.random(300, 9000) + else + newGroupData.units[unitNum]["alt"] = land.getHeight({newGroupData.units[unitNum]["x"], newGroupData.units[unitNum]["y"]}) + math.random(200, 3000) + end + end + end + end + end + + if newGroupData.start_time then + newGroupData.startTime = newGroupData.start_time + end + + if newGroupData.startTime and newGroupData.startTime ~= 0 and dbData == true then + local timeDif = timer.getAbsTime() - timer.getTime0() + if timeDif > newGroupData.startTime then + newGroupData.startTime = 0 + else + newGroupData.startTime = newGroupData.startTime - timeDif + end + + end + + if route then + newGroupData.route = route + end + --mist.debug.writeData(mist.utils.serialize,{'teleportToPoint', newGroupData}, 'newGroupData.lua') + if string.lower(newGroupData.category) == 'static' then + return mist.dynAddStatic(newGroupData) + end + return mist.dynAdd(newGroupData) + + end + + mist.respawnInZone = function(gpName, zone, disperse, maxDisp) + + if type(gpName) == 'table' and gpName:getName() then + gpName = gpName:getName() + elseif type(gpName) == 'table' and gpName[1]:getName() then + gpName = math.random(#gpName) + else + gpName = tostring(gpName) + 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 + local vars = {} + vars.gpName = gpName + vars.action = 'respawn' + vars.point = zone.point + vars.radius = zone.radius + vars.disperse = disperse + vars.maxDisp = maxDisp + return mist.teleportToPoint(vars) + end + + mist.cloneInZone = function(gpName, zone, disperse, maxDisp) + + if type(gpName) == 'table' then + gpName = gpName:getName() + else + gpName = tostring(gpName) + 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 + local vars = {} + vars.gpName = gpName + vars.action = 'clone' + vars.point = zone.point + vars.radius = zone.radius + vars.disperse = disperse + vars.maxDisp = maxDisp + return mist.teleportToPoint(vars) + end + + mist.teleportInZone = function(gpName, zone, disperse, maxDisp) -- groupName, zoneName or table of Zone Names, keepForm is a boolean + if type(gpName) == 'table' and gpName:getName() then + gpName = gpName:getName() + else + gpName = tostring(gpName) + 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 + + local vars = {} + vars.gpName = gpName + vars.action = 'tele' + vars.point = zone.point + vars.radius = zone.radius + vars.disperse = disperse + vars.maxDisp = maxDisp + return mist.teleportToPoint(vars) + end + + mist.respawnGroup = function(gpName, task) + local vars = {} + vars.gpName = gpName + vars.action = 'respawn' + if task and type(task) ~= 'number' then + vars.route = mist.getGroupRoute(gpName, 'task') + end + local newGroup = mist.teleportToPoint(vars) + if task and type(task) == 'number' then + local newRoute = mist.getGroupRoute(gpName, 'task') + mist.scheduleFunction(mist.goRoute, {newGroup, newRoute}, timer.getTime() + task) + end + return newGroup + end + + mist.cloneGroup = function(gpName, task) + local vars = {} + vars.gpName = gpName + vars.action = 'clone' + if task and type(task) ~= 'number' then + vars.route = mist.getGroupRoute(gpName, 'task') + end + local newGroup = mist.teleportToPoint(vars) + if task and type(task) == 'number' then + local newRoute = mist.getGroupRoute(gpName, 'task') + mist.scheduleFunction(mist.goRoute, {newGroup, newRoute}, timer.getTime() + task) + end + return newGroup + end + + mist.teleportGroup = function(gpName, task) + local vars = {} + vars.gpName = gpName + vars.action = 'teleport' + if task and type(task) ~= 'number' then + vars.route = mist.getGroupRoute(gpName, 'task') + end + local newGroup = mist.teleportToPoint(vars) + if task and type(task) == 'number' then + local newRoute = mist.getGroupRoute(gpName, 'task') + mist.scheduleFunction(mist.goRoute, {newGroup, newRoute}, timer.getTime() + task) + end + return newGroup + end + + mist.spawnRandomizedGroup = function(groupName, vars) -- need to debug + if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then + local gpData = mist.getGroupData(groupName) + gpData.units = mist.randomizeGroupOrder(gpData.units, vars) + gpData.route = mist.getGroupRoute(groupName, 'task') + + mist.dynAdd(gpData) + end + + return true + end + + mist.randomizeNumTable = function(vars) + local newTable = {} + + local excludeIndex = {} + local randomTable = {} + + if vars and vars.exclude and type(vars.exclude) == 'table' then + for index, data in pairs(vars.exclude) do + excludeIndex[data] = true + end + end + + local low, hi, size + + if vars.size then + size = vars.size + end + + if vars and vars.lowerLimit and type(vars.lowerLimit) == 'number' then + low = mist.utils.round(vars.lowerLimit) + else + low = 1 + end + + if vars and vars.upperLimit and type(vars.upperLimit) == 'number' then + hi = mist.utils.round(vars.upperLimit) + else + hi = size + end + + local choices = {} + -- add to exclude list and create list of what to randomize + for i = 1, size do + if not (i >= low and i <= hi) then + + excludeIndex[i] = true + end + if not excludeIndex[i] then + table.insert(choices, i) + else + newTable[i] = i + end + end + + for ind, num in pairs(choices) do + local found = false + local x = 0 + while found == false do + x = mist.random(size) -- get random number from list + local addNew = true + for index, _ in pairs(excludeIndex) do + if index == x then + addNew = false + break + end + end + if addNew == true then + excludeIndex[x] = true + found = true + end + excludeIndex[x] = true + + end + newTable[num] = x + end + --[[ + for i = 1, #newTable do + env.info(newTable[i]) + end + ]] + return newTable + end + + mist.randomizeGroupOrder = function(passedUnits, vars) + -- figure out what to exclude, and send data to other func + local units = passedUnits + + if passedUnits.units then + units = passUnits.units + end + + local exclude = {} + local excludeNum = {} + if vars and vars.excludeType and type(vars.excludeType) == 'table' then + exclude = vars.excludeType + end + + if vars and vars.excludeNum and type(vars.excludeNum) == 'table' then + excludeNum = vars.excludeNum + end + + local low, hi + + if vars and vars.lowerLimit and type(vars.lowerLimit) == 'number' then + low = mist.utils.round(vars.lowerLimit) + else + low = 1 + end + + if vars and vars.upperLimit and type(vars.upperLimit) == 'number' then + hi = mist.utils.round(vars.upperLimit) + else + hi = #units + end + + + local excludeNum = {} + for unitIndex, unitData in pairs(units) do + if unitIndex >= low and unitIndex <= hi then -- if within range + local found = false + if #exclude > 0 then + for excludeType, index in pairs(exclude) do -- check if excluded + if mist.stringMatch(excludeType, unitData.type) then -- if excluded + excludeNum[unitIndex] = unitIndex + found = true + end + end + end + else -- unitIndex is either to low, or to high: added to exclude list + excludeNum[unitIndex] = unitId + end + end + + local newGroup = {} + local newOrder = mist.randomizeNumTable({exclude = excludeNum, size = #units}) + + for unitIndex, unitData in pairs(units) do + for i = 1, #newOrder do + if newOrder[i] == unitIndex then + newGroup[i] = mist.utils.deepCopy(units[i]) -- gets all of the unit data + newGroup[i].type = mist.utils.deepCopy(unitData.type) + newGroup[i].skill = mist.utils.deepCopy(unitData.skill) + newGroup[i].unitName = mist.utils.deepCopy(unitData.unitName) + newGroup[i].unitIndex = mist.utils.deepCopy(unitData.unitIndex) -- replaces the units data with a new type + end + end + end + return newGroup + end + + mist.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 = mist.getGroupRoute(useGroupRoute) + end + else + useRoute = vars.route + local posStart = mist.getLeadPos(gpData) + useRoute[1] = mist.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 = mist.getLeadPos(gpData) + + + useRoute[#useRoute + 1] = mist.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] = mist.ground.buildWP(tempRoute[i], tempForm, tempSpeed) + end + + if pType and string.lower(pType) == 'doubleback' then + local curRoute = mist.utils.deepCopy(useRoute) + for i = #curRoute, 2, -1 do + useRoute[#useRoute + 1] = mist.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] = 'mist.ground.patrolRoute(' + cTask3[#cTask3 + 1] = mist.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 + mist.goRoute(gpData, useRoute) + + return + end + + mist.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 + + mist.ground.patrolRoute(vars) + + return + end + + + mist.random = function(firstNum, secondNum) -- no support for decimals + local lowNum, highNum + if not secondNum then + highNum = firstNum + lowNum = 1 + else + lowNum = firstNum + highNum = secondNum + end + local total = 1 + if math.abs(highNum - lowNum + 1) < 50 then -- if total values is less than 50 + total = math.modf(50/math.abs(highNum - lowNum + 1)) -- make x copies required to be above 50 + end + local choices = {} + for i = 1, total do -- iterate required number of times + for x = lowNum, highNum do -- iterate between the range + choices[#choices +1] = x -- add each entry to a table + end + end + local rtnVal = math.random(#choices) -- will now do a math.random of at least 50 choices + for i = 1, 10 do + rtnVal = math.random(#choices) -- iterate a few times for giggles + end + return choices[rtnVal] + end -mist.stringMatch = function(s1, s2, bool) - local exclude = {'%-', '%(', '%)', '%_', '%[', '%]', '%.', '%#', '% ', '%{', '%}', '%$', '%%', '%?', '%+', '%^'} - if type(s1) == 'string' and type(s2) == 'string' then - for i , str in pairs(exclude) do - s1 = string.gsub(s1, str, '') - s2 = string.gsub(s2, str, '') - end - if not bool then - s1 = string.lower(s1) - s2 = string.lower(s2) - end - - if s1 == s2 then - return true - else - return false - end - else - env.info('mist.stringMatch; Either the first or second variable were not a string') - return false - end -end + mist.stringMatch = function(s1, s2, bool) + local exclude = {'%-', '%(', '%)', '%_', '%[', '%]', '%.', '%#', '% ', '%{', '%}', '%$', '%%', '%?', '%+', '%^'} + if type(s1) == 'string' and type(s2) == 'string' then + for i , str in pairs(exclude) do + s1 = string.gsub(s1, str, '') + s2 = string.gsub(s2, str, '') + end + if not bool then + s1 = string.lower(s1) + s2 = string.lower(s2) + end -mist.matchString = mist.stringMatch -- both commands work because order out type of I + if s1 == s2 then + return true + else + return false + end + else + env.info('mist.stringMatch; Either the first or second variable were not a string') + return false + end + end -mist.time = {} --- returns a string for specified military time --- theTime is optional --- if present current time in mil time is returned --- if number or table the time is converted into mil tim -mist.time.convertToSec = function(timeTable) - - timeInSec = 0 - if timeTable and type(timeTable) == 'number' then - timeInSec = timeTable - elseif timeTable and type(timeTable) == 'table' and (timeTable.d or timeTable.h or timeTable.m or timeTable.s) then - if timeTable.d and type(timeTable.d) == 'number' then - timeInSec = timeInSec + (timeTable.d*86400) - end - if timeTable.h and type(timeTable.h) == 'number' then - timeInSec = timeInSec + (timeTable.h*3600) - end - if timeTable.m and type(timeTable.m) == 'number' then - timeInSec = timeInSec + (timeTable.m*60) - end - if timeTable.s and type(timeTable.s) == 'number' then - timeInSec = timeInSec + timeTable.s - end - - end - return timeInSec -end + mist.matchString = mist.stringMatch -- both commands work because order out type of I -mist.time.getDHMS = function(timeInSec) - if timeInSec and type(timeInSec) == 'number' then - local tbl = {d = 0, h = 0, m = 0, s = 0} - if timeInSec > 86400 then - while timeInSec > 86400 do - tbl.d = tbl.d + 1 - timeInSec = timeInSec - 86400 - end - end - if timeInSec > 3600 then - while timeInSec > 3600 do - tbl.h = tbl.h + 1 - timeInSec = timeInSec - 3600 - end - end - if timeInSec > 60 then - while timeInSec > 60 do - tbl.m = tbl.m + 1 - timeInSec = timeInSec - 60 - end - end - tbl.s = timeInSec - return tbl - else - env.info('mist.time.getDHMS didnt recieve number') - return - end -end + mist.time = {} + -- returns a string for specified military time + -- theTime is optional + -- if present current time in mil time is returned + -- if number or table the time is converted into mil tim + mist.time.convertToSec = function(timeTable) -mist.getMilString = function(theTime) - local timeInSec = 0 - if theTime then - timeInSec = mist.time.convertToSec(theTime) - else - timeInSec = mist.utils.round(timer.getAbsTime(), 0) - end - - local DHMS = mist.time.getDHMS(timeInSec) - - return tostring(string.format('%02d', DHMS.h) .. string.format('%02d',DHMS.m)) -end + timeInSec = 0 + if timeTable and type(timeTable) == 'number' then + timeInSec = timeTable + elseif timeTable and type(timeTable) == 'table' and (timeTable.d or timeTable.h or timeTable.m or timeTable.s) then + if timeTable.d and type(timeTable.d) == 'number' then + timeInSec = timeInSec + (timeTable.d*86400) + end + if timeTable.h and type(timeTable.h) == 'number' then + timeInSec = timeInSec + (timeTable.h*3600) + end + if timeTable.m and type(timeTable.m) == 'number' then + timeInSec = timeInSec + (timeTable.m*60) + end + if timeTable.s and type(timeTable.s) == 'number' then + timeInSec = timeInSec + timeTable.s + end -mist.getClockString = function(theTime, hour) - local timeInSec = 0 - if theTime then - timeInSec = mist.time.convertToSec(theTime) - else - timeInSec = mist.utils.round(timer.getAbsTime(), 0) - end - local DHMS = mist.time.getDHMS(timeInSec) - if hour then - if DHMS.h > 12 then - DHMS.h = DHMS.h - 12 - return tostring(string.format('%02d', DHMS.h) .. ':' .. string.format('%02d',DHMS.m) .. ':' .. string.format('%02d',DHMS.s) .. ' PM') - else - return tostring(string.format('%02d', DHMS.h) .. ':' .. string.format('%02d',DHMS.m) .. ':' .. string.format('%02d',DHMS.s) .. ' AM') - end - else - return tostring(string.format('%02d', DHMS.h) .. ':' .. string.format('%02d',DHMS.m) .. ':' .. string.format('%02d',DHMS.s)) - end -end + end + return timeInSec + end --- returns the date in string format --- both variables optional --- first val returns with the month as a string --- 2nd val defins if it should be written the American way or the wrong way. -mist.time.getDate = function(convert) - local cal = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} -- starts at june. 2011. 2012 is leap year, sigh. add a simple check for leap year - local date = {} - date.d = 0 - date.m = 6 - date.y = 2011 + mist.time.getDHMS = function(timeInSec) + if timeInSec and type(timeInSec) == 'number' then + local tbl = {d = 0, h = 0, m = 0, s = 0} + if timeInSec > 86400 then + while timeInSec > 86400 do + tbl.d = tbl.d + 1 + timeInSec = timeInSec - 86400 + end + end + if timeInSec > 3600 then + while timeInSec > 3600 do + tbl.h = tbl.h + 1 + timeInSec = timeInSec - 3600 + end + end + if timeInSec > 60 then + while timeInSec > 60 do + tbl.m = tbl.m + 1 + timeInSec = timeInSec - 60 + end + end + tbl.s = timeInSec + return tbl + else + env.info('mist.time.getDHMS didnt recieve number') + return + end + end - local start = 0 - local timeInSec = mist.utils.round(timer.getAbsTime()) - if convert and type(convert) == 'number' then - timeInSec = convert - end - - while start < timeInSec do - - if date.d == cal[date.m] then - --if y % 4 >= 0 and i == 2 then -- for leap year. DCS doesnt do leapyear, but I am keeping the code dormant because maybe with WW2 the mission year may become relevant - - --else - date.m = date.m + 1 - date.d = 0 - --end - end - if date.m == 13 then - date.m = 1 - date.y = date.y + 1 - end - date.d = date.d + 1 - start = start + 86400 - end - return date -end + mist.getMilString = function(theTime) + local timeInSec = 0 + if theTime then + timeInSec = mist.time.convertToSec(theTime) + else + timeInSec = mist.utils.round(timer.getAbsTime(), 0) + end -mist.time.relativeToStart = function(time) - if type(time) == 'number' then - return time - timer.getTime0() - end -end + local DHMS = mist.time.getDHMS(timeInSec) -mist.getDateString = function(rtnType, murica, oTime) -- returns date based on time - local word = {'January', 'Feburary', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' } -- 'etc - local curTime = 0 - if oTime then - curTime = oTime - else - curTime = mist.utils.round(timer.getAbsTime()) - end - local tbl = mist.time.getDate(curTime) - - if rtnType then - if murica then - return tostring(word[tbl.m] .. ' ' .. tbl.d .. ' ' .. tbl.y) - else - return tostring(tbl.d .. ' ' .. word[tbl.m] .. ' ' .. tbl.y) - end - else - if murica then - return tostring(tbl.m .. '.' .. tbl.d .. '.' .. tbl.y) - else - return tostring(tbl.d .. '.' .. tbl.m .. '.' .. tbl.y) - end - end -end ---WIP -mist.time.milToGame = function(milString, rtnType) --converts a military time. By default returns the abosolute time that event would occur. With optional value it returns how many seconds from time of call till that time. - local curTime = mist.utils.round(timer.getAbsTime()) - local milTimeInSec = 0 + return tostring(string.format('%02d', DHMS.h) .. string.format('%02d',DHMS.m)) + end - if milString and type(milString) == 'string' and string.len(milString) >= 4 then - local hr = tonumber(string.sub(milString, 1, 2)) - local mi = tonumber(string.sub(milString, 3)) - milTimeInSec = milTimeInSec + (mi*60) + (hr*3600) - elseif milString and type(milString) == 'table' and (milString.d or milString.h or milString.m or milString.s) then - milTimeInSec = mist.time.convertToSec(milString) - end - - local startTime = timer.getTime0() - local daysOffset = 0 - if startTime > 86400 then - daysOffset = mist.utils.round(startTime/86400) - if daysOffset > 0 then - milTimeInSec = milTimeInSec *daysOffset - end - end - - if curTime > milTimeInSec then - milTimeInSec = milTimeInSec + 86400 - end - if rtnType then - milTimeInSec = milTimeInSec - startTime - end - return milTimeInSec -end + mist.getClockString = function(theTime, hour) + local timeInSec = 0 + if theTime then + timeInSec = mist.time.convertToSec(theTime) + else + timeInSec = mist.utils.round(timer.getAbsTime(), 0) + end + local DHMS = mist.time.getDHMS(timeInSec) + if hour then + if DHMS.h > 12 then + DHMS.h = DHMS.h - 12 + return tostring(string.format('%02d', DHMS.h) .. ':' .. string.format('%02d',DHMS.m) .. ':' .. string.format('%02d',DHMS.s) .. ' PM') + else + return tostring(string.format('%02d', DHMS.h) .. ':' .. string.format('%02d',DHMS.m) .. ':' .. string.format('%02d',DHMS.s) .. ' AM') + end + else + return tostring(string.format('%02d', DHMS.h) .. ':' .. string.format('%02d',DHMS.m) .. ':' .. string.format('%02d',DHMS.s)) + end + end -mist.DBs.const = {} + -- returns the date in string format + -- both variables optional + -- first val returns with the month as a string + -- 2nd val defins if it should be written the American way or the wrong way. + mist.time.getDate = function(convert) + local cal = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} -- starts at june. 2011. 2012 is leap year, sigh. add a simple check for leap year + local date = {} + date.d = 0 + date.m = 6 + date.y = 2011 ---[[ - ['LAND'] = 1, - ['SHALLOW_WATER'] = 2, - ['WATER'] = 3, - ['ROAD'] = 4, - ['RUNWAY'] = 5 -]] ---[[mist.DBs.const.ME_SSE_terms = { - ['ME'] = { - ['vehicle'] = {'GROUND', 'GROUND_UNIT'}, - ['plane'] = {'AIRPLANE'}, - }, - ['SSE'] = { - }, + local start = 0 + local timeInSec = mist.utils.round(timer.getAbsTime()) + if convert and type(convert) == 'number' then + timeInSec = convert + end + + while start < timeInSec do + + if date.d == cal[date.m] then + --if y % 4 >= 0 and i == 2 then -- for leap year. DCS doesnt do leapyear, but I am keeping the code dormant because maybe with WW2 the mission year may become relevant + + --else + date.m = date.m + 1 + date.d = 0 + --end + end + if date.m == 13 then + date.m = 1 + date.y = date.y + 1 + end + date.d = date.d + 1 + start = start + 86400 + end + return date + end + + mist.time.relativeToStart = function(time) + if type(time) == 'number' then + return time - timer.getTime0() + end + end + + mist.getDateString = function(rtnType, murica, oTime) -- returns date based on time + local word = {'January', 'Feburary', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' } -- 'etc + local curTime = 0 + if oTime then + curTime = oTime + else + curTime = mist.utils.round(timer.getAbsTime()) + end + local tbl = mist.time.getDate(curTime) + + if rtnType then + if murica then + return tostring(word[tbl.m] .. ' ' .. tbl.d .. ' ' .. tbl.y) + else + return tostring(tbl.d .. ' ' .. word[tbl.m] .. ' ' .. tbl.y) + end + else + if murica then + return tostring(tbl.m .. '.' .. tbl.d .. '.' .. tbl.y) + else + return tostring(tbl.d .. '.' .. tbl.m .. '.' .. tbl.y) + end + end + end + --WIP + mist.time.milToGame = function(milString, rtnType) --converts a military time. By default returns the abosolute time that event would occur. With optional value it returns how many seconds from time of call till that time. + local curTime = mist.utils.round(timer.getAbsTime()) + local milTimeInSec = 0 + + if milString and type(milString) == 'string' and string.len(milString) >= 4 then + local hr = tonumber(string.sub(milString, 1, 2)) + local mi = tonumber(string.sub(milString, 3)) + milTimeInSec = milTimeInSec + (mi*60) + (hr*3600) + elseif milString and type(milString) == 'table' and (milString.d or milString.h or milString.m or milString.s) then + milTimeInSec = mist.time.convertToSec(milString) + end + + local startTime = timer.getTime0() + local daysOffset = 0 + if startTime > 86400 then + daysOffset = mist.utils.round(startTime/86400) + if daysOffset > 0 then + milTimeInSec = milTimeInSec *daysOffset + end + end + + if curTime > milTimeInSec then + milTimeInSec = milTimeInSec + 86400 + end + if rtnType then + milTimeInSec = milTimeInSec - startTime + end + return milTimeInSec + end + + mist.DBs.const = {} + + --[[ + ['LAND'] = 1, + ['SHALLOW_WATER'] = 2, + ['WATER'] = 3, + ['ROAD'] = 4, + ['RUNWAY'] = 5 + ]] + --[[mist.DBs.const.ME_SSE_terms = { + ['ME'] = { + ['vehicle'] = {'GROUND', 'GROUND_UNIT'}, + ['plane'] = {'AIRPLANE'}, + }, + ['SSE'] = { + }, }]] -mist.DBs.const.callsigns = { -- not accessible by SSE, must use static list :-/ - ['NATO'] = { - ['rules'] = { - ['groupLimit'] = 9, - }, - ['AWACS'] = { - ['Overlord'] = 1, - ['Magic'] = 2, - ['Wizard'] = 3, - ['Focus'] = 4, - ['Darkstar'] = 5, - }, - ['TANKER'] = { - ['Texaco'] = 1, - ['Arco'] = 2, - ['Shell'] = 3, - }, - ['JTAC'] = { - ['Axeman'] = 1, - ['Darknight'] = 2, - ['Warrior'] = 3, - ['Pointer'] = 4, - ['Eyeball'] = 5, - ['Moonbeam'] = 6, - ['Whiplash'] = 7, - ['Finger'] = 8, - ['Pinpoint'] = 9, - ['Ferret'] = 10, - ['Shaba'] = 11, - ['Playboy'] = 12, - ['Hammer'] = 13, - ['Jaguar'] = 14, - ['Deathstar'] = 15, - ['Anvil'] = 16, - ['Firefly'] = 17, - ['Mantis'] = 18, - ['Badger'] = 19, - }, - ['aircraft'] = { - ['Enfield'] = 1, - ['Springfield'] = 2, - ['Uzi'] = 3, - ['Colt'] = 4, - ['Dodge'] = 5, - ['Ford'] = 6, - ['Chevy'] = 7, - ['Pontiac'] = 8, - }, - - ['unique'] = { - ['A10'] = { - ['Hawg'] = 9, - ['Boar'] = 10, - ['Pig'] = 11, - ['Tusk'] = 12, - ['rules'] = { - ['canUseAircraft'] = true, - ['appliesTo'] = { - 'A-10C', - 'A-10A', - }, - }, - }, - }, - }, + mist.DBs.const.callsigns = { -- not accessible by SSE, must use static list :-/ + ['NATO'] = { + ['rules'] = { + ['groupLimit'] = 9, + }, + ['AWACS'] = { + ['Overlord'] = 1, + ['Magic'] = 2, + ['Wizard'] = 3, + ['Focus'] = 4, + ['Darkstar'] = 5, + }, + ['TANKER'] = { + ['Texaco'] = 1, + ['Arco'] = 2, + ['Shell'] = 3, + }, + ['JTAC'] = { + ['Axeman'] = 1, + ['Darknight'] = 2, + ['Warrior'] = 3, + ['Pointer'] = 4, + ['Eyeball'] = 5, + ['Moonbeam'] = 6, + ['Whiplash'] = 7, + ['Finger'] = 8, + ['Pinpoint'] = 9, + ['Ferret'] = 10, + ['Shaba'] = 11, + ['Playboy'] = 12, + ['Hammer'] = 13, + ['Jaguar'] = 14, + ['Deathstar'] = 15, + ['Anvil'] = 16, + ['Firefly'] = 17, + ['Mantis'] = 18, + ['Badger'] = 19, + }, + ['aircraft'] = { + ['Enfield'] = 1, + ['Springfield'] = 2, + ['Uzi'] = 3, + ['Colt'] = 4, + ['Dodge'] = 5, + ['Ford'] = 6, + ['Chevy'] = 7, + ['Pontiac'] = 8, + }, + + ['unique'] = { + ['A10'] = { + ['Hawg'] = 9, + ['Boar'] = 10, + ['Pig'] = 11, + ['Tusk'] = 12, + ['rules'] = { + ['canUseAircraft'] = true, + ['appliesTo'] = { + 'A-10C', + 'A-10A', + }, + }, + }, + }, + }, } --[[ scope: - { - units = {...}, -- unit names. - coa = {...}, -- coa names - countries = {...}, -- country names - CA = {...}, -- looks just like coa. - unitTypes = { red = {}, blue = {}, all = {}, Russia = {},} - } - - +{ + units = {...}, -- unit names. + coa = {...}, -- coa names + countries = {...}, -- country names + CA = {...}, -- looks just like coa. + unitTypes = { red = {}, blue = {}, all = {}, Russia = {},} +} + + scope examples: { units = { 'Hawg11', 'Hawg12' }, CA = {'blue'} } @@ -6113,3 +6113,6 @@ scope examples: end mist.main() env.info(('Mist version ' .. mist.majorVersion .. '.' .. mist.minorVersion .. '.' .. mist.build .. ' loaded.')) + +-- vim: sw=2:ts=2 + From 52304e9fef2d59f1776cd25e733c9374b96ac14a Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Mon, 4 Jan 2016 20:03:22 +0100 Subject: [PATCH 02/42] cleanup some comments --- mist.lua | 95 ++++++++++---------------------------------------------- 1 file changed, 17 insertions(+), 78 deletions(-) diff --git a/mist.lua b/mist.lua index c05653d..abc9cdf 100644 --- a/mist.lua +++ b/mist.lua @@ -17,8 +17,7 @@ mist.majorVersion = 4 mist.minorVersion = 0 mist.build = 60 --------------------------------------------------------------------------------------------------------------- --- the main area +-- the main scope do local coroutines = {} @@ -401,7 +400,6 @@ do -- THE MAIN FUNCTION -- Accessed 100 times/sec. mist.main = function() timer.scheduleFunction(mist.main, {}, timer.getTime() + 0.01) --reschedule first in case of Lua error - ---------------------------------------------------------------------------------------------------------- --area to add new stuff in write_DB_table_counter = write_DB_table_counter + 1 if write_DB_table_counter == 10 then @@ -435,7 +433,6 @@ do end end - ----------------------------------------------------------------------------------------------------------- --updating alive units update_alive_units_counter = update_alive_units_counter + 1 if update_alive_units_counter == 5 then @@ -454,8 +451,7 @@ do mist.do_scheduled_functions() end -- end of mist.main - -------------------------------------------- - ------------ mist dyn add stuff for coroutines + -- mist dyn add stuff for coroutines local mistGpId = 7000 local mistUnitId = 7000 local mistDynAddIndex = 1 @@ -485,7 +481,8 @@ do end local function groupSpawned(event) - if event.id == world.event.S_EVENT_BIRTH and timer.getTime0() < timer.getAbsTime()then -- dont need to add units spawned in at the start of the mission if mist is loaded in init line + -- dont need to add units spawned in at the start of the mission if mist is loaded in init line + if event.id == world.event.S_EVENT_BIRTH and timer.getTime0() < timer.getAbsTime() then table.insert(tempSpawnedUnits,(event.initiator)) end end @@ -587,11 +584,9 @@ do return false end - mist.dynAdd = function(newGroup) -- same as coalition.add function in SSE. checks the passed data to see if its valid. - --Will generate groupId, groupName, unitId, and unitName if needed - -- - - + -- same as coalition.add function in SSE. checks the passed data to see if its valid. + -- Will generate groupId, groupName, unitId, and unitName if needed + mist.dynAdd = function(newGroup) --mist.debug.writeData(mist.utils.serialize,{'msg', newGroup}, 'newGroupOrig.lua') local cntry = newGroup.country @@ -770,7 +765,6 @@ do end - --------------------------------------------------------------------------------------------- --Modified Slmod task scheduler, superior to timer.scheduleFunction local Tasks = {} @@ -811,7 +805,6 @@ do end end - -------------------------------------------------------------------------------------------------------------------- -- not intended for users to use this function. mist.do_scheduled_functions = function() local i = 1 @@ -851,7 +844,7 @@ do local idNum = 0 - --Simplified event handler + -- Simplified event handler mist.addEventHandler = function(f) --id is optional! local handler = {} idNum = idNum + 1 @@ -878,10 +871,7 @@ do -- mist.scheduleFunction(checkSpawnedEvents, {}, timer.getTime() + 5, 1) end ------------------------------------------------------------------------------------------------------------- - ----------------------------------------------------------------------------------------------- -- Utils- conversion, Lua utils, etc. mist.utils = {} @@ -1171,7 +1161,7 @@ end --porting in Slmod's serialize_slmod mist.utils.serialize = function(name, value, level) - -----Based on ED's serialize_simple2 + --Based on ED's serialize_simple2 local basicSerialize = function (o) if type(o) == "number" then return tostring(o) @@ -1183,9 +1173,6 @@ mist.utils.serialize = function(name, value, level) end local serialize_to_t = function (name, value, level) - ----Based on ED's serialize_simple2 - - local var_str_tbl = {} if level == nil then level = "" end if level ~= "" then level = level.." " end @@ -1415,7 +1402,6 @@ mist.debug.dumpDBs = function() end end ------------------------------------------------------------------------------------------------------------------ --3D Vector manipulation mist.vec = {} @@ -1453,7 +1439,6 @@ end mist.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. mist.tostringMGRS = function(MGRS, acc) @@ -1847,12 +1832,10 @@ function mist.getClimbAngle(unit) end end end ------------------------------------------------------------------------------------------------------------ -- Database building mist.DBs = {} mist.DBs.missionData = {} ------------------------------------------ if env.mission then mist.DBs.missionData['startTime'] = env.mission.start_time @@ -1872,8 +1855,6 @@ if env.mission then end ----------------------------------------- - mist.DBs.zonesByName = {} mist.DBs.zonesByNum = {} @@ -1902,7 +1883,6 @@ 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 mist.DBs.units[coa_name] = {} - ---------------------------------------------- -- build nav points DB mist.DBs.navPoints[coa_name] = {} if coa_data.nav_points then --navpoints @@ -1920,7 +1900,6 @@ for coa_name, coa_data in pairs(env.mission.coalition) do end end end - ------------------------------------------------- if coa_data.country then --there is a country table for cntry_id, cntry_data in pairs(coa_data.country) do @@ -2107,8 +2086,7 @@ for coa_name, coa_data in pairs(mist.DBs.units) do end end --------------- --------- mist unitID funcs +-- mist unitID funcs do for id, idData in pairs(mist.DBs.unitsById) do if idData.unitId > mist.nextUnitId then @@ -2137,7 +2115,6 @@ do local mt = {} mt.__newindex = function(t, key, val) - --------------------------------------------------------------- local original_key = key --only for duplicate runtime IDs. local key_ind = 1 while mist.DBs.deadObjects[key] do @@ -2145,7 +2122,6 @@ do key = tostring(original_key) .. ' #' .. tostring(key_ind) key_ind = key_ind + 1 end - --------------------------------------------------------------- if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then --print('object found in alive_units') @@ -2204,7 +2180,6 @@ do local id = event.initiator.id_ -- initial ID, could change if there is a duplicate id_ already dead. local val = {object = event.initiator} -- the new entry in mist.DBs.deadObjects. - --------------------------------------------------------------- local original_id = id --only for duplicate runtime IDs. local id_ind = 1 while mist.DBs.deadObjects[id] do @@ -2212,7 +2187,6 @@ do id = tostring(original_id) .. ' #' .. tostring(id_ind) id_ind = id_ind + 1 end - --------------------------------------------------------------- if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then --print('object found in alive_units') @@ -2289,10 +2263,6 @@ do mist.addEventHandler(addClientsToActive)]] end - - - - function mist.makeUnitTable(tbl) --[[ @@ -3524,7 +3494,6 @@ mist.getAvgGroupPos = function(groupName) end ---------------------------------------------------------------------------------------- -- demos mist.demos = {} @@ -3639,11 +3608,7 @@ mist.demos.printFlightData = function(unit) end end --------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------- --start of Mission task functions ---***************************************************** mist.ground = {} mist.fixedWing = {} mist.heli = {} @@ -3675,7 +3640,8 @@ mist.goRoute = function(group, path) return false end -function mist.getGroupRoute(groupIdent, task) -- same as getGroupPoints but returns speed and formation type along with vec2 of point} +-- same as getGroupPoints but returns speed and formation type along with vec2 of point} +function mist.getGroupRoute(groupIdent, task) -- refactor to search by groupId and allow groupId and groupName as inputs local gpId = groupIdent if type(groupIdent) == 'string' and not tonumber(groupIdent) then @@ -3911,13 +3877,6 @@ mist.heli.buildWP = function(point, WPtype, speed, alt, altType) return wp end - - ---------------------------------- - --- - - -- need to return a Vec3 or Vec2? function mist.getRandPointInCircle(point, radius, innerRadius) local theta = 2*math.pi*math.random() @@ -4151,20 +4110,13 @@ mist.getLeadPos = function(group) end -- end of Mission task functions --------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------- - - - --------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------- --------MESAGGES------ +-- MESAGGES do local messageList = {} - local messageDisplayRate = 0.1 -- this defines the max refresh rate of the message box it honestly only needs to go faster than this for precision timing stuff (which could be its own function) + -- this defines the max refresh rate of the message box it honestly only needs to + -- go faster than this for precision timing stuff (which could be its own function) + local messageDisplayRate = 0.1 local messageID = 0 local displayActive = false local displayFuncId = 0 @@ -4507,13 +4459,7 @@ end]] end -- End of message system --------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------- -- Beginning of coordinate messages --[[ Design: @@ -4841,7 +4787,6 @@ mist.message.add{ end --------------------------------------------------------------------------------------------- -- basically, just sub-types of mist.msgBR... saves folks the work of getting the ref point. --[[ vars.units- table of unit names (NOT unitNameTable- maybe this should change). @@ -5009,17 +4954,11 @@ mist.message.add{ msgFor = msgFor } end - - -- end of coordinate messages --------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------- + -- start of sct Merge - do -- all function uses of group and unit Ids must be in this do statement - mist.groupTableCheck = function(groupData) local isOk = false From 78d55fc69905fa6365288f20dfc54a3f12ccb696 Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Mon, 4 Jan 2016 21:02:01 +0100 Subject: [PATCH 03/42] use coherent function defintion style --- mist.lua | 248 +++++++++++++++++++++++++++---------------------------- 1 file changed, 124 insertions(+), 124 deletions(-) diff --git a/mist.lua b/mist.lua index abc9cdf..4171f41 100644 --- a/mist.lua +++ b/mist.lua @@ -398,7 +398,7 @@ do local check_spawn_events_counter = 0 -- THE MAIN FUNCTION -- Accessed 100 times/sec. - mist.main = function() + function mist.main() timer.scheduleFunction(mist.main, {}, timer.getTime() + 0.01) --reschedule first in case of Lua error --area to add new stuff in write_DB_table_counter = write_DB_table_counter + 1 @@ -451,16 +451,16 @@ do mist.do_scheduled_functions() end -- end of mist.main + -- mist dyn add stuff for coroutines local mistGpId = 7000 local mistUnitId = 7000 local mistDynAddIndex = 1 - mist.nextGroupId = 1 mist.nextUnitId = 1 - mist.getNextUnitId = function() + function mist.getNextUnitId() mist.nextUnitId = mist.nextUnitId + 1 if mist.nextUnitId > 6900 then mist.nextUnitId = 14000 @@ -468,7 +468,7 @@ do return mist.nextUnitId end - mist.getNextGroupId = function() + function mist.getNextGroupId() mist.nextGroupId = mist.nextGroupId + 1 if mist.nextGroupId > 6900 then mist.nextGroupId = 14000 @@ -476,7 +476,7 @@ do return mist.nextGroupId end - mist.getLastDBUpdateTime = function() + function mist.getLastDBUpdateTime() return lastUpdateTime end @@ -490,7 +490,7 @@ do - mist.dynAddStatic = function(staticObj) + function mist.dynAddStatic(staticObj) local newObj = {} newObj.groupId = staticObj.groupId newObj.category = staticObj.category @@ -586,7 +586,7 @@ do -- same as coalition.add function in SSE. checks the passed data to see if its valid. -- Will generate groupId, groupName, unitId, and unitName if needed - mist.dynAdd = function(newGroup) + function mist.dynAdd(newGroup) --mist.debug.writeData(mist.utils.serialize,{'msg', newGroup}, 'newGroupOrig.lua') local cntry = newGroup.country @@ -778,7 +778,7 @@ do rep - time between repetitions of this function (OPTIONAL) st - time when repetitions of this function will stop automatically (OPTIONAL) ]] - mist.scheduleFunction = function(f, vars, t, rep, st) + function mist.scheduleFunction(f, vars, t, rep, st) --verify correct types assert(type(f) == 'function', 'variable 1, expected function, got ' .. type(f)) assert(type(vars) == 'table' or vars == nil, 'variable 2, expected table or nil, got ' .. type(f)) @@ -794,7 +794,7 @@ do end -- removes a scheduled function based on the function's id. returns true if successful, false if not successful. - mist.removeFunction = function(id) + function mist.removeFunction(id) local i = 1 while i <= #Tasks do if Tasks[i].id == id then @@ -806,7 +806,7 @@ do end -- not intended for users to use this function. - mist.do_scheduled_functions = function() + function mist.do_scheduled_functionsfunction() local i = 1 while i <= #Tasks do if not Tasks[i].rep then -- not a repeated process @@ -845,19 +845,19 @@ do local idNum = 0 -- Simplified event handler - mist.addEventHandler = function(f) --id is optional! + function mist.addEventHandler(f) --id is optional! local handler = {} idNum = idNum + 1 handler.id = idNum handler.f = f - handler.onEvent = function(self, event) + local function handler.onEvent(self, event) self.f(event) end world.addEventHandler(handler) return handler.id end - mist.removeEventHandler = function(id) + function mist.removeEventHandler(id) for key, handler in pairs(world.eventHandlers) do if handler.id and handler.id == id then world.eventHandlers[key] = nil @@ -875,43 +875,43 @@ end -- Utils- conversion, Lua utils, etc. mist.utils = {} -mist.utils.toDegree = function(angle) +function mist.utils.toDegree (angle) return angle*180/math.pi end -mist.utils.toRadian = function(angle) +function mist.utils.toRadian (angle) return angle*math.pi/180 end -mist.utils.metersToNM = function(meters) +function mist.utils.metersToNM (meters) return meters/1852 end -mist.utils.metersToFeet = function(meters) +function mist.utils.metersToFeet (meters) return meters/0.3048 end -mist.utils.NMToMeters = function(NM) +function mist.utils.NMToMeters (NM) return NM*1852 end -mist.utils.feetToMeters = function(feet) +function mist.utils.feetToMeters (feet) return feet*0.3048 end -mist.utils.mpsToKnots = function(mps) +function mist.utils.mpsToKnots (mps) return mps*3600/1852 end -mist.utils.mpsToKmph = function(mps) +function mist.utils.mpsToKmph (mps) return mps*3.6 end -mist.utils.knotsToMps = function(knots) +function mist.utils.knotsToMps (knots) return knots*1852/3600 end -mist.utils.kmphToMps = function(kmph) +function mist.utils.kmphToMps (kmph) return kmph/3.6 end @@ -946,7 +946,7 @@ function mist.utils.makeVec3GL(Vec2, offset) end end -mist.utils.zoneToVec3 = function(zone) +function mist.utils.zoneToVec3 (zone) local new = {} if type(zone) == 'table' and zone.point then new.x = zone.point.x @@ -1020,7 +1020,7 @@ end --from http://lua-users.org/wiki/CopyTable -mist.utils.deepCopy = function(object) +function mist.utils.deepCopy(object) local lookup_table = {} local function _copy(object) if type(object) ~= "table" then @@ -1040,12 +1040,12 @@ end -- From http://lua-users.org/wiki/SimpleRound -- use negative idp for rounding ahead of decimal place, positive for rounding after decimal place -mist.utils.round = function(num, idp) +function mist.utils.round(num, idp) local mult = 10^(idp or 0) return math.floor(num * mult + 0.5) / mult end -mist.utils.roundTbl = function(tbl, idp) +function mist.utils.roundTbl(tbl, idp) for id, val in pairs(tbl) do if type(val) == 'number' then tbl[id] = mist.utils.round(val, idp) @@ -1055,7 +1055,7 @@ mist.utils.roundTbl = function(tbl, idp) end -- porting in Slmod's dostring -mist.utils.dostring = function(s) +function mist.utils.dostring(s) local f, err = loadstring(s) if f then return true, f() @@ -1146,7 +1146,7 @@ function mist.utils.typeCheck(fname, type_tbl, var_tbl) end --porting in Slmod's "safestring" basic serialize -mist.utils.basicSerialize = function(s) +function mist.utils.basicSerialize(s) if s == nil then return "\"\"" else @@ -1160,9 +1160,9 @@ mist.utils.basicSerialize = function(s) end --porting in Slmod's serialize_slmod -mist.utils.serialize = function(name, value, level) +function mist.utils.serialize(name, value, level) --Based on ED's serialize_simple2 - local basicSerialize = function (o) + local function basicSerialize(o) if type(o) == "number" then return tostring(o) elseif type(o) == "boolean" then @@ -1172,7 +1172,7 @@ mist.utils.serialize = function(name, value, level) end end - local serialize_to_t = function (name, value, level) + local function serialize_to_t(name, value, level) local var_str_tbl = {} if level == nil then level = "" end if level ~= "" then level = level.." " end @@ -1214,9 +1214,9 @@ mist.utils.serialize = function(name, value, level) end -- porting in slmod's serialize_wcycles -mist.utils.serializeWithCycles = function(name, value, saved) +function mist.utils.serializeWithCycles(name, value, saved) --mostly straight out of Programming in Lua - local basicSerialize = function (o) + local function basicSerialize(o) if type(o) == "number" then return tostring(o) elseif type(o) == "boolean" then @@ -1252,7 +1252,7 @@ mist.utils.serializeWithCycles = function(name, value, saved) end -- porting in Slmod's serialize_slmod2 -mist.utils.oneLineSerialize = function(tbl) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function +function mist.utils.oneLineSerialize(tbl) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function if type(tbl) == 'table' then --function only works for tables! local tbl_str = {} @@ -1292,7 +1292,7 @@ mist.utils.oneLineSerialize = function(tbl) -- serialization of a table all on end --Function to create string for viewing the contents of a table -NOT for serialization -mist.utils.tableShow = function(tbl, loc, indent, tableshow_tbls) --based on serialize_slmod, this is a _G serialization +function mist.utils.tableShow(tbl, loc, indent, tableshow_tbls) --based on serialize_slmod, this is a _G serialization tableshow_tbls = tableshow_tbls or {} --create table of tables loc = loc or "" indent = indent or "" @@ -1362,7 +1362,7 @@ end mist.debug = {} -mist.debug.dump_G = function(fname) +function mist.debug.dump_G(fname) if lfs and io then local fdir = lfs.writedir() .. [[Logs\]] .. fname local f = io.open(fdir, 'w') @@ -1378,7 +1378,7 @@ mist.debug.dump_G = function(fname) end end -mist.debug.writeData = function(fcn, fcnVars, fname) +function mist.debug.writeData(fcn, fcnVars, fname) if lfs and io then local fdir = lfs.writedir() .. [[Logs\]] .. fname local f = io.open(fdir, 'w') @@ -1394,7 +1394,7 @@ mist.debug.writeData = function(fcn, fcnVars, fname) end end -mist.debug.dumpDBs = function() +function mist.debug.dumpDBs() for DBname, DB in pairs(mist.DBs) do if type(DB) == 'table' and type(DBname) == 'string' then mist.debug.writeData(mist.utils.serialize, {DBname, DB}, 'mist_DBs_' .. DBname .. '.lua') @@ -1405,43 +1405,43 @@ end --3D Vector manipulation mist.vec = {} -mist.vec.add = function(vec1, vec2) +function mist.vec.add(vec1, vec2) return {x = vec1.x + vec2.x, y = vec1.y + vec2.y, z = vec1.z + vec2.z} end -mist.vec.sub = function(vec1, vec2) +function mist.vec.sub(vec1, vec2) return {x = vec1.x - vec2.x, y = vec1.y - vec2.y, z = vec1.z - vec2.z} end -mist.vec.scalarMult = function(vec, mult) +function mist.vec.scalarMult(vec, mult) return {x = vec.x*mult, y = vec.y*mult, z = vec.z*mult} end mist.vec.scalar_mult = mist.vec.scalarMult -mist.vec.dp = function(vec1, vec2) +function mist.vec.dp (vec1, vec2) return vec1.x*vec2.x + vec1.y*vec2.y + vec1.z*vec2.z end -mist.vec.cp = function(vec1, vec2) +function mist.vec.cp(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 -mist.vec.mag = function(vec) +function mist.vec.mag(vec) return (vec.x^2 + vec.y^2 + vec.z^2)^0.5 end -mist.vec.getUnitVec = function(vec) +function mist.vec.getUnitVec(vec) local mag = mist.vec.mag(vec) return { x = vec.x/mag, y = vec.y/mag, z = vec.z/mag } end -mist.vec.rotateVec2 = function(vec2, theta) +function mist.vec.rotateVec2(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. -mist.tostringMGRS = function(MGRS, acc) +function mist.tostringMGRS(MGRS, acc) if acc == 0 then return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph else @@ -1457,7 +1457,7 @@ position after the decimal of the least significant digit: So: 42.32 - acc of 2. ]] -mist.tostringLL = function(lat, lon, acc, DMS) +function mist.tostringLL(lat, lon, acc, DMS) local latHemi, lonHemi if lat > 0 then @@ -1544,7 +1544,7 @@ end 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.]] -mist.tostringBR = function(az, dist, alt, metric) +function mist.tostringBR(az, dist, alt, metric) az = mist.utils.round(mist.utils.toDegree(az), 0) if metric then @@ -1565,7 +1565,7 @@ mist.tostringBR = function(az, dist, alt, metric) return s end -mist.getNorthCorrection = function(gPoint) --gets the correction needed for true north +function mist.getNorthCorrection(gPoint) --gets the correction needed for true north local point = mist.utils.deepCopy(gPoint) if not point.z then --Vec2; convert to Vec3 point.z = point.y @@ -1576,7 +1576,7 @@ mist.getNorthCorrection = function(gPoint) --gets the correction needed for tru return math.atan2(north_posit.z - point.z, north_posit.x - point.x) end -mist.getUnitSkill = function(unitName) +function mist.getUnitSkill(unitName) if Unit.getByName(unitName) and Unit.getByName(unitName):isExist() == true then local lunit = Unit.getByName(unitName) for name, data in pairs(mist.DBs.unitsByName) do @@ -2114,7 +2114,7 @@ mist.DBs.deadObjects = {} do local mt = {} - mt.__newindex = function(t, key, val) + function mt.__newindex(t, key, val) local original_key = key --only for duplicate runtime IDs. local key_ind = 1 while mist.DBs.deadObjects[key] do @@ -2640,7 +2640,7 @@ end end -mist.getDeadMapObjsInZones = function(zone_names) +function mist.getDeadMapObjsInZones(zone_names) -- zone_names: table of zone names -- returns: table of dead map objects (indexed numerically) local map_objs = {} @@ -2663,7 +2663,7 @@ mist.getDeadMapObjsInZones = function(zone_names) end -mist.getDeadMapObjsInPolygonZone = function(zone) +function mist.getDeadMapObjsInPolygonZone(zone) -- zone_names: table of zone names -- returns: table of dead map objects (indexed numerically) local map_objs = {} @@ -2679,7 +2679,7 @@ end mist.flagFunc = {} -mist.flagFunc.mapobjs_dead_zones = function(vars) +function mist.flagFunc.mapobjs_dead_zones(vars) --[[vars needs to be: zones = table or string, flag = number, @@ -2726,7 +2726,7 @@ end -mist.flagFunc.mapobjs_dead_polygon = function(vars) +function mist.flagFunc.mapobjs_dead_polygon(vars) --[[vars needs to be: zone = table, flag = number, @@ -2807,7 +2807,7 @@ function mist.pointInPolygon(point, poly, maxalt) --raycasting point in polygon. end end -mist.getUnitsInPolygon = function (unit_names, polyZone, max_alt) +function mist.getUnitsInPolygon(unit_names, polyZone, max_alt) local units = {} for i = 1, #unit_names do @@ -3142,7 +3142,7 @@ function mist.flagFunc.units_in_moving_zones(vars) end -mist.getUnitsLOS = function(unitset1, altoffset1, unitset2, altoffset2, radius) +function mist.getUnitsLOS(unitset1, altoffset1, unitset2, altoffset2, radius) radius = radius or math.huge local unit_info1 = {} @@ -3193,7 +3193,7 @@ mist.getUnitsLOS = function(unitset1, altoffset1, unitset2, altoffset2, radius) return LOS_data end -mist.flagFunc.units_LOS = function(vars) +function mist.flagFunc.units_LOS(vars) --[[vars needs to be: unitset1 = table, altoffset1 = number, @@ -3270,7 +3270,7 @@ toggle = boolean or nil end end -mist.flagFunc.group_alive = function(vars) +function mist.flagFunc.group_alive(vars) --[[vars groupName flag @@ -3315,7 +3315,7 @@ stopFlag end -mist.flagFunc.group_dead = function(vars) +function mist.flagFunc.group_dead(vars) local type_tbl = { [{'group', 'groupname', 'gp', 'groupName'}] = 'string', flag = {'number', 'string'}, @@ -3351,7 +3351,7 @@ mist.flagFunc.group_dead = function(vars) end end -mist.flagFunc.group_alive_less_than = function(vars) +function mist.flagFunc.group_alive_less_than(vars) local type_tbl = { [{'group', 'groupname', 'gp', 'groupName'}] = 'string', percent = 'number', @@ -3395,7 +3395,7 @@ mist.flagFunc.group_alive_less_than = function(vars) end end -mist.flagFunc.group_alive_more_than = function(vars) +function mist.flagFunc.group_alive_more_than(vars) local type_tbl = { [{'group', 'groupname', 'gp', 'groupName'}] = 'string', percent = 'number', @@ -3439,7 +3439,7 @@ mist.flagFunc.group_alive_more_than = function(vars) end end -mist.getAvgPoint = function(points) +function mist.getAvgPoint(points) local avgX, avgY, avgZ, totNum = 0, 0, 0, 0 for i = 1, #points do local nPoint = mist.utils.makeVec3(points[i]) @@ -3457,7 +3457,7 @@ end --Gets the average position of a group of units (by name) -mist.getAvgPos = function(unitNames) +function mist.getAvgPos(unitNames) local avgX, avgY, avgZ, totNum = 0, 0, 0, 0 for i = 1, #unitNames do local unit @@ -3481,7 +3481,7 @@ mist.getAvgPos = function(unitNames) end end -mist.getAvgGroupPos = function(groupName) +function mist.getAvgGroupPos(groupName) if type(groupName) == 'string' and Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then groupName = Group.getByName(groupName) end @@ -3497,7 +3497,7 @@ end -- demos mist.demos = {} -mist.demos.printFlightData = function(unit) +function mist.demos.printFlightData(unit) if unit:isExist() then local function printData(unit, prevVel, prevE, prevTime) local angles = mist.getAttitude(unit) @@ -3616,7 +3616,7 @@ mist.air = {} mist.air.fixedWing = {} mist.air.heli = {} -mist.goRoute = function(group, path) +function mist.goRoute(group, path) local misTask = { id = 'Mission', params = { @@ -3698,11 +3698,11 @@ end -mist.ground.buildPath = function() end -- ???? +function mist.ground.buildPath() end -- ???? -- No longer accepts path -mist.ground.buildWP = function(point, overRideForm, overRideSpeed) +function mist.ground.buildWP(point, overRideForm, overRideSpeed) local wp = {} wp.x = point.x @@ -3759,7 +3759,7 @@ mist.ground.buildWP = function(point, overRideForm, overRideSpeed) end -mist.fixedWing.buildWP = function(point, WPtype, speed, alt, altType) +function mist.fixedWing.buildWP(point, WPtype, speed, alt, altType) local wp = {} wp.x = point.x @@ -3818,7 +3818,7 @@ mist.fixedWing.buildWP = function(point, WPtype, speed, alt, altType) return wp end -mist.heli.buildWP = function(point, WPtype, speed, alt, altType) +function mist.heli.buildWP(point, WPtype, speed, alt, altType) local wp = {} wp.x = point.x @@ -3905,14 +3905,14 @@ function mist.getRandPointInCircle(point, radius, innerRadius) return rndCoord end -mist.getRandomPointInZone = function(zoneName, innerRadius) +function mist.getRandomPointInZone(zoneName, innerRadius) if type(zoneName) == 'string' and type(trigger.misc.getZone(zoneName)) == 'table' then return mist.getRandPointInCircle(trigger.misc.getZone(zoneName).point, trigger.misc.getZone(zoneName).radius, innerRadius) end return false end -mist.groupToRandomPoint = function(vars) +function mist.groupToRandomPoint(vars) local group = vars.group --Required local point = vars.point --required local radius = vars.radius or 0 @@ -3966,7 +3966,7 @@ mist.groupToRandomPoint = function(vars) return end -mist.groupRandomDistSelf = function(gpData, dist, form, heading, speed) +function mist.groupRandomDistSelf(gpData, dist, form, heading, speed) local pos = mist.getLeadPos(gpData) local fakeZone = {} fakeZone.radius = dist or math.random(300, 1000) @@ -3976,7 +3976,7 @@ mist.groupRandomDistSelf = function(gpData, dist, form, heading, speed) return end -mist.groupToRandomZone = function(gpData, zone, form, heading, speed) +function mist.groupToRandomZone(gpData, zone, form, heading, speed) if type(gpData) == 'string' then gpData = Group.getByName(gpData) end @@ -4004,7 +4004,7 @@ mist.groupToRandomZone = function(gpData, zone, form, heading, speed) return end -mist.isTerrainValid = function(coord, terrainTypes) -- vec2/3 and enum or table of acceptable terrain types +function mist.isTerrainValid(coord, terrainTypes) -- vec2/3 and enum or table of acceptable terrain types if coord.z then coord.y = coord.z end @@ -4033,7 +4033,7 @@ mist.isTerrainValid = function(coord, terrainTypes) -- vec2/3 and enum or table return false end -mist.terrainHeightDiff = function(coord, searchSize) +function mist.terrainHeightDiff(coord, searchSize) local samples = {} local searchRadius = 5 if searchSize then @@ -4066,7 +4066,7 @@ end -mist.groupToPoint = function(gpData, point, form, heading, speed, useRoads) +function mist.groupToPoint(gpData, point, form, heading, speed, useRoads) if type(point) == 'string' then point = trigger.misc.getZone(point) end @@ -4087,7 +4087,7 @@ mist.groupToPoint = function(gpData, point, form, heading, speed, useRoads) end -mist.getLeadPos = function(group) +function mist.getLeadPos(group) if type(group) == 'string' then -- group name group = Group.getByName(group) end @@ -4276,7 +4276,7 @@ do ['Bf-109K-4'] = {'Bf-109'}, } - --[[mist.setCAGroupMSG = function(val) + --[[function mist.setCAGroupMSG(val) if type(val) == 'boolean' then caMSGtoGroup = val return true @@ -4502,7 +4502,7 @@ format for consistency. Maybe individual variable specification could also be s vars.units - table of unit names (NOT unitNameTable- maybe this should change). vars.acc - integer between 0 and 5, inclusive ]] -mist.getMGRSString = function(vars) +function mist.getMGRSString(vars) local units = vars.units local acc = vars.acc or 5 local avgPos = mist.getAvgPos(units) @@ -4518,7 +4518,7 @@ vars.DMS - if true, output in degrees, minutes, seconds. Otherwise, output in d ]] -mist.getLLString = function(vars) +function mist.getLLString(vars) local units = vars.units local acc = vars.acc or 3 local DMS = vars.DMS @@ -4536,7 +4536,7 @@ 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. ]] -mist.getBRString = function(vars) +function mist.getBRString(vars) local units = vars.units local ref = mist.utils.makeVec3(vars.ref, 0) -- turn it into Vec3 if it is not already. local alt = vars.alt @@ -4560,7 +4560,7 @@ vars.heading - direction vars.radius - number vars.headingDegrees - boolean, switches heading to degrees ]] -mist.getLeadingPos = function(vars) +function mist.getLeadingPos(vars) local units = vars.units local heading = vars.heading local radius = vars.radius @@ -4618,7 +4618,7 @@ vars.radius - number vars.headingDegrees - boolean, switches heading to degrees vars.acc - number, 0 to 5. ]] -mist.getLeadingMGRSString = function(vars) +function mist.getLeadingMGRSString(vars) local pos = mist.getLeadingPos(vars) if pos then local acc = vars.acc or 5 @@ -4634,7 +4634,7 @@ 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. ]] -mist.getLeadingLLString = function(vars) +function mist.getLeadingLLString(vars) local pos = mist.getLeadingPos(vars) if pos then local acc = vars.acc or 3 @@ -4655,7 +4655,7 @@ vars.metric - boolean, if true, use km instead of NM. vars.alt - boolean, if true, include altitude. vars.ref - vec3/vec2 reference point. ]] -mist.getLeadingBRString = function(vars) +function mist.getLeadingBRString(vars) local pos = mist.getLeadingPos(vars) if pos then local ref = vars.ref @@ -4686,7 +4686,7 @@ vars.text - text in the message vars.displayTime - self explanatory vars.msgFor - scope ]] -mist.msgMGRS = function(vars) +function mist.msgMGRS(vars) local units = vars.units local acc = vars.acc local text = vars.text @@ -4719,7 +4719,7 @@ vars.text - text in the message vars.displayTime - self explanatory vars.msgFor - scope ]] -mist.msgLL = function(vars) +function mist.msgLL(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 @@ -4757,7 +4757,7 @@ vars.text - text of the message vars.displayTime vars.msgFor - scope ]] -mist.msgBR = function(vars) +function mist.msgBR(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 mist.getBRString local alt = vars.alt @@ -4797,7 +4797,7 @@ vars.text - text of the message vars.displayTime vars.msgFor - scope ]] -mist.msgBullseye = function(vars) +function mist.msgBullseye(vars) if string.lower(vars.ref) == 'red' then vars.ref = mist.DBs.missionData.bullseye.red mist.msgBR(vars) @@ -4817,7 +4817,7 @@ vars.displayTime vars.msgFor - scope ]] -mist.msgBRA = function(vars) +function mist.msgBRA(vars) if Unit.getByName(vars.ref) and Unit.getByName(vars.ref):isExist() == true then vars.ref = Unit.getByName(vars.ref):getPosition().p if not vars.alt then @@ -4838,7 +4838,7 @@ vars.text - text of the message vars.displayTime vars.msgFor - scope ]] -mist.msgLeadingMGRS = function(vars) +function mist.msgLeadingMGRS(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 @@ -4879,7 +4879,7 @@ vars.text - text of the message vars.displayTime vars.msgFor - scope ]] -mist.msgLeadingLL = function(vars) +function mist.msgLeadingLL(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 @@ -4923,7 +4923,7 @@ vars.text - text of the message vars.displayTime vars.msgFor - scope ]] -mist.msgLeadingBR = function(vars) +function mist.msgLeadingBR(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 @@ -4959,7 +4959,7 @@ end -- start of sct Merge do -- all function uses of group and unit Ids must be in this do statement - mist.groupTableCheck = function(groupData) + function mist.groupTableCheck(groupData) local isOk = false if groupData.country then @@ -4983,7 +4983,7 @@ do -- all function uses of group and unit Ids must be in this do statement return isOk end - mist.getCurrentGroupData = function(gpName) + function mist.getCurrentGroupData(gpName) local dbData = mist.getGroupData(gpName) if Group.getByName(gpName) and Group.getByName(gpName):isExist() == true then @@ -5033,7 +5033,7 @@ do -- all function uses of group and unit Ids must be in this do statement end - mist.getGroupData = function(gpName) + function mist.getGroupData(gpName) local found = false local newData = {} if mist.DBs.groupsByName[gpName] then @@ -5098,7 +5098,7 @@ do -- all function uses of group and unit Ids must be in this do statement end end - mist.getPayload = function(unitIdent) + function mist.getPayload(unitIdent) -- refactor to search by groupId and allow groupId and groupName as inputs local unitId = unitIdent if type(unitIdent) == 'string' and not tonumber(unitIdent) then @@ -5138,7 +5138,7 @@ do -- all function uses of group and unit Ids must be in this do statement return end - mist.getGroupPayload = function(groupIdent) + function mist.getGroupPayload(groupIdent) local gpId = groupIdent if type(groupIdent) == 'string' and not tonumber(groupIdent) then gpId = mist.DBs.MEgroupsByName[groupIdent].groupId @@ -5177,7 +5177,7 @@ do -- all function uses of group and unit Ids must be in this do statement end - mist.teleportToPoint = function(vars) -- main teleport function that all of teleport/respawn functions call + function mist.teleportToPoint(vars) -- main teleport function that all of teleport/respawn functions call local point = vars.point local gpName @@ -5313,7 +5313,7 @@ do -- all function uses of group and unit Ids must be in this do statement end - mist.respawnInZone = function(gpName, zone, disperse, maxDisp) + function mist.respawnInZone(gpName, zone, disperse, maxDisp) if type(gpName) == 'table' and gpName:getName() then gpName = gpName:getName() @@ -5338,7 +5338,7 @@ do -- all function uses of group and unit Ids must be in this do statement return mist.teleportToPoint(vars) end - mist.cloneInZone = function(gpName, zone, disperse, maxDisp) + function mist.cloneInZone(gpName, zone, disperse, maxDisp) if type(gpName) == 'table' then gpName = gpName:getName() @@ -5361,7 +5361,7 @@ do -- all function uses of group and unit Ids must be in this do statement return mist.teleportToPoint(vars) end - mist.teleportInZone = function(gpName, zone, disperse, maxDisp) -- groupName, zoneName or table of Zone Names, keepForm is a boolean + function mist.teleportInZone(gpName, zone, disperse, maxDisp) -- groupName, zoneName or table of Zone Names, keepForm is a boolean if type(gpName) == 'table' and gpName:getName() then gpName = gpName:getName() else @@ -5384,7 +5384,7 @@ do -- all function uses of group and unit Ids must be in this do statement return mist.teleportToPoint(vars) end - mist.respawnGroup = function(gpName, task) + function mist.respawnGroup(gpName, task) local vars = {} vars.gpName = gpName vars.action = 'respawn' @@ -5399,7 +5399,7 @@ do -- all function uses of group and unit Ids must be in this do statement return newGroup end - mist.cloneGroup = function(gpName, task) + function mist.cloneGroup(gpName, task) local vars = {} vars.gpName = gpName vars.action = 'clone' @@ -5414,7 +5414,7 @@ do -- all function uses of group and unit Ids must be in this do statement return newGroup end - mist.teleportGroup = function(gpName, task) + function mist.teleportGroup(gpName, task) local vars = {} vars.gpName = gpName vars.action = 'teleport' @@ -5429,7 +5429,7 @@ do -- all function uses of group and unit Ids must be in this do statement return newGroup end - mist.spawnRandomizedGroup = function(groupName, vars) -- need to debug + function mist.spawnRandomizedGroup(groupName, vars) -- need to debug if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then local gpData = mist.getGroupData(groupName) gpData.units = mist.randomizeGroupOrder(gpData.units, vars) @@ -5441,7 +5441,7 @@ do -- all function uses of group and unit Ids must be in this do statement return true end - mist.randomizeNumTable = function(vars) + function mist.randomizeNumTable(vars) local newTable = {} local excludeIndex = {} @@ -5514,7 +5514,7 @@ do -- all function uses of group and unit Ids must be in this do statement return newTable end - mist.randomizeGroupOrder = function(passedUnits, vars) + function mist.randomizeGroupOrder(passedUnits, vars) -- figure out what to exclude, and send data to other func local units = passedUnits @@ -5581,7 +5581,7 @@ do -- all function uses of group and unit Ids must be in this do statement return newGroup end - mist.ground.patrolRoute = function(vars) + function mist.ground.patrolRoute(vars) local tempRoute = {} @@ -5682,7 +5682,7 @@ do -- all function uses of group and unit Ids must be in this do statement return end - mist.ground.patrol = function(gpData, pType, form, speed) + function mist.ground.patrol(gpData, pType, form, speed) local vars = {} if type(gpData) == 'table' and gpData:getName() then @@ -5701,7 +5701,7 @@ do -- all function uses of group and unit Ids must be in this do statement end - mist.random = function(firstNum, secondNum) -- no support for decimals + function mist.random(firstNum, secondNum) -- no support for decimals local lowNum, highNum if not secondNum then highNum = firstNum @@ -5729,7 +5729,7 @@ do -- all function uses of group and unit Ids must be in this do statement - mist.stringMatch = function(s1, s2, bool) + function mist.stringMatch(s1, s2, bool) local exclude = {'%-', '%(', '%)', '%_', '%[', '%]', '%.', '%#', '% ', '%{', '%}', '%$', '%%', '%?', '%+', '%^'} if type(s1) == 'string' and type(s2) == 'string' then for i , str in pairs(exclude) do @@ -5759,7 +5759,7 @@ do -- all function uses of group and unit Ids must be in this do statement -- theTime is optional -- if present current time in mil time is returned -- if number or table the time is converted into mil tim - mist.time.convertToSec = function(timeTable) + function mist.time.convertToSec(timeTable) timeInSec = 0 if timeTable and type(timeTable) == 'number' then @@ -5782,7 +5782,7 @@ do -- all function uses of group and unit Ids must be in this do statement return timeInSec end - mist.time.getDHMS = function(timeInSec) + function mist.time.getDHMS(timeInSec) if timeInSec and type(timeInSec) == 'number' then local tbl = {d = 0, h = 0, m = 0, s = 0} if timeInSec > 86400 then @@ -5811,7 +5811,7 @@ do -- all function uses of group and unit Ids must be in this do statement end end - mist.getMilString = function(theTime) + function mist.getMilString(theTime) local timeInSec = 0 if theTime then timeInSec = mist.time.convertToSec(theTime) @@ -5824,7 +5824,7 @@ do -- all function uses of group and unit Ids must be in this do statement return tostring(string.format('%02d', DHMS.h) .. string.format('%02d',DHMS.m)) end - mist.getClockString = function(theTime, hour) + function mist.getClockString(theTime, hour) local timeInSec = 0 if theTime then timeInSec = mist.time.convertToSec(theTime) @@ -5848,7 +5848,7 @@ do -- all function uses of group and unit Ids must be in this do statement -- both variables optional -- first val returns with the month as a string -- 2nd val defins if it should be written the American way or the wrong way. - mist.time.getDate = function(convert) + function mist.time.getDate(convert) local cal = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} -- starts at june. 2011. 2012 is leap year, sigh. add a simple check for leap year local date = {} date.d = 0 @@ -5881,13 +5881,13 @@ do -- all function uses of group and unit Ids must be in this do statement return date end - mist.time.relativeToStart = function(time) + function mist.time.relativeToStart(time) if type(time) == 'number' then return time - timer.getTime0() end end - mist.getDateString = function(rtnType, murica, oTime) -- returns date based on time + function mist.getDateString(rtnType, murica, oTime) -- returns date based on time local word = {'January', 'Feburary', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' } -- 'etc local curTime = 0 if oTime then @@ -5912,7 +5912,7 @@ do -- all function uses of group and unit Ids must be in this do statement end end --WIP - mist.time.milToGame = function(milString, rtnType) --converts a military time. By default returns the abosolute time that event would occur. With optional value it returns how many seconds from time of call till that time. + function mist.time.milToGame(milString, rtnType) --converts a military time. By default returns the abosolute time that event would occur. With optional value it returns how many seconds from time of call till that time. local curTime = mist.utils.round(timer.getAbsTime()) local milTimeInSec = 0 From d7b77b120189f9e06506132564287bafaaa22794 Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Mon, 4 Jan 2016 21:02:01 +0100 Subject: [PATCH 04/42] use coherent function defintion style --- mist.lua | 248 +++++++++++++++++++++++++++---------------------------- 1 file changed, 124 insertions(+), 124 deletions(-) diff --git a/mist.lua b/mist.lua index abc9cdf..3efb010 100644 --- a/mist.lua +++ b/mist.lua @@ -398,7 +398,7 @@ do local check_spawn_events_counter = 0 -- THE MAIN FUNCTION -- Accessed 100 times/sec. - mist.main = function() + function mist.main() timer.scheduleFunction(mist.main, {}, timer.getTime() + 0.01) --reschedule first in case of Lua error --area to add new stuff in write_DB_table_counter = write_DB_table_counter + 1 @@ -451,16 +451,16 @@ do mist.do_scheduled_functions() end -- end of mist.main + -- mist dyn add stuff for coroutines local mistGpId = 7000 local mistUnitId = 7000 local mistDynAddIndex = 1 - mist.nextGroupId = 1 mist.nextUnitId = 1 - mist.getNextUnitId = function() + function mist.getNextUnitId() mist.nextUnitId = mist.nextUnitId + 1 if mist.nextUnitId > 6900 then mist.nextUnitId = 14000 @@ -468,7 +468,7 @@ do return mist.nextUnitId end - mist.getNextGroupId = function() + function mist.getNextGroupId() mist.nextGroupId = mist.nextGroupId + 1 if mist.nextGroupId > 6900 then mist.nextGroupId = 14000 @@ -476,7 +476,7 @@ do return mist.nextGroupId end - mist.getLastDBUpdateTime = function() + function mist.getLastDBUpdateTime() return lastUpdateTime end @@ -490,7 +490,7 @@ do - mist.dynAddStatic = function(staticObj) + function mist.dynAddStatic(staticObj) local newObj = {} newObj.groupId = staticObj.groupId newObj.category = staticObj.category @@ -586,7 +586,7 @@ do -- same as coalition.add function in SSE. checks the passed data to see if its valid. -- Will generate groupId, groupName, unitId, and unitName if needed - mist.dynAdd = function(newGroup) + function mist.dynAdd(newGroup) --mist.debug.writeData(mist.utils.serialize,{'msg', newGroup}, 'newGroupOrig.lua') local cntry = newGroup.country @@ -778,7 +778,7 @@ do rep - time between repetitions of this function (OPTIONAL) st - time when repetitions of this function will stop automatically (OPTIONAL) ]] - mist.scheduleFunction = function(f, vars, t, rep, st) + function mist.scheduleFunction(f, vars, t, rep, st) --verify correct types assert(type(f) == 'function', 'variable 1, expected function, got ' .. type(f)) assert(type(vars) == 'table' or vars == nil, 'variable 2, expected table or nil, got ' .. type(f)) @@ -794,7 +794,7 @@ do end -- removes a scheduled function based on the function's id. returns true if successful, false if not successful. - mist.removeFunction = function(id) + function mist.removeFunction(id) local i = 1 while i <= #Tasks do if Tasks[i].id == id then @@ -806,7 +806,7 @@ do end -- not intended for users to use this function. - mist.do_scheduled_functions = function() + function mist.do_scheduled_functions() local i = 1 while i <= #Tasks do if not Tasks[i].rep then -- not a repeated process @@ -845,19 +845,19 @@ do local idNum = 0 -- Simplified event handler - mist.addEventHandler = function(f) --id is optional! + function mist.addEventHandler(f) --id is optional! local handler = {} idNum = idNum + 1 handler.id = idNum handler.f = f - handler.onEvent = function(self, event) + local function handler.onEvent(self, event) self.f(event) end world.addEventHandler(handler) return handler.id end - mist.removeEventHandler = function(id) + function mist.removeEventHandler(id) for key, handler in pairs(world.eventHandlers) do if handler.id and handler.id == id then world.eventHandlers[key] = nil @@ -875,43 +875,43 @@ end -- Utils- conversion, Lua utils, etc. mist.utils = {} -mist.utils.toDegree = function(angle) +function mist.utils.toDegree (angle) return angle*180/math.pi end -mist.utils.toRadian = function(angle) +function mist.utils.toRadian (angle) return angle*math.pi/180 end -mist.utils.metersToNM = function(meters) +function mist.utils.metersToNM (meters) return meters/1852 end -mist.utils.metersToFeet = function(meters) +function mist.utils.metersToFeet (meters) return meters/0.3048 end -mist.utils.NMToMeters = function(NM) +function mist.utils.NMToMeters (NM) return NM*1852 end -mist.utils.feetToMeters = function(feet) +function mist.utils.feetToMeters (feet) return feet*0.3048 end -mist.utils.mpsToKnots = function(mps) +function mist.utils.mpsToKnots (mps) return mps*3600/1852 end -mist.utils.mpsToKmph = function(mps) +function mist.utils.mpsToKmph (mps) return mps*3.6 end -mist.utils.knotsToMps = function(knots) +function mist.utils.knotsToMps (knots) return knots*1852/3600 end -mist.utils.kmphToMps = function(kmph) +function mist.utils.kmphToMps (kmph) return kmph/3.6 end @@ -946,7 +946,7 @@ function mist.utils.makeVec3GL(Vec2, offset) end end -mist.utils.zoneToVec3 = function(zone) +function mist.utils.zoneToVec3 (zone) local new = {} if type(zone) == 'table' and zone.point then new.x = zone.point.x @@ -1020,7 +1020,7 @@ end --from http://lua-users.org/wiki/CopyTable -mist.utils.deepCopy = function(object) +function mist.utils.deepCopy(object) local lookup_table = {} local function _copy(object) if type(object) ~= "table" then @@ -1040,12 +1040,12 @@ end -- From http://lua-users.org/wiki/SimpleRound -- use negative idp for rounding ahead of decimal place, positive for rounding after decimal place -mist.utils.round = function(num, idp) +function mist.utils.round(num, idp) local mult = 10^(idp or 0) return math.floor(num * mult + 0.5) / mult end -mist.utils.roundTbl = function(tbl, idp) +function mist.utils.roundTbl(tbl, idp) for id, val in pairs(tbl) do if type(val) == 'number' then tbl[id] = mist.utils.round(val, idp) @@ -1055,7 +1055,7 @@ mist.utils.roundTbl = function(tbl, idp) end -- porting in Slmod's dostring -mist.utils.dostring = function(s) +function mist.utils.dostring(s) local f, err = loadstring(s) if f then return true, f() @@ -1146,7 +1146,7 @@ function mist.utils.typeCheck(fname, type_tbl, var_tbl) end --porting in Slmod's "safestring" basic serialize -mist.utils.basicSerialize = function(s) +function mist.utils.basicSerialize(s) if s == nil then return "\"\"" else @@ -1160,9 +1160,9 @@ mist.utils.basicSerialize = function(s) end --porting in Slmod's serialize_slmod -mist.utils.serialize = function(name, value, level) +function mist.utils.serialize(name, value, level) --Based on ED's serialize_simple2 - local basicSerialize = function (o) + local function basicSerialize(o) if type(o) == "number" then return tostring(o) elseif type(o) == "boolean" then @@ -1172,7 +1172,7 @@ mist.utils.serialize = function(name, value, level) end end - local serialize_to_t = function (name, value, level) + local function serialize_to_t(name, value, level) local var_str_tbl = {} if level == nil then level = "" end if level ~= "" then level = level.." " end @@ -1214,9 +1214,9 @@ mist.utils.serialize = function(name, value, level) end -- porting in slmod's serialize_wcycles -mist.utils.serializeWithCycles = function(name, value, saved) +function mist.utils.serializeWithCycles(name, value, saved) --mostly straight out of Programming in Lua - local basicSerialize = function (o) + local function basicSerialize(o) if type(o) == "number" then return tostring(o) elseif type(o) == "boolean" then @@ -1252,7 +1252,7 @@ mist.utils.serializeWithCycles = function(name, value, saved) end -- porting in Slmod's serialize_slmod2 -mist.utils.oneLineSerialize = function(tbl) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function +function mist.utils.oneLineSerialize(tbl) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function if type(tbl) == 'table' then --function only works for tables! local tbl_str = {} @@ -1292,7 +1292,7 @@ mist.utils.oneLineSerialize = function(tbl) -- serialization of a table all on end --Function to create string for viewing the contents of a table -NOT for serialization -mist.utils.tableShow = function(tbl, loc, indent, tableshow_tbls) --based on serialize_slmod, this is a _G serialization +function mist.utils.tableShow(tbl, loc, indent, tableshow_tbls) --based on serialize_slmod, this is a _G serialization tableshow_tbls = tableshow_tbls or {} --create table of tables loc = loc or "" indent = indent or "" @@ -1362,7 +1362,7 @@ end mist.debug = {} -mist.debug.dump_G = function(fname) +function mist.debug.dump_G(fname) if lfs and io then local fdir = lfs.writedir() .. [[Logs\]] .. fname local f = io.open(fdir, 'w') @@ -1378,7 +1378,7 @@ mist.debug.dump_G = function(fname) end end -mist.debug.writeData = function(fcn, fcnVars, fname) +function mist.debug.writeData(fcn, fcnVars, fname) if lfs and io then local fdir = lfs.writedir() .. [[Logs\]] .. fname local f = io.open(fdir, 'w') @@ -1394,7 +1394,7 @@ mist.debug.writeData = function(fcn, fcnVars, fname) end end -mist.debug.dumpDBs = function() +function mist.debug.dumpDBs() for DBname, DB in pairs(mist.DBs) do if type(DB) == 'table' and type(DBname) == 'string' then mist.debug.writeData(mist.utils.serialize, {DBname, DB}, 'mist_DBs_' .. DBname .. '.lua') @@ -1405,43 +1405,43 @@ end --3D Vector manipulation mist.vec = {} -mist.vec.add = function(vec1, vec2) +function mist.vec.add(vec1, vec2) return {x = vec1.x + vec2.x, y = vec1.y + vec2.y, z = vec1.z + vec2.z} end -mist.vec.sub = function(vec1, vec2) +function mist.vec.sub(vec1, vec2) return {x = vec1.x - vec2.x, y = vec1.y - vec2.y, z = vec1.z - vec2.z} end -mist.vec.scalarMult = function(vec, mult) +function mist.vec.scalarMult(vec, mult) return {x = vec.x*mult, y = vec.y*mult, z = vec.z*mult} end mist.vec.scalar_mult = mist.vec.scalarMult -mist.vec.dp = function(vec1, vec2) +function mist.vec.dp (vec1, vec2) return vec1.x*vec2.x + vec1.y*vec2.y + vec1.z*vec2.z end -mist.vec.cp = function(vec1, vec2) +function mist.vec.cp(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 -mist.vec.mag = function(vec) +function mist.vec.mag(vec) return (vec.x^2 + vec.y^2 + vec.z^2)^0.5 end -mist.vec.getUnitVec = function(vec) +function mist.vec.getUnitVec(vec) local mag = mist.vec.mag(vec) return { x = vec.x/mag, y = vec.y/mag, z = vec.z/mag } end -mist.vec.rotateVec2 = function(vec2, theta) +function mist.vec.rotateVec2(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. -mist.tostringMGRS = function(MGRS, acc) +function mist.tostringMGRS(MGRS, acc) if acc == 0 then return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph else @@ -1457,7 +1457,7 @@ position after the decimal of the least significant digit: So: 42.32 - acc of 2. ]] -mist.tostringLL = function(lat, lon, acc, DMS) +function mist.tostringLL(lat, lon, acc, DMS) local latHemi, lonHemi if lat > 0 then @@ -1544,7 +1544,7 @@ end 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.]] -mist.tostringBR = function(az, dist, alt, metric) +function mist.tostringBR(az, dist, alt, metric) az = mist.utils.round(mist.utils.toDegree(az), 0) if metric then @@ -1565,7 +1565,7 @@ mist.tostringBR = function(az, dist, alt, metric) return s end -mist.getNorthCorrection = function(gPoint) --gets the correction needed for true north +function mist.getNorthCorrection(gPoint) --gets the correction needed for true north local point = mist.utils.deepCopy(gPoint) if not point.z then --Vec2; convert to Vec3 point.z = point.y @@ -1576,7 +1576,7 @@ mist.getNorthCorrection = function(gPoint) --gets the correction needed for tru return math.atan2(north_posit.z - point.z, north_posit.x - point.x) end -mist.getUnitSkill = function(unitName) +function mist.getUnitSkill(unitName) if Unit.getByName(unitName) and Unit.getByName(unitName):isExist() == true then local lunit = Unit.getByName(unitName) for name, data in pairs(mist.DBs.unitsByName) do @@ -2114,7 +2114,7 @@ mist.DBs.deadObjects = {} do local mt = {} - mt.__newindex = function(t, key, val) + function mt.__newindex(t, key, val) local original_key = key --only for duplicate runtime IDs. local key_ind = 1 while mist.DBs.deadObjects[key] do @@ -2640,7 +2640,7 @@ end end -mist.getDeadMapObjsInZones = function(zone_names) +function mist.getDeadMapObjsInZones(zone_names) -- zone_names: table of zone names -- returns: table of dead map objects (indexed numerically) local map_objs = {} @@ -2663,7 +2663,7 @@ mist.getDeadMapObjsInZones = function(zone_names) end -mist.getDeadMapObjsInPolygonZone = function(zone) +function mist.getDeadMapObjsInPolygonZone(zone) -- zone_names: table of zone names -- returns: table of dead map objects (indexed numerically) local map_objs = {} @@ -2679,7 +2679,7 @@ end mist.flagFunc = {} -mist.flagFunc.mapobjs_dead_zones = function(vars) +function mist.flagFunc.mapobjs_dead_zones(vars) --[[vars needs to be: zones = table or string, flag = number, @@ -2726,7 +2726,7 @@ end -mist.flagFunc.mapobjs_dead_polygon = function(vars) +function mist.flagFunc.mapobjs_dead_polygon(vars) --[[vars needs to be: zone = table, flag = number, @@ -2807,7 +2807,7 @@ function mist.pointInPolygon(point, poly, maxalt) --raycasting point in polygon. end end -mist.getUnitsInPolygon = function (unit_names, polyZone, max_alt) +function mist.getUnitsInPolygon(unit_names, polyZone, max_alt) local units = {} for i = 1, #unit_names do @@ -3142,7 +3142,7 @@ function mist.flagFunc.units_in_moving_zones(vars) end -mist.getUnitsLOS = function(unitset1, altoffset1, unitset2, altoffset2, radius) +function mist.getUnitsLOS(unitset1, altoffset1, unitset2, altoffset2, radius) radius = radius or math.huge local unit_info1 = {} @@ -3193,7 +3193,7 @@ mist.getUnitsLOS = function(unitset1, altoffset1, unitset2, altoffset2, radius) return LOS_data end -mist.flagFunc.units_LOS = function(vars) +function mist.flagFunc.units_LOS(vars) --[[vars needs to be: unitset1 = table, altoffset1 = number, @@ -3270,7 +3270,7 @@ toggle = boolean or nil end end -mist.flagFunc.group_alive = function(vars) +function mist.flagFunc.group_alive(vars) --[[vars groupName flag @@ -3315,7 +3315,7 @@ stopFlag end -mist.flagFunc.group_dead = function(vars) +function mist.flagFunc.group_dead(vars) local type_tbl = { [{'group', 'groupname', 'gp', 'groupName'}] = 'string', flag = {'number', 'string'}, @@ -3351,7 +3351,7 @@ mist.flagFunc.group_dead = function(vars) end end -mist.flagFunc.group_alive_less_than = function(vars) +function mist.flagFunc.group_alive_less_than(vars) local type_tbl = { [{'group', 'groupname', 'gp', 'groupName'}] = 'string', percent = 'number', @@ -3395,7 +3395,7 @@ mist.flagFunc.group_alive_less_than = function(vars) end end -mist.flagFunc.group_alive_more_than = function(vars) +function mist.flagFunc.group_alive_more_than(vars) local type_tbl = { [{'group', 'groupname', 'gp', 'groupName'}] = 'string', percent = 'number', @@ -3439,7 +3439,7 @@ mist.flagFunc.group_alive_more_than = function(vars) end end -mist.getAvgPoint = function(points) +function mist.getAvgPoint(points) local avgX, avgY, avgZ, totNum = 0, 0, 0, 0 for i = 1, #points do local nPoint = mist.utils.makeVec3(points[i]) @@ -3457,7 +3457,7 @@ end --Gets the average position of a group of units (by name) -mist.getAvgPos = function(unitNames) +function mist.getAvgPos(unitNames) local avgX, avgY, avgZ, totNum = 0, 0, 0, 0 for i = 1, #unitNames do local unit @@ -3481,7 +3481,7 @@ mist.getAvgPos = function(unitNames) end end -mist.getAvgGroupPos = function(groupName) +function mist.getAvgGroupPos(groupName) if type(groupName) == 'string' and Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then groupName = Group.getByName(groupName) end @@ -3497,7 +3497,7 @@ end -- demos mist.demos = {} -mist.demos.printFlightData = function(unit) +function mist.demos.printFlightData(unit) if unit:isExist() then local function printData(unit, prevVel, prevE, prevTime) local angles = mist.getAttitude(unit) @@ -3616,7 +3616,7 @@ mist.air = {} mist.air.fixedWing = {} mist.air.heli = {} -mist.goRoute = function(group, path) +function mist.goRoute(group, path) local misTask = { id = 'Mission', params = { @@ -3698,11 +3698,11 @@ end -mist.ground.buildPath = function() end -- ???? +function mist.ground.buildPath() end -- ???? -- No longer accepts path -mist.ground.buildWP = function(point, overRideForm, overRideSpeed) +function mist.ground.buildWP(point, overRideForm, overRideSpeed) local wp = {} wp.x = point.x @@ -3759,7 +3759,7 @@ mist.ground.buildWP = function(point, overRideForm, overRideSpeed) end -mist.fixedWing.buildWP = function(point, WPtype, speed, alt, altType) +function mist.fixedWing.buildWP(point, WPtype, speed, alt, altType) local wp = {} wp.x = point.x @@ -3818,7 +3818,7 @@ mist.fixedWing.buildWP = function(point, WPtype, speed, alt, altType) return wp end -mist.heli.buildWP = function(point, WPtype, speed, alt, altType) +function mist.heli.buildWP(point, WPtype, speed, alt, altType) local wp = {} wp.x = point.x @@ -3905,14 +3905,14 @@ function mist.getRandPointInCircle(point, radius, innerRadius) return rndCoord end -mist.getRandomPointInZone = function(zoneName, innerRadius) +function mist.getRandomPointInZone(zoneName, innerRadius) if type(zoneName) == 'string' and type(trigger.misc.getZone(zoneName)) == 'table' then return mist.getRandPointInCircle(trigger.misc.getZone(zoneName).point, trigger.misc.getZone(zoneName).radius, innerRadius) end return false end -mist.groupToRandomPoint = function(vars) +function mist.groupToRandomPoint(vars) local group = vars.group --Required local point = vars.point --required local radius = vars.radius or 0 @@ -3966,7 +3966,7 @@ mist.groupToRandomPoint = function(vars) return end -mist.groupRandomDistSelf = function(gpData, dist, form, heading, speed) +function mist.groupRandomDistSelf(gpData, dist, form, heading, speed) local pos = mist.getLeadPos(gpData) local fakeZone = {} fakeZone.radius = dist or math.random(300, 1000) @@ -3976,7 +3976,7 @@ mist.groupRandomDistSelf = function(gpData, dist, form, heading, speed) return end -mist.groupToRandomZone = function(gpData, zone, form, heading, speed) +function mist.groupToRandomZone(gpData, zone, form, heading, speed) if type(gpData) == 'string' then gpData = Group.getByName(gpData) end @@ -4004,7 +4004,7 @@ mist.groupToRandomZone = function(gpData, zone, form, heading, speed) return end -mist.isTerrainValid = function(coord, terrainTypes) -- vec2/3 and enum or table of acceptable terrain types +function mist.isTerrainValid(coord, terrainTypes) -- vec2/3 and enum or table of acceptable terrain types if coord.z then coord.y = coord.z end @@ -4033,7 +4033,7 @@ mist.isTerrainValid = function(coord, terrainTypes) -- vec2/3 and enum or table return false end -mist.terrainHeightDiff = function(coord, searchSize) +function mist.terrainHeightDiff(coord, searchSize) local samples = {} local searchRadius = 5 if searchSize then @@ -4066,7 +4066,7 @@ end -mist.groupToPoint = function(gpData, point, form, heading, speed, useRoads) +function mist.groupToPoint(gpData, point, form, heading, speed, useRoads) if type(point) == 'string' then point = trigger.misc.getZone(point) end @@ -4087,7 +4087,7 @@ mist.groupToPoint = function(gpData, point, form, heading, speed, useRoads) end -mist.getLeadPos = function(group) +function mist.getLeadPos(group) if type(group) == 'string' then -- group name group = Group.getByName(group) end @@ -4276,7 +4276,7 @@ do ['Bf-109K-4'] = {'Bf-109'}, } - --[[mist.setCAGroupMSG = function(val) + --[[function mist.setCAGroupMSG(val) if type(val) == 'boolean' then caMSGtoGroup = val return true @@ -4502,7 +4502,7 @@ format for consistency. Maybe individual variable specification could also be s vars.units - table of unit names (NOT unitNameTable- maybe this should change). vars.acc - integer between 0 and 5, inclusive ]] -mist.getMGRSString = function(vars) +function mist.getMGRSString(vars) local units = vars.units local acc = vars.acc or 5 local avgPos = mist.getAvgPos(units) @@ -4518,7 +4518,7 @@ vars.DMS - if true, output in degrees, minutes, seconds. Otherwise, output in d ]] -mist.getLLString = function(vars) +function mist.getLLString(vars) local units = vars.units local acc = vars.acc or 3 local DMS = vars.DMS @@ -4536,7 +4536,7 @@ 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. ]] -mist.getBRString = function(vars) +function mist.getBRString(vars) local units = vars.units local ref = mist.utils.makeVec3(vars.ref, 0) -- turn it into Vec3 if it is not already. local alt = vars.alt @@ -4560,7 +4560,7 @@ vars.heading - direction vars.radius - number vars.headingDegrees - boolean, switches heading to degrees ]] -mist.getLeadingPos = function(vars) +function mist.getLeadingPos(vars) local units = vars.units local heading = vars.heading local radius = vars.radius @@ -4618,7 +4618,7 @@ vars.radius - number vars.headingDegrees - boolean, switches heading to degrees vars.acc - number, 0 to 5. ]] -mist.getLeadingMGRSString = function(vars) +function mist.getLeadingMGRSString(vars) local pos = mist.getLeadingPos(vars) if pos then local acc = vars.acc or 5 @@ -4634,7 +4634,7 @@ 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. ]] -mist.getLeadingLLString = function(vars) +function mist.getLeadingLLString(vars) local pos = mist.getLeadingPos(vars) if pos then local acc = vars.acc or 3 @@ -4655,7 +4655,7 @@ vars.metric - boolean, if true, use km instead of NM. vars.alt - boolean, if true, include altitude. vars.ref - vec3/vec2 reference point. ]] -mist.getLeadingBRString = function(vars) +function mist.getLeadingBRString(vars) local pos = mist.getLeadingPos(vars) if pos then local ref = vars.ref @@ -4686,7 +4686,7 @@ vars.text - text in the message vars.displayTime - self explanatory vars.msgFor - scope ]] -mist.msgMGRS = function(vars) +function mist.msgMGRS(vars) local units = vars.units local acc = vars.acc local text = vars.text @@ -4719,7 +4719,7 @@ vars.text - text in the message vars.displayTime - self explanatory vars.msgFor - scope ]] -mist.msgLL = function(vars) +function mist.msgLL(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 @@ -4757,7 +4757,7 @@ vars.text - text of the message vars.displayTime vars.msgFor - scope ]] -mist.msgBR = function(vars) +function mist.msgBR(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 mist.getBRString local alt = vars.alt @@ -4797,7 +4797,7 @@ vars.text - text of the message vars.displayTime vars.msgFor - scope ]] -mist.msgBullseye = function(vars) +function mist.msgBullseye(vars) if string.lower(vars.ref) == 'red' then vars.ref = mist.DBs.missionData.bullseye.red mist.msgBR(vars) @@ -4817,7 +4817,7 @@ vars.displayTime vars.msgFor - scope ]] -mist.msgBRA = function(vars) +function mist.msgBRA(vars) if Unit.getByName(vars.ref) and Unit.getByName(vars.ref):isExist() == true then vars.ref = Unit.getByName(vars.ref):getPosition().p if not vars.alt then @@ -4838,7 +4838,7 @@ vars.text - text of the message vars.displayTime vars.msgFor - scope ]] -mist.msgLeadingMGRS = function(vars) +function mist.msgLeadingMGRS(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 @@ -4879,7 +4879,7 @@ vars.text - text of the message vars.displayTime vars.msgFor - scope ]] -mist.msgLeadingLL = function(vars) +function mist.msgLeadingLL(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 @@ -4923,7 +4923,7 @@ vars.text - text of the message vars.displayTime vars.msgFor - scope ]] -mist.msgLeadingBR = function(vars) +function mist.msgLeadingBR(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 @@ -4959,7 +4959,7 @@ end -- start of sct Merge do -- all function uses of group and unit Ids must be in this do statement - mist.groupTableCheck = function(groupData) + function mist.groupTableCheck(groupData) local isOk = false if groupData.country then @@ -4983,7 +4983,7 @@ do -- all function uses of group and unit Ids must be in this do statement return isOk end - mist.getCurrentGroupData = function(gpName) + function mist.getCurrentGroupData(gpName) local dbData = mist.getGroupData(gpName) if Group.getByName(gpName) and Group.getByName(gpName):isExist() == true then @@ -5033,7 +5033,7 @@ do -- all function uses of group and unit Ids must be in this do statement end - mist.getGroupData = function(gpName) + function mist.getGroupData(gpName) local found = false local newData = {} if mist.DBs.groupsByName[gpName] then @@ -5098,7 +5098,7 @@ do -- all function uses of group and unit Ids must be in this do statement end end - mist.getPayload = function(unitIdent) + function mist.getPayload(unitIdent) -- refactor to search by groupId and allow groupId and groupName as inputs local unitId = unitIdent if type(unitIdent) == 'string' and not tonumber(unitIdent) then @@ -5138,7 +5138,7 @@ do -- all function uses of group and unit Ids must be in this do statement return end - mist.getGroupPayload = function(groupIdent) + function mist.getGroupPayload(groupIdent) local gpId = groupIdent if type(groupIdent) == 'string' and not tonumber(groupIdent) then gpId = mist.DBs.MEgroupsByName[groupIdent].groupId @@ -5177,7 +5177,7 @@ do -- all function uses of group and unit Ids must be in this do statement end - mist.teleportToPoint = function(vars) -- main teleport function that all of teleport/respawn functions call + function mist.teleportToPoint(vars) -- main teleport function that all of teleport/respawn functions call local point = vars.point local gpName @@ -5313,7 +5313,7 @@ do -- all function uses of group and unit Ids must be in this do statement end - mist.respawnInZone = function(gpName, zone, disperse, maxDisp) + function mist.respawnInZone(gpName, zone, disperse, maxDisp) if type(gpName) == 'table' and gpName:getName() then gpName = gpName:getName() @@ -5338,7 +5338,7 @@ do -- all function uses of group and unit Ids must be in this do statement return mist.teleportToPoint(vars) end - mist.cloneInZone = function(gpName, zone, disperse, maxDisp) + function mist.cloneInZone(gpName, zone, disperse, maxDisp) if type(gpName) == 'table' then gpName = gpName:getName() @@ -5361,7 +5361,7 @@ do -- all function uses of group and unit Ids must be in this do statement return mist.teleportToPoint(vars) end - mist.teleportInZone = function(gpName, zone, disperse, maxDisp) -- groupName, zoneName or table of Zone Names, keepForm is a boolean + function mist.teleportInZone(gpName, zone, disperse, maxDisp) -- groupName, zoneName or table of Zone Names, keepForm is a boolean if type(gpName) == 'table' and gpName:getName() then gpName = gpName:getName() else @@ -5384,7 +5384,7 @@ do -- all function uses of group and unit Ids must be in this do statement return mist.teleportToPoint(vars) end - mist.respawnGroup = function(gpName, task) + function mist.respawnGroup(gpName, task) local vars = {} vars.gpName = gpName vars.action = 'respawn' @@ -5399,7 +5399,7 @@ do -- all function uses of group and unit Ids must be in this do statement return newGroup end - mist.cloneGroup = function(gpName, task) + function mist.cloneGroup(gpName, task) local vars = {} vars.gpName = gpName vars.action = 'clone' @@ -5414,7 +5414,7 @@ do -- all function uses of group and unit Ids must be in this do statement return newGroup end - mist.teleportGroup = function(gpName, task) + function mist.teleportGroup(gpName, task) local vars = {} vars.gpName = gpName vars.action = 'teleport' @@ -5429,7 +5429,7 @@ do -- all function uses of group and unit Ids must be in this do statement return newGroup end - mist.spawnRandomizedGroup = function(groupName, vars) -- need to debug + function mist.spawnRandomizedGroup(groupName, vars) -- need to debug if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then local gpData = mist.getGroupData(groupName) gpData.units = mist.randomizeGroupOrder(gpData.units, vars) @@ -5441,7 +5441,7 @@ do -- all function uses of group and unit Ids must be in this do statement return true end - mist.randomizeNumTable = function(vars) + function mist.randomizeNumTable(vars) local newTable = {} local excludeIndex = {} @@ -5514,7 +5514,7 @@ do -- all function uses of group and unit Ids must be in this do statement return newTable end - mist.randomizeGroupOrder = function(passedUnits, vars) + function mist.randomizeGroupOrder(passedUnits, vars) -- figure out what to exclude, and send data to other func local units = passedUnits @@ -5581,7 +5581,7 @@ do -- all function uses of group and unit Ids must be in this do statement return newGroup end - mist.ground.patrolRoute = function(vars) + function mist.ground.patrolRoute(vars) local tempRoute = {} @@ -5682,7 +5682,7 @@ do -- all function uses of group and unit Ids must be in this do statement return end - mist.ground.patrol = function(gpData, pType, form, speed) + function mist.ground.patrol(gpData, pType, form, speed) local vars = {} if type(gpData) == 'table' and gpData:getName() then @@ -5701,7 +5701,7 @@ do -- all function uses of group and unit Ids must be in this do statement end - mist.random = function(firstNum, secondNum) -- no support for decimals + function mist.random(firstNum, secondNum) -- no support for decimals local lowNum, highNum if not secondNum then highNum = firstNum @@ -5729,7 +5729,7 @@ do -- all function uses of group and unit Ids must be in this do statement - mist.stringMatch = function(s1, s2, bool) + function mist.stringMatch(s1, s2, bool) local exclude = {'%-', '%(', '%)', '%_', '%[', '%]', '%.', '%#', '% ', '%{', '%}', '%$', '%%', '%?', '%+', '%^'} if type(s1) == 'string' and type(s2) == 'string' then for i , str in pairs(exclude) do @@ -5759,7 +5759,7 @@ do -- all function uses of group and unit Ids must be in this do statement -- theTime is optional -- if present current time in mil time is returned -- if number or table the time is converted into mil tim - mist.time.convertToSec = function(timeTable) + function mist.time.convertToSec(timeTable) timeInSec = 0 if timeTable and type(timeTable) == 'number' then @@ -5782,7 +5782,7 @@ do -- all function uses of group and unit Ids must be in this do statement return timeInSec end - mist.time.getDHMS = function(timeInSec) + function mist.time.getDHMS(timeInSec) if timeInSec and type(timeInSec) == 'number' then local tbl = {d = 0, h = 0, m = 0, s = 0} if timeInSec > 86400 then @@ -5811,7 +5811,7 @@ do -- all function uses of group and unit Ids must be in this do statement end end - mist.getMilString = function(theTime) + function mist.getMilString(theTime) local timeInSec = 0 if theTime then timeInSec = mist.time.convertToSec(theTime) @@ -5824,7 +5824,7 @@ do -- all function uses of group and unit Ids must be in this do statement return tostring(string.format('%02d', DHMS.h) .. string.format('%02d',DHMS.m)) end - mist.getClockString = function(theTime, hour) + function mist.getClockString(theTime, hour) local timeInSec = 0 if theTime then timeInSec = mist.time.convertToSec(theTime) @@ -5848,7 +5848,7 @@ do -- all function uses of group and unit Ids must be in this do statement -- both variables optional -- first val returns with the month as a string -- 2nd val defins if it should be written the American way or the wrong way. - mist.time.getDate = function(convert) + function mist.time.getDate(convert) local cal = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} -- starts at june. 2011. 2012 is leap year, sigh. add a simple check for leap year local date = {} date.d = 0 @@ -5881,13 +5881,13 @@ do -- all function uses of group and unit Ids must be in this do statement return date end - mist.time.relativeToStart = function(time) + function mist.time.relativeToStart(time) if type(time) == 'number' then return time - timer.getTime0() end end - mist.getDateString = function(rtnType, murica, oTime) -- returns date based on time + function mist.getDateString(rtnType, murica, oTime) -- returns date based on time local word = {'January', 'Feburary', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' } -- 'etc local curTime = 0 if oTime then @@ -5912,7 +5912,7 @@ do -- all function uses of group and unit Ids must be in this do statement end end --WIP - mist.time.milToGame = function(milString, rtnType) --converts a military time. By default returns the abosolute time that event would occur. With optional value it returns how many seconds from time of call till that time. + function mist.time.milToGame(milString, rtnType) --converts a military time. By default returns the abosolute time that event would occur. With optional value it returns how many seconds from time of call till that time. local curTime = mist.utils.round(timer.getAbsTime()) local milTimeInSec = 0 From cbea7031416736fb7d6b50405f98ce1d4dd12f0f Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Tue, 5 Jan 2016 18:43:23 +0100 Subject: [PATCH 05/42] some more newlines removed --- mist.lua | 58 +++++++++++--------------------------------------------- 1 file changed, 11 insertions(+), 47 deletions(-) diff --git a/mist.lua b/mist.lua index 5cba25e..2606ac2 100644 --- a/mist.lua +++ b/mist.lua @@ -397,10 +397,10 @@ do local write_DB_table_counter = 0 local check_spawn_events_counter = 0 - -- THE MAIN FUNCTION -- Accessed 100 times/sec. + -- The main function. Accessed 100 times/sec. function mist.main() timer.scheduleFunction(mist.main, {}, timer.getTime() + 0.01) --reschedule first in case of Lua error - --area to add new stuff in + write_DB_table_counter = write_DB_table_counter + 1 if write_DB_table_counter == 10 then @@ -487,9 +487,6 @@ do end end - - - function mist.dynAddStatic(staticObj) local newObj = {} newObj.groupId = staticObj.groupId @@ -840,8 +837,6 @@ do end end - - local idNum = 0 -- Simplified event handler @@ -850,7 +845,7 @@ do idNum = idNum + 1 handler.id = idNum handler.f = f - local function handler.onEvent(self, event) + function handler.onEvent(self, event) self.f(event) end world.addEventHandler(handler) @@ -872,7 +867,7 @@ do end --- Utils- conversion, Lua utils, etc. +-- mist.utils: conversion, lua utils, etc. functions mist.utils = {} function mist.utils.toDegree (angle) @@ -1017,8 +1012,6 @@ function mist.utils.unitToWP(unit) return false end - - --from http://lua-users.org/wiki/CopyTable function mist.utils.deepCopy(object) local lookup_table = {} @@ -1064,7 +1057,6 @@ function mist.utils.dostring(s) end end - --[[ mist.utils.typeCheck(fname, type_tbl, var_tbl) Type-checking function: Checks a var_tbl to a type_tbl. Returns true if the var_tbl passes the type check, returns false plus an error message if the var_tbl fails. @@ -1360,6 +1352,7 @@ function mist.utils.tableShow(tbl, loc, indent, tableshow_tbls) --based on seria end end +-- mist.debug: debug functions mist.debug = {} function mist.debug.dump_G(fname) @@ -1402,7 +1395,7 @@ function mist.debug.dumpDBs() end end ---3D Vector manipulation +-- mist.vec: 3D vector manipulation functions mist.vec = {} function mist.vec.add(vec1, vec2) @@ -1832,7 +1825,8 @@ function mist.getClimbAngle(unit) end end end --- Database building + +-- mist.DBs: various tables acting as databases mist.DBs = {} mist.DBs.missionData = {} @@ -2724,8 +2718,6 @@ initial_number end end - - function mist.flagFunc.mapobjs_dead_polygon(vars) --[[vars needs to be: zone = table, @@ -2767,8 +2759,6 @@ initial_number end end - - function mist.pointInPolygon(point, poly, maxalt) --raycasting point in polygon. Code from http://softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm --[[local type_tbl = { point = {'table'}, @@ -2895,9 +2885,6 @@ unitTableDef = table or nil end - - - function mist.getUnitsInZones(unit_names, zone_names, zone_type) zone_type = zone_type or 'cylinder' @@ -2953,7 +2940,6 @@ function mist.getUnitsInZones(unit_names, zone_names, zone_type) return in_zone_units end - function mist.flagFunc.units_in_zones(vars) --[[vars needs to be: units = table, @@ -3015,7 +3001,6 @@ function mist.flagFunc.units_in_zones(vars) end - function mist.getUnitsInMovingZones(unit_names, zone_unit_names, radius, zone_type) zone_type = zone_type or 'cylinder' @@ -3065,8 +3050,6 @@ function mist.getUnitsInMovingZones(unit_names, zone_unit_names, radius, zone_ty return in_zone_units end - - function mist.flagFunc.units_in_moving_zones(vars) --[[vars needs to be: units = table, @@ -3141,7 +3124,6 @@ function mist.flagFunc.units_in_moving_zones(vars) end - function mist.getUnitsLOS(unitset1, altoffset1, unitset2, altoffset2, radius) radius = radius or math.huge @@ -3455,7 +3437,6 @@ function mist.getAvgPoint(points) end end - --Gets the average position of a group of units (by name) function mist.getAvgPos(unitNames) local avgX, avgY, avgZ, totNum = 0, 0, 0, 0 @@ -3608,6 +3589,7 @@ function mist.demos.printFlightData(unit) end end + --start of Mission task functions mist.ground = {} mist.fixedWing = {} @@ -3696,11 +3678,8 @@ function mist.getGroupRoute(groupIdent, task) end --for coa_name, coa_data in pairs(mission.coalition) do end - - function mist.ground.buildPath() end -- ???? - -- No longer accepts path function mist.ground.buildWP(point, overRideForm, overRideSpeed) @@ -4064,8 +4043,6 @@ function mist.terrainHeightDiff(coord, searchSize) return mist.utils.round(tMax - tMin, 2) end - - function mist.groupToPoint(gpData, point, form, heading, speed, useRoads) if type(point) == 'string' then point = trigger.misc.getZone(point) @@ -4086,7 +4063,6 @@ function mist.groupToPoint(gpData, point, form, heading, speed, useRoads) return end - function mist.getLeadPos(group) if type(group) == 'string' then -- group name group = Group.getByName(group) @@ -4479,7 +4455,6 @@ mist.getLeadingLLString(UnitNameTable, dir, radius acc) mist.getBRString(UnitNameTable, ref, alt, metric) mist.getLeadingBRString(UnitNameTable, ref, alt, metric, dir, radius, acc) -- vars versions? - mist.sendMGRSMsg(vars) mist.sendLeadingMGRSMsg(vars) @@ -4515,8 +4490,6 @@ end 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. - - ]] function mist.getLLString(vars) local units = vars.units @@ -4529,7 +4502,6 @@ function mist.getLLString(vars) 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? @@ -4610,7 +4582,6 @@ function mist.getLeadingPos(vars) end end - --[[ vars for mist.getLeadingMGRSString: vars.units - table of unit names vars.heading - direction @@ -4644,8 +4615,6 @@ function mist.getLeadingLLString(vars) end end - - --[[ vars for mist.getLeadingBRString: vars.units - table of unit names vars.heading - direction, number @@ -4747,7 +4716,6 @@ mist.message.add{ end - --[[ vars.units- table of unit names (NOT unitNameTable- maybe this should change). vars.ref - vec3 ref point, maybe overload for vec2 as well? @@ -4786,7 +4754,6 @@ mist.message.add{ end - -- basically, just sub-types of mist.msgBR... saves folks the work of getting the ref point. --[[ vars.units- table of unit names (NOT unitNameTable- maybe this should change). @@ -4816,7 +4783,6 @@ vars.text - text of the message vars.displayTime vars.msgFor - scope ]] - function mist.msgBRA(vars) if Unit.getByName(vars.ref) and Unit.getByName(vars.ref):isExist() == true then vars.ref = Unit.getByName(vars.ref):getPosition().p @@ -4826,7 +4792,6 @@ function mist.msgBRA(vars) mist.msgBR(vars) end end --------------------------------------------------------------------------------------------- --[[ vars for mist.msgLeadingMGRS: vars.units - table of unit names @@ -4868,6 +4833,7 @@ mist.message.add{ end + --[[ vars for mist.msgLeadingLL: vars.units - table of unit names vars.heading - direction, number @@ -5700,7 +5666,6 @@ do -- all function uses of group and unit Ids must be in this do statement return end - function mist.random(firstNum, secondNum) -- no support for decimals local lowNum, highNum if not secondNum then @@ -5727,8 +5692,6 @@ do -- all function uses of group and unit Ids must be in this do statement return choices[rtnVal] end - - function mist.stringMatch(s1, s2, bool) local exclude = {'%-', '%(', '%)', '%_', '%[', '%]', '%.', '%#', '% ', '%{', '%}', '%$', '%%', '%?', '%+', '%^'} if type(s1) == 'string' and type(s2) == 'string' then @@ -5754,6 +5717,7 @@ do -- all function uses of group and unit Ids must be in this do statement mist.matchString = mist.stringMatch -- both commands work because order out type of I + -- mist.time: time conversion functions mist.time = {} -- returns a string for specified military time -- theTime is optional From 6e7f5e3a3d04554d640f23368ddeb92e5d1b33f3 Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Tue, 5 Jan 2016 20:50:26 +0100 Subject: [PATCH 06/42] added some ldoc --- mist.lua | 50 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/mist.lua b/mist.lua index 2606ac2..0bfed17 100644 --- a/mist.lua +++ b/mist.lua @@ -9,7 +9,8 @@ Official Release: https://github.com/mrSkortch/MissionScriptingTools/tree/master ]] ---MiST Mission Scripting Tools +--- MiST Mission Scripting Tools +-- @module mist mist = {} -- don't change these @@ -867,49 +868,83 @@ do end --- mist.utils: conversion, lua utils, etc. functions +--- Utility methods. E.g. conversions between units etc. +-- @section utils mist.utils = {} +--- Converts angle in radians to degrees +-- @param angle angle in radians +-- @return angle in degrees function mist.utils.toDegree (angle) return angle*180/math.pi end +--- Converts angle in degrees to radians +-- @param angle angle in degrees +-- @return angle in degrees function mist.utils.toRadian (angle) return angle*math.pi/180 end +--- Converts meters to nautical miles +-- @param meters distance in meters +-- @return distance in nautical miles function mist.utils.metersToNM (meters) return meters/1852 end +--- Converts meters to feet +-- @param meters distance in meters +-- @return distance in feet function mist.utils.metersToFeet (meters) return meters/0.3048 end +--- Converts nautical miles to meters +-- @param NM distance in nautical miles +-- @return distance in meters function mist.utils.NMToMeters (NM) return NM*1852 end +--- Converts feet to meters +-- @param feet distance in feet +-- @return distance in meters function mist.utils.feetToMeters (feet) return feet*0.3048 end +--- Converts meters per second to knots +-- @param mps speed in m/s +-- @return speed in knots function mist.utils.mpsToKnots (mps) return mps*3600/1852 end +--- Converts meters per second to kilometers per hour +-- @param mps speed in m/s +-- @return speed in km/h function mist.utils.mpsToKmph (mps) return mps*3.6 end +--- Converts knots to meters per second +-- @param knots speed in knots +-- @return speed in m/s function mist.utils.knotsToMps (knots) return knots*1852/3600 end +--- Converts kilometers per hour to meters per second +-- @param kmph speed in km/h +-- @return speed in m/s function mist.utils.kmphToMps (kmph) return kmph/3.6 end +--- Converts a Vec3 to a Vec2. +-- @param Vec3 the 3D vector +-- @return vector converted to Vec2 function mist.utils.makeVec2(Vec3) if Vec3.z then return {x = Vec3.x, y = Vec3.z} @@ -918,6 +953,9 @@ function mist.utils.makeVec2(Vec3) end end +--- Converts a Vec2 to a Vec3. +-- @param Vec2 the 2D vector +-- @param y optional new y axis (altitude) value. If omitted it's 0. function mist.utils.makeVec3(Vec2, y) if not Vec2.z then if Vec2.alt and not y then @@ -931,6 +969,12 @@ function mist.utils.makeVec3(Vec2, y) end end +--- Converts a Vec2 to a Vec3 using ground level as altitude. +-- The ground level at the specific point is used as altitude (y-axis) +-- for the new vector. Optionally a offset can be specified. +-- @tparam Vec2 Vec2 the 2D vector +-- @param[opt] offset offset to be applied to the ground level +-- @return new 3D vector function mist.utils.makeVec3GL(Vec2, offset) local adj = offset or 0 @@ -1621,8 +1665,6 @@ function mist.getGroupPoints(groupIdent) -- if groupname exists in env.mission end --for coa_name, coa_data in pairs(mission.coalition) do end - - --[[ table attitude = getAttitude(string unitname) -- will work on any unit, even if not an aircraft. attitude = { From e19c4f4c2c1d78543c9dee240366ebc6a2060803 Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Tue, 5 Jan 2016 23:29:22 +0100 Subject: [PATCH 07/42] implemented basic logger --- mist.lua | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/mist.lua b/mist.lua index 0bfed17..0644689 100644 --- a/mist.lua +++ b/mist.lua @@ -18,6 +18,93 @@ mist.majorVersion = 4 mist.minorVersion = 0 mist.build = 60 +--- Logger class +-- @section Logger +mist.Logger = { + tag = "", + level = 1, +} + +-- Logger scope +do + --- Logger constructor + -- creates a new logger with empty tag and log level set to 'error' + -- @tparam[opt] mist.Logger optional existing logger object + function mist.Logger:new(l) + l = l or {} + setmetatable(l, self) + self.__index = self + return l + end + + --- Sets the level of verbosity for this logger + -- "none" or 0 disables all logging + -- "error" or 1 shows error messages only + -- "warning" or 2 shows error and warning messages + -- "info" or 3 shows error, warning and info messages + -- @param level this can either be a string or an integer level + function mist.Logger:setLevel(level) + if type(level) == 'string' then + if level == 'none' or level == 'off' then + self.level = 0 + elseif level == 'error' then + self.level = 1 + elseif level == 'warning' then + self.level = 2 + elseif level == 'info' then + self.level = 3 + end + elseif type(level) == 'number' then + self.level = level + end + end + + --- parses text and substitutes keywords with values from given array + -- @tparam string text string containing keywords to substitute with values + -- @tparam array vars values to use for substitution + -- @treturn string new string with keywords substituted + local function mist.Logger:substituteKeys(text, vars) + local substText = text + for identifier in text:gmatch("%%%d") do + local index = tonumber(identifier:match("%d+")) + local value = vars[index] + if type(value) == 'table' then + value = mist.utils.oneLineSerialize(value) + end + substText:gsub(identifier, value) + end + return substText + end + + function mist.Logger:error(text, vars, showBox) + showBox = showBox or false + if self.level >= 1 then + if vars then + text = self.subsituteKeys(text, vars) + end + env.error(tag .. ' | ' .. text, showBox) + end + end + + function mist.Logger:warn(text, vars) + if self.level >= 2 then + if vars then + text = self.subsituteKeys(text, vars) + end + env.warning(tag .. ' | ' .. text) + end + end + + function mist.Logger:info(text, vars) + if self.level >= 4 then + if vars then + text = self.subsituteKeys(text, vars) + end + env.error(tag .. ' | ' .. text) + end + end +end + -- the main scope do local coroutines = {} @@ -6056,6 +6143,9 @@ scope examples: {unitTypes = { blue = {'A-10C'}}} ]] end + + + mist.main() env.info(('Mist version ' .. mist.majorVersion .. '.' .. mist.minorVersion .. '.' .. mist.build .. ' loaded.')) From 08b8811e31116da1ef8af40171dd020500e71375 Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Wed, 6 Jan 2016 00:49:30 +0100 Subject: [PATCH 08/42] fixed/enhanced logger and added some ldoc --- mist.lua | 125 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 89 insertions(+), 36 deletions(-) diff --git a/mist.lua b/mist.lua index 0644689..3d7e610 100644 --- a/mist.lua +++ b/mist.lua @@ -9,8 +9,11 @@ Official Release: https://github.com/mrSkortch/MissionScriptingTools/tree/master ]] ---- MiST Mission Scripting Tools +--- MiST Mission Scripting Tools. -- @module mist +-- @author Speed +-- @author Grimes +-- @author lukrop mist = {} -- don't change these @@ -18,8 +21,8 @@ mist.majorVersion = 4 mist.minorVersion = 0 mist.build = 60 ---- Logger class --- @section Logger +--- Logger class. +-- @type mist.Logger mist.Logger = { tag = "", level = 1, @@ -27,9 +30,12 @@ mist.Logger = { -- Logger scope do - --- Logger constructor - -- creates a new logger with empty tag and log level set to 'error' - -- @tparam[opt] mist.Logger optional existing logger object + --- Logger constructor. + -- Creates a new logger object. + -- @tparam[opt] table l optional logger object. + -- @usage myLogger = mist.Logger:new + -- @usage myLogger = mist.Logger:new{tag = "MyScript", level = 2} + -- @treturn mist.Logger function mist.Logger:new(l) l = l or {} setmetatable(l, self) @@ -37,12 +43,14 @@ do return l end - --- Sets the level of verbosity for this logger + --- Sets the level of verbosity for this logger. -- "none" or 0 disables all logging -- "error" or 1 shows error messages only -- "warning" or 2 shows error and warning messages -- "info" or 3 shows error, warning and info messages -- @param level this can either be a string or an integer level + -- @usage myLogger:setLevel("info") + -- @usage myLogger:setLevel(3) -- show everything! function mist.Logger:setLevel(level) if type(level) == 'string' then if level == 'none' or level == 'off' then @@ -63,7 +71,7 @@ do -- @tparam string text string containing keywords to substitute with values -- @tparam array vars values to use for substitution -- @treturn string new string with keywords substituted - local function mist.Logger:substituteKeys(text, vars) + local function substituteKeys(text, vars) local substText = text for identifier in text:gmatch("%%%d") do local index = tonumber(identifier:match("%d+")) @@ -76,31 +84,54 @@ do return substText end + --- Logs an error. + -- logs to dcs.log as long as at least the "error" log level is set. + -- @tparam string text the text with keywords to substitute. + -- @tparam[opt] table vars this the values to be used for substitution. + -- @tparam[opt] boolean showBox whether to show a box and pause the game. + -- @usage myLogger:error("Just an error!") + -- @usage myLogger:error("This is a serious error! Foo is %1 instead of 'bar'", {'derp'}) + -- @usage myLogger:error("It's a trap!", {}, true) function mist.Logger:error(text, vars, showBox) showBox = showBox or false if self.level >= 1 then if vars then - text = self.subsituteKeys(text, vars) + -- check if table is empty + if not next(vars) == nil then + text = subsituteKeys(text, vars) + end end env.error(tag .. ' | ' .. text, showBox) end end + --- Logs a warning. + -- @tparam string text the text with keywords to substitute. + -- @tparam[opt] table vars this the values to be used for substitution. + -- @usage myLogger:warn("I warned you! Those %1 from the interwebs are %2", {"geeks", 1337}) function mist.Logger:warn(text, vars) if self.level >= 2 then if vars then - text = self.subsituteKeys(text, vars) + if not next(vars) == nil then + text = subsituteKeys(text, vars) + end end env.warning(tag .. ' | ' .. text) end end + --- Logs a info + -- @tparam string text the text with keywords to substitute. + -- @tparam[opt] table vars this the values to be used for substitution. + -- @see warn function mist.Logger:info(text, vars) if self.level >= 4 then if vars then - text = self.subsituteKeys(text, vars) + if not next(vars) == nil then + text = subsituteKeys(text, vars) + end end - env.error(tag .. ' | ' .. text) + env.info(tag .. ' | ' .. text) end end end @@ -955,7 +986,8 @@ do end ---- Utility methods. E.g. conversions between units etc. +--- Utility methods. +-- E.g. conversions between units etc. -- @section utils mist.utils = {} @@ -988,9 +1020,9 @@ function mist.utils.metersToFeet (meters) end --- Converts nautical miles to meters --- @param NM distance in nautical miles +-- @param nm distance in nautical miles -- @return distance in meters -function mist.utils.NMToMeters (NM) +function mist.utils.NMToMeters (nm) return NM*1852 end @@ -1030,48 +1062,51 @@ function mist.utils.kmphToMps (kmph) end --- Converts a Vec3 to a Vec2. --- @param Vec3 the 3D vector +-- @tparam Vec3 vec the 3D vector -- @return vector converted to Vec2 -function mist.utils.makeVec2(Vec3) - if Vec3.z then - return {x = Vec3.x, y = Vec3.z} +function mist.utils.makeVec2(vec) + if vec.z then + return {x = vec.x, y = vec.z} else - return {x = Vec3.x, y = Vec3.y} -- it was actually already vec2. + return {x = vec.x, y = vec.y} -- it was actually already vec2. end end --- Converts a Vec2 to a Vec3. --- @param Vec2 the 2D vector +-- @tparam Vec2 vec the 2D vector -- @param y optional new y axis (altitude) value. If omitted it's 0. -function mist.utils.makeVec3(Vec2, y) - if not Vec2.z then - if Vec2.alt and not y then - y = Vec2.alt +function mist.utils.makeVec3(vec, y) + if not vec.z then + if vec.alt and not y then + y = vec.alt elseif not y then y = 0 end - return {x = Vec2.x, y = y, z = Vec2.y} + return {x = vec.x, y = y, z = vec.y} else - return {x = Vec2.x, y = Vec2.y, z = Vec2.z} -- it was already Vec3, actually. + return {x = vec.x, y = vec.y, z = vec.z} -- it was already Vec3, actually. end end --- Converts a Vec2 to a Vec3 using ground level as altitude. -- The ground level at the specific point is used as altitude (y-axis) -- for the new vector. Optionally a offset can be specified. --- @tparam Vec2 Vec2 the 2D vector +-- @tparam Vec2 vec the 2D vector -- @param[opt] offset offset to be applied to the ground level -- @return new 3D vector -function mist.utils.makeVec3GL(Vec2, offset) +function mist.utils.makeVec3GL(vec, offset) local adj = offset or 0 - if not Vec2.z then - return {x = Vec2.x, y = (land.getHeight(Vec2) + adj), z = Vec2.y} + if not vec.z then + return {x = vec.x, y = (land.getHeight(vec) + adj), z = vec.y} else - return {x = Vec2.x, y = (land.getHeight({x = Vec2.x, y = Vec2.z}) + adj), z = Vec2.z} + return {x = vec.x, y = (land.getHeight({x = vec.x, y = vec.z}) + adj), z = vec.z} end end +--- Returns the center of a zone as Vec3 +-- @tparam string|table zone trigger zone name or table +-- @treturn Vec3 center of the zone function mist.utils.zoneToVec3 (zone) local new = {} if type(zone) == 'table' and zone.point then @@ -1090,30 +1125,43 @@ function mist.utils.zoneToVec3 (zone) end end --- gets heading-error corrected direction from point along vector vec. +--- Returns heading-error corrected direction. +-- True-north corrected direction from point along vector vec. +-- @tparam Vec3 vec +-- @tparam Vec2 point +-- @return heading-error corrected direction from point. function mist.utils.getDir(vec, point) local dir = math.atan2(vec.z, vec.x) if point then dir = dir + mist.getNorthCorrection(point) end if dir < 0 then - dir = dir + 2*math.pi -- put dir in range of 0 to 2*pi + 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) +--- Returns distance in meters between two points. +-- @tparam Vec2|Vec3 point1 first point +-- @tparam Vec2|Vec3 point2 second point +-- @treturn number distance between given points. function mist.utils.get2DDist(point1, point2) point1 = mist.utils.makeVec3(point1) point2 = mist.utils.makeVec3(point2) return mist.vec.mag({x = point1.x - point2.x, y = 0, z = point1.z - point2.z}) end --- gets distance in meters between two points (3 dimensional) +--- Returns distance in meters between two points in 3D space. +-- @tparam Vec3 point1 first point +-- @tparam Vec3 point2 second point +-- @treturn number distancen between given points in 3D space. function mist.utils.get3DDist(point1, point2) return mist.vec.mag({x = point1.x - point2.x, y = point1.y - point2.y, z = point1.z - point2.z}) end +--- Creates a waypoint from a vector. +-- @tparam Vec2|Vec3 vec position of the new waypoint +-- @treturn Waypoint a new waypoint to be used inside paths. function mist.utils.vecToWP(vec) local newWP = {} newWP.x = vec.x @@ -1127,6 +1175,11 @@ function mist.utils.vecToWP(vec) return newWP end +--- Creates a waypoint from a unit. +-- This function also considers the units speed. +-- The alt_type of this waypoint is set to "BARO". +-- @tparam Unit unit Unit whose position and speed will be used. +-- @treturn Waypoint new waypoint. function mist.utils.unitToWP(unit) if type(unit) == 'string' then if Unit.getByName(unit) then From 7dcb0eb144c188cac2a35b247a750f6c7c849a5e Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Wed, 6 Jan 2016 03:15:10 +0100 Subject: [PATCH 09/42] fixed logger and added alert --- mist.lua | 83 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 42 insertions(+), 41 deletions(-) diff --git a/mist.lua b/mist.lua index 3d7e610..9af2b02 100644 --- a/mist.lua +++ b/mist.lua @@ -24,7 +24,7 @@ mist.build = 60 --- Logger class. -- @type mist.Logger mist.Logger = { - tag = "", + tag = "MIST", level = 1, } @@ -69,69 +69,70 @@ do --- parses text and substitutes keywords with values from given array -- @tparam string text string containing keywords to substitute with values - -- @tparam array vars values to use for substitution + -- @param ... variables to use for substitution -- @treturn string new string with keywords substituted - local function substituteKeys(text, vars) - local substText = text - for identifier in text:gmatch("%%%d") do - local index = tonumber(identifier:match("%d+")) - local value = vars[index] + local function formatText(text, ...) + for index,value in ipairs(arg) do + -- TODO: check for getmetatabel(value).__tostring if type(value) == 'table' then value = mist.utils.oneLineSerialize(value) + else + value = tostring(value) end - substText:gsub(identifier, value) + text = text:gsub('$' .. index, value) end - return substText + return text + end + + --- Logs error and shows alert window. + -- This logs an error to the dcs.log and shows a popup window, + -- pausing the simulation. This works always even if logging is + -- disabled by setting a log level of "none" or 0. + -- @tparam string text the text with keywords to substitute. + -- @param ... variables to be used for substitution. + -- @usage myLogger:alert("Shit just hit the fan! WEEEE!!!11") + function mist.Logger:alert(text, ...) + text = formatText(text, unpack(arg)) + env.error(self.tag .. ' | ' .. text, true) end --- Logs an error. - -- logs to dcs.log as long as at least the "error" log level is set. + -- logs a message prefixed with this loggers tag to dcs.log as + -- long as at least the "error" log level (1) is set. -- @tparam string text the text with keywords to substitute. - -- @tparam[opt] table vars this the values to be used for substitution. - -- @tparam[opt] boolean showBox whether to show a box and pause the game. + -- @param ... variables to be used for substitution. -- @usage myLogger:error("Just an error!") - -- @usage myLogger:error("This is a serious error! Foo is %1 instead of 'bar'", {'derp'}) - -- @usage myLogger:error("It's a trap!", {}, true) - function mist.Logger:error(text, vars, showBox) - showBox = showBox or false + -- @usage myLogger:error("Foo is $1 instead of $2", foo, "bar") + function mist.Logger:error(text, ...) if self.level >= 1 then - if vars then - -- check if table is empty - if not next(vars) == nil then - text = subsituteKeys(text, vars) - end - end - env.error(tag .. ' | ' .. text, showBox) + text = formatText(text, unpack(arg)) + env.error(self.tag .. ' | ' .. text) end end --- Logs a warning. + -- logs a message prefixed with this loggers tag to dcs.log as + -- long as at least the "warning" log level (2) is set. -- @tparam string text the text with keywords to substitute. - -- @tparam[opt] table vars this the values to be used for substitution. - -- @usage myLogger:warn("I warned you! Those %1 from the interwebs are %2", {"geeks", 1337}) - function mist.Logger:warn(text, vars) + -- @param ... variables to be used for substitution. + -- @usage myLogger:warn("Mother warned you! Those $1 from the interwebs are $2", {"geeks", 1337}) + function mist.Logger:warn(text, ...) if self.level >= 2 then - if vars then - if not next(vars) == nil then - text = subsituteKeys(text, vars) - end - end - env.warning(tag .. ' | ' .. text) + text = formatText(text, unpack(arg)) + env.warning(self.tag .. ' | ' .. text) end end --- Logs a info + -- logs a message prefixed with this loggers tag to dcs.log as + -- long as the highest log level (3) "info" is set. -- @tparam string text the text with keywords to substitute. - -- @tparam[opt] table vars this the values to be used for substitution. + -- @param ... variables to be used for substitution. -- @see warn - function mist.Logger:info(text, vars) - if self.level >= 4 then - if vars then - if not next(vars) == nil then - text = subsituteKeys(text, vars) - end - end - env.info(tag .. ' | ' .. text) + function mist.Logger:info(text, ...) + if self.level >= 3 then + text = formatText(text, unpack(arg)) + env.info(self.tag .. ' | ' .. text) end end end From 2e198095e9a420c9eb235cd6c656c372c3fd7ddd Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Wed, 6 Jan 2016 04:39:20 +0100 Subject: [PATCH 10/42] completed utils ldoc --- mist.lua | 133 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 89 insertions(+), 44 deletions(-) diff --git a/mist.lua b/mist.lua index 9af2b02..70d382c 100644 --- a/mist.lua +++ b/mist.lua @@ -50,7 +50,8 @@ do -- "info" or 3 shows error, warning and info messages -- @param level this can either be a string or an integer level -- @usage myLogger:setLevel("info") - -- @usage myLogger:setLevel(3) -- show everything! + -- @usage -- log everything + --myLogger:setLevel(3) function mist.Logger:setLevel(level) if type(level) == 'string' then if level == 'none' or level == 'off' then @@ -67,7 +68,7 @@ do end end - --- parses text and substitutes keywords with values from given array + --- parses text and substitutes keywords with values from given array. -- @tparam string text string containing keywords to substitute with values -- @param ... variables to use for substitution -- @treturn string new string with keywords substituted @@ -123,7 +124,7 @@ do end end - --- Logs a info + --- Logs a info. -- logs a message prefixed with this loggers tag to dcs.log as -- long as the highest log level (3) "info" is set. -- @tparam string text the text with keywords to substitute. @@ -992,70 +993,70 @@ end -- @section utils mist.utils = {} ---- Converts angle in radians to degrees +--- Converts angle in radians to degrees. -- @param angle angle in radians -- @return angle in degrees function mist.utils.toDegree (angle) return angle*180/math.pi end ---- Converts angle in degrees to radians +--- Converts angle in degrees to radians. -- @param angle angle in degrees -- @return angle in degrees function mist.utils.toRadian (angle) return angle*math.pi/180 end ---- Converts meters to nautical miles +--- Converts meters to nautical miles. -- @param meters distance in meters -- @return distance in nautical miles function mist.utils.metersToNM (meters) return meters/1852 end ---- Converts meters to feet +--- Converts meters to feet. -- @param meters distance in meters -- @return distance in feet function mist.utils.metersToFeet (meters) return meters/0.3048 end ---- Converts nautical miles to meters +--- Converts nautical miles to meters. -- @param nm distance in nautical miles -- @return distance in meters function mist.utils.NMToMeters (nm) return NM*1852 end ---- Converts feet to meters +--- Converts feet to meters. -- @param feet distance in feet -- @return distance in meters function mist.utils.feetToMeters (feet) return feet*0.3048 end ---- Converts meters per second to knots +--- Converts meters per second to knots. -- @param mps speed in m/s -- @return speed in knots function mist.utils.mpsToKnots (mps) return mps*3600/1852 end ---- Converts meters per second to kilometers per hour +--- Converts meters per second to kilometers per hour. -- @param mps speed in m/s -- @return speed in km/h function mist.utils.mpsToKmph (mps) return mps*3.6 end ---- Converts knots to meters per second +--- Converts knots to meters per second. -- @param knots speed in knots -- @return speed in m/s function mist.utils.knotsToMps (knots) return knots*1852/3600 end ---- Converts kilometers per hour to meters per second +--- Converts kilometers per hour to meters per second. -- @param kmph speed in km/h -- @return speed in m/s function mist.utils.kmphToMps (kmph) @@ -1105,7 +1106,7 @@ function mist.utils.makeVec3GL(vec, offset) end end ---- Returns the center of a zone as Vec3 +--- Returns the center of a zone as Vec3. -- @tparam string|table zone trigger zone name or table -- @treturn Vec3 center of the zone function mist.utils.zoneToVec3 (zone) @@ -1197,7 +1198,11 @@ function mist.utils.unitToWP(unit) return false end ---from http://lua-users.org/wiki/CopyTable +--- Creates a deep copy of a object. +-- Usually this object is a table. +-- See also: from http://lua-users.org/wiki/CopyTable +-- @param object object to copy +-- @return copy of object function mist.utils.deepCopy(object) local lookup_table = {} local function _copy(object) @@ -1216,13 +1221,19 @@ function mist.utils.deepCopy(object) return _copy(object) end +--- Simple rounding function. -- From http://lua-users.org/wiki/SimpleRound -- use negative idp for rounding ahead of decimal place, positive for rounding after decimal place +-- @tparam number num number to round +-- @param idp function mist.utils.round(num, idp) local mult = 10^(idp or 0) return math.floor(num * mult + 0.5) / mult end +--- Rounds all numbers inside a table. +-- @tparam table tbl table in which to round numbers +-- @param idp function mist.utils.roundTbl(tbl, idp) for id, val in pairs(tbl) do if type(val) == 'number' then @@ -1232,7 +1243,10 @@ function mist.utils.roundTbl(tbl, idp) return tbl end --- porting in Slmod's dostring +--- Executes the given string. +-- borrowed from Slmod +-- @tparam string s string containing LUA code. +-- @treturn boolean true if successfully executed, false otherwise function mist.utils.dostring(s) local f, err = loadstring(s) if f then @@ -1242,21 +1256,26 @@ function mist.utils.dostring(s) end end ---[[ mist.utils.typeCheck(fname, type_tbl, var_tbl) -Type-checking function: -Checks a var_tbl to a type_tbl. Returns true if the var_tbl passes the type check, returns false plus an error message if the var_tbl fails. - -type_tbl examples: -type_tbl = { {'table', 'number'}, 'string', 'number', 'number', {'string', 'nil'}, {'number', 'nil'} } -Compare to a var_tbl with up to six entries; var_tbl index 1 must be a table or a number; index 2, a string; index 3, a number; -index 4, a number; index 5, either a string or nil; and index 6, either a number or nil. - -Another example: -type_tbl = { {'text', 'msg', 'text_out'} = 'string', display_time = 'number', display_mode = {'string', 'nil'} coa = {'string', 'nil'}} - -var_tbl must have a string at one of the following table keys: "text", "msg", or "text_out". var_tbl must have a number at table key "display_time", -the table key "display_mode" must be either a string or nil, and the table key "coa" must be either a string or nil. -]] +--- Checks a table's types. +-- This function checks a tables types against a specifically forged type table. +-- @param fname +-- @tparam table type_tbl +-- @tparam table var_tbl +-- @usage -- specifically forged type table +-- type_tbl = { +-- {'table', 'number'}, +-- 'string', +-- 'number', +-- 'number', +-- {'string','nil'}, +-- {'number', 'nil'} +-- } +-- -- my_tbl index 1 must be a table or a number; +-- -- index 2, a string; index 3, a number; +-- -- index 4, a number; index 5, either a string or nil; +-- -- and index 6, either a number or nil. +-- mist.utils.typeCheck(type_tbl, my_tb) +-- @return true if table passes the check, false otherwise. function mist.utils.typeCheck(fname, type_tbl, var_tbl) --env.info('type check') for type_key, type_val in pairs(type_tbl) do @@ -1322,21 +1341,32 @@ function mist.utils.typeCheck(fname, type_tbl, var_tbl) return true end ---porting in Slmod's "safestring" basic serialize -function mist.utils.basicSerialize(s) - if s == nil then +--- Serializes the give variable to a string. +-- borrowed from slmod +-- @param var variable to serialize +-- @treturn string variable serialized to string +function mist.utils.basicSerialize(var) + if var == nil then return "\"\"" else - if ((type(s) == 'number') or (type(s) == 'boolean') or (type(s) == 'function') or (type(s) == 'table') or (type(s) == 'userdata') ) then - return tostring(s) - elseif type(s) == 'string' then - s = string.format('%q', s) - return s + if ((type(var) == 'number') or + (type(var) == 'boolean') or + (type(var) == 'function') or + (type(var) == 'table') or + (type(var) == 'userdata') ) then + return tostring(var) + elseif type(var) == 'string' then + var = string.format('%q', var) + return var end end end ---porting in Slmod's serialize_slmod +--- Serialize value +-- borrowed from slmod (serialize_slmod) +-- @param name +-- @param value value to serialize +-- @param level function mist.utils.serialize(name, value, level) --Based on ED's serialize_simple2 local function basicSerialize(o) @@ -1390,7 +1420,11 @@ function mist.utils.serialize(name, value, level) return table.concat(t_str) end --- porting in slmod's serialize_wcycles +--- Serialize value supporting cycles. +-- borrowed from slmod (serialize_wcycles) +-- @param name +-- @param value value to serialize +-- @param saved function mist.utils.serializeWithCycles(name, value, saved) --mostly straight out of Programming in Lua local function basicSerialize(o) @@ -1428,8 +1462,12 @@ function mist.utils.serializeWithCycles(name, value, saved) end end --- porting in Slmod's serialize_slmod2 -function mist.utils.oneLineSerialize(tbl) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function +--- Serialize a table to a single line string. +-- serialization of a table all on a single line, no comments, made to replace old get_table_string function +-- borrowed from slmod +-- @tparam table tbl table to serialize. +-- @treturn string string containing serialized table +function mist.utils.oneLineSerialize(tbl) if type(tbl) == 'table' then --function only works for tables! local tbl_str = {} @@ -1468,7 +1506,14 @@ function mist.utils.oneLineSerialize(tbl) -- serialization of a table all on a end end ---Function to create string for viewing the contents of a table -NOT for serialization +--- Returns table in a easy readable string representation. +-- this function is not meant for serialization because it uses +-- newlines for better readability. +-- @param tbl table to show +-- @param loc +-- @param indent +-- @param tableshow_tbls +-- @return human readable string representation of given table function mist.utils.tableShow(tbl, loc, indent, tableshow_tbls) --based on serialize_slmod, this is a _G serialization tableshow_tbls = tableshow_tbls or {} --create table of tables loc = loc or "" From f269f2cb0a8bfd14f4c02a625d400a815dd31418 Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Wed, 6 Jan 2016 17:33:12 +0100 Subject: [PATCH 11/42] added ldoc for debug and vec functions --- mist.lua | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/mist.lua b/mist.lua index 70d382c..bfdd782 100644 --- a/mist.lua +++ b/mist.lua @@ -1582,9 +1582,17 @@ function mist.utils.tableShow(tbl, loc, indent, tableshow_tbls) --based on seria end end --- mist.debug: debug functions +--- Debug functions +-- @section debug mist.debug = {} +--- Dumps the global table _G. +-- This dumps the global table _G to a file in +-- the DCS\Logs directory. +-- This function requires you to disable script sanitization +-- in $DCS_ROOT\Scripts\MissionScripting.lua to access lfs and io +-- libraries. +-- @param fname function mist.debug.dump_G(fname) if lfs and io then local fdir = lfs.writedir() .. [[Logs\]] .. fname @@ -1601,6 +1609,13 @@ function mist.debug.dump_G(fname) end end +--- Write debug data to file. +-- This function requires you to disable script sanitization +-- in $DCS_ROOT\Scripts\MissionScripting.lua to access lfs and io +-- libraries. +-- @param fcn +-- @param fcnVars +-- @param fname function mist.debug.writeData(fcn, fcnVars, fname) if lfs and io then local fdir = lfs.writedir() .. [[Logs\]] .. fname @@ -1617,6 +1632,10 @@ function mist.debug.writeData(fcn, fcnVars, fname) end end +--- Write mist databases to file. +-- This function requires you to disable script sanitization +-- in $DCS_ROOT\Scripts\MissionScripting.lua to access lfs and io +-- libraries. function mist.debug.dumpDBs() for DBname, DB in pairs(mist.DBs) do if type(DB) == 'table' and type(DBname) == 'string' then @@ -1625,40 +1644,71 @@ function mist.debug.dumpDBs() end end --- mist.vec: 3D vector manipulation functions +--- 3D Vector manipulation functions +-- @section mist.vec mist.vec = {} +--- Vector addition. +-- @tparam Vec3 vec1 first vector +-- @tparam Vec3 vec2 second vector +-- @treturn Vec3 new vector, sum of vec1 and vec2. function mist.vec.add(vec1, vec2) return {x = vec1.x + vec2.x, y = vec1.y + vec2.y, z = vec1.z + vec2.z} end +--- Vector substraction. +-- @tparam Vec3 vec1 first vector +-- @tparam Vec3 vec2 second vector +-- @treturn Vec3 new vector, vec2 substracted from vec1. function mist.vec.sub(vec1, vec2) return {x = vec1.x - vec2.x, y = vec1.y - vec2.y, z = vec1.z - vec2.z} end +--- Vector scalar multiplication. +-- @tparam Vec3 vec vector to multiply +-- @tparam number mult scalar multiplicator +-- @treturn Vec3 new vector multiplied with the given scalar function mist.vec.scalarMult(vec, mult) return {x = vec.x*mult, y = vec.y*mult, z = vec.z*mult} end mist.vec.scalar_mult = mist.vec.scalarMult +--- Vector dot product. +-- @tparam Vec3 vec1 first vector +-- @tparam Vec3 vec2 second vector +-- @treturn number dot product of given vectors function mist.vec.dp (vec1, vec2) return vec1.x*vec2.x + vec1.y*vec2.y + vec1.z*vec2.z end +--- Vector cross product. +-- @tparam Vec3 vec1 first vector +-- @tparam Vec3 vec2 second vector +-- @treturn Vec3 new vector, cross product of vec1 and vec2. function mist.vec.cp(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 +--- Vector magnitude +-- @tparam Vec3 vec vector +-- @treturn number magnitude of vector vec function mist.vec.mag(vec) return (vec.x^2 + vec.y^2 + vec.z^2)^0.5 end +--- Unit vector +-- @tparam Vec3 vec +-- @treturn Vec3 unit vector of vec function mist.vec.getUnitVec(vec) local mag = mist.vec.mag(vec) return { x = vec.x/mag, y = vec.y/mag, z = vec.z/mag } end +--- Rotate vector. +-- @tparam Vec2 vec2 to rotoate +-- @tparam number theta +-- @return Vec2 rotated vector. function mist.vec.rotateVec2(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 From 750e06d42f49b5b5ae66e340cd27cae184477aa7 Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Thu, 7 Jan 2016 11:20:10 +0100 Subject: [PATCH 12/42] some more ldoc --- mist.lua | 496 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 276 insertions(+), 220 deletions(-) diff --git a/mist.lua b/mist.lua index bfdd782..ad8a7cd 100644 --- a/mist.lua +++ b/mist.lua @@ -30,14 +30,20 @@ mist.Logger = { -- Logger scope do - --- Logger constructor. - -- Creates a new logger object. + --- Creates a new logger. + -- Each logger has it's own tag and log level. -- @tparam[opt] table l optional logger object. -- @usage myLogger = mist.Logger:new -- @usage myLogger = mist.Logger:new{tag = "MyScript", level = 2} -- @treturn mist.Logger function mist.Logger:new(l) l = l or {} + if not l.tag then + l.tag = "MIST" + end + if not l.level then + l.level = 1 + end setmetatable(l, self) self.__index = self return l @@ -988,7 +994,7 @@ do end ---- Utility methods. +--- Utility functions. -- E.g. conversions between units etc. -- @section utils mist.utils = {} @@ -1583,7 +1589,7 @@ function mist.utils.tableShow(tbl, loc, indent, tableshow_tbls) --based on seria end --- Debug functions --- @section debug +-- @section mist.debug mist.debug = {} --- Dumps the global table _G. @@ -1644,7 +1650,7 @@ function mist.debug.dumpDBs() end end ---- 3D Vector manipulation functions +--- 3D Vector functions -- @section mist.vec mist.vec = {} @@ -2104,9 +2110,16 @@ function mist.getClimbAngle(unit) end end --- mist.DBs: various tables acting as databases +--- mist.DBs: various tables acting as databases +-- @section mist.DBs mist.DBs = {} +--- Mission data +-- @table mist.DBs.missionData +-- @field startTime mission start time +-- @field theatre mission theatre/map e.g. Caucasus +-- @field version mission version +-- @field files mission resources mist.DBs.missionData = {} if env.mission then @@ -2949,8 +2962,30 @@ function mist.getDeadMapObjsInPolygonZone(zone) return map_objs end +--- Flag functions. +-- The mist "Flag functions" are functions that are similar to Slmod functions +-- that detect a game condition and set a flag when that game condition is met. +-- +-- They are intended to be used by persons with little or no experience in Lua +-- programming, but with a good knowledge of the DCS mission editor. +-- @section mist.flagFunc mist.flagFunc = {} +--- Sets a flag if map objects are destroyed inside a zone. +-- Once this function is run, it will start a continuously evaluated process +-- that will set a flag true if map objects (such as bridges, buildings in +-- town, etc.) die (or have died) in a mission editor zone (or set of zones). +-- This will only happen once; once the flag is set true, the process ends. +-- @usage +-- -- Example vars table +-- vars = { +-- zones = { "zone1", "zone2" }, -- can also be a single string +-- flag = 3, -- number of the flag +-- stopflag = 4, -- optional number of the stop flag +-- req_num = 10, -- optional minimum amount of map objects needed to die +-- } +-- mist.flagFuncs.mapobjs_dead_zones(vars) +-- @tparam table vars table containing parameters. function mist.flagFunc.mapobjs_dead_zones(vars) --[[vars needs to be: zones = table or string, @@ -2996,6 +3031,26 @@ initial_number end end +--- Sets a flag if map objects are destroyed inside a polygon. +-- Once this function is run, it will start a continuously evaluated process +-- that will set a flag true if map objects (such as bridges, buildings in +-- town, etc.) die (or have died) in a polygon. +-- This will only happen once; once the flag is set true, the process ends. +-- @usage +-- -- Example vars table +-- vars = { +-- zone = { +-- [1] = mist.DBs.unitsByName['NE corner'].point, +-- [2] = mist.DBs.unitsByName['SE corner'].point, +-- [3] = mist.DBs.unitsByName['SW corner'].point, +-- [4] = mist.DBs.unitsByName['NW corner'].point +-- } +-- flag = 3, -- number of the flag +-- stopflag = 4, -- optional number of the stop flag +-- req_num = 10, -- optional minimum amount of map objects needed to die +-- } +-- mist.flagFuncs.mapobjs_dead_zones(vars) +-- @tparam table vars table containing parameters. function mist.flagFunc.mapobjs_dead_polygon(vars) --[[vars needs to be: zone = table, @@ -3037,61 +3092,6 @@ initial_number end end -function mist.pointInPolygon(point, poly, maxalt) --raycasting point in polygon. Code from http://softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm - --[[local type_tbl = { - point = {'table'}, - poly = {'table'}, - maxalt = {'number', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.pointInPolygon', type_tbl, {point, poly, maxalt}) - assert(err, errmsg) - ]] - point = mist.utils.makeVec3(point) - local px = point.x - local pz = point.z - local cn = 0 - local newpoly = mist.utils.deepCopy(poly) - - if not maxalt or (point.y <= maxalt) then - local polysize = #newpoly - newpoly[#newpoly + 1] = newpoly[1] - - newpoly[1] = mist.utils.makeVec3(newpoly[1]) - - for k = 1, polysize do - newpoly[k+1] = mist.utils.makeVec3(newpoly[k+1]) - if ((newpoly[k].z <= pz) and (newpoly[k+1].z > pz)) or ((newpoly[k].z > pz) and (newpoly[k+1].z <= pz)) then - local vt = (pz - newpoly[k].z) / (newpoly[k+1].z - newpoly[k].z) - if (px < newpoly[k].x + vt*(newpoly[k+1].x - newpoly[k].x)) then - cn = cn + 1 - end - end - end - - return cn%2 == 1 - else - return false - end -end - -function mist.getUnitsInPolygon(unit_names, polyZone, max_alt) - local units = {} - - for i = 1, #unit_names do - units[#units + 1] = Unit.getByName(unitNames[i]) - end - - local inZoneUnits = {} - for i =1, #units do - if units[i]:isActive() and mist.pointInPolygon(units[i]:getPosition().p, polyZone, max_alt) then - inZoneUnits[inZoneUnits + 1] = units[i] - end - end - - return inZoneUnits -end - function mist.flagFunc.units_in_polygon(vars) --[[vars needs to be: units = table, @@ -3163,61 +3163,6 @@ unitTableDef = table or nil end -function mist.getUnitsInZones(unit_names, zone_names, zone_type) - - zone_type = zone_type or 'cylinder' - if zone_type == 'c' or zone_type == 'cylindrical' or zone_type == 'C' then - zone_type = 'cylinder' - end - if zone_type == 's' or zone_type == 'spherical' or zone_type == 'S' then - zone_type = 'sphere' - end - - assert(zone_type == 'cylinder' or zone_type == 'sphere', 'invalid zone_type: ' .. tostring(zone_type)) - - local units = {} - local zones = {} - - for k = 1, #unit_names do - local unit = Unit.getByName(unit_names[k]) - if unit then - units[#units + 1] = unit - end - end - - - for k = 1, #zone_names do - local zone = trigger.misc.getZone(zone_names[k]) - if zone then - zones[#zones + 1] = {radius = zone.radius, x = zone.point.x, y = zone.point.y, z = zone.point.z} - end - end - - local in_zone_units = {} - - for units_ind = 1, #units do - for zones_ind = 1, #zones do - if zone_type == 'sphere' then --add land height value for sphere zone type - local alt = land.getHeight({x = zones[zones_ind].x, y = zones[zones_ind].z}) - if alt then - zones[zones_ind].y = alt - end - end - local unit_pos = units[units_ind]:getPosition().p - if unit_pos and units[units_ind]:isActive() == true then - if zone_type == 'cylinder' and (((unit_pos.x - zones[zones_ind].x)^2 + (unit_pos.z - zones[zones_ind].z)^2)^0.5 <= zones[zones_ind].radius) then - in_zone_units[#in_zone_units + 1] = units[units_ind] - break - elseif zone_type == 'sphere' and (((unit_pos.x - zones[zones_ind].x)^2 + (unit_pos.y - zones[zones_ind].y)^2 + (unit_pos.z - zones[zones_ind].z)^2)^0.5 <= zones[zones_ind].radius) then - in_zone_units[#in_zone_units + 1] = units[units_ind] - break - end - end - end - end - return in_zone_units -end - function mist.flagFunc.units_in_zones(vars) --[[vars needs to be: units = table, @@ -3279,55 +3224,6 @@ function mist.flagFunc.units_in_zones(vars) end -function mist.getUnitsInMovingZones(unit_names, zone_unit_names, radius, zone_type) - - zone_type = zone_type or 'cylinder' - if zone_type == 'c' or zone_type == 'cylindrical' or zone_type == 'C' then - zone_type = 'cylinder' - end - if zone_type == 's' or zone_type == 'spherical' or zone_type == 'S' then - zone_type = 'sphere' - end - - assert(zone_type == 'cylinder' or zone_type == 'sphere', 'invalid zone_type: ' .. tostring(zone_type)) - - local units = {} - local zone_units = {} - - for k = 1, #unit_names do - local unit = Unit.getByName(unit_names[k]) - if unit then - units[#units + 1] = unit - end - end - - for k = 1, #zone_unit_names do - local unit = Unit.getByName(zone_unit_names[k]) - if unit then - zone_units[#zone_units + 1] = unit - end - end - - local in_zone_units = {} - - for units_ind = 1, #units do - for zone_units_ind = 1, #zone_units do - local unit_pos = units[units_ind]:getPosition().p - local zone_unit_pos = zone_units[zone_units_ind]:getPosition().p - if unit_pos and zone_unit_pos and units[units_ind]:isActive() == true then - if zone_type == 'cylinder' and (((unit_pos.x - zone_unit_pos.x)^2 + (unit_pos.z - zone_unit_pos.z)^2)^0.5 <= radius) then - in_zone_units[#in_zone_units + 1] = units[units_ind] - break - elseif zone_type == 'sphere' and (((unit_pos.x - zone_unit_pos.x)^2 + (unit_pos.y - zone_unit_pos.y)^2 + (unit_pos.z - zone_unit_pos.z)^2)^0.5 <= radius) then - in_zone_units[#in_zone_units + 1] = units[units_ind] - break - end - end - end - end - return in_zone_units -end - function mist.flagFunc.units_in_moving_zones(vars) --[[vars needs to be: units = table, @@ -3402,57 +3298,6 @@ function mist.flagFunc.units_in_moving_zones(vars) end -function mist.getUnitsLOS(unitset1, altoffset1, unitset2, altoffset2, radius) - radius = radius or math.huge - - local unit_info1 = {} - local unit_info2 = {} - - -- get the positions all in one step, saves execution time. - for unitset1_ind = 1, #unitset1 do - local unit1 = Unit.getByName(unitset1[unitset1_ind]) - if unit1 and unit1:isActive() == true then - unit_info1[#unit_info1 + 1] = {} - unit_info1[#unit_info1]["unit"] = unit1 - unit_info1[#unit_info1]["pos"] = unit1:getPosition().p - end - end - - for unitset2_ind = 1, #unitset2 do - local unit2 = Unit.getByName(unitset2[unitset2_ind]) - if unit2 and unit2:isActive() == true then - unit_info2[#unit_info2 + 1] = {} - unit_info2[#unit_info2]["unit"] = unit2 - unit_info2[#unit_info2]["pos"] = unit2:getPosition().p - end - end - - local LOS_data = {} - -- now compute los - for unit1_ind = 1, #unit_info1 do - local unit_added = false - for unit2_ind = 1, #unit_info2 do - if radius == math.huge or (mist.vec.mag(mist.vec.sub(unit_info1[unit1_ind].pos, unit_info2[unit2_ind].pos)) < radius) then -- inside radius - local point1 = { x = unit_info1[unit1_ind].pos.x, y = unit_info1[unit1_ind].pos.y + altoffset1, z = unit_info1[unit1_ind].pos.z} - local point2 = { x = unit_info2[unit2_ind].pos.x, y = unit_info2[unit2_ind].pos.y + altoffset2, z = unit_info2[unit2_ind].pos.z} - if land.isVisible(point1, point2) then - if unit_added == false then - unit_added = true - LOS_data[#LOS_data + 1] = {} - LOS_data[#LOS_data]['unit'] = unit_info1[unit1_ind].unit - LOS_data[#LOS_data]['vis'] = {} - LOS_data[#LOS_data]['vis'][#LOS_data[#LOS_data]['vis'] + 1] = unit_info2[unit2_ind].unit - else - LOS_data[#LOS_data]['vis'][#LOS_data[#LOS_data]['vis'] + 1] = unit_info2[unit2_ind].unit - end - end - end - end - end - - return LOS_data -end - function mist.flagFunc.units_LOS(vars) --[[vars needs to be: unitset1 = table, @@ -3699,6 +3544,216 @@ function mist.flagFunc.group_alive_more_than(vars) end end +function mist.pointInPolygon(point, poly, maxalt) --raycasting point in polygon. Code from http://softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm + --[[local type_tbl = { + point = {'table'}, + poly = {'table'}, + maxalt = {'number', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.pointInPolygon', type_tbl, {point, poly, maxalt}) + assert(err, errmsg) + ]] + point = mist.utils.makeVec3(point) + local px = point.x + local pz = point.z + local cn = 0 + local newpoly = mist.utils.deepCopy(poly) + + if not maxalt or (point.y <= maxalt) then + local polysize = #newpoly + newpoly[#newpoly + 1] = newpoly[1] + + newpoly[1] = mist.utils.makeVec3(newpoly[1]) + + for k = 1, polysize do + newpoly[k+1] = mist.utils.makeVec3(newpoly[k+1]) + if ((newpoly[k].z <= pz) and (newpoly[k+1].z > pz)) or ((newpoly[k].z > pz) and (newpoly[k+1].z <= pz)) then + local vt = (pz - newpoly[k].z) / (newpoly[k+1].z - newpoly[k].z) + if (px < newpoly[k].x + vt*(newpoly[k+1].x - newpoly[k].x)) then + cn = cn + 1 + end + end + end + + return cn%2 == 1 + else + return false + end +end + +function mist.getUnitsInPolygon(unit_names, polyZone, max_alt) + local units = {} + + for i = 1, #unit_names do + units[#units + 1] = Unit.getByName(unitNames[i]) + end + + local inZoneUnits = {} + for i =1, #units do + if units[i]:isActive() and mist.pointInPolygon(units[i]:getPosition().p, polyZone, max_alt) then + inZoneUnits[inZoneUnits + 1] = units[i] + end + end + + return inZoneUnits +end + +function mist.getUnitsInZones(unit_names, zone_names, zone_type) + + zone_type = zone_type or 'cylinder' + if zone_type == 'c' or zone_type == 'cylindrical' or zone_type == 'C' then + zone_type = 'cylinder' + end + if zone_type == 's' or zone_type == 'spherical' or zone_type == 'S' then + zone_type = 'sphere' + end + + assert(zone_type == 'cylinder' or zone_type == 'sphere', 'invalid zone_type: ' .. tostring(zone_type)) + + local units = {} + local zones = {} + + for k = 1, #unit_names do + local unit = Unit.getByName(unit_names[k]) + if unit then + units[#units + 1] = unit + end + end + + + for k = 1, #zone_names do + local zone = trigger.misc.getZone(zone_names[k]) + if zone then + zones[#zones + 1] = {radius = zone.radius, x = zone.point.x, y = zone.point.y, z = zone.point.z} + end + end + + local in_zone_units = {} + + for units_ind = 1, #units do + for zones_ind = 1, #zones do + if zone_type == 'sphere' then --add land height value for sphere zone type + local alt = land.getHeight({x = zones[zones_ind].x, y = zones[zones_ind].z}) + if alt then + zones[zones_ind].y = alt + end + end + local unit_pos = units[units_ind]:getPosition().p + if unit_pos and units[units_ind]:isActive() == true then + if zone_type == 'cylinder' and (((unit_pos.x - zones[zones_ind].x)^2 + (unit_pos.z - zones[zones_ind].z)^2)^0.5 <= zones[zones_ind].radius) then + in_zone_units[#in_zone_units + 1] = units[units_ind] + break + elseif zone_type == 'sphere' and (((unit_pos.x - zones[zones_ind].x)^2 + (unit_pos.y - zones[zones_ind].y)^2 + (unit_pos.z - zones[zones_ind].z)^2)^0.5 <= zones[zones_ind].radius) then + in_zone_units[#in_zone_units + 1] = units[units_ind] + break + end + end + end + end + return in_zone_units +end + +function mist.getUnitsInMovingZones(unit_names, zone_unit_names, radius, zone_type) + + zone_type = zone_type or 'cylinder' + if zone_type == 'c' or zone_type == 'cylindrical' or zone_type == 'C' then + zone_type = 'cylinder' + end + if zone_type == 's' or zone_type == 'spherical' or zone_type == 'S' then + zone_type = 'sphere' + end + + assert(zone_type == 'cylinder' or zone_type == 'sphere', 'invalid zone_type: ' .. tostring(zone_type)) + + local units = {} + local zone_units = {} + + for k = 1, #unit_names do + local unit = Unit.getByName(unit_names[k]) + if unit then + units[#units + 1] = unit + end + end + + for k = 1, #zone_unit_names do + local unit = Unit.getByName(zone_unit_names[k]) + if unit then + zone_units[#zone_units + 1] = unit + end + end + + local in_zone_units = {} + + for units_ind = 1, #units do + for zone_units_ind = 1, #zone_units do + local unit_pos = units[units_ind]:getPosition().p + local zone_unit_pos = zone_units[zone_units_ind]:getPosition().p + if unit_pos and zone_unit_pos and units[units_ind]:isActive() == true then + if zone_type == 'cylinder' and (((unit_pos.x - zone_unit_pos.x)^2 + (unit_pos.z - zone_unit_pos.z)^2)^0.5 <= radius) then + in_zone_units[#in_zone_units + 1] = units[units_ind] + break + elseif zone_type == 'sphere' and (((unit_pos.x - zone_unit_pos.x)^2 + (unit_pos.y - zone_unit_pos.y)^2 + (unit_pos.z - zone_unit_pos.z)^2)^0.5 <= radius) then + in_zone_units[#in_zone_units + 1] = units[units_ind] + break + end + end + end + end + return in_zone_units +end + +function mist.getUnitsLOS(unitset1, altoffset1, unitset2, altoffset2, radius) + radius = radius or math.huge + + local unit_info1 = {} + local unit_info2 = {} + + -- get the positions all in one step, saves execution time. + for unitset1_ind = 1, #unitset1 do + local unit1 = Unit.getByName(unitset1[unitset1_ind]) + if unit1 and unit1:isActive() == true then + unit_info1[#unit_info1 + 1] = {} + unit_info1[#unit_info1]["unit"] = unit1 + unit_info1[#unit_info1]["pos"] = unit1:getPosition().p + end + end + + for unitset2_ind = 1, #unitset2 do + local unit2 = Unit.getByName(unitset2[unitset2_ind]) + if unit2 and unit2:isActive() == true then + unit_info2[#unit_info2 + 1] = {} + unit_info2[#unit_info2]["unit"] = unit2 + unit_info2[#unit_info2]["pos"] = unit2:getPosition().p + end + end + + local LOS_data = {} + -- now compute los + for unit1_ind = 1, #unit_info1 do + local unit_added = false + for unit2_ind = 1, #unit_info2 do + if radius == math.huge or (mist.vec.mag(mist.vec.sub(unit_info1[unit1_ind].pos, unit_info2[unit2_ind].pos)) < radius) then -- inside radius + local point1 = { x = unit_info1[unit1_ind].pos.x, y = unit_info1[unit1_ind].pos.y + altoffset1, z = unit_info1[unit1_ind].pos.z} + local point2 = { x = unit_info2[unit2_ind].pos.x, y = unit_info2[unit2_ind].pos.y + altoffset2, z = unit_info2[unit2_ind].pos.z} + if land.isVisible(point1, point2) then + if unit_added == false then + unit_added = true + LOS_data[#LOS_data + 1] = {} + LOS_data[#LOS_data]['unit'] = unit_info1[unit1_ind].unit + LOS_data[#LOS_data]['vis'] = {} + LOS_data[#LOS_data]['vis'][#LOS_data[#LOS_data]['vis'] + 1] = unit_info2[unit2_ind].unit + else + LOS_data[#LOS_data]['vis'][#LOS_data[#LOS_data]['vis'] + 1] = unit_info2[unit2_ind].unit + end + end + end + end + end + + return LOS_data +end + function mist.getAvgPoint(points) local avgX, avgY, avgZ, totNum = 0, 0, 0, 0 for i = 1, #points do @@ -3753,7 +3808,8 @@ function mist.getAvgGroupPos(groupName) end --- demos +--- Demo functions. +-- @section mist.demos mist.demos = {} function mist.demos.printFlightData(unit) @@ -3868,7 +3924,8 @@ function mist.demos.printFlightData(unit) end ---start of Mission task functions +--- Group task functions. +-- @section tasks mist.ground = {} mist.fixedWing = {} mist.heli = {} @@ -4933,6 +4990,7 @@ vars.text - text in the message vars.displayTime - self explanatory vars.msgFor - scope ]] + function mist.msgMGRS(vars) local units = vars.units local acc = vars.acc @@ -6293,8 +6351,6 @@ scope examples: ]] end - - mist.main() env.info(('Mist version ' .. mist.majorVersion .. '.' .. mist.minorVersion .. '.' .. mist.build .. ' loaded.')) From 337d93b2aa4c8c8209fcda2ded413bfdce0121fb Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Thu, 7 Jan 2016 13:05:27 +0100 Subject: [PATCH 13/42] introduced scopes simulating different files. Using scopes for various groups of functions simulating files. Allows for use of some local functions and better organization. Using a editor which is capable of folding this huge file gets way easier to navigate. --- mist.lua | 7645 +++++++++++++++++++++++++++--------------------------- 1 file changed, 3802 insertions(+), 3843 deletions(-) diff --git a/mist.lua b/mist.lua index ad8a7cd..17d83d8 100644 --- a/mist.lua +++ b/mist.lua @@ -21,131 +21,7 @@ mist.majorVersion = 4 mist.minorVersion = 0 mist.build = 60 ---- Logger class. --- @type mist.Logger -mist.Logger = { - tag = "MIST", - level = 1, -} - --- Logger scope -do - --- Creates a new logger. - -- Each logger has it's own tag and log level. - -- @tparam[opt] table l optional logger object. - -- @usage myLogger = mist.Logger:new - -- @usage myLogger = mist.Logger:new{tag = "MyScript", level = 2} - -- @treturn mist.Logger - function mist.Logger:new(l) - l = l or {} - if not l.tag then - l.tag = "MIST" - end - if not l.level then - l.level = 1 - end - setmetatable(l, self) - self.__index = self - return l - end - - --- Sets the level of verbosity for this logger. - -- "none" or 0 disables all logging - -- "error" or 1 shows error messages only - -- "warning" or 2 shows error and warning messages - -- "info" or 3 shows error, warning and info messages - -- @param level this can either be a string or an integer level - -- @usage myLogger:setLevel("info") - -- @usage -- log everything - --myLogger:setLevel(3) - function mist.Logger:setLevel(level) - if type(level) == 'string' then - if level == 'none' or level == 'off' then - self.level = 0 - elseif level == 'error' then - self.level = 1 - elseif level == 'warning' then - self.level = 2 - elseif level == 'info' then - self.level = 3 - end - elseif type(level) == 'number' then - self.level = level - end - end - - --- parses text and substitutes keywords with values from given array. - -- @tparam string text string containing keywords to substitute with values - -- @param ... variables to use for substitution - -- @treturn string new string with keywords substituted - local function formatText(text, ...) - for index,value in ipairs(arg) do - -- TODO: check for getmetatabel(value).__tostring - if type(value) == 'table' then - value = mist.utils.oneLineSerialize(value) - else - value = tostring(value) - end - text = text:gsub('$' .. index, value) - end - return text - end - - --- Logs error and shows alert window. - -- This logs an error to the dcs.log and shows a popup window, - -- pausing the simulation. This works always even if logging is - -- disabled by setting a log level of "none" or 0. - -- @tparam string text the text with keywords to substitute. - -- @param ... variables to be used for substitution. - -- @usage myLogger:alert("Shit just hit the fan! WEEEE!!!11") - function mist.Logger:alert(text, ...) - text = formatText(text, unpack(arg)) - env.error(self.tag .. ' | ' .. text, true) - end - - --- Logs an error. - -- logs a message prefixed with this loggers tag to dcs.log as - -- long as at least the "error" log level (1) is set. - -- @tparam string text the text with keywords to substitute. - -- @param ... variables to be used for substitution. - -- @usage myLogger:error("Just an error!") - -- @usage myLogger:error("Foo is $1 instead of $2", foo, "bar") - function mist.Logger:error(text, ...) - if self.level >= 1 then - text = formatText(text, unpack(arg)) - env.error(self.tag .. ' | ' .. text) - end - end - - --- Logs a warning. - -- logs a message prefixed with this loggers tag to dcs.log as - -- long as at least the "warning" log level (2) is set. - -- @tparam string text the text with keywords to substitute. - -- @param ... variables to be used for substitution. - -- @usage myLogger:warn("Mother warned you! Those $1 from the interwebs are $2", {"geeks", 1337}) - function mist.Logger:warn(text, ...) - if self.level >= 2 then - text = formatText(text, unpack(arg)) - env.warning(self.tag .. ' | ' .. text) - end - end - - --- Logs a info. - -- logs a message prefixed with this loggers tag to dcs.log as - -- long as the highest log level (3) "info" is set. - -- @tparam string text the text with keywords to substitute. - -- @param ... variables to be used for substitution. - -- @see warn - function mist.Logger:info(text, ...) - if self.level >= 3 then - text = formatText(text, unpack(arg)) - env.info(self.tag .. ' | ' .. text) - end - end -end - --- the main scope -do +do -- the main scope local coroutines = {} local tempSpawnedUnits = {} -- birth events added here @@ -154,6 +30,23 @@ do local writeGroups = {} local lastUpdateTime = 0 + local update_alive_units_counter = 0 + local write_DB_table_counter = 0 + local check_spawn_events_counter = 0 + + local mistGpId = 7000 + local mistUnitId = 7000 + local mistDynAddIndex = 1 + + local Tasks = {} + local task_id = 0 + local idNum = 0 + + mist.nextGroupId = 1 + mist.nextUnitId = 1 + + mist.addEventHandler(groupSpawned) + local function updateAliveUnits() -- coroutine function local lalive_units = mist.DBs.aliveUnits -- local references for faster execution local lunits = mist.DBs.unitsByNum @@ -219,8 +112,6 @@ do return false end - - newTable.name = newObject:getName() newTable.groupId = tonumber(newObject:getID()) newTable.groupName = newObject:getName() @@ -375,8 +266,6 @@ do return newTable end - - local function checkSpawnedEvents() if #tempSpawnedUnits > 0 then local groupsToAdd = {} @@ -445,9 +334,7 @@ do end end - local function updateDBTables() - local i = 0 for index, newTable in pairs(writeGroups) do i = i + 1 @@ -520,11 +407,15 @@ do end end - local update_alive_units_counter = 0 - local write_DB_table_counter = 0 - local check_spawn_events_counter = 0 + local function groupSpawned(event) + -- dont need to add units spawned in at the start of the mission if mist is loaded in init line + if event.id == world.event.S_EVENT_BIRTH and timer.getTime0() < timer.getAbsTime() then + table.insert(tempSpawnedUnits,(event.initiator)) + end + end - -- The main function. Accessed 100 times/sec. + --- The main function. + -- Run 100 times per second. function mist.main() timer.scheduleFunction(mist.main, {}, timer.getTime() + 0.01) --reschedule first in case of Lua error @@ -579,14 +470,6 @@ do mist.doScheduledFunctions() end -- end of mist.main - -- mist dyn add stuff for coroutines - local mistGpId = 7000 - local mistUnitId = 7000 - local mistDynAddIndex = 1 - - mist.nextGroupId = 1 - mist.nextUnitId = 1 - function mist.getNextUnitId() mist.nextUnitId = mist.nextUnitId + 1 if mist.nextUnitId > 6900 then @@ -607,13 +490,6 @@ do return lastUpdateTime end - local function groupSpawned(event) - -- dont need to add units spawned in at the start of the mission if mist is loaded in init line - if event.id == world.event.S_EVENT_BIRTH and timer.getTime0() < timer.getAbsTime() then - table.insert(tempSpawnedUnits,(event.initiator)) - end - end - function mist.dynAddStatic(staticObj) local newObj = {} newObj.groupId = staticObj.groupId @@ -890,9 +766,6 @@ do end --Modified Slmod task scheduler, superior to timer.scheduleFunction - - local Tasks = {} - local task_id = 0 --[[ mist.scheduleFunction: int id = mist.scheduleFunction(f function, vars table, t number, rep number, st number) id - integer id of this function task @@ -964,8 +837,6 @@ do end end - local idNum = 0 - -- Simplified event handler function mist.addEventHandler(f) --id is optional! local handler = {} @@ -989,925 +860,194 @@ do return false end - mist.addEventHandler(groupSpawned) - -- mist.scheduleFunction(checkSpawnedEvents, {}, timer.getTime() + 5, 1) - -end - ---- Utility functions. --- E.g. conversions between units etc. --- @section utils -mist.utils = {} - ---- Converts angle in radians to degrees. --- @param angle angle in radians --- @return angle in degrees -function mist.utils.toDegree (angle) - return angle*180/math.pi -end - ---- Converts angle in degrees to radians. --- @param angle angle in degrees --- @return angle in degrees -function mist.utils.toRadian (angle) - return angle*math.pi/180 -end - ---- Converts meters to nautical miles. --- @param meters distance in meters --- @return distance in nautical miles -function mist.utils.metersToNM (meters) - return meters/1852 -end - ---- Converts meters to feet. --- @param meters distance in meters --- @return distance in feet -function mist.utils.metersToFeet (meters) - return meters/0.3048 -end - ---- Converts nautical miles to meters. --- @param nm distance in nautical miles --- @return distance in meters -function mist.utils.NMToMeters (nm) - return NM*1852 -end - ---- Converts feet to meters. --- @param feet distance in feet --- @return distance in meters -function mist.utils.feetToMeters (feet) - return feet*0.3048 -end - ---- Converts meters per second to knots. --- @param mps speed in m/s --- @return speed in knots -function mist.utils.mpsToKnots (mps) - return mps*3600/1852 -end - ---- Converts meters per second to kilometers per hour. --- @param mps speed in m/s --- @return speed in km/h -function mist.utils.mpsToKmph (mps) - return mps*3.6 -end - ---- Converts knots to meters per second. --- @param knots speed in knots --- @return speed in m/s -function mist.utils.knotsToMps (knots) - return knots*1852/3600 -end - ---- Converts kilometers per hour to meters per second. --- @param kmph speed in km/h --- @return speed in m/s -function mist.utils.kmphToMps (kmph) - return kmph/3.6 -end - ---- Converts a Vec3 to a Vec2. --- @tparam Vec3 vec the 3D vector --- @return vector converted to Vec2 -function mist.utils.makeVec2(vec) - if vec.z then - return {x = vec.x, y = vec.z} - else - return {x = vec.x, y = vec.y} -- it was actually already vec2. - end -end - ---- Converts a Vec2 to a Vec3. --- @tparam Vec2 vec the 2D vector --- @param y optional new y axis (altitude) value. If omitted it's 0. -function mist.utils.makeVec3(vec, y) - if not vec.z then - if vec.alt and not y then - y = vec.alt - elseif not y then - y = 0 - end - return {x = vec.x, y = y, z = vec.y} - else - return {x = vec.x, y = vec.y, z = vec.z} -- it was already Vec3, actually. - end -end - ---- Converts a Vec2 to a Vec3 using ground level as altitude. --- The ground level at the specific point is used as altitude (y-axis) --- for the new vector. Optionally a offset can be specified. --- @tparam Vec2 vec the 2D vector --- @param[opt] offset offset to be applied to the ground level --- @return new 3D vector -function mist.utils.makeVec3GL(vec, offset) - local adj = offset or 0 - - if not vec.z then - return {x = vec.x, y = (land.getHeight(vec) + adj), z = vec.y} - else - return {x = vec.x, y = (land.getHeight({x = vec.x, y = vec.z}) + adj), z = vec.z} - end -end - ---- Returns the center of a zone as Vec3. --- @tparam string|table zone trigger zone name or table --- @treturn Vec3 center of the zone -function mist.utils.zoneToVec3 (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 - ---- Returns heading-error corrected direction. --- True-north corrected direction from point along vector vec. --- @tparam Vec3 vec --- @tparam Vec2 point --- @return heading-error corrected direction from point. -function mist.utils.getDir(vec, point) - local dir = math.atan2(vec.z, vec.x) - if point then - dir = dir + mist.getNorthCorrection(point) - end - if dir < 0 then - dir = dir + 2 * math.pi -- put dir in range of 0 to 2*pi - end - return dir -end - ---- Returns distance in meters between two points. --- @tparam Vec2|Vec3 point1 first point --- @tparam Vec2|Vec3 point2 second point --- @treturn number distance between given points. -function mist.utils.get2DDist(point1, point2) - point1 = mist.utils.makeVec3(point1) - point2 = mist.utils.makeVec3(point2) - return mist.vec.mag({x = point1.x - point2.x, y = 0, z = point1.z - point2.z}) -end - ---- Returns distance in meters between two points in 3D space. --- @tparam Vec3 point1 first point --- @tparam Vec3 point2 second point --- @treturn number distancen between given points in 3D space. -function mist.utils.get3DDist(point1, point2) - return mist.vec.mag({x = point1.x - point2.x, y = point1.y - point2.y, z = point1.z - point2.z}) -end - ---- Creates a waypoint from a vector. --- @tparam Vec2|Vec3 vec position of the new waypoint --- @treturn Waypoint a new waypoint to be used inside paths. -function mist.utils.vecToWP(vec) - local newWP = {} - newWP.x = vec.x - newWP.y = vec.y - if vec.z then - newWP.alt = vec.y - newWP.y = vec.z - else - newWP.alt = land.getHeight({x = vec.x, y = vec.y}) - end - return newWP -end - ---- Creates a waypoint from a unit. --- This function also considers the units speed. --- The alt_type of this waypoint is set to "BARO". --- @tparam Unit unit Unit whose position and speed will be used. --- @treturn Waypoint new waypoint. -function mist.utils.unitToWP(unit) - if type(unit) == 'string' then - if Unit.getByName(unit) then - unit = Unit.getByName(unit) - end - end - if unit:isExist() == true then - local new = mist.utils.vecToWP(unit:getPosition().p) - new.speed = mist.vec.mag(unit:getVelocity()) - new.alt_type = "BARO" - - return new - end - return false -end - ---- Creates a deep copy of a object. --- Usually this object is a table. --- See also: from http://lua-users.org/wiki/CopyTable --- @param object object to copy --- @return copy of object -function mist.utils.deepCopy(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 - return _copy(object) -end - ---- Simple rounding function. --- From http://lua-users.org/wiki/SimpleRound --- use negative idp for rounding ahead of decimal place, positive for rounding after decimal place --- @tparam number num number to round --- @param idp -function mist.utils.round(num, idp) - local mult = 10^(idp or 0) - return math.floor(num * mult + 0.5) / mult -end - ---- Rounds all numbers inside a table. --- @tparam table tbl table in which to round numbers --- @param idp -function mist.utils.roundTbl(tbl, idp) - for id, val in pairs(tbl) do - if type(val) == 'number' then - tbl[id] = mist.utils.round(val, idp) - end - end - return tbl -end - ---- Executes the given string. --- borrowed from Slmod --- @tparam string s string containing LUA code. --- @treturn boolean true if successfully executed, false otherwise -function mist.utils.dostring(s) - local f, err = loadstring(s) - if f then - return true, f() - else - return false, err - end -end - ---- Checks a table's types. --- This function checks a tables types against a specifically forged type table. --- @param fname --- @tparam table type_tbl --- @tparam table var_tbl --- @usage -- specifically forged type table --- type_tbl = { --- {'table', 'number'}, --- 'string', --- 'number', --- 'number', --- {'string','nil'}, --- {'number', 'nil'} --- } --- -- my_tbl index 1 must be a table or a number; --- -- index 2, a string; index 3, a number; --- -- index 4, a number; index 5, either a string or nil; --- -- and index 6, either a number or nil. --- mist.utils.typeCheck(type_tbl, my_tb) --- @return true if table passes the check, false otherwise. -function mist.utils.typeCheck(fname, type_tbl, var_tbl) - --env.info('type check') - for type_key, type_val in pairs(type_tbl) do - --print('type_key:') - --print(type_key) - --print('type_val:') - --print(type_val) - - --type_key can be a table of accepted keys- so try to find one that is not nil - local type_key_str = '' - local act_key = type_key -- actual key within var_tbl - necessary to use for multiple possible key variables. Initialize to type_key - if type(type_key) == 'table' then - - for i = 1, #type_key do - if i ~= 1 then - type_key_str = type_key_str .. '/' - end - type_key_str = type_key_str .. tostring(type_key[i]) - if var_tbl[type_key[i]] ~= nil then - act_key = type_key[i] -- found a non-nil entry, make act_key now this val. - end - end + -- acc- the accuracy of each easting/northing. 0, 1, 2, 3, 4, or 5. + function mist.tostringMGRS(MGRS, acc) + if acc == 0 then + return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph else - type_key_str = tostring(type_key) + return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph .. ' ' .. string.format('%0' .. acc .. 'd', mist.utils.round(MGRS.Easting/(10^(5-acc)), 0)) + .. ' ' .. string.format('%0' .. acc .. 'd', mist.utils.round(MGRS.Northing/(10^(5-acc)), 0)) end + end - local err_msg = 'Error in function ' .. fname .. ', parameter "' .. type_key_str .. '", expected: ' - local passed_check = false + --[[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. + ]] + function mist.tostringLL(lat, lon, acc, DMS) - if type(type_tbl[type_key]) == 'table' then - --print('err_msg, before and after:') - --print(err_msg) - for j = 1, #type_tbl[type_key] do - - if j == 1 then - err_msg = err_msg .. type_tbl[type_key][j] - else - err_msg = err_msg .. ' or ' .. type_tbl[type_key][j] - end - - if type(var_tbl[act_key]) == type_tbl[type_key][j] then - passed_check = true - end - end - --print(err_msg) + local latHemi, lonHemi + if lat > 0 then + latHemi = 'N' else - --print('err_msg, before and after:') - --print(err_msg) - err_msg = err_msg .. type_tbl[type_key] - --print(err_msg) - if type(var_tbl[act_key]) == type_tbl[type_key] then - passed_check = true + 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 = mist.utils.round((oldLatMin - latMin)*60, acc) + + local oldLonMin = lonMin + lonMin = math.floor(lonMin) + local lonSec = mist.utils.round((oldLonMin - lonMin)*60, acc) + + if latSec == 60 then + latSec = 0 + latMin = latMin + 1 end - end - - if not passed_check then - err_msg = err_msg .. ', got ' .. type(var_tbl[act_key]) - return false, err_msg - end - end - return true -end - ---- Serializes the give variable to a string. --- borrowed from slmod --- @param var variable to serialize --- @treturn string variable serialized to string -function mist.utils.basicSerialize(var) - if var == nil then - return "\"\"" - else - if ((type(var) == 'number') or - (type(var) == 'boolean') or - (type(var) == 'function') or - (type(var) == 'table') or - (type(var) == 'userdata') ) then - return tostring(var) - elseif type(var) == 'string' then - var = string.format('%q', var) - return var - end - end -end - ---- Serialize value --- borrowed from slmod (serialize_slmod) --- @param name --- @param value value to serialize --- @param level -function mist.utils.serialize(name, value, level) - --Based on ED's serialize_simple2 - local function basicSerialize(o) - if type(o) == "number" then - return tostring(o) - elseif type(o) == "boolean" then - return tostring(o) - else -- assume it is a string - return mist.utils.basicSerialize(o) - end - end - - local function serializeToTbl(name, value, level) - local var_str_tbl = {} - if level == nil then level = "" end - if level ~= "" then level = level.." " end - - table.insert(var_str_tbl, level .. name .. " = ") - - if type(value) == "number" or type(value) == "string" or type(value) == "boolean" then - table.insert(var_str_tbl, basicSerialize(value) .. ",\n") - elseif type(value) == "table" then - table.insert(var_str_tbl, "\n"..level.."{\n") - - for k,v in pairs(value) do -- serialize its fields - local key - if type(k) == "number" then - key = string.format("[%s]", k) - else - key = string.format("[%q]", k) - end - - table.insert(var_str_tbl, mist.utils.serialize(key, v, level.." ")) - + if lonSec == 60 then + lonSec = 0 + lonMin = lonMin + 1 end - if level == "" then - table.insert(var_str_tbl, level.."} -- end of "..name.."\n") + local secFrmtStr -- create the formatting string for the seconds place + if acc <= 0 then -- no decimal place. + secFrmtStr = '%02d' else - table.insert(var_str_tbl, level.."}, -- end of "..name.."\n") - + local width = 3 + acc -- 01.310 - that's a width of 6, for example. + secFrmtStr = '%0' .. width .. '.' .. acc .. 'f' end - else - env.info("Cannot serialize a "..type(value)) - end - return var_str_tbl - end - local t_str = serializeToTbl(name, value, level) + 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 - return table.concat(t_str) -end + else -- degrees, decimal minutes. + latMin = mist.utils.round(latMin, acc) + lonMin = mist.utils.round(lonMin, acc) ---- Serialize value supporting cycles. --- borrowed from slmod (serialize_wcycles) --- @param name --- @param value value to serialize --- @param saved -function mist.utils.serializeWithCycles(name, value, saved) - --mostly straight out of Programming in Lua - local function basicSerialize(o) - if type(o) == "number" then - return tostring(o) - elseif type(o) == "boolean" then - return tostring(o) - else -- assume it is a string - return mist.utils.basicSerialize(o) - end - end + if latMin == 60 then + latMin = 0 + latDeg = latDeg + 1 + end - local t_str = {} - saved = saved or {} -- initial value - if ((type(value) == 'string') or (type(value) == 'number') or (type(value) == 'table') or (type(value) == 'boolean')) then - table.insert(t_str, name .. " = ") - if type(value) == "number" or type(value) == "string" or type(value) == "boolean" then - table.insert(t_str, basicSerialize(value) .. "\n") - else + if lonMin == 60 then + lonMin = 0 + lonDeg = lonDeg + 1 + end - if saved[value] then -- value already saved? - table.insert(t_str, saved[value] .. "\n") + local minFrmtStr -- create the formatting string for the minutes place + if acc <= 0 then -- no decimal place. + minFrmtStr = '%02d' else - saved[value] = name -- save name for next time - table.insert(t_str, "{}\n") - for k,v in pairs(value) do -- save its fields - local fieldname = string.format("%s[%s]", name, basicSerialize(k)) - table.insert(t_str, mist.utils.serializeWithCycles(fieldname, v, saved)) - end - end - end - return table.concat(t_str) - else - return "" - end -end - ---- Serialize a table to a single line string. --- serialization of a table all on a single line, no comments, made to replace old get_table_string function --- borrowed from slmod --- @tparam table tbl table to serialize. --- @treturn string string containing serialized table -function mist.utils.oneLineSerialize(tbl) - if type(tbl) == 'table' then --function only works for tables! - - local tbl_str = {} - - tbl_str[#tbl_str + 1] = '{ ' - - for ind,val in pairs(tbl) do -- serialize its fields - if type(ind) == "number" then - tbl_str[#tbl_str + 1] = '[' - tbl_str[#tbl_str + 1] = tostring(ind) - tbl_str[#tbl_str + 1] = '] = ' - else --must be a string - tbl_str[#tbl_str + 1] = '[' - tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(ind) - tbl_str[#tbl_str + 1] = '] = ' + local width = 3 + acc -- 01.310 - that's a width of 6, for example. + minFrmtStr = '%0' .. width .. '.' .. acc .. 'f' end - if ((type(val) == 'number') or (type(val) == 'boolean')) then - tbl_str[#tbl_str + 1] = tostring(val) - tbl_str[#tbl_str + 1] = ', ' - elseif type(val) == 'string' then - tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(val) - tbl_str[#tbl_str + 1] = ', ' - elseif type(val) == 'nil' then -- won't ever happen, right? - tbl_str[#tbl_str + 1] = 'nil, ' - elseif type(val) == 'table' then - tbl_str[#tbl_str + 1] = mist.utils.oneLineSerialize(val) - tbl_str[#tbl_str + 1] = ', ' --I think this is right, I just added it - else - env.info('unable to serialize value type ' .. mist.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind)) - end + return string.format('%02d', latDeg) .. ' ' .. string.format(minFrmtStr, latMin) .. '\'' .. latHemi .. ' ' + .. string.format('%02d', lonDeg) .. ' ' .. string.format(minFrmtStr, lonMin) .. '\'' .. lonHemi end - tbl_str[#tbl_str + 1] = '}' - return table.concat(tbl_str) - end -end - ---- Returns table in a easy readable string representation. --- this function is not meant for serialization because it uses --- newlines for better readability. --- @param tbl table to show --- @param loc --- @param indent --- @param tableshow_tbls --- @return human readable string representation of given table -function mist.utils.tableShow(tbl, loc, indent, tableshow_tbls) --based on serialize_slmod, this is a _G serialization - tableshow_tbls = tableshow_tbls or {} --create table of tables - loc = loc or "" - indent = indent or "" - if type(tbl) == 'table' then --function only works for tables! - tableshow_tbls[tbl] = loc - - local tbl_str = {} - - tbl_str[#tbl_str + 1] = indent .. '{\n' - - for ind,val in pairs(tbl) do -- serialize its fields - if type(ind) == "number" then - tbl_str[#tbl_str + 1] = indent - tbl_str[#tbl_str + 1] = loc .. '[' - tbl_str[#tbl_str + 1] = tostring(ind) - tbl_str[#tbl_str + 1] = '] = ' - else - tbl_str[#tbl_str + 1] = indent - tbl_str[#tbl_str + 1] = loc .. '[' - tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(ind) - tbl_str[#tbl_str + 1] = '] = ' - end - - if ((type(val) == 'number') or (type(val) == 'boolean')) then - tbl_str[#tbl_str + 1] = tostring(val) - tbl_str[#tbl_str + 1] = ',\n' - elseif type(val) == 'string' then - tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(val) - tbl_str[#tbl_str + 1] = ',\n' - elseif type(val) == 'nil' then -- won't ever happen, right? - tbl_str[#tbl_str + 1] = 'nil,\n' - elseif type(val) == 'table' then - if tableshow_tbls[val] then - tbl_str[#tbl_str + 1] = tostring(val) .. ' already defined: ' .. tableshow_tbls[val] .. ',\n' - else - tableshow_tbls[val] = loc .. '[' .. mist.utils.basicSerialize(ind) .. ']' - tbl_str[#tbl_str + 1] = tostring(val) .. ' ' - tbl_str[#tbl_str + 1] = mist.utils.tableShow(val, loc .. '[' .. mist.utils.basicSerialize(ind).. ']', indent .. ' ', tableshow_tbls) - tbl_str[#tbl_str + 1] = ',\n' - end - elseif type(val) == 'function' then - if debug and debug.getinfo then - local fcnname = tostring(val) - local info = debug.getinfo(val, "S") - if info.what == "C" then - tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', C function') .. ',\n' - else - if (string.sub(info.source, 1, 2) == [[./]]) then - tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', defined in (' .. info.linedefined .. '-' .. info.lastlinedefined .. ')' .. info.source) ..',\n' - else - tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', defined in (' .. info.linedefined .. '-' .. info.lastlinedefined .. ')') ..',\n' - end - end - - else - tbl_str[#tbl_str + 1] = 'a function,\n' - end - else - tbl_str[#tbl_str + 1] = 'unable to serialize value type ' .. mist.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind) - end - end - - tbl_str[#tbl_str + 1] = indent .. '}' - return table.concat(tbl_str) - end -end - ---- Debug functions --- @section mist.debug -mist.debug = {} - ---- Dumps the global table _G. --- This dumps the global table _G to a file in --- the DCS\Logs directory. --- This function requires you to disable script sanitization --- in $DCS_ROOT\Scripts\MissionScripting.lua to access lfs and io --- libraries. --- @param fname -function mist.debug.dump_G(fname) - if lfs and io then - local fdir = lfs.writedir() .. [[Logs\]] .. fname - local f = io.open(fdir, 'w') - f:write(mist.utils.tableShow(_G)) - f:close() - local errmsg = 'mist.debug.dump_G wrote data to ' .. fdir - env.info(errmsg) - trigger.action.outText(errmsg, 10) - else - local errmsg = 'Error: insufficient libraries to run mist.debug.dump_G, you must disable the sanitization of the io and lfs libraries in ./Scripts/MissionScripting.lua' - env.info(errmsg) - trigger.action.outText(errmsg, 10) - end -end - ---- Write debug data to file. --- This function requires you to disable script sanitization --- in $DCS_ROOT\Scripts\MissionScripting.lua to access lfs and io --- libraries. --- @param fcn --- @param fcnVars --- @param fname -function mist.debug.writeData(fcn, fcnVars, fname) - if lfs and io then - local fdir = lfs.writedir() .. [[Logs\]] .. fname - local f = io.open(fdir, 'w') - f:write(fcn(unpack(fcnVars, 1, table.maxn(fcnVars)))) - f:close() - local errmsg = 'mist.debug.writeData wrote data to ' .. fdir - env.info(errmsg) - trigger.action.outText(errmsg, 10) - else - local errmsg = 'Error: insufficient libraries to run mist.debug.writeData, you must disable the sanitization of the io and lfs libraries in ./Scripts/MissionScripting.lua' - env.info(errmsg) - trigger.action.outText(errmsg, 10) - end -end - ---- Write mist databases to file. --- This function requires you to disable script sanitization --- in $DCS_ROOT\Scripts\MissionScripting.lua to access lfs and io --- libraries. -function mist.debug.dumpDBs() - for DBname, DB in pairs(mist.DBs) do - if type(DB) == 'table' and type(DBname) == 'string' then - mist.debug.writeData(mist.utils.serialize, {DBname, DB}, 'mist_DBs_' .. DBname .. '.lua') - end - end -end - ---- 3D Vector functions --- @section mist.vec -mist.vec = {} - ---- Vector addition. --- @tparam Vec3 vec1 first vector --- @tparam Vec3 vec2 second vector --- @treturn Vec3 new vector, sum of vec1 and vec2. -function mist.vec.add(vec1, vec2) - return {x = vec1.x + vec2.x, y = vec1.y + vec2.y, z = vec1.z + vec2.z} -end - ---- Vector substraction. --- @tparam Vec3 vec1 first vector --- @tparam Vec3 vec2 second vector --- @treturn Vec3 new vector, vec2 substracted from vec1. -function mist.vec.sub(vec1, vec2) - return {x = vec1.x - vec2.x, y = vec1.y - vec2.y, z = vec1.z - vec2.z} -end - ---- Vector scalar multiplication. --- @tparam Vec3 vec vector to multiply --- @tparam number mult scalar multiplicator --- @treturn Vec3 new vector multiplied with the given scalar -function mist.vec.scalarMult(vec, mult) - return {x = vec.x*mult, y = vec.y*mult, z = vec.z*mult} -end - -mist.vec.scalar_mult = mist.vec.scalarMult - ---- Vector dot product. --- @tparam Vec3 vec1 first vector --- @tparam Vec3 vec2 second vector --- @treturn number dot product of given vectors -function mist.vec.dp (vec1, vec2) - return vec1.x*vec2.x + vec1.y*vec2.y + vec1.z*vec2.z -end - ---- Vector cross product. --- @tparam Vec3 vec1 first vector --- @tparam Vec3 vec2 second vector --- @treturn Vec3 new vector, cross product of vec1 and vec2. -function mist.vec.cp(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 - ---- Vector magnitude --- @tparam Vec3 vec vector --- @treturn number magnitude of vector vec -function mist.vec.mag(vec) - return (vec.x^2 + vec.y^2 + vec.z^2)^0.5 -end - ---- Unit vector --- @tparam Vec3 vec --- @treturn Vec3 unit vector of vec -function mist.vec.getUnitVec(vec) - local mag = mist.vec.mag(vec) - return { x = vec.x/mag, y = vec.y/mag, z = vec.z/mag } -end - ---- Rotate vector. --- @tparam Vec2 vec2 to rotoate --- @tparam number theta --- @return Vec2 rotated vector. -function mist.vec.rotateVec2(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. -function mist.tostringMGRS(MGRS, acc) - if acc == 0 then - return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph - else - return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph .. ' ' .. string.format('%0' .. acc .. 'd', mist.utils.round(MGRS.Easting/(10^(5-acc)), 0)) - .. ' ' .. string.format('%0' .. acc .. 'd', mist.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. -]] -function mist.tostringLL(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 = mist.utils.round((oldLatMin - latMin)*60, acc) - - local oldLonMin = lonMin - lonMin = math.floor(lonMin) - local lonSec = mist.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 = mist.utils.round(latMin, acc) - lonMin = mist.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: 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.]] -function mist.tostringBR(az, dist, alt, metric) - az = mist.utils.round(mist.utils.toDegree(az), 0) + function mist.tostringBR(az, dist, alt, metric) + az = mist.utils.round(mist.utils.toDegree(az), 0) - if metric then - dist = mist.utils.round(dist/1000, 0) - else - dist = mist.utils.round(mist.utils.metersToNM(dist), 0) - end - - local s = string.format('%03d', az) .. ' for ' .. dist - - if alt then if metric then - s = s .. ' at ' .. mist.utils.round(alt, 0) + dist = mist.utils.round(dist/1000, 0) else - s = s .. ' at ' .. mist.utils.round(mist.utils.metersToFeet(alt), 0) + dist = mist.utils.round(mist.utils.metersToNM(dist), 0) end - end - return s -end -function mist.getNorthCorrection(gPoint) --gets the correction needed for true north - local point = mist.utils.deepCopy(gPoint) - 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 + local s = string.format('%03d', az) .. ' for ' .. dist -function mist.getUnitSkill(unitName) - if Unit.getByName(unitName) and Unit.getByName(unitName):isExist() == true then - local lunit = Unit.getByName(unitName) - for name, data in pairs(mist.DBs.unitsByName) do - if name == unitName and data.type == lunit:getTypeName() and data.unitId == lunit:getID() and data.skill then - return data.skill + if alt then + if metric then + s = s .. ' at ' .. mist.utils.round(alt, 0) + else + s = s .. ' at ' .. mist.utils.round(mist.utils.metersToFeet(alt), 0) end end - end - return false -end - - -function mist.getGroupPoints(groupIdent) -- if groupname exists in env.mission, then returns table of the group's points in numerical order, such as: { [1] = {x = 299435.224, y = -1146632.6773}, [2] = { x = 663324.6563, y = 322424.1112}} - -- 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 = mist.DBs.MEgroupsByName[groupIdent].groupId + return s 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 - if not point.point then - points[point_num] = { x = point.x, y = point.y } - else - points[point_num] = point.point --it's possible that the ME could move to the point = Vec2 notation. + function mist.getNorthCorrection(gPoint) --gets the correction needed for true north + local point = mist.utils.deepCopy(gPoint) + 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 + + function mist.getUnitSkill(unitName) + if Unit.getByName(unitName) and Unit.getByName(unitName):isExist() == true then + local lunit = Unit.getByName(unitName) + for name, data in pairs(mist.DBs.unitsByName) do + if name == unitName and data.type == lunit:getTypeName() and data.unitId == lunit:getID() and data.skill then + return data.skill + end + end + end + return false + end + + function mist.getGroupPoints(groupIdent) -- if groupname exists in env.mission, then returns table of the group's points in numerical order, such as: { [1] = {x = 299435.224, y = -1146632.6773}, [2] = { x = 663324.6563, y = 322424.1112}} + -- 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 = mist.DBs.MEgroupsByName[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 + if not point.point then + points[point_num] = { x = point.x, y = point.y } + else + points[point_num] = point.point --it's possible that the ME could move to the point = Vec2 notation. + end end + return points 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 + 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 ---[[ table attitude = getAttitude(string unitname) -- will work on any unit, even if not an aircraft. + --[[ table attitude = getAttitude(string unitname) -- will work on any unit, even if not an aircraft. attitude = { Heading = number, -- in radians, range of 0 to 2*pi, relative to true north @@ -1926,631 +1066,193 @@ attitude = { } ]] -function mist.getAttitude(unit) - local unitpos = unit:getPosition() - if unitpos then + function mist.getAttitude(unit) + local unitpos = unit:getPosition() + if unitpos then - local Heading = math.atan2(unitpos.x.z, unitpos.x.x) + local Heading = math.atan2(unitpos.x.z, unitpos.x.x) - Heading = Heading + mist.getNorthCorrection(unitpos.p) - - if Heading < 0 then - Heading = Heading + 2*math.pi -- put heading in range of 0 to 2*pi - end - ---- heading complete.---- - - local Pitch = math.asin(unitpos.x.y) - ---- pitch complete.---- - - -- now get roll: - --maybe not the best way to do it, but it works. - - --first, make a vector that is perpendicular to y and unitpos.x with cross product - local cp = mist.vec.cp(unitpos.x, {x = 0, y = 1, z = 0}) - - --now, get dot product of of this cross product with unitpos.z - local dp = mist.vec.dp(cp, unitpos.z) - - --now get the magnitude of the roll (magnitude of the angle between two vectors is acos(vec1.vec2/|vec1||vec2|) - local Roll = math.acos(dp/(mist.vec.mag(cp)*mist.vec.mag(unitpos.z))) - - --now, have to get sign of roll. - -- by convention, making right roll positive - -- to get sign of roll, use the y component of unitpos.z. For right roll, y component is negative. - - if unitpos.z.y > 0 then -- left roll, flip the sign of the roll - Roll = -Roll - end - ---- roll complete. ---- - - --now, work on yaw, AoA, climb, and abs velocity - local Yaw - local AoA - local ClimbAngle - - -- get unit velocity - local unitvel = unit:getVelocity() - if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! - local AxialVel = {} --unit velocity transformed into aircraft axes directions - - --transform velocity components in direction of aircraft axes. - AxialVel.x = mist.vec.dp(unitpos.x, unitvel) - AxialVel.y = mist.vec.dp(unitpos.y, unitvel) - AxialVel.z = mist.vec.dp(unitpos.z, unitvel) - - --Yaw is the angle between unitpos.x and the x and z velocities - --define right yaw as positive - Yaw = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = 0, z = AxialVel.z})/mist.vec.mag({x = AxialVel.x, y = 0, z = AxialVel.z})) - - --now set correct direction: - if AxialVel.z > 0 then - Yaw = -Yaw - end - - -- AoA is angle between unitpos.x and the x and y velocities - AoA = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = AxialVel.y, z = 0})/mist.vec.mag({x = AxialVel.x, y = AxialVel.y, z = 0})) - - --now set correct direction: - if AxialVel.y > 0 then - AoA = -AoA - end - - ClimbAngle = math.asin(unitvel.y/mist.vec.mag(unitvel)) - end - return { Heading = Heading, Pitch = Pitch, Roll = Roll, Yaw = Yaw, AoA = AoA, ClimbAngle = ClimbAngle} - else - env.info('unit:getPosition() is nil!') - end -end - -function mist.getHeading(unit, rawHeading) - local unitpos = unit:getPosition() - if unitpos then - local Heading = math.atan2(unitpos.x.z, unitpos.x.x) - if not rawHeading then Heading = Heading + mist.getNorthCorrection(unitpos.p) - end - if Heading < 0 then - Heading = Heading + 2*math.pi -- put heading in range of 0 to 2*pi - end - return Heading - end -end -function mist.getPitch(unit) - local unitpos = unit:getPosition() - if unitpos then - return math.asin(unitpos.x.y) - end -end - -function mist.getRoll(unit) - local unitpos = unit:getPosition() - if unitpos then - -- now get roll: - --maybe not the best way to do it, but it works. - - --first, make a vector that is perpendicular to y and unitpos.x with cross product - local cp = mist.vec.cp(unitpos.x, {x = 0, y = 1, z = 0}) - - --now, get dot product of of this cross product with unitpos.z - local dp = mist.vec.dp(cp, unitpos.z) - - --now get the magnitude of the roll (magnitude of the angle between two vectors is acos(vec1.vec2/|vec1||vec2|) - local Roll = math.acos(dp/(mist.vec.mag(cp)*mist.vec.mag(unitpos.z))) - - --now, have to get sign of roll. - -- by convention, making right roll positive - -- to get sign of roll, use the y component of unitpos.z. For right roll, y component is negative. - - if unitpos.z.y > 0 then -- left roll, flip the sign of the roll - Roll = -Roll - end - return Roll - end -end - -function mist.getYaw(unit) - local unitpos = unit:getPosition() - if unitpos then - -- get unit velocity - local unitvel = unit:getVelocity() - if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! - local AxialVel = {} --unit velocity transformed into aircraft axes directions - - --transform velocity components in direction of aircraft axes. - AxialVel.x = mist.vec.dp(unitpos.x, unitvel) - AxialVel.y = mist.vec.dp(unitpos.y, unitvel) - AxialVel.z = mist.vec.dp(unitpos.z, unitvel) - - --Yaw is the angle between unitpos.x and the x and z velocities - --define right yaw as positive - local Yaw = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = 0, z = AxialVel.z})/mist.vec.mag({x = AxialVel.x, y = 0, z = AxialVel.z})) - - --now set correct direction: - if AxialVel.z > 0 then - Yaw = -Yaw + if Heading < 0 then + Heading = Heading + 2*math.pi -- put heading in range of 0 to 2*pi end - return Yaw - end - end -end + ---- heading complete.---- -function mist.getAoA(unit) - local unitpos = unit:getPosition() - if unitpos then - local unitvel = unit:getVelocity() - if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! - local AxialVel = {} --unit velocity transformed into aircraft axes directions + local Pitch = math.asin(unitpos.x.y) + ---- pitch complete.---- - --transform velocity components in direction of aircraft axes. - AxialVel.x = mist.vec.dp(unitpos.x, unitvel) - AxialVel.y = mist.vec.dp(unitpos.y, unitvel) - AxialVel.z = mist.vec.dp(unitpos.z, unitvel) + -- now get roll: + --maybe not the best way to do it, but it works. - -- AoA is angle between unitpos.x and the x and y velocities - local AoA = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = AxialVel.y, z = 0})/mist.vec.mag({x = AxialVel.x, y = AxialVel.y, z = 0})) + --first, make a vector that is perpendicular to y and unitpos.x with cross product + local cp = mist.vec.cp(unitpos.x, {x = 0, y = 1, z = 0}) - --now set correct direction: - if AxialVel.y > 0 then - AoA = -AoA + --now, get dot product of of this cross product with unitpos.z + local dp = mist.vec.dp(cp, unitpos.z) + + --now get the magnitude of the roll (magnitude of the angle between two vectors is acos(vec1.vec2/|vec1||vec2|) + local Roll = math.acos(dp/(mist.vec.mag(cp)*mist.vec.mag(unitpos.z))) + + --now, have to get sign of roll. + -- by convention, making right roll positive + -- to get sign of roll, use the y component of unitpos.z. For right roll, y component is negative. + + if unitpos.z.y > 0 then -- left roll, flip the sign of the roll + Roll = -Roll end - return AoA - end - end -end + ---- roll complete. ---- -function mist.getClimbAngle(unit) - local unitpos = unit:getPosition() - if unitpos then - local unitvel = unit:getVelocity() - if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! - return math.asin(unitvel.y/mist.vec.mag(unitvel)) - end - end -end + --now, work on yaw, AoA, climb, and abs velocity + local Yaw + local AoA + local ClimbAngle ---- mist.DBs: various tables acting as databases --- @section mist.DBs -mist.DBs = {} + -- get unit velocity + local unitvel = unit:getVelocity() + if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! + local AxialVel = {} --unit velocity transformed into aircraft axes directions ---- Mission data --- @table mist.DBs.missionData --- @field startTime mission start time --- @field theatre mission theatre/map e.g. Caucasus --- @field version mission version --- @field files mission resources -mist.DBs.missionData = {} -if env.mission then + --transform velocity components in direction of aircraft axes. + AxialVel.x = mist.vec.dp(unitpos.x, unitvel) + AxialVel.y = mist.vec.dp(unitpos.y, unitvel) + AxialVel.z = mist.vec.dp(unitpos.z, unitvel) - mist.DBs.missionData['startTime'] = env.mission.start_time - mist.DBs.missionData['theatre'] = env.mission.theatre - mist.DBs.missionData['version'] = env.mission.version - mist.DBs.missionData['files'] = {} - if type(env.mission.resourceCounter) == 'table' then - for fIndex, fData in pairs (env.mission.resourceCounter) do - mist.DBs.missionData.files[#mist.DBs.missionData.files + 1] = mist.utils.deepCopy(fIndex) - end - end - mist.DBs.missionData['bullseye'] = {['red'] = {}, ['blue'] = {}} -- if we add more coalition specific data then bullsye should be categorized by coaliton. For now its just the bullseye table - mist.DBs.missionData.bullseye.red['x'] = env.mission.coalition.red.bullseye.x --should it be point.x? - mist.DBs.missionData.bullseye.red['y'] = env.mission.coalition.red.bullseye.y - mist.DBs.missionData.bullseye.blue['x'] = env.mission.coalition.blue.bullseye.x - mist.DBs.missionData.bullseye.blue['y'] = env.mission.coalition.blue.bullseye.y + --Yaw is the angle between unitpos.x and the x and z velocities + --define right yaw as positive + Yaw = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = 0, z = AxialVel.z})/mist.vec.mag({x = AxialVel.x, y = 0, z = AxialVel.z})) -end - -mist.DBs.zonesByName = {} -mist.DBs.zonesByNum = {} - - -if env.mission.triggers and env.mission.triggers.zones then - for zone_ind, zone_data in pairs(env.mission.triggers.zones) do - if type(zone_data) == 'table' then - local zone = mist.utils.deepCopy(zone_data) - zone['point'] = {} -- point is used by SSE - zone['point']['x'] = zone_data.x - zone['point']['y'] = 0 - zone['point']['z'] = zone_data.y - - mist.DBs.zonesByName[zone_data.name] = zone - mist.DBs.zonesByNum[#mist.DBs.zonesByNum + 1] = mist.utils.deepCopy(zone) --[[deepcopy so that the zone in zones_by_name and the zone in - zones_by_num se are different objects.. don't want them linked.]] - end - end -end - -mist.DBs.navPoints = {} -mist.DBs.units = {} ---Build mist.db.units and mist.DBs.navPoints -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 - mist.DBs.units[coa_name] = {} - - -- build nav points DB - mist.DBs.navPoints[coa_name] = {} - if coa_data.nav_points then --navpoints - --mist.debug.writeData (mist.utils.serialize,{'NavPoints',coa_data.nav_points}, 'NavPoints.txt') - for nav_ind, nav_data in pairs(coa_data.nav_points) do - - if type(nav_data) == 'table' then - mist.DBs.navPoints[coa_name][nav_ind] = mist.utils.deepCopy(nav_data) - - mist.DBs.navPoints[coa_name][nav_ind]['name'] = nav_data.callsignStr -- name is a little bit more self-explanatory. - mist.DBs.navPoints[coa_name][nav_ind]['point'] = {} -- point is used by SSE, support it. - mist.DBs.navPoints[coa_name][nav_ind]['point']['x'] = nav_data.x - mist.DBs.navPoints[coa_name][nav_ind]['point']['y'] = 0 - mist.DBs.navPoints[coa_name][nav_ind]['point']['z'] = nav_data.y - end - end - end - if coa_data.country then --there is a country table - for cntry_id, cntry_data in pairs(coa_data.country) do - - local countryName = string.lower(cntry_data.name) - mist.DBs.units[coa_name][countryName] = {} - mist.DBs.units[coa_name][countryName]["countryId"] = cntry_data.id - - if type(cntry_data) == 'table' then --just making sure - - 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" or obj_type_name == "static" then --should be an unncessary check - - local category = obj_type_name - - 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! - - mist.DBs.units[coa_name][countryName][category] = {} - - for group_num, group_data in pairs(obj_type_data.group) do - - if group_data and group_data.units and type(group_data.units) == 'table' then --making sure again- this is a valid group - - mist.DBs.units[coa_name][countryName][category][group_num] = {} - local groupName = group_data.name - if env.mission.version > 7 then - groupName = env.getValueDictByKey(groupName) - end - mist.DBs.units[coa_name][countryName][category][group_num]["groupName"] = groupName - mist.DBs.units[coa_name][countryName][category][group_num]["groupId"] = group_data.groupId - mist.DBs.units[coa_name][countryName][category][group_num]["category"] = category - mist.DBs.units[coa_name][countryName][category][group_num]["coalition"] = coa_name - mist.DBs.units[coa_name][countryName][category][group_num]["country"] = countryName - mist.DBs.units[coa_name][countryName][category][group_num]["countryId"] = cntry_data.id - mist.DBs.units[coa_name][countryName][category][group_num]["startTime"] = group_data.start_time - mist.DBs.units[coa_name][countryName][category][group_num]["task"] = group_data.task - mist.DBs.units[coa_name][countryName][category][group_num]["hidden"] = group_data.hidden - - mist.DBs.units[coa_name][countryName][category][group_num]["units"] = {} - - mist.DBs.units[coa_name][countryName][category][group_num]["radioSet"] = group_data.radioSet - mist.DBs.units[coa_name][countryName][category][group_num]["uncontrolled"] = group_data.uncontrolled - mist.DBs.units[coa_name][countryName][category][group_num]["frequency"] = group_data.frequency - mist.DBs.units[coa_name][countryName][category][group_num]["modulation"] = group_data.modulation - - for unit_num, unit_data in pairs(group_data.units) do - local units_tbl = mist.DBs.units[coa_name][countryName][category][group_num]["units"] --pointer to the units table for this group - - units_tbl[unit_num] = {} - if env.mission.version > 7 then - units_tbl[unit_num]["unitName"] = env.getValueDictByKey(unit_data.name) - else - units_tbl[unit_num]["unitName"] = unit_data.name - end - units_tbl[unit_num]["type"] = unit_data.type - units_tbl[unit_num]["skill"] = unit_data.skill --will be nil for statics - units_tbl[unit_num]["unitId"] = unit_data.unitId - units_tbl[unit_num]["category"] = category - units_tbl[unit_num]["coalition"] = coa_name - units_tbl[unit_num]["country"] = countryName - units_tbl[unit_num]["countryId"] = cntry_data.id - units_tbl[unit_num]["heading"] = unit_data.heading - units_tbl[unit_num]["playerCanDrive"] = unit_data.playerCanDrive - units_tbl[unit_num]["alt"] = unit_data.alt - units_tbl[unit_num]["alt_type"] = unit_data.alt_type - units_tbl[unit_num]["speed"] = unit_data.speed - units_tbl[unit_num]["livery_id"] = unit_data.livery_id - if unit_data.point then --ME currently does not work like this, but it might one day - units_tbl[unit_num]["point"] = unit_data.point - else - units_tbl[unit_num]["point"] = {} - units_tbl[unit_num]["point"]["x"] = unit_data.x - units_tbl[unit_num]["point"]["y"] = unit_data.y - end - units_tbl[unit_num]['x'] = unit_data.x - units_tbl[unit_num]['y'] = unit_data.y - - units_tbl[unit_num]["callsign"] = unit_data.callsign - units_tbl[unit_num]["onboard_num"] = unit_data.onboard_num - units_tbl[unit_num]["hardpoint_racks"] = unit_data.hardpoint_racks - units_tbl[unit_num]["psi"] = unit_data.psi - - - units_tbl[unit_num]["groupName"] = groupName - units_tbl[unit_num]["groupId"] = group_data.groupId - - if unit_data.AddPropAircraft then - units_tbl[unit_num]["AddPropAircraft"] = unit_data.AddPropAircraft - end - - if category == 'static' then - units_tbl[unit_num]["categoryStatic"] = unit_data.category - units_tbl[unit_num]["shape_name"] = unit_data.shape_name - if unit_data.mass then - units_tbl[unit_num]["mass"] = unit_data.mass - end - - if unit_data.canCargo then - units_tbl[unit_num]["canCargo"] = unit_data.canCargo - end - end - - end --for unit_num, unit_data in pairs(group_data.units) do - end --if group_data and group_data.units then - 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 --if type(cntry_data) == 'table' then - 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 - -mist.DBs.unitsByName = {} -mist.DBs.unitsById = {} -mist.DBs.unitsByCat = {} - -mist.DBs.unitsByCat['helicopter'] = {} -- adding default categories -mist.DBs.unitsByCat['plane'] = {} -mist.DBs.unitsByCat['ship'] = {} -mist.DBs.unitsByCat['static'] = {} -mist.DBs.unitsByCat['vehicle'] = {} - -mist.DBs.unitsByNum = {} - -mist.DBs.groupsByName = {} -mist.DBs.groupsById = {} -mist.DBs.humansByName = {} -mist.DBs.humansById = {} - -mist.DBs.dynGroupsAdded = {} -- will be filled by mist.dbUpdate from dynamically spawned groups -mist.DBs.activeHumans = {} - -mist.DBs.aliveUnits = {} -- will be filled in by the "updateAliveUnits" coroutine in mist.main. - -mist.DBs.removedAliveUnits = {} -- will be filled in by the "updateAliveUnits" coroutine in mist.main. --- create mist.DBs.oldAliveUnits --- do --- local intermediate_alive_units = {} -- between 0 and 0.5 secs old --- local function make_old_alive_units() -- called every 0.5 secs, makes the old_alive_units DB which is just a copy of alive_units that is 0.5 to 1 sec old --- if intermediate_alive_units then --- mist.DBs.oldAliveUnits = mist.utils.deepCopy(intermediate_alive_units) --- end --- intermediate_alive_units = mist.utils.deepCopy(mist.DBs.aliveUnits) --- timer.scheduleFunction(make_old_alive_units, nil, timer.getTime() + 0.5) --- end - --- make_old_alive_units() --- end - - ---Build DBs -for coa_name, coa_data in pairs(mist.DBs.units) do - for cntry_name, cntry_data in pairs(coa_data) do - for category_name, category_data in pairs(cntry_data) do - if type(category_data) == 'table' then - for group_ind, group_data in pairs(category_data) do - if type(group_data) == 'table' and group_data.units and type(group_data.units) == 'table' and #group_data.units > 0 then -- OCD paradigm programming - mist.DBs.groupsByName[group_data.groupName] = mist.utils.deepCopy(group_data) - mist.DBs.groupsById[group_data.groupId] = mist.utils.deepCopy(group_data) - for unit_ind, unit_data in pairs(group_data.units) do - mist.DBs.unitsByName[unit_data.unitName] = mist.utils.deepCopy(unit_data) - mist.DBs.unitsById[unit_data.unitId] = mist.utils.deepCopy(unit_data) - - mist.DBs.unitsByCat[unit_data.category] = mist.DBs.unitsByCat[unit_data.category] or {} -- future-proofing against new categories... - table.insert(mist.DBs.unitsByCat[unit_data.category], mist.utils.deepCopy(unit_data)) - --print('inserting ' .. unit_data.unitName) - table.insert(mist.DBs.unitsByNum, mist.utils.deepCopy(unit_data)) - - if unit_data.skill and (unit_data.skill == "Client" or unit_data.skill == "Player") then - mist.DBs.humansByName[unit_data.unitName] = mist.utils.deepCopy(unit_data) - mist.DBs.humansById[unit_data.unitId] = mist.utils.deepCopy(unit_data) - --if Unit.getByName(unit_data.unitName) then - -- mist.DBs.activeHumans[unit_data.unitName] = mist.utils.deepCopy(unit_data) - -- mist.DBs.activeHumans[unit_data.unitName].playerName = Unit.getByName(unit_data.unitName):getPlayerName() - --end - end - end - end - end - end - end - end -end - --- mist unitID funcs -do - for id, idData in pairs(mist.DBs.unitsById) do - if idData.unitId > mist.nextUnitId then - mist.nextUnitId = mist.utils.deepCopy(idData.unitId) - end - if idData.groupId > mist.nextGroupId then - mist.nextGroupId = mist.utils.deepCopy(idData.groupId) - end - end -end - ---DynDBs -mist.DBs.MEunits = mist.utils.deepCopy(mist.DBs.units) -mist.DBs.MEunitsByName = mist.utils.deepCopy(mist.DBs.unitsByName) -mist.DBs.MEunitsById = mist.utils.deepCopy(mist.DBs.unitsById) -mist.DBs.MEunitsByCat = mist.utils.deepCopy(mist.DBs.unitsByCat) -mist.DBs.MEunitsByNum = mist.utils.deepCopy(mist.DBs.unitsByNum) -mist.DBs.MEgroupsByName = mist.utils.deepCopy(mist.DBs.groupsByName) -mist.DBs.MEgroupsById = mist.utils.deepCopy(mist.DBs.groupsById) -------------- - - -mist.DBs.deadObjects = {} - -do - local mt = {} - - function mt.__newindex(t, key, val) - local original_key = key --only for duplicate runtime IDs. - local key_ind = 1 - while mist.DBs.deadObjects[key] do - --print('duplicate runtime id of previously dead object- key: ' .. tostring(key)) - key = tostring(original_key) .. ' #' .. tostring(key_ind) - key_ind = key_ind + 1 - end - - if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then - --print('object found in alive_units') - val['objectData'] = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) - local pos = Object.getPosition(val.object) - if pos then - val['objectPos'] = pos.p - end - val['objectType'] = mist.DBs.aliveUnits[val.object.id_].category - - elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units - --print('object found in old_alive_units') - val['objectData'] = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) - local pos = Object.getPosition(val.object) - if pos then - val['objectPos'] = pos.p - end - val['objectType'] = mist.DBs.removedAliveUnits[val.object.id_].category - - else --attempt to determine if static object... - --print('object not found in alive units or old alive units') - local pos = Object.getPosition(val.object) - if pos then - local static_found = false - for ind, static in pairs(mist.DBs.unitsByCat['static']) do - if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... - --print('correlated dead static object to position') - val['objectData'] = static - val['objectPos'] = pos.p - val['objectType'] = 'static' - static_found = true - break - end - end - if not static_found then - val['objectPos'] = pos.p - val['objectType'] = 'building' - end - else - val['objectType'] = 'unknown' - end - end - rawset(t, key, val) - end - - setmetatable(mist.DBs.deadObjects, mt) -end - --- Event handler to start creating the dead_objects table -do - - local function addDeadObject(event) - if event.id == world.event.S_EVENT_DEAD or event.id == world.event.S_EVENT_CRASH then - if event.initiator and event.initiator.id_ and event.initiator.id_ > 0 then - - local id = event.initiator.id_ -- initial ID, could change if there is a duplicate id_ already dead. - local val = {object = event.initiator} -- the new entry in mist.DBs.deadObjects. - - local original_id = id --only for duplicate runtime IDs. - local id_ind = 1 - while mist.DBs.deadObjects[id] do - --print('duplicate runtime id of previously dead object- id: ' .. tostring(id)) - id = tostring(original_id) .. ' #' .. tostring(id_ind) - id_ind = id_ind + 1 + --now set correct direction: + if AxialVel.z > 0 then + Yaw = -Yaw end - if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then - --print('object found in alive_units') - val['objectData'] = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) - local pos = Object.getPosition(val.object) - if pos then - val['objectPos'] = pos.p - end - val['objectType'] = mist.DBs.aliveUnits[val.object.id_].category - --[[if mist.DBs.activeHumans[Unit.getName(val.object)] then - --trigger.action.outText('remove via death: ' .. Unit.getName(val.object),20) - mist.DBs.activeHumans[Unit.getName(val.object)] = nil - end]] - elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units - --print('object found in old_alive_units') - val['objectData'] = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) - local pos = Object.getPosition(val.object) - if pos then - val['objectPos'] = pos.p - end - val['objectType'] = mist.DBs.removedAliveUnits[val.object.id_].category + -- AoA is angle between unitpos.x and the x and y velocities + AoA = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = AxialVel.y, z = 0})/mist.vec.mag({x = AxialVel.x, y = AxialVel.y, z = 0})) - else --attempt to determine if static object... - --print('object not found in alive units or old alive units') - local pos = Object.getPosition(val.object) - if pos then - local static_found = false - for ind, static in pairs(mist.DBs.unitsByCat['static']) do - if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... - --print('correlated dead static object to position') - val['objectData'] = static - val['objectPos'] = pos.p - val['objectType'] = 'static' - static_found = true - break - end - end - if not static_found then - val['objectPos'] = pos.p - val['objectType'] = 'building' - end - else - val['objectType'] = 'unknown' - end + --now set correct direction: + if AxialVel.y > 0 then + AoA = -AoA end - mist.DBs.deadObjects[id] = val + ClimbAngle = math.asin(unitvel.y/mist.vec.mag(unitvel)) + end + return { Heading = Heading, Pitch = Pitch, Roll = Roll, Yaw = Yaw, AoA = AoA, ClimbAngle = ClimbAngle} + else + env.info('unit:getPosition() is nil!') + end + end + + function mist.getHeading(unit, rawHeading) + local unitpos = unit:getPosition() + if unitpos then + local Heading = math.atan2(unitpos.x.z, unitpos.x.x) + if not rawHeading then + Heading = Heading + mist.getNorthCorrection(unitpos.p) + end + if Heading < 0 then + Heading = Heading + 2*math.pi -- put heading in range of 0 to 2*pi + end + return Heading + end + end + + function mist.getPitch(unit) + local unitpos = unit:getPosition() + if unitpos then + return math.asin(unitpos.x.y) + end + end + + function mist.getRoll(unit) + local unitpos = unit:getPosition() + if unitpos then + -- now get roll: + --maybe not the best way to do it, but it works. + + --first, make a vector that is perpendicular to y and unitpos.x with cross product + local cp = mist.vec.cp(unitpos.x, {x = 0, y = 1, z = 0}) + + --now, get dot product of of this cross product with unitpos.z + local dp = mist.vec.dp(cp, unitpos.z) + + --now get the magnitude of the roll (magnitude of the angle between two vectors is acos(vec1.vec2/|vec1||vec2|) + local Roll = math.acos(dp/(mist.vec.mag(cp)*mist.vec.mag(unitpos.z))) + + --now, have to get sign of roll. + -- by convention, making right roll positive + -- to get sign of roll, use the y component of unitpos.z. For right roll, y component is negative. + + if unitpos.z.y > 0 then -- left roll, flip the sign of the roll + Roll = -Roll + end + return Roll + end + end + + function mist.getYaw(unit) + local unitpos = unit:getPosition() + if unitpos then + -- get unit velocity + local unitvel = unit:getVelocity() + if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! + local AxialVel = {} --unit velocity transformed into aircraft axes directions + + --transform velocity components in direction of aircraft axes. + AxialVel.x = mist.vec.dp(unitpos.x, unitvel) + AxialVel.y = mist.vec.dp(unitpos.y, unitvel) + AxialVel.z = mist.vec.dp(unitpos.z, unitvel) + + --Yaw is the angle between unitpos.x and the x and z velocities + --define right yaw as positive + local Yaw = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = 0, z = AxialVel.z})/mist.vec.mag({x = AxialVel.x, y = 0, z = AxialVel.z})) + + --now set correct direction: + if AxialVel.z > 0 then + Yaw = -Yaw + end + return Yaw end end end + function mist.getAoA(unit) + local unitpos = unit:getPosition() + if unitpos then + local unitvel = unit:getVelocity() + if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! + local AxialVel = {} --unit velocity transformed into aircraft axes directions - mist.addEventHandler(addDeadObject) + --transform velocity components in direction of aircraft axes. + AxialVel.x = mist.vec.dp(unitpos.x, unitvel) + AxialVel.y = mist.vec.dp(unitpos.y, unitvel) + AxialVel.z = mist.vec.dp(unitpos.z, unitvel) - --[[ - local function addClientsToActive(event) - if event.id == world.event.S_EVENT_PLAYER_ENTER_UNIT or event.id == world.event.S_EVENT_BIRTH then - env.info(mist.utils.tableShow(event)) - if Unit.getPlayerName(event.initiator) then - env.info(Unit.getPlayerName(event.initiator)) - local newU = mist.utils.deepCopy(mist.DBs.unitsByName[Unit.getName(event.initiator)]) - newU.playerName = Unit.getPlayerName(event.initiator) - mist.DBs.activeHumans[Unit.getName(event.initiator)] = newU - --trigger.action.outText('added: ' .. Unit.getName(event.initiator), 20) - end - elseif event.id == world.event.S_EVENT_PLAYER_LEAVE_UNIT and event.initiator then - if mist.DBs.activeHumans[Unit.getName(event.initiator)] then - mist.DBs.activeHumans[Unit.getName(event.initiator)] = nil - -- trigger.action.outText('removed via control: ' .. Unit.getName(event.initiator), 20) + -- AoA is angle between unitpos.x and the x and y velocities + local AoA = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = AxialVel.y, z = 0})/mist.vec.mag({x = AxialVel.x, y = AxialVel.y, z = 0})) + + --now set correct direction: + if AxialVel.y > 0 then + AoA = -AoA + end + return AoA end end end - mist.addEventHandler(addClientsToActive)]] -end + function mist.getClimbAngle(unit) + local unitpos = unit:getPosition() + if unitpos then + local unitvel = unit:getVelocity() + if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! + return math.asin(unitvel.y/mist.vec.mag(unitvel)) + end + end + end -function mist.makeUnitTable(tbl) + function mist.makeUnitTable(tbl) - --[[ + --[[ Prefixes: "[-u]" - subtract this unit if its in the table "[g]" - add this group to the table @@ -2625,23 +1327,251 @@ Country names to be used in [c] and [-c] short-cuts: "Italy" ]] - --Assumption: will be passed a table of strings, sequential - local units_by_name = {} + --Assumption: will be passed a table of strings, sequential + local units_by_name = {} - local l_munits = mist.DBs.units --local reference for faster execution - for i = 1, #tbl do - local unit = tbl[i] - if unit:sub(1,4) == '[-u]' then --subtract a unit - if units_by_name[unit:sub(5)] then -- 5 to end - units_by_name[unit:sub(5)] = nil --remove + local l_munits = mist.DBs.units --local reference for faster execution + for i = 1, #tbl do + local unit = tbl[i] + if unit:sub(1,4) == '[-u]' then --subtract a unit + if units_by_name[unit:sub(5)] then -- 5 to end + units_by_name[unit:sub(5)] = nil --remove + end + elseif unit:sub(1,3) == '[g]' then -- add a group + for coa, coa_tbl in pairs(l_munits) do + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' and group_tbl.groupName == unit:sub(4) then -- index 4 to end + for unit_ind, unit in pairs(group_tbl.units) do + units_by_name[unit.unitName] = true --add + end + end + end + end + end + end end - elseif unit:sub(1,3) == '[g]' then -- add a group + elseif unit:sub(1,4) == '[-g]' then -- subtract a group for coa, coa_tbl in pairs(l_munits) do for country, country_table in pairs(coa_tbl) do for unit_type, unit_type_tbl in pairs(country_table) do if type(unit_type_tbl) == 'table' then for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' and group_tbl.groupName == unit:sub(4) then -- index 4 to end + if type(group_tbl) == 'table' and group_tbl.groupName == unit:sub(5) then -- index 5 to end + for unit_ind, unit in pairs(group_tbl.units) do + if units_by_name[unit.unitName] then + units_by_name[unit.unitName] = nil --remove + end + end + end + end + end + end + end + end +elseif unit:sub(1,3) == '[c]' then -- add a country + local category = '' + local country_start = 4 + if unit:sub(4,15) == '[helicopter]' then + category = 'helicopter' + country_start = 16 + elseif unit:sub(4,10) == '[plane]' then + category = 'plane' + country_start = 11 + elseif unit:sub(4,9) == '[ship]' then + category = 'ship' + country_start = 10 + elseif unit:sub(4,12) == '[vehicle]' then + category = 'vehicle' + country_start = 13 + end + for coa, coa_tbl in pairs(l_munits) do + for country, country_table in pairs(coa_tbl) do + if country == string.lower(unit:sub(country_start)) then -- match + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + units_by_name[unit.unitName] = true --add + end + end + end + end + end + end + end + end +elseif unit:sub(1,4) == '[-c]' then -- subtract a country + local category = '' + local country_start = 5 + if unit:sub(5,16) == '[helicopter]' then + category = 'helicopter' + country_start = 17 + elseif unit:sub(5,11) == '[plane]' then + category = 'plane' + country_start = 12 + elseif unit:sub(5,10) == '[ship]' then + category = 'ship' + country_start = 11 + elseif unit:sub(5,13) == '[vehicle]' then + category = 'vehicle' + country_start = 14 + end + for coa, coa_tbl in pairs(l_munits) do + for country, country_table in pairs(coa_tbl) do + if country == string.lower(unit:sub(country_start)) then -- match + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + if units_by_name[unit.unitName] then + units_by_name[unit.unitName] = nil --remove + end + end + end + end + end + end + end + end + end +elseif unit:sub(1,6) == '[blue]' then -- add blue coalition + local category = '' + if unit:sub(7) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(7) == '[plane]' then + category = 'plane' + elseif unit:sub(7) == '[ship]' then + category = 'ship' + elseif unit:sub(7) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + if coa == 'blue' then + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + units_by_name[unit.unitName] = true --add + end + end + end + end + end + end + end + end +elseif unit:sub(1,7) == '[-blue]' then -- subtract blue coalition + local category = '' + if unit:sub(8) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(8) == '[plane]' then + category = 'plane' + elseif unit:sub(8) == '[ship]' then + category = 'ship' + elseif unit:sub(8) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + if coa == 'blue' then + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + if units_by_name[unit.unitName] then + units_by_name[unit.unitName] = nil --remove + end + end + end + end + end + end + end + end + end +elseif unit:sub(1,5) == '[red]' then -- add red coalition + local category = '' + if unit:sub(6) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(6) == '[plane]' then + category = 'plane' + elseif unit:sub(6) == '[ship]' then + category = 'ship' + elseif unit:sub(6) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + if coa == 'red' then + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + units_by_name[unit.unitName] = true --add + end + end + end + end + end + end + end + end +elseif unit:sub(1,6) == '[-red]' then -- subtract red coalition + local category = '' + if unit:sub(7) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(7) == '[plane]' then + category = 'plane' + elseif unit:sub(7) == '[ship]' then + category = 'ship' + elseif unit:sub(7) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + if coa == 'red' then + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + if units_by_name[unit.unitName] then + units_by_name[unit.unitName] = nil --remove + end + end + end + end + end + end + end + end + end +elseif unit:sub(1,5) == '[all]' then -- add all of a certain category (or all categories) + local category = '' + if unit:sub(6) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(6) == '[plane]' then + category = 'plane' + elseif unit:sub(6) == '[ship]' then + category = 'ship' + elseif unit:sub(6) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then for unit_ind, unit in pairs(group_tbl.units) do units_by_name[unit.unitName] = true --add end @@ -2651,16 +1581,27 @@ Country names to be used in [c] and [-c] short-cuts: end end end -elseif unit:sub(1,4) == '[-g]' then -- subtract a group +elseif unit:sub(1,6) == '[-all]' then -- subtract all of a certain category (or all categories) + local category = '' + if unit:sub(7) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(7) == '[plane]' then + category = 'plane' + elseif unit:sub(7) == '[ship]' then + category = 'ship' + elseif unit:sub(7) == '[vehicle]' then + category = 'vehicle' + end for coa, coa_tbl in pairs(l_munits) do for country, country_table in pairs(coa_tbl) do for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' then + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' and group_tbl.groupName == unit:sub(5) then -- index 5 to end - for unit_ind, unit in pairs(group_tbl.units) do - if units_by_name[unit.unitName] then - units_by_name[unit.unitName] = nil --remove + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + if units_by_name[unit.unitName] then + units_by_name[unit.unitName] = nil --remove + end end end end @@ -2668,248 +1609,9 @@ elseif unit:sub(1,4) == '[-g]' then -- subtract a group end end end +else -- just a regular unit + units_by_name[unit] = true --add end - elseif unit:sub(1,3) == '[c]' then -- add a country - local category = '' - local country_start = 4 - if unit:sub(4,15) == '[helicopter]' then - category = 'helicopter' - country_start = 16 - elseif unit:sub(4,10) == '[plane]' then - category = 'plane' - country_start = 11 - elseif unit:sub(4,9) == '[ship]' then - category = 'ship' - country_start = 10 - elseif unit:sub(4,12) == '[vehicle]' then - category = 'vehicle' - country_start = 13 - end - for coa, coa_tbl in pairs(l_munits) do - for country, country_table in pairs(coa_tbl) do - if country == string.lower(unit:sub(country_start)) then -- match - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - units_by_name[unit.unitName] = true --add - end - end - end - end - end - end - end - end - elseif unit:sub(1,4) == '[-c]' then -- subtract a country - local category = '' - local country_start = 5 - if unit:sub(5,16) == '[helicopter]' then - category = 'helicopter' - country_start = 17 - elseif unit:sub(5,11) == '[plane]' then - category = 'plane' - country_start = 12 - elseif unit:sub(5,10) == '[ship]' then - category = 'ship' - country_start = 11 - elseif unit:sub(5,13) == '[vehicle]' then - category = 'vehicle' - country_start = 14 - end - for coa, coa_tbl in pairs(l_munits) do - for country, country_table in pairs(coa_tbl) do - if country == string.lower(unit:sub(country_start)) then -- match - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - if units_by_name[unit.unitName] then - units_by_name[unit.unitName] = nil --remove - end - end - end - end - end - end - end - end - end - elseif unit:sub(1,6) == '[blue]' then -- add blue coalition - local category = '' - if unit:sub(7) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(7) == '[plane]' then - category = 'plane' - elseif unit:sub(7) == '[ship]' then - category = 'ship' - elseif unit:sub(7) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - if coa == 'blue' then - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - units_by_name[unit.unitName] = true --add - end - end - end - end - end - end - end - end - elseif unit:sub(1,7) == '[-blue]' then -- subtract blue coalition - local category = '' - if unit:sub(8) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(8) == '[plane]' then - category = 'plane' - elseif unit:sub(8) == '[ship]' then - category = 'ship' - elseif unit:sub(8) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - if coa == 'blue' then - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - if units_by_name[unit.unitName] then - units_by_name[unit.unitName] = nil --remove - end - end - end - end - end - end - end - end - end - elseif unit:sub(1,5) == '[red]' then -- add red coalition - local category = '' - if unit:sub(6) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(6) == '[plane]' then - category = 'plane' - elseif unit:sub(6) == '[ship]' then - category = 'ship' - elseif unit:sub(6) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - if coa == 'red' then - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - units_by_name[unit.unitName] = true --add - end - end - end - end - end - end - end - end - elseif unit:sub(1,6) == '[-red]' then -- subtract red coalition - local category = '' - if unit:sub(7) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(7) == '[plane]' then - category = 'plane' - elseif unit:sub(7) == '[ship]' then - category = 'ship' - elseif unit:sub(7) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - if coa == 'red' then - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - if units_by_name[unit.unitName] then - units_by_name[unit.unitName] = nil --remove - end - end - end - end - end - end - end - end - end - elseif unit:sub(1,5) == '[all]' then -- add all of a certain category (or all categories) - local category = '' - if unit:sub(6) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(6) == '[plane]' then - category = 'plane' - elseif unit:sub(6) == '[ship]' then - category = 'ship' - elseif unit:sub(6) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - units_by_name[unit.unitName] = true --add - end - end - end - end - end - end - end - elseif unit:sub(1,6) == '[-all]' then -- subtract all of a certain category (or all categories) - local category = '' - if unit:sub(7) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(7) == '[plane]' then - category = 'plane' - elseif unit:sub(7) == '[ship]' then - category = 'ship' - elseif unit:sub(7) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - if units_by_name[unit.unitName] then - units_by_name[unit.unitName] = nil --remove - end - end - end - end - end - end - end - end - else -- just a regular unit - units_by_name[unit] = true --add - end end local units_tbl = {} -- indexed sequentially @@ -2924,7 +1626,6 @@ end return units_tbl end - function mist.getDeadMapObjsInZones(zone_names) -- zone_names: table of zone names -- returns: table of dead map objects (indexed numerically) @@ -2947,7 +1648,6 @@ function mist.getDeadMapObjsInZones(zone_names) return map_objs end - function mist.getDeadMapObjsInPolygonZone(zone) -- zone_names: table of zone names -- returns: table of dead map objects (indexed numerically) @@ -2962,588 +1662,6 @@ function mist.getDeadMapObjsInPolygonZone(zone) return map_objs end ---- Flag functions. --- The mist "Flag functions" are functions that are similar to Slmod functions --- that detect a game condition and set a flag when that game condition is met. --- --- They are intended to be used by persons with little or no experience in Lua --- programming, but with a good knowledge of the DCS mission editor. --- @section mist.flagFunc -mist.flagFunc = {} - ---- Sets a flag if map objects are destroyed inside a zone. --- Once this function is run, it will start a continuously evaluated process --- that will set a flag true if map objects (such as bridges, buildings in --- town, etc.) die (or have died) in a mission editor zone (or set of zones). --- This will only happen once; once the flag is set true, the process ends. --- @usage --- -- Example vars table --- vars = { --- zones = { "zone1", "zone2" }, -- can also be a single string --- flag = 3, -- number of the flag --- stopflag = 4, -- optional number of the stop flag --- req_num = 10, -- optional minimum amount of map objects needed to die --- } --- mist.flagFuncs.mapobjs_dead_zones(vars) --- @tparam table vars table containing parameters. -function mist.flagFunc.mapobjs_dead_zones(vars) - --[[vars needs to be: -zones = table or string, -flag = number, -stopflag = number or nil, -req_num = number or nil - -AND used by function, -initial_number - -]] - -- type_tbl - local type_tbl = { - [{'zones', 'zone'}] = {'table', 'string'}, - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - [{'req_num', 'reqnum'}] = {'number', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.mapobjs_dead_zones', type_tbl, vars) - assert(err, errmsg) - local zones = vars.zones or vars.zone - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local req_num = vars.req_num or vars.reqnum or 1 - local initial_number = vars.initial_number - - if type(zones) == 'string' then - zones = {zones} - end - - if not initial_number then - initial_number = #mist.getDeadMapObjsInZones(zones) - end - - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - if (#mist.getDeadMapObjsInZones(zones) - initial_number) >= req_num and trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - return - else - mist.scheduleFunction(mist.flagFunc.mapobjs_dead_zones, {{zones = zones, flag = flag, stopflag = stopflag, req_num = req_num, initial_number = initial_number}}, timer.getTime() + 1) - end - end -end - ---- Sets a flag if map objects are destroyed inside a polygon. --- Once this function is run, it will start a continuously evaluated process --- that will set a flag true if map objects (such as bridges, buildings in --- town, etc.) die (or have died) in a polygon. --- This will only happen once; once the flag is set true, the process ends. --- @usage --- -- Example vars table --- vars = { --- zone = { --- [1] = mist.DBs.unitsByName['NE corner'].point, --- [2] = mist.DBs.unitsByName['SE corner'].point, --- [3] = mist.DBs.unitsByName['SW corner'].point, --- [4] = mist.DBs.unitsByName['NW corner'].point --- } --- flag = 3, -- number of the flag --- stopflag = 4, -- optional number of the stop flag --- req_num = 10, -- optional minimum amount of map objects needed to die --- } --- mist.flagFuncs.mapobjs_dead_zones(vars) --- @tparam table vars table containing parameters. -function mist.flagFunc.mapobjs_dead_polygon(vars) - --[[vars needs to be: -zone = table, -flag = number, -stopflag = number or nil, -req_num = number or nil - -AND used by function, -initial_number - -]] - -- type_tbl - local type_tbl = { - [{'zone', 'polyzone'}] = 'table', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - [{'req_num', 'reqnum'}] = {'number', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.mapobjs_dead_polygon', type_tbl, vars) - assert(err, errmsg) - local zone = vars.zone or vars.polyzone - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local req_num = vars.req_num or vars.reqnum or 1 - local initial_number = vars.initial_number - - if not initial_number then - initial_number = #mist.getDeadMapObjsInPolygonZone(zone) - end - - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - if (#mist.getDeadMapObjsInPolygonZone(zone) - initial_number) >= req_num and trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - return - else - mist.scheduleFunction(mist.flagFunc.mapobjs_dead_polygon, {{zone = zone, flag = flag, stopflag = stopflag, req_num = req_num, initial_number = initial_number}}, timer.getTime() + 1) - end - end -end - -function mist.flagFunc.units_in_polygon(vars) - --[[vars needs to be: -units = table, -zone = table, -flag = number, -stopflag = number or nil, -maxalt = number or nil, -interval = number or nil, -req_num = number or nil -toggle = boolean or nil -unitTableDef = table or nil -]] - -- type_tbl - local type_tbl = { - [{'units', 'unit'}] = 'table', - [{'zone', 'polyzone'}] = 'table', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - [{'maxalt', 'alt'}] = {'number', 'nil'}, - interval = {'number', 'nil'}, - [{'req_num', 'reqnum'}] = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - unitTableDef = {'table', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_in_polygon', type_tbl, vars) - assert(err, errmsg) - local units = vars.units or vars.unit - local zone = vars.zone or vars.polyzone - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local interval = vars.interval or 1 - local maxalt = vars.maxalt or vars.alt - local req_num = vars.req_num or vars.reqnum or 1 - local toggle = vars.toggle or nil - local unitTableDef = vars.unitTableDef - - if not units.processed then - unitTableDef = mist.utils.deepCopy(units) - end - - if (units.processed and units.processed < mist.getLastDBUpdateTime()) or not units.processed then -- run unit table short cuts - units = mist.makeUnitTable(unitTableDef) - end - - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == 0) then - local num_in_zone = 0 - for i = 1, #units do - local unit = Unit.getByName(units[i]) - if unit then - local pos = unit:getPosition().p - if mist.pointInPolygon(pos, zone, maxalt) then - num_in_zone = num_in_zone + 1 - if num_in_zone >= req_num and trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - break - end - end - end - end - if toggle and (num_in_zone < req_num) and trigger.misc.getUserFlag(flag) > 0 then - trigger.action.setUserFlag(flag, false) - end - -- do another check in case stopflag was set true by this function - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == 0) then - mist.scheduleFunction(mist.flagFunc.units_in_polygon, {{units = units, zone = zone, flag = flag, stopflag = stopflag, interval = interval, req_num = req_num, maxalt = maxalt, toggle = toggle, unitTableDef = unitTableDef}}, timer.getTime() + interval) - end - end - -end - -function mist.flagFunc.units_in_zones(vars) - --[[vars needs to be: - units = table, - zones = table, - flag = number, - stopflag = number or nil, - zone_type = string or nil, - req_num = number or nil, - interval = number or nil - toggle = boolean or nil - ]] - -- type_tbl - local type_tbl = { - units = 'table', - zones = 'table', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - [{'zone_type', 'zonetype'}] = {'string', 'nil'}, - [{'req_num', 'reqnum'}] = {'number', 'nil'}, - interval = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - unitTableDef = {'table', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_in_zones', type_tbl, vars) - assert(err, errmsg) - local units = vars.units - local zones = vars.zones - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local zone_type = vars.zone_type or vars.zonetype or 'cylinder' - local req_num = vars.req_num or vars.reqnum or 1 - local interval = vars.interval or 1 - local toggle = vars.toggle or nil - local unitTableDef = vars.unitTableDef - - if not units.processed then - unitTableDef = mist.utils.deepCopy(units) - end - - if (units.processed and units.processed < mist.getLastDBUpdateTime()) or not units.processed then -- run unit table short cuts - units = mist.makeUnitTable(unitTableDef) - end - - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - - local in_zone_units = mist.getUnitsInZones(units, zones, zone_type) - - if #in_zone_units >= req_num and trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - elseif #in_zone_units < req_num and toggle then - trigger.action.setUserFlag(flag, false) - end - -- do another check in case stopflag was set true by this function - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - mist.scheduleFunction(mist.flagFunc.units_in_zones, {{units = units, zones = zones, flag = flag, stopflag = stopflag, zone_type = zone_type, req_num = req_num, interval = interval, toggle = toggle, unitTableDef = unitTableDef}}, timer.getTime() + interval) - end - end - -end - -function mist.flagFunc.units_in_moving_zones(vars) - --[[vars needs to be: - units = table, - zone_units = table, - radius = number, - flag = number, - stopflag = number or nil, - zone_type = string or nil, - req_num = number or nil, - interval = number or nil - toggle = boolean or nil - ]] - -- type_tbl - local type_tbl = { - units = 'table', - [{'zone_units', 'zoneunits'}] = 'table', - radius = 'number', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - [{'zone_type', 'zonetype'}] = {'string', 'nil'}, - [{'req_num', 'reqnum'}] = {'number', 'nil'}, - interval = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - unitTableDef = {'table', 'nil'}, - zUnitTableDef = {'table', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_in_moving_zones', type_tbl, vars) - assert(err, errmsg) - local units = vars.units - local zone_units = vars.zone_units or vars.zoneunits - local radius = vars.radius - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local zone_type = vars.zone_type or vars.zonetype or 'cylinder' - local req_num = vars.req_num or vars.reqnum or 1 - local interval = vars.interval or 1 - local toggle = vars.toggle or nil - local unitTableDef = vars.unitTableDef - local zUnitTableDef = vars.zUnitTableDef - - if not units.processed then - unitTableDef = mist.utils.deepCopy(units) - end - - if not zone_units.processed then - zUnitTableDef = mist.utils.deepCopy(zone_units) - end - - if (units.processed and units.processed < mist.getLastDBUpdateTime()) or not units.processed then -- run unit table short cuts - units = mist.makeUnitTable(unitTableDef) - end - - if (zone_units.processed and zone_units.processed < mist.getLastDBUpdateTime()) or not zone_units.processed then -- run unit table short cuts - zone_units = mist.makeUnitTable(zUnitTableDef) - end - - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - - local in_zone_units = mist.getUnitsInMovingZones(units, zone_units, radius, zone_type) - - if #in_zone_units >= req_num and trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - elseif #in_zone_units < req_num and toggle then - trigger.action.setUserFlag(flag, false) - end - -- do another check in case stopflag was set true by this function - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - mist.scheduleFunction(mist.flagFunc.units_in_moving_zones, {{units = units, zone_units = zone_units, radius = radius, flag = flag, stopflag = stopflag, zone_type = zone_type, req_num = req_num, interval = interval, toggle = toggle, unitTableDef = unitTableDef, zUnitTableDef = zUnitTableDef}}, timer.getTime() + interval) - end - end - -end - -function mist.flagFunc.units_LOS(vars) - --[[vars needs to be: -unitset1 = table, -altoffset1 = number, -unitset2 = table, -altoffset2 = number, -flag = number, -stopflag = number or nil, -radius = number or nil, -interval = number or nil, -req_num = number or nil -toggle = boolean or nil -]] - -- type_tbl - local type_tbl = { - [{'unitset1', 'units1'}] = 'table', - [{'altoffset1', 'alt1'}] = 'number', - [{'unitset2', 'units2'}] = 'table', - [{'altoffset2', 'alt2'}] = 'number', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - [{'req_num', 'reqnum'}] = {'number', 'nil'}, - interval = {'number', 'nil'}, - radius = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - unitTableDef1 = {'table', 'nil'}, - unitTableDef2 = {'table', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_LOS', type_tbl, vars) - assert(err, errmsg) - local unitset1 = vars.unitset1 or vars.units1 - local altoffset1 = vars.altoffset1 or vars.alt1 - local unitset2 = vars.unitset2 or vars.units2 - local altoffset2 = vars.altoffset2 or vars.alt2 - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local interval = vars.interval or 1 - local radius = vars.radius or math.huge - local req_num = vars.req_num or vars.reqnum or 1 - local toggle = vars.toggle or nil - local unitTableDef1 = vars.unitTableDef1 - local unitTableDef2 = vars.unitTableDef2 - - if not unitset1.processed then - unitTableDef1 = mist.utils.deepCopy(unitset1) - end - - if not unitset2.processed then - unitTableDef2 = mist.utils.deepCopy(unitset2) - end - - if (unitset1.processed and unitset1.processed < mist.getLastDBUpdateTime()) or not unitset1.processed then -- run unit table short cuts - unitset1 = mist.makeUnitTable(unitTableDef1) - end - - if (unitset2.processed and unitset2.processed < mist.getLastDBUpdateTime()) or not unitset2.processed then -- run unit table short cuts - unitset2 = mist.makeUnitTable(unitTableDef2) - end - - - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - - local unitLOSdata = mist.getUnitsLOS(unitset1, altoffset1, unitset2, altoffset2, radius) - - if #unitLOSdata >= req_num and trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - elseif #unitLOSdata < req_num and toggle then - trigger.action.setUserFlag(flag, false) - end - -- do another check in case stopflag was set true by this function - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - mist.scheduleFunction(mist.flagFunc.units_LOS, {{unitset1 = unitset1, altoffset1 = altoffset1, unitset2 = unitset2, altoffset2 = altoffset2, flag = flag, stopflag = stopflag, radius = radius, req_num = req_num, interval = interval, toggle = toggle, unitTableDef1 = unitTableDef1, unitTableDef2 = unitTableDef2}}, timer.getTime() + interval) - end - end -end - -function mist.flagFunc.group_alive(vars) - --[[vars -groupName -flag -toggle -interval -stopFlag - -]] - local type_tbl = { - [{'group', 'groupname', 'gp', 'groupName'}] = 'string', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - interval = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_alive', type_tbl, vars) - assert(err, errmsg) - - local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local interval = vars.interval or 1 - local toggle = vars.toggle or nil - - - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true and #Group.getByName(groupName):getUnits() > 0 then - if trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - end - else - if toggle then - trigger.action.setUserFlag(flag, false) - end - end - end - - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - mist.scheduleFunction(mist.flagFunc.group_alive, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle}}, timer.getTime() + interval) - end - -end - -function mist.flagFunc.group_dead(vars) - local type_tbl = { - [{'group', 'groupname', 'gp', 'groupName'}] = 'string', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - interval = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_dead', type_tbl, vars) - assert(err, errmsg) - - local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local interval = vars.interval or 1 - local toggle = vars.toggle or nil - - - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - if (Group.getByName(groupName) and Group.getByName(groupName):isExist() == false) or (Group.getByName(groupName) and #Group.getByName(groupName):getUnits() < 1) or not Group.getByName(groupName) then - if trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - end - else - if toggle then - trigger.action.setUserFlag(flag, false) - end - end - end - - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - mist.scheduleFunction(mist.flagFunc.group_dead, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle}}, timer.getTime() + interval) - end -end - -function mist.flagFunc.group_alive_less_than(vars) - local type_tbl = { - [{'group', 'groupname', 'gp', 'groupName'}] = 'string', - percent = 'number', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - interval = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_alive_less_than', type_tbl, vars) - assert(err, errmsg) - - local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname - local flag = vars.flag - local percent = vars.percent - local stopflag = vars.stopflag or -1 - local interval = vars.interval or 1 - local toggle = vars.toggle or nil - - - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then - if Group.getByName(groupName):getSize()/Group.getByName(groupName):getInitialSize() < percent/100 then - if trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - end - else - if toggle then - trigger.action.setUserFlag(flag, false) - end - end - else - if trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - end - end - end - - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - mist.scheduleFunction(mist.flagFunc.group_alive_less_than, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle, percent = percent}}, timer.getTime() + interval) - end -end - -function mist.flagFunc.group_alive_more_than(vars) - local type_tbl = { - [{'group', 'groupname', 'gp', 'groupName'}] = 'string', - percent = 'number', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - interval = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_alive_more_than', type_tbl, vars) - assert(err, errmsg) - - local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname - local flag = vars.flag - local percent = vars.percent - local stopflag = vars.stopflag or -1 - local interval = vars.interval or 1 - local toggle = vars.toggle or nil - - - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then - if Group.getByName(groupName):getSize()/Group.getByName(groupName):getInitialSize() > percent/100 then - if trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - end - else - if toggle and trigger.misc.getUserFlag(flag) == 1 then - trigger.action.setUserFlag(flag, false) - end - end - else --- just in case - if toggle and trigger.misc.getUserFlag(flag) == 1 then - trigger.action.setUserFlag(flag, false) - end - end - end - - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - mist.scheduleFunction(mist.flagFunc.group_alive_more_than, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle, percent = percent}}, timer.getTime() + interval) - end -end - function mist.pointInPolygon(point, poly, maxalt) --raycasting point in polygon. Code from http://softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm --[[local type_tbl = { point = {'table'}, @@ -3808,1006 +1926,6 @@ function mist.getAvgGroupPos(groupName) end ---- Demo functions. --- @section mist.demos -mist.demos = {} - -function mist.demos.printFlightData(unit) - if unit:isExist() then - local function printData(unit, prevVel, prevE, prevTime) - local angles = mist.getAttitude(unit) - if angles then - local Heading = angles.Heading - local Pitch = angles.Pitch - local Roll = angles.Roll - local Yaw = angles.Yaw - local AoA = angles.AoA - local ClimbAngle = angles.ClimbAngle - - if not Heading then - Heading = 'NA' - else - Heading = string.format('%12.2f', mist.utils.toDegree(Heading)) - end - - if not Pitch then - Pitch = 'NA' - else - Pitch = string.format('%12.2f', mist.utils.toDegree(Pitch)) - end - - if not Roll then - Roll = 'NA' - else - Roll = string.format('%12.2f', mist.utils.toDegree(Roll)) - end - - local AoAplusYaw = 'NA' - if AoA and Yaw then - AoAplusYaw = string.format('%12.2f', mist.utils.toDegree((AoA^2 + Yaw^2)^0.5)) - end - - if not Yaw then - Yaw = 'NA' - else - Yaw = string.format('%12.2f', mist.utils.toDegree(Yaw)) - end - - if not AoA then - AoA = 'NA' - else - AoA = string.format('%12.2f', mist.utils.toDegree(AoA)) - end - - if not ClimbAngle then - ClimbAngle = 'NA' - else - ClimbAngle = string.format('%12.2f', mist.utils.toDegree(ClimbAngle)) - end - local unitPos = unit:getPosition() - local unitVel = unit:getVelocity() - local curTime = timer.getTime() - local absVel = string.format('%12.2f', mist.vec.mag(unitVel)) - - - local unitAcc = 'NA' - local Gs = 'NA' - local axialGs = 'NA' - local transGs = 'NA' - if prevVel and prevTime then - local xAcc = (unitVel.x - prevVel.x)/(curTime - prevTime) - local yAcc = (unitVel.y - prevVel.y)/(curTime - prevTime) - local zAcc = (unitVel.z - prevVel.z)/(curTime - prevTime) - - unitAcc = string.format('%12.2f', mist.vec.mag({x = xAcc, y = yAcc, z = zAcc})) - Gs = string.format('%12.2f', mist.vec.mag({x = xAcc, y = yAcc + 9.81, z = zAcc})/9.81) - axialGs = string.format('%12.2f', mist.vec.dp({x = xAcc, y = yAcc + 9.81, z = zAcc}, unitPos.x)/9.81) - transGs = string.format('%12.2f', mist.vec.mag(mist.vec.cp({x = xAcc, y = yAcc + 9.81, z = zAcc}, unitPos.x))/9.81) - end - - local E = 0.5*mist.vec.mag(unitVel)^2 + 9.81*unitPos.p.y - - local energy = string.format('%12.2e', E) - - local dEdt = 'NA' - if prevE and prevTime then - dEdt = string.format('%12.2e', (E - prevE)/(curTime - prevTime)) - end - - trigger.action.outText(string.format('%-25s', 'Heading: ') .. Heading .. ' degrees\n' .. string.format('%-25s', 'Roll: ') .. Roll .. ' degrees\n' .. string.format('%-25s', 'Pitch: ') .. Pitch - .. ' degrees\n' .. string.format('%-25s', 'Yaw: ') .. Yaw .. ' degrees\n' .. string.format('%-25s', 'AoA: ') .. AoA .. ' degrees\n' .. string.format('%-25s', 'AoA plus Yaw: ') .. AoAplusYaw .. ' degrees\n' .. string.format('%-25s', 'Climb Angle: ') .. - ClimbAngle .. ' degrees\n' .. string.format('%-25s', 'Absolute Velocity: ') .. absVel .. ' m/s\n' .. string.format('%-25s', 'Absolute Acceleration: ') .. unitAcc ..' m/s^2\n' - .. string.format('%-25s', 'Axial G loading: ') .. axialGs .. ' g\n' .. string.format('%-25s', 'Transverse G loading: ') .. transGs .. ' g\n' .. string.format('%-25s', 'Absolute G loading: ') .. Gs .. ' g\n' .. string.format('%-25s', 'Energy: ') .. energy .. ' J/kg\n' .. string.format('%-25s', 'dE/dt: ') .. dEdt .. - ' J/(kg*s)', 1) - - return unitVel, E, curTime - end - end - - local function frameFinder(unit, prevVel, prevE, prevTime) - if unit:isExist() then - local currVel = unit:getVelocity() - if prevVel and (prevVel.x ~= currVel.x or prevVel.y ~= currVel.y or prevVel.z ~= currVel.z) or (prevTime and (timer.getTime() - prevTime) > 0.25) then - prevVel, prevE, prevTime = printData(unit, prevVel, prevE, prevTime) - end - mist.scheduleFunction(frameFinder, {unit, prevVel, prevE, prevTime}, timer.getTime() + 0.005) -- it can't go this fast, limited to the 100 times a sec check right now. - end - end - - - local curVel = unit:getVelocity() - local curTime = timer.getTime() - local curE = 0.5*mist.vec.mag(curVel)^2 + 9.81*unit:getPosition().p.y - frameFinder(unit, curVel, curE, curTime) - - end - -end - ---- Group task functions. --- @section tasks -mist.ground = {} -mist.fixedWing = {} -mist.heli = {} -mist.air = {} -mist.air.fixedWing = {} -mist.air.heli = {} - -function mist.goRoute(group, path) - local misTask = { - id = 'Mission', - params = { - route = { - points = mist.utils.deepCopy(path), - }, - }, - } - if type(group) == 'string' then - group = Group.getByName(group) - end - local groupCon = nil - if group then - groupCon = group:getController() - if groupCon then - groupCon:setTask(misTask) - return true - end - end - --Controller.setTask(groupCon, misTask) - return false -end - --- same as getGroupPoints but returns speed and formation type along with vec2 of point} -function mist.getGroupRoute(groupIdent, task) - -- 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 = mist.DBs.MEgroupsByName[groupIdent].groupId - end - - for coa_name, coa_data in pairs(env.mission.coalition) do - if (coa_name == 'red' or coa_name == 'blue') and type(coa_data) == 'table' then - if coa_data.country then --there is a country table - for cntry_id, cntry_data in pairs(coa_data.country) do - for obj_type_name, obj_type_data in pairs(cntry_data) do - if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" then -- only these types have points - if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's a group! - for group_num, group_data in pairs(obj_type_data.group) do - if group_data and group_data.groupId == gpId then -- this is the group we are looking for - if group_data.route and group_data.route.points and #group_data.route.points > 0 then - local points = {} - - for point_num, point in pairs(group_data.route.points) do - local routeData = {} - if not point.point then - routeData.x = point.x - routeData.y = point.y - else - routeData.point = point.point --it's possible that the ME could move to the point = Vec2 notation. - end - routeData.form = point.action - routeData.speed = point.speed - routeData.alt = point.alt - routeData.alt_type = point.alt_type - routeData.airdromeId = point.airdromeId - routeData.helipadId = point.helipadId - routeData.type = point.type - routeData.action = point.action - if task then - routeData.task = point.task - end - points[point_num] = routeData - end - - return points - end - return - end --if group_data and group_data.name and group_data.name == 'groupname' - end --for group_num, group_data in pairs(obj_type_data.group) do - end --if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then - end --if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then - end --for obj_type_name, obj_type_data in pairs(cntry_data) do - end --for cntry_id, cntry_data in pairs(coa_data.country) do - end --if coa_data.country then --there is a country table - end --if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then - end --for coa_name, coa_data in pairs(mission.coalition) do -end - -function mist.ground.buildPath() end -- ???? - --- No longer accepts path -function mist.ground.buildWP(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 = mist.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 - -function mist.fixedWing.buildWP(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 altType == 'agl' then - wp.alt_type = 'RADIO' - elseif altType == 'baro' or altType == '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 = mist.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 - -function mist.heli.buildWP(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 altType == 'agl' then - wp.alt_type = 'RADIO' - elseif altType == 'baro' or altType == '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 = mist.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 - --- need to return a Vec3 or Vec2? -function mist.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 - -function mist.getRandomPointInZone(zoneName, innerRadius) - if type(zoneName) == 'string' and type(trigger.misc.getZone(zoneName)) == 'table' then - return mist.getRandPointInCircle(trigger.misc.getZone(zoneName).point, trigger.misc.getZone(zoneName).radius, innerRadius) - end - return false -end - -function mist.groupToRandomPoint(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 mist.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 = mist.getRandPointInCircle(point, radius, innerRadius) - - local offset = {} - local posStart = mist.getLeadPos(group) - - offset.x = mist.utils.round(math.sin(heading - (math.pi/2)) * 50 + rndCoord.x, 3) - offset.z = mist.utils.round(math.cos(heading + (math.pi/2)) * 50 + rndCoord.y, 3) - path[#path + 1] = mist.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] = mist.ground.buildWP({['x'] = posStart.x + 11, ['z'] = posStart.z + 11}, 'off_road', speed) - path[#path + 1] = mist.ground.buildWP(posStart, 'on_road', speed) - path[#path + 1] = mist.ground.buildWP(offset, 'on_road', speed) - else - path[#path + 1] = mist.ground.buildWP({['x'] = posStart.x + 25, ['z'] = posStart.z + 25}, form, speed) - end - - path[#path + 1] = mist.ground.buildWP(offset, form, speed) - path[#path + 1] = mist.ground.buildWP(rndCoord, form, speed) - - mist.goRoute(group, path) - - return -end - -function mist.groupRandomDistSelf(gpData, dist, form, heading, speed) - local pos = mist.getLeadPos(gpData) - local fakeZone = {} - fakeZone.radius = dist or math.random(300, 1000) - fakeZone.point = {x = pos.x, y = pos.y, z = pos.z} - mist.groupToRandomZone(gpData, fakeZone, form, heading, speed) - - return -end - -function mist.groupToRandomZone(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 = mist.utils.kmphToMps(speed) - end - - local vars = {} - vars.group = gpData - vars.radius = zone.radius - vars.form = form - vars.headingDegrees = heading - vars.speed = speed - vars.point = mist.utils.zoneToVec3(zone) - - mist.groupToRandomPoint(vars) - - return -end - -function mist.isTerrainValid(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 - -function mist.terrainHeightDiff(coord, searchSize) - local samples = {} - local searchRadius = 5 - if searchSize then - searchRadius = searchSize - end - if type(coord) == 'string' then - coord = mist.utils.zoneToVec3(coord) - end - - coord = mist.utils.makeVec2(coord) - - samples[#samples + 1] = land.getHeight(coord) - for i = 0, 360, 30 do - samples[#samples + 1] = land.getHeight({x = (coord.x + (math.sin(math.rad(i))*searchRadius)), y = (coord.y + (math.cos(math.rad(i))*searchRadius))}) - if searchRadius >= 20 then -- if search radius is sorta large, take a sample halfway between center and outer edge - samples[#samples + 1] = land.getHeight({x = (coord.x + (math.sin(math.rad(i))*(searchRadius/2))), y = (coord.y + (math.cos(math.rad(i))*(searchRadius/2)))}) - end - end - local tMax, tMin = 0, 1000000 - for index, height in pairs(samples) do - if height > tMax then - tMax = height - end - if height < tMin then - tMin = height - end - end - return mist.utils.round(tMax - tMin, 2) -end - -function mist.groupToPoint(gpData, point, form, heading, speed, useRoads) - if type(point) == 'string' then - point = trigger.misc.getZone(point) - end - if speed then - speed = mist.utils.kmphToMps(speed) - end - - local vars = {} - vars.group = gpData - vars.form = form - vars.headingDegrees = heading - vars.speed = speed - vars.disableRoads = useRoads - vars.point = mist.utils.zoneToVec3(point) - mist.groupToRandomPoint(vars) - - return -end - -function mist.getLeadPos(group) - if type(group) == 'string' then -- group name - group = Group.getByName(group) - end - - local units = group:getUnits() - - local leader = units[1] - if not Unit.isExist(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 Unit.isExist(unit) and ind < lowestInd then - lowestInd = ind - return unit:getPosition().p - end - end - end - if leader and Unit.isExist(leader) then -- maybe a little too paranoid now... - return leader:getPosition().p - end -end - --- end of Mission task functions - --- MESAGGES -do - local messageList = {} - -- this defines the max refresh rate of the message box it honestly only needs to - -- go faster than this for precision timing stuff (which could be its own function) - local messageDisplayRate = 0.1 - local messageID = 0 - local displayActive = false - local displayFuncId = 0 - - local caSlots = false - local caMSGtoGroup = false - - if env.mission.groundControl then -- just to be sure? - for index, value in pairs(env.mission.groundControl) do - if type(value) == 'table' then - for roleName, roleVal in pairs(value) do - for rIndex, rVal in pairs(roleVal) do - if rIndex == 'red' or rIndex == 'blue' then - if env.mission.groundControl[index][roleName][rIndex] > 0 then - caSlots = true - break - end - end - end - end - elseif type(value) == 'boolean' and value == true then - caSlots = true - break - end - end - end - - local function mistdisplayV5() - --[[thoughts to improve upon - event handler based activeClients table. - display messages only when there is an update - possibly co-routine it. -]] - end - - local function mistdisplayV4() - local activeClients = {} - - for clientId, clientData in pairs(mist.DBs.humansById) do - if Unit.getByName(clientData.unitName) and Unit.getByName(clientData.unitName):isExist() == true then - activeClients[clientData.groupId] = clientData.groupName - end - end - - --[[if caSlots == true and caMSGtoGroup == true then - - end]] - - - if #messageList > 0 then - if displayActive == false then - displayActive = true - end - --mist.debug.writeData(mist.utils.serialize,{'msg', messageList}, 'messageList.lua') - local msgTableText = {} - local msgTableSound = {} - - for messageId, messageData in pairs(messageList) do - if messageData.displayedFor > messageData.displayTime then - messageData:remove() -- now using the remove/destroy function. - else - if messageData.displayedFor then - messageData.displayedFor = messageData.displayedFor + messageDisplayRate - end - local nextSound = 1000 - local soundIndex = 0 - - if messageData.multSound and #messageData.multSound > 0 then - for index, sData in pairs(messageData.multSound) do - if sData.time <= messageData.displayedFor and sData.played == false and sData.time < nextSound then -- find index of the next sound to be played - nextSound = sData.time - soundIndex = index - end - end - if soundIndex ~= 0 then - messageData.multSound[soundIndex].played = true - end - end - - for recIndex, recData in pairs(messageData.msgFor) do -- iterate recipiants - if recData == 'RED' or recData == 'BLUE' or activeClients[recData] then -- rec exists - if messageData.text then -- text - if not msgTableText[recData] then -- create table entry for text - msgTableText[recData] = {} - msgTableText[recData].text = {} - if recData == 'RED' or recData == 'BLUE' then - msgTableText[recData].text[1] = '-------Combined Arms Message-------- \n' - end - msgTableText[recData].text[#msgTableText[recData].text + 1] = messageData.text - msgTableText[recData].displayTime = messageData.displayTime - messageData.displayedFor - else -- add to table entry and adjust display time if needed - if recData == 'RED' or recData == 'BLUE' then - msgTableText[recData].text[#msgTableText[recData].text + 1] = '\n ---------------- Combined Arms Message: \n' - else - msgTableText[recData].text[#msgTableText[recData].text + 1] = '\n ---------------- \n' - end - msgTableText[recData].text[#msgTableText[recData].text + 1] = messageData.text - if msgTableText[recData].displayTime < messageData.displayTime - messageData.displayedFor then - msgTableText[recData].displayTime = messageData.displayTime - messageData.displayedFor - else - msgTableText[recData].displayTime = 1 - end - end - end - if soundIndex ~= 0 then - msgTableSound[recData] = messageData.multSound[soundIndex].file - end - end - end - - - end - end - ------- new display - - if caSlots == true and caMSGtoGroup == false then - if msgTableText['RED'] then - trigger.action.outTextForCoalition(coalition.side.RED, table.concat(msgTableText['RED'].text), msgTableText['RED'].displayTime, true) - - end - if msgTableText['BLUE'] then - trigger.action.outTextForCoalition(coalition.side.BLUE, table.concat(msgTableText['BLUE'].text), msgTableText['BLUE'].displayTime, true) - end - end - - for index, msgData in pairs(msgTableText) do - if type(index) == 'number' then -- its a groupNumber - trigger.action.outTextForGroup(index, table.concat(msgData.text), msgData.displayTime, true) - end - end - --- new audio - if msgTableSound['RED'] then - trigger.action.outSoundForCoalition(coalition.side.RED, msgTableSound['RED']) - end - if msgTableSound['BLUE'] then - trigger.action.outSoundForCoalition(coalition.side.BLUE, msgTableSound['BLUE']) - end - - - for index, file in pairs(msgTableSound) do - if type(index) == 'number' then -- its a groupNumber - trigger.action.outSoundForGroup(index, file) - end - end - else - mist.removeFunction(displayFuncId) - displayActive = false - end - - end - - local typeBase = { - ['Mi-8MT'] = {'Mi-8MTV2', 'Mi-8MTV', 'Mi-8'}, - ['MiG-21Bis'] = {'Mig-21'}, - ['MiG-15bis'] = {'Mig-15'}, - ['FW-190D9'] = {'FW-190'}, - ['Bf-109K-4'] = {'Bf-109'}, - } - - --[[function mist.setCAGroupMSG(val) - if type(val) == 'boolean' then - caMSGtoGroup = val - return true - end - return false -end]] - - mist.message = { - - - add = function(vars) - local function msgSpamFilter(recList, spamBlockOn) - for id, name in pairs(recList) do - if name == spamBlockOn then - -- env.info('already on recList') - return recList - end - end - --env.info('add to recList') - table.insert(recList, spamBlockOn) - return recList - end - - --[[ - local vars = {} - vars.text = 'Hello World' - vars.displayTime = 20 - vars.msgFor = {coa = {'red'}, countries = {'Ukraine', 'Georgia'}, unitTypes = {'A-10C'}} - mist.message.add(vars) - - Displays the message for all red coalition players. Players belonging to Ukraine and Georgia, and all A-10Cs on the map - - ]] - - - local new = {} - new.text = vars.text -- The actual message - new.displayTime = vars.displayTime -- How long will the message appear for - new.displayedFor = 0 -- how long the message has been displayed so far - new.name = vars.name -- ID to overwrite the older message (if it exists) Basically it replaces a message that is displayed with new text. - new.addedAt = timer.getTime() - new.update = true - - if vars.multSound and vars.multSound[1] then - new.multSound = vars.multSound - else - new.multSound = {} - end - - if vars.sound or vars.fileName then -- converts old sound file system into new multSound format - local sound = vars.sound - if vars.fileName then - sound = vars.fileName - end - new.multSound[#new.multSound+1] = {time = 0.1, file = sound} - end - - if #new.multSound > 0 then - for i, data in pairs(new.multSound) do - data.played = false - end - end - - local newMsgFor = {} -- list of all groups message displays for - for forIndex, forData in pairs(vars.msgFor) do - for list, listData in pairs(forData) do - for clientId, clientData in pairs(mist.DBs.humansById) do - forIndex = string.lower(forIndex) - if type(listData) == 'string' then - listData = string.lower(listData) - end - if (forIndex == 'coa' and (listData == string.lower(clientData.coalition) or listData == 'all')) or (forIndex == 'countries' and string.lower(clientData.country) == listData) or (forIndex == 'units' and string.lower(clientData.unitName) == listData) then -- - newMsgFor = msgSpamFilter(newMsgFor, clientData.groupId) -- so units dont get the same message twice if complex rules are given - --table.insert(newMsgFor, clientId) - elseif forIndex == 'unittypes' then - for typeId, typeData in pairs(listData) do - local found = false - for clientDataEntry, clientDataVal in pairs(clientData) do - if type(clientDataVal) == 'string' then - if mist.matchString(list, clientDataVal) == true or list == 'all' then - local sString = typeData - for rName, pTbl in pairs(typeBase) do -- just a quick check to see if the user may have meant something and got the specific type of the unit wrong - for pIndex, pName in pairs(pTbl) do - if mist.stringMatch(sString, pName) then - sString = rName - end - end - end - if sString == clientData.type then - found = true - newMsgFor = msgSpamFilter(newMsgFor, clientData.groupId) -- sends info oto other function to see if client is already recieving the current message. - --table.insert(newMsgFor, clientId) - end - end - end - if found == true then -- shouldn't this be elsewhere too? - break - end - end - end - - end - end - for coaData, coaId in pairs(coalition.side) do - if string.lower(forIndex) == 'coa' or string.lower(forIndex) == 'ca' then - if listData == string.lower(coaData) or listData == 'all' then - newMsgFor = msgSpamFilter(newMsgFor, coaData) - end - end - end - end - end - - if #newMsgFor > 0 then - new.msgFor = newMsgFor -- I swear its not confusing - - else - return false - end - - - if vars.name and type(vars.name) == 'string' then - for i = 1, #messageList do - if messageList[i].name then - if messageList[i].name == vars.name then - --env.info('updateMessage') - messageList[i].displayedFor = 0 - messageList[i].addedAt = timer.getTime() - messageList[i].sound = new.sound - messageList[i].text = new.text - messageList[i].msgFor = new.msgFor - messageList[i].multSound = new.multSound - messageList[i].update = true - return messageList[i].messageID - end - end - end - end - - messageID = messageID + 1 - new.messageID = messageID - - --mist.debug.writeData(mist.utils.serialize,{'msg', new}, 'newMsg.lua') - - - messageList[#messageList + 1] = new - - local mt = { __index = mist.message} - setmetatable(new, mt) - - if displayActive == false then - displayActive = true - displayFuncId = mist.scheduleFunction(mistdisplayV4, {}, timer.getTime() + messageDisplayRate, messageDisplayRate) - end - - return messageID - - end, - - remove = function(self) -- Now a self variable; the former functionality taken up by mist.message.removeById. - for i, msgData in pairs(messageList) do - if messageList[i] == self then - table.remove(messageList, i) - return true --removal successful - end - end - return false -- removal not successful this script fails at life! - end, - - removeById = function(id) -- This function is NOT passed a self variable; it is the remove by id function. - for i, msgData in pairs(messageList) do - if messageList[i].messageID == id then - table.remove(messageList, i) - return true --removal successful - end - end - return false -- removal not successful this script fails at life! - end, - } - -end --- End of message system - --- Beginning of coordinate messages ---[[ -Design: - -Already have: -mist.tostringBR = function(az, dist, alt, metric) -mist.tostringLL = function(lat, lon, acc, DMS) -mist.tostringMGRS = function(MGRS, acc) - -Need: -mist.getMGRSString(UnitNameTable, acc) -mist.getLeadingMGRSString(UnitNameTable, dir, radius, acc) - -mist.getLLString(UnitNameTable, acc) -mist.getLeadingLLString(UnitNameTable, dir, radius acc) - -mist.getBRString(UnitNameTable, ref, alt, metric) -mist.getLeadingBRString(UnitNameTable, ref, alt, metric, dir, radius, acc) -- vars versions? - -mist.sendMGRSMsg(vars) -mist.sendLeadingMGRSMsg(vars) - -mist.sendLLMsg(vars) -mist.sendLeadingLLMsg(vars) - -mist.sendBRMsg(vars) -mist.sendLeadingBRMsg(vars) - -]] - ---[[RE-EXAMINE USAGE OF VARS FOR SIMPLE FUNCTIONS. -(Answer: the leading functions require a lot of input variables; maybe better to leave in vars -format for consistency. Maybe individual variable specification could also be supported?) - -]] - - --[[ vars for mist.getMGRSString: vars.units - table of unit names (NOT unitNameTable- maybe this should change). vars.acc - integer between 0 and 5, inclusive @@ -4976,290 +2094,9 @@ function mist.getLeadingBRString(vars) end end ---[[ vars for mist.message.add - vars.text = 'Hello World' - vars.displayTime = 20 - vars.msgFor = {coa = {'red'}, countries = {'Ukraine', 'Georgia'}, unitTypes = {'A-10C'}} - -]] - ---[[ vars for mist.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 -]] - -function mist.msgMGRS(vars) - local units = vars.units - local acc = vars.acc - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor - - local s = mist.getMGRSString{units = units, acc = acc} - local newText - if text then - 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 -else - newText = s -end -mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor -} end ---[[ vars for mist.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 -]] -function mist.msgLL(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 = mist.getLLString{units = units, acc = acc, DMS = DMS} - local newText - if text then - 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 -else - newText = s -end - -mist.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 -]] -function mist.msgBR(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 mist.getBRString - local alt = vars.alt - local metric = vars.metric - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor - - local s = mist.getBRString{units = units, ref = ref, alt = alt, metric = metric} - local newText - if text then - 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 -else - newText = s -end - -mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor -} - -end - --- basically, just sub-types of mist.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 -]] -function mist.msgBullseye(vars) - if string.lower(vars.ref) == 'red' then - vars.ref = mist.DBs.missionData.bullseye.red - mist.msgBR(vars) - elseif string.lower(vars.ref) == 'blue' then - vars.ref = mist.DBs.missionData.bullseye.blue - mist.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 -]] -function mist.msgBRA(vars) - if Unit.getByName(vars.ref) and Unit.getByName(vars.ref):isExist() == true then - vars.ref = Unit.getByName(vars.ref):getPosition().p - if not vars.alt then - vars.alt = true - end - mist.msgBR(vars) - end -end - ---[[ vars for mist.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 -]] -function mist.msgLeadingMGRS(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 = mist.getLeadingMGRSString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc} - local newText - if text then - 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 -else - newText = s -end - -mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor -} - - -end - ---[[ vars for mist.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 -]] -function mist.msgLeadingLL(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 = mist.getLeadingLLString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc, DMS = DMS} - local newText - - if text then - 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 -else - newText = s -end - -mist.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 -]] -function mist.msgLeadingBR(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 mist.getBRString - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor - - local s = mist.getLeadingBRString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, metric = metric, alt = alt, ref = ref} - local newText - - if text then - 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 -else - newText = s -end - -mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor -} -end --- end of coordinate messages - --- start of sct Merge -do -- all function uses of group and unit Ids must be in this do statement +do -- group functions scope function mist.groupTableCheck(groupData) local isOk = false @@ -6053,6 +2890,2448 @@ do -- all function uses of group and unit Ids must be in this do statement mist.matchString = mist.stringMatch -- both commands work because order out type of I + mist.DBs.const = {} + + --[[ + ['LAND'] = 1, + ['SHALLOW_WATER'] = 2, + ['WATER'] = 3, + ['ROAD'] = 4, + ['RUNWAY'] = 5 + ]] + --[[mist.DBs.const.ME_SSE_terms = { + ['ME'] = { + ['vehicle'] = {'GROUND', 'GROUND_UNIT'}, + ['plane'] = {'AIRPLANE'}, + }, + ['SSE'] = { + }, + +}]] + + + mist.DBs.const.callsigns = { -- not accessible by SSE, must use static list :-/ + ['NATO'] = { + ['rules'] = { + ['groupLimit'] = 9, + }, + ['AWACS'] = { + ['Overlord'] = 1, + ['Magic'] = 2, + ['Wizard'] = 3, + ['Focus'] = 4, + ['Darkstar'] = 5, + }, + ['TANKER'] = { + ['Texaco'] = 1, + ['Arco'] = 2, + ['Shell'] = 3, + }, + ['JTAC'] = { + ['Axeman'] = 1, + ['Darknight'] = 2, + ['Warrior'] = 3, + ['Pointer'] = 4, + ['Eyeball'] = 5, + ['Moonbeam'] = 6, + ['Whiplash'] = 7, + ['Finger'] = 8, + ['Pinpoint'] = 9, + ['Ferret'] = 10, + ['Shaba'] = 11, + ['Playboy'] = 12, + ['Hammer'] = 13, + ['Jaguar'] = 14, + ['Deathstar'] = 15, + ['Anvil'] = 16, + ['Firefly'] = 17, + ['Mantis'] = 18, + ['Badger'] = 19, + }, + ['aircraft'] = { + ['Enfield'] = 1, + ['Springfield'] = 2, + ['Uzi'] = 3, + ['Colt'] = 4, + ['Dodge'] = 5, + ['Ford'] = 6, + ['Chevy'] = 7, + ['Pontiac'] = 8, + }, + + ['unique'] = { + ['A10'] = { + ['Hawg'] = 9, + ['Boar'] = 10, + ['Pig'] = 11, + ['Tusk'] = 12, + ['rules'] = { + ['canUseAircraft'] = true, + ['appliesTo'] = { + 'A-10C', + 'A-10A', + }, + }, + }, + }, + }, + +} +--[[ scope: +{ + units = {...}, -- unit names. + coa = {...}, -- coa names + countries = {...}, -- country names + CA = {...}, -- looks just like coa. + unitTypes = { red = {}, blue = {}, all = {}, Russia = {},} +} + + +scope examples: + +{ units = { 'Hawg11', 'Hawg12' }, CA = {'blue'} } + +{ countries = {'Georgia'}, unitTypes = {blue = {'A-10C', 'A-10A'}}} + +{ coa = {'all'}} + +{unitTypes = { blue = {'A-10C'}}} +]] +end + +do -- mist.Logger scope + --- Logger class. + -- @type mist.Logger + mist.Logger = { + tag = "MIST", + level = 1, + } + + --- Creates a new logger. + -- Each logger has it's own tag and log level. + -- @tparam[opt] table l optional logger object. + -- @usage myLogger = mist.Logger:new + -- @usage myLogger = mist.Logger:new{tag = "MyScript", level = 2} + -- @treturn mist.Logger + function mist.Logger:new(l) + l = l or {} + if not l.tag then + l.tag = "MIST" + end + if not l.level then + l.level = 1 + end + setmetatable(l, self) + self.__index = self + return l + end + + --- Sets the level of verbosity for this logger. + -- "none" or 0 disables all logging + -- "error" or 1 shows error messages only + -- "warning" or 2 shows error and warning messages + -- "info" or 3 shows error, warning and info messages + -- @param level this can either be a string or an integer level + -- @usage myLogger:setLevel("info") + -- @usage -- log everything + --myLogger:setLevel(3) + function mist.Logger:setLevel(level) + if type(level) == 'string' then + if level == 'none' or level == 'off' then + self.level = 0 + elseif level == 'error' then + self.level = 1 + elseif level == 'warning' then + self.level = 2 + elseif level == 'info' then + self.level = 3 + end + elseif type(level) == 'number' then + self.level = level + end + end + + --- parses text and substitutes keywords with values from given array. + -- @tparam string text string containing keywords to substitute with values + -- @param ... variables to use for substitution + -- @treturn string new string with keywords substituted + local function formatText(text, ...) + for index,value in ipairs(arg) do + -- TODO: check for getmetatabel(value).__tostring + if type(value) == 'table' then + value = mist.utils.oneLineSerialize(value) + else + value = tostring(value) + end + text = text:gsub('$' .. index, value) + end + return text + end + + --- Logs error and shows alert window. + -- This logs an error to the dcs.log and shows a popup window, + -- pausing the simulation. This works always even if logging is + -- disabled by setting a log level of "none" or 0. + -- @tparam string text the text with keywords to substitute. + -- @param ... variables to be used for substitution. + -- @usage myLogger:alert("Shit just hit the fan! WEEEE!!!11") + function mist.Logger:alert(text, ...) + text = formatText(text, unpack(arg)) + env.error(self.tag .. ' | ' .. text, true) + end + + --- Logs an error. + -- logs a message prefixed with this loggers tag to dcs.log as + -- long as at least the "error" log level (1) is set. + -- @tparam string text the text with keywords to substitute. + -- @param ... variables to be used for substitution. + -- @usage myLogger:error("Just an error!") + -- @usage myLogger:error("Foo is $1 instead of $2", foo, "bar") + function mist.Logger:error(text, ...) + if self.level >= 1 then + text = formatText(text, unpack(arg)) + env.error(self.tag .. ' | ' .. text) + end + end + + --- Logs a warning. + -- logs a message prefixed with this loggers tag to dcs.log as + -- long as at least the "warning" log level (2) is set. + -- @tparam string text the text with keywords to substitute. + -- @param ... variables to be used for substitution. + -- @usage myLogger:warn("Mother warned you! Those $1 from the interwebs are $2", {"geeks", 1337}) + function mist.Logger:warn(text, ...) + if self.level >= 2 then + text = formatText(text, unpack(arg)) + env.warning(self.tag .. ' | ' .. text) + end + end + + --- Logs a info. + -- logs a message prefixed with this loggers tag to dcs.log as + -- long as the highest log level (3) "info" is set. + -- @tparam string text the text with keywords to substitute. + -- @param ... variables to be used for substitution. + -- @see warn + function mist.Logger:info(text, ...) + if self.level >= 3 then + text = formatText(text, unpack(arg)) + env.info(self.tag .. ' | ' .. text) + end + end +end + +do -- mist.util scope + --- Utility functions. + -- E.g. conversions between units etc. + -- @section utils + mist.utils = {} + + --- Converts angle in radians to degrees. + -- @param angle angle in radians + -- @return angle in degrees + function mist.utils.toDegree (angle) + return angle*180/math.pi + end + + --- Converts angle in degrees to radians. + -- @param angle angle in degrees + -- @return angle in degrees + function mist.utils.toRadian (angle) + return angle*math.pi/180 + end + + --- Converts meters to nautical miles. + -- @param meters distance in meters + -- @return distance in nautical miles + function mist.utils.metersToNM (meters) + return meters/1852 + end + + --- Converts meters to feet. + -- @param meters distance in meters + -- @return distance in feet + function mist.utils.metersToFeet (meters) + return meters/0.3048 + end + + --- Converts nautical miles to meters. + -- @param nm distance in nautical miles + -- @return distance in meters + function mist.utils.NMToMeters (nm) + return NM*1852 + end + + --- Converts feet to meters. + -- @param feet distance in feet + -- @return distance in meters + function mist.utils.feetToMeters (feet) + return feet*0.3048 + end + + --- Converts meters per second to knots. + -- @param mps speed in m/s + -- @return speed in knots + function mist.utils.mpsToKnots (mps) + return mps*3600/1852 + end + + --- Converts meters per second to kilometers per hour. + -- @param mps speed in m/s + -- @return speed in km/h + function mist.utils.mpsToKmph (mps) + return mps*3.6 + end + + --- Converts knots to meters per second. + -- @param knots speed in knots + -- @return speed in m/s + function mist.utils.knotsToMps (knots) + return knots*1852/3600 + end + + --- Converts kilometers per hour to meters per second. + -- @param kmph speed in km/h + -- @return speed in m/s + function mist.utils.kmphToMps (kmph) + return kmph/3.6 + end + + --- Converts a Vec3 to a Vec2. + -- @tparam Vec3 vec the 3D vector + -- @return vector converted to Vec2 + function mist.utils.makeVec2(vec) + if vec.z then + return {x = vec.x, y = vec.z} + else + return {x = vec.x, y = vec.y} -- it was actually already vec2. + end + end + + --- Converts a Vec2 to a Vec3. + -- @tparam Vec2 vec the 2D vector + -- @param y optional new y axis (altitude) value. If omitted it's 0. + function mist.utils.makeVec3(vec, y) + if not vec.z then + if vec.alt and not y then + y = vec.alt + elseif not y then + y = 0 + end + return {x = vec.x, y = y, z = vec.y} + else + return {x = vec.x, y = vec.y, z = vec.z} -- it was already Vec3, actually. + end + end + + --- Converts a Vec2 to a Vec3 using ground level as altitude. + -- The ground level at the specific point is used as altitude (y-axis) + -- for the new vector. Optionally a offset can be specified. + -- @tparam Vec2 vec the 2D vector + -- @param[opt] offset offset to be applied to the ground level + -- @return new 3D vector + function mist.utils.makeVec3GL(vec, offset) + local adj = offset or 0 + + if not vec.z then + return {x = vec.x, y = (land.getHeight(vec) + adj), z = vec.y} + else + return {x = vec.x, y = (land.getHeight({x = vec.x, y = vec.z}) + adj), z = vec.z} + end + end + + --- Returns the center of a zone as Vec3. + -- @tparam string|table zone trigger zone name or table + -- @treturn Vec3 center of the zone + function mist.utils.zoneToVec3 (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 + + --- Returns heading-error corrected direction. + -- True-north corrected direction from point along vector vec. + -- @tparam Vec3 vec + -- @tparam Vec2 point + -- @return heading-error corrected direction from point. + function mist.utils.getDir(vec, point) + local dir = math.atan2(vec.z, vec.x) + if point then + dir = dir + mist.getNorthCorrection(point) + end + if dir < 0 then + dir = dir + 2 * math.pi -- put dir in range of 0 to 2*pi + end + return dir + end + + --- Returns distance in meters between two points. + -- @tparam Vec2|Vec3 point1 first point + -- @tparam Vec2|Vec3 point2 second point + -- @treturn number distance between given points. + function mist.utils.get2DDist(point1, point2) + point1 = mist.utils.makeVec3(point1) + point2 = mist.utils.makeVec3(point2) + return mist.vec.mag({x = point1.x - point2.x, y = 0, z = point1.z - point2.z}) + end + + --- Returns distance in meters between two points in 3D space. + -- @tparam Vec3 point1 first point + -- @tparam Vec3 point2 second point + -- @treturn number distancen between given points in 3D space. + function mist.utils.get3DDist(point1, point2) + return mist.vec.mag({x = point1.x - point2.x, y = point1.y - point2.y, z = point1.z - point2.z}) + end + + --- Creates a waypoint from a vector. + -- @tparam Vec2|Vec3 vec position of the new waypoint + -- @treturn Waypoint a new waypoint to be used inside paths. + function mist.utils.vecToWP(vec) + local newWP = {} + newWP.x = vec.x + newWP.y = vec.y + if vec.z then + newWP.alt = vec.y + newWP.y = vec.z + else + newWP.alt = land.getHeight({x = vec.x, y = vec.y}) + end + return newWP + end + + --- Creates a waypoint from a unit. + -- This function also considers the units speed. + -- The alt_type of this waypoint is set to "BARO". + -- @tparam Unit unit Unit whose position and speed will be used. + -- @treturn Waypoint new waypoint. + function mist.utils.unitToWP(unit) + if type(unit) == 'string' then + if Unit.getByName(unit) then + unit = Unit.getByName(unit) + end + end + if unit:isExist() == true then + local new = mist.utils.vecToWP(unit:getPosition().p) + new.speed = mist.vec.mag(unit:getVelocity()) + new.alt_type = "BARO" + + return new + end + return false + end + + --- Creates a deep copy of a object. + -- Usually this object is a table. + -- See also: from http://lua-users.org/wiki/CopyTable + -- @param object object to copy + -- @return copy of object + function mist.utils.deepCopy(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 + return _copy(object) + end + + --- Simple rounding function. + -- From http://lua-users.org/wiki/SimpleRound + -- use negative idp for rounding ahead of decimal place, positive for rounding after decimal place + -- @tparam number num number to round + -- @param idp + function mist.utils.round(num, idp) + local mult = 10^(idp or 0) + return math.floor(num * mult + 0.5) / mult + end + + --- Rounds all numbers inside a table. + -- @tparam table tbl table in which to round numbers + -- @param idp + function mist.utils.roundTbl(tbl, idp) + for id, val in pairs(tbl) do + if type(val) == 'number' then + tbl[id] = mist.utils.round(val, idp) + end + end + return tbl + end + + --- Executes the given string. + -- borrowed from Slmod + -- @tparam string s string containing LUA code. + -- @treturn boolean true if successfully executed, false otherwise + function mist.utils.dostring(s) + local f, err = loadstring(s) + if f then + return true, f() + else + return false, err + end + end + + --- Checks a table's types. + -- This function checks a tables types against a specifically forged type table. + -- @param fname + -- @tparam table type_tbl + -- @tparam table var_tbl + -- @usage -- specifically forged type table + -- type_tbl = { + -- {'table', 'number'}, + -- 'string', + -- 'number', + -- 'number', + -- {'string','nil'}, + -- {'number', 'nil'} + -- } + -- -- my_tbl index 1 must be a table or a number; + -- -- index 2, a string; index 3, a number; + -- -- index 4, a number; index 5, either a string or nil; + -- -- and index 6, either a number or nil. + -- mist.utils.typeCheck(type_tbl, my_tb) + -- @return true if table passes the check, false otherwise. + function mist.utils.typeCheck(fname, type_tbl, var_tbl) + --env.info('type check') + for type_key, type_val in pairs(type_tbl) do + --print('type_key:') + --print(type_key) + --print('type_val:') + --print(type_val) + + --type_key can be a table of accepted keys- so try to find one that is not nil + local type_key_str = '' + local act_key = type_key -- actual key within var_tbl - necessary to use for multiple possible key variables. Initialize to type_key + if type(type_key) == 'table' then + + for i = 1, #type_key do + if i ~= 1 then + type_key_str = type_key_str .. '/' + end + type_key_str = type_key_str .. tostring(type_key[i]) + if var_tbl[type_key[i]] ~= nil then + act_key = type_key[i] -- found a non-nil entry, make act_key now this val. + end + end + else + type_key_str = tostring(type_key) + end + + local err_msg = 'Error in function ' .. fname .. ', parameter "' .. type_key_str .. '", expected: ' + local passed_check = false + + if type(type_tbl[type_key]) == 'table' then + --print('err_msg, before and after:') + --print(err_msg) + for j = 1, #type_tbl[type_key] do + + if j == 1 then + err_msg = err_msg .. type_tbl[type_key][j] + else + err_msg = err_msg .. ' or ' .. type_tbl[type_key][j] + end + + if type(var_tbl[act_key]) == type_tbl[type_key][j] then + passed_check = true + end + end + --print(err_msg) + else + --print('err_msg, before and after:') + --print(err_msg) + err_msg = err_msg .. type_tbl[type_key] + --print(err_msg) + if type(var_tbl[act_key]) == type_tbl[type_key] then + passed_check = true + + end + + end + + if not passed_check then + err_msg = err_msg .. ', got ' .. type(var_tbl[act_key]) + return false, err_msg + end + end + return true + end + + --- Serializes the give variable to a string. + -- borrowed from slmod + -- @param var variable to serialize + -- @treturn string variable serialized to string + function mist.utils.basicSerialize(var) + if var == nil then + return "\"\"" + else + if ((type(var) == 'number') or + (type(var) == 'boolean') or + (type(var) == 'function') or + (type(var) == 'table') or + (type(var) == 'userdata') ) then + return tostring(var) + elseif type(var) == 'string' then + var = string.format('%q', var) + return var + end + end +end + +--- Serialize value +-- borrowed from slmod (serialize_slmod) +-- @param name +-- @param value value to serialize +-- @param level +function mist.utils.serialize(name, value, level) + --Based on ED's serialize_simple2 + local function basicSerialize(o) + if type(o) == "number" then + return tostring(o) + elseif type(o) == "boolean" then + return tostring(o) + else -- assume it is a string + return mist.utils.basicSerialize(o) + end + end + + local function serializeToTbl(name, value, level) + local var_str_tbl = {} + if level == nil then level = "" end + if level ~= "" then level = level.." " end + + table.insert(var_str_tbl, level .. name .. " = ") + + if type(value) == "number" or type(value) == "string" or type(value) == "boolean" then + table.insert(var_str_tbl, basicSerialize(value) .. ",\n") + elseif type(value) == "table" then + table.insert(var_str_tbl, "\n"..level.."{\n") + + for k,v in pairs(value) do -- serialize its fields + local key + if type(k) == "number" then + key = string.format("[%s]", k) + else + key = string.format("[%q]", k) + end + + table.insert(var_str_tbl, mist.utils.serialize(key, v, level.." ")) + + end + if level == "" then + table.insert(var_str_tbl, level.."} -- end of "..name.."\n") + + else + table.insert(var_str_tbl, level.."}, -- end of "..name.."\n") + + end + else + env.info("Cannot serialize a "..type(value)) + end + return var_str_tbl + end + + local t_str = serializeToTbl(name, value, level) + + return table.concat(t_str) +end + +--- Serialize value supporting cycles. +-- borrowed from slmod (serialize_wcycles) +-- @param name +-- @param value value to serialize +-- @param saved +function mist.utils.serializeWithCycles(name, value, saved) + --mostly straight out of Programming in Lua + local function basicSerialize(o) + if type(o) == "number" then + return tostring(o) + elseif type(o) == "boolean" then + return tostring(o) + else -- assume it is a string + return mist.utils.basicSerialize(o) + end + end + + local t_str = {} + saved = saved or {} -- initial value + if ((type(value) == 'string') or (type(value) == 'number') or (type(value) == 'table') or (type(value) == 'boolean')) then + table.insert(t_str, name .. " = ") + if type(value) == "number" or type(value) == "string" or type(value) == "boolean" then + table.insert(t_str, basicSerialize(value) .. "\n") + else + + if saved[value] then -- value already saved? + table.insert(t_str, saved[value] .. "\n") + else + saved[value] = name -- save name for next time + table.insert(t_str, "{}\n") + for k,v in pairs(value) do -- save its fields + local fieldname = string.format("%s[%s]", name, basicSerialize(k)) + table.insert(t_str, mist.utils.serializeWithCycles(fieldname, v, saved)) + end + end + end + return table.concat(t_str) + else + return "" + end +end + +--- Serialize a table to a single line string. +-- serialization of a table all on a single line, no comments, made to replace old get_table_string function +-- borrowed from slmod +-- @tparam table tbl table to serialize. +-- @treturn string string containing serialized table +function mist.utils.oneLineSerialize(tbl) + if type(tbl) == 'table' then --function only works for tables! + + local tbl_str = {} + + tbl_str[#tbl_str + 1] = '{ ' + + for ind,val in pairs(tbl) do -- serialize its fields + if type(ind) == "number" then + tbl_str[#tbl_str + 1] = '[' + tbl_str[#tbl_str + 1] = tostring(ind) + tbl_str[#tbl_str + 1] = '] = ' + else --must be a string + tbl_str[#tbl_str + 1] = '[' + tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(ind) + tbl_str[#tbl_str + 1] = '] = ' + end + + if ((type(val) == 'number') or (type(val) == 'boolean')) then + tbl_str[#tbl_str + 1] = tostring(val) + tbl_str[#tbl_str + 1] = ', ' + elseif type(val) == 'string' then + tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(val) + tbl_str[#tbl_str + 1] = ', ' + elseif type(val) == 'nil' then -- won't ever happen, right? + tbl_str[#tbl_str + 1] = 'nil, ' + elseif type(val) == 'table' then + tbl_str[#tbl_str + 1] = mist.utils.oneLineSerialize(val) + tbl_str[#tbl_str + 1] = ', ' --I think this is right, I just added it + else + env.info('unable to serialize value type ' .. mist.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind)) + end + + end + tbl_str[#tbl_str + 1] = '}' + return table.concat(tbl_str) + end +end + +--- Returns table in a easy readable string representation. +-- this function is not meant for serialization because it uses +-- newlines for better readability. +-- @param tbl table to show +-- @param loc +-- @param indent +-- @param tableshow_tbls +-- @return human readable string representation of given table +function mist.utils.tableShow(tbl, loc, indent, tableshow_tbls) --based on serialize_slmod, this is a _G serialization + tableshow_tbls = tableshow_tbls or {} --create table of tables + loc = loc or "" + indent = indent or "" + if type(tbl) == 'table' then --function only works for tables! + tableshow_tbls[tbl] = loc + + local tbl_str = {} + + tbl_str[#tbl_str + 1] = indent .. '{\n' + + for ind,val in pairs(tbl) do -- serialize its fields + if type(ind) == "number" then + tbl_str[#tbl_str + 1] = indent + tbl_str[#tbl_str + 1] = loc .. '[' + tbl_str[#tbl_str + 1] = tostring(ind) + tbl_str[#tbl_str + 1] = '] = ' + else + tbl_str[#tbl_str + 1] = indent + tbl_str[#tbl_str + 1] = loc .. '[' + tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(ind) + tbl_str[#tbl_str + 1] = '] = ' + end + + if ((type(val) == 'number') or (type(val) == 'boolean')) then + tbl_str[#tbl_str + 1] = tostring(val) + tbl_str[#tbl_str + 1] = ',\n' + elseif type(val) == 'string' then + tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(val) + tbl_str[#tbl_str + 1] = ',\n' + elseif type(val) == 'nil' then -- won't ever happen, right? + tbl_str[#tbl_str + 1] = 'nil,\n' + elseif type(val) == 'table' then + if tableshow_tbls[val] then + tbl_str[#tbl_str + 1] = tostring(val) .. ' already defined: ' .. tableshow_tbls[val] .. ',\n' + else + tableshow_tbls[val] = loc .. '[' .. mist.utils.basicSerialize(ind) .. ']' + tbl_str[#tbl_str + 1] = tostring(val) .. ' ' + tbl_str[#tbl_str + 1] = mist.utils.tableShow(val, loc .. '[' .. mist.utils.basicSerialize(ind).. ']', indent .. ' ', tableshow_tbls) + tbl_str[#tbl_str + 1] = ',\n' + end + elseif type(val) == 'function' then + if debug and debug.getinfo then + local fcnname = tostring(val) + local info = debug.getinfo(val, "S") + if info.what == "C" then + tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', C function') .. ',\n' + else + if (string.sub(info.source, 1, 2) == [[./]]) then + tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', defined in (' .. info.linedefined .. '-' .. info.lastlinedefined .. ')' .. info.source) ..',\n' + else + tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', defined in (' .. info.linedefined .. '-' .. info.lastlinedefined .. ')') ..',\n' + end + end + + else + tbl_str[#tbl_str + 1] = 'a function,\n' + end + else + tbl_str[#tbl_str + 1] = 'unable to serialize value type ' .. mist.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind) + end + end + + tbl_str[#tbl_str + 1] = indent .. '}' + return table.concat(tbl_str) + end +end +end + +do -- mist.debug scope + --- Debug functions + -- @section mist.debug + mist.debug = {} + + --- Dumps the global table _G. + -- This dumps the global table _G to a file in + -- the DCS\Logs directory. + -- This function requires you to disable script sanitization + -- in $DCS_ROOT\Scripts\MissionScripting.lua to access lfs and io + -- libraries. + -- @param fname + function mist.debug.dump_G(fname) + if lfs and io then + local fdir = lfs.writedir() .. [[Logs\]] .. fname + local f = io.open(fdir, 'w') + f:write(mist.utils.tableShow(_G)) + f:close() + local errmsg = 'mist.debug.dump_G wrote data to ' .. fdir + env.info(errmsg) + trigger.action.outText(errmsg, 10) + else + local errmsg = 'Error: insufficient libraries to run mist.debug.dump_G, you must disable the sanitization of the io and lfs libraries in ./Scripts/MissionScripting.lua' + env.info(errmsg) + trigger.action.outText(errmsg, 10) + end + end + + --- Write debug data to file. + -- This function requires you to disable script sanitization + -- in $DCS_ROOT\Scripts\MissionScripting.lua to access lfs and io + -- libraries. + -- @param fcn + -- @param fcnVars + -- @param fname + function mist.debug.writeData(fcn, fcnVars, fname) + if lfs and io then + local fdir = lfs.writedir() .. [[Logs\]] .. fname + local f = io.open(fdir, 'w') + f:write(fcn(unpack(fcnVars, 1, table.maxn(fcnVars)))) + f:close() + local errmsg = 'mist.debug.writeData wrote data to ' .. fdir + env.info(errmsg) + trigger.action.outText(errmsg, 10) + else + local errmsg = 'Error: insufficient libraries to run mist.debug.writeData, you must disable the sanitization of the io and lfs libraries in ./Scripts/MissionScripting.lua' + env.info(errmsg) + trigger.action.outText(errmsg, 10) + end + end + + --- Write mist databases to file. + -- This function requires you to disable script sanitization + -- in $DCS_ROOT\Scripts\MissionScripting.lua to access lfs and io + -- libraries. + function mist.debug.dumpDBs() + for DBname, DB in pairs(mist.DBs) do + if type(DB) == 'table' and type(DBname) == 'string' then + mist.debug.writeData(mist.utils.serialize, {DBname, DB}, 'mist_DBs_' .. DBname .. '.lua') + end + end + end +end + +do -- mist.vec scope + --- 3D Vector functions + -- @section mist.vec + mist.vec = {} + + --- Vector addition. + -- @tparam Vec3 vec1 first vector + -- @tparam Vec3 vec2 second vector + -- @treturn Vec3 new vector, sum of vec1 and vec2. + function mist.vec.add(vec1, vec2) + return {x = vec1.x + vec2.x, y = vec1.y + vec2.y, z = vec1.z + vec2.z} + end + + --- Vector substraction. + -- @tparam Vec3 vec1 first vector + -- @tparam Vec3 vec2 second vector + -- @treturn Vec3 new vector, vec2 substracted from vec1. + function mist.vec.sub(vec1, vec2) + return {x = vec1.x - vec2.x, y = vec1.y - vec2.y, z = vec1.z - vec2.z} + end + + --- Vector scalar multiplication. + -- @tparam Vec3 vec vector to multiply + -- @tparam number mult scalar multiplicator + -- @treturn Vec3 new vector multiplied with the given scalar + function mist.vec.scalarMult(vec, mult) + return {x = vec.x*mult, y = vec.y*mult, z = vec.z*mult} + end + + mist.vec.scalar_mult = mist.vec.scalarMult + + --- Vector dot product. + -- @tparam Vec3 vec1 first vector + -- @tparam Vec3 vec2 second vector + -- @treturn number dot product of given vectors + function mist.vec.dp (vec1, vec2) + return vec1.x*vec2.x + vec1.y*vec2.y + vec1.z*vec2.z + end + + --- Vector cross product. + -- @tparam Vec3 vec1 first vector + -- @tparam Vec3 vec2 second vector + -- @treturn Vec3 new vector, cross product of vec1 and vec2. + function mist.vec.cp(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 + + --- Vector magnitude + -- @tparam Vec3 vec vector + -- @treturn number magnitude of vector vec + function mist.vec.mag(vec) + return (vec.x^2 + vec.y^2 + vec.z^2)^0.5 + end + + --- Unit vector + -- @tparam Vec3 vec + -- @treturn Vec3 unit vector of vec + function mist.vec.getUnitVec(vec) + local mag = mist.vec.mag(vec) + return { x = vec.x/mag, y = vec.y/mag, z = vec.z/mag } + end + + --- Rotate vector. + -- @tparam Vec2 vec2 to rotoate + -- @tparam number theta + -- @return Vec2 rotated vector. + function mist.vec.rotateVec2(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 +end + +do -- mist.flagFunc scope + --- Flag functions. + -- The mist "Flag functions" are functions that are similar to Slmod functions + -- that detect a game condition and set a flag when that game condition is met. + -- + -- They are intended to be used by persons with little or no experience in Lua + -- programming, but with a good knowledge of the DCS mission editor. + -- @section mist.flagFunc + mist.flagFunc = {} + + --- Sets a flag if map objects are destroyed inside a zone. + -- Once this function is run, it will start a continuously evaluated process + -- that will set a flag true if map objects (such as bridges, buildings in + -- town, etc.) die (or have died) in a mission editor zone (or set of zones). + -- This will only happen once; once the flag is set true, the process ends. + -- @usage + -- -- Example vars table + -- vars = { + -- zones = { "zone1", "zone2" }, -- can also be a single string + -- flag = 3, -- number of the flag + -- stopflag = 4, -- optional number of the stop flag + -- req_num = 10, -- optional minimum amount of map objects needed to die + -- } + -- mist.flagFuncs.mapobjs_dead_zones(vars) + -- @tparam table vars table containing parameters. + function mist.flagFunc.mapobjs_dead_zones(vars) + --[[vars needs to be: +zones = table or string, +flag = number, +stopflag = number or nil, +req_num = number or nil + +AND used by function, +initial_number + +]] + -- type_tbl + local type_tbl = { + [{'zones', 'zone'}] = {'table', 'string'}, + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + [{'req_num', 'reqnum'}] = {'number', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.mapobjs_dead_zones', type_tbl, vars) + assert(err, errmsg) + local zones = vars.zones or vars.zone + local flag = vars.flag + local stopflag = vars.stopflag or -1 + local req_num = vars.req_num or vars.reqnum or 1 + local initial_number = vars.initial_number + + if type(zones) == 'string' then + zones = {zones} + end + + if not initial_number then + initial_number = #mist.getDeadMapObjsInZones(zones) + end + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + if (#mist.getDeadMapObjsInZones(zones) - initial_number) >= req_num and trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + return + else + mist.scheduleFunction(mist.flagFunc.mapobjs_dead_zones, {{zones = zones, flag = flag, stopflag = stopflag, req_num = req_num, initial_number = initial_number}}, timer.getTime() + 1) + end + end + end + + --- Sets a flag if map objects are destroyed inside a polygon. + -- Once this function is run, it will start a continuously evaluated process + -- that will set a flag true if map objects (such as bridges, buildings in + -- town, etc.) die (or have died) in a polygon. + -- This will only happen once; once the flag is set true, the process ends. + -- @usage + -- -- Example vars table + -- vars = { + -- zone = { + -- [1] = mist.DBs.unitsByName['NE corner'].point, + -- [2] = mist.DBs.unitsByName['SE corner'].point, + -- [3] = mist.DBs.unitsByName['SW corner'].point, + -- [4] = mist.DBs.unitsByName['NW corner'].point + -- } + -- flag = 3, -- number of the flag + -- stopflag = 4, -- optional number of the stop flag + -- req_num = 10, -- optional minimum amount of map objects needed to die + -- } + -- mist.flagFuncs.mapobjs_dead_zones(vars) + -- @tparam table vars table containing parameters. + function mist.flagFunc.mapobjs_dead_polygon(vars) + --[[vars needs to be: +zone = table, +flag = number, +stopflag = number or nil, +req_num = number or nil + +AND used by function, +initial_number + +]] + -- type_tbl + local type_tbl = { + [{'zone', 'polyzone'}] = 'table', + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + [{'req_num', 'reqnum'}] = {'number', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.mapobjs_dead_polygon', type_tbl, vars) + assert(err, errmsg) + local zone = vars.zone or vars.polyzone + local flag = vars.flag + local stopflag = vars.stopflag or -1 + local req_num = vars.req_num or vars.reqnum or 1 + local initial_number = vars.initial_number + + if not initial_number then + initial_number = #mist.getDeadMapObjsInPolygonZone(zone) + end + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + if (#mist.getDeadMapObjsInPolygonZone(zone) - initial_number) >= req_num and trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + return + else + mist.scheduleFunction(mist.flagFunc.mapobjs_dead_polygon, {{zone = zone, flag = flag, stopflag = stopflag, req_num = req_num, initial_number = initial_number}}, timer.getTime() + 1) + end + end + end + + function mist.flagFunc.units_in_polygon(vars) + --[[vars needs to be: +units = table, +zone = table, +flag = number, +stopflag = number or nil, +maxalt = number or nil, +interval = number or nil, +req_num = number or nil +toggle = boolean or nil +unitTableDef = table or nil +]] + -- type_tbl + local type_tbl = { + [{'units', 'unit'}] = 'table', + [{'zone', 'polyzone'}] = 'table', + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + [{'maxalt', 'alt'}] = {'number', 'nil'}, + interval = {'number', 'nil'}, + [{'req_num', 'reqnum'}] = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + unitTableDef = {'table', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_in_polygon', type_tbl, vars) + assert(err, errmsg) + local units = vars.units or vars.unit + local zone = vars.zone or vars.polyzone + local flag = vars.flag + local stopflag = vars.stopflag or -1 + local interval = vars.interval or 1 + local maxalt = vars.maxalt or vars.alt + local req_num = vars.req_num or vars.reqnum or 1 + local toggle = vars.toggle or nil + local unitTableDef = vars.unitTableDef + + if not units.processed then + unitTableDef = mist.utils.deepCopy(units) + end + + if (units.processed and units.processed < mist.getLastDBUpdateTime()) or not units.processed then -- run unit table short cuts + units = mist.makeUnitTable(unitTableDef) + end + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == 0) then + local num_in_zone = 0 + for i = 1, #units do + local unit = Unit.getByName(units[i]) + if unit then + local pos = unit:getPosition().p + if mist.pointInPolygon(pos, zone, maxalt) then + num_in_zone = num_in_zone + 1 + if num_in_zone >= req_num and trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + break + end + end + end + end + if toggle and (num_in_zone < req_num) and trigger.misc.getUserFlag(flag) > 0 then + trigger.action.setUserFlag(flag, false) + end + -- do another check in case stopflag was set true by this function + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == 0) then + mist.scheduleFunction(mist.flagFunc.units_in_polygon, {{units = units, zone = zone, flag = flag, stopflag = stopflag, interval = interval, req_num = req_num, maxalt = maxalt, toggle = toggle, unitTableDef = unitTableDef}}, timer.getTime() + interval) + end + end + + end + + function mist.flagFunc.units_in_zones(vars) + --[[vars needs to be: + units = table, + zones = table, + flag = number, + stopflag = number or nil, + zone_type = string or nil, + req_num = number or nil, + interval = number or nil + toggle = boolean or nil + ]] + -- type_tbl + local type_tbl = { + units = 'table', + zones = 'table', + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + [{'zone_type', 'zonetype'}] = {'string', 'nil'}, + [{'req_num', 'reqnum'}] = {'number', 'nil'}, + interval = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + unitTableDef = {'table', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_in_zones', type_tbl, vars) + assert(err, errmsg) + local units = vars.units + local zones = vars.zones + local flag = vars.flag + local stopflag = vars.stopflag or -1 + local zone_type = vars.zone_type or vars.zonetype or 'cylinder' + local req_num = vars.req_num or vars.reqnum or 1 + local interval = vars.interval or 1 + local toggle = vars.toggle or nil + local unitTableDef = vars.unitTableDef + + if not units.processed then + unitTableDef = mist.utils.deepCopy(units) + end + + if (units.processed and units.processed < mist.getLastDBUpdateTime()) or not units.processed then -- run unit table short cuts + units = mist.makeUnitTable(unitTableDef) + end + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + + local in_zone_units = mist.getUnitsInZones(units, zones, zone_type) + + if #in_zone_units >= req_num and trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + elseif #in_zone_units < req_num and toggle then + trigger.action.setUserFlag(flag, false) + end + -- do another check in case stopflag was set true by this function + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + mist.scheduleFunction(mist.flagFunc.units_in_zones, {{units = units, zones = zones, flag = flag, stopflag = stopflag, zone_type = zone_type, req_num = req_num, interval = interval, toggle = toggle, unitTableDef = unitTableDef}}, timer.getTime() + interval) + end + end + + end + + function mist.flagFunc.units_in_moving_zones(vars) + --[[vars needs to be: + units = table, + zone_units = table, + radius = number, + flag = number, + stopflag = number or nil, + zone_type = string or nil, + req_num = number or nil, + interval = number or nil + toggle = boolean or nil + ]] + -- type_tbl + local type_tbl = { + units = 'table', + [{'zone_units', 'zoneunits'}] = 'table', + radius = 'number', + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + [{'zone_type', 'zonetype'}] = {'string', 'nil'}, + [{'req_num', 'reqnum'}] = {'number', 'nil'}, + interval = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + unitTableDef = {'table', 'nil'}, + zUnitTableDef = {'table', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_in_moving_zones', type_tbl, vars) + assert(err, errmsg) + local units = vars.units + local zone_units = vars.zone_units or vars.zoneunits + local radius = vars.radius + local flag = vars.flag + local stopflag = vars.stopflag or -1 + local zone_type = vars.zone_type or vars.zonetype or 'cylinder' + local req_num = vars.req_num or vars.reqnum or 1 + local interval = vars.interval or 1 + local toggle = vars.toggle or nil + local unitTableDef = vars.unitTableDef + local zUnitTableDef = vars.zUnitTableDef + + if not units.processed then + unitTableDef = mist.utils.deepCopy(units) + end + + if not zone_units.processed then + zUnitTableDef = mist.utils.deepCopy(zone_units) + end + + if (units.processed and units.processed < mist.getLastDBUpdateTime()) or not units.processed then -- run unit table short cuts + units = mist.makeUnitTable(unitTableDef) + end + + if (zone_units.processed and zone_units.processed < mist.getLastDBUpdateTime()) or not zone_units.processed then -- run unit table short cuts + zone_units = mist.makeUnitTable(zUnitTableDef) + end + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + + local in_zone_units = mist.getUnitsInMovingZones(units, zone_units, radius, zone_type) + + if #in_zone_units >= req_num and trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + elseif #in_zone_units < req_num and toggle then + trigger.action.setUserFlag(flag, false) + end + -- do another check in case stopflag was set true by this function + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + mist.scheduleFunction(mist.flagFunc.units_in_moving_zones, {{units = units, zone_units = zone_units, radius = radius, flag = flag, stopflag = stopflag, zone_type = zone_type, req_num = req_num, interval = interval, toggle = toggle, unitTableDef = unitTableDef, zUnitTableDef = zUnitTableDef}}, timer.getTime() + interval) + end + end + + end + + function mist.flagFunc.units_LOS(vars) + --[[vars needs to be: +unitset1 = table, +altoffset1 = number, +unitset2 = table, +altoffset2 = number, +flag = number, +stopflag = number or nil, +radius = number or nil, +interval = number or nil, +req_num = number or nil +toggle = boolean or nil +]] + -- type_tbl + local type_tbl = { + [{'unitset1', 'units1'}] = 'table', + [{'altoffset1', 'alt1'}] = 'number', + [{'unitset2', 'units2'}] = 'table', + [{'altoffset2', 'alt2'}] = 'number', + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + [{'req_num', 'reqnum'}] = {'number', 'nil'}, + interval = {'number', 'nil'}, + radius = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + unitTableDef1 = {'table', 'nil'}, + unitTableDef2 = {'table', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_LOS', type_tbl, vars) + assert(err, errmsg) + local unitset1 = vars.unitset1 or vars.units1 + local altoffset1 = vars.altoffset1 or vars.alt1 + local unitset2 = vars.unitset2 or vars.units2 + local altoffset2 = vars.altoffset2 or vars.alt2 + local flag = vars.flag + local stopflag = vars.stopflag or -1 + local interval = vars.interval or 1 + local radius = vars.radius or math.huge + local req_num = vars.req_num or vars.reqnum or 1 + local toggle = vars.toggle or nil + local unitTableDef1 = vars.unitTableDef1 + local unitTableDef2 = vars.unitTableDef2 + + if not unitset1.processed then + unitTableDef1 = mist.utils.deepCopy(unitset1) + end + + if not unitset2.processed then + unitTableDef2 = mist.utils.deepCopy(unitset2) + end + + if (unitset1.processed and unitset1.processed < mist.getLastDBUpdateTime()) or not unitset1.processed then -- run unit table short cuts + unitset1 = mist.makeUnitTable(unitTableDef1) + end + + if (unitset2.processed and unitset2.processed < mist.getLastDBUpdateTime()) or not unitset2.processed then -- run unit table short cuts + unitset2 = mist.makeUnitTable(unitTableDef2) + end + + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + + local unitLOSdata = mist.getUnitsLOS(unitset1, altoffset1, unitset2, altoffset2, radius) + + if #unitLOSdata >= req_num and trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + elseif #unitLOSdata < req_num and toggle then + trigger.action.setUserFlag(flag, false) + end + -- do another check in case stopflag was set true by this function + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + mist.scheduleFunction(mist.flagFunc.units_LOS, {{unitset1 = unitset1, altoffset1 = altoffset1, unitset2 = unitset2, altoffset2 = altoffset2, flag = flag, stopflag = stopflag, radius = radius, req_num = req_num, interval = interval, toggle = toggle, unitTableDef1 = unitTableDef1, unitTableDef2 = unitTableDef2}}, timer.getTime() + interval) + end + end + end + + function mist.flagFunc.group_alive(vars) + --[[vars +groupName +flag +toggle +interval +stopFlag + +]] + local type_tbl = { + [{'group', 'groupname', 'gp', 'groupName'}] = 'string', + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + interval = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_alive', type_tbl, vars) + assert(err, errmsg) + + local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname + local flag = vars.flag + local stopflag = vars.stopflag or -1 + local interval = vars.interval or 1 + local toggle = vars.toggle or nil + + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true and #Group.getByName(groupName):getUnits() > 0 then + if trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + end + else + if toggle then + trigger.action.setUserFlag(flag, false) + end + end + end + + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + mist.scheduleFunction(mist.flagFunc.group_alive, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle}}, timer.getTime() + interval) + end + + end + + function mist.flagFunc.group_dead(vars) + local type_tbl = { + [{'group', 'groupname', 'gp', 'groupName'}] = 'string', + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + interval = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_dead', type_tbl, vars) + assert(err, errmsg) + + local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname + local flag = vars.flag + local stopflag = vars.stopflag or -1 + local interval = vars.interval or 1 + local toggle = vars.toggle or nil + + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + if (Group.getByName(groupName) and Group.getByName(groupName):isExist() == false) or (Group.getByName(groupName) and #Group.getByName(groupName):getUnits() < 1) or not Group.getByName(groupName) then + if trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + end + else + if toggle then + trigger.action.setUserFlag(flag, false) + end + end + end + + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + mist.scheduleFunction(mist.flagFunc.group_dead, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle}}, timer.getTime() + interval) + end + end + + function mist.flagFunc.group_alive_less_than(vars) + local type_tbl = { + [{'group', 'groupname', 'gp', 'groupName'}] = 'string', + percent = 'number', + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + interval = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_alive_less_than', type_tbl, vars) + assert(err, errmsg) + + local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname + local flag = vars.flag + local percent = vars.percent + local stopflag = vars.stopflag or -1 + local interval = vars.interval or 1 + local toggle = vars.toggle or nil + + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then + if Group.getByName(groupName):getSize()/Group.getByName(groupName):getInitialSize() < percent/100 then + if trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + end + else + if toggle then + trigger.action.setUserFlag(flag, false) + end + end + else + if trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + end + end + end + + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + mist.scheduleFunction(mist.flagFunc.group_alive_less_than, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle, percent = percent}}, timer.getTime() + interval) + end + end + + function mist.flagFunc.group_alive_more_than(vars) + local type_tbl = { + [{'group', 'groupname', 'gp', 'groupName'}] = 'string', + percent = 'number', + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + interval = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_alive_more_than', type_tbl, vars) + assert(err, errmsg) + + local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname + local flag = vars.flag + local percent = vars.percent + local stopflag = vars.stopflag or -1 + local interval = vars.interval or 1 + local toggle = vars.toggle or nil + + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then + if Group.getByName(groupName):getSize()/Group.getByName(groupName):getInitialSize() > percent/100 then + if trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + end + else + if toggle and trigger.misc.getUserFlag(flag) == 1 then + trigger.action.setUserFlag(flag, false) + end + end + else --- just in case + if toggle and trigger.misc.getUserFlag(flag) == 1 then + trigger.action.setUserFlag(flag, false) + end + end + end + + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + mist.scheduleFunction(mist.flagFunc.group_alive_more_than, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle, percent = percent}}, timer.getTime() + interval) + end + end + +end + +do -- mist.msg scope + local messageList = {} + -- this defines the max refresh rate of the message box it honestly only needs to + -- go faster than this for precision timing stuff (which could be its own function) + local messageDisplayRate = 0.1 + local messageID = 0 + local displayActive = false + local displayFuncId = 0 + + local caSlots = false + local caMSGtoGroup = false + + if env.mission.groundControl then -- just to be sure? + for index, value in pairs(env.mission.groundControl) do + if type(value) == 'table' then + for roleName, roleVal in pairs(value) do + for rIndex, rVal in pairs(roleVal) do + if rIndex == 'red' or rIndex == 'blue' then + if env.mission.groundControl[index][roleName][rIndex] > 0 then + caSlots = true + break + end + end + end + end + elseif type(value) == 'boolean' and value == true then + caSlots = true + break + end + end + end + + local function mistdisplayV5() + --[[thoughts to improve upon + event handler based activeClients table. + display messages only when there is an update + possibly co-routine it. +]] + end + + local function mistdisplayV4() + local activeClients = {} + + for clientId, clientData in pairs(mist.DBs.humansById) do + if Unit.getByName(clientData.unitName) and Unit.getByName(clientData.unitName):isExist() == true then + activeClients[clientData.groupId] = clientData.groupName + end + end + + --[[if caSlots == true and caMSGtoGroup == true then + + end]] + + + if #messageList > 0 then + if displayActive == false then + displayActive = true + end + --mist.debug.writeData(mist.utils.serialize,{'msg', messageList}, 'messageList.lua') + local msgTableText = {} + local msgTableSound = {} + + for messageId, messageData in pairs(messageList) do + if messageData.displayedFor > messageData.displayTime then + messageData:remove() -- now using the remove/destroy function. + else + if messageData.displayedFor then + messageData.displayedFor = messageData.displayedFor + messageDisplayRate + end + local nextSound = 1000 + local soundIndex = 0 + + if messageData.multSound and #messageData.multSound > 0 then + for index, sData in pairs(messageData.multSound) do + if sData.time <= messageData.displayedFor and sData.played == false and sData.time < nextSound then -- find index of the next sound to be played + nextSound = sData.time + soundIndex = index + end + end + if soundIndex ~= 0 then + messageData.multSound[soundIndex].played = true + end + end + + for recIndex, recData in pairs(messageData.msgFor) do -- iterate recipiants + if recData == 'RED' or recData == 'BLUE' or activeClients[recData] then -- rec exists + if messageData.text then -- text + if not msgTableText[recData] then -- create table entry for text + msgTableText[recData] = {} + msgTableText[recData].text = {} + if recData == 'RED' or recData == 'BLUE' then + msgTableText[recData].text[1] = '-------Combined Arms Message-------- \n' + end + msgTableText[recData].text[#msgTableText[recData].text + 1] = messageData.text + msgTableText[recData].displayTime = messageData.displayTime - messageData.displayedFor + else -- add to table entry and adjust display time if needed + if recData == 'RED' or recData == 'BLUE' then + msgTableText[recData].text[#msgTableText[recData].text + 1] = '\n ---------------- Combined Arms Message: \n' + else + msgTableText[recData].text[#msgTableText[recData].text + 1] = '\n ---------------- \n' + end + msgTableText[recData].text[#msgTableText[recData].text + 1] = messageData.text + if msgTableText[recData].displayTime < messageData.displayTime - messageData.displayedFor then + msgTableText[recData].displayTime = messageData.displayTime - messageData.displayedFor + else + msgTableText[recData].displayTime = 1 + end + end + end + if soundIndex ~= 0 then + msgTableSound[recData] = messageData.multSound[soundIndex].file + end + end + end + + + end + end + ------- new display + + if caSlots == true and caMSGtoGroup == false then + if msgTableText['RED'] then + trigger.action.outTextForCoalition(coalition.side.RED, table.concat(msgTableText['RED'].text), msgTableText['RED'].displayTime, true) + + end + if msgTableText['BLUE'] then + trigger.action.outTextForCoalition(coalition.side.BLUE, table.concat(msgTableText['BLUE'].text), msgTableText['BLUE'].displayTime, true) + end + end + + for index, msgData in pairs(msgTableText) do + if type(index) == 'number' then -- its a groupNumber + trigger.action.outTextForGroup(index, table.concat(msgData.text), msgData.displayTime, true) + end + end + --- new audio + if msgTableSound['RED'] then + trigger.action.outSoundForCoalition(coalition.side.RED, msgTableSound['RED']) + end + if msgTableSound['BLUE'] then + trigger.action.outSoundForCoalition(coalition.side.BLUE, msgTableSound['BLUE']) + end + + + for index, file in pairs(msgTableSound) do + if type(index) == 'number' then -- its a groupNumber + trigger.action.outSoundForGroup(index, file) + end + end + else + mist.removeFunction(displayFuncId) + displayActive = false + end + + end + + local typeBase = { + ['Mi-8MT'] = {'Mi-8MTV2', 'Mi-8MTV', 'Mi-8'}, + ['MiG-21Bis'] = {'Mig-21'}, + ['MiG-15bis'] = {'Mig-15'}, + ['FW-190D9'] = {'FW-190'}, + ['Bf-109K-4'] = {'Bf-109'}, + } + + --[[function mist.setCAGroupMSG(val) + if type(val) == 'boolean' then + caMSGtoGroup = val + return true + end + return false +end]] + + mist.message = { + + + add = function(vars) + local function msgSpamFilter(recList, spamBlockOn) + for id, name in pairs(recList) do + if name == spamBlockOn then + -- env.info('already on recList') + return recList + end + end + --env.info('add to recList') + table.insert(recList, spamBlockOn) + return recList + end + + --[[ + local vars = {} + vars.text = 'Hello World' + vars.displayTime = 20 + vars.msgFor = {coa = {'red'}, countries = {'Ukraine', 'Georgia'}, unitTypes = {'A-10C'}} + mist.message.add(vars) + + Displays the message for all red coalition players. Players belonging to Ukraine and Georgia, and all A-10Cs on the map + + ]] + + + local new = {} + new.text = vars.text -- The actual message + new.displayTime = vars.displayTime -- How long will the message appear for + new.displayedFor = 0 -- how long the message has been displayed so far + new.name = vars.name -- ID to overwrite the older message (if it exists) Basically it replaces a message that is displayed with new text. + new.addedAt = timer.getTime() + new.update = true + + if vars.multSound and vars.multSound[1] then + new.multSound = vars.multSound + else + new.multSound = {} + end + + if vars.sound or vars.fileName then -- converts old sound file system into new multSound format + local sound = vars.sound + if vars.fileName then + sound = vars.fileName + end + new.multSound[#new.multSound+1] = {time = 0.1, file = sound} + end + + if #new.multSound > 0 then + for i, data in pairs(new.multSound) do + data.played = false + end + end + + local newMsgFor = {} -- list of all groups message displays for + for forIndex, forData in pairs(vars.msgFor) do + for list, listData in pairs(forData) do + for clientId, clientData in pairs(mist.DBs.humansById) do + forIndex = string.lower(forIndex) + if type(listData) == 'string' then + listData = string.lower(listData) + end + if (forIndex == 'coa' and (listData == string.lower(clientData.coalition) or listData == 'all')) or (forIndex == 'countries' and string.lower(clientData.country) == listData) or (forIndex == 'units' and string.lower(clientData.unitName) == listData) then -- + newMsgFor = msgSpamFilter(newMsgFor, clientData.groupId) -- so units dont get the same message twice if complex rules are given + --table.insert(newMsgFor, clientId) + elseif forIndex == 'unittypes' then + for typeId, typeData in pairs(listData) do + local found = false + for clientDataEntry, clientDataVal in pairs(clientData) do + if type(clientDataVal) == 'string' then + if mist.matchString(list, clientDataVal) == true or list == 'all' then + local sString = typeData + for rName, pTbl in pairs(typeBase) do -- just a quick check to see if the user may have meant something and got the specific type of the unit wrong + for pIndex, pName in pairs(pTbl) do + if mist.stringMatch(sString, pName) then + sString = rName + end + end + end + if sString == clientData.type then + found = true + newMsgFor = msgSpamFilter(newMsgFor, clientData.groupId) -- sends info oto other function to see if client is already recieving the current message. + --table.insert(newMsgFor, clientId) + end + end + end + if found == true then -- shouldn't this be elsewhere too? + break + end + end + end + + end + end + for coaData, coaId in pairs(coalition.side) do + if string.lower(forIndex) == 'coa' or string.lower(forIndex) == 'ca' then + if listData == string.lower(coaData) or listData == 'all' then + newMsgFor = msgSpamFilter(newMsgFor, coaData) + end + end + end + end + end + + if #newMsgFor > 0 then + new.msgFor = newMsgFor -- I swear its not confusing + + else + return false + end + + + if vars.name and type(vars.name) == 'string' then + for i = 1, #messageList do + if messageList[i].name then + if messageList[i].name == vars.name then + --env.info('updateMessage') + messageList[i].displayedFor = 0 + messageList[i].addedAt = timer.getTime() + messageList[i].sound = new.sound + messageList[i].text = new.text + messageList[i].msgFor = new.msgFor + messageList[i].multSound = new.multSound + messageList[i].update = true + return messageList[i].messageID + end + end + end + end + + messageID = messageID + 1 + new.messageID = messageID + + --mist.debug.writeData(mist.utils.serialize,{'msg', new}, 'newMsg.lua') + + + messageList[#messageList + 1] = new + + local mt = { __index = mist.message} + setmetatable(new, mt) + + if displayActive == false then + displayActive = true + displayFuncId = mist.scheduleFunction(mistdisplayV4, {}, timer.getTime() + messageDisplayRate, messageDisplayRate) + end + + return messageID + + end, + + remove = function(self) -- Now a self variable; the former functionality taken up by mist.message.removeById. + for i, msgData in pairs(messageList) do + if messageList[i] == self then + table.remove(messageList, i) + return true --removal successful + end + end + return false -- removal not successful this script fails at life! + end, + + removeById = function(id) -- This function is NOT passed a self variable; it is the remove by id function. + for i, msgData in pairs(messageList) do + if messageList[i].messageID == id then + table.remove(messageList, i) + return true --removal successful + end + end + return false -- removal not successful this script fails at life! + end, + } + +end + +do -- mist.demos scope + --- Demo functions. + -- @section mist.demos + mist.demos = {} + + function mist.demos.printFlightData(unit) + if unit:isExist() then + local function printData(unit, prevVel, prevE, prevTime) + local angles = mist.getAttitude(unit) + if angles then + local Heading = angles.Heading + local Pitch = angles.Pitch + local Roll = angles.Roll + local Yaw = angles.Yaw + local AoA = angles.AoA + local ClimbAngle = angles.ClimbAngle + + if not Heading then + Heading = 'NA' + else + Heading = string.format('%12.2f', mist.utils.toDegree(Heading)) + end + + if not Pitch then + Pitch = 'NA' + else + Pitch = string.format('%12.2f', mist.utils.toDegree(Pitch)) + end + + if not Roll then + Roll = 'NA' + else + Roll = string.format('%12.2f', mist.utils.toDegree(Roll)) + end + + local AoAplusYaw = 'NA' + if AoA and Yaw then + AoAplusYaw = string.format('%12.2f', mist.utils.toDegree((AoA^2 + Yaw^2)^0.5)) + end + + if not Yaw then + Yaw = 'NA' + else + Yaw = string.format('%12.2f', mist.utils.toDegree(Yaw)) + end + + if not AoA then + AoA = 'NA' + else + AoA = string.format('%12.2f', mist.utils.toDegree(AoA)) + end + + if not ClimbAngle then + ClimbAngle = 'NA' + else + ClimbAngle = string.format('%12.2f', mist.utils.toDegree(ClimbAngle)) + end + local unitPos = unit:getPosition() + local unitVel = unit:getVelocity() + local curTime = timer.getTime() + local absVel = string.format('%12.2f', mist.vec.mag(unitVel)) + + + local unitAcc = 'NA' + local Gs = 'NA' + local axialGs = 'NA' + local transGs = 'NA' + if prevVel and prevTime then + local xAcc = (unitVel.x - prevVel.x)/(curTime - prevTime) + local yAcc = (unitVel.y - prevVel.y)/(curTime - prevTime) + local zAcc = (unitVel.z - prevVel.z)/(curTime - prevTime) + + unitAcc = string.format('%12.2f', mist.vec.mag({x = xAcc, y = yAcc, z = zAcc})) + Gs = string.format('%12.2f', mist.vec.mag({x = xAcc, y = yAcc + 9.81, z = zAcc})/9.81) + axialGs = string.format('%12.2f', mist.vec.dp({x = xAcc, y = yAcc + 9.81, z = zAcc}, unitPos.x)/9.81) + transGs = string.format('%12.2f', mist.vec.mag(mist.vec.cp({x = xAcc, y = yAcc + 9.81, z = zAcc}, unitPos.x))/9.81) + end + + local E = 0.5*mist.vec.mag(unitVel)^2 + 9.81*unitPos.p.y + + local energy = string.format('%12.2e', E) + + local dEdt = 'NA' + if prevE and prevTime then + dEdt = string.format('%12.2e', (E - prevE)/(curTime - prevTime)) + end + + trigger.action.outText(string.format('%-25s', 'Heading: ') .. Heading .. ' degrees\n' .. string.format('%-25s', 'Roll: ') .. Roll .. ' degrees\n' .. string.format('%-25s', 'Pitch: ') .. Pitch + .. ' degrees\n' .. string.format('%-25s', 'Yaw: ') .. Yaw .. ' degrees\n' .. string.format('%-25s', 'AoA: ') .. AoA .. ' degrees\n' .. string.format('%-25s', 'AoA plus Yaw: ') .. AoAplusYaw .. ' degrees\n' .. string.format('%-25s', 'Climb Angle: ') .. + ClimbAngle .. ' degrees\n' .. string.format('%-25s', 'Absolute Velocity: ') .. absVel .. ' m/s\n' .. string.format('%-25s', 'Absolute Acceleration: ') .. unitAcc ..' m/s^2\n' + .. string.format('%-25s', 'Axial G loading: ') .. axialGs .. ' g\n' .. string.format('%-25s', 'Transverse G loading: ') .. transGs .. ' g\n' .. string.format('%-25s', 'Absolute G loading: ') .. Gs .. ' g\n' .. string.format('%-25s', 'Energy: ') .. energy .. ' J/kg\n' .. string.format('%-25s', 'dE/dt: ') .. dEdt .. + ' J/(kg*s)', 1) + + return unitVel, E, curTime + end + end + + local function frameFinder(unit, prevVel, prevE, prevTime) + if unit:isExist() then + local currVel = unit:getVelocity() + if prevVel and (prevVel.x ~= currVel.x or prevVel.y ~= currVel.y or prevVel.z ~= currVel.z) or (prevTime and (timer.getTime() - prevTime) > 0.25) then + prevVel, prevE, prevTime = printData(unit, prevVel, prevE, prevTime) + end + mist.scheduleFunction(frameFinder, {unit, prevVel, prevE, prevTime}, timer.getTime() + 0.005) -- it can't go this fast, limited to the 100 times a sec check right now. + end + end + + + local curVel = unit:getVelocity() + local curTime = timer.getTime() + local curE = 0.5*mist.vec.mag(curVel)^2 + 9.81*unit:getPosition().p.y + frameFinder(unit, curVel, curE, curTime) + + end + + end + +end + +do -- mist.DBs scope + --- mist.DBs: various tables acting as databases + -- @section mist.DBs + mist.DBs = {} + + --- Mission data + -- @table mist.DBs.missionData + -- @field startTime mission start time + -- @field theatre mission theatre/map e.g. Caucasus + -- @field version mission version + -- @field files mission resources + mist.DBs.missionData = {} + if env.mission then + + mist.DBs.missionData['startTime'] = env.mission.start_time + mist.DBs.missionData['theatre'] = env.mission.theatre + mist.DBs.missionData['version'] = env.mission.version + mist.DBs.missionData['files'] = {} + if type(env.mission.resourceCounter) == 'table' then + for fIndex, fData in pairs (env.mission.resourceCounter) do + mist.DBs.missionData.files[#mist.DBs.missionData.files + 1] = mist.utils.deepCopy(fIndex) + end + end + mist.DBs.missionData['bullseye'] = {['red'] = {}, ['blue'] = {}} -- if we add more coalition specific data then bullsye should be categorized by coaliton. For now its just the bullseye table + mist.DBs.missionData.bullseye.red['x'] = env.mission.coalition.red.bullseye.x --should it be point.x? + mist.DBs.missionData.bullseye.red['y'] = env.mission.coalition.red.bullseye.y + mist.DBs.missionData.bullseye.blue['x'] = env.mission.coalition.blue.bullseye.x + mist.DBs.missionData.bullseye.blue['y'] = env.mission.coalition.blue.bullseye.y + end + + mist.DBs.zonesByName = {} + mist.DBs.zonesByNum = {} + + + if env.mission.triggers and env.mission.triggers.zones then + for zone_ind, zone_data in pairs(env.mission.triggers.zones) do + if type(zone_data) == 'table' then + local zone = mist.utils.deepCopy(zone_data) + zone['point'] = {} -- point is used by SSE + zone['point']['x'] = zone_data.x + zone['point']['y'] = 0 + zone['point']['z'] = zone_data.y + + mist.DBs.zonesByName[zone_data.name] = zone + mist.DBs.zonesByNum[#mist.DBs.zonesByNum + 1] = mist.utils.deepCopy(zone) --[[deepcopy so that the zone in zones_by_name and the zone in + zones_by_num se are different objects.. don't want them linked.]] + end + end + end + + mist.DBs.navPoints = {} + mist.DBs.units = {} + --Build mist.db.units and mist.DBs.navPoints + 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 + mist.DBs.units[coa_name] = {} + + -- build nav points DB + mist.DBs.navPoints[coa_name] = {} + if coa_data.nav_points then --navpoints + --mist.debug.writeData (mist.utils.serialize,{'NavPoints',coa_data.nav_points}, 'NavPoints.txt') + for nav_ind, nav_data in pairs(coa_data.nav_points) do + + if type(nav_data) == 'table' then + mist.DBs.navPoints[coa_name][nav_ind] = mist.utils.deepCopy(nav_data) + + mist.DBs.navPoints[coa_name][nav_ind]['name'] = nav_data.callsignStr -- name is a little bit more self-explanatory. + mist.DBs.navPoints[coa_name][nav_ind]['point'] = {} -- point is used by SSE, support it. + mist.DBs.navPoints[coa_name][nav_ind]['point']['x'] = nav_data.x + mist.DBs.navPoints[coa_name][nav_ind]['point']['y'] = 0 + mist.DBs.navPoints[coa_name][nav_ind]['point']['z'] = nav_data.y + end + end + end + if coa_data.country then --there is a country table + for cntry_id, cntry_data in pairs(coa_data.country) do + + local countryName = string.lower(cntry_data.name) + mist.DBs.units[coa_name][countryName] = {} + mist.DBs.units[coa_name][countryName]["countryId"] = cntry_data.id + + if type(cntry_data) == 'table' then --just making sure + + 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" or obj_type_name == "static" then --should be an unncessary check + + local category = obj_type_name + + 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! + + mist.DBs.units[coa_name][countryName][category] = {} + + for group_num, group_data in pairs(obj_type_data.group) do + + if group_data and group_data.units and type(group_data.units) == 'table' then --making sure again- this is a valid group + + mist.DBs.units[coa_name][countryName][category][group_num] = {} + local groupName = group_data.name + if env.mission.version > 7 then + groupName = env.getValueDictByKey(groupName) + end + mist.DBs.units[coa_name][countryName][category][group_num]["groupName"] = groupName + mist.DBs.units[coa_name][countryName][category][group_num]["groupId"] = group_data.groupId + mist.DBs.units[coa_name][countryName][category][group_num]["category"] = category + mist.DBs.units[coa_name][countryName][category][group_num]["coalition"] = coa_name + mist.DBs.units[coa_name][countryName][category][group_num]["country"] = countryName + mist.DBs.units[coa_name][countryName][category][group_num]["countryId"] = cntry_data.id + mist.DBs.units[coa_name][countryName][category][group_num]["startTime"] = group_data.start_time + mist.DBs.units[coa_name][countryName][category][group_num]["task"] = group_data.task + mist.DBs.units[coa_name][countryName][category][group_num]["hidden"] = group_data.hidden + + mist.DBs.units[coa_name][countryName][category][group_num]["units"] = {} + + mist.DBs.units[coa_name][countryName][category][group_num]["radioSet"] = group_data.radioSet + mist.DBs.units[coa_name][countryName][category][group_num]["uncontrolled"] = group_data.uncontrolled + mist.DBs.units[coa_name][countryName][category][group_num]["frequency"] = group_data.frequency + mist.DBs.units[coa_name][countryName][category][group_num]["modulation"] = group_data.modulation + + for unit_num, unit_data in pairs(group_data.units) do + local units_tbl = mist.DBs.units[coa_name][countryName][category][group_num]["units"] --pointer to the units table for this group + + units_tbl[unit_num] = {} + if env.mission.version > 7 then + units_tbl[unit_num]["unitName"] = env.getValueDictByKey(unit_data.name) + else + units_tbl[unit_num]["unitName"] = unit_data.name + end + units_tbl[unit_num]["type"] = unit_data.type + units_tbl[unit_num]["skill"] = unit_data.skill --will be nil for statics + units_tbl[unit_num]["unitId"] = unit_data.unitId + units_tbl[unit_num]["category"] = category + units_tbl[unit_num]["coalition"] = coa_name + units_tbl[unit_num]["country"] = countryName + units_tbl[unit_num]["countryId"] = cntry_data.id + units_tbl[unit_num]["heading"] = unit_data.heading + units_tbl[unit_num]["playerCanDrive"] = unit_data.playerCanDrive + units_tbl[unit_num]["alt"] = unit_data.alt + units_tbl[unit_num]["alt_type"] = unit_data.alt_type + units_tbl[unit_num]["speed"] = unit_data.speed + units_tbl[unit_num]["livery_id"] = unit_data.livery_id + if unit_data.point then --ME currently does not work like this, but it might one day + units_tbl[unit_num]["point"] = unit_data.point + else + units_tbl[unit_num]["point"] = {} + units_tbl[unit_num]["point"]["x"] = unit_data.x + units_tbl[unit_num]["point"]["y"] = unit_data.y + end + units_tbl[unit_num]['x'] = unit_data.x + units_tbl[unit_num]['y'] = unit_data.y + + units_tbl[unit_num]["callsign"] = unit_data.callsign + units_tbl[unit_num]["onboard_num"] = unit_data.onboard_num + units_tbl[unit_num]["hardpoint_racks"] = unit_data.hardpoint_racks + units_tbl[unit_num]["psi"] = unit_data.psi + + + units_tbl[unit_num]["groupName"] = groupName + units_tbl[unit_num]["groupId"] = group_data.groupId + + if unit_data.AddPropAircraft then + units_tbl[unit_num]["AddPropAircraft"] = unit_data.AddPropAircraft + end + + if category == 'static' then + units_tbl[unit_num]["categoryStatic"] = unit_data.category + units_tbl[unit_num]["shape_name"] = unit_data.shape_name + if unit_data.mass then + units_tbl[unit_num]["mass"] = unit_data.mass + end + + if unit_data.canCargo then + units_tbl[unit_num]["canCargo"] = unit_data.canCargo + end + end + + end --for unit_num, unit_data in pairs(group_data.units) do + end --if group_data and group_data.units then + 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 --if type(cntry_data) == 'table' then + 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 + + mist.DBs.unitsByName = {} + mist.DBs.unitsById = {} + mist.DBs.unitsByCat = {} + + mist.DBs.unitsByCat['helicopter'] = {} -- adding default categories + mist.DBs.unitsByCat['plane'] = {} + mist.DBs.unitsByCat['ship'] = {} + mist.DBs.unitsByCat['static'] = {} + mist.DBs.unitsByCat['vehicle'] = {} + + mist.DBs.unitsByNum = {} + + mist.DBs.groupsByName = {} + mist.DBs.groupsById = {} + mist.DBs.humansByName = {} + mist.DBs.humansById = {} + + mist.DBs.dynGroupsAdded = {} -- will be filled by mist.dbUpdate from dynamically spawned groups + mist.DBs.activeHumans = {} + + mist.DBs.aliveUnits = {} -- will be filled in by the "updateAliveUnits" coroutine in mist.main. + + mist.DBs.removedAliveUnits = {} -- will be filled in by the "updateAliveUnits" coroutine in mist.main. + + -- create mist.DBs.oldAliveUnits + -- do + -- local intermediate_alive_units = {} -- between 0 and 0.5 secs old + -- local function make_old_alive_units() -- called every 0.5 secs, makes the old_alive_units DB which is just a copy of alive_units that is 0.5 to 1 sec old + -- if intermediate_alive_units then + -- mist.DBs.oldAliveUnits = mist.utils.deepCopy(intermediate_alive_units) + -- end + -- intermediate_alive_units = mist.utils.deepCopy(mist.DBs.aliveUnits) + -- timer.scheduleFunction(make_old_alive_units, nil, timer.getTime() + 0.5) + -- end + + -- make_old_alive_units() + -- end + + --Build DBs + for coa_name, coa_data in pairs(mist.DBs.units) do + for cntry_name, cntry_data in pairs(coa_data) do + for category_name, category_data in pairs(cntry_data) do + if type(category_data) == 'table' then + for group_ind, group_data in pairs(category_data) do + if type(group_data) == 'table' and group_data.units and type(group_data.units) == 'table' and #group_data.units > 0 then -- OCD paradigm programming + mist.DBs.groupsByName[group_data.groupName] = mist.utils.deepCopy(group_data) + mist.DBs.groupsById[group_data.groupId] = mist.utils.deepCopy(group_data) + for unit_ind, unit_data in pairs(group_data.units) do + mist.DBs.unitsByName[unit_data.unitName] = mist.utils.deepCopy(unit_data) + mist.DBs.unitsById[unit_data.unitId] = mist.utils.deepCopy(unit_data) + + mist.DBs.unitsByCat[unit_data.category] = mist.DBs.unitsByCat[unit_data.category] or {} -- future-proofing against new categories... + table.insert(mist.DBs.unitsByCat[unit_data.category], mist.utils.deepCopy(unit_data)) + --print('inserting ' .. unit_data.unitName) + table.insert(mist.DBs.unitsByNum, mist.utils.deepCopy(unit_data)) + + if unit_data.skill and (unit_data.skill == "Client" or unit_data.skill == "Player") then + mist.DBs.humansByName[unit_data.unitName] = mist.utils.deepCopy(unit_data) + mist.DBs.humansById[unit_data.unitId] = mist.utils.deepCopy(unit_data) + --if Unit.getByName(unit_data.unitName) then + -- mist.DBs.activeHumans[unit_data.unitName] = mist.utils.deepCopy(unit_data) + -- mist.DBs.activeHumans[unit_data.unitName].playerName = Unit.getByName(unit_data.unitName):getPlayerName() + --end + end + end + end + end + end + end + end + end + + + --DynDBs + mist.DBs.MEunits = mist.utils.deepCopy(mist.DBs.units) + mist.DBs.MEunitsByName = mist.utils.deepCopy(mist.DBs.unitsByName) + mist.DBs.MEunitsById = mist.utils.deepCopy(mist.DBs.unitsById) + mist.DBs.MEunitsByCat = mist.utils.deepCopy(mist.DBs.unitsByCat) + mist.DBs.MEunitsByNum = mist.utils.deepCopy(mist.DBs.unitsByNum) + mist.DBs.MEgroupsByName = mist.utils.deepCopy(mist.DBs.groupsByName) + mist.DBs.MEgroupsById = mist.utils.deepCopy(mist.DBs.groupsById) + ------------- + + mist.DBs.deadObjects = {} + + do + local mt = {} + + function mt.__newindex(t, key, val) + local original_key = key --only for duplicate runtime IDs. + local key_ind = 1 + while mist.DBs.deadObjects[key] do + --print('duplicate runtime id of previously dead object- key: ' .. tostring(key)) + key = tostring(original_key) .. ' #' .. tostring(key_ind) + key_ind = key_ind + 1 + end + + if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then + --print('object found in alive_units') + val['objectData'] = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) + local pos = Object.getPosition(val.object) + if pos then + val['objectPos'] = pos.p + end + val['objectType'] = mist.DBs.aliveUnits[val.object.id_].category + + elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units + --print('object found in old_alive_units') + val['objectData'] = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) + local pos = Object.getPosition(val.object) + if pos then + val['objectPos'] = pos.p + end + val['objectType'] = mist.DBs.removedAliveUnits[val.object.id_].category + + else --attempt to determine if static object... + --print('object not found in alive units or old alive units') + local pos = Object.getPosition(val.object) + if pos then + local static_found = false + for ind, static in pairs(mist.DBs.unitsByCat['static']) do + if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... + --print('correlated dead static object to position') + val['objectData'] = static + val['objectPos'] = pos.p + val['objectType'] = 'static' + static_found = true + break + end + end + if not static_found then + val['objectPos'] = pos.p + val['objectType'] = 'building' + end + else + val['objectType'] = 'unknown' + end + end + rawset(t, key, val) + end + + setmetatable(mist.DBs.deadObjects, mt) + end + + -- Event handler to start creating the dead_objects table + do + + local function addDeadObject(event) + if event.id == world.event.S_EVENT_DEAD or event.id == world.event.S_EVENT_CRASH then + if event.initiator and event.initiator.id_ and event.initiator.id_ > 0 then + + local id = event.initiator.id_ -- initial ID, could change if there is a duplicate id_ already dead. + local val = {object = event.initiator} -- the new entry in mist.DBs.deadObjects. + + local original_id = id --only for duplicate runtime IDs. + local id_ind = 1 + while mist.DBs.deadObjects[id] do + --print('duplicate runtime id of previously dead object- id: ' .. tostring(id)) + id = tostring(original_id) .. ' #' .. tostring(id_ind) + id_ind = id_ind + 1 + end + + if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then + --print('object found in alive_units') + val['objectData'] = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) + local pos = Object.getPosition(val.object) + if pos then + val['objectPos'] = pos.p + end + val['objectType'] = mist.DBs.aliveUnits[val.object.id_].category + --[[if mist.DBs.activeHumans[Unit.getName(val.object)] then + --trigger.action.outText('remove via death: ' .. Unit.getName(val.object),20) + mist.DBs.activeHumans[Unit.getName(val.object)] = nil + end]] + elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units + --print('object found in old_alive_units') + val['objectData'] = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) + local pos = Object.getPosition(val.object) + if pos then + val['objectPos'] = pos.p + end + val['objectType'] = mist.DBs.removedAliveUnits[val.object.id_].category + + else --attempt to determine if static object... + --print('object not found in alive units or old alive units') + local pos = Object.getPosition(val.object) + if pos then + local static_found = false + for ind, static in pairs(mist.DBs.unitsByCat['static']) do + if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... + --print('correlated dead static object to position') + val['objectData'] = static + val['objectPos'] = pos.p + val['objectType'] = 'static' + static_found = true + break + end + end + if not static_found then + val['objectPos'] = pos.p + val['objectType'] = 'building' + end + else + val['objectType'] = 'unknown' + end + end + mist.DBs.deadObjects[id] = val + + end + end + end + + + mist.addEventHandler(addDeadObject) + + --[[ + local function addClientsToActive(event) + if event.id == world.event.S_EVENT_PLAYER_ENTER_UNIT or event.id == world.event.S_EVENT_BIRTH then + env.info(mist.utils.tableShow(event)) + if Unit.getPlayerName(event.initiator) then + env.info(Unit.getPlayerName(event.initiator)) + local newU = mist.utils.deepCopy(mist.DBs.unitsByName[Unit.getName(event.initiator)]) + newU.playerName = Unit.getPlayerName(event.initiator) + mist.DBs.activeHumans[Unit.getName(event.initiator)] = newU + --trigger.action.outText('added: ' .. Unit.getName(event.initiator), 20) + end + elseif event.id == world.event.S_EVENT_PLAYER_LEAVE_UNIT and event.initiator then + if mist.DBs.activeHumans[Unit.getName(event.initiator)] then + mist.DBs.activeHumans[Unit.getName(event.initiator)] = nil + -- trigger.action.outText('removed via control: ' .. Unit.getName(event.initiator), 20) + end + end + end + + mist.addEventHandler(addClientsToActive)]] + end + +end + +do -- mist.time scope -- mist.time: time conversion functions mist.time = {} -- returns a string for specified military time @@ -6242,113 +5521,793 @@ do -- all function uses of group and unit Ids must be in this do statement return milTimeInSec end - mist.DBs.const = {} - --[[ - ['LAND'] = 1, - ['SHALLOW_WATER'] = 2, - ['WATER'] = 3, - ['ROAD'] = 4, - ['RUNWAY'] = 5 - ]] - --[[mist.DBs.const.ME_SSE_terms = { - ['ME'] = { - ['vehicle'] = {'GROUND', 'GROUND_UNIT'}, - ['plane'] = {'AIRPLANE'}, - }, - ['SSE'] = { - }, +end -}]] +do -- group tasks scope + --- Group task functions. + -- @section tasks + mist.ground = {} + mist.fixedWing = {} + mist.heli = {} + mist.air = {} + mist.air.fixedWing = {} + mist.air.heli = {} - - mist.DBs.const.callsigns = { -- not accessible by SSE, must use static list :-/ - ['NATO'] = { - ['rules'] = { - ['groupLimit'] = 9, - }, - ['AWACS'] = { - ['Overlord'] = 1, - ['Magic'] = 2, - ['Wizard'] = 3, - ['Focus'] = 4, - ['Darkstar'] = 5, - }, - ['TANKER'] = { - ['Texaco'] = 1, - ['Arco'] = 2, - ['Shell'] = 3, - }, - ['JTAC'] = { - ['Axeman'] = 1, - ['Darknight'] = 2, - ['Warrior'] = 3, - ['Pointer'] = 4, - ['Eyeball'] = 5, - ['Moonbeam'] = 6, - ['Whiplash'] = 7, - ['Finger'] = 8, - ['Pinpoint'] = 9, - ['Ferret'] = 10, - ['Shaba'] = 11, - ['Playboy'] = 12, - ['Hammer'] = 13, - ['Jaguar'] = 14, - ['Deathstar'] = 15, - ['Anvil'] = 16, - ['Firefly'] = 17, - ['Mantis'] = 18, - ['Badger'] = 19, - }, - ['aircraft'] = { - ['Enfield'] = 1, - ['Springfield'] = 2, - ['Uzi'] = 3, - ['Colt'] = 4, - ['Dodge'] = 5, - ['Ford'] = 6, - ['Chevy'] = 7, - ['Pontiac'] = 8, - }, - - ['unique'] = { - ['A10'] = { - ['Hawg'] = 9, - ['Boar'] = 10, - ['Pig'] = 11, - ['Tusk'] = 12, - ['rules'] = { - ['canUseAircraft'] = true, - ['appliesTo'] = { - 'A-10C', - 'A-10A', - }, + function mist.goRoute(group, path) + local misTask = { + id = 'Mission', + params = { + route = { + points = mist.utils.deepCopy(path), }, }, - }, - }, + } + if type(group) == 'string' then + group = Group.getByName(group) + end + local groupCon = nil + if group then + groupCon = group:getController() + if groupCon then + groupCon:setTask(misTask) + return true + end + end + --Controller.setTask(groupCon, misTask) + return false + end -} ---[[ scope: -{ - units = {...}, -- unit names. - coa = {...}, -- coa names - countries = {...}, -- country names - CA = {...}, -- looks just like coa. - unitTypes = { red = {}, blue = {}, all = {}, Russia = {},} -} + -- same as getGroupPoints but returns speed and formation type along with vec2 of point} + function mist.getGroupRoute(groupIdent, task) + -- 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 = mist.DBs.MEgroupsByName[groupIdent].groupId + end + + for coa_name, coa_data in pairs(env.mission.coalition) do + if (coa_name == 'red' or coa_name == 'blue') and type(coa_data) == 'table' then + if coa_data.country then --there is a country table + for cntry_id, cntry_data in pairs(coa_data.country) do + for obj_type_name, obj_type_data in pairs(cntry_data) do + if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" then -- only these types have points + if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's a group! + for group_num, group_data in pairs(obj_type_data.group) do + if group_data and group_data.groupId == gpId then -- this is the group we are looking for + if group_data.route and group_data.route.points and #group_data.route.points > 0 then + local points = {} + + for point_num, point in pairs(group_data.route.points) do + local routeData = {} + if not point.point then + routeData.x = point.x + routeData.y = point.y + else + routeData.point = point.point --it's possible that the ME could move to the point = Vec2 notation. + end + routeData.form = point.action + routeData.speed = point.speed + routeData.alt = point.alt + routeData.alt_type = point.alt_type + routeData.airdromeId = point.airdromeId + routeData.helipadId = point.helipadId + routeData.type = point.type + routeData.action = point.action + if task then + routeData.task = point.task + end + points[point_num] = routeData + end + + return points + end + return + end --if group_data and group_data.name and group_data.name == 'groupname' + end --for group_num, group_data in pairs(obj_type_data.group) do + end --if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then + end --if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then + end --for obj_type_name, obj_type_data in pairs(cntry_data) do + end --for cntry_id, cntry_data in pairs(coa_data.country) do + end --if coa_data.country then --there is a country table + end --if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then + end --for coa_name, coa_data in pairs(mission.coalition) do + end + + function mist.ground.buildPath() end -- ???? + + -- No longer accepts path + function mist.ground.buildWP(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 = mist.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 + + function mist.fixedWing.buildWP(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 altType == 'agl' then + wp.alt_type = 'RADIO' + elseif altType == 'baro' or altType == '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 = mist.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 + + function mist.heli.buildWP(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 altType == 'agl' then + wp.alt_type = 'RADIO' + elseif altType == 'baro' or altType == '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 = mist.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 + + -- need to return a Vec3 or Vec2? + function mist.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 + + function mist.getRandomPointInZone(zoneName, innerRadius) + if type(zoneName) == 'string' and type(trigger.misc.getZone(zoneName)) == 'table' then + return mist.getRandPointInCircle(trigger.misc.getZone(zoneName).point, trigger.misc.getZone(zoneName).radius, innerRadius) + end + return false + end + + function mist.groupToRandomPoint(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 mist.utils.kmphToMps(20) -scope examples: + local useRoads + if not vars.disableRoads then + useRoads = true + else + useRoads = false + end -{ units = { 'Hawg11', 'Hawg12' }, CA = {'blue'} } + local path = {} -{ countries = {'Georgia'}, unitTypes = {blue = {'A-10C', 'A-10A'}}} + if headingDegrees then + heading = headingDegrees*math.pi/180 + end -{ coa = {'all'}} + if heading >= 2*math.pi then + heading = heading - 2*math.pi + end -{unitTypes = { blue = {'A-10C'}}} + local rndCoord = mist.getRandPointInCircle(point, radius, innerRadius) + + local offset = {} + local posStart = mist.getLeadPos(group) + + offset.x = mist.utils.round(math.sin(heading - (math.pi/2)) * 50 + rndCoord.x, 3) + offset.z = mist.utils.round(math.cos(heading + (math.pi/2)) * 50 + rndCoord.y, 3) + path[#path + 1] = mist.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] = mist.ground.buildWP({['x'] = posStart.x + 11, ['z'] = posStart.z + 11}, 'off_road', speed) + path[#path + 1] = mist.ground.buildWP(posStart, 'on_road', speed) + path[#path + 1] = mist.ground.buildWP(offset, 'on_road', speed) + else + path[#path + 1] = mist.ground.buildWP({['x'] = posStart.x + 25, ['z'] = posStart.z + 25}, form, speed) + end + + path[#path + 1] = mist.ground.buildWP(offset, form, speed) + path[#path + 1] = mist.ground.buildWP(rndCoord, form, speed) + + mist.goRoute(group, path) + + return + end + + function mist.groupRandomDistSelf(gpData, dist, form, heading, speed) + local pos = mist.getLeadPos(gpData) + local fakeZone = {} + fakeZone.radius = dist or math.random(300, 1000) + fakeZone.point = {x = pos.x, y = pos.y, z = pos.z} + mist.groupToRandomZone(gpData, fakeZone, form, heading, speed) + + return + end + + function mist.groupToRandomZone(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 = mist.utils.kmphToMps(speed) + end + + local vars = {} + vars.group = gpData + vars.radius = zone.radius + vars.form = form + vars.headingDegrees = heading + vars.speed = speed + vars.point = mist.utils.zoneToVec3(zone) + + mist.groupToRandomPoint(vars) + + return + end + + function mist.isTerrainValid(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 + + function mist.terrainHeightDiff(coord, searchSize) + local samples = {} + local searchRadius = 5 + if searchSize then + searchRadius = searchSize + end + if type(coord) == 'string' then + coord = mist.utils.zoneToVec3(coord) + end + + coord = mist.utils.makeVec2(coord) + + samples[#samples + 1] = land.getHeight(coord) + for i = 0, 360, 30 do + samples[#samples + 1] = land.getHeight({x = (coord.x + (math.sin(math.rad(i))*searchRadius)), y = (coord.y + (math.cos(math.rad(i))*searchRadius))}) + if searchRadius >= 20 then -- if search radius is sorta large, take a sample halfway between center and outer edge + samples[#samples + 1] = land.getHeight({x = (coord.x + (math.sin(math.rad(i))*(searchRadius/2))), y = (coord.y + (math.cos(math.rad(i))*(searchRadius/2)))}) + end + end + local tMax, tMin = 0, 1000000 + for index, height in pairs(samples) do + if height > tMax then + tMax = height + end + if height < tMin then + tMin = height + end + end + return mist.utils.round(tMax - tMin, 2) + end + + function mist.groupToPoint(gpData, point, form, heading, speed, useRoads) + if type(point) == 'string' then + point = trigger.misc.getZone(point) + end + if speed then + speed = mist.utils.kmphToMps(speed) + end + + local vars = {} + vars.group = gpData + vars.form = form + vars.headingDegrees = heading + vars.speed = speed + vars.disableRoads = useRoads + vars.point = mist.utils.zoneToVec3(point) + mist.groupToRandomPoint(vars) + + return + end + + function mist.getLeadPos(group) + if type(group) == 'string' then -- group name + group = Group.getByName(group) + end + + local units = group:getUnits() + + local leader = units[1] + if not Unit.isExist(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 Unit.isExist(unit) and ind < lowestInd then + lowestInd = ind + return unit:getPosition().p + end + end + end + if leader and Unit.isExist(leader) then -- maybe a little too paranoid now... + return leader:getPosition().p + end + end + +end + +do -- general purpose messages + --[[ vars for mist.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 ]] + function mist.msgMGRS(vars) + local units = vars.units + local acc = vars.acc + local text = vars.text + local displayTime = vars.displayTime + local msgFor = vars.msgFor + + local s = mist.getMGRSString{units = units, acc = acc} + local newText + if text then + 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 + else + newText = s + end + mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor + } +end + +--[[ vars for mist.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 +]] +function mist.msgLL(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 = mist.getLLString{units = units, acc = acc, DMS = DMS} + local newText + if text then + 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 +else + newText = s +end + +mist.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 +]] +function mist.msgBR(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 mist.getBRString + local alt = vars.alt + local metric = vars.metric + local text = vars.text + local displayTime = vars.displayTime + local msgFor = vars.msgFor + + local s = mist.getBRString{units = units, ref = ref, alt = alt, metric = metric} + local newText + if text then + 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 +else + newText = s +end + +mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor +} + +end + +-- basically, just sub-types of mist.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 +]] +function mist.msgBullseye(vars) + if string.lower(vars.ref) == 'red' then + vars.ref = mist.DBs.missionData.bullseye.red + mist.msgBR(vars) + elseif string.lower(vars.ref) == 'blue' then + vars.ref = mist.DBs.missionData.bullseye.blue + mist.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 +]] +function mist.msgBRA(vars) + if Unit.getByName(vars.ref) and Unit.getByName(vars.ref):isExist() == true then + vars.ref = Unit.getByName(vars.ref):getPosition().p + if not vars.alt then + vars.alt = true + end + mist.msgBR(vars) + end +end + +--[[ vars for mist.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 +]] +function mist.msgLeadingMGRS(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 = mist.getLeadingMGRSString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc} + local newText + if text then + 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 +else + newText = s +end + +mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor +} + + +end + +--[[ vars for mist.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 +]] +function mist.msgLeadingLL(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 = mist.getLeadingLLString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc, DMS = DMS} + local newText + + if text then + 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 +else + newText = s +end + +mist.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 +]] +function mist.msgLeadingBR(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 mist.getBRString + local text = vars.text + local displayTime = vars.displayTime + local msgFor = vars.msgFor + + local s = mist.getLeadingBRString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, metric = metric, alt = alt, ref = ref} + local newText + + if text then + 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 +else + newText = s +end + +mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor +} +end +end + +do -- mist unitID funcs + + for id, idData in pairs(mist.DBs.unitsById) do + if idData.unitId > mist.nextUnitId then + mist.nextUnitId = mist.utils.deepCopy(idData.unitId) + end + if idData.groupId > mist.nextGroupId then + mist.nextGroupId = mist.utils.deepCopy(idData.groupId) + end + end end mist.main() From 33aa8ec3d5d4375857f62ae154425ec0facb2233 Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Thu, 7 Jan 2016 13:57:08 +0100 Subject: [PATCH 14/42] fixed indention --- mist.lua | 1697 +++++++++++++++++++++++++++--------------------------- 1 file changed, 842 insertions(+), 855 deletions(-) diff --git a/mist.lua b/mist.lua index 17d83d8..96cbaa5 100644 --- a/mist.lua +++ b/mist.lua @@ -45,7 +45,6 @@ do -- the main scope mist.nextGroupId = 1 mist.nextUnitId = 1 - mist.addEventHandler(groupSpawned) local function updateAliveUnits() -- coroutine function local lalive_units = mist.DBs.aliveUnits -- local references for faster execution @@ -843,7 +842,7 @@ do -- the main scope idNum = idNum + 1 handler.id = idNum handler.f = f - function handler.onEvent(self, event) + function handler:onEvent(event) self.f(event) end world.addEventHandler(handler) @@ -1048,24 +1047,23 @@ do -- the main scope end --[[ table attitude = getAttitude(string unitname) -- will work on any unit, even if not an aircraft. + attitude = { + Heading = number, -- in radians, range of 0 to 2*pi, relative to true north + Pitch = number, -- in radians, range of -pi/2 to pi/2 + Roll = number, -- in radians, range of 0 to 2*pi, right roll is positive direction -attitude = { - Heading = number, -- in radians, range of 0 to 2*pi, relative to true north - Pitch = number, -- in radians, range of -pi/2 to pi/2 - Roll = number, -- in radians, range of 0 to 2*pi, right roll is positive direction + --Yaw, AoA, ClimbAngle - relative to earth reference- DOES NOT TAKE INTO ACCOUNT WIND. + Yaw = number, -- in radians, range of -pi to pi, right yaw is positive direction + AoA = number, -- in radians, range of -pi to pi, rotation of aircraft to the right in comparison to flight direction being positive + ClimbAngle = number, -- in radians, range of -pi/2 to pi/2 - --Yaw, AoA, ClimbAngle - relative to earth reference- DOES NOT TAKE INTO ACCOUNT WIND. - Yaw = number, -- in radians, range of -pi to pi, right yaw is positive direction - AoA = number, -- in radians, range of -pi to pi, rotation of aircraft to the right in comparison to flight direction being positive - ClimbAngle = number, -- in radians, range of -pi/2 to pi/2 + --Maybe later? + AxialVel = table, velocity of the aircraft transformed into directions of aircraft axes + Speed = number -- absolute velocity in meters/sec - --Maybe later? - AxialVel = table, velocity of the aircraft transformed into directions of aircraft axes - Speed = number -- absolute velocity in meters/sec + } - } - -]] + ]] function mist.getAttitude(unit) local unitpos = unit:getPosition() if unitpos then @@ -1251,81 +1249,80 @@ attitude = { end function mist.makeUnitTable(tbl) - --[[ -Prefixes: -"[-u]" - subtract this unit if its in the table -"[g]" - add this group to the table -"[-g]" - subtract this group from the table -"[c]" - add this country's units -"[-c]" - subtract this country's units if any are in the table + Prefixes: + "[-u]" - subtract this unit if its in the table + "[g]" - add this group to the table + "[-g]" - subtract this group from the table + "[c]" - add this country's units + "[-c]" - subtract this country's units if any are in the table -Stand-alone identifiers -"[all]" - add all units -"[-all]" - subtract all units (not very useful by itself) -"[blue]" - add all blue units -"[-blue]" - subtract all blue units -"[red]" - add all red coalition units -"[-red]" - subtract all red units + Stand-alone identifiers + "[all]" - add all units + "[-all]" - subtract all units (not very useful by itself) + "[blue]" - add all blue units + "[-blue]" - subtract all blue units + "[red]" - add all red coalition units + "[-red]" - subtract all red units -Compound Identifiers: -"[c][helicopter]" - add all of this country's helicopters -"[-c][helicopter]" - subtract all of this country's helicopters -"[c][plane]" - add all of this country's planes -"[-c][plane]" - subtract all of this country's planes -"[c][ship]" - add all of this country's ships -"[-c][ship]" - subtract all of this country's ships -"[c][vehicle]" - add all of this country's vehicles -"[-c][vehicle]" - subtract all of this country's vehicles + Compound Identifiers: + "[c][helicopter]" - add all of this country's helicopters + "[-c][helicopter]" - subtract all of this country's helicopters + "[c][plane]" - add all of this country's planes + "[-c][plane]" - subtract all of this country's planes + "[c][ship]" - add all of this country's ships + "[-c][ship]" - subtract all of this country's ships + "[c][vehicle]" - add all of this country's vehicles + "[-c][vehicle]" - subtract all of this country's vehicles -"[all][helicopter]" - add all helicopters -"[-all][helicopter]" - subtract all helicopters -"[all][plane]" - add all planes -"[-all][plane]" - subtract all planes -"[all][ship]" - add all ships -"[-all][ship]" - subtract all ships -"[all][vehicle]" - add all vehicles -"[-all][vehicle]" - subtract all vehicles + "[all][helicopter]" - add all helicopters + "[-all][helicopter]" - subtract all helicopters + "[all][plane]" - add all planes + "[-all][plane]" - subtract all planes + "[all][ship]" - add all ships + "[-all][ship]" - subtract all ships + "[all][vehicle]" - add all vehicles + "[-all][vehicle]" - subtract all vehicles -"[blue][helicopter]" - add all blue coalition helicopters -"[-blue][helicopter]" - subtract all blue coalition helicopters -"[blue][plane]" - add all blue coalition planes -"[-blue][plane]" - subtract all blue coalition planes -"[blue][ship]" - add all blue coalition ships -"[-blue][ship]" - subtract all blue coalition ships -"[blue][vehicle]" - add all blue coalition vehicles -"[-blue][vehicle]" - subtract all blue coalition vehicles + "[blue][helicopter]" - add all blue coalition helicopters + "[-blue][helicopter]" - subtract all blue coalition helicopters + "[blue][plane]" - add all blue coalition planes + "[-blue][plane]" - subtract all blue coalition planes + "[blue][ship]" - add all blue coalition ships + "[-blue][ship]" - subtract all blue coalition ships + "[blue][vehicle]" - add all blue coalition vehicles + "[-blue][vehicle]" - subtract all blue coalition vehicles -"[red][helicopter]" - add all red coalition helicopters -"[-red][helicopter]" - subtract all red coalition helicopters -"[red][plane]" - add all red coalition planes -"[-red][plane]" - subtract all red coalition planes -"[red][ship]" - add all red coalition ships -"[-red][ship]" - subtract all red coalition ships -"[red][vehicle]" - add all red coalition vehicles -"[-red][vehicle]" - subtract all red coalition vehicles + "[red][helicopter]" - add all red coalition helicopters + "[-red][helicopter]" - subtract all red coalition helicopters + "[red][plane]" - add all red coalition planes + "[-red][plane]" - subtract all red coalition planes + "[red][ship]" - add all red coalition ships + "[-red][ship]" - subtract all red coalition ships + "[red][vehicle]" - add all red coalition vehicles + "[-red][vehicle]" - subtract all red coalition vehicles -Country names to be used in [c] and [-c] short-cuts: -"Turkey" -"Norway" -"The Netherlands" -"Spain" -"UK" -"Denmark" -"USA" -"Georgia" -"Germany" -"Belgium" -"Canada" -"France" -"Israel" -"Ukraine" -"Russia" -"South Osetia" -"Abkhazia" -"Italy" -]] + Country names to be used in [c] and [-c] short-cuts: + "Turkey" + "Norway" + "The Netherlands" + "Spain" + "UK" + "Denmark" + "USA" + "Georgia" + "Germany" + "Belgium" + "Canada" + "France" + "Israel" + "Ukraine" + "Russia" + "South Osetia" + "Abkhazia" + "Italy" + ]] --Assumption: will be passed a table of strings, sequential local units_by_name = {} @@ -1343,93 +1340,10 @@ Country names to be used in [c] and [-c] short-cuts: for unit_type, unit_type_tbl in pairs(country_table) do if type(unit_type_tbl) == 'table' then for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' and group_tbl.groupName == unit:sub(4) then -- index 4 to end - for unit_ind, unit in pairs(group_tbl.units) do - units_by_name[unit.unitName] = true --add - end - end - end - end - end - end - end - elseif unit:sub(1,4) == '[-g]' then -- subtract a group - for coa, coa_tbl in pairs(l_munits) do - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' and group_tbl.groupName == unit:sub(5) then -- index 5 to end - for unit_ind, unit in pairs(group_tbl.units) do - if units_by_name[unit.unitName] then - units_by_name[unit.unitName] = nil --remove - end - end - end - end - end - end - end - end -elseif unit:sub(1,3) == '[c]' then -- add a country - local category = '' - local country_start = 4 - if unit:sub(4,15) == '[helicopter]' then - category = 'helicopter' - country_start = 16 - elseif unit:sub(4,10) == '[plane]' then - category = 'plane' - country_start = 11 - elseif unit:sub(4,9) == '[ship]' then - category = 'ship' - country_start = 10 - elseif unit:sub(4,12) == '[vehicle]' then - category = 'vehicle' - country_start = 13 - end - for coa, coa_tbl in pairs(l_munits) do - for country, country_table in pairs(coa_tbl) do - if country == string.lower(unit:sub(country_start)) then -- match - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - units_by_name[unit.unitName] = true --add - end - end - end - end - end - end - end - end -elseif unit:sub(1,4) == '[-c]' then -- subtract a country - local category = '' - local country_start = 5 - if unit:sub(5,16) == '[helicopter]' then - category = 'helicopter' - country_start = 17 - elseif unit:sub(5,11) == '[plane]' then - category = 'plane' - country_start = 12 - elseif unit:sub(5,10) == '[ship]' then - category = 'ship' - country_start = 11 - elseif unit:sub(5,13) == '[vehicle]' then - category = 'vehicle' - country_start = 14 - end - for coa, coa_tbl in pairs(l_munits) do - for country, country_table in pairs(coa_tbl) do - if country == string.lower(unit:sub(country_start)) then -- match - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - if units_by_name[unit.unitName] then - units_by_name[unit.unitName] = nil --remove + if type(group_tbl) == 'table' and group_tbl.groupName == unit:sub(4) then + -- index 4 to end + for unit_ind, unit in pairs(group_tbl.units) do + units_by_name[unit.unitName] = true --add end end end @@ -1437,57 +1351,18 @@ elseif unit:sub(1,4) == '[-c]' then -- subtract a country end end end - end - end -elseif unit:sub(1,6) == '[blue]' then -- add blue coalition - local category = '' - if unit:sub(7) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(7) == '[plane]' then - category = 'plane' - elseif unit:sub(7) == '[ship]' then - category = 'ship' - elseif unit:sub(7) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - if coa == 'blue' then - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - units_by_name[unit.unitName] = true --add - end - end - end - end - end - end - end - end -elseif unit:sub(1,7) == '[-blue]' then -- subtract blue coalition - local category = '' - if unit:sub(8) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(8) == '[plane]' then - category = 'plane' - elseif unit:sub(8) == '[ship]' then - category = 'ship' - elseif unit:sub(8) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - if coa == 'blue' then - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - if units_by_name[unit.unitName] then - units_by_name[unit.unitName] = nil --remove + elseif unit:sub(1,4) == '[-g]' then -- subtract a group + for coa, coa_tbl in pairs(l_munits) do + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' and group_tbl.groupName == unit:sub(5) then + -- index 5 to end + for unit_ind, unit in pairs(group_tbl.units) do + if units_by_name[unit.unitName] then + units_by_name[unit.unitName] = nil --remove + end end end end @@ -1495,57 +1370,32 @@ elseif unit:sub(1,7) == '[-blue]' then -- subtract blue coalition end end end - end - end -elseif unit:sub(1,5) == '[red]' then -- add red coalition - local category = '' - if unit:sub(6) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(6) == '[plane]' then - category = 'plane' - elseif unit:sub(6) == '[ship]' then - category = 'ship' - elseif unit:sub(6) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - if coa == 'red' then - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - units_by_name[unit.unitName] = true --add - end - end - end - end - end + elseif unit:sub(1,3) == '[c]' then -- add a country + local category = '' + local country_start = 4 + if unit:sub(4,15) == '[helicopter]' then + category = 'helicopter' + country_start = 16 + elseif unit:sub(4,10) == '[plane]' then + category = 'plane' + country_start = 11 + elseif unit:sub(4,9) == '[ship]' then + category = 'ship' + country_start = 10 + elseif unit:sub(4,12) == '[vehicle]' then + category = 'vehicle' + country_start = 13 end - end - end -elseif unit:sub(1,6) == '[-red]' then -- subtract red coalition - local category = '' - if unit:sub(7) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(7) == '[plane]' then - category = 'plane' - elseif unit:sub(7) == '[ship]' then - category = 'ship' - elseif unit:sub(7) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - if coa == 'red' then - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - if units_by_name[unit.unitName] then - units_by_name[unit.unitName] = nil --remove + for coa, coa_tbl in pairs(l_munits) do + for country, country_table in pairs(coa_tbl) do + if country == string.lower(unit:sub(country_start)) then -- match + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + units_by_name[unit.unitName] = true --add + end end end end @@ -1553,66 +1403,215 @@ elseif unit:sub(1,6) == '[-red]' then -- subtract red coalition end end end - end - end -elseif unit:sub(1,5) == '[all]' then -- add all of a certain category (or all categories) - local category = '' - if unit:sub(6) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(6) == '[plane]' then - category = 'plane' - elseif unit:sub(6) == '[ship]' then - category = 'ship' - elseif unit:sub(6) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - units_by_name[unit.unitName] = true --add - end - end - end - end + elseif unit:sub(1,4) == '[-c]' then -- subtract a country + local category = '' + local country_start = 5 + if unit:sub(5,16) == '[helicopter]' then + category = 'helicopter' + country_start = 17 + elseif unit:sub(5,11) == '[plane]' then + category = 'plane' + country_start = 12 + elseif unit:sub(5,10) == '[ship]' then + category = 'ship' + country_start = 11 + elseif unit:sub(5,13) == '[vehicle]' then + category = 'vehicle' + country_start = 14 end - end - end -elseif unit:sub(1,6) == '[-all]' then -- subtract all of a certain category (or all categories) - local category = '' - if unit:sub(7) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(7) == '[plane]' then - category = 'plane' - elseif unit:sub(7) == '[ship]' then - category = 'ship' - elseif unit:sub(7) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - if units_by_name[unit.unitName] then - units_by_name[unit.unitName] = nil --remove + for coa, coa_tbl in pairs(l_munits) do + for country, country_table in pairs(coa_tbl) do + if country == string.lower(unit:sub(country_start)) then -- match + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + if units_by_name[unit.unitName] then + units_by_name[unit.unitName] = nil --remove + end + end + end end end end end end end + elseif unit:sub(1,6) == '[blue]' then -- add blue coalition + local category = '' + if unit:sub(7) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(7) == '[plane]' then + category = 'plane' + elseif unit:sub(7) == '[ship]' then + category = 'ship' + elseif unit:sub(7) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + if coa == 'blue' then + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + units_by_name[unit.unitName] = true --add + end + end + end + end + end + end + end + end + elseif unit:sub(1,7) == '[-blue]' then -- subtract blue coalition + local category = '' + if unit:sub(8) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(8) == '[plane]' then + category = 'plane' + elseif unit:sub(8) == '[ship]' then + category = 'ship' + elseif unit:sub(8) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + if coa == 'blue' then + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + if units_by_name[unit.unitName] then + units_by_name[unit.unitName] = nil --remove + end + end + end + end + end + end + end + end + end + elseif unit:sub(1,5) == '[red]' then -- add red coalition + local category = '' + if unit:sub(6) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(6) == '[plane]' then + category = 'plane' + elseif unit:sub(6) == '[ship]' then + category = 'ship' + elseif unit:sub(6) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + if coa == 'red' then + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + units_by_name[unit.unitName] = true --add + end + end + end + end + end + end + end + end + elseif unit:sub(1,6) == '[-red]' then -- subtract red coalition + local category = '' + if unit:sub(7) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(7) == '[plane]' then + category = 'plane' + elseif unit:sub(7) == '[ship]' then + category = 'ship' + elseif unit:sub(7) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + if coa == 'red' then + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + if units_by_name[unit.unitName] then + units_by_name[unit.unitName] = nil --remove + end + end + end + end + end + end + end + end + end + elseif unit:sub(1,5) == '[all]' then -- add all of a certain category (or all categories) + local category = '' + if unit:sub(6) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(6) == '[plane]' then + category = 'plane' + elseif unit:sub(6) == '[ship]' then + category = 'ship' + elseif unit:sub(6) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + units_by_name[unit.unitName] = true --add + end + end + end + end + end + end + end + elseif unit:sub(1,6) == '[-all]' then -- subtract all of a certain category (or all categories) + local category = '' + if unit:sub(7) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(7) == '[plane]' then + category = 'plane' + elseif unit:sub(7) == '[ship]' then + category = 'ship' + elseif unit:sub(7) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + if units_by_name[unit.unitName] then + units_by_name[unit.unitName] = nil --remove + end + end + end + end + end + end + end + end + else -- just a regular unit + units_by_name[unit] = true --add end end -else -- just a regular unit - units_by_name[unit] = true --add -end - end local units_tbl = {} -- indexed sequentially for unit_name, val in pairs(units_by_name) do @@ -2094,6 +2093,7 @@ function mist.getLeadingBRString(vars) end end + mist.addEventHandler(groupSpawned) end do -- group functions scope @@ -2720,124 +2720,6 @@ do -- group functions scope return newGroup end - function mist.ground.patrolRoute(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 = mist.getGroupRoute(useGroupRoute) - end - else - useRoute = vars.route - local posStart = mist.getLeadPos(gpData) - useRoute[1] = mist.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 = mist.getLeadPos(gpData) - - - useRoute[#useRoute + 1] = mist.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] = mist.ground.buildWP(tempRoute[i], tempForm, tempSpeed) - end - - if pType and string.lower(pType) == 'doubleback' then - local curRoute = mist.utils.deepCopy(useRoute) - for i = #curRoute, 2, -1 do - useRoute[#useRoute + 1] = mist.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] = 'mist.ground.patrolRoute(' - cTask3[#cTask3 + 1] = mist.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 - mist.goRoute(gpData, useRoute) - - return - end - - function mist.ground.patrol(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 - - mist.ground.patrolRoute(vars) - - return - end function mist.random(firstNum, secondNum) -- no support for decimals local lowNum, highNum @@ -2890,94 +2772,8 @@ do -- group functions scope mist.matchString = mist.stringMatch -- both commands work because order out type of I - mist.DBs.const = {} - --[[ - ['LAND'] = 1, - ['SHALLOW_WATER'] = 2, - ['WATER'] = 3, - ['ROAD'] = 4, - ['RUNWAY'] = 5 - ]] - --[[mist.DBs.const.ME_SSE_terms = { - ['ME'] = { - ['vehicle'] = {'GROUND', 'GROUND_UNIT'}, - ['plane'] = {'AIRPLANE'}, - }, - ['SSE'] = { - }, - -}]] - - - mist.DBs.const.callsigns = { -- not accessible by SSE, must use static list :-/ - ['NATO'] = { - ['rules'] = { - ['groupLimit'] = 9, - }, - ['AWACS'] = { - ['Overlord'] = 1, - ['Magic'] = 2, - ['Wizard'] = 3, - ['Focus'] = 4, - ['Darkstar'] = 5, - }, - ['TANKER'] = { - ['Texaco'] = 1, - ['Arco'] = 2, - ['Shell'] = 3, - }, - ['JTAC'] = { - ['Axeman'] = 1, - ['Darknight'] = 2, - ['Warrior'] = 3, - ['Pointer'] = 4, - ['Eyeball'] = 5, - ['Moonbeam'] = 6, - ['Whiplash'] = 7, - ['Finger'] = 8, - ['Pinpoint'] = 9, - ['Ferret'] = 10, - ['Shaba'] = 11, - ['Playboy'] = 12, - ['Hammer'] = 13, - ['Jaguar'] = 14, - ['Deathstar'] = 15, - ['Anvil'] = 16, - ['Firefly'] = 17, - ['Mantis'] = 18, - ['Badger'] = 19, - }, - ['aircraft'] = { - ['Enfield'] = 1, - ['Springfield'] = 2, - ['Uzi'] = 3, - ['Colt'] = 4, - ['Dodge'] = 5, - ['Ford'] = 6, - ['Chevy'] = 7, - ['Pontiac'] = 8, - }, - - ['unique'] = { - ['A10'] = { - ['Hawg'] = 9, - ['Boar'] = 10, - ['Pig'] = 11, - ['Tusk'] = 12, - ['rules'] = { - ['canUseAircraft'] = true, - ['appliesTo'] = { - 'A-10C', - 'A-10A', - }, - }, - }, - }, - }, - -} ---[[ scope: + --[[ scope: { units = {...}, -- unit names. coa = {...}, -- coa names @@ -5116,46 +4912,114 @@ do -- mist.DBs scope mist.DBs.removedAliveUnits = {} -- will be filled in by the "updateAliveUnits" coroutine in mist.main. - -- create mist.DBs.oldAliveUnits - -- do - -- local intermediate_alive_units = {} -- between 0 and 0.5 secs old - -- local function make_old_alive_units() -- called every 0.5 secs, makes the old_alive_units DB which is just a copy of alive_units that is 0.5 to 1 sec old - -- if intermediate_alive_units then - -- mist.DBs.oldAliveUnits = mist.utils.deepCopy(intermediate_alive_units) - -- end - -- intermediate_alive_units = mist.utils.deepCopy(mist.DBs.aliveUnits) - -- timer.scheduleFunction(make_old_alive_units, nil, timer.getTime() + 0.5) - -- end + mist.DBs.const = {} - -- make_old_alive_units() - -- end + mist.DBs.const.callsigns = { -- not accessible by SSE, must use static list :-/ + ['NATO'] = { + ['rules'] = { + ['groupLimit'] = 9, + }, + ['AWACS'] = { + ['Overlord'] = 1, + ['Magic'] = 2, + ['Wizard'] = 3, + ['Focus'] = 4, + ['Darkstar'] = 5, + }, + ['TANKER'] = { + ['Texaco'] = 1, + ['Arco'] = 2, + ['Shell'] = 3, + }, + ['JTAC'] = { + ['Axeman'] = 1, + ['Darknight'] = 2, + ['Warrior'] = 3, + ['Pointer'] = 4, + ['Eyeball'] = 5, + ['Moonbeam'] = 6, + ['Whiplash'] = 7, + ['Finger'] = 8, + ['Pinpoint'] = 9, + ['Ferret'] = 10, + ['Shaba'] = 11, + ['Playboy'] = 12, + ['Hammer'] = 13, + ['Jaguar'] = 14, + ['Deathstar'] = 15, + ['Anvil'] = 16, + ['Firefly'] = 17, + ['Mantis'] = 18, + ['Badger'] = 19, + }, + ['aircraft'] = { + ['Enfield'] = 1, + ['Springfield'] = 2, + ['Uzi'] = 3, + ['Colt'] = 4, + ['Dodge'] = 5, + ['Ford'] = 6, + ['Chevy'] = 7, + ['Pontiac'] = 8, + }, - --Build DBs - for coa_name, coa_data in pairs(mist.DBs.units) do - for cntry_name, cntry_data in pairs(coa_data) do - for category_name, category_data in pairs(cntry_data) do - if type(category_data) == 'table' then - for group_ind, group_data in pairs(category_data) do - if type(group_data) == 'table' and group_data.units and type(group_data.units) == 'table' and #group_data.units > 0 then -- OCD paradigm programming - mist.DBs.groupsByName[group_data.groupName] = mist.utils.deepCopy(group_data) - mist.DBs.groupsById[group_data.groupId] = mist.utils.deepCopy(group_data) - for unit_ind, unit_data in pairs(group_data.units) do - mist.DBs.unitsByName[unit_data.unitName] = mist.utils.deepCopy(unit_data) - mist.DBs.unitsById[unit_data.unitId] = mist.utils.deepCopy(unit_data) + ['unique'] = { + ['A10'] = { + ['Hawg'] = 9, + ['Boar'] = 10, + ['Pig'] = 11, + ['Tusk'] = 12, + ['rules'] = { + ['canUseAircraft'] = true, + ['appliesTo'] = { + 'A-10C', + 'A-10A', + }, + }, + }, + }, + }, - mist.DBs.unitsByCat[unit_data.category] = mist.DBs.unitsByCat[unit_data.category] or {} -- future-proofing against new categories... - table.insert(mist.DBs.unitsByCat[unit_data.category], mist.utils.deepCopy(unit_data)) - --print('inserting ' .. unit_data.unitName) - table.insert(mist.DBs.unitsByNum, mist.utils.deepCopy(unit_data)) +} +-- create mist.DBs.oldAliveUnits +-- do +-- local intermediate_alive_units = {} -- between 0 and 0.5 secs old +-- local function make_old_alive_units() -- called every 0.5 secs, makes the old_alive_units DB which is just a copy of alive_units that is 0.5 to 1 sec old +-- if intermediate_alive_units then +-- mist.DBs.oldAliveUnits = mist.utils.deepCopy(intermediate_alive_units) +-- end +-- intermediate_alive_units = mist.utils.deepCopy(mist.DBs.aliveUnits) +-- timer.scheduleFunction(make_old_alive_units, nil, timer.getTime() + 0.5) +-- end - if unit_data.skill and (unit_data.skill == "Client" or unit_data.skill == "Player") then - mist.DBs.humansByName[unit_data.unitName] = mist.utils.deepCopy(unit_data) - mist.DBs.humansById[unit_data.unitId] = mist.utils.deepCopy(unit_data) - --if Unit.getByName(unit_data.unitName) then - -- mist.DBs.activeHumans[unit_data.unitName] = mist.utils.deepCopy(unit_data) - -- mist.DBs.activeHumans[unit_data.unitName].playerName = Unit.getByName(unit_data.unitName):getPlayerName() - --end - end +-- make_old_alive_units() +-- end + +--Build DBs +for coa_name, coa_data in pairs(mist.DBs.units) do + for cntry_name, cntry_data in pairs(coa_data) do + for category_name, category_data in pairs(cntry_data) do + if type(category_data) == 'table' then + for group_ind, group_data in pairs(category_data) do + if type(group_data) == 'table' and group_data.units and type(group_data.units) == 'table' and #group_data.units > 0 then -- OCD paradigm programming + mist.DBs.groupsByName[group_data.groupName] = mist.utils.deepCopy(group_data) + mist.DBs.groupsById[group_data.groupId] = mist.utils.deepCopy(group_data) + for unit_ind, unit_data in pairs(group_data.units) do + mist.DBs.unitsByName[unit_data.unitName] = mist.utils.deepCopy(unit_data) + mist.DBs.unitsById[unit_data.unitId] = mist.utils.deepCopy(unit_data) + + mist.DBs.unitsByCat[unit_data.category] = mist.DBs.unitsByCat[unit_data.category] or {} -- future-proofing against new categories... + table.insert(mist.DBs.unitsByCat[unit_data.category], mist.utils.deepCopy(unit_data)) + --print('inserting ' .. unit_data.unitName) + table.insert(mist.DBs.unitsByNum, mist.utils.deepCopy(unit_data)) + + if unit_data.skill and (unit_data.skill == "Client" or unit_data.skill == "Player") then + mist.DBs.humansByName[unit_data.unitName] = mist.utils.deepCopy(unit_data) + mist.DBs.humansById[unit_data.unitId] = mist.utils.deepCopy(unit_data) + --if Unit.getByName(unit_data.unitName) then + -- mist.DBs.activeHumans[unit_data.unitName] = mist.utils.deepCopy(unit_data) + -- mist.DBs.activeHumans[unit_data.unitName].playerName = Unit.getByName(unit_data.unitName):getPlayerName() + --end end end end @@ -5163,151 +5027,152 @@ do -- mist.DBs scope end end end +end - --DynDBs - mist.DBs.MEunits = mist.utils.deepCopy(mist.DBs.units) - mist.DBs.MEunitsByName = mist.utils.deepCopy(mist.DBs.unitsByName) - mist.DBs.MEunitsById = mist.utils.deepCopy(mist.DBs.unitsById) - mist.DBs.MEunitsByCat = mist.utils.deepCopy(mist.DBs.unitsByCat) - mist.DBs.MEunitsByNum = mist.utils.deepCopy(mist.DBs.unitsByNum) - mist.DBs.MEgroupsByName = mist.utils.deepCopy(mist.DBs.groupsByName) - mist.DBs.MEgroupsById = mist.utils.deepCopy(mist.DBs.groupsById) - ------------- +--DynDBs +mist.DBs.MEunits = mist.utils.deepCopy(mist.DBs.units) +mist.DBs.MEunitsByName = mist.utils.deepCopy(mist.DBs.unitsByName) +mist.DBs.MEunitsById = mist.utils.deepCopy(mist.DBs.unitsById) +mist.DBs.MEunitsByCat = mist.utils.deepCopy(mist.DBs.unitsByCat) +mist.DBs.MEunitsByNum = mist.utils.deepCopy(mist.DBs.unitsByNum) +mist.DBs.MEgroupsByName = mist.utils.deepCopy(mist.DBs.groupsByName) +mist.DBs.MEgroupsById = mist.utils.deepCopy(mist.DBs.groupsById) +------------- - mist.DBs.deadObjects = {} +mist.DBs.deadObjects = {} - do - local mt = {} +do + local mt = {} - function mt.__newindex(t, key, val) - local original_key = key --only for duplicate runtime IDs. - local key_ind = 1 - while mist.DBs.deadObjects[key] do - --print('duplicate runtime id of previously dead object- key: ' .. tostring(key)) - key = tostring(original_key) .. ' #' .. tostring(key_ind) - key_ind = key_ind + 1 - end - - if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then - --print('object found in alive_units') - val['objectData'] = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) - local pos = Object.getPosition(val.object) - if pos then - val['objectPos'] = pos.p - end - val['objectType'] = mist.DBs.aliveUnits[val.object.id_].category - - elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units - --print('object found in old_alive_units') - val['objectData'] = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) - local pos = Object.getPosition(val.object) - if pos then - val['objectPos'] = pos.p - end - val['objectType'] = mist.DBs.removedAliveUnits[val.object.id_].category - - else --attempt to determine if static object... - --print('object not found in alive units or old alive units') - local pos = Object.getPosition(val.object) - if pos then - local static_found = false - for ind, static in pairs(mist.DBs.unitsByCat['static']) do - if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... - --print('correlated dead static object to position') - val['objectData'] = static - val['objectPos'] = pos.p - val['objectType'] = 'static' - static_found = true - break - end - end - if not static_found then - val['objectPos'] = pos.p - val['objectType'] = 'building' - end - else - val['objectType'] = 'unknown' - end - end - rawset(t, key, val) + function mt.__newindex(t, key, val) + local original_key = key --only for duplicate runtime IDs. + local key_ind = 1 + while mist.DBs.deadObjects[key] do + --print('duplicate runtime id of previously dead object- key: ' .. tostring(key)) + key = tostring(original_key) .. ' #' .. tostring(key_ind) + key_ind = key_ind + 1 end - setmetatable(mist.DBs.deadObjects, mt) + if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then + --print('object found in alive_units') + val['objectData'] = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) + local pos = Object.getPosition(val.object) + if pos then + val['objectPos'] = pos.p + end + val['objectType'] = mist.DBs.aliveUnits[val.object.id_].category + + elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units + --print('object found in old_alive_units') + val['objectData'] = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) + local pos = Object.getPosition(val.object) + if pos then + val['objectPos'] = pos.p + end + val['objectType'] = mist.DBs.removedAliveUnits[val.object.id_].category + + else --attempt to determine if static object... + --print('object not found in alive units or old alive units') + local pos = Object.getPosition(val.object) + if pos then + local static_found = false + for ind, static in pairs(mist.DBs.unitsByCat['static']) do + if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... + --print('correlated dead static object to position') + val['objectData'] = static + val['objectPos'] = pos.p + val['objectType'] = 'static' + static_found = true + break + end + end + if not static_found then + val['objectPos'] = pos.p + val['objectType'] = 'building' + end + else + val['objectType'] = 'unknown' + end + end + rawset(t, key, val) end - -- Event handler to start creating the dead_objects table - do + setmetatable(mist.DBs.deadObjects, mt) +end - local function addDeadObject(event) - if event.id == world.event.S_EVENT_DEAD or event.id == world.event.S_EVENT_CRASH then - if event.initiator and event.initiator.id_ and event.initiator.id_ > 0 then +-- Event handler to start creating the dead_objects table +do - local id = event.initiator.id_ -- initial ID, could change if there is a duplicate id_ already dead. - local val = {object = event.initiator} -- the new entry in mist.DBs.deadObjects. + local function addDeadObject(event) + if event.id == world.event.S_EVENT_DEAD or event.id == world.event.S_EVENT_CRASH then + if event.initiator and event.initiator.id_ and event.initiator.id_ > 0 then - local original_id = id --only for duplicate runtime IDs. - local id_ind = 1 - while mist.DBs.deadObjects[id] do - --print('duplicate runtime id of previously dead object- id: ' .. tostring(id)) - id = tostring(original_id) .. ' #' .. tostring(id_ind) - id_ind = id_ind + 1 + local id = event.initiator.id_ -- initial ID, could change if there is a duplicate id_ already dead. + local val = {object = event.initiator} -- the new entry in mist.DBs.deadObjects. + + local original_id = id --only for duplicate runtime IDs. + local id_ind = 1 + while mist.DBs.deadObjects[id] do + --print('duplicate runtime id of previously dead object- id: ' .. tostring(id)) + id = tostring(original_id) .. ' #' .. tostring(id_ind) + id_ind = id_ind + 1 + end + + if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then + --print('object found in alive_units') + val['objectData'] = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) + local pos = Object.getPosition(val.object) + if pos then + val['objectPos'] = pos.p end - - if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then - --print('object found in alive_units') - val['objectData'] = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) - local pos = Object.getPosition(val.object) - if pos then - val['objectPos'] = pos.p - end - val['objectType'] = mist.DBs.aliveUnits[val.object.id_].category - --[[if mist.DBs.activeHumans[Unit.getName(val.object)] then - --trigger.action.outText('remove via death: ' .. Unit.getName(val.object),20) + val['objectType'] = mist.DBs.aliveUnits[val.object.id_].category + --[[if mist.DBs.activeHumans[Unit.getName(val.object)] then + --trigger.action.outText('remove via death: ' .. Unit.getName(val.object),20) mist.DBs.activeHumans[Unit.getName(val.object)] = nil end]] - elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units - --print('object found in old_alive_units') - val['objectData'] = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) - local pos = Object.getPosition(val.object) - if pos then - val['objectPos'] = pos.p - end - val['objectType'] = mist.DBs.removedAliveUnits[val.object.id_].category - - else --attempt to determine if static object... - --print('object not found in alive units or old alive units') - local pos = Object.getPosition(val.object) - if pos then - local static_found = false - for ind, static in pairs(mist.DBs.unitsByCat['static']) do - if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... - --print('correlated dead static object to position') - val['objectData'] = static - val['objectPos'] = pos.p - val['objectType'] = 'static' - static_found = true - break - end - end - if not static_found then - val['objectPos'] = pos.p - val['objectType'] = 'building' - end - else - val['objectType'] = 'unknown' - end + elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units + --print('object found in old_alive_units') + val['objectData'] = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) + local pos = Object.getPosition(val.object) + if pos then + val['objectPos'] = pos.p end - mist.DBs.deadObjects[id] = val + val['objectType'] = mist.DBs.removedAliveUnits[val.object.id_].category + else --attempt to determine if static object... + --print('object not found in alive units or old alive units') + local pos = Object.getPosition(val.object) + if pos then + local static_found = false + for ind, static in pairs(mist.DBs.unitsByCat['static']) do + if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... + --print('correlated dead static object to position') + val['objectData'] = static + val['objectPos'] = pos.p + val['objectType'] = 'static' + static_found = true + break + end + end + if not static_found then + val['objectPos'] = pos.p + val['objectType'] = 'building' + end + else + val['objectType'] = 'unknown' + end end + mist.DBs.deadObjects[id] = val + end end + end - mist.addEventHandler(addDeadObject) + mist.addEventHandler(addDeadObject) - --[[ + --[[ local function addClientsToActive(event) if event.id == world.event.S_EVENT_PLAYER_ENTER_UNIT or event.id == world.event.S_EVENT_BIRTH then env.info(mist.utils.tableShow(event)) @@ -5614,7 +5479,124 @@ do -- group tasks scope end --for coa_name, coa_data in pairs(mission.coalition) do end - function mist.ground.buildPath() end -- ???? + -- function mist.ground.buildPath() end -- ???? + + function mist.ground.patrolRoute(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 = mist.getGroupRoute(useGroupRoute) + end + else + useRoute = vars.route + local posStart = mist.getLeadPos(gpData) + useRoute[1] = mist.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 = mist.getLeadPos(gpData) + + + useRoute[#useRoute + 1] = mist.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] = mist.ground.buildWP(tempRoute[i], tempForm, tempSpeed) + end + + if pType and string.lower(pType) == 'doubleback' then + local curRoute = mist.utils.deepCopy(useRoute) + for i = #curRoute, 2, -1 do + useRoute[#useRoute + 1] = mist.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] = 'mist.ground.patrolRoute(' + cTask3[#cTask3 + 1] = mist.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 + mist.goRoute(gpData, useRoute) + + return + end + + function mist.ground.patrol(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 + + mist.ground.patrolRoute(vars) + + return + end -- No longer accepts path function mist.ground.buildWP(point, overRideForm, overRideSpeed) @@ -6043,20 +6025,21 @@ vars.msgFor - scope if text then 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 + else + -- just append to the end. + newText = text .. s + end + else + newText = s end - else - newText = s + mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor + } end - mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } -end ---[[ vars for mist.msgLL + --[[ vars for mist.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. @@ -6064,35 +6047,36 @@ vars.text - text in the message vars.displayTime - self explanatory vars.msgFor - scope ]] -function mist.msgLL(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 + function mist.msgLL(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 = mist.getLLString{units = units, acc = acc, DMS = DMS} + local newText + if text then + if string.find(text, '%%s') then -- look for %s + newText = string.format(text, s) -- insert the coordinates into the message + else + -- just append to the end. + newText = text .. s + end + else + newText = s + end + + mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor + } - local s = mist.getLLString{units = units, acc = acc, DMS = DMS} - local newText - if text then - 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 -else - newText = s -end -mist.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 @@ -6101,37 +6085,38 @@ vars.text - text of the message vars.displayTime vars.msgFor - scope ]] -function mist.msgBR(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 mist.getBRString - local alt = vars.alt - local metric = vars.metric - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor + function mist.msgBR(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 mist.getBRString + local alt = vars.alt + local metric = vars.metric + local text = vars.text + local displayTime = vars.displayTime + local msgFor = vars.msgFor + + local s = mist.getBRString{units = units, ref = ref, alt = alt, metric = metric} + local newText + if text then + if string.find(text, '%%s') then -- look for %s + newText = string.format(text, s) -- insert the coordinates into the message + else + -- just append to the end. + newText = text .. s + end + else + newText = s + end + + mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor + } - local s = mist.getBRString{units = units, ref = ref, alt = alt, metric = metric} - local newText - if text then - 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 -else - newText = s -end -mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor -} - -end - --- basically, just sub-types of mist.msgBR... saves folks the work of getting the ref point. ---[[ + -- basically, just sub-types of mist.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 @@ -6140,17 +6125,17 @@ vars.text - text of the message vars.displayTime vars.msgFor - scope ]] -function mist.msgBullseye(vars) - if string.lower(vars.ref) == 'red' then - vars.ref = mist.DBs.missionData.bullseye.red - mist.msgBR(vars) - elseif string.lower(vars.ref) == 'blue' then - vars.ref = mist.DBs.missionData.bullseye.blue - mist.msgBR(vars) + function mist.msgBullseye(vars) + if string.lower(vars.ref) == 'red' then + vars.ref = mist.DBs.missionData.bullseye.red + mist.msgBR(vars) + elseif string.lower(vars.ref) == 'blue' then + vars.ref = mist.DBs.missionData.bullseye.blue + mist.msgBR(vars) + end 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 @@ -6159,17 +6144,17 @@ vars.text - text of the message vars.displayTime vars.msgFor - scope ]] -function mist.msgBRA(vars) - if Unit.getByName(vars.ref) and Unit.getByName(vars.ref):isExist() == true then - vars.ref = Unit.getByName(vars.ref):getPosition().p - if not vars.alt then - vars.alt = true + function mist.msgBRA(vars) + if Unit.getByName(vars.ref) and Unit.getByName(vars.ref):isExist() == true then + vars.ref = Unit.getByName(vars.ref):getPosition().p + if not vars.alt then + vars.alt = true + end + mist.msgBR(vars) end - mist.msgBR(vars) end -end ---[[ vars for mist.msgLeadingMGRS: + --[[ vars for mist.msgLeadingMGRS: vars.units - table of unit names vars.heading - direction vars.radius - number @@ -6179,38 +6164,39 @@ vars.text - text of the message vars.displayTime vars.msgFor - scope ]] -function mist.msgLeadingMGRS(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 + function mist.msgLeadingMGRS(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 = mist.getLeadingMGRSString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc} + local newText + if text then + if string.find(text, '%%s') then -- look for %s + newText = string.format(text, s) -- insert the coordinates into the message + else + -- just append to the end. + newText = text .. s + end + else + newText = s + end + + mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor + } + - local s = mist.getLeadingMGRSString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc} - local newText - if text then - 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 -else - newText = s -end -mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor -} - - -end - ---[[ vars for mist.msgLeadingLL: + --[[ vars for mist.msgLeadingLL: vars.units - table of unit names vars.heading - direction, number vars.radius - number @@ -6221,39 +6207,40 @@ vars.text - text of the message vars.displayTime vars.msgFor - scope ]] -function mist.msgLeadingLL(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 + function mist.msgLeadingLL(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 = mist.getLeadingLLString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc, DMS = DMS} - local newText + local s = mist.getLeadingLLString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc, DMS = DMS} + local newText + + if text then + if string.find(text, '%%s') then -- look for %s + newText = string.format(text, s) -- insert the coordinates into the message + else + -- just append to the end. + newText = text .. s + end + else + newText = s + end + + mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor + } - if text then - 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 -else - newText = s -end -mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor -} - -end - ---[[ + --[[ vars.units - table of unit names vars.heading - direction, number vars.radius - number @@ -6265,41 +6252,41 @@ vars.text - text of the message vars.displayTime vars.msgFor - scope ]] -function mist.msgLeadingBR(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 mist.getBRString - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor + function mist.msgLeadingBR(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 mist.getBRString + local text = vars.text + local displayTime = vars.displayTime + local msgFor = vars.msgFor - local s = mist.getLeadingBRString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, metric = metric, alt = alt, ref = ref} - local newText + local s = mist.getLeadingBRString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, metric = metric, alt = alt, ref = ref} + local newText - if text then - 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 + if text then + if string.find(text, '%%s') then -- look for %s + newText = string.format(text, s) -- insert the coordinates into the message + else + -- just append to the end. + newText = text .. s + end + else + newText = s + end + + mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor + } end -else - newText = s -end - -mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor -} -end end do -- mist unitID funcs - for id, idData in pairs(mist.DBs.unitsById) do if idData.unitId > mist.nextUnitId then mist.nextUnitId = mist.utils.deepCopy(idData.unitId) From 5803be3904493898b09f4f0af5a7afff99cfabf7 Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Thu, 7 Jan 2016 21:07:57 +0100 Subject: [PATCH 15/42] switched to new logger created a init function, creating a logger and calling main --- mist.lua | 128 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 78 insertions(+), 50 deletions(-) diff --git a/mist.lua b/mist.lua index 96cbaa5..a1a7d71 100644 --- a/mist.lua +++ b/mist.lua @@ -21,6 +21,9 @@ mist.majorVersion = 4 mist.minorVersion = 0 mist.build = 60 +-- forward declaration of log shorthand +local log + do -- the main scope local coroutines = {} @@ -45,7 +48,6 @@ do -- the main scope mist.nextGroupId = 1 mist.nextUnitId = 1 - local function updateAliveUnits() -- coroutine function local lalive_units = mist.DBs.aliveUnits -- local references for faster execution local lunits = mist.DBs.unitsByNum @@ -105,9 +107,9 @@ do -- the main scope elseif StaticObject.getByName(event) then newObject = StaticObject.getByName(event) newType = 'static' - -- env.info('its static') + -- log:info('its static') else - env.info('WTF') + log:info('WTF') return false end @@ -413,8 +415,28 @@ do -- the main scope end end + --- init function. + -- creates logger, adds default event handler + -- and calls main the first time. + function mist.init() + -- create logger + mist.log = mist.Logger:new() + log = mist.log + -- set info log level + log:setLevel("info") + + -- add event handler for group spawns + mist.addEventHandler(groupSpawned) + + -- call main the first time therafter it reschedules itself. + mist.main() + + log:info('MIST version $1.$2.$3 loaded', mist.majorVersion, mist.minorVersion, mist.build) + end + --- The main function. -- Run 100 times per second. + -- You shouldn't call this function. function mist.main() timer.scheduleFunction(mist.main, {}, timer.getTime() + 0.01) --reschedule first in case of Lua error @@ -708,7 +730,7 @@ do -- the main scope newGroup.units[unitIndex].alt_type = 'RADIO' newGroup.units[unitIndex].speed = 60 else - --[[env.info('check height') + --[[log:info('check height') newGroup.units[unitIndex].alt = land.getHeight({x = newGroup.units[unitIndex].x, y = newGroup.units[unitIndex].y}) newGroup.units[unitIndex].alt_type = 'BARO']] end @@ -811,7 +833,7 @@ do -- the main scope table.remove(Tasks, i) local err, errmsg = pcall(Task.f, unpack(Task.vars, 1, table.maxn(Task.vars))) if not err then - env.info('mist.scheduleFunction, error in scheduled function: ' .. errmsg) + log:error('mist.scheduleFunction, error in scheduled function: $1', errmsg) end --Task.f(unpack(Task.vars, 1, table.maxn(Task.vars))) -- do the task, do not increment i else @@ -825,7 +847,7 @@ do -- the main scope Task.t = timer.getTime() + Task.rep --schedule next run local err, errmsg = pcall(Task.f, unpack(Task.vars, 1, table.maxn(Task.vars))) if not err then - env.info('mist.scheduleFunction, error in scheduled function: ' .. errmsg) + log:error('mist.scheduleFunction, error in scheduled function: $1' .. errmsg) end --Tasks[i].f(unpack(Tasks[i].vars, 1, table.maxn(Tasks[i].vars))) -- do the task i = i + 1 @@ -1137,7 +1159,7 @@ do -- the main scope end return { Heading = Heading, Pitch = Pitch, Roll = Roll, Yaw = Yaw, AoA = AoA, ClimbAngle = ClimbAngle} else - env.info('unit:getPosition() is nil!') + log:error("Couldn't get unit's position") end end @@ -2093,7 +2115,6 @@ function mist.getLeadingBRString(vars) end end - mist.addEventHandler(groupSpawned) end do -- group functions scope @@ -2232,7 +2253,7 @@ do -- group functions scope return newData else - env.info(gpName .. ' not found in mist.getGroupData') + log:error('$1 not found in MIST database', gpName) return end end @@ -2270,10 +2291,10 @@ do -- group functions scope end end else - env.info('mist.getPayload got ' .. type(unitIdent)) + log:error('Need string or number. Got: $1', type(unitIdent)) return false end - env.info('mist.getPayload, payload not found') + log:warn("Couldn't find payload for unit: $1", unitIdent) return end @@ -2308,10 +2329,10 @@ do -- group functions scope end end else - env.info('mist.getGroupPayload got ' .. type(groupName)) + log:error('Need string or number. Got: $1', type(groupIdent)) return false end - env.info('mist.getGroupPayload, payload not found') + log:warn("Couldn't find payload for group: $1", groupIdent) return end @@ -2325,7 +2346,7 @@ do -- group functions scope elseif vars.groupName then gpName = vars.groupName else - env.info('teleportToPoint missing either vars.groupName or vars.gpName') + log:error('Missing field groupName or gpName in variable table') end local action = vars.action @@ -2387,7 +2408,7 @@ do -- group functions scope end end if valid == false then - env.info('mist.teleportToPoint; vars.point not a valid coordinate') + log:error('point supplied in variable table is not a valid coordinate.') return false end end @@ -2647,7 +2668,7 @@ do -- group functions scope end --[[ for i = 1, #newTable do - env.info(newTable[i]) + log:info(newTable[i]) end ]] return newTable @@ -2765,7 +2786,7 @@ do -- group functions scope return false end else - env.info('mist.stringMatch; Either the first or second variable were not a string') + log:error('Either the first or second variable were not a string') return false end end @@ -2798,10 +2819,7 @@ end do -- mist.Logger scope --- Logger class. -- @type mist.Logger - mist.Logger = { - tag = "MIST", - level = 1, - } + mist.Logger = {} --- Creates a new logger. -- Each logger has it's own tag and log level. @@ -2810,7 +2828,12 @@ do -- mist.Logger scope -- @usage myLogger = mist.Logger:new{tag = "MyScript", level = 2} -- @treturn mist.Logger function mist.Logger:new(l) - l = l or {} + l = l or { + tag = "MIST", + level = 1, + } + -- in case the user only supplied + -- either tag or level if not l.tag then l.tag = "MIST" end @@ -2861,7 +2884,16 @@ do -- mist.Logger scope end text = text:gsub('$' .. index, value) end - return text + local dInfo = debug.getinfo(3) + local fName = dInfo.name + -- local fsrc = dinfo.short_src + --local fLine = dInfo.linedefined + local cLine = dInfo.currentline + if fName then + return fName .. '|' .. cLine .. ': ' .. text + else + return cLine .. ': ' .. text + end end --- Logs error and shows alert window. @@ -2873,7 +2905,7 @@ do -- mist.Logger scope -- @usage myLogger:alert("Shit just hit the fan! WEEEE!!!11") function mist.Logger:alert(text, ...) text = formatText(text, unpack(arg)) - env.error(self.tag .. ' | ' .. text, true) + env.error(self.tag .. '|' .. text, true) end --- Logs an error. @@ -2886,7 +2918,7 @@ do -- mist.Logger scope function mist.Logger:error(text, ...) if self.level >= 1 then text = formatText(text, unpack(arg)) - env.error(self.tag .. ' | ' .. text) + env.error(self.tag .. '|' .. text) end end @@ -2899,7 +2931,7 @@ do -- mist.Logger scope function mist.Logger:warn(text, ...) if self.level >= 2 then text = formatText(text, unpack(arg)) - env.warning(self.tag .. ' | ' .. text) + env.warning(self.tag .. '|' .. text) end end @@ -2912,7 +2944,7 @@ do -- mist.Logger scope function mist.Logger:info(text, ...) if self.level >= 3 then text = formatText(text, unpack(arg)) - env.info(self.tag .. ' | ' .. text) + env.info(self.tag .. '|' .. text) end end end @@ -3207,7 +3239,7 @@ do -- mist.util scope -- mist.utils.typeCheck(type_tbl, my_tb) -- @return true if table passes the check, false otherwise. function mist.utils.typeCheck(fname, type_tbl, var_tbl) - --env.info('type check') + --log:info('type check') for type_key, type_val in pairs(type_tbl) do --print('type_key:') --print(type_key) @@ -3340,7 +3372,7 @@ function mist.utils.serialize(name, value, level) end else - env.info("Cannot serialize a "..type(value)) + log:error('Cannot serialize a $1', type(value)) end return var_str_tbl end @@ -3427,7 +3459,7 @@ function mist.utils.oneLineSerialize(tbl) tbl_str[#tbl_str + 1] = mist.utils.oneLineSerialize(val) tbl_str[#tbl_str + 1] = ', ' --I think this is right, I just added it else - env.info('unable to serialize value type ' .. mist.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind)) + log:war('Unable to serialize value type $1 at index $2', mist.utils.basicSerialize(type(val)), tostring(ind)) end end @@ -3531,13 +3563,11 @@ do -- mist.debug scope local f = io.open(fdir, 'w') f:write(mist.utils.tableShow(_G)) f:close() - local errmsg = 'mist.debug.dump_G wrote data to ' .. fdir - env.info(errmsg) - trigger.action.outText(errmsg, 10) + log:info('Wrote debug data to $1', fdir) + --trigger.action.outText(errmsg, 10) else - local errmsg = 'Error: insufficient libraries to run mist.debug.dump_G, you must disable the sanitization of the io and lfs libraries in ./Scripts/MissionScripting.lua' - env.info(errmsg) - trigger.action.outText(errmsg, 10) + log:alert('insufficient libraries to run mist.debug.dump_G, you must disable the sanitization of the io and lfs libraries in ./Scripts/MissionScripting.lua') + --trigger.action.outText(errmsg, 10) end end @@ -3554,13 +3584,11 @@ do -- mist.debug scope local f = io.open(fdir, 'w') f:write(fcn(unpack(fcnVars, 1, table.maxn(fcnVars)))) f:close() - local errmsg = 'mist.debug.writeData wrote data to ' .. fdir - env.info(errmsg) - trigger.action.outText(errmsg, 10) + log:info('Wrote debug data to $1', fdir) + --trigger.action.outText(errmsg, 10) else - local errmsg = 'Error: insufficient libraries to run mist.debug.writeData, you must disable the sanitization of the io and lfs libraries in ./Scripts/MissionScripting.lua' - env.info(errmsg) - trigger.action.outText(errmsg, 10) + log:alert('insufficient libraries to run mist.debug.dump_G, you must disable the sanitization of the io and lfs libraries in ./Scripts/MissionScripting.lua') + --trigger.action.outText(errmsg, 10) end end @@ -4412,11 +4440,11 @@ end]] local function msgSpamFilter(recList, spamBlockOn) for id, name in pairs(recList) do if name == spamBlockOn then - -- env.info('already on recList') + -- log:info('already on recList') return recList end end - --env.info('add to recList') + --log:info('add to recList') table.insert(recList, spamBlockOn) return recList end @@ -4523,7 +4551,7 @@ end]] for i = 1, #messageList do if messageList[i].name then if messageList[i].name == vars.name then - --env.info('updateMessage') + --log:info('updateMessage') messageList[i].displayedFor = 0 messageList[i].addedAt = timer.getTime() messageList[i].sound = new.sound @@ -5175,9 +5203,9 @@ do --[[ local function addClientsToActive(event) if event.id == world.event.S_EVENT_PLAYER_ENTER_UNIT or event.id == world.event.S_EVENT_BIRTH then - env.info(mist.utils.tableShow(event)) + log:info(mist.utils.tableShow(event)) if Unit.getPlayerName(event.initiator) then - env.info(Unit.getPlayerName(event.initiator)) + log:info(Unit.getPlayerName(event.initiator)) local newU = mist.utils.deepCopy(mist.DBs.unitsByName[Unit.getName(event.initiator)]) newU.playerName = Unit.getPlayerName(event.initiator) mist.DBs.activeHumans[Unit.getName(event.initiator)] = newU @@ -5250,7 +5278,7 @@ do -- mist.time scope tbl.s = timeInSec return tbl else - env.info('mist.time.getDHMS didnt recieve number') + log:error("Didn't recieve number") return end end @@ -6297,8 +6325,8 @@ do -- mist unitID funcs end end -mist.main() -env.info(('Mist version ' .. mist.majorVersion .. '.' .. mist.minorVersion .. '.' .. mist.build .. ' loaded.')) +-- initialize mist +mist.init() -- vim: sw=2:ts=2 From e5a89d2d38e73dd5dfb0786843328a73a83e9684 Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Thu, 7 Jan 2016 22:06:31 +0100 Subject: [PATCH 16/42] added description --- mist.lua | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/mist.lua b/mist.lua index a1a7d71..029e125 100644 --- a/mist.lua +++ b/mist.lua @@ -1,15 +1,25 @@ ---[[ -Links: - -ED Forum Thread: http://forums.eagle.ru/showthread.php?t=98616 - -Github -Development: https://github.com/mrSkortch/MissionScriptingTools/tree/development -Official Release: https://github.com/mrSkortch/MissionScriptingTools/tree/master - -]] - ---- MiST Mission Scripting Tools. +--- MIST Mission Scripting Tools. +-- MIssion Scripting Tools (MIST) is a collection of Lua functions +-- and databases that is intended to be a supplement to the standard +-- Lua functions included in the simulator scripting engine. +-- +-- MIST functions and databases provide ready-made solutions to many common +-- scripting tasks and challenges, enabling easier scripting and saving +-- mission scripters time. The table mist.flagFuncs contains a set of +-- Lua functions (that are similar to Slmod functions) that do not +-- require detailed Lua knowledge to use. +-- +-- However, the majority of MIST does require knowledge of the Lua language, +-- and, if you are going to utilize these components of MIST, it is necessary +-- that you read the Simulator Scripting Engine guide on the official ED wiki. +-- +-- Links: +-- ED Forum Thread: http://forums.eagle.ru/showthread.php?t=98616 +-- +-- Github +-- Development: https://github.com/mrSkortch/MissionScriptingTools/tree/development +-- Official Release: https://github.com/mrSkortch/MissionScriptingTools/tree/master +-- -- @module mist -- @author Speed -- @author Grimes From b0a34ffcc1f5a0796bc84353cf6439d59356e2ba Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Thu, 7 Jan 2016 23:32:54 +0100 Subject: [PATCH 17/42] more ldoc, made doScheduledFunctions local --- mist.lua | 1748 +++++++++++++++++++++++++++--------------------------- 1 file changed, 883 insertions(+), 865 deletions(-) diff --git a/mist.lua b/mist.lua index 029e125..2f2f54a 100644 --- a/mist.lua +++ b/mist.lua @@ -1,29 +1,35 @@ ---- MIST Mission Scripting Tools. --- MIssion Scripting Tools (MIST) is a collection of Lua functions --- and databases that is intended to be a supplement to the standard --- Lua functions included in the simulator scripting engine. --- --- MIST functions and databases provide ready-made solutions to many common --- scripting tasks and challenges, enabling easier scripting and saving --- mission scripters time. The table mist.flagFuncs contains a set of --- Lua functions (that are similar to Slmod functions) that do not --- require detailed Lua knowledge to use. --- --- However, the majority of MIST does require knowledge of the Lua language, --- and, if you are going to utilize these components of MIST, it is necessary --- that you read the Simulator Scripting Engine guide on the official ED wiki. --- --- Links: --- ED Forum Thread: http://forums.eagle.ru/showthread.php?t=98616 --- --- Github --- Development: https://github.com/mrSkortch/MissionScriptingTools/tree/development --- Official Release: https://github.com/mrSkortch/MissionScriptingTools/tree/master --- --- @module mist --- @author Speed --- @author Grimes --- @author lukrop +--[[-- +MIST Mission Scripting Tools. +## Description: +MIssion Scripting Tools (MIST) is a collection of Lua functions +and databases that is intended to be a supplement to the standard +Lua functions included in the simulator scripting engine. + +MIST functions and databases provide ready-made solutions to many common +scripting tasks and challenges, enabling easier scripting and saving +mission scripters time. The table mist.flagFuncs contains a set of +Lua functions (that are similar to Slmod functions) that do not +require detailed Lua knowledge to use. + +However, the majority of MIST does require knowledge of the Lua language, +and, if you are going to utilize these components of MIST, it is necessary +that you read the Simulator Scripting Engine guide on the official ED wiki. + +## Links: + +ED Forum Thread: + +##Github: + +Development + +Official Releases + +@script mist +@author Speed +@author Grimes +@author lukrop +]] mist = {} -- don't change these @@ -425,6 +431,128 @@ do -- the main scope end end + local function doScheduledFunctions() + local i = 1 + while i <= #Tasks do + if not Tasks[i].rep then -- not a repeated process + if Tasks[i].t <= timer.getTime() then + local Task = Tasks[i] -- local reference + table.remove(Tasks, i) + local err, errmsg = pcall(Task.f, unpack(Task.vars, 1, table.maxn(Task.vars))) + if not err then + log:error('mist.scheduleFunction, error in scheduled function: $1', errmsg) + end + --Task.f(unpack(Task.vars, 1, table.maxn(Task.vars))) -- do the task, do not increment i + else + i = i + 1 + end + else + if Tasks[i].st and Tasks[i].st <= timer.getTime() then --if a stoptime was specified, and the stop time exceeded + table.remove(Tasks, i) -- stop time exceeded, do not execute, do not increment i + elseif Tasks[i].t <= timer.getTime() then + local Task = Tasks[i] -- local reference + Task.t = timer.getTime() + Task.rep --schedule next run + local err, errmsg = pcall(Task.f, unpack(Task.vars, 1, table.maxn(Task.vars))) + if not err then + log:error('mist.scheduleFunction, error in scheduled function: $1' .. errmsg) + end + --Tasks[i].f(unpack(Tasks[i].vars, 1, table.maxn(Tasks[i].vars))) -- do the task + i = i + 1 + else + i = i + 1 + end + end + end + end + + -- Event handler to start creating the dead_objects table + local function addDeadObject(event) + if event.id == world.event.S_EVENT_DEAD or event.id == world.event.S_EVENT_CRASH then + if event.initiator and event.initiator.id_ and event.initiator.id_ > 0 then + + local id = event.initiator.id_ -- initial ID, could change if there is a duplicate id_ already dead. + local val = {object = event.initiator} -- the new entry in mist.DBs.deadObjects. + + local original_id = id --only for duplicate runtime IDs. + local id_ind = 1 + while mist.DBs.deadObjects[id] do + --print('duplicate runtime id of previously dead object- id: ' .. tostring(id)) + id = tostring(original_id) .. ' #' .. tostring(id_ind) + id_ind = id_ind + 1 + end + + if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then + --print('object found in alive_units') + val['objectData'] = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) + local pos = Object.getPosition(val.object) + if pos then + val['objectPos'] = pos.p + end + val['objectType'] = mist.DBs.aliveUnits[val.object.id_].category + --[[if mist.DBs.activeHumans[Unit.getName(val.object)] then + --trigger.action.outText('remove via death: ' .. Unit.getName(val.object),20) + mist.DBs.activeHumans[Unit.getName(val.object)] = nil + end]] + elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units + --print('object found in old_alive_units') + val['objectData'] = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) + local pos = Object.getPosition(val.object) + if pos then + val['objectPos'] = pos.p + end + val['objectType'] = mist.DBs.removedAliveUnits[val.object.id_].category + + else --attempt to determine if static object... + --print('object not found in alive units or old alive units') + local pos = Object.getPosition(val.object) + if pos then + local static_found = false + for ind, static in pairs(mist.DBs.unitsByCat['static']) do + if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... + --print('correlated dead static object to position') + val['objectData'] = static + val['objectPos'] = pos.p + val['objectType'] = 'static' + static_found = true + break + end + end + if not static_found then + val['objectPos'] = pos.p + val['objectType'] = 'building' + end + else + val['objectType'] = 'unknown' + end + end + mist.DBs.deadObjects[id] = val + + end + end + end + + --[[ + local function addClientsToActive(event) + if event.id == world.event.S_EVENT_PLAYER_ENTER_UNIT or event.id == world.event.S_EVENT_BIRTH then + log:info(mist.utils.tableShow(event)) + if Unit.getPlayerName(event.initiator) then + log:info(Unit.getPlayerName(event.initiator)) + local newU = mist.utils.deepCopy(mist.DBs.unitsByName[Unit.getName(event.initiator)]) + newU.playerName = Unit.getPlayerName(event.initiator) + mist.DBs.activeHumans[Unit.getName(event.initiator)] = newU + --trigger.action.outText('added: ' .. Unit.getName(event.initiator), 20) + end + elseif event.id == world.event.S_EVENT_PLAYER_LEAVE_UNIT and event.initiator then + if mist.DBs.activeHumans[Unit.getName(event.initiator)] then + mist.DBs.activeHumans[Unit.getName(event.initiator)] = nil + -- trigger.action.outText('removed via control: ' .. Unit.getName(event.initiator), 20) + end + end + end + + mist.addEventHandler(addClientsToActive) + ]] + --- init function. -- creates logger, adds default event handler -- and calls main the first time. @@ -437,6 +565,7 @@ do -- the main scope -- add event handler for group spawns mist.addEventHandler(groupSpawned) + mist.addEventHandler(addDeadObject) -- call main the first time therafter it reschedules itself. mist.main() @@ -498,9 +627,11 @@ do -- the main scope end end - mist.doScheduledFunctions() + doScheduledFunctions() end -- end of mist.main + --- Returns next unit id. + -- @treturn number next unit id. function mist.getNextUnitId() mist.nextUnitId = mist.nextUnitId + 1 if mist.nextUnitId > 6900 then @@ -509,6 +640,8 @@ do -- the main scope return mist.nextUnitId end + --- Returns next group id. + -- @treturn number next group id. function mist.getNextGroupId() mist.nextGroupId = mist.nextGroupId + 1 if mist.nextGroupId > 6900 then @@ -517,10 +650,15 @@ do -- the main scope return mist.nextGroupId end + --- Returns timestamp of last database update. + -- @treturn timestamp of last database update function mist.getLastDBUpdateTime() return lastUpdateTime end + --- Spawns a static object to the game world. + -- @todo write good docs + -- @tparam table staticObj table containing data needed for the object creation function mist.dynAddStatic(staticObj) local newObj = {} newObj.groupId = staticObj.groupId @@ -615,8 +753,10 @@ do -- the main scope return false end - -- same as coalition.add function in SSE. checks the passed data to see if its valid. + --- Spawns a dynamic group into the game world. + -- Same as coalition.add function in SSE. checks the passed data to see if its valid. -- Will generate groupId, groupName, unitId, and unitName if needed + -- @tparam table newGroup table containting values needed for spawning a group. function mist.dynAdd(newGroup) --mist.debug.writeData(mist.utils.serialize,{'msg', newGroup}, 'newGroupOrig.lua') @@ -796,16 +936,15 @@ do -- the main scope end - --Modified Slmod task scheduler, superior to timer.scheduleFunction - --[[ mist.scheduleFunction: - int id = mist.scheduleFunction(f function, vars table, t number, rep number, st number) - id - integer id of this function task - f - function to run - vars - table of vars for that function - t - time to run function - rep - time between repetitions of this function (OPTIONAL) - st - time when repetitions of this function will stop automatically (OPTIONAL) - ]] + --- Schedules a function. + -- Modified Slmod task scheduler, superior to timer.scheduleFunction + -- @tparam function f function to schedule + -- @tparam table vars array containing all parameters passed to the function + -- @tparam number t time in seconds from mission start to schedule the function to. + -- @tparam[opt] number rep time between repetitions of the function + -- @tparam[opt] number st time in seconds from mission start at which the function + -- should stop to be rescheduled. + -- @treturn number scheduled function id. function mist.scheduleFunction(f, vars, t, rep, st) --verify correct types assert(type(f) == 'function', 'variable 1, expected function, got ' .. type(f)) @@ -821,7 +960,9 @@ do -- the main scope return task_id end - -- removes a scheduled function based on the function's id. returns true if successful, false if not successful. + --- Removes a scheduled function. + -- @tparam number id function id + -- @treturn boolean true if function was successfully removed, false otherwise. function mist.removeFunction(id) local i = 1 while i <= #Tasks do @@ -833,41 +974,6 @@ do -- the main scope end end - -- not intended for users to use this function. - function mist.doScheduledFunctions() - local i = 1 - while i <= #Tasks do - if not Tasks[i].rep then -- not a repeated process - if Tasks[i].t <= timer.getTime() then - local Task = Tasks[i] -- local reference - table.remove(Tasks, i) - local err, errmsg = pcall(Task.f, unpack(Task.vars, 1, table.maxn(Task.vars))) - if not err then - log:error('mist.scheduleFunction, error in scheduled function: $1', errmsg) - end - --Task.f(unpack(Task.vars, 1, table.maxn(Task.vars))) -- do the task, do not increment i - else - i = i + 1 - end - else - if Tasks[i].st and Tasks[i].st <= timer.getTime() then --if a stoptime was specified, and the stop time exceeded - table.remove(Tasks, i) -- stop time exceeded, do not execute, do not increment i - elseif Tasks[i].t <= timer.getTime() then - local Task = Tasks[i] -- local reference - Task.t = timer.getTime() + Task.rep --schedule next run - local err, errmsg = pcall(Task.f, unpack(Task.vars, 1, table.maxn(Task.vars))) - if not err then - log:error('mist.scheduleFunction, error in scheduled function: $1' .. errmsg) - end - --Tasks[i].f(unpack(Tasks[i].vars, 1, table.maxn(Tasks[i].vars))) -- do the task - i = i + 1 - else - i = i + 1 - end - end - end - end - -- Simplified event handler function mist.addEventHandler(f) --id is optional! local handler = {} @@ -2127,8 +2233,14 @@ end end +--- Group functions. +-- @section groups do -- group functions scope + --- Check table used for group creation. + -- @tparam table groupData table to check. + -- @treturn boolean true if a group can be spawned using + -- this table, false otherwise. function mist.groupTableCheck(groupData) local isOk = false @@ -2826,9 +2938,9 @@ scope examples: ]] end +--- Logger class. +-- @type mist.Logger do -- mist.Logger scope - --- Logger class. - -- @type mist.Logger mist.Logger = {} --- Creates a new logger. @@ -2959,10 +3071,10 @@ do -- mist.Logger scope end end +--- Utility functions. +-- E.g. conversions between units etc. +-- @section mist.utils do -- mist.util scope - --- Utility functions. - -- E.g. conversions between units etc. - -- @section utils mist.utils = {} --- Converts angle in radians to degrees. @@ -3555,9 +3667,9 @@ function mist.utils.tableShow(tbl, loc, indent, tableshow_tbls) --based on seria end end +--- Debug functions +-- @section mist.debug do -- mist.debug scope - --- Debug functions - -- @section mist.debug mist.debug = {} --- Dumps the global table _G. @@ -3615,9 +3727,9 @@ do -- mist.debug scope end end +--- 3D Vector functions +-- @section mist.vec do -- mist.vec scope - --- 3D Vector functions - -- @section mist.vec mist.vec = {} --- Vector addition. @@ -3686,14 +3798,14 @@ do -- mist.vec scope end end +--- Flag functions. +-- The mist "Flag functions" are functions that are similar to Slmod functions +-- that detect a game condition and set a flag when that game condition is met. +-- +-- They are intended to be used by persons with little or no experience in Lua +-- programming, but with a good knowledge of the DCS mission editor. +-- @section mist.flagFunc do -- mist.flagFunc scope - --- Flag functions. - -- The mist "Flag functions" are functions that are similar to Slmod functions - -- that detect a game condition and set a flag when that game condition is met. - -- - -- They are intended to be used by persons with little or no experience in Lua - -- programming, but with a good knowledge of the DCS mission editor. - -- @section mist.flagFunc mist.flagFunc = {} --- Sets a flag if map objects are destroyed inside a zone. @@ -4271,6 +4383,9 @@ stopFlag end +--- Message functions. +-- Messaging system +-- @section mist.msg do -- mist.msg scope local messageList = {} -- this defines the max refresh rate of the message box it honestly only needs to @@ -4616,11 +4731,289 @@ end]] end, } + --[[ vars for mist.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 +]] + function mist.msgMGRS(vars) + local units = vars.units + local acc = vars.acc + local text = vars.text + local displayTime = vars.displayTime + local msgFor = vars.msgFor + + local s = mist.getMGRSString{units = units, acc = acc} + local newText + if text then + if string.find(text, '%%s') then -- look for %s + newText = string.format(text, s) -- insert the coordinates into the message + else + -- just append to the end. + newText = text .. s + end + else + newText = s + end + mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor + } + end + + --[[ vars for mist.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 +]] + function mist.msgLL(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 = mist.getLLString{units = units, acc = acc, DMS = DMS} + local newText + if text then + if string.find(text, '%%s') then -- look for %s + newText = string.format(text, s) -- insert the coordinates into the message + else + -- just append to the end. + newText = text .. s + end + else + newText = s + end + + mist.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 +]] + function mist.msgBR(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 mist.getBRString + local alt = vars.alt + local metric = vars.metric + local text = vars.text + local displayTime = vars.displayTime + local msgFor = vars.msgFor + + local s = mist.getBRString{units = units, ref = ref, alt = alt, metric = metric} + local newText + if text then + if string.find(text, '%%s') then -- look for %s + newText = string.format(text, s) -- insert the coordinates into the message + else + -- just append to the end. + newText = text .. s + end + else + newText = s + end + + mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor + } + + end + + -- basically, just sub-types of mist.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 +]] + function mist.msgBullseye(vars) + if string.lower(vars.ref) == 'red' then + vars.ref = mist.DBs.missionData.bullseye.red + mist.msgBR(vars) + elseif string.lower(vars.ref) == 'blue' then + vars.ref = mist.DBs.missionData.bullseye.blue + mist.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 +]] + function mist.msgBRA(vars) + if Unit.getByName(vars.ref) and Unit.getByName(vars.ref):isExist() == true then + vars.ref = Unit.getByName(vars.ref):getPosition().p + if not vars.alt then + vars.alt = true + end + mist.msgBR(vars) + end + end + + --[[ vars for mist.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 +]] + function mist.msgLeadingMGRS(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 = mist.getLeadingMGRSString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc} + local newText + if text then + if string.find(text, '%%s') then -- look for %s + newText = string.format(text, s) -- insert the coordinates into the message + else + -- just append to the end. + newText = text .. s + end + else + newText = s + end + + mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor + } + + + end + + --[[ vars for mist.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 +]] + function mist.msgLeadingLL(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 = mist.getLeadingLLString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc, DMS = DMS} + local newText + + if text then + if string.find(text, '%%s') then -- look for %s + newText = string.format(text, s) -- insert the coordinates into the message + else + -- just append to the end. + newText = text .. s + end + else + newText = s + end + + mist.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 +]] + function mist.msgLeadingBR(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 mist.getBRString + local text = vars.text + local displayTime = vars.displayTime + local msgFor = vars.msgFor + + local s = mist.getLeadingBRString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, metric = metric, alt = alt, ref = ref} + local newText + + if text then + if string.find(text, '%%s') then -- look for %s + newText = string.format(text, s) -- insert the coordinates into the message + else + -- just append to the end. + newText = text .. s + end + else + newText = s + end + + mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor + } + end end +--- Demo functions. +-- @section mist.demos do -- mist.demos scope - --- Demo functions. - -- @section mist.demos mist.demos = {} function mist.demos.printFlightData(unit) @@ -4737,505 +5130,9 @@ do -- mist.demos scope end -do -- mist.DBs scope - --- mist.DBs: various tables acting as databases - -- @section mist.DBs - mist.DBs = {} - - --- Mission data - -- @table mist.DBs.missionData - -- @field startTime mission start time - -- @field theatre mission theatre/map e.g. Caucasus - -- @field version mission version - -- @field files mission resources - mist.DBs.missionData = {} - if env.mission then - - mist.DBs.missionData['startTime'] = env.mission.start_time - mist.DBs.missionData['theatre'] = env.mission.theatre - mist.DBs.missionData['version'] = env.mission.version - mist.DBs.missionData['files'] = {} - if type(env.mission.resourceCounter) == 'table' then - for fIndex, fData in pairs (env.mission.resourceCounter) do - mist.DBs.missionData.files[#mist.DBs.missionData.files + 1] = mist.utils.deepCopy(fIndex) - end - end - mist.DBs.missionData['bullseye'] = {['red'] = {}, ['blue'] = {}} -- if we add more coalition specific data then bullsye should be categorized by coaliton. For now its just the bullseye table - mist.DBs.missionData.bullseye.red['x'] = env.mission.coalition.red.bullseye.x --should it be point.x? - mist.DBs.missionData.bullseye.red['y'] = env.mission.coalition.red.bullseye.y - mist.DBs.missionData.bullseye.blue['x'] = env.mission.coalition.blue.bullseye.x - mist.DBs.missionData.bullseye.blue['y'] = env.mission.coalition.blue.bullseye.y - end - - mist.DBs.zonesByName = {} - mist.DBs.zonesByNum = {} - - - if env.mission.triggers and env.mission.triggers.zones then - for zone_ind, zone_data in pairs(env.mission.triggers.zones) do - if type(zone_data) == 'table' then - local zone = mist.utils.deepCopy(zone_data) - zone['point'] = {} -- point is used by SSE - zone['point']['x'] = zone_data.x - zone['point']['y'] = 0 - zone['point']['z'] = zone_data.y - - mist.DBs.zonesByName[zone_data.name] = zone - mist.DBs.zonesByNum[#mist.DBs.zonesByNum + 1] = mist.utils.deepCopy(zone) --[[deepcopy so that the zone in zones_by_name and the zone in - zones_by_num se are different objects.. don't want them linked.]] - end - end - end - - mist.DBs.navPoints = {} - mist.DBs.units = {} - --Build mist.db.units and mist.DBs.navPoints - 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 - mist.DBs.units[coa_name] = {} - - -- build nav points DB - mist.DBs.navPoints[coa_name] = {} - if coa_data.nav_points then --navpoints - --mist.debug.writeData (mist.utils.serialize,{'NavPoints',coa_data.nav_points}, 'NavPoints.txt') - for nav_ind, nav_data in pairs(coa_data.nav_points) do - - if type(nav_data) == 'table' then - mist.DBs.navPoints[coa_name][nav_ind] = mist.utils.deepCopy(nav_data) - - mist.DBs.navPoints[coa_name][nav_ind]['name'] = nav_data.callsignStr -- name is a little bit more self-explanatory. - mist.DBs.navPoints[coa_name][nav_ind]['point'] = {} -- point is used by SSE, support it. - mist.DBs.navPoints[coa_name][nav_ind]['point']['x'] = nav_data.x - mist.DBs.navPoints[coa_name][nav_ind]['point']['y'] = 0 - mist.DBs.navPoints[coa_name][nav_ind]['point']['z'] = nav_data.y - end - end - end - if coa_data.country then --there is a country table - for cntry_id, cntry_data in pairs(coa_data.country) do - - local countryName = string.lower(cntry_data.name) - mist.DBs.units[coa_name][countryName] = {} - mist.DBs.units[coa_name][countryName]["countryId"] = cntry_data.id - - if type(cntry_data) == 'table' then --just making sure - - 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" or obj_type_name == "static" then --should be an unncessary check - - local category = obj_type_name - - 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! - - mist.DBs.units[coa_name][countryName][category] = {} - - for group_num, group_data in pairs(obj_type_data.group) do - - if group_data and group_data.units and type(group_data.units) == 'table' then --making sure again- this is a valid group - - mist.DBs.units[coa_name][countryName][category][group_num] = {} - local groupName = group_data.name - if env.mission.version > 7 then - groupName = env.getValueDictByKey(groupName) - end - mist.DBs.units[coa_name][countryName][category][group_num]["groupName"] = groupName - mist.DBs.units[coa_name][countryName][category][group_num]["groupId"] = group_data.groupId - mist.DBs.units[coa_name][countryName][category][group_num]["category"] = category - mist.DBs.units[coa_name][countryName][category][group_num]["coalition"] = coa_name - mist.DBs.units[coa_name][countryName][category][group_num]["country"] = countryName - mist.DBs.units[coa_name][countryName][category][group_num]["countryId"] = cntry_data.id - mist.DBs.units[coa_name][countryName][category][group_num]["startTime"] = group_data.start_time - mist.DBs.units[coa_name][countryName][category][group_num]["task"] = group_data.task - mist.DBs.units[coa_name][countryName][category][group_num]["hidden"] = group_data.hidden - - mist.DBs.units[coa_name][countryName][category][group_num]["units"] = {} - - mist.DBs.units[coa_name][countryName][category][group_num]["radioSet"] = group_data.radioSet - mist.DBs.units[coa_name][countryName][category][group_num]["uncontrolled"] = group_data.uncontrolled - mist.DBs.units[coa_name][countryName][category][group_num]["frequency"] = group_data.frequency - mist.DBs.units[coa_name][countryName][category][group_num]["modulation"] = group_data.modulation - - for unit_num, unit_data in pairs(group_data.units) do - local units_tbl = mist.DBs.units[coa_name][countryName][category][group_num]["units"] --pointer to the units table for this group - - units_tbl[unit_num] = {} - if env.mission.version > 7 then - units_tbl[unit_num]["unitName"] = env.getValueDictByKey(unit_data.name) - else - units_tbl[unit_num]["unitName"] = unit_data.name - end - units_tbl[unit_num]["type"] = unit_data.type - units_tbl[unit_num]["skill"] = unit_data.skill --will be nil for statics - units_tbl[unit_num]["unitId"] = unit_data.unitId - units_tbl[unit_num]["category"] = category - units_tbl[unit_num]["coalition"] = coa_name - units_tbl[unit_num]["country"] = countryName - units_tbl[unit_num]["countryId"] = cntry_data.id - units_tbl[unit_num]["heading"] = unit_data.heading - units_tbl[unit_num]["playerCanDrive"] = unit_data.playerCanDrive - units_tbl[unit_num]["alt"] = unit_data.alt - units_tbl[unit_num]["alt_type"] = unit_data.alt_type - units_tbl[unit_num]["speed"] = unit_data.speed - units_tbl[unit_num]["livery_id"] = unit_data.livery_id - if unit_data.point then --ME currently does not work like this, but it might one day - units_tbl[unit_num]["point"] = unit_data.point - else - units_tbl[unit_num]["point"] = {} - units_tbl[unit_num]["point"]["x"] = unit_data.x - units_tbl[unit_num]["point"]["y"] = unit_data.y - end - units_tbl[unit_num]['x'] = unit_data.x - units_tbl[unit_num]['y'] = unit_data.y - - units_tbl[unit_num]["callsign"] = unit_data.callsign - units_tbl[unit_num]["onboard_num"] = unit_data.onboard_num - units_tbl[unit_num]["hardpoint_racks"] = unit_data.hardpoint_racks - units_tbl[unit_num]["psi"] = unit_data.psi - - - units_tbl[unit_num]["groupName"] = groupName - units_tbl[unit_num]["groupId"] = group_data.groupId - - if unit_data.AddPropAircraft then - units_tbl[unit_num]["AddPropAircraft"] = unit_data.AddPropAircraft - end - - if category == 'static' then - units_tbl[unit_num]["categoryStatic"] = unit_data.category - units_tbl[unit_num]["shape_name"] = unit_data.shape_name - if unit_data.mass then - units_tbl[unit_num]["mass"] = unit_data.mass - end - - if unit_data.canCargo then - units_tbl[unit_num]["canCargo"] = unit_data.canCargo - end - end - - end --for unit_num, unit_data in pairs(group_data.units) do - end --if group_data and group_data.units then - 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 --if type(cntry_data) == 'table' then - 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 - - mist.DBs.unitsByName = {} - mist.DBs.unitsById = {} - mist.DBs.unitsByCat = {} - - mist.DBs.unitsByCat['helicopter'] = {} -- adding default categories - mist.DBs.unitsByCat['plane'] = {} - mist.DBs.unitsByCat['ship'] = {} - mist.DBs.unitsByCat['static'] = {} - mist.DBs.unitsByCat['vehicle'] = {} - - mist.DBs.unitsByNum = {} - - mist.DBs.groupsByName = {} - mist.DBs.groupsById = {} - mist.DBs.humansByName = {} - mist.DBs.humansById = {} - - mist.DBs.dynGroupsAdded = {} -- will be filled by mist.dbUpdate from dynamically spawned groups - mist.DBs.activeHumans = {} - - mist.DBs.aliveUnits = {} -- will be filled in by the "updateAliveUnits" coroutine in mist.main. - - mist.DBs.removedAliveUnits = {} -- will be filled in by the "updateAliveUnits" coroutine in mist.main. - - mist.DBs.const = {} - - mist.DBs.const.callsigns = { -- not accessible by SSE, must use static list :-/ - ['NATO'] = { - ['rules'] = { - ['groupLimit'] = 9, - }, - ['AWACS'] = { - ['Overlord'] = 1, - ['Magic'] = 2, - ['Wizard'] = 3, - ['Focus'] = 4, - ['Darkstar'] = 5, - }, - ['TANKER'] = { - ['Texaco'] = 1, - ['Arco'] = 2, - ['Shell'] = 3, - }, - ['JTAC'] = { - ['Axeman'] = 1, - ['Darknight'] = 2, - ['Warrior'] = 3, - ['Pointer'] = 4, - ['Eyeball'] = 5, - ['Moonbeam'] = 6, - ['Whiplash'] = 7, - ['Finger'] = 8, - ['Pinpoint'] = 9, - ['Ferret'] = 10, - ['Shaba'] = 11, - ['Playboy'] = 12, - ['Hammer'] = 13, - ['Jaguar'] = 14, - ['Deathstar'] = 15, - ['Anvil'] = 16, - ['Firefly'] = 17, - ['Mantis'] = 18, - ['Badger'] = 19, - }, - ['aircraft'] = { - ['Enfield'] = 1, - ['Springfield'] = 2, - ['Uzi'] = 3, - ['Colt'] = 4, - ['Dodge'] = 5, - ['Ford'] = 6, - ['Chevy'] = 7, - ['Pontiac'] = 8, - }, - - ['unique'] = { - ['A10'] = { - ['Hawg'] = 9, - ['Boar'] = 10, - ['Pig'] = 11, - ['Tusk'] = 12, - ['rules'] = { - ['canUseAircraft'] = true, - ['appliesTo'] = { - 'A-10C', - 'A-10A', - }, - }, - }, - }, - }, - -} --- create mist.DBs.oldAliveUnits --- do --- local intermediate_alive_units = {} -- between 0 and 0.5 secs old --- local function make_old_alive_units() -- called every 0.5 secs, makes the old_alive_units DB which is just a copy of alive_units that is 0.5 to 1 sec old --- if intermediate_alive_units then --- mist.DBs.oldAliveUnits = mist.utils.deepCopy(intermediate_alive_units) --- end --- intermediate_alive_units = mist.utils.deepCopy(mist.DBs.aliveUnits) --- timer.scheduleFunction(make_old_alive_units, nil, timer.getTime() + 0.5) --- end - --- make_old_alive_units() --- end - ---Build DBs -for coa_name, coa_data in pairs(mist.DBs.units) do - for cntry_name, cntry_data in pairs(coa_data) do - for category_name, category_data in pairs(cntry_data) do - if type(category_data) == 'table' then - for group_ind, group_data in pairs(category_data) do - if type(group_data) == 'table' and group_data.units and type(group_data.units) == 'table' and #group_data.units > 0 then -- OCD paradigm programming - mist.DBs.groupsByName[group_data.groupName] = mist.utils.deepCopy(group_data) - mist.DBs.groupsById[group_data.groupId] = mist.utils.deepCopy(group_data) - for unit_ind, unit_data in pairs(group_data.units) do - mist.DBs.unitsByName[unit_data.unitName] = mist.utils.deepCopy(unit_data) - mist.DBs.unitsById[unit_data.unitId] = mist.utils.deepCopy(unit_data) - - mist.DBs.unitsByCat[unit_data.category] = mist.DBs.unitsByCat[unit_data.category] or {} -- future-proofing against new categories... - table.insert(mist.DBs.unitsByCat[unit_data.category], mist.utils.deepCopy(unit_data)) - --print('inserting ' .. unit_data.unitName) - table.insert(mist.DBs.unitsByNum, mist.utils.deepCopy(unit_data)) - - if unit_data.skill and (unit_data.skill == "Client" or unit_data.skill == "Player") then - mist.DBs.humansByName[unit_data.unitName] = mist.utils.deepCopy(unit_data) - mist.DBs.humansById[unit_data.unitId] = mist.utils.deepCopy(unit_data) - --if Unit.getByName(unit_data.unitName) then - -- mist.DBs.activeHumans[unit_data.unitName] = mist.utils.deepCopy(unit_data) - -- mist.DBs.activeHumans[unit_data.unitName].playerName = Unit.getByName(unit_data.unitName):getPlayerName() - --end - end - end - end - end - end - end - end -end - - ---DynDBs -mist.DBs.MEunits = mist.utils.deepCopy(mist.DBs.units) -mist.DBs.MEunitsByName = mist.utils.deepCopy(mist.DBs.unitsByName) -mist.DBs.MEunitsById = mist.utils.deepCopy(mist.DBs.unitsById) -mist.DBs.MEunitsByCat = mist.utils.deepCopy(mist.DBs.unitsByCat) -mist.DBs.MEunitsByNum = mist.utils.deepCopy(mist.DBs.unitsByNum) -mist.DBs.MEgroupsByName = mist.utils.deepCopy(mist.DBs.groupsByName) -mist.DBs.MEgroupsById = mist.utils.deepCopy(mist.DBs.groupsById) -------------- - -mist.DBs.deadObjects = {} - -do - local mt = {} - - function mt.__newindex(t, key, val) - local original_key = key --only for duplicate runtime IDs. - local key_ind = 1 - while mist.DBs.deadObjects[key] do - --print('duplicate runtime id of previously dead object- key: ' .. tostring(key)) - key = tostring(original_key) .. ' #' .. tostring(key_ind) - key_ind = key_ind + 1 - end - - if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then - --print('object found in alive_units') - val['objectData'] = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) - local pos = Object.getPosition(val.object) - if pos then - val['objectPos'] = pos.p - end - val['objectType'] = mist.DBs.aliveUnits[val.object.id_].category - - elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units - --print('object found in old_alive_units') - val['objectData'] = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) - local pos = Object.getPosition(val.object) - if pos then - val['objectPos'] = pos.p - end - val['objectType'] = mist.DBs.removedAliveUnits[val.object.id_].category - - else --attempt to determine if static object... - --print('object not found in alive units or old alive units') - local pos = Object.getPosition(val.object) - if pos then - local static_found = false - for ind, static in pairs(mist.DBs.unitsByCat['static']) do - if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... - --print('correlated dead static object to position') - val['objectData'] = static - val['objectPos'] = pos.p - val['objectType'] = 'static' - static_found = true - break - end - end - if not static_found then - val['objectPos'] = pos.p - val['objectType'] = 'building' - end - else - val['objectType'] = 'unknown' - end - end - rawset(t, key, val) - end - - setmetatable(mist.DBs.deadObjects, mt) -end - --- Event handler to start creating the dead_objects table -do - - local function addDeadObject(event) - if event.id == world.event.S_EVENT_DEAD or event.id == world.event.S_EVENT_CRASH then - if event.initiator and event.initiator.id_ and event.initiator.id_ > 0 then - - local id = event.initiator.id_ -- initial ID, could change if there is a duplicate id_ already dead. - local val = {object = event.initiator} -- the new entry in mist.DBs.deadObjects. - - local original_id = id --only for duplicate runtime IDs. - local id_ind = 1 - while mist.DBs.deadObjects[id] do - --print('duplicate runtime id of previously dead object- id: ' .. tostring(id)) - id = tostring(original_id) .. ' #' .. tostring(id_ind) - id_ind = id_ind + 1 - end - - if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then - --print('object found in alive_units') - val['objectData'] = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) - local pos = Object.getPosition(val.object) - if pos then - val['objectPos'] = pos.p - end - val['objectType'] = mist.DBs.aliveUnits[val.object.id_].category - --[[if mist.DBs.activeHumans[Unit.getName(val.object)] then - --trigger.action.outText('remove via death: ' .. Unit.getName(val.object),20) - mist.DBs.activeHumans[Unit.getName(val.object)] = nil - end]] - elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units - --print('object found in old_alive_units') - val['objectData'] = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) - local pos = Object.getPosition(val.object) - if pos then - val['objectPos'] = pos.p - end - val['objectType'] = mist.DBs.removedAliveUnits[val.object.id_].category - - else --attempt to determine if static object... - --print('object not found in alive units or old alive units') - local pos = Object.getPosition(val.object) - if pos then - local static_found = false - for ind, static in pairs(mist.DBs.unitsByCat['static']) do - if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... - --print('correlated dead static object to position') - val['objectData'] = static - val['objectPos'] = pos.p - val['objectType'] = 'static' - static_found = true - break - end - end - if not static_found then - val['objectPos'] = pos.p - val['objectType'] = 'building' - end - else - val['objectType'] = 'unknown' - end - end - mist.DBs.deadObjects[id] = val - - end - end - end - - - mist.addEventHandler(addDeadObject) - - --[[ - local function addClientsToActive(event) - if event.id == world.event.S_EVENT_PLAYER_ENTER_UNIT or event.id == world.event.S_EVENT_BIRTH then - log:info(mist.utils.tableShow(event)) - if Unit.getPlayerName(event.initiator) then - log:info(Unit.getPlayerName(event.initiator)) - local newU = mist.utils.deepCopy(mist.DBs.unitsByName[Unit.getName(event.initiator)]) - newU.playerName = Unit.getPlayerName(event.initiator) - mist.DBs.activeHumans[Unit.getName(event.initiator)] = newU - --trigger.action.outText('added: ' .. Unit.getName(event.initiator), 20) - end - elseif event.id == world.event.S_EVENT_PLAYER_LEAVE_UNIT and event.initiator then - if mist.DBs.activeHumans[Unit.getName(event.initiator)] then - mist.DBs.activeHumans[Unit.getName(event.initiator)] = nil - -- trigger.action.outText('removed via control: ' .. Unit.getName(event.initiator), 20) - end - end - end - - mist.addEventHandler(addClientsToActive)]] - end - -end - +--- Time conversion functions. +-- @section mist.time do -- mist.time scope - -- mist.time: time conversion functions mist.time = {} -- returns a string for specified military time -- theTime is optional @@ -5427,9 +5324,9 @@ do -- mist.time scope end +--- Group task functions. +-- @section tasks do -- group tasks scope - --- Group task functions. - -- @section tasks mist.ground = {} mist.fixedWing = {} mist.heli = {} @@ -6043,285 +5940,406 @@ do -- group tasks scope end -do -- general purpose messages - --[[ vars for mist.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 -]] - function mist.msgMGRS(vars) - local units = vars.units - local acc = vars.acc - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor +--- Database tables. +-- @section mist.DBs +do -- mist.DBs scope + mist.DBs = {} - local s = mist.getMGRSString{units = units, acc = acc} - local newText - if text then - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message + --- Mission data + -- @table mist.DBs.missionData + -- @field startTime mission start time + -- @field theatre mission theatre/map e.g. Caucasus + -- @field version mission version + -- @field files mission resources + mist.DBs.missionData = {} + if env.mission then + + mist.DBs.missionData['startTime'] = env.mission.start_time + mist.DBs.missionData['theatre'] = env.mission.theatre + mist.DBs.missionData['version'] = env.mission.version + mist.DBs.missionData['files'] = {} + if type(env.mission.resourceCounter) == 'table' then + for fIndex, fData in pairs (env.mission.resourceCounter) do + mist.DBs.missionData.files[#mist.DBs.missionData.files + 1] = mist.utils.deepCopy(fIndex) + end + end + mist.DBs.missionData['bullseye'] = {['red'] = {}, ['blue'] = {}} -- if we add more coalition specific data then bullsye should be categorized by coaliton. For now its just the bullseye table + mist.DBs.missionData.bullseye.red['x'] = env.mission.coalition.red.bullseye.x --should it be point.x? + mist.DBs.missionData.bullseye.red['y'] = env.mission.coalition.red.bullseye.y + mist.DBs.missionData.bullseye.blue['x'] = env.mission.coalition.blue.bullseye.x + mist.DBs.missionData.bullseye.blue['y'] = env.mission.coalition.blue.bullseye.y + end + + mist.DBs.zonesByName = {} + mist.DBs.zonesByNum = {} + + + if env.mission.triggers and env.mission.triggers.zones then + for zone_ind, zone_data in pairs(env.mission.triggers.zones) do + if type(zone_data) == 'table' then + local zone = mist.utils.deepCopy(zone_data) + zone['point'] = {} -- point is used by SSE + zone['point']['x'] = zone_data.x + zone['point']['y'] = 0 + zone['point']['z'] = zone_data.y + + mist.DBs.zonesByName[zone_data.name] = zone + mist.DBs.zonesByNum[#mist.DBs.zonesByNum + 1] = mist.utils.deepCopy(zone) --[[deepcopy so that the zone in zones_by_name and the zone in + zones_by_num se are different objects.. don't want them linked.]] + end + end + end + + mist.DBs.navPoints = {} + mist.DBs.units = {} + --Build mist.db.units and mist.DBs.navPoints + 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 + mist.DBs.units[coa_name] = {} + + -- build nav points DB + mist.DBs.navPoints[coa_name] = {} + if coa_data.nav_points then --navpoints + --mist.debug.writeData (mist.utils.serialize,{'NavPoints',coa_data.nav_points}, 'NavPoints.txt') + for nav_ind, nav_data in pairs(coa_data.nav_points) do + + if type(nav_data) == 'table' then + mist.DBs.navPoints[coa_name][nav_ind] = mist.utils.deepCopy(nav_data) + + mist.DBs.navPoints[coa_name][nav_ind]['name'] = nav_data.callsignStr -- name is a little bit more self-explanatory. + mist.DBs.navPoints[coa_name][nav_ind]['point'] = {} -- point is used by SSE, support it. + mist.DBs.navPoints[coa_name][nav_ind]['point']['x'] = nav_data.x + mist.DBs.navPoints[coa_name][nav_ind]['point']['y'] = 0 + mist.DBs.navPoints[coa_name][nav_ind]['point']['z'] = nav_data.y + end + end + end + if coa_data.country then --there is a country table + for cntry_id, cntry_data in pairs(coa_data.country) do + + local countryName = string.lower(cntry_data.name) + mist.DBs.units[coa_name][countryName] = {} + mist.DBs.units[coa_name][countryName]["countryId"] = cntry_data.id + + if type(cntry_data) == 'table' then --just making sure + + 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" or obj_type_name == "static" then --should be an unncessary check + + local category = obj_type_name + + 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! + + mist.DBs.units[coa_name][countryName][category] = {} + + for group_num, group_data in pairs(obj_type_data.group) do + + if group_data and group_data.units and type(group_data.units) == 'table' then --making sure again- this is a valid group + + mist.DBs.units[coa_name][countryName][category][group_num] = {} + local groupName = group_data.name + if env.mission.version > 7 then + groupName = env.getValueDictByKey(groupName) + end + mist.DBs.units[coa_name][countryName][category][group_num]["groupName"] = groupName + mist.DBs.units[coa_name][countryName][category][group_num]["groupId"] = group_data.groupId + mist.DBs.units[coa_name][countryName][category][group_num]["category"] = category + mist.DBs.units[coa_name][countryName][category][group_num]["coalition"] = coa_name + mist.DBs.units[coa_name][countryName][category][group_num]["country"] = countryName + mist.DBs.units[coa_name][countryName][category][group_num]["countryId"] = cntry_data.id + mist.DBs.units[coa_name][countryName][category][group_num]["startTime"] = group_data.start_time + mist.DBs.units[coa_name][countryName][category][group_num]["task"] = group_data.task + mist.DBs.units[coa_name][countryName][category][group_num]["hidden"] = group_data.hidden + + mist.DBs.units[coa_name][countryName][category][group_num]["units"] = {} + + mist.DBs.units[coa_name][countryName][category][group_num]["radioSet"] = group_data.radioSet + mist.DBs.units[coa_name][countryName][category][group_num]["uncontrolled"] = group_data.uncontrolled + mist.DBs.units[coa_name][countryName][category][group_num]["frequency"] = group_data.frequency + mist.DBs.units[coa_name][countryName][category][group_num]["modulation"] = group_data.modulation + + for unit_num, unit_data in pairs(group_data.units) do + local units_tbl = mist.DBs.units[coa_name][countryName][category][group_num]["units"] --pointer to the units table for this group + + units_tbl[unit_num] = {} + if env.mission.version > 7 then + units_tbl[unit_num]["unitName"] = env.getValueDictByKey(unit_data.name) + else + units_tbl[unit_num]["unitName"] = unit_data.name + end + units_tbl[unit_num]["type"] = unit_data.type + units_tbl[unit_num]["skill"] = unit_data.skill --will be nil for statics + units_tbl[unit_num]["unitId"] = unit_data.unitId + units_tbl[unit_num]["category"] = category + units_tbl[unit_num]["coalition"] = coa_name + units_tbl[unit_num]["country"] = countryName + units_tbl[unit_num]["countryId"] = cntry_data.id + units_tbl[unit_num]["heading"] = unit_data.heading + units_tbl[unit_num]["playerCanDrive"] = unit_data.playerCanDrive + units_tbl[unit_num]["alt"] = unit_data.alt + units_tbl[unit_num]["alt_type"] = unit_data.alt_type + units_tbl[unit_num]["speed"] = unit_data.speed + units_tbl[unit_num]["livery_id"] = unit_data.livery_id + if unit_data.point then --ME currently does not work like this, but it might one day + units_tbl[unit_num]["point"] = unit_data.point + else + units_tbl[unit_num]["point"] = {} + units_tbl[unit_num]["point"]["x"] = unit_data.x + units_tbl[unit_num]["point"]["y"] = unit_data.y + end + units_tbl[unit_num]['x'] = unit_data.x + units_tbl[unit_num]['y'] = unit_data.y + + units_tbl[unit_num]["callsign"] = unit_data.callsign + units_tbl[unit_num]["onboard_num"] = unit_data.onboard_num + units_tbl[unit_num]["hardpoint_racks"] = unit_data.hardpoint_racks + units_tbl[unit_num]["psi"] = unit_data.psi + + + units_tbl[unit_num]["groupName"] = groupName + units_tbl[unit_num]["groupId"] = group_data.groupId + + if unit_data.AddPropAircraft then + units_tbl[unit_num]["AddPropAircraft"] = unit_data.AddPropAircraft + end + + if category == 'static' then + units_tbl[unit_num]["categoryStatic"] = unit_data.category + units_tbl[unit_num]["shape_name"] = unit_data.shape_name + if unit_data.mass then + units_tbl[unit_num]["mass"] = unit_data.mass + end + + if unit_data.canCargo then + units_tbl[unit_num]["canCargo"] = unit_data.canCargo + end + end + + end --for unit_num, unit_data in pairs(group_data.units) do + end --if group_data and group_data.units then + 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 --if type(cntry_data) == 'table' then + 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 + + mist.DBs.unitsByName = {} + mist.DBs.unitsById = {} + mist.DBs.unitsByCat = {} + + mist.DBs.unitsByCat['helicopter'] = {} -- adding default categories + mist.DBs.unitsByCat['plane'] = {} + mist.DBs.unitsByCat['ship'] = {} + mist.DBs.unitsByCat['static'] = {} + mist.DBs.unitsByCat['vehicle'] = {} + + mist.DBs.unitsByNum = {} + + mist.DBs.groupsByName = {} + mist.DBs.groupsById = {} + mist.DBs.humansByName = {} + mist.DBs.humansById = {} + + mist.DBs.dynGroupsAdded = {} -- will be filled by mist.dbUpdate from dynamically spawned groups + mist.DBs.activeHumans = {} + + mist.DBs.aliveUnits = {} -- will be filled in by the "updateAliveUnits" coroutine in mist.main. + + mist.DBs.removedAliveUnits = {} -- will be filled in by the "updateAliveUnits" coroutine in mist.main. + + mist.DBs.const = {} + + -- not accessible by SSE, must use static list :-/ + mist.DBs.const.callsigns = { + ['NATO'] = { + ['rules'] = { + ['groupLimit'] = 9, + }, + ['AWACS'] = { + ['Overlord'] = 1, + ['Magic'] = 2, + ['Wizard'] = 3, + ['Focus'] = 4, + ['Darkstar'] = 5, + }, + ['TANKER'] = { + ['Texaco'] = 1, + ['Arco'] = 2, + ['Shell'] = 3, + }, + ['JTAC'] = { + ['Axeman'] = 1, + ['Darknight'] = 2, + ['Warrior'] = 3, + ['Pointer'] = 4, + ['Eyeball'] = 5, + ['Moonbeam'] = 6, + ['Whiplash'] = 7, + ['Finger'] = 8, + ['Pinpoint'] = 9, + ['Ferret'] = 10, + ['Shaba'] = 11, + ['Playboy'] = 12, + ['Hammer'] = 13, + ['Jaguar'] = 14, + ['Deathstar'] = 15, + ['Anvil'] = 16, + ['Firefly'] = 17, + ['Mantis'] = 18, + ['Badger'] = 19, + }, + ['aircraft'] = { + ['Enfield'] = 1, + ['Springfield'] = 2, + ['Uzi'] = 3, + ['Colt'] = 4, + ['Dodge'] = 5, + ['Ford'] = 6, + ['Chevy'] = 7, + ['Pontiac'] = 8, + }, + + ['unique'] = { + ['A10'] = { + ['Hawg'] = 9, + ['Boar'] = 10, + ['Pig'] = 11, + ['Tusk'] = 12, + ['rules'] = { + ['canUseAircraft'] = true, + ['appliesTo'] = { + 'A-10C', + 'A-10A', + }, + }, + }, + }, + }, + } + -- create mist.DBs.oldAliveUnits + -- do + -- local intermediate_alive_units = {} -- between 0 and 0.5 secs old + -- local function make_old_alive_units() -- called every 0.5 secs, makes the old_alive_units DB which is just a copy of alive_units that is 0.5 to 1 sec old + -- if intermediate_alive_units then + -- mist.DBs.oldAliveUnits = mist.utils.deepCopy(intermediate_alive_units) + -- end + -- intermediate_alive_units = mist.utils.deepCopy(mist.DBs.aliveUnits) + -- timer.scheduleFunction(make_old_alive_units, nil, timer.getTime() + 0.5) + -- end + + -- make_old_alive_units() + -- end + + --Build DBs + for coa_name, coa_data in pairs(mist.DBs.units) do + for cntry_name, cntry_data in pairs(coa_data) do + for category_name, category_data in pairs(cntry_data) do + if type(category_data) == 'table' then + for group_ind, group_data in pairs(category_data) do + if type(group_data) == 'table' and group_data.units and type(group_data.units) == 'table' and #group_data.units > 0 then -- OCD paradigm programming + mist.DBs.groupsByName[group_data.groupName] = mist.utils.deepCopy(group_data) + mist.DBs.groupsById[group_data.groupId] = mist.utils.deepCopy(group_data) + for unit_ind, unit_data in pairs(group_data.units) do + mist.DBs.unitsByName[unit_data.unitName] = mist.utils.deepCopy(unit_data) + mist.DBs.unitsById[unit_data.unitId] = mist.utils.deepCopy(unit_data) + + mist.DBs.unitsByCat[unit_data.category] = mist.DBs.unitsByCat[unit_data.category] or {} -- future-proofing against new categories... + table.insert(mist.DBs.unitsByCat[unit_data.category], mist.utils.deepCopy(unit_data)) + --print('inserting ' .. unit_data.unitName) + table.insert(mist.DBs.unitsByNum, mist.utils.deepCopy(unit_data)) + + if unit_data.skill and (unit_data.skill == "Client" or unit_data.skill == "Player") then + mist.DBs.humansByName[unit_data.unitName] = mist.utils.deepCopy(unit_data) + mist.DBs.humansById[unit_data.unitId] = mist.utils.deepCopy(unit_data) + --if Unit.getByName(unit_data.unitName) then + -- mist.DBs.activeHumans[unit_data.unitName] = mist.utils.deepCopy(unit_data) + -- mist.DBs.activeHumans[unit_data.unitName].playerName = Unit.getByName(unit_data.unitName):getPlayerName() + --end + end + end + end + end + end + end + end + end + + --DynDBs + mist.DBs.MEunits = mist.utils.deepCopy(mist.DBs.units) + mist.DBs.MEunitsByName = mist.utils.deepCopy(mist.DBs.unitsByName) + mist.DBs.MEunitsById = mist.utils.deepCopy(mist.DBs.unitsById) + mist.DBs.MEunitsByCat = mist.utils.deepCopy(mist.DBs.unitsByCat) + mist.DBs.MEunitsByNum = mist.utils.deepCopy(mist.DBs.unitsByNum) + mist.DBs.MEgroupsByName = mist.utils.deepCopy(mist.DBs.groupsByName) + mist.DBs.MEgroupsById = mist.utils.deepCopy(mist.DBs.groupsById) + + mist.DBs.deadObjects = {} + +end + +do + local mt = {} + + function mt.__newindex(t, key, val) + local original_key = key --only for duplicate runtime IDs. + local key_ind = 1 + while mist.DBs.deadObjects[key] do + --print('duplicate runtime id of previously dead object- key: ' .. tostring(key)) + key = tostring(original_key) .. ' #' .. tostring(key_ind) + key_ind = key_ind + 1 + end + + if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then + --print('object found in alive_units') + val['objectData'] = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) + local pos = Object.getPosition(val.object) + if pos then + val['objectPos'] = pos.p + end + val['objectType'] = mist.DBs.aliveUnits[val.object.id_].category + + elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units + --print('object found in old_alive_units') + val['objectData'] = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) + local pos = Object.getPosition(val.object) + if pos then + val['objectPos'] = pos.p + end + val['objectType'] = mist.DBs.removedAliveUnits[val.object.id_].category + + else --attempt to determine if static object... + --print('object not found in alive units or old alive units') + local pos = Object.getPosition(val.object) + if pos then + local static_found = false + for ind, static in pairs(mist.DBs.unitsByCat['static']) do + if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... + --print('correlated dead static object to position') + val['objectData'] = static + val['objectPos'] = pos.p + val['objectType'] = 'static' + static_found = true + break + end + end + if not static_found then + val['objectPos'] = pos.p + val['objectType'] = 'building' + end else - -- just append to the end. - newText = text .. s + val['objectType'] = 'unknown' end - else - newText = s end - mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } + rawset(t, key, val) end - --[[ vars for mist.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 -]] - function mist.msgLL(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 = mist.getLLString{units = units, acc = acc, DMS = DMS} - local newText - if text then - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else - -- just append to the end. - newText = text .. s - end - else - newText = s - end - - mist.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 -]] - function mist.msgBR(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 mist.getBRString - local alt = vars.alt - local metric = vars.metric - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor - - local s = mist.getBRString{units = units, ref = ref, alt = alt, metric = metric} - local newText - if text then - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else - -- just append to the end. - newText = text .. s - end - else - newText = s - end - - mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } - - end - - -- basically, just sub-types of mist.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 -]] - function mist.msgBullseye(vars) - if string.lower(vars.ref) == 'red' then - vars.ref = mist.DBs.missionData.bullseye.red - mist.msgBR(vars) - elseif string.lower(vars.ref) == 'blue' then - vars.ref = mist.DBs.missionData.bullseye.blue - mist.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 -]] - function mist.msgBRA(vars) - if Unit.getByName(vars.ref) and Unit.getByName(vars.ref):isExist() == true then - vars.ref = Unit.getByName(vars.ref):getPosition().p - if not vars.alt then - vars.alt = true - end - mist.msgBR(vars) - end - end - - --[[ vars for mist.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 -]] - function mist.msgLeadingMGRS(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 = mist.getLeadingMGRSString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc} - local newText - if text then - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else - -- just append to the end. - newText = text .. s - end - else - newText = s - end - - mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } - - - end - - --[[ vars for mist.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 -]] - function mist.msgLeadingLL(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 = mist.getLeadingLLString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc, DMS = DMS} - local newText - - if text then - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else - -- just append to the end. - newText = text .. s - end - else - newText = s - end - - mist.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 -]] - function mist.msgLeadingBR(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 mist.getBRString - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor - - local s = mist.getLeadingBRString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, metric = metric, alt = alt, ref = ref} - local newText - - if text then - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else - -- just append to the end. - newText = text .. s - end - else - newText = s - end - - mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } - end + setmetatable(mist.DBs.deadObjects, mt) end do -- mist unitID funcs From c62b6c0cf692f240127c49fe73f6432e0e75b87b Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Fri, 8 Jan 2016 15:09:49 +0100 Subject: [PATCH 18/42] more ldoc --- mist.lua | 77 +++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 20 deletions(-) diff --git a/mist.lua b/mist.lua index 2f2f54a..55ac5ef 100644 --- a/mist.lua +++ b/mist.lua @@ -974,7 +974,9 @@ do -- the main scope end end - -- Simplified event handler + --- Registers an event handler. + -- @tparam function f function handling event + -- @treturn number id of the event handler function mist.addEventHandler(f) --id is optional! local handler = {} idNum = idNum + 1 @@ -987,6 +989,9 @@ do -- the main scope return handler.id end + --- Removes event handler with given id. + -- @tparam number id event handler id + -- @treturn boolean true on success, false otherwise function mist.removeEventHandler(id) for key, handler in pairs(world.eventHandlers) do if handler.id and handler.id == id then @@ -1133,6 +1138,9 @@ do -- the main scope return math.atan2(north_posit.z - point.z, north_posit.x - point.x) end + --- Returns skill of the given unit. + -- @tparam string unitName unit name + -- @return skill of the unit function mist.getUnitSkill(unitName) if Unit.getByName(unitName) and Unit.getByName(unitName):isExist() == true then local lunit = Unit.getByName(unitName) @@ -1145,8 +1153,16 @@ do -- the main scope return false end - function mist.getGroupPoints(groupIdent) -- if groupname exists in env.mission, then returns table of the group's points in numerical order, such as: { [1] = {x = 299435.224, y = -1146632.6773}, [2] = { x = 663324.6563, y = 322424.1112}} - -- refactor to search by groupId and allow groupId and groupName as inputs + --- Returns an array containing a group's units positions. + -- e.g. + -- { + -- [1] = {x = 299435.224, y = -1146632.6773}, + -- [2] = {x = 663324.6563, y = 322424.1112} + -- } + -- @tparam number|string groupIdent group id or name + -- @treturn table array containing positions of each group member + function mist.getGroupPoints(groupIdent) + -- search by groupId and allow groupId and groupName as inputs local gpId = groupIdent if type(groupIdent) == 'string' and not tonumber(groupIdent) then gpId = mist.DBs.MEgroupsByName[groupIdent].groupId @@ -1184,24 +1200,26 @@ do -- the main scope end --for coa_name, coa_data in pairs(mission.coalition) do end - --[[ table attitude = getAttitude(string unitname) -- will work on any unit, even if not an aircraft. - attitude = { - Heading = number, -- in radians, range of 0 to 2*pi, relative to true north - Pitch = number, -- in radians, range of -pi/2 to pi/2 - Roll = number, -- in radians, range of 0 to 2*pi, right roll is positive direction + --- getUnitAttitude(unit) return values. + -- Yaw, AoA, ClimbAngle - relative to earth reference + -- DOES NOT TAKE INTO ACCOUNT WIND. + -- @table attitude + -- @tfield number Heading in radians, range of 0 to 2*pi, + -- relative to true north. + -- @tfield number Pitch in radians, range of -pi/2 to pi/2 + -- @tfield number Roll in radians, range of 0 to 2*pi, + -- right roll is positive direction. + -- @tfield number Yaw in radians, range of -pi to pi, + -- right yaw is positive direction. + -- @tfield number AoA in radians, range of -pi to pi, + -- rotation of aircraft to the right in comparison to + -- flight direction being positive. + -- @tfield number ClimbAngle in radians, range of -pi/2 to pi/2 - --Yaw, AoA, ClimbAngle - relative to earth reference- DOES NOT TAKE INTO ACCOUNT WIND. - Yaw = number, -- in radians, range of -pi to pi, right yaw is positive direction - AoA = number, -- in radians, range of -pi to pi, rotation of aircraft to the right in comparison to flight direction being positive - ClimbAngle = number, -- in radians, range of -pi/2 to pi/2 - - --Maybe later? - AxialVel = table, velocity of the aircraft transformed into directions of aircraft axes - Speed = number -- absolute velocity in meters/sec - - } - - ]] + --- Returns the attitude of a given unit. + -- Will work on any unit, even if not an aircraft. + -- @tparam Unit unit unit whose attitude is returned. + -- @treturn table @{attitude} function mist.getAttitude(unit) local unitpos = unit:getPosition() if unitpos then @@ -1279,6 +1297,10 @@ do -- the main scope end end + --- Returns heading of given unit. + -- @tparam Unit unit unit whose heading is returned. + -- @treturn number heading of the unit, in range + -- of 0 to 2*pi. function mist.getHeading(unit, rawHeading) local unitpos = unit:getPosition() if unitpos then @@ -1293,6 +1315,9 @@ do -- the main scope end end + --- Returns given unit's pitch + -- @tparam Unit unit unit whose pitch is returned. + -- @treturn number pitch of given unit function mist.getPitch(unit) local unitpos = unit:getPosition() if unitpos then @@ -1300,6 +1325,9 @@ do -- the main scope end end + --- Returns given unit's roll. + -- @tparam Unit unit unit whose roll is returned. + -- @treturn number roll of given unit function mist.getRoll(unit) local unitpos = unit:getPosition() if unitpos then @@ -1326,6 +1354,9 @@ do -- the main scope end end + --- Returns given unit's yaw. + -- @tparam Unit unit unit whose yaw is returned. + -- @treturn number yaw of given unit. function mist.getYaw(unit) local unitpos = unit:getPosition() if unitpos then @@ -1352,6 +1383,9 @@ do -- the main scope end end + --- Returns given unit's angle of attack. + -- @tparam Unit unit unit to get AoA from. + -- @treturn number angle of attack of the given unit. function mist.getAoA(unit) local unitpos = unit:getPosition() if unitpos then @@ -1376,6 +1410,9 @@ do -- the main scope end end + --- Returns given unit's climb angle. + -- @tparam Unit unit unit to get climb angle from. + -- @treturn number climb angle of given unit. function mist.getClimbAngle(unit) local unitpos = unit:getPosition() if unitpos then From 4d0617a96367e51721a73dc1dd3aea4f831d64cf Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Sat, 9 Jan 2016 20:26:38 +0100 Subject: [PATCH 19/42] improved logger --- mist.lua | 71 ++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/mist.lua b/mist.lua index a1a7d71..7dab9fe 100644 --- a/mist.lua +++ b/mist.lua @@ -2823,34 +2823,30 @@ do -- mist.Logger scope --- Creates a new logger. -- Each logger has it's own tag and log level. - -- @tparam[opt] table l optional logger object. - -- @usage myLogger = mist.Logger:new - -- @usage myLogger = mist.Logger:new{tag = "MyScript", level = 2} + -- @tparam string tag tag which appears at the start of + -- every log line produced by this logger. + -- @tparam[opt] number|string level the log level defines which messages + -- will be logged and which will be omitted. Log level 3 beeing the most verbose + -- and 0 disabling all output. This can also be a string. Allowed strings are: + -- "none" (0), "error" (1), "warning" (2) and "info" (3). + -- @usage myLogger = mist.Logger:new("MyScript") + -- @usage myLogger = mist.Logger:new("MyScript", 2) + -- @usage myLogger = mist.Logger:new("MyScript", "info") -- @treturn mist.Logger - function mist.Logger:new(l) - l = l or { - tag = "MIST", - level = 1, - } - -- in case the user only supplied - -- either tag or level - if not l.tag then - l.tag = "MIST" - end - if not l.level then - l.level = 1 - end + function mist.Logger:new(tag, level) + local l = {} + l.tag = tag setmetatable(l, self) self.__index = self + mist.Logger.setLevel(self, level) return l end --- Sets the level of verbosity for this logger. - -- "none" or 0 disables all logging - -- "error" or 1 shows error messages only - -- "warning" or 2 shows error and warning messages - -- "info" or 3 shows error, warning and info messages - -- @param level this can either be a string or an integer level + -- @tparam[opt] number|string level the log level defines which messages + -- will be logged and which will be omitted. Log level 3 beeing the most verbose + -- and 0 disabling all output. This can also be a string. Allowed strings are: + -- "none" (0), "error" (1), "warning" (2) and "info" (3). -- @usage myLogger:setLevel("info") -- @usage -- log everything --myLogger:setLevel(3) @@ -2896,6 +2892,15 @@ do -- mist.Logger scope end end + local function serializeVar(var) + if type(var) == 'table' then + var = mist.utils.oneLineSerialize(var) + else + var = tostring(var) + end + return var + end + --- Logs error and shows alert window. -- This logs an error to the dcs.log and shows a popup window, -- pausing the simulation. This works always even if logging is @@ -2904,7 +2909,11 @@ do -- mist.Logger scope -- @param ... variables to be used for substitution. -- @usage myLogger:alert("Shit just hit the fan! WEEEE!!!11") function mist.Logger:alert(text, ...) - text = formatText(text, unpack(arg)) + if type(text) ~= 'string' then + text = serializeVar(text) + else + text = formatText(text, unpack(arg)) + end env.error(self.tag .. '|' .. text, true) end @@ -2917,7 +2926,11 @@ do -- mist.Logger scope -- @usage myLogger:error("Foo is $1 instead of $2", foo, "bar") function mist.Logger:error(text, ...) if self.level >= 1 then - text = formatText(text, unpack(arg)) + if type(text) ~= 'string' then + text = serializeVar(text) + else + text = formatText(text, unpack(arg)) + end env.error(self.tag .. '|' .. text) end end @@ -2930,7 +2943,11 @@ do -- mist.Logger scope -- @usage myLogger:warn("Mother warned you! Those $1 from the interwebs are $2", {"geeks", 1337}) function mist.Logger:warn(text, ...) if self.level >= 2 then - text = formatText(text, unpack(arg)) + if type(text) ~= 'string' then + text = serializeVar(text) + else + text = formatText(text, unpack(arg)) + end env.warning(self.tag .. '|' .. text) end end @@ -2943,7 +2960,11 @@ do -- mist.Logger scope -- @see warn function mist.Logger:info(text, ...) if self.level >= 3 then - text = formatText(text, unpack(arg)) + if type(text) ~= 'string' then + text = serializeVar(text) + else + text = formatText(text, unpack(arg)) + end env.info(self.tag .. '|' .. text) end end From a7a9cb6808a996ebf2dc8072d50781ee5f01f2f9 Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Sat, 9 Jan 2016 20:52:12 +0100 Subject: [PATCH 20/42] fixed new call --- mist.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mist.lua b/mist.lua index 7dab9fe..08857c1 100644 --- a/mist.lua +++ b/mist.lua @@ -420,7 +420,7 @@ do -- the main scope -- and calls main the first time. function mist.init() -- create logger - mist.log = mist.Logger:new() + mist.log = mist.Logger:new("MIST") log = mist.log -- set info log level log:setLevel("info") @@ -2838,7 +2838,7 @@ do -- mist.Logger scope l.tag = tag setmetatable(l, self) self.__index = self - mist.Logger.setLevel(self, level) + self:setLevel(level) return l end From 1aa6948db47224c5a877204e81be3461d2d51c08 Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Mon, 11 Jan 2016 19:31:16 +0100 Subject: [PATCH 21/42] use new logger though most of those debug messages could probably be removed. --- mist.lua | 46 ++++++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/mist.lua b/mist.lua index 6bd90b4..a12a77c 100644 --- a/mist.lua +++ b/mist.lua @@ -82,7 +82,7 @@ do -- the main scope if lunits[i].category ~= 'static' then -- can't get statics with Unit.getByName :( local unit = lUnit.getByName(lunits[i].unitName) if unit then - --print('unit named ' .. lunits[i].unitName .. ' alive!') + log:info("unit named $1 alive!", lunits[i].unitName) local pos = unit:getPosition() local newtbl = ldeepcopy(lunits[i]) if pos then @@ -95,9 +95,9 @@ do -- the main scope end end if i%units_per_run == 0 then - --print('yielding at: ' .. tostring(i)) + log:info("yielding at: $1", i) coroutine.yield() - --print('resuming at: ' .. tostring(i)) + log:info("resuming at: $1", i) end end -- All units updated, remove any "alive" units that were not updated- they are dead! @@ -476,13 +476,13 @@ do -- the main scope local original_id = id --only for duplicate runtime IDs. local id_ind = 1 while mist.DBs.deadObjects[id] do - --print('duplicate runtime id of previously dead object- id: ' .. tostring(id)) + log:info('duplicate runtime id of previously dead object id: $1', id) id = tostring(original_id) .. ' #' .. tostring(id_ind) id_ind = id_ind + 1 end if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then - --print('object found in alive_units') + log:info('object found in alive_units') val['objectData'] = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) local pos = Object.getPosition(val.object) if pos then @@ -494,7 +494,7 @@ do -- the main scope mist.DBs.activeHumans[Unit.getName(val.object)] = nil end]] elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units - --print('object found in old_alive_units') + log:info('object found in old_alive_units') val['objectData'] = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) local pos = Object.getPosition(val.object) if pos then @@ -503,13 +503,13 @@ do -- the main scope val['objectType'] = mist.DBs.removedAliveUnits[val.object.id_].category else --attempt to determine if static object... - --print('object not found in alive units or old alive units') + log:info('object not found in alive units or old alive units') local pos = Object.getPosition(val.object) if pos then local static_found = false for ind, static in pairs(mist.DBs.unitsByCat['static']) do if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... - --print('correlated dead static object to position') + log:info('correlated dead static object to position') val['objectData'] = static val['objectPos'] = pos.p val['objectType'] = 'static' @@ -3420,12 +3420,9 @@ do -- mist.util scope -- mist.utils.typeCheck(type_tbl, my_tb) -- @return true if table passes the check, false otherwise. function mist.utils.typeCheck(fname, type_tbl, var_tbl) - --log:info('type check') + -- log:info('type check') for type_key, type_val in pairs(type_tbl) do - --print('type_key:') - --print(type_key) - --print('type_val:') - --print(type_val) + -- log:info('type_key: $1 type_val: $2', type_key, type_val) --type_key can be a table of accepted keys- so try to find one that is not nil local type_key_str = '' @@ -3449,8 +3446,7 @@ do -- mist.util scope local passed_check = false if type(type_tbl[type_key]) == 'table' then - --print('err_msg, before and after:') - --print(err_msg) + -- log:info('err_msg, before: $1', err_msg) for j = 1, #type_tbl[type_key] do if j == 1 then @@ -3463,15 +3459,13 @@ do -- mist.util scope passed_check = true end end - --print(err_msg) + -- log:info('err_msg, after: $1', err_msg) else - --print('err_msg, before and after:') - --print(err_msg) + -- log:info('err_msg, before: $1', err_msg) err_msg = err_msg .. type_tbl[type_key] - --print(err_msg) + -- log:info('err_msg, after: $1', err_msg) if type(var_tbl[act_key]) == type_tbl[type_key] then passed_check = true - end end @@ -6310,7 +6304,7 @@ do -- mist.DBs scope mist.DBs.unitsByCat[unit_data.category] = mist.DBs.unitsByCat[unit_data.category] or {} -- future-proofing against new categories... table.insert(mist.DBs.unitsByCat[unit_data.category], mist.utils.deepCopy(unit_data)) - --print('inserting ' .. unit_data.unitName) + log:info('inserting $1', unit_data.unitName) table.insert(mist.DBs.unitsByNum, mist.utils.deepCopy(unit_data)) if unit_data.skill and (unit_data.skill == "Client" or unit_data.skill == "Player") then @@ -6349,13 +6343,13 @@ do local original_key = key --only for duplicate runtime IDs. local key_ind = 1 while mist.DBs.deadObjects[key] do - --print('duplicate runtime id of previously dead object- key: ' .. tostring(key)) + log:warn('duplicate runtime id of previously dead object key: $1', key) key = tostring(original_key) .. ' #' .. tostring(key_ind) key_ind = key_ind + 1 end if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then - --print('object found in alive_units') + log:info('object found in alive_units') val['objectData'] = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) local pos = Object.getPosition(val.object) if pos then @@ -6364,7 +6358,7 @@ do val['objectType'] = mist.DBs.aliveUnits[val.object.id_].category elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units - --print('object found in old_alive_units') + log:info('object found in old_alive_units') val['objectData'] = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) local pos = Object.getPosition(val.object) if pos then @@ -6373,13 +6367,13 @@ do val['objectType'] = mist.DBs.removedAliveUnits[val.object.id_].category else --attempt to determine if static object... - --print('object not found in alive units or old alive units') + log:info('object not found in alive units or old alive units') local pos = Object.getPosition(val.object) if pos then local static_found = false for ind, static in pairs(mist.DBs.unitsByCat['static']) do if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... - --print('correlated dead static object to position') + log:info('correlated dead static object to position') val['objectData'] = static val['objectPos'] = pos.p val['objectType'] = 'static' From b10b56aa9540181f9b4fead67b05a5df22208a24 Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Mon, 11 Jan 2016 20:28:30 +0100 Subject: [PATCH 22/42] save some brackets and single quotes. they are an endangered species :grin: --- mist.lua | 242 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 124 insertions(+), 118 deletions(-) diff --git a/mist.lua b/mist.lua index a12a77c..5a8d1e3 100644 --- a/mist.lua +++ b/mist.lua @@ -40,6 +40,8 @@ mist.build = 61 -- forward declaration of log shorthand local log +--- Functions +-- @section mist do -- the main scope local coroutines = {} @@ -86,10 +88,10 @@ do -- the main scope local pos = unit:getPosition() local newtbl = ldeepcopy(lunits[i]) if pos then - newtbl['pos'] = pos.p + newtbl.pos = pos.p end - newtbl['unit'] = unit - --newtbl['rt_id'] = unit.id_ + newtbl.unit = unit + --newtbl.rt_id = unit.id_ lalive_units[unit.id_] = newtbl updatedUnits[unit.id_] = true end @@ -113,7 +115,7 @@ do -- the main scope local function dbUpdate(event) local newTable = {} - newTable['startTime'] = 0 + newTable.startTime = 0 if type(event) == 'string' then -- if name of an object. local newObject @@ -146,11 +148,11 @@ do -- the main scope end for countryData, countryId in pairs(country.id) do if newTable.country and string.upper(countryData) == string.upper(newTable.country) or countryId == newTable.countryId then - newTable['countryId'] = countryId - newTable['country'] = string.lower(countryData) + newTable.countryId = countryId + newTable.country = string.lower(countryData) for coaData, coaId in pairs(coalition.side) do if coaId == coalition.getCountryCoalition(countryId) then - newTable['coalition'] = string.lower(coaData) + newTable.coalition = string.lower(coaData) end end end @@ -158,11 +160,11 @@ do -- the main scope for catData, catId in pairs(Unit.Category) do if newType == 'group' and Group.getByName(newTable.groupName):isExist() then if catId == Group.getByName(newTable.groupName):getCategory() then - newTable['category'] = string.lower(catData) + newTable.category = string.lower(catData) end elseif newType == 'static' and StaticObject.getByName(newTable.groupName):isExist() then if catId == StaticObject.getByName(newTable.groupName):getCategory() then - newTable['category'] = string.lower(catData) + newTable.category = string.lower(catData) end end @@ -276,7 +278,7 @@ do -- the main scope end end --mist.debug.writeData(mist.utils.serialize,{'msg', newTable}, timer.getAbsTime() ..'Group.lua') - newTable['timeAdded'] = timer.getAbsTime() -- only on the dynGroupsAdded table. For other reference, see start time + newTable.timeAdded = timer.getAbsTime() -- only on the dynGroupsAdded table. For other reference, see start time --mist.debug.dumpDBs() --end @@ -369,18 +371,18 @@ do -- the main scope mistCategory = string.lower(newTable.category) end - if string.upper(newTable['category']) == 'GROUND_UNIT' then + if string.upper(newTable.category) == 'GROUND_UNIT' then mistCategory = 'vehicle' - newTable['category'] = mistCategory - elseif string.upper(newTable['category']) == 'AIRPLANE' then + newTable.category = mistCategory + elseif string.upper(newTable.category) == 'AIRPLANE' then mistCategory = 'plane' - newTable['category'] = mistCategory - elseif string.upper(newTable['category']) == 'HELICOPTER' then + newTable.category = mistCategory + elseif string.upper(newTable.category) == 'HELICOPTER' then mistCategory = 'helicopter' - newTable['category'] = mistCategory - elseif string.upper(newTable['category']) == 'SHIP' then + newTable.category = mistCategory + elseif string.upper(newTable.category) == 'SHIP' then mistCategory = 'ship' - newTable['category'] = mistCategory + newTable.category = mistCategory end for newId, newUnitData in pairs(newTable.units) do newUnitData.category = mistCategory @@ -399,7 +401,7 @@ do -- the main scope if not mist.DBs.units[newTable.coalition][newTable.country] then mist.DBs.units[newTable.coalition][(newTable.country)] = {} - mist.DBs.units[newTable.coalition][(newTable.country)]['countryId'] = newTable.countryId + mist.DBs.units[newTable.coalition][(newTable.country)].countryId = newTable.countryId end if not mist.DBs.units[newTable.coalition][newTable.country][mistCategory] then mist.DBs.units[newTable.coalition][(newTable.country)][mistCategory] = {} @@ -483,46 +485,46 @@ do -- the main scope if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then log:info('object found in alive_units') - val['objectData'] = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) + val.objectData = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) local pos = Object.getPosition(val.object) if pos then - val['objectPos'] = pos.p + val.objectPos = pos.p end - val['objectType'] = mist.DBs.aliveUnits[val.object.id_].category + val.objectType = mist.DBs.aliveUnits[val.object.id_].category --[[if mist.DBs.activeHumans[Unit.getName(val.object)] then --trigger.action.outText('remove via death: ' .. Unit.getName(val.object),20) mist.DBs.activeHumans[Unit.getName(val.object)] = nil end]] elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units log:info('object found in old_alive_units') - val['objectData'] = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) + val.objectData = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) local pos = Object.getPosition(val.object) if pos then - val['objectPos'] = pos.p + val.objectPos = pos.p end - val['objectType'] = mist.DBs.removedAliveUnits[val.object.id_].category + val.objectType = mist.DBs.removedAliveUnits[val.object.id_].category else --attempt to determine if static object... log:info('object not found in alive units or old alive units') local pos = Object.getPosition(val.object) if pos then local static_found = false - for ind, static in pairs(mist.DBs.unitsByCat['static']) do + for ind, static in pairs(mist.DBs.unitsByCat.static) do if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... log:info('correlated dead static object to position') - val['objectData'] = static - val['objectPos'] = pos.p - val['objectType'] = 'static' + val.objectData = static + val.objectPos = pos.p + val.objectType = 'static' static_found = true break end end if not static_found then - val['objectPos'] = pos.p - val['objectType'] = 'building' + val.objectPos = pos.p + val.objectType = 'building' end else - val['objectType'] = 'unknown' + val.objectType = 'unknown' end end mist.DBs.deadObjects[id] = val @@ -585,7 +587,7 @@ do -- the main scope write_DB_table_counter = 0 if not coroutines.updateDBTables then - coroutines['updateDBTables'] = coroutine.create(updateDBTables) + coroutines.updateDBTables = coroutine.create(updateDBTables) end coroutine.resume(coroutines.updateDBTables) @@ -601,7 +603,7 @@ do -- the main scope check_spawn_events_counter = 0 if not coroutines.checkSpawnedEvents then - coroutines['checkSpawnedEvents'] = coroutine.create(checkSpawnedEvents) + coroutines.checkSpawnedEvents = coroutine.create(checkSpawnedEvents) end coroutine.resume(coroutines.checkSpawnedEvents) @@ -617,7 +619,7 @@ do -- the main scope update_alive_units_counter = 0 if not coroutines.update_alive_units then - coroutines['updateAliveUnits'] = coroutine.create(updateAliveUnits) + coroutines.updateAliveUnits = coroutine.create(updateAliveUnits) end coroutine.resume(coroutines.updateAliveUnits) @@ -822,14 +824,14 @@ do -- the main scope end if newGroup.groupName or newGroup.name then if newGroup.groupName then - newGroup['name'] = newGroup.groupName + newGroup.name = newGroup.groupName elseif newGroup.name then - newGroup['name'] = newGroup.name + newGroup.name = newGroup.name end end if newGroup.clone and mist.DBs.groupsByName[newGroup.name] or not newGroup.name then - newGroup['name'] = tostring(tostring(country.name[cntry]) .. tostring(typeName) .. mistDynAddIndex) + newGroup.name = tostring(tostring(country.name[cntry]) .. tostring(typeName) .. mistDynAddIndex) end if not newGroup.hidden then @@ -853,7 +855,7 @@ do -- the main scope local originalName = newGroup.units[unitIndex].unitName or newGroup.units[unitIndex].name if newGroup.clone or not unitData.unitId then mistUnitId = mistUnitId + 1 - newGroup.units[unitIndex]['unitId'] = mistUnitId + newGroup.units[unitIndex].unitId = mistUnitId end if newGroup.units[unitIndex].unitName or newGroup.units[unitIndex].name then if newGroup.units[unitIndex].unitName then @@ -1299,6 +1301,7 @@ do -- the main scope --- Returns heading of given unit. -- @tparam Unit unit unit whose heading is returned. + -- @param rawHeading -- @treturn number heading of the unit, in range -- of 0 to 2*pi. function mist.getHeading(unit, rawHeading) @@ -1423,8 +1426,8 @@ do -- the main scope end end - function mist.makeUnitTable(tbl) - --[[ + --[[-- + Returns a table containing unit names. Prefixes: "[-u]" - subtract this unit if its in the table "[g]" - add this group to the table @@ -1497,8 +1500,10 @@ do -- the main scope "South Osetia" "Abkhazia" "Italy" - ]] + @tparam table tbl sequential strings + ]] + function mist.makeUnitTable(tbl) --Assumption: will be passed a table of strings, sequential local units_by_name = {} @@ -1796,7 +1801,7 @@ do -- the main scope end - units_tbl['processed'] = timer.getTime() --add the processed flag + units_tbl.processed = timer.getTime() --add the processed flag return units_tbl end @@ -2032,11 +2037,11 @@ function mist.getUnitsLOS(unitset1, altoffset1, unitset2, altoffset2, radius) if unit_added == false then unit_added = true LOS_data[#LOS_data + 1] = {} - LOS_data[#LOS_data]['unit'] = unit_info1[unit1_ind].unit - LOS_data[#LOS_data]['vis'] = {} - LOS_data[#LOS_data]['vis'][#LOS_data[#LOS_data]['vis'] + 1] = unit_info2[unit2_ind].unit + LOS_data[#LOS_data].unit = unit_info1[unit1_ind].unit + LOS_data[#LOS_data].vis = {} + LOS_data[#LOS_data].vis[#LOS_data[#LOS_data].vis + 1] = unit_info2[unit2_ind].unit else - LOS_data[#LOS_data]['vis'][#LOS_data[#LOS_data]['vis'] + 1] = unit_info2[unit2_ind].unit + LOS_data[#LOS_data].vis[#LOS_data[#LOS_data].vis + 1] = unit_info2[unit2_ind].unit end end end @@ -2325,18 +2330,18 @@ do -- group functions scope for unitNum, unitData in pairs(newGroup:getUnits()) do newData.units[unitNum] = {} newData.units[unitNum]["unitId"] = tonumber(unitData:getID()) - newData.units[unitNum]['point'] = unitData.point - newData.units[unitNum]['x'] = unitData:getPosition().p.x - newData.units[unitNum]['y'] = unitData:getPosition().p.z + newData.units[unitNum].point = unitData.point + newData.units[unitNum].x = unitData:getPosition().p.x + newData.units[unitNum].y = unitData:getPosition().p.z newData.units[unitNum]["type"] = unitData:getTypeName() newData.units[unitNum]["skill"] = mist.getUnitSkill(unitData:getName()) -- get velocity needed newData.units[unitNum]["unitName"] = unitData:getName() newData.units[unitNum]["heading"] = mist.getHeading(unitData, true) -- added to DBs - newData.units[unitNum]['alt'] = unitData:getPosition().p.y + newData.units[unitNum].alt = unitData:getPosition().p.y newData.country = string.lower(country.name[unitData:getCountry()]) - newData.units[unitNum]['callsign'] = unitData:getCallsign() + newData.units[unitNum].callsign = unitData:getCallsign() end return newData @@ -2382,12 +2387,12 @@ do -- group functions scope newData.units[unitNum] = {} newData.units[unitNum]["unitId"] = unitData.unitId - --newData.units[unitNum]['point'] = unitData.point - newData.units[unitNum]['x'] = unitData.point.x - newData.units[unitNum]['y'] = unitData.point.y - newData.units[unitNum]['alt'] = unitData.alt - newData.units[unitNum]['alt_type'] = unitData.alt_type - newData.units[unitNum]['speed'] = unitData.speed + --newData.units[unitNum].point = unitData.point + newData.units[unitNum].x = unitData.point.x + newData.units[unitNum].y = unitData.point.y + newData.units[unitNum].alt = unitData.alt + newData.units[unitNum].alt_type = unitData.alt_type + newData.units[unitNum].speed = unitData.speed newData.units[unitNum]["type"] = unitData.type newData.units[unitNum]["skill"] = unitData.skill newData.units[unitNum]["unitName"] = unitData.unitName @@ -2397,16 +2402,16 @@ do -- group functions scope if newData.category == 'plane' or newData.category == 'helicopter' then newData.units[unitNum]["payload"] = payloads[unitNum] - newData.units[unitNum]['livery_id'] = unitData.livery_id - newData.units[unitNum]['onboard_num'] = unitData.onboard_num - newData.units[unitNum]['callsign'] = unitData.callsign - newData.units[unitNum]['AddPropAircraft'] = unitData.AddPropAircraft + newData.units[unitNum].livery_id = unitData.livery_id + newData.units[unitNum].onboard_num = unitData.onboard_num + newData.units[unitNum].callsign = unitData.callsign + newData.units[unitNum].AddPropAircraft = unitData.AddPropAircraft end if newData.category == 'static' then - newData.units[unitNum]['categoryStatic'] = unitData.categoryStatic - newData.units[unitNum]['mass'] = unitData.mass - newData.units[unitNum]['canCargo'] = unitData.canCargo - newData.units[unitNum]['shape_name'] = unitData.shape_name + newData.units[unitNum].categoryStatic = unitData.categoryStatic + newData.units[unitNum].mass = unitData.mass + newData.units[unitNum].canCargo = unitData.canCargo + newData.units[unitNum].shape_name = unitData.shape_name end end @@ -2543,7 +2548,7 @@ do -- group functions scope newGroupData = vars.groupData end - local diff = {['x'] = 0, ['y'] = 0} + local diff = {x = 0, y = 0} local newCoord, origCoord if point then local valid = false @@ -2561,7 +2566,7 @@ do -- group functions scope newCoord = mist.getRandPointInCircle(point, radius, innerRadius) if mist.isTerrainValid(newCoord, validTerrain) then origCoord = mist.utils.deepCopy(newCoord) - diff = {['x'] = (newCoord.x - newGroupData.units[1].x), ['y'] = (newCoord.y - newGroupData.units[1].y)} + diff = {x = (newCoord.x - newGroupData.units[1].x), y = (newCoord.y - newGroupData.units[1].y)} valid = true break end @@ -2586,8 +2591,8 @@ do -- group functions scope --newCoord = mist.getRandPointInCircle(zone.point, zone.radius) end - newGroupData.units[unitNum]['x'] = newCoord.x - newGroupData.units[unitNum]['y'] = newCoord.y + newGroupData.units[unitNum].x = newCoord.x + newGroupData.units[unitNum].y = newCoord.y else newGroupData.units[unitNum]["x"] = unitData.x + diff.x newGroupData.units[unitNum]["y"] = unitData.y + diff.y @@ -4560,12 +4565,12 @@ do -- mist.msg scope ------- new display if caSlots == true and caMSGtoGroup == false then - if msgTableText['RED'] then - trigger.action.outTextForCoalition(coalition.side.RED, table.concat(msgTableText['RED'].text), msgTableText['RED'].displayTime, true) + if msgTableText.RED then + trigger.action.outTextForCoalition(coalition.side.RED, table.concat(msgTableText.RED.text), msgTableText.RED.displayTime, true) end - if msgTableText['BLUE'] then - trigger.action.outTextForCoalition(coalition.side.BLUE, table.concat(msgTableText['BLUE'].text), msgTableText['BLUE'].displayTime, true) + if msgTableText.BLUE then + trigger.action.outTextForCoalition(coalition.side.BLUE, table.concat(msgTableText.BLUE.text), msgTableText.BLUE.displayTime, true) end end @@ -4575,11 +4580,11 @@ do -- mist.msg scope end end --- new audio - if msgTableSound['RED'] then - trigger.action.outSoundForCoalition(coalition.side.RED, msgTableSound['RED']) + if msgTableSound.RED then + trigger.action.outSoundForCoalition(coalition.side.RED, msgTableSound.RED) end - if msgTableSound['BLUE'] then - trigger.action.outSoundForCoalition(coalition.side.BLUE, msgTableSound['BLUE']) + if msgTableSound.BLUE then + trigger.action.outSoundForCoalition(coalition.side.BLUE, msgTableSound.BLUE) end @@ -5836,11 +5841,11 @@ do -- group tasks scope if useRoads == true and ((point.x - posStart.x)^2 + (point.z - posStart.z)^2)^0.5 > radius * 1.3 then - path[#path + 1] = mist.ground.buildWP({['x'] = posStart.x + 11, ['z'] = posStart.z + 11}, 'off_road', speed) + path[#path + 1] = mist.ground.buildWP({x = posStart.x + 11, z = posStart.z + 11}, 'off_road', speed) path[#path + 1] = mist.ground.buildWP(posStart, 'on_road', speed) path[#path + 1] = mist.ground.buildWP(offset, 'on_road', speed) else - path[#path + 1] = mist.ground.buildWP({['x'] = posStart.x + 25, ['z'] = posStart.z + 25}, form, speed) + path[#path + 1] = mist.ground.buildWP({x = posStart.x + 25, z = posStart.z + 25}, form, speed) end path[#path + 1] = mist.ground.buildWP(offset, form, speed) @@ -6007,20 +6012,21 @@ do -- mist.DBs scope mist.DBs.missionData = {} if env.mission then - mist.DBs.missionData['startTime'] = env.mission.start_time - mist.DBs.missionData['theatre'] = env.mission.theatre - mist.DBs.missionData['version'] = env.mission.version - mist.DBs.missionData['files'] = {} + mist.DBs.missionData.startTime = env.mission.start_time + mist.DBs.missionData.theatre = env.mission.theatre + mist.DBs.missionData.version = env.mission.version + mist.DBs.missionData.files = {} if type(env.mission.resourceCounter) == 'table' then for fIndex, fData in pairs (env.mission.resourceCounter) do mist.DBs.missionData.files[#mist.DBs.missionData.files + 1] = mist.utils.deepCopy(fIndex) end end - mist.DBs.missionData['bullseye'] = {['red'] = {}, ['blue'] = {}} -- if we add more coalition specific data then bullsye should be categorized by coaliton. For now its just the bullseye table - mist.DBs.missionData.bullseye.red['x'] = env.mission.coalition.red.bullseye.x --should it be point.x? - mist.DBs.missionData.bullseye.red['y'] = env.mission.coalition.red.bullseye.y - mist.DBs.missionData.bullseye.blue['x'] = env.mission.coalition.blue.bullseye.x - mist.DBs.missionData.bullseye.blue['y'] = env.mission.coalition.blue.bullseye.y + -- if we add more coalition specific data then bullsye should be categorized by coaliton. For now its just the bullseye table + mist.DBs.missionData.bullseye = {red = {}, blue = {}} + mist.DBs.missionData.bullseye.red.x = env.mission.coalition.red.bullseye.x --should it be point.x? + mist.DBs.missionData.bullseye.red.y = env.mission.coalition.red.bullseye.y + mist.DBs.missionData.bullseye.blue.x = env.mission.coalition.blue.bullseye.x + mist.DBs.missionData.bullseye.blue.y = env.mission.coalition.blue.bullseye.y end mist.DBs.zonesByName = {} @@ -6031,10 +6037,10 @@ do -- mist.DBs scope for zone_ind, zone_data in pairs(env.mission.triggers.zones) do if type(zone_data) == 'table' then local zone = mist.utils.deepCopy(zone_data) - zone['point'] = {} -- point is used by SSE - zone['point']['x'] = zone_data.x - zone['point']['y'] = 0 - zone['point']['z'] = zone_data.y + zone.point = {} -- point is used by SSE + zone.point.x = zone_data.x + zone.point.y = 0 + zone.point.z = zone_data.y mist.DBs.zonesByName[zone_data.name] = zone mist.DBs.zonesByNum[#mist.DBs.zonesByNum + 1] = mist.utils.deepCopy(zone) --[[deepcopy so that the zone in zones_by_name and the zone in @@ -6060,11 +6066,11 @@ do -- mist.DBs scope if type(nav_data) == 'table' then mist.DBs.navPoints[coa_name][nav_ind] = mist.utils.deepCopy(nav_data) - mist.DBs.navPoints[coa_name][nav_ind]['name'] = nav_data.callsignStr -- name is a little bit more self-explanatory. - mist.DBs.navPoints[coa_name][nav_ind]['point'] = {} -- point is used by SSE, support it. - mist.DBs.navPoints[coa_name][nav_ind]['point']['x'] = nav_data.x - mist.DBs.navPoints[coa_name][nav_ind]['point']['y'] = 0 - mist.DBs.navPoints[coa_name][nav_ind]['point']['z'] = nav_data.y + mist.DBs.navPoints[coa_name][nav_ind].name = nav_data.callsignStr -- name is a little bit more self-explanatory. + mist.DBs.navPoints[coa_name][nav_ind].point = {} -- point is used by SSE, support it. + mist.DBs.navPoints[coa_name][nav_ind].point.x = nav_data.x + mist.DBs.navPoints[coa_name][nav_ind].point.y = 0 + mist.DBs.navPoints[coa_name][nav_ind].point.z = nav_data.y end end end @@ -6142,8 +6148,8 @@ do -- mist.DBs scope units_tbl[unit_num]["point"]["x"] = unit_data.x units_tbl[unit_num]["point"]["y"] = unit_data.y end - units_tbl[unit_num]['x'] = unit_data.x - units_tbl[unit_num]['y'] = unit_data.y + units_tbl[unit_num].x = unit_data.x + units_tbl[unit_num].y = unit_data.y units_tbl[unit_num]["callsign"] = unit_data.callsign units_tbl[unit_num]["onboard_num"] = unit_data.onboard_num @@ -6186,11 +6192,11 @@ do -- mist.DBs scope mist.DBs.unitsById = {} mist.DBs.unitsByCat = {} - mist.DBs.unitsByCat['helicopter'] = {} -- adding default categories - mist.DBs.unitsByCat['plane'] = {} - mist.DBs.unitsByCat['ship'] = {} - mist.DBs.unitsByCat['static'] = {} - mist.DBs.unitsByCat['vehicle'] = {} + mist.DBs.unitsByCat.helicopter = {} -- adding default categories + mist.DBs.unitsByCat.plane = {} + mist.DBs.unitsByCat.ship = {} + mist.DBs.unitsByCat.static = {} + mist.DBs.unitsByCat.vehicle = {} mist.DBs.unitsByNum = {} @@ -6350,43 +6356,43 @@ do if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then log:info('object found in alive_units') - val['objectData'] = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) + val.objectData = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) local pos = Object.getPosition(val.object) if pos then - val['objectPos'] = pos.p + val.objectPos = pos.p end - val['objectType'] = mist.DBs.aliveUnits[val.object.id_].category + val.objectType = mist.DBs.aliveUnits[val.object.id_].category elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units log:info('object found in old_alive_units') - val['objectData'] = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) + val.objectData = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) local pos = Object.getPosition(val.object) if pos then - val['objectPos'] = pos.p + val.objectPos = pos.p end - val['objectType'] = mist.DBs.removedAliveUnits[val.object.id_].category + val.objectType = mist.DBs.removedAliveUnits[val.object.id_].category else --attempt to determine if static object... log:info('object not found in alive units or old alive units') local pos = Object.getPosition(val.object) if pos then local static_found = false - for ind, static in pairs(mist.DBs.unitsByCat['static']) do + for ind, static in pairs(mist.DBs.unitsByCat.static) do if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... log:info('correlated dead static object to position') - val['objectData'] = static - val['objectPos'] = pos.p - val['objectType'] = 'static' + val.objectData = static + val.objectPos = pos.p + val.objectType = 'static' static_found = true break end end if not static_found then - val['objectPos'] = pos.p - val['objectType'] = 'building' + val.objectPos = pos.p + val.objectType = 'building' end else - val['objectType'] = 'unknown' + val.objectType = 'unknown' end end rawset(t, key, val) From 4370fb3c72c4834be5d5715268227b14acebf637 Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Mon, 11 Jan 2016 22:38:52 +0100 Subject: [PATCH 23/42] move classes to the bottom, some more ldoc --- mist.lua | 465 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 281 insertions(+), 184 deletions(-) diff --git a/mist.lua b/mist.lua index 5a8d1e3..794ab67 100644 --- a/mist.lua +++ b/mist.lua @@ -25,7 +25,7 @@ Development Official Releases -@script mist +@script MIST @author Speed @author Grimes @author lukrop @@ -40,9 +40,8 @@ mist.build = 61 -- forward declaration of log shorthand local log ---- Functions --- @section mist do -- the main scope + local coroutines = {} local tempSpawnedUnits = {} -- birth events added here @@ -558,6 +557,7 @@ do -- the main scope --- init function. -- creates logger, adds default event handler -- and calls main the first time. + -- @function mist.init function mist.init() -- create logger mist.log = mist.Logger:new("MIST") @@ -1004,7 +1004,10 @@ do -- the main scope return false end - -- acc- the accuracy of each easting/northing. 0, 1, 2, 3, 4, or 5. + --- Returns MGRS coordinates as string. + -- @tparam string MGRS MGRS coordinates + -- @tparam number acc the accuracy of each easting/northing. + -- Can be: 0, 1, 2, 3, 4, or 5. function mist.tostringMGRS(MGRS, acc) if acc == 0 then return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph @@ -1427,15 +1430,23 @@ do -- the main scope end --[[-- - Returns a table containing unit names. - Prefixes: + Unit name table. + Many Mist functions require tables of unit names, which are known + in Mist as UnitNameTables. These follow a special set of shortcuts + borrowed from Slmod. These shortcuts alleviate the problem of entering + huge lists of unit names by hand, and in many cases, they remove the + need to even know the names of the units in the first place! + + These are the unit table "short-cut" commands: + + Prefixes: "[-u]" - subtract this unit if its in the table "[g]" - add this group to the table "[-g]" - subtract this group from the table "[c]" - add this country's units "[-c]" - subtract this country's units if any are in the table - Stand-alone identifiers + Stand-alone identifiers "[all]" - add all units "[-all]" - subtract all units (not very useful by itself) "[blue]" - add all blue units @@ -1443,7 +1454,7 @@ do -- the main scope "[red]" - add all red coalition units "[-red]" - subtract all red units - Compound Identifiers: + Compound Identifiers: "[c][helicopter]" - add all of this country's helicopters "[-c][helicopter]" - subtract all of this country's helicopters "[c][plane]" - add all of this country's planes @@ -1480,29 +1491,90 @@ do -- the main scope "[red][vehicle]" - add all red coalition vehicles "[-red][vehicle]" - subtract all red coalition vehicles + Country names to be used in [c] and [-c] short-cuts: + Turkey + Norway + The Netherlands + Spain + 11 + UK + Denmark + USA + Georgia + Germany + Belgium + Canada + France + Israel + Ukraine + Russia + South Ossetia + Abkhazia + Italy + Australia + Austria + Belarus + Bulgaria + Czech Republic + China + Croatia + Finland + Greece + Hungary + India + Iran + Iraq + Japan + Kazakhstan + North Korea + Pakistan + Poland + Romania + Saudi Arabia + Serbia, Slovakia + South Korea + Sweden + Switzerland + Syria + USAF Aggressors - Country names to be used in [c] and [-c] short-cuts: - "Turkey" - "Norway" - "The Netherlands" - "Spain" - "UK" - "Denmark" - "USA" - "Georgia" - "Germany" - "Belgium" - "Canada" - "France" - "Israel" - "Ukraine" - "Russia" - "South Osetia" - "Abkhazia" - "Italy" + Do NOT use a '[u]' notation for single units. Single units are referenced + the same way as before: Simply input their names as strings. - @tparam table tbl sequential strings + These unit tables are evaluated in order, and you cannot subtract a unit + from a table before it is added. For example: + + {'[blue]', '[-c]Georgia'} + + will evaluate to all of blue coalition except those units owned by the + country named "Georgia"; however: + + {'[-c]Georgia', '[blue]'} + + will evaluate to all of the units in blue coalition, because the addition + of all units owned by blue coalition occurred AFTER the subtraction of all + units owned by Georgia (which actually subtracted nothing at all, since + there were no units in the table when the subtraction occurred). + + More examples: + + {'[blue][plane]', '[-c]Georgia', '[-g]Hawg 1'} + + Evaluates to all blue planes, except those blue units owned by the country + named "Georgia" and the units in the group named "Hawg1". + + + {'[g]arty1', '[g]arty2', '[-u]arty1_AD', '[-u]arty2_AD', 'Shark 11' } + + Evaluates to the unit named "Shark 11", plus all the units in groups named + "arty1" and "arty2" except those that are named "arty1\_AD" and "arty2\_AD". + + @table UnitNameTable ]] + + --- Returns a table containing unit names. + -- @tparam table tbl sequential strings + -- @treturn table @{UnitNameTable} function mist.makeUnitTable(tbl) --Assumption: will be passed a table of strings, sequential local units_by_name = {} @@ -2905,7 +2977,6 @@ do -- group functions scope return newGroup end - function mist.random(firstNum, secondNum) -- no support for decimals local lowNum, highNum if not secondNum then @@ -2957,7 +3028,6 @@ do -- group functions scope mist.matchString = mist.stringMatch -- both commands work because order out type of I - --[[ scope: { units = {...}, -- unit names. @@ -2980,160 +3050,6 @@ scope examples: ]] end ---- Logger class. --- @type mist.Logger -do -- mist.Logger scope - mist.Logger = {} - - --- Creates a new logger. - -- Each logger has it's own tag and log level. - -- @tparam string tag tag which appears at the start of - -- every log line produced by this logger. - -- @tparam[opt] number|string level the log level defines which messages - -- will be logged and which will be omitted. Log level 3 beeing the most verbose - -- and 0 disabling all output. This can also be a string. Allowed strings are: - -- "none" (0), "error" (1), "warning" (2) and "info" (3). - -- @usage myLogger = mist.Logger:new("MyScript") - -- @usage myLogger = mist.Logger:new("MyScript", 2) - -- @usage myLogger = mist.Logger:new("MyScript", "info") - -- @treturn mist.Logger - function mist.Logger:new(tag, level) - local l = {} - l.tag = tag - setmetatable(l, self) - self.__index = self - self:setLevel(level) - return l - end - - --- Sets the level of verbosity for this logger. - -- @tparam[opt] number|string level the log level defines which messages - -- will be logged and which will be omitted. Log level 3 beeing the most verbose - -- and 0 disabling all output. This can also be a string. Allowed strings are: - -- "none" (0), "error" (1), "warning" (2) and "info" (3). - -- @usage myLogger:setLevel("info") - -- @usage -- log everything - --myLogger:setLevel(3) - function mist.Logger:setLevel(level) - if type(level) == 'string' then - if level == 'none' or level == 'off' then - self.level = 0 - elseif level == 'error' then - self.level = 1 - elseif level == 'warning' then - self.level = 2 - elseif level == 'info' then - self.level = 3 - end - elseif type(level) == 'number' then - self.level = level - end - end - - --- parses text and substitutes keywords with values from given array. - -- @tparam string text string containing keywords to substitute with values - -- @param ... variables to use for substitution - -- @treturn string new string with keywords substituted - local function formatText(text, ...) - for index,value in ipairs(arg) do - -- TODO: check for getmetatabel(value).__tostring - if type(value) == 'table' then - value = mist.utils.oneLineSerialize(value) - else - value = tostring(value) - end - text = text:gsub('$' .. index, value) - end - local dInfo = debug.getinfo(3) - local fName = dInfo.name - -- local fsrc = dinfo.short_src - --local fLine = dInfo.linedefined - local cLine = dInfo.currentline - if fName then - return fName .. '|' .. cLine .. ': ' .. text - else - return cLine .. ': ' .. text - end - end - - local function serializeVar(var) - if type(var) == 'table' then - var = mist.utils.oneLineSerialize(var) - else - var = tostring(var) - end - return var - end - - --- Logs error and shows alert window. - -- This logs an error to the dcs.log and shows a popup window, - -- pausing the simulation. This works always even if logging is - -- disabled by setting a log level of "none" or 0. - -- @tparam string text the text with keywords to substitute. - -- @param ... variables to be used for substitution. - -- @usage myLogger:alert("Shit just hit the fan! WEEEE!!!11") - function mist.Logger:alert(text, ...) - if type(text) ~= 'string' then - text = serializeVar(text) - else - text = formatText(text, unpack(arg)) - end - env.error(self.tag .. '|' .. text, true) - end - - --- Logs an error. - -- logs a message prefixed with this loggers tag to dcs.log as - -- long as at least the "error" log level (1) is set. - -- @tparam string text the text with keywords to substitute. - -- @param ... variables to be used for substitution. - -- @usage myLogger:error("Just an error!") - -- @usage myLogger:error("Foo is $1 instead of $2", foo, "bar") - function mist.Logger:error(text, ...) - if self.level >= 1 then - if type(text) ~= 'string' then - text = serializeVar(text) - else - text = formatText(text, unpack(arg)) - end - env.error(self.tag .. '|' .. text) - end - end - - --- Logs a warning. - -- logs a message prefixed with this loggers tag to dcs.log as - -- long as at least the "warning" log level (2) is set. - -- @tparam string text the text with keywords to substitute. - -- @param ... variables to be used for substitution. - -- @usage myLogger:warn("Mother warned you! Those $1 from the interwebs are $2", {"geeks", 1337}) - function mist.Logger:warn(text, ...) - if self.level >= 2 then - if type(text) ~= 'string' then - text = serializeVar(text) - else - text = formatText(text, unpack(arg)) - end - env.warning(self.tag .. '|' .. text) - end - end - - --- Logs a info. - -- logs a message prefixed with this loggers tag to dcs.log as - -- long as the highest log level (3) "info" is set. - -- @tparam string text the text with keywords to substitute. - -- @param ... variables to be used for substitution. - -- @see warn - function mist.Logger:info(text, ...) - if self.level >= 3 then - if type(text) ~= 'string' then - text = serializeVar(text) - else - text = formatText(text, unpack(arg)) - end - env.info(self.tag .. '|' .. text) - end - end -end - --- Utility functions. -- E.g. conversions between units etc. -- @section mist.utils @@ -3987,6 +3903,10 @@ initial_number end end + + --- Sets a flag if unit(s) is/are inside a polygon. + -- @tparam table vars @{unitsInPolygonVars} + -- @todo document function mist.flagFunc.units_in_polygon(vars) --[[vars needs to be: units = table, @@ -4058,6 +3978,8 @@ unitTableDef = table or nil end + --- Sets a flag if unit(s) is/are inside a trigger zone. + -- @todo document function mist.flagFunc.units_in_zones(vars) --[[vars needs to be: units = table, @@ -4119,6 +4041,8 @@ unitTableDef = table or nil end + --- Sets a flag if unit(s) is/are inside a moving zone. + -- @todo document function mist.flagFunc.units_in_moving_zones(vars) --[[vars needs to be: units = table, @@ -4193,6 +4117,8 @@ unitTableDef = table or nil end + --- Sets a flag if units have line of sight to each other. + -- @todo document function mist.flagFunc.units_LOS(vars) --[[vars needs to be: unitset1 = table, @@ -4270,6 +4196,8 @@ toggle = boolean or nil end end + --- Sets a flag if group is alive. + -- @todo document function mist.flagFunc.group_alive(vars) --[[vars groupName @@ -4315,6 +4243,8 @@ stopFlag end + --- Sets a flag if group is dead. + -- @todo document function mist.flagFunc.group_dead(vars) local type_tbl = { [{'group', 'groupname', 'gp', 'groupName'}] = 'string', @@ -4351,6 +4281,8 @@ stopFlag end end + --- Sets a flag if less than given percent of group is alive. + -- @todo document function mist.flagFunc.group_alive_less_than(vars) local type_tbl = { [{'group', 'groupname', 'gp', 'groupName'}] = 'string', @@ -4395,6 +4327,8 @@ stopFlag end end + --- Sets a flag if more than given percent of group is alive. + -- @todo document function mist.flagFunc.group_alive_more_than(vars) local type_tbl = { [{'group', 'groupname', 'gp', 'groupName'}] = 'string', @@ -6412,6 +6346,169 @@ do -- mist unitID funcs end end +--- Tables used as parameters. +-- @section varTables + +--- mist.flagFunc.units_in_polygon parameter table. +-- @table unitsInPolygonVars +-- @field unit name table @{UnitNameTable}. +-- @field zone table defining a polygon. + + +--- Logger class. +-- @type mist.Logger +do -- mist.Logger scope + mist.Logger = {} + + --- Creates a new logger. + -- Each logger has it's own tag and log level. + -- @tparam string tag tag which appears at the start of + -- every log line produced by this logger. + -- @tparam[opt] number|string level the log level defines which messages + -- will be logged and which will be omitted. Log level 3 beeing the most verbose + -- and 0 disabling all output. This can also be a string. Allowed strings are: + -- "none" (0), "error" (1), "warning" (2) and "info" (3). + -- @usage myLogger = mist.Logger:new("MyScript") + -- @usage myLogger = mist.Logger:new("MyScript", 2) + -- @usage myLogger = mist.Logger:new("MyScript", "info") + -- @treturn mist.Logger + function mist.Logger:new(tag, level) + local l = {} + l.tag = tag + setmetatable(l, self) + self.__index = self + self:setLevel(level) + return l + end + + --- Sets the level of verbosity for this logger. + -- @tparam[opt] number|string level the log level defines which messages + -- will be logged and which will be omitted. Log level 3 beeing the most verbose + -- and 0 disabling all output. This can also be a string. Allowed strings are: + -- "none" (0), "error" (1), "warning" (2) and "info" (3). + -- @usage myLogger:setLevel("info") + -- @usage -- log everything + --myLogger:setLevel(3) + function mist.Logger:setLevel(level) + if type(level) == 'string' then + if level == 'none' or level == 'off' then + self.level = 0 + elseif level == 'error' then + self.level = 1 + elseif level == 'warning' then + self.level = 2 + elseif level == 'info' then + self.level = 3 + end + elseif type(level) == 'number' then + self.level = level + end + end + + --- parses text and substitutes keywords with values from given array. + -- @tparam string text string containing keywords to substitute with values + -- @param ... variables to use for substitution + -- @treturn string new string with keywords substituted + local function formatText(text, ...) + for index,value in ipairs(arg) do + -- TODO: check for getmetatabel(value).__tostring + if type(value) == 'table' then + value = mist.utils.oneLineSerialize(value) + else + value = tostring(value) + end + text = text:gsub('$' .. index, value) + end + local dInfo = debug.getinfo(3) + local fName = dInfo.name + -- local fsrc = dinfo.short_src + --local fLine = dInfo.linedefined + local cLine = dInfo.currentline + if fName then + return fName .. '|' .. cLine .. ': ' .. text + else + return cLine .. ': ' .. text + end + end + + local function serializeVar(var) + if type(var) == 'table' then + var = mist.utils.oneLineSerialize(var) + else + var = tostring(var) + end + return var + end + + --- Logs error and shows alert window. + -- This logs an error to the dcs.log and shows a popup window, + -- pausing the simulation. This works always even if logging is + -- disabled by setting a log level of "none" or 0. + -- @tparam string text the text with keywords to substitute. + -- @param ... variables to be used for substitution. + -- @usage myLogger:alert("Shit just hit the fan! WEEEE!!!11") + function mist.Logger:alert(text, ...) + if type(text) ~= 'string' then + text = serializeVar(text) + else + text = formatText(text, unpack(arg)) + end + env.error(self.tag .. '|' .. text, true) + end + + --- Logs an error. + -- logs a message prefixed with this loggers tag to dcs.log as + -- long as at least the "error" log level (1) is set. + -- @tparam string text the text with keywords to substitute. + -- @param ... variables to be used for substitution. + -- @usage myLogger:error("Just an error!") + -- @usage myLogger:error("Foo is $1 instead of $2", foo, "bar") + function mist.Logger:error(text, ...) + if self.level >= 1 then + if type(text) ~= 'string' then + text = serializeVar(text) + else + text = formatText(text, unpack(arg)) + end + env.error(self.tag .. '|' .. text) + end + end + + --- Logs a warning. + -- logs a message prefixed with this loggers tag to dcs.log as + -- long as at least the "warning" log level (2) is set. + -- @tparam string text the text with keywords to substitute. + -- @param ... variables to be used for substitution. + -- @usage myLogger:warn("Mother warned you! Those $1 from the interwebs are $2", {"geeks", 1337}) + function mist.Logger:warn(text, ...) + if self.level >= 2 then + if type(text) ~= 'string' then + text = serializeVar(text) + else + text = formatText(text, unpack(arg)) + end + env.warning(self.tag .. '|' .. text) + end + end + + --- Logs a info. + -- logs a message prefixed with this loggers tag to dcs.log as + -- long as the highest log level (3) "info" is set. + -- @tparam string text the text with keywords to substitute. + -- @param ... variables to be used for substitution. + -- @see warn + function mist.Logger:info(text, ...) + if self.level >= 3 then + if type(text) ~= 'string' then + text = serializeVar(text) + else + text = formatText(text, unpack(arg)) + end + env.info(self.tag .. '|' .. text) + end + end +end + -- initialize mist mist.init() From f1a3c3e0f20dd9faad9ae8a9444a2b52fd2a7478 Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Mon, 11 Jan 2016 23:48:06 +0100 Subject: [PATCH 24/42] moved db initialization to init and made it local --- mist.lua | 832 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 417 insertions(+), 415 deletions(-) diff --git a/mist.lua b/mist.lua index 794ab67..b25a653 100644 --- a/mist.lua +++ b/mist.lua @@ -41,7 +41,6 @@ mist.build = 61 local log do -- the main scope - local coroutines = {} local tempSpawnedUnits = {} -- birth events added here @@ -65,6 +64,413 @@ do -- the main scope mist.nextGroupId = 1 mist.nextUnitId = 1 + local function initDBs() -- mist.DBs scope + mist.DBs = {} + + mist.DBs.missionData = {} + if env.mission then + + mist.DBs.missionData.startTime = env.mission.start_time + mist.DBs.missionData.theatre = env.mission.theatre + mist.DBs.missionData.version = env.mission.version + mist.DBs.missionData.files = {} + if type(env.mission.resourceCounter) == 'table' then + for fIndex, fData in pairs (env.mission.resourceCounter) do + mist.DBs.missionData.files[#mist.DBs.missionData.files + 1] = mist.utils.deepCopy(fIndex) + end + end + -- if we add more coalition specific data then bullsye should be categorized by coaliton. For now its just the bullseye table + mist.DBs.missionData.bullseye = {red = {}, blue = {}} + mist.DBs.missionData.bullseye.red.x = env.mission.coalition.red.bullseye.x --should it be point.x? + mist.DBs.missionData.bullseye.red.y = env.mission.coalition.red.bullseye.y + mist.DBs.missionData.bullseye.blue.x = env.mission.coalition.blue.bullseye.x + mist.DBs.missionData.bullseye.blue.y = env.mission.coalition.blue.bullseye.y + end + + mist.DBs.zonesByName = {} + mist.DBs.zonesByNum = {} + + + if env.mission.triggers and env.mission.triggers.zones then + for zone_ind, zone_data in pairs(env.mission.triggers.zones) do + if type(zone_data) == 'table' then + local zone = mist.utils.deepCopy(zone_data) + zone.point = {} -- point is used by SSE + zone.point.x = zone_data.x + zone.point.y = 0 + zone.point.z = zone_data.y + + mist.DBs.zonesByName[zone_data.name] = zone + mist.DBs.zonesByNum[#mist.DBs.zonesByNum + 1] = mist.utils.deepCopy(zone) --[[deepcopy so that the zone in zones_by_name and the zone in + zones_by_num se are different objects.. don't want them linked.]] + end + end + end + + mist.DBs.navPoints = {} + mist.DBs.units = {} + --Build mist.db.units and mist.DBs.navPoints + 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 + mist.DBs.units[coa_name] = {} + + -- build nav points DB + mist.DBs.navPoints[coa_name] = {} + if coa_data.nav_points then --navpoints + --mist.debug.writeData (mist.utils.serialize,{'NavPoints',coa_data.nav_points}, 'NavPoints.txt') + for nav_ind, nav_data in pairs(coa_data.nav_points) do + + if type(nav_data) == 'table' then + mist.DBs.navPoints[coa_name][nav_ind] = mist.utils.deepCopy(nav_data) + + mist.DBs.navPoints[coa_name][nav_ind].name = nav_data.callsignStr -- name is a little bit more self-explanatory. + mist.DBs.navPoints[coa_name][nav_ind].point = {} -- point is used by SSE, support it. + mist.DBs.navPoints[coa_name][nav_ind].point.x = nav_data.x + mist.DBs.navPoints[coa_name][nav_ind].point.y = 0 + mist.DBs.navPoints[coa_name][nav_ind].point.z = nav_data.y + end + end + end + if coa_data.country then --there is a country table + for cntry_id, cntry_data in pairs(coa_data.country) do + + local countryName = string.lower(cntry_data.name) + mist.DBs.units[coa_name][countryName] = {} + mist.DBs.units[coa_name][countryName]["countryId"] = cntry_data.id + + if type(cntry_data) == 'table' then --just making sure + + 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" or obj_type_name == "static" then --should be an unncessary check + + local category = obj_type_name + + 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! + + mist.DBs.units[coa_name][countryName][category] = {} + + for group_num, group_data in pairs(obj_type_data.group) do + + if group_data and group_data.units and type(group_data.units) == 'table' then --making sure again- this is a valid group + + mist.DBs.units[coa_name][countryName][category][group_num] = {} + local groupName = group_data.name + if env.mission.version > 7 then + groupName = env.getValueDictByKey(groupName) + end + mist.DBs.units[coa_name][countryName][category][group_num]["groupName"] = groupName + mist.DBs.units[coa_name][countryName][category][group_num]["groupId"] = group_data.groupId + mist.DBs.units[coa_name][countryName][category][group_num]["category"] = category + mist.DBs.units[coa_name][countryName][category][group_num]["coalition"] = coa_name + mist.DBs.units[coa_name][countryName][category][group_num]["country"] = countryName + mist.DBs.units[coa_name][countryName][category][group_num]["countryId"] = cntry_data.id + mist.DBs.units[coa_name][countryName][category][group_num]["startTime"] = group_data.start_time + mist.DBs.units[coa_name][countryName][category][group_num]["task"] = group_data.task + mist.DBs.units[coa_name][countryName][category][group_num]["hidden"] = group_data.hidden + + mist.DBs.units[coa_name][countryName][category][group_num]["units"] = {} + + mist.DBs.units[coa_name][countryName][category][group_num]["radioSet"] = group_data.radioSet + mist.DBs.units[coa_name][countryName][category][group_num]["uncontrolled"] = group_data.uncontrolled + mist.DBs.units[coa_name][countryName][category][group_num]["frequency"] = group_data.frequency + mist.DBs.units[coa_name][countryName][category][group_num]["modulation"] = group_data.modulation + + for unit_num, unit_data in pairs(group_data.units) do + local units_tbl = mist.DBs.units[coa_name][countryName][category][group_num]["units"] --pointer to the units table for this group + + units_tbl[unit_num] = {} + if env.mission.version > 7 then + units_tbl[unit_num]["unitName"] = env.getValueDictByKey(unit_data.name) + else + units_tbl[unit_num]["unitName"] = unit_data.name + end + units_tbl[unit_num]["type"] = unit_data.type + units_tbl[unit_num]["skill"] = unit_data.skill --will be nil for statics + units_tbl[unit_num]["unitId"] = unit_data.unitId + units_tbl[unit_num]["category"] = category + units_tbl[unit_num]["coalition"] = coa_name + units_tbl[unit_num]["country"] = countryName + units_tbl[unit_num]["countryId"] = cntry_data.id + units_tbl[unit_num]["heading"] = unit_data.heading + units_tbl[unit_num]["playerCanDrive"] = unit_data.playerCanDrive + units_tbl[unit_num]["alt"] = unit_data.alt + units_tbl[unit_num]["alt_type"] = unit_data.alt_type + units_tbl[unit_num]["speed"] = unit_data.speed + units_tbl[unit_num]["livery_id"] = unit_data.livery_id + if unit_data.point then --ME currently does not work like this, but it might one day + units_tbl[unit_num]["point"] = unit_data.point + else + units_tbl[unit_num]["point"] = {} + units_tbl[unit_num]["point"]["x"] = unit_data.x + units_tbl[unit_num]["point"]["y"] = unit_data.y + end + units_tbl[unit_num].x = unit_data.x + units_tbl[unit_num].y = unit_data.y + + units_tbl[unit_num]["callsign"] = unit_data.callsign + units_tbl[unit_num]["onboard_num"] = unit_data.onboard_num + units_tbl[unit_num]["hardpoint_racks"] = unit_data.hardpoint_racks + units_tbl[unit_num]["psi"] = unit_data.psi + + + units_tbl[unit_num]["groupName"] = groupName + units_tbl[unit_num]["groupId"] = group_data.groupId + + if unit_data.AddPropAircraft then + units_tbl[unit_num]["AddPropAircraft"] = unit_data.AddPropAircraft + end + + if category == 'static' then + units_tbl[unit_num]["categoryStatic"] = unit_data.category + units_tbl[unit_num]["shape_name"] = unit_data.shape_name + if unit_data.mass then + units_tbl[unit_num]["mass"] = unit_data.mass + end + + if unit_data.canCargo then + units_tbl[unit_num]["canCargo"] = unit_data.canCargo + end + end + + end --for unit_num, unit_data in pairs(group_data.units) do + end --if group_data and group_data.units then + 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 --if type(cntry_data) == 'table' then + 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 + + mist.DBs.unitsByName = {} + mist.DBs.unitsById = {} + mist.DBs.unitsByCat = {} + + mist.DBs.unitsByCat.helicopter = {} -- adding default categories + mist.DBs.unitsByCat.plane = {} + mist.DBs.unitsByCat.ship = {} + mist.DBs.unitsByCat.static = {} + mist.DBs.unitsByCat.vehicle = {} + + mist.DBs.unitsByNum = {} + + mist.DBs.groupsByName = {} + mist.DBs.groupsById = {} + mist.DBs.humansByName = {} + mist.DBs.humansById = {} + + mist.DBs.dynGroupsAdded = {} -- will be filled by mist.dbUpdate from dynamically spawned groups + mist.DBs.activeHumans = {} + + mist.DBs.aliveUnits = {} -- will be filled in by the "updateAliveUnits" coroutine in mist.main. + + mist.DBs.removedAliveUnits = {} -- will be filled in by the "updateAliveUnits" coroutine in mist.main. + + mist.DBs.const = {} + + -- not accessible by SSE, must use static list :-/ + mist.DBs.const.callsigns = { + ['NATO'] = { + ['rules'] = { + ['groupLimit'] = 9, + }, + ['AWACS'] = { + ['Overlord'] = 1, + ['Magic'] = 2, + ['Wizard'] = 3, + ['Focus'] = 4, + ['Darkstar'] = 5, + }, + ['TANKER'] = { + ['Texaco'] = 1, + ['Arco'] = 2, + ['Shell'] = 3, + }, + ['JTAC'] = { + ['Axeman'] = 1, + ['Darknight'] = 2, + ['Warrior'] = 3, + ['Pointer'] = 4, + ['Eyeball'] = 5, + ['Moonbeam'] = 6, + ['Whiplash'] = 7, + ['Finger'] = 8, + ['Pinpoint'] = 9, + ['Ferret'] = 10, + ['Shaba'] = 11, + ['Playboy'] = 12, + ['Hammer'] = 13, + ['Jaguar'] = 14, + ['Deathstar'] = 15, + ['Anvil'] = 16, + ['Firefly'] = 17, + ['Mantis'] = 18, + ['Badger'] = 19, + }, + ['aircraft'] = { + ['Enfield'] = 1, + ['Springfield'] = 2, + ['Uzi'] = 3, + ['Colt'] = 4, + ['Dodge'] = 5, + ['Ford'] = 6, + ['Chevy'] = 7, + ['Pontiac'] = 8, + }, + + ['unique'] = { + ['A10'] = { + ['Hawg'] = 9, + ['Boar'] = 10, + ['Pig'] = 11, + ['Tusk'] = 12, + ['rules'] = { + ['canUseAircraft'] = true, + ['appliesTo'] = { + 'A-10C', + 'A-10A', + }, + }, + }, + }, + }, + } + -- create mist.DBs.oldAliveUnits + -- do + -- local intermediate_alive_units = {} -- between 0 and 0.5 secs old + -- local function make_old_alive_units() -- called every 0.5 secs, makes the old_alive_units DB which is just a copy of alive_units that is 0.5 to 1 sec old + -- if intermediate_alive_units then + -- mist.DBs.oldAliveUnits = mist.utils.deepCopy(intermediate_alive_units) + -- end + -- intermediate_alive_units = mist.utils.deepCopy(mist.DBs.aliveUnits) + -- timer.scheduleFunction(make_old_alive_units, nil, timer.getTime() + 0.5) + -- end + + -- make_old_alive_units() + -- end + + --Build DBs + for coa_name, coa_data in pairs(mist.DBs.units) do + for cntry_name, cntry_data in pairs(coa_data) do + for category_name, category_data in pairs(cntry_data) do + if type(category_data) == 'table' then + for group_ind, group_data in pairs(category_data) do + if type(group_data) == 'table' and group_data.units and type(group_data.units) == 'table' and #group_data.units > 0 then -- OCD paradigm programming + mist.DBs.groupsByName[group_data.groupName] = mist.utils.deepCopy(group_data) + mist.DBs.groupsById[group_data.groupId] = mist.utils.deepCopy(group_data) + for unit_ind, unit_data in pairs(group_data.units) do + mist.DBs.unitsByName[unit_data.unitName] = mist.utils.deepCopy(unit_data) + mist.DBs.unitsById[unit_data.unitId] = mist.utils.deepCopy(unit_data) + + mist.DBs.unitsByCat[unit_data.category] = mist.DBs.unitsByCat[unit_data.category] or {} -- future-proofing against new categories... + table.insert(mist.DBs.unitsByCat[unit_data.category], mist.utils.deepCopy(unit_data)) + log:info('inserting $1', unit_data.unitName) + table.insert(mist.DBs.unitsByNum, mist.utils.deepCopy(unit_data)) + + if unit_data.skill and (unit_data.skill == "Client" or unit_data.skill == "Player") then + mist.DBs.humansByName[unit_data.unitName] = mist.utils.deepCopy(unit_data) + mist.DBs.humansById[unit_data.unitId] = mist.utils.deepCopy(unit_data) + --if Unit.getByName(unit_data.unitName) then + -- mist.DBs.activeHumans[unit_data.unitName] = mist.utils.deepCopy(unit_data) + -- mist.DBs.activeHumans[unit_data.unitName].playerName = Unit.getByName(unit_data.unitName):getPlayerName() + --end + end + end + end + end + end + end + end + end + + --DynDBs + mist.DBs.MEunits = mist.utils.deepCopy(mist.DBs.units) + mist.DBs.MEunitsByName = mist.utils.deepCopy(mist.DBs.unitsByName) + mist.DBs.MEunitsById = mist.utils.deepCopy(mist.DBs.unitsById) + mist.DBs.MEunitsByCat = mist.utils.deepCopy(mist.DBs.unitsByCat) + mist.DBs.MEunitsByNum = mist.utils.deepCopy(mist.DBs.unitsByNum) + mist.DBs.MEgroupsByName = mist.utils.deepCopy(mist.DBs.groupsByName) + mist.DBs.MEgroupsById = mist.utils.deepCopy(mist.DBs.groupsById) + + mist.DBs.deadObjects = {} + + do + local mt = {} + + function mt.__newindex(t, key, val) + local original_key = key --only for duplicate runtime IDs. + local key_ind = 1 + while mist.DBs.deadObjects[key] do + log:warn('duplicate runtime id of previously dead object key: $1', key) + key = tostring(original_key) .. ' #' .. tostring(key_ind) + key_ind = key_ind + 1 + end + + if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then + log:info('object found in alive_units') + val.objectData = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) + local pos = Object.getPosition(val.object) + if pos then + val.objectPos = pos.p + end + val.objectType = mist.DBs.aliveUnits[val.object.id_].category + + elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units + log:info('object found in old_alive_units') + val.objectData = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) + local pos = Object.getPosition(val.object) + if pos then + val.objectPos = pos.p + end + val.objectType = mist.DBs.removedAliveUnits[val.object.id_].category + + else --attempt to determine if static object... + log:info('object not found in alive units or old alive units') + local pos = Object.getPosition(val.object) + if pos then + local static_found = false + for ind, static in pairs(mist.DBs.unitsByCat.static) do + if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... + log:info('correlated dead static object to position') + val.objectData = static + val.objectPos = pos.p + val.objectType = 'static' + static_found = true + break + end + end + if not static_found then + val.objectPos = pos.p + val.objectType = 'building' + end + else + val.objectType = 'unknown' + end + end + rawset(t, key, val) + end + + setmetatable(mist.DBs.deadObjects, mt) + end + + do -- mist unitID funcs + for id, idData in pairs(mist.DBs.unitsById) do + if idData.unitId > mist.nextUnitId then + mist.nextUnitId = mist.utils.deepCopy(idData.unitId) + end + if idData.groupId > mist.nextGroupId then + mist.nextGroupId = mist.utils.deepCopy(idData.groupId) + end + end + end + + + end + local function updateAliveUnits() -- coroutine function local lalive_units = mist.DBs.aliveUnits -- local references for faster execution local lunits = mist.DBs.unitsByNum @@ -83,7 +489,7 @@ do -- the main scope if lunits[i].category ~= 'static' then -- can't get statics with Unit.getByName :( local unit = lUnit.getByName(lunits[i].unitName) if unit then - log:info("unit named $1 alive!", lunits[i].unitName) + --log:info("unit named $1 alive!", lunits[i].unitName) -- spammy local pos = unit:getPosition() local newtbl = ldeepcopy(lunits[i]) if pos then @@ -96,9 +502,7 @@ do -- the main scope end end if i%units_per_run == 0 then - log:info("yielding at: $1", i) coroutine.yield() - log:info("resuming at: $1", i) end end -- All units updated, remove any "alive" units that were not updated- they are dead! @@ -565,6 +969,9 @@ do -- the main scope -- set info log level log:setLevel("info") + log:info("initializing databases") + initDBs() + -- add event handler for group spawns mist.addEventHandler(groupSpawned) mist.addEventHandler(addDeadObject) @@ -5934,417 +6341,13 @@ end --- Database tables. -- @section mist.DBs -do -- mist.DBs scope - mist.DBs = {} - --- Mission data - -- @table mist.DBs.missionData - -- @field startTime mission start time - -- @field theatre mission theatre/map e.g. Caucasus - -- @field version mission version - -- @field files mission resources - mist.DBs.missionData = {} - if env.mission then - - mist.DBs.missionData.startTime = env.mission.start_time - mist.DBs.missionData.theatre = env.mission.theatre - mist.DBs.missionData.version = env.mission.version - mist.DBs.missionData.files = {} - if type(env.mission.resourceCounter) == 'table' then - for fIndex, fData in pairs (env.mission.resourceCounter) do - mist.DBs.missionData.files[#mist.DBs.missionData.files + 1] = mist.utils.deepCopy(fIndex) - end - end - -- if we add more coalition specific data then bullsye should be categorized by coaliton. For now its just the bullseye table - mist.DBs.missionData.bullseye = {red = {}, blue = {}} - mist.DBs.missionData.bullseye.red.x = env.mission.coalition.red.bullseye.x --should it be point.x? - mist.DBs.missionData.bullseye.red.y = env.mission.coalition.red.bullseye.y - mist.DBs.missionData.bullseye.blue.x = env.mission.coalition.blue.bullseye.x - mist.DBs.missionData.bullseye.blue.y = env.mission.coalition.blue.bullseye.y - end - - mist.DBs.zonesByName = {} - mist.DBs.zonesByNum = {} - - - if env.mission.triggers and env.mission.triggers.zones then - for zone_ind, zone_data in pairs(env.mission.triggers.zones) do - if type(zone_data) == 'table' then - local zone = mist.utils.deepCopy(zone_data) - zone.point = {} -- point is used by SSE - zone.point.x = zone_data.x - zone.point.y = 0 - zone.point.z = zone_data.y - - mist.DBs.zonesByName[zone_data.name] = zone - mist.DBs.zonesByNum[#mist.DBs.zonesByNum + 1] = mist.utils.deepCopy(zone) --[[deepcopy so that the zone in zones_by_name and the zone in - zones_by_num se are different objects.. don't want them linked.]] - end - end - end - - mist.DBs.navPoints = {} - mist.DBs.units = {} - --Build mist.db.units and mist.DBs.navPoints - 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 - mist.DBs.units[coa_name] = {} - - -- build nav points DB - mist.DBs.navPoints[coa_name] = {} - if coa_data.nav_points then --navpoints - --mist.debug.writeData (mist.utils.serialize,{'NavPoints',coa_data.nav_points}, 'NavPoints.txt') - for nav_ind, nav_data in pairs(coa_data.nav_points) do - - if type(nav_data) == 'table' then - mist.DBs.navPoints[coa_name][nav_ind] = mist.utils.deepCopy(nav_data) - - mist.DBs.navPoints[coa_name][nav_ind].name = nav_data.callsignStr -- name is a little bit more self-explanatory. - mist.DBs.navPoints[coa_name][nav_ind].point = {} -- point is used by SSE, support it. - mist.DBs.navPoints[coa_name][nav_ind].point.x = nav_data.x - mist.DBs.navPoints[coa_name][nav_ind].point.y = 0 - mist.DBs.navPoints[coa_name][nav_ind].point.z = nav_data.y - end - end - end - if coa_data.country then --there is a country table - for cntry_id, cntry_data in pairs(coa_data.country) do - - local countryName = string.lower(cntry_data.name) - mist.DBs.units[coa_name][countryName] = {} - mist.DBs.units[coa_name][countryName]["countryId"] = cntry_data.id - - if type(cntry_data) == 'table' then --just making sure - - 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" or obj_type_name == "static" then --should be an unncessary check - - local category = obj_type_name - - 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! - - mist.DBs.units[coa_name][countryName][category] = {} - - for group_num, group_data in pairs(obj_type_data.group) do - - if group_data and group_data.units and type(group_data.units) == 'table' then --making sure again- this is a valid group - - mist.DBs.units[coa_name][countryName][category][group_num] = {} - local groupName = group_data.name - if env.mission.version > 7 then - groupName = env.getValueDictByKey(groupName) - end - mist.DBs.units[coa_name][countryName][category][group_num]["groupName"] = groupName - mist.DBs.units[coa_name][countryName][category][group_num]["groupId"] = group_data.groupId - mist.DBs.units[coa_name][countryName][category][group_num]["category"] = category - mist.DBs.units[coa_name][countryName][category][group_num]["coalition"] = coa_name - mist.DBs.units[coa_name][countryName][category][group_num]["country"] = countryName - mist.DBs.units[coa_name][countryName][category][group_num]["countryId"] = cntry_data.id - mist.DBs.units[coa_name][countryName][category][group_num]["startTime"] = group_data.start_time - mist.DBs.units[coa_name][countryName][category][group_num]["task"] = group_data.task - mist.DBs.units[coa_name][countryName][category][group_num]["hidden"] = group_data.hidden - - mist.DBs.units[coa_name][countryName][category][group_num]["units"] = {} - - mist.DBs.units[coa_name][countryName][category][group_num]["radioSet"] = group_data.radioSet - mist.DBs.units[coa_name][countryName][category][group_num]["uncontrolled"] = group_data.uncontrolled - mist.DBs.units[coa_name][countryName][category][group_num]["frequency"] = group_data.frequency - mist.DBs.units[coa_name][countryName][category][group_num]["modulation"] = group_data.modulation - - for unit_num, unit_data in pairs(group_data.units) do - local units_tbl = mist.DBs.units[coa_name][countryName][category][group_num]["units"] --pointer to the units table for this group - - units_tbl[unit_num] = {} - if env.mission.version > 7 then - units_tbl[unit_num]["unitName"] = env.getValueDictByKey(unit_data.name) - else - units_tbl[unit_num]["unitName"] = unit_data.name - end - units_tbl[unit_num]["type"] = unit_data.type - units_tbl[unit_num]["skill"] = unit_data.skill --will be nil for statics - units_tbl[unit_num]["unitId"] = unit_data.unitId - units_tbl[unit_num]["category"] = category - units_tbl[unit_num]["coalition"] = coa_name - units_tbl[unit_num]["country"] = countryName - units_tbl[unit_num]["countryId"] = cntry_data.id - units_tbl[unit_num]["heading"] = unit_data.heading - units_tbl[unit_num]["playerCanDrive"] = unit_data.playerCanDrive - units_tbl[unit_num]["alt"] = unit_data.alt - units_tbl[unit_num]["alt_type"] = unit_data.alt_type - units_tbl[unit_num]["speed"] = unit_data.speed - units_tbl[unit_num]["livery_id"] = unit_data.livery_id - if unit_data.point then --ME currently does not work like this, but it might one day - units_tbl[unit_num]["point"] = unit_data.point - else - units_tbl[unit_num]["point"] = {} - units_tbl[unit_num]["point"]["x"] = unit_data.x - units_tbl[unit_num]["point"]["y"] = unit_data.y - end - units_tbl[unit_num].x = unit_data.x - units_tbl[unit_num].y = unit_data.y - - units_tbl[unit_num]["callsign"] = unit_data.callsign - units_tbl[unit_num]["onboard_num"] = unit_data.onboard_num - units_tbl[unit_num]["hardpoint_racks"] = unit_data.hardpoint_racks - units_tbl[unit_num]["psi"] = unit_data.psi - - - units_tbl[unit_num]["groupName"] = groupName - units_tbl[unit_num]["groupId"] = group_data.groupId - - if unit_data.AddPropAircraft then - units_tbl[unit_num]["AddPropAircraft"] = unit_data.AddPropAircraft - end - - if category == 'static' then - units_tbl[unit_num]["categoryStatic"] = unit_data.category - units_tbl[unit_num]["shape_name"] = unit_data.shape_name - if unit_data.mass then - units_tbl[unit_num]["mass"] = unit_data.mass - end - - if unit_data.canCargo then - units_tbl[unit_num]["canCargo"] = unit_data.canCargo - end - end - - end --for unit_num, unit_data in pairs(group_data.units) do - end --if group_data and group_data.units then - 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 --if type(cntry_data) == 'table' then - 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 - - mist.DBs.unitsByName = {} - mist.DBs.unitsById = {} - mist.DBs.unitsByCat = {} - - mist.DBs.unitsByCat.helicopter = {} -- adding default categories - mist.DBs.unitsByCat.plane = {} - mist.DBs.unitsByCat.ship = {} - mist.DBs.unitsByCat.static = {} - mist.DBs.unitsByCat.vehicle = {} - - mist.DBs.unitsByNum = {} - - mist.DBs.groupsByName = {} - mist.DBs.groupsById = {} - mist.DBs.humansByName = {} - mist.DBs.humansById = {} - - mist.DBs.dynGroupsAdded = {} -- will be filled by mist.dbUpdate from dynamically spawned groups - mist.DBs.activeHumans = {} - - mist.DBs.aliveUnits = {} -- will be filled in by the "updateAliveUnits" coroutine in mist.main. - - mist.DBs.removedAliveUnits = {} -- will be filled in by the "updateAliveUnits" coroutine in mist.main. - - mist.DBs.const = {} - - -- not accessible by SSE, must use static list :-/ - mist.DBs.const.callsigns = { - ['NATO'] = { - ['rules'] = { - ['groupLimit'] = 9, - }, - ['AWACS'] = { - ['Overlord'] = 1, - ['Magic'] = 2, - ['Wizard'] = 3, - ['Focus'] = 4, - ['Darkstar'] = 5, - }, - ['TANKER'] = { - ['Texaco'] = 1, - ['Arco'] = 2, - ['Shell'] = 3, - }, - ['JTAC'] = { - ['Axeman'] = 1, - ['Darknight'] = 2, - ['Warrior'] = 3, - ['Pointer'] = 4, - ['Eyeball'] = 5, - ['Moonbeam'] = 6, - ['Whiplash'] = 7, - ['Finger'] = 8, - ['Pinpoint'] = 9, - ['Ferret'] = 10, - ['Shaba'] = 11, - ['Playboy'] = 12, - ['Hammer'] = 13, - ['Jaguar'] = 14, - ['Deathstar'] = 15, - ['Anvil'] = 16, - ['Firefly'] = 17, - ['Mantis'] = 18, - ['Badger'] = 19, - }, - ['aircraft'] = { - ['Enfield'] = 1, - ['Springfield'] = 2, - ['Uzi'] = 3, - ['Colt'] = 4, - ['Dodge'] = 5, - ['Ford'] = 6, - ['Chevy'] = 7, - ['Pontiac'] = 8, - }, - - ['unique'] = { - ['A10'] = { - ['Hawg'] = 9, - ['Boar'] = 10, - ['Pig'] = 11, - ['Tusk'] = 12, - ['rules'] = { - ['canUseAircraft'] = true, - ['appliesTo'] = { - 'A-10C', - 'A-10A', - }, - }, - }, - }, - }, - } - -- create mist.DBs.oldAliveUnits - -- do - -- local intermediate_alive_units = {} -- between 0 and 0.5 secs old - -- local function make_old_alive_units() -- called every 0.5 secs, makes the old_alive_units DB which is just a copy of alive_units that is 0.5 to 1 sec old - -- if intermediate_alive_units then - -- mist.DBs.oldAliveUnits = mist.utils.deepCopy(intermediate_alive_units) - -- end - -- intermediate_alive_units = mist.utils.deepCopy(mist.DBs.aliveUnits) - -- timer.scheduleFunction(make_old_alive_units, nil, timer.getTime() + 0.5) - -- end - - -- make_old_alive_units() - -- end - - --Build DBs - for coa_name, coa_data in pairs(mist.DBs.units) do - for cntry_name, cntry_data in pairs(coa_data) do - for category_name, category_data in pairs(cntry_data) do - if type(category_data) == 'table' then - for group_ind, group_data in pairs(category_data) do - if type(group_data) == 'table' and group_data.units and type(group_data.units) == 'table' and #group_data.units > 0 then -- OCD paradigm programming - mist.DBs.groupsByName[group_data.groupName] = mist.utils.deepCopy(group_data) - mist.DBs.groupsById[group_data.groupId] = mist.utils.deepCopy(group_data) - for unit_ind, unit_data in pairs(group_data.units) do - mist.DBs.unitsByName[unit_data.unitName] = mist.utils.deepCopy(unit_data) - mist.DBs.unitsById[unit_data.unitId] = mist.utils.deepCopy(unit_data) - - mist.DBs.unitsByCat[unit_data.category] = mist.DBs.unitsByCat[unit_data.category] or {} -- future-proofing against new categories... - table.insert(mist.DBs.unitsByCat[unit_data.category], mist.utils.deepCopy(unit_data)) - log:info('inserting $1', unit_data.unitName) - table.insert(mist.DBs.unitsByNum, mist.utils.deepCopy(unit_data)) - - if unit_data.skill and (unit_data.skill == "Client" or unit_data.skill == "Player") then - mist.DBs.humansByName[unit_data.unitName] = mist.utils.deepCopy(unit_data) - mist.DBs.humansById[unit_data.unitId] = mist.utils.deepCopy(unit_data) - --if Unit.getByName(unit_data.unitName) then - -- mist.DBs.activeHumans[unit_data.unitName] = mist.utils.deepCopy(unit_data) - -- mist.DBs.activeHumans[unit_data.unitName].playerName = Unit.getByName(unit_data.unitName):getPlayerName() - --end - end - end - end - end - end - end - end - end - - --DynDBs - mist.DBs.MEunits = mist.utils.deepCopy(mist.DBs.units) - mist.DBs.MEunitsByName = mist.utils.deepCopy(mist.DBs.unitsByName) - mist.DBs.MEunitsById = mist.utils.deepCopy(mist.DBs.unitsById) - mist.DBs.MEunitsByCat = mist.utils.deepCopy(mist.DBs.unitsByCat) - mist.DBs.MEunitsByNum = mist.utils.deepCopy(mist.DBs.unitsByNum) - mist.DBs.MEgroupsByName = mist.utils.deepCopy(mist.DBs.groupsByName) - mist.DBs.MEgroupsById = mist.utils.deepCopy(mist.DBs.groupsById) - - mist.DBs.deadObjects = {} - -end - -do - local mt = {} - - function mt.__newindex(t, key, val) - local original_key = key --only for duplicate runtime IDs. - local key_ind = 1 - while mist.DBs.deadObjects[key] do - log:warn('duplicate runtime id of previously dead object key: $1', key) - key = tostring(original_key) .. ' #' .. tostring(key_ind) - key_ind = key_ind + 1 - end - - if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then - log:info('object found in alive_units') - val.objectData = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) - local pos = Object.getPosition(val.object) - if pos then - val.objectPos = pos.p - end - val.objectType = mist.DBs.aliveUnits[val.object.id_].category - - elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units - log:info('object found in old_alive_units') - val.objectData = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) - local pos = Object.getPosition(val.object) - if pos then - val.objectPos = pos.p - end - val.objectType = mist.DBs.removedAliveUnits[val.object.id_].category - - else --attempt to determine if static object... - log:info('object not found in alive units or old alive units') - local pos = Object.getPosition(val.object) - if pos then - local static_found = false - for ind, static in pairs(mist.DBs.unitsByCat.static) do - if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... - log:info('correlated dead static object to position') - val.objectData = static - val.objectPos = pos.p - val.objectType = 'static' - static_found = true - break - end - end - if not static_found then - val.objectPos = pos.p - val.objectType = 'building' - end - else - val.objectType = 'unknown' - end - end - rawset(t, key, val) - end - - setmetatable(mist.DBs.deadObjects, mt) -end - -do -- mist unitID funcs - for id, idData in pairs(mist.DBs.unitsById) do - if idData.unitId > mist.nextUnitId then - mist.nextUnitId = mist.utils.deepCopy(idData.unitId) - end - if idData.groupId > mist.nextGroupId then - mist.nextGroupId = mist.utils.deepCopy(idData.groupId) - end - end -end +--- Mission data +-- @table mist.DBs.missionData +-- @field startTime mission start time +-- @field theatre mission theatre/map e.g. Caucasus +-- @field version mission version +-- @field files mission resources --- Tables used as parameters. -- @section varTables @@ -6354,7 +6357,6 @@ end -- @field unit name table @{UnitNameTable}. -- @field zone table defining a polygon. - --- Logger class. -- @type mist.Logger do -- mist.Logger scope From 42e67cf48125d1ceb1d9df66bbd85202b1473b30 Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Tue, 12 Jan 2016 14:59:33 +0100 Subject: [PATCH 25/42] logger now allows log messages >= 4096 chars when displaying big tables they often got cut off before their end. This is avoided by splitting log messages at the 4000th char. --- mist.lua | 143 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 92 insertions(+), 51 deletions(-) diff --git a/mist.lua b/mist.lua index b25a653..4d2f00a 100644 --- a/mist.lua +++ b/mist.lua @@ -6362,6 +6362,53 @@ end do -- mist.Logger scope mist.Logger = {} + --- parses text and substitutes keywords with values from given array. + -- @param text string containing keywords to substitute with values + -- or a variable. + -- @param ... variables to use for substitution in string. + -- @treturn string new string with keywords substituted or + -- value of variable as string. + local function formatText(text, ...) + if type(text) ~= 'string' then + if type(text) == 'table' then + text = mist.utils.oneLineSerialize(text) + else + text = tostring(text) + end + else + for index,value in ipairs(arg) do + -- TODO: check for getmetatabel(value).__tostring + if type(value) == 'table' then + value = mist.utils.oneLineSerialize(value) + else + value = tostring(value) + end + text = text:gsub('$' .. index, value) + end + end + local dInfo = debug.getinfo(3) + local fName = dInfo.name + -- local fsrc = dinfo.short_src + --local fLine = dInfo.linedefined + local cLine = dInfo.currentline + if fName then + return fName .. '|' .. cLine .. ': ' .. text + else + return cLine .. ': ' .. text + end + end + + local function splitText(text) + local tbl = {} + while text:len() > 4000 do + local sub = text:sub(1, 4000) + text = text:sub(4001) + table.insert(tbl, sub) + end + table.insert(tbl, text) + return tbl + end + --- Creates a new logger. -- Each logger has it's own tag and log level. -- @tparam string tag tag which appears at the start of @@ -6407,41 +6454,6 @@ do -- mist.Logger scope end end - --- parses text and substitutes keywords with values from given array. - -- @tparam string text string containing keywords to substitute with values - -- @param ... variables to use for substitution - -- @treturn string new string with keywords substituted - local function formatText(text, ...) - for index,value in ipairs(arg) do - -- TODO: check for getmetatabel(value).__tostring - if type(value) == 'table' then - value = mist.utils.oneLineSerialize(value) - else - value = tostring(value) - end - text = text:gsub('$' .. index, value) - end - local dInfo = debug.getinfo(3) - local fName = dInfo.name - -- local fsrc = dinfo.short_src - --local fLine = dInfo.linedefined - local cLine = dInfo.currentline - if fName then - return fName .. '|' .. cLine .. ': ' .. text - else - return cLine .. ': ' .. text - end - end - - local function serializeVar(var) - if type(var) == 'table' then - var = mist.utils.oneLineSerialize(var) - else - var = tostring(var) - end - return var - end - --- Logs error and shows alert window. -- This logs an error to the dcs.log and shows a popup window, -- pausing the simulation. This works always even if logging is @@ -6450,12 +6462,19 @@ do -- mist.Logger scope -- @param ... variables to be used for substitution. -- @usage myLogger:alert("Shit just hit the fan! WEEEE!!!11") function mist.Logger:alert(text, ...) - if type(text) ~= 'string' then - text = serializeVar(text) + text = formatText(text, unpack(arg)) + if text:len() > 4000 then + local texts = splitText(text) + for i = 1, #texts do + if i == 1 then + env.error(self.tag .. '|' .. texts[i], true) + else + env.error(texts[i]) + end + end else - text = formatText(text, unpack(arg)) + env.error(self.tag .. '|' .. text, true) end - env.error(self.tag .. '|' .. text, true) end --- Logs an error. @@ -6467,12 +6486,19 @@ do -- mist.Logger scope -- @usage myLogger:error("Foo is $1 instead of $2", foo, "bar") function mist.Logger:error(text, ...) if self.level >= 1 then - if type(text) ~= 'string' then - text = serializeVar(text) + text = formatText(text, unpack(arg)) + if text:len() > 4000 then + local texts = splitText(text) + for i = 1, #texts do + if i == 1 then + env.error(self.tag .. '|' .. texts[i]) + else + env.error(texts[i]) + end + end else - text = formatText(text, unpack(arg)) + env.error(self.tag .. '|' .. text) end - env.error(self.tag .. '|' .. text) end end @@ -6484,12 +6510,19 @@ do -- mist.Logger scope -- @usage myLogger:warn("Mother warned you! Those $1 from the interwebs are $2", {"geeks", 1337}) function mist.Logger:warn(text, ...) if self.level >= 2 then - if type(text) ~= 'string' then - text = serializeVar(text) + text = formatText(text, unpack(arg)) + if text:len() > 4000 then + local texts = splitText(text) + for i = 1, #texts do + if i == 1 then + env.warning(self.tag .. '|' .. texts[i]) + else + env.warning(texts[i]) + end + end else - text = formatText(text, unpack(arg)) + env.warning(self.tag .. '|' .. text) end - env.warning(self.tag .. '|' .. text) end end @@ -6501,14 +6534,22 @@ do -- mist.Logger scope -- @see warn function mist.Logger:info(text, ...) if self.level >= 3 then - if type(text) ~= 'string' then - text = serializeVar(text) + text = formatText(text, unpack(arg)) + if text:len() > 4000 then + local texts = splitText(text) + for i = 1, #texts do + if i == 1 then + env.info(self.tag .. '|' .. texts[i]) + else + env.info(texts[i]) + end + end else - text = formatText(text, unpack(arg)) + env.info(self.tag .. '|' .. text) end - env.info(self.tag .. '|' .. text) end end + end -- initialize mist From 864e89d43b8da3d8bbd6e3670ce59c1d36585b6c Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Tue, 12 Jan 2016 20:23:36 +0100 Subject: [PATCH 26/42] fixed groupTableCheck and added some ldoc --- mist.lua | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/mist.lua b/mist.lua index 4d2f00a..ee5365d 100644 --- a/mist.lua +++ b/mist.lua @@ -2763,27 +2763,24 @@ do -- group functions scope -- @treturn boolean true if a group can be spawned using -- this table, false otherwise. function mist.groupTableCheck(groupData) - local isOk = false - - if groupData.country then - isOk = true + -- return false if country, category + -- or units are missing + if not groupData.country and + not groupData.category and + not groupData.units then + return false end - if groupData.category then - isOk = true - else - isOk = false - end - if groupData.units then - for unitId, unitData in pairs(groupData.units) do - if unitData.x and unitData.y and unitData.type then - isOk = true - end + -- return false if unitData misses + -- x, y or type + for unitId, unitData in pairs(groupData.units) do + if not unitData.x and + not unitData.y and + not unitData.type then + return false end - else - isOk = false end - - return isOk + -- everything we need is here return true + return true end function mist.getCurrentGroupData(gpName) @@ -5733,6 +5730,13 @@ do -- group tasks scope mist.air.fixedWing = {} mist.air.heli = {} + --- Tasks group to follow a route. + -- This sets the mission task for the given group. + -- Any wrapped actions inside the path (like enroute + -- tasks) will be executed. + -- @tparam Group group group to task. + -- @tparam table path containing + -- points defining a route. function mist.goRoute(group, path) local misTask = { id = 'Mission', @@ -5745,15 +5749,13 @@ do -- group tasks scope if type(group) == 'string' then group = Group.getByName(group) end - local groupCon = nil if group then - groupCon = group:getController() + local groupCon = group:getController() if groupCon then groupCon:setTask(misTask) return true end end - --Controller.setTask(groupCon, misTask) return false end From 12f2a39465eb2f6e6c83c9eb62c55e03f0e9b686 Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Wed, 13 Jan 2016 00:36:04 +0100 Subject: [PATCH 27/42] actually fix groupTableCheck --- mist.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mist.lua b/mist.lua index ee5365d..a7dda77 100644 --- a/mist.lua +++ b/mist.lua @@ -2765,16 +2765,16 @@ do -- group functions scope function mist.groupTableCheck(groupData) -- return false if country, category -- or units are missing - if not groupData.country and - not groupData.category and + if not groupData.country or + not groupData.category or not groupData.units then return false end -- return false if unitData misses -- x, y or type for unitId, unitData in pairs(groupData.units) do - if not unitData.x and - not unitData.y and + if not unitData.x or + not unitData.y or not unitData.type then return false end From 74b1e9fcc09d75c9e885730f8565d01aad4cf9c3 Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Wed, 13 Jan 2016 11:28:08 +0100 Subject: [PATCH 28/42] Some more ldoc --- mist.lua | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/mist.lua b/mist.lua index a7dda77..00929e4 100644 --- a/mist.lua +++ b/mist.lua @@ -2783,6 +2783,7 @@ do -- group functions scope return true end + --- Returns group data table of give group. function mist.getCurrentGroupData(gpName) local dbData = mist.getGroupData(gpName) @@ -4307,10 +4308,16 @@ initial_number end end - --- Sets a flag if unit(s) is/are inside a polygon. -- @tparam table vars @{unitsInPolygonVars} - -- @todo document + -- @usage -- set flag 11 to true as soon as any blue vehicles + -- -- are inside the polygon shape created off of the waypoints + -- -- of the group forest1 + -- mist.flagFunc.units_in_polygon { + -- units = {'[blue][vehicle]'}, + -- zone = mist.getGroupPoints('forest1'), + -- flag = 11 + -- } function mist.flagFunc.units_in_polygon(vars) --[[vars needs to be: units = table, @@ -4956,7 +4963,6 @@ end]] mist.message = { - add = function(vars) local function msgSpamFilter(recList, spamBlockOn) for id, name in pairs(recList) do @@ -6356,8 +6362,20 @@ end --- mist.flagFunc.units_in_polygon parameter table. -- @table unitsInPolygonVars --- @field unit name table @{UnitNameTable}. --- @field zone table defining a polygon. +-- @tfield table unit name table @{UnitNameTable}. +-- @tfield table zone table defining a polygon. +-- @tfield number|string flag flag to set to true. +-- @tfield[opt] number|string stopflag if set to true the function +-- will stop evaluating. +-- @tfield[opt] number maxalt maximum altitude (MSL) for the +-- polygon. +-- @tfield[opt] number req_num minimum number of units that have +-- to be in the polygon. +-- @tfield[opt] number interval sets the interval for +-- checking if units are inside of the polygon in seconds. Default: 1. +-- @tfield[opt] boolean toggle switch the flag to false if required +-- conditions are not met. Default: false. +-- @tfield[opt] table unitTableDef --- Logger class. -- @type mist.Logger From 57613f0aabc6702bfc9d325eef58e2311673d985 Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Wed, 13 Jan 2016 12:06:39 +0100 Subject: [PATCH 29/42] save some more brackets and quotes --- mist.lua | 136 +++++++++++++++++++++++++++---------------------------- 1 file changed, 68 insertions(+), 68 deletions(-) diff --git a/mist.lua b/mist.lua index 00929e4..8bb7fcf 100644 --- a/mist.lua +++ b/mist.lua @@ -137,7 +137,7 @@ do -- the main scope local countryName = string.lower(cntry_data.name) mist.DBs.units[coa_name][countryName] = {} - mist.DBs.units[coa_name][countryName]["countryId"] = cntry_data.id + mist.DBs.units[coa_name][countryName].countryId = cntry_data.id if type(cntry_data) == 'table' then --just making sure @@ -160,77 +160,77 @@ do -- the main scope if env.mission.version > 7 then groupName = env.getValueDictByKey(groupName) end - mist.DBs.units[coa_name][countryName][category][group_num]["groupName"] = groupName - mist.DBs.units[coa_name][countryName][category][group_num]["groupId"] = group_data.groupId - mist.DBs.units[coa_name][countryName][category][group_num]["category"] = category - mist.DBs.units[coa_name][countryName][category][group_num]["coalition"] = coa_name - mist.DBs.units[coa_name][countryName][category][group_num]["country"] = countryName - mist.DBs.units[coa_name][countryName][category][group_num]["countryId"] = cntry_data.id - mist.DBs.units[coa_name][countryName][category][group_num]["startTime"] = group_data.start_time - mist.DBs.units[coa_name][countryName][category][group_num]["task"] = group_data.task - mist.DBs.units[coa_name][countryName][category][group_num]["hidden"] = group_data.hidden + mist.DBs.units[coa_name][countryName][category][group_num].groupName = groupName + mist.DBs.units[coa_name][countryName][category][group_num].groupId = group_data.groupId + mist.DBs.units[coa_name][countryName][category][group_num].category = category + mist.DBs.units[coa_name][countryName][category][group_num].coalition = coa_name + mist.DBs.units[coa_name][countryName][category][group_num].country = countryName + mist.DBs.units[coa_name][countryName][category][group_num].countryId = cntry_data.id + mist.DBs.units[coa_name][countryName][category][group_num].startTime = group_data.start_time + mist.DBs.units[coa_name][countryName][category][group_num].task = group_data.task + mist.DBs.units[coa_name][countryName][category][group_num].hidden = group_data.hidden - mist.DBs.units[coa_name][countryName][category][group_num]["units"] = {} + mist.DBs.units[coa_name][countryName][category][group_num].units = {} - mist.DBs.units[coa_name][countryName][category][group_num]["radioSet"] = group_data.radioSet - mist.DBs.units[coa_name][countryName][category][group_num]["uncontrolled"] = group_data.uncontrolled - mist.DBs.units[coa_name][countryName][category][group_num]["frequency"] = group_data.frequency - mist.DBs.units[coa_name][countryName][category][group_num]["modulation"] = group_data.modulation + mist.DBs.units[coa_name][countryName][category][group_num].radioSet = group_data.radioSet + mist.DBs.units[coa_name][countryName][category][group_num].uncontrolled = group_data.uncontrolled + mist.DBs.units[coa_name][countryName][category][group_num].frequency = group_data.frequency + mist.DBs.units[coa_name][countryName][category][group_num].modulation = group_data.modulation for unit_num, unit_data in pairs(group_data.units) do - local units_tbl = mist.DBs.units[coa_name][countryName][category][group_num]["units"] --pointer to the units table for this group + local units_tbl = mist.DBs.units[coa_name][countryName][category][group_num].units --pointer to the units table for this group units_tbl[unit_num] = {} if env.mission.version > 7 then - units_tbl[unit_num]["unitName"] = env.getValueDictByKey(unit_data.name) + units_tbl[unit_num].unitName = env.getValueDictByKey(unit_data.name) else - units_tbl[unit_num]["unitName"] = unit_data.name + units_tbl[unit_num].unitName = unit_data.name end - units_tbl[unit_num]["type"] = unit_data.type - units_tbl[unit_num]["skill"] = unit_data.skill --will be nil for statics - units_tbl[unit_num]["unitId"] = unit_data.unitId - units_tbl[unit_num]["category"] = category - units_tbl[unit_num]["coalition"] = coa_name - units_tbl[unit_num]["country"] = countryName - units_tbl[unit_num]["countryId"] = cntry_data.id - units_tbl[unit_num]["heading"] = unit_data.heading - units_tbl[unit_num]["playerCanDrive"] = unit_data.playerCanDrive - units_tbl[unit_num]["alt"] = unit_data.alt - units_tbl[unit_num]["alt_type"] = unit_data.alt_type - units_tbl[unit_num]["speed"] = unit_data.speed - units_tbl[unit_num]["livery_id"] = unit_data.livery_id + units_tbl[unit_num].type = unit_data.type + units_tbl[unit_num].skill = unit_data.skill --will be nil for statics + units_tbl[unit_num].unitId = unit_data.unitId + units_tbl[unit_num].category = category + units_tbl[unit_num].coalition = coa_name + units_tbl[unit_num].country = countryName + units_tbl[unit_num].countryId = cntry_data.id + units_tbl[unit_num].heading = unit_data.heading + units_tbl[unit_num].playerCanDrive = unit_data.playerCanDrive + units_tbl[unit_num].alt = unit_data.alt + units_tbl[unit_num].alt_type = unit_data.alt_type + units_tbl[unit_num].speed = unit_data.speed + units_tbl[unit_num].livery_id = unit_data.livery_id if unit_data.point then --ME currently does not work like this, but it might one day - units_tbl[unit_num]["point"] = unit_data.point + units_tbl[unit_num].point = unit_data.point else - units_tbl[unit_num]["point"] = {} - units_tbl[unit_num]["point"]["x"] = unit_data.x - units_tbl[unit_num]["point"]["y"] = unit_data.y + units_tbl[unit_num].point = {} + units_tbl[unit_num].point.x = unit_data.x + units_tbl[unit_num].point.y = unit_data.y end units_tbl[unit_num].x = unit_data.x units_tbl[unit_num].y = unit_data.y - units_tbl[unit_num]["callsign"] = unit_data.callsign - units_tbl[unit_num]["onboard_num"] = unit_data.onboard_num - units_tbl[unit_num]["hardpoint_racks"] = unit_data.hardpoint_racks - units_tbl[unit_num]["psi"] = unit_data.psi + units_tbl[unit_num].callsign = unit_data.callsign + units_tbl[unit_num].onboard_num = unit_data.onboard_num + units_tbl[unit_num].hardpoint_racks = unit_data.hardpoint_racks + units_tbl[unit_num].psi = unit_data.psi - units_tbl[unit_num]["groupName"] = groupName - units_tbl[unit_num]["groupId"] = group_data.groupId + units_tbl[unit_num].groupName = groupName + units_tbl[unit_num].groupId = group_data.groupId if unit_data.AddPropAircraft then - units_tbl[unit_num]["AddPropAircraft"] = unit_data.AddPropAircraft + units_tbl[unit_num].AddPropAircraft = unit_data.AddPropAircraft end if category == 'static' then - units_tbl[unit_num]["categoryStatic"] = unit_data.category - units_tbl[unit_num]["shape_name"] = unit_data.shape_name + units_tbl[unit_num].categoryStatic = unit_data.category + units_tbl[unit_num].shape_name = unit_data.shape_name if unit_data.mass then - units_tbl[unit_num]["mass"] = unit_data.mass + units_tbl[unit_num].mass = unit_data.mass end if unit_data.canCargo then - units_tbl[unit_num]["canCargo"] = unit_data.canCargo + units_tbl[unit_num].canCargo = unit_data.canCargo end end @@ -2490,8 +2490,8 @@ function mist.getUnitsLOS(unitset1, altoffset1, unitset2, altoffset2, radius) local unit1 = Unit.getByName(unitset1[unitset1_ind]) if unit1 and unit1:isActive() == true then unit_info1[#unit_info1 + 1] = {} - unit_info1[#unit_info1]["unit"] = unit1 - unit_info1[#unit_info1]["pos"] = unit1:getPosition().p + unit_info1[#unit_info1].unit = unit1 + unit_info1[#unit_info1].pos = unit1:getPosition().p end end @@ -2499,8 +2499,8 @@ function mist.getUnitsLOS(unitset1, altoffset1, unitset2, altoffset2, radius) local unit2 = Unit.getByName(unitset2[unitset2_ind]) if unit2 and unit2:isActive() == true then unit_info2[#unit_info2 + 1] = {} - unit_info2[#unit_info2]["unit"] = unit2 - unit_info2[#unit_info2]["pos"] = unit2:getPosition().p + unit_info2[#unit_info2].unit = unit2 + unit_info2[#unit_info2].pos = unit2:getPosition().p end end @@ -2806,16 +2806,16 @@ do -- group functions scope local newUnits = newGroup:getUnits() for unitNum, unitData in pairs(newGroup:getUnits()) do newData.units[unitNum] = {} - newData.units[unitNum]["unitId"] = tonumber(unitData:getID()) + newData.units[unitNum].unitId = tonumber(unitData:getID()) newData.units[unitNum].point = unitData.point newData.units[unitNum].x = unitData:getPosition().p.x newData.units[unitNum].y = unitData:getPosition().p.z - newData.units[unitNum]["type"] = unitData:getTypeName() - newData.units[unitNum]["skill"] = mist.getUnitSkill(unitData:getName()) + newData.units[unitNum].type = unitData:getTypeName() + newData.units[unitNum].skill = mist.getUnitSkill(unitData:getName()) -- get velocity needed - newData.units[unitNum]["unitName"] = unitData:getName() - newData.units[unitNum]["heading"] = mist.getHeading(unitData, true) -- added to DBs + newData.units[unitNum].unitName = unitData:getName() + newData.units[unitNum].heading = mist.getHeading(unitData, true) -- added to DBs newData.units[unitNum].alt = unitData:getPosition().p.y newData.country = string.lower(country.name[unitData:getCountry()]) newData.units[unitNum].callsign = unitData:getCallsign() @@ -2863,22 +2863,22 @@ do -- group functions scope for unitNum, unitData in pairs(newData.units) do newData.units[unitNum] = {} - newData.units[unitNum]["unitId"] = unitData.unitId + newData.units[unitNum].unitId = unitData.unitId --newData.units[unitNum].point = unitData.point newData.units[unitNum].x = unitData.point.x newData.units[unitNum].y = unitData.point.y newData.units[unitNum].alt = unitData.alt newData.units[unitNum].alt_type = unitData.alt_type newData.units[unitNum].speed = unitData.speed - newData.units[unitNum]["type"] = unitData.type - newData.units[unitNum]["skill"] = unitData.skill - newData.units[unitNum]["unitName"] = unitData.unitName - newData.units[unitNum]["heading"] = unitData.heading -- added to DBs - newData.units[unitNum]["playerCanDrive"] = unitData.playerCanDrive -- added to DBs + newData.units[unitNum].type = unitData.type + newData.units[unitNum].skill = unitData.skill + newData.units[unitNum].unitName = unitData.unitName + newData.units[unitNum].heading = unitData.heading -- added to DBs + newData.units[unitNum].playerCanDrive = unitData.playerCanDrive -- added to DBs if newData.category == 'plane' or newData.category == 'helicopter' then - newData.units[unitNum]["payload"] = payloads[unitNum] + newData.units[unitNum].payload = payloads[unitNum] newData.units[unitNum].livery_id = unitData.livery_id newData.units[unitNum].onboard_num = unitData.onboard_num newData.units[unitNum].callsign = unitData.callsign @@ -3071,18 +3071,18 @@ do -- group functions scope newGroupData.units[unitNum].x = newCoord.x newGroupData.units[unitNum].y = newCoord.y else - newGroupData.units[unitNum]["x"] = unitData.x + diff.x - newGroupData.units[unitNum]["y"] = unitData.y + diff.y + newGroupData.units[unitNum].x = unitData.x + diff.x + newGroupData.units[unitNum].y = unitData.y + diff.y end if point then if (newGroupData.category == 'plane' or newGroupData.category == 'helicopter') then - if point.z and point.y > 0 and point.y > land.getHeight({newGroupData.units[unitNum]["x"], newGroupData.units[unitNum]["y"]}) + 10 then - newGroupData.units[unitNum]["alt"] = point.y + if point.z and point.y > 0 and point.y > land.getHeight({newGroupData.units[unitNum].x, newGroupData.units[unitNum].y}) + 10 then + newGroupData.units[unitNum].alt = point.y else if newGroupData.category == 'plane' then - newGroupData.units[unitNum]["alt"] = land.getHeight({newGroupData.units[unitNum]["x"], newGroupData.units[unitNum]["y"]}) + math.random(300, 9000) + newGroupData.units[unitNum].alt = land.getHeight({newGroupData.units[unitNum].x, newGroupData.units[unitNum].y}) + math.random(300, 9000) else - newGroupData.units[unitNum]["alt"] = land.getHeight({newGroupData.units[unitNum]["x"], newGroupData.units[unitNum]["y"]}) + math.random(200, 3000) + newGroupData.units[unitNum].alt = land.getHeight({newGroupData.units[unitNum].x, newGroupData.units[unitNum].y}) + math.random(200, 3000) end end end From a11b254bbd3c0d245e0db7fb31bec3f16c57cf6e Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Wed, 13 Jan 2016 13:44:43 +0100 Subject: [PATCH 30/42] consistent variable naming --- mist.lua | 70 +++++++++++++++++++++++++++----------------------------- 1 file changed, 34 insertions(+), 36 deletions(-) diff --git a/mist.lua b/mist.lua index 8bb7fcf..26abf47 100644 --- a/mist.lua +++ b/mist.lua @@ -49,16 +49,16 @@ do -- the main scope local writeGroups = {} local lastUpdateTime = 0 - local update_alive_units_counter = 0 - local write_DB_table_counter = 0 - local check_spawn_events_counter = 0 + local updateAliveUnitsCounter = 0 + local writeDbTableCounter = 0 + local checkSpawnEventsCounter = 0 local mistGpId = 7000 local mistUnitId = 7000 local mistDynAddIndex = 1 - local Tasks = {} - local task_id = 0 + local scheduledTasks = {} + local taskId = 0 local idNum = 0 mist.nextGroupId = 1 @@ -838,30 +838,30 @@ do -- the main scope local function doScheduledFunctions() local i = 1 - while i <= #Tasks do - if not Tasks[i].rep then -- not a repeated process - if Tasks[i].t <= timer.getTime() then - local Task = Tasks[i] -- local reference - table.remove(Tasks, i) - local err, errmsg = pcall(Task.f, unpack(Task.vars, 1, table.maxn(Task.vars))) + while i <= #scheduledTasks do + if not scheduledTasks[i].rep then -- not a repeated process + if scheduledTasks[i].t <= timer.getTime() then + local task = scheduledTasks[i] -- local reference + table.remove(scheduledTasks, i) + local err, errmsg = pcall(task.f, unpack(task.vars, 1, table.maxn(task.vars))) if not err then log:error('mist.scheduleFunction, error in scheduled function: $1', errmsg) end - --Task.f(unpack(Task.vars, 1, table.maxn(Task.vars))) -- do the task, do not increment i + --task.f(unpack(task.vars, 1, table.maxn(task.vars))) -- do the task, do not increment i else i = i + 1 end else - if Tasks[i].st and Tasks[i].st <= timer.getTime() then --if a stoptime was specified, and the stop time exceeded - table.remove(Tasks, i) -- stop time exceeded, do not execute, do not increment i - elseif Tasks[i].t <= timer.getTime() then - local Task = Tasks[i] -- local reference - Task.t = timer.getTime() + Task.rep --schedule next run - local err, errmsg = pcall(Task.f, unpack(Task.vars, 1, table.maxn(Task.vars))) + if scheduledTasks[i].st and scheduledTasks[i].st <= timer.getTime() then --if a stoptime was specified, and the stop time exceeded + table.remove(scheduledTasks, i) -- stop time exceeded, do not execute, do not increment i + elseif scheduledTasks[i].t <= timer.getTime() then + local task = scheduledTasks[i] -- local reference + task.t = timer.getTime() + task.rep --schedule next run + local err, errmsg = pcall(task.f, unpack(task.vars, 1, table.maxn(task.vars))) if not err then log:error('mist.scheduleFunction, error in scheduled function: $1' .. errmsg) end - --Tasks[i].f(unpack(Tasks[i].vars, 1, table.maxn(Tasks[i].vars))) -- do the task + --scheduledTasks[i].f(unpack(scheduledTasks[i].vars, 1, table.maxn(scheduledTasks[i].vars))) -- do the task i = i + 1 else i = i + 1 @@ -988,10 +988,9 @@ do -- the main scope function mist.main() timer.scheduleFunction(mist.main, {}, timer.getTime() + 0.01) --reschedule first in case of Lua error - write_DB_table_counter = write_DB_table_counter + 1 - if write_DB_table_counter == 10 then - - write_DB_table_counter = 0 + writeDbTableCounter = writeDbTableCounter + 1 + if writeDbTableCounter == 10 then + writeDbTableCounter = 0 if not coroutines.updateDBTables then coroutines.updateDBTables = coroutine.create(updateDBTables) @@ -1004,10 +1003,9 @@ do -- the main scope end end - check_spawn_events_counter = check_spawn_events_counter + 1 - if check_spawn_events_counter == 10 then - - check_spawn_events_counter = 0 + checkSpawnEventsCounter = checkSpawnEventsCounter + 1 + if checkSpawnEventsCounter == 10 then + checkSpawnEventsCounter = 0 if not coroutines.checkSpawnedEvents then coroutines.checkSpawnedEvents = coroutine.create(checkSpawnedEvents) @@ -1021,9 +1019,9 @@ do -- the main scope end --updating alive units - update_alive_units_counter = update_alive_units_counter + 1 - if update_alive_units_counter == 5 then - update_alive_units_counter = 0 + updateAliveUnitsCounter = updateAliveUnitsCounter + 1 + if updateAliveUnitsCounter == 5 then + updateAliveUnitsCounter = 0 if not coroutines.update_alive_units then coroutines.updateAliveUnits = coroutine.create(updateAliveUnits) @@ -1364,9 +1362,9 @@ do -- the main scope if not vars then vars = {} end - task_id = task_id + 1 - table.insert(Tasks, {f = f, vars = vars, t = t, rep = rep, st = st, id = task_id}) - return task_id + taskId = taskId + 1 + table.insert(scheduledTasks, {f = f, vars = vars, t = t, rep = rep, st = st, id = taskId}) + return taskId end --- Removes a scheduled function. @@ -1374,9 +1372,9 @@ do -- the main scope -- @treturn boolean true if function was successfully removed, false otherwise. function mist.removeFunction(id) local i = 1 - while i <= #Tasks do - if Tasks[i].id == id then - table.remove(Tasks, i) + while i <= #scheduledTasks do + if scheduledTasks[i].id == id then + table.remove(scheduledTasks, i) else i = i + 1 end From 9dd046dbce344b3eb59298372b51252029334363 Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Wed, 13 Jan 2016 14:10:28 +0100 Subject: [PATCH 31/42] added msg log function --- mist.lua | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/mist.lua b/mist.lua index 26abf47..be33d96 100644 --- a/mist.lua +++ b/mist.lua @@ -845,7 +845,7 @@ do -- the main scope table.remove(scheduledTasks, i) local err, errmsg = pcall(task.f, unpack(task.vars, 1, table.maxn(task.vars))) if not err then - log:error('mist.scheduleFunction, error in scheduled function: $1', errmsg) + log:error('Error in scheduled function: $1', errmsg) end --task.f(unpack(task.vars, 1, table.maxn(task.vars))) -- do the task, do not increment i else @@ -859,7 +859,7 @@ do -- the main scope task.t = timer.getTime() + task.rep --schedule next run local err, errmsg = pcall(task.f, unpack(task.vars, 1, table.maxn(task.vars))) if not err then - log:error('mist.scheduleFunction, error in scheduled function: $1' .. errmsg) + log:error('Error in scheduled function: $1' .. errmsg) end --scheduledTasks[i].f(unpack(scheduledTasks[i].vars, 1, table.maxn(scheduledTasks[i].vars))) -- do the task i = i + 1 @@ -965,9 +965,10 @@ do -- the main scope function mist.init() -- create logger mist.log = mist.Logger:new("MIST") - log = mist.log - -- set info log level - log:setLevel("info") + log = mist.log -- log shorthand + -- set warning log level, showing only + -- warnings and errors + log:setLevel("warning") log:info("initializing databases") initDBs() @@ -979,7 +980,7 @@ do -- the main scope -- call main the first time therafter it reschedules itself. mist.main() - log:info('MIST version $1.$2.$3 loaded', mist.majorVersion, mist.minorVersion, mist.build) + log:msg('MIST version $1.$2.$3 loaded', mist.majorVersion, mist.minorVersion, mist.build) end --- The main function. @@ -6495,6 +6496,26 @@ do -- mist.Logger scope end end + --- Logs a message, disregarding the log level. + -- @tparam string text the text with keywords to substitute. + -- @param ... variables to be used for substitution. + -- @usage myLogger:msg("Always logged!") + function mist.Logger:msg(text, ...) + text = formatText(text, unpack(arg)) + if text:len() > 4000 then + local texts = splitText(text) + for i = 1, #texts do + if i == 1 then + env.info(self.tag .. '|' .. texts[i]) + else + env.info(texts[i]) + end + end + else + env.info(self.tag .. '|' .. text) + end + end + --- Logs an error. -- logs a message prefixed with this loggers tag to dcs.log as -- long as at least the "error" log level (1) is set. From 2a0ab57480fe5f02bea87931db447c1b9e6c5bba Mon Sep 17 00:00:00 2001 From: mrSkortch Date: Thu, 14 Jan 2016 03:31:38 -0700 Subject: [PATCH 32/42] commited apparent local change so I can compare stuff --- mist.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/mist.lua b/mist.lua index 41abdc6..6e6db55 100644 --- a/mist.lua +++ b/mist.lua @@ -5376,7 +5376,6 @@ mist.teleportToPoint = function(vars) -- main teleport function that all of tele end mist.respawnInZone = function(gpName, zone, disperse, maxDisp) - if type(gpName) == 'table' and gpName:getName() then gpName = gpName:getName() elseif type(gpName) == 'table' and gpName[1]:getName() then From b40a919bb0a2f5297f02d39687875b00a1a7828f Mon Sep 17 00:00:00 2001 From: mrSkortch Date: Mon, 18 Jan 2016 04:33:36 -0700 Subject: [PATCH 33/42] Fixed the botched merge There was a conflict on merging lukrop's commit. I've fixed it and made some other changes. -Retabulated entire file. Fans of terrible drink from the 90s rejoice. -Moved the mist init message back to the end of file and into the old format. -Commented out several of the new log:info statements that replaced old commented out print statements. --- mist.lua | 12558 +++++++++++++++++++++++++---------------------------- 1 file changed, 5908 insertions(+), 6650 deletions(-) diff --git a/mist.lua b/mist.lua index 1c4d016..437c574 100644 --- a/mist.lua +++ b/mist.lua @@ -35,2551 +35,2550 @@ mist = {} -- don't change these mist.majorVersion = 4 mist.minorVersion = 1 -mist.build = 61 +mist.build = 63 -- forward declaration of log shorthand local log do -- the main scope - local coroutines = {} - - local tempSpawnedUnits = {} -- birth events added here - local mistAddedObjects = {} -- mist.dynAdd unit data added here - local mistAddedGroups = {} -- mist.dynAdd groupdata added here - local writeGroups = {} - local lastUpdateTime = 0 - - local updateAliveUnitsCounter = 0 - local writeDbTableCounter = 0 - local checkSpawnEventsCounter = 0 - - local mistGpId = 7000 - local mistUnitId = 7000 - local mistDynAddIndex = 1 - - local scheduledTasks = {} - local taskId = 0 - local idNum = 0 - - mist.nextGroupId = 1 - mist.nextUnitId = 1 - - local function initDBs() -- mist.DBs scope - mist.DBs = {} - - mist.DBs.missionData = {} - if env.mission then - - mist.DBs.missionData.startTime = env.mission.start_time - mist.DBs.missionData.theatre = env.mission.theatre - mist.DBs.missionData.version = env.mission.version - mist.DBs.missionData.files = {} - if type(env.mission.resourceCounter) == 'table' then - for fIndex, fData in pairs (env.mission.resourceCounter) do - mist.DBs.missionData.files[#mist.DBs.missionData.files + 1] = mist.utils.deepCopy(fIndex) - end - end - -- if we add more coalition specific data then bullsye should be categorized by coaliton. For now its just the bullseye table - mist.DBs.missionData.bullseye = {red = {}, blue = {}} - mist.DBs.missionData.bullseye.red.x = env.mission.coalition.red.bullseye.x --should it be point.x? - mist.DBs.missionData.bullseye.red.y = env.mission.coalition.red.bullseye.y - mist.DBs.missionData.bullseye.blue.x = env.mission.coalition.blue.bullseye.x - mist.DBs.missionData.bullseye.blue.y = env.mission.coalition.blue.bullseye.y - end - - mist.DBs.zonesByName = {} - mist.DBs.zonesByNum = {} - - - if env.mission.triggers and env.mission.triggers.zones then - for zone_ind, zone_data in pairs(env.mission.triggers.zones) do - if type(zone_data) == 'table' then - local zone = mist.utils.deepCopy(zone_data) - zone.point = {} -- point is used by SSE - zone.point.x = zone_data.x - zone.point.y = 0 - zone.point.z = zone_data.y - - mist.DBs.zonesByName[zone_data.name] = zone - mist.DBs.zonesByNum[#mist.DBs.zonesByNum + 1] = mist.utils.deepCopy(zone) --[[deepcopy so that the zone in zones_by_name and the zone in - zones_by_num se are different objects.. don't want them linked.]] - end - end - end - - mist.DBs.navPoints = {} - mist.DBs.units = {} - --Build mist.db.units and mist.DBs.navPoints - 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 - mist.DBs.units[coa_name] = {} - - -- build nav points DB - mist.DBs.navPoints[coa_name] = {} - if coa_data.nav_points then --navpoints - --mist.debug.writeData (mist.utils.serialize,{'NavPoints',coa_data.nav_points}, 'NavPoints.txt') - for nav_ind, nav_data in pairs(coa_data.nav_points) do - - if type(nav_data) == 'table' then - mist.DBs.navPoints[coa_name][nav_ind] = mist.utils.deepCopy(nav_data) - - mist.DBs.navPoints[coa_name][nav_ind].name = nav_data.callsignStr -- name is a little bit more self-explanatory. - mist.DBs.navPoints[coa_name][nav_ind].point = {} -- point is used by SSE, support it. - mist.DBs.navPoints[coa_name][nav_ind].point.x = nav_data.x - mist.DBs.navPoints[coa_name][nav_ind].point.y = 0 - mist.DBs.navPoints[coa_name][nav_ind].point.z = nav_data.y - end - end - end - if coa_data.country then --there is a country table - for cntry_id, cntry_data in pairs(coa_data.country) do - - local countryName = string.lower(cntry_data.name) - mist.DBs.units[coa_name][countryName] = {} - mist.DBs.units[coa_name][countryName].countryId = cntry_data.id - - if type(cntry_data) == 'table' then --just making sure - - 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" or obj_type_name == "static" then --should be an unncessary check - - local category = obj_type_name - - 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! - - mist.DBs.units[coa_name][countryName][category] = {} - - for group_num, group_data in pairs(obj_type_data.group) do - - if group_data and group_data.units and type(group_data.units) == 'table' then --making sure again- this is a valid group - - mist.DBs.units[coa_name][countryName][category][group_num] = {} - local groupName = group_data.name - if env.mission.version > 7 then - groupName = env.getValueDictByKey(groupName) - end - mist.DBs.units[coa_name][countryName][category][group_num].groupName = groupName - mist.DBs.units[coa_name][countryName][category][group_num].groupId = group_data.groupId - mist.DBs.units[coa_name][countryName][category][group_num].category = category - mist.DBs.units[coa_name][countryName][category][group_num].coalition = coa_name - mist.DBs.units[coa_name][countryName][category][group_num].country = countryName - mist.DBs.units[coa_name][countryName][category][group_num].countryId = cntry_data.id - mist.DBs.units[coa_name][countryName][category][group_num].startTime = group_data.start_time - mist.DBs.units[coa_name][countryName][category][group_num].task = group_data.task - mist.DBs.units[coa_name][countryName][category][group_num].hidden = group_data.hidden - - mist.DBs.units[coa_name][countryName][category][group_num].units = {} - - mist.DBs.units[coa_name][countryName][category][group_num].radioSet = group_data.radioSet - mist.DBs.units[coa_name][countryName][category][group_num].uncontrolled = group_data.uncontrolled - mist.DBs.units[coa_name][countryName][category][group_num].frequency = group_data.frequency - mist.DBs.units[coa_name][countryName][category][group_num].modulation = group_data.modulation - - for unit_num, unit_data in pairs(group_data.units) do - local units_tbl = mist.DBs.units[coa_name][countryName][category][group_num].units --pointer to the units table for this group - - units_tbl[unit_num] = {} - if env.mission.version > 7 then - units_tbl[unit_num].unitName = env.getValueDictByKey(unit_data.name) - else - units_tbl[unit_num].unitName = unit_data.name - end - units_tbl[unit_num].type = unit_data.type - units_tbl[unit_num].skill = unit_data.skill --will be nil for statics - units_tbl[unit_num].unitId = unit_data.unitId - units_tbl[unit_num].category = category - units_tbl[unit_num].coalition = coa_name - units_tbl[unit_num].country = countryName - units_tbl[unit_num].countryId = cntry_data.id - units_tbl[unit_num].heading = unit_data.heading - units_tbl[unit_num].playerCanDrive = unit_data.playerCanDrive - units_tbl[unit_num].alt = unit_data.alt - units_tbl[unit_num].alt_type = unit_data.alt_type - units_tbl[unit_num].speed = unit_data.speed - units_tbl[unit_num].livery_id = unit_data.livery_id - if unit_data.point then --ME currently does not work like this, but it might one day - units_tbl[unit_num].point = unit_data.point - else - units_tbl[unit_num].point = {} - units_tbl[unit_num].point.x = unit_data.x - units_tbl[unit_num].point.y = unit_data.y - end - units_tbl[unit_num].x = unit_data.x - units_tbl[unit_num].y = unit_data.y - - units_tbl[unit_num].callsign = unit_data.callsign - units_tbl[unit_num].onboard_num = unit_data.onboard_num - units_tbl[unit_num].hardpoint_racks = unit_data.hardpoint_racks - units_tbl[unit_num].psi = unit_data.psi - - - units_tbl[unit_num].groupName = groupName - units_tbl[unit_num].groupId = group_data.groupId - - if unit_data.AddPropAircraft then - units_tbl[unit_num].AddPropAircraft = unit_data.AddPropAircraft - end - - if category == 'static' then - units_tbl[unit_num].categoryStatic = unit_data.category - units_tbl[unit_num].shape_name = unit_data.shape_name - if unit_data.mass then - units_tbl[unit_num].mass = unit_data.mass - end - - if unit_data.canCargo then - units_tbl[unit_num].canCargo = unit_data.canCargo - end - end - - end --for unit_num, unit_data in pairs(group_data.units) do - end --if group_data and group_data.units then - 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 --if type(cntry_data) == 'table' then - 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 - - mist.DBs.unitsByName = {} - mist.DBs.unitsById = {} - mist.DBs.unitsByCat = {} - - mist.DBs.unitsByCat.helicopter = {} -- adding default categories - mist.DBs.unitsByCat.plane = {} - mist.DBs.unitsByCat.ship = {} - mist.DBs.unitsByCat.static = {} - mist.DBs.unitsByCat.vehicle = {} - - mist.DBs.unitsByNum = {} - - mist.DBs.groupsByName = {} - mist.DBs.groupsById = {} - mist.DBs.humansByName = {} - mist.DBs.humansById = {} - - mist.DBs.dynGroupsAdded = {} -- will be filled by mist.dbUpdate from dynamically spawned groups - mist.DBs.activeHumans = {} - - mist.DBs.aliveUnits = {} -- will be filled in by the "updateAliveUnits" coroutine in mist.main. - - mist.DBs.removedAliveUnits = {} -- will be filled in by the "updateAliveUnits" coroutine in mist.main. - - mist.DBs.const = {} - - -- not accessible by SSE, must use static list :-/ - mist.DBs.const.callsigns = { - ['NATO'] = { - ['rules'] = { - ['groupLimit'] = 9, - }, - ['AWACS'] = { - ['Overlord'] = 1, - ['Magic'] = 2, - ['Wizard'] = 3, - ['Focus'] = 4, - ['Darkstar'] = 5, - }, - ['TANKER'] = { - ['Texaco'] = 1, - ['Arco'] = 2, - ['Shell'] = 3, - }, - ['JTAC'] = { - ['Axeman'] = 1, - ['Darknight'] = 2, - ['Warrior'] = 3, - ['Pointer'] = 4, - ['Eyeball'] = 5, - ['Moonbeam'] = 6, - ['Whiplash'] = 7, - ['Finger'] = 8, - ['Pinpoint'] = 9, - ['Ferret'] = 10, - ['Shaba'] = 11, - ['Playboy'] = 12, - ['Hammer'] = 13, - ['Jaguar'] = 14, - ['Deathstar'] = 15, - ['Anvil'] = 16, - ['Firefly'] = 17, - ['Mantis'] = 18, - ['Badger'] = 19, - }, - ['aircraft'] = { - ['Enfield'] = 1, - ['Springfield'] = 2, - ['Uzi'] = 3, - ['Colt'] = 4, - ['Dodge'] = 5, - ['Ford'] = 6, - ['Chevy'] = 7, - ['Pontiac'] = 8, - }, - - ['unique'] = { - ['A10'] = { - ['Hawg'] = 9, - ['Boar'] = 10, - ['Pig'] = 11, - ['Tusk'] = 12, - ['rules'] = { - ['canUseAircraft'] = true, - ['appliesTo'] = { - 'A-10C', - 'A-10A', - }, - }, - }, - }, - }, - } - -- create mist.DBs.oldAliveUnits - -- do - -- local intermediate_alive_units = {} -- between 0 and 0.5 secs old - -- local function make_old_alive_units() -- called every 0.5 secs, makes the old_alive_units DB which is just a copy of alive_units that is 0.5 to 1 sec old - -- if intermediate_alive_units then - -- mist.DBs.oldAliveUnits = mist.utils.deepCopy(intermediate_alive_units) - -- end - -- intermediate_alive_units = mist.utils.deepCopy(mist.DBs.aliveUnits) - -- timer.scheduleFunction(make_old_alive_units, nil, timer.getTime() + 0.5) - -- end - - -- make_old_alive_units() - -- end - - --Build DBs - for coa_name, coa_data in pairs(mist.DBs.units) do - for cntry_name, cntry_data in pairs(coa_data) do - for category_name, category_data in pairs(cntry_data) do - if type(category_data) == 'table' then - for group_ind, group_data in pairs(category_data) do - if type(group_data) == 'table' and group_data.units and type(group_data.units) == 'table' and #group_data.units > 0 then -- OCD paradigm programming - mist.DBs.groupsByName[group_data.groupName] = mist.utils.deepCopy(group_data) - mist.DBs.groupsById[group_data.groupId] = mist.utils.deepCopy(group_data) - for unit_ind, unit_data in pairs(group_data.units) do - mist.DBs.unitsByName[unit_data.unitName] = mist.utils.deepCopy(unit_data) - mist.DBs.unitsById[unit_data.unitId] = mist.utils.deepCopy(unit_data) - - mist.DBs.unitsByCat[unit_data.category] = mist.DBs.unitsByCat[unit_data.category] or {} -- future-proofing against new categories... - table.insert(mist.DBs.unitsByCat[unit_data.category], mist.utils.deepCopy(unit_data)) - log:info('inserting $1', unit_data.unitName) - table.insert(mist.DBs.unitsByNum, mist.utils.deepCopy(unit_data)) - - if unit_data.skill and (unit_data.skill == "Client" or unit_data.skill == "Player") then - mist.DBs.humansByName[unit_data.unitName] = mist.utils.deepCopy(unit_data) - mist.DBs.humansById[unit_data.unitId] = mist.utils.deepCopy(unit_data) - --if Unit.getByName(unit_data.unitName) then - -- mist.DBs.activeHumans[unit_data.unitName] = mist.utils.deepCopy(unit_data) - -- mist.DBs.activeHumans[unit_data.unitName].playerName = Unit.getByName(unit_data.unitName):getPlayerName() - --end - end - end - end - end - end - end - end - end - - --DynDBs - mist.DBs.MEunits = mist.utils.deepCopy(mist.DBs.units) - mist.DBs.MEunitsByName = mist.utils.deepCopy(mist.DBs.unitsByName) - mist.DBs.MEunitsById = mist.utils.deepCopy(mist.DBs.unitsById) - mist.DBs.MEunitsByCat = mist.utils.deepCopy(mist.DBs.unitsByCat) - mist.DBs.MEunitsByNum = mist.utils.deepCopy(mist.DBs.unitsByNum) - mist.DBs.MEgroupsByName = mist.utils.deepCopy(mist.DBs.groupsByName) - mist.DBs.MEgroupsById = mist.utils.deepCopy(mist.DBs.groupsById) - - mist.DBs.deadObjects = {} - - do - local mt = {} - - function mt.__newindex(t, key, val) - local original_key = key --only for duplicate runtime IDs. - local key_ind = 1 - while mist.DBs.deadObjects[key] do - log:warn('duplicate runtime id of previously dead object key: $1', key) - key = tostring(original_key) .. ' #' .. tostring(key_ind) - key_ind = key_ind + 1 - end - - if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then - log:info('object found in alive_units') - val.objectData = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) - local pos = Object.getPosition(val.object) - if pos then - val.objectPos = pos.p - end - val.objectType = mist.DBs.aliveUnits[val.object.id_].category - - elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units - log:info('object found in old_alive_units') - val.objectData = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) - local pos = Object.getPosition(val.object) - if pos then - val.objectPos = pos.p - end - val.objectType = mist.DBs.removedAliveUnits[val.object.id_].category - - else --attempt to determine if static object... - log:info('object not found in alive units or old alive units') - local pos = Object.getPosition(val.object) - if pos then - local static_found = false - for ind, static in pairs(mist.DBs.unitsByCat.static) do - if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... - log:info('correlated dead static object to position') - val.objectData = static - val.objectPos = pos.p - val.objectType = 'static' - static_found = true - break - end - end - if not static_found then - val.objectPos = pos.p - val.objectType = 'building' - end - else - val.objectType = 'unknown' - end - end - rawset(t, key, val) - end - - setmetatable(mist.DBs.deadObjects, mt) - end - - do -- mist unitID funcs - for id, idData in pairs(mist.DBs.unitsById) do - if idData.unitId > mist.nextUnitId then - mist.nextUnitId = mist.utils.deepCopy(idData.unitId) - end - if idData.groupId > mist.nextGroupId then - mist.nextGroupId = mist.utils.deepCopy(idData.groupId) - end - end - end - - - end - - local function updateAliveUnits() -- coroutine function - local lalive_units = mist.DBs.aliveUnits -- local references for faster execution - local lunits = mist.DBs.unitsByNum - local ldeepcopy = mist.utils.deepCopy - local lUnit = Unit - local lremovedAliveUnits = mist.DBs.removedAliveUnits - local updatedUnits = {} - - if #lunits > 0 then - local units_per_run = math.ceil(#lunits/20) - if units_per_run < 5 then - units_per_run = 5 - end - - for i = 1, #lunits do - if lunits[i].category ~= 'static' then -- can't get statics with Unit.getByName :( - local unit = lUnit.getByName(lunits[i].unitName) - if unit then - --log:info("unit named $1 alive!", lunits[i].unitName) -- spammy - local pos = unit:getPosition() - local newtbl = ldeepcopy(lunits[i]) - if pos then - newtbl.pos = pos.p - end - newtbl.unit = unit - --newtbl.rt_id = unit.id_ - lalive_units[unit.id_] = newtbl - updatedUnits[unit.id_] = true - end - end - if i%units_per_run == 0 then - coroutine.yield() - end - end - -- All units updated, remove any "alive" units that were not updated- they are dead! - for unit_id, unit in pairs(lalive_units) do - if not updatedUnits[unit_id] then - lremovedAliveUnits[unit_id] = unit - lalive_units[unit_id] = nil - end - end - end - end - - local function dbUpdate(event) - local newTable = {} - - newTable.startTime = 0 - - if type(event) == 'string' then -- if name of an object. - local newObject - local newType = 'group' - if Group.getByName(event) then - newObject = Group.getByName(event) - elseif StaticObject.getByName(event) then - newObject = StaticObject.getByName(event) - newType = 'static' - -- log:info('its static') - else - log:info('WTF') - return false - end - - newTable.name = newObject:getName() - newTable.groupId = tonumber(newObject:getID()) - newTable.groupName = newObject:getName() - local unitOneRef - if newType == 'static' then - unitOneRef = newObject - newTable.countryId = tonumber(newObject:getCountry()) - newTable.coalitionId = tonumber(newObject:getCoalition()) - newTable.category = 'static' - else - unitOneRef = newObject:getUnits() - newTable.countryId = tonumber(unitOneRef[1]:getCountry()) - newTable.coalitionId = tonumber(unitOneRef[1]:getCoalition()) - newTable.category = tonumber(newObject:getCategory()) - end - for countryData, countryId in pairs(country.id) do - if newTable.country and string.upper(countryData) == string.upper(newTable.country) or countryId == newTable.countryId then - newTable.countryId = countryId - newTable.country = string.lower(countryData) - for coaData, coaId in pairs(coalition.side) do - if coaId == coalition.getCountryCoalition(countryId) then - newTable.coalition = string.lower(coaData) - end - end - end - end - for catData, catId in pairs(Unit.Category) do - if newType == 'group' and Group.getByName(newTable.groupName):isExist() then - if catId == Group.getByName(newTable.groupName):getCategory() then - newTable.category = string.lower(catData) - end - elseif newType == 'static' and StaticObject.getByName(newTable.groupName):isExist() then - if catId == StaticObject.getByName(newTable.groupName):getCategory() then - newTable.category = string.lower(catData) - end - - end - end - local gfound = false - for index, data in pairs(mistAddedGroups) do - if mist.stringMatch(data.name, newTable.groupName) == true then - gfound = true - newTable.task = data.task - newTable.modulation = data.modulation - newTable.uncontrolled = data.uncontrolled - newTable.radioSet = data.radioSet - newTable.hidden = data.hidden - newTable.startTime = data.start_time - mistAddedGroups[index] = nil - end - end - - if gfound == false then - newTable.uncontrolled = false - newTable.hidden = false - end - - newTable.units = {} - if newType == 'group' then - for unitId, unitData in pairs(unitOneRef) do - newTable.units[unitId] = {} - newTable.units[unitId].unitName = unitData:getName() - - newTable.units[unitId].x = mist.utils.round(unitData:getPosition().p.x) - newTable.units[unitId].y = mist.utils.round(unitData:getPosition().p.z) - newTable.units[unitId].point = {} - newTable.units[unitId].point.x = newTable.units[unitId].x - newTable.units[unitId].point.y = newTable.units[unitId].y - newTable.units[unitId].alt = mist.utils.round(unitData:getPosition().p.y) - newTable.units[unitId].speed = mist.vec.mag(unitData:getVelocity()) - - newTable.units[unitId].heading = mist.getHeading(unitData, true) - - newTable.units[unitId].type = unitData:getTypeName() - newTable.units[unitId].unitId = tonumber(unitData:getID()) - - - newTable.units[unitId].groupName = newTable.groupName - newTable.units[unitId].groupId = newTable.groupId - newTable.units[unitId].countryId = newTable.countryId - newTable.units[unitId].coalitionId = newTable.coalitionId - newTable.units[unitId].coalition = newTable.coalition - newTable.units[unitId].country = newTable.country - local found = false - for index, data in pairs(mistAddedObjects) do - if mist.stringMatch(data.name, newTable.units[unitId].unitName) == true then - found = true - newTable.units[unitId].livery_id = data.livery_id - newTable.units[unitId].skill = data.skill - newTable.units[unitId].alt_type = data.alt_type - newTable.units[unitId].callsign = data.callsign - newTable.units[unitId].psi = data.psi - mistAddedObjects[index] = nil - end - if found == false then - newTable.units[unitId].skill = "High" - newTable.units[unitId].alt_type = "BARO" - end - end - - end - else -- its a static - newTable.category = 'static' - newTable.units[1] = {} - newTable.units[1].unitName = newObject:getName() - newTable.units[1].category = 'static' - newTable.units[1].x = mist.utils.round(newObject:getPosition().p.x) - newTable.units[1].y = mist.utils.round(newObject:getPosition().p.z) - newTable.units[1].point = {} - newTable.units[1].point.x = newTable.units[1].x - newTable.units[1].point.y = newTable.units[1].y - newTable.units[1].alt = mist.utils.round(newObject:getPosition().p.y) - newTable.units[1].heading = mist.getHeading(newObject, true) - newTable.units[1].type = newObject:getTypeName() - newTable.units[1].unitId = tonumber(newObject:getID()) - newTable.units[1].groupName = newTable.name - newTable.units[1].groupId = newTable.groupId - newTable.units[1].countryId = newTable.countryId - newTable.units[1].country = newTable.country - newTable.units[1].coalitionId = newTable.coalitionId - newTable.units[1].coalition = newTable.coalition - if newObject:getCategory() == 6 and newObject:getCargoDisplayName() then - local mass = newObject:getCargoDisplayName() - mass = string.gsub(mass, ' ', '') - mass = string.gsub(mass, 'kg', '') - newTable.units[1].mass = tonumber(mass) - newTable.units[1].categoryStatic = 'Cargos' - newTable.units[1].canCargo = true - newTable.units[1].shape_name = 'ab-212_cargo' - end - - ----- search mist added objects for extra data if applicable - for index, data in pairs(mistAddedObjects) do - if mist.stringMatch(data.name, newTable.units[1].unitName) == true then - newTable.units[1].shape_name = data.shape_name -- for statics - newTable.units[1].livery_id = data.livery_id - newTable.units[1].airdromeId = data.airdromeId - newTable.units[1].mass = data.mass - newTable.units[1].canCargo = data.canCargo - newTable.units[1].categoryStatic = data.categoryStatic - newTable.units[1].type = 'cargo1' - mistAddedObjects[index] = nil - end - end - end - end - --mist.debug.writeData(mist.utils.serialize,{'msg', newTable}, timer.getAbsTime() ..'Group.lua') - newTable.timeAdded = timer.getAbsTime() -- only on the dynGroupsAdded table. For other reference, see start time - --mist.debug.dumpDBs() - --end - - return newTable - end - - local function checkSpawnedEvents() - if #tempSpawnedUnits > 0 then - local groupsToAdd = {} - local added = false - local ltemp = tempSpawnedUnits - local ltable = table - - local updatesPerRun = math.ceil(#tempSpawnedUnits/20) - if updatesPerRun < 5 then - updatesPerRun = 5 - end - for x = 1, #tempSpawnedUnits do - local spawnedObj = ltemp[x] - if spawnedObj and spawnedObj:isExist() then - local found = false - for name, val in pairs(groupsToAdd) do - if spawnedObj:getCategory() == 1 then -- normal groups - if mist.stringMatch(spawnedObj:getGroup():getName(), name) == true then - found = true - break - end - elseif spawnedObj:getCategory() == 3 or spawnedObj:getCategory() == 6 then -- static objects - if mist.stringMatch(spawnedObj:getName(), name) == true then - found = true - break - end - end - end - -- for some reason cargo objects are returning as category == 6. - if found == false then - added = true - if spawnedObj:getCategory() == 1 then -- normal groups - groupsToAdd[spawnedObj:getGroup():getName()] = true - elseif spawnedObj:getCategory() == 3 or spawnedObj:getCategory() == 6 then -- static objects - groupsToAdd[spawnedObj:getName()] = true - end - - end - end - - table.remove(ltemp, x) - if x%updatesPerRun == 0 then - coroutine.yield() - end - end - - if added == true then - for groupName, val in pairs(groupsToAdd) do - local dataChanged = false - if mist.DBs.groupsByName[groupName] then - for _index, data in pairs(mist.DBs.groupsByName[groupName]) do - if data.unitName ~= spawnedObj:getName() and data.unitId ~= spawnedObj:getID() and data.type ~= spawnedObj:getTypeName() then - dataChanged = true - break - end - end - if dataChanged == false then - groupsToAdd[groupName] = false - end - end - if groupsToAdd[groupName] == true or not mist.DBs.groupsByName[groupName] then - writeGroups[#writeGroups + 1] = dbUpdate(groupName) - end - end - end - end - end - - local function updateDBTables() - local i = 0 - for index, newTable in pairs(writeGroups) do - i = i + 1 - end - local savesPerRun = math.ceil(i/10) - if savesPerRun < 5 then - savesPerRun = 5 - end - if i > 0 then - local ldeepCopy = mist.utils.deepCopy - for x = 1, i do - local newTable = writeGroups[x] - local mistCategory - if type(newTable.category) == 'string' then - mistCategory = string.lower(newTable.category) - end - - if string.upper(newTable.category) == 'GROUND_UNIT' then - mistCategory = 'vehicle' - newTable.category = mistCategory - elseif string.upper(newTable.category) == 'AIRPLANE' then - mistCategory = 'plane' - newTable.category = mistCategory - elseif string.upper(newTable.category) == 'HELICOPTER' then - mistCategory = 'helicopter' - newTable.category = mistCategory - elseif string.upper(newTable.category) == 'SHIP' then - mistCategory = 'ship' - newTable.category = mistCategory - end - for newId, newUnitData in pairs(newTable.units) do - newUnitData.category = mistCategory - if newUnitData.unitId then - mist.DBs.unitsById[tonumber(newUnitData.unitId)] = ldeepCopy(newUnitData) - end - - mist.DBs.unitsByName[newUnitData.unitName] = ldeepCopy(newUnitData) - mist.DBs.unitsByCat[mistCategory][#mist.DBs.unitsByCat[mistCategory] + 1] = ldeepCopy(newUnitData) - mist.DBs.unitsByNum[#mist.DBs.unitsByNum + 1] = ldeepCopy(newUnitData) - end - -- this is a really annoying DB to populate. Gotta create new tables in case its missing - if not mist.DBs.units[newTable.coalition] then - mist.DBs.units[newTable.coalition] = {} - end - - if not mist.DBs.units[newTable.coalition][newTable.country] then - mist.DBs.units[newTable.coalition][(newTable.country)] = {} - mist.DBs.units[newTable.coalition][(newTable.country)].countryId = newTable.countryId - end - if not mist.DBs.units[newTable.coalition][newTable.country][mistCategory] then - mist.DBs.units[newTable.coalition][(newTable.country)][mistCategory] = {} - end - mist.DBs.units[newTable.coalition][(newTable.country)][mistCategory][#mist.DBs.units[newTable.coalition][(newTable.country)][mistCategory] + 1] = ldeepCopy(newTable) - - if newTable.groupId then - mist.DBs.groupsById[newTable.groupId] = ldeepCopy(newTable) - end - - mist.DBs.groupsByName[newTable.name] = ldeepCopy(newTable) - mist.DBs.dynGroupsAdded[#mist.DBs.dynGroupsAdded + 1] = ldeepCopy(newTable) - - writeGroups[x] = nil - if x%savesPerRun == 0 then - coroutine.yield() - end - end - if timer.getTime() > lastUpdateTime then - lastUpdateTime = timer.getTime() - end - end - end - - local function groupSpawned(event) - -- dont need to add units spawned in at the start of the mission if mist is loaded in init line - if event.id == world.event.S_EVENT_BIRTH and timer.getTime0() < timer.getAbsTime() then - table.insert(tempSpawnedUnits,(event.initiator)) - end - end - - local function doScheduledFunctions() - local i = 1 - while i <= #scheduledTasks do - if not scheduledTasks[i].rep then -- not a repeated process - if scheduledTasks[i].t <= timer.getTime() then - local task = scheduledTasks[i] -- local reference - table.remove(scheduledTasks, i) - local err, errmsg = pcall(task.f, unpack(task.vars, 1, table.maxn(task.vars))) - if not err then - log:error('Error in scheduled function: $1', errmsg) - end - --task.f(unpack(task.vars, 1, table.maxn(task.vars))) -- do the task, do not increment i - else - i = i + 1 - end - else - if scheduledTasks[i].st and scheduledTasks[i].st <= timer.getTime() then --if a stoptime was specified, and the stop time exceeded - table.remove(scheduledTasks, i) -- stop time exceeded, do not execute, do not increment i - elseif scheduledTasks[i].t <= timer.getTime() then - local task = scheduledTasks[i] -- local reference - task.t = timer.getTime() + task.rep --schedule next run - local err, errmsg = pcall(task.f, unpack(task.vars, 1, table.maxn(task.vars))) - if not err then - log:error('Error in scheduled function: $1' .. errmsg) - end - --scheduledTasks[i].f(unpack(scheduledTasks[i].vars, 1, table.maxn(scheduledTasks[i].vars))) -- do the task - i = i + 1 - else - i = i + 1 - end - end - end - end - - -- Event handler to start creating the dead_objects table - local function addDeadObject(event) - if event.id == world.event.S_EVENT_DEAD or event.id == world.event.S_EVENT_CRASH then - if event.initiator and event.initiator.id_ and event.initiator.id_ > 0 then - - local id = event.initiator.id_ -- initial ID, could change if there is a duplicate id_ already dead. - local val = {object = event.initiator} -- the new entry in mist.DBs.deadObjects. - - local original_id = id --only for duplicate runtime IDs. - local id_ind = 1 - while mist.DBs.deadObjects[id] do - log:info('duplicate runtime id of previously dead object id: $1', id) - id = tostring(original_id) .. ' #' .. tostring(id_ind) - id_ind = id_ind + 1 - end - - if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then - log:info('object found in alive_units') - val.objectData = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) - local pos = Object.getPosition(val.object) - if pos then - val.objectPos = pos.p - end - val.objectType = mist.DBs.aliveUnits[val.object.id_].category - --[[if mist.DBs.activeHumans[Unit.getName(val.object)] then - --trigger.action.outText('remove via death: ' .. Unit.getName(val.object),20) - mist.DBs.activeHumans[Unit.getName(val.object)] = nil - end]] - elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units - log:info('object found in old_alive_units') - val.objectData = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) - local pos = Object.getPosition(val.object) - if pos then - val.objectPos = pos.p - end - val.objectType = mist.DBs.removedAliveUnits[val.object.id_].category - - else --attempt to determine if static object... - log:info('object not found in alive units or old alive units') - local pos = Object.getPosition(val.object) - if pos then - local static_found = false - for ind, static in pairs(mist.DBs.unitsByCat.static) do - if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... - log:info('correlated dead static object to position') - val.objectData = static - val.objectPos = pos.p - val.objectType = 'static' - static_found = true - break - end - end - if not static_found then - val.objectPos = pos.p - val.objectType = 'building' - end - else - val.objectType = 'unknown' - end - end - mist.DBs.deadObjects[id] = val - - end - end - end - - --[[ - local function addClientsToActive(event) - if event.id == world.event.S_EVENT_PLAYER_ENTER_UNIT or event.id == world.event.S_EVENT_BIRTH then - log:info(mist.utils.tableShow(event)) - if Unit.getPlayerName(event.initiator) then - log:info(Unit.getPlayerName(event.initiator)) - local newU = mist.utils.deepCopy(mist.DBs.unitsByName[Unit.getName(event.initiator)]) - newU.playerName = Unit.getPlayerName(event.initiator) - mist.DBs.activeHumans[Unit.getName(event.initiator)] = newU - --trigger.action.outText('added: ' .. Unit.getName(event.initiator), 20) - end - elseif event.id == world.event.S_EVENT_PLAYER_LEAVE_UNIT and event.initiator then - if mist.DBs.activeHumans[Unit.getName(event.initiator)] then - mist.DBs.activeHumans[Unit.getName(event.initiator)] = nil - -- trigger.action.outText('removed via control: ' .. Unit.getName(event.initiator), 20) - end - end - end - - mist.addEventHandler(addClientsToActive) - ]] - - --- init function. - -- creates logger, adds default event handler - -- and calls main the first time. - -- @function mist.init - function mist.init() - -- create logger - mist.log = mist.Logger:new("MIST") - log = mist.log -- log shorthand - -- set warning log level, showing only - -- warnings and errors - log:setLevel("warning") - - log:info("initializing databases") - initDBs() - - -- add event handler for group spawns - mist.addEventHandler(groupSpawned) - mist.addEventHandler(addDeadObject) - - -- call main the first time therafter it reschedules itself. - mist.main() - - log:msg('MIST version $1.$2.$3 loaded', mist.majorVersion, mist.minorVersion, mist.build) - end - - --- The main function. - -- Run 100 times per second. - -- You shouldn't call this function. - function mist.main() - timer.scheduleFunction(mist.main, {}, timer.getTime() + 0.01) --reschedule first in case of Lua error - - writeDbTableCounter = writeDbTableCounter + 1 - if writeDbTableCounter == 10 then - writeDbTableCounter = 0 - - if not coroutines.updateDBTables then - coroutines.updateDBTables = coroutine.create(updateDBTables) - end - - coroutine.resume(coroutines.updateDBTables) - - if coroutine.status(coroutines.updateDBTables) == 'dead' then - coroutines.updateDBTables = nil - end - end - - checkSpawnEventsCounter = checkSpawnEventsCounter + 1 - if checkSpawnEventsCounter == 10 then - checkSpawnEventsCounter = 0 - - if not coroutines.checkSpawnedEvents then - coroutines.checkSpawnedEvents = coroutine.create(checkSpawnedEvents) - end - - coroutine.resume(coroutines.checkSpawnedEvents) - - if coroutine.status(coroutines.checkSpawnedEvents) == 'dead' then - coroutines.checkSpawnedEvents = nil - end - end - - --updating alive units - updateAliveUnitsCounter = updateAliveUnitsCounter + 1 - if updateAliveUnitsCounter == 5 then - updateAliveUnitsCounter = 0 - - if not coroutines.update_alive_units then - coroutines.updateAliveUnits = coroutine.create(updateAliveUnits) - end - - coroutine.resume(coroutines.updateAliveUnits) - - if coroutine.status(coroutines.updateAliveUnits) == 'dead' then - coroutines.updateAliveUnits = nil - end - end - - doScheduledFunctions() - end -- end of mist.main - - --- Returns next unit id. - -- @treturn number next unit id. - function mist.getNextUnitId() - mist.nextUnitId = mist.nextUnitId + 1 - if mist.nextUnitId > 6900 then - mist.nextUnitId = 14000 - end - return mist.nextUnitId - end - - --- Returns next group id. - -- @treturn number next group id. - function mist.getNextGroupId() - mist.nextGroupId = mist.nextGroupId + 1 - if mist.nextGroupId > 6900 then - mist.nextGroupId = 14000 - end - return mist.nextGroupId - end - - --- Returns timestamp of last database update. - -- @treturn timestamp of last database update - function mist.getLastDBUpdateTime() - return lastUpdateTime - end - - --- Spawns a static object to the game world. - -- @todo write good docs - -- @tparam table staticObj table containing data needed for the object creation - function mist.dynAddStatic(staticObj) - local newObj = {} - newObj.groupId = staticObj.groupId - newObj.category = staticObj.category - newObj.type = staticObj.type - newObj.unitId = staticObj.unitId - newObj.y = staticObj.y - newObj.x = staticObj.x - newObj.heading = staticObj.heading - newObj.name = staticObj.name - newObj.dead = staticObj.dead - newObj.country = staticObj.country - newObj.countryId = staticObj.countryId - newObj.clone = staticObj.clone - newObj.shape_name = staticObj.shape_name - newObj.canCargo = staticObj.canCargo - newObj.mass = staticObj.mass - newObj.categoryStatic = staticObj.categoryStatic - if staticObj.units then -- if its mist format - newObj.groupId = staticObj.units[1].groupId - newObj.category = staticObj.units[1].category - newObj.type = staticObj.units[1].type - newObj.unitId = staticObj.units[1].unitId - newObj.y = staticObj.units[1].y - newObj.x = staticObj.units[1].x - newObj.heading = staticObj.units[1].heading - newObj.name = staticObj.units[1].name - newObj.dead = staticObj.units[1].dead - newObj.country = staticObj.units[1].country - newObj.countryId = staticObj.units[1].countryId - newObj.shape_name = staticObj.units[1].shape_name - newObj.canCargo = staticObj.units[1].canCargo - newObj.mass = staticObj.units[1].mass - newObj.categoryStatic = staticObj.units[1].categoryStatic - end - - if not newObj.country then - return false - end - - local newCountry = newObj.country - if newObj.countryId then - newCountry = newObj.countryId - end - for countryId, countryName in pairs(country.name) do - if type(newObj.country) == 'string' then - if tostring(countryName) == string.upper(newObj.country) then - newCountry = countryName - end - elseif type(newObj.country) == 'number' then - if countryId == newObj.country then - newCountry = countryName - end - end - end - - if newObj.clone or not newObj.groupId then - mistGpId = mistGpId + 1 - newObj.groupId = mistGpId - end - - if newObj.clone or not newObj.unitId then - mistUnitId = mistUnitId + 1 - newObj.unitId = mistUnitId - end - - if newObj.clone or not newObj.name then - mistDynAddIndex = mistDynAddIndex + 1 - newObj.name = (country.name[newCountry] .. ' static ' .. mistDynAddIndex) - end - - if not newObj.dead then - newObj.dead = false - end - - if not newObj.heading then - newObj.heading = math.random(360) - end - - if newObj.categoryStatic then - newObj.category = newObj.categoryStatic - end - if newObj.mass then - newObj.category = 'Cargos' - end - mistAddedObjects[#mistAddedObjects + 1] = mist.utils.deepCopy(newObj) - if newObj.x and newObj.y and newObj.type and type(newObj.x) == 'number' and type(newObj.y) == 'number' and type(newObj.type) == 'string' then - coalition.addStaticObject(country.id[newCountry], newObj) - - return newObj - end - return false - end - - --- Spawns a dynamic group into the game world. - -- Same as coalition.add function in SSE. checks the passed data to see if its valid. - -- Will generate groupId, groupName, unitId, and unitName if needed - -- @tparam table newGroup table containting values needed for spawning a group. - function mist.dynAdd(newGroup) - - --mist.debug.writeData(mist.utils.serialize,{'msg', newGroup}, 'newGroupOrig.lua') - local cntry = newGroup.country - if newGroup.countryId then - cntry = newGroup.countryId - end - - local groupType = newGroup.category - local newCountry = '' - -- validate data - for countryId, countryName in pairs(country.name) do - if type(cntry) == 'string' then - cntry = cntry:gsub("%s+", "_") - if tostring(countryName) == string.upper(cntry) then - newCountry = countryName - end - elseif type(cntry) == 'number' then - if countryId == cntry then - newCountry = countryName - end - end - end - - if newCountry == '' then - return false - end - - local newCat = '' - for catName, catId in pairs(Unit.Category) do - if type(groupType) == 'string' then - if tostring(catName) == string.upper(groupType) then - newCat = catName - end - elseif type(groupType) == 'number' then - if catId == groupType then - newCat = catName - end - end - - if catName == 'GROUND_UNIT' and (string.upper(groupType) == 'VEHICLE' or string.upper(groupType) == 'GROUND') then - newCat = 'GROUND_UNIT' - elseif catName == 'AIRPLANE' and string.upper(groupType) == 'PLANE' then - newCat = 'AIRPLANE' - end - end - local typeName - if newCat == 'GROUND_UNIT' then - typeName = ' gnd ' - elseif newCat == 'AIRPLANE' then - typeName = ' air ' - elseif newCat == 'HELICOPTER' then - typeName = ' hel ' - elseif newCat == 'SHIP' then - typeName = ' shp ' - elseif newCat == 'BUILDING' then - typeName = ' bld ' - end - if newGroup.clone or not newGroup.groupId then - mistDynAddIndex = mistDynAddIndex + 1 - mistGpId = mistGpId + 1 - newGroup.groupId = mistGpId - end - if newGroup.groupName or newGroup.name then - if newGroup.groupName then - newGroup.name = newGroup.groupName - elseif newGroup.name then - newGroup.name = newGroup.name - end - end - - if newGroup.clone and mist.DBs.groupsByName[newGroup.name] or not newGroup.name then - newGroup.name = tostring(tostring(country.name[cntry]) .. tostring(typeName) .. mistDynAddIndex) - end - - if not newGroup.hidden then - newGroup.hidden = false - end - - if not newGroup.visible then - newGroup.visible = false - end - - if (newGroup.start_time and type(newGroup.start_time) ~= 'number') or not newGroup.start_time then - if newGroup.startTime then - newGroup.start_time = mist.utils.round(newGroup.startTime) - else - newGroup.start_time = 0 - end - end - - - for unitIndex, unitData in pairs(newGroup.units) do - local originalName = newGroup.units[unitIndex].unitName or newGroup.units[unitIndex].name - if newGroup.clone or not unitData.unitId then - mistUnitId = mistUnitId + 1 - newGroup.units[unitIndex].unitId = mistUnitId - end - if newGroup.units[unitIndex].unitName or newGroup.units[unitIndex].name then - if newGroup.units[unitIndex].unitName then - newGroup.units[unitIndex].name = newGroup.units[unitIndex].unitName - elseif newGroup.units[unitIndex].name then - newGroup.units[unitIndex].name = newGroup.units[unitIndex].name - end - end - if newGroup.clone or not unitData.name then - newGroup.units[unitIndex].name = tostring(newGroup.name .. ' unit' .. unitIndex) - end - - if not unitData.skill then - newGroup.units[unitIndex].skill = 'Random' - end - - if not unitData.alt then - if newCat == 'AIRPLANE' then - newGroup.units[unitIndex].alt = 2000 - newGroup.units[unitIndex].alt_type = 'RADIO' - newGroup.units[unitIndex].speed = 150 - elseif newCat == 'HELICOPTER' then - newGroup.units[unitIndex].alt = 500 - newGroup.units[unitIndex].alt_type = 'RADIO' - newGroup.units[unitIndex].speed = 60 - else - --[[log:info('check height') - newGroup.units[unitIndex].alt = land.getHeight({x = newGroup.units[unitIndex].x, y = newGroup.units[unitIndex].y}) - newGroup.units[unitIndex].alt_type = 'BARO']] - end - - - end - - if newCat == 'AIRPLANE' or newCat == 'HELICOPTER' then - if newGroup.units[unitIndex].alt_type and newGroup.units[unitIndex].alt_type ~= 'BARO' or not newGroup.units[unitIndex].alt_type then - newGroup.units[unitIndex].alt_type = 'RADIO' - end - if not unitData.speed then - if newCat == 'AIRPLANE' then - newGroup.units[unitIndex].speed = 150 - elseif newCat == 'HELICOPTER' then - newGroup.units[unitIndex].speed = 60 - end - end - if not unitData.payload then - newGroup.units[unitIndex].payload = mist.getPayload(originalName) - end - end - mistAddedObjects[#mistAddedObjects + 1] = mist.utils.deepCopy(newGroup.units[unitIndex]) - end - mistAddedGroups[#mistAddedGroups + 1] = mist.utils.deepCopy(newGroup) - if newGroup.route and not newGroup.route.points then - if not newGroup.route.points and newGroup.route[1] then - local copyRoute = newGroup.route - newGroup.route = {} - newGroup.route.points = copyRoute - end - end - newGroup.country = newCountry - - - --mist.debug.writeData(mist.utils.serialize,{'msg', newGroup}, 'newGroup.lua') - - -- sanitize table - newGroup.groupName = nil - newGroup.clone = nil - newGroup.category = nil - newGroup.country = nil - - newGroup.tasks = {} - - for unitIndex, unitData in pairs(newGroup.units) do - newGroup.units[unitIndex].unitName = nil - end - - coalition.addGroup(country.id[newCountry], Unit.Category[newCat], newGroup) - - return newGroup - - end - - --- Schedules a function. - -- Modified Slmod task scheduler, superior to timer.scheduleFunction - -- @tparam function f function to schedule - -- @tparam table vars array containing all parameters passed to the function - -- @tparam number t time in seconds from mission start to schedule the function to. - -- @tparam[opt] number rep time between repetitions of the function - -- @tparam[opt] number st time in seconds from mission start at which the function - -- should stop to be rescheduled. - -- @treturn number scheduled function id. - function mist.scheduleFunction(f, vars, t, rep, st) - --verify correct types - assert(type(f) == 'function', 'variable 1, expected function, got ' .. type(f)) - assert(type(vars) == 'table' or vars == nil, 'variable 2, expected table or nil, got ' .. type(f)) - assert(type(t) == 'number', 'variable 3, expected number, got ' .. type(t)) - assert(type(rep) == 'number' or rep == nil, 'variable 4, expected number or nil, got ' .. type(rep)) - assert(type(st) == 'number' or st == nil, 'variable 5, expected number or nil, got ' .. type(st)) - if not vars then - vars = {} - end - taskId = taskId + 1 - table.insert(scheduledTasks, {f = f, vars = vars, t = t, rep = rep, st = st, id = taskId}) - return taskId - end - - --- Removes a scheduled function. - -- @tparam number id function id - -- @treturn boolean true if function was successfully removed, false otherwise. - function mist.removeFunction(id) - local i = 1 - while i <= #scheduledTasks do - if scheduledTasks[i].id == id then - table.remove(scheduledTasks, i) - else - i = i + 1 - end - end - end - - --- Registers an event handler. - -- @tparam function f function handling event - -- @treturn number id of the event handler - function mist.addEventHandler(f) --id is optional! - local handler = {} - idNum = idNum + 1 - handler.id = idNum - handler.f = f - function handler:onEvent(event) - self.f(event) - end - world.addEventHandler(handler) - return handler.id - end - - --- Removes event handler with given id. - -- @tparam number id event handler id - -- @treturn boolean true on success, false otherwise - function mist.removeEventHandler(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 - - --- Returns MGRS coordinates as string. - -- @tparam string MGRS MGRS coordinates - -- @tparam number acc the accuracy of each easting/northing. - -- Can be: 0, 1, 2, 3, 4, or 5. - function mist.tostringMGRS(MGRS, acc) - if acc == 0 then - return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph - else - return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph .. ' ' .. string.format('%0' .. acc .. 'd', mist.utils.round(MGRS.Easting/(10^(5-acc)), 0)) - .. ' ' .. string.format('%0' .. acc .. 'd', mist.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. - ]] - function mist.tostringLL(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 = mist.utils.round((oldLatMin - latMin)*60, acc) - - local oldLonMin = lonMin - lonMin = math.floor(lonMin) - local lonSec = mist.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 = mist.utils.round(latMin, acc) - lonMin = mist.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.]] - function mist.tostringBR(az, dist, alt, metric) - az = mist.utils.round(mist.utils.toDegree(az), 0) - - if metric then - dist = mist.utils.round(dist/1000, 0) - else - dist = mist.utils.round(mist.utils.metersToNM(dist), 0) - end - - local s = string.format('%03d', az) .. ' for ' .. dist - - if alt then - if metric then - s = s .. ' at ' .. mist.utils.round(alt, 0) - else - s = s .. ' at ' .. mist.utils.round(mist.utils.metersToFeet(alt), 0) - end - end - return s - end - - function mist.getNorthCorrection(gPoint) --gets the correction needed for true north - local point = mist.utils.deepCopy(gPoint) - 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 - - --- Returns skill of the given unit. - -- @tparam string unitName unit name - -- @return skill of the unit - function mist.getUnitSkill(unitName) - if Unit.getByName(unitName) and Unit.getByName(unitName):isExist() == true then - local lunit = Unit.getByName(unitName) - for name, data in pairs(mist.DBs.unitsByName) do - if name == unitName and data.type == lunit:getTypeName() and data.unitId == lunit:getID() and data.skill then - return data.skill - end - end - end - return false - end - - --- Returns an array containing a group's units positions. - -- e.g. - -- { - -- [1] = {x = 299435.224, y = -1146632.6773}, - -- [2] = {x = 663324.6563, y = 322424.1112} - -- } - -- @tparam number|string groupIdent group id or name - -- @treturn table array containing positions of each group member - function mist.getGroupPoints(groupIdent) - -- search by groupId and allow groupId and groupName as inputs - local gpId = groupIdent - if type(groupIdent) == 'string' and not tonumber(groupIdent) then - gpId = mist.DBs.MEgroupsByName[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 - if not point.point then - points[point_num] = { x = point.x, y = point.y } - else - points[point_num] = point.point --it's possible that the ME could move to the point = Vec2 notation. - end - 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 - - --- getUnitAttitude(unit) return values. - -- Yaw, AoA, ClimbAngle - relative to earth reference - -- DOES NOT TAKE INTO ACCOUNT WIND. - -- @table attitude - -- @tfield number Heading in radians, range of 0 to 2*pi, - -- relative to true north. - -- @tfield number Pitch in radians, range of -pi/2 to pi/2 - -- @tfield number Roll in radians, range of 0 to 2*pi, - -- right roll is positive direction. - -- @tfield number Yaw in radians, range of -pi to pi, - -- right yaw is positive direction. - -- @tfield number AoA in radians, range of -pi to pi, - -- rotation of aircraft to the right in comparison to - -- flight direction being positive. - -- @tfield number ClimbAngle in radians, range of -pi/2 to pi/2 - - --- Returns the attitude of a given unit. - -- Will work on any unit, even if not an aircraft. - -- @tparam Unit unit unit whose attitude is returned. - -- @treturn table @{attitude} - function mist.getAttitude(unit) - local unitpos = unit:getPosition() - if unitpos then - - local Heading = math.atan2(unitpos.x.z, unitpos.x.x) - - Heading = Heading + mist.getNorthCorrection(unitpos.p) - - if Heading < 0 then - Heading = Heading + 2*math.pi -- put heading in range of 0 to 2*pi - end - ---- heading complete.---- - - local Pitch = math.asin(unitpos.x.y) - ---- pitch complete.---- - - -- now get roll: - --maybe not the best way to do it, but it works. - - --first, make a vector that is perpendicular to y and unitpos.x with cross product - local cp = mist.vec.cp(unitpos.x, {x = 0, y = 1, z = 0}) - - --now, get dot product of of this cross product with unitpos.z - local dp = mist.vec.dp(cp, unitpos.z) - - --now get the magnitude of the roll (magnitude of the angle between two vectors is acos(vec1.vec2/|vec1||vec2|) - local Roll = math.acos(dp/(mist.vec.mag(cp)*mist.vec.mag(unitpos.z))) - - --now, have to get sign of roll. - -- by convention, making right roll positive - -- to get sign of roll, use the y component of unitpos.z. For right roll, y component is negative. - - if unitpos.z.y > 0 then -- left roll, flip the sign of the roll - Roll = -Roll - end - ---- roll complete. ---- - - --now, work on yaw, AoA, climb, and abs velocity - local Yaw - local AoA - local ClimbAngle - - -- get unit velocity - local unitvel = unit:getVelocity() - if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! - local AxialVel = {} --unit velocity transformed into aircraft axes directions - - --transform velocity components in direction of aircraft axes. - AxialVel.x = mist.vec.dp(unitpos.x, unitvel) - AxialVel.y = mist.vec.dp(unitpos.y, unitvel) - AxialVel.z = mist.vec.dp(unitpos.z, unitvel) - - --Yaw is the angle between unitpos.x and the x and z velocities - --define right yaw as positive - Yaw = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = 0, z = AxialVel.z})/mist.vec.mag({x = AxialVel.x, y = 0, z = AxialVel.z})) - - --now set correct direction: - if AxialVel.z > 0 then - Yaw = -Yaw - end - - -- AoA is angle between unitpos.x and the x and y velocities - AoA = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = AxialVel.y, z = 0})/mist.vec.mag({x = AxialVel.x, y = AxialVel.y, z = 0})) - - --now set correct direction: - if AxialVel.y > 0 then - AoA = -AoA - end - - ClimbAngle = math.asin(unitvel.y/mist.vec.mag(unitvel)) - end - return { Heading = Heading, Pitch = Pitch, Roll = Roll, Yaw = Yaw, AoA = AoA, ClimbAngle = ClimbAngle} - else - log:error("Couldn't get unit's position") - end - end - - --- Returns heading of given unit. - -- @tparam Unit unit unit whose heading is returned. - -- @param rawHeading - -- @treturn number heading of the unit, in range - -- of 0 to 2*pi. - function mist.getHeading(unit, rawHeading) - local unitpos = unit:getPosition() - if unitpos then - local Heading = math.atan2(unitpos.x.z, unitpos.x.x) - if not rawHeading then - Heading = Heading + mist.getNorthCorrection(unitpos.p) - end - if Heading < 0 then - Heading = Heading + 2*math.pi -- put heading in range of 0 to 2*pi - end - return Heading - end - end - - --- Returns given unit's pitch - -- @tparam Unit unit unit whose pitch is returned. - -- @treturn number pitch of given unit - function mist.getPitch(unit) - local unitpos = unit:getPosition() - if unitpos then - return math.asin(unitpos.x.y) - end - end - - --- Returns given unit's roll. - -- @tparam Unit unit unit whose roll is returned. - -- @treturn number roll of given unit - function mist.getRoll(unit) - local unitpos = unit:getPosition() - if unitpos then - -- now get roll: - --maybe not the best way to do it, but it works. - - --first, make a vector that is perpendicular to y and unitpos.x with cross product - local cp = mist.vec.cp(unitpos.x, {x = 0, y = 1, z = 0}) - - --now, get dot product of of this cross product with unitpos.z - local dp = mist.vec.dp(cp, unitpos.z) - - --now get the magnitude of the roll (magnitude of the angle between two vectors is acos(vec1.vec2/|vec1||vec2|) - local Roll = math.acos(dp/(mist.vec.mag(cp)*mist.vec.mag(unitpos.z))) - - --now, have to get sign of roll. - -- by convention, making right roll positive - -- to get sign of roll, use the y component of unitpos.z. For right roll, y component is negative. - - if unitpos.z.y > 0 then -- left roll, flip the sign of the roll - Roll = -Roll - end - return Roll - end - end - - --- Returns given unit's yaw. - -- @tparam Unit unit unit whose yaw is returned. - -- @treturn number yaw of given unit. - function mist.getYaw(unit) - local unitpos = unit:getPosition() - if unitpos then - -- get unit velocity - local unitvel = unit:getVelocity() - if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! - local AxialVel = {} --unit velocity transformed into aircraft axes directions - - --transform velocity components in direction of aircraft axes. - AxialVel.x = mist.vec.dp(unitpos.x, unitvel) - AxialVel.y = mist.vec.dp(unitpos.y, unitvel) - AxialVel.z = mist.vec.dp(unitpos.z, unitvel) - - --Yaw is the angle between unitpos.x and the x and z velocities - --define right yaw as positive - local Yaw = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = 0, z = AxialVel.z})/mist.vec.mag({x = AxialVel.x, y = 0, z = AxialVel.z})) - - --now set correct direction: - if AxialVel.z > 0 then - Yaw = -Yaw - end - return Yaw - end - end - end - - --- Returns given unit's angle of attack. - -- @tparam Unit unit unit to get AoA from. - -- @treturn number angle of attack of the given unit. - function mist.getAoA(unit) - local unitpos = unit:getPosition() - if unitpos then - local unitvel = unit:getVelocity() - if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! - local AxialVel = {} --unit velocity transformed into aircraft axes directions - - --transform velocity components in direction of aircraft axes. - AxialVel.x = mist.vec.dp(unitpos.x, unitvel) - AxialVel.y = mist.vec.dp(unitpos.y, unitvel) - AxialVel.z = mist.vec.dp(unitpos.z, unitvel) - - -- AoA is angle between unitpos.x and the x and y velocities - local AoA = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = AxialVel.y, z = 0})/mist.vec.mag({x = AxialVel.x, y = AxialVel.y, z = 0})) - - --now set correct direction: - if AxialVel.y > 0 then - AoA = -AoA - end - return AoA - end - end - end - - --- Returns given unit's climb angle. - -- @tparam Unit unit unit to get climb angle from. - -- @treturn number climb angle of given unit. - function mist.getClimbAngle(unit) - local unitpos = unit:getPosition() - if unitpos then - local unitvel = unit:getVelocity() - if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! - return math.asin(unitvel.y/mist.vec.mag(unitvel)) - end - end - end - - --[[-- - Unit name table. - Many Mist functions require tables of unit names, which are known - in Mist as UnitNameTables. These follow a special set of shortcuts - borrowed from Slmod. These shortcuts alleviate the problem of entering - huge lists of unit names by hand, and in many cases, they remove the - need to even know the names of the units in the first place! - - These are the unit table "short-cut" commands: - - Prefixes: - "[-u]" - subtract this unit if its in the table - "[g]" - add this group to the table - "[-g]" - subtract this group from the table - "[c]" - add this country's units - "[-c]" - subtract this country's units if any are in the table - - Stand-alone identifiers - "[all]" - add all units - "[-all]" - subtract all units (not very useful by itself) - "[blue]" - add all blue units - "[-blue]" - subtract all blue units - "[red]" - add all red coalition units - "[-red]" - subtract all red units - - Compound Identifiers: - "[c][helicopter]" - add all of this country's helicopters - "[-c][helicopter]" - subtract all of this country's helicopters - "[c][plane]" - add all of this country's planes - "[-c][plane]" - subtract all of this country's planes - "[c][ship]" - add all of this country's ships - "[-c][ship]" - subtract all of this country's ships - "[c][vehicle]" - add all of this country's vehicles - "[-c][vehicle]" - subtract all of this country's vehicles - - "[all][helicopter]" - add all helicopters - "[-all][helicopter]" - subtract all helicopters - "[all][plane]" - add all planes - "[-all][plane]" - subtract all planes - "[all][ship]" - add all ships - "[-all][ship]" - subtract all ships - "[all][vehicle]" - add all vehicles - "[-all][vehicle]" - subtract all vehicles - - "[blue][helicopter]" - add all blue coalition helicopters - "[-blue][helicopter]" - subtract all blue coalition helicopters - "[blue][plane]" - add all blue coalition planes - "[-blue][plane]" - subtract all blue coalition planes - "[blue][ship]" - add all blue coalition ships - "[-blue][ship]" - subtract all blue coalition ships - "[blue][vehicle]" - add all blue coalition vehicles - "[-blue][vehicle]" - subtract all blue coalition vehicles - - "[red][helicopter]" - add all red coalition helicopters - "[-red][helicopter]" - subtract all red coalition helicopters - "[red][plane]" - add all red coalition planes - "[-red][plane]" - subtract all red coalition planes - "[red][ship]" - add all red coalition ships - "[-red][ship]" - subtract all red coalition ships - "[red][vehicle]" - add all red coalition vehicles - "[-red][vehicle]" - subtract all red coalition vehicles - - Country names to be used in [c] and [-c] short-cuts: - Turkey - Norway - The Netherlands - Spain - 11 - UK - Denmark - USA - Georgia - Germany - Belgium - Canada - France - Israel - Ukraine - Russia - South Ossetia - Abkhazia - Italy - Australia - Austria - Belarus - Bulgaria - Czech Republic - China - Croatia - Finland - Greece - Hungary - India - Iran - Iraq - Japan - Kazakhstan - North Korea - Pakistan - Poland - Romania - Saudi Arabia - Serbia, Slovakia - South Korea - Sweden - Switzerland - Syria - USAF Aggressors - - Do NOT use a '[u]' notation for single units. Single units are referenced - the same way as before: Simply input their names as strings. - - These unit tables are evaluated in order, and you cannot subtract a unit - from a table before it is added. For example: - - {'[blue]', '[-c]Georgia'} - - will evaluate to all of blue coalition except those units owned by the - country named "Georgia"; however: - - {'[-c]Georgia', '[blue]'} - - will evaluate to all of the units in blue coalition, because the addition - of all units owned by blue coalition occurred AFTER the subtraction of all - units owned by Georgia (which actually subtracted nothing at all, since - there were no units in the table when the subtraction occurred). - - More examples: - - {'[blue][plane]', '[-c]Georgia', '[-g]Hawg 1'} - - Evaluates to all blue planes, except those blue units owned by the country - named "Georgia" and the units in the group named "Hawg1". - - - {'[g]arty1', '[g]arty2', '[-u]arty1_AD', '[-u]arty2_AD', 'Shark 11' } - - Evaluates to the unit named "Shark 11", plus all the units in groups named - "arty1" and "arty2" except those that are named "arty1\_AD" and "arty2\_AD". - - @table UnitNameTable - ]] - - --- Returns a table containing unit names. - -- @tparam table tbl sequential strings - -- @treturn table @{UnitNameTable} - function mist.makeUnitTable(tbl) - --Assumption: will be passed a table of strings, sequential - local units_by_name = {} - - local l_munits = mist.DBs.units --local reference for faster execution - for i = 1, #tbl do - local unit = tbl[i] - if unit:sub(1,4) == '[-u]' then --subtract a unit - if units_by_name[unit:sub(5)] then -- 5 to end - units_by_name[unit:sub(5)] = nil --remove - end - elseif unit:sub(1,3) == '[g]' then -- add a group - for coa, coa_tbl in pairs(l_munits) do - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' and group_tbl.groupName == unit:sub(4) then - -- index 4 to end - for unit_ind, unit in pairs(group_tbl.units) do - units_by_name[unit.unitName] = true --add - end - end - end - end - end - end - end - elseif unit:sub(1,4) == '[-g]' then -- subtract a group - for coa, coa_tbl in pairs(l_munits) do - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' and group_tbl.groupName == unit:sub(5) then - -- index 5 to end - for unit_ind, unit in pairs(group_tbl.units) do - if units_by_name[unit.unitName] then - units_by_name[unit.unitName] = nil --remove - end - end - end - end - end - end - end - end - elseif unit:sub(1,3) == '[c]' then -- add a country - local category = '' - local country_start = 4 - if unit:sub(4,15) == '[helicopter]' then - category = 'helicopter' - country_start = 16 - elseif unit:sub(4,10) == '[plane]' then - category = 'plane' - country_start = 11 - elseif unit:sub(4,9) == '[ship]' then - category = 'ship' - country_start = 10 - elseif unit:sub(4,12) == '[vehicle]' then - category = 'vehicle' - country_start = 13 - end - for coa, coa_tbl in pairs(l_munits) do - for country, country_table in pairs(coa_tbl) do - if country == string.lower(unit:sub(country_start)) then -- match - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - units_by_name[unit.unitName] = true --add - end - end - end - end - end - end - end - end - elseif unit:sub(1,4) == '[-c]' then -- subtract a country - local category = '' - local country_start = 5 - if unit:sub(5,16) == '[helicopter]' then - category = 'helicopter' - country_start = 17 - elseif unit:sub(5,11) == '[plane]' then - category = 'plane' - country_start = 12 - elseif unit:sub(5,10) == '[ship]' then - category = 'ship' - country_start = 11 - elseif unit:sub(5,13) == '[vehicle]' then - category = 'vehicle' - country_start = 14 - end - for coa, coa_tbl in pairs(l_munits) do - for country, country_table in pairs(coa_tbl) do - if country == string.lower(unit:sub(country_start)) then -- match - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - if units_by_name[unit.unitName] then - units_by_name[unit.unitName] = nil --remove - end - end - end - end - end - end - end - end - end - elseif unit:sub(1,6) == '[blue]' then -- add blue coalition - local category = '' - if unit:sub(7) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(7) == '[plane]' then - category = 'plane' - elseif unit:sub(7) == '[ship]' then - category = 'ship' - elseif unit:sub(7) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - if coa == 'blue' then - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - units_by_name[unit.unitName] = true --add - end - end - end - end - end - end - end - end - elseif unit:sub(1,7) == '[-blue]' then -- subtract blue coalition - local category = '' - if unit:sub(8) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(8) == '[plane]' then - category = 'plane' - elseif unit:sub(8) == '[ship]' then - category = 'ship' - elseif unit:sub(8) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - if coa == 'blue' then - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - if units_by_name[unit.unitName] then - units_by_name[unit.unitName] = nil --remove - end - end - end - end - end - end - end - end - end - elseif unit:sub(1,5) == '[red]' then -- add red coalition - local category = '' - if unit:sub(6) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(6) == '[plane]' then - category = 'plane' - elseif unit:sub(6) == '[ship]' then - category = 'ship' - elseif unit:sub(6) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - if coa == 'red' then - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - units_by_name[unit.unitName] = true --add - end - end - end - end - end - end - end - end - elseif unit:sub(1,6) == '[-red]' then -- subtract red coalition - local category = '' - if unit:sub(7) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(7) == '[plane]' then - category = 'plane' - elseif unit:sub(7) == '[ship]' then - category = 'ship' - elseif unit:sub(7) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - if coa == 'red' then - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - if units_by_name[unit.unitName] then - units_by_name[unit.unitName] = nil --remove - end - end - end - end - end - end - end - end - end - elseif unit:sub(1,5) == '[all]' then -- add all of a certain category (or all categories) - local category = '' - if unit:sub(6) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(6) == '[plane]' then - category = 'plane' - elseif unit:sub(6) == '[ship]' then - category = 'ship' - elseif unit:sub(6) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - units_by_name[unit.unitName] = true --add - end - end - end - end - end - end - end - elseif unit:sub(1,6) == '[-all]' then -- subtract all of a certain category (or all categories) - local category = '' - if unit:sub(7) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(7) == '[plane]' then - category = 'plane' - elseif unit:sub(7) == '[ship]' then - category = 'ship' - elseif unit:sub(7) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - if units_by_name[unit.unitName] then - units_by_name[unit.unitName] = nil --remove - end - end - end - end - end - end - end - end - else -- just a regular unit - units_by_name[unit] = true --add - end - end - - local units_tbl = {} -- indexed sequentially - for unit_name, val in pairs(units_by_name) do - if val then - units_tbl[#units_tbl + 1] = unit_name -- add all the units to the table - end - end - - - units_tbl.processed = timer.getTime() --add the processed flag - return units_tbl + local coroutines = {} + + local tempSpawnedUnits = {} -- birth events added here + local mistAddedObjects = {} -- mist.dynAdd unit data added here + local mistAddedGroups = {} -- mist.dynAdd groupdata added here + local writeGroups = {} + local lastUpdateTime = 0 + + local updateAliveUnitsCounter = 0 + local writeDbTableCounter = 0 + local checkSpawnEventsCounter = 0 + + local mistGpId = 7000 + local mistUnitId = 7000 + local mistDynAddIndex = 1 + + local scheduledTasks = {} + local taskId = 0 + local idNum = 0 + + mist.nextGroupId = 1 + mist.nextUnitId = 1 + + local function initDBs() -- mist.DBs scope + mist.DBs = {} + + mist.DBs.missionData = {} + if env.mission then + + mist.DBs.missionData.startTime = env.mission.start_time + mist.DBs.missionData.theatre = env.mission.theatre + mist.DBs.missionData.version = env.mission.version + mist.DBs.missionData.files = {} + if type(env.mission.resourceCounter) == 'table' then + for fIndex, fData in pairs (env.mission.resourceCounter) do + mist.DBs.missionData.files[#mist.DBs.missionData.files + 1] = mist.utils.deepCopy(fIndex) + end + end + -- if we add more coalition specific data then bullsye should be categorized by coaliton. For now its just the bullseye table + mist.DBs.missionData.bullseye = {red = {}, blue = {}} + mist.DBs.missionData.bullseye.red.x = env.mission.coalition.red.bullseye.x --should it be point.x? + mist.DBs.missionData.bullseye.red.y = env.mission.coalition.red.bullseye.y + mist.DBs.missionData.bullseye.blue.x = env.mission.coalition.blue.bullseye.x + mist.DBs.missionData.bullseye.blue.y = env.mission.coalition.blue.bullseye.y + end + + mist.DBs.zonesByName = {} + mist.DBs.zonesByNum = {} + + + if env.mission.triggers and env.mission.triggers.zones then + for zone_ind, zone_data in pairs(env.mission.triggers.zones) do + if type(zone_data) == 'table' then + local zone = mist.utils.deepCopy(zone_data) + zone.point = {} -- point is used by SSE + zone.point.x = zone_data.x + zone.point.y = 0 + zone.point.z = zone_data.y + + mist.DBs.zonesByName[zone_data.name] = zone + mist.DBs.zonesByNum[#mist.DBs.zonesByNum + 1] = mist.utils.deepCopy(zone) --[[deepcopy so that the zone in zones_by_name and the zone in + zones_by_num se are different objects.. don't want them linked.]] + end + end + end + + mist.DBs.navPoints = {} + mist.DBs.units = {} + --Build mist.db.units and mist.DBs.navPoints + 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 + mist.DBs.units[coa_name] = {} + + -- build nav points DB + mist.DBs.navPoints[coa_name] = {} + if coa_data.nav_points then --navpoints + --mist.debug.writeData (mist.utils.serialize,{'NavPoints',coa_data.nav_points}, 'NavPoints.txt') + for nav_ind, nav_data in pairs(coa_data.nav_points) do + + if type(nav_data) == 'table' then + mist.DBs.navPoints[coa_name][nav_ind] = mist.utils.deepCopy(nav_data) + + mist.DBs.navPoints[coa_name][nav_ind].name = nav_data.callsignStr -- name is a little bit more self-explanatory. + mist.DBs.navPoints[coa_name][nav_ind].point = {} -- point is used by SSE, support it. + mist.DBs.navPoints[coa_name][nav_ind].point.x = nav_data.x + mist.DBs.navPoints[coa_name][nav_ind].point.y = 0 + mist.DBs.navPoints[coa_name][nav_ind].point.z = nav_data.y + end + end + end + if coa_data.country then --there is a country table + for cntry_id, cntry_data in pairs(coa_data.country) do + + local countryName = string.lower(cntry_data.name) + mist.DBs.units[coa_name][countryName] = {} + mist.DBs.units[coa_name][countryName].countryId = cntry_data.id + + if type(cntry_data) == 'table' then --just making sure + + 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" or obj_type_name == "static" then --should be an unncessary check + + local category = obj_type_name + + 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! + + mist.DBs.units[coa_name][countryName][category] = {} + + for group_num, group_data in pairs(obj_type_data.group) do + + if group_data and group_data.units and type(group_data.units) == 'table' then --making sure again- this is a valid group + + mist.DBs.units[coa_name][countryName][category][group_num] = {} + local groupName = group_data.name + if env.mission.version > 7 then + groupName = env.getValueDictByKey(groupName) + end + mist.DBs.units[coa_name][countryName][category][group_num].groupName = groupName + mist.DBs.units[coa_name][countryName][category][group_num].groupId = group_data.groupId + mist.DBs.units[coa_name][countryName][category][group_num].category = category + mist.DBs.units[coa_name][countryName][category][group_num].coalition = coa_name + mist.DBs.units[coa_name][countryName][category][group_num].country = countryName + mist.DBs.units[coa_name][countryName][category][group_num].countryId = cntry_data.id + mist.DBs.units[coa_name][countryName][category][group_num].startTime = group_data.start_time + mist.DBs.units[coa_name][countryName][category][group_num].task = group_data.task + mist.DBs.units[coa_name][countryName][category][group_num].hidden = group_data.hidden + + mist.DBs.units[coa_name][countryName][category][group_num].units = {} + + mist.DBs.units[coa_name][countryName][category][group_num].radioSet = group_data.radioSet + mist.DBs.units[coa_name][countryName][category][group_num].uncontrolled = group_data.uncontrolled + mist.DBs.units[coa_name][countryName][category][group_num].frequency = group_data.frequency + mist.DBs.units[coa_name][countryName][category][group_num].modulation = group_data.modulation + + for unit_num, unit_data in pairs(group_data.units) do + local units_tbl = mist.DBs.units[coa_name][countryName][category][group_num].units --pointer to the units table for this group + + units_tbl[unit_num] = {} + if env.mission.version > 7 then + units_tbl[unit_num].unitName = env.getValueDictByKey(unit_data.name) + else + units_tbl[unit_num].unitName = unit_data.name + end + units_tbl[unit_num].type = unit_data.type + units_tbl[unit_num].skill = unit_data.skill --will be nil for statics + units_tbl[unit_num].unitId = unit_data.unitId + units_tbl[unit_num].category = category + units_tbl[unit_num].coalition = coa_name + units_tbl[unit_num].country = countryName + units_tbl[unit_num].countryId = cntry_data.id + units_tbl[unit_num].heading = unit_data.heading + units_tbl[unit_num].playerCanDrive = unit_data.playerCanDrive + units_tbl[unit_num].alt = unit_data.alt + units_tbl[unit_num].alt_type = unit_data.alt_type + units_tbl[unit_num].speed = unit_data.speed + units_tbl[unit_num].livery_id = unit_data.livery_id + if unit_data.point then --ME currently does not work like this, but it might one day + units_tbl[unit_num].point = unit_data.point + else + units_tbl[unit_num].point = {} + units_tbl[unit_num].point.x = unit_data.x + units_tbl[unit_num].point.y = unit_data.y + end + units_tbl[unit_num].x = unit_data.x + units_tbl[unit_num].y = unit_data.y + + units_tbl[unit_num].callsign = unit_data.callsign + units_tbl[unit_num].onboard_num = unit_data.onboard_num + units_tbl[unit_num].hardpoint_racks = unit_data.hardpoint_racks + units_tbl[unit_num].psi = unit_data.psi + + + units_tbl[unit_num].groupName = groupName + units_tbl[unit_num].groupId = group_data.groupId + + if unit_data.AddPropAircraft then + units_tbl[unit_num].AddPropAircraft = unit_data.AddPropAircraft + end + + if category == 'static' then + units_tbl[unit_num].categoryStatic = unit_data.category + units_tbl[unit_num].shape_name = unit_data.shape_name + if unit_data.mass then + units_tbl[unit_num].mass = unit_data.mass + end + + if unit_data.canCargo then + units_tbl[unit_num].canCargo = unit_data.canCargo + end + end + + end --for unit_num, unit_data in pairs(group_data.units) do + end --if group_data and group_data.units then + 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 --if type(cntry_data) == 'table' then + 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 + + mist.DBs.unitsByName = {} + mist.DBs.unitsById = {} + mist.DBs.unitsByCat = {} + + mist.DBs.unitsByCat.helicopter = {} -- adding default categories + mist.DBs.unitsByCat.plane = {} + mist.DBs.unitsByCat.ship = {} + mist.DBs.unitsByCat.static = {} + mist.DBs.unitsByCat.vehicle = {} + + mist.DBs.unitsByNum = {} + + mist.DBs.groupsByName = {} + mist.DBs.groupsById = {} + mist.DBs.humansByName = {} + mist.DBs.humansById = {} + + mist.DBs.dynGroupsAdded = {} -- will be filled by mist.dbUpdate from dynamically spawned groups + mist.DBs.activeHumans = {} + + mist.DBs.aliveUnits = {} -- will be filled in by the "updateAliveUnits" coroutine in mist.main. + + mist.DBs.removedAliveUnits = {} -- will be filled in by the "updateAliveUnits" coroutine in mist.main. + + mist.DBs.const = {} + + -- not accessible by SSE, must use static list :-/ + mist.DBs.const.callsigns = { + ['NATO'] = { + ['rules'] = { + ['groupLimit'] = 9, + }, + ['AWACS'] = { + ['Overlord'] = 1, + ['Magic'] = 2, + ['Wizard'] = 3, + ['Focus'] = 4, + ['Darkstar'] = 5, + }, + ['TANKER'] = { + ['Texaco'] = 1, + ['Arco'] = 2, + ['Shell'] = 3, + }, + ['JTAC'] = { + ['Axeman'] = 1, + ['Darknight'] = 2, + ['Warrior'] = 3, + ['Pointer'] = 4, + ['Eyeball'] = 5, + ['Moonbeam'] = 6, + ['Whiplash'] = 7, + ['Finger'] = 8, + ['Pinpoint'] = 9, + ['Ferret'] = 10, + ['Shaba'] = 11, + ['Playboy'] = 12, + ['Hammer'] = 13, + ['Jaguar'] = 14, + ['Deathstar'] = 15, + ['Anvil'] = 16, + ['Firefly'] = 17, + ['Mantis'] = 18, + ['Badger'] = 19, + }, + ['aircraft'] = { + ['Enfield'] = 1, + ['Springfield'] = 2, + ['Uzi'] = 3, + ['Colt'] = 4, + ['Dodge'] = 5, + ['Ford'] = 6, + ['Chevy'] = 7, + ['Pontiac'] = 8, + }, + + ['unique'] = { + ['A10'] = { + ['Hawg'] = 9, + ['Boar'] = 10, + ['Pig'] = 11, + ['Tusk'] = 12, + ['rules'] = { + ['canUseAircraft'] = true, + ['appliesTo'] = { + 'A-10C', + 'A-10A', + }, + }, + }, + }, + }, + } + -- create mist.DBs.oldAliveUnits + -- do + -- local intermediate_alive_units = {} -- between 0 and 0.5 secs old + -- local function make_old_alive_units() -- called every 0.5 secs, makes the old_alive_units DB which is just a copy of alive_units that is 0.5 to 1 sec old + -- if intermediate_alive_units then + -- mist.DBs.oldAliveUnits = mist.utils.deepCopy(intermediate_alive_units) + -- end + -- intermediate_alive_units = mist.utils.deepCopy(mist.DBs.aliveUnits) + -- timer.scheduleFunction(make_old_alive_units, nil, timer.getTime() + 0.5) + -- end + + -- make_old_alive_units() + -- end + + --Build DBs + for coa_name, coa_data in pairs(mist.DBs.units) do + for cntry_name, cntry_data in pairs(coa_data) do + for category_name, category_data in pairs(cntry_data) do + if type(category_data) == 'table' then + for group_ind, group_data in pairs(category_data) do + if type(group_data) == 'table' and group_data.units and type(group_data.units) == 'table' and #group_data.units > 0 then -- OCD paradigm programming + mist.DBs.groupsByName[group_data.groupName] = mist.utils.deepCopy(group_data) + mist.DBs.groupsById[group_data.groupId] = mist.utils.deepCopy(group_data) + for unit_ind, unit_data in pairs(group_data.units) do + mist.DBs.unitsByName[unit_data.unitName] = mist.utils.deepCopy(unit_data) + mist.DBs.unitsById[unit_data.unitId] = mist.utils.deepCopy(unit_data) + + mist.DBs.unitsByCat[unit_data.category] = mist.DBs.unitsByCat[unit_data.category] or {} -- future-proofing against new categories... + table.insert(mist.DBs.unitsByCat[unit_data.category], mist.utils.deepCopy(unit_data)) + --log:info('inserting $1', unit_data.unitName) + table.insert(mist.DBs.unitsByNum, mist.utils.deepCopy(unit_data)) + + if unit_data.skill and (unit_data.skill == "Client" or unit_data.skill == "Player") then + mist.DBs.humansByName[unit_data.unitName] = mist.utils.deepCopy(unit_data) + mist.DBs.humansById[unit_data.unitId] = mist.utils.deepCopy(unit_data) + --if Unit.getByName(unit_data.unitName) then + -- mist.DBs.activeHumans[unit_data.unitName] = mist.utils.deepCopy(unit_data) + -- mist.DBs.activeHumans[unit_data.unitName].playerName = Unit.getByName(unit_data.unitName):getPlayerName() + --end + end + end + end + end + end + end + end + end + + --DynDBs + mist.DBs.MEunits = mist.utils.deepCopy(mist.DBs.units) + mist.DBs.MEunitsByName = mist.utils.deepCopy(mist.DBs.unitsByName) + mist.DBs.MEunitsById = mist.utils.deepCopy(mist.DBs.unitsById) + mist.DBs.MEunitsByCat = mist.utils.deepCopy(mist.DBs.unitsByCat) + mist.DBs.MEunitsByNum = mist.utils.deepCopy(mist.DBs.unitsByNum) + mist.DBs.MEgroupsByName = mist.utils.deepCopy(mist.DBs.groupsByName) + mist.DBs.MEgroupsById = mist.utils.deepCopy(mist.DBs.groupsById) + + mist.DBs.deadObjects = {} + + do + local mt = {} + + function mt.__newindex(t, key, val) + local original_key = key --only for duplicate runtime IDs. + local key_ind = 1 + while mist.DBs.deadObjects[key] do + --log:warn('duplicate runtime id of previously dead object key: $1', key) + key = tostring(original_key) .. ' #' .. tostring(key_ind) + key_ind = key_ind + 1 + end + + if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then + --log:info('object found in alive_units') + val.objectData = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) + local pos = Object.getPosition(val.object) + if pos then + val.objectPos = pos.p + end + val.objectType = mist.DBs.aliveUnits[val.object.id_].category + + elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units + --log:info('object found in old_alive_units') + val.objectData = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) + local pos = Object.getPosition(val.object) + if pos then + val.objectPos = pos.p + end + val.objectType = mist.DBs.removedAliveUnits[val.object.id_].category + + else --attempt to determine if static object... + --log:info('object not found in alive units or old alive units') + local pos = Object.getPosition(val.object) + if pos then + local static_found = false + for ind, static in pairs(mist.DBs.unitsByCat.static) do + if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... + --log:info('correlated dead static object to position') + val.objectData = static + val.objectPos = pos.p + val.objectType = 'static' + static_found = true + break + end + end + if not static_found then + val.objectPos = pos.p + val.objectType = 'building' + end + else + val.objectType = 'unknown' + end + end + rawset(t, key, val) + end + + setmetatable(mist.DBs.deadObjects, mt) + end + + do -- mist unitID funcs + for id, idData in pairs(mist.DBs.unitsById) do + if idData.unitId > mist.nextUnitId then + mist.nextUnitId = mist.utils.deepCopy(idData.unitId) + end + if idData.groupId > mist.nextGroupId then + mist.nextGroupId = mist.utils.deepCopy(idData.groupId) + end + end + end + + + end + + local function updateAliveUnits() -- coroutine function + local lalive_units = mist.DBs.aliveUnits -- local references for faster execution + local lunits = mist.DBs.unitsByNum + local ldeepcopy = mist.utils.deepCopy + local lUnit = Unit + local lremovedAliveUnits = mist.DBs.removedAliveUnits + local updatedUnits = {} + + if #lunits > 0 then + local units_per_run = math.ceil(#lunits/20) + if units_per_run < 5 then + units_per_run = 5 + end + + for i = 1, #lunits do + if lunits[i].category ~= 'static' then -- can't get statics with Unit.getByName :( + local unit = lUnit.getByName(lunits[i].unitName) + if unit then + --log:info("unit named $1 alive!", lunits[i].unitName) -- spammy + local pos = unit:getPosition() + local newtbl = ldeepcopy(lunits[i]) + if pos then + newtbl.pos = pos.p + end + newtbl.unit = unit + --newtbl.rt_id = unit.id_ + lalive_units[unit.id_] = newtbl + updatedUnits[unit.id_] = true + end + end + if i%units_per_run == 0 then + coroutine.yield() + end + end + -- All units updated, remove any "alive" units that were not updated- they are dead! + for unit_id, unit in pairs(lalive_units) do + if not updatedUnits[unit_id] then + lremovedAliveUnits[unit_id] = unit + lalive_units[unit_id] = nil + end + end + end + end + + local function dbUpdate(event) + local newTable = {} + + newTable.startTime = 0 + + if type(event) == 'string' then -- if name of an object. + local newObject + local newType = 'group' + if Group.getByName(event) then + newObject = Group.getByName(event) + elseif StaticObject.getByName(event) then + newObject = StaticObject.getByName(event) + newType = 'static' + -- log:info('its static') + else + log:info('WTF') + return false + end + + newTable.name = newObject:getName() + newTable.groupId = tonumber(newObject:getID()) + newTable.groupName = newObject:getName() + local unitOneRef + if newType == 'static' then + unitOneRef = newObject + newTable.countryId = tonumber(newObject:getCountry()) + newTable.coalitionId = tonumber(newObject:getCoalition()) + newTable.category = 'static' + else + unitOneRef = newObject:getUnits() + newTable.countryId = tonumber(unitOneRef[1]:getCountry()) + newTable.coalitionId = tonumber(unitOneRef[1]:getCoalition()) + newTable.category = tonumber(newObject:getCategory()) + end + for countryData, countryId in pairs(country.id) do + if newTable.country and string.upper(countryData) == string.upper(newTable.country) or countryId == newTable.countryId then + newTable.countryId = countryId + newTable.country = string.lower(countryData) + for coaData, coaId in pairs(coalition.side) do + if coaId == coalition.getCountryCoalition(countryId) then + newTable.coalition = string.lower(coaData) + end + end + end + end + for catData, catId in pairs(Unit.Category) do + if newType == 'group' and Group.getByName(newTable.groupName):isExist() then + if catId == Group.getByName(newTable.groupName):getCategory() then + newTable.category = string.lower(catData) + end + elseif newType == 'static' and StaticObject.getByName(newTable.groupName):isExist() then + if catId == StaticObject.getByName(newTable.groupName):getCategory() then + newTable.category = string.lower(catData) + end + + end + end + local gfound = false + for index, data in pairs(mistAddedGroups) do + if mist.stringMatch(data.name, newTable.groupName) == true then + gfound = true + newTable.task = data.task + newTable.modulation = data.modulation + newTable.uncontrolled = data.uncontrolled + newTable.radioSet = data.radioSet + newTable.hidden = data.hidden + newTable.startTime = data.start_time + mistAddedGroups[index] = nil + end + end + + if gfound == false then + newTable.uncontrolled = false + newTable.hidden = false + end + + newTable.units = {} + if newType == 'group' then + for unitId, unitData in pairs(unitOneRef) do + newTable.units[unitId] = {} + newTable.units[unitId].unitName = unitData:getName() + + newTable.units[unitId].x = mist.utils.round(unitData:getPosition().p.x) + newTable.units[unitId].y = mist.utils.round(unitData:getPosition().p.z) + newTable.units[unitId].point = {} + newTable.units[unitId].point.x = newTable.units[unitId].x + newTable.units[unitId].point.y = newTable.units[unitId].y + newTable.units[unitId].alt = mist.utils.round(unitData:getPosition().p.y) + newTable.units[unitId].speed = mist.vec.mag(unitData:getVelocity()) + + newTable.units[unitId].heading = mist.getHeading(unitData, true) + + newTable.units[unitId].type = unitData:getTypeName() + newTable.units[unitId].unitId = tonumber(unitData:getID()) + + + newTable.units[unitId].groupName = newTable.groupName + newTable.units[unitId].groupId = newTable.groupId + newTable.units[unitId].countryId = newTable.countryId + newTable.units[unitId].coalitionId = newTable.coalitionId + newTable.units[unitId].coalition = newTable.coalition + newTable.units[unitId].country = newTable.country + local found = false + for index, data in pairs(mistAddedObjects) do + if mist.stringMatch(data.name, newTable.units[unitId].unitName) == true then + found = true + newTable.units[unitId].livery_id = data.livery_id + newTable.units[unitId].skill = data.skill + newTable.units[unitId].alt_type = data.alt_type + newTable.units[unitId].callsign = data.callsign + newTable.units[unitId].psi = data.psi + mistAddedObjects[index] = nil + end + if found == false then + newTable.units[unitId].skill = "High" + newTable.units[unitId].alt_type = "BARO" + end + end + + end + else -- its a static + newTable.category = 'static' + newTable.units[1] = {} + newTable.units[1].unitName = newObject:getName() + newTable.units[1].category = 'static' + newTable.units[1].x = mist.utils.round(newObject:getPosition().p.x) + newTable.units[1].y = mist.utils.round(newObject:getPosition().p.z) + newTable.units[1].point = {} + newTable.units[1].point.x = newTable.units[1].x + newTable.units[1].point.y = newTable.units[1].y + newTable.units[1].alt = mist.utils.round(newObject:getPosition().p.y) + newTable.units[1].heading = mist.getHeading(newObject, true) + newTable.units[1].type = newObject:getTypeName() + newTable.units[1].unitId = tonumber(newObject:getID()) + newTable.units[1].groupName = newTable.name + newTable.units[1].groupId = newTable.groupId + newTable.units[1].countryId = newTable.countryId + newTable.units[1].country = newTable.country + newTable.units[1].coalitionId = newTable.coalitionId + newTable.units[1].coalition = newTable.coalition + if newObject:getCategory() == 6 and newObject:getCargoDisplayName() then + local mass = newObject:getCargoDisplayName() + mass = string.gsub(mass, ' ', '') + mass = string.gsub(mass, 'kg', '') + newTable.units[1].mass = tonumber(mass) + newTable.units[1].categoryStatic = 'Cargos' + newTable.units[1].canCargo = true + newTable.units[1].shape_name = 'ab-212_cargo' + end + + ----- search mist added objects for extra data if applicable + for index, data in pairs(mistAddedObjects) do + if mist.stringMatch(data.name, newTable.units[1].unitName) == true then + newTable.units[1].shape_name = data.shape_name -- for statics + newTable.units[1].livery_id = data.livery_id + newTable.units[1].airdromeId = data.airdromeId + newTable.units[1].mass = data.mass + newTable.units[1].canCargo = data.canCargo + newTable.units[1].categoryStatic = data.categoryStatic + newTable.units[1].type = 'cargo1' + mistAddedObjects[index] = nil + end + end + end + end + --mist.debug.writeData(mist.utils.serialize,{'msg', newTable}, timer.getAbsTime() ..'Group.lua') + newTable.timeAdded = timer.getAbsTime() -- only on the dynGroupsAdded table. For other reference, see start time + --mist.debug.dumpDBs() + --end + + return newTable + end + + local function checkSpawnedEvents() + if #tempSpawnedUnits > 0 then + local groupsToAdd = {} + local added = false + local ltemp = tempSpawnedUnits + local ltable = table + + local updatesPerRun = math.ceil(#tempSpawnedUnits/20) + if updatesPerRun < 5 then + updatesPerRun = 5 + end + for x = 1, #tempSpawnedUnits do + local spawnedObj = ltemp[x] + if spawnedObj and spawnedObj:isExist() then + local found = false + for name, val in pairs(groupsToAdd) do + if spawnedObj:getCategory() == 1 then -- normal groups + if mist.stringMatch(spawnedObj:getGroup():getName(), name) == true then + found = true + break + end + elseif spawnedObj:getCategory() == 3 or spawnedObj:getCategory() == 6 then -- static objects + if mist.stringMatch(spawnedObj:getName(), name) == true then + found = true + break + end + end + end + -- for some reason cargo objects are returning as category == 6. + if found == false then + added = true + if spawnedObj:getCategory() == 1 then -- normal groups + groupsToAdd[spawnedObj:getGroup():getName()] = true + elseif spawnedObj:getCategory() == 3 or spawnedObj:getCategory() == 6 then -- static objects + groupsToAdd[spawnedObj:getName()] = true + end + + end + end + + table.remove(ltemp, x) + if x%updatesPerRun == 0 then + coroutine.yield() + end + end + + if added == true then + for groupName, val in pairs(groupsToAdd) do + local dataChanged = false + if mist.DBs.groupsByName[groupName] then + for _index, data in pairs(mist.DBs.groupsByName[groupName]) do + if data.unitName ~= spawnedObj:getName() and data.unitId ~= spawnedObj:getID() and data.type ~= spawnedObj:getTypeName() then + dataChanged = true + break + end + end + if dataChanged == false then + groupsToAdd[groupName] = false + end + end + if groupsToAdd[groupName] == true or not mist.DBs.groupsByName[groupName] then + writeGroups[#writeGroups + 1] = dbUpdate(groupName) + end + end + end + end + end + + local function updateDBTables() + local i = 0 + for index, newTable in pairs(writeGroups) do + i = i + 1 + end + local savesPerRun = math.ceil(i/10) + if savesPerRun < 5 then + savesPerRun = 5 + end + if i > 0 then + local ldeepCopy = mist.utils.deepCopy + for x = 1, i do + local newTable = writeGroups[x] + local mistCategory + if type(newTable.category) == 'string' then + mistCategory = string.lower(newTable.category) + end + + if string.upper(newTable.category) == 'GROUND_UNIT' then + mistCategory = 'vehicle' + newTable.category = mistCategory + elseif string.upper(newTable.category) == 'AIRPLANE' then + mistCategory = 'plane' + newTable.category = mistCategory + elseif string.upper(newTable.category) == 'HELICOPTER' then + mistCategory = 'helicopter' + newTable.category = mistCategory + elseif string.upper(newTable.category) == 'SHIP' then + mistCategory = 'ship' + newTable.category = mistCategory + end + for newId, newUnitData in pairs(newTable.units) do + newUnitData.category = mistCategory + if newUnitData.unitId then + mist.DBs.unitsById[tonumber(newUnitData.unitId)] = ldeepCopy(newUnitData) + end + + mist.DBs.unitsByName[newUnitData.unitName] = ldeepCopy(newUnitData) + mist.DBs.unitsByCat[mistCategory][#mist.DBs.unitsByCat[mistCategory] + 1] = ldeepCopy(newUnitData) + mist.DBs.unitsByNum[#mist.DBs.unitsByNum + 1] = ldeepCopy(newUnitData) + end + -- this is a really annoying DB to populate. Gotta create new tables in case its missing + if not mist.DBs.units[newTable.coalition] then + mist.DBs.units[newTable.coalition] = {} + end + + if not mist.DBs.units[newTable.coalition][newTable.country] then + mist.DBs.units[newTable.coalition][(newTable.country)] = {} + mist.DBs.units[newTable.coalition][(newTable.country)].countryId = newTable.countryId + end + if not mist.DBs.units[newTable.coalition][newTable.country][mistCategory] then + mist.DBs.units[newTable.coalition][(newTable.country)][mistCategory] = {} + end + mist.DBs.units[newTable.coalition][(newTable.country)][mistCategory][#mist.DBs.units[newTable.coalition][(newTable.country)][mistCategory] + 1] = ldeepCopy(newTable) + + if newTable.groupId then + mist.DBs.groupsById[newTable.groupId] = ldeepCopy(newTable) + end + + mist.DBs.groupsByName[newTable.name] = ldeepCopy(newTable) + mist.DBs.dynGroupsAdded[#mist.DBs.dynGroupsAdded + 1] = ldeepCopy(newTable) + + writeGroups[x] = nil + if x%savesPerRun == 0 then + coroutine.yield() + end + end + if timer.getTime() > lastUpdateTime then + lastUpdateTime = timer.getTime() + end + end + end + + local function groupSpawned(event) + -- dont need to add units spawned in at the start of the mission if mist is loaded in init line + if event.id == world.event.S_EVENT_BIRTH and timer.getTime0() < timer.getAbsTime() then + table.insert(tempSpawnedUnits,(event.initiator)) + end + end + + local function doScheduledFunctions() + local i = 1 + while i <= #scheduledTasks do + if not scheduledTasks[i].rep then -- not a repeated process + if scheduledTasks[i].t <= timer.getTime() then + local task = scheduledTasks[i] -- local reference + table.remove(scheduledTasks, i) + local err, errmsg = pcall(task.f, unpack(task.vars, 1, table.maxn(task.vars))) + if not err then + log:error('Error in scheduled function: $1', errmsg) + end + --task.f(unpack(task.vars, 1, table.maxn(task.vars))) -- do the task, do not increment i + else + i = i + 1 + end + else + if scheduledTasks[i].st and scheduledTasks[i].st <= timer.getTime() then --if a stoptime was specified, and the stop time exceeded + table.remove(scheduledTasks, i) -- stop time exceeded, do not execute, do not increment i + elseif scheduledTasks[i].t <= timer.getTime() then + local task = scheduledTasks[i] -- local reference + task.t = timer.getTime() + task.rep --schedule next run + local err, errmsg = pcall(task.f, unpack(task.vars, 1, table.maxn(task.vars))) + if not err then + log:error('Error in scheduled function: $1' .. errmsg) + end + --scheduledTasks[i].f(unpack(scheduledTasks[i].vars, 1, table.maxn(scheduledTasks[i].vars))) -- do the task + i = i + 1 + else + i = i + 1 + end + end + end + end + + -- Event handler to start creating the dead_objects table + local function addDeadObject(event) + if event.id == world.event.S_EVENT_DEAD or event.id == world.event.S_EVENT_CRASH then + if event.initiator and event.initiator.id_ and event.initiator.id_ > 0 then + + local id = event.initiator.id_ -- initial ID, could change if there is a duplicate id_ already dead. + local val = {object = event.initiator} -- the new entry in mist.DBs.deadObjects. + + local original_id = id --only for duplicate runtime IDs. + local id_ind = 1 + while mist.DBs.deadObjects[id] do + --log:info('duplicate runtime id of previously dead object id: $1', id) + id = tostring(original_id) .. ' #' .. tostring(id_ind) + id_ind = id_ind + 1 + end + + if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then + --log:info('object found in alive_units') + val.objectData = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) + local pos = Object.getPosition(val.object) + if pos then + val.objectPos = pos.p + end + val.objectType = mist.DBs.aliveUnits[val.object.id_].category + --[[if mist.DBs.activeHumans[Unit.getName(val.object)] then + --trigger.action.outText('remove via death: ' .. Unit.getName(val.object),20) + mist.DBs.activeHumans[Unit.getName(val.object)] = nil + end]] + elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units + --log:info('object found in old_alive_units') + val.objectData = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) + local pos = Object.getPosition(val.object) + if pos then + val.objectPos = pos.p + end + val.objectType = mist.DBs.removedAliveUnits[val.object.id_].category + + else --attempt to determine if static object... + --log:info('object not found in alive units or old alive units') + local pos = Object.getPosition(val.object) + if pos then + local static_found = false + for ind, static in pairs(mist.DBs.unitsByCat.static) do + if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... + --log:info('correlated dead static object to position') + val.objectData = static + val.objectPos = pos.p + val.objectType = 'static' + static_found = true + break + end + end + if not static_found then + val.objectPos = pos.p + val.objectType = 'building' + end + else + val.objectType = 'unknown' + end + end + mist.DBs.deadObjects[id] = val + end + end + end + + --[[ + local function addClientsToActive(event) + if event.id == world.event.S_EVENT_PLAYER_ENTER_UNIT or event.id == world.event.S_EVENT_BIRTH then + log:info(mist.utils.tableShow(event)) + if Unit.getPlayerName(event.initiator) then + log:info(Unit.getPlayerName(event.initiator)) + local newU = mist.utils.deepCopy(mist.DBs.unitsByName[Unit.getName(event.initiator)]) + newU.playerName = Unit.getPlayerName(event.initiator) + mist.DBs.activeHumans[Unit.getName(event.initiator)] = newU + --trigger.action.outText('added: ' .. Unit.getName(event.initiator), 20) + end + elseif event.id == world.event.S_EVENT_PLAYER_LEAVE_UNIT and event.initiator then + if mist.DBs.activeHumans[Unit.getName(event.initiator)] then + mist.DBs.activeHumans[Unit.getName(event.initiator)] = nil + -- trigger.action.outText('removed via control: ' .. Unit.getName(event.initiator), 20) + end + end + end + + mist.addEventHandler(addClientsToActive) + ]] + + --- init function. + -- creates logger, adds default event handler + -- and calls main the first time. + -- @function mist.init + function mist.init() + -- create logger + mist.log = mist.Logger:new("MIST") + log = mist.log -- log shorthand + -- set warning log level, showing only + -- warnings and errors + log:setLevel("warning") + + log:info("initializing databases") + initDBs() + + -- add event handler for group spawns + mist.addEventHandler(groupSpawned) + mist.addEventHandler(addDeadObject) + + -- call main the first time therafter it reschedules itself. + mist.main() + --log:msg('MIST version $1.$2.$3 loaded', mist.majorVersion, mist.minorVersion, mist.build) + return + end + + --- The main function. + -- Run 100 times per second. + -- You shouldn't call this function. + function mist.main() + timer.scheduleFunction(mist.main, {}, timer.getTime() + 0.01) --reschedule first in case of Lua error + + writeDbTableCounter = writeDbTableCounter + 1 + if writeDbTableCounter == 10 then + writeDbTableCounter = 0 + + if not coroutines.updateDBTables then + coroutines.updateDBTables = coroutine.create(updateDBTables) + end + + coroutine.resume(coroutines.updateDBTables) + + if coroutine.status(coroutines.updateDBTables) == 'dead' then + coroutines.updateDBTables = nil + end + end + + checkSpawnEventsCounter = checkSpawnEventsCounter + 1 + if checkSpawnEventsCounter == 10 then + checkSpawnEventsCounter = 0 + + if not coroutines.checkSpawnedEvents then + coroutines.checkSpawnedEvents = coroutine.create(checkSpawnedEvents) + end + + coroutine.resume(coroutines.checkSpawnedEvents) + + if coroutine.status(coroutines.checkSpawnedEvents) == 'dead' then + coroutines.checkSpawnedEvents = nil + end + end + + --updating alive units + updateAliveUnitsCounter = updateAliveUnitsCounter + 1 + if updateAliveUnitsCounter == 5 then + updateAliveUnitsCounter = 0 + + if not coroutines.update_alive_units then + coroutines.updateAliveUnits = coroutine.create(updateAliveUnits) + end + + coroutine.resume(coroutines.updateAliveUnits) + + if coroutine.status(coroutines.updateAliveUnits) == 'dead' then + coroutines.updateAliveUnits = nil + end + end + + doScheduledFunctions() + end -- end of mist.main + + --- Returns next unit id. + -- @treturn number next unit id. + function mist.getNextUnitId() + mist.nextUnitId = mist.nextUnitId + 1 + if mist.nextUnitId > 6900 then + mist.nextUnitId = 14000 + end + return mist.nextUnitId + end + + --- Returns next group id. + -- @treturn number next group id. + function mist.getNextGroupId() + mist.nextGroupId = mist.nextGroupId + 1 + if mist.nextGroupId > 6900 then + mist.nextGroupId = 14000 + end + return mist.nextGroupId + end + + --- Returns timestamp of last database update. + -- @treturn timestamp of last database update + function mist.getLastDBUpdateTime() + return lastUpdateTime + end + + --- Spawns a static object to the game world. + -- @todo write good docs + -- @tparam table staticObj table containing data needed for the object creation + function mist.dynAddStatic(staticObj) + local newObj = {} + newObj.groupId = staticObj.groupId + newObj.category = staticObj.category + newObj.type = staticObj.type + newObj.unitId = staticObj.unitId + newObj.y = staticObj.y + newObj.x = staticObj.x + newObj.heading = staticObj.heading + newObj.name = staticObj.name + newObj.dead = staticObj.dead + newObj.country = staticObj.country + newObj.countryId = staticObj.countryId + newObj.clone = staticObj.clone + newObj.shape_name = staticObj.shape_name + newObj.canCargo = staticObj.canCargo + newObj.mass = staticObj.mass + newObj.categoryStatic = staticObj.categoryStatic + if staticObj.units then -- if its mist format + newObj.groupId = staticObj.units[1].groupId + newObj.category = staticObj.units[1].category + newObj.type = staticObj.units[1].type + newObj.unitId = staticObj.units[1].unitId + newObj.y = staticObj.units[1].y + newObj.x = staticObj.units[1].x + newObj.heading = staticObj.units[1].heading + newObj.name = staticObj.units[1].name + newObj.dead = staticObj.units[1].dead + newObj.country = staticObj.units[1].country + newObj.countryId = staticObj.units[1].countryId + newObj.shape_name = staticObj.units[1].shape_name + newObj.canCargo = staticObj.units[1].canCargo + newObj.mass = staticObj.units[1].mass + newObj.categoryStatic = staticObj.units[1].categoryStatic + end + + if not newObj.country then + return false + end + + local newCountry = newObj.country + if newObj.countryId then + newCountry = newObj.countryId + end + for countryId, countryName in pairs(country.name) do + if type(newObj.country) == 'string' then + if tostring(countryName) == string.upper(newObj.country) then + newCountry = countryName + end + elseif type(newObj.country) == 'number' then + if countryId == newObj.country then + newCountry = countryName + end + end + end + + if newObj.clone or not newObj.groupId then + mistGpId = mistGpId + 1 + newObj.groupId = mistGpId + end + + if newObj.clone or not newObj.unitId then + mistUnitId = mistUnitId + 1 + newObj.unitId = mistUnitId + end + + if newObj.clone or not newObj.name then + mistDynAddIndex = mistDynAddIndex + 1 + newObj.name = (country.name[newCountry] .. ' static ' .. mistDynAddIndex) + end + + if not newObj.dead then + newObj.dead = false + end + + if not newObj.heading then + newObj.heading = math.random(360) + end + + if newObj.categoryStatic then + newObj.category = newObj.categoryStatic + end + if newObj.mass then + newObj.category = 'Cargos' + end + mistAddedObjects[#mistAddedObjects + 1] = mist.utils.deepCopy(newObj) + if newObj.x and newObj.y and newObj.type and type(newObj.x) == 'number' and type(newObj.y) == 'number' and type(newObj.type) == 'string' then + coalition.addStaticObject(country.id[newCountry], newObj) + + return newObj + end + return false + end + + --- Spawns a dynamic group into the game world. + -- Same as coalition.add function in SSE. checks the passed data to see if its valid. + -- Will generate groupId, groupName, unitId, and unitName if needed + -- @tparam table newGroup table containting values needed for spawning a group. + function mist.dynAdd(newGroup) + + --mist.debug.writeData(mist.utils.serialize,{'msg', newGroup}, 'newGroupOrig.lua') + local cntry = newGroup.country + if newGroup.countryId then + cntry = newGroup.countryId + end + + local groupType = newGroup.category + local newCountry = '' + -- validate data + for countryId, countryName in pairs(country.name) do + if type(cntry) == 'string' then + cntry = cntry:gsub("%s+", "_") + if tostring(countryName) == string.upper(cntry) then + newCountry = countryName + end + elseif type(cntry) == 'number' then + if countryId == cntry then + newCountry = countryName + end + end + end + + if newCountry == '' then + return false + end + + local newCat = '' + for catName, catId in pairs(Unit.Category) do + if type(groupType) == 'string' then + if tostring(catName) == string.upper(groupType) then + newCat = catName + end + elseif type(groupType) == 'number' then + if catId == groupType then + newCat = catName + end + end + + if catName == 'GROUND_UNIT' and (string.upper(groupType) == 'VEHICLE' or string.upper(groupType) == 'GROUND') then + newCat = 'GROUND_UNIT' + elseif catName == 'AIRPLANE' and string.upper(groupType) == 'PLANE' then + newCat = 'AIRPLANE' + end + end + local typeName + if newCat == 'GROUND_UNIT' then + typeName = ' gnd ' + elseif newCat == 'AIRPLANE' then + typeName = ' air ' + elseif newCat == 'HELICOPTER' then + typeName = ' hel ' + elseif newCat == 'SHIP' then + typeName = ' shp ' + elseif newCat == 'BUILDING' then + typeName = ' bld ' + end + if newGroup.clone or not newGroup.groupId then + mistDynAddIndex = mistDynAddIndex + 1 + mistGpId = mistGpId + 1 + newGroup.groupId = mistGpId + end + if newGroup.groupName or newGroup.name then + if newGroup.groupName then + newGroup.name = newGroup.groupName + elseif newGroup.name then + newGroup.name = newGroup.name + end + end + + if newGroup.clone and mist.DBs.groupsByName[newGroup.name] or not newGroup.name then + newGroup.name = tostring(tostring(country.name[cntry]) .. tostring(typeName) .. mistDynAddIndex) + end + + if not newGroup.hidden then + newGroup.hidden = false + end + + if not newGroup.visible then + newGroup.visible = false + end + + if (newGroup.start_time and type(newGroup.start_time) ~= 'number') or not newGroup.start_time then + if newGroup.startTime then + newGroup.start_time = mist.utils.round(newGroup.startTime) + else + newGroup.start_time = 0 + end + end + + + for unitIndex, unitData in pairs(newGroup.units) do + local originalName = newGroup.units[unitIndex].unitName or newGroup.units[unitIndex].name + if newGroup.clone or not unitData.unitId then + mistUnitId = mistUnitId + 1 + newGroup.units[unitIndex].unitId = mistUnitId + end + if newGroup.units[unitIndex].unitName or newGroup.units[unitIndex].name then + if newGroup.units[unitIndex].unitName then + newGroup.units[unitIndex].name = newGroup.units[unitIndex].unitName + elseif newGroup.units[unitIndex].name then + newGroup.units[unitIndex].name = newGroup.units[unitIndex].name + end + end + if newGroup.clone or not unitData.name then + newGroup.units[unitIndex].name = tostring(newGroup.name .. ' unit' .. unitIndex) + end + + if not unitData.skill then + newGroup.units[unitIndex].skill = 'Random' + end + + if not unitData.alt then + if newCat == 'AIRPLANE' then + newGroup.units[unitIndex].alt = 2000 + newGroup.units[unitIndex].alt_type = 'RADIO' + newGroup.units[unitIndex].speed = 150 + elseif newCat == 'HELICOPTER' then + newGroup.units[unitIndex].alt = 500 + newGroup.units[unitIndex].alt_type = 'RADIO' + newGroup.units[unitIndex].speed = 60 + else + --[[log:info('check height') + newGroup.units[unitIndex].alt = land.getHeight({x = newGroup.units[unitIndex].x, y = newGroup.units[unitIndex].y}) + newGroup.units[unitIndex].alt_type = 'BARO']] + end + + + end + + if newCat == 'AIRPLANE' or newCat == 'HELICOPTER' then + if newGroup.units[unitIndex].alt_type and newGroup.units[unitIndex].alt_type ~= 'BARO' or not newGroup.units[unitIndex].alt_type then + newGroup.units[unitIndex].alt_type = 'RADIO' + end + if not unitData.speed then + if newCat == 'AIRPLANE' then + newGroup.units[unitIndex].speed = 150 + elseif newCat == 'HELICOPTER' then + newGroup.units[unitIndex].speed = 60 + end + end + if not unitData.payload then + newGroup.units[unitIndex].payload = mist.getPayload(originalName) + end + end + mistAddedObjects[#mistAddedObjects + 1] = mist.utils.deepCopy(newGroup.units[unitIndex]) + end + mistAddedGroups[#mistAddedGroups + 1] = mist.utils.deepCopy(newGroup) + if newGroup.route and not newGroup.route.points then + if not newGroup.route.points and newGroup.route[1] then + local copyRoute = newGroup.route + newGroup.route = {} + newGroup.route.points = copyRoute + end + end + newGroup.country = newCountry + + + --mist.debug.writeData(mist.utils.serialize,{'msg', newGroup}, 'newGroup.lua') + + -- sanitize table + newGroup.groupName = nil + newGroup.clone = nil + newGroup.category = nil + newGroup.country = nil + + newGroup.tasks = {} + + for unitIndex, unitData in pairs(newGroup.units) do + newGroup.units[unitIndex].unitName = nil + end + + coalition.addGroup(country.id[newCountry], Unit.Category[newCat], newGroup) + + return newGroup + + end + + --- Schedules a function. + -- Modified Slmod task scheduler, superior to timer.scheduleFunction + -- @tparam function f function to schedule + -- @tparam table vars array containing all parameters passed to the function + -- @tparam number t time in seconds from mission start to schedule the function to. + -- @tparam[opt] number rep time between repetitions of the function + -- @tparam[opt] number st time in seconds from mission start at which the function + -- should stop to be rescheduled. + -- @treturn number scheduled function id. + function mist.scheduleFunction(f, vars, t, rep, st) + --verify correct types + assert(type(f) == 'function', 'variable 1, expected function, got ' .. type(f)) + assert(type(vars) == 'table' or vars == nil, 'variable 2, expected table or nil, got ' .. type(f)) + assert(type(t) == 'number', 'variable 3, expected number, got ' .. type(t)) + assert(type(rep) == 'number' or rep == nil, 'variable 4, expected number or nil, got ' .. type(rep)) + assert(type(st) == 'number' or st == nil, 'variable 5, expected number or nil, got ' .. type(st)) + if not vars then + vars = {} + end + taskId = taskId + 1 + table.insert(scheduledTasks, {f = f, vars = vars, t = t, rep = rep, st = st, id = taskId}) + return taskId + end + + --- Removes a scheduled function. + -- @tparam number id function id + -- @treturn boolean true if function was successfully removed, false otherwise. + function mist.removeFunction(id) + local i = 1 + while i <= #scheduledTasks do + if scheduledTasks[i].id == id then + table.remove(scheduledTasks, i) + else + i = i + 1 + end + end + end + + --- Registers an event handler. + -- @tparam function f function handling event + -- @treturn number id of the event handler + function mist.addEventHandler(f) --id is optional! + local handler = {} + idNum = idNum + 1 + handler.id = idNum + handler.f = f + function handler:onEvent(event) + self.f(event) + end + world.addEventHandler(handler) + return handler.id + end + + --- Removes event handler with given id. + -- @tparam number id event handler id + -- @treturn boolean true on success, false otherwise + function mist.removeEventHandler(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 + + --- Returns MGRS coordinates as string. + -- @tparam string MGRS MGRS coordinates + -- @tparam number acc the accuracy of each easting/northing. + -- Can be: 0, 1, 2, 3, 4, or 5. + function mist.tostringMGRS(MGRS, acc) + if acc == 0 then + return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph + else + return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph .. ' ' .. string.format('%0' .. acc .. 'd', mist.utils.round(MGRS.Easting/(10^(5-acc)), 0)) + .. ' ' .. string.format('%0' .. acc .. 'd', mist.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. + ]] + function mist.tostringLL(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 = mist.utils.round((oldLatMin - latMin)*60, acc) + + local oldLonMin = lonMin + lonMin = math.floor(lonMin) + local lonSec = mist.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 = mist.utils.round(latMin, acc) + lonMin = mist.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.]] + function mist.tostringBR(az, dist, alt, metric) + az = mist.utils.round(mist.utils.toDegree(az), 0) + + if metric then + dist = mist.utils.round(dist/1000, 0) + else + dist = mist.utils.round(mist.utils.metersToNM(dist), 0) + end + + local s = string.format('%03d', az) .. ' for ' .. dist + + if alt then + if metric then + s = s .. ' at ' .. mist.utils.round(alt, 0) + else + s = s .. ' at ' .. mist.utils.round(mist.utils.metersToFeet(alt), 0) + end + end + return s + end + + function mist.getNorthCorrection(gPoint) --gets the correction needed for true north + local point = mist.utils.deepCopy(gPoint) + 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 + + --- Returns skill of the given unit. + -- @tparam string unitName unit name + -- @return skill of the unit + function mist.getUnitSkill(unitName) + if Unit.getByName(unitName) and Unit.getByName(unitName):isExist() == true then + local lunit = Unit.getByName(unitName) + for name, data in pairs(mist.DBs.unitsByName) do + if name == unitName and data.type == lunit:getTypeName() and data.unitId == lunit:getID() and data.skill then + return data.skill + end + end + end + return false + end + + --- Returns an array containing a group's units positions. + -- e.g. + -- { + -- [1] = {x = 299435.224, y = -1146632.6773}, + -- [2] = {x = 663324.6563, y = 322424.1112} + -- } + -- @tparam number|string groupIdent group id or name + -- @treturn table array containing positions of each group member + function mist.getGroupPoints(groupIdent) + -- search by groupId and allow groupId and groupName as inputs + local gpId = groupIdent + if type(groupIdent) == 'string' and not tonumber(groupIdent) then + gpId = mist.DBs.MEgroupsByName[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 + if not point.point then + points[point_num] = { x = point.x, y = point.y } + else + points[point_num] = point.point --it's possible that the ME could move to the point = Vec2 notation. + end + 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 + + --- getUnitAttitude(unit) return values. + -- Yaw, AoA, ClimbAngle - relative to earth reference + -- DOES NOT TAKE INTO ACCOUNT WIND. + -- @table attitude + -- @tfield number Heading in radians, range of 0 to 2*pi, + -- relative to true north. + -- @tfield number Pitch in radians, range of -pi/2 to pi/2 + -- @tfield number Roll in radians, range of 0 to 2*pi, + -- right roll is positive direction. + -- @tfield number Yaw in radians, range of -pi to pi, + -- right yaw is positive direction. + -- @tfield number AoA in radians, range of -pi to pi, + -- rotation of aircraft to the right in comparison to + -- flight direction being positive. + -- @tfield number ClimbAngle in radians, range of -pi/2 to pi/2 + + --- Returns the attitude of a given unit. + -- Will work on any unit, even if not an aircraft. + -- @tparam Unit unit unit whose attitude is returned. + -- @treturn table @{attitude} + function mist.getAttitude(unit) + local unitpos = unit:getPosition() + if unitpos then + + local Heading = math.atan2(unitpos.x.z, unitpos.x.x) + + Heading = Heading + mist.getNorthCorrection(unitpos.p) + + if Heading < 0 then + Heading = Heading + 2*math.pi -- put heading in range of 0 to 2*pi + end + ---- heading complete.---- + + local Pitch = math.asin(unitpos.x.y) + ---- pitch complete.---- + + -- now get roll: + --maybe not the best way to do it, but it works. + + --first, make a vector that is perpendicular to y and unitpos.x with cross product + local cp = mist.vec.cp(unitpos.x, {x = 0, y = 1, z = 0}) + + --now, get dot product of of this cross product with unitpos.z + local dp = mist.vec.dp(cp, unitpos.z) + + --now get the magnitude of the roll (magnitude of the angle between two vectors is acos(vec1.vec2/|vec1||vec2|) + local Roll = math.acos(dp/(mist.vec.mag(cp)*mist.vec.mag(unitpos.z))) + + --now, have to get sign of roll. + -- by convention, making right roll positive + -- to get sign of roll, use the y component of unitpos.z. For right roll, y component is negative. + + if unitpos.z.y > 0 then -- left roll, flip the sign of the roll + Roll = -Roll + end + ---- roll complete. ---- + + --now, work on yaw, AoA, climb, and abs velocity + local Yaw + local AoA + local ClimbAngle + + -- get unit velocity + local unitvel = unit:getVelocity() + if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! + local AxialVel = {} --unit velocity transformed into aircraft axes directions + + --transform velocity components in direction of aircraft axes. + AxialVel.x = mist.vec.dp(unitpos.x, unitvel) + AxialVel.y = mist.vec.dp(unitpos.y, unitvel) + AxialVel.z = mist.vec.dp(unitpos.z, unitvel) + + --Yaw is the angle between unitpos.x and the x and z velocities + --define right yaw as positive + Yaw = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = 0, z = AxialVel.z})/mist.vec.mag({x = AxialVel.x, y = 0, z = AxialVel.z})) + + --now set correct direction: + if AxialVel.z > 0 then + Yaw = -Yaw + end + + -- AoA is angle between unitpos.x and the x and y velocities + AoA = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = AxialVel.y, z = 0})/mist.vec.mag({x = AxialVel.x, y = AxialVel.y, z = 0})) + + --now set correct direction: + if AxialVel.y > 0 then + AoA = -AoA + end + + ClimbAngle = math.asin(unitvel.y/mist.vec.mag(unitvel)) + end + return { Heading = Heading, Pitch = Pitch, Roll = Roll, Yaw = Yaw, AoA = AoA, ClimbAngle = ClimbAngle} + else + log:error("Couldn't get unit's position") + end + end + + --- Returns heading of given unit. + -- @tparam Unit unit unit whose heading is returned. + -- @param rawHeading + -- @treturn number heading of the unit, in range + -- of 0 to 2*pi. + function mist.getHeading(unit, rawHeading) + local unitpos = unit:getPosition() + if unitpos then + local Heading = math.atan2(unitpos.x.z, unitpos.x.x) + if not rawHeading then + Heading = Heading + mist.getNorthCorrection(unitpos.p) + end + if Heading < 0 then + Heading = Heading + 2*math.pi -- put heading in range of 0 to 2*pi + end + return Heading + end + end + + --- Returns given unit's pitch + -- @tparam Unit unit unit whose pitch is returned. + -- @treturn number pitch of given unit + function mist.getPitch(unit) + local unitpos = unit:getPosition() + if unitpos then + return math.asin(unitpos.x.y) + end + end + + --- Returns given unit's roll. + -- @tparam Unit unit unit whose roll is returned. + -- @treturn number roll of given unit + function mist.getRoll(unit) + local unitpos = unit:getPosition() + if unitpos then + -- now get roll: + --maybe not the best way to do it, but it works. + + --first, make a vector that is perpendicular to y and unitpos.x with cross product + local cp = mist.vec.cp(unitpos.x, {x = 0, y = 1, z = 0}) + + --now, get dot product of of this cross product with unitpos.z + local dp = mist.vec.dp(cp, unitpos.z) + + --now get the magnitude of the roll (magnitude of the angle between two vectors is acos(vec1.vec2/|vec1||vec2|) + local Roll = math.acos(dp/(mist.vec.mag(cp)*mist.vec.mag(unitpos.z))) + + --now, have to get sign of roll. + -- by convention, making right roll positive + -- to get sign of roll, use the y component of unitpos.z. For right roll, y component is negative. + + if unitpos.z.y > 0 then -- left roll, flip the sign of the roll + Roll = -Roll + end + return Roll + end + end + + --- Returns given unit's yaw. + -- @tparam Unit unit unit whose yaw is returned. + -- @treturn number yaw of given unit. + function mist.getYaw(unit) + local unitpos = unit:getPosition() + if unitpos then + -- get unit velocity + local unitvel = unit:getVelocity() + if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! + local AxialVel = {} --unit velocity transformed into aircraft axes directions + + --transform velocity components in direction of aircraft axes. + AxialVel.x = mist.vec.dp(unitpos.x, unitvel) + AxialVel.y = mist.vec.dp(unitpos.y, unitvel) + AxialVel.z = mist.vec.dp(unitpos.z, unitvel) + + --Yaw is the angle between unitpos.x and the x and z velocities + --define right yaw as positive + local Yaw = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = 0, z = AxialVel.z})/mist.vec.mag({x = AxialVel.x, y = 0, z = AxialVel.z})) + + --now set correct direction: + if AxialVel.z > 0 then + Yaw = -Yaw + end + return Yaw + end + end + end + + --- Returns given unit's angle of attack. + -- @tparam Unit unit unit to get AoA from. + -- @treturn number angle of attack of the given unit. + function mist.getAoA(unit) + local unitpos = unit:getPosition() + if unitpos then + local unitvel = unit:getVelocity() + if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! + local AxialVel = {} --unit velocity transformed into aircraft axes directions + + --transform velocity components in direction of aircraft axes. + AxialVel.x = mist.vec.dp(unitpos.x, unitvel) + AxialVel.y = mist.vec.dp(unitpos.y, unitvel) + AxialVel.z = mist.vec.dp(unitpos.z, unitvel) + + -- AoA is angle between unitpos.x and the x and y velocities + local AoA = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = AxialVel.y, z = 0})/mist.vec.mag({x = AxialVel.x, y = AxialVel.y, z = 0})) + + --now set correct direction: + if AxialVel.y > 0 then + AoA = -AoA + end + return AoA + end + end + end + + --- Returns given unit's climb angle. + -- @tparam Unit unit unit to get climb angle from. + -- @treturn number climb angle of given unit. + function mist.getClimbAngle(unit) + local unitpos = unit:getPosition() + if unitpos then + local unitvel = unit:getVelocity() + if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! + return math.asin(unitvel.y/mist.vec.mag(unitvel)) + end + end + end + + --[[-- + Unit name table. + Many Mist functions require tables of unit names, which are known + in Mist as UnitNameTables. These follow a special set of shortcuts + borrowed from Slmod. These shortcuts alleviate the problem of entering + huge lists of unit names by hand, and in many cases, they remove the + need to even know the names of the units in the first place! + + These are the unit table "short-cut" commands: + + Prefixes: + "[-u]" - subtract this unit if its in the table + "[g]" - add this group to the table + "[-g]" - subtract this group from the table + "[c]" - add this country's units + "[-c]" - subtract this country's units if any are in the table + + Stand-alone identifiers + "[all]" - add all units + "[-all]" - subtract all units (not very useful by itself) + "[blue]" - add all blue units + "[-blue]" - subtract all blue units + "[red]" - add all red coalition units + "[-red]" - subtract all red units + + Compound Identifiers: + "[c][helicopter]" - add all of this country's helicopters + "[-c][helicopter]" - subtract all of this country's helicopters + "[c][plane]" - add all of this country's planes + "[-c][plane]" - subtract all of this country's planes + "[c][ship]" - add all of this country's ships + "[-c][ship]" - subtract all of this country's ships + "[c][vehicle]" - add all of this country's vehicles + "[-c][vehicle]" - subtract all of this country's vehicles + + "[all][helicopter]" - add all helicopters + "[-all][helicopter]" - subtract all helicopters + "[all][plane]" - add all planes + "[-all][plane]" - subtract all planes + "[all][ship]" - add all ships + "[-all][ship]" - subtract all ships + "[all][vehicle]" - add all vehicles + "[-all][vehicle]" - subtract all vehicles + + "[blue][helicopter]" - add all blue coalition helicopters + "[-blue][helicopter]" - subtract all blue coalition helicopters + "[blue][plane]" - add all blue coalition planes + "[-blue][plane]" - subtract all blue coalition planes + "[blue][ship]" - add all blue coalition ships + "[-blue][ship]" - subtract all blue coalition ships + "[blue][vehicle]" - add all blue coalition vehicles + "[-blue][vehicle]" - subtract all blue coalition vehicles + + "[red][helicopter]" - add all red coalition helicopters + "[-red][helicopter]" - subtract all red coalition helicopters + "[red][plane]" - add all red coalition planes + "[-red][plane]" - subtract all red coalition planes + "[red][ship]" - add all red coalition ships + "[-red][ship]" - subtract all red coalition ships + "[red][vehicle]" - add all red coalition vehicles + "[-red][vehicle]" - subtract all red coalition vehicles + + Country names to be used in [c] and [-c] short-cuts: + Turkey + Norway + The Netherlands + Spain + 11 + UK + Denmark + USA + Georgia + Germany + Belgium + Canada + France + Israel + Ukraine + Russia + South Ossetia + Abkhazia + Italy + Australia + Austria + Belarus + Bulgaria + Czech Republic + China + Croatia + Finland + Greece + Hungary + India + Iran + Iraq + Japan + Kazakhstan + North Korea + Pakistan + Poland + Romania + Saudi Arabia + Serbia, Slovakia + South Korea + Sweden + Switzerland + Syria + USAF Aggressors + + Do NOT use a '[u]' notation for single units. Single units are referenced + the same way as before: Simply input their names as strings. + + These unit tables are evaluated in order, and you cannot subtract a unit + from a table before it is added. For example: + + {'[blue]', '[-c]Georgia'} + + will evaluate to all of blue coalition except those units owned by the + country named "Georgia"; however: + + {'[-c]Georgia', '[blue]'} + + will evaluate to all of the units in blue coalition, because the addition + of all units owned by blue coalition occurred AFTER the subtraction of all + units owned by Georgia (which actually subtracted nothing at all, since + there were no units in the table when the subtraction occurred). + + More examples: + + {'[blue][plane]', '[-c]Georgia', '[-g]Hawg 1'} + + Evaluates to all blue planes, except those blue units owned by the country + named "Georgia" and the units in the group named "Hawg1". + + + {'[g]arty1', '[g]arty2', '[-u]arty1_AD', '[-u]arty2_AD', 'Shark 11' } + + Evaluates to the unit named "Shark 11", plus all the units in groups named + "arty1" and "arty2" except those that are named "arty1\_AD" and "arty2\_AD". + + @table UnitNameTable + ]] + + --- Returns a table containing unit names. + -- @tparam table tbl sequential strings + -- @treturn table @{UnitNameTable} + function mist.makeUnitTable(tbl) + --Assumption: will be passed a table of strings, sequential + local units_by_name = {} + + local l_munits = mist.DBs.units --local reference for faster execution + for i = 1, #tbl do + local unit = tbl[i] + if unit:sub(1,4) == '[-u]' then --subtract a unit + if units_by_name[unit:sub(5)] then -- 5 to end + units_by_name[unit:sub(5)] = nil --remove + end + elseif unit:sub(1,3) == '[g]' then -- add a group + for coa, coa_tbl in pairs(l_munits) do + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' and group_tbl.groupName == unit:sub(4) then + -- index 4 to end + for unit_ind, unit in pairs(group_tbl.units) do + units_by_name[unit.unitName] = true --add + end + end + end + end + end + end + end + elseif unit:sub(1,4) == '[-g]' then -- subtract a group + for coa, coa_tbl in pairs(l_munits) do + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' and group_tbl.groupName == unit:sub(5) then + -- index 5 to end + for unit_ind, unit in pairs(group_tbl.units) do + if units_by_name[unit.unitName] then + units_by_name[unit.unitName] = nil --remove + end + end + end + end + end + end + end + end + elseif unit:sub(1,3) == '[c]' then -- add a country + local category = '' + local country_start = 4 + if unit:sub(4,15) == '[helicopter]' then + category = 'helicopter' + country_start = 16 + elseif unit:sub(4,10) == '[plane]' then + category = 'plane' + country_start = 11 + elseif unit:sub(4,9) == '[ship]' then + category = 'ship' + country_start = 10 + elseif unit:sub(4,12) == '[vehicle]' then + category = 'vehicle' + country_start = 13 + end + for coa, coa_tbl in pairs(l_munits) do + for country, country_table in pairs(coa_tbl) do + if country == string.lower(unit:sub(country_start)) then -- match + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + units_by_name[unit.unitName] = true --add + end + end + end + end + end + end + end + end + elseif unit:sub(1,4) == '[-c]' then -- subtract a country + local category = '' + local country_start = 5 + if unit:sub(5,16) == '[helicopter]' then + category = 'helicopter' + country_start = 17 + elseif unit:sub(5,11) == '[plane]' then + category = 'plane' + country_start = 12 + elseif unit:sub(5,10) == '[ship]' then + category = 'ship' + country_start = 11 + elseif unit:sub(5,13) == '[vehicle]' then + category = 'vehicle' + country_start = 14 + end + for coa, coa_tbl in pairs(l_munits) do + for country, country_table in pairs(coa_tbl) do + if country == string.lower(unit:sub(country_start)) then -- match + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + if units_by_name[unit.unitName] then + units_by_name[unit.unitName] = nil --remove + end + end + end + end + end + end + end + end + end + elseif unit:sub(1,6) == '[blue]' then -- add blue coalition + local category = '' + if unit:sub(7) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(7) == '[plane]' then + category = 'plane' + elseif unit:sub(7) == '[ship]' then + category = 'ship' + elseif unit:sub(7) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + if coa == 'blue' then + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + units_by_name[unit.unitName] = true --add + end + end + end + end + end + end + end + end + elseif unit:sub(1,7) == '[-blue]' then -- subtract blue coalition + local category = '' + if unit:sub(8) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(8) == '[plane]' then + category = 'plane' + elseif unit:sub(8) == '[ship]' then + category = 'ship' + elseif unit:sub(8) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + if coa == 'blue' then + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + if units_by_name[unit.unitName] then + units_by_name[unit.unitName] = nil --remove + end + end + end + end + end + end + end + end + end + elseif unit:sub(1,5) == '[red]' then -- add red coalition + local category = '' + if unit:sub(6) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(6) == '[plane]' then + category = 'plane' + elseif unit:sub(6) == '[ship]' then + category = 'ship' + elseif unit:sub(6) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + if coa == 'red' then + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + units_by_name[unit.unitName] = true --add + end + end + end + end + end + end + end + end + elseif unit:sub(1,6) == '[-red]' then -- subtract red coalition + local category = '' + if unit:sub(7) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(7) == '[plane]' then + category = 'plane' + elseif unit:sub(7) == '[ship]' then + category = 'ship' + elseif unit:sub(7) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + if coa == 'red' then + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + if units_by_name[unit.unitName] then + units_by_name[unit.unitName] = nil --remove + end + end + end + end + end + end + end + end + end + elseif unit:sub(1,5) == '[all]' then -- add all of a certain category (or all categories) + local category = '' + if unit:sub(6) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(6) == '[plane]' then + category = 'plane' + elseif unit:sub(6) == '[ship]' then + category = 'ship' + elseif unit:sub(6) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + units_by_name[unit.unitName] = true --add + end + end + end + end + end + end + end + elseif unit:sub(1,6) == '[-all]' then -- subtract all of a certain category (or all categories) + local category = '' + if unit:sub(7) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(7) == '[plane]' then + category = 'plane' + elseif unit:sub(7) == '[ship]' then + category = 'ship' + elseif unit:sub(7) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + if units_by_name[unit.unitName] then + units_by_name[unit.unitName] = nil --remove + end + end + end + end + end + end + end + end + else -- just a regular unit + units_by_name[unit] = true --add + end + end + + local units_tbl = {} -- indexed sequentially + for unit_name, val in pairs(units_by_name) do + if val then + units_tbl[#units_tbl + 1] = unit_name -- add all the units to the table + end + end + + + units_tbl.processed = timer.getTime() --add the processed flag + return units_tbl end function mist.getDeadMapObjsInZones(zone_names) - -- zone_names: table of zone names - -- returns: table of dead map objects (indexed numerically) - local map_objs = {} - local zones = {} - for i = 1, #zone_names do - if mist.DBs.zonesByName[zone_names[i]] then - zones[#zones + 1] = mist.DBs.zonesByName[zone_names[i]] - end - end - for obj_id, obj in pairs(mist.DBs.deadObjects) do - if obj.objectType and obj.objectType == 'building' then --dead map object - for i = 1, #zones do - if ((zones[i].point.x - obj.objectPos.x)^2 + (zones[i].point.z - obj.objectPos.z)^2)^0.5 <= zones[i].radius then - map_objs[#map_objs + 1] = mist.utils.deepCopy(obj) - end - end - end - end - return map_objs + -- zone_names: table of zone names + -- returns: table of dead map objects (indexed numerically) + local map_objs = {} + local zones = {} + for i = 1, #zone_names do + if mist.DBs.zonesByName[zone_names[i]] then + zones[#zones + 1] = mist.DBs.zonesByName[zone_names[i]] + end + end + for obj_id, obj in pairs(mist.DBs.deadObjects) do + if obj.objectType and obj.objectType == 'building' then --dead map object + for i = 1, #zones do + if ((zones[i].point.x - obj.objectPos.x)^2 + (zones[i].point.z - obj.objectPos.z)^2)^0.5 <= zones[i].radius then + map_objs[#map_objs + 1] = mist.utils.deepCopy(obj) + end + end + end + end + return map_objs end function mist.getDeadMapObjsInPolygonZone(zone) - -- zone_names: table of zone names - -- returns: table of dead map objects (indexed numerically) - local map_objs = {} - for obj_id, obj in pairs(mist.DBs.deadObjects) do - if obj.objectType and obj.objectType == 'building' then --dead map object - if mist.pointInPolygon(obj.objectPos, zone) then - map_objs[#map_objs + 1] = mist.utils.deepCopy(obj) - end - end - end - return map_objs + -- zone_names: table of zone names + -- returns: table of dead map objects (indexed numerically) + local map_objs = {} + for obj_id, obj in pairs(mist.DBs.deadObjects) do + if obj.objectType and obj.objectType == 'building' then --dead map object + if mist.pointInPolygon(obj.objectPos, zone) then + map_objs[#map_objs + 1] = mist.utils.deepCopy(obj) + end + end + end + return map_objs end function mist.pointInPolygon(point, poly, maxalt) --raycasting point in polygon. Code from http://softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm - --[[local type_tbl = { - point = {'table'}, - poly = {'table'}, - maxalt = {'number', 'nil'}, - } + --[[local type_tbl = { + point = {'table'}, + poly = {'table'}, + maxalt = {'number', 'nil'}, + } - local err, errmsg = mist.utils.typeCheck('mist.pointInPolygon', type_tbl, {point, poly, maxalt}) - assert(err, errmsg) - ]] - point = mist.utils.makeVec3(point) - local px = point.x - local pz = point.z - local cn = 0 - local newpoly = mist.utils.deepCopy(poly) + local err, errmsg = mist.utils.typeCheck('mist.pointInPolygon', type_tbl, {point, poly, maxalt}) + assert(err, errmsg) + ]] + point = mist.utils.makeVec3(point) + local px = point.x + local pz = point.z + local cn = 0 + local newpoly = mist.utils.deepCopy(poly) - if not maxalt or (point.y <= maxalt) then - local polysize = #newpoly - newpoly[#newpoly + 1] = newpoly[1] + if not maxalt or (point.y <= maxalt) then + local polysize = #newpoly + newpoly[#newpoly + 1] = newpoly[1] - newpoly[1] = mist.utils.makeVec3(newpoly[1]) + newpoly[1] = mist.utils.makeVec3(newpoly[1]) - for k = 1, polysize do - newpoly[k+1] = mist.utils.makeVec3(newpoly[k+1]) - if ((newpoly[k].z <= pz) and (newpoly[k+1].z > pz)) or ((newpoly[k].z > pz) and (newpoly[k+1].z <= pz)) then - local vt = (pz - newpoly[k].z) / (newpoly[k+1].z - newpoly[k].z) - if (px < newpoly[k].x + vt*(newpoly[k+1].x - newpoly[k].x)) then - cn = cn + 1 - end - end - end + for k = 1, polysize do + newpoly[k+1] = mist.utils.makeVec3(newpoly[k+1]) + if ((newpoly[k].z <= pz) and (newpoly[k+1].z > pz)) or ((newpoly[k].z > pz) and (newpoly[k+1].z <= pz)) then + local vt = (pz - newpoly[k].z) / (newpoly[k+1].z - newpoly[k].z) + if (px < newpoly[k].x + vt*(newpoly[k+1].x - newpoly[k].x)) then + cn = cn + 1 + end + end + end - return cn%2 == 1 - else - return false - end + return cn%2 == 1 + else + return false + end end function mist.getUnitsInPolygon(unit_names, polyZone, max_alt) - local units = {} + local units = {} - for i = 1, #unit_names do - units[#units + 1] = Unit.getByName(unitNames[i]) - end + for i = 1, #unit_names do + units[#units + 1] = Unit.getByName(unitNames[i]) + end - local inZoneUnits = {} - for i =1, #units do - if units[i]:isActive() and mist.pointInPolygon(units[i]:getPosition().p, polyZone, max_alt) then - inZoneUnits[inZoneUnits + 1] = units[i] - end - end + local inZoneUnits = {} + for i =1, #units do + if units[i]:isActive() and mist.pointInPolygon(units[i]:getPosition().p, polyZone, max_alt) then + inZoneUnits[inZoneUnits + 1] = units[i] + end + end - return inZoneUnits + return inZoneUnits end function mist.getUnitsInZones(unit_names, zone_names, zone_type) - zone_type = zone_type or 'cylinder' - if zone_type == 'c' or zone_type == 'cylindrical' or zone_type == 'C' then - zone_type = 'cylinder' - end - if zone_type == 's' or zone_type == 'spherical' or zone_type == 'S' then - zone_type = 'sphere' - end + zone_type = zone_type or 'cylinder' + if zone_type == 'c' or zone_type == 'cylindrical' or zone_type == 'C' then + zone_type = 'cylinder' + end + if zone_type == 's' or zone_type == 'spherical' or zone_type == 'S' then + zone_type = 'sphere' + end - assert(zone_type == 'cylinder' or zone_type == 'sphere', 'invalid zone_type: ' .. tostring(zone_type)) + assert(zone_type == 'cylinder' or zone_type == 'sphere', 'invalid zone_type: ' .. tostring(zone_type)) - local units = {} - local zones = {} + local units = {} + local zones = {} - for k = 1, #unit_names do - local unit = Unit.getByName(unit_names[k]) - if unit then - units[#units + 1] = unit - end - end + for k = 1, #unit_names do + local unit = Unit.getByName(unit_names[k]) + if unit then + units[#units + 1] = unit + end + end - for k = 1, #zone_names do - local zone = trigger.misc.getZone(zone_names[k]) - if zone then - zones[#zones + 1] = {radius = zone.radius, x = zone.point.x, y = zone.point.y, z = zone.point.z} - end - end + for k = 1, #zone_names do + local zone = trigger.misc.getZone(zone_names[k]) + if zone then + zones[#zones + 1] = {radius = zone.radius, x = zone.point.x, y = zone.point.y, z = zone.point.z} + end + end - local in_zone_units = {} + local in_zone_units = {} - for units_ind = 1, #units do - for zones_ind = 1, #zones do - if zone_type == 'sphere' then --add land height value for sphere zone type - local alt = land.getHeight({x = zones[zones_ind].x, y = zones[zones_ind].z}) - if alt then - zones[zones_ind].y = alt - end - end - local unit_pos = units[units_ind]:getPosition().p - if unit_pos and units[units_ind]:isActive() == true then - if zone_type == 'cylinder' and (((unit_pos.x - zones[zones_ind].x)^2 + (unit_pos.z - zones[zones_ind].z)^2)^0.5 <= zones[zones_ind].radius) then - in_zone_units[#in_zone_units + 1] = units[units_ind] - break - elseif zone_type == 'sphere' and (((unit_pos.x - zones[zones_ind].x)^2 + (unit_pos.y - zones[zones_ind].y)^2 + (unit_pos.z - zones[zones_ind].z)^2)^0.5 <= zones[zones_ind].radius) then - in_zone_units[#in_zone_units + 1] = units[units_ind] - break - end - end - end - end - return in_zone_units + for units_ind = 1, #units do + for zones_ind = 1, #zones do + if zone_type == 'sphere' then --add land height value for sphere zone type + local alt = land.getHeight({x = zones[zones_ind].x, y = zones[zones_ind].z}) + if alt then + zones[zones_ind].y = alt + end + end + local unit_pos = units[units_ind]:getPosition().p + if unit_pos and units[units_ind]:isActive() == true then + if zone_type == 'cylinder' and (((unit_pos.x - zones[zones_ind].x)^2 + (unit_pos.z - zones[zones_ind].z)^2)^0.5 <= zones[zones_ind].radius) then + in_zone_units[#in_zone_units + 1] = units[units_ind] + break + elseif zone_type == 'sphere' and (((unit_pos.x - zones[zones_ind].x)^2 + (unit_pos.y - zones[zones_ind].y)^2 + (unit_pos.z - zones[zones_ind].z)^2)^0.5 <= zones[zones_ind].radius) then + in_zone_units[#in_zone_units + 1] = units[units_ind] + break + end + end + end + end + return in_zone_units end function mist.getUnitsInMovingZones(unit_names, zone_unit_names, radius, zone_type) - zone_type = zone_type or 'cylinder' - if zone_type == 'c' or zone_type == 'cylindrical' or zone_type == 'C' then - zone_type = 'cylinder' - end - if zone_type == 's' or zone_type == 'spherical' or zone_type == 'S' then - zone_type = 'sphere' - end + zone_type = zone_type or 'cylinder' + if zone_type == 'c' or zone_type == 'cylindrical' or zone_type == 'C' then + zone_type = 'cylinder' + end + if zone_type == 's' or zone_type == 'spherical' or zone_type == 'S' then + zone_type = 'sphere' + end - assert(zone_type == 'cylinder' or zone_type == 'sphere', 'invalid zone_type: ' .. tostring(zone_type)) + assert(zone_type == 'cylinder' or zone_type == 'sphere', 'invalid zone_type: ' .. tostring(zone_type)) - local units = {} - local zone_units = {} + local units = {} + local zone_units = {} - for k = 1, #unit_names do - local unit = Unit.getByName(unit_names[k]) - if unit then - units[#units + 1] = unit - end - end + for k = 1, #unit_names do + local unit = Unit.getByName(unit_names[k]) + if unit then + units[#units + 1] = unit + end + end - for k = 1, #zone_unit_names do - local unit = Unit.getByName(zone_unit_names[k]) - if unit then - zone_units[#zone_units + 1] = unit - end - end + for k = 1, #zone_unit_names do + local unit = Unit.getByName(zone_unit_names[k]) + if unit then + zone_units[#zone_units + 1] = unit + end + end - local in_zone_units = {} + local in_zone_units = {} - for units_ind = 1, #units do - for zone_units_ind = 1, #zone_units do - local unit_pos = units[units_ind]:getPosition().p - local zone_unit_pos = zone_units[zone_units_ind]:getPosition().p - if unit_pos and zone_unit_pos and units[units_ind]:isActive() == true then - if zone_type == 'cylinder' and (((unit_pos.x - zone_unit_pos.x)^2 + (unit_pos.z - zone_unit_pos.z)^2)^0.5 <= radius) then - in_zone_units[#in_zone_units + 1] = units[units_ind] - break - elseif zone_type == 'sphere' and (((unit_pos.x - zone_unit_pos.x)^2 + (unit_pos.y - zone_unit_pos.y)^2 + (unit_pos.z - zone_unit_pos.z)^2)^0.5 <= radius) then - in_zone_units[#in_zone_units + 1] = units[units_ind] - break - end - end - end - end - return in_zone_units + for units_ind = 1, #units do + for zone_units_ind = 1, #zone_units do + local unit_pos = units[units_ind]:getPosition().p + local zone_unit_pos = zone_units[zone_units_ind]:getPosition().p + if unit_pos and zone_unit_pos and units[units_ind]:isActive() == true then + if zone_type == 'cylinder' and (((unit_pos.x - zone_unit_pos.x)^2 + (unit_pos.z - zone_unit_pos.z)^2)^0.5 <= radius) then + in_zone_units[#in_zone_units + 1] = units[units_ind] + break + elseif zone_type == 'sphere' and (((unit_pos.x - zone_unit_pos.x)^2 + (unit_pos.y - zone_unit_pos.y)^2 + (unit_pos.z - zone_unit_pos.z)^2)^0.5 <= radius) then + in_zone_units[#in_zone_units + 1] = units[units_ind] + break + end + end + end + end + return in_zone_units end function mist.getUnitsLOS(unitset1, altoffset1, unitset2, altoffset2, radius) - radius = radius or math.huge + radius = radius or math.huge - local unit_info1 = {} - local unit_info2 = {} + local unit_info1 = {} + local unit_info2 = {} - -- get the positions all in one step, saves execution time. - for unitset1_ind = 1, #unitset1 do - local unit1 = Unit.getByName(unitset1[unitset1_ind]) - if unit1 and unit1:isActive() == true then - unit_info1[#unit_info1 + 1] = {} - unit_info1[#unit_info1].unit = unit1 - unit_info1[#unit_info1].pos = unit1:getPosition().p - end - end + -- get the positions all in one step, saves execution time. + for unitset1_ind = 1, #unitset1 do + local unit1 = Unit.getByName(unitset1[unitset1_ind]) + if unit1 and unit1:isActive() == true then + unit_info1[#unit_info1 + 1] = {} + unit_info1[#unit_info1].unit = unit1 + unit_info1[#unit_info1].pos = unit1:getPosition().p + end + end - for unitset2_ind = 1, #unitset2 do - local unit2 = Unit.getByName(unitset2[unitset2_ind]) - if unit2 and unit2:isActive() == true then - unit_info2[#unit_info2 + 1] = {} - unit_info2[#unit_info2].unit = unit2 - unit_info2[#unit_info2].pos = unit2:getPosition().p - end - end + for unitset2_ind = 1, #unitset2 do + local unit2 = Unit.getByName(unitset2[unitset2_ind]) + if unit2 and unit2:isActive() == true then + unit_info2[#unit_info2 + 1] = {} + unit_info2[#unit_info2].unit = unit2 + unit_info2[#unit_info2].pos = unit2:getPosition().p + end + end - local LOS_data = {} - -- now compute los - for unit1_ind = 1, #unit_info1 do - local unit_added = false - for unit2_ind = 1, #unit_info2 do - if radius == math.huge or (mist.vec.mag(mist.vec.sub(unit_info1[unit1_ind].pos, unit_info2[unit2_ind].pos)) < radius) then -- inside radius - local point1 = { x = unit_info1[unit1_ind].pos.x, y = unit_info1[unit1_ind].pos.y + altoffset1, z = unit_info1[unit1_ind].pos.z} - local point2 = { x = unit_info2[unit2_ind].pos.x, y = unit_info2[unit2_ind].pos.y + altoffset2, z = unit_info2[unit2_ind].pos.z} - if land.isVisible(point1, point2) then - if unit_added == false then - unit_added = true - LOS_data[#LOS_data + 1] = {} - LOS_data[#LOS_data].unit = unit_info1[unit1_ind].unit - LOS_data[#LOS_data].vis = {} - LOS_data[#LOS_data].vis[#LOS_data[#LOS_data].vis + 1] = unit_info2[unit2_ind].unit - else - LOS_data[#LOS_data].vis[#LOS_data[#LOS_data].vis + 1] = unit_info2[unit2_ind].unit - end - end - end - end - end + local LOS_data = {} + -- now compute los + for unit1_ind = 1, #unit_info1 do + local unit_added = false + for unit2_ind = 1, #unit_info2 do + if radius == math.huge or (mist.vec.mag(mist.vec.sub(unit_info1[unit1_ind].pos, unit_info2[unit2_ind].pos)) < radius) then -- inside radius + local point1 = { x = unit_info1[unit1_ind].pos.x, y = unit_info1[unit1_ind].pos.y + altoffset1, z = unit_info1[unit1_ind].pos.z} + local point2 = { x = unit_info2[unit2_ind].pos.x, y = unit_info2[unit2_ind].pos.y + altoffset2, z = unit_info2[unit2_ind].pos.z} + if land.isVisible(point1, point2) then + if unit_added == false then + unit_added = true + LOS_data[#LOS_data + 1] = {} + LOS_data[#LOS_data].unit = unit_info1[unit1_ind].unit + LOS_data[#LOS_data].vis = {} + LOS_data[#LOS_data].vis[#LOS_data[#LOS_data].vis + 1] = unit_info2[unit2_ind].unit + else + LOS_data[#LOS_data].vis[#LOS_data[#LOS_data].vis + 1] = unit_info2[unit2_ind].unit + end + end + end + end + end - return LOS_data + return LOS_data end function mist.getAvgPoint(points) - local avgX, avgY, avgZ, totNum = 0, 0, 0, 0 - for i = 1, #points do - local nPoint = mist.utils.makeVec3(points[i]) - if nPoint.z then - avgX = avgX + nPoint.x - avgY = avgY + nPoint.y - avgZ = avgZ + nPoint.z - totNum = totNum + 1 - end - end - if totNum ~= 0 then - return {x = avgX/totNum, y = avgY/totNum, z = avgZ/totNum} - end + local avgX, avgY, avgZ, totNum = 0, 0, 0, 0 + for i = 1, #points do + local nPoint = mist.utils.makeVec3(points[i]) + if nPoint.z then + avgX = avgX + nPoint.x + avgY = avgY + nPoint.y + avgZ = avgZ + nPoint.z + totNum = totNum + 1 + end + end + if totNum ~= 0 then + return {x = avgX/totNum, y = avgY/totNum, z = avgZ/totNum} + end end --Gets the average position of a group of units (by name) function mist.getAvgPos(unitNames) - local avgX, avgY, avgZ, totNum = 0, 0, 0, 0 - for i = 1, #unitNames do - local unit - if Unit.getByName(unitNames[i]) then - unit = Unit.getByName(unitNames[i]) - elseif StaticObject.getByName(unitNames[i]) then - unit = StaticObject.getByName(unitNames[i]) - end - if unit then - local pos = unit:getPosition().p - if pos then -- you never know O.o - avgX = avgX + pos.x - avgY = avgY + pos.y - avgZ = avgZ + pos.z - totNum = totNum + 1 - end - end - end - if totNum ~= 0 then - return {x = avgX/totNum, y = avgY/totNum, z = avgZ/totNum} - end + local avgX, avgY, avgZ, totNum = 0, 0, 0, 0 + for i = 1, #unitNames do + local unit + if Unit.getByName(unitNames[i]) then + unit = Unit.getByName(unitNames[i]) + elseif StaticObject.getByName(unitNames[i]) then + unit = StaticObject.getByName(unitNames[i]) + end + if unit then + local pos = unit:getPosition().p + if pos then -- you never know O.o + avgX = avgX + pos.x + avgY = avgY + pos.y + avgZ = avgZ + pos.z + totNum = totNum + 1 + end + end + end + if totNum ~= 0 then + return {x = avgX/totNum, y = avgY/totNum, z = avgZ/totNum} + end end function mist.getAvgGroupPos(groupName) - if type(groupName) == 'string' and Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then - groupName = Group.getByName(groupName) - end - local units = {} - for i = 1, #groupName:getSize() do - table.insert(units, groupName.getUnit(i):getName()) - end + if type(groupName) == 'string' and Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then + groupName = Group.getByName(groupName) + end + local units = {} + for i = 1, #groupName:getSize() do + table.insert(units, groupName.getUnit(i):getName()) + end - return mist.getAvgPos(units) + return mist.getAvgPos(units) end @@ -2588,51 +2587,51 @@ vars.units - table of unit names (NOT unitNameTable- maybe this should change). vars.acc - integer between 0 and 5, inclusive ]] function mist.getMGRSString(vars) - local units = vars.units - local acc = vars.acc or 5 - local avgPos = mist.getAvgPos(units) - if avgPos then - return mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(avgPos)), acc) - end + local units = vars.units + local acc = vars.acc or 5 + local avgPos = mist.getAvgPos(units) + if avgPos then + return mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(avgPos)), acc) + end end --[[ vars for mist.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. +vars.DMS - if true, output in degrees, minutes, seconds. Otherwise, output in degrees, minutes. ]] function mist.getLLString(vars) - local units = vars.units - local acc = vars.acc or 3 - local DMS = vars.DMS - local avgPos = mist.getAvgPos(units) - if avgPos then - local lat, lon = coord.LOtoLL(avgPos) - return mist.tostringLL(lat, lon, acc, DMS) - end + local units = vars.units + local acc = vars.acc or 3 + local DMS = vars.DMS + local avgPos = mist.getAvgPos(units) + if avgPos then + local lat, lon = coord.LOtoLL(avgPos) + return mist.tostringLL(lat, lon, acc, DMS) + 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.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. ]] function mist.getBRString(vars) - local units = vars.units - local ref = mist.utils.makeVec3(vars.ref, 0) -- turn it into Vec3 if it is not already. - local alt = vars.alt - local metric = vars.metric - local avgPos = mist.getAvgPos(units) - if avgPos then - local vec = {x = avgPos.x - ref.x, y = avgPos.y - ref.y, z = avgPos.z - ref.z} - local dir = mist.utils.getDir(vec, ref) - local dist = mist.utils.get2DDist(avgPos, ref) - if alt then - alt = avgPos.y - end - return mist.tostringBR(dir, dist, alt, metric) - end + local units = vars.units + local ref = mist.utils.makeVec3(vars.ref, 0) -- turn it into Vec3 if it is not already. + local alt = vars.alt + local metric = vars.metric + local avgPos = mist.getAvgPos(units) + if avgPos then + local vec = {x = avgPos.x - ref.x, y = avgPos.y - ref.y, z = avgPos.z - ref.z} + local dir = mist.utils.getDir(vec, ref) + local dist = mist.utils.get2DDist(avgPos, ref) + if alt then + alt = avgPos.y + end + return mist.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. @@ -2643,53 +2642,53 @@ vars.radius - number vars.headingDegrees - boolean, switches heading to degrees ]] function mist.getLeadingPos(vars) - local units = vars.units - local heading = vars.heading - local radius = vars.radius - if vars.headingDegrees then - heading = mist.utils.toRadian(vars.headingDegrees) - end + local units = vars.units + local heading = vars.heading + local radius = vars.radius + if vars.headingDegrees then + heading = mist.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 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 = mist.vec.rotateVec2(mist.utils.makeVec2(unitPosTbl[i]), heading) - if (not maxPos) or maxPos < rotatedVec2.x then - maxPos = rotatedVec2.x - maxPosInd = i - end - end + local maxPosInd -- maxPos - the furthest in direction defined by heading; maxPosInd = + for i = 1, #unitPosTbl do + local rotatedVec2 = mist.vec.rotateVec2(mist.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 mist.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 + --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 mist.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 + return avgPos + end end --[[ vars for mist.getLeadingMGRSString: @@ -2700,11 +2699,11 @@ vars.headingDegrees - boolean, switches heading to degrees vars.acc - number, 0 to 5. ]] function mist.getLeadingMGRSString(vars) - local pos = mist.getLeadingPos(vars) - if pos then - local acc = vars.acc or 5 - return mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(pos)), acc) - end + local pos = mist.getLeadingPos(vars) + if pos then + local acc = vars.acc or 5 + return mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(pos)), acc) + end end --[[ vars for mist.getLeadingLLString: @@ -2713,16 +2712,16 @@ 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. +vars.DMS - boolean, true if you want DMS. ]] function mist.getLeadingLLString(vars) - local pos = mist.getLeadingPos(vars) - if pos then - local acc = vars.acc or 3 - local DMS = vars.DMS - local lat, lon = coord.LOtoLL(pos) - return mist.tostringLL(lat, lon, acc, DMS) - end + local pos = mist.getLeadingPos(vars) + if pos then + local acc = vars.acc or 3 + local DMS = vars.DMS + local lat, lon = coord.LOtoLL(pos) + return mist.tostringLL(lat, lon, acc, DMS) + end end --[[ vars for mist.getLeadingBRString: @@ -2735,20 +2734,20 @@ vars.alt - boolean, if true, include altitude. vars.ref - vec3/vec2 reference point. ]] function mist.getLeadingBRString(vars) - local pos = mist.getLeadingPos(vars) - if pos then - local ref = vars.ref - local alt = vars.alt - local metric = vars.metric + local pos = mist.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 = mist.utils.getDir(vec, ref) - local dist = mist.utils.get2DDist(pos, ref) - if alt then - alt = pos.y - end - return mist.tostringBR(dir, dist, alt, metric) - end + local vec = {x = pos.x - ref.x, y = pos.y - ref.y, z = pos.z - ref.z} + local dir = mist.utils.getDir(vec, ref) + local dist = mist.utils.get2DDist(pos, ref) + if alt then + alt = pos.y + end + return mist.tostringBR(dir, dist, alt, metric) + end end end @@ -2757,694 +2756,694 @@ end -- @section groups do -- group functions scope - --- Check table used for group creation. - -- @tparam table groupData table to check. - -- @treturn boolean true if a group can be spawned using - -- this table, false otherwise. - function mist.groupTableCheck(groupData) - -- return false if country, category - -- or units are missing - if not groupData.country or - not groupData.category or - not groupData.units then - return false - end - -- return false if unitData misses - -- x, y or type - for unitId, unitData in pairs(groupData.units) do - if not unitData.x or - not unitData.y or - not unitData.type then - return false - end - end - -- everything we need is here return true - return true - end - - --- Returns group data table of give group. - function mist.getCurrentGroupData(gpName) - local dbData = mist.getGroupData(gpName) - - if Group.getByName(gpName) and Group.getByName(gpName):isExist() == true then - local newGroup = Group.getByName(gpName) - local newData = {} - newData.name = gpName - newData.groupId = tonumber(newGroup:getID()) - newData.category = newGroup:getCategory() - newData.groupName = gpName - newData.hidden = dbData.hidden - - if newData.category == 2 then - newData.category = 'vehicle' - elseif newData.category == 3 then - newData.category = 'ship' - end - - newData.units = {} - local newUnits = newGroup:getUnits() - for unitNum, unitData in pairs(newGroup:getUnits()) do - newData.units[unitNum] = {} - newData.units[unitNum].unitId = tonumber(unitData:getID()) - newData.units[unitNum].point = unitData.point - newData.units[unitNum].x = unitData:getPosition().p.x - newData.units[unitNum].y = unitData:getPosition().p.z - newData.units[unitNum].type = unitData:getTypeName() - newData.units[unitNum].skill = mist.getUnitSkill(unitData:getName()) - - -- get velocity needed - newData.units[unitNum].unitName = unitData:getName() - newData.units[unitNum].heading = mist.getHeading(unitData, true) -- added to DBs - newData.units[unitNum].alt = unitData:getPosition().p.y - newData.country = string.lower(country.name[unitData:getCountry()]) - newData.units[unitNum].callsign = unitData:getCallsign() - end - - return newData - elseif StaticObject.getByName(gpName) and StaticObject.getByName(gpName):isExist() == true then - local staticObj = StaticObject.getByName(gpName) - dbData.units[1].x = staticObj:getPosition().p.x - dbData.units[1].y = staticObj:getPosition().p.z - dbData.units[1].alt = staticObj:getPosition().p.y - dbData.units[1].heading = mist.getHeading(staticObj, true) - - return dbData - end - - end - - function mist.getGroupData(gpName) - local found = false - local newData = {} - if mist.DBs.groupsByName[gpName] then - newData = mist.utils.deepCopy(mist.DBs.groupsByName[gpName]) - found = true - end - - if found == false then - for groupName, groupData in pairs(mist.DBs.groupsByName) do - if mist.stringMatch(groupName, gpName) == true then - newData = mist.utils.deepCopy(groupData) - newData.groupName = groupName - found = true - break - end - end - end - - local payloads - if newData.category == 'plane' or newData.category == 'helicopter' then - payloads = mist.getGroupPayload(newData.groupName) - end - if found == true then - --newData.hidden = false -- maybe add this to DBs - - for unitNum, unitData in pairs(newData.units) do - newData.units[unitNum] = {} - - newData.units[unitNum].unitId = unitData.unitId - --newData.units[unitNum].point = unitData.point - newData.units[unitNum].x = unitData.point.x - newData.units[unitNum].y = unitData.point.y - newData.units[unitNum].alt = unitData.alt - newData.units[unitNum].alt_type = unitData.alt_type - newData.units[unitNum].speed = unitData.speed - newData.units[unitNum].type = unitData.type - newData.units[unitNum].skill = unitData.skill - newData.units[unitNum].unitName = unitData.unitName - newData.units[unitNum].heading = unitData.heading -- added to DBs - newData.units[unitNum].playerCanDrive = unitData.playerCanDrive -- added to DBs - - - if newData.category == 'plane' or newData.category == 'helicopter' then - newData.units[unitNum].payload = payloads[unitNum] - newData.units[unitNum].livery_id = unitData.livery_id - newData.units[unitNum].onboard_num = unitData.onboard_num - newData.units[unitNum].callsign = unitData.callsign - newData.units[unitNum].AddPropAircraft = unitData.AddPropAircraft - end - if newData.category == 'static' then - newData.units[unitNum].categoryStatic = unitData.categoryStatic - newData.units[unitNum].mass = unitData.mass - newData.units[unitNum].canCargo = unitData.canCargo - newData.units[unitNum].shape_name = unitData.shape_name - end - end - - return newData - else - log:error('$1 not found in MIST database', gpName) - return - end - end - - function mist.getPayload(unitIdent) - -- refactor to search by groupId and allow groupId and groupName as inputs - local unitId = unitIdent - if type(unitIdent) == 'string' and not tonumber(unitIdent) then - unitId = mist.DBs.MEunitsByName[unitIdent].unitId - end - local gpId = mist.DBs.MEunitsById[unitId].groupId - - if gpId and unitId then - 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 - for unitIndex, unitData in pairs(group_data.units) do --group index - if unitData.unitId == unitId then - return unitData.payload - end - end - end - end - end - end - end - end - end - end - end - else - log:error('Need string or number. Got: $1', type(unitIdent)) - return false - end - log:warn("Couldn't find payload for unit: $1", unitIdent) - return - end - - function mist.getGroupPayload(groupIdent) - local gpId = groupIdent - if type(groupIdent) == 'string' and not tonumber(groupIdent) then - gpId = mist.DBs.MEgroupsByName[groupIdent].groupId - end - - if gpId then - 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 - local payloads = {} - for unitIndex, unitData in pairs(group_data.units) do --group index - payloads[unitIndex] = unitData.payload - end - return payloads - end - end - end - end - end - end - end - end - end - else - log:error('Need string or number. Got: $1', type(groupIdent)) - return false - end - log:warn("Couldn't find payload for group: $1", groupIdent) - return - - end - - function mist.teleportToPoint(vars) -- main teleport function that all of teleport/respawn functions call - local point = vars.point - - local gpName - if vars.gpName then - gpName = vars.gpName - elseif vars.groupName then - gpName = vars.groupName - else - log:error('Missing field groupName or gpName in variable table') - end - - local action = vars.action - - local disperse = vars.disperse or false - local maxDisp = vars.maxDisp - if not vars.maxDisp then - maxDisp = 200 - else - maxDisp = vars.maxDisp - end - local radius = vars.radius or 0 - local innerRadius = vars.innerRadius - - local route = vars.route - local dbData = false - - local newGroupData - if gpName and not vars.groupData then - if string.lower(action) == 'teleport' or string.lower(action) == 'tele' then - newGroupData = mist.getCurrentGroupData(gpName) - elseif string.lower(action) == 'respawn' then - newGroupData = mist.getGroupData(gpName) - dbData = true - elseif string.lower(action) == 'clone' then - newGroupData = mist.getGroupData(gpName) - newGroupData.clone = 'order66' - dbData = true - else - action = 'tele' - newGroupData = mist.getCurrentGroupData(gpName) - end - else - action = 'tele' - newGroupData = vars.groupData - end - - local diff = {x = 0, y = 0} - local newCoord, origCoord - if point then - local valid = false - - local validTerrain - if string.lower(newGroupData.category) == 'ship' then - validTerrain = {'SHALLOW_WATER' , 'WATER'} - elseif string.lower(newGroupData.category) == 'vehicle' then - validTerrain = {'LAND', 'ROAD'} - else - validTerrain = {'LAND', 'ROAD', 'SHALLOW_WATER', 'WATER', 'RUNWAY'} - end - - for i = 1, 100 do - newCoord = mist.getRandPointInCircle(point, radius, innerRadius) - if mist.isTerrainValid(newCoord, validTerrain) then - origCoord = mist.utils.deepCopy(newCoord) - diff = {x = (newCoord.x - newGroupData.units[1].x), y = (newCoord.y - newGroupData.units[1].y)} - valid = true - break - end - end - if valid == false then - log:error('point supplied in variable table is not a valid coordinate.') - return false - end - end - if not newGroupData.country and mist.DBs.groupsByName[newGroupData.groupName].country then - newGroupData.country = mist.DBs.groupsByName[newGroupData.groupName].country - end - if not newGroupData.category and mist.DBs.groupsByName[newGroupData.groupName].category then - newGroupData.category = mist.DBs.groupsByName[newGroupData.groupName].category - end - - for unitNum, unitData in pairs(newGroupData.units) do - if disperse then - if maxDisp and type(maxDisp) == 'number' and unitNum ~= 1 then - newCoord = mist.getRandPointInCircle(origCoord, maxDisp) - --else - --newCoord = mist.getRandPointInCircle(zone.point, zone.radius) - end - - newGroupData.units[unitNum].x = newCoord.x - newGroupData.units[unitNum].y = newCoord.y - else - newGroupData.units[unitNum].x = unitData.x + diff.x - newGroupData.units[unitNum].y = unitData.y + diff.y - end - if point then - if (newGroupData.category == 'plane' or newGroupData.category == 'helicopter') then - if point.z and point.y > 0 and point.y > land.getHeight({newGroupData.units[unitNum].x, newGroupData.units[unitNum].y}) + 10 then - newGroupData.units[unitNum].alt = point.y - else - if newGroupData.category == 'plane' then - newGroupData.units[unitNum].alt = land.getHeight({newGroupData.units[unitNum].x, newGroupData.units[unitNum].y}) + math.random(300, 9000) - else - newGroupData.units[unitNum].alt = land.getHeight({newGroupData.units[unitNum].x, newGroupData.units[unitNum].y}) + math.random(200, 3000) - end - end - end - end - end - - if newGroupData.start_time then - newGroupData.startTime = newGroupData.start_time - end - - if newGroupData.startTime and newGroupData.startTime ~= 0 and dbData == true then - local timeDif = timer.getAbsTime() - timer.getTime0() - if timeDif > newGroupData.startTime then - newGroupData.startTime = 0 - else - newGroupData.startTime = newGroupData.startTime - timeDif - end - - end - - if route then - newGroupData.route = route - end - --mist.debug.writeData(mist.utils.serialize,{'teleportToPoint', newGroupData}, 'newGroupData.lua') - if string.lower(newGroupData.category) == 'static' then - return mist.dynAddStatic(newGroupData) - end - return mist.dynAdd(newGroupData) - - end - - function mist.respawnInZone(gpName, zone, disperse, maxDisp) - - if type(gpName) == 'table' and gpName:getName() then - gpName = gpName:getName() - elseif type(gpName) == 'table' and gpName[1]:getName() then - gpName = math.random(#gpName) - else - gpName = tostring(gpName) - 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 - local vars = {} - vars.gpName = gpName - vars.action = 'respawn' - vars.point = zone.point - vars.radius = zone.radius - vars.disperse = disperse - vars.maxDisp = maxDisp - return mist.teleportToPoint(vars) - end - - function mist.cloneInZone(gpName, zone, disperse, maxDisp) - - if type(gpName) == 'table' then - gpName = gpName:getName() - else - gpName = tostring(gpName) - 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 - local vars = {} - vars.gpName = gpName - vars.action = 'clone' - vars.point = zone.point - vars.radius = zone.radius - vars.disperse = disperse - vars.maxDisp = maxDisp - return mist.teleportToPoint(vars) - end - - function mist.teleportInZone(gpName, zone, disperse, maxDisp) -- groupName, zoneName or table of Zone Names, keepForm is a boolean - if type(gpName) == 'table' and gpName:getName() then - gpName = gpName:getName() - else - gpName = tostring(gpName) - 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 - - local vars = {} - vars.gpName = gpName - vars.action = 'tele' - vars.point = zone.point - vars.radius = zone.radius - vars.disperse = disperse - vars.maxDisp = maxDisp - return mist.teleportToPoint(vars) - end - - function mist.respawnGroup(gpName, task) - local vars = {} - vars.gpName = gpName - vars.action = 'respawn' - if task and type(task) ~= 'number' then - vars.route = mist.getGroupRoute(gpName, 'task') - end - local newGroup = mist.teleportToPoint(vars) - if task and type(task) == 'number' then - local newRoute = mist.getGroupRoute(gpName, 'task') - mist.scheduleFunction(mist.goRoute, {newGroup, newRoute}, timer.getTime() + task) - end - return newGroup - end - - function mist.cloneGroup(gpName, task) - local vars = {} - vars.gpName = gpName - vars.action = 'clone' - if task and type(task) ~= 'number' then - vars.route = mist.getGroupRoute(gpName, 'task') - end - local newGroup = mist.teleportToPoint(vars) - if task and type(task) == 'number' then - local newRoute = mist.getGroupRoute(gpName, 'task') - mist.scheduleFunction(mist.goRoute, {newGroup, newRoute}, timer.getTime() + task) - end - return newGroup - end - - function mist.teleportGroup(gpName, task) - local vars = {} - vars.gpName = gpName - vars.action = 'teleport' - if task and type(task) ~= 'number' then - vars.route = mist.getGroupRoute(gpName, 'task') - end - local newGroup = mist.teleportToPoint(vars) - if task and type(task) == 'number' then - local newRoute = mist.getGroupRoute(gpName, 'task') - mist.scheduleFunction(mist.goRoute, {newGroup, newRoute}, timer.getTime() + task) - end - return newGroup - end - - function mist.spawnRandomizedGroup(groupName, vars) -- need to debug - if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then - local gpData = mist.getGroupData(groupName) - gpData.units = mist.randomizeGroupOrder(gpData.units, vars) - gpData.route = mist.getGroupRoute(groupName, 'task') - - mist.dynAdd(gpData) - end - - return true - end - - function mist.randomizeNumTable(vars) - local newTable = {} - - local excludeIndex = {} - local randomTable = {} - - if vars and vars.exclude and type(vars.exclude) == 'table' then - for index, data in pairs(vars.exclude) do - excludeIndex[data] = true - end - end - - local low, hi, size - - if vars.size then - size = vars.size - end - - if vars and vars.lowerLimit and type(vars.lowerLimit) == 'number' then - low = mist.utils.round(vars.lowerLimit) - else - low = 1 - end - - if vars and vars.upperLimit and type(vars.upperLimit) == 'number' then - hi = mist.utils.round(vars.upperLimit) - else - hi = size - end - - local choices = {} - -- add to exclude list and create list of what to randomize - for i = 1, size do - if not (i >= low and i <= hi) then - - excludeIndex[i] = true - end - if not excludeIndex[i] then - table.insert(choices, i) - else - newTable[i] = i - end - end - - for ind, num in pairs(choices) do - local found = false - local x = 0 - while found == false do - x = mist.random(size) -- get random number from list - local addNew = true - for index, _ in pairs(excludeIndex) do - if index == x then - addNew = false - break - end - end - if addNew == true then - excludeIndex[x] = true - found = true - end - excludeIndex[x] = true - - end - newTable[num] = x - end - --[[ - for i = 1, #newTable do - log:info(newTable[i]) - end - ]] - return newTable - end - - function mist.randomizeGroupOrder(passedUnits, vars) - -- figure out what to exclude, and send data to other func - local units = passedUnits - - if passedUnits.units then - units = passUnits.units - end - - local exclude = {} - local excludeNum = {} - if vars and vars.excludeType and type(vars.excludeType) == 'table' then - exclude = vars.excludeType - end - - if vars and vars.excludeNum and type(vars.excludeNum) == 'table' then - excludeNum = vars.excludeNum - end - - local low, hi - - if vars and vars.lowerLimit and type(vars.lowerLimit) == 'number' then - low = mist.utils.round(vars.lowerLimit) - else - low = 1 - end - - if vars and vars.upperLimit and type(vars.upperLimit) == 'number' then - hi = mist.utils.round(vars.upperLimit) - else - hi = #units - end - - - local excludeNum = {} - for unitIndex, unitData in pairs(units) do - if unitIndex >= low and unitIndex <= hi then -- if within range - local found = false - if #exclude > 0 then - for excludeType, index in pairs(exclude) do -- check if excluded - if mist.stringMatch(excludeType, unitData.type) then -- if excluded - excludeNum[unitIndex] = unitIndex - found = true - end - end - end - else -- unitIndex is either to low, or to high: added to exclude list - excludeNum[unitIndex] = unitId - end - end - - local newGroup = {} - local newOrder = mist.randomizeNumTable({exclude = excludeNum, size = #units}) - - for unitIndex, unitData in pairs(units) do - for i = 1, #newOrder do - if newOrder[i] == unitIndex then - newGroup[i] = mist.utils.deepCopy(units[i]) -- gets all of the unit data - newGroup[i].type = mist.utils.deepCopy(unitData.type) - newGroup[i].skill = mist.utils.deepCopy(unitData.skill) - newGroup[i].unitName = mist.utils.deepCopy(unitData.unitName) - newGroup[i].unitIndex = mist.utils.deepCopy(unitData.unitIndex) -- replaces the units data with a new type - end - end - end - return newGroup - end - - function mist.random(firstNum, secondNum) -- no support for decimals - local lowNum, highNum - if not secondNum then - highNum = firstNum - lowNum = 1 - else - lowNum = firstNum - highNum = secondNum - end - local total = 1 - if math.abs(highNum - lowNum + 1) < 50 then -- if total values is less than 50 - total = math.modf(50/math.abs(highNum - lowNum + 1)) -- make x copies required to be above 50 - end - local choices = {} - for i = 1, total do -- iterate required number of times - for x = lowNum, highNum do -- iterate between the range - choices[#choices +1] = x -- add each entry to a table - end - end - local rtnVal = math.random(#choices) -- will now do a math.random of at least 50 choices - for i = 1, 10 do - rtnVal = math.random(#choices) -- iterate a few times for giggles - end - return choices[rtnVal] - end - - function mist.stringMatch(s1, s2, bool) - local exclude = {'%-', '%(', '%)', '%_', '%[', '%]', '%.', '%#', '% ', '%{', '%}', '%$', '%%', '%?', '%+', '%^'} - if type(s1) == 'string' and type(s2) == 'string' then - for i , str in pairs(exclude) do - s1 = string.gsub(s1, str, '') - s2 = string.gsub(s2, str, '') - end - if not bool then - s1 = string.lower(s1) - s2 = string.lower(s2) - end - - if s1 == s2 then - return true - else - return false - end - else - log:error('Either the first or second variable were not a string') - return false - end - end - - mist.matchString = mist.stringMatch -- both commands work because order out type of I - - --[[ scope: + --- Check table used for group creation. + -- @tparam table groupData table to check. + -- @treturn boolean true if a group can be spawned using + -- this table, false otherwise. + function mist.groupTableCheck(groupData) + -- return false if country, category + -- or units are missing + if not groupData.country or + not groupData.category or + not groupData.units then + return false + end + -- return false if unitData misses + -- x, y or type + for unitId, unitData in pairs(groupData.units) do + if not unitData.x or + not unitData.y or + not unitData.type then + return false + end + end + -- everything we need is here return true + return true + end + + --- Returns group data table of give group. + function mist.getCurrentGroupData(gpName) + local dbData = mist.getGroupData(gpName) + + if Group.getByName(gpName) and Group.getByName(gpName):isExist() == true then + local newGroup = Group.getByName(gpName) + local newData = {} + newData.name = gpName + newData.groupId = tonumber(newGroup:getID()) + newData.category = newGroup:getCategory() + newData.groupName = gpName + newData.hidden = dbData.hidden + + if newData.category == 2 then + newData.category = 'vehicle' + elseif newData.category == 3 then + newData.category = 'ship' + end + + newData.units = {} + local newUnits = newGroup:getUnits() + for unitNum, unitData in pairs(newGroup:getUnits()) do + newData.units[unitNum] = {} + newData.units[unitNum].unitId = tonumber(unitData:getID()) + newData.units[unitNum].point = unitData.point + newData.units[unitNum].x = unitData:getPosition().p.x + newData.units[unitNum].y = unitData:getPosition().p.z + newData.units[unitNum].type = unitData:getTypeName() + newData.units[unitNum].skill = mist.getUnitSkill(unitData:getName()) + + -- get velocity needed + newData.units[unitNum].unitName = unitData:getName() + newData.units[unitNum].heading = mist.getHeading(unitData, true) -- added to DBs + newData.units[unitNum].alt = unitData:getPosition().p.y + newData.country = string.lower(country.name[unitData:getCountry()]) + newData.units[unitNum].callsign = unitData:getCallsign() + end + + return newData + elseif StaticObject.getByName(gpName) and StaticObject.getByName(gpName):isExist() == true then + local staticObj = StaticObject.getByName(gpName) + dbData.units[1].x = staticObj:getPosition().p.x + dbData.units[1].y = staticObj:getPosition().p.z + dbData.units[1].alt = staticObj:getPosition().p.y + dbData.units[1].heading = mist.getHeading(staticObj, true) + + return dbData + end + + end + + function mist.getGroupData(gpName) + local found = false + local newData = {} + if mist.DBs.groupsByName[gpName] then + newData = mist.utils.deepCopy(mist.DBs.groupsByName[gpName]) + found = true + end + + if found == false then + for groupName, groupData in pairs(mist.DBs.groupsByName) do + if mist.stringMatch(groupName, gpName) == true then + newData = mist.utils.deepCopy(groupData) + newData.groupName = groupName + found = true + break + end + end + end + + local payloads + if newData.category == 'plane' or newData.category == 'helicopter' then + payloads = mist.getGroupPayload(newData.groupName) + end + if found == true then + --newData.hidden = false -- maybe add this to DBs + + for unitNum, unitData in pairs(newData.units) do + newData.units[unitNum] = {} + + newData.units[unitNum].unitId = unitData.unitId + --newData.units[unitNum].point = unitData.point + newData.units[unitNum].x = unitData.point.x + newData.units[unitNum].y = unitData.point.y + newData.units[unitNum].alt = unitData.alt + newData.units[unitNum].alt_type = unitData.alt_type + newData.units[unitNum].speed = unitData.speed + newData.units[unitNum].type = unitData.type + newData.units[unitNum].skill = unitData.skill + newData.units[unitNum].unitName = unitData.unitName + newData.units[unitNum].heading = unitData.heading -- added to DBs + newData.units[unitNum].playerCanDrive = unitData.playerCanDrive -- added to DBs + + + if newData.category == 'plane' or newData.category == 'helicopter' then + newData.units[unitNum].payload = payloads[unitNum] + newData.units[unitNum].livery_id = unitData.livery_id + newData.units[unitNum].onboard_num = unitData.onboard_num + newData.units[unitNum].callsign = unitData.callsign + newData.units[unitNum].AddPropAircraft = unitData.AddPropAircraft + end + if newData.category == 'static' then + newData.units[unitNum].categoryStatic = unitData.categoryStatic + newData.units[unitNum].mass = unitData.mass + newData.units[unitNum].canCargo = unitData.canCargo + newData.units[unitNum].shape_name = unitData.shape_name + end + end + + return newData + else + log:error('$1 not found in MIST database', gpName) + return + end + end + + function mist.getPayload(unitIdent) + -- refactor to search by groupId and allow groupId and groupName as inputs + local unitId = unitIdent + if type(unitIdent) == 'string' and not tonumber(unitIdent) then + unitId = mist.DBs.MEunitsByName[unitIdent].unitId + end + local gpId = mist.DBs.MEunitsById[unitId].groupId + + if gpId and unitId then + 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 + for unitIndex, unitData in pairs(group_data.units) do --group index + if unitData.unitId == unitId then + return unitData.payload + end + end + end + end + end + end + end + end + end + end + end + else + log:error('Need string or number. Got: $1', type(unitIdent)) + return false + end + log:warn("Couldn't find payload for unit: $1", unitIdent) + return + end + + function mist.getGroupPayload(groupIdent) + local gpId = groupIdent + if type(groupIdent) == 'string' and not tonumber(groupIdent) then + gpId = mist.DBs.MEgroupsByName[groupIdent].groupId + end + + if gpId then + 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 + local payloads = {} + for unitIndex, unitData in pairs(group_data.units) do --group index + payloads[unitIndex] = unitData.payload + end + return payloads + end + end + end + end + end + end + end + end + end + else + log:error('Need string or number. Got: $1', type(groupIdent)) + return false + end + log:warn("Couldn't find payload for group: $1", groupIdent) + return + + end + + function mist.teleportToPoint(vars) -- main teleport function that all of teleport/respawn functions call + local point = vars.point + + local gpName + if vars.gpName then + gpName = vars.gpName + elseif vars.groupName then + gpName = vars.groupName + else + log:error('Missing field groupName or gpName in variable table') + end + + local action = vars.action + + local disperse = vars.disperse or false + local maxDisp = vars.maxDisp + if not vars.maxDisp then + maxDisp = 200 + else + maxDisp = vars.maxDisp + end + local radius = vars.radius or 0 + local innerRadius = vars.innerRadius + + local route = vars.route + local dbData = false + + local newGroupData + if gpName and not vars.groupData then + if string.lower(action) == 'teleport' or string.lower(action) == 'tele' then + newGroupData = mist.getCurrentGroupData(gpName) + elseif string.lower(action) == 'respawn' then + newGroupData = mist.getGroupData(gpName) + dbData = true + elseif string.lower(action) == 'clone' then + newGroupData = mist.getGroupData(gpName) + newGroupData.clone = 'order66' + dbData = true + else + action = 'tele' + newGroupData = mist.getCurrentGroupData(gpName) + end + else + action = 'tele' + newGroupData = vars.groupData + end + + local diff = {x = 0, y = 0} + local newCoord, origCoord + if point then + local valid = false + + local validTerrain + if string.lower(newGroupData.category) == 'ship' then + validTerrain = {'SHALLOW_WATER' , 'WATER'} + elseif string.lower(newGroupData.category) == 'vehicle' then + validTerrain = {'LAND', 'ROAD'} + else + validTerrain = {'LAND', 'ROAD', 'SHALLOW_WATER', 'WATER', 'RUNWAY'} + end + + for i = 1, 100 do + newCoord = mist.getRandPointInCircle(point, radius, innerRadius) + if mist.isTerrainValid(newCoord, validTerrain) then + origCoord = mist.utils.deepCopy(newCoord) + diff = {x = (newCoord.x - newGroupData.units[1].x), y = (newCoord.y - newGroupData.units[1].y)} + valid = true + break + end + end + if valid == false then + log:error('point supplied in variable table is not a valid coordinate.') + return false + end + end + if not newGroupData.country and mist.DBs.groupsByName[newGroupData.groupName].country then + newGroupData.country = mist.DBs.groupsByName[newGroupData.groupName].country + end + if not newGroupData.category and mist.DBs.groupsByName[newGroupData.groupName].category then + newGroupData.category = mist.DBs.groupsByName[newGroupData.groupName].category + end + + for unitNum, unitData in pairs(newGroupData.units) do + if disperse then + if maxDisp and type(maxDisp) == 'number' and unitNum ~= 1 then + newCoord = mist.getRandPointInCircle(origCoord, maxDisp) + --else + --newCoord = mist.getRandPointInCircle(zone.point, zone.radius) + end + + newGroupData.units[unitNum].x = newCoord.x + newGroupData.units[unitNum].y = newCoord.y + else + newGroupData.units[unitNum].x = unitData.x + diff.x + newGroupData.units[unitNum].y = unitData.y + diff.y + end + if point then + if (newGroupData.category == 'plane' or newGroupData.category == 'helicopter') then + if point.z and point.y > 0 and point.y > land.getHeight({newGroupData.units[unitNum].x, newGroupData.units[unitNum].y}) + 10 then + newGroupData.units[unitNum].alt = point.y + else + if newGroupData.category == 'plane' then + newGroupData.units[unitNum].alt = land.getHeight({newGroupData.units[unitNum].x, newGroupData.units[unitNum].y}) + math.random(300, 9000) + else + newGroupData.units[unitNum].alt = land.getHeight({newGroupData.units[unitNum].x, newGroupData.units[unitNum].y}) + math.random(200, 3000) + end + end + end + end + end + + if newGroupData.start_time then + newGroupData.startTime = newGroupData.start_time + end + + if newGroupData.startTime and newGroupData.startTime ~= 0 and dbData == true then + local timeDif = timer.getAbsTime() - timer.getTime0() + if timeDif > newGroupData.startTime then + newGroupData.startTime = 0 + else + newGroupData.startTime = newGroupData.startTime - timeDif + end + + end + + if route then + newGroupData.route = route + end + --mist.debug.writeData(mist.utils.serialize,{'teleportToPoint', newGroupData}, 'newGroupData.lua') + if string.lower(newGroupData.category) == 'static' then + return mist.dynAddStatic(newGroupData) + end + return mist.dynAdd(newGroupData) + + end + + function mist.respawnInZone(gpName, zone, disperse, maxDisp) + + if type(gpName) == 'table' and gpName:getName() then + gpName = gpName:getName() + elseif type(gpName) == 'table' and gpName[1]:getName() then + gpName = math.random(#gpName) + else + gpName = tostring(gpName) + 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 + local vars = {} + vars.gpName = gpName + vars.action = 'respawn' + vars.point = zone.point + vars.radius = zone.radius + vars.disperse = disperse + vars.maxDisp = maxDisp + return mist.teleportToPoint(vars) + end + + function mist.cloneInZone(gpName, zone, disperse, maxDisp) + + if type(gpName) == 'table' then + gpName = gpName:getName() + else + gpName = tostring(gpName) + 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 + local vars = {} + vars.gpName = gpName + vars.action = 'clone' + vars.point = zone.point + vars.radius = zone.radius + vars.disperse = disperse + vars.maxDisp = maxDisp + return mist.teleportToPoint(vars) + end + + function mist.teleportInZone(gpName, zone, disperse, maxDisp) -- groupName, zoneName or table of Zone Names, keepForm is a boolean + if type(gpName) == 'table' and gpName:getName() then + gpName = gpName:getName() + else + gpName = tostring(gpName) + 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 + + local vars = {} + vars.gpName = gpName + vars.action = 'tele' + vars.point = zone.point + vars.radius = zone.radius + vars.disperse = disperse + vars.maxDisp = maxDisp + return mist.teleportToPoint(vars) + end + + function mist.respawnGroup(gpName, task) + local vars = {} + vars.gpName = gpName + vars.action = 'respawn' + if task and type(task) ~= 'number' then + vars.route = mist.getGroupRoute(gpName, 'task') + end + local newGroup = mist.teleportToPoint(vars) + if task and type(task) == 'number' then + local newRoute = mist.getGroupRoute(gpName, 'task') + mist.scheduleFunction(mist.goRoute, {newGroup, newRoute}, timer.getTime() + task) + end + return newGroup + end + + function mist.cloneGroup(gpName, task) + local vars = {} + vars.gpName = gpName + vars.action = 'clone' + if task and type(task) ~= 'number' then + vars.route = mist.getGroupRoute(gpName, 'task') + end + local newGroup = mist.teleportToPoint(vars) + if task and type(task) == 'number' then + local newRoute = mist.getGroupRoute(gpName, 'task') + mist.scheduleFunction(mist.goRoute, {newGroup, newRoute}, timer.getTime() + task) + end + return newGroup + end + + function mist.teleportGroup(gpName, task) + local vars = {} + vars.gpName = gpName + vars.action = 'teleport' + if task and type(task) ~= 'number' then + vars.route = mist.getGroupRoute(gpName, 'task') + end + local newGroup = mist.teleportToPoint(vars) + if task and type(task) == 'number' then + local newRoute = mist.getGroupRoute(gpName, 'task') + mist.scheduleFunction(mist.goRoute, {newGroup, newRoute}, timer.getTime() + task) + end + return newGroup + end + + function mist.spawnRandomizedGroup(groupName, vars) -- need to debug + if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then + local gpData = mist.getGroupData(groupName) + gpData.units = mist.randomizeGroupOrder(gpData.units, vars) + gpData.route = mist.getGroupRoute(groupName, 'task') + + mist.dynAdd(gpData) + end + + return true + end + + function mist.randomizeNumTable(vars) + local newTable = {} + + local excludeIndex = {} + local randomTable = {} + + if vars and vars.exclude and type(vars.exclude) == 'table' then + for index, data in pairs(vars.exclude) do + excludeIndex[data] = true + end + end + + local low, hi, size + + if vars.size then + size = vars.size + end + + if vars and vars.lowerLimit and type(vars.lowerLimit) == 'number' then + low = mist.utils.round(vars.lowerLimit) + else + low = 1 + end + + if vars and vars.upperLimit and type(vars.upperLimit) == 'number' then + hi = mist.utils.round(vars.upperLimit) + else + hi = size + end + + local choices = {} + -- add to exclude list and create list of what to randomize + for i = 1, size do + if not (i >= low and i <= hi) then + + excludeIndex[i] = true + end + if not excludeIndex[i] then + table.insert(choices, i) + else + newTable[i] = i + end + end + + for ind, num in pairs(choices) do + local found = false + local x = 0 + while found == false do + x = mist.random(size) -- get random number from list + local addNew = true + for index, _ in pairs(excludeIndex) do + if index == x then + addNew = false + break + end + end + if addNew == true then + excludeIndex[x] = true + found = true + end + excludeIndex[x] = true + + end + newTable[num] = x + end + --[[ + for i = 1, #newTable do + log:info(newTable[i]) + end + ]] + return newTable + end + + function mist.randomizeGroupOrder(passedUnits, vars) + -- figure out what to exclude, and send data to other func + local units = passedUnits + + if passedUnits.units then + units = passUnits.units + end + + local exclude = {} + local excludeNum = {} + if vars and vars.excludeType and type(vars.excludeType) == 'table' then + exclude = vars.excludeType + end + + if vars and vars.excludeNum and type(vars.excludeNum) == 'table' then + excludeNum = vars.excludeNum + end + + local low, hi + + if vars and vars.lowerLimit and type(vars.lowerLimit) == 'number' then + low = mist.utils.round(vars.lowerLimit) + else + low = 1 + end + + if vars and vars.upperLimit and type(vars.upperLimit) == 'number' then + hi = mist.utils.round(vars.upperLimit) + else + hi = #units + end + + + local excludeNum = {} + for unitIndex, unitData in pairs(units) do + if unitIndex >= low and unitIndex <= hi then -- if within range + local found = false + if #exclude > 0 then + for excludeType, index in pairs(exclude) do -- check if excluded + if mist.stringMatch(excludeType, unitData.type) then -- if excluded + excludeNum[unitIndex] = unitIndex + found = true + end + end + end + else -- unitIndex is either to low, or to high: added to exclude list + excludeNum[unitIndex] = unitId + end + end + + local newGroup = {} + local newOrder = mist.randomizeNumTable({exclude = excludeNum, size = #units}) + + for unitIndex, unitData in pairs(units) do + for i = 1, #newOrder do + if newOrder[i] == unitIndex then + newGroup[i] = mist.utils.deepCopy(units[i]) -- gets all of the unit data + newGroup[i].type = mist.utils.deepCopy(unitData.type) + newGroup[i].skill = mist.utils.deepCopy(unitData.skill) + newGroup[i].unitName = mist.utils.deepCopy(unitData.unitName) + newGroup[i].unitIndex = mist.utils.deepCopy(unitData.unitIndex) -- replaces the units data with a new type + end + end + end + return newGroup + end + + function mist.random(firstNum, secondNum) -- no support for decimals + local lowNum, highNum + if not secondNum then + highNum = firstNum + lowNum = 1 + else + lowNum = firstNum + highNum = secondNum + end + local total = 1 + if math.abs(highNum - lowNum + 1) < 50 then -- if total values is less than 50 + total = math.modf(50/math.abs(highNum - lowNum + 1)) -- make x copies required to be above 50 + end + local choices = {} + for i = 1, total do -- iterate required number of times + for x = lowNum, highNum do -- iterate between the range + choices[#choices +1] = x -- add each entry to a table + end + end + local rtnVal = math.random(#choices) -- will now do a math.random of at least 50 choices + for i = 1, 10 do + rtnVal = math.random(#choices) -- iterate a few times for giggles + end + return choices[rtnVal] + end + + function mist.stringMatch(s1, s2, bool) + local exclude = {'%-', '%(', '%)', '%_', '%[', '%]', '%.', '%#', '% ', '%{', '%}', '%$', '%%', '%?', '%+', '%^'} + if type(s1) == 'string' and type(s2) == 'string' then + for i , str in pairs(exclude) do + s1 = string.gsub(s1, str, '') + s2 = string.gsub(s2, str, '') + end + if not bool then + s1 = string.lower(s1) + s2 = string.lower(s2) + end + + if s1 == s2 then + return true + else + return false + end + else + log:error('Either the first or second variable were not a string') + return false + end + end + + mist.matchString = mist.stringMatch -- both commands work because order out type of I + + --[[ scope: { - units = {...}, -- unit names. - coa = {...}, -- coa names - countries = {...}, -- country names - CA = {...}, -- looks just like coa. - unitTypes = { red = {}, blue = {}, all = {}, Russia = {},} + units = {...}, -- unit names. + coa = {...}, -- coa names + countries = {...}, -- country names + CA = {...}, -- looks just like coa. + unitTypes = { red = {}, blue = {}, all = {}, Russia = {},} } scope examples: -{ units = { 'Hawg11', 'Hawg12' }, CA = {'blue'} } +{ units = { 'Hawg11', 'Hawg12' }, CA = {'blue'} } { countries = {'Georgia'}, unitTypes = {blue = {'A-10C', 'A-10A'}}} @@ -3458,370 +3457,370 @@ end -- E.g. conversions between units etc. -- @section mist.utils do -- mist.util scope - mist.utils = {} + mist.utils = {} - --- Converts angle in radians to degrees. - -- @param angle angle in radians - -- @return angle in degrees - function mist.utils.toDegree (angle) - return angle*180/math.pi - end + --- Converts angle in radians to degrees. + -- @param angle angle in radians + -- @return angle in degrees + function mist.utils.toDegree (angle) + return angle*180/math.pi + end - --- Converts angle in degrees to radians. - -- @param angle angle in degrees - -- @return angle in degrees - function mist.utils.toRadian (angle) - return angle*math.pi/180 - end + --- Converts angle in degrees to radians. + -- @param angle angle in degrees + -- @return angle in degrees + function mist.utils.toRadian (angle) + return angle*math.pi/180 + end - --- Converts meters to nautical miles. - -- @param meters distance in meters - -- @return distance in nautical miles - function mist.utils.metersToNM (meters) - return meters/1852 - end + --- Converts meters to nautical miles. + -- @param meters distance in meters + -- @return distance in nautical miles + function mist.utils.metersToNM (meters) + return meters/1852 + end - --- Converts meters to feet. - -- @param meters distance in meters - -- @return distance in feet - function mist.utils.metersToFeet (meters) - return meters/0.3048 - end + --- Converts meters to feet. + -- @param meters distance in meters + -- @return distance in feet + function mist.utils.metersToFeet (meters) + return meters/0.3048 + end - --- Converts nautical miles to meters. - -- @param nm distance in nautical miles - -- @return distance in meters - function mist.utils.NMToMeters (nm) - return NM*1852 - end + --- Converts nautical miles to meters. + -- @param nm distance in nautical miles + -- @return distance in meters + function mist.utils.NMToMeters (nm) + return NM*1852 + end - --- Converts feet to meters. - -- @param feet distance in feet - -- @return distance in meters - function mist.utils.feetToMeters (feet) - return feet*0.3048 - end + --- Converts feet to meters. + -- @param feet distance in feet + -- @return distance in meters + function mist.utils.feetToMeters (feet) + return feet*0.3048 + end - --- Converts meters per second to knots. - -- @param mps speed in m/s - -- @return speed in knots - function mist.utils.mpsToKnots (mps) - return mps*3600/1852 - end + --- Converts meters per second to knots. + -- @param mps speed in m/s + -- @return speed in knots + function mist.utils.mpsToKnots (mps) + return mps*3600/1852 + end - --- Converts meters per second to kilometers per hour. - -- @param mps speed in m/s - -- @return speed in km/h - function mist.utils.mpsToKmph (mps) - return mps*3.6 - end + --- Converts meters per second to kilometers per hour. + -- @param mps speed in m/s + -- @return speed in km/h + function mist.utils.mpsToKmph (mps) + return mps*3.6 + end - --- Converts knots to meters per second. - -- @param knots speed in knots - -- @return speed in m/s - function mist.utils.knotsToMps (knots) - return knots*1852/3600 - end + --- Converts knots to meters per second. + -- @param knots speed in knots + -- @return speed in m/s + function mist.utils.knotsToMps (knots) + return knots*1852/3600 + end - --- Converts kilometers per hour to meters per second. - -- @param kmph speed in km/h - -- @return speed in m/s - function mist.utils.kmphToMps (kmph) - return kmph/3.6 - end + --- Converts kilometers per hour to meters per second. + -- @param kmph speed in km/h + -- @return speed in m/s + function mist.utils.kmphToMps (kmph) + return kmph/3.6 + end - --- Converts a Vec3 to a Vec2. - -- @tparam Vec3 vec the 3D vector - -- @return vector converted to Vec2 - function mist.utils.makeVec2(vec) - if vec.z then - return {x = vec.x, y = vec.z} - else - return {x = vec.x, y = vec.y} -- it was actually already vec2. - end - end + --- Converts a Vec3 to a Vec2. + -- @tparam Vec3 vec the 3D vector + -- @return vector converted to Vec2 + function mist.utils.makeVec2(vec) + if vec.z then + return {x = vec.x, y = vec.z} + else + return {x = vec.x, y = vec.y} -- it was actually already vec2. + end + end - --- Converts a Vec2 to a Vec3. - -- @tparam Vec2 vec the 2D vector - -- @param y optional new y axis (altitude) value. If omitted it's 0. - function mist.utils.makeVec3(vec, y) - if not vec.z then - if vec.alt and not y then - y = vec.alt - elseif not y then - y = 0 - end - return {x = vec.x, y = y, z = vec.y} - else - return {x = vec.x, y = vec.y, z = vec.z} -- it was already Vec3, actually. - end - end + --- Converts a Vec2 to a Vec3. + -- @tparam Vec2 vec the 2D vector + -- @param y optional new y axis (altitude) value. If omitted it's 0. + function mist.utils.makeVec3(vec, y) + if not vec.z then + if vec.alt and not y then + y = vec.alt + elseif not y then + y = 0 + end + return {x = vec.x, y = y, z = vec.y} + else + return {x = vec.x, y = vec.y, z = vec.z} -- it was already Vec3, actually. + end + end - --- Converts a Vec2 to a Vec3 using ground level as altitude. - -- The ground level at the specific point is used as altitude (y-axis) - -- for the new vector. Optionally a offset can be specified. - -- @tparam Vec2 vec the 2D vector - -- @param[opt] offset offset to be applied to the ground level - -- @return new 3D vector - function mist.utils.makeVec3GL(vec, offset) - local adj = offset or 0 + --- Converts a Vec2 to a Vec3 using ground level as altitude. + -- The ground level at the specific point is used as altitude (y-axis) + -- for the new vector. Optionally a offset can be specified. + -- @tparam Vec2 vec the 2D vector + -- @param[opt] offset offset to be applied to the ground level + -- @return new 3D vector + function mist.utils.makeVec3GL(vec, offset) + local adj = offset or 0 - if not vec.z then - return {x = vec.x, y = (land.getHeight(vec) + adj), z = vec.y} - else - return {x = vec.x, y = (land.getHeight({x = vec.x, y = vec.z}) + adj), z = vec.z} - end - end + if not vec.z then + return {x = vec.x, y = (land.getHeight(vec) + adj), z = vec.y} + else + return {x = vec.x, y = (land.getHeight({x = vec.x, y = vec.z}) + adj), z = vec.z} + end + end - --- Returns the center of a zone as Vec3. - -- @tparam string|table zone trigger zone name or table - -- @treturn Vec3 center of the zone - function mist.utils.zoneToVec3 (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 + --- Returns the center of a zone as Vec3. + -- @tparam string|table zone trigger zone name or table + -- @treturn Vec3 center of the zone + function mist.utils.zoneToVec3 (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 - --- Returns heading-error corrected direction. - -- True-north corrected direction from point along vector vec. - -- @tparam Vec3 vec - -- @tparam Vec2 point - -- @return heading-error corrected direction from point. - function mist.utils.getDir(vec, point) - local dir = math.atan2(vec.z, vec.x) - if point then - dir = dir + mist.getNorthCorrection(point) - end - if dir < 0 then - dir = dir + 2 * math.pi -- put dir in range of 0 to 2*pi - end - return dir - end + --- Returns heading-error corrected direction. + -- True-north corrected direction from point along vector vec. + -- @tparam Vec3 vec + -- @tparam Vec2 point + -- @return heading-error corrected direction from point. + function mist.utils.getDir(vec, point) + local dir = math.atan2(vec.z, vec.x) + if point then + dir = dir + mist.getNorthCorrection(point) + end + if dir < 0 then + dir = dir + 2 * math.pi -- put dir in range of 0 to 2*pi + end + return dir + end - --- Returns distance in meters between two points. - -- @tparam Vec2|Vec3 point1 first point - -- @tparam Vec2|Vec3 point2 second point - -- @treturn number distance between given points. - function mist.utils.get2DDist(point1, point2) - point1 = mist.utils.makeVec3(point1) - point2 = mist.utils.makeVec3(point2) - return mist.vec.mag({x = point1.x - point2.x, y = 0, z = point1.z - point2.z}) - end + --- Returns distance in meters between two points. + -- @tparam Vec2|Vec3 point1 first point + -- @tparam Vec2|Vec3 point2 second point + -- @treturn number distance between given points. + function mist.utils.get2DDist(point1, point2) + point1 = mist.utils.makeVec3(point1) + point2 = mist.utils.makeVec3(point2) + return mist.vec.mag({x = point1.x - point2.x, y = 0, z = point1.z - point2.z}) + end - --- Returns distance in meters between two points in 3D space. - -- @tparam Vec3 point1 first point - -- @tparam Vec3 point2 second point - -- @treturn number distancen between given points in 3D space. - function mist.utils.get3DDist(point1, point2) - return mist.vec.mag({x = point1.x - point2.x, y = point1.y - point2.y, z = point1.z - point2.z}) - end + --- Returns distance in meters between two points in 3D space. + -- @tparam Vec3 point1 first point + -- @tparam Vec3 point2 second point + -- @treturn number distancen between given points in 3D space. + function mist.utils.get3DDist(point1, point2) + return mist.vec.mag({x = point1.x - point2.x, y = point1.y - point2.y, z = point1.z - point2.z}) + end - --- Creates a waypoint from a vector. - -- @tparam Vec2|Vec3 vec position of the new waypoint - -- @treturn Waypoint a new waypoint to be used inside paths. - function mist.utils.vecToWP(vec) - local newWP = {} - newWP.x = vec.x - newWP.y = vec.y - if vec.z then - newWP.alt = vec.y - newWP.y = vec.z - else - newWP.alt = land.getHeight({x = vec.x, y = vec.y}) - end - return newWP - end + --- Creates a waypoint from a vector. + -- @tparam Vec2|Vec3 vec position of the new waypoint + -- @treturn Waypoint a new waypoint to be used inside paths. + function mist.utils.vecToWP(vec) + local newWP = {} + newWP.x = vec.x + newWP.y = vec.y + if vec.z then + newWP.alt = vec.y + newWP.y = vec.z + else + newWP.alt = land.getHeight({x = vec.x, y = vec.y}) + end + return newWP + end - --- Creates a waypoint from a unit. - -- This function also considers the units speed. - -- The alt_type of this waypoint is set to "BARO". - -- @tparam Unit pUnit Unit whose position and speed will be used. - -- @treturn Waypoint new waypoint. - function mist.utils.unitToWP(pUnit) - local unit = mist.utils.deepCopy(pUnit) - if type(unit) == 'string' then - if Unit.getByName(unit) then - unit = Unit.getByName(unit) - end - end - if unit:isExist() == true then - local new = mist.utils.vecToWP(unit:getPosition().p) - new.speed = mist.vec.mag(unit:getVelocity()) - new.alt_type = "BARO" + --- Creates a waypoint from a unit. + -- This function also considers the units speed. + -- The alt_type of this waypoint is set to "BARO". + -- @tparam Unit pUnit Unit whose position and speed will be used. + -- @treturn Waypoint new waypoint. + function mist.utils.unitToWP(pUnit) + local unit = mist.utils.deepCopy(pUnit) + if type(unit) == 'string' then + if Unit.getByName(unit) then + unit = Unit.getByName(unit) + end + end + if unit:isExist() == true then + local new = mist.utils.vecToWP(unit:getPosition().p) + new.speed = mist.vec.mag(unit:getVelocity()) + new.alt_type = "BARO" - return new - end - return false - end + return new + end + return false + end - --- Creates a deep copy of a object. - -- Usually this object is a table. - -- See also: from http://lua-users.org/wiki/CopyTable - -- @param object object to copy - -- @return copy of object - function mist.utils.deepCopy(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 - return _copy(object) - end + --- Creates a deep copy of a object. + -- Usually this object is a table. + -- See also: from http://lua-users.org/wiki/CopyTable + -- @param object object to copy + -- @return copy of object + function mist.utils.deepCopy(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 + return _copy(object) + end - --- Simple rounding function. - -- From http://lua-users.org/wiki/SimpleRound - -- use negative idp for rounding ahead of decimal place, positive for rounding after decimal place - -- @tparam number num number to round - -- @param idp - function mist.utils.round(num, idp) - local mult = 10^(idp or 0) - return math.floor(num * mult + 0.5) / mult - end + --- Simple rounding function. + -- From http://lua-users.org/wiki/SimpleRound + -- use negative idp for rounding ahead of decimal place, positive for rounding after decimal place + -- @tparam number num number to round + -- @param idp + function mist.utils.round(num, idp) + local mult = 10^(idp or 0) + return math.floor(num * mult + 0.5) / mult + end - --- Rounds all numbers inside a table. - -- @tparam table tbl table in which to round numbers - -- @param idp - function mist.utils.roundTbl(tbl, idp) - for id, val in pairs(tbl) do - if type(val) == 'number' then - tbl[id] = mist.utils.round(val, idp) - end - end - return tbl - end + --- Rounds all numbers inside a table. + -- @tparam table tbl table in which to round numbers + -- @param idp + function mist.utils.roundTbl(tbl, idp) + for id, val in pairs(tbl) do + if type(val) == 'number' then + tbl[id] = mist.utils.round(val, idp) + end + end + return tbl + end - --- Executes the given string. - -- borrowed from Slmod - -- @tparam string s string containing LUA code. - -- @treturn boolean true if successfully executed, false otherwise - function mist.utils.dostring(s) - local f, err = loadstring(s) - if f then - return true, f() - else - return false, err - end - end + --- Executes the given string. + -- borrowed from Slmod + -- @tparam string s string containing LUA code. + -- @treturn boolean true if successfully executed, false otherwise + function mist.utils.dostring(s) + local f, err = loadstring(s) + if f then + return true, f() + else + return false, err + end + end - --- Checks a table's types. - -- This function checks a tables types against a specifically forged type table. - -- @param fname - -- @tparam table type_tbl - -- @tparam table var_tbl - -- @usage -- specifically forged type table - -- type_tbl = { - -- {'table', 'number'}, - -- 'string', - -- 'number', - -- 'number', - -- {'string','nil'}, - -- {'number', 'nil'} - -- } - -- -- my_tbl index 1 must be a table or a number; - -- -- index 2, a string; index 3, a number; - -- -- index 4, a number; index 5, either a string or nil; - -- -- and index 6, either a number or nil. - -- mist.utils.typeCheck(type_tbl, my_tb) - -- @return true if table passes the check, false otherwise. - function mist.utils.typeCheck(fname, type_tbl, var_tbl) - -- log:info('type check') - for type_key, type_val in pairs(type_tbl) do - -- log:info('type_key: $1 type_val: $2', type_key, type_val) + --- Checks a table's types. + -- This function checks a tables types against a specifically forged type table. + -- @param fname + -- @tparam table type_tbl + -- @tparam table var_tbl + -- @usage -- specifically forged type table + -- type_tbl = { + -- {'table', 'number'}, + -- 'string', + -- 'number', + -- 'number', + -- {'string','nil'}, + -- {'number', 'nil'} + -- } + -- -- my_tbl index 1 must be a table or a number; + -- -- index 2, a string; index 3, a number; + -- -- index 4, a number; index 5, either a string or nil; + -- -- and index 6, either a number or nil. + -- mist.utils.typeCheck(type_tbl, my_tb) + -- @return true if table passes the check, false otherwise. + function mist.utils.typeCheck(fname, type_tbl, var_tbl) + -- log:info('type check') + for type_key, type_val in pairs(type_tbl) do + -- log:info('type_key: $1 type_val: $2', type_key, type_val) - --type_key can be a table of accepted keys- so try to find one that is not nil - local type_key_str = '' - local act_key = type_key -- actual key within var_tbl - necessary to use for multiple possible key variables. Initialize to type_key - if type(type_key) == 'table' then + --type_key can be a table of accepted keys- so try to find one that is not nil + local type_key_str = '' + local act_key = type_key -- actual key within var_tbl - necessary to use for multiple possible key variables. Initialize to type_key + if type(type_key) == 'table' then - for i = 1, #type_key do - if i ~= 1 then - type_key_str = type_key_str .. '/' - end - type_key_str = type_key_str .. tostring(type_key[i]) - if var_tbl[type_key[i]] ~= nil then - act_key = type_key[i] -- found a non-nil entry, make act_key now this val. - end - end - else - type_key_str = tostring(type_key) - end + for i = 1, #type_key do + if i ~= 1 then + type_key_str = type_key_str .. '/' + end + type_key_str = type_key_str .. tostring(type_key[i]) + if var_tbl[type_key[i]] ~= nil then + act_key = type_key[i] -- found a non-nil entry, make act_key now this val. + end + end + else + type_key_str = tostring(type_key) + end - local err_msg = 'Error in function ' .. fname .. ', parameter "' .. type_key_str .. '", expected: ' - local passed_check = false + local err_msg = 'Error in function ' .. fname .. ', parameter "' .. type_key_str .. '", expected: ' + local passed_check = false - if type(type_tbl[type_key]) == 'table' then - -- log:info('err_msg, before: $1', err_msg) - for j = 1, #type_tbl[type_key] do + if type(type_tbl[type_key]) == 'table' then + -- log:info('err_msg, before: $1', err_msg) + for j = 1, #type_tbl[type_key] do - if j == 1 then - err_msg = err_msg .. type_tbl[type_key][j] - else - err_msg = err_msg .. ' or ' .. type_tbl[type_key][j] - end + if j == 1 then + err_msg = err_msg .. type_tbl[type_key][j] + else + err_msg = err_msg .. ' or ' .. type_tbl[type_key][j] + end - if type(var_tbl[act_key]) == type_tbl[type_key][j] then - passed_check = true - end - end - -- log:info('err_msg, after: $1', err_msg) - else - -- log:info('err_msg, before: $1', err_msg) - err_msg = err_msg .. type_tbl[type_key] - -- log:info('err_msg, after: $1', err_msg) - if type(var_tbl[act_key]) == type_tbl[type_key] then - passed_check = true - end + if type(var_tbl[act_key]) == type_tbl[type_key][j] then + passed_check = true + end + end + -- log:info('err_msg, after: $1', err_msg) + else + -- log:info('err_msg, before: $1', err_msg) + err_msg = err_msg .. type_tbl[type_key] + -- log:info('err_msg, after: $1', err_msg) + if type(var_tbl[act_key]) == type_tbl[type_key] then + passed_check = true + end - end + end - if not passed_check then - err_msg = err_msg .. ', got ' .. type(var_tbl[act_key]) - return false, err_msg - end - end - return true - end + if not passed_check then + err_msg = err_msg .. ', got ' .. type(var_tbl[act_key]) + return false, err_msg + end + end + return true + end - --- Serializes the give variable to a string. - -- borrowed from slmod - -- @param var variable to serialize - -- @treturn string variable serialized to string - function mist.utils.basicSerialize(var) - if var == nil then - return "\"\"" - else - if ((type(var) == 'number') or - (type(var) == 'boolean') or - (type(var) == 'function') or - (type(var) == 'table') or - (type(var) == 'userdata') ) then - return tostring(var) - elseif type(var) == 'string' then - var = string.format('%q', var) - return var - end - end + --- Serializes the give variable to a string. + -- borrowed from slmod + -- @param var variable to serialize + -- @treturn string variable serialized to string + function mist.utils.basicSerialize(var) + if var == nil then + return "\"\"" + else + if ((type(var) == 'number') or + (type(var) == 'boolean') or + (type(var) == 'function') or + (type(var) == 'table') or + (type(var) == 'userdata') ) then + return tostring(var) + elseif type(var) == 'string' then + var = string.format('%q', var) + return var + end + end end --- Serialize value @@ -3830,56 +3829,56 @@ end -- @param value value to serialize -- @param level function mist.utils.serialize(name, value, level) - --Based on ED's serialize_simple2 - local function basicSerialize(o) - if type(o) == "number" then - return tostring(o) - elseif type(o) == "boolean" then - return tostring(o) - else -- assume it is a string - return mist.utils.basicSerialize(o) - end - end + --Based on ED's serialize_simple2 + local function basicSerialize(o) + if type(o) == "number" then + return tostring(o) + elseif type(o) == "boolean" then + return tostring(o) + else -- assume it is a string + return mist.utils.basicSerialize(o) + end + end - local function serializeToTbl(name, value, level) - local var_str_tbl = {} - if level == nil then level = "" end - if level ~= "" then level = level.." " end + local function serializeToTbl(name, value, level) + local var_str_tbl = {} + if level == nil then level = "" end + if level ~= "" then level = level.." " end - table.insert(var_str_tbl, level .. name .. " = ") + table.insert(var_str_tbl, level .. name .. " = ") - if type(value) == "number" or type(value) == "string" or type(value) == "boolean" then - table.insert(var_str_tbl, basicSerialize(value) .. ",\n") - elseif type(value) == "table" then - table.insert(var_str_tbl, "\n"..level.."{\n") + if type(value) == "number" or type(value) == "string" or type(value) == "boolean" then + table.insert(var_str_tbl, basicSerialize(value) .. ",\n") + elseif type(value) == "table" then + table.insert(var_str_tbl, "\n"..level.."{\n") - for k,v in pairs(value) do -- serialize its fields - local key - if type(k) == "number" then - key = string.format("[%s]", k) - else - key = string.format("[%q]", k) - end + for k,v in pairs(value) do -- serialize its fields + local key + if type(k) == "number" then + key = string.format("[%s]", k) + else + key = string.format("[%q]", k) + end - table.insert(var_str_tbl, mist.utils.serialize(key, v, level.." ")) + table.insert(var_str_tbl, mist.utils.serialize(key, v, level.." ")) - end - if level == "" then - table.insert(var_str_tbl, level.."} -- end of "..name.."\n") + end + if level == "" then + table.insert(var_str_tbl, level.."} -- end of "..name.."\n") - else - table.insert(var_str_tbl, level.."}, -- end of "..name.."\n") + else + table.insert(var_str_tbl, level.."}, -- end of "..name.."\n") - end - else - log:error('Cannot serialize a $1', type(value)) - end - return var_str_tbl - end + end + else + log:error('Cannot serialize a $1', type(value)) + end + return var_str_tbl + end - local t_str = serializeToTbl(name, value, level) + local t_str = serializeToTbl(name, value, level) - return table.concat(t_str) + return table.concat(t_str) end --- Serialize value supporting cycles. @@ -3888,40 +3887,40 @@ end -- @param value value to serialize -- @param saved function mist.utils.serializeWithCycles(name, value, saved) - --mostly straight out of Programming in Lua - local function basicSerialize(o) - if type(o) == "number" then - return tostring(o) - elseif type(o) == "boolean" then - return tostring(o) - else -- assume it is a string - return mist.utils.basicSerialize(o) - end - end + --mostly straight out of Programming in Lua + local function basicSerialize(o) + if type(o) == "number" then + return tostring(o) + elseif type(o) == "boolean" then + return tostring(o) + else -- assume it is a string + return mist.utils.basicSerialize(o) + end + end - local t_str = {} - saved = saved or {} -- initial value - if ((type(value) == 'string') or (type(value) == 'number') or (type(value) == 'table') or (type(value) == 'boolean')) then - table.insert(t_str, name .. " = ") - if type(value) == "number" or type(value) == "string" or type(value) == "boolean" then - table.insert(t_str, basicSerialize(value) .. "\n") - else + local t_str = {} + saved = saved or {} -- initial value + if ((type(value) == 'string') or (type(value) == 'number') or (type(value) == 'table') or (type(value) == 'boolean')) then + table.insert(t_str, name .. " = ") + if type(value) == "number" or type(value) == "string" or type(value) == "boolean" then + table.insert(t_str, basicSerialize(value) .. "\n") + else - if saved[value] then -- value already saved? - table.insert(t_str, saved[value] .. "\n") - else - saved[value] = name -- save name for next time - table.insert(t_str, "{}\n") - for k,v in pairs(value) do -- save its fields - local fieldname = string.format("%s[%s]", name, basicSerialize(k)) - table.insert(t_str, mist.utils.serializeWithCycles(fieldname, v, saved)) - end - end - end - return table.concat(t_str) - else - return "" - end + if saved[value] then -- value already saved? + table.insert(t_str, saved[value] .. "\n") + else + saved[value] = name -- save name for next time + table.insert(t_str, "{}\n") + for k,v in pairs(value) do -- save its fields + local fieldname = string.format("%s[%s]", name, basicSerialize(k)) + table.insert(t_str, mist.utils.serializeWithCycles(fieldname, v, saved)) + end + end + end + return table.concat(t_str) + else + return "" + end end --- Serialize a table to a single line string. @@ -3930,42 +3929,42 @@ end -- @tparam table tbl table to serialize. -- @treturn string string containing serialized table function mist.utils.oneLineSerialize(tbl) - if type(tbl) == 'table' then --function only works for tables! + if type(tbl) == 'table' then --function only works for tables! - local tbl_str = {} + local tbl_str = {} - tbl_str[#tbl_str + 1] = '{ ' + tbl_str[#tbl_str + 1] = '{ ' - for ind,val in pairs(tbl) do -- serialize its fields - if type(ind) == "number" then - tbl_str[#tbl_str + 1] = '[' - tbl_str[#tbl_str + 1] = tostring(ind) - tbl_str[#tbl_str + 1] = '] = ' - else --must be a string - tbl_str[#tbl_str + 1] = '[' - tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(ind) - tbl_str[#tbl_str + 1] = '] = ' - end + for ind,val in pairs(tbl) do -- serialize its fields + if type(ind) == "number" then + tbl_str[#tbl_str + 1] = '[' + tbl_str[#tbl_str + 1] = tostring(ind) + tbl_str[#tbl_str + 1] = '] = ' + else --must be a string + tbl_str[#tbl_str + 1] = '[' + tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(ind) + tbl_str[#tbl_str + 1] = '] = ' + end - if ((type(val) == 'number') or (type(val) == 'boolean')) then - tbl_str[#tbl_str + 1] = tostring(val) - tbl_str[#tbl_str + 1] = ', ' - elseif type(val) == 'string' then - tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(val) - tbl_str[#tbl_str + 1] = ', ' - elseif type(val) == 'nil' then -- won't ever happen, right? - tbl_str[#tbl_str + 1] = 'nil, ' - elseif type(val) == 'table' then - tbl_str[#tbl_str + 1] = mist.utils.oneLineSerialize(val) - tbl_str[#tbl_str + 1] = ', ' --I think this is right, I just added it - else - log:war('Unable to serialize value type $1 at index $2', mist.utils.basicSerialize(type(val)), tostring(ind)) - end + if ((type(val) == 'number') or (type(val) == 'boolean')) then + tbl_str[#tbl_str + 1] = tostring(val) + tbl_str[#tbl_str + 1] = ', ' + elseif type(val) == 'string' then + tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(val) + tbl_str[#tbl_str + 1] = ', ' + elseif type(val) == 'nil' then -- won't ever happen, right? + tbl_str[#tbl_str + 1] = 'nil, ' + elseif type(val) == 'table' then + tbl_str[#tbl_str + 1] = mist.utils.oneLineSerialize(val) + tbl_str[#tbl_str + 1] = ', ' --I think this is right, I just added it + else + log:war('Unable to serialize value type $1 at index $2', mist.utils.basicSerialize(type(val)), tostring(ind)) + end - end - tbl_str[#tbl_str + 1] = '}' - return table.concat(tbl_str) - end + end + tbl_str[#tbl_str + 1] = '}' + return table.concat(tbl_str) + end end --- Returns table in a easy readable string representation. @@ -3977,203 +3976,205 @@ end -- @param tableshow_tbls -- @return human readable string representation of given table function mist.utils.tableShow(tbl, loc, indent, tableshow_tbls) --based on serialize_slmod, this is a _G serialization - tableshow_tbls = tableshow_tbls or {} --create table of tables - loc = loc or "" - indent = indent or "" - if type(tbl) == 'table' then --function only works for tables! - tableshow_tbls[tbl] = loc + tableshow_tbls = tableshow_tbls or {} --create table of tables + loc = loc or "" + indent = indent or "" + if type(tbl) == 'table' then --function only works for tables! + tableshow_tbls[tbl] = loc - local tbl_str = {} + local tbl_str = {} - tbl_str[#tbl_str + 1] = indent .. '{\n' + tbl_str[#tbl_str + 1] = indent .. '{\n' - for ind,val in pairs(tbl) do -- serialize its fields - if type(ind) == "number" then - tbl_str[#tbl_str + 1] = indent - tbl_str[#tbl_str + 1] = loc .. '[' - tbl_str[#tbl_str + 1] = tostring(ind) - tbl_str[#tbl_str + 1] = '] = ' - else - tbl_str[#tbl_str + 1] = indent - tbl_str[#tbl_str + 1] = loc .. '[' - tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(ind) - tbl_str[#tbl_str + 1] = '] = ' - end + for ind,val in pairs(tbl) do -- serialize its fields + if type(ind) == "number" then + tbl_str[#tbl_str + 1] = indent + tbl_str[#tbl_str + 1] = loc .. '[' + tbl_str[#tbl_str + 1] = tostring(ind) + tbl_str[#tbl_str + 1] = '] = ' + else + tbl_str[#tbl_str + 1] = indent + tbl_str[#tbl_str + 1] = loc .. '[' + tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(ind) + tbl_str[#tbl_str + 1] = '] = ' + end - if ((type(val) == 'number') or (type(val) == 'boolean')) then - tbl_str[#tbl_str + 1] = tostring(val) - tbl_str[#tbl_str + 1] = ',\n' - elseif type(val) == 'string' then - tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(val) - tbl_str[#tbl_str + 1] = ',\n' - elseif type(val) == 'nil' then -- won't ever happen, right? - tbl_str[#tbl_str + 1] = 'nil,\n' - elseif type(val) == 'table' then - if tableshow_tbls[val] then - tbl_str[#tbl_str + 1] = tostring(val) .. ' already defined: ' .. tableshow_tbls[val] .. ',\n' - else - tableshow_tbls[val] = loc .. '[' .. mist.utils.basicSerialize(ind) .. ']' - tbl_str[#tbl_str + 1] = tostring(val) .. ' ' - tbl_str[#tbl_str + 1] = mist.utils.tableShow(val, loc .. '[' .. mist.utils.basicSerialize(ind).. ']', indent .. ' ', tableshow_tbls) - tbl_str[#tbl_str + 1] = ',\n' - end - elseif type(val) == 'function' then - if debug and debug.getinfo then - local fcnname = tostring(val) - local info = debug.getinfo(val, "S") - if info.what == "C" then - tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', C function') .. ',\n' - else - if (string.sub(info.source, 1, 2) == [[./]]) then - tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', defined in (' .. info.linedefined .. '-' .. info.lastlinedefined .. ')' .. info.source) ..',\n' - else - tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', defined in (' .. info.linedefined .. '-' .. info.lastlinedefined .. ')') ..',\n' - end - end + if ((type(val) == 'number') or (type(val) == 'boolean')) then + tbl_str[#tbl_str + 1] = tostring(val) + tbl_str[#tbl_str + 1] = ',\n' + elseif type(val) == 'string' then + tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(val) + tbl_str[#tbl_str + 1] = ',\n' + elseif type(val) == 'nil' then -- won't ever happen, right? + tbl_str[#tbl_str + 1] = 'nil,\n' + elseif type(val) == 'table' then + if tableshow_tbls[val] then + tbl_str[#tbl_str + 1] = tostring(val) .. ' already defined: ' .. tableshow_tbls[val] .. ',\n' + else + tableshow_tbls[val] = loc .. '[' .. mist.utils.basicSerialize(ind) .. ']' + tbl_str[#tbl_str + 1] = tostring(val) .. ' ' + tbl_str[#tbl_str + 1] = mist.utils.tableShow(val, loc .. '[' .. mist.utils.basicSerialize(ind).. ']', indent .. ' ', tableshow_tbls) + tbl_str[#tbl_str + 1] = ',\n' + end + elseif type(val) == 'function' then + if debug and debug.getinfo then + local fcnname = tostring(val) + local info = debug.getinfo(val, "S") + if info.what == "C" then + tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', C function') .. ',\n' + else + if (string.sub(info.source, 1, 2) == [[./]]) then + tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', defined in (' .. info.linedefined .. '-' .. info.lastlinedefined .. ')' .. info.source) ..',\n' + else + tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', defined in (' .. info.linedefined .. '-' .. info.lastlinedefined .. ')') ..',\n' + end + end - else - tbl_str[#tbl_str + 1] = 'a function,\n' - end - else - tbl_str[#tbl_str + 1] = 'unable to serialize value type ' .. mist.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind) - end - end + else + tbl_str[#tbl_str + 1] = 'a function,\n' + end + else + tbl_str[#tbl_str + 1] = 'unable to serialize value type ' .. mist.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind) + end + end - tbl_str[#tbl_str + 1] = indent .. '}' - return table.concat(tbl_str) - end + tbl_str[#tbl_str + 1] = indent .. '}' + return table.concat(tbl_str) + end end end --- Debug functions -- @section mist.debug do -- mist.debug scope - mist.debug = {} + mist.debug = {} - --- Dumps the global table _G. - -- This dumps the global table _G to a file in - -- the DCS\Logs directory. - -- This function requires you to disable script sanitization - -- in $DCS_ROOT\Scripts\MissionScripting.lua to access lfs and io - -- libraries. - -- @param fname - function mist.debug.dump_G(fname) - if lfs and io then - local fdir = lfs.writedir() .. [[Logs\]] .. fname - local f = io.open(fdir, 'w') - f:write(mist.utils.tableShow(_G)) - f:close() - log:info('Wrote debug data to $1', fdir) - --trigger.action.outText(errmsg, 10) - else - log:alert('insufficient libraries to run mist.debug.dump_G, you must disable the sanitization of the io and lfs libraries in ./Scripts/MissionScripting.lua') - --trigger.action.outText(errmsg, 10) - end - end + --- Dumps the global table _G. + -- This dumps the global table _G to a file in + -- the DCS\Logs directory. + -- This function requires you to disable script sanitization + -- in $DCS_ROOT\Scripts\MissionScripting.lua to access lfs and io + -- libraries. + -- @param fname + function mist.debug.dump_G(fname) + if lfs and io then + local fdir = lfs.writedir() .. [[Logs\]] .. fname + local f = io.open(fdir, 'w') + f:write(mist.utils.tableShow(_G)) + f:close() + log:info('Wrote debug data to $1', fdir) + --trigger.action.outText(errmsg, 10) + else + log:alert('insufficient libraries to run mist.debug.dump_G, you must disable the sanitization of the io and lfs libraries in ./Scripts/MissionScripting.lua') + --trigger.action.outText(errmsg, 10) + end + end - --- Write debug data to file. - -- This function requires you to disable script sanitization - -- in $DCS_ROOT\Scripts\MissionScripting.lua to access lfs and io - -- libraries. - -- @param fcn - -- @param fcnVars - -- @param fname - function mist.debug.writeData(fcn, fcnVars, fname) - if lfs and io then - local fdir = lfs.writedir() .. [[Logs\]] .. fname - local f = io.open(fdir, 'w') - f:write(fcn(unpack(fcnVars, 1, table.maxn(fcnVars)))) - f:close() - log:info('Wrote debug data to $1', fdir) - --trigger.action.outText(errmsg, 10) - else - log:alert('insufficient libraries to run mist.debug.dump_G, you must disable the sanitization of the io and lfs libraries in ./Scripts/MissionScripting.lua') - --trigger.action.outText(errmsg, 10) - end - end + --- Write debug data to file. + -- This function requires you to disable script sanitization + -- in $DCS_ROOT\Scripts\MissionScripting.lua to access lfs and io + -- libraries. + -- @param fcn + -- @param fcnVars + -- @param fname + function mist.debug.writeData(fcn, fcnVars, fname) + if lfs and io then + local fdir = lfs.writedir() .. [[Logs\]] .. fname + local f = io.open(fdir, 'w') + f:write(fcn(unpack(fcnVars, 1, table.maxn(fcnVars)))) + f:close() + log:info('Wrote debug data to $1', fdir) + local errmsg = 'mist.debug.writeData wrote data to ' .. fdir + trigger.action.outText(errmsg, 10) + else + local errmsg = 'Error: insufficient libraries to run mist.debug.writeData, you must disable the sanitization of the io and lfs libraries in ./Scripts/MissionScripting.lua' + log:alert(errmsg) + trigger.action.outText(errmsg, 10) + end + end - --- Write mist databases to file. - -- This function requires you to disable script sanitization - -- in $DCS_ROOT\Scripts\MissionScripting.lua to access lfs and io - -- libraries. - function mist.debug.dumpDBs() - for DBname, DB in pairs(mist.DBs) do - if type(DB) == 'table' and type(DBname) == 'string' then - mist.debug.writeData(mist.utils.serialize, {DBname, DB}, 'mist_DBs_' .. DBname .. '.lua') - end - end - end + --- Write mist databases to file. + -- This function requires you to disable script sanitization + -- in $DCS_ROOT\Scripts\MissionScripting.lua to access lfs and io + -- libraries. + function mist.debug.dumpDBs() + for DBname, DB in pairs(mist.DBs) do + if type(DB) == 'table' and type(DBname) == 'string' then + mist.debug.writeData(mist.utils.serialize, {DBname, DB}, 'mist_DBs_' .. DBname .. '.lua') + end + end + end end --- 3D Vector functions -- @section mist.vec do -- mist.vec scope - mist.vec = {} + mist.vec = {} - --- Vector addition. - -- @tparam Vec3 vec1 first vector - -- @tparam Vec3 vec2 second vector - -- @treturn Vec3 new vector, sum of vec1 and vec2. - function mist.vec.add(vec1, vec2) - return {x = vec1.x + vec2.x, y = vec1.y + vec2.y, z = vec1.z + vec2.z} - end + --- Vector addition. + -- @tparam Vec3 vec1 first vector + -- @tparam Vec3 vec2 second vector + -- @treturn Vec3 new vector, sum of vec1 and vec2. + function mist.vec.add(vec1, vec2) + return {x = vec1.x + vec2.x, y = vec1.y + vec2.y, z = vec1.z + vec2.z} + end - --- Vector substraction. - -- @tparam Vec3 vec1 first vector - -- @tparam Vec3 vec2 second vector - -- @treturn Vec3 new vector, vec2 substracted from vec1. - function mist.vec.sub(vec1, vec2) - return {x = vec1.x - vec2.x, y = vec1.y - vec2.y, z = vec1.z - vec2.z} - end + --- Vector substraction. + -- @tparam Vec3 vec1 first vector + -- @tparam Vec3 vec2 second vector + -- @treturn Vec3 new vector, vec2 substracted from vec1. + function mist.vec.sub(vec1, vec2) + return {x = vec1.x - vec2.x, y = vec1.y - vec2.y, z = vec1.z - vec2.z} + end - --- Vector scalar multiplication. - -- @tparam Vec3 vec vector to multiply - -- @tparam number mult scalar multiplicator - -- @treturn Vec3 new vector multiplied with the given scalar - function mist.vec.scalarMult(vec, mult) - return {x = vec.x*mult, y = vec.y*mult, z = vec.z*mult} - end + --- Vector scalar multiplication. + -- @tparam Vec3 vec vector to multiply + -- @tparam number mult scalar multiplicator + -- @treturn Vec3 new vector multiplied with the given scalar + function mist.vec.scalarMult(vec, mult) + return {x = vec.x*mult, y = vec.y*mult, z = vec.z*mult} + end - mist.vec.scalar_mult = mist.vec.scalarMult + mist.vec.scalar_mult = mist.vec.scalarMult - --- Vector dot product. - -- @tparam Vec3 vec1 first vector - -- @tparam Vec3 vec2 second vector - -- @treturn number dot product of given vectors - function mist.vec.dp (vec1, vec2) - return vec1.x*vec2.x + vec1.y*vec2.y + vec1.z*vec2.z - end + --- Vector dot product. + -- @tparam Vec3 vec1 first vector + -- @tparam Vec3 vec2 second vector + -- @treturn number dot product of given vectors + function mist.vec.dp (vec1, vec2) + return vec1.x*vec2.x + vec1.y*vec2.y + vec1.z*vec2.z + end - --- Vector cross product. - -- @tparam Vec3 vec1 first vector - -- @tparam Vec3 vec2 second vector - -- @treturn Vec3 new vector, cross product of vec1 and vec2. - function mist.vec.cp(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 + --- Vector cross product. + -- @tparam Vec3 vec1 first vector + -- @tparam Vec3 vec2 second vector + -- @treturn Vec3 new vector, cross product of vec1 and vec2. + function mist.vec.cp(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 - --- Vector magnitude - -- @tparam Vec3 vec vector - -- @treturn number magnitude of vector vec - function mist.vec.mag(vec) - return (vec.x^2 + vec.y^2 + vec.z^2)^0.5 - end + --- Vector magnitude + -- @tparam Vec3 vec vector + -- @treturn number magnitude of vector vec + function mist.vec.mag(vec) + return (vec.x^2 + vec.y^2 + vec.z^2)^0.5 + end - --- Unit vector - -- @tparam Vec3 vec - -- @treturn Vec3 unit vector of vec - function mist.vec.getUnitVec(vec) - local mag = mist.vec.mag(vec) - return { x = vec.x/mag, y = vec.y/mag, z = vec.z/mag } - end + --- Unit vector + -- @tparam Vec3 vec + -- @treturn Vec3 unit vector of vec + function mist.vec.getUnitVec(vec) + local mag = mist.vec.mag(vec) + return { x = vec.x/mag, y = vec.y/mag, z = vec.z/mag } + end - --- Rotate vector. - -- @tparam Vec2 vec2 to rotoate - -- @tparam number theta - -- @return Vec2 rotated vector. - function mist.vec.rotateVec2(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 + --- Rotate vector. + -- @tparam Vec2 vec2 to rotoate + -- @tparam number theta + -- @return Vec2 rotated vector. + function mist.vec.rotateVec2(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 end --- Flag functions. @@ -4184,25 +4185,25 @@ end -- programming, but with a good knowledge of the DCS mission editor. -- @section mist.flagFunc do -- mist.flagFunc scope - mist.flagFunc = {} + mist.flagFunc = {} - --- Sets a flag if map objects are destroyed inside a zone. - -- Once this function is run, it will start a continuously evaluated process - -- that will set a flag true if map objects (such as bridges, buildings in - -- town, etc.) die (or have died) in a mission editor zone (or set of zones). - -- This will only happen once; once the flag is set true, the process ends. - -- @usage - -- -- Example vars table - -- vars = { - -- zones = { "zone1", "zone2" }, -- can also be a single string - -- flag = 3, -- number of the flag - -- stopflag = 4, -- optional number of the stop flag - -- req_num = 10, -- optional minimum amount of map objects needed to die - -- } - -- mist.flagFuncs.mapobjs_dead_zones(vars) - -- @tparam table vars table containing parameters. - function mist.flagFunc.mapobjs_dead_zones(vars) - --[[vars needs to be: + --- Sets a flag if map objects are destroyed inside a zone. + -- Once this function is run, it will start a continuously evaluated process + -- that will set a flag true if map objects (such as bridges, buildings in + -- town, etc.) die (or have died) in a mission editor zone (or set of zones). + -- This will only happen once; once the flag is set true, the process ends. + -- @usage + -- -- Example vars table + -- vars = { + -- zones = { "zone1", "zone2" }, -- can also be a single string + -- flag = 3, -- number of the flag + -- stopflag = 4, -- optional number of the stop flag + -- req_num = 10, -- optional minimum amount of map objects needed to die + -- } + -- mist.flagFuncs.mapobjs_dead_zones(vars) + -- @tparam table vars table containing parameters. + function mist.flagFunc.mapobjs_dead_zones(vars) + --[[vars needs to be: zones = table or string, flag = number, stopflag = number or nil, @@ -4212,62 +4213,62 @@ AND used by function, initial_number ]] - -- type_tbl - local type_tbl = { - [{'zones', 'zone'}] = {'table', 'string'}, - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - [{'req_num', 'reqnum'}] = {'number', 'nil'}, - } + -- type_tbl + local type_tbl = { + [{'zones', 'zone'}] = {'table', 'string'}, + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + [{'req_num', 'reqnum'}] = {'number', 'nil'}, + } - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.mapobjs_dead_zones', type_tbl, vars) - assert(err, errmsg) - local zones = vars.zones or vars.zone - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local req_num = vars.req_num or vars.reqnum or 1 - local initial_number = vars.initial_number + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.mapobjs_dead_zones', type_tbl, vars) + assert(err, errmsg) + local zones = vars.zones or vars.zone + local flag = vars.flag + local stopflag = vars.stopflag or -1 + local req_num = vars.req_num or vars.reqnum or 1 + local initial_number = vars.initial_number - if type(zones) == 'string' then - zones = {zones} - end + if type(zones) == 'string' then + zones = {zones} + end - if not initial_number then - initial_number = #mist.getDeadMapObjsInZones(zones) - end + if not initial_number then + initial_number = #mist.getDeadMapObjsInZones(zones) + end - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - if (#mist.getDeadMapObjsInZones(zones) - initial_number) >= req_num and trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - return - else - mist.scheduleFunction(mist.flagFunc.mapobjs_dead_zones, {{zones = zones, flag = flag, stopflag = stopflag, req_num = req_num, initial_number = initial_number}}, timer.getTime() + 1) - end - end - end + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + if (#mist.getDeadMapObjsInZones(zones) - initial_number) >= req_num and trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + return + else + mist.scheduleFunction(mist.flagFunc.mapobjs_dead_zones, {{zones = zones, flag = flag, stopflag = stopflag, req_num = req_num, initial_number = initial_number}}, timer.getTime() + 1) + end + end + end - --- Sets a flag if map objects are destroyed inside a polygon. - -- Once this function is run, it will start a continuously evaluated process - -- that will set a flag true if map objects (such as bridges, buildings in - -- town, etc.) die (or have died) in a polygon. - -- This will only happen once; once the flag is set true, the process ends. - -- @usage - -- -- Example vars table - -- vars = { - -- zone = { - -- [1] = mist.DBs.unitsByName['NE corner'].point, - -- [2] = mist.DBs.unitsByName['SE corner'].point, - -- [3] = mist.DBs.unitsByName['SW corner'].point, - -- [4] = mist.DBs.unitsByName['NW corner'].point - -- } - -- flag = 3, -- number of the flag - -- stopflag = 4, -- optional number of the stop flag - -- req_num = 10, -- optional minimum amount of map objects needed to die - -- } - -- mist.flagFuncs.mapobjs_dead_zones(vars) - -- @tparam table vars table containing parameters. - function mist.flagFunc.mapobjs_dead_polygon(vars) - --[[vars needs to be: + --- Sets a flag if map objects are destroyed inside a polygon. + -- Once this function is run, it will start a continuously evaluated process + -- that will set a flag true if map objects (such as bridges, buildings in + -- town, etc.) die (or have died) in a polygon. + -- This will only happen once; once the flag is set true, the process ends. + -- @usage + -- -- Example vars table + -- vars = { + -- zone = { + -- [1] = mist.DBs.unitsByName['NE corner'].point, + -- [2] = mist.DBs.unitsByName['SE corner'].point, + -- [3] = mist.DBs.unitsByName['SW corner'].point, + -- [4] = mist.DBs.unitsByName['NW corner'].point + -- } + -- flag = 3, -- number of the flag + -- stopflag = 4, -- optional number of the stop flag + -- req_num = 10, -- optional minimum amount of map objects needed to die + -- } + -- mist.flagFuncs.mapobjs_dead_zones(vars) + -- @tparam table vars table containing parameters. + function mist.flagFunc.mapobjs_dead_polygon(vars) + --[[vars needs to be: zone = table, flag = number, stopflag = number or nil, @@ -4277,260 +4278,260 @@ AND used by function, initial_number ]] - -- type_tbl - local type_tbl = { - [{'zone', 'polyzone'}] = 'table', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - [{'req_num', 'reqnum'}] = {'number', 'nil'}, - } + -- type_tbl + local type_tbl = { + [{'zone', 'polyzone'}] = 'table', + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + [{'req_num', 'reqnum'}] = {'number', 'nil'}, + } - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.mapobjs_dead_polygon', type_tbl, vars) - assert(err, errmsg) - local zone = vars.zone or vars.polyzone - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local req_num = vars.req_num or vars.reqnum or 1 - local initial_number = vars.initial_number + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.mapobjs_dead_polygon', type_tbl, vars) + assert(err, errmsg) + local zone = vars.zone or vars.polyzone + local flag = vars.flag + local stopflag = vars.stopflag or -1 + local req_num = vars.req_num or vars.reqnum or 1 + local initial_number = vars.initial_number - if not initial_number then - initial_number = #mist.getDeadMapObjsInPolygonZone(zone) - end + if not initial_number then + initial_number = #mist.getDeadMapObjsInPolygonZone(zone) + end - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - if (#mist.getDeadMapObjsInPolygonZone(zone) - initial_number) >= req_num and trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - return - else - mist.scheduleFunction(mist.flagFunc.mapobjs_dead_polygon, {{zone = zone, flag = flag, stopflag = stopflag, req_num = req_num, initial_number = initial_number}}, timer.getTime() + 1) - end - end - end + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + if (#mist.getDeadMapObjsInPolygonZone(zone) - initial_number) >= req_num and trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + return + else + mist.scheduleFunction(mist.flagFunc.mapobjs_dead_polygon, {{zone = zone, flag = flag, stopflag = stopflag, req_num = req_num, initial_number = initial_number}}, timer.getTime() + 1) + end + end + end - --- Sets a flag if unit(s) is/are inside a polygon. - -- @tparam table vars @{unitsInPolygonVars} - -- @usage -- set flag 11 to true as soon as any blue vehicles - -- -- are inside the polygon shape created off of the waypoints - -- -- of the group forest1 - -- mist.flagFunc.units_in_polygon { - -- units = {'[blue][vehicle]'}, - -- zone = mist.getGroupPoints('forest1'), - -- flag = 11 - -- } - function mist.flagFunc.units_in_polygon(vars) - --[[vars needs to be: + --- Sets a flag if unit(s) is/are inside a polygon. + -- @tparam table vars @{unitsInPolygonVars} + -- @usage -- set flag 11 to true as soon as any blue vehicles + -- -- are inside the polygon shape created off of the waypoints + -- -- of the group forest1 + -- mist.flagFunc.units_in_polygon { + -- units = {'[blue][vehicle]'}, + -- zone = mist.getGroupPoints('forest1'), + -- flag = 11 + -- } + function mist.flagFunc.units_in_polygon(vars) + --[[vars needs to be: units = table, zone = table, flag = number, stopflag = number or nil, maxalt = number or nil, -interval = number or nil, +interval = number or nil, req_num = number or nil toggle = boolean or nil unitTableDef = table or nil ]] - -- type_tbl - local type_tbl = { - [{'units', 'unit'}] = 'table', - [{'zone', 'polyzone'}] = 'table', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - [{'maxalt', 'alt'}] = {'number', 'nil'}, - interval = {'number', 'nil'}, - [{'req_num', 'reqnum'}] = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - unitTableDef = {'table', 'nil'}, - } + -- type_tbl + local type_tbl = { + [{'units', 'unit'}] = 'table', + [{'zone', 'polyzone'}] = 'table', + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + [{'maxalt', 'alt'}] = {'number', 'nil'}, + interval = {'number', 'nil'}, + [{'req_num', 'reqnum'}] = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + unitTableDef = {'table', 'nil'}, + } - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_in_polygon', type_tbl, vars) - assert(err, errmsg) - local units = vars.units or vars.unit - local zone = vars.zone or vars.polyzone - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local interval = vars.interval or 1 - local maxalt = vars.maxalt or vars.alt - local req_num = vars.req_num or vars.reqnum or 1 - local toggle = vars.toggle or nil - local unitTableDef = vars.unitTableDef + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_in_polygon', type_tbl, vars) + assert(err, errmsg) + local units = vars.units or vars.unit + local zone = vars.zone or vars.polyzone + local flag = vars.flag + local stopflag = vars.stopflag or -1 + local interval = vars.interval or 1 + local maxalt = vars.maxalt or vars.alt + local req_num = vars.req_num or vars.reqnum or 1 + local toggle = vars.toggle or nil + local unitTableDef = vars.unitTableDef - if not units.processed then - unitTableDef = mist.utils.deepCopy(units) - end + if not units.processed then + unitTableDef = mist.utils.deepCopy(units) + end - if (units.processed and units.processed < mist.getLastDBUpdateTime()) or not units.processed then -- run unit table short cuts - units = mist.makeUnitTable(unitTableDef) - end + if (units.processed and units.processed < mist.getLastDBUpdateTime()) or not units.processed then -- run unit table short cuts + units = mist.makeUnitTable(unitTableDef) + end - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == 0) then - local num_in_zone = 0 - for i = 1, #units do - local unit = Unit.getByName(units[i]) - if unit then - local pos = unit:getPosition().p - if mist.pointInPolygon(pos, zone, maxalt) then - num_in_zone = num_in_zone + 1 - if num_in_zone >= req_num and trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - break - end - end - end - end - if toggle and (num_in_zone < req_num) and trigger.misc.getUserFlag(flag) > 0 then - trigger.action.setUserFlag(flag, false) - end - -- do another check in case stopflag was set true by this function - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == 0) then - mist.scheduleFunction(mist.flagFunc.units_in_polygon, {{units = units, zone = zone, flag = flag, stopflag = stopflag, interval = interval, req_num = req_num, maxalt = maxalt, toggle = toggle, unitTableDef = unitTableDef}}, timer.getTime() + interval) - end - end + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == 0) then + local num_in_zone = 0 + for i = 1, #units do + local unit = Unit.getByName(units[i]) + if unit then + local pos = unit:getPosition().p + if mist.pointInPolygon(pos, zone, maxalt) then + num_in_zone = num_in_zone + 1 + if num_in_zone >= req_num and trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + break + end + end + end + end + if toggle and (num_in_zone < req_num) and trigger.misc.getUserFlag(flag) > 0 then + trigger.action.setUserFlag(flag, false) + end + -- do another check in case stopflag was set true by this function + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == 0) then + mist.scheduleFunction(mist.flagFunc.units_in_polygon, {{units = units, zone = zone, flag = flag, stopflag = stopflag, interval = interval, req_num = req_num, maxalt = maxalt, toggle = toggle, unitTableDef = unitTableDef}}, timer.getTime() + interval) + end + end - end + end - --- Sets a flag if unit(s) is/are inside a trigger zone. - -- @todo document - function mist.flagFunc.units_in_zones(vars) - --[[vars needs to be: - units = table, - zones = table, - flag = number, - stopflag = number or nil, - zone_type = string or nil, - req_num = number or nil, - interval = number or nil - toggle = boolean or nil - ]] - -- type_tbl - local type_tbl = { - units = 'table', - zones = 'table', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - [{'zone_type', 'zonetype'}] = {'string', 'nil'}, - [{'req_num', 'reqnum'}] = {'number', 'nil'}, - interval = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - unitTableDef = {'table', 'nil'}, - } + --- Sets a flag if unit(s) is/are inside a trigger zone. + -- @todo document + function mist.flagFunc.units_in_zones(vars) + --[[vars needs to be: + units = table, + zones = table, + flag = number, + stopflag = number or nil, + zone_type = string or nil, + req_num = number or nil, + interval = number or nil + toggle = boolean or nil + ]] + -- type_tbl + local type_tbl = { + units = 'table', + zones = 'table', + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + [{'zone_type', 'zonetype'}] = {'string', 'nil'}, + [{'req_num', 'reqnum'}] = {'number', 'nil'}, + interval = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + unitTableDef = {'table', 'nil'}, + } - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_in_zones', type_tbl, vars) - assert(err, errmsg) - local units = vars.units - local zones = vars.zones - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local zone_type = vars.zone_type or vars.zonetype or 'cylinder' - local req_num = vars.req_num or vars.reqnum or 1 - local interval = vars.interval or 1 - local toggle = vars.toggle or nil - local unitTableDef = vars.unitTableDef + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_in_zones', type_tbl, vars) + assert(err, errmsg) + local units = vars.units + local zones = vars.zones + local flag = vars.flag + local stopflag = vars.stopflag or -1 + local zone_type = vars.zone_type or vars.zonetype or 'cylinder' + local req_num = vars.req_num or vars.reqnum or 1 + local interval = vars.interval or 1 + local toggle = vars.toggle or nil + local unitTableDef = vars.unitTableDef - if not units.processed then - unitTableDef = mist.utils.deepCopy(units) - end + if not units.processed then + unitTableDef = mist.utils.deepCopy(units) + end - if (units.processed and units.processed < mist.getLastDBUpdateTime()) or not units.processed then -- run unit table short cuts - units = mist.makeUnitTable(unitTableDef) - end + if (units.processed and units.processed < mist.getLastDBUpdateTime()) or not units.processed then -- run unit table short cuts + units = mist.makeUnitTable(unitTableDef) + end - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - local in_zone_units = mist.getUnitsInZones(units, zones, zone_type) + local in_zone_units = mist.getUnitsInZones(units, zones, zone_type) - if #in_zone_units >= req_num and trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - elseif #in_zone_units < req_num and toggle then - trigger.action.setUserFlag(flag, false) - end - -- do another check in case stopflag was set true by this function - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - mist.scheduleFunction(mist.flagFunc.units_in_zones, {{units = units, zones = zones, flag = flag, stopflag = stopflag, zone_type = zone_type, req_num = req_num, interval = interval, toggle = toggle, unitTableDef = unitTableDef}}, timer.getTime() + interval) - end - end + if #in_zone_units >= req_num and trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + elseif #in_zone_units < req_num and toggle then + trigger.action.setUserFlag(flag, false) + end + -- do another check in case stopflag was set true by this function + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + mist.scheduleFunction(mist.flagFunc.units_in_zones, {{units = units, zones = zones, flag = flag, stopflag = stopflag, zone_type = zone_type, req_num = req_num, interval = interval, toggle = toggle, unitTableDef = unitTableDef}}, timer.getTime() + interval) + end + end - end + end - --- Sets a flag if unit(s) is/are inside a moving zone. - -- @todo document - function mist.flagFunc.units_in_moving_zones(vars) - --[[vars needs to be: - units = table, - zone_units = table, - radius = number, - flag = number, - stopflag = number or nil, - zone_type = string or nil, - req_num = number or nil, - interval = number or nil - toggle = boolean or nil - ]] - -- type_tbl - local type_tbl = { - units = 'table', - [{'zone_units', 'zoneunits'}] = 'table', - radius = 'number', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - [{'zone_type', 'zonetype'}] = {'string', 'nil'}, - [{'req_num', 'reqnum'}] = {'number', 'nil'}, - interval = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - unitTableDef = {'table', 'nil'}, - zUnitTableDef = {'table', 'nil'}, - } + --- Sets a flag if unit(s) is/are inside a moving zone. + -- @todo document + function mist.flagFunc.units_in_moving_zones(vars) + --[[vars needs to be: + units = table, + zone_units = table, + radius = number, + flag = number, + stopflag = number or nil, + zone_type = string or nil, + req_num = number or nil, + interval = number or nil + toggle = boolean or nil + ]] + -- type_tbl + local type_tbl = { + units = 'table', + [{'zone_units', 'zoneunits'}] = 'table', + radius = 'number', + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + [{'zone_type', 'zonetype'}] = {'string', 'nil'}, + [{'req_num', 'reqnum'}] = {'number', 'nil'}, + interval = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + unitTableDef = {'table', 'nil'}, + zUnitTableDef = {'table', 'nil'}, + } - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_in_moving_zones', type_tbl, vars) - assert(err, errmsg) - local units = vars.units - local zone_units = vars.zone_units or vars.zoneunits - local radius = vars.radius - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local zone_type = vars.zone_type or vars.zonetype or 'cylinder' - local req_num = vars.req_num or vars.reqnum or 1 - local interval = vars.interval or 1 - local toggle = vars.toggle or nil - local unitTableDef = vars.unitTableDef - local zUnitTableDef = vars.zUnitTableDef + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_in_moving_zones', type_tbl, vars) + assert(err, errmsg) + local units = vars.units + local zone_units = vars.zone_units or vars.zoneunits + local radius = vars.radius + local flag = vars.flag + local stopflag = vars.stopflag or -1 + local zone_type = vars.zone_type or vars.zonetype or 'cylinder' + local req_num = vars.req_num or vars.reqnum or 1 + local interval = vars.interval or 1 + local toggle = vars.toggle or nil + local unitTableDef = vars.unitTableDef + local zUnitTableDef = vars.zUnitTableDef - if not units.processed then - unitTableDef = mist.utils.deepCopy(units) - end + if not units.processed then + unitTableDef = mist.utils.deepCopy(units) + end - if not zone_units.processed then - zUnitTableDef = mist.utils.deepCopy(zone_units) - end + if not zone_units.processed then + zUnitTableDef = mist.utils.deepCopy(zone_units) + end - if (units.processed and units.processed < mist.getLastDBUpdateTime()) or not units.processed then -- run unit table short cuts - units = mist.makeUnitTable(unitTableDef) - end + if (units.processed and units.processed < mist.getLastDBUpdateTime()) or not units.processed then -- run unit table short cuts + units = mist.makeUnitTable(unitTableDef) + end - if (zone_units.processed and zone_units.processed < mist.getLastDBUpdateTime()) or not zone_units.processed then -- run unit table short cuts - zone_units = mist.makeUnitTable(zUnitTableDef) - end + if (zone_units.processed and zone_units.processed < mist.getLastDBUpdateTime()) or not zone_units.processed then -- run unit table short cuts + zone_units = mist.makeUnitTable(zUnitTableDef) + end - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - local in_zone_units = mist.getUnitsInMovingZones(units, zone_units, radius, zone_type) + local in_zone_units = mist.getUnitsInMovingZones(units, zone_units, radius, zone_type) - if #in_zone_units >= req_num and trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - elseif #in_zone_units < req_num and toggle then - trigger.action.setUserFlag(flag, false) - end - -- do another check in case stopflag was set true by this function - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - mist.scheduleFunction(mist.flagFunc.units_in_moving_zones, {{units = units, zone_units = zone_units, radius = radius, flag = flag, stopflag = stopflag, zone_type = zone_type, req_num = req_num, interval = interval, toggle = toggle, unitTableDef = unitTableDef, zUnitTableDef = zUnitTableDef}}, timer.getTime() + interval) - end - end + if #in_zone_units >= req_num and trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + elseif #in_zone_units < req_num and toggle then + trigger.action.setUserFlag(flag, false) + end + -- do another check in case stopflag was set true by this function + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + mist.scheduleFunction(mist.flagFunc.units_in_moving_zones, {{units = units, zone_units = zone_units, radius = radius, flag = flag, stopflag = stopflag, zone_type = zone_type, req_num = req_num, interval = interval, toggle = toggle, unitTableDef = unitTableDef, zUnitTableDef = zUnitTableDef}}, timer.getTime() + interval) + end + end - end + end - --- Sets a flag if units have line of sight to each other. - -- @todo document - function mist.flagFunc.units_LOS(vars) - --[[vars needs to be: + --- Sets a flag if units have line of sight to each other. + -- @todo document + function mist.flagFunc.units_LOS(vars) + --[[vars needs to be: unitset1 = table, altoffset1 = number, unitset2 = table, @@ -4538,78 +4539,78 @@ altoffset2 = number, flag = number, stopflag = number or nil, radius = number or nil, -interval = number or nil, +interval = number or nil, req_num = number or nil toggle = boolean or nil ]] - -- type_tbl - local type_tbl = { - [{'unitset1', 'units1'}] = 'table', - [{'altoffset1', 'alt1'}] = 'number', - [{'unitset2', 'units2'}] = 'table', - [{'altoffset2', 'alt2'}] = 'number', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - [{'req_num', 'reqnum'}] = {'number', 'nil'}, - interval = {'number', 'nil'}, - radius = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - unitTableDef1 = {'table', 'nil'}, - unitTableDef2 = {'table', 'nil'}, - } + -- type_tbl + local type_tbl = { + [{'unitset1', 'units1'}] = 'table', + [{'altoffset1', 'alt1'}] = 'number', + [{'unitset2', 'units2'}] = 'table', + [{'altoffset2', 'alt2'}] = 'number', + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + [{'req_num', 'reqnum'}] = {'number', 'nil'}, + interval = {'number', 'nil'}, + radius = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + unitTableDef1 = {'table', 'nil'}, + unitTableDef2 = {'table', 'nil'}, + } - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_LOS', type_tbl, vars) - assert(err, errmsg) - local unitset1 = vars.unitset1 or vars.units1 - local altoffset1 = vars.altoffset1 or vars.alt1 - local unitset2 = vars.unitset2 or vars.units2 - local altoffset2 = vars.altoffset2 or vars.alt2 - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local interval = vars.interval or 1 - local radius = vars.radius or math.huge - local req_num = vars.req_num or vars.reqnum or 1 - local toggle = vars.toggle or nil - local unitTableDef1 = vars.unitTableDef1 - local unitTableDef2 = vars.unitTableDef2 + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_LOS', type_tbl, vars) + assert(err, errmsg) + local unitset1 = vars.unitset1 or vars.units1 + local altoffset1 = vars.altoffset1 or vars.alt1 + local unitset2 = vars.unitset2 or vars.units2 + local altoffset2 = vars.altoffset2 or vars.alt2 + local flag = vars.flag + local stopflag = vars.stopflag or -1 + local interval = vars.interval or 1 + local radius = vars.radius or math.huge + local req_num = vars.req_num or vars.reqnum or 1 + local toggle = vars.toggle or nil + local unitTableDef1 = vars.unitTableDef1 + local unitTableDef2 = vars.unitTableDef2 - if not unitset1.processed then - unitTableDef1 = mist.utils.deepCopy(unitset1) - end + if not unitset1.processed then + unitTableDef1 = mist.utils.deepCopy(unitset1) + end - if not unitset2.processed then - unitTableDef2 = mist.utils.deepCopy(unitset2) - end + if not unitset2.processed then + unitTableDef2 = mist.utils.deepCopy(unitset2) + end - if (unitset1.processed and unitset1.processed < mist.getLastDBUpdateTime()) or not unitset1.processed then -- run unit table short cuts - unitset1 = mist.makeUnitTable(unitTableDef1) - end + if (unitset1.processed and unitset1.processed < mist.getLastDBUpdateTime()) or not unitset1.processed then -- run unit table short cuts + unitset1 = mist.makeUnitTable(unitTableDef1) + end - if (unitset2.processed and unitset2.processed < mist.getLastDBUpdateTime()) or not unitset2.processed then -- run unit table short cuts - unitset2 = mist.makeUnitTable(unitTableDef2) - end + if (unitset2.processed and unitset2.processed < mist.getLastDBUpdateTime()) or not unitset2.processed then -- run unit table short cuts + unitset2 = mist.makeUnitTable(unitTableDef2) + end - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - local unitLOSdata = mist.getUnitsLOS(unitset1, altoffset1, unitset2, altoffset2, radius) + local unitLOSdata = mist.getUnitsLOS(unitset1, altoffset1, unitset2, altoffset2, radius) - if #unitLOSdata >= req_num and trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - elseif #unitLOSdata < req_num and toggle then - trigger.action.setUserFlag(flag, false) - end - -- do another check in case stopflag was set true by this function - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - mist.scheduleFunction(mist.flagFunc.units_LOS, {{unitset1 = unitset1, altoffset1 = altoffset1, unitset2 = unitset2, altoffset2 = altoffset2, flag = flag, stopflag = stopflag, radius = radius, req_num = req_num, interval = interval, toggle = toggle, unitTableDef1 = unitTableDef1, unitTableDef2 = unitTableDef2}}, timer.getTime() + interval) - end - end - end + if #unitLOSdata >= req_num and trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + elseif #unitLOSdata < req_num and toggle then + trigger.action.setUserFlag(flag, false) + end + -- do another check in case stopflag was set true by this function + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + mist.scheduleFunction(mist.flagFunc.units_LOS, {{unitset1 = unitset1, altoffset1 = altoffset1, unitset2 = unitset2, altoffset2 = altoffset2, flag = flag, stopflag = stopflag, radius = radius, req_num = req_num, interval = interval, toggle = toggle, unitTableDef1 = unitTableDef1, unitTableDef2 = unitTableDef2}}, timer.getTime() + interval) + end + end + end - --- Sets a flag if group is alive. - -- @todo document - function mist.flagFunc.group_alive(vars) - --[[vars + --- Sets a flag if group is alive. + -- @todo document + function mist.flagFunc.group_alive(vars) + --[[vars groupName flag toggle @@ -4617,171 +4618,171 @@ interval stopFlag ]] - local type_tbl = { - [{'group', 'groupname', 'gp', 'groupName'}] = 'string', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - interval = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - } + local type_tbl = { + [{'group', 'groupname', 'gp', 'groupName'}] = 'string', + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + interval = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + } - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_alive', type_tbl, vars) - assert(err, errmsg) + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_alive', type_tbl, vars) + assert(err, errmsg) - local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local interval = vars.interval or 1 - local toggle = vars.toggle or nil + local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname + local flag = vars.flag + local stopflag = vars.stopflag or -1 + local interval = vars.interval or 1 + local toggle = vars.toggle or nil - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true and #Group.getByName(groupName):getUnits() > 0 then - if trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - end - else - if toggle then - trigger.action.setUserFlag(flag, false) - end - end - end + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true and #Group.getByName(groupName):getUnits() > 0 then + if trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + end + else + if toggle then + trigger.action.setUserFlag(flag, false) + end + end + end - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - mist.scheduleFunction(mist.flagFunc.group_alive, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle}}, timer.getTime() + interval) - end + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + mist.scheduleFunction(mist.flagFunc.group_alive, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle}}, timer.getTime() + interval) + end - end + end - --- Sets a flag if group is dead. - -- @todo document - function mist.flagFunc.group_dead(vars) - local type_tbl = { - [{'group', 'groupname', 'gp', 'groupName'}] = 'string', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - interval = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - } + --- Sets a flag if group is dead. + -- @todo document + function mist.flagFunc.group_dead(vars) + local type_tbl = { + [{'group', 'groupname', 'gp', 'groupName'}] = 'string', + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + interval = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + } - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_dead', type_tbl, vars) - assert(err, errmsg) + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_dead', type_tbl, vars) + assert(err, errmsg) - local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local interval = vars.interval or 1 - local toggle = vars.toggle or nil + local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname + local flag = vars.flag + local stopflag = vars.stopflag or -1 + local interval = vars.interval or 1 + local toggle = vars.toggle or nil - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - if (Group.getByName(groupName) and Group.getByName(groupName):isExist() == false) or (Group.getByName(groupName) and #Group.getByName(groupName):getUnits() < 1) or not Group.getByName(groupName) then - if trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - end - else - if toggle then - trigger.action.setUserFlag(flag, false) - end - end - end + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + if (Group.getByName(groupName) and Group.getByName(groupName):isExist() == false) or (Group.getByName(groupName) and #Group.getByName(groupName):getUnits() < 1) or not Group.getByName(groupName) then + if trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + end + else + if toggle then + trigger.action.setUserFlag(flag, false) + end + end + end - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - mist.scheduleFunction(mist.flagFunc.group_dead, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle}}, timer.getTime() + interval) - end - end + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + mist.scheduleFunction(mist.flagFunc.group_dead, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle}}, timer.getTime() + interval) + end + end - --- Sets a flag if less than given percent of group is alive. - -- @todo document - function mist.flagFunc.group_alive_less_than(vars) - local type_tbl = { - [{'group', 'groupname', 'gp', 'groupName'}] = 'string', - percent = 'number', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - interval = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - } + --- Sets a flag if less than given percent of group is alive. + -- @todo document + function mist.flagFunc.group_alive_less_than(vars) + local type_tbl = { + [{'group', 'groupname', 'gp', 'groupName'}] = 'string', + percent = 'number', + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + interval = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + } - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_alive_less_than', type_tbl, vars) - assert(err, errmsg) + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_alive_less_than', type_tbl, vars) + assert(err, errmsg) - local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname - local flag = vars.flag - local percent = vars.percent - local stopflag = vars.stopflag or -1 - local interval = vars.interval or 1 - local toggle = vars.toggle or nil + local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname + local flag = vars.flag + local percent = vars.percent + local stopflag = vars.stopflag or -1 + local interval = vars.interval or 1 + local toggle = vars.toggle or nil - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then - if Group.getByName(groupName):getSize()/Group.getByName(groupName):getInitialSize() < percent/100 then - if trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - end - else - if toggle then - trigger.action.setUserFlag(flag, false) - end - end - else - if trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - end - end - end + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then + if Group.getByName(groupName):getSize()/Group.getByName(groupName):getInitialSize() < percent/100 then + if trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + end + else + if toggle then + trigger.action.setUserFlag(flag, false) + end + end + else + if trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + end + end + end - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - mist.scheduleFunction(mist.flagFunc.group_alive_less_than, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle, percent = percent}}, timer.getTime() + interval) - end - end + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + mist.scheduleFunction(mist.flagFunc.group_alive_less_than, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle, percent = percent}}, timer.getTime() + interval) + end + end - --- Sets a flag if more than given percent of group is alive. - -- @todo document - function mist.flagFunc.group_alive_more_than(vars) - local type_tbl = { - [{'group', 'groupname', 'gp', 'groupName'}] = 'string', - percent = 'number', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - interval = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - } + --- Sets a flag if more than given percent of group is alive. + -- @todo document + function mist.flagFunc.group_alive_more_than(vars) + local type_tbl = { + [{'group', 'groupname', 'gp', 'groupName'}] = 'string', + percent = 'number', + flag = {'number', 'string'}, + stopflag = {'number', 'string', 'nil'}, + interval = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + } - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_alive_more_than', type_tbl, vars) - assert(err, errmsg) + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_alive_more_than', type_tbl, vars) + assert(err, errmsg) - local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname - local flag = vars.flag - local percent = vars.percent - local stopflag = vars.stopflag or -1 - local interval = vars.interval or 1 - local toggle = vars.toggle or nil + local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname + local flag = vars.flag + local percent = vars.percent + local stopflag = vars.stopflag or -1 + local interval = vars.interval or 1 + local toggle = vars.toggle or nil - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then - if Group.getByName(groupName):getSize()/Group.getByName(groupName):getInitialSize() > percent/100 then - if trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - end - else - if toggle and trigger.misc.getUserFlag(flag) == 1 then - trigger.action.setUserFlag(flag, false) - end - end - else --- just in case - if toggle and trigger.misc.getUserFlag(flag) == 1 then - trigger.action.setUserFlag(flag, false) - end - end - end + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then + if Group.getByName(groupName):getSize()/Group.getByName(groupName):getInitialSize() > percent/100 then + if trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + end + else + if toggle and trigger.misc.getUserFlag(flag) == 1 then + trigger.action.setUserFlag(flag, false) + end + end + else --- just in case + if toggle and trigger.misc.getUserFlag(flag) == 1 then + trigger.action.setUserFlag(flag, false) + end + end + end - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - mist.scheduleFunction(mist.flagFunc.group_alive_more_than, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle, percent = percent}}, timer.getTime() + interval) - end - end + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + mist.scheduleFunction(mist.flagFunc.group_alive_more_than, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle, percent = percent}}, timer.getTime() + interval) + end + end end @@ -4789,498 +4790,498 @@ end -- Messaging system -- @section mist.msg do -- mist.msg scope - local messageList = {} - -- this defines the max refresh rate of the message box it honestly only needs to - -- go faster than this for precision timing stuff (which could be its own function) - local messageDisplayRate = 0.1 - local messageID = 0 - local displayActive = false - local displayFuncId = 0 + local messageList = {} + -- this defines the max refresh rate of the message box it honestly only needs to + -- go faster than this for precision timing stuff (which could be its own function) + local messageDisplayRate = 0.1 + local messageID = 0 + local displayActive = false + local displayFuncId = 0 - local caSlots = false - local caMSGtoGroup = false + local caSlots = false + local caMSGtoGroup = false - if env.mission.groundControl then -- just to be sure? - for index, value in pairs(env.mission.groundControl) do - if type(value) == 'table' then - for roleName, roleVal in pairs(value) do - for rIndex, rVal in pairs(roleVal) do - if rIndex == 'red' or rIndex == 'blue' then - if env.mission.groundControl[index][roleName][rIndex] > 0 then - caSlots = true - break - end - end - end - end - elseif type(value) == 'boolean' and value == true then - caSlots = true - break - end - end - end + if env.mission.groundControl then -- just to be sure? + for index, value in pairs(env.mission.groundControl) do + if type(value) == 'table' then + for roleName, roleVal in pairs(value) do + for rIndex, rVal in pairs(roleVal) do + if rIndex == 'red' or rIndex == 'blue' then + if env.mission.groundControl[index][roleName][rIndex] > 0 then + caSlots = true + break + end + end + end + end + elseif type(value) == 'boolean' and value == true then + caSlots = true + break + end + end + end - local function mistdisplayV5() - --[[thoughts to improve upon - event handler based activeClients table. - display messages only when there is an update - possibly co-routine it. + local function mistdisplayV5() + --[[thoughts to improve upon + event handler based activeClients table. + display messages only when there is an update + possibly co-routine it. ]] - end + end - local function mistdisplayV4() - local activeClients = {} + local function mistdisplayV4() + local activeClients = {} - for clientId, clientData in pairs(mist.DBs.humansById) do - if Unit.getByName(clientData.unitName) and Unit.getByName(clientData.unitName):isExist() == true then - activeClients[clientData.groupId] = clientData.groupName - end - end + for clientId, clientData in pairs(mist.DBs.humansById) do + if Unit.getByName(clientData.unitName) and Unit.getByName(clientData.unitName):isExist() == true then + activeClients[clientData.groupId] = clientData.groupName + end + end - --[[if caSlots == true and caMSGtoGroup == true then + --[[if caSlots == true and caMSGtoGroup == true then - end]] + end]] - if #messageList > 0 then - if displayActive == false then - displayActive = true - end - --mist.debug.writeData(mist.utils.serialize,{'msg', messageList}, 'messageList.lua') - local msgTableText = {} - local msgTableSound = {} + if #messageList > 0 then + if displayActive == false then + displayActive = true + end + --mist.debug.writeData(mist.utils.serialize,{'msg', messageList}, 'messageList.lua') + local msgTableText = {} + local msgTableSound = {} - for messageId, messageData in pairs(messageList) do - if messageData.displayedFor > messageData.displayTime then - messageData:remove() -- now using the remove/destroy function. - else - if messageData.displayedFor then - messageData.displayedFor = messageData.displayedFor + messageDisplayRate - end - local nextSound = 1000 - local soundIndex = 0 + for messageId, messageData in pairs(messageList) do + if messageData.displayedFor > messageData.displayTime then + messageData:remove() -- now using the remove/destroy function. + else + if messageData.displayedFor then + messageData.displayedFor = messageData.displayedFor + messageDisplayRate + end + local nextSound = 1000 + local soundIndex = 0 - if messageData.multSound and #messageData.multSound > 0 then - for index, sData in pairs(messageData.multSound) do - if sData.time <= messageData.displayedFor and sData.played == false and sData.time < nextSound then -- find index of the next sound to be played - nextSound = sData.time - soundIndex = index - end - end - if soundIndex ~= 0 then - messageData.multSound[soundIndex].played = true - end - end + if messageData.multSound and #messageData.multSound > 0 then + for index, sData in pairs(messageData.multSound) do + if sData.time <= messageData.displayedFor and sData.played == false and sData.time < nextSound then -- find index of the next sound to be played + nextSound = sData.time + soundIndex = index + end + end + if soundIndex ~= 0 then + messageData.multSound[soundIndex].played = true + end + end - for recIndex, recData in pairs(messageData.msgFor) do -- iterate recipiants - if recData == 'RED' or recData == 'BLUE' or activeClients[recData] then -- rec exists - if messageData.text then -- text - if not msgTableText[recData] then -- create table entry for text - msgTableText[recData] = {} - msgTableText[recData].text = {} - if recData == 'RED' or recData == 'BLUE' then - msgTableText[recData].text[1] = '-------Combined Arms Message-------- \n' - end - msgTableText[recData].text[#msgTableText[recData].text + 1] = messageData.text - msgTableText[recData].displayTime = messageData.displayTime - messageData.displayedFor - else -- add to table entry and adjust display time if needed - if recData == 'RED' or recData == 'BLUE' then - msgTableText[recData].text[#msgTableText[recData].text + 1] = '\n ---------------- Combined Arms Message: \n' - else - msgTableText[recData].text[#msgTableText[recData].text + 1] = '\n ---------------- \n' - end - msgTableText[recData].text[#msgTableText[recData].text + 1] = messageData.text - if msgTableText[recData].displayTime < messageData.displayTime - messageData.displayedFor then - msgTableText[recData].displayTime = messageData.displayTime - messageData.displayedFor - else - msgTableText[recData].displayTime = 1 - end - end - end - if soundIndex ~= 0 then - msgTableSound[recData] = messageData.multSound[soundIndex].file - end - end - end + for recIndex, recData in pairs(messageData.msgFor) do -- iterate recipiants + if recData == 'RED' or recData == 'BLUE' or activeClients[recData] then -- rec exists + if messageData.text then -- text + if not msgTableText[recData] then -- create table entry for text + msgTableText[recData] = {} + msgTableText[recData].text = {} + if recData == 'RED' or recData == 'BLUE' then + msgTableText[recData].text[1] = '-------Combined Arms Message-------- \n' + end + msgTableText[recData].text[#msgTableText[recData].text + 1] = messageData.text + msgTableText[recData].displayTime = messageData.displayTime - messageData.displayedFor + else -- add to table entry and adjust display time if needed + if recData == 'RED' or recData == 'BLUE' then + msgTableText[recData].text[#msgTableText[recData].text + 1] = '\n ---------------- Combined Arms Message: \n' + else + msgTableText[recData].text[#msgTableText[recData].text + 1] = '\n ---------------- \n' + end + msgTableText[recData].text[#msgTableText[recData].text + 1] = messageData.text + if msgTableText[recData].displayTime < messageData.displayTime - messageData.displayedFor then + msgTableText[recData].displayTime = messageData.displayTime - messageData.displayedFor + else + msgTableText[recData].displayTime = 1 + end + end + end + if soundIndex ~= 0 then + msgTableSound[recData] = messageData.multSound[soundIndex].file + end + end + end - end - end - ------- new display + end + end + ------- new display - if caSlots == true and caMSGtoGroup == false then - if msgTableText.RED then - trigger.action.outTextForCoalition(coalition.side.RED, table.concat(msgTableText.RED.text), msgTableText.RED.displayTime, true) + if caSlots == true and caMSGtoGroup == false then + if msgTableText.RED then + trigger.action.outTextForCoalition(coalition.side.RED, table.concat(msgTableText.RED.text), msgTableText.RED.displayTime, true) - end - if msgTableText.BLUE then - trigger.action.outTextForCoalition(coalition.side.BLUE, table.concat(msgTableText.BLUE.text), msgTableText.BLUE.displayTime, true) - end - end + end + if msgTableText.BLUE then + trigger.action.outTextForCoalition(coalition.side.BLUE, table.concat(msgTableText.BLUE.text), msgTableText.BLUE.displayTime, true) + end + end - for index, msgData in pairs(msgTableText) do - if type(index) == 'number' then -- its a groupNumber - trigger.action.outTextForGroup(index, table.concat(msgData.text), msgData.displayTime, true) - end - end - --- new audio - if msgTableSound.RED then - trigger.action.outSoundForCoalition(coalition.side.RED, msgTableSound.RED) - end - if msgTableSound.BLUE then - trigger.action.outSoundForCoalition(coalition.side.BLUE, msgTableSound.BLUE) - end + for index, msgData in pairs(msgTableText) do + if type(index) == 'number' then -- its a groupNumber + trigger.action.outTextForGroup(index, table.concat(msgData.text), msgData.displayTime, true) + end + end + --- new audio + if msgTableSound.RED then + trigger.action.outSoundForCoalition(coalition.side.RED, msgTableSound.RED) + end + if msgTableSound.BLUE then + trigger.action.outSoundForCoalition(coalition.side.BLUE, msgTableSound.BLUE) + end - for index, file in pairs(msgTableSound) do - if type(index) == 'number' then -- its a groupNumber - trigger.action.outSoundForGroup(index, file) - end - end - else - mist.removeFunction(displayFuncId) - displayActive = false - end + for index, file in pairs(msgTableSound) do + if type(index) == 'number' then -- its a groupNumber + trigger.action.outSoundForGroup(index, file) + end + end + else + mist.removeFunction(displayFuncId) + displayActive = false + end - end + end - local typeBase = { - ['Mi-8MT'] = {'Mi-8MTV2', 'Mi-8MTV', 'Mi-8'}, - ['MiG-21Bis'] = {'Mig-21'}, - ['MiG-15bis'] = {'Mig-15'}, - ['FW-190D9'] = {'FW-190'}, - ['Bf-109K-4'] = {'Bf-109'}, - } + local typeBase = { + ['Mi-8MT'] = {'Mi-8MTV2', 'Mi-8MTV', 'Mi-8'}, + ['MiG-21Bis'] = {'Mig-21'}, + ['MiG-15bis'] = {'Mig-15'}, + ['FW-190D9'] = {'FW-190'}, + ['Bf-109K-4'] = {'Bf-109'}, + } - --[[function mist.setCAGroupMSG(val) - if type(val) == 'boolean' then - caMSGtoGroup = val - return true - end - return false + --[[function mist.setCAGroupMSG(val) + if type(val) == 'boolean' then + caMSGtoGroup = val + return true + end + return false end]] - mist.message = { + mist.message = { - add = function(vars) - local function msgSpamFilter(recList, spamBlockOn) - for id, name in pairs(recList) do - if name == spamBlockOn then - -- log:info('already on recList') - return recList - end - end - --log:info('add to recList') - table.insert(recList, spamBlockOn) - return recList - end + add = function(vars) + local function msgSpamFilter(recList, spamBlockOn) + for id, name in pairs(recList) do + if name == spamBlockOn then + -- log:info('already on recList') + return recList + end + end + --log:info('add to recList') + table.insert(recList, spamBlockOn) + return recList + end - --[[ - local vars = {} - vars.text = 'Hello World' - vars.displayTime = 20 - vars.msgFor = {coa = {'red'}, countries = {'Ukraine', 'Georgia'}, unitTypes = {'A-10C'}} - mist.message.add(vars) + --[[ + local vars = {} + vars.text = 'Hello World' + vars.displayTime = 20 + vars.msgFor = {coa = {'red'}, countries = {'Ukraine', 'Georgia'}, unitTypes = {'A-10C'}} + mist.message.add(vars) - Displays the message for all red coalition players. Players belonging to Ukraine and Georgia, and all A-10Cs on the map + Displays the message for all red coalition players. Players belonging to Ukraine and Georgia, and all A-10Cs on the map - ]] + ]] - local new = {} - new.text = vars.text -- The actual message - new.displayTime = vars.displayTime -- How long will the message appear for - new.displayedFor = 0 -- how long the message has been displayed so far - new.name = vars.name -- ID to overwrite the older message (if it exists) Basically it replaces a message that is displayed with new text. - new.addedAt = timer.getTime() - new.update = true + local new = {} + new.text = vars.text -- The actual message + new.displayTime = vars.displayTime -- How long will the message appear for + new.displayedFor = 0 -- how long the message has been displayed so far + new.name = vars.name -- ID to overwrite the older message (if it exists) Basically it replaces a message that is displayed with new text. + new.addedAt = timer.getTime() + new.update = true - if vars.multSound and vars.multSound[1] then - new.multSound = vars.multSound - else - new.multSound = {} - end + if vars.multSound and vars.multSound[1] then + new.multSound = vars.multSound + else + new.multSound = {} + end - if vars.sound or vars.fileName then -- converts old sound file system into new multSound format - local sound = vars.sound - if vars.fileName then - sound = vars.fileName - end - new.multSound[#new.multSound+1] = {time = 0.1, file = sound} - end + if vars.sound or vars.fileName then -- converts old sound file system into new multSound format + local sound = vars.sound + if vars.fileName then + sound = vars.fileName + end + new.multSound[#new.multSound+1] = {time = 0.1, file = sound} + end - if #new.multSound > 0 then - for i, data in pairs(new.multSound) do - data.played = false - end - end + if #new.multSound > 0 then + for i, data in pairs(new.multSound) do + data.played = false + end + end - local newMsgFor = {} -- list of all groups message displays for - for forIndex, forData in pairs(vars.msgFor) do - for list, listData in pairs(forData) do - for clientId, clientData in pairs(mist.DBs.humansById) do - forIndex = string.lower(forIndex) - if type(listData) == 'string' then - listData = string.lower(listData) - end - if (forIndex == 'coa' and (listData == string.lower(clientData.coalition) or listData == 'all')) or (forIndex == 'countries' and string.lower(clientData.country) == listData) or (forIndex == 'units' and string.lower(clientData.unitName) == listData) then -- - newMsgFor = msgSpamFilter(newMsgFor, clientData.groupId) -- so units dont get the same message twice if complex rules are given - --table.insert(newMsgFor, clientId) - elseif forIndex == 'unittypes' then - for typeId, typeData in pairs(listData) do - local found = false - for clientDataEntry, clientDataVal in pairs(clientData) do - if type(clientDataVal) == 'string' then - if mist.matchString(list, clientDataVal) == true or list == 'all' then - local sString = typeData - for rName, pTbl in pairs(typeBase) do -- just a quick check to see if the user may have meant something and got the specific type of the unit wrong - for pIndex, pName in pairs(pTbl) do - if mist.stringMatch(sString, pName) then - sString = rName - end - end - end - if sString == clientData.type then - found = true - newMsgFor = msgSpamFilter(newMsgFor, clientData.groupId) -- sends info oto other function to see if client is already recieving the current message. - --table.insert(newMsgFor, clientId) - end - end - end - if found == true then -- shouldn't this be elsewhere too? - break - end - end - end + local newMsgFor = {} -- list of all groups message displays for + for forIndex, forData in pairs(vars.msgFor) do + for list, listData in pairs(forData) do + for clientId, clientData in pairs(mist.DBs.humansById) do + forIndex = string.lower(forIndex) + if type(listData) == 'string' then + listData = string.lower(listData) + end + if (forIndex == 'coa' and (listData == string.lower(clientData.coalition) or listData == 'all')) or (forIndex == 'countries' and string.lower(clientData.country) == listData) or (forIndex == 'units' and string.lower(clientData.unitName) == listData) then -- + newMsgFor = msgSpamFilter(newMsgFor, clientData.groupId) -- so units dont get the same message twice if complex rules are given + --table.insert(newMsgFor, clientId) + elseif forIndex == 'unittypes' then + for typeId, typeData in pairs(listData) do + local found = false + for clientDataEntry, clientDataVal in pairs(clientData) do + if type(clientDataVal) == 'string' then + if mist.matchString(list, clientDataVal) == true or list == 'all' then + local sString = typeData + for rName, pTbl in pairs(typeBase) do -- just a quick check to see if the user may have meant something and got the specific type of the unit wrong + for pIndex, pName in pairs(pTbl) do + if mist.stringMatch(sString, pName) then + sString = rName + end + end + end + if sString == clientData.type then + found = true + newMsgFor = msgSpamFilter(newMsgFor, clientData.groupId) -- sends info oto other function to see if client is already recieving the current message. + --table.insert(newMsgFor, clientId) + end + end + end + if found == true then -- shouldn't this be elsewhere too? + break + end + end + end - end - end - for coaData, coaId in pairs(coalition.side) do - if string.lower(forIndex) == 'coa' or string.lower(forIndex) == 'ca' then - if listData == string.lower(coaData) or listData == 'all' then - newMsgFor = msgSpamFilter(newMsgFor, coaData) - end - end - end - end - end + end + end + for coaData, coaId in pairs(coalition.side) do + if string.lower(forIndex) == 'coa' or string.lower(forIndex) == 'ca' then + if listData == string.lower(coaData) or listData == 'all' then + newMsgFor = msgSpamFilter(newMsgFor, coaData) + end + end + end + end + end - if #newMsgFor > 0 then - new.msgFor = newMsgFor -- I swear its not confusing + if #newMsgFor > 0 then + new.msgFor = newMsgFor -- I swear its not confusing - else - return false - end + else + return false + end - if vars.name and type(vars.name) == 'string' then - for i = 1, #messageList do - if messageList[i].name then - if messageList[i].name == vars.name then - --log:info('updateMessage') - messageList[i].displayedFor = 0 - messageList[i].addedAt = timer.getTime() - messageList[i].sound = new.sound - messageList[i].text = new.text - messageList[i].msgFor = new.msgFor - messageList[i].multSound = new.multSound - messageList[i].update = true - return messageList[i].messageID - end - end - end - end + if vars.name and type(vars.name) == 'string' then + for i = 1, #messageList do + if messageList[i].name then + if messageList[i].name == vars.name then + --log:info('updateMessage') + messageList[i].displayedFor = 0 + messageList[i].addedAt = timer.getTime() + messageList[i].sound = new.sound + messageList[i].text = new.text + messageList[i].msgFor = new.msgFor + messageList[i].multSound = new.multSound + messageList[i].update = true + return messageList[i].messageID + end + end + end + end - messageID = messageID + 1 - new.messageID = messageID + messageID = messageID + 1 + new.messageID = messageID - --mist.debug.writeData(mist.utils.serialize,{'msg', new}, 'newMsg.lua') + --mist.debug.writeData(mist.utils.serialize,{'msg', new}, 'newMsg.lua') - messageList[#messageList + 1] = new + messageList[#messageList + 1] = new - local mt = { __index = mist.message} - setmetatable(new, mt) + local mt = { __index = mist.message} + setmetatable(new, mt) - if displayActive == false then - displayActive = true - displayFuncId = mist.scheduleFunction(mistdisplayV4, {}, timer.getTime() + messageDisplayRate, messageDisplayRate) - end + if displayActive == false then + displayActive = true + displayFuncId = mist.scheduleFunction(mistdisplayV4, {}, timer.getTime() + messageDisplayRate, messageDisplayRate) + end - return messageID + return messageID - end, + end, - remove = function(self) -- Now a self variable; the former functionality taken up by mist.message.removeById. - for i, msgData in pairs(messageList) do - if messageList[i] == self then - table.remove(messageList, i) - return true --removal successful - end - end - return false -- removal not successful this script fails at life! - end, + remove = function(self) -- Now a self variable; the former functionality taken up by mist.message.removeById. + for i, msgData in pairs(messageList) do + if messageList[i] == self then + table.remove(messageList, i) + return true --removal successful + end + end + return false -- removal not successful this script fails at life! + end, - removeById = function(id) -- This function is NOT passed a self variable; it is the remove by id function. - for i, msgData in pairs(messageList) do - if messageList[i].messageID == id then - table.remove(messageList, i) - return true --removal successful - end - end - return false -- removal not successful this script fails at life! - end, - } + removeById = function(id) -- This function is NOT passed a self variable; it is the remove by id function. + for i, msgData in pairs(messageList) do + if messageList[i].messageID == id then + table.remove(messageList, i) + return true --removal successful + end + end + return false -- removal not successful this script fails at life! + end, + } - --[[ vars for mist.msgMGRS + --[[ vars for mist.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 ]] - function mist.msgMGRS(vars) - local units = vars.units - local acc = vars.acc - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor + function mist.msgMGRS(vars) + local units = vars.units + local acc = vars.acc + local text = vars.text + local displayTime = vars.displayTime + local msgFor = vars.msgFor - local s = mist.getMGRSString{units = units, acc = acc} - local newText - if text then - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else - -- just append to the end. - newText = text .. s - end - else - newText = s - end - mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } - end + local s = mist.getMGRSString{units = units, acc = acc} + local newText + if text then + if string.find(text, '%%s') then -- look for %s + newText = string.format(text, s) -- insert the coordinates into the message + else + -- just append to the end. + newText = text .. s + end + else + newText = s + end + mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor + } + end - --[[ vars for mist.msgLL + --[[ vars for mist.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.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 ]] - function mist.msgLL(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 + function mist.msgLL(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 = mist.getLLString{units = units, acc = acc, DMS = DMS} - local newText - if text then - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else - -- just append to the end. - newText = text .. s - end - else - newText = s - end + local s = mist.getLLString{units = units, acc = acc, DMS = DMS} + local newText + if text then + if string.find(text, '%%s') then -- look for %s + newText = string.format(text, s) -- insert the coordinates into the message + else + -- just append to the end. + newText = text .. s + end + else + newText = s + end - mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } + mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor + } - 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.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 ]] - function mist.msgBR(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 mist.getBRString - local alt = vars.alt - local metric = vars.metric - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor + function mist.msgBR(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 mist.getBRString + local alt = vars.alt + local metric = vars.metric + local text = vars.text + local displayTime = vars.displayTime + local msgFor = vars.msgFor - local s = mist.getBRString{units = units, ref = ref, alt = alt, metric = metric} - local newText - if text then - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else - -- just append to the end. - newText = text .. s - end - else - newText = s - end + local s = mist.getBRString{units = units, ref = ref, alt = alt, metric = metric} + local newText + if text then + if string.find(text, '%%s') then -- look for %s + newText = string.format(text, s) -- insert the coordinates into the message + else + -- just append to the end. + newText = text .. s + end + else + newText = s + end - mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } + mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor + } - end + end - -- basically, just sub-types of mist.msgBR... saves folks the work of getting the ref point. - --[[ + -- basically, just sub-types of mist.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.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 ]] - function mist.msgBullseye(vars) - if string.lower(vars.ref) == 'red' then - vars.ref = mist.DBs.missionData.bullseye.red - mist.msgBR(vars) - elseif string.lower(vars.ref) == 'blue' then - vars.ref = mist.DBs.missionData.bullseye.blue - mist.msgBR(vars) - end - end + function mist.msgBullseye(vars) + if string.lower(vars.ref) == 'red' then + vars.ref = mist.DBs.missionData.bullseye.red + mist.msgBR(vars) + elseif string.lower(vars.ref) == 'blue' then + vars.ref = mist.DBs.missionData.bullseye.blue + mist.msgBR(vars) + end + end - --[[ + --[[ vars.units- table of unit names (NOT unitNameTable- maybe this should change). -vars.ref - unit name of reference point +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 ]] - function mist.msgBRA(vars) - if Unit.getByName(vars.ref) and Unit.getByName(vars.ref):isExist() == true then - vars.ref = Unit.getByName(vars.ref):getPosition().p - if not vars.alt then - vars.alt = true - end - mist.msgBR(vars) - end - end + function mist.msgBRA(vars) + if Unit.getByName(vars.ref) and Unit.getByName(vars.ref):isExist() == true then + vars.ref = Unit.getByName(vars.ref):getPosition().p + if not vars.alt then + vars.alt = true + end + mist.msgBR(vars) + end + end - --[[ vars for mist.msgLeadingMGRS: + --[[ vars for mist.msgLeadingMGRS: vars.units - table of unit names vars.heading - direction vars.radius - number @@ -5290,87 +5291,87 @@ vars.text - text of the message vars.displayTime vars.msgFor - scope ]] - function mist.msgLeadingMGRS(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 + function mist.msgLeadingMGRS(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 = mist.getLeadingMGRSString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc} - local newText - if text then - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else - -- just append to the end. - newText = text .. s - end - else - newText = s - end + local s = mist.getLeadingMGRSString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc} + local newText + if text then + if string.find(text, '%%s') then -- look for %s + newText = string.format(text, s) -- insert the coordinates into the message + else + -- just append to the end. + newText = text .. s + end + else + newText = s + end - mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } + mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor + } - end + end - --[[ vars for mist.msgLeadingLL: + --[[ vars for mist.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.DMS - boolean, true if you want DMS. (optional) vars.text - text of the message vars.displayTime vars.msgFor - scope ]] - function mist.msgLeadingLL(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 + function mist.msgLeadingLL(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 = mist.getLeadingLLString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc, DMS = DMS} - local newText + local s = mist.getLeadingLLString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc, DMS = DMS} + local newText - if text then - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else - -- just append to the end. - newText = text .. s - end - else - newText = s - end + if text then + if string.find(text, '%%s') then -- look for %s + newText = string.format(text, s) -- insert the coordinates into the message + else + -- just append to the end. + newText = text .. s + end + else + newText = s + end - mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } + mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor + } - end + end - --[[ + --[[ vars.units - table of unit names vars.heading - direction, number vars.radius - number -vars.headingDegrees - boolean, switches heading to degrees (optional) +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. @@ -5378,349 +5379,349 @@ vars.text - text of the message vars.displayTime vars.msgFor - scope ]] - function mist.msgLeadingBR(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 mist.getBRString - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor + function mist.msgLeadingBR(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 mist.getBRString + local text = vars.text + local displayTime = vars.displayTime + local msgFor = vars.msgFor - local s = mist.getLeadingBRString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, metric = metric, alt = alt, ref = ref} - local newText + local s = mist.getLeadingBRString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, metric = metric, alt = alt, ref = ref} + local newText - if text then - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else - -- just append to the end. - newText = text .. s - end - else - newText = s - end + if text then + if string.find(text, '%%s') then -- look for %s + newText = string.format(text, s) -- insert the coordinates into the message + else + -- just append to the end. + newText = text .. s + end + else + newText = s + end - mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } - end + mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor + } + end end --- Demo functions. -- @section mist.demos do -- mist.demos scope - mist.demos = {} + mist.demos = {} - function mist.demos.printFlightData(unit) - if unit:isExist() then - local function printData(unit, prevVel, prevE, prevTime) - local angles = mist.getAttitude(unit) - if angles then - local Heading = angles.Heading - local Pitch = angles.Pitch - local Roll = angles.Roll - local Yaw = angles.Yaw - local AoA = angles.AoA - local ClimbAngle = angles.ClimbAngle + function mist.demos.printFlightData(unit) + if unit:isExist() then + local function printData(unit, prevVel, prevE, prevTime) + local angles = mist.getAttitude(unit) + if angles then + local Heading = angles.Heading + local Pitch = angles.Pitch + local Roll = angles.Roll + local Yaw = angles.Yaw + local AoA = angles.AoA + local ClimbAngle = angles.ClimbAngle - if not Heading then - Heading = 'NA' - else - Heading = string.format('%12.2f', mist.utils.toDegree(Heading)) - end + if not Heading then + Heading = 'NA' + else + Heading = string.format('%12.2f', mist.utils.toDegree(Heading)) + end - if not Pitch then - Pitch = 'NA' - else - Pitch = string.format('%12.2f', mist.utils.toDegree(Pitch)) - end + if not Pitch then + Pitch = 'NA' + else + Pitch = string.format('%12.2f', mist.utils.toDegree(Pitch)) + end - if not Roll then - Roll = 'NA' - else - Roll = string.format('%12.2f', mist.utils.toDegree(Roll)) - end + if not Roll then + Roll = 'NA' + else + Roll = string.format('%12.2f', mist.utils.toDegree(Roll)) + end - local AoAplusYaw = 'NA' - if AoA and Yaw then - AoAplusYaw = string.format('%12.2f', mist.utils.toDegree((AoA^2 + Yaw^2)^0.5)) - end + local AoAplusYaw = 'NA' + if AoA and Yaw then + AoAplusYaw = string.format('%12.2f', mist.utils.toDegree((AoA^2 + Yaw^2)^0.5)) + end - if not Yaw then - Yaw = 'NA' - else - Yaw = string.format('%12.2f', mist.utils.toDegree(Yaw)) - end + if not Yaw then + Yaw = 'NA' + else + Yaw = string.format('%12.2f', mist.utils.toDegree(Yaw)) + end - if not AoA then - AoA = 'NA' - else - AoA = string.format('%12.2f', mist.utils.toDegree(AoA)) - end + if not AoA then + AoA = 'NA' + else + AoA = string.format('%12.2f', mist.utils.toDegree(AoA)) + end - if not ClimbAngle then - ClimbAngle = 'NA' - else - ClimbAngle = string.format('%12.2f', mist.utils.toDegree(ClimbAngle)) - end - local unitPos = unit:getPosition() - local unitVel = unit:getVelocity() - local curTime = timer.getTime() - local absVel = string.format('%12.2f', mist.vec.mag(unitVel)) + if not ClimbAngle then + ClimbAngle = 'NA' + else + ClimbAngle = string.format('%12.2f', mist.utils.toDegree(ClimbAngle)) + end + local unitPos = unit:getPosition() + local unitVel = unit:getVelocity() + local curTime = timer.getTime() + local absVel = string.format('%12.2f', mist.vec.mag(unitVel)) - local unitAcc = 'NA' - local Gs = 'NA' - local axialGs = 'NA' - local transGs = 'NA' - if prevVel and prevTime then - local xAcc = (unitVel.x - prevVel.x)/(curTime - prevTime) - local yAcc = (unitVel.y - prevVel.y)/(curTime - prevTime) - local zAcc = (unitVel.z - prevVel.z)/(curTime - prevTime) + local unitAcc = 'NA' + local Gs = 'NA' + local axialGs = 'NA' + local transGs = 'NA' + if prevVel and prevTime then + local xAcc = (unitVel.x - prevVel.x)/(curTime - prevTime) + local yAcc = (unitVel.y - prevVel.y)/(curTime - prevTime) + local zAcc = (unitVel.z - prevVel.z)/(curTime - prevTime) - unitAcc = string.format('%12.2f', mist.vec.mag({x = xAcc, y = yAcc, z = zAcc})) - Gs = string.format('%12.2f', mist.vec.mag({x = xAcc, y = yAcc + 9.81, z = zAcc})/9.81) - axialGs = string.format('%12.2f', mist.vec.dp({x = xAcc, y = yAcc + 9.81, z = zAcc}, unitPos.x)/9.81) - transGs = string.format('%12.2f', mist.vec.mag(mist.vec.cp({x = xAcc, y = yAcc + 9.81, z = zAcc}, unitPos.x))/9.81) - end + unitAcc = string.format('%12.2f', mist.vec.mag({x = xAcc, y = yAcc, z = zAcc})) + Gs = string.format('%12.2f', mist.vec.mag({x = xAcc, y = yAcc + 9.81, z = zAcc})/9.81) + axialGs = string.format('%12.2f', mist.vec.dp({x = xAcc, y = yAcc + 9.81, z = zAcc}, unitPos.x)/9.81) + transGs = string.format('%12.2f', mist.vec.mag(mist.vec.cp({x = xAcc, y = yAcc + 9.81, z = zAcc}, unitPos.x))/9.81) + end - local E = 0.5*mist.vec.mag(unitVel)^2 + 9.81*unitPos.p.y + local E = 0.5*mist.vec.mag(unitVel)^2 + 9.81*unitPos.p.y - local energy = string.format('%12.2e', E) + local energy = string.format('%12.2e', E) - local dEdt = 'NA' - if prevE and prevTime then - dEdt = string.format('%12.2e', (E - prevE)/(curTime - prevTime)) - end + local dEdt = 'NA' + if prevE and prevTime then + dEdt = string.format('%12.2e', (E - prevE)/(curTime - prevTime)) + end - trigger.action.outText(string.format('%-25s', 'Heading: ') .. Heading .. ' degrees\n' .. string.format('%-25s', 'Roll: ') .. Roll .. ' degrees\n' .. string.format('%-25s', 'Pitch: ') .. Pitch - .. ' degrees\n' .. string.format('%-25s', 'Yaw: ') .. Yaw .. ' degrees\n' .. string.format('%-25s', 'AoA: ') .. AoA .. ' degrees\n' .. string.format('%-25s', 'AoA plus Yaw: ') .. AoAplusYaw .. ' degrees\n' .. string.format('%-25s', 'Climb Angle: ') .. - ClimbAngle .. ' degrees\n' .. string.format('%-25s', 'Absolute Velocity: ') .. absVel .. ' m/s\n' .. string.format('%-25s', 'Absolute Acceleration: ') .. unitAcc ..' m/s^2\n' - .. string.format('%-25s', 'Axial G loading: ') .. axialGs .. ' g\n' .. string.format('%-25s', 'Transverse G loading: ') .. transGs .. ' g\n' .. string.format('%-25s', 'Absolute G loading: ') .. Gs .. ' g\n' .. string.format('%-25s', 'Energy: ') .. energy .. ' J/kg\n' .. string.format('%-25s', 'dE/dt: ') .. dEdt .. - ' J/(kg*s)', 1) + trigger.action.outText(string.format('%-25s', 'Heading: ') .. Heading .. ' degrees\n' .. string.format('%-25s', 'Roll: ') .. Roll .. ' degrees\n' .. string.format('%-25s', 'Pitch: ') .. Pitch + .. ' degrees\n' .. string.format('%-25s', 'Yaw: ') .. Yaw .. ' degrees\n' .. string.format('%-25s', 'AoA: ') .. AoA .. ' degrees\n' .. string.format('%-25s', 'AoA plus Yaw: ') .. AoAplusYaw .. ' degrees\n' .. string.format('%-25s', 'Climb Angle: ') .. + ClimbAngle .. ' degrees\n' .. string.format('%-25s', 'Absolute Velocity: ') .. absVel .. ' m/s\n' .. string.format('%-25s', 'Absolute Acceleration: ') .. unitAcc ..' m/s^2\n' + .. string.format('%-25s', 'Axial G loading: ') .. axialGs .. ' g\n' .. string.format('%-25s', 'Transverse G loading: ') .. transGs .. ' g\n' .. string.format('%-25s', 'Absolute G loading: ') .. Gs .. ' g\n' .. string.format('%-25s', 'Energy: ') .. energy .. ' J/kg\n' .. string.format('%-25s', 'dE/dt: ') .. dEdt .. + ' J/(kg*s)', 1) - return unitVel, E, curTime - end - end + return unitVel, E, curTime + end + end - local function frameFinder(unit, prevVel, prevE, prevTime) - if unit:isExist() then - local currVel = unit:getVelocity() - if prevVel and (prevVel.x ~= currVel.x or prevVel.y ~= currVel.y or prevVel.z ~= currVel.z) or (prevTime and (timer.getTime() - prevTime) > 0.25) then - prevVel, prevE, prevTime = printData(unit, prevVel, prevE, prevTime) - end - mist.scheduleFunction(frameFinder, {unit, prevVel, prevE, prevTime}, timer.getTime() + 0.005) -- it can't go this fast, limited to the 100 times a sec check right now. - end - end + local function frameFinder(unit, prevVel, prevE, prevTime) + if unit:isExist() then + local currVel = unit:getVelocity() + if prevVel and (prevVel.x ~= currVel.x or prevVel.y ~= currVel.y or prevVel.z ~= currVel.z) or (prevTime and (timer.getTime() - prevTime) > 0.25) then + prevVel, prevE, prevTime = printData(unit, prevVel, prevE, prevTime) + end + mist.scheduleFunction(frameFinder, {unit, prevVel, prevE, prevTime}, timer.getTime() + 0.005) -- it can't go this fast, limited to the 100 times a sec check right now. + end + end - local curVel = unit:getVelocity() - local curTime = timer.getTime() - local curE = 0.5*mist.vec.mag(curVel)^2 + 9.81*unit:getPosition().p.y - frameFinder(unit, curVel, curE, curTime) + local curVel = unit:getVelocity() + local curTime = timer.getTime() + local curE = 0.5*mist.vec.mag(curVel)^2 + 9.81*unit:getPosition().p.y + frameFinder(unit, curVel, curE, curTime) - end + end - end + end end --- Time conversion functions. -- @section mist.time do -- mist.time scope - mist.time = {} - -- returns a string for specified military time - -- theTime is optional - -- if present current time in mil time is returned - -- if number or table the time is converted into mil tim - function mist.time.convertToSec(timeTable) + mist.time = {} + -- returns a string for specified military time + -- theTime is optional + -- if present current time in mil time is returned + -- if number or table the time is converted into mil tim + function mist.time.convertToSec(timeTable) - timeInSec = 0 - if timeTable and type(timeTable) == 'number' then - timeInSec = timeTable - elseif timeTable and type(timeTable) == 'table' and (timeTable.d or timeTable.h or timeTable.m or timeTable.s) then - if timeTable.d and type(timeTable.d) == 'number' then - timeInSec = timeInSec + (timeTable.d*86400) - end - if timeTable.h and type(timeTable.h) == 'number' then - timeInSec = timeInSec + (timeTable.h*3600) - end - if timeTable.m and type(timeTable.m) == 'number' then - timeInSec = timeInSec + (timeTable.m*60) - end - if timeTable.s and type(timeTable.s) == 'number' then - timeInSec = timeInSec + timeTable.s - end + timeInSec = 0 + if timeTable and type(timeTable) == 'number' then + timeInSec = timeTable + elseif timeTable and type(timeTable) == 'table' and (timeTable.d or timeTable.h or timeTable.m or timeTable.s) then + if timeTable.d and type(timeTable.d) == 'number' then + timeInSec = timeInSec + (timeTable.d*86400) + end + if timeTable.h and type(timeTable.h) == 'number' then + timeInSec = timeInSec + (timeTable.h*3600) + end + if timeTable.m and type(timeTable.m) == 'number' then + timeInSec = timeInSec + (timeTable.m*60) + end + if timeTable.s and type(timeTable.s) == 'number' then + timeInSec = timeInSec + timeTable.s + end - end - return timeInSec - end + end + return timeInSec + end - function mist.time.getDHMS(timeInSec) - if timeInSec and type(timeInSec) == 'number' then - local tbl = {d = 0, h = 0, m = 0, s = 0} - if timeInSec > 86400 then - while timeInSec > 86400 do - tbl.d = tbl.d + 1 - timeInSec = timeInSec - 86400 - end - end - if timeInSec > 3600 then - while timeInSec > 3600 do - tbl.h = tbl.h + 1 - timeInSec = timeInSec - 3600 - end - end - if timeInSec > 60 then - while timeInSec > 60 do - tbl.m = tbl.m + 1 - timeInSec = timeInSec - 60 - end - end - tbl.s = timeInSec - return tbl - else - log:error("Didn't recieve number") - return - end - end + function mist.time.getDHMS(timeInSec) + if timeInSec and type(timeInSec) == 'number' then + local tbl = {d = 0, h = 0, m = 0, s = 0} + if timeInSec > 86400 then + while timeInSec > 86400 do + tbl.d = tbl.d + 1 + timeInSec = timeInSec - 86400 + end + end + if timeInSec > 3600 then + while timeInSec > 3600 do + tbl.h = tbl.h + 1 + timeInSec = timeInSec - 3600 + end + end + if timeInSec > 60 then + while timeInSec > 60 do + tbl.m = tbl.m + 1 + timeInSec = timeInSec - 60 + end + end + tbl.s = timeInSec + return tbl + else + log:error("Didn't recieve number") + return + end + end - function mist.getMilString(theTime) - local timeInSec = 0 - if theTime then - timeInSec = mist.time.convertToSec(theTime) - else - timeInSec = mist.utils.round(timer.getAbsTime(), 0) - end + function mist.getMilString(theTime) + local timeInSec = 0 + if theTime then + timeInSec = mist.time.convertToSec(theTime) + else + timeInSec = mist.utils.round(timer.getAbsTime(), 0) + end - local DHMS = mist.time.getDHMS(timeInSec) + local DHMS = mist.time.getDHMS(timeInSec) - return tostring(string.format('%02d', DHMS.h) .. string.format('%02d',DHMS.m)) - end + return tostring(string.format('%02d', DHMS.h) .. string.format('%02d',DHMS.m)) + end - function mist.getClockString(theTime, hour) - local timeInSec = 0 - if theTime then - timeInSec = mist.time.convertToSec(theTime) - else - timeInSec = mist.utils.round(timer.getAbsTime(), 0) - end - local DHMS = mist.time.getDHMS(timeInSec) - if hour then - if DHMS.h > 12 then - DHMS.h = DHMS.h - 12 - return tostring(string.format('%02d', DHMS.h) .. ':' .. string.format('%02d',DHMS.m) .. ':' .. string.format('%02d',DHMS.s) .. ' PM') - else - return tostring(string.format('%02d', DHMS.h) .. ':' .. string.format('%02d',DHMS.m) .. ':' .. string.format('%02d',DHMS.s) .. ' AM') - end - else - return tostring(string.format('%02d', DHMS.h) .. ':' .. string.format('%02d',DHMS.m) .. ':' .. string.format('%02d',DHMS.s)) - end - end + function mist.getClockString(theTime, hour) + local timeInSec = 0 + if theTime then + timeInSec = mist.time.convertToSec(theTime) + else + timeInSec = mist.utils.round(timer.getAbsTime(), 0) + end + local DHMS = mist.time.getDHMS(timeInSec) + if hour then + if DHMS.h > 12 then + DHMS.h = DHMS.h - 12 + return tostring(string.format('%02d', DHMS.h) .. ':' .. string.format('%02d',DHMS.m) .. ':' .. string.format('%02d',DHMS.s) .. ' PM') + else + return tostring(string.format('%02d', DHMS.h) .. ':' .. string.format('%02d',DHMS.m) .. ':' .. string.format('%02d',DHMS.s) .. ' AM') + end + else + return tostring(string.format('%02d', DHMS.h) .. ':' .. string.format('%02d',DHMS.m) .. ':' .. string.format('%02d',DHMS.s)) + end + end - -- returns the date in string format - -- both variables optional - -- first val returns with the month as a string - -- 2nd val defins if it should be written the American way or the wrong way. - function mist.time.getDate(convert) - local cal = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} -- starts at june. 2011. 2012 is leap year, sigh. add a simple check for leap year - local date = {} - date.d = 0 - date.m = 6 - date.y = 2011 + -- returns the date in string format + -- both variables optional + -- first val returns with the month as a string + -- 2nd val defins if it should be written the American way or the wrong way. + function mist.time.getDate(convert) + local cal = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} -- starts at june. 2011. 2012 is leap year, sigh. add a simple check for leap year + local date = {} + date.d = 0 + date.m = 6 + date.y = 2011 - local start = 0 - local timeInSec = mist.utils.round(timer.getAbsTime()) - if convert and type(convert) == 'number' then - timeInSec = convert - end + local start = 0 + local timeInSec = mist.utils.round(timer.getAbsTime()) + if convert and type(convert) == 'number' then + timeInSec = convert + end - while start < timeInSec do + while start < timeInSec do - if date.d == cal[date.m] then - --if y % 4 >= 0 and i == 2 then -- for leap year. DCS doesnt do leapyear, but I am keeping the code dormant because maybe with WW2 the mission year may become relevant + if date.d == cal[date.m] then + --if y % 4 >= 0 and i == 2 then -- for leap year. DCS doesnt do leapyear, but I am keeping the code dormant because maybe with WW2 the mission year may become relevant - --else - date.m = date.m + 1 - date.d = 0 - --end - end - if date.m == 13 then - date.m = 1 - date.y = date.y + 1 - end - date.d = date.d + 1 - start = start + 86400 - end - return date - end + --else + date.m = date.m + 1 + date.d = 0 + --end + end + if date.m == 13 then + date.m = 1 + date.y = date.y + 1 + end + date.d = date.d + 1 + start = start + 86400 + end + return date + end - function mist.time.relativeToStart(time) - if type(time) == 'number' then - return time - timer.getTime0() - end - end + function mist.time.relativeToStart(time) + if type(time) == 'number' then + return time - timer.getTime0() + end + end - function mist.getDateString(rtnType, murica, oTime) -- returns date based on time - local word = {'January', 'Feburary', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' } -- 'etc - local curTime = 0 - if oTime then - curTime = oTime - else - curTime = mist.utils.round(timer.getAbsTime()) - end - local tbl = mist.time.getDate(curTime) + function mist.getDateString(rtnType, murica, oTime) -- returns date based on time + local word = {'January', 'Feburary', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' } -- 'etc + local curTime = 0 + if oTime then + curTime = oTime + else + curTime = mist.utils.round(timer.getAbsTime()) + end + local tbl = mist.time.getDate(curTime) - if rtnType then - if murica then - return tostring(word[tbl.m] .. ' ' .. tbl.d .. ' ' .. tbl.y) - else - return tostring(tbl.d .. ' ' .. word[tbl.m] .. ' ' .. tbl.y) - end - else - if murica then - return tostring(tbl.m .. '.' .. tbl.d .. '.' .. tbl.y) - else - return tostring(tbl.d .. '.' .. tbl.m .. '.' .. tbl.y) - end - end - end - --WIP - function mist.time.milToGame(milString, rtnType) --converts a military time. By default returns the abosolute time that event would occur. With optional value it returns how many seconds from time of call till that time. - local curTime = mist.utils.round(timer.getAbsTime()) - local milTimeInSec = 0 + if rtnType then + if murica then + return tostring(word[tbl.m] .. ' ' .. tbl.d .. ' ' .. tbl.y) + else + return tostring(tbl.d .. ' ' .. word[tbl.m] .. ' ' .. tbl.y) + end + else + if murica then + return tostring(tbl.m .. '.' .. tbl.d .. '.' .. tbl.y) + else + return tostring(tbl.d .. '.' .. tbl.m .. '.' .. tbl.y) + end + end + end + --WIP + function mist.time.milToGame(milString, rtnType) --converts a military time. By default returns the abosolute time that event would occur. With optional value it returns how many seconds from time of call till that time. + local curTime = mist.utils.round(timer.getAbsTime()) + local milTimeInSec = 0 - if milString and type(milString) == 'string' and string.len(milString) >= 4 then - local hr = tonumber(string.sub(milString, 1, 2)) - local mi = tonumber(string.sub(milString, 3)) - milTimeInSec = milTimeInSec + (mi*60) + (hr*3600) - elseif milString and type(milString) == 'table' and (milString.d or milString.h or milString.m or milString.s) then - milTimeInSec = mist.time.convertToSec(milString) - end + if milString and type(milString) == 'string' and string.len(milString) >= 4 then + local hr = tonumber(string.sub(milString, 1, 2)) + local mi = tonumber(string.sub(milString, 3)) + milTimeInSec = milTimeInSec + (mi*60) + (hr*3600) + elseif milString and type(milString) == 'table' and (milString.d or milString.h or milString.m or milString.s) then + milTimeInSec = mist.time.convertToSec(milString) + end - local startTime = timer.getTime0() - local daysOffset = 0 - if startTime > 86400 then - daysOffset = mist.utils.round(startTime/86400) - if daysOffset > 0 then - milTimeInSec = milTimeInSec *daysOffset - end - end + local startTime = timer.getTime0() + local daysOffset = 0 + if startTime > 86400 then + daysOffset = mist.utils.round(startTime/86400) + if daysOffset > 0 then + milTimeInSec = milTimeInSec *daysOffset + end + end - if curTime > milTimeInSec then - milTimeInSec = milTimeInSec + 86400 - end - if rtnType then - milTimeInSec = milTimeInSec - startTime - end - return milTimeInSec - end + if curTime > milTimeInSec then + milTimeInSec = milTimeInSec + 86400 + end + if rtnType then + milTimeInSec = milTimeInSec - startTime + end + return milTimeInSec + end end @@ -5728,621 +5729,621 @@ end --- Group task functions. -- @section tasks do -- group tasks scope - mist.ground = {} - mist.fixedWing = {} - mist.heli = {} - mist.air = {} - mist.air.fixedWing = {} - mist.air.heli = {} - - --- Tasks group to follow a route. - -- This sets the mission task for the given group. - -- Any wrapped actions inside the path (like enroute - -- tasks) will be executed. - -- @tparam Group group group to task. - -- @tparam table path containing - -- points defining a route. - function mist.goRoute(group, path) - local misTask = { - id = 'Mission', - params = { - route = { - points = mist.utils.deepCopy(path), - }, - }, - } - if type(group) == 'string' then - group = Group.getByName(group) - end - if group then - local groupCon = group:getController() - if groupCon then - groupCon:setTask(misTask) - return true - end - end - return false - end - - -- same as getGroupPoints but returns speed and formation type along with vec2 of point} - function mist.getGroupRoute(groupIdent, task) - -- 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 = mist.DBs.MEgroupsByName[groupIdent].groupId - end - - for coa_name, coa_data in pairs(env.mission.coalition) do - if (coa_name == 'red' or coa_name == 'blue') and type(coa_data) == 'table' then - if coa_data.country then --there is a country table - for cntry_id, cntry_data in pairs(coa_data.country) do - for obj_type_name, obj_type_data in pairs(cntry_data) do - if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" then -- only these types have points - if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's a group! - for group_num, group_data in pairs(obj_type_data.group) do - if group_data and group_data.groupId == gpId then -- this is the group we are looking for - if group_data.route and group_data.route.points and #group_data.route.points > 0 then - local points = {} - - for point_num, point in pairs(group_data.route.points) do - local routeData = {} - if not point.point then - routeData.x = point.x - routeData.y = point.y - else - routeData.point = point.point --it's possible that the ME could move to the point = Vec2 notation. - end - routeData.form = point.action - routeData.speed = point.speed - routeData.alt = point.alt - routeData.alt_type = point.alt_type - routeData.airdromeId = point.airdromeId - routeData.helipadId = point.helipadId - routeData.type = point.type - routeData.action = point.action - if task then - routeData.task = point.task - end - points[point_num] = routeData - end - - return points - end - return - end --if group_data and group_data.name and group_data.name == 'groupname' - end --for group_num, group_data in pairs(obj_type_data.group) do - end --if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then - end --if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then - end --for obj_type_name, obj_type_data in pairs(cntry_data) do - end --for cntry_id, cntry_data in pairs(coa_data.country) do - end --if coa_data.country then --there is a country table - end --if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then - end --for coa_name, coa_data in pairs(mission.coalition) do - end - - -- function mist.ground.buildPath() end -- ???? - - function mist.ground.patrolRoute(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 = mist.getGroupRoute(useGroupRoute) - end - else - useRoute = vars.route - local posStart = mist.getLeadPos(gpData) - useRoute[1] = mist.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 = mist.getLeadPos(gpData) - - - useRoute[#useRoute + 1] = mist.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] = mist.ground.buildWP(tempRoute[i], tempForm, tempSpeed) - end - - if pType and string.lower(pType) == 'doubleback' then - local curRoute = mist.utils.deepCopy(useRoute) - for i = #curRoute, 2, -1 do - useRoute[#useRoute + 1] = mist.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] = 'mist.ground.patrolRoute(' - cTask3[#cTask3 + 1] = mist.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 - mist.goRoute(gpData, useRoute) - - return - end - - function mist.ground.patrol(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 - - mist.ground.patrolRoute(vars) - - return - end - - -- No longer accepts path - function mist.ground.buildWP(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 = mist.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 - - function mist.fixedWing.buildWP(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 altType == 'agl' then - wp.alt_type = 'RADIO' - elseif altType == 'baro' or altType == '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 = mist.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 - - function mist.heli.buildWP(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 altType == 'agl' then - wp.alt_type = 'RADIO' - elseif altType == 'baro' or altType == '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 = mist.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 - - -- need to return a Vec3 or Vec2? - function mist.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 - - function mist.getRandomPointInZone(zoneName, innerRadius) - if type(zoneName) == 'string' and type(trigger.misc.getZone(zoneName)) == 'table' then - return mist.getRandPointInCircle(trigger.misc.getZone(zoneName).point, trigger.misc.getZone(zoneName).radius, innerRadius) - end - return false - end - - function mist.groupToRandomPoint(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 mist.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 = mist.getRandPointInCircle(point, radius, innerRadius) - - local offset = {} - local posStart = mist.getLeadPos(group) - - offset.x = mist.utils.round(math.sin(heading - (math.pi/2)) * 50 + rndCoord.x, 3) - offset.z = mist.utils.round(math.cos(heading + (math.pi/2)) * 50 + rndCoord.y, 3) - path[#path + 1] = mist.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] = mist.ground.buildWP({x = posStart.x + 11, z = posStart.z + 11}, 'off_road', speed) - path[#path + 1] = mist.ground.buildWP(posStart, 'on_road', speed) - path[#path + 1] = mist.ground.buildWP(offset, 'on_road', speed) - else - path[#path + 1] = mist.ground.buildWP({x = posStart.x + 25, z = posStart.z + 25}, form, speed) - end - - path[#path + 1] = mist.ground.buildWP(offset, form, speed) - path[#path + 1] = mist.ground.buildWP(rndCoord, form, speed) - - mist.goRoute(group, path) - - return - end - - function mist.groupRandomDistSelf(gpData, dist, form, heading, speed) - local pos = mist.getLeadPos(gpData) - local fakeZone = {} - fakeZone.radius = dist or math.random(300, 1000) - fakeZone.point = {x = pos.x, y = pos.y, z = pos.z} - mist.groupToRandomZone(gpData, fakeZone, form, heading, speed) - - return - end - - function mist.groupToRandomZone(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 = mist.utils.kmphToMps(speed) - end - - local vars = {} - vars.group = gpData - vars.radius = zone.radius - vars.form = form - vars.headingDegrees = heading - vars.speed = speed - vars.point = mist.utils.zoneToVec3(zone) - - mist.groupToRandomPoint(vars) - - return - end - - function mist.isTerrainValid(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 - - function mist.terrainHeightDiff(coord, searchSize) - local samples = {} - local searchRadius = 5 - if searchSize then - searchRadius = searchSize - end - if type(coord) == 'string' then - coord = mist.utils.zoneToVec3(coord) - end - - coord = mist.utils.makeVec2(coord) - - samples[#samples + 1] = land.getHeight(coord) - for i = 0, 360, 30 do - samples[#samples + 1] = land.getHeight({x = (coord.x + (math.sin(math.rad(i))*searchRadius)), y = (coord.y + (math.cos(math.rad(i))*searchRadius))}) - if searchRadius >= 20 then -- if search radius is sorta large, take a sample halfway between center and outer edge - samples[#samples + 1] = land.getHeight({x = (coord.x + (math.sin(math.rad(i))*(searchRadius/2))), y = (coord.y + (math.cos(math.rad(i))*(searchRadius/2)))}) - end - end - local tMax, tMin = 0, 1000000 - for index, height in pairs(samples) do - if height > tMax then - tMax = height - end - if height < tMin then - tMin = height - end - end - return mist.utils.round(tMax - tMin, 2) - end - - function mist.groupToPoint(gpData, point, form, heading, speed, useRoads) - if type(point) == 'string' then - point = trigger.misc.getZone(point) - end - if speed then - speed = mist.utils.kmphToMps(speed) - end - - local vars = {} - vars.group = gpData - vars.form = form - vars.headingDegrees = heading - vars.speed = speed - vars.disableRoads = useRoads - vars.point = mist.utils.zoneToVec3(point) - mist.groupToRandomPoint(vars) - - return - end - - function mist.getLeadPos(group) - if type(group) == 'string' then -- group name - group = Group.getByName(group) - end - - local units = group:getUnits() - - local leader = units[1] - if not Unit.isExist(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 Unit.isExist(unit) and ind < lowestInd then - lowestInd = ind - return unit:getPosition().p - end - end - end - if leader and Unit.isExist(leader) then -- maybe a little too paranoid now... - return leader:getPosition().p - end - end + mist.ground = {} + mist.fixedWing = {} + mist.heli = {} + mist.air = {} + mist.air.fixedWing = {} + mist.air.heli = {} + + --- Tasks group to follow a route. + -- This sets the mission task for the given group. + -- Any wrapped actions inside the path (like enroute + -- tasks) will be executed. + -- @tparam Group group group to task. + -- @tparam table path containing + -- points defining a route. + function mist.goRoute(group, path) + local misTask = { + id = 'Mission', + params = { + route = { + points = mist.utils.deepCopy(path), + }, + }, + } + if type(group) == 'string' then + group = Group.getByName(group) + end + if group then + local groupCon = group:getController() + if groupCon then + groupCon:setTask(misTask) + return true + end + end + return false + end + + -- same as getGroupPoints but returns speed and formation type along with vec2 of point} + function mist.getGroupRoute(groupIdent, task) + -- 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 = mist.DBs.MEgroupsByName[groupIdent].groupId + end + + for coa_name, coa_data in pairs(env.mission.coalition) do + if (coa_name == 'red' or coa_name == 'blue') and type(coa_data) == 'table' then + if coa_data.country then --there is a country table + for cntry_id, cntry_data in pairs(coa_data.country) do + for obj_type_name, obj_type_data in pairs(cntry_data) do + if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" then -- only these types have points + if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's a group! + for group_num, group_data in pairs(obj_type_data.group) do + if group_data and group_data.groupId == gpId then -- this is the group we are looking for + if group_data.route and group_data.route.points and #group_data.route.points > 0 then + local points = {} + + for point_num, point in pairs(group_data.route.points) do + local routeData = {} + if not point.point then + routeData.x = point.x + routeData.y = point.y + else + routeData.point = point.point --it's possible that the ME could move to the point = Vec2 notation. + end + routeData.form = point.action + routeData.speed = point.speed + routeData.alt = point.alt + routeData.alt_type = point.alt_type + routeData.airdromeId = point.airdromeId + routeData.helipadId = point.helipadId + routeData.type = point.type + routeData.action = point.action + if task then + routeData.task = point.task + end + points[point_num] = routeData + end + + return points + end + return + end --if group_data and group_data.name and group_data.name == 'groupname' + end --for group_num, group_data in pairs(obj_type_data.group) do + end --if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then + end --if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then + end --for obj_type_name, obj_type_data in pairs(cntry_data) do + end --for cntry_id, cntry_data in pairs(coa_data.country) do + end --if coa_data.country then --there is a country table + end --if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then + end --for coa_name, coa_data in pairs(mission.coalition) do + end + + -- function mist.ground.buildPath() end -- ???? + + function mist.ground.patrolRoute(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 = mist.getGroupRoute(useGroupRoute) + end + else + useRoute = vars.route + local posStart = mist.getLeadPos(gpData) + useRoute[1] = mist.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 = mist.getLeadPos(gpData) + + + useRoute[#useRoute + 1] = mist.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] = mist.ground.buildWP(tempRoute[i], tempForm, tempSpeed) + end + + if pType and string.lower(pType) == 'doubleback' then + local curRoute = mist.utils.deepCopy(useRoute) + for i = #curRoute, 2, -1 do + useRoute[#useRoute + 1] = mist.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] = 'mist.ground.patrolRoute(' + cTask3[#cTask3 + 1] = mist.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 + mist.goRoute(gpData, useRoute) + + return + end + + function mist.ground.patrol(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 + + mist.ground.patrolRoute(vars) + + return + end + + -- No longer accepts path + function mist.ground.buildWP(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 = mist.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 + + function mist.fixedWing.buildWP(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 altType == 'agl' then + wp.alt_type = 'RADIO' + elseif altType == 'baro' or altType == '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 = mist.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 + + function mist.heli.buildWP(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 altType == 'agl' then + wp.alt_type = 'RADIO' + elseif altType == 'baro' or altType == '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 = mist.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 + + -- need to return a Vec3 or Vec2? + function mist.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 + + function mist.getRandomPointInZone(zoneName, innerRadius) + if type(zoneName) == 'string' and type(trigger.misc.getZone(zoneName)) == 'table' then + return mist.getRandPointInCircle(trigger.misc.getZone(zoneName).point, trigger.misc.getZone(zoneName).radius, innerRadius) + end + return false + end + + function mist.groupToRandomPoint(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 mist.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 = mist.getRandPointInCircle(point, radius, innerRadius) + + local offset = {} + local posStart = mist.getLeadPos(group) + + offset.x = mist.utils.round(math.sin(heading - (math.pi/2)) * 50 + rndCoord.x, 3) + offset.z = mist.utils.round(math.cos(heading + (math.pi/2)) * 50 + rndCoord.y, 3) + path[#path + 1] = mist.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] = mist.ground.buildWP({x = posStart.x + 11, z = posStart.z + 11}, 'off_road', speed) + path[#path + 1] = mist.ground.buildWP(posStart, 'on_road', speed) + path[#path + 1] = mist.ground.buildWP(offset, 'on_road', speed) + else + path[#path + 1] = mist.ground.buildWP({x = posStart.x + 25, z = posStart.z + 25}, form, speed) + end + + path[#path + 1] = mist.ground.buildWP(offset, form, speed) + path[#path + 1] = mist.ground.buildWP(rndCoord, form, speed) + + mist.goRoute(group, path) + + return + end + + function mist.groupRandomDistSelf(gpData, dist, form, heading, speed) + local pos = mist.getLeadPos(gpData) + local fakeZone = {} + fakeZone.radius = dist or math.random(300, 1000) + fakeZone.point = {x = pos.x, y = pos.y, z = pos.z} + mist.groupToRandomZone(gpData, fakeZone, form, heading, speed) + + return + end + + function mist.groupToRandomZone(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 = mist.utils.kmphToMps(speed) + end + + local vars = {} + vars.group = gpData + vars.radius = zone.radius + vars.form = form + vars.headingDegrees = heading + vars.speed = speed + vars.point = mist.utils.zoneToVec3(zone) + + mist.groupToRandomPoint(vars) + + return + end + + function mist.isTerrainValid(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 + + function mist.terrainHeightDiff(coord, searchSize) + local samples = {} + local searchRadius = 5 + if searchSize then + searchRadius = searchSize + end + if type(coord) == 'string' then + coord = mist.utils.zoneToVec3(coord) + end + + coord = mist.utils.makeVec2(coord) + + samples[#samples + 1] = land.getHeight(coord) + for i = 0, 360, 30 do + samples[#samples + 1] = land.getHeight({x = (coord.x + (math.sin(math.rad(i))*searchRadius)), y = (coord.y + (math.cos(math.rad(i))*searchRadius))}) + if searchRadius >= 20 then -- if search radius is sorta large, take a sample halfway between center and outer edge + samples[#samples + 1] = land.getHeight({x = (coord.x + (math.sin(math.rad(i))*(searchRadius/2))), y = (coord.y + (math.cos(math.rad(i))*(searchRadius/2)))}) + end + end + local tMax, tMin = 0, 1000000 + for index, height in pairs(samples) do + if height > tMax then + tMax = height + end + if height < tMin then + tMin = height + end + end + return mist.utils.round(tMax - tMin, 2) + end + + function mist.groupToPoint(gpData, point, form, heading, speed, useRoads) + if type(point) == 'string' then + point = trigger.misc.getZone(point) + end + if speed then + speed = mist.utils.kmphToMps(speed) + end + + local vars = {} + vars.group = gpData + vars.form = form + vars.headingDegrees = heading + vars.speed = speed + vars.disableRoads = useRoads + vars.point = mist.utils.zoneToVec3(point) + mist.groupToRandomPoint(vars) + + return + end + + function mist.getLeadPos(group) + if type(group) == 'string' then -- group name + group = Group.getByName(group) + end + + local units = group:getUnits() + + local leader = units[1] + if not Unit.isExist(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 Unit.isExist(unit) and ind < lowestInd then + lowestInd = ind + return unit:getPosition().p + end + end + end + if leader and Unit.isExist(leader) then -- maybe a little too paranoid now... + return leader:getPosition().p + end + end end @@ -6379,961 +6380,218 @@ end --- Logger class. -- @type mist.Logger do -- mist.Logger scope - mist.Logger = {} + mist.Logger = {} - --- parses text and substitutes keywords with values from given array. - -- @param text string containing keywords to substitute with values - -- or a variable. - -- @param ... variables to use for substitution in string. - -- @treturn string new string with keywords substituted or - -- value of variable as string. - local function formatText(text, ...) - if type(text) ~= 'string' then - if type(text) == 'table' then - text = mist.utils.oneLineSerialize(text) - else - text = tostring(text) - end - else - for index,value in ipairs(arg) do - -- TODO: check for getmetatabel(value).__tostring - if type(value) == 'table' then - value = mist.utils.oneLineSerialize(value) - else - value = tostring(value) - end - text = text:gsub('$' .. index, value) - end - end - local dInfo = debug.getinfo(3) - local fName = dInfo.name - -- local fsrc = dinfo.short_src - --local fLine = dInfo.linedefined - local cLine = dInfo.currentline - if fName then - return fName .. '|' .. cLine .. ': ' .. text - else - return cLine .. ': ' .. text - end - end + --- parses text and substitutes keywords with values from given array. + -- @param text string containing keywords to substitute with values + -- or a variable. + -- @param ... variables to use for substitution in string. + -- @treturn string new string with keywords substituted or + -- value of variable as string. + local function formatText(text, ...) + if type(text) ~= 'string' then + if type(text) == 'table' then + text = mist.utils.oneLineSerialize(text) + else + text = tostring(text) + end + else + for index,value in ipairs(arg) do + -- TODO: check for getmetatabel(value).__tostring + if type(value) == 'table' then + value = mist.utils.oneLineSerialize(value) + else + value = tostring(value) + end + text = text:gsub('$' .. index, value) + end + end + local dInfo = debug.getinfo(3) + local fName = dInfo.name + -- local fsrc = dinfo.short_src + --local fLine = dInfo.linedefined + local cLine = dInfo.currentline + if fName then + return fName .. '|' .. cLine .. ': ' .. text + else + return cLine .. ': ' .. text + end + end - local function splitText(text) - local tbl = {} - while text:len() > 4000 do - local sub = text:sub(1, 4000) - text = text:sub(4001) - table.insert(tbl, sub) - end - table.insert(tbl, text) - return tbl - end + local function splitText(text) + local tbl = {} + while text:len() > 4000 do + local sub = text:sub(1, 4000) + text = text:sub(4001) + table.insert(tbl, sub) + end + table.insert(tbl, text) + return tbl + end - --- Creates a new logger. - -- Each logger has it's own tag and log level. - -- @tparam string tag tag which appears at the start of - -- every log line produced by this logger. - -- @tparam[opt] number|string level the log level defines which messages - -- will be logged and which will be omitted. Log level 3 beeing the most verbose - -- and 0 disabling all output. This can also be a string. Allowed strings are: - -- "none" (0), "error" (1), "warning" (2) and "info" (3). - -- @usage myLogger = mist.Logger:new("MyScript") - -- @usage myLogger = mist.Logger:new("MyScript", 2) - -- @usage myLogger = mist.Logger:new("MyScript", "info") - -- @treturn mist.Logger - function mist.Logger:new(tag, level) - local l = {} - l.tag = tag - setmetatable(l, self) - self.__index = self - self:setLevel(level) - return l - end + --- Creates a new logger. + -- Each logger has it's own tag and log level. + -- @tparam string tag tag which appears at the start of + -- every log line produced by this logger. + -- @tparam[opt] number|string level the log level defines which messages + -- will be logged and which will be omitted. Log level 3 beeing the most verbose + -- and 0 disabling all output. This can also be a string. Allowed strings are: + -- "none" (0), "error" (1), "warning" (2) and "info" (3). + -- @usage myLogger = mist.Logger:new("MyScript") + -- @usage myLogger = mist.Logger:new("MyScript", 2) + -- @usage myLogger = mist.Logger:new("MyScript", "info") + -- @treturn mist.Logger + function mist.Logger:new(tag, level) + local l = {} + l.tag = tag + setmetatable(l, self) + self.__index = self + self:setLevel(level) + return l + end - --- Sets the level of verbosity for this logger. - -- @tparam[opt] number|string level the log level defines which messages - -- will be logged and which will be omitted. Log level 3 beeing the most verbose - -- and 0 disabling all output. This can also be a string. Allowed strings are: - -- "none" (0), "error" (1), "warning" (2) and "info" (3). - -- @usage myLogger:setLevel("info") - -- @usage -- log everything - --myLogger:setLevel(3) - function mist.Logger:setLevel(level) - if type(level) == 'string' then - if level == 'none' or level == 'off' then - self.level = 0 - elseif level == 'error' then - self.level = 1 - elseif level == 'warning' then - self.level = 2 - elseif level == 'info' then - self.level = 3 - end - elseif type(level) == 'number' then - self.level = level - end - end + --- Sets the level of verbosity for this logger. + -- @tparam[opt] number|string level the log level defines which messages + -- will be logged and which will be omitted. Log level 3 beeing the most verbose + -- and 0 disabling all output. This can also be a string. Allowed strings are: + -- "none" (0), "error" (1), "warning" (2) and "info" (3). + -- @usage myLogger:setLevel("info") + -- @usage -- log everything + --myLogger:setLevel(3) + function mist.Logger:setLevel(level) + if type(level) == 'string' then + if level == 'none' or level == 'off' then + self.level = 0 + elseif level == 'error' then + self.level = 1 + elseif level == 'warning' then + self.level = 2 + elseif level == 'info' then + self.level = 3 + end + elseif type(level) == 'number' then + self.level = level + end + end - --- Logs error and shows alert window. - -- This logs an error to the dcs.log and shows a popup window, - -- pausing the simulation. This works always even if logging is - -- disabled by setting a log level of "none" or 0. - -- @tparam string text the text with keywords to substitute. - -- @param ... variables to be used for substitution. - -- @usage myLogger:alert("Shit just hit the fan! WEEEE!!!11") - function mist.Logger:alert(text, ...) - text = formatText(text, unpack(arg)) - if text:len() > 4000 then - local texts = splitText(text) - for i = 1, #texts do - if i == 1 then - env.error(self.tag .. '|' .. texts[i], true) - else - env.error(texts[i]) - end - end - else - env.error(self.tag .. '|' .. text, true) - end - end + --- Logs error and shows alert window. + -- This logs an error to the dcs.log and shows a popup window, + -- pausing the simulation. This works always even if logging is + -- disabled by setting a log level of "none" or 0. + -- @tparam string text the text with keywords to substitute. + -- @param ... variables to be used for substitution. + -- @usage myLogger:alert("Shit just hit the fan! WEEEE!!!11") + function mist.Logger:alert(text, ...) + text = formatText(text, unpack(arg)) + if text:len() > 4000 then + local texts = splitText(text) + for i = 1, #texts do + if i == 1 then + env.error(self.tag .. '|' .. texts[i], true) + else + env.error(texts[i]) + end + end + else + env.error(self.tag .. '|' .. text, true) + end + end - --- Logs a message, disregarding the log level. - -- @tparam string text the text with keywords to substitute. - -- @param ... variables to be used for substitution. - -- @usage myLogger:msg("Always logged!") - function mist.Logger:msg(text, ...) - text = formatText(text, unpack(arg)) - if text:len() > 4000 then - local texts = splitText(text) - for i = 1, #texts do - if i == 1 then - env.info(self.tag .. '|' .. texts[i]) - else - env.info(texts[i]) - end - end - else - env.info(self.tag .. '|' .. text) - end - end + --- Logs a message, disregarding the log level. + -- @tparam string text the text with keywords to substitute. + -- @param ... variables to be used for substitution. + -- @usage myLogger:msg("Always logged!") + function mist.Logger:msg(text, ...) + text = formatText(text, unpack(arg)) + if text:len() > 4000 then + local texts = splitText(text) + for i = 1, #texts do + if i == 1 then + env.info(self.tag .. '|' .. texts[i]) + else + env.info(texts[i]) + end + end + else + env.info(self.tag .. '|' .. text) + end + end - --- Logs an error. - -- logs a message prefixed with this loggers tag to dcs.log as - -- long as at least the "error" log level (1) is set. - -- @tparam string text the text with keywords to substitute. - -- @param ... variables to be used for substitution. - -- @usage myLogger:error("Just an error!") - -- @usage myLogger:error("Foo is $1 instead of $2", foo, "bar") - function mist.Logger:error(text, ...) - if self.level >= 1 then - text = formatText(text, unpack(arg)) - if text:len() > 4000 then - local texts = splitText(text) - for i = 1, #texts do - if i == 1 then - env.error(self.tag .. '|' .. texts[i]) - else - env.error(texts[i]) - end - end - else - env.error(self.tag .. '|' .. text) - end - end - end + --- Logs an error. + -- logs a message prefixed with this loggers tag to dcs.log as + -- long as at least the "error" log level (1) is set. + -- @tparam string text the text with keywords to substitute. + -- @param ... variables to be used for substitution. + -- @usage myLogger:error("Just an error!") + -- @usage myLogger:error("Foo is $1 instead of $2", foo, "bar") + function mist.Logger:error(text, ...) + if self.level >= 1 then + text = formatText(text, unpack(arg)) + if text:len() > 4000 then + local texts = splitText(text) + for i = 1, #texts do + if i == 1 then + env.error(self.tag .. '|' .. texts[i]) + else + env.error(texts[i]) + end + end + else + env.error(self.tag .. '|' .. text) + end + end + end - --- Logs a warning. - -- logs a message prefixed with this loggers tag to dcs.log as - -- long as at least the "warning" log level (2) is set. - -- @tparam string text the text with keywords to substitute. - -- @param ... variables to be used for substitution. - -- @usage myLogger:warn("Mother warned you! Those $1 from the interwebs are $2", {"geeks", 1337}) - function mist.Logger:warn(text, ...) - if self.level >= 2 then - text = formatText(text, unpack(arg)) - if text:len() > 4000 then - local texts = splitText(text) - for i = 1, #texts do - if i == 1 then - env.warning(self.tag .. '|' .. texts[i]) - else - env.warning(texts[i]) - end - end - else - env.warning(self.tag .. '|' .. text) - end - end - end + --- Logs a warning. + -- logs a message prefixed with this loggers tag to dcs.log as + -- long as at least the "warning" log level (2) is set. + -- @tparam string text the text with keywords to substitute. + -- @param ... variables to be used for substitution. + -- @usage myLogger:warn("Mother warned you! Those $1 from the interwebs are $2", {"geeks", 1337}) + function mist.Logger:warn(text, ...) + if self.level >= 2 then + text = formatText(text, unpack(arg)) + if text:len() > 4000 then + local texts = splitText(text) + for i = 1, #texts do + if i == 1 then + env.warning(self.tag .. '|' .. texts[i]) + else + env.warning(texts[i]) + end + end + else + env.warning(self.tag .. '|' .. text) + end + end + end - --- Logs a info. - -- logs a message prefixed with this loggers tag to dcs.log as - -- long as the highest log level (3) "info" is set. - -- @tparam string text the text with keywords to substitute. - -- @param ... variables to be used for substitution. - -- @see warn - function mist.Logger:info(text, ...) - if self.level >= 3 then - text = formatText(text, unpack(arg)) - if text:len() > 4000 then - local texts = splitText(text) - for i = 1, #texts do - if i == 1 then - env.info(self.tag .. '|' .. texts[i]) - else - env.info(texts[i]) - end - end - else - env.info(self.tag .. '|' .. text) - end - end - end + --- Logs a info. + -- logs a message prefixed with this loggers tag to dcs.log as + -- long as the highest log level (3) "info" is set. + -- @tparam string text the text with keywords to substitute. + -- @param ... variables to be used for substitution. + -- @see warn + function mist.Logger:info(text, ...) + if self.level >= 3 then + text = formatText(text, unpack(arg)) + if text:len() > 4000 then + local texts = splitText(text) + for i = 1, #texts do + if i == 1 then + env.info(self.tag .. '|' .. texts[i]) + else + env.info(texts[i]) + end + end + else + env.info(self.tag .. '|' .. text) + end + end + end end -- initialize mist mist.init() - --- vim: sw=2:ts=2 - -<<<<<<< HEAD -mist.respawnInZone = function(gpName, zone, disperse, maxDisp) - if type(gpName) == 'table' and gpName:getName() then - gpName = gpName:getName() - elseif type(gpName) == 'table' and gpName[1]:getName() then - gpName = math.random(#gpName) - else - gpName = tostring(gpName) - 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 - local vars = {} - vars.gpName = gpName - vars.action = 'respawn' - vars.point = zone.point - vars.radius = zone.radius - vars.disperse = disperse - vars.maxDisp = maxDisp - return mist.teleportToPoint(vars) -end - -mist.cloneInZone = function(gpName, zone, disperse, maxDisp) - - if type(gpName) == 'table' then - gpName = gpName:getName() - else - gpName = tostring(gpName) - 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 - local vars = {} - vars.gpName = gpName - vars.action = 'clone' - vars.point = zone.point - vars.radius = zone.radius - vars.disperse = disperse - vars.maxDisp = maxDisp - return mist.teleportToPoint(vars) -end - -mist.teleportInZone = function(gpName, zone, disperse, maxDisp) -- groupName, zoneName or table of Zone Names, keepForm is a boolean - if type(gpName) == 'table' and gpName:getName() then - gpName = gpName:getName() - else - gpName = tostring(gpName) - 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 - - local vars = {} - vars.gpName = gpName - vars.action = 'tele' - vars.point = zone.point - vars.radius = zone.radius - vars.disperse = disperse - vars.maxDisp = maxDisp - return mist.teleportToPoint(vars) -end - -mist.respawnGroup = function(gpName, task) - local vars = {} - vars.gpName = gpName - vars.action = 'respawn' - if task and type(task) ~= 'number' then - vars.route = mist.getGroupRoute(gpName, 'task') - end - local newGroup = mist.teleportToPoint(vars) - if task and type(task) == 'number' then - local newRoute = mist.getGroupRoute(gpName, 'task') - mist.scheduleFunction(mist.goRoute, {newGroup, newRoute}, timer.getTime() + task) - end - return newGroup -end - -mist.cloneGroup = function(gpName, task) - local vars = {} - vars.gpName = gpName - vars.action = 'clone' - if task and type(task) ~= 'number' then - vars.route = mist.getGroupRoute(gpName, 'task') - end - local newGroup = mist.teleportToPoint(vars) - if task and type(task) == 'number' then - local newRoute = mist.getGroupRoute(gpName, 'task') - mist.scheduleFunction(mist.goRoute, {newGroup, newRoute}, timer.getTime() + task) - end - return newGroup -end - -mist.teleportGroup = function(gpName, task) - local vars = {} - vars.gpName = gpName - vars.action = 'teleport' - if task and type(task) ~= 'number' then - vars.route = mist.getGroupRoute(gpName, 'task') - end - local newGroup = mist.teleportToPoint(vars) - if task and type(task) == 'number' then - local newRoute = mist.getGroupRoute(gpName, 'task') - mist.scheduleFunction(mist.goRoute, {newGroup, newRoute}, timer.getTime() + task) - end - return newGroup -end - -mist.spawnRandomizedGroup = function(groupName, vars) -- need to debug - if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then - local gpData = mist.getGroupData(groupName) - gpData.units = mist.randomizeGroupOrder(gpData.units, vars) - gpData.route = mist.getGroupRoute(groupName, 'task') - - mist.dynAdd(gpData) - end - - return true -end - -mist.randomizeNumTable = function(vars) - local newTable = {} - - local excludeIndex = {} - local randomTable = {} - - if vars and vars.exclude and type(vars.exclude) == 'table' then - for index, data in pairs(vars.exclude) do - excludeIndex[data] = true - end - end - - local low, hi, size - - if vars.size then - size = vars.size - end - - if vars and vars.lowerLimit and type(vars.lowerLimit) == 'number' then - low = mist.utils.round(vars.lowerLimit) - else - low = 1 - end - - if vars and vars.upperLimit and type(vars.upperLimit) == 'number' then - hi = mist.utils.round(vars.upperLimit) - else - hi = size - end - - local choices = {} - -- add to exclude list and create list of what to randomize - for i = 1, size do - if not (i >= low and i <= hi) then - - excludeIndex[i] = true - end - if not excludeIndex[i] then - table.insert(choices, i) - else - newTable[i] = i - end - end - - for ind, num in pairs(choices) do - local found = false - local x = 0 - while found == false do - x = mist.random(size) -- get random number from list - local addNew = true - for index, _ in pairs(excludeIndex) do - if index == x then - addNew = false - break - end - end - if addNew == true then - excludeIndex[x] = true - found = true - end - excludeIndex[x] = true - - end - newTable[num] = x - end - --[[ - for i = 1, #newTable do - env.info(newTable[i]) - end - ]] - return newTable -end - -mist.randomizeGroupOrder = function(passedUnits, vars) - -- figure out what to exclude, and send data to other func - local units = passedUnits - - if passedUnits.units then - units = passUnits.units - end - - local exclude = {} - local excludeNum = {} - if vars and vars.excludeType and type(vars.excludeType) == 'table' then - exclude = vars.excludeType - end - - if vars and vars.excludeNum and type(vars.excludeNum) == 'table' then - excludeNum = vars.excludeNum - end - - local low, hi - - if vars and vars.lowerLimit and type(vars.lowerLimit) == 'number' then - low = mist.utils.round(vars.lowerLimit) - else - low = 1 - end - - if vars and vars.upperLimit and type(vars.upperLimit) == 'number' then - hi = mist.utils.round(vars.upperLimit) - else - hi = #units - end - - - local excludeNum = {} - for unitIndex, unitData in pairs(units) do - if unitIndex >= low and unitIndex <= hi then -- if within range - local found = false - if #exclude > 0 then - for excludeType, index in pairs(exclude) do -- check if excluded - if mist.stringMatch(excludeType, unitData.type) then -- if excluded - excludeNum[unitIndex] = unitIndex - found = true - end - end - end - else -- unitIndex is either to low, or to high: added to exclude list - excludeNum[unitIndex] = unitId - end - end - - local newGroup = {} - local newOrder = mist.randomizeNumTable({exclude = excludeNum, size = #units}) - - for unitIndex, unitData in pairs(units) do - for i = 1, #newOrder do - if newOrder[i] == unitIndex then - newGroup[i] = mist.utils.deepCopy(units[i]) -- gets all of the unit data - newGroup[i].type = mist.utils.deepCopy(unitData.type) - newGroup[i].skill = mist.utils.deepCopy(unitData.skill) - newGroup[i].unitName = mist.utils.deepCopy(unitData.unitName) - newGroup[i].unitIndex = mist.utils.deepCopy(unitData.unitIndex) -- replaces the units data with a new type - end - end - end - return newGroup -end - -mist.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 = mist.getGroupRoute(useGroupRoute) - end - else - useRoute = vars.route - local posStart = mist.getLeadPos(gpData) - useRoute[1] = mist.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 = mist.getLeadPos(gpData) - - - useRoute[#useRoute + 1] = mist.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] = mist.ground.buildWP(tempRoute[i], tempForm, tempSpeed) - end - - if pType and string.lower(pType) == 'doubleback' then - local curRoute = mist.utils.deepCopy(useRoute) - for i = #curRoute, 2, -1 do - useRoute[#useRoute + 1] = mist.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] = 'mist.ground.patrolRoute(' - cTask3[#cTask3 + 1] = mist.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 - mist.goRoute(gpData, useRoute) - - return -end - -mist.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 - - mist.ground.patrolRoute(vars) - - return -end - - -mist.random = function(firstNum, secondNum) -- no support for decimals - local lowNum, highNum - if not secondNum then - highNum = firstNum - lowNum = 1 - else - lowNum = firstNum - highNum = secondNum - end - local total = 1 - if math.abs(highNum - lowNum + 1) < 50 then -- if total values is less than 50 - total = math.modf(50/math.abs(highNum - lowNum + 1)) -- make x copies required to be above 50 - end - local choices = {} - for i = 1, total do -- iterate required number of times - for x = lowNum, highNum do -- iterate between the range - choices[#choices +1] = x -- add each entry to a table - end - end - local rtnVal = math.random(#choices) -- will now do a math.random of at least 50 choices - for i = 1, 10 do - rtnVal = math.random(#choices) -- iterate a few times for giggles - end - return choices[rtnVal] -end - - - -mist.stringMatch = function(s1, s2, bool) - local exclude = {'%-', '%(', '%)', '%_', '%[', '%]', '%.', '%#', '% ', '%{', '%}', '%$', '%%', '%?', '%+', '%^'} - if type(s1) == 'string' and type(s2) == 'string' then - for i , str in pairs(exclude) do - s1 = string.gsub(s1, str, '') - s2 = string.gsub(s2, str, '') - end - if not bool then - s1 = string.lower(s1) - s2 = string.lower(s2) - end - - if s1 == s2 then - return true - else - return false - end - else - env.info('mist.stringMatch; Either the first or second variable were not a string') - return false - end -end - -mist.matchString = mist.stringMatch -- both commands work because order out type of I - -mist.time = {} --- returns a string for specified military time --- theTime is optional --- if present current time in mil time is returned --- if number or table the time is converted into mil tim -mist.time.convertToSec = function(timeTable) - - timeInSec = 0 - if timeTable and type(timeTable) == 'number' then - timeInSec = timeTable - elseif timeTable and type(timeTable) == 'table' and (timeTable.d or timeTable.h or timeTable.m or timeTable.s) then - if timeTable.d and type(timeTable.d) == 'number' then - timeInSec = timeInSec + (timeTable.d*86400) - end - if timeTable.h and type(timeTable.h) == 'number' then - timeInSec = timeInSec + (timeTable.h*3600) - end - if timeTable.m and type(timeTable.m) == 'number' then - timeInSec = timeInSec + (timeTable.m*60) - end - if timeTable.s and type(timeTable.s) == 'number' then - timeInSec = timeInSec + timeTable.s - end - - end - return timeInSec -end - -mist.time.getDHMS = function(timeInSec) - if timeInSec and type(timeInSec) == 'number' then - local tbl = {d = 0, h = 0, m = 0, s = 0} - if timeInSec > 86400 then - while timeInSec > 86400 do - tbl.d = tbl.d + 1 - timeInSec = timeInSec - 86400 - end - end - if timeInSec > 3600 then - while timeInSec > 3600 do - tbl.h = tbl.h + 1 - timeInSec = timeInSec - 3600 - end - end - if timeInSec > 60 then - while timeInSec > 60 do - tbl.m = tbl.m + 1 - timeInSec = timeInSec - 60 - end - end - tbl.s = timeInSec - return tbl - else - env.info('mist.time.getDHMS didnt recieve number') - return - end -end - -mist.getMilString = function(theTime) - local timeInSec = 0 - if theTime then - timeInSec = mist.time.convertToSec(theTime) - else - timeInSec = mist.utils.round(timer.getAbsTime(), 0) - end - - local DHMS = mist.time.getDHMS(timeInSec) - - return tostring(string.format('%02d', DHMS.h) .. string.format('%02d',DHMS.m)) -end - -mist.getClockString = function(theTime, hour) - local timeInSec = 0 - if theTime then - timeInSec = mist.time.convertToSec(theTime) - else - timeInSec = mist.utils.round(timer.getAbsTime(), 0) - end - local DHMS = mist.time.getDHMS(timeInSec) - if hour then - if DHMS.h > 12 then - DHMS.h = DHMS.h - 12 - return tostring(string.format('%02d', DHMS.h) .. ':' .. string.format('%02d',DHMS.m) .. ':' .. string.format('%02d',DHMS.s) .. ' PM') - else - return tostring(string.format('%02d', DHMS.h) .. ':' .. string.format('%02d',DHMS.m) .. ':' .. string.format('%02d',DHMS.s) .. ' AM') - end - else - return tostring(string.format('%02d', DHMS.h) .. ':' .. string.format('%02d',DHMS.m) .. ':' .. string.format('%02d',DHMS.s)) - end -end - --- returns the date in string format --- both variables optional --- first val returns with the month as a string --- 2nd val defins if it should be written the American way or the wrong way. -mist.time.getDate = function(convert) - local cal = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} -- starts at june. 2011. 2012 is leap year, sigh. add a simple check for leap year - local date = {} - date.d = 0 - date.m = 6 - date.y = 2011 - - local start = 0 - local timeInSec = mist.utils.round(timer.getAbsTime()) - if convert and type(convert) == 'number' then - timeInSec = convert - end - - while start < timeInSec do - - if date.d == cal[date.m] then - --if y % 4 >= 0 and i == 2 then -- for leap year. DCS doesnt do leapyear, but I am keeping the code dormant because maybe with WW2 the mission year may become relevant - - --else - date.m = date.m + 1 - date.d = 0 - --end - end - if date.m == 13 then - date.m = 1 - date.y = date.y + 1 - end - date.d = date.d + 1 - start = start + 86400 - end - return date -end - -mist.time.relativeToStart = function(time) - if type(time) == 'number' then - return time - timer.getTime0() - end -end - -mist.getDateString = function(rtnType, murica, oTime) -- returns date based on time - local word = {'January', 'Feburary', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' } -- 'etc - local curTime = 0 - if oTime then - curTime = oTime - else - curTime = mist.utils.round(timer.getAbsTime()) - end - local tbl = mist.time.getDate(curTime) - - if rtnType then - if murica then - return tostring(word[tbl.m] .. ' ' .. tbl.d .. ' ' .. tbl.y) - else - return tostring(tbl.d .. ' ' .. word[tbl.m] .. ' ' .. tbl.y) - end - else - if murica then - return tostring(tbl.m .. '.' .. tbl.d .. '.' .. tbl.y) - else - return tostring(tbl.d .. '.' .. tbl.m .. '.' .. tbl.y) - end - end -end ---WIP -mist.time.milToGame = function(milString, rtnType) --converts a military time. By default returns the abosolute time that event would occur. With optional value it returns how many seconds from time of call till that time. - local curTime = mist.utils.round(timer.getAbsTime()) - local milTimeInSec = 0 - - if milString and type(milString) == 'string' and string.len(milString) >= 4 then - local hr = tonumber(string.sub(milString, 1, 2)) - local mi = tonumber(string.sub(milString, 3)) - milTimeInSec = milTimeInSec + (mi*60) + (hr*3600) - elseif milString and type(milString) == 'table' and (milString.d or milString.h or milString.m or milString.s) then - milTimeInSec = mist.time.convertToSec(milString) - end - - local startTime = timer.getTime0() - local daysOffset = 0 - if startTime > 86400 then - daysOffset = mist.utils.round(startTime/86400) - if daysOffset > 0 then - milTimeInSec = milTimeInSec *daysOffset - end - end - - if curTime > milTimeInSec then - milTimeInSec = milTimeInSec + 86400 - end - if rtnType then - milTimeInSec = milTimeInSec - startTime - end - return milTimeInSec -end - -mist.DBs.const = {} - ---[[ - ['LAND'] = 1, - ['SHALLOW_WATER'] = 2, - ['WATER'] = 3, - ['ROAD'] = 4, - ['RUNWAY'] = 5 -]] ---[[mist.DBs.const.ME_SSE_terms = { - ['ME'] = { - ['vehicle'] = {'GROUND', 'GROUND_UNIT'}, - ['plane'] = {'AIRPLANE'}, - }, - ['SSE'] = { - }, - -}]] - - -mist.DBs.const.callsigns = { -- not accessible by SSE, must use static list :-/ - ['NATO'] = { - ['rules'] = { - ['groupLimit'] = 9, - }, - ['AWACS'] = { - ['Overlord'] = 1, - ['Magic'] = 2, - ['Wizard'] = 3, - ['Focus'] = 4, - ['Darkstar'] = 5, - }, - ['TANKER'] = { - ['Texaco'] = 1, - ['Arco'] = 2, - ['Shell'] = 3, - }, - ['JTAC'] = { - ['Axeman'] = 1, - ['Darknight'] = 2, - ['Warrior'] = 3, - ['Pointer'] = 4, - ['Eyeball'] = 5, - ['Moonbeam'] = 6, - ['Whiplash'] = 7, - ['Finger'] = 8, - ['Pinpoint'] = 9, - ['Ferret'] = 10, - ['Shaba'] = 11, - ['Playboy'] = 12, - ['Hammer'] = 13, - ['Jaguar'] = 14, - ['Deathstar'] = 15, - ['Anvil'] = 16, - ['Firefly'] = 17, - ['Mantis'] = 18, - ['Badger'] = 19, - }, - ['aircraft'] = { - ['Enfield'] = 1, - ['Springfield'] = 2, - ['Uzi'] = 3, - ['Colt'] = 4, - ['Dodge'] = 5, - ['Ford'] = 6, - ['Chevy'] = 7, - ['Pontiac'] = 8, - }, - - ['unique'] = { - ['A10'] = { - ['Hawg'] = 9, - ['Boar'] = 10, - ['Pig'] = 11, - ['Tusk'] = 12, - ['rules'] = { - ['canUseAircraft'] = true, - ['appliesTo'] = { - 'A-10C', - 'A-10A', - }, - }, - }, - }, - }, - -} ---[[ scope: - { - units = {...}, -- unit names. - coa = {...}, -- coa names - countries = {...}, -- country names - CA = {...}, -- looks just like coa. - unitTypes = { red = {}, blue = {}, all = {}, Russia = {},} - } - - -scope examples: - -{ units = { 'Hawg11', 'Hawg12' }, CA = {'blue'} } - -{ countries = {'Georgia'}, unitTypes = {blue = {'A-10C', 'A-10A'}}} - -{ coa = {'all'}} - -{unitTypes = { blue = {'A-10C'}}} -]] -end -mist.main() env.info(('Mist version ' .. mist.majorVersion .. '.' .. mist.minorVersion .. '.' .. mist.build .. ' loaded.')) -======= ->>>>>>> origin/development From b8da182cb8d93379e421ae0c85dd08b93db1a410 Mon Sep 17 00:00:00 2001 From: mrSkortch Date: Mon, 18 Jan 2016 04:48:07 -0700 Subject: [PATCH 34/42] FlagFuncs changes The old flagFuncs required 'stopflag' to be all lower case. I've changed it so that the camel case spelling is also accepted. stopFlag added camelCase named versions of flagFuncs. These are functions that simply call the default functions that use '_' between words. --- mist.lua | 51 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/mist.lua b/mist.lua index 437c574..15f19f6 100644 --- a/mist.lua +++ b/mist.lua @@ -4217,7 +4217,7 @@ initial_number local type_tbl = { [{'zones', 'zone'}] = {'table', 'string'}, flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, + [{'stopflag', 'stopFlag'}] = {'number', 'string', 'nil'}, [{'req_num', 'reqnum'}] = {'number', 'nil'}, } @@ -4225,7 +4225,7 @@ initial_number assert(err, errmsg) local zones = vars.zones or vars.zone local flag = vars.flag - local stopflag = vars.stopflag or -1 + local stopflag = vars.stopflag or vars.stopFlag or -1 local req_num = vars.req_num or vars.reqnum or 1 local initial_number = vars.initial_number @@ -4282,7 +4282,7 @@ initial_number local type_tbl = { [{'zone', 'polyzone'}] = 'table', flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, + [{'stopflag', 'stopFlag'}] = {'number', 'string', 'nil'}, [{'req_num', 'reqnum'}] = {'number', 'nil'}, } @@ -4290,7 +4290,7 @@ initial_number assert(err, errmsg) local zone = vars.zone or vars.polyzone local flag = vars.flag - local stopflag = vars.stopflag or -1 + local stopflag = vars.stopflag or vars.stopFlag or -1 local req_num = vars.req_num or vars.reqnum or 1 local initial_number = vars.initial_number @@ -4335,7 +4335,7 @@ unitTableDef = table or nil [{'units', 'unit'}] = 'table', [{'zone', 'polyzone'}] = 'table', flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, + [{'stopflag', 'stopFlag'}] = {'number', 'string', 'nil'}, [{'maxalt', 'alt'}] = {'number', 'nil'}, interval = {'number', 'nil'}, [{'req_num', 'reqnum'}] = {'number', 'nil'}, @@ -4348,7 +4348,7 @@ unitTableDef = table or nil local units = vars.units or vars.unit local zone = vars.zone or vars.polyzone local flag = vars.flag - local stopflag = vars.stopflag or -1 + local stopflag = vars.stopflag or vars.stopFlag or -1 local interval = vars.interval or 1 local maxalt = vars.maxalt or vars.alt local req_num = vars.req_num or vars.reqnum or 1 @@ -4407,7 +4407,7 @@ unitTableDef = table or nil units = 'table', zones = 'table', flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, + [{'stopflag', 'stopFlag'}] = {'number', 'string', 'nil'}, [{'zone_type', 'zonetype'}] = {'string', 'nil'}, [{'req_num', 'reqnum'}] = {'number', 'nil'}, interval = {'number', 'nil'}, @@ -4420,7 +4420,7 @@ unitTableDef = table or nil local units = vars.units local zones = vars.zones local flag = vars.flag - local stopflag = vars.stopflag or -1 + local stopflag = vars.stopflag or vars.stopFlag or -1 local zone_type = vars.zone_type or vars.zonetype or 'cylinder' local req_num = vars.req_num or vars.reqnum or 1 local interval = vars.interval or 1 @@ -4472,7 +4472,7 @@ unitTableDef = table or nil [{'zone_units', 'zoneunits'}] = 'table', radius = 'number', flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, + [{'stopflag', 'stopFlag'}] = {'number', 'string', 'nil'}, [{'zone_type', 'zonetype'}] = {'string', 'nil'}, [{'req_num', 'reqnum'}] = {'number', 'nil'}, interval = {'number', 'nil'}, @@ -4487,7 +4487,7 @@ unitTableDef = table or nil local zone_units = vars.zone_units or vars.zoneunits local radius = vars.radius local flag = vars.flag - local stopflag = vars.stopflag or -1 + local stopflag = vars.stopflag or vars.stopFlag or -1 local zone_type = vars.zone_type or vars.zonetype or 'cylinder' local req_num = vars.req_num or vars.reqnum or 1 local interval = vars.interval or 1 @@ -4550,7 +4550,7 @@ toggle = boolean or nil [{'unitset2', 'units2'}] = 'table', [{'altoffset2', 'alt2'}] = 'number', flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, + [{'stopflag', 'stopFlag'}] = {'number', 'string', 'nil'}, [{'req_num', 'reqnum'}] = {'number', 'nil'}, interval = {'number', 'nil'}, radius = {'number', 'nil'}, @@ -4566,7 +4566,7 @@ toggle = boolean or nil local unitset2 = vars.unitset2 or vars.units2 local altoffset2 = vars.altoffset2 or vars.alt2 local flag = vars.flag - local stopflag = vars.stopflag or -1 + local stopflag = vars.stopflag or vars.stopFlag or -1 local interval = vars.interval or 1 local radius = vars.radius or math.huge local req_num = vars.req_num or vars.reqnum or 1 @@ -4621,7 +4621,7 @@ stopFlag local type_tbl = { [{'group', 'groupname', 'gp', 'groupName'}] = 'string', flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, + [{'stopflag', 'stopFlag'}] = {'number', 'string', 'nil'}, interval = {'number', 'nil'}, toggle = {'boolean', 'nil'}, } @@ -4631,7 +4631,7 @@ stopFlag local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname local flag = vars.flag - local stopflag = vars.stopflag or -1 + local stopflag = vars.stopflag or vars.stopFlag or -1 local interval = vars.interval or 1 local toggle = vars.toggle or nil @@ -4660,7 +4660,7 @@ stopFlag local type_tbl = { [{'group', 'groupname', 'gp', 'groupName'}] = 'string', flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, + [{'stopflag', 'stopFlag'}] = {'number', 'string', 'nil'}, interval = {'number', 'nil'}, toggle = {'boolean', 'nil'}, } @@ -4670,7 +4670,7 @@ stopFlag local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname local flag = vars.flag - local stopflag = vars.stopflag or -1 + local stopflag = vars.stopflag or vars.stopFlag or -1 local interval = vars.interval or 1 local toggle = vars.toggle or nil @@ -4699,7 +4699,7 @@ stopFlag [{'group', 'groupname', 'gp', 'groupName'}] = 'string', percent = 'number', flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, + [{'stopflag', 'stopFlag'}] = {'number', 'string', 'nil'}, interval = {'number', 'nil'}, toggle = {'boolean', 'nil'}, } @@ -4710,7 +4710,7 @@ stopFlag local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname local flag = vars.flag local percent = vars.percent - local stopflag = vars.stopflag or -1 + local stopflag = vars.stopflag or vars.stopFlag or -1 local interval = vars.interval or 1 local toggle = vars.toggle or nil @@ -4745,7 +4745,7 @@ stopFlag [{'group', 'groupname', 'gp', 'groupName'}] = 'string', percent = 'number', flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, + [{'stopflag', 'stopFlag'}] = {'number', 'string', 'nil'}, interval = {'number', 'nil'}, toggle = {'boolean', 'nil'}, } @@ -4756,7 +4756,7 @@ stopFlag local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname local flag = vars.flag local percent = vars.percent - local stopflag = vars.stopflag or -1 + local stopflag = vars.stopflag or vars.stopFlag or -1 local interval = vars.interval or 1 local toggle = vars.toggle or nil @@ -4783,6 +4783,17 @@ stopFlag mist.scheduleFunction(mist.flagFunc.group_alive_more_than, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle, percent = percent}}, timer.getTime() + interval) end end + + function mist.flagFunc.mapobjsDeadPolygon = mist.flagFunc.mapobjs_dead_polygon + function mist.flagFunc.mapobjsDeadZones = mist.flagFunc.Mapobjs_dead_zones + function mist.flagFunc.unitsInZones = mist.flagFunc.units_in_zones + function mist.flagFunc.unitsInMovingZones = mist.flagFunc.units_in_moving_zones + function mist.flagFunc.unitsInPolygon = mist.flagFunc.units_in_polygon + function mist.flagFunc.unitsLOS = mist.flagFunc.units_LOS + function mist.flagFunc.groupAlive = mist.flagFunc.group_alive + function mist.flagFunc.groupDead = mist.flagFunc.group_dead + function mist.flagFunc.groupAliveMoreThan = mist.flagFunc.group_alive_more_than + function mist.flagFunc.groupAliveLessThan = mist.flagFunc.group_alive_less_than end From f74c5b488a8785b7d36ad51ab7aa63bac90907e2 Mon Sep 17 00:00:00 2001 From: mrSkortch Date: Mon, 18 Jan 2016 05:00:53 -0700 Subject: [PATCH 35/42] Whoops Derp. That was silly of me. --- mist.lua | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/mist.lua b/mist.lua index 15f19f6..2e72536 100644 --- a/mist.lua +++ b/mist.lua @@ -4784,16 +4784,16 @@ stopFlag end end - function mist.flagFunc.mapobjsDeadPolygon = mist.flagFunc.mapobjs_dead_polygon - function mist.flagFunc.mapobjsDeadZones = mist.flagFunc.Mapobjs_dead_zones - function mist.flagFunc.unitsInZones = mist.flagFunc.units_in_zones - function mist.flagFunc.unitsInMovingZones = mist.flagFunc.units_in_moving_zones - function mist.flagFunc.unitsInPolygon = mist.flagFunc.units_in_polygon - function mist.flagFunc.unitsLOS = mist.flagFunc.units_LOS - function mist.flagFunc.groupAlive = mist.flagFunc.group_alive - function mist.flagFunc.groupDead = mist.flagFunc.group_dead - function mist.flagFunc.groupAliveMoreThan = mist.flagFunc.group_alive_more_than - function mist.flagFunc.groupAliveLessThan = mist.flagFunc.group_alive_less_than + mist.flagFunc.mapobjsDeadPolygon = mist.flagFunc.mapobjs_dead_polygon + mist.flagFunc.mapobjsDeadZones = mist.flagFunc.Mapobjs_dead_zones + mist.flagFunc.unitsInZones = mist.flagFunc.units_in_zones + mist.flagFunc.unitsInMovingZones = mist.flagFunc.units_in_moving_zones + mist.flagFunc.unitsInPolygon = mist.flagFunc.units_in_polygon + mist.flagFunc.unitsLOS = mist.flagFunc.units_LOS + mist.flagFunc.groupAlive = mist.flagFunc.group_alive + mist.flagFunc.groupDead = mist.flagFunc.group_dead + mist.flagFunc.groupAliveMoreThan = mist.flagFunc.group_alive_more_than + mist.flagFunc.groupAliveLessThan = mist.flagFunc.group_alive_less_than end From 5fdaa269fdfb315a90ee4dd5a3c648d8dcdf3135 Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Sat, 5 Mar 2016 13:24:48 +0100 Subject: [PATCH 36/42] Fix logger if debug module is sanitized. --- mist.lua | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/mist.lua b/mist.lua index 2e72536..2d8449f 100644 --- a/mist.lua +++ b/mist.lua @@ -6417,15 +6417,19 @@ do -- mist.Logger scope text = text:gsub('$' .. index, value) end end - local dInfo = debug.getinfo(3) - local fName = dInfo.name - -- local fsrc = dinfo.short_src - --local fLine = dInfo.linedefined - local cLine = dInfo.currentline - if fName then + if debug then + local dInfo = debug.getinfo(3) + local fName = dInfo.name + local cLine = dInfo.currentline + -- local fsrc = dinfo.short_src + --local fLine = dInfo.linedefined + end + if fName and cLine then return fName .. '|' .. cLine .. ': ' .. text - else + elseif cLine then return cLine .. ': ' .. text + else + return ' ' .. text end end From 3c9da5754baedcb4d3a3c994c2c9d9811c4d64d2 Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Sat, 5 Mar 2016 17:04:27 +0100 Subject: [PATCH 37/42] Replaced spaces with tabs. Removed trailing spaces. --- mist.lua | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/mist.lua b/mist.lua index 2d8449f..c40c3d6 100644 --- a/mist.lua +++ b/mist.lua @@ -1512,10 +1512,10 @@ do -- the main scope 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.]] + 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.]] function mist.tostringBR(az, dist, alt, metric) az = mist.utils.round(mist.utils.toDegree(az), 0) @@ -4783,7 +4783,7 @@ stopFlag mist.scheduleFunction(mist.flagFunc.group_alive_more_than, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle, percent = percent}}, timer.getTime() + interval) end end - + mist.flagFunc.mapobjsDeadPolygon = mist.flagFunc.mapobjs_dead_polygon mist.flagFunc.mapobjsDeadZones = mist.flagFunc.Mapobjs_dead_zones mist.flagFunc.unitsInZones = mist.flagFunc.units_in_zones @@ -4834,10 +4834,10 @@ do -- mist.msg scope local function mistdisplayV5() --[[thoughts to improve upon - event handler based activeClients table. - display messages only when there is an update - possibly co-routine it. -]] + event handler based activeClients table. + display messages only when there is an update + possibly co-routine it. + ]] end local function mistdisplayV4() @@ -5512,11 +5512,9 @@ do -- mist.demos scope end trigger.action.outText(string.format('%-25s', 'Heading: ') .. Heading .. ' degrees\n' .. string.format('%-25s', 'Roll: ') .. Roll .. ' degrees\n' .. string.format('%-25s', 'Pitch: ') .. Pitch - .. ' degrees\n' .. string.format('%-25s', 'Yaw: ') .. Yaw .. ' degrees\n' .. string.format('%-25s', 'AoA: ') .. AoA .. ' degrees\n' .. string.format('%-25s', 'AoA plus Yaw: ') .. AoAplusYaw .. ' degrees\n' .. string.format('%-25s', 'Climb Angle: ') .. - ClimbAngle .. ' degrees\n' .. string.format('%-25s', 'Absolute Velocity: ') .. absVel .. ' m/s\n' .. string.format('%-25s', 'Absolute Acceleration: ') .. unitAcc ..' m/s^2\n' - .. string.format('%-25s', 'Axial G loading: ') .. axialGs .. ' g\n' .. string.format('%-25s', 'Transverse G loading: ') .. transGs .. ' g\n' .. string.format('%-25s', 'Absolute G loading: ') .. Gs .. ' g\n' .. string.format('%-25s', 'Energy: ') .. energy .. ' J/kg\n' .. string.format('%-25s', 'dE/dt: ') .. dEdt .. - ' J/(kg*s)', 1) - + .. ' degrees\n' .. string.format('%-25s', 'Yaw: ') .. Yaw .. ' degrees\n' .. string.format('%-25s', 'AoA: ') .. AoA .. ' degrees\n' .. string.format('%-25s', 'AoA plus Yaw: ') .. AoAplusYaw .. ' degrees\n' .. string.format('%-25s', 'Climb Angle: ') .. + ClimbAngle .. ' degrees\n' .. string.format('%-25s', 'Absolute Velocity: ') .. absVel .. ' m/s\n' .. string.format('%-25s', 'Absolute Acceleration: ') .. unitAcc ..' m/s^2\n' + .. string.format('%-25s', 'Axial G loading: ') .. axialGs .. ' g\n' .. string.format('%-25s', 'Transverse G loading: ') .. transGs .. ' g\n' .. string.format('%-25s', 'Absolute G loading: ') .. Gs .. ' g\n' .. string.format('%-25s', 'Energy: ') .. energy .. ' J/kg\n' .. string.format('%-25s', 'dE/dt: ') .. dEdt ..' J/(kg*s)', 1) return unitVel, E, curTime end end From 6761878ce38b4dfe00c2f6748569c922a4838ee1 Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Wed, 9 Mar 2016 18:46:37 +0100 Subject: [PATCH 38/42] fix missing line number and func name --- mist.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mist.lua b/mist.lua index c40c3d6..da3a90a 100644 --- a/mist.lua +++ b/mist.lua @@ -6415,10 +6415,12 @@ do -- mist.Logger scope text = text:gsub('$' .. index, value) end end + local fName = nil + local cLine = nil if debug then local dInfo = debug.getinfo(3) - local fName = dInfo.name - local cLine = dInfo.currentline + fName = dInfo.name + cLine = dInfo.currentline -- local fsrc = dinfo.short_src --local fLine = dInfo.linedefined end From c2fddb436a2c06ba0558b7b045ce66009e98a381 Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Wed, 9 Mar 2016 18:55:06 +0100 Subject: [PATCH 39/42] vim modeline, fixed indention See https://www.cs.swarthmore.edu/help/vim/modelines.html --- mist.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mist.lua b/mist.lua index da3a90a..4e47e7e 100644 --- a/mist.lua +++ b/mist.lua @@ -6415,8 +6415,8 @@ do -- mist.Logger scope text = text:gsub('$' .. index, value) end end - local fName = nil - local cLine = nil + local fName = nil + local cLine = nil if debug then local dInfo = debug.getinfo(3) fName = dInfo.name @@ -6610,3 +6610,5 @@ end -- initialize mist mist.init() env.info(('Mist version ' .. mist.majorVersion .. '.' .. mist.minorVersion .. '.' .. mist.build .. ' loaded.')) + +-- vim: noet:ts=2:sw=2 From fc0467f3723bfe2e607032cc317cef197e69c83f Mon Sep 17 00:00:00 2001 From: Lukas Kropatschek Date: Thu, 10 Mar 2016 15:52:59 +0100 Subject: [PATCH 40/42] Fix updating of alive units. --- mist.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mist.lua b/mist.lua index 2e72536..8526e59 100644 --- a/mist.lua +++ b/mist.lua @@ -1023,7 +1023,7 @@ do -- the main scope if updateAliveUnitsCounter == 5 then updateAliveUnitsCounter = 0 - if not coroutines.update_alive_units then + if not coroutines.updateAliveUnits then coroutines.updateAliveUnits = coroutine.create(updateAliveUnits) end From ff506e0ec2156a5c63074414f4589cb0b8ca735e Mon Sep 17 00:00:00 2001 From: mrSkortch Date: Fri, 11 Mar 2016 02:29:23 -0700 Subject: [PATCH 41/42] Changed build number Changed the build number --- mist.lua | 2 +- rev changelog.txt | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/mist.lua b/mist.lua index 25ce652..eb017f3 100644 --- a/mist.lua +++ b/mist.lua @@ -35,7 +35,7 @@ mist = {} -- don't change these mist.majorVersion = 4 mist.minorVersion = 1 -mist.build = 63 +mist.build = 65 -- forward declaration of log shorthand local log diff --git a/rev changelog.txt b/rev changelog.txt index f4c3bbd..9aa72fb 100644 --- a/rev changelog.txt +++ b/rev changelog.txt @@ -1,3 +1,12 @@ +v65 +-Fixed update db tables not updating + +v64 +-Fixes to debug code + +v62 and v63 +-Donno + v61 - 4.1 release -Fixed locality issue with unitToWP From 34c4082938fed07e4995e90cf9e87c34569930ea Mon Sep 17 00:00:00 2001 From: mrSkortch Date: Tue, 19 Apr 2016 03:25:09 -0600 Subject: [PATCH 42/42] 4.2 Release Files --- Mist guide.pdf | Bin 1230880 -> 487658 bytes mist.lua | 30 +- mist_4_1_61.lua | 6116 ----------------------------------------- mist_4_2_66.lua | 6620 +++++++++++++++++++++++++++++++++++++++++++++ mist_doc_file.doc | Bin 529408 -> 543232 bytes rev changelog.txt | 4 + 6 files changed, 6642 insertions(+), 6128 deletions(-) delete mode 100644 mist_4_1_61.lua create mode 100644 mist_4_2_66.lua diff --git a/Mist guide.pdf b/Mist guide.pdf index b7da2abc4d41b3a5888eb1e2c0ba0e3a6c33e7dc..9f87e9e7bd520a5c8d0ec29bdf4dcebc5d15e0ce 100644 GIT binary patch literal 487658 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

xoCW3K0!?%c~mVmX`)P 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 =sPSbm4pe2Y^H?PHJx@vf>1NJcZ^W@PLyM2 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

{q)GEoVmW6 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%0A-<}shu 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�N`aTE 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

k3is5I*hF;*tenvK$#eV>#{rNfCC<#6hY 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

SlnJhH8OvWh-TUAJvSVt_SlY|dCyf?YO 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

oc(9%YJ+-I!g>y-=CCUCgiXT7F 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

f2l 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

u|gOp=(+$$ilocIa5F~eZhUK`f=SeX1WOOx5qmN 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 }tk=I$+wQD-uM9)i#w_Yu(&Gs3kq?~MgbISz@H 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

?gxFbaL2NKKRJs}wB926Q8YriBMH?tADG8L7 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 z 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 diff --git a/mist.lua b/mist.lua index eb017f3..1dfed03 100644 --- a/mist.lua +++ b/mist.lua @@ -34,8 +34,8 @@ mist = {} -- don't change these mist.majorVersion = 4 -mist.minorVersion = 1 -mist.build = 65 +mist.minorVersion = 2 +mist.build = 66 -- forward declaration of log shorthand local log @@ -6474,18 +6474,24 @@ do -- mist.Logger scope -- @usage -- log everything --myLogger:setLevel(3) function mist.Logger:setLevel(level) - if type(level) == 'string' then - if level == 'none' or level == 'off' then - self.level = 0 - elseif level == 'error' then - self.level = 1 - elseif level == 'warning' then + if not level then + self.level = 2 + else + if type(level) == 'string' then + if level == 'none' or level == 'off' then + self.level = 0 + elseif level == 'error' then + self.level = 1 + elseif level == 'warning' then + self.level = 2 + elseif level == 'info' then + self.level = 3 + end + elseif type(level) == 'number' then + self.level = level + else self.level = 2 - elseif level == 'info' then - self.level = 3 end - elseif type(level) == 'number' then - self.level = level end end diff --git a/mist_4_1_61.lua b/mist_4_1_61.lua deleted file mode 100644 index 41abdc6..0000000 --- a/mist_4_1_61.lua +++ /dev/null @@ -1,6116 +0,0 @@ ---[[ -Links: - -ED Forum Thread: http://forums.eagle.ru/showthread.php?t=98616 - -Github -Development: https://github.com/mrSkortch/MissionScriptingTools/tree/development -Official Release: https://github.com/mrSkortch/MissionScriptingTools/tree/master - -]] - ---MiST Mission Scripting Tools -mist = {} - --- don't change these -mist.majorVersion = 4 -mist.minorVersion = 1 -mist.build = 61 - --------------------------------------------------------------------------------------------------------------- --- the main area -do - local coroutines = {} - - local tempSpawnedUnits = {} -- birth events added here - local mistAddedObjects = {} -- mist.dynAdd unit data added here - local mistAddedGroups = {} -- mist.dynAdd groupdata added here - local writeGroups = {} - local lastUpdateTime = 0 - - local function update_alive_units() -- coroutine function - local lalive_units = mist.DBs.aliveUnits -- local references for faster execution - local lunits = mist.DBs.unitsByNum - local ldeepcopy = mist.utils.deepCopy - local lUnit = Unit - local lremovedAliveUnits = mist.DBs.removedAliveUnits - local updatedUnits = {} - - if #lunits > 0 then - local units_per_run = math.ceil(#lunits/20) - if units_per_run < 5 then - units_per_run = 5 - end - - for i = 1, #lunits do - if lunits[i].category ~= 'static' then -- can't get statics with Unit.getByName :( - local unit = lUnit.getByName(lunits[i].unitName) - if unit then - --print('unit named ' .. lunits[i].unitName .. ' alive!') - local pos = unit:getPosition() - local newtbl = ldeepcopy(lunits[i]) - if pos then - newtbl['pos'] = pos.p - end - newtbl['unit'] = unit - --newtbl['rt_id'] = unit.id_ - lalive_units[unit.id_] = newtbl - updatedUnits[unit.id_] = true - end - end - if i%units_per_run == 0 then - --print('yielding at: ' .. tostring(i)) - coroutine.yield() - --print('resuming at: ' .. tostring(i)) - end - end - -- All units updated, remove any "alive" units that were not updated- they are dead! - for unit_id, unit in pairs(lalive_units) do - if not updatedUnits[unit_id] then - lremovedAliveUnits[unit_id] = unit - lalive_units[unit_id] = nil - end - end - end - end - - local function dbUpdate(event) - local newTable = {} - - newTable['startTime'] = 0 - - if type(event) == 'string' then -- if name of an object. - local newObject - local newType = 'group' - if Group.getByName(event) then - newObject = Group.getByName(event) - elseif StaticObject.getByName(event) then - newObject = StaticObject.getByName(event) - newType = 'static' - -- env.info('its static') - else - env.info('WTF') - return false - end - - - - newTable.name = newObject:getName() - newTable.groupId = tonumber(newObject:getID()) - newTable.groupName = newObject:getName() - local unitOneRef - if newType == 'static' then - unitOneRef = newObject - newTable.countryId = tonumber(newObject:getCountry()) - newTable.coalitionId = tonumber(newObject:getCoalition()) - newTable.category = 'static' - else - unitOneRef = newObject:getUnits() - newTable.countryId = tonumber(unitOneRef[1]:getCountry()) - newTable.coalitionId = tonumber(unitOneRef[1]:getCoalition()) - newTable.category = tonumber(newObject:getCategory()) - end - for countryData, countryId in pairs(country.id) do - if newTable.country and string.upper(countryData) == string.upper(newTable.country) or countryId == newTable.countryId then - newTable['countryId'] = countryId - newTable['country'] = string.lower(countryData) - for coaData, coaId in pairs(coalition.side) do - if coaId == coalition.getCountryCoalition(countryId) then - newTable['coalition'] = string.lower(coaData) - end - end - end - end - for catData, catId in pairs(Unit.Category) do - if newType == 'group' and Group.getByName(newTable.groupName):isExist() then - if catId == Group.getByName(newTable.groupName):getCategory() then - newTable['category'] = string.lower(catData) - end - elseif newType == 'static' and StaticObject.getByName(newTable.groupName):isExist() then - if catId == StaticObject.getByName(newTable.groupName):getCategory() then - newTable['category'] = string.lower(catData) - end - - end - end - local gfound = false - for index, data in pairs(mistAddedGroups) do - if mist.stringMatch(data.name, newTable.groupName) == true then - gfound = true - newTable.task = data.task - newTable.modulation = data.modulation - newTable.uncontrolled = data.uncontrolled - newTable.radioSet = data.radioSet - newTable.hidden = data.hidden - newTable.startTime = data.start_time - mistAddedGroups[index] = nil - end - end - - if gfound == false then - newTable.uncontrolled = false - newTable.hidden = false - end - - newTable.units = {} - if newType == 'group' then - for unitId, unitData in pairs(unitOneRef) do - newTable.units[unitId] = {} - newTable.units[unitId].unitName = unitData:getName() - - newTable.units[unitId].x = mist.utils.round(unitData:getPosition().p.x) - newTable.units[unitId].y = mist.utils.round(unitData:getPosition().p.z) - newTable.units[unitId].point = {} - newTable.units[unitId].point.x = newTable.units[unitId].x - newTable.units[unitId].point.y = newTable.units[unitId].y - newTable.units[unitId].alt = mist.utils.round(unitData:getPosition().p.y) - newTable.units[unitId].speed = mist.vec.mag(unitData:getVelocity()) - - newTable.units[unitId].heading = mist.getHeading(unitData, true) - - newTable.units[unitId].type = unitData:getTypeName() - newTable.units[unitId].unitId = tonumber(unitData:getID()) - - - newTable.units[unitId].groupName = newTable.groupName - newTable.units[unitId].groupId = newTable.groupId - newTable.units[unitId].countryId = newTable.countryId - newTable.units[unitId].coalitionId = newTable.coalitionId - newTable.units[unitId].coalition = newTable.coalition - newTable.units[unitId].country = newTable.country - local found = false - for index, data in pairs(mistAddedObjects) do - if mist.stringMatch(data.name, newTable.units[unitId].unitName) == true then - found = true - newTable.units[unitId].livery_id = data.livery_id - newTable.units[unitId].skill = data.skill - newTable.units[unitId].alt_type = data.alt_type - newTable.units[unitId].callsign = data.callsign - newTable.units[unitId].psi = data.psi - mistAddedObjects[index] = nil - end - if found == false then - newTable.units[unitId].skill = "High" - newTable.units[unitId].alt_type = "BARO" - end - end - - end - else -- its a static - newTable.category = 'static' - newTable.units[1] = {} - newTable.units[1].unitName = newObject:getName() - newTable.units[1].category = 'static' - newTable.units[1].x = mist.utils.round(newObject:getPosition().p.x) - newTable.units[1].y = mist.utils.round(newObject:getPosition().p.z) - newTable.units[1].point = {} - newTable.units[1].point.x = newTable.units[1].x - newTable.units[1].point.y = newTable.units[1].y - newTable.units[1].alt = mist.utils.round(newObject:getPosition().p.y) - newTable.units[1].heading = mist.getHeading(newObject, true) - newTable.units[1].type = newObject:getTypeName() - newTable.units[1].unitId = tonumber(newObject:getID()) - newTable.units[1].groupName = newTable.name - newTable.units[1].groupId = newTable.groupId - newTable.units[1].countryId = newTable.countryId - newTable.units[1].country = newTable.country - newTable.units[1].coalitionId = newTable.coalitionId - newTable.units[1].coalition = newTable.coalition - if newObject:getCategory() == 6 and newObject:getCargoDisplayName() then - local mass = newObject:getCargoDisplayName() - mass = string.gsub(mass, ' ', '') - mass = string.gsub(mass, 'kg', '') - newTable.units[1].mass = tonumber(mass) - newTable.units[1].categoryStatic = 'Cargos' - newTable.units[1].canCargo = true - newTable.units[1].shape_name = 'ab-212_cargo' - end - - ----- search mist added objects for extra data if applicable - for index, data in pairs(mistAddedObjects) do - if mist.stringMatch(data.name, newTable.units[1].unitName) == true then - newTable.units[1].shape_name = data.shape_name -- for statics - newTable.units[1].livery_id = data.livery_id - newTable.units[1].airdromeId = data.airdromeId - newTable.units[1].mass = data.mass - newTable.units[1].canCargo = data.canCargo - newTable.units[1].categoryStatic = data.categoryStatic - newTable.units[1].type = 'cargo1' - mistAddedObjects[index] = nil - end - end - end - end - --mist.debug.writeData(mist.utils.serialize,{'msg', newTable}, timer.getAbsTime() ..'Group.lua') - newTable['timeAdded'] = timer.getAbsTime() -- only on the dynGroupsAdded table. For other reference, see start time - --mist.debug.dumpDBs() - --end - - return newTable - end - - - - local function checkSpawnedEvents() - if #tempSpawnedUnits > 0 then - local groupsToAdd = {} - local added = false - local ltemp = tempSpawnedUnits - local ltable = table - - local updatesPerRun = math.ceil(#tempSpawnedUnits/20) - if updatesPerRun < 5 then - updatesPerRun = 5 - end - for x = 1, #tempSpawnedUnits do - local spawnedObj = ltemp[x] - if spawnedObj and spawnedObj:isExist() then - local found = false - for name, val in pairs(groupsToAdd) do - if spawnedObj:getCategory() == 1 then -- normal groups - if mist.stringMatch(spawnedObj:getGroup():getName(), name) == true then - found = true - break - end - elseif spawnedObj:getCategory() == 3 or spawnedObj:getCategory() == 6 then -- static objects - if mist.stringMatch(spawnedObj:getName(), name) == true then - found = true - break - end - end - end - -- for some reason cargo objects are returning as category == 6. - if found == false then - added = true - if spawnedObj:getCategory() == 1 then -- normal groups - groupsToAdd[spawnedObj:getGroup():getName()] = true - elseif spawnedObj:getCategory() == 3 or spawnedObj:getCategory() == 6 then -- static objects - groupsToAdd[spawnedObj:getName()] = true - end - - end - end - - table.remove(ltemp, x) - if x%updatesPerRun == 0 then - coroutine.yield() - end - end - - if added == true then - for groupName, val in pairs(groupsToAdd) do - local dataChanged = false - if mist.DBs.groupsByName[groupName] then - for _index, data in pairs(mist.DBs.groupsByName[groupName]) do - if data.unitName ~= spawnedObj:getName() and data.unitId ~= spawnedObj:getID() and data.type ~= spawnedObj:getTypeName() then - dataChanged = true - break - end - end - if dataChanged == false then - groupsToAdd[groupName] = false - end - end - if groupsToAdd[groupName] == true or not mist.DBs.groupsByName[groupName] then - writeGroups[#writeGroups + 1] = dbUpdate(groupName) - end - end - end - end - end - - - local function updateDBTables() - - local i = 0 - for index, newTable in pairs(writeGroups) do - i = i + 1 - end - local savesPerRun = math.ceil(i/10) - if savesPerRun < 5 then - savesPerRun = 5 - end - if i > 0 then - local ldeepCopy = mist.utils.deepCopy - for x = 1, i do - local newTable = writeGroups[x] - local mistCategory - if type(newTable.category) == 'string' then - mistCategory = string.lower(newTable.category) - end - - if string.upper(newTable['category']) == 'GROUND_UNIT' then - mistCategory = 'vehicle' - newTable['category'] = mistCategory - elseif string.upper(newTable['category']) == 'AIRPLANE' then - mistCategory = 'plane' - newTable['category'] = mistCategory - elseif string.upper(newTable['category']) == 'HELICOPTER' then - mistCategory = 'helicopter' - newTable['category'] = mistCategory - elseif string.upper(newTable['category']) == 'SHIP' then - mistCategory = 'ship' - newTable['category'] = mistCategory - end - for newId, newUnitData in pairs(newTable.units) do - newUnitData.category = mistCategory - if newUnitData.unitId then - mist.DBs.unitsById[tonumber(newUnitData.unitId)] = ldeepCopy(newUnitData) - end - - mist.DBs.unitsByName[newUnitData.unitName] = ldeepCopy(newUnitData) - mist.DBs.unitsByCat[mistCategory][#mist.DBs.unitsByCat[mistCategory] + 1] = ldeepCopy(newUnitData) - mist.DBs.unitsByNum[#mist.DBs.unitsByNum + 1] = ldeepCopy(newUnitData) - end - -- this is a really annoying DB to populate. Gotta create new tables in case its missing - if not mist.DBs.units[newTable.coalition] then - mist.DBs.units[newTable.coalition] = {} - end - - if not mist.DBs.units[newTable.coalition][newTable.country] then - mist.DBs.units[newTable.coalition][(newTable.country)] = {} - mist.DBs.units[newTable.coalition][(newTable.country)]['countryId'] = newTable.countryId - end - if not mist.DBs.units[newTable.coalition][newTable.country][mistCategory] then - mist.DBs.units[newTable.coalition][(newTable.country)][mistCategory] = {} - end - mist.DBs.units[newTable.coalition][(newTable.country)][mistCategory][#mist.DBs.units[newTable.coalition][(newTable.country)][mistCategory] + 1] = ldeepCopy(newTable) - - if newTable.groupId then - mist.DBs.groupsById[newTable.groupId] = ldeepCopy(newTable) - end - - mist.DBs.groupsByName[newTable.name] = ldeepCopy(newTable) - mist.DBs.dynGroupsAdded[#mist.DBs.dynGroupsAdded + 1] = ldeepCopy(newTable) - - writeGroups[x] = nil - if x%savesPerRun == 0 then - coroutine.yield() - end - end - if timer.getTime() > lastUpdateTime then - lastUpdateTime = timer.getTime() - end - end - end - - local update_alive_units_counter = 0 - local write_DB_table_counter = 0 - local check_spawn_events_counter = 0 - - -- THE MAIN FUNCTION -- Accessed 100 times/sec. - mist.main = function() - timer.scheduleFunction(mist.main, {}, timer.getTime() + 0.01) --reschedule first in case of Lua error - ---------------------------------------------------------------------------------------------------------- - --area to add new stuff in - write_DB_table_counter = write_DB_table_counter + 1 - if write_DB_table_counter == 10 then - - write_DB_table_counter = 0 - - if not coroutines.updateDBTables then - coroutines['updateDBTables'] = coroutine.create(updateDBTables) - end - - coroutine.resume(coroutines.updateDBTables) - - if coroutine.status(coroutines.updateDBTables) == 'dead' then - coroutines.updateDBTables = nil - end - end - - check_spawn_events_counter = check_spawn_events_counter + 1 - if check_spawn_events_counter == 10 then - - check_spawn_events_counter = 0 - - if not coroutines.checkSpawnedEvents then - coroutines['checkSpawnedEvents'] = coroutine.create(checkSpawnedEvents) - end - - coroutine.resume(coroutines.checkSpawnedEvents) - - if coroutine.status(coroutines.checkSpawnedEvents) == 'dead' then - coroutines.checkSpawnedEvents = nil - end - end - - ----------------------------------------------------------------------------------------------------------- - --updating alive units - update_alive_units_counter = update_alive_units_counter + 1 - if update_alive_units_counter == 5 then - update_alive_units_counter = 0 - - if not coroutines.update_alive_units then - coroutines['update_alive_units'] = coroutine.create(update_alive_units) - end - - coroutine.resume(coroutines.update_alive_units) - - if coroutine.status(coroutines.update_alive_units) == 'dead' then - coroutines.update_alive_units = nil - end - end - - mist.do_scheduled_functions() - end -- end of mist.main - -------------------------------------------- - ------------ mist dyn add stuff for coroutines - local mistGpId = 7000 - local mistUnitId = 7000 - local mistDynAddIndex = 1 - - - mist.nextGroupId = 1 - mist.nextUnitId = 1 - - mist.getNextUnitId = function() - mist.nextUnitId = mist.nextUnitId + 1 - if mist.nextUnitId > 6900 then - mist.nextUnitId = 14000 - end - return mist.nextUnitId - end - - mist.getNextGroupId = function() - mist.nextGroupId = mist.nextGroupId + 1 - if mist.nextGroupId > 6900 then - mist.nextGroupId = 14000 - end - return mist.nextGroupId - end - - mist.getLastDBUpdateTime = function() - return lastUpdateTime - end - - local function groupSpawned(event) - if event.id == world.event.S_EVENT_BIRTH and timer.getTime0() < timer.getAbsTime()then -- dont need to add units spawned in at the start of the mission if mist is loaded in init line - table.insert(tempSpawnedUnits,(event.initiator)) - end - end - - - - - mist.dynAddStatic = function(staticObj) - local newObj = {} - newObj.groupId = staticObj.groupId - newObj.category = staticObj.category - newObj.type = staticObj.type - newObj.unitId = staticObj.unitId - newObj.y = staticObj.y - newObj.x = staticObj.x - newObj.heading = staticObj.heading - newObj.name = staticObj.name - newObj.dead = staticObj.dead - newObj.country = staticObj.country - newObj.countryId = staticObj.countryId - newObj.clone = staticObj.clone - newObj.shape_name = staticObj.shape_name - newObj.canCargo = staticObj.canCargo - newObj.mass = staticObj.mass - newObj.categoryStatic = staticObj.categoryStatic - if staticObj.units then -- if its mist format - newObj.groupId = staticObj.units[1].groupId - newObj.category = staticObj.units[1].category - newObj.type = staticObj.units[1].type - newObj.unitId = staticObj.units[1].unitId - newObj.y = staticObj.units[1].y - newObj.x = staticObj.units[1].x - newObj.heading = staticObj.units[1].heading - newObj.name = staticObj.units[1].name - newObj.dead = staticObj.units[1].dead - newObj.country = staticObj.units[1].country - newObj.countryId = staticObj.units[1].countryId - newObj.shape_name = staticObj.units[1].shape_name - newObj.canCargo = staticObj.units[1].canCargo - newObj.mass = staticObj.units[1].mass - newObj.categoryStatic = staticObj.units[1].categoryStatic - end - - if not newObj.country then - return false - end - - local newCountry = newObj.country - if newObj.countryId then - newCountry = newObj.countryId - end - for countryId, countryName in pairs(country.name) do - if type(newObj.country) == 'string' then - if tostring(countryName) == string.upper(newObj.country) then - newCountry = countryName - end - elseif type(newObj.country) == 'number' then - if countryId == newObj.country then - newCountry = countryName - end - end - end - - if newObj.clone or not newObj.groupId then - mistGpId = mistGpId + 1 - newObj.groupId = mistGpId - end - - if newObj.clone or not newObj.unitId then - mistUnitId = mistUnitId + 1 - newObj.unitId = mistUnitId - end - - if newObj.clone or not newObj.name then - mistDynAddIndex = mistDynAddIndex + 1 - newObj.name = (country.name[newCountry] .. ' static ' .. mistDynAddIndex) - end - - if not newObj.dead then - newObj.dead = false - end - - if not newObj.heading then - newObj.heading = math.random(360) - end - - if newObj.categoryStatic then - newObj.category = newObj.categoryStatic - end - if newObj.mass then - newObj.category = 'Cargos' - end - mistAddedObjects[#mistAddedObjects + 1] = mist.utils.deepCopy(newObj) - if newObj.x and newObj.y and newObj.type and type(newObj.x) == 'number' and type(newObj.y) == 'number' and type(newObj.type) == 'string' then - coalition.addStaticObject(country.id[newCountry], newObj) - - return newObj - end - return false - end - - mist.dynAdd = function(newGroup) -- same as coalition.add function in SSE. checks the passed data to see if its valid. ---Will generate groupId, groupName, unitId, and unitName if needed --- - - - - --mist.debug.writeData(mist.utils.serialize,{'msg', newGroup}, 'newGroupOrig.lua') - local cntry = newGroup.country - if newGroup.countryId then - cntry = newGroup.countryId - end - - local groupType = newGroup.category - local newCountry = '' - -- validate data - for countryId, countryName in pairs(country.name) do - if type(cntry) == 'string' then - cntry = cntry:gsub("%s+", "_") - if tostring(countryName) == string.upper(cntry) then - newCountry = countryName - end - elseif type(cntry) == 'number' then - if countryId == cntry then - newCountry = countryName - end - end - end - - if newCountry == '' then - return false - end - - local newCat = '' - for catName, catId in pairs(Unit.Category) do - if type(groupType) == 'string' then - if tostring(catName) == string.upper(groupType) then - newCat = catName - end - elseif type(groupType) == 'number' then - if catId == groupType then - newCat = catName - end - end - - if catName == 'GROUND_UNIT' and (string.upper(groupType) == 'VEHICLE' or string.upper(groupType) == 'GROUND') then - newCat = 'GROUND_UNIT' - elseif catName == 'AIRPLANE' and string.upper(groupType) == 'PLANE' then - newCat = 'AIRPLANE' - end - end - local typeName - if newCat == 'GROUND_UNIT' then - typeName = ' gnd ' - elseif newCat == 'AIRPLANE' then - typeName = ' air ' - elseif newCat == 'HELICOPTER' then - typeName = ' hel ' - elseif newCat == 'SHIP' then - typeName = ' shp ' - elseif newCat == 'BUILDING' then - typeName = ' bld ' - end - if newGroup.clone or not newGroup.groupId then - mistDynAddIndex = mistDynAddIndex + 1 - mistGpId = mistGpId + 1 - newGroup.groupId = mistGpId - end - if newGroup.groupName or newGroup.name then - if newGroup.groupName then - newGroup['name'] = newGroup.groupName - elseif newGroup.name then - newGroup['name'] = newGroup.name - end - end - - if newGroup.clone and mist.DBs.groupsByName[newGroup.name] or not newGroup.name then - newGroup['name'] = tostring(tostring(country.name[cntry]) .. tostring(typeName) .. mistDynAddIndex) - end - - if not newGroup.hidden then - newGroup.hidden = false - end - - if not newGroup.visible then - newGroup.visible = false - end - - if (newGroup.start_time and type(newGroup.start_time) ~= 'number') or not newGroup.start_time then - if newGroup.startTime then - newGroup.start_time = mist.utils.round(newGroup.startTime) - else - newGroup.start_time = 0 - end - end - - - for unitIndex, unitData in pairs(newGroup.units) do - local originalName = newGroup.units[unitIndex].unitName or newGroup.units[unitIndex].name - if newGroup.clone or not unitData.unitId then - mistUnitId = mistUnitId + 1 - newGroup.units[unitIndex]['unitId'] = mistUnitId - end - if newGroup.units[unitIndex].unitName or newGroup.units[unitIndex].name then - if newGroup.units[unitIndex].unitName then - newGroup.units[unitIndex].name = newGroup.units[unitIndex].unitName - elseif newGroup.units[unitIndex].name then - newGroup.units[unitIndex].name = newGroup.units[unitIndex].name - end - end - if newGroup.clone or not unitData.name then - newGroup.units[unitIndex].name = tostring(newGroup.name .. ' unit' .. unitIndex) - end - - if not unitData.skill then - newGroup.units[unitIndex].skill = 'Random' - end - - if not unitData.alt then - if newCat == 'AIRPLANE' then - newGroup.units[unitIndex].alt = 2000 - newGroup.units[unitIndex].alt_type = 'RADIO' - newGroup.units[unitIndex].speed = 150 - elseif newCat == 'HELICOPTER' then - newGroup.units[unitIndex].alt = 500 - newGroup.units[unitIndex].alt_type = 'RADIO' - newGroup.units[unitIndex].speed = 60 - else - --[[env.info('check height') - newGroup.units[unitIndex].alt = land.getHeight({x = newGroup.units[unitIndex].x, y = newGroup.units[unitIndex].y}) - newGroup.units[unitIndex].alt_type = 'BARO']] - end - - - end - - if newCat == 'AIRPLANE' or newCat == 'HELICOPTER' then - if newGroup.units[unitIndex].alt_type and newGroup.units[unitIndex].alt_type ~= 'BARO' or not newGroup.units[unitIndex].alt_type then - newGroup.units[unitIndex].alt_type = 'RADIO' - end - if not unitData.speed then - if newCat == 'AIRPLANE' then - newGroup.units[unitIndex].speed = 150 - elseif newCat == 'HELICOPTER' then - newGroup.units[unitIndex].speed = 60 - end - end - if not unitData.payload then - newGroup.units[unitIndex].payload = mist.getPayload(originalName) - end - end - mistAddedObjects[#mistAddedObjects + 1] = mist.utils.deepCopy(newGroup.units[unitIndex]) - end - mistAddedGroups[#mistAddedGroups + 1] = mist.utils.deepCopy(newGroup) - if newGroup.route and not newGroup.route.points then - if not newGroup.route.points and newGroup.route[1] then - local copyRoute = newGroup.route - newGroup.route = {} - newGroup.route.points = copyRoute - end - end - newGroup.country = newCountry - - - --mist.debug.writeData(mist.utils.serialize,{'msg', newGroup}, 'newGroup.lua') - - -- sanitize table - newGroup.groupName = nil - newGroup.clone = nil - newGroup.category = nil - newGroup.country = nil - - newGroup.tasks = {} - - for unitIndex, unitData in pairs(newGroup.units) do - newGroup.units[unitIndex].unitName = nil - end - - coalition.addGroup(country.id[newCountry], Unit.Category[newCat], newGroup) - - return newGroup - - end - ---------------------------------------------------------------------------------------------- ---Modified Slmod task scheduler, superior to timer.scheduleFunction - - local Tasks = {} - local task_id = 0 - --[[ mist.scheduleFunction: - int id = mist.schedule_task(f function, vars table, t number, rep number, st number) - id - integer id of this function task - f - function to run - vars - table of vars for that function - t - time to run function - rep - time between repetitions of this function (OPTIONAL) - st - time when repetitions of this function will stop automatically (OPTIONAL) - ]] - mist.scheduleFunction = function(f, vars, t, rep, st) - --verify correct types - assert(type(f) == 'function', 'variable 1, expected function, got ' .. type(f)) - assert(type(vars) == 'table' or vars == nil, 'variable 2, expected table or nil, got ' .. type(f)) - assert(type(t) == 'number', 'variable 3, expected number, got ' .. type(t)) - assert(type(rep) == 'number' or rep == nil, 'variable 4, expected number or nil, got ' .. type(rep)) - assert(type(st) == 'number' or st == nil, 'variable 5, expected number or nil, got ' .. type(st)) - if not vars then - vars = {} - end - task_id = task_id + 1 - table.insert(Tasks, {f = f, vars = vars, t = t, rep = rep, st = st, id = task_id}) - return task_id - end - - -- removes a scheduled function based on the function's id. returns true if successful, false if not successful. - mist.removeFunction = function(id) - local i = 1 - while i <= #Tasks do - if Tasks[i].id == id then - table.remove(Tasks, i) - else - i = i + 1 - end - end - end - - -------------------------------------------------------------------------------------------------------------------- - -- not intended for users to use this function. - mist.do_scheduled_functions = function() - local i = 1 - while i <= #Tasks do - if not Tasks[i].rep then -- not a repeated process - if Tasks[i].t <= timer.getTime() then - local Task = Tasks[i] -- local reference - table.remove(Tasks, i) - local err, errmsg = pcall(Task.f, unpack(Task.vars, 1, table.maxn(Task.vars))) - if not err then - env.info('mist.scheduleFunction, error in scheduled function: ' .. errmsg) - end - --Task.f(unpack(Task.vars, 1, table.maxn(Task.vars))) -- do the task, do not increment i - else - i = i + 1 - end - else - if Tasks[i].st and Tasks[i].st <= timer.getTime() then --if a stoptime was specified, and the stop time exceeded - table.remove(Tasks, i) -- stop time exceeded, do not execute, do not increment i - elseif Tasks[i].t <= timer.getTime() then - local Task = Tasks[i] -- local reference - Task.t = timer.getTime() + Task.rep --schedule next run - local err, errmsg = pcall(Task.f, unpack(Task.vars, 1, table.maxn(Task.vars))) - if not err then - env.info('mist.scheduleFunction, error in scheduled function: ' .. errmsg) - end - --Tasks[i].f(unpack(Tasks[i].vars, 1, table.maxn(Tasks[i].vars))) -- do the task - i = i + 1 - else - i = i + 1 - end - end - end - end - - - - local idNum = 0 - - --Simplified event handler - mist.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) - return handler.id - end - - mist.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 - - mist.addEventHandler(groupSpawned) --- mist.scheduleFunction(checkSpawnedEvents, {}, timer.getTime() + 5, 1) - -end ------------------------------------------------------------------------------------------------------------- - - ----------------------------------------------------------------------------------------------- --- Utils- conversion, Lua utils, etc. -mist.utils = {} - -mist.utils.toDegree = function(angle) - return angle*180/math.pi -end - -mist.utils.toRadian = function(angle) - return angle*math.pi/180 -end - -mist.utils.metersToNM = function(meters) - return meters/1852 -end - -mist.utils.metersToFeet = function(meters) - return meters/0.3048 -end - -mist.utils.NMToMeters = function(NM) - return NM*1852 -end - -mist.utils.feetToMeters = function(feet) - return feet*0.3048 -end - -mist.utils.mpsToKnots = function(mps) - return mps*3600/1852 -end - -mist.utils.mpsToKmph = function(mps) - return mps*3.6 -end - -mist.utils.knotsToMps = function(knots) - return knots*1852/3600 -end - -mist.utils.kmphToMps = function(kmph) - return kmph/3.6 -end - -function mist.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 mist.utils.makeVec3(Vec2, y) - if not Vec2.z then - if Vec2.alt and not y then - y = Vec2.alt - elseif 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 mist.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 - -mist.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 mist.utils.getDir(vec, point) - local dir = math.atan2(vec.z, vec.x) - if point then - dir = dir + mist.getNorthCorrection(point) - end - 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 mist.utils.get2DDist(point1, point2) - point1 = mist.utils.makeVec3(point1) - point2 = mist.utils.makeVec3(point2) - return mist.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 mist.utils.get3DDist(point1, point2) - return mist.vec.mag({x = point1.x - point2.x, y = point1.y - point2.y, z = point1.z - point2.z}) -end - -function mist.utils.vecToWP(vec) - local newWP = {} - newWP.x = vec.x - newWP.y = vec.y - if vec.z then - newWP.alt = vec.y - newWP.y = vec.z - else - newWP.alt = land.getHeight({x = vec.x, y = vec.y}) - end - return newWP -end - -function mist.utils.unitToWP(pUnit) - local unit = mist.utils.deepCopy(pUnit) - if type(unit) == 'string' then - if Unit.getByName(unit) then - unit = Unit.getByName(unit) - end - end - if unit:isExist() == true then - local new = mist.utils.vecToWP(unit:getPosition().p) - new.speed = mist.vec.mag(unit:getVelocity()) - new.alt_type = "BARO" - - return new - end - return false -end - - - ---from http://lua-users.org/wiki/CopyTable -mist.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 - return _copy(object) -end - --- From http://lua-users.org/wiki/SimpleRound --- use negative idp for rounding ahead of decimal place, positive for rounding after decimal place -mist.utils.round = function(num, idp) - local mult = 10^(idp or 0) - return math.floor(num * mult + 0.5) / mult -end - -mist.utils.roundTbl = function(tbl, idp) - for id, val in pairs(tbl) do - if type(val) == 'number' then - tbl[id] = mist.utils.round(val, idp) - end - end - return tbl -end - --- porting in Slmod's dostring -mist.utils.dostring = function(s) - local f, err = loadstring(s) - if f then - return true, f() - else - return false, err - end -end - - ---[[ mist.utils.typeCheck(fname, type_tbl, var_tbl) -Type-checking function: -Checks a var_tbl to a type_tbl. Returns true if the var_tbl passes the type check, returns false plus an error message if the var_tbl fails. - -type_tbl examples: -type_tbl = { {'table', 'number'}, 'string', 'number', 'number', {'string', 'nil'}, {'number', 'nil'} } -Compare to a var_tbl with up to six entries; var_tbl index 1 must be a table or a number; index 2, a string; index 3, a number; -index 4, a number; index 5, either a string or nil; and index 6, either a number or nil. - -Another example: -type_tbl = { {'text', 'msg', 'text_out'} = 'string', display_time = 'number', display_mode = {'string', 'nil'} coa = {'string', 'nil'}} - -var_tbl must have a string at one of the following table keys: "text", "msg", or "text_out". var_tbl must have a number at table key "display_time", -the table key "display_mode" must be either a string or nil, and the table key "coa" must be either a string or nil. -]] -function mist.utils.typeCheck(fname, type_tbl, var_tbl) - --env.info('type check') - for type_key, type_val in pairs(type_tbl) do - --print('type_key:') - --print(type_key) - --print('type_val:') - --print(type_val) - - --type_key can be a table of accepted keys- so try to find one that is not nil - local type_key_str = '' - local act_key = type_key -- actual key within var_tbl - necessary to use for multiple possible key variables. Initialize to type_key - if type(type_key) == 'table' then - - for i = 1, #type_key do - if i ~= 1 then - type_key_str = type_key_str .. '/' - end - type_key_str = type_key_str .. tostring(type_key[i]) - if var_tbl[type_key[i]] ~= nil then - act_key = type_key[i] -- found a non-nil entry, make act_key now this val. - end - end - else - type_key_str = tostring(type_key) - end - - local err_msg = 'Error in function ' .. fname .. ', parameter "' .. type_key_str .. '", expected: ' - local passed_check = false - - if type(type_tbl[type_key]) == 'table' then - --print('err_msg, before and after:') - --print(err_msg) - for j = 1, #type_tbl[type_key] do - - if j == 1 then - err_msg = err_msg .. type_tbl[type_key][j] - else - err_msg = err_msg .. ' or ' .. type_tbl[type_key][j] - end - - if type(var_tbl[act_key]) == type_tbl[type_key][j] then - passed_check = true - end - end - --print(err_msg) - else - --print('err_msg, before and after:') - --print(err_msg) - err_msg = err_msg .. type_tbl[type_key] - --print(err_msg) - if type(var_tbl[act_key]) == type_tbl[type_key] then - passed_check = true - - end - - end - - if not passed_check then - err_msg = err_msg .. ', got ' .. type(var_tbl[act_key]) - return false, err_msg - end - end - return true -end - ---porting in Slmod's "safestring" basic serialize -mist.utils.basicSerialize = function(s) - if s == nil then - return "\"\"" - else - if ((type(s) == 'number') or (type(s) == 'boolean') or (type(s) == 'function') or (type(s) == 'table') or (type(s) == 'userdata') ) then - return tostring(s) - elseif type(s) == 'string' then - s = string.format('%q', s) - return s - end - end -end - ---porting in Slmod's serialize_slmod -mist.utils.serialize = function(name, value, level) - -----Based on ED's serialize_simple2 - local basicSerialize = function (o) - if type(o) == "number" then - return tostring(o) - elseif type(o) == "boolean" then - return tostring(o) - else -- assume it is a string - return mist.utils.basicSerialize(o) - end - end - - local serialize_to_t = function (name, value, level) - ----Based on ED's serialize_simple2 - - - local var_str_tbl = {} - if level == nil then level = "" end - if level ~= "" then level = level.." " end - - table.insert(var_str_tbl, level .. name .. " = ") - - if type(value) == "number" or type(value) == "string" or type(value) == "boolean" then - table.insert(var_str_tbl, basicSerialize(value) .. ",\n") - elseif type(value) == "table" then - table.insert(var_str_tbl, "\n"..level.."{\n") - - for k,v in pairs(value) do -- serialize its fields - local key - if type(k) == "number" then - key = string.format("[%s]", k) - else - key = string.format("[%q]", k) - end - - table.insert(var_str_tbl, mist.utils.serialize(key, v, level.." ")) - - end - if level == "" then - table.insert(var_str_tbl, level.."} -- end of "..name.."\n") - - else - table.insert(var_str_tbl, level.."}, -- end of "..name.."\n") - - end - else - env.info("Cannot serialize a "..type(value)) - end - return var_str_tbl - end - - local t_str = serialize_to_t(name, value, level) - - return table.concat(t_str) -end - --- porting in slmod's serialize_wcycles -mist.utils.serializeWithCycles = function(name, value, saved) - --mostly straight out of Programming in Lua - local basicSerialize = function (o) - if type(o) == "number" then - return tostring(o) - elseif type(o) == "boolean" then - return tostring(o) - else -- assume it is a string - return mist.utils.basicSerialize(o) - end - end - - local t_str = {} - saved = saved or {} -- initial value - if ((type(value) == 'string') or (type(value) == 'number') or (type(value) == 'table') or (type(value) == 'boolean')) then - table.insert(t_str, name .. " = ") - if type(value) == "number" or type(value) == "string" or type(value) == "boolean" then - table.insert(t_str, basicSerialize(value) .. "\n") - else - - if saved[value] then -- value already saved? - table.insert(t_str, saved[value] .. "\n") - else - saved[value] = name -- save name for next time - table.insert(t_str, "{}\n") - for k,v in pairs(value) do -- save its fields - local fieldname = string.format("%s[%s]", name, basicSerialize(k)) - table.insert(t_str, mist.utils.serializeWithCycles(fieldname, v, saved)) - end - end - end - return table.concat(t_str) - else - return "" - end -end - --- porting in Slmod's serialize_slmod2 -mist.utils.oneLineSerialize = function(tbl) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function - if type(tbl) == 'table' then --function only works for tables! - - local tbl_str = {} - - tbl_str[#tbl_str + 1] = '{ ' - - for ind,val in pairs(tbl) do -- serialize its fields - if type(ind) == "number" then - tbl_str[#tbl_str + 1] = '[' - tbl_str[#tbl_str + 1] = tostring(ind) - tbl_str[#tbl_str + 1] = '] = ' - else --must be a string - tbl_str[#tbl_str + 1] = '[' - tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(ind) - tbl_str[#tbl_str + 1] = '] = ' - end - - if ((type(val) == 'number') or (type(val) == 'boolean')) then - tbl_str[#tbl_str + 1] = tostring(val) - tbl_str[#tbl_str + 1] = ', ' - elseif type(val) == 'string' then - tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(val) - tbl_str[#tbl_str + 1] = ', ' - elseif type(val) == 'nil' then -- won't ever happen, right? - tbl_str[#tbl_str + 1] = 'nil, ' - elseif type(val) == 'table' then - tbl_str[#tbl_str + 1] = mist.utils.oneLineSerialize(val) - tbl_str[#tbl_str + 1] = ', ' --I think this is right, I just added it - else - env.info('unable to serialize value type ' .. mist.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind)) - end - - end - tbl_str[#tbl_str + 1] = '}' - return table.concat(tbl_str) - end -end - ---Function to create string for viewing the contents of a table -NOT for serialization -mist.utils.tableShow = function(tbl, loc, indent, tableshow_tbls) --based on serialize_slmod, this is a _G serialization - tableshow_tbls = tableshow_tbls or {} --create table of tables - loc = loc or "" - indent = indent or "" - if type(tbl) == 'table' then --function only works for tables! - tableshow_tbls[tbl] = loc - - local tbl_str = {} - - tbl_str[#tbl_str + 1] = indent .. '{\n' - - for ind,val in pairs(tbl) do -- serialize its fields - if type(ind) == "number" then - tbl_str[#tbl_str + 1] = indent - tbl_str[#tbl_str + 1] = loc .. '[' - tbl_str[#tbl_str + 1] = tostring(ind) - tbl_str[#tbl_str + 1] = '] = ' - else - tbl_str[#tbl_str + 1] = indent - tbl_str[#tbl_str + 1] = loc .. '[' - tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(ind) - tbl_str[#tbl_str + 1] = '] = ' - end - - if ((type(val) == 'number') or (type(val) == 'boolean')) then - tbl_str[#tbl_str + 1] = tostring(val) - tbl_str[#tbl_str + 1] = ',\n' - elseif type(val) == 'string' then - tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(val) - tbl_str[#tbl_str + 1] = ',\n' - elseif type(val) == 'nil' then -- won't ever happen, right? - tbl_str[#tbl_str + 1] = 'nil,\n' - elseif type(val) == 'table' then - if tableshow_tbls[val] then - tbl_str[#tbl_str + 1] = tostring(val) .. ' already defined: ' .. tableshow_tbls[val] .. ',\n' - else - tableshow_tbls[val] = loc .. '[' .. mist.utils.basicSerialize(ind) .. ']' - tbl_str[#tbl_str + 1] = tostring(val) .. ' ' - tbl_str[#tbl_str + 1] = mist.utils.tableShow(val, loc .. '[' .. mist.utils.basicSerialize(ind).. ']', indent .. ' ', tableshow_tbls) - tbl_str[#tbl_str + 1] = ',\n' - end - elseif type(val) == 'function' then - if debug and debug.getinfo then - local fcnname = tostring(val) - local info = debug.getinfo(val, "S") - if info.what == "C" then - tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', C function') .. ',\n' - else - if (string.sub(info.source, 1, 2) == [[./]]) then - tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', defined in (' .. info.linedefined .. '-' .. info.lastlinedefined .. ')' .. info.source) ..',\n' - else - tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', defined in (' .. info.linedefined .. '-' .. info.lastlinedefined .. ')') ..',\n' - end - end - - else - tbl_str[#tbl_str + 1] = 'a function,\n' - end - else - tbl_str[#tbl_str + 1] = 'unable to serialize value type ' .. mist.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind) - end - end - - tbl_str[#tbl_str + 1] = indent .. '}' - return table.concat(tbl_str) - end -end - -mist.debug = {} - -mist.debug.dump_G = function(fname) - if lfs and io then - local fdir = lfs.writedir() .. [[Logs\]] .. fname - local f = io.open(fdir, 'w') - f:write(mist.utils.tableShow(_G)) - f:close() - local errmsg = 'mist.debug.dump_G wrote data to ' .. fdir - env.info(errmsg) - trigger.action.outText(errmsg, 10) - else - local errmsg = 'Error: insufficient libraries to run mist.debug.dump_G, you must disable the sanitization of the io and lfs libraries in ./Scripts/MissionScripting.lua' - env.info(errmsg) - trigger.action.outText(errmsg, 10) - end -end - -mist.debug.writeData = function(fcn, fcnVars, fname) - if lfs and io then - local fdir = lfs.writedir() .. [[Logs\]] .. fname - local f = io.open(fdir, 'w') - f:write(fcn(unpack(fcnVars, 1, table.maxn(fcnVars)))) - f:close() - local errmsg = 'mist.debug.writeData wrote data to ' .. fdir - env.info(errmsg) - trigger.action.outText(errmsg, 10) - else - local errmsg = 'Error: insufficient libraries to run mist.debug.writeData, you must disable the sanitization of the io and lfs libraries in ./Scripts/MissionScripting.lua' - env.info(errmsg) - trigger.action.outText(errmsg, 10) - end -end - -mist.debug.dumpDBs = function() - for DBname, DB in pairs(mist.DBs) do - if type(DB) == 'table' and type(DBname) == 'string' then - mist.debug.writeData(mist.utils.serialize, {DBname, DB}, 'mist_DBs_' .. DBname .. '.lua') - end - end -end - ------------------------------------------------------------------------------------------------------------------ ---3D Vector manipulation -mist.vec = {} - -mist.vec.add = function(vec1, vec2) - return {x = vec1.x + vec2.x, y = vec1.y + vec2.y, z = vec1.z + vec2.z} -end - -mist.vec.sub = function(vec1, vec2) - return {x = vec1.x - vec2.x, y = vec1.y - vec2.y, z = vec1.z - vec2.z} -end - -mist.vec.scalarMult = function(vec, mult) - return {x = vec.x*mult, y = vec.y*mult, z = vec.z*mult} -end - -mist.vec.scalar_mult = mist.vec.scalarMult - -mist.vec.dp = function(vec1, vec2) - return vec1.x*vec2.x + vec1.y*vec2.y + vec1.z*vec2.z -end - -mist.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 - -mist.vec.mag = function(vec) - return (vec.x^2 + vec.y^2 + vec.z^2)^0.5 -end - -mist.vec.getUnitVec = function(vec) - local mag = mist.vec.mag(vec) - return { x = vec.x/mag, y = vec.y/mag, z = vec.z/mag } -end - -mist.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. -mist.tostringMGRS = function(MGRS, acc) - if acc == 0 then - return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph - else - return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph .. ' ' .. string.format('%0' .. acc .. 'd', mist.utils.round(MGRS.Easting/(10^(5-acc)), 0)) - .. ' ' .. string.format('%0' .. acc .. 'd', mist.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. -]] -mist.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 = mist.utils.round((oldLatMin - latMin)*60, acc) - - local oldLonMin = lonMin - lonMin = math.floor(lonMin) - local lonSec = mist.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 = mist.utils.round(latMin, acc) - lonMin = mist.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.]] -mist.tostringBR = function(az, dist, alt, metric) - az = mist.utils.round(mist.utils.toDegree(az), 0) - - if metric then - dist = mist.utils.round(dist/1000, 0) - else - dist = mist.utils.round(mist.utils.metersToNM(dist), 0) - end - - local s = string.format('%03d', az) .. ' for ' .. dist - - if alt then - if metric then - s = s .. ' at ' .. mist.utils.round(alt, 0) - else - s = s .. ' at ' .. mist.utils.round(mist.utils.metersToFeet(alt), 0) - end - end - return s -end - -mist.getNorthCorrection = function(gPoint) --gets the correction needed for true north - local point = mist.utils.deepCopy(gPoint) - 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 - -mist.getUnitSkill = function(unitName) - if Unit.getByName(unitName) and Unit.getByName(unitName):isExist() == true then - local lunit = Unit.getByName(unitName) - for name, data in pairs(mist.DBs.unitsByName) do - if name == unitName and data.type == lunit:getTypeName() and data.unitId == lunit:getID() and data.skill then - return data.skill - end - end - end - return false -end - - -function mist.getGroupPoints(groupIdent) -- if groupname exists in env.mission, then returns table of the group's points in numerical order, such as: { [1] = {x = 299435.224, y = -1146632.6773}, [2] = { x = 663324.6563, y = 322424.1112}} - -- 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 = mist.DBs.MEgroupsByName[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 - if not point.point then - points[point_num] = { x = point.x, y = point.y } - else - points[point_num] = point.point --it's possible that the ME could move to the point = Vec2 notation. - end - 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 - - - ---[[ table attitude = getAttitude(string unitname) -- will work on any unit, even if not an aircraft. - -attitude = { - Heading = number, -- in radians, range of 0 to 2*pi, relative to true north - Pitch = number, -- in radians, range of -pi/2 to pi/2 - Roll = number, -- in radians, range of 0 to 2*pi, right roll is positive direction - - --Yaw, AoA, ClimbAngle - relative to earth reference- DOES NOT TAKE INTO ACCOUNT WIND. - Yaw = number, -- in radians, range of -pi to pi, right yaw is positive direction - AoA = number, -- in radians, range of -pi to pi, rotation of aircraft to the right in comparison to flight direction being positive - ClimbAngle = number, -- in radians, range of -pi/2 to pi/2 - - --Maybe later? - AxialVel = table, velocity of the aircraft transformed into directions of aircraft axes - Speed = number -- absolute velocity in meters/sec - - } - -]] -function mist.getAttitude(unit) - local unitpos = unit:getPosition() - if unitpos then - - local Heading = math.atan2(unitpos.x.z, unitpos.x.x) - - Heading = Heading + mist.getNorthCorrection(unitpos.p) - - if Heading < 0 then - Heading = Heading + 2*math.pi -- put heading in range of 0 to 2*pi - end - ---- heading complete.---- - - local Pitch = math.asin(unitpos.x.y) - ---- pitch complete.---- - - -- now get roll: - --maybe not the best way to do it, but it works. - - --first, make a vector that is perpendicular to y and unitpos.x with cross product - local cp = mist.vec.cp(unitpos.x, {x = 0, y = 1, z = 0}) - - --now, get dot product of of this cross product with unitpos.z - local dp = mist.vec.dp(cp, unitpos.z) - - --now get the magnitude of the roll (magnitude of the angle between two vectors is acos(vec1.vec2/|vec1||vec2|) - local Roll = math.acos(dp/(mist.vec.mag(cp)*mist.vec.mag(unitpos.z))) - - --now, have to get sign of roll. - -- by convention, making right roll positive - -- to get sign of roll, use the y component of unitpos.z. For right roll, y component is negative. - - if unitpos.z.y > 0 then -- left roll, flip the sign of the roll - Roll = -Roll - end - ---- roll complete. ---- - - --now, work on yaw, AoA, climb, and abs velocity - local Yaw - local AoA - local ClimbAngle - - -- get unit velocity - local unitvel = unit:getVelocity() - if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! - local AxialVel = {} --unit velocity transformed into aircraft axes directions - - --transform velocity components in direction of aircraft axes. - AxialVel.x = mist.vec.dp(unitpos.x, unitvel) - AxialVel.y = mist.vec.dp(unitpos.y, unitvel) - AxialVel.z = mist.vec.dp(unitpos.z, unitvel) - - --Yaw is the angle between unitpos.x and the x and z velocities - --define right yaw as positive - Yaw = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = 0, z = AxialVel.z})/mist.vec.mag({x = AxialVel.x, y = 0, z = AxialVel.z})) - - --now set correct direction: - if AxialVel.z > 0 then - Yaw = -Yaw - end - - -- AoA is angle between unitpos.x and the x and y velocities - AoA = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = AxialVel.y, z = 0})/mist.vec.mag({x = AxialVel.x, y = AxialVel.y, z = 0})) - - --now set correct direction: - if AxialVel.y > 0 then - AoA = -AoA - end - - ClimbAngle = math.asin(unitvel.y/mist.vec.mag(unitvel)) - end - return { Heading = Heading, Pitch = Pitch, Roll = Roll, Yaw = Yaw, AoA = AoA, ClimbAngle = ClimbAngle} - else - env.info('unit:getPosition() is nil!') - end -end - -function mist.getHeading(unit, rawHeading) - local unitpos = unit:getPosition() - if unitpos then - local Heading = math.atan2(unitpos.x.z, unitpos.x.x) - if not rawHeading then - Heading = Heading + mist.getNorthCorrection(unitpos.p) - end - if Heading < 0 then - Heading = Heading + 2*math.pi -- put heading in range of 0 to 2*pi - end - return Heading - end -end - -function mist.getPitch(unit) - local unitpos = unit:getPosition() - if unitpos then - return math.asin(unitpos.x.y) - end -end - -function mist.getRoll(unit) - local unitpos = unit:getPosition() - if unitpos then - -- now get roll: - --maybe not the best way to do it, but it works. - - --first, make a vector that is perpendicular to y and unitpos.x with cross product - local cp = mist.vec.cp(unitpos.x, {x = 0, y = 1, z = 0}) - - --now, get dot product of of this cross product with unitpos.z - local dp = mist.vec.dp(cp, unitpos.z) - - --now get the magnitude of the roll (magnitude of the angle between two vectors is acos(vec1.vec2/|vec1||vec2|) - local Roll = math.acos(dp/(mist.vec.mag(cp)*mist.vec.mag(unitpos.z))) - - --now, have to get sign of roll. - -- by convention, making right roll positive - -- to get sign of roll, use the y component of unitpos.z. For right roll, y component is negative. - - if unitpos.z.y > 0 then -- left roll, flip the sign of the roll - Roll = -Roll - end - return Roll - end -end - -function mist.getYaw(unit) - local unitpos = unit:getPosition() - if unitpos then - -- get unit velocity - local unitvel = unit:getVelocity() - if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! - local AxialVel = {} --unit velocity transformed into aircraft axes directions - - --transform velocity components in direction of aircraft axes. - AxialVel.x = mist.vec.dp(unitpos.x, unitvel) - AxialVel.y = mist.vec.dp(unitpos.y, unitvel) - AxialVel.z = mist.vec.dp(unitpos.z, unitvel) - - --Yaw is the angle between unitpos.x and the x and z velocities - --define right yaw as positive - local Yaw = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = 0, z = AxialVel.z})/mist.vec.mag({x = AxialVel.x, y = 0, z = AxialVel.z})) - - --now set correct direction: - if AxialVel.z > 0 then - Yaw = -Yaw - end - return Yaw - end - end -end - -function mist.getAoA(unit) - local unitpos = unit:getPosition() - if unitpos then - local unitvel = unit:getVelocity() - if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! - local AxialVel = {} --unit velocity transformed into aircraft axes directions - - --transform velocity components in direction of aircraft axes. - AxialVel.x = mist.vec.dp(unitpos.x, unitvel) - AxialVel.y = mist.vec.dp(unitpos.y, unitvel) - AxialVel.z = mist.vec.dp(unitpos.z, unitvel) - - -- AoA is angle between unitpos.x and the x and y velocities - local AoA = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = AxialVel.y, z = 0})/mist.vec.mag({x = AxialVel.x, y = AxialVel.y, z = 0})) - - --now set correct direction: - if AxialVel.y > 0 then - AoA = -AoA - end - return AoA - end - end -end - -function mist.getClimbAngle(unit) - local unitpos = unit:getPosition() - if unitpos then - local unitvel = unit:getVelocity() - if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! - return math.asin(unitvel.y/mist.vec.mag(unitvel)) - end - end -end ------------------------------------------------------------------------------------------------------------ --- Database building -mist.DBs = {} - -mist.DBs.missionData = {} ------------------------------------------ -if env.mission then - - mist.DBs.missionData['startTime'] = env.mission.start_time - mist.DBs.missionData['theatre'] = env.mission.theatre - mist.DBs.missionData['version'] = env.mission.version - mist.DBs.missionData['files'] = {} - if type(env.mission.resourceCounter) == 'table' then - for fIndex, fData in pairs (env.mission.resourceCounter) do - mist.DBs.missionData.files[#mist.DBs.missionData.files + 1] = mist.utils.deepCopy(fIndex) - end - end - mist.DBs.missionData['bullseye'] = {['red'] = {}, ['blue'] = {}} -- if we add more coalition specific data then bullsye should be categorized by coaliton. For now its just the bullseye table - mist.DBs.missionData.bullseye.red['x'] = env.mission.coalition.red.bullseye.x --should it be point.x? - mist.DBs.missionData.bullseye.red['y'] = env.mission.coalition.red.bullseye.y - mist.DBs.missionData.bullseye.blue['x'] = env.mission.coalition.blue.bullseye.x - mist.DBs.missionData.bullseye.blue['y'] = env.mission.coalition.blue.bullseye.y - -end - ----------------------------------------- - -mist.DBs.zonesByName = {} -mist.DBs.zonesByNum = {} - - -if env.mission.triggers and env.mission.triggers.zones then - for zone_ind, zone_data in pairs(env.mission.triggers.zones) do - if type(zone_data) == 'table' then - local zone = mist.utils.deepCopy(zone_data) - zone['point'] = {} -- point is used by SSE - zone['point']['x'] = zone_data.x - zone['point']['y'] = 0 - zone['point']['z'] = zone_data.y - - mist.DBs.zonesByName[zone_data.name] = zone - mist.DBs.zonesByNum[#mist.DBs.zonesByNum + 1] = mist.utils.deepCopy(zone) --[[deepcopy so that the zone in zones_by_name and the zone in - zones_by_num se are different objects.. don't want them linked.]] - end - end -end - -mist.DBs.navPoints = {} -mist.DBs.units = {} - --Build mist.db.units and mist.DBs.navPoints -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 - mist.DBs.units[coa_name] = {} - - ---------------------------------------------- - -- build nav points DB - mist.DBs.navPoints[coa_name] = {} - if coa_data.nav_points then --navpoints - --mist.debug.writeData (mist.utils.serialize,{'NavPoints',coa_data.nav_points}, 'NavPoints.txt') - for nav_ind, nav_data in pairs(coa_data.nav_points) do - - if type(nav_data) == 'table' then - mist.DBs.navPoints[coa_name][nav_ind] = mist.utils.deepCopy(nav_data) - - mist.DBs.navPoints[coa_name][nav_ind]['name'] = nav_data.callsignStr -- name is a little bit more self-explanatory. - mist.DBs.navPoints[coa_name][nav_ind]['point'] = {} -- point is used by SSE, support it. - mist.DBs.navPoints[coa_name][nav_ind]['point']['x'] = nav_data.x - mist.DBs.navPoints[coa_name][nav_ind]['point']['y'] = 0 - mist.DBs.navPoints[coa_name][nav_ind]['point']['z'] = nav_data.y - end - end - end - ------------------------------------------------- - if coa_data.country then --there is a country table - for cntry_id, cntry_data in pairs(coa_data.country) do - - local countryName = string.lower(cntry_data.name) - mist.DBs.units[coa_name][countryName] = {} - mist.DBs.units[coa_name][countryName]["countryId"] = cntry_data.id - - if type(cntry_data) == 'table' then --just making sure - - 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" or obj_type_name == "static" then --should be an unncessary check - - local category = obj_type_name - - 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! - - mist.DBs.units[coa_name][countryName][category] = {} - - for group_num, group_data in pairs(obj_type_data.group) do - - if group_data and group_data.units and type(group_data.units) == 'table' then --making sure again- this is a valid group - - mist.DBs.units[coa_name][countryName][category][group_num] = {} - local groupName = group_data.name - if env.mission.version > 7 then - groupName = env.getValueDictByKey(groupName) - end - mist.DBs.units[coa_name][countryName][category][group_num]["groupName"] = groupName - mist.DBs.units[coa_name][countryName][category][group_num]["groupId"] = group_data.groupId - mist.DBs.units[coa_name][countryName][category][group_num]["category"] = category - mist.DBs.units[coa_name][countryName][category][group_num]["coalition"] = coa_name - mist.DBs.units[coa_name][countryName][category][group_num]["country"] = countryName - mist.DBs.units[coa_name][countryName][category][group_num]["countryId"] = cntry_data.id - mist.DBs.units[coa_name][countryName][category][group_num]["startTime"] = group_data.start_time - mist.DBs.units[coa_name][countryName][category][group_num]["task"] = group_data.task - mist.DBs.units[coa_name][countryName][category][group_num]["hidden"] = group_data.hidden - - mist.DBs.units[coa_name][countryName][category][group_num]["units"] = {} - - mist.DBs.units[coa_name][countryName][category][group_num]["radioSet"] = group_data.radioSet - mist.DBs.units[coa_name][countryName][category][group_num]["uncontrolled"] = group_data.uncontrolled - mist.DBs.units[coa_name][countryName][category][group_num]["frequency"] = group_data.frequency - mist.DBs.units[coa_name][countryName][category][group_num]["modulation"] = group_data.modulation - - for unit_num, unit_data in pairs(group_data.units) do - local units_tbl = mist.DBs.units[coa_name][countryName][category][group_num]["units"] --pointer to the units table for this group - - units_tbl[unit_num] = {} - if env.mission.version > 7 then - units_tbl[unit_num]["unitName"] = env.getValueDictByKey(unit_data.name) - else - units_tbl[unit_num]["unitName"] = unit_data.name - end - units_tbl[unit_num]["type"] = unit_data.type - units_tbl[unit_num]["skill"] = unit_data.skill --will be nil for statics - units_tbl[unit_num]["unitId"] = unit_data.unitId - units_tbl[unit_num]["category"] = category - units_tbl[unit_num]["coalition"] = coa_name - units_tbl[unit_num]["country"] = countryName - units_tbl[unit_num]["countryId"] = cntry_data.id - units_tbl[unit_num]["heading"] = unit_data.heading - units_tbl[unit_num]["playerCanDrive"] = unit_data.playerCanDrive - units_tbl[unit_num]["alt"] = unit_data.alt - units_tbl[unit_num]["alt_type"] = unit_data.alt_type - units_tbl[unit_num]["speed"] = unit_data.speed - units_tbl[unit_num]["livery_id"] = unit_data.livery_id - if unit_data.point then --ME currently does not work like this, but it might one day - units_tbl[unit_num]["point"] = unit_data.point - else - units_tbl[unit_num]["point"] = {} - units_tbl[unit_num]["point"]["x"] = unit_data.x - units_tbl[unit_num]["point"]["y"] = unit_data.y - end - units_tbl[unit_num]['x'] = unit_data.x - units_tbl[unit_num]['y'] = unit_data.y - - units_tbl[unit_num]["callsign"] = unit_data.callsign - units_tbl[unit_num]["onboard_num"] = unit_data.onboard_num - units_tbl[unit_num]["hardpoint_racks"] = unit_data.hardpoint_racks - units_tbl[unit_num]["psi"] = unit_data.psi - - - units_tbl[unit_num]["groupName"] = groupName - units_tbl[unit_num]["groupId"] = group_data.groupId - - if unit_data.AddPropAircraft then - units_tbl[unit_num]["AddPropAircraft"] = unit_data.AddPropAircraft - end - - if category == 'static' then - units_tbl[unit_num]["categoryStatic"] = unit_data.category - units_tbl[unit_num]["shape_name"] = unit_data.shape_name - if unit_data.mass then - units_tbl[unit_num]["mass"] = unit_data.mass - end - - if unit_data.canCargo then - units_tbl[unit_num]["canCargo"] = unit_data.canCargo - end - end - - end --for unit_num, unit_data in pairs(group_data.units) do - end --if group_data and group_data.units then - 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 --if type(cntry_data) == 'table' then - 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 - -mist.DBs.unitsByName = {} -mist.DBs.unitsById = {} -mist.DBs.unitsByCat = {} - -mist.DBs.unitsByCat['helicopter'] = {} -- adding default categories -mist.DBs.unitsByCat['plane'] = {} -mist.DBs.unitsByCat['ship'] = {} -mist.DBs.unitsByCat['static'] = {} -mist.DBs.unitsByCat['vehicle'] = {} - -mist.DBs.unitsByNum = {} - -mist.DBs.groupsByName = {} -mist.DBs.groupsById = {} -mist.DBs.humansByName = {} -mist.DBs.humansById = {} - -mist.DBs.dynGroupsAdded = {} -- will be filled by mist.dbUpdate from dynamically spawned groups -mist.DBs.activeHumans = {} - -mist.DBs.aliveUnits = {} -- will be filled in by the "update_alive_units" coroutine in mist.main. - -mist.DBs.removedAliveUnits = {} -- will be filled in by the "update_alive_units" coroutine in mist.main. --- create mist.DBs.oldAliveUnits --- do - -- local intermediate_alive_units = {} -- between 0 and 0.5 secs old - -- local function make_old_alive_units() -- called every 0.5 secs, makes the old_alive_units DB which is just a copy of alive_units that is 0.5 to 1 sec old - -- if intermediate_alive_units then - -- mist.DBs.oldAliveUnits = mist.utils.deepCopy(intermediate_alive_units) - -- end - -- intermediate_alive_units = mist.utils.deepCopy(mist.DBs.aliveUnits) - -- timer.scheduleFunction(make_old_alive_units, nil, timer.getTime() + 0.5) - -- end - - -- make_old_alive_units() --- end - - ---Build DBs -for coa_name, coa_data in pairs(mist.DBs.units) do - for cntry_name, cntry_data in pairs(coa_data) do - for category_name, category_data in pairs(cntry_data) do - if type(category_data) == 'table' then - for group_ind, group_data in pairs(category_data) do - if type(group_data) == 'table' and group_data.units and type(group_data.units) == 'table' and #group_data.units > 0 then -- OCD paradigm programming - mist.DBs.groupsByName[group_data.groupName] = mist.utils.deepCopy(group_data) - mist.DBs.groupsById[group_data.groupId] = mist.utils.deepCopy(group_data) - for unit_ind, unit_data in pairs(group_data.units) do - mist.DBs.unitsByName[unit_data.unitName] = mist.utils.deepCopy(unit_data) - mist.DBs.unitsById[unit_data.unitId] = mist.utils.deepCopy(unit_data) - - mist.DBs.unitsByCat[unit_data.category] = mist.DBs.unitsByCat[unit_data.category] or {} -- future-proofing against new categories... - table.insert(mist.DBs.unitsByCat[unit_data.category], mist.utils.deepCopy(unit_data)) - --print('inserting ' .. unit_data.unitName) - table.insert(mist.DBs.unitsByNum, mist.utils.deepCopy(unit_data)) - - if unit_data.skill and (unit_data.skill == "Client" or unit_data.skill == "Player") then - mist.DBs.humansByName[unit_data.unitName] = mist.utils.deepCopy(unit_data) - mist.DBs.humansById[unit_data.unitId] = mist.utils.deepCopy(unit_data) - --if Unit.getByName(unit_data.unitName) then - -- mist.DBs.activeHumans[unit_data.unitName] = mist.utils.deepCopy(unit_data) - -- mist.DBs.activeHumans[unit_data.unitName].playerName = Unit.getByName(unit_data.unitName):getPlayerName() - --end - end - end - end - end - end - end - end -end - --------------- --------- mist unitID funcs -do - for id, idData in pairs(mist.DBs.unitsById) do - if idData.unitId > mist.nextUnitId then - mist.nextUnitId = mist.utils.deepCopy(idData.unitId) - end - if idData.groupId > mist.nextGroupId then - mist.nextGroupId = mist.utils.deepCopy(idData.groupId) - end - end -end - ---DynDBs -mist.DBs.MEunits = mist.utils.deepCopy(mist.DBs.units) -mist.DBs.MEunitsByName = mist.utils.deepCopy(mist.DBs.unitsByName) -mist.DBs.MEunitsById = mist.utils.deepCopy(mist.DBs.unitsById) -mist.DBs.MEunitsByCat = mist.utils.deepCopy(mist.DBs.unitsByCat) -mist.DBs.MEunitsByNum = mist.utils.deepCopy(mist.DBs.unitsByNum) -mist.DBs.MEgroupsByName = mist.utils.deepCopy(mist.DBs.groupsByName) -mist.DBs.MEgroupsById = mist.utils.deepCopy(mist.DBs.groupsById) -------------- - - -mist.DBs.deadObjects = {} - -do - local mt = {} - - mt.__newindex = function(t, key, val) - --------------------------------------------------------------- - local original_key = key --only for duplicate runtime IDs. - local key_ind = 1 - while mist.DBs.deadObjects[key] do - --print('duplicate runtime id of previously dead object- key: ' .. tostring(key)) - key = tostring(original_key) .. ' #' .. tostring(key_ind) - key_ind = key_ind + 1 - end - --------------------------------------------------------------- - - if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then - --print('object found in alive_units') - val['objectData'] = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) - local pos = Object.getPosition(val.object) - if pos then - val['objectPos'] = pos.p - end - val['objectType'] = mist.DBs.aliveUnits[val.object.id_].category - - elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units - --print('object found in old_alive_units') - val['objectData'] = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) - local pos = Object.getPosition(val.object) - if pos then - val['objectPos'] = pos.p - end - val['objectType'] = mist.DBs.removedAliveUnits[val.object.id_].category - - else --attempt to determine if static object... - --print('object not found in alive units or old alive units') - local pos = Object.getPosition(val.object) - if pos then - local static_found = false - for ind, static in pairs(mist.DBs.unitsByCat['static']) do - if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... - --print('correlated dead static object to position') - val['objectData'] = static - val['objectPos'] = pos.p - val['objectType'] = 'static' - static_found = true - break - end - end - if not static_found then - val['objectPos'] = pos.p - val['objectType'] = 'building' - end - else - val['objectType'] = 'unknown' - end - end - rawset(t, key, val) - end - - setmetatable(mist.DBs.deadObjects, mt) -end - --- Event handler to start creating the dead_objects table -do - - local function addDeadObject(event) - if event.id == world.event.S_EVENT_DEAD or event.id == world.event.S_EVENT_CRASH then - if event.initiator and event.initiator.id_ and event.initiator.id_ > 0 then - - local id = event.initiator.id_ -- initial ID, could change if there is a duplicate id_ already dead. - local val = {object = event.initiator} -- the new entry in mist.DBs.deadObjects. - - --------------------------------------------------------------- - local original_id = id --only for duplicate runtime IDs. - local id_ind = 1 - while mist.DBs.deadObjects[id] do - --print('duplicate runtime id of previously dead object- id: ' .. tostring(id)) - id = tostring(original_id) .. ' #' .. tostring(id_ind) - id_ind = id_ind + 1 - end - --------------------------------------------------------------- - - if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then - --print('object found in alive_units') - val['objectData'] = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) - local pos = Object.getPosition(val.object) - if pos then - val['objectPos'] = pos.p - end - val['objectType'] = mist.DBs.aliveUnits[val.object.id_].category - --[[if mist.DBs.activeHumans[Unit.getName(val.object)] then - --trigger.action.outText('remove via death: ' .. Unit.getName(val.object),20) - mist.DBs.activeHumans[Unit.getName(val.object)] = nil - end]] - elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units - --print('object found in old_alive_units') - val['objectData'] = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) - local pos = Object.getPosition(val.object) - if pos then - val['objectPos'] = pos.p - end - val['objectType'] = mist.DBs.removedAliveUnits[val.object.id_].category - - else --attempt to determine if static object... - --print('object not found in alive units or old alive units') - local pos = Object.getPosition(val.object) - if pos then - local static_found = false - for ind, static in pairs(mist.DBs.unitsByCat['static']) do - if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... - --print('correlated dead static object to position') - val['objectData'] = static - val['objectPos'] = pos.p - val['objectType'] = 'static' - static_found = true - break - end - end - if not static_found then - val['objectPos'] = pos.p - val['objectType'] = 'building' - end - else - val['objectType'] = 'unknown' - end - end - mist.DBs.deadObjects[id] = val - - end - end - end - - - mist.addEventHandler(addDeadObject) - - --[[ - local function addClientsToActive(event) - if event.id == world.event.S_EVENT_PLAYER_ENTER_UNIT or event.id == world.event.S_EVENT_BIRTH then - env.info(mist.utils.tableShow(event)) - if Unit.getPlayerName(event.initiator) then - env.info(Unit.getPlayerName(event.initiator)) - local newU = mist.utils.deepCopy(mist.DBs.unitsByName[Unit.getName(event.initiator)]) - newU.playerName = Unit.getPlayerName(event.initiator) - mist.DBs.activeHumans[Unit.getName(event.initiator)] = newU - --trigger.action.outText('added: ' .. Unit.getName(event.initiator), 20) - end - elseif event.id == world.event.S_EVENT_PLAYER_LEAVE_UNIT and event.initiator then - if mist.DBs.activeHumans[Unit.getName(event.initiator)] then - mist.DBs.activeHumans[Unit.getName(event.initiator)] = nil - -- trigger.action.outText('removed via control: ' .. Unit.getName(event.initiator), 20) - end - end - end - - mist.addEventHandler(addClientsToActive)]] -end - - - - - -function mist.makeUnitTable(tbl) - ---[[ -Prefixes: -"[-u]" - subtract this unit if its in the table -"[g]" - add this group to the table -"[-g]" - subtract this group from the table -"[c]" - add this country's units -"[-c]" - subtract this country's units if any are in the table - -Stand-alone identifiers -"[all]" - add all units -"[-all]" - subtract all units (not very useful by itself) -"[blue]" - add all blue units -"[-blue]" - subtract all blue units -"[red]" - add all red coalition units -"[-red]" - subtract all red units - -Compound Identifiers: -"[c][helicopter]" - add all of this country's helicopters -"[-c][helicopter]" - subtract all of this country's helicopters -"[c][plane]" - add all of this country's planes -"[-c][plane]" - subtract all of this country's planes -"[c][ship]" - add all of this country's ships -"[-c][ship]" - subtract all of this country's ships -"[c][vehicle]" - add all of this country's vehicles -"[-c][vehicle]" - subtract all of this country's vehicles - -"[all][helicopter]" - add all helicopters -"[-all][helicopter]" - subtract all helicopters -"[all][plane]" - add all planes -"[-all][plane]" - subtract all planes -"[all][ship]" - add all ships -"[-all][ship]" - subtract all ships -"[all][vehicle]" - add all vehicles -"[-all][vehicle]" - subtract all vehicles - -"[blue][helicopter]" - add all blue coalition helicopters -"[-blue][helicopter]" - subtract all blue coalition helicopters -"[blue][plane]" - add all blue coalition planes -"[-blue][plane]" - subtract all blue coalition planes -"[blue][ship]" - add all blue coalition ships -"[-blue][ship]" - subtract all blue coalition ships -"[blue][vehicle]" - add all blue coalition vehicles -"[-blue][vehicle]" - subtract all blue coalition vehicles - -"[red][helicopter]" - add all red coalition helicopters -"[-red][helicopter]" - subtract all red coalition helicopters -"[red][plane]" - add all red coalition planes -"[-red][plane]" - subtract all red coalition planes -"[red][ship]" - add all red coalition ships -"[-red][ship]" - subtract all red coalition ships -"[red][vehicle]" - add all red coalition vehicles -"[-red][vehicle]" - subtract all red coalition vehicles - - -Country names to be used in [c] and [-c] short-cuts: -"Turkey" -"Norway" -"The Netherlands" -"Spain" -"UK" -"Denmark" -"USA" -"Georgia" -"Germany" -"Belgium" -"Canada" -"France" -"Israel" -"Ukraine" -"Russia" -"South Osetia" -"Abkhazia" -"Italy" -]] - - --Assumption: will be passed a table of strings, sequential - local units_by_name = {} - - local l_munits = mist.DBs.units --local reference for faster execution - for i = 1, #tbl do - local unit = tbl[i] - if unit:sub(1,4) == '[-u]' then --subtract a unit - if units_by_name[unit:sub(5)] then -- 5 to end - units_by_name[unit:sub(5)] = nil --remove - end - elseif unit:sub(1,3) == '[g]' then -- add a group - for coa, coa_tbl in pairs(l_munits) do - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' and group_tbl.groupName == unit:sub(4) then -- index 4 to end - for unit_ind, unit in pairs(group_tbl.units) do - units_by_name[unit.unitName] = true --add - end - end - end - end - end - end - end - elseif unit:sub(1,4) == '[-g]' then -- subtract a group - for coa, coa_tbl in pairs(l_munits) do - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' and group_tbl.groupName == unit:sub(5) then -- index 5 to end - for unit_ind, unit in pairs(group_tbl.units) do - if units_by_name[unit.unitName] then - units_by_name[unit.unitName] = nil --remove - end - end - end - end - end - end - end - end - elseif unit:sub(1,3) == '[c]' then -- add a country - local category = '' - local country_start = 4 - if unit:sub(4,15) == '[helicopter]' then - category = 'helicopter' - country_start = 16 - elseif unit:sub(4,10) == '[plane]' then - category = 'plane' - country_start = 11 - elseif unit:sub(4,9) == '[ship]' then - category = 'ship' - country_start = 10 - elseif unit:sub(4,12) == '[vehicle]' then - category = 'vehicle' - country_start = 13 - end - for coa, coa_tbl in pairs(l_munits) do - for country, country_table in pairs(coa_tbl) do - if country == string.lower(unit:sub(country_start)) then -- match - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - units_by_name[unit.unitName] = true --add - end - end - end - end - end - end - end - end - elseif unit:sub(1,4) == '[-c]' then -- subtract a country - local category = '' - local country_start = 5 - if unit:sub(5,16) == '[helicopter]' then - category = 'helicopter' - country_start = 17 - elseif unit:sub(5,11) == '[plane]' then - category = 'plane' - country_start = 12 - elseif unit:sub(5,10) == '[ship]' then - category = 'ship' - country_start = 11 - elseif unit:sub(5,13) == '[vehicle]' then - category = 'vehicle' - country_start = 14 - end - for coa, coa_tbl in pairs(l_munits) do - for country, country_table in pairs(coa_tbl) do - if country == string.lower(unit:sub(country_start)) then -- match - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - if units_by_name[unit.unitName] then - units_by_name[unit.unitName] = nil --remove - end - end - end - end - end - end - end - end - end - elseif unit:sub(1,6) == '[blue]' then -- add blue coalition - local category = '' - if unit:sub(7) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(7) == '[plane]' then - category = 'plane' - elseif unit:sub(7) == '[ship]' then - category = 'ship' - elseif unit:sub(7) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - if coa == 'blue' then - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - units_by_name[unit.unitName] = true --add - end - end - end - end - end - end - end - end - elseif unit:sub(1,7) == '[-blue]' then -- subtract blue coalition - local category = '' - if unit:sub(8) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(8) == '[plane]' then - category = 'plane' - elseif unit:sub(8) == '[ship]' then - category = 'ship' - elseif unit:sub(8) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - if coa == 'blue' then - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - if units_by_name[unit.unitName] then - units_by_name[unit.unitName] = nil --remove - end - end - end - end - end - end - end - end - end - elseif unit:sub(1,5) == '[red]' then -- add red coalition - local category = '' - if unit:sub(6) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(6) == '[plane]' then - category = 'plane' - elseif unit:sub(6) == '[ship]' then - category = 'ship' - elseif unit:sub(6) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - if coa == 'red' then - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - units_by_name[unit.unitName] = true --add - end - end - end - end - end - end - end - end - elseif unit:sub(1,6) == '[-red]' then -- subtract red coalition - local category = '' - if unit:sub(7) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(7) == '[plane]' then - category = 'plane' - elseif unit:sub(7) == '[ship]' then - category = 'ship' - elseif unit:sub(7) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - if coa == 'red' then - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - if units_by_name[unit.unitName] then - units_by_name[unit.unitName] = nil --remove - end - end - end - end - end - end - end - end - end - elseif unit:sub(1,5) == '[all]' then -- add all of a certain category (or all categories) - local category = '' - if unit:sub(6) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(6) == '[plane]' then - category = 'plane' - elseif unit:sub(6) == '[ship]' then - category = 'ship' - elseif unit:sub(6) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - units_by_name[unit.unitName] = true --add - end - end - end - end - end - end - end - elseif unit:sub(1,6) == '[-all]' then -- subtract all of a certain category (or all categories) - local category = '' - if unit:sub(7) == '[helicopter]' then - category = 'helicopter' - elseif unit:sub(7) == '[plane]' then - category = 'plane' - elseif unit:sub(7) == '[ship]' then - category = 'ship' - elseif unit:sub(7) == '[vehicle]' then - category = 'vehicle' - end - for coa, coa_tbl in pairs(l_munits) do - for country, country_table in pairs(coa_tbl) do - for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then - for group_ind, group_tbl in pairs(unit_type_tbl) do - if type(group_tbl) == 'table' then - for unit_ind, unit in pairs(group_tbl.units) do - if units_by_name[unit.unitName] then - units_by_name[unit.unitName] = nil --remove - end - end - end - end - end - end - end - end - else -- just a regular unit - units_by_name[unit] = true --add - end - end - - local units_tbl = {} -- indexed sequentially - for unit_name, val in pairs(units_by_name) do - if val then - units_tbl[#units_tbl + 1] = unit_name -- add all the units to the table - end - end - - - units_tbl['processed'] = timer.getTime() --add the processed flag - return units_tbl -end - - -mist.getDeadMapObjsInZones = function(zone_names) --- zone_names: table of zone names --- returns: table of dead map objects (indexed numerically) - local map_objs = {} - local zones = {} - for i = 1, #zone_names do - if mist.DBs.zonesByName[zone_names[i]] then - zones[#zones + 1] = mist.DBs.zonesByName[zone_names[i]] - end - end - for obj_id, obj in pairs(mist.DBs.deadObjects) do - if obj.objectType and obj.objectType == 'building' then --dead map object - for i = 1, #zones do - if ((zones[i].point.x - obj.objectPos.x)^2 + (zones[i].point.z - obj.objectPos.z)^2)^0.5 <= zones[i].radius then - map_objs[#map_objs + 1] = mist.utils.deepCopy(obj) - end - end - end - end - return map_objs -end - - -mist.getDeadMapObjsInPolygonZone = function(zone) --- zone_names: table of zone names --- returns: table of dead map objects (indexed numerically) - local map_objs = {} - for obj_id, obj in pairs(mist.DBs.deadObjects) do - if obj.objectType and obj.objectType == 'building' then --dead map object - if mist.pointInPolygon(obj.objectPos, zone) then - map_objs[#map_objs + 1] = mist.utils.deepCopy(obj) - end - end - end - return map_objs -end - -mist.flagFunc = {} - -mist.flagFunc.mapobjs_dead_zones = function(vars) ---[[vars needs to be: -zones = table or string, -flag = number, -stopflag = number or nil, -req_num = number or nil - -AND used by function, -initial_number - -]] --- type_tbl - local type_tbl = { - [{'zones', 'zone'}] = {'table', 'string'}, - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - [{'req_num', 'reqnum'}] = {'number', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.mapobjs_dead_zones', type_tbl, vars) - assert(err, errmsg) - local zones = vars.zones or vars.zone - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local req_num = vars.req_num or vars.reqnum or 1 - local initial_number = vars.initial_number - - if type(zones) == 'string' then - zones = {zones} - end - - if not initial_number then - initial_number = #mist.getDeadMapObjsInZones(zones) - end - - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - if (#mist.getDeadMapObjsInZones(zones) - initial_number) >= req_num and trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - return - else - mist.scheduleFunction(mist.flagFunc.mapobjs_dead_zones, {{zones = zones, flag = flag, stopflag = stopflag, req_num = req_num, initial_number = initial_number}}, timer.getTime() + 1) - end - end -end - - - -mist.flagFunc.mapobjs_dead_polygon = function(vars) ---[[vars needs to be: -zone = table, -flag = number, -stopflag = number or nil, -req_num = number or nil - -AND used by function, -initial_number - -]] --- type_tbl - local type_tbl = { - [{'zone', 'polyzone'}] = 'table', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - [{'req_num', 'reqnum'}] = {'number', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.mapobjs_dead_polygon', type_tbl, vars) - assert(err, errmsg) - local zone = vars.zone or vars.polyzone - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local req_num = vars.req_num or vars.reqnum or 1 - local initial_number = vars.initial_number - - if not initial_number then - initial_number = #mist.getDeadMapObjsInPolygonZone(zone) - end - - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - if (#mist.getDeadMapObjsInPolygonZone(zone) - initial_number) >= req_num and trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - return - else - mist.scheduleFunction(mist.flagFunc.mapobjs_dead_polygon, {{zone = zone, flag = flag, stopflag = stopflag, req_num = req_num, initial_number = initial_number}}, timer.getTime() + 1) - end - end -end - - - -function mist.pointInPolygon(point, poly, maxalt) --raycasting point in polygon. Code from http://softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm - --[[local type_tbl = { - point = {'table'}, - poly = {'table'}, - maxalt = {'number', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.pointInPolygon', type_tbl, {point, poly, maxalt}) - assert(err, errmsg) - ]] - point = mist.utils.makeVec3(point) - local px = point.x - local pz = point.z - local cn = 0 - local newpoly = mist.utils.deepCopy(poly) - - if not maxalt or (point.y <= maxalt) then - local polysize = #newpoly - newpoly[#newpoly + 1] = newpoly[1] - - newpoly[1] = mist.utils.makeVec3(newpoly[1]) - - for k = 1, polysize do - newpoly[k+1] = mist.utils.makeVec3(newpoly[k+1]) - if ((newpoly[k].z <= pz) and (newpoly[k+1].z > pz)) or ((newpoly[k].z > pz) and (newpoly[k+1].z <= pz)) then - local vt = (pz - newpoly[k].z) / (newpoly[k+1].z - newpoly[k].z) - if (px < newpoly[k].x + vt*(newpoly[k+1].x - newpoly[k].x)) then - cn = cn + 1 - end - end - end - - return cn%2 == 1 - else - return false - end -end - -mist.getUnitsInPolygon = function (unit_names, polyZone, max_alt) - local units = {} - - for i = 1, #unit_names do - units[#units + 1] = Unit.getByName(unitNames[i]) - end - - local inZoneUnits = {} - for i =1, #units do - if units[i]:isActive() and mist.pointInPolygon(units[i]:getPosition().p, polyZone, max_alt) then - inZoneUnits[inZoneUnits + 1] = units[i] - end - end - - return inZoneUnits -end - -function mist.flagFunc.units_in_polygon(vars) ---[[vars needs to be: -units = table, -zone = table, -flag = number, -stopflag = number or nil, -maxalt = number or nil, -interval = number or nil, -req_num = number or nil -toggle = boolean or nil -unitTableDef = table or nil -]] --- type_tbl - local type_tbl = { - [{'units', 'unit'}] = 'table', - [{'zone', 'polyzone'}] = 'table', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - [{'maxalt', 'alt'}] = {'number', 'nil'}, - interval = {'number', 'nil'}, - [{'req_num', 'reqnum'}] = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - unitTableDef = {'table', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_in_polygon', type_tbl, vars) - assert(err, errmsg) - local units = vars.units or vars.unit - local zone = vars.zone or vars.polyzone - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local interval = vars.interval or 1 - local maxalt = vars.maxalt or vars.alt - local req_num = vars.req_num or vars.reqnum or 1 - local toggle = vars.toggle or nil - local unitTableDef = vars.unitTableDef - - if not units.processed then - unitTableDef = mist.utils.deepCopy(units) - end - - if (units.processed and units.processed < mist.getLastDBUpdateTime()) or not units.processed then -- run unit table short cuts - units = mist.makeUnitTable(unitTableDef) - end - - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == 0) then - local num_in_zone = 0 - for i = 1, #units do - local unit = Unit.getByName(units[i]) - if unit then - local pos = unit:getPosition().p - if mist.pointInPolygon(pos, zone, maxalt) then - num_in_zone = num_in_zone + 1 - if num_in_zone >= req_num and trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - break - end - end - end - end - if toggle and (num_in_zone < req_num) and trigger.misc.getUserFlag(flag) > 0 then - trigger.action.setUserFlag(flag, false) - end - -- do another check in case stopflag was set true by this function - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == 0) then - mist.scheduleFunction(mist.flagFunc.units_in_polygon, {{units = units, zone = zone, flag = flag, stopflag = stopflag, interval = interval, req_num = req_num, maxalt = maxalt, toggle = toggle, unitTableDef = unitTableDef}}, timer.getTime() + interval) - end - end - -end - - - - -function mist.getUnitsInZones(unit_names, zone_names, zone_type) - - zone_type = zone_type or 'cylinder' - if zone_type == 'c' or zone_type == 'cylindrical' or zone_type == 'C' then - zone_type = 'cylinder' - end - if zone_type == 's' or zone_type == 'spherical' or zone_type == 'S' then - zone_type = 'sphere' - end - - assert(zone_type == 'cylinder' or zone_type == 'sphere', 'invalid zone_type: ' .. tostring(zone_type)) - - local units = {} - local zones = {} - - for k = 1, #unit_names do - local unit = Unit.getByName(unit_names[k]) - if unit then - units[#units + 1] = unit - end - end - - - for k = 1, #zone_names do - local zone = trigger.misc.getZone(zone_names[k]) - if zone then - zones[#zones + 1] = {radius = zone.radius, x = zone.point.x, y = zone.point.y, z = zone.point.z} - end - end - - local in_zone_units = {} - - for units_ind = 1, #units do - for zones_ind = 1, #zones do - if zone_type == 'sphere' then --add land height value for sphere zone type - local alt = land.getHeight({x = zones[zones_ind].x, y = zones[zones_ind].z}) - if alt then - zones[zones_ind].y = alt - end - end - local unit_pos = units[units_ind]:getPosition().p - if unit_pos and units[units_ind]:isActive() == true then - if zone_type == 'cylinder' and (((unit_pos.x - zones[zones_ind].x)^2 + (unit_pos.z - zones[zones_ind].z)^2)^0.5 <= zones[zones_ind].radius) then - in_zone_units[#in_zone_units + 1] = units[units_ind] - break - elseif zone_type == 'sphere' and (((unit_pos.x - zones[zones_ind].x)^2 + (unit_pos.y - zones[zones_ind].y)^2 + (unit_pos.z - zones[zones_ind].z)^2)^0.5 <= zones[zones_ind].radius) then - in_zone_units[#in_zone_units + 1] = units[units_ind] - break - end - end - end - end - return in_zone_units -end - - -function mist.flagFunc.units_in_zones(vars) - --[[vars needs to be: - units = table, - zones = table, - flag = number, - stopflag = number or nil, - zone_type = string or nil, - req_num = number or nil, - interval = number or nil - toggle = boolean or nil - ]] - -- type_tbl - local type_tbl = { - units = 'table', - zones = 'table', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - [{'zone_type', 'zonetype'}] = {'string', 'nil'}, - [{'req_num', 'reqnum'}] = {'number', 'nil'}, - interval = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - unitTableDef = {'table', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_in_zones', type_tbl, vars) - assert(err, errmsg) - local units = vars.units - local zones = vars.zones - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local zone_type = vars.zone_type or vars.zonetype or 'cylinder' - local req_num = vars.req_num or vars.reqnum or 1 - local interval = vars.interval or 1 - local toggle = vars.toggle or nil - local unitTableDef = vars.unitTableDef - - if not units.processed then - unitTableDef = mist.utils.deepCopy(units) - end - - if (units.processed and units.processed < mist.getLastDBUpdateTime()) or not units.processed then -- run unit table short cuts - units = mist.makeUnitTable(unitTableDef) - end - - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - - local in_zone_units = mist.getUnitsInZones(units, zones, zone_type) - - if #in_zone_units >= req_num and trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - elseif #in_zone_units < req_num and toggle then - trigger.action.setUserFlag(flag, false) - end - -- do another check in case stopflag was set true by this function - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - mist.scheduleFunction(mist.flagFunc.units_in_zones, {{units = units, zones = zones, flag = flag, stopflag = stopflag, zone_type = zone_type, req_num = req_num, interval = interval, toggle = toggle, unitTableDef = unitTableDef}}, timer.getTime() + interval) - end - end - -end - - -function mist.getUnitsInMovingZones(unit_names, zone_unit_names, radius, zone_type) - - zone_type = zone_type or 'cylinder' - if zone_type == 'c' or zone_type == 'cylindrical' or zone_type == 'C' then - zone_type = 'cylinder' - end - if zone_type == 's' or zone_type == 'spherical' or zone_type == 'S' then - zone_type = 'sphere' - end - - assert(zone_type == 'cylinder' or zone_type == 'sphere', 'invalid zone_type: ' .. tostring(zone_type)) - - local units = {} - local zone_units = {} - - for k = 1, #unit_names do - local unit = Unit.getByName(unit_names[k]) - if unit then - units[#units + 1] = unit - end - end - - for k = 1, #zone_unit_names do - local unit = Unit.getByName(zone_unit_names[k]) - if unit then - zone_units[#zone_units + 1] = unit - end - end - - local in_zone_units = {} - - for units_ind = 1, #units do - for zone_units_ind = 1, #zone_units do - local unit_pos = units[units_ind]:getPosition().p - local zone_unit_pos = zone_units[zone_units_ind]:getPosition().p - if unit_pos and zone_unit_pos and units[units_ind]:isActive() == true then - if zone_type == 'cylinder' and (((unit_pos.x - zone_unit_pos.x)^2 + (unit_pos.z - zone_unit_pos.z)^2)^0.5 <= radius) then - in_zone_units[#in_zone_units + 1] = units[units_ind] - break - elseif zone_type == 'sphere' and (((unit_pos.x - zone_unit_pos.x)^2 + (unit_pos.y - zone_unit_pos.y)^2 + (unit_pos.z - zone_unit_pos.z)^2)^0.5 <= radius) then - in_zone_units[#in_zone_units + 1] = units[units_ind] - break - end - end - end - end - return in_zone_units -end - - - -function mist.flagFunc.units_in_moving_zones(vars) - --[[vars needs to be: - units = table, - zone_units = table, - radius = number, - flag = number, - stopflag = number or nil, - zone_type = string or nil, - req_num = number or nil, - interval = number or nil - toggle = boolean or nil - ]] - -- type_tbl - local type_tbl = { - units = 'table', - [{'zone_units', 'zoneunits'}] = 'table', - radius = 'number', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - [{'zone_type', 'zonetype'}] = {'string', 'nil'}, - [{'req_num', 'reqnum'}] = {'number', 'nil'}, - interval = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - unitTableDef = {'table', 'nil'}, - zUnitTableDef = {'table', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_in_moving_zones', type_tbl, vars) - assert(err, errmsg) - local units = vars.units - local zone_units = vars.zone_units or vars.zoneunits - local radius = vars.radius - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local zone_type = vars.zone_type or vars.zonetype or 'cylinder' - local req_num = vars.req_num or vars.reqnum or 1 - local interval = vars.interval or 1 - local toggle = vars.toggle or nil - local unitTableDef = vars.unitTableDef - local zUnitTableDef = vars.zUnitTableDef - - if not units.processed then - unitTableDef = mist.utils.deepCopy(units) - end - - if not zone_units.processed then - zUnitTableDef = mist.utils.deepCopy(zone_units) - end - - if (units.processed and units.processed < mist.getLastDBUpdateTime()) or not units.processed then -- run unit table short cuts - units = mist.makeUnitTable(unitTableDef) - end - - if (zone_units.processed and zone_units.processed < mist.getLastDBUpdateTime()) or not zone_units.processed then -- run unit table short cuts - zone_units = mist.makeUnitTable(zUnitTableDef) - end - - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - - local in_zone_units = mist.getUnitsInMovingZones(units, zone_units, radius, zone_type) - - if #in_zone_units >= req_num and trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - elseif #in_zone_units < req_num and toggle then - trigger.action.setUserFlag(flag, false) - end - -- do another check in case stopflag was set true by this function - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - mist.scheduleFunction(mist.flagFunc.units_in_moving_zones, {{units = units, zone_units = zone_units, radius = radius, flag = flag, stopflag = stopflag, zone_type = zone_type, req_num = req_num, interval = interval, toggle = toggle, unitTableDef = unitTableDef, zUnitTableDef = zUnitTableDef}}, timer.getTime() + interval) - end - end - -end - - -mist.getUnitsLOS = function(unitset1, altoffset1, unitset2, altoffset2, radius) - radius = radius or math.huge - - local unit_info1 = {} - local unit_info2 = {} - - -- get the positions all in one step, saves execution time. - for unitset1_ind = 1, #unitset1 do - local unit1 = Unit.getByName(unitset1[unitset1_ind]) - if unit1 and unit1:isActive() == true then - unit_info1[#unit_info1 + 1] = {} - unit_info1[#unit_info1]["unit"] = unit1 - unit_info1[#unit_info1]["pos"] = unit1:getPosition().p - end - end - - for unitset2_ind = 1, #unitset2 do - local unit2 = Unit.getByName(unitset2[unitset2_ind]) - if unit2 and unit2:isActive() == true then - unit_info2[#unit_info2 + 1] = {} - unit_info2[#unit_info2]["unit"] = unit2 - unit_info2[#unit_info2]["pos"] = unit2:getPosition().p - end - end - - local LOS_data = {} - -- now compute los - for unit1_ind = 1, #unit_info1 do - local unit_added = false - for unit2_ind = 1, #unit_info2 do - if radius == math.huge or (mist.vec.mag(mist.vec.sub(unit_info1[unit1_ind].pos, unit_info2[unit2_ind].pos)) < radius) then -- inside radius - local point1 = { x = unit_info1[unit1_ind].pos.x, y = unit_info1[unit1_ind].pos.y + altoffset1, z = unit_info1[unit1_ind].pos.z} - local point2 = { x = unit_info2[unit2_ind].pos.x, y = unit_info2[unit2_ind].pos.y + altoffset2, z = unit_info2[unit2_ind].pos.z} - if land.isVisible(point1, point2) then - if unit_added == false then - unit_added = true - LOS_data[#LOS_data + 1] = {} - LOS_data[#LOS_data]['unit'] = unit_info1[unit1_ind].unit - LOS_data[#LOS_data]['vis'] = {} - LOS_data[#LOS_data]['vis'][#LOS_data[#LOS_data]['vis'] + 1] = unit_info2[unit2_ind].unit - else - LOS_data[#LOS_data]['vis'][#LOS_data[#LOS_data]['vis'] + 1] = unit_info2[unit2_ind].unit - end - end - end - end - end - - return LOS_data -end - -mist.flagFunc.units_LOS = function(vars) ---[[vars needs to be: -unitset1 = table, -altoffset1 = number, -unitset2 = table, -altoffset2 = number, -flag = number, -stopflag = number or nil, -radius = number or nil, -interval = number or nil, -req_num = number or nil -toggle = boolean or nil -]] --- type_tbl - local type_tbl = { - [{'unitset1', 'units1'}] = 'table', - [{'altoffset1', 'alt1'}] = 'number', - [{'unitset2', 'units2'}] = 'table', - [{'altoffset2', 'alt2'}] = 'number', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - [{'req_num', 'reqnum'}] = {'number', 'nil'}, - interval = {'number', 'nil'}, - radius = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - unitTableDef1 = {'table', 'nil'}, - unitTableDef2 = {'table', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_LOS', type_tbl, vars) - assert(err, errmsg) - local unitset1 = vars.unitset1 or vars.units1 - local altoffset1 = vars.altoffset1 or vars.alt1 - local unitset2 = vars.unitset2 or vars.units2 - local altoffset2 = vars.altoffset2 or vars.alt2 - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local interval = vars.interval or 1 - local radius = vars.radius or math.huge - local req_num = vars.req_num or vars.reqnum or 1 - local toggle = vars.toggle or nil - local unitTableDef1 = vars.unitTableDef1 - local unitTableDef2 = vars.unitTableDef2 - - if not unitset1.processed then - unitTableDef1 = mist.utils.deepCopy(unitset1) - end - - if not unitset2.processed then - unitTableDef2 = mist.utils.deepCopy(unitset2) - end - - if (unitset1.processed and unitset1.processed < mist.getLastDBUpdateTime()) or not unitset1.processed then -- run unit table short cuts - unitset1 = mist.makeUnitTable(unitTableDef1) - end - - if (unitset2.processed and unitset2.processed < mist.getLastDBUpdateTime()) or not unitset2.processed then -- run unit table short cuts - unitset2 = mist.makeUnitTable(unitTableDef2) - end - - - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - - local unitLOSdata = mist.getUnitsLOS(unitset1, altoffset1, unitset2, altoffset2, radius) - - if #unitLOSdata >= req_num and trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - elseif #unitLOSdata < req_num and toggle then - trigger.action.setUserFlag(flag, false) - end - -- do another check in case stopflag was set true by this function - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - mist.scheduleFunction(mist.flagFunc.units_LOS, {{unitset1 = unitset1, altoffset1 = altoffset1, unitset2 = unitset2, altoffset2 = altoffset2, flag = flag, stopflag = stopflag, radius = radius, req_num = req_num, interval = interval, toggle = toggle, unitTableDef1 = unitTableDef1, unitTableDef2 = unitTableDef2}}, timer.getTime() + interval) - end - end -end - -mist.flagFunc.group_alive = function(vars) ---[[vars -groupName -flag -toggle -interval -stopFlag - -]] - local type_tbl = { - [{'group', 'groupname', 'gp', 'groupName'}] = 'string', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - interval = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_alive', type_tbl, vars) - assert(err, errmsg) - - local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local interval = vars.interval or 1 - local toggle = vars.toggle or nil - - - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true and #Group.getByName(groupName):getUnits() > 0 then - if trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - end - else - if toggle then - trigger.action.setUserFlag(flag, false) - end - end - end - - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - mist.scheduleFunction(mist.flagFunc.group_alive, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle}}, timer.getTime() + interval) - end - -end - -mist.flagFunc.group_dead = function(vars) - local type_tbl = { - [{'group', 'groupname', 'gp', 'groupName'}] = 'string', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - interval = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_dead', type_tbl, vars) - assert(err, errmsg) - - local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname - local flag = vars.flag - local stopflag = vars.stopflag or -1 - local interval = vars.interval or 1 - local toggle = vars.toggle or nil - - - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - if (Group.getByName(groupName) and Group.getByName(groupName):isExist() == false) or (Group.getByName(groupName) and #Group.getByName(groupName):getUnits() < 1) or not Group.getByName(groupName) then - if trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - end - else - if toggle then - trigger.action.setUserFlag(flag, false) - end - end - end - - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - mist.scheduleFunction(mist.flagFunc.group_dead, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle}}, timer.getTime() + interval) - end -end - -mist.flagFunc.group_alive_less_than = function(vars) - local type_tbl = { - [{'group', 'groupname', 'gp', 'groupName'}] = 'string', - percent = 'number', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - interval = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_alive_less_than', type_tbl, vars) - assert(err, errmsg) - - local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname - local flag = vars.flag - local percent = vars.percent - local stopflag = vars.stopflag or -1 - local interval = vars.interval or 1 - local toggle = vars.toggle or nil - - - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then - if Group.getByName(groupName):getSize()/Group.getByName(groupName):getInitialSize() < percent/100 then - if trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - end - else - if toggle then - trigger.action.setUserFlag(flag, false) - end - end - else - if trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - end - end - end - - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - mist.scheduleFunction(mist.flagFunc.group_alive_less_than, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle, percent = percent}}, timer.getTime() + interval) - end -end - -mist.flagFunc.group_alive_more_than = function(vars) - local type_tbl = { - [{'group', 'groupname', 'gp', 'groupName'}] = 'string', - percent = 'number', - flag = {'number', 'string'}, - stopflag = {'number', 'string', 'nil'}, - interval = {'number', 'nil'}, - toggle = {'boolean', 'nil'}, - } - - local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_alive_more_than', type_tbl, vars) - assert(err, errmsg) - - local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname - local flag = vars.flag - local percent = vars.percent - local stopflag = vars.stopflag or -1 - local interval = vars.interval or 1 - local toggle = vars.toggle or nil - - - if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then - if Group.getByName(groupName):getSize()/Group.getByName(groupName):getInitialSize() > percent/100 then - if trigger.misc.getUserFlag(flag) == 0 then - trigger.action.setUserFlag(flag, true) - end - else - if toggle and trigger.misc.getUserFlag(flag) == 1 then - trigger.action.setUserFlag(flag, false) - end - end - else --- just in case - if toggle and trigger.misc.getUserFlag(flag) == 1 then - trigger.action.setUserFlag(flag, false) - end - end - end - - if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then - mist.scheduleFunction(mist.flagFunc.group_alive_more_than, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle, percent = percent}}, timer.getTime() + interval) - end -end - -mist.getAvgPoint = function(points) - local avgX, avgY, avgZ, totNum = 0, 0, 0, 0 - for i = 1, #points do - local nPoint = mist.utils.makeVec3(points[i]) - if nPoint.z then - avgX = avgX + nPoint.x - avgY = avgY + nPoint.y - avgZ = avgZ + nPoint.z - totNum = totNum + 1 - end - end - if totNum ~= 0 then - return {x = avgX/totNum, y = avgY/totNum, z = avgZ/totNum} - end -end - - ---Gets the average position of a group of units (by name) -mist.getAvgPos = function(unitNames) - local avgX, avgY, avgZ, totNum = 0, 0, 0, 0 - for i = 1, #unitNames do - local unit - if Unit.getByName(unitNames[i]) then - unit = Unit.getByName(unitNames[i]) - elseif StaticObject.getByName(unitNames[i]) then - unit = StaticObject.getByName(unitNames[i]) - end - if unit then - local pos = unit:getPosition().p - if pos then -- you never know O.o - avgX = avgX + pos.x - avgY = avgY + pos.y - avgZ = avgZ + pos.z - totNum = totNum + 1 - end - end - end - if totNum ~= 0 then - return {x = avgX/totNum, y = avgY/totNum, z = avgZ/totNum} - end -end - -mist.getAvgGroupPos = function(groupName) - if type(groupName) == 'string' and Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then - groupName = Group.getByName(groupName) - end - local units = {} - for i = 1, #groupName:getSize() do - table.insert(units, groupName.getUnit(i):getName()) - end - - return mist.getAvgPos(units) - -end - ---------------------------------------------------------------------------------------- --- demos -mist.demos = {} - -mist.demos.printFlightData = function(unit) - if unit:isExist() then - local function printData(unit, prevVel, prevE, prevTime) - local angles = mist.getAttitude(unit) - if angles then - local Heading = angles.Heading - local Pitch = angles.Pitch - local Roll = angles.Roll - local Yaw = angles.Yaw - local AoA = angles.AoA - local ClimbAngle = angles.ClimbAngle - - if not Heading then - Heading = 'NA' - else - Heading = string.format('%12.2f', mist.utils.toDegree(Heading)) - end - - if not Pitch then - Pitch = 'NA' - else - Pitch = string.format('%12.2f', mist.utils.toDegree(Pitch)) - end - - if not Roll then - Roll = 'NA' - else - Roll = string.format('%12.2f', mist.utils.toDegree(Roll)) - end - - local AoAplusYaw = 'NA' - if AoA and Yaw then - AoAplusYaw = string.format('%12.2f', mist.utils.toDegree((AoA^2 + Yaw^2)^0.5)) - end - - if not Yaw then - Yaw = 'NA' - else - Yaw = string.format('%12.2f', mist.utils.toDegree(Yaw)) - end - - if not AoA then - AoA = 'NA' - else - AoA = string.format('%12.2f', mist.utils.toDegree(AoA)) - end - - if not ClimbAngle then - ClimbAngle = 'NA' - else - ClimbAngle = string.format('%12.2f', mist.utils.toDegree(ClimbAngle)) - end - local unitPos = unit:getPosition() - local unitVel = unit:getVelocity() - local curTime = timer.getTime() - local absVel = string.format('%12.2f', mist.vec.mag(unitVel)) - - - local unitAcc = 'NA' - local Gs = 'NA' - local axialGs = 'NA' - local transGs = 'NA' - if prevVel and prevTime then - local xAcc = (unitVel.x - prevVel.x)/(curTime - prevTime) - local yAcc = (unitVel.y - prevVel.y)/(curTime - prevTime) - local zAcc = (unitVel.z - prevVel.z)/(curTime - prevTime) - - unitAcc = string.format('%12.2f', mist.vec.mag({x = xAcc, y = yAcc, z = zAcc})) - Gs = string.format('%12.2f', mist.vec.mag({x = xAcc, y = yAcc + 9.81, z = zAcc})/9.81) - axialGs = string.format('%12.2f', mist.vec.dp({x = xAcc, y = yAcc + 9.81, z = zAcc}, unitPos.x)/9.81) - transGs = string.format('%12.2f', mist.vec.mag(mist.vec.cp({x = xAcc, y = yAcc + 9.81, z = zAcc}, unitPos.x))/9.81) - end - - local E = 0.5*mist.vec.mag(unitVel)^2 + 9.81*unitPos.p.y - - local energy = string.format('%12.2e', E) - - local dEdt = 'NA' - if prevE and prevTime then - dEdt = string.format('%12.2e', (E - prevE)/(curTime - prevTime)) - end - - trigger.action.outText(string.format('%-25s', 'Heading: ') .. Heading .. ' degrees\n' .. string.format('%-25s', 'Roll: ') .. Roll .. ' degrees\n' .. string.format('%-25s', 'Pitch: ') .. Pitch - .. ' degrees\n' .. string.format('%-25s', 'Yaw: ') .. Yaw .. ' degrees\n' .. string.format('%-25s', 'AoA: ') .. AoA .. ' degrees\n' .. string.format('%-25s', 'AoA plus Yaw: ') .. AoAplusYaw .. ' degrees\n' .. string.format('%-25s', 'Climb Angle: ') .. - ClimbAngle .. ' degrees\n' .. string.format('%-25s', 'Absolute Velocity: ') .. absVel .. ' m/s\n' .. string.format('%-25s', 'Absolute Acceleration: ') .. unitAcc ..' m/s^2\n' - .. string.format('%-25s', 'Axial G loading: ') .. axialGs .. ' g\n' .. string.format('%-25s', 'Transverse G loading: ') .. transGs .. ' g\n' .. string.format('%-25s', 'Absolute G loading: ') .. Gs .. ' g\n' .. string.format('%-25s', 'Energy: ') .. energy .. ' J/kg\n' .. string.format('%-25s', 'dE/dt: ') .. dEdt .. - ' J/(kg*s)', 1) - - return unitVel, E, curTime - end - end - - local function frameFinder(unit, prevVel, prevE, prevTime) - if unit:isExist() then - local currVel = unit:getVelocity() - if prevVel and (prevVel.x ~= currVel.x or prevVel.y ~= currVel.y or prevVel.z ~= currVel.z) or (prevTime and (timer.getTime() - prevTime) > 0.25) then - prevVel, prevE, prevTime = printData(unit, prevVel, prevE, prevTime) - end - mist.scheduleFunction(frameFinder, {unit, prevVel, prevE, prevTime}, timer.getTime() + 0.005) -- it can't go this fast, limited to the 100 times a sec check right now. - end - end - - - local curVel = unit:getVelocity() - local curTime = timer.getTime() - local curE = 0.5*mist.vec.mag(curVel)^2 + 9.81*unit:getPosition().p.y - frameFinder(unit, curVel, curE, curTime) - - end - -end --------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------- ---start of Mission task functions ---***************************************************** -mist.ground = {} -mist.fixedWing = {} -mist.heli = {} -mist.air = {} -mist.air.fixedWing = {} -mist.air.heli = {} - -mist.goRoute = function(group, path) - local misTask = { - id = 'Mission', - params = { - route = { - points = mist.utils.deepCopy(path), - }, - }, - } - if type(group) == 'string' then - group = Group.getByName(group) - end - local groupCon = nil - if group then - groupCon = group:getController() - if groupCon then - groupCon:setTask(misTask) - return true - end - end - --Controller.setTask(groupCon, misTask) - return false -end - -function mist.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 = mist.DBs.MEgroupsByName[groupIdent].groupId - end - - for coa_name, coa_data in pairs(env.mission.coalition) do - if (coa_name == 'red' or coa_name == 'blue') and type(coa_data) == 'table' then - if coa_data.country then --there is a country table - for cntry_id, cntry_data in pairs(coa_data.country) do - for obj_type_name, obj_type_data in pairs(cntry_data) do - if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" then -- only these types have points - if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's a group! - for group_num, group_data in pairs(obj_type_data.group) do - if group_data and group_data.groupId == gpId then -- this is the group we are looking for - if group_data.route and group_data.route.points and #group_data.route.points > 0 then - local points = {} - - for point_num, point in pairs(group_data.route.points) do - local routeData = {} - if not point.point then - routeData.x = point.x - routeData.y = point.y - else - routeData.point = point.point --it's possible that the ME could move to the point = Vec2 notation. - end - routeData.form = point.action - routeData.speed = point.speed - routeData.alt = point.alt - routeData.alt_type = point.alt_type - routeData.airdromeId = point.airdromeId - routeData.helipadId = point.helipadId - routeData.type = point.type - routeData.action = point.action - if task then - routeData.task = point.task - end - points[point_num] = routeData - end - - return points - end - return - end --if group_data and group_data.name and group_data.name == 'groupname' - end --for group_num, group_data in pairs(obj_type_data.group) do - end --if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then - end --if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then - end --for obj_type_name, obj_type_data in pairs(cntry_data) do - end --for cntry_id, cntry_data in pairs(coa_data.country) do - end --if coa_data.country then --there is a country table - end --if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then - end --for coa_name, coa_data in pairs(mission.coalition) do -end - - - -mist.ground.buildPath = function() end -- ???? - - --- No longer accepts path -mist.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 = mist.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 - -mist.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 altType == 'agl' then - wp.alt_type = 'RADIO' - elseif altType == 'baro' or altType == '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 = mist.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 - -mist.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 altType == 'agl' then - wp.alt_type = 'RADIO' - elseif altType == 'baro' or altType == '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 = mist.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 - - - ---------------------------------- - --- - - --- need to return a Vec3 or Vec2? -function mist.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 - -mist.getRandomPointInZone = function(zoneName, innerRadius) - if type(zoneName) == 'string' and type(trigger.misc.getZone(zoneName)) == 'table' then - return mist.getRandPointInCircle(trigger.misc.getZone(zoneName).point, trigger.misc.getZone(zoneName).radius, innerRadius) - end - return false -end - -mist.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 mist.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 = mist.getRandPointInCircle(point, radius, innerRadius) - - local offset = {} - local posStart = mist.getLeadPos(group) - - offset.x = mist.utils.round(math.sin(heading - (math.pi/2)) * 50 + rndCoord.x, 3) - offset.z = mist.utils.round(math.cos(heading + (math.pi/2)) * 50 + rndCoord.y, 3) - path[#path + 1] = mist.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] = mist.ground.buildWP({['x'] = posStart.x + 11, ['z'] = posStart.z + 11}, 'off_road', speed) - path[#path + 1] = mist.ground.buildWP(posStart, 'on_road', speed) - path[#path + 1] = mist.ground.buildWP(offset, 'on_road', speed) - else - path[#path + 1] = mist.ground.buildWP({['x'] = posStart.x + 25, ['z'] = posStart.z + 25}, form, speed) - end - - path[#path + 1] = mist.ground.buildWP(offset, form, speed) - path[#path + 1] = mist.ground.buildWP(rndCoord, form, speed) - - mist.goRoute(group, path) - - return -end - -mist.groupRandomDistSelf = function(gpData, dist, form, heading, speed) - local pos = mist.getLeadPos(gpData) - local fakeZone = {} - fakeZone.radius = dist or math.random(300, 1000) - fakeZone.point = {x = pos.x, y = pos.y, z = pos.z} - mist.groupToRandomZone(gpData, fakeZone, form, heading, speed) - - return -end - -mist.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 = mist.utils.kmphToMps(speed) - end - - local vars = {} - vars.group = gpData - vars.radius = zone.radius - vars.form = form - vars.headingDegrees = heading - vars.speed = speed - vars.point = mist.utils.zoneToVec3(zone) - - mist.groupToRandomPoint(vars) - - return -end - -mist.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 - -mist.terrainHeightDiff = function(coord, searchSize) - local samples = {} - local searchRadius = 5 - if searchSize then - searchRadius = searchSize - end - if type(coord) == 'string' then - coord = mist.utils.zoneToVec3(coord) - end - - coord = mist.utils.makeVec2(coord) - - samples[#samples + 1] = land.getHeight(coord) - for i = 0, 360, 30 do - samples[#samples + 1] = land.getHeight({x = (coord.x + (math.sin(math.rad(i))*searchRadius)), y = (coord.y + (math.cos(math.rad(i))*searchRadius))}) - if searchRadius >= 20 then -- if search radius is sorta large, take a sample halfway between center and outer edge - samples[#samples + 1] = land.getHeight({x = (coord.x + (math.sin(math.rad(i))*(searchRadius/2))), y = (coord.y + (math.cos(math.rad(i))*(searchRadius/2)))}) - end - end - local tMax, tMin = 0, 1000000 - for index, height in pairs(samples) do - if height > tMax then - tMax = height - end - if height < tMin then - tMin = height - end - end - return mist.utils.round(tMax - tMin, 2) -end - - - -mist.groupToPoint = function(gpData, point, form, heading, speed, useRoads) - if type(point) == 'string' then - point = trigger.misc.getZone(point) - end - if speed then - speed = mist.utils.kmphToMps(speed) - end - - local vars = {} - vars.group = gpData - vars.form = form - vars.headingDegrees = heading - vars.speed = speed - vars.disableRoads = useRoads - vars.point = mist.utils.zoneToVec3(point) - mist.groupToRandomPoint(vars) - - return -end - - -mist.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 Unit.isExist(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 Unit.isExist(unit) and ind < lowestInd then - lowestInd = ind - return unit:getPosition().p - end - end - end - if leader and Unit.isExist(leader) then -- maybe a little too paranoid now... - return leader:getPosition().p - end -end - --- end of Mission task functions --------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------- - - - --------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------- --------MESAGGES------ - -do - local messageList = {} - local messageDisplayRate = 0.1 -- this defines the max refresh rate of the message box it honestly only needs to go faster than this for precision timing stuff (which could be its own function) - local messageID = 0 - local displayActive = false - local displayFuncId = 0 - - local caSlots = false - local caMSGtoGroup = false - - if env.mission.groundControl then -- just to be sure? - for index, value in pairs(env.mission.groundControl) do - if type(value) == 'table' then - for roleName, roleVal in pairs(value) do - for rIndex, rVal in pairs(roleVal) do - if rIndex == 'red' or rIndex == 'blue' then - if env.mission.groundControl[index][roleName][rIndex] > 0 then - caSlots = true - break - end - end - end - end - elseif type(value) == 'boolean' and value == true then - caSlots = true - break - end - end - end - - local function mistdisplayV5() - --[[thoughts to improve upon - event handler based activeClients table. - display messages only when there is an update - possibly co-routine it. -]] - end - - local function mistdisplayV4() - local activeClients = {} - - for clientId, clientData in pairs(mist.DBs.humansById) do - if Unit.getByName(clientData.unitName) and Unit.getByName(clientData.unitName):isExist() == true then - activeClients[clientData.groupId] = clientData.groupName - end - end - - --[[if caSlots == true and caMSGtoGroup == true then - - end]] - - - if #messageList > 0 then - if displayActive == false then - displayActive = true - end - --mist.debug.writeData(mist.utils.serialize,{'msg', messageList}, 'messageList.lua') - local msgTableText = {} - local msgTableSound = {} - - for messageId, messageData in pairs(messageList) do - if messageData.displayedFor > messageData.displayTime then - messageData:remove() -- now using the remove/destroy function. - else - if messageData.displayedFor then - messageData.displayedFor = messageData.displayedFor + messageDisplayRate - end - local nextSound = 1000 - local soundIndex = 0 - - if messageData.multSound and #messageData.multSound > 0 then - for index, sData in pairs(messageData.multSound) do - if sData.time <= messageData.displayedFor and sData.played == false and sData.time < nextSound then -- find index of the next sound to be played - nextSound = sData.time - soundIndex = index - end - end - if soundIndex ~= 0 then - messageData.multSound[soundIndex].played = true - end - end - - for recIndex, recData in pairs(messageData.msgFor) do -- iterate recipiants - if recData == 'RED' or recData == 'BLUE' or activeClients[recData] then -- rec exists - if messageData.text then -- text - if not msgTableText[recData] then -- create table entry for text - msgTableText[recData] = {} - msgTableText[recData].text = {} - if recData == 'RED' or recData == 'BLUE' then - msgTableText[recData].text[1] = '-------Combined Arms Message-------- \n' - end - msgTableText[recData].text[#msgTableText[recData].text + 1] = messageData.text - msgTableText[recData].displayTime = messageData.displayTime - messageData.displayedFor - else -- add to table entry and adjust display time if needed - if recData == 'RED' or recData == 'BLUE' then - msgTableText[recData].text[#msgTableText[recData].text + 1] = '\n ---------------- Combined Arms Message: \n' - else - msgTableText[recData].text[#msgTableText[recData].text + 1] = '\n ---------------- \n' - end - msgTableText[recData].text[#msgTableText[recData].text + 1] = messageData.text - if msgTableText[recData].displayTime < messageData.displayTime - messageData.displayedFor then - msgTableText[recData].displayTime = messageData.displayTime - messageData.displayedFor - else - msgTableText[recData].displayTime = 1 - end - end - end - if soundIndex ~= 0 then - msgTableSound[recData] = messageData.multSound[soundIndex].file - end - end - end - - - end - end - ------- new display - - if caSlots == true and caMSGtoGroup == false then - if msgTableText['RED'] then - trigger.action.outTextForCoalition(coalition.side.RED, table.concat(msgTableText['RED'].text), msgTableText['RED'].displayTime, true) - - end - if msgTableText['BLUE'] then - trigger.action.outTextForCoalition(coalition.side.BLUE, table.concat(msgTableText['BLUE'].text), msgTableText['BLUE'].displayTime, true) - end - end - - for index, msgData in pairs(msgTableText) do - if type(index) == 'number' then -- its a groupNumber - trigger.action.outTextForGroup(index, table.concat(msgData.text), msgData.displayTime, true) - end - end - --- new audio - if msgTableSound['RED'] then - trigger.action.outSoundForCoalition(coalition.side.RED, msgTableSound['RED']) - end - if msgTableSound['BLUE'] then - trigger.action.outSoundForCoalition(coalition.side.BLUE, msgTableSound['BLUE']) - end - - - for index, file in pairs(msgTableSound) do - if type(index) == 'number' then -- its a groupNumber - trigger.action.outSoundForGroup(index, file) - end - end - else - mist.removeFunction(displayFuncId) - displayActive = false - end - - end - - local typeBase = { - ['Mi-8MT'] = {'Mi-8MTV2', 'Mi-8MTV', 'Mi-8'}, - ['MiG-21Bis'] = {'Mig-21'}, - ['MiG-15bis'] = {'Mig-15'}, - ['FW-190D9'] = {'FW-190'}, - ['Bf-109K-4'] = {'Bf-109'}, - } - - --[[mist.setCAGroupMSG = function(val) - if type(val) == 'boolean' then - caMSGtoGroup = val - return true - end - return false - end]] - - mist.message = { - - - add = function(vars) - local function msgSpamFilter(recList, spamBlockOn) - for id, name in pairs(recList) do - if name == spamBlockOn then - -- env.info('already on recList') - return recList - end - end - --env.info('add to recList') - table.insert(recList, spamBlockOn) - return recList - end - - --[[ - local vars = {} - vars.text = 'Hello World' - vars.displayTime = 20 - vars.msgFor = {coa = {'red'}, countries = {'Ukraine', 'Georgia'}, unitTypes = {'A-10C'}} - mist.message.add(vars) - - Displays the message for all red coalition players. Players belonging to Ukraine and Georgia, and all A-10Cs on the map - - ]] - - - local new = {} - new.text = vars.text -- The actual message - new.displayTime = vars.displayTime -- How long will the message appear for - new.displayedFor = 0 -- how long the message has been displayed so far - new.name = vars.name -- ID to overwrite the older message (if it exists) Basically it replaces a message that is displayed with new text. - new.addedAt = timer.getTime() - new.update = true - - if vars.multSound and vars.multSound[1] then - new.multSound = vars.multSound - else - new.multSound = {} - end - - if vars.sound or vars.fileName then -- converts old sound file system into new multSound format - local sound = vars.sound - if vars.fileName then - sound = vars.fileName - end - new.multSound[#new.multSound+1] = {time = 0.1, file = sound} - end - - if #new.multSound > 0 then - for i, data in pairs(new.multSound) do - data.played = false - end - end - - local newMsgFor = {} -- list of all groups message displays for - for forIndex, forData in pairs(vars.msgFor) do - for list, listData in pairs(forData) do - for clientId, clientData in pairs(mist.DBs.humansById) do - forIndex = string.lower(forIndex) - if type(listData) == 'string' then - listData = string.lower(listData) - end - if (forIndex == 'coa' and (listData == string.lower(clientData.coalition) or listData == 'all')) or (forIndex == 'countries' and string.lower(clientData.country) == listData) or (forIndex == 'units' and string.lower(clientData.unitName) == listData) then -- - newMsgFor = msgSpamFilter(newMsgFor, clientData.groupId) -- so units dont get the same message twice if complex rules are given - --table.insert(newMsgFor, clientId) - elseif forIndex == 'unittypes' then - for typeId, typeData in pairs(listData) do - local found = false - for clientDataEntry, clientDataVal in pairs(clientData) do - if type(clientDataVal) == 'string' then - if mist.matchString(list, clientDataVal) == true or list == 'all' then - local sString = typeData - for rName, pTbl in pairs(typeBase) do -- just a quick check to see if the user may have meant something and got the specific type of the unit wrong - for pIndex, pName in pairs(pTbl) do - if mist.stringMatch(sString, pName) then - sString = rName - end - end - end - if sString == clientData.type then - found = true - newMsgFor = msgSpamFilter(newMsgFor, clientData.groupId) -- sends info oto other function to see if client is already recieving the current message. - --table.insert(newMsgFor, clientId) - end - end - end - if found == true then -- shouldn't this be elsewhere too? - break - end - end - end - - end - end - for coaData, coaId in pairs(coalition.side) do - if string.lower(forIndex) == 'coa' or string.lower(forIndex) == 'ca' then - if listData == string.lower(coaData) or listData == 'all' then - newMsgFor = msgSpamFilter(newMsgFor, coaData) - end - end - end - end - end - - if #newMsgFor > 0 then - new.msgFor = newMsgFor -- I swear its not confusing - - else - return false - end - - - if vars.name and type(vars.name) == 'string' then - for i = 1, #messageList do - if messageList[i].name then - if messageList[i].name == vars.name then - --env.info('updateMessage') - messageList[i].displayedFor = 0 - messageList[i].addedAt = timer.getTime() - messageList[i].sound = new.sound - messageList[i].text = new.text - messageList[i].msgFor = new.msgFor - messageList[i].multSound = new.multSound - messageList[i].update = true - return messageList[i].messageID - end - end - end - end - - messageID = messageID + 1 - new.messageID = messageID - - --mist.debug.writeData(mist.utils.serialize,{'msg', new}, 'newMsg.lua') - - - messageList[#messageList + 1] = new - - local mt = { __index = mist.message} - setmetatable(new, mt) - - if displayActive == false then - displayActive = true - displayFuncId = mist.scheduleFunction(mistdisplayV4, {}, timer.getTime() + messageDisplayRate, messageDisplayRate) - end - - return messageID - - end, - - remove = function(self) -- Now a self variable; the former functionality taken up by mist.message.removeById. - for i, msgData in pairs(messageList) do - if messageList[i] == self then - table.remove(messageList, i) - return true --removal successful - end - end - return false -- removal not successful this script fails at life! - end, - - removeById = function(id) -- This function is NOT passed a self variable; it is the remove by id function. - for i, msgData in pairs(messageList) do - if messageList[i].messageID == id then - table.remove(messageList, i) - return true --removal successful - end - end - return false -- removal not successful this script fails at life! - end, - } - -end --- End of message system --------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------- - --------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------- --- Beginning of coordinate messages ---[[ -Design: - -Already have: -mist.tostringBR = function(az, dist, alt, metric) -mist.tostringLL = function(lat, lon, acc, DMS) -mist.tostringMGRS = function(MGRS, acc) - -Need: -mist.getMGRSString(UnitNameTable, acc) -mist.getLeadingMGRSString(UnitNameTable, dir, radius, acc) - -mist.getLLString(UnitNameTable, acc) -mist.getLeadingLLString(UnitNameTable, dir, radius acc) - -mist.getBRString(UnitNameTable, ref, alt, metric) -mist.getLeadingBRString(UnitNameTable, ref, alt, metric, dir, radius, acc) -- vars versions? - - -mist.sendMGRSMsg(vars) -mist.sendLeadingMGRSMsg(vars) - -mist.sendLLMsg(vars) -mist.sendLeadingLLMsg(vars) - -mist.sendBRMsg(vars) -mist.sendLeadingBRMsg(vars) - -]] - ---[[RE-EXAMINE USAGE OF VARS FOR SIMPLE FUNCTIONS. -(Answer: the leading functions require a lot of input variables; maybe better to leave in vars -format for consistency. Maybe individual variable specification could also be supported?) - -]] - - ---[[ vars for mist.getMGRSString: -vars.units - table of unit names (NOT unitNameTable- maybe this should change). -vars.acc - integer between 0 and 5, inclusive -]] -mist.getMGRSString = function(vars) - local units = vars.units - local acc = vars.acc or 5 - local avgPos = mist.getAvgPos(units) - if avgPos then - return mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(avgPos)), acc) - end -end - ---[[ vars for mist.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. - - -]] -mist.getLLString = function(vars) - local units = vars.units - local acc = vars.acc or 3 - local DMS = vars.DMS - local avgPos = mist.getAvgPos(units) - if avgPos then - local lat, lon = coord.LOtoLL(avgPos) - return mist.tostringLL(lat, lon, acc, DMS) - 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. -]] -mist.getBRString = function(vars) - local units = vars.units - local ref = mist.utils.makeVec3(vars.ref, 0) -- turn it into Vec3 if it is not already. - local alt = vars.alt - local metric = vars.metric - local avgPos = mist.getAvgPos(units) - if avgPos then - local vec = {x = avgPos.x - ref.x, y = avgPos.y - ref.y, z = avgPos.z - ref.z} - local dir = mist.utils.getDir(vec, ref) - local dist = mist.utils.get2DDist(avgPos, ref) - if alt then - alt = avgPos.y - end - return mist.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 mist.getLeadingPos: -vars.units - table of unit names -vars.heading - direction -vars.radius - number -vars.headingDegrees - boolean, switches heading to degrees -]] -mist.getLeadingPos = function(vars) - local units = vars.units - local heading = vars.heading - local radius = vars.radius - if vars.headingDegrees then - heading = mist.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 = mist.vec.rotateVec2(mist.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 mist.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 mist.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. -]] -mist.getLeadingMGRSString = function(vars) - local pos = mist.getLeadingPos(vars) - if pos then - local acc = vars.acc or 5 - return mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(pos)), acc) - end -end - ---[[ vars for mist.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. -]] -mist.getLeadingLLString = function(vars) - local pos = mist.getLeadingPos(vars) - if pos then - local acc = vars.acc or 3 - local DMS = vars.DMS - local lat, lon = coord.LOtoLL(pos) - return mist.tostringLL(lat, lon, acc, DMS) - end -end - - - ---[[ vars for mist.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. -]] -mist.getLeadingBRString = function(vars) - local pos = mist.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 = mist.utils.getDir(vec, ref) - local dist = mist.utils.get2DDist(pos, ref) - if alt then - alt = pos.y - end - return mist.tostringBR(dir, dist, alt, metric) - end -end - ---[[ vars for mist.message.add - vars.text = 'Hello World' - vars.displayTime = 20 - vars.msgFor = {coa = {'red'}, countries = {'Ukraine', 'Georgia'}, unitTypes = {'A-10C'}} - -]] - ---[[ vars for mist.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 -]] -mist.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 = mist.getMGRSString{units = units, acc = acc} - local newText - if text then - 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 - else - newText = s - end - mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } -end - ---[[ vars for mist.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 -]] -mist.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 = mist.getLLString{units = units, acc = acc, DMS = DMS} - local newText - if text then - 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 - else - newText = s - end - - mist.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 -]] -mist.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 mist.getBRString - local alt = vars.alt - local metric = vars.metric - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor - - local s = mist.getBRString{units = units, ref = ref, alt = alt, metric = metric} - local newText - if text then - 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 - else - newText = s - end - - mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } - -end - - --------------------------------------------------------------------------------------------- --- basically, just sub-types of mist.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 -]] -mist.msgBullseye = function(vars) - if string.lower(vars.ref) == 'red' then - vars.ref = mist.DBs.missionData.bullseye.red - mist.msgBR(vars) - elseif string.lower(vars.ref) == 'blue' then - vars.ref = mist.DBs.missionData.bullseye.blue - mist.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 -]] - -mist.msgBRA = function(vars) - if Unit.getByName(vars.ref) and Unit.getByName(vars.ref):isExist() == true then - vars.ref = Unit.getByName(vars.ref):getPosition().p - if not vars.alt then - vars.alt = true - end - mist.msgBR(vars) - end -end --------------------------------------------------------------------------------------------- - ---[[ vars for mist.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 -]] -mist.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 = mist.getLeadingMGRSString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc} - local newText - if text then - 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 - else - newText = s - end - - mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } - - -end ---[[ vars for mist.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 -]] -mist.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 = mist.getLeadingLLString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc, DMS = DMS} - local newText - - if text then - 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 - else - newText = s - end - - mist.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 -]] -mist.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 mist.getBRString - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor - - local s = mist.getLeadingBRString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, metric = metric, alt = alt, ref = ref} - local newText - - if text then - 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 - else - newText = s - end - - mist.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } -end - - --- end of coordinate messages --------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------- --- start of sct Merge - -do -- all function uses of group and unit Ids must be in this do statement - - -mist.groupTableCheck = function(groupData) - local isOk = false - - if groupData.country then - isOk = true - end - if groupData.category then - isOk = true - else - isOk = false - end - if groupData.units then - for unitId, unitData in pairs(groupData.units) do - if unitData.x and unitData.y and unitData.type then - isOk = true - end - end - else - isOk = false - end - - return isOk -end - -mist.getCurrentGroupData = function(gpName) - local dbData = mist.getGroupData(gpName) - - if Group.getByName(gpName) and Group.getByName(gpName):isExist() == true then - local newGroup = Group.getByName(gpName) - local newData = {} - newData.name = gpName - newData.groupId = tonumber(newGroup:getID()) - newData.category = newGroup:getCategory() - newData.groupName = gpName - newData.hidden = dbData.hidden - - if newData.category == 2 then - newData.category = 'vehicle' - elseif newData.category == 3 then - newData.category = 'ship' - end - - newData.units = {} - local newUnits = newGroup:getUnits() - for unitNum, unitData in pairs(newGroup:getUnits()) do - newData.units[unitNum] = {} - newData.units[unitNum]["unitId"] = tonumber(unitData:getID()) - newData.units[unitNum]['point'] = unitData.point - newData.units[unitNum]['x'] = unitData:getPosition().p.x - newData.units[unitNum]['y'] = unitData:getPosition().p.z - newData.units[unitNum]["type"] = unitData:getTypeName() - newData.units[unitNum]["skill"] = mist.getUnitSkill(unitData:getName()) - - -- get velocity needed - newData.units[unitNum]["unitName"] = unitData:getName() - newData.units[unitNum]["heading"] = mist.getHeading(unitData, true) -- added to DBs - newData.units[unitNum]['alt'] = unitData:getPosition().p.y - newData.country = string.lower(country.name[unitData:getCountry()]) - newData.units[unitNum]['callsign'] = unitData:getCallsign() - end - - return newData - elseif StaticObject.getByName(gpName) and StaticObject.getByName(gpName):isExist() == true then - local staticObj = StaticObject.getByName(gpName) - dbData.units[1].x = staticObj:getPosition().p.x - dbData.units[1].y = staticObj:getPosition().p.z - dbData.units[1].alt = staticObj:getPosition().p.y - dbData.units[1].heading = mist.getHeading(staticObj, true) - - return dbData - end - -end - -mist.getGroupData = function(gpName) - local found = false - local newData = {} - if mist.DBs.groupsByName[gpName] then - newData = mist.utils.deepCopy(mist.DBs.groupsByName[gpName]) - found = true - end - - if found == false then - for groupName, groupData in pairs(mist.DBs.groupsByName) do - if mist.stringMatch(groupName, gpName) == true then - newData = mist.utils.deepCopy(groupData) - newData.groupName = groupName - found = true - break - end - end - end - - local payloads - if newData.category == 'plane' or newData.category == 'helicopter' then - payloads = mist.getGroupPayload(newData.groupName) - end - if found == true then - --newData.hidden = false -- maybe add this to DBs - - for unitNum, unitData in pairs(newData.units) do - newData.units[unitNum] = {} - - newData.units[unitNum]["unitId"] = unitData.unitId - --newData.units[unitNum]['point'] = unitData.point - newData.units[unitNum]['x'] = unitData.point.x - newData.units[unitNum]['y'] = unitData.point.y - newData.units[unitNum]['alt'] = unitData.alt - newData.units[unitNum]['alt_type'] = unitData.alt_type - newData.units[unitNum]['speed'] = unitData.speed - newData.units[unitNum]["type"] = unitData.type - newData.units[unitNum]["skill"] = unitData.skill - newData.units[unitNum]["unitName"] = unitData.unitName - newData.units[unitNum]["heading"] = unitData.heading -- added to DBs - newData.units[unitNum]["playerCanDrive"] = unitData.playerCanDrive -- added to DBs - - - if newData.category == 'plane' or newData.category == 'helicopter' then - newData.units[unitNum]["payload"] = payloads[unitNum] - newData.units[unitNum]['livery_id'] = unitData.livery_id - newData.units[unitNum]['onboard_num'] = unitData.onboard_num - newData.units[unitNum]['callsign'] = unitData.callsign - newData.units[unitNum]['AddPropAircraft'] = unitData.AddPropAircraft - end - if newData.category == 'static' then - newData.units[unitNum]['categoryStatic'] = unitData.categoryStatic - newData.units[unitNum]['mass'] = unitData.mass - newData.units[unitNum]['canCargo'] = unitData.canCargo - newData.units[unitNum]['shape_name'] = unitData.shape_name - end - end - - return newData - else - env.info(gpName .. ' not found in mist.getGroupData') - return - end -end - -mist.getPayload = function(unitIdent) - -- refactor to search by groupId and allow groupId and groupName as inputs - local unitId = unitIdent - if type(unitIdent) == 'string' and not tonumber(unitIdent) then - unitId = mist.DBs.MEunitsByName[unitIdent].unitId - end - local gpId = mist.DBs.MEunitsById[unitId].groupId - - if gpId and unitId then - 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 - for unitIndex, unitData in pairs(group_data.units) do --group index - if unitData.unitId == unitId then - return unitData.payload - end - end - end - end - end - end - end - end - end - end - end - else - env.info('mist.getPayload got ' .. type(unitIdent)) - return false - end - env.info('mist.getPayload, payload not found') - return -end - -mist.getGroupPayload = function(groupIdent) - local gpId = groupIdent - if type(groupIdent) == 'string' and not tonumber(groupIdent) then - gpId = mist.DBs.MEgroupsByName[groupIdent].groupId - end - - if gpId then - 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 - local payloads = {} - for unitIndex, unitData in pairs(group_data.units) do --group index - payloads[unitIndex] = unitData.payload - end - return payloads - end - end - end - end - end - end - end - end - end - else - env.info('mist.getGroupPayload got ' .. type(groupName)) - return false - end - env.info('mist.getGroupPayload, payload not found') - return - -end - -mist.teleportToPoint = function(vars) -- main teleport function that all of teleport/respawn functions call - local point = vars.point - - local gpName - if vars.gpName then - gpName = vars.gpName - elseif vars.groupName then - gpName = vars.groupName - else - env.info('teleportToPoint missing either vars.groupName or vars.gpName') - end - - local action = vars.action - - local disperse = vars.disperse or false - local maxDisp = vars.maxDisp - if not vars.maxDisp then - maxDisp = 200 - else - maxDisp = vars.maxDisp - end - local radius = vars.radius or 0 - local innerRadius = vars.innerRadius - - local route = vars.route - local dbData = false - - local newGroupData - if gpName and not vars.groupData then - if string.lower(action) == 'teleport' or string.lower(action) == 'tele' then - newGroupData = mist.getCurrentGroupData(gpName) - elseif string.lower(action) == 'respawn' then - newGroupData = mist.getGroupData(gpName) - dbData = true - elseif string.lower(action) == 'clone' then - newGroupData = mist.getGroupData(gpName) - newGroupData.clone = 'order66' - dbData = true - else - action = 'tele' - newGroupData = mist.getCurrentGroupData(gpName) - end - else - action = 'tele' - newGroupData = vars.groupData - end - - local diff = {['x'] = 0, ['y'] = 0} - local newCoord, origCoord - if point then - local valid = false - - local validTerrain - if string.lower(newGroupData.category) == 'ship' then - validTerrain = {'SHALLOW_WATER' , 'WATER'} - elseif string.lower(newGroupData.category) == 'vehicle' then - validTerrain = {'LAND', 'ROAD'} - else - validTerrain = {'LAND', 'ROAD', 'SHALLOW_WATER', 'WATER', 'RUNWAY'} - end - - for i = 1, 100 do - newCoord = mist.getRandPointInCircle(point, radius, innerRadius) - if mist.isTerrainValid(newCoord, validTerrain) then - origCoord = mist.utils.deepCopy(newCoord) - diff = {['x'] = (newCoord.x - newGroupData.units[1].x), ['y'] = (newCoord.y - newGroupData.units[1].y)} - valid = true - break - end - end - if valid == false then - env.info('mist.teleportToPoint; vars.point not a valid coordinate') - return false - end - end - if not newGroupData.country and mist.DBs.groupsByName[newGroupData.groupName].country then - newGroupData.country = mist.DBs.groupsByName[newGroupData.groupName].country - end - if not newGroupData.category and mist.DBs.groupsByName[newGroupData.groupName].category then - newGroupData.category = mist.DBs.groupsByName[newGroupData.groupName].category - end - - for unitNum, unitData in pairs(newGroupData.units) do - if disperse then - if maxDisp and type(maxDisp) == 'number' and unitNum ~= 1 then - newCoord = mist.getRandPointInCircle(origCoord, maxDisp) - --else - --newCoord = mist.getRandPointInCircle(zone.point, zone.radius) - end - - newGroupData.units[unitNum]['x'] = newCoord.x - newGroupData.units[unitNum]['y'] = newCoord.y - else - newGroupData.units[unitNum]["x"] = unitData.x + diff.x - newGroupData.units[unitNum]["y"] = unitData.y + diff.y - end - if point then - if (newGroupData.category == 'plane' or newGroupData.category == 'helicopter') then - if point.z and point.y > 0 and point.y > land.getHeight({newGroupData.units[unitNum]["x"], newGroupData.units[unitNum]["y"]}) + 10 then - newGroupData.units[unitNum]["alt"] = point.y - else - if newGroupData.category == 'plane' then - newGroupData.units[unitNum]["alt"] = land.getHeight({newGroupData.units[unitNum]["x"], newGroupData.units[unitNum]["y"]}) + math.random(300, 9000) - else - newGroupData.units[unitNum]["alt"] = land.getHeight({newGroupData.units[unitNum]["x"], newGroupData.units[unitNum]["y"]}) + math.random(200, 3000) - end - end - end - end - end - - if newGroupData.start_time then - newGroupData.startTime = newGroupData.start_time - end - - if newGroupData.startTime and newGroupData.startTime ~= 0 and dbData == true then - local timeDif = timer.getAbsTime() - timer.getTime0() - if timeDif > newGroupData.startTime then - newGroupData.startTime = 0 - else - newGroupData.startTime = newGroupData.startTime - timeDif - end - - end - - if route then - newGroupData.route = route - end - --mist.debug.writeData(mist.utils.serialize,{'teleportToPoint', newGroupData}, 'newGroupData.lua') - if string.lower(newGroupData.category) == 'static' then - return mist.dynAddStatic(newGroupData) - end - return mist.dynAdd(newGroupData) - -end - -mist.respawnInZone = function(gpName, zone, disperse, maxDisp) - - if type(gpName) == 'table' and gpName:getName() then - gpName = gpName:getName() - elseif type(gpName) == 'table' and gpName[1]:getName() then - gpName = math.random(#gpName) - else - gpName = tostring(gpName) - 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 - local vars = {} - vars.gpName = gpName - vars.action = 'respawn' - vars.point = zone.point - vars.radius = zone.radius - vars.disperse = disperse - vars.maxDisp = maxDisp - return mist.teleportToPoint(vars) -end - -mist.cloneInZone = function(gpName, zone, disperse, maxDisp) - - if type(gpName) == 'table' then - gpName = gpName:getName() - else - gpName = tostring(gpName) - 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 - local vars = {} - vars.gpName = gpName - vars.action = 'clone' - vars.point = zone.point - vars.radius = zone.radius - vars.disperse = disperse - vars.maxDisp = maxDisp - return mist.teleportToPoint(vars) -end - -mist.teleportInZone = function(gpName, zone, disperse, maxDisp) -- groupName, zoneName or table of Zone Names, keepForm is a boolean - if type(gpName) == 'table' and gpName:getName() then - gpName = gpName:getName() - else - gpName = tostring(gpName) - 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 - - local vars = {} - vars.gpName = gpName - vars.action = 'tele' - vars.point = zone.point - vars.radius = zone.radius - vars.disperse = disperse - vars.maxDisp = maxDisp - return mist.teleportToPoint(vars) -end - -mist.respawnGroup = function(gpName, task) - local vars = {} - vars.gpName = gpName - vars.action = 'respawn' - if task and type(task) ~= 'number' then - vars.route = mist.getGroupRoute(gpName, 'task') - end - local newGroup = mist.teleportToPoint(vars) - if task and type(task) == 'number' then - local newRoute = mist.getGroupRoute(gpName, 'task') - mist.scheduleFunction(mist.goRoute, {newGroup, newRoute}, timer.getTime() + task) - end - return newGroup -end - -mist.cloneGroup = function(gpName, task) - local vars = {} - vars.gpName = gpName - vars.action = 'clone' - if task and type(task) ~= 'number' then - vars.route = mist.getGroupRoute(gpName, 'task') - end - local newGroup = mist.teleportToPoint(vars) - if task and type(task) == 'number' then - local newRoute = mist.getGroupRoute(gpName, 'task') - mist.scheduleFunction(mist.goRoute, {newGroup, newRoute}, timer.getTime() + task) - end - return newGroup -end - -mist.teleportGroup = function(gpName, task) - local vars = {} - vars.gpName = gpName - vars.action = 'teleport' - if task and type(task) ~= 'number' then - vars.route = mist.getGroupRoute(gpName, 'task') - end - local newGroup = mist.teleportToPoint(vars) - if task and type(task) == 'number' then - local newRoute = mist.getGroupRoute(gpName, 'task') - mist.scheduleFunction(mist.goRoute, {newGroup, newRoute}, timer.getTime() + task) - end - return newGroup -end - -mist.spawnRandomizedGroup = function(groupName, vars) -- need to debug - if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then - local gpData = mist.getGroupData(groupName) - gpData.units = mist.randomizeGroupOrder(gpData.units, vars) - gpData.route = mist.getGroupRoute(groupName, 'task') - - mist.dynAdd(gpData) - end - - return true -end - -mist.randomizeNumTable = function(vars) - local newTable = {} - - local excludeIndex = {} - local randomTable = {} - - if vars and vars.exclude and type(vars.exclude) == 'table' then - for index, data in pairs(vars.exclude) do - excludeIndex[data] = true - end - end - - local low, hi, size - - if vars.size then - size = vars.size - end - - if vars and vars.lowerLimit and type(vars.lowerLimit) == 'number' then - low = mist.utils.round(vars.lowerLimit) - else - low = 1 - end - - if vars and vars.upperLimit and type(vars.upperLimit) == 'number' then - hi = mist.utils.round(vars.upperLimit) - else - hi = size - end - - local choices = {} - -- add to exclude list and create list of what to randomize - for i = 1, size do - if not (i >= low and i <= hi) then - - excludeIndex[i] = true - end - if not excludeIndex[i] then - table.insert(choices, i) - else - newTable[i] = i - end - end - - for ind, num in pairs(choices) do - local found = false - local x = 0 - while found == false do - x = mist.random(size) -- get random number from list - local addNew = true - for index, _ in pairs(excludeIndex) do - if index == x then - addNew = false - break - end - end - if addNew == true then - excludeIndex[x] = true - found = true - end - excludeIndex[x] = true - - end - newTable[num] = x - end - --[[ - for i = 1, #newTable do - env.info(newTable[i]) - end - ]] - return newTable -end - -mist.randomizeGroupOrder = function(passedUnits, vars) - -- figure out what to exclude, and send data to other func - local units = passedUnits - - if passedUnits.units then - units = passUnits.units - end - - local exclude = {} - local excludeNum = {} - if vars and vars.excludeType and type(vars.excludeType) == 'table' then - exclude = vars.excludeType - end - - if vars and vars.excludeNum and type(vars.excludeNum) == 'table' then - excludeNum = vars.excludeNum - end - - local low, hi - - if vars and vars.lowerLimit and type(vars.lowerLimit) == 'number' then - low = mist.utils.round(vars.lowerLimit) - else - low = 1 - end - - if vars and vars.upperLimit and type(vars.upperLimit) == 'number' then - hi = mist.utils.round(vars.upperLimit) - else - hi = #units - end - - - local excludeNum = {} - for unitIndex, unitData in pairs(units) do - if unitIndex >= low and unitIndex <= hi then -- if within range - local found = false - if #exclude > 0 then - for excludeType, index in pairs(exclude) do -- check if excluded - if mist.stringMatch(excludeType, unitData.type) then -- if excluded - excludeNum[unitIndex] = unitIndex - found = true - end - end - end - else -- unitIndex is either to low, or to high: added to exclude list - excludeNum[unitIndex] = unitId - end - end - - local newGroup = {} - local newOrder = mist.randomizeNumTable({exclude = excludeNum, size = #units}) - - for unitIndex, unitData in pairs(units) do - for i = 1, #newOrder do - if newOrder[i] == unitIndex then - newGroup[i] = mist.utils.deepCopy(units[i]) -- gets all of the unit data - newGroup[i].type = mist.utils.deepCopy(unitData.type) - newGroup[i].skill = mist.utils.deepCopy(unitData.skill) - newGroup[i].unitName = mist.utils.deepCopy(unitData.unitName) - newGroup[i].unitIndex = mist.utils.deepCopy(unitData.unitIndex) -- replaces the units data with a new type - end - end - end - return newGroup -end - -mist.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 = mist.getGroupRoute(useGroupRoute) - end - else - useRoute = vars.route - local posStart = mist.getLeadPos(gpData) - useRoute[1] = mist.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 = mist.getLeadPos(gpData) - - - useRoute[#useRoute + 1] = mist.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] = mist.ground.buildWP(tempRoute[i], tempForm, tempSpeed) - end - - if pType and string.lower(pType) == 'doubleback' then - local curRoute = mist.utils.deepCopy(useRoute) - for i = #curRoute, 2, -1 do - useRoute[#useRoute + 1] = mist.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] = 'mist.ground.patrolRoute(' - cTask3[#cTask3 + 1] = mist.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 - mist.goRoute(gpData, useRoute) - - return -end - -mist.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 - - mist.ground.patrolRoute(vars) - - return -end - - -mist.random = function(firstNum, secondNum) -- no support for decimals - local lowNum, highNum - if not secondNum then - highNum = firstNum - lowNum = 1 - else - lowNum = firstNum - highNum = secondNum - end - local total = 1 - if math.abs(highNum - lowNum + 1) < 50 then -- if total values is less than 50 - total = math.modf(50/math.abs(highNum - lowNum + 1)) -- make x copies required to be above 50 - end - local choices = {} - for i = 1, total do -- iterate required number of times - for x = lowNum, highNum do -- iterate between the range - choices[#choices +1] = x -- add each entry to a table - end - end - local rtnVal = math.random(#choices) -- will now do a math.random of at least 50 choices - for i = 1, 10 do - rtnVal = math.random(#choices) -- iterate a few times for giggles - end - return choices[rtnVal] -end - - - -mist.stringMatch = function(s1, s2, bool) - local exclude = {'%-', '%(', '%)', '%_', '%[', '%]', '%.', '%#', '% ', '%{', '%}', '%$', '%%', '%?', '%+', '%^'} - if type(s1) == 'string' and type(s2) == 'string' then - for i , str in pairs(exclude) do - s1 = string.gsub(s1, str, '') - s2 = string.gsub(s2, str, '') - end - if not bool then - s1 = string.lower(s1) - s2 = string.lower(s2) - end - - if s1 == s2 then - return true - else - return false - end - else - env.info('mist.stringMatch; Either the first or second variable were not a string') - return false - end -end - -mist.matchString = mist.stringMatch -- both commands work because order out type of I - -mist.time = {} --- returns a string for specified military time --- theTime is optional --- if present current time in mil time is returned --- if number or table the time is converted into mil tim -mist.time.convertToSec = function(timeTable) - - timeInSec = 0 - if timeTable and type(timeTable) == 'number' then - timeInSec = timeTable - elseif timeTable and type(timeTable) == 'table' and (timeTable.d or timeTable.h or timeTable.m or timeTable.s) then - if timeTable.d and type(timeTable.d) == 'number' then - timeInSec = timeInSec + (timeTable.d*86400) - end - if timeTable.h and type(timeTable.h) == 'number' then - timeInSec = timeInSec + (timeTable.h*3600) - end - if timeTable.m and type(timeTable.m) == 'number' then - timeInSec = timeInSec + (timeTable.m*60) - end - if timeTable.s and type(timeTable.s) == 'number' then - timeInSec = timeInSec + timeTable.s - end - - end - return timeInSec -end - -mist.time.getDHMS = function(timeInSec) - if timeInSec and type(timeInSec) == 'number' then - local tbl = {d = 0, h = 0, m = 0, s = 0} - if timeInSec > 86400 then - while timeInSec > 86400 do - tbl.d = tbl.d + 1 - timeInSec = timeInSec - 86400 - end - end - if timeInSec > 3600 then - while timeInSec > 3600 do - tbl.h = tbl.h + 1 - timeInSec = timeInSec - 3600 - end - end - if timeInSec > 60 then - while timeInSec > 60 do - tbl.m = tbl.m + 1 - timeInSec = timeInSec - 60 - end - end - tbl.s = timeInSec - return tbl - else - env.info('mist.time.getDHMS didnt recieve number') - return - end -end - -mist.getMilString = function(theTime) - local timeInSec = 0 - if theTime then - timeInSec = mist.time.convertToSec(theTime) - else - timeInSec = mist.utils.round(timer.getAbsTime(), 0) - end - - local DHMS = mist.time.getDHMS(timeInSec) - - return tostring(string.format('%02d', DHMS.h) .. string.format('%02d',DHMS.m)) -end - -mist.getClockString = function(theTime, hour) - local timeInSec = 0 - if theTime then - timeInSec = mist.time.convertToSec(theTime) - else - timeInSec = mist.utils.round(timer.getAbsTime(), 0) - end - local DHMS = mist.time.getDHMS(timeInSec) - if hour then - if DHMS.h > 12 then - DHMS.h = DHMS.h - 12 - return tostring(string.format('%02d', DHMS.h) .. ':' .. string.format('%02d',DHMS.m) .. ':' .. string.format('%02d',DHMS.s) .. ' PM') - else - return tostring(string.format('%02d', DHMS.h) .. ':' .. string.format('%02d',DHMS.m) .. ':' .. string.format('%02d',DHMS.s) .. ' AM') - end - else - return tostring(string.format('%02d', DHMS.h) .. ':' .. string.format('%02d',DHMS.m) .. ':' .. string.format('%02d',DHMS.s)) - end -end - --- returns the date in string format --- both variables optional --- first val returns with the month as a string --- 2nd val defins if it should be written the American way or the wrong way. -mist.time.getDate = function(convert) - local cal = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} -- starts at june. 2011. 2012 is leap year, sigh. add a simple check for leap year - local date = {} - date.d = 0 - date.m = 6 - date.y = 2011 - - local start = 0 - local timeInSec = mist.utils.round(timer.getAbsTime()) - if convert and type(convert) == 'number' then - timeInSec = convert - end - - while start < timeInSec do - - if date.d == cal[date.m] then - --if y % 4 >= 0 and i == 2 then -- for leap year. DCS doesnt do leapyear, but I am keeping the code dormant because maybe with WW2 the mission year may become relevant - - --else - date.m = date.m + 1 - date.d = 0 - --end - end - if date.m == 13 then - date.m = 1 - date.y = date.y + 1 - end - date.d = date.d + 1 - start = start + 86400 - end - return date -end - -mist.time.relativeToStart = function(time) - if type(time) == 'number' then - return time - timer.getTime0() - end -end - -mist.getDateString = function(rtnType, murica, oTime) -- returns date based on time - local word = {'January', 'Feburary', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' } -- 'etc - local curTime = 0 - if oTime then - curTime = oTime - else - curTime = mist.utils.round(timer.getAbsTime()) - end - local tbl = mist.time.getDate(curTime) - - if rtnType then - if murica then - return tostring(word[tbl.m] .. ' ' .. tbl.d .. ' ' .. tbl.y) - else - return tostring(tbl.d .. ' ' .. word[tbl.m] .. ' ' .. tbl.y) - end - else - if murica then - return tostring(tbl.m .. '.' .. tbl.d .. '.' .. tbl.y) - else - return tostring(tbl.d .. '.' .. tbl.m .. '.' .. tbl.y) - end - end -end ---WIP -mist.time.milToGame = function(milString, rtnType) --converts a military time. By default returns the abosolute time that event would occur. With optional value it returns how many seconds from time of call till that time. - local curTime = mist.utils.round(timer.getAbsTime()) - local milTimeInSec = 0 - - if milString and type(milString) == 'string' and string.len(milString) >= 4 then - local hr = tonumber(string.sub(milString, 1, 2)) - local mi = tonumber(string.sub(milString, 3)) - milTimeInSec = milTimeInSec + (mi*60) + (hr*3600) - elseif milString and type(milString) == 'table' and (milString.d or milString.h or milString.m or milString.s) then - milTimeInSec = mist.time.convertToSec(milString) - end - - local startTime = timer.getTime0() - local daysOffset = 0 - if startTime > 86400 then - daysOffset = mist.utils.round(startTime/86400) - if daysOffset > 0 then - milTimeInSec = milTimeInSec *daysOffset - end - end - - if curTime > milTimeInSec then - milTimeInSec = milTimeInSec + 86400 - end - if rtnType then - milTimeInSec = milTimeInSec - startTime - end - return milTimeInSec -end - -mist.DBs.const = {} - ---[[ - ['LAND'] = 1, - ['SHALLOW_WATER'] = 2, - ['WATER'] = 3, - ['ROAD'] = 4, - ['RUNWAY'] = 5 -]] ---[[mist.DBs.const.ME_SSE_terms = { - ['ME'] = { - ['vehicle'] = {'GROUND', 'GROUND_UNIT'}, - ['plane'] = {'AIRPLANE'}, - }, - ['SSE'] = { - }, - -}]] - - -mist.DBs.const.callsigns = { -- not accessible by SSE, must use static list :-/ - ['NATO'] = { - ['rules'] = { - ['groupLimit'] = 9, - }, - ['AWACS'] = { - ['Overlord'] = 1, - ['Magic'] = 2, - ['Wizard'] = 3, - ['Focus'] = 4, - ['Darkstar'] = 5, - }, - ['TANKER'] = { - ['Texaco'] = 1, - ['Arco'] = 2, - ['Shell'] = 3, - }, - ['JTAC'] = { - ['Axeman'] = 1, - ['Darknight'] = 2, - ['Warrior'] = 3, - ['Pointer'] = 4, - ['Eyeball'] = 5, - ['Moonbeam'] = 6, - ['Whiplash'] = 7, - ['Finger'] = 8, - ['Pinpoint'] = 9, - ['Ferret'] = 10, - ['Shaba'] = 11, - ['Playboy'] = 12, - ['Hammer'] = 13, - ['Jaguar'] = 14, - ['Deathstar'] = 15, - ['Anvil'] = 16, - ['Firefly'] = 17, - ['Mantis'] = 18, - ['Badger'] = 19, - }, - ['aircraft'] = { - ['Enfield'] = 1, - ['Springfield'] = 2, - ['Uzi'] = 3, - ['Colt'] = 4, - ['Dodge'] = 5, - ['Ford'] = 6, - ['Chevy'] = 7, - ['Pontiac'] = 8, - }, - - ['unique'] = { - ['A10'] = { - ['Hawg'] = 9, - ['Boar'] = 10, - ['Pig'] = 11, - ['Tusk'] = 12, - ['rules'] = { - ['canUseAircraft'] = true, - ['appliesTo'] = { - 'A-10C', - 'A-10A', - }, - }, - }, - }, - }, - -} ---[[ scope: - { - units = {...}, -- unit names. - coa = {...}, -- coa names - countries = {...}, -- country names - CA = {...}, -- looks just like coa. - unitTypes = { red = {}, blue = {}, all = {}, Russia = {},} - } - - -scope examples: - -{ units = { 'Hawg11', 'Hawg12' }, CA = {'blue'} } - -{ countries = {'Georgia'}, unitTypes = {blue = {'A-10C', 'A-10A'}}} - -{ coa = {'all'}} - -{unitTypes = { blue = {'A-10C'}}} -]] -end -mist.main() -env.info(('Mist version ' .. mist.majorVersion .. '.' .. mist.minorVersion .. '.' .. mist.build .. ' loaded.')) diff --git a/mist_4_2_66.lua b/mist_4_2_66.lua new file mode 100644 index 0000000..1dfed03 --- /dev/null +++ b/mist_4_2_66.lua @@ -0,0 +1,6620 @@ +--[[-- +MIST Mission Scripting Tools. +## Description: +MIssion Scripting Tools (MIST) is a collection of Lua functions +and databases that is intended to be a supplement to the standard +Lua functions included in the simulator scripting engine. + +MIST functions and databases provide ready-made solutions to many common +scripting tasks and challenges, enabling easier scripting and saving +mission scripters time. The table mist.flagFuncs contains a set of +Lua functions (that are similar to Slmod functions) that do not +require detailed Lua knowledge to use. + +However, the majority of MIST does require knowledge of the Lua language, +and, if you are going to utilize these components of MIST, it is necessary +that you read the Simulator Scripting Engine guide on the official ED wiki. + +## Links: + +ED Forum Thread: + +##Github: + +Development + +Official Releases + +@script MIST +@author Speed +@author Grimes +@author lukrop +]] +mist = {} + +-- don't change these +mist.majorVersion = 4 +mist.minorVersion = 2 +mist.build = 66 + +-- forward declaration of log shorthand +local log + +do -- the main scope + local coroutines = {} + + local tempSpawnedUnits = {} -- birth events added here + local mistAddedObjects = {} -- mist.dynAdd unit data added here + local mistAddedGroups = {} -- mist.dynAdd groupdata added here + local writeGroups = {} + local lastUpdateTime = 0 + + local updateAliveUnitsCounter = 0 + local writeDbTableCounter = 0 + local checkSpawnEventsCounter = 0 + + local mistGpId = 7000 + local mistUnitId = 7000 + local mistDynAddIndex = 1 + + local scheduledTasks = {} + local taskId = 0 + local idNum = 0 + + mist.nextGroupId = 1 + mist.nextUnitId = 1 + + local function initDBs() -- mist.DBs scope + mist.DBs = {} + + mist.DBs.missionData = {} + if env.mission then + + mist.DBs.missionData.startTime = env.mission.start_time + mist.DBs.missionData.theatre = env.mission.theatre + mist.DBs.missionData.version = env.mission.version + mist.DBs.missionData.files = {} + if type(env.mission.resourceCounter) == 'table' then + for fIndex, fData in pairs (env.mission.resourceCounter) do + mist.DBs.missionData.files[#mist.DBs.missionData.files + 1] = mist.utils.deepCopy(fIndex) + end + end + -- if we add more coalition specific data then bullsye should be categorized by coaliton. For now its just the bullseye table + mist.DBs.missionData.bullseye = {red = {}, blue = {}} + mist.DBs.missionData.bullseye.red.x = env.mission.coalition.red.bullseye.x --should it be point.x? + mist.DBs.missionData.bullseye.red.y = env.mission.coalition.red.bullseye.y + mist.DBs.missionData.bullseye.blue.x = env.mission.coalition.blue.bullseye.x + mist.DBs.missionData.bullseye.blue.y = env.mission.coalition.blue.bullseye.y + end + + mist.DBs.zonesByName = {} + mist.DBs.zonesByNum = {} + + + if env.mission.triggers and env.mission.triggers.zones then + for zone_ind, zone_data in pairs(env.mission.triggers.zones) do + if type(zone_data) == 'table' then + local zone = mist.utils.deepCopy(zone_data) + zone.point = {} -- point is used by SSE + zone.point.x = zone_data.x + zone.point.y = 0 + zone.point.z = zone_data.y + + mist.DBs.zonesByName[zone_data.name] = zone + mist.DBs.zonesByNum[#mist.DBs.zonesByNum + 1] = mist.utils.deepCopy(zone) --[[deepcopy so that the zone in zones_by_name and the zone in + zones_by_num se are different objects.. don't want them linked.]] + end + end + end + + mist.DBs.navPoints = {} + mist.DBs.units = {} + --Build mist.db.units and mist.DBs.navPoints + 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 + mist.DBs.units[coa_name] = {} + + -- build nav points DB + mist.DBs.navPoints[coa_name] = {} + if coa_data.nav_points then --navpoints + --mist.debug.writeData (mist.utils.serialize,{'NavPoints',coa_data.nav_points}, 'NavPoints.txt') + for nav_ind, nav_data in pairs(coa_data.nav_points) do + + if type(nav_data) == 'table' then + mist.DBs.navPoints[coa_name][nav_ind] = mist.utils.deepCopy(nav_data) + + mist.DBs.navPoints[coa_name][nav_ind].name = nav_data.callsignStr -- name is a little bit more self-explanatory. + mist.DBs.navPoints[coa_name][nav_ind].point = {} -- point is used by SSE, support it. + mist.DBs.navPoints[coa_name][nav_ind].point.x = nav_data.x + mist.DBs.navPoints[coa_name][nav_ind].point.y = 0 + mist.DBs.navPoints[coa_name][nav_ind].point.z = nav_data.y + end + end + end + if coa_data.country then --there is a country table + for cntry_id, cntry_data in pairs(coa_data.country) do + + local countryName = string.lower(cntry_data.name) + mist.DBs.units[coa_name][countryName] = {} + mist.DBs.units[coa_name][countryName].countryId = cntry_data.id + + if type(cntry_data) == 'table' then --just making sure + + 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" or obj_type_name == "static" then --should be an unncessary check + + local category = obj_type_name + + 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! + + mist.DBs.units[coa_name][countryName][category] = {} + + for group_num, group_data in pairs(obj_type_data.group) do + + if group_data and group_data.units and type(group_data.units) == 'table' then --making sure again- this is a valid group + + mist.DBs.units[coa_name][countryName][category][group_num] = {} + local groupName = group_data.name + if env.mission.version > 7 then + groupName = env.getValueDictByKey(groupName) + end + mist.DBs.units[coa_name][countryName][category][group_num].groupName = groupName + mist.DBs.units[coa_name][countryName][category][group_num].groupId = group_data.groupId + mist.DBs.units[coa_name][countryName][category][group_num].category = category + mist.DBs.units[coa_name][countryName][category][group_num].coalition = coa_name + mist.DBs.units[coa_name][countryName][category][group_num].country = countryName + mist.DBs.units[coa_name][countryName][category][group_num].countryId = cntry_data.id + mist.DBs.units[coa_name][countryName][category][group_num].startTime = group_data.start_time + mist.DBs.units[coa_name][countryName][category][group_num].task = group_data.task + mist.DBs.units[coa_name][countryName][category][group_num].hidden = group_data.hidden + + mist.DBs.units[coa_name][countryName][category][group_num].units = {} + + mist.DBs.units[coa_name][countryName][category][group_num].radioSet = group_data.radioSet + mist.DBs.units[coa_name][countryName][category][group_num].uncontrolled = group_data.uncontrolled + mist.DBs.units[coa_name][countryName][category][group_num].frequency = group_data.frequency + mist.DBs.units[coa_name][countryName][category][group_num].modulation = group_data.modulation + + for unit_num, unit_data in pairs(group_data.units) do + local units_tbl = mist.DBs.units[coa_name][countryName][category][group_num].units --pointer to the units table for this group + + units_tbl[unit_num] = {} + if env.mission.version > 7 then + units_tbl[unit_num].unitName = env.getValueDictByKey(unit_data.name) + else + units_tbl[unit_num].unitName = unit_data.name + end + units_tbl[unit_num].type = unit_data.type + units_tbl[unit_num].skill = unit_data.skill --will be nil for statics + units_tbl[unit_num].unitId = unit_data.unitId + units_tbl[unit_num].category = category + units_tbl[unit_num].coalition = coa_name + units_tbl[unit_num].country = countryName + units_tbl[unit_num].countryId = cntry_data.id + units_tbl[unit_num].heading = unit_data.heading + units_tbl[unit_num].playerCanDrive = unit_data.playerCanDrive + units_tbl[unit_num].alt = unit_data.alt + units_tbl[unit_num].alt_type = unit_data.alt_type + units_tbl[unit_num].speed = unit_data.speed + units_tbl[unit_num].livery_id = unit_data.livery_id + if unit_data.point then --ME currently does not work like this, but it might one day + units_tbl[unit_num].point = unit_data.point + else + units_tbl[unit_num].point = {} + units_tbl[unit_num].point.x = unit_data.x + units_tbl[unit_num].point.y = unit_data.y + end + units_tbl[unit_num].x = unit_data.x + units_tbl[unit_num].y = unit_data.y + + units_tbl[unit_num].callsign = unit_data.callsign + units_tbl[unit_num].onboard_num = unit_data.onboard_num + units_tbl[unit_num].hardpoint_racks = unit_data.hardpoint_racks + units_tbl[unit_num].psi = unit_data.psi + + + units_tbl[unit_num].groupName = groupName + units_tbl[unit_num].groupId = group_data.groupId + + if unit_data.AddPropAircraft then + units_tbl[unit_num].AddPropAircraft = unit_data.AddPropAircraft + end + + if category == 'static' then + units_tbl[unit_num].categoryStatic = unit_data.category + units_tbl[unit_num].shape_name = unit_data.shape_name + if unit_data.mass then + units_tbl[unit_num].mass = unit_data.mass + end + + if unit_data.canCargo then + units_tbl[unit_num].canCargo = unit_data.canCargo + end + end + + end --for unit_num, unit_data in pairs(group_data.units) do + end --if group_data and group_data.units then + 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 --if type(cntry_data) == 'table' then + 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 + + mist.DBs.unitsByName = {} + mist.DBs.unitsById = {} + mist.DBs.unitsByCat = {} + + mist.DBs.unitsByCat.helicopter = {} -- adding default categories + mist.DBs.unitsByCat.plane = {} + mist.DBs.unitsByCat.ship = {} + mist.DBs.unitsByCat.static = {} + mist.DBs.unitsByCat.vehicle = {} + + mist.DBs.unitsByNum = {} + + mist.DBs.groupsByName = {} + mist.DBs.groupsById = {} + mist.DBs.humansByName = {} + mist.DBs.humansById = {} + + mist.DBs.dynGroupsAdded = {} -- will be filled by mist.dbUpdate from dynamically spawned groups + mist.DBs.activeHumans = {} + + mist.DBs.aliveUnits = {} -- will be filled in by the "updateAliveUnits" coroutine in mist.main. + + mist.DBs.removedAliveUnits = {} -- will be filled in by the "updateAliveUnits" coroutine in mist.main. + + mist.DBs.const = {} + + -- not accessible by SSE, must use static list :-/ + mist.DBs.const.callsigns = { + ['NATO'] = { + ['rules'] = { + ['groupLimit'] = 9, + }, + ['AWACS'] = { + ['Overlord'] = 1, + ['Magic'] = 2, + ['Wizard'] = 3, + ['Focus'] = 4, + ['Darkstar'] = 5, + }, + ['TANKER'] = { + ['Texaco'] = 1, + ['Arco'] = 2, + ['Shell'] = 3, + }, + ['JTAC'] = { + ['Axeman'] = 1, + ['Darknight'] = 2, + ['Warrior'] = 3, + ['Pointer'] = 4, + ['Eyeball'] = 5, + ['Moonbeam'] = 6, + ['Whiplash'] = 7, + ['Finger'] = 8, + ['Pinpoint'] = 9, + ['Ferret'] = 10, + ['Shaba'] = 11, + ['Playboy'] = 12, + ['Hammer'] = 13, + ['Jaguar'] = 14, + ['Deathstar'] = 15, + ['Anvil'] = 16, + ['Firefly'] = 17, + ['Mantis'] = 18, + ['Badger'] = 19, + }, + ['aircraft'] = { + ['Enfield'] = 1, + ['Springfield'] = 2, + ['Uzi'] = 3, + ['Colt'] = 4, + ['Dodge'] = 5, + ['Ford'] = 6, + ['Chevy'] = 7, + ['Pontiac'] = 8, + }, + + ['unique'] = { + ['A10'] = { + ['Hawg'] = 9, + ['Boar'] = 10, + ['Pig'] = 11, + ['Tusk'] = 12, + ['rules'] = { + ['canUseAircraft'] = true, + ['appliesTo'] = { + 'A-10C', + 'A-10A', + }, + }, + }, + }, + }, + } + -- create mist.DBs.oldAliveUnits + -- do + -- local intermediate_alive_units = {} -- between 0 and 0.5 secs old + -- local function make_old_alive_units() -- called every 0.5 secs, makes the old_alive_units DB which is just a copy of alive_units that is 0.5 to 1 sec old + -- if intermediate_alive_units then + -- mist.DBs.oldAliveUnits = mist.utils.deepCopy(intermediate_alive_units) + -- end + -- intermediate_alive_units = mist.utils.deepCopy(mist.DBs.aliveUnits) + -- timer.scheduleFunction(make_old_alive_units, nil, timer.getTime() + 0.5) + -- end + + -- make_old_alive_units() + -- end + + --Build DBs + for coa_name, coa_data in pairs(mist.DBs.units) do + for cntry_name, cntry_data in pairs(coa_data) do + for category_name, category_data in pairs(cntry_data) do + if type(category_data) == 'table' then + for group_ind, group_data in pairs(category_data) do + if type(group_data) == 'table' and group_data.units and type(group_data.units) == 'table' and #group_data.units > 0 then -- OCD paradigm programming + mist.DBs.groupsByName[group_data.groupName] = mist.utils.deepCopy(group_data) + mist.DBs.groupsById[group_data.groupId] = mist.utils.deepCopy(group_data) + for unit_ind, unit_data in pairs(group_data.units) do + mist.DBs.unitsByName[unit_data.unitName] = mist.utils.deepCopy(unit_data) + mist.DBs.unitsById[unit_data.unitId] = mist.utils.deepCopy(unit_data) + + mist.DBs.unitsByCat[unit_data.category] = mist.DBs.unitsByCat[unit_data.category] or {} -- future-proofing against new categories... + table.insert(mist.DBs.unitsByCat[unit_data.category], mist.utils.deepCopy(unit_data)) + --log:info('inserting $1', unit_data.unitName) + table.insert(mist.DBs.unitsByNum, mist.utils.deepCopy(unit_data)) + + if unit_data.skill and (unit_data.skill == "Client" or unit_data.skill == "Player") then + mist.DBs.humansByName[unit_data.unitName] = mist.utils.deepCopy(unit_data) + mist.DBs.humansById[unit_data.unitId] = mist.utils.deepCopy(unit_data) + --if Unit.getByName(unit_data.unitName) then + -- mist.DBs.activeHumans[unit_data.unitName] = mist.utils.deepCopy(unit_data) + -- mist.DBs.activeHumans[unit_data.unitName].playerName = Unit.getByName(unit_data.unitName):getPlayerName() + --end + end + end + end + end + end + end + end + end + + --DynDBs + mist.DBs.MEunits = mist.utils.deepCopy(mist.DBs.units) + mist.DBs.MEunitsByName = mist.utils.deepCopy(mist.DBs.unitsByName) + mist.DBs.MEunitsById = mist.utils.deepCopy(mist.DBs.unitsById) + mist.DBs.MEunitsByCat = mist.utils.deepCopy(mist.DBs.unitsByCat) + mist.DBs.MEunitsByNum = mist.utils.deepCopy(mist.DBs.unitsByNum) + mist.DBs.MEgroupsByName = mist.utils.deepCopy(mist.DBs.groupsByName) + mist.DBs.MEgroupsById = mist.utils.deepCopy(mist.DBs.groupsById) + + mist.DBs.deadObjects = {} + + do + local mt = {} + + function mt.__newindex(t, key, val) + local original_key = key --only for duplicate runtime IDs. + local key_ind = 1 + while mist.DBs.deadObjects[key] do + --log:warn('duplicate runtime id of previously dead object key: $1', key) + key = tostring(original_key) .. ' #' .. tostring(key_ind) + key_ind = key_ind + 1 + end + + if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then + --log:info('object found in alive_units') + val.objectData = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) + local pos = Object.getPosition(val.object) + if pos then + val.objectPos = pos.p + end + val.objectType = mist.DBs.aliveUnits[val.object.id_].category + + elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units + --log:info('object found in old_alive_units') + val.objectData = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) + local pos = Object.getPosition(val.object) + if pos then + val.objectPos = pos.p + end + val.objectType = mist.DBs.removedAliveUnits[val.object.id_].category + + else --attempt to determine if static object... + --log:info('object not found in alive units or old alive units') + local pos = Object.getPosition(val.object) + if pos then + local static_found = false + for ind, static in pairs(mist.DBs.unitsByCat.static) do + if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... + --log:info('correlated dead static object to position') + val.objectData = static + val.objectPos = pos.p + val.objectType = 'static' + static_found = true + break + end + end + if not static_found then + val.objectPos = pos.p + val.objectType = 'building' + end + else + val.objectType = 'unknown' + end + end + rawset(t, key, val) + end + + setmetatable(mist.DBs.deadObjects, mt) + end + + do -- mist unitID funcs + for id, idData in pairs(mist.DBs.unitsById) do + if idData.unitId > mist.nextUnitId then + mist.nextUnitId = mist.utils.deepCopy(idData.unitId) + end + if idData.groupId > mist.nextGroupId then + mist.nextGroupId = mist.utils.deepCopy(idData.groupId) + end + end + end + + + end + + local function updateAliveUnits() -- coroutine function + local lalive_units = mist.DBs.aliveUnits -- local references for faster execution + local lunits = mist.DBs.unitsByNum + local ldeepcopy = mist.utils.deepCopy + local lUnit = Unit + local lremovedAliveUnits = mist.DBs.removedAliveUnits + local updatedUnits = {} + + if #lunits > 0 then + local units_per_run = math.ceil(#lunits/20) + if units_per_run < 5 then + units_per_run = 5 + end + + for i = 1, #lunits do + if lunits[i].category ~= 'static' then -- can't get statics with Unit.getByName :( + local unit = lUnit.getByName(lunits[i].unitName) + if unit then + --log:info("unit named $1 alive!", lunits[i].unitName) -- spammy + local pos = unit:getPosition() + local newtbl = ldeepcopy(lunits[i]) + if pos then + newtbl.pos = pos.p + end + newtbl.unit = unit + --newtbl.rt_id = unit.id_ + lalive_units[unit.id_] = newtbl + updatedUnits[unit.id_] = true + end + end + if i%units_per_run == 0 then + coroutine.yield() + end + end + -- All units updated, remove any "alive" units that were not updated- they are dead! + for unit_id, unit in pairs(lalive_units) do + if not updatedUnits[unit_id] then + lremovedAliveUnits[unit_id] = unit + lalive_units[unit_id] = nil + end + end + end + end + + local function dbUpdate(event) + local newTable = {} + + newTable.startTime = 0 + + if type(event) == 'string' then -- if name of an object. + local newObject + local newType = 'group' + if Group.getByName(event) then + newObject = Group.getByName(event) + elseif StaticObject.getByName(event) then + newObject = StaticObject.getByName(event) + newType = 'static' + -- log:info('its static') + else + log:info('WTF') + return false + end + + newTable.name = newObject:getName() + newTable.groupId = tonumber(newObject:getID()) + newTable.groupName = newObject:getName() + local unitOneRef + if newType == 'static' then + unitOneRef = newObject + newTable.countryId = tonumber(newObject:getCountry()) + newTable.coalitionId = tonumber(newObject:getCoalition()) + newTable.category = 'static' + else + unitOneRef = newObject:getUnits() + newTable.countryId = tonumber(unitOneRef[1]:getCountry()) + newTable.coalitionId = tonumber(unitOneRef[1]:getCoalition()) + newTable.category = tonumber(newObject:getCategory()) + end + for countryData, countryId in pairs(country.id) do + if newTable.country and string.upper(countryData) == string.upper(newTable.country) or countryId == newTable.countryId then + newTable.countryId = countryId + newTable.country = string.lower(countryData) + for coaData, coaId in pairs(coalition.side) do + if coaId == coalition.getCountryCoalition(countryId) then + newTable.coalition = string.lower(coaData) + end + end + end + end + for catData, catId in pairs(Unit.Category) do + if newType == 'group' and Group.getByName(newTable.groupName):isExist() then + if catId == Group.getByName(newTable.groupName):getCategory() then + newTable.category = string.lower(catData) + end + elseif newType == 'static' and StaticObject.getByName(newTable.groupName):isExist() then + if catId == StaticObject.getByName(newTable.groupName):getCategory() then + newTable.category = string.lower(catData) + end + + end + end + local gfound = false + for index, data in pairs(mistAddedGroups) do + if mist.stringMatch(data.name, newTable.groupName) == true then + gfound = true + newTable.task = data.task + newTable.modulation = data.modulation + newTable.uncontrolled = data.uncontrolled + newTable.radioSet = data.radioSet + newTable.hidden = data.hidden + newTable.startTime = data.start_time + mistAddedGroups[index] = nil + end + end + + if gfound == false then + newTable.uncontrolled = false + newTable.hidden = false + end + + newTable.units = {} + if newType == 'group' then + for unitId, unitData in pairs(unitOneRef) do + newTable.units[unitId] = {} + newTable.units[unitId].unitName = unitData:getName() + + newTable.units[unitId].x = mist.utils.round(unitData:getPosition().p.x) + newTable.units[unitId].y = mist.utils.round(unitData:getPosition().p.z) + newTable.units[unitId].point = {} + newTable.units[unitId].point.x = newTable.units[unitId].x + newTable.units[unitId].point.y = newTable.units[unitId].y + newTable.units[unitId].alt = mist.utils.round(unitData:getPosition().p.y) + newTable.units[unitId].speed = mist.vec.mag(unitData:getVelocity()) + + newTable.units[unitId].heading = mist.getHeading(unitData, true) + + newTable.units[unitId].type = unitData:getTypeName() + newTable.units[unitId].unitId = tonumber(unitData:getID()) + + + newTable.units[unitId].groupName = newTable.groupName + newTable.units[unitId].groupId = newTable.groupId + newTable.units[unitId].countryId = newTable.countryId + newTable.units[unitId].coalitionId = newTable.coalitionId + newTable.units[unitId].coalition = newTable.coalition + newTable.units[unitId].country = newTable.country + local found = false + for index, data in pairs(mistAddedObjects) do + if mist.stringMatch(data.name, newTable.units[unitId].unitName) == true then + found = true + newTable.units[unitId].livery_id = data.livery_id + newTable.units[unitId].skill = data.skill + newTable.units[unitId].alt_type = data.alt_type + newTable.units[unitId].callsign = data.callsign + newTable.units[unitId].psi = data.psi + mistAddedObjects[index] = nil + end + if found == false then + newTable.units[unitId].skill = "High" + newTable.units[unitId].alt_type = "BARO" + end + end + + end + else -- its a static + newTable.category = 'static' + newTable.units[1] = {} + newTable.units[1].unitName = newObject:getName() + newTable.units[1].category = 'static' + newTable.units[1].x = mist.utils.round(newObject:getPosition().p.x) + newTable.units[1].y = mist.utils.round(newObject:getPosition().p.z) + newTable.units[1].point = {} + newTable.units[1].point.x = newTable.units[1].x + newTable.units[1].point.y = newTable.units[1].y + newTable.units[1].alt = mist.utils.round(newObject:getPosition().p.y) + newTable.units[1].heading = mist.getHeading(newObject, true) + newTable.units[1].type = newObject:getTypeName() + newTable.units[1].unitId = tonumber(newObject:getID()) + newTable.units[1].groupName = newTable.name + newTable.units[1].groupId = newTable.groupId + newTable.units[1].countryId = newTable.countryId + newTable.units[1].country = newTable.country + newTable.units[1].coalitionId = newTable.coalitionId + newTable.units[1].coalition = newTable.coalition + if newObject:getCategory() == 6 and newObject:getCargoDisplayName() then + local mass = newObject:getCargoDisplayName() + mass = string.gsub(mass, ' ', '') + mass = string.gsub(mass, 'kg', '') + newTable.units[1].mass = tonumber(mass) + newTable.units[1].categoryStatic = 'Cargos' + newTable.units[1].canCargo = true + newTable.units[1].shape_name = 'ab-212_cargo' + end + + ----- search mist added objects for extra data if applicable + for index, data in pairs(mistAddedObjects) do + if mist.stringMatch(data.name, newTable.units[1].unitName) == true then + newTable.units[1].shape_name = data.shape_name -- for statics + newTable.units[1].livery_id = data.livery_id + newTable.units[1].airdromeId = data.airdromeId + newTable.units[1].mass = data.mass + newTable.units[1].canCargo = data.canCargo + newTable.units[1].categoryStatic = data.categoryStatic + newTable.units[1].type = 'cargo1' + mistAddedObjects[index] = nil + end + end + end + end + --mist.debug.writeData(mist.utils.serialize,{'msg', newTable}, timer.getAbsTime() ..'Group.lua') + newTable.timeAdded = timer.getAbsTime() -- only on the dynGroupsAdded table. For other reference, see start time + --mist.debug.dumpDBs() + --end + + return newTable + end + + local function checkSpawnedEvents() + if #tempSpawnedUnits > 0 then + local groupsToAdd = {} + local added = false + local ltemp = tempSpawnedUnits + local ltable = table + + local updatesPerRun = math.ceil(#tempSpawnedUnits/20) + if updatesPerRun < 5 then + updatesPerRun = 5 + end + for x = 1, #tempSpawnedUnits do + local spawnedObj = ltemp[x] + if spawnedObj and spawnedObj:isExist() then + local found = false + for name, val in pairs(groupsToAdd) do + if spawnedObj:getCategory() == 1 then -- normal groups + if mist.stringMatch(spawnedObj:getGroup():getName(), name) == true then + found = true + break + end + elseif spawnedObj:getCategory() == 3 or spawnedObj:getCategory() == 6 then -- static objects + if mist.stringMatch(spawnedObj:getName(), name) == true then + found = true + break + end + end + end + -- for some reason cargo objects are returning as category == 6. + if found == false then + added = true + if spawnedObj:getCategory() == 1 then -- normal groups + groupsToAdd[spawnedObj:getGroup():getName()] = true + elseif spawnedObj:getCategory() == 3 or spawnedObj:getCategory() == 6 then -- static objects + groupsToAdd[spawnedObj:getName()] = true + end + + end + end + + table.remove(ltemp, x) + if x%updatesPerRun == 0 then + coroutine.yield() + end + end + + if added == true then + for groupName, val in pairs(groupsToAdd) do + local dataChanged = false + if mist.DBs.groupsByName[groupName] then + for _index, data in pairs(mist.DBs.groupsByName[groupName]) do + if data.unitName ~= spawnedObj:getName() and data.unitId ~= spawnedObj:getID() and data.type ~= spawnedObj:getTypeName() then + dataChanged = true + break + end + end + if dataChanged == false then + groupsToAdd[groupName] = false + end + end + if groupsToAdd[groupName] == true or not mist.DBs.groupsByName[groupName] then + writeGroups[#writeGroups + 1] = dbUpdate(groupName) + end + end + end + end + end + + local function updateDBTables() + local i = 0 + for index, newTable in pairs(writeGroups) do + i = i + 1 + end + local savesPerRun = math.ceil(i/10) + if savesPerRun < 5 then + savesPerRun = 5 + end + if i > 0 then + local ldeepCopy = mist.utils.deepCopy + for x = 1, i do + local newTable = writeGroups[x] + local mistCategory + if type(newTable.category) == 'string' then + mistCategory = string.lower(newTable.category) + end + + if string.upper(newTable.category) == 'GROUND_UNIT' then + mistCategory = 'vehicle' + newTable.category = mistCategory + elseif string.upper(newTable.category) == 'AIRPLANE' then + mistCategory = 'plane' + newTable.category = mistCategory + elseif string.upper(newTable.category) == 'HELICOPTER' then + mistCategory = 'helicopter' + newTable.category = mistCategory + elseif string.upper(newTable.category) == 'SHIP' then + mistCategory = 'ship' + newTable.category = mistCategory + end + for newId, newUnitData in pairs(newTable.units) do + newUnitData.category = mistCategory + if newUnitData.unitId then + mist.DBs.unitsById[tonumber(newUnitData.unitId)] = ldeepCopy(newUnitData) + end + + mist.DBs.unitsByName[newUnitData.unitName] = ldeepCopy(newUnitData) + mist.DBs.unitsByCat[mistCategory][#mist.DBs.unitsByCat[mistCategory] + 1] = ldeepCopy(newUnitData) + mist.DBs.unitsByNum[#mist.DBs.unitsByNum + 1] = ldeepCopy(newUnitData) + end + -- this is a really annoying DB to populate. Gotta create new tables in case its missing + if not mist.DBs.units[newTable.coalition] then + mist.DBs.units[newTable.coalition] = {} + end + + if not mist.DBs.units[newTable.coalition][newTable.country] then + mist.DBs.units[newTable.coalition][(newTable.country)] = {} + mist.DBs.units[newTable.coalition][(newTable.country)].countryId = newTable.countryId + end + if not mist.DBs.units[newTable.coalition][newTable.country][mistCategory] then + mist.DBs.units[newTable.coalition][(newTable.country)][mistCategory] = {} + end + mist.DBs.units[newTable.coalition][(newTable.country)][mistCategory][#mist.DBs.units[newTable.coalition][(newTable.country)][mistCategory] + 1] = ldeepCopy(newTable) + + if newTable.groupId then + mist.DBs.groupsById[newTable.groupId] = ldeepCopy(newTable) + end + + mist.DBs.groupsByName[newTable.name] = ldeepCopy(newTable) + mist.DBs.dynGroupsAdded[#mist.DBs.dynGroupsAdded + 1] = ldeepCopy(newTable) + + writeGroups[x] = nil + if x%savesPerRun == 0 then + coroutine.yield() + end + end + if timer.getTime() > lastUpdateTime then + lastUpdateTime = timer.getTime() + end + end + end + + local function groupSpawned(event) + -- dont need to add units spawned in at the start of the mission if mist is loaded in init line + if event.id == world.event.S_EVENT_BIRTH and timer.getTime0() < timer.getAbsTime() then + table.insert(tempSpawnedUnits,(event.initiator)) + end + end + + local function doScheduledFunctions() + local i = 1 + while i <= #scheduledTasks do + if not scheduledTasks[i].rep then -- not a repeated process + if scheduledTasks[i].t <= timer.getTime() then + local task = scheduledTasks[i] -- local reference + table.remove(scheduledTasks, i) + local err, errmsg = pcall(task.f, unpack(task.vars, 1, table.maxn(task.vars))) + if not err then + log:error('Error in scheduled function: $1', errmsg) + end + --task.f(unpack(task.vars, 1, table.maxn(task.vars))) -- do the task, do not increment i + else + i = i + 1 + end + else + if scheduledTasks[i].st and scheduledTasks[i].st <= timer.getTime() then --if a stoptime was specified, and the stop time exceeded + table.remove(scheduledTasks, i) -- stop time exceeded, do not execute, do not increment i + elseif scheduledTasks[i].t <= timer.getTime() then + local task = scheduledTasks[i] -- local reference + task.t = timer.getTime() + task.rep --schedule next run + local err, errmsg = pcall(task.f, unpack(task.vars, 1, table.maxn(task.vars))) + if not err then + log:error('Error in scheduled function: $1' .. errmsg) + end + --scheduledTasks[i].f(unpack(scheduledTasks[i].vars, 1, table.maxn(scheduledTasks[i].vars))) -- do the task + i = i + 1 + else + i = i + 1 + end + end + end + end + + -- Event handler to start creating the dead_objects table + local function addDeadObject(event) + if event.id == world.event.S_EVENT_DEAD or event.id == world.event.S_EVENT_CRASH then + if event.initiator and event.initiator.id_ and event.initiator.id_ > 0 then + + local id = event.initiator.id_ -- initial ID, could change if there is a duplicate id_ already dead. + local val = {object = event.initiator} -- the new entry in mist.DBs.deadObjects. + + local original_id = id --only for duplicate runtime IDs. + local id_ind = 1 + while mist.DBs.deadObjects[id] do + --log:info('duplicate runtime id of previously dead object id: $1', id) + id = tostring(original_id) .. ' #' .. tostring(id_ind) + id_ind = id_ind + 1 + end + + if mist.DBs.aliveUnits and mist.DBs.aliveUnits[val.object.id_] then + --log:info('object found in alive_units') + val.objectData = mist.utils.deepCopy(mist.DBs.aliveUnits[val.object.id_]) + local pos = Object.getPosition(val.object) + if pos then + val.objectPos = pos.p + end + val.objectType = mist.DBs.aliveUnits[val.object.id_].category + --[[if mist.DBs.activeHumans[Unit.getName(val.object)] then + --trigger.action.outText('remove via death: ' .. Unit.getName(val.object),20) + mist.DBs.activeHumans[Unit.getName(val.object)] = nil + end]] + elseif mist.DBs.removedAliveUnits and mist.DBs.removedAliveUnits[val.object.id_] then -- it didn't exist in alive_units, check old_alive_units + --log:info('object found in old_alive_units') + val.objectData = mist.utils.deepCopy(mist.DBs.removedAliveUnits[val.object.id_]) + local pos = Object.getPosition(val.object) + if pos then + val.objectPos = pos.p + end + val.objectType = mist.DBs.removedAliveUnits[val.object.id_].category + + else --attempt to determine if static object... + --log:info('object not found in alive units or old alive units') + local pos = Object.getPosition(val.object) + if pos then + local static_found = false + for ind, static in pairs(mist.DBs.unitsByCat.static) do + if ((pos.p.x - static.point.x)^2 + (pos.p.z - static.point.y)^2)^0.5 < 0.1 then --really, it should be zero... + --log:info('correlated dead static object to position') + val.objectData = static + val.objectPos = pos.p + val.objectType = 'static' + static_found = true + break + end + end + if not static_found then + val.objectPos = pos.p + val.objectType = 'building' + end + else + val.objectType = 'unknown' + end + end + mist.DBs.deadObjects[id] = val + end + end + end + + --[[ + local function addClientsToActive(event) + if event.id == world.event.S_EVENT_PLAYER_ENTER_UNIT or event.id == world.event.S_EVENT_BIRTH then + log:info(mist.utils.tableShow(event)) + if Unit.getPlayerName(event.initiator) then + log:info(Unit.getPlayerName(event.initiator)) + local newU = mist.utils.deepCopy(mist.DBs.unitsByName[Unit.getName(event.initiator)]) + newU.playerName = Unit.getPlayerName(event.initiator) + mist.DBs.activeHumans[Unit.getName(event.initiator)] = newU + --trigger.action.outText('added: ' .. Unit.getName(event.initiator), 20) + end + elseif event.id == world.event.S_EVENT_PLAYER_LEAVE_UNIT and event.initiator then + if mist.DBs.activeHumans[Unit.getName(event.initiator)] then + mist.DBs.activeHumans[Unit.getName(event.initiator)] = nil + -- trigger.action.outText('removed via control: ' .. Unit.getName(event.initiator), 20) + end + end + end + + mist.addEventHandler(addClientsToActive) + ]] + + --- init function. + -- creates logger, adds default event handler + -- and calls main the first time. + -- @function mist.init + function mist.init() + -- create logger + mist.log = mist.Logger:new("MIST") + log = mist.log -- log shorthand + -- set warning log level, showing only + -- warnings and errors + log:setLevel("warning") + + log:info("initializing databases") + initDBs() + + -- add event handler for group spawns + mist.addEventHandler(groupSpawned) + mist.addEventHandler(addDeadObject) + + -- call main the first time therafter it reschedules itself. + mist.main() + --log:msg('MIST version $1.$2.$3 loaded', mist.majorVersion, mist.minorVersion, mist.build) + return + end + + --- The main function. + -- Run 100 times per second. + -- You shouldn't call this function. + function mist.main() + timer.scheduleFunction(mist.main, {}, timer.getTime() + 0.01) --reschedule first in case of Lua error + + writeDbTableCounter = writeDbTableCounter + 1 + if writeDbTableCounter == 10 then + writeDbTableCounter = 0 + + if not coroutines.updateDBTables then + coroutines.updateDBTables = coroutine.create(updateDBTables) + end + + coroutine.resume(coroutines.updateDBTables) + + if coroutine.status(coroutines.updateDBTables) == 'dead' then + coroutines.updateDBTables = nil + end + end + + checkSpawnEventsCounter = checkSpawnEventsCounter + 1 + if checkSpawnEventsCounter == 10 then + checkSpawnEventsCounter = 0 + + if not coroutines.checkSpawnedEvents then + coroutines.checkSpawnedEvents = coroutine.create(checkSpawnedEvents) + end + + coroutine.resume(coroutines.checkSpawnedEvents) + + if coroutine.status(coroutines.checkSpawnedEvents) == 'dead' then + coroutines.checkSpawnedEvents = nil + end + end + + --updating alive units + updateAliveUnitsCounter = updateAliveUnitsCounter + 1 + if updateAliveUnitsCounter == 5 then + updateAliveUnitsCounter = 0 + + if not coroutines.updateAliveUnits then + coroutines.updateAliveUnits = coroutine.create(updateAliveUnits) + end + + coroutine.resume(coroutines.updateAliveUnits) + + if coroutine.status(coroutines.updateAliveUnits) == 'dead' then + coroutines.updateAliveUnits = nil + end + end + + doScheduledFunctions() + end -- end of mist.main + + --- Returns next unit id. + -- @treturn number next unit id. + function mist.getNextUnitId() + mist.nextUnitId = mist.nextUnitId + 1 + if mist.nextUnitId > 6900 then + mist.nextUnitId = 14000 + end + return mist.nextUnitId + end + + --- Returns next group id. + -- @treturn number next group id. + function mist.getNextGroupId() + mist.nextGroupId = mist.nextGroupId + 1 + if mist.nextGroupId > 6900 then + mist.nextGroupId = 14000 + end + return mist.nextGroupId + end + + --- Returns timestamp of last database update. + -- @treturn timestamp of last database update + function mist.getLastDBUpdateTime() + return lastUpdateTime + end + + --- Spawns a static object to the game world. + -- @todo write good docs + -- @tparam table staticObj table containing data needed for the object creation + function mist.dynAddStatic(staticObj) + local newObj = {} + newObj.groupId = staticObj.groupId + newObj.category = staticObj.category + newObj.type = staticObj.type + newObj.unitId = staticObj.unitId + newObj.y = staticObj.y + newObj.x = staticObj.x + newObj.heading = staticObj.heading + newObj.name = staticObj.name + newObj.dead = staticObj.dead + newObj.country = staticObj.country + newObj.countryId = staticObj.countryId + newObj.clone = staticObj.clone + newObj.shape_name = staticObj.shape_name + newObj.canCargo = staticObj.canCargo + newObj.mass = staticObj.mass + newObj.categoryStatic = staticObj.categoryStatic + if staticObj.units then -- if its mist format + newObj.groupId = staticObj.units[1].groupId + newObj.category = staticObj.units[1].category + newObj.type = staticObj.units[1].type + newObj.unitId = staticObj.units[1].unitId + newObj.y = staticObj.units[1].y + newObj.x = staticObj.units[1].x + newObj.heading = staticObj.units[1].heading + newObj.name = staticObj.units[1].name + newObj.dead = staticObj.units[1].dead + newObj.country = staticObj.units[1].country + newObj.countryId = staticObj.units[1].countryId + newObj.shape_name = staticObj.units[1].shape_name + newObj.canCargo = staticObj.units[1].canCargo + newObj.mass = staticObj.units[1].mass + newObj.categoryStatic = staticObj.units[1].categoryStatic + end + + if not newObj.country then + return false + end + + local newCountry = newObj.country + if newObj.countryId then + newCountry = newObj.countryId + end + for countryId, countryName in pairs(country.name) do + if type(newObj.country) == 'string' then + if tostring(countryName) == string.upper(newObj.country) then + newCountry = countryName + end + elseif type(newObj.country) == 'number' then + if countryId == newObj.country then + newCountry = countryName + end + end + end + + if newObj.clone or not newObj.groupId then + mistGpId = mistGpId + 1 + newObj.groupId = mistGpId + end + + if newObj.clone or not newObj.unitId then + mistUnitId = mistUnitId + 1 + newObj.unitId = mistUnitId + end + + if newObj.clone or not newObj.name then + mistDynAddIndex = mistDynAddIndex + 1 + newObj.name = (country.name[newCountry] .. ' static ' .. mistDynAddIndex) + end + + if not newObj.dead then + newObj.dead = false + end + + if not newObj.heading then + newObj.heading = math.random(360) + end + + if newObj.categoryStatic then + newObj.category = newObj.categoryStatic + end + if newObj.mass then + newObj.category = 'Cargos' + end + mistAddedObjects[#mistAddedObjects + 1] = mist.utils.deepCopy(newObj) + if newObj.x and newObj.y and newObj.type and type(newObj.x) == 'number' and type(newObj.y) == 'number' and type(newObj.type) == 'string' then + coalition.addStaticObject(country.id[newCountry], newObj) + + return newObj + end + return false + end + + --- Spawns a dynamic group into the game world. + -- Same as coalition.add function in SSE. checks the passed data to see if its valid. + -- Will generate groupId, groupName, unitId, and unitName if needed + -- @tparam table newGroup table containting values needed for spawning a group. + function mist.dynAdd(newGroup) + + --mist.debug.writeData(mist.utils.serialize,{'msg', newGroup}, 'newGroupOrig.lua') + local cntry = newGroup.country + if newGroup.countryId then + cntry = newGroup.countryId + end + + local groupType = newGroup.category + local newCountry = '' + -- validate data + for countryId, countryName in pairs(country.name) do + if type(cntry) == 'string' then + cntry = cntry:gsub("%s+", "_") + if tostring(countryName) == string.upper(cntry) then + newCountry = countryName + end + elseif type(cntry) == 'number' then + if countryId == cntry then + newCountry = countryName + end + end + end + + if newCountry == '' then + return false + end + + local newCat = '' + for catName, catId in pairs(Unit.Category) do + if type(groupType) == 'string' then + if tostring(catName) == string.upper(groupType) then + newCat = catName + end + elseif type(groupType) == 'number' then + if catId == groupType then + newCat = catName + end + end + + if catName == 'GROUND_UNIT' and (string.upper(groupType) == 'VEHICLE' or string.upper(groupType) == 'GROUND') then + newCat = 'GROUND_UNIT' + elseif catName == 'AIRPLANE' and string.upper(groupType) == 'PLANE' then + newCat = 'AIRPLANE' + end + end + local typeName + if newCat == 'GROUND_UNIT' then + typeName = ' gnd ' + elseif newCat == 'AIRPLANE' then + typeName = ' air ' + elseif newCat == 'HELICOPTER' then + typeName = ' hel ' + elseif newCat == 'SHIP' then + typeName = ' shp ' + elseif newCat == 'BUILDING' then + typeName = ' bld ' + end + if newGroup.clone or not newGroup.groupId then + mistDynAddIndex = mistDynAddIndex + 1 + mistGpId = mistGpId + 1 + newGroup.groupId = mistGpId + end + if newGroup.groupName or newGroup.name then + if newGroup.groupName then + newGroup.name = newGroup.groupName + elseif newGroup.name then + newGroup.name = newGroup.name + end + end + + if newGroup.clone and mist.DBs.groupsByName[newGroup.name] or not newGroup.name then + newGroup.name = tostring(tostring(country.name[cntry]) .. tostring(typeName) .. mistDynAddIndex) + end + + if not newGroup.hidden then + newGroup.hidden = false + end + + if not newGroup.visible then + newGroup.visible = false + end + + if (newGroup.start_time and type(newGroup.start_time) ~= 'number') or not newGroup.start_time then + if newGroup.startTime then + newGroup.start_time = mist.utils.round(newGroup.startTime) + else + newGroup.start_time = 0 + end + end + + + for unitIndex, unitData in pairs(newGroup.units) do + local originalName = newGroup.units[unitIndex].unitName or newGroup.units[unitIndex].name + if newGroup.clone or not unitData.unitId then + mistUnitId = mistUnitId + 1 + newGroup.units[unitIndex].unitId = mistUnitId + end + if newGroup.units[unitIndex].unitName or newGroup.units[unitIndex].name then + if newGroup.units[unitIndex].unitName then + newGroup.units[unitIndex].name = newGroup.units[unitIndex].unitName + elseif newGroup.units[unitIndex].name then + newGroup.units[unitIndex].name = newGroup.units[unitIndex].name + end + end + if newGroup.clone or not unitData.name then + newGroup.units[unitIndex].name = tostring(newGroup.name .. ' unit' .. unitIndex) + end + + if not unitData.skill then + newGroup.units[unitIndex].skill = 'Random' + end + + if not unitData.alt then + if newCat == 'AIRPLANE' then + newGroup.units[unitIndex].alt = 2000 + newGroup.units[unitIndex].alt_type = 'RADIO' + newGroup.units[unitIndex].speed = 150 + elseif newCat == 'HELICOPTER' then + newGroup.units[unitIndex].alt = 500 + newGroup.units[unitIndex].alt_type = 'RADIO' + newGroup.units[unitIndex].speed = 60 + else + --[[log:info('check height') + newGroup.units[unitIndex].alt = land.getHeight({x = newGroup.units[unitIndex].x, y = newGroup.units[unitIndex].y}) + newGroup.units[unitIndex].alt_type = 'BARO']] + end + + + end + + if newCat == 'AIRPLANE' or newCat == 'HELICOPTER' then + if newGroup.units[unitIndex].alt_type and newGroup.units[unitIndex].alt_type ~= 'BARO' or not newGroup.units[unitIndex].alt_type then + newGroup.units[unitIndex].alt_type = 'RADIO' + end + if not unitData.speed then + if newCat == 'AIRPLANE' then + newGroup.units[unitIndex].speed = 150 + elseif newCat == 'HELICOPTER' then + newGroup.units[unitIndex].speed = 60 + end + end + if not unitData.payload then + newGroup.units[unitIndex].payload = mist.getPayload(originalName) + end + end + mistAddedObjects[#mistAddedObjects + 1] = mist.utils.deepCopy(newGroup.units[unitIndex]) + end + mistAddedGroups[#mistAddedGroups + 1] = mist.utils.deepCopy(newGroup) + if newGroup.route and not newGroup.route.points then + if not newGroup.route.points and newGroup.route[1] then + local copyRoute = newGroup.route + newGroup.route = {} + newGroup.route.points = copyRoute + end + end + newGroup.country = newCountry + + + --mist.debug.writeData(mist.utils.serialize,{'msg', newGroup}, 'newGroup.lua') + + -- sanitize table + newGroup.groupName = nil + newGroup.clone = nil + newGroup.category = nil + newGroup.country = nil + + newGroup.tasks = {} + + for unitIndex, unitData in pairs(newGroup.units) do + newGroup.units[unitIndex].unitName = nil + end + + coalition.addGroup(country.id[newCountry], Unit.Category[newCat], newGroup) + + return newGroup + + end + + --- Schedules a function. + -- Modified Slmod task scheduler, superior to timer.scheduleFunction + -- @tparam function f function to schedule + -- @tparam table vars array containing all parameters passed to the function + -- @tparam number t time in seconds from mission start to schedule the function to. + -- @tparam[opt] number rep time between repetitions of the function + -- @tparam[opt] number st time in seconds from mission start at which the function + -- should stop to be rescheduled. + -- @treturn number scheduled function id. + function mist.scheduleFunction(f, vars, t, rep, st) + --verify correct types + assert(type(f) == 'function', 'variable 1, expected function, got ' .. type(f)) + assert(type(vars) == 'table' or vars == nil, 'variable 2, expected table or nil, got ' .. type(f)) + assert(type(t) == 'number', 'variable 3, expected number, got ' .. type(t)) + assert(type(rep) == 'number' or rep == nil, 'variable 4, expected number or nil, got ' .. type(rep)) + assert(type(st) == 'number' or st == nil, 'variable 5, expected number or nil, got ' .. type(st)) + if not vars then + vars = {} + end + taskId = taskId + 1 + table.insert(scheduledTasks, {f = f, vars = vars, t = t, rep = rep, st = st, id = taskId}) + return taskId + end + + --- Removes a scheduled function. + -- @tparam number id function id + -- @treturn boolean true if function was successfully removed, false otherwise. + function mist.removeFunction(id) + local i = 1 + while i <= #scheduledTasks do + if scheduledTasks[i].id == id then + table.remove(scheduledTasks, i) + else + i = i + 1 + end + end + end + + --- Registers an event handler. + -- @tparam function f function handling event + -- @treturn number id of the event handler + function mist.addEventHandler(f) --id is optional! + local handler = {} + idNum = idNum + 1 + handler.id = idNum + handler.f = f + function handler:onEvent(event) + self.f(event) + end + world.addEventHandler(handler) + return handler.id + end + + --- Removes event handler with given id. + -- @tparam number id event handler id + -- @treturn boolean true on success, false otherwise + function mist.removeEventHandler(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 + + --- Returns MGRS coordinates as string. + -- @tparam string MGRS MGRS coordinates + -- @tparam number acc the accuracy of each easting/northing. + -- Can be: 0, 1, 2, 3, 4, or 5. + function mist.tostringMGRS(MGRS, acc) + if acc == 0 then + return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph + else + return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph .. ' ' .. string.format('%0' .. acc .. 'd', mist.utils.round(MGRS.Easting/(10^(5-acc)), 0)) + .. ' ' .. string.format('%0' .. acc .. 'd', mist.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. + ]] + function mist.tostringLL(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 = mist.utils.round((oldLatMin - latMin)*60, acc) + + local oldLonMin = lonMin + lonMin = math.floor(lonMin) + local lonSec = mist.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 = mist.utils.round(latMin, acc) + lonMin = mist.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.]] + function mist.tostringBR(az, dist, alt, metric) + az = mist.utils.round(mist.utils.toDegree(az), 0) + + if metric then + dist = mist.utils.round(dist/1000, 0) + else + dist = mist.utils.round(mist.utils.metersToNM(dist), 0) + end + + local s = string.format('%03d', az) .. ' for ' .. dist + + if alt then + if metric then + s = s .. ' at ' .. mist.utils.round(alt, 0) + else + s = s .. ' at ' .. mist.utils.round(mist.utils.metersToFeet(alt), 0) + end + end + return s + end + + function mist.getNorthCorrection(gPoint) --gets the correction needed for true north + local point = mist.utils.deepCopy(gPoint) + 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 + + --- Returns skill of the given unit. + -- @tparam string unitName unit name + -- @return skill of the unit + function mist.getUnitSkill(unitName) + if Unit.getByName(unitName) and Unit.getByName(unitName):isExist() == true then + local lunit = Unit.getByName(unitName) + for name, data in pairs(mist.DBs.unitsByName) do + if name == unitName and data.type == lunit:getTypeName() and data.unitId == lunit:getID() and data.skill then + return data.skill + end + end + end + return false + end + + --- Returns an array containing a group's units positions. + -- e.g. + -- { + -- [1] = {x = 299435.224, y = -1146632.6773}, + -- [2] = {x = 663324.6563, y = 322424.1112} + -- } + -- @tparam number|string groupIdent group id or name + -- @treturn table array containing positions of each group member + function mist.getGroupPoints(groupIdent) + -- search by groupId and allow groupId and groupName as inputs + local gpId = groupIdent + if type(groupIdent) == 'string' and not tonumber(groupIdent) then + gpId = mist.DBs.MEgroupsByName[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 + if not point.point then + points[point_num] = { x = point.x, y = point.y } + else + points[point_num] = point.point --it's possible that the ME could move to the point = Vec2 notation. + end + 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 + + --- getUnitAttitude(unit) return values. + -- Yaw, AoA, ClimbAngle - relative to earth reference + -- DOES NOT TAKE INTO ACCOUNT WIND. + -- @table attitude + -- @tfield number Heading in radians, range of 0 to 2*pi, + -- relative to true north. + -- @tfield number Pitch in radians, range of -pi/2 to pi/2 + -- @tfield number Roll in radians, range of 0 to 2*pi, + -- right roll is positive direction. + -- @tfield number Yaw in radians, range of -pi to pi, + -- right yaw is positive direction. + -- @tfield number AoA in radians, range of -pi to pi, + -- rotation of aircraft to the right in comparison to + -- flight direction being positive. + -- @tfield number ClimbAngle in radians, range of -pi/2 to pi/2 + + --- Returns the attitude of a given unit. + -- Will work on any unit, even if not an aircraft. + -- @tparam Unit unit unit whose attitude is returned. + -- @treturn table @{attitude} + function mist.getAttitude(unit) + local unitpos = unit:getPosition() + if unitpos then + + local Heading = math.atan2(unitpos.x.z, unitpos.x.x) + + Heading = Heading + mist.getNorthCorrection(unitpos.p) + + if Heading < 0 then + Heading = Heading + 2*math.pi -- put heading in range of 0 to 2*pi + end + ---- heading complete.---- + + local Pitch = math.asin(unitpos.x.y) + ---- pitch complete.---- + + -- now get roll: + --maybe not the best way to do it, but it works. + + --first, make a vector that is perpendicular to y and unitpos.x with cross product + local cp = mist.vec.cp(unitpos.x, {x = 0, y = 1, z = 0}) + + --now, get dot product of of this cross product with unitpos.z + local dp = mist.vec.dp(cp, unitpos.z) + + --now get the magnitude of the roll (magnitude of the angle between two vectors is acos(vec1.vec2/|vec1||vec2|) + local Roll = math.acos(dp/(mist.vec.mag(cp)*mist.vec.mag(unitpos.z))) + + --now, have to get sign of roll. + -- by convention, making right roll positive + -- to get sign of roll, use the y component of unitpos.z. For right roll, y component is negative. + + if unitpos.z.y > 0 then -- left roll, flip the sign of the roll + Roll = -Roll + end + ---- roll complete. ---- + + --now, work on yaw, AoA, climb, and abs velocity + local Yaw + local AoA + local ClimbAngle + + -- get unit velocity + local unitvel = unit:getVelocity() + if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! + local AxialVel = {} --unit velocity transformed into aircraft axes directions + + --transform velocity components in direction of aircraft axes. + AxialVel.x = mist.vec.dp(unitpos.x, unitvel) + AxialVel.y = mist.vec.dp(unitpos.y, unitvel) + AxialVel.z = mist.vec.dp(unitpos.z, unitvel) + + --Yaw is the angle between unitpos.x and the x and z velocities + --define right yaw as positive + Yaw = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = 0, z = AxialVel.z})/mist.vec.mag({x = AxialVel.x, y = 0, z = AxialVel.z})) + + --now set correct direction: + if AxialVel.z > 0 then + Yaw = -Yaw + end + + -- AoA is angle between unitpos.x and the x and y velocities + AoA = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = AxialVel.y, z = 0})/mist.vec.mag({x = AxialVel.x, y = AxialVel.y, z = 0})) + + --now set correct direction: + if AxialVel.y > 0 then + AoA = -AoA + end + + ClimbAngle = math.asin(unitvel.y/mist.vec.mag(unitvel)) + end + return { Heading = Heading, Pitch = Pitch, Roll = Roll, Yaw = Yaw, AoA = AoA, ClimbAngle = ClimbAngle} + else + log:error("Couldn't get unit's position") + end + end + + --- Returns heading of given unit. + -- @tparam Unit unit unit whose heading is returned. + -- @param rawHeading + -- @treturn number heading of the unit, in range + -- of 0 to 2*pi. + function mist.getHeading(unit, rawHeading) + local unitpos = unit:getPosition() + if unitpos then + local Heading = math.atan2(unitpos.x.z, unitpos.x.x) + if not rawHeading then + Heading = Heading + mist.getNorthCorrection(unitpos.p) + end + if Heading < 0 then + Heading = Heading + 2*math.pi -- put heading in range of 0 to 2*pi + end + return Heading + end + end + + --- Returns given unit's pitch + -- @tparam Unit unit unit whose pitch is returned. + -- @treturn number pitch of given unit + function mist.getPitch(unit) + local unitpos = unit:getPosition() + if unitpos then + return math.asin(unitpos.x.y) + end + end + + --- Returns given unit's roll. + -- @tparam Unit unit unit whose roll is returned. + -- @treturn number roll of given unit + function mist.getRoll(unit) + local unitpos = unit:getPosition() + if unitpos then + -- now get roll: + --maybe not the best way to do it, but it works. + + --first, make a vector that is perpendicular to y and unitpos.x with cross product + local cp = mist.vec.cp(unitpos.x, {x = 0, y = 1, z = 0}) + + --now, get dot product of of this cross product with unitpos.z + local dp = mist.vec.dp(cp, unitpos.z) + + --now get the magnitude of the roll (magnitude of the angle between two vectors is acos(vec1.vec2/|vec1||vec2|) + local Roll = math.acos(dp/(mist.vec.mag(cp)*mist.vec.mag(unitpos.z))) + + --now, have to get sign of roll. + -- by convention, making right roll positive + -- to get sign of roll, use the y component of unitpos.z. For right roll, y component is negative. + + if unitpos.z.y > 0 then -- left roll, flip the sign of the roll + Roll = -Roll + end + return Roll + end + end + + --- Returns given unit's yaw. + -- @tparam Unit unit unit whose yaw is returned. + -- @treturn number yaw of given unit. + function mist.getYaw(unit) + local unitpos = unit:getPosition() + if unitpos then + -- get unit velocity + local unitvel = unit:getVelocity() + if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! + local AxialVel = {} --unit velocity transformed into aircraft axes directions + + --transform velocity components in direction of aircraft axes. + AxialVel.x = mist.vec.dp(unitpos.x, unitvel) + AxialVel.y = mist.vec.dp(unitpos.y, unitvel) + AxialVel.z = mist.vec.dp(unitpos.z, unitvel) + + --Yaw is the angle between unitpos.x and the x and z velocities + --define right yaw as positive + local Yaw = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = 0, z = AxialVel.z})/mist.vec.mag({x = AxialVel.x, y = 0, z = AxialVel.z})) + + --now set correct direction: + if AxialVel.z > 0 then + Yaw = -Yaw + end + return Yaw + end + end + end + + --- Returns given unit's angle of attack. + -- @tparam Unit unit unit to get AoA from. + -- @treturn number angle of attack of the given unit. + function mist.getAoA(unit) + local unitpos = unit:getPosition() + if unitpos then + local unitvel = unit:getVelocity() + if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! + local AxialVel = {} --unit velocity transformed into aircraft axes directions + + --transform velocity components in direction of aircraft axes. + AxialVel.x = mist.vec.dp(unitpos.x, unitvel) + AxialVel.y = mist.vec.dp(unitpos.y, unitvel) + AxialVel.z = mist.vec.dp(unitpos.z, unitvel) + + -- AoA is angle between unitpos.x and the x and y velocities + local AoA = math.acos(mist.vec.dp({x = 1, y = 0, z = 0}, {x = AxialVel.x, y = AxialVel.y, z = 0})/mist.vec.mag({x = AxialVel.x, y = AxialVel.y, z = 0})) + + --now set correct direction: + if AxialVel.y > 0 then + AoA = -AoA + end + return AoA + end + end + end + + --- Returns given unit's climb angle. + -- @tparam Unit unit unit to get climb angle from. + -- @treturn number climb angle of given unit. + function mist.getClimbAngle(unit) + local unitpos = unit:getPosition() + if unitpos then + local unitvel = unit:getVelocity() + if mist.vec.mag(unitvel) ~= 0 then --must have non-zero velocity! + return math.asin(unitvel.y/mist.vec.mag(unitvel)) + end + end + end + + --[[-- + Unit name table. + Many Mist functions require tables of unit names, which are known + in Mist as UnitNameTables. These follow a special set of shortcuts + borrowed from Slmod. These shortcuts alleviate the problem of entering + huge lists of unit names by hand, and in many cases, they remove the + need to even know the names of the units in the first place! + + These are the unit table "short-cut" commands: + + Prefixes: + "[-u]" - subtract this unit if its in the table + "[g]" - add this group to the table + "[-g]" - subtract this group from the table + "[c]" - add this country's units + "[-c]" - subtract this country's units if any are in the table + + Stand-alone identifiers + "[all]" - add all units + "[-all]" - subtract all units (not very useful by itself) + "[blue]" - add all blue units + "[-blue]" - subtract all blue units + "[red]" - add all red coalition units + "[-red]" - subtract all red units + + Compound Identifiers: + "[c][helicopter]" - add all of this country's helicopters + "[-c][helicopter]" - subtract all of this country's helicopters + "[c][plane]" - add all of this country's planes + "[-c][plane]" - subtract all of this country's planes + "[c][ship]" - add all of this country's ships + "[-c][ship]" - subtract all of this country's ships + "[c][vehicle]" - add all of this country's vehicles + "[-c][vehicle]" - subtract all of this country's vehicles + + "[all][helicopter]" - add all helicopters + "[-all][helicopter]" - subtract all helicopters + "[all][plane]" - add all planes + "[-all][plane]" - subtract all planes + "[all][ship]" - add all ships + "[-all][ship]" - subtract all ships + "[all][vehicle]" - add all vehicles + "[-all][vehicle]" - subtract all vehicles + + "[blue][helicopter]" - add all blue coalition helicopters + "[-blue][helicopter]" - subtract all blue coalition helicopters + "[blue][plane]" - add all blue coalition planes + "[-blue][plane]" - subtract all blue coalition planes + "[blue][ship]" - add all blue coalition ships + "[-blue][ship]" - subtract all blue coalition ships + "[blue][vehicle]" - add all blue coalition vehicles + "[-blue][vehicle]" - subtract all blue coalition vehicles + + "[red][helicopter]" - add all red coalition helicopters + "[-red][helicopter]" - subtract all red coalition helicopters + "[red][plane]" - add all red coalition planes + "[-red][plane]" - subtract all red coalition planes + "[red][ship]" - add all red coalition ships + "[-red][ship]" - subtract all red coalition ships + "[red][vehicle]" - add all red coalition vehicles + "[-red][vehicle]" - subtract all red coalition vehicles + + Country names to be used in [c] and [-c] short-cuts: + Turkey + Norway + The Netherlands + Spain + 11 + UK + Denmark + USA + Georgia + Germany + Belgium + Canada + France + Israel + Ukraine + Russia + South Ossetia + Abkhazia + Italy + Australia + Austria + Belarus + Bulgaria + Czech Republic + China + Croatia + Finland + Greece + Hungary + India + Iran + Iraq + Japan + Kazakhstan + North Korea + Pakistan + Poland + Romania + Saudi Arabia + Serbia, Slovakia + South Korea + Sweden + Switzerland + Syria + USAF Aggressors + + Do NOT use a '[u]' notation for single units. Single units are referenced + the same way as before: Simply input their names as strings. + + These unit tables are evaluated in order, and you cannot subtract a unit + from a table before it is added. For example: + + {'[blue]', '[-c]Georgia'} + + will evaluate to all of blue coalition except those units owned by the + country named "Georgia"; however: + + {'[-c]Georgia', '[blue]'} + + will evaluate to all of the units in blue coalition, because the addition + of all units owned by blue coalition occurred AFTER the subtraction of all + units owned by Georgia (which actually subtracted nothing at all, since + there were no units in the table when the subtraction occurred). + + More examples: + + {'[blue][plane]', '[-c]Georgia', '[-g]Hawg 1'} + + Evaluates to all blue planes, except those blue units owned by the country + named "Georgia" and the units in the group named "Hawg1". + + + {'[g]arty1', '[g]arty2', '[-u]arty1_AD', '[-u]arty2_AD', 'Shark 11' } + + Evaluates to the unit named "Shark 11", plus all the units in groups named + "arty1" and "arty2" except those that are named "arty1\_AD" and "arty2\_AD". + + @table UnitNameTable + ]] + + --- Returns a table containing unit names. + -- @tparam table tbl sequential strings + -- @treturn table @{UnitNameTable} + function mist.makeUnitTable(tbl) + --Assumption: will be passed a table of strings, sequential + local units_by_name = {} + + local l_munits = mist.DBs.units --local reference for faster execution + for i = 1, #tbl do + local unit = tbl[i] + if unit:sub(1,4) == '[-u]' then --subtract a unit + if units_by_name[unit:sub(5)] then -- 5 to end + units_by_name[unit:sub(5)] = nil --remove + end + elseif unit:sub(1,3) == '[g]' then -- add a group + for coa, coa_tbl in pairs(l_munits) do + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' and group_tbl.groupName == unit:sub(4) then + -- index 4 to end + for unit_ind, unit in pairs(group_tbl.units) do + units_by_name[unit.unitName] = true --add + end + end + end + end + end + end + end + elseif unit:sub(1,4) == '[-g]' then -- subtract a group + for coa, coa_tbl in pairs(l_munits) do + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' and group_tbl.groupName == unit:sub(5) then + -- index 5 to end + for unit_ind, unit in pairs(group_tbl.units) do + if units_by_name[unit.unitName] then + units_by_name[unit.unitName] = nil --remove + end + end + end + end + end + end + end + end + elseif unit:sub(1,3) == '[c]' then -- add a country + local category = '' + local country_start = 4 + if unit:sub(4,15) == '[helicopter]' then + category = 'helicopter' + country_start = 16 + elseif unit:sub(4,10) == '[plane]' then + category = 'plane' + country_start = 11 + elseif unit:sub(4,9) == '[ship]' then + category = 'ship' + country_start = 10 + elseif unit:sub(4,12) == '[vehicle]' then + category = 'vehicle' + country_start = 13 + end + for coa, coa_tbl in pairs(l_munits) do + for country, country_table in pairs(coa_tbl) do + if country == string.lower(unit:sub(country_start)) then -- match + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + units_by_name[unit.unitName] = true --add + end + end + end + end + end + end + end + end + elseif unit:sub(1,4) == '[-c]' then -- subtract a country + local category = '' + local country_start = 5 + if unit:sub(5,16) == '[helicopter]' then + category = 'helicopter' + country_start = 17 + elseif unit:sub(5,11) == '[plane]' then + category = 'plane' + country_start = 12 + elseif unit:sub(5,10) == '[ship]' then + category = 'ship' + country_start = 11 + elseif unit:sub(5,13) == '[vehicle]' then + category = 'vehicle' + country_start = 14 + end + for coa, coa_tbl in pairs(l_munits) do + for country, country_table in pairs(coa_tbl) do + if country == string.lower(unit:sub(country_start)) then -- match + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + if units_by_name[unit.unitName] then + units_by_name[unit.unitName] = nil --remove + end + end + end + end + end + end + end + end + end + elseif unit:sub(1,6) == '[blue]' then -- add blue coalition + local category = '' + if unit:sub(7) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(7) == '[plane]' then + category = 'plane' + elseif unit:sub(7) == '[ship]' then + category = 'ship' + elseif unit:sub(7) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + if coa == 'blue' then + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + units_by_name[unit.unitName] = true --add + end + end + end + end + end + end + end + end + elseif unit:sub(1,7) == '[-blue]' then -- subtract blue coalition + local category = '' + if unit:sub(8) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(8) == '[plane]' then + category = 'plane' + elseif unit:sub(8) == '[ship]' then + category = 'ship' + elseif unit:sub(8) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + if coa == 'blue' then + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + if units_by_name[unit.unitName] then + units_by_name[unit.unitName] = nil --remove + end + end + end + end + end + end + end + end + end + elseif unit:sub(1,5) == '[red]' then -- add red coalition + local category = '' + if unit:sub(6) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(6) == '[plane]' then + category = 'plane' + elseif unit:sub(6) == '[ship]' then + category = 'ship' + elseif unit:sub(6) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + if coa == 'red' then + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + units_by_name[unit.unitName] = true --add + end + end + end + end + end + end + end + end + elseif unit:sub(1,6) == '[-red]' then -- subtract red coalition + local category = '' + if unit:sub(7) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(7) == '[plane]' then + category = 'plane' + elseif unit:sub(7) == '[ship]' then + category = 'ship' + elseif unit:sub(7) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + if coa == 'red' then + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + if units_by_name[unit.unitName] then + units_by_name[unit.unitName] = nil --remove + end + end + end + end + end + end + end + end + end + elseif unit:sub(1,5) == '[all]' then -- add all of a certain category (or all categories) + local category = '' + if unit:sub(6) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(6) == '[plane]' then + category = 'plane' + elseif unit:sub(6) == '[ship]' then + category = 'ship' + elseif unit:sub(6) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + units_by_name[unit.unitName] = true --add + end + end + end + end + end + end + end + elseif unit:sub(1,6) == '[-all]' then -- subtract all of a certain category (or all categories) + local category = '' + if unit:sub(7) == '[helicopter]' then + category = 'helicopter' + elseif unit:sub(7) == '[plane]' then + category = 'plane' + elseif unit:sub(7) == '[ship]' then + category = 'ship' + elseif unit:sub(7) == '[vehicle]' then + category = 'vehicle' + end + for coa, coa_tbl in pairs(l_munits) do + for country, country_table in pairs(coa_tbl) do + for unit_type, unit_type_tbl in pairs(country_table) do + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + for group_ind, group_tbl in pairs(unit_type_tbl) do + if type(group_tbl) == 'table' then + for unit_ind, unit in pairs(group_tbl.units) do + if units_by_name[unit.unitName] then + units_by_name[unit.unitName] = nil --remove + end + end + end + end + end + end + end + end + else -- just a regular unit + units_by_name[unit] = true --add + end + end + + local units_tbl = {} -- indexed sequentially + for unit_name, val in pairs(units_by_name) do + if val then + units_tbl[#units_tbl + 1] = unit_name -- add all the units to the table + end + end + + + units_tbl.processed = timer.getTime() --add the processed flag + return units_tbl +end + +function mist.getDeadMapObjsInZones(zone_names) + -- zone_names: table of zone names + -- returns: table of dead map objects (indexed numerically) + local map_objs = {} + local zones = {} + for i = 1, #zone_names do + if mist.DBs.zonesByName[zone_names[i]] then + zones[#zones + 1] = mist.DBs.zonesByName[zone_names[i]] + end + end + for obj_id, obj in pairs(mist.DBs.deadObjects) do + if obj.objectType and obj.objectType == 'building' then --dead map object + for i = 1, #zones do + if ((zones[i].point.x - obj.objectPos.x)^2 + (zones[i].point.z - obj.objectPos.z)^2)^0.5 <= zones[i].radius then + map_objs[#map_objs + 1] = mist.utils.deepCopy(obj) + end + end + end + end + return map_objs +end + +function mist.getDeadMapObjsInPolygonZone(zone) + -- zone_names: table of zone names + -- returns: table of dead map objects (indexed numerically) + local map_objs = {} + for obj_id, obj in pairs(mist.DBs.deadObjects) do + if obj.objectType and obj.objectType == 'building' then --dead map object + if mist.pointInPolygon(obj.objectPos, zone) then + map_objs[#map_objs + 1] = mist.utils.deepCopy(obj) + end + end + end + return map_objs +end + +function mist.pointInPolygon(point, poly, maxalt) --raycasting point in polygon. Code from http://softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm + --[[local type_tbl = { + point = {'table'}, + poly = {'table'}, + maxalt = {'number', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.pointInPolygon', type_tbl, {point, poly, maxalt}) + assert(err, errmsg) + ]] + point = mist.utils.makeVec3(point) + local px = point.x + local pz = point.z + local cn = 0 + local newpoly = mist.utils.deepCopy(poly) + + if not maxalt or (point.y <= maxalt) then + local polysize = #newpoly + newpoly[#newpoly + 1] = newpoly[1] + + newpoly[1] = mist.utils.makeVec3(newpoly[1]) + + for k = 1, polysize do + newpoly[k+1] = mist.utils.makeVec3(newpoly[k+1]) + if ((newpoly[k].z <= pz) and (newpoly[k+1].z > pz)) or ((newpoly[k].z > pz) and (newpoly[k+1].z <= pz)) then + local vt = (pz - newpoly[k].z) / (newpoly[k+1].z - newpoly[k].z) + if (px < newpoly[k].x + vt*(newpoly[k+1].x - newpoly[k].x)) then + cn = cn + 1 + end + end + end + + return cn%2 == 1 + else + return false + end +end + +function mist.getUnitsInPolygon(unit_names, polyZone, max_alt) + local units = {} + + for i = 1, #unit_names do + units[#units + 1] = Unit.getByName(unitNames[i]) + end + + local inZoneUnits = {} + for i =1, #units do + if units[i]:isActive() and mist.pointInPolygon(units[i]:getPosition().p, polyZone, max_alt) then + inZoneUnits[inZoneUnits + 1] = units[i] + end + end + + return inZoneUnits +end + +function mist.getUnitsInZones(unit_names, zone_names, zone_type) + + zone_type = zone_type or 'cylinder' + if zone_type == 'c' or zone_type == 'cylindrical' or zone_type == 'C' then + zone_type = 'cylinder' + end + if zone_type == 's' or zone_type == 'spherical' or zone_type == 'S' then + zone_type = 'sphere' + end + + assert(zone_type == 'cylinder' or zone_type == 'sphere', 'invalid zone_type: ' .. tostring(zone_type)) + + local units = {} + local zones = {} + + for k = 1, #unit_names do + local unit = Unit.getByName(unit_names[k]) + if unit then + units[#units + 1] = unit + end + end + + + for k = 1, #zone_names do + local zone = trigger.misc.getZone(zone_names[k]) + if zone then + zones[#zones + 1] = {radius = zone.radius, x = zone.point.x, y = zone.point.y, z = zone.point.z} + end + end + + local in_zone_units = {} + + for units_ind = 1, #units do + for zones_ind = 1, #zones do + if zone_type == 'sphere' then --add land height value for sphere zone type + local alt = land.getHeight({x = zones[zones_ind].x, y = zones[zones_ind].z}) + if alt then + zones[zones_ind].y = alt + end + end + local unit_pos = units[units_ind]:getPosition().p + if unit_pos and units[units_ind]:isActive() == true then + if zone_type == 'cylinder' and (((unit_pos.x - zones[zones_ind].x)^2 + (unit_pos.z - zones[zones_ind].z)^2)^0.5 <= zones[zones_ind].radius) then + in_zone_units[#in_zone_units + 1] = units[units_ind] + break + elseif zone_type == 'sphere' and (((unit_pos.x - zones[zones_ind].x)^2 + (unit_pos.y - zones[zones_ind].y)^2 + (unit_pos.z - zones[zones_ind].z)^2)^0.5 <= zones[zones_ind].radius) then + in_zone_units[#in_zone_units + 1] = units[units_ind] + break + end + end + end + end + return in_zone_units +end + +function mist.getUnitsInMovingZones(unit_names, zone_unit_names, radius, zone_type) + + zone_type = zone_type or 'cylinder' + if zone_type == 'c' or zone_type == 'cylindrical' or zone_type == 'C' then + zone_type = 'cylinder' + end + if zone_type == 's' or zone_type == 'spherical' or zone_type == 'S' then + zone_type = 'sphere' + end + + assert(zone_type == 'cylinder' or zone_type == 'sphere', 'invalid zone_type: ' .. tostring(zone_type)) + + local units = {} + local zone_units = {} + + for k = 1, #unit_names do + local unit = Unit.getByName(unit_names[k]) + if unit then + units[#units + 1] = unit + end + end + + for k = 1, #zone_unit_names do + local unit = Unit.getByName(zone_unit_names[k]) + if unit then + zone_units[#zone_units + 1] = unit + end + end + + local in_zone_units = {} + + for units_ind = 1, #units do + for zone_units_ind = 1, #zone_units do + local unit_pos = units[units_ind]:getPosition().p + local zone_unit_pos = zone_units[zone_units_ind]:getPosition().p + if unit_pos and zone_unit_pos and units[units_ind]:isActive() == true then + if zone_type == 'cylinder' and (((unit_pos.x - zone_unit_pos.x)^2 + (unit_pos.z - zone_unit_pos.z)^2)^0.5 <= radius) then + in_zone_units[#in_zone_units + 1] = units[units_ind] + break + elseif zone_type == 'sphere' and (((unit_pos.x - zone_unit_pos.x)^2 + (unit_pos.y - zone_unit_pos.y)^2 + (unit_pos.z - zone_unit_pos.z)^2)^0.5 <= radius) then + in_zone_units[#in_zone_units + 1] = units[units_ind] + break + end + end + end + end + return in_zone_units +end + +function mist.getUnitsLOS(unitset1, altoffset1, unitset2, altoffset2, radius) + radius = radius or math.huge + + local unit_info1 = {} + local unit_info2 = {} + + -- get the positions all in one step, saves execution time. + for unitset1_ind = 1, #unitset1 do + local unit1 = Unit.getByName(unitset1[unitset1_ind]) + if unit1 and unit1:isActive() == true then + unit_info1[#unit_info1 + 1] = {} + unit_info1[#unit_info1].unit = unit1 + unit_info1[#unit_info1].pos = unit1:getPosition().p + end + end + + for unitset2_ind = 1, #unitset2 do + local unit2 = Unit.getByName(unitset2[unitset2_ind]) + if unit2 and unit2:isActive() == true then + unit_info2[#unit_info2 + 1] = {} + unit_info2[#unit_info2].unit = unit2 + unit_info2[#unit_info2].pos = unit2:getPosition().p + end + end + + local LOS_data = {} + -- now compute los + for unit1_ind = 1, #unit_info1 do + local unit_added = false + for unit2_ind = 1, #unit_info2 do + if radius == math.huge or (mist.vec.mag(mist.vec.sub(unit_info1[unit1_ind].pos, unit_info2[unit2_ind].pos)) < radius) then -- inside radius + local point1 = { x = unit_info1[unit1_ind].pos.x, y = unit_info1[unit1_ind].pos.y + altoffset1, z = unit_info1[unit1_ind].pos.z} + local point2 = { x = unit_info2[unit2_ind].pos.x, y = unit_info2[unit2_ind].pos.y + altoffset2, z = unit_info2[unit2_ind].pos.z} + if land.isVisible(point1, point2) then + if unit_added == false then + unit_added = true + LOS_data[#LOS_data + 1] = {} + LOS_data[#LOS_data].unit = unit_info1[unit1_ind].unit + LOS_data[#LOS_data].vis = {} + LOS_data[#LOS_data].vis[#LOS_data[#LOS_data].vis + 1] = unit_info2[unit2_ind].unit + else + LOS_data[#LOS_data].vis[#LOS_data[#LOS_data].vis + 1] = unit_info2[unit2_ind].unit + end + end + end + end + end + + return LOS_data +end + +function mist.getAvgPoint(points) + local avgX, avgY, avgZ, totNum = 0, 0, 0, 0 + for i = 1, #points do + local nPoint = mist.utils.makeVec3(points[i]) + if nPoint.z then + avgX = avgX + nPoint.x + avgY = avgY + nPoint.y + avgZ = avgZ + nPoint.z + totNum = totNum + 1 + end + end + if totNum ~= 0 then + return {x = avgX/totNum, y = avgY/totNum, z = avgZ/totNum} + end +end + +--Gets the average position of a group of units (by name) +function mist.getAvgPos(unitNames) + local avgX, avgY, avgZ, totNum = 0, 0, 0, 0 + for i = 1, #unitNames do + local unit + if Unit.getByName(unitNames[i]) then + unit = Unit.getByName(unitNames[i]) + elseif StaticObject.getByName(unitNames[i]) then + unit = StaticObject.getByName(unitNames[i]) + end + if unit then + local pos = unit:getPosition().p + if pos then -- you never know O.o + avgX = avgX + pos.x + avgY = avgY + pos.y + avgZ = avgZ + pos.z + totNum = totNum + 1 + end + end + end + if totNum ~= 0 then + return {x = avgX/totNum, y = avgY/totNum, z = avgZ/totNum} + end +end + +function mist.getAvgGroupPos(groupName) + if type(groupName) == 'string' and Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then + groupName = Group.getByName(groupName) + end + local units = {} + for i = 1, #groupName:getSize() do + table.insert(units, groupName.getUnit(i):getName()) + end + + return mist.getAvgPos(units) + +end + +--[[ vars for mist.getMGRSString: +vars.units - table of unit names (NOT unitNameTable- maybe this should change). +vars.acc - integer between 0 and 5, inclusive +]] +function mist.getMGRSString(vars) + local units = vars.units + local acc = vars.acc or 5 + local avgPos = mist.getAvgPos(units) + if avgPos then + return mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(avgPos)), acc) + end +end + +--[[ vars for mist.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. +]] +function mist.getLLString(vars) + local units = vars.units + local acc = vars.acc or 3 + local DMS = vars.DMS + local avgPos = mist.getAvgPos(units) + if avgPos then + local lat, lon = coord.LOtoLL(avgPos) + return mist.tostringLL(lat, lon, acc, DMS) + 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. +]] +function mist.getBRString(vars) + local units = vars.units + local ref = mist.utils.makeVec3(vars.ref, 0) -- turn it into Vec3 if it is not already. + local alt = vars.alt + local metric = vars.metric + local avgPos = mist.getAvgPos(units) + if avgPos then + local vec = {x = avgPos.x - ref.x, y = avgPos.y - ref.y, z = avgPos.z - ref.z} + local dir = mist.utils.getDir(vec, ref) + local dist = mist.utils.get2DDist(avgPos, ref) + if alt then + alt = avgPos.y + end + return mist.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 mist.getLeadingPos: +vars.units - table of unit names +vars.heading - direction +vars.radius - number +vars.headingDegrees - boolean, switches heading to degrees +]] +function mist.getLeadingPos(vars) + local units = vars.units + local heading = vars.heading + local radius = vars.radius + if vars.headingDegrees then + heading = mist.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 = mist.vec.rotateVec2(mist.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 mist.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 mist.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. +]] +function mist.getLeadingMGRSString(vars) + local pos = mist.getLeadingPos(vars) + if pos then + local acc = vars.acc or 5 + return mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(pos)), acc) + end +end + +--[[ vars for mist.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. +]] +function mist.getLeadingLLString(vars) + local pos = mist.getLeadingPos(vars) + if pos then + local acc = vars.acc or 3 + local DMS = vars.DMS + local lat, lon = coord.LOtoLL(pos) + return mist.tostringLL(lat, lon, acc, DMS) + end +end + +--[[ vars for mist.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. +]] +function mist.getLeadingBRString(vars) + local pos = mist.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 = mist.utils.getDir(vec, ref) + local dist = mist.utils.get2DDist(pos, ref) + if alt then + alt = pos.y + end + return mist.tostringBR(dir, dist, alt, metric) + end +end + +end + +--- Group functions. +-- @section groups +do -- group functions scope + + --- Check table used for group creation. + -- @tparam table groupData table to check. + -- @treturn boolean true if a group can be spawned using + -- this table, false otherwise. + function mist.groupTableCheck(groupData) + -- return false if country, category + -- or units are missing + if not groupData.country or + not groupData.category or + not groupData.units then + return false + end + -- return false if unitData misses + -- x, y or type + for unitId, unitData in pairs(groupData.units) do + if not unitData.x or + not unitData.y or + not unitData.type then + return false + end + end + -- everything we need is here return true + return true + end + + --- Returns group data table of give group. + function mist.getCurrentGroupData(gpName) + local dbData = mist.getGroupData(gpName) + + if Group.getByName(gpName) and Group.getByName(gpName):isExist() == true then + local newGroup = Group.getByName(gpName) + local newData = {} + newData.name = gpName + newData.groupId = tonumber(newGroup:getID()) + newData.category = newGroup:getCategory() + newData.groupName = gpName + newData.hidden = dbData.hidden + + if newData.category == 2 then + newData.category = 'vehicle' + elseif newData.category == 3 then + newData.category = 'ship' + end + + newData.units = {} + local newUnits = newGroup:getUnits() + for unitNum, unitData in pairs(newGroup:getUnits()) do + newData.units[unitNum] = {} + newData.units[unitNum].unitId = tonumber(unitData:getID()) + newData.units[unitNum].point = unitData.point + newData.units[unitNum].x = unitData:getPosition().p.x + newData.units[unitNum].y = unitData:getPosition().p.z + newData.units[unitNum].type = unitData:getTypeName() + newData.units[unitNum].skill = mist.getUnitSkill(unitData:getName()) + + -- get velocity needed + newData.units[unitNum].unitName = unitData:getName() + newData.units[unitNum].heading = mist.getHeading(unitData, true) -- added to DBs + newData.units[unitNum].alt = unitData:getPosition().p.y + newData.country = string.lower(country.name[unitData:getCountry()]) + newData.units[unitNum].callsign = unitData:getCallsign() + end + + return newData + elseif StaticObject.getByName(gpName) and StaticObject.getByName(gpName):isExist() == true then + local staticObj = StaticObject.getByName(gpName) + dbData.units[1].x = staticObj:getPosition().p.x + dbData.units[1].y = staticObj:getPosition().p.z + dbData.units[1].alt = staticObj:getPosition().p.y + dbData.units[1].heading = mist.getHeading(staticObj, true) + + return dbData + end + + end + + function mist.getGroupData(gpName) + local found = false + local newData = {} + if mist.DBs.groupsByName[gpName] then + newData = mist.utils.deepCopy(mist.DBs.groupsByName[gpName]) + found = true + end + + if found == false then + for groupName, groupData in pairs(mist.DBs.groupsByName) do + if mist.stringMatch(groupName, gpName) == true then + newData = mist.utils.deepCopy(groupData) + newData.groupName = groupName + found = true + break + end + end + end + + local payloads + if newData.category == 'plane' or newData.category == 'helicopter' then + payloads = mist.getGroupPayload(newData.groupName) + end + if found == true then + --newData.hidden = false -- maybe add this to DBs + + for unitNum, unitData in pairs(newData.units) do + newData.units[unitNum] = {} + + newData.units[unitNum].unitId = unitData.unitId + --newData.units[unitNum].point = unitData.point + newData.units[unitNum].x = unitData.point.x + newData.units[unitNum].y = unitData.point.y + newData.units[unitNum].alt = unitData.alt + newData.units[unitNum].alt_type = unitData.alt_type + newData.units[unitNum].speed = unitData.speed + newData.units[unitNum].type = unitData.type + newData.units[unitNum].skill = unitData.skill + newData.units[unitNum].unitName = unitData.unitName + newData.units[unitNum].heading = unitData.heading -- added to DBs + newData.units[unitNum].playerCanDrive = unitData.playerCanDrive -- added to DBs + + + if newData.category == 'plane' or newData.category == 'helicopter' then + newData.units[unitNum].payload = payloads[unitNum] + newData.units[unitNum].livery_id = unitData.livery_id + newData.units[unitNum].onboard_num = unitData.onboard_num + newData.units[unitNum].callsign = unitData.callsign + newData.units[unitNum].AddPropAircraft = unitData.AddPropAircraft + end + if newData.category == 'static' then + newData.units[unitNum].categoryStatic = unitData.categoryStatic + newData.units[unitNum].mass = unitData.mass + newData.units[unitNum].canCargo = unitData.canCargo + newData.units[unitNum].shape_name = unitData.shape_name + end + end + + return newData + else + log:error('$1 not found in MIST database', gpName) + return + end + end + + function mist.getPayload(unitIdent) + -- refactor to search by groupId and allow groupId and groupName as inputs + local unitId = unitIdent + if type(unitIdent) == 'string' and not tonumber(unitIdent) then + unitId = mist.DBs.MEunitsByName[unitIdent].unitId + end + local gpId = mist.DBs.MEunitsById[unitId].groupId + + if gpId and unitId then + 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 + for unitIndex, unitData in pairs(group_data.units) do --group index + if unitData.unitId == unitId then + return unitData.payload + end + end + end + end + end + end + end + end + end + end + end + else + log:error('Need string or number. Got: $1', type(unitIdent)) + return false + end + log:warn("Couldn't find payload for unit: $1", unitIdent) + return + end + + function mist.getGroupPayload(groupIdent) + local gpId = groupIdent + if type(groupIdent) == 'string' and not tonumber(groupIdent) then + gpId = mist.DBs.MEgroupsByName[groupIdent].groupId + end + + if gpId then + 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 + local payloads = {} + for unitIndex, unitData in pairs(group_data.units) do --group index + payloads[unitIndex] = unitData.payload + end + return payloads + end + end + end + end + end + end + end + end + end + else + log:error('Need string or number. Got: $1', type(groupIdent)) + return false + end + log:warn("Couldn't find payload for group: $1", groupIdent) + return + + end + + function mist.teleportToPoint(vars) -- main teleport function that all of teleport/respawn functions call + local point = vars.point + + local gpName + if vars.gpName then + gpName = vars.gpName + elseif vars.groupName then + gpName = vars.groupName + else + log:error('Missing field groupName or gpName in variable table') + end + + local action = vars.action + + local disperse = vars.disperse or false + local maxDisp = vars.maxDisp + if not vars.maxDisp then + maxDisp = 200 + else + maxDisp = vars.maxDisp + end + local radius = vars.radius or 0 + local innerRadius = vars.innerRadius + + local route = vars.route + local dbData = false + + local newGroupData + if gpName and not vars.groupData then + if string.lower(action) == 'teleport' or string.lower(action) == 'tele' then + newGroupData = mist.getCurrentGroupData(gpName) + elseif string.lower(action) == 'respawn' then + newGroupData = mist.getGroupData(gpName) + dbData = true + elseif string.lower(action) == 'clone' then + newGroupData = mist.getGroupData(gpName) + newGroupData.clone = 'order66' + dbData = true + else + action = 'tele' + newGroupData = mist.getCurrentGroupData(gpName) + end + else + action = 'tele' + newGroupData = vars.groupData + end + + local diff = {x = 0, y = 0} + local newCoord, origCoord + if point then + local valid = false + + local validTerrain + if string.lower(newGroupData.category) == 'ship' then + validTerrain = {'SHALLOW_WATER' , 'WATER'} + elseif string.lower(newGroupData.category) == 'vehicle' then + validTerrain = {'LAND', 'ROAD'} + else + validTerrain = {'LAND', 'ROAD', 'SHALLOW_WATER', 'WATER', 'RUNWAY'} + end + + for i = 1, 100 do + newCoord = mist.getRandPointInCircle(point, radius, innerRadius) + if mist.isTerrainValid(newCoord, validTerrain) then + origCoord = mist.utils.deepCopy(newCoord) + diff = {x = (newCoord.x - newGroupData.units[1].x), y = (newCoord.y - newGroupData.units[1].y)} + valid = true + break + end + end + if valid == false then + log:error('point supplied in variable table is not a valid coordinate.') + return false + end + end + if not newGroupData.country and mist.DBs.groupsByName[newGroupData.groupName].country then + newGroupData.country = mist.DBs.groupsByName[newGroupData.groupName].country + end + if not newGroupData.category and mist.DBs.groupsByName[newGroupData.groupName].category then + newGroupData.category = mist.DBs.groupsByName[newGroupData.groupName].category + end + + for unitNum, unitData in pairs(newGroupData.units) do + if disperse then + if maxDisp and type(maxDisp) == 'number' and unitNum ~= 1 then + newCoord = mist.getRandPointInCircle(origCoord, maxDisp) + --else + --newCoord = mist.getRandPointInCircle(zone.point, zone.radius) + end + + newGroupData.units[unitNum].x = newCoord.x + newGroupData.units[unitNum].y = newCoord.y + else + newGroupData.units[unitNum].x = unitData.x + diff.x + newGroupData.units[unitNum].y = unitData.y + diff.y + end + if point then + if (newGroupData.category == 'plane' or newGroupData.category == 'helicopter') then + if point.z and point.y > 0 and point.y > land.getHeight({newGroupData.units[unitNum].x, newGroupData.units[unitNum].y}) + 10 then + newGroupData.units[unitNum].alt = point.y + else + if newGroupData.category == 'plane' then + newGroupData.units[unitNum].alt = land.getHeight({newGroupData.units[unitNum].x, newGroupData.units[unitNum].y}) + math.random(300, 9000) + else + newGroupData.units[unitNum].alt = land.getHeight({newGroupData.units[unitNum].x, newGroupData.units[unitNum].y}) + math.random(200, 3000) + end + end + end + end + end + + if newGroupData.start_time then + newGroupData.startTime = newGroupData.start_time + end + + if newGroupData.startTime and newGroupData.startTime ~= 0 and dbData == true then + local timeDif = timer.getAbsTime() - timer.getTime0() + if timeDif > newGroupData.startTime then + newGroupData.startTime = 0 + else + newGroupData.startTime = newGroupData.startTime - timeDif + end + + end + + if route then + newGroupData.route = route + end + --mist.debug.writeData(mist.utils.serialize,{'teleportToPoint', newGroupData}, 'newGroupData.lua') + if string.lower(newGroupData.category) == 'static' then + return mist.dynAddStatic(newGroupData) + end + return mist.dynAdd(newGroupData) + + end + + function mist.respawnInZone(gpName, zone, disperse, maxDisp) + + if type(gpName) == 'table' and gpName:getName() then + gpName = gpName:getName() + elseif type(gpName) == 'table' and gpName[1]:getName() then + gpName = math.random(#gpName) + else + gpName = tostring(gpName) + 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 + local vars = {} + vars.gpName = gpName + vars.action = 'respawn' + vars.point = zone.point + vars.radius = zone.radius + vars.disperse = disperse + vars.maxDisp = maxDisp + return mist.teleportToPoint(vars) + end + + function mist.cloneInZone(gpName, zone, disperse, maxDisp) + + if type(gpName) == 'table' then + gpName = gpName:getName() + else + gpName = tostring(gpName) + 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 + local vars = {} + vars.gpName = gpName + vars.action = 'clone' + vars.point = zone.point + vars.radius = zone.radius + vars.disperse = disperse + vars.maxDisp = maxDisp + return mist.teleportToPoint(vars) + end + + function mist.teleportInZone(gpName, zone, disperse, maxDisp) -- groupName, zoneName or table of Zone Names, keepForm is a boolean + if type(gpName) == 'table' and gpName:getName() then + gpName = gpName:getName() + else + gpName = tostring(gpName) + 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 + + local vars = {} + vars.gpName = gpName + vars.action = 'tele' + vars.point = zone.point + vars.radius = zone.radius + vars.disperse = disperse + vars.maxDisp = maxDisp + return mist.teleportToPoint(vars) + end + + function mist.respawnGroup(gpName, task) + local vars = {} + vars.gpName = gpName + vars.action = 'respawn' + if task and type(task) ~= 'number' then + vars.route = mist.getGroupRoute(gpName, 'task') + end + local newGroup = mist.teleportToPoint(vars) + if task and type(task) == 'number' then + local newRoute = mist.getGroupRoute(gpName, 'task') + mist.scheduleFunction(mist.goRoute, {newGroup, newRoute}, timer.getTime() + task) + end + return newGroup + end + + function mist.cloneGroup(gpName, task) + local vars = {} + vars.gpName = gpName + vars.action = 'clone' + if task and type(task) ~= 'number' then + vars.route = mist.getGroupRoute(gpName, 'task') + end + local newGroup = mist.teleportToPoint(vars) + if task and type(task) == 'number' then + local newRoute = mist.getGroupRoute(gpName, 'task') + mist.scheduleFunction(mist.goRoute, {newGroup, newRoute}, timer.getTime() + task) + end + return newGroup + end + + function mist.teleportGroup(gpName, task) + local vars = {} + vars.gpName = gpName + vars.action = 'teleport' + if task and type(task) ~= 'number' then + vars.route = mist.getGroupRoute(gpName, 'task') + end + local newGroup = mist.teleportToPoint(vars) + if task and type(task) == 'number' then + local newRoute = mist.getGroupRoute(gpName, 'task') + mist.scheduleFunction(mist.goRoute, {newGroup, newRoute}, timer.getTime() + task) + end + return newGroup + end + + function mist.spawnRandomizedGroup(groupName, vars) -- need to debug + if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then + local gpData = mist.getGroupData(groupName) + gpData.units = mist.randomizeGroupOrder(gpData.units, vars) + gpData.route = mist.getGroupRoute(groupName, 'task') + + mist.dynAdd(gpData) + end + + return true + end + + function mist.randomizeNumTable(vars) + local newTable = {} + + local excludeIndex = {} + local randomTable = {} + + if vars and vars.exclude and type(vars.exclude) == 'table' then + for index, data in pairs(vars.exclude) do + excludeIndex[data] = true + end + end + + local low, hi, size + + if vars.size then + size = vars.size + end + + if vars and vars.lowerLimit and type(vars.lowerLimit) == 'number' then + low = mist.utils.round(vars.lowerLimit) + else + low = 1 + end + + if vars and vars.upperLimit and type(vars.upperLimit) == 'number' then + hi = mist.utils.round(vars.upperLimit) + else + hi = size + end + + local choices = {} + -- add to exclude list and create list of what to randomize + for i = 1, size do + if not (i >= low and i <= hi) then + + excludeIndex[i] = true + end + if not excludeIndex[i] then + table.insert(choices, i) + else + newTable[i] = i + end + end + + for ind, num in pairs(choices) do + local found = false + local x = 0 + while found == false do + x = mist.random(size) -- get random number from list + local addNew = true + for index, _ in pairs(excludeIndex) do + if index == x then + addNew = false + break + end + end + if addNew == true then + excludeIndex[x] = true + found = true + end + excludeIndex[x] = true + + end + newTable[num] = x + end + --[[ + for i = 1, #newTable do + log:info(newTable[i]) + end + ]] + return newTable + end + + function mist.randomizeGroupOrder(passedUnits, vars) + -- figure out what to exclude, and send data to other func + local units = passedUnits + + if passedUnits.units then + units = passUnits.units + end + + local exclude = {} + local excludeNum = {} + if vars and vars.excludeType and type(vars.excludeType) == 'table' then + exclude = vars.excludeType + end + + if vars and vars.excludeNum and type(vars.excludeNum) == 'table' then + excludeNum = vars.excludeNum + end + + local low, hi + + if vars and vars.lowerLimit and type(vars.lowerLimit) == 'number' then + low = mist.utils.round(vars.lowerLimit) + else + low = 1 + end + + if vars and vars.upperLimit and type(vars.upperLimit) == 'number' then + hi = mist.utils.round(vars.upperLimit) + else + hi = #units + end + + + local excludeNum = {} + for unitIndex, unitData in pairs(units) do + if unitIndex >= low and unitIndex <= hi then -- if within range + local found = false + if #exclude > 0 then + for excludeType, index in pairs(exclude) do -- check if excluded + if mist.stringMatch(excludeType, unitData.type) then -- if excluded + excludeNum[unitIndex] = unitIndex + found = true + end + end + end + else -- unitIndex is either to low, or to high: added to exclude list + excludeNum[unitIndex] = unitId + end + end + + local newGroup = {} + local newOrder = mist.randomizeNumTable({exclude = excludeNum, size = #units}) + + for unitIndex, unitData in pairs(units) do + for i = 1, #newOrder do + if newOrder[i] == unitIndex then + newGroup[i] = mist.utils.deepCopy(units[i]) -- gets all of the unit data + newGroup[i].type = mist.utils.deepCopy(unitData.type) + newGroup[i].skill = mist.utils.deepCopy(unitData.skill) + newGroup[i].unitName = mist.utils.deepCopy(unitData.unitName) + newGroup[i].unitIndex = mist.utils.deepCopy(unitData.unitIndex) -- replaces the units data with a new type + end + end + end + return newGroup + end + + function mist.random(firstNum, secondNum) -- no support for decimals + local lowNum, highNum + if not secondNum then + highNum = firstNum + lowNum = 1 + else + lowNum = firstNum + highNum = secondNum + end + local total = 1 + if math.abs(highNum - lowNum + 1) < 50 then -- if total values is less than 50 + total = math.modf(50/math.abs(highNum - lowNum + 1)) -- make x copies required to be above 50 + end + local choices = {} + for i = 1, total do -- iterate required number of times + for x = lowNum, highNum do -- iterate between the range + choices[#choices +1] = x -- add each entry to a table + end + end + local rtnVal = math.random(#choices) -- will now do a math.random of at least 50 choices + for i = 1, 10 do + rtnVal = math.random(#choices) -- iterate a few times for giggles + end + return choices[rtnVal] + end + + function mist.stringMatch(s1, s2, bool) + local exclude = {'%-', '%(', '%)', '%_', '%[', '%]', '%.', '%#', '% ', '%{', '%}', '%$', '%%', '%?', '%+', '%^'} + if type(s1) == 'string' and type(s2) == 'string' then + for i , str in pairs(exclude) do + s1 = string.gsub(s1, str, '') + s2 = string.gsub(s2, str, '') + end + if not bool then + s1 = string.lower(s1) + s2 = string.lower(s2) + end + + if s1 == s2 then + return true + else + return false + end + else + log:error('Either the first or second variable were not a string') + return false + end + end + + mist.matchString = mist.stringMatch -- both commands work because order out type of I + + --[[ scope: +{ + units = {...}, -- unit names. + coa = {...}, -- coa names + countries = {...}, -- country names + CA = {...}, -- looks just like coa. + unitTypes = { red = {}, blue = {}, all = {}, Russia = {},} +} + + +scope examples: + +{ units = { 'Hawg11', 'Hawg12' }, CA = {'blue'} } + +{ countries = {'Georgia'}, unitTypes = {blue = {'A-10C', 'A-10A'}}} + +{ coa = {'all'}} + +{unitTypes = { blue = {'A-10C'}}} +]] +end + +--- Utility functions. +-- E.g. conversions between units etc. +-- @section mist.utils +do -- mist.util scope + mist.utils = {} + + --- Converts angle in radians to degrees. + -- @param angle angle in radians + -- @return angle in degrees + function mist.utils.toDegree (angle) + return angle*180/math.pi + end + + --- Converts angle in degrees to radians. + -- @param angle angle in degrees + -- @return angle in degrees + function mist.utils.toRadian (angle) + return angle*math.pi/180 + end + + --- Converts meters to nautical miles. + -- @param meters distance in meters + -- @return distance in nautical miles + function mist.utils.metersToNM (meters) + return meters/1852 + end + + --- Converts meters to feet. + -- @param meters distance in meters + -- @return distance in feet + function mist.utils.metersToFeet (meters) + return meters/0.3048 + end + + --- Converts nautical miles to meters. + -- @param nm distance in nautical miles + -- @return distance in meters + function mist.utils.NMToMeters (nm) + return NM*1852 + end + + --- Converts feet to meters. + -- @param feet distance in feet + -- @return distance in meters + function mist.utils.feetToMeters (feet) + return feet*0.3048 + end + + --- Converts meters per second to knots. + -- @param mps speed in m/s + -- @return speed in knots + function mist.utils.mpsToKnots (mps) + return mps*3600/1852 + end + + --- Converts meters per second to kilometers per hour. + -- @param mps speed in m/s + -- @return speed in km/h + function mist.utils.mpsToKmph (mps) + return mps*3.6 + end + + --- Converts knots to meters per second. + -- @param knots speed in knots + -- @return speed in m/s + function mist.utils.knotsToMps (knots) + return knots*1852/3600 + end + + --- Converts kilometers per hour to meters per second. + -- @param kmph speed in km/h + -- @return speed in m/s + function mist.utils.kmphToMps (kmph) + return kmph/3.6 + end + + --- Converts a Vec3 to a Vec2. + -- @tparam Vec3 vec the 3D vector + -- @return vector converted to Vec2 + function mist.utils.makeVec2(vec) + if vec.z then + return {x = vec.x, y = vec.z} + else + return {x = vec.x, y = vec.y} -- it was actually already vec2. + end + end + + --- Converts a Vec2 to a Vec3. + -- @tparam Vec2 vec the 2D vector + -- @param y optional new y axis (altitude) value. If omitted it's 0. + function mist.utils.makeVec3(vec, y) + if not vec.z then + if vec.alt and not y then + y = vec.alt + elseif not y then + y = 0 + end + return {x = vec.x, y = y, z = vec.y} + else + return {x = vec.x, y = vec.y, z = vec.z} -- it was already Vec3, actually. + end + end + + --- Converts a Vec2 to a Vec3 using ground level as altitude. + -- The ground level at the specific point is used as altitude (y-axis) + -- for the new vector. Optionally a offset can be specified. + -- @tparam Vec2 vec the 2D vector + -- @param[opt] offset offset to be applied to the ground level + -- @return new 3D vector + function mist.utils.makeVec3GL(vec, offset) + local adj = offset or 0 + + if not vec.z then + return {x = vec.x, y = (land.getHeight(vec) + adj), z = vec.y} + else + return {x = vec.x, y = (land.getHeight({x = vec.x, y = vec.z}) + adj), z = vec.z} + end + end + + --- Returns the center of a zone as Vec3. + -- @tparam string|table zone trigger zone name or table + -- @treturn Vec3 center of the zone + function mist.utils.zoneToVec3 (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 + + --- Returns heading-error corrected direction. + -- True-north corrected direction from point along vector vec. + -- @tparam Vec3 vec + -- @tparam Vec2 point + -- @return heading-error corrected direction from point. + function mist.utils.getDir(vec, point) + local dir = math.atan2(vec.z, vec.x) + if point then + dir = dir + mist.getNorthCorrection(point) + end + if dir < 0 then + dir = dir + 2 * math.pi -- put dir in range of 0 to 2*pi + end + return dir + end + + --- Returns distance in meters between two points. + -- @tparam Vec2|Vec3 point1 first point + -- @tparam Vec2|Vec3 point2 second point + -- @treturn number distance between given points. + function mist.utils.get2DDist(point1, point2) + point1 = mist.utils.makeVec3(point1) + point2 = mist.utils.makeVec3(point2) + return mist.vec.mag({x = point1.x - point2.x, y = 0, z = point1.z - point2.z}) + end + + --- Returns distance in meters between two points in 3D space. + -- @tparam Vec3 point1 first point + -- @tparam Vec3 point2 second point + -- @treturn number distancen between given points in 3D space. + function mist.utils.get3DDist(point1, point2) + return mist.vec.mag({x = point1.x - point2.x, y = point1.y - point2.y, z = point1.z - point2.z}) + end + + --- Creates a waypoint from a vector. + -- @tparam Vec2|Vec3 vec position of the new waypoint + -- @treturn Waypoint a new waypoint to be used inside paths. + function mist.utils.vecToWP(vec) + local newWP = {} + newWP.x = vec.x + newWP.y = vec.y + if vec.z then + newWP.alt = vec.y + newWP.y = vec.z + else + newWP.alt = land.getHeight({x = vec.x, y = vec.y}) + end + return newWP + end + + --- Creates a waypoint from a unit. + -- This function also considers the units speed. + -- The alt_type of this waypoint is set to "BARO". + -- @tparam Unit pUnit Unit whose position and speed will be used. + -- @treturn Waypoint new waypoint. + function mist.utils.unitToWP(pUnit) + local unit = mist.utils.deepCopy(pUnit) + if type(unit) == 'string' then + if Unit.getByName(unit) then + unit = Unit.getByName(unit) + end + end + if unit:isExist() == true then + local new = mist.utils.vecToWP(unit:getPosition().p) + new.speed = mist.vec.mag(unit:getVelocity()) + new.alt_type = "BARO" + + return new + end + return false + end + + --- Creates a deep copy of a object. + -- Usually this object is a table. + -- See also: from http://lua-users.org/wiki/CopyTable + -- @param object object to copy + -- @return copy of object + function mist.utils.deepCopy(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 + return _copy(object) + end + + --- Simple rounding function. + -- From http://lua-users.org/wiki/SimpleRound + -- use negative idp for rounding ahead of decimal place, positive for rounding after decimal place + -- @tparam number num number to round + -- @param idp + function mist.utils.round(num, idp) + local mult = 10^(idp or 0) + return math.floor(num * mult + 0.5) / mult + end + + --- Rounds all numbers inside a table. + -- @tparam table tbl table in which to round numbers + -- @param idp + function mist.utils.roundTbl(tbl, idp) + for id, val in pairs(tbl) do + if type(val) == 'number' then + tbl[id] = mist.utils.round(val, idp) + end + end + return tbl + end + + --- Executes the given string. + -- borrowed from Slmod + -- @tparam string s string containing LUA code. + -- @treturn boolean true if successfully executed, false otherwise + function mist.utils.dostring(s) + local f, err = loadstring(s) + if f then + return true, f() + else + return false, err + end + end + + --- Checks a table's types. + -- This function checks a tables types against a specifically forged type table. + -- @param fname + -- @tparam table type_tbl + -- @tparam table var_tbl + -- @usage -- specifically forged type table + -- type_tbl = { + -- {'table', 'number'}, + -- 'string', + -- 'number', + -- 'number', + -- {'string','nil'}, + -- {'number', 'nil'} + -- } + -- -- my_tbl index 1 must be a table or a number; + -- -- index 2, a string; index 3, a number; + -- -- index 4, a number; index 5, either a string or nil; + -- -- and index 6, either a number or nil. + -- mist.utils.typeCheck(type_tbl, my_tb) + -- @return true if table passes the check, false otherwise. + function mist.utils.typeCheck(fname, type_tbl, var_tbl) + -- log:info('type check') + for type_key, type_val in pairs(type_tbl) do + -- log:info('type_key: $1 type_val: $2', type_key, type_val) + + --type_key can be a table of accepted keys- so try to find one that is not nil + local type_key_str = '' + local act_key = type_key -- actual key within var_tbl - necessary to use for multiple possible key variables. Initialize to type_key + if type(type_key) == 'table' then + + for i = 1, #type_key do + if i ~= 1 then + type_key_str = type_key_str .. '/' + end + type_key_str = type_key_str .. tostring(type_key[i]) + if var_tbl[type_key[i]] ~= nil then + act_key = type_key[i] -- found a non-nil entry, make act_key now this val. + end + end + else + type_key_str = tostring(type_key) + end + + local err_msg = 'Error in function ' .. fname .. ', parameter "' .. type_key_str .. '", expected: ' + local passed_check = false + + if type(type_tbl[type_key]) == 'table' then + -- log:info('err_msg, before: $1', err_msg) + for j = 1, #type_tbl[type_key] do + + if j == 1 then + err_msg = err_msg .. type_tbl[type_key][j] + else + err_msg = err_msg .. ' or ' .. type_tbl[type_key][j] + end + + if type(var_tbl[act_key]) == type_tbl[type_key][j] then + passed_check = true + end + end + -- log:info('err_msg, after: $1', err_msg) + else + -- log:info('err_msg, before: $1', err_msg) + err_msg = err_msg .. type_tbl[type_key] + -- log:info('err_msg, after: $1', err_msg) + if type(var_tbl[act_key]) == type_tbl[type_key] then + passed_check = true + end + + end + + if not passed_check then + err_msg = err_msg .. ', got ' .. type(var_tbl[act_key]) + return false, err_msg + end + end + return true + end + + --- Serializes the give variable to a string. + -- borrowed from slmod + -- @param var variable to serialize + -- @treturn string variable serialized to string + function mist.utils.basicSerialize(var) + if var == nil then + return "\"\"" + else + if ((type(var) == 'number') or + (type(var) == 'boolean') or + (type(var) == 'function') or + (type(var) == 'table') or + (type(var) == 'userdata') ) then + return tostring(var) + elseif type(var) == 'string' then + var = string.format('%q', var) + return var + end + end +end + +--- Serialize value +-- borrowed from slmod (serialize_slmod) +-- @param name +-- @param value value to serialize +-- @param level +function mist.utils.serialize(name, value, level) + --Based on ED's serialize_simple2 + local function basicSerialize(o) + if type(o) == "number" then + return tostring(o) + elseif type(o) == "boolean" then + return tostring(o) + else -- assume it is a string + return mist.utils.basicSerialize(o) + end + end + + local function serializeToTbl(name, value, level) + local var_str_tbl = {} + if level == nil then level = "" end + if level ~= "" then level = level.." " end + + table.insert(var_str_tbl, level .. name .. " = ") + + if type(value) == "number" or type(value) == "string" or type(value) == "boolean" then + table.insert(var_str_tbl, basicSerialize(value) .. ",\n") + elseif type(value) == "table" then + table.insert(var_str_tbl, "\n"..level.."{\n") + + for k,v in pairs(value) do -- serialize its fields + local key + if type(k) == "number" then + key = string.format("[%s]", k) + else + key = string.format("[%q]", k) + end + + table.insert(var_str_tbl, mist.utils.serialize(key, v, level.." ")) + + end + if level == "" then + table.insert(var_str_tbl, level.."} -- end of "..name.."\n") + + else + table.insert(var_str_tbl, level.."}, -- end of "..name.."\n") + + end + else + log:error('Cannot serialize a $1', type(value)) + end + return var_str_tbl + end + + local t_str = serializeToTbl(name, value, level) + + return table.concat(t_str) +end + +--- Serialize value supporting cycles. +-- borrowed from slmod (serialize_wcycles) +-- @param name +-- @param value value to serialize +-- @param saved +function mist.utils.serializeWithCycles(name, value, saved) + --mostly straight out of Programming in Lua + local function basicSerialize(o) + if type(o) == "number" then + return tostring(o) + elseif type(o) == "boolean" then + return tostring(o) + else -- assume it is a string + return mist.utils.basicSerialize(o) + end + end + + local t_str = {} + saved = saved or {} -- initial value + if ((type(value) == 'string') or (type(value) == 'number') or (type(value) == 'table') or (type(value) == 'boolean')) then + table.insert(t_str, name .. " = ") + if type(value) == "number" or type(value) == "string" or type(value) == "boolean" then + table.insert(t_str, basicSerialize(value) .. "\n") + else + + if saved[value] then -- value already saved? + table.insert(t_str, saved[value] .. "\n") + else + saved[value] = name -- save name for next time + table.insert(t_str, "{}\n") + for k,v in pairs(value) do -- save its fields + local fieldname = string.format("%s[%s]", name, basicSerialize(k)) + table.insert(t_str, mist.utils.serializeWithCycles(fieldname, v, saved)) + end + end + end + return table.concat(t_str) + else + return "" + end +end + +--- Serialize a table to a single line string. +-- serialization of a table all on a single line, no comments, made to replace old get_table_string function +-- borrowed from slmod +-- @tparam table tbl table to serialize. +-- @treturn string string containing serialized table +function mist.utils.oneLineSerialize(tbl) + if type(tbl) == 'table' then --function only works for tables! + + local tbl_str = {} + + tbl_str[#tbl_str + 1] = '{ ' + + for ind,val in pairs(tbl) do -- serialize its fields + if type(ind) == "number" then + tbl_str[#tbl_str + 1] = '[' + tbl_str[#tbl_str + 1] = tostring(ind) + tbl_str[#tbl_str + 1] = '] = ' + else --must be a string + tbl_str[#tbl_str + 1] = '[' + tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(ind) + tbl_str[#tbl_str + 1] = '] = ' + end + + if ((type(val) == 'number') or (type(val) == 'boolean')) then + tbl_str[#tbl_str + 1] = tostring(val) + tbl_str[#tbl_str + 1] = ', ' + elseif type(val) == 'string' then + tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(val) + tbl_str[#tbl_str + 1] = ', ' + elseif type(val) == 'nil' then -- won't ever happen, right? + tbl_str[#tbl_str + 1] = 'nil, ' + elseif type(val) == 'table' then + tbl_str[#tbl_str + 1] = mist.utils.oneLineSerialize(val) + tbl_str[#tbl_str + 1] = ', ' --I think this is right, I just added it + else + log:war('Unable to serialize value type $1 at index $2', mist.utils.basicSerialize(type(val)), tostring(ind)) + end + + end + tbl_str[#tbl_str + 1] = '}' + return table.concat(tbl_str) + end +end + +--- Returns table in a easy readable string representation. +-- this function is not meant for serialization because it uses +-- newlines for better readability. +-- @param tbl table to show +-- @param loc +-- @param indent +-- @param tableshow_tbls +-- @return human readable string representation of given table +function mist.utils.tableShow(tbl, loc, indent, tableshow_tbls) --based on serialize_slmod, this is a _G serialization + tableshow_tbls = tableshow_tbls or {} --create table of tables + loc = loc or "" + indent = indent or "" + if type(tbl) == 'table' then --function only works for tables! + tableshow_tbls[tbl] = loc + + local tbl_str = {} + + tbl_str[#tbl_str + 1] = indent .. '{\n' + + for ind,val in pairs(tbl) do -- serialize its fields + if type(ind) == "number" then + tbl_str[#tbl_str + 1] = indent + tbl_str[#tbl_str + 1] = loc .. '[' + tbl_str[#tbl_str + 1] = tostring(ind) + tbl_str[#tbl_str + 1] = '] = ' + else + tbl_str[#tbl_str + 1] = indent + tbl_str[#tbl_str + 1] = loc .. '[' + tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(ind) + tbl_str[#tbl_str + 1] = '] = ' + end + + if ((type(val) == 'number') or (type(val) == 'boolean')) then + tbl_str[#tbl_str + 1] = tostring(val) + tbl_str[#tbl_str + 1] = ',\n' + elseif type(val) == 'string' then + tbl_str[#tbl_str + 1] = mist.utils.basicSerialize(val) + tbl_str[#tbl_str + 1] = ',\n' + elseif type(val) == 'nil' then -- won't ever happen, right? + tbl_str[#tbl_str + 1] = 'nil,\n' + elseif type(val) == 'table' then + if tableshow_tbls[val] then + tbl_str[#tbl_str + 1] = tostring(val) .. ' already defined: ' .. tableshow_tbls[val] .. ',\n' + else + tableshow_tbls[val] = loc .. '[' .. mist.utils.basicSerialize(ind) .. ']' + tbl_str[#tbl_str + 1] = tostring(val) .. ' ' + tbl_str[#tbl_str + 1] = mist.utils.tableShow(val, loc .. '[' .. mist.utils.basicSerialize(ind).. ']', indent .. ' ', tableshow_tbls) + tbl_str[#tbl_str + 1] = ',\n' + end + elseif type(val) == 'function' then + if debug and debug.getinfo then + local fcnname = tostring(val) + local info = debug.getinfo(val, "S") + if info.what == "C" then + tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', C function') .. ',\n' + else + if (string.sub(info.source, 1, 2) == [[./]]) then + tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', defined in (' .. info.linedefined .. '-' .. info.lastlinedefined .. ')' .. info.source) ..',\n' + else + tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', defined in (' .. info.linedefined .. '-' .. info.lastlinedefined .. ')') ..',\n' + end + end + + else + tbl_str[#tbl_str + 1] = 'a function,\n' + end + else + tbl_str[#tbl_str + 1] = 'unable to serialize value type ' .. mist.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind) + end + end + + tbl_str[#tbl_str + 1] = indent .. '}' + return table.concat(tbl_str) + end +end +end + +--- Debug functions +-- @section mist.debug +do -- mist.debug scope + mist.debug = {} + + --- Dumps the global table _G. + -- This dumps the global table _G to a file in + -- the DCS\Logs directory. + -- This function requires you to disable script sanitization + -- in $DCS_ROOT\Scripts\MissionScripting.lua to access lfs and io + -- libraries. + -- @param fname + function mist.debug.dump_G(fname) + if lfs and io then + local fdir = lfs.writedir() .. [[Logs\]] .. fname + local f = io.open(fdir, 'w') + f:write(mist.utils.tableShow(_G)) + f:close() + log:info('Wrote debug data to $1', fdir) + --trigger.action.outText(errmsg, 10) + else + log:alert('insufficient libraries to run mist.debug.dump_G, you must disable the sanitization of the io and lfs libraries in ./Scripts/MissionScripting.lua') + --trigger.action.outText(errmsg, 10) + end + end + + --- Write debug data to file. + -- This function requires you to disable script sanitization + -- in $DCS_ROOT\Scripts\MissionScripting.lua to access lfs and io + -- libraries. + -- @param fcn + -- @param fcnVars + -- @param fname + function mist.debug.writeData(fcn, fcnVars, fname) + if lfs and io then + local fdir = lfs.writedir() .. [[Logs\]] .. fname + local f = io.open(fdir, 'w') + f:write(fcn(unpack(fcnVars, 1, table.maxn(fcnVars)))) + f:close() + log:info('Wrote debug data to $1', fdir) + local errmsg = 'mist.debug.writeData wrote data to ' .. fdir + trigger.action.outText(errmsg, 10) + else + local errmsg = 'Error: insufficient libraries to run mist.debug.writeData, you must disable the sanitization of the io and lfs libraries in ./Scripts/MissionScripting.lua' + log:alert(errmsg) + trigger.action.outText(errmsg, 10) + end + end + + --- Write mist databases to file. + -- This function requires you to disable script sanitization + -- in $DCS_ROOT\Scripts\MissionScripting.lua to access lfs and io + -- libraries. + function mist.debug.dumpDBs() + for DBname, DB in pairs(mist.DBs) do + if type(DB) == 'table' and type(DBname) == 'string' then + mist.debug.writeData(mist.utils.serialize, {DBname, DB}, 'mist_DBs_' .. DBname .. '.lua') + end + end + end +end + +--- 3D Vector functions +-- @section mist.vec +do -- mist.vec scope + mist.vec = {} + + --- Vector addition. + -- @tparam Vec3 vec1 first vector + -- @tparam Vec3 vec2 second vector + -- @treturn Vec3 new vector, sum of vec1 and vec2. + function mist.vec.add(vec1, vec2) + return {x = vec1.x + vec2.x, y = vec1.y + vec2.y, z = vec1.z + vec2.z} + end + + --- Vector substraction. + -- @tparam Vec3 vec1 first vector + -- @tparam Vec3 vec2 second vector + -- @treturn Vec3 new vector, vec2 substracted from vec1. + function mist.vec.sub(vec1, vec2) + return {x = vec1.x - vec2.x, y = vec1.y - vec2.y, z = vec1.z - vec2.z} + end + + --- Vector scalar multiplication. + -- @tparam Vec3 vec vector to multiply + -- @tparam number mult scalar multiplicator + -- @treturn Vec3 new vector multiplied with the given scalar + function mist.vec.scalarMult(vec, mult) + return {x = vec.x*mult, y = vec.y*mult, z = vec.z*mult} + end + + mist.vec.scalar_mult = mist.vec.scalarMult + + --- Vector dot product. + -- @tparam Vec3 vec1 first vector + -- @tparam Vec3 vec2 second vector + -- @treturn number dot product of given vectors + function mist.vec.dp (vec1, vec2) + return vec1.x*vec2.x + vec1.y*vec2.y + vec1.z*vec2.z + end + + --- Vector cross product. + -- @tparam Vec3 vec1 first vector + -- @tparam Vec3 vec2 second vector + -- @treturn Vec3 new vector, cross product of vec1 and vec2. + function mist.vec.cp(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 + + --- Vector magnitude + -- @tparam Vec3 vec vector + -- @treturn number magnitude of vector vec + function mist.vec.mag(vec) + return (vec.x^2 + vec.y^2 + vec.z^2)^0.5 + end + + --- Unit vector + -- @tparam Vec3 vec + -- @treturn Vec3 unit vector of vec + function mist.vec.getUnitVec(vec) + local mag = mist.vec.mag(vec) + return { x = vec.x/mag, y = vec.y/mag, z = vec.z/mag } + end + + --- Rotate vector. + -- @tparam Vec2 vec2 to rotoate + -- @tparam number theta + -- @return Vec2 rotated vector. + function mist.vec.rotateVec2(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 +end + +--- Flag functions. +-- The mist "Flag functions" are functions that are similar to Slmod functions +-- that detect a game condition and set a flag when that game condition is met. +-- +-- They are intended to be used by persons with little or no experience in Lua +-- programming, but with a good knowledge of the DCS mission editor. +-- @section mist.flagFunc +do -- mist.flagFunc scope + mist.flagFunc = {} + + --- Sets a flag if map objects are destroyed inside a zone. + -- Once this function is run, it will start a continuously evaluated process + -- that will set a flag true if map objects (such as bridges, buildings in + -- town, etc.) die (or have died) in a mission editor zone (or set of zones). + -- This will only happen once; once the flag is set true, the process ends. + -- @usage + -- -- Example vars table + -- vars = { + -- zones = { "zone1", "zone2" }, -- can also be a single string + -- flag = 3, -- number of the flag + -- stopflag = 4, -- optional number of the stop flag + -- req_num = 10, -- optional minimum amount of map objects needed to die + -- } + -- mist.flagFuncs.mapobjs_dead_zones(vars) + -- @tparam table vars table containing parameters. + function mist.flagFunc.mapobjs_dead_zones(vars) + --[[vars needs to be: +zones = table or string, +flag = number, +stopflag = number or nil, +req_num = number or nil + +AND used by function, +initial_number + +]] + -- type_tbl + local type_tbl = { + [{'zones', 'zone'}] = {'table', 'string'}, + flag = {'number', 'string'}, + [{'stopflag', 'stopFlag'}] = {'number', 'string', 'nil'}, + [{'req_num', 'reqnum'}] = {'number', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.mapobjs_dead_zones', type_tbl, vars) + assert(err, errmsg) + local zones = vars.zones or vars.zone + local flag = vars.flag + local stopflag = vars.stopflag or vars.stopFlag or -1 + local req_num = vars.req_num or vars.reqnum or 1 + local initial_number = vars.initial_number + + if type(zones) == 'string' then + zones = {zones} + end + + if not initial_number then + initial_number = #mist.getDeadMapObjsInZones(zones) + end + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + if (#mist.getDeadMapObjsInZones(zones) - initial_number) >= req_num and trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + return + else + mist.scheduleFunction(mist.flagFunc.mapobjs_dead_zones, {{zones = zones, flag = flag, stopflag = stopflag, req_num = req_num, initial_number = initial_number}}, timer.getTime() + 1) + end + end + end + + --- Sets a flag if map objects are destroyed inside a polygon. + -- Once this function is run, it will start a continuously evaluated process + -- that will set a flag true if map objects (such as bridges, buildings in + -- town, etc.) die (or have died) in a polygon. + -- This will only happen once; once the flag is set true, the process ends. + -- @usage + -- -- Example vars table + -- vars = { + -- zone = { + -- [1] = mist.DBs.unitsByName['NE corner'].point, + -- [2] = mist.DBs.unitsByName['SE corner'].point, + -- [3] = mist.DBs.unitsByName['SW corner'].point, + -- [4] = mist.DBs.unitsByName['NW corner'].point + -- } + -- flag = 3, -- number of the flag + -- stopflag = 4, -- optional number of the stop flag + -- req_num = 10, -- optional minimum amount of map objects needed to die + -- } + -- mist.flagFuncs.mapobjs_dead_zones(vars) + -- @tparam table vars table containing parameters. + function mist.flagFunc.mapobjs_dead_polygon(vars) + --[[vars needs to be: +zone = table, +flag = number, +stopflag = number or nil, +req_num = number or nil + +AND used by function, +initial_number + +]] + -- type_tbl + local type_tbl = { + [{'zone', 'polyzone'}] = 'table', + flag = {'number', 'string'}, + [{'stopflag', 'stopFlag'}] = {'number', 'string', 'nil'}, + [{'req_num', 'reqnum'}] = {'number', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.mapobjs_dead_polygon', type_tbl, vars) + assert(err, errmsg) + local zone = vars.zone or vars.polyzone + local flag = vars.flag + local stopflag = vars.stopflag or vars.stopFlag or -1 + local req_num = vars.req_num or vars.reqnum or 1 + local initial_number = vars.initial_number + + if not initial_number then + initial_number = #mist.getDeadMapObjsInPolygonZone(zone) + end + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + if (#mist.getDeadMapObjsInPolygonZone(zone) - initial_number) >= req_num and trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + return + else + mist.scheduleFunction(mist.flagFunc.mapobjs_dead_polygon, {{zone = zone, flag = flag, stopflag = stopflag, req_num = req_num, initial_number = initial_number}}, timer.getTime() + 1) + end + end + end + + --- Sets a flag if unit(s) is/are inside a polygon. + -- @tparam table vars @{unitsInPolygonVars} + -- @usage -- set flag 11 to true as soon as any blue vehicles + -- -- are inside the polygon shape created off of the waypoints + -- -- of the group forest1 + -- mist.flagFunc.units_in_polygon { + -- units = {'[blue][vehicle]'}, + -- zone = mist.getGroupPoints('forest1'), + -- flag = 11 + -- } + function mist.flagFunc.units_in_polygon(vars) + --[[vars needs to be: +units = table, +zone = table, +flag = number, +stopflag = number or nil, +maxalt = number or nil, +interval = number or nil, +req_num = number or nil +toggle = boolean or nil +unitTableDef = table or nil +]] + -- type_tbl + local type_tbl = { + [{'units', 'unit'}] = 'table', + [{'zone', 'polyzone'}] = 'table', + flag = {'number', 'string'}, + [{'stopflag', 'stopFlag'}] = {'number', 'string', 'nil'}, + [{'maxalt', 'alt'}] = {'number', 'nil'}, + interval = {'number', 'nil'}, + [{'req_num', 'reqnum'}] = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + unitTableDef = {'table', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_in_polygon', type_tbl, vars) + assert(err, errmsg) + local units = vars.units or vars.unit + local zone = vars.zone or vars.polyzone + local flag = vars.flag + local stopflag = vars.stopflag or vars.stopFlag or -1 + local interval = vars.interval or 1 + local maxalt = vars.maxalt or vars.alt + local req_num = vars.req_num or vars.reqnum or 1 + local toggle = vars.toggle or nil + local unitTableDef = vars.unitTableDef + + if not units.processed then + unitTableDef = mist.utils.deepCopy(units) + end + + if (units.processed and units.processed < mist.getLastDBUpdateTime()) or not units.processed then -- run unit table short cuts + units = mist.makeUnitTable(unitTableDef) + end + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == 0) then + local num_in_zone = 0 + for i = 1, #units do + local unit = Unit.getByName(units[i]) + if unit then + local pos = unit:getPosition().p + if mist.pointInPolygon(pos, zone, maxalt) then + num_in_zone = num_in_zone + 1 + if num_in_zone >= req_num and trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + break + end + end + end + end + if toggle and (num_in_zone < req_num) and trigger.misc.getUserFlag(flag) > 0 then + trigger.action.setUserFlag(flag, false) + end + -- do another check in case stopflag was set true by this function + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == 0) then + mist.scheduleFunction(mist.flagFunc.units_in_polygon, {{units = units, zone = zone, flag = flag, stopflag = stopflag, interval = interval, req_num = req_num, maxalt = maxalt, toggle = toggle, unitTableDef = unitTableDef}}, timer.getTime() + interval) + end + end + + end + + --- Sets a flag if unit(s) is/are inside a trigger zone. + -- @todo document + function mist.flagFunc.units_in_zones(vars) + --[[vars needs to be: + units = table, + zones = table, + flag = number, + stopflag = number or nil, + zone_type = string or nil, + req_num = number or nil, + interval = number or nil + toggle = boolean or nil + ]] + -- type_tbl + local type_tbl = { + units = 'table', + zones = 'table', + flag = {'number', 'string'}, + [{'stopflag', 'stopFlag'}] = {'number', 'string', 'nil'}, + [{'zone_type', 'zonetype'}] = {'string', 'nil'}, + [{'req_num', 'reqnum'}] = {'number', 'nil'}, + interval = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + unitTableDef = {'table', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_in_zones', type_tbl, vars) + assert(err, errmsg) + local units = vars.units + local zones = vars.zones + local flag = vars.flag + local stopflag = vars.stopflag or vars.stopFlag or -1 + local zone_type = vars.zone_type or vars.zonetype or 'cylinder' + local req_num = vars.req_num or vars.reqnum or 1 + local interval = vars.interval or 1 + local toggle = vars.toggle or nil + local unitTableDef = vars.unitTableDef + + if not units.processed then + unitTableDef = mist.utils.deepCopy(units) + end + + if (units.processed and units.processed < mist.getLastDBUpdateTime()) or not units.processed then -- run unit table short cuts + units = mist.makeUnitTable(unitTableDef) + end + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + + local in_zone_units = mist.getUnitsInZones(units, zones, zone_type) + + if #in_zone_units >= req_num and trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + elseif #in_zone_units < req_num and toggle then + trigger.action.setUserFlag(flag, false) + end + -- do another check in case stopflag was set true by this function + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + mist.scheduleFunction(mist.flagFunc.units_in_zones, {{units = units, zones = zones, flag = flag, stopflag = stopflag, zone_type = zone_type, req_num = req_num, interval = interval, toggle = toggle, unitTableDef = unitTableDef}}, timer.getTime() + interval) + end + end + + end + + --- Sets a flag if unit(s) is/are inside a moving zone. + -- @todo document + function mist.flagFunc.units_in_moving_zones(vars) + --[[vars needs to be: + units = table, + zone_units = table, + radius = number, + flag = number, + stopflag = number or nil, + zone_type = string or nil, + req_num = number or nil, + interval = number or nil + toggle = boolean or nil + ]] + -- type_tbl + local type_tbl = { + units = 'table', + [{'zone_units', 'zoneunits'}] = 'table', + radius = 'number', + flag = {'number', 'string'}, + [{'stopflag', 'stopFlag'}] = {'number', 'string', 'nil'}, + [{'zone_type', 'zonetype'}] = {'string', 'nil'}, + [{'req_num', 'reqnum'}] = {'number', 'nil'}, + interval = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + unitTableDef = {'table', 'nil'}, + zUnitTableDef = {'table', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_in_moving_zones', type_tbl, vars) + assert(err, errmsg) + local units = vars.units + local zone_units = vars.zone_units or vars.zoneunits + local radius = vars.radius + local flag = vars.flag + local stopflag = vars.stopflag or vars.stopFlag or -1 + local zone_type = vars.zone_type or vars.zonetype or 'cylinder' + local req_num = vars.req_num or vars.reqnum or 1 + local interval = vars.interval or 1 + local toggle = vars.toggle or nil + local unitTableDef = vars.unitTableDef + local zUnitTableDef = vars.zUnitTableDef + + if not units.processed then + unitTableDef = mist.utils.deepCopy(units) + end + + if not zone_units.processed then + zUnitTableDef = mist.utils.deepCopy(zone_units) + end + + if (units.processed and units.processed < mist.getLastDBUpdateTime()) or not units.processed then -- run unit table short cuts + units = mist.makeUnitTable(unitTableDef) + end + + if (zone_units.processed and zone_units.processed < mist.getLastDBUpdateTime()) or not zone_units.processed then -- run unit table short cuts + zone_units = mist.makeUnitTable(zUnitTableDef) + end + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + + local in_zone_units = mist.getUnitsInMovingZones(units, zone_units, radius, zone_type) + + if #in_zone_units >= req_num and trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + elseif #in_zone_units < req_num and toggle then + trigger.action.setUserFlag(flag, false) + end + -- do another check in case stopflag was set true by this function + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + mist.scheduleFunction(mist.flagFunc.units_in_moving_zones, {{units = units, zone_units = zone_units, radius = radius, flag = flag, stopflag = stopflag, zone_type = zone_type, req_num = req_num, interval = interval, toggle = toggle, unitTableDef = unitTableDef, zUnitTableDef = zUnitTableDef}}, timer.getTime() + interval) + end + end + + end + + --- Sets a flag if units have line of sight to each other. + -- @todo document + function mist.flagFunc.units_LOS(vars) + --[[vars needs to be: +unitset1 = table, +altoffset1 = number, +unitset2 = table, +altoffset2 = number, +flag = number, +stopflag = number or nil, +radius = number or nil, +interval = number or nil, +req_num = number or nil +toggle = boolean or nil +]] + -- type_tbl + local type_tbl = { + [{'unitset1', 'units1'}] = 'table', + [{'altoffset1', 'alt1'}] = 'number', + [{'unitset2', 'units2'}] = 'table', + [{'altoffset2', 'alt2'}] = 'number', + flag = {'number', 'string'}, + [{'stopflag', 'stopFlag'}] = {'number', 'string', 'nil'}, + [{'req_num', 'reqnum'}] = {'number', 'nil'}, + interval = {'number', 'nil'}, + radius = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + unitTableDef1 = {'table', 'nil'}, + unitTableDef2 = {'table', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.units_LOS', type_tbl, vars) + assert(err, errmsg) + local unitset1 = vars.unitset1 or vars.units1 + local altoffset1 = vars.altoffset1 or vars.alt1 + local unitset2 = vars.unitset2 or vars.units2 + local altoffset2 = vars.altoffset2 or vars.alt2 + local flag = vars.flag + local stopflag = vars.stopflag or vars.stopFlag or -1 + local interval = vars.interval or 1 + local radius = vars.radius or math.huge + local req_num = vars.req_num or vars.reqnum or 1 + local toggle = vars.toggle or nil + local unitTableDef1 = vars.unitTableDef1 + local unitTableDef2 = vars.unitTableDef2 + + if not unitset1.processed then + unitTableDef1 = mist.utils.deepCopy(unitset1) + end + + if not unitset2.processed then + unitTableDef2 = mist.utils.deepCopy(unitset2) + end + + if (unitset1.processed and unitset1.processed < mist.getLastDBUpdateTime()) or not unitset1.processed then -- run unit table short cuts + unitset1 = mist.makeUnitTable(unitTableDef1) + end + + if (unitset2.processed and unitset2.processed < mist.getLastDBUpdateTime()) or not unitset2.processed then -- run unit table short cuts + unitset2 = mist.makeUnitTable(unitTableDef2) + end + + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + + local unitLOSdata = mist.getUnitsLOS(unitset1, altoffset1, unitset2, altoffset2, radius) + + if #unitLOSdata >= req_num and trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + elseif #unitLOSdata < req_num and toggle then + trigger.action.setUserFlag(flag, false) + end + -- do another check in case stopflag was set true by this function + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + mist.scheduleFunction(mist.flagFunc.units_LOS, {{unitset1 = unitset1, altoffset1 = altoffset1, unitset2 = unitset2, altoffset2 = altoffset2, flag = flag, stopflag = stopflag, radius = radius, req_num = req_num, interval = interval, toggle = toggle, unitTableDef1 = unitTableDef1, unitTableDef2 = unitTableDef2}}, timer.getTime() + interval) + end + end + end + + --- Sets a flag if group is alive. + -- @todo document + function mist.flagFunc.group_alive(vars) + --[[vars +groupName +flag +toggle +interval +stopFlag + +]] + local type_tbl = { + [{'group', 'groupname', 'gp', 'groupName'}] = 'string', + flag = {'number', 'string'}, + [{'stopflag', 'stopFlag'}] = {'number', 'string', 'nil'}, + interval = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_alive', type_tbl, vars) + assert(err, errmsg) + + local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname + local flag = vars.flag + local stopflag = vars.stopflag or vars.stopFlag or -1 + local interval = vars.interval or 1 + local toggle = vars.toggle or nil + + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true and #Group.getByName(groupName):getUnits() > 0 then + if trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + end + else + if toggle then + trigger.action.setUserFlag(flag, false) + end + end + end + + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + mist.scheduleFunction(mist.flagFunc.group_alive, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle}}, timer.getTime() + interval) + end + + end + + --- Sets a flag if group is dead. + -- @todo document + function mist.flagFunc.group_dead(vars) + local type_tbl = { + [{'group', 'groupname', 'gp', 'groupName'}] = 'string', + flag = {'number', 'string'}, + [{'stopflag', 'stopFlag'}] = {'number', 'string', 'nil'}, + interval = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_dead', type_tbl, vars) + assert(err, errmsg) + + local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname + local flag = vars.flag + local stopflag = vars.stopflag or vars.stopFlag or -1 + local interval = vars.interval or 1 + local toggle = vars.toggle or nil + + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + if (Group.getByName(groupName) and Group.getByName(groupName):isExist() == false) or (Group.getByName(groupName) and #Group.getByName(groupName):getUnits() < 1) or not Group.getByName(groupName) then + if trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + end + else + if toggle then + trigger.action.setUserFlag(flag, false) + end + end + end + + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + mist.scheduleFunction(mist.flagFunc.group_dead, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle}}, timer.getTime() + interval) + end + end + + --- Sets a flag if less than given percent of group is alive. + -- @todo document + function mist.flagFunc.group_alive_less_than(vars) + local type_tbl = { + [{'group', 'groupname', 'gp', 'groupName'}] = 'string', + percent = 'number', + flag = {'number', 'string'}, + [{'stopflag', 'stopFlag'}] = {'number', 'string', 'nil'}, + interval = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_alive_less_than', type_tbl, vars) + assert(err, errmsg) + + local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname + local flag = vars.flag + local percent = vars.percent + local stopflag = vars.stopflag or vars.stopFlag or -1 + local interval = vars.interval or 1 + local toggle = vars.toggle or nil + + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then + if Group.getByName(groupName):getSize()/Group.getByName(groupName):getInitialSize() < percent/100 then + if trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + end + else + if toggle then + trigger.action.setUserFlag(flag, false) + end + end + else + if trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + end + end + end + + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + mist.scheduleFunction(mist.flagFunc.group_alive_less_than, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle, percent = percent}}, timer.getTime() + interval) + end + end + + --- Sets a flag if more than given percent of group is alive. + -- @todo document + function mist.flagFunc.group_alive_more_than(vars) + local type_tbl = { + [{'group', 'groupname', 'gp', 'groupName'}] = 'string', + percent = 'number', + flag = {'number', 'string'}, + [{'stopflag', 'stopFlag'}] = {'number', 'string', 'nil'}, + interval = {'number', 'nil'}, + toggle = {'boolean', 'nil'}, + } + + local err, errmsg = mist.utils.typeCheck('mist.flagFunc.group_alive_more_than', type_tbl, vars) + assert(err, errmsg) + + local groupName = vars.groupName or vars.group or vars.gp or vars.Groupname + local flag = vars.flag + local percent = vars.percent + local stopflag = vars.stopflag or vars.stopFlag or -1 + local interval = vars.interval or 1 + local toggle = vars.toggle or nil + + + if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + if Group.getByName(groupName) and Group.getByName(groupName):isExist() == true then + if Group.getByName(groupName):getSize()/Group.getByName(groupName):getInitialSize() > percent/100 then + if trigger.misc.getUserFlag(flag) == 0 then + trigger.action.setUserFlag(flag, true) + end + else + if toggle and trigger.misc.getUserFlag(flag) == 1 then + trigger.action.setUserFlag(flag, false) + end + end + else --- just in case + if toggle and trigger.misc.getUserFlag(flag) == 1 then + trigger.action.setUserFlag(flag, false) + end + end + end + + if (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == false) then + mist.scheduleFunction(mist.flagFunc.group_alive_more_than, {{groupName = groupName, flag = flag, stopflag = stopflag, interval = interval, toggle = toggle, percent = percent}}, timer.getTime() + interval) + end + end + + mist.flagFunc.mapobjsDeadPolygon = mist.flagFunc.mapobjs_dead_polygon + mist.flagFunc.mapobjsDeadZones = mist.flagFunc.Mapobjs_dead_zones + mist.flagFunc.unitsInZones = mist.flagFunc.units_in_zones + mist.flagFunc.unitsInMovingZones = mist.flagFunc.units_in_moving_zones + mist.flagFunc.unitsInPolygon = mist.flagFunc.units_in_polygon + mist.flagFunc.unitsLOS = mist.flagFunc.units_LOS + mist.flagFunc.groupAlive = mist.flagFunc.group_alive + mist.flagFunc.groupDead = mist.flagFunc.group_dead + mist.flagFunc.groupAliveMoreThan = mist.flagFunc.group_alive_more_than + mist.flagFunc.groupAliveLessThan = mist.flagFunc.group_alive_less_than + +end + +--- Message functions. +-- Messaging system +-- @section mist.msg +do -- mist.msg scope + local messageList = {} + -- this defines the max refresh rate of the message box it honestly only needs to + -- go faster than this for precision timing stuff (which could be its own function) + local messageDisplayRate = 0.1 + local messageID = 0 + local displayActive = false + local displayFuncId = 0 + + local caSlots = false + local caMSGtoGroup = false + + if env.mission.groundControl then -- just to be sure? + for index, value in pairs(env.mission.groundControl) do + if type(value) == 'table' then + for roleName, roleVal in pairs(value) do + for rIndex, rVal in pairs(roleVal) do + if rIndex == 'red' or rIndex == 'blue' then + if env.mission.groundControl[index][roleName][rIndex] > 0 then + caSlots = true + break + end + end + end + end + elseif type(value) == 'boolean' and value == true then + caSlots = true + break + end + end + end + + local function mistdisplayV5() + --[[thoughts to improve upon + event handler based activeClients table. + display messages only when there is an update + possibly co-routine it. + ]] + end + + local function mistdisplayV4() + local activeClients = {} + + for clientId, clientData in pairs(mist.DBs.humansById) do + if Unit.getByName(clientData.unitName) and Unit.getByName(clientData.unitName):isExist() == true then + activeClients[clientData.groupId] = clientData.groupName + end + end + + --[[if caSlots == true and caMSGtoGroup == true then + + end]] + + + if #messageList > 0 then + if displayActive == false then + displayActive = true + end + --mist.debug.writeData(mist.utils.serialize,{'msg', messageList}, 'messageList.lua') + local msgTableText = {} + local msgTableSound = {} + + for messageId, messageData in pairs(messageList) do + if messageData.displayedFor > messageData.displayTime then + messageData:remove() -- now using the remove/destroy function. + else + if messageData.displayedFor then + messageData.displayedFor = messageData.displayedFor + messageDisplayRate + end + local nextSound = 1000 + local soundIndex = 0 + + if messageData.multSound and #messageData.multSound > 0 then + for index, sData in pairs(messageData.multSound) do + if sData.time <= messageData.displayedFor and sData.played == false and sData.time < nextSound then -- find index of the next sound to be played + nextSound = sData.time + soundIndex = index + end + end + if soundIndex ~= 0 then + messageData.multSound[soundIndex].played = true + end + end + + for recIndex, recData in pairs(messageData.msgFor) do -- iterate recipiants + if recData == 'RED' or recData == 'BLUE' or activeClients[recData] then -- rec exists + if messageData.text then -- text + if not msgTableText[recData] then -- create table entry for text + msgTableText[recData] = {} + msgTableText[recData].text = {} + if recData == 'RED' or recData == 'BLUE' then + msgTableText[recData].text[1] = '-------Combined Arms Message-------- \n' + end + msgTableText[recData].text[#msgTableText[recData].text + 1] = messageData.text + msgTableText[recData].displayTime = messageData.displayTime - messageData.displayedFor + else -- add to table entry and adjust display time if needed + if recData == 'RED' or recData == 'BLUE' then + msgTableText[recData].text[#msgTableText[recData].text + 1] = '\n ---------------- Combined Arms Message: \n' + else + msgTableText[recData].text[#msgTableText[recData].text + 1] = '\n ---------------- \n' + end + msgTableText[recData].text[#msgTableText[recData].text + 1] = messageData.text + if msgTableText[recData].displayTime < messageData.displayTime - messageData.displayedFor then + msgTableText[recData].displayTime = messageData.displayTime - messageData.displayedFor + else + msgTableText[recData].displayTime = 1 + end + end + end + if soundIndex ~= 0 then + msgTableSound[recData] = messageData.multSound[soundIndex].file + end + end + end + + + end + end + ------- new display + + if caSlots == true and caMSGtoGroup == false then + if msgTableText.RED then + trigger.action.outTextForCoalition(coalition.side.RED, table.concat(msgTableText.RED.text), msgTableText.RED.displayTime, true) + + end + if msgTableText.BLUE then + trigger.action.outTextForCoalition(coalition.side.BLUE, table.concat(msgTableText.BLUE.text), msgTableText.BLUE.displayTime, true) + end + end + + for index, msgData in pairs(msgTableText) do + if type(index) == 'number' then -- its a groupNumber + trigger.action.outTextForGroup(index, table.concat(msgData.text), msgData.displayTime, true) + end + end + --- new audio + if msgTableSound.RED then + trigger.action.outSoundForCoalition(coalition.side.RED, msgTableSound.RED) + end + if msgTableSound.BLUE then + trigger.action.outSoundForCoalition(coalition.side.BLUE, msgTableSound.BLUE) + end + + + for index, file in pairs(msgTableSound) do + if type(index) == 'number' then -- its a groupNumber + trigger.action.outSoundForGroup(index, file) + end + end + else + mist.removeFunction(displayFuncId) + displayActive = false + end + + end + + local typeBase = { + ['Mi-8MT'] = {'Mi-8MTV2', 'Mi-8MTV', 'Mi-8'}, + ['MiG-21Bis'] = {'Mig-21'}, + ['MiG-15bis'] = {'Mig-15'}, + ['FW-190D9'] = {'FW-190'}, + ['Bf-109K-4'] = {'Bf-109'}, + } + + --[[function mist.setCAGroupMSG(val) + if type(val) == 'boolean' then + caMSGtoGroup = val + return true + end + return false +end]] + + mist.message = { + + add = function(vars) + local function msgSpamFilter(recList, spamBlockOn) + for id, name in pairs(recList) do + if name == spamBlockOn then + -- log:info('already on recList') + return recList + end + end + --log:info('add to recList') + table.insert(recList, spamBlockOn) + return recList + end + + --[[ + local vars = {} + vars.text = 'Hello World' + vars.displayTime = 20 + vars.msgFor = {coa = {'red'}, countries = {'Ukraine', 'Georgia'}, unitTypes = {'A-10C'}} + mist.message.add(vars) + + Displays the message for all red coalition players. Players belonging to Ukraine and Georgia, and all A-10Cs on the map + + ]] + + + local new = {} + new.text = vars.text -- The actual message + new.displayTime = vars.displayTime -- How long will the message appear for + new.displayedFor = 0 -- how long the message has been displayed so far + new.name = vars.name -- ID to overwrite the older message (if it exists) Basically it replaces a message that is displayed with new text. + new.addedAt = timer.getTime() + new.update = true + + if vars.multSound and vars.multSound[1] then + new.multSound = vars.multSound + else + new.multSound = {} + end + + if vars.sound or vars.fileName then -- converts old sound file system into new multSound format + local sound = vars.sound + if vars.fileName then + sound = vars.fileName + end + new.multSound[#new.multSound+1] = {time = 0.1, file = sound} + end + + if #new.multSound > 0 then + for i, data in pairs(new.multSound) do + data.played = false + end + end + + local newMsgFor = {} -- list of all groups message displays for + for forIndex, forData in pairs(vars.msgFor) do + for list, listData in pairs(forData) do + for clientId, clientData in pairs(mist.DBs.humansById) do + forIndex = string.lower(forIndex) + if type(listData) == 'string' then + listData = string.lower(listData) + end + if (forIndex == 'coa' and (listData == string.lower(clientData.coalition) or listData == 'all')) or (forIndex == 'countries' and string.lower(clientData.country) == listData) or (forIndex == 'units' and string.lower(clientData.unitName) == listData) then -- + newMsgFor = msgSpamFilter(newMsgFor, clientData.groupId) -- so units dont get the same message twice if complex rules are given + --table.insert(newMsgFor, clientId) + elseif forIndex == 'unittypes' then + for typeId, typeData in pairs(listData) do + local found = false + for clientDataEntry, clientDataVal in pairs(clientData) do + if type(clientDataVal) == 'string' then + if mist.matchString(list, clientDataVal) == true or list == 'all' then + local sString = typeData + for rName, pTbl in pairs(typeBase) do -- just a quick check to see if the user may have meant something and got the specific type of the unit wrong + for pIndex, pName in pairs(pTbl) do + if mist.stringMatch(sString, pName) then + sString = rName + end + end + end + if sString == clientData.type then + found = true + newMsgFor = msgSpamFilter(newMsgFor, clientData.groupId) -- sends info oto other function to see if client is already recieving the current message. + --table.insert(newMsgFor, clientId) + end + end + end + if found == true then -- shouldn't this be elsewhere too? + break + end + end + end + + end + end + for coaData, coaId in pairs(coalition.side) do + if string.lower(forIndex) == 'coa' or string.lower(forIndex) == 'ca' then + if listData == string.lower(coaData) or listData == 'all' then + newMsgFor = msgSpamFilter(newMsgFor, coaData) + end + end + end + end + end + + if #newMsgFor > 0 then + new.msgFor = newMsgFor -- I swear its not confusing + + else + return false + end + + + if vars.name and type(vars.name) == 'string' then + for i = 1, #messageList do + if messageList[i].name then + if messageList[i].name == vars.name then + --log:info('updateMessage') + messageList[i].displayedFor = 0 + messageList[i].addedAt = timer.getTime() + messageList[i].sound = new.sound + messageList[i].text = new.text + messageList[i].msgFor = new.msgFor + messageList[i].multSound = new.multSound + messageList[i].update = true + return messageList[i].messageID + end + end + end + end + + messageID = messageID + 1 + new.messageID = messageID + + --mist.debug.writeData(mist.utils.serialize,{'msg', new}, 'newMsg.lua') + + + messageList[#messageList + 1] = new + + local mt = { __index = mist.message} + setmetatable(new, mt) + + if displayActive == false then + displayActive = true + displayFuncId = mist.scheduleFunction(mistdisplayV4, {}, timer.getTime() + messageDisplayRate, messageDisplayRate) + end + + return messageID + + end, + + remove = function(self) -- Now a self variable; the former functionality taken up by mist.message.removeById. + for i, msgData in pairs(messageList) do + if messageList[i] == self then + table.remove(messageList, i) + return true --removal successful + end + end + return false -- removal not successful this script fails at life! + end, + + removeById = function(id) -- This function is NOT passed a self variable; it is the remove by id function. + for i, msgData in pairs(messageList) do + if messageList[i].messageID == id then + table.remove(messageList, i) + return true --removal successful + end + end + return false -- removal not successful this script fails at life! + end, + } + + --[[ vars for mist.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 +]] + function mist.msgMGRS(vars) + local units = vars.units + local acc = vars.acc + local text = vars.text + local displayTime = vars.displayTime + local msgFor = vars.msgFor + + local s = mist.getMGRSString{units = units, acc = acc} + local newText + if text then + if string.find(text, '%%s') then -- look for %s + newText = string.format(text, s) -- insert the coordinates into the message + else + -- just append to the end. + newText = text .. s + end + else + newText = s + end + mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor + } + end + + --[[ vars for mist.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 +]] + function mist.msgLL(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 = mist.getLLString{units = units, acc = acc, DMS = DMS} + local newText + if text then + if string.find(text, '%%s') then -- look for %s + newText = string.format(text, s) -- insert the coordinates into the message + else + -- just append to the end. + newText = text .. s + end + else + newText = s + end + + mist.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 +]] + function mist.msgBR(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 mist.getBRString + local alt = vars.alt + local metric = vars.metric + local text = vars.text + local displayTime = vars.displayTime + local msgFor = vars.msgFor + + local s = mist.getBRString{units = units, ref = ref, alt = alt, metric = metric} + local newText + if text then + if string.find(text, '%%s') then -- look for %s + newText = string.format(text, s) -- insert the coordinates into the message + else + -- just append to the end. + newText = text .. s + end + else + newText = s + end + + mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor + } + + end + + -- basically, just sub-types of mist.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 +]] + function mist.msgBullseye(vars) + if string.lower(vars.ref) == 'red' then + vars.ref = mist.DBs.missionData.bullseye.red + mist.msgBR(vars) + elseif string.lower(vars.ref) == 'blue' then + vars.ref = mist.DBs.missionData.bullseye.blue + mist.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 +]] + function mist.msgBRA(vars) + if Unit.getByName(vars.ref) and Unit.getByName(vars.ref):isExist() == true then + vars.ref = Unit.getByName(vars.ref):getPosition().p + if not vars.alt then + vars.alt = true + end + mist.msgBR(vars) + end + end + + --[[ vars for mist.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 +]] + function mist.msgLeadingMGRS(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 = mist.getLeadingMGRSString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc} + local newText + if text then + if string.find(text, '%%s') then -- look for %s + newText = string.format(text, s) -- insert the coordinates into the message + else + -- just append to the end. + newText = text .. s + end + else + newText = s + end + + mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor + } + + + end + + --[[ vars for mist.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 +]] + function mist.msgLeadingLL(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 = mist.getLeadingLLString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc, DMS = DMS} + local newText + + if text then + if string.find(text, '%%s') then -- look for %s + newText = string.format(text, s) -- insert the coordinates into the message + else + -- just append to the end. + newText = text .. s + end + else + newText = s + end + + mist.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 +]] + function mist.msgLeadingBR(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 mist.getBRString + local text = vars.text + local displayTime = vars.displayTime + local msgFor = vars.msgFor + + local s = mist.getLeadingBRString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, metric = metric, alt = alt, ref = ref} + local newText + + if text then + if string.find(text, '%%s') then -- look for %s + newText = string.format(text, s) -- insert the coordinates into the message + else + -- just append to the end. + newText = text .. s + end + else + newText = s + end + + mist.message.add{ + text = newText, + displayTime = displayTime, + msgFor = msgFor + } + end +end + +--- Demo functions. +-- @section mist.demos +do -- mist.demos scope + mist.demos = {} + + function mist.demos.printFlightData(unit) + if unit:isExist() then + local function printData(unit, prevVel, prevE, prevTime) + local angles = mist.getAttitude(unit) + if angles then + local Heading = angles.Heading + local Pitch = angles.Pitch + local Roll = angles.Roll + local Yaw = angles.Yaw + local AoA = angles.AoA + local ClimbAngle = angles.ClimbAngle + + if not Heading then + Heading = 'NA' + else + Heading = string.format('%12.2f', mist.utils.toDegree(Heading)) + end + + if not Pitch then + Pitch = 'NA' + else + Pitch = string.format('%12.2f', mist.utils.toDegree(Pitch)) + end + + if not Roll then + Roll = 'NA' + else + Roll = string.format('%12.2f', mist.utils.toDegree(Roll)) + end + + local AoAplusYaw = 'NA' + if AoA and Yaw then + AoAplusYaw = string.format('%12.2f', mist.utils.toDegree((AoA^2 + Yaw^2)^0.5)) + end + + if not Yaw then + Yaw = 'NA' + else + Yaw = string.format('%12.2f', mist.utils.toDegree(Yaw)) + end + + if not AoA then + AoA = 'NA' + else + AoA = string.format('%12.2f', mist.utils.toDegree(AoA)) + end + + if not ClimbAngle then + ClimbAngle = 'NA' + else + ClimbAngle = string.format('%12.2f', mist.utils.toDegree(ClimbAngle)) + end + local unitPos = unit:getPosition() + local unitVel = unit:getVelocity() + local curTime = timer.getTime() + local absVel = string.format('%12.2f', mist.vec.mag(unitVel)) + + + local unitAcc = 'NA' + local Gs = 'NA' + local axialGs = 'NA' + local transGs = 'NA' + if prevVel and prevTime then + local xAcc = (unitVel.x - prevVel.x)/(curTime - prevTime) + local yAcc = (unitVel.y - prevVel.y)/(curTime - prevTime) + local zAcc = (unitVel.z - prevVel.z)/(curTime - prevTime) + + unitAcc = string.format('%12.2f', mist.vec.mag({x = xAcc, y = yAcc, z = zAcc})) + Gs = string.format('%12.2f', mist.vec.mag({x = xAcc, y = yAcc + 9.81, z = zAcc})/9.81) + axialGs = string.format('%12.2f', mist.vec.dp({x = xAcc, y = yAcc + 9.81, z = zAcc}, unitPos.x)/9.81) + transGs = string.format('%12.2f', mist.vec.mag(mist.vec.cp({x = xAcc, y = yAcc + 9.81, z = zAcc}, unitPos.x))/9.81) + end + + local E = 0.5*mist.vec.mag(unitVel)^2 + 9.81*unitPos.p.y + + local energy = string.format('%12.2e', E) + + local dEdt = 'NA' + if prevE and prevTime then + dEdt = string.format('%12.2e', (E - prevE)/(curTime - prevTime)) + end + + trigger.action.outText(string.format('%-25s', 'Heading: ') .. Heading .. ' degrees\n' .. string.format('%-25s', 'Roll: ') .. Roll .. ' degrees\n' .. string.format('%-25s', 'Pitch: ') .. Pitch + .. ' degrees\n' .. string.format('%-25s', 'Yaw: ') .. Yaw .. ' degrees\n' .. string.format('%-25s', 'AoA: ') .. AoA .. ' degrees\n' .. string.format('%-25s', 'AoA plus Yaw: ') .. AoAplusYaw .. ' degrees\n' .. string.format('%-25s', 'Climb Angle: ') .. + ClimbAngle .. ' degrees\n' .. string.format('%-25s', 'Absolute Velocity: ') .. absVel .. ' m/s\n' .. string.format('%-25s', 'Absolute Acceleration: ') .. unitAcc ..' m/s^2\n' + .. string.format('%-25s', 'Axial G loading: ') .. axialGs .. ' g\n' .. string.format('%-25s', 'Transverse G loading: ') .. transGs .. ' g\n' .. string.format('%-25s', 'Absolute G loading: ') .. Gs .. ' g\n' .. string.format('%-25s', 'Energy: ') .. energy .. ' J/kg\n' .. string.format('%-25s', 'dE/dt: ') .. dEdt ..' J/(kg*s)', 1) + return unitVel, E, curTime + end + end + + local function frameFinder(unit, prevVel, prevE, prevTime) + if unit:isExist() then + local currVel = unit:getVelocity() + if prevVel and (prevVel.x ~= currVel.x or prevVel.y ~= currVel.y or prevVel.z ~= currVel.z) or (prevTime and (timer.getTime() - prevTime) > 0.25) then + prevVel, prevE, prevTime = printData(unit, prevVel, prevE, prevTime) + end + mist.scheduleFunction(frameFinder, {unit, prevVel, prevE, prevTime}, timer.getTime() + 0.005) -- it can't go this fast, limited to the 100 times a sec check right now. + end + end + + + local curVel = unit:getVelocity() + local curTime = timer.getTime() + local curE = 0.5*mist.vec.mag(curVel)^2 + 9.81*unit:getPosition().p.y + frameFinder(unit, curVel, curE, curTime) + + end + + end + +end + +--- Time conversion functions. +-- @section mist.time +do -- mist.time scope + mist.time = {} + -- returns a string for specified military time + -- theTime is optional + -- if present current time in mil time is returned + -- if number or table the time is converted into mil tim + function mist.time.convertToSec(timeTable) + + timeInSec = 0 + if timeTable and type(timeTable) == 'number' then + timeInSec = timeTable + elseif timeTable and type(timeTable) == 'table' and (timeTable.d or timeTable.h or timeTable.m or timeTable.s) then + if timeTable.d and type(timeTable.d) == 'number' then + timeInSec = timeInSec + (timeTable.d*86400) + end + if timeTable.h and type(timeTable.h) == 'number' then + timeInSec = timeInSec + (timeTable.h*3600) + end + if timeTable.m and type(timeTable.m) == 'number' then + timeInSec = timeInSec + (timeTable.m*60) + end + if timeTable.s and type(timeTable.s) == 'number' then + timeInSec = timeInSec + timeTable.s + end + + end + return timeInSec + end + + function mist.time.getDHMS(timeInSec) + if timeInSec and type(timeInSec) == 'number' then + local tbl = {d = 0, h = 0, m = 0, s = 0} + if timeInSec > 86400 then + while timeInSec > 86400 do + tbl.d = tbl.d + 1 + timeInSec = timeInSec - 86400 + end + end + if timeInSec > 3600 then + while timeInSec > 3600 do + tbl.h = tbl.h + 1 + timeInSec = timeInSec - 3600 + end + end + if timeInSec > 60 then + while timeInSec > 60 do + tbl.m = tbl.m + 1 + timeInSec = timeInSec - 60 + end + end + tbl.s = timeInSec + return tbl + else + log:error("Didn't recieve number") + return + end + end + + function mist.getMilString(theTime) + local timeInSec = 0 + if theTime then + timeInSec = mist.time.convertToSec(theTime) + else + timeInSec = mist.utils.round(timer.getAbsTime(), 0) + end + + local DHMS = mist.time.getDHMS(timeInSec) + + return tostring(string.format('%02d', DHMS.h) .. string.format('%02d',DHMS.m)) + end + + function mist.getClockString(theTime, hour) + local timeInSec = 0 + if theTime then + timeInSec = mist.time.convertToSec(theTime) + else + timeInSec = mist.utils.round(timer.getAbsTime(), 0) + end + local DHMS = mist.time.getDHMS(timeInSec) + if hour then + if DHMS.h > 12 then + DHMS.h = DHMS.h - 12 + return tostring(string.format('%02d', DHMS.h) .. ':' .. string.format('%02d',DHMS.m) .. ':' .. string.format('%02d',DHMS.s) .. ' PM') + else + return tostring(string.format('%02d', DHMS.h) .. ':' .. string.format('%02d',DHMS.m) .. ':' .. string.format('%02d',DHMS.s) .. ' AM') + end + else + return tostring(string.format('%02d', DHMS.h) .. ':' .. string.format('%02d',DHMS.m) .. ':' .. string.format('%02d',DHMS.s)) + end + end + + -- returns the date in string format + -- both variables optional + -- first val returns with the month as a string + -- 2nd val defins if it should be written the American way or the wrong way. + function mist.time.getDate(convert) + local cal = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} -- starts at june. 2011. 2012 is leap year, sigh. add a simple check for leap year + local date = {} + date.d = 0 + date.m = 6 + date.y = 2011 + + local start = 0 + local timeInSec = mist.utils.round(timer.getAbsTime()) + if convert and type(convert) == 'number' then + timeInSec = convert + end + + while start < timeInSec do + + if date.d == cal[date.m] then + --if y % 4 >= 0 and i == 2 then -- for leap year. DCS doesnt do leapyear, but I am keeping the code dormant because maybe with WW2 the mission year may become relevant + + --else + date.m = date.m + 1 + date.d = 0 + --end + end + if date.m == 13 then + date.m = 1 + date.y = date.y + 1 + end + date.d = date.d + 1 + start = start + 86400 + end + return date + end + + function mist.time.relativeToStart(time) + if type(time) == 'number' then + return time - timer.getTime0() + end + end + + function mist.getDateString(rtnType, murica, oTime) -- returns date based on time + local word = {'January', 'Feburary', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' } -- 'etc + local curTime = 0 + if oTime then + curTime = oTime + else + curTime = mist.utils.round(timer.getAbsTime()) + end + local tbl = mist.time.getDate(curTime) + + if rtnType then + if murica then + return tostring(word[tbl.m] .. ' ' .. tbl.d .. ' ' .. tbl.y) + else + return tostring(tbl.d .. ' ' .. word[tbl.m] .. ' ' .. tbl.y) + end + else + if murica then + return tostring(tbl.m .. '.' .. tbl.d .. '.' .. tbl.y) + else + return tostring(tbl.d .. '.' .. tbl.m .. '.' .. tbl.y) + end + end + end + --WIP + function mist.time.milToGame(milString, rtnType) --converts a military time. By default returns the abosolute time that event would occur. With optional value it returns how many seconds from time of call till that time. + local curTime = mist.utils.round(timer.getAbsTime()) + local milTimeInSec = 0 + + if milString and type(milString) == 'string' and string.len(milString) >= 4 then + local hr = tonumber(string.sub(milString, 1, 2)) + local mi = tonumber(string.sub(milString, 3)) + milTimeInSec = milTimeInSec + (mi*60) + (hr*3600) + elseif milString and type(milString) == 'table' and (milString.d or milString.h or milString.m or milString.s) then + milTimeInSec = mist.time.convertToSec(milString) + end + + local startTime = timer.getTime0() + local daysOffset = 0 + if startTime > 86400 then + daysOffset = mist.utils.round(startTime/86400) + if daysOffset > 0 then + milTimeInSec = milTimeInSec *daysOffset + end + end + + if curTime > milTimeInSec then + milTimeInSec = milTimeInSec + 86400 + end + if rtnType then + milTimeInSec = milTimeInSec - startTime + end + return milTimeInSec + end + + +end + +--- Group task functions. +-- @section tasks +do -- group tasks scope + mist.ground = {} + mist.fixedWing = {} + mist.heli = {} + mist.air = {} + mist.air.fixedWing = {} + mist.air.heli = {} + + --- Tasks group to follow a route. + -- This sets the mission task for the given group. + -- Any wrapped actions inside the path (like enroute + -- tasks) will be executed. + -- @tparam Group group group to task. + -- @tparam table path containing + -- points defining a route. + function mist.goRoute(group, path) + local misTask = { + id = 'Mission', + params = { + route = { + points = mist.utils.deepCopy(path), + }, + }, + } + if type(group) == 'string' then + group = Group.getByName(group) + end + if group then + local groupCon = group:getController() + if groupCon then + groupCon:setTask(misTask) + return true + end + end + return false + end + + -- same as getGroupPoints but returns speed and formation type along with vec2 of point} + function mist.getGroupRoute(groupIdent, task) + -- 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 = mist.DBs.MEgroupsByName[groupIdent].groupId + end + + for coa_name, coa_data in pairs(env.mission.coalition) do + if (coa_name == 'red' or coa_name == 'blue') and type(coa_data) == 'table' then + if coa_data.country then --there is a country table + for cntry_id, cntry_data in pairs(coa_data.country) do + for obj_type_name, obj_type_data in pairs(cntry_data) do + if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" then -- only these types have points + if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's a group! + for group_num, group_data in pairs(obj_type_data.group) do + if group_data and group_data.groupId == gpId then -- this is the group we are looking for + if group_data.route and group_data.route.points and #group_data.route.points > 0 then + local points = {} + + for point_num, point in pairs(group_data.route.points) do + local routeData = {} + if not point.point then + routeData.x = point.x + routeData.y = point.y + else + routeData.point = point.point --it's possible that the ME could move to the point = Vec2 notation. + end + routeData.form = point.action + routeData.speed = point.speed + routeData.alt = point.alt + routeData.alt_type = point.alt_type + routeData.airdromeId = point.airdromeId + routeData.helipadId = point.helipadId + routeData.type = point.type + routeData.action = point.action + if task then + routeData.task = point.task + end + points[point_num] = routeData + end + + return points + end + return + end --if group_data and group_data.name and group_data.name == 'groupname' + end --for group_num, group_data in pairs(obj_type_data.group) do + end --if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then + end --if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then + end --for obj_type_name, obj_type_data in pairs(cntry_data) do + end --for cntry_id, cntry_data in pairs(coa_data.country) do + end --if coa_data.country then --there is a country table + end --if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then + end --for coa_name, coa_data in pairs(mission.coalition) do + end + + -- function mist.ground.buildPath() end -- ???? + + function mist.ground.patrolRoute(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 = mist.getGroupRoute(useGroupRoute) + end + else + useRoute = vars.route + local posStart = mist.getLeadPos(gpData) + useRoute[1] = mist.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 = mist.getLeadPos(gpData) + + + useRoute[#useRoute + 1] = mist.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] = mist.ground.buildWP(tempRoute[i], tempForm, tempSpeed) + end + + if pType and string.lower(pType) == 'doubleback' then + local curRoute = mist.utils.deepCopy(useRoute) + for i = #curRoute, 2, -1 do + useRoute[#useRoute + 1] = mist.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] = 'mist.ground.patrolRoute(' + cTask3[#cTask3 + 1] = mist.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 + mist.goRoute(gpData, useRoute) + + return + end + + function mist.ground.patrol(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 + + mist.ground.patrolRoute(vars) + + return + end + + -- No longer accepts path + function mist.ground.buildWP(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 = mist.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 + + function mist.fixedWing.buildWP(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 altType == 'agl' then + wp.alt_type = 'RADIO' + elseif altType == 'baro' or altType == '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 = mist.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 + + function mist.heli.buildWP(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 altType == 'agl' then + wp.alt_type = 'RADIO' + elseif altType == 'baro' or altType == '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 = mist.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 + + -- need to return a Vec3 or Vec2? + function mist.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 + + function mist.getRandomPointInZone(zoneName, innerRadius) + if type(zoneName) == 'string' and type(trigger.misc.getZone(zoneName)) == 'table' then + return mist.getRandPointInCircle(trigger.misc.getZone(zoneName).point, trigger.misc.getZone(zoneName).radius, innerRadius) + end + return false + end + + function mist.groupToRandomPoint(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 mist.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 = mist.getRandPointInCircle(point, radius, innerRadius) + + local offset = {} + local posStart = mist.getLeadPos(group) + + offset.x = mist.utils.round(math.sin(heading - (math.pi/2)) * 50 + rndCoord.x, 3) + offset.z = mist.utils.round(math.cos(heading + (math.pi/2)) * 50 + rndCoord.y, 3) + path[#path + 1] = mist.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] = mist.ground.buildWP({x = posStart.x + 11, z = posStart.z + 11}, 'off_road', speed) + path[#path + 1] = mist.ground.buildWP(posStart, 'on_road', speed) + path[#path + 1] = mist.ground.buildWP(offset, 'on_road', speed) + else + path[#path + 1] = mist.ground.buildWP({x = posStart.x + 25, z = posStart.z + 25}, form, speed) + end + + path[#path + 1] = mist.ground.buildWP(offset, form, speed) + path[#path + 1] = mist.ground.buildWP(rndCoord, form, speed) + + mist.goRoute(group, path) + + return + end + + function mist.groupRandomDistSelf(gpData, dist, form, heading, speed) + local pos = mist.getLeadPos(gpData) + local fakeZone = {} + fakeZone.radius = dist or math.random(300, 1000) + fakeZone.point = {x = pos.x, y = pos.y, z = pos.z} + mist.groupToRandomZone(gpData, fakeZone, form, heading, speed) + + return + end + + function mist.groupToRandomZone(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 = mist.utils.kmphToMps(speed) + end + + local vars = {} + vars.group = gpData + vars.radius = zone.radius + vars.form = form + vars.headingDegrees = heading + vars.speed = speed + vars.point = mist.utils.zoneToVec3(zone) + + mist.groupToRandomPoint(vars) + + return + end + + function mist.isTerrainValid(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 + + function mist.terrainHeightDiff(coord, searchSize) + local samples = {} + local searchRadius = 5 + if searchSize then + searchRadius = searchSize + end + if type(coord) == 'string' then + coord = mist.utils.zoneToVec3(coord) + end + + coord = mist.utils.makeVec2(coord) + + samples[#samples + 1] = land.getHeight(coord) + for i = 0, 360, 30 do + samples[#samples + 1] = land.getHeight({x = (coord.x + (math.sin(math.rad(i))*searchRadius)), y = (coord.y + (math.cos(math.rad(i))*searchRadius))}) + if searchRadius >= 20 then -- if search radius is sorta large, take a sample halfway between center and outer edge + samples[#samples + 1] = land.getHeight({x = (coord.x + (math.sin(math.rad(i))*(searchRadius/2))), y = (coord.y + (math.cos(math.rad(i))*(searchRadius/2)))}) + end + end + local tMax, tMin = 0, 1000000 + for index, height in pairs(samples) do + if height > tMax then + tMax = height + end + if height < tMin then + tMin = height + end + end + return mist.utils.round(tMax - tMin, 2) + end + + function mist.groupToPoint(gpData, point, form, heading, speed, useRoads) + if type(point) == 'string' then + point = trigger.misc.getZone(point) + end + if speed then + speed = mist.utils.kmphToMps(speed) + end + + local vars = {} + vars.group = gpData + vars.form = form + vars.headingDegrees = heading + vars.speed = speed + vars.disableRoads = useRoads + vars.point = mist.utils.zoneToVec3(point) + mist.groupToRandomPoint(vars) + + return + end + + function mist.getLeadPos(group) + if type(group) == 'string' then -- group name + group = Group.getByName(group) + end + + local units = group:getUnits() + + local leader = units[1] + if not Unit.isExist(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 Unit.isExist(unit) and ind < lowestInd then + lowestInd = ind + return unit:getPosition().p + end + end + end + if leader and Unit.isExist(leader) then -- maybe a little too paranoid now... + return leader:getPosition().p + end + end + +end + +--- Database tables. +-- @section mist.DBs + +--- Mission data +-- @table mist.DBs.missionData +-- @field startTime mission start time +-- @field theatre mission theatre/map e.g. Caucasus +-- @field version mission version +-- @field files mission resources + +--- Tables used as parameters. +-- @section varTables + +--- mist.flagFunc.units_in_polygon parameter table. +-- @table unitsInPolygonVars +-- @tfield table unit name table @{UnitNameTable}. +-- @tfield table zone table defining a polygon. +-- @tfield number|string flag flag to set to true. +-- @tfield[opt] number|string stopflag if set to true the function +-- will stop evaluating. +-- @tfield[opt] number maxalt maximum altitude (MSL) for the +-- polygon. +-- @tfield[opt] number req_num minimum number of units that have +-- to be in the polygon. +-- @tfield[opt] number interval sets the interval for +-- checking if units are inside of the polygon in seconds. Default: 1. +-- @tfield[opt] boolean toggle switch the flag to false if required +-- conditions are not met. Default: false. +-- @tfield[opt] table unitTableDef + +--- Logger class. +-- @type mist.Logger +do -- mist.Logger scope + mist.Logger = {} + + --- parses text and substitutes keywords with values from given array. + -- @param text string containing keywords to substitute with values + -- or a variable. + -- @param ... variables to use for substitution in string. + -- @treturn string new string with keywords substituted or + -- value of variable as string. + local function formatText(text, ...) + if type(text) ~= 'string' then + if type(text) == 'table' then + text = mist.utils.oneLineSerialize(text) + else + text = tostring(text) + end + else + for index,value in ipairs(arg) do + -- TODO: check for getmetatabel(value).__tostring + if type(value) == 'table' then + value = mist.utils.oneLineSerialize(value) + else + value = tostring(value) + end + text = text:gsub('$' .. index, value) + end + end + local fName = nil + local cLine = nil + if debug then + local dInfo = debug.getinfo(3) + fName = dInfo.name + cLine = dInfo.currentline + -- local fsrc = dinfo.short_src + --local fLine = dInfo.linedefined + end + if fName and cLine then + return fName .. '|' .. cLine .. ': ' .. text + elseif cLine then + return cLine .. ': ' .. text + else + return ' ' .. text + end + end + + local function splitText(text) + local tbl = {} + while text:len() > 4000 do + local sub = text:sub(1, 4000) + text = text:sub(4001) + table.insert(tbl, sub) + end + table.insert(tbl, text) + return tbl + end + + --- Creates a new logger. + -- Each logger has it's own tag and log level. + -- @tparam string tag tag which appears at the start of + -- every log line produced by this logger. + -- @tparam[opt] number|string level the log level defines which messages + -- will be logged and which will be omitted. Log level 3 beeing the most verbose + -- and 0 disabling all output. This can also be a string. Allowed strings are: + -- "none" (0), "error" (1), "warning" (2) and "info" (3). + -- @usage myLogger = mist.Logger:new("MyScript") + -- @usage myLogger = mist.Logger:new("MyScript", 2) + -- @usage myLogger = mist.Logger:new("MyScript", "info") + -- @treturn mist.Logger + function mist.Logger:new(tag, level) + local l = {} + l.tag = tag + setmetatable(l, self) + self.__index = self + self:setLevel(level) + return l + end + + --- Sets the level of verbosity for this logger. + -- @tparam[opt] number|string level the log level defines which messages + -- will be logged and which will be omitted. Log level 3 beeing the most verbose + -- and 0 disabling all output. This can also be a string. Allowed strings are: + -- "none" (0), "error" (1), "warning" (2) and "info" (3). + -- @usage myLogger:setLevel("info") + -- @usage -- log everything + --myLogger:setLevel(3) + function mist.Logger:setLevel(level) + if not level then + self.level = 2 + else + if type(level) == 'string' then + if level == 'none' or level == 'off' then + self.level = 0 + elseif level == 'error' then + self.level = 1 + elseif level == 'warning' then + self.level = 2 + elseif level == 'info' then + self.level = 3 + end + elseif type(level) == 'number' then + self.level = level + else + self.level = 2 + end + end + end + + --- Logs error and shows alert window. + -- This logs an error to the dcs.log and shows a popup window, + -- pausing the simulation. This works always even if logging is + -- disabled by setting a log level of "none" or 0. + -- @tparam string text the text with keywords to substitute. + -- @param ... variables to be used for substitution. + -- @usage myLogger:alert("Shit just hit the fan! WEEEE!!!11") + function mist.Logger:alert(text, ...) + text = formatText(text, unpack(arg)) + if text:len() > 4000 then + local texts = splitText(text) + for i = 1, #texts do + if i == 1 then + env.error(self.tag .. '|' .. texts[i], true) + else + env.error(texts[i]) + end + end + else + env.error(self.tag .. '|' .. text, true) + end + end + + --- Logs a message, disregarding the log level. + -- @tparam string text the text with keywords to substitute. + -- @param ... variables to be used for substitution. + -- @usage myLogger:msg("Always logged!") + function mist.Logger:msg(text, ...) + text = formatText(text, unpack(arg)) + if text:len() > 4000 then + local texts = splitText(text) + for i = 1, #texts do + if i == 1 then + env.info(self.tag .. '|' .. texts[i]) + else + env.info(texts[i]) + end + end + else + env.info(self.tag .. '|' .. text) + end + end + + --- Logs an error. + -- logs a message prefixed with this loggers tag to dcs.log as + -- long as at least the "error" log level (1) is set. + -- @tparam string text the text with keywords to substitute. + -- @param ... variables to be used for substitution. + -- @usage myLogger:error("Just an error!") + -- @usage myLogger:error("Foo is $1 instead of $2", foo, "bar") + function mist.Logger:error(text, ...) + if self.level >= 1 then + text = formatText(text, unpack(arg)) + if text:len() > 4000 then + local texts = splitText(text) + for i = 1, #texts do + if i == 1 then + env.error(self.tag .. '|' .. texts[i]) + else + env.error(texts[i]) + end + end + else + env.error(self.tag .. '|' .. text) + end + end + end + + --- Logs a warning. + -- logs a message prefixed with this loggers tag to dcs.log as + -- long as at least the "warning" log level (2) is set. + -- @tparam string text the text with keywords to substitute. + -- @param ... variables to be used for substitution. + -- @usage myLogger:warn("Mother warned you! Those $1 from the interwebs are $2", {"geeks", 1337}) + function mist.Logger:warn(text, ...) + if self.level >= 2 then + text = formatText(text, unpack(arg)) + if text:len() > 4000 then + local texts = splitText(text) + for i = 1, #texts do + if i == 1 then + env.warning(self.tag .. '|' .. texts[i]) + else + env.warning(texts[i]) + end + end + else + env.warning(self.tag .. '|' .. text) + end + end + end + + --- Logs a info. + -- logs a message prefixed with this loggers tag to dcs.log as + -- long as the highest log level (3) "info" is set. + -- @tparam string text the text with keywords to substitute. + -- @param ... variables to be used for substitution. + -- @see warn + function mist.Logger:info(text, ...) + if self.level >= 3 then + text = formatText(text, unpack(arg)) + if text:len() > 4000 then + local texts = splitText(text) + for i = 1, #texts do + if i == 1 then + env.info(self.tag .. '|' .. texts[i]) + else + env.info(texts[i]) + end + end + else + env.info(self.tag .. '|' .. text) + end + end + end + +end + +-- initialize mist +mist.init() +env.info(('Mist version ' .. mist.majorVersion .. '.' .. mist.minorVersion .. '.' .. mist.build .. ' loaded.')) + +-- vim: noet:ts=2:sw=2 diff --git a/mist_doc_file.doc b/mist_doc_file.doc index fe2d6fb169f7127113c33a32bc0501706cf1756d..2dd48b79e3ce7fafe93d784e1b793b45adcd83ad 100644 GIT binary patch delta 102129 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 Yin?~|(4j|hx1Pl%h2`UF$0uk07e@%rWdHyG delta 91965 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 bOMhRfq<>K9QdKKeE?K3N*0;L%h$R09Hb1YM diff --git a/rev changelog.txt b/rev changelog.txt index 9aa72fb..7fa9043 100644 --- a/rev changelog.txt +++ b/rev changelog.txt @@ -1,3 +1,7 @@ +v66 +-Added default setting for logger if no value present +-Added documentation for logger + v65 -Fixed update db tables not updating

p)}R7g^Bif@$XeaX`BI`-o0&yttv# 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 t&=oxibz}S8_L~!ZEZCu0m^=`GvodE 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