mission = 
{
    ["requiredModules"] = 
    {
    }, -- end of ["requiredModules"]
    ["date"] = 
    {
        ["Day"] = 21,
        ["Year"] = 2016,
        ["Month"] = 6,
    }, -- end of ["date"]
    ["result"] = 
    {
        ["offline"] = 
        {
            ["conditions"] = 
            {
            }, -- end of ["conditions"]
            ["actions"] = 
            {
            }, -- end of ["actions"]
            ["func"] = 
            {
            }, -- end of ["func"]
        }, -- end of ["offline"]
        ["total"] = 0,
        ["blue"] = 
        {
            ["conditions"] = 
            {
            }, -- end of ["conditions"]
            ["actions"] = 
            {
            }, -- end of ["actions"]
            ["func"] = 
            {
            }, -- end of ["func"]
        }, -- end of ["blue"]
        ["red"] = 
        {
            ["conditions"] = 
            {
            }, -- end of ["conditions"]
            ["actions"] = 
            {
            }, -- end of ["actions"]
            ["func"] = 
            {
            }, -- end of ["func"]
        }, -- end of ["red"]
    }, -- end of ["result"]
    ["maxDictId"] = 5,
    ["trig"] = 
    {
        ["actions"] = 
        {
            [1] = "a_do_script(\"dcsCommon = {}\\\
dcsCommon.version = \\\"3.0.7\\\"\\\
--[[-- VERSION HISTORY \\\
3.0.0  - removed bad bug in stringStartsWith, only relevant if caseSensitive is false \\\
       - point2text new intsOnly option \\\
       - arrangeGroupDataIntoFormation minDist harden\\\
       - cleanup \\\
       - new pointInDirectionOfPointXYY()\\\
       - createGroundGroupWithUnits now supports liveries\\\
       - new getAllExistingPlayersAndUnits()\\\
3.0.1  - clone: better handling of string type\\\
3.0.2  - new getPlayerUnit() \\\
3.0.3  - createStaticObjectForCoalitionInRandomRing() returns x and z \\\
       - isTroopCarrier() also supports 'helo' keyword\\\
        - new createTakeOffFromGroundRoutePointData()\\\
3.0.4  - getGroupLocation() hardened, optional verbose \\\
3.0.5  - new getNthItem()\\\
       - new getFirstItem()\\\
       - arrayContainsString() can handle dicts \\\
       - new pointXpercentYdegOffAB()\\\
3.0.6  - new arrayContainsStringCaseInsensitive()\\\
3.0.7  - fixed small bug in wildArrayContainsString \\\
\\\
--]]--\\\
\\\
    -- dcsCommon is a library of common lua functions \\\
    -- for easy access and simple mission programming\\\
    -- (c) 2021 - 2024 by Christian Franz and cf/x AG\\\
\\\
    dcsCommon.verbose = false -- set to true to see debug messages. Lots of them\\\
    dcsCommon.uuidStr = \\\"uuid-\\\"\\\
    dcsCommon.simpleUUID = 76543 -- a number to start. as good as any\\\
    \\\
    -- globals\\\
    dcsCommon.cbID = 0 -- callback id for simple callback scheduling\\\
    dcsCommon.troopCarriers = {\\\"Mi-8MT\\\", \\\"UH-1H\\\", \\\"Mi-24P\\\"} -- Ka-50, Apache and Gazelle can't carry troops\\\
    dcsCommon.coalitionSides = {0, 1, 2}\\\
    dcsCommon.maxCountry = 86 -- number of countries defined in total \\\
    \\\
    -- lookup tables\\\
    dcsCommon.groupID2Name = {}\\\
    dcsCommon.unitID2Name = {}\\\
    dcsCommon.unitID2X = {}\\\
    dcsCommon.unitID2Y = {}\\\
\\\
    -- verify that a module is loaded. obviously not required\\\
    -- for dcsCommon, but all higher-order modules\\\
    function dcsCommon.libCheck(testingFor, requiredLibs)\\\
        local canRun = true \\\
        for idx, libName in pairs(requiredLibs) do \\\
            if not _G[libName] then \\\
                trigger.action.outText(\\\"*** \\\" .. testingFor .. \\\" requires \\\" .. libName, 30)\\\
                canRun = false \\\
            end\\\
        end\\\
        return canRun\\\
    end\\\
\\\
    -- read all groups and units from miz and build a reference table\\\
    function dcsCommon.collectMissionIDs()\\\
    -- create cross reference tables to be able to get a group or\\\
    -- unit's name by ID\\\
        for coa_name_miz, coa_data in pairs(env.mission.coalition) do -- iterate all coalitions\\\
            local coa_name = coa_name_miz\\\
            if string.lower(coa_name_miz) == 'neutrals' then -- remove 's' at neutralS\\\
                coa_name = 'neutral'\\\
            end\\\
            -- directly convert coalition into number for easier access later\\\
            local coaNum = 0\\\
            if coa_name == \\\"red\\\" then coaNum = 1 end \\\
            if coa_name == \\\"blue\\\" then coaNum = 2 end \\\
            \\\
            if type(coa_data) == 'table' then -- coalition = {bullseye, nav_points, name, county}, \\\
                                              -- with county being an array \\\
                if coa_data.country then -- make sure there a country table for this coalition\\\
                    for cntry_id, cntry_data in pairs(coa_data.country) do -- iterate all countries for this \\\
                        -- per country = {id, name, vehicle, helicopter, plane, ship, static}\\\
                        local countryName = string.lower(cntry_data.name)\\\
                        local countryID = cntry_data.id \\\
                        if type(cntry_data) == 'table' then    -- filter strings .id and .name \\\
                            for obj_type_name, obj_type_data in pairs(cntry_data) do\\\
                                -- only look at helos, ships, planes and vehicles\\\
                                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\\\" -- what about \\\"cargo\\\"?\\\
                                then -- (so it's not id or name)\\\
                                    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 at least one group!\\\
                                        for group_num, group_data in pairs(obj_type_data.group) do\\\
                                            \\\
                                            local aName = group_data.name \\\
                                            local aID = group_data.groupId\\\
                                            -- store this reference \\\
                                            dcsCommon.groupID2Name[aID] = aName \\\
                                            \\\
                                            -- now iterate all units in this group \\\
                                            -- for player into \\\
                                            for unit_num, unit_data in pairs(group_data.units) do\\\
                                                if unit_data.name and unit_data.unitId then \\\
                                                    -- store this reference \\\
                                                    dcsCommon.unitID2Name[unit_data.unitId] = unit_data.name\\\
                                                    dcsCommon.unitID2X[unit_data.unitId] = unit_data.x\\\
                                                    dcsCommon.unitID2Y[unit_data.unitId] = unit_data.y\\\
                                                end\\\
                                            end -- for all units\\\
                                        end -- for all groups \\\
                                    end --if has category data \\\
                                end --if plane, helo etc... category\\\
                            end --for all objects in country \\\
                        end --if has country data \\\
                    end --for all countries in coalition\\\
                end --if coalition has country table \\\
            end -- if there is coalition data  \\\
        end --for all coalitions in mission \\\
    end\\\
\\\
    function dcsCommon.getUnitNameByID(theID)\\\
        -- accessor function for later expansion\\\
        return dcsCommon.unitID2Name[theID]\\\
    end\\\
    \\\
    function dcsCommon.getGroupNameByID(theID)\\\
        -- accessor function for later expansion \\\
        return dcsCommon.groupID2Name[theID]\\\
    end\\\
\\\
    function dcsCommon.getUnitStartPosByID(theID)\\\
        local x = dcsCommon.unitID2X[theID]\\\
        local y = dcsCommon.unitID2Y[theID]\\\
        return x, y\\\
    end\\\
    \\\
    -- returns only positive values, lo must be >0 and <= hi \\\
    function dcsCommon.randomBetween(loBound, hiBound)\\\
        if not loBound then loBound = 1 end \\\
        if not hiBound then hiBound = 1 end \\\
        if loBound == hiBound then return loBound end \\\
\\\
        local delayMin = loBound\\\
        local delayMax = hiBound \\\
        local delay = delayMax \\\
    \\\
        if delayMin ~= delayMax then \\\
            -- pick random in range , say 3-7 --> 5 s!\\\
            local delayDiff = (delayMax - delayMin) + 1 -- 7-3 + 1\\\
            delay = dcsCommon.smallRandom(delayDiff) - 1 --> 0-4\\\
            delay = delay + delayMin \\\
            if delay > delayMax then delay = delayMax end \\\
            if delay < 1 then delay = 1 end \\\
        \\\
            if dcsCommon.verbose then \\\
                trigger.action.outText(\\\"+++dcsC: delay range \\\" .. delayMin .. \\\"-\\\" .. delayMax .. \\\": selected \\\" .. delay, 30)\\\
            end\\\
        end\\\
        \\\
        return delay\\\
    end\\\
    \\\
\\\
    -- taken inspiration from mist, as dcs lua has issues with\\\
    -- random numbers smaller than 50. Given a range of x numbers 1..x, it is \\\
    -- repeated a number of times until it fills an array of at least \\\
    -- 50 items (usually some more), and only then one itemis picked from \\\
    -- that array with a random number that is from a greater range (0..50+)\\\
    function dcsCommon.smallRandom(theNum) -- adapted from mist, only support ints\\\
        theNum = math.floor(theNum)\\\
        if theNum >= 50 then return math.random(theNum) end\\\
        if theNum < 1 then\\\
            trigger.action.outText(\\\"smallRandom: invoke with argument < 1 (\\\" .. theNum .. \\\"), using 1\\\", 30)\\\
            theNum = 1 \\\
        end \\\
        -- for small randoms (<50) \\\
        local lowNum, highNum\\\
        highNum = theNum\\\
        lowNum = 1\\\
        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)) -- number of times to repeat whole range to get above 50. e.g. 11 would be 5 times 1 .. 11, giving us 55 items total \\\
        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, 15 do\\\
            rtnVal = math.random(#choices) -- iterate 15 times for randomization\\\
        end\\\
        return choices[rtnVal] -- return indexed\\\
    end\\\
    \\\
    function dcsCommon.getNthItem(theSet, n)\\\
        local count = 1 \\\
        for key, value in pairs(theSet) do\\\
            if count == n then return value end \\\
            count = count + 1 \\\
        end\\\
        return nil \\\
    end\\\
\\\
    function dcsCommon.getFirstItem(theSet)\\\
        return dcsCommon.getNthItem(theSet, 1)\\\
    end\\\
\\\
    function dcsCommon.getSizeOfTable(theTable)\\\
        local count = 0\\\
        for _ in pairs(theTable) do count = count + 1 end\\\
        return count\\\
    end\\\
\\\
    function dcsCommon.findAndRemoveFromTable(theTable, theElement) -- assumes array \\\
        if not theElement then return false end \\\
        if not theTable then return false end \\\
        for i=1, #theTable do \\\
            if theTable[i] == theElement then \\\
                -- this element found. remove from table \\\
                table.remove(theTable, i)\\\
                return true \\\
            end\\\
        end\\\
    end\\\
\\\
    function dcsCommon.pickRandom(theTable)\\\
        if not theTable then \\\
            trigger.action.outText(\\\"*** warning: nil table in pick random\\\", 30)\\\
        end\\\
        \\\
        if #theTable < 1 then \\\
            trigger.action.outText(\\\"*** warning: zero choice in pick random\\\", 30)\\\
            --local k = i.ll \\\
            return nil\\\
        end\\\
        if #theTable == 1 then return theTable[1] end\\\
        r = dcsCommon.smallRandom(#theTable) --r = math.random(#theTable)\\\
        return theTable[r]\\\
    end\\\
\\\
    -- enumerateTable - make an array out of a table for indexed access\\\
    function dcsCommon.enumerateTable(theTable)\\\
        if not theTable then theTable = {} end\\\
        local array = {}\\\
        for key, value in pairs(theTable) do \\\
            table.insert(array, value)\\\
        end\\\
        return array\\\
    end\\\
\\\
    -- combine table. creates new \\\
    function dcsCommon.combineTables(inOne, inTwo)\\\
        local outTable = {}\\\
        for idx, element in pairs(inOne) do \\\
            table.insert(outTable, element)\\\
        end\\\
        for idx, element in pairs(inTwo) do \\\
            table.insert(outTable, element)\\\
        end\\\
        return outTable\\\
    end\\\
    \\\
    function dcsCommon.addToTableIfNew(theTable, theElement)\\\
        for idx, anElement in pairs(theTable) do \\\
            if anElement == theElement then return end \\\
        end\\\
        table.insert(theTable, theElement)\\\
    end\\\
-- \\\
-- A I R F I E L D S  A N D  F A R P S  \\\
--\\\
\\\
    -- airfield management \\\
    function dcsCommon.getAirbaseCat(aBase)\\\
        if not aBase then return nil end \\\
        \\\
        local airDesc = aBase:getDesc()\\\
        if not airDesc then return nil end \\\
        \\\
        local airCat = airDesc.category\\\
        return airCat \\\
    end\\\
\\\
    -- get free parking slot. optional parkingType can be used to \\\
    -- filter for a scpecific type, e.g. 104 = open field\\\
    function dcsCommon.getFirstFreeParkingSlot(aerodrome, parkingType) \\\
        if not aerodrome then return nil end \\\
        local freeSlots = aerodrome:getParking(true)\\\
        \\\
        for idx, theSlot in pairs(freeSlots) do \\\
            if not parkingType then \\\
                -- simply return the first we come across\\\
                return theSlot\\\
            end        \\\
            \\\
            if theSlot.Term_Type == parkingType then \\\
                return theSlot \\\
            end\\\
        end\\\
        \\\
        return nil \\\
    end\\\
\\\
    -- getAirbasesInRangeOfPoint: get airbases that are in range of point \\\
    function dcsCommon.getAirbasesInRangeOfPoint(center, range, filterCat, filterCoalition)\\\
        if not center then return {} end \\\
        if not range then range = 500 end -- 500m default \\\
        local basesInRange = {}\\\
        \\\
        local allAB = dcsCommon.getAirbasesWhoseNameContains(\\\"*\\\", filterCat, filterCoalition)\\\
        for idx, aBase in pairs(allAB) do             \\\
            local delta = dcsCommon.dist(center, aBase:getPoint())\\\
            if delta <= range then \\\
                table.insert(basesInRange, aBase)\\\
            end\\\
        end\\\
        return basesInRange\\\
    end\\\
\\\
    -- getAirbasesInRangeOfAirbase returns all airbases that \\\
    -- are in range of the given airbase \\\
    function dcsCommon.getAirbasesInRangeOfAirbase(airbase, includeCenter, range, filterCat, filterCoalition)\\\
        if not airbase then return {} end\\\
        if not range then range = 150000 end \\\
        local center = airbase:getPoint() \\\
        local centerName = airbase:getName() \\\
        \\\
        local ABinRange = {}\\\
        local allAB = dcsCommon.getAirbasesWhoseNameContains(\\\"*\\\", filterCat, filterCoalition)\\\
        \\\
        for idx, aBase in pairs(allAB) do \\\
            if aBase:getName() ~= centerName then \\\
                local delta = dcsCommon.dist(center, aBase:getPoint())\\\
                if delta <= range then \\\
                    table.insert(ABinRange, aBase)\\\
                end\\\
            end        \\\
        end\\\
        \\\
        if includeCenter then \\\
            table.insert(ABinRange, airbase)\\\
        end\\\
        \\\
        return ABinRange\\\
    end\\\
\\\
    function dcsCommon.getAirbasesInRangeOfAirbaseList(theCenterList, includeList, range, filterCat, filterCoalition)\\\
        local collectorDict = {}\\\
        for idx, aCenter in pairs(theCenterList) do \\\
            -- get all surrounding airbases. returns list of airfields \\\
            local surroundingAB = dcsCommon.getAirbasesInRangeOfAirbase(airbase, includeList, range, filterCat, filterCoalition)\\\
            \\\
            for idx2, theAirField in pairs (surroundingAB) do \\\
                collectorDict[airField] = theAirField \\\
            end\\\
        end\\\
        \\\
        -- make result an array\\\
        local theABList = dcsCommon.enumerateTable(collectorDict)\\\
        return theABList\\\
    end\\\
\\\
    -- getAirbasesWhoseNameContains - get all airbases containing \\\
    -- a name. filterCat is optional and can be aerodrome (0), farp (1), ship (2)\\\
    -- filterCoalition is optional and can be 0 (neutral), 1 (red), 2 (blue) or \\\
    -- a table containing categories, e.g. {0, 2} = airfields and ships but not farps \\\
    -- if no name given or aName = \\\"*\\\", then all bases are returned prior to filtering \\\
    function dcsCommon.getAirbasesWhoseNameContains(aName, filterCat, filterCoalition)\\\
        if not aName then aName = \\\"*\\\" end \\\
        local allYourBase = world.getAirbases() -- get em all \\\
        local areBelongToUs = {}\\\
        -- now iterate all bases\\\
        for idx, aBase in pairs(allYourBase) do\\\
            local airBaseName = aBase:getName() -- get display name\\\
            if aName == \\\"*\\\" or dcsCommon.containsString(airBaseName, aName) then \\\
                -- containsString is case insesitive unless told otherwise\\\
                local doAdd = true  \\\
                if filterCat then \\\
                    local aCat = dcsCommon.getAirbaseCat(aBase)\\\
                    if type(filterCat) == \\\"table\\\" then \\\
                        local hit = false\\\
                        for idx, fCat in pairs(filterCat) do \\\
                            if fCat == aCat then hit = true end\\\
                        end\\\
                        doAdd = doAdd and hit \\\
                    else \\\
                        -- make sure the airbase is of that category \\\
                        local airCat = aCat\\\
                        doAdd = doAdd and airCat == filterCat \\\
                    end\\\
                end\\\
                \\\
                if filterCoalition then \\\
                    doAdd = doAdd and filterCoalition == aBase:getCoalition()\\\
                end\\\
                \\\
                if doAdd then \\\
                    -- all good, add to table\\\
                    table.insert(areBelongToUs, aBase)\\\
                end            \\\
            end\\\
        end\\\
        return areBelongToUs\\\
    end\\\
\\\
    function dcsCommon.getFirstAirbaseWhoseNameContains(aName, filterCat, filterCoalition)\\\
        local allBases = dcsCommon.getAirbasesWhoseNameContains(aName, filterCat, filterCoalition)\\\
        for idx, aBase in pairs (allBases) do \\\
            -- simply return first \\\
            return aBase\\\
        end\\\
        return nil \\\
    end    \\\
\\\
    function dcsCommon.getClosestAirbaseTo(thePoint, filterCat, filterCoalition, allYourBase)\\\
        local delta = math.huge\\\
        if not allYourBase then \\\
            allYourBase = dcsCommon.getAirbasesWhoseNameContains(\\\"*\\\", filterCat, filterCoalition) -- get em all and filter\\\
        end \\\
        \\\
        local closestBase = nil \\\
        for idx, aBase in pairs(allYourBase) do\\\
            -- iterate them all \\\
            local abPoint = aBase:getPoint()\\\
            newDelta = dcsCommon.dist(thePoint, {x=abPoint.x, y = 0, z=abPoint.z})\\\
            if newDelta < delta then \\\
                delta = newDelta\\\
                closestBase = aBase\\\
            end\\\
        end\\\
        return closestBase, delta \\\
    end\\\
\\\
    function dcsCommon.getClosestFreeSlotForCatInAirbaseTo(cat, x, y, theAirbase, ignore)\\\
        if not theAirbase then return nil end \\\
        if not ignore then ignore = {} end \\\
        if not cat then return nil end \\\
        if (not cat == \\\"helicopter\\\") and (not cat == \\\"plane\\\") then \\\
            trigger.action.outText(\\\"+++common-getslotforcat: wrong cat <\\\" .. cat .. \\\">\\\", 30)\\\
            return nil \\\
        end\\\
        local allFree = theAirbase:getParking(true) --  only free slots\\\
        local filterFreeByType = {}\\\
        for idx, aSlot in pairs(allFree) do \\\
            local termT = aSlot.Term_Type\\\
            if termT == 104 or \\\
            (termT == 72 and cat == \\\"plane\\\") or \\\
            (termT == 68 and cat == \\\"plane\\\") or \\\
            (termT == 40 and cat == \\\"helicopter\\\") then \\\
                table.insert(filterFreeByType, aSlot)\\\
            else \\\
                -- we skip this slot, not good for type \\\
            end\\\
        end\\\
        \\\
        if #filterFreeByType == 0 then \\\
            return nil\\\
        end \\\
        \\\
        local reallyFree = {}\\\
        for idx, aSlot in pairs(filterFreeByType) do \\\
            local slotNum = aSlot.Term_Index\\\
            isTaken = false \\\
            for idy, taken in pairs(ignore) do \\\
                if taken == slotNum then isTaken = true end \\\
            end\\\
            if not isTaken then \\\
                table.insert(reallyFree, aSlot)\\\
            end\\\
        end\\\
        \\\
        if #reallyFree < 1 then \\\
            reallyFree = filterFreeByType\\\
        end\\\
        \\\
        local closestDist = math.huge \\\
        local closestSlot = nil \\\
        local p = {x = x, y = 0, z = y} -- !!\\\
        for idx, aSlot in pairs(reallyFree) do \\\
            local sp = {x = aSlot.vTerminalPos.x, y = 0, z = aSlot.vTerminalPos.z}\\\
            local currDist = dcsCommon.distFlat(p, sp)\\\
            if currDist < closestDist then \\\
                closestSlot = aSlot \\\
                closestDist = currDist \\\
            end\\\
        end\\\
        return closestSlot\\\
    end\\\
\\\
-- \\\
-- U N I T S   M A N A G E M E N T \\\
--\\\
\\\
    -- number of living units in group\\\
    function dcsCommon.livingUnitsInGroup(group)\\\
        local living = 0\\\
        local allUnits = group:getUnits()\\\
        for key, aUnit in pairs(allUnits) do \\\
            if aUnit:isExist() and aUnit:getLife() >= 1 then \\\
                living = living + 1\\\
            end\\\
        end\\\
        return living\\\
    end\\\
\\\
    -- closest living unit in group to a point\\\
    function dcsCommon.getClosestLivingUnitToPoint(group, p)\\\
        if not p then return nil end\\\
        if not group then return nil end\\\
        local closestUnit = nil\\\
        local closestDist = math.huge\\\
        local allUnits = group:getUnits()\\\
        for key, aUnit in pairs(allUnits) do \\\
            if aUnit:isExist() and aUnit:getLife() >= 1 then \\\
                local thisDist = dcsCommon.dist(p, aUnit:getPoint())\\\
                if thisDist < closestDist then \\\
                    closestDist = thisDist\\\
                    closestUnit = aUnit \\\
                end\\\
            end\\\
        end\\\
        return closestUnit, closestDist\\\
    end\\\
    \\\
    -- closest living group to a point - cat can be nil or one of Group.Category = { AIRPLANE = 0, HELICOPTER = 1, GROUND = 2, SHIP = 3, TRAIN = 4}\\\
    function dcsCommon.getClosestLivingGroupToPoint(p, coal, cat) \\\
        if not cat then cat = 2 end -- ground is default \\\
        local closestGroup = nil;\\\
        local closestGroupDist = math.huge\\\
        local allGroups =  coalition.getGroups(coal, cat) -- get all groups from this coalition, perhaps filtered by cat \\\
        for key, grp in pairs(allGroups) do\\\
            local closestUnit, dist = dcsCommon.getClosestLivingUnitToPoint(grp, p)\\\
            if closestUnit then \\\
                if dist < closestGroupDist then \\\
                    closestGroup = grp\\\
                    closestGroupDist = dist\\\
                end\\\
            end            \\\
        end\\\
        return closestGroup, closestGroupDist\\\
    end\\\
\\\
    function dcsCommon.getLivingGroupsAndDistInRangeToPoint(p, range, coal, cat) \\\
        if not cat then cat = 2 end -- ground is default \\\
        local groupsInRange = {};\\\
        local allGroups = coalition.getGroups(coal, cat) -- get all groups from this coalition, perhaps filtered by cat \\\
        for key, grp in pairs(allGroups) do\\\
            local closestUnit, dist = dcsCommon.getClosestLivingUnitToPoint(grp, p)\\\
            if closestUnit then \\\
                if dist < range then \\\
                    table.insert(groupsInRange, {group = grp, dist = dist}) -- array\\\
                end\\\
            end            \\\
        end\\\
        -- sort the groups by distance\\\
        table.sort(groupsInRange, function (left, right) return left.dist < right.dist end )\\\
        return groupsInRange\\\
    end\\\
\\\
    -- distFlat ignores y, input must be xyz points, NOT xy points  \\\
    function dcsCommon.distFlat(p1, p2) \\\
        local point1 = {x = p1.x, y = 0, z=p1.z}\\\
        local point2 = {x = p2.x, y = 0, z=p2.z}\\\
        return dcsCommon.dist(point1, point2)\\\
    end\\\
    \\\
    \\\
    -- distance between points\\\
    function dcsCommon.dist(point1, point2)     -- returns distance between two points\\\
      -- supports xyz and xy notations\\\
      if not point1 then \\\
        trigger.action.outText(\\\"+++ warning: nil point1 in common:dist\\\", 30)\\\
        point1 = {x=0, y=0, z=0}\\\
      end\\\
\\\
      if not point2 then \\\
        trigger.action.outText(\\\"+++ warning: nil point2 in common:dist\\\", 30)\\\
        point2 = {x=0, y=0, z=0}\\\
        stop.here.now = 1\\\
      end\\\
      \\\
      local p1 = {x = point1.x, y = point1.y}\\\
      if not point1.z then \\\
        p1.z = p1.y\\\
        p1.y = 0\\\
      else \\\
        p1.z = point1.z\\\
      end\\\
      \\\
      local p2 = {x = point2.x, y = point2.y}\\\
      if not point2.z then \\\
        p2.z = p2.y\\\
        p2.y = 0\\\
      else \\\
        p2.z = point2.z\\\
      end\\\
      \\\
      local x = p1.x - p2.x\\\
      local y = p1.y - p2.y \\\
      local z = p1.z - p2.z\\\
      \\\
      return (x*x + y*y + z*z)^0.5\\\
    end\\\
\\\
    function dcsCommon.delta(name1, name2) -- returns distance (in meters) of two named objects\\\
      local n1Pos = Unit.getByName(name1):getPosition().p\\\
      local n2Pos = Unit.getByName(name2):getPosition().p\\\
      return dcsCommon.dist(n1Pos, n2Pos)\\\
    end\\\
\\\
    -- lerp between a and b, x being 0..1 (percentage), clipped to [0..1]\\\
    function dcsCommon.lerp(a, b, x) \\\
        if not a then return 0 end\\\
        if not b then return 0 end\\\
        if not x then return a end\\\
        if x < 0 then x = 0 end \\\
        if x > 1 then x = 1 end \\\
        return a + (b - a ) * x\\\
    end\\\
\\\
    function dcsCommon.bearingFromAtoB(A, B) -- coords in x, z \\\
        if not A then \\\
            trigger.action.outText(\\\"WARNING: no 'A' in bearingFromAtoB\\\", 30)\\\
            return 0\\\
        end\\\
        if not B then\\\
            trigger.action.outText(\\\"WARNING: no 'B' in bearingFromAtoB\\\", 30)\\\
            return 0\\\
        end\\\
        if not A.x then \\\
            trigger.action.outText(\\\"WARNING: no 'A.x' (type A =<\\\" .. type(A) .. \\\">)in bearingFromAtoB\\\", 30)\\\
            return 0\\\
        end\\\
        if not A.z then \\\
            trigger.action.outText(\\\"WARNING: no 'A.z' (type A =<\\\" .. type(A) .. \\\">)in bearingFromAtoB\\\", 30)\\\
            return 0\\\
        end\\\
        if not B.x then \\\
            trigger.action.outText(\\\"WARNING: no 'B.x' (type B =<\\\" .. type(B) .. \\\">)in bearingFromAtoB\\\", 30)\\\
            return 0\\\
        end\\\
        if not B.z then \\\
            trigger.action.outText(\\\"WARNING: no 'B.z' (type B =<\\\" .. type(B) .. \\\">)in bearingFromAtoB\\\", 30)\\\
            return 0\\\
        end\\\
        \\\
        local dx = B.x - A.x\\\
        local dz = B.z - A.z\\\
        local bearing = math.atan2(dz, dx) -- in radiants\\\
        return bearing\\\
    end\\\
\\\
    function dcsCommon.bearingFromAtoBusingXY(A, B) -- coords in x, y \\\
        if not A then \\\
            trigger.action.outText(\\\"WARNING: no 'A' in bearingFromAtoBXY\\\", 30)\\\
            return 0\\\
        end\\\
        if not B then\\\
            trigger.action.outText(\\\"WARNING: no 'B' in bearingFromAtoBXY\\\", 30)\\\
            return 0\\\
        end\\\
        if not A.x then \\\
            trigger.action.outText(\\\"WARNING: no 'A.x' (type A =<\\\" .. type(A) .. \\\">)in bearingFromAtoBXY\\\", 30)\\\
            return 0\\\
        end\\\
        if not A.y then \\\
            trigger.action.outText(\\\"WARNING: no 'A.y' (type A =<\\\" .. type(A) .. \\\">)in bearingFromAtoBXY\\\", 30)\\\
            return 0\\\
        end\\\
        if not B.x then \\\
            trigger.action.outText(\\\"WARNING: no 'B.x' (type B =<\\\" .. type(B) .. \\\">)in bearingFromAtoBXY\\\", 30)\\\
            return 0\\\
        end\\\
        if not B.y then \\\
            trigger.action.outText(\\\"WARNING: no 'B.y' (type B =<\\\" .. type(B) .. \\\">)in bearingFromAtoBXY\\\", 30)\\\
            return 0\\\
        end\\\
        \\\
        local dx = B.x - A.x\\\
        local dz = B.y - A.y\\\
        local bearing = math.atan2(dz, dx) -- in radiants\\\
        return bearing\\\
    end\\\
\\\
    function dcsCommon.bearingInDegreesFromAtoB(A, B)\\\
        local bearing = dcsCommon.bearingFromAtoB(A, B) -- in rads \\\
        bearing = math.floor(bearing / math.pi * 180)\\\
        if bearing < 0 then bearing = bearing + 360 end\\\
        if bearing > 360 then bearing = bearing - 360 end\\\
        return bearing\\\
    end\\\
    \\\
    function dcsCommon.compassPositionOfARelativeToB(A, B)\\\
        -- warning: is REVERSE in order for bearing, returns a string like 'Sorth', 'Southwest'\\\
        if not A then return \\\"***error:A***\\\" end\\\
        if not B then return \\\"***error:B***\\\" end\\\
        local bearing = dcsCommon.bearingInDegreesFromAtoB(B, A) -- returns 0..360\\\
        if bearing < 23 then return \\\"North\\\" end \\\
        if bearing < 68 then return \\\"NE\\\" end\\\
        if bearing < 112 then return \\\"East\\\" end \\\
        if bearing < 158 then return \\\"SE\\\" end \\\
        if bearing < 202 then return \\\"South\\\" end \\\
        if bearing < 248 then return \\\"SW\\\" end \\\
        if bearing < 292 then return \\\"West\\\" end\\\
        if bearing < 338 then return \\\"NW\\\" end \\\
        return \\\"North\\\"\\\
    end\\\
    \\\
    function dcsCommon.bearing2degrees(inRad)\\\
        local degrees = inRad / math.pi * 180\\\
        if degrees < 0 then degrees = degrees + 360 end \\\
        if degrees > 360 then degrees = degrees - 360 end \\\
        return degrees \\\
    end\\\
    \\\
    function dcsCommon.bearing2compass(inrad)\\\
        local bearing = math.floor(inrad / math.pi * 180)\\\
        if bearing < 0 then bearing = bearing + 360 end\\\
        if bearing > 360 then bearing = bearing - 360 end\\\
        return dcsCommon.bearingdegrees2compass(bearing)\\\
    end\\\
    \\\
    function dcsCommon.bearingdegrees2compass(bearing)\\\
        if bearing < 23 then return \\\"North\\\" end \\\
        if bearing < 68 then return \\\"NE\\\" end\\\
        if bearing < 112 then return \\\"East\\\" end \\\
        if bearing < 158 then return \\\"SE\\\" end \\\
        if bearing < 202 then return \\\"South\\\" end \\\
        if bearing < 248 then return \\\"SW\\\" end \\\
        if bearing < 292 then return \\\"West\\\" end\\\
        if bearing < 338 then return \\\"NW\\\" end \\\
        return \\\"North\\\"\\\
    end\\\
    \\\
    function dcsCommon.clockPositionOfARelativeToB(A, B, headingOfBInDegrees)\\\
        -- o'clock notation \\\
        if not A then return \\\"***error:A***\\\" end\\\
        if not B then return \\\"***error:B***\\\" end\\\
        if not headingOfBInDegrees then headingOfBInDegrees = 0 end \\\
        local bearing = dcsCommon.bearingInDegreesFromAtoB(B, A) -- returns 0..360 \\\
        bearing = bearing - headingOfBInDegrees\\\
        return dcsCommon.getClockDirection(bearing)\\\
    end \\\
    \\\
    -- given a heading, return clock with 0 being 12, 180 being 6 etc.\\\
    function dcsCommon.getClockDirection(direction) -- inspired by cws, improvements my own\\\
        if not direction then return 0 end\\\
        direction = math.fmod (direction, 360)\\\
        while direction < 0 do \\\
            direction = direction + 360\\\
        end\\\
        while direction >= 360 do \\\
            direction = direction - 360\\\
        end\\\
        if direction < 15 then -- special case 12 o'clock past 12 o'clock\\\
            return 12\\\
        end\\\
    \\\
        direction = direction + 15 -- add offset so we get all other times correct\\\
        return math.floor(direction/30)\\\
    \\\
    end\\\
\\\
    function dcsCommon.getGeneralDirection(direction) -- inspired by cws, improvements my own\\\
        if not direction then return \\\"unkown\\\" end\\\
        direction = math.fmod (direction, 360)\\\
        while direction < 0 do \\\
            direction = direction + 360\\\
        end\\\
        while direction >= 360 do \\\
            direction = direction - 360\\\
        end\\\
        if direction < 45 then return \\\"ahead\\\" end    \\\
        if direction < 135 then return \\\"right\\\" end\\\
        if direction < 225 then return \\\"behind\\\" end\\\
        if direction < 315 then return \\\"left\\\" end \\\
        return \\\"ahead\\\"\\\
    end\\\
    \\\
    function dcsCommon.getNauticalDirection(direction) -- inspired by cws, improvements my own\\\
        if not direction then return \\\"unkown\\\" end\\\
        direction = math.fmod (direction, 360)\\\
        while direction < 0 do \\\
            direction = direction + 360\\\
        end\\\
        while direction >= 360 do \\\
            direction = direction - 360\\\
        end\\\
        if direction < 45 then return \\\"ahead\\\" end    \\\
        if direction < 135 then return \\\"starboard\\\" end\\\
        if direction < 225 then return \\\"aft\\\" end\\\
        if direction < 315 then return \\\"port\\\" end \\\
        return \\\"ahead\\\"\\\
    end\\\
\\\
    function dcsCommon.aspectByDirection(direction) -- inspired by cws, improvements my own\\\
        if not direction then return \\\"unkown\\\" end\\\
        direction = math.fmod (direction, 360)\\\
        while direction < 0 do \\\
            direction = direction + 360\\\
        end\\\
        while direction >= 360 do \\\
            direction = direction - 360\\\
        end\\\
        \\\
        if direction < 45 then return \\\"hot\\\" end    \\\
        if direction < 135 then return \\\"beam\\\" end\\\
        if direction < 225 then return \\\"drag\\\" end\\\
        if direction < 315 then return \\\"beam\\\" end \\\
        return \\\"hot\\\"\\\
    end\\\
    \\\
    function dcsCommon.whichSideOfMine(theUnit, target) -- returs two values: -1/1 = left/right and \\\"left\\\"/\\\"right\\\" \\\
        if not theUnit then return nil end \\\
        if not target then return nil end \\\
        local uDOF = theUnit:getPosition() -- returns p, x, y, z Vec3\\\
        -- with x, y, z being the normalised vectors for right, up, forward \\\
        local heading = math.atan2(uDOF.x.z, uDOF.x.x) -- returns rads\\\
        if heading < 0 then\\\
            heading = heading + 2 * math.pi    -- put heading in range of 0 to 2*pi\\\
        end\\\
        -- heading now runs from 0 through 2Pi\\\
        local A = uDOF.p\\\
        local B = target:getPoint() \\\
         \\\
        -- now get bearing from theUnit to target  \\\
        local dx = B.x - A.x\\\
        local dz = B.z - A.z\\\
        local bearing = math.atan2(dz, dx) -- in rads\\\
        if bearing < 0 then\\\
            bearing = bearing + 2 * math.pi    -- make bearing 0 to 2*pi\\\
        end\\\
\\\
        -- we now have bearing to B, and own heading. \\\
        -- subtract own heading from bearing to see at what \\\
        -- bearing target would be if we 'turned the world' so\\\
        -- that theUnit is heading 0\\\
        local dBearing = bearing - heading\\\
        -- if result < 0 or > Pi (=180°), target is left from us\\\
        if dBearing < 0 or dBearing > math.pi then return -1, \\\"left\\\" end\\\
        return 1, \\\"right\\\"\\\
        -- note: no separate case for straight in front or behind\\\
    end\\\
    \\\
    -- Distance of point p to line defined by p1,p2 \\\
    -- only on XZ map \\\
    function dcsCommon.distanceOfPointPToLineXZ(p, p1, p2)\\\
        local x21 = p2.x - p1.x \\\
        local y10 = p1.z - p.z \\\
        local x10 = p1.x - p.x \\\
        local y21 = p2.z - p1.z \\\
        local numer = math.abs((x21*y10) - (x10 * y21))\\\
        local denom = math.sqrt(x21 * x21 + y21 * y21)\\\
        local dist = numer/denom \\\
        return dist \\\
    end\\\
    \\\
    function dcsCommon.randomDegrees()\\\
        local degrees = math.random(360) * 3.14152 / 180\\\
        return degrees\\\
    end\\\
\\\
    function dcsCommon.randomPercent()\\\
        local percent = math.random(100)/100\\\
        return percent\\\
    end\\\
\\\
    function dcsCommon.randomPointOnPerimeter(sourceRadius, x, z) \\\
        return dcsCommon.randomPointInCircle(sourceRadius, sourceRadius-1, x, z)\\\
    end\\\
\\\
    function dcsCommon.randomPointInCircle(sourceRadius, innerRadius, x, z)\\\
        if not x then x = 0 end\\\
        if not z then z = 0 end \\\
        \\\
        --local y = 0\\\
        if not innerRadius then innerRadius = 0 end        \\\
        if innerRadius < 0 then innerRadius = 0 end\\\
        \\\
        local percent = dcsCommon.randomPercent() -- 1 / math.random(100)\\\
        -- now lets get a random degree\\\
        local degrees = dcsCommon.randomDegrees() -- math.random(360) * 3.14152 / 180 -- ok, it's actually radiants. \\\
        local r = (sourceRadius-innerRadius) * percent \\\
        x = x + (innerRadius + r) * math.cos(degrees)\\\
        z = z + (innerRadius + r) * math.sin(degrees)\\\
    \\\
        local thePoint = {}\\\
        thePoint.x = x\\\
        thePoint.y = 0\\\
        thePoint.z = z \\\
        \\\
        return thePoint, degrees\\\
    end\\\
\\\
    function dcsCommon.newPointAtDegreesRange(p1, degrees, radius)\\\
        local rads = degrees * 3.14152 / 180\\\
        local p2 = dcsCommon.newPointAtAngleRange(p1, rads, radius)\\\
        return p2 \\\
    end\\\
    \\\
    function dcsCommon.newPointAtAngleRange(p1, angle, radius)\\\
        local p2 = {}\\\
        p2.x = p1.x + radius * math.cos(angle)\\\
        p2.y = p1.y \\\
        p2.z = p1.z + radius * math.sin(angle)\\\
        return p2 \\\
    end\\\
\\\
    -- get group location: get the group's location by \\\
    -- accessing the fist existing, alive member of the group that it finds\\\
    function dcsCommon.getGroupLocation(group, verbose, gName)\\\
        if not verbose then verbose = false end \\\
        -- nifty trick from mist: make this work with group and group name\\\
        if type(group) == 'string' then -- group name\\\
            group = Group.getByName(group)\\\
        end\\\
        \\\
        -- get all units\\\
        local allUnits = group:getUnits()\\\
        if not allUnits then \\\
            if verbose then \\\
                trigger.action.outText(\\\"++++common: no group location for <\\\" .. gName .. \\\">, skipping.\\\", 30)\\\
            end\\\
            return nil \\\
        end \\\
\\\
        -- iterate through all members of group until one is alive and exists\\\
        for index, theUnit in pairs(allUnits) do \\\
            if (theUnit:isExist() and theUnit:getLife() > 0) then \\\
                return theUnit:getPosition().p \\\
            end\\\
        end\\\
\\\
        -- if we get here, there was no live unit \\\
        return nil \\\
        \\\
    end\\\
\\\
    -- get the group's first Unit that exists and is \\\
    -- alive \\\
    function dcsCommon.getGroupUnit(group)\\\
        if not group then return nil  end\\\
        \\\
        -- nifty trick from mist: make this work with group and group name\\\
        if type(group) == 'string' then -- group name\\\
            group = Group.getByName(group)\\\
        end\\\
        \\\
        if not group:isExist() then return nil end \\\
        \\\
        -- get all units\\\
        local allUnits = group:getUnits()\\\
\\\
        -- iterate through all members of group until one is alive and exists\\\
        for index, theUnit in pairs(allUnits) do \\\
            if Unit.isExist(theUnit) and theUnit:getLife() > 0 then \\\
                return theUnit\\\
            end;\\\
        end\\\
\\\
        -- if we get here, there was no live unit \\\
        return nil \\\
        \\\
    end\\\
\\\
    -- and here the alias\\\
    function dcsCommon.getFirstLivingUnit(group)\\\
        return dcsCommon.getGroupUnit(group)\\\
    end\\\
    \\\
    -- isGroupAlive returns true if there is at least one unit in the group that isn't dead\\\
    function dcsCommon.isGroupAlive(group)\\\
        return (dcsCommon.getGroupUnit(group) ~= nil) \\\
    end\\\
\\\
    function dcsCommon.getLiveGroupUnits(group)\\\
        -- nifty trick from mist: make this work with group and group name\\\
        if type(group) == 'string' then -- group name\\\
            group = Group.getByName(group)\\\
        end\\\
        \\\
        local liveUnits = {}\\\
        -- get all units\\\
        local allUnits = group:getUnits()\\\
\\\
        -- iterate through all members of group until one is alive and exists\\\
        for index, theUnit in pairs(allUnits) do \\\
            if (theUnit:isExist() and theUnit:getLife() > 0) then \\\
                table.insert(liveUnits, theUnit) \\\
            end;\\\
        end\\\
\\\
        -- if we get here, there was no live unit \\\
        return liveUnits\\\
    end\\\
\\\
    function dcsCommon.getGroupTypeString(group) -- convert into comma separated types \\\
        if not group then \\\
            trigger.action.outText(\\\"+++cmn getGroupTypeString: nil group\\\", 30)\\\
            return \\\"\\\" \\\
        end\\\
        if not dcsCommon.isGroupAlive(group) then \\\
            trigger.action.outText(\\\"+++cmn getGroupTypeString: dead group\\\", 30)\\\
            return \\\"\\\" \\\
        end \\\
        local theTypes = \\\"\\\"\\\
        local liveUnits = dcsCommon.getLiveGroupUnits(group)\\\
        for i=1, #liveUnits do \\\
            if i > 1 then theTypes = theTypes .. \\\",\\\" end\\\
            theTypes = theTypes .. liveUnits[i]:getTypeName()\\\
        end\\\
        return theTypes\\\
    end\\\
\\\
    function dcsCommon.getGroupTypes(group) \\\
        if not group then \\\
            trigger.action.outText(\\\"+++cmn getGroupTypes: nil group\\\", 30)\\\
            return {}\\\
        end\\\
        if not dcsCommon.isGroupAlive(group) then \\\
            trigger.action.outText(\\\"+++cmn getGroupTypes: dead group\\\", 30)\\\
            return {}\\\
        end \\\
        local liveUnits = dcsCommon.getLiveGroupUnits(group)\\\
        local unitTypes = {}\\\
        for i=1, #liveUnits do \\\
            table.insert(unitTypes, liveUnits[i]:getTypeName())\\\
        end\\\
        return unitTypes\\\
    end\\\
\\\
    function dcsCommon.getEnemyCoalitionFor(aCoalition)\\\
        if type(aCoalition) == \\\"string\\\" then \\\
            aCoalition = aCoalition:lower()\\\
            if aCoalition == \\\"red\\\" then return 2 end\\\
            if aCoalition == \\\"blue\\\" then return 1 end\\\
            return nil \\\
        end\\\
        if aCoalition == 1 then return 2 end\\\
        if aCoalition == 2 then return 1 end\\\
        return nil\\\
    end\\\
\\\
    function dcsCommon.getACountryForCoalition(aCoalition)\\\
        -- scan the table of countries and get the first country that is part of aCoalition\\\
        -- this is useful if you want to create troops for a coalition but don't know the\\\
        -- coalition's countries \\\
        -- we start with id=0 (Russia), go to id=85 (Slovenia), but skip id = 14\\\
        local i = 0\\\
        while i < dcsCommon.maxCountry do -- 86 do \\\
            if i ~= 14 then \\\
                if (coalition.getCountryCoalition(i) == aCoalition) then return i end\\\
            end\\\
            i = i + 1\\\
        end\\\
        \\\
        return nil\\\
    end\\\
    \\\
    function dcsCommon.getCountriesForCoalition(aCoalition)\\\
        if not aCoalition then aCoalition = 0 end \\\
        local allCty = {}\\\
        \\\
        local i = 0\\\
        while i < dcsCommon.maxCountry do \\\
            if i ~= 14 then -- there is no county 14\\\
                if (coalition.getCountryCoalition(i) == aCoalition) then \\\
                    table.insert(allCty, i) \\\
                end\\\
            end\\\
            i = i + 1\\\
        end\\\
        return allCty\\\
    end\\\
--\\\
--\\\
-- C A L L B A C K   H A N D L E R \\\
--\\\
--\\\
\\\
    -- installing callbacks\\\
    -- based on mist, with optional additional hooks for pre- and post-\\\
    -- processing of the event\\\
    -- when filtering occurs in pre, an alternative 'rejected' handler can be called \\\
    function dcsCommon.addEventHandler(f, pre, post, rejected) -- returns ID \\\
        local handler = {} -- build a wrapper and connect the onEvent\\\
        handler.id = dcsCommon.uuid(\\\"eventHandler\\\")\\\
        handler.f = f -- the callback itself\\\
        if (rejected) then handler.rejected = rejected end\\\
        -- now set up pre- and post-processors. defaults are set in place\\\
        -- so pre and post are optional. If pre returns false, the callback will\\\
        -- not be invoked\\\
        if (pre) then handler.pre = pre else handler.pre = dcsCommon.preCall end\\\
        if (post) then handler.post = post else handler.post = dcsCommon.postCall end\\\
        function handler:onEvent(event)\\\
            if not self.pre(event) then \\\
                if dcsCommon.verbose then\\\
                end\\\
                if (self.rejected) then self.rejected(event) end \\\
                return\\\
            end\\\
            self.f(event) -- call the handler\\\
            self.post(event) -- do post-processing\\\
        end\\\
        world.addEventHandler(handler)\\\
        return handler.id\\\
    end\\\
\\\
    function dcsCommon.preCall(e)\\\
        -- we can filter here\\\
        -- if we return false, the call is abortet\\\
        if dcsCommon.verbose then\\\
            trigger.action.outText(\\\"event \\\" .. e.id .. \\\" received: PRE-PROCESSING\\\", 10)\\\
        end\\\
        return true;\\\
    end;\\\
\\\
    function dcsCommon.postCall(e)\\\
        -- we do pos proccing here \\\
        if dcsCommon.verbose then\\\
            trigger.action.outText(\\\"event \\\" .. e.id .. \\\" received: post proc\\\", 10)\\\
        end\\\
    end\\\
    \\\
    -- highly specific eventhandler for one event only\\\
    -- based on above, with direct filtering built in; skips pre\\\
    -- but does post\\\
    function dcsCommon.addEventHandlerForEventTypes(f, evTypes, post, rejected) -- returns ID \\\
        local handler = {} -- build a wrapper and connect the onEvent\\\
        dcsCommon.cbID = dcsCommon.cbID + 1 -- increment unique count\\\
        handler.id = dcsCommon.cbID\\\
        handler.what = evTypes\\\
        if (rejected) then handler.rejected = rejected end \\\
        \\\
        handler.f = f -- set the callback itself\\\
        -- now set up post-processor. pre is hard-coded to match evType\\\
        -- post is optional. If event.id is not in evTypes, the callback will\\\
        -- not be invoked\\\
        if (post) then handler.post = post else handler.post = dcsCommon.postCall end\\\
        function handler:onEvent(event)\\\
            hasMatch = false;\\\
            for key, evType in pairs(self.what) do\\\
                if evType == event.id then\\\
                    hasMatch = true;\\\
                    break;\\\
                end;\\\
            end;\\\
            if not hasMatch then \\\
                if dcsCommon.verbose then\\\
                    trigger.action.outText(\\\"event \\\" .. e.id .. \\\" discarded - not in whitelist evTypes\\\", 10)\\\
                end\\\
                if (self.rejected) then self.rejected(event) end \\\
                return;\\\
            end;\\\
            \\\
            self.f(event) -- call the actual handler as passed to us\\\
            self.post(event) -- do post-processing \\\
        end\\\
        world.addEventHandler(handler) -- add to event handlers\\\
        return handler.id\\\
    end\\\
    \\\
    \\\
    \\\
    -- remove event handler / callback, identical to Mist \\\
    -- note we don't call world.removeEventHandler, but rather directly \\\
    -- access world.eventHandlers directly and remove kvp directly.\\\
    function dcsCommon.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\\\
\\\
--\\\
--\\\
-- C L O N I N G \\\
--\\\
--\\\
    -- topClone is a shallow clone of orig, only top level is iterated,\\\
    -- all values are ref-copied\\\
    function dcsCommon.topClone(orig)\\\
        if not orig then return nil end \\\
        local orig_type = type(orig)\\\
        local copy\\\
        if orig_type == 'table' then\\\
            copy = {}\\\
            for orig_key, orig_value in pairs(orig) do\\\
                copy[orig_key] = orig_value\\\
            end\\\
        else -- number, string, boolean, etc\\\
            copy = orig\\\
        end\\\
        return copy\\\
    end\\\
\\\
    -- clone is a recursive clone which will also clone\\\
    -- deeper levels, as used in units \\\
    function dcsCommon.clone(orig, stripMeta)\\\
        if not orig then return nil end \\\
        local orig_type = type(orig)\\\
        local copy\\\
        if orig_type == 'table' then\\\
            copy = {}\\\
            for orig_key, orig_value in next, orig, nil do\\\
                copy[dcsCommon.clone(orig_key)] = dcsCommon.clone(orig_value)\\\
            end\\\
            if not stripMeta then \\\
                -- also connect meta data\\\
                setmetatable(copy, dcsCommon.clone(getmetatable(orig)))\\\
            else \\\
                -- strip all except string, and for strings use a fresh string \\\
                if type(copy) == \\\"string\\\" then \\\
                    local tmp = \\\"\\\"\\\
                    tmp = tmp .. copy -- will get rid of any foreign metas for string \\\
                    copy = tmp \\\
                end\\\
            end\\\
        elseif orig_type == \\\"string\\\" then \\\
            local tmp = \\\"\\\"\\\
            copy = tmp .. orig \\\
        else -- number, string, boolean, etc\\\
            copy = orig\\\
        end\\\
        return copy\\\
    end\\\
\\\
    function dcsCommon.copyArray(inArray)\\\
        if not inArray then return nil end \\\
        \\\
        -- warning: this is a ref copy!\\\
        local theCopy = {}\\\
        for idx, element in pairs(inArray) do \\\
            table.insert(theCopy, element)\\\
        end\\\
        return theCopy \\\
    end\\\
--\\\
-- \\\
-- S P A W N I N G \\\
-- \\\
-- \\\
\\\
    function dcsCommon.createEmptyGroundGroupData (name)\\\
        local theGroup = {} -- empty group\\\
        theGroup.visible = false\\\
        theGroup.taskSelected = true\\\
        -- theGroup.route = {}\\\
        -- theGroup.groupId = id\\\
        theGroup.tasks = {}\\\
        -- theGroup.hidden = false -- hidden on f10?\\\
\\\
        theGroup.units = { } -- insert units here! -- use addUnitToGroupData\\\
\\\
        theGroup.x = 0\\\
        theGroup.y = 0\\\
        theGroup.name = name\\\
        -- theGroup.start_time = 0\\\
        theGroup.task = \\\"Ground Nothing\\\"\\\
        \\\
        return theGroup\\\
    end;\\\
\\\
    function dcsCommon.createEmptyAircraftGroupData (name)\\\
        local theGroup = dcsCommon.createEmptyGroundGroupData(name)--{} -- empty group\\\
\\\
        theGroup.task = \\\"Nothing\\\" -- can be others, like Transport, CAS, etc\\\
        -- returns with empty route\\\
        theGroup.route = dcsCommon.createEmptyAircraftRouteData() -- we can add points here \\\
        return theGroup\\\
    end;\\\
\\\
    function dcsCommon.createAircraftRoutePointData(x, z, altitudeInFeet, knots, altType, action)\\\
        local rp = {}\\\
        rp.x = x\\\
        rp.y = z\\\
        rp.action = \\\"Turning Point\\\"\\\
        rp.type = \\\"Turning Point\\\"\\\
        if action then rp.action = action; rp.type = action end -- warning: may not be correct, need to verify later\\\
        rp.alt = altitudeInFeet * 0.3048 -- in m \\\
        rp.speed = knots * 0.514444 -- we use m/s\\\
        rp.alt_type = \\\"BARO\\\"\\\
        if (altType) then rp.alt_type = altType end \\\
        return rp\\\
    end\\\
\\\
    function dcsCommon.addRoutePointDataToRouteData(inRoute, x, z, altitudeInFeet, knots, altType, action)\\\
        local p = dcsCommon.createAircraftRoutePointData(x, z, altitudeInFeet, knots, altType, action)\\\
        local thePoints = inRoute.points \\\
        table.insert(thePoints, p)\\\
    end\\\
    \\\
    function dcsCommon.addRoutePointDataToGroupData(group, x, z, altitudeInFeet, knots, altType, action)\\\
        if not group.route then group.route = dcsCommon.createEmptyAircraftRouteData() end\\\
        local theRoute = group.route \\\
        dcsCommon.addRoutePointDataToRouteData(theRoute, x, z, altitudeInFeet, knots, altType, action)\\\
    end\\\
\\\
    function dcsCommon.addRoutePointForGroupData(theGroup, theRP)\\\
        if not theGroup then return end \\\
        if not theGroup.route then theGroup.route = dcsCommon.createEmptyAircraftRouteData() end\\\
        \\\
        local theRoute = theGroup.route \\\
        local thePoints = theRoute.points \\\
        table.insert(thePoints, theRP)\\\
    end\\\
    \\\
    function dcsCommon.createEmptyAircraftRouteData()\\\
        local route = {}\\\
        route.points = {}\\\
        return route\\\
    end\\\
\\\
    function dcsCommon.createTakeOffFromGroundRoutePointData(pt, isHot) -- vec 3!\\\
        if not pt then return nil end \\\
            \\\
        local rp = {}    \\\
        rp.x = pt.x\\\
        rp.y = pt.z\\\
        rp.alt = pt.y \\\
        if isHot then \\\
            rp.action = \\\"From Ground Area Hot\\\"\\\
            rp.type = \\\"TakeOffGroundHot\\\" \\\
        else\\\
            rp.action = \\\"From Ground Area\\\" -- add \\\" Hot\\\" if hot\\\
            rp.type = \\\"TakeOffGround\\\" -- add \\\"Hot\\\" (NO blank) if hot\\\
        end\\\
        rp.speed = 10 --  that's 36 km/h \\\
        rp.alt_type = \\\"BARO\\\"\\\
        return rp\\\
    end\\\
    \\\
    function dcsCommon.createTakeOffFromParkingRoutePointData(aerodrome)\\\
        if not aerodrome then return nil end \\\
            \\\
        local rp = {}    \\\
        local freeParkingSlot = dcsCommon.getFirstFreeParkingSlot(aerodrome, 104) -- get big slot first \\\
        if not freeParkingSlot then \\\
            freeParkingSlot = dcsCommon.getFirstFreeParkingSlot(aerodrome) -- try any size\\\
        end\\\
            \\\
        if not freeParkingSlot then \\\
            trigger.action.outText(\\\"civA: no free parking at \\\" .. aerodrome:getName(), 30)\\\
            return nil \\\
        end\\\
            \\\
        local p = freeParkingSlot.vTerminalPos\\\
            \\\
        rp.airdromeId = aerodrome:getID() \\\
        rp.x = p.x\\\
        rp.y = p.z\\\
        rp.alt = p.y \\\
        rp.action = \\\"From Parking Area\\\"\\\
        rp.type = \\\"TakeOffParking\\\"\\\
            \\\
        rp.speed = 100 --  that's 360 km/h \\\
        rp.alt_type = \\\"BARO\\\"\\\
        return rp\\\
    end\\\
\\\
    function dcsCommon.createOverheadAirdromeRoutePointData(aerodrome)\\\
        if not aerodrome then return nil end \\\
        local rp = {}            \\\
        local p = aerodrome:getPoint()\\\
        rp.x = p.x\\\
        rp.y = p.z\\\
        rp.alt = p.y + 2000 -- 6000 ft overhead\\\
        rp.action = \\\"Turning Point\\\"\\\
        rp.type = \\\"Turning Point\\\"\\\
            \\\
        rp.speed = 133; -- in m/s? If so, that's 360 km/h \\\
        rp.alt_type = \\\"BARO\\\"\\\
        return rp\\\
    end\\\
    function dcsCommon.createOverheadAirdromeRoutPintData(aerodrome) -- backwards-compat to typo \\\
        return dcsCommon.createOverheadAirdromeRoutePointData(aerodrome)\\\
    end \\\
    \\\
\\\
    function dcsCommon.createLandAtAerodromeRoutePointData(aerodrome)\\\
        if not aerodrome then return nil end \\\
            \\\
        local rp = {}            \\\
        local p = aerodrome:getPoint()\\\
        rp.airdromeId = aerodrome:getID() \\\
        rp.x = p.x\\\
        rp.y = p.z\\\
        rp.alt = land.getHeight({x=p.x, y=p.z}) --p.y \\\
        rp.action = \\\"Landing\\\"\\\
        rp.type = \\\"Land\\\"\\\
            \\\
        rp.speed = 100; -- in m/s? If so, that's 360 km/h \\\
        rp.alt_type = \\\"BARO\\\"\\\
        return rp\\\
    end\\\
\\\
    function dcsCommon.createSimpleRoutePointData(p, alt, speed)\\\
        if not speed then speed = 133 end \\\
        if not alt then alt = 8000 end -- 24'000 feet \\\
        local rp = {}\\\
        rp.x = p.x\\\
        rp.y = p.z\\\
        rp.alt = alt\\\
        rp.action = \\\"Turning Point\\\"\\\
        rp.type = \\\"Turning Point\\\"\\\
            \\\
        rp.speed = speed; -- in m/s? If so, that's 360 km/h \\\
        rp.alt_type = \\\"BARO\\\"\\\
        return rp\\\
    end \\\
    \\\
    function dcsCommon.createRPFormationData(findex) -- must be added as \\\"task\\\" to an RP. use 4 for Echelon right\\\
        local task = {}\\\
        task.id = \\\"ComboTask\\\"\\\
        local params = {}\\\
        task.params = params\\\
        local tasks = {}\\\
        params.tasks = tasks\\\
        local t1 = {}\\\
        tasks[1] = t1\\\
        t1.number = 1\\\
        t1.auto = false \\\
        t1.id = \\\"WrappedAction\\\"\\\
        t1.enabled = true\\\
        local t1p = {}\\\
        t1.params = t1p\\\
        local action = {}\\\
        t1p.action = action \\\
        action.id = \\\"Option\\\"\\\
        local ap = {}\\\
        action.params = ap\\\
        ap.variantIndex = 3\\\
        ap.name = 5 -- AI.Option.Air.ID 5 = Formation \\\
        ap.formationIndex = findex -- 4 is echelon_right\\\
        ap.value = 262147\\\
        \\\
        return task \\\
    end\\\
\\\
    function dcsCommon.addTaskDataToRP(theTask, theGroup, rpIndex)\\\
        local theRoute = theGroup.route\\\
        local thePoints = theRoute.points\\\
        local rp = thePoints[rpIndex]\\\
        rp.task = theTask\\\
    end\\\
    \\\
    -- create a minimal payload table that is compatible with creating \\\
    -- a unit. you may need to alter this before adding the unit to\\\
    -- the mission. all params optional \\\
    function dcsCommon.createPayload(fuel, flare, chaff, gun) \\\
        local payload = {}\\\
        payload.pylons = {}\\\
        if not fuel then fuel = 1000 end -- in kg. check against fuelMassMax in type desc\\\
        if not flare then flare = 0 end\\\
        if not chaff then chaff = 0 end\\\
        if not gun then gun = 0 end\\\
        return payload \\\
        \\\
    end\\\
\\\
    function dcsCommon.createCallsign(cs) \\\
        local callsign = {}\\\
        callsign[1] = 1\\\
        callsign[2] = 1\\\
        callsign[3] = 1\\\
        if not cs then cs = \\\"Enfield11\\\" end\\\
        callsign.name = cs\\\
        return callsign\\\
    end\\\
    \\\
\\\
    -- create the data table required to spawn a unit.\\\
    -- unit types are defined in https://github.com/mrSkortch/DCS-miscScripts/tree/master/ObjectDB\\\
    function dcsCommon.createGroundUnitData(name, unitType, transportable)\\\
        local theUnit = {}\\\
        unitType = dcsCommon.trim(unitType)\\\
        theUnit.type = unitType -- e.g. \\\"LAV-25\\\",\\\
        if not transportable then transportable = false end -- elaborate, not requried code\\\
        theUnit.transportable = {[\\\"randomTransportable\\\"] = transportable} \\\
        -- theUnit.unitId = id \\\
        theUnit.skill = \\\"Average\\\" -- always average \\\
        theUnit.x = 0 -- make it zero, zero!\\\
        theUnit.y = 0\\\
        theUnit.name = name\\\
        theUnit.playerCanDrive = false\\\
        theUnit.heading = 0\\\
        return theUnit\\\
    end \\\
\\\
    function dcsCommon.createAircraftUnitData(name, unitType, transportable, altitude, speed, heading)\\\
        local theAirUnit = dcsCommon.createGroundUnitData(name, unitType, transportable)\\\
        theAirUnit.alt = 100 -- make it 100m\\\
        if altitude then theAirUnit.alt = altitude end \\\
        theAirUnit.alt_type = \\\"RADIO\\\" -- AGL\\\
        theAirUnit.speed = 77 -- m/s --> 150 knots\\\
        if speed then theAirUnit.speed = speed end \\\
        if heading then theAirUnit.heading = heading end \\\
        theAirUnit.payload = dcsCommon.createPayload()\\\
        theAirUnit.callsign = dcsCommon.createCallsign()\\\
        return theAirUnit\\\
    end\\\
    \\\
\\\
    function dcsCommon.addUnitToGroupData(theUnit, theGroup, dx, dy, heading)\\\
        -- add a unit to a group, and place it at dx, dy of group's position,\\\
        -- taking into account unit's own current location\\\
        if not dx then dx = 0 end\\\
        if not dy then dy = 0 end\\\
        if not heading then heading = 0 end\\\
        theUnit.x = theUnit.x + dx + theGroup.x\\\
        theUnit.y = theUnit.y + dy + theGroup.y \\\
        theUnit.heading = heading\\\
        table.insert(theGroup.units, theUnit)\\\
    end;\\\
\\\
    function dcsCommon.createSingleUnitGroup(name, theUnitType, x, z, heading) \\\
        -- create the container \\\
        local theNewGroup = dcsCommon.createEmptyGroundGroupData(name)\\\
        local aUnit = {}\\\
        aUnit = dcsCommon.createGroundUnitData(name .. \\\"-1\\\", theUnitType, false)\\\
--        trigger.action.outText(\\\"dcsCommon - unit name retval \\\" .. aUnit.name, 30)\\\
        dcsCommon.addUnitToGroupData(aUnit, theNewGroup, x, z, heading)\\\
        return theNewGroup\\\
    end\\\
    \\\
\\\
    function dcsCommon.arrangeGroupDataIntoFormation(theNewGroup, radius, minDist, formation, innerRadius)\\\
        -- formations:\\\
        --    (default) \\\"line\\\" (left to right along x) -- that is Y direction\\\
        --    \\\"line_v\\\" a line top to bottom -- that is X direction\\\
        --    \\\"chevron\\\" - left to right middle too top\\\
        --    \\\"scattered\\\", \\\"random\\\" -- random, innerRadius used to clear area in center\\\
        --       \\\"circle\\\", \\\"circle_forward\\\" -- circle, forward facing\\\
        --    \\\"circle_in\\\" -- circle, inwarf facing\\\
        --    \\\"circle_out\\\" -- circle, outward facing\\\
        --    \\\"grid\\\", \\\"square\\\", \\\"rect\\\" -- optimal rectangle\\\
        --    \\\"2cols\\\", \\\"2deep\\\" -- 2 columns, n deep \\\
        --    \\\"2wide\\\" -- 2 columns wide, 2 deep \\\
\\\
        local num = #theNewGroup.units \\\
        \\\
        -- now do the formation stuff\\\
        -- make sure that they keep minimum  distance \\\
        if formation == \\\"LINE_V\\\" then \\\
            -- top to bottom in zone (heding 0). -- will run through x-coordinate \\\
            -- use entire radius top to bottom \\\
            local currX = -radius\\\
            local increment = radius * 2/(num - 1) -- MUST NOT TRY WITH 1 UNIT!\\\
            for i=1, num do\\\
            \\\
                local u = theNewGroup.units[i]\\\
                u.x = currX\\\
                currX = currX + increment\\\
            end\\\
        \\\
        elseif formation == \\\"LINE\\\" then \\\
            -- left to right in zone. runs through Y\\\
            -- left and right are y because at heading 0, forward is x (not y as expected)\\\
            -- if only one, place in middle of circle and be done \\\
            if num == 1 then \\\
                -- nothing. just stay in the middle \\\
            else \\\
                local currY = -radius\\\
                local increment = radius * 2/(num - 1) -- MUST NOT TRY WITH 1 UNIT!\\\
                for i=1, num do\\\
                    local u = theNewGroup.units[i]\\\
                    u.y = currY\\\
                    currY = currY + increment\\\
                end    \\\
            end \\\
            \\\
        elseif formation == \\\"CHEVRON\\\" then \\\
            -- left to right in zone. runs through Y\\\
            -- left and right are y because at heading 0, forward is x (not y as expected)\\\
            local currY = -radius\\\
            local currX = 0\\\
            local incrementY = radius * 2/(num - 1) -- MUST NOT TRY WITH 1 UNIT!\\\
            local incrementX = radius * 2/(num - 1) -- MUST NOT TRY WITH 1 UNIT!\\\
            for i=1, num do\\\
                local u = theNewGroup.units[i]\\\
                u.x = currX\\\
                u.y = currY\\\
                -- calc coords for NEXT iteration\\\
                currY = currY + incrementY -- march left to right\\\
                if i < num / 2 then -- march up\\\
                    currX = currX + incrementX \\\
                elseif i == num / 2 then -- even number, keep height\\\
                    currX = currX + 0 \\\
                else \\\
                    currX = currX - incrementX -- march down \\\
                end \\\
                -- note: when unit number even, the wedge is sloped. may need an odd/even test for better looks\\\
            end    \\\
\\\
        elseif formation == \\\"SCATTERED\\\" or formation == \\\"RANDOM\\\" then \\\
            -- use randomPointInCircle and tehn iterate over all vehicles for mindelta\\\
            processedUnits = {}\\\
            if not minDist then minDist = 10 end \\\
            for i=1, num do\\\
                local emergencyBreak = 1 -- prevent endless loop\\\
                local lowDist = 10000\\\
                local uPoint = {}\\\
                local thePoint = {}\\\
                repeat     -- get random point until mindistance to all is kept or emergencybreak\\\
                    thePoint = dcsCommon.randomPointInCircle(radius, innerRadius) -- returns x, 0, z\\\
                    -- check if too close to others\\\
                    for idx, rUnit in pairs(processedUnits) do -- get min dist to all positioned units\\\
                        --trigger.action.outText(\\\"rPnt: thePoint =  \\\" .. dcsCommon.point2text(thePoint), 30)\\\
                        uPoint.x = rUnit.x\\\
                        uPoint.y = 0\\\
                        uPoint.z = rUnit.y \\\
                        --trigger.action.outText(\\\"rPnt: uPoint =  \\\" .. dcsCommon.point2text(uPoint), 30)\\\
                        local dist = dcsCommon.dist(thePoint, uPoint) -- measure distance to unit\\\
                        if (dist < lowDist) then lowDist = dist end\\\
                    end\\\
                    emergencyBreak = emergencyBreak + 1\\\
                until (emergencyBreak > 20) or (lowDist > minDist)\\\
                -- we have random x, y \\\
                local u = theNewGroup.units[i] -- get unit to position\\\
                u.x = thePoint.x\\\
                u.y = thePoint.z -- z --> y mapping! \\\
                -- now add the unit to the 'processed' set \\\
                table.insert(processedUnits, u)\\\
            end    \\\
\\\
        elseif dcsCommon.stringStartsWith(formation, \\\"CIRCLE\\\") then\\\
            -- units are arranged on perimeter of circle defined by radius \\\
            local currAngle = 0\\\
            local angleInc = 2 * 3.14157 / num -- increase per spoke \\\
            for i=1, num do\\\
                local u = theNewGroup.units[i] -- get unit \\\
                u.x = radius * math.cos(currAngle)\\\
                u.y = radius * math.sin(currAngle)\\\
                \\\
                -- now baldower out heading \\\
                -- circle, circle_forward no modifier of heading\\\
                if dcsCommon.stringStartsWith(formation, \\\"CIRCLE_IN\\\") then \\\
                    -- make the heading inward faceing - that's angle + pi\\\
                    u.heading = u.heading + currAngle + 3.14157\\\
                elseif dcsCommon.stringStartsWith(formation, \\\"CIRCLE_OUT\\\") then \\\
                    u.heading = u.heading + currAngle + 0\\\
                end\\\
\\\
                currAngle = currAngle + angleInc\\\
            end\\\
        elseif formation == \\\"GRID\\\" or formation == \\\"SQUARE\\\" or formation == \\\"RECT\\\" then \\\
            if num < 2 then return end \\\
            -- arrange units in an w x h grid\\\
            -- e-g- 12 units = 4 x 3. \\\
            -- calculate w \\\
            local w = math.floor(num^(0.5) + 0.5)\\\
            dcsCommon.arrangeGroupInNColumns(theNewGroup, w, radius)\\\
            \\\
        elseif formation == \\\"2DEEP\\\" or formation == \\\"2COLS\\\" then\\\
            if num < 2 then return end \\\
            -- arrange units in an 2 x h grid\\\
            local w = 2\\\
            dcsCommon.arrangeGroupInNColumnsDeep(theNewGroup, w, radius)\\\
\\\
        elseif formation == \\\"2WIDE\\\" then\\\
            if num < 2 then return end \\\
            -- arrange units in an 2 x h grid\\\
            local w = 2\\\
            dcsCommon.arrangeGroupInNColumns(theNewGroup, w, radius)\\\
        else \\\
            trigger.action.outText(\\\"dcsCommon - unknown formation: \\\" .. formation, 30)\\\
        end\\\
    \\\
    end\\\
    \\\
    function dcsCommon.arrangeGroupInNColumns(theNewGroup, w, radius)\\\
        local num = #theNewGroup.units\\\
        local h = math.floor(num / w)\\\
        if (num % w) > 0 then \\\
            h = h + 1\\\
        end\\\
        local i = 1\\\
        local xInc = 0 \\\
        if w > 1 then xInc = 2 * radius / (w-1) end\\\
        local yInc = 0\\\
        if h > 1 then yInc = 2 * radius / (h-1) end \\\
        local currY = radius \\\
        if h < 2 then currY = 0 end -- special:_ place in Y middle if only one row)\\\
        while h > 0 do \\\
            local currX = radius \\\
            local wCnt = w \\\
            while wCnt > 0 and (i <= num) do \\\
                local u = theNewGroup.units[i] -- get unit \\\
                u.x = currX\\\
                u.y = currY\\\
                currX = currX - xInc\\\
                wCnt = wCnt - 1\\\
                i = i + 1\\\
            end\\\
            currY = currY - yInc \\\
            h = h - 1\\\
        end\\\
    end\\\
    \\\
    function dcsCommon.arrangeGroupInNColumnsDeep(theNewGroup, w, radius)\\\
        local num = #theNewGroup.units\\\
        local h = math.floor(num / w)\\\
        if (num % w) > 0 then \\\
            h = h + 1\\\
        end\\\
        local i = 1\\\
        local yInc = 0 \\\
        if w > 1 then yInc = 2 * radius / (w-1) end\\\
        local xInc = 0\\\
        if h > 1 then xInc = 2 * radius / (h-1) end \\\
        local currX = radius \\\
        if h < 2 then currX = 0 end -- special:_ place in Y middle if only one row)\\\
        while h > 0 do \\\
            local currY = radius \\\
            local wCnt = w \\\
            while wCnt > 0 and (i <= num) do \\\
                local u = theNewGroup.units[i] -- get unit \\\
                u.x = currX\\\
                u.y = currY\\\
                currY = currY - yInc\\\
                wCnt = wCnt - 1\\\
                i = i + 1\\\
            end\\\
            currX = currX - xInc \\\
            h = h - 1\\\
        end\\\
    end\\\
    \\\
    \\\
    function dcsCommon.createGroundGroupWithUnits(name, theUnitTypes, radius, minDist, formation, innerRadius, liveries)\\\
        -- liveries is indexed by typeName and provides alternate livery names \\\
        -- from default.\\\
        if not minDist then minDist = 4 end -- meters\\\
        if not formation then formation = \\\"line\\\" end \\\
        if not radius then radius = 30 end -- meters \\\
        if not innerRadius then innerRadius = 0 end\\\
        if not liveries then liveries = {} end \\\
        formation = formation:upper()\\\
        -- theUnitTypes can be either a single string or a table of strings\\\
        -- see here for TypeName https://github.com/mrSkortch/DCS-miscScripts/tree/master/ObjectDB\\\
        -- formation defines how the units are going to be arranged in the\\\
        -- formation specified. \\\
        -- formations:\\\
        --    (default) \\\"line\\\" (left to right along x) -- that is Y direction\\\
        --    \\\"line_V\\\" a line top to bottom -- that is X direction\\\
        --    \\\"chevron\\\" - left to right middle too top\\\
        --    \\\"scattered\\\", \\\"random\\\" -- random, innerRadius used to clear area in center\\\
        --       \\\"circle\\\", \\\"circle_forward\\\" -- circle, forward facing\\\
        --    \\\"circle_in\\\" -- circle, inwarf facing\\\
        --    \\\"circle_out\\\" -- circle, outward facing\\\
\\\
        -- first, we create a group\\\
        local theNewGroup = dcsCommon.createEmptyGroundGroupData(name)\\\
        \\\
        -- now add a single unit or multiple units\\\
        if type(theUnitTypes) ~= \\\"table\\\" then             \\\
            local aUnit = {}\\\
            aUnit = dcsCommon.createGroundUnitData(name .. \\\"-1\\\", theUnitTypes, false)\\\
            dcsCommon.addUnitToGroupData(aUnit, theNewGroup, 0, 0) -- create with data at location (0,0)\\\
            return theNewGroup\\\
        end \\\
\\\
        -- if we get here, theUnitTypes is a table\\\
        -- now loop and create a unit for each table\\\
        local num = 1\\\
        for key, theType in pairs(theUnitTypes) do \\\
            -- trigger.action.outText(\\\"+++dcsC: creating unit \\\" .. name .. \\\"-\\\" .. num .. \\\": \\\" .. theType, 30)\\\
            local aUnit = dcsCommon.createGroundUnitData(name .. \\\"-\\\"..num, theType, false)\\\
            local theLivery = liveries[theType]\\\
            if theLivery then \\\
                aUnit.livery_id = theLivery\\\
            end \\\
            dcsCommon.addUnitToGroupData(aUnit, theNewGroup, 0, 0)\\\
            num = num + 1\\\
        end\\\
        \\\
        dcsCommon.arrangeGroupDataIntoFormation(theNewGroup, radius, minDist, formation, innerRadius)\\\
        return theNewGroup\\\
\\\
    \\\
    end\\\
\\\
-- create a new group, based on group in mission. Groups coords are 0,0 for group and all\\\
-- x,y and heading\\\
    function dcsCommon.createGroupDataFromLiveGroup(name, newName) \\\
        if not newName then newName = dcsCommon.uuid(\\\"uniqName\\\") end\\\
        -- get access to the group\\\
        local liveGroup = Group.getByName(name)\\\
        if not liveGroup then return nil end\\\
        -- get the categorty\\\
        local cat = liveGroup:getCategory()\\\
        local theNewGroup = {}\\\
        \\\
        -- create a new empty group at (0,0) \\\
        if cat == Group.Category.AIRPLANE or cat == Group.Category.HELICOPTER then \\\
            theNewGroup = dcsCommon.createEmptyAircraftGroupData(newName)\\\
        elseif cat == Group.Category.GROUND then\\\
            theNewGroup = dcsCommon.createEmptyGroudGroupData(newName)\\\
        else \\\
            trigger.action.outText(\\\"dcsCommon - unknown category: \\\" .. cat, 30)\\\
            return nil\\\
        end\\\
        \\\
\\\
        -- now get all units from live group and create data units\\\
        -- note that unit data for group has x=0, y=0\\\
        liveUnits = liveGroup:getUnits()\\\
        \\\
        for index, theUnit in pairs(liveUnits) do \\\
            -- for each unit we get the desc \\\
            local desc = theUnit:getDesc() -- of interest is only typename \\\
            local newUnit = dcsCommon.createGroundUnitData(dcsCommon.uuid(newName),\\\
                                                           desc.typeName,\\\
                                                           false)\\\
            -- we now basically have a ground unit at (0,0) \\\
            -- add mandatory fields by type\\\
            if cat == Group.Category.AIRPLANE or cat == Group.Category.HELICOPTER then \\\
                newUnit.alt = 100 -- make it 100m\\\
                newUnit.alt_type = \\\"RADIO\\\" -- AGL\\\
                newUnit.speed = 77 -- m/s --> 150 knots\\\
                newUnit.payload = dcsCommon.createPayload() -- empty payload\\\
                newUnit.callsign = dcsCommon.createCallsign() -- 'enfield11'\\\
                \\\
            elseif cat == Group.Category.GROUND then\\\
                -- we got all we need\\\
            else \\\
\\\
            end            \\\
            \\\
        end\\\
    \\\
    end;\\\
    \\\
    function dcsCommon.pointInDirectionOfPointXYY(dir, dist, p) -- dir in rad, p in XYZ returns XZZ \\\
        local fx = math.cos(dir)\\\
        local fy = math.sin(dir) \\\
        local p2 = {}\\\
        p2.x = p.x + dist * fx\\\
        p2.y = p.z + dist * fy\\\
        p2.z = p2.y -- make p2 XYY vec2/3 upcast\\\
        return p2\\\
    end\\\
\\\
    function dcsCommon.pointXpercentYdegOffAB(A, B, xPer, yDeg) -- rets xzz point \\\
        local bearingRad = dcsCommon.bearingFromAtoB(A, B)\\\
        local dist = dcsCommon.dist(A, B)\\\
        local deviation = bearingRad + yDeg * 0.0174533\\\
        local newDist = dist * xPer/100\\\
        local newPoint = dcsCommon.pointInDirectionOfPointXYY(deviation, newDist, A)\\\
        return newPoint\\\
    end\\\
\\\
    function dcsCommon.rotatePointAroundOriginRad(inX, inY, angle) -- angle in degrees\\\
        local c = math.cos(angle)\\\
        local s = math.sin(angle)\\\
        local px\\\
        local py \\\
        px = inX * c - inY * s\\\
        py = inX * s + inY * c\\\
        return px, py        \\\
    end\\\
    \\\
    function dcsCommon.rotatePointAroundOrigin(inX, inY, angle) -- angle in degrees\\\
        local rads =  3.14152 / 180 -- convert to radiants. \\\
        angle = angle * rads -- turns into rads\\\
        local px, py = dcsCommon.rotatePointAroundOriginRad(inX, inY, angle)\\\
        return px, py        \\\
    end\\\
    \\\
    function dcsCommon.rotatePointAroundPointRad(x, y, px, py, angle)\\\
        x = x - px \\\
        y = y - py\\\
        x, y = dcsCommon.rotatePointAroundOriginRad(x, y, angle)\\\
        x = x + px \\\
        y = y + py \\\
        return x, y\\\
    end\\\
\\\
    function dcsCommon.rotatePointAroundPointDeg(x, y, px, py, degrees)\\\
        x, y = dcsCommon.rotatePointAroundPointRad(x, y, px, py, degrees * 3.14152 / 180)\\\
        return x, y\\\
    end\\\
\\\
    -- rotates a Vec3-base inPoly on XZ pane around inPoint on XZ pane\\\
     function dcsCommon.rotatePoly3AroundVec3Rad(inPoly, inPoint, rads)\\\
        local outPoly = {}\\\
        for idx, aVertex in pairs(inPoly) do \\\
            local x, z = dcsCommon.rotatePointAroundPointRad(aVertex.x, aVertex.z, inPoint.x, inPoint.z, rads)        \\\
            local v3 = {x = x, y = aVertex.y, z = z}\\\
            outPoly[idx] = v3\\\
        end \\\
        return outPoly \\\
    end\\\
\\\
    function dcsCommon.rotateUnitData(theUnit, degrees, cx, cz)\\\
        if not cx then cx = 0 end\\\
        if not cz then cz = 0 end\\\
        local cy = cz \\\
        \\\
        local rads = degrees *  3.14152 / 180\\\
        do\\\
            theUnit.x = theUnit.x - cx -- MOVE TO ORIGIN OF ROTATION\\\
            theUnit.y = theUnit.y - cy                 \\\
            theUnit.x, theUnit.y = dcsCommon.rotatePointAroundOrigin(theUnit.x, theUnit.y, degrees)\\\
            theUnit.x = theUnit.x + cx -- MOVE BACK \\\
            theUnit.y = theUnit.y + cy                 \\\
\\\
            -- may also want to increase heading by degrees\\\
            theUnit.heading = theUnit.heading + rads \\\
        end\\\
    end\\\
    \\\
\\\
    function dcsCommon.rotateGroupData(theGroup, degrees, cx, cz)\\\
        if not cx then cx = 0 end\\\
        if not cz then cz = 0 end\\\
        local cy = cz \\\
        \\\
        local rads = degrees *  3.14152 / 180\\\
        -- turns all units in group around the group's center by degrees.\\\
        -- may also need to turn individual units by same amount\\\
        for i, theUnit in pairs (theGroup.units) do\\\
            theUnit.x = theUnit.x - cx -- MOVE TO ORIGIN OF ROTATION\\\
            theUnit.y = theUnit.y - cy                 \\\
            theUnit.x, theUnit.y = dcsCommon.rotatePointAroundOrigin(theUnit.x, theUnit.y, degrees)\\\
            theUnit.x = theUnit.x + cx -- MOVE BACK \\\
            theUnit.y = theUnit.y + cy                 \\\
\\\
            -- may also want to increase heading by degrees\\\
            theUnit.heading = theUnit.heading + rads \\\
            if theUnit.psi then \\\
                theUnit.psi = -theUnit.heading \\\
            end\\\
        end\\\
    end\\\
\\\
    function dcsCommon.offsetGroupData(theGroup, dx, dy)\\\
        -- add dx and dy to group's and all unit's coords\\\
        for i, theUnit in pairs (theGroup.units) do \\\
            theUnit.x = theUnit.x + dx\\\
            theUnit.y = theUnit.y + dy\\\
        end\\\
        \\\
        theGroup.x = theGroup.x + dx\\\
        theGroup.y = theGroup.y + dy \\\
    end\\\
    \\\
    function dcsCommon.moveGroupDataTo(theGroup, xAbs, yAbs)\\\
        local dx = xAbs-theGroup.x\\\
        local dy = yAbs-theGroup.y\\\
        dcsCommon.offsetGroupData(theGroup, dx, dy)\\\
    end\\\
    \\\
    -- static objectr shapes and types are defined here\\\
    -- https://github.com/mrSkortch/DCS-miscScripts/tree/master/ObjectDB/Statics\\\
    \\\
    function dcsCommon.createStaticObjectData(name, objType, heading, dead, cargo, mass)\\\
        local staticObj = {}\\\
        if not heading then heading = 0 end \\\
        if not dead then dead = false end \\\
        if not cargo then cargo = false end \\\
        objType = dcsCommon.trim(objType) \\\
        \\\
        staticObj.heading = heading\\\
        -- staticObj.groupId = 0\\\
        -- staticObj.shape_name = shape -- e.g. H-Windsock_RW\\\
        staticObj.type = objType  -- e.g. Windsock\\\
        -- [\\\"unitId\\\"] = 3,\\\
        staticObj.rate = 1 -- score when killed\\\
        staticObj.name = name\\\
        -- staticObj.category = \\\"Fortifications\\\",\\\
        staticObj.y = 0\\\
        staticObj.x = 0\\\
        staticObj.dead = dead\\\
        staticObj.canCargo = cargo -- to cargo\\\
        if cargo then \\\
            if not mass then mass = 1234 end \\\
            staticObj.mass = mass -- to cargo\\\
        end\\\
        return staticObj\\\
    end\\\
    \\\
    function dcsCommon.createStaticObjectDataAt(loc, name, objType, heading, dead)\\\
        local theData = dcsCommon.createStaticObjectData(name, objType, heading, dead)\\\
        theData.x = loc.x\\\
        theData.y = loc.z \\\
        return theData\\\
    end\\\
    \\\
    function dcsCommon.createStaticObjectForCoalitionAtLocation(theCoalition, loc, name, objType, heading, dead) \\\
        if not heading then heading = math.random(360) * 3.1415 / 180 end\\\
        local theData = dcsCommon.createStaticObjectDataAt(loc, name, objType, heading, dead)\\\
        local theStatic = coalition.addStaticObject(theCoalition, theData) -- warning! coalition is not country!\\\
        return theStatic\\\
    end\\\
    \\\
    function dcsCommon.createStaticObjectForCoalitionInRandomRing(theCoalition, objType, x, z, innerRadius, outerRadius, heading, alive) \\\
        if not outerRadius then outerRadius = innerRadius end\\\
        if not heading then heading = math.random(360) * 3.1415 / 180 end\\\
        local dead = not alive\\\
        local p = dcsCommon.randomPointInCircle(outerRadius, innerRadius, x, z)\\\
        local theData = dcsCommon.createStaticObjectData(dcsCommon.uuid(\\\"static\\\"), objType, heading, dead)\\\
        theData.x = p.x\\\
        theData.y = p.z \\\
        \\\
        local theStatic = coalition.addStaticObject(theCoalition, theData) -- warning! coalition is not country \\\
        return theStatic, p.x, p.z \\\
    end\\\
    \\\
    \\\
    \\\
    function dcsCommon.linkStaticDataToUnit(theStatic, theUnit, dx, dy, heading)\\\
        if not theStatic then \\\
            trigger.action.OutText(\\\"+++dcsC: NIL theStatic on linkStatic!\\\", 30)\\\
            return \\\
        end\\\
        -- NOTE: we may get current heading and subtract/add \\\
        -- to original heading \\\
        local rotX, rotY = dcsCommon.rotatePointAroundOrigin(dx, dy, -heading)\\\
        \\\
        if not theUnit then return end\\\
        if not theUnit:isExist() then return end \\\
        theStatic.linkOffset = true \\\
        theStatic.linkUnit = theUnit:getID()\\\
        local unitPos = theUnit:getPoint()\\\
        local offsets = {}\\\
        offsets.x = rotX  \\\
        offsets.y = rotY \\\
        offsets.angle = 0\\\
        theStatic.offsets = offsets\\\
    end\\\
    \\\
    function dcsCommon.offsetStaticData(theStatic, dx, dy)\\\
        theStatic.x = theStatic.x + dx\\\
        theStatic.y = theStatic.y + dy\\\
        -- now check if thre is a route (for linked objects)\\\
        if theStatic.route then \\\
            -- access points[1] x and y and copy from main\\\
            theStatic.route.points[1].x = theStatic.x\\\
            theStatic.route.points[1].y = theStatic.y\\\
        end\\\
    end\\\
    \\\
    function dcsCommon.moveStaticDataTo(theStatic, x, y)\\\
        theStatic.x = x\\\
        theStatic.y = y\\\
        -- now check if thre is a route (for linked objects)\\\
        if theStatic.route then \\\
            -- access points[1] x and y and copy from main\\\
            theStatic.route.points[1].x = theStatic.x\\\
            theStatic.route.points[1].y = theStatic.y\\\
        end\\\
\\\
    end\\\
\\\
function dcsCommon.synchGroupData(inGroupData) -- update group data block by \\\
-- comparing it to spawned group and update units by x, y, heding and isExist \\\
-- modifies inGroupData!\\\
    if not inGroupData then return end \\\
    -- groupdata from game, NOT MX DATA!\\\
    -- we synch the units and their coords \\\
    local livingUnits = {}\\\
    for idx, unitData in pairs(inGroupData.units) do \\\
        local theUnit = Unit.getByName(unitData.name)\\\
        if theUnit and theUnit:isExist() and theUnit:getLife()>1 then \\\
            -- update x and y and heading\\\
            local pos = theUnit:getPoint()\\\
            unitData.unitId = theUnit:getID()\\\
            unitData.x = pos.x \\\
            unitData.y = pos.z -- !!!!\\\
            unitData.heading = dcsCommon.getUnitHeading(gUnit)\\\
            table.insert(livingUnits, unitData)\\\
        end\\\
    end\\\
    inGroupData.units = livingUnits \\\
end\\\
\\\
--\\\
--\\\
-- M I S C   M E T H O D S \\\
--\\\
--\\\
\\\
-- as arrayContainsString, except it includes wildcard matches if EITHER \\\
-- ends on \\\"*\\\"\\\
    function dcsCommon.wildArrayContainsString(theArray, theString, caseSensitive) \\\
        if not theArray then return false end\\\
        if not theString then return false end\\\
        if not caseSensitive then caseSensitive = false end \\\
        if type(theArray) ~= \\\"table\\\" then \\\
            trigger.action.outText(\\\"***wildArrayContainsString: theArray is not type table but <\\\" .. type(theArray) .. \\\">\\\", 30)\\\
        end\\\
        if not caseSensitive then theString = string.upper(theString) end \\\
        \\\
        local wildIn = dcsCommon.stringEndsWith(theString, \\\"*\\\")\\\
        if wildIn then theString = dcsCommon.removeEnding(theString, \\\"*\\\") end \\\
        for idx, theElement in pairs(theArray) do -- i = 1, #theArray do \\\
            if not caseSensitive then theElement = string.upper(theElement) end \\\
            local wildEle = dcsCommon.stringEndsWith(theElement, \\\"*\\\")\\\
            if wildEle then theElement = dcsCommon.removeEnding(theElement, \\\"*\\\") end \\\
\\\
            if wildEle and wildIn then \\\
                -- both end on wildcards, partial match for both\\\
                if dcsCommon.stringStartsWith(theElement, theString) then return true end \\\
                if dcsCommon.stringStartsWith(theString, theElement) then return true end \\\
            elseif wildEle then \\\
                -- Element is a wildcard, partial match \\\
                if dcsCommon.stringStartsWith(theString, theElement) then return true end\\\
\\\
            elseif wildIn then\\\
                -- theString is a wildcard. partial match \\\
                if dcsCommon.stringStartsWith(theElement, theString) then return true end\\\
            else\\\
                -- standard: no wildcards, full match\\\
                if theElement == theString then return true end \\\
            end\\\
            \\\
        end\\\
        return false \\\
    end\\\
\\\
\\\
    function dcsCommon.arrayContainsString(theArray, theString) \\\
        if not theArray then return false end\\\
        if not theString then return false end\\\
        if type(theArray) ~= \\\"table\\\" then \\\
            trigger.action.outText(\\\"***arrayContainsString: theArray is not type <table> but <\\\" .. type(theArray) .. \\\">\\\", 30)\\\
        end\\\
        for idx, item in pairs(theArray) do \\\
--        for i = 1, #theArray do \\\
            if item == theString then return true end \\\
        end\\\
        return false \\\
    end\\\
\\\
    function dcsCommon.arrayContainsStringCaseInsensitive(theArray, theString) -- case insensitive\\\
        if not theArray then return false end\\\
        if not theString then return false end\\\
        if type(theArray) ~= \\\"table\\\" then \\\
            trigger.action.outText(\\\"***arrayContainsStringCI: theArray is not type <table> but <\\\" .. type(theArray) .. \\\">\\\", 30)\\\
        end\\\
        theString = string.upper(theString)\\\
        for idx, item in pairs(theArray) do \\\
            if string.upper(item) == theString then return true end \\\
        end\\\
        return false \\\
    end\\\
    \\\
    function dcsCommon.splitString(inputstr, sep) \\\
        if sep == nil then\\\
            sep = \\\"%s\\\"\\\
        end\\\
        if inputstr == nil then \\\
            inputstr = \\\"\\\"\\\
        end\\\
        \\\
        local t={}\\\
        for str in string.gmatch(inputstr, \\\"([^\\\"..sep..\\\"]+)\\\") do\\\
            table.insert(t, str)\\\
        end\\\
        return t\\\
    \\\
    end\\\
    \\\
    function dcsCommon.trimFront(inputstr) \\\
        if not inputstr then return nil end \\\
        local s = inputstr\\\
        while string.len(s) > 1 and string.sub(s, 1, 1) == \\\" \\\" do \\\
            local snew = string.sub(s, 2) -- all except first\\\
            s = snew\\\
        end\\\
        return s\\\
    end\\\
    \\\
    function dcsCommon.trimBack(inputstr)\\\
        if not inputstr then return nil end \\\
        local s = inputstr\\\
        while string.len(s) > 1 and string.sub(s, -1) == \\\" \\\" do \\\
            local snew = string.sub(s, 1, -2) -- all except last\\\
            s = snew\\\
        end\\\
        return s\\\
    end\\\
    \\\
    function dcsCommon.trim(inputstr) \\\
        local t1 = dcsCommon.trimFront(inputstr)\\\
        local t2 = dcsCommon.trimBack(t1)\\\
        return t2\\\
    end\\\
    \\\
    function dcsCommon.trimArray(theArray)\\\
        local trimmedArray = {}\\\
        for idx, element in pairs(theArray) do \\\
            local tel = dcsCommon.trim(element)\\\
            table.insert(trimmedArray, tel)\\\
        end\\\
        return trimmedArray\\\
    end\\\
    \\\
    function dcsCommon.string2Array(inString, deli, uCase)\\\
        if not inString then return {} end \\\
        if not deli then return {} end \\\
        if not uCase then uCase = false end\\\
        if uCase then inString = string.upper(inString) end\\\
        inString = dcsCommon.trim(inString)\\\
        if dcsCommon.containsString(inString, deli) then \\\
            local a = dcsCommon.splitString(inString, deli)\\\
            a = dcsCommon.trimArray(a)\\\
            return a \\\
        else \\\
            return {inString}\\\
        end\\\
    end\\\
    \\\
    function dcsCommon.array2string(inArray, deli)\\\
        if not deli then deli = \\\", \\\" end\\\
        if type(inArray) ~= \\\"table\\\" then return \\\"<err in array2string: not an array>\\\" end\\\
        local s = \\\"\\\"\\\
        local count = 0\\\
        for idx, ele in pairs(inArray) do\\\
            if count > 0 then s = s .. deli .. \\\" \\\" end\\\
            s = s .. ele\\\
            count = count + 1\\\
        end\\\
        return s\\\
    end\\\
    \\\
    function dcsCommon.stripLF(theString)\\\
        return theString:gsub(\\\"[\\\\r\\\\n]\\\", \\\"\\\")\\\
    end\\\
    \\\
    function dcsCommon.removeBlanks(theString)\\\
        return theString:gsub(\\\"%s\\\", \\\"\\\")\\\
    end\\\
    \\\
    function dcsCommon.stringIsPositiveNumber(theString)\\\
        -- only full integer positive numbers supported \\\
        if not theString then return false end \\\
--        if theString == \\\"\\\" then return false end \\\
        for i = 1, #theString do \\\
            local c = theString:sub(i,i)\\\
            if c < \\\"0\\\" or c > \\\"9\\\" then return false end \\\
        end\\\
        return true \\\
    end\\\
    \\\
    function dcsCommon.stringStartsWithDigit(theString)\\\
        if #theString < 1 then return false end \\\
        local c = string.sub(theString, 1, 1) \\\
        return c >= \\\"0\\\" and c <= \\\"9\\\" \\\
    end\\\
    \\\
    function dcsCommon.stringStartsWithLetter(theString)\\\
        if #theString < 1 then return false end \\\
        local c = string.sub(theString, 1, 1)\\\
        if c >= \\\"a\\\" and c <= \\\"z\\\" then return true end  \\\
        if c >= \\\"A\\\" and c <= \\\"Z\\\" then return true end \\\
        return false \\\
    end\\\
    \\\
    function dcsCommon.stringStartsWith(theString, thePrefix, caseInsensitive)\\\
        if not theString then return false end \\\
        if not thePrefix then return false end \\\
        if not caseInsensitive then caseInsensitive = false end \\\
        \\\
        if caseInsensitive then \\\
            theString = string.upper(theString)\\\
            thePrefix = string.upper(thePrefix)\\\
        end\\\
        -- superseded: string.find (s, pattern [, init [, plain]]) solves the problem  \\\
        local i, j = string.find(theString, thePrefix, 1, true)\\\
        return (i == 1)\\\
    end\\\
    \\\
    function dcsCommon.removePrefix(theString, thePrefix)\\\
        if not dcsCommon.stringStartsWith(theString, thePrefix) then \\\
            return theString\\\
        end;\\\
        return theString:sub(1 + #thePrefix)\\\
    end\\\
    \\\
    function dcsCommon.stringEndsWith(theString, theEnding)\\\
        return theEnding == \\\"\\\" or theString:sub(-#theEnding) == theEnding\\\
    end\\\
    \\\
    function dcsCommon.removeEnding(theString, theEnding) \\\
        if not dcsCommon.stringEndsWith(theString, theEnding) then \\\
            return theString\\\
        end\\\
        return theString:sub(1, #theString - #theEnding)\\\
    end\\\
    \\\
    function dcsCommon.containsString(inString, what, caseSensitive)\\\
        if (not caseSensitive) then \\\
            inString = string.upper(inString)\\\
            what = string.upper(what)\\\
        end\\\
        if inString == what then return true end -- when entire match \\\
        return string.find(inString, what, 1, true) -- 1, true means start at 1, plaintext\\\
    end\\\
    \\\
    function dcsCommon.bool2Text(theBool) \\\
        if not theBool then theBool = false end \\\
        if theBool then return \\\"true\\\" end \\\
        return \\\"false\\\"\\\
    end\\\
    \\\
    function dcsCommon.bool2YesNo(theBool)\\\
        if not theBool then \\\
            theBool = false\\\
            return \\\"NIL\\\"\\\
        end \\\
        if theBool then return \\\"yes\\\" end \\\
        return \\\"no\\\"\\\
    end\\\
    \\\
    function dcsCommon.bool2Num(theBool)\\\
        if not theBool then theBool = false end \\\
        if theBool then return 1 end \\\
        return 0\\\
    end\\\
\\\
    function dcsCommon.point2text(p, intsOnly) \\\
        if not intsOnly then intsOnly = false end \\\
        if not p then return \\\"<!NIL!>\\\" end \\\
        local t = \\\"[x=\\\"\\\
        if intsOnly then \\\
            if p.x then t = t .. math.floor(p.x) .. \\\", \\\" else t = t .. \\\"<nil>, \\\" end \\\
            if p.y then t = t .. \\\"y=\\\" .. math.floor(p.y) .. \\\", \\\" else t = t .. \\\"y=<nil>, \\\" end \\\
            if p.z then t = t .. \\\"z=\\\" .. math.floor(p.z) .. \\\"]\\\" else t = t .. \\\"z=<nil>]\\\" end \\\
        else \\\
            if p.x then t = t .. p.x .. \\\", \\\" else t = t .. \\\"<nil>, \\\" end \\\
            if p.y then t = t .. \\\"y=\\\" .. p.y .. \\\", \\\" else t = t .. \\\"y=<nil>, \\\" end \\\
            if p.z then t = t .. \\\"z=\\\" .. p.z .. \\\"]\\\" else t = t .. \\\"z=<nil>]\\\" end \\\
        end\\\
        return t \\\
    end\\\
\\\
    function dcsCommon.string2GroupCat(inString)\\\
\\\
        if not inString then return 2 end -- default ground \\\
        inString = inString:lower()\\\
        inString = dcsCommon.trim(inString)\\\
\\\
        local catNum = tonumber(inString)\\\
        if catNum then \\\
            if catNum < 0 then catNum = 0 end \\\
            if catNum > 4 then catNum = 4 end \\\
            return catNum \\\
        end\\\
    \\\
        catNum = 2 -- ground default \\\
        if dcsCommon.stringStartsWith(inString, \\\"grou\\\") then catNum = 2 end \\\
        if dcsCommon.stringStartsWith(inString, \\\"air\\\") then catNum = 0 end\\\
        if dcsCommon.stringStartsWith(inString, \\\"hel\\\") then catNum = 1 end\\\
        if dcsCommon.stringStartsWith(inString, \\\"shi\\\") then catNum = 3 end\\\
        if dcsCommon.stringStartsWith(inString, \\\"trai\\\") then catNum = 4 end\\\
\\\
        return catNum\\\
    end\\\
\\\
    function dcsCommon.string2ObjectCat(inString)\\\
\\\
        if not inString then return 3 end -- default static \\\
        inString = inString:lower()\\\
        inString = dcsCommon.trim(inString)\\\
\\\
        local catNum = tonumber(inString)\\\
        if catNum then \\\
            if catNum < 0 then catNum = 0 end \\\
            if catNum > 6 then catNum = 6 end \\\
            return catNum \\\
        end\\\
    \\\
        catNum = 3 -- static default \\\
        if dcsCommon.stringStartsWith(inString, \\\"uni\\\") then catNum = 1 end \\\
        if dcsCommon.stringStartsWith(inString, \\\"wea\\\") then catNum = 2 end\\\
        if dcsCommon.stringStartsWith(inString, \\\"bas\\\") then catNum = 4 end\\\
        if dcsCommon.stringStartsWith(inString, \\\"sce\\\") then catNum = 5 end\\\
        if dcsCommon.stringStartsWith(inString, \\\"car\\\") then catNum = 6 end\\\
\\\
        return catNum\\\
    end\\\
\\\
    function dcsCommon.menu2text(inMenu)\\\
        if not inMenu then return \\\"<nil>\\\" end\\\
        local s = \\\"\\\"\\\
        for n, v in pairs(inMenu) do \\\
            if type(v) == \\\"string\\\" then \\\
                if s == \\\"\\\" then s = \\\"[\\\" .. v .. \\\"]\\\"  else \\\
                    s = s .. \\\" | [\\\" .. type(v) .. \\\"]\\\" end\\\
            else \\\
                if s == \\\"\\\" then s = \\\"[<\\\" .. type(v) .. \\\">]\\\"  else\\\
                    s = s .. \\\" | [<\\\" .. type(v) .. \\\">]\\\" end\\\
            end\\\
        end\\\
        return s\\\
    end\\\
\\\
    -- recursively show the contents of a variable\\\
    function dcsCommon.dumpVar(key, value, prefix, inrecursion)\\\
        if not inrecursion then \\\
            -- output a marker to find in the log / screen\\\
            env.info(\\\"*** dcsCommon vardump START\\\")\\\
        end\\\
        if not value then value = \\\"nil\\\" end\\\
        if not prefix then prefix = \\\"\\\" end\\\
        prefix = \\\" \\\" .. prefix\\\
        if type(value) == \\\"table\\\" then \\\
            env.info(prefix .. key .. \\\": [ \\\")\\\
            -- iterate through all kvp\\\
            for k,v in pairs (value) do\\\
                dcsCommon.dumpVar(k, v, prefix, true)\\\
            end\\\
            env.info(prefix .. \\\" ] - end \\\" .. key)\\\
            \\\
        elseif type(value) == \\\"boolean\\\" then \\\
            local b = \\\"false\\\"\\\
            if value then b = \\\"true\\\" end\\\
            env.info(prefix .. key .. \\\": \\\" .. b)\\\
            \\\
        else -- simple var, show contents, ends recursion\\\
            env.info(prefix .. key .. \\\": \\\" .. value)\\\
        end\\\
        \\\
        if not inrecursion then \\\
            -- output a marker to find in the log / screen\\\
            trigger.action.outText(\\\"=== dcsCommon vardump end\\\", 30)\\\
            env.info(\\\"=== dcsCommon vardump end\\\")\\\
        end\\\
    end\\\
    \\\
    function dcsCommon.dumpVar2Str(key, value, prefix, inrecursion)\\\
        -- dumps to screen, not string \\\
        if not inrecursion then \\\
            -- output a marker to find in the log / screen\\\
            trigger.action.outText(\\\"*** dcsCommon vardump START\\\",30)\\\
        end\\\
        if not value then value = \\\"nil\\\" end\\\
        if not prefix then prefix = \\\"\\\" end\\\
        prefix = \\\" \\\" .. prefix\\\
        if getmetatable(value) then \\\
            if type(value) == \\\"string\\\" then \\\
            else \\\
                trigger.action.outText(prefix .. key .. (\\\" .. type(value) .. \\\") .. \\\" HAS META\\\", 30)\\\
            end\\\
        end\\\
        if type(value) == \\\"table\\\" then \\\
            trigger.action.outText(prefix .. key .. \\\": [ \\\", 30)\\\
            -- iterate through all kvp\\\
            for k,v in pairs (value) do\\\
                dcsCommon.dumpVar2Str(k, v, prefix, true)\\\
            end\\\
            trigger.action.outText(prefix .. \\\" ] - end \\\" .. key, 30)\\\
            \\\
        elseif type(value) == \\\"boolean\\\" then \\\
            local b = \\\"false\\\"\\\
            if value then b = \\\"true\\\" end\\\
            trigger.action.outText(prefix .. key .. \\\": \\\" .. b, 30)\\\
            \\\
        else -- simple var, show contents, ends recursion\\\
            trigger.action.outText(prefix .. key .. \\\": \\\" .. value, 30)\\\
        end\\\
        \\\
        if not inrecursion then \\\
            -- output a marker to find in the log / screen\\\
            trigger.action.outText(\\\"=== dcsCommon vardump end\\\", 30)\\\
        end\\\
    end\\\
        \\\
    function dcsCommon.numberUUID()\\\
        dcsCommon.simpleUUID = dcsCommon.simpleUUID + 1\\\
        return dcsCommon.simpleUUID\\\
    end\\\
\\\
    function dcsCommon.uuid(prefix)\\\
        --dcsCommon.uuIdent = dcsCommon.uuIdent + 1\\\
        if not prefix then prefix = dcsCommon.uuidStr end\\\
        return prefix .. \\\"-\\\" .. dcsCommon.numberUUID() -- dcsCommon.uuIdent\\\
    end\\\
    \\\
    function dcsCommon.event2text(id) \\\
        if not id then return \\\"error\\\" end\\\
        if id == 0 then return \\\"invalid\\\" end\\\
        -- translate the event id to text\\\
        local events = {\\\"shot\\\", \\\"hit\\\", \\\"takeoff\\\", \\\"land\\\",\\\
                        \\\"crash\\\", \\\"eject\\\", \\\"refuel\\\", \\\"dead\\\", -- 8\\\
                        \\\"pilot dead\\\", \\\"base captured\\\", \\\"mission start\\\", \\\"mission end\\\", -- 12\\\
                        \\\"took control\\\", \\\"refuel stop\\\", \\\"birth\\\", \\\"human failure\\\", -- 16 \\\
                        \\\"det. failure\\\", \\\"engine start\\\", \\\"engine stop\\\", \\\"player enter unit\\\", -- 20\\\
                        \\\"player leave unit\\\", \\\"player comment\\\", \\\"start shoot\\\", \\\"end shoot\\\", -- 24\\\
                        \\\"mark add\\\", \\\"mark changed\\\", \\\"mark removed\\\", \\\"kill\\\", -- 28 \\\
                        \\\"score\\\", \\\"unit lost\\\", \\\"land after eject\\\", \\\"Paratrooper land\\\", -- 32 \\\
                        \\\"chair discard after eject\\\", \\\"weapon add\\\", \\\"trigger zone\\\", \\\"landing quality mark\\\", -- 36\\\
                        \\\"BDA\\\", \\\"AI Abort Mission\\\", \\\"DayNight\\\", \\\"Flight Time\\\", -- 40\\\
                        \\\"Pilot Suicide\\\", \\\"player cap airfield\\\", \\\"emergency landing\\\", \\\"unit create task\\\", -- 44\\\
                        \\\"unit delete task\\\", \\\"Simulation start\\\", \\\"weapon rearm\\\", \\\"weapon drop\\\", -- 48\\\
                        \\\"unit task timeout\\\", \\\"unit task stage\\\", -- 50\\\
                        \\\"subtask score\\\", \\\"extra score\\\", \\\"mission restart\\\", \\\"winner\\\", \\\
                        \\\"postponed takeoff\\\", \\\"postponed land\\\", -- 56\\\
                        \\\"max\\\"}\\\
        if id > #events then return \\\"Unknown (ID=\\\" .. id .. \\\")\\\" end\\\
        return events[id]\\\
    end\\\
\\\
    function dcsCommon.smokeColor2Text(smokeColor)\\\
        if (smokeColor == 0) then return \\\"Green\\\" end\\\
        if (smokeColor == 1) then return \\\"Red\\\" end\\\
        if (smokeColor == 2) then return \\\"White\\\" end\\\
        if (smokeColor == 3) then return \\\"Orange\\\" end\\\
        if (smokeColor == 4) then return \\\"Blue\\\" end\\\
        \\\
        return (\\\"unknown: \\\" .. smokeColor)\\\
    end\\\
    \\\
    function dcsCommon.flareColor2Text(flareColor)\\\
        if (flareColor == 0) then return \\\"Green\\\" end\\\
        if (flareColor == 1) then return \\\"Red\\\" end\\\
        if (flareColor == 2) then return \\\"White\\\" end\\\
        if (flareColor == 3) then return \\\"Yellow\\\" end\\\
        if (flareColor < 0) then return \\\"Random\\\" end \\\
        return (\\\"unknown: \\\" .. flareColor)\\\
    end\\\
    \\\
    function dcsCommon.smokeColor2Num(smokeColor)\\\
        if not smokeColor then smokeColor = \\\"green\\\" end \\\
        if type(smokeColor) ~= \\\"string\\\" then return 0 end \\\
        smokeColor = smokeColor:lower()\\\
        if (smokeColor == \\\"green\\\") then return 0 end \\\
        if (smokeColor == \\\"red\\\") then return 1 end \\\
        if (smokeColor == \\\"white\\\") then return 2 end \\\
        if (smokeColor == \\\"orange\\\") then return 3 end \\\
        if (smokeColor == \\\"blue\\\") then return 4 end \\\
        return 0\\\
    end\\\
\\\
    function dcsCommon.flareColor2Num(flareColor)\\\
        if not flareColor then flareColor = \\\"green\\\" end \\\
        if type(flareColor) ~= \\\"string\\\" then return 0 end \\\
        flareColor = flareColor:lower()\\\
        if (flareColor == \\\"green\\\") then return 0 end \\\
        if (flareColor == \\\"red\\\") then return 1 end \\\
        if (flareColor == \\\"white\\\") then return 2 end \\\
        if (flareColor == \\\"yellow\\\") then return 3 end \\\
        if (flareColor == \\\"random\\\") then return -1 end \\\
        if (flareColor == \\\"rnd\\\") then return -1 end \\\
        return 0\\\
    end\\\
\\\
    \\\
    function dcsCommon.markPointWithSmoke(p, smokeColor)\\\
        if not smokeColor then smokeColor = 0 end \\\
        local x = p.x \\\
        local z = p.z -- do NOT change the point directly\\\
        -- height-correct\\\
        local y = land.getHeight({x = x, y = z})\\\
        local newPoint= {x = x, y = y + 2, z = z}\\\
        trigger.action.smoke(newPoint, smokeColor)\\\
    end\\\
\\\
-- based on buzzer1977's idea, channel is number, eg in 74X, channel is 74, mode is \\\"X\\\"\\\
    function dcsCommon.tacan2freq(channel, mode)    \\\
        if not mode then mode = \\\"X\\\" end \\\
        if not channel then channel = 1 end \\\
        if type(mode) ~= \\\"string\\\" then mode = \\\"X\\\" end \\\
        mode = mode:upper()\\\
        local offset = 1000000 * channel\\\
        if channel < 64 then \\\
            if mode == \\\"Y\\\" then\\\
                return 1087000000 + offset\\\
            end\\\
            return 961000000 + offset -- mode x\\\
        end\\\
    \\\
        if mode == \\\"Y\\\" then\\\
            return 961000000 + offset\\\
        end\\\
        return 1087000000 + offset -- mode x\\\
    end\\\
    \\\
    function dcsCommon.processHMS(msg, delta)\\\
        local rS = math.floor(delta)\\\
        local remainS = tostring(rS)\\\
        local rM = math.floor(delta/60)\\\
        local remainM = tostring(rM)\\\
        local rH = math.floor(delta/3600)\\\
        local remainH = tostring(rH)\\\
        local hmsH = remainH \\\
        if rH < 10 then hmsH = \\\"0\\\" .. hmsH end \\\
        \\\
        local hmsCount = delta - (rH * 3600) -- mins left \\\
        local mins = math.floor (hmsCount / 60)\\\
        local hmsM = tostring(mins)\\\
        if mins < 10 then hmsM = \\\"0\\\" .. hmsM end \\\
        \\\
        hmsCount = hmsCount - (mins * 60) \\\
        local secs = math.floor(hmsCount)\\\
        local hmsS = tostring(secs)\\\
        if secs < 10 then hmsS = \\\"0\\\" .. hmsS end \\\
        \\\
        msg = string.gsub(msg, \\\"<s>\\\", remainS)\\\
        msg = string.gsub(msg, \\\"<m>\\\", remainM)\\\
        msg = string.gsub(msg, \\\"<h>\\\", remainH)\\\
        \\\
        msg = string.gsub(msg, \\\"<:s>\\\", hmsS)\\\
        msg = string.gsub(msg, \\\"<:m>\\\", hmsM)\\\
        msg = string.gsub(msg, \\\"<:h>\\\", hmsH)\\\
        \\\
        return msg \\\
    end\\\
    \\\
    function dcsCommon.nowString()\\\
        local absSecs = timer.getAbsTime()-- + env.mission.start_time\\\
        while absSecs > 86400 do \\\
            absSecs = absSecs - 86400 -- subtract out all days \\\
        end\\\
        return dcsCommon.processHMS(\\\"<:h>:<:m>:<:s>\\\", absSecs)\\\
    end\\\
    \\\
    function dcsCommon.str2num(inVal, default) \\\
        if not default then default = 0 end\\\
        if not inVal then return default end\\\
        if type(inVal) == \\\"number\\\" then return inVal end                 \\\
        local num = nil\\\
        if type(inVal) == \\\"string\\\" then num = tonumber(inVal) end\\\
        if not num then return default end\\\
        return num\\\
    end\\\
    \\\
    function dcsCommon.stringRemainsStartingWith(theString, startingWith)\\\
        -- find the first position where startingWith starts \\\
        local pos = theString:find(startingWith)\\\
        if not pos then return theString end \\\
        -- now return the entire remainder of the string from pos \\\
        local nums = theString:len() - pos + 1\\\
        return theString:sub(-nums)\\\
    end\\\
\\\
--\\\
--\\\
-- V E C T O R   M A T H \\\
--\\\
--\\\
\\\
function dcsCommon.vAdd(a, b) \\\
    local r = {}\\\
    if not a then a = {x = 0, y = 0, z = 0} end\\\
    if not b then b = {x = 0, y = 0, z = 0} end\\\
    r.x = a.x + b.x \\\
    r.y = a.y + b.y \\\
    if a.z and b.z then \\\
        r.z = a.z + b.z \\\
    end \\\
    return r \\\
end\\\
\\\
function dcsCommon.vSub(a, b) \\\
    local r = {}\\\
    if not a then a = {x = 0, y = 0, z = 0} end\\\
    if not b then b = {x = 0, y = 0, z = 0} end\\\
    r.x = a.x - b.x \\\
    r.y = a.y - b.y \\\
    if a.z and b.z then \\\
        r.z = a.z - b.z \\\
    end \\\
    return r \\\
end\\\
\\\
function dcsCommon.vMultScalar(a, f) \\\
    local r = {}\\\
    if not a then a = {x = 0, y = 0, z = 0} end\\\
    if not f then f = 0 end\\\
    r.x = a.x * f \\\
    r.y = a.y * f \\\
    if a.z then \\\
        r.z = a.z * f\\\
    end        \\\
    return r \\\
end\\\
\\\
function dcsCommon.vLerp (a, b, t)\\\
    if not a then a = {x = 0, y = 0, z = 0} end\\\
    if not b then b = {x = 0, y = 0, z = 0} end\\\
    \\\
    local d = dcsCommon.vSub(b, a)\\\
    local dt = dcsCommon.vMultScalar(d, t)\\\
    local r = dcsCommon.vAdd(a, dt)\\\
    return r\\\
end\\\
\\\
function dcsCommon.mag(x, y, z) \\\
    if not x then x = 0 end\\\
    if not y then y = 0 end \\\
    if not z then z = 0 end \\\
    \\\
    return (x * x + y * y + z * z)^0.5\\\
end\\\
\\\
function dcsCommon.vMag(a) \\\
    if not a then return 0 end \\\
    if not a.x then a.x = 0 end \\\
    if not a.y then a.y = 0 end \\\
    if not a.z then a.z = 0 end\\\
    return dcsCommon.mag(a.x, a.y, a.z) \\\
end\\\
\\\
function dcsCommon.magSquare(x, y, z) \\\
    if not x then x = 0 end\\\
    if not y then y = 0 end \\\
    if not z then z = 0 end \\\
    \\\
    return (x * x + y * y + z * z)\\\
end\\\
\\\
function dcsCommon.vNorm(a) \\\
    if not a then return {x = 0, y = 0, z = 0} end \\\
    m = dcsCommon.vMag(a)\\\
    if m <= 0 then return {x = 0, y = 0, z = 0} end \\\
    local r = {}\\\
    r.x = a.x / m \\\
    r.y = a.y / m \\\
    r.z = a.z / m\\\
    return r \\\
end\\\
\\\
function dcsCommon.dot (a, b) \\\
    if not a then a = {} end \\\
    if not a.x then a.x = 0 end \\\
    if not a.y then a.y = 0 end \\\
    if not a.z then a.z = 0 end\\\
    if not b then b = {} end \\\
    if not b.x then b.x = 0 end \\\
    if not b.y then b.y = 0 end \\\
    if not b.z then b.z = 0 end \\\
    \\\
    return a.x * b.x + a.y * b.y + a.z * b.z \\\
end\\\
--\\\
-- UNIT MISC\\\
-- \\\
function dcsCommon.isSceneryObject(theUnit)\\\
    if not theUnit then return false end\\\
    return Object.getCategory(theUnit) == 5 \\\
--    return theUnit.getCoalition == nil -- scenery objects do not return a coalition \\\
end\\\
\\\
function dcsCommon.isTroopCarrierType(theType, carriers)\\\
    if not theType then return false end \\\
    if not carriers then carriers = dcsCommon.troopCarriers \\\
    end \\\
    -- remember that arrayContainsString is case INsensitive by default \\\
    if dcsCommon.wildArrayContainsString(carriers, theType) then \\\
        -- may add additional tests before returning true\\\
        return true\\\
    end\\\
    \\\
    -- see if user wanted 'any' or 'all' supported\\\
    if dcsCommon.arrayContainsString(carriers, \\\"any\\\") then \\\
        return true \\\
    end \\\
    \\\
    if dcsCommon.arrayContainsString(carriers, \\\"all\\\") then \\\
        return true \\\
    end \\\
    \\\
    return false\\\
end\\\
\\\
function dcsCommon.isTroopCarrier(theUnit, carriers)\\\
    -- return true if conf can carry troups\\\
    if not theUnit then return false end\\\
\\\
    -- see if carriers contains \\\"helo\\\" and theUnit is a helo \\\
    if dcsCommon.arrayContainsString(carriers, \\\"helo\\\") or dcsCommon.arrayContainsString(carriers, \\\"helos\\\")then\\\
        local grp = theUnit:getGroup() \\\
        if grp:getCategory() == 1 then -- NOT category bug prone, is a group check \\\
            return true \\\
        end\\\
    end    \\\
    \\\
    local uType = theUnit:getTypeName()\\\
    return dcsCommon.isTroopCarrierType(uType, carriers) \\\
end\\\
\\\
function dcsCommon.getPlayerUnit(playerName) \\\
    if not playerName then return nil end \\\
    for idx, theSide in pairs(dcsCommon.coalitionSides) do\\\
        local thePlayers = coalition.getPlayers(theSide) \\\
        for idy, theUnit in pairs (thePlayers) do \\\
            if theUnit and theUnit:isExist() and theUnit.getPlayerName \\\
            and theUnit:getPlayerName() == playerName then \\\
                return theUnit\\\
            end\\\
        end\\\
    end\\\
    return nil\\\
end \\\
\\\
function dcsCommon.getAllExistingPlayerUnitsRaw()\\\
    local apu = {}\\\
    for idx, theSide in pairs(dcsCommon.coalitionSides) do\\\
        local thePlayers = coalition.getPlayers(theSide) \\\
        for idy, theUnit in pairs (thePlayers) do \\\
            if theUnit and theUnit:isExist() then \\\
                table.insert(apu, theUnit)\\\
            end\\\
        end\\\
    end\\\
    return apu \\\
end\\\
\\\
function dcsCommon.getAllExistingPlayersAndUnits() -- units indexed by player name\\\
-- designed to replace cases for cfxPlayer.getAllPlayer invocations\\\
    local apu = {}\\\
    for idx, theSide in pairs(dcsCommon.coalitionSides) do\\\
        local thePlayers = coalition.getPlayers(theSide) \\\
        for idy, theUnit in pairs (thePlayers) do \\\
            if theUnit and theUnit:isExist() then \\\
                local pName = theUnit:getPlayerName()\\\
                apu[pName] = theUnit\\\
            end\\\
        end\\\
    end\\\
    return apu \\\
end\\\
\\\
function dcsCommon.getUnitAlt(theUnit)\\\
    if not theUnit then return 0 end\\\
    if not Unit.isExist(theUnit) then return 0 end -- safer \\\
    local p = theUnit:getPoint()\\\
    return p.y \\\
end\\\
\\\
function dcsCommon.getUnitAGL(theUnit)\\\
    if not theUnit then return 0 end\\\
    if not Unit.isExist(theUnit) then return 0 end -- safe fix\\\
    local p = theUnit:getPoint()\\\
    local alt = p.y \\\
    local loc = {x = p.x, y = p.z}\\\
    local landElev = land.getHeight(loc)\\\
    return alt - landElev\\\
end \\\
\\\
function dcsCommon.getUnitSpeed(theUnit)\\\
    if not theUnit then return 0 end\\\
    if not Unit.isExist(theUnit) then return 0 end \\\
    local v = theUnit:getVelocity()\\\
    return dcsCommon.mag(v.x, v.y, v.z)\\\
end\\\
\\\
-- closing velocity of u1 and u2, seen from u1\\\
function dcsCommon.getClosingVelocity(u1, u2)\\\
    if not u1 then return 0 end \\\
    if not u2 then return 0 end \\\
    if not u1:isExist() then return 0 end \\\
    if not u2:isExist() then return 0 end \\\
    local v1 = u1:getVelocity()\\\
    local v2 = u2:getVelocity()\\\
    local dV = dcsCommon.vSub(v1,v2)\\\
    local a = u1:getPoint()\\\
    local b = u2:getPoint() \\\
    local aMinusB = dcsCommon.vSub(a,b) -- vector from u2 to u1\\\
    local abMag = dcsCommon.vMag(aMinusB) -- distance u1 to u2 \\\
    if abMag < .0001 then return 0 end \\\
    -- project deltaV onto vector from u2 to u1 \\\
    local vClose = dcsCommon.dot(dV, aMinusB) / abMag \\\
    return vClose \\\
end\\\
\\\
function dcsCommon.getGroupAvgSpeed(theGroup)\\\
    if not theGroup then return 0 end \\\
    if not dcsCommon.isGroupAlive(theGroup) then return 0 end \\\
    local totalSpeed = 0\\\
    local cnt = 0 \\\
    local livingUnits = theGroup:getUnits()\\\
    for idx, theUnit in pairs(livingUnits) do \\\
        cnt = cnt + 1\\\
        totalSpeed = totalSpeed + dcsCommon.getUnitSpeed(theUnit)\\\
    end \\\
    if cnt == 0 then return 0 end \\\
    return totalSpeed / cnt \\\
end\\\
 \\\
function dcsCommon.getGroupMaxSpeed(theGroup)\\\
    if not theGroup then return 0 end \\\
    if not dcsCommon.isGroupAlive(theGroup) then return 0 end \\\
    local maxSpeed = 0\\\
    local livingUnits = theGroup:getUnits()\\\
    for idx, theUnit in pairs(livingUnits) do \\\
        currSpeed = dcsCommon.getUnitSpeed(theUnit)\\\
        if currSpeed > maxSpeed then maxSpeed = currSpeed end \\\
    end \\\
    return maxSpeed\\\
end \\\
\\\
function dcsCommon.getUnitHeading(theUnit)\\\
    if not theUnit then return 0 end \\\
    if not theUnit:isExist() then return 0 end \\\
    local pos = theUnit:getPosition() -- returns three vectors, p is location\\\
\\\
    local heading = math.atan2(pos.x.z, pos.x.x)\\\
    -- make sure positive only, add 360 degrees\\\
    if heading < 0 then\\\
        heading = heading + 2 * math.pi    -- put heading in range of 0 to 2*pi\\\
    end\\\
    return heading \\\
end\\\
\\\
function dcsCommon.getUnitHeadingDegrees(theUnit)\\\
    local heading = dcsCommon.getUnitHeading(theUnit)\\\
    return heading * 57.2958 -- 180 / math.pi \\\
end\\\
\\\
function dcsCommon.typeIsInfantry(theType)\\\
    local isInfantry =  \\\
                dcsCommon.containsString(theType, \\\"infantry\\\", false) or \\\
                dcsCommon.containsString(theType, \\\"paratrooper\\\", false) or\\\
                dcsCommon.containsString(theType, \\\"stinger\\\", false) or\\\
                dcsCommon.containsString(theType, \\\"manpad\\\", false) or\\\
                dcsCommon.containsString(theType, \\\"soldier\\\", false) or \\\
                dcsCommon.containsString(theType, \\\"SA-18 Igla\\\", false)\\\
    return isInfantry\\\
end\\\
\\\
function dcsCommon.unitIsInfantry(theUnit)\\\
    if not theUnit then return false end \\\
    if not theUnit:isExist() then return end\\\
    local theType = theUnit:getTypeName()\\\
    return dcsCommon.typeIsInfantry(theType)\\\
end\\\
\\\
function dcsCommon.coalition2county(inCoalition)\\\
    -- simply return UN troops for 0 neutral,\\\
    -- joint red for 1  red\\\
    -- joint blue for 2 blue \\\
    if inCoalition == 1 then return 81 end -- cjtf red\\\
    if inCoalition == 2 then return 80 end -- blue \\\
    if type(inCoalition) == \\\"string\\\" then \\\
            inCoalition = inCoalition:lower()\\\
            if inCoalition == \\\"red\\\" then return 81 end\\\
            if inCoalition == \\\"blue\\\" then return 80 end\\\
    end\\\
        \\\
    trigger.action.outText(\\\"+++dcsC: coalition2county in (\\\" .. inCoalition .. \\\") converts to UN (82)!\\\", 30)\\\
    return 82 -- UN \\\
    \\\
end\\\
\\\
function dcsCommon.coalition2Text(coa)\\\
    if not coa then return \\\"!nil!\\\" end \\\
    if coa == 0 then return \\\"NEUTRAL\\\" end \\\
    if coa == 1 then return \\\"RED\\\" end \\\
    if coa == 2 then return \\\"BLUE\\\" end \\\
    return \\\"?UNKNOWN?\\\"\\\
end\\\
\\\
function dcsCommon.latLon2Text(lat, lon)\\\
    -- inspired by mist, thanks Grimes!\\\
    -- returns two strings: lat and lon \\\
    \\\
    -- determine hemispheres by sign\\\
    local latHemi, lonHemi\\\
    if lat > 0 then latHemi = 'N' else latHemi = 'S' end\\\
    if lon > 0 then lonHemi = 'E' else lonHemi = 'W' end\\\
\\\
    -- remove sign since we have hemi\\\
    lat = math.abs(lat)\\\
    lon = math.abs(lon)\\\
\\\
    -- calc deg / mins \\\
    local latDeg = math.floor(lat)\\\
    local latMin = (lat - latDeg) * 60\\\
    local lonDeg = math.floor(lon)\\\
    local lonMin = (lon - lonDeg) * 60\\\
\\\
    -- calc seconds \\\
    local rawLatMin = latMin\\\
    latMin = math.floor(latMin)\\\
    local latSec = (rawLatMin - latMin) * 60\\\
    local rawLonMin = lonMin\\\
    lonMin = math.floor(lonMin)\\\
    local lonSec = (rawLonMin - lonMin) * 60\\\
\\\
    -- correct for rounding errors \\\
    if latSec >= 60 then\\\
        latSec = latSec - 60\\\
        latMin = latMin + 1\\\
    end\\\
    if lonSec >= 60 then\\\
        lonSec = lonSec - 60\\\
        lonMin = lonMin + 1\\\
    end\\\
\\\
    -- prepare string output \\\
    local secFrmtStr = '%06.3f'\\\
    local lat = string.format('%02d', latDeg) .. '°' .. string.format('%02d', latMin) .. \\\"'\\\" .. string.format(secFrmtStr, latSec) .. '\\\"' .. latHemi\\\
    local lon = string.format('%02d', lonDeg) .. '°' .. string.format('%02d', lonMin) .. \\\"'\\\" .. string.format(secFrmtStr, lonSec) .. '\\\"' .. lonHemi\\\
    return lat, lon  \\\
end\\\
\\\
-- get mission name. If mission file name without \\\".miz\\\"\\\
function dcsCommon.getMissionName()\\\
    local mn = net.dostring_in(\\\"gui\\\", \\\"return DCS.getMissionName()\\\")\\\
    return mn\\\
end\\\
\\\
function dcsCommon.numberArrayFromString(inString, default) -- moved from cfxZones\\\
    if not default then default = 0 end \\\
    if string.len(inString) < 1 then \\\
        trigger.action.outText(\\\"+++dcsCommon: empty numbers\\\", 30)\\\
        return {default, } \\\
    end\\\
    \\\
    local flags = {}\\\
    local rawElements = dcsCommon.splitString(inString, \\\",\\\")\\\
    -- go over all elements \\\
    for idx, anElement in pairs(rawElements) do \\\
        anElement = dcsCommon.trim(anElement)\\\
        if dcsCommon.stringStartsWithDigit(anElement) and dcsCommon.containsString(anElement, \\\"-\\\") then \\\
            -- interpret this as a range\\\
            local theRange = dcsCommon.splitString(anElement, \\\"-\\\")\\\
            local lowerBound = theRange[1]\\\
            lowerBound = tonumber(lowerBound)\\\
            local upperBound = theRange[2]\\\
            upperBound = tonumber(upperBound)\\\
            if lowerBound and upperBound then\\\
                -- swap if wrong order\\\
                if lowerBound > upperBound then \\\
                    local temp = upperBound\\\
                    upperBound = lowerBound\\\
                    lowerBound = temp \\\
                end\\\
                -- now add add numbers to flags\\\
                for f=lowerBound, upperBound do \\\
                    table.insert(flags, tostring(f))\\\
                end\\\
            else\\\
                -- bounds illegal\\\
                trigger.action.outText(\\\"+++dcsCommon: ignored range <\\\" .. anElement .. \\\"> (range)\\\", 30)\\\
            end\\\
        else\\\
            -- single number\\\
            f = dcsCommon.trim(anElement)\\\
            f = tonumber(f)\\\
            if f then \\\
                table.insert(flags, f)\\\
            end\\\
        end\\\
    end\\\
    if #flags < 1 then flags = {default, } end \\\
    return flags\\\
end \\\
\\\
function dcsCommon.flagArrayFromString(inString, verbose)\\\
    if not verbose then verbose = false end \\\
    \\\
    if verbose then \\\
        trigger.action.outText(\\\"+++flagArray: processing <\\\" .. inString .. \\\">\\\", 30)\\\
    end \\\
\\\
    if string.len(inString) < 1 then \\\
        trigger.action.outText(\\\"+++flagArray: empty flags\\\", 30)\\\
        return {} \\\
    end\\\
    \\\
    \\\
    local flags = {}\\\
    local rawElements = dcsCommon.splitString(inString, \\\",\\\")\\\
    -- go over all elements \\\
    for idx, anElement in pairs(rawElements) do \\\
        anElement = dcsCommon.trim(anElement)\\\
        if dcsCommon.stringStartsWithDigit(anElement) and  dcsCommon.containsString(anElement, \\\"-\\\") then \\\
            -- interpret this as a range\\\
            local theRange = dcsCommon.splitString(anElement, \\\"-\\\")\\\
            local lowerBound = theRange[1]\\\
            lowerBound = tonumber(lowerBound)\\\
            local upperBound = theRange[2]\\\
            upperBound = tonumber(upperBound)\\\
            if lowerBound and upperBound then\\\
                -- swap if wrong order\\\
                if lowerBound > upperBound then \\\
                    local temp = upperBound\\\
                    upperBound = lowerBound\\\
                    lowerBound = temp \\\
                end\\\
                -- now add add numbers to flags\\\
                for f=lowerBound, upperBound do \\\
                    table.insert(flags, f)\\\
\\\
                end\\\
            else\\\
                -- bounds illegal\\\
                trigger.action.outText(\\\"+++flagArray: ignored range <\\\" .. anElement .. \\\"> (range)\\\", 30)\\\
            end\\\
        else\\\
            -- single number\\\
            local f = dcsCommon.trim(anElement) -- DML flag upgrade: accept strings tonumber(anElement)\\\
            if f then \\\
                table.insert(flags, f)\\\
\\\
            else \\\
                trigger.action.outText(\\\"+++flagArray: ignored element <\\\" .. anElement .. \\\"> (single)\\\", 30)\\\
            end\\\
        end\\\
    end\\\
    if verbose then \\\
        trigger.action.outText(\\\"+++flagArray: <\\\" .. #flags .. \\\"> flags total\\\", 30)\\\
    end \\\
    return flags\\\
end\\\
\\\
function dcsCommon.rangeArrayFromString(inString, verbose)\\\
    if not verbose then verbose = false end \\\
    \\\
    if verbose then \\\
        trigger.action.outText(\\\"+++rangeArray: processing <\\\" .. inString .. \\\">\\\", 30)\\\
    end \\\
\\\
    if string.len(inString) < 1 then \\\
        trigger.action.outText(\\\"+++rangeArray: empty ranges\\\", 30)\\\
        return {} \\\
    end\\\
    \\\
    local ranges = {}\\\
    local rawElements = dcsCommon.splitString(inString, \\\",\\\")\\\
    -- go over all elements \\\
    for idx, anElement in pairs(rawElements) do \\\
        anElement = dcsCommon.trim(anElement)\\\
        local outRange = {}\\\
        if dcsCommon.stringStartsWithDigit(anElement) and  dcsCommon.containsString(anElement, \\\"-\\\") then \\\
            -- interpret this as a range\\\
            local theRange = dcsCommon.splitString(anElement, \\\"-\\\")\\\
            local lowerBound = theRange[1]\\\
            lowerBound = tonumber(lowerBound)\\\
            local upperBound = theRange[2]\\\
            upperBound = tonumber(upperBound)\\\
            if lowerBound and upperBound then\\\
                -- swap if wrong order\\\
                if lowerBound > upperBound then \\\
                    local temp = upperBound\\\
                    upperBound = lowerBound\\\
                    lowerBound = temp \\\
                end\\\
                -- now add to ranges\\\
                outRange[1] = lowerBound\\\
                outRange[2] = upperBound\\\
                table.insert(ranges, outRange)\\\
                if verbose then \\\
                    trigger.action.outText(\\\"+++rangeArray: new range <\\\" .. lowerBound .. \\\"> to <\\\" .. upperBound .. \\\">\\\", 30)\\\
                end\\\
            else\\\
                -- bounds illegal\\\
                trigger.action.outText(\\\"+++rangeArray: ignored range <\\\" .. anElement .. \\\"> (range)\\\", 30)\\\
            end\\\
        else\\\
            -- single number\\\
            local f = dcsCommon.trim(anElement) \\\
            f = tonumber(f)\\\
            if f then \\\
                outRange[1] = f\\\
                outRange[2] = f\\\
                table.insert(ranges, outRange)\\\
                if verbose then \\\
                    trigger.action.outText(\\\"+++rangeArray: new (single-val) range <\\\" .. f .. \\\"> to <\\\" .. f .. \\\">\\\", 30)\\\
                end\\\
            else \\\
                trigger.action.outText(\\\"+++rangeArray: ignored element <\\\" .. anElement .. \\\"> (single)\\\", 30)\\\
            end\\\
        end\\\
    end\\\
    if verbose then \\\
        trigger.action.outText(\\\"+++rangeArray: <\\\" .. #ranges .. \\\"> ranges total\\\", 30)\\\
    end \\\
    return ranges\\\
end\\\
\\\
function dcsCommon.incFlag(flagName)\\\
    local v = trigger.misc.getUserFlag(flagName)\\\
    trigger.action.setUserFlag(flagName, v + 1)\\\
end\\\
\\\
function dcsCommon.decFlag(flagName)\\\
    local v = trigger.misc.getUserFlag(flagName)\\\
    trigger.action.setUserFlag(flagName, v - 1)\\\
end\\\
\\\
function dcsCommon.objectHandler(theObject, theCollector)\\\
    table.insert(theCollector, theObject)\\\
    return true \\\
end\\\
\\\
function dcsCommon.getObjectsForCatAtPointWithRadius(aCat, thePoint, theRadius)\\\
    if not aCat then aCat = Object.Category.UNIT end \\\
    local p = {x=thePoint.x, y=thePoint.y, z=thePoint.z}\\\
    local collector = {}\\\
    \\\
    -- now build the search argument \\\
    local args = {\\\
            id = world.VolumeType.SPHERE,\\\
            params = {\\\
                point = p,\\\
                radius = theRadius\\\
            }\\\
        }\\\
    \\\
    -- now call search\\\
    world.searchObjects(aCat, args, dcsCommon.objectHandler, collector)\\\
    return collector\\\
end\\\
\\\
function dcsCommon.getSceneryObjectsInZone(theZone) -- DCS ZONE!!! \\\
    local aCat = 5 -- scenery\\\
    -- WARNING: WE ARE USING DCS ZONES, NOT CFX!!!\\\
    local p = {x=theZone.x, y=0, z=theZone.y}\\\
    local lp = {x = p.x, y = p.z}\\\
    p.y = land.getHeight(lp)\\\
    local collector = {}\\\
    \\\
    -- now build the search argument \\\
    local args = {\\\
            id = world.VolumeType.SPHERE,\\\
            params = {\\\
                point = p,\\\
                radius = theZone.radius\\\
            }\\\
        }\\\
    \\\
    -- now call search\\\
    world.searchObjects(aCat, args, dcsCommon.objectHandler, collector)\\\
    return collector\\\
end\\\
\\\
function dcsCommon.getSceneryObjectInZoneByName(theName, theZone) -- DCS ZONE!!!\\\
    local allObs = dcsCommon.getSceneryObjectsInZone(theZone)\\\
    for idx, anObject in pairs(allObs) do \\\
        if tostring(anObject:getName()) == theName then return anObject end \\\
    end\\\
    return nil \\\
end\\\
\\\
--\\\
-- bitwise operators\\\
--\\\
function dcsCommon.bitAND32(a, b)\\\
    if not a then a = 0 end \\\
    if not b then b = 0 end \\\
    local z = 0\\\
    local e = 1\\\
    for i = 0, 31 do \\\
        local a1 = a % 2 -- 0 or 1\\\
        local b1 = b % 2 -- 0 or 1\\\
        if a1 == 1 and b1 == 1 then \\\
            a = a - 1 -- remove bit \\\
            b = b - 1 \\\
            z = z + e\\\
        else\\\
            if a1 == 1 then a = a - 1 end -- remove bit \\\
            if b1 == 1 then b = b - 1 end \\\
        end\\\
        a = a / 2 -- shift right\\\
        b = b / 2        \\\
        e = e * 2 -- raise e by 1 \\\
    end\\\
    return z\\\
end\\\
\\\
function dcsCommon.num2bin(a)\\\
    if not a then a = 0 end \\\
    local z = \\\"\\\"\\\
    for i = 0, 31 do \\\
        local a1 = a % 2 -- 0 or 1\\\
        if a1 == 1 then \\\
            a = a - 1 -- remove bit \\\
            z = \\\"1\\\"..z\\\
        else\\\
            z = \\\"0\\\"..z\\\
        end\\\
        a = a / 2 -- shift right\\\
    end\\\
    return z\\\
end\\\
\\\
function dcsCommon.LSR(a, num)\\\
    if not a then a = 0 end \\\
    if not num then num = 16 end \\\
    for i = 1, num do \\\
        local a1 = a % 2 -- 0 or 1\\\
        if a1 == 1 then \\\
            a = a - 1 -- remove bit \\\
        end\\\
        a = a / 2 -- shift right\\\
    end\\\
    return a\\\
end\\\
\\\
--\\\
-- string wildcards \\\
--\\\
function dcsCommon.processStringWildcards(inMsg)\\\
    -- Replace STATIC bits of message like CR and zone name \\\
    if not inMsg then return \\\"<nil inMsg>\\\" end\\\
    local formerType = type(inMsg)\\\
    if formerType ~= \\\"string\\\" then inMsg = tostring(inMsg) end  \\\
    if not inMsg then inMsg = \\\"<inMsg is incompatible type \\\" .. formerType .. \\\">\\\" end \\\
    local outMsg = \\\"\\\"\\\
    -- replace line feeds \\\
    outMsg = inMsg:gsub(\\\"<n>\\\", \\\"\\\\n\\\")\\\
\\\
    return outMsg \\\
end\\\
\\\
--\\\
-- phonetic alphabet \\\
--\\\
dcsCommon.alphabet = {\\\
    a = \\\"alpha\\\",\\\
    b = \\\"bravo\\\",\\\
    c = \\\"charlie\\\",\\\
    d = \\\"delta\\\",\\\
    e = \\\"echo\\\",\\\
    f = \\\"foxtrot\\\",\\\
    g = \\\"golf\\\",\\\
    h = \\\"hotel\\\",\\\
    i = \\\"india\\\",\\\
    j = \\\"juliet\\\",\\\
    k = \\\"kilo\\\",\\\
    l = \\\"lima\\\",\\\
    m = \\\"mike\\\",\\\
    n = \\\"november\\\",\\\
    o = \\\"oscar\\\",\\\
    p = \\\"papa\\\",\\\
    q = \\\"quebec\\\",\\\
    r = \\\"romeo\\\",\\\
    s = \\\"sierra\\\",\\\
    t = \\\"tango\\\",\\\
    u = \\\"uniform\\\",\\\
    v = \\\"victor\\\",\\\
    w = \\\"whiskey\\\",\\\
    x = \\\"x-ray\\\",\\\
    y = \\\"yankee\\\",\\\
    z = \\\"zulu\\\",\\\
[\\\"0\\\"] = \\\"zero\\\",\\\
[\\\"1\\\"] = \\\"wun\\\",\\\
[\\\"2\\\"] = \\\"too\\\",\\\
[\\\"3\\\"] = \\\"tree\\\",\\\
[\\\"4\\\"] = \\\"fower\\\",\\\
[\\\"5\\\"] = \\\"fife\\\" ,\\\
[\\\"6\\\"] = \\\"six\\\",\\\
[\\\"7\\\"] = \\\"seven\\\",\\\
[\\\"8\\\"] = \\\"att\\\",\\\
[\\\"9\\\"] = \\\"niner\\\",\\\
[\\\" \\\"] = \\\"break\\\",\\\
}\\\
\\\
function dcsCommon.letter(inChar)\\\
    local theChar = \\\"\\\"\\\
    if type(inChar == \\\"string\\\") then \\\
        if #inChar < 1 then return \\\"#ERROR0#\\\" end\\\
        inChar = string.lower(inChar)\\\
        theChar = string.sub(inChar, 1, 1)\\\
    elseif type(inChar == \\\"number\\\") then \\\
        if inChar > 255 then return \\\"#ERROR>#\\\" end \\\
        if inChar < 0 then return \\\"#ERROR<#\\\" end \\\
        theChar = char(inChar)\\\
    else \\\
        return \\\"#ERRORT#\\\"\\\
    end\\\
--    trigger.action.outText(\\\"doing <\\\" .. theChar .. \\\">\\\", 30)\\\
    local a = dcsCommon.alphabet[theChar]\\\
    if a == nil then a = \\\"#ERROR?#\\\" end \\\
    return a \\\
end\\\
\\\
function dcsCommon.spellString(inString)\\\
    local res = \\\"\\\"\\\
    local first = true \\\
    for i = 1, #inString do\\\
        local c = inString:sub(i,i)\\\
        if first then \\\
            res = dcsCommon.letter(c)\\\
            first = false \\\
        else \\\
            res = res .. \\\" \\\" .. dcsCommon.letter(c)\\\
        end\\\
    end\\\
    return res \\\
end\\\
\\\
dcsCommon.letters = {\\\"A\\\", \\\"B\\\", \\\"C\\\", \\\"D\\\", \\\"E\\\", \\\"F\\\", \\\"G\\\", \\\"H\\\", \\\"I\\\", \\\"J\\\", \\\"K\\\", \\\"L\\\", \\\"M\\\", \\\"N\\\", \\\
\\\"O\\\", \\\"P\\\", \\\"Q\\\", \\\"R\\\", \\\"S\\\", \\\"T\\\", \\\"U\\\", \\\"V\\\", \\\"W\\\", \\\"X\\\", \\\"Y\\\", \\\"Z\\\", }\\\
function dcsCommon.randomLetter(lowercase)\\\
    local theLetter = dcsCommon.pickRandom(dcsCommon.letters)\\\
    if lowercase then theLetter = string.lower(theLetter) end \\\
    return theLetter\\\
end\\\
\\\
--\\\
-- RGBA from hex\\\
--\\\
function dcsCommon.hexString2RGBA(inString) \\\
    -- enter with \\\"#FF0020\\\" (RGB) or \\\"#FF00AB99\\\" RGBA\\\
    -- check if it starts with #\\\
    if not inString then return nil end \\\
    if #inString ~= 7 and #inString ~=9 then return nil end \\\
    if inString:sub(1, 1) ~= \\\"#\\\" then return nil end \\\
    inString = inString:lower()\\\
    local red = tonumber(\\\"0x\\\" .. inString:sub(2,3)) \\\
    if not red then red = 0 end \\\
    local green = tonumber(\\\"0x\\\" .. inString:sub(4,5))\\\
    if not green then green = 0 end \\\
    local blue = tonumber(\\\"0x\\\" .. inString:sub(6,7))\\\
    if not blue then blue = 0 end \\\
    local alpha = 255 \\\
    if #inString == 9 then \\\
        alpha = tonumber(\\\"0x\\\" .. inString:sub(8,9))\\\
    end\\\
    if not alpha then alpha = 0 end\\\
    return {red/255, green/255, blue/255, alpha/255}\\\
end\\\
\\\
\\\
--\\\
-- Player handling \\\
--\\\
function dcsCommon.playerName2Coalition(playerName)\\\
    if not playerName then return 0 end \\\
    local factions = {1,2}\\\
    for idx, theFaction in pairs(factions) do \\\
        local players = coalition.getPlayers(theFaction)\\\
        for idy, theUnit in pairs(players) do \\\
            local upName = theUnit:getPlayerName()\\\
            if upName == playerName then return theFaction end\\\
        end\\\
    end\\\
    return 0\\\
end\\\
\\\
function dcsCommon.isPlayerUnit(theUnit)\\\
    -- new patch. simply check if getPlayerName returns something\\\
    if not theUnit then return false end \\\
    if not Unit.isExist(theUnit) then return end \\\
    if not theUnit.getPlayerName then return false end -- map/static object \\\
    local pName = theUnit:getPlayerName()\\\
    if pName then return true end \\\
    return false \\\
end\\\
\\\
function dcsCommon.getPlayerUnit(name)\\\
    for coa = 1, 2 do \\\
        local players = coalition.getPlayers(coa)\\\
        for idx, theUnit in pairs(players) do \\\
            if theUnit:getPlayerName() == name then return theUnit end\\\
        end\\\
    end\\\
    return nil \\\
end\\\
\\\
--\\\
-- theater and theater-related stuff \\\
--\\\
function dcsCommon.getMapName()\\\
    return env.mission.theatre\\\
end\\\
\\\
dcsCommon.magDecls = {Caucasus = 6.5,\\\
                      MarianaIslands = 1,\\\
                      Nevada = 12,\\\
                      PersianGulf = 2,\\\
                      Syria = 4,\\\
                      Normandy = -12 -- 1944, -1 in 2016 \\\
                      -- SinaiMap still missing \\\
                      -- Falklands still missing, big differences \\\
                      }\\\
                      \\\
function dcsCommon.getMagDeclForPoint(point) \\\
    -- WARNING! Approximations only, map-wide, not adjusted for year nor location!\\\
    -- serves as a stub for the day when DCS provides correct info \\\
    local map = dcsCommon.getMapName()\\\
    local decl = dcsCommon.magDecls[map]\\\
    if not decl then \\\
        trigger.action.outText(\\\"+++dcsC: unknown map <\\\" .. map .. \\\">, using dclenation 0\\\", 30)\\\
        decl = 0\\\
    end\\\
    return decl \\\
end \\\
\\\
--\\\
-- iterators\\\
--\\\
-- iteratePlayers - call callback for all player units\\\
-- callback is of signature callback(playerUnit)\\\
--\\\
\\\
function dcsCommon.iteratePlayers(callBack)\\\
    local factions = {0, 1, 2}\\\
    for idx, theFaction in pairs(factions) do \\\
        local players = coalition.getPlayers(theFaction)\\\
        for idy, theUnit in pairs(players) do \\\
            callBack(theUnit)\\\
        end\\\
    end\\\
end\\\
\\\
\\\
--\\\
-- MISC POINT CREATION\\\
--\\\
function dcsCommon.createPoint(x, y, z)\\\
    local newPoint = {}\\\
    newPoint.x = x\\\
    newPoint.y = y\\\
    newPoint.z = z -- works even if Z == nil\\\
    return newPoint\\\
end\\\
\\\
function dcsCommon.copyPoint(inPoint) \\\
    local newPoint = {}\\\
    newPoint.x = inPoint.x\\\
    newPoint.y = inPoint.y\\\
    -- handle xz only \\\
    if inPoint.z then \\\
        newPoint.z = inPoint.z \\\
    else \\\
        newPoint.z = inPoint.y \\\
    end\\\
    return newPoint    \\\
end\\\
\\\
--\\\
-- SEMAPHORES\\\
--\\\
dcsCommon.semaphores = {}\\\
\\\
-- replacement for trigger.misc.getUserFlag\\\
function dcsCommon.getUserFlag(flagName)\\\
    if dcsCommon.semaphores[flagName] then \\\
        return dcsCommon.semaphores[flagName]\\\
    end\\\
    \\\
    return trigger.misc.getUserFlag(flagName)\\\
end\\\
\\\
-- replacement for trigger.action.setUserFlag \\\
function dcsCommon.setUserFlag(flagName, theValue)\\\
    -- not yet connected: semaphores\\\
    \\\
    -- forget semaphore content if new value is old-school \\\
    if type(theValue) == \\\"number\\\" then \\\
        dcsCommon.semaphores[theValue] = nil --return to old-school \\\
    end\\\
    trigger.action.setUserFlag(flagName, theValue)\\\
end\\\
\\\
--\\\
--\\\
-- INIT\\\
--\\\
--\\\
    -- init any variables, tables etc that the lib requires internally\\\
    function dcsCommon.init()\\\
        cbID = 0\\\
        -- create ID tables\\\
        dcsCommon.collectMissionIDs()\\\
        \\\
        --dcsCommon.uuIdent = 0\\\
        if (dcsCommon.verbose) or true then\\\
          trigger.action.outText(\\\"dcsCommon v\\\" .. dcsCommon.version .. \\\" loaded\\\", 10)\\\
        end\\\
    end\\\
\\\
    \\\
-- do init. \\\
dcsCommon.init()\\\
\\\
\");a_do_script(\"cfxZones = {}\\\
cfxZones.version = \\\"4.3.2\\\"\\\
\\\
-- cf/x zone management module\\\
-- reads dcs zones and makes them accessible and mutable \\\
-- by scripting.\\\
--\\\
-- Copyright (c) 2021 - 2024 by Christian Franz and cf/x AG\\\
--\\\
\\\
--[[-- VERSION HISTORY\\\
- 4.0.0   - dmlZone OOP API started \\\
          - code revision / refactoring \\\
          - moved createPoint and copxPoint to dcsCommon, added bridging code \\\
          - re-routed all createPoint() invocations to dcsCommon \\\
          - removed anyPlayerInZone() because of cfxPlayer dependency\\\
          - numberArrayFromString() moved to dcsCommon, bridged \\\
          - flagArrayFromString() moved to dcsCommon, bridged \\\
          - doPollFlag() can differentiate between number method and string method \\\
            to enable passing an immediate negative value \\\
          - getNumberFromZoneProperty() enforces number return even on default\\\
          - immediate method switched to preceeding '#', to resolve conflict witzh \\\
            negative numbers, backwards compatibility with old (dysfunctional) method \\\
- 4.0.1   - dmlZone:getName()\\\
- 4.0.2   - removed verbosity from declutterZone (both versions)\\\
- 4.0.3   - new processDynamicVZU()\\\
          - wildcard uses processDynamicVZU\\\
- 4.0.4   - setFlagValue now supports multiple flags (OOP and classic)\\\
          - doSetFlagValue optimizations \\\
- 4.0.5   - dynamicAB wildcard \\\
          - processDynamicValueVU\\\
- 4.0.6   - hash mark forgotten QoL\\\
- 4.0.7   - drawZone()\\\
- 4.0.8   - markZoneWithObjects()\\\
          - cleanup \\\
          - markCenterWithObject\\\
          - markPointWithObject\\\
- 4.0.9   - createPolyZone now correctly returns new zone \\\
          - createSimplePolyZone correctly passes location to createPolyZone \\\
          - createPolyZone now correctly sets zone.point\\\
          - createPolyZone now correctly inits dcsOrigin\\\
          - createCircleZone noew correctly inits dcsOrigin\\\
- 4.0.10  - getBoolFromZoneProperty also supports \\\"on\\\" (=true) and \\\"off\\\" (=false)\\\
- 4.1.0   - getBoolFromZoneProperty 'on/off' support for dml variant as well \\\
- 4.1.1   - evalRemainder() updates \\\
- 4.1.2   - hash property missing warning \\\
- 4.2.0   - new createRandomPointInPopulatedZone()\\\
- 4.3.0   - boolean supports maybe, random, rnd, ?\\\
          - small optimization for randomInRange()\\\
          - randomDelayFromPositiveRange also allows 0 \\\
- 4.3.1   - new drawText() for zones \\\
          - dmlZones:getClosestZone() bridge \\\
- 4.3.2   - new getListFromZoneProperty()\\\
\\\
--]]--\\\
\\\
--\\\
-- ====================\\\
-- OOP dmlZone API HERE\\\
-- ====================\\\
--\\\
\\\
dmlZone = {}\\\
function dmlZone:new(o)\\\
    o = o or {}\\\
    setmetatable(o, self)\\\
    self.__index = self \\\
    self.name = \\\"dmlZone raw\\\"\\\
    self.isCircle = false\\\
    self.isPoly = false\\\
    self.radius = 0\\\
    self.poly = {}\\\
    self.bounds = {}\\\
    self.properties = {}\\\
    return o \\\
end \\\
\\\
--\\\
-- CLASSIC INTERFACE\\\
--\\\
cfxZones.verbose = false\\\
cfxZones.caseSensitiveProperties = false -- set to true to make property names case sensitive \\\
cfxZones.ups = 1 -- updates per second. updates moving zones\\\
\\\
cfxZones.zones = {} -- these are the zone as retrieved from the mission.\\\
                    -- ALWAYS USE THESE, NEVER DCS's ZONES!!!!\\\
\\\
function cfxZones.readFromDCS(clearfirst)\\\
    if (clearfirst) then\\\
        cfxZones.zones = {}\\\
    end\\\
    -- not all missions have triggers or zones\\\
    if not env.mission.triggers then \\\
        if cfxZones.verbose then \\\
            trigger.action.outText(\\\"cf/x zones: no env.triggers defined\\\", 10)\\\
        end\\\
        return\\\
    end\\\
    \\\
    if not env.mission.triggers.zones then \\\
        if cfxZones.verbose then \\\
            trigger.action.outText(\\\"cf/x zones: no zones defined\\\", 10)\\\
        end\\\
        return;\\\
    end\\\
\\\
    -- we only retrieve the data we need. At this point it is name, location and radius\\\
    -- and put this in our own little  structure. we also convert to all upper case name for index\\\
    -- and assume that the name may also carry meaning, e.g. 'LZ:' defines a landing zone\\\
    -- so we can quickly create other sets from this\\\
    -- zone object. DCS 2.7 introduced quads, so this is supported as well\\\
    --   name - name in upper case\\\
    --   isCircle - true if circular zone \\\
    --   isPoly - true if zone is defined by convex polygon, e.g. quad \\\
    --   point - vec3 (x 0 z) - zone's in-world center, used to place the coordinate\\\
    --   radius - number, zero when quad\\\
    --   bounds - aabb with attributes ul, ur, ll, lr (upper left .. lower right) as (x, 0, z)\\\
    --   poly - array 1..n of poly points, wound counter-clockwise \\\
    \\\
    for i, dcsZone in pairs(env.mission.triggers.zones) do\\\
        if type(dcsZone) == 'table' then -- hint taken from MIST: verify type when reading from dcs\\\
                                         -- dcs data is like a box of chocolates...\\\
            local newZone = dmlZone:new(nil) -- WAS: {} -- OOP introduction July 2023\\\
            -- name, converted to upper is used only for indexing\\\
            -- the original name remains untouched\\\
            newZone.dcsZone = dcsZone\\\
            newZone.name = dcsZone.name\\\
            newZone.isCircle = false\\\
            newZone.isPoly = false\\\
            newZone.radius = 0\\\
            newZone.poly = {}\\\
            newZone.bounds = {}\\\
            newZone.properties = {} -- dcs has this too, copy if present\\\
            if dcsZone.properties then \\\
                newZone.properties = dcsZone.properties \\\
            else\\\
                newZone.properties = {}\\\
            end -- WARNING: REF COPY. May need to clone \\\
            \\\
            local upperName = newZone.name:upper()\\\
            \\\
            -- location as 'point'\\\
            -- WARNING: zones locs are 2D (x,y) pairs, while y in DCS is altitude.\\\
            --          so we need to change (x,y) into (x, 0, z). Since Zones have no\\\
            --          altitude (they are an infinite cylinder) this works. Remember to \\\
            --          drop y from zone calculations to see if inside. \\\
            -- WARNING: ME linked zones have a relative x any y \\\
            --          to the linked unit \\\
            if dcsZone.linkUnit then \\\
                -- calculate the zone's real position by accessing the unit's MX data \\\
                -- as precached by dcsCommon\\\
                local ux, uy = dcsCommon.getUnitStartPosByID(dcsZone.linkUnit)\\\
                newZone.point = dcsCommon.createPoint(ux + dcsZone.x, 0, uy + dcsZone.y)\\\
                newZone.dcsOrigin = dcsCommon.createPoint(ux + dcsZone.x, 0, uy + dcsZone.y)\\\
            else \\\
                newZone.point = dcsCommon.createPoint(dcsZone.x, 0, dcsZone.y)\\\
                newZone.dcsOrigin = dcsCommon.createPoint(dcsZone.x, 0, dcsZone.y)\\\
            end\\\
\\\
            -- start type processing. if zone.type exists, we have a mission \\\
            -- created with 2.7 or above, else earlier \\\
            local zoneType = 0\\\
            if (dcsZone.type) then \\\
                zoneType = dcsZone.type \\\
            end\\\
            \\\
            if zoneType == 0 then \\\
                -- circular zone \\\
                newZone.isCircle = true \\\
                newZone.radius = dcsZone.radius\\\
                newZone.maxRadius = newZone.radius -- same for circular\\\
    \\\
            elseif zoneType == 2 then\\\
                -- polyZone\\\
                newZone.isPoly = true \\\
                newZone.radius = dcsZone.radius -- radius is still written in DCS, may change later. The radius has no meaning and is the last radius written before zone changed to poly.\\\
                -- note that newZone.point is only inside the tone for \\\
                -- convex polys, and DML only correctly works with convex polys\\\
                -- now transfer all point in the poly\\\
                -- note: DCS in 2.7 misspells vertices as 'verticies'\\\
                -- correct for this \\\
                newZone.maxRadius = 0\\\
                local verts = {}\\\
                if dcsZone.verticies then verts = dcsZone.verticies \\\
                else \\\
                    -- in later versions, this was corrected\\\
                    verts = dcsZone.vertices -- see if this is ever called\\\
                end\\\
                \\\
                for v=1, #verts do\\\
                    local dcsPoint = verts[v]\\\
                    local polyPoint = cfxZones.createPointFromDCSPoint(dcsPoint) -- (x, y) --> (x, 0, y-->z)\\\
                    newZone.poly[v] = polyPoint\\\
                    -- measure distance from zone's point, and store maxRadius \\\
                    -- dcs always saves a point with the poly zone \\\
                    local dist = dcsCommon.dist(newZone.point, polyPoint)\\\
                    if dist > newZone.maxRadius then newZone.maxRadius = dist end \\\
                end\\\
            else \\\
                \\\
                trigger.action.outText(\\\"cf/x zones: malformed zone #\\\" .. i .. \\\" unknown type \\\" .. zoneType, 10)\\\
            end\\\
            \\\
\\\
            -- calculate bounds\\\
            cfxZones.calculateZoneBounds(newZone) \\\
\\\
            -- add to my table\\\
            cfxZones.zones[upperName] = newZone -- WARNING: UPPER ZONE!!!\\\
            --trigger.action.outText(\\\"znd: procced \\\" .. newZone.name .. \\\" with radius \\\" .. newZone.radius, 30)\\\
        else\\\
            if cfxZones.verbose then \\\
                trigger.action.outText(\\\"cf/x zones: malformed zone #\\\" .. i .. \\\" dropped\\\", 10)\\\
            end\\\
        end -- else var not a table\\\
        \\\
    end -- for all zones kvp\\\
end -- readFromDCS\\\
\\\
function cfxZones.calculateZoneBounds(theZone)\\\
    if not (theZone) then return \\\
    end\\\
    \\\
    local bounds = theZone.bounds -- copy ref!\\\
    \\\
    if theZone.isCircle then \\\
        -- aabb are easy: center +/- radius \\\
        local center = theZone.point\\\
        local radius = theZone.radius \\\
        -- dcs uses z+ is down on map\\\
        -- upper left is center - radius \\\
        bounds.ul = dcsCommon.createPoint(center.x - radius, 0, center.z - radius)\\\
        bounds.ur = dcsCommon.createPoint(center.x + radius, 0, center.z - radius)\\\
        bounds.ll = dcsCommon.createPoint(center.x - radius, 0, center.z + radius)\\\
        bounds.lr = dcsCommon.createPoint(center.x + radius, 0, center.z + radius)\\\
        \\\
    elseif theZone.isPoly then\\\
        local poly = theZone.poly -- ref copy!\\\
        -- create the four points\\\
        local ll = cfxZones.createPointFromPoint(poly[1])\\\
        local lr = cfxZones.createPointFromPoint(poly[1])\\\
        local ul = cfxZones.createPointFromPoint(poly[1])\\\
        local ur = cfxZones.createPointFromPoint(poly[1])\\\
        \\\
        local pRad = dcsCommon.dist(theZone.point, poly[1]) -- rRad is radius for polygon from theZone.point \\\
        \\\
        -- now iterate through all points and adjust bounds accordingly \\\
        for v=2, #poly do \\\
            local vertex = poly[v]\\\
            if (vertex.x < ll.x) then ll.x = vertex.x; ul.x = vertex.x end \\\
            if (vertex.x > lr.x) then lr.x = vertex.x; ur.x = vertex.x end \\\
            if (vertex.z < ul.z) then ul.z = vertex.z; ur.z = vertex.z end\\\
            --if (vertex.z > ll.z) then ll.z = vertex.z; lr.z = vertex.z end\\\
            if (vertex.z > ur.z) then ur.z = vertex.z; ul.z = vertex.z end             \\\
            local dp = dcsCommon.dist(theZone.point, vertex)\\\
            if dp > pRad then pRad = dp end -- find largst distance to vertex\\\
        end\\\
        \\\
        -- now keep the new point references\\\
        -- and store them in the zone's bounds\\\
        bounds.ll = ll\\\
        bounds.lr = lr\\\
        bounds.ul = ul\\\
        bounds.ur = ur \\\
        -- we may need to ascertain why we need ul, ur, ll, lr instead of just ll and ur \\\
        -- store pRad \\\
        theZone.pRad = pRad -- not sure we'll ever need that, but at least we have it\\\
\\\
    else \\\
        -- huston, we have a problem\\\
        if cfxZones.verbose then \\\
            trigger.action.outText(\\\"cf/x zones: calc bounds: zone \\\" .. theZone.name .. \\\" has unknown type\\\", 30)\\\
        end\\\
    end\\\
    \\\
end\\\
\\\
function dmlZone:calculateZoneBounds()\\\
    cfxZones.calculateZoneBounds(self)\\\
end \\\
\\\
function cfxZones.createPoint(x, y, z)  -- bridge to dcsCommon, backward comp.\\\
    return dcsCommon.createPoint(x, y, z) \\\
end\\\
\\\
function cfxZones.copyPoint(inPoint) -- bridge to dcsCommon, backward comp.\\\
    return dcsCommon.copyPoint(inPoint)\\\
end\\\
\\\
function cfxZones.createHeightCorrectedPoint(inPoint) -- this should be in dcsCommon\\\
    local cP = dcsCommon.createPoint(inPoint.x, land.getHeight({x=inPoint.x, y=inPoint.z}),inPoint.z)\\\
    return cP\\\
end\\\
\\\
function cfxZones.getHeightCorrectedZonePoint(theZone)\\\
    local thePoint = cfxZone.getPoint(theZone)\\\
    return cfxZones.createHeightCorrectedPoint(thePoint)\\\
end\\\
\\\
function dmlZone:getHeightCorrectedZonePoint()\\\
    local thePoint = self:getPoint()\\\
    return dcsCommon.createPoint(thePoint.x, land.getHeight({x=thePoint.x, y=thePoint.z}),thePoint.z)\\\
end\\\
\\\
function cfxZones.createPointFromPoint(inPoint)\\\
    return cfxZones.copyPoint(inPoint)\\\
end\\\
\\\
function cfxZones.createPointFromDCSPoint(inPoint) \\\
    return dcsCommon.createPoint(inPoint.x, 0, inPoint.y)\\\
end\\\
\\\
\\\
function cfxZones.createRandomPointInsideBounds(bounds)\\\
    -- warning: bounds do not move woth zone! may have to be updated\\\
    local x = math.random(bounds.ll.x, ur.x)\\\
    local z = math.random(bounds.ll.z, ur.z)\\\
    return dcsCommon.createPoint(x, 0, z)\\\
end\\\
\\\
function cfxZones.createRandomPointOnZoneBoundary(theZone)\\\
    if not theZone then return nil end \\\
    if theZone.isPoly then \\\
        local loc, dx, dy = cfxZones.createRandomPointInPolyZone(theZone, true)\\\
        return loc, dx, dy \\\
    else \\\
        local loc, dx, dy = cfxZones.createRandomPointInCircleZone(theZone, true)\\\
        return loc, dx, dy \\\
    end\\\
end\\\
\\\
function dmlZone:createRandomPointOnZoneBoundary()\\\
    return cfxZones.createRandomPointOnZoneBoundary(self)\\\
end \\\
\\\
function cfxZones.createRandomPointInZone(theZone)\\\
    if not theZone then return nil end \\\
    if theZone.isPoly then \\\
        local loc, dx, dy = cfxZones.createRandomPointInPolyZone(theZone)\\\
        return loc, dx, dy \\\
    else \\\
        local loc, dx, dy = cfxZones.createRandomPointInCircleZone(theZone)\\\
        return loc, dx, dy \\\
    end\\\
end\\\
\\\
function dmlZone:createRandomPointInZone()\\\
    local loc, dx, dy = cfxZones.createRandomPointInZone(self)\\\
    return loc, dx, dy\\\
end \\\
\\\
\\\
function cfxZones.randomPointInZone(theZone)\\\
    local loc, dx, dy =  cfxZones.createRandomPointInZone(theZone)\\\
    return loc, dx, dy \\\
end\\\
\\\
function dmlZone:randomPointInZone()\\\
    local loc, dx, dy =  cfxZones.createRandomPointInZone(self)\\\
    return loc, dx, dy \\\
end\\\
\\\
function cfxZones.createRandomPointInCircleZone(theZone, onEdge)\\\
    if not theZone.isCircle then \\\
        trigger.action.outText(\\\"+++Zones: warning - createRandomPointInCircleZone called for non-circle zone <\\\" .. theZone.name .. \\\">\\\", 30)\\\
        return {x=theZone.point.x, y=0, z=theZone.point.z}\\\
    end\\\
    \\\
    -- ok, let's first create a random percentage value for the new radius\\\
    -- now lets get a random degree\\\
    local degrees = math.random() * 2 * 3.14152 -- radiants. \\\
    local r = theZone.radius \\\
    if not onEdge then \\\
        r = r * math.random()\\\
    end \\\
    local p = cfxZones.getPoint(theZone) -- force update of zone if linked\\\
    local dx = r * math.cos(degrees)\\\
    local dz = r * math.sin(degrees)\\\
    local px = p.x + dx -- r * math.cos(degrees)\\\
    local pz = p.z + dz -- r * math.sin(degrees)\\\
    return {x=px, y=0, z = pz}, dx, dz -- returns loc and offsets to theZone.point\\\
end\\\
\\\
function dmlZone:createRandomPointInCircleZone(theZone, onEdge)\\\
    local p, dx, dz = cfxZones.createRandomPointInCircleZone(self, onEdge)\\\
    return p, dx, dz \\\
end \\\
\\\
function cfxZones.createRandomPointInPolyZone(theZone, onEdge)\\\
    if not theZone.isPoly then \\\
        trigger.action.outText(\\\"+++Zones: warning - createRandomPointInPolyZone called for non-poly zone <\\\" .. theZone.name .. \\\">\\\", 30)\\\
        return dcsCommon.createPoint(theZone.point.x, 0, theZone.point.z)\\\
    end\\\
    -- force update of all points \\\
    local p = cfxZones.getPoint(theZone)\\\
    \\\
    -- point in convex poly: choose two different lines from that polygon \\\
    local lineIdxA = dcsCommon.smallRandom(#theZone.poly)\\\
    repeat lineIdxB = dcsCommon.smallRandom(#theZone.poly) until (lineIdxA ~= lineIdxB)\\\
    \\\
    -- we now have two different lines. pick a random point on each. \\\
    -- we use lerp to pick any point between a and b \\\
    local a = theZone.poly[lineIdxA]\\\
    lineIdxA = lineIdxA + 1 -- get next point in poly and wrap around\\\
    if lineIdxA > #theZone.poly then lineIdxA = 1 end \\\
    local b = theZone.poly[lineIdxA] \\\
    local randompercent = math.random()\\\
    local sourceA = dcsCommon.vLerp (a, b, randompercent)\\\
    -- if all we want is a point on an edge, we are done \\\
    if onEdge then \\\
        local polyPoint = sourceA\\\
        return polyPoint, polyPoint.x - p.x, polyPoint.z - p.z -- return loc, dx, dz \\\
    end \\\
    \\\
    -- now get point on second line \\\
    a = theZone.poly[lineIdxB]\\\
    lineIdxB = lineIdxB + 1 -- get next point in poly and wrap around\\\
    if lineIdxB > #theZone.poly then lineIdxB = 1 end \\\
    b = theZone.poly[lineIdxB] \\\
    randompercent = math.random()\\\
    local sourceB = dcsCommon.vLerp (a, b, randompercent)\\\
    \\\
    -- now take a random point on that line that entirely \\\
    -- runs through the poly \\\
    randompercent = math.random()\\\
    local polyPoint = dcsCommon.vLerp (sourceA, sourceB, randompercent)\\\
    return polyPoint, polyPoint.x - p.x, polyPoint.z - p.z -- return loc, dx, dz \\\
end\\\
\\\
function dmlZone:createRandomPointInPolyZone(onEdge)\\\
    local p, dx, dz = cfxZones.createRandomPointInPolyZone(self, onEdge)\\\
    return p, dx, dz \\\
end \\\
\\\
function dmlZone:createRandomPointInPopulatedZone(radius, maxTries)\\\
    if not maxTries then maxTries = 20 end \\\
    if not radius then radius = 10 end -- meters \\\
    local cnt = 0\\\
    local p, dx, dz\\\
    repeat\\\
        p, dx, dz = self:createRandomPointInZone() -- p is x, 0, z \\\
        local hits, collector = cfxZones.objectsInRange(p, radius) \\\
        if hits < 1 then return p, dx, dz end\\\
        if hits == 1 then \\\
            local o = collector[1]\\\
            local op = o:getPoint()\\\
            d = dcsCommon.distFlat(op, p)\\\
--            trigger.action.outText(\\\"singleDist = \\\" .. d, 30)\\\
            if d > radius/2 then \\\
--                trigger.action.outText(\\\"good enough, will use\\\", 30)\\\
                return p, dx, dz \\\
            end \\\
        end \\\
        cnt = cnt + 1\\\
--        trigger.action.outText(hits .. \\\"hits --> failed try \\\" .. cnt, 30)\\\
    until cnt > maxTries\\\
    return p, dx, dz\\\
end\\\
\\\
function cfxZones.createRandomPointInPopulatedZone(theZone, radius, maxTries)\\\
    if not theZone then return nil, nil, nil end \\\
    local p, dx, dz = theZone:createRandomPointInPopulatedZone(radius, maxTries)\\\
    return p, dx, dz\\\
end\\\
\\\
--[[--\\\
function dmlZone:createRandomPointInPopulatedZone(radius, maxTries)\\\
    if not maxTries then maxTries = 20 end \\\
    local cnt = 0\\\
    local p, dx, dz\\\
    p, dx, dz = self:createRandomPointInZone() -- p is x, 0, z \\\
    repeat\\\
        local hits = cfxZones.objectsInRange(p, radius) \\\
        if hits < 1 then return p, dx, dz end \\\
        -- move to the right by radius\\\
        p.z = p.z + radius\\\
        dz = dz + radius \\\
        cnt = cnt + 1\\\
        trigger.action.outText(\\\"failed try \\\" .. cnt, 30)\\\
    until cnt > maxTries\\\
    return p, dx, dz\\\
end\\\
--]]--\\\
function cfxZones.objectHandler(theObject, theCollector) -- for world.search\\\
    table.insert(theCollector, theObject)\\\
    return true \\\
end\\\
\\\
function cfxZones.objectsInRange(pt, range) \\\
    if not range then range = 100 end -- meters\\\
    local allCats = {1, 2, 3, 4, 5, 6} -- all cats \\\
    local lp = {x = pt.x, y = pt.z}\\\
    pt.y = land.getHeight(lp)\\\
    local collector = {}    \\\
    -- now build the search argument \\\
    local args = {\\\
            id = world.VolumeType.SPHERE,\\\
            params = {\\\
                point = pt,\\\
                radius = range -- range\\\
            }\\\
        }\\\
    -- now call search\\\
    world.searchObjects(allCats, args, cfxZones.objectHandler, collector)\\\
    -- now filter for distance because search finds too many \\\
    local filtered = {}\\\
    for idx, anObject in pairs(collector) do \\\
        -- calc dist and filter \\\
        local op = anObject:getPoint()\\\
        local dist = dcsCommon.dist(pt, op)\\\
        if dist < range then \\\
--            local e = {\\\
--                    dist = dist,\\\
--                    o = anObject\\\
--                }\\\
--            table.insert(filtered, e)\\\
            table.insert(filtered, anObject)\\\
        end\\\
    end \\\
    \\\
    return #filtered, filtered  \\\
end\\\
\\\
function cfxZones.addZoneToManagedZones(theZone)\\\
    local upperName = string.upper(theZone.name) -- newZone.name:upper()\\\
    cfxZones.zones[upperName] = theZone\\\
end\\\
\\\
function dmlZone:addZoneToManagedZones()\\\
    local upperName = string.upper(self.name) -- newZone.name:upper()\\\
    cfxZones.zones[upperName] = self\\\
end\\\
\\\
function cfxZones.createUniqueZoneName(inName, searchSet)\\\
    if not inName then return nil end \\\
    if not searchSet then searchSet = cfxZones.zones end \\\
    inName = inName:upper()\\\
    while searchSet[inName] ~= nil do \\\
        inName = inName .. \\\"X\\\"\\\
    end\\\
    return inName\\\
end\\\
\\\
function cfxZones.createSimpleZone(name, location, radius, addToManaged)\\\
    if not radius then radius = 10 end\\\
    if not addToManaged then addToManaged = false end \\\
    if not location then \\\
        location = {}\\\
    end\\\
    if not location.x then location.x = 0 end \\\
    if not location.z then location.z = 0 end \\\
    \\\
    local newZone = cfxZones.createCircleZone(name, location.x, location.z, radius)\\\
    \\\
    if addToManaged then \\\
        cfxZones.addZoneToManagedZones(newZone)\\\
    end\\\
    return newZone\\\
end\\\
\\\
function cfxZones.createCircleZone(name, x, z, radius) \\\
    local newZone = dmlZone:new(nil) -- {} OOP compatibility \\\
    newZone.isCircle = true\\\
    newZone.isPoly = false\\\
    newZone.poly = {}\\\
    newZone.bounds = {}\\\
            \\\
    newZone.name = name\\\
    newZone.radius = radius\\\
    newZone.point = dcsCommon.createPoint(x, 0, z)\\\
     newZone.dcsOrigin = dcsCommon.createPoint(x, 0, z)\\\
\\\
    -- props \\\
    newZone.properties = {}\\\
    \\\
    -- calculate my bounds\\\
    cfxZones.calculateZoneBounds(newZone)\\\
    \\\
    return newZone\\\
end\\\
\\\
function cfxZones.createSimplePolyZone(name, location, points, addToManaged)\\\
    if not addToManaged then addToManaged = false end \\\
    if not location then \\\
        location = {}\\\
    end\\\
    if not location.x then location.x = 0 end \\\
    if not location.z then location.z = 0 end \\\
    if not location.y then location.y = 0 end \\\
\\\
    local newZone = cfxZones.createPolyZone(name, points, location)\\\
    \\\
    if addToManaged then \\\
        cfxZones.addZoneToManagedZones(newZone)\\\
    end\\\
    return newZone\\\
end\\\
\\\
function cfxZones.createSimpleQuadZone(name, location, points, addToManaged)\\\
    if not location then \\\
        location = {}\\\
    end\\\
    if not location.x then location.x = 0 end \\\
    if not location.z then location.z = 0 end \\\
        \\\
    -- synthesize 4 points if they don't exist\\\
    -- remember: in DCS positive x is up, positive z is right \\\
    if not points then \\\
        points = {} \\\
    end\\\
    if not points[1] then \\\
        -- upper left \\\
        points[1] = {x = location.x-1, y = 0, z = location.z-1}\\\
    end\\\
    if not points[2] then \\\
        -- upper right \\\
        points[2] = {x = location.x-1, y = 0, z = location.z+1}\\\
    end\\\
    if not points[3] then \\\
        -- lower right \\\
        points[3] = {x = location.x+1, y = 0, z = location.z+1}\\\
    end\\\
    if not points[4] then \\\
        -- lower left \\\
        points[4] = {x = location.x+1, y = 0, z = location.z-1}\\\
    end\\\
    \\\
    return cfxZones.createSimplePolyZone(name, location, points, addToManaged)\\\
end\\\
\\\
function cfxZones.createPolyZone(name, poly, location) -- poly must be array of point type\\\
    local newZone = dmlZone:new(nil) -- {} OOP compatibility \\\
    if not location then location = {x=0, y=0, z=0} end \\\
    newZone.point = dcsCommon.createPoint(location.x, 0, location.z)\\\
    newZone.dcsOrigin = dcsCommon.createPoint(location.x, 0, location.z)\\\
    newZone.isCircle = false\\\
    newZone.isPoly = true\\\
    newZone.poly = {}\\\
    newZone.bounds = {}\\\
            \\\
    newZone.name = name\\\
    newZone.radius = 0\\\
    -- copy poly\\\
    for v=1, #poly do \\\
        local theVertex = poly[v] \\\
        newZone.poly[v] = cfxZones.createPointFromPoint(theVertex) \\\
    end\\\
    \\\
    -- properties \\\
    newZone.properties = {}\\\
    \\\
    cfxZones.calculateZoneBounds(newZone)\\\
    return newZone \\\
end\\\
\\\
function cfxZones.createRandomZoneInZone(name, inZone, targetRadius, entirelyInside)\\\
    -- create a new circular zone with center placed inside inZone\\\
    -- if entirelyInside is false, only the zone's center is guaranteed to be inside\\\
    -- inZone.\\\
    -- entirelyInside is not guaranteed for polyzones\\\
        \\\
    if inZone.isCircle then \\\
        local sourceRadius = inZone.radius\\\
        if entirelyInside and targetRadius > sourceRadius then targetRadius = sourceRadius end\\\
        if entirelyInside then sourceRadius = sourceRadius - targetRadius end\\\
    \\\
        -- ok, let's first create a random percentage value for the new radius\\\
        local percent = 1 / math.random(100)\\\
        -- now lets get a random degree\\\
        local degrees = math.random(360) * 3.14152 / 180 -- ok, it's actually radiants. \\\
        local r = sourceRadius * percent \\\
        local x = inZone.point.x + r * math.cos(degrees)\\\
        local z = inZone.point.z + r * math.sin(degrees)\\\
        -- construct new zone\\\
        local newZone = cfxZones.createCircleZone(name, x, z, targetRadius)\\\
        return newZone\\\
    \\\
    elseif inZone.isPoly then \\\
        local newPoint = cfxZones.createRandomPointInPolyZone(inZone)\\\
        -- construct new zone\\\
        local newZone = cfxZones.createCircleZone(name, newPoint.x, newPoint.z, targetRadius)\\\
        return newZone\\\
        \\\
    else \\\
        -- zone type unknown\\\
        trigger.action.outText(\\\"CreateZoneInZone: unknown zone type for inZone =\\\" .. inZone.name ,  10)\\\
        return nil \\\
    end\\\
end\\\
\\\
-- polygon inside zone calculations\\\
\\\
\\\
-- isleft returns true if point P is to the left of line AB \\\
-- by determining the sign (up or down) of the normal vector of \\\
-- the two vectors PA and PB in the y coordinate. We arbitrarily define\\\
-- left as being > 0, so right is <= 0. As long as we always use the \\\
-- same comparison, it does not matter what up or down mean.\\\
-- this is important because we don't know if dcs always winds quads\\\
-- the same way, we must simply assume that they are wound as a polygon \\\
function cfxZones.isLeftXZ(A, B, P)\\\
    return ((B.x - A.x)*(P.z - A.z) - (B.z - A.z)*(P.x - A.x)) > 0\\\
end\\\
\\\
-- returns true/false for inside\\\
function cfxZones.isPointInsideQuad(thePoint, A, B, C, D) \\\
    -- Inside test (only convex polygons): \\\
    -- point lies on the same side of each quad's vertex AB, BC, CD, DA\\\
    -- how do we find out which side a point lies on? via the cross product\\\
    -- see isLeft below\\\
    \\\
    -- so all we need to do is make sure all results of isLeft for all\\\
    -- four sides are the same\\\
    mustMatch = isLeftXZ(A, B, thePoint) -- all test results must be the same and we are ok\\\
                                       -- they just must be the same side.\\\
    if (cfxZones.isLeftXZ(B, C, thePoint ~= mustMatch)) then return false end -- on other side than all before\\\
    if (cfxZones.isLeftXZ(C, D, thePoint ~= mustMatch)) then return false end \\\
    if (cfxZones.isLeftXZ(D, A, thePoint ~= mustMatch)) then return false end\\\
    return true\\\
end\\\
\\\
-- generalized version of insideQuad, assumes winding of poly, poly convex, poly closed\\\
function cfxZones.isPointInsidePoly(thePoint, poly)\\\
    local mustMatch = cfxZones.isLeftXZ(poly[1], poly[2], thePoint)\\\
    for v=2, #poly-1 do \\\
        if cfxZones.isLeftXZ(poly[v], poly[v+1], thePoint) ~= mustMatch then return false end\\\
    end\\\
    -- final test\\\
    if cfxZones.isLeftXZ(poly[#poly], poly[1], thePoint) ~= mustMatch then return false end\\\
    \\\
    return true\\\
end;\\\
\\\
function cfxZones.isPointInsideZone(thePoint, theZone, radiusIncrease)\\\
    -- radiusIncrease only works for circle zones \\\
    if not radiusIncrease then radiusIncrease = 0 end \\\
    local p = {x=thePoint.x, y = 0, z = thePoint.z} -- zones have no altitude\\\
    if (theZone.isCircle) then \\\
        local zp = cfxZones.getPoint(theZone)\\\
        local d = dcsCommon.dist(p, theZone.point)\\\
        return d < theZone.radius + radiusIncrease, d \\\
    end \\\
    \\\
    if (theZone.isPoly) then \\\
        --trigger.action.outText(\\\"zne: isPointInside: \\\" .. theZone.name .. \\\" is Polyzone!\\\", 30)\\\
        return (cfxZones.isPointInsidePoly(p, theZone.poly)), 0 -- always returns delta 0\\\
    end\\\
\\\
    trigger.action.outText(\\\"isPointInsideZone: Unknown zone type for \\\" .. outerZone.name, 10)\\\
end\\\
\\\
function dmlZone:isPointInsideZone(thePoint, radiusIncrease) -- warning: param order!\\\
    return cfxZones.isPointInsideZone(thePoint, self, radiusIncrease)\\\
end \\\
\\\
-- isZoneInZone returns true if center of innerZone is inside  outerZone\\\
function cfxZones.isZoneInsideZone(innerZone, outerZone) \\\
    local p = cfxZones.getPoint(innerZone)\\\
    return cfxZones.isPointInsideZone(p, outerZone)    \\\
end\\\
\\\
function dmlZone:isZoneInsideZone(outerZone) \\\
    return cfxZones.isPointInsideZone(self:getPoint(), outerZone)    \\\
end\\\
\\\
function cfxZones.getZonesContainingPoint(thePoint, testZones) -- return array \\\
    if not testZones then \\\
        testZones = cfxZones.zones \\\
    end \\\
    \\\
    local containerZones = {}\\\
    for tName, tData in pairs(testZones) do \\\
        if cfxZones.isPointInsideZone(thePoint, tData) then \\\
            table.insert(containerZones, tData)\\\
        end\\\
    end\\\
\\\
    return containerZones\\\
end\\\
\\\
function cfxZones.getFirstZoneContainingPoint(thePoint, testZones)\\\
    if not testZones then \\\
        testZones = cfxZones.zones \\\
    end \\\
    \\\
    for tName, tData in pairs(testZones) do \\\
        if cfxZones.isPointInsideZone(thePoint, tData) then \\\
            return tData\\\
        end\\\
    end\\\
\\\
    return nil\\\
end\\\
\\\
function cfxZones.getAllZonesInsideZone(superZone, testZones) -- returnes array!\\\
    if not testZones then \\\
        testZones = cfxZones.zones \\\
    end \\\
    \\\
    local containedZones = {}\\\
    for zName, zData in pairs(testZones) do\\\
        if cfxZones.isZoneInsideZone(zData, superZone) then \\\
            if zData ~= superZone then \\\
                -- we filter superzone because superzone usually resides \\\
                -- inside itself \\\
                table.insert(containedZones, zData)\\\
            end\\\
        end\\\
    end\\\
    return containedZones \\\
end\\\
\\\
function dmlZone:getAllZonesInsideZone(testZones)\\\
    return cfxZones.getAllZonesInsideZone(self, testZones)\\\
end\\\
\\\
\\\
function cfxZones.getZonesWithAttributeNamed(attributeName, testZones)\\\
    if not testZones then testZones = cfxZones.zones end \\\
\\\
    local attributZones = {}\\\
    for aName,aZone in pairs(testZones) do\\\
        local attr = cfxZones.getZoneProperty(aZone, attributeName)\\\
        if attr then \\\
            -- this zone has the requested attribute\\\
            table.insert(attributZones, aZone)\\\
        end\\\
    end\\\
    return attributZones\\\
end\\\
\\\
--\\\
-- zone volume management\\\
--\\\
\\\
function cfxZones.getZoneVolume(theZone)\\\
    if not theZone then return nil end \\\
    \\\
    if (theZone.isCircle) then \\\
        -- create a sphere volume\\\
        local p = cfxZones.getPoint(theZone)\\\
        p.y = land.getHeight({x = p.x, y = p.z})\\\
        local r = theZone.radius\\\
        if r < 10 then r = 10 end \\\
        local vol = {\\\
            id = world.VolumeType.SPHERE,\\\
            params = {\\\
                point = p,\\\
                radius = r\\\
            }\\\
        }\\\
        return vol \\\
    elseif (theZone.isPoly) then \\\
        -- build the box volume, using the zone's bounds ll and ur points \\\
        local lowerLeft = {}\\\
        -- we build x = westerm y = southern, Z = alt \\\
        local alt = land.getHeight({x=theZone.bounds.ll.x, y = theZone.bounds.ll.z}) - 10\\\
        lowerLeft.x = theZone.bounds.ll.x \\\
        lowerLeft.z = theZone.bounds.ll.z \\\
        lowerLeft.y = alt -- we go lower \\\
        \\\
        local upperRight = {}\\\
        alt = land.getHeight({x=theZone.bounds.ur.x, y = theZone.bounds.ur.z}) + 10\\\
        upperRight.x = theZone.bounds.ur.x \\\
        upperRight.z = theZone.bounds.ur.z \\\
        upperRight.y = alt -- we go higher \\\
        \\\
        -- construct volume \\\
        local vol = {\\\
            id = world.VolumeType.BOX,\\\
            params = {\\\
                min = lowerLeft,\\\
                max = upperRight\\\
            }\\\
        }\\\
        return vol \\\
    else \\\
        trigger.action.outText(\\\"zne: unknown zone type for <\\\" .. theZone.name .. \\\">\\\", 30)\\\
    end\\\
end\\\
\\\
function dmlZone:getZoneVolume()\\\
    return cfxZones.getZoneVolume(self)\\\
end \\\
\\\
\\\
function cfxZones.declutterZone(theZone)\\\
    if not theZone then return end \\\
    local theVol = cfxZones.getZoneVolume(theZone)\\\
    world.removeJunk(theVol)\\\
end\\\
\\\
function dmlZone:declutterZone()\\\
    local theVol = cfxZones.getZoneVolume(self)\\\
    world.removeJunk(theVol)\\\
end\\\
\\\
--\\\
-- units / groups in zone\\\
--\\\
function cfxZones.allGroupsInZone(theZone, categ) -- categ is optional, must be code \\\
    -- warning: does not check for existing!\\\
    local inZones = {}\\\
    local coals = {0, 1, 2} -- all coalitions\\\
    for idx, coa in pairs(coals) do \\\
        local allGroups = coalition.getGroups(coa, categ)\\\
        for key, group in pairs(allGroups) do -- iterate all groups\\\
            if cfxZones.isGroupPartiallyInZone(group, theZone) then\\\
                table.insert(inZones, group)\\\
            end\\\
        end\\\
    end\\\
    return inZones\\\
end\\\
\\\
function dmlZone:allGroupsInZone(categ)\\\
    return cfxZones.allGroupsInZone(self, categ)\\\
end\\\
\\\
function cfxZones.allGroupNamesInZone(theZone, categ) -- categ is optional, must be code \\\
    -- warning: does not check for existing!\\\
    local inZones = {}\\\
    local coals = {0, 1, 2} -- all coalitions\\\
    for idx, coa in pairs(coals) do \\\
        local allGroups = coalition.getGroups(coa, categ)\\\
        for key, group in pairs(allGroups) do -- iterate all groups\\\
            if cfxZones.isGroupPartiallyInZone(group, theZone) then\\\
                table.insert(inZones, group:getName())\\\
            end\\\
        end\\\
    end\\\
    return inZones\\\
end\\\
\\\
function dmlZone:allGroupNamesInZone(categ)\\\
    return cfxZones.allGroupNamesInZone(self, categ)\\\
end\\\
\\\
function cfxZones.allStaticsInZone(theZone, useOrigin) -- categ is optional, must be code \\\
    -- warning: does not check for existing!\\\
    local inZones = {}\\\
    local coals = {0, 1, 2} -- all coalitions\\\
    for idx, coa in pairs(coals) do \\\
        local allStats = coalition.getStaticObjects(coa)\\\
        for key, statO in pairs(allStats) do -- iterate all groups\\\
            local oP = statO:getPoint()\\\
            if useOrigin then \\\
                if cfxZones.pointInZone(oP, theZone, true) then \\\
                    -- use DCS original coords\\\
                    table.insert(inZones, statO)\\\
                end\\\
            elseif cfxZones.pointInZone(oP, theZone) then\\\
                table.insert(inZones, statO)\\\
            end\\\
        end\\\
    end\\\
    return inZones\\\
end\\\
\\\
function dmlZone:allStaticsInZone(useOrigin)\\\
    return cfxZones.allStaticsInZone(self, useOrigin)\\\
end\\\
\\\
\\\
function cfxZones.groupsOfCoalitionPartiallyInZone(coal, theZone, categ) -- categ is optional\\\
    local groupsInZone = {}\\\
    local allGroups = coalition.getGroups(coal, categ)\\\
    for key, group in pairs(allGroups) do -- iterate all groups\\\
        if group:isExist() then\\\
            if cfxZones.isGroupPartiallyInZone(group, theZone) then\\\
                table.insert(groupsInZone, group)            \\\
            end\\\
        end\\\
    end\\\
    return groupsInZone\\\
end\\\
\\\
function cfxZones.isGroupPartiallyInZone(aGroup, aZone)\\\
    if not aGroup then return false end \\\
    if not aZone then return false end \\\
        \\\
    if not aGroup:isExist() then return false end \\\
    local allUnits = aGroup:getUnits()\\\
    for uk, aUnit in pairs (allUnits) do \\\
        if Unit.isExist(aUnit) and aUnit:getLife() > 1 then         \\\
            local p = aUnit:getPoint()\\\
            local inzone, percent, dist = cfxZones.pointInZone(p, aZone)\\\
            if inzone then        \\\
                return true\\\
            end \\\
        end\\\
    end\\\
    return false\\\
end\\\
\\\
function cfxZones.isEntireGroupInZone(aGroup, aZone)\\\
    if not aGroup then return false end \\\
    if not aZone then return false end \\\
    if not aGroup:isExist() then return false end \\\
    local allUnits = aGroup:getUnits()\\\
    for uk, aUnit in pairs (allUnits) do \\\
        if aUnit:isExist() and aUnit:getLife() > 1 then \\\
            local p = aUnit:getPoint()\\\
            if not cfxZones.isPointInsideZone(p, aZone) then \\\
                return false\\\
            end\\\
        end\\\
    end\\\
    return true\\\
end\\\
\\\
function dmlZone:isEntireGroupInZone(aGroup)\\\
    return cfxZones.isEntireGroupInZone(aGroup, self)\\\
end\\\
\\\
--\\\
-- Zone Manipulation\\\
--\\\
\\\
function cfxZones.offsetZone(theZone, dx, dz)\\\
    -- first, update center \\\
    theZone.point.x = theZone.point.x + dx\\\
    theZone.point.z = theZone.point.z + dz \\\
    \\\
    -- now process all polygon points - it's empty for circular, so don't worry\\\
    for v=1, #theZone.poly do \\\
        theZone.poly[v].x = theZone.poly[v].x + dx\\\
        theZone.poly[v].z = theZone.poly[v].z + dz \\\
    end\\\
    \\\
    -- update zone bounds \\\
    theZone.bounds.ll.x = theZone.bounds.ll.x + dx \\\
    theZone.bounds.lr.x = theZone.bounds.lr.x + dx\\\
    theZone.bounds.ul.x = theZone.bounds.ul.x + dx \\\
    theZone.bounds.ur.x = theZone.bounds.ur.x + dx\\\
\\\
    theZone.bounds.ll.z = theZone.bounds.ll.z + dz \\\
    theZone.bounds.lr.z = theZone.bounds.lr.z + dz\\\
    theZone.bounds.ul.z = theZone.bounds.ul.z + dz \\\
    theZone.bounds.ur.z = theZone.bounds.ur.z + dz\\\
    \\\
end\\\
\\\
function dmlZone:offsetZone(dx, dz)\\\
    cfxZones.offsetZone(self, dx, dz)\\\
end\\\
\\\
\\\
function cfxZones.moveZoneTo(theZone, x, z)\\\
    local dx = x - theZone.point.x\\\
    local dz = z - theZone.point.z \\\
    cfxZones.offsetZone(theZone, dx, dz)\\\
end;\\\
\\\
function dmlZone:moveZoneTo(x, z)\\\
    cfxZones.moveZoneTo(self, x, z)\\\
end\\\
\\\
function cfxZones.centerZoneOnUnit(theZone, theUnit) \\\
    local thePoint = theUnit:getPoint()\\\
    cfxZones.moveZoneTo(theZone, thePoint.x, thePoint.z)\\\
end\\\
\\\
function dmlZone:centerZoneOnUnit(theUnit) \\\
    local thePoint = theUnit:getPoint()\\\
    self:moveZoneTo(thePoint.x, thePoint.z)\\\
end\\\
\\\
\\\
function cfxZones.dumpZones(zoneTable)\\\
    if not zoneTable then zoneTable = cfxZones.zones end \\\
    \\\
    trigger.action.outText(\\\"Zones START\\\", 10)\\\
    for i, zone in pairs(zoneTable) do \\\
        local myType = \\\"unknown\\\"\\\
        if zone.isCircle then myType = \\\"Circle\\\" end\\\
        if zone.isPoly then myType = \\\"Poly\\\" end \\\
        \\\
        trigger.action.outText(\\\"#\\\".. i .. \\\": \\\" .. zone.name .. \\\" of type \\\" .. myType, 10)\\\
    end\\\
    trigger.action.outText(\\\"Zones end\\\", 10)\\\
end\\\
\\\
function cfxZones.keysForTable(theTable)\\\
    local keyset={}\\\
    local n=0\\\
\\\
    for k,v in pairs(tab) do\\\
        n=n+1\\\
        keyset[n]=k\\\
    end\\\
    return keyset\\\
end\\\
\\\
\\\
--\\\
-- return all zones that have a specific named property\\\
--\\\
function cfxZones.zonesWithProperty(propertyName, searchSet)\\\
    if not searchSet then searchSet = cfxZones.zones end \\\
    local theZones = {}\\\
    for k, aZone in pairs(searchSet) do \\\
        if not aZone then \\\
            trigger.action.outText(\\\"+++zone: nil aZone for \\\" .. k, 30)\\\
        else \\\
            local lU = cfxZones.getZoneProperty(aZone, propertyName)\\\
            if lU then \\\
                table.insert(theZones, aZone)\\\
            end\\\
        end\\\
    end    \\\
    return theZones\\\
end\\\
\\\
--\\\
-- return all zones from the zone table that begin with string prefix\\\
--\\\
function cfxZones.zonesStartingWithName(prefix, searchSet)\\\
    if not searchSet then searchSet = cfxZones.zones end \\\
    local prefixZones = {}\\\
    prefix = prefix:upper() -- all zones have UPPERCASE NAMES! THEY SCREAM AT YOU\\\
    for name, zone in pairs(searchSet) do\\\
        if dcsCommon.stringStartsWith(name:upper(), prefix) then\\\
            prefixZones[name] = zone -- note: ref copy!\\\
        end\\\
    end\\\
    \\\
    return prefixZones\\\
end\\\
\\\
--\\\
-- return all zones from the zone table that begin with the string or set of strings passed in prefix \\\
-- if you pass 'true' as second (optional) parameter, it will first look for all zones that begin\\\
-- with '+' and return only those. Use during debugging to force finding a specific zone\\\
--\\\
function cfxZones.zonesStartingWith(prefix, searchSet, debugging)\\\
    -- you can force zones by having their name start with \\\"+\\\"\\\
    -- which will force them to return immediately if debugging is true for this call\\\
\\\
    if (debugging) then \\\
        local debugZones = cfxZones.zonesStartingWithName(\\\"+\\\", searchSet)\\\
        if not (next(debugZones) == nil) then -- # operator only works on array elements \\\
            --trigger.action.outText(\\\"returning zones with prefix <\\\" .. prefix .. \\\">\\\", 10)\\\
            return debugZones \\\
        end \\\
    end\\\
    \\\
    if (type(prefix) == \\\"string\\\") then \\\
        return cfxZones.zonesStartingWithName(prefix, searchSet)\\\
    end\\\
    \\\
    local allZones = {}\\\
    for i=1, #prefix do \\\
        -- iterate through all names in prefix set\\\
        local theName = prefix[i]\\\
        local newZones = cfxZones.zonesStartingWithName(theName, searchSet)\\\
        -- add them all to current table\\\
        for zName, zInfo in pairs(newZones) do \\\
            allZones[zName] = zInfo -- will also replace doublets\\\
        end\\\
    end\\\
    \\\
    return allZones\\\
end\\\
\\\
function cfxZones.getZoneByName(aName, searchSet) \\\
    if not searchSet then searchSet = cfxZones.zones end \\\
    aName = aName:upper()\\\
    return searchSet[aName] -- the joys of key value pairs\\\
end\\\
\\\
function cfxZones.getZonesContainingString(aString, searchSet) \\\
    if not searchSet then searchSet = cfxZones.zones end\\\
    aString = string.upper(aString)\\\
    resultSet = {}\\\
    for zName, zData in pairs(searchSet) do \\\
        if aString == string.upper(zData.name) then \\\
            resultSet[zName] = zData\\\
        end\\\
    end\\\
    \\\
end;\\\
\\\
-- filter zones by range to a point. returns indexed set\\\
function cfxZones.getZonesInRange(point, range, theZones)\\\
    if not theZones then theZones = cfxZones.zones end\\\
    \\\
    local inRangeSet = {}\\\
    for zName, zData in pairs (theZones) do \\\
        if dcsCommon.dist(point, zData.point) < range then \\\
            table.insert(inRangeSet, zData)\\\
        end\\\
    end\\\
    return inRangeSet \\\
end\\\
\\\
-- get closest zone returns the zone that is closest to point \\\
function cfxZones.getClosestZone(point, theZones)\\\
    if not theZones then theZones = cfxZones.zones end\\\
    local lPoint = {x=point.x, y=0, z=point.z}\\\
    local currDelta = math.huge \\\
    local closestZone = nil\\\
    for zName, zData in pairs(theZones) do \\\
        local zPoint = cfxZones.getPoint(zData)\\\
        local delta = dcsCommon.dist(lPoint, zPoint) -- emulate flag compare \\\
        if (delta < currDelta) then \\\
            currDelta = delta\\\
            closestZone = zData\\\
        end\\\
    end\\\
    return closestZone, currDelta \\\
end\\\
\\\
function dmlZone:getClosestZone(theZones)\\\
    local closestZone, currDelta = cfxZones.getClosestZone(self:getPoint(), theZones)\\\
    return closestZone, currDelta \\\
end\\\
\\\
-- return a random zone from the table passed in zones\\\
function cfxZones.pickRandomZoneFrom(zones)\\\
    if not zones then zones = cfxZones.zones end\\\
    local indexedZones = dcsCommon.enumerateTable(zones)\\\
    local r = math.random(#indexedZones)\\\
    return indexedZones[r]\\\
end\\\
\\\
-- return an zone element by index \\\
function cfxZones.getZoneByIndex(theZones, theIndex) \\\
    local enumeratedZones = dcsCommon.enumerateTable(theZones)\\\
    if (theIndex > #enumeratedZones) then\\\
        trigger.action.outText(\\\"WARNING: zone index \\\" .. theIndex .. \\\" out of bounds - max = \\\" .. #enumeratedZones, 30)\\\
        return nil end\\\
    if (theIndex < 1) then return nil end\\\
    \\\
    return enumeratedZones[theIndex]\\\
end\\\
\\\
-- place a smoke marker in center of zone, offset by dx, dy \\\
function cfxZones.markZoneWithSmoke(theZone, dx, dz, smokeColor, alt)\\\
    if not alt then alt = 5 end \\\
    local point = cfxZones.getPoint(theZone) --{} -- theZone.point\\\
    point.x = point.x + dx -- getpoint updates and returns copy \\\
    point.z = point.z + dz \\\
    -- get height at point \\\
    point.y = land.getHeight({x = point.x, y = point.z}) + alt\\\
    -- height-correct\\\
    --local newPoint= {x = point.x, y = land.getHeight({x = point.x, y = point.z}) + 3, z= point.z}\\\
    trigger.action.smoke(point, smokeColor)\\\
end\\\
\\\
function dmlZone:markZoneWithSmoke(dx, dz, smokeColor, alt)\\\
    cfxZones.markZoneWithSmoke(self, dx, dz, smokeColor, alt)\\\
end\\\
\\\
-- place a smoke marker in center of zone, offset by radius and degrees \\\
function cfxZones.markZoneWithSmokePolar(theZone, radius, degrees, smokeColor, alt)\\\
    local rads = degrees * math.pi / 180\\\
    local dx = radius * math.sin(rads)\\\
    local dz = radius * math.cos(rads)\\\
    cfxZones.markZoneWithSmoke(theZone, dx, dz, smokeColor, alt)\\\
end\\\
\\\
function dmlZone:markZoneWithSmokePolar(radius, degrees, smokeColor, alt)\\\
    cfxZones.markZoneWithSmokePolar(self, radius, degrees, smokeColor, alt)\\\
end\\\
\\\
-- place a smoke marker in center of zone, offset by radius and randomized degrees \\\
function cfxZones.markZoneWithSmokePolarRandom(theZone, radius, smokeColor)\\\
    local degrees = math.random(360)\\\
    cfxZones.markZoneWithSmokePolar(theZone, radius, degrees, smokeColor)\\\
end\\\
\\\
function dmlZone:markZoneWithSmokePolarRandom(radius, smokeColor)\\\
    local degrees = math.random(360)\\\
    self:markZoneWithSmokePolar(radius, degrees, smokeColor)\\\
end\\\
\\\
function cfxZones.pointInOneOfZones(thePoint, zoneArray, useOrig) \\\
    if not zoneArray then zoneArray = cfxZones.zones end \\\
    for idx, theZone in pairs(zoneArray) do \\\
        local isIn, percent, dist = cfxZones.pointInZone(thePoint, theZone, useOrig)\\\
        if isIn then return isIn, percent, dist, theZone end \\\
    end\\\
    return false, 0, 0, nil \\\
end\\\
\\\
\\\
-- unitInZone returns true if theUnit is inside the zone \\\
-- the second value returned is the percentage of distance\\\
-- from center to rim, with 100% being entirely in center, 0 = outside\\\
-- the third value returned is the distance to center\\\
function cfxZones.pointInZone(thePoint, theZone, useOrig)\\\
\\\
    if not (theZone) then return false, 0, 0 end\\\
        \\\
    local pflat = {x = thePoint.x, y = 0, z = thePoint.z}\\\
    \\\
    local zpoint \\\
    if useOrig then\\\
        zpoint = cfxZones.getDCSOrigin(theZone)\\\
    else \\\
        zpoint = cfxZones.getPoint(theZone) -- updates zone if linked \\\
    end\\\
    local ppoint = thePoint -- xyz\\\
    local pflat = {x = ppoint.x, y = 0, z = ppoint.z}\\\
    local dist = dcsCommon.dist(zpoint, pflat)\\\
    \\\
    if theZone.isCircle then \\\
        if theZone.radius <= 0 then \\\
            return false, 0, 0\\\
        end\\\
\\\
        local success = dist < theZone.radius\\\
        local percentage = 0\\\
        if (success) then \\\
            percentage = 1 - dist / theZone.radius \\\
        end\\\
        return success, percentage, dist \\\
    \\\
    elseif theZone.isPoly then\\\
        local success = cfxZones.isPointInsidePoly(pflat, theZone.poly)\\\
        return success, 0, dist\\\
    else \\\
        trigger.action.outText(\\\"pointInZone: Unknown zone type for \\\" .. theZone.name, 10)\\\
    end\\\
\\\
    return false\\\
end\\\
\\\
function dmlZone:pointInZone(thePoint, useOrig)\\\
    return cfxZones.pointInZone(thePoint, self, useOrig)\\\
end\\\
\\\
\\\
function cfxZones.unitInZone(theUnit, theZone)\\\
    if not (theUnit) then return false, 0, 0 end\\\
    if not (theUnit:isExist()) then return false, 0, 0 end\\\
    -- force zone update if it is linked to another zone \\\
    -- pointInZone does update\\\
    local thePoint = theUnit:getPoint()\\\
    return cfxZones.pointInZone(thePoint, theZone)\\\
end\\\
\\\
function dmlZone:unitInZone(theUnit)\\\
    if not (theUnit) then return false, 0, 0 end\\\
    if not (theUnit:isExist()) then return false, 0, 0 end\\\
    -- force zone update if it is linked to another zone \\\
    -- pointInZone does update\\\
    local thePoint = theUnit:getPoint()\\\
    return self:pointInZone(thePoint)\\\
end\\\
\\\
-- returns all units of the input set that are inside the zone \\\
function cfxZones.unitsInZone(theUnits, theZone)\\\
    if not theUnits then return {} end\\\
    if not theZone then return {} end\\\
    \\\
    local zoneUnits = {}\\\
    for index, aUnit in pairs(theUnits) do \\\
        if cfxZones.unitInZone(aUnit, theZone) then \\\
            table.insert( zoneUnits, aUnit)\\\
        end\\\
    end\\\
    return zoneUnits\\\
end\\\
\\\
function dmlZone:unitsInZone(theUnits)\\\
    if not theUnits then return {} end\\\
    local zoneUnits = {}\\\
    for index, aUnit in pairs(theUnits) do \\\
        if self:unitInZone(aUnit) then \\\
            table.insert(zoneUnits, aUnit)\\\
        end\\\
    end\\\
    return zoneUnits\\\
end\\\
\\\
function cfxZones.closestUnitToZoneCenter(theUnits, theZone)\\\
    -- does not care if they really are in zone. call unitsInZone first\\\
    -- if you need to have them filtered\\\
    -- theUnits MUST BE ARRAY\\\
    if not theUnits then return nil end\\\
    if #theUnits == 0 then return nil end\\\
    local closestUnit = theUnits[1]\\\
    local zP = cfxZones.getPoint(theZone)\\\
    local smallestDist = math.huge\\\
    for i=2, #theUnits do\\\
        local aUnit = theUnits[i]\\\
        local currDist = dcsCommon.dist(zP, aUnit:getPoint())\\\
        if smallestDist > currDelta then \\\
            closestUnit = aUnit\\\
            smallestDist = currDist\\\
        end\\\
    end\\\
    return closestUnit\\\
end\\\
\\\
function dmlZone:closestUnitToZoneCenter(theUnits)\\\
    return cfxZones.closestUnitToZoneCenter(theUnits, self)\\\
end\\\
\\\
-- grow zone\\\
function cfxZones.growZone()\\\
    -- circular zones simply increase radius\\\
    -- poly zones: not defined \\\
    \\\
end\\\
\\\
\\\
-- creating units in a zone\\\
function cfxZones.createGroundUnitsInZoneForCoalition (theCoalition, groupName, theZone, theUnits, formation, heading, liveries) \\\
    -- theUnits can be string or table of string \\\
    if not groupName then groupName = \\\"G_\\\"..theZone.name end \\\
    -- group name will be taken from zone name and prependend with \\\"G_\\\"\\\
    local theGroup = dcsCommon.createGroundGroupWithUnits(groupName, theUnits, theZone.radius, nil, formation, nil, liveries)\\\
    \\\
    -- turn the entire formation to heading\\\
    if (not heading) then heading = 0 end\\\
    dcsCommon.rotateGroupData(theGroup, heading) -- currently, group is still at origin, no cx, cy\\\
    \\\
    \\\
    -- now move the group to center of theZone\\\
    dcsCommon.moveGroupDataTo(theGroup, \\\
                          theZone.point.x, \\\
                          theZone.point.z) -- watchit: Z!!!\\\
\\\
    -- create the group in the world and return it\\\
    -- first we need to translate the coalition to a legal \\\
    -- country. we use UN for neutral, cjtf for red and blue \\\
    local theSideCJTF = dcsCommon.coalition2county(theCoalition)\\\
    -- store cty and cat for later access. DCS doesn't need it, but we may \\\
    \\\
    theGroup.cty = theSideCJTF\\\
    theGroup.cat = Group.Category.GROUND\\\
    \\\
    -- create a copy of the group data for \\\
    -- later reference \\\
    local groupDataCopy = dcsCommon.clone(theGroup)\\\
\\\
    local newGroup = coalition.addGroup(theSideCJTF, Group.Category.GROUND, theGroup)\\\
    return newGroup, groupDataCopy\\\
end\\\
\\\
--\\\
-- ===============\\\
-- FLAG PROCESSING \\\
-- ===============\\\
--\\\
\\\
--\\\
-- Flag Pulling \\\
--\\\
function cfxZones.pulseFlag(theFlag, method, theZone)\\\
    local args = {}\\\
    args.theFlag = theFlag\\\
    args.method = method\\\
    args.theZone = theZone \\\
    local delay = 3\\\
    if dcsCommon.containsString(method, \\\",\\\") then \\\
        local parts = dcsCommon.splitString(method, \\\",\\\")\\\
        delay = parts[2]\\\
        if delay then delay = tonumber(delay) end  \\\
    end\\\
    if not delay then delay = 3 end \\\
    if theZone.verbose then \\\
        trigger.action.outText(\\\"+++zne: RAISING pulse t=\\\"..delay..\\\" for flag <\\\" .. theFlag .. \\\"> in zone <\\\" .. theZone.name ..\\\">\\\", 30)\\\
    end \\\
    local newVal = 1\\\
    cfxZones.setFlagValue(theFlag, newVal, theZone)\\\
    \\\
    -- schedule second half of pulse \\\
    timer.scheduleFunction(cfxZones.unPulseFlag, args, timer.getTime() + delay)\\\
end\\\
\\\
function dmlZone:pulseFlag(theFlag, method)\\\
    cfxZones.pulseFlag(theFlag, method, self)\\\
end\\\
\\\
function cfxZones.unPulseFlag(args)\\\
    local theZone = args.theZone\\\
    local method = args.method \\\
    local theFlag = args.theFlag \\\
    local newVal = 0\\\
    -- we may later use method to determine pulse direction / newVal\\\
    -- for now, we always go low \\\
    if theZone.verbose then \\\
        trigger.action.outText(\\\"+++zne: DOWNPULSE pulse for flag <\\\" .. theFlag .. \\\"> in zone <\\\" .. theZone.name ..\\\">\\\", 30)\\\
    end\\\
    cfxZones.setFlagValue(theFlag, newVal, theZone)\\\
end\\\
\\\
function cfxZones.evalRemainder(remainder, theZone)\\\
    local rNum = tonumber(remainder)\\\
    if not rNum then \\\
        -- we use remainder as name for flag \\\
        -- PROCESS ESCAPE SEQUENCES\\\
        local esc = string.sub(remainder, 1, 1)\\\
        local last = string.sub(remainder, -1)\\\
        if esc == \\\"@\\\" then \\\
            remainder = string.sub(remainder, 2)\\\
            remainder = dcsCommon.trim(remainder)\\\
        end\\\
        \\\
        if esc == \\\"(\\\" and last == \\\")\\\" and string.len(remainder) > 2 then \\\
            -- note: iisues with startswith(\\\"(\\\") ???\\\
            remainder = string.sub(remainder, 2, -2)\\\
            remainder = dcsCommon.trim(remainder)        \\\
        end\\\
        if esc == \\\"\\\\\\\"\\\" and last == \\\"\\\\\\\"\\\" and string.len(remainder) > 2 then \\\
            remainder = string.sub(remainder, 2, -2)\\\
            remainder = dcsCommon.trim(remainder)        \\\
        end\\\
        if cfxZones.verbose then \\\
            trigger.action.outText(\\\"+++zne: accessing flag <\\\" .. remainder .. \\\">\\\", 30)\\\
        end \\\
        rNum = cfxZones.getFlagValue(remainder, theZone)\\\
    end \\\
    return rNum\\\
end\\\
\\\
function dmlZone:evalRemainder(remainder)\\\
    return cfxZones.evalRemainder(remainder, self)\\\
end\\\
\\\
function cfxZones.doPollFlag(theFlag, method, theZone) -- no OOP equivalent\\\
    -- WARNING: \\\
    -- if method is a number string, it will be interpreted as follows:\\\
    -- positive number: set immediate \\\
    -- negative: decrement by amouint\\\
    if not theZone then \\\
        trigger.action.outText(\\\"+++zones: nil theZone on pollFlag\\\", 30)\\\
    end\\\
\\\
    local mt = type(method)\\\
    if mt == \\\"number\\\" then \\\
        method = \\\"#\\\" .. method -- convert to immediate \\\
        mt = \\\"string\\\"\\\
    elseif mt ~= \\\"string\\\" then \\\
        trigger.action.outText(\\\"+++zne: warning: zone <\\\" .. theZone.name .. \\\"> method type <\\\" .. mt .. \\\"> received. Ignoring\\\", 30)\\\
        return \\\
    end\\\
\\\
    local val = nil\\\
    method = method:lower()\\\
    method = dcsCommon.trim(method)\\\
    val = tonumber(method) -- see if val can be directly converted \\\
    if dcsCommon.stringStartsWith(method, \\\"+\\\") or \\\
       dcsCommon.stringStartsWith(method, \\\"-\\\") \\\
    then \\\
        -- skip this processing, a legal method can start with \\\"+\\\" or \\\"-\\\"\\\
        -- and we interpret it as a method to increase or decrease by amount\\\
    elseif (val ~= nil) then \\\
        -- provision to handle direct (positive) numbers (legacy support)\\\
        -- method can be converted to number but does not start with - or +\\\
        -- since all negative numbers start with '-' above guard will skip, positive will end up here\\\
        cfxZones.setFlagValue(theFlag, val, theZone)\\\
        if cfxZones.verbose or theZone.verbose then\\\
            trigger.action.outText(\\\"+++zones: flag <\\\" .. theFlag .. \\\"> changed to #\\\" .. val, 30)\\\
        end \\\
        return\\\
    else \\\
    end\\\
\\\
    if dcsCommon.stringStartsWith(method, \\\"#\\\") then \\\
        -- immediate value command. remove # and eval remainder \\\
        local remainder = dcsCommon.removePrefix(method, \\\"#\\\")\\\
        val = cfxZones.evalRemainder(remainder) -- always returens a number\\\
        cfxZones.setFlagValue(theFlag, val, theZone)\\\
        if theZone.verbose then \\\
            trigger.action.outText(\\\"+++zones: poll setting immediate <\\\" .. theFlag .. \\\"> in <\\\" .. theZone.name .. \\\"> to <\\\" .. val .. \\\">\\\", 30)\\\
        end\\\
        return \\\
    end\\\
    \\\
    local currVal = cfxZones.getFlagValue(theFlag, theZone)\\\
    if method == \\\"inc\\\" or method == \\\"f+1\\\" then \\\
        --trigger.action.setUserFlag(theFlag, currVal + 1)\\\
        cfxZones.setFlagValue(theFlag, currVal+1, theZone)\\\
        \\\
    elseif method == \\\"dec\\\" or method == \\\"f-1\\\" then \\\
        -- trigger.action.setUserFlag(theFlag, currVal - 1)\\\
        cfxZones.setFlagValue(theFlag, currVal-1, theZone)\\\
\\\
    elseif method == \\\"off\\\" or method == \\\"f=0\\\" then \\\
        -- trigger.action.setUserFlag(theFlag, 0)\\\
        cfxZones.setFlagValue(theFlag, 0, theZone)\\\
\\\
    elseif method == \\\"flip\\\" or method == \\\"xor\\\" then \\\
        if currVal ~= 0 then \\\
--            trigger.action.setUserFlag(theFlag, 0)\\\
            cfxZones.setFlagValue(theFlag, 0, theZone)\\\
\\\
        else \\\
            --trigger.action.setUserFlag(theFlag, 1)\\\
            cfxZones.setFlagValue(theFlag, 1, theZone)\\\
        end\\\
        \\\
    elseif dcsCommon.stringStartsWith(method, \\\"pulse\\\") then \\\
        cfxZones.pulseFlag(theFlag, method, theZone)\\\
        \\\
    elseif dcsCommon.stringStartsWith(method, \\\"+\\\") then \\\
        -- we add whatever is to the right \\\
        local remainder = dcsCommon.removePrefix(method, \\\"+\\\")\\\
        local adder = cfxZones.evalRemainder(remainder)\\\
        cfxZones.setFlagValue(theFlag, currVal+adder, theZone)\\\
        if theZone.verbose then \\\
            trigger.action.outText(\\\"+++zones: (poll) updating with '+' flag <\\\" .. theFlag .. \\\"> in <\\\" .. theZone.name .. \\\"> by <\\\" .. adder .. \\\"> to <\\\" .. adder + currVal .. \\\">\\\", 30)\\\
        end\\\
        \\\
    elseif dcsCommon.stringStartsWith(method, \\\"-\\\") then \\\
        -- we subtract whatever is to the right \\\
        local remainder = dcsCommon.removePrefix(method, \\\"-\\\")\\\
        local adder = cfxZones.evalRemainder(remainder)\\\
        cfxZones.setFlagValue(theFlag, currVal-adder, theZone)\\\
\\\
    else \\\
        if method ~= \\\"on\\\" and method ~= \\\"f=1\\\" then \\\
            trigger.action.outText(\\\"+++zones: unknown method <\\\" .. method .. \\\"> - using 'on'\\\", 30)\\\
        end\\\
        -- default: on.\\\
--        trigger.action.setUserFlag(theFlag, 1)\\\
        cfxZones.setFlagValue(theFlag, 1, theZone)\\\
    end\\\
    \\\
    if cfxZones.verbose then\\\
        local newVal = cfxZones.getFlagValue(theFlag, theZone)\\\
        trigger.action.outText(\\\"+++zones: flag <\\\" .. theFlag .. \\\"> changed from \\\" .. currVal .. \\\" to \\\" .. newVal, 30)\\\
    end \\\
end\\\
\\\
function cfxZones.pollFlag(theFlag, method, theZone) \\\
    --trigger.action.outText(\\\"enter pollflag for flag <\\\" .. theFlag .. \\\"> of zone <\\\" .. theZone.name .. \\\">\\\", 30)\\\
    local allFlags = {}\\\
    if dcsCommon.containsString(theFlag, \\\",\\\") then \\\
        if cfxZones.verbose then \\\
            trigger.action.outText(\\\"+++zones: will poll flag set <\\\" .. theFlag .. \\\"> with \\\" .. method, 30)\\\
        end\\\
        allFlags = dcsCommon.splitString(theFlag, \\\",\\\")\\\
    else \\\
        table.insert(allFlags, theFlag)\\\
    end\\\
    \\\
    for idx, aFlag in pairs(allFlags) do \\\
        aFlag = dcsCommon.trim(aFlag)\\\
        -- note: mey require range preprocessing, but that's not\\\
        -- a priority \\\
        cfxZones.doPollFlag(aFlag, method, theZone)\\\
    end \\\
end\\\
\\\
function dmlZone:pollFlag(theFlag, method)\\\
    cfxZones.pollFlag(theFlag, method, self)\\\
end\\\
\\\
function cfxZones.expandFlagName(theFlag, theZone) \\\
    if not theFlag then return \\\"!NIL\\\" end \\\
    local zoneName = \\\"<dummy>\\\"\\\
    if theZone then \\\
        zoneName = theZone.name -- for flag wildcards\\\
    end\\\
    \\\
    if type(theFlag) == \\\"number\\\" then \\\
        -- straight number, return \\\
        return theFlag\\\
    end\\\
    \\\
    -- we assume it's a string now\\\
    theFlag = dcsCommon.trim(theFlag) -- clear leading/trailing spaces\\\
    local nFlag = tonumber(theFlag) \\\
    if nFlag then -- a number, legal\\\
        return theFlag\\\
    end\\\
        \\\
    -- now do wildcard processing. we have alphanumeric\\\
    if dcsCommon.stringStartsWith(theFlag, \\\"*\\\") then  \\\
        theFlag = zoneName .. theFlag\\\
    end\\\
    return theFlag\\\
end\\\
\\\
function dmlZone:setFlagValue(theFlag, theValue)\\\
    cfxZones.setFlagValueMult(theFlag, theValue, self)\\\
end\\\
\\\
function cfxZones.setFlagValue(theFlag, theValue, theZone)\\\
    cfxZones.setFlagValueMult(theFlag, theValue, theZone)\\\
end\\\
\\\
function cfxZones.setFlagValueMult(theFlag, theValue, theZone)\\\
    local allFlags = {}\\\
    if dcsCommon.containsString(theFlag, \\\",\\\") then \\\
        if cfxZones.verbose then \\\
            trigger.action.outText(\\\"+++zones: will multi-set flags <\\\" .. theFlag .. \\\"> to \\\" .. theValue, 30)\\\
        end\\\
        allFlags = dcsCommon.splitString(theFlag, \\\",\\\")\\\
    else \\\
        table.insert(allFlags, theFlag)\\\
    end\\\
    \\\
    for idx, aFlag in pairs(allFlags) do \\\
        aFlag = dcsCommon.trim(aFlag)\\\
        -- note: mey require range preprocessing, but that's not\\\
        -- a priority \\\
        cfxZones.doSetFlagValue(aFlag, theValue, theZone)\\\
    end \\\
end\\\
\\\
function cfxZones.doSetFlagValue(theFlag, theValue, theZone)\\\
    local zoneName = \\\"<dummy>\\\"\\\
    if not theZone then \\\
        trigger.action.outText(\\\"+++Zne: no zone on setFlagValue\\\", 30) -- mod me for detector\\\
    else \\\
        zoneName = theZone.name -- for flag wildcards\\\
    end\\\
    \\\
    if type(theFlag) == \\\"number\\\" then \\\
        -- straight set, oldschool ME flag \\\
        trigger.action.setUserFlag(theFlag, theValue)\\\
        return \\\
    end\\\
    \\\
    -- we assume it's a string now\\\
    theFlag = dcsCommon.trim(theFlag) -- clear leading/trailing spaces    \\\
    -- some QoL: detect \\\"<none>\\\"\\\
    if dcsCommon.containsString(theFlag, \\\"<none>\\\") then \\\
        trigger.action.outText(\\\"+++Zone: warning - setFlag has '<none>' flag name in zone <\\\" .. zoneName .. \\\">\\\", 30) -- if error, intended break\\\
    end\\\
    \\\
    -- now do wildcard processing. we have alphanumeric\\\
    if dcsCommon.stringStartsWith(theFlag, \\\"*\\\") then  \\\
        theFlag = zoneName .. theFlag\\\
    end\\\
    trigger.action.setUserFlag(theFlag, theValue)\\\
end \\\
\\\
\\\
\\\
function cfxZones.getFlagValue(theFlag, theZone)\\\
    local zoneName = \\\"<dummy>\\\"\\\
    if not theZone or not theZone.name then \\\
        trigger.action.outText(\\\"+++Zne: no zone or zone name on getFlagValue\\\", 30)\\\
    else \\\
        zoneName = theZone.name -- for flag wildcards\\\
    end\\\
    \\\
    if type(theFlag) == \\\"number\\\" then \\\
        -- straight get, ME flag \\\
        return tonumber(trigger.misc.getUserFlag(theFlag))\\\
    end\\\
    \\\
    -- we assume it's a string now\\\
    theFlag = dcsCommon.trim(theFlag) -- clear leading/trailing spaces\\\
    local nFlag = tonumber(theFlag) \\\
    if nFlag then \\\
        return tonumber(trigger.misc.getUserFlag(theFlag))\\\
    end\\\
    \\\
    -- some QoL: detect \\\"<none>\\\"\\\
    if dcsCommon.containsString(theFlag, \\\"<none>\\\") then \\\
        trigger.action.outText(\\\"+++Zone: warning - getFlag has '<none>' flag name in zone <\\\" .. zoneName .. \\\">\\\", 30) -- break here\\\
    end\\\
    \\\
    -- now do wildcard processing. we have alphanumeric\\\
    if dcsCommon.stringStartsWith(theFlag, \\\"*\\\") then  \\\
            theFlag = zoneName .. theFlag\\\
    end\\\
    return tonumber(trigger.misc.getUserFlag(theFlag))\\\
end\\\
\\\
function dmlZone:getFlagValue(theFlag)\\\
    return cfxZones.getFlagValue(theFlag, self)\\\
end\\\
\\\
function cfxZones.verifyMethod(theMethod, theZone)\\\
    local lMethod = string.lower(theMethod)\\\
    if lMethod == \\\"#\\\" or lMethod == \\\"change\\\" then \\\
        return true\\\
    end\\\
\\\
    if lMethod == \\\"0\\\" or lMethod == \\\"no\\\" or lMethod == \\\"false\\\" \\\
       or lMethod == \\\"off\\\" then \\\
        return true  \\\
    end\\\
    \\\
    if lMethod == \\\"1\\\" or lMethod == \\\"yes\\\" or lMethod == \\\"true\\\" \\\
       or lMethod == \\\"on\\\" then \\\
        return true  \\\
    end\\\
    \\\
    if lMethod == \\\"inc\\\" or lMethod == \\\"+1\\\" then \\\
        return true\\\
    end\\\
    \\\
    if lMethod == \\\"dec\\\" or lMethod == \\\"-1\\\" then \\\
        return true \\\
    end \\\
    \\\
    if lMethod == \\\"lohi\\\" or lMethod == \\\"pulse\\\" then \\\
        return true\\\
    end\\\
    \\\
    if lMethod == \\\"hilo\\\" then \\\
        return true\\\
    end\\\
    \\\
    -- number constraints\\\
    -- or flag constraints     -- ONLY RETURN TRUE IF CHANGE AND CONSTRAINT MET \\\
    local op = string.sub(theMethod, 1, 1) \\\
    local remainder = string.sub(theMethod, 2)\\\
    remainder = dcsCommon.trim(remainder) -- remove all leading and trailing spaces\\\
\\\
    if true then \\\
        -- we have a comparison = \\\">\\\", \\\"=\\\", \\\"<\\\" followed by a number \\\
        -- THEY TRIGGER EACH TIME lastVal <> currVal AND condition IS MET  \\\
        if op == \\\"=\\\" then \\\
            return true\\\
        end\\\
        \\\
        if op == \\\"#\\\" or op == \\\"~\\\" then \\\
            return true\\\
        end \\\
        \\\
        if op == \\\"<\\\" then \\\
            return true\\\
        end\\\
        \\\
        if op == \\\">\\\" then \\\
            return true\\\
        end\\\
    end\\\
    \\\
    return false \\\
end\\\
\\\
function dmlZone:verifyMethod(theMethod)\\\
    return cfxZones.verifyMethod(theMethod, self)\\\
end\\\
\\\
-- method-based flag testing \\\
function cfxZones.evalFlagMethodImmediate(currVal, theMethod, theZone)\\\
    -- immediate eval - does not look at last val. \\\
    -- return true/false/value based on theMethod's contraints \\\
    -- simple constraints\\\
    local lMethod = string.lower(theMethod)\\\
    if lMethod == \\\"#\\\" or lMethod == \\\"change\\\" then \\\
        -- ALWAYS RETURNS TRUE for currval <> 0, flase if currval = 0\\\
        return currVal ~= 0  \\\
    end\\\
    \\\
    if lMethod == \\\"0\\\" or lMethod == \\\"no\\\" or lMethod == \\\"false\\\" \\\
       or lMethod == \\\"off\\\" then \\\
        -- WARNING: ALWAYS RETURNS FALSE\\\
        return false  \\\
    end\\\
    \\\
    if lMethod == \\\"1\\\" or lMethod == \\\"yes\\\" or lMethod == \\\"true\\\" \\\
       or lMethod == \\\"on\\\" then \\\
        -- WARNING: ALWAYS RETURNS TRUE\\\
        return true  \\\
    end\\\
    \\\
    if lMethod == \\\"inc\\\" or lMethod == \\\"+1\\\" then \\\
        return currVal+1 -- this may be unexpected\\\
    end\\\
    \\\
    if lMethod == \\\"dec\\\" or lMethod == \\\"-1\\\" then \\\
        return currVal-1 -- this may be unexpectd\\\
    end \\\
    \\\
    -- number constraints\\\
    -- or flag constraints \\\
    -- ONLY RETURN TRUE IF CHANGE AND CONSTRAINT MET \\\
    local op = string.sub(theMethod, 1, 1) \\\
    local remainder = string.sub(theMethod, 2)\\\
    remainder = dcsCommon.trim(remainder) -- remove all leading and trailing spaces\\\
    local rNum = tonumber(remainder)\\\
    if not rNum then \\\
        -- we use remainder as name for flag \\\
        -- PROCESS ESCAPE SEQUENCES\\\
        local esc = string.sub(remainder, 1, 1)\\\
        local last = string.sub(remainder, -1)\\\
        if esc == \\\"@\\\" then \\\
            remainder = string.sub(remainder, 2)\\\
            remainder = dcsCommon.trim(remainder)\\\
        end\\\
        \\\
        if esc == \\\"(\\\" and last == \\\")\\\" and string.len(remainder) > 2 then \\\
            -- note: iisues with startswith(\\\"(\\\") ???\\\
            remainder = string.sub(remainder, 2, -2)\\\
            remainder = dcsCommon.trim(remainder)        \\\
        end\\\
        if esc == \\\"\\\\\\\"\\\" and last == \\\"\\\\\\\"\\\" and string.len(remainder) > 2 then \\\
            remainder = string.sub(remainder, 2, -2)\\\
            remainder = dcsCommon.trim(remainder)        \\\
        end\\\
        if cfxZones.verbose then \\\
            trigger.action.outText(\\\"+++zne: accessing flag <\\\" .. remainder .. \\\">\\\", 30)\\\
        end \\\
        rNum = cfxZones.getFlagValue(remainder, theZone)\\\
    end \\\
    if rNum then \\\
        -- we have a comparison = \\\">\\\", \\\"=\\\", \\\"<\\\" followed by a number  \\\
        if op == \\\"=\\\" then \\\
            return currVal == rNum\\\
        end\\\
        \\\
        if op == \\\"#\\\" or op == \\\"~\\\" then \\\
            return currVal ~= rNum \\\
        end \\\
        \\\
        if op == \\\"<\\\" then \\\
            return currVal < rNum\\\
        end\\\
        \\\
        if op == \\\">\\\" then \\\
            return currVal > rNum\\\
        end\\\
    end\\\
    \\\
    -- if we get here, we have an error \\\
    local zoneName = \\\"<NIL>\\\"\\\
    if theZone then zoneName = theZone.name end \\\
    trigger.action.outText(\\\"+++Zne: illegal |\\\" .. theMethod .. \\\"| in eval for zone \\\" .. zoneName, 30 )\\\
    return false     \\\
end\\\
\\\
function dmlZone:evalFlagMethodImmediate(currVal, theMethod, theZone)\\\
    return cfxZones.evalFlagMethodImmediate(currVal, theMethod, self)\\\
end\\\
\\\
\\\
function cfxZones.testFlagByMethodForZone(currVal, lastVal, theMethod, theZone)\\\
    -- return true/false based on theMethod's contraints \\\
    -- simple constraints\\\
    -- ONLY RETURN TRUE IF CHANGE AND CONSTRAINT MET \\\
    local lMethod = string.lower(theMethod)\\\
    if lMethod == \\\"#\\\" or lMethod == \\\"change\\\" then \\\
        -- check if currVal different from lastVal\\\
        return currVal ~= lastVal  \\\
    end\\\
    \\\
    if lMethod == \\\"0\\\" or lMethod == \\\"no\\\" or lMethod == \\\"false\\\" \\\
       or lMethod == \\\"off\\\" then \\\
        -- WARNING: ONLY RETURNS TRUE IF FALSE AND lastval not zero!\\\
        return currVal == 0 and currVal ~= lastVal  \\\
    end\\\
    \\\
    if lMethod == \\\"1\\\" or lMethod == \\\"yes\\\" or lMethod == \\\"true\\\" \\\
       or lMethod == \\\"on\\\" then \\\
        -- WARNING: only returns true if lastval was false!!!!\\\
        return (currVal ~= 0 and lastVal == 0)  \\\
    end\\\
    \\\
    if lMethod == \\\"inc\\\" or lMethod == \\\"+1\\\" then \\\
--        return currVal == lastVal+1 -- better: test for greater than \\\
        return currVal > lastVal\\\
    end\\\
    \\\
    if lMethod == \\\"dec\\\" or lMethod == \\\"-1\\\" then \\\
        --return currVal == lastVal-1\\\
        return currVal < lastVal \\\
    end \\\
    \\\
    if lMethod == \\\"lohi\\\" or lMethod == \\\"pulse\\\" then \\\
        return (lastVal <= 0 and currVal > 0)\\\
    end\\\
    \\\
    if lMethod == \\\"hilo\\\" then \\\
        return (lastVal > 0 and currVal <= 0)\\\
    end\\\
    \\\
    -- number constraints\\\
    -- or flag constraints \\\
    -- ONLY RETURN TRUE IF CHANGE AND CONSTRAINT MET \\\
    local op = string.sub(theMethod, 1, 1) \\\
    local remainder = string.sub(theMethod, 2)\\\
    remainder = dcsCommon.trim(remainder) -- remove all leading and trailing spaces\\\
    local rNum = tonumber(remainder)\\\
    if not rNum then \\\
        -- we use remainder as name for flag \\\
        -- PROCESS ESCAPE SEQUENCES\\\
        local esc = string.sub(remainder, 1, 1)\\\
        local last = string.sub(remainder, -1)\\\
        if esc == \\\"@\\\" then \\\
            remainder = string.sub(remainder, 2)\\\
            remainder = dcsCommon.trim(remainder)\\\
        end\\\
        \\\
        if esc == \\\"(\\\" and last == \\\")\\\" and string.len(remainder) > 2 then \\\
            -- note: iisues with startswith(\\\"(\\\") ???\\\
            remainder = string.sub(remainder, 2, -2)\\\
            remainder = dcsCommon.trim(remainder)        \\\
        end\\\
        if esc == \\\"\\\\\\\"\\\" and last == \\\"\\\\\\\"\\\" and string.len(remainder) > 2 then \\\
            remainder = string.sub(remainder, 2, -2)\\\
            remainder = dcsCommon.trim(remainder)        \\\
        end\\\
        if cfxZones.verbose then \\\
            trigger.action.outText(\\\"+++zne: accessing flag <\\\" .. remainder .. \\\">\\\", 30)\\\
        end \\\
        rNum = cfxZones.getFlagValue(remainder, theZone)\\\
    end \\\
    if rNum then \\\
        -- we have a comparison = \\\">\\\", \\\"=\\\", \\\"<\\\" followed by a number \\\
        -- THEY TRIGGER EACH TIME lastVal <> currVal AND condition IS MET  \\\
        if op == \\\"=\\\" then \\\
            return currVal == rNum and lastVal ~= currVal\\\
        end\\\
        \\\
        if op == \\\"#\\\" or op == \\\"~\\\" then \\\
            return currVal ~= rNum and lastVal ~= currVal \\\
        end \\\
        \\\
        if op == \\\"<\\\" then \\\
            return currVal < rNum and lastVal ~= currVal\\\
        end\\\
        \\\
        if op == \\\">\\\" then \\\
            return currVal > rNum and lastVal ~= currVal\\\
        end\\\
    end\\\
    \\\
    -- if we get here, we have an error \\\
    local zoneName = \\\"<NIL>\\\"\\\
    if theZone then zoneName = theZone.name end \\\
    trigger.action.outText(\\\"+++Zne: illegal method constraints |\\\" .. theMethod .. \\\"| for zone \\\" .. zoneName, 30 )\\\
    return false \\\
end\\\
\\\
-- WARNING: testZoneFlag must also support non-dmlZone!!!\\\
function cfxZones.testZoneFlag(theZone, theFlagName, theMethod, latchName)\\\
    -- returns two values: true/false method result, and curr value\\\
    -- returns true if method constraints are met for flag theFlagName\\\
    -- as defined by theMethod \\\
    if not theMethod then \\\
        theMethod = \\\"change\\\"\\\
    end \\\
    \\\
    -- will read and update theZone[latchName] as appropriate \\\
    if not theZone then \\\
        trigger.action.outText(\\\"+++Zne: no zone for testZoneFlag\\\", 30)\\\
        return nil, nil \\\
    end \\\
    if not theFlagName then \\\
        -- this is common, no error, only on verbose \\\
        if cfxZones.verbose then \\\
            trigger.action.outText(\\\"+++Zne: no flagName for zone \\\" .. theZone.name .. \\\" for testZoneFlag\\\", 30)\\\
        end \\\
        return nil, nil\\\
    end\\\
    if not latchName then \\\
        trigger.action.outText(\\\"+++Zne: no latchName for zone \\\" .. theZone.name .. \\\" for testZoneFlag\\\", 30)\\\
        return nil, nil \\\
    end\\\
    -- get current value \\\
    local currVal = cfxZones.getFlagValue(theFlagName, theZone)\\\
    \\\
    -- get last value from latch\\\
    local lastVal = theZone[latchName]\\\
    if not lastVal then \\\
        trigger.action.outText(\\\"+++Zne: latch <\\\" .. latchName .. \\\"> not valid for zone \\\" .. theZone.name, 30) -- intentional break here \\\
        return nil, nil\\\
    end\\\
    \\\
    -- now, test by method \\\
    -- we should only test if currVal <> lastVal \\\
    if currVal == lastVal then\\\
        return false, currVal\\\
    end \\\
    \\\
    local testResult = cfxZones.testFlagByMethodForZone(currVal, lastVal, theMethod, theZone)\\\
\\\
    -- update latch by method\\\
    theZone[latchName] = currVal \\\
\\\
    -- return result\\\
    return testResult, currVal\\\
end\\\
\\\
function dmlZone:testZoneFlag(theFlagName, theMethod, latchName)\\\
    local r, v = cfxZones.testZoneFlag(self, theFlagName, theMethod, latchName)\\\
    return r, v \\\
end\\\
\\\
function cfxZones.numberArrayFromString(inString, default) -- bridge\\\
    return dcsCommon.numberArrayFromString(inString, default)\\\
end\\\
 \\\
\\\
function cfxZones.flagArrayFromString(inString) -- dcsCommon bridge \\\
    return dcsCommon.flagArrayFromString(inString)\\\
end\\\
\\\
\\\
--\\\
-- Drawing a Zone\\\
--\\\
\\\
function cfxZones.drawZone(theZone, lineColor, fillColor, markID)\\\
    if not theZone then return 0 end \\\
    if not lineColor then lineColor = {0.8, 0.8, 0.8, 1.0} end\\\
    if not fillColor then fillColor = {0.8, 0.8, 0.8, 0.0} end \\\
    if not markID then markID = dcsCommon.numberUUID() end \\\
    \\\
    if theZone.isCircle then \\\
        trigger.action.circleToAll(-1, markID, theZone.point, theZone.radius, lineColor, fillColor, 1, true, \\\"\\\")\\\
    else \\\
        local poly = theZone.poly\\\
        trigger.action.quadToAll(-1, markID, poly[4], poly[3], poly[2], poly[1], lineColor, fillColor, 1, true, \\\"\\\") -- note: left winding to get fill color\\\
    end\\\
    \\\
    return markID\\\
end\\\
\\\
function dmlZone:drawZone(lineColor, fillColor, markID)\\\
    return cfxZones.drawZone(self, lineColor, fillColor, markID)\\\
end\\\
\\\
function cfxZones.drawText(theZone, theText, fSize, lineColor, fillColor)\\\
    if not theZone then return end \\\
    if not fSize then fSize = 12 end \\\
    if not lineColor then lineColor = {0.8, 0.8, 0.8, 1.0} end\\\
    if not fillColor then fillColor = {0, 0, 0, 0} end \\\
    local markID = dcsCommon.numberUUID()\\\
    local p = theZone:getPoint()\\\
    local offset = {x = p.x, y = 0, z = p.z} \\\
    trigger.action.textToAll(-1, markID, offset, lineColor , fillColor , fSize, true , theText)\\\
    return markID\\\
end\\\
\\\
function dmlZone:drawText(theText, fSize, lineColor, fillColor)\\\
    return cfxZones.drawText(self, theText, fSize, lineColor, fillColor)\\\
end\\\
--\\\
-- ===================\\\
-- PROPERTY PROCESSING\\\
-- =================== \\\
--\\\
\\\
function cfxZones.getAllZoneProperties(theZone, caseInsensitive, numbersOnly) -- return as dict \\\
    if not caseInsensitive then caseInsensitive = false end \\\
    if not numbersOnly then numbersOnly = false end \\\
    if not theZone then return {} end \\\
    \\\
    local dcsProps = theZone.properties -- zone properties in dcs format \\\
    local props = {}\\\
    -- dcs has all properties as array with values .key and .value \\\
    -- so convert them into a dictionary \\\
    for i=1, #dcsProps do \\\
        local theProp = dcsProps[i]\\\
        local theKey = \\\"dummy\\\"\\\
        if string.len(theProp.key) > 0 then theKey = theProp.key end \\\
        if caseInsensitive then theKey = theKey:upper() end \\\
        local v = theProp.value \\\
        if numbersOnly then \\\
            v = tonumber(v)\\\
            if not v then v = 0 end \\\
        end\\\
        props[theKey] = v\\\
    end\\\
    return props \\\
end\\\
\\\
function dmlZone:getAllZoneProperties(caseInsensitive, numbersOnly)\\\
    return cfxZones.getAllZoneProperties(self, caseInsensitive, numbersOnly)\\\
end\\\
\\\
function cfxZones.extractPropertyFromDCS(theKey, theProperties)\\\
-- trim\\\
    theKey = dcsCommon.trim(theKey) \\\
--    make lower case conversion if not case sensitive\\\
    if not cfxZones.caseSensitiveProperties then \\\
        theKey = string.lower(theKey)\\\
    end\\\
\\\
-- iterate all keys and compare to what we are looking for     \\\
    for i=1, #theProperties do\\\
        local theP = theProperties[i]\\\
         \\\
        local existingKey = dcsCommon.trim(theP.key)  \\\
        if not cfxZones.caseSensitiveProperties then \\\
            existingKey = string.lower(existingKey)\\\
        end\\\
        if existingKey == theKey then \\\
            return theP.value\\\
        end\\\
        \\\
        -- now check after removing all blanks \\\
        existingKey = dcsCommon.removeBlanks(existingKey)\\\
        if existingKey == theKey then \\\
            return theP.value\\\
        end\\\
    end\\\
    return nil \\\
end\\\
\\\
function cfxZones.getZoneProperty(cZone, theKey)\\\
    if not cZone then \\\
        trigger.action.outText(\\\"+++zone: no zone in getZoneProperty\\\", 30)\\\
        return nil\\\
    end \\\
    if not theKey then \\\
        trigger.action.outText(\\\"+++zone: no property key in getZoneProperty for zone \\\" .. cZone.name, 30)\\\
        return \\\
    end    \\\
\\\
    local props = cZone.properties\\\
    local theVal = cfxZones.extractPropertyFromDCS(theKey, props)\\\
    return theVal\\\
end\\\
\\\
function dmlZone:getZoneProperty(theKey)\\\
    if not theKey then \\\
        trigger.action.outText(\\\"+++zone: no property key in OOP getZoneProperty for zone \\\" .. self.name, 30)\\\
        return nil  \\\
    end    \\\
    local props = self.properties\\\
    local theVal = cfxZones.extractPropertyFromDCS(theKey, props)\\\
    return theVal\\\
end\\\
\\\
\\\
function cfxZones.getStringFromZoneProperty(theZone, theProperty, default)\\\
    if not default then default = \\\"\\\" end\\\
-- OOP heavy duty test here\\\
    local p = theZone:getZoneProperty(theProperty)\\\
    if not p then return default end\\\
    if type(p) == \\\"string\\\" then \\\
        p = dcsCommon.trim(p)\\\
        if p == \\\"\\\" then p = default end \\\
        return p\\\
    end\\\
    return default -- warning. what if it was a number first?\\\
end\\\
\\\
function dmlZone:getStringFromZoneProperty(theProperty, default)\\\
    if not default then default = \\\"\\\" end\\\
    local p = self:getZoneProperty(theProperty)\\\
    if not p then return default end\\\
    if type(p) == \\\"string\\\" then \\\
        p = dcsCommon.trim(p)\\\
        if p == \\\"\\\" then p = default end \\\
        return p\\\
    end\\\
    return default -- warning. what if it was a number first?\\\
end\\\
\\\
function cfxZones.getMinMaxFromZoneProperty(theZone, theProperty)\\\
    local p = cfxZones.getZoneProperty(theZone, theProperty)\\\
    local theNumbers = dcsCommon.splitString(p, \\\" \\\")\\\
    return tonumber(theNumbers[1]), tonumber(theNumbers[2])\\\
end\\\
\\\
function dmlZone:getMinMaxFromZoneProperty(theProperty)\\\
    local p = self:getZoneProperty(theProperty)\\\
    local theNumbers = dcsCommon.splitString(p, \\\" \\\")\\\
    return tonumber(theNumbers[1]), tonumber(theNumbers[2])\\\
end\\\
\\\
function cfxZones.randomInRange(minVal, maxVal) -- should be moved to dcsCommon\\\
    if maxVal < minVal then \\\
        local t = minVal\\\
        minVal = maxVal \\\
        maxVal = t\\\
    end\\\
    return cfxZones.randomDelayFromPositiveRange(minVal, maxVal)\\\
end\\\
\\\
function cfxZones.randomDelayFromPositiveRange(minVal, maxVal) -- should be moved to dcsCommon \\\
    if not maxVal then return minVal end \\\
    if not minVal then return maxVal end \\\
    if minVal == maxVal then return minVal end \\\
    local delay = maxVal\\\
    if minVal >= 0 and minVal < delay then \\\
        -- we want a randomized from time from minTime .. delay\\\
        local varPart = delay - minVal + 1\\\
        varPart = dcsCommon.smallRandom(varPart) - 1\\\
        delay = minVal + varPart\\\
    end\\\
    return delay \\\
end\\\
\\\
function cfxZones.getPositiveRangeFromZoneProperty(theZone, theProperty, default, defaultmax)\\\
    -- reads property as string, and interprets as range 'a-b'. \\\
    -- if not a range but single number, returns both for upper and lower \\\
    --trigger.action.outText(\\\"***Zne: enter with <\\\" .. theZone.name .. \\\">: range for property <\\\" .. theProperty .. \\\">!\\\", 30)\\\
    if not default then default = 0 end \\\
    if not defaultmax then defaultmax = default end \\\
    \\\
    local lowerBound = default\\\
    local upperBound = defaultmax \\\
    \\\
    local rangeString = cfxZones.getStringFromZoneProperty(theZone, theProperty, \\\"\\\")\\\
    if dcsCommon.containsString(rangeString, \\\"-\\\") then \\\
        local theRange = dcsCommon.splitString(rangeString, \\\"-\\\")\\\
        lowerBound = theRange[1]\\\
        lowerBound = tonumber(lowerBound)\\\
        upperBound = theRange[2]\\\
        upperBound = tonumber(upperBound)\\\
        if lowerBound and upperBound then\\\
            -- swap if wrong order\\\
            if lowerBound > upperBound then \\\
                local temp = upperBound\\\
                upperBound = lowerBound\\\
                lowerBound = temp \\\
            end\\\
\\\
        else\\\
            -- bounds illegal\\\
            trigger.action.outText(\\\"+++Zne: illegal range  <\\\" .. rangeString .. \\\">, using \\\" .. default .. \\\"-\\\" .. defaultmax, 30)\\\
            lowerBound = default\\\
            upperBound = defaultmax \\\
        end\\\
    else \\\
        upperBound = cfxZones.getNumberFromZoneProperty(theZone, theProperty, defaultmax) -- between pulses \\\
        lowerBound = upperBound\\\
    end\\\
\\\
    return lowerBound, upperBound\\\
end\\\
\\\
function dmlZone:getPositiveRangeFromZoneProperty(theProperty, default, defaultmax)\\\
    local lo, up = cfxZones.getPositiveRangeFromZoneProperty(self, theProperty, default, defaultmax)\\\
    return lo, up \\\
end\\\
\\\
function cfxZones.getListFromZoneProperty(theZone, theProperty, defaultItem) -- comma delimited\\\
    if not defaultItem then defaultItem = \\\"default\\\" end \\\
    \\\
    local theString = theZone:getStringFromZoneProperty(theProperty, defaultItem)\\\
    if dcsCommon.containsString(theString, \\\",\\\") then \\\
        local theArray = dcsCommon.splitString(theString, ',')\\\
        theArray = dcsCommon.trimArray(theArray)\\\
        return theArray\\\
    else \\\
        return {theString}\\\
    end\\\
    \\\
    return nil \\\
end\\\
\\\
function dmlZone:getListFromZoneProperty(theProperty, defaultItem)\\\
    return cfxZones.getListFromZoneProperty(self, theProperty, defaultItem)\\\
end\\\
\\\
function cfxZones.hasProperty(theZone, theProperty) \\\
    if not theProperty then \\\
        trigger.action.outText(\\\"+++zne: WARNING - hasProperty called with nil theProperty for zone <\\\" .. theZone.name .. \\\">\\\", 30)\\\
        return false \\\
    end \\\
    local foundIt = cfxZones.getZoneProperty(theZone, theProperty)\\\
    if not foundIt then \\\
        -- check for possible forgotten or exchanged IO flags \\\
        if string.sub(theProperty, -1) == \\\"?\\\" then\\\
            local lessOp = theProperty:sub(1,-2)\\\
            if cfxZones.getZoneProperty(theZone, lessOp) ~= nil then \\\
                trigger.action.outText(\\\"*** NOTE: \\\" .. theZone.name .. \\\"'s property <\\\" .. lessOp .. \\\"> may be missing a Query ('?') symbol\\\", 30)\\\
            end\\\
            local lessPlus = lessOp .. \\\"!\\\"\\\
            if cfxZones.getZoneProperty(theZone, lessPlus) ~= nil then \\\
                trigger.action.outText(\\\"*** NOTE: \\\" .. theZone.name .. \\\"'s property <\\\" .. lessOp .. \\\"> may be using '!' instead of '?' for input\\\", 30)\\\
            end\\\
            return false \\\
        end\\\
        \\\
        if string.sub(theProperty, -1) == \\\"!\\\" then \\\
            local lessOp = theProperty:sub(1,-2)\\\
            if cfxZones.getZoneProperty(theZone, lessOp) ~= nil then \\\
                trigger.action.outText(\\\"*** NOTE: \\\" .. theZone.name .. \\\"'s property <\\\" .. lessOp .. \\\"> may be missing a Bang! ('!') symbol\\\", 30)\\\
            end\\\
            local lessPlus = lessOp .. \\\"?\\\"\\\
            if cfxZones.getZoneProperty(theZone, lessPlus) ~= nil then \\\
                trigger.action.outText(\\\"*** NOTE: \\\" .. theZone.name .. \\\"'s property <\\\" .. lessOp .. \\\"> may be using '!' instead of '?' for input\\\", 30)\\\
            end\\\
            return false \\\
        end\\\
        \\\
        if string.sub(theProperty, -1) == \\\":\\\" then \\\
            local lessOp = theProperty:sub(1,-2)\\\
            if cfxZones.getZoneProperty(theZone, lessOp) ~= nil then \\\
                trigger.action.outText(\\\"*** NOTE: \\\" .. theZone.name .. \\\"'s property <\\\" .. lessOp .. \\\"> may be missing a colon (':') at end\\\", 30)\\\
            end\\\
            return false \\\
        end\\\
        \\\
        if string.sub(theProperty, -1) == \\\"#\\\" then \\\
            local lessOp = theProperty:sub(1,-2)\\\
            if cfxZones.getZoneProperty(theZone, lessOp) ~= nil then \\\
                trigger.action.outText(\\\"*** NOTE: \\\" .. theZone.name .. \\\"'s property <\\\" .. lessOp .. \\\"> may be missing a hash mark ('#') at end\\\", 30)\\\
            end\\\
            return false \\\
        end\\\
        \\\
        return false \\\
    end\\\
    return true \\\
end\\\
\\\
function dmlZone:hasProperty(theProperty) \\\
    if not theProperty then \\\
        trigger.action.outText(\\\"+++zne: WARNING - hasProperty called with nil theProperty for zone <\\\" .. self.name .. \\\">\\\", 30)\\\
        return false \\\
    end \\\
    local foundIt = self:getZoneProperty(theProperty)\\\
    if not foundIt then \\\
        -- check for possible forgotten or exchanged IO flags \\\
        if string.sub(theProperty, -1) == \\\"?\\\" then\\\
            local lessOp = theProperty:sub(1,-2)\\\
            if self:getZoneProperty(lessOp) ~= nil then \\\
                trigger.action.outText(\\\"*** NOTE: \\\" .. self.name .. \\\"'s property <\\\" .. lessOp .. \\\"> may be missing a Query ('?') symbol\\\", 30)\\\
            end\\\
            local lessPlus = lessOp .. \\\"!\\\"\\\
            if self:getZoneProperty(lessPlus) ~= nil then \\\
                trigger.action.outText(\\\"*** NOTE: \\\" .. self.name .. \\\"'s property <\\\" .. lessOp .. \\\"> may be using '!' instead of '?' for input\\\", 30)\\\
            end\\\
            return false \\\
        end\\\
        \\\
        if string.sub(theProperty, -1) == \\\"!\\\" then \\\
            local lessOp = theProperty:sub(1,-2)\\\
            if self:getZoneProperty(lessOp) ~= nil then \\\
                trigger.action.outText(\\\"*** NOTE: \\\" .. self.name .. \\\"'s property <\\\" .. lessOp .. \\\"> may be missing a Bang! ('!') symbol\\\", 30)\\\
            end\\\
            local lessPlus = lessOp .. \\\"?\\\"\\\
            if self:getZoneProperty(lessPlus) ~= nil then \\\
                trigger.action.outText(\\\"*** NOTE: \\\" .. self.name .. \\\"'s property <\\\" .. lessOp .. \\\"> may be using '!' instead of '?' for input\\\", 30)\\\
            end\\\
            return false \\\
        end\\\
        \\\
        if string.sub(theProperty, -1) == \\\":\\\" then \\\
            local lessOp = theProperty:sub(1,-2)\\\
            if self:getZoneProperty(lessOp) ~= nil then \\\
                trigger.action.outText(\\\"*** NOTE: \\\" .. self.name .. \\\"'s property <\\\" .. lessOp .. \\\"> may be missing a colon (':') at end\\\", 30)\\\
            end\\\
            return false \\\
        end\\\
        \\\
        if string.sub(theProperty, -1) == \\\"#\\\" then \\\
            local lessOp = theProperty:sub(1,-2)\\\
            if self:getZoneProperty(lessOp) ~= nil then \\\
                trigger.action.outText(\\\"*** NOTE: \\\" .. self.name .. \\\"'s property <\\\" .. lessOp .. \\\"> may be missing a hash mark ('#') at end\\\", 30)\\\
            end\\\
            return false \\\
        end\\\
        \\\
        return false \\\
    end\\\
    return true \\\
end\\\
\\\
function cfxZones.getBoolFromZoneProperty(theZone, theProperty, defaultVal)\\\
    if not defaultVal then defaultVal = false end \\\
    if type(defaultVal) ~= \\\"boolean\\\" then \\\
        defaultVal = false \\\
    end\\\
\\\
    if not theZone then \\\
        trigger.action.outText(\\\"WARNING: NIL Zone in getBoolFromZoneProperty\\\", 30)\\\
        return defaultVal\\\
    end\\\
\\\
\\\
    local p = cfxZones.getZoneProperty(theZone, theProperty)\\\
    if not p then return defaultVal end\\\
\\\
    -- make sure we compare so default always works when \\\
    -- answer isn't exactly the opposite\\\
    p = p:lower() \\\
    p = dcsCommon.trim(p) \\\
    if defaultVal == false then \\\
        -- only go true if exact match to yes or true \\\
        theBool = false \\\
        theBool = (p == 'true') or (p == 'yes') or (p == \\\"1\\\") or (p == 'on')\\\
        return theBool\\\
    end\\\
    \\\
    -- special: return a random value if p == \\\"rnd\\\" or \\\"?\\\" or \\\"maybe\\\"\\\
    if (p == \\\"?\\\") or (p == \\\"rnd\\\") or (p == \\\"random\\\") or (p == \\\"maybe\\\") then \\\
        return (math.random(1000) < 500) -- 50:50\\\
    end     \\\
    \\\
    local theBool = true \\\
    -- only go false if exactly no or false or \\\"0\\\"\\\
    theBool = (p ~= 'false') and (p ~= 'no') and (p ~= \\\"0\\\") and (p~=\\\"off\\\")\\\
    return theBool\\\
end\\\
\\\
function dmlZone:getBoolFromZoneProperty(theProperty, defaultVal)\\\
    if not defaultVal then defaultVal = false end \\\
    if type(defaultVal) ~= \\\"boolean\\\" then \\\
        defaultVal = false \\\
    end\\\
\\\
    local p = self:getZoneProperty(theProperty)\\\
    if not p then return defaultVal end\\\
\\\
    -- make sure we compare so default always works when \\\
    -- answer isn't exactly the opposite\\\
    p = p:lower() \\\
    p = dcsCommon.trim(p) \\\
    if defaultVal == false then \\\
        -- only go true if exact match to yes or true \\\
        theBool = false \\\
        theBool = (p == 'true') or (p == 'yes') or (p == \\\"1\\\") or (p==\\\"on\\\")\\\
        return theBool\\\
    end\\\
    \\\
    -- special: return a random value if p == \\\"rnd\\\" or \\\"?\\\" or \\\"maybe\\\"\\\
    if (p == \\\"?\\\") or (p == \\\"rnd\\\") or (p == \\\"random\\\") or (p == \\\"maybe\\\") then \\\
        return (math.random(1000) < 500) -- 50:50\\\
    end     \\\
    \\\
    local theBool = true \\\
    -- only go false if exactly no or false or \\\"0\\\"\\\
    theBool = (p ~= 'false') and (p ~= 'no') and (p ~= \\\"0\\\") and (p ~= \\\"off\\\")\\\
    return theBool\\\
end\\\
\\\
function cfxZones.getCoalitionFromZoneProperty(theZone, theProperty, default)\\\
    if not default then default = 0 end\\\
    local p = cfxZones.getZoneProperty(theZone, theProperty)\\\
    if not p then return default end  \\\
    if type(p) == \\\"number\\\" then -- can't currently really happen\\\
        if p == 1 then return 1 end \\\
        if p == 2 then return 2 end \\\
        return 0\\\
    end\\\
    \\\
    if type(p) == \\\"string\\\" then \\\
        if p == \\\"1\\\" then return 1 end \\\
        if p == \\\"2\\\" then return 2 end \\\
        if p == \\\"0\\\" then return 0 end \\\
        \\\
        p = p:lower()\\\
        \\\
        if p == \\\"red\\\" then return 1 end \\\
        if p == \\\"blue\\\" then return 2 end \\\
        if p == \\\"neutral\\\" then return 0 end\\\
        if p == \\\"all\\\" then return 0 end \\\
        return default \\\
    end\\\
    \\\
    return default \\\
end\\\
\\\
function dmlZone:getCoalitionFromZoneProperty(theProperty, default)\\\
    if not default then default = 0 end\\\
    local p = self:getZoneProperty(theProperty)\\\
    if not p then return default end  \\\
    if type(p) == \\\"number\\\" then -- can't currently really happen\\\
        if p == 1 then return 1 end \\\
        if p == 2 then return 2 end \\\
        return 0\\\
    end\\\
    \\\
    if type(p) == \\\"string\\\" then \\\
        if p == \\\"1\\\" then return 1 end \\\
        if p == \\\"2\\\" then return 2 end \\\
        if p == \\\"0\\\" then return 0 end \\\
        \\\
        p = p:lower()\\\
        \\\
        if p == \\\"red\\\" then return 1 end \\\
        if p == \\\"blue\\\" then return 2 end \\\
        if p == \\\"neutral\\\" then return 0 end\\\
        if p == \\\"all\\\" then return 0 end \\\
        return default \\\
    end\\\
    \\\
    return default \\\
end\\\
\\\
function cfxZones.getNumberFromZoneProperty(theZone, theProperty, default)\\\
    if not default then default = 0 end\\\
    default = tonumber(default)\\\
    if not default then default = 0 end -- enforce default numbner as well \\\
    local p = cfxZones.getZoneProperty(theZone, theProperty)\\\
    p = tonumber(p)\\\
    if not p then p = default end \\\
    return p\\\
end\\\
\\\
function dmlZone:getNumberFromZoneProperty(theProperty, default) \\\
    if not default then default = 0 end\\\
    default = tonumber(default)\\\
    if not default then default = 0 end -- enforce default numbner as well \\\
    local p = self:getZoneProperty(theProperty)\\\
    p = tonumber(p)\\\
    if not p then p = default end \\\
    return p\\\
end\\\
\\\
function cfxZones.getVectorFromZoneProperty(theZone, theProperty, minDims, defaultVal)\\\
    if not minDims then minDims = 0 end \\\
    if not defaultVal then defaultVal = 0 end \\\
    local s = cfxZones.getStringFromZoneProperty(theZone, theProperty, \\\"\\\")\\\
    local sVec = dcsCommon.splitString(s, \\\",\\\")\\\
    local nVec = {}\\\
    for idx, numString in pairs (sVec) do \\\
        local n = tonumber(numString)\\\
        if not n then n = defaultVal end\\\
        table.insert(nVec, n)\\\
    end\\\
    -- make sure vector contains at least minDims values \\\
    while #nVec < minDims do \\\
        table.insert(nVec, defaultVal)\\\
    end\\\
    \\\
    return nVec \\\
end\\\
\\\
function dmlZone:getVectorFromZoneProperty(theProperty, minDims, defaultVal)\\\
    if not minDims then minDims = 0 end \\\
    if not defaultVal then defaultVal = 0 end \\\
    local s = self:getStringFromZoneProperty(theProperty, \\\"\\\")\\\
    local sVec = dcsCommon.splitString(s, \\\",\\\")\\\
    local nVec = {}\\\
    for idx, numString in pairs (sVec) do \\\
        local n = tonumber(numString)\\\
        if not n then n = defaultVal end\\\
        table.insert(nVec, n)\\\
    end\\\
    -- make sure vector contains at least minDims values \\\
    while #nVec < minDims do \\\
        table.insert(nVec, defaultVal)\\\
    end\\\
    \\\
    return nVec \\\
end\\\
\\\
function cfxZones.getRGBVectorFromZoneProperty(theZone, theProperty, defaultVal)\\\
    if not defaultVal then defaultVal = {1.0, 1.0, 1.0} end \\\
    if #defaultVal ~=3 then defaultVal = {1.0, 1.0, 1.0} end\\\
    local s = cfxZones.getStringFromZoneProperty(theZone, theProperty, \\\"\\\")\\\
    local sVec = dcsCommon.splitString(s, \\\",\\\")\\\
    local nVec = {}\\\
    for i = 1, 3 do \\\
        n = sVec[i]\\\
        if n then n = tonumber(n) end \\\
        if not n then n = defaultVal[i] end \\\
        if n > 1.0 then n = 1.0 end\\\
        if n < 0 then n = 0 end \\\
        nVec[i] = n\\\
    end\\\
    return nVec \\\
end\\\
\\\
function dmlZone:getRGBVectorFromZoneProperty(theProperty, defaultVal)\\\
    if not defaultVal then defaultVal = {1.0, 1.0, 1.0} end \\\
    if #defaultVal ~=3 then defaultVal = {1.0, 1.0, 1.0} end\\\
    local s = self:getStringFromZoneProperty(theProperty, \\\"\\\")\\\
    local sVec = dcsCommon.splitString(s, \\\",\\\")\\\
    local nVec = {}\\\
    for i = 1, 3 do \\\
        n = sVec[i]\\\
        if n then n = tonumber(n) end \\\
        if not n then n = defaultVal[i] end \\\
        if n > 1.0 then n = 1.0 end\\\
        if n < 0 then n = 0 end \\\
        nVec[i] = n\\\
    end\\\
    return nVec \\\
end\\\
\\\
\\\
function cfxZones.getRGBAVectorFromZoneProperty(theZone, theProperty, defaultVal)\\\
    if not defaultVal then defaultVal = {1.0, 1.0, 1.0, 1.0} end \\\
    if #defaultVal ~=4 then defaultVal = {1.0, 1.0, 1.0, 1.0} end\\\
    local s = cfxZones.getStringFromZoneProperty(theZone, theProperty, \\\"\\\")\\\
    s = dcsCommon.trim(s)\\\
    if s:sub(1,1) == \\\"#\\\" then \\\
        -- it's probably a \\\"#RRGGBBAA\\\" format hex string \\\
        local hVec = dcsCommon.hexString2RGBA(s)\\\
        if hVec then return hVec end \\\
    end\\\
\\\
    local sVec = dcsCommon.splitString(s, \\\",\\\")\\\
    local nVec = {}\\\
    for i = 1, 4 do \\\
        n = sVec[i]\\\
        if n then n = tonumber(n) end \\\
        if not n then n = defaultVal[i] end \\\
        if n > 1.0 then n = 1.0 end\\\
        if n < 0 then n = 0 end \\\
        nVec[i] = n\\\
    end\\\
        \\\
    return nVec \\\
end\\\
\\\
function dmlZone:getRGBAVectorFromZoneProperty(theProperty, defaultVal)\\\
    if not defaultVal then defaultVal = {1.0, 1.0, 1.0, 1.0} end \\\
    if #defaultVal ~=4 then defaultVal = {1.0, 1.0, 1.0, 1.0} end\\\
    local s = self:getStringFromZoneProperty(theProperty, \\\"\\\")\\\
    s = dcsCommon.trim(s)\\\
    if s:sub(1,1) == \\\"#\\\" then \\\
        -- it's probably a \\\"#RRGGBBAA\\\" format hex string \\\
        local hVec = dcsCommon.hexString2RGBA(s)\\\
        if hVec then return hVec end \\\
    end\\\
\\\
    local sVec = dcsCommon.splitString(s, \\\",\\\")\\\
    local nVec = {}\\\
    for i = 1, 4 do \\\
        n = sVec[i]\\\
        if n then n = tonumber(n) end \\\
        if not n then n = defaultVal[i] end \\\
        if n > 1.0 then n = 1.0 end\\\
        if n < 0 then n = 0 end \\\
        nVec[i] = n\\\
    end\\\
        \\\
    return nVec \\\
end\\\
\\\
function cfxZones.getRGBFromZoneProperty(theZone, theProperty, default)\\\
    --if not default then default = {1.0, 1.0, 1.0} end -- white \\\
    local rawRGB = cfxZones.getVectorFromZoneProperty(theZone, theProperty, 3, 1.0)\\\
    local retVal = {}\\\
    for i = 1, 3 do \\\
        local cp = rawRGB[i]\\\
        if cp > 1.0 then cp = 1.0 end\\\
        if cp < 0 then cp = 0 end \\\
        retVal[i] = cp\\\
    end\\\
    return retVal\\\
end\\\
\\\
function dmlZone:getRGBFromZoneProperty(theProperty, default)\\\
    --if not default then default = {1.0, 1.0, 1.0} end -- white \\\
    local rawRGB = self:getVectorFromZoneProperty(theProperty, 3, 1.0)\\\
    local retVal = {}\\\
    for i = 1, 3 do \\\
        local cp = rawRGB[i]\\\
        if cp > 1.0 then cp = 1.0 end\\\
        if cp < 0 then cp = 0 end \\\
        retVal[i] = cp\\\
    end\\\
    return retVal\\\
end\\\
\\\
\\\
function cfxZones.getSmokeColorStringFromZoneProperty(theZone, theProperty, default) -- smoke as 'red', 'green', or 1..5\\\
    if not default then default = \\\"red\\\" end \\\
    local s = cfxZones.getStringFromZoneProperty(theZone, theProperty, default)\\\
    s = s:lower()\\\
    s = dcsCommon.trim(s)\\\
    -- check numbers \\\
    if (s == \\\"0\\\") then return \\\"green\\\" end\\\
    if (s == \\\"1\\\") then return \\\"red\\\" end\\\
    if (s == \\\"2\\\") then return \\\"white\\\" end\\\
    if (s == \\\"3\\\") then return \\\"orange\\\" end\\\
    if (s == \\\"4\\\") then return \\\"blue\\\" end\\\
    \\\
    if s == \\\"green\\\" or\\\
       s == \\\"red\\\" or\\\
       s == \\\"white\\\" or\\\
       s == \\\"orange\\\" or\\\
       s == \\\"blue\\\" then return s end\\\
\\\
    return default \\\
end\\\
\\\
function dmlZone:getSmokeColorStringFromZoneProperty(theProperty, default) -- smoke as 'red', 'green', or 1..5\\\
    if not default then default = \\\"red\\\" end \\\
    local s = self:getStringFromZoneProperty(theProperty, default)\\\
    s = s:lower()\\\
    s = dcsCommon.trim(s)\\\
    -- check numbers \\\
    if (s == \\\"0\\\") then return \\\"green\\\" end\\\
    if (s == \\\"1\\\") then return \\\"red\\\" end\\\
    if (s == \\\"2\\\") then return \\\"white\\\" end\\\
    if (s == \\\"3\\\") then return \\\"orange\\\" end\\\
    if (s == \\\"4\\\") then return \\\"blue\\\" end\\\
    \\\
    if s == \\\"green\\\" or\\\
       s == \\\"red\\\" or\\\
       s == \\\"white\\\" or\\\
       s == \\\"orange\\\" or\\\
       s == \\\"blue\\\" then return s end\\\
\\\
    return default \\\
end\\\
\\\
function cfxZones.getFlareColorStringFromZoneProperty(theZone, theProperty, default) -- smoke as 'red', 'green', or 1..5\\\
    if not default then default = \\\"red\\\" end \\\
    local s = cfxZones.getStringFromZoneProperty(theZone, theProperty, default)\\\
    s = s:lower()\\\
    s = dcsCommon.trim(s)\\\
    -- check numbers \\\
    if (s == \\\"rnd\\\") then return \\\"random\\\" end \\\
    if (s == \\\"0\\\") then return \\\"green\\\" end\\\
    if (s == \\\"1\\\") then return \\\"red\\\" end\\\
    if (s == \\\"2\\\") then return \\\"white\\\" end\\\
    if (s == \\\"3\\\") then return \\\"yellow\\\" end\\\
    if (s == \\\"-1\\\") then return \\\"random\\\" end  \\\
    \\\
    if s == \\\"green\\\" or\\\
       s == \\\"red\\\" or\\\
       s == \\\"white\\\" or\\\
       s == \\\"yellow\\\" or \\\
       s == \\\"random\\\" then\\\
    return s end\\\
\\\
    return default \\\
end\\\
\\\
function dmlZone:getFlareColorStringFromZoneProperty(theProperty, default) -- smoke as 'red', 'green', or 1..5\\\
    if not default then default = \\\"red\\\" end \\\
    local s = self:getStringFromZoneProperty(theProperty, default)\\\
    s = s:lower()\\\
    s = dcsCommon.trim(s)\\\
    -- check numbers \\\
    if (s == \\\"rnd\\\") then return \\\"random\\\" end \\\
    if (s == \\\"0\\\") then return \\\"green\\\" end\\\
    if (s == \\\"1\\\") then return \\\"red\\\" end\\\
    if (s == \\\"2\\\") then return \\\"white\\\" end\\\
    if (s == \\\"3\\\") then return \\\"yellow\\\" end\\\
    if (s == \\\"-1\\\") then return \\\"random\\\" end  \\\
    \\\
    if s == \\\"green\\\" or\\\
       s == \\\"red\\\" or\\\
       s == \\\"white\\\" or\\\
       s == \\\"yellow\\\" or \\\
       s == \\\"random\\\" then\\\
    return s end\\\
\\\
    return default \\\
end\\\
\\\
--\\\
-- Zone-based wildcard processing\\\
-- \\\
\\\
-- process <z>\\\
function cfxZones.processZoneStatics(inMsg, theZone)\\\
    if theZone then \\\
        inMsg = inMsg:gsub(\\\"<z>\\\", theZone.name)\\\
    end\\\
    return inMsg \\\
end\\\
\\\
function dmlZone:processZoneStatics(inMsg, theZone)\\\
    inMsg = inMsg:gsub(\\\"<z>\\\", self.name)\\\
    return inMsg \\\
end\\\
\\\
-- process <t>, <lat>, <lon>, <ele>, <mgrs> \\\
function cfxZones.processSimpleZoneDynamics(inMsg, theZone, timeFormat, imperialUnits)\\\
    if not inMsg then return \\\"<nil inMsg>\\\" end\\\
    -- replace <t> with current mission time HMS\\\
    local absSecs = timer.getAbsTime()-- + env.mission.start_time\\\
    while absSecs > 86400 do \\\
        absSecs = absSecs - 86400 -- subtract out all days \\\
    end\\\
    if not timeFormat then timeFormat = \\\"<:h>:<:m>:<:s>\\\" end \\\
    local timeString  = dcsCommon.processHMS(timeFormat, absSecs)\\\
    local outMsg = inMsg:gsub(\\\"<t>\\\", timeString)\\\
    \\\
    -- replace <lat> with lat of zone point and <lon> with lon of zone point \\\
    -- and <mgrs> with mgrs coords of zone point \\\
    local currPoint = cfxZones.getPoint(theZone)\\\
    local lat, lon = coord.LOtoLL(currPoint)\\\
    lat, lon = dcsCommon.latLon2Text(lat, lon)\\\
    local alt = land.getHeight({x = currPoint.x, y = currPoint.z})\\\
    if imperialUnits then \\\
        alt = math.floor(alt * 3.28084) -- feet \\\
    else \\\
        alt = math.floor(alt) -- meters \\\
    end \\\
    outMsg = outMsg:gsub(\\\"<lat>\\\", lat)\\\
    outMsg = outMsg:gsub(\\\"<lon>\\\", lon)\\\
    outMsg = outMsg:gsub(\\\"<ele>\\\", alt)\\\
    local grid = coord.LLtoMGRS(coord.LOtoLL(currPoint))\\\
    local mgrs = grid.UTMZone .. ' ' .. grid.MGRSDigraph .. ' ' .. grid.Easting .. ' ' .. grid.Northing\\\
    outMsg = outMsg:gsub(\\\"<mgrs>\\\", mgrs)\\\
    return outMsg\\\
end \\\
\\\
-- process <v: flag>, <rsp: flag> <rrnd>\\\
function cfxZones.processDynamicValues(inMsg, theZone, msgResponses)\\\
    -- replace all occurences of <v: flagName> with their values \\\
    local pattern = \\\"<v:%s*[%s%w%*%d%.%-_]+>\\\" -- no list allowed but blanks and * and . and - and _ --> we fail on the other specials to keep this simple \\\
    local outMsg = inMsg\\\
    repeat -- iterate all patterns one by one \\\
        local startLoc, endLoc = string.find(outMsg, pattern)\\\
        if startLoc then \\\
            local theValParam = string.sub(outMsg, startLoc, endLoc)\\\
            -- strip lead and trailer \\\
            local param = string.gsub(theValParam, \\\"<v:%s*\\\", \\\"\\\")\\\
            param = string.gsub(param, \\\">\\\",\\\"\\\")\\\
            -- param = dcsCommon.trim(param) -- trim is called anyway\\\
            -- access flag\\\
            local val = cfxZones.getFlagValue(param, theZone)\\\
            val = tostring(val)\\\
            if not val then val = \\\"NULL\\\" end \\\
            -- replace pattern in original with new val \\\
            outMsg = string.gsub(outMsg, pattern, val, 1) -- only one sub!\\\
        end\\\
    until not startLoc\\\
    \\\
    -- now process rsp \\\
    pattern = \\\"<rsp:%s*[%s%w%*%d%.%-_]+>\\\" -- no list allowed but blanks and * and . and - and _ --> we fail on the other specials to keep this simple \\\
\\\
    if msgResponses and (#msgResponses > 0) then -- only if this zone has an array\\\
        --trigger.action.outText(\\\"enter response proccing\\\", 30)\\\
        repeat -- iterate all patterns one by one \\\
            local startLoc, endLoc = string.find(outMsg, pattern)\\\
            if startLoc then \\\
                local theValParam = string.sub(outMsg, startLoc, endLoc)\\\
                -- strip lead and trailer \\\
                local param = string.gsub(theValParam, \\\"<rsp:%s*\\\", \\\"\\\")\\\
                param = string.gsub(param, \\\">\\\",\\\"\\\")\\\
                \\\
                -- access flag\\\
                local val = cfxZones.getFlagValue(param, theZone)\\\
                if not val or (val < 1) then val = 1 end \\\
                if val > msgResponses then val = msgResponses end \\\
                \\\
                val = msgResponses[val]\\\
                val = dcsCommon.trim(val)\\\
                -- replace pattern in original with new val \\\
                outMsg = string.gsub(outMsg, pattern, val, 1) -- only one sub!\\\
            end\\\
        until not startLoc\\\
        \\\
        -- rnd response \\\
        local rndRsp = dcsCommon.pickRandom(msgResponses)\\\
        outMsg = outMsg:gsub (\\\"<rrnd>\\\", rndRsp)\\\
    end\\\
    \\\
    return outMsg\\\
end\\\
\\\
-- process <t: flag>\\\
function cfxZones.processDynamicTime(inMsg, theZone, timeFormat)\\\
    if not timeFormat then timeFormat = \\\"<:h>:<:m>:<:s>\\\" end\\\
    -- replace all occurences of <t: flagName> with their values \\\
    local pattern = \\\"<t:%s*[%s%w%*%d%.%-_]+>\\\" -- no list allowed but blanks and * and . and - and _ --> we fail on the other specials to keep this simple \\\
    local outMsg = inMsg\\\
    repeat -- iterate all patterns one by one \\\
        local startLoc, endLoc = string.find(outMsg, pattern)\\\
        if startLoc then \\\
            local theValParam = string.sub(outMsg, startLoc, endLoc)\\\
            -- strip lead and trailer \\\
            local param = string.gsub(theValParam, \\\"<t:%s*\\\", \\\"\\\")\\\
            param = string.gsub(param, \\\">\\\",\\\"\\\")\\\
            -- access flag\\\
            local val = cfxZones.getFlagValue(param, theZone)\\\
            -- use this to process as time value \\\
            --trigger.action.outText(\\\"time: accessing <\\\" .. param .. \\\"> and received <\\\" .. val .. \\\">\\\", 30)\\\
            local timeString  = dcsCommon.processHMS(timeFormat, val)\\\
            \\\
            if not timeString then timeString = \\\"NULL\\\" end \\\
            -- replace pattern in original with new val \\\
            outMsg = string.gsub(outMsg, pattern, timeString, 1) -- only one sub!\\\
        end\\\
    until not startLoc\\\
    return outMsg\\\
end\\\
\\\
-- process <lat/lon/ele/mgrs/lle/latlon/alt/vel/hdg/rhdg/type/player: zone/unit>\\\
function cfxZones.processDynamicLoc(inMsg, imperialUnits, responses)\\\
    local locales = {\\\"lat\\\", \\\"lon\\\", \\\"ele\\\", \\\"mgrs\\\", \\\"lle\\\", \\\"latlon\\\", \\\"alt\\\", \\\"vel\\\", \\\"hdg\\\", \\\"rhdg\\\", \\\"type\\\", \\\"player\\\"}\\\
    local outMsg = inMsg\\\
    local uHead = 0\\\
    for idx, aLocale in pairs(locales) do \\\
        local pattern = \\\"<\\\" .. aLocale .. \\\":%s*[%s%w%*%d%.%-_]+>\\\"\\\
        repeat -- iterate all patterns one by one \\\
            local startLoc, endLoc = string.find(outMsg, pattern)\\\
            if startLoc then\\\
                local theValParam = string.sub(outMsg, startLoc, endLoc)\\\
                -- strip lead and trailer \\\
                local param = string.gsub(theValParam, \\\"<\\\" .. aLocale .. \\\":%s*\\\", \\\"\\\")\\\
                param = string.gsub(param, \\\">\\\",\\\"\\\")\\\
                -- find zone or unit\\\
                param = dcsCommon.trim(param)\\\
                local thePoint = nil \\\
                local tZone = cfxZones.getZoneByName(param)\\\
                local tUnit = Unit.getByName(param)\\\
                local spd = 0\\\
                local angels = 0 \\\
                local theType = \\\"<errType>\\\"\\\
                local playerName = \\\"Unknown\\\"\\\
                if tZone then\\\
                    theType = \\\"Zone\\\"\\\
                    playerName = \\\"?zone?\\\"\\\
                    thePoint = cfxZones.getPoint(tZone)\\\
                    if tZone.linkedUnit and Unit.isExist(tZone.linkedUnit) then \\\
                        local lU = tZone.linkedUnit\\\
                        local masterPoint = lU:getPoint()\\\
                        thePoint.y = masterPoint.y \\\
                        spd = dcsCommon.getUnitSpeed(lU)\\\
                        spd = math.floor(spd * 3.6)\\\
                        uHead = math.floor(dcsCommon.getUnitHeading(tUnit) * 57.2958) -- to degrees.\\\
                    else \\\
                        -- since zones always have elevation of 0, \\\
                        -- now get the elevation from the map \\\
                        thePoint.y = land.getHeight({x = thePoint.x, y = thePoint.z})\\\
                    end\\\
                elseif tUnit then \\\
                    if Unit.isExist(tUnit) then\\\
                        theType = tUnit:getTypeName()\\\
                        if tUnit.getPlayerName and tUnit:getPlayerName() then\\\
                            playerName = tUnit:getPlayerName()\\\
                        end\\\
                        thePoint = tUnit:getPoint()\\\
                        spd = dcsCommon.getUnitSpeed(tUnit)\\\
                        -- convert m/s to km/h \\\
                        spd = math.floor(spd * 3.6)\\\
                        uHead = math.floor(dcsCommon.getUnitHeading(tUnit) * 57.2958) -- to degrees. \\\
                    end\\\
                else \\\
                    -- nothing to do, remove me.\\\
                end\\\
\\\
                local locString = \\\"err\\\"\\\
                if thePoint then \\\
                    -- now that we have a point, we can do locale-specific\\\
                    -- processing. return result in locString\\\
                    local lat, lon, alt = coord.LOtoLL(thePoint)\\\
                    lat, lon = dcsCommon.latLon2Text(lat, lon)\\\
                    angels = math.floor(thePoint.y) \\\
                    if imperialUnits then \\\
                        alt = math.floor(alt * 3.28084) -- feet\\\
                        spd = math.floor(spd * 0.539957) -- km/h to knots    \\\
                        angels = math.floor(angels * 3.28084)\\\
                    else \\\
                        alt = math.floor(alt) -- meters \\\
                    end \\\
                    \\\
                    if angels > 1000 then \\\
                        angels = math.floor(angels / 100) * 100 \\\
                    end\\\
                    \\\
                    if aLocale == \\\"lat\\\" then locString = lat \\\
                    elseif aLocale == \\\"lon\\\" then locString = lon \\\
                    elseif aLocale == \\\"ele\\\" then locString = tostring(alt)\\\
                    elseif aLocale == \\\"lle\\\" then locString = lat .. \\\" \\\" .. lon .. \\\" ele \\\" .. tostring(alt) \\\
                    elseif aLocale == \\\"latlon\\\" then locString = lat .. \\\" \\\" .. lon \\\
                    elseif aLocale == \\\"alt\\\" then locString = tostring(angels) -- don't confuse alt and angels, bad var naming here\\\
                    elseif aLocale == \\\"vel\\\" then locString = tostring(spd)\\\
                    elseif aLocale == \\\"hdg\\\" then locString = tostring(uHead)\\\
                    elseif aLocale == \\\"type\\\" then locString = theType \\\
                    elseif aLocale == \\\"player\\\" then locString = playerName \\\
                    elseif aLocale == \\\"rhdg\\\" and (responses) then \\\
                        local offset = cfxZones.rspMapper360(uHead, #responses)\\\
                        locString = dcsCommon.trim(responses[offset])\\\
                    else \\\
                        -- we have mgrs\\\
                        local grid = coord.LLtoMGRS(coord.LOtoLL(thePoint))\\\
                        locString = grid.UTMZone .. ' ' .. grid.MGRSDigraph .. ' ' .. grid.Easting .. ' ' .. grid.Northing\\\
                    end\\\
                end\\\
                -- replace pattern in original with new val \\\
                outMsg = string.gsub(outMsg, pattern, locString, 1) -- only one sub!\\\
            end -- if startloc\\\
        until not startLoc\\\
    end -- for all locales \\\
    return outMsg\\\
end\\\
\\\
-- process reference that can be flag, Zone, or unit.\\\
-- i.e. <coa: xyz>\\\
function cfxZones.processDynamicVZU(inMsg)\\\
local locales = {\\\"coa\\\",}\\\
    local outMsg = inMsg\\\
    local uHead = 0\\\
    for idx, aLocale in pairs(locales) do \\\
        local pattern = \\\"<\\\" .. aLocale .. \\\":%s*[%s%w%*%d%.%-_]+>\\\" -- e.g. \\\"<coa: flag Name>\\\
        repeat -- iterate all patterns one by one \\\
            local startLoc, endLoc = string.find(outMsg, pattern)\\\
            if startLoc then\\\
                local theValParam = string.sub(outMsg, startLoc, endLoc)\\\
                -- strip lead and trailer \\\
                local param = string.gsub(theValParam, \\\"<\\\" .. aLocale .. \\\":%s*\\\", \\\"\\\") -- remove \\\"<coa:\\\"\\\
                param = string.gsub(param, \\\">\\\",\\\"\\\") -- remove trailing \\\">\\\"\\\
                -- find zone or unit\\\
                param = dcsCommon.trim(param) -- param = \\\"flag Name\\\"\\\
                local tZone = cfxZones.getZoneByName(param)\\\
                local tUnit = Unit.getByName(param)\\\
\\\
                local locString = \\\"err\\\"\\\
                if aLocale == \\\"coa\\\" then\\\
                    coa = trigger.misc.getUserFlag(param)\\\
                    if tZone then coa = tZone.owner end \\\
                    if tUnit and Unit:isExist(tUnit) then coa = tUnit:getCoalition() end \\\
                    locString = dcsCommon.coalition2Text(coa)\\\
                end\\\
\\\
                outMsg = string.gsub(outMsg, pattern, locString, 1) -- only one sub!\\\
            end -- if startloc\\\
        until not startLoc\\\
    end -- for all locales \\\
    return outMsg\\\
end\\\
\\\
-- process two-value vars that can be flag or unit and return interpreted value\\\
-- i.e. <alive: Aerial-1-1>\\\
function cfxZones.processDynamicValueVU(inMsg)\\\
local locales = {\\\"yes\\\", \\\"true\\\", \\\"alive\\\", \\\"in\\\"}\\\
    local outMsg = inMsg\\\
    local uHead = 0\\\
    for idx, aLocale in pairs(locales) do \\\
        local pattern = \\\"<\\\" .. aLocale .. \\\":%s*[%s%w%*%d%.%-_]+>\\\" -- e.g. \\\"<yes: flagOrUnitName>\\\
        repeat -- iterate all patterns one by one \\\
            local startLoc, endLoc = string.find(outMsg, pattern)\\\
            if startLoc then\\\
                local theValParam = string.sub(outMsg, startLoc, endLoc)\\\
                -- strip lead and trailer \\\
                local param = string.gsub(theValParam, \\\"<\\\" .. aLocale .. \\\":%s*\\\", \\\"\\\") -- remove \\\"<alive:\\\"\\\
                param = string.gsub(param, \\\">\\\",\\\"\\\") -- remove trailing \\\">\\\"\\\
                -- find zone or unit\\\
                param = dcsCommon.trim(param) -- param = \\\"flagOrUnitName\\\"\\\
                local tUnit = Unit.getByName(param)\\\
                local yesNo = trigger.misc.getUserFlag(param) ~= 0\\\
                if tUnit then yesNo = Unit.isExist(tUnit) end\\\
                local locString = \\\"err\\\"\\\
                if aLocale == \\\"yes\\\" then                    \\\
                    if yesNo then locString = \\\"yes\\\" else locString = \\\"no\\\" end\\\
                elseif aLocale == \\\"true\\\" then \\\
                    if yesNo then locString = \\\"true\\\" else locString = \\\"false\\\" end \\\
                elseif aLocale == \\\"alive\\\" then \\\
                    if yesNo then locString = \\\"alive\\\" else locString = \\\"dead\\\" end\\\
                elseif aLocale == \\\"in\\\" then \\\
                    if yesNo then locString = \\\"in\\\" else locString = \\\"out\\\" end\\\
                end\\\
\\\
                outMsg = string.gsub(outMsg, pattern, locString, 1) -- only one sub!\\\
            end -- if startloc\\\
        until not startLoc\\\
    end -- for all locales \\\
    return outMsg\\\
end\\\
\\\
function cfxZones.processDynamicAB(inMsg, locale)\\\
    local outMsg = inMsg\\\
    if not locale then locale = \\\"A/B\\\" end \\\
    \\\
    -- <A/B: flagOrUnitName [val A | val B]>\\\
    local replacerValPattern = \\\"<\\\".. locale .. \\\":%s*[%s%w%*%d%.%-_]+\\\" .. \\\"%[[%s%w]+|[%s%w]+%]\\\"..\\\">\\\"\\\
    repeat \\\
        local startLoc, endLoc = string.find(outMsg, replacerValPattern)\\\
        if startLoc then \\\
            local rp = string.sub(outMsg, startLoc, endLoc)\\\
            -- get val/unit name \\\
            local valA, valB = string.find(rp, \\\":%s*[%s%w%*%d%.%-_]+%[\\\")\\\
            local val = string.sub(rp, valA+1, valB-1)\\\
            val = dcsCommon.trim(val)\\\
            -- get left and right \\\
            local leftA, leftB = string.find(rp, \\\"%[[%s%w]+|\\\" ) -- from \\\"[\\\" to \\\"|\\\"\\\
            local rightA, rightB = string.find(rp, \\\"|[%s%w]+%]\\\") -- from \\\"|\\\" to \\\"]\\\"\\\
            left = string.sub(rp, leftA+1, leftB-1)\\\
            left = dcsCommon.trim(left)\\\
            right = string.sub(rp, rightA+1, rightB-1)\\\
            right = dcsCommon.trim(right)        \\\
            local yesno = false\\\
            -- see if unit exists\\\
            local theUnit = Unit.getByName(val)\\\
            if theUnit then \\\
                yesno = Unit:isExist(theUnit)\\\
            else \\\
                yesno = trigger.misc.getUserFlag(val) ~= 0\\\
            end\\\
\\\
            local locString = left \\\
            if yesno then locString = right end \\\
            outMsg = string.gsub(outMsg, replacerValPattern, locString, 1)\\\
        end\\\
    until not startLoc \\\
    return outMsg\\\
end\\\
\\\
function cfxZones.rspMapper360(directionInDegrees, numResponses)\\\
    -- maps responses around a clock. Clock has 12 'responses' (12, 1, .., 11), \\\
    -- with the first (12) also mapping to the last half arc \\\
    -- this method dynamically 'winds' the responses around \\\
    -- a clock and returns the index of the message to display \\\
    if numResponses < 1 then numResponses = 1 end \\\
    directionInDegrees = math.floor(directionInDegrees) \\\
    while directionInDegrees < 0 do directionInDegrees = directionInDegrees + 360 end \\\
    while directionInDegrees >= 360 do directionInDegrees = directionInDegrees - 360 end \\\
    -- now we have 0..360 \\\
    -- calculate arc per item \\\
    local arcPerItem = 360 / numResponses\\\
    local halfArc = arcPerItem / 2\\\
\\\
    -- we now map 0..360 to (0-halfArc..360-halfArc) by shifting \\\
    -- direction by half-arc and clipping back 0..360\\\
    -- and now we can directly derive the index of the response \\\
    directionInDegrees = directionInDegrees + halfArc\\\
    if directionInDegrees >= 360 then directionInDegrees = directionInDegrees - 360 end \\\
    \\\
    local index = math.floor(directionInDegrees / arcPerItem) + 1 -- 1 .. numResponses \\\
    \\\
    return index \\\
end\\\
\\\
-- replaces dcsCommon with same name \\\
-- timeFormat is optional, default is \\\"<:h>:<:m>:<:s>\\\"\\\
-- imperialUnits is optional, defaults to meters \\\
-- responses is an array of string, defaults to {}\\\
function cfxZones.processStringWildcards(inMsg, theZone, timeFormat, imperialUnits, responses)\\\
    if not inMsg then return \\\"<nil inMsg>\\\" end\\\
    local formerType = type(inMsg)\\\
    if formerType ~= \\\"string\\\" then inMsg = tostring(inMsg) end\\\
    if not inMsg then inMsg = \\\"<inMsg is incompatible type \\\" .. formerType .. \\\">\\\" end\\\
    local theMsg = inMsg\\\
    -- process common DCS stuff like /n \\\
    theMsg = dcsCommon.processStringWildcards(theMsg) -- call old inherited\\\
    -- process <z>\\\
    theMsg = cfxZones.processZoneStatics(theMsg, theZone)\\\
    -- process <t>, <lat>, <lon>, <ele>, <mgrs>\\\
    theMsg = cfxZones.processSimpleZoneDynamics(theMsg, theZone, timeFormat, imperialUnits)\\\
    -- process <v: flag>, <rsp: flag> <rrnd>\\\
    theMsg = cfxZones.processDynamicValues(theMsg, theZone, responses)\\\
    -- process <t: flag>\\\
    theMsg = cfxZones.processDynamicTime(theMsg, theZone, timeFormat)\\\
    -- process <lat/lon/ele/mgrs/lle/latlon/alt/vel/hdg/rhdg/type/player: zone/unit>\\\
    theMsg = cfxZones.processDynamicLoc(theMsg, imperialUnits, responses)\\\
    -- process values that can be derived from flag (default), zone or unit \\\
    theMsg = cfxZones.processDynamicVZU(theMsg)\\\
    theMsg = cfxZones.processDynamicAB(theMsg)\\\
    theMsg = cfxZones.processDynamicValueVU(theMsg)\\\
    return theMsg\\\
end\\\
\\\
--\\\
-- ============\\\
-- MOVING ZONES \\\
-- ============ \\\
-- \\\
-- Moving zones contain a link to their unit\\\
-- they are always located at an offset (x,z) or delta, phi \\\
-- to their master unit. delta phi allows adjustment for heading\\\
-- The cool thing about moving zones in cfx is that they do not\\\
-- require special handling, they are always updated \\\
-- and work with 'pointinzone' etc automatically\\\
\\\
-- Always works on cfx Zones, NEVER on DCS zones.\\\
--\\\
-- requires that readFromDCS has been done\\\
--\\\
function cfxZones.getDCSOrigin(aZone)\\\
    local o = {}\\\
    o.x = aZone.dcsOrigin.x\\\
    o.y = 0\\\
    o.z = aZone.dcsOrigin.z \\\
    return o\\\
end\\\
\\\
function dmlZone:getDCSOrigin()\\\
    local o = {}\\\
    if not self.dcsOrigin then \\\
        trigger.action.outText(\\\"dmlZone (OOP): no dcsOrigin defined for zone <\\\" .. self.name .. \\\">\\\", 30)\\\
        o.x = 0\\\
        o.y = 0\\\
        o.z = 0\\\
    else\\\
        o.x = self.dcsOrigin.x\\\
        o.y = 0\\\
        o.z = self.dcsOrigin.z \\\
    end \\\
    return o\\\
end\\\
\\\
function cfxZones.getLinkedUnit(theZone)\\\
    if not theZone then return nil end \\\
    if not theZone.linkedUnit then return nil end \\\
    if not Unit.isExist(theZone.linkedUnit) then return nil end \\\
    return theZone.linkedUnit \\\
end\\\
\\\
function dmlZone:getLinkedUnit()\\\
    if not self.linkedUnit then return nil end \\\
    if not Unit.isExist(self.linkedUnit) then return nil end \\\
    return self.linkedUnit \\\
end\\\
\\\
function cfxZones.getPoint(aZone, getHeight) -- always works, even linked, returned point can be reused\\\
-- returned y (when using getHeight) is that of the land, else 0 \\\
    if not getHeight then getHeight = false end \\\
    if aZone.linkedUnit then \\\
        local theUnit = aZone.linkedUnit\\\
        -- has a link. is link existing?\\\
        if Unit.isExist(theUnit) then \\\
            -- updates zone position \\\
            cfxZones.centerZoneOnUnit(aZone, theUnit)\\\
            local dx = aZone.dx\\\
            local dy = aZone.dy\\\
            if aZone.useHeading then \\\
                dx, dy = cfxZones.calcHeadingOffset(aZone, theUnit)\\\
            end\\\
            cfxZones.offsetZone(aZone, dx, dy)\\\
        end\\\
    end\\\
    local thePos = {}\\\
    thePos.x = aZone.point.x\\\
    thePos.z = aZone.point.z\\\
    if not getHeight then \\\
        thePos.y = 0 -- aZone.y \\\
    else \\\
        thePos.y = land.getHeight({x = thePos.x, y = thePos.z})\\\
    end\\\
    \\\
    return thePos \\\
end\\\
\\\
function dmlZone:getPoint(getHeight)\\\
    if not getHeight then getHeight = false end \\\
    if self.linkedUnit then \\\
        local theUnit = self.linkedUnit\\\
        -- has a link. is link existing?\\\
        if Unit.isExist(theUnit) then \\\
            -- updates zone position \\\
            self:centerZoneOnUnit(theUnit)\\\
            local dx = self.dx\\\
            local dy = self.dy\\\
            if self.useHeading then \\\
                dx, dy = self:calcHeadingOffset(theUnit)\\\
            end\\\
            self:offsetZone(dx, dy)\\\
        end\\\
    end\\\
    local thePos = {}\\\
    thePos.x = self.point.x\\\
    thePos.z = self.point.z\\\
    if not getHeight then \\\
        thePos.y = 0 -- aZone.y \\\
    else \\\
        thePos.y = land.getHeight({x = thePos.x, y = thePos.z})\\\
    end\\\
    \\\
    return thePos \\\
end\\\
\\\
function dmlZone:getName() -- no cfxZones.bridge!\\\
    return self.name \\\
end\\\
\\\
function cfxZones.linkUnitToZone(theUnit, theZone, dx, dy) -- note: dy is really Z, don't get confused!!!!\\\
    theZone.linkedUnit = theUnit\\\
    if not dx then dx = 0 end\\\
    if not dy then dy = 0 end \\\
    theZone.dx = dx\\\
    theZone.dy = dy \\\
    theZone.rxy = math.sqrt(dx * dx + dy * dy) -- radius \\\
    local unitHeading = dcsCommon.getUnitHeading(theUnit)\\\
    local bearingOffset = math.atan2(dy, dx) -- rads \\\
    if bearingOffset < 0 then bearingOffset = bearingOffset + 2 * 3.141592 end \\\
\\\
    local dPhi = bearingOffset - unitHeading\\\
    if dPhi < 0 then dPhi = dPhi + 2 * 3.141592 end\\\
    if (theZone.verbose and theZone.useHeading) then \\\
        trigger.action.outText(\\\"Zone is at <\\\" .. math.floor(57.2958 * dPhi) .. \\\"> relative to unit heading\\\", 30)\\\
    end\\\
    theZone.dPhi = dPhi -- constant delta between unit heading and \\\
    -- direction to zone \\\
    theZone.uHdg = unitHeading -- original unit heading to turn other \\\
    -- units if need be \\\
end\\\
\\\
function dmlZone:linkUnitToZone(theUnit, dx, dy) -- note: dy is really Z, don't get confused!!!!\\\
    self.linkedUnit = theUnit\\\
    if not dx then dx = 0 end\\\
    if not dy then dy = 0 end \\\
    self.dx = dx\\\
    self.dy = dy \\\
    self.rxy = math.sqrt(dx * dx + dy * dy) -- radius \\\
    local unitHeading = dcsCommon.getUnitHeading(theUnit)\\\
    local bearingOffset = math.atan2(dy, dx) -- rads \\\
    if bearingOffset < 0 then bearingOffset = bearingOffset + 2 * 3.141592 end \\\
\\\
    local dPhi = bearingOffset - unitHeading\\\
    if dPhi < 0 then dPhi = dPhi + 2 * 3.141592 end\\\
    if (self.verbose and self.useHeading) then \\\
        trigger.action.outText(\\\"Zone <\\\" .. self.name .. \\\"> is at <\\\" .. math.floor(57.2958 * dPhi) .. \\\"> relative to unit heading\\\", 30)\\\
    end\\\
    self.dPhi = dPhi -- constant delta between unit heading and \\\
    -- direction to zone \\\
    self.uHdg = unitHeading -- original unit heading to turn other \\\
    -- units if need be \\\
end\\\
\\\
function cfxZones.zonesLinkedToUnit(theUnit) -- returns all zones linked to this unit \\\
    if not theUnit then return {} end \\\
    local linkedZones = {}\\\
    for idx, theZone in pairs (cfxZones.zones) do \\\
        if theZone.linkedUnit == theUnit then \\\
            table.insert(linkedZones, theZone)\\\
        end\\\
    end\\\
    return linkedZones\\\
end\\\
\\\
function cfxZones.calcHeadingOffset(aZone, theUnit)\\\
    -- recalc dx and dy based on ry and current heading \\\
    -- since 0 degrees is [0,1] = [0,r] the calculation of \\\
    -- rotated coords can be simplified from \\\
    -- xr = x cos phi - y sin phi = -r sin phi\\\
    -- yr = y cos phi + x sin phi = r cos phi \\\
    local unitHeading = dcsCommon.getUnitHeading(theUnit)\\\
    -- add heading offset \\\
    local zoneBearing = unitHeading + aZone.dPhi \\\
    if zoneBearing > 2 * 3.141592 then zoneBearing = zoneBearing - 2 * 3.141592 end \\\
                    \\\
    -- in DCS, positive x is north (wtf?) and positive z is east \\\
    local dy = (-aZone.rxy) * math.sin(zoneBearing)\\\
    local dx = aZone.rxy * math.cos(zoneBearing)\\\
    return dx, -dy -- note: dy is z coord!!!!\\\
end\\\
\\\
function dmlZone:calcHeadingOffset(theUnit)\\\
    local unitHeading = dcsCommon.getUnitHeading(theUnit)\\\
    local zoneBearing = unitHeading + self.dPhi \\\
    if zoneBearing > 2 * 3.141592 then zoneBearing = zoneBearing - 2 * 3.141592 end \\\
    -- in DCS, positive x is north (wtf?) and positive z is east \\\
    local dy = (-self.rxy) * math.sin(zoneBearing)\\\
    local dx = self.rxy * math.cos(zoneBearing)\\\
    return dx, -dy -- note: dy is z coord!!!!\\\
end\\\
\\\
\\\
function cfxZones.updateMovingZones()\\\
    cfxZones.updateSchedule = timer.scheduleFunction(cfxZones.updateMovingZones, {}, timer.getTime() + 1/cfxZones.ups)\\\
    -- simply scan all cfx zones for the linkName property, and if present\\\
    -- update the zone's points\\\
    for aName,aZone in pairs(cfxZones.zones) do\\\
        -- only do this if ther is a linkName property, \\\
        -- else this zone isn't linked. link name is harmonized from \\\
        -- both linkUnit non-DML and linedUnit DML        \\\
        if aZone.linkName then \\\
            if aZone.linkBroken then \\\
                -- try to relink \\\
                cfxZones.initLink(aZone)\\\
            else --if aZone.linkName then  \\\
                -- always re-acquire linkedUnit via Unit.getByName()\\\
                -- this way we gloss over any replacements via spawns\\\
                aZone.linkedUnit = Unit.getByName(aZone.linkName)\\\
            end\\\
            \\\
            if aZone.linkedUnit then \\\
                local theUnit = aZone.linkedUnit\\\
                -- has a link. is link existing?\\\
                if theUnit:isExist() then \\\
                    cfxZones.centerZoneOnUnit(aZone, theUnit)\\\
                    local dx = aZone.dx \\\
                    local dy = aZone.dy -- this is actually z \\\
                    if aZone.useHeading then \\\
                        dx, dy = cfxZones.calcHeadingOffset(aZone, theUnit)\\\
                    end\\\
                    cfxZones.offsetZone(aZone, dx, dy)\\\
                else \\\
                    -- we lost link (track level)\\\
                    aZone.linkBroken = true \\\
                    aZone.linkedUnit = nil \\\
                end\\\
            else \\\
                -- we lost link (top level)\\\
                aZone.linkBroken = true \\\
                aZone.linkedUnit = nil \\\
            end\\\
        else \\\
            -- this zone isn't linked\\\
        end\\\
    end\\\
end\\\
\\\
function cfxZones.initLink(theZone)\\\
    theZone.linkBroken = true \\\
    theZone.linkedUnit = nil \\\
    theUnit = Unit.getByName(theZone.linkName)\\\
    if theUnit then\\\
\\\
        local dx = 0\\\
        local dz = 0\\\
        if theZone.useOffset or theZone.useHeading then \\\
            local A = cfxZones.getDCSOrigin(theZone)\\\
            local B = theUnit:getPoint()\\\
            local delta = dcsCommon.vSub(A,B) \\\
            dx = delta.x \\\
            dz = delta.z\\\
        end\\\
        cfxZones.linkUnitToZone(theUnit, theZone, dx, dz) -- also sets theZone.linkedUnit\\\
\\\
        if theZone.verbose then \\\
            trigger.action.outText(\\\"Link established for zone <\\\" .. theZone.name .. \\\"> to unit <\\\" .. theZone.linkName .. \\\">: dx=<\\\" .. math.floor(dx) .. \\\">, dz=<\\\" .. math.floor(dz) .. \\\"> dist = <\\\" .. math.floor(math.sqrt(dx * dx + dz * dz)) .. \\\">\\\" , 30)\\\
        end \\\
        theZone.linkBroken = nil \\\
\\\
    else \\\
        if theZone.verbose then \\\
            trigger.action.outText(\\\"Linked unit: no unit <\\\" .. theZone.linkName .. \\\"> to link <\\\" .. theZone.name .. \\\"> to\\\", 30)\\\
        end\\\
    end\\\
end\\\
\\\
function dmlZone:initLink()\\\
    self.linkBroken = true \\\
    self.linkedUnit = nil \\\
    theUnit = Unit.getByName(self.linkName)\\\
    if theUnit then\\\
\\\
        local dx = 0\\\
        local dz = 0\\\
        if self.useOffset or self.useHeading then \\\
            local A = self:getDCSOrigin()\\\
            local B = theUnit:getPoint()\\\
            local delta = dcsCommon.vSub(A,B) \\\
            dx = delta.x \\\
            dz = delta.z\\\
        end\\\
        self:linkUnitToZone(theUnit, dx, dz) -- also sets theZone.linkedUnit\\\
\\\
        if self.verbose then \\\
            trigger.action.outText(\\\"Link established for zone <\\\" .. self.name .. \\\"> to unit <\\\" .. self.linkName .. \\\">: dx=<\\\" .. math.floor(dx) .. \\\">, dz=<\\\" .. math.floor(dz) .. \\\"> dist = <\\\" .. math.floor(math.sqrt(dx * dx + dz * dz)) .. \\\">\\\" , 30)\\\
        end \\\
        self.linkBroken = nil \\\
\\\
    else \\\
        if self.verbose then \\\
            trigger.action.outText(\\\"Linked unit: no unit <\\\" .. self.linkName .. \\\"> to link <\\\" .. self.name .. \\\"> to\\\", 30)\\\
        end\\\
    end\\\
end\\\
\\\
function cfxZones.startMovingZones()\\\
    -- read all zones, and look for a property called 'linkedUnit'\\\
    -- which will make them a linked zone if there is a unit that exists\\\
    -- also suppors 'useOffset' and 'useHeading'\\\
    for aName,aZone in pairs(cfxZones.zones) do\\\
        \\\
        local lU = nil \\\
        -- check if DCS zone has the linkUnit new attribute introduced in \\\
        -- late 2022 with 2.8\\\
        if aZone.dcsZone.linkUnit then \\\
            local theID = aZone.dcsZone.linkUnit \\\
            lU = dcsCommon.getUnitNameByID(theID)\\\
            if not lU then \\\
                trigger.action.outText(\\\"WARNING: Zone <\\\" .. aZone.name .. \\\">: cannot resolve linked unit ID <\\\" .. theID .. \\\">\\\", 30)\\\
                lU = \\\"***DML link err***\\\"\\\
            end\\\
        elseif cfxZones.hasProperty(aZone, \\\"linkedUnit\\\") then \\\
            lU = cfxZones.getZoneProperty(aZone, \\\"linkedUnit\\\")\\\
        end\\\
        \\\
        -- sanity check \\\
        if aZone.dcsZone.linkUnit and cfxZones.hasProperty(aZone, \\\"linkedUnit\\\") then \\\
            trigger.action.outText(\\\"WARNING: Zone <\\\" .. aZone.name .. \\\"> has dual unit link definition. Will use link to unit <\\\" .. lU .. \\\">\\\", 30)\\\
        end\\\
        \\\
        if lU then \\\
            aZone.linkName = lU\\\
            aZone.useOffset = cfxZones.getBoolFromZoneProperty(aZone, \\\"useOffset\\\", false)\\\
            aZone.useHeading = cfxZones.getBoolFromZoneProperty(aZone, \\\"useHeading\\\", false)\\\
            \\\
            cfxZones.initLink(aZone)\\\
\\\
        end\\\
        \\\
    end\\\
end\\\
\\\
--\\\
-- marking zones \\\
--\\\
\\\
function cfxZones.spreadNObjectsOverLine(theZone, n, objType, left, right, cty) -- leaves last position free \\\
    trigger.action.outText(\\\"left = \\\" .. dcsCommon.point2text(left) .. \\\", right = \\\" .. dcsCommon.point2text(right),30)\\\
    \\\
    local a = {x=left.x, y=left.z}\\\
    local b = {x=right.x, y=right.z}\\\
    local dir = dcsCommon.vSub(b,a) -- vector from left to right\\\
    local dirInc = dcsCommon.vMultScalar(dir, 1/n) \\\
    local count = 0 \\\
    local p = {x=left.x, y = left.z}\\\
    local baseName = dcsCommon.uuid(theZone.name)\\\
    while count < n do \\\
        local theStaticData = dcsCommon.createStaticObjectData(dcsCommon.uuid(theZone.name), objType)\\\
        dcsCommon.moveStaticDataTo(theStaticData, p.x, p.y)\\\
        local theObject = coalition.addStaticObject(cty, theStaticData)\\\
        p = dcsCommon.vAdd(p, dirInc) \\\
        count = count + 1\\\
    end\\\
end\\\
\\\
function cfxZones.markZoneWithObjects(theZone, objType, qtrNum, markCenter, cty) -- returns set \\\
    if not objType then objType = \\\"Black_Tyre_RF\\\" end \\\
    if not qtrNum then qtrNum = 3 end -- +1 for number of marks per quarter \\\
    if not cty then cty = dcsCommon.getACountryForCoalition(0) end -- some neutral county\\\
    local p = theZone:getPoint()\\\
    local newObjects = {}\\\
    \\\
    if theZone.isPoly then \\\
        -- we place 4 * (qtrnum + 1) objects around the edge of the zone \\\
        -- we mark each poly along v-->v+1, placing ip and qtrNum additional points \\\
        local o = cfxZones.spreadNObjectsOverLine(theZone, qtrNum + 1, objType, theZone.poly[1], theZone.poly[2], cty)\\\
        local p = cfxZones.spreadNObjectsOverLine(theZone, qtrNum + 1, objType, theZone.poly[2], theZone.poly[3], cty)\\\
        local q = cfxZones.spreadNObjectsOverLine(theZone, qtrNum + 1, objType, theZone.poly[3], theZone.poly[4], cty)\\\
        local r = cfxZones.spreadNObjectsOverLine(theZone, qtrNum + 1, objType, theZone.poly[4], theZone.poly[1], cty)\\\
        o = dcsCommon.combineTables(o,p)\\\
        p = dcsCommon.combineTables(q,r)\\\
        newObjects = dcsCommon.combineTables(o,p)\\\
        \\\
    else \\\
        local numObjects = (qtrNum + 1) * 4\\\
        local degrees = 3.14157 / 180\\\
        local degreeIncrement = (360 / numObjects) * degrees\\\
        local currDegree = 0\\\
        local radius = theZone.radius\\\
        for i=1, numObjects do \\\
            local ox = p.x + math.cos(currDegree) * radius\\\
            local oy = p.z + math.sin(currDegree) * radius -- note: z!\\\
            local theStaticData = dcsCommon.createStaticObjectData(dcsCommon.uuid(theZone.name), objType)\\\
            dcsCommon.moveStaticDataTo(theStaticData, ox, oy)\\\
            local theObject = coalition.addStaticObject(cty, theStaticData)\\\
            table.insert(newObjects, theObject)\\\
            currDegree = currDegree + degreeIncrement\\\
        end\\\
    end\\\
    \\\
    if markCenter then \\\
        -- also mark the center \\\
        local theObject = cfxZones.markPointWithObject(p, objType, cty)\\\
        table.insert(newObjects, theObject)\\\
    end     \\\
    \\\
    return newObjects\\\
end\\\
\\\
function dmlZone:markZoneWithObjects(objType, qtrNum, markCenter, cty) -- returns set \\\
    return cfxZones.markZoneWithObjects(self, objType, qtrNum, markCenter)\\\
end\\\
\\\
function cfxZones.markCenterWithObject(theZone, objType, cty) -- returns object\\\
    local p = cfxZones.getPoint(theZone)\\\
    local theObject = cfxZones.markPointWithObject(theZone, p, objType, cty)\\\
    return theObject\\\
end\\\
\\\
function dmlZone:markCenterWithObject(objType, cty) -- returns object \\\
    return cfxZones.markCenterWithObject(self, objType, cty)\\\
end\\\
\\\
function cfxZones.markPointWithObject(theZone, p, theType, cty) -- returns object \\\
    if not cty then cty = dcsCommon.getACountryForCoalition(0) end\\\
    local ox = p.x\\\
    local oy = p.y     \\\
    if p.z then oy = p.z end -- support vec 2 and vec 3 \\\
    local theStaticData = dcsCommon.createStaticObjectData(dcsCommon.uuid(theZone.name), theType)\\\
    dcsCommon.moveStaticDataTo(theStaticData, ox, oy)\\\
    local theObject = coalition.addStaticObject(cty, theStaticData)\\\
    return theObject\\\
end\\\
\\\
function dmlZone:markPointWithObject(p, theType, cty) -- returns object \\\
    return cfxZones.markPointWithObject(self, p, theType, cty)\\\
end\\\
--\\\
-- ===========\\\
-- INIT MODULE\\\
-- ===========\\\
--\\\
\\\
function cfxZones.initZoneVerbosity()\\\
    for aName,aZone in pairs(cfxZones.zones) do\\\
        -- support for zone-local verbose flag \\\
        aZone.verbose = cfxZones.getBoolFromZoneProperty(aZone, \\\"verbose\\\", false)\\\
    end\\\
end\\\
\\\
function cfxZones.init()\\\
    -- read all zones into my own db\\\
    cfxZones.readFromDCS(true) -- true: erase old\\\
\\\
    -- pre-read zone owner for all zones\\\
    -- much like verbose, all zones have owner\\\
    for n, aZone in pairs(cfxZones.zones) do\\\
        aZone.owner = cfxZones.getCoalitionFromZoneProperty(aZone, \\\"owner\\\", 0)\\\
    end\\\
        \\\
    -- enable all zone's verbose flags if present\\\
    -- must be done BEFORE we start the moving zones \\\
    cfxZones.initZoneVerbosity()\\\
    \\\
    -- now initialize moving zones\\\
    cfxZones.startMovingZones()\\\
    cfxZones.updateMovingZones() -- will auto-repeat\\\
    \\\
    trigger.action.outText(\\\"cf/x Zones v\\\".. cfxZones.version .. \\\": loaded, zones:\\\" .. dcsCommon.getSizeOfTable(cfxZones.zones), 30)\\\
\\\
end\\\
\\\
-- get everything rolling\\\
cfxZones.init()\\\
\");a_do_script(\"-- cfxCommander - issue dcs commands to groups etc\\\
--\\\
-- supports scheduling\\\
-- *** EXTENDS ZONES: 'pathing' attribute \\\
--\\\
cfxCommander = {}\\\
cfxCommander.version = \\\"1.1.4\\\"\\\
--[[-- VERSION HISTORY\\\
 - 1.0.5 - createWPListForGroupToPointViaRoads: detect no road found \\\
 - 1.0.6 - build in more group checks in assign wp list \\\
         - added sanity checks for doScheduledTask\\\
         - assignWPListToGroup now can schedule tasks \\\
         - makeGroupGoThere supports scheduling\\\
         - makeGroupGoTherePreferringRoads supports scheduling \\\
         - scheduleTaskForGroup supports immediate execution\\\
         - makeGroupHalt\\\
 - 1.0.7 - warning if road shorter than direct\\\
         - forceOffRoad option\\\
         - noRoadsAtAll option \\\
 - 1.1.0 - load libs \\\
         - pathing zones. Currently only supports \\\
         - offroad to override road-usage\\\
         - pathing zones are overridden by noRoadsAtAll\\\
         - CommanderConfig zones \\\
 - 1.1.1 - default pathing for pathing zone is normal, not offroad \\\
 - 1.1.2 - makeGroupTransmit \\\
         - makeGroupStopTransmitting\\\
         - verbose check before path warning\\\
         - added delay defaulting for most scheduling functions \\\
 - 1.1.3 - isExist() guard improvements for multiple methods\\\
         - cleaned up comments\\\
 - 1.1.4 - hardened makeGroupGoThere()\\\
 \\\
--]]--\\\
\\\
cfxCommander.requiredLibs = {\\\
    \\\"dcsCommon\\\", -- common is of course needed for everything\\\
    \\\"cfxZones\\\", -- zones management for pathing zones \\\
}\\\
\\\
cfxCommander.verbose = false \\\
cfxCommander.forceOffRoad = true -- if true, vehicles path follow roads, but may drive offroad (they follow vertex points from path but not the road as they are still commanded 'offroad')\\\
cfxCommander.noRoadsAtAll = true  -- if true, always go direct, overrides forceOffRoad when true. Always a two-point path. Here, there, bang! \\\
cfxCommander.pathZones = {} -- zones that can override road settings\\\
\\\
--\\\
-- path zone\\\
--\\\
function cfxCommander.processPathingZone(aZone) -- process attribute and add to zone\\\
    local pathing = cfxZones.getStringFromZoneProperty(aZone, \\\"pathing\\\", \\\"normal\\\") -- must be \\\"offroad\\\" to force offroad\\\
    pathing = pathing:lower()\\\
    -- currently no validation of attribute \\\
    aZone.pathing = pathing\\\
end \\\
\\\
function cfxCommander.addPathingZone(aZone)\\\
    table.insert(cfxCommander.pathZones, aZone)\\\
end \\\
\\\
function cfxCommander.hasPathZoneFor(here, there)\\\
    for idx, aZone in pairs(cfxCommander.pathZones) do \\\
        if cfxZones.pointInZone(here, aZone) then return aZone end \\\
        if cfxZones.pointInZone(there, aZone) then return aZone end\\\
    end\\\
    return nil\\\
end\\\
\\\
--\\\
-- Config Zone Reading if present \\\
--\\\
function cfxCommander.readConfigZone()\\\
    -- note: must match exactly!!!!\\\
    local theZone = cfxZones.getZoneByName(\\\"CommanderConfig\\\") \\\
    if not theZone then \\\
        trigger.action.outText(\\\"+++cmdr: no config zone!\\\", 30) \\\
        return \\\
    end \\\
    \\\
    trigger.action.outText(\\\"+++cmdr: found config zone!\\\", 30) \\\
    \\\
    cfxCommander.verbose = cfxZones.getBoolFromZoneProperty(theZone, \\\"verbose\\\", false)\\\
    cfxCommander.forceOffRoad = cfxZones.getBoolFromZoneProperty(theZone, \\\"forceOffRoad\\\", false) -- if true, vehicles path follow roads, but may drive offroad\\\
    cfxCommander.noRoadsAtAll = cfxZones.getBoolFromZoneProperty(theZone, \\\"noRoadsAtAll\\\", false)\\\
\\\
end\\\
\\\
--\\\
-- Options are key, value pairs. Scheduler when you are creating groups\\\
-- \\\
\\\
function cfxCommander.doOption(data) \\\
    if cfxCommander.verbose then \\\
        trigger.action.outText(\\\"Commander: setting option \\\" .. data.key .. \\\" --> \\\" .. data.value, 30)\\\
    end\\\
\\\
    local theController = data.group:getController()\\\
    theController:setOption(data.key, data.value)\\\
end\\\
\\\
function cfxCommander.scheduleOptionForGroup(group, key, value, delay) \\\
    local data = {}\\\
    if not delay then delay = 0.1 end \\\
    data.group = group\\\
    data.key = key\\\
    data.value = value \\\
    timer.scheduleFunction(cfxCommander.doOption, data, timer.getTime() + delay)\\\
end\\\
\\\
--\\\
-- performCommand is a special version of issuing a command\\\
-- that can be easily schduled by pushing the commandData on \\\
-- the stack with scheduling it \\\
-- group or name must be filled to get the group,\\\
-- and the command table is what is going to be passed to the setCommand\\\
-- commands are given in an array, so you can stack commands \\\
function cfxCommander.performCommands(commandData)\\\
    -- see if we have a group\\\
    if not commandData.group then \\\
        commandData.group = Group.getByName(commandData.name) -- better be inited!\\\
    end\\\
    -- get the AI\\\
    local theController = commandData.group:getController()\\\
    for i=1, #commandData.commands do\\\
        if cfxCommander.verbose then \\\
            trigger.action.outText(\\\"Commander: performing \\\" .. commandData.commands[i].id, 30)\\\
        end\\\
        theController:setCommand(commandData.commands[i])\\\
    end\\\
    \\\
    return nil -- a timer called us, so we return no desire to be rescheduled\\\
end\\\
\\\
function cfxCommander.scheduleCommands(data, delay)\\\
    if not delay then delay = 1 end \\\
    timer.scheduleFunction(cfxCommander.performCommands, data, timer.getTime() + delay)\\\
end\\\
\\\
function cfxCommander.scheduleSingleCommand(group, command, delay) \\\
    if not delay then delay = 1 end \\\
    local data = createCommandDataTableFor(group)\\\
    cfxCommander.addCommand(data, command)\\\
    cfxCommander.scheduleCommands(data, delay)\\\
end\\\
\\\
\\\
function cfxCommander.createCommandDataTableFor(group, name)\\\
    local cD = {}\\\
    if not group then \\\
        cD.name = name\\\
    else\\\
        cD.group = group\\\
    end\\\
    cD.commands={}\\\
    return cD\\\
end\\\
\\\
function cfxCommander.addCommand(theCD, theCommand)\\\
    if not theCD then return end \\\
    if not theCommand then return end \\\
    \\\
    table.insert(theCD.commands, theCommand)\\\
end\\\
\\\
function cfxCommander.createSetFrequencyCommand(freq, modulator)\\\
    local theCmd = {}\\\
    if not freq then freq = 100 end \\\
    if not modulator then modulator = 0 end -- AM = 0, default\\\
    theCmd.id = 'SetFrequency'\\\
    theCmd.params = {}\\\
    theCmd.params.frequency = freq * 10000 -- 88 --> 880000. 124 --> 1.24 MHz\\\
    theCmd.params.modulation = modulator\\\
    return theCmd\\\
end\\\
\\\
-- oneShot is optional. if present and anything but false, will cause message to \\\
-- me sent only once, no loops\\\
function cfxCommander.createTransmissionCommand(filename, oneShot)\\\
    local looping = true\\\
    if not filename then filename = \\\"dummy\\\" end \\\
    if oneShot then looping = false end\\\
    local theCmd = {}\\\
    theCmd.id = 'TransmitMessage'\\\
    theCmd.params = {}\\\
    theCmd.params.loop = looping\\\
    theCmd.params.file = \\\"l10n/DEFAULT/\\\" .. filename -- need to prepend the resource string\\\
    return theCmd\\\
end\\\
\\\
function cfxCommander.createStopTransmissionCommand()\\\
    local theCmd = {}\\\
    theCmd.id = 'stopTransmission'\\\
    theCmd.params = {}\\\
    return theCmd\\\
end\\\
\\\
--\\\
-- tasks\\\
-- \\\
\\\
function cfxCommander.doScheduledTask(data) \\\
    if cfxCommander.verbose then \\\
        trigger.action.outText(\\\"Commander: setting task \\\" .. data.task.id .. \\\" for group \\\" .. data.group:getName(), 30)\\\
    end\\\
    local theGroup = data.group \\\
    if not theGroup then return end \\\
    if not Group.isExist(theGroup) then return end \\\
--    if not theGroup.isExist then return end\\\
    \\\
    local theController = theGroup:getController()\\\
    theController:pushTask(data.task)\\\
end\\\
\\\
function cfxCommander.scheduleTaskForGroup(group, task, delay)\\\
    if not delay then delay = 0 end \\\
    local data = {}\\\
    data.group = group\\\
    data.task = task\\\
    if delay < 0.001 then \\\
        cfxCommander.doScheduledTask(data) -- immediate execution\\\
        return \\\
    end\\\
    timer.scheduleFunction(cfxCommander.doScheduledTask, data, timer.getTime() + delay)\\\
end\\\
\\\
function cfxCommander.createAttackGroupCommand(theGroupToAttack)\\\
    local task = {}\\\
    task.id = 'AttackGroup'\\\
    task.params = {}\\\
    task.params.groupID = theGroupToAttack:getID()\\\
    return task\\\
end\\\
\\\
function cfxCommander.createEngageGroupCommand(theGroupToAttack)\\\
    local task = {}\\\
    task.id = 'EngageGroup'\\\
    task.params = {}\\\
    task.params.groupID = theGroupToAttack:getID()\\\
    return task\\\
end\\\
\\\
--\\\
-- waypoints, routes etc \\\
--\\\
\\\
-- basic waypoint is for ground units. point can be xyz or xy \\\
function cfxCommander.createBasicWaypoint(point, speed, formation)\\\
    local wp = {}\\\
    wp.x = point.x\\\
    -- support xyz and xy format\\\
    if point.z then \\\
        wp.y = point.z\\\
    else\\\
        wp.y = point.y\\\
    end\\\
    \\\
    if not speed then speed = 6 end -- 6 m/s = 20 kph\\\
    wp.speed = speed \\\
    \\\
    if cfxCommander.forceOffRoad then \\\
        formation = \\\"Off Road\\\"\\\
    end\\\
    \\\
    if not formation then formation = \\\"Off Road\\\" end\\\
    -- legal formations:\\\
    -- Off road\\\
    -- On Road -- second letter upper case?\\\
    -- Cone \\\
    -- Rank\\\
    -- Diamond\\\
    -- Vee\\\
    -- EchelonR\\\
    -- EchelonL\\\
    wp.action = formation -- silly name, but that's how ME does it\\\
    wp.type = 'Turning Point'\\\
    return wp\\\
\\\
end\\\
\\\
function cfxCommander.buildTaskFromWPList(wpList)\\\
    -- build the task that will make a group follow the WP list\\\
    -- we do this by creating a \\\"Mission\\\" task around the WP List\\\
    -- WP list is consumed by this action\\\
    local missionTask = {}\\\
    missionTask.id = \\\"Mission\\\"\\\
    missionTask.params = {}\\\
    missionTask.params.route = {}\\\
    missionTask.params.route.points=wpList\\\
    return missionTask\\\
end\\\
\\\
function cfxCommander.assignWPListToGroup(group, wpList, delay)\\\
    if not delay then delay = 0 end \\\
    if not group then return end \\\
    if type(group) == 'string' then -- group name, nice mist trick \\\
        group = Group.getByName(group)\\\
    end\\\
    if not group then return end \\\
    if not Group.isExist(group) then return end \\\
    \\\
    local theTask = cfxCommander.buildTaskFromWPList(wpList)\\\
    local ctrl = group:getController()\\\
\\\
--[[--\\\
    if delay < 0.001 then -- immediate action\\\
        if ctrl then\\\
            ctrl:setTask(theTask)\\\
        end\\\
    else \\\
        -- delay execution of this command by the specified amount \\\
        -- of seconds \\\
        cfxCommander.scheduleTaskForGroup(group, theTask, delay)\\\
    end\\\
--]]--\\\
    cfxCommander.scheduleTaskForGroup(group, theTask, delay)\\\
end\\\
\\\
function cfxCommander.createWPListForGroupToPoint(group, point, speed, formation)\\\
    if type(group) == 'string' then -- group name\\\
        group = Group.getByName(group)\\\
    end\\\
\\\
    local wpList = {}\\\
    -- here we are, and we want to go there. In DCS, this means that\\\
    -- we need to create a wp list consisting of here and there\\\
    local here = dcsCommon.getGroupLocation(group)\\\
    local wpHere = cfxCommander.createBasicWaypoint(here, speed, formation)\\\
    local wpThere = cfxCommander.createBasicWaypoint(point, speed, formation)\\\
    wpList[1] = wpHere\\\
    wpList[2] = wpThere\\\
    return wpList\\\
end\\\
\\\
-- make a ground units group head to a waypoint by replacing the entire mission\\\
-- with a two-waypoint lsit from (here) to there at speed and formation. formation\\\
-- default is 'off road'\\\
function cfxCommander.makeGroupGoThere(group, there, speed, formation, delay)\\\
    if not delay then delay = 0 end \\\
    if type(group) == 'string' then -- group name\\\
        group = Group.getByName(group)\\\
    end\\\
\\\
    if not Group.isExist(group) then \\\
        trigger.action.outText(\\\"cmdr: makeGroupGoThere() - group does not exist\\\", 30)\\\
        return \\\
    end \\\
\\\
    -- check that we can get a location for the group \\\
    local here = dcsCommon.getGroupLocation(group)\\\
    if not here then \\\
        return \\\
    end \\\
    \\\
    local wp = cfxCommander.createWPListForGroupToPoint(group, there, speed, formation)\\\
    \\\
    cfxCommander.assignWPListToGroup(group, wp, delay)\\\
end\\\
\\\
function cfxCommander.calculatePathLength(roadPoints)\\\
    local totalLen = 0\\\
    if #roadPoints < 2 then return 0 end\\\
    for i=1, #roadPoints-1 do\\\
        totalLen = totalLen + dcsCommon.dist(roadPoints[i], roadPoints[i+1])\\\
    end\\\
    return totalLen\\\
end\\\
\\\
-- make ground units go from here (group location) to there, using roads if possible\\\
function cfxCommander.createWPListForGroupToPointViaRoads(group, point, speed)\\\
    if type(group) == 'string' then -- group name\\\
        group = Group.getByName(group)\\\
    end\\\
\\\
    local wpList = {}\\\
    -- here we are, and we want to go there. In DCS, this means that\\\
    -- we need to create a wp list consisting of here and there\\\
    -- when going via roads, we add to more wayoints:\\\
    -- go on-roads and leaveRoads. \\\
    -- only if we can get these two additional points, we do that, else we \\\
    -- fall back to direct route \\\
    \\\
    local here = dcsCommon.getGroupLocation(group)\\\
\\\
    -- now generate a list of all points from here to there that uses roads\\\
    local rawRoadPoints = land.findPathOnRoads('roads', here.x, here.z, point.x, point.z)\\\
    -- this is the entire path. calculate the length and make \\\
    -- sure that path on-road isn't more than twice as long \\\
    -- that can happen if a bridge is out or we need to go around a hill\\\
    if not rawRoadPoints or #rawRoadPoints<3 then \\\
        trigger.action.outText(\\\"+++ no roads leading there. Taking direct approach\\\", 30)\\\
        return cfxCommander.createWPListForGroupToPoint(group, point, speed)\\\
    end\\\
    \\\
    local pathLength = cfxCommander.calculatePathLength(rawRoadPoints)\\\
    local direct = dcsCommon.dist(here, point)\\\
    if pathLength < direct and cfxCommander.verbose then \\\
        trigger.action.outText(\\\"+++dcsC: WARNING road path (\\\" .. pathLength .. \\\") shorter than direct route(\\\" .. direct .. \\\"), will not path correctly\\\", 30)\\\
    end\\\
    \\\
    if pathLength > (2 * direct) then \\\
        -- road takes too long, take direct approach\\\
        --trigger.action.outText(\\\"+++ road path (\\\" .. pathLength .. \\\") > twice direct route(\\\" .. direct .. \\\"), commencing direct off-road\\\", 30)\\\
        return cfxCommander.createWPListForGroupToPoint(group, point, speed)\\\
    end\\\
    \\\
    --trigger.action.outText(\\\"+++ \\\".. group:getName() .. \\\": choosing road path l=\\\" .. pathLength .. \\\" over direct route d=\\\" .. direct, 30)\\\
    \\\
    -- if we are here, the road trip is valid \\\
    for idx, wp in pairs(rawRoadPoints) do \\\
        -- createBasic... supports w.xy format\\\
        local theNewWP = cfxCommander.createBasicWaypoint(wp, speed, \\\"On Road\\\") -- force off road for better compatibility?\\\
        table.insert(wpList, theNewWP)\\\
    end\\\
    \\\
    \\\
    \\\
    -- now make first and last entry OFF Road\\\
    local wpc = wpList[1]\\\
    wpc.action = \\\"Off Road\\\"\\\
    wpc = wpList[#wpList]\\\
    wpc.action = \\\"Off Road\\\"\\\
\\\
    return wpList\\\
end\\\
\\\
function cfxCommander.makeGroupGoTherePreferringRoads(group, there, speed, delay)\\\
    if type(group) == 'string' then -- group name\\\
        group = Group.getByName(group)\\\
    end\\\
    if not delay then delay = 0 end \\\
\\\
\\\
    if cfxCommander.noRoadsAtAll then \\\
        -- we don't even follow roads, completely forced off\\\
        cfxCommander.makeGroupGoThere(group, there, speed, \\\"Off Road\\\", delay)\\\
        return \\\
    end\\\
\\\
    -- see if we have an override situation \\\
    -- for one of the two points where a pathing Zone \\\
    -- overrides the roads setting \\\
    if #cfxCommander.pathZones > 0 then  \\\
        local here = dcsCommon.getGroupLocation(group)\\\
        local oRide = cfxCommander.hasPathZoneFor(here, there)\\\
        if oRide and oRide.pathing == \\\"offroad\\\" then \\\
            -- yup, override road preference\\\
            cfxCommander.makeGroupGoThere(group, there, speed, \\\"Off Road\\\", delay)\\\
            return \\\
        end\\\
    end\\\
\\\
    -- viaRoads will only use roads if the road trip isn't more than twice \\\
    -- as long as the direct route \\\
    local wp = cfxCommander.createWPListForGroupToPointViaRoads(group, there, speed)\\\
    cfxCommander.assignWPListToGroup(group, wp, delay)\\\
end\\\
\\\
\\\
function cfxCommander.makeGroupHalt(group, delay)\\\
    if not group then return end \\\
    if not Group.isExist(group) then return end \\\
    if not delay then delay = 0 end \\\
    local theTask = {id = 'Hold', params = {}}\\\
    cfxCommander.scheduleTaskForGroup(group, theTask, delay)\\\
end\\\
\\\
function cfxCommander.makeGroupTransmit(group, tenKHz, filename, oneShot, delay)\\\
    if not group then return end \\\
    if not tenKHz then tenKHz = 20 end -- default to 200KHz\\\
    if not delay then delay = 1.0 end \\\
    if not filename then return end \\\
    if not oneShot then oneShot = false end \\\
    \\\
    -- now build the transmission command\\\
    local theCommands = cfxCommander.createCommandDataTableFor(group)\\\
    local cmd = cfxCommander.createSetFrequencyCommand(tenKHz) -- freq in 10000 Hz\\\
    cfxCommander.addCommand(theCommands, cmd)\\\
    cmd = cfxCommander.createTransmissionCommand(filename, oneShot)\\\
    cfxCommander.addCommand(theCommands, cmd)\\\
    cfxCommander.scheduleCommands(theCommands, delay)\\\
end \\\
\\\
function cfxCommander.makeGroupStopTransmitting(group, delay)\\\
    if not delay then delay = 1 end \\\
    if not group then return end \\\
    local theCommands = cfxCommander.createCommandDataTableFor(group)\\\
    local cmd = cfxCommander.createStopTransmissionCommand()\\\
    cfxCommander.addCommand(theCommands, cmd)\\\
    cfxCommander.scheduleCommands(theCommands, delay)\\\
end\\\
\\\
\\\
function cfxCommander.start()\\\
    -- make sure we have loaded all relevant libraries \\\
    if not dcsCommon.libCheck(\\\"cfx Commander\\\", cfxCommander.requiredLibs) then \\\
        trigger.action.outText(\\\"cf/x Commander aborted: missing libraries\\\", 30)\\\
        return false \\\
    end\\\
    \\\
    -- identify and process all 'pathing' zones\\\
    local pathZones = cfxZones.getZonesWithAttributeNamed(\\\"pathing\\\")\\\
    \\\
    -- now create a spawner for all, add them to the spawner updater, and spawn for all zones that are not\\\
    -- paused \\\
    for k, aZone in pairs(pathZones) do \\\
        cfxCommander.processPathingZone(aZone) -- process attribute and add to zone\\\
        cfxCommander.addPathingZone(aZone) -- remember it so we can smoke it\\\
    end\\\
    \\\
    -- read config overides \\\
    cfxCommander.readConfigZone()\\\
    \\\
    return true\\\
end\\\
\\\
if cfxCommander.start() then \\\
    trigger.action.outText(\\\"cfxCommander v\\\" .. cfxCommander.version .. \\\" loaded\\\", 30)\\\
else \\\
    trigger.action.outText(\\\"+++cfxCommander load FAILED\\\", 30)\\\
    cfxCommander = nil\\\
end\\\
\\\
--[[-- known issues\\\
\\\
- troops remain motionless until all are repaired or produced after cature\\\
- long roads / roads not taken in persia \\\
- all troops red and blue become motionless when one zone is occupied\\\
- after capture, the troop capturing remains, all others can go on. one will always remain there \\\
- rethink the factor to add to road, and simply add 100m \\\
\\\
 TODO: break long distances into smaller paths, and gravitate towards pathing zones if they have a 'gravitate' or similar attribute \\\
--]]--\\\
\");a_do_script(\"cfxGroundTroops = {}\\\
cfxGroundTroops.version = \\\"2.1.0\\\"\\\
cfxGroundTroops.ups = 1\\\
cfxGroundTroops.verbose = false \\\
cfxGroundTroops.requiredLibs = {\\\
    \\\"dcsCommon\\\", -- common is of course needed for everything\\\
                 -- pretty stupid to check for this since we \\\
                 -- need common to invoke the check, but anyway\\\
    \\\"cfxCommander\\\", -- generic data module for weight \\\
    -- cfxOwnedZones is optional \\\
}\\\
-- ground troops: a module to manage ground toops. makes groups of ground troops\\\
-- patrol and engage enemies and signal idle\\\
-- understands cfxOwnedZones orders 'attackOwnedZone' and will re-direct\\\
-- troops when a zone was captured by interacting with cfxOwnedZones to \\\
-- find the nearest non-owned zone and direct the group there \\\
\\\
-- USAGE\\\
-- Allocate a group in game and issue them marching orders towars a goal \\\
-- then createGroundTroops to allocate a structure used by this \\\
-- module and addTroopsToPool to have them then managed by this \\\
-- module \\\
\\\
cfxGroundTroops.deployedTroops = {} -- indexed by group name \\\
cfxGroundTroops.jtacCB = {} -- jtac callbacks, to be implemented \\\
\\\
--[[--\\\
 version history\\\
\\\
   2.0.0 - dmlZones \\\
         - jtacSound \\\
         - cleanup \\\
         - jtacVerbose \\\
   2.0.1 - small fiex ti checkPileUp()\\\
   2.1.0 - captureandhold - oneshot attackowned \\\
   \\\
\\\
  an entry into the deployed troop table has the following attributes\\\
  - group - the group \\\
  - orders: \\\"guard\\\" - will guard the spot and look for enemies in range\\\
            \\\"patrol\\\" - will walk between way points back and forth \\\
            \\\"laze\\\" - will stay in place and try to laze visible vehicles in range\\\
            \\\"attackOwnedZone\\\" - interface to cfxOwnedZones module, seeks out\\\
              enemy zones to attack and capture them\\\
            \\\"captureandhold\\\" - interface to ownedZones, seeks out nearest enemy \\\
              or neutral owned zone. once captured, it stays there \\\
            \\\"wait-<some other orders>\\\" do nothing. the \\\"wait\\\" prefix will be removed some time and <some other order> then revealed. Used at least by heloTroops\\\
            \\\"train\\\" - target dummies. ROE=HOLD, no ground loop \\\
            \\\"attack\\\" - transition to destination, once there, stop and \\\
            switch to guard. requires destination zone be set to a valid cfxZone\\\
  - coalition - the coalition from the group\\\
  - enemy - if set, the group this group it is engaging. this means the group is fighting and not idle\\\
  - name - name of group, dan be freely changed\\\
  - signature - \\\"cfx\\\" to tell apart from dcs groups \\\
  - range = range to look for enemies. default is 300m. In \\\"laze\\\" orders, range to laze\\\
  - lazeTarget - target currently lazing\\\
  - lazeCode - laser code. default is 1688\\\
  - moving - has been given orders to move somewhere already. used for first movement order with attack orders \\\
\\\
 \\\
 usage:\\\
 take a dcs group of ground troops and create a cfx ground troop record with \\\
  createGroundTroops()\\\
 then add this to the manager with \\\
  addGroundTroopsToPool()\\\
 \\\
 you can control what the group is to do by changing the cfx troop attribute orders \\\
 you can install a callback that will notify you if a troop reached a goal or\\\
 was killed with addTroopsCallback() which will also give a reason\\\
 callback pattern is myCallback(reason, theGroup, orders, data) with troop being the \\\
 group, and orders the original orders, and reason a string containing why the \\\
 callback was invoked. Currently defined reasons are\\\
   - \\\"dead\\\" - entire group was killed \\\
   - \\\"arrived\\\" - at least a part of group arrived at destination (only with some orders)\\\
--]]--\\\
\\\
--\\\
-- UPDATE MODELS\\\
-- standard is update all every time: fastest, but may cause \\\
-- performance issues\\\
-- queued will work one every pass (except for lazed), distributing the load much better \\\
-- schedueld installs a callback for each group separately and thus distributes the load over time much better \\\
\\\
function cfxGroundTroops.invokeCallbacks(ID, jtac, tgt, data)\\\
    -- IS is aqui, lost, dead, jtac died. jtac is group, tgt is unit, data is rest \\\
    for idx, cb in pairs(cfxGroundTroops.jtacCB) do \\\
        cb(ID, jtac, tgt, data)\\\
    end\\\
end\\\
\\\
function cfxGroundTroops.addJtacCB(theCB)\\\
    table.insert(cfxGroundTroops.jtacCB, theCB)\\\
end\\\
\\\
cfxGroundTroops.troopQueue = {} -- FIFO stack \\\
-- return the best tracking interval for this type of orders \\\
\\\
function cfxGroundTroops.readConfigZone()\\\
    local theZone = cfxZones.getZoneByName(\\\"groundTroopsConfig\\\") \\\
    if not theZone then \\\
        theZone = cfxZones.createSimpleZone(\\\"groundTroopsConfig\\\") \\\
    end \\\
        \\\
    cfxGroundTroops.queuedUpdates = theZone:getBoolFromZoneProperty(\\\"queuedUpdates\\\", false)\\\
    cfxGroundTroops.scheduledUpdates = theZone:getBoolFromZoneProperty(\\\"scheduledUpdates\\\", false)\\\
    cfxGroundTroops.maxManagedTroops = theZone:getNumberFromZoneProperty(\\\"maxManagedTroops\\\", 67)\\\
    cfxGroundTroops.monitorNumbers = theZone:getBoolFromZoneProperty(\\\"monitorNumbers\\\", false)\\\
    cfxGroundTroops.standardScheduleInterval = theZone:getNumberFromZoneProperty(\\\"standardScheduleInterval\\\", 30)    \\\
    cfxGroundTroops.guardUpdateInterval = theZone:getNumberFromZoneProperty(\\\"guardUpdateInterval\\\", 30)\\\
    cfxGroundTroops.trackingUpdateInterval = theZone:getNumberFromZoneProperty(\\\"trackingUpdateInterval\\\", 0.5)\\\
\\\
    cfxGroundTroops.jtacSound = theZone:getStringFromZoneProperty(\\\"jtacSound\\\", \\\"UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav\\\")\\\
    cfxGroundTroops.jtacVerbose = theZone:getBoolFromZoneProperty(\\\"jtacVerbose\\\", true)\\\
    cfxGroundTroops.laseCode = theZone:getNumberFromZoneProperty(\\\"jtacLaserCode\\\", 1688)\\\
    if theZone:hasProperty(\\\"lazeCode\\\") then \\\
        cfxGroundTroops.laseCode = theZone:getNumberFromZoneProperty(\\\"lazeCode\\\", 1688)\\\
    end \\\
    if theZone:hasProperty(\\\"laseCode\\\") then \\\
        cfxGroundTroops.laseCode = theZone:getNumberFromZoneProperty(\\\"laseCode\\\", 1688)\\\
    end \\\
    if theZone:hasProperty(\\\"laserCode\\\") then \\\
        cfxGroundTroops.laseCode = theZone:getNumberFromZoneProperty(\\\"laserCode\\\", 1688)\\\
    end \\\
    cfxGroundTroops.verbose = theZone.verbose \\\
\\\
    if cfxGroundTroops.verbose then \\\
        trigger.action.outText(\\\"+++gndT: read config zone!\\\", 30) \\\
    end\\\
end\\\
\\\
\\\
-- \\\
-- Callback handling\\\
--\\\
\\\
cfxGroundTroops.troopsCallback = {}\\\
\\\
function cfxGroundTroops.addTroopsCallback(theCallback)\\\
    table.insert(cfxGroundTroops.troopsCallback, theCallback)\\\
end\\\
\\\
function cfxGroundTroops.invokeCallbacksFor(reason, troops, data)\\\
    if not data then data = {} end\\\
    data.troops = troops \\\
    for idx, theCB in pairs (cfxGroundTroops.troopsCallback) do \\\
        theCB(reason, troops.group, troops.orders, data)\\\
    end\\\
end\\\
\\\
function cfxGroundTroops.getScheduleInterval(orders)\\\
    if orders == \\\"laze\\\" then \\\
        return cfxGroundTroops.trackingUpdateInterval\\\
    end\\\
    return cfxGroundTroops.standardScheduleInterval\\\
end\\\
\\\
-- create controller commands to attack a group \\\"enemies\\\"\\\
-- enemies are an attribute of the troop structure\\\
-- usually called from a group on guard when idling \\\
function cfxGroundTroops.makeTroopsEngageEnemies(troop)\\\
    local group = troop.group\\\
    if not Group.isExist(group) then \\\
        trigger.action.outText(\\\"+++gndT: troup don't exist, dropping\\\", 30)\\\
        return \\\
    end\\\
    \\\
    local enemies = troop.enemy\\\
    local from = dcsCommon.getGroupLocation(group)\\\
    if not from then return end -- the commandos died\\\
    local there = dcsCommon.getGroupLocation(enemies)\\\
    if not there then return end\\\
    \\\
    -- we lerp to 2/3 of enemy location\\\
    there = dcsCommon.vLerp(from, there, 0.66) \\\
    \\\
    local speed = 10 -- m/s = 10 km/h -- wait. 10 m/s is 36 km/h \\\
    cfxCommander.makeGroupGoThere(group, there, speed)\\\
    local attask = cfxCommander.createAttackGroupCommand(enemies)\\\
    cfxCommander.scheduleTaskForGroup(group, attask, 0.5)\\\
    troop.moving = true \\\
end\\\
\\\
-- make the troops engage a cfxZone passed in the destination \\\
-- attribute \\\
function cfxGroundTroops.makeTroopsEngageZone(troop)\\\
    local group = troop.group\\\
    if not group:isExist() then \\\
        trigger.action.outText(\\\"+++gndT: make troops engage zone: troops do not exist, exiting\\\", 30)\\\
        return \\\
    end\\\
    \\\
    local enemyZone = troop.destination -- must be cfxZone \\\
    local from = dcsCommon.getGroupLocation(group)\\\
    if not from then return end -- the group died\\\
    local there = enemyZone.point -- access zone position\\\
    if not there then return end\\\
        \\\
    local speed = 14 -- m/s; 10 m/s = 36 km/h\\\
    \\\
    -- make troops stop in 1 second, then start in 5 seconds to give AI respite \\\
    cfxCommander.makeGroupHalt(group, 1) -- 1 second delay\\\
    cfxCommander.makeGroupGoTherePreferringRoads(group, there, speed, 5)\\\
\\\
    -- remember that we have issued a move order \\\
    troop.moving = true     \\\
end\\\
\\\
function cfxGroundTroops.switchToOffroad(troops)\\\
    -- we may need to test if we already did this, \\\
    -- but not for now \\\
    \\\
    -- this is called when troops are stuck \\\
    -- on their route for longer than allowed\\\
    -- we now force a direct approach \\\
    local group = troops.group\\\
    if not group:isExist() then \\\
        return\\\
    end \\\
    \\\
    local enemies = troops.destination\\\
    local from = dcsCommon.getGroupLocation(group)\\\
    if not from then return end -- the commandos died\\\
    local there = enemies.point\\\
    if not there then return end\\\
        \\\
    local speed = 14 -- m/s; 10 m/s = 36 km/h\\\
    \\\
    cfxCommander.makeGroupHalt(group, 0) -- no delay, halt now\\\
    cfxCommander.makeGroupGoThere(group, there, speed, \\\"Off Road\\\", 5)\\\
    \\\
    troops.lastOrderDate = timer.getTime()\\\
    troops.speedWarning = 0\\\
end\\\
\\\
--\\\
-- update loop for troops that have 'attackOwnedZones' as \\\
-- their orders\\\
-- if they have no destination zone, or the zone they are \\\
-- are heading for is already owned by their side, then look for \\\
-- the closest enemy zone, and cut attack orders to move there \\\
function cfxGroundTroops.getClosestEnemyZone(troop)\\\
    local p = dcsCommon.getGroupLocation(troop.group)\\\
    local tempZone = cfxZones.createSimpleZone(\\\"tz\\\", p, 100)\\\
    tempZone.owner = troop.side\\\
    local newTarget = cfxOwnedZones.getNearestEnemyOwnedZone(tempZone, true) -- 'true' will also target neutral zones \\\
    return newTarget\\\
end\\\
\\\
function cfxGroundTroops.updateZoneAttackers(troop)\\\
    if not troop then return end \\\
    if not cfxOwnedZones then \\\
        trigger.action.outText(\\\"+++gndT: update zone attackers requires ownedZones\\\", 30)\\\
        return \\\
    end\\\
    troop.insideDestination = false -- mark as not inside \\\
    \\\
    local newTargetZone = cfxGroundTroops.getClosestEnemyZone(troop)\\\
    if not newTargetZone then\\\
        -- all target zones are friendly, go to guard mode\\\
        troop.orders = \\\"guard\\\"\\\
        return \\\
    end\\\
    \\\
    if newTargetZone ~= troop.destination then \\\
        if troop.destination and troop.orders == \\\"captureandhold\\\" then \\\
            troop.lastOrderDate = timer.getTime() -- we may even dismiss them \\\
            -- from troop array. But orders should remain when picked up by helo \\\
            -- we never change target. Stay.\\\
            return \\\
        end \\\
        troop.destination = newTargetZone \\\
        cfxGroundTroops.makeTroopsEngageZone(troop)\\\
        troop.lastOrderDate = timer.getTime()\\\
        troop.speedWarning = 0\\\
        return\\\
    end\\\
    \\\
    -- if we get here, we should be under way to our nearest enemy zone\\\
    if not troop.moving then \\\
        cfxGroundTroops.makeTroopsEngageZone(troop)\\\
        return \\\
    end \\\
    \\\
    -- if we get here, we are under way to troop.destination\\\
    -- check if we are inside the zone, and if so, set variable to true \\\
    local p = dcsCommon.getGroupLocation(troop.group)\\\
    troop.insideDestination = cfxZones.isPointInsideZone(p, troop.destination)\\\
    \\\
    -- if we get here, we need no change \\\
    \\\
end\\\
\\\
-- attackers simply travel to their destination (zone), and then switch to \\\
-- guard orders once they arrive \\\
function cfxGroundTroops.updateAttackers(troop) \\\
    if not troop then return end \\\
    if not troop.destination then return end \\\
    if not troop.group:isExist() then return end \\\
    \\\
    -- if we are not moving, we need to issue move oders now\\\
    -- this can happen if previously, there was a 'wait' command \\\
    -- and this now was removed so we end up in the method \\\
    if not troop.moving then \\\
        cfxGroundTroops.makeTroopsEngageZone(troop)\\\
        return \\\
    end \\\
    \\\
    \\\
    if cfxZones.isGroupPartiallyInZone(troop.group, troop.destination) then\\\
        -- we have arrived\\\
        -- we could now also initiate a general callback with reason\\\
        cfxGroundTroops.invokeCallbacksFor(\\\"arrived\\\", troop)\\\
        troop.orders = \\\"guard\\\"\\\
        return \\\
    end\\\
        \\\
--    if we get here, we need no change \\\
end\\\
\\\
-- update loop for a group that has \\\"guard\\\" orders.\\\
-- basically it stands around and looks for enemies \\\
-- until it finds a group, and then engages the enemy\\\
-- when engaged, it is not looking for other enemies\\\
-- 'engaged' means that the troop.enemy attribute is set\\\
 \\\
function cfxGroundTroops.updateGuards(troop)\\\
    if not troop.group:isExist() then \\\
        return \\\
    end \\\
    \\\
    local theEnemy = troop.enemy\\\
    if theEnemy then \\\
        -- see if enemy is dead \\\
        if not dcsCommon.isGroupAlive(theEnemy) then \\\
            troop.enemy = nil\\\
            -- yup, zed's dead. next time around, we won't be checking this again\\\
            trigger.action.outText(troop.name .. \\\" has neutralized enemy forces\\\", 30)\\\
            --DONE: invoke callback for defeating troops\\\
            local data = {}\\\
            data.enemy = theEnemy\\\
            cfxGroundTroops.invokeCallbacksFor(\\\"neutralized\\\", troop, data)\\\
            return\\\
        end\\\
        -- yes, we are still engaged\\\
        return \\\
    end\\\
    \\\
    -- we are currently unengaged. look for an enemy\\\
    if not troop.range then troop.range = 300 end\\\
    troop.coalition = troop.group:getCoalition()\\\
    local enemyCoal = dcsCommon.getEnemyCoalitionFor(troop.coalition)\\\
    local cat = Group.Category.GROUND\\\
    local p = dcsCommon.getGroupLocation(troop.group)\\\
    local enemies, enemyDist = dcsCommon.getClosestLivingGroupToPoint(p, enemyCoal, cat) \\\
    local maxRange = troop.range -- meters \\\
    -- if we have enemies then schedule a path to go there\\\
    if enemies and (enemyDist < maxRange) then \\\
        troop.enemy = enemies\\\
        --timer.scheduleFunction(cfxGroundTroops.makeGroupEngageEnemies, troop, timer.getTime() + 1.0)\\\
        cfxGroundTroops.makeTroopsEngageEnemies(troop)\\\
        trigger.action.outText(troop.name .. \\\" is engaging enemy forces at range \\\" .. math.floor(enemyDist) .. \\\"meters\\\", 30)\\\
        --DONE: invoke callback for engaging troops, pass data \\\
        local data = {}\\\
        data.enemy = enemies\\\
        cfxGroundTroops.invokeCallbacksFor(\\\"engaging\\\", troop, data)\\\
    elseif enemies then \\\
        --trigger.action.outText(troop.name .. \\\" enemiy out of range: \\\" .. math.floor(enemyDist) .. \\\"meters\\\", 30)\\\
    else \\\
        --trigger.action.outText(troop.name .. \\\" no enemies\\\", 30)\\\
    end\\\
end\\\
\\\
--\\\
-- update loop for units that laze targets.\\\
-- they can only laze if they are alive, but update \\\
-- will take care of that, so when we are here, there \\\
-- is at least one of them alive\\\
-- \\\
\\\
function cfxGroundTroops.findLazeTarget(troop)\\\
    local here = troop.group:getUnit(1):getPoint()\\\
    troop.coalition = troop.group:getCoalition()\\\
    local enemyCoal = dcsCommon.getEnemyCoalitionFor(troop.coalition)\\\
    --local enemySide = dcsCommon.getEnemyCoalitionFor(troop.side)\\\
    local cat = Group.Category.GROUND\\\
    local enemyGroups = dcsCommon.getLivingGroupsAndDistInRangeToPoint(here, troop.range, enemyCoal, cat) \\\
    -- we now have a list of possible targets in range\\\
    if #enemyGroups < 1 then     \\\
        -- no targets in range\\\
        return nil \\\
    end\\\
\\\
    here = {x = here.x, y = here.y + 2.0, z = here.z} -- raise by 2.0m\\\
    \\\
    -- iterate through the list until we find the first target \\\
    -- that fits the bill and return it\\\
\\\
    for i=1, #enemyGroups do    \\\
        -- get all units for this group \\\
        local aGroup = enemyGroups[i].group -- remember, they are in a {dist, group} tuple\\\
        local theUnits = aGroup:getUnits()\\\
        -- iterate all units \\\
        for udx, aUnit in pairs(theUnits) do \\\
            if (aUnit:isExist() and aUnit:getLife() > 1) then \\\
                -- unit lives \\\
                -- now, we need to filter infantry. we do this by \\\
                -- pre-fetching the typeString\\\
                -- and checking if the name contains some infantry-\\\
                -- typical strings. Idea taken from JTAC script \\\
                local isInfantry =  dcsCommon.unitIsInfantry(theUnit)\\\
                if not isInfantry then \\\
                    -- this is a vehicle, is it in line of sight?\\\
                    -- raise the point 2m above ground for both points\\\
                    -- as done in jtac script\\\
                    local there = aUnit:getPoint()  \\\
                    there = {x = there.x, y = there.y + 2.0, z = there.z}\\\
                   \\\
                    if land.isVisible(here, there) then \\\
                        -- we found a visible vehicle in \\\
                        -- the nearest group to us in range \\\
                        -- that is visible!\\\
                        return aUnit\\\
                    end -- if visible\\\
                end -- if infantry \\\
            end -- if alive \\\
        end -- for all units\\\
    end -- for all enemy groups\\\
    return nil -- no unit found \\\
end\\\
\\\
function cfxGroundTroops.lazerOff(troop)\\\
    if troop.lazerPointer then \\\
        troop.lazerPointer:destroy()\\\
    end\\\
    troop.lazerPointer = nil \\\
    troop.lazingUnit = nil \\\
end\\\
\\\
function cfxGroundTroops.trackLazer(troop)\\\
    -- the only thing that must be set when entering here is\\\
    -- lazeTarget. We set up the rest\\\
    if not troop.lazingUnit then \\\
        troop.lazingUnit = troop.group:getUnit(1) -- get first unit\\\
        if troop.lazingUnit:getLife() < 1 then \\\
            trigger.action.outText(\\\"+++ LazingUnit is dead, getUnit works differently from what docs say, need to filter for lively units\\\", 30)\\\
        end\\\
    end\\\
    \\\
    if not troop.lazerPointer then\\\
        local there = troop.lazeTarget:getPoint()\\\
        troop.lazerPointer = Spot.createLaser(troop.lazingUnit,{x = 0, y = 2, z = 0}, there, cfxGroundTroops.laseCode)\\\
        troop.lazeTargetType = troop.lazeTarget:getTypeName()\\\
        if cfxGroundTroops.jtacVerbose then \\\
            trigger.action.outTextForCoalition(troop.side, troop.name .. \\\" tally target - lasing \\\" .. troop.lazeTargetType .. \\\", code \\\" .. cfxGroundTroops.laseCode .. \\\"!\\\", 30)\\\
            trigger.action.outSoundForCoalition(troop.side, cfxGroundTroops.jtacSound) -- \\\"UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav\\\")\\\
        end \\\
        troop.lastLazerSpot = there -- remember last spot\\\
        local data = {}\\\
        data.enemy = troop.lazeTarget\\\
        data.tracker = troop.lazingUnit\\\
        cfxGroundTroops.invokeCallbacksFor(\\\"lase:tracking\\\", troop, data)\\\
        return\\\
    end\\\
        \\\
    -- if we get here, we update the lazerPointer\\\
    local there = troop.lazeTarget:getPoint()\\\
    -- we may only want to update the laser spot when dist > trigger\\\
    troop.lazerPointer:setPoint(there)\\\
    -- we may want to report dist\\\
    troop.lastLazerSpot = there\\\
end\\\
\\\
function cfxGroundTroops.updateLaze(troop)\\\
    -- check if we have a laze target. \\\
    -- check if lazing unit was killed, and therefore lost target\\\
    if troop.lazingUnit then \\\
        -- check that unit still alive\\\
        if troop.lazingUnit:isExist() and \\\
        troop.lazingUnit:getLife() >= 1 then\\\
        else \\\
            cfxGroundTroops.lazerOff(troop)\\\
            troop.lazeTarget = nil\\\
            if cfxGroundTroops.jtacVerbose then \\\
                trigger.action.outTextForCoalition(troop.side, troop.name .. \\\" reports lasing \\\" .. troop.lazeTargetType .. \\\" interrupted. Re-acquiring.\\\", 30)\\\
                trigger.action.outSoundForCoalition(troop.side, cfxGroundTroops.jtacSound) -- \\\"UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav\\\")\\\
            end \\\
            troop.lazingUnit = nil \\\
            cfxGroundTroops.invokeCallbacksFor(\\\"lase:stop\\\", troop)\\\
            return -- we'll re-acquire through a new unit next round\\\
        end\\\
    end\\\
    \\\
    -- if we get here, a lazing unit     \\\
    if troop.lazeTarget then \\\
        -- check if that target is alive and in range\\\
        if troop.lazeTarget:isExist() and troop.lazeTarget:getLife() >= 1 then\\\
            -- note: when we laze a target, we know that we have a lazing unit\\\
            local here = troop.lazingUnit:getPoint()\\\
            -- check if it has moved out of range \\\
            local there = troop.lazeTarget:getPoint()\\\
            if dcsCommon.dist(here, there) > troop.range then \\\
                -- troop out of range\\\
                if cfxGroundTroops.jtacVerbose then \\\
                    trigger.action.outTextForCoalition(troop.side, troop.name .. \\\" lost sight of lazed target \\\" .. troop.lazeTargetType, 30)\\\
                    trigger.action.outSoundForCoalition(troop.side, cfxGroundTroops.jtacSound) -- \\\"UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav\\\")\\\
                end \\\
                troop.lazeTarget = nil\\\
                cfxGroundTroops.lazerOff(troop)\\\
                troop.lazingUnit = nil\\\
                cfxGroundTroops.invokeCallbacksFor(\\\"lase:stop\\\", troop)\\\
                return \\\
            end\\\
            \\\
            -- if we get here, we need to update the target point \\\
            cfxGroundTroops.trackLazer(troop)\\\
            return\\\
        else\\\
            -- target died\\\
            if cfxGroundTroops.jtacVerbose then \\\
                trigger.action.outTextForCoalition(troop.side, troop.name .. \\\" confirms kill for \\\" .. troop.lazeTargetType, 30)\\\
                trigger.action.outSoundForCoalition(troop.side, cfxGroundTroops.jtacSound) -- \\\"UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav\\\")\\\
            end \\\
            troop.lazeTarget = nil\\\
            cfxGroundTroops.lazerOff(troop)\\\
            troop.lazingUnit = nil\\\
            cfxGroundTroops.invokeCallbacksFor(\\\"lase:stop\\\", troop)\\\
            return\\\
        end        \\\
    end\\\
    \\\
    -- if we get here, we must look for a laze target \\\
    troop.lazeTarget = cfxGroundTroops.findLazeTarget(troop)\\\
    if troop.lazeTarget then \\\
        cfxGroundTroops.trackLazer(troop) -- will also set up lazing unit \\\
    end\\\
end\\\
\\\
\\\
function cfxGroundTroops.updateWait(troop)\\\
    -- currently nothing to do    \\\
end\\\
\\\
function cfxGroundTroops.updateTroops(troop)\\\
    -- if orders start with \\\"wait-\\\" then the troops \\\
    -- simply do nothing\\\
    if dcsCommon.stringStartsWith(troop.orders, \\\"wait-\\\") then\\\
        -- the troops are waiting to be picked update\\\
        -- when they are dropped again, thre prefix to \\\
        -- their order is removed, and the 'real' orders \\\
        -- are revealed. For now, do nothing\\\
        cfxGroundTroops.updateWait(troop)\\\
    --REMEMBER: LOWER CASE ONLY!\\\
    elseif troop.orders == \\\"guard\\\" then \\\
        cfxGroundTroops.updateGuards(troop)\\\
    \\\
    elseif troop.orders == \\\"attackownedzone\\\" then \\\
        cfxGroundTroops.updateZoneAttackers(troop)\\\
\\\
    elseif troop.orders == \\\"captureandhold\\\" then \\\
        cfxGroundTroops.updateZoneAttackers(troop) \\\
        \\\
    elseif troop.orders == \\\"laze\\\" then \\\
        cfxGroundTroops.updateLaze(troop)\\\
    \\\
    elseif troop.orders == \\\"attackzone\\\" then \\\
        cfxGroundTroops.updateAttackers(troop)\\\
        \\\
    else \\\
        trigger.action.outText(\\\"+++ updated troops \\\" .. troop.name .. \\\" have unknown orders \\\" .. troop.orders, 30)\\\
    end\\\
    \\\
end\\\
\\\
--\\\
-- all at once \\\
--\\\
function cfxGroundTroops.update()\\\
    cfxGroundTroops.updateSchedule = timer.scheduleFunction(cfxGroundTroops.update, {}, timer.getTime() + 1/cfxGroundTroops.ups)\\\
    -- iterate all my troops and build next \\\
    -- versions pool\\\
    local liveTroops = {} -- filtered table, indexed by name \\\
    for idx, troop in pairs(cfxGroundTroops.deployedTroops) do \\\
        local group = troop.group \\\
        if not dcsCommon.isGroupAlive(group) then \\\
            -- group dead. remove from pool\\\
            -- this happens by not copying it into the poos\\\
            cfxGroundTroops.invokeCallbacksFor(\\\"dead\\\", troop) -- notify anyone who is interested that we are no longer proccing these \\\
        else \\\
            -- work with this groop according to its orders\\\
            cfxGroundTroops.updateTroops(troop)\\\
            -- since group is alive remember it for next loop\\\
            liveTroops[idx] = troop -- do NOT use insert as we have indexed table by name\\\
        end\\\
    end\\\
    -- liveTroops holds all troops that are still alive and will\\\
    -- be revisited next loop\\\
    cfxGroundTroops.deployedTroops = liveTroops\\\
end\\\
\\\
--\\\
-- UpdateQueued looks for the first unordered (.receivedOrders == false) group\\\
-- and processes them. if orders are 'laze', it will always be ordered \\\
--\\\
\\\
\\\
function cfxGroundTroops.updateQueued()\\\
    cfxGroundTroops.updateSchedule = timer.scheduleFunction(cfxGroundTroops.updateQueued, {}, timer.getTime() + 1/cfxGroundTroops.ups)\\\
    -- iterate all my troops and build next \\\
    -- versions pool\\\
    local liveTroops = {}\\\
    local hasOrdered = false -- so far, no orders have been given \\\
    for idx, troop in pairs(cfxGroundTroops.deployedTroops) do \\\
        local group = troop.group \\\
        if not dcsCommon.isGroupAlive(group) then \\\
            -- group dead. remove from pool\\\
            -- this happens by not copying it to liveTroops \\\
            -- trigger.action.outText(\\\"+++ removing ground troops \\\" .. troop.name, 30)\\\
            cfxGroundTroops.invokeCallbacksFor(\\\"dead\\\", troop) -- notify anyone who is interested that we are no longer proccing these \\\
        else \\\
            -- check if this is a lazer \\\
            if troop.orders == \\\"laze\\\" then \\\
                -- lazers are updated each turn \\\
                cfxGroundTroops.updateLaze(troop)\\\
            else \\\
                if not hasOrdered and not (troop.receivedOrders) then \\\
                -- work with this groop according to its orders\\\
                cfxGroundTroops.updateTroops(troop)\\\
                troop.receivedOrders = true -- this one has received orders \\\
                hasOrdered = true \\\
                end \\\
            end\\\
            liveTroops[idx] = troop -- do NOT use insert as we have indexed table\\\
        end\\\
    end\\\
    -- liveTroops holds all troops that are still alive and will\\\
    -- be revisited next loop\\\
    cfxGroundTroops.deployedTroops = liveTroops\\\
    \\\
    -- if no orders have been passed, clear all troop's .receivedOrders flag \\\
    -- and the loop starts anew next loop \\\
    if not hasOrdered then \\\
        for idx, troop in pairs(cfxGroundTroops.deployedTroops) do\\\
            troop.receivedOrders = nil  \\\
        end\\\
    end\\\
end\\\
\\\
--\\\
-- in updateCheckOnly we simply check the ground queue \\\
-- if there are troops added that need scheduling (i.e. have \\\
-- been passed in by addTroops and schedule them \\\
--\\\
function cfxGroundTroops.updateCheckOnly()\\\
    -- re-schedule myself in 1 second \\\
    timer.scheduleFunction(cfxGroundTroops.updateCheckOnly, {}, timer.getTime() + 1)\\\
    \\\
    -- iterate through all troops, and \\\
    -- see if there are any that have not been scheduled \\\
    -- to schedule them for updates in 1 second\\\
    -- that will be the first time that they are scheduled,\\\
    -- all others will be self-scheduled \\\
    for idx, troop in pairs(cfxGroundTroops.deployedTroops) do \\\
        if not troop.hasBeenScheduled then \\\
            local params = {troop}\\\
            troop.hasBeenScheduled = true \\\
            troop.updateID = timer.scheduleFunction(cfxGroundTroops.updateSingleScheduled, params, timer.getTime() + 1)\\\
            --trigger.action.outText(\\\"+++groundT: scheduling troops <\\\".. troop.group:getName() ..\\\"> with orders <\\\" .. troop.orders .. \\\">\\\", 30)\\\
        end\\\
    end\\\
    -- note that alive checks are now done during the scheduled\\\
    -- update, not every time for all\\\
\\\
end\\\
\\\
function cfxGroundTroops.updateSingleScheduled(params)\\\
    local troops = params[1]\\\
    troops.updateID = nil -- erase update id \\\
    if not troops then \\\
        trigger.action.outText(\\\"+++groundT WARNING: nil troop in updateSingle\\\", 30)\\\
        return -- no further action required, no longer updates\\\
    end\\\
    \\\
    local group = troops.group \\\
    -- see if we have been taken out of the pool or updated\\\
    -- if so, exit \\\
    \\\
    if not group:isExist() then \\\
        -- simply never again look at it. \\\
        return \\\
    end\\\
    \\\
    if cfxGroundTroops.deployedTroops[troops.group:getName()] ~= troops then \\\
        -- trigger.action.outText(\\\"+++groundT NOTE: troops <\\\".. troops.group:getName() ..\\\"> was removed from pool. Cancel Update\\\", 30)\\\
        return -- no further reschedule\\\
    end\\\
    \\\
    -- see if scheduling is turned off\\\
    if not troops.reschedule then \\\
        trigger.action.outText(\\\"+++groundT NOTE: no longer updating <\\\".. troops.group:getName() ..\\\"> per reschedule param\\\", 30)\\\
        return \\\
    end\\\
    \\\
    -- now, check if still alive \\\
    if not dcsCommon.isGroupAlive(group) then \\\
        -- group dead, no longer updates \\\
        cfxGroundTroops.invokeCallbacksFor(\\\"dead\\\", troops) -- notify anyone who is interested that we are no longer proccing these \\\
        cfxGroundTroops.removeTroopsFromPool(troops)\\\
        return -- nothing else to do\\\
    end\\\
    \\\
    -- now, execute the update itself, standard update \\\
    cfxGroundTroops.updateTroops(troops)\\\
    \\\
    -- check max speed of group. if < 0.1 then note and increase \\\
    -- speedWarning. if not, reset speed warning \\\
    if troops.orders == \\\"attackownedzone\\\" and dcsCommon.getGroupMaxSpeed(troops.group) < 0.1 then \\\
        if not troops.speedWarning then troops.speedWarning = 0 end\\\
        troops.speedWarning = troops.speedWarning + 1\\\
    else\\\
        troops.speedWarning = 0 -- reset\\\
    end\\\
    \\\
    if troops.speedWarning > 5 then -- make me 5\\\
        lastOrder = timer.getTime() - troops.lastOrderDate \\\
        -- this may be a matter of too many waypoints. \\\
        -- maybe issue orders to go to their destination directly?\\\
        -- now force an order to go directly.\\\
        if troops.speedWarning > 5 then \\\
            if troops.isOffroad then \\\
                -- we already switched to off-road. take me \\\
                -- out of the managed queue, I'm not going \\\
                -- anywhere\\\
                cfxGroundTroops.removeTroopsFromPool(troops)\\\
            else \\\
                cfxGroundTroops.switchToOffroad(troops)\\\
                troops.isOffroad = true -- so we know that we already did that\\\
            end\\\
        end \\\
    end\\\
    \\\
    -- now reschedule update for my best time \\\
    local updateTime = cfxGroundTroops.getScheduleInterval(troops.orders)\\\
    troops.updateID = timer.scheduleFunction(cfxGroundTroops.updateSingleScheduled, params, timer.getTime() + updateTime)\\\
end\\\
\\\
\\\
--\\\
-- PILEUP and TIE BRAKERS\\\
--\\\
-- there may come a situation where troops gather in \\\
-- one zone because the zone isn't won - some other troops \\\
-- are there and noone moves. \\\
-- a tie-break is required\\\
--\\\
\\\
-- checkpile up: every so often, we test if we have run into a \\\
-- pileup-situation. this happens if there are more than n \\\
-- units with group-attacker order in the same zone, and that \\\
-- zone is their destination \\\
-- this can be easily detected by the insideDestination flag \\\
-- checkPileUp should be run every minute or so \\\
 \\\
function cfxGroundTroops.checkPileUp()\\\
    -- schedule my next call \\\
    timer.scheduleFunction(cfxGroundTroops.checkPileUp, {}, timer.getTime() + 60)\\\
    local thePiles = {}\\\
    if not cfxOwnedZones then \\\
        return \\\
    end\\\
    \\\
    -- create a list of all piles \\\
--    for idx, oz in pairs(cfxOwnedZones.zones) do \\\
    for idx, oz in pairs(cfxOwnedZones.allManagedOwnedZones) do \\\
        local newPile = {}\\\
        newPile[1] = 0 -- no red inZone here \\\
        newPile[2] = 0 -- no blue inZone here \\\
        newPile.zone = oz -- the zone we are looking at \\\
        thePiles[oz] = newPile \\\
    end\\\
    \\\
    -- now iterate through all currently alive groups and \\\
    -- attribute them to their piles \\\
    for idx, troop in pairs(cfxGroundTroops.deployedTroops) do \\\
        -- get each group and count them if they are inside\\\
        -- their destination \\\
        if troop.insideDestination and troop.group:isExist() then\\\
            local side = troop.group:getCoalition()\\\
            local thePile = thePiles[troop.destination]\\\
            local theSide = troop.group:getCoalition()\\\
            thePile[theSide] = thePile[theSide] + 1 -- we count groups, not units  \\\
        end\\\
    end\\\
    \\\
    -- a pileup happens, if there are more than 3 groups in destination zone\\\
    -- with NO other troops present (usually the case)\\\
    -- or when there are 5 groups more than the number for the other side \\\
    -- so now scan all piles\\\
    for idx, thePile in pairs(thePiles) do \\\
        -- check red pileup \\\
        if thePile[1] > 3 and thePile[2] == 0 then \\\
            -- simple pileup. 3 groups, no others except defenders and \\\
            -- perhaps transients \\\
            cfxGroundTroops.breakTie(thePile, 1)\\\
        elseif thePile[1] >= thePile[2] + 5 then \\\
            -- numerical pileup \\\
            cfxGroundTroops.breakTie(thePile, 1)\\\
        end\\\
        \\\
        -- check blue loside \\\
        if thePile[2] >= 3 and thePile[1] == 0 then \\\
            -- simple pileup. 3 groups, no others except defenders and \\\
            -- perhaps transients \\\
            cfxGroundTroops.breakTie(thePile, 2)\\\
        elseif thePile[2] >= thePile[1] + 5 then \\\
            -- numerical pileup \\\
            cfxGroundTroops.breakTie(thePile, 2)\\\
        end\\\
    end\\\
end\\\
\\\
function cfxGroundTroops.breakTie(thePile, winner)\\\
    trigger.action.outText(\\\"+++ groundT: TIEBREAK - winner is \\\" .. winner .. \\\" in zone \\\" .. thePile.zone.name .. \\\": \\\" .. thePile[1] .. \\\":\\\" .. thePile[2] , 30)\\\
    -- now add some code to do the actual tie breaking: remove all units that \\\
    -- are inside the zone and who belong to the other side \\\
    local loser = 1 -- red default \\\
    local theZone = thePile.zone \\\
    if winner == 1 then loser = 2 end \\\
    -- now get all ground groups for the losing side\\\
    local losingGround = coalition.getGroups(loser, Group.Category.GROUND)\\\
    for idx, theGroup in pairs(losingGround) do \\\
        -- if alive, check if inside the zone \\\
        if theGroup:isExist() and dcsCommon.isGroupAlive(theGroup) then \\\
            -- make sure it's not a transient\\\
            if not isDeployedGroundTroop(theGroup) then \\\
                local p = dcsCommon.getGroupLocation(theGroup) \\\
                if cfxZones.isPointInsideZone(p, theZone) then \\\
                    trigger.action.outText(\\\"+++ groundT: TIEBREAK - destroying group \\\" .. theGroup:getName() , 30)\\\
                    -- we delete this group now\\\
                    theGroup:destroy()\\\
                end\\\
            end\\\
        end\\\
    end\\\
end\\\
\\\
--\\\
-- sanity checks for rescheduling\\\
--\\\
function cfxGroundTroops.checkSchedules()\\\
    timer.scheduleFunction(cfxGroundTroops.checkSchedules, {}, timer.getTime() + 10)\\\
    for idx, troop in pairs(cfxGroundTroops.deployedTroops) do\\\
        -- check if troop is not scheduled \\\
        -- if this happens to a group more than a certain times,\\\
        -- it has somehow dropped out of the reschedule \\\
        -- plan and needs to be scheduled \\\
        if troop.updateID == nil then \\\
            troop.unscheduleCount = troop.unscheduleCount + 1\\\
            if (troop.unscheduleCount > 1) and troop.group:isExist() then \\\
                trigger.action.outText(\\\"+++ groundT: unscheduled group  \\\" .. troop.group:getName() .. \\\" cnt=\\\" .. troop.unscheduleCount , 30)\\\
            end \\\
        end\\\
    end\\\
end\\\
\\\
--\\\
-- REPORTING \\\
--\\\
-- \\\
-- get a report of troops as string \\\
-- \\\
function cfxGroundTroops.getTroopReport(theSide, ignoreInfantry)\\\
    if not ignoreInfantry then ignoreInfantry = false end \\\
    local report = \\\"GROUND FORCES REPORT\\\"\\\
    for idx, troop in pairs(cfxGroundTroops.deployedTroops) do \\\
        if troop.side == theSide and troop.group:isExist() then \\\
            local unitNum = troop.group:getSize()\\\
            report = report .. \\\"\\\\n\\\" .. troop.name .. \\\" (\\\".. unitNum ..\\\"): <\\\" .. troop.orders .. \\\">\\\" \\\
            if troop.orders == \\\"attackownedzone\\\" then \\\
                if troop.destination then     \\\
                    report = report .. \\\" move towards \\\" .. troop.destination.name \\\
                else \\\
                    report = report .. \\\" (selecting destination)\\\"\\\
                end\\\
            end\\\
        end\\\
    end\\\
    report = report .. \\\"\\\\n---END REPORT\\\\n\\\"\\\
    return report \\\
end\\\
\\\
--\\\
-- createGroundTroop\\\
-- use this to create a cfxGroundTroops from a dcs group\\\
--\\\
function cfxGroundTroops.createGroundTroops(inGroup, range, orders) \\\
    local newTroops = {}\\\
    if not orders then \\\
        orders = \\\"guard\\\" \\\
    end\\\
    if orders:lower() == \\\"lase\\\" then \\\
        orders = \\\"laze\\\" -- we use WRONG spelling here, cause we're cool. yeah, right.\\\
    end\\\
    newTroops.insideDestination = false\\\
    newTroops.unscheduleCount = 0 -- will count up as we aren't scheduled\\\
    newTroops.speedWarning = 0\\\
    newTroops.isOffroad = false -- if true, we switched to direct orders, not roads, after standstill\\\
    newTroops.group = inGroup\\\
    newTroops.orders = orders:lower()\\\
    newTroops.coalition = inGroup:getCoalition()\\\
    newTroops.side = newTroops.coalition -- because we'e been using both.\\\
    newTroops.name = inGroup:getName()\\\
    newTroops.moving = false -- set to not have received move orders yet \\\
    newTroops.signature = \\\"cfx\\\" -- to verify this is groundTroop group, not dcs groups\\\
    if not range then range = 300 end\\\
    newTroops.range = range\\\
    return newTroops\\\
end\\\
\\\
function cfxGroundTroops.addGroundTroopsToPool(troops) -- troops MUST be a table that I understand, with \\\
    if not troops then return end\\\
    if troops.signature ~= \\\"cfx\\\" then \\\
        trigger.action.outText(\\\"+++ adding ground troops with unsupported troop signature\\\", 30)\\\
        return \\\
    end\\\
    if not troops.orders then troops.orders = \\\"guard\\\" end \\\
    troops.orders = troops.orders:lower()\\\
    troops.reschedule = true -- in case we use scheduled update \\\
    -- we now add to internal array. this is worked on by all \\\
    -- update meths, on scheduled upadtes, it is only used to \\\
    -- pick up, and do the initial schedule, after that they \\\
    -- all re-schedule themselves \\\
    troops.hasBeenScheduled = false -- so far, no updates \\\
    -- hasBeenScheduled is used by updateCheckOnly when scheduled \\\
    -- updates are used. \\\
    \\\
    -- now add to actively managed table or queue it if enabled\\\
    if cfxGroundTroops.maxManagedTroops > 0 and dcsCommon.getSizeOfTable(cfxGroundTroops.deployedTroops) >= cfxGroundTroops.maxManagedTroops then \\\
        -- we need to queue \\\
        table.insert(cfxGroundTroops.troopQueue, troops)\\\
    else\\\
        -- add to deployed set\\\
        cfxGroundTroops.deployedTroops[troops.group:getName()] = troops\\\
    end\\\
end\\\
\\\
function cfxGroundTroops.removeTroopsFromPool(troops)\\\
    \\\
    if not troops then return end \\\
    if troops.signature ~= \\\"cfx\\\" then return end\\\
\\\
    if not troops.group:isExist() then \\\
        trigger.action.outText(\\\"warning: removeFromPool called with inexistant group\\\", 30)\\\
        return \\\
    end\\\
    \\\
    if cfxGroundTroops.deployedTroops[troops.group:getName()] then \\\
        local troop = cfxGroundTroops.deployedTroops[troops.group:getName()]\\\
        troops.reschedule = false -- so a reschedule wont update any more\\\
        cfxGroundTroops.deployedTroops[troops.group:getName()] = nil\\\
        return \\\
    end\\\
    \\\
    -- if we get here, we need to check if perhaps the troops \\\
    -- are in the queue\\\
    for i=1, #cfxGroundTroops.troopQueue do \\\
        if cfxGroundTroops.troopQueue[i] == troops then \\\
            table.remove(cfxGroundTroops.troopQueue, i)\\\
            return\\\
        end\\\
    end\\\
end\\\
\\\
function isDeployedGroundTroop(aGroup) \\\
    if not aGroup then return false end \\\
    -- see if its already managed\\\
    if cfxGroundTroops.deployedTroops[aGroup:getName()] ~= nil then \\\
        return true \\\
    end \\\
    \\\
    -- see if it's in the queue \\\
    for i=1, #cfxGroundTroops.troopQueue do \\\
        if cfxGroundTroops.troopQueue[i] == troops then \\\
            return true\\\
        end\\\
    end\\\
    -- if we get here, it's neither managed nor queued\\\
    return false \\\
--    return cfxGroundTroops.deployedTroops[aGroup:getName()] ~= nil \\\
end\\\
\\\
function cfxGroundTroops.getGroundTroopsForGroup(aGroup) \\\
    if not (cfxGroundTroops.deployedTroops[aGroup:getName()]) then\\\
        -- see if it's queued \\\
        for i=1, #cfxGroundTroops.troopQueue do \\\
            local troops = cfxGroundTroops.troopQueue[i]\\\
            if troops.group == aGroup then \\\
                return troops\\\
            end\\\
        end\\\
        \\\
        if cfxGroundTroops.verbose then \\\
            trigger.action.outText(\\\"+++gndT - WARNING: cannot find group \\\" .. aGroup:getName() .. \\\" for troop retrieval. Known troops are:\\\", 30)\\\
        end \\\
        for k,v in pairs(cfxGroundTroops.deployedTroops) do \\\
            trigger.action.outText(\\\"+++ \\\".. k .. \\\": has v: \\\" .. v.name, 30)\\\
        end\\\
        return nil\\\
    end\\\
    \\\
    return cfxGroundTroops.deployedTroops[aGroup:getName()]\\\
end\\\
\\\
function cfxGroundTroops.monitorQueues()\\\
    timer.scheduleFunction(cfxGroundTroops.monitorQueues, {}, timer.getTime() + 5)\\\
    \\\
    -- calculate the numbers \\\
    local num = dcsCommon.getSizeOfTable(cfxGroundTroops.deployedTroops)\\\
    \\\
    local msg = \\\"+++ gndT - Groups Managed: <\\\" .. num .. \\\">\\\"\\\
    -- display the numbers\\\
    if cfxGroundTroops.maxManagedTroops > 0 then \\\
        msg = msg .. \\\" capped at \\\" .. cfxGroundTroops.maxManagedTroops .. \\\", q size is <\\\" .. #cfxGroundTroops.troopQueue .. \\\">\\\"\\\
    end\\\
    trigger.action.outText(msg, 30)\\\
end\\\
\\\
\\\
-- manageQueue: if depth of deployedTroops is below max and we have \\\
-- items in queue, pop off first one and put in managed table \\\
-- checked once every 2 seconds \\\
function cfxGroundTroops.manageQueues() \\\
    timer.scheduleFunction(cfxGroundTroops.manageQueues, {}, timer.getTime() + 2)\\\
    if cfxGroundTroops.maxManagedTroops < 1 then return end\\\
    \\\
    -- if we get here, we have a limit on managed \\\
    -- items \\\
    if #cfxGroundTroops.troopQueue < 1 then return end \\\
    \\\
    -- if we here, there are items waiting in the queue\\\
    while dcsCommon.getSizeOfTable(cfxGroundTroops.deployedTroops) < cfxGroundTroops.maxManagedTroops and #cfxGroundTroops.troopQueue > 0 do \\\
        -- transfer items from the front to the managed queue \\\
        local theTroops = cfxGroundTroops.troopQueue[1]\\\
        table.remove(cfxGroundTroops.troopQueue, 1)\\\
        if theTroops.group:isExist() then \\\
            cfxGroundTroops.deployedTroops[theTroops.group:getName()] = theTroops\\\
        end\\\
    end\\\
end\\\
\\\
\\\
function cfxGroundTroops.start()\\\
    if not dcsCommon.libCheck(\\\"cfx Ground Troops\\\",\\\
                              cfxGroundTroops.requiredLibs)\\\
    then \\\
        trigger.action.outText(\\\"cf/x Ground Troops aborted: missing libraries\\\", 30)\\\
        return false \\\
    end\\\
    \\\
    -- read optional config zone \\\
    cfxGroundTroops.readConfigZone()\\\
    \\\
    if cfxGroundTroops.scheduledUpdates then \\\
        cfxGroundTroops.queuedUpdates = false \\\
        cfxGroundTroops.updateCheckOnly()\\\
        cfxGroundTroops.checkSchedules() -- check regularly if all troops have been updated by checking their ID\\\
    elseif cfxGroundTroops.queuedUpdates then \\\
        cfxGroundTroops.updateQueued()\\\
    else     \\\
        cfxGroundTroops.update()\\\
    end \\\
    -- now install a regular pileup check \\\
    timer.scheduleFunction(cfxGroundTroops.checkPileUp, {}, timer.getTime() + 60) \\\
    \\\
    if cfxGroundTroops.monitorNumbers then \\\
        timer.scheduleFunction(cfxGroundTroops.monitorQueues, {}, timer.getTime() + 5) \\\
    end\\\
    \\\
    if cfxGroundTroops.maxManagedTroops > 0 then\\\
        timer.scheduleFunction(cfxGroundTroops.manageQueues, {}, timer.getTime() + 1) \\\
    end \\\
    \\\
    trigger.action.outText(\\\"cf/x Ground Troops v\\\" .. cfxGroundTroops.version .. \\\" started\\\", 30)\\\
    \\\
    if not cfxOwnedZones then \\\
        --trigger.action.outText(\\\"+++groundT: pileUp - owned zones not yet ready\\\", 30)\\\
    end\\\
    return true \\\
end\\\
\\\
if not cfxGroundTroops.start() then \\\
    cfxGroundTroops = nil \\\
    trigger.action.outText(\\\"cfxGroundTroops aborted load\\\", 30)\\\
end\\\
\\\
--[[--\\\
 TO DO \\\
 \\\
 - implement 'patrol' orders!!! \\\
   \\\
   when ordering a new route, issue a command to stop in 1 second\\\
 and another with new marching orders in 5 seconds \\\
 look at setTask() and resetTask() for controller\\\
 - change group logic to set itself up to 'requestOrders' with group as parameter, so they can decide themselves how quickly they want to be re-tasked\\\
 \\\
 - DONE enqueue and dequeue methods with capped ground troops size \\\
 - named locs have strategic values attached (default = 1), and distance is divided by strat value to get at priority when rerouting \\\
 \\\
 - difficulty increase: make enemy troops better by raining their spawned level \\\
 \\\
 - check out simple slot block SSB (pre-moose) to see if we can implement slot blocking for downed pilots \\\
 \\\
 - new 'wanda' (wander) module to make airports more lively: zone, have individuals/single vehicle wander around. two waypoints (start and stop), that are zones, and whenever they reach one or are at speed 0, they get a new one. may have pause before they go to next. \\\
 variant on above: selection of zones that are somehow connected, and destinations are made between these for patrolling zone. can force order, loop, and ping-pong. \\\
--]]--\");a_do_script(\"cfxSpawnZones = {}\\\
cfxSpawnZones.version = \\\"2.0.1\\\"\\\
cfxSpawnZones.requiredLibs = {\\\
    \\\"dcsCommon\\\", -- common is of course needed for everything\\\
                 -- pretty stupid to check for this since we \\\
                 -- need common to invoke the check, but anyway\\\
    \\\"cfxZones\\\", -- Zones, of course \\\
    \\\"cfxCommander\\\", -- to make troops do stuff\\\
    \\\"cfxGroundTroops\\\", -- for ordering then around \\\
}\\\
cfxSpawnZones.ups = 1\\\
cfxSpawnZones.verbose = false \\\
\\\
-- persistence: all groups we ever spawned. \\\
-- is regularly GC'd\\\
\\\
cfxSpawnZones.spawnedGroups = {}\\\
\\\
--\\\
-- Zones that conform with this requirements spawn toops automatically\\\
--   *** DOES NOT EXTEND ZONES *** LINKED OWNER via masterOwner ***\\\
-- \\\
--[[--\\\
-- version history \\\
   2.0.0 - dmlZones\\\
         - moved \\\"types\\\" to spawner \\\
         - baseName defaults to zone name, as it is safe for naming\\\
         - spawnWithSpawner direct link in spawner to spawnZones\\\
   2.0.1 - fix in verifySpawnOwnership() when not master zone found \\\
  --]]--\\\
  \\\
cfxSpawnZones.allSpawners = {}\\\
cfxSpawnZones.callbacks = {} -- signature: cb(reason, group, spawner)\\\
 \\\
--\\\
-- C A L L B A C K S \\\
-- \\\
function cfxSpawnZones.addCallback(theCallback)\\\
    table.insert(cfxSpawnZones.callbacks, theCallback)\\\
end\\\
\\\
function cfxSpawnZones.invokeCallbacksFor(reason, theGroup, theSpawner)\\\
    for idx, theCB in pairs (cfxSpawnZones.callbacks) do \\\
        theCB(reason, theGroup, theSpawner)\\\
    end\\\
end\\\
\\\
\\\
--\\\
-- creating a spawner\\\
--\\\
function cfxSpawnZones.createSpawner(inZone)\\\
    local theSpawner = {}\\\
    theSpawner.zone = inZone\\\
    theSpawner.name = inZone.name \\\
    theSpawner.spawnWithSpawner = cfxSpawnZones.spawnWithSpawner\\\
    -- interface to groupTracker \\\
    -- WARNING: attaches to ZONE, not spawner object\\\
    if inZone:hasProperty(\\\"trackWith:\\\") then \\\
        inZone.trackWith = inZone:getStringFromZoneProperty(\\\"trackWith:\\\", \\\"<None>\\\")\\\
    end\\\
    \\\
    -- interface to delicates \\\
    if inZone:hasProperty(\\\"useDelicates\\\") then \\\
        theSpawner.delicateName = dcsCommon.trim(inZone:getStringFromZoneProperty(\\\"useDelicates\\\", \\\"<none>\\\"))\\\
        if theSpawner.delicateName == \\\"*\\\" then theSpawner.delicateName = inZone.name end \\\
    end\\\
    \\\
    -- connect with ME if a trigger flag is given \\\
    if inZone:hasProperty(\\\"f?\\\") then \\\
        theSpawner.triggerFlag = inZone:getStringFromZoneProperty(\\\"f?\\\", \\\"none\\\")\\\
        theSpawner.lastTriggerValue = trigger.misc.getUserFlag(theSpawner.triggerFlag)\\\
    elseif inZone:hasProperty(\\\"spawn?\\\") then \\\
        theSpawner.triggerFlag = inZone:getStringFromZoneProperty(\\\"spawn?\\\", \\\"none\\\")\\\
        theSpawner.lastTriggerValue = trigger.misc.getUserFlag(theSpawner.triggerFlag)\\\
    elseif inZone:hasProperty(\\\"spawnUnits?\\\") then \\\
        theSpawner.triggerFlag = inZone:getStringFromZoneProperty( \\\"spawnObject?\\\", \\\"none\\\")\\\
        theSpawner.lastTriggerValue = trigger.misc.getUserFlag(theSpawner.triggerFlag)\\\
    end\\\
    \\\
    if inZone:hasProperty(\\\"activate?\\\") then \\\
        theSpawner.activateFlag = inZone:getStringFromZoneProperty( \\\"activate?\\\", \\\"none\\\")\\\
        theSpawner.lastActivateValue = trigger.misc.getUserFlag(theSpawner.activateFlag)\\\
    end\\\
    \\\
    if inZone:hasProperty(\\\"pause?\\\") then \\\
        theSpawner.pauseFlag = inZone:getStringFromZoneProperty(\\\"pause?\\\", \\\"none\\\")\\\
        theSpawner.lastPauseValue = trigger.misc.getUserFlag(theSpawner.pauseFlag)\\\
    end\\\
    \\\
    if inZone:hasProperty(\\\"types\\\") then \\\
        theSpawner.types = inZone:getStringFromZoneProperty(\\\"types\\\", \\\"Soldier M4\\\")\\\
    else \\\
        theSpawner.types = inZone:getStringFromZoneProperty(\\\"spawner\\\", \\\"Soldier M4\\\")\\\
    end\\\
    -- synthesize types * typeMult\\\
    if inZone:hasProperty(\\\"typeMult\\\") then \\\
        local n = inZone:getNumberFromZoneProperty(\\\"typeMult\\\", 1)\\\
        local repeater = \\\"\\\"\\\
        if n < 1 then n = 1 end \\\
        while n > 1 do \\\
            repeater = repeater .. \\\",\\\" .. theSpawner.types\\\
            n = n - 1\\\
        end\\\
        theSpawner.types = theSpawner.types .. repeater \\\
    end\\\
    \\\
    theSpawner.country = inZone:getNumberFromZoneProperty(\\\"country\\\", 0)\\\
    if inZone:hasProperty(\\\"masterOwner\\\") then \\\
        theSpawner.masterZoneName = inZone:getStringFromZoneProperty(\\\"masterOwner\\\", \\\"\\\")\\\
        if theSpawner.masterZoneName == \\\"\\\" then theSpawner.masterZoneName = nil end \\\
    end \\\
    \\\
    theSpawner.rawOwner = coalition.getCountryCoalition(theSpawner.country)\\\
    -- theSpawner.baseName = inZone:getStringFromZoneProperty(\\\"baseName\\\", dcsCommon.uuid(\\\"SpwnDflt\\\"))\\\
    theSpawner.baseName = inZone:getStringFromZoneProperty(\\\"baseName\\\", \\\"*\\\")\\\
    theSpawner.baseName = dcsCommon.trim(theSpawner.baseName)\\\
    if theSpawner.baseName == \\\"*\\\" then \\\
        theSpawner.baseName = inZone.name -- convenience shortcut\\\
    end\\\
    \\\
    theSpawner.cooldown = inZone:getNumberFromZoneProperty(\\\"cooldown\\\", 60)\\\
    theSpawner.autoRemove = inZone:getBoolFromZoneProperty(\\\"autoRemove\\\", false)\\\
    theSpawner.lastSpawnTimeStamp = -10000 -- init so it will always work\\\
    theSpawner.heading = inZone:getNumberFromZoneProperty(\\\"heading\\\", 0)    \\\
    theSpawner.cdTimer = 0 -- used for cooldown. if timer.getTime < this value, don't spawn\\\
    theSpawner.cdStarted = false -- used to initiate cooldown when theSpawn disappears\\\
    theSpawner.count = 1 -- used to create names, and count how many groups created\\\
    theSpawner.theSpawn = nil -- link to last spawned group\\\
    theSpawner.formation = inZone:getStringFromZoneProperty(\\\"formation\\\", \\\"circle_out\\\")\\\
    theSpawner.paused = inZone:getBoolFromZoneProperty(\\\"paused\\\", false)\\\
    -- orders are always converted to all lower case \\\
    theSpawner.orders = inZone:getStringFromZoneProperty(\\\"orders\\\", \\\"guard\\\"):lower() \\\
    -- used to assign orders, default is 'guard', use \\\"laze\\\" to make them laze targets. can be 'wait-' which may auto-convert to 'guard' after pick-up by helo, to be handled outside.\\\
    -- use \\\"train\\\" to tell them to HOLD WEAPONS, don't move and don't participate in loop, so we have in effect target dummies\\\
    -- can also use order 'dummy' or 'dummies' to switch to train\\\
    if theSpawner.orders:lower() == \\\"dummy\\\" or theSpawner.orders:lower() == \\\"dummies\\\" then theSpawner.orders = \\\"train\\\" end \\\
    if theSpawner.orders:lower() == \\\"training\\\" then theSpawner.orders = \\\"train\\\" end \\\
    \\\
    theSpawner.range = inZone:getNumberFromZoneProperty(\\\"range\\\", 300) -- if we have a range, for example enemy detection for Lasing or engage range\\\
    theSpawner.maxSpawns = inZone:getNumberFromZoneProperty(\\\"maxSpawns\\\", -1) -- if there is a limit on how many troops can spawn. -1 = endless spawns\\\
    theSpawner.requestable = inZone:getBoolFromZoneProperty( \\\"requestable\\\", false)\\\
    if theSpawner.requestable then \\\
        theSpawner.paused = true \\\
        if inZone.verbose or cfxSpawnZones.verbose then \\\
            trigger.action.outText(\\\"+++spwn: spawner <\\\" .. inZone.name .. \\\"> paused: requestable enabled\\\", 30)\\\
        end\\\
    end\\\
    if inZone:hasProperty(\\\"target\\\") then \\\
        theSpawner.target = inZone:getStringFromZoneProperty(\\\"target\\\", \\\"\\\")\\\
        if theSpawner.target == \\\"\\\" then -- this is the defaut case \\\
            theSpawner.target = nil \\\
        end\\\
    end \\\
    \\\
    if cfxSpawnZones.verbose or inZone.verbose then \\\
        trigger.action.outText(\\\"+++spwn: created spawner for <\\\" .. inZone.name .. \\\">\\\", 30)\\\
    end    \\\
    \\\
    return theSpawner\\\
end\\\
\\\
function cfxSpawnZones.addSpawner(aSpawner)\\\
    cfxSpawnZones.allSpawners[aSpawner.zone] = aSpawner\\\
end\\\
\\\
function cfxSpawnZones.removeSpawner(aSpawner)\\\
    cfxSpawnZones.allSpawners[aSpawner.zone] = nil\\\
end\\\
\\\
function cfxSpawnZones.getSpawnerForZone(aZone)\\\
    return cfxSpawnZones.allSpawners[aZone]\\\
end\\\
\\\
function cfxSpawnZones.getSpawnerForZoneNamed(aName)\\\
    local aZone = cfxZones.getZoneByName(aName) \\\
    if not aZone then return nil end \\\
    return cfxSpawnZones.getSpawnerForZone(aZone)\\\
end\\\
\\\
\\\
function cfxSpawnZones.getRequestableSpawnersInRange(aPoint, aRange, aSide)\\\
    -- trigger.action.outText(\\\"enter requestable spawners for side \\\" .. aSide , 30)\\\
    if not aSide then aSide = 0 end  \\\
    if not aRange then aRange = 200 end \\\
    if not aPoint then return {} end \\\
\\\
    local theSpawners = {}\\\
    for aZone, aSpawner in pairs(cfxSpawnZones.allSpawners) do \\\
        -- iterate all zones and collect those that match \\\
        local hasMatch = true \\\
        local delta = dcsCommon.distFlat(aPoint, cfxZones.getPoint(aZone))\\\
        if delta>aRange then hasMatch = false end \\\
        if aSide ~= 0 then \\\
            -- check if side is correct for owned zone \\\
            if not cfxSpawnZones.verifySpawnOwnership(aSpawner) then \\\
                -- failed ownership test. owner of master \\\
                -- is not my own zone \\\
                hasMatch = false \\\
            end\\\
        end\\\
        \\\
        if aSide ~= aSpawner.rawOwner then \\\
            -- only return spawners with this side\\\
            -- note: this will NOT work with neutral players \\\
            hasMatch = false \\\
        end\\\
        \\\
        if not aSpawner.requestable then \\\
            hasMatch = false \\\
        end\\\
        \\\
        if hasMatch then \\\
            table.insert(theSpawners, aSpawner)\\\
        end\\\
    end\\\
    \\\
    return theSpawners\\\
end\\\
--\\\
-- spawn troops \\\
-- \\\
function cfxSpawnZones.verifySpawnOwnership(spawner)\\\
    -- returns false ONLY if masterSpawn disagrees\\\
    if not spawner.masterZoneName then \\\
        --trigger.action.outText(\\\"spawner \\\" .. spawner.name .. \\\" no master, go!\\\", 30)\\\
        return true \\\
    end -- no master owner, all ok\\\
    local myCoalition = spawner.rawOwner\\\
    local masterZone = cfxZones.getZoneByName(spawner.masterZoneName)\\\
    if not masterZone then \\\
        trigger.action.outText(\\\"spawner \\\" .. spawner.name .. \\\" DID NOT FIND MASTER ZONE <\\\" .. spawner.masterZoneName .. \\\">\\\", 30)\\\
        return false \\\
    end\\\
    \\\
    if not masterZone.owner then \\\
        --trigger.action.outText(\\\"spawner \\\" .. spawner.name .. \\\" - masterZone \\\" .. masterZone.name .. \\\" HAS NO OWNER????\\\", 30)\\\
        return true \\\
    end\\\
    \\\
    if (myCoalition ~= masterZone.owner) then \\\
        -- can't spawn, surrounding area owned by enemy\\\
        return false \\\
    end\\\
\\\
    return true\\\
end\\\
\\\
function cfxSpawnZones.spawnWithSpawner(aSpawner)\\\
    if type(aSpawner) == \\\"string\\\" then -- return spawner for zone of that name\\\
        aSpawner = cfxSpawnZones.getSpawnerForZoneNamed(aName)\\\
    end\\\
    if not aSpawner then return end \\\
    local theZone = aSpawner.zone -- retrieve the zone that defined me \\\
    \\\
    if cfxSpawnZones.verbose or theZone.verbose then \\\
        trigger.action.outText(\\\"+++spwn: started spawn with spawner for <\\\" .. theZone.name .. \\\">\\\", 30)\\\
    end\\\
    \\\
    -- will NOT check if conditions are met. This forces a spawn\\\
    local unitTypes = {} -- build type names\\\
    --local p = aSpawner.zone.point  \\\
    local p = cfxZones.getPoint(theZone) -- aSpawner.zone.point\\\
        \\\
    -- split the conf.troopsOnBoardTypes into an array of types\\\
    unitTypes = dcsCommon.splitString(aSpawner.types, \\\",\\\")\\\
    if #unitTypes < 1 then \\\
        table.insert(unitTypes, \\\"Soldier M4\\\") -- make it one m4 trooper as fallback\\\
    end\\\
    \\\
    local theCountry = aSpawner.country  \\\
    local theCoalition = coalition.getCountryCoalition(theCountry)\\\
    \\\
    local theGroup, theData = cfxZones.createGroundUnitsInZoneForCoalition (\\\
                theCoalition, \\\
                aSpawner.baseName .. \\\"-\\\" .. aSpawner.count, -- must be unique \\\
                aSpawner.zone,                                             \\\
                unitTypes, \\\
                aSpawner.formation,\\\
                aSpawner.heading)\\\
    if cfxSpawnZones.verbose or theZone.verbose then \\\
        -- check created group size versus requested size \\\
        trigger.action.outText(\\\"+++spwn: created <\\\" .. theGroup:getSize() .. \\\"> units, requested <\\\" .. #unitTypes .. \\\"> units, formation <\\\" .. aSpawner.formation .. \\\">\\\", 30)\\\
        trigger.action.outText(\\\"+++spwn: zone <\\\" .. theZone.name .. \\\">center at <\\\" .. dcsCommon.point2text(p) .. \\\">\\\", 30)\\\
        local allUnits = theGroup:getUnits()\\\
        for idx, myUnit in pairs (allUnits) do \\\
            local pos = myUnit:getPoint()\\\
            trigger.action.outText(\\\"unit <\\\" .. myUnit:getName() .. \\\"> at \\\" .. dcsCommon.point2text(pos), 30)\\\
        end\\\
    end\\\
    \\\
    aSpawner.theSpawn = theGroup\\\
    aSpawner.count = aSpawner.count + 1 \\\
\\\
    -- insert into collector for persistence\\\
    local troopData = {}\\\
    troopData.groupData = theData\\\
    troopData.orders = aSpawner.orders -- always set \\\
    troopData.side = theCoalition\\\
    troopData.target = aSpawner.target -- can be nil!\\\
    troopData.tracker = theZone.trackWith -- taken from ZONE!!, can be nil\\\
    troopData.range = aSpawner.range\\\
    cfxSpawnZones.spawnedGroups[theData.name] = troopData \\\
    \\\
    -- remember: orders are always lower case only \\\
    if aSpawner.orders and (\\\
       aSpawner.orders:lower() == \\\"training\\\" or \\\
       aSpawner.orders:lower() == \\\"train\\\" )\\\
    then \\\
        -- make them ROE \\\"HOLD\\\"\\\
        -- remember to do this in persistence as well!\\\
        -- they aren't fed to cfxGroundTroops.\\\
        -- we should update groundTroops to simply \\\
        -- drop those with 'train' or 'training'\\\
        cfxCommander.scheduleOptionForGroup(\\\
            theGroup, \\\
            AI.Option.Ground.id.ROE, \\\
            AI.Option.Ground.val.ROE.WEAPON_HOLD, \\\
            1.0)\\\
    else \\\
        local newTroops = cfxGroundTroops.createGroundTroops(theGroup, aSpawner.range, aSpawner.orders) \\\
        cfxGroundTroops.addGroundTroopsToPool(newTroops)\\\
        \\\
        -- see if we have defined a target zone as destination\\\
        -- and set it accordingly \\\
        if aSpawner.target then \\\
            local destZone = cfxZones.getZoneByName(aSpawner.target)\\\
            if destZone then\\\
                newTroops.destination = destZone \\\
            else \\\
                trigger.action.outText(\\\"+++ spawner \\\" .. aSpawner.name .. \\\" has illegal (unknown) target zone <\\\" .. aSpawner.target .. \\\">. Pausing.\\\", 30)\\\
                aSpawner.paused = true \\\
            end\\\
        elseif aSpawner.orders == \\\"attackzone\\\" then \\\
            -- attackZone command but no zone given\\\
            trigger.action.outText(\\\"+++ spawner \\\" .. aSpawner.name .. \\\" has no target but attackZone command. Pausing.\\\", 30)\\\
                aSpawner.paused = true         \\\
        end \\\
        \\\
    end\\\
    \\\
    -- hand off to delicates \\\
    if aSpawner.delicateName and delicates then \\\
    -- pass this object to the delicate zone mentioned \\\
        local theDeli = delicates.getDelicatesByName(aSpawner.delicateName)\\\
        if theDeli then \\\
            delicates.addGroupToInventoryForZone(theDeli, newTroops)\\\
        else \\\
            trigger.action.outText(\\\"+++Spwn: spawner <\\\" .. aZone.name .. \\\"> can't find delicates <\\\" .. aSpawner.delicateName .. \\\">\\\", 30)\\\
        end\\\
    end\\\
    \\\
    -- track this if we are have a trackwith attribute \\\
    -- note that we retrieve trackwith from ZONE, not spawner \\\
    if theZone.trackWith then \\\
        cfxSpawnZones.handoffTracking(theGroup, theZone) \\\
    end\\\
            \\\
    -- callback to all who want to know \\\
    cfxSpawnZones.invokeCallbacksFor(\\\"spawned\\\", theGroup, aSpawner)\\\
    \\\
    -- timestamp so we can check against cooldown on manual spawn\\\
    aSpawner.lastSpawnTimeStamp = timer.getTime()\\\
    -- make sure a requestable spawner is always paused \\\
    if aSpawner.requestable then \\\
        aSpawner.paused = true \\\
    end\\\
    \\\
    if aSpawner.autoRemove then \\\
        -- simply remove the group \\\
        aSpawner.theSpawn = nil\\\
    end\\\
end\\\
\\\
function cfxSpawnZones.handoffTracking(theGroup, theZone)\\\
-- note that this method works on theZone, not Spawner object\\\
    if not groupTracker then \\\
        trigger.action.outText(\\\"+++spawner: <\\\" .. theZone.name .. \\\"> trackWith requires groupTracker module\\\", 30) \\\
        return \\\
    end\\\
    local trackerName = theZone.trackWith\\\
\\\
    -- now assemble a list of all trackers\\\
    if cfxSpawnZones.verbose or theZone.verbose then \\\
        trigger.action.outText(\\\"+++spawner: spawn pass-off: \\\" .. trackerName, 30)\\\
    end \\\
    \\\
    local trackerNames = {}\\\
    if dcsCommon.containsString(trackerName, ',') then\\\
        trackerNames = dcsCommon.splitString(trackerName, ',')\\\
    else \\\
        table.insert(trackerNames, trackerName)\\\
    end\\\
    for idx, aTrk in pairs(trackerNames) do \\\
        local theName = dcsCommon.trim(aTrk)\\\
        if theName == \\\"*\\\" then theName = theZone.name end \\\
        local theTracker = groupTracker.getTrackerByName(theName)\\\
        if not theTracker then \\\
            trigger.action.outText(\\\"+++spawner: <\\\" .. theZone.name .. \\\">: cannot find tracker named <\\\".. theName .. \\\">\\\", 30) \\\
        else \\\
            groupTracker.addGroupToTracker(theGroup, theTracker)\\\
             if cfxSpawnZones.verbose or theZone.verbose then \\\
                trigger.action.outText(\\\"+++spawner: added \\\" .. theGroup:getName() .. \\\" to tracker \\\" .. theName, 30)\\\
             end\\\
        end \\\
    end \\\
end\\\
\\\
--\\\
-- U P D A T E \\\
--\\\
function cfxSpawnZones.GC()\\\
    -- GC run. remove all my dead remembered troops\\\
    local filteredAttackers = {}\\\
    local before = #cfxSpawnZones.spawnedGroups\\\
    for gName, gData in pairs (cfxSpawnZones.spawnedGroups) do \\\
        -- all we need to do is get the group of that name\\\
        -- and if it still returns units we are fine \\\
        local gameGroup = Group.getByName(gName)\\\
        if gameGroup and gameGroup:isExist() and gameGroup:getSize() > 0 then \\\
            filteredAttackers[gName] = gData\\\
        end\\\
    end\\\
    cfxSpawnZones.spawnedGroups = filteredAttackers\\\
    if cfxSpawnZones.verbose then \\\
        trigger.action.outText(\\\"spawn zones GC ran: before <\\\" .. before .. \\\">, after <\\\" .. #cfxSpawnZones.spawnedGroups .. \\\">\\\", 30)\\\
    end\\\
end\\\
\\\
function cfxSpawnZones.update()\\\
    cfxSpawnZones.updateSchedule = timer.scheduleFunction(cfxSpawnZones.update, {}, timer.getTime() + 1/cfxSpawnZones.ups)\\\
    \\\
    for key, spawner in pairs (cfxSpawnZones.allSpawners) do \\\
        -- see if the spawn is dead or was removed\\\
        local needsSpawn = true \\\
        if spawner.theSpawn then \\\
            local group = spawner.theSpawn\\\
            if group:isExist() then \\\
                -- see how many members of this group are still alive\\\
                local liveUnits = group:getSize() --dcsCommon.getLiveGroupUnits(group)\\\
                -- spawn is still alive, will not spawn\\\
                if liveUnits > 1 then \\\
                    -- we may want to check if this member is still inside\\\
                    -- of spawn location. currently we don't do that\\\
                    needsSpawn = false \\\
                end\\\
            end\\\
        end\\\
    \\\
        if spawner.paused then needsSpawn = false end \\\
        \\\
        -- see if we spawned maximum number of times already\\\
        -- or have -1 as maxspawn, indicating endless\\\
        if needsSpawn and spawner.maxSpawns > -1 then \\\
            needsSpawn = spawner.maxSpawns > 0\\\
        end\\\
        \\\
        if needsSpawn then \\\
            -- is this the first time? \\\
            if not spawner.cdStarted then \\\
                -- no, start cooldown\\\
                spawner.cdStarted = true \\\
                spawner.cdTimer = timer.getTime() + spawner.cooldown\\\
            end\\\
        end\\\
\\\
        -- still on cooldown?\\\
        if timer.getTime() < spawner.cdTimer then needsSpawn = false end \\\
        \\\
        -- is master zone still alinged with me?\\\
        needsSpawn = needsSpawn and cfxSpawnZones.verifySpawnOwnership(spawner)\\\
\\\
        -- check if perhaps our watchtriggers causes spawn\\\
        if spawner.pauseFlag then \\\
            local currTriggerVal = trigger.misc.getUserFlag(spawner.pauseFlag)\\\
            if currTriggerVal ~= spawner.lastPauseValue then\\\
                spawner.paused = true  \\\
                needsSpawn = false\\\
                spawner.lastPauseValue = currTriggerVal\\\
            end\\\
        end\\\
        \\\
        if spawner.triggerFlag then \\\
            local currTriggerVal = trigger.misc.getUserFlag(spawner.triggerFlag)\\\
            if currTriggerVal ~= spawner.lastTriggerValue then\\\
                needsSpawn = true \\\
                spawner.lastTriggerValue = currTriggerVal\\\
            end\\\
        end\\\
        \\\
        if spawner.activateFlag then \\\
            local currTriggerVal = trigger.misc.getUserFlag(spawner.activateFlag)\\\
            if currTriggerVal ~= spawner.lastActivateValue then\\\
                spawner.paused = false  \\\
                spawner.lastActivateValue = currTriggerVal\\\
            end\\\
        end\\\
\\\
        \\\
                \\\
        -- if we get here, and needsSpawn is still set, we go ahead and spawn\\\
        if needsSpawn then \\\
---            trigger.action.outText(\\\"+++ spawning for zone \\\" .. spawner.zone.name, 30)\\\
            cfxSpawnZones.spawnWithSpawner(spawner)\\\
            spawner.cdStarted = false -- reset spawner cd signal \\\
            if spawner.maxSpawns > 0 then \\\
                spawner.maxSpawns = spawner.maxSpawns - 1\\\
            end\\\
            if spawner.maxSpawns == 0 then \\\
                spawner.paused = true \\\
                if cfxSpawnZones.verbose then \\\
                    trigger.action.outText(\\\"+++ maxspawn -- turning off  zone \\\" .. spawner.zone.name, 30)\\\
                end \\\
            end\\\
        else \\\
            -- trigger.action.outText(\\\"+++ NOSPAWN for zone \\\" .. spawner.zone.name, 30)\\\
        end\\\
    end\\\
end\\\
\\\
function cfxSpawnZones.houseKeeping()\\\
    timer.scheduleFunction(cfxSpawnZones.houseKeeping, {}, timer.getTime() + 5 * 60) -- every 5 minutes \\\
    cfxSpawnZones.GC()\\\
end\\\
\\\
--\\\
-- LOAD/SAVE\\\
--\\\
function cfxSpawnZones.saveData()\\\
    local theData = {}\\\
    local allSpawnerData = {}\\\
    -- now iterate all spawners and collect their data\\\
    for theZone, theSpawner in pairs(cfxSpawnZones.allSpawners) do \\\
        local zName = theZone.name \\\
        local spawnData = {}\\\
        if theSpawner.spawn and theSpawner.spawn:isExist() then \\\
            spawnData.spawn = theSpawner.spawn:getName()\\\
        end\\\
        spawnData.count = theSpawner.count\\\
        spawnData.paused = theSpawner.paused \\\
        spawnData.cdStarted = theSpawner.cdStarted\\\
        spawnData.cdTimer = theSpawner.cdTimer - timer.getTime() -- what remains of the cooldown time \\\
        \\\
        allSpawnerData[zName] = spawnData\\\
    end\\\
    \\\
    -- run a GC\\\
    cfxSpawnZones.GC()\\\
    -- now collect all living groups\\\
    -- no longer required to check if group is lively\\\
    local allLivingTroopData = {}\\\
    for gName, gData in pairs(cfxSpawnZones.spawnedGroups) do \\\
        local sData = dcsCommon.clone(gData)\\\
        dcsCommon.synchGroupData(sData.groupData)\\\
        allLivingTroopData[gName] = sData\\\
    end\\\
    \\\
    theData.spawnerData = allSpawnerData\\\
    theData.troopData = allLivingTroopData\\\
    return theData\\\
end\\\
\\\
function cfxSpawnZones.loadData()\\\
    if not persistence then return end \\\
    local theData = persistence.getSavedDataForModule(\\\"cfxSpawnZones\\\")\\\
    if not theData then \\\
        if cfxSpawnZones.verbose then \\\
            trigger.action.outText(\\\"+++spwn: no save date received, skipping.\\\", 30)\\\
        end\\\
        return\\\
    end\\\
    \\\
    -- we begin by re-spawning all spawned groups so that the \\\
    -- spwners can then later link to them \\\
    local allTroopData = theData.troopData\\\
    for gName, gdTroop in pairs (allTroopData) do \\\
        local gData = gdTroop.groupData \\\
        local orders = gdTroop.orders \\\
        local target = gdTroop.target\\\
        local tracker = gdTroop.tracker \\\
        local side = gdTroop.side \\\
        local range = gdTroop.range\\\
        local cty = gData.cty \\\
        local cat = gData.cat  \\\
        \\\
        -- now spawn, but first \\\
        -- add to my own attacker queue so we can save later \\\
        local gdClone = dcsCommon.clone(gdTroop)\\\
        cfxSpawnZones.spawnedGroups[gName] = gdClone \\\
        local theGroup = coalition.addGroup(cty, cat, gData)\\\
        -- post-proccing for 'train' orders\\\
        if orders and (orders == \\\"train\\\" ) then \\\
            -- make them ROE \\\"HOLD\\\"\\\
            cfxCommander.scheduleOptionForGroup(\\\
                theGroup, \\\
                AI.Option.Ground.id.ROE, \\\
                AI.Option.Ground.val.ROE.WEAPON_HOLD, \\\
                1.0)\\\
        else \\\
            -- add to groundTroops \\\
            local newTroops = cfxGroundTroops.createGroundTroops(theGroup, range, orders) \\\
            cfxGroundTroops.addGroundTroopsToPool(newTroops)\\\
            -- engage a target zone \\\
            if target then \\\
                local destZone = cfxZones.getZoneByName(target)\\\
                if destZone then\\\
                    newTroops.destination = destZone\\\
                    cfxGroundTroops.makeTroopsEngageZone(newTroops)\\\
                end \\\
            end\\\
        end \\\
                \\\
        -- post-proccing for trackwith [may not be needed when we]\\\
        -- have persistence in the tracking module. that module \\\
        -- simply schedules re-connecting after one second \\\
    end\\\
    \\\
    -- now set up all spawners with save data \\\
    local allSpawnerData = theData.spawnerData\\\
    for zName, sData in pairs (allSpawnerData) do \\\
        local theZone = cfxZones.getZoneByName(zName)\\\
        if theZone then \\\
            local theSpawner = cfxSpawnZones.getSpawnerForZone(theZone)\\\
            if theSpawner then \\\
                theSpawner.inited = true -- inited by persistence\\\
                theSpawner.count = sData.count \\\
                theSpawner.paused = sData.paused\\\
                theSpawner.cdStarted = sData.cdStarted\\\
                if theSpawner.cdStarted then \\\
                    theSpawner.cdTimer = timer.getTime() + sData.cdTimer\\\
                else \\\
                    theSpawner.cdTimer = -1\\\
                end\\\
                if sData.spawn then \\\
                    local theGroup = Group.getByName(sData.spawn)\\\
                    if theGroup then \\\
                        theSpawner.spawn = theGroup\\\
                    else \\\
                        trigger.action.outText(\\\"+++spwn (persistence): can't re-connect spawner <\\\" .. zName .. \\\"> with group <\\\" .. sData.spawn .. \\\">, skipping\\\", 30)\\\
                    end\\\
                end\\\
            else \\\
                trigger.action.outText(\\\"+++spwn (persistence): can't find spawner for zone <\\\" .. zName .. \\\">, skipping\\\", 30)\\\
            end\\\
        else \\\
            trigger.action.outText(\\\"+++spwn (persistence): can't find zone <\\\" .. zName .. \\\"> for spawner, skipping\\\", 30)\\\
        end\\\
    end\\\
    \\\
end\\\
\\\
--\\\
-- START \\\
--\\\
function cfxSpawnZones.initialSpawnCheck(aSpawner)\\\
    if not aSpawner.paused \\\
    and cfxSpawnZones.verifySpawnOwnership(aSpawner) \\\
    and aSpawner.maxSpawns ~= 0 \\\
    and not aSpawner.inited \\\
    then \\\
        cfxSpawnZones.spawnWithSpawner(aSpawner)\\\
        -- update spawn count and make sure we haven't spawned the one and only \\\
        if aSpawner.maxSpawns > 0 then \\\
            aSpawner.maxSpawns = aSpawner.maxSpawns - 1\\\
        end\\\
        if aSpawner.maxSpawns == 0 then \\\
            aSpawner.paused = true \\\
            trigger.action.outText(\\\"+++ maxspawn -- turning off  zone \\\" .. aSpawner.zone.name, 30)\\\
        end\\\
    end\\\
end\\\
\\\
function cfxSpawnZones.start()\\\
    if not dcsCommon.libCheck(\\\"cfx Spawn Zones\\\", \\\
        cfxSpawnZones.requiredLibs) then\\\
        return false \\\
    end\\\
    \\\
    -- collect all spawn zones \\\
    local attrZones = cfxZones.getZonesWithAttributeNamed(\\\"spawner\\\")\\\
    \\\
    -- now create a spawner for all, add them to the spawner updater, and spawn for all zones that are not\\\
    -- paused \\\
    for k, aZone in pairs(attrZones) do \\\
        local aSpawner = cfxSpawnZones.createSpawner(aZone)\\\
        cfxSpawnZones.addSpawner(aSpawner)\\\
    end\\\
    \\\
    -- we now do persistence\\\
    if persistence then \\\
        -- sign up for persistence \\\
        callbacks = {}\\\
        callbacks.persistData = cfxSpawnZones.saveData\\\
        persistence.registerModule(\\\"cfxSpawnZones\\\", callbacks)\\\
        -- now load my data \\\
        cfxSpawnZones.loadData()\\\
    end\\\
    \\\
    -- we now spawn if not taken care of by load / save \\\
    for theZone, aSpawner in pairs(cfxSpawnZones.allSpawners) do\\\
        cfxSpawnZones.initialSpawnCheck(aSpawner)\\\
    end\\\
    \\\
    -- and start the regular update calls\\\
    cfxSpawnZones.update()\\\
    \\\
    -- start housekeeping \\\
    cfxSpawnZones.houseKeeping()\\\
    \\\
    trigger.action.outText(\\\"cfx Spawn Zones v\\\" .. cfxSpawnZones.version .. \\\" started.\\\", 30)\\\
    return true\\\
end\\\
\\\
if not cfxSpawnZones.start() then \\\
    trigger.action.outText(\\\"cf/x Spawn Zones aborted: missing libraries\\\", 30)\\\
    cfxSpawnZones = nil \\\
end\\\
\\\
--[[--\\\
IMPROVEMENTS\\\
 'notMasterOwner' a flag to invert ownership, so we can spawn blue if masterOwner is red\\\
  \\\
  take apart owned zone and spawner, so we have a more canonical behaviour\\\
  \\\
  'repair' flag - have repair logic for units that are spawned just like now the owned zones do\\\
--]]--\");a_do_script(\"cfxHeloTroops = {}\\\
cfxHeloTroops.version = \\\"3.0.4\\\"\\\
cfxHeloTroops.verbose = false \\\
cfxHeloTroops.autoDrop = true \\\
cfxHeloTroops.autoPickup = false \\\
cfxHeloTroops.pickupRange = 100 -- meters \\\
cfxHeloTroops.requestRange = 500 -- meters\\\
--\\\
--[[--\\\
 VERSION HISTORY\\\
 1.1.3 - repaired forgetting 'wait-' when loading/disembarking\\\
 1.1.4 - corrected coalition bug in deployTroopsFromHelicopter \\\
 2.0.0 - added weight change when troops enter and leave the helicopter \\\
       - idividual troop capa max per helicopter \\\
 2.0.1 - lib loader verification\\\
       - uses dcsCommon.isTroopCarrier(theUnit)\\\
 2.0.2 - can now deploy from spawners with \\\"requestable\\\" attribute\\\
 2.1.0 - supports config zones\\\
       - check spawner legality by types  \\\
       - updated types to include 2.7.6 additions to infantry\\\
       - updated types to include stinger/manpads \\\
 2.2.0 - minor maintenance (dcsCommon)\\\
       - (re?) connected readConfigZone (wtf?)\\\
       - persistence support\\\
       - made legalTroops entrirely optional and defer to dcsComon else\\\
 2.3.0 - interface with owned zones and playerScore when \\\
       - combat-dropping troops into non-owned owned zone.\\\
       - prevent auto-load from pre-empting loading csar troops \\\
 2.3.1 - added ability to self-define troopCarriers via config\\\
 2.4.0 - added missing support for attackZone orders (destination)\\\
       - eliminated cfxPlayer module import and all dependencies\\\
       - added support for groupTracker / limbo \\\
       - removed restriction to only apply to helicopters in anticipation of the C-130 Hercules appearing in the game\\\
 2.4.1 - new actionSound attribute, sound plays to group whenever \\\
         troops have boarded or disembarked\\\
 3.0.0 - added requestable cloner support \\\
       - harmonized spawning invocations across cloners and spawners \\\
       - dmlZones \\\
       - requestRange attribute\\\
 3.0.1 - fixed a bug with legalTroops attribute\\\
 3.0.2 - fixed a typo in in-air menu \\\
 3.0.3 - pointInZone check for insertion rather than radius \\\
 3.0.4 - also handles picking up troops with orders \\\"captureandhold\\\"\\\
 \\\
--]]--\\\
--\\\
-- cfxHeloTroops -- a module to pick up and drop infantry. \\\
-- Can be used with ANY aircraft, configured by default to be \\\
-- restricted to troop-carrying helicopters.\\\
-- might be configure to apply to any type you want using the \\\
-- configuration zone.\\\
\\\
\\\
cfxHeloTroops.requiredLibs = {\\\
    \\\"dcsCommon\\\", -- common is of course needed for everything\\\
                 -- pretty stupid to check for this since we \\\
                 -- need common to invoke the check, but anyway\\\
    \\\"cfxZones\\\", -- Zones, of course \\\
    \\\"cfxCommander\\\", -- to make troops do stuff\\\
    \\\"cfxGroundTroops\\\", -- generic when dropping troops\\\
}\\\
\\\
cfxHeloTroops.unitConfigs = {} -- all configs are stored by unit's name \\\
cfxHeloTroops.troopWeight = 100 -- kg average weight per trooper \\\
\\\
-- persistence support \\\
cfxHeloTroops.deployedTroops = {}\\\
\\\
function cfxHeloTroops.resetConfig(conf)\\\
    conf.autoDrop = cfxHeloTroops.autoDrop --if true, will drop troops on-board upon touchdown\\\
    conf.autoPickup = cfxHeloTroops.autoPickup -- if true will load nearest troops upon touchdown\\\
    conf.pickupRange = cfxHeloTroops.pickupRange --meters, maybe make per helo?\\\
    conf.currentState = -1 -- 0 = landed, 1 = airborne, -1 undetermined\\\
    conf.troopsOnBoardNum = 0 -- if not 0, we have troops and can spawnm/drop\\\
    conf.troopCapacity = 8 -- should be depending on airframe \\\
    -- troopsOnBoard.name contains name of group\\\
    -- the other fields info for troops picked up \\\
    conf.troopsOnBoard = {} -- table with the following\\\
    conf.troopsOnBoard.name = \\\"***reset***\\\"\\\
    conf.dropFormation = \\\"circle_out\\\" -- may be chosen later?\\\
end\\\
\\\
function cfxHeloTroops.createDefaultConfig(theUnit)\\\
    local conf = {}\\\
    cfxHeloTroops.resetConfig(conf)\\\
\\\
    conf.myMainMenu = nil -- this is where the main menu for group will be stored\\\
    conf.myCommands = nil -- this is where we put all teh commands in \\\
    return conf \\\
end\\\
\\\
\\\
function cfxHeloTroops.getUnitConfig(theUnit) -- will create new config if not existing\\\
    if not theUnit then\\\
        trigger.action.outText(\\\"+++WARNING: nil unit in get config!\\\", 30)\\\
        return nil \\\
    end\\\
    local c = cfxHeloTroops.unitConfigs[theUnit:getName()]\\\
    if not c then \\\
        c = cfxHeloTroops.createDefaultConfig(theUnit)\\\
        cfxHeloTroops.unitConfigs[theUnit:getName()] = c \\\
    end\\\
    return c \\\
end\\\
\\\
function cfxHeloTroops.getConfigForUnitNamed(aName)\\\
    return cfxHeloTroops.unitConfigs[aName]\\\
end\\\
\\\
--\\\
--\\\
-- LANDED\\\
--\\\
--\\\
function cfxHeloTroops.loadClosestGroup(conf)\\\
    local p = conf.unit:getPosition().p\\\
    local cat = Group.Category.GROUND\\\
    local unitsToLoad = dcsCommon.getLivingGroupsAndDistInRangeToPoint(p, conf.pickupRange, conf.unit:getCoalition(), cat) \\\
    \\\
    -- groups may contain units that are not for transport.\\\
    -- for now we only load troops with legal type strings \\\
    unitsToLoad = cfxHeloTroops.filterTroopsByType(unitsToLoad)\\\
\\\
    -- now limit the options to the five closest legal groups\\\
    local numUnits = #unitsToLoad\\\
    if numUnits < 1 then return false end -- on false will drop through \\\
    \\\
    local aTeam = unitsToLoad[1] -- get first (closest) entry\\\
    local dist = aTeam.dist \\\
    local group = aTeam.group \\\
    cfxHeloTroops.doLoadGroup({conf, group})\\\
    return true -- will have loaded and reset menu\\\
end\\\
\\\
function cfxHeloTroops.heloLanded(theUnit)\\\
    -- when we have landed, \\\
    if not dcsCommon.isTroopCarrier(theUnit, cfxHeloTroops.troopCarriers) then return end\\\
    \\\
    local conf = cfxHeloTroops.getUnitConfig(theUnit)\\\
    conf.unit = theUnit\\\
    conf.currentState = 0\\\
    \\\
    -- we look if we auto-unload\\\
    if conf.autoDrop then \\\
        if conf.troopsOnBoardNum > 0 then \\\
            cfxHeloTroops.doDeployTroops({conf, \\\"autodrop\\\"})\\\
            -- already called set menu, can exit directly\\\
            return\\\
        end\\\
        -- when we get here, we have no troops to drop on board \\\
        -- so nothing to do really except look if we can pick up troops\\\
        -- set menu will do that for us    \\\
    end\\\
    \\\
    if conf.autoPickup then \\\
        if conf.troopsOnBoardNum < 1 then\\\
            -- load the closest group\\\
            if cfxHeloTroops.loadClosestGroup(conf) then \\\
                return\\\
            end\\\
        end        \\\
    end\\\
    \\\
    -- when we get here, we simply set the newest menus and are done \\\
    -- reset menu \\\
    cfxHeloTroops.removeComms(conf.unit)\\\
    cfxHeloTroops.setCommsMenu(conf.unit)\\\
end\\\
\\\
--\\\
--\\\
-- Helo took off\\\
--\\\
--\\\
function cfxHeloTroops.heloDeparted(theUnit)\\\
    if not dcsCommon.isTroopCarrier(theUnit, cfxHeloTroops.troopCarriers) then return end\\\
    \\\
    -- when we take off, all that needs to be done is to change the state \\\
    -- to airborne, and then set the status flag \\\
    local conf = cfxHeloTroops.getUnitConfig(theUnit)\\\
    conf.currentState = 1 -- in the air \\\
    \\\
    cfxHeloTroops.removeComms(conf.unit)\\\
    cfxHeloTroops.setCommsMenu(conf.unit)\\\
    \\\
end\\\
\\\
--\\\
-- \\\
-- Helo Crashed \\\
--\\\
--\\\
function cfxHeloTroops.cleanHelo(theUnit)\\\
    -- clean up \\\
    local conf = cfxHeloTroops.getUnitConfig(theUnit)\\\
    conf.unit = theUnit \\\
    conf.troopsOnBoardNum = 0 -- all dead \\\
    conf.currentState = -1 -- (we don't know)\\\
\\\
    -- check if we need to interface with groupTracker \\\
    if conf.troopsOnBoard.name and groupTracker then \\\
        local theName = conf.troopsOnBoard.name\\\
        -- there was (possibly) a group on board. see if it was tracked\\\
        local isTracking, numTracking, trackers = groupTracker.groupNameTrackedBy(theName)\\\
        \\\
        -- if so, remove it from limbo \\\
        if isTracking then \\\
            for idx, theTracker in pairs(trackers) do \\\
                groupTracker.removeGroupNamedFromTracker(theName, theTracker)\\\
                if cfxHeloTroops.verbose then \\\
                    trigger.action.outText(\\\"+++Helo: removed group <\\\" .. theName .. \\\"> from tracker <\\\" .. theTracker.name .. \\\">\\\", 30)\\\
                end \\\
            end \\\
        end\\\
    end\\\
    conf.troopsOnBoard = {}\\\
end\\\
\\\
function cfxHeloTroops.heloCrashed(theUnit)\\\
    if not dcsCommon.isTroopCarrier(theUnit, cfxHeloTroops.troopCarriers) then return \\\
    end\\\
    -- clean up \\\
    cfxHeloTroops.cleanHelo(theUnit)\\\
end\\\
\\\
--\\\
--\\\
-- M E N U   H A N D L I N G   &   R E S P O N S E \\\
-- \\\
-- \\\
function cfxHeloTroops.clearCommsSubmenus(conf)\\\
    if conf.myCommands then \\\
        for i=1, #conf.myCommands do\\\
            missionCommands.removeItemForGroup(conf.id, conf.myCommands[i])\\\
        end\\\
    end\\\
    conf.myCommands = {}\\\
end\\\
\\\
function cfxHeloTroops.removeCommsFromConfig(conf)\\\
    cfxHeloTroops.clearCommsSubmenus(conf)\\\
    \\\
    if conf.myMainMenu then \\\
        missionCommands.removeItemForGroup(conf.id, conf.myMainMenu) \\\
        conf.myMainMenu = nil\\\
    end\\\
end\\\
\\\
function cfxHeloTroops.removeComms(theUnit)\\\
    if not theUnit then return end\\\
    if not theUnit:isExist() then return end \\\
    \\\
    local group = theUnit:getGroup() \\\
    local id = group:getID()\\\
    local conf = cfxHeloTroops.getUnitConfig(theUnit)\\\
    conf.id = id\\\
    conf.unit = theUnit \\\
    \\\
    cfxHeloTroops.removeCommsFromConfig(conf)\\\
end\\\
\\\
function cfxHeloTroops.addConfigMenu(conf)\\\
    -- we add the a menu showing current state \\\
    -- and the option to change fro auto drop \\\
    -- and auto pickup \\\
    local onOff = \\\"OFF\\\"\\\
    if conf.autoDrop then onOff = \\\"ON\\\" end \\\
    local theCommand =  missionCommands.addCommandForGroup(\\\
                conf.id, \\\
                'Auto-Drop: ' .. onOff .. ' - Select to change',\\\
                conf.myMainMenu,\\\
                cfxHeloTroops.redirectToggleConfig, \\\
                {conf, \\\"drop\\\"}\\\
                )\\\
    table.insert(conf.myCommands, theCommand)\\\
    onOff = \\\"OFF\\\"\\\
    if conf.autoPickup then onOff = \\\"ON\\\" end \\\
    theCommand =  missionCommands.addCommandForGroup(\\\
                conf.id, \\\
                'Auto-Pickup: ' .. onOff .. ' - Select to change',\\\
                conf.myMainMenu,\\\
                cfxHeloTroops.redirectToggleConfig, \\\
                {conf, \\\"pickup\\\"}\\\
                )\\\
    table.insert(conf.myCommands, theCommand)\\\
end\\\
\\\
function cfxHeloTroops.setCommsMenu(theUnit)\\\
    -- depending on own load state, we set the command structure\\\
    -- it begins at 10-other, and has 'Assault Troops' as main menu with submenus\\\
    -- as required \\\
    if not theUnit then return end\\\
    if not theUnit:isExist() then return end \\\
    \\\
    -- we only add this menu to troop carriers \\\
    if not dcsCommon.isTroopCarrier(theUnit, cfxHeloTroops.troopCarriers) then\\\
        if cfxHeloTroops.verbose then \\\
            trigger.action.outText(\\\"+++heloT - player unit <\\\" .. theUnit:getName() .. \\\"> type <\\\" .. theUnit:getTypeName() .. \\\"> is not legal troop carrier.\\\", 30)\\\
        end\\\
        return \\\
    end\\\
    \\\
    local group = theUnit:getGroup() \\\
    local id = group:getID()\\\
    local conf = cfxHeloTroops.getUnitConfig(theUnit)\\\
    conf.id = id; -- we do this ALWAYS to it is current even after a crash \\\
    conf.unit = theUnit -- link back\\\
    \\\
    -- ok, first, if we don't have an F-10 menu, create one \\\
    if not (conf.myMainMenu) then \\\
        conf.myMainMenu = missionCommands.addSubMenuForGroup(id, 'Airlift Troops') \\\
    end\\\
    \\\
    -- clear out existing commands\\\
    cfxHeloTroops.clearCommsSubmenus(conf)\\\
    \\\
    -- now we have a menu without submenus. \\\
    -- add our own submenus\\\
    cfxHeloTroops.addConfigMenu(conf)\\\
    \\\
    -- now see if we are on the ground or in the air\\\
    -- or unknown\\\
    if conf.currentState < 0 then \\\
        conf.currentState = 0 -- landed\\\
        if theUnit:inAir() then \\\
            conf.currentState = 1\\\
        end\\\
    end\\\
    \\\
    if conf.currentState == 0 then \\\
        cfxHeloTroops.addGroundMenu(conf)\\\
    else \\\
        cfxHeloTroops.addAirborneMenu(conf)\\\
    end\\\
    \\\
end\\\
\\\
function cfxHeloTroops.addAirborneMenu(conf)\\\
    -- while we are airborne, there isn't much to do except add a status menu that does nothing\\\
    -- but we can add some instructions\\\
    -- let's begin by assuming no troops aboard\\\
    local commandTxt = \\\"(To load troops, land in proximity to them)\\\"\\\
    if conf.troopsOnBoardNum > 0 then \\\
        commandTxt = \\\"(You are carrying \\\" .. conf.troopsOnBoardNum .. \\\" Assault Troops. Land to deploy them)\\\"\\\
    end\\\
    local theCommand =  missionCommands.addCommandForGroup(\\\
                conf.id, \\\
                commandTxt,\\\
                conf.myMainMenu,\\\
                cfxHeloTroops.redirectNoAction, \\\
                {conf, \\\"none\\\"}\\\
                )\\\
    table.insert(conf.myCommands, theCommand)\\\
end\\\
\\\
function cfxHeloTroops.redirectNoAction(args)\\\
    -- actually, we do not redirect since there is nothing to do\\\
end\\\
\\\
function cfxHeloTroops.addGroundMenu(conf)\\\
    -- this is the most complex menu. Player can deploy troops when loaded\\\
    -- and load troops when they are in proximity\\\
    \\\
    -- case 1: troops aboard \\\
    if conf.troopsOnBoardNum > 0 then \\\
        local theCommand =  missionCommands.addCommandForGroup(\\\
                conf.id, \\\
                \\\"Deploy Team <\\\" .. conf.troopsOnBoard.name .. \\\">\\\",\\\
                conf.myMainMenu,\\\
                cfxHeloTroops.redirectDeployTroops, \\\
                {conf, \\\"deploy\\\"}\\\
                )\\\
        table.insert(conf.myCommands, theCommand)\\\
        return\\\
    end\\\
    \\\
    -- case 2A: no troops aboard, and requestable spawners/cloners in range \\\
    local p = conf.unit:getPosition().p\\\
    local mySide = conf.unit:getCoalition() \\\
\\\
    -- collect available spawn zones \\\
    local availableSpawners = {}\\\
    if cfxSpawnZones then -- only if SpawnZones is implemented \\\
        local availableSpawnersRaw = cfxSpawnZones.getRequestableSpawnersInRange(p, cfxHeloTroops.requestRange, mySide)\\\
        \\\
        for idx, aSpawner in pairs(availableSpawnersRaw) do \\\
            -- filter all spawners that spawn \\\"illegal\\\" troops\\\
            local theTypes = aSpawner.types\\\
            local typeArray = dcsCommon.splitString(theTypes, ',')\\\
            typeArray = dcsCommon.trimArray(typeArray)\\\
            local allLegal = true \\\
            -- check agianst default (dcsCommon) or own definition (if exists)\\\
            for idy, aType in pairs(typeArray) do \\\
                if cfxHeloTroops.legalTroops then \\\
                    if not dcsCommon.arrayContainsString(cfxHeloTroops.legalTroops, aType) then \\\
                        allLegal = false \\\
                    end\\\
                else \\\
                    if not dcsCommon.typeIsInfantry(aType) then \\\
                        allLegal = false \\\
                    end\\\
                end\\\
            end\\\
            if allLegal then \\\
                table.insert(availableSpawners, aSpawner)\\\
            end\\\
        end\\\
    end \\\
    \\\
    -- collect available clone zones \\\
    if cloneZones then \\\
        local availableSpawnersRaw = cloneZones.getRequestableClonersInRange(p, cfxHeloTroops.requestRange, mySide)\\\
        for idx, aSpawner in pairs(availableSpawnersRaw) do \\\
            -- filter all spawners that spawn \\\"illegal\\\" troops or have none\\\
            local theTypes = aSpawner.allTypes\\\
            local allLegal = true\\\
            local numTypes = dcsCommon.getSizeOfTable(theTypes)\\\
            if numTypes > 0 then \\\
                for aType, cnt in pairs(theTypes) do\\\
                    if cfxHeloTroops.legalTroops then \\\
                        if not dcsCommon.arrayContainsString(cfxHeloTroops.legalTroops, aType) then \\\
                            allLegal = false \\\
                        end\\\
                    else \\\
                        if not dcsCommon.typeIsInfantry(aType) then \\\
                            allLegal = false \\\
                        end\\\
                    end\\\
                end\\\
            else \\\
                allegal = false \\\
            end\\\
            \\\
            if allLegal then \\\
                table.insert(availableSpawners, aSpawner)\\\
            end\\\
        end\\\
    end\\\
    \\\
    local numSpawners = #availableSpawners\\\
    if numSpawners > 5 then numSpawners = 5 end \\\
    while numSpawners > 0 do\\\
        -- for each spawner in range, create a \\\
        -- spawn menu item\\\
        local spawner = availableSpawners[numSpawners]\\\
        local theName = spawner.baseName \\\
        local comm = \\\"Request <\\\" .. theName .. \\\"> troops for transport\\\" -- .. math.floor(aTeam.dist) .. \\\"m away\\\"\\\
        local theCommand =  missionCommands.addCommandForGroup(\\\
            conf.id, \\\
            comm,\\\
            conf.myMainMenu,\\\
            cfxHeloTroops.redirectSpawnGroup, \\\
            {conf, spawner}\\\
            )\\\
        table.insert(conf.myCommands, theCommand)\\\
        numSpawners = numSpawners - 1\\\
    end\\\
    \\\
    -- case 2B: no troops aboard. see if there are troops around \\\
    -- that we can load up \\\
    \\\
    local cat = Group.Category.GROUND\\\
    local unitsToLoad = dcsCommon.getLivingGroupsAndDistInRangeToPoint(p, conf.pickupRange, conf.unit:getCoalition(), cat) \\\
    \\\
    -- now, the groups may contain units that are not for transport.\\\
    -- later we can filter this by weight, or other cool stuff\\\
    -- for now we simply only troopy with legal type strings \\\
    -- TODO: add weight filtering \\\
    unitsToLoad = cfxHeloTroops.filterTroopsByType(unitsToLoad)\\\
\\\
    -- now limit the options to the five closest legal groups\\\
    local numUnits = #unitsToLoad\\\
    if numUnits > 5 then numUnits = 5 end\\\
    if numUnits < 1 then \\\
        local theCommand =  missionCommands.addCommandForGroup(\\\
                conf.id, \\\
                \\\"(No units in range)\\\",\\\
                conf.myMainMenu,\\\
                cfxHeloTroops.redirectNoAction, \\\
                {conf, \\\"none\\\"}\\\
                )\\\
        table.insert(conf.myCommands, theCommand)\\\
        return\\\
    end\\\
    \\\
    -- add an entry for each group in units to load \\\
    for i=1, numUnits do \\\
        local aTeam = unitsToLoad[i]\\\
        local dist = aTeam.dist \\\
        local group = aTeam.group \\\
        local tNum = group:getSize()\\\
        local comm = \\\"Load <\\\" .. group:getName() .. \\\"> \\\" .. tNum .. \\\" Members\\\" -- .. math.floor(aTeam.dist) .. \\\"m away\\\"\\\
        local theCommand =  missionCommands.addCommandForGroup(\\\
                conf.id, \\\
                comm,\\\
                conf.myMainMenu,\\\
                cfxHeloTroops.redirectLoadGroup, \\\
                {conf, group}\\\
                )\\\
        table.insert(conf.myCommands, theCommand)\\\
    end\\\
end\\\
\\\
function cfxHeloTroops.filterTroopsByType(unitsToLoad)\\\
    local filteredGroups = {}\\\
    for idx, aTeam in pairs(unitsToLoad) do \\\
        local group = aTeam.group\\\
        local theTypes = dcsCommon.getGroupTypeString(group)\\\
\\\
        local aT = dcsCommon.splitString(theTypes, \\\",\\\")\\\
        local pass = true \\\
        for iT, sT in pairs(aT) do \\\
            -- check if this is a valid type \\\
            if cfxHeloTroops.legalTroops then \\\
                if not dcsCommon.arrayContainsString(cfxHeloTroops.legalTroops, sT) then \\\
                    pass = false\\\
                    break \\\
                end\\\
            else \\\
                if not dcsCommon.typeIsInfantry(sT) then \\\
                    pass = false\\\
                    break \\\
                end\\\
            end\\\
        end \\\
        -- check if we are about to pre-empt a CSAR mission\\\
        if csarManager then \\\
            if csarManager.isCSARTarget(group) then \\\
                -- this one is managed by csarManager,\\\
                -- don't load it for helo troops\\\
                pass = false \\\
            end\\\
        end\\\
            \\\
        if pass then \\\
            table.insert(filteredGroups, aTeam)\\\
        end\\\
    end\\\
    return filteredGroups\\\
end\\\
\\\
--\\\
-- T O G G L E S \\\
--\\\
\\\
function cfxHeloTroops.redirectToggleConfig(args)\\\
    timer.scheduleFunction(cfxHeloTroops.doToggleConfig, args, timer.getTime() + 0.1)\\\
end\\\
\\\
function cfxHeloTroops.doToggleConfig(args)\\\
    local conf = args[1]\\\
    local what = args[2]\\\
    if what == \\\"drop\\\" then \\\
        conf.autoDrop = not conf.autoDrop\\\
        if conf.autoDrop then \\\
            trigger.action.outTextForGroup(conf.id, \\\"Now deploying troops immediately after landing\\\", 30)\\\
        else \\\
            trigger.action.outTextForGroup(conf.id, \\\"Troops will now only deploy when told to\\\", 30)        \\\
        end\\\
    else \\\
        conf.autoPickup = not conf.autoPickup\\\
        if conf.autoPickup then \\\
            trigger.action.outTextForGroup(conf.id, \\\"Nearest troops will now automatically board after landing\\\", 30)\\\
        else\\\
            trigger.action.outTextForGroup(conf.id, \\\"Troops will now board only after being ordered to do so\\\", 30)\\\
        end\\\
        \\\
    end\\\
    \\\
    cfxHeloTroops.setCommsMenu(conf.unit)\\\
end\\\
\\\
\\\
--\\\
-- Deploying Troops\\\
--\\\
function cfxHeloTroops.redirectDeployTroops(args)\\\
    timer.scheduleFunction(cfxHeloTroops.doDeployTroops, args, timer.getTime() + 0.1)\\\
end\\\
\\\
function cfxHeloTroops.scoreWhenCapturing(theUnit)\\\
    if theUnit and Unit.isExist(theUnit) and theUnit.getPlayerName then \\\
        -- see if wer are inside a non-alinged zone\\\
        -- and this includes a neutral zone \\\
        local coa = theUnit:getCoalition()\\\
        local p = theUnit:getPoint()\\\
        local theGroup = theUnit:getGroup()\\\
        local ID = theGroup:getID()\\\
        local nearestZone, dist = cfxOwnedZones.getNearestOwnedZoneToPoint(p)\\\
        if nearestZone and nearestZone:pointInZone(p) then -- dist < nearestZone.radius then \\\
            -- we are inside an owned zone!\\\
            if nearestZone.owner ~= coa then \\\
                -- yup, combat drop!\\\
                local theScore = cfxHeloTroops.combatDropScore\\\
                local pName = theUnit:getPlayerName()\\\
                if pName then \\\
                    cfxPlayerScore.updateScoreForPlayer(pName, theScore)\\\
                    cfxPlayerScore.logFeatForPlayer(pName, \\\"Combat Troop Insertion at \\\" .. nearestZone.name, coa)\\\
                end\\\
            end\\\
        end\\\
    end\\\
end\\\
\\\
function cfxHeloTroops.doDeployTroops(args)\\\
    local conf = args[1]\\\
    local what = args[2]\\\
    -- deploy the troops I have on board in formation\\\
    cfxHeloTroops.deployTroopsFromHelicopter(conf)\\\
    \\\
    -- interface with playerscore if we dropped \\\
    -- inside an enemy-owned zone \\\
    if cfxPlayerScore and cfxOwnedZones then \\\
        local theUnit = conf.unit\\\
        cfxHeloTroops.scoreWhenCapturing(theUnit)\\\
    end\\\
    \\\
    -- set own troops to 0 and erase type string \\\
    conf.troopsOnBoardNum = 0\\\
    conf.troopsOnBoard = {}\\\
    conf.troopsOnBoard.name = \\\"***wasdeployed***\\\"\\\
    \\\
    -- reset menu \\\
    cfxHeloTroops.removeComms(conf.unit)\\\
    cfxHeloTroops.setCommsMenu(conf.unit)\\\
end\\\
\\\
\\\
function cfxHeloTroops.deployTroopsFromHelicopter(conf)\\\
-- we have troops, drop them now\\\
    local unitTypes = {} -- build type names\\\
    local theUnit = conf.unit \\\
    local p = theUnit:getPoint() \\\
        \\\
    -- split the conf.troopsOnBoardTypes into an array of types\\\
    unitTypes = dcsCommon.splitString(conf.troopsOnBoard.types, \\\",\\\")\\\
    if #unitTypes < 1 then \\\
        table.insert(unitTypes, \\\"Soldier M4\\\") -- make it one m4 trooper as fallback\\\
    end\\\
    \\\
    local range = conf.troopsOnBoard.range\\\
    local orders = conf.troopsOnBoard.orders \\\
    local dest = conf.troopsOnBoard.destination\\\
    local theName = conf.troopsOnBoard.name \\\
    \\\
    if not orders then orders = \\\"guard\\\" end\\\
    \\\
    -- order processing: if the orders were pre-pended with \\\"wait-\\\"\\\
    -- we now remove that, so after dropping they do what their \\\
    -- orders where AFTER being picked up\\\
    if dcsCommon.stringStartsWith(orders, \\\"wait-\\\") then \\\
        orders = dcsCommon.removePrefix(orders, \\\"wait-\\\")\\\
        trigger.action.outTextForGroup(conf.id, \\\"+++ <\\\" .. conf.troopsOnBoard.name .. \\\"> revoke 'wait' orders, proceed with <\\\".. orders .. \\\">\\\", 30)\\\
    end\\\
    \\\
    local chopperZone = cfxZones.createSimpleZone(\\\"choppa\\\", p, 12) -- 12 m radius around choppa\\\
    local theCoalition = theUnit:getGroup():getCoalition() -- make it choppers COALITION\\\
    local theGroup, theData = cfxZones.createGroundUnitsInZoneForCoalition (\\\
                theCoalition,                                                 \\\
                theName, -- group name, may be tracked \\\
                chopperZone,                                             \\\
                unitTypes,                                                     \\\
                conf.dropFormation,\\\
                90)\\\
    -- persistence management \\\
    local troopData = {}\\\
    troopData.groupData = theData\\\
    troopData.orders = orders -- always set  \\\
    troopData.side = theCoalition\\\
    troopData.range = range\\\
    troopData.destination = dest -- only for attackzone orders \\\
    cfxHeloTroops.deployedTroops[theData.name] = troopData \\\
    \\\
    local troop = cfxGroundTroops.createGroundTroops(theGroup, range, orders) \\\
    troop.destination = dest -- transfer target zone for attackzone oders\\\
    cfxGroundTroops.addGroundTroopsToPool(troop) -- will schedule move orders\\\
    trigger.action.outTextForGroup(conf.id, \\\"<\\\" .. theGroup:getName() .. \\\"> have deployed to the ground with orders \\\" .. orders .. \\\"!\\\", 30)\\\
    trigger.action.outSoundForGroup(conf.id, cfxHeloTroops.actionSound) \\\
    -- see if this is tracked by a tracker, and pass them back so \\\
    -- they can un-limbo \\\
    if groupTracker then \\\
        local isTracking, numTracking, trackers = groupTracker.groupNameTrackedBy(theName)\\\
        if isTracking then \\\
            for idx, theTracker in pairs (trackers) do \\\
                groupTracker.addGroupToTracker(theGroup, theTracker)\\\
                if cfxHeloTroops.verbose then \\\
                    trigger.action.outText(\\\"+++Helo: un-limbo and tracking group <\\\" .. theName .. \\\"> with tracker <\\\" .. theTracker.name .. \\\">\\\", 30)\\\
                end\\\
            end\\\
        end\\\
    end\\\
end\\\
\\\
--\\\
-- Loading Troops\\\
--\\\
function cfxHeloTroops.redirectLoadGroup(args)\\\
    timer.scheduleFunction(cfxHeloTroops.doLoadGroup, args, timer.getTime() + 0.1)\\\
end\\\
\\\
function cfxHeloTroops.doLoadGroup(args) \\\
    local conf = args[1]\\\
    local group = args[2]\\\
    conf.troopsOnBoard = {}\\\
    -- all we need to do is disassemble the group into type \\\
    conf.troopsOnBoard.types = dcsCommon.getGroupTypeString(group)\\\
    -- get the size \\\
    conf.troopsOnBoardNum = group:getSize()\\\
    -- and name \\\
    local gName = group:getName()\\\
    conf.troopsOnBoard.name = gName\\\
    -- and put it all into the helicopter config \\\
    \\\
    -- now we need to destroy the group. Let's prepare:\\\
    -- if it was tracked, tell tracker to move it to limbo \\\
    -- to remember it even if it's destroyed \\\
    if groupTracker then\\\
        -- only if groupTracker is active\\\
        local isTracking, numTracking, trackers = groupTracker.groupTrackedBy(group)\\\
        if isTracking then \\\
            -- we need to put them in limbo for every tracker \\\
            for idx, aTracker in pairs(trackers) do \\\
                if cfxHeloTroops.verbose then \\\
                    trigger.action.outText(\\\"+++Helo: moving group <\\\" .. gName .. \\\"> to limbo for tracker <\\\" .. aTracker.name .. \\\">\\\", 30)\\\
                end \\\
                groupTracker.moveGroupToLimboForTracker(group, aTracker)\\\
            end\\\
        end\\\
    end \\\
    \\\
    -- then, remove it from the pool\\\
    local pooledGroup = cfxGroundTroops.getGroundTroopsForGroup(group)\\\
    if pooledGroup then \\\
        -- copy some important info from the troops \\\
        -- if they are set \\\
        conf.troopsOnBoard.orders = pooledGroup.orders\\\
        conf.troopsOnBoard.range = pooledGroup.range\\\
        conf.troopsOnBoard.destination = pooledGroup.destination -- may be nil \\\
        if pooledGroup.orders and pooledGroup.orders == \\\"captureandhold\\\" then \\\
            conf.troopsOnBoard.destination = nil -- forget last destination so they can be helo-redeployed\\\
        end \\\
        cfxGroundTroops.removeTroopsFromPool(pooledGroup)\\\
        trigger.action.outTextForGroup(conf.id, \\\"Team '\\\".. conf.troopsOnBoard.name ..\\\"' loaded and has orders <\\\" .. conf.troopsOnBoard.orders .. \\\">\\\", 30)\\\
        --trigger.action.outSoundForGroup(conf.id, cfxHeloTroops.actionSound) --  \\\"Quest Snare 3.wav\\\")\\\
    else \\\
        if cfxHeloTroops.verbose then \\\
            trigger.action.outText(\\\"+++heloT: \\\".. conf.troopsOnBoard.name ..\\\" was not committed to ground troops\\\", 30)\\\
        end\\\
    end\\\
        \\\
    -- now simply destroy the group\\\
    -- we'll re-assemble it when we deploy it \\\
    -- TODO: add weight changing code \\\
    -- TODO: ensure compatibility with CSAR module\\\
    group:destroy()\\\
    \\\
    -- now immediately run a GC so this group is removed \\\
    -- from any save data\\\
    cfxHeloTroops.GC()\\\
    \\\
    -- say so \\\
    trigger.action.outTextForGroup(conf.id, \\\"Team '\\\".. conf.troopsOnBoard.name ..\\\"' aboard, ready to go!\\\", 30)\\\
    trigger.action.outSoundForGroup(conf.id, cfxHeloTroops.actionSound) --  \\\"Quest Snare 3.wav\\\")\\\
\\\
    -- reset menu \\\
    cfxHeloTroops.removeComms(conf.unit)\\\
    cfxHeloTroops.setCommsMenu(conf.unit)\\\
end\\\
\\\
--\\\
-- spawning troops \\\
--\\\
function cfxHeloTroops.redirectSpawnGroup(args)\\\
    timer.scheduleFunction(cfxHeloTroops.doSpawnGroup, args, timer.getTime() + 0.1)\\\
end\\\
\\\
function cfxHeloTroops.delayedCommsResetForUnit(args)\\\
    local theUnit = args[1]\\\
    cfxHeloTroops.removeComms(theUnit)\\\
    cfxHeloTroops.setCommsMenu(theUnit)\\\
end\\\
\\\
function cfxHeloTroops.doSpawnGroup(args)\\\
    local conf = args[1]\\\
    local theSpawner = args[2]\\\
    -- NOTE: theSpawner can be of type cfxSpawnZone !!!OR!!! cfxCloneZones\\\
    -- make sure cooldown on spawner has timed out, else \\\
    -- notify that you have to wait \\\
    local now = timer.getTime()\\\
    if now < (theSpawner.lastSpawnTimeStamp + theSpawner.cooldown) then \\\
        local delta = math.floor(theSpawner.lastSpawnTimeStamp + theSpawner.cooldown - now)\\\
        trigger.action.outTextForGroup(conf.id, \\\"Still redeploying (\\\" .. delta .. \\\" seconds left)\\\", 30)\\\
        return \\\
    end\\\
    \\\
    --cfxSpawnZones.spawnWithSpawner(theSpawner) -- old code \\\
    theSpawner.spawnWithSpawner(theSpawner) -- can be both spawner and cloner \\\
    trigger.action.outTextForGroup(conf.id, \\\"Deploying <\\\" .. theSpawner.baseName .. \\\"> now...\\\", 30)\\\
    \\\
    -- reset all comms so we can include new troops \\\
    -- into load menu \\\
    timer.scheduleFunction(cfxHeloTroops.delayedCommsResetForUnit, {conf.unit, \\\"ignore\\\"}, now + 1.0)\\\
end\\\
\\\
-- \\\
-- handle events \\\
-- \\\
function cfxHeloTroops:onEvent(theEvent)\\\
    local theID = theEvent.id\\\
    local initiator = theEvent.initiator \\\
    if not initiator then return end -- not interested \\\
    local theUnit = initiator \\\
    local name = theUnit:getName() \\\
    -- see if this is a player aircraft \\\
    if not theUnit.getPlayerName then return end -- not a player \\\
    if not theUnit:getPlayerName() then return end -- not a player \\\
    \\\
    -- only for helicopters -- overridedden by troop carriers\\\
    -- we don't check for cat any more, so any airframe \\\
    -- can be used as long as it's ok with isTroopCarrier()\\\
    \\\
    -- only for troop carriers\\\
    if not dcsCommon.isTroopCarrier(theUnit, cfxHeloTroops.troopCarriers) then \\\
        return \\\
    end\\\
    \\\
    if theID == 4 then -- land \\\
        cfxHeloTroops.heloLanded(theUnit)\\\
    end\\\
    \\\
    if theID == 3 then -- take off \\\
        cfxHeloTroops.heloDeparted(theUnit)\\\
    end\\\
    \\\
    if theID == 5 then -- crash\\\
        cfxHeloTroops.heloCrashed(theUnit)\\\
    end\\\
    \\\
    if theID == 20 or  -- player enter \\\
       theID == 15 then -- birth\\\
       cfxHeloTroops.cleanHelo(theUnit)\\\
    end\\\
    \\\
    if theID == 21 then -- player leave \\\
        cfxHeloTroops.cleanHelo(theUnit)\\\
        local conf = cfxHeloTroops.getConfigForUnitNamed(name)\\\
        if conf then \\\
            cfxHeloTroops.removeCommsFromConfig(conf)\\\
        end\\\
        return \\\
    end\\\
\\\
    cfxHeloTroops.setCommsMenu(theUnit)    \\\
end\\\
\\\
--\\\
-- Regular GC and housekeeping\\\
--\\\
function cfxHeloTroops.GC()\\\
    -- GC run. remove all my dead remembered troops\\\
    local filteredAttackers = {}\\\
    local before = #cfxHeloTroops.deployedTroops\\\
    for gName, gData in pairs (cfxHeloTroops.deployedTroops) do \\\
        -- all we need to do is get the group of that name\\\
        -- and if it still returns units we are fine \\\
        local gameGroup = Group.getByName(gName)\\\
        if gameGroup and gameGroup:isExist() and gameGroup:getSize() > 0 then \\\
            filteredAttackers[gName] = gData\\\
        end\\\
    end\\\
    cfxHeloTroops.deployedTroops = filteredAttackers\\\
\\\
    if cfxHeloTroops.verbose then \\\
        trigger.action.outText(\\\"helo troops GC ran: before <\\\" .. before .. \\\">, after <\\\" .. #cfxHeloTroops.deployedTroops .. \\\">\\\", 30)\\\
    end \\\
end\\\
\\\
function cfxHeloTroops.houseKeeping()\\\
    timer.scheduleFunction(cfxHeloTroops.houseKeeping, {}, timer.getTime() + 5 * 60) -- every 5 minutes \\\
    cfxHeloTroops.GC()\\\
end\\\
\\\
--\\\
-- read config zone\\\
--\\\
function cfxHeloTroops.readConfigZone()\\\
    -- note: must match exactly!!!!\\\
    local theZone = cfxZones.getZoneByName(\\\"heloTroopsConfig\\\") \\\
    if not theZone then \\\
        theZone = cfxZones.createSimpleZone(\\\"heloTroopsConfig\\\")\\\
    end \\\
\\\
    cfxHeloTroops.verbose = theZone:getBoolFromZoneProperty(\\\"verbose\\\", false)\\\
    \\\
    if theZone:hasProperty(\\\"legalTroops\\\") then \\\
        local theTypesString = theZone:getStringFromZoneProperty(\\\"legalTroops\\\", \\\"\\\")\\\
        local unitTypes = dcsCommon.splitString(theTypesString, \\\",\\\")\\\
        if #unitTypes < 1 then \\\
            unitTypes = {\\\"Soldier AK\\\", \\\"Infantry AK\\\", \\\"Infantry AK ver2\\\", \\\"Infantry AK ver3\\\", \\\"Infantry AK Ins\\\", \\\"Soldier M249\\\", \\\"Soldier M4 GRG\\\", \\\"Soldier M4\\\", \\\"Soldier RPG\\\", \\\"Paratrooper AKS-74\\\", \\\"Paratrooper RPG-16\\\", \\\"Stinger comm dsr\\\", \\\"Stinger comm\\\", \\\"Soldier stinger\\\", \\\"SA-18 Igla-S comm\\\", \\\"SA-18 Igla-S manpad\\\", \\\"Igla manpad INS\\\", \\\"SA-18 Igla comm\\\", \\\"SA-18 Igla manpad\\\",} -- default \\\
        else\\\
            unitTypes = dcsCommon.trimArray(unitTypes)\\\
        end\\\
        cfxHeloTroops.legalTroops = unitTypes\\\
    end    \\\
    \\\
    cfxHeloTroops.troopWeight = theZone:getNumberFromZoneProperty(\\\"troopWeight\\\", 100) -- kg average weight per trooper \\\
    \\\
    cfxHeloTroops.autoDrop = theZone:getBoolFromZoneProperty(\\\"autoDrop\\\", false)    \\\
    cfxHeloTroops.autoPickup = theZone:getBoolFromZoneProperty(\\\"autoPickup\\\", false)\\\
    cfxHeloTroops.pickupRange = theZone:getNumberFromZoneProperty(\\\"pickupRange\\\", 100)\\\
    cfxHeloTroops.combatDropScore = theZone:getNumberFromZoneProperty( \\\"combatDropScore\\\", 200)\\\
    \\\
    cfxHeloTroops.actionSound = theZone:getStringFromZoneProperty(\\\"actionSound\\\", \\\"Quest Snare 3.wav\\\")\\\
    \\\
    cfxHeloTroops.requestRange = theZone:getNumberFromZoneProperty(\\\"requestRange\\\", 500)\\\
    -- add own troop carriers \\\
    if theZone:hasProperty(\\\"troopCarriers\\\") then \\\
        local tc = theZone:getStringFromZoneProperty(\\\"troopCarriers\\\", \\\"UH-1D\\\")\\\
        tc = dcsCommon.splitString(tc, \\\",\\\")\\\
        cfxHeloTroops.troopCarriers = dcsCommon.trimArray(tc)\\\
    end\\\
end\\\
\\\
--\\\
-- Load / Save data \\\
--\\\
function cfxHeloTroops.saveData()\\\
    local theData = {}\\\
    local allTroopData = {}\\\
    -- run a GC pre-emptively \\\
    cfxHeloTroops.GC()\\\
    -- now simply iterate and save all deployed troops \\\
    for gName, gData in pairs(cfxHeloTroops.deployedTroops) do \\\
        local sData = dcsCommon.clone(gData)\\\
        dcsCommon.synchGroupData(sData.groupData)\\\
        allTroopData[gName] = sData\\\
    end\\\
    theData.troops = allTroopData\\\
    return theData\\\
end\\\
\\\
function cfxHeloTroops.loadData()\\\
    if not persistence then return end \\\
    local theData = persistence.getSavedDataForModule(\\\"cfxHeloTroops\\\")\\\
    if not theData then \\\
        if cfxHeloTroops.verbose then \\\
            trigger.action.outText(\\\"+++heloT: no save date received, skipping.\\\", 30)\\\
        end\\\
        return\\\
    end\\\
    \\\
    -- simply spawn all troops that we have carried around and \\\
    -- were still alive when we saved. Troops that were picked \\\
    -- up by helos never made it to the save file \\\
    local allTroopData = theData.troops\\\
    for gName, gdTroop in pairs (allTroopData) do \\\
        local gData = gdTroop.groupData \\\
        local orders = gdTroop.orders \\\
        local side = gdTroop.side \\\
        local range = gdTroop.range\\\
        local cty = gData.cty \\\
        local cat = gData.cat  \\\
        \\\
        -- now spawn, but first \\\
        -- add to my own deployed queue so we can save later \\\
        local gdClone = dcsCommon.clone(gdTroop)\\\
        cfxHeloTroops.deployedTroops[gName] = gdClone \\\
        local theGroup = coalition.addGroup(cty, cat, gData)\\\
        -- post-proccing for cfxGroundTroops\\\
\\\
        -- add to groundTroops \\\
        local newTroops = cfxGroundTroops.createGroundTroops(theGroup, range, orders) \\\
        cfxGroundTroops.addGroundTroopsToPool(newTroops)\\\
    end\\\
end\\\
\\\
\\\
--\\\
-- Start \\\
--\\\
function cfxHeloTroops.start()\\\
    -- check libs\\\
    if not dcsCommon.libCheck(\\\"cfx Helo Troops\\\", \\\
        cfxHeloTroops.requiredLibs) then\\\
        return false \\\
    end\\\
    \\\
    -- read config zone\\\
    cfxHeloTroops.readConfigZone()\\\
    \\\
    -- start housekeeping \\\
    cfxHeloTroops.houseKeeping()\\\
    \\\
    world.addEventHandler(cfxHeloTroops)\\\
    trigger.action.outText(\\\"cf/x Helo Troops v\\\" .. cfxHeloTroops.version .. \\\" started\\\", 30)\\\
    \\\
    -- persistence:\\\
    -- load all save data and populate map with troops that\\\
    -- we deployed when we last saved. \\\
    if persistence then \\\
        -- sign up for persistence \\\
        callbacks = {}\\\
        callbacks.persistData = cfxHeloTroops.saveData\\\
        persistence.registerModule(\\\"cfxHeloTroops\\\", callbacks)\\\
        -- now load my data \\\
        cfxHeloTroops.loadData()\\\
    end\\\
    \\\
    return true \\\
end\\\
\\\
-- let's get rolling\\\
if not cfxHeloTroops.start() then \\\
    trigger.action.outText(\\\"cf/x Helo Troops aborted: missing libraries\\\", 30)\\\
    cfxHeloTroops = nil \\\
end\\\
\\\
\\\
-- TODO: weight when loading troops \");",
        }, -- end of ["actions"]
        ["events"] = 
        {
        }, -- end of ["events"]
        ["custom"] = 
        {
        }, -- end of ["custom"]
        ["func"] = 
        {
        }, -- end of ["func"]
        ["flag"] = 
        {
            [1] = true,
        }, -- end of ["flag"]
        ["conditions"] = 
        {
            [1] = "return(true)",
        }, -- end of ["conditions"]
        ["customStartup"] = 
        {
        }, -- end of ["customStartup"]
        ["funcStartup"] = 
        {
            [1] = "if mission.trig.conditions[1]() then mission.trig.actions[1]() end",
        }, -- end of ["funcStartup"]
    }, -- end of ["trig"]
    ["pictureFileNameN"] = 
    {
    }, -- end of ["pictureFileNameN"]
    ["groundControl"] = 
    {
        ["passwords"] = 
        {
            ["artillery_commander"] = 
            {
            }, -- end of ["artillery_commander"]
            ["instructor"] = 
            {
            }, -- end of ["instructor"]
            ["observer"] = 
            {
            }, -- end of ["observer"]
            ["forward_observer"] = 
            {
            }, -- end of ["forward_observer"]
        }, -- end of ["passwords"]
        ["roles"] = 
        {
            ["artillery_commander"] = 
            {
                ["neutrals"] = 0,
                ["blue"] = 0,
                ["red"] = 0,
            }, -- end of ["artillery_commander"]
            ["instructor"] = 
            {
                ["neutrals"] = 0,
                ["blue"] = 0,
                ["red"] = 0,
            }, -- end of ["instructor"]
            ["observer"] = 
            {
                ["neutrals"] = 0,
                ["blue"] = 0,
                ["red"] = 0,
            }, -- end of ["observer"]
            ["forward_observer"] = 
            {
                ["neutrals"] = 0,
                ["blue"] = 0,
                ["red"] = 0,
            }, -- end of ["forward_observer"]
        }, -- end of ["roles"]
        ["isPilotControlVehicles"] = false,
    }, -- end of ["groundControl"]
    ["descriptionBlueTask"] = "DictKey_descriptionBlueTask_3",
    ["weather"] = 
    {
        ["atmosphere_type"] = 0,
        ["wind"] = 
        {
            ["at8000"] = 
            {
                ["speed"] = 0,
                ["dir"] = 0,
            }, -- end of ["at8000"]
            ["at2000"] = 
            {
                ["speed"] = 0,
                ["dir"] = 0,
            }, -- end of ["at2000"]
            ["atGround"] = 
            {
                ["speed"] = 0,
                ["dir"] = 0,
            }, -- end of ["atGround"]
        }, -- end of ["wind"]
        ["enable_fog"] = false,
        ["groundTurbulence"] = 0,
        ["halo"] = 
        {
            ["preset"] = "off",
        }, -- end of ["halo"]
        ["enable_dust"] = false,
        ["season"] = 
        {
            ["temperature"] = 20,
        }, -- end of ["season"]
        ["type_weather"] = 0,
        ["modifiedTime"] = false,
        ["cyclones"] = 
        {
        }, -- end of ["cyclones"]
        ["name"] = "Winter, clean sky",
        ["fog"] = 
        {
            ["thickness"] = 0,
            ["visibility"] = 0,
        }, -- end of ["fog"]
        ["dust_density"] = 0,
        ["qnh"] = 760,
        ["visibility"] = 
        {
            ["distance"] = 80000,
        }, -- end of ["visibility"]
        ["clouds"] = 
        {
            ["thickness"] = 200,
            ["density"] = 0,
            ["preset"] = "Preset2",
            ["base"] = 2500,
            ["iprecptns"] = 0,
        }, -- end of ["clouds"]
    }, -- end of ["weather"]
    ["theatre"] = "Caucasus",
    ["triggers"] = 
    {
        ["zones"] = 
        {
            [1] = 
            {
                ["radius"] = 152.4,
                ["zoneId"] = 165,
                ["color"] = 
                {
                    [1] = 1,
                    [2] = 1,
                    [3] = 0.50196078431373,
                    [4] = 0.14901960784314,
                }, -- end of ["color"]
                ["properties"] = 
                {
                    [1] = 
                    {
                        ["key"] = "verbose",
                        ["value"] = "yes",
                    }, -- end of [1]
                    [2] = 
                    {
                        ["key"] = "troopCarriers",
                        ["value"] = "UH-1H, SA342Minigun",
                    }, -- end of [2]
                }, -- end of ["properties"]
                ["hidden"] = false,
                ["y"] = 646332.15416854,
                ["x"] = -281340.57372146,
                ["name"] = "heloTroopsConfig",
                ["type"] = 0,
            }, -- end of [1]
            [2] = 
            {
                ["radius"] = 5,
                ["zoneId"] = 330,
                ["color"] = 
                {
                    [1] = 1,
                    [2] = 1,
                    [3] = 1,
                    [4] = 0.15,
                }, -- end of ["color"]
                ["properties"] = 
                {
                    [1] = 
                    {
                        ["key"] = "spawner",
                        ["value"] = "legal spawner, all types are OK",
                    }, -- end of [1]
                    [2] = 
                    {
                        ["key"] = "types",
                        ["value"] = "Soldier M4, Soldier M4, Soldier M4",
                    }, -- end of [2]
                    [3] = 
                    {
                        ["key"] = "country",
                        ["value"] = "0",
                    }, -- end of [3]
                    [4] = 
                    {
                        ["key"] = "baseName",
                        ["value"] = "Legal Team Six",
                    }, -- end of [4]
                    [5] = 
                    {
                        ["key"] = "requestable",
                        ["value"] = "yes",
                    }, -- end of [5]
                }, -- end of ["properties"]
                ["hidden"] = false,
                ["y"] = 646632.20910684,
                ["x"] = -281431.10142485,
                ["name"] = "Legal Requestable Team",
                ["type"] = 0,
            }, -- end of [2]
            [3] = 
            {
                ["radius"] = 5,
                ["zoneId"] = 495,
                ["color"] = 
                {
                    [1] = 1,
                    [2] = 1,
                    [3] = 1,
                    [4] = 0.15,
                }, -- end of ["color"]
                ["properties"] = 
                {
                    [1] = 
                    {
                        ["key"] = "spawner",
                        ["value"] = "Hummer cant be picked up",
                    }, -- end of [1]
                    [2] = 
                    {
                        ["key"] = "types",
                        ["value"] = "Soldier M4, Soldier M4, Hummer, Soldier M4",
                    }, -- end of [2]
                    [3] = 
                    {
                        ["key"] = "country",
                        ["value"] = "0",
                    }, -- end of [3]
                    [4] = 
                    {
                        ["key"] = "baseName",
                        ["value"] = "Illegal Team",
                    }, -- end of [4]
                    [5] = 
                    {
                        ["key"] = "requestable",
                        ["value"] = "no",
                    }, -- end of [5]
                }, -- end of ["properties"]
                ["hidden"] = false,
                ["y"] = 646589.16996161,
                ["x"] = -281442.62048265,
                ["name"] = "Cant touch this",
                ["type"] = 0,
            }, -- end of [3]
        }, -- end of ["zones"]
    }, -- end of ["triggers"]
    ["map"] = 
    {
        ["centerY"] = 630396.85507254,
        ["zoom"] = 86613.651427386,
        ["centerX"] = -285171.57702751,
    }, -- end of ["map"]
    ["coalitions"] = 
    {
        ["neutrals"] = 
        {
            [1] = 70,
            [2] = 83,
            [3] = 23,
            [4] = 65,
            [5] = 86,
            [6] = 64,
            [7] = 25,
            [8] = 63,
            [9] = 76,
            [10] = 84,
            [11] = 29,
            [12] = 62,
            [13] = 30,
            [14] = 78,
            [15] = 87,
            [16] = 31,
            [17] = 61,
            [18] = 32,
            [19] = 33,
            [20] = 60,
            [21] = 17,
            [22] = 35,
            [23] = 69,
            [24] = 36,
            [25] = 59,
            [26] = 71,
            [27] = 79,
            [28] = 58,
            [29] = 57,
            [30] = 56,
            [31] = 55,
            [32] = 88,
            [33] = 73,
            [34] = 39,
            [35] = 89,
            [36] = 54,
            [37] = 77,
            [38] = 72,
            [39] = 41,
            [40] = 42,
            [41] = 44,
            [42] = 85,
            [43] = 75,
            [44] = 53,
            [45] = 22,
            [46] = 52,
            [47] = 66,
            [48] = 51,
            [49] = 74,
            [50] = 82,
            [51] = 7,
            [52] = 68,
            [53] = 50,
            [54] = 49,
            [55] = 48,
            [56] = 67,
            [57] = 90,
        }, -- end of ["neutrals"]
        ["blue"] = 
        {
            [1] = 21,
            [2] = 11,
            [3] = 8,
            [4] = 80,
            [5] = 28,
            [6] = 26,
            [7] = 13,
            [8] = 5,
            [9] = 16,
            [10] = 6,
            [11] = 15,
            [12] = 20,
            [13] = 12,
            [14] = 40,
            [15] = 45,
            [16] = 9,
            [17] = 46,
            [18] = 10,
            [19] = 3,
            [20] = 4,
            [21] = 1,
            [22] = 2,
        }, -- end of ["blue"]
        ["red"] = 
        {
            [1] = 18,
            [2] = 24,
            [3] = 27,
            [4] = 81,
            [5] = 34,
            [6] = 37,
            [7] = 38,
            [8] = 0,
            [9] = 43,
            [10] = 19,
            [11] = 47,
        }, -- end of ["red"]
    }, -- end of ["coalitions"]
    ["descriptionText"] = "DictKey_descriptionText_1",
    ["pictureFileNameR"] = 
    {
    }, -- end of ["pictureFileNameR"]
    ["goals"] = 
    {
    }, -- end of ["goals"]
    ["descriptionNeutralsTask"] = "DictKey_descriptionNeutralsTask_4",
    ["descriptionRedTask"] = "DictKey_descriptionRedTask_2",
    ["pictureFileNameB"] = 
    {
    }, -- end of ["pictureFileNameB"]
    ["coalition"] = 
    {
        ["neutrals"] = 
        {
            ["bullseye"] = 
            {
                ["y"] = 0,
                ["x"] = 0,
            }, -- end of ["bullseye"]
            ["nav_points"] = 
            {
            }, -- end of ["nav_points"]
            ["name"] = "neutrals",
            ["country"] = 
            {
            }, -- end of ["country"]
        }, -- end of ["neutrals"]
        ["blue"] = 
        {
            ["bullseye"] = 
            {
                ["y"] = 617414,
                ["x"] = -291014,
            }, -- end of ["bullseye"]
            ["nav_points"] = 
            {
            }, -- end of ["nav_points"]
            ["name"] = "blue",
            ["country"] = 
            {
                [1] = 
                {
                    ["id"] = 2,
                    ["vehicle"] = 
                    {
                        ["group"] = 
                        {
                            [1] = 
                            {
                                ["visible"] = false,
                                ["tasks"] = 
                                {
                                }, -- end of ["tasks"]
                                ["uncontrollable"] = false,
                                ["task"] = "Ground Nothing",
                                ["taskSelected"] = true,
                                ["route"] = 
                                {
                                    ["spans"] = 
                                    {
                                    }, -- end of ["spans"]
                                    ["points"] = 
                                    {
                                        [1] = 
                                        {
                                            ["alt"] = 13,
                                            ["type"] = "Turning Point",
                                            ["ETA"] = 0,
                                            ["alt_type"] = "BARO",
                                            ["formation_template"] = "",
                                            ["y"] = 648407.77608277,
                                            ["x"] = -281876.50942694,
                                            ["ETA_locked"] = true,
                                            ["speed"] = 0,
                                            ["action"] = "Off Road",
                                            ["task"] = 
                                            {
                                                ["id"] = "ComboTask",
                                                ["params"] = 
                                                {
                                                    ["tasks"] = 
                                                    {
                                                    }, -- end of ["tasks"]
                                                }, -- end of ["params"]
                                            }, -- end of ["task"]
                                            ["speed_locked"] = true,
                                        }, -- end of [1]
                                    }, -- end of ["points"]
                                }, -- end of ["route"]
                                ["groupId"] = 9,
                                ["hidden"] = false,
                                ["units"] = 
                                {
                                    [1] = 
                                    {
                                        ["skill"] = "Average",
                                        ["coldAtStart"] = false,
                                        ["type"] = "M978 HEMTT Tanker",
                                        ["unitId"] = 26,
                                        ["y"] = 648407.77608277,
                                        ["x"] = -281876.50942694,
                                        ["name"] = "Ground-1-1",
                                        ["heading"] = 0,
                                        ["playerCanDrive"] = false,
                                    }, -- end of [1]
                                }, -- end of ["units"]
                                ["y"] = 648407.77608277,
                                ["x"] = -281876.50942694,
                                ["name"] = "Kill me",
                                ["start_time"] = 0,
                            }, -- end of [1]
                        }, -- end of ["group"]
                    }, -- end of ["vehicle"]
                    ["name"] = "USA",
                }, -- end of [1]
            }, -- end of ["country"]
        }, -- end of ["blue"]
        ["red"] = 
        {
            ["bullseye"] = 
            {
                ["y"] = 371700,
                ["x"] = 11557,
            }, -- end of ["bullseye"]
            ["nav_points"] = 
            {
            }, -- end of ["nav_points"]
            ["name"] = "red",
            ["country"] = 
            {
                [1] = 
                {
                    ["helicopter"] = 
                    {
                        ["group"] = 
                        {
                            [1] = 
                            {
                                ["modulation"] = 0,
                                ["tasks"] = 
                                {
                                }, -- end of ["tasks"]
                                ["radioSet"] = false,
                                ["task"] = "Transport",
                                ["uncontrolled"] = false,
                                ["route"] = 
                                {
                                    ["points"] = 
                                    {
                                        [1] = 
                                        {
                                            ["alt"] = 500,
                                            ["action"] = "From Ground Area Hot",
                                            ["alt_type"] = "BARO",
                                            ["properties"] = 
                                            {
                                                ["addopt"] = 
                                                {
                                                }, -- end of ["addopt"]
                                            }, -- end of ["properties"]
                                            ["speed"] = 44.444444444444,
                                            ["task"] = 
                                            {
                                                ["id"] = "ComboTask",
                                                ["params"] = 
                                                {
                                                    ["tasks"] = 
                                                    {
                                                    }, -- end of ["tasks"]
                                                }, -- end of ["params"]
                                            }, -- end of ["task"]
                                            ["type"] = "TakeOffGroundHot",
                                            ["ETA"] = 0,
                                            ["ETA_locked"] = true,
                                            ["y"] = 646608.49245426,
                                            ["x"] = -281447.86276107,
                                            ["speed_locked"] = true,
                                            ["formation_template"] = "",
                                        }, -- end of [1]
                                    }, -- end of ["points"]
                                }, -- end of ["route"]
                                ["groupId"] = 2,
                                ["hidden"] = false,
                                ["units"] = 
                                {
                                    [1] = 
                                    {
                                        ["alt"] = 500,
                                        ["hardpoint_racks"] = true,
                                        ["alt_type"] = "BARO",
                                        ["livery_id"] = "Italy 15B Stormo S.A.R -Soccorso",
                                        ["skill"] = "Client",
                                        ["ropeLength"] = 15,
                                        ["speed"] = 44.444444444444,
                                        ["AddPropAircraft"] = 
                                        {
                                            ["SoloFlight"] = false,
                                            ["ExhaustScreen"] = true,
                                            ["GunnersAISkill"] = 90,
                                            ["NetCrewControlPriority"] = 0,
                                            ["EngineResource"] = 90,
                                        }, -- end of ["AddPropAircraft"]
                                        ["type"] = "UH-1H",
                                        ["Radio"] = 
                                        {
                                            [1] = 
                                            {
                                                ["channels"] = 
                                                {
                                                    [1] = 251,
                                                    [2] = 264,
                                                    [4] = 256,
                                                    [8] = 257,
                                                    [16] = 261,
                                                    [17] = 267,
                                                    [9] = 255,
                                                    [18] = 251,
                                                    [5] = 254,
                                                    [10] = 262,
                                                    [20] = 266,
                                                    [11] = 259,
                                                    [3] = 265,
                                                    [6] = 250,
                                                    [12] = 268,
                                                    [13] = 269,
                                                    [7] = 270,
                                                    [14] = 260,
                                                    [19] = 253,
                                                    [15] = 263,
                                                }, -- end of ["channels"]
                                                ["modulations"] = 
                                                {
                                                }, -- end of ["modulations"]
                                                ["channelsNames"] = 
                                                {
                                                }, -- end of ["channelsNames"]
                                            }, -- end of [1]
                                        }, -- end of ["Radio"]
                                        ["unitId"] = 2,
                                        ["psi"] = -0,
                                        ["onboard_num"] = "011",
                                        ["y"] = 646608.49245426,
                                        ["x"] = -281447.86276107,
                                        ["name"] = "Hooi",
                                        ["payload"] = 
                                        {
                                            ["pylons"] = 
                                            {
                                            }, -- end of ["pylons"]
                                            ["fuel"] = "631",
                                            ["flare"] = 60,
                                            ["chaff"] = 0,
                                            ["gun"] = 100,
                                        }, -- end of ["payload"]
                                        ["heading"] = 0,
                                        ["callsign"] = 
                                        {
                                            [1] = 2,
                                            [2] = 1,
                                            ["name"] = "Springfield11",
                                            [3] = 1,
                                        }, -- end of ["callsign"]
                                    }, -- end of [1]
                                }, -- end of ["units"]
                                ["y"] = 646608.49245426,
                                ["x"] = -281447.86276107,
                                ["name"] = "Hooi",
                                ["communication"] = true,
                                ["start_time"] = 0,
                                ["uncontrollable"] = false,
                                ["frequency"] = 251,
                            }, -- end of [1]
                            [2] = 
                            {
                                ["modulation"] = 0,
                                ["tasks"] = 
                                {
                                }, -- end of ["tasks"]
                                ["radioSet"] = false,
                                ["task"] = "Transport",
                                ["uncontrolled"] = false,
                                ["route"] = 
                                {
                                    ["points"] = 
                                    {
                                        [1] = 
                                        {
                                            ["alt"] = 500,
                                            ["action"] = "From Ground Area Hot",
                                            ["alt_type"] = "BARO",
                                            ["properties"] = 
                                            {
                                                ["addopt"] = 
                                                {
                                                }, -- end of ["addopt"]
                                            }, -- end of ["properties"]
                                            ["speed"] = 44.444444444444,
                                            ["task"] = 
                                            {
                                                ["id"] = "ComboTask",
                                                ["params"] = 
                                                {
                                                    ["tasks"] = 
                                                    {
                                                    }, -- end of ["tasks"]
                                                }, -- end of ["params"]
                                            }, -- end of ["task"]
                                            ["type"] = "TakeOffGroundHot",
                                            ["ETA"] = 0,
                                            ["ETA_locked"] = true,
                                            ["y"] = 646622.14628956,
                                            ["x"] = -281443.45639707,
                                            ["speed_locked"] = true,
                                            ["formation_template"] = "",
                                        }, -- end of [1]
                                    }, -- end of ["points"]
                                }, -- end of ["route"]
                                ["groupId"] = 6,
                                ["hidden"] = false,
                                ["units"] = 
                                {
                                    [1] = 
                                    {
                                        ["alt"] = 500,
                                        ["hardpoint_racks"] = true,
                                        ["alt_type"] = "BARO",
                                        ["livery_id"] = "BP_RS01",
                                        ["skill"] = "Client",
                                        ["ropeLength"] = 15,
                                        ["speed"] = 44.444444444444,
                                        ["AddPropAircraft"] = 
                                        {
                                            ["LeftEngineResource"] = 90,
                                            ["RightEngineResource"] = 90,
                                            ["CargoHalfdoor"] = true,
                                            ["NetCrewControlPriority"] = 1,
                                            ["ExhaustScreen"] = true,
                                            ["HumanOrchestra"] = false,
                                            ["GunnersAISkill"] = 90,
                                            ["AdditionalArmor"] = true,
                                            ["NS430allow"] = true,
                                        }, -- end of ["AddPropAircraft"]
                                        ["type"] = "Mi-8MT",
                                        ["Radio"] = 
                                        {
                                            [1] = 
                                            {
                                                ["channels"] = 
                                                {
                                                    [1] = 127.5,
                                                    [2] = 135,
                                                    [4] = 127,
                                                    [8] = 128,
                                                    [16] = 132,
                                                    [17] = 138,
                                                    [9] = 126,
                                                    [18] = 122,
                                                    [5] = 125,
                                                    [10] = 133,
                                                    [20] = 137,
                                                    [11] = 130,
                                                    [3] = 136,
                                                    [6] = 121,
                                                    [12] = 129,
                                                    [13] = 123,
                                                    [7] = 141,
                                                    [14] = 131,
                                                    [15] = 134,
                                                    [19] = 124,
                                                }, -- end of ["channels"]
                                                ["modulations"] = 
                                                {
                                                }, -- end of ["modulations"]
                                                ["channelsNames"] = 
                                                {
                                                }, -- end of ["channelsNames"]
                                            }, -- end of [1]
                                            [2] = 
                                            {
                                                ["channels"] = 
                                                {
                                                    [7] = 40,
                                                    [1] = 21.5,
                                                    [2] = 25.7,
                                                    [4] = 28,
                                                    [8] = 50,
                                                    [9] = 55.5,
                                                    [5] = 30,
                                                    [10] = 59.9,
                                                    [3] = 27,
                                                    [6] = 32,
                                                }, -- end of ["channels"]
                                                ["modulations"] = 
                                                {
                                                }, -- end of ["modulations"]
                                                ["channelsNames"] = 
                                                {
                                                }, -- end of ["channelsNames"]
                                            }, -- end of [2]
                                        }, -- end of ["Radio"]
                                        ["unitId"] = 23,
                                        ["psi"] = -0,
                                        ["onboard_num"] = "012",
                                        ["y"] = 646622.14628956,
                                        ["x"] = -281443.45639707,
                                        ["name"] = "Hippie One",
                                        ["payload"] = 
                                        {
                                            ["pylons"] = 
                                            {
                                            }, -- end of ["pylons"]
                                            ["fuel"] = "1929",
                                            ["flare"] = 128,
                                            ["chaff"] = 0,
                                            ["gun"] = 100,
                                        }, -- end of ["payload"]
                                        ["heading"] = 0,
                                        ["callsign"] = 
                                        {
                                            [1] = 3,
                                            [2] = 1,
                                            ["name"] = "Uzi11",
                                            [3] = 1,
                                        }, -- end of ["callsign"]
                                    }, -- end of [1]
                                }, -- end of ["units"]
                                ["y"] = 646622.14628956,
                                ["x"] = -281443.45639707,
                                ["name"] = "Hippie No Carry",
                                ["communication"] = true,
                                ["start_time"] = 0,
                                ["uncontrollable"] = false,
                                ["frequency"] = 127.5,
                            }, -- end of [2]
                            [3] = 
                            {
                                ["modulation"] = 0,
                                ["tasks"] = 
                                {
                                }, -- end of ["tasks"]
                                ["radioSet"] = false,
                                ["task"] = "CAS",
                                ["uncontrolled"] = false,
                                ["route"] = 
                                {
                                    ["points"] = 
                                    {
                                        [1] = 
                                        {
                                            ["alt"] = 500,
                                            ["action"] = "From Ground Area Hot",
                                            ["alt_type"] = "BARO",
                                            ["properties"] = 
                                            {
                                                ["addopt"] = 
                                                {
                                                }, -- end of ["addopt"]
                                            }, -- end of ["properties"]
                                            ["speed"] = 44.444444444444,
                                            ["task"] = 
                                            {
                                                ["id"] = "ComboTask",
                                                ["params"] = 
                                                {
                                                    ["tasks"] = 
                                                    {
                                                        [1] = 
                                                        {
                                                            ["enabled"] = true,
                                                            ["key"] = "CAS",
                                                            ["id"] = "EngageTargets",
                                                            ["number"] = 1,
                                                            ["auto"] = true,
                                                            ["params"] = 
                                                            {
                                                                ["targetTypes"] = 
                                                                {
                                                                    [1] = "Helicopters",
                                                                    [2] = "Ground Units",
                                                                    [3] = "Light armed ships",
                                                                }, -- end of ["targetTypes"]
                                                                ["priority"] = 0,
                                                            }, -- end of ["params"]
                                                        }, -- end of [1]
                                                    }, -- end of ["tasks"]
                                                }, -- end of ["params"]
                                            }, -- end of ["task"]
                                            ["type"] = "TakeOffGroundHot",
                                            ["ETA"] = 0,
                                            ["ETA_locked"] = true,
                                            ["y"] = 646612.92556062,
                                            ["x"] = -281473.71468779,
                                            ["speed_locked"] = true,
                                            ["formation_template"] = "",
                                        }, -- end of [1]
                                    }, -- end of ["points"]
                                }, -- end of ["route"]
                                ["groupId"] = 7,
                                ["hidden"] = false,
                                ["units"] = 
                                {
                                    [1] = 
                                    {
                                        ["alt"] = 500,
                                        ["hardpoint_racks"] = true,
                                        ["alt_type"] = "BARO",
                                        ["livery_id"] = "Georgian Air Force",
                                        ["skill"] = "Client",
                                        ["ropeLength"] = 15,
                                        ["speed"] = 44.444444444444,
                                        ["AddPropAircraft"] = 
                                        {
                                            ["LeftEngineResource"] = 90,
                                            ["RightEngineResource"] = 90,
                                            ["GunnersAISkill"] = 90,
                                            ["PilotNVG"] = true,
                                            ["NetCrewControlPriority"] = 0,
                                            ["OverrideIFF"] = 0,
                                            ["R60equipment"] = true,
                                            ["OperatorNVG"] = true,
                                            ["SimplifiedAI"] = false,
                                            ["ExhaustScreen"] = true,
                                            ["TrackAirTargets"] = true,
                                            ["HumanOrchestra"] = false,
                                            ["HideAngleBoxes"] = false,
                                            ["NS430allow"] = true,
                                        }, -- end of ["AddPropAircraft"]
                                        ["type"] = "Mi-24P",
                                        ["Radio"] = 
                                        {
                                            [1] = 
                                            {
                                                ["channels"] = 
                                                {
                                                    [1] = 127.5,
                                                    [2] = 135,
                                                    [4] = 127,
                                                    [8] = 128,
                                                    [16] = 132,
                                                    [17] = 138,
                                                    [9] = 126,
                                                    [18] = 122,
                                                    [5] = 125,
                                                    [10] = 133,
                                                    [20] = 137,
                                                    [11] = 130,
                                                    [3] = 136,
                                                    [6] = 121,
                                                    [12] = 129,
                                                    [13] = 123,
                                                    [7] = 141,
                                                    [14] = 131,
                                                    [15] = 134,
                                                    [19] = 124,
                                                }, -- end of ["channels"]
                                                ["modulations"] = 
                                                {
                                                }, -- end of ["modulations"]
                                                ["channelsNames"] = 
                                                {
                                                }, -- end of ["channelsNames"]
                                            }, -- end of [1]
                                            [2] = 
                                            {
                                                ["channels"] = 
                                                {
                                                    [7] = 40,
                                                    [1] = 21.5,
                                                    [2] = 25.7,
                                                    [4] = 28,
                                                    [8] = 50,
                                                    [9] = 55.5,
                                                    [5] = 30,
                                                    [10] = 59.9,
                                                    [3] = 27,
                                                    [6] = 32,
                                                }, -- end of ["channels"]
                                                ["modulations"] = 
                                                {
                                                }, -- end of ["modulations"]
                                                ["channelsNames"] = 
                                                {
                                                }, -- end of ["channelsNames"]
                                            }, -- end of [2]
                                        }, -- end of ["Radio"]
                                        ["unitId"] = 24,
                                        ["psi"] = -0,
                                        ["onboard_num"] = "013",
                                        ["y"] = 646612.92556062,
                                        ["x"] = -281473.71468779,
                                        ["name"] = "Hinder One",
                                        ["payload"] = 
                                        {
                                            ["pylons"] = 
                                            {
                                            }, -- end of ["pylons"]
                                            ["fuel"] = 1701,
                                            ["flare"] = 192,
                                            ["ammo_type"] = 1,
                                            ["chaff"] = 0,
                                            ["gun"] = 100,
                                            ["restricted"] = 
                                            {
                                            }, -- end of ["restricted"]
                                        }, -- end of ["payload"]
                                        ["heading"] = 0,
                                        ["callsign"] = 
                                        {
                                            [1] = 4,
                                            [2] = 1,
                                            ["name"] = "Colt11",
                                            [3] = 1,
                                        }, -- end of ["callsign"]
                                    }, -- end of [1]
                                }, -- end of ["units"]
                                ["y"] = 646612.92556062,
                                ["x"] = -281473.71468779,
                                ["name"] = "Hinder No Carry",
                                ["communication"] = true,
                                ["start_time"] = 0,
                                ["uncontrollable"] = false,
                                ["frequency"] = 127.5,
                            }, -- end of [3]
                            [4] = 
                            {
                                ["modulation"] = 0,
                                ["tasks"] = 
                                {
                                }, -- end of ["tasks"]
                                ["radioSet"] = false,
                                ["task"] = "CAS",
                                ["uncontrolled"] = false,
                                ["route"] = 
                                {
                                    ["points"] = 
                                    {
                                        [1] = 
                                        {
                                            ["alt"] = 500,
                                            ["action"] = "From Ground Area Hot",
                                            ["alt_type"] = "BARO",
                                            ["properties"] = 
                                            {
                                                ["addopt"] = 
                                                {
                                                }, -- end of ["addopt"]
                                            }, -- end of ["properties"]
                                            ["speed"] = 44.444444444444,
                                            ["task"] = 
                                            {
                                                ["id"] = "ComboTask",
                                                ["params"] = 
                                                {
                                                    ["tasks"] = 
                                                    {
                                                        [1] = 
                                                        {
                                                            ["enabled"] = true,
                                                            ["key"] = "CAS",
                                                            ["id"] = "EngageTargets",
                                                            ["number"] = 1,
                                                            ["auto"] = true,
                                                            ["params"] = 
                                                            {
                                                                ["targetTypes"] = 
                                                                {
                                                                    [1] = "Helicopters",
                                                                    [2] = "Ground Units",
                                                                    [3] = "Light armed ships",
                                                                }, -- end of ["targetTypes"]
                                                                ["priority"] = 0,
                                                            }, -- end of ["params"]
                                                        }, -- end of [1]
                                                    }, -- end of ["tasks"]
                                                }, -- end of ["params"]
                                            }, -- end of ["task"]
                                            ["type"] = "TakeOffGroundHot",
                                            ["ETA"] = 0,
                                            ["ETA_locked"] = true,
                                            ["y"] = 646615.22155077,
                                            ["x"] = -281396.69465442,
                                            ["speed_locked"] = true,
                                            ["formation_template"] = "",
                                        }, -- end of [1]
                                    }, -- end of ["points"]
                                }, -- end of ["route"]
                                ["groupId"] = 8,
                                ["hidden"] = false,
                                ["units"] = 
                                {
                                    [1] = 
                                    {
                                        ["alt"] = 500,
                                        ["hardpoint_racks"] = true,
                                        ["alt_type"] = "BARO",
                                        ["livery_id"] = "Italy Aeronautica Militare",
                                        ["skill"] = "Client",
                                        ["ropeLength"] = 15,
                                        ["speed"] = 44.444444444444,
                                        ["type"] = "Ka-50",
                                        ["Radio"] = 
                                        {
                                            [1] = 
                                            {
                                                ["channels"] = 
                                                {
                                                    [7] = 40,
                                                    [1] = 21.5,
                                                    [2] = 25.7,
                                                    [4] = 28,
                                                    [8] = 50,
                                                    [9] = 55.5,
                                                    [5] = 30,
                                                    [10] = 59.9,
                                                    [3] = 27,
                                                    [6] = 32,
                                                }, -- end of ["channels"]
                                                ["modulations"] = 
                                                {
                                                }, -- end of ["modulations"]
                                                ["channelsNames"] = 
                                                {
                                                }, -- end of ["channelsNames"]
                                            }, -- end of [1]
                                            [2] = 
                                            {
                                                ["channels"] = 
                                                {
                                                    [14] = 0.283,
                                                    [13] = 0.583,
                                                    [7] = 0.443,
                                                    [1] = 0.625,
                                                    [2] = 0.303,
                                                    [4] = 0.591,
                                                    [8] = 0.215,
                                                    [16] = 1.21,
                                                    [9] = 0.525,
                                                    [5] = 0.408,
                                                    [10] = 1.065,
                                                    [11] = 0.718,
                                                    [3] = 0.289,
                                                    [6] = 0.803,
                                                    [12] = 0.35,
                                                    [15] = 0.995,
                                                }, -- end of ["channels"]
                                                ["modulations"] = 
                                                {
                                                }, -- end of ["modulations"]
                                                ["channelsNames"] = 
                                                {
                                                }, -- end of ["channelsNames"]
                                            }, -- end of [2]
                                        }, -- end of ["Radio"]
                                        ["unitId"] = 25,
                                        ["psi"] = -3.2637657012294,
                                        ["onboard_num"] = "014",
                                        ["y"] = 646615.22155077,
                                        ["x"] = -281396.69465442,
                                        ["name"] = "Sharky",
                                        ["payload"] = 
                                        {
                                            ["pylons"] = 
                                            {
                                            }, -- end of ["pylons"]
                                            ["fuel"] = "1450",
                                            ["flare"] = 128,
                                            ["chaff"] = 0,
                                            ["gun"] = 100,
                                        }, -- end of ["payload"]
                                        ["heading"] = 3.2637657012294,
                                        ["callsign"] = 
                                        {
                                            [1] = 5,
                                            [2] = 1,
                                            ["name"] = "Dodge11",
                                            [3] = 1,
                                        }, -- end of ["callsign"]
                                    }, -- end of [1]
                                }, -- end of ["units"]
                                ["y"] = 646615.22155077,
                                ["x"] = -281396.69465442,
                                ["name"] = "Sharky No Carry",
                                ["communication"] = true,
                                ["start_time"] = 0,
                                ["uncontrollable"] = false,
                                ["frequency"] = 124,
                            }, -- end of [4]
                            [5] = 
                            {
                                ["modulation"] = 0,
                                ["tasks"] = 
                                {
                                }, -- end of ["tasks"]
                                ["radioSet"] = false,
                                ["task"] = "CAS",
                                ["uncontrolled"] = false,
                                ["route"] = 
                                {
                                    ["points"] = 
                                    {
                                        [1] = 
                                        {
                                            ["alt"] = 500,
                                            ["action"] = "From Ground Area Hot",
                                            ["alt_type"] = "BARO",
                                            ["properties"] = 
                                            {
                                                ["addopt"] = 
                                                {
                                                }, -- end of ["addopt"]
                                            }, -- end of ["properties"]
                                            ["speed"] = 46.25,
                                            ["task"] = 
                                            {
                                                ["id"] = "ComboTask",
                                                ["params"] = 
                                                {
                                                    ["tasks"] = 
                                                    {
                                                        [1] = 
                                                        {
                                                            ["number"] = 1,
                                                            ["key"] = "CAS",
                                                            ["id"] = "EngageTargets",
                                                            ["enabled"] = true,
                                                            ["auto"] = true,
                                                            ["params"] = 
                                                            {
                                                                ["targetTypes"] = 
                                                                {
                                                                    [1] = "Helicopters",
                                                                    [2] = "Ground Units",
                                                                    [3] = "Light armed ships",
                                                                }, -- end of ["targetTypes"]
                                                                ["priority"] = 0,
                                                            }, -- end of ["params"]
                                                        }, -- end of [1]
                                                        [2] = 
                                                        {
                                                            ["number"] = 2,
                                                            ["auto"] = true,
                                                            ["id"] = "WrappedAction",
                                                            ["enabled"] = true,
                                                            ["params"] = 
                                                            {
                                                                ["action"] = 
                                                                {
                                                                    ["id"] = "EPLRS",
                                                                    ["params"] = 
                                                                    {
                                                                        ["value"] = true,
                                                                        ["groupId"] = 1,
                                                                    }, -- end of ["params"]
                                                                }, -- end of ["action"]
                                                            }, -- end of ["params"]
                                                        }, -- end of [2]
                                                    }, -- end of ["tasks"]
                                                }, -- end of ["params"]
                                            }, -- end of ["task"]
                                            ["type"] = "TakeOffGroundHot",
                                            ["ETA"] = 0,
                                            ["ETA_locked"] = true,
                                            ["y"] = 646623.95750435,
                                            ["x"] = -281419.94797164,
                                            ["speed_locked"] = true,
                                            ["formation_template"] = "",
                                        }, -- end of [1]
                                    }, -- end of ["points"]
                                }, -- end of ["route"]
                                ["groupId"] = 16,
                                ["hidden"] = false,
                                ["units"] = 
                                {
                                    [1] = 
                                    {
                                        ["alt"] = 500,
                                        ["hardpoint_racks"] = true,
                                        ["alt_type"] = "BARO",
                                        ["livery_id"] = "combat sable",
                                        ["skill"] = "Client",
                                        ["speed"] = 46.25,
                                        ["AddPropAircraft"] = 
                                        {
                                            ["RemoveTablet"] = false,
                                            ["NS430allow"] = true,
                                        }, -- end of ["AddPropAircraft"]
                                        ["type"] = "SA342Minigun",
                                        ["Radio"] = 
                                        {
                                            [1] = 
                                            {
                                                ["channels"] = 
                                                {
                                                    [6] = 41,
                                                    [2] = 31,
                                                    [8] = 50,
                                                    [3] = 32,
                                                    [1] = 30,
                                                    [4] = 33,
                                                    [5] = 40,
                                                    [7] = 42,
                                                }, -- end of ["channels"]
                                                ["modulations"] = 
                                                {
                                                }, -- end of ["modulations"]
                                                ["channelsNames"] = 
                                                {
                                                }, -- end of ["channelsNames"]
                                            }, -- end of [1]
                                        }, -- end of ["Radio"]
                                        ["unitId"] = 37,
                                        ["psi"] = -3.2637657012294,
                                        ["onboard_num"] = "015",
                                        ["y"] = 646623.95750435,
                                        ["x"] = -281419.94797164,
                                        ["name"] = "Sharky-1-1",
                                        ["payload"] = 
                                        {
                                            ["pylons"] = 
                                            {
                                            }, -- end of ["pylons"]
                                            ["fuel"] = 416.33,
                                            ["flare"] = 32,
                                            ["chaff"] = 0,
                                            ["gun"] = 100,
                                        }, -- end of ["payload"]
                                        ["heading"] = 3.2637657012294,
                                        ["callsign"] = 
                                        {
                                            [1] = 6,
                                            [2] = 1,
                                            ["name"] = "Ford11",
                                            [3] = 1,
                                        }, -- end of ["callsign"]
                                    }, -- end of [1]
                                }, -- end of ["units"]
                                ["y"] = 646623.95750435,
                                ["x"] = -281419.94797164,
                                ["name"] = "Gazelle Can Carry",
                                ["communication"] = true,
                                ["start_time"] = 0,
                                ["uncontrollable"] = false,
                                ["frequency"] = 124,
                            }, -- end of [5]
                        }, -- end of ["group"]
                    }, -- end of ["helicopter"]
                    ["name"] = "CJTF Red",
                    ["ship"] = 
                    {
                        ["group"] = 
                        {
                            [1] = 
                            {
                                ["visible"] = false,
                                ["tasks"] = 
                                {
                                }, -- end of ["tasks"]
                                ["uncontrollable"] = false,
                                ["route"] = 
                                {
                                    ["points"] = 
                                    {
                                        [1] = 
                                        {
                                            ["alt"] = 0,
                                            ["type"] = "Turning Point",
                                            ["ETA"] = 0,
                                            ["alt_type"] = "BARO",
                                            ["formation_template"] = "",
                                            ["y"] = 613009.6642368,
                                            ["x"] = -288676.37620491,
                                            ["ETA_locked"] = true,
                                            ["speed"] = 0,
                                            ["action"] = "Turning Point",
                                            ["task"] = 
                                            {
                                                ["id"] = "ComboTask",
                                                ["params"] = 
                                                {
                                                    ["tasks"] = 
                                                    {
                                                    }, -- end of ["tasks"]
                                                }, -- end of ["params"]
                                            }, -- end of ["task"]
                                            ["speed_locked"] = true,
                                        }, -- end of [1]
                                    }, -- end of ["points"]
                                }, -- end of ["route"]
                                ["groupId"] = 11,
                                ["hidden"] = false,
                                ["units"] = 
                                {
                                    [1] = 
                                    {
                                        ["modulation"] = 0,
                                        ["skill"] = "Average",
                                        ["type"] = "Seawise_Giant",
                                        ["unitId"] = 28,
                                        ["y"] = 613009.6642368,
                                        ["x"] = -288676.37620491,
                                        ["name"] = "Naval-1-1",
                                        ["heading"] = 0,
                                        ["frequency"] = 127500000,
                                    }, -- end of [1]
                                }, -- end of ["units"]
                                ["y"] = 613009.6642368,
                                ["x"] = -288676.37620491,
                                ["name"] = "Naval-1",
                                ["start_time"] = 0,
                            }, -- end of [1]
                        }, -- end of ["group"]
                    }, -- end of ["ship"]
                    ["id"] = 81,
                    ["vehicle"] = 
                    {
                        ["group"] = 
                        {
                            [1] = 
                            {
                                ["visible"] = false,
                                ["tasks"] = 
                                {
                                }, -- end of ["tasks"]
                                ["uncontrollable"] = false,
                                ["task"] = "Ground Nothing",
                                ["taskSelected"] = true,
                                ["route"] = 
                                {
                                    ["spans"] = 
                                    {
                                        [1] = 
                                        {
                                            [1] = 
                                            {
                                                ["y"] = 646608.18247087,
                                                ["x"] = -281393.76252178,
                                            }, -- end of [1]
                                            [2] = 
                                            {
                                                ["y"] = 646608.01100419,
                                                ["x"] = -281413.41282643,
                                            }, -- end of [2]
                                        }, -- end of [1]
                                    }, -- end of ["spans"]
                                    ["points"] = 
                                    {
                                        [1] = 
                                        {
                                            ["alt"] = 13,
                                            ["type"] = "Turning Point",
                                            ["ETA"] = 0,
                                            ["alt_type"] = "BARO",
                                            ["formation_template"] = "",
                                            ["y"] = 646606.497606,
                                            ["x"] = -281425.52001201,
                                            ["ETA_locked"] = true,
                                            ["speed"] = 0,
                                            ["action"] = "Off Road",
                                            ["task"] = 
                                            {
                                                ["id"] = "ComboTask",
                                                ["params"] = 
                                                {
                                                    ["tasks"] = 
                                                    {
                                                    }, -- end of ["tasks"]
                                                }, -- end of ["params"]
                                            }, -- end of ["task"]
                                            ["speed_locked"] = true,
                                        }, -- end of [1]
                                    }, -- end of ["points"]
                                }, -- end of ["route"]
                                ["groupId"] = 3,
                                ["hidden"] = false,
                                ["units"] = 
                                {
                                    [1] = 
                                    {
                                        ["skill"] = "Average",
                                        ["coldAtStart"] = false,
                                        ["type"] = "Soldier AK",
                                        ["unitId"] = 5,
                                        ["y"] = 646606.497606,
                                        ["x"] = -281425.52001201,
                                        ["name"] = "Pick Me Up-3",
                                        ["heading"] = 3.1503183364552,
                                        ["playerCanDrive"] = false,
                                    }, -- end of [1]
                                    [2] = 
                                    {
                                        ["skill"] = "Average",
                                        ["coldAtStart"] = false,
                                        ["type"] = "Soldier AK",
                                        ["unitId"] = 4,
                                        ["y"] = 646600.14133356,
                                        ["x"] = -281420.37445814,
                                        ["name"] = "Pick Me Up-1",
                                        ["heading"] = 3.1503183364552,
                                        ["playerCanDrive"] = false,
                                    }, -- end of [2]
                                    [3] = 
                                    {
                                        ["skill"] = "Average",
                                        ["coldAtStart"] = false,
                                        ["type"] = "Soldier AK",
                                        ["unitId"] = 3,
                                        ["y"] = 646612.55119879,
                                        ["x"] = -281421.43383688,
                                        ["name"] = "Pick Me Up-2",
                                        ["heading"] = 3.1503183364552,
                                        ["playerCanDrive"] = false,
                                    }, -- end of [3]
                                }, -- end of ["units"]
                                ["y"] = 646606.497606,
                                ["x"] = -281425.52001201,
                                ["name"] = "Pick Me Up",
                                ["start_time"] = 0,
                            }, -- end of [1]
                            [2] = 
                            {
                                ["visible"] = false,
                                ["tasks"] = 
                                {
                                }, -- end of ["tasks"]
                                ["uncontrollable"] = false,
                                ["task"] = "Ground Nothing",
                                ["taskSelected"] = true,
                                ["route"] = 
                                {
                                    ["spans"] = 
                                    {
                                        [1] = 
                                        {
                                            [1] = 
                                            {
                                                ["y"] = 646608.18247087,
                                                ["x"] = -281393.76252178,
                                            }, -- end of [1]
                                            [2] = 
                                            {
                                                ["y"] = 646608.01100419,
                                                ["x"] = -281413.41282643,
                                            }, -- end of [2]
                                        }, -- end of [1]
                                    }, -- end of ["spans"]
                                    ["points"] = 
                                    {
                                        [1] = 
                                        {
                                            ["alt"] = 13,
                                            ["type"] = "Turning Point",
                                            ["ETA"] = 0,
                                            ["alt_type"] = "BARO",
                                            ["formation_template"] = "",
                                            ["y"] = 646595.06729515,
                                            ["x"] = -281456.02248979,
                                            ["ETA_locked"] = true,
                                            ["speed"] = 0,
                                            ["action"] = "Off Road",
                                            ["task"] = 
                                            {
                                                ["id"] = "ComboTask",
                                                ["params"] = 
                                                {
                                                    ["tasks"] = 
                                                    {
                                                    }, -- end of ["tasks"]
                                                }, -- end of ["params"]
                                            }, -- end of ["task"]
                                            ["speed_locked"] = true,
                                        }, -- end of [1]
                                    }, -- end of ["points"]
                                }, -- end of ["route"]
                                ["groupId"] = 4,
                                ["hidden"] = false,
                                ["units"] = 
                                {
                                    [1] = 
                                    {
                                        ["skill"] = "Average",
                                        ["coldAtStart"] = false,
                                        ["type"] = "Infantry AK",
                                        ["unitId"] = 10,
                                        ["y"] = 646595.06729515,
                                        ["x"] = -281456.02248979,
                                        ["name"] = "Pick Me Up Too-1",
                                        ["heading"] = 1.1868238913561,
                                        ["playerCanDrive"] = false,
                                    }, -- end of [1]
                                    [2] = 
                                    {
                                        ["skill"] = "Average",
                                        ["coldAtStart"] = false,
                                        ["type"] = "Infantry AK",
                                        ["unitId"] = 6,
                                        ["y"] = 646583.27262808,
                                        ["x"] = -281460.82772452,
                                        ["name"] = "Pick Me Up-2-1",
                                        ["heading"] = 1.1868238913561,
                                        ["playerCanDrive"] = false,
                                    }, -- end of [2]
                                    [3] = 
                                    {
                                        ["skill"] = "Average",
                                        ["coldAtStart"] = false,
                                        ["type"] = "Infantry AK Ins",
                                        ["unitId"] = 9,
                                        ["y"] = 646590.48048018,
                                        ["x"] = -281465.41453949,
                                        ["name"] = "Pick Me Up-2-4",
                                        ["heading"] = 1.3788101090755,
                                        ["playerCanDrive"] = false,
                                    }, -- end of [3]
                                    [4] = 
                                    {
                                        ["skill"] = "Average",
                                        ["coldAtStart"] = false,
                                        ["type"] = "Soldier AK",
                                        ["unitId"] = 7,
                                        ["y"] = 646586.76734425,
                                        ["x"] = -281453.61987242,
                                        ["name"] = "Pick Me Up-2-2",
                                        ["heading"] = 1.3788101090755,
                                        ["playerCanDrive"] = false,
                                    }, -- end of [4]
                                    [5] = 
                                    {
                                        ["skill"] = "Average",
                                        ["coldAtStart"] = false,
                                        ["type"] = "Infantry AK ver3",
                                        ["unitId"] = 8,
                                        ["y"] = 646597.46991252,
                                        ["x"] = -281461.91982333,
                                        ["name"] = "Pick Me Up-2-3",
                                        ["heading"] = 1.2915436464758,
                                        ["playerCanDrive"] = false,
                                    }, -- end of [5]
                                }, -- end of ["units"]
                                ["y"] = 646595.06729515,
                                ["x"] = -281456.02248979,
                                ["name"] = "Pick Me Up Too",
                                ["start_time"] = 0,
                            }, -- end of [2]
                            [3] = 
                            {
                                ["visible"] = false,
                                ["tasks"] = 
                                {
                                }, -- end of ["tasks"]
                                ["uncontrollable"] = false,
                                ["task"] = "Ground Nothing",
                                ["taskSelected"] = true,
                                ["route"] = 
                                {
                                    ["spans"] = 
                                    {
                                        [1] = 
                                        {
                                            [1] = 
                                            {
                                                ["y"] = 646608.18247087,
                                                ["x"] = -281393.76252178,
                                            }, -- end of [1]
                                            [2] = 
                                            {
                                                ["y"] = 646608.01100419,
                                                ["x"] = -281413.41282643,
                                            }, -- end of [2]
                                        }, -- end of [1]
                                    }, -- end of ["spans"]
                                    ["points"] = 
                                    {
                                        [1] = 
                                        {
                                            ["alt"] = 13,
                                            ["type"] = "Turning Point",
                                            ["ETA"] = 0,
                                            ["alt_type"] = "BARO",
                                            ["formation_template"] = "",
                                            ["y"] = 646994.39970704,
                                            ["x"] = -281110.47063949,
                                            ["ETA_locked"] = true,
                                            ["speed"] = 0,
                                            ["action"] = "Off Road",
                                            ["task"] = 
                                            {
                                                ["id"] = "ComboTask",
                                                ["params"] = 
                                                {
                                                    ["tasks"] = 
                                                    {
                                                    }, -- end of ["tasks"]
                                                }, -- end of ["params"]
                                            }, -- end of ["task"]
                                            ["speed_locked"] = true,
                                        }, -- end of [1]
                                    }, -- end of ["points"]
                                }, -- end of ["route"]
                                ["groupId"] = 5,
                                ["hidden"] = false,
                                ["units"] = 
                                {
                                    [1] = 
                                    {
                                        ["skill"] = "Average",
                                        ["coldAtStart"] = false,
                                        ["type"] = "Stinger comm dsr",
                                        ["unitId"] = 22,
                                        ["y"] = 646994.39970704,
                                        ["x"] = -281110.47063949,
                                        ["name"] = "Missileer Pickup-8",
                                        ["heading"] = 5.4454272662223,
                                        ["playerCanDrive"] = false,
                                    }, -- end of [1]
                                    [2] = 
                                    {
                                        ["skill"] = "Average",
                                        ["coldAtStart"] = false,
                                        ["type"] = "Stinger comm",
                                        ["unitId"] = 21,
                                        ["y"] = 646994.83654656,
                                        ["x"] = -281118.11533111,
                                        ["name"] = "Missileer Pickup-7",
                                        ["heading"] = 5.3930673886625,
                                        ["playerCanDrive"] = false,
                                    }, -- end of [2]
                                    [3] = 
                                    {
                                        ["skill"] = "Average",
                                        ["coldAtStart"] = false,
                                        ["type"] = "Soldier stinger",
                                        ["unitId"] = 20,
                                        ["y"] = 646995.71022561,
                                        ["x"] = -281125.32318321,
                                        ["name"] = "Missileer Pickup-6",
                                        ["heading"] = 5.4454272662223,
                                        ["playerCanDrive"] = false,
                                    }, -- end of [3]
                                    [4] = 
                                    {
                                        ["skill"] = "Average",
                                        ["coldAtStart"] = false,
                                        ["type"] = "SA-18 Igla-S comm",
                                        ["unitId"] = 19,
                                        ["y"] = 646984.13397829,
                                        ["x"] = -281111.56273829,
                                        ["name"] = "Missileer Pickup-5",
                                        ["heading"] = 5.3058009260628,
                                        ["playerCanDrive"] = false,
                                    }, -- end of [4]
                                    [5] = 
                                    {
                                        ["skill"] = "Average",
                                        ["coldAtStart"] = false,
                                        ["type"] = "SA-18 Igla-S manpad",
                                        ["unitId"] = 18,
                                        ["y"] = 646985.67169118,
                                        ["x"] = -281119.10928153,
                                        ["name"] = "Missileer Pickup-4",
                                        ["heading"] = 5.4105206811824,
                                        ["playerCanDrive"] = true,
                                    }, -- end of [5]
                                    [6] = 
                                    {
                                        ["skill"] = "Average",
                                        ["coldAtStart"] = false,
                                        ["type"] = "Igla manpad INS",
                                        ["unitId"] = 17,
                                        ["y"] = 646986.09975614,
                                        ["x"] = -281127.07054129,
                                        ["name"] = "Missileer Pickup-3",
                                        ["heading"] = 5.2708943410229,
                                        ["playerCanDrive"] = false,
                                    }, -- end of [6]
                                    [7] = 
                                    {
                                        ["skill"] = "Average",
                                        ["coldAtStart"] = false,
                                        ["type"] = "SA-18 Igla comm",
                                        ["unitId"] = 16,
                                        ["y"] = 646977.14454596,
                                        ["x"] = -281117.67849159,
                                        ["name"] = "Missileer Pickup-2",
                                        ["heading"] = 5.3756140961425,
                                        ["playerCanDrive"] = false,
                                    }, -- end of [7]
                                    [8] = 
                                    {
                                        ["skill"] = "Average",
                                        ["coldAtStart"] = false,
                                        ["type"] = "SA-18 Igla manpad",
                                        ["unitId"] = 15,
                                        ["y"] = 646977.36296572,
                                        ["x"] = -281124.23108441,
                                        ["name"] = "Missileer Pickup-1",
                                        ["heading"] = 5.4454272662223,
                                        ["playerCanDrive"] = false,
                                    }, -- end of [8]
                                }, -- end of ["units"]
                                ["y"] = 646994.39970704,
                                ["x"] = -281110.47063949,
                                ["name"] = "Missileer Pickup",
                                ["start_time"] = 0,
                            }, -- end of [3]
                        }, -- end of ["group"]
                    }, -- end of ["vehicle"]
                    ["plane"] = 
                    {
                        ["group"] = 
                        {
                            [1] = 
                            {
                                ["modulation"] = 0,
                                ["tasks"] = 
                                {
                                }, -- end of ["tasks"]
                                ["radioSet"] = false,
                                ["task"] = "CAS",
                                ["uncontrolled"] = false,
                                ["route"] = 
                                {
                                    ["points"] = 
                                    {
                                        [1] = 
                                        {
                                            ["alt"] = 13,
                                            ["action"] = "From Parking Area Hot",
                                            ["alt_type"] = "BARO",
                                            ["properties"] = 
                                            {
                                                ["addopt"] = 
                                                {
                                                }, -- end of ["addopt"]
                                            }, -- end of ["properties"]
                                            ["speed"] = 138.88888888889,
                                            ["task"] = 
                                            {
                                                ["id"] = "ComboTask",
                                                ["params"] = 
                                                {
                                                    ["tasks"] = 
                                                    {
                                                        [1] = 
                                                        {
                                                            ["enabled"] = true,
                                                            ["key"] = "CAS",
                                                            ["id"] = "EngageTargets",
                                                            ["number"] = 1,
                                                            ["auto"] = true,
                                                            ["params"] = 
                                                            {
                                                                ["targetTypes"] = 
                                                                {
                                                                    [1] = "Helicopters",
                                                                    [2] = "Ground Units",
                                                                    [3] = "Light armed ships",
                                                                }, -- end of ["targetTypes"]
                                                                ["priority"] = 0,
                                                            }, -- end of ["params"]
                                                        }, -- end of [1]
                                                        [2] = 
                                                        {
                                                            ["enabled"] = true,
                                                            ["auto"] = true,
                                                            ["id"] = "WrappedAction",
                                                            ["number"] = 2,
                                                            ["params"] = 
                                                            {
                                                                ["action"] = 
                                                                {
                                                                    ["id"] = "Option",
                                                                    ["params"] = 
                                                                    {
                                                                        ["value"] = 2,
                                                                        ["name"] = 1,
                                                                    }, -- end of ["params"]
                                                                }, -- end of ["action"]
                                                            }, -- end of ["params"]
                                                        }, -- end of [2]
                                                        [3] = 
                                                        {
                                                            ["enabled"] = true,
                                                            ["auto"] = true,
                                                            ["id"] = "WrappedAction",
                                                            ["number"] = 3,
                                                            ["params"] = 
                                                            {
                                                                ["action"] = 
                                                                {
                                                                    ["id"] = "Option",
                                                                    ["params"] = 
                                                                    {
                                                                        ["value"] = 1,
                                                                        ["name"] = 3,
                                                                    }, -- end of ["params"]
                                                                }, -- end of ["action"]
                                                            }, -- end of ["params"]
                                                        }, -- end of [3]
                                                        [4] = 
                                                        {
                                                            ["enabled"] = true,
                                                            ["auto"] = true,
                                                            ["id"] = "WrappedAction",
                                                            ["number"] = 4,
                                                            ["params"] = 
                                                            {
                                                                ["action"] = 
                                                                {
                                                                    ["id"] = "Option",
                                                                    ["params"] = 
                                                                    {
                                                                        ["variantIndex"] = 2,
                                                                        ["name"] = 5,
                                                                        ["formationIndex"] = 2,
                                                                        ["value"] = 131074,
                                                                    }, -- end of ["params"]
                                                                }, -- end of ["action"]
                                                            }, -- end of ["params"]
                                                        }, -- end of [4]
                                                        [5] = 
                                                        {
                                                            ["enabled"] = true,
                                                            ["auto"] = true,
                                                            ["id"] = "WrappedAction",
                                                            ["number"] = 5,
                                                            ["params"] = 
                                                            {
                                                                ["action"] = 
                                                                {
                                                                    ["id"] = "Option",
                                                                    ["params"] = 
                                                                    {
                                                                        ["value"] = true,
                                                                        ["name"] = 15,
                                                                    }, -- end of ["params"]
                                                                }, -- end of ["action"]
                                                            }, -- end of ["params"]
                                                        }, -- end of [5]
                                                        [6] = 
                                                        {
                                                            ["enabled"] = true,
                                                            ["auto"] = true,
                                                            ["id"] = "WrappedAction",
                                                            ["number"] = 6,
                                                            ["params"] = 
                                                            {
                                                                ["action"] = 
                                                                {
                                                                    ["id"] = "Option",
                                                                    ["params"] = 
                                                                    {
                                                                        ["targetTypes"] = 
                                                                        {
                                                                        }, -- end of ["targetTypes"]
                                                                        ["name"] = 21,
                                                                        ["value"] = "none;",
                                                                        ["noTargetTypes"] = 
                                                                        {
                                                                            [1] = "Fighters",
                                                                            [2] = "Multirole fighters",
                                                                            [3] = "Bombers",
                                                                            [4] = "Helicopters",
                                                                            [5] = "Infantry",
                                                                            [6] = "Fortifications",
                                                                            [7] = "Tanks",
                                                                            [8] = "IFV",
                                                                            [9] = "APC",
                                                                            [10] = "Artillery",
                                                                            [11] = "Unarmed vehicles",
                                                                            [12] = "AAA",
                                                                            [13] = "SR SAM",
                                                                            [14] = "MR SAM",
                                                                            [15] = "LR SAM",
                                                                            [16] = "Aircraft Carriers",
                                                                            [17] = "Cruisers",
                                                                            [18] = "Destroyers",
                                                                            [19] = "Frigates",
                                                                            [20] = "Corvettes",
                                                                            [21] = "Light armed ships",
                                                                            [22] = "Unarmed ships",
                                                                            [23] = "Submarines",
                                                                            [24] = "Cruise missiles",
                                                                            [25] = "Antiship Missiles",
                                                                            [26] = "AA Missiles",
                                                                            [27] = "AG Missiles",
                                                                            [28] = "SA Missiles",
                                                                            [29] = "UAVs",
                                                                        }, -- end of ["noTargetTypes"]
                                                                    }, -- end of ["params"]
                                                                }, -- end of ["action"]
                                                            }, -- end of ["params"]
                                                        }, -- end of [6]
                                                        [7] = 
                                                        {
                                                            ["enabled"] = true,
                                                            ["auto"] = true,
                                                            ["id"] = "WrappedAction",
                                                            ["number"] = 7,
                                                            ["params"] = 
                                                            {
                                                                ["action"] = 
                                                                {
                                                                    ["id"] = "Option",
                                                                    ["params"] = 
                                                                    {
                                                                        ["value"] = true,
                                                                        ["name"] = 19,
                                                                    }, -- end of ["params"]
                                                                }, -- end of ["action"]
                                                            }, -- end of ["params"]
                                                        }, -- end of [7]
                                                    }, -- end of ["tasks"]
                                                }, -- end of ["params"]
                                            }, -- end of ["task"]
                                            ["type"] = "TakeOffParkingHot",
                                            ["ETA"] = 0,
                                            ["ETA_locked"] = true,
                                            ["y"] = 646373.17498617,
                                            ["x"] = -281607.28417614,
                                            ["speed_locked"] = true,
                                            ["formation_template"] = "",
                                            ["airdromeId"] = 23,
                                        }, -- end of [1]
                                    }, -- end of ["points"]
                                }, -- end of ["route"]
                                ["groupId"] = 1,
                                ["hidden"] = false,
                                ["units"] = 
                                {
                                    [1] = 
                                    {
                                        ["alt"] = 13,
                                        ["alt_type"] = "BARO",
                                        ["livery_id"] = "Algerian AF Desert KU-03",
                                        ["skill"] = "Client",
                                        ["parking"] = "2",
                                        ["speed"] = 138.88888888889,
                                        ["type"] = "Su-25T",
                                        ["unitId"] = 1,
                                        ["psi"] = 0,
                                        ["onboard_num"] = "010",
                                        ["parking_id"] = "01",
                                        ["x"] = -281607.28417614,
                                        ["name"] = "Ground Frog-1",
                                        ["payload"] = 
                                        {
                                            ["pylons"] = 
                                            {
                                            }, -- end of ["pylons"]
                                            ["fuel"] = "3790",
                                            ["flare"] = 128,
                                            ["chaff"] = 128,
                                            ["gun"] = 100,
                                        }, -- end of ["payload"]
                                        ["y"] = 646373.17498617,
                                        ["heading"] = 0,
                                        ["callsign"] = 
                                        {
                                            [1] = 1,
                                            [2] = 1,
                                            ["name"] = "Enfield11",
                                            [3] = 1,
                                        }, -- end of ["callsign"]
                                    }, -- end of [1]
                                }, -- end of ["units"]
                                ["y"] = 646373.17498617,
                                ["x"] = -281607.28417614,
                                ["name"] = "Ground Frog",
                                ["communication"] = true,
                                ["start_time"] = 0,
                                ["uncontrollable"] = false,
                                ["frequency"] = 124,
                            }, -- end of [1]
                        }, -- end of ["group"]
                    }, -- end of ["plane"]
                    ["static"] = 
                    {
                        ["group"] = 
                        {
                            [1] = 
                            {
                                ["heading"] = 0,
                                ["route"] = 
                                {
                                    ["points"] = 
                                    {
                                        [1] = 
                                        {
                                            ["alt"] = 0,
                                            ["type"] = "",
                                            ["name"] = "",
                                            ["y"] = 613255.40248687,
                                            ["speed"] = 0,
                                            ["x"] = -288785.56746765,
                                            ["formation_template"] = "",
                                            ["action"] = "",
                                        }, -- end of [1]
                                    }, -- end of ["points"]
                                }, -- end of ["route"]
                                ["groupId"] = 10,
                                ["units"] = 
                                {
                                    [1] = 
                                    {
                                        ["category"] = "Heliports",
                                        ["shape_name"] = "gas_platform",
                                        ["type"] = "Gas platform",
                                        ["unitId"] = 27,
                                        ["heliport_callsign_id"] = 1,
                                        ["heliport_modulation"] = 0,
                                        ["rate"] = 100,
                                        ["y"] = 613255.40248687,
                                        ["x"] = -288785.56746765,
                                        ["name"] = "Static Gas platform-1-1",
                                        ["heliport_frequency"] = "127.5",
                                        ["heading"] = 0,
                                    }, -- end of [1]
                                }, -- end of ["units"]
                                ["y"] = 613255.40248687,
                                ["x"] = -288785.56746765,
                                ["name"] = "Static Gas platform-1",
                                ["dead"] = false,
                            }, -- end of [1]
                        }, -- end of ["group"]
                    }, -- end of ["static"]
                }, -- end of [1]
                [2] = 
                {
                    ["id"] = 0,
                    ["static"] = 
                    {
                        ["group"] = 
                        {
                            [1] = 
                            {
                                ["heading"] = 2.4434609527921,
                                ["route"] = 
                                {
                                    ["points"] = 
                                    {
                                        [1] = 
                                        {
                                            ["alt"] = 0,
                                            ["type"] = "",
                                            ["name"] = "",
                                            ["y"] = 646342.48303112,
                                            ["speed"] = 0,
                                            ["x"] = -281640.55862357,
                                            ["formation_template"] = "",
                                            ["action"] = "",
                                        }, -- end of [1]
                                    }, -- end of ["points"]
                                }, -- end of ["route"]
                                ["groupId"] = 15,
                                ["units"] = 
                                {
                                    [1] = 
                                    {
                                        ["livery_id"] = "af standard 1",
                                        ["category"] = "Planes",
                                        ["type"] = "Su-25T",
                                        ["unitId"] = 36,
                                        ["rate"] = "50",
                                        ["y"] = 646342.48303112,
                                        ["x"] = -281640.55862357,
                                        ["name"] = "Static Su-25T-1-1",
                                        ["heading"] = 2.4434609527921,
                                    }, -- end of [1]
                                }, -- end of ["units"]
                                ["y"] = 646342.48303112,
                                ["x"] = -281640.55862357,
                                ["name"] = "Static Su-25T-1",
                                ["dead"] = false,
                            }, -- end of [1]
                        }, -- end of ["group"]
                    }, -- end of ["static"]
                    ["name"] = "Russia",
                }, -- end of [2]
            }, -- end of ["country"]
        }, -- end of ["red"]
    }, -- end of ["coalition"]
    ["sortie"] = "DictKey_sortie_5",
    ["version"] = 22,
    ["trigrules"] = 
    {
        [1] = 
        {
            ["rules"] = 
            {
            }, -- end of ["rules"]
            ["eventlist"] = "",
            ["actions"] = 
            {
                [1] = 
                {
                    ["text"] = "dcsCommon = {}\
dcsCommon.version = \"3.0.7\"\
--[[-- VERSION HISTORY \
3.0.0  - removed bad bug in stringStartsWith, only relevant if caseSensitive is false \
       - point2text new intsOnly option \
       - arrangeGroupDataIntoFormation minDist harden\
       - cleanup \
       - new pointInDirectionOfPointXYY()\
       - createGroundGroupWithUnits now supports liveries\
       - new getAllExistingPlayersAndUnits()\
3.0.1  - clone: better handling of string type\
3.0.2  - new getPlayerUnit() \
3.0.3  - createStaticObjectForCoalitionInRandomRing() returns x and z \
       - isTroopCarrier() also supports 'helo' keyword\
        - new createTakeOffFromGroundRoutePointData()\
3.0.4  - getGroupLocation() hardened, optional verbose \
3.0.5  - new getNthItem()\
       - new getFirstItem()\
       - arrayContainsString() can handle dicts \
       - new pointXpercentYdegOffAB()\
3.0.6  - new arrayContainsStringCaseInsensitive()\
3.0.7  - fixed small bug in wildArrayContainsString \
\
--]]--\
\
    -- dcsCommon is a library of common lua functions \
    -- for easy access and simple mission programming\
    -- (c) 2021 - 2024 by Christian Franz and cf/x AG\
\
    dcsCommon.verbose = false -- set to true to see debug messages. Lots of them\
    dcsCommon.uuidStr = \"uuid-\"\
    dcsCommon.simpleUUID = 76543 -- a number to start. as good as any\
    \
    -- globals\
    dcsCommon.cbID = 0 -- callback id for simple callback scheduling\
    dcsCommon.troopCarriers = {\"Mi-8MT\", \"UH-1H\", \"Mi-24P\"} -- Ka-50, Apache and Gazelle can't carry troops\
    dcsCommon.coalitionSides = {0, 1, 2}\
    dcsCommon.maxCountry = 86 -- number of countries defined in total \
    \
    -- lookup tables\
    dcsCommon.groupID2Name = {}\
    dcsCommon.unitID2Name = {}\
    dcsCommon.unitID2X = {}\
    dcsCommon.unitID2Y = {}\
\
    -- verify that a module is loaded. obviously not required\
    -- for dcsCommon, but all higher-order modules\
    function dcsCommon.libCheck(testingFor, requiredLibs)\
        local canRun = true \
        for idx, libName in pairs(requiredLibs) do \
            if not _G[libName] then \
                trigger.action.outText(\"*** \" .. testingFor .. \" requires \" .. libName, 30)\
                canRun = false \
            end\
        end\
        return canRun\
    end\
\
    -- read all groups and units from miz and build a reference table\
    function dcsCommon.collectMissionIDs()\
    -- create cross reference tables to be able to get a group or\
    -- unit's name by ID\
        for coa_name_miz, coa_data in pairs(env.mission.coalition) do -- iterate all coalitions\
            local coa_name = coa_name_miz\
            if string.lower(coa_name_miz) == 'neutrals' then -- remove 's' at neutralS\
                coa_name = 'neutral'\
            end\
            -- directly convert coalition into number for easier access later\
            local coaNum = 0\
            if coa_name == \"red\" then coaNum = 1 end \
            if coa_name == \"blue\" then coaNum = 2 end \
            \
            if type(coa_data) == 'table' then -- coalition = {bullseye, nav_points, name, county}, \
                                              -- with county being an array \
                if coa_data.country then -- make sure there a country table for this coalition\
                    for cntry_id, cntry_data in pairs(coa_data.country) do -- iterate all countries for this \
                        -- per country = {id, name, vehicle, helicopter, plane, ship, static}\
                        local countryName = string.lower(cntry_data.name)\
                        local countryID = cntry_data.id \
                        if type(cntry_data) == 'table' then    -- filter strings .id and .name \
                            for obj_type_name, obj_type_data in pairs(cntry_data) do\
                                -- only look at helos, ships, planes and vehicles\
                                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\" -- what about \"cargo\"?\
                                then -- (so it's not id or name)\
                                    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 at least one group!\
                                        for group_num, group_data in pairs(obj_type_data.group) do\
                                            \
                                            local aName = group_data.name \
                                            local aID = group_data.groupId\
                                            -- store this reference \
                                            dcsCommon.groupID2Name[aID] = aName \
                                            \
                                            -- now iterate all units in this group \
                                            -- for player into \
                                            for unit_num, unit_data in pairs(group_data.units) do\
                                                if unit_data.name and unit_data.unitId then \
                                                    -- store this reference \
                                                    dcsCommon.unitID2Name[unit_data.unitId] = unit_data.name\
                                                    dcsCommon.unitID2X[unit_data.unitId] = unit_data.x\
                                                    dcsCommon.unitID2Y[unit_data.unitId] = unit_data.y\
                                                end\
                                            end -- for all units\
                                        end -- for all groups \
                                    end --if has category data \
                                end --if plane, helo etc... category\
                            end --for all objects in country \
                        end --if has country data \
                    end --for all countries in coalition\
                end --if coalition has country table \
            end -- if there is coalition data  \
        end --for all coalitions in mission \
    end\
\
    function dcsCommon.getUnitNameByID(theID)\
        -- accessor function for later expansion\
        return dcsCommon.unitID2Name[theID]\
    end\
    \
    function dcsCommon.getGroupNameByID(theID)\
        -- accessor function for later expansion \
        return dcsCommon.groupID2Name[theID]\
    end\
\
    function dcsCommon.getUnitStartPosByID(theID)\
        local x = dcsCommon.unitID2X[theID]\
        local y = dcsCommon.unitID2Y[theID]\
        return x, y\
    end\
    \
    -- returns only positive values, lo must be >0 and <= hi \
    function dcsCommon.randomBetween(loBound, hiBound)\
        if not loBound then loBound = 1 end \
        if not hiBound then hiBound = 1 end \
        if loBound == hiBound then return loBound end \
\
        local delayMin = loBound\
        local delayMax = hiBound \
        local delay = delayMax \
    \
        if delayMin ~= delayMax then \
            -- pick random in range , say 3-7 --> 5 s!\
            local delayDiff = (delayMax - delayMin) + 1 -- 7-3 + 1\
            delay = dcsCommon.smallRandom(delayDiff) - 1 --> 0-4\
            delay = delay + delayMin \
            if delay > delayMax then delay = delayMax end \
            if delay < 1 then delay = 1 end \
        \
            if dcsCommon.verbose then \
                trigger.action.outText(\"+++dcsC: delay range \" .. delayMin .. \"-\" .. delayMax .. \": selected \" .. delay, 30)\
            end\
        end\
        \
        return delay\
    end\
    \
\
    -- taken inspiration from mist, as dcs lua has issues with\
    -- random numbers smaller than 50. Given a range of x numbers 1..x, it is \
    -- repeated a number of times until it fills an array of at least \
    -- 50 items (usually some more), and only then one itemis picked from \
    -- that array with a random number that is from a greater range (0..50+)\
    function dcsCommon.smallRandom(theNum) -- adapted from mist, only support ints\
        theNum = math.floor(theNum)\
        if theNum >= 50 then return math.random(theNum) end\
        if theNum < 1 then\
            trigger.action.outText(\"smallRandom: invoke with argument < 1 (\" .. theNum .. \"), using 1\", 30)\
            theNum = 1 \
        end \
        -- for small randoms (<50) \
        local lowNum, highNum\
        highNum = theNum\
        lowNum = 1\
        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)) -- number of times to repeat whole range to get above 50. e.g. 11 would be 5 times 1 .. 11, giving us 55 items total \
        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, 15 do\
            rtnVal = math.random(#choices) -- iterate 15 times for randomization\
        end\
        return choices[rtnVal] -- return indexed\
    end\
    \
    function dcsCommon.getNthItem(theSet, n)\
        local count = 1 \
        for key, value in pairs(theSet) do\
            if count == n then return value end \
            count = count + 1 \
        end\
        return nil \
    end\
\
    function dcsCommon.getFirstItem(theSet)\
        return dcsCommon.getNthItem(theSet, 1)\
    end\
\
    function dcsCommon.getSizeOfTable(theTable)\
        local count = 0\
        for _ in pairs(theTable) do count = count + 1 end\
        return count\
    end\
\
    function dcsCommon.findAndRemoveFromTable(theTable, theElement) -- assumes array \
        if not theElement then return false end \
        if not theTable then return false end \
        for i=1, #theTable do \
            if theTable[i] == theElement then \
                -- this element found. remove from table \
                table.remove(theTable, i)\
                return true \
            end\
        end\
    end\
\
    function dcsCommon.pickRandom(theTable)\
        if not theTable then \
            trigger.action.outText(\"*** warning: nil table in pick random\", 30)\
        end\
        \
        if #theTable < 1 then \
            trigger.action.outText(\"*** warning: zero choice in pick random\", 30)\
            --local k = i.ll \
            return nil\
        end\
        if #theTable == 1 then return theTable[1] end\
        r = dcsCommon.smallRandom(#theTable) --r = math.random(#theTable)\
        return theTable[r]\
    end\
\
    -- enumerateTable - make an array out of a table for indexed access\
    function dcsCommon.enumerateTable(theTable)\
        if not theTable then theTable = {} end\
        local array = {}\
        for key, value in pairs(theTable) do \
            table.insert(array, value)\
        end\
        return array\
    end\
\
    -- combine table. creates new \
    function dcsCommon.combineTables(inOne, inTwo)\
        local outTable = {}\
        for idx, element in pairs(inOne) do \
            table.insert(outTable, element)\
        end\
        for idx, element in pairs(inTwo) do \
            table.insert(outTable, element)\
        end\
        return outTable\
    end\
    \
    function dcsCommon.addToTableIfNew(theTable, theElement)\
        for idx, anElement in pairs(theTable) do \
            if anElement == theElement then return end \
        end\
        table.insert(theTable, theElement)\
    end\
-- \
-- A I R F I E L D S  A N D  F A R P S  \
--\
\
    -- airfield management \
    function dcsCommon.getAirbaseCat(aBase)\
        if not aBase then return nil end \
        \
        local airDesc = aBase:getDesc()\
        if not airDesc then return nil end \
        \
        local airCat = airDesc.category\
        return airCat \
    end\
\
    -- get free parking slot. optional parkingType can be used to \
    -- filter for a scpecific type, e.g. 104 = open field\
    function dcsCommon.getFirstFreeParkingSlot(aerodrome, parkingType) \
        if not aerodrome then return nil end \
        local freeSlots = aerodrome:getParking(true)\
        \
        for idx, theSlot in pairs(freeSlots) do \
            if not parkingType then \
                -- simply return the first we come across\
                return theSlot\
            end        \
            \
            if theSlot.Term_Type == parkingType then \
                return theSlot \
            end\
        end\
        \
        return nil \
    end\
\
    -- getAirbasesInRangeOfPoint: get airbases that are in range of point \
    function dcsCommon.getAirbasesInRangeOfPoint(center, range, filterCat, filterCoalition)\
        if not center then return {} end \
        if not range then range = 500 end -- 500m default \
        local basesInRange = {}\
        \
        local allAB = dcsCommon.getAirbasesWhoseNameContains(\"*\", filterCat, filterCoalition)\
        for idx, aBase in pairs(allAB) do             \
            local delta = dcsCommon.dist(center, aBase:getPoint())\
            if delta <= range then \
                table.insert(basesInRange, aBase)\
            end\
        end\
        return basesInRange\
    end\
\
    -- getAirbasesInRangeOfAirbase returns all airbases that \
    -- are in range of the given airbase \
    function dcsCommon.getAirbasesInRangeOfAirbase(airbase, includeCenter, range, filterCat, filterCoalition)\
        if not airbase then return {} end\
        if not range then range = 150000 end \
        local center = airbase:getPoint() \
        local centerName = airbase:getName() \
        \
        local ABinRange = {}\
        local allAB = dcsCommon.getAirbasesWhoseNameContains(\"*\", filterCat, filterCoalition)\
        \
        for idx, aBase in pairs(allAB) do \
            if aBase:getName() ~= centerName then \
                local delta = dcsCommon.dist(center, aBase:getPoint())\
                if delta <= range then \
                    table.insert(ABinRange, aBase)\
                end\
            end        \
        end\
        \
        if includeCenter then \
            table.insert(ABinRange, airbase)\
        end\
        \
        return ABinRange\
    end\
\
    function dcsCommon.getAirbasesInRangeOfAirbaseList(theCenterList, includeList, range, filterCat, filterCoalition)\
        local collectorDict = {}\
        for idx, aCenter in pairs(theCenterList) do \
            -- get all surrounding airbases. returns list of airfields \
            local surroundingAB = dcsCommon.getAirbasesInRangeOfAirbase(airbase, includeList, range, filterCat, filterCoalition)\
            \
            for idx2, theAirField in pairs (surroundingAB) do \
                collectorDict[airField] = theAirField \
            end\
        end\
        \
        -- make result an array\
        local theABList = dcsCommon.enumerateTable(collectorDict)\
        return theABList\
    end\
\
    -- getAirbasesWhoseNameContains - get all airbases containing \
    -- a name. filterCat is optional and can be aerodrome (0), farp (1), ship (2)\
    -- filterCoalition is optional and can be 0 (neutral), 1 (red), 2 (blue) or \
    -- a table containing categories, e.g. {0, 2} = airfields and ships but not farps \
    -- if no name given or aName = \"*\", then all bases are returned prior to filtering \
    function dcsCommon.getAirbasesWhoseNameContains(aName, filterCat, filterCoalition)\
        if not aName then aName = \"*\" end \
        local allYourBase = world.getAirbases() -- get em all \
        local areBelongToUs = {}\
        -- now iterate all bases\
        for idx, aBase in pairs(allYourBase) do\
            local airBaseName = aBase:getName() -- get display name\
            if aName == \"*\" or dcsCommon.containsString(airBaseName, aName) then \
                -- containsString is case insesitive unless told otherwise\
                local doAdd = true  \
                if filterCat then \
                    local aCat = dcsCommon.getAirbaseCat(aBase)\
                    if type(filterCat) == \"table\" then \
                        local hit = false\
                        for idx, fCat in pairs(filterCat) do \
                            if fCat == aCat then hit = true end\
                        end\
                        doAdd = doAdd and hit \
                    else \
                        -- make sure the airbase is of that category \
                        local airCat = aCat\
                        doAdd = doAdd and airCat == filterCat \
                    end\
                end\
                \
                if filterCoalition then \
                    doAdd = doAdd and filterCoalition == aBase:getCoalition()\
                end\
                \
                if doAdd then \
                    -- all good, add to table\
                    table.insert(areBelongToUs, aBase)\
                end            \
            end\
        end\
        return areBelongToUs\
    end\
\
    function dcsCommon.getFirstAirbaseWhoseNameContains(aName, filterCat, filterCoalition)\
        local allBases = dcsCommon.getAirbasesWhoseNameContains(aName, filterCat, filterCoalition)\
        for idx, aBase in pairs (allBases) do \
            -- simply return first \
            return aBase\
        end\
        return nil \
    end    \
\
    function dcsCommon.getClosestAirbaseTo(thePoint, filterCat, filterCoalition, allYourBase)\
        local delta = math.huge\
        if not allYourBase then \
            allYourBase = dcsCommon.getAirbasesWhoseNameContains(\"*\", filterCat, filterCoalition) -- get em all and filter\
        end \
        \
        local closestBase = nil \
        for idx, aBase in pairs(allYourBase) do\
            -- iterate them all \
            local abPoint = aBase:getPoint()\
            newDelta = dcsCommon.dist(thePoint, {x=abPoint.x, y = 0, z=abPoint.z})\
            if newDelta < delta then \
                delta = newDelta\
                closestBase = aBase\
            end\
        end\
        return closestBase, delta \
    end\
\
    function dcsCommon.getClosestFreeSlotForCatInAirbaseTo(cat, x, y, theAirbase, ignore)\
        if not theAirbase then return nil end \
        if not ignore then ignore = {} end \
        if not cat then return nil end \
        if (not cat == \"helicopter\") and (not cat == \"plane\") then \
            trigger.action.outText(\"+++common-getslotforcat: wrong cat <\" .. cat .. \">\", 30)\
            return nil \
        end\
        local allFree = theAirbase:getParking(true) --  only free slots\
        local filterFreeByType = {}\
        for idx, aSlot in pairs(allFree) do \
            local termT = aSlot.Term_Type\
            if termT == 104 or \
            (termT == 72 and cat == \"plane\") or \
            (termT == 68 and cat == \"plane\") or \
            (termT == 40 and cat == \"helicopter\") then \
                table.insert(filterFreeByType, aSlot)\
            else \
                -- we skip this slot, not good for type \
            end\
        end\
        \
        if #filterFreeByType == 0 then \
            return nil\
        end \
        \
        local reallyFree = {}\
        for idx, aSlot in pairs(filterFreeByType) do \
            local slotNum = aSlot.Term_Index\
            isTaken = false \
            for idy, taken in pairs(ignore) do \
                if taken == slotNum then isTaken = true end \
            end\
            if not isTaken then \
                table.insert(reallyFree, aSlot)\
            end\
        end\
        \
        if #reallyFree < 1 then \
            reallyFree = filterFreeByType\
        end\
        \
        local closestDist = math.huge \
        local closestSlot = nil \
        local p = {x = x, y = 0, z = y} -- !!\
        for idx, aSlot in pairs(reallyFree) do \
            local sp = {x = aSlot.vTerminalPos.x, y = 0, z = aSlot.vTerminalPos.z}\
            local currDist = dcsCommon.distFlat(p, sp)\
            if currDist < closestDist then \
                closestSlot = aSlot \
                closestDist = currDist \
            end\
        end\
        return closestSlot\
    end\
\
-- \
-- U N I T S   M A N A G E M E N T \
--\
\
    -- number of living units in group\
    function dcsCommon.livingUnitsInGroup(group)\
        local living = 0\
        local allUnits = group:getUnits()\
        for key, aUnit in pairs(allUnits) do \
            if aUnit:isExist() and aUnit:getLife() >= 1 then \
                living = living + 1\
            end\
        end\
        return living\
    end\
\
    -- closest living unit in group to a point\
    function dcsCommon.getClosestLivingUnitToPoint(group, p)\
        if not p then return nil end\
        if not group then return nil end\
        local closestUnit = nil\
        local closestDist = math.huge\
        local allUnits = group:getUnits()\
        for key, aUnit in pairs(allUnits) do \
            if aUnit:isExist() and aUnit:getLife() >= 1 then \
                local thisDist = dcsCommon.dist(p, aUnit:getPoint())\
                if thisDist < closestDist then \
                    closestDist = thisDist\
                    closestUnit = aUnit \
                end\
            end\
        end\
        return closestUnit, closestDist\
    end\
    \
    -- closest living group to a point - cat can be nil or one of Group.Category = { AIRPLANE = 0, HELICOPTER = 1, GROUND = 2, SHIP = 3, TRAIN = 4}\
    function dcsCommon.getClosestLivingGroupToPoint(p, coal, cat) \
        if not cat then cat = 2 end -- ground is default \
        local closestGroup = nil;\
        local closestGroupDist = math.huge\
        local allGroups =  coalition.getGroups(coal, cat) -- get all groups from this coalition, perhaps filtered by cat \
        for key, grp in pairs(allGroups) do\
            local closestUnit, dist = dcsCommon.getClosestLivingUnitToPoint(grp, p)\
            if closestUnit then \
                if dist < closestGroupDist then \
                    closestGroup = grp\
                    closestGroupDist = dist\
                end\
            end            \
        end\
        return closestGroup, closestGroupDist\
    end\
\
    function dcsCommon.getLivingGroupsAndDistInRangeToPoint(p, range, coal, cat) \
        if not cat then cat = 2 end -- ground is default \
        local groupsInRange = {};\
        local allGroups = coalition.getGroups(coal, cat) -- get all groups from this coalition, perhaps filtered by cat \
        for key, grp in pairs(allGroups) do\
            local closestUnit, dist = dcsCommon.getClosestLivingUnitToPoint(grp, p)\
            if closestUnit then \
                if dist < range then \
                    table.insert(groupsInRange, {group = grp, dist = dist}) -- array\
                end\
            end            \
        end\
        -- sort the groups by distance\
        table.sort(groupsInRange, function (left, right) return left.dist < right.dist end )\
        return groupsInRange\
    end\
\
    -- distFlat ignores y, input must be xyz points, NOT xy points  \
    function dcsCommon.distFlat(p1, p2) \
        local point1 = {x = p1.x, y = 0, z=p1.z}\
        local point2 = {x = p2.x, y = 0, z=p2.z}\
        return dcsCommon.dist(point1, point2)\
    end\
    \
    \
    -- distance between points\
    function dcsCommon.dist(point1, point2)     -- returns distance between two points\
      -- supports xyz and xy notations\
      if not point1 then \
        trigger.action.outText(\"+++ warning: nil point1 in common:dist\", 30)\
        point1 = {x=0, y=0, z=0}\
      end\
\
      if not point2 then \
        trigger.action.outText(\"+++ warning: nil point2 in common:dist\", 30)\
        point2 = {x=0, y=0, z=0}\
        stop.here.now = 1\
      end\
      \
      local p1 = {x = point1.x, y = point1.y}\
      if not point1.z then \
        p1.z = p1.y\
        p1.y = 0\
      else \
        p1.z = point1.z\
      end\
      \
      local p2 = {x = point2.x, y = point2.y}\
      if not point2.z then \
        p2.z = p2.y\
        p2.y = 0\
      else \
        p2.z = point2.z\
      end\
      \
      local x = p1.x - p2.x\
      local y = p1.y - p2.y \
      local z = p1.z - p2.z\
      \
      return (x*x + y*y + z*z)^0.5\
    end\
\
    function dcsCommon.delta(name1, name2) -- returns distance (in meters) of two named objects\
      local n1Pos = Unit.getByName(name1):getPosition().p\
      local n2Pos = Unit.getByName(name2):getPosition().p\
      return dcsCommon.dist(n1Pos, n2Pos)\
    end\
\
    -- lerp between a and b, x being 0..1 (percentage), clipped to [0..1]\
    function dcsCommon.lerp(a, b, x) \
        if not a then return 0 end\
        if not b then return 0 end\
        if not x then return a end\
        if x < 0 then x = 0 end \
        if x > 1 then x = 1 end \
        return a + (b - a ) * x\
    end\
\
    function dcsCommon.bearingFromAtoB(A, B) -- coords in x, z \
        if not A then \
            trigger.action.outText(\"WARNING: no 'A' in bearingFromAtoB\", 30)\
            return 0\
        end\
        if not B then\
            trigger.action.outText(\"WARNING: no 'B' in bearingFromAtoB\", 30)\
            return 0\
        end\
        if not A.x then \
            trigger.action.outText(\"WARNING: no 'A.x' (type A =<\" .. type(A) .. \">)in bearingFromAtoB\", 30)\
            return 0\
        end\
        if not A.z then \
            trigger.action.outText(\"WARNING: no 'A.z' (type A =<\" .. type(A) .. \">)in bearingFromAtoB\", 30)\
            return 0\
        end\
        if not B.x then \
            trigger.action.outText(\"WARNING: no 'B.x' (type B =<\" .. type(B) .. \">)in bearingFromAtoB\", 30)\
            return 0\
        end\
        if not B.z then \
            trigger.action.outText(\"WARNING: no 'B.z' (type B =<\" .. type(B) .. \">)in bearingFromAtoB\", 30)\
            return 0\
        end\
        \
        local dx = B.x - A.x\
        local dz = B.z - A.z\
        local bearing = math.atan2(dz, dx) -- in radiants\
        return bearing\
    end\
\
    function dcsCommon.bearingFromAtoBusingXY(A, B) -- coords in x, y \
        if not A then \
            trigger.action.outText(\"WARNING: no 'A' in bearingFromAtoBXY\", 30)\
            return 0\
        end\
        if not B then\
            trigger.action.outText(\"WARNING: no 'B' in bearingFromAtoBXY\", 30)\
            return 0\
        end\
        if not A.x then \
            trigger.action.outText(\"WARNING: no 'A.x' (type A =<\" .. type(A) .. \">)in bearingFromAtoBXY\", 30)\
            return 0\
        end\
        if not A.y then \
            trigger.action.outText(\"WARNING: no 'A.y' (type A =<\" .. type(A) .. \">)in bearingFromAtoBXY\", 30)\
            return 0\
        end\
        if not B.x then \
            trigger.action.outText(\"WARNING: no 'B.x' (type B =<\" .. type(B) .. \">)in bearingFromAtoBXY\", 30)\
            return 0\
        end\
        if not B.y then \
            trigger.action.outText(\"WARNING: no 'B.y' (type B =<\" .. type(B) .. \">)in bearingFromAtoBXY\", 30)\
            return 0\
        end\
        \
        local dx = B.x - A.x\
        local dz = B.y - A.y\
        local bearing = math.atan2(dz, dx) -- in radiants\
        return bearing\
    end\
\
    function dcsCommon.bearingInDegreesFromAtoB(A, B)\
        local bearing = dcsCommon.bearingFromAtoB(A, B) -- in rads \
        bearing = math.floor(bearing / math.pi * 180)\
        if bearing < 0 then bearing = bearing + 360 end\
        if bearing > 360 then bearing = bearing - 360 end\
        return bearing\
    end\
    \
    function dcsCommon.compassPositionOfARelativeToB(A, B)\
        -- warning: is REVERSE in order for bearing, returns a string like 'Sorth', 'Southwest'\
        if not A then return \"***error:A***\" end\
        if not B then return \"***error:B***\" end\
        local bearing = dcsCommon.bearingInDegreesFromAtoB(B, A) -- returns 0..360\
        if bearing < 23 then return \"North\" end \
        if bearing < 68 then return \"NE\" end\
        if bearing < 112 then return \"East\" end \
        if bearing < 158 then return \"SE\" end \
        if bearing < 202 then return \"South\" end \
        if bearing < 248 then return \"SW\" end \
        if bearing < 292 then return \"West\" end\
        if bearing < 338 then return \"NW\" end \
        return \"North\"\
    end\
    \
    function dcsCommon.bearing2degrees(inRad)\
        local degrees = inRad / math.pi * 180\
        if degrees < 0 then degrees = degrees + 360 end \
        if degrees > 360 then degrees = degrees - 360 end \
        return degrees \
    end\
    \
    function dcsCommon.bearing2compass(inrad)\
        local bearing = math.floor(inrad / math.pi * 180)\
        if bearing < 0 then bearing = bearing + 360 end\
        if bearing > 360 then bearing = bearing - 360 end\
        return dcsCommon.bearingdegrees2compass(bearing)\
    end\
    \
    function dcsCommon.bearingdegrees2compass(bearing)\
        if bearing < 23 then return \"North\" end \
        if bearing < 68 then return \"NE\" end\
        if bearing < 112 then return \"East\" end \
        if bearing < 158 then return \"SE\" end \
        if bearing < 202 then return \"South\" end \
        if bearing < 248 then return \"SW\" end \
        if bearing < 292 then return \"West\" end\
        if bearing < 338 then return \"NW\" end \
        return \"North\"\
    end\
    \
    function dcsCommon.clockPositionOfARelativeToB(A, B, headingOfBInDegrees)\
        -- o'clock notation \
        if not A then return \"***error:A***\" end\
        if not B then return \"***error:B***\" end\
        if not headingOfBInDegrees then headingOfBInDegrees = 0 end \
        local bearing = dcsCommon.bearingInDegreesFromAtoB(B, A) -- returns 0..360 \
        bearing = bearing - headingOfBInDegrees\
        return dcsCommon.getClockDirection(bearing)\
    end \
    \
    -- given a heading, return clock with 0 being 12, 180 being 6 etc.\
    function dcsCommon.getClockDirection(direction) -- inspired by cws, improvements my own\
        if not direction then return 0 end\
        direction = math.fmod (direction, 360)\
        while direction < 0 do \
            direction = direction + 360\
        end\
        while direction >= 360 do \
            direction = direction - 360\
        end\
        if direction < 15 then -- special case 12 o'clock past 12 o'clock\
            return 12\
        end\
    \
        direction = direction + 15 -- add offset so we get all other times correct\
        return math.floor(direction/30)\
    \
    end\
\
    function dcsCommon.getGeneralDirection(direction) -- inspired by cws, improvements my own\
        if not direction then return \"unkown\" end\
        direction = math.fmod (direction, 360)\
        while direction < 0 do \
            direction = direction + 360\
        end\
        while direction >= 360 do \
            direction = direction - 360\
        end\
        if direction < 45 then return \"ahead\" end    \
        if direction < 135 then return \"right\" end\
        if direction < 225 then return \"behind\" end\
        if direction < 315 then return \"left\" end \
        return \"ahead\"\
    end\
    \
    function dcsCommon.getNauticalDirection(direction) -- inspired by cws, improvements my own\
        if not direction then return \"unkown\" end\
        direction = math.fmod (direction, 360)\
        while direction < 0 do \
            direction = direction + 360\
        end\
        while direction >= 360 do \
            direction = direction - 360\
        end\
        if direction < 45 then return \"ahead\" end    \
        if direction < 135 then return \"starboard\" end\
        if direction < 225 then return \"aft\" end\
        if direction < 315 then return \"port\" end \
        return \"ahead\"\
    end\
\
    function dcsCommon.aspectByDirection(direction) -- inspired by cws, improvements my own\
        if not direction then return \"unkown\" end\
        direction = math.fmod (direction, 360)\
        while direction < 0 do \
            direction = direction + 360\
        end\
        while direction >= 360 do \
            direction = direction - 360\
        end\
        \
        if direction < 45 then return \"hot\" end    \
        if direction < 135 then return \"beam\" end\
        if direction < 225 then return \"drag\" end\
        if direction < 315 then return \"beam\" end \
        return \"hot\"\
    end\
    \
    function dcsCommon.whichSideOfMine(theUnit, target) -- returs two values: -1/1 = left/right and \"left\"/\"right\" \
        if not theUnit then return nil end \
        if not target then return nil end \
        local uDOF = theUnit:getPosition() -- returns p, x, y, z Vec3\
        -- with x, y, z being the normalised vectors for right, up, forward \
        local heading = math.atan2(uDOF.x.z, uDOF.x.x) -- returns rads\
        if heading < 0 then\
            heading = heading + 2 * math.pi    -- put heading in range of 0 to 2*pi\
        end\
        -- heading now runs from 0 through 2Pi\
        local A = uDOF.p\
        local B = target:getPoint() \
         \
        -- now get bearing from theUnit to target  \
        local dx = B.x - A.x\
        local dz = B.z - A.z\
        local bearing = math.atan2(dz, dx) -- in rads\
        if bearing < 0 then\
            bearing = bearing + 2 * math.pi    -- make bearing 0 to 2*pi\
        end\
\
        -- we now have bearing to B, and own heading. \
        -- subtract own heading from bearing to see at what \
        -- bearing target would be if we 'turned the world' so\
        -- that theUnit is heading 0\
        local dBearing = bearing - heading\
        -- if result < 0 or > Pi (=180°), target is left from us\
        if dBearing < 0 or dBearing > math.pi then return -1, \"left\" end\
        return 1, \"right\"\
        -- note: no separate case for straight in front or behind\
    end\
    \
    -- Distance of point p to line defined by p1,p2 \
    -- only on XZ map \
    function dcsCommon.distanceOfPointPToLineXZ(p, p1, p2)\
        local x21 = p2.x - p1.x \
        local y10 = p1.z - p.z \
        local x10 = p1.x - p.x \
        local y21 = p2.z - p1.z \
        local numer = math.abs((x21*y10) - (x10 * y21))\
        local denom = math.sqrt(x21 * x21 + y21 * y21)\
        local dist = numer/denom \
        return dist \
    end\
    \
    function dcsCommon.randomDegrees()\
        local degrees = math.random(360) * 3.14152 / 180\
        return degrees\
    end\
\
    function dcsCommon.randomPercent()\
        local percent = math.random(100)/100\
        return percent\
    end\
\
    function dcsCommon.randomPointOnPerimeter(sourceRadius, x, z) \
        return dcsCommon.randomPointInCircle(sourceRadius, sourceRadius-1, x, z)\
    end\
\
    function dcsCommon.randomPointInCircle(sourceRadius, innerRadius, x, z)\
        if not x then x = 0 end\
        if not z then z = 0 end \
        \
        --local y = 0\
        if not innerRadius then innerRadius = 0 end        \
        if innerRadius < 0 then innerRadius = 0 end\
        \
        local percent = dcsCommon.randomPercent() -- 1 / math.random(100)\
        -- now lets get a random degree\
        local degrees = dcsCommon.randomDegrees() -- math.random(360) * 3.14152 / 180 -- ok, it's actually radiants. \
        local r = (sourceRadius-innerRadius) * percent \
        x = x + (innerRadius + r) * math.cos(degrees)\
        z = z + (innerRadius + r) * math.sin(degrees)\
    \
        local thePoint = {}\
        thePoint.x = x\
        thePoint.y = 0\
        thePoint.z = z \
        \
        return thePoint, degrees\
    end\
\
    function dcsCommon.newPointAtDegreesRange(p1, degrees, radius)\
        local rads = degrees * 3.14152 / 180\
        local p2 = dcsCommon.newPointAtAngleRange(p1, rads, radius)\
        return p2 \
    end\
    \
    function dcsCommon.newPointAtAngleRange(p1, angle, radius)\
        local p2 = {}\
        p2.x = p1.x + radius * math.cos(angle)\
        p2.y = p1.y \
        p2.z = p1.z + radius * math.sin(angle)\
        return p2 \
    end\
\
    -- get group location: get the group's location by \
    -- accessing the fist existing, alive member of the group that it finds\
    function dcsCommon.getGroupLocation(group, verbose, gName)\
        if not verbose then verbose = false end \
        -- nifty trick from mist: make this work with group and group name\
        if type(group) == 'string' then -- group name\
            group = Group.getByName(group)\
        end\
        \
        -- get all units\
        local allUnits = group:getUnits()\
        if not allUnits then \
            if verbose then \
                trigger.action.outText(\"++++common: no group location for <\" .. gName .. \">, skipping.\", 30)\
            end\
            return nil \
        end \
\
        -- iterate through all members of group until one is alive and exists\
        for index, theUnit in pairs(allUnits) do \
            if (theUnit:isExist() and theUnit:getLife() > 0) then \
                return theUnit:getPosition().p \
            end\
        end\
\
        -- if we get here, there was no live unit \
        return nil \
        \
    end\
\
    -- get the group's first Unit that exists and is \
    -- alive \
    function dcsCommon.getGroupUnit(group)\
        if not group then return nil  end\
        \
        -- nifty trick from mist: make this work with group and group name\
        if type(group) == 'string' then -- group name\
            group = Group.getByName(group)\
        end\
        \
        if not group:isExist() then return nil end \
        \
        -- get all units\
        local allUnits = group:getUnits()\
\
        -- iterate through all members of group until one is alive and exists\
        for index, theUnit in pairs(allUnits) do \
            if Unit.isExist(theUnit) and theUnit:getLife() > 0 then \
                return theUnit\
            end;\
        end\
\
        -- if we get here, there was no live unit \
        return nil \
        \
    end\
\
    -- and here the alias\
    function dcsCommon.getFirstLivingUnit(group)\
        return dcsCommon.getGroupUnit(group)\
    end\
    \
    -- isGroupAlive returns true if there is at least one unit in the group that isn't dead\
    function dcsCommon.isGroupAlive(group)\
        return (dcsCommon.getGroupUnit(group) ~= nil) \
    end\
\
    function dcsCommon.getLiveGroupUnits(group)\
        -- nifty trick from mist: make this work with group and group name\
        if type(group) == 'string' then -- group name\
            group = Group.getByName(group)\
        end\
        \
        local liveUnits = {}\
        -- get all units\
        local allUnits = group:getUnits()\
\
        -- iterate through all members of group until one is alive and exists\
        for index, theUnit in pairs(allUnits) do \
            if (theUnit:isExist() and theUnit:getLife() > 0) then \
                table.insert(liveUnits, theUnit) \
            end;\
        end\
\
        -- if we get here, there was no live unit \
        return liveUnits\
    end\
\
    function dcsCommon.getGroupTypeString(group) -- convert into comma separated types \
        if not group then \
            trigger.action.outText(\"+++cmn getGroupTypeString: nil group\", 30)\
            return \"\" \
        end\
        if not dcsCommon.isGroupAlive(group) then \
            trigger.action.outText(\"+++cmn getGroupTypeString: dead group\", 30)\
            return \"\" \
        end \
        local theTypes = \"\"\
        local liveUnits = dcsCommon.getLiveGroupUnits(group)\
        for i=1, #liveUnits do \
            if i > 1 then theTypes = theTypes .. \",\" end\
            theTypes = theTypes .. liveUnits[i]:getTypeName()\
        end\
        return theTypes\
    end\
\
    function dcsCommon.getGroupTypes(group) \
        if not group then \
            trigger.action.outText(\"+++cmn getGroupTypes: nil group\", 30)\
            return {}\
        end\
        if not dcsCommon.isGroupAlive(group) then \
            trigger.action.outText(\"+++cmn getGroupTypes: dead group\", 30)\
            return {}\
        end \
        local liveUnits = dcsCommon.getLiveGroupUnits(group)\
        local unitTypes = {}\
        for i=1, #liveUnits do \
            table.insert(unitTypes, liveUnits[i]:getTypeName())\
        end\
        return unitTypes\
    end\
\
    function dcsCommon.getEnemyCoalitionFor(aCoalition)\
        if type(aCoalition) == \"string\" then \
            aCoalition = aCoalition:lower()\
            if aCoalition == \"red\" then return 2 end\
            if aCoalition == \"blue\" then return 1 end\
            return nil \
        end\
        if aCoalition == 1 then return 2 end\
        if aCoalition == 2 then return 1 end\
        return nil\
    end\
\
    function dcsCommon.getACountryForCoalition(aCoalition)\
        -- scan the table of countries and get the first country that is part of aCoalition\
        -- this is useful if you want to create troops for a coalition but don't know the\
        -- coalition's countries \
        -- we start with id=0 (Russia), go to id=85 (Slovenia), but skip id = 14\
        local i = 0\
        while i < dcsCommon.maxCountry do -- 86 do \
            if i ~= 14 then \
                if (coalition.getCountryCoalition(i) == aCoalition) then return i end\
            end\
            i = i + 1\
        end\
        \
        return nil\
    end\
    \
    function dcsCommon.getCountriesForCoalition(aCoalition)\
        if not aCoalition then aCoalition = 0 end \
        local allCty = {}\
        \
        local i = 0\
        while i < dcsCommon.maxCountry do \
            if i ~= 14 then -- there is no county 14\
                if (coalition.getCountryCoalition(i) == aCoalition) then \
                    table.insert(allCty, i) \
                end\
            end\
            i = i + 1\
        end\
        return allCty\
    end\
--\
--\
-- C A L L B A C K   H A N D L E R \
--\
--\
\
    -- installing callbacks\
    -- based on mist, with optional additional hooks for pre- and post-\
    -- processing of the event\
    -- when filtering occurs in pre, an alternative 'rejected' handler can be called \
    function dcsCommon.addEventHandler(f, pre, post, rejected) -- returns ID \
        local handler = {} -- build a wrapper and connect the onEvent\
        handler.id = dcsCommon.uuid(\"eventHandler\")\
        handler.f = f -- the callback itself\
        if (rejected) then handler.rejected = rejected end\
        -- now set up pre- and post-processors. defaults are set in place\
        -- so pre and post are optional. If pre returns false, the callback will\
        -- not be invoked\
        if (pre) then handler.pre = pre else handler.pre = dcsCommon.preCall end\
        if (post) then handler.post = post else handler.post = dcsCommon.postCall end\
        function handler:onEvent(event)\
            if not self.pre(event) then \
                if dcsCommon.verbose then\
                end\
                if (self.rejected) then self.rejected(event) end \
                return\
            end\
            self.f(event) -- call the handler\
            self.post(event) -- do post-processing\
        end\
        world.addEventHandler(handler)\
        return handler.id\
    end\
\
    function dcsCommon.preCall(e)\
        -- we can filter here\
        -- if we return false, the call is abortet\
        if dcsCommon.verbose then\
            trigger.action.outText(\"event \" .. e.id .. \" received: PRE-PROCESSING\", 10)\
        end\
        return true;\
    end;\
\
    function dcsCommon.postCall(e)\
        -- we do pos proccing here \
        if dcsCommon.verbose then\
            trigger.action.outText(\"event \" .. e.id .. \" received: post proc\", 10)\
        end\
    end\
    \
    -- highly specific eventhandler for one event only\
    -- based on above, with direct filtering built in; skips pre\
    -- but does post\
    function dcsCommon.addEventHandlerForEventTypes(f, evTypes, post, rejected) -- returns ID \
        local handler = {} -- build a wrapper and connect the onEvent\
        dcsCommon.cbID = dcsCommon.cbID + 1 -- increment unique count\
        handler.id = dcsCommon.cbID\
        handler.what = evTypes\
        if (rejected) then handler.rejected = rejected end \
        \
        handler.f = f -- set the callback itself\
        -- now set up post-processor. pre is hard-coded to match evType\
        -- post is optional. If event.id is not in evTypes, the callback will\
        -- not be invoked\
        if (post) then handler.post = post else handler.post = dcsCommon.postCall end\
        function handler:onEvent(event)\
            hasMatch = false;\
            for key, evType in pairs(self.what) do\
                if evType == event.id then\
                    hasMatch = true;\
                    break;\
                end;\
            end;\
            if not hasMatch then \
                if dcsCommon.verbose then\
                    trigger.action.outText(\"event \" .. e.id .. \" discarded - not in whitelist evTypes\", 10)\
                end\
                if (self.rejected) then self.rejected(event) end \
                return;\
            end;\
            \
            self.f(event) -- call the actual handler as passed to us\
            self.post(event) -- do post-processing \
        end\
        world.addEventHandler(handler) -- add to event handlers\
        return handler.id\
    end\
    \
    \
    \
    -- remove event handler / callback, identical to Mist \
    -- note we don't call world.removeEventHandler, but rather directly \
    -- access world.eventHandlers directly and remove kvp directly.\
    function dcsCommon.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\
\
--\
--\
-- C L O N I N G \
--\
--\
    -- topClone is a shallow clone of orig, only top level is iterated,\
    -- all values are ref-copied\
    function dcsCommon.topClone(orig)\
        if not orig then return nil end \
        local orig_type = type(orig)\
        local copy\
        if orig_type == 'table' then\
            copy = {}\
            for orig_key, orig_value in pairs(orig) do\
                copy[orig_key] = orig_value\
            end\
        else -- number, string, boolean, etc\
            copy = orig\
        end\
        return copy\
    end\
\
    -- clone is a recursive clone which will also clone\
    -- deeper levels, as used in units \
    function dcsCommon.clone(orig, stripMeta)\
        if not orig then return nil end \
        local orig_type = type(orig)\
        local copy\
        if orig_type == 'table' then\
            copy = {}\
            for orig_key, orig_value in next, orig, nil do\
                copy[dcsCommon.clone(orig_key)] = dcsCommon.clone(orig_value)\
            end\
            if not stripMeta then \
                -- also connect meta data\
                setmetatable(copy, dcsCommon.clone(getmetatable(orig)))\
            else \
                -- strip all except string, and for strings use a fresh string \
                if type(copy) == \"string\" then \
                    local tmp = \"\"\
                    tmp = tmp .. copy -- will get rid of any foreign metas for string \
                    copy = tmp \
                end\
            end\
        elseif orig_type == \"string\" then \
            local tmp = \"\"\
            copy = tmp .. orig \
        else -- number, string, boolean, etc\
            copy = orig\
        end\
        return copy\
    end\
\
    function dcsCommon.copyArray(inArray)\
        if not inArray then return nil end \
        \
        -- warning: this is a ref copy!\
        local theCopy = {}\
        for idx, element in pairs(inArray) do \
            table.insert(theCopy, element)\
        end\
        return theCopy \
    end\
--\
-- \
-- S P A W N I N G \
-- \
-- \
\
    function dcsCommon.createEmptyGroundGroupData (name)\
        local theGroup = {} -- empty group\
        theGroup.visible = false\
        theGroup.taskSelected = true\
        -- theGroup.route = {}\
        -- theGroup.groupId = id\
        theGroup.tasks = {}\
        -- theGroup.hidden = false -- hidden on f10?\
\
        theGroup.units = { } -- insert units here! -- use addUnitToGroupData\
\
        theGroup.x = 0\
        theGroup.y = 0\
        theGroup.name = name\
        -- theGroup.start_time = 0\
        theGroup.task = \"Ground Nothing\"\
        \
        return theGroup\
    end;\
\
    function dcsCommon.createEmptyAircraftGroupData (name)\
        local theGroup = dcsCommon.createEmptyGroundGroupData(name)--{} -- empty group\
\
        theGroup.task = \"Nothing\" -- can be others, like Transport, CAS, etc\
        -- returns with empty route\
        theGroup.route = dcsCommon.createEmptyAircraftRouteData() -- we can add points here \
        return theGroup\
    end;\
\
    function dcsCommon.createAircraftRoutePointData(x, z, altitudeInFeet, knots, altType, action)\
        local rp = {}\
        rp.x = x\
        rp.y = z\
        rp.action = \"Turning Point\"\
        rp.type = \"Turning Point\"\
        if action then rp.action = action; rp.type = action end -- warning: may not be correct, need to verify later\
        rp.alt = altitudeInFeet * 0.3048 -- in m \
        rp.speed = knots * 0.514444 -- we use m/s\
        rp.alt_type = \"BARO\"\
        if (altType) then rp.alt_type = altType end \
        return rp\
    end\
\
    function dcsCommon.addRoutePointDataToRouteData(inRoute, x, z, altitudeInFeet, knots, altType, action)\
        local p = dcsCommon.createAircraftRoutePointData(x, z, altitudeInFeet, knots, altType, action)\
        local thePoints = inRoute.points \
        table.insert(thePoints, p)\
    end\
    \
    function dcsCommon.addRoutePointDataToGroupData(group, x, z, altitudeInFeet, knots, altType, action)\
        if not group.route then group.route = dcsCommon.createEmptyAircraftRouteData() end\
        local theRoute = group.route \
        dcsCommon.addRoutePointDataToRouteData(theRoute, x, z, altitudeInFeet, knots, altType, action)\
    end\
\
    function dcsCommon.addRoutePointForGroupData(theGroup, theRP)\
        if not theGroup then return end \
        if not theGroup.route then theGroup.route = dcsCommon.createEmptyAircraftRouteData() end\
        \
        local theRoute = theGroup.route \
        local thePoints = theRoute.points \
        table.insert(thePoints, theRP)\
    end\
    \
    function dcsCommon.createEmptyAircraftRouteData()\
        local route = {}\
        route.points = {}\
        return route\
    end\
\
    function dcsCommon.createTakeOffFromGroundRoutePointData(pt, isHot) -- vec 3!\
        if not pt then return nil end \
            \
        local rp = {}    \
        rp.x = pt.x\
        rp.y = pt.z\
        rp.alt = pt.y \
        if isHot then \
            rp.action = \"From Ground Area Hot\"\
            rp.type = \"TakeOffGroundHot\" \
        else\
            rp.action = \"From Ground Area\" -- add \" Hot\" if hot\
            rp.type = \"TakeOffGround\" -- add \"Hot\" (NO blank) if hot\
        end\
        rp.speed = 10 --  that's 36 km/h \
        rp.alt_type = \"BARO\"\
        return rp\
    end\
    \
    function dcsCommon.createTakeOffFromParkingRoutePointData(aerodrome)\
        if not aerodrome then return nil end \
            \
        local rp = {}    \
        local freeParkingSlot = dcsCommon.getFirstFreeParkingSlot(aerodrome, 104) -- get big slot first \
        if not freeParkingSlot then \
            freeParkingSlot = dcsCommon.getFirstFreeParkingSlot(aerodrome) -- try any size\
        end\
            \
        if not freeParkingSlot then \
            trigger.action.outText(\"civA: no free parking at \" .. aerodrome:getName(), 30)\
            return nil \
        end\
            \
        local p = freeParkingSlot.vTerminalPos\
            \
        rp.airdromeId = aerodrome:getID() \
        rp.x = p.x\
        rp.y = p.z\
        rp.alt = p.y \
        rp.action = \"From Parking Area\"\
        rp.type = \"TakeOffParking\"\
            \
        rp.speed = 100 --  that's 360 km/h \
        rp.alt_type = \"BARO\"\
        return rp\
    end\
\
    function dcsCommon.createOverheadAirdromeRoutePointData(aerodrome)\
        if not aerodrome then return nil end \
        local rp = {}            \
        local p = aerodrome:getPoint()\
        rp.x = p.x\
        rp.y = p.z\
        rp.alt = p.y + 2000 -- 6000 ft overhead\
        rp.action = \"Turning Point\"\
        rp.type = \"Turning Point\"\
            \
        rp.speed = 133; -- in m/s? If so, that's 360 km/h \
        rp.alt_type = \"BARO\"\
        return rp\
    end\
    function dcsCommon.createOverheadAirdromeRoutPintData(aerodrome) -- backwards-compat to typo \
        return dcsCommon.createOverheadAirdromeRoutePointData(aerodrome)\
    end \
    \
\
    function dcsCommon.createLandAtAerodromeRoutePointData(aerodrome)\
        if not aerodrome then return nil end \
            \
        local rp = {}            \
        local p = aerodrome:getPoint()\
        rp.airdromeId = aerodrome:getID() \
        rp.x = p.x\
        rp.y = p.z\
        rp.alt = land.getHeight({x=p.x, y=p.z}) --p.y \
        rp.action = \"Landing\"\
        rp.type = \"Land\"\
            \
        rp.speed = 100; -- in m/s? If so, that's 360 km/h \
        rp.alt_type = \"BARO\"\
        return rp\
    end\
\
    function dcsCommon.createSimpleRoutePointData(p, alt, speed)\
        if not speed then speed = 133 end \
        if not alt then alt = 8000 end -- 24'000 feet \
        local rp = {}\
        rp.x = p.x\
        rp.y = p.z\
        rp.alt = alt\
        rp.action = \"Turning Point\"\
        rp.type = \"Turning Point\"\
            \
        rp.speed = speed; -- in m/s? If so, that's 360 km/h \
        rp.alt_type = \"BARO\"\
        return rp\
    end \
    \
    function dcsCommon.createRPFormationData(findex) -- must be added as \"task\" to an RP. use 4 for Echelon right\
        local task = {}\
        task.id = \"ComboTask\"\
        local params = {}\
        task.params = params\
        local tasks = {}\
        params.tasks = tasks\
        local t1 = {}\
        tasks[1] = t1\
        t1.number = 1\
        t1.auto = false \
        t1.id = \"WrappedAction\"\
        t1.enabled = true\
        local t1p = {}\
        t1.params = t1p\
        local action = {}\
        t1p.action = action \
        action.id = \"Option\"\
        local ap = {}\
        action.params = ap\
        ap.variantIndex = 3\
        ap.name = 5 -- AI.Option.Air.ID 5 = Formation \
        ap.formationIndex = findex -- 4 is echelon_right\
        ap.value = 262147\
        \
        return task \
    end\
\
    function dcsCommon.addTaskDataToRP(theTask, theGroup, rpIndex)\
        local theRoute = theGroup.route\
        local thePoints = theRoute.points\
        local rp = thePoints[rpIndex]\
        rp.task = theTask\
    end\
    \
    -- create a minimal payload table that is compatible with creating \
    -- a unit. you may need to alter this before adding the unit to\
    -- the mission. all params optional \
    function dcsCommon.createPayload(fuel, flare, chaff, gun) \
        local payload = {}\
        payload.pylons = {}\
        if not fuel then fuel = 1000 end -- in kg. check against fuelMassMax in type desc\
        if not flare then flare = 0 end\
        if not chaff then chaff = 0 end\
        if not gun then gun = 0 end\
        return payload \
        \
    end\
\
    function dcsCommon.createCallsign(cs) \
        local callsign = {}\
        callsign[1] = 1\
        callsign[2] = 1\
        callsign[3] = 1\
        if not cs then cs = \"Enfield11\" end\
        callsign.name = cs\
        return callsign\
    end\
    \
\
    -- create the data table required to spawn a unit.\
    -- unit types are defined in https://github.com/mrSkortch/DCS-miscScripts/tree/master/ObjectDB\
    function dcsCommon.createGroundUnitData(name, unitType, transportable)\
        local theUnit = {}\
        unitType = dcsCommon.trim(unitType)\
        theUnit.type = unitType -- e.g. \"LAV-25\",\
        if not transportable then transportable = false end -- elaborate, not requried code\
        theUnit.transportable = {[\"randomTransportable\"] = transportable} \
        -- theUnit.unitId = id \
        theUnit.skill = \"Average\" -- always average \
        theUnit.x = 0 -- make it zero, zero!\
        theUnit.y = 0\
        theUnit.name = name\
        theUnit.playerCanDrive = false\
        theUnit.heading = 0\
        return theUnit\
    end \
\
    function dcsCommon.createAircraftUnitData(name, unitType, transportable, altitude, speed, heading)\
        local theAirUnit = dcsCommon.createGroundUnitData(name, unitType, transportable)\
        theAirUnit.alt = 100 -- make it 100m\
        if altitude then theAirUnit.alt = altitude end \
        theAirUnit.alt_type = \"RADIO\" -- AGL\
        theAirUnit.speed = 77 -- m/s --> 150 knots\
        if speed then theAirUnit.speed = speed end \
        if heading then theAirUnit.heading = heading end \
        theAirUnit.payload = dcsCommon.createPayload()\
        theAirUnit.callsign = dcsCommon.createCallsign()\
        return theAirUnit\
    end\
    \
\
    function dcsCommon.addUnitToGroupData(theUnit, theGroup, dx, dy, heading)\
        -- add a unit to a group, and place it at dx, dy of group's position,\
        -- taking into account unit's own current location\
        if not dx then dx = 0 end\
        if not dy then dy = 0 end\
        if not heading then heading = 0 end\
        theUnit.x = theUnit.x + dx + theGroup.x\
        theUnit.y = theUnit.y + dy + theGroup.y \
        theUnit.heading = heading\
        table.insert(theGroup.units, theUnit)\
    end;\
\
    function dcsCommon.createSingleUnitGroup(name, theUnitType, x, z, heading) \
        -- create the container \
        local theNewGroup = dcsCommon.createEmptyGroundGroupData(name)\
        local aUnit = {}\
        aUnit = dcsCommon.createGroundUnitData(name .. \"-1\", theUnitType, false)\
--        trigger.action.outText(\"dcsCommon - unit name retval \" .. aUnit.name, 30)\
        dcsCommon.addUnitToGroupData(aUnit, theNewGroup, x, z, heading)\
        return theNewGroup\
    end\
    \
\
    function dcsCommon.arrangeGroupDataIntoFormation(theNewGroup, radius, minDist, formation, innerRadius)\
        -- formations:\
        --    (default) \"line\" (left to right along x) -- that is Y direction\
        --    \"line_v\" a line top to bottom -- that is X direction\
        --    \"chevron\" - left to right middle too top\
        --    \"scattered\", \"random\" -- random, innerRadius used to clear area in center\
        --       \"circle\", \"circle_forward\" -- circle, forward facing\
        --    \"circle_in\" -- circle, inwarf facing\
        --    \"circle_out\" -- circle, outward facing\
        --    \"grid\", \"square\", \"rect\" -- optimal rectangle\
        --    \"2cols\", \"2deep\" -- 2 columns, n deep \
        --    \"2wide\" -- 2 columns wide, 2 deep \
\
        local num = #theNewGroup.units \
        \
        -- now do the formation stuff\
        -- make sure that they keep minimum  distance \
        if formation == \"LINE_V\" then \
            -- top to bottom in zone (heding 0). -- will run through x-coordinate \
            -- use entire radius top to bottom \
            local currX = -radius\
            local increment = radius * 2/(num - 1) -- MUST NOT TRY WITH 1 UNIT!\
            for i=1, num do\
            \
                local u = theNewGroup.units[i]\
                u.x = currX\
                currX = currX + increment\
            end\
        \
        elseif formation == \"LINE\" then \
            -- left to right in zone. runs through Y\
            -- left and right are y because at heading 0, forward is x (not y as expected)\
            -- if only one, place in middle of circle and be done \
            if num == 1 then \
                -- nothing. just stay in the middle \
            else \
                local currY = -radius\
                local increment = radius * 2/(num - 1) -- MUST NOT TRY WITH 1 UNIT!\
                for i=1, num do\
                    local u = theNewGroup.units[i]\
                    u.y = currY\
                    currY = currY + increment\
                end    \
            end \
            \
        elseif formation == \"CHEVRON\" then \
            -- left to right in zone. runs through Y\
            -- left and right are y because at heading 0, forward is x (not y as expected)\
            local currY = -radius\
            local currX = 0\
            local incrementY = radius * 2/(num - 1) -- MUST NOT TRY WITH 1 UNIT!\
            local incrementX = radius * 2/(num - 1) -- MUST NOT TRY WITH 1 UNIT!\
            for i=1, num do\
                local u = theNewGroup.units[i]\
                u.x = currX\
                u.y = currY\
                -- calc coords for NEXT iteration\
                currY = currY + incrementY -- march left to right\
                if i < num / 2 then -- march up\
                    currX = currX + incrementX \
                elseif i == num / 2 then -- even number, keep height\
                    currX = currX + 0 \
                else \
                    currX = currX - incrementX -- march down \
                end \
                -- note: when unit number even, the wedge is sloped. may need an odd/even test for better looks\
            end    \
\
        elseif formation == \"SCATTERED\" or formation == \"RANDOM\" then \
            -- use randomPointInCircle and tehn iterate over all vehicles for mindelta\
            processedUnits = {}\
            if not minDist then minDist = 10 end \
            for i=1, num do\
                local emergencyBreak = 1 -- prevent endless loop\
                local lowDist = 10000\
                local uPoint = {}\
                local thePoint = {}\
                repeat     -- get random point until mindistance to all is kept or emergencybreak\
                    thePoint = dcsCommon.randomPointInCircle(radius, innerRadius) -- returns x, 0, z\
                    -- check if too close to others\
                    for idx, rUnit in pairs(processedUnits) do -- get min dist to all positioned units\
                        --trigger.action.outText(\"rPnt: thePoint =  \" .. dcsCommon.point2text(thePoint), 30)\
                        uPoint.x = rUnit.x\
                        uPoint.y = 0\
                        uPoint.z = rUnit.y \
                        --trigger.action.outText(\"rPnt: uPoint =  \" .. dcsCommon.point2text(uPoint), 30)\
                        local dist = dcsCommon.dist(thePoint, uPoint) -- measure distance to unit\
                        if (dist < lowDist) then lowDist = dist end\
                    end\
                    emergencyBreak = emergencyBreak + 1\
                until (emergencyBreak > 20) or (lowDist > minDist)\
                -- we have random x, y \
                local u = theNewGroup.units[i] -- get unit to position\
                u.x = thePoint.x\
                u.y = thePoint.z -- z --> y mapping! \
                -- now add the unit to the 'processed' set \
                table.insert(processedUnits, u)\
            end    \
\
        elseif dcsCommon.stringStartsWith(formation, \"CIRCLE\") then\
            -- units are arranged on perimeter of circle defined by radius \
            local currAngle = 0\
            local angleInc = 2 * 3.14157 / num -- increase per spoke \
            for i=1, num do\
                local u = theNewGroup.units[i] -- get unit \
                u.x = radius * math.cos(currAngle)\
                u.y = radius * math.sin(currAngle)\
                \
                -- now baldower out heading \
                -- circle, circle_forward no modifier of heading\
                if dcsCommon.stringStartsWith(formation, \"CIRCLE_IN\") then \
                    -- make the heading inward faceing - that's angle + pi\
                    u.heading = u.heading + currAngle + 3.14157\
                elseif dcsCommon.stringStartsWith(formation, \"CIRCLE_OUT\") then \
                    u.heading = u.heading + currAngle + 0\
                end\
\
                currAngle = currAngle + angleInc\
            end\
        elseif formation == \"GRID\" or formation == \"SQUARE\" or formation == \"RECT\" then \
            if num < 2 then return end \
            -- arrange units in an w x h grid\
            -- e-g- 12 units = 4 x 3. \
            -- calculate w \
            local w = math.floor(num^(0.5) + 0.5)\
            dcsCommon.arrangeGroupInNColumns(theNewGroup, w, radius)\
            \
        elseif formation == \"2DEEP\" or formation == \"2COLS\" then\
            if num < 2 then return end \
            -- arrange units in an 2 x h grid\
            local w = 2\
            dcsCommon.arrangeGroupInNColumnsDeep(theNewGroup, w, radius)\
\
        elseif formation == \"2WIDE\" then\
            if num < 2 then return end \
            -- arrange units in an 2 x h grid\
            local w = 2\
            dcsCommon.arrangeGroupInNColumns(theNewGroup, w, radius)\
        else \
            trigger.action.outText(\"dcsCommon - unknown formation: \" .. formation, 30)\
        end\
    \
    end\
    \
    function dcsCommon.arrangeGroupInNColumns(theNewGroup, w, radius)\
        local num = #theNewGroup.units\
        local h = math.floor(num / w)\
        if (num % w) > 0 then \
            h = h + 1\
        end\
        local i = 1\
        local xInc = 0 \
        if w > 1 then xInc = 2 * radius / (w-1) end\
        local yInc = 0\
        if h > 1 then yInc = 2 * radius / (h-1) end \
        local currY = radius \
        if h < 2 then currY = 0 end -- special:_ place in Y middle if only one row)\
        while h > 0 do \
            local currX = radius \
            local wCnt = w \
            while wCnt > 0 and (i <= num) do \
                local u = theNewGroup.units[i] -- get unit \
                u.x = currX\
                u.y = currY\
                currX = currX - xInc\
                wCnt = wCnt - 1\
                i = i + 1\
            end\
            currY = currY - yInc \
            h = h - 1\
        end\
    end\
    \
    function dcsCommon.arrangeGroupInNColumnsDeep(theNewGroup, w, radius)\
        local num = #theNewGroup.units\
        local h = math.floor(num / w)\
        if (num % w) > 0 then \
            h = h + 1\
        end\
        local i = 1\
        local yInc = 0 \
        if w > 1 then yInc = 2 * radius / (w-1) end\
        local xInc = 0\
        if h > 1 then xInc = 2 * radius / (h-1) end \
        local currX = radius \
        if h < 2 then currX = 0 end -- special:_ place in Y middle if only one row)\
        while h > 0 do \
            local currY = radius \
            local wCnt = w \
            while wCnt > 0 and (i <= num) do \
                local u = theNewGroup.units[i] -- get unit \
                u.x = currX\
                u.y = currY\
                currY = currY - yInc\
                wCnt = wCnt - 1\
                i = i + 1\
            end\
            currX = currX - xInc \
            h = h - 1\
        end\
    end\
    \
    \
    function dcsCommon.createGroundGroupWithUnits(name, theUnitTypes, radius, minDist, formation, innerRadius, liveries)\
        -- liveries is indexed by typeName and provides alternate livery names \
        -- from default.\
        if not minDist then minDist = 4 end -- meters\
        if not formation then formation = \"line\" end \
        if not radius then radius = 30 end -- meters \
        if not innerRadius then innerRadius = 0 end\
        if not liveries then liveries = {} end \
        formation = formation:upper()\
        -- theUnitTypes can be either a single string or a table of strings\
        -- see here for TypeName https://github.com/mrSkortch/DCS-miscScripts/tree/master/ObjectDB\
        -- formation defines how the units are going to be arranged in the\
        -- formation specified. \
        -- formations:\
        --    (default) \"line\" (left to right along x) -- that is Y direction\
        --    \"line_V\" a line top to bottom -- that is X direction\
        --    \"chevron\" - left to right middle too top\
        --    \"scattered\", \"random\" -- random, innerRadius used to clear area in center\
        --       \"circle\", \"circle_forward\" -- circle, forward facing\
        --    \"circle_in\" -- circle, inwarf facing\
        --    \"circle_out\" -- circle, outward facing\
\
        -- first, we create a group\
        local theNewGroup = dcsCommon.createEmptyGroundGroupData(name)\
        \
        -- now add a single unit or multiple units\
        if type(theUnitTypes) ~= \"table\" then             \
            local aUnit = {}\
            aUnit = dcsCommon.createGroundUnitData(name .. \"-1\", theUnitTypes, false)\
            dcsCommon.addUnitToGroupData(aUnit, theNewGroup, 0, 0) -- create with data at location (0,0)\
            return theNewGroup\
        end \
\
        -- if we get here, theUnitTypes is a table\
        -- now loop and create a unit for each table\
        local num = 1\
        for key, theType in pairs(theUnitTypes) do \
            -- trigger.action.outText(\"+++dcsC: creating unit \" .. name .. \"-\" .. num .. \": \" .. theType, 30)\
            local aUnit = dcsCommon.createGroundUnitData(name .. \"-\"..num, theType, false)\
            local theLivery = liveries[theType]\
            if theLivery then \
                aUnit.livery_id = theLivery\
            end \
            dcsCommon.addUnitToGroupData(aUnit, theNewGroup, 0, 0)\
            num = num + 1\
        end\
        \
        dcsCommon.arrangeGroupDataIntoFormation(theNewGroup, radius, minDist, formation, innerRadius)\
        return theNewGroup\
\
    \
    end\
\
-- create a new group, based on group in mission. Groups coords are 0,0 for group and all\
-- x,y and heading\
    function dcsCommon.createGroupDataFromLiveGroup(name, newName) \
        if not newName then newName = dcsCommon.uuid(\"uniqName\") end\
        -- get access to the group\
        local liveGroup = Group.getByName(name)\
        if not liveGroup then return nil end\
        -- get the categorty\
        local cat = liveGroup:getCategory()\
        local theNewGroup = {}\
        \
        -- create a new empty group at (0,0) \
        if cat == Group.Category.AIRPLANE or cat == Group.Category.HELICOPTER then \
            theNewGroup = dcsCommon.createEmptyAircraftGroupData(newName)\
        elseif cat == Group.Category.GROUND then\
            theNewGroup = dcsCommon.createEmptyGroudGroupData(newName)\
        else \
            trigger.action.outText(\"dcsCommon - unknown category: \" .. cat, 30)\
            return nil\
        end\
        \
\
        -- now get all units from live group and create data units\
        -- note that unit data for group has x=0, y=0\
        liveUnits = liveGroup:getUnits()\
        \
        for index, theUnit in pairs(liveUnits) do \
            -- for each unit we get the desc \
            local desc = theUnit:getDesc() -- of interest is only typename \
            local newUnit = dcsCommon.createGroundUnitData(dcsCommon.uuid(newName),\
                                                           desc.typeName,\
                                                           false)\
            -- we now basically have a ground unit at (0,0) \
            -- add mandatory fields by type\
            if cat == Group.Category.AIRPLANE or cat == Group.Category.HELICOPTER then \
                newUnit.alt = 100 -- make it 100m\
                newUnit.alt_type = \"RADIO\" -- AGL\
                newUnit.speed = 77 -- m/s --> 150 knots\
                newUnit.payload = dcsCommon.createPayload() -- empty payload\
                newUnit.callsign = dcsCommon.createCallsign() -- 'enfield11'\
                \
            elseif cat == Group.Category.GROUND then\
                -- we got all we need\
            else \
\
            end            \
            \
        end\
    \
    end;\
    \
    function dcsCommon.pointInDirectionOfPointXYY(dir, dist, p) -- dir in rad, p in XYZ returns XZZ \
        local fx = math.cos(dir)\
        local fy = math.sin(dir) \
        local p2 = {}\
        p2.x = p.x + dist * fx\
        p2.y = p.z + dist * fy\
        p2.z = p2.y -- make p2 XYY vec2/3 upcast\
        return p2\
    end\
\
    function dcsCommon.pointXpercentYdegOffAB(A, B, xPer, yDeg) -- rets xzz point \
        local bearingRad = dcsCommon.bearingFromAtoB(A, B)\
        local dist = dcsCommon.dist(A, B)\
        local deviation = bearingRad + yDeg * 0.0174533\
        local newDist = dist * xPer/100\
        local newPoint = dcsCommon.pointInDirectionOfPointXYY(deviation, newDist, A)\
        return newPoint\
    end\
\
    function dcsCommon.rotatePointAroundOriginRad(inX, inY, angle) -- angle in degrees\
        local c = math.cos(angle)\
        local s = math.sin(angle)\
        local px\
        local py \
        px = inX * c - inY * s\
        py = inX * s + inY * c\
        return px, py        \
    end\
    \
    function dcsCommon.rotatePointAroundOrigin(inX, inY, angle) -- angle in degrees\
        local rads =  3.14152 / 180 -- convert to radiants. \
        angle = angle * rads -- turns into rads\
        local px, py = dcsCommon.rotatePointAroundOriginRad(inX, inY, angle)\
        return px, py        \
    end\
    \
    function dcsCommon.rotatePointAroundPointRad(x, y, px, py, angle)\
        x = x - px \
        y = y - py\
        x, y = dcsCommon.rotatePointAroundOriginRad(x, y, angle)\
        x = x + px \
        y = y + py \
        return x, y\
    end\
\
    function dcsCommon.rotatePointAroundPointDeg(x, y, px, py, degrees)\
        x, y = dcsCommon.rotatePointAroundPointRad(x, y, px, py, degrees * 3.14152 / 180)\
        return x, y\
    end\
\
    -- rotates a Vec3-base inPoly on XZ pane around inPoint on XZ pane\
     function dcsCommon.rotatePoly3AroundVec3Rad(inPoly, inPoint, rads)\
        local outPoly = {}\
        for idx, aVertex in pairs(inPoly) do \
            local x, z = dcsCommon.rotatePointAroundPointRad(aVertex.x, aVertex.z, inPoint.x, inPoint.z, rads)        \
            local v3 = {x = x, y = aVertex.y, z = z}\
            outPoly[idx] = v3\
        end \
        return outPoly \
    end\
\
    function dcsCommon.rotateUnitData(theUnit, degrees, cx, cz)\
        if not cx then cx = 0 end\
        if not cz then cz = 0 end\
        local cy = cz \
        \
        local rads = degrees *  3.14152 / 180\
        do\
            theUnit.x = theUnit.x - cx -- MOVE TO ORIGIN OF ROTATION\
            theUnit.y = theUnit.y - cy                 \
            theUnit.x, theUnit.y = dcsCommon.rotatePointAroundOrigin(theUnit.x, theUnit.y, degrees)\
            theUnit.x = theUnit.x + cx -- MOVE BACK \
            theUnit.y = theUnit.y + cy                 \
\
            -- may also want to increase heading by degrees\
            theUnit.heading = theUnit.heading + rads \
        end\
    end\
    \
\
    function dcsCommon.rotateGroupData(theGroup, degrees, cx, cz)\
        if not cx then cx = 0 end\
        if not cz then cz = 0 end\
        local cy = cz \
        \
        local rads = degrees *  3.14152 / 180\
        -- turns all units in group around the group's center by degrees.\
        -- may also need to turn individual units by same amount\
        for i, theUnit in pairs (theGroup.units) do\
            theUnit.x = theUnit.x - cx -- MOVE TO ORIGIN OF ROTATION\
            theUnit.y = theUnit.y - cy                 \
            theUnit.x, theUnit.y = dcsCommon.rotatePointAroundOrigin(theUnit.x, theUnit.y, degrees)\
            theUnit.x = theUnit.x + cx -- MOVE BACK \
            theUnit.y = theUnit.y + cy                 \
\
            -- may also want to increase heading by degrees\
            theUnit.heading = theUnit.heading + rads \
            if theUnit.psi then \
                theUnit.psi = -theUnit.heading \
            end\
        end\
    end\
\
    function dcsCommon.offsetGroupData(theGroup, dx, dy)\
        -- add dx and dy to group's and all unit's coords\
        for i, theUnit in pairs (theGroup.units) do \
            theUnit.x = theUnit.x + dx\
            theUnit.y = theUnit.y + dy\
        end\
        \
        theGroup.x = theGroup.x + dx\
        theGroup.y = theGroup.y + dy \
    end\
    \
    function dcsCommon.moveGroupDataTo(theGroup, xAbs, yAbs)\
        local dx = xAbs-theGroup.x\
        local dy = yAbs-theGroup.y\
        dcsCommon.offsetGroupData(theGroup, dx, dy)\
    end\
    \
    -- static objectr shapes and types are defined here\
    -- https://github.com/mrSkortch/DCS-miscScripts/tree/master/ObjectDB/Statics\
    \
    function dcsCommon.createStaticObjectData(name, objType, heading, dead, cargo, mass)\
        local staticObj = {}\
        if not heading then heading = 0 end \
        if not dead then dead = false end \
        if not cargo then cargo = false end \
        objType = dcsCommon.trim(objType) \
        \
        staticObj.heading = heading\
        -- staticObj.groupId = 0\
        -- staticObj.shape_name = shape -- e.g. H-Windsock_RW\
        staticObj.type = objType  -- e.g. Windsock\
        -- [\"unitId\"] = 3,\
        staticObj.rate = 1 -- score when killed\
        staticObj.name = name\
        -- staticObj.category = \"Fortifications\",\
        staticObj.y = 0\
        staticObj.x = 0\
        staticObj.dead = dead\
        staticObj.canCargo = cargo -- to cargo\
        if cargo then \
            if not mass then mass = 1234 end \
            staticObj.mass = mass -- to cargo\
        end\
        return staticObj\
    end\
    \
    function dcsCommon.createStaticObjectDataAt(loc, name, objType, heading, dead)\
        local theData = dcsCommon.createStaticObjectData(name, objType, heading, dead)\
        theData.x = loc.x\
        theData.y = loc.z \
        return theData\
    end\
    \
    function dcsCommon.createStaticObjectForCoalitionAtLocation(theCoalition, loc, name, objType, heading, dead) \
        if not heading then heading = math.random(360) * 3.1415 / 180 end\
        local theData = dcsCommon.createStaticObjectDataAt(loc, name, objType, heading, dead)\
        local theStatic = coalition.addStaticObject(theCoalition, theData) -- warning! coalition is not country!\
        return theStatic\
    end\
    \
    function dcsCommon.createStaticObjectForCoalitionInRandomRing(theCoalition, objType, x, z, innerRadius, outerRadius, heading, alive) \
        if not outerRadius then outerRadius = innerRadius end\
        if not heading then heading = math.random(360) * 3.1415 / 180 end\
        local dead = not alive\
        local p = dcsCommon.randomPointInCircle(outerRadius, innerRadius, x, z)\
        local theData = dcsCommon.createStaticObjectData(dcsCommon.uuid(\"static\"), objType, heading, dead)\
        theData.x = p.x\
        theData.y = p.z \
        \
        local theStatic = coalition.addStaticObject(theCoalition, theData) -- warning! coalition is not country \
        return theStatic, p.x, p.z \
    end\
    \
    \
    \
    function dcsCommon.linkStaticDataToUnit(theStatic, theUnit, dx, dy, heading)\
        if not theStatic then \
            trigger.action.OutText(\"+++dcsC: NIL theStatic on linkStatic!\", 30)\
            return \
        end\
        -- NOTE: we may get current heading and subtract/add \
        -- to original heading \
        local rotX, rotY = dcsCommon.rotatePointAroundOrigin(dx, dy, -heading)\
        \
        if not theUnit then return end\
        if not theUnit:isExist() then return end \
        theStatic.linkOffset = true \
        theStatic.linkUnit = theUnit:getID()\
        local unitPos = theUnit:getPoint()\
        local offsets = {}\
        offsets.x = rotX  \
        offsets.y = rotY \
        offsets.angle = 0\
        theStatic.offsets = offsets\
    end\
    \
    function dcsCommon.offsetStaticData(theStatic, dx, dy)\
        theStatic.x = theStatic.x + dx\
        theStatic.y = theStatic.y + dy\
        -- now check if thre is a route (for linked objects)\
        if theStatic.route then \
            -- access points[1] x and y and copy from main\
            theStatic.route.points[1].x = theStatic.x\
            theStatic.route.points[1].y = theStatic.y\
        end\
    end\
    \
    function dcsCommon.moveStaticDataTo(theStatic, x, y)\
        theStatic.x = x\
        theStatic.y = y\
        -- now check if thre is a route (for linked objects)\
        if theStatic.route then \
            -- access points[1] x and y and copy from main\
            theStatic.route.points[1].x = theStatic.x\
            theStatic.route.points[1].y = theStatic.y\
        end\
\
    end\
\
function dcsCommon.synchGroupData(inGroupData) -- update group data block by \
-- comparing it to spawned group and update units by x, y, heding and isExist \
-- modifies inGroupData!\
    if not inGroupData then return end \
    -- groupdata from game, NOT MX DATA!\
    -- we synch the units and their coords \
    local livingUnits = {}\
    for idx, unitData in pairs(inGroupData.units) do \
        local theUnit = Unit.getByName(unitData.name)\
        if theUnit and theUnit:isExist() and theUnit:getLife()>1 then \
            -- update x and y and heading\
            local pos = theUnit:getPoint()\
            unitData.unitId = theUnit:getID()\
            unitData.x = pos.x \
            unitData.y = pos.z -- !!!!\
            unitData.heading = dcsCommon.getUnitHeading(gUnit)\
            table.insert(livingUnits, unitData)\
        end\
    end\
    inGroupData.units = livingUnits \
end\
\
--\
--\
-- M I S C   M E T H O D S \
--\
--\
\
-- as arrayContainsString, except it includes wildcard matches if EITHER \
-- ends on \"*\"\
    function dcsCommon.wildArrayContainsString(theArray, theString, caseSensitive) \
        if not theArray then return false end\
        if not theString then return false end\
        if not caseSensitive then caseSensitive = false end \
        if type(theArray) ~= \"table\" then \
            trigger.action.outText(\"***wildArrayContainsString: theArray is not type table but <\" .. type(theArray) .. \">\", 30)\
        end\
        if not caseSensitive then theString = string.upper(theString) end \
        \
        local wildIn = dcsCommon.stringEndsWith(theString, \"*\")\
        if wildIn then theString = dcsCommon.removeEnding(theString, \"*\") end \
        for idx, theElement in pairs(theArray) do -- i = 1, #theArray do \
            if not caseSensitive then theElement = string.upper(theElement) end \
            local wildEle = dcsCommon.stringEndsWith(theElement, \"*\")\
            if wildEle then theElement = dcsCommon.removeEnding(theElement, \"*\") end \
\
            if wildEle and wildIn then \
                -- both end on wildcards, partial match for both\
                if dcsCommon.stringStartsWith(theElement, theString) then return true end \
                if dcsCommon.stringStartsWith(theString, theElement) then return true end \
            elseif wildEle then \
                -- Element is a wildcard, partial match \
                if dcsCommon.stringStartsWith(theString, theElement) then return true end\
\
            elseif wildIn then\
                -- theString is a wildcard. partial match \
                if dcsCommon.stringStartsWith(theElement, theString) then return true end\
            else\
                -- standard: no wildcards, full match\
                if theElement == theString then return true end \
            end\
            \
        end\
        return false \
    end\
\
\
    function dcsCommon.arrayContainsString(theArray, theString) \
        if not theArray then return false end\
        if not theString then return false end\
        if type(theArray) ~= \"table\" then \
            trigger.action.outText(\"***arrayContainsString: theArray is not type <table> but <\" .. type(theArray) .. \">\", 30)\
        end\
        for idx, item in pairs(theArray) do \
--        for i = 1, #theArray do \
            if item == theString then return true end \
        end\
        return false \
    end\
\
    function dcsCommon.arrayContainsStringCaseInsensitive(theArray, theString) -- case insensitive\
        if not theArray then return false end\
        if not theString then return false end\
        if type(theArray) ~= \"table\" then \
            trigger.action.outText(\"***arrayContainsStringCI: theArray is not type <table> but <\" .. type(theArray) .. \">\", 30)\
        end\
        theString = string.upper(theString)\
        for idx, item in pairs(theArray) do \
            if string.upper(item) == theString then return true end \
        end\
        return false \
    end\
    \
    function dcsCommon.splitString(inputstr, sep) \
        if sep == nil then\
            sep = \"%s\"\
        end\
        if inputstr == nil then \
            inputstr = \"\"\
        end\
        \
        local t={}\
        for str in string.gmatch(inputstr, \"([^\"..sep..\"]+)\") do\
            table.insert(t, str)\
        end\
        return t\
    \
    end\
    \
    function dcsCommon.trimFront(inputstr) \
        if not inputstr then return nil end \
        local s = inputstr\
        while string.len(s) > 1 and string.sub(s, 1, 1) == \" \" do \
            local snew = string.sub(s, 2) -- all except first\
            s = snew\
        end\
        return s\
    end\
    \
    function dcsCommon.trimBack(inputstr)\
        if not inputstr then return nil end \
        local s = inputstr\
        while string.len(s) > 1 and string.sub(s, -1) == \" \" do \
            local snew = string.sub(s, 1, -2) -- all except last\
            s = snew\
        end\
        return s\
    end\
    \
    function dcsCommon.trim(inputstr) \
        local t1 = dcsCommon.trimFront(inputstr)\
        local t2 = dcsCommon.trimBack(t1)\
        return t2\
    end\
    \
    function dcsCommon.trimArray(theArray)\
        local trimmedArray = {}\
        for idx, element in pairs(theArray) do \
            local tel = dcsCommon.trim(element)\
            table.insert(trimmedArray, tel)\
        end\
        return trimmedArray\
    end\
    \
    function dcsCommon.string2Array(inString, deli, uCase)\
        if not inString then return {} end \
        if not deli then return {} end \
        if not uCase then uCase = false end\
        if uCase then inString = string.upper(inString) end\
        inString = dcsCommon.trim(inString)\
        if dcsCommon.containsString(inString, deli) then \
            local a = dcsCommon.splitString(inString, deli)\
            a = dcsCommon.trimArray(a)\
            return a \
        else \
            return {inString}\
        end\
    end\
    \
    function dcsCommon.array2string(inArray, deli)\
        if not deli then deli = \", \" end\
        if type(inArray) ~= \"table\" then return \"<err in array2string: not an array>\" end\
        local s = \"\"\
        local count = 0\
        for idx, ele in pairs(inArray) do\
            if count > 0 then s = s .. deli .. \" \" end\
            s = s .. ele\
            count = count + 1\
        end\
        return s\
    end\
    \
    function dcsCommon.stripLF(theString)\
        return theString:gsub(\"[\\r\\n]\", \"\")\
    end\
    \
    function dcsCommon.removeBlanks(theString)\
        return theString:gsub(\"%s\", \"\")\
    end\
    \
    function dcsCommon.stringIsPositiveNumber(theString)\
        -- only full integer positive numbers supported \
        if not theString then return false end \
--        if theString == \"\" then return false end \
        for i = 1, #theString do \
            local c = theString:sub(i,i)\
            if c < \"0\" or c > \"9\" then return false end \
        end\
        return true \
    end\
    \
    function dcsCommon.stringStartsWithDigit(theString)\
        if #theString < 1 then return false end \
        local c = string.sub(theString, 1, 1) \
        return c >= \"0\" and c <= \"9\" \
    end\
    \
    function dcsCommon.stringStartsWithLetter(theString)\
        if #theString < 1 then return false end \
        local c = string.sub(theString, 1, 1)\
        if c >= \"a\" and c <= \"z\" then return true end  \
        if c >= \"A\" and c <= \"Z\" then return true end \
        return false \
    end\
    \
    function dcsCommon.stringStartsWith(theString, thePrefix, caseInsensitive)\
        if not theString then return false end \
        if not thePrefix then return false end \
        if not caseInsensitive then caseInsensitive = false end \
        \
        if caseInsensitive then \
            theString = string.upper(theString)\
            thePrefix = string.upper(thePrefix)\
        end\
        -- superseded: string.find (s, pattern [, init [, plain]]) solves the problem  \
        local i, j = string.find(theString, thePrefix, 1, true)\
        return (i == 1)\
    end\
    \
    function dcsCommon.removePrefix(theString, thePrefix)\
        if not dcsCommon.stringStartsWith(theString, thePrefix) then \
            return theString\
        end;\
        return theString:sub(1 + #thePrefix)\
    end\
    \
    function dcsCommon.stringEndsWith(theString, theEnding)\
        return theEnding == \"\" or theString:sub(-#theEnding) == theEnding\
    end\
    \
    function dcsCommon.removeEnding(theString, theEnding) \
        if not dcsCommon.stringEndsWith(theString, theEnding) then \
            return theString\
        end\
        return theString:sub(1, #theString - #theEnding)\
    end\
    \
    function dcsCommon.containsString(inString, what, caseSensitive)\
        if (not caseSensitive) then \
            inString = string.upper(inString)\
            what = string.upper(what)\
        end\
        if inString == what then return true end -- when entire match \
        return string.find(inString, what, 1, true) -- 1, true means start at 1, plaintext\
    end\
    \
    function dcsCommon.bool2Text(theBool) \
        if not theBool then theBool = false end \
        if theBool then return \"true\" end \
        return \"false\"\
    end\
    \
    function dcsCommon.bool2YesNo(theBool)\
        if not theBool then \
            theBool = false\
            return \"NIL\"\
        end \
        if theBool then return \"yes\" end \
        return \"no\"\
    end\
    \
    function dcsCommon.bool2Num(theBool)\
        if not theBool then theBool = false end \
        if theBool then return 1 end \
        return 0\
    end\
\
    function dcsCommon.point2text(p, intsOnly) \
        if not intsOnly then intsOnly = false end \
        if not p then return \"<!NIL!>\" end \
        local t = \"[x=\"\
        if intsOnly then \
            if p.x then t = t .. math.floor(p.x) .. \", \" else t = t .. \"<nil>, \" end \
            if p.y then t = t .. \"y=\" .. math.floor(p.y) .. \", \" else t = t .. \"y=<nil>, \" end \
            if p.z then t = t .. \"z=\" .. math.floor(p.z) .. \"]\" else t = t .. \"z=<nil>]\" end \
        else \
            if p.x then t = t .. p.x .. \", \" else t = t .. \"<nil>, \" end \
            if p.y then t = t .. \"y=\" .. p.y .. \", \" else t = t .. \"y=<nil>, \" end \
            if p.z then t = t .. \"z=\" .. p.z .. \"]\" else t = t .. \"z=<nil>]\" end \
        end\
        return t \
    end\
\
    function dcsCommon.string2GroupCat(inString)\
\
        if not inString then return 2 end -- default ground \
        inString = inString:lower()\
        inString = dcsCommon.trim(inString)\
\
        local catNum = tonumber(inString)\
        if catNum then \
            if catNum < 0 then catNum = 0 end \
            if catNum > 4 then catNum = 4 end \
            return catNum \
        end\
    \
        catNum = 2 -- ground default \
        if dcsCommon.stringStartsWith(inString, \"grou\") then catNum = 2 end \
        if dcsCommon.stringStartsWith(inString, \"air\") then catNum = 0 end\
        if dcsCommon.stringStartsWith(inString, \"hel\") then catNum = 1 end\
        if dcsCommon.stringStartsWith(inString, \"shi\") then catNum = 3 end\
        if dcsCommon.stringStartsWith(inString, \"trai\") then catNum = 4 end\
\
        return catNum\
    end\
\
    function dcsCommon.string2ObjectCat(inString)\
\
        if not inString then return 3 end -- default static \
        inString = inString:lower()\
        inString = dcsCommon.trim(inString)\
\
        local catNum = tonumber(inString)\
        if catNum then \
            if catNum < 0 then catNum = 0 end \
            if catNum > 6 then catNum = 6 end \
            return catNum \
        end\
    \
        catNum = 3 -- static default \
        if dcsCommon.stringStartsWith(inString, \"uni\") then catNum = 1 end \
        if dcsCommon.stringStartsWith(inString, \"wea\") then catNum = 2 end\
        if dcsCommon.stringStartsWith(inString, \"bas\") then catNum = 4 end\
        if dcsCommon.stringStartsWith(inString, \"sce\") then catNum = 5 end\
        if dcsCommon.stringStartsWith(inString, \"car\") then catNum = 6 end\
\
        return catNum\
    end\
\
    function dcsCommon.menu2text(inMenu)\
        if not inMenu then return \"<nil>\" end\
        local s = \"\"\
        for n, v in pairs(inMenu) do \
            if type(v) == \"string\" then \
                if s == \"\" then s = \"[\" .. v .. \"]\"  else \
                    s = s .. \" | [\" .. type(v) .. \"]\" end\
            else \
                if s == \"\" then s = \"[<\" .. type(v) .. \">]\"  else\
                    s = s .. \" | [<\" .. type(v) .. \">]\" end\
            end\
        end\
        return s\
    end\
\
    -- recursively show the contents of a variable\
    function dcsCommon.dumpVar(key, value, prefix, inrecursion)\
        if not inrecursion then \
            -- output a marker to find in the log / screen\
            env.info(\"*** dcsCommon vardump START\")\
        end\
        if not value then value = \"nil\" end\
        if not prefix then prefix = \"\" end\
        prefix = \" \" .. prefix\
        if type(value) == \"table\" then \
            env.info(prefix .. key .. \": [ \")\
            -- iterate through all kvp\
            for k,v in pairs (value) do\
                dcsCommon.dumpVar(k, v, prefix, true)\
            end\
            env.info(prefix .. \" ] - end \" .. key)\
            \
        elseif type(value) == \"boolean\" then \
            local b = \"false\"\
            if value then b = \"true\" end\
            env.info(prefix .. key .. \": \" .. b)\
            \
        else -- simple var, show contents, ends recursion\
            env.info(prefix .. key .. \": \" .. value)\
        end\
        \
        if not inrecursion then \
            -- output a marker to find in the log / screen\
            trigger.action.outText(\"=== dcsCommon vardump end\", 30)\
            env.info(\"=== dcsCommon vardump end\")\
        end\
    end\
    \
    function dcsCommon.dumpVar2Str(key, value, prefix, inrecursion)\
        -- dumps to screen, not string \
        if not inrecursion then \
            -- output a marker to find in the log / screen\
            trigger.action.outText(\"*** dcsCommon vardump START\",30)\
        end\
        if not value then value = \"nil\" end\
        if not prefix then prefix = \"\" end\
        prefix = \" \" .. prefix\
        if getmetatable(value) then \
            if type(value) == \"string\" then \
            else \
                trigger.action.outText(prefix .. key .. (\" .. type(value) .. \") .. \" HAS META\", 30)\
            end\
        end\
        if type(value) == \"table\" then \
            trigger.action.outText(prefix .. key .. \": [ \", 30)\
            -- iterate through all kvp\
            for k,v in pairs (value) do\
                dcsCommon.dumpVar2Str(k, v, prefix, true)\
            end\
            trigger.action.outText(prefix .. \" ] - end \" .. key, 30)\
            \
        elseif type(value) == \"boolean\" then \
            local b = \"false\"\
            if value then b = \"true\" end\
            trigger.action.outText(prefix .. key .. \": \" .. b, 30)\
            \
        else -- simple var, show contents, ends recursion\
            trigger.action.outText(prefix .. key .. \": \" .. value, 30)\
        end\
        \
        if not inrecursion then \
            -- output a marker to find in the log / screen\
            trigger.action.outText(\"=== dcsCommon vardump end\", 30)\
        end\
    end\
        \
    function dcsCommon.numberUUID()\
        dcsCommon.simpleUUID = dcsCommon.simpleUUID + 1\
        return dcsCommon.simpleUUID\
    end\
\
    function dcsCommon.uuid(prefix)\
        --dcsCommon.uuIdent = dcsCommon.uuIdent + 1\
        if not prefix then prefix = dcsCommon.uuidStr end\
        return prefix .. \"-\" .. dcsCommon.numberUUID() -- dcsCommon.uuIdent\
    end\
    \
    function dcsCommon.event2text(id) \
        if not id then return \"error\" end\
        if id == 0 then return \"invalid\" end\
        -- translate the event id to text\
        local events = {\"shot\", \"hit\", \"takeoff\", \"land\",\
                        \"crash\", \"eject\", \"refuel\", \"dead\", -- 8\
                        \"pilot dead\", \"base captured\", \"mission start\", \"mission end\", -- 12\
                        \"took control\", \"refuel stop\", \"birth\", \"human failure\", -- 16 \
                        \"det. failure\", \"engine start\", \"engine stop\", \"player enter unit\", -- 20\
                        \"player leave unit\", \"player comment\", \"start shoot\", \"end shoot\", -- 24\
                        \"mark add\", \"mark changed\", \"mark removed\", \"kill\", -- 28 \
                        \"score\", \"unit lost\", \"land after eject\", \"Paratrooper land\", -- 32 \
                        \"chair discard after eject\", \"weapon add\", \"trigger zone\", \"landing quality mark\", -- 36\
                        \"BDA\", \"AI Abort Mission\", \"DayNight\", \"Flight Time\", -- 40\
                        \"Pilot Suicide\", \"player cap airfield\", \"emergency landing\", \"unit create task\", -- 44\
                        \"unit delete task\", \"Simulation start\", \"weapon rearm\", \"weapon drop\", -- 48\
                        \"unit task timeout\", \"unit task stage\", -- 50\
                        \"subtask score\", \"extra score\", \"mission restart\", \"winner\", \
                        \"postponed takeoff\", \"postponed land\", -- 56\
                        \"max\"}\
        if id > #events then return \"Unknown (ID=\" .. id .. \")\" end\
        return events[id]\
    end\
\
    function dcsCommon.smokeColor2Text(smokeColor)\
        if (smokeColor == 0) then return \"Green\" end\
        if (smokeColor == 1) then return \"Red\" end\
        if (smokeColor == 2) then return \"White\" end\
        if (smokeColor == 3) then return \"Orange\" end\
        if (smokeColor == 4) then return \"Blue\" end\
        \
        return (\"unknown: \" .. smokeColor)\
    end\
    \
    function dcsCommon.flareColor2Text(flareColor)\
        if (flareColor == 0) then return \"Green\" end\
        if (flareColor == 1) then return \"Red\" end\
        if (flareColor == 2) then return \"White\" end\
        if (flareColor == 3) then return \"Yellow\" end\
        if (flareColor < 0) then return \"Random\" end \
        return (\"unknown: \" .. flareColor)\
    end\
    \
    function dcsCommon.smokeColor2Num(smokeColor)\
        if not smokeColor then smokeColor = \"green\" end \
        if type(smokeColor) ~= \"string\" then return 0 end \
        smokeColor = smokeColor:lower()\
        if (smokeColor == \"green\") then return 0 end \
        if (smokeColor == \"red\") then return 1 end \
        if (smokeColor == \"white\") then return 2 end \
        if (smokeColor == \"orange\") then return 3 end \
        if (smokeColor == \"blue\") then return 4 end \
        return 0\
    end\
\
    function dcsCommon.flareColor2Num(flareColor)\
        if not flareColor then flareColor = \"green\" end \
        if type(flareColor) ~= \"string\" then return 0 end \
        flareColor = flareColor:lower()\
        if (flareColor == \"green\") then return 0 end \
        if (flareColor == \"red\") then return 1 end \
        if (flareColor == \"white\") then return 2 end \
        if (flareColor == \"yellow\") then return 3 end \
        if (flareColor == \"random\") then return -1 end \
        if (flareColor == \"rnd\") then return -1 end \
        return 0\
    end\
\
    \
    function dcsCommon.markPointWithSmoke(p, smokeColor)\
        if not smokeColor then smokeColor = 0 end \
        local x = p.x \
        local z = p.z -- do NOT change the point directly\
        -- height-correct\
        local y = land.getHeight({x = x, y = z})\
        local newPoint= {x = x, y = y + 2, z = z}\
        trigger.action.smoke(newPoint, smokeColor)\
    end\
\
-- based on buzzer1977's idea, channel is number, eg in 74X, channel is 74, mode is \"X\"\
    function dcsCommon.tacan2freq(channel, mode)    \
        if not mode then mode = \"X\" end \
        if not channel then channel = 1 end \
        if type(mode) ~= \"string\" then mode = \"X\" end \
        mode = mode:upper()\
        local offset = 1000000 * channel\
        if channel < 64 then \
            if mode == \"Y\" then\
                return 1087000000 + offset\
            end\
            return 961000000 + offset -- mode x\
        end\
    \
        if mode == \"Y\" then\
            return 961000000 + offset\
        end\
        return 1087000000 + offset -- mode x\
    end\
    \
    function dcsCommon.processHMS(msg, delta)\
        local rS = math.floor(delta)\
        local remainS = tostring(rS)\
        local rM = math.floor(delta/60)\
        local remainM = tostring(rM)\
        local rH = math.floor(delta/3600)\
        local remainH = tostring(rH)\
        local hmsH = remainH \
        if rH < 10 then hmsH = \"0\" .. hmsH end \
        \
        local hmsCount = delta - (rH * 3600) -- mins left \
        local mins = math.floor (hmsCount / 60)\
        local hmsM = tostring(mins)\
        if mins < 10 then hmsM = \"0\" .. hmsM end \
        \
        hmsCount = hmsCount - (mins * 60) \
        local secs = math.floor(hmsCount)\
        local hmsS = tostring(secs)\
        if secs < 10 then hmsS = \"0\" .. hmsS end \
        \
        msg = string.gsub(msg, \"<s>\", remainS)\
        msg = string.gsub(msg, \"<m>\", remainM)\
        msg = string.gsub(msg, \"<h>\", remainH)\
        \
        msg = string.gsub(msg, \"<:s>\", hmsS)\
        msg = string.gsub(msg, \"<:m>\", hmsM)\
        msg = string.gsub(msg, \"<:h>\", hmsH)\
        \
        return msg \
    end\
    \
    function dcsCommon.nowString()\
        local absSecs = timer.getAbsTime()-- + env.mission.start_time\
        while absSecs > 86400 do \
            absSecs = absSecs - 86400 -- subtract out all days \
        end\
        return dcsCommon.processHMS(\"<:h>:<:m>:<:s>\", absSecs)\
    end\
    \
    function dcsCommon.str2num(inVal, default) \
        if not default then default = 0 end\
        if not inVal then return default end\
        if type(inVal) == \"number\" then return inVal end                 \
        local num = nil\
        if type(inVal) == \"string\" then num = tonumber(inVal) end\
        if not num then return default end\
        return num\
    end\
    \
    function dcsCommon.stringRemainsStartingWith(theString, startingWith)\
        -- find the first position where startingWith starts \
        local pos = theString:find(startingWith)\
        if not pos then return theString end \
        -- now return the entire remainder of the string from pos \
        local nums = theString:len() - pos + 1\
        return theString:sub(-nums)\
    end\
\
--\
--\
-- V E C T O R   M A T H \
--\
--\
\
function dcsCommon.vAdd(a, b) \
    local r = {}\
    if not a then a = {x = 0, y = 0, z = 0} end\
    if not b then b = {x = 0, y = 0, z = 0} end\
    r.x = a.x + b.x \
    r.y = a.y + b.y \
    if a.z and b.z then \
        r.z = a.z + b.z \
    end \
    return r \
end\
\
function dcsCommon.vSub(a, b) \
    local r = {}\
    if not a then a = {x = 0, y = 0, z = 0} end\
    if not b then b = {x = 0, y = 0, z = 0} end\
    r.x = a.x - b.x \
    r.y = a.y - b.y \
    if a.z and b.z then \
        r.z = a.z - b.z \
    end \
    return r \
end\
\
function dcsCommon.vMultScalar(a, f) \
    local r = {}\
    if not a then a = {x = 0, y = 0, z = 0} end\
    if not f then f = 0 end\
    r.x = a.x * f \
    r.y = a.y * f \
    if a.z then \
        r.z = a.z * f\
    end        \
    return r \
end\
\
function dcsCommon.vLerp (a, b, t)\
    if not a then a = {x = 0, y = 0, z = 0} end\
    if not b then b = {x = 0, y = 0, z = 0} end\
    \
    local d = dcsCommon.vSub(b, a)\
    local dt = dcsCommon.vMultScalar(d, t)\
    local r = dcsCommon.vAdd(a, dt)\
    return r\
end\
\
function dcsCommon.mag(x, y, z) \
    if not x then x = 0 end\
    if not y then y = 0 end \
    if not z then z = 0 end \
    \
    return (x * x + y * y + z * z)^0.5\
end\
\
function dcsCommon.vMag(a) \
    if not a then return 0 end \
    if not a.x then a.x = 0 end \
    if not a.y then a.y = 0 end \
    if not a.z then a.z = 0 end\
    return dcsCommon.mag(a.x, a.y, a.z) \
end\
\
function dcsCommon.magSquare(x, y, z) \
    if not x then x = 0 end\
    if not y then y = 0 end \
    if not z then z = 0 end \
    \
    return (x * x + y * y + z * z)\
end\
\
function dcsCommon.vNorm(a) \
    if not a then return {x = 0, y = 0, z = 0} end \
    m = dcsCommon.vMag(a)\
    if m <= 0 then return {x = 0, y = 0, z = 0} end \
    local r = {}\
    r.x = a.x / m \
    r.y = a.y / m \
    r.z = a.z / m\
    return r \
end\
\
function dcsCommon.dot (a, b) \
    if not a then a = {} end \
    if not a.x then a.x = 0 end \
    if not a.y then a.y = 0 end \
    if not a.z then a.z = 0 end\
    if not b then b = {} end \
    if not b.x then b.x = 0 end \
    if not b.y then b.y = 0 end \
    if not b.z then b.z = 0 end \
    \
    return a.x * b.x + a.y * b.y + a.z * b.z \
end\
--\
-- UNIT MISC\
-- \
function dcsCommon.isSceneryObject(theUnit)\
    if not theUnit then return false end\
    return Object.getCategory(theUnit) == 5 \
--    return theUnit.getCoalition == nil -- scenery objects do not return a coalition \
end\
\
function dcsCommon.isTroopCarrierType(theType, carriers)\
    if not theType then return false end \
    if not carriers then carriers = dcsCommon.troopCarriers \
    end \
    -- remember that arrayContainsString is case INsensitive by default \
    if dcsCommon.wildArrayContainsString(carriers, theType) then \
        -- may add additional tests before returning true\
        return true\
    end\
    \
    -- see if user wanted 'any' or 'all' supported\
    if dcsCommon.arrayContainsString(carriers, \"any\") then \
        return true \
    end \
    \
    if dcsCommon.arrayContainsString(carriers, \"all\") then \
        return true \
    end \
    \
    return false\
end\
\
function dcsCommon.isTroopCarrier(theUnit, carriers)\
    -- return true if conf can carry troups\
    if not theUnit then return false end\
\
    -- see if carriers contains \"helo\" and theUnit is a helo \
    if dcsCommon.arrayContainsString(carriers, \"helo\") or dcsCommon.arrayContainsString(carriers, \"helos\")then\
        local grp = theUnit:getGroup() \
        if grp:getCategory() == 1 then -- NOT category bug prone, is a group check \
            return true \
        end\
    end    \
    \
    local uType = theUnit:getTypeName()\
    return dcsCommon.isTroopCarrierType(uType, carriers) \
end\
\
function dcsCommon.getPlayerUnit(playerName) \
    if not playerName then return nil end \
    for idx, theSide in pairs(dcsCommon.coalitionSides) do\
        local thePlayers = coalition.getPlayers(theSide) \
        for idy, theUnit in pairs (thePlayers) do \
            if theUnit and theUnit:isExist() and theUnit.getPlayerName \
            and theUnit:getPlayerName() == playerName then \
                return theUnit\
            end\
        end\
    end\
    return nil\
end \
\
function dcsCommon.getAllExistingPlayerUnitsRaw()\
    local apu = {}\
    for idx, theSide in pairs(dcsCommon.coalitionSides) do\
        local thePlayers = coalition.getPlayers(theSide) \
        for idy, theUnit in pairs (thePlayers) do \
            if theUnit and theUnit:isExist() then \
                table.insert(apu, theUnit)\
            end\
        end\
    end\
    return apu \
end\
\
function dcsCommon.getAllExistingPlayersAndUnits() -- units indexed by player name\
-- designed to replace cases for cfxPlayer.getAllPlayer invocations\
    local apu = {}\
    for idx, theSide in pairs(dcsCommon.coalitionSides) do\
        local thePlayers = coalition.getPlayers(theSide) \
        for idy, theUnit in pairs (thePlayers) do \
            if theUnit and theUnit:isExist() then \
                local pName = theUnit:getPlayerName()\
                apu[pName] = theUnit\
            end\
        end\
    end\
    return apu \
end\
\
function dcsCommon.getUnitAlt(theUnit)\
    if not theUnit then return 0 end\
    if not Unit.isExist(theUnit) then return 0 end -- safer \
    local p = theUnit:getPoint()\
    return p.y \
end\
\
function dcsCommon.getUnitAGL(theUnit)\
    if not theUnit then return 0 end\
    if not Unit.isExist(theUnit) then return 0 end -- safe fix\
    local p = theUnit:getPoint()\
    local alt = p.y \
    local loc = {x = p.x, y = p.z}\
    local landElev = land.getHeight(loc)\
    return alt - landElev\
end \
\
function dcsCommon.getUnitSpeed(theUnit)\
    if not theUnit then return 0 end\
    if not Unit.isExist(theUnit) then return 0 end \
    local v = theUnit:getVelocity()\
    return dcsCommon.mag(v.x, v.y, v.z)\
end\
\
-- closing velocity of u1 and u2, seen from u1\
function dcsCommon.getClosingVelocity(u1, u2)\
    if not u1 then return 0 end \
    if not u2 then return 0 end \
    if not u1:isExist() then return 0 end \
    if not u2:isExist() then return 0 end \
    local v1 = u1:getVelocity()\
    local v2 = u2:getVelocity()\
    local dV = dcsCommon.vSub(v1,v2)\
    local a = u1:getPoint()\
    local b = u2:getPoint() \
    local aMinusB = dcsCommon.vSub(a,b) -- vector from u2 to u1\
    local abMag = dcsCommon.vMag(aMinusB) -- distance u1 to u2 \
    if abMag < .0001 then return 0 end \
    -- project deltaV onto vector from u2 to u1 \
    local vClose = dcsCommon.dot(dV, aMinusB) / abMag \
    return vClose \
end\
\
function dcsCommon.getGroupAvgSpeed(theGroup)\
    if not theGroup then return 0 end \
    if not dcsCommon.isGroupAlive(theGroup) then return 0 end \
    local totalSpeed = 0\
    local cnt = 0 \
    local livingUnits = theGroup:getUnits()\
    for idx, theUnit in pairs(livingUnits) do \
        cnt = cnt + 1\
        totalSpeed = totalSpeed + dcsCommon.getUnitSpeed(theUnit)\
    end \
    if cnt == 0 then return 0 end \
    return totalSpeed / cnt \
end\
 \
function dcsCommon.getGroupMaxSpeed(theGroup)\
    if not theGroup then return 0 end \
    if not dcsCommon.isGroupAlive(theGroup) then return 0 end \
    local maxSpeed = 0\
    local livingUnits = theGroup:getUnits()\
    for idx, theUnit in pairs(livingUnits) do \
        currSpeed = dcsCommon.getUnitSpeed(theUnit)\
        if currSpeed > maxSpeed then maxSpeed = currSpeed end \
    end \
    return maxSpeed\
end \
\
function dcsCommon.getUnitHeading(theUnit)\
    if not theUnit then return 0 end \
    if not theUnit:isExist() then return 0 end \
    local pos = theUnit:getPosition() -- returns three vectors, p is location\
\
    local heading = math.atan2(pos.x.z, pos.x.x)\
    -- make sure positive only, add 360 degrees\
    if heading < 0 then\
        heading = heading + 2 * math.pi    -- put heading in range of 0 to 2*pi\
    end\
    return heading \
end\
\
function dcsCommon.getUnitHeadingDegrees(theUnit)\
    local heading = dcsCommon.getUnitHeading(theUnit)\
    return heading * 57.2958 -- 180 / math.pi \
end\
\
function dcsCommon.typeIsInfantry(theType)\
    local isInfantry =  \
                dcsCommon.containsString(theType, \"infantry\", false) or \
                dcsCommon.containsString(theType, \"paratrooper\", false) or\
                dcsCommon.containsString(theType, \"stinger\", false) or\
                dcsCommon.containsString(theType, \"manpad\", false) or\
                dcsCommon.containsString(theType, \"soldier\", false) or \
                dcsCommon.containsString(theType, \"SA-18 Igla\", false)\
    return isInfantry\
end\
\
function dcsCommon.unitIsInfantry(theUnit)\
    if not theUnit then return false end \
    if not theUnit:isExist() then return end\
    local theType = theUnit:getTypeName()\
    return dcsCommon.typeIsInfantry(theType)\
end\
\
function dcsCommon.coalition2county(inCoalition)\
    -- simply return UN troops for 0 neutral,\
    -- joint red for 1  red\
    -- joint blue for 2 blue \
    if inCoalition == 1 then return 81 end -- cjtf red\
    if inCoalition == 2 then return 80 end -- blue \
    if type(inCoalition) == \"string\" then \
            inCoalition = inCoalition:lower()\
            if inCoalition == \"red\" then return 81 end\
            if inCoalition == \"blue\" then return 80 end\
    end\
        \
    trigger.action.outText(\"+++dcsC: coalition2county in (\" .. inCoalition .. \") converts to UN (82)!\", 30)\
    return 82 -- UN \
    \
end\
\
function dcsCommon.coalition2Text(coa)\
    if not coa then return \"!nil!\" end \
    if coa == 0 then return \"NEUTRAL\" end \
    if coa == 1 then return \"RED\" end \
    if coa == 2 then return \"BLUE\" end \
    return \"?UNKNOWN?\"\
end\
\
function dcsCommon.latLon2Text(lat, lon)\
    -- inspired by mist, thanks Grimes!\
    -- returns two strings: lat and lon \
    \
    -- determine hemispheres by sign\
    local latHemi, lonHemi\
    if lat > 0 then latHemi = 'N' else latHemi = 'S' end\
    if lon > 0 then lonHemi = 'E' else lonHemi = 'W' end\
\
    -- remove sign since we have hemi\
    lat = math.abs(lat)\
    lon = math.abs(lon)\
\
    -- calc deg / mins \
    local latDeg = math.floor(lat)\
    local latMin = (lat - latDeg) * 60\
    local lonDeg = math.floor(lon)\
    local lonMin = (lon - lonDeg) * 60\
\
    -- calc seconds \
    local rawLatMin = latMin\
    latMin = math.floor(latMin)\
    local latSec = (rawLatMin - latMin) * 60\
    local rawLonMin = lonMin\
    lonMin = math.floor(lonMin)\
    local lonSec = (rawLonMin - lonMin) * 60\
\
    -- correct for rounding errors \
    if latSec >= 60 then\
        latSec = latSec - 60\
        latMin = latMin + 1\
    end\
    if lonSec >= 60 then\
        lonSec = lonSec - 60\
        lonMin = lonMin + 1\
    end\
\
    -- prepare string output \
    local secFrmtStr = '%06.3f'\
    local lat = string.format('%02d', latDeg) .. '°' .. string.format('%02d', latMin) .. \"'\" .. string.format(secFrmtStr, latSec) .. '\"' .. latHemi\
    local lon = string.format('%02d', lonDeg) .. '°' .. string.format('%02d', lonMin) .. \"'\" .. string.format(secFrmtStr, lonSec) .. '\"' .. lonHemi\
    return lat, lon  \
end\
\
-- get mission name. If mission file name without \".miz\"\
function dcsCommon.getMissionName()\
    local mn = net.dostring_in(\"gui\", \"return DCS.getMissionName()\")\
    return mn\
end\
\
function dcsCommon.numberArrayFromString(inString, default) -- moved from cfxZones\
    if not default then default = 0 end \
    if string.len(inString) < 1 then \
        trigger.action.outText(\"+++dcsCommon: empty numbers\", 30)\
        return {default, } \
    end\
    \
    local flags = {}\
    local rawElements = dcsCommon.splitString(inString, \",\")\
    -- go over all elements \
    for idx, anElement in pairs(rawElements) do \
        anElement = dcsCommon.trim(anElement)\
        if dcsCommon.stringStartsWithDigit(anElement) and dcsCommon.containsString(anElement, \"-\") then \
            -- interpret this as a range\
            local theRange = dcsCommon.splitString(anElement, \"-\")\
            local lowerBound = theRange[1]\
            lowerBound = tonumber(lowerBound)\
            local upperBound = theRange[2]\
            upperBound = tonumber(upperBound)\
            if lowerBound and upperBound then\
                -- swap if wrong order\
                if lowerBound > upperBound then \
                    local temp = upperBound\
                    upperBound = lowerBound\
                    lowerBound = temp \
                end\
                -- now add add numbers to flags\
                for f=lowerBound, upperBound do \
                    table.insert(flags, tostring(f))\
                end\
            else\
                -- bounds illegal\
                trigger.action.outText(\"+++dcsCommon: ignored range <\" .. anElement .. \"> (range)\", 30)\
            end\
        else\
            -- single number\
            f = dcsCommon.trim(anElement)\
            f = tonumber(f)\
            if f then \
                table.insert(flags, f)\
            end\
        end\
    end\
    if #flags < 1 then flags = {default, } end \
    return flags\
end \
\
function dcsCommon.flagArrayFromString(inString, verbose)\
    if not verbose then verbose = false end \
    \
    if verbose then \
        trigger.action.outText(\"+++flagArray: processing <\" .. inString .. \">\", 30)\
    end \
\
    if string.len(inString) < 1 then \
        trigger.action.outText(\"+++flagArray: empty flags\", 30)\
        return {} \
    end\
    \
    \
    local flags = {}\
    local rawElements = dcsCommon.splitString(inString, \",\")\
    -- go over all elements \
    for idx, anElement in pairs(rawElements) do \
        anElement = dcsCommon.trim(anElement)\
        if dcsCommon.stringStartsWithDigit(anElement) and  dcsCommon.containsString(anElement, \"-\") then \
            -- interpret this as a range\
            local theRange = dcsCommon.splitString(anElement, \"-\")\
            local lowerBound = theRange[1]\
            lowerBound = tonumber(lowerBound)\
            local upperBound = theRange[2]\
            upperBound = tonumber(upperBound)\
            if lowerBound and upperBound then\
                -- swap if wrong order\
                if lowerBound > upperBound then \
                    local temp = upperBound\
                    upperBound = lowerBound\
                    lowerBound = temp \
                end\
                -- now add add numbers to flags\
                for f=lowerBound, upperBound do \
                    table.insert(flags, f)\
\
                end\
            else\
                -- bounds illegal\
                trigger.action.outText(\"+++flagArray: ignored range <\" .. anElement .. \"> (range)\", 30)\
            end\
        else\
            -- single number\
            local f = dcsCommon.trim(anElement) -- DML flag upgrade: accept strings tonumber(anElement)\
            if f then \
                table.insert(flags, f)\
\
            else \
                trigger.action.outText(\"+++flagArray: ignored element <\" .. anElement .. \"> (single)\", 30)\
            end\
        end\
    end\
    if verbose then \
        trigger.action.outText(\"+++flagArray: <\" .. #flags .. \"> flags total\", 30)\
    end \
    return flags\
end\
\
function dcsCommon.rangeArrayFromString(inString, verbose)\
    if not verbose then verbose = false end \
    \
    if verbose then \
        trigger.action.outText(\"+++rangeArray: processing <\" .. inString .. \">\", 30)\
    end \
\
    if string.len(inString) < 1 then \
        trigger.action.outText(\"+++rangeArray: empty ranges\", 30)\
        return {} \
    end\
    \
    local ranges = {}\
    local rawElements = dcsCommon.splitString(inString, \",\")\
    -- go over all elements \
    for idx, anElement in pairs(rawElements) do \
        anElement = dcsCommon.trim(anElement)\
        local outRange = {}\
        if dcsCommon.stringStartsWithDigit(anElement) and  dcsCommon.containsString(anElement, \"-\") then \
            -- interpret this as a range\
            local theRange = dcsCommon.splitString(anElement, \"-\")\
            local lowerBound = theRange[1]\
            lowerBound = tonumber(lowerBound)\
            local upperBound = theRange[2]\
            upperBound = tonumber(upperBound)\
            if lowerBound and upperBound then\
                -- swap if wrong order\
                if lowerBound > upperBound then \
                    local temp = upperBound\
                    upperBound = lowerBound\
                    lowerBound = temp \
                end\
                -- now add to ranges\
                outRange[1] = lowerBound\
                outRange[2] = upperBound\
                table.insert(ranges, outRange)\
                if verbose then \
                    trigger.action.outText(\"+++rangeArray: new range <\" .. lowerBound .. \"> to <\" .. upperBound .. \">\", 30)\
                end\
            else\
                -- bounds illegal\
                trigger.action.outText(\"+++rangeArray: ignored range <\" .. anElement .. \"> (range)\", 30)\
            end\
        else\
            -- single number\
            local f = dcsCommon.trim(anElement) \
            f = tonumber(f)\
            if f then \
                outRange[1] = f\
                outRange[2] = f\
                table.insert(ranges, outRange)\
                if verbose then \
                    trigger.action.outText(\"+++rangeArray: new (single-val) range <\" .. f .. \"> to <\" .. f .. \">\", 30)\
                end\
            else \
                trigger.action.outText(\"+++rangeArray: ignored element <\" .. anElement .. \"> (single)\", 30)\
            end\
        end\
    end\
    if verbose then \
        trigger.action.outText(\"+++rangeArray: <\" .. #ranges .. \"> ranges total\", 30)\
    end \
    return ranges\
end\
\
function dcsCommon.incFlag(flagName)\
    local v = trigger.misc.getUserFlag(flagName)\
    trigger.action.setUserFlag(flagName, v + 1)\
end\
\
function dcsCommon.decFlag(flagName)\
    local v = trigger.misc.getUserFlag(flagName)\
    trigger.action.setUserFlag(flagName, v - 1)\
end\
\
function dcsCommon.objectHandler(theObject, theCollector)\
    table.insert(theCollector, theObject)\
    return true \
end\
\
function dcsCommon.getObjectsForCatAtPointWithRadius(aCat, thePoint, theRadius)\
    if not aCat then aCat = Object.Category.UNIT end \
    local p = {x=thePoint.x, y=thePoint.y, z=thePoint.z}\
    local collector = {}\
    \
    -- now build the search argument \
    local args = {\
            id = world.VolumeType.SPHERE,\
            params = {\
                point = p,\
                radius = theRadius\
            }\
        }\
    \
    -- now call search\
    world.searchObjects(aCat, args, dcsCommon.objectHandler, collector)\
    return collector\
end\
\
function dcsCommon.getSceneryObjectsInZone(theZone) -- DCS ZONE!!! \
    local aCat = 5 -- scenery\
    -- WARNING: WE ARE USING DCS ZONES, NOT CFX!!!\
    local p = {x=theZone.x, y=0, z=theZone.y}\
    local lp = {x = p.x, y = p.z}\
    p.y = land.getHeight(lp)\
    local collector = {}\
    \
    -- now build the search argument \
    local args = {\
            id = world.VolumeType.SPHERE,\
            params = {\
                point = p,\
                radius = theZone.radius\
            }\
        }\
    \
    -- now call search\
    world.searchObjects(aCat, args, dcsCommon.objectHandler, collector)\
    return collector\
end\
\
function dcsCommon.getSceneryObjectInZoneByName(theName, theZone) -- DCS ZONE!!!\
    local allObs = dcsCommon.getSceneryObjectsInZone(theZone)\
    for idx, anObject in pairs(allObs) do \
        if tostring(anObject:getName()) == theName then return anObject end \
    end\
    return nil \
end\
\
--\
-- bitwise operators\
--\
function dcsCommon.bitAND32(a, b)\
    if not a then a = 0 end \
    if not b then b = 0 end \
    local z = 0\
    local e = 1\
    for i = 0, 31 do \
        local a1 = a % 2 -- 0 or 1\
        local b1 = b % 2 -- 0 or 1\
        if a1 == 1 and b1 == 1 then \
            a = a - 1 -- remove bit \
            b = b - 1 \
            z = z + e\
        else\
            if a1 == 1 then a = a - 1 end -- remove bit \
            if b1 == 1 then b = b - 1 end \
        end\
        a = a / 2 -- shift right\
        b = b / 2        \
        e = e * 2 -- raise e by 1 \
    end\
    return z\
end\
\
function dcsCommon.num2bin(a)\
    if not a then a = 0 end \
    local z = \"\"\
    for i = 0, 31 do \
        local a1 = a % 2 -- 0 or 1\
        if a1 == 1 then \
            a = a - 1 -- remove bit \
            z = \"1\"..z\
        else\
            z = \"0\"..z\
        end\
        a = a / 2 -- shift right\
    end\
    return z\
end\
\
function dcsCommon.LSR(a, num)\
    if not a then a = 0 end \
    if not num then num = 16 end \
    for i = 1, num do \
        local a1 = a % 2 -- 0 or 1\
        if a1 == 1 then \
            a = a - 1 -- remove bit \
        end\
        a = a / 2 -- shift right\
    end\
    return a\
end\
\
--\
-- string wildcards \
--\
function dcsCommon.processStringWildcards(inMsg)\
    -- Replace STATIC bits of message like CR and zone name \
    if not inMsg then return \"<nil inMsg>\" end\
    local formerType = type(inMsg)\
    if formerType ~= \"string\" then inMsg = tostring(inMsg) end  \
    if not inMsg then inMsg = \"<inMsg is incompatible type \" .. formerType .. \">\" end \
    local outMsg = \"\"\
    -- replace line feeds \
    outMsg = inMsg:gsub(\"<n>\", \"\\n\")\
\
    return outMsg \
end\
\
--\
-- phonetic alphabet \
--\
dcsCommon.alphabet = {\
    a = \"alpha\",\
    b = \"bravo\",\
    c = \"charlie\",\
    d = \"delta\",\
    e = \"echo\",\
    f = \"foxtrot\",\
    g = \"golf\",\
    h = \"hotel\",\
    i = \"india\",\
    j = \"juliet\",\
    k = \"kilo\",\
    l = \"lima\",\
    m = \"mike\",\
    n = \"november\",\
    o = \"oscar\",\
    p = \"papa\",\
    q = \"quebec\",\
    r = \"romeo\",\
    s = \"sierra\",\
    t = \"tango\",\
    u = \"uniform\",\
    v = \"victor\",\
    w = \"whiskey\",\
    x = \"x-ray\",\
    y = \"yankee\",\
    z = \"zulu\",\
[\"0\"] = \"zero\",\
[\"1\"] = \"wun\",\
[\"2\"] = \"too\",\
[\"3\"] = \"tree\",\
[\"4\"] = \"fower\",\
[\"5\"] = \"fife\" ,\
[\"6\"] = \"six\",\
[\"7\"] = \"seven\",\
[\"8\"] = \"att\",\
[\"9\"] = \"niner\",\
[\" \"] = \"break\",\
}\
\
function dcsCommon.letter(inChar)\
    local theChar = \"\"\
    if type(inChar == \"string\") then \
        if #inChar < 1 then return \"#ERROR0#\" end\
        inChar = string.lower(inChar)\
        theChar = string.sub(inChar, 1, 1)\
    elseif type(inChar == \"number\") then \
        if inChar > 255 then return \"#ERROR>#\" end \
        if inChar < 0 then return \"#ERROR<#\" end \
        theChar = char(inChar)\
    else \
        return \"#ERRORT#\"\
    end\
--    trigger.action.outText(\"doing <\" .. theChar .. \">\", 30)\
    local a = dcsCommon.alphabet[theChar]\
    if a == nil then a = \"#ERROR?#\" end \
    return a \
end\
\
function dcsCommon.spellString(inString)\
    local res = \"\"\
    local first = true \
    for i = 1, #inString do\
        local c = inString:sub(i,i)\
        if first then \
            res = dcsCommon.letter(c)\
            first = false \
        else \
            res = res .. \" \" .. dcsCommon.letter(c)\
        end\
    end\
    return res \
end\
\
dcsCommon.letters = {\"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\", \"I\", \"J\", \"K\", \"L\", \"M\", \"N\", \
\"O\", \"P\", \"Q\", \"R\", \"S\", \"T\", \"U\", \"V\", \"W\", \"X\", \"Y\", \"Z\", }\
function dcsCommon.randomLetter(lowercase)\
    local theLetter = dcsCommon.pickRandom(dcsCommon.letters)\
    if lowercase then theLetter = string.lower(theLetter) end \
    return theLetter\
end\
\
--\
-- RGBA from hex\
--\
function dcsCommon.hexString2RGBA(inString) \
    -- enter with \"#FF0020\" (RGB) or \"#FF00AB99\" RGBA\
    -- check if it starts with #\
    if not inString then return nil end \
    if #inString ~= 7 and #inString ~=9 then return nil end \
    if inString:sub(1, 1) ~= \"#\" then return nil end \
    inString = inString:lower()\
    local red = tonumber(\"0x\" .. inString:sub(2,3)) \
    if not red then red = 0 end \
    local green = tonumber(\"0x\" .. inString:sub(4,5))\
    if not green then green = 0 end \
    local blue = tonumber(\"0x\" .. inString:sub(6,7))\
    if not blue then blue = 0 end \
    local alpha = 255 \
    if #inString == 9 then \
        alpha = tonumber(\"0x\" .. inString:sub(8,9))\
    end\
    if not alpha then alpha = 0 end\
    return {red/255, green/255, blue/255, alpha/255}\
end\
\
\
--\
-- Player handling \
--\
function dcsCommon.playerName2Coalition(playerName)\
    if not playerName then return 0 end \
    local factions = {1,2}\
    for idx, theFaction in pairs(factions) do \
        local players = coalition.getPlayers(theFaction)\
        for idy, theUnit in pairs(players) do \
            local upName = theUnit:getPlayerName()\
            if upName == playerName then return theFaction end\
        end\
    end\
    return 0\
end\
\
function dcsCommon.isPlayerUnit(theUnit)\
    -- new patch. simply check if getPlayerName returns something\
    if not theUnit then return false end \
    if not Unit.isExist(theUnit) then return end \
    if not theUnit.getPlayerName then return false end -- map/static object \
    local pName = theUnit:getPlayerName()\
    if pName then return true end \
    return false \
end\
\
function dcsCommon.getPlayerUnit(name)\
    for coa = 1, 2 do \
        local players = coalition.getPlayers(coa)\
        for idx, theUnit in pairs(players) do \
            if theUnit:getPlayerName() == name then return theUnit end\
        end\
    end\
    return nil \
end\
\
--\
-- theater and theater-related stuff \
--\
function dcsCommon.getMapName()\
    return env.mission.theatre\
end\
\
dcsCommon.magDecls = {Caucasus = 6.5,\
                      MarianaIslands = 1,\
                      Nevada = 12,\
                      PersianGulf = 2,\
                      Syria = 4,\
                      Normandy = -12 -- 1944, -1 in 2016 \
                      -- SinaiMap still missing \
                      -- Falklands still missing, big differences \
                      }\
                      \
function dcsCommon.getMagDeclForPoint(point) \
    -- WARNING! Approximations only, map-wide, not adjusted for year nor location!\
    -- serves as a stub for the day when DCS provides correct info \
    local map = dcsCommon.getMapName()\
    local decl = dcsCommon.magDecls[map]\
    if not decl then \
        trigger.action.outText(\"+++dcsC: unknown map <\" .. map .. \">, using dclenation 0\", 30)\
        decl = 0\
    end\
    return decl \
end \
\
--\
-- iterators\
--\
-- iteratePlayers - call callback for all player units\
-- callback is of signature callback(playerUnit)\
--\
\
function dcsCommon.iteratePlayers(callBack)\
    local factions = {0, 1, 2}\
    for idx, theFaction in pairs(factions) do \
        local players = coalition.getPlayers(theFaction)\
        for idy, theUnit in pairs(players) do \
            callBack(theUnit)\
        end\
    end\
end\
\
\
--\
-- MISC POINT CREATION\
--\
function dcsCommon.createPoint(x, y, z)\
    local newPoint = {}\
    newPoint.x = x\
    newPoint.y = y\
    newPoint.z = z -- works even if Z == nil\
    return newPoint\
end\
\
function dcsCommon.copyPoint(inPoint) \
    local newPoint = {}\
    newPoint.x = inPoint.x\
    newPoint.y = inPoint.y\
    -- handle xz only \
    if inPoint.z then \
        newPoint.z = inPoint.z \
    else \
        newPoint.z = inPoint.y \
    end\
    return newPoint    \
end\
\
--\
-- SEMAPHORES\
--\
dcsCommon.semaphores = {}\
\
-- replacement for trigger.misc.getUserFlag\
function dcsCommon.getUserFlag(flagName)\
    if dcsCommon.semaphores[flagName] then \
        return dcsCommon.semaphores[flagName]\
    end\
    \
    return trigger.misc.getUserFlag(flagName)\
end\
\
-- replacement for trigger.action.setUserFlag \
function dcsCommon.setUserFlag(flagName, theValue)\
    -- not yet connected: semaphores\
    \
    -- forget semaphore content if new value is old-school \
    if type(theValue) == \"number\" then \
        dcsCommon.semaphores[theValue] = nil --return to old-school \
    end\
    trigger.action.setUserFlag(flagName, theValue)\
end\
\
--\
--\
-- INIT\
--\
--\
    -- init any variables, tables etc that the lib requires internally\
    function dcsCommon.init()\
        cbID = 0\
        -- create ID tables\
        dcsCommon.collectMissionIDs()\
        \
        --dcsCommon.uuIdent = 0\
        if (dcsCommon.verbose) or true then\
          trigger.action.outText(\"dcsCommon v\" .. dcsCommon.version .. \" loaded\", 10)\
        end\
    end\
\
    \
-- do init. \
dcsCommon.init()\
\
",
                    ["predicate"] = "a_do_script",
                }, -- end of [1]
                [2] = 
                {
                    ["text"] = "cfxZones = {}\
cfxZones.version = \"4.3.2\"\
\
-- cf/x zone management module\
-- reads dcs zones and makes them accessible and mutable \
-- by scripting.\
--\
-- Copyright (c) 2021 - 2024 by Christian Franz and cf/x AG\
--\
\
--[[-- VERSION HISTORY\
- 4.0.0   - dmlZone OOP API started \
          - code revision / refactoring \
          - moved createPoint and copxPoint to dcsCommon, added bridging code \
          - re-routed all createPoint() invocations to dcsCommon \
          - removed anyPlayerInZone() because of cfxPlayer dependency\
          - numberArrayFromString() moved to dcsCommon, bridged \
          - flagArrayFromString() moved to dcsCommon, bridged \
          - doPollFlag() can differentiate between number method and string method \
            to enable passing an immediate negative value \
          - getNumberFromZoneProperty() enforces number return even on default\
          - immediate method switched to preceeding '#', to resolve conflict witzh \
            negative numbers, backwards compatibility with old (dysfunctional) method \
- 4.0.1   - dmlZone:getName()\
- 4.0.2   - removed verbosity from declutterZone (both versions)\
- 4.0.3   - new processDynamicVZU()\
          - wildcard uses processDynamicVZU\
- 4.0.4   - setFlagValue now supports multiple flags (OOP and classic)\
          - doSetFlagValue optimizations \
- 4.0.5   - dynamicAB wildcard \
          - processDynamicValueVU\
- 4.0.6   - hash mark forgotten QoL\
- 4.0.7   - drawZone()\
- 4.0.8   - markZoneWithObjects()\
          - cleanup \
          - markCenterWithObject\
          - markPointWithObject\
- 4.0.9   - createPolyZone now correctly returns new zone \
          - createSimplePolyZone correctly passes location to createPolyZone \
          - createPolyZone now correctly sets zone.point\
          - createPolyZone now correctly inits dcsOrigin\
          - createCircleZone noew correctly inits dcsOrigin\
- 4.0.10  - getBoolFromZoneProperty also supports \"on\" (=true) and \"off\" (=false)\
- 4.1.0   - getBoolFromZoneProperty 'on/off' support for dml variant as well \
- 4.1.1   - evalRemainder() updates \
- 4.1.2   - hash property missing warning \
- 4.2.0   - new createRandomPointInPopulatedZone()\
- 4.3.0   - boolean supports maybe, random, rnd, ?\
          - small optimization for randomInRange()\
          - randomDelayFromPositiveRange also allows 0 \
- 4.3.1   - new drawText() for zones \
          - dmlZones:getClosestZone() bridge \
- 4.3.2   - new getListFromZoneProperty()\
\
--]]--\
\
--\
-- ====================\
-- OOP dmlZone API HERE\
-- ====================\
--\
\
dmlZone = {}\
function dmlZone:new(o)\
    o = o or {}\
    setmetatable(o, self)\
    self.__index = self \
    self.name = \"dmlZone raw\"\
    self.isCircle = false\
    self.isPoly = false\
    self.radius = 0\
    self.poly = {}\
    self.bounds = {}\
    self.properties = {}\
    return o \
end \
\
--\
-- CLASSIC INTERFACE\
--\
cfxZones.verbose = false\
cfxZones.caseSensitiveProperties = false -- set to true to make property names case sensitive \
cfxZones.ups = 1 -- updates per second. updates moving zones\
\
cfxZones.zones = {} -- these are the zone as retrieved from the mission.\
                    -- ALWAYS USE THESE, NEVER DCS's ZONES!!!!\
\
function cfxZones.readFromDCS(clearfirst)\
    if (clearfirst) then\
        cfxZones.zones = {}\
    end\
    -- not all missions have triggers or zones\
    if not env.mission.triggers then \
        if cfxZones.verbose then \
            trigger.action.outText(\"cf/x zones: no env.triggers defined\", 10)\
        end\
        return\
    end\
    \
    if not env.mission.triggers.zones then \
        if cfxZones.verbose then \
            trigger.action.outText(\"cf/x zones: no zones defined\", 10)\
        end\
        return;\
    end\
\
    -- we only retrieve the data we need. At this point it is name, location and radius\
    -- and put this in our own little  structure. we also convert to all upper case name for index\
    -- and assume that the name may also carry meaning, e.g. 'LZ:' defines a landing zone\
    -- so we can quickly create other sets from this\
    -- zone object. DCS 2.7 introduced quads, so this is supported as well\
    --   name - name in upper case\
    --   isCircle - true if circular zone \
    --   isPoly - true if zone is defined by convex polygon, e.g. quad \
    --   point - vec3 (x 0 z) - zone's in-world center, used to place the coordinate\
    --   radius - number, zero when quad\
    --   bounds - aabb with attributes ul, ur, ll, lr (upper left .. lower right) as (x, 0, z)\
    --   poly - array 1..n of poly points, wound counter-clockwise \
    \
    for i, dcsZone in pairs(env.mission.triggers.zones) do\
        if type(dcsZone) == 'table' then -- hint taken from MIST: verify type when reading from dcs\
                                         -- dcs data is like a box of chocolates...\
            local newZone = dmlZone:new(nil) -- WAS: {} -- OOP introduction July 2023\
            -- name, converted to upper is used only for indexing\
            -- the original name remains untouched\
            newZone.dcsZone = dcsZone\
            newZone.name = dcsZone.name\
            newZone.isCircle = false\
            newZone.isPoly = false\
            newZone.radius = 0\
            newZone.poly = {}\
            newZone.bounds = {}\
            newZone.properties = {} -- dcs has this too, copy if present\
            if dcsZone.properties then \
                newZone.properties = dcsZone.properties \
            else\
                newZone.properties = {}\
            end -- WARNING: REF COPY. May need to clone \
            \
            local upperName = newZone.name:upper()\
            \
            -- location as 'point'\
            -- WARNING: zones locs are 2D (x,y) pairs, while y in DCS is altitude.\
            --          so we need to change (x,y) into (x, 0, z). Since Zones have no\
            --          altitude (they are an infinite cylinder) this works. Remember to \
            --          drop y from zone calculations to see if inside. \
            -- WARNING: ME linked zones have a relative x any y \
            --          to the linked unit \
            if dcsZone.linkUnit then \
                -- calculate the zone's real position by accessing the unit's MX data \
                -- as precached by dcsCommon\
                local ux, uy = dcsCommon.getUnitStartPosByID(dcsZone.linkUnit)\
                newZone.point = dcsCommon.createPoint(ux + dcsZone.x, 0, uy + dcsZone.y)\
                newZone.dcsOrigin = dcsCommon.createPoint(ux + dcsZone.x, 0, uy + dcsZone.y)\
            else \
                newZone.point = dcsCommon.createPoint(dcsZone.x, 0, dcsZone.y)\
                newZone.dcsOrigin = dcsCommon.createPoint(dcsZone.x, 0, dcsZone.y)\
            end\
\
            -- start type processing. if zone.type exists, we have a mission \
            -- created with 2.7 or above, else earlier \
            local zoneType = 0\
            if (dcsZone.type) then \
                zoneType = dcsZone.type \
            end\
            \
            if zoneType == 0 then \
                -- circular zone \
                newZone.isCircle = true \
                newZone.radius = dcsZone.radius\
                newZone.maxRadius = newZone.radius -- same for circular\
    \
            elseif zoneType == 2 then\
                -- polyZone\
                newZone.isPoly = true \
                newZone.radius = dcsZone.radius -- radius is still written in DCS, may change later. The radius has no meaning and is the last radius written before zone changed to poly.\
                -- note that newZone.point is only inside the tone for \
                -- convex polys, and DML only correctly works with convex polys\
                -- now transfer all point in the poly\
                -- note: DCS in 2.7 misspells vertices as 'verticies'\
                -- correct for this \
                newZone.maxRadius = 0\
                local verts = {}\
                if dcsZone.verticies then verts = dcsZone.verticies \
                else \
                    -- in later versions, this was corrected\
                    verts = dcsZone.vertices -- see if this is ever called\
                end\
                \
                for v=1, #verts do\
                    local dcsPoint = verts[v]\
                    local polyPoint = cfxZones.createPointFromDCSPoint(dcsPoint) -- (x, y) --> (x, 0, y-->z)\
                    newZone.poly[v] = polyPoint\
                    -- measure distance from zone's point, and store maxRadius \
                    -- dcs always saves a point with the poly zone \
                    local dist = dcsCommon.dist(newZone.point, polyPoint)\
                    if dist > newZone.maxRadius then newZone.maxRadius = dist end \
                end\
            else \
                \
                trigger.action.outText(\"cf/x zones: malformed zone #\" .. i .. \" unknown type \" .. zoneType, 10)\
            end\
            \
\
            -- calculate bounds\
            cfxZones.calculateZoneBounds(newZone) \
\
            -- add to my table\
            cfxZones.zones[upperName] = newZone -- WARNING: UPPER ZONE!!!\
            --trigger.action.outText(\"znd: procced \" .. newZone.name .. \" with radius \" .. newZone.radius, 30)\
        else\
            if cfxZones.verbose then \
                trigger.action.outText(\"cf/x zones: malformed zone #\" .. i .. \" dropped\", 10)\
            end\
        end -- else var not a table\
        \
    end -- for all zones kvp\
end -- readFromDCS\
\
function cfxZones.calculateZoneBounds(theZone)\
    if not (theZone) then return \
    end\
    \
    local bounds = theZone.bounds -- copy ref!\
    \
    if theZone.isCircle then \
        -- aabb are easy: center +/- radius \
        local center = theZone.point\
        local radius = theZone.radius \
        -- dcs uses z+ is down on map\
        -- upper left is center - radius \
        bounds.ul = dcsCommon.createPoint(center.x - radius, 0, center.z - radius)\
        bounds.ur = dcsCommon.createPoint(center.x + radius, 0, center.z - radius)\
        bounds.ll = dcsCommon.createPoint(center.x - radius, 0, center.z + radius)\
        bounds.lr = dcsCommon.createPoint(center.x + radius, 0, center.z + radius)\
        \
    elseif theZone.isPoly then\
        local poly = theZone.poly -- ref copy!\
        -- create the four points\
        local ll = cfxZones.createPointFromPoint(poly[1])\
        local lr = cfxZones.createPointFromPoint(poly[1])\
        local ul = cfxZones.createPointFromPoint(poly[1])\
        local ur = cfxZones.createPointFromPoint(poly[1])\
        \
        local pRad = dcsCommon.dist(theZone.point, poly[1]) -- rRad is radius for polygon from theZone.point \
        \
        -- now iterate through all points and adjust bounds accordingly \
        for v=2, #poly do \
            local vertex = poly[v]\
            if (vertex.x < ll.x) then ll.x = vertex.x; ul.x = vertex.x end \
            if (vertex.x > lr.x) then lr.x = vertex.x; ur.x = vertex.x end \
            if (vertex.z < ul.z) then ul.z = vertex.z; ur.z = vertex.z end\
            --if (vertex.z > ll.z) then ll.z = vertex.z; lr.z = vertex.z end\
            if (vertex.z > ur.z) then ur.z = vertex.z; ul.z = vertex.z end             \
            local dp = dcsCommon.dist(theZone.point, vertex)\
            if dp > pRad then pRad = dp end -- find largst distance to vertex\
        end\
        \
        -- now keep the new point references\
        -- and store them in the zone's bounds\
        bounds.ll = ll\
        bounds.lr = lr\
        bounds.ul = ul\
        bounds.ur = ur \
        -- we may need to ascertain why we need ul, ur, ll, lr instead of just ll and ur \
        -- store pRad \
        theZone.pRad = pRad -- not sure we'll ever need that, but at least we have it\
\
    else \
        -- huston, we have a problem\
        if cfxZones.verbose then \
            trigger.action.outText(\"cf/x zones: calc bounds: zone \" .. theZone.name .. \" has unknown type\", 30)\
        end\
    end\
    \
end\
\
function dmlZone:calculateZoneBounds()\
    cfxZones.calculateZoneBounds(self)\
end \
\
function cfxZones.createPoint(x, y, z)  -- bridge to dcsCommon, backward comp.\
    return dcsCommon.createPoint(x, y, z) \
end\
\
function cfxZones.copyPoint(inPoint) -- bridge to dcsCommon, backward comp.\
    return dcsCommon.copyPoint(inPoint)\
end\
\
function cfxZones.createHeightCorrectedPoint(inPoint) -- this should be in dcsCommon\
    local cP = dcsCommon.createPoint(inPoint.x, land.getHeight({x=inPoint.x, y=inPoint.z}),inPoint.z)\
    return cP\
end\
\
function cfxZones.getHeightCorrectedZonePoint(theZone)\
    local thePoint = cfxZone.getPoint(theZone)\
    return cfxZones.createHeightCorrectedPoint(thePoint)\
end\
\
function dmlZone:getHeightCorrectedZonePoint()\
    local thePoint = self:getPoint()\
    return dcsCommon.createPoint(thePoint.x, land.getHeight({x=thePoint.x, y=thePoint.z}),thePoint.z)\
end\
\
function cfxZones.createPointFromPoint(inPoint)\
    return cfxZones.copyPoint(inPoint)\
end\
\
function cfxZones.createPointFromDCSPoint(inPoint) \
    return dcsCommon.createPoint(inPoint.x, 0, inPoint.y)\
end\
\
\
function cfxZones.createRandomPointInsideBounds(bounds)\
    -- warning: bounds do not move woth zone! may have to be updated\
    local x = math.random(bounds.ll.x, ur.x)\
    local z = math.random(bounds.ll.z, ur.z)\
    return dcsCommon.createPoint(x, 0, z)\
end\
\
function cfxZones.createRandomPointOnZoneBoundary(theZone)\
    if not theZone then return nil end \
    if theZone.isPoly then \
        local loc, dx, dy = cfxZones.createRandomPointInPolyZone(theZone, true)\
        return loc, dx, dy \
    else \
        local loc, dx, dy = cfxZones.createRandomPointInCircleZone(theZone, true)\
        return loc, dx, dy \
    end\
end\
\
function dmlZone:createRandomPointOnZoneBoundary()\
    return cfxZones.createRandomPointOnZoneBoundary(self)\
end \
\
function cfxZones.createRandomPointInZone(theZone)\
    if not theZone then return nil end \
    if theZone.isPoly then \
        local loc, dx, dy = cfxZones.createRandomPointInPolyZone(theZone)\
        return loc, dx, dy \
    else \
        local loc, dx, dy = cfxZones.createRandomPointInCircleZone(theZone)\
        return loc, dx, dy \
    end\
end\
\
function dmlZone:createRandomPointInZone()\
    local loc, dx, dy = cfxZones.createRandomPointInZone(self)\
    return loc, dx, dy\
end \
\
\
function cfxZones.randomPointInZone(theZone)\
    local loc, dx, dy =  cfxZones.createRandomPointInZone(theZone)\
    return loc, dx, dy \
end\
\
function dmlZone:randomPointInZone()\
    local loc, dx, dy =  cfxZones.createRandomPointInZone(self)\
    return loc, dx, dy \
end\
\
function cfxZones.createRandomPointInCircleZone(theZone, onEdge)\
    if not theZone.isCircle then \
        trigger.action.outText(\"+++Zones: warning - createRandomPointInCircleZone called for non-circle zone <\" .. theZone.name .. \">\", 30)\
        return {x=theZone.point.x, y=0, z=theZone.point.z}\
    end\
    \
    -- ok, let's first create a random percentage value for the new radius\
    -- now lets get a random degree\
    local degrees = math.random() * 2 * 3.14152 -- radiants. \
    local r = theZone.radius \
    if not onEdge then \
        r = r * math.random()\
    end \
    local p = cfxZones.getPoint(theZone) -- force update of zone if linked\
    local dx = r * math.cos(degrees)\
    local dz = r * math.sin(degrees)\
    local px = p.x + dx -- r * math.cos(degrees)\
    local pz = p.z + dz -- r * math.sin(degrees)\
    return {x=px, y=0, z = pz}, dx, dz -- returns loc and offsets to theZone.point\
end\
\
function dmlZone:createRandomPointInCircleZone(theZone, onEdge)\
    local p, dx, dz = cfxZones.createRandomPointInCircleZone(self, onEdge)\
    return p, dx, dz \
end \
\
function cfxZones.createRandomPointInPolyZone(theZone, onEdge)\
    if not theZone.isPoly then \
        trigger.action.outText(\"+++Zones: warning - createRandomPointInPolyZone called for non-poly zone <\" .. theZone.name .. \">\", 30)\
        return dcsCommon.createPoint(theZone.point.x, 0, theZone.point.z)\
    end\
    -- force update of all points \
    local p = cfxZones.getPoint(theZone)\
    \
    -- point in convex poly: choose two different lines from that polygon \
    local lineIdxA = dcsCommon.smallRandom(#theZone.poly)\
    repeat lineIdxB = dcsCommon.smallRandom(#theZone.poly) until (lineIdxA ~= lineIdxB)\
    \
    -- we now have two different lines. pick a random point on each. \
    -- we use lerp to pick any point between a and b \
    local a = theZone.poly[lineIdxA]\
    lineIdxA = lineIdxA + 1 -- get next point in poly and wrap around\
    if lineIdxA > #theZone.poly then lineIdxA = 1 end \
    local b = theZone.poly[lineIdxA] \
    local randompercent = math.random()\
    local sourceA = dcsCommon.vLerp (a, b, randompercent)\
    -- if all we want is a point on an edge, we are done \
    if onEdge then \
        local polyPoint = sourceA\
        return polyPoint, polyPoint.x - p.x, polyPoint.z - p.z -- return loc, dx, dz \
    end \
    \
    -- now get point on second line \
    a = theZone.poly[lineIdxB]\
    lineIdxB = lineIdxB + 1 -- get next point in poly and wrap around\
    if lineIdxB > #theZone.poly then lineIdxB = 1 end \
    b = theZone.poly[lineIdxB] \
    randompercent = math.random()\
    local sourceB = dcsCommon.vLerp (a, b, randompercent)\
    \
    -- now take a random point on that line that entirely \
    -- runs through the poly \
    randompercent = math.random()\
    local polyPoint = dcsCommon.vLerp (sourceA, sourceB, randompercent)\
    return polyPoint, polyPoint.x - p.x, polyPoint.z - p.z -- return loc, dx, dz \
end\
\
function dmlZone:createRandomPointInPolyZone(onEdge)\
    local p, dx, dz = cfxZones.createRandomPointInPolyZone(self, onEdge)\
    return p, dx, dz \
end \
\
function dmlZone:createRandomPointInPopulatedZone(radius, maxTries)\
    if not maxTries then maxTries = 20 end \
    if not radius then radius = 10 end -- meters \
    local cnt = 0\
    local p, dx, dz\
    repeat\
        p, dx, dz = self:createRandomPointInZone() -- p is x, 0, z \
        local hits, collector = cfxZones.objectsInRange(p, radius) \
        if hits < 1 then return p, dx, dz end\
        if hits == 1 then \
            local o = collector[1]\
            local op = o:getPoint()\
            d = dcsCommon.distFlat(op, p)\
--            trigger.action.outText(\"singleDist = \" .. d, 30)\
            if d > radius/2 then \
--                trigger.action.outText(\"good enough, will use\", 30)\
                return p, dx, dz \
            end \
        end \
        cnt = cnt + 1\
--        trigger.action.outText(hits .. \"hits --> failed try \" .. cnt, 30)\
    until cnt > maxTries\
    return p, dx, dz\
end\
\
function cfxZones.createRandomPointInPopulatedZone(theZone, radius, maxTries)\
    if not theZone then return nil, nil, nil end \
    local p, dx, dz = theZone:createRandomPointInPopulatedZone(radius, maxTries)\
    return p, dx, dz\
end\
\
--[[--\
function dmlZone:createRandomPointInPopulatedZone(radius, maxTries)\
    if not maxTries then maxTries = 20 end \
    local cnt = 0\
    local p, dx, dz\
    p, dx, dz = self:createRandomPointInZone() -- p is x, 0, z \
    repeat\
        local hits = cfxZones.objectsInRange(p, radius) \
        if hits < 1 then return p, dx, dz end \
        -- move to the right by radius\
        p.z = p.z + radius\
        dz = dz + radius \
        cnt = cnt + 1\
        trigger.action.outText(\"failed try \" .. cnt, 30)\
    until cnt > maxTries\
    return p, dx, dz\
end\
--]]--\
function cfxZones.objectHandler(theObject, theCollector) -- for world.search\
    table.insert(theCollector, theObject)\
    return true \
end\
\
function cfxZones.objectsInRange(pt, range) \
    if not range then range = 100 end -- meters\
    local allCats = {1, 2, 3, 4, 5, 6} -- all cats \
    local lp = {x = pt.x, y = pt.z}\
    pt.y = land.getHeight(lp)\
    local collector = {}    \
    -- now build the search argument \
    local args = {\
            id = world.VolumeType.SPHERE,\
            params = {\
                point = pt,\
                radius = range -- range\
            }\
        }\
    -- now call search\
    world.searchObjects(allCats, args, cfxZones.objectHandler, collector)\
    -- now filter for distance because search finds too many \
    local filtered = {}\
    for idx, anObject in pairs(collector) do \
        -- calc dist and filter \
        local op = anObject:getPoint()\
        local dist = dcsCommon.dist(pt, op)\
        if dist < range then \
--            local e = {\
--                    dist = dist,\
--                    o = anObject\
--                }\
--            table.insert(filtered, e)\
            table.insert(filtered, anObject)\
        end\
    end \
    \
    return #filtered, filtered  \
end\
\
function cfxZones.addZoneToManagedZones(theZone)\
    local upperName = string.upper(theZone.name) -- newZone.name:upper()\
    cfxZones.zones[upperName] = theZone\
end\
\
function dmlZone:addZoneToManagedZones()\
    local upperName = string.upper(self.name) -- newZone.name:upper()\
    cfxZones.zones[upperName] = self\
end\
\
function cfxZones.createUniqueZoneName(inName, searchSet)\
    if not inName then return nil end \
    if not searchSet then searchSet = cfxZones.zones end \
    inName = inName:upper()\
    while searchSet[inName] ~= nil do \
        inName = inName .. \"X\"\
    end\
    return inName\
end\
\
function cfxZones.createSimpleZone(name, location, radius, addToManaged)\
    if not radius then radius = 10 end\
    if not addToManaged then addToManaged = false end \
    if not location then \
        location = {}\
    end\
    if not location.x then location.x = 0 end \
    if not location.z then location.z = 0 end \
    \
    local newZone = cfxZones.createCircleZone(name, location.x, location.z, radius)\
    \
    if addToManaged then \
        cfxZones.addZoneToManagedZones(newZone)\
    end\
    return newZone\
end\
\
function cfxZones.createCircleZone(name, x, z, radius) \
    local newZone = dmlZone:new(nil) -- {} OOP compatibility \
    newZone.isCircle = true\
    newZone.isPoly = false\
    newZone.poly = {}\
    newZone.bounds = {}\
            \
    newZone.name = name\
    newZone.radius = radius\
    newZone.point = dcsCommon.createPoint(x, 0, z)\
     newZone.dcsOrigin = dcsCommon.createPoint(x, 0, z)\
\
    -- props \
    newZone.properties = {}\
    \
    -- calculate my bounds\
    cfxZones.calculateZoneBounds(newZone)\
    \
    return newZone\
end\
\
function cfxZones.createSimplePolyZone(name, location, points, addToManaged)\
    if not addToManaged then addToManaged = false end \
    if not location then \
        location = {}\
    end\
    if not location.x then location.x = 0 end \
    if not location.z then location.z = 0 end \
    if not location.y then location.y = 0 end \
\
    local newZone = cfxZones.createPolyZone(name, points, location)\
    \
    if addToManaged then \
        cfxZones.addZoneToManagedZones(newZone)\
    end\
    return newZone\
end\
\
function cfxZones.createSimpleQuadZone(name, location, points, addToManaged)\
    if not location then \
        location = {}\
    end\
    if not location.x then location.x = 0 end \
    if not location.z then location.z = 0 end \
        \
    -- synthesize 4 points if they don't exist\
    -- remember: in DCS positive x is up, positive z is right \
    if not points then \
        points = {} \
    end\
    if not points[1] then \
        -- upper left \
        points[1] = {x = location.x-1, y = 0, z = location.z-1}\
    end\
    if not points[2] then \
        -- upper right \
        points[2] = {x = location.x-1, y = 0, z = location.z+1}\
    end\
    if not points[3] then \
        -- lower right \
        points[3] = {x = location.x+1, y = 0, z = location.z+1}\
    end\
    if not points[4] then \
        -- lower left \
        points[4] = {x = location.x+1, y = 0, z = location.z-1}\
    end\
    \
    return cfxZones.createSimplePolyZone(name, location, points, addToManaged)\
end\
\
function cfxZones.createPolyZone(name, poly, location) -- poly must be array of point type\
    local newZone = dmlZone:new(nil) -- {} OOP compatibility \
    if not location then location = {x=0, y=0, z=0} end \
    newZone.point = dcsCommon.createPoint(location.x, 0, location.z)\
    newZone.dcsOrigin = dcsCommon.createPoint(location.x, 0, location.z)\
    newZone.isCircle = false\
    newZone.isPoly = true\
    newZone.poly = {}\
    newZone.bounds = {}\
            \
    newZone.name = name\
    newZone.radius = 0\
    -- copy poly\
    for v=1, #poly do \
        local theVertex = poly[v] \
        newZone.poly[v] = cfxZones.createPointFromPoint(theVertex) \
    end\
    \
    -- properties \
    newZone.properties = {}\
    \
    cfxZones.calculateZoneBounds(newZone)\
    return newZone \
end\
\
function cfxZones.createRandomZoneInZone(name, inZone, targetRadius, entirelyInside)\
    -- create a new circular zone with center placed inside inZone\
    -- if entirelyInside is false, only the zone's center is guaranteed to be inside\
    -- inZone.\
    -- entirelyInside is not guaranteed for polyzones\
        \
    if inZone.isCircle then \
        local sourceRadius = inZone.radius\
        if entirelyInside and targetRadius > sourceRadius then targetRadius = sourceRadius end\
        if entirelyInside then sourceRadius = sourceRadius - targetRadius end\
    \
        -- ok, let's first create a random percentage value for the new radius\
        local percent = 1 / math.random(100)\
        -- now lets get a random degree\
        local degrees = math.random(360) * 3.14152 / 180 -- ok, it's actually radiants. \
        local r = sourceRadius * percent \
        local x = inZone.point.x + r * math.cos(degrees)\
        local z = inZone.point.z + r * math.sin(degrees)\
        -- construct new zone\
        local newZone = cfxZones.createCircleZone(name, x, z, targetRadius)\
        return newZone\
    \
    elseif inZone.isPoly then \
        local newPoint = cfxZones.createRandomPointInPolyZone(inZone)\
        -- construct new zone\
        local newZone = cfxZones.createCircleZone(name, newPoint.x, newPoint.z, targetRadius)\
        return newZone\
        \
    else \
        -- zone type unknown\
        trigger.action.outText(\"CreateZoneInZone: unknown zone type for inZone =\" .. inZone.name ,  10)\
        return nil \
    end\
end\
\
-- polygon inside zone calculations\
\
\
-- isleft returns true if point P is to the left of line AB \
-- by determining the sign (up or down) of the normal vector of \
-- the two vectors PA and PB in the y coordinate. We arbitrarily define\
-- left as being > 0, so right is <= 0. As long as we always use the \
-- same comparison, it does not matter what up or down mean.\
-- this is important because we don't know if dcs always winds quads\
-- the same way, we must simply assume that they are wound as a polygon \
function cfxZones.isLeftXZ(A, B, P)\
    return ((B.x - A.x)*(P.z - A.z) - (B.z - A.z)*(P.x - A.x)) > 0\
end\
\
-- returns true/false for inside\
function cfxZones.isPointInsideQuad(thePoint, A, B, C, D) \
    -- Inside test (only convex polygons): \
    -- point lies on the same side of each quad's vertex AB, BC, CD, DA\
    -- how do we find out which side a point lies on? via the cross product\
    -- see isLeft below\
    \
    -- so all we need to do is make sure all results of isLeft for all\
    -- four sides are the same\
    mustMatch = isLeftXZ(A, B, thePoint) -- all test results must be the same and we are ok\
                                       -- they just must be the same side.\
    if (cfxZones.isLeftXZ(B, C, thePoint ~= mustMatch)) then return false end -- on other side than all before\
    if (cfxZones.isLeftXZ(C, D, thePoint ~= mustMatch)) then return false end \
    if (cfxZones.isLeftXZ(D, A, thePoint ~= mustMatch)) then return false end\
    return true\
end\
\
-- generalized version of insideQuad, assumes winding of poly, poly convex, poly closed\
function cfxZones.isPointInsidePoly(thePoint, poly)\
    local mustMatch = cfxZones.isLeftXZ(poly[1], poly[2], thePoint)\
    for v=2, #poly-1 do \
        if cfxZones.isLeftXZ(poly[v], poly[v+1], thePoint) ~= mustMatch then return false end\
    end\
    -- final test\
    if cfxZones.isLeftXZ(poly[#poly], poly[1], thePoint) ~= mustMatch then return false end\
    \
    return true\
end;\
\
function cfxZones.isPointInsideZone(thePoint, theZone, radiusIncrease)\
    -- radiusIncrease only works for circle zones \
    if not radiusIncrease then radiusIncrease = 0 end \
    local p = {x=thePoint.x, y = 0, z = thePoint.z} -- zones have no altitude\
    if (theZone.isCircle) then \
        local zp = cfxZones.getPoint(theZone)\
        local d = dcsCommon.dist(p, theZone.point)\
        return d < theZone.radius + radiusIncrease, d \
    end \
    \
    if (theZone.isPoly) then \
        --trigger.action.outText(\"zne: isPointInside: \" .. theZone.name .. \" is Polyzone!\", 30)\
        return (cfxZones.isPointInsidePoly(p, theZone.poly)), 0 -- always returns delta 0\
    end\
\
    trigger.action.outText(\"isPointInsideZone: Unknown zone type for \" .. outerZone.name, 10)\
end\
\
function dmlZone:isPointInsideZone(thePoint, radiusIncrease) -- warning: param order!\
    return cfxZones.isPointInsideZone(thePoint, self, radiusIncrease)\
end \
\
-- isZoneInZone returns true if center of innerZone is inside  outerZone\
function cfxZones.isZoneInsideZone(innerZone, outerZone) \
    local p = cfxZones.getPoint(innerZone)\
    return cfxZones.isPointInsideZone(p, outerZone)    \
end\
\
function dmlZone:isZoneInsideZone(outerZone) \
    return cfxZones.isPointInsideZone(self:getPoint(), outerZone)    \
end\
\
function cfxZones.getZonesContainingPoint(thePoint, testZones) -- return array \
    if not testZones then \
        testZones = cfxZones.zones \
    end \
    \
    local containerZones = {}\
    for tName, tData in pairs(testZones) do \
        if cfxZones.isPointInsideZone(thePoint, tData) then \
            table.insert(containerZones, tData)\
        end\
    end\
\
    return containerZones\
end\
\
function cfxZones.getFirstZoneContainingPoint(thePoint, testZones)\
    if not testZones then \
        testZones = cfxZones.zones \
    end \
    \
    for tName, tData in pairs(testZones) do \
        if cfxZones.isPointInsideZone(thePoint, tData) then \
            return tData\
        end\
    end\
\
    return nil\
end\
\
function cfxZones.getAllZonesInsideZone(superZone, testZones) -- returnes array!\
    if not testZones then \
        testZones = cfxZones.zones \
    end \
    \
    local containedZones = {}\
    for zName, zData in pairs(testZones) do\
        if cfxZones.isZoneInsideZone(zData, superZone) then \
            if zData ~= superZone then \
                -- we filter superzone because superzone usually resides \
                -- inside itself \
                table.insert(containedZones, zData)\
            end\
        end\
    end\
    return containedZones \
end\
\
function dmlZone:getAllZonesInsideZone(testZones)\
    return cfxZones.getAllZonesInsideZone(self, testZones)\
end\
\
\
function cfxZones.getZonesWithAttributeNamed(attributeName, testZones)\
    if not testZones then testZones = cfxZones.zones end \
\
    local attributZones = {}\
    for aName,aZone in pairs(testZones) do\
        local attr = cfxZones.getZoneProperty(aZone, attributeName)\
        if attr then \
            -- this zone has the requested attribute\
            table.insert(attributZones, aZone)\
        end\
    end\
    return attributZones\
end\
\
--\
-- zone volume management\
--\
\
function cfxZones.getZoneVolume(theZone)\
    if not theZone then return nil end \
    \
    if (theZone.isCircle) then \
        -- create a sphere volume\
        local p = cfxZones.getPoint(theZone)\
        p.y = land.getHeight({x = p.x, y = p.z})\
        local r = theZone.radius\
        if r < 10 then r = 10 end \
        local vol = {\
            id = world.VolumeType.SPHERE,\
            params = {\
                point = p,\
                radius = r\
            }\
        }\
        return vol \
    elseif (theZone.isPoly) then \
        -- build the box volume, using the zone's bounds ll and ur points \
        local lowerLeft = {}\
        -- we build x = westerm y = southern, Z = alt \
        local alt = land.getHeight({x=theZone.bounds.ll.x, y = theZone.bounds.ll.z}) - 10\
        lowerLeft.x = theZone.bounds.ll.x \
        lowerLeft.z = theZone.bounds.ll.z \
        lowerLeft.y = alt -- we go lower \
        \
        local upperRight = {}\
        alt = land.getHeight({x=theZone.bounds.ur.x, y = theZone.bounds.ur.z}) + 10\
        upperRight.x = theZone.bounds.ur.x \
        upperRight.z = theZone.bounds.ur.z \
        upperRight.y = alt -- we go higher \
        \
        -- construct volume \
        local vol = {\
            id = world.VolumeType.BOX,\
            params = {\
                min = lowerLeft,\
                max = upperRight\
            }\
        }\
        return vol \
    else \
        trigger.action.outText(\"zne: unknown zone type for <\" .. theZone.name .. \">\", 30)\
    end\
end\
\
function dmlZone:getZoneVolume()\
    return cfxZones.getZoneVolume(self)\
end \
\
\
function cfxZones.declutterZone(theZone)\
    if not theZone then return end \
    local theVol = cfxZones.getZoneVolume(theZone)\
    world.removeJunk(theVol)\
end\
\
function dmlZone:declutterZone()\
    local theVol = cfxZones.getZoneVolume(self)\
    world.removeJunk(theVol)\
end\
\
--\
-- units / groups in zone\
--\
function cfxZones.allGroupsInZone(theZone, categ) -- categ is optional, must be code \
    -- warning: does not check for existing!\
    local inZones = {}\
    local coals = {0, 1, 2} -- all coalitions\
    for idx, coa in pairs(coals) do \
        local allGroups = coalition.getGroups(coa, categ)\
        for key, group in pairs(allGroups) do -- iterate all groups\
            if cfxZones.isGroupPartiallyInZone(group, theZone) then\
                table.insert(inZones, group)\
            end\
        end\
    end\
    return inZones\
end\
\
function dmlZone:allGroupsInZone(categ)\
    return cfxZones.allGroupsInZone(self, categ)\
end\
\
function cfxZones.allGroupNamesInZone(theZone, categ) -- categ is optional, must be code \
    -- warning: does not check for existing!\
    local inZones = {}\
    local coals = {0, 1, 2} -- all coalitions\
    for idx, coa in pairs(coals) do \
        local allGroups = coalition.getGroups(coa, categ)\
        for key, group in pairs(allGroups) do -- iterate all groups\
            if cfxZones.isGroupPartiallyInZone(group, theZone) then\
                table.insert(inZones, group:getName())\
            end\
        end\
    end\
    return inZones\
end\
\
function dmlZone:allGroupNamesInZone(categ)\
    return cfxZones.allGroupNamesInZone(self, categ)\
end\
\
function cfxZones.allStaticsInZone(theZone, useOrigin) -- categ is optional, must be code \
    -- warning: does not check for existing!\
    local inZones = {}\
    local coals = {0, 1, 2} -- all coalitions\
    for idx, coa in pairs(coals) do \
        local allStats = coalition.getStaticObjects(coa)\
        for key, statO in pairs(allStats) do -- iterate all groups\
            local oP = statO:getPoint()\
            if useOrigin then \
                if cfxZones.pointInZone(oP, theZone, true) then \
                    -- use DCS original coords\
                    table.insert(inZones, statO)\
                end\
            elseif cfxZones.pointInZone(oP, theZone) then\
                table.insert(inZones, statO)\
            end\
        end\
    end\
    return inZones\
end\
\
function dmlZone:allStaticsInZone(useOrigin)\
    return cfxZones.allStaticsInZone(self, useOrigin)\
end\
\
\
function cfxZones.groupsOfCoalitionPartiallyInZone(coal, theZone, categ) -- categ is optional\
    local groupsInZone = {}\
    local allGroups = coalition.getGroups(coal, categ)\
    for key, group in pairs(allGroups) do -- iterate all groups\
        if group:isExist() then\
            if cfxZones.isGroupPartiallyInZone(group, theZone) then\
                table.insert(groupsInZone, group)            \
            end\
        end\
    end\
    return groupsInZone\
end\
\
function cfxZones.isGroupPartiallyInZone(aGroup, aZone)\
    if not aGroup then return false end \
    if not aZone then return false end \
        \
    if not aGroup:isExist() then return false end \
    local allUnits = aGroup:getUnits()\
    for uk, aUnit in pairs (allUnits) do \
        if Unit.isExist(aUnit) and aUnit:getLife() > 1 then         \
            local p = aUnit:getPoint()\
            local inzone, percent, dist = cfxZones.pointInZone(p, aZone)\
            if inzone then        \
                return true\
            end \
        end\
    end\
    return false\
end\
\
function cfxZones.isEntireGroupInZone(aGroup, aZone)\
    if not aGroup then return false end \
    if not aZone then return false end \
    if not aGroup:isExist() then return false end \
    local allUnits = aGroup:getUnits()\
    for uk, aUnit in pairs (allUnits) do \
        if aUnit:isExist() and aUnit:getLife() > 1 then \
            local p = aUnit:getPoint()\
            if not cfxZones.isPointInsideZone(p, aZone) then \
                return false\
            end\
        end\
    end\
    return true\
end\
\
function dmlZone:isEntireGroupInZone(aGroup)\
    return cfxZones.isEntireGroupInZone(aGroup, self)\
end\
\
--\
-- Zone Manipulation\
--\
\
function cfxZones.offsetZone(theZone, dx, dz)\
    -- first, update center \
    theZone.point.x = theZone.point.x + dx\
    theZone.point.z = theZone.point.z + dz \
    \
    -- now process all polygon points - it's empty for circular, so don't worry\
    for v=1, #theZone.poly do \
        theZone.poly[v].x = theZone.poly[v].x + dx\
        theZone.poly[v].z = theZone.poly[v].z + dz \
    end\
    \
    -- update zone bounds \
    theZone.bounds.ll.x = theZone.bounds.ll.x + dx \
    theZone.bounds.lr.x = theZone.bounds.lr.x + dx\
    theZone.bounds.ul.x = theZone.bounds.ul.x + dx \
    theZone.bounds.ur.x = theZone.bounds.ur.x + dx\
\
    theZone.bounds.ll.z = theZone.bounds.ll.z + dz \
    theZone.bounds.lr.z = theZone.bounds.lr.z + dz\
    theZone.bounds.ul.z = theZone.bounds.ul.z + dz \
    theZone.bounds.ur.z = theZone.bounds.ur.z + dz\
    \
end\
\
function dmlZone:offsetZone(dx, dz)\
    cfxZones.offsetZone(self, dx, dz)\
end\
\
\
function cfxZones.moveZoneTo(theZone, x, z)\
    local dx = x - theZone.point.x\
    local dz = z - theZone.point.z \
    cfxZones.offsetZone(theZone, dx, dz)\
end;\
\
function dmlZone:moveZoneTo(x, z)\
    cfxZones.moveZoneTo(self, x, z)\
end\
\
function cfxZones.centerZoneOnUnit(theZone, theUnit) \
    local thePoint = theUnit:getPoint()\
    cfxZones.moveZoneTo(theZone, thePoint.x, thePoint.z)\
end\
\
function dmlZone:centerZoneOnUnit(theUnit) \
    local thePoint = theUnit:getPoint()\
    self:moveZoneTo(thePoint.x, thePoint.z)\
end\
\
\
function cfxZones.dumpZones(zoneTable)\
    if not zoneTable then zoneTable = cfxZones.zones end \
    \
    trigger.action.outText(\"Zones START\", 10)\
    for i, zone in pairs(zoneTable) do \
        local myType = \"unknown\"\
        if zone.isCircle then myType = \"Circle\" end\
        if zone.isPoly then myType = \"Poly\" end \
        \
        trigger.action.outText(\"#\".. i .. \": \" .. zone.name .. \" of type \" .. myType, 10)\
    end\
    trigger.action.outText(\"Zones end\", 10)\
end\
\
function cfxZones.keysForTable(theTable)\
    local keyset={}\
    local n=0\
\
    for k,v in pairs(tab) do\
        n=n+1\
        keyset[n]=k\
    end\
    return keyset\
end\
\
\
--\
-- return all zones that have a specific named property\
--\
function cfxZones.zonesWithProperty(propertyName, searchSet)\
    if not searchSet then searchSet = cfxZones.zones end \
    local theZones = {}\
    for k, aZone in pairs(searchSet) do \
        if not aZone then \
            trigger.action.outText(\"+++zone: nil aZone for \" .. k, 30)\
        else \
            local lU = cfxZones.getZoneProperty(aZone, propertyName)\
            if lU then \
                table.insert(theZones, aZone)\
            end\
        end\
    end    \
    return theZones\
end\
\
--\
-- return all zones from the zone table that begin with string prefix\
--\
function cfxZones.zonesStartingWithName(prefix, searchSet)\
    if not searchSet then searchSet = cfxZones.zones end \
    local prefixZones = {}\
    prefix = prefix:upper() -- all zones have UPPERCASE NAMES! THEY SCREAM AT YOU\
    for name, zone in pairs(searchSet) do\
        if dcsCommon.stringStartsWith(name:upper(), prefix) then\
            prefixZones[name] = zone -- note: ref copy!\
        end\
    end\
    \
    return prefixZones\
end\
\
--\
-- return all zones from the zone table that begin with the string or set of strings passed in prefix \
-- if you pass 'true' as second (optional) parameter, it will first look for all zones that begin\
-- with '+' and return only those. Use during debugging to force finding a specific zone\
--\
function cfxZones.zonesStartingWith(prefix, searchSet, debugging)\
    -- you can force zones by having their name start with \"+\"\
    -- which will force them to return immediately if debugging is true for this call\
\
    if (debugging) then \
        local debugZones = cfxZones.zonesStartingWithName(\"+\", searchSet)\
        if not (next(debugZones) == nil) then -- # operator only works on array elements \
            --trigger.action.outText(\"returning zones with prefix <\" .. prefix .. \">\", 10)\
            return debugZones \
        end \
    end\
    \
    if (type(prefix) == \"string\") then \
        return cfxZones.zonesStartingWithName(prefix, searchSet)\
    end\
    \
    local allZones = {}\
    for i=1, #prefix do \
        -- iterate through all names in prefix set\
        local theName = prefix[i]\
        local newZones = cfxZones.zonesStartingWithName(theName, searchSet)\
        -- add them all to current table\
        for zName, zInfo in pairs(newZones) do \
            allZones[zName] = zInfo -- will also replace doublets\
        end\
    end\
    \
    return allZones\
end\
\
function cfxZones.getZoneByName(aName, searchSet) \
    if not searchSet then searchSet = cfxZones.zones end \
    aName = aName:upper()\
    return searchSet[aName] -- the joys of key value pairs\
end\
\
function cfxZones.getZonesContainingString(aString, searchSet) \
    if not searchSet then searchSet = cfxZones.zones end\
    aString = string.upper(aString)\
    resultSet = {}\
    for zName, zData in pairs(searchSet) do \
        if aString == string.upper(zData.name) then \
            resultSet[zName] = zData\
        end\
    end\
    \
end;\
\
-- filter zones by range to a point. returns indexed set\
function cfxZones.getZonesInRange(point, range, theZones)\
    if not theZones then theZones = cfxZones.zones end\
    \
    local inRangeSet = {}\
    for zName, zData in pairs (theZones) do \
        if dcsCommon.dist(point, zData.point) < range then \
            table.insert(inRangeSet, zData)\
        end\
    end\
    return inRangeSet \
end\
\
-- get closest zone returns the zone that is closest to point \
function cfxZones.getClosestZone(point, theZones)\
    if not theZones then theZones = cfxZones.zones end\
    local lPoint = {x=point.x, y=0, z=point.z}\
    local currDelta = math.huge \
    local closestZone = nil\
    for zName, zData in pairs(theZones) do \
        local zPoint = cfxZones.getPoint(zData)\
        local delta = dcsCommon.dist(lPoint, zPoint) -- emulate flag compare \
        if (delta < currDelta) then \
            currDelta = delta\
            closestZone = zData\
        end\
    end\
    return closestZone, currDelta \
end\
\
function dmlZone:getClosestZone(theZones)\
    local closestZone, currDelta = cfxZones.getClosestZone(self:getPoint(), theZones)\
    return closestZone, currDelta \
end\
\
-- return a random zone from the table passed in zones\
function cfxZones.pickRandomZoneFrom(zones)\
    if not zones then zones = cfxZones.zones end\
    local indexedZones = dcsCommon.enumerateTable(zones)\
    local r = math.random(#indexedZones)\
    return indexedZones[r]\
end\
\
-- return an zone element by index \
function cfxZones.getZoneByIndex(theZones, theIndex) \
    local enumeratedZones = dcsCommon.enumerateTable(theZones)\
    if (theIndex > #enumeratedZones) then\
        trigger.action.outText(\"WARNING: zone index \" .. theIndex .. \" out of bounds - max = \" .. #enumeratedZones, 30)\
        return nil end\
    if (theIndex < 1) then return nil end\
    \
    return enumeratedZones[theIndex]\
end\
\
-- place a smoke marker in center of zone, offset by dx, dy \
function cfxZones.markZoneWithSmoke(theZone, dx, dz, smokeColor, alt)\
    if not alt then alt = 5 end \
    local point = cfxZones.getPoint(theZone) --{} -- theZone.point\
    point.x = point.x + dx -- getpoint updates and returns copy \
    point.z = point.z + dz \
    -- get height at point \
    point.y = land.getHeight({x = point.x, y = point.z}) + alt\
    -- height-correct\
    --local newPoint= {x = point.x, y = land.getHeight({x = point.x, y = point.z}) + 3, z= point.z}\
    trigger.action.smoke(point, smokeColor)\
end\
\
function dmlZone:markZoneWithSmoke(dx, dz, smokeColor, alt)\
    cfxZones.markZoneWithSmoke(self, dx, dz, smokeColor, alt)\
end\
\
-- place a smoke marker in center of zone, offset by radius and degrees \
function cfxZones.markZoneWithSmokePolar(theZone, radius, degrees, smokeColor, alt)\
    local rads = degrees * math.pi / 180\
    local dx = radius * math.sin(rads)\
    local dz = radius * math.cos(rads)\
    cfxZones.markZoneWithSmoke(theZone, dx, dz, smokeColor, alt)\
end\
\
function dmlZone:markZoneWithSmokePolar(radius, degrees, smokeColor, alt)\
    cfxZones.markZoneWithSmokePolar(self, radius, degrees, smokeColor, alt)\
end\
\
-- place a smoke marker in center of zone, offset by radius and randomized degrees \
function cfxZones.markZoneWithSmokePolarRandom(theZone, radius, smokeColor)\
    local degrees = math.random(360)\
    cfxZones.markZoneWithSmokePolar(theZone, radius, degrees, smokeColor)\
end\
\
function dmlZone:markZoneWithSmokePolarRandom(radius, smokeColor)\
    local degrees = math.random(360)\
    self:markZoneWithSmokePolar(radius, degrees, smokeColor)\
end\
\
function cfxZones.pointInOneOfZones(thePoint, zoneArray, useOrig) \
    if not zoneArray then zoneArray = cfxZones.zones end \
    for idx, theZone in pairs(zoneArray) do \
        local isIn, percent, dist = cfxZones.pointInZone(thePoint, theZone, useOrig)\
        if isIn then return isIn, percent, dist, theZone end \
    end\
    return false, 0, 0, nil \
end\
\
\
-- unitInZone returns true if theUnit is inside the zone \
-- the second value returned is the percentage of distance\
-- from center to rim, with 100% being entirely in center, 0 = outside\
-- the third value returned is the distance to center\
function cfxZones.pointInZone(thePoint, theZone, useOrig)\
\
    if not (theZone) then return false, 0, 0 end\
        \
    local pflat = {x = thePoint.x, y = 0, z = thePoint.z}\
    \
    local zpoint \
    if useOrig then\
        zpoint = cfxZones.getDCSOrigin(theZone)\
    else \
        zpoint = cfxZones.getPoint(theZone) -- updates zone if linked \
    end\
    local ppoint = thePoint -- xyz\
    local pflat = {x = ppoint.x, y = 0, z = ppoint.z}\
    local dist = dcsCommon.dist(zpoint, pflat)\
    \
    if theZone.isCircle then \
        if theZone.radius <= 0 then \
            return false, 0, 0\
        end\
\
        local success = dist < theZone.radius\
        local percentage = 0\
        if (success) then \
            percentage = 1 - dist / theZone.radius \
        end\
        return success, percentage, dist \
    \
    elseif theZone.isPoly then\
        local success = cfxZones.isPointInsidePoly(pflat, theZone.poly)\
        return success, 0, dist\
    else \
        trigger.action.outText(\"pointInZone: Unknown zone type for \" .. theZone.name, 10)\
    end\
\
    return false\
end\
\
function dmlZone:pointInZone(thePoint, useOrig)\
    return cfxZones.pointInZone(thePoint, self, useOrig)\
end\
\
\
function cfxZones.unitInZone(theUnit, theZone)\
    if not (theUnit) then return false, 0, 0 end\
    if not (theUnit:isExist()) then return false, 0, 0 end\
    -- force zone update if it is linked to another zone \
    -- pointInZone does update\
    local thePoint = theUnit:getPoint()\
    return cfxZones.pointInZone(thePoint, theZone)\
end\
\
function dmlZone:unitInZone(theUnit)\
    if not (theUnit) then return false, 0, 0 end\
    if not (theUnit:isExist()) then return false, 0, 0 end\
    -- force zone update if it is linked to another zone \
    -- pointInZone does update\
    local thePoint = theUnit:getPoint()\
    return self:pointInZone(thePoint)\
end\
\
-- returns all units of the input set that are inside the zone \
function cfxZones.unitsInZone(theUnits, theZone)\
    if not theUnits then return {} end\
    if not theZone then return {} end\
    \
    local zoneUnits = {}\
    for index, aUnit in pairs(theUnits) do \
        if cfxZones.unitInZone(aUnit, theZone) then \
            table.insert( zoneUnits, aUnit)\
        end\
    end\
    return zoneUnits\
end\
\
function dmlZone:unitsInZone(theUnits)\
    if not theUnits then return {} end\
    local zoneUnits = {}\
    for index, aUnit in pairs(theUnits) do \
        if self:unitInZone(aUnit) then \
            table.insert(zoneUnits, aUnit)\
        end\
    end\
    return zoneUnits\
end\
\
function cfxZones.closestUnitToZoneCenter(theUnits, theZone)\
    -- does not care if they really are in zone. call unitsInZone first\
    -- if you need to have them filtered\
    -- theUnits MUST BE ARRAY\
    if not theUnits then return nil end\
    if #theUnits == 0 then return nil end\
    local closestUnit = theUnits[1]\
    local zP = cfxZones.getPoint(theZone)\
    local smallestDist = math.huge\
    for i=2, #theUnits do\
        local aUnit = theUnits[i]\
        local currDist = dcsCommon.dist(zP, aUnit:getPoint())\
        if smallestDist > currDelta then \
            closestUnit = aUnit\
            smallestDist = currDist\
        end\
    end\
    return closestUnit\
end\
\
function dmlZone:closestUnitToZoneCenter(theUnits)\
    return cfxZones.closestUnitToZoneCenter(theUnits, self)\
end\
\
-- grow zone\
function cfxZones.growZone()\
    -- circular zones simply increase radius\
    -- poly zones: not defined \
    \
end\
\
\
-- creating units in a zone\
function cfxZones.createGroundUnitsInZoneForCoalition (theCoalition, groupName, theZone, theUnits, formation, heading, liveries) \
    -- theUnits can be string or table of string \
    if not groupName then groupName = \"G_\"..theZone.name end \
    -- group name will be taken from zone name and prependend with \"G_\"\
    local theGroup = dcsCommon.createGroundGroupWithUnits(groupName, theUnits, theZone.radius, nil, formation, nil, liveries)\
    \
    -- turn the entire formation to heading\
    if (not heading) then heading = 0 end\
    dcsCommon.rotateGroupData(theGroup, heading) -- currently, group is still at origin, no cx, cy\
    \
    \
    -- now move the group to center of theZone\
    dcsCommon.moveGroupDataTo(theGroup, \
                          theZone.point.x, \
                          theZone.point.z) -- watchit: Z!!!\
\
    -- create the group in the world and return it\
    -- first we need to translate the coalition to a legal \
    -- country. we use UN for neutral, cjtf for red and blue \
    local theSideCJTF = dcsCommon.coalition2county(theCoalition)\
    -- store cty and cat for later access. DCS doesn't need it, but we may \
    \
    theGroup.cty = theSideCJTF\
    theGroup.cat = Group.Category.GROUND\
    \
    -- create a copy of the group data for \
    -- later reference \
    local groupDataCopy = dcsCommon.clone(theGroup)\
\
    local newGroup = coalition.addGroup(theSideCJTF, Group.Category.GROUND, theGroup)\
    return newGroup, groupDataCopy\
end\
\
--\
-- ===============\
-- FLAG PROCESSING \
-- ===============\
--\
\
--\
-- Flag Pulling \
--\
function cfxZones.pulseFlag(theFlag, method, theZone)\
    local args = {}\
    args.theFlag = theFlag\
    args.method = method\
    args.theZone = theZone \
    local delay = 3\
    if dcsCommon.containsString(method, \",\") then \
        local parts = dcsCommon.splitString(method, \",\")\
        delay = parts[2]\
        if delay then delay = tonumber(delay) end  \
    end\
    if not delay then delay = 3 end \
    if theZone.verbose then \
        trigger.action.outText(\"+++zne: RAISING pulse t=\"..delay..\" for flag <\" .. theFlag .. \"> in zone <\" .. theZone.name ..\">\", 30)\
    end \
    local newVal = 1\
    cfxZones.setFlagValue(theFlag, newVal, theZone)\
    \
    -- schedule second half of pulse \
    timer.scheduleFunction(cfxZones.unPulseFlag, args, timer.getTime() + delay)\
end\
\
function dmlZone:pulseFlag(theFlag, method)\
    cfxZones.pulseFlag(theFlag, method, self)\
end\
\
function cfxZones.unPulseFlag(args)\
    local theZone = args.theZone\
    local method = args.method \
    local theFlag = args.theFlag \
    local newVal = 0\
    -- we may later use method to determine pulse direction / newVal\
    -- for now, we always go low \
    if theZone.verbose then \
        trigger.action.outText(\"+++zne: DOWNPULSE pulse for flag <\" .. theFlag .. \"> in zone <\" .. theZone.name ..\">\", 30)\
    end\
    cfxZones.setFlagValue(theFlag, newVal, theZone)\
end\
\
function cfxZones.evalRemainder(remainder, theZone)\
    local rNum = tonumber(remainder)\
    if not rNum then \
        -- we use remainder as name for flag \
        -- PROCESS ESCAPE SEQUENCES\
        local esc = string.sub(remainder, 1, 1)\
        local last = string.sub(remainder, -1)\
        if esc == \"@\" then \
            remainder = string.sub(remainder, 2)\
            remainder = dcsCommon.trim(remainder)\
        end\
        \
        if esc == \"(\" and last == \")\" and string.len(remainder) > 2 then \
            -- note: iisues with startswith(\"(\") ???\
            remainder = string.sub(remainder, 2, -2)\
            remainder = dcsCommon.trim(remainder)        \
        end\
        if esc == \"\\\"\" and last == \"\\\"\" and string.len(remainder) > 2 then \
            remainder = string.sub(remainder, 2, -2)\
            remainder = dcsCommon.trim(remainder)        \
        end\
        if cfxZones.verbose then \
            trigger.action.outText(\"+++zne: accessing flag <\" .. remainder .. \">\", 30)\
        end \
        rNum = cfxZones.getFlagValue(remainder, theZone)\
    end \
    return rNum\
end\
\
function dmlZone:evalRemainder(remainder)\
    return cfxZones.evalRemainder(remainder, self)\
end\
\
function cfxZones.doPollFlag(theFlag, method, theZone) -- no OOP equivalent\
    -- WARNING: \
    -- if method is a number string, it will be interpreted as follows:\
    -- positive number: set immediate \
    -- negative: decrement by amouint\
    if not theZone then \
        trigger.action.outText(\"+++zones: nil theZone on pollFlag\", 30)\
    end\
\
    local mt = type(method)\
    if mt == \"number\" then \
        method = \"#\" .. method -- convert to immediate \
        mt = \"string\"\
    elseif mt ~= \"string\" then \
        trigger.action.outText(\"+++zne: warning: zone <\" .. theZone.name .. \"> method type <\" .. mt .. \"> received. Ignoring\", 30)\
        return \
    end\
\
    local val = nil\
    method = method:lower()\
    method = dcsCommon.trim(method)\
    val = tonumber(method) -- see if val can be directly converted \
    if dcsCommon.stringStartsWith(method, \"+\") or \
       dcsCommon.stringStartsWith(method, \"-\") \
    then \
        -- skip this processing, a legal method can start with \"+\" or \"-\"\
        -- and we interpret it as a method to increase or decrease by amount\
    elseif (val ~= nil) then \
        -- provision to handle direct (positive) numbers (legacy support)\
        -- method can be converted to number but does not start with - or +\
        -- since all negative numbers start with '-' above guard will skip, positive will end up here\
        cfxZones.setFlagValue(theFlag, val, theZone)\
        if cfxZones.verbose or theZone.verbose then\
            trigger.action.outText(\"+++zones: flag <\" .. theFlag .. \"> changed to #\" .. val, 30)\
        end \
        return\
    else \
    end\
\
    if dcsCommon.stringStartsWith(method, \"#\") then \
        -- immediate value command. remove # and eval remainder \
        local remainder = dcsCommon.removePrefix(method, \"#\")\
        val = cfxZones.evalRemainder(remainder) -- always returens a number\
        cfxZones.setFlagValue(theFlag, val, theZone)\
        if theZone.verbose then \
            trigger.action.outText(\"+++zones: poll setting immediate <\" .. theFlag .. \"> in <\" .. theZone.name .. \"> to <\" .. val .. \">\", 30)\
        end\
        return \
    end\
    \
    local currVal = cfxZones.getFlagValue(theFlag, theZone)\
    if method == \"inc\" or method == \"f+1\" then \
        --trigger.action.setUserFlag(theFlag, currVal + 1)\
        cfxZones.setFlagValue(theFlag, currVal+1, theZone)\
        \
    elseif method == \"dec\" or method == \"f-1\" then \
        -- trigger.action.setUserFlag(theFlag, currVal - 1)\
        cfxZones.setFlagValue(theFlag, currVal-1, theZone)\
\
    elseif method == \"off\" or method == \"f=0\" then \
        -- trigger.action.setUserFlag(theFlag, 0)\
        cfxZones.setFlagValue(theFlag, 0, theZone)\
\
    elseif method == \"flip\" or method == \"xor\" then \
        if currVal ~= 0 then \
--            trigger.action.setUserFlag(theFlag, 0)\
            cfxZones.setFlagValue(theFlag, 0, theZone)\
\
        else \
            --trigger.action.setUserFlag(theFlag, 1)\
            cfxZones.setFlagValue(theFlag, 1, theZone)\
        end\
        \
    elseif dcsCommon.stringStartsWith(method, \"pulse\") then \
        cfxZones.pulseFlag(theFlag, method, theZone)\
        \
    elseif dcsCommon.stringStartsWith(method, \"+\") then \
        -- we add whatever is to the right \
        local remainder = dcsCommon.removePrefix(method, \"+\")\
        local adder = cfxZones.evalRemainder(remainder)\
        cfxZones.setFlagValue(theFlag, currVal+adder, theZone)\
        if theZone.verbose then \
            trigger.action.outText(\"+++zones: (poll) updating with '+' flag <\" .. theFlag .. \"> in <\" .. theZone.name .. \"> by <\" .. adder .. \"> to <\" .. adder + currVal .. \">\", 30)\
        end\
        \
    elseif dcsCommon.stringStartsWith(method, \"-\") then \
        -- we subtract whatever is to the right \
        local remainder = dcsCommon.removePrefix(method, \"-\")\
        local adder = cfxZones.evalRemainder(remainder)\
        cfxZones.setFlagValue(theFlag, currVal-adder, theZone)\
\
    else \
        if method ~= \"on\" and method ~= \"f=1\" then \
            trigger.action.outText(\"+++zones: unknown method <\" .. method .. \"> - using 'on'\", 30)\
        end\
        -- default: on.\
--        trigger.action.setUserFlag(theFlag, 1)\
        cfxZones.setFlagValue(theFlag, 1, theZone)\
    end\
    \
    if cfxZones.verbose then\
        local newVal = cfxZones.getFlagValue(theFlag, theZone)\
        trigger.action.outText(\"+++zones: flag <\" .. theFlag .. \"> changed from \" .. currVal .. \" to \" .. newVal, 30)\
    end \
end\
\
function cfxZones.pollFlag(theFlag, method, theZone) \
    --trigger.action.outText(\"enter pollflag for flag <\" .. theFlag .. \"> of zone <\" .. theZone.name .. \">\", 30)\
    local allFlags = {}\
    if dcsCommon.containsString(theFlag, \",\") then \
        if cfxZones.verbose then \
            trigger.action.outText(\"+++zones: will poll flag set <\" .. theFlag .. \"> with \" .. method, 30)\
        end\
        allFlags = dcsCommon.splitString(theFlag, \",\")\
    else \
        table.insert(allFlags, theFlag)\
    end\
    \
    for idx, aFlag in pairs(allFlags) do \
        aFlag = dcsCommon.trim(aFlag)\
        -- note: mey require range preprocessing, but that's not\
        -- a priority \
        cfxZones.doPollFlag(aFlag, method, theZone)\
    end \
end\
\
function dmlZone:pollFlag(theFlag, method)\
    cfxZones.pollFlag(theFlag, method, self)\
end\
\
function cfxZones.expandFlagName(theFlag, theZone) \
    if not theFlag then return \"!NIL\" end \
    local zoneName = \"<dummy>\"\
    if theZone then \
        zoneName = theZone.name -- for flag wildcards\
    end\
    \
    if type(theFlag) == \"number\" then \
        -- straight number, return \
        return theFlag\
    end\
    \
    -- we assume it's a string now\
    theFlag = dcsCommon.trim(theFlag) -- clear leading/trailing spaces\
    local nFlag = tonumber(theFlag) \
    if nFlag then -- a number, legal\
        return theFlag\
    end\
        \
    -- now do wildcard processing. we have alphanumeric\
    if dcsCommon.stringStartsWith(theFlag, \"*\") then  \
        theFlag = zoneName .. theFlag\
    end\
    return theFlag\
end\
\
function dmlZone:setFlagValue(theFlag, theValue)\
    cfxZones.setFlagValueMult(theFlag, theValue, self)\
end\
\
function cfxZones.setFlagValue(theFlag, theValue, theZone)\
    cfxZones.setFlagValueMult(theFlag, theValue, theZone)\
end\
\
function cfxZones.setFlagValueMult(theFlag, theValue, theZone)\
    local allFlags = {}\
    if dcsCommon.containsString(theFlag, \",\") then \
        if cfxZones.verbose then \
            trigger.action.outText(\"+++zones: will multi-set flags <\" .. theFlag .. \"> to \" .. theValue, 30)\
        end\
        allFlags = dcsCommon.splitString(theFlag, \",\")\
    else \
        table.insert(allFlags, theFlag)\
    end\
    \
    for idx, aFlag in pairs(allFlags) do \
        aFlag = dcsCommon.trim(aFlag)\
        -- note: mey require range preprocessing, but that's not\
        -- a priority \
        cfxZones.doSetFlagValue(aFlag, theValue, theZone)\
    end \
end\
\
function cfxZones.doSetFlagValue(theFlag, theValue, theZone)\
    local zoneName = \"<dummy>\"\
    if not theZone then \
        trigger.action.outText(\"+++Zne: no zone on setFlagValue\", 30) -- mod me for detector\
    else \
        zoneName = theZone.name -- for flag wildcards\
    end\
    \
    if type(theFlag) == \"number\" then \
        -- straight set, oldschool ME flag \
        trigger.action.setUserFlag(theFlag, theValue)\
        return \
    end\
    \
    -- we assume it's a string now\
    theFlag = dcsCommon.trim(theFlag) -- clear leading/trailing spaces    \
    -- some QoL: detect \"<none>\"\
    if dcsCommon.containsString(theFlag, \"<none>\") then \
        trigger.action.outText(\"+++Zone: warning - setFlag has '<none>' flag name in zone <\" .. zoneName .. \">\", 30) -- if error, intended break\
    end\
    \
    -- now do wildcard processing. we have alphanumeric\
    if dcsCommon.stringStartsWith(theFlag, \"*\") then  \
        theFlag = zoneName .. theFlag\
    end\
    trigger.action.setUserFlag(theFlag, theValue)\
end \
\
\
\
function cfxZones.getFlagValue(theFlag, theZone)\
    local zoneName = \"<dummy>\"\
    if not theZone or not theZone.name then \
        trigger.action.outText(\"+++Zne: no zone or zone name on getFlagValue\", 30)\
    else \
        zoneName = theZone.name -- for flag wildcards\
    end\
    \
    if type(theFlag) == \"number\" then \
        -- straight get, ME flag \
        return tonumber(trigger.misc.getUserFlag(theFlag))\
    end\
    \
    -- we assume it's a string now\
    theFlag = dcsCommon.trim(theFlag) -- clear leading/trailing spaces\
    local nFlag = tonumber(theFlag) \
    if nFlag then \
        return tonumber(trigger.misc.getUserFlag(theFlag))\
    end\
    \
    -- some QoL: detect \"<none>\"\
    if dcsCommon.containsString(theFlag, \"<none>\") then \
        trigger.action.outText(\"+++Zone: warning - getFlag has '<none>' flag name in zone <\" .. zoneName .. \">\", 30) -- break here\
    end\
    \
    -- now do wildcard processing. we have alphanumeric\
    if dcsCommon.stringStartsWith(theFlag, \"*\") then  \
            theFlag = zoneName .. theFlag\
    end\
    return tonumber(trigger.misc.getUserFlag(theFlag))\
end\
\
function dmlZone:getFlagValue(theFlag)\
    return cfxZones.getFlagValue(theFlag, self)\
end\
\
function cfxZones.verifyMethod(theMethod, theZone)\
    local lMethod = string.lower(theMethod)\
    if lMethod == \"#\" or lMethod == \"change\" then \
        return true\
    end\
\
    if lMethod == \"0\" or lMethod == \"no\" or lMethod == \"false\" \
       or lMethod == \"off\" then \
        return true  \
    end\
    \
    if lMethod == \"1\" or lMethod == \"yes\" or lMethod == \"true\" \
       or lMethod == \"on\" then \
        return true  \
    end\
    \
    if lMethod == \"inc\" or lMethod == \"+1\" then \
        return true\
    end\
    \
    if lMethod == \"dec\" or lMethod == \"-1\" then \
        return true \
    end \
    \
    if lMethod == \"lohi\" or lMethod == \"pulse\" then \
        return true\
    end\
    \
    if lMethod == \"hilo\" then \
        return true\
    end\
    \
    -- number constraints\
    -- or flag constraints     -- ONLY RETURN TRUE IF CHANGE AND CONSTRAINT MET \
    local op = string.sub(theMethod, 1, 1) \
    local remainder = string.sub(theMethod, 2)\
    remainder = dcsCommon.trim(remainder) -- remove all leading and trailing spaces\
\
    if true then \
        -- we have a comparison = \">\", \"=\", \"<\" followed by a number \
        -- THEY TRIGGER EACH TIME lastVal <> currVal AND condition IS MET  \
        if op == \"=\" then \
            return true\
        end\
        \
        if op == \"#\" or op == \"~\" then \
            return true\
        end \
        \
        if op == \"<\" then \
            return true\
        end\
        \
        if op == \">\" then \
            return true\
        end\
    end\
    \
    return false \
end\
\
function dmlZone:verifyMethod(theMethod)\
    return cfxZones.verifyMethod(theMethod, self)\
end\
\
-- method-based flag testing \
function cfxZones.evalFlagMethodImmediate(currVal, theMethod, theZone)\
    -- immediate eval - does not look at last val. \
    -- return true/false/value based on theMethod's contraints \
    -- simple constraints\
    local lMethod = string.lower(theMethod)\
    if lMethod == \"#\" or lMethod == \"change\" then \
        -- ALWAYS RETURNS TRUE for currval <> 0, flase if currval = 0\
        return currVal ~= 0  \
    end\
    \
    if lMethod == \"0\" or lMethod == \"no\" or lMethod == \"false\" \
       or lMethod == \"off\" then \
        -- WARNING: ALWAYS RETURNS FALSE\
        return false  \
    end\
    \
    if lMethod == \"1\" or lMethod == \"yes\" or lMethod == \"true\" \
       or lMethod == \"on\" then \
        -- WARNING: ALWAYS RETURNS TRUE\
        return true  \
    end\
    \
    if lMethod == \"inc\" or lMethod == \"+1\" then \
        return currVal+1 -- this may be unexpected\
    end\
    \
    if lMethod == \"dec\" or lMethod == \"-1\" then \
        return currVal-1 -- this may be unexpectd\
    end \
    \
    -- number constraints\
    -- or flag constraints \
    -- ONLY RETURN TRUE IF CHANGE AND CONSTRAINT MET \
    local op = string.sub(theMethod, 1, 1) \
    local remainder = string.sub(theMethod, 2)\
    remainder = dcsCommon.trim(remainder) -- remove all leading and trailing spaces\
    local rNum = tonumber(remainder)\
    if not rNum then \
        -- we use remainder as name for flag \
        -- PROCESS ESCAPE SEQUENCES\
        local esc = string.sub(remainder, 1, 1)\
        local last = string.sub(remainder, -1)\
        if esc == \"@\" then \
            remainder = string.sub(remainder, 2)\
            remainder = dcsCommon.trim(remainder)\
        end\
        \
        if esc == \"(\" and last == \")\" and string.len(remainder) > 2 then \
            -- note: iisues with startswith(\"(\") ???\
            remainder = string.sub(remainder, 2, -2)\
            remainder = dcsCommon.trim(remainder)        \
        end\
        if esc == \"\\\"\" and last == \"\\\"\" and string.len(remainder) > 2 then \
            remainder = string.sub(remainder, 2, -2)\
            remainder = dcsCommon.trim(remainder)        \
        end\
        if cfxZones.verbose then \
            trigger.action.outText(\"+++zne: accessing flag <\" .. remainder .. \">\", 30)\
        end \
        rNum = cfxZones.getFlagValue(remainder, theZone)\
    end \
    if rNum then \
        -- we have a comparison = \">\", \"=\", \"<\" followed by a number  \
        if op == \"=\" then \
            return currVal == rNum\
        end\
        \
        if op == \"#\" or op == \"~\" then \
            return currVal ~= rNum \
        end \
        \
        if op == \"<\" then \
            return currVal < rNum\
        end\
        \
        if op == \">\" then \
            return currVal > rNum\
        end\
    end\
    \
    -- if we get here, we have an error \
    local zoneName = \"<NIL>\"\
    if theZone then zoneName = theZone.name end \
    trigger.action.outText(\"+++Zne: illegal |\" .. theMethod .. \"| in eval for zone \" .. zoneName, 30 )\
    return false     \
end\
\
function dmlZone:evalFlagMethodImmediate(currVal, theMethod, theZone)\
    return cfxZones.evalFlagMethodImmediate(currVal, theMethod, self)\
end\
\
\
function cfxZones.testFlagByMethodForZone(currVal, lastVal, theMethod, theZone)\
    -- return true/false based on theMethod's contraints \
    -- simple constraints\
    -- ONLY RETURN TRUE IF CHANGE AND CONSTRAINT MET \
    local lMethod = string.lower(theMethod)\
    if lMethod == \"#\" or lMethod == \"change\" then \
        -- check if currVal different from lastVal\
        return currVal ~= lastVal  \
    end\
    \
    if lMethod == \"0\" or lMethod == \"no\" or lMethod == \"false\" \
       or lMethod == \"off\" then \
        -- WARNING: ONLY RETURNS TRUE IF FALSE AND lastval not zero!\
        return currVal == 0 and currVal ~= lastVal  \
    end\
    \
    if lMethod == \"1\" or lMethod == \"yes\" or lMethod == \"true\" \
       or lMethod == \"on\" then \
        -- WARNING: only returns true if lastval was false!!!!\
        return (currVal ~= 0 and lastVal == 0)  \
    end\
    \
    if lMethod == \"inc\" or lMethod == \"+1\" then \
--        return currVal == lastVal+1 -- better: test for greater than \
        return currVal > lastVal\
    end\
    \
    if lMethod == \"dec\" or lMethod == \"-1\" then \
        --return currVal == lastVal-1\
        return currVal < lastVal \
    end \
    \
    if lMethod == \"lohi\" or lMethod == \"pulse\" then \
        return (lastVal <= 0 and currVal > 0)\
    end\
    \
    if lMethod == \"hilo\" then \
        return (lastVal > 0 and currVal <= 0)\
    end\
    \
    -- number constraints\
    -- or flag constraints \
    -- ONLY RETURN TRUE IF CHANGE AND CONSTRAINT MET \
    local op = string.sub(theMethod, 1, 1) \
    local remainder = string.sub(theMethod, 2)\
    remainder = dcsCommon.trim(remainder) -- remove all leading and trailing spaces\
    local rNum = tonumber(remainder)\
    if not rNum then \
        -- we use remainder as name for flag \
        -- PROCESS ESCAPE SEQUENCES\
        local esc = string.sub(remainder, 1, 1)\
        local last = string.sub(remainder, -1)\
        if esc == \"@\" then \
            remainder = string.sub(remainder, 2)\
            remainder = dcsCommon.trim(remainder)\
        end\
        \
        if esc == \"(\" and last == \")\" and string.len(remainder) > 2 then \
            -- note: iisues with startswith(\"(\") ???\
            remainder = string.sub(remainder, 2, -2)\
            remainder = dcsCommon.trim(remainder)        \
        end\
        if esc == \"\\\"\" and last == \"\\\"\" and string.len(remainder) > 2 then \
            remainder = string.sub(remainder, 2, -2)\
            remainder = dcsCommon.trim(remainder)        \
        end\
        if cfxZones.verbose then \
            trigger.action.outText(\"+++zne: accessing flag <\" .. remainder .. \">\", 30)\
        end \
        rNum = cfxZones.getFlagValue(remainder, theZone)\
    end \
    if rNum then \
        -- we have a comparison = \">\", \"=\", \"<\" followed by a number \
        -- THEY TRIGGER EACH TIME lastVal <> currVal AND condition IS MET  \
        if op == \"=\" then \
            return currVal == rNum and lastVal ~= currVal\
        end\
        \
        if op == \"#\" or op == \"~\" then \
            return currVal ~= rNum and lastVal ~= currVal \
        end \
        \
        if op == \"<\" then \
            return currVal < rNum and lastVal ~= currVal\
        end\
        \
        if op == \">\" then \
            return currVal > rNum and lastVal ~= currVal\
        end\
    end\
    \
    -- if we get here, we have an error \
    local zoneName = \"<NIL>\"\
    if theZone then zoneName = theZone.name end \
    trigger.action.outText(\"+++Zne: illegal method constraints |\" .. theMethod .. \"| for zone \" .. zoneName, 30 )\
    return false \
end\
\
-- WARNING: testZoneFlag must also support non-dmlZone!!!\
function cfxZones.testZoneFlag(theZone, theFlagName, theMethod, latchName)\
    -- returns two values: true/false method result, and curr value\
    -- returns true if method constraints are met for flag theFlagName\
    -- as defined by theMethod \
    if not theMethod then \
        theMethod = \"change\"\
    end \
    \
    -- will read and update theZone[latchName] as appropriate \
    if not theZone then \
        trigger.action.outText(\"+++Zne: no zone for testZoneFlag\", 30)\
        return nil, nil \
    end \
    if not theFlagName then \
        -- this is common, no error, only on verbose \
        if cfxZones.verbose then \
            trigger.action.outText(\"+++Zne: no flagName for zone \" .. theZone.name .. \" for testZoneFlag\", 30)\
        end \
        return nil, nil\
    end\
    if not latchName then \
        trigger.action.outText(\"+++Zne: no latchName for zone \" .. theZone.name .. \" for testZoneFlag\", 30)\
        return nil, nil \
    end\
    -- get current value \
    local currVal = cfxZones.getFlagValue(theFlagName, theZone)\
    \
    -- get last value from latch\
    local lastVal = theZone[latchName]\
    if not lastVal then \
        trigger.action.outText(\"+++Zne: latch <\" .. latchName .. \"> not valid for zone \" .. theZone.name, 30) -- intentional break here \
        return nil, nil\
    end\
    \
    -- now, test by method \
    -- we should only test if currVal <> lastVal \
    if currVal == lastVal then\
        return false, currVal\
    end \
    \
    local testResult = cfxZones.testFlagByMethodForZone(currVal, lastVal, theMethod, theZone)\
\
    -- update latch by method\
    theZone[latchName] = currVal \
\
    -- return result\
    return testResult, currVal\
end\
\
function dmlZone:testZoneFlag(theFlagName, theMethod, latchName)\
    local r, v = cfxZones.testZoneFlag(self, theFlagName, theMethod, latchName)\
    return r, v \
end\
\
function cfxZones.numberArrayFromString(inString, default) -- bridge\
    return dcsCommon.numberArrayFromString(inString, default)\
end\
 \
\
function cfxZones.flagArrayFromString(inString) -- dcsCommon bridge \
    return dcsCommon.flagArrayFromString(inString)\
end\
\
\
--\
-- Drawing a Zone\
--\
\
function cfxZones.drawZone(theZone, lineColor, fillColor, markID)\
    if not theZone then return 0 end \
    if not lineColor then lineColor = {0.8, 0.8, 0.8, 1.0} end\
    if not fillColor then fillColor = {0.8, 0.8, 0.8, 0.0} end \
    if not markID then markID = dcsCommon.numberUUID() end \
    \
    if theZone.isCircle then \
        trigger.action.circleToAll(-1, markID, theZone.point, theZone.radius, lineColor, fillColor, 1, true, \"\")\
    else \
        local poly = theZone.poly\
        trigger.action.quadToAll(-1, markID, poly[4], poly[3], poly[2], poly[1], lineColor, fillColor, 1, true, \"\") -- note: left winding to get fill color\
    end\
    \
    return markID\
end\
\
function dmlZone:drawZone(lineColor, fillColor, markID)\
    return cfxZones.drawZone(self, lineColor, fillColor, markID)\
end\
\
function cfxZones.drawText(theZone, theText, fSize, lineColor, fillColor)\
    if not theZone then return end \
    if not fSize then fSize = 12 end \
    if not lineColor then lineColor = {0.8, 0.8, 0.8, 1.0} end\
    if not fillColor then fillColor = {0, 0, 0, 0} end \
    local markID = dcsCommon.numberUUID()\
    local p = theZone:getPoint()\
    local offset = {x = p.x, y = 0, z = p.z} \
    trigger.action.textToAll(-1, markID, offset, lineColor , fillColor , fSize, true , theText)\
    return markID\
end\
\
function dmlZone:drawText(theText, fSize, lineColor, fillColor)\
    return cfxZones.drawText(self, theText, fSize, lineColor, fillColor)\
end\
--\
-- ===================\
-- PROPERTY PROCESSING\
-- =================== \
--\
\
function cfxZones.getAllZoneProperties(theZone, caseInsensitive, numbersOnly) -- return as dict \
    if not caseInsensitive then caseInsensitive = false end \
    if not numbersOnly then numbersOnly = false end \
    if not theZone then return {} end \
    \
    local dcsProps = theZone.properties -- zone properties in dcs format \
    local props = {}\
    -- dcs has all properties as array with values .key and .value \
    -- so convert them into a dictionary \
    for i=1, #dcsProps do \
        local theProp = dcsProps[i]\
        local theKey = \"dummy\"\
        if string.len(theProp.key) > 0 then theKey = theProp.key end \
        if caseInsensitive then theKey = theKey:upper() end \
        local v = theProp.value \
        if numbersOnly then \
            v = tonumber(v)\
            if not v then v = 0 end \
        end\
        props[theKey] = v\
    end\
    return props \
end\
\
function dmlZone:getAllZoneProperties(caseInsensitive, numbersOnly)\
    return cfxZones.getAllZoneProperties(self, caseInsensitive, numbersOnly)\
end\
\
function cfxZones.extractPropertyFromDCS(theKey, theProperties)\
-- trim\
    theKey = dcsCommon.trim(theKey) \
--    make lower case conversion if not case sensitive\
    if not cfxZones.caseSensitiveProperties then \
        theKey = string.lower(theKey)\
    end\
\
-- iterate all keys and compare to what we are looking for     \
    for i=1, #theProperties do\
        local theP = theProperties[i]\
         \
        local existingKey = dcsCommon.trim(theP.key)  \
        if not cfxZones.caseSensitiveProperties then \
            existingKey = string.lower(existingKey)\
        end\
        if existingKey == theKey then \
            return theP.value\
        end\
        \
        -- now check after removing all blanks \
        existingKey = dcsCommon.removeBlanks(existingKey)\
        if existingKey == theKey then \
            return theP.value\
        end\
    end\
    return nil \
end\
\
function cfxZones.getZoneProperty(cZone, theKey)\
    if not cZone then \
        trigger.action.outText(\"+++zone: no zone in getZoneProperty\", 30)\
        return nil\
    end \
    if not theKey then \
        trigger.action.outText(\"+++zone: no property key in getZoneProperty for zone \" .. cZone.name, 30)\
        return \
    end    \
\
    local props = cZone.properties\
    local theVal = cfxZones.extractPropertyFromDCS(theKey, props)\
    return theVal\
end\
\
function dmlZone:getZoneProperty(theKey)\
    if not theKey then \
        trigger.action.outText(\"+++zone: no property key in OOP getZoneProperty for zone \" .. self.name, 30)\
        return nil  \
    end    \
    local props = self.properties\
    local theVal = cfxZones.extractPropertyFromDCS(theKey, props)\
    return theVal\
end\
\
\
function cfxZones.getStringFromZoneProperty(theZone, theProperty, default)\
    if not default then default = \"\" end\
-- OOP heavy duty test here\
    local p = theZone:getZoneProperty(theProperty)\
    if not p then return default end\
    if type(p) == \"string\" then \
        p = dcsCommon.trim(p)\
        if p == \"\" then p = default end \
        return p\
    end\
    return default -- warning. what if it was a number first?\
end\
\
function dmlZone:getStringFromZoneProperty(theProperty, default)\
    if not default then default = \"\" end\
    local p = self:getZoneProperty(theProperty)\
    if not p then return default end\
    if type(p) == \"string\" then \
        p = dcsCommon.trim(p)\
        if p == \"\" then p = default end \
        return p\
    end\
    return default -- warning. what if it was a number first?\
end\
\
function cfxZones.getMinMaxFromZoneProperty(theZone, theProperty)\
    local p = cfxZones.getZoneProperty(theZone, theProperty)\
    local theNumbers = dcsCommon.splitString(p, \" \")\
    return tonumber(theNumbers[1]), tonumber(theNumbers[2])\
end\
\
function dmlZone:getMinMaxFromZoneProperty(theProperty)\
    local p = self:getZoneProperty(theProperty)\
    local theNumbers = dcsCommon.splitString(p, \" \")\
    return tonumber(theNumbers[1]), tonumber(theNumbers[2])\
end\
\
function cfxZones.randomInRange(minVal, maxVal) -- should be moved to dcsCommon\
    if maxVal < minVal then \
        local t = minVal\
        minVal = maxVal \
        maxVal = t\
    end\
    return cfxZones.randomDelayFromPositiveRange(minVal, maxVal)\
end\
\
function cfxZones.randomDelayFromPositiveRange(minVal, maxVal) -- should be moved to dcsCommon \
    if not maxVal then return minVal end \
    if not minVal then return maxVal end \
    if minVal == maxVal then return minVal end \
    local delay = maxVal\
    if minVal >= 0 and minVal < delay then \
        -- we want a randomized from time from minTime .. delay\
        local varPart = delay - minVal + 1\
        varPart = dcsCommon.smallRandom(varPart) - 1\
        delay = minVal + varPart\
    end\
    return delay \
end\
\
function cfxZones.getPositiveRangeFromZoneProperty(theZone, theProperty, default, defaultmax)\
    -- reads property as string, and interprets as range 'a-b'. \
    -- if not a range but single number, returns both for upper and lower \
    --trigger.action.outText(\"***Zne: enter with <\" .. theZone.name .. \">: range for property <\" .. theProperty .. \">!\", 30)\
    if not default then default = 0 end \
    if not defaultmax then defaultmax = default end \
    \
    local lowerBound = default\
    local upperBound = defaultmax \
    \
    local rangeString = cfxZones.getStringFromZoneProperty(theZone, theProperty, \"\")\
    if dcsCommon.containsString(rangeString, \"-\") then \
        local theRange = dcsCommon.splitString(rangeString, \"-\")\
        lowerBound = theRange[1]\
        lowerBound = tonumber(lowerBound)\
        upperBound = theRange[2]\
        upperBound = tonumber(upperBound)\
        if lowerBound and upperBound then\
            -- swap if wrong order\
            if lowerBound > upperBound then \
                local temp = upperBound\
                upperBound = lowerBound\
                lowerBound = temp \
            end\
\
        else\
            -- bounds illegal\
            trigger.action.outText(\"+++Zne: illegal range  <\" .. rangeString .. \">, using \" .. default .. \"-\" .. defaultmax, 30)\
            lowerBound = default\
            upperBound = defaultmax \
        end\
    else \
        upperBound = cfxZones.getNumberFromZoneProperty(theZone, theProperty, defaultmax) -- between pulses \
        lowerBound = upperBound\
    end\
\
    return lowerBound, upperBound\
end\
\
function dmlZone:getPositiveRangeFromZoneProperty(theProperty, default, defaultmax)\
    local lo, up = cfxZones.getPositiveRangeFromZoneProperty(self, theProperty, default, defaultmax)\
    return lo, up \
end\
\
function cfxZones.getListFromZoneProperty(theZone, theProperty, defaultItem) -- comma delimited\
    if not defaultItem then defaultItem = \"default\" end \
    \
    local theString = theZone:getStringFromZoneProperty(theProperty, defaultItem)\
    if dcsCommon.containsString(theString, \",\") then \
        local theArray = dcsCommon.splitString(theString, ',')\
        theArray = dcsCommon.trimArray(theArray)\
        return theArray\
    else \
        return {theString}\
    end\
    \
    return nil \
end\
\
function dmlZone:getListFromZoneProperty(theProperty, defaultItem)\
    return cfxZones.getListFromZoneProperty(self, theProperty, defaultItem)\
end\
\
function cfxZones.hasProperty(theZone, theProperty) \
    if not theProperty then \
        trigger.action.outText(\"+++zne: WARNING - hasProperty called with nil theProperty for zone <\" .. theZone.name .. \">\", 30)\
        return false \
    end \
    local foundIt = cfxZones.getZoneProperty(theZone, theProperty)\
    if not foundIt then \
        -- check for possible forgotten or exchanged IO flags \
        if string.sub(theProperty, -1) == \"?\" then\
            local lessOp = theProperty:sub(1,-2)\
            if cfxZones.getZoneProperty(theZone, lessOp) ~= nil then \
                trigger.action.outText(\"*** NOTE: \" .. theZone.name .. \"'s property <\" .. lessOp .. \"> may be missing a Query ('?') symbol\", 30)\
            end\
            local lessPlus = lessOp .. \"!\"\
            if cfxZones.getZoneProperty(theZone, lessPlus) ~= nil then \
                trigger.action.outText(\"*** NOTE: \" .. theZone.name .. \"'s property <\" .. lessOp .. \"> may be using '!' instead of '?' for input\", 30)\
            end\
            return false \
        end\
        \
        if string.sub(theProperty, -1) == \"!\" then \
            local lessOp = theProperty:sub(1,-2)\
            if cfxZones.getZoneProperty(theZone, lessOp) ~= nil then \
                trigger.action.outText(\"*** NOTE: \" .. theZone.name .. \"'s property <\" .. lessOp .. \"> may be missing a Bang! ('!') symbol\", 30)\
            end\
            local lessPlus = lessOp .. \"?\"\
            if cfxZones.getZoneProperty(theZone, lessPlus) ~= nil then \
                trigger.action.outText(\"*** NOTE: \" .. theZone.name .. \"'s property <\" .. lessOp .. \"> may be using '!' instead of '?' for input\", 30)\
            end\
            return false \
        end\
        \
        if string.sub(theProperty, -1) == \":\" then \
            local lessOp = theProperty:sub(1,-2)\
            if cfxZones.getZoneProperty(theZone, lessOp) ~= nil then \
                trigger.action.outText(\"*** NOTE: \" .. theZone.name .. \"'s property <\" .. lessOp .. \"> may be missing a colon (':') at end\", 30)\
            end\
            return false \
        end\
        \
        if string.sub(theProperty, -1) == \"#\" then \
            local lessOp = theProperty:sub(1,-2)\
            if cfxZones.getZoneProperty(theZone, lessOp) ~= nil then \
                trigger.action.outText(\"*** NOTE: \" .. theZone.name .. \"'s property <\" .. lessOp .. \"> may be missing a hash mark ('#') at end\", 30)\
            end\
            return false \
        end\
        \
        return false \
    end\
    return true \
end\
\
function dmlZone:hasProperty(theProperty) \
    if not theProperty then \
        trigger.action.outText(\"+++zne: WARNING - hasProperty called with nil theProperty for zone <\" .. self.name .. \">\", 30)\
        return false \
    end \
    local foundIt = self:getZoneProperty(theProperty)\
    if not foundIt then \
        -- check for possible forgotten or exchanged IO flags \
        if string.sub(theProperty, -1) == \"?\" then\
            local lessOp = theProperty:sub(1,-2)\
            if self:getZoneProperty(lessOp) ~= nil then \
                trigger.action.outText(\"*** NOTE: \" .. self.name .. \"'s property <\" .. lessOp .. \"> may be missing a Query ('?') symbol\", 30)\
            end\
            local lessPlus = lessOp .. \"!\"\
            if self:getZoneProperty(lessPlus) ~= nil then \
                trigger.action.outText(\"*** NOTE: \" .. self.name .. \"'s property <\" .. lessOp .. \"> may be using '!' instead of '?' for input\", 30)\
            end\
            return false \
        end\
        \
        if string.sub(theProperty, -1) == \"!\" then \
            local lessOp = theProperty:sub(1,-2)\
            if self:getZoneProperty(lessOp) ~= nil then \
                trigger.action.outText(\"*** NOTE: \" .. self.name .. \"'s property <\" .. lessOp .. \"> may be missing a Bang! ('!') symbol\", 30)\
            end\
            local lessPlus = lessOp .. \"?\"\
            if self:getZoneProperty(lessPlus) ~= nil then \
                trigger.action.outText(\"*** NOTE: \" .. self.name .. \"'s property <\" .. lessOp .. \"> may be using '!' instead of '?' for input\", 30)\
            end\
            return false \
        end\
        \
        if string.sub(theProperty, -1) == \":\" then \
            local lessOp = theProperty:sub(1,-2)\
            if self:getZoneProperty(lessOp) ~= nil then \
                trigger.action.outText(\"*** NOTE: \" .. self.name .. \"'s property <\" .. lessOp .. \"> may be missing a colon (':') at end\", 30)\
            end\
            return false \
        end\
        \
        if string.sub(theProperty, -1) == \"#\" then \
            local lessOp = theProperty:sub(1,-2)\
            if self:getZoneProperty(lessOp) ~= nil then \
                trigger.action.outText(\"*** NOTE: \" .. self.name .. \"'s property <\" .. lessOp .. \"> may be missing a hash mark ('#') at end\", 30)\
            end\
            return false \
        end\
        \
        return false \
    end\
    return true \
end\
\
function cfxZones.getBoolFromZoneProperty(theZone, theProperty, defaultVal)\
    if not defaultVal then defaultVal = false end \
    if type(defaultVal) ~= \"boolean\" then \
        defaultVal = false \
    end\
\
    if not theZone then \
        trigger.action.outText(\"WARNING: NIL Zone in getBoolFromZoneProperty\", 30)\
        return defaultVal\
    end\
\
\
    local p = cfxZones.getZoneProperty(theZone, theProperty)\
    if not p then return defaultVal end\
\
    -- make sure we compare so default always works when \
    -- answer isn't exactly the opposite\
    p = p:lower() \
    p = dcsCommon.trim(p) \
    if defaultVal == false then \
        -- only go true if exact match to yes or true \
        theBool = false \
        theBool = (p == 'true') or (p == 'yes') or (p == \"1\") or (p == 'on')\
        return theBool\
    end\
    \
    -- special: return a random value if p == \"rnd\" or \"?\" or \"maybe\"\
    if (p == \"?\") or (p == \"rnd\") or (p == \"random\") or (p == \"maybe\") then \
        return (math.random(1000) < 500) -- 50:50\
    end     \
    \
    local theBool = true \
    -- only go false if exactly no or false or \"0\"\
    theBool = (p ~= 'false') and (p ~= 'no') and (p ~= \"0\") and (p~=\"off\")\
    return theBool\
end\
\
function dmlZone:getBoolFromZoneProperty(theProperty, defaultVal)\
    if not defaultVal then defaultVal = false end \
    if type(defaultVal) ~= \"boolean\" then \
        defaultVal = false \
    end\
\
    local p = self:getZoneProperty(theProperty)\
    if not p then return defaultVal end\
\
    -- make sure we compare so default always works when \
    -- answer isn't exactly the opposite\
    p = p:lower() \
    p = dcsCommon.trim(p) \
    if defaultVal == false then \
        -- only go true if exact match to yes or true \
        theBool = false \
        theBool = (p == 'true') or (p == 'yes') or (p == \"1\") or (p==\"on\")\
        return theBool\
    end\
    \
    -- special: return a random value if p == \"rnd\" or \"?\" or \"maybe\"\
    if (p == \"?\") or (p == \"rnd\") or (p == \"random\") or (p == \"maybe\") then \
        return (math.random(1000) < 500) -- 50:50\
    end     \
    \
    local theBool = true \
    -- only go false if exactly no or false or \"0\"\
    theBool = (p ~= 'false') and (p ~= 'no') and (p ~= \"0\") and (p ~= \"off\")\
    return theBool\
end\
\
function cfxZones.getCoalitionFromZoneProperty(theZone, theProperty, default)\
    if not default then default = 0 end\
    local p = cfxZones.getZoneProperty(theZone, theProperty)\
    if not p then return default end  \
    if type(p) == \"number\" then -- can't currently really happen\
        if p == 1 then return 1 end \
        if p == 2 then return 2 end \
        return 0\
    end\
    \
    if type(p) == \"string\" then \
        if p == \"1\" then return 1 end \
        if p == \"2\" then return 2 end \
        if p == \"0\" then return 0 end \
        \
        p = p:lower()\
        \
        if p == \"red\" then return 1 end \
        if p == \"blue\" then return 2 end \
        if p == \"neutral\" then return 0 end\
        if p == \"all\" then return 0 end \
        return default \
    end\
    \
    return default \
end\
\
function dmlZone:getCoalitionFromZoneProperty(theProperty, default)\
    if not default then default = 0 end\
    local p = self:getZoneProperty(theProperty)\
    if not p then return default end  \
    if type(p) == \"number\" then -- can't currently really happen\
        if p == 1 then return 1 end \
        if p == 2 then return 2 end \
        return 0\
    end\
    \
    if type(p) == \"string\" then \
        if p == \"1\" then return 1 end \
        if p == \"2\" then return 2 end \
        if p == \"0\" then return 0 end \
        \
        p = p:lower()\
        \
        if p == \"red\" then return 1 end \
        if p == \"blue\" then return 2 end \
        if p == \"neutral\" then return 0 end\
        if p == \"all\" then return 0 end \
        return default \
    end\
    \
    return default \
end\
\
function cfxZones.getNumberFromZoneProperty(theZone, theProperty, default)\
    if not default then default = 0 end\
    default = tonumber(default)\
    if not default then default = 0 end -- enforce default numbner as well \
    local p = cfxZones.getZoneProperty(theZone, theProperty)\
    p = tonumber(p)\
    if not p then p = default end \
    return p\
end\
\
function dmlZone:getNumberFromZoneProperty(theProperty, default) \
    if not default then default = 0 end\
    default = tonumber(default)\
    if not default then default = 0 end -- enforce default numbner as well \
    local p = self:getZoneProperty(theProperty)\
    p = tonumber(p)\
    if not p then p = default end \
    return p\
end\
\
function cfxZones.getVectorFromZoneProperty(theZone, theProperty, minDims, defaultVal)\
    if not minDims then minDims = 0 end \
    if not defaultVal then defaultVal = 0 end \
    local s = cfxZones.getStringFromZoneProperty(theZone, theProperty, \"\")\
    local sVec = dcsCommon.splitString(s, \",\")\
    local nVec = {}\
    for idx, numString in pairs (sVec) do \
        local n = tonumber(numString)\
        if not n then n = defaultVal end\
        table.insert(nVec, n)\
    end\
    -- make sure vector contains at least minDims values \
    while #nVec < minDims do \
        table.insert(nVec, defaultVal)\
    end\
    \
    return nVec \
end\
\
function dmlZone:getVectorFromZoneProperty(theProperty, minDims, defaultVal)\
    if not minDims then minDims = 0 end \
    if not defaultVal then defaultVal = 0 end \
    local s = self:getStringFromZoneProperty(theProperty, \"\")\
    local sVec = dcsCommon.splitString(s, \",\")\
    local nVec = {}\
    for idx, numString in pairs (sVec) do \
        local n = tonumber(numString)\
        if not n then n = defaultVal end\
        table.insert(nVec, n)\
    end\
    -- make sure vector contains at least minDims values \
    while #nVec < minDims do \
        table.insert(nVec, defaultVal)\
    end\
    \
    return nVec \
end\
\
function cfxZones.getRGBVectorFromZoneProperty(theZone, theProperty, defaultVal)\
    if not defaultVal then defaultVal = {1.0, 1.0, 1.0} end \
    if #defaultVal ~=3 then defaultVal = {1.0, 1.0, 1.0} end\
    local s = cfxZones.getStringFromZoneProperty(theZone, theProperty, \"\")\
    local sVec = dcsCommon.splitString(s, \",\")\
    local nVec = {}\
    for i = 1, 3 do \
        n = sVec[i]\
        if n then n = tonumber(n) end \
        if not n then n = defaultVal[i] end \
        if n > 1.0 then n = 1.0 end\
        if n < 0 then n = 0 end \
        nVec[i] = n\
    end\
    return nVec \
end\
\
function dmlZone:getRGBVectorFromZoneProperty(theProperty, defaultVal)\
    if not defaultVal then defaultVal = {1.0, 1.0, 1.0} end \
    if #defaultVal ~=3 then defaultVal = {1.0, 1.0, 1.0} end\
    local s = self:getStringFromZoneProperty(theProperty, \"\")\
    local sVec = dcsCommon.splitString(s, \",\")\
    local nVec = {}\
    for i = 1, 3 do \
        n = sVec[i]\
        if n then n = tonumber(n) end \
        if not n then n = defaultVal[i] end \
        if n > 1.0 then n = 1.0 end\
        if n < 0 then n = 0 end \
        nVec[i] = n\
    end\
    return nVec \
end\
\
\
function cfxZones.getRGBAVectorFromZoneProperty(theZone, theProperty, defaultVal)\
    if not defaultVal then defaultVal = {1.0, 1.0, 1.0, 1.0} end \
    if #defaultVal ~=4 then defaultVal = {1.0, 1.0, 1.0, 1.0} end\
    local s = cfxZones.getStringFromZoneProperty(theZone, theProperty, \"\")\
    s = dcsCommon.trim(s)\
    if s:sub(1,1) == \"#\" then \
        -- it's probably a \"#RRGGBBAA\" format hex string \
        local hVec = dcsCommon.hexString2RGBA(s)\
        if hVec then return hVec end \
    end\
\
    local sVec = dcsCommon.splitString(s, \",\")\
    local nVec = {}\
    for i = 1, 4 do \
        n = sVec[i]\
        if n then n = tonumber(n) end \
        if not n then n = defaultVal[i] end \
        if n > 1.0 then n = 1.0 end\
        if n < 0 then n = 0 end \
        nVec[i] = n\
    end\
        \
    return nVec \
end\
\
function dmlZone:getRGBAVectorFromZoneProperty(theProperty, defaultVal)\
    if not defaultVal then defaultVal = {1.0, 1.0, 1.0, 1.0} end \
    if #defaultVal ~=4 then defaultVal = {1.0, 1.0, 1.0, 1.0} end\
    local s = self:getStringFromZoneProperty(theProperty, \"\")\
    s = dcsCommon.trim(s)\
    if s:sub(1,1) == \"#\" then \
        -- it's probably a \"#RRGGBBAA\" format hex string \
        local hVec = dcsCommon.hexString2RGBA(s)\
        if hVec then return hVec end \
    end\
\
    local sVec = dcsCommon.splitString(s, \",\")\
    local nVec = {}\
    for i = 1, 4 do \
        n = sVec[i]\
        if n then n = tonumber(n) end \
        if not n then n = defaultVal[i] end \
        if n > 1.0 then n = 1.0 end\
        if n < 0 then n = 0 end \
        nVec[i] = n\
    end\
        \
    return nVec \
end\
\
function cfxZones.getRGBFromZoneProperty(theZone, theProperty, default)\
    --if not default then default = {1.0, 1.0, 1.0} end -- white \
    local rawRGB = cfxZones.getVectorFromZoneProperty(theZone, theProperty, 3, 1.0)\
    local retVal = {}\
    for i = 1, 3 do \
        local cp = rawRGB[i]\
        if cp > 1.0 then cp = 1.0 end\
        if cp < 0 then cp = 0 end \
        retVal[i] = cp\
    end\
    return retVal\
end\
\
function dmlZone:getRGBFromZoneProperty(theProperty, default)\
    --if not default then default = {1.0, 1.0, 1.0} end -- white \
    local rawRGB = self:getVectorFromZoneProperty(theProperty, 3, 1.0)\
    local retVal = {}\
    for i = 1, 3 do \
        local cp = rawRGB[i]\
        if cp > 1.0 then cp = 1.0 end\
        if cp < 0 then cp = 0 end \
        retVal[i] = cp\
    end\
    return retVal\
end\
\
\
function cfxZones.getSmokeColorStringFromZoneProperty(theZone, theProperty, default) -- smoke as 'red', 'green', or 1..5\
    if not default then default = \"red\" end \
    local s = cfxZones.getStringFromZoneProperty(theZone, theProperty, default)\
    s = s:lower()\
    s = dcsCommon.trim(s)\
    -- check numbers \
    if (s == \"0\") then return \"green\" end\
    if (s == \"1\") then return \"red\" end\
    if (s == \"2\") then return \"white\" end\
    if (s == \"3\") then return \"orange\" end\
    if (s == \"4\") then return \"blue\" end\
    \
    if s == \"green\" or\
       s == \"red\" or\
       s == \"white\" or\
       s == \"orange\" or\
       s == \"blue\" then return s end\
\
    return default \
end\
\
function dmlZone:getSmokeColorStringFromZoneProperty(theProperty, default) -- smoke as 'red', 'green', or 1..5\
    if not default then default = \"red\" end \
    local s = self:getStringFromZoneProperty(theProperty, default)\
    s = s:lower()\
    s = dcsCommon.trim(s)\
    -- check numbers \
    if (s == \"0\") then return \"green\" end\
    if (s == \"1\") then return \"red\" end\
    if (s == \"2\") then return \"white\" end\
    if (s == \"3\") then return \"orange\" end\
    if (s == \"4\") then return \"blue\" end\
    \
    if s == \"green\" or\
       s == \"red\" or\
       s == \"white\" or\
       s == \"orange\" or\
       s == \"blue\" then return s end\
\
    return default \
end\
\
function cfxZones.getFlareColorStringFromZoneProperty(theZone, theProperty, default) -- smoke as 'red', 'green', or 1..5\
    if not default then default = \"red\" end \
    local s = cfxZones.getStringFromZoneProperty(theZone, theProperty, default)\
    s = s:lower()\
    s = dcsCommon.trim(s)\
    -- check numbers \
    if (s == \"rnd\") then return \"random\" end \
    if (s == \"0\") then return \"green\" end\
    if (s == \"1\") then return \"red\" end\
    if (s == \"2\") then return \"white\" end\
    if (s == \"3\") then return \"yellow\" end\
    if (s == \"-1\") then return \"random\" end  \
    \
    if s == \"green\" or\
       s == \"red\" or\
       s == \"white\" or\
       s == \"yellow\" or \
       s == \"random\" then\
    return s end\
\
    return default \
end\
\
function dmlZone:getFlareColorStringFromZoneProperty(theProperty, default) -- smoke as 'red', 'green', or 1..5\
    if not default then default = \"red\" end \
    local s = self:getStringFromZoneProperty(theProperty, default)\
    s = s:lower()\
    s = dcsCommon.trim(s)\
    -- check numbers \
    if (s == \"rnd\") then return \"random\" end \
    if (s == \"0\") then return \"green\" end\
    if (s == \"1\") then return \"red\" end\
    if (s == \"2\") then return \"white\" end\
    if (s == \"3\") then return \"yellow\" end\
    if (s == \"-1\") then return \"random\" end  \
    \
    if s == \"green\" or\
       s == \"red\" or\
       s == \"white\" or\
       s == \"yellow\" or \
       s == \"random\" then\
    return s end\
\
    return default \
end\
\
--\
-- Zone-based wildcard processing\
-- \
\
-- process <z>\
function cfxZones.processZoneStatics(inMsg, theZone)\
    if theZone then \
        inMsg = inMsg:gsub(\"<z>\", theZone.name)\
    end\
    return inMsg \
end\
\
function dmlZone:processZoneStatics(inMsg, theZone)\
    inMsg = inMsg:gsub(\"<z>\", self.name)\
    return inMsg \
end\
\
-- process <t>, <lat>, <lon>, <ele>, <mgrs> \
function cfxZones.processSimpleZoneDynamics(inMsg, theZone, timeFormat, imperialUnits)\
    if not inMsg then return \"<nil inMsg>\" end\
    -- replace <t> with current mission time HMS\
    local absSecs = timer.getAbsTime()-- + env.mission.start_time\
    while absSecs > 86400 do \
        absSecs = absSecs - 86400 -- subtract out all days \
    end\
    if not timeFormat then timeFormat = \"<:h>:<:m>:<:s>\" end \
    local timeString  = dcsCommon.processHMS(timeFormat, absSecs)\
    local outMsg = inMsg:gsub(\"<t>\", timeString)\
    \
    -- replace <lat> with lat of zone point and <lon> with lon of zone point \
    -- and <mgrs> with mgrs coords of zone point \
    local currPoint = cfxZones.getPoint(theZone)\
    local lat, lon = coord.LOtoLL(currPoint)\
    lat, lon = dcsCommon.latLon2Text(lat, lon)\
    local alt = land.getHeight({x = currPoint.x, y = currPoint.z})\
    if imperialUnits then \
        alt = math.floor(alt * 3.28084) -- feet \
    else \
        alt = math.floor(alt) -- meters \
    end \
    outMsg = outMsg:gsub(\"<lat>\", lat)\
    outMsg = outMsg:gsub(\"<lon>\", lon)\
    outMsg = outMsg:gsub(\"<ele>\", alt)\
    local grid = coord.LLtoMGRS(coord.LOtoLL(currPoint))\
    local mgrs = grid.UTMZone .. ' ' .. grid.MGRSDigraph .. ' ' .. grid.Easting .. ' ' .. grid.Northing\
    outMsg = outMsg:gsub(\"<mgrs>\", mgrs)\
    return outMsg\
end \
\
-- process <v: flag>, <rsp: flag> <rrnd>\
function cfxZones.processDynamicValues(inMsg, theZone, msgResponses)\
    -- replace all occurences of <v: flagName> with their values \
    local pattern = \"<v:%s*[%s%w%*%d%.%-_]+>\" -- no list allowed but blanks and * and . and - and _ --> we fail on the other specials to keep this simple \
    local outMsg = inMsg\
    repeat -- iterate all patterns one by one \
        local startLoc, endLoc = string.find(outMsg, pattern)\
        if startLoc then \
            local theValParam = string.sub(outMsg, startLoc, endLoc)\
            -- strip lead and trailer \
            local param = string.gsub(theValParam, \"<v:%s*\", \"\")\
            param = string.gsub(param, \">\",\"\")\
            -- param = dcsCommon.trim(param) -- trim is called anyway\
            -- access flag\
            local val = cfxZones.getFlagValue(param, theZone)\
            val = tostring(val)\
            if not val then val = \"NULL\" end \
            -- replace pattern in original with new val \
            outMsg = string.gsub(outMsg, pattern, val, 1) -- only one sub!\
        end\
    until not startLoc\
    \
    -- now process rsp \
    pattern = \"<rsp:%s*[%s%w%*%d%.%-_]+>\" -- no list allowed but blanks and * and . and - and _ --> we fail on the other specials to keep this simple \
\
    if msgResponses and (#msgResponses > 0) then -- only if this zone has an array\
        --trigger.action.outText(\"enter response proccing\", 30)\
        repeat -- iterate all patterns one by one \
            local startLoc, endLoc = string.find(outMsg, pattern)\
            if startLoc then \
                local theValParam = string.sub(outMsg, startLoc, endLoc)\
                -- strip lead and trailer \
                local param = string.gsub(theValParam, \"<rsp:%s*\", \"\")\
                param = string.gsub(param, \">\",\"\")\
                \
                -- access flag\
                local val = cfxZones.getFlagValue(param, theZone)\
                if not val or (val < 1) then val = 1 end \
                if val > msgResponses then val = msgResponses end \
                \
                val = msgResponses[val]\
                val = dcsCommon.trim(val)\
                -- replace pattern in original with new val \
                outMsg = string.gsub(outMsg, pattern, val, 1) -- only one sub!\
            end\
        until not startLoc\
        \
        -- rnd response \
        local rndRsp = dcsCommon.pickRandom(msgResponses)\
        outMsg = outMsg:gsub (\"<rrnd>\", rndRsp)\
    end\
    \
    return outMsg\
end\
\
-- process <t: flag>\
function cfxZones.processDynamicTime(inMsg, theZone, timeFormat)\
    if not timeFormat then timeFormat = \"<:h>:<:m>:<:s>\" end\
    -- replace all occurences of <t: flagName> with their values \
    local pattern = \"<t:%s*[%s%w%*%d%.%-_]+>\" -- no list allowed but blanks and * and . and - and _ --> we fail on the other specials to keep this simple \
    local outMsg = inMsg\
    repeat -- iterate all patterns one by one \
        local startLoc, endLoc = string.find(outMsg, pattern)\
        if startLoc then \
            local theValParam = string.sub(outMsg, startLoc, endLoc)\
            -- strip lead and trailer \
            local param = string.gsub(theValParam, \"<t:%s*\", \"\")\
            param = string.gsub(param, \">\",\"\")\
            -- access flag\
            local val = cfxZones.getFlagValue(param, theZone)\
            -- use this to process as time value \
            --trigger.action.outText(\"time: accessing <\" .. param .. \"> and received <\" .. val .. \">\", 30)\
            local timeString  = dcsCommon.processHMS(timeFormat, val)\
            \
            if not timeString then timeString = \"NULL\" end \
            -- replace pattern in original with new val \
            outMsg = string.gsub(outMsg, pattern, timeString, 1) -- only one sub!\
        end\
    until not startLoc\
    return outMsg\
end\
\
-- process <lat/lon/ele/mgrs/lle/latlon/alt/vel/hdg/rhdg/type/player: zone/unit>\
function cfxZones.processDynamicLoc(inMsg, imperialUnits, responses)\
    local locales = {\"lat\", \"lon\", \"ele\", \"mgrs\", \"lle\", \"latlon\", \"alt\", \"vel\", \"hdg\", \"rhdg\", \"type\", \"player\"}\
    local outMsg = inMsg\
    local uHead = 0\
    for idx, aLocale in pairs(locales) do \
        local pattern = \"<\" .. aLocale .. \":%s*[%s%w%*%d%.%-_]+>\"\
        repeat -- iterate all patterns one by one \
            local startLoc, endLoc = string.find(outMsg, pattern)\
            if startLoc then\
                local theValParam = string.sub(outMsg, startLoc, endLoc)\
                -- strip lead and trailer \
                local param = string.gsub(theValParam, \"<\" .. aLocale .. \":%s*\", \"\")\
                param = string.gsub(param, \">\",\"\")\
                -- find zone or unit\
                param = dcsCommon.trim(param)\
                local thePoint = nil \
                local tZone = cfxZones.getZoneByName(param)\
                local tUnit = Unit.getByName(param)\
                local spd = 0\
                local angels = 0 \
                local theType = \"<errType>\"\
                local playerName = \"Unknown\"\
                if tZone then\
                    theType = \"Zone\"\
                    playerName = \"?zone?\"\
                    thePoint = cfxZones.getPoint(tZone)\
                    if tZone.linkedUnit and Unit.isExist(tZone.linkedUnit) then \
                        local lU = tZone.linkedUnit\
                        local masterPoint = lU:getPoint()\
                        thePoint.y = masterPoint.y \
                        spd = dcsCommon.getUnitSpeed(lU)\
                        spd = math.floor(spd * 3.6)\
                        uHead = math.floor(dcsCommon.getUnitHeading(tUnit) * 57.2958) -- to degrees.\
                    else \
                        -- since zones always have elevation of 0, \
                        -- now get the elevation from the map \
                        thePoint.y = land.getHeight({x = thePoint.x, y = thePoint.z})\
                    end\
                elseif tUnit then \
                    if Unit.isExist(tUnit) then\
                        theType = tUnit:getTypeName()\
                        if tUnit.getPlayerName and tUnit:getPlayerName() then\
                            playerName = tUnit:getPlayerName()\
                        end\
                        thePoint = tUnit:getPoint()\
                        spd = dcsCommon.getUnitSpeed(tUnit)\
                        -- convert m/s to km/h \
                        spd = math.floor(spd * 3.6)\
                        uHead = math.floor(dcsCommon.getUnitHeading(tUnit) * 57.2958) -- to degrees. \
                    end\
                else \
                    -- nothing to do, remove me.\
                end\
\
                local locString = \"err\"\
                if thePoint then \
                    -- now that we have a point, we can do locale-specific\
                    -- processing. return result in locString\
                    local lat, lon, alt = coord.LOtoLL(thePoint)\
                    lat, lon = dcsCommon.latLon2Text(lat, lon)\
                    angels = math.floor(thePoint.y) \
                    if imperialUnits then \
                        alt = math.floor(alt * 3.28084) -- feet\
                        spd = math.floor(spd * 0.539957) -- km/h to knots    \
                        angels = math.floor(angels * 3.28084)\
                    else \
                        alt = math.floor(alt) -- meters \
                    end \
                    \
                    if angels > 1000 then \
                        angels = math.floor(angels / 100) * 100 \
                    end\
                    \
                    if aLocale == \"lat\" then locString = lat \
                    elseif aLocale == \"lon\" then locString = lon \
                    elseif aLocale == \"ele\" then locString = tostring(alt)\
                    elseif aLocale == \"lle\" then locString = lat .. \" \" .. lon .. \" ele \" .. tostring(alt) \
                    elseif aLocale == \"latlon\" then locString = lat .. \" \" .. lon \
                    elseif aLocale == \"alt\" then locString = tostring(angels) -- don't confuse alt and angels, bad var naming here\
                    elseif aLocale == \"vel\" then locString = tostring(spd)\
                    elseif aLocale == \"hdg\" then locString = tostring(uHead)\
                    elseif aLocale == \"type\" then locString = theType \
                    elseif aLocale == \"player\" then locString = playerName \
                    elseif aLocale == \"rhdg\" and (responses) then \
                        local offset = cfxZones.rspMapper360(uHead, #responses)\
                        locString = dcsCommon.trim(responses[offset])\
                    else \
                        -- we have mgrs\
                        local grid = coord.LLtoMGRS(coord.LOtoLL(thePoint))\
                        locString = grid.UTMZone .. ' ' .. grid.MGRSDigraph .. ' ' .. grid.Easting .. ' ' .. grid.Northing\
                    end\
                end\
                -- replace pattern in original with new val \
                outMsg = string.gsub(outMsg, pattern, locString, 1) -- only one sub!\
            end -- if startloc\
        until not startLoc\
    end -- for all locales \
    return outMsg\
end\
\
-- process reference that can be flag, Zone, or unit.\
-- i.e. <coa: xyz>\
function cfxZones.processDynamicVZU(inMsg)\
local locales = {\"coa\",}\
    local outMsg = inMsg\
    local uHead = 0\
    for idx, aLocale in pairs(locales) do \
        local pattern = \"<\" .. aLocale .. \":%s*[%s%w%*%d%.%-_]+>\" -- e.g. \"<coa: flag Name>\
        repeat -- iterate all patterns one by one \
            local startLoc, endLoc = string.find(outMsg, pattern)\
            if startLoc then\
                local theValParam = string.sub(outMsg, startLoc, endLoc)\
                -- strip lead and trailer \
                local param = string.gsub(theValParam, \"<\" .. aLocale .. \":%s*\", \"\") -- remove \"<coa:\"\
                param = string.gsub(param, \">\",\"\") -- remove trailing \">\"\
                -- find zone or unit\
                param = dcsCommon.trim(param) -- param = \"flag Name\"\
                local tZone = cfxZones.getZoneByName(param)\
                local tUnit = Unit.getByName(param)\
\
                local locString = \"err\"\
                if aLocale == \"coa\" then\
                    coa = trigger.misc.getUserFlag(param)\
                    if tZone then coa = tZone.owner end \
                    if tUnit and Unit:isExist(tUnit) then coa = tUnit:getCoalition() end \
                    locString = dcsCommon.coalition2Text(coa)\
                end\
\
                outMsg = string.gsub(outMsg, pattern, locString, 1) -- only one sub!\
            end -- if startloc\
        until not startLoc\
    end -- for all locales \
    return outMsg\
end\
\
-- process two-value vars that can be flag or unit and return interpreted value\
-- i.e. <alive: Aerial-1-1>\
function cfxZones.processDynamicValueVU(inMsg)\
local locales = {\"yes\", \"true\", \"alive\", \"in\"}\
    local outMsg = inMsg\
    local uHead = 0\
    for idx, aLocale in pairs(locales) do \
        local pattern = \"<\" .. aLocale .. \":%s*[%s%w%*%d%.%-_]+>\" -- e.g. \"<yes: flagOrUnitName>\
        repeat -- iterate all patterns one by one \
            local startLoc, endLoc = string.find(outMsg, pattern)\
            if startLoc then\
                local theValParam = string.sub(outMsg, startLoc, endLoc)\
                -- strip lead and trailer \
                local param = string.gsub(theValParam, \"<\" .. aLocale .. \":%s*\", \"\") -- remove \"<alive:\"\
                param = string.gsub(param, \">\",\"\") -- remove trailing \">\"\
                -- find zone or unit\
                param = dcsCommon.trim(param) -- param = \"flagOrUnitName\"\
                local tUnit = Unit.getByName(param)\
                local yesNo = trigger.misc.getUserFlag(param) ~= 0\
                if tUnit then yesNo = Unit.isExist(tUnit) end\
                local locString = \"err\"\
                if aLocale == \"yes\" then                    \
                    if yesNo then locString = \"yes\" else locString = \"no\" end\
                elseif aLocale == \"true\" then \
                    if yesNo then locString = \"true\" else locString = \"false\" end \
                elseif aLocale == \"alive\" then \
                    if yesNo then locString = \"alive\" else locString = \"dead\" end\
                elseif aLocale == \"in\" then \
                    if yesNo then locString = \"in\" else locString = \"out\" end\
                end\
\
                outMsg = string.gsub(outMsg, pattern, locString, 1) -- only one sub!\
            end -- if startloc\
        until not startLoc\
    end -- for all locales \
    return outMsg\
end\
\
function cfxZones.processDynamicAB(inMsg, locale)\
    local outMsg = inMsg\
    if not locale then locale = \"A/B\" end \
    \
    -- <A/B: flagOrUnitName [val A | val B]>\
    local replacerValPattern = \"<\".. locale .. \":%s*[%s%w%*%d%.%-_]+\" .. \"%[[%s%w]+|[%s%w]+%]\"..\">\"\
    repeat \
        local startLoc, endLoc = string.find(outMsg, replacerValPattern)\
        if startLoc then \
            local rp = string.sub(outMsg, startLoc, endLoc)\
            -- get val/unit name \
            local valA, valB = string.find(rp, \":%s*[%s%w%*%d%.%-_]+%[\")\
            local val = string.sub(rp, valA+1, valB-1)\
            val = dcsCommon.trim(val)\
            -- get left and right \
            local leftA, leftB = string.find(rp, \"%[[%s%w]+|\" ) -- from \"[\" to \"|\"\
            local rightA, rightB = string.find(rp, \"|[%s%w]+%]\") -- from \"|\" to \"]\"\
            left = string.sub(rp, leftA+1, leftB-1)\
            left = dcsCommon.trim(left)\
            right = string.sub(rp, rightA+1, rightB-1)\
            right = dcsCommon.trim(right)        \
            local yesno = false\
            -- see if unit exists\
            local theUnit = Unit.getByName(val)\
            if theUnit then \
                yesno = Unit:isExist(theUnit)\
            else \
                yesno = trigger.misc.getUserFlag(val) ~= 0\
            end\
\
            local locString = left \
            if yesno then locString = right end \
            outMsg = string.gsub(outMsg, replacerValPattern, locString, 1)\
        end\
    until not startLoc \
    return outMsg\
end\
\
function cfxZones.rspMapper360(directionInDegrees, numResponses)\
    -- maps responses around a clock. Clock has 12 'responses' (12, 1, .., 11), \
    -- with the first (12) also mapping to the last half arc \
    -- this method dynamically 'winds' the responses around \
    -- a clock and returns the index of the message to display \
    if numResponses < 1 then numResponses = 1 end \
    directionInDegrees = math.floor(directionInDegrees) \
    while directionInDegrees < 0 do directionInDegrees = directionInDegrees + 360 end \
    while directionInDegrees >= 360 do directionInDegrees = directionInDegrees - 360 end \
    -- now we have 0..360 \
    -- calculate arc per item \
    local arcPerItem = 360 / numResponses\
    local halfArc = arcPerItem / 2\
\
    -- we now map 0..360 to (0-halfArc..360-halfArc) by shifting \
    -- direction by half-arc and clipping back 0..360\
    -- and now we can directly derive the index of the response \
    directionInDegrees = directionInDegrees + halfArc\
    if directionInDegrees >= 360 then directionInDegrees = directionInDegrees - 360 end \
    \
    local index = math.floor(directionInDegrees / arcPerItem) + 1 -- 1 .. numResponses \
    \
    return index \
end\
\
-- replaces dcsCommon with same name \
-- timeFormat is optional, default is \"<:h>:<:m>:<:s>\"\
-- imperialUnits is optional, defaults to meters \
-- responses is an array of string, defaults to {}\
function cfxZones.processStringWildcards(inMsg, theZone, timeFormat, imperialUnits, responses)\
    if not inMsg then return \"<nil inMsg>\" end\
    local formerType = type(inMsg)\
    if formerType ~= \"string\" then inMsg = tostring(inMsg) end\
    if not inMsg then inMsg = \"<inMsg is incompatible type \" .. formerType .. \">\" end\
    local theMsg = inMsg\
    -- process common DCS stuff like /n \
    theMsg = dcsCommon.processStringWildcards(theMsg) -- call old inherited\
    -- process <z>\
    theMsg = cfxZones.processZoneStatics(theMsg, theZone)\
    -- process <t>, <lat>, <lon>, <ele>, <mgrs>\
    theMsg = cfxZones.processSimpleZoneDynamics(theMsg, theZone, timeFormat, imperialUnits)\
    -- process <v: flag>, <rsp: flag> <rrnd>\
    theMsg = cfxZones.processDynamicValues(theMsg, theZone, responses)\
    -- process <t: flag>\
    theMsg = cfxZones.processDynamicTime(theMsg, theZone, timeFormat)\
    -- process <lat/lon/ele/mgrs/lle/latlon/alt/vel/hdg/rhdg/type/player: zone/unit>\
    theMsg = cfxZones.processDynamicLoc(theMsg, imperialUnits, responses)\
    -- process values that can be derived from flag (default), zone or unit \
    theMsg = cfxZones.processDynamicVZU(theMsg)\
    theMsg = cfxZones.processDynamicAB(theMsg)\
    theMsg = cfxZones.processDynamicValueVU(theMsg)\
    return theMsg\
end\
\
--\
-- ============\
-- MOVING ZONES \
-- ============ \
-- \
-- Moving zones contain a link to their unit\
-- they are always located at an offset (x,z) or delta, phi \
-- to their master unit. delta phi allows adjustment for heading\
-- The cool thing about moving zones in cfx is that they do not\
-- require special handling, they are always updated \
-- and work with 'pointinzone' etc automatically\
\
-- Always works on cfx Zones, NEVER on DCS zones.\
--\
-- requires that readFromDCS has been done\
--\
function cfxZones.getDCSOrigin(aZone)\
    local o = {}\
    o.x = aZone.dcsOrigin.x\
    o.y = 0\
    o.z = aZone.dcsOrigin.z \
    return o\
end\
\
function dmlZone:getDCSOrigin()\
    local o = {}\
    if not self.dcsOrigin then \
        trigger.action.outText(\"dmlZone (OOP): no dcsOrigin defined for zone <\" .. self.name .. \">\", 30)\
        o.x = 0\
        o.y = 0\
        o.z = 0\
    else\
        o.x = self.dcsOrigin.x\
        o.y = 0\
        o.z = self.dcsOrigin.z \
    end \
    return o\
end\
\
function cfxZones.getLinkedUnit(theZone)\
    if not theZone then return nil end \
    if not theZone.linkedUnit then return nil end \
    if not Unit.isExist(theZone.linkedUnit) then return nil end \
    return theZone.linkedUnit \
end\
\
function dmlZone:getLinkedUnit()\
    if not self.linkedUnit then return nil end \
    if not Unit.isExist(self.linkedUnit) then return nil end \
    return self.linkedUnit \
end\
\
function cfxZones.getPoint(aZone, getHeight) -- always works, even linked, returned point can be reused\
-- returned y (when using getHeight) is that of the land, else 0 \
    if not getHeight then getHeight = false end \
    if aZone.linkedUnit then \
        local theUnit = aZone.linkedUnit\
        -- has a link. is link existing?\
        if Unit.isExist(theUnit) then \
            -- updates zone position \
            cfxZones.centerZoneOnUnit(aZone, theUnit)\
            local dx = aZone.dx\
            local dy = aZone.dy\
            if aZone.useHeading then \
                dx, dy = cfxZones.calcHeadingOffset(aZone, theUnit)\
            end\
            cfxZones.offsetZone(aZone, dx, dy)\
        end\
    end\
    local thePos = {}\
    thePos.x = aZone.point.x\
    thePos.z = aZone.point.z\
    if not getHeight then \
        thePos.y = 0 -- aZone.y \
    else \
        thePos.y = land.getHeight({x = thePos.x, y = thePos.z})\
    end\
    \
    return thePos \
end\
\
function dmlZone:getPoint(getHeight)\
    if not getHeight then getHeight = false end \
    if self.linkedUnit then \
        local theUnit = self.linkedUnit\
        -- has a link. is link existing?\
        if Unit.isExist(theUnit) then \
            -- updates zone position \
            self:centerZoneOnUnit(theUnit)\
            local dx = self.dx\
            local dy = self.dy\
            if self.useHeading then \
                dx, dy = self:calcHeadingOffset(theUnit)\
            end\
            self:offsetZone(dx, dy)\
        end\
    end\
    local thePos = {}\
    thePos.x = self.point.x\
    thePos.z = self.point.z\
    if not getHeight then \
        thePos.y = 0 -- aZone.y \
    else \
        thePos.y = land.getHeight({x = thePos.x, y = thePos.z})\
    end\
    \
    return thePos \
end\
\
function dmlZone:getName() -- no cfxZones.bridge!\
    return self.name \
end\
\
function cfxZones.linkUnitToZone(theUnit, theZone, dx, dy) -- note: dy is really Z, don't get confused!!!!\
    theZone.linkedUnit = theUnit\
    if not dx then dx = 0 end\
    if not dy then dy = 0 end \
    theZone.dx = dx\
    theZone.dy = dy \
    theZone.rxy = math.sqrt(dx * dx + dy * dy) -- radius \
    local unitHeading = dcsCommon.getUnitHeading(theUnit)\
    local bearingOffset = math.atan2(dy, dx) -- rads \
    if bearingOffset < 0 then bearingOffset = bearingOffset + 2 * 3.141592 end \
\
    local dPhi = bearingOffset - unitHeading\
    if dPhi < 0 then dPhi = dPhi + 2 * 3.141592 end\
    if (theZone.verbose and theZone.useHeading) then \
        trigger.action.outText(\"Zone is at <\" .. math.floor(57.2958 * dPhi) .. \"> relative to unit heading\", 30)\
    end\
    theZone.dPhi = dPhi -- constant delta between unit heading and \
    -- direction to zone \
    theZone.uHdg = unitHeading -- original unit heading to turn other \
    -- units if need be \
end\
\
function dmlZone:linkUnitToZone(theUnit, dx, dy) -- note: dy is really Z, don't get confused!!!!\
    self.linkedUnit = theUnit\
    if not dx then dx = 0 end\
    if not dy then dy = 0 end \
    self.dx = dx\
    self.dy = dy \
    self.rxy = math.sqrt(dx * dx + dy * dy) -- radius \
    local unitHeading = dcsCommon.getUnitHeading(theUnit)\
    local bearingOffset = math.atan2(dy, dx) -- rads \
    if bearingOffset < 0 then bearingOffset = bearingOffset + 2 * 3.141592 end \
\
    local dPhi = bearingOffset - unitHeading\
    if dPhi < 0 then dPhi = dPhi + 2 * 3.141592 end\
    if (self.verbose and self.useHeading) then \
        trigger.action.outText(\"Zone <\" .. self.name .. \"> is at <\" .. math.floor(57.2958 * dPhi) .. \"> relative to unit heading\", 30)\
    end\
    self.dPhi = dPhi -- constant delta between unit heading and \
    -- direction to zone \
    self.uHdg = unitHeading -- original unit heading to turn other \
    -- units if need be \
end\
\
function cfxZones.zonesLinkedToUnit(theUnit) -- returns all zones linked to this unit \
    if not theUnit then return {} end \
    local linkedZones = {}\
    for idx, theZone in pairs (cfxZones.zones) do \
        if theZone.linkedUnit == theUnit then \
            table.insert(linkedZones, theZone)\
        end\
    end\
    return linkedZones\
end\
\
function cfxZones.calcHeadingOffset(aZone, theUnit)\
    -- recalc dx and dy based on ry and current heading \
    -- since 0 degrees is [0,1] = [0,r] the calculation of \
    -- rotated coords can be simplified from \
    -- xr = x cos phi - y sin phi = -r sin phi\
    -- yr = y cos phi + x sin phi = r cos phi \
    local unitHeading = dcsCommon.getUnitHeading(theUnit)\
    -- add heading offset \
    local zoneBearing = unitHeading + aZone.dPhi \
    if zoneBearing > 2 * 3.141592 then zoneBearing = zoneBearing - 2 * 3.141592 end \
                    \
    -- in DCS, positive x is north (wtf?) and positive z is east \
    local dy = (-aZone.rxy) * math.sin(zoneBearing)\
    local dx = aZone.rxy * math.cos(zoneBearing)\
    return dx, -dy -- note: dy is z coord!!!!\
end\
\
function dmlZone:calcHeadingOffset(theUnit)\
    local unitHeading = dcsCommon.getUnitHeading(theUnit)\
    local zoneBearing = unitHeading + self.dPhi \
    if zoneBearing > 2 * 3.141592 then zoneBearing = zoneBearing - 2 * 3.141592 end \
    -- in DCS, positive x is north (wtf?) and positive z is east \
    local dy = (-self.rxy) * math.sin(zoneBearing)\
    local dx = self.rxy * math.cos(zoneBearing)\
    return dx, -dy -- note: dy is z coord!!!!\
end\
\
\
function cfxZones.updateMovingZones()\
    cfxZones.updateSchedule = timer.scheduleFunction(cfxZones.updateMovingZones, {}, timer.getTime() + 1/cfxZones.ups)\
    -- simply scan all cfx zones for the linkName property, and if present\
    -- update the zone's points\
    for aName,aZone in pairs(cfxZones.zones) do\
        -- only do this if ther is a linkName property, \
        -- else this zone isn't linked. link name is harmonized from \
        -- both linkUnit non-DML and linedUnit DML        \
        if aZone.linkName then \
            if aZone.linkBroken then \
                -- try to relink \
                cfxZones.initLink(aZone)\
            else --if aZone.linkName then  \
                -- always re-acquire linkedUnit via Unit.getByName()\
                -- this way we gloss over any replacements via spawns\
                aZone.linkedUnit = Unit.getByName(aZone.linkName)\
            end\
            \
            if aZone.linkedUnit then \
                local theUnit = aZone.linkedUnit\
                -- has a link. is link existing?\
                if theUnit:isExist() then \
                    cfxZones.centerZoneOnUnit(aZone, theUnit)\
                    local dx = aZone.dx \
                    local dy = aZone.dy -- this is actually z \
                    if aZone.useHeading then \
                        dx, dy = cfxZones.calcHeadingOffset(aZone, theUnit)\
                    end\
                    cfxZones.offsetZone(aZone, dx, dy)\
                else \
                    -- we lost link (track level)\
                    aZone.linkBroken = true \
                    aZone.linkedUnit = nil \
                end\
            else \
                -- we lost link (top level)\
                aZone.linkBroken = true \
                aZone.linkedUnit = nil \
            end\
        else \
            -- this zone isn't linked\
        end\
    end\
end\
\
function cfxZones.initLink(theZone)\
    theZone.linkBroken = true \
    theZone.linkedUnit = nil \
    theUnit = Unit.getByName(theZone.linkName)\
    if theUnit then\
\
        local dx = 0\
        local dz = 0\
        if theZone.useOffset or theZone.useHeading then \
            local A = cfxZones.getDCSOrigin(theZone)\
            local B = theUnit:getPoint()\
            local delta = dcsCommon.vSub(A,B) \
            dx = delta.x \
            dz = delta.z\
        end\
        cfxZones.linkUnitToZone(theUnit, theZone, dx, dz) -- also sets theZone.linkedUnit\
\
        if theZone.verbose then \
            trigger.action.outText(\"Link established for zone <\" .. theZone.name .. \"> to unit <\" .. theZone.linkName .. \">: dx=<\" .. math.floor(dx) .. \">, dz=<\" .. math.floor(dz) .. \"> dist = <\" .. math.floor(math.sqrt(dx * dx + dz * dz)) .. \">\" , 30)\
        end \
        theZone.linkBroken = nil \
\
    else \
        if theZone.verbose then \
            trigger.action.outText(\"Linked unit: no unit <\" .. theZone.linkName .. \"> to link <\" .. theZone.name .. \"> to\", 30)\
        end\
    end\
end\
\
function dmlZone:initLink()\
    self.linkBroken = true \
    self.linkedUnit = nil \
    theUnit = Unit.getByName(self.linkName)\
    if theUnit then\
\
        local dx = 0\
        local dz = 0\
        if self.useOffset or self.useHeading then \
            local A = self:getDCSOrigin()\
            local B = theUnit:getPoint()\
            local delta = dcsCommon.vSub(A,B) \
            dx = delta.x \
            dz = delta.z\
        end\
        self:linkUnitToZone(theUnit, dx, dz) -- also sets theZone.linkedUnit\
\
        if self.verbose then \
            trigger.action.outText(\"Link established for zone <\" .. self.name .. \"> to unit <\" .. self.linkName .. \">: dx=<\" .. math.floor(dx) .. \">, dz=<\" .. math.floor(dz) .. \"> dist = <\" .. math.floor(math.sqrt(dx * dx + dz * dz)) .. \">\" , 30)\
        end \
        self.linkBroken = nil \
\
    else \
        if self.verbose then \
            trigger.action.outText(\"Linked unit: no unit <\" .. self.linkName .. \"> to link <\" .. self.name .. \"> to\", 30)\
        end\
    end\
end\
\
function cfxZones.startMovingZones()\
    -- read all zones, and look for a property called 'linkedUnit'\
    -- which will make them a linked zone if there is a unit that exists\
    -- also suppors 'useOffset' and 'useHeading'\
    for aName,aZone in pairs(cfxZones.zones) do\
        \
        local lU = nil \
        -- check if DCS zone has the linkUnit new attribute introduced in \
        -- late 2022 with 2.8\
        if aZone.dcsZone.linkUnit then \
            local theID = aZone.dcsZone.linkUnit \
            lU = dcsCommon.getUnitNameByID(theID)\
            if not lU then \
                trigger.action.outText(\"WARNING: Zone <\" .. aZone.name .. \">: cannot resolve linked unit ID <\" .. theID .. \">\", 30)\
                lU = \"***DML link err***\"\
            end\
        elseif cfxZones.hasProperty(aZone, \"linkedUnit\") then \
            lU = cfxZones.getZoneProperty(aZone, \"linkedUnit\")\
        end\
        \
        -- sanity check \
        if aZone.dcsZone.linkUnit and cfxZones.hasProperty(aZone, \"linkedUnit\") then \
            trigger.action.outText(\"WARNING: Zone <\" .. aZone.name .. \"> has dual unit link definition. Will use link to unit <\" .. lU .. \">\", 30)\
        end\
        \
        if lU then \
            aZone.linkName = lU\
            aZone.useOffset = cfxZones.getBoolFromZoneProperty(aZone, \"useOffset\", false)\
            aZone.useHeading = cfxZones.getBoolFromZoneProperty(aZone, \"useHeading\", false)\
            \
            cfxZones.initLink(aZone)\
\
        end\
        \
    end\
end\
\
--\
-- marking zones \
--\
\
function cfxZones.spreadNObjectsOverLine(theZone, n, objType, left, right, cty) -- leaves last position free \
    trigger.action.outText(\"left = \" .. dcsCommon.point2text(left) .. \", right = \" .. dcsCommon.point2text(right),30)\
    \
    local a = {x=left.x, y=left.z}\
    local b = {x=right.x, y=right.z}\
    local dir = dcsCommon.vSub(b,a) -- vector from left to right\
    local dirInc = dcsCommon.vMultScalar(dir, 1/n) \
    local count = 0 \
    local p = {x=left.x, y = left.z}\
    local baseName = dcsCommon.uuid(theZone.name)\
    while count < n do \
        local theStaticData = dcsCommon.createStaticObjectData(dcsCommon.uuid(theZone.name), objType)\
        dcsCommon.moveStaticDataTo(theStaticData, p.x, p.y)\
        local theObject = coalition.addStaticObject(cty, theStaticData)\
        p = dcsCommon.vAdd(p, dirInc) \
        count = count + 1\
    end\
end\
\
function cfxZones.markZoneWithObjects(theZone, objType, qtrNum, markCenter, cty) -- returns set \
    if not objType then objType = \"Black_Tyre_RF\" end \
    if not qtrNum then qtrNum = 3 end -- +1 for number of marks per quarter \
    if not cty then cty = dcsCommon.getACountryForCoalition(0) end -- some neutral county\
    local p = theZone:getPoint()\
    local newObjects = {}\
    \
    if theZone.isPoly then \
        -- we place 4 * (qtrnum + 1) objects around the edge of the zone \
        -- we mark each poly along v-->v+1, placing ip and qtrNum additional points \
        local o = cfxZones.spreadNObjectsOverLine(theZone, qtrNum + 1, objType, theZone.poly[1], theZone.poly[2], cty)\
        local p = cfxZones.spreadNObjectsOverLine(theZone, qtrNum + 1, objType, theZone.poly[2], theZone.poly[3], cty)\
        local q = cfxZones.spreadNObjectsOverLine(theZone, qtrNum + 1, objType, theZone.poly[3], theZone.poly[4], cty)\
        local r = cfxZones.spreadNObjectsOverLine(theZone, qtrNum + 1, objType, theZone.poly[4], theZone.poly[1], cty)\
        o = dcsCommon.combineTables(o,p)\
        p = dcsCommon.combineTables(q,r)\
        newObjects = dcsCommon.combineTables(o,p)\
        \
    else \
        local numObjects = (qtrNum + 1) * 4\
        local degrees = 3.14157 / 180\
        local degreeIncrement = (360 / numObjects) * degrees\
        local currDegree = 0\
        local radius = theZone.radius\
        for i=1, numObjects do \
            local ox = p.x + math.cos(currDegree) * radius\
            local oy = p.z + math.sin(currDegree) * radius -- note: z!\
            local theStaticData = dcsCommon.createStaticObjectData(dcsCommon.uuid(theZone.name), objType)\
            dcsCommon.moveStaticDataTo(theStaticData, ox, oy)\
            local theObject = coalition.addStaticObject(cty, theStaticData)\
            table.insert(newObjects, theObject)\
            currDegree = currDegree + degreeIncrement\
        end\
    end\
    \
    if markCenter then \
        -- also mark the center \
        local theObject = cfxZones.markPointWithObject(p, objType, cty)\
        table.insert(newObjects, theObject)\
    end     \
    \
    return newObjects\
end\
\
function dmlZone:markZoneWithObjects(objType, qtrNum, markCenter, cty) -- returns set \
    return cfxZones.markZoneWithObjects(self, objType, qtrNum, markCenter)\
end\
\
function cfxZones.markCenterWithObject(theZone, objType, cty) -- returns object\
    local p = cfxZones.getPoint(theZone)\
    local theObject = cfxZones.markPointWithObject(theZone, p, objType, cty)\
    return theObject\
end\
\
function dmlZone:markCenterWithObject(objType, cty) -- returns object \
    return cfxZones.markCenterWithObject(self, objType, cty)\
end\
\
function cfxZones.markPointWithObject(theZone, p, theType, cty) -- returns object \
    if not cty then cty = dcsCommon.getACountryForCoalition(0) end\
    local ox = p.x\
    local oy = p.y     \
    if p.z then oy = p.z end -- support vec 2 and vec 3 \
    local theStaticData = dcsCommon.createStaticObjectData(dcsCommon.uuid(theZone.name), theType)\
    dcsCommon.moveStaticDataTo(theStaticData, ox, oy)\
    local theObject = coalition.addStaticObject(cty, theStaticData)\
    return theObject\
end\
\
function dmlZone:markPointWithObject(p, theType, cty) -- returns object \
    return cfxZones.markPointWithObject(self, p, theType, cty)\
end\
--\
-- ===========\
-- INIT MODULE\
-- ===========\
--\
\
function cfxZones.initZoneVerbosity()\
    for aName,aZone in pairs(cfxZones.zones) do\
        -- support for zone-local verbose flag \
        aZone.verbose = cfxZones.getBoolFromZoneProperty(aZone, \"verbose\", false)\
    end\
end\
\
function cfxZones.init()\
    -- read all zones into my own db\
    cfxZones.readFromDCS(true) -- true: erase old\
\
    -- pre-read zone owner for all zones\
    -- much like verbose, all zones have owner\
    for n, aZone in pairs(cfxZones.zones) do\
        aZone.owner = cfxZones.getCoalitionFromZoneProperty(aZone, \"owner\", 0)\
    end\
        \
    -- enable all zone's verbose flags if present\
    -- must be done BEFORE we start the moving zones \
    cfxZones.initZoneVerbosity()\
    \
    -- now initialize moving zones\
    cfxZones.startMovingZones()\
    cfxZones.updateMovingZones() -- will auto-repeat\
    \
    trigger.action.outText(\"cf/x Zones v\".. cfxZones.version .. \": loaded, zones:\" .. dcsCommon.getSizeOfTable(cfxZones.zones), 30)\
\
end\
\
-- get everything rolling\
cfxZones.init()\
",
                    ["predicate"] = "a_do_script",
                }, -- end of [2]
                [3] = 
                {
                    ["text"] = "-- cfxCommander - issue dcs commands to groups etc\
--\
-- supports scheduling\
-- *** EXTENDS ZONES: 'pathing' attribute \
--\
cfxCommander = {}\
cfxCommander.version = \"1.1.4\"\
--[[-- VERSION HISTORY\
 - 1.0.5 - createWPListForGroupToPointViaRoads: detect no road found \
 - 1.0.6 - build in more group checks in assign wp list \
         - added sanity checks for doScheduledTask\
         - assignWPListToGroup now can schedule tasks \
         - makeGroupGoThere supports scheduling\
         - makeGroupGoTherePreferringRoads supports scheduling \
         - scheduleTaskForGroup supports immediate execution\
         - makeGroupHalt\
 - 1.0.7 - warning if road shorter than direct\
         - forceOffRoad option\
         - noRoadsAtAll option \
 - 1.1.0 - load libs \
         - pathing zones. Currently only supports \
         - offroad to override road-usage\
         - pathing zones are overridden by noRoadsAtAll\
         - CommanderConfig zones \
 - 1.1.1 - default pathing for pathing zone is normal, not offroad \
 - 1.1.2 - makeGroupTransmit \
         - makeGroupStopTransmitting\
         - verbose check before path warning\
         - added delay defaulting for most scheduling functions \
 - 1.1.3 - isExist() guard improvements for multiple methods\
         - cleaned up comments\
 - 1.1.4 - hardened makeGroupGoThere()\
 \
--]]--\
\
cfxCommander.requiredLibs = {\
    \"dcsCommon\", -- common is of course needed for everything\
    \"cfxZones\", -- zones management for pathing zones \
}\
\
cfxCommander.verbose = false \
cfxCommander.forceOffRoad = true -- if true, vehicles path follow roads, but may drive offroad (they follow vertex points from path but not the road as they are still commanded 'offroad')\
cfxCommander.noRoadsAtAll = true  -- if true, always go direct, overrides forceOffRoad when true. Always a two-point path. Here, there, bang! \
cfxCommander.pathZones = {} -- zones that can override road settings\
\
--\
-- path zone\
--\
function cfxCommander.processPathingZone(aZone) -- process attribute and add to zone\
    local pathing = cfxZones.getStringFromZoneProperty(aZone, \"pathing\", \"normal\") -- must be \"offroad\" to force offroad\
    pathing = pathing:lower()\
    -- currently no validation of attribute \
    aZone.pathing = pathing\
end \
\
function cfxCommander.addPathingZone(aZone)\
    table.insert(cfxCommander.pathZones, aZone)\
end \
\
function cfxCommander.hasPathZoneFor(here, there)\
    for idx, aZone in pairs(cfxCommander.pathZones) do \
        if cfxZones.pointInZone(here, aZone) then return aZone end \
        if cfxZones.pointInZone(there, aZone) then return aZone end\
    end\
    return nil\
end\
\
--\
-- Config Zone Reading if present \
--\
function cfxCommander.readConfigZone()\
    -- note: must match exactly!!!!\
    local theZone = cfxZones.getZoneByName(\"CommanderConfig\") \
    if not theZone then \
        trigger.action.outText(\"+++cmdr: no config zone!\", 30) \
        return \
    end \
    \
    trigger.action.outText(\"+++cmdr: found config zone!\", 30) \
    \
    cfxCommander.verbose = cfxZones.getBoolFromZoneProperty(theZone, \"verbose\", false)\
    cfxCommander.forceOffRoad = cfxZones.getBoolFromZoneProperty(theZone, \"forceOffRoad\", false) -- if true, vehicles path follow roads, but may drive offroad\
    cfxCommander.noRoadsAtAll = cfxZones.getBoolFromZoneProperty(theZone, \"noRoadsAtAll\", false)\
\
end\
\
--\
-- Options are key, value pairs. Scheduler when you are creating groups\
-- \
\
function cfxCommander.doOption(data) \
    if cfxCommander.verbose then \
        trigger.action.outText(\"Commander: setting option \" .. data.key .. \" --> \" .. data.value, 30)\
    end\
\
    local theController = data.group:getController()\
    theController:setOption(data.key, data.value)\
end\
\
function cfxCommander.scheduleOptionForGroup(group, key, value, delay) \
    local data = {}\
    if not delay then delay = 0.1 end \
    data.group = group\
    data.key = key\
    data.value = value \
    timer.scheduleFunction(cfxCommander.doOption, data, timer.getTime() + delay)\
end\
\
--\
-- performCommand is a special version of issuing a command\
-- that can be easily schduled by pushing the commandData on \
-- the stack with scheduling it \
-- group or name must be filled to get the group,\
-- and the command table is what is going to be passed to the setCommand\
-- commands are given in an array, so you can stack commands \
function cfxCommander.performCommands(commandData)\
    -- see if we have a group\
    if not commandData.group then \
        commandData.group = Group.getByName(commandData.name) -- better be inited!\
    end\
    -- get the AI\
    local theController = commandData.group:getController()\
    for i=1, #commandData.commands do\
        if cfxCommander.verbose then \
            trigger.action.outText(\"Commander: performing \" .. commandData.commands[i].id, 30)\
        end\
        theController:setCommand(commandData.commands[i])\
    end\
    \
    return nil -- a timer called us, so we return no desire to be rescheduled\
end\
\
function cfxCommander.scheduleCommands(data, delay)\
    if not delay then delay = 1 end \
    timer.scheduleFunction(cfxCommander.performCommands, data, timer.getTime() + delay)\
end\
\
function cfxCommander.scheduleSingleCommand(group, command, delay) \
    if not delay then delay = 1 end \
    local data = createCommandDataTableFor(group)\
    cfxCommander.addCommand(data, command)\
    cfxCommander.scheduleCommands(data, delay)\
end\
\
\
function cfxCommander.createCommandDataTableFor(group, name)\
    local cD = {}\
    if not group then \
        cD.name = name\
    else\
        cD.group = group\
    end\
    cD.commands={}\
    return cD\
end\
\
function cfxCommander.addCommand(theCD, theCommand)\
    if not theCD then return end \
    if not theCommand then return end \
    \
    table.insert(theCD.commands, theCommand)\
end\
\
function cfxCommander.createSetFrequencyCommand(freq, modulator)\
    local theCmd = {}\
    if not freq then freq = 100 end \
    if not modulator then modulator = 0 end -- AM = 0, default\
    theCmd.id = 'SetFrequency'\
    theCmd.params = {}\
    theCmd.params.frequency = freq * 10000 -- 88 --> 880000. 124 --> 1.24 MHz\
    theCmd.params.modulation = modulator\
    return theCmd\
end\
\
-- oneShot is optional. if present and anything but false, will cause message to \
-- me sent only once, no loops\
function cfxCommander.createTransmissionCommand(filename, oneShot)\
    local looping = true\
    if not filename then filename = \"dummy\" end \
    if oneShot then looping = false end\
    local theCmd = {}\
    theCmd.id = 'TransmitMessage'\
    theCmd.params = {}\
    theCmd.params.loop = looping\
    theCmd.params.file = \"l10n/DEFAULT/\" .. filename -- need to prepend the resource string\
    return theCmd\
end\
\
function cfxCommander.createStopTransmissionCommand()\
    local theCmd = {}\
    theCmd.id = 'stopTransmission'\
    theCmd.params = {}\
    return theCmd\
end\
\
--\
-- tasks\
-- \
\
function cfxCommander.doScheduledTask(data) \
    if cfxCommander.verbose then \
        trigger.action.outText(\"Commander: setting task \" .. data.task.id .. \" for group \" .. data.group:getName(), 30)\
    end\
    local theGroup = data.group \
    if not theGroup then return end \
    if not Group.isExist(theGroup) then return end \
--    if not theGroup.isExist then return end\
    \
    local theController = theGroup:getController()\
    theController:pushTask(data.task)\
end\
\
function cfxCommander.scheduleTaskForGroup(group, task, delay)\
    if not delay then delay = 0 end \
    local data = {}\
    data.group = group\
    data.task = task\
    if delay < 0.001 then \
        cfxCommander.doScheduledTask(data) -- immediate execution\
        return \
    end\
    timer.scheduleFunction(cfxCommander.doScheduledTask, data, timer.getTime() + delay)\
end\
\
function cfxCommander.createAttackGroupCommand(theGroupToAttack)\
    local task = {}\
    task.id = 'AttackGroup'\
    task.params = {}\
    task.params.groupID = theGroupToAttack:getID()\
    return task\
end\
\
function cfxCommander.createEngageGroupCommand(theGroupToAttack)\
    local task = {}\
    task.id = 'EngageGroup'\
    task.params = {}\
    task.params.groupID = theGroupToAttack:getID()\
    return task\
end\
\
--\
-- waypoints, routes etc \
--\
\
-- basic waypoint is for ground units. point can be xyz or xy \
function cfxCommander.createBasicWaypoint(point, speed, formation)\
    local wp = {}\
    wp.x = point.x\
    -- support xyz and xy format\
    if point.z then \
        wp.y = point.z\
    else\
        wp.y = point.y\
    end\
    \
    if not speed then speed = 6 end -- 6 m/s = 20 kph\
    wp.speed = speed \
    \
    if cfxCommander.forceOffRoad then \
        formation = \"Off Road\"\
    end\
    \
    if not formation then formation = \"Off Road\" end\
    -- legal formations:\
    -- Off road\
    -- On Road -- second letter upper case?\
    -- Cone \
    -- Rank\
    -- Diamond\
    -- Vee\
    -- EchelonR\
    -- EchelonL\
    wp.action = formation -- silly name, but that's how ME does it\
    wp.type = 'Turning Point'\
    return wp\
\
end\
\
function cfxCommander.buildTaskFromWPList(wpList)\
    -- build the task that will make a group follow the WP list\
    -- we do this by creating a \"Mission\" task around the WP List\
    -- WP list is consumed by this action\
    local missionTask = {}\
    missionTask.id = \"Mission\"\
    missionTask.params = {}\
    missionTask.params.route = {}\
    missionTask.params.route.points=wpList\
    return missionTask\
end\
\
function cfxCommander.assignWPListToGroup(group, wpList, delay)\
    if not delay then delay = 0 end \
    if not group then return end \
    if type(group) == 'string' then -- group name, nice mist trick \
        group = Group.getByName(group)\
    end\
    if not group then return end \
    if not Group.isExist(group) then return end \
    \
    local theTask = cfxCommander.buildTaskFromWPList(wpList)\
    local ctrl = group:getController()\
\
--[[--\
    if delay < 0.001 then -- immediate action\
        if ctrl then\
            ctrl:setTask(theTask)\
        end\
    else \
        -- delay execution of this command by the specified amount \
        -- of seconds \
        cfxCommander.scheduleTaskForGroup(group, theTask, delay)\
    end\
--]]--\
    cfxCommander.scheduleTaskForGroup(group, theTask, delay)\
end\
\
function cfxCommander.createWPListForGroupToPoint(group, point, speed, formation)\
    if type(group) == 'string' then -- group name\
        group = Group.getByName(group)\
    end\
\
    local wpList = {}\
    -- here we are, and we want to go there. In DCS, this means that\
    -- we need to create a wp list consisting of here and there\
    local here = dcsCommon.getGroupLocation(group)\
    local wpHere = cfxCommander.createBasicWaypoint(here, speed, formation)\
    local wpThere = cfxCommander.createBasicWaypoint(point, speed, formation)\
    wpList[1] = wpHere\
    wpList[2] = wpThere\
    return wpList\
end\
\
-- make a ground units group head to a waypoint by replacing the entire mission\
-- with a two-waypoint lsit from (here) to there at speed and formation. formation\
-- default is 'off road'\
function cfxCommander.makeGroupGoThere(group, there, speed, formation, delay)\
    if not delay then delay = 0 end \
    if type(group) == 'string' then -- group name\
        group = Group.getByName(group)\
    end\
\
    if not Group.isExist(group) then \
        trigger.action.outText(\"cmdr: makeGroupGoThere() - group does not exist\", 30)\
        return \
    end \
\
    -- check that we can get a location for the group \
    local here = dcsCommon.getGroupLocation(group)\
    if not here then \
        return \
    end \
    \
    local wp = cfxCommander.createWPListForGroupToPoint(group, there, speed, formation)\
    \
    cfxCommander.assignWPListToGroup(group, wp, delay)\
end\
\
function cfxCommander.calculatePathLength(roadPoints)\
    local totalLen = 0\
    if #roadPoints < 2 then return 0 end\
    for i=1, #roadPoints-1 do\
        totalLen = totalLen + dcsCommon.dist(roadPoints[i], roadPoints[i+1])\
    end\
    return totalLen\
end\
\
-- make ground units go from here (group location) to there, using roads if possible\
function cfxCommander.createWPListForGroupToPointViaRoads(group, point, speed)\
    if type(group) == 'string' then -- group name\
        group = Group.getByName(group)\
    end\
\
    local wpList = {}\
    -- here we are, and we want to go there. In DCS, this means that\
    -- we need to create a wp list consisting of here and there\
    -- when going via roads, we add to more wayoints:\
    -- go on-roads and leaveRoads. \
    -- only if we can get these two additional points, we do that, else we \
    -- fall back to direct route \
    \
    local here = dcsCommon.getGroupLocation(group)\
\
    -- now generate a list of all points from here to there that uses roads\
    local rawRoadPoints = land.findPathOnRoads('roads', here.x, here.z, point.x, point.z)\
    -- this is the entire path. calculate the length and make \
    -- sure that path on-road isn't more than twice as long \
    -- that can happen if a bridge is out or we need to go around a hill\
    if not rawRoadPoints or #rawRoadPoints<3 then \
        trigger.action.outText(\"+++ no roads leading there. Taking direct approach\", 30)\
        return cfxCommander.createWPListForGroupToPoint(group, point, speed)\
    end\
    \
    local pathLength = cfxCommander.calculatePathLength(rawRoadPoints)\
    local direct = dcsCommon.dist(here, point)\
    if pathLength < direct and cfxCommander.verbose then \
        trigger.action.outText(\"+++dcsC: WARNING road path (\" .. pathLength .. \") shorter than direct route(\" .. direct .. \"), will not path correctly\", 30)\
    end\
    \
    if pathLength > (2 * direct) then \
        -- road takes too long, take direct approach\
        --trigger.action.outText(\"+++ road path (\" .. pathLength .. \") > twice direct route(\" .. direct .. \"), commencing direct off-road\", 30)\
        return cfxCommander.createWPListForGroupToPoint(group, point, speed)\
    end\
    \
    --trigger.action.outText(\"+++ \".. group:getName() .. \": choosing road path l=\" .. pathLength .. \" over direct route d=\" .. direct, 30)\
    \
    -- if we are here, the road trip is valid \
    for idx, wp in pairs(rawRoadPoints) do \
        -- createBasic... supports w.xy format\
        local theNewWP = cfxCommander.createBasicWaypoint(wp, speed, \"On Road\") -- force off road for better compatibility?\
        table.insert(wpList, theNewWP)\
    end\
    \
    \
    \
    -- now make first and last entry OFF Road\
    local wpc = wpList[1]\
    wpc.action = \"Off Road\"\
    wpc = wpList[#wpList]\
    wpc.action = \"Off Road\"\
\
    return wpList\
end\
\
function cfxCommander.makeGroupGoTherePreferringRoads(group, there, speed, delay)\
    if type(group) == 'string' then -- group name\
        group = Group.getByName(group)\
    end\
    if not delay then delay = 0 end \
\
\
    if cfxCommander.noRoadsAtAll then \
        -- we don't even follow roads, completely forced off\
        cfxCommander.makeGroupGoThere(group, there, speed, \"Off Road\", delay)\
        return \
    end\
\
    -- see if we have an override situation \
    -- for one of the two points where a pathing Zone \
    -- overrides the roads setting \
    if #cfxCommander.pathZones > 0 then  \
        local here = dcsCommon.getGroupLocation(group)\
        local oRide = cfxCommander.hasPathZoneFor(here, there)\
        if oRide and oRide.pathing == \"offroad\" then \
            -- yup, override road preference\
            cfxCommander.makeGroupGoThere(group, there, speed, \"Off Road\", delay)\
            return \
        end\
    end\
\
    -- viaRoads will only use roads if the road trip isn't more than twice \
    -- as long as the direct route \
    local wp = cfxCommander.createWPListForGroupToPointViaRoads(group, there, speed)\
    cfxCommander.assignWPListToGroup(group, wp, delay)\
end\
\
\
function cfxCommander.makeGroupHalt(group, delay)\
    if not group then return end \
    if not Group.isExist(group) then return end \
    if not delay then delay = 0 end \
    local theTask = {id = 'Hold', params = {}}\
    cfxCommander.scheduleTaskForGroup(group, theTask, delay)\
end\
\
function cfxCommander.makeGroupTransmit(group, tenKHz, filename, oneShot, delay)\
    if not group then return end \
    if not tenKHz then tenKHz = 20 end -- default to 200KHz\
    if not delay then delay = 1.0 end \
    if not filename then return end \
    if not oneShot then oneShot = false end \
    \
    -- now build the transmission command\
    local theCommands = cfxCommander.createCommandDataTableFor(group)\
    local cmd = cfxCommander.createSetFrequencyCommand(tenKHz) -- freq in 10000 Hz\
    cfxCommander.addCommand(theCommands, cmd)\
    cmd = cfxCommander.createTransmissionCommand(filename, oneShot)\
    cfxCommander.addCommand(theCommands, cmd)\
    cfxCommander.scheduleCommands(theCommands, delay)\
end \
\
function cfxCommander.makeGroupStopTransmitting(group, delay)\
    if not delay then delay = 1 end \
    if not group then return end \
    local theCommands = cfxCommander.createCommandDataTableFor(group)\
    local cmd = cfxCommander.createStopTransmissionCommand()\
    cfxCommander.addCommand(theCommands, cmd)\
    cfxCommander.scheduleCommands(theCommands, delay)\
end\
\
\
function cfxCommander.start()\
    -- make sure we have loaded all relevant libraries \
    if not dcsCommon.libCheck(\"cfx Commander\", cfxCommander.requiredLibs) then \
        trigger.action.outText(\"cf/x Commander aborted: missing libraries\", 30)\
        return false \
    end\
    \
    -- identify and process all 'pathing' zones\
    local pathZones = cfxZones.getZonesWithAttributeNamed(\"pathing\")\
    \
    -- now create a spawner for all, add them to the spawner updater, and spawn for all zones that are not\
    -- paused \
    for k, aZone in pairs(pathZones) do \
        cfxCommander.processPathingZone(aZone) -- process attribute and add to zone\
        cfxCommander.addPathingZone(aZone) -- remember it so we can smoke it\
    end\
    \
    -- read config overides \
    cfxCommander.readConfigZone()\
    \
    return true\
end\
\
if cfxCommander.start() then \
    trigger.action.outText(\"cfxCommander v\" .. cfxCommander.version .. \" loaded\", 30)\
else \
    trigger.action.outText(\"+++cfxCommander load FAILED\", 30)\
    cfxCommander = nil\
end\
\
--[[-- known issues\
\
- troops remain motionless until all are repaired or produced after cature\
- long roads / roads not taken in persia \
- all troops red and blue become motionless when one zone is occupied\
- after capture, the troop capturing remains, all others can go on. one will always remain there \
- rethink the factor to add to road, and simply add 100m \
\
 TODO: break long distances into smaller paths, and gravitate towards pathing zones if they have a 'gravitate' or similar attribute \
--]]--\
",
                    ["predicate"] = "a_do_script",
                }, -- end of [3]
                [4] = 
                {
                    ["text"] = "cfxGroundTroops = {}\
cfxGroundTroops.version = \"2.1.0\"\
cfxGroundTroops.ups = 1\
cfxGroundTroops.verbose = false \
cfxGroundTroops.requiredLibs = {\
    \"dcsCommon\", -- common is of course needed for everything\
                 -- pretty stupid to check for this since we \
                 -- need common to invoke the check, but anyway\
    \"cfxCommander\", -- generic data module for weight \
    -- cfxOwnedZones is optional \
}\
-- ground troops: a module to manage ground toops. makes groups of ground troops\
-- patrol and engage enemies and signal idle\
-- understands cfxOwnedZones orders 'attackOwnedZone' and will re-direct\
-- troops when a zone was captured by interacting with cfxOwnedZones to \
-- find the nearest non-owned zone and direct the group there \
\
-- USAGE\
-- Allocate a group in game and issue them marching orders towars a goal \
-- then createGroundTroops to allocate a structure used by this \
-- module and addTroopsToPool to have them then managed by this \
-- module \
\
cfxGroundTroops.deployedTroops = {} -- indexed by group name \
cfxGroundTroops.jtacCB = {} -- jtac callbacks, to be implemented \
\
--[[--\
 version history\
\
   2.0.0 - dmlZones \
         - jtacSound \
         - cleanup \
         - jtacVerbose \
   2.0.1 - small fiex ti checkPileUp()\
   2.1.0 - captureandhold - oneshot attackowned \
   \
\
  an entry into the deployed troop table has the following attributes\
  - group - the group \
  - orders: \"guard\" - will guard the spot and look for enemies in range\
            \"patrol\" - will walk between way points back and forth \
            \"laze\" - will stay in place and try to laze visible vehicles in range\
            \"attackOwnedZone\" - interface to cfxOwnedZones module, seeks out\
              enemy zones to attack and capture them\
            \"captureandhold\" - interface to ownedZones, seeks out nearest enemy \
              or neutral owned zone. once captured, it stays there \
            \"wait-<some other orders>\" do nothing. the \"wait\" prefix will be removed some time and <some other order> then revealed. Used at least by heloTroops\
            \"train\" - target dummies. ROE=HOLD, no ground loop \
            \"attack\" - transition to destination, once there, stop and \
            switch to guard. requires destination zone be set to a valid cfxZone\
  - coalition - the coalition from the group\
  - enemy - if set, the group this group it is engaging. this means the group is fighting and not idle\
  - name - name of group, dan be freely changed\
  - signature - \"cfx\" to tell apart from dcs groups \
  - range = range to look for enemies. default is 300m. In \"laze\" orders, range to laze\
  - lazeTarget - target currently lazing\
  - lazeCode - laser code. default is 1688\
  - moving - has been given orders to move somewhere already. used for first movement order with attack orders \
\
 \
 usage:\
 take a dcs group of ground troops and create a cfx ground troop record with \
  createGroundTroops()\
 then add this to the manager with \
  addGroundTroopsToPool()\
 \
 you can control what the group is to do by changing the cfx troop attribute orders \
 you can install a callback that will notify you if a troop reached a goal or\
 was killed with addTroopsCallback() which will also give a reason\
 callback pattern is myCallback(reason, theGroup, orders, data) with troop being the \
 group, and orders the original orders, and reason a string containing why the \
 callback was invoked. Currently defined reasons are\
   - \"dead\" - entire group was killed \
   - \"arrived\" - at least a part of group arrived at destination (only with some orders)\
--]]--\
\
--\
-- UPDATE MODELS\
-- standard is update all every time: fastest, but may cause \
-- performance issues\
-- queued will work one every pass (except for lazed), distributing the load much better \
-- schedueld installs a callback for each group separately and thus distributes the load over time much better \
\
function cfxGroundTroops.invokeCallbacks(ID, jtac, tgt, data)\
    -- IS is aqui, lost, dead, jtac died. jtac is group, tgt is unit, data is rest \
    for idx, cb in pairs(cfxGroundTroops.jtacCB) do \
        cb(ID, jtac, tgt, data)\
    end\
end\
\
function cfxGroundTroops.addJtacCB(theCB)\
    table.insert(cfxGroundTroops.jtacCB, theCB)\
end\
\
cfxGroundTroops.troopQueue = {} -- FIFO stack \
-- return the best tracking interval for this type of orders \
\
function cfxGroundTroops.readConfigZone()\
    local theZone = cfxZones.getZoneByName(\"groundTroopsConfig\") \
    if not theZone then \
        theZone = cfxZones.createSimpleZone(\"groundTroopsConfig\") \
    end \
        \
    cfxGroundTroops.queuedUpdates = theZone:getBoolFromZoneProperty(\"queuedUpdates\", false)\
    cfxGroundTroops.scheduledUpdates = theZone:getBoolFromZoneProperty(\"scheduledUpdates\", false)\
    cfxGroundTroops.maxManagedTroops = theZone:getNumberFromZoneProperty(\"maxManagedTroops\", 67)\
    cfxGroundTroops.monitorNumbers = theZone:getBoolFromZoneProperty(\"monitorNumbers\", false)\
    cfxGroundTroops.standardScheduleInterval = theZone:getNumberFromZoneProperty(\"standardScheduleInterval\", 30)    \
    cfxGroundTroops.guardUpdateInterval = theZone:getNumberFromZoneProperty(\"guardUpdateInterval\", 30)\
    cfxGroundTroops.trackingUpdateInterval = theZone:getNumberFromZoneProperty(\"trackingUpdateInterval\", 0.5)\
\
    cfxGroundTroops.jtacSound = theZone:getStringFromZoneProperty(\"jtacSound\", \"UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav\")\
    cfxGroundTroops.jtacVerbose = theZone:getBoolFromZoneProperty(\"jtacVerbose\", true)\
    cfxGroundTroops.laseCode = theZone:getNumberFromZoneProperty(\"jtacLaserCode\", 1688)\
    if theZone:hasProperty(\"lazeCode\") then \
        cfxGroundTroops.laseCode = theZone:getNumberFromZoneProperty(\"lazeCode\", 1688)\
    end \
    if theZone:hasProperty(\"laseCode\") then \
        cfxGroundTroops.laseCode = theZone:getNumberFromZoneProperty(\"laseCode\", 1688)\
    end \
    if theZone:hasProperty(\"laserCode\") then \
        cfxGroundTroops.laseCode = theZone:getNumberFromZoneProperty(\"laserCode\", 1688)\
    end \
    cfxGroundTroops.verbose = theZone.verbose \
\
    if cfxGroundTroops.verbose then \
        trigger.action.outText(\"+++gndT: read config zone!\", 30) \
    end\
end\
\
\
-- \
-- Callback handling\
--\
\
cfxGroundTroops.troopsCallback = {}\
\
function cfxGroundTroops.addTroopsCallback(theCallback)\
    table.insert(cfxGroundTroops.troopsCallback, theCallback)\
end\
\
function cfxGroundTroops.invokeCallbacksFor(reason, troops, data)\
    if not data then data = {} end\
    data.troops = troops \
    for idx, theCB in pairs (cfxGroundTroops.troopsCallback) do \
        theCB(reason, troops.group, troops.orders, data)\
    end\
end\
\
function cfxGroundTroops.getScheduleInterval(orders)\
    if orders == \"laze\" then \
        return cfxGroundTroops.trackingUpdateInterval\
    end\
    return cfxGroundTroops.standardScheduleInterval\
end\
\
-- create controller commands to attack a group \"enemies\"\
-- enemies are an attribute of the troop structure\
-- usually called from a group on guard when idling \
function cfxGroundTroops.makeTroopsEngageEnemies(troop)\
    local group = troop.group\
    if not Group.isExist(group) then \
        trigger.action.outText(\"+++gndT: troup don't exist, dropping\", 30)\
        return \
    end\
    \
    local enemies = troop.enemy\
    local from = dcsCommon.getGroupLocation(group)\
    if not from then return end -- the commandos died\
    local there = dcsCommon.getGroupLocation(enemies)\
    if not there then return end\
    \
    -- we lerp to 2/3 of enemy location\
    there = dcsCommon.vLerp(from, there, 0.66) \
    \
    local speed = 10 -- m/s = 10 km/h -- wait. 10 m/s is 36 km/h \
    cfxCommander.makeGroupGoThere(group, there, speed)\
    local attask = cfxCommander.createAttackGroupCommand(enemies)\
    cfxCommander.scheduleTaskForGroup(group, attask, 0.5)\
    troop.moving = true \
end\
\
-- make the troops engage a cfxZone passed in the destination \
-- attribute \
function cfxGroundTroops.makeTroopsEngageZone(troop)\
    local group = troop.group\
    if not group:isExist() then \
        trigger.action.outText(\"+++gndT: make troops engage zone: troops do not exist, exiting\", 30)\
        return \
    end\
    \
    local enemyZone = troop.destination -- must be cfxZone \
    local from = dcsCommon.getGroupLocation(group)\
    if not from then return end -- the group died\
    local there = enemyZone.point -- access zone position\
    if not there then return end\
        \
    local speed = 14 -- m/s; 10 m/s = 36 km/h\
    \
    -- make troops stop in 1 second, then start in 5 seconds to give AI respite \
    cfxCommander.makeGroupHalt(group, 1) -- 1 second delay\
    cfxCommander.makeGroupGoTherePreferringRoads(group, there, speed, 5)\
\
    -- remember that we have issued a move order \
    troop.moving = true     \
end\
\
function cfxGroundTroops.switchToOffroad(troops)\
    -- we may need to test if we already did this, \
    -- but not for now \
    \
    -- this is called when troops are stuck \
    -- on their route for longer than allowed\
    -- we now force a direct approach \
    local group = troops.group\
    if not group:isExist() then \
        return\
    end \
    \
    local enemies = troops.destination\
    local from = dcsCommon.getGroupLocation(group)\
    if not from then return end -- the commandos died\
    local there = enemies.point\
    if not there then return end\
        \
    local speed = 14 -- m/s; 10 m/s = 36 km/h\
    \
    cfxCommander.makeGroupHalt(group, 0) -- no delay, halt now\
    cfxCommander.makeGroupGoThere(group, there, speed, \"Off Road\", 5)\
    \
    troops.lastOrderDate = timer.getTime()\
    troops.speedWarning = 0\
end\
\
--\
-- update loop for troops that have 'attackOwnedZones' as \
-- their orders\
-- if they have no destination zone, or the zone they are \
-- are heading for is already owned by their side, then look for \
-- the closest enemy zone, and cut attack orders to move there \
function cfxGroundTroops.getClosestEnemyZone(troop)\
    local p = dcsCommon.getGroupLocation(troop.group)\
    local tempZone = cfxZones.createSimpleZone(\"tz\", p, 100)\
    tempZone.owner = troop.side\
    local newTarget = cfxOwnedZones.getNearestEnemyOwnedZone(tempZone, true) -- 'true' will also target neutral zones \
    return newTarget\
end\
\
function cfxGroundTroops.updateZoneAttackers(troop)\
    if not troop then return end \
    if not cfxOwnedZones then \
        trigger.action.outText(\"+++gndT: update zone attackers requires ownedZones\", 30)\
        return \
    end\
    troop.insideDestination = false -- mark as not inside \
    \
    local newTargetZone = cfxGroundTroops.getClosestEnemyZone(troop)\
    if not newTargetZone then\
        -- all target zones are friendly, go to guard mode\
        troop.orders = \"guard\"\
        return \
    end\
    \
    if newTargetZone ~= troop.destination then \
        if troop.destination and troop.orders == \"captureandhold\" then \
            troop.lastOrderDate = timer.getTime() -- we may even dismiss them \
            -- from troop array. But orders should remain when picked up by helo \
            -- we never change target. Stay.\
            return \
        end \
        troop.destination = newTargetZone \
        cfxGroundTroops.makeTroopsEngageZone(troop)\
        troop.lastOrderDate = timer.getTime()\
        troop.speedWarning = 0\
        return\
    end\
    \
    -- if we get here, we should be under way to our nearest enemy zone\
    if not troop.moving then \
        cfxGroundTroops.makeTroopsEngageZone(troop)\
        return \
    end \
    \
    -- if we get here, we are under way to troop.destination\
    -- check if we are inside the zone, and if so, set variable to true \
    local p = dcsCommon.getGroupLocation(troop.group)\
    troop.insideDestination = cfxZones.isPointInsideZone(p, troop.destination)\
    \
    -- if we get here, we need no change \
    \
end\
\
-- attackers simply travel to their destination (zone), and then switch to \
-- guard orders once they arrive \
function cfxGroundTroops.updateAttackers(troop) \
    if not troop then return end \
    if not troop.destination then return end \
    if not troop.group:isExist() then return end \
    \
    -- if we are not moving, we need to issue move oders now\
    -- this can happen if previously, there was a 'wait' command \
    -- and this now was removed so we end up in the method \
    if not troop.moving then \
        cfxGroundTroops.makeTroopsEngageZone(troop)\
        return \
    end \
    \
    \
    if cfxZones.isGroupPartiallyInZone(troop.group, troop.destination) then\
        -- we have arrived\
        -- we could now also initiate a general callback with reason\
        cfxGroundTroops.invokeCallbacksFor(\"arrived\", troop)\
        troop.orders = \"guard\"\
        return \
    end\
        \
--    if we get here, we need no change \
end\
\
-- update loop for a group that has \"guard\" orders.\
-- basically it stands around and looks for enemies \
-- until it finds a group, and then engages the enemy\
-- when engaged, it is not looking for other enemies\
-- 'engaged' means that the troop.enemy attribute is set\
 \
function cfxGroundTroops.updateGuards(troop)\
    if not troop.group:isExist() then \
        return \
    end \
    \
    local theEnemy = troop.enemy\
    if theEnemy then \
        -- see if enemy is dead \
        if not dcsCommon.isGroupAlive(theEnemy) then \
            troop.enemy = nil\
            -- yup, zed's dead. next time around, we won't be checking this again\
            trigger.action.outText(troop.name .. \" has neutralized enemy forces\", 30)\
            --DONE: invoke callback for defeating troops\
            local data = {}\
            data.enemy = theEnemy\
            cfxGroundTroops.invokeCallbacksFor(\"neutralized\", troop, data)\
            return\
        end\
        -- yes, we are still engaged\
        return \
    end\
    \
    -- we are currently unengaged. look for an enemy\
    if not troop.range then troop.range = 300 end\
    troop.coalition = troop.group:getCoalition()\
    local enemyCoal = dcsCommon.getEnemyCoalitionFor(troop.coalition)\
    local cat = Group.Category.GROUND\
    local p = dcsCommon.getGroupLocation(troop.group)\
    local enemies, enemyDist = dcsCommon.getClosestLivingGroupToPoint(p, enemyCoal, cat) \
    local maxRange = troop.range -- meters \
    -- if we have enemies then schedule a path to go there\
    if enemies and (enemyDist < maxRange) then \
        troop.enemy = enemies\
        --timer.scheduleFunction(cfxGroundTroops.makeGroupEngageEnemies, troop, timer.getTime() + 1.0)\
        cfxGroundTroops.makeTroopsEngageEnemies(troop)\
        trigger.action.outText(troop.name .. \" is engaging enemy forces at range \" .. math.floor(enemyDist) .. \"meters\", 30)\
        --DONE: invoke callback for engaging troops, pass data \
        local data = {}\
        data.enemy = enemies\
        cfxGroundTroops.invokeCallbacksFor(\"engaging\", troop, data)\
    elseif enemies then \
        --trigger.action.outText(troop.name .. \" enemiy out of range: \" .. math.floor(enemyDist) .. \"meters\", 30)\
    else \
        --trigger.action.outText(troop.name .. \" no enemies\", 30)\
    end\
end\
\
--\
-- update loop for units that laze targets.\
-- they can only laze if they are alive, but update \
-- will take care of that, so when we are here, there \
-- is at least one of them alive\
-- \
\
function cfxGroundTroops.findLazeTarget(troop)\
    local here = troop.group:getUnit(1):getPoint()\
    troop.coalition = troop.group:getCoalition()\
    local enemyCoal = dcsCommon.getEnemyCoalitionFor(troop.coalition)\
    --local enemySide = dcsCommon.getEnemyCoalitionFor(troop.side)\
    local cat = Group.Category.GROUND\
    local enemyGroups = dcsCommon.getLivingGroupsAndDistInRangeToPoint(here, troop.range, enemyCoal, cat) \
    -- we now have a list of possible targets in range\
    if #enemyGroups < 1 then     \
        -- no targets in range\
        return nil \
    end\
\
    here = {x = here.x, y = here.y + 2.0, z = here.z} -- raise by 2.0m\
    \
    -- iterate through the list until we find the first target \
    -- that fits the bill and return it\
\
    for i=1, #enemyGroups do    \
        -- get all units for this group \
        local aGroup = enemyGroups[i].group -- remember, they are in a {dist, group} tuple\
        local theUnits = aGroup:getUnits()\
        -- iterate all units \
        for udx, aUnit in pairs(theUnits) do \
            if (aUnit:isExist() and aUnit:getLife() > 1) then \
                -- unit lives \
                -- now, we need to filter infantry. we do this by \
                -- pre-fetching the typeString\
                -- and checking if the name contains some infantry-\
                -- typical strings. Idea taken from JTAC script \
                local isInfantry =  dcsCommon.unitIsInfantry(theUnit)\
                if not isInfantry then \
                    -- this is a vehicle, is it in line of sight?\
                    -- raise the point 2m above ground for both points\
                    -- as done in jtac script\
                    local there = aUnit:getPoint()  \
                    there = {x = there.x, y = there.y + 2.0, z = there.z}\
                   \
                    if land.isVisible(here, there) then \
                        -- we found a visible vehicle in \
                        -- the nearest group to us in range \
                        -- that is visible!\
                        return aUnit\
                    end -- if visible\
                end -- if infantry \
            end -- if alive \
        end -- for all units\
    end -- for all enemy groups\
    return nil -- no unit found \
end\
\
function cfxGroundTroops.lazerOff(troop)\
    if troop.lazerPointer then \
        troop.lazerPointer:destroy()\
    end\
    troop.lazerPointer = nil \
    troop.lazingUnit = nil \
end\
\
function cfxGroundTroops.trackLazer(troop)\
    -- the only thing that must be set when entering here is\
    -- lazeTarget. We set up the rest\
    if not troop.lazingUnit then \
        troop.lazingUnit = troop.group:getUnit(1) -- get first unit\
        if troop.lazingUnit:getLife() < 1 then \
            trigger.action.outText(\"+++ LazingUnit is dead, getUnit works differently from what docs say, need to filter for lively units\", 30)\
        end\
    end\
    \
    if not troop.lazerPointer then\
        local there = troop.lazeTarget:getPoint()\
        troop.lazerPointer = Spot.createLaser(troop.lazingUnit,{x = 0, y = 2, z = 0}, there, cfxGroundTroops.laseCode)\
        troop.lazeTargetType = troop.lazeTarget:getTypeName()\
        if cfxGroundTroops.jtacVerbose then \
            trigger.action.outTextForCoalition(troop.side, troop.name .. \" tally target - lasing \" .. troop.lazeTargetType .. \", code \" .. cfxGroundTroops.laseCode .. \"!\", 30)\
            trigger.action.outSoundForCoalition(troop.side, cfxGroundTroops.jtacSound) -- \"UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav\")\
        end \
        troop.lastLazerSpot = there -- remember last spot\
        local data = {}\
        data.enemy = troop.lazeTarget\
        data.tracker = troop.lazingUnit\
        cfxGroundTroops.invokeCallbacksFor(\"lase:tracking\", troop, data)\
        return\
    end\
        \
    -- if we get here, we update the lazerPointer\
    local there = troop.lazeTarget:getPoint()\
    -- we may only want to update the laser spot when dist > trigger\
    troop.lazerPointer:setPoint(there)\
    -- we may want to report dist\
    troop.lastLazerSpot = there\
end\
\
function cfxGroundTroops.updateLaze(troop)\
    -- check if we have a laze target. \
    -- check if lazing unit was killed, and therefore lost target\
    if troop.lazingUnit then \
        -- check that unit still alive\
        if troop.lazingUnit:isExist() and \
        troop.lazingUnit:getLife() >= 1 then\
        else \
            cfxGroundTroops.lazerOff(troop)\
            troop.lazeTarget = nil\
            if cfxGroundTroops.jtacVerbose then \
                trigger.action.outTextForCoalition(troop.side, troop.name .. \" reports lasing \" .. troop.lazeTargetType .. \" interrupted. Re-acquiring.\", 30)\
                trigger.action.outSoundForCoalition(troop.side, cfxGroundTroops.jtacSound) -- \"UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav\")\
            end \
            troop.lazingUnit = nil \
            cfxGroundTroops.invokeCallbacksFor(\"lase:stop\", troop)\
            return -- we'll re-acquire through a new unit next round\
        end\
    end\
    \
    -- if we get here, a lazing unit     \
    if troop.lazeTarget then \
        -- check if that target is alive and in range\
        if troop.lazeTarget:isExist() and troop.lazeTarget:getLife() >= 1 then\
            -- note: when we laze a target, we know that we have a lazing unit\
            local here = troop.lazingUnit:getPoint()\
            -- check if it has moved out of range \
            local there = troop.lazeTarget:getPoint()\
            if dcsCommon.dist(here, there) > troop.range then \
                -- troop out of range\
                if cfxGroundTroops.jtacVerbose then \
                    trigger.action.outTextForCoalition(troop.side, troop.name .. \" lost sight of lazed target \" .. troop.lazeTargetType, 30)\
                    trigger.action.outSoundForCoalition(troop.side, cfxGroundTroops.jtacSound) -- \"UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav\")\
                end \
                troop.lazeTarget = nil\
                cfxGroundTroops.lazerOff(troop)\
                troop.lazingUnit = nil\
                cfxGroundTroops.invokeCallbacksFor(\"lase:stop\", troop)\
                return \
            end\
            \
            -- if we get here, we need to update the target point \
            cfxGroundTroops.trackLazer(troop)\
            return\
        else\
            -- target died\
            if cfxGroundTroops.jtacVerbose then \
                trigger.action.outTextForCoalition(troop.side, troop.name .. \" confirms kill for \" .. troop.lazeTargetType, 30)\
                trigger.action.outSoundForCoalition(troop.side, cfxGroundTroops.jtacSound) -- \"UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav\")\
            end \
            troop.lazeTarget = nil\
            cfxGroundTroops.lazerOff(troop)\
            troop.lazingUnit = nil\
            cfxGroundTroops.invokeCallbacksFor(\"lase:stop\", troop)\
            return\
        end        \
    end\
    \
    -- if we get here, we must look for a laze target \
    troop.lazeTarget = cfxGroundTroops.findLazeTarget(troop)\
    if troop.lazeTarget then \
        cfxGroundTroops.trackLazer(troop) -- will also set up lazing unit \
    end\
end\
\
\
function cfxGroundTroops.updateWait(troop)\
    -- currently nothing to do    \
end\
\
function cfxGroundTroops.updateTroops(troop)\
    -- if orders start with \"wait-\" then the troops \
    -- simply do nothing\
    if dcsCommon.stringStartsWith(troop.orders, \"wait-\") then\
        -- the troops are waiting to be picked update\
        -- when they are dropped again, thre prefix to \
        -- their order is removed, and the 'real' orders \
        -- are revealed. For now, do nothing\
        cfxGroundTroops.updateWait(troop)\
    --REMEMBER: LOWER CASE ONLY!\
    elseif troop.orders == \"guard\" then \
        cfxGroundTroops.updateGuards(troop)\
    \
    elseif troop.orders == \"attackownedzone\" then \
        cfxGroundTroops.updateZoneAttackers(troop)\
\
    elseif troop.orders == \"captureandhold\" then \
        cfxGroundTroops.updateZoneAttackers(troop) \
        \
    elseif troop.orders == \"laze\" then \
        cfxGroundTroops.updateLaze(troop)\
    \
    elseif troop.orders == \"attackzone\" then \
        cfxGroundTroops.updateAttackers(troop)\
        \
    else \
        trigger.action.outText(\"+++ updated troops \" .. troop.name .. \" have unknown orders \" .. troop.orders, 30)\
    end\
    \
end\
\
--\
-- all at once \
--\
function cfxGroundTroops.update()\
    cfxGroundTroops.updateSchedule = timer.scheduleFunction(cfxGroundTroops.update, {}, timer.getTime() + 1/cfxGroundTroops.ups)\
    -- iterate all my troops and build next \
    -- versions pool\
    local liveTroops = {} -- filtered table, indexed by name \
    for idx, troop in pairs(cfxGroundTroops.deployedTroops) do \
        local group = troop.group \
        if not dcsCommon.isGroupAlive(group) then \
            -- group dead. remove from pool\
            -- this happens by not copying it into the poos\
            cfxGroundTroops.invokeCallbacksFor(\"dead\", troop) -- notify anyone who is interested that we are no longer proccing these \
        else \
            -- work with this groop according to its orders\
            cfxGroundTroops.updateTroops(troop)\
            -- since group is alive remember it for next loop\
            liveTroops[idx] = troop -- do NOT use insert as we have indexed table by name\
        end\
    end\
    -- liveTroops holds all troops that are still alive and will\
    -- be revisited next loop\
    cfxGroundTroops.deployedTroops = liveTroops\
end\
\
--\
-- UpdateQueued looks for the first unordered (.receivedOrders == false) group\
-- and processes them. if orders are 'laze', it will always be ordered \
--\
\
\
function cfxGroundTroops.updateQueued()\
    cfxGroundTroops.updateSchedule = timer.scheduleFunction(cfxGroundTroops.updateQueued, {}, timer.getTime() + 1/cfxGroundTroops.ups)\
    -- iterate all my troops and build next \
    -- versions pool\
    local liveTroops = {}\
    local hasOrdered = false -- so far, no orders have been given \
    for idx, troop in pairs(cfxGroundTroops.deployedTroops) do \
        local group = troop.group \
        if not dcsCommon.isGroupAlive(group) then \
            -- group dead. remove from pool\
            -- this happens by not copying it to liveTroops \
            -- trigger.action.outText(\"+++ removing ground troops \" .. troop.name, 30)\
            cfxGroundTroops.invokeCallbacksFor(\"dead\", troop) -- notify anyone who is interested that we are no longer proccing these \
        else \
            -- check if this is a lazer \
            if troop.orders == \"laze\" then \
                -- lazers are updated each turn \
                cfxGroundTroops.updateLaze(troop)\
            else \
                if not hasOrdered and not (troop.receivedOrders) then \
                -- work with this groop according to its orders\
                cfxGroundTroops.updateTroops(troop)\
                troop.receivedOrders = true -- this one has received orders \
                hasOrdered = true \
                end \
            end\
            liveTroops[idx] = troop -- do NOT use insert as we have indexed table\
        end\
    end\
    -- liveTroops holds all troops that are still alive and will\
    -- be revisited next loop\
    cfxGroundTroops.deployedTroops = liveTroops\
    \
    -- if no orders have been passed, clear all troop's .receivedOrders flag \
    -- and the loop starts anew next loop \
    if not hasOrdered then \
        for idx, troop in pairs(cfxGroundTroops.deployedTroops) do\
            troop.receivedOrders = nil  \
        end\
    end\
end\
\
--\
-- in updateCheckOnly we simply check the ground queue \
-- if there are troops added that need scheduling (i.e. have \
-- been passed in by addTroops and schedule them \
--\
function cfxGroundTroops.updateCheckOnly()\
    -- re-schedule myself in 1 second \
    timer.scheduleFunction(cfxGroundTroops.updateCheckOnly, {}, timer.getTime() + 1)\
    \
    -- iterate through all troops, and \
    -- see if there are any that have not been scheduled \
    -- to schedule them for updates in 1 second\
    -- that will be the first time that they are scheduled,\
    -- all others will be self-scheduled \
    for idx, troop in pairs(cfxGroundTroops.deployedTroops) do \
        if not troop.hasBeenScheduled then \
            local params = {troop}\
            troop.hasBeenScheduled = true \
            troop.updateID = timer.scheduleFunction(cfxGroundTroops.updateSingleScheduled, params, timer.getTime() + 1)\
            --trigger.action.outText(\"+++groundT: scheduling troops <\".. troop.group:getName() ..\"> with orders <\" .. troop.orders .. \">\", 30)\
        end\
    end\
    -- note that alive checks are now done during the scheduled\
    -- update, not every time for all\
\
end\
\
function cfxGroundTroops.updateSingleScheduled(params)\
    local troops = params[1]\
    troops.updateID = nil -- erase update id \
    if not troops then \
        trigger.action.outText(\"+++groundT WARNING: nil troop in updateSingle\", 30)\
        return -- no further action required, no longer updates\
    end\
    \
    local group = troops.group \
    -- see if we have been taken out of the pool or updated\
    -- if so, exit \
    \
    if not group:isExist() then \
        -- simply never again look at it. \
        return \
    end\
    \
    if cfxGroundTroops.deployedTroops[troops.group:getName()] ~= troops then \
        -- trigger.action.outText(\"+++groundT NOTE: troops <\".. troops.group:getName() ..\"> was removed from pool. Cancel Update\", 30)\
        return -- no further reschedule\
    end\
    \
    -- see if scheduling is turned off\
    if not troops.reschedule then \
        trigger.action.outText(\"+++groundT NOTE: no longer updating <\".. troops.group:getName() ..\"> per reschedule param\", 30)\
        return \
    end\
    \
    -- now, check if still alive \
    if not dcsCommon.isGroupAlive(group) then \
        -- group dead, no longer updates \
        cfxGroundTroops.invokeCallbacksFor(\"dead\", troops) -- notify anyone who is interested that we are no longer proccing these \
        cfxGroundTroops.removeTroopsFromPool(troops)\
        return -- nothing else to do\
    end\
    \
    -- now, execute the update itself, standard update \
    cfxGroundTroops.updateTroops(troops)\
    \
    -- check max speed of group. if < 0.1 then note and increase \
    -- speedWarning. if not, reset speed warning \
    if troops.orders == \"attackownedzone\" and dcsCommon.getGroupMaxSpeed(troops.group) < 0.1 then \
        if not troops.speedWarning then troops.speedWarning = 0 end\
        troops.speedWarning = troops.speedWarning + 1\
    else\
        troops.speedWarning = 0 -- reset\
    end\
    \
    if troops.speedWarning > 5 then -- make me 5\
        lastOrder = timer.getTime() - troops.lastOrderDate \
        -- this may be a matter of too many waypoints. \
        -- maybe issue orders to go to their destination directly?\
        -- now force an order to go directly.\
        if troops.speedWarning > 5 then \
            if troops.isOffroad then \
                -- we already switched to off-road. take me \
                -- out of the managed queue, I'm not going \
                -- anywhere\
                cfxGroundTroops.removeTroopsFromPool(troops)\
            else \
                cfxGroundTroops.switchToOffroad(troops)\
                troops.isOffroad = true -- so we know that we already did that\
            end\
        end \
    end\
    \
    -- now reschedule update for my best time \
    local updateTime = cfxGroundTroops.getScheduleInterval(troops.orders)\
    troops.updateID = timer.scheduleFunction(cfxGroundTroops.updateSingleScheduled, params, timer.getTime() + updateTime)\
end\
\
\
--\
-- PILEUP and TIE BRAKERS\
--\
-- there may come a situation where troops gather in \
-- one zone because the zone isn't won - some other troops \
-- are there and noone moves. \
-- a tie-break is required\
--\
\
-- checkpile up: every so often, we test if we have run into a \
-- pileup-situation. this happens if there are more than n \
-- units with group-attacker order in the same zone, and that \
-- zone is their destination \
-- this can be easily detected by the insideDestination flag \
-- checkPileUp should be run every minute or so \
 \
function cfxGroundTroops.checkPileUp()\
    -- schedule my next call \
    timer.scheduleFunction(cfxGroundTroops.checkPileUp, {}, timer.getTime() + 60)\
    local thePiles = {}\
    if not cfxOwnedZones then \
        return \
    end\
    \
    -- create a list of all piles \
--    for idx, oz in pairs(cfxOwnedZones.zones) do \
    for idx, oz in pairs(cfxOwnedZones.allManagedOwnedZones) do \
        local newPile = {}\
        newPile[1] = 0 -- no red inZone here \
        newPile[2] = 0 -- no blue inZone here \
        newPile.zone = oz -- the zone we are looking at \
        thePiles[oz] = newPile \
    end\
    \
    -- now iterate through all currently alive groups and \
    -- attribute them to their piles \
    for idx, troop in pairs(cfxGroundTroops.deployedTroops) do \
        -- get each group and count them if they are inside\
        -- their destination \
        if troop.insideDestination and troop.group:isExist() then\
            local side = troop.group:getCoalition()\
            local thePile = thePiles[troop.destination]\
            local theSide = troop.group:getCoalition()\
            thePile[theSide] = thePile[theSide] + 1 -- we count groups, not units  \
        end\
    end\
    \
    -- a pileup happens, if there are more than 3 groups in destination zone\
    -- with NO other troops present (usually the case)\
    -- or when there are 5 groups more than the number for the other side \
    -- so now scan all piles\
    for idx, thePile in pairs(thePiles) do \
        -- check red pileup \
        if thePile[1] > 3 and thePile[2] == 0 then \
            -- simple pileup. 3 groups, no others except defenders and \
            -- perhaps transients \
            cfxGroundTroops.breakTie(thePile, 1)\
        elseif thePile[1] >= thePile[2] + 5 then \
            -- numerical pileup \
            cfxGroundTroops.breakTie(thePile, 1)\
        end\
        \
        -- check blue loside \
        if thePile[2] >= 3 and thePile[1] == 0 then \
            -- simple pileup. 3 groups, no others except defenders and \
            -- perhaps transients \
            cfxGroundTroops.breakTie(thePile, 2)\
        elseif thePile[2] >= thePile[1] + 5 then \
            -- numerical pileup \
            cfxGroundTroops.breakTie(thePile, 2)\
        end\
    end\
end\
\
function cfxGroundTroops.breakTie(thePile, winner)\
    trigger.action.outText(\"+++ groundT: TIEBREAK - winner is \" .. winner .. \" in zone \" .. thePile.zone.name .. \": \" .. thePile[1] .. \":\" .. thePile[2] , 30)\
    -- now add some code to do the actual tie breaking: remove all units that \
    -- are inside the zone and who belong to the other side \
    local loser = 1 -- red default \
    local theZone = thePile.zone \
    if winner == 1 then loser = 2 end \
    -- now get all ground groups for the losing side\
    local losingGround = coalition.getGroups(loser, Group.Category.GROUND)\
    for idx, theGroup in pairs(losingGround) do \
        -- if alive, check if inside the zone \
        if theGroup:isExist() and dcsCommon.isGroupAlive(theGroup) then \
            -- make sure it's not a transient\
            if not isDeployedGroundTroop(theGroup) then \
                local p = dcsCommon.getGroupLocation(theGroup) \
                if cfxZones.isPointInsideZone(p, theZone) then \
                    trigger.action.outText(\"+++ groundT: TIEBREAK - destroying group \" .. theGroup:getName() , 30)\
                    -- we delete this group now\
                    theGroup:destroy()\
                end\
            end\
        end\
    end\
end\
\
--\
-- sanity checks for rescheduling\
--\
function cfxGroundTroops.checkSchedules()\
    timer.scheduleFunction(cfxGroundTroops.checkSchedules, {}, timer.getTime() + 10)\
    for idx, troop in pairs(cfxGroundTroops.deployedTroops) do\
        -- check if troop is not scheduled \
        -- if this happens to a group more than a certain times,\
        -- it has somehow dropped out of the reschedule \
        -- plan and needs to be scheduled \
        if troop.updateID == nil then \
            troop.unscheduleCount = troop.unscheduleCount + 1\
            if (troop.unscheduleCount > 1) and troop.group:isExist() then \
                trigger.action.outText(\"+++ groundT: unscheduled group  \" .. troop.group:getName() .. \" cnt=\" .. troop.unscheduleCount , 30)\
            end \
        end\
    end\
end\
\
--\
-- REPORTING \
--\
-- \
-- get a report of troops as string \
-- \
function cfxGroundTroops.getTroopReport(theSide, ignoreInfantry)\
    if not ignoreInfantry then ignoreInfantry = false end \
    local report = \"GROUND FORCES REPORT\"\
    for idx, troop in pairs(cfxGroundTroops.deployedTroops) do \
        if troop.side == theSide and troop.group:isExist() then \
            local unitNum = troop.group:getSize()\
            report = report .. \"\\n\" .. troop.name .. \" (\".. unitNum ..\"): <\" .. troop.orders .. \">\" \
            if troop.orders == \"attackownedzone\" then \
                if troop.destination then     \
                    report = report .. \" move towards \" .. troop.destination.name \
                else \
                    report = report .. \" (selecting destination)\"\
                end\
            end\
        end\
    end\
    report = report .. \"\\n---END REPORT\\n\"\
    return report \
end\
\
--\
-- createGroundTroop\
-- use this to create a cfxGroundTroops from a dcs group\
--\
function cfxGroundTroops.createGroundTroops(inGroup, range, orders) \
    local newTroops = {}\
    if not orders then \
        orders = \"guard\" \
    end\
    if orders:lower() == \"lase\" then \
        orders = \"laze\" -- we use WRONG spelling here, cause we're cool. yeah, right.\
    end\
    newTroops.insideDestination = false\
    newTroops.unscheduleCount = 0 -- will count up as we aren't scheduled\
    newTroops.speedWarning = 0\
    newTroops.isOffroad = false -- if true, we switched to direct orders, not roads, after standstill\
    newTroops.group = inGroup\
    newTroops.orders = orders:lower()\
    newTroops.coalition = inGroup:getCoalition()\
    newTroops.side = newTroops.coalition -- because we'e been using both.\
    newTroops.name = inGroup:getName()\
    newTroops.moving = false -- set to not have received move orders yet \
    newTroops.signature = \"cfx\" -- to verify this is groundTroop group, not dcs groups\
    if not range then range = 300 end\
    newTroops.range = range\
    return newTroops\
end\
\
function cfxGroundTroops.addGroundTroopsToPool(troops) -- troops MUST be a table that I understand, with \
    if not troops then return end\
    if troops.signature ~= \"cfx\" then \
        trigger.action.outText(\"+++ adding ground troops with unsupported troop signature\", 30)\
        return \
    end\
    if not troops.orders then troops.orders = \"guard\" end \
    troops.orders = troops.orders:lower()\
    troops.reschedule = true -- in case we use scheduled update \
    -- we now add to internal array. this is worked on by all \
    -- update meths, on scheduled upadtes, it is only used to \
    -- pick up, and do the initial schedule, after that they \
    -- all re-schedule themselves \
    troops.hasBeenScheduled = false -- so far, no updates \
    -- hasBeenScheduled is used by updateCheckOnly when scheduled \
    -- updates are used. \
    \
    -- now add to actively managed table or queue it if enabled\
    if cfxGroundTroops.maxManagedTroops > 0 and dcsCommon.getSizeOfTable(cfxGroundTroops.deployedTroops) >= cfxGroundTroops.maxManagedTroops then \
        -- we need to queue \
        table.insert(cfxGroundTroops.troopQueue, troops)\
    else\
        -- add to deployed set\
        cfxGroundTroops.deployedTroops[troops.group:getName()] = troops\
    end\
end\
\
function cfxGroundTroops.removeTroopsFromPool(troops)\
    \
    if not troops then return end \
    if troops.signature ~= \"cfx\" then return end\
\
    if not troops.group:isExist() then \
        trigger.action.outText(\"warning: removeFromPool called with inexistant group\", 30)\
        return \
    end\
    \
    if cfxGroundTroops.deployedTroops[troops.group:getName()] then \
        local troop = cfxGroundTroops.deployedTroops[troops.group:getName()]\
        troops.reschedule = false -- so a reschedule wont update any more\
        cfxGroundTroops.deployedTroops[troops.group:getName()] = nil\
        return \
    end\
    \
    -- if we get here, we need to check if perhaps the troops \
    -- are in the queue\
    for i=1, #cfxGroundTroops.troopQueue do \
        if cfxGroundTroops.troopQueue[i] == troops then \
            table.remove(cfxGroundTroops.troopQueue, i)\
            return\
        end\
    end\
end\
\
function isDeployedGroundTroop(aGroup) \
    if not aGroup then return false end \
    -- see if its already managed\
    if cfxGroundTroops.deployedTroops[aGroup:getName()] ~= nil then \
        return true \
    end \
    \
    -- see if it's in the queue \
    for i=1, #cfxGroundTroops.troopQueue do \
        if cfxGroundTroops.troopQueue[i] == troops then \
            return true\
        end\
    end\
    -- if we get here, it's neither managed nor queued\
    return false \
--    return cfxGroundTroops.deployedTroops[aGroup:getName()] ~= nil \
end\
\
function cfxGroundTroops.getGroundTroopsForGroup(aGroup) \
    if not (cfxGroundTroops.deployedTroops[aGroup:getName()]) then\
        -- see if it's queued \
        for i=1, #cfxGroundTroops.troopQueue do \
            local troops = cfxGroundTroops.troopQueue[i]\
            if troops.group == aGroup then \
                return troops\
            end\
        end\
        \
        if cfxGroundTroops.verbose then \
            trigger.action.outText(\"+++gndT - WARNING: cannot find group \" .. aGroup:getName() .. \" for troop retrieval. Known troops are:\", 30)\
        end \
        for k,v in pairs(cfxGroundTroops.deployedTroops) do \
            trigger.action.outText(\"+++ \".. k .. \": has v: \" .. v.name, 30)\
        end\
        return nil\
    end\
    \
    return cfxGroundTroops.deployedTroops[aGroup:getName()]\
end\
\
function cfxGroundTroops.monitorQueues()\
    timer.scheduleFunction(cfxGroundTroops.monitorQueues, {}, timer.getTime() + 5)\
    \
    -- calculate the numbers \
    local num = dcsCommon.getSizeOfTable(cfxGroundTroops.deployedTroops)\
    \
    local msg = \"+++ gndT - Groups Managed: <\" .. num .. \">\"\
    -- display the numbers\
    if cfxGroundTroops.maxManagedTroops > 0 then \
        msg = msg .. \" capped at \" .. cfxGroundTroops.maxManagedTroops .. \", q size is <\" .. #cfxGroundTroops.troopQueue .. \">\"\
    end\
    trigger.action.outText(msg, 30)\
end\
\
\
-- manageQueue: if depth of deployedTroops is below max and we have \
-- items in queue, pop off first one and put in managed table \
-- checked once every 2 seconds \
function cfxGroundTroops.manageQueues() \
    timer.scheduleFunction(cfxGroundTroops.manageQueues, {}, timer.getTime() + 2)\
    if cfxGroundTroops.maxManagedTroops < 1 then return end\
    \
    -- if we get here, we have a limit on managed \
    -- items \
    if #cfxGroundTroops.troopQueue < 1 then return end \
    \
    -- if we here, there are items waiting in the queue\
    while dcsCommon.getSizeOfTable(cfxGroundTroops.deployedTroops) < cfxGroundTroops.maxManagedTroops and #cfxGroundTroops.troopQueue > 0 do \
        -- transfer items from the front to the managed queue \
        local theTroops = cfxGroundTroops.troopQueue[1]\
        table.remove(cfxGroundTroops.troopQueue, 1)\
        if theTroops.group:isExist() then \
            cfxGroundTroops.deployedTroops[theTroops.group:getName()] = theTroops\
        end\
    end\
end\
\
\
function cfxGroundTroops.start()\
    if not dcsCommon.libCheck(\"cfx Ground Troops\",\
                              cfxGroundTroops.requiredLibs)\
    then \
        trigger.action.outText(\"cf/x Ground Troops aborted: missing libraries\", 30)\
        return false \
    end\
    \
    -- read optional config zone \
    cfxGroundTroops.readConfigZone()\
    \
    if cfxGroundTroops.scheduledUpdates then \
        cfxGroundTroops.queuedUpdates = false \
        cfxGroundTroops.updateCheckOnly()\
        cfxGroundTroops.checkSchedules() -- check regularly if all troops have been updated by checking their ID\
    elseif cfxGroundTroops.queuedUpdates then \
        cfxGroundTroops.updateQueued()\
    else     \
        cfxGroundTroops.update()\
    end \
    -- now install a regular pileup check \
    timer.scheduleFunction(cfxGroundTroops.checkPileUp, {}, timer.getTime() + 60) \
    \
    if cfxGroundTroops.monitorNumbers then \
        timer.scheduleFunction(cfxGroundTroops.monitorQueues, {}, timer.getTime() + 5) \
    end\
    \
    if cfxGroundTroops.maxManagedTroops > 0 then\
        timer.scheduleFunction(cfxGroundTroops.manageQueues, {}, timer.getTime() + 1) \
    end \
    \
    trigger.action.outText(\"cf/x Ground Troops v\" .. cfxGroundTroops.version .. \" started\", 30)\
    \
    if not cfxOwnedZones then \
        --trigger.action.outText(\"+++groundT: pileUp - owned zones not yet ready\", 30)\
    end\
    return true \
end\
\
if not cfxGroundTroops.start() then \
    cfxGroundTroops = nil \
    trigger.action.outText(\"cfxGroundTroops aborted load\", 30)\
end\
\
--[[--\
 TO DO \
 \
 - implement 'patrol' orders!!! \
   \
   when ordering a new route, issue a command to stop in 1 second\
 and another with new marching orders in 5 seconds \
 look at setTask() and resetTask() for controller\
 - change group logic to set itself up to 'requestOrders' with group as parameter, so they can decide themselves how quickly they want to be re-tasked\
 \
 - DONE enqueue and dequeue methods with capped ground troops size \
 - named locs have strategic values attached (default = 1), and distance is divided by strat value to get at priority when rerouting \
 \
 - difficulty increase: make enemy troops better by raining their spawned level \
 \
 - check out simple slot block SSB (pre-moose) to see if we can implement slot blocking for downed pilots \
 \
 - new 'wanda' (wander) module to make airports more lively: zone, have individuals/single vehicle wander around. two waypoints (start and stop), that are zones, and whenever they reach one or are at speed 0, they get a new one. may have pause before they go to next. \
 variant on above: selection of zones that are somehow connected, and destinations are made between these for patrolling zone. can force order, loop, and ping-pong. \
--]]--",
                    ["predicate"] = "a_do_script",
                }, -- end of [4]
                [5] = 
                {
                    ["text"] = "cfxSpawnZones = {}\
cfxSpawnZones.version = \"2.0.1\"\
cfxSpawnZones.requiredLibs = {\
    \"dcsCommon\", -- common is of course needed for everything\
                 -- pretty stupid to check for this since we \
                 -- need common to invoke the check, but anyway\
    \"cfxZones\", -- Zones, of course \
    \"cfxCommander\", -- to make troops do stuff\
    \"cfxGroundTroops\", -- for ordering then around \
}\
cfxSpawnZones.ups = 1\
cfxSpawnZones.verbose = false \
\
-- persistence: all groups we ever spawned. \
-- is regularly GC'd\
\
cfxSpawnZones.spawnedGroups = {}\
\
--\
-- Zones that conform with this requirements spawn toops automatically\
--   *** DOES NOT EXTEND ZONES *** LINKED OWNER via masterOwner ***\
-- \
--[[--\
-- version history \
   2.0.0 - dmlZones\
         - moved \"types\" to spawner \
         - baseName defaults to zone name, as it is safe for naming\
         - spawnWithSpawner direct link in spawner to spawnZones\
   2.0.1 - fix in verifySpawnOwnership() when not master zone found \
  --]]--\
  \
cfxSpawnZones.allSpawners = {}\
cfxSpawnZones.callbacks = {} -- signature: cb(reason, group, spawner)\
 \
--\
-- C A L L B A C K S \
-- \
function cfxSpawnZones.addCallback(theCallback)\
    table.insert(cfxSpawnZones.callbacks, theCallback)\
end\
\
function cfxSpawnZones.invokeCallbacksFor(reason, theGroup, theSpawner)\
    for idx, theCB in pairs (cfxSpawnZones.callbacks) do \
        theCB(reason, theGroup, theSpawner)\
    end\
end\
\
\
--\
-- creating a spawner\
--\
function cfxSpawnZones.createSpawner(inZone)\
    local theSpawner = {}\
    theSpawner.zone = inZone\
    theSpawner.name = inZone.name \
    theSpawner.spawnWithSpawner = cfxSpawnZones.spawnWithSpawner\
    -- interface to groupTracker \
    -- WARNING: attaches to ZONE, not spawner object\
    if inZone:hasProperty(\"trackWith:\") then \
        inZone.trackWith = inZone:getStringFromZoneProperty(\"trackWith:\", \"<None>\")\
    end\
    \
    -- interface to delicates \
    if inZone:hasProperty(\"useDelicates\") then \
        theSpawner.delicateName = dcsCommon.trim(inZone:getStringFromZoneProperty(\"useDelicates\", \"<none>\"))\
        if theSpawner.delicateName == \"*\" then theSpawner.delicateName = inZone.name end \
    end\
    \
    -- connect with ME if a trigger flag is given \
    if inZone:hasProperty(\"f?\") then \
        theSpawner.triggerFlag = inZone:getStringFromZoneProperty(\"f?\", \"none\")\
        theSpawner.lastTriggerValue = trigger.misc.getUserFlag(theSpawner.triggerFlag)\
    elseif inZone:hasProperty(\"spawn?\") then \
        theSpawner.triggerFlag = inZone:getStringFromZoneProperty(\"spawn?\", \"none\")\
        theSpawner.lastTriggerValue = trigger.misc.getUserFlag(theSpawner.triggerFlag)\
    elseif inZone:hasProperty(\"spawnUnits?\") then \
        theSpawner.triggerFlag = inZone:getStringFromZoneProperty( \"spawnObject?\", \"none\")\
        theSpawner.lastTriggerValue = trigger.misc.getUserFlag(theSpawner.triggerFlag)\
    end\
    \
    if inZone:hasProperty(\"activate?\") then \
        theSpawner.activateFlag = inZone:getStringFromZoneProperty( \"activate?\", \"none\")\
        theSpawner.lastActivateValue = trigger.misc.getUserFlag(theSpawner.activateFlag)\
    end\
    \
    if inZone:hasProperty(\"pause?\") then \
        theSpawner.pauseFlag = inZone:getStringFromZoneProperty(\"pause?\", \"none\")\
        theSpawner.lastPauseValue = trigger.misc.getUserFlag(theSpawner.pauseFlag)\
    end\
    \
    if inZone:hasProperty(\"types\") then \
        theSpawner.types = inZone:getStringFromZoneProperty(\"types\", \"Soldier M4\")\
    else \
        theSpawner.types = inZone:getStringFromZoneProperty(\"spawner\", \"Soldier M4\")\
    end\
    -- synthesize types * typeMult\
    if inZone:hasProperty(\"typeMult\") then \
        local n = inZone:getNumberFromZoneProperty(\"typeMult\", 1)\
        local repeater = \"\"\
        if n < 1 then n = 1 end \
        while n > 1 do \
            repeater = repeater .. \",\" .. theSpawner.types\
            n = n - 1\
        end\
        theSpawner.types = theSpawner.types .. repeater \
    end\
    \
    theSpawner.country = inZone:getNumberFromZoneProperty(\"country\", 0)\
    if inZone:hasProperty(\"masterOwner\") then \
        theSpawner.masterZoneName = inZone:getStringFromZoneProperty(\"masterOwner\", \"\")\
        if theSpawner.masterZoneName == \"\" then theSpawner.masterZoneName = nil end \
    end \
    \
    theSpawner.rawOwner = coalition.getCountryCoalition(theSpawner.country)\
    -- theSpawner.baseName = inZone:getStringFromZoneProperty(\"baseName\", dcsCommon.uuid(\"SpwnDflt\"))\
    theSpawner.baseName = inZone:getStringFromZoneProperty(\"baseName\", \"*\")\
    theSpawner.baseName = dcsCommon.trim(theSpawner.baseName)\
    if theSpawner.baseName == \"*\" then \
        theSpawner.baseName = inZone.name -- convenience shortcut\
    end\
    \
    theSpawner.cooldown = inZone:getNumberFromZoneProperty(\"cooldown\", 60)\
    theSpawner.autoRemove = inZone:getBoolFromZoneProperty(\"autoRemove\", false)\
    theSpawner.lastSpawnTimeStamp = -10000 -- init so it will always work\
    theSpawner.heading = inZone:getNumberFromZoneProperty(\"heading\", 0)    \
    theSpawner.cdTimer = 0 -- used for cooldown. if timer.getTime < this value, don't spawn\
    theSpawner.cdStarted = false -- used to initiate cooldown when theSpawn disappears\
    theSpawner.count = 1 -- used to create names, and count how many groups created\
    theSpawner.theSpawn = nil -- link to last spawned group\
    theSpawner.formation = inZone:getStringFromZoneProperty(\"formation\", \"circle_out\")\
    theSpawner.paused = inZone:getBoolFromZoneProperty(\"paused\", false)\
    -- orders are always converted to all lower case \
    theSpawner.orders = inZone:getStringFromZoneProperty(\"orders\", \"guard\"):lower() \
    -- used to assign orders, default is 'guard', use \"laze\" to make them laze targets. can be 'wait-' which may auto-convert to 'guard' after pick-up by helo, to be handled outside.\
    -- use \"train\" to tell them to HOLD WEAPONS, don't move and don't participate in loop, so we have in effect target dummies\
    -- can also use order 'dummy' or 'dummies' to switch to train\
    if theSpawner.orders:lower() == \"dummy\" or theSpawner.orders:lower() == \"dummies\" then theSpawner.orders = \"train\" end \
    if theSpawner.orders:lower() == \"training\" then theSpawner.orders = \"train\" end \
    \
    theSpawner.range = inZone:getNumberFromZoneProperty(\"range\", 300) -- if we have a range, for example enemy detection for Lasing or engage range\
    theSpawner.maxSpawns = inZone:getNumberFromZoneProperty(\"maxSpawns\", -1) -- if there is a limit on how many troops can spawn. -1 = endless spawns\
    theSpawner.requestable = inZone:getBoolFromZoneProperty( \"requestable\", false)\
    if theSpawner.requestable then \
        theSpawner.paused = true \
        if inZone.verbose or cfxSpawnZones.verbose then \
            trigger.action.outText(\"+++spwn: spawner <\" .. inZone.name .. \"> paused: requestable enabled\", 30)\
        end\
    end\
    if inZone:hasProperty(\"target\") then \
        theSpawner.target = inZone:getStringFromZoneProperty(\"target\", \"\")\
        if theSpawner.target == \"\" then -- this is the defaut case \
            theSpawner.target = nil \
        end\
    end \
    \
    if cfxSpawnZones.verbose or inZone.verbose then \
        trigger.action.outText(\"+++spwn: created spawner for <\" .. inZone.name .. \">\", 30)\
    end    \
    \
    return theSpawner\
end\
\
function cfxSpawnZones.addSpawner(aSpawner)\
    cfxSpawnZones.allSpawners[aSpawner.zone] = aSpawner\
end\
\
function cfxSpawnZones.removeSpawner(aSpawner)\
    cfxSpawnZones.allSpawners[aSpawner.zone] = nil\
end\
\
function cfxSpawnZones.getSpawnerForZone(aZone)\
    return cfxSpawnZones.allSpawners[aZone]\
end\
\
function cfxSpawnZones.getSpawnerForZoneNamed(aName)\
    local aZone = cfxZones.getZoneByName(aName) \
    if not aZone then return nil end \
    return cfxSpawnZones.getSpawnerForZone(aZone)\
end\
\
\
function cfxSpawnZones.getRequestableSpawnersInRange(aPoint, aRange, aSide)\
    -- trigger.action.outText(\"enter requestable spawners for side \" .. aSide , 30)\
    if not aSide then aSide = 0 end  \
    if not aRange then aRange = 200 end \
    if not aPoint then return {} end \
\
    local theSpawners = {}\
    for aZone, aSpawner in pairs(cfxSpawnZones.allSpawners) do \
        -- iterate all zones and collect those that match \
        local hasMatch = true \
        local delta = dcsCommon.distFlat(aPoint, cfxZones.getPoint(aZone))\
        if delta>aRange then hasMatch = false end \
        if aSide ~= 0 then \
            -- check if side is correct for owned zone \
            if not cfxSpawnZones.verifySpawnOwnership(aSpawner) then \
                -- failed ownership test. owner of master \
                -- is not my own zone \
                hasMatch = false \
            end\
        end\
        \
        if aSide ~= aSpawner.rawOwner then \
            -- only return spawners with this side\
            -- note: this will NOT work with neutral players \
            hasMatch = false \
        end\
        \
        if not aSpawner.requestable then \
            hasMatch = false \
        end\
        \
        if hasMatch then \
            table.insert(theSpawners, aSpawner)\
        end\
    end\
    \
    return theSpawners\
end\
--\
-- spawn troops \
-- \
function cfxSpawnZones.verifySpawnOwnership(spawner)\
    -- returns false ONLY if masterSpawn disagrees\
    if not spawner.masterZoneName then \
        --trigger.action.outText(\"spawner \" .. spawner.name .. \" no master, go!\", 30)\
        return true \
    end -- no master owner, all ok\
    local myCoalition = spawner.rawOwner\
    local masterZone = cfxZones.getZoneByName(spawner.masterZoneName)\
    if not masterZone then \
        trigger.action.outText(\"spawner \" .. spawner.name .. \" DID NOT FIND MASTER ZONE <\" .. spawner.masterZoneName .. \">\", 30)\
        return false \
    end\
    \
    if not masterZone.owner then \
        --trigger.action.outText(\"spawner \" .. spawner.name .. \" - masterZone \" .. masterZone.name .. \" HAS NO OWNER????\", 30)\
        return true \
    end\
    \
    if (myCoalition ~= masterZone.owner) then \
        -- can't spawn, surrounding area owned by enemy\
        return false \
    end\
\
    return true\
end\
\
function cfxSpawnZones.spawnWithSpawner(aSpawner)\
    if type(aSpawner) == \"string\" then -- return spawner for zone of that name\
        aSpawner = cfxSpawnZones.getSpawnerForZoneNamed(aName)\
    end\
    if not aSpawner then return end \
    local theZone = aSpawner.zone -- retrieve the zone that defined me \
    \
    if cfxSpawnZones.verbose or theZone.verbose then \
        trigger.action.outText(\"+++spwn: started spawn with spawner for <\" .. theZone.name .. \">\", 30)\
    end\
    \
    -- will NOT check if conditions are met. This forces a spawn\
    local unitTypes = {} -- build type names\
    --local p = aSpawner.zone.point  \
    local p = cfxZones.getPoint(theZone) -- aSpawner.zone.point\
        \
    -- split the conf.troopsOnBoardTypes into an array of types\
    unitTypes = dcsCommon.splitString(aSpawner.types, \",\")\
    if #unitTypes < 1 then \
        table.insert(unitTypes, \"Soldier M4\") -- make it one m4 trooper as fallback\
    end\
    \
    local theCountry = aSpawner.country  \
    local theCoalition = coalition.getCountryCoalition(theCountry)\
    \
    local theGroup, theData = cfxZones.createGroundUnitsInZoneForCoalition (\
                theCoalition, \
                aSpawner.baseName .. \"-\" .. aSpawner.count, -- must be unique \
                aSpawner.zone,                                             \
                unitTypes, \
                aSpawner.formation,\
                aSpawner.heading)\
    if cfxSpawnZones.verbose or theZone.verbose then \
        -- check created group size versus requested size \
        trigger.action.outText(\"+++spwn: created <\" .. theGroup:getSize() .. \"> units, requested <\" .. #unitTypes .. \"> units, formation <\" .. aSpawner.formation .. \">\", 30)\
        trigger.action.outText(\"+++spwn: zone <\" .. theZone.name .. \">center at <\" .. dcsCommon.point2text(p) .. \">\", 30)\
        local allUnits = theGroup:getUnits()\
        for idx, myUnit in pairs (allUnits) do \
            local pos = myUnit:getPoint()\
            trigger.action.outText(\"unit <\" .. myUnit:getName() .. \"> at \" .. dcsCommon.point2text(pos), 30)\
        end\
    end\
    \
    aSpawner.theSpawn = theGroup\
    aSpawner.count = aSpawner.count + 1 \
\
    -- insert into collector for persistence\
    local troopData = {}\
    troopData.groupData = theData\
    troopData.orders = aSpawner.orders -- always set \
    troopData.side = theCoalition\
    troopData.target = aSpawner.target -- can be nil!\
    troopData.tracker = theZone.trackWith -- taken from ZONE!!, can be nil\
    troopData.range = aSpawner.range\
    cfxSpawnZones.spawnedGroups[theData.name] = troopData \
    \
    -- remember: orders are always lower case only \
    if aSpawner.orders and (\
       aSpawner.orders:lower() == \"training\" or \
       aSpawner.orders:lower() == \"train\" )\
    then \
        -- make them ROE \"HOLD\"\
        -- remember to do this in persistence as well!\
        -- they aren't fed to cfxGroundTroops.\
        -- we should update groundTroops to simply \
        -- drop those with 'train' or 'training'\
        cfxCommander.scheduleOptionForGroup(\
            theGroup, \
            AI.Option.Ground.id.ROE, \
            AI.Option.Ground.val.ROE.WEAPON_HOLD, \
            1.0)\
    else \
        local newTroops = cfxGroundTroops.createGroundTroops(theGroup, aSpawner.range, aSpawner.orders) \
        cfxGroundTroops.addGroundTroopsToPool(newTroops)\
        \
        -- see if we have defined a target zone as destination\
        -- and set it accordingly \
        if aSpawner.target then \
            local destZone = cfxZones.getZoneByName(aSpawner.target)\
            if destZone then\
                newTroops.destination = destZone \
            else \
                trigger.action.outText(\"+++ spawner \" .. aSpawner.name .. \" has illegal (unknown) target zone <\" .. aSpawner.target .. \">. Pausing.\", 30)\
                aSpawner.paused = true \
            end\
        elseif aSpawner.orders == \"attackzone\" then \
            -- attackZone command but no zone given\
            trigger.action.outText(\"+++ spawner \" .. aSpawner.name .. \" has no target but attackZone command. Pausing.\", 30)\
                aSpawner.paused = true         \
        end \
        \
    end\
    \
    -- hand off to delicates \
    if aSpawner.delicateName and delicates then \
    -- pass this object to the delicate zone mentioned \
        local theDeli = delicates.getDelicatesByName(aSpawner.delicateName)\
        if theDeli then \
            delicates.addGroupToInventoryForZone(theDeli, newTroops)\
        else \
            trigger.action.outText(\"+++Spwn: spawner <\" .. aZone.name .. \"> can't find delicates <\" .. aSpawner.delicateName .. \">\", 30)\
        end\
    end\
    \
    -- track this if we are have a trackwith attribute \
    -- note that we retrieve trackwith from ZONE, not spawner \
    if theZone.trackWith then \
        cfxSpawnZones.handoffTracking(theGroup, theZone) \
    end\
            \
    -- callback to all who want to know \
    cfxSpawnZones.invokeCallbacksFor(\"spawned\", theGroup, aSpawner)\
    \
    -- timestamp so we can check against cooldown on manual spawn\
    aSpawner.lastSpawnTimeStamp = timer.getTime()\
    -- make sure a requestable spawner is always paused \
    if aSpawner.requestable then \
        aSpawner.paused = true \
    end\
    \
    if aSpawner.autoRemove then \
        -- simply remove the group \
        aSpawner.theSpawn = nil\
    end\
end\
\
function cfxSpawnZones.handoffTracking(theGroup, theZone)\
-- note that this method works on theZone, not Spawner object\
    if not groupTracker then \
        trigger.action.outText(\"+++spawner: <\" .. theZone.name .. \"> trackWith requires groupTracker module\", 30) \
        return \
    end\
    local trackerName = theZone.trackWith\
\
    -- now assemble a list of all trackers\
    if cfxSpawnZones.verbose or theZone.verbose then \
        trigger.action.outText(\"+++spawner: spawn pass-off: \" .. trackerName, 30)\
    end \
    \
    local trackerNames = {}\
    if dcsCommon.containsString(trackerName, ',') then\
        trackerNames = dcsCommon.splitString(trackerName, ',')\
    else \
        table.insert(trackerNames, trackerName)\
    end\
    for idx, aTrk in pairs(trackerNames) do \
        local theName = dcsCommon.trim(aTrk)\
        if theName == \"*\" then theName = theZone.name end \
        local theTracker = groupTracker.getTrackerByName(theName)\
        if not theTracker then \
            trigger.action.outText(\"+++spawner: <\" .. theZone.name .. \">: cannot find tracker named <\".. theName .. \">\", 30) \
        else \
            groupTracker.addGroupToTracker(theGroup, theTracker)\
             if cfxSpawnZones.verbose or theZone.verbose then \
                trigger.action.outText(\"+++spawner: added \" .. theGroup:getName() .. \" to tracker \" .. theName, 30)\
             end\
        end \
    end \
end\
\
--\
-- U P D A T E \
--\
function cfxSpawnZones.GC()\
    -- GC run. remove all my dead remembered troops\
    local filteredAttackers = {}\
    local before = #cfxSpawnZones.spawnedGroups\
    for gName, gData in pairs (cfxSpawnZones.spawnedGroups) do \
        -- all we need to do is get the group of that name\
        -- and if it still returns units we are fine \
        local gameGroup = Group.getByName(gName)\
        if gameGroup and gameGroup:isExist() and gameGroup:getSize() > 0 then \
            filteredAttackers[gName] = gData\
        end\
    end\
    cfxSpawnZones.spawnedGroups = filteredAttackers\
    if cfxSpawnZones.verbose then \
        trigger.action.outText(\"spawn zones GC ran: before <\" .. before .. \">, after <\" .. #cfxSpawnZones.spawnedGroups .. \">\", 30)\
    end\
end\
\
function cfxSpawnZones.update()\
    cfxSpawnZones.updateSchedule = timer.scheduleFunction(cfxSpawnZones.update, {}, timer.getTime() + 1/cfxSpawnZones.ups)\
    \
    for key, spawner in pairs (cfxSpawnZones.allSpawners) do \
        -- see if the spawn is dead or was removed\
        local needsSpawn = true \
        if spawner.theSpawn then \
            local group = spawner.theSpawn\
            if group:isExist() then \
                -- see how many members of this group are still alive\
                local liveUnits = group:getSize() --dcsCommon.getLiveGroupUnits(group)\
                -- spawn is still alive, will not spawn\
                if liveUnits > 1 then \
                    -- we may want to check if this member is still inside\
                    -- of spawn location. currently we don't do that\
                    needsSpawn = false \
                end\
            end\
        end\
    \
        if spawner.paused then needsSpawn = false end \
        \
        -- see if we spawned maximum number of times already\
        -- or have -1 as maxspawn, indicating endless\
        if needsSpawn and spawner.maxSpawns > -1 then \
            needsSpawn = spawner.maxSpawns > 0\
        end\
        \
        if needsSpawn then \
            -- is this the first time? \
            if not spawner.cdStarted then \
                -- no, start cooldown\
                spawner.cdStarted = true \
                spawner.cdTimer = timer.getTime() + spawner.cooldown\
            end\
        end\
\
        -- still on cooldown?\
        if timer.getTime() < spawner.cdTimer then needsSpawn = false end \
        \
        -- is master zone still alinged with me?\
        needsSpawn = needsSpawn and cfxSpawnZones.verifySpawnOwnership(spawner)\
\
        -- check if perhaps our watchtriggers causes spawn\
        if spawner.pauseFlag then \
            local currTriggerVal = trigger.misc.getUserFlag(spawner.pauseFlag)\
            if currTriggerVal ~= spawner.lastPauseValue then\
                spawner.paused = true  \
                needsSpawn = false\
                spawner.lastPauseValue = currTriggerVal\
            end\
        end\
        \
        if spawner.triggerFlag then \
            local currTriggerVal = trigger.misc.getUserFlag(spawner.triggerFlag)\
            if currTriggerVal ~= spawner.lastTriggerValue then\
                needsSpawn = true \
                spawner.lastTriggerValue = currTriggerVal\
            end\
        end\
        \
        if spawner.activateFlag then \
            local currTriggerVal = trigger.misc.getUserFlag(spawner.activateFlag)\
            if currTriggerVal ~= spawner.lastActivateValue then\
                spawner.paused = false  \
                spawner.lastActivateValue = currTriggerVal\
            end\
        end\
\
        \
                \
        -- if we get here, and needsSpawn is still set, we go ahead and spawn\
        if needsSpawn then \
---            trigger.action.outText(\"+++ spawning for zone \" .. spawner.zone.name, 30)\
            cfxSpawnZones.spawnWithSpawner(spawner)\
            spawner.cdStarted = false -- reset spawner cd signal \
            if spawner.maxSpawns > 0 then \
                spawner.maxSpawns = spawner.maxSpawns - 1\
            end\
            if spawner.maxSpawns == 0 then \
                spawner.paused = true \
                if cfxSpawnZones.verbose then \
                    trigger.action.outText(\"+++ maxspawn -- turning off  zone \" .. spawner.zone.name, 30)\
                end \
            end\
        else \
            -- trigger.action.outText(\"+++ NOSPAWN for zone \" .. spawner.zone.name, 30)\
        end\
    end\
end\
\
function cfxSpawnZones.houseKeeping()\
    timer.scheduleFunction(cfxSpawnZones.houseKeeping, {}, timer.getTime() + 5 * 60) -- every 5 minutes \
    cfxSpawnZones.GC()\
end\
\
--\
-- LOAD/SAVE\
--\
function cfxSpawnZones.saveData()\
    local theData = {}\
    local allSpawnerData = {}\
    -- now iterate all spawners and collect their data\
    for theZone, theSpawner in pairs(cfxSpawnZones.allSpawners) do \
        local zName = theZone.name \
        local spawnData = {}\
        if theSpawner.spawn and theSpawner.spawn:isExist() then \
            spawnData.spawn = theSpawner.spawn:getName()\
        end\
        spawnData.count = theSpawner.count\
        spawnData.paused = theSpawner.paused \
        spawnData.cdStarted = theSpawner.cdStarted\
        spawnData.cdTimer = theSpawner.cdTimer - timer.getTime() -- what remains of the cooldown time \
        \
        allSpawnerData[zName] = spawnData\
    end\
    \
    -- run a GC\
    cfxSpawnZones.GC()\
    -- now collect all living groups\
    -- no longer required to check if group is lively\
    local allLivingTroopData = {}\
    for gName, gData in pairs(cfxSpawnZones.spawnedGroups) do \
        local sData = dcsCommon.clone(gData)\
        dcsCommon.synchGroupData(sData.groupData)\
        allLivingTroopData[gName] = sData\
    end\
    \
    theData.spawnerData = allSpawnerData\
    theData.troopData = allLivingTroopData\
    return theData\
end\
\
function cfxSpawnZones.loadData()\
    if not persistence then return end \
    local theData = persistence.getSavedDataForModule(\"cfxSpawnZones\")\
    if not theData then \
        if cfxSpawnZones.verbose then \
            trigger.action.outText(\"+++spwn: no save date received, skipping.\", 30)\
        end\
        return\
    end\
    \
    -- we begin by re-spawning all spawned groups so that the \
    -- spwners can then later link to them \
    local allTroopData = theData.troopData\
    for gName, gdTroop in pairs (allTroopData) do \
        local gData = gdTroop.groupData \
        local orders = gdTroop.orders \
        local target = gdTroop.target\
        local tracker = gdTroop.tracker \
        local side = gdTroop.side \
        local range = gdTroop.range\
        local cty = gData.cty \
        local cat = gData.cat  \
        \
        -- now spawn, but first \
        -- add to my own attacker queue so we can save later \
        local gdClone = dcsCommon.clone(gdTroop)\
        cfxSpawnZones.spawnedGroups[gName] = gdClone \
        local theGroup = coalition.addGroup(cty, cat, gData)\
        -- post-proccing for 'train' orders\
        if orders and (orders == \"train\" ) then \
            -- make them ROE \"HOLD\"\
            cfxCommander.scheduleOptionForGroup(\
                theGroup, \
                AI.Option.Ground.id.ROE, \
                AI.Option.Ground.val.ROE.WEAPON_HOLD, \
                1.0)\
        else \
            -- add to groundTroops \
            local newTroops = cfxGroundTroops.createGroundTroops(theGroup, range, orders) \
            cfxGroundTroops.addGroundTroopsToPool(newTroops)\
            -- engage a target zone \
            if target then \
                local destZone = cfxZones.getZoneByName(target)\
                if destZone then\
                    newTroops.destination = destZone\
                    cfxGroundTroops.makeTroopsEngageZone(newTroops)\
                end \
            end\
        end \
                \
        -- post-proccing for trackwith [may not be needed when we]\
        -- have persistence in the tracking module. that module \
        -- simply schedules re-connecting after one second \
    end\
    \
    -- now set up all spawners with save data \
    local allSpawnerData = theData.spawnerData\
    for zName, sData in pairs (allSpawnerData) do \
        local theZone = cfxZones.getZoneByName(zName)\
        if theZone then \
            local theSpawner = cfxSpawnZones.getSpawnerForZone(theZone)\
            if theSpawner then \
                theSpawner.inited = true -- inited by persistence\
                theSpawner.count = sData.count \
                theSpawner.paused = sData.paused\
                theSpawner.cdStarted = sData.cdStarted\
                if theSpawner.cdStarted then \
                    theSpawner.cdTimer = timer.getTime() + sData.cdTimer\
                else \
                    theSpawner.cdTimer = -1\
                end\
                if sData.spawn then \
                    local theGroup = Group.getByName(sData.spawn)\
                    if theGroup then \
                        theSpawner.spawn = theGroup\
                    else \
                        trigger.action.outText(\"+++spwn (persistence): can't re-connect spawner <\" .. zName .. \"> with group <\" .. sData.spawn .. \">, skipping\", 30)\
                    end\
                end\
            else \
                trigger.action.outText(\"+++spwn (persistence): can't find spawner for zone <\" .. zName .. \">, skipping\", 30)\
            end\
        else \
            trigger.action.outText(\"+++spwn (persistence): can't find zone <\" .. zName .. \"> for spawner, skipping\", 30)\
        end\
    end\
    \
end\
\
--\
-- START \
--\
function cfxSpawnZones.initialSpawnCheck(aSpawner)\
    if not aSpawner.paused \
    and cfxSpawnZones.verifySpawnOwnership(aSpawner) \
    and aSpawner.maxSpawns ~= 0 \
    and not aSpawner.inited \
    then \
        cfxSpawnZones.spawnWithSpawner(aSpawner)\
        -- update spawn count and make sure we haven't spawned the one and only \
        if aSpawner.maxSpawns > 0 then \
            aSpawner.maxSpawns = aSpawner.maxSpawns - 1\
        end\
        if aSpawner.maxSpawns == 0 then \
            aSpawner.paused = true \
            trigger.action.outText(\"+++ maxspawn -- turning off  zone \" .. aSpawner.zone.name, 30)\
        end\
    end\
end\
\
function cfxSpawnZones.start()\
    if not dcsCommon.libCheck(\"cfx Spawn Zones\", \
        cfxSpawnZones.requiredLibs) then\
        return false \
    end\
    \
    -- collect all spawn zones \
    local attrZones = cfxZones.getZonesWithAttributeNamed(\"spawner\")\
    \
    -- now create a spawner for all, add them to the spawner updater, and spawn for all zones that are not\
    -- paused \
    for k, aZone in pairs(attrZones) do \
        local aSpawner = cfxSpawnZones.createSpawner(aZone)\
        cfxSpawnZones.addSpawner(aSpawner)\
    end\
    \
    -- we now do persistence\
    if persistence then \
        -- sign up for persistence \
        callbacks = {}\
        callbacks.persistData = cfxSpawnZones.saveData\
        persistence.registerModule(\"cfxSpawnZones\", callbacks)\
        -- now load my data \
        cfxSpawnZones.loadData()\
    end\
    \
    -- we now spawn if not taken care of by load / save \
    for theZone, aSpawner in pairs(cfxSpawnZones.allSpawners) do\
        cfxSpawnZones.initialSpawnCheck(aSpawner)\
    end\
    \
    -- and start the regular update calls\
    cfxSpawnZones.update()\
    \
    -- start housekeeping \
    cfxSpawnZones.houseKeeping()\
    \
    trigger.action.outText(\"cfx Spawn Zones v\" .. cfxSpawnZones.version .. \" started.\", 30)\
    return true\
end\
\
if not cfxSpawnZones.start() then \
    trigger.action.outText(\"cf/x Spawn Zones aborted: missing libraries\", 30)\
    cfxSpawnZones = nil \
end\
\
--[[--\
IMPROVEMENTS\
 'notMasterOwner' a flag to invert ownership, so we can spawn blue if masterOwner is red\
  \
  take apart owned zone and spawner, so we have a more canonical behaviour\
  \
  'repair' flag - have repair logic for units that are spawned just like now the owned zones do\
--]]--",
                    ["predicate"] = "a_do_script",
                }, -- end of [5]
                [6] = 
                {
                    ["text"] = "cfxHeloTroops = {}\
cfxHeloTroops.version = \"3.0.4\"\
cfxHeloTroops.verbose = false \
cfxHeloTroops.autoDrop = true \
cfxHeloTroops.autoPickup = false \
cfxHeloTroops.pickupRange = 100 -- meters \
cfxHeloTroops.requestRange = 500 -- meters\
--\
--[[--\
 VERSION HISTORY\
 1.1.3 - repaired forgetting 'wait-' when loading/disembarking\
 1.1.4 - corrected coalition bug in deployTroopsFromHelicopter \
 2.0.0 - added weight change when troops enter and leave the helicopter \
       - idividual troop capa max per helicopter \
 2.0.1 - lib loader verification\
       - uses dcsCommon.isTroopCarrier(theUnit)\
 2.0.2 - can now deploy from spawners with \"requestable\" attribute\
 2.1.0 - supports config zones\
       - check spawner legality by types  \
       - updated types to include 2.7.6 additions to infantry\
       - updated types to include stinger/manpads \
 2.2.0 - minor maintenance (dcsCommon)\
       - (re?) connected readConfigZone (wtf?)\
       - persistence support\
       - made legalTroops entrirely optional and defer to dcsComon else\
 2.3.0 - interface with owned zones and playerScore when \
       - combat-dropping troops into non-owned owned zone.\
       - prevent auto-load from pre-empting loading csar troops \
 2.3.1 - added ability to self-define troopCarriers via config\
 2.4.0 - added missing support for attackZone orders (destination)\
       - eliminated cfxPlayer module import and all dependencies\
       - added support for groupTracker / limbo \
       - removed restriction to only apply to helicopters in anticipation of the C-130 Hercules appearing in the game\
 2.4.1 - new actionSound attribute, sound plays to group whenever \
         troops have boarded or disembarked\
 3.0.0 - added requestable cloner support \
       - harmonized spawning invocations across cloners and spawners \
       - dmlZones \
       - requestRange attribute\
 3.0.1 - fixed a bug with legalTroops attribute\
 3.0.2 - fixed a typo in in-air menu \
 3.0.3 - pointInZone check for insertion rather than radius \
 3.0.4 - also handles picking up troops with orders \"captureandhold\"\
 \
--]]--\
--\
-- cfxHeloTroops -- a module to pick up and drop infantry. \
-- Can be used with ANY aircraft, configured by default to be \
-- restricted to troop-carrying helicopters.\
-- might be configure to apply to any type you want using the \
-- configuration zone.\
\
\
cfxHeloTroops.requiredLibs = {\
    \"dcsCommon\", -- common is of course needed for everything\
                 -- pretty stupid to check for this since we \
                 -- need common to invoke the check, but anyway\
    \"cfxZones\", -- Zones, of course \
    \"cfxCommander\", -- to make troops do stuff\
    \"cfxGroundTroops\", -- generic when dropping troops\
}\
\
cfxHeloTroops.unitConfigs = {} -- all configs are stored by unit's name \
cfxHeloTroops.troopWeight = 100 -- kg average weight per trooper \
\
-- persistence support \
cfxHeloTroops.deployedTroops = {}\
\
function cfxHeloTroops.resetConfig(conf)\
    conf.autoDrop = cfxHeloTroops.autoDrop --if true, will drop troops on-board upon touchdown\
    conf.autoPickup = cfxHeloTroops.autoPickup -- if true will load nearest troops upon touchdown\
    conf.pickupRange = cfxHeloTroops.pickupRange --meters, maybe make per helo?\
    conf.currentState = -1 -- 0 = landed, 1 = airborne, -1 undetermined\
    conf.troopsOnBoardNum = 0 -- if not 0, we have troops and can spawnm/drop\
    conf.troopCapacity = 8 -- should be depending on airframe \
    -- troopsOnBoard.name contains name of group\
    -- the other fields info for troops picked up \
    conf.troopsOnBoard = {} -- table with the following\
    conf.troopsOnBoard.name = \"***reset***\"\
    conf.dropFormation = \"circle_out\" -- may be chosen later?\
end\
\
function cfxHeloTroops.createDefaultConfig(theUnit)\
    local conf = {}\
    cfxHeloTroops.resetConfig(conf)\
\
    conf.myMainMenu = nil -- this is where the main menu for group will be stored\
    conf.myCommands = nil -- this is where we put all teh commands in \
    return conf \
end\
\
\
function cfxHeloTroops.getUnitConfig(theUnit) -- will create new config if not existing\
    if not theUnit then\
        trigger.action.outText(\"+++WARNING: nil unit in get config!\", 30)\
        return nil \
    end\
    local c = cfxHeloTroops.unitConfigs[theUnit:getName()]\
    if not c then \
        c = cfxHeloTroops.createDefaultConfig(theUnit)\
        cfxHeloTroops.unitConfigs[theUnit:getName()] = c \
    end\
    return c \
end\
\
function cfxHeloTroops.getConfigForUnitNamed(aName)\
    return cfxHeloTroops.unitConfigs[aName]\
end\
\
--\
--\
-- LANDED\
--\
--\
function cfxHeloTroops.loadClosestGroup(conf)\
    local p = conf.unit:getPosition().p\
    local cat = Group.Category.GROUND\
    local unitsToLoad = dcsCommon.getLivingGroupsAndDistInRangeToPoint(p, conf.pickupRange, conf.unit:getCoalition(), cat) \
    \
    -- groups may contain units that are not for transport.\
    -- for now we only load troops with legal type strings \
    unitsToLoad = cfxHeloTroops.filterTroopsByType(unitsToLoad)\
\
    -- now limit the options to the five closest legal groups\
    local numUnits = #unitsToLoad\
    if numUnits < 1 then return false end -- on false will drop through \
    \
    local aTeam = unitsToLoad[1] -- get first (closest) entry\
    local dist = aTeam.dist \
    local group = aTeam.group \
    cfxHeloTroops.doLoadGroup({conf, group})\
    return true -- will have loaded and reset menu\
end\
\
function cfxHeloTroops.heloLanded(theUnit)\
    -- when we have landed, \
    if not dcsCommon.isTroopCarrier(theUnit, cfxHeloTroops.troopCarriers) then return end\
    \
    local conf = cfxHeloTroops.getUnitConfig(theUnit)\
    conf.unit = theUnit\
    conf.currentState = 0\
    \
    -- we look if we auto-unload\
    if conf.autoDrop then \
        if conf.troopsOnBoardNum > 0 then \
            cfxHeloTroops.doDeployTroops({conf, \"autodrop\"})\
            -- already called set menu, can exit directly\
            return\
        end\
        -- when we get here, we have no troops to drop on board \
        -- so nothing to do really except look if we can pick up troops\
        -- set menu will do that for us    \
    end\
    \
    if conf.autoPickup then \
        if conf.troopsOnBoardNum < 1 then\
            -- load the closest group\
            if cfxHeloTroops.loadClosestGroup(conf) then \
                return\
            end\
        end        \
    end\
    \
    -- when we get here, we simply set the newest menus and are done \
    -- reset menu \
    cfxHeloTroops.removeComms(conf.unit)\
    cfxHeloTroops.setCommsMenu(conf.unit)\
end\
\
--\
--\
-- Helo took off\
--\
--\
function cfxHeloTroops.heloDeparted(theUnit)\
    if not dcsCommon.isTroopCarrier(theUnit, cfxHeloTroops.troopCarriers) then return end\
    \
    -- when we take off, all that needs to be done is to change the state \
    -- to airborne, and then set the status flag \
    local conf = cfxHeloTroops.getUnitConfig(theUnit)\
    conf.currentState = 1 -- in the air \
    \
    cfxHeloTroops.removeComms(conf.unit)\
    cfxHeloTroops.setCommsMenu(conf.unit)\
    \
end\
\
--\
-- \
-- Helo Crashed \
--\
--\
function cfxHeloTroops.cleanHelo(theUnit)\
    -- clean up \
    local conf = cfxHeloTroops.getUnitConfig(theUnit)\
    conf.unit = theUnit \
    conf.troopsOnBoardNum = 0 -- all dead \
    conf.currentState = -1 -- (we don't know)\
\
    -- check if we need to interface with groupTracker \
    if conf.troopsOnBoard.name and groupTracker then \
        local theName = conf.troopsOnBoard.name\
        -- there was (possibly) a group on board. see if it was tracked\
        local isTracking, numTracking, trackers = groupTracker.groupNameTrackedBy(theName)\
        \
        -- if so, remove it from limbo \
        if isTracking then \
            for idx, theTracker in pairs(trackers) do \
                groupTracker.removeGroupNamedFromTracker(theName, theTracker)\
                if cfxHeloTroops.verbose then \
                    trigger.action.outText(\"+++Helo: removed group <\" .. theName .. \"> from tracker <\" .. theTracker.name .. \">\", 30)\
                end \
            end \
        end\
    end\
    conf.troopsOnBoard = {}\
end\
\
function cfxHeloTroops.heloCrashed(theUnit)\
    if not dcsCommon.isTroopCarrier(theUnit, cfxHeloTroops.troopCarriers) then return \
    end\
    -- clean up \
    cfxHeloTroops.cleanHelo(theUnit)\
end\
\
--\
--\
-- M E N U   H A N D L I N G   &   R E S P O N S E \
-- \
-- \
function cfxHeloTroops.clearCommsSubmenus(conf)\
    if conf.myCommands then \
        for i=1, #conf.myCommands do\
            missionCommands.removeItemForGroup(conf.id, conf.myCommands[i])\
        end\
    end\
    conf.myCommands = {}\
end\
\
function cfxHeloTroops.removeCommsFromConfig(conf)\
    cfxHeloTroops.clearCommsSubmenus(conf)\
    \
    if conf.myMainMenu then \
        missionCommands.removeItemForGroup(conf.id, conf.myMainMenu) \
        conf.myMainMenu = nil\
    end\
end\
\
function cfxHeloTroops.removeComms(theUnit)\
    if not theUnit then return end\
    if not theUnit:isExist() then return end \
    \
    local group = theUnit:getGroup() \
    local id = group:getID()\
    local conf = cfxHeloTroops.getUnitConfig(theUnit)\
    conf.id = id\
    conf.unit = theUnit \
    \
    cfxHeloTroops.removeCommsFromConfig(conf)\
end\
\
function cfxHeloTroops.addConfigMenu(conf)\
    -- we add the a menu showing current state \
    -- and the option to change fro auto drop \
    -- and auto pickup \
    local onOff = \"OFF\"\
    if conf.autoDrop then onOff = \"ON\" end \
    local theCommand =  missionCommands.addCommandForGroup(\
                conf.id, \
                'Auto-Drop: ' .. onOff .. ' - Select to change',\
                conf.myMainMenu,\
                cfxHeloTroops.redirectToggleConfig, \
                {conf, \"drop\"}\
                )\
    table.insert(conf.myCommands, theCommand)\
    onOff = \"OFF\"\
    if conf.autoPickup then onOff = \"ON\" end \
    theCommand =  missionCommands.addCommandForGroup(\
                conf.id, \
                'Auto-Pickup: ' .. onOff .. ' - Select to change',\
                conf.myMainMenu,\
                cfxHeloTroops.redirectToggleConfig, \
                {conf, \"pickup\"}\
                )\
    table.insert(conf.myCommands, theCommand)\
end\
\
function cfxHeloTroops.setCommsMenu(theUnit)\
    -- depending on own load state, we set the command structure\
    -- it begins at 10-other, and has 'Assault Troops' as main menu with submenus\
    -- as required \
    if not theUnit then return end\
    if not theUnit:isExist() then return end \
    \
    -- we only add this menu to troop carriers \
    if not dcsCommon.isTroopCarrier(theUnit, cfxHeloTroops.troopCarriers) then\
        if cfxHeloTroops.verbose then \
            trigger.action.outText(\"+++heloT - player unit <\" .. theUnit:getName() .. \"> type <\" .. theUnit:getTypeName() .. \"> is not legal troop carrier.\", 30)\
        end\
        return \
    end\
    \
    local group = theUnit:getGroup() \
    local id = group:getID()\
    local conf = cfxHeloTroops.getUnitConfig(theUnit)\
    conf.id = id; -- we do this ALWAYS to it is current even after a crash \
    conf.unit = theUnit -- link back\
    \
    -- ok, first, if we don't have an F-10 menu, create one \
    if not (conf.myMainMenu) then \
        conf.myMainMenu = missionCommands.addSubMenuForGroup(id, 'Airlift Troops') \
    end\
    \
    -- clear out existing commands\
    cfxHeloTroops.clearCommsSubmenus(conf)\
    \
    -- now we have a menu without submenus. \
    -- add our own submenus\
    cfxHeloTroops.addConfigMenu(conf)\
    \
    -- now see if we are on the ground or in the air\
    -- or unknown\
    if conf.currentState < 0 then \
        conf.currentState = 0 -- landed\
        if theUnit:inAir() then \
            conf.currentState = 1\
        end\
    end\
    \
    if conf.currentState == 0 then \
        cfxHeloTroops.addGroundMenu(conf)\
    else \
        cfxHeloTroops.addAirborneMenu(conf)\
    end\
    \
end\
\
function cfxHeloTroops.addAirborneMenu(conf)\
    -- while we are airborne, there isn't much to do except add a status menu that does nothing\
    -- but we can add some instructions\
    -- let's begin by assuming no troops aboard\
    local commandTxt = \"(To load troops, land in proximity to them)\"\
    if conf.troopsOnBoardNum > 0 then \
        commandTxt = \"(You are carrying \" .. conf.troopsOnBoardNum .. \" Assault Troops. Land to deploy them)\"\
    end\
    local theCommand =  missionCommands.addCommandForGroup(\
                conf.id, \
                commandTxt,\
                conf.myMainMenu,\
                cfxHeloTroops.redirectNoAction, \
                {conf, \"none\"}\
                )\
    table.insert(conf.myCommands, theCommand)\
end\
\
function cfxHeloTroops.redirectNoAction(args)\
    -- actually, we do not redirect since there is nothing to do\
end\
\
function cfxHeloTroops.addGroundMenu(conf)\
    -- this is the most complex menu. Player can deploy troops when loaded\
    -- and load troops when they are in proximity\
    \
    -- case 1: troops aboard \
    if conf.troopsOnBoardNum > 0 then \
        local theCommand =  missionCommands.addCommandForGroup(\
                conf.id, \
                \"Deploy Team <\" .. conf.troopsOnBoard.name .. \">\",\
                conf.myMainMenu,\
                cfxHeloTroops.redirectDeployTroops, \
                {conf, \"deploy\"}\
                )\
        table.insert(conf.myCommands, theCommand)\
        return\
    end\
    \
    -- case 2A: no troops aboard, and requestable spawners/cloners in range \
    local p = conf.unit:getPosition().p\
    local mySide = conf.unit:getCoalition() \
\
    -- collect available spawn zones \
    local availableSpawners = {}\
    if cfxSpawnZones then -- only if SpawnZones is implemented \
        local availableSpawnersRaw = cfxSpawnZones.getRequestableSpawnersInRange(p, cfxHeloTroops.requestRange, mySide)\
        \
        for idx, aSpawner in pairs(availableSpawnersRaw) do \
            -- filter all spawners that spawn \"illegal\" troops\
            local theTypes = aSpawner.types\
            local typeArray = dcsCommon.splitString(theTypes, ',')\
            typeArray = dcsCommon.trimArray(typeArray)\
            local allLegal = true \
            -- check agianst default (dcsCommon) or own definition (if exists)\
            for idy, aType in pairs(typeArray) do \
                if cfxHeloTroops.legalTroops then \
                    if not dcsCommon.arrayContainsString(cfxHeloTroops.legalTroops, aType) then \
                        allLegal = false \
                    end\
                else \
                    if not dcsCommon.typeIsInfantry(aType) then \
                        allLegal = false \
                    end\
                end\
            end\
            if allLegal then \
                table.insert(availableSpawners, aSpawner)\
            end\
        end\
    end \
    \
    -- collect available clone zones \
    if cloneZones then \
        local availableSpawnersRaw = cloneZones.getRequestableClonersInRange(p, cfxHeloTroops.requestRange, mySide)\
        for idx, aSpawner in pairs(availableSpawnersRaw) do \
            -- filter all spawners that spawn \"illegal\" troops or have none\
            local theTypes = aSpawner.allTypes\
            local allLegal = true\
            local numTypes = dcsCommon.getSizeOfTable(theTypes)\
            if numTypes > 0 then \
                for aType, cnt in pairs(theTypes) do\
                    if cfxHeloTroops.legalTroops then \
                        if not dcsCommon.arrayContainsString(cfxHeloTroops.legalTroops, aType) then \
                            allLegal = false \
                        end\
                    else \
                        if not dcsCommon.typeIsInfantry(aType) then \
                            allLegal = false \
                        end\
                    end\
                end\
            else \
                allegal = false \
            end\
            \
            if allLegal then \
                table.insert(availableSpawners, aSpawner)\
            end\
        end\
    end\
    \
    local numSpawners = #availableSpawners\
    if numSpawners > 5 then numSpawners = 5 end \
    while numSpawners > 0 do\
        -- for each spawner in range, create a \
        -- spawn menu item\
        local spawner = availableSpawners[numSpawners]\
        local theName = spawner.baseName \
        local comm = \"Request <\" .. theName .. \"> troops for transport\" -- .. math.floor(aTeam.dist) .. \"m away\"\
        local theCommand =  missionCommands.addCommandForGroup(\
            conf.id, \
            comm,\
            conf.myMainMenu,\
            cfxHeloTroops.redirectSpawnGroup, \
            {conf, spawner}\
            )\
        table.insert(conf.myCommands, theCommand)\
        numSpawners = numSpawners - 1\
    end\
    \
    -- case 2B: no troops aboard. see if there are troops around \
    -- that we can load up \
    \
    local cat = Group.Category.GROUND\
    local unitsToLoad = dcsCommon.getLivingGroupsAndDistInRangeToPoint(p, conf.pickupRange, conf.unit:getCoalition(), cat) \
    \
    -- now, the groups may contain units that are not for transport.\
    -- later we can filter this by weight, or other cool stuff\
    -- for now we simply only troopy with legal type strings \
    -- TODO: add weight filtering \
    unitsToLoad = cfxHeloTroops.filterTroopsByType(unitsToLoad)\
\
    -- now limit the options to the five closest legal groups\
    local numUnits = #unitsToLoad\
    if numUnits > 5 then numUnits = 5 end\
    if numUnits < 1 then \
        local theCommand =  missionCommands.addCommandForGroup(\
                conf.id, \
                \"(No units in range)\",\
                conf.myMainMenu,\
                cfxHeloTroops.redirectNoAction, \
                {conf, \"none\"}\
                )\
        table.insert(conf.myCommands, theCommand)\
        return\
    end\
    \
    -- add an entry for each group in units to load \
    for i=1, numUnits do \
        local aTeam = unitsToLoad[i]\
        local dist = aTeam.dist \
        local group = aTeam.group \
        local tNum = group:getSize()\
        local comm = \"Load <\" .. group:getName() .. \"> \" .. tNum .. \" Members\" -- .. math.floor(aTeam.dist) .. \"m away\"\
        local theCommand =  missionCommands.addCommandForGroup(\
                conf.id, \
                comm,\
                conf.myMainMenu,\
                cfxHeloTroops.redirectLoadGroup, \
                {conf, group}\
                )\
        table.insert(conf.myCommands, theCommand)\
    end\
end\
\
function cfxHeloTroops.filterTroopsByType(unitsToLoad)\
    local filteredGroups = {}\
    for idx, aTeam in pairs(unitsToLoad) do \
        local group = aTeam.group\
        local theTypes = dcsCommon.getGroupTypeString(group)\
\
        local aT = dcsCommon.splitString(theTypes, \",\")\
        local pass = true \
        for iT, sT in pairs(aT) do \
            -- check if this is a valid type \
            if cfxHeloTroops.legalTroops then \
                if not dcsCommon.arrayContainsString(cfxHeloTroops.legalTroops, sT) then \
                    pass = false\
                    break \
                end\
            else \
                if not dcsCommon.typeIsInfantry(sT) then \
                    pass = false\
                    break \
                end\
            end\
        end \
        -- check if we are about to pre-empt a CSAR mission\
        if csarManager then \
            if csarManager.isCSARTarget(group) then \
                -- this one is managed by csarManager,\
                -- don't load it for helo troops\
                pass = false \
            end\
        end\
            \
        if pass then \
            table.insert(filteredGroups, aTeam)\
        end\
    end\
    return filteredGroups\
end\
\
--\
-- T O G G L E S \
--\
\
function cfxHeloTroops.redirectToggleConfig(args)\
    timer.scheduleFunction(cfxHeloTroops.doToggleConfig, args, timer.getTime() + 0.1)\
end\
\
function cfxHeloTroops.doToggleConfig(args)\
    local conf = args[1]\
    local what = args[2]\
    if what == \"drop\" then \
        conf.autoDrop = not conf.autoDrop\
        if conf.autoDrop then \
            trigger.action.outTextForGroup(conf.id, \"Now deploying troops immediately after landing\", 30)\
        else \
            trigger.action.outTextForGroup(conf.id, \"Troops will now only deploy when told to\", 30)        \
        end\
    else \
        conf.autoPickup = not conf.autoPickup\
        if conf.autoPickup then \
            trigger.action.outTextForGroup(conf.id, \"Nearest troops will now automatically board after landing\", 30)\
        else\
            trigger.action.outTextForGroup(conf.id, \"Troops will now board only after being ordered to do so\", 30)\
        end\
        \
    end\
    \
    cfxHeloTroops.setCommsMenu(conf.unit)\
end\
\
\
--\
-- Deploying Troops\
--\
function cfxHeloTroops.redirectDeployTroops(args)\
    timer.scheduleFunction(cfxHeloTroops.doDeployTroops, args, timer.getTime() + 0.1)\
end\
\
function cfxHeloTroops.scoreWhenCapturing(theUnit)\
    if theUnit and Unit.isExist(theUnit) and theUnit.getPlayerName then \
        -- see if wer are inside a non-alinged zone\
        -- and this includes a neutral zone \
        local coa = theUnit:getCoalition()\
        local p = theUnit:getPoint()\
        local theGroup = theUnit:getGroup()\
        local ID = theGroup:getID()\
        local nearestZone, dist = cfxOwnedZones.getNearestOwnedZoneToPoint(p)\
        if nearestZone and nearestZone:pointInZone(p) then -- dist < nearestZone.radius then \
            -- we are inside an owned zone!\
            if nearestZone.owner ~= coa then \
                -- yup, combat drop!\
                local theScore = cfxHeloTroops.combatDropScore\
                local pName = theUnit:getPlayerName()\
                if pName then \
                    cfxPlayerScore.updateScoreForPlayer(pName, theScore)\
                    cfxPlayerScore.logFeatForPlayer(pName, \"Combat Troop Insertion at \" .. nearestZone.name, coa)\
                end\
            end\
        end\
    end\
end\
\
function cfxHeloTroops.doDeployTroops(args)\
    local conf = args[1]\
    local what = args[2]\
    -- deploy the troops I have on board in formation\
    cfxHeloTroops.deployTroopsFromHelicopter(conf)\
    \
    -- interface with playerscore if we dropped \
    -- inside an enemy-owned zone \
    if cfxPlayerScore and cfxOwnedZones then \
        local theUnit = conf.unit\
        cfxHeloTroops.scoreWhenCapturing(theUnit)\
    end\
    \
    -- set own troops to 0 and erase type string \
    conf.troopsOnBoardNum = 0\
    conf.troopsOnBoard = {}\
    conf.troopsOnBoard.name = \"***wasdeployed***\"\
    \
    -- reset menu \
    cfxHeloTroops.removeComms(conf.unit)\
    cfxHeloTroops.setCommsMenu(conf.unit)\
end\
\
\
function cfxHeloTroops.deployTroopsFromHelicopter(conf)\
-- we have troops, drop them now\
    local unitTypes = {} -- build type names\
    local theUnit = conf.unit \
    local p = theUnit:getPoint() \
        \
    -- split the conf.troopsOnBoardTypes into an array of types\
    unitTypes = dcsCommon.splitString(conf.troopsOnBoard.types, \",\")\
    if #unitTypes < 1 then \
        table.insert(unitTypes, \"Soldier M4\") -- make it one m4 trooper as fallback\
    end\
    \
    local range = conf.troopsOnBoard.range\
    local orders = conf.troopsOnBoard.orders \
    local dest = conf.troopsOnBoard.destination\
    local theName = conf.troopsOnBoard.name \
    \
    if not orders then orders = \"guard\" end\
    \
    -- order processing: if the orders were pre-pended with \"wait-\"\
    -- we now remove that, so after dropping they do what their \
    -- orders where AFTER being picked up\
    if dcsCommon.stringStartsWith(orders, \"wait-\") then \
        orders = dcsCommon.removePrefix(orders, \"wait-\")\
        trigger.action.outTextForGroup(conf.id, \"+++ <\" .. conf.troopsOnBoard.name .. \"> revoke 'wait' orders, proceed with <\".. orders .. \">\", 30)\
    end\
    \
    local chopperZone = cfxZones.createSimpleZone(\"choppa\", p, 12) -- 12 m radius around choppa\
    local theCoalition = theUnit:getGroup():getCoalition() -- make it choppers COALITION\
    local theGroup, theData = cfxZones.createGroundUnitsInZoneForCoalition (\
                theCoalition,                                                 \
                theName, -- group name, may be tracked \
                chopperZone,                                             \
                unitTypes,                                                     \
                conf.dropFormation,\
                90)\
    -- persistence management \
    local troopData = {}\
    troopData.groupData = theData\
    troopData.orders = orders -- always set  \
    troopData.side = theCoalition\
    troopData.range = range\
    troopData.destination = dest -- only for attackzone orders \
    cfxHeloTroops.deployedTroops[theData.name] = troopData \
    \
    local troop = cfxGroundTroops.createGroundTroops(theGroup, range, orders) \
    troop.destination = dest -- transfer target zone for attackzone oders\
    cfxGroundTroops.addGroundTroopsToPool(troop) -- will schedule move orders\
    trigger.action.outTextForGroup(conf.id, \"<\" .. theGroup:getName() .. \"> have deployed to the ground with orders \" .. orders .. \"!\", 30)\
    trigger.action.outSoundForGroup(conf.id, cfxHeloTroops.actionSound) \
    -- see if this is tracked by a tracker, and pass them back so \
    -- they can un-limbo \
    if groupTracker then \
        local isTracking, numTracking, trackers = groupTracker.groupNameTrackedBy(theName)\
        if isTracking then \
            for idx, theTracker in pairs (trackers) do \
                groupTracker.addGroupToTracker(theGroup, theTracker)\
                if cfxHeloTroops.verbose then \
                    trigger.action.outText(\"+++Helo: un-limbo and tracking group <\" .. theName .. \"> with tracker <\" .. theTracker.name .. \">\", 30)\
                end\
            end\
        end\
    end\
end\
\
--\
-- Loading Troops\
--\
function cfxHeloTroops.redirectLoadGroup(args)\
    timer.scheduleFunction(cfxHeloTroops.doLoadGroup, args, timer.getTime() + 0.1)\
end\
\
function cfxHeloTroops.doLoadGroup(args) \
    local conf = args[1]\
    local group = args[2]\
    conf.troopsOnBoard = {}\
    -- all we need to do is disassemble the group into type \
    conf.troopsOnBoard.types = dcsCommon.getGroupTypeString(group)\
    -- get the size \
    conf.troopsOnBoardNum = group:getSize()\
    -- and name \
    local gName = group:getName()\
    conf.troopsOnBoard.name = gName\
    -- and put it all into the helicopter config \
    \
    -- now we need to destroy the group. Let's prepare:\
    -- if it was tracked, tell tracker to move it to limbo \
    -- to remember it even if it's destroyed \
    if groupTracker then\
        -- only if groupTracker is active\
        local isTracking, numTracking, trackers = groupTracker.groupTrackedBy(group)\
        if isTracking then \
            -- we need to put them in limbo for every tracker \
            for idx, aTracker in pairs(trackers) do \
                if cfxHeloTroops.verbose then \
                    trigger.action.outText(\"+++Helo: moving group <\" .. gName .. \"> to limbo for tracker <\" .. aTracker.name .. \">\", 30)\
                end \
                groupTracker.moveGroupToLimboForTracker(group, aTracker)\
            end\
        end\
    end \
    \
    -- then, remove it from the pool\
    local pooledGroup = cfxGroundTroops.getGroundTroopsForGroup(group)\
    if pooledGroup then \
        -- copy some important info from the troops \
        -- if they are set \
        conf.troopsOnBoard.orders = pooledGroup.orders\
        conf.troopsOnBoard.range = pooledGroup.range\
        conf.troopsOnBoard.destination = pooledGroup.destination -- may be nil \
        if pooledGroup.orders and pooledGroup.orders == \"captureandhold\" then \
            conf.troopsOnBoard.destination = nil -- forget last destination so they can be helo-redeployed\
        end \
        cfxGroundTroops.removeTroopsFromPool(pooledGroup)\
        trigger.action.outTextForGroup(conf.id, \"Team '\".. conf.troopsOnBoard.name ..\"' loaded and has orders <\" .. conf.troopsOnBoard.orders .. \">\", 30)\
        --trigger.action.outSoundForGroup(conf.id, cfxHeloTroops.actionSound) --  \"Quest Snare 3.wav\")\
    else \
        if cfxHeloTroops.verbose then \
            trigger.action.outText(\"+++heloT: \".. conf.troopsOnBoard.name ..\" was not committed to ground troops\", 30)\
        end\
    end\
        \
    -- now simply destroy the group\
    -- we'll re-assemble it when we deploy it \
    -- TODO: add weight changing code \
    -- TODO: ensure compatibility with CSAR module\
    group:destroy()\
    \
    -- now immediately run a GC so this group is removed \
    -- from any save data\
    cfxHeloTroops.GC()\
    \
    -- say so \
    trigger.action.outTextForGroup(conf.id, \"Team '\".. conf.troopsOnBoard.name ..\"' aboard, ready to go!\", 30)\
    trigger.action.outSoundForGroup(conf.id, cfxHeloTroops.actionSound) --  \"Quest Snare 3.wav\")\
\
    -- reset menu \
    cfxHeloTroops.removeComms(conf.unit)\
    cfxHeloTroops.setCommsMenu(conf.unit)\
end\
\
--\
-- spawning troops \
--\
function cfxHeloTroops.redirectSpawnGroup(args)\
    timer.scheduleFunction(cfxHeloTroops.doSpawnGroup, args, timer.getTime() + 0.1)\
end\
\
function cfxHeloTroops.delayedCommsResetForUnit(args)\
    local theUnit = args[1]\
    cfxHeloTroops.removeComms(theUnit)\
    cfxHeloTroops.setCommsMenu(theUnit)\
end\
\
function cfxHeloTroops.doSpawnGroup(args)\
    local conf = args[1]\
    local theSpawner = args[2]\
    -- NOTE: theSpawner can be of type cfxSpawnZone !!!OR!!! cfxCloneZones\
    -- make sure cooldown on spawner has timed out, else \
    -- notify that you have to wait \
    local now = timer.getTime()\
    if now < (theSpawner.lastSpawnTimeStamp + theSpawner.cooldown) then \
        local delta = math.floor(theSpawner.lastSpawnTimeStamp + theSpawner.cooldown - now)\
        trigger.action.outTextForGroup(conf.id, \"Still redeploying (\" .. delta .. \" seconds left)\", 30)\
        return \
    end\
    \
    --cfxSpawnZones.spawnWithSpawner(theSpawner) -- old code \
    theSpawner.spawnWithSpawner(theSpawner) -- can be both spawner and cloner \
    trigger.action.outTextForGroup(conf.id, \"Deploying <\" .. theSpawner.baseName .. \"> now...\", 30)\
    \
    -- reset all comms so we can include new troops \
    -- into load menu \
    timer.scheduleFunction(cfxHeloTroops.delayedCommsResetForUnit, {conf.unit, \"ignore\"}, now + 1.0)\
end\
\
-- \
-- handle events \
-- \
function cfxHeloTroops:onEvent(theEvent)\
    local theID = theEvent.id\
    local initiator = theEvent.initiator \
    if not initiator then return end -- not interested \
    local theUnit = initiator \
    local name = theUnit:getName() \
    -- see if this is a player aircraft \
    if not theUnit.getPlayerName then return end -- not a player \
    if not theUnit:getPlayerName() then return end -- not a player \
    \
    -- only for helicopters -- overridedden by troop carriers\
    -- we don't check for cat any more, so any airframe \
    -- can be used as long as it's ok with isTroopCarrier()\
    \
    -- only for troop carriers\
    if not dcsCommon.isTroopCarrier(theUnit, cfxHeloTroops.troopCarriers) then \
        return \
    end\
    \
    if theID == 4 then -- land \
        cfxHeloTroops.heloLanded(theUnit)\
    end\
    \
    if theID == 3 then -- take off \
        cfxHeloTroops.heloDeparted(theUnit)\
    end\
    \
    if theID == 5 then -- crash\
        cfxHeloTroops.heloCrashed(theUnit)\
    end\
    \
    if theID == 20 or  -- player enter \
       theID == 15 then -- birth\
       cfxHeloTroops.cleanHelo(theUnit)\
    end\
    \
    if theID == 21 then -- player leave \
        cfxHeloTroops.cleanHelo(theUnit)\
        local conf = cfxHeloTroops.getConfigForUnitNamed(name)\
        if conf then \
            cfxHeloTroops.removeCommsFromConfig(conf)\
        end\
        return \
    end\
\
    cfxHeloTroops.setCommsMenu(theUnit)    \
end\
\
--\
-- Regular GC and housekeeping\
--\
function cfxHeloTroops.GC()\
    -- GC run. remove all my dead remembered troops\
    local filteredAttackers = {}\
    local before = #cfxHeloTroops.deployedTroops\
    for gName, gData in pairs (cfxHeloTroops.deployedTroops) do \
        -- all we need to do is get the group of that name\
        -- and if it still returns units we are fine \
        local gameGroup = Group.getByName(gName)\
        if gameGroup and gameGroup:isExist() and gameGroup:getSize() > 0 then \
            filteredAttackers[gName] = gData\
        end\
    end\
    cfxHeloTroops.deployedTroops = filteredAttackers\
\
    if cfxHeloTroops.verbose then \
        trigger.action.outText(\"helo troops GC ran: before <\" .. before .. \">, after <\" .. #cfxHeloTroops.deployedTroops .. \">\", 30)\
    end \
end\
\
function cfxHeloTroops.houseKeeping()\
    timer.scheduleFunction(cfxHeloTroops.houseKeeping, {}, timer.getTime() + 5 * 60) -- every 5 minutes \
    cfxHeloTroops.GC()\
end\
\
--\
-- read config zone\
--\
function cfxHeloTroops.readConfigZone()\
    -- note: must match exactly!!!!\
    local theZone = cfxZones.getZoneByName(\"heloTroopsConfig\") \
    if not theZone then \
        theZone = cfxZones.createSimpleZone(\"heloTroopsConfig\")\
    end \
\
    cfxHeloTroops.verbose = theZone:getBoolFromZoneProperty(\"verbose\", false)\
    \
    if theZone:hasProperty(\"legalTroops\") then \
        local theTypesString = theZone:getStringFromZoneProperty(\"legalTroops\", \"\")\
        local unitTypes = dcsCommon.splitString(theTypesString, \",\")\
        if #unitTypes < 1 then \
            unitTypes = {\"Soldier AK\", \"Infantry AK\", \"Infantry AK ver2\", \"Infantry AK ver3\", \"Infantry AK Ins\", \"Soldier M249\", \"Soldier M4 GRG\", \"Soldier M4\", \"Soldier RPG\", \"Paratrooper AKS-74\", \"Paratrooper RPG-16\", \"Stinger comm dsr\", \"Stinger comm\", \"Soldier stinger\", \"SA-18 Igla-S comm\", \"SA-18 Igla-S manpad\", \"Igla manpad INS\", \"SA-18 Igla comm\", \"SA-18 Igla manpad\",} -- default \
        else\
            unitTypes = dcsCommon.trimArray(unitTypes)\
        end\
        cfxHeloTroops.legalTroops = unitTypes\
    end    \
    \
    cfxHeloTroops.troopWeight = theZone:getNumberFromZoneProperty(\"troopWeight\", 100) -- kg average weight per trooper \
    \
    cfxHeloTroops.autoDrop = theZone:getBoolFromZoneProperty(\"autoDrop\", false)    \
    cfxHeloTroops.autoPickup = theZone:getBoolFromZoneProperty(\"autoPickup\", false)\
    cfxHeloTroops.pickupRange = theZone:getNumberFromZoneProperty(\"pickupRange\", 100)\
    cfxHeloTroops.combatDropScore = theZone:getNumberFromZoneProperty( \"combatDropScore\", 200)\
    \
    cfxHeloTroops.actionSound = theZone:getStringFromZoneProperty(\"actionSound\", \"Quest Snare 3.wav\")\
    \
    cfxHeloTroops.requestRange = theZone:getNumberFromZoneProperty(\"requestRange\", 500)\
    -- add own troop carriers \
    if theZone:hasProperty(\"troopCarriers\") then \
        local tc = theZone:getStringFromZoneProperty(\"troopCarriers\", \"UH-1D\")\
        tc = dcsCommon.splitString(tc, \",\")\
        cfxHeloTroops.troopCarriers = dcsCommon.trimArray(tc)\
    end\
end\
\
--\
-- Load / Save data \
--\
function cfxHeloTroops.saveData()\
    local theData = {}\
    local allTroopData = {}\
    -- run a GC pre-emptively \
    cfxHeloTroops.GC()\
    -- now simply iterate and save all deployed troops \
    for gName, gData in pairs(cfxHeloTroops.deployedTroops) do \
        local sData = dcsCommon.clone(gData)\
        dcsCommon.synchGroupData(sData.groupData)\
        allTroopData[gName] = sData\
    end\
    theData.troops = allTroopData\
    return theData\
end\
\
function cfxHeloTroops.loadData()\
    if not persistence then return end \
    local theData = persistence.getSavedDataForModule(\"cfxHeloTroops\")\
    if not theData then \
        if cfxHeloTroops.verbose then \
            trigger.action.outText(\"+++heloT: no save date received, skipping.\", 30)\
        end\
        return\
    end\
    \
    -- simply spawn all troops that we have carried around and \
    -- were still alive when we saved. Troops that were picked \
    -- up by helos never made it to the save file \
    local allTroopData = theData.troops\
    for gName, gdTroop in pairs (allTroopData) do \
        local gData = gdTroop.groupData \
        local orders = gdTroop.orders \
        local side = gdTroop.side \
        local range = gdTroop.range\
        local cty = gData.cty \
        local cat = gData.cat  \
        \
        -- now spawn, but first \
        -- add to my own deployed queue so we can save later \
        local gdClone = dcsCommon.clone(gdTroop)\
        cfxHeloTroops.deployedTroops[gName] = gdClone \
        local theGroup = coalition.addGroup(cty, cat, gData)\
        -- post-proccing for cfxGroundTroops\
\
        -- add to groundTroops \
        local newTroops = cfxGroundTroops.createGroundTroops(theGroup, range, orders) \
        cfxGroundTroops.addGroundTroopsToPool(newTroops)\
    end\
end\
\
\
--\
-- Start \
--\
function cfxHeloTroops.start()\
    -- check libs\
    if not dcsCommon.libCheck(\"cfx Helo Troops\", \
        cfxHeloTroops.requiredLibs) then\
        return false \
    end\
    \
    -- read config zone\
    cfxHeloTroops.readConfigZone()\
    \
    -- start housekeeping \
    cfxHeloTroops.houseKeeping()\
    \
    world.addEventHandler(cfxHeloTroops)\
    trigger.action.outText(\"cf/x Helo Troops v\" .. cfxHeloTroops.version .. \" started\", 30)\
    \
    -- persistence:\
    -- load all save data and populate map with troops that\
    -- we deployed when we last saved. \
    if persistence then \
        -- sign up for persistence \
        callbacks = {}\
        callbacks.persistData = cfxHeloTroops.saveData\
        persistence.registerModule(\"cfxHeloTroops\", callbacks)\
        -- now load my data \
        cfxHeloTroops.loadData()\
    end\
    \
    return true \
end\
\
-- let's get rolling\
if not cfxHeloTroops.start() then \
    trigger.action.outText(\"cf/x Helo Troops aborted: missing libraries\", 30)\
    cfxHeloTroops = nil \
end\
\
\
-- TODO: weight when loading troops ",
                    ["predicate"] = "a_do_script",
                }, -- end of [6]
            }, -- end of ["actions"]
            ["predicate"] = "triggerStart",
            ["comment"] = "Load DML",
        }, -- end of [1]
    }, -- end of ["trigrules"]
    ["currentKey"] = 4246,
    ["failures"] = 
    {
        ["pp_damage_EmergMaxTempr"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "pp_damage_EmergMaxTempr",
            ["mm"] = 0,
        }, -- end of ["pp_damage_EmergMaxTempr"]
        ["CTRL_RUDDER_ROD_MAJOR_DAMAGE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CTRL_RUDDER_ROD_MAJOR_DAMAGE",
            ["mm"] = 0,
        }, -- end of ["CTRL_RUDDER_ROD_MAJOR_DAMAGE"]
        ["WEAP_GUN_00_AMMO_BELT_SEVERED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "WEAP_GUN_00_AMMO_BELT_SEVERED",
            ["mm"] = 0,
        }, -- end of ["WEAP_GUN_00_AMMO_BELT_SEVERED"]
        ["AN_ALR69V_FAILURE_SENSOR_TAIL_RIGHT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AN_ALR69V_FAILURE_SENSOR_TAIL_RIGHT",
            ["mm"] = 0,
        }, -- end of ["AN_ALR69V_FAILURE_SENSOR_TAIL_RIGHT"]
        ["CTRL_ELEVATOR_ROD_DESTROYED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CTRL_ELEVATOR_ROD_DESTROYED",
            ["mm"] = 0,
        }, -- end of ["CTRL_ELEVATOR_ROD_DESTROYED"]
        ["PNEM_RH_BRAKE_HOSE_PERFORATED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "PNEM_RH_BRAKE_HOSE_PERFORATED",
            ["mm"] = 0,
        }, -- end of ["PNEM_RH_BRAKE_HOSE_PERFORATED"]
        ["ADI_UNIT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ADI_UNIT",
            ["mm"] = 0,
        }, -- end of ["ADI_UNIT"]
        ["CTRL_RUDDER_TRIM_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CTRL_RUDDER_TRIM_FAILURE",
            ["mm"] = 0,
        }, -- end of ["CTRL_RUDDER_TRIM_FAILURE"]
        ["tail_reductor_chip"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "tail_reductor_chip",
            ["mm"] = 0,
        }, -- end of ["tail_reductor_chip"]
        ["UNCR_C_STRUT_DOWN_LOCK_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "UNCR_C_STRUT_DOWN_LOCK_FAILURE",
            ["mm"] = 0,
        }, -- end of ["UNCR_C_STRUT_DOWN_LOCK_FAILURE"]
        ["MWMMC_FAILURE_CPU"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MWMMC_FAILURE_CPU",
            ["mm"] = 0,
        }, -- end of ["MWMMC_FAILURE_CPU"]
        ["AN_ALE_40V_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AN_ALE_40V_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["AN_ALE_40V_FAILURE_TOTAL"]
        ["AN_ALE_40V_FAILURE_CONTAINER_LEFT_WING"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AN_ALE_40V_FAILURE_CONTAINER_LEFT_WING",
            ["mm"] = 0,
        }, -- end of ["AN_ALE_40V_FAILURE_CONTAINER_LEFT_WING"]
        ["battery_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "battery_fail",
            ["mm"] = 0,
        }, -- end of ["battery_fail"]
        ["FLEX_S_BKP_LAMP_DEFECTIVE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FLEX_S_BKP_LAMP_DEFECTIVE",
            ["mm"] = 0,
        }, -- end of ["FLEX_S_BKP_LAMP_DEFECTIVE"]
        ["FCS_FAILURE_P_SENSOR_1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_P_SENSOR_1",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_P_SENSOR_1"]
        ["oil_press_drop"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "oil_press_drop",
            ["mm"] = 0,
        }, -- end of ["oil_press_drop"]
        ["Failure_Fuel_RightBoostPump"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Fuel_RightBoostPump",
            ["mm"] = 0,
        }, -- end of ["Failure_Fuel_RightBoostPump"]
        ["Failure_ECS_Valve"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_ECS_Valve",
            ["mm"] = 0,
        }, -- end of ["Failure_ECS_Valve"]
        ["CNI_FAILURE_COM2_SECOS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CNI_FAILURE_COM2_SECOS",
            ["mm"] = 0,
        }, -- end of ["CNI_FAILURE_COM2_SECOS"]
        ["MWMMC_FAILURE_1553B_RWR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MWMMC_FAILURE_1553B_RWR",
            ["mm"] = 0,
        }, -- end of ["MWMMC_FAILURE_1553B_RWR"]
        ["BOOSTER_COIL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BOOSTER_COIL",
            ["mm"] = 0,
        }, -- end of ["BOOSTER_COIL"]
        ["yaw_damper_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "yaw_damper_fail",
            ["mm"] = 0,
        }, -- end of ["yaw_damper_fail"]
        ["pp_damage_OilPump"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "pp_damage_OilPump",
            ["mm"] = 0,
        }, -- end of ["pp_damage_OilPump"]
        ["hydr_serv_leaks"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "hydr_serv_leaks",
            ["mm"] = 0,
        }, -- end of ["hydr_serv_leaks"]
        ["EMMC_FAILURE_BATTERY_FCS1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_BATTERY_FCS1",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_BATTERY_FCS1"]
        ["CADC_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CADC_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["CADC_FAILURE_TOTAL"]
        ["es_damage_MainGenerator"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "es_damage_MainGenerator",
            ["mm"] = 0,
        }, -- end of ["es_damage_MainGenerator"]
        ["WEAPONS_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "WEAPONS_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["WEAPONS_FAILURE_TOTAL"]
        ["FCS_FAILURE_Q_SENSOR_4"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_Q_SENSOR_4",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_Q_SENSOR_4"]
        ["RWR_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWR_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["RWR_FAILURE_TOTAL"]
        ["1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "1",
            ["mm"] = 0,
        }, -- end of ["1"]
        ["CNI_FAILURE_IFF_TX"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CNI_FAILURE_IFF_TX",
            ["mm"] = 0,
        }, -- end of ["CNI_FAILURE_IFF_TX"]
        ["altitude_chain_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "altitude_chain_fail",
            ["mm"] = 0,
        }, -- end of ["altitude_chain_fail"]
        ["2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "2",
            ["mm"] = 0,
        }, -- end of ["2"]
        ["5"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "5",
            ["mm"] = 0,
        }, -- end of ["5"]
        ["4"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "4",
            ["mm"] = 0,
        }, -- end of ["4"]
        ["7"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "7",
            ["mm"] = 0,
        }, -- end of ["7"]
        ["6"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "6",
            ["mm"] = 0,
        }, -- end of ["6"]
        ["LEFT_CYLINDER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "LEFT_CYLINDER",
            ["mm"] = 0,
        }, -- end of ["LEFT_CYLINDER"]
        ["UNCR_DN_HOSES_CLOGGED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "UNCR_DN_HOSES_CLOGGED",
            ["mm"] = 0,
        }, -- end of ["UNCR_DN_HOSES_CLOGGED"]
        ["GUN_LEFT_MG131_OPEN_CIRCUIT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_LEFT_MG131_OPEN_CIRCUIT",
            ["mm"] = 0,
        }, -- end of ["GUN_LEFT_MG131_OPEN_CIRCUIT"]
        ["CTRL_ELEVATOR_ROD_MAJOR_DAMAGE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CTRL_ELEVATOR_ROD_MAJOR_DAMAGE",
            ["mm"] = 0,
        }, -- end of ["CTRL_ELEVATOR_ROD_MAJOR_DAMAGE"]
        ["SUCTION_PUMP_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SUCTION_PUMP_FAILURE",
            ["mm"] = 0,
        }, -- end of ["SUCTION_PUMP_FAILURE"]
        ["fsf_RightBoostPump"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fsf_RightBoostPump",
            ["mm"] = 0,
        }, -- end of ["fsf_RightBoostPump"]
        ["asc_r"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "asc_r",
            ["mm"] = 0,
        }, -- end of ["asc_r"]
        ["BOMBS_SOLENOID_FAULT_LEFT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BOMBS_SOLENOID_FAULT_LEFT",
            ["mm"] = 0,
        }, -- end of ["BOMBS_SOLENOID_FAULT_LEFT"]
        ["BOMBS_ARMING_NO_VOLATAGE_LEFT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BOMBS_ARMING_NO_VOLATAGE_LEFT",
            ["mm"] = 0,
        }, -- end of ["BOMBS_ARMING_NO_VOLATAGE_LEFT"]
        ["ELEC_BOOSTER_FUEL_PUMP_0_COIL_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_BOOSTER_FUEL_PUMP_0_COIL_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_BOOSTER_FUEL_PUMP_0_COIL_FAILURE"]
        ["FUEL_BOOSTER_FUEL_PUMP_0_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_BOOSTER_FUEL_PUMP_0_FAILURE",
            ["mm"] = 0,
        }, -- end of ["FUEL_BOOSTER_FUEL_PUMP_0_FAILURE"]
        ["ELEC_UC_LAMP_LHD_BULB_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_UC_LAMP_LHD_BULB_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_UC_LAMP_LHD_BULB_FAILURE"]
        ["FUEL_VALVE_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_VALVE_LEAK",
            ["mm"] = 0,
        }, -- end of ["FUEL_VALVE_LEAK"]
        ["AN_ALE_40V_FAILURE_CONTAINER_LEFT_GEAR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AN_ALE_40V_FAILURE_CONTAINER_LEFT_GEAR",
            ["mm"] = 0,
        }, -- end of ["AN_ALE_40V_FAILURE_CONTAINER_LEFT_GEAR"]
        ["INST_MASTER_COMPASS_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INST_MASTER_COMPASS_FAILURE",
            ["mm"] = 0,
        }, -- end of ["INST_MASTER_COMPASS_FAILURE"]
        ["CARBAIR_SHORT_CIRCUIT_BLB"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CARBAIR_SHORT_CIRCUIT_BLB",
            ["mm"] = 0,
        }, -- end of ["CARBAIR_SHORT_CIRCUIT_BLB"]
        ["FUEL_LH_WING_TANK_MINOR_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_LH_WING_TANK_MINOR_LEAK",
            ["mm"] = 0,
        }, -- end of ["FUEL_LH_WING_TANK_MINOR_LEAK"]
        ["LEFT_TANK_PUMP_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "LEFT_TANK_PUMP_FAULT",
            ["mm"] = 0,
        }, -- end of ["LEFT_TANK_PUMP_FAULT"]
        ["Failure_Ctrl_LEF"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Ctrl_LEF",
            ["mm"] = 0,
        }, -- end of ["Failure_Ctrl_LEF"]
        ["GUN_FAIL_LEFT_IN_GUN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_FAIL_LEFT_IN_GUN",
            ["mm"] = 0,
        }, -- end of ["GUN_FAIL_LEFT_IN_GUN"]
        ["FDU"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FDU",
            ["mm"] = 0,
        }, -- end of ["FDU"]
        ["ELEC_UC_LAMP_CD_BULB_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_UC_LAMP_CD_BULB_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_UC_LAMP_CD_BULB_FAILURE"]
        ["sas_pitch_left"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "sas_pitch_left",
            ["mm"] = 0,
        }, -- end of ["sas_pitch_left"]
        ["abris_hardware"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "abris_hardware",
            ["mm"] = 0,
        }, -- end of ["abris_hardware"]
        ["hydr2_leaks"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "hydr2_leaks",
            ["mm"] = 0,
        }, -- end of ["hydr2_leaks"]
        ["RWR_FAILURE_CONTROL_BOX"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWR_FAILURE_CONTROL_BOX",
            ["mm"] = 0,
        }, -- end of ["RWR_FAILURE_CONTROL_BOX"]
        ["MSC_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MSC_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["MSC_FAILURE_TOTAL"]
        ["ecm"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ecm",
            ["mm"] = 0,
        }, -- end of ["ecm"]
        ["TGP_FAILURE_RIGHT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TGP_FAILURE_RIGHT",
            ["mm"] = 0,
        }, -- end of ["TGP_FAILURE_RIGHT"]
        ["ap_global_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ap_global_fail",
            ["mm"] = 0,
        }, -- end of ["ap_global_fail"]
        ["hs_damage_AltHydro"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "hs_damage_AltHydro",
            ["mm"] = 0,
        }, -- end of ["hs_damage_AltHydro"]
        ["BAT_SOLENOID_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BAT_SOLENOID_FAULT",
            ["mm"] = 0,
        }, -- end of ["BAT_SOLENOID_FAULT"]
        ["UNCR_LH_STRUT_UP_LOCK_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "UNCR_LH_STRUT_UP_LOCK_FAILURE",
            ["mm"] = 0,
        }, -- end of ["UNCR_LH_STRUT_UP_LOCK_FAILURE"]
        ["asc_a"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "asc_a",
            ["mm"] = 0,
        }, -- end of ["asc_a"]
        ["TURNIND_POINTER_VIBRATES"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TURNIND_POINTER_VIBRATES",
            ["mm"] = 0,
        }, -- end of ["TURNIND_POINTER_VIBRATES"]
        ["WEAP_GUN_05_AMMO_BELT_SEVERED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "WEAP_GUN_05_AMMO_BELT_SEVERED",
            ["mm"] = 0,
        }, -- end of ["WEAP_GUN_05_AMMO_BELT_SEVERED"]
        ["GUN_RIGHT_OUT_AMMUN_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_RIGHT_OUT_AMMUN_FAULT",
            ["mm"] = 0,
        }, -- end of ["GUN_RIGHT_OUT_AMMUN_FAULT"]
        ["GUN_RIGHT_IN_AMMUN_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_RIGHT_IN_AMMUN_FAULT",
            ["mm"] = 0,
        }, -- end of ["GUN_RIGHT_IN_AMMUN_FAULT"]
        ["engine_seized"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "engine_seized",
            ["mm"] = 0,
        }, -- end of ["engine_seized"]
        ["ELEC_GENERATOR_REGULATOR_MALFUNCTION"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_GENERATOR_REGULATOR_MALFUNCTION",
            ["mm"] = 0,
        }, -- end of ["ELEC_GENERATOR_REGULATOR_MALFUNCTION"]
        ["R_GEAR_BRAKE_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "R_GEAR_BRAKE_FAULT",
            ["mm"] = 0,
        }, -- end of ["R_GEAR_BRAKE_FAULT"]
        ["left_front_transfer_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "left_front_transfer_fail",
            ["mm"] = 0,
        }, -- end of ["left_front_transfer_fail"]
        ["COMPASS_NO_TORQUE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "COMPASS_NO_TORQUE",
            ["mm"] = 0,
        }, -- end of ["COMPASS_NO_TORQUE"]
        ["COOLANT_BREAK_BULB"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "COOLANT_BREAK_BULB",
            ["mm"] = 0,
        }, -- end of ["COOLANT_BREAK_BULB"]
        ["L_GEAR_BRAKE_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "L_GEAR_BRAKE_FAULT",
            ["mm"] = 0,
        }, -- end of ["L_GEAR_BRAKE_FAULT"]
        ["MAINPOWER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MAINPOWER",
            ["mm"] = 0,
        }, -- end of ["MAINPOWER"]
        ["fuel_gauges_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fuel_gauges_fail",
            ["mm"] = 0,
        }, -- end of ["fuel_gauges_fail"]
        ["ef_vibration"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ef_vibration",
            ["mm"] = 0,
        }, -- end of ["ef_vibration"]
        ["STARTER_BURNOUT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "STARTER_BURNOUT",
            ["mm"] = 0,
        }, -- end of ["STARTER_BURNOUT"]
        ["SWMMC_FAILURE_CMFCD"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SWMMC_FAILURE_CMFCD",
            ["mm"] = 0,
        }, -- end of ["SWMMC_FAILURE_CMFCD"]
        ["OXYN_PRIMARY_CONTAINER_PERFORATED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "OXYN_PRIMARY_CONTAINER_PERFORATED",
            ["mm"] = 0,
        }, -- end of ["OXYN_PRIMARY_CONTAINER_PERFORATED"]
        ["FAILURE_HYDRAULICS_1_ACCU"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAILURE_HYDRAULICS_1_ACCU",
            ["mm"] = 0,
        }, -- end of ["FAILURE_HYDRAULICS_1_ACCU"]
        ["OIL_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "OIL_LEAK",
            ["mm"] = 0,
        }, -- end of ["OIL_LEAK"]
        ["FCS_FAILURE_LG_3"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_LG_3",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_LG_3"]
        ["OIL_SYSTEM_FAIL_050"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "OIL_SYSTEM_FAIL_050",
            ["mm"] = 0,
        }, -- end of ["OIL_SYSTEM_FAIL_050"]
        ["ENGINE_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENGINE_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["ENGINE_FAILURE_TOTAL"]
        ["incidometer_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "incidometer_fail",
            ["mm"] = 0,
        }, -- end of ["incidometer_fail"]
        ["CTRL_RUDDER_ROD_DESTROYED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CTRL_RUDDER_ROD_DESTROYED",
            ["mm"] = 0,
        }, -- end of ["CTRL_RUDDER_ROD_DESTROYED"]
        ["HYDR1PUMP"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "HYDR1PUMP",
            ["mm"] = 0,
        }, -- end of ["HYDR1PUMP"]
        ["GUN_RIGHT_IN_BARREL_WORN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_RIGHT_IN_BARREL_WORN",
            ["mm"] = 0,
        }, -- end of ["GUN_RIGHT_IN_BARREL_WORN"]
        ["FUEL_FUEL_PUMP_P1_DEGRADED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_FUEL_PUMP_P1_DEGRADED",
            ["mm"] = 0,
        }, -- end of ["FUEL_FUEL_PUMP_P1_DEGRADED"]
        ["FUEL_NITRO_TANK_MAJOR_LEAK"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "FUEL_NITRO_TANK_MAJOR_LEAK",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["FUEL_NITRO_TANK_MAJOR_LEAK"]
        ["FCS_FAILURE_ROLL_AUGD_2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_ROLL_AUGD_2",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_ROLL_AUGD_2"]
        ["PNEM_PRIMARY_CONTAINER_PERFORATED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "PNEM_PRIMARY_CONTAINER_PERFORATED",
            ["mm"] = 0,
        }, -- end of ["PNEM_PRIMARY_CONTAINER_PERFORATED"]
        ["GUN_RIGHT_OUT_BARREL_WORN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_RIGHT_OUT_BARREL_WORN",
            ["mm"] = 0,
        }, -- end of ["GUN_RIGHT_OUT_BARREL_WORN"]
        ["oil_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "oil_fail",
            ["mm"] = 0,
        }, -- end of ["oil_fail"]
        ["L_ENG_POP_STALL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "L_ENG_POP_STALL",
            ["mm"] = 0,
        }, -- end of ["L_ENG_POP_STALL"]
        ["MD1_GYRO_TOTAL_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MD1_GYRO_TOTAL_FAILURE",
            ["mm"] = 0,
        }, -- end of ["MD1_GYRO_TOTAL_FAILURE"]
        ["SWMMC_AAP_NO_RS422_COMM"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SWMMC_AAP_NO_RS422_COMM",
            ["mm"] = 0,
        }, -- end of ["SWMMC_AAP_NO_RS422_COMM"]
        ["ENG0_WATERRADIATOR0_PIERCED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_WATERRADIATOR0_PIERCED",
            ["mm"] = 0,
        }, -- end of ["ENG0_WATERRADIATOR0_PIERCED"]
        ["FUEL_TANK_00_MAJOR_LEAK"] = 
        {
            ["hh"] = 0,
            ["id"] = "FUEL_TANK_00_MAJOR_LEAK",
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["FUEL_TANK_00_MAJOR_LEAK"]
        ["gear_lever_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "gear_lever_fail",
            ["mm"] = 0,
        }, -- end of ["gear_lever_fail"]
        ["F2_BOTTOM_CYLINDER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "F2_BOTTOM_CYLINDER",
            ["mm"] = 0,
        }, -- end of ["F2_BOTTOM_CYLINDER"]
        ["FLEX_S_NO_GUN_SIGN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FLEX_S_NO_GUN_SIGN",
            ["mm"] = 0,
        }, -- end of ["FLEX_S_NO_GUN_SIGN"]
        ["R_ENG_POP_STALL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "R_ENG_POP_STALL",
            ["mm"] = 0,
        }, -- end of ["R_ENG_POP_STALL"]
        ["GUN_LEFT_IN_OPEN_CIRCUIT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_LEFT_IN_OPEN_CIRCUIT",
            ["mm"] = 0,
        }, -- end of ["GUN_LEFT_IN_OPEN_CIRCUIT"]
        ["HYD_PUMP_1_FAIL_100"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "HYD_PUMP_1_FAIL_100",
            ["mm"] = 0,
        }, -- end of ["HYD_PUMP_1_FAIL_100"]
        ["eng_computer_total_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "eng_computer_total_fail",
            ["mm"] = 0,
        }, -- end of ["eng_computer_total_fail"]
        ["hs_damage_GainPump"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "hs_damage_GainPump",
            ["mm"] = 0,
        }, -- end of ["hs_damage_GainPump"]
        ["FCS_FAILURE_PITCH_RATE_GYRO_3"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_PITCH_RATE_GYRO_3",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_PITCH_RATE_GYRO_3"]
        ["CTRL_AILERON_ROD_MAJOR_DAMAGE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CTRL_AILERON_ROD_MAJOR_DAMAGE",
            ["mm"] = 0,
        }, -- end of ["CTRL_AILERON_ROD_MAJOR_DAMAGE"]
        ["CK_UNIT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CK_UNIT",
            ["mm"] = 0,
        }, -- end of ["CK_UNIT"]
        ["electropump_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "electropump_fail",
            ["mm"] = 0,
        }, -- end of ["electropump_fail"]
        ["FUEL_ENGINE0_FUEL_PUMP_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_ENGINE0_FUEL_PUMP_FAILURE",
            ["mm"] = 0,
        }, -- end of ["FUEL_ENGINE0_FUEL_PUMP_FAILURE"]
        ["RWR_FAILURE_ANTENNA_REAR_RIGHT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWR_FAILURE_ANTENNA_REAR_RIGHT",
            ["mm"] = 0,
        }, -- end of ["RWR_FAILURE_ANTENNA_REAR_RIGHT"]
        ["RADARALTUNIT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RADARALTUNIT",
            ["mm"] = 0,
        }, -- end of ["RADARALTUNIT"]
        ["EMMC_FAILURE_DC_GENERATOR_VOLTAGE_LOW"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_DC_GENERATOR_VOLTAGE_LOW",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_DC_GENERATOR_VOLTAGE_LOW"]
        ["CARBAIR_OPEN_CIRCUIT_BLB"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CARBAIR_OPEN_CIRCUIT_BLB",
            ["mm"] = 0,
        }, -- end of ["CARBAIR_OPEN_CIRCUIT_BLB"]
        ["Failure_Comp_ADC"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Comp_ADC",
            ["mm"] = 0,
        }, -- end of ["Failure_Comp_ADC"]
        ["ENG0_OIL_HOSE_1_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_OIL_HOSE_1_LEAK",
            ["mm"] = 0,
        }, -- end of ["ENG0_OIL_HOSE_1_LEAK"]
        ["IMU_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "IMU_FAILURE",
            ["mm"] = 0,
        }, -- end of ["IMU_FAILURE"]
        ["HYDRO_LOW_AIR_PRESSURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "HYDRO_LOW_AIR_PRESSURE",
            ["mm"] = 0,
        }, -- end of ["HYDRO_LOW_AIR_PRESSURE"]
        ["ELEC_GREEN_SIGNAL_LIGHT_BROKEN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_GREEN_SIGNAL_LIGHT_BROKEN",
            ["mm"] = 0,
        }, -- end of ["ELEC_GREEN_SIGNAL_LIGHT_BROKEN"]
        ["FUEL_MAIN_FUEL_PUMP_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_MAIN_FUEL_PUMP_FAILURE",
            ["mm"] = 0,
        }, -- end of ["FUEL_MAIN_FUEL_PUMP_FAILURE"]
        ["ELEC_GENERATOR_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_GENERATOR_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_GENERATOR_FAILURE"]
        ["FR24ANTENNA"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FR24ANTENNA",
            ["mm"] = 0,
        }, -- end of ["FR24ANTENNA"]
        ["FCS_FAILURE_L_ELEVATOR_HYD_2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_L_ELEVATOR_HYD_2",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_L_ELEVATOR_HYD_2"]
        ["GENERATOR_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GENERATOR_FAULT",
            ["mm"] = 0,
        }, -- end of ["GENERATOR_FAULT"]
        ["ssf_static_pressure_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ssf_static_pressure_fail",
            ["mm"] = 0,
        }, -- end of ["ssf_static_pressure_fail"]
        ["FUEL_PUMP_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_PUMP_FAILURE",
            ["mm"] = 0,
        }, -- end of ["FUEL_PUMP_FAILURE"]
        ["EMMC_FAILURE_SCU_DC2AC115V"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_SCU_DC2AC115V",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_SCU_DC2AC115V"]
        ["ELEC_NAVLIGHT_RED_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_NAVLIGHT_RED_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_NAVLIGHT_RED_FAILURE"]
        ["RWR_FAILURE_QUAD45"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWR_FAILURE_QUAD45",
            ["mm"] = 0,
        }, -- end of ["RWR_FAILURE_QUAD45"]
        ["Failure_PP_LeftPTS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_PP_LeftPTS",
            ["mm"] = 0,
        }, -- end of ["Failure_PP_LeftPTS"]
        ["INS_FAILURE_ALT_INVALID"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INS_FAILURE_ALT_INVALID",
            ["mm"] = 0,
        }, -- end of ["INS_FAILURE_ALT_INVALID"]
        ["INS_FAILURE_VELOCITY"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INS_FAILURE_VELOCITY",
            ["mm"] = 0,
        }, -- end of ["INS_FAILURE_VELOCITY"]
        ["ARBS_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ARBS_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["ARBS_FAILURE_TOTAL"]
        ["OXY_FAILURE_R_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "OXY_FAILURE_R_LEAK",
            ["mm"] = 0,
        }, -- end of ["OXY_FAILURE_R_LEAK"]
        ["fs_damage_right_cell_boost_pump"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fs_damage_right_cell_boost_pump",
            ["mm"] = 0,
        }, -- end of ["fs_damage_right_cell_boost_pump"]
        ["GUN_RIGHT_OUT_MOUNT_LOOSE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_RIGHT_OUT_MOUNT_LOOSE",
            ["mm"] = 0,
        }, -- end of ["GUN_RIGHT_OUT_MOUNT_LOOSE"]
        ["FUEL_TANK_03_FIRE"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "FUEL_TANK_03_FIRE",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["FUEL_TANK_03_FIRE"]
        ["ef_fuel_reg"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ef_fuel_reg",
            ["mm"] = 0,
        }, -- end of ["ef_fuel_reg"]
        ["ENG0_WATER_RADIATOR_1_PIERCED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_WATER_RADIATOR_1_PIERCED",
            ["mm"] = 0,
        }, -- end of ["ENG0_WATER_RADIATOR_1_PIERCED"]
        ["L_ENG_SEIZED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "L_ENG_SEIZED",
            ["mm"] = 0,
        }, -- end of ["L_ENG_SEIZED"]
        ["GSH23_CHARGED_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GSH23_CHARGED_FAILURE",
            ["mm"] = 0,
        }, -- end of ["GSH23_CHARGED_FAILURE"]
        ["R_ENG_SEIZED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "R_ENG_SEIZED",
            ["mm"] = 0,
        }, -- end of ["R_ENG_SEIZED"]
        ["TACH_POOR_CONNECTION"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TACH_POOR_CONNECTION",
            ["mm"] = 0,
        }, -- end of ["TACH_POOR_CONNECTION"]
        ["CTRL_RH_SLAT_JAMMED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CTRL_RH_SLAT_JAMMED",
            ["mm"] = 0,
        }, -- end of ["CTRL_RH_SLAT_JAMMED"]
        ["PNEM_ENGINE_HOSE_PERFORATED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "PNEM_ENGINE_HOSE_PERFORATED",
            ["mm"] = 0,
        }, -- end of ["PNEM_ENGINE_HOSE_PERFORATED"]
        ["Failure_PP_EngR_AB_FFCS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_PP_EngR_AB_FFCS",
            ["mm"] = 0,
        }, -- end of ["Failure_PP_EngR_AB_FFCS"]
        ["ENG0_OIL_RADIATOR_1_PIERCED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_OIL_RADIATOR_1_PIERCED",
            ["mm"] = 0,
        }, -- end of ["ENG0_OIL_RADIATOR_1_PIERCED"]
        ["R_GEAR_UPL_JAMMED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "R_GEAR_UPL_JAMMED",
            ["mm"] = 0,
        }, -- end of ["R_GEAR_UPL_JAMMED"]
        ["hydro_left"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "hydro_left",
            ["mm"] = 0,
        }, -- end of ["hydro_left"]
        ["FCS_FAILURE_YAW_LVDT_2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_YAW_LVDT_2",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_YAW_LVDT_2"]
        ["LANDINGGEARL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "LANDINGGEARL",
            ["mm"] = 0,
        }, -- end of ["LANDINGGEARL"]
        ["fs_damage_BoosterPump"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fs_damage_BoosterPump",
            ["mm"] = 0,
        }, -- end of ["fs_damage_BoosterPump"]
        ["UHF_ARC_159_FAILURE_REMOTE_DISPLAY_RIO"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "UHF_ARC_159_FAILURE_REMOTE_DISPLAY_RIO",
            ["mm"] = 0,
        }, -- end of ["UHF_ARC_159_FAILURE_REMOTE_DISPLAY_RIO"]
        ["RWR_FAILURE_DISPLAY_PILOT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWR_FAILURE_DISPLAY_PILOT",
            ["mm"] = 0,
        }, -- end of ["RWR_FAILURE_DISPLAY_PILOT"]
        ["STATION_3_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "STATION_3_FAILURE",
            ["mm"] = 0,
        }, -- end of ["STATION_3_FAILURE"]
        ["CDU_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CDU_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["CDU_FAILURE_TOTAL"]
        ["INT_HYDRO_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INT_HYDRO_LEAK",
            ["mm"] = 0,
        }, -- end of ["INT_HYDRO_LEAK"]
        ["FAILURE_EXT_LIGHT_FORMATION_LEFT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAILURE_EXT_LIGHT_FORMATION_LEFT",
            ["mm"] = 0,
        }, -- end of ["FAILURE_EXT_LIGHT_FORMATION_LEFT"]
        ["STARTER_WIRING"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "STARTER_WIRING",
            ["mm"] = 0,
        }, -- end of ["STARTER_WIRING"]
        ["right_airbrake_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "right_airbrake_fail",
            ["mm"] = 0,
        }, -- end of ["right_airbrake_fail"]
        ["pitch_trim_runaway_up"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "pitch_trim_runaway_up",
            ["mm"] = 0,
        }, -- end of ["pitch_trim_runaway_up"]
        ["ELEVONSERVINNERRIGHT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEVONSERVINNERRIGHT",
            ["mm"] = 0,
        }, -- end of ["ELEVONSERVINNERRIGHT"]
        ["VHF_ANTENNA_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "VHF_ANTENNA_FAULT",
            ["mm"] = 0,
        }, -- end of ["VHF_ANTENNA_FAULT"]
        ["es_damage_GeneratorLeft"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "es_damage_GeneratorLeft",
            ["mm"] = 0,
        }, -- end of ["es_damage_GeneratorLeft"]
        ["engine_chip"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "engine_chip",
            ["mm"] = 0,
        }, -- end of ["engine_chip"]
        ["GUN_LEFT_CENTER_AMMUN_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_LEFT_CENTER_AMMUN_FAULT",
            ["mm"] = 0,
        }, -- end of ["GUN_LEFT_CENTER_AMMUN_FAULT"]
        ["CADC_FAILURE_MACH"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CADC_FAILURE_MACH",
            ["mm"] = 0,
        }, -- end of ["CADC_FAILURE_MACH"]
        ["Failure_Fuel_ExtTankTransferR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Fuel_ExtTankTransferR",
            ["mm"] = 0,
        }, -- end of ["Failure_Fuel_ExtTankTransferR"]
        ["RightEngine_ShaveInOil"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RightEngine_ShaveInOil",
            ["mm"] = 0,
        }, -- end of ["RightEngine_ShaveInOil"]
        ["ELEC_ROCKETS_RH_LOOM_SEVERED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_ROCKETS_RH_LOOM_SEVERED",
            ["mm"] = 0,
        }, -- end of ["ELEC_ROCKETS_RH_LOOM_SEVERED"]
        ["rudder_loss"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "rudder_loss",
            ["mm"] = 0,
        }, -- end of ["rudder_loss"]
        ["WEAP_GUN_06_DAMAGED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "WEAP_GUN_06_DAMAGED",
            ["mm"] = 0,
        }, -- end of ["WEAP_GUN_06_DAMAGED"]
        ["ENG0_WATER_RADIATOR_1_MAJOR_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_WATER_RADIATOR_1_MAJOR_LEAK",
            ["mm"] = 0,
        }, -- end of ["ENG0_WATER_RADIATOR_1_MAJOR_LEAK"]
        ["tacan_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "tacan_fail",
            ["mm"] = 0,
        }, -- end of ["tacan_fail"]
        ["BOMBS_NO_VOLATAGE_BOTH"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BOMBS_NO_VOLATAGE_BOTH",
            ["mm"] = 0,
        }, -- end of ["BOMBS_NO_VOLATAGE_BOTH"]
        ["FR22RADIO"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FR22RADIO",
            ["mm"] = 0,
        }, -- end of ["FR22RADIO"]
        ["CARBAIR_SHORT_CIRCUIT_LEADS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CARBAIR_SHORT_CIRCUIT_LEADS",
            ["mm"] = 0,
        }, -- end of ["CARBAIR_SHORT_CIRCUIT_LEADS"]
        ["PITOT_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "PITOT_FAILURE",
            ["mm"] = 0,
        }, -- end of ["PITOT_FAILURE"]
        ["ELEC_BOOSTER_FUEL_PUMP_2_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_BOOSTER_FUEL_PUMP_2_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_BOOSTER_FUEL_PUMP_2_FAILURE"]
        ["SNS_FAILURE_COMPUTER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SNS_FAILURE_COMPUTER",
            ["mm"] = 0,
        }, -- end of ["SNS_FAILURE_COMPUTER"]
        ["csf_PitchTrim"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "csf_PitchTrim",
            ["mm"] = 0,
        }, -- end of ["csf_PitchTrim"]
        ["CANARDSERVOLEFT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CANARDSERVOLEFT",
            ["mm"] = 0,
        }, -- end of ["CANARDSERVOLEFT"]
        ["nosecone_stuck_forward"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "nosecone_stuck_forward",
            ["mm"] = 0,
        }, -- end of ["nosecone_stuck_forward"]
        ["trim_yaw_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "trim_yaw_fail",
            ["mm"] = 0,
        }, -- end of ["trim_yaw_fail"]
        ["FWD_TANK_PUMP_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FWD_TANK_PUMP_FAULT",
            ["mm"] = 0,
        }, -- end of ["FWD_TANK_PUMP_FAULT"]
        ["CNI_FAILURE_COM1_SECOS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CNI_FAILURE_COM1_SECOS",
            ["mm"] = 0,
        }, -- end of ["CNI_FAILURE_COM1_SECOS"]
        ["Failure_Fuel_Tank4Transfer"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Fuel_Tank4Transfer",
            ["mm"] = 0,
        }, -- end of ["Failure_Fuel_Tank4Transfer"]
        ["Failure_PP_EngR_OilLeak"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_PP_EngR_OilLeak",
            ["mm"] = 0,
        }, -- end of ["Failure_PP_EngR_OilLeak"]
        ["RKL_41_TOTAL_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RKL_41_TOTAL_FAILURE",
            ["mm"] = 0,
        }, -- end of ["RKL_41_TOTAL_FAILURE"]
        ["EMMC_FAILURE_DADS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_DADS",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_DADS"]
        ["hs_damage_UtilityHydro"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "hs_damage_UtilityHydro",
            ["mm"] = 0,
        }, -- end of ["hs_damage_UtilityHydro"]
        ["ILS_FAILURE_ANT_MARKER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ILS_FAILURE_ANT_MARKER",
            ["mm"] = 0,
        }, -- end of ["ILS_FAILURE_ANT_MARKER"]
        ["FCS_FAILURE_PITCH_RATE_GYRO_1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_PITCH_RATE_GYRO_1",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_PITCH_RATE_GYRO_1"]
        ["MWMMC_FAILURE_1553B_TACAN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MWMMC_FAILURE_1553B_TACAN",
            ["mm"] = 0,
        }, -- end of ["MWMMC_FAILURE_1553B_TACAN"]
        ["HYDR_UNLOAD_VALVE_NOT_UNLOAD"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "HYDR_UNLOAD_VALVE_NOT_UNLOAD",
            ["mm"] = 0,
        }, -- end of ["HYDR_UNLOAD_VALVE_NOT_UNLOAD"]
        ["FCS_FAILURE_L_ELEVATOR_ELEC_D"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_L_ELEVATOR_ELEC_D",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_L_ELEVATOR_ELEC_D"]
        ["FUEL_TANK_01_MEDIUM_LEAK"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "FUEL_TANK_01_MEDIUM_LEAK",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["FUEL_TANK_01_MEDIUM_LEAK"]
        ["EPRSENSOR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EPRSENSOR",
            ["mm"] = 0,
        }, -- end of ["EPRSENSOR"]
        ["MWMMC_FAILURE_1553B_IFF"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MWMMC_FAILURE_1553B_IFF",
            ["mm"] = 0,
        }, -- end of ["MWMMC_FAILURE_1553B_IFF"]
        ["RWRUNIT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWRUNIT",
            ["mm"] = 0,
        }, -- end of ["RWRUNIT"]
        ["ENG_ALT_2_FAIL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG_ALT_2_FAIL",
            ["mm"] = 0,
        }, -- end of ["ENG_ALT_2_FAIL"]
        ["ZCP_FAILURE_MALFUNC"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ZCP_FAILURE_MALFUNC",
            ["mm"] = 0,
        }, -- end of ["ZCP_FAILURE_MALFUNC"]
        ["ICS_FAILURE_AMPLIFIER_PILOT_NORM"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ICS_FAILURE_AMPLIFIER_PILOT_NORM",
            ["mm"] = 0,
        }, -- end of ["ICS_FAILURE_AMPLIFIER_PILOT_NORM"]
        ["oxy_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "oxy_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["oxy_FAILURE_TOTAL"]
        ["ICS_FAILURE_AMPLIFIER_RIO_NORM"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ICS_FAILURE_AMPLIFIER_RIO_NORM",
            ["mm"] = 0,
        }, -- end of ["ICS_FAILURE_AMPLIFIER_RIO_NORM"]
        ["left_feeder_leaks"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "left_feeder_leaks",
            ["mm"] = 0,
        }, -- end of ["left_feeder_leaks"]
        ["ENGINE_FAILURE_N2_TURBINE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENGINE_FAILURE_N2_TURBINE",
            ["mm"] = 0,
        }, -- end of ["ENGINE_FAILURE_N2_TURBINE"]
        ["BOMBS_ARMING_BROKEN_SOLENOID_RIGHT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BOMBS_ARMING_BROKEN_SOLENOID_RIGHT",
            ["mm"] = 0,
        }, -- end of ["BOMBS_ARMING_BROKEN_SOLENOID_RIGHT"]
        ["EXT_HYDRO_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EXT_HYDRO_LEAK",
            ["mm"] = 0,
        }, -- end of ["EXT_HYDRO_LEAK"]
        ["DNS_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "DNS_FAILURE",
            ["mm"] = 0,
        }, -- end of ["DNS_FAILURE"]
        ["gyros_main_att_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "gyros_main_att_fail",
            ["mm"] = 0,
        }, -- end of ["gyros_main_att_fail"]
        ["Failure_PP_EngL_Nozzle_CS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_PP_EngL_Nozzle_CS",
            ["mm"] = 0,
        }, -- end of ["Failure_PP_EngL_Nozzle_CS"]
        ["R_ENG_FIRE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "R_ENG_FIRE",
            ["mm"] = 0,
        }, -- end of ["R_ENG_FIRE"]
        ["FCS_FAILURE_COMP_2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_COMP_2",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_COMP_2"]
        ["L_ENG_FIRE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "L_ENG_FIRE",
            ["mm"] = 0,
        }, -- end of ["L_ENG_FIRE"]
        ["FCS_FAILURE_PITCH_LVDT_3"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_PITCH_LVDT_3",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_PITCH_LVDT_3"]
        ["W_S_R"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "W_S_R",
            ["mm"] = 0,
        }, -- end of ["W_S_R"]
        ["SAR_2_95"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "SAR_2_95",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["SAR_2_95"]
        ["UNCR_C_STRUT_UP_LOCK_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "UNCR_C_STRUT_UP_LOCK_FAILURE",
            ["mm"] = 0,
        }, -- end of ["UNCR_C_STRUT_UP_LOCK_FAILURE"]
        ["EZ42_NO_POWER_SUPPLY"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EZ42_NO_POWER_SUPPLY",
            ["mm"] = 0,
        }, -- end of ["EZ42_NO_POWER_SUPPLY"]
        ["MWMMC_FAILURE_AVI"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MWMMC_FAILURE_AVI",
            ["mm"] = 0,
        }, -- end of ["MWMMC_FAILURE_AVI"]
        ["FCS_FAILURE_NZ_SENSOR_3"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_NZ_SENSOR_3",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_NZ_SENSOR_3"]
        ["PUMP_SEPARATOR_CLOGGED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "PUMP_SEPARATOR_CLOGGED",
            ["mm"] = 0,
        }, -- end of ["PUMP_SEPARATOR_CLOGGED"]
        ["Vibration_LeftEngine"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "Vibration_LeftEngine",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["Vibration_LeftEngine"]
        ["explosive_depressurization"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "explosive_depressurization",
            ["mm"] = 0,
        }, -- end of ["explosive_depressurization"]
        ["engine_flameout_recoverable"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "engine_flameout_recoverable",
            ["mm"] = 0,
        }, -- end of ["engine_flameout_recoverable"]
        ["DOORS_TV_JAMMED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "DOORS_TV_JAMMED",
            ["mm"] = 0,
        }, -- end of ["DOORS_TV_JAMMED"]
        ["UHF_ARC_159_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "UHF_ARC_159_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["UHF_ARC_159_FAILURE_TOTAL"]
        ["AN_ALR69V_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AN_ALR69V_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["AN_ALR69V_FAILURE_TOTAL"]
        ["Failure_Hyd_HYD1A_Leak"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Hyd_HYD1A_Leak",
            ["mm"] = 0,
        }, -- end of ["Failure_Hyd_HYD1A_Leak"]
        ["R_GEAR_MOTOR_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "R_GEAR_MOTOR_FAULT",
            ["mm"] = 0,
        }, -- end of ["R_GEAR_MOTOR_FAULT"]
        ["VHF_ARC_182_FAILURE_REMOTE_DISPLAY"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "VHF_ARC_182_FAILURE_REMOTE_DISPLAY",
            ["mm"] = 0,
        }, -- end of ["VHF_ARC_182_FAILURE_REMOTE_DISPLAY"]
        ["PNEM_MACHINE_GUNS_HOSE_PERFORATED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "PNEM_MACHINE_GUNS_HOSE_PERFORATED",
            ["mm"] = 0,
        }, -- end of ["PNEM_MACHINE_GUNS_HOSE_PERFORATED"]
        ["FCS_FAILURE_ROLL_ELEC_SERVO_1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_ROLL_ELEC_SERVO_1",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_ROLL_ELEC_SERVO_1"]
        ["ENGINE_FAILURE_AB_IGNITION_UNIT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENGINE_FAILURE_AB_IGNITION_UNIT",
            ["mm"] = 0,
        }, -- end of ["ENGINE_FAILURE_AB_IGNITION_UNIT"]
        ["R_ENG_COMPRESSOR_STALL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "R_ENG_COMPRESSOR_STALL",
            ["mm"] = 0,
        }, -- end of ["R_ENG_COMPRESSOR_STALL"]
        ["partial_comp_stall"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "partial_comp_stall",
            ["mm"] = 0,
        }, -- end of ["partial_comp_stall"]
        ["R_ENG_AICS_RAMP_FAIL_IN_POS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "R_ENG_AICS_RAMP_FAIL_IN_POS",
            ["mm"] = 0,
        }, -- end of ["R_ENG_AICS_RAMP_FAIL_IN_POS"]
        ["helmet"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "helmet",
            ["mm"] = 0,
        }, -- end of ["helmet"]
        ["batteries_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "batteries_fail",
            ["mm"] = 0,
        }, -- end of ["batteries_fail"]
        ["hs_damage_MainPump"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "hs_damage_MainPump",
            ["mm"] = 0,
        }, -- end of ["hs_damage_MainPump"]
        ["MainReductor_ShaveInOil"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MainReductor_ShaveInOil",
            ["mm"] = 0,
        }, -- end of ["MainReductor_ShaveInOil"]
        ["COOLANT_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "COOLANT_LEAK",
            ["mm"] = 0,
        }, -- end of ["COOLANT_LEAK"]
        ["FUEL_FUEL_PUMP_P2_DEGRADED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_FUEL_PUMP_P2_DEGRADED",
            ["mm"] = 0,
        }, -- end of ["FUEL_FUEL_PUMP_P2_DEGRADED"]
        ["PNEM_SECONDARY_CONTAINER_PERFORATED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "PNEM_SECONDARY_CONTAINER_PERFORATED",
            ["mm"] = 0,
        }, -- end of ["PNEM_SECONDARY_CONTAINER_PERFORATED"]
        ["PNEM_RH_FLAP_JACK_BUSTED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "PNEM_RH_FLAP_JACK_BUSTED",
            ["mm"] = 0,
        }, -- end of ["PNEM_RH_FLAP_JACK_BUSTED"]
        ["WEAP_GUN_02_AMMO_BELT_SEVERED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "WEAP_GUN_02_AMMO_BELT_SEVERED",
            ["mm"] = 0,
        }, -- end of ["WEAP_GUN_02_AMMO_BELT_SEVERED"]
        ["AAR_47_FAILURE_SENSOR_LEFT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AAR_47_FAILURE_SENSOR_LEFT",
            ["mm"] = 0,
        }, -- end of ["AAR_47_FAILURE_SENSOR_LEFT"]
        ["VHF_ARC_182_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "VHF_ARC_182_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["VHF_ARC_182_FAILURE_TOTAL"]
        ["GUN_LEFT_MG131_BARREL_WORN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_LEFT_MG131_BARREL_WORN",
            ["mm"] = 0,
        }, -- end of ["GUN_LEFT_MG131_BARREL_WORN"]
        ["OXYN_PRIMARY_CONTAINER_MINOR_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "OXYN_PRIMARY_CONTAINER_MINOR_LEAK",
            ["mm"] = 0,
        }, -- end of ["OXYN_PRIMARY_CONTAINER_MINOR_LEAK"]
        ["PPF_RE_TEMPER_LIM_OFF"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "PPF_RE_TEMPER_LIM_OFF",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["PPF_RE_TEMPER_LIM_OFF"]
        ["FCS_FAILURE_R_ELEVATOR_ELEC_A"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_R_ELEVATOR_ELEC_A",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_R_ELEVATOR_ELEC_A"]
        ["DC_BUS_GENERATOR_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "DC_BUS_GENERATOR_FAILURE",
            ["mm"] = 0,
        }, -- end of ["DC_BUS_GENERATOR_FAILURE"]
        ["ELEC_BATTERY_DESTROYED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_BATTERY_DESTROYED",
            ["mm"] = 0,
        }, -- end of ["ELEC_BATTERY_DESTROYED"]
        ["Failure_Sens_LeftPitotHeater"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Sens_LeftPitotHeater",
            ["mm"] = 0,
        }, -- end of ["Failure_Sens_LeftPitotHeater"]
        ["HYD_ALT_2_FAIL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "HYD_ALT_2_FAIL",
            ["mm"] = 0,
        }, -- end of ["HYD_ALT_2_FAIL"]
        ["TAIL_GEAR_FAIL_GO_DOWN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TAIL_GEAR_FAIL_GO_DOWN",
            ["mm"] = 0,
        }, -- end of ["TAIL_GEAR_FAIL_GO_DOWN"]
        ["GUN_FAIL_RIGHT_CENTER_GUN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_FAIL_RIGHT_CENTER_GUN",
            ["mm"] = 0,
        }, -- end of ["GUN_FAIL_RIGHT_CENTER_GUN"]
        ["LeftEngine_ShaveInOil"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "LeftEngine_ShaveInOil",
            ["mm"] = 0,
        }, -- end of ["LeftEngine_ShaveInOil"]
        ["ELEC_UC_LAMP_RHD_BULB_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_UC_LAMP_RHD_BULB_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_UC_LAMP_RHD_BULB_FAILURE"]
        ["MAIN_R_GEAR_D_LOCK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MAIN_R_GEAR_D_LOCK",
            ["mm"] = 0,
        }, -- end of ["MAIN_R_GEAR_D_LOCK"]
        ["FAILURE_HYDRAULICS_2_EXTERNAL_LEAKAGE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAILURE_HYDRAULICS_2_EXTERNAL_LEAKAGE",
            ["mm"] = 0,
        }, -- end of ["FAILURE_HYDRAULICS_2_EXTERNAL_LEAKAGE"]
        ["ENGINE_FAILURE_N2_COMPRESSOR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENGINE_FAILURE_N2_COMPRESSOR",
            ["mm"] = 0,
        }, -- end of ["ENGINE_FAILURE_N2_COMPRESSOR"]
        ["fs_damage_MainPump"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fs_damage_MainPump",
            ["mm"] = 0,
        }, -- end of ["fs_damage_MainPump"]
        ["ELEC_FUEL_PUMP_P1_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_FUEL_PUMP_P1_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_FUEL_PUMP_P1_FAILURE"]
        ["K14_FIXED_LAMP_DEFECTIVE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "K14_FIXED_LAMP_DEFECTIVE",
            ["mm"] = 0,
        }, -- end of ["K14_FIXED_LAMP_DEFECTIVE"]
        ["RDR_FAILURE_RX_FRONT_END_DEGRATION"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RDR_FAILURE_RX_FRONT_END_DEGRATION",
            ["mm"] = 0,
        }, -- end of ["RDR_FAILURE_RX_FRONT_END_DEGRATION"]
        ["EMMC_FAILURE_ELECT_EQUIP_HOT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_ELECT_EQUIP_HOT",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_ELECT_EQUIP_HOT"]
        ["CTRL_LANDING_FLAPS_RH_HOSE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CTRL_LANDING_FLAPS_RH_HOSE",
            ["mm"] = 0,
        }, -- end of ["CTRL_LANDING_FLAPS_RH_HOSE"]
        ["Failure_Elec_LeftGenerator"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Elec_LeftGenerator",
            ["mm"] = 0,
        }, -- end of ["Failure_Elec_LeftGenerator"]
        ["BOMBS_ARMING_BROKEN_WIRING"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BOMBS_ARMING_BROKEN_WIRING",
            ["mm"] = 0,
        }, -- end of ["BOMBS_ARMING_BROKEN_WIRING"]
        ["RWR_FAILURE_SENSOR_LEFT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWR_FAILURE_SENSOR_LEFT",
            ["mm"] = 0,
        }, -- end of ["RWR_FAILURE_SENSOR_LEFT"]
        ["MWMMC_FAILURE_1553B_ILS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MWMMC_FAILURE_1553B_ILS",
            ["mm"] = 0,
        }, -- end of ["MWMMC_FAILURE_1553B_ILS"]
        ["gyros_att_indicator"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "gyros_att_indicator",
            ["mm"] = 0,
        }, -- end of ["gyros_att_indicator"]
        ["TOP_CYLINDER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TOP_CYLINDER",
            ["mm"] = 0,
        }, -- end of ["TOP_CYLINDER"]
        ["RADAR_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RADAR_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["RADAR_FAILURE_TOTAL"]
        ["PNEM_FLAPS_HOSE_PERFORATED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "PNEM_FLAPS_HOSE_PERFORATED",
            ["mm"] = 0,
        }, -- end of ["PNEM_FLAPS_HOSE_PERFORATED"]
        ["KPP_1273_GYRO_TOTAL_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "KPP_1273_GYRO_TOTAL_FAILURE",
            ["mm"] = 0,
        }, -- end of ["KPP_1273_GYRO_TOTAL_FAILURE"]
        ["front_central_leaks"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "front_central_leaks",
            ["mm"] = 0,
        }, -- end of ["front_central_leaks"]
        ["L_GEAR_UPL_NOT_LOCK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "L_GEAR_UPL_NOT_LOCK",
            ["mm"] = 0,
        }, -- end of ["L_GEAR_UPL_NOT_LOCK"]
        ["FUEL_MAIN_FUEL_PUMP_DEGRADED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_MAIN_FUEL_PUMP_DEGRADED",
            ["mm"] = 0,
        }, -- end of ["FUEL_MAIN_FUEL_PUMP_DEGRADED"]
        ["sas_yaw_left"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "sas_yaw_left",
            ["mm"] = 0,
        }, -- end of ["sas_yaw_left"]
        ["AC_BUS_PO7501_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AC_BUS_PO7501_FAILURE",
            ["mm"] = 0,
        }, -- end of ["AC_BUS_PO7501_FAILURE"]
        ["FAILURE_EXT_LIGHT_TAXI"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAILURE_EXT_LIGHT_TAXI",
            ["mm"] = 0,
        }, -- end of ["FAILURE_EXT_LIGHT_TAXI"]
        ["OIL_T_IND_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "OIL_T_IND_FAULT",
            ["mm"] = 0,
        }, -- end of ["OIL_T_IND_FAULT"]
        ["ELEC_RH_FLAPS_DRIVE_WIRE_SEVERED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_RH_FLAPS_DRIVE_WIRE_SEVERED",
            ["mm"] = 0,
        }, -- end of ["ELEC_RH_FLAPS_DRIVE_WIRE_SEVERED"]
        ["R_GEAR_UPL_NOT_LOCK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "R_GEAR_UPL_NOT_LOCK",
            ["mm"] = 0,
        }, -- end of ["R_GEAR_UPL_NOT_LOCK"]
        ["MWMMC_FAILURE_1553B_SPJ"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MWMMC_FAILURE_1553B_SPJ",
            ["mm"] = 0,
        }, -- end of ["MWMMC_FAILURE_1553B_SPJ"]
        ["CNI_FAILURE_TACAN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CNI_FAILURE_TACAN",
            ["mm"] = 0,
        }, -- end of ["CNI_FAILURE_TACAN"]
        ["RightEngine_Fire"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RightEngine_Fire",
            ["mm"] = 0,
        }, -- end of ["RightEngine_Fire"]
        ["VHF_ARC_182_FAILURE_DISPLAY"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "VHF_ARC_182_FAILURE_DISPLAY",
            ["mm"] = 0,
        }, -- end of ["VHF_ARC_182_FAILURE_DISPLAY"]
        ["UNCR_LH_STRUT_UP_LOCK_JAMMED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "UNCR_LH_STRUT_UP_LOCK_JAMMED",
            ["mm"] = 0,
        }, -- end of ["UNCR_LH_STRUT_UP_LOCK_JAMMED"]
        ["ILS_FAILURE_ANTENNA"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ILS_FAILURE_ANTENNA",
            ["mm"] = 0,
        }, -- end of ["ILS_FAILURE_ANTENNA"]
        ["pp_damage_OilSeparator"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "pp_damage_OilSeparator",
            ["mm"] = 0,
        }, -- end of ["pp_damage_OilSeparator"]
        ["BOMBS_ARMING_BROKEN_SOLENOID_LEFT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BOMBS_ARMING_BROKEN_SOLENOID_LEFT",
            ["mm"] = 0,
        }, -- end of ["BOMBS_ARMING_BROKEN_SOLENOID_LEFT"]
        ["Failure_PP_EngR_Main_FFCS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_PP_EngR_Main_FFCS",
            ["mm"] = 0,
        }, -- end of ["Failure_PP_EngR_Main_FFCS"]
        ["FUEL_NITRO_TANK_LEAK_STOPPED"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "FUEL_NITRO_TANK_LEAK_STOPPED",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["FUEL_NITRO_TANK_LEAK_STOPPED"]
        ["FUEL_TANK_03_FIRE_STOPPED"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "FUEL_TANK_03_FIRE_STOPPED",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["FUEL_TANK_03_FIRE_STOPPED"]
        ["FCS_FAILURE_EFCS_2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_EFCS_2",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_EFCS_2"]
        ["Failure_Hyd_IsolatedHYD2BSystem_Leak"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Hyd_IsolatedHYD2BSystem_Leak",
            ["mm"] = 0,
        }, -- end of ["Failure_Hyd_IsolatedHYD2BSystem_Leak"]
        ["COMPASS_POINTER_PULLS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "COMPASS_POINTER_PULLS",
            ["mm"] = 0,
        }, -- end of ["COMPASS_POINTER_PULLS"]
        ["GUN_RIGHT_OUT_OPEN_CIRCUIT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_RIGHT_OUT_OPEN_CIRCUIT",
            ["mm"] = 0,
        }, -- end of ["GUN_RIGHT_OUT_OPEN_CIRCUIT"]
        ["ELEC_NAVLIGHT_WHITE_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_NAVLIGHT_WHITE_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_NAVLIGHT_WHITE_FAILURE"]
        ["AIRBRAKE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AIRBRAKE",
            ["mm"] = 0,
        }, -- end of ["AIRBRAKE"]
        ["BARAX_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BARAX_fail",
            ["mm"] = 0,
        }, -- end of ["BARAX_fail"]
        ["TURNIND_INCORRECT_SENS_VAC_HIGH"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TURNIND_INCORRECT_SENS_VAC_HIGH",
            ["mm"] = 0,
        }, -- end of ["TURNIND_INCORRECT_SENS_VAC_HIGH"]
        ["IGNITION_NO_OUTPUT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "IGNITION_NO_OUTPUT",
            ["mm"] = 0,
        }, -- end of ["IGNITION_NO_OUTPUT"]
        ["BOMBS_ARMING_BROKEN_WIRING_RIGHT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BOMBS_ARMING_BROKEN_WIRING_RIGHT",
            ["mm"] = 0,
        }, -- end of ["BOMBS_ARMING_BROKEN_WIRING_RIGHT"]
        ["chip_in_oil"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "chip_in_oil",
            ["mm"] = 0,
        }, -- end of ["chip_in_oil"]
        ["ELEC_BOOSTER_FUEL_PUMP_1_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_BOOSTER_FUEL_PUMP_1_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_BOOSTER_FUEL_PUMP_1_FAILURE"]
        ["COMPASS_ERRATIC_OPERATION"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "COMPASS_ERRATIC_OPERATION",
            ["mm"] = 0,
        }, -- end of ["COMPASS_ERRATIC_OPERATION"]
        ["TID_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TID_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["TID_FAILURE_TOTAL"]
        ["slat_outer_left_stuck"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "slat_outer_left_stuck",
            ["mm"] = 0,
        }, -- end of ["slat_outer_left_stuck"]
        ["ELEVONSERVOUTERRIGHT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEVONSERVOUTERRIGHT",
            ["mm"] = 0,
        }, -- end of ["ELEVONSERVOUTERRIGHT"]
        ["ELEC_UC_LAMP_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_UC_LAMP_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_UC_LAMP_FAILURE"]
        ["STATION_5_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "STATION_5_FAILURE",
            ["mm"] = 0,
        }, -- end of ["STATION_5_FAILURE"]
        ["right_rear_transfer_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "right_rear_transfer_fail",
            ["mm"] = 0,
        }, -- end of ["right_rear_transfer_fail"]
        ["EMMC_FAILURE_SHARS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_SHARS",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_SHARS"]
        ["D2_LEFT_CYLINDER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "D2_LEFT_CYLINDER",
            ["mm"] = 0,
        }, -- end of ["D2_LEFT_CYLINDER"]
        ["Surge_LeftEngine"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Surge_LeftEngine",
            ["mm"] = 0,
        }, -- end of ["Surge_LeftEngine"]
        ["BOMBS_RUST_LEFT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BOMBS_RUST_LEFT",
            ["mm"] = 0,
        }, -- end of ["BOMBS_RUST_LEFT"]
        ["STATION_7_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "STATION_7_FAILURE",
            ["mm"] = 0,
        }, -- end of ["STATION_7_FAILURE"]
        ["ARN_83_ADF_DAMAGE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ARN_83_ADF_DAMAGE",
            ["mm"] = 0,
        }, -- end of ["ARN_83_ADF_DAMAGE"]
        ["csf_PitchDamper"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "csf_PitchDamper",
            ["mm"] = 0,
        }, -- end of ["csf_PitchDamper"]
        ["asc"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "asc",
            ["mm"] = 0,
        }, -- end of ["asc"]
        ["FAILURE_HYDRAULICS_EMERGE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAILURE_HYDRAULICS_EMERGE",
            ["mm"] = 0,
        }, -- end of ["FAILURE_HYDRAULICS_EMERGE"]
        ["ENG0_OILRADIATOR0_PIERCED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_OILRADIATOR0_PIERCED",
            ["mm"] = 0,
        }, -- end of ["ENG0_OILRADIATOR0_PIERCED"]
        ["DTC_FAILURE_DATA_DECIPHER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "DTC_FAILURE_DATA_DECIPHER",
            ["mm"] = 0,
        }, -- end of ["DTC_FAILURE_DATA_DECIPHER"]
        ["ENG0_CARBURETTOR_OIL_FEED_CLOGGED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_CARBURETTOR_OIL_FEED_CLOGGED",
            ["mm"] = 0,
        }, -- end of ["ENG0_CARBURETTOR_OIL_FEED_CLOGGED"]
        ["FCS_FAILURE_WOW_1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_WOW_1",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_WOW_1"]
        ["UNLOAD_VALVE_NOT_UNLOAD"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "UNLOAD_VALVE_NOT_UNLOAD",
            ["mm"] = 0,
        }, -- end of ["UNLOAD_VALVE_NOT_UNLOAD"]
        ["EMMC_FAILURE_SCU_AC2AC36V"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_SCU_AC2AC36V",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_SCU_AC2AC36V"]
        ["UNLOAD_VALVE_NOT_LOAD"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "UNLOAD_VALVE_NOT_LOAD",
            ["mm"] = 0,
        }, -- end of ["UNLOAD_VALVE_NOT_LOAD"]
        ["TURNIND_INCORRECT_SENS_VAC_LOW"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TURNIND_INCORRECT_SENS_VAC_LOW",
            ["mm"] = 0,
        }, -- end of ["TURNIND_INCORRECT_SENS_VAC_LOW"]
        ["Failure_Elec_RightTransformerRectifier"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Elec_RightTransformerRectifier",
            ["mm"] = 0,
        }, -- end of ["Failure_Elec_RightTransformerRectifier"]
        ["RDR_FAILURE_DEGRATED_PERFORMANCE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RDR_FAILURE_DEGRATED_PERFORMANCE",
            ["mm"] = 0,
        }, -- end of ["RDR_FAILURE_DEGRATED_PERFORMANCE"]
        ["Failure_LeftEngine"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_LeftEngine",
            ["mm"] = 0,
        }, -- end of ["Failure_LeftEngine"]
        ["EMMC_FAILURE_TRU"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_TRU",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_TRU"]
        ["hydr1_pump_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "hydr1_pump_fail",
            ["mm"] = 0,
        }, -- end of ["hydr1_pump_fail"]
        ["OESP_FAILURE_CH_DISP_L"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "OESP_FAILURE_CH_DISP_L",
            ["mm"] = 0,
        }, -- end of ["OESP_FAILURE_CH_DISP_L"]
        ["UHF_ARC_159_FAILURE_DISPLAY"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "UHF_ARC_159_FAILURE_DISPLAY",
            ["mm"] = 0,
        }, -- end of ["UHF_ARC_159_FAILURE_DISPLAY"]
        ["ILS_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ILS_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["ILS_FAILURE_TOTAL"]
        ["INS_FAILURE_HEADING"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INS_FAILURE_HEADING",
            ["mm"] = 0,
        }, -- end of ["INS_FAILURE_HEADING"]
        ["MWMMC_FAILURE_1553B_LMFCD"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MWMMC_FAILURE_1553B_LMFCD",
            ["mm"] = 0,
        }, -- end of ["MWMMC_FAILURE_1553B_LMFCD"]
        ["as_damage_Depressurization"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "as_damage_Depressurization",
            ["mm"] = 0,
        }, -- end of ["as_damage_Depressurization"]
        ["inverter1_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "inverter1_fail",
            ["mm"] = 0,
        }, -- end of ["inverter1_fail"]
        ["L_ENG_OIL_LEAK_MODERATE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "L_ENG_OIL_LEAK_MODERATE",
            ["mm"] = 0,
        }, -- end of ["L_ENG_OIL_LEAK_MODERATE"]
        ["FAILURE_HYDRAULICS_2_PUMP"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAILURE_HYDRAULICS_2_PUMP",
            ["mm"] = 0,
        }, -- end of ["FAILURE_HYDRAULICS_2_PUMP"]
        ["EMMC_FAILURE_BATTERY_FCS2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_BATTERY_FCS2",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_BATTERY_FCS2"]
        ["R_ENG_OIL_LEAK_MODERATE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "R_ENG_OIL_LEAK_MODERATE",
            ["mm"] = 0,
        }, -- end of ["R_ENG_OIL_LEAK_MODERATE"]
        ["SWMMC_FAILURE_RMFCD"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SWMMC_FAILURE_RMFCD",
            ["mm"] = 0,
        }, -- end of ["SWMMC_FAILURE_RMFCD"]
        ["RIGHT_MFCD_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RIGHT_MFCD_FAILURE",
            ["mm"] = 0,
        }, -- end of ["RIGHT_MFCD_FAILURE"]
        ["airbrake_cutout_microsw_malf"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "airbrake_cutout_microsw_malf",
            ["mm"] = 0,
        }, -- end of ["airbrake_cutout_microsw_malf"]
        ["SMS_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SMS_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["SMS_FAILURE_TOTAL"]
        ["Failure_Comp_MC1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Comp_MC1",
            ["mm"] = 0,
        }, -- end of ["Failure_Comp_MC1"]
        ["INST_PITOT_DAMAGE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INST_PITOT_DAMAGE",
            ["mm"] = 0,
        }, -- end of ["INST_PITOT_DAMAGE"]
        ["SWMMC_FAILURE_LMFCD"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SWMMC_FAILURE_LMFCD",
            ["mm"] = 0,
        }, -- end of ["SWMMC_FAILURE_LMFCD"]
        ["FUEL_TANK_03_MEDIUM_LEAK"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "FUEL_TANK_03_MEDIUM_LEAK",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["FUEL_TANK_03_MEDIUM_LEAK"]
        ["MWMMC_FAILURE_1553B_RMFCD"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MWMMC_FAILURE_1553B_RMFCD",
            ["mm"] = 0,
        }, -- end of ["MWMMC_FAILURE_1553B_RMFCD"]
        ["FAILURE_SMS_PYLON_1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAILURE_SMS_PYLON_1",
            ["mm"] = 0,
        }, -- end of ["FAILURE_SMS_PYLON_1"]
        ["LEFT_GUNNER_KILLED_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "LEFT_GUNNER_KILLED_FAILURE",
            ["mm"] = 0,
        }, -- end of ["LEFT_GUNNER_KILLED_FAILURE"]
        ["ENG0_WATERRADIATOR1_PIERCED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_WATERRADIATOR1_PIERCED",
            ["mm"] = 0,
        }, -- end of ["ENG0_WATERRADIATOR1_PIERCED"]
        ["chute_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "chute_fail",
            ["mm"] = 0,
        }, -- end of ["chute_fail"]
        ["es_damage_InverterPT500C"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "es_damage_InverterPT500C",
            ["mm"] = 0,
        }, -- end of ["es_damage_InverterPT500C"]
        ["LANDING_LIGHTS_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "LANDING_LIGHTS_FAILURE",
            ["mm"] = 0,
        }, -- end of ["LANDING_LIGHTS_FAILURE"]
        ["GUN_LEFT_MG131_AMMUN_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_LEFT_MG131_AMMUN_FAULT",
            ["mm"] = 0,
        }, -- end of ["GUN_LEFT_MG131_AMMUN_FAULT"]
        ["RWR_FAILURE_RECEIVER_XX1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWR_FAILURE_RECEIVER_XX1",
            ["mm"] = 0,
        }, -- end of ["RWR_FAILURE_RECEIVER_XX1"]
        ["fuel_accu_leaks"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fuel_accu_leaks",
            ["mm"] = 0,
        }, -- end of ["fuel_accu_leaks"]
        ["SWMMC_FAILURE_PS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SWMMC_FAILURE_PS",
            ["mm"] = 0,
        }, -- end of ["SWMMC_FAILURE_PS"]
        ["GUN_LEFT_OUT_OPEN_CIRCUIT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_LEFT_OUT_OPEN_CIRCUIT",
            ["mm"] = 0,
        }, -- end of ["GUN_LEFT_OUT_OPEN_CIRCUIT"]
        ["TRN_FAIL_AUX"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TRN_FAIL_AUX",
            ["mm"] = 0,
        }, -- end of ["TRN_FAIL_AUX"]
        ["FLEX_S_NO_POWER_SUPPLY"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FLEX_S_NO_POWER_SUPPLY",
            ["mm"] = 0,
        }, -- end of ["FLEX_S_NO_POWER_SUPPLY"]
        ["MWMMC_FAILURE_PS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MWMMC_FAILURE_PS",
            ["mm"] = 0,
        }, -- end of ["MWMMC_FAILURE_PS"]
        ["ELEC_CABIN_LIGHTS_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_CABIN_LIGHTS_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_CABIN_LIGHTS_FAILURE"]
        ["FUEL_FORWARD_TANK_MINOR_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_FORWARD_TANK_MINOR_LEAK",
            ["mm"] = 0,
        }, -- end of ["FUEL_FORWARD_TANK_MINOR_LEAK"]
        ["INS_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INS_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["INS_FAILURE_TOTAL"]
        ["ELEC_ROCKETS_LH_LOOM_SEVERED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_ROCKETS_LH_LOOM_SEVERED",
            ["mm"] = 0,
        }, -- end of ["ELEC_ROCKETS_LH_LOOM_SEVERED"]
        ["sensf_CADC"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "sensf_CADC",
            ["mm"] = 0,
        }, -- end of ["sensf_CADC"]
        ["RADAR_ALTIMETR_LEFT_ANT_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RADAR_ALTIMETR_LEFT_ANT_FAILURE",
            ["mm"] = 0,
        }, -- end of ["RADAR_ALTIMETR_LEFT_ANT_FAILURE"]
        ["hydro"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "hydro",
            ["mm"] = 0,
        }, -- end of ["hydro"]
        ["FUEL_TANK_02_MINOR_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_TANK_02_MINOR_LEAK",
            ["mm"] = 0,
        }, -- end of ["FUEL_TANK_02_MINOR_LEAK"]
        ["HUD_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "HUD_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["HUD_FAILURE_TOTAL"]
        ["ELEC_BATTERY_OVERHEAT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_BATTERY_OVERHEAT",
            ["mm"] = 0,
        }, -- end of ["ELEC_BATTERY_OVERHEAT"]
        ["slats_stuck"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "slats_stuck",
            ["mm"] = 0,
        }, -- end of ["slats_stuck"]
        ["alt1_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "alt1_fail",
            ["mm"] = 0,
        }, -- end of ["alt1_fail"]
        ["WEAP_GUN_04_AMMO_BELT_SEVERED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "WEAP_GUN_04_AMMO_BELT_SEVERED",
            ["mm"] = 0,
        }, -- end of ["WEAP_GUN_04_AMMO_BELT_SEVERED"]
        ["Failure_ECS_OBOGS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_ECS_OBOGS",
            ["mm"] = 0,
        }, -- end of ["Failure_ECS_OBOGS"]
        ["EMMC_FAILURE_CANOPY_UNLOCK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_CANOPY_UNLOCK",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_CANOPY_UNLOCK"]
        ["fire_sys_fireko50"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fire_sys_fireko50",
            ["mm"] = 0,
        }, -- end of ["fire_sys_fireko50"]
        ["FUEL_TANK_01_EXPLODED"] = 
        {
            ["hh"] = 0,
            ["id"] = "FUEL_TANK_01_EXPLODED",
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["FUEL_TANK_01_EXPLODED"]
        ["AHRS_FAILURE_GYRO"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AHRS_FAILURE_GYRO",
            ["mm"] = 0,
        }, -- end of ["AHRS_FAILURE_GYRO"]
        ["es_damage_VU2"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "es_damage_VU2",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["es_damage_VU2"]
        ["ENG0_OIL_HOSE_1_BURST"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_OIL_HOSE_1_BURST",
            ["mm"] = 0,
        }, -- end of ["ENG0_OIL_HOSE_1_BURST"]
        ["AN_ALE_40V_FAILURE_CONTAINER_RIGHT_WING"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AN_ALE_40V_FAILURE_CONTAINER_RIGHT_WING",
            ["mm"] = 0,
        }, -- end of ["AN_ALE_40V_FAILURE_CONTAINER_RIGHT_WING"]
        ["TailReductor_LowOilPressure"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "TailReductor_LowOilPressure",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["TailReductor_LowOilPressure"]
        ["INS_FAILURE_DATA_INVALID"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INS_FAILURE_DATA_INVALID",
            ["mm"] = 0,
        }, -- end of ["INS_FAILURE_DATA_INVALID"]
        ["OXY_FAILURE_AUTO_100_O2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "OXY_FAILURE_AUTO_100_O2",
            ["mm"] = 0,
        }, -- end of ["OXY_FAILURE_AUTO_100_O2"]
        ["EMMC_FAILURE_AC_GROUND"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_AC_GROUND",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_AC_GROUND"]
        ["pp_damage_MainStabFactor"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "pp_damage_MainStabFactor",
            ["mm"] = 0,
        }, -- end of ["pp_damage_MainStabFactor"]
        ["hydro_right"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "hydro_right",
            ["mm"] = 0,
        }, -- end of ["hydro_right"]
        ["hydr1_leaks"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "hydr1_leaks",
            ["mm"] = 0,
        }, -- end of ["hydr1_leaks"]
        ["FAILURE_HYDRAULICS_1_EXTERNAL_LEAKAGE_SEVERE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAILURE_HYDRAULICS_1_EXTERNAL_LEAKAGE_SEVERE",
            ["mm"] = 0,
        }, -- end of ["FAILURE_HYDRAULICS_1_EXTERNAL_LEAKAGE_SEVERE"]
        ["INST_TACH1_POOR_CONNECTION"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INST_TACH1_POOR_CONNECTION",
            ["mm"] = 0,
        }, -- end of ["INST_TACH1_POOR_CONNECTION"]
        ["FUEL_TANK_00_MEDIUM_LEAK"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "FUEL_TANK_00_MEDIUM_LEAK",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["FUEL_TANK_00_MEDIUM_LEAK"]
        ["SADL_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SADL_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["SADL_FAILURE_TOTAL"]
        ["EMMC_FAILURE_STATIC_INVERTER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_STATIC_INVERTER",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_STATIC_INVERTER"]
        ["Failure_Elec_RightGenerator"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Elec_RightGenerator",
            ["mm"] = 0,
        }, -- end of ["Failure_Elec_RightGenerator"]
        ["R_ENG_AICS_RAMP_FAIL_OPEN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "R_ENG_AICS_RAMP_FAIL_OPEN",
            ["mm"] = 0,
        }, -- end of ["R_ENG_AICS_RAMP_FAIL_OPEN"]
        ["ELEVONOUTERLEFT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEVONOUTERLEFT",
            ["mm"] = 0,
        }, -- end of ["ELEVONOUTERLEFT"]
        ["SAR_1_95"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "SAR_1_95",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["SAR_1_95"]
        ["DOPPLER_UNIT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "DOPPLER_UNIT",
            ["mm"] = 0,
        }, -- end of ["DOPPLER_UNIT"]
        ["flaps_fault"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "flaps_fault",
            ["mm"] = 0,
        }, -- end of ["flaps_fault"]
        ["L_ENG_AICS_RAMP_FAIL_OPEN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "L_ENG_AICS_RAMP_FAIL_OPEN",
            ["mm"] = 0,
        }, -- end of ["L_ENG_AICS_RAMP_FAIL_OPEN"]
        ["TAIL_GEAR_C_CABLE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TAIL_GEAR_C_CABLE",
            ["mm"] = 0,
        }, -- end of ["TAIL_GEAR_C_CABLE"]
        ["hydr2_reserv_leaks"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "hydr2_reserv_leaks",
            ["mm"] = 0,
        }, -- end of ["hydr2_reserv_leaks"]
        ["damper"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "damper",
            ["mm"] = 0,
        }, -- end of ["damper"]
        ["UNCR_LH_STRUT_DOWN_LOCK_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "UNCR_LH_STRUT_DOWN_LOCK_FAILURE",
            ["mm"] = 0,
        }, -- end of ["UNCR_LH_STRUT_DOWN_LOCK_FAILURE"]
        ["ELEC_FUEL_PUMP_P2_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_FUEL_PUMP_P2_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_FUEL_PUMP_P2_FAILURE"]
        ["BCKPITOT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BCKPITOT",
            ["mm"] = 0,
        }, -- end of ["BCKPITOT"]
        ["GMC_MAGN_COMP_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GMC_MAGN_COMP_FAILURE",
            ["mm"] = 0,
        }, -- end of ["GMC_MAGN_COMP_FAILURE"]
        ["ELECTRIC_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELECTRIC_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELECTRIC_FAILURE"]
        ["EMMC_FAILURE_AC_GENERATOR_FEED_LINE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_AC_GENERATOR_FEED_LINE",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_AC_GENERATOR_FEED_LINE"]
        ["ENG0_GOVERNOR_REGULATOR_MALFUNCTION"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_GOVERNOR_REGULATOR_MALFUNCTION",
            ["mm"] = 0,
        }, -- end of ["ENG0_GOVERNOR_REGULATOR_MALFUNCTION"]
        ["DTC_FAILURE_READER_BROKEN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "DTC_FAILURE_READER_BROKEN",
            ["mm"] = 0,
        }, -- end of ["DTC_FAILURE_READER_BROKEN"]
        ["ELEC_PITOT_HEAT_ELEMENT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_PITOT_HEAT_ELEMENT",
            ["mm"] = 0,
        }, -- end of ["ELEC_PITOT_HEAT_ELEMENT"]
        ["pitch_trim_runaway_down"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "pitch_trim_runaway_down",
            ["mm"] = 0,
        }, -- end of ["pitch_trim_runaway_down"]
        ["ARN_83_TOTAL_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ARN_83_TOTAL_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ARN_83_TOTAL_FAILURE"]
        ["CTRL_LANDING_FLAPS_MECHANICAL_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CTRL_LANDING_FLAPS_MECHANICAL_FAILURE",
            ["mm"] = 0,
        }, -- end of ["CTRL_LANDING_FLAPS_MECHANICAL_FAILURE"]
        ["SWMMC_CSU_NO_RS422_COMM"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SWMMC_CSU_NO_RS422_COMM",
            ["mm"] = 0,
        }, -- end of ["SWMMC_CSU_NO_RS422_COMM"]
        ["Failure_Hyd_HYD2A_Leak"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Hyd_HYD2A_Leak",
            ["mm"] = 0,
        }, -- end of ["Failure_Hyd_HYD2A_Leak"]
        ["gyros_emergency_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "gyros_emergency_fail",
            ["mm"] = 0,
        }, -- end of ["gyros_emergency_fail"]
        ["CADC_WING_SWEEP_INDICATOR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CADC_WING_SWEEP_INDICATOR",
            ["mm"] = 0,
        }, -- end of ["CADC_WING_SWEEP_INDICATOR"]
        ["sight_lamps_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "sight_lamps_fail",
            ["mm"] = 0,
        }, -- end of ["sight_lamps_fail"]
        ["EMMC_FAILURE_AC_GENERATOR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_AC_GENERATOR",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_AC_GENERATOR"]
        ["RADARASS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RADARASS",
            ["mm"] = 0,
        }, -- end of ["RADARASS"]
        ["fs_aft_LH_leakage"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fs_aft_LH_leakage",
            ["mm"] = 0,
        }, -- end of ["fs_aft_LH_leakage"]
        ["aileron_trim_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "aileron_trim_fail",
            ["mm"] = 0,
        }, -- end of ["aileron_trim_fail"]
        ["CADC_WING_SWEEP_COMMAND_CHANNEL_1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CADC_WING_SWEEP_COMMAND_CHANNEL_1",
            ["mm"] = 0,
        }, -- end of ["CADC_WING_SWEEP_COMMAND_CHANNEL_1"]
        ["ENG0_TURBINE_LUBRICATION"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_TURBINE_LUBRICATION",
            ["mm"] = 0,
        }, -- end of ["ENG0_TURBINE_LUBRICATION"]
        ["FCS_FAILURE_ROLL_LVDT_1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_ROLL_LVDT_1",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_ROLL_LVDT_1"]
        ["FUEL_TANK_00_FIRE"] = 
        {
            ["hh"] = 0,
            ["id"] = "FUEL_TANK_00_FIRE",
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["FUEL_TANK_00_FIRE"]
        ["flaps_stuck"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "flaps_stuck",
            ["mm"] = 0,
        }, -- end of ["flaps_stuck"]
        ["UNCR_RH_STRUT_UP_LOCK_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "UNCR_RH_STRUT_UP_LOCK_FAILURE",
            ["mm"] = 0,
        }, -- end of ["UNCR_RH_STRUT_UP_LOCK_FAILURE"]
        ["CADC_FAILURE_DYNAMIC"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CADC_FAILURE_DYNAMIC",
            ["mm"] = 0,
        }, -- end of ["CADC_FAILURE_DYNAMIC"]
        ["Failure_Fuel_QuantityGaging"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Fuel_QuantityGaging",
            ["mm"] = 0,
        }, -- end of ["Failure_Fuel_QuantityGaging"]
        ["MWMMC_FAILURE_1553B_CMFCD"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MWMMC_FAILURE_1553B_CMFCD",
            ["mm"] = 0,
        }, -- end of ["MWMMC_FAILURE_1553B_CMFCD"]
        ["fuel_sys_left_transfer_pump"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fuel_sys_left_transfer_pump",
            ["mm"] = 0,
        }, -- end of ["fuel_sys_left_transfer_pump"]
        ["rudder_trim_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "rudder_trim_fail",
            ["mm"] = 0,
        }, -- end of ["rudder_trim_fail"]
        ["HYD_Transf"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "HYD_Transf",
            ["mm"] = 0,
        }, -- end of ["HYD_Transf"]
        ["FCS_FAILURE_L_ELEVATOR_ELEC_C"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_L_ELEVATOR_ELEC_C",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_L_ELEVATOR_ELEC_C"]
        ["es_damage_StarterGenerator"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "es_damage_StarterGenerator",
            ["mm"] = 0,
        }, -- end of ["es_damage_StarterGenerator"]
        ["es_damage_Battery"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "es_damage_Battery",
            ["mm"] = 0,
        }, -- end of ["es_damage_Battery"]
        ["ppf_FireLeft"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ppf_FireLeft",
            ["mm"] = 0,
        }, -- end of ["ppf_FireLeft"]
        ["EMMC_FAILURE_DC_GENERATOR_VOLTAGE_HIGH"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_DC_GENERATOR_VOLTAGE_HIGH",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_DC_GENERATOR_VOLTAGE_HIGH"]
        ["VHF_AM_RADIO_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "VHF_AM_RADIO_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["VHF_AM_RADIO_FAILURE_TOTAL"]
        ["VHF_SQUELCH_RELAY"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "VHF_SQUELCH_RELAY",
            ["mm"] = 0,
        }, -- end of ["VHF_SQUELCH_RELAY"]
        ["RPMFault_LeftEngine"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "RPMFault_LeftEngine",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["RPMFault_LeftEngine"]
        ["F2_TOP_CYLINDER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "F2_TOP_CYLINDER",
            ["mm"] = 0,
        }, -- end of ["F2_TOP_CYLINDER"]
        ["FUEL_BOOSTER_FUEL_PUMP_DEGRADED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_BOOSTER_FUEL_PUMP_DEGRADED",
            ["mm"] = 0,
        }, -- end of ["FUEL_BOOSTER_FUEL_PUMP_DEGRADED"]
        ["INST_SPEEDOMETER_DEPRESSURIZATION"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INST_SPEEDOMETER_DEPRESSURIZATION",
            ["mm"] = 0,
        }, -- end of ["INST_SPEEDOMETER_DEPRESSURIZATION"]
        ["Failure_PP_EngL_Main_FFCS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_PP_EngL_Main_FFCS",
            ["mm"] = 0,
        }, -- end of ["Failure_PP_EngL_Main_FFCS"]
        ["K14_NO_POWER_SUPPLY"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "K14_NO_POWER_SUPPLY",
            ["mm"] = 0,
        }, -- end of ["K14_NO_POWER_SUPPLY"]
        ["INS_FAILURE_ATTITUDE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INS_FAILURE_ATTITUDE",
            ["mm"] = 0,
        }, -- end of ["INS_FAILURE_ATTITUDE"]
        ["INST_TACH0_POOR_CONNECTION"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INST_TACH0_POOR_CONNECTION",
            ["mm"] = 0,
        }, -- end of ["INST_TACH0_POOR_CONNECTION"]
        ["ENG0_BOOST_REGULATOR_MALFUNCTION"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_BOOST_REGULATOR_MALFUNCTION",
            ["mm"] = 0,
        }, -- end of ["ENG0_BOOST_REGULATOR_MALFUNCTION"]
        ["MANIFOLD_LINE_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MANIFOLD_LINE_LEAK",
            ["mm"] = 0,
        }, -- end of ["MANIFOLD_LINE_LEAK"]
        ["INS_FAILURE_ACC"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INS_FAILURE_ACC",
            ["mm"] = 0,
        }, -- end of ["INS_FAILURE_ACC"]
        ["IFFCC_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "IFFCC_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["IFFCC_FAILURE_TOTAL"]
        ["RWR_FAILURE_QUAD225"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWR_FAILURE_QUAD225",
            ["mm"] = 0,
        }, -- end of ["RWR_FAILURE_QUAD225"]
        ["ENG0_TURBINE_MALFUNCTION"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_TURBINE_MALFUNCTION",
            ["mm"] = 0,
        }, -- end of ["ENG0_TURBINE_MALFUNCTION"]
        ["FCS_FAILURE_ROLL_ELEC_SERVO_2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_ROLL_ELEC_SERVO_2",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_ROLL_ELEC_SERVO_2"]
        ["RDR_FAILURE_PREESURIZATION"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RDR_FAILURE_PREESURIZATION",
            ["mm"] = 0,
        }, -- end of ["RDR_FAILURE_PREESURIZATION"]
        ["ENGINE_FAILURE_APD88_STARTER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENGINE_FAILURE_APD88_STARTER",
            ["mm"] = 0,
        }, -- end of ["ENGINE_FAILURE_APD88_STARTER"]
        ["AN_ALR69V_FAILURE_SENSOR_NOSE_LEFT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AN_ALR69V_FAILURE_SENSOR_NOSE_LEFT",
            ["mm"] = 0,
        }, -- end of ["AN_ALR69V_FAILURE_SENSOR_NOSE_LEFT"]
        ["Failure_PP_RightPTS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_PP_RightPTS",
            ["mm"] = 0,
        }, -- end of ["Failure_PP_RightPTS"]
        ["GUN_RIGHT_MG131_OPEN_CIRCUIT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_RIGHT_MG131_OPEN_CIRCUIT",
            ["mm"] = 0,
        }, -- end of ["GUN_RIGHT_MG131_OPEN_CIRCUIT"]
        ["RUDDERSERV"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RUDDERSERV",
            ["mm"] = 0,
        }, -- end of ["RUDDERSERV"]
        ["TACAN_FAILURE_TRANSMITTER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TACAN_FAILURE_TRANSMITTER",
            ["mm"] = 0,
        }, -- end of ["TACAN_FAILURE_TRANSMITTER"]
        ["GUN_RIGHT_MG151_BARREL_WORN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_RIGHT_MG151_BARREL_WORN",
            ["mm"] = 0,
        }, -- end of ["GUN_RIGHT_MG151_BARREL_WORN"]
        ["GUN_LEFT_IN_BARREL_WORN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_LEFT_IN_BARREL_WORN",
            ["mm"] = 0,
        }, -- end of ["GUN_LEFT_IN_BARREL_WORN"]
        ["SAR_fast"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "SAR_fast",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["SAR_fast"]
        ["CTRL_TAIL_ROTOR_CONTROL_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CTRL_TAIL_ROTOR_CONTROL_FAILURE",
            ["mm"] = 0,
        }, -- end of ["CTRL_TAIL_ROTOR_CONTROL_FAILURE"]
        ["ELEC_BOMBABWGERAT_CHARGING_MALFUNCTION"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_BOMBABWGERAT_CHARGING_MALFUNCTION",
            ["mm"] = 0,
        }, -- end of ["ELEC_BOMBABWGERAT_CHARGING_MALFUNCTION"]
        ["W_S_L"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "W_S_L",
            ["mm"] = 0,
        }, -- end of ["W_S_L"]
        ["FCS_FAILURE_Q_SENSOR_2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_Q_SENSOR_2",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_Q_SENSOR_2"]
        ["VHF_TOTAL_DAMAGE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "VHF_TOTAL_DAMAGE",
            ["mm"] = 0,
        }, -- end of ["VHF_TOTAL_DAMAGE"]
        ["OIL_DILUTION_SOLENOID"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "OIL_DILUTION_SOLENOID",
            ["mm"] = 0,
        }, -- end of ["OIL_DILUTION_SOLENOID"]
        ["OXYN_RIGHT_CONTAINER_PERFORATED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "OXYN_RIGHT_CONTAINER_PERFORATED",
            ["mm"] = 0,
        }, -- end of ["OXYN_RIGHT_CONTAINER_PERFORATED"]
        ["FCS_FAILURE_L_ELEVATOR_HYD_1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_L_ELEVATOR_HYD_1",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_L_ELEVATOR_HYD_1"]
        ["AOASENSOR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AOASENSOR",
            ["mm"] = 0,
        }, -- end of ["AOASENSOR"]
        ["A11_CLOCK_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "A11_CLOCK_FAILURE",
            ["mm"] = 0,
        }, -- end of ["A11_CLOCK_FAILURE"]
        ["fuel_sys_feed_tank_pump"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fuel_sys_feed_tank_pump",
            ["mm"] = 0,
        }, -- end of ["fuel_sys_feed_tank_pump"]
        ["HYDR2ACC"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "HYDR2ACC",
            ["mm"] = 0,
        }, -- end of ["HYDR2ACC"]
        ["WEAP_GUN_01_DAMAGED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "WEAP_GUN_01_DAMAGED",
            ["mm"] = 0,
        }, -- end of ["WEAP_GUN_01_DAMAGED"]
        ["RDR_FAILURE_RECEIVER_DEGRATION"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RDR_FAILURE_RECEIVER_DEGRATION",
            ["mm"] = 0,
        }, -- end of ["RDR_FAILURE_RECEIVER_DEGRATION"]
        ["SOPLO_FAILURE_PARTIAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SOPLO_FAILURE_PARTIAL",
            ["mm"] = 0,
        }, -- end of ["SOPLO_FAILURE_PARTIAL"]
        ["hsf_ControlHydraulic"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "hsf_ControlHydraulic",
            ["mm"] = 0,
        }, -- end of ["hsf_ControlHydraulic"]
        ["JADRO_1A_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "JADRO_1A_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["JADRO_1A_FAILURE_TOTAL"]
        ["KPP_FAILURE_PARTIAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "KPP_FAILURE_PARTIAL",
            ["mm"] = 0,
        }, -- end of ["KPP_FAILURE_PARTIAL"]
        ["mach_chain_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "mach_chain_fail",
            ["mm"] = 0,
        }, -- end of ["mach_chain_fail"]
        ["OIL_RADIATOR_SENSOR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "OIL_RADIATOR_SENSOR",
            ["mm"] = 0,
        }, -- end of ["OIL_RADIATOR_SENSOR"]
        ["DMT_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "DMT_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["DMT_FAILURE_TOTAL"]
        ["right_front_leaks"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "right_front_leaks",
            ["mm"] = 0,
        }, -- end of ["right_front_leaks"]
        ["FWD_TANK_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FWD_TANK_LEAK",
            ["mm"] = 0,
        }, -- end of ["FWD_TANK_LEAK"]
        ["radioalt_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "radioalt_fail",
            ["mm"] = 0,
        }, -- end of ["radioalt_fail"]
        ["INST_VARIOMETER_CLOGGED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INST_VARIOMETER_CLOGGED",
            ["mm"] = 0,
        }, -- end of ["INST_VARIOMETER_CLOGGED"]
        ["OESP_FAILURE_FL_DISP_L"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "OESP_FAILURE_FL_DISP_L",
            ["mm"] = 0,
        }, -- end of ["OESP_FAILURE_FL_DISP_L"]
        ["Failure_Gear_NWS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Gear_NWS",
            ["mm"] = 0,
        }, -- end of ["Failure_Gear_NWS"]
        ["gear_left_stuck"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "gear_left_stuck",
            ["mm"] = 0,
        }, -- end of ["gear_left_stuck"]
        ["rws"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "rws",
            ["mm"] = 0,
        }, -- end of ["rws"]
        ["HUDDISPLAY"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "HUDDISPLAY",
            ["mm"] = 0,
        }, -- end of ["HUDDISPLAY"]
        ["SWMMC_FAILURE_IOC"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SWMMC_FAILURE_IOC",
            ["mm"] = 0,
        }, -- end of ["SWMMC_FAILURE_IOC"]
        ["OIL_RADIATOR_MOTOR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "OIL_RADIATOR_MOTOR",
            ["mm"] = 0,
        }, -- end of ["OIL_RADIATOR_MOTOR"]
        ["hs_damage_GainAutoUnload"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "hs_damage_GainAutoUnload",
            ["mm"] = 0,
        }, -- end of ["hs_damage_GainAutoUnload"]
        ["FUSELAGE_TANK_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUSELAGE_TANK_LEAK",
            ["mm"] = 0,
        }, -- end of ["FUSELAGE_TANK_LEAK"]
        ["MWMMC_FAILURE_IOC"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MWMMC_FAILURE_IOC",
            ["mm"] = 0,
        }, -- end of ["MWMMC_FAILURE_IOC"]
        ["SAR_hover_flight_glide"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "SAR_hover_flight_glide",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["SAR_hover_flight_glide"]
        ["pitot_heat_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "pitot_heat_fail",
            ["mm"] = 0,
        }, -- end of ["pitot_heat_fail"]
        ["r_gen"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "r_gen",
            ["mm"] = 0,
        }, -- end of ["r_gen"]
        ["OIL_DILUTION_WIRE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "OIL_DILUTION_WIRE",
            ["mm"] = 0,
        }, -- end of ["OIL_DILUTION_WIRE"]
        ["GUN_RIGHT_MG151_OPEN_CIRCUIT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_RIGHT_MG151_OPEN_CIRCUIT",
            ["mm"] = 0,
        }, -- end of ["GUN_RIGHT_MG151_OPEN_CIRCUIT"]
        ["FCS_FAILURE_YAW_ELEC_SERVO_2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_YAW_ELEC_SERVO_2",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_YAW_ELEC_SERVO_2"]
        ["ELEC_SIGNAL_LIGHTS_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_SIGNAL_LIGHTS_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_SIGNAL_LIGHTS_FAILURE"]
        ["AAR_47_FAILURE_SENSOR_RIGHT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AAR_47_FAILURE_SENSOR_RIGHT",
            ["mm"] = 0,
        }, -- end of ["AAR_47_FAILURE_SENSOR_RIGHT"]
        ["FAILURE_HYDRAULICS_1_PUMP"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAILURE_HYDRAULICS_1_PUMP",
            ["mm"] = 0,
        }, -- end of ["FAILURE_HYDRAULICS_1_PUMP"]
        ["R_GEAR_DLK_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "R_GEAR_DLK_FAULT",
            ["mm"] = 0,
        }, -- end of ["R_GEAR_DLK_FAULT"]
        ["fuel_sys_300left"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "fuel_sys_300left",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["fuel_sys_300left"]
        ["FUEL_NITRO_TANK_MINOR_LEAK"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "FUEL_NITRO_TANK_MINOR_LEAK",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["FUEL_NITRO_TANK_MINOR_LEAK"]
        ["FUEL_DROPTANK_MINOR_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_DROPTANK_MINOR_LEAK",
            ["mm"] = 0,
        }, -- end of ["FUEL_DROPTANK_MINOR_LEAK"]
        ["ELEC_RH_GEAR_DRIVE_WIRE_SEVERED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_RH_GEAR_DRIVE_WIRE_SEVERED",
            ["mm"] = 0,
        }, -- end of ["ELEC_RH_GEAR_DRIVE_WIRE_SEVERED"]
        ["INST_HUD_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INST_HUD_FAILURE",
            ["mm"] = 0,
        }, -- end of ["INST_HUD_FAILURE"]
        ["L_GEAR_DLK_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "L_GEAR_DLK_FAULT",
            ["mm"] = 0,
        }, -- end of ["L_GEAR_DLK_FAULT"]
        ["ELEC_LH_JUNCTION_BOX_DESTROYED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_LH_JUNCTION_BOX_DESTROYED",
            ["mm"] = 0,
        }, -- end of ["ELEC_LH_JUNCTION_BOX_DESTROYED"]
        ["IGNITION_TERM_CONNECT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "IGNITION_TERM_CONNECT",
            ["mm"] = 0,
        }, -- end of ["IGNITION_TERM_CONNECT"]
        ["ELEVONSERVOUTERLEFT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEVONSERVOUTERLEFT",
            ["mm"] = 0,
        }, -- end of ["ELEVONSERVOUTERLEFT"]
        ["NGear_ret_fault"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "NGear_ret_fault",
            ["mm"] = 0,
        }, -- end of ["NGear_ret_fault"]
        ["FUELTANK3R"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUELTANK3R",
            ["mm"] = 0,
        }, -- end of ["FUELTANK3R"]
        ["ENG0_OIL_RADIATOR_0_PIERCED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_OIL_RADIATOR_0_PIERCED",
            ["mm"] = 0,
        }, -- end of ["ENG0_OIL_RADIATOR_0_PIERCED"]
        ["aileron_loss"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "aileron_loss",
            ["mm"] = 0,
        }, -- end of ["aileron_loss"]
        ["FCS_FAILURE_ROLL_RATE_GYRO_1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_ROLL_RATE_GYRO_1",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_ROLL_RATE_GYRO_1"]
        ["BOMBS_DAMAGE_LINKAGE_LEFT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BOMBS_DAMAGE_LINKAGE_LEFT",
            ["mm"] = 0,
        }, -- end of ["BOMBS_DAMAGE_LINKAGE_LEFT"]
        ["FUSELAGE_TANK_PUMP_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUSELAGE_TANK_PUMP_FAULT",
            ["mm"] = 0,
        }, -- end of ["FUSELAGE_TANK_PUMP_FAULT"]
        ["FUEL_BOOSTER_FUEL_PUMP_1_DEGRADED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_BOOSTER_FUEL_PUMP_1_DEGRADED",
            ["mm"] = 0,
        }, -- end of ["FUEL_BOOSTER_FUEL_PUMP_1_DEGRADED"]
        ["hydro_main"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "hydro_main",
            ["mm"] = 0,
        }, -- end of ["hydro_main"]
        ["RGear_ret_fault"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RGear_ret_fault",
            ["mm"] = 0,
        }, -- end of ["RGear_ret_fault"]
        ["CNI_FAILURE_RALT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CNI_FAILURE_RALT",
            ["mm"] = 0,
        }, -- end of ["CNI_FAILURE_RALT"]
        ["TAIL_GEAR_U_LOCK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TAIL_GEAR_U_LOCK",
            ["mm"] = 0,
        }, -- end of ["TAIL_GEAR_U_LOCK"]
        ["RADAR_ALT_TOTAL_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RADAR_ALT_TOTAL_FAILURE",
            ["mm"] = 0,
        }, -- end of ["RADAR_ALT_TOTAL_FAILURE"]
        ["INST_VARIOMETER_DEPRESSURIZATION"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INST_VARIOMETER_DEPRESSURIZATION",
            ["mm"] = 0,
        }, -- end of ["INST_VARIOMETER_DEPRESSURIZATION"]
        ["RDR_FAILURE_SERVO_OVERHEAT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RDR_FAILURE_SERVO_OVERHEAT",
            ["mm"] = 0,
        }, -- end of ["RDR_FAILURE_SERVO_OVERHEAT"]
        ["FUEL_TANK_01_MAJOR_LEAK"] = 
        {
            ["hh"] = 0,
            ["id"] = "FUEL_TANK_01_MAJOR_LEAK",
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["FUEL_TANK_01_MAJOR_LEAK"]
        ["MWMMC_FAILURE_1553B_FCS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MWMMC_FAILURE_1553B_FCS",
            ["mm"] = 0,
        }, -- end of ["MWMMC_FAILURE_1553B_FCS"]
        ["CTRL_LH_SLAT_JAMMED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CTRL_LH_SLAT_JAMMED",
            ["mm"] = 0,
        }, -- end of ["CTRL_LH_SLAT_JAMMED"]
        ["ELEVONSERVINNERLEFT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEVONSERVINNERLEFT",
            ["mm"] = 0,
        }, -- end of ["ELEVONSERVINNERLEFT"]
        ["TAPEREC"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TAPEREC",
            ["mm"] = 0,
        }, -- end of ["TAPEREC"]
        ["ILS_FAILURE_ANT_LOCALIZER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ILS_FAILURE_ANT_LOCALIZER",
            ["mm"] = 0,
        }, -- end of ["ILS_FAILURE_ANT_LOCALIZER"]
        ["PPF_LE_TEMPER_LIM_OFF"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "PPF_LE_TEMPER_LIM_OFF",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["PPF_LE_TEMPER_LIM_OFF"]
        ["TVDISPLAY"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TVDISPLAY",
            ["mm"] = 0,
        }, -- end of ["TVDISPLAY"]
        ["RDR_FAILURE_PEDESTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RDR_FAILURE_PEDESTAL",
            ["mm"] = 0,
        }, -- end of ["RDR_FAILURE_PEDESTAL"]
        ["RDR_FAILURE_SERVOLOOP"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RDR_FAILURE_SERVOLOOP",
            ["mm"] = 0,
        }, -- end of ["RDR_FAILURE_SERVOLOOP"]
        ["ENG0_OIL_HOSE_0_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_OIL_HOSE_0_LEAK",
            ["mm"] = 0,
        }, -- end of ["ENG0_OIL_HOSE_0_LEAK"]
        ["ELEC_EMERGENCY_GENERATOR_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_EMERGENCY_GENERATOR_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_EMERGENCY_GENERATOR_FAILURE"]
        ["ICS_FAILURE_AMPLIFIER_RIO_BU"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ICS_FAILURE_AMPLIFIER_RIO_BU",
            ["mm"] = 0,
        }, -- end of ["ICS_FAILURE_AMPLIFIER_RIO_BU"]
        ["STARTER_SOL_SHORT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "STARTER_SOL_SHORT",
            ["mm"] = 0,
        }, -- end of ["STARTER_SOL_SHORT"]
        ["FCS_FAILURE_NZ_SENSOR_2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_NZ_SENSOR_2",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_NZ_SENSOR_2"]
        ["FCS_FAILURE_COMP_3"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_COMP_3",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_COMP_3"]
        ["FUELTANK5L"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUELTANK5L",
            ["mm"] = 0,
        }, -- end of ["FUELTANK5L"]
        ["FCS_FAILURE_YAW_RATE_GYRO_2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_YAW_RATE_GYRO_2",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_YAW_RATE_GYRO_2"]
        ["OESP_FAILURE_MAWS_R"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "OESP_FAILURE_MAWS_R",
            ["mm"] = 0,
        }, -- end of ["OESP_FAILURE_MAWS_R"]
        ["RWR_FAILURE_COMPUTER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWR_FAILURE_COMPUTER",
            ["mm"] = 0,
        }, -- end of ["RWR_FAILURE_COMPUTER"]
        ["esf_LeftRectifier"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "esf_LeftRectifier",
            ["mm"] = 0,
        }, -- end of ["esf_LeftRectifier"]
        ["FAILURE_EXT_LIGHT_NAV_RIGHT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAILURE_EXT_LIGHT_NAV_RIGHT",
            ["mm"] = 0,
        }, -- end of ["FAILURE_EXT_LIGHT_NAV_RIGHT"]
        ["AAR_47_FAILURE_SENSOR_BOTTOM"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AAR_47_FAILURE_SENSOR_BOTTOM",
            ["mm"] = 0,
        }, -- end of ["AAR_47_FAILURE_SENSOR_BOTTOM"]
        ["BOMBS_ARMING_BROKEN_WIRING_LEFT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BOMBS_ARMING_BROKEN_WIRING_LEFT",
            ["mm"] = 0,
        }, -- end of ["BOMBS_ARMING_BROKEN_WIRING_LEFT"]
        ["MAINPITOT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MAINPITOT",
            ["mm"] = 0,
        }, -- end of ["MAINPITOT"]
        ["FCS_FAILURE_R_ELEVATOR_ELEC_D"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_R_ELEVATOR_ELEC_D",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_R_ELEVATOR_ELEC_D"]
        ["slat_inner_left_stuck"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "slat_inner_left_stuck",
            ["mm"] = 0,
        }, -- end of ["slat_inner_left_stuck"]
        ["CTRL_AILERON_ROD_MINOR_DAMAGE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CTRL_AILERON_ROD_MINOR_DAMAGE",
            ["mm"] = 0,
        }, -- end of ["CTRL_AILERON_ROD_MINOR_DAMAGE"]
        ["FCS_FAILURE_COMP_1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_COMP_1",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_COMP_1"]
        ["COOLANT_RADIATOR_WIRING"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "COOLANT_RADIATOR_WIRING",
            ["mm"] = 0,
        }, -- end of ["COOLANT_RADIATOR_WIRING"]
        ["OESP_FAILURE_MAWS_L"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "OESP_FAILURE_MAWS_L",
            ["mm"] = 0,
        }, -- end of ["OESP_FAILURE_MAWS_L"]
        ["AIRSPEED_INDICATOR_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AIRSPEED_INDICATOR_FAILURE",
            ["mm"] = 0,
        }, -- end of ["AIRSPEED_INDICATOR_FAILURE"]
        ["ELEC_RH_JUNCTION_BOX_DESTROYED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_RH_JUNCTION_BOX_DESTROYED",
            ["mm"] = 0,
        }, -- end of ["ELEC_RH_JUNCTION_BOX_DESTROYED"]
        ["abris_software"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "abris_software",
            ["mm"] = 0,
        }, -- end of ["abris_software"]
        ["ef_surge"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ef_surge",
            ["mm"] = 0,
        }, -- end of ["ef_surge"]
        ["ROCKETS_INTERVALOMETER_WIRING"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ROCKETS_INTERVALOMETER_WIRING",
            ["mm"] = 0,
        }, -- end of ["ROCKETS_INTERVALOMETER_WIRING"]
        ["pitot_blocked"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "pitot_blocked",
            ["mm"] = 0,
        }, -- end of ["pitot_blocked"]
        ["CANARDFLAPLEFT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CANARDFLAPLEFT",
            ["mm"] = 0,
        }, -- end of ["CANARDFLAPLEFT"]
        ["fs_forward_LH_leakage"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fs_forward_LH_leakage",
            ["mm"] = 0,
        }, -- end of ["fs_forward_LH_leakage"]
        ["hydro_main_irreversible_valve"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "hydro_main_irreversible_valve",
            ["mm"] = 0,
        }, -- end of ["hydro_main_irreversible_valve"]
        ["right_fuel_pump_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "right_fuel_pump_fail",
            ["mm"] = 0,
        }, -- end of ["right_fuel_pump_fail"]
        ["GYROS_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GYROS_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["GYROS_FAILURE_TOTAL"]
        ["UHF_ARC_159_FAILURE_TRANSCEIVER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "UHF_ARC_159_FAILURE_TRANSCEIVER",
            ["mm"] = 0,
        }, -- end of ["UHF_ARC_159_FAILURE_TRANSCEIVER"]
        ["GUN_RIGHT_CENTER_BARREL_WORN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_RIGHT_CENTER_BARREL_WORN",
            ["mm"] = 0,
        }, -- end of ["GUN_RIGHT_CENTER_BARREL_WORN"]
        ["FAILURE_HYDRAULICS_2_INTERNAL_LEAKAGE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAILURE_HYDRAULICS_2_INTERNAL_LEAKAGE",
            ["mm"] = 0,
        }, -- end of ["FAILURE_HYDRAULICS_2_INTERNAL_LEAKAGE"]
        ["ROOF_AIRSPEED_INDICATOR_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ROOF_AIRSPEED_INDICATOR_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ROOF_AIRSPEED_INDICATOR_FAILURE"]
        ["ENG0_NITROS_PUMP_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_NITROS_PUMP_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ENG0_NITROS_PUMP_FAILURE"]
        ["MWMMC_FAILURE_1553B_OESP"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MWMMC_FAILURE_1553B_OESP",
            ["mm"] = 0,
        }, -- end of ["MWMMC_FAILURE_1553B_OESP"]
        ["RDR_FAILURE_ARRAY"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RDR_FAILURE_ARRAY",
            ["mm"] = 0,
        }, -- end of ["RDR_FAILURE_ARRAY"]
        ["CTRL_ELEVATOR_TRIM_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CTRL_ELEVATOR_TRIM_FAILURE",
            ["mm"] = 0,
        }, -- end of ["CTRL_ELEVATOR_TRIM_FAILURE"]
        ["RDR_FAILURE_ANTENNA_DEGRATION"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RDR_FAILURE_ANTENNA_DEGRATION",
            ["mm"] = 0,
        }, -- end of ["RDR_FAILURE_ANTENNA_DEGRATION"]
        ["FUELTANK4R"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUELTANK4R",
            ["mm"] = 0,
        }, -- end of ["FUELTANK4R"]
        ["static_blocked"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "static_blocked",
            ["mm"] = 0,
        }, -- end of ["static_blocked"]
        ["MainReducer_Fire"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MainReducer_Fire",
            ["mm"] = 0,
        }, -- end of ["MainReducer_Fire"]
        ["CTRL_ELEVATOR_ROD_MINOR_DAMAGE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CTRL_ELEVATOR_ROD_MINOR_DAMAGE",
            ["mm"] = 0,
        }, -- end of ["CTRL_ELEVATOR_ROD_MINOR_DAMAGE"]
        ["FAILURE_HYDRAULICS_1_INTERNAL_LEAKAGE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAILURE_HYDRAULICS_1_INTERNAL_LEAKAGE",
            ["mm"] = 0,
        }, -- end of ["FAILURE_HYDRAULICS_1_INTERNAL_LEAKAGE"]
        ["MWMMC_FAILURE_1553B_HUD"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MWMMC_FAILURE_1553B_HUD",
            ["mm"] = 0,
        }, -- end of ["MWMMC_FAILURE_1553B_HUD"]
        ["EGTSENSOR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EGTSENSOR",
            ["mm"] = 0,
        }, -- end of ["EGTSENSOR"]
        ["FUEL_NITRO_TANK_00_LEAK_STOPPED"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "FUEL_NITRO_TANK_00_LEAK_STOPPED",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["FUEL_NITRO_TANK_00_LEAK_STOPPED"]
        ["RKL_41_ANT_DAMAGE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RKL_41_ANT_DAMAGE",
            ["mm"] = 0,
        }, -- end of ["RKL_41_ANT_DAMAGE"]
        ["tr2_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "tr2_fail",
            ["mm"] = 0,
        }, -- end of ["tr2_fail"]
        ["SWMMC_FAILURE_HUD"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SWMMC_FAILURE_HUD",
            ["mm"] = 0,
        }, -- end of ["SWMMC_FAILURE_HUD"]
        ["STARTER_RELAY_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "STARTER_RELAY_FAULT",
            ["mm"] = 0,
        }, -- end of ["STARTER_RELAY_FAULT"]
        ["FCS_FAILURE_PITCH_RATE_GYRO_2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_PITCH_RATE_GYRO_2",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_PITCH_RATE_GYRO_2"]
        ["BCKGYRO"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BCKGYRO",
            ["mm"] = 0,
        }, -- end of ["BCKGYRO"]
        ["INST_TACH0_RESISTANCE_MISMATCH"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INST_TACH0_RESISTANCE_MISMATCH",
            ["mm"] = 0,
        }, -- end of ["INST_TACH0_RESISTANCE_MISMATCH"]
        ["OXYGEN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "OXYGEN",
            ["mm"] = 0,
        }, -- end of ["OXYGEN"]
        ["hsf_UtilityHydraulic"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "hsf_UtilityHydraulic",
            ["mm"] = 0,
        }, -- end of ["hsf_UtilityHydraulic"]
        ["TACH_BREAK_IN_INDICATOR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TACH_BREAK_IN_INDICATOR",
            ["mm"] = 0,
        }, -- end of ["TACH_BREAK_IN_INDICATOR"]
        ["pp_damage_MainMaxNormFreq"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "pp_damage_MainMaxNormFreq",
            ["mm"] = 0,
        }, -- end of ["pp_damage_MainMaxNormFreq"]
        ["HORIZON_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "HORIZON_FAULT",
            ["mm"] = 0,
        }, -- end of ["HORIZON_FAULT"]
        ["ELEC_BOOSTER_FUEL_PUMP_COIL_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_BOOSTER_FUEL_PUMP_COIL_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_BOOSTER_FUEL_PUMP_COIL_FAILURE"]
        ["FAILURE_HYDRAULICS_2_EXTERNAL_LEAKAGE_SEVERE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAILURE_HYDRAULICS_2_EXTERNAL_LEAKAGE_SEVERE",
            ["mm"] = 0,
        }, -- end of ["FAILURE_HYDRAULICS_2_EXTERNAL_LEAKAGE_SEVERE"]
        ["left_wing_leaks"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "left_wing_leaks",
            ["mm"] = 0,
        }, -- end of ["left_wing_leaks"]
        ["28"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "28",
            ["mm"] = 0,
        }, -- end of ["28"]
        ["INST_DI_EXCESSIVE_DRIFT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INST_DI_EXCESSIVE_DRIFT",
            ["mm"] = 0,
        }, -- end of ["INST_DI_EXCESSIVE_DRIFT"]
        ["HYDR1ACC"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "HYDR1ACC",
            ["mm"] = 0,
        }, -- end of ["HYDR1ACC"]
        ["BAT_SOLENOID_WIRING"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BAT_SOLENOID_WIRING",
            ["mm"] = 0,
        }, -- end of ["BAT_SOLENOID_WIRING"]
        ["ELEC_BOOSTER_FUEL_PUMP_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_BOOSTER_FUEL_PUMP_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_BOOSTER_FUEL_PUMP_FAILURE"]
        ["LANDINGGEARR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "LANDINGGEARR",
            ["mm"] = 0,
        }, -- end of ["LANDINGGEARR"]
        ["fuel_sys_swapping_pumps"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "fuel_sys_swapping_pumps",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["fuel_sys_swapping_pumps"]
        ["UHF_RADIO_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "UHF_RADIO_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["UHF_RADIO_FAILURE_TOTAL"]
        ["APU_Fire"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "APU_Fire",
            ["mm"] = 0,
        }, -- end of ["APU_Fire"]
        ["incidometer_blockage_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "incidometer_blockage_fail",
            ["mm"] = 0,
        }, -- end of ["incidometer_blockage_fail"]
        ["PNEM_SUPERCHARGER_JACK_JAMMED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "PNEM_SUPERCHARGER_JACK_JAMMED",
            ["mm"] = 0,
        }, -- end of ["PNEM_SUPERCHARGER_JACK_JAMMED"]
        ["ENG0_STARTER_CLUTCH_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_STARTER_CLUTCH_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ENG0_STARTER_CLUTCH_FAILURE"]
        ["HYD_PUMP_3_FAIL_100"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "HYD_PUMP_3_FAIL_100",
            ["mm"] = 0,
        }, -- end of ["HYD_PUMP_3_FAIL_100"]
        ["TCN_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TCN_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["TCN_FAILURE_TOTAL"]
        ["FCS_FAILURE_L_ELEVATOR_ELEC_B"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_L_ELEVATOR_ELEC_B",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_L_ELEVATOR_ELEC_B"]
        ["FCS_FAILURE_ROLL_RATE_GYRO_2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_ROLL_RATE_GYRO_2",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_ROLL_RATE_GYRO_2"]
        ["ELEC_FUEL_PUMP_P2_COIL_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_FUEL_PUMP_P2_COIL_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_FUEL_PUMP_P2_COIL_FAILURE"]
        ["INST_TACH0_LOOM_SEVERED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INST_TACH0_LOOM_SEVERED",
            ["mm"] = 0,
        }, -- end of ["INST_TACH0_LOOM_SEVERED"]
        ["DVMS_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "DVMS_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["DVMS_FAILURE_TOTAL"]
        ["VHF_FM_RADIO_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "VHF_FM_RADIO_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["VHF_FM_RADIO_FAILURE_TOTAL"]
        ["BOMBS_DAMAGE_ELINKAGE_RIGHT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BOMBS_DAMAGE_ELINKAGE_RIGHT",
            ["mm"] = 0,
        }, -- end of ["BOMBS_DAMAGE_ELINKAGE_RIGHT"]
        ["FUELTANK1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUELTANK1",
            ["mm"] = 0,
        }, -- end of ["FUELTANK1"]
        ["external_tanks_transfer_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "external_tanks_transfer_fail",
            ["mm"] = 0,
        }, -- end of ["external_tanks_transfer_fail"]
        ["ENG0_WATER_RADIATOR_1_MEDIUM_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_WATER_RADIATOR_1_MEDIUM_LEAK",
            ["mm"] = 0,
        }, -- end of ["ENG0_WATER_RADIATOR_1_MEDIUM_LEAK"]
        ["EMMC_FAILURE_BATTERY_DC1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_BATTERY_DC1",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_BATTERY_DC1"]
        ["CADC_WING_SWEEP_COMMAND_CHANNEL_2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CADC_WING_SWEEP_COMMAND_CHANNEL_2",
            ["mm"] = 0,
        }, -- end of ["CADC_WING_SWEEP_COMMAND_CHANNEL_2"]
        ["ELEC_GOVERNOR_BOX_DAMAGED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_GOVERNOR_BOX_DAMAGED",
            ["mm"] = 0,
        }, -- end of ["ELEC_GOVERNOR_BOX_DAMAGED"]
        ["ecf"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["mm"] = 0,
        }, -- end of ["ecf"]
        ["rotor"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "rotor",
            ["mm"] = 0,
        }, -- end of ["rotor"]
        ["GMC1AE_GYRO_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GMC1AE_GYRO_FAILURE",
            ["mm"] = 0,
        }, -- end of ["GMC1AE_GYRO_FAILURE"]
        ["ELEC_RH_CABIN_LIGHT_POOR_CONTACT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_RH_CABIN_LIGHT_POOR_CONTACT",
            ["mm"] = 0,
        }, -- end of ["ELEC_RH_CABIN_LIGHT_POOR_CONTACT"]
        ["REVERSER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "REVERSER",
            ["mm"] = 0,
        }, -- end of ["REVERSER"]
        ["Failure_Ctrl_Aileron"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Ctrl_Aileron",
            ["mm"] = 0,
        }, -- end of ["Failure_Ctrl_Aileron"]
        ["OXY_FAILURE_HIGH_PRESS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "OXY_FAILURE_HIGH_PRESS",
            ["mm"] = 0,
        }, -- end of ["OXY_FAILURE_HIGH_PRESS"]
        ["autopilot"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "autopilot",
            ["mm"] = 0,
        }, -- end of ["autopilot"]
        ["OXYN_TOP_CONTAINER_PERFORATED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "OXYN_TOP_CONTAINER_PERFORATED",
            ["mm"] = 0,
        }, -- end of ["OXYN_TOP_CONTAINER_PERFORATED"]
        ["WEAP_GUN_06_AMMO_BELT_SEVERED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "WEAP_GUN_06_AMMO_BELT_SEVERED",
            ["mm"] = 0,
        }, -- end of ["WEAP_GUN_06_AMMO_BELT_SEVERED"]
        ["RDR_FAILURE_TRANSMITTER_DEGRATION"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RDR_FAILURE_TRANSMITTER_DEGRATION",
            ["mm"] = 0,
        }, -- end of ["RDR_FAILURE_TRANSMITTER_DEGRATION"]
        ["right_wing_transfer_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "right_wing_transfer_fail",
            ["mm"] = 0,
        }, -- end of ["right_wing_transfer_fail"]
        ["AHRS_FAILURE_MAD"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AHRS_FAILURE_MAD",
            ["mm"] = 0,
        }, -- end of ["AHRS_FAILURE_MAD"]
        ["ENG0_WATER_RADIATOR_1_MINOR_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_WATER_RADIATOR_1_MINOR_LEAK",
            ["mm"] = 0,
        }, -- end of ["ENG0_WATER_RADIATOR_1_MINOR_LEAK"]
        ["TURBINE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TURBINE",
            ["mm"] = 0,
        }, -- end of ["TURBINE"]
        ["right_front_transfer_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "right_front_transfer_fail",
            ["mm"] = 0,
        }, -- end of ["right_front_transfer_fail"]
        ["FUEL_BOOSTER_FUEL_PUMP_2_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_BOOSTER_FUEL_PUMP_2_FAILURE",
            ["mm"] = 0,
        }, -- end of ["FUEL_BOOSTER_FUEL_PUMP_2_FAILURE"]
        ["AUTOPILOT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AUTOPILOT",
            ["mm"] = 0,
        }, -- end of ["AUTOPILOT"]
        ["fs_aft_RH_leakage"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fs_aft_RH_leakage",
            ["mm"] = 0,
        }, -- end of ["fs_aft_RH_leakage"]
        ["discharge_valves_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "discharge_valves_fail",
            ["mm"] = 0,
        }, -- end of ["discharge_valves_fail"]
        ["nosecone_stuck_backward"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "nosecone_stuck_backward",
            ["mm"] = 0,
        }, -- end of ["nosecone_stuck_backward"]
        ["TURNIND_POINTER_NOT_SET_ZERO"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TURNIND_POINTER_NOT_SET_ZERO",
            ["mm"] = 0,
        }, -- end of ["TURNIND_POINTER_NOT_SET_ZERO"]
        ["CADC_PRESSURE_SENSOR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CADC_PRESSURE_SENSOR",
            ["mm"] = 0,
        }, -- end of ["CADC_PRESSURE_SENSOR"]
        ["Failure_Gear_WOW"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Gear_WOW",
            ["mm"] = 0,
        }, -- end of ["Failure_Gear_WOW"]
        ["dme_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "dme_fail",
            ["mm"] = 0,
        }, -- end of ["dme_fail"]
        ["UHF_ARC_159_FAILURE_REMOTE_DISPLAY"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "UHF_ARC_159_FAILURE_REMOTE_DISPLAY",
            ["mm"] = 0,
        }, -- end of ["UHF_ARC_159_FAILURE_REMOTE_DISPLAY"]
        ["GUN_LEFT_CENTER_MOUNT_LOOSE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_LEFT_CENTER_MOUNT_LOOSE",
            ["mm"] = 0,
        }, -- end of ["GUN_LEFT_CENTER_MOUNT_LOOSE"]
        ["WEAP_GUN_03_DAMAGED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "WEAP_GUN_03_DAMAGED",
            ["mm"] = 0,
        }, -- end of ["WEAP_GUN_03_DAMAGED"]
        ["Failure_Ctrl_FCS_Ch1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Ctrl_FCS_Ch1",
            ["mm"] = 0,
        }, -- end of ["Failure_Ctrl_FCS_Ch1"]
        ["RDR_FAILURE_PROCESSOR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RDR_FAILURE_PROCESSOR",
            ["mm"] = 0,
        }, -- end of ["RDR_FAILURE_PROCESSOR"]
        ["asc_p"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "asc_p",
            ["mm"] = 0,
        }, -- end of ["asc_p"]
        ["oxygen_regulator_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "oxygen_regulator_fail",
            ["mm"] = 0,
        }, -- end of ["oxygen_regulator_fail"]
        ["OXYN_LEFT_CONTAINER_PERFORATED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "OXYN_LEFT_CONTAINER_PERFORATED",
            ["mm"] = 0,
        }, -- end of ["OXYN_LEFT_CONTAINER_PERFORATED"]
        ["RDR_FAILURE_PROCESSOR_DEGRATION"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RDR_FAILURE_PROCESSOR_DEGRATION",
            ["mm"] = 0,
        }, -- end of ["RDR_FAILURE_PROCESSOR_DEGRATION"]
        ["loc_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "loc_fail",
            ["mm"] = 0,
        }, -- end of ["loc_fail"]
        ["ELEC_SIGNAL_LIGHTS_MALFUNCTION"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_SIGNAL_LIGHTS_MALFUNCTION",
            ["mm"] = 0,
        }, -- end of ["ELEC_SIGNAL_LIGHTS_MALFUNCTION"]
        ["29"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "29",
            ["mm"] = 0,
        }, -- end of ["29"]
        ["pp_damage_EmergMaxFreq"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "pp_damage_EmergMaxFreq",
            ["mm"] = 0,
        }, -- end of ["pp_damage_EmergMaxFreq"]
        ["GUN_LEFT_MG151_AMMUN_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_LEFT_MG151_AMMUN_FAULT",
            ["mm"] = 0,
        }, -- end of ["GUN_LEFT_MG151_AMMUN_FAULT"]
        ["ef_rt12"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ef_rt12",
            ["mm"] = 0,
        }, -- end of ["ef_rt12"]
        ["UNCR_RH_STRUT_HOSE_PIERCED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "UNCR_RH_STRUT_HOSE_PIERCED",
            ["mm"] = 0,
        }, -- end of ["UNCR_RH_STRUT_HOSE_PIERCED"]
        ["es_damage_DMR"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "es_damage_DMR",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["es_damage_DMR"]
        ["AN_ALR69V_FAILURE_SENSOR_NOSE_RIGHT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AN_ALR69V_FAILURE_SENSOR_NOSE_RIGHT",
            ["mm"] = 0,
        }, -- end of ["AN_ALR69V_FAILURE_SENSOR_NOSE_RIGHT"]
        ["BOMBS_DAMAGE_LINKAGE_RIGHT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BOMBS_DAMAGE_LINKAGE_RIGHT",
            ["mm"] = 0,
        }, -- end of ["BOMBS_DAMAGE_LINKAGE_RIGHT"]
        ["trim_elect_supply"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "trim_elect_supply",
            ["mm"] = 0,
        }, -- end of ["trim_elect_supply"]
        ["starter_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "starter_fail",
            ["mm"] = 0,
        }, -- end of ["starter_fail"]
        ["ef_fire"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ef_fire",
            ["mm"] = 0,
        }, -- end of ["ef_fire"]
        ["CTRL_LANDING_FLAPS_LH_MECHANICAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CTRL_LANDING_FLAPS_LH_MECHANICAL",
            ["mm"] = 0,
        }, -- end of ["CTRL_LANDING_FLAPS_LH_MECHANICAL"]
        ["detotalizer_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "detotalizer_fail",
            ["mm"] = 0,
        }, -- end of ["detotalizer_fail"]
        ["engine_antiice_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "engine_antiice_fail",
            ["mm"] = 0,
        }, -- end of ["engine_antiice_fail"]
        ["ELEC_STARTER_LOOM_SEVERED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_STARTER_LOOM_SEVERED",
            ["mm"] = 0,
        }, -- end of ["ELEC_STARTER_LOOM_SEVERED"]
        ["trim_pitch_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "trim_pitch_fail",
            ["mm"] = 0,
        }, -- end of ["trim_pitch_fail"]
        ["RDR_FAILURE_RECEIVER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RDR_FAILURE_RECEIVER",
            ["mm"] = 0,
        }, -- end of ["RDR_FAILURE_RECEIVER"]
        ["EMMC_FAILURE_COCKPIT_PRESSURE_LOW"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_COCKPIT_PRESSURE_LOW",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_COCKPIT_PRESSURE_LOW"]
        ["SNS_FAILURE_ANTENNA"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SNS_FAILURE_ANTENNA",
            ["mm"] = 0,
        }, -- end of ["SNS_FAILURE_ANTENNA"]
        ["ELEC_LH_GEAR_DRIVE_WIRE_SEVERED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_LH_GEAR_DRIVE_WIRE_SEVERED",
            ["mm"] = 0,
        }, -- end of ["ELEC_LH_GEAR_DRIVE_WIRE_SEVERED"]
        ["EMMC_FAILURE_BAU"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_BAU",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_BAU"]
        ["pilot_aids_1_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "pilot_aids_1_fail",
            ["mm"] = 0,
        }, -- end of ["pilot_aids_1_fail"]
        ["ppf_RightOil"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ppf_RightOil",
            ["mm"] = 0,
        }, -- end of ["ppf_RightOil"]
        ["MAGNETO_1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MAGNETO_1",
            ["mm"] = 0,
        }, -- end of ["MAGNETO_1"]
        ["BOMBS_NO_VOLATAGE_AT_RACK_RIGHT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BOMBS_NO_VOLATAGE_AT_RACK_RIGHT",
            ["mm"] = 0,
        }, -- end of ["BOMBS_NO_VOLATAGE_AT_RACK_RIGHT"]
        ["FCS_FAILURE_WOW_2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_WOW_2",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_WOW_2"]
        ["right_feeder_leaks"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "right_feeder_leaks",
            ["mm"] = 0,
        }, -- end of ["right_feeder_leaks"]
        ["ols_damage_OilPump"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ols_damage_OilPump",
            ["mm"] = 0,
        }, -- end of ["ols_damage_OilPump"]
        ["WEAP_GUN_01_AMMO_BELT_SEVERED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "WEAP_GUN_01_AMMO_BELT_SEVERED",
            ["mm"] = 0,
        }, -- end of ["WEAP_GUN_01_AMMO_BELT_SEVERED"]
        ["gear_right_stuck"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "gear_right_stuck",
            ["mm"] = 0,
        }, -- end of ["gear_right_stuck"]
        ["TGP_FAILURE_LEFT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TGP_FAILURE_LEFT",
            ["mm"] = 0,
        }, -- end of ["TGP_FAILURE_LEFT"]
        ["L_ENG_AICS_RAMP_FAIL_IN_POS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "L_ENG_AICS_RAMP_FAIL_IN_POS",
            ["mm"] = 0,
        }, -- end of ["L_ENG_AICS_RAMP_FAIL_IN_POS"]
        ["ppf_RightGearbox"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ppf_RightGearbox",
            ["mm"] = 0,
        }, -- end of ["ppf_RightGearbox"]
        ["gear_down_lock_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "gear_down_lock_fail",
            ["mm"] = 0,
        }, -- end of ["gear_down_lock_fail"]
        ["STATION_6_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "STATION_6_FAILURE",
            ["mm"] = 0,
        }, -- end of ["STATION_6_FAILURE"]
        ["EMMC_FAILURE_DADS_RPTU"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_DADS_RPTU",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_DADS_RPTU"]
        ["BOMBS_TRAIN_DEFECTIVE_SWITCH"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BOMBS_TRAIN_DEFECTIVE_SWITCH",
            ["mm"] = 0,
        }, -- end of ["BOMBS_TRAIN_DEFECTIVE_SWITCH"]
        ["OIL_SYSTEM_FAIL_100"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "OIL_SYSTEM_FAIL_100",
            ["mm"] = 0,
        }, -- end of ["OIL_SYSTEM_FAIL_100"]
        ["FAILURE_HYDRAULICS_1_EXTERNAL_LEAKAGE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAILURE_HYDRAULICS_1_EXTERNAL_LEAKAGE",
            ["mm"] = 0,
        }, -- end of ["FAILURE_HYDRAULICS_1_EXTERNAL_LEAKAGE"]
        ["EMMC_FAILURE_AC_GENERATOR_SUBSYSTEM"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_AC_GENERATOR_SUBSYSTEM",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_AC_GENERATOR_SUBSYSTEM"]
        ["FAILURE_HYDRAULICS_2_ACCU"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAILURE_HYDRAULICS_2_ACCU",
            ["mm"] = 0,
        }, -- end of ["FAILURE_HYDRAULICS_2_ACCU"]
        ["BACKUPGENERATOR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BACKUPGENERATOR",
            ["mm"] = 0,
        }, -- end of ["BACKUPGENERATOR"]
        ["FCS_FAILURE_PITCH_LVDT_2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_PITCH_LVDT_2",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_PITCH_LVDT_2"]
        ["EMMC_FAILURE_TRU_AC2DC28V"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_TRU_AC2DC28V",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_TRU_AC2DC28V"]
        ["L_GEAR_MOTOR_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "L_GEAR_MOTOR_FAULT",
            ["mm"] = 0,
        }, -- end of ["L_GEAR_MOTOR_FAULT"]
        ["ppf_FireRight"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ppf_FireRight",
            ["mm"] = 0,
        }, -- end of ["ppf_FireRight"]
        ["SHARS_FAILURE_SENSOR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SHARS_FAILURE_SENSOR",
            ["mm"] = 0,
        }, -- end of ["SHARS_FAILURE_SENSOR"]
        ["OXY_FAILURE_R_LEAK_SEVERE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "OXY_FAILURE_R_LEAK_SEVERE",
            ["mm"] = 0,
        }, -- end of ["OXY_FAILURE_R_LEAK_SEVERE"]
        ["FCS_FAILURE_R_ELEVATOR_ELEC_B"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_R_ELEVATOR_ELEC_B",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_R_ELEVATOR_ELEC_B"]
        ["ELEVONOUTERRIGHT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEVONOUTERRIGHT",
            ["mm"] = 0,
        }, -- end of ["ELEVONOUTERRIGHT"]
        ["pp_damage_MainMaxTempr"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "pp_damage_MainMaxTempr",
            ["mm"] = 0,
        }, -- end of ["pp_damage_MainMaxTempr"]
        ["UNCR_LH_WHEEL_BRAKE_DAMAGED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "UNCR_LH_WHEEL_BRAKE_DAMAGED",
            ["mm"] = 0,
        }, -- end of ["UNCR_LH_WHEEL_BRAKE_DAMAGED"]
        ["SWMMC_FAILURE_AVI"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SWMMC_FAILURE_AVI",
            ["mm"] = 0,
        }, -- end of ["SWMMC_FAILURE_AVI"]
        ["fsf_CrossfeedValve"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fsf_CrossfeedValve",
            ["mm"] = 0,
        }, -- end of ["fsf_CrossfeedValve"]
        ["INST_VARIOMETR_DEPRESSURIZATION"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INST_VARIOMETR_DEPRESSURIZATION",
            ["mm"] = 0,
        }, -- end of ["INST_VARIOMETR_DEPRESSURIZATION"]
        ["ELEC_MAIN_FUEL_PUMP_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_MAIN_FUEL_PUMP_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_MAIN_FUEL_PUMP_FAILURE"]
        ["aoa_limiter"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "aoa_limiter",
            ["mm"] = 0,
        }, -- end of ["aoa_limiter"]
        ["STARTER_LOSE_CON"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "STARTER_LOSE_CON",
            ["mm"] = 0,
        }, -- end of ["STARTER_LOSE_CON"]
        ["RGear_ext_fault"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RGear_ext_fault",
            ["mm"] = 0,
        }, -- end of ["RGear_ext_fault"]
        ["ENG0_WASTEGATE_OIL_FEED_CLOGGED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_WASTEGATE_OIL_FEED_CLOGGED",
            ["mm"] = 0,
        }, -- end of ["ENG0_WASTEGATE_OIL_FEED_CLOGGED"]
        ["BOOST_REG"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BOOST_REG",
            ["mm"] = 0,
        }, -- end of ["BOOST_REG"]
        ["ELEC_UMFORMER_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_UMFORMER_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_UMFORMER_FAILURE"]
        ["r_conv"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "r_conv",
            ["mm"] = 0,
        }, -- end of ["r_conv"]
        ["DDD_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "DDD_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["DDD_FAILURE_TOTAL"]
        ["mlws"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "mlws",
            ["mm"] = 0,
        }, -- end of ["mlws"]
        ["FUELTANK2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUELTANK2",
            ["mm"] = 0,
        }, -- end of ["FUELTANK2"]
        ["44"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "44",
            ["mm"] = 0,
        }, -- end of ["44"]
        ["FAILURE_HYDRAULICS_EMERGE_ACCU"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAILURE_HYDRAULICS_EMERGE_ACCU",
            ["mm"] = 0,
        }, -- end of ["FAILURE_HYDRAULICS_EMERGE_ACCU"]
        ["fire_sys_fire_LE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fire_sys_fire_LE",
            ["mm"] = 0,
        }, -- end of ["fire_sys_fire_LE"]
        ["BOMBS_SOLENOID_FAULT_RIGHT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BOMBS_SOLENOID_FAULT_RIGHT",
            ["mm"] = 0,
        }, -- end of ["BOMBS_SOLENOID_FAULT_RIGHT"]
        ["FLIR_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FLIR_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["FLIR_FAILURE_TOTAL"]
        ["inverters_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "inverters_fail",
            ["mm"] = 0,
        }, -- end of ["inverters_fail"]
        ["l_conv"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "l_conv",
            ["mm"] = 0,
        }, -- end of ["l_conv"]
        ["STRAKE_LEFT_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "STRAKE_LEFT_FAILURE",
            ["mm"] = 0,
        }, -- end of ["STRAKE_LEFT_FAILURE"]
        ["CMS_FAILURE_PROGRAMMER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CMS_FAILURE_PROGRAMMER",
            ["mm"] = 0,
        }, -- end of ["CMS_FAILURE_PROGRAMMER"]
        ["es_damage_VU1"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "es_damage_VU1",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["es_damage_VU1"]
        ["elevator_loss"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "elevator_loss",
            ["mm"] = 0,
        }, -- end of ["elevator_loss"]
        ["ELEC_RED_SIGNAL_LIGHT_BROKEN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_RED_SIGNAL_LIGHT_BROKEN",
            ["mm"] = 0,
        }, -- end of ["ELEC_RED_SIGNAL_LIGHT_BROKEN"]
        ["CADC_FAILURE_BARO_ALT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CADC_FAILURE_BARO_ALT",
            ["mm"] = 0,
        }, -- end of ["CADC_FAILURE_BARO_ALT"]
        ["gyros_temp_drift"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "gyros_temp_drift",
            ["mm"] = 0,
        }, -- end of ["gyros_temp_drift"]
        ["HUD_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "HUD_FAILURE",
            ["mm"] = 0,
        }, -- end of ["HUD_FAILURE"]
        ["PNEM_LOW_PRS_JUNCTION_BOX_DESTROYED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "PNEM_LOW_PRS_JUNCTION_BOX_DESTROYED",
            ["mm"] = 0,
        }, -- end of ["PNEM_LOW_PRS_JUNCTION_BOX_DESTROYED"]
        ["compressor_damage"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "compressor_damage",
            ["mm"] = 0,
        }, -- end of ["compressor_damage"]
        ["mfd"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "mfd",
            ["mm"] = 0,
        }, -- end of ["mfd"]
        ["CARBAIR_GND_LEAD"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CARBAIR_GND_LEAD",
            ["mm"] = 0,
        }, -- end of ["CARBAIR_GND_LEAD"]
        ["fuel_leak"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fuel_leak",
            ["mm"] = 0,
        }, -- end of ["fuel_leak"]
        ["AIRBRAKESERVO"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AIRBRAKESERVO",
            ["mm"] = 0,
        }, -- end of ["AIRBRAKESERVO"]
        ["hs_damage_MainAccumulator"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "hs_damage_MainAccumulator",
            ["mm"] = 0,
        }, -- end of ["hs_damage_MainAccumulator"]
        ["PNEM_LH_BRAKE_HOSE_PERFORATED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "PNEM_LH_BRAKE_HOSE_PERFORATED",
            ["mm"] = 0,
        }, -- end of ["PNEM_LH_BRAKE_HOSE_PERFORATED"]
        ["AFK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AFK",
            ["mm"] = 0,
        }, -- end of ["AFK"]
        ["UHF_ARC_159_FAILURE_INTERNAL_MODULE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "UHF_ARC_159_FAILURE_INTERNAL_MODULE",
            ["mm"] = 0,
        }, -- end of ["UHF_ARC_159_FAILURE_INTERNAL_MODULE"]
        ["STATION_2_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "STATION_2_FAILURE",
            ["mm"] = 0,
        }, -- end of ["STATION_2_FAILURE"]
        ["INST_CLOCK_MALFUNCTION"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INST_CLOCK_MALFUNCTION",
            ["mm"] = 0,
        }, -- end of ["INST_CLOCK_MALFUNCTION"]
        ["pp_damage_Ignition"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "pp_damage_Ignition",
            ["mm"] = 0,
        }, -- end of ["pp_damage_Ignition"]
        ["WEAP_GUN_03_AMMO_BELT_SEVERED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "WEAP_GUN_03_AMMO_BELT_SEVERED",
            ["mm"] = 0,
        }, -- end of ["WEAP_GUN_03_AMMO_BELT_SEVERED"]
        ["FUEL_ENGINE0_FUEL_PUMP_DEGRADED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_ENGINE0_FUEL_PUMP_DEGRADED",
            ["mm"] = 0,
        }, -- end of ["FUEL_ENGINE0_FUEL_PUMP_DEGRADED"]
        ["ILS_FAILURE_ANT_GLIDESLOPE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ILS_FAILURE_ANT_GLIDESLOPE",
            ["mm"] = 0,
        }, -- end of ["ILS_FAILURE_ANT_GLIDESLOPE"]
        ["FUELTANK3L"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUELTANK3L",
            ["mm"] = 0,
        }, -- end of ["FUELTANK3L"]
        ["EEC_Failure_RightEngine"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EEC_Failure_RightEngine",
            ["mm"] = 0,
        }, -- end of ["EEC_Failure_RightEngine"]
        ["hydro_auxiliary"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "hydro_auxiliary",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["hydro_auxiliary"]
        ["GUN_LEFT_MG151_OPEN_CIRCUIT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_LEFT_MG151_OPEN_CIRCUIT",
            ["mm"] = 0,
        }, -- end of ["GUN_LEFT_MG151_OPEN_CIRCUIT"]
        ["slats"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "slats",
            ["mm"] = 0,
        }, -- end of ["slats"]
        ["CNI_FAILURE_ILS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CNI_FAILURE_ILS",
            ["mm"] = 0,
        }, -- end of ["CNI_FAILURE_ILS"]
        ["ROCKETS_DEFECTIVE_WIRING"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ROCKETS_DEFECTIVE_WIRING",
            ["mm"] = 0,
        }, -- end of ["ROCKETS_DEFECTIVE_WIRING"]
        ["ICS_FAILURE_AMPLIFIER_PILOT_BU"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ICS_FAILURE_AMPLIFIER_PILOT_BU",
            ["mm"] = 0,
        }, -- end of ["ICS_FAILURE_AMPLIFIER_PILOT_BU"]
        ["hydro_diaphragm"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "hydro_diaphragm",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["hydro_diaphragm"]
        ["eng_computer_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "eng_computer_fail",
            ["mm"] = 0,
        }, -- end of ["eng_computer_fail"]
        ["EXT_TANK_PUMP_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EXT_TANK_PUMP_FAULT",
            ["mm"] = 0,
        }, -- end of ["EXT_TANK_PUMP_FAULT"]
        ["FUEL_FUSELAGE_TANK_MINOR_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_FUSELAGE_TANK_MINOR_LEAK",
            ["mm"] = 0,
        }, -- end of ["FUEL_FUSELAGE_TANK_MINOR_LEAK"]
        ["left_wing_transfer_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "left_wing_transfer_fail",
            ["mm"] = 0,
        }, -- end of ["left_wing_transfer_fail"]
        ["EMMC_FAILURE_DADS_MPTU"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_DADS_MPTU",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_DADS_MPTU"]
        ["MWMMC_FAILURE_DMP"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MWMMC_FAILURE_DMP",
            ["mm"] = 0,
        }, -- end of ["MWMMC_FAILURE_DMP"]
        ["fsf_AutoBalance"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fsf_AutoBalance",
            ["mm"] = 0,
        }, -- end of ["fsf_AutoBalance"]
        ["ELEVONINNERLEFT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEVONINNERLEFT",
            ["mm"] = 0,
        }, -- end of ["ELEVONINNERLEFT"]
        ["FCS_FAILURE_PITCH_LVDT_4"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_PITCH_LVDT_4",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_PITCH_LVDT_4"]
        ["ENG0_TURBINE_IMPELLER_DAMAGED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_TURBINE_IMPELLER_DAMAGED",
            ["mm"] = 0,
        }, -- end of ["ENG0_TURBINE_IMPELLER_DAMAGED"]
        ["Failure_PP_LeftAMAD_OilLeak"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_PP_LeftAMAD_OilLeak",
            ["mm"] = 0,
        }, -- end of ["Failure_PP_LeftAMAD_OilLeak"]
        ["es_damage_EmergGen"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "es_damage_EmergGen",
            ["mm"] = 0,
        }, -- end of ["es_damage_EmergGen"]
        ["NOSE_AIRSPEED_INDICATOR_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "NOSE_AIRSPEED_INDICATOR_FAILURE",
            ["mm"] = 0,
        }, -- end of ["NOSE_AIRSPEED_INDICATOR_FAILURE"]
        ["RIGHT_FLAP_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RIGHT_FLAP_FAULT",
            ["mm"] = 0,
        }, -- end of ["RIGHT_FLAP_FAULT"]
        ["GMC_TOTAL_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GMC_TOTAL_FAILURE",
            ["mm"] = 0,
        }, -- end of ["GMC_TOTAL_FAILURE"]
        ["SWMMC_FAILURE_DMP"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SWMMC_FAILURE_DMP",
            ["mm"] = 0,
        }, -- end of ["SWMMC_FAILURE_DMP"]
        ["EMMC_FAILURE_EMMC"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_EMMC",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_EMMC"]
        ["ELEC_BOMBABWGERAT_RELEASE_MALFUNCTION"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_BOMBABWGERAT_RELEASE_MALFUNCTION",
            ["mm"] = 0,
        }, -- end of ["ELEC_BOMBABWGERAT_RELEASE_MALFUNCTION"]
        ["RWRANTREAR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWRANTREAR",
            ["mm"] = 0,
        }, -- end of ["RWRANTREAR"]
        ["FCS_FAILURE_YAW_RATE_GYRO_1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_YAW_RATE_GYRO_1",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_YAW_RATE_GYRO_1"]
        ["TRIMMER_DRIVE_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TRIMMER_DRIVE_FAULT",
            ["mm"] = 0,
        }, -- end of ["TRIMMER_DRIVE_FAULT"]
        ["acs"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["mm"] = 0,
        }, -- end of ["acs"]
        ["PNEM_CANNONS_HOSE_PERFORATED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "PNEM_CANNONS_HOSE_PERFORATED",
            ["mm"] = 0,
        }, -- end of ["PNEM_CANNONS_HOSE_PERFORATED"]
        ["VHF_CRFUEL_MAIN_TANK_MINOR_LEAKYSTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "VHF_CRFUEL_MAIN_TANK_MINOR_LEAKYSTAL",
            ["mm"] = 0,
        }, -- end of ["VHF_CRFUEL_MAIN_TANK_MINOR_LEAKYSTAL"]
        ["AN_ALE_40V_FAILURE_CONTAINER_RIGHT_GEAR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AN_ALE_40V_FAILURE_CONTAINER_RIGHT_GEAR",
            ["mm"] = 0,
        }, -- end of ["AN_ALE_40V_FAILURE_CONTAINER_RIGHT_GEAR"]
        ["os_damage_BalloonLeakage"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "os_damage_BalloonLeakage",
            ["mm"] = 0,
        }, -- end of ["os_damage_BalloonLeakage"]
        ["K14_MOV_LAMP_DEFECTIVE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "K14_MOV_LAMP_DEFECTIVE",
            ["mm"] = 0,
        }, -- end of ["K14_MOV_LAMP_DEFECTIVE"]
        ["RWR_FAILURE_ANTENNA_FRONT_RIGHT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWR_FAILURE_ANTENNA_FRONT_RIGHT",
            ["mm"] = 0,
        }, -- end of ["RWR_FAILURE_ANTENNA_FRONT_RIGHT"]
        ["SAR_1_2_95"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SAR_1_2_95",
            ["mm"] = 0,
        }, -- end of ["SAR_1_2_95"]
        ["RWR_FAILURE_SENSOR_TAIL_R"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWR_FAILURE_SENSOR_TAIL_R",
            ["mm"] = 0,
        }, -- end of ["RWR_FAILURE_SENSOR_TAIL_R"]
        ["brakes_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "brakes_fail",
            ["mm"] = 0,
        }, -- end of ["brakes_fail"]
        ["TACH_RESISTANCE_ADJ"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TACH_RESISTANCE_ADJ",
            ["mm"] = 0,
        }, -- end of ["TACH_RESISTANCE_ADJ"]
        ["EMMC_FAILURE_FUEL_START_PUMP"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_FUEL_START_PUMP",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_FUEL_START_PUMP"]
        ["FUEL_TANK_01_LEAK_STOPPED"] = 
        {
            ["hh"] = 0,
            ["id"] = "FUEL_TANK_01_LEAK_STOPPED",
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["FUEL_TANK_01_LEAK_STOPPED"]
        ["TransitionalReductor_LowOilPressure"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "TransitionalReductor_LowOilPressure",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["TransitionalReductor_LowOilPressure"]
        ["FUEL_REAR_TANK_MINOR_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_REAR_TANK_MINOR_LEAK",
            ["mm"] = 0,
        }, -- end of ["FUEL_REAR_TANK_MINOR_LEAK"]
        ["GUN_RIGHT_MG131_BARREL_WORN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_RIGHT_MG131_BARREL_WORN",
            ["mm"] = 0,
        }, -- end of ["GUN_RIGHT_MG131_BARREL_WORN"]
        ["STRAKE_RIGHT_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "STRAKE_RIGHT_FAILURE",
            ["mm"] = 0,
        }, -- end of ["STRAKE_RIGHT_FAILURE"]
        ["PNEM_RADIATOR_JACK_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "PNEM_RADIATOR_JACK_FAILURE",
            ["mm"] = 0,
        }, -- end of ["PNEM_RADIATOR_JACK_FAILURE"]
        ["FCS_FAILURE_WOW_4"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_WOW_4",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_WOW_4"]
        ["FCS_FAILURE_NZ_SENSOR_1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_NZ_SENSOR_1",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_NZ_SENSOR_1"]
        ["ENG0_GOVERNOR_MALFUNCTION"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_GOVERNOR_MALFUNCTION",
            ["mm"] = 0,
        }, -- end of ["ENG0_GOVERNOR_MALFUNCTION"]
        ["left_airbrake_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "left_airbrake_fail",
            ["mm"] = 0,
        }, -- end of ["left_airbrake_fail"]
        ["ppf_RightNozzleControl"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ppf_RightNozzleControl",
            ["mm"] = 0,
        }, -- end of ["ppf_RightNozzleControl"]
        ["ENG0_GOVERNOR_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_GOVERNOR_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ENG0_GOVERNOR_FAILURE"]
        ["TransitionalReductor_ShaveInOil"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TransitionalReductor_ShaveInOil",
            ["mm"] = 0,
        }, -- end of ["TransitionalReductor_ShaveInOil"]
        ["main_reductor_chip"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "main_reductor_chip",
            ["mm"] = 0,
        }, -- end of ["main_reductor_chip"]
        ["left_rear_leaks"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "left_rear_leaks",
            ["mm"] = 0,
        }, -- end of ["left_rear_leaks"]
        ["generator_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "generator_fail",
            ["mm"] = 0,
        }, -- end of ["generator_fail"]
        ["CTRL_RUDDER_ROD_MINOR_DAMAGE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CTRL_RUDDER_ROD_MINOR_DAMAGE",
            ["mm"] = 0,
        }, -- end of ["CTRL_RUDDER_ROD_MINOR_DAMAGE"]
        ["engine_droop_failure"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "engine_droop_failure",
            ["mm"] = 0,
        }, -- end of ["engine_droop_failure"]
        ["cabin_temp_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "cabin_temp_fail",
            ["mm"] = 0,
        }, -- end of ["cabin_temp_fail"]
        ["FAILURE_SMS_PYLON_2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAILURE_SMS_PYLON_2",
            ["mm"] = 0,
        }, -- end of ["FAILURE_SMS_PYLON_2"]
        ["FUEL_TANK_00_LEAK_STOPPED"] = 
        {
            ["hh"] = 0,
            ["id"] = "FUEL_TANK_00_LEAK_STOPPED",
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["FUEL_TANK_00_LEAK_STOPPED"]
        ["INST_COMPASS_MALFUNCTION"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INST_COMPASS_MALFUNCTION",
            ["mm"] = 0,
        }, -- end of ["INST_COMPASS_MALFUNCTION"]
        ["FUEL_TANK_00_EXPLODED"] = 
        {
            ["hh"] = 0,
            ["id"] = "FUEL_TANK_00_EXPLODED",
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["FUEL_TANK_00_EXPLODED"]
        ["pitch_trim_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "pitch_trim_fail",
            ["mm"] = 0,
        }, -- end of ["pitch_trim_fail"]
        ["DTC_FAILURE_CARD_BROKEN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "DTC_FAILURE_CARD_BROKEN",
            ["mm"] = 0,
        }, -- end of ["DTC_FAILURE_CARD_BROKEN"]
        ["ELEC_REAR_FUEL_PUMP_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_REAR_FUEL_PUMP_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_REAR_FUEL_PUMP_FAILURE"]
        ["COOLANT_T_IND_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "COOLANT_T_IND_FAULT",
            ["mm"] = 0,
        }, -- end of ["COOLANT_T_IND_FAULT"]
        ["GUN_LEFT_OUT_BARREL_WORN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_LEFT_OUT_BARREL_WORN",
            ["mm"] = 0,
        }, -- end of ["GUN_LEFT_OUT_BARREL_WORN"]
        ["CADC_FAILURE_TEMPERATURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CADC_FAILURE_TEMPERATURE",
            ["mm"] = 0,
        }, -- end of ["CADC_FAILURE_TEMPERATURE"]
        ["fs_forward_RH_leakage"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fs_forward_RH_leakage",
            ["mm"] = 0,
        }, -- end of ["fs_forward_RH_leakage"]
        ["engine_surge_failure"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "engine_surge_failure",
            ["mm"] = 0,
        }, -- end of ["engine_surge_failure"]
        ["CADC_FAILURE_PRESSURE_ALT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CADC_FAILURE_PRESSURE_ALT",
            ["mm"] = 0,
        }, -- end of ["CADC_FAILURE_PRESSURE_ALT"]
        ["ELEC_MASTER_COMPASS_HARNESS_CUT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_MASTER_COMPASS_HARNESS_CUT",
            ["mm"] = 0,
        }, -- end of ["ELEC_MASTER_COMPASS_HARNESS_CUT"]
        ["SUPERCHARGER_WIRE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SUPERCHARGER_WIRE",
            ["mm"] = 0,
        }, -- end of ["SUPERCHARGER_WIRE"]
        ["sensf_PITOT_DAMAGE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "sensf_PITOT_DAMAGE",
            ["mm"] = 0,
        }, -- end of ["sensf_PITOT_DAMAGE"]
        ["trim_roll_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "trim_roll_fail",
            ["mm"] = 0,
        }, -- end of ["trim_roll_fail"]
        ["equip_temp_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "equip_temp_fail",
            ["mm"] = 0,
        }, -- end of ["equip_temp_fail"]
        ["FUEL_GAUGE_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_GAUGE_FAULT",
            ["mm"] = 0,
        }, -- end of ["FUEL_GAUGE_FAULT"]
        ["right_rear_leaks"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "right_rear_leaks",
            ["mm"] = 0,
        }, -- end of ["right_rear_leaks"]
        ["DEFECTIVE_INSTRUMENT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "DEFECTIVE_INSTRUMENT",
            ["mm"] = 0,
        }, -- end of ["DEFECTIVE_INSTRUMENT"]
        ["gs_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "gs_fail",
            ["mm"] = 0,
        }, -- end of ["gs_fail"]
        ["RB05ANT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RB05ANT",
            ["mm"] = 0,
        }, -- end of ["RB05ANT"]
        ["FCS_FAILURE_P_SENSOR_2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_P_SENSOR_2",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_P_SENSOR_2"]
        ["ENG0_WATER_RADIATOR_0_MINOR_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_WATER_RADIATOR_0_MINOR_LEAK",
            ["mm"] = 0,
        }, -- end of ["ENG0_WATER_RADIATOR_0_MINOR_LEAK"]
        ["FAILURE_SMS_PYLON_6"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAILURE_SMS_PYLON_6",
            ["mm"] = 0,
        }, -- end of ["FAILURE_SMS_PYLON_6"]
        ["hydr2_pump_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "hydr2_pump_fail",
            ["mm"] = 0,
        }, -- end of ["hydr2_pump_fail"]
        ["EMMC_FAILURE_FUEL_UPPER_PUMP"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_FUEL_UPPER_PUMP",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_FUEL_UPPER_PUMP"]
        ["FCS_FAILURE_LG_4"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_LG_4",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_LG_4"]
        ["EMMC_FAILURE_DC_GENERATOR_CONTROLLER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_DC_GENERATOR_CONTROLLER",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_DC_GENERATOR_CONTROLLER"]
        ["GUN_FAIL_LEFT_OUT_GUN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_FAIL_LEFT_OUT_GUN",
            ["mm"] = 0,
        }, -- end of ["GUN_FAIL_LEFT_OUT_GUN"]
        ["pp_damage_EmergMaxNormFreq"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "pp_damage_EmergMaxNormFreq",
            ["mm"] = 0,
        }, -- end of ["pp_damage_EmergMaxNormFreq"]
        ["esf_RightRectifier"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "esf_RightRectifier",
            ["mm"] = 0,
        }, -- end of ["esf_RightRectifier"]
        ["RWR_FAILURE_QUAD315"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWR_FAILURE_QUAD315",
            ["mm"] = 0,
        }, -- end of ["RWR_FAILURE_QUAD315"]
        ["L_ENG_HPT_OVERSPEED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "L_ENG_HPT_OVERSPEED",
            ["mm"] = 0,
        }, -- end of ["L_ENG_HPT_OVERSPEED"]
        ["GUN_RIGHT_MG131_AMMUN_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_RIGHT_MG131_AMMUN_FAULT",
            ["mm"] = 0,
        }, -- end of ["GUN_RIGHT_MG131_AMMUN_FAULT"]
        ["R_ENG_HPT_OVERSPEED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "R_ENG_HPT_OVERSPEED",
            ["mm"] = 0,
        }, -- end of ["R_ENG_HPT_OVERSPEED"]
        ["ELEC_FORWARD_FUEL_PUMP_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_FORWARD_FUEL_PUMP_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_FORWARD_FUEL_PUMP_FAILURE"]
        ["34"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "34",
            ["mm"] = 0,
        }, -- end of ["34"]
        ["ignition_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ignition_fail",
            ["mm"] = 0,
        }, -- end of ["ignition_fail"]
        ["COMPRESSOR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "COMPRESSOR",
            ["mm"] = 0,
        }, -- end of ["COMPRESSOR"]
        ["VHF_ARC_182_FAILURE_INTERNAL_MODULE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "VHF_ARC_182_FAILURE_INTERNAL_MODULE",
            ["mm"] = 0,
        }, -- end of ["VHF_ARC_182_FAILURE_INTERNAL_MODULE"]
        ["CADC_MANEUVER_FLAP_COMMAND"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CADC_MANEUVER_FLAP_COMMAND",
            ["mm"] = 0,
        }, -- end of ["CADC_MANEUVER_FLAP_COMMAND"]
        ["start_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "start_fail",
            ["mm"] = 0,
        }, -- end of ["start_fail"]
        ["FUEL_MW50_TANK_DRAIN_VALVE_SEVERED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_MW50_TANK_DRAIN_VALVE_SEVERED",
            ["mm"] = 0,
        }, -- end of ["FUEL_MW50_TANK_DRAIN_VALVE_SEVERED"]
        ["TAIL_GEAR_D_LOCK_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TAIL_GEAR_D_LOCK_FAULT",
            ["mm"] = 0,
        }, -- end of ["TAIL_GEAR_D_LOCK_FAULT"]
        ["ELEC_BOOSTER_FUEL_PUMP_0_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_BOOSTER_FUEL_PUMP_0_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_BOOSTER_FUEL_PUMP_0_FAILURE"]
        ["GTS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GTS",
            ["mm"] = 0,
        }, -- end of ["GTS"]
        ["GUN_RIGHT_IN_OPEN_CIRCUIT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_RIGHT_IN_OPEN_CIRCUIT",
            ["mm"] = 0,
        }, -- end of ["GUN_RIGHT_IN_OPEN_CIRCUIT"]
        ["ELEC_BOMBABWGERAT_CHARGING_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_BOMBABWGERAT_CHARGING_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_BOMBABWGERAT_CHARGING_FAILURE"]
        ["RWR_FAILURE_MBE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWR_FAILURE_MBE",
            ["mm"] = 0,
        }, -- end of ["RWR_FAILURE_MBE"]
        ["PILOT_KILLED_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "PILOT_KILLED_FAILURE",
            ["mm"] = 0,
        }, -- end of ["PILOT_KILLED_FAILURE"]
        ["FCS_FAILURE_COMP_4"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_COMP_4",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_COMP_4"]
        ["JAMMER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "JAMMER",
            ["mm"] = 0,
        }, -- end of ["JAMMER"]
        ["ssf_full_pressure_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ssf_full_pressure_fail",
            ["mm"] = 0,
        }, -- end of ["ssf_full_pressure_fail"]
        ["GUN_FAIL_RIGHT_IN_GUN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_FAIL_RIGHT_IN_GUN",
            ["mm"] = 0,
        }, -- end of ["GUN_FAIL_RIGHT_IN_GUN"]
        ["r_engine"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "r_engine",
            ["mm"] = 0,
        }, -- end of ["r_engine"]
        ["TRN_FAIL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TRN_FAIL",
            ["mm"] = 0,
        }, -- end of ["TRN_FAIL"]
        ["RKL_41_ADF_DAMAGE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RKL_41_ADF_DAMAGE",
            ["mm"] = 0,
        }, -- end of ["RKL_41_ADF_DAMAGE"]
        ["FCS_FAILURE_YAW_AUGD_1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_YAW_AUGD_1",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_YAW_AUGD_1"]
        ["15"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "15",
            ["mm"] = 0,
        }, -- end of ["15"]
        ["25"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "25",
            ["mm"] = 0,
        }, -- end of ["25"]
        ["es_damage_Generator"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "es_damage_Generator",
            ["mm"] = 0,
        }, -- end of ["es_damage_Generator"]
        ["ELEC_C5_LAMP_POOR_CONTACT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_C5_LAMP_POOR_CONTACT",
            ["mm"] = 0,
        }, -- end of ["ELEC_C5_LAMP_POOR_CONTACT"]
        ["pilot_aids_2_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "pilot_aids_2_fail",
            ["mm"] = 0,
        }, -- end of ["pilot_aids_2_fail"]
        ["INS_PU_REJECTED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INS_PU_REJECTED",
            ["mm"] = 0,
        }, -- end of ["INS_PU_REJECTED"]
        ["l_engine"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "l_engine",
            ["mm"] = 0,
        }, -- end of ["l_engine"]
        ["OXY_FAILURE_L_LEAK_SEVERE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "OXY_FAILURE_L_LEAK_SEVERE",
            ["mm"] = 0,
        }, -- end of ["OXY_FAILURE_L_LEAK_SEVERE"]
        ["EMMC_FAILURE_FUEL_LOW_LEVEL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_FUEL_LOW_LEVEL",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_FUEL_LOW_LEVEL"]
        ["ELEC_FUEL_PUMP_P1_COIL_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_FUEL_PUMP_P1_COIL_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_FUEL_PUMP_P1_COIL_FAILURE"]
        ["EMMC_FAILURE_DADS_LPTU"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_DADS_LPTU",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_DADS_LPTU"]
        ["COOLANT_SHORT_CIRCUIT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "COOLANT_SHORT_CIRCUIT",
            ["mm"] = 0,
        }, -- end of ["COOLANT_SHORT_CIRCUIT"]
        ["R_ENG_AICS_RAMP_FAIL_CLOSED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "R_ENG_AICS_RAMP_FAIL_CLOSED",
            ["mm"] = 0,
        }, -- end of ["R_ENG_AICS_RAMP_FAIL_CLOSED"]
        ["CADC_FAILURE_TAS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CADC_FAILURE_TAS",
            ["mm"] = 0,
        }, -- end of ["CADC_FAILURE_TAS"]
        ["csf_AutoFlap"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "csf_AutoFlap",
            ["mm"] = 0,
        }, -- end of ["csf_AutoFlap"]
        ["UNCR_C_STRUT_HOSE_PIERCED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "UNCR_C_STRUT_HOSE_PIERCED",
            ["mm"] = 0,
        }, -- end of ["UNCR_C_STRUT_HOSE_PIERCED"]
        ["CMS_FAILURE_RIGHT_DISPENSER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CMS_FAILURE_RIGHT_DISPENSER",
            ["mm"] = 0,
        }, -- end of ["CMS_FAILURE_RIGHT_DISPENSER"]
        ["43"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "43",
            ["mm"] = 0,
        }, -- end of ["43"]
        ["FUEL_TANK_00_MINOR_LEAK"] = 
        {
            ["hh"] = 0,
            ["id"] = "FUEL_TANK_00_MINOR_LEAK",
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["FUEL_TANK_00_MINOR_LEAK"]
        ["TURNIND_POINTER_FAILS_NO_VACUUM"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TURNIND_POINTER_FAILS_NO_VACUUM",
            ["mm"] = 0,
        }, -- end of ["TURNIND_POINTER_FAILS_NO_VACUUM"]
        ["gear_nose_stuck"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "gear_nose_stuck",
            ["mm"] = 0,
        }, -- end of ["gear_nose_stuck"]
        ["PITOT_HEAT_WIRING"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "PITOT_HEAT_WIRING",
            ["mm"] = 0,
        }, -- end of ["PITOT_HEAT_WIRING"]
        ["FCS_FAILURE_YAW_LVDT_1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_YAW_LVDT_1",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_YAW_LVDT_1"]
        ["ELEC_BOOSTER_FUEL_PUMP_1_COIL_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_BOOSTER_FUEL_PUMP_1_COIL_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_BOOSTER_FUEL_PUMP_1_COIL_FAILURE"]
        ["22"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "22",
            ["mm"] = 0,
        }, -- end of ["22"]
        ["HYD_ALT_1_FAIL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "HYD_ALT_1_FAIL",
            ["mm"] = 0,
        }, -- end of ["HYD_ALT_1_FAIL"]
        ["es_damage_MainInverter"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "es_damage_MainInverter",
            ["mm"] = 0,
        }, -- end of ["es_damage_MainInverter"]
        ["AUX_TANK_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AUX_TANK_LEAK",
            ["mm"] = 0,
        }, -- end of ["AUX_TANK_LEAK"]
        ["9"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "9",
            ["mm"] = 0,
        }, -- end of ["9"]
        ["es_damage_RadarInverter"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "es_damage_RadarInverter",
            ["mm"] = 0,
        }, -- end of ["es_damage_RadarInverter"]
        ["ENGINE_FAILURE_COMBUSTOR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENGINE_FAILURE_COMBUSTOR",
            ["mm"] = 0,
        }, -- end of ["ENGINE_FAILURE_COMBUSTOR"]
        ["asc_y"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "asc_y",
            ["mm"] = 0,
        }, -- end of ["asc_y"]
        ["3"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "3",
            ["mm"] = 0,
        }, -- end of ["3"]
        ["MAIN_L_GEAR_D_LOCK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MAIN_L_GEAR_D_LOCK",
            ["mm"] = 0,
        }, -- end of ["MAIN_L_GEAR_D_LOCK"]
        ["FAILURE_EXT_LIGHT_LAND"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAILURE_EXT_LIGHT_LAND",
            ["mm"] = 0,
        }, -- end of ["FAILURE_EXT_LIGHT_LAND"]
        ["adf_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "adf_fail",
            ["mm"] = 0,
        }, -- end of ["adf_fail"]
        ["FUEL_RH_WING_TANK_MINOR_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_RH_WING_TANK_MINOR_LEAK",
            ["mm"] = 0,
        }, -- end of ["FUEL_RH_WING_TANK_MINOR_LEAK"]
        ["LGear_ret_fault"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "LGear_ret_fault",
            ["mm"] = 0,
        }, -- end of ["LGear_ret_fault"]
        ["FUEL_DROPTANK_LINE_SEVERED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_DROPTANK_LINE_SEVERED",
            ["mm"] = 0,
        }, -- end of ["FUEL_DROPTANK_LINE_SEVERED"]
        ["EMMC_FAILURE_LWC"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_LWC",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_LWC"]
        ["WEAP_GUN_05_DAMAGED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "WEAP_GUN_05_DAMAGED",
            ["mm"] = 0,
        }, -- end of ["WEAP_GUN_05_DAMAGED"]
        ["FAILURE_SMS_PYLON_7"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAILURE_SMS_PYLON_7",
            ["mm"] = 0,
        }, -- end of ["FAILURE_SMS_PYLON_7"]
        ["TILS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TILS",
            ["mm"] = 0,
        }, -- end of ["TILS"]
        ["TACAN_FAILURE_RECEIVER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TACAN_FAILURE_RECEIVER",
            ["mm"] = 0,
        }, -- end of ["TACAN_FAILURE_RECEIVER"]
        ["13"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "13",
            ["mm"] = 0,
        }, -- end of ["13"]
        ["es_damage_AltInverter"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "es_damage_AltInverter",
            ["mm"] = 0,
        }, -- end of ["es_damage_AltInverter"]
        ["33"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "33",
            ["mm"] = 0,
        }, -- end of ["33"]
        ["23"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "23",
            ["mm"] = 0,
        }, -- end of ["23"]
        ["42"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "42",
            ["mm"] = 0,
        }, -- end of ["42"]
        ["SUPERCHARGER_SOLENOID"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SUPERCHARGER_SOLENOID",
            ["mm"] = 0,
        }, -- end of ["SUPERCHARGER_SOLENOID"]
        ["fs_damage_transfer_pumps"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fs_damage_transfer_pumps",
            ["mm"] = 0,
        }, -- end of ["fs_damage_transfer_pumps"]
        ["SWMMC_FAILURE_CTVS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SWMMC_FAILURE_CTVS",
            ["mm"] = 0,
        }, -- end of ["SWMMC_FAILURE_CTVS"]
        ["fire_sys_fireAPU"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fire_sys_fireAPU",
            ["mm"] = 0,
        }, -- end of ["fire_sys_fireAPU"]
        ["AC_BUS_PO7502_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AC_BUS_PO7502_FAILURE",
            ["mm"] = 0,
        }, -- end of ["AC_BUS_PO7502_FAILURE"]
        ["EMMC_FAILURE_FUEL_LOWER_PUMP"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_FUEL_LOWER_PUMP",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_FUEL_LOWER_PUMP"]
        ["RWRANTLEFT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWRANTLEFT",
            ["mm"] = 0,
        }, -- end of ["RWRANTLEFT"]
        ["EXT_TANK_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EXT_TANK_LEAK",
            ["mm"] = 0,
        }, -- end of ["EXT_TANK_LEAK"]
        ["fs_damage_swapping_pumps"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fs_damage_swapping_pumps",
            ["mm"] = 0,
        }, -- end of ["fs_damage_swapping_pumps"]
        ["CMS_FAILURE_LEFT_DISPENSER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CMS_FAILURE_LEFT_DISPENSER",
            ["mm"] = 0,
        }, -- end of ["CMS_FAILURE_LEFT_DISPENSER"]
        ["ELEC_SUPERCHARGER_SOLENOID_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_SUPERCHARGER_SOLENOID_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_SUPERCHARGER_SOLENOID_FAILURE"]
        ["hydr_leak"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "hydr_leak",
            ["mm"] = 0,
        }, -- end of ["hydr_leak"]
        ["COOLANT_POOR_CONNTECT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "COOLANT_POOR_CONNTECT",
            ["mm"] = 0,
        }, -- end of ["COOLANT_POOR_CONNTECT"]
        ["ELEC_PITOT_HEAT_WIRING"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_PITOT_HEAT_WIRING",
            ["mm"] = 0,
        }, -- end of ["ELEC_PITOT_HEAT_WIRING"]
        ["flap_right_stuck"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "flap_right_stuck",
            ["mm"] = 0,
        }, -- end of ["flap_right_stuck"]
        ["RWR_FAILURE_DISPLAY_RIO"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWR_FAILURE_DISPLAY_RIO",
            ["mm"] = 0,
        }, -- end of ["RWR_FAILURE_DISPLAY_RIO"]
        ["WEAPONS_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "WEAPONS_FAILURE",
            ["mm"] = 0,
        }, -- end of ["WEAPONS_FAILURE"]
        ["GUN_LEFT_MG151_BARREL_WORN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_LEFT_MG151_BARREL_WORN",
            ["mm"] = 0,
        }, -- end of ["GUN_LEFT_MG151_BARREL_WORN"]
        ["JESTER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "JESTER",
            ["mm"] = 0,
        }, -- end of ["JESTER"]
        ["R_GEAR_UPL_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "R_GEAR_UPL_FAULT",
            ["mm"] = 0,
        }, -- end of ["R_GEAR_UPL_FAULT"]
        ["FCS_FAILURE_YAW_ELEC_SERVO_1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_YAW_ELEC_SERVO_1",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_YAW_ELEC_SERVO_1"]
        ["miss_bus_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "miss_bus_fail",
            ["mm"] = 0,
        }, -- end of ["miss_bus_fail"]
        ["GMC_GYRO_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GMC_GYRO_FAILURE",
            ["mm"] = 0,
        }, -- end of ["GMC_GYRO_FAILURE"]
        ["ELEC_AMBER_SIGNAL_LIGHT_BROKEN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_AMBER_SIGNAL_LIGHT_BROKEN",
            ["mm"] = 0,
        }, -- end of ["ELEC_AMBER_SIGNAL_LIGHT_BROKEN"]
        ["FCS_FAILURE_YAW_AUGD_2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_YAW_AUGD_2",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_YAW_AUGD_2"]
        ["EMMC_FAILURE_AC_GENERATOR_CONTROLLER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_AC_GENERATOR_CONTROLLER",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_AC_GENERATOR_CONTROLLER"]
        ["right_wing_leaks"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "right_wing_leaks",
            ["mm"] = 0,
        }, -- end of ["right_wing_leaks"]
        ["total_comp_stall"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "total_comp_stall",
            ["mm"] = 0,
        }, -- end of ["total_comp_stall"]
        ["12"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "12",
            ["mm"] = 0,
        }, -- end of ["12"]
        ["RIGHT_CYLINDER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RIGHT_CYLINDER",
            ["mm"] = 0,
        }, -- end of ["RIGHT_CYLINDER"]
        ["32"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "32",
            ["mm"] = 0,
        }, -- end of ["32"]
        ["l_gen"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "l_gen",
            ["mm"] = 0,
        }, -- end of ["l_gen"]
        ["L_ENG_TURBINE_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "L_ENG_TURBINE_FAILURE",
            ["mm"] = 0,
        }, -- end of ["L_ENG_TURBINE_FAILURE"]
        ["FCS_FAILURE_NZ_SENSOR_4"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_NZ_SENSOR_4",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_NZ_SENSOR_4"]
        ["SWMMC_FAILURE_CPU"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SWMMC_FAILURE_CPU",
            ["mm"] = 0,
        }, -- end of ["SWMMC_FAILURE_CPU"]
        ["INS_WIND_INVALID"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INS_WIND_INVALID",
            ["mm"] = 0,
        }, -- end of ["INS_WIND_INVALID"]
        ["hs_damage_MainAutoUnload"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "hs_damage_MainAutoUnload",
            ["mm"] = 0,
        }, -- end of ["hs_damage_MainAutoUnload"]
        ["anemo_central_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "anemo_central_fail",
            ["mm"] = 0,
        }, -- end of ["anemo_central_fail"]
        ["R_ENG_TURBINE_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "R_ENG_TURBINE_FAILURE",
            ["mm"] = 0,
        }, -- end of ["R_ENG_TURBINE_FAILURE"]
        ["TACAN_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TACAN_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["TACAN_FAILURE_TOTAL"]
        ["ENG0_JAMMED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_JAMMED",
            ["mm"] = 0,
        }, -- end of ["ENG0_JAMMED"]
        ["TURNIND_POINTER_FAILS_DEFECTIVE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TURNIND_POINTER_FAILS_DEFECTIVE",
            ["mm"] = 0,
        }, -- end of ["TURNIND_POINTER_FAILS_DEFECTIVE"]
        ["FUEL_BOOSTER_FUEL_PUMP_2_DEGRADED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_BOOSTER_FUEL_PUMP_2_DEGRADED",
            ["mm"] = 0,
        }, -- end of ["FUEL_BOOSTER_FUEL_PUMP_2_DEGRADED"]
        ["laser_failure"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "laser_failure",
            ["mm"] = 0,
        }, -- end of ["laser_failure"]
        ["FAILURE_SMS_PYLON_4"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAILURE_SMS_PYLON_4",
            ["mm"] = 0,
        }, -- end of ["FAILURE_SMS_PYLON_4"]
        ["EMERGENCY_GEAR_LEVER_PULLED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMERGENCY_GEAR_LEVER_PULLED",
            ["mm"] = 0,
        }, -- end of ["EMERGENCY_GEAR_LEVER_PULLED"]
        ["ENG0_STARTER_MOTOR_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_STARTER_MOTOR_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ENG0_STARTER_MOTOR_FAILURE"]
        ["FUELTANK4L"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUELTANK4L",
            ["mm"] = 0,
        }, -- end of ["FUELTANK4L"]
        ["ELEC_STARTER_BURNOUT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_STARTER_BURNOUT",
            ["mm"] = 0,
        }, -- end of ["ELEC_STARTER_BURNOUT"]
        ["Failure_Elec_LeftTransformerRectifier"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Elec_LeftTransformerRectifier",
            ["mm"] = 0,
        }, -- end of ["Failure_Elec_LeftTransformerRectifier"]
        ["CTRL_COMPASS_DESTROYED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CTRL_COMPASS_DESTROYED",
            ["mm"] = 0,
        }, -- end of ["CTRL_COMPASS_DESTROYED"]
        ["ENGINE_FAILURE_N1_COMPRESSOR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENGINE_FAILURE_N1_COMPRESSOR",
            ["mm"] = 0,
        }, -- end of ["ENGINE_FAILURE_N1_COMPRESSOR"]
        ["COOLANT_DEFECTIVE_IND"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "COOLANT_DEFECTIVE_IND",
            ["mm"] = 0,
        }, -- end of ["COOLANT_DEFECTIVE_IND"]
        ["GUN_LEFT_OUT_MOUNT_LOOSE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_LEFT_OUT_MOUNT_LOOSE",
            ["mm"] = 0,
        }, -- end of ["GUN_LEFT_OUT_MOUNT_LOOSE"]
        ["SWMMC_DVR_NO_RS422_COMM"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SWMMC_DVR_NO_RS422_COMM",
            ["mm"] = 0,
        }, -- end of ["SWMMC_DVR_NO_RS422_COMM"]
        ["FUEL_BOOSTER_FUEL_PUMP_0_DEGRADED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_BOOSTER_FUEL_PUMP_0_DEGRADED",
            ["mm"] = 0,
        }, -- end of ["FUEL_BOOSTER_FUEL_PUMP_0_DEGRADED"]
        ["LEFT_FLAP_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "LEFT_FLAP_FAULT",
            ["mm"] = 0,
        }, -- end of ["LEFT_FLAP_FAULT"]
        ["PNEM_BRAKE_RELAY_MALFUNCTION"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "PNEM_BRAKE_RELAY_MALFUNCTION",
            ["mm"] = 0,
        }, -- end of ["PNEM_BRAKE_RELAY_MALFUNCTION"]
        ["ENG0_OIL_PUMP_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_OIL_PUMP_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ENG0_OIL_PUMP_FAILURE"]
        ["AGD1_GYRO_TOTAL_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AGD1_GYRO_TOTAL_FAILURE",
            ["mm"] = 0,
        }, -- end of ["AGD1_GYRO_TOTAL_FAILURE"]
        ["es_damage_Inverter115_2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "es_damage_Inverter115_2",
            ["mm"] = 0,
        }, -- end of ["es_damage_Inverter115_2"]
        ["TAIL_GEAR_FAIL_GO_UP"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TAIL_GEAR_FAIL_GO_UP",
            ["mm"] = 0,
        }, -- end of ["TAIL_GEAR_FAIL_GO_UP"]
        ["RWRANTRIGHT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWRANTRIGHT",
            ["mm"] = 0,
        }, -- end of ["RWRANTRIGHT"]
        ["FCS_FAILURE_EFCS_1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_EFCS_1",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_EFCS_1"]
        ["RADAR_ALTIMETR_RIGHT_ANT_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RADAR_ALTIMETR_RIGHT_ANT_FAILURE",
            ["mm"] = 0,
        }, -- end of ["RADAR_ALTIMETR_RIGHT_ANT_FAILURE"]
        ["FCS_FAILURE_P_SENSOR_3"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_P_SENSOR_3",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_P_SENSOR_3"]
        ["RADARALTANT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RADARALTANT",
            ["mm"] = 0,
        }, -- end of ["RADARALTANT"]
        ["Failure_Hyd_HYD1B_Leak"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Hyd_HYD1B_Leak",
            ["mm"] = 0,
        }, -- end of ["Failure_Hyd_HYD1B_Leak"]
        ["FCS_FAILURE_PITCH_LVDT_1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_PITCH_LVDT_1",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_PITCH_LVDT_1"]
        ["DEFECTIVE_MECHANISM"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "DEFECTIVE_MECHANISM",
            ["mm"] = 0,
        }, -- end of ["DEFECTIVE_MECHANISM"]
        ["pp_damage_MainMaxFreq"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "pp_damage_MainMaxFreq",
            ["mm"] = 0,
        }, -- end of ["pp_damage_MainMaxFreq"]
        ["Failure_Comp_MC2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Comp_MC2",
            ["mm"] = 0,
        }, -- end of ["Failure_Comp_MC2"]
        ["FCS_FAILURE_AOA_SENSOR_3"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_AOA_SENSOR_3",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_AOA_SENSOR_3"]
        ["UNCR_RH_STRUT_DOWN_LOCK_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "UNCR_RH_STRUT_DOWN_LOCK_FAILURE",
            ["mm"] = 0,
        }, -- end of ["UNCR_RH_STRUT_DOWN_LOCK_FAILURE"]
        ["GUN_FAIL_LEFT_CENTER_GUN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_FAIL_LEFT_CENTER_GUN",
            ["mm"] = 0,
        }, -- end of ["GUN_FAIL_LEFT_CENTER_GUN"]
        ["FCS_FAILURE_LG_1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_LG_1",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_LG_1"]
        ["PNEM_LH_FLAP_JACK_BUSTED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "PNEM_LH_FLAP_JACK_BUSTED",
            ["mm"] = 0,
        }, -- end of ["PNEM_LH_FLAP_JACK_BUSTED"]
        ["CTRL_LANDING_FLAPS_LH_HOSE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CTRL_LANDING_FLAPS_LH_HOSE",
            ["mm"] = 0,
        }, -- end of ["CTRL_LANDING_FLAPS_LH_HOSE"]
        ["FUEL_TANK_01_FIRE_STOPPED"] = 
        {
            ["hh"] = 0,
            ["id"] = "FUEL_TANK_01_FIRE_STOPPED",
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["FUEL_TANK_01_FIRE_STOPPED"]
        ["UNCR_RH_STRUT_DRIVE_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "UNCR_RH_STRUT_DRIVE_FAILURE",
            ["mm"] = 0,
        }, -- end of ["UNCR_RH_STRUT_DRIVE_FAILURE"]
        ["RPMFault_RightEngine"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "RPMFault_RightEngine",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["RPMFault_RightEngine"]
        ["gyros_general_BSM_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "gyros_general_BSM_fail",
            ["mm"] = 0,
        }, -- end of ["gyros_general_BSM_fail"]
        ["UNCR_LH_STRUT_HOSE_PIERCED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "UNCR_LH_STRUT_HOSE_PIERCED",
            ["mm"] = 0,
        }, -- end of ["UNCR_LH_STRUT_HOSE_PIERCED"]
        ["ELEC_MSB_CB_BUSTED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_MSB_CB_BUSTED",
            ["mm"] = 0,
        }, -- end of ["ELEC_MSB_CB_BUSTED"]
        ["FCS_FAILURE_NY_SENSOR_1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_NY_SENSOR_1",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_NY_SENSOR_1"]
        ["INST_TACH1_LOOM_SEVERED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INST_TACH1_LOOM_SEVERED",
            ["mm"] = 0,
        }, -- end of ["INST_TACH1_LOOM_SEVERED"]
        ["RDR_FAILURE_TRANSMITTER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RDR_FAILURE_TRANSMITTER",
            ["mm"] = 0,
        }, -- end of ["RDR_FAILURE_TRANSMITTER"]
        ["apus"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "apus",
            ["mm"] = 0,
        }, -- end of ["apus"]
        ["CTRL_AILERON_ROD_DESTROYED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CTRL_AILERON_ROD_DESTROYED",
            ["mm"] = 0,
        }, -- end of ["CTRL_AILERON_ROD_DESTROYED"]
        ["Surge_RightEngine"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Surge_RightEngine",
            ["mm"] = 0,
        }, -- end of ["Surge_RightEngine"]
        ["FCS_FAILURE_AOA_SENSOR_2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_AOA_SENSOR_2",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_AOA_SENSOR_2"]
        ["UNCR_LH_STRUT_DRIVE_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "UNCR_LH_STRUT_DRIVE_FAILURE",
            ["mm"] = 0,
        }, -- end of ["UNCR_LH_STRUT_DRIVE_FAILURE"]
        ["ENGINE_FAILURE_N1_TURBINE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENGINE_FAILURE_N1_TURBINE",
            ["mm"] = 0,
        }, -- end of ["ENGINE_FAILURE_N1_TURBINE"]
        ["HYDR_EXTERNAL_LEAKAGE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "HYDR_EXTERNAL_LEAKAGE",
            ["mm"] = 0,
        }, -- end of ["HYDR_EXTERNAL_LEAKAGE"]
        ["hydr1_reserv_leaks"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "hydr1_reserv_leaks",
            ["mm"] = 0,
        }, -- end of ["hydr1_reserv_leaks"]
        ["pp_damage_BladesBrake"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "pp_damage_BladesBrake",
            ["mm"] = 0,
        }, -- end of ["pp_damage_BladesBrake"]
        ["BATTERY"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BATTERY",
            ["mm"] = 0,
        }, -- end of ["BATTERY"]
        ["RWR_FAILURE_RECEIVER_XX3"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWR_FAILURE_RECEIVER_XX3",
            ["mm"] = 0,
        }, -- end of ["RWR_FAILURE_RECEIVER_XX3"]
        ["GUN_LEFT_IN_AMMUN_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_LEFT_IN_AMMUN_FAULT",
            ["mm"] = 0,
        }, -- end of ["GUN_LEFT_IN_AMMUN_FAULT"]
        ["CADC_ANGLE_OF_ATTACK_SIGNAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CADC_ANGLE_OF_ATTACK_SIGNAL",
            ["mm"] = 0,
        }, -- end of ["CADC_ANGLE_OF_ATTACK_SIGNAL"]
        ["37"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "37",
            ["mm"] = 0,
        }, -- end of ["37"]
        ["EMMC_FAILURE_SCU_DC2AC36V"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_SCU_DC2AC36V",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_SCU_DC2AC36V"]
        ["L_ENG_OIL_LEAK_SEVERE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "L_ENG_OIL_LEAK_SEVERE",
            ["mm"] = 0,
        }, -- end of ["L_ENG_OIL_LEAK_SEVERE"]
        ["FCS_FAILURE_PITCH_RATE_GYRO_4"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_PITCH_RATE_GYRO_4",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_PITCH_RATE_GYRO_4"]
        ["FUEL_TANK_01_FIRE"] = 
        {
            ["hh"] = 0,
            ["id"] = "FUEL_TANK_01_FIRE",
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["FUEL_TANK_01_FIRE"]
        ["COM1_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "COM1_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["COM1_FAILURE_TOTAL"]
        ["CTRL_AILERON_TRIM_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CTRL_AILERON_TRIM_FAILURE",
            ["mm"] = 0,
        }, -- end of ["CTRL_AILERON_TRIM_FAILURE"]
        ["MW_50_VALVE_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MW_50_VALVE_FAULT",
            ["mm"] = 0,
        }, -- end of ["MW_50_VALVE_FAULT"]
        ["OXY_FAILURE_L_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "OXY_FAILURE_L_LEAK",
            ["mm"] = 0,
        }, -- end of ["OXY_FAILURE_L_LEAK"]
        ["broken_guards"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "broken_guards",
            ["mm"] = 0,
        }, -- end of ["broken_guards"]
        ["UNCR_RH_STRUT_UP_LOCK_JAMMED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "UNCR_RH_STRUT_UP_LOCK_JAMMED",
            ["mm"] = 0,
        }, -- end of ["UNCR_RH_STRUT_UP_LOCK_JAMMED"]
        ["Failure_RightEngine"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_RightEngine",
            ["mm"] = 0,
        }, -- end of ["Failure_RightEngine"]
        ["BATT_FAIL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BATT_FAIL",
            ["mm"] = 0,
        }, -- end of ["BATT_FAIL"]
        ["HYDR_INTERNAL_LEAKAGE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "HYDR_INTERNAL_LEAKAGE",
            ["mm"] = 0,
        }, -- end of ["HYDR_INTERNAL_LEAKAGE"]
        ["VHF_SHORTED_CTL_BOX"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "VHF_SHORTED_CTL_BOX",
            ["mm"] = 0,
        }, -- end of ["VHF_SHORTED_CTL_BOX"]
        ["CLOCK_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CLOCK_FAILURE",
            ["mm"] = 0,
        }, -- end of ["CLOCK_FAILURE"]
        ["FCS_FAILURE_AOA_SENSOR_1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_AOA_SENSOR_1",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_AOA_SENSOR_1"]
        ["OIL_RADIATOR_WIRING"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "OIL_RADIATOR_WIRING",
            ["mm"] = 0,
        }, -- end of ["OIL_RADIATOR_WIRING"]
        ["27"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "27",
            ["mm"] = 0,
        }, -- end of ["27"]
        ["CTRL_LANDING_FLAPS_LH_DRIVE_DAMAGED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CTRL_LANDING_FLAPS_LH_DRIVE_DAMAGED",
            ["mm"] = 0,
        }, -- end of ["CTRL_LANDING_FLAPS_LH_DRIVE_DAMAGED"]
        ["UNCR_UP_HOSES_CLOGGED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "UNCR_UP_HOSES_CLOGGED",
            ["mm"] = 0,
        }, -- end of ["UNCR_UP_HOSES_CLOGGED"]
        ["MWMMC_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MWMMC_FAILURE",
            ["mm"] = 0,
        }, -- end of ["MWMMC_FAILURE"]
        ["17"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "17",
            ["mm"] = 0,
        }, -- end of ["17"]
        ["IMU_FAILURE_QUANTIZER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "IMU_FAILURE_QUANTIZER",
            ["mm"] = 0,
        }, -- end of ["IMU_FAILURE_QUANTIZER"]
        ["CTRL_LANDING_FLAPS_RH_DRIVE_DAMAGED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CTRL_LANDING_FLAPS_RH_DRIVE_DAMAGED",
            ["mm"] = 0,
        }, -- end of ["CTRL_LANDING_FLAPS_RH_DRIVE_DAMAGED"]
        ["ELEC_ROCKETS_CIRCUITRY_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_ROCKETS_CIRCUITRY_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_ROCKETS_CIRCUITRY_FAILURE"]
        ["engine_flameout"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "engine_flameout",
            ["mm"] = 0,
        }, -- end of ["engine_flameout"]
        ["SWMMC_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SWMMC_FAILURE",
            ["mm"] = 0,
        }, -- end of ["SWMMC_FAILURE"]
        ["VHF_VT_BURNED_OUT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "VHF_VT_BURNED_OUT",
            ["mm"] = 0,
        }, -- end of ["VHF_VT_BURNED_OUT"]
        ["COPILOT_KILLED_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "COPILOT_KILLED_FAILURE",
            ["mm"] = 0,
        }, -- end of ["COPILOT_KILLED_FAILURE"]
        ["10"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "10",
            ["mm"] = 0,
        }, -- end of ["10"]
        ["36"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "36",
            ["mm"] = 0,
        }, -- end of ["36"]
        ["RADARDISPL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RADARDISPL",
            ["mm"] = 0,
        }, -- end of ["RADARDISPL"]
        ["GUN_LEFT_CENTER_BARREL_WORN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_LEFT_CENTER_BARREL_WORN",
            ["mm"] = 0,
        }, -- end of ["GUN_LEFT_CENTER_BARREL_WORN"]
        ["pitch_chain_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "pitch_chain_fail",
            ["mm"] = 0,
        }, -- end of ["pitch_chain_fail"]
        ["STARTER_SOLENOID"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "STARTER_SOLENOID",
            ["mm"] = 0,
        }, -- end of ["STARTER_SOLENOID"]
        ["ELEC_DROPTANK_FUEL_PUMP_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_DROPTANK_FUEL_PUMP_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_DROPTANK_FUEL_PUMP_FAILURE"]
        ["FUEL_BOOSTER_FUEL_PUMP_1_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_BOOSTER_FUEL_PUMP_1_FAILURE",
            ["mm"] = 0,
        }, -- end of ["FUEL_BOOSTER_FUEL_PUMP_1_FAILURE"]
        ["REAR_TANK_PUMP_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "REAR_TANK_PUMP_FAULT",
            ["mm"] = 0,
        }, -- end of ["REAR_TANK_PUMP_FAULT"]
        ["PUMP_FAILS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "PUMP_FAILS",
            ["mm"] = 0,
        }, -- end of ["PUMP_FAILS"]
        ["AGK_47B_GYRO_TOTAL_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AGK_47B_GYRO_TOTAL_FAILURE",
            ["mm"] = 0,
        }, -- end of ["AGK_47B_GYRO_TOTAL_FAILURE"]
        ["FUEL_MAIN_TANK_MAJOR_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_MAIN_TANK_MAJOR_LEAK",
            ["mm"] = 0,
        }, -- end of ["FUEL_MAIN_TANK_MAJOR_LEAK"]
        ["STATION_4_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "STATION_4_FAILURE",
            ["mm"] = 0,
        }, -- end of ["STATION_4_FAILURE"]
        ["FAILURE_EXT_LIGHT_NAV_TAIL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAILURE_EXT_LIGHT_NAV_TAIL",
            ["mm"] = 0,
        }, -- end of ["FAILURE_EXT_LIGHT_NAV_TAIL"]
        ["OESP_FAILURE_FL_DISP_R"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "OESP_FAILURE_FL_DISP_R",
            ["mm"] = 0,
        }, -- end of ["OESP_FAILURE_FL_DISP_R"]
        ["GUN_RIGHT_CENTER_AMMUN_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_RIGHT_CENTER_AMMUN_FAULT",
            ["mm"] = 0,
        }, -- end of ["GUN_RIGHT_CENTER_AMMUN_FAULT"]
        ["MWMMC_FAILURE_1553B_RDR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MWMMC_FAILURE_1553B_RDR",
            ["mm"] = 0,
        }, -- end of ["MWMMC_FAILURE_1553B_RDR"]
        ["COPILOT_GYRO_TOTAL_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "COPILOT_GYRO_TOTAL_FAILURE",
            ["mm"] = 0,
        }, -- end of ["COPILOT_GYRO_TOTAL_FAILURE"]
        ["Failure_PP_RightAMAD_OilLeak"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_PP_RightAMAD_OilLeak",
            ["mm"] = 0,
        }, -- end of ["Failure_PP_RightAMAD_OilLeak"]
        ["MWMMC_FAILURE_1553B_SAIU"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MWMMC_FAILURE_1553B_SAIU",
            ["mm"] = 0,
        }, -- end of ["MWMMC_FAILURE_1553B_SAIU"]
        ["DATACARTRIDGE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "DATACARTRIDGE",
            ["mm"] = 0,
        }, -- end of ["DATACARTRIDGE"]
        ["ENG0_MAGNETO1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_MAGNETO1",
            ["mm"] = 0,
        }, -- end of ["ENG0_MAGNETO1"]
        ["FUEL_TANK_04_MINOR_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_TANK_04_MINOR_LEAK",
            ["mm"] = 0,
        }, -- end of ["FUEL_TANK_04_MINOR_LEAK"]
        ["INSUF_FUEL_PRES"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INSUF_FUEL_PRES",
            ["mm"] = 0,
        }, -- end of ["INSUF_FUEL_PRES"]
        ["26"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "26",
            ["mm"] = 0,
        }, -- end of ["26"]
        ["PNEM_COMPRESSOR_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "PNEM_COMPRESSOR_FAILURE",
            ["mm"] = 0,
        }, -- end of ["PNEM_COMPRESSOR_FAILURE"]
        ["PROP_GOVERNOR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "PROP_GOVERNOR",
            ["mm"] = 0,
        }, -- end of ["PROP_GOVERNOR"]
        ["MANIFOLD_SHIFT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MANIFOLD_SHIFT",
            ["mm"] = 0,
        }, -- end of ["MANIFOLD_SHIFT"]
        ["R_ENG_OIL_LEAK_SLOW"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "R_ENG_OIL_LEAK_SLOW",
            ["mm"] = 0,
        }, -- end of ["R_ENG_OIL_LEAK_SLOW"]
        ["Failure_Fuel_Tank1Transfer"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Fuel_Tank1Transfer",
            ["mm"] = 0,
        }, -- end of ["Failure_Fuel_Tank1Transfer"]
        ["ILS_FAILURE_DECODER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ILS_FAILURE_DECODER",
            ["mm"] = 0,
        }, -- end of ["ILS_FAILURE_DECODER"]
        ["RIGHT_GUNNER_KILLED_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RIGHT_GUNNER_KILLED_FAILURE",
            ["mm"] = 0,
        }, -- end of ["RIGHT_GUNNER_KILLED_FAILURE"]
        ["41"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "41",
            ["mm"] = 0,
        }, -- end of ["41"]
        ["GUN_RIGHT_CENTER_MOUNT_LOOSE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_RIGHT_CENTER_MOUNT_LOOSE",
            ["mm"] = 0,
        }, -- end of ["GUN_RIGHT_CENTER_MOUNT_LOOSE"]
        ["GUN_RIGHT_MG151_AMMUN_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_RIGHT_MG151_AMMUN_FAULT",
            ["mm"] = 0,
        }, -- end of ["GUN_RIGHT_MG151_AMMUN_FAULT"]
        ["FCS_FAILURE_ROLL_LVDT_2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_ROLL_LVDT_2",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_ROLL_LVDT_2"]
        ["fs_damage_EnginePump"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fs_damage_EnginePump",
            ["mm"] = 0,
        }, -- end of ["fs_damage_EnginePump"]
        ["gyros_main_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "gyros_main_fail",
            ["mm"] = 0,
        }, -- end of ["gyros_main_fail"]
        ["RWR_FAILURE_BLANKER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWR_FAILURE_BLANKER",
            ["mm"] = 0,
        }, -- end of ["RWR_FAILURE_BLANKER"]
        ["FAILURE_EXT_LIGHT_FORMATION_RIGHT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAILURE_EXT_LIGHT_FORMATION_RIGHT",
            ["mm"] = 0,
        }, -- end of ["FAILURE_EXT_LIGHT_FORMATION_RIGHT"]
        ["HYDR_PUMP_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "HYDR_PUMP_FAILURE",
            ["mm"] = 0,
        }, -- end of ["HYDR_PUMP_FAILURE"]
        ["EMMC_FAILURE_BATTERY_DC2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_BATTERY_DC2",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_BATTERY_DC2"]
        ["CADC_RUDDER_AUTHORITY_COMMAND"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CADC_RUDDER_AUTHORITY_COMMAND",
            ["mm"] = 0,
        }, -- end of ["CADC_RUDDER_AUTHORITY_COMMAND"]
        ["front_central_transfer_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "front_central_transfer_fail",
            ["mm"] = 0,
        }, -- end of ["front_central_transfer_fail"]
        ["ENG0_MAGNETO0"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_MAGNETO0",
            ["mm"] = 0,
        }, -- end of ["ENG0_MAGNETO0"]
        ["OXY_FAILURE_AIR_O2_SWITCH"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "OXY_FAILURE_AIR_O2_SWITCH",
            ["mm"] = 0,
        }, -- end of ["OXY_FAILURE_AIR_O2_SWITCH"]
        ["OESP_FAILURE_CH_DISP_R"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "OESP_FAILURE_CH_DISP_R",
            ["mm"] = 0,
        }, -- end of ["OESP_FAILURE_CH_DISP_R"]
        ["Failure_PP_EngR_Nozzle_CS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_PP_EngR_Nozzle_CS",
            ["mm"] = 0,
        }, -- end of ["Failure_PP_EngR_Nozzle_CS"]
        ["esf_RightGenerator"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "esf_RightGenerator",
            ["mm"] = 0,
        }, -- end of ["esf_RightGenerator"]
        ["HYDR_UNLOAD_VALVE_NOT_LOAD"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "HYDR_UNLOAD_VALVE_NOT_LOAD",
            ["mm"] = 0,
        }, -- end of ["HYDR_UNLOAD_VALVE_NOT_LOAD"]
        ["STATION_1_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "STATION_1_FAILURE",
            ["mm"] = 0,
        }, -- end of ["STATION_1_FAILURE"]
        ["left_rear_transfer_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "left_rear_transfer_fail",
            ["mm"] = 0,
        }, -- end of ["left_rear_transfer_fail"]
        ["EZ42_MOTOR_DEFECTIVE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EZ42_MOTOR_DEFECTIVE",
            ["mm"] = 0,
        }, -- end of ["EZ42_MOTOR_DEFECTIVE"]
        ["fuel_sys_transfer_pumps"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fuel_sys_transfer_pumps",
            ["mm"] = 0,
        }, -- end of ["fuel_sys_transfer_pumps"]
        ["CADC_TOTAL_TEMPERATURE_SIGNAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CADC_TOTAL_TEMPERATURE_SIGNAL",
            ["mm"] = 0,
        }, -- end of ["CADC_TOTAL_TEMPERATURE_SIGNAL"]
        ["RWR_FAILURE_SENSOR_TAIL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWR_FAILURE_SENSOR_TAIL",
            ["mm"] = 0,
        }, -- end of ["RWR_FAILURE_SENSOR_TAIL"]
        ["nosecone_stuck"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "nosecone_stuck",
            ["mm"] = 0,
        }, -- end of ["nosecone_stuck"]
        ["31"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "31",
            ["mm"] = 0,
        }, -- end of ["31"]
        ["21"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "21",
            ["mm"] = 0,
        }, -- end of ["21"]
        ["11"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "11",
            ["mm"] = 0,
        }, -- end of ["11"]
        ["FAILURE_EXT_LIGHT_NAV_LEFT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAILURE_EXT_LIGHT_NAV_LEFT",
            ["mm"] = 0,
        }, -- end of ["FAILURE_EXT_LIGHT_NAV_LEFT"]
        ["HYDR2PUMP"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "HYDR2PUMP",
            ["mm"] = 0,
        }, -- end of ["HYDR2PUMP"]
        ["es_damage_Starter"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "es_damage_Starter",
            ["mm"] = 0,
        }, -- end of ["es_damage_Starter"]
        ["40"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "40",
            ["mm"] = 0,
        }, -- end of ["40"]
        ["PITOT_HEAT_ELEMENT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "PITOT_HEAT_ELEMENT",
            ["mm"] = 0,
        }, -- end of ["PITOT_HEAT_ELEMENT"]
        ["ELEC_NAVLIGHT_GREEN_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_NAVLIGHT_GREEN_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_NAVLIGHT_GREEN_FAILURE"]
        ["ENGINE_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENGINE_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ENGINE_FAILURE"]
        ["MWMMC_FAILURE_MBI"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MWMMC_FAILURE_MBI",
            ["mm"] = 0,
        }, -- end of ["MWMMC_FAILURE_MBI"]
        ["SWMMC_FAILURE_MBI"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SWMMC_FAILURE_MBI",
            ["mm"] = 0,
        }, -- end of ["SWMMC_FAILURE_MBI"]
        ["esf_StaticInverter"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "esf_StaticInverter",
            ["mm"] = 0,
        }, -- end of ["esf_StaticInverter"]
        ["RUDDER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RUDDER",
            ["mm"] = 0,
        }, -- end of ["RUDDER"]
        ["ENG0_TURBINE_OIL_TANK_PIERCED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_TURBINE_OIL_TANK_PIERCED",
            ["mm"] = 0,
        }, -- end of ["ENG0_TURBINE_OIL_TANK_PIERCED"]
        ["slat_outer_right_stuck"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "slat_outer_right_stuck",
            ["mm"] = 0,
        }, -- end of ["slat_outer_right_stuck"]
        ["PUMP_RELIEF_VALVE_SCREEN_CLOGGED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "PUMP_RELIEF_VALVE_SCREEN_CLOGGED",
            ["mm"] = 0,
        }, -- end of ["PUMP_RELIEF_VALVE_SCREEN_CLOGGED"]
        ["es_damage_GeneratorRight"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "es_damage_GeneratorRight",
            ["mm"] = 0,
        }, -- end of ["es_damage_GeneratorRight"]
        ["LEFT_WING_TANK_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "LEFT_WING_TANK_LEAK",
            ["mm"] = 0,
        }, -- end of ["LEFT_WING_TANK_LEAK"]
        ["AFN2_DAMAGE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AFN2_DAMAGE",
            ["mm"] = 0,
        }, -- end of ["AFN2_DAMAGE"]
        ["FUEL_TANK_03_EXPLODED"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "FUEL_TANK_03_EXPLODED",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["FUEL_TANK_03_EXPLODED"]
        ["MAINGENERATOR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MAINGENERATOR",
            ["mm"] = 0,
        }, -- end of ["MAINGENERATOR"]
        ["MainReductor_LowOilPressure"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MainReductor_LowOilPressure",
            ["mm"] = 0,
        }, -- end of ["MainReductor_LowOilPressure"]
        ["16"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "16",
            ["mm"] = 0,
        }, -- end of ["16"]
        ["overspeed_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "overspeed_fail",
            ["mm"] = 0,
        }, -- end of ["overspeed_fail"]
        ["EGI_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EGI_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["EGI_FAILURE_TOTAL"]
        ["SAR_1_101"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SAR_1_101",
            ["mm"] = 0,
        }, -- end of ["SAR_1_101"]
        ["ADC_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ADC_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["ADC_FAILURE_TOTAL"]
        ["SWMMC_FAILURE_AAP"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SWMMC_FAILURE_AAP",
            ["mm"] = 0,
        }, -- end of ["SWMMC_FAILURE_AAP"]
        ["RDR_FAILURE_TRANSMITTER_OVERHEAT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RDR_FAILURE_TRANSMITTER_OVERHEAT",
            ["mm"] = 0,
        }, -- end of ["RDR_FAILURE_TRANSMITTER_OVERHEAT"]
        ["GUN_LEFT_OUT_AMMUN_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_LEFT_OUT_AMMUN_FAULT",
            ["mm"] = 0,
        }, -- end of ["GUN_LEFT_OUT_AMMUN_FAULT"]
        ["AHRS_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AHRS_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["AHRS_FAILURE_TOTAL"]
        ["20"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "20",
            ["mm"] = 0,
        }, -- end of ["20"]
        ["30"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "30",
            ["mm"] = 0,
        }, -- end of ["30"]
        ["L_ENG_OIL_LEAK_SLOW"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "L_ENG_OIL_LEAK_SLOW",
            ["mm"] = 0,
        }, -- end of ["L_ENG_OIL_LEAK_SLOW"]
        ["VHF_VT207_DEFECTIVE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "VHF_VT207_DEFECTIVE",
            ["mm"] = 0,
        }, -- end of ["VHF_VT207_DEFECTIVE"]
        ["ELEC_RETICLE_BULB_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_RETICLE_BULB_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_RETICLE_BULB_FAILURE"]
        ["R_ENG_OIL_LEAK_SEVERE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "R_ENG_OIL_LEAK_SEVERE",
            ["mm"] = 0,
        }, -- end of ["R_ENG_OIL_LEAK_SEVERE"]
        ["EMMC_FAILURE_PROBES_HEATING"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_PROBES_HEATING",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_PROBES_HEATING"]
        ["es_damage_Inverter36x3"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "es_damage_Inverter36x3",
            ["mm"] = 0,
        }, -- end of ["es_damage_Inverter36x3"]
        ["CNI_FAILURE_COM1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CNI_FAILURE_COM1",
            ["mm"] = 0,
        }, -- end of ["CNI_FAILURE_COM1"]
        ["FCS_FAILURE_Q_SENSOR_1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_Q_SENSOR_1",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_Q_SENSOR_1"]
        ["RightEngine_LowOilPressure"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RightEngine_LowOilPressure",
            ["mm"] = 0,
        }, -- end of ["RightEngine_LowOilPressure"]
        ["SWMMC_FAILURE_CSU"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SWMMC_FAILURE_CSU",
            ["mm"] = 0,
        }, -- end of ["SWMMC_FAILURE_CSU"]
        ["radar"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "radar",
            ["mm"] = 0,
        }, -- end of ["radar"]
        ["ELEC_MSB_DAMAGED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_MSB_DAMAGED",
            ["mm"] = 0,
        }, -- end of ["ELEC_MSB_DAMAGED"]
        ["EMMC_FAILURE_DC_GENERATOR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_DC_GENERATOR",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_DC_GENERATOR"]
        ["Failure_Ctrl_FCS_Ch3"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Ctrl_FCS_Ch3",
            ["mm"] = 0,
        }, -- end of ["Failure_Ctrl_FCS_Ch3"]
        ["es_damage_VU3"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "es_damage_VU3",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["es_damage_VU3"]
        ["COOLANT_UNPRES"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "COOLANT_UNPRES",
            ["mm"] = 0,
        }, -- end of ["COOLANT_UNPRES"]
        ["ARN_82_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ARN_82_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["ARN_82_FAILURE_TOTAL"]
        ["VHF_ARC_182_FAILURE_TRANSCEIVER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "VHF_ARC_182_FAILURE_TRANSCEIVER",
            ["mm"] = 0,
        }, -- end of ["VHF_ARC_182_FAILURE_TRANSCEIVER"]
        ["fs_damage_engine_pump"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fs_damage_engine_pump",
            ["mm"] = 0,
        }, -- end of ["fs_damage_engine_pump"]
        ["TURNIND_INCORRECT_SENS_DEFECTIVE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TURNIND_INCORRECT_SENS_DEFECTIVE",
            ["mm"] = 0,
        }, -- end of ["TURNIND_INCORRECT_SENS_DEFECTIVE"]
        ["eos"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "eos",
            ["mm"] = 0,
        }, -- end of ["eos"]
        ["ELEC_ROCKETS_STEP_MOTOR_MALFUNCTION"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_ROCKETS_STEP_MOTOR_MALFUNCTION",
            ["mm"] = 0,
        }, -- end of ["ELEC_ROCKETS_STEP_MOTOR_MALFUNCTION"]
        ["CADC_STABILIZER_AUTHORITY_COMMAND"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CADC_STABILIZER_AUTHORITY_COMMAND",
            ["mm"] = 0,
        }, -- end of ["CADC_STABILIZER_AUTHORITY_COMMAND"]
        ["FCS_FAILURE_L_ELEVATOR_ELEC_A"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_L_ELEVATOR_ELEC_A",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_L_ELEVATOR_ELEC_A"]
        ["UHF_ARC_159_FAILURE_ANTENNA"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "UHF_ARC_159_FAILURE_ANTENNA",
            ["mm"] = 0,
        }, -- end of ["UHF_ARC_159_FAILURE_ANTENNA"]
        ["FUEL_TANK_03_MINOR_LEAK"] = 
        {
            ["hh"] = 0,
            ["id"] = "FUEL_TANK_03_MINOR_LEAK",
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["FUEL_TANK_03_MINOR_LEAK"]
        ["SWMMC_FAILURE_DVR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SWMMC_FAILURE_DVR",
            ["mm"] = 0,
        }, -- end of ["SWMMC_FAILURE_DVR"]
        ["hs_damage_GainAccumulator"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "hs_damage_GainAccumulator",
            ["mm"] = 0,
        }, -- end of ["hs_damage_GainAccumulator"]
        ["GUN_LEFT_IN_MOUNT_LOOSE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_LEFT_IN_MOUNT_LOOSE",
            ["mm"] = 0,
        }, -- end of ["GUN_LEFT_IN_MOUNT_LOOSE"]
        ["PITOT_HEAT_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "PITOT_HEAT_FAULT",
            ["mm"] = 0,
        }, -- end of ["PITOT_HEAT_FAULT"]
        ["GUN_FAIL_RIGHT_OUT_GUN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_FAIL_RIGHT_OUT_GUN",
            ["mm"] = 0,
        }, -- end of ["GUN_FAIL_RIGHT_OUT_GUN"]
        ["HYD_Flight"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "HYD_Flight",
            ["mm"] = 0,
        }, -- end of ["HYD_Flight"]
        ["FR22ANTENNA"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FR22ANTENNA",
            ["mm"] = 0,
        }, -- end of ["FR22ANTENNA"]
        ["engine_fire"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "engine_fire",
            ["mm"] = 0,
        }, -- end of ["engine_fire"]
        ["ppf_LeftNozzleControl"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ppf_LeftNozzleControl",
            ["mm"] = 0,
        }, -- end of ["ppf_LeftNozzleControl"]
        ["BAT_SOLENOID_DEFECTIVE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BAT_SOLENOID_DEFECTIVE",
            ["mm"] = 0,
        }, -- end of ["BAT_SOLENOID_DEFECTIVE"]
        ["FAULTY_ROCKET_LEFT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAULTY_ROCKET_LEFT",
            ["mm"] = 0,
        }, -- end of ["FAULTY_ROCKET_LEFT"]
        ["FUEL_TANK_03_LEAK_STOPPED"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "FUEL_TANK_03_LEAK_STOPPED",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["FUEL_TANK_03_LEAK_STOPPED"]
        ["BOMBS_DAMAGE_ELINKAGE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BOMBS_DAMAGE_ELINKAGE",
            ["mm"] = 0,
        }, -- end of ["BOMBS_DAMAGE_ELINKAGE"]
        ["LeftEngine_Fire"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "LeftEngine_Fire",
            ["mm"] = 0,
        }, -- end of ["LeftEngine_Fire"]
        ["ELEC_STARTER_SOLENOID_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_STARTER_SOLENOID_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_STARTER_SOLENOID_FAILURE"]
        ["left_front_leaks"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "left_front_leaks",
            ["mm"] = 0,
        }, -- end of ["left_front_leaks"]
        ["L_GEAR_UPL_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "L_GEAR_UPL_FAULT",
            ["mm"] = 0,
        }, -- end of ["L_GEAR_UPL_FAULT"]
        ["EZ42_FIXED_LAMP_DEFECTIVE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EZ42_FIXED_LAMP_DEFECTIVE",
            ["mm"] = 0,
        }, -- end of ["EZ42_FIXED_LAMP_DEFECTIVE"]
        ["ENG0_WATER_RADIATOR_0_MAJOR_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_WATER_RADIATOR_0_MAJOR_LEAK",
            ["mm"] = 0,
        }, -- end of ["ENG0_WATER_RADIATOR_0_MAJOR_LEAK"]
        ["VHF_ARC_182_FAILURE_ANTENNA"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "VHF_ARC_182_FAILURE_ANTENNA",
            ["mm"] = 0,
        }, -- end of ["VHF_ARC_182_FAILURE_ANTENNA"]
        ["FCS_FAILURE_NY_SENSOR_2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_NY_SENSOR_2",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_NY_SENSOR_2"]
        ["HORIZON_BAR_NOT_SETTLE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "HORIZON_BAR_NOT_SETTLE",
            ["mm"] = 0,
        }, -- end of ["HORIZON_BAR_NOT_SETTLE"]
        ["FR24RADIO"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FR24RADIO",
            ["mm"] = 0,
        }, -- end of ["FR24RADIO"]
        ["BURNER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BURNER",
            ["mm"] = 0,
        }, -- end of ["BURNER"]
        ["antiskid_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "antiskid_fail",
            ["mm"] = 0,
        }, -- end of ["antiskid_fail"]
        ["TailReductor_ShaveInOil"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TailReductor_ShaveInOil",
            ["mm"] = 0,
        }, -- end of ["TailReductor_ShaveInOil"]
        ["INST_DI_MECHANICAL_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INST_DI_MECHANICAL_FAILURE",
            ["mm"] = 0,
        }, -- end of ["INST_DI_MECHANICAL_FAILURE"]
        ["ENGINE_FAILURE_DEEC"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENGINE_FAILURE_DEEC",
            ["mm"] = 0,
        }, -- end of ["ENGINE_FAILURE_DEEC"]
        ["FCS_FAILURE_P_SENSOR_4"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_P_SENSOR_4",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_P_SENSOR_4"]
        ["vor_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "vor_fail",
            ["mm"] = 0,
        }, -- end of ["vor_fail"]
        ["RWR_FAILURE_DB_NOT_LOADED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWR_FAILURE_DB_NOT_LOADED",
            ["mm"] = 0,
        }, -- end of ["RWR_FAILURE_DB_NOT_LOADED"]
        ["BOMBS_TRAIN_DEFECTIVE_WIRING"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BOMBS_TRAIN_DEFECTIVE_WIRING",
            ["mm"] = 0,
        }, -- end of ["BOMBS_TRAIN_DEFECTIVE_WIRING"]
        ["Failure_Fuel_ExtTankTransferL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Fuel_ExtTankTransferL",
            ["mm"] = 0,
        }, -- end of ["Failure_Fuel_ExtTankTransferL"]
        ["FUEL_NITRO_TANK_00_MAJOR_LEAK"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "FUEL_NITRO_TANK_00_MAJOR_LEAK",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["FUEL_NITRO_TANK_00_MAJOR_LEAK"]
        ["ENG0_WATER_RADIATOR_0_MEDIUM_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_WATER_RADIATOR_0_MEDIUM_LEAK",
            ["mm"] = 0,
        }, -- end of ["ENG0_WATER_RADIATOR_0_MEDIUM_LEAK"]
        ["CARBAIR_SHORT_CIRCUIT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CARBAIR_SHORT_CIRCUIT",
            ["mm"] = 0,
        }, -- end of ["CARBAIR_SHORT_CIRCUIT"]
        ["STARTER_RELAY"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "STARTER_RELAY",
            ["mm"] = 0,
        }, -- end of ["STARTER_RELAY"]
        ["FAILURE_SMS_PYLON_3"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAILURE_SMS_PYLON_3",
            ["mm"] = 0,
        }, -- end of ["FAILURE_SMS_PYLON_3"]
        ["CTRL_LANDING_FLAPS_RH_MECHANICAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CTRL_LANDING_FLAPS_RH_MECHANICAL",
            ["mm"] = 0,
        }, -- end of ["CTRL_LANDING_FLAPS_RH_MECHANICAL"]
        ["INS_FAILURE_ALGNMENT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INS_FAILURE_ALGNMENT",
            ["mm"] = 0,
        }, -- end of ["INS_FAILURE_ALGNMENT"]
        ["COM2_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "COM2_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["COM2_FAILURE_TOTAL"]
        ["INS_FAILURE_GYRO"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INS_FAILURE_GYRO",
            ["mm"] = 0,
        }, -- end of ["INS_FAILURE_GYRO"]
        ["Failure_Sens_RightPitotHeater"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Sens_RightPitotHeater",
            ["mm"] = 0,
        }, -- end of ["Failure_Sens_RightPitotHeater"]
        ["GUN_RIGHT_IN_MOUNT_LOOSE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_RIGHT_IN_MOUNT_LOOSE",
            ["mm"] = 0,
        }, -- end of ["GUN_RIGHT_IN_MOUNT_LOOSE"]
        ["HAW"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "HAW",
            ["mm"] = 0,
        }, -- end of ["HAW"]
        ["ENG0_TURBINE_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_TURBINE_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ENG0_TURBINE_FAILURE"]
        ["45"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "45",
            ["mm"] = 0,
        }, -- end of ["45"]
        ["35"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "35",
            ["mm"] = 0,
        }, -- end of ["35"]
        ["LEFT_MFCD_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "LEFT_MFCD_FAILURE",
            ["mm"] = 0,
        }, -- end of ["LEFT_MFCD_FAILURE"]
        ["8"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "8",
            ["mm"] = 0,
        }, -- end of ["8"]
        ["ELEC_STARTER_RELAY_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_STARTER_RELAY_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_STARTER_RELAY_FAILURE"]
        ["CNI_FAILURE_COM2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CNI_FAILURE_COM2",
            ["mm"] = 0,
        }, -- end of ["CNI_FAILURE_COM2"]
        ["RWR_FAILURE_SENSOR_TAIL_F"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWR_FAILURE_SENSOR_TAIL_F",
            ["mm"] = 0,
        }, -- end of ["RWR_FAILURE_SENSOR_TAIL_F"]
        ["DOORS_TVC_BROKEN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "DOORS_TVC_BROKEN",
            ["mm"] = 0,
        }, -- end of ["DOORS_TVC_BROKEN"]
        ["ELEC_STARTER_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_STARTER_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_STARTER_FAILURE"]
        ["HYDRRESERVPUMP"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "HYDRRESERVPUMP",
            ["mm"] = 0,
        }, -- end of ["HYDRRESERVPUMP"]
        ["fs_damage_left_cell_boost_pump"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fs_damage_left_cell_boost_pump",
            ["mm"] = 0,
        }, -- end of ["fs_damage_left_cell_boost_pump"]
        ["RDR_FAILURE_RX_FRONT_END"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RDR_FAILURE_RX_FRONT_END",
            ["mm"] = 0,
        }, -- end of ["RDR_FAILURE_RX_FRONT_END"]
        ["BOMBS_DAMAGE_ELINKAGE_LEFT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BOMBS_DAMAGE_ELINKAGE_LEFT",
            ["mm"] = 0,
        }, -- end of ["BOMBS_DAMAGE_ELINKAGE_LEFT"]
        ["INS_FAILURE_NAV_COMPUTER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INS_FAILURE_NAV_COMPUTER",
            ["mm"] = 0,
        }, -- end of ["INS_FAILURE_NAV_COMPUTER"]
        ["L_ENG_NOZZLE_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "L_ENG_NOZZLE_FAILURE",
            ["mm"] = 0,
        }, -- end of ["L_ENG_NOZZLE_FAILURE"]
        ["ENGINE_JAM"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENGINE_JAM",
            ["mm"] = 0,
        }, -- end of ["ENGINE_JAM"]
        ["EMMC_FAILURE_LANDING_GEAR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_LANDING_GEAR",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_LANDING_GEAR"]
        ["MAGNETO_2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MAGNETO_2",
            ["mm"] = 0,
        }, -- end of ["MAGNETO_2"]
        ["L_ENG_AICS_RAMP_FAIL_CLOSED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "L_ENG_AICS_RAMP_FAIL_CLOSED",
            ["mm"] = 0,
        }, -- end of ["L_ENG_AICS_RAMP_FAIL_CLOSED"]
        ["IFF"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "IFF",
            ["mm"] = 0,
        }, -- end of ["IFF"]
        ["RWR_FAILURE_SENSOR_RIGHT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWR_FAILURE_SENSOR_RIGHT",
            ["mm"] = 0,
        }, -- end of ["RWR_FAILURE_SENSOR_RIGHT"]
        ["Failure_Elec_EmergencyBattery"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Elec_EmergencyBattery",
            ["mm"] = 0,
        }, -- end of ["Failure_Elec_EmergencyBattery"]
        ["RWR_FAILURE_ANTENNA_FRONT_LEFT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWR_FAILURE_ANTENNA_FRONT_LEFT",
            ["mm"] = 0,
        }, -- end of ["RWR_FAILURE_ANTENNA_FRONT_LEFT"]
        ["FUEL_AUX_TANK_MINOR_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_AUX_TANK_MINOR_LEAK",
            ["mm"] = 0,
        }, -- end of ["FUEL_AUX_TANK_MINOR_LEAK"]
        ["EEC_Failure_LeftEngine"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EEC_Failure_LeftEngine",
            ["mm"] = 0,
        }, -- end of ["EEC_Failure_LeftEngine"]
        ["UNCR_RH_WHEEL_BRAKE_DAMAGED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "UNCR_RH_WHEEL_BRAKE_DAMAGED",
            ["mm"] = 0,
        }, -- end of ["UNCR_RH_WHEEL_BRAKE_DAMAGED"]
        ["FUEL_TANK_03_MAJOR_LEAK"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "FUEL_TANK_03_MAJOR_LEAK",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["FUEL_TANK_03_MAJOR_LEAK"]
        ["AAR_47_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AAR_47_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["AAR_47_FAILURE_TOTAL"]
        ["COOLANT_RADIATOR_SENSOR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "COOLANT_RADIATOR_SENSOR",
            ["mm"] = 0,
        }, -- end of ["COOLANT_RADIATOR_SENSOR"]
        ["CADC_CSDC_CONNECTION"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CADC_CSDC_CONNECTION",
            ["mm"] = 0,
        }, -- end of ["CADC_CSDC_CONNECTION"]
        ["engine_driveshaft_failure"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "engine_driveshaft_failure",
            ["mm"] = 0,
        }, -- end of ["engine_driveshaft_failure"]
        ["PUMP_RELIEF_VALVE_LEAKS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "PUMP_RELIEF_VALVE_LEAKS",
            ["mm"] = 0,
        }, -- end of ["PUMP_RELIEF_VALVE_LEAKS"]
        ["hs_damage_AuxAccumulator"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "hs_damage_AuxAccumulator",
            ["mm"] = 0,
        }, -- end of ["hs_damage_AuxAccumulator"]
        ["WEAP_GUN_04_DAMAGED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "WEAP_GUN_04_DAMAGED",
            ["mm"] = 0,
        }, -- end of ["WEAP_GUN_04_DAMAGED"]
        ["ENGINE_FAILURE_NOZZLE_CONTROLLER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENGINE_FAILURE_NOZZLE_CONTROLLER",
            ["mm"] = 0,
        }, -- end of ["ENGINE_FAILURE_NOZZLE_CONTROLLER"]
        ["CADC_FAILURE_STATIC"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CADC_FAILURE_STATIC",
            ["mm"] = 0,
        }, -- end of ["CADC_FAILURE_STATIC"]
        ["Failure_Elec_UtilityBattery"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Elec_UtilityBattery",
            ["mm"] = 0,
        }, -- end of ["Failure_Elec_UtilityBattery"]
        ["ENG0_OIL_HOSE_0_BURST"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_OIL_HOSE_0_BURST",
            ["mm"] = 0,
        }, -- end of ["ENG0_OIL_HOSE_0_BURST"]
        ["FUEL_NITRO_TANK_00_MINOR_LEAK"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "FUEL_NITRO_TANK_00_MINOR_LEAK",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["FUEL_NITRO_TANK_00_MINOR_LEAK"]
        ["IFFCC_FAILURE_GUN"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "IFFCC_FAILURE_GUN",
            ["mm"] = 0,
        }, -- end of ["IFFCC_FAILURE_GUN"]
        ["R_ENG_NOZZLE_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "R_ENG_NOZZLE_FAILURE",
            ["mm"] = 0,
        }, -- end of ["R_ENG_NOZZLE_FAILURE"]
        ["CNI_FAILURE_IFF_RX"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CNI_FAILURE_IFF_RX",
            ["mm"] = 0,
        }, -- end of ["CNI_FAILURE_IFF_RX"]
        ["slat_inner_right_stuck"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "slat_inner_right_stuck",
            ["mm"] = 0,
        }, -- end of ["slat_inner_right_stuck"]
        ["UNCR_CONTROLLER_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "UNCR_CONTROLLER_FAILURE",
            ["mm"] = 0,
        }, -- end of ["UNCR_CONTROLLER_FAILURE"]
        ["FUEL_MAIN_TANK_MINOR_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUEL_MAIN_TANK_MINOR_LEAK",
            ["mm"] = 0,
        }, -- end of ["FUEL_MAIN_TANK_MINOR_LEAK"]
        ["FUEL_TANK_01_MINOR_LEAK"] = 
        {
            ["hh"] = 0,
            ["id"] = "FUEL_TANK_01_MINOR_LEAK",
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["FUEL_TANK_01_MINOR_LEAK"]
        ["DTC_FAILURE_DATA_CRC"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "DTC_FAILURE_DATA_CRC",
            ["mm"] = 0,
        }, -- end of ["DTC_FAILURE_DATA_CRC"]
        ["esf_LeftGenerator"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "esf_LeftGenerator",
            ["mm"] = 0,
        }, -- end of ["esf_LeftGenerator"]
        ["K14_MOTOR_DEFECTIVE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "K14_MOTOR_DEFECTIVE",
            ["mm"] = 0,
        }, -- end of ["K14_MOTOR_DEFECTIVE"]
        ["AB_fire"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AB_fire",
            ["mm"] = 0,
        }, -- end of ["AB_fire"]
        ["14"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "14",
            ["mm"] = 0,
        }, -- end of ["14"]
        ["24"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "24",
            ["mm"] = 0,
        }, -- end of ["24"]
        ["IMU_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "IMU_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["IMU_FAILURE_TOTAL"]
        ["INS_PART_FAIL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INS_PART_FAIL",
            ["mm"] = 0,
        }, -- end of ["INS_PART_FAIL"]
        ["ELEC_SUPERCHARGER_BULB_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_SUPERCHARGER_BULB_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_SUPERCHARGER_BULB_FAILURE"]
        ["MWMMC_FAILURE_1553B_RALT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MWMMC_FAILURE_1553B_RALT",
            ["mm"] = 0,
        }, -- end of ["MWMMC_FAILURE_1553B_RALT"]
        ["Failure_PP_EngL_OilLeak"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_PP_EngL_OilLeak",
            ["mm"] = 0,
        }, -- end of ["Failure_PP_EngL_OilLeak"]
        ["CANARDSERVORIGHT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CANARDSERVORIGHT",
            ["mm"] = 0,
        }, -- end of ["CANARDSERVORIGHT"]
        ["REAR_TANK_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "REAR_TANK_LEAK",
            ["mm"] = 0,
        }, -- end of ["REAR_TANK_LEAK"]
        ["hs_damage_MainHydro"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "hs_damage_MainHydro",
            ["mm"] = 0,
        }, -- end of ["hs_damage_MainHydro"]
        ["tr1_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "tr1_fail",
            ["mm"] = 0,
        }, -- end of ["tr1_fail"]
        ["FCS_FAILURE_R_ELEVATOR_HYD_1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_R_ELEVATOR_HYD_1",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_R_ELEVATOR_HYD_1"]
        ["alt2_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "alt2_fail",
            ["mm"] = 0,
        }, -- end of ["alt2_fail"]
        ["RIGHT_TANK_PUMP_FAULT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RIGHT_TANK_PUMP_FAULT",
            ["mm"] = 0,
        }, -- end of ["RIGHT_TANK_PUMP_FAULT"]
        ["AN_ALR69V_FAILURE_SENSOR_TAIL_LEFT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AN_ALR69V_FAILURE_SENSOR_TAIL_LEFT",
            ["mm"] = 0,
        }, -- end of ["AN_ALR69V_FAILURE_SENSOR_TAIL_LEFT"]
        ["BOMBS_DAMAGE_LINKAGE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BOMBS_DAMAGE_LINKAGE",
            ["mm"] = 0,
        }, -- end of ["BOMBS_DAMAGE_LINKAGE"]
        ["FCS_FAILURE_WOW_3"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_WOW_3",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_WOW_3"]
        ["HYD_Combined"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "HYD_Combined",
            ["mm"] = 0,
        }, -- end of ["HYD_Combined"]
        ["fs_aft_central_leakage"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fs_aft_central_leakage",
            ["mm"] = 0,
        }, -- end of ["fs_aft_central_leakage"]
        ["BATTERY_OVERHEAT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BATTERY_OVERHEAT",
            ["mm"] = 0,
        }, -- end of ["BATTERY_OVERHEAT"]
        ["L_GEAR_UPL_JAMMED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "L_GEAR_UPL_JAMMED",
            ["mm"] = 0,
        }, -- end of ["L_GEAR_UPL_JAMMED"]
        ["ELEC_LH_CABIN_LIGHT_POOR_CONTACT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_LH_CABIN_LIGHT_POOR_CONTACT",
            ["mm"] = 0,
        }, -- end of ["ELEC_LH_CABIN_LIGHT_POOR_CONTACT"]
        ["RADIO_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RADIO_FAILURE",
            ["mm"] = 0,
        }, -- end of ["RADIO_FAILURE"]
        ["Failure_Fuel_LeftBoostPump"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Fuel_LeftBoostPump",
            ["mm"] = 0,
        }, -- end of ["Failure_Fuel_LeftBoostPump"]
        ["sas_yaw_right"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "sas_yaw_right",
            ["mm"] = 0,
        }, -- end of ["sas_yaw_right"]
        ["ELEC_LH_FLAPS_DRIVE_WIRE_SEVERED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_LH_FLAPS_DRIVE_WIRE_SEVERED",
            ["mm"] = 0,
        }, -- end of ["ELEC_LH_FLAPS_DRIVE_WIRE_SEVERED"]
        ["Failure_PP_EngL_AB_FFCS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_PP_EngL_AB_FFCS",
            ["mm"] = 0,
        }, -- end of ["Failure_PP_EngL_AB_FFCS"]
        ["FUELTANK5R"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FUELTANK5R",
            ["mm"] = 0,
        }, -- end of ["FUELTANK5R"]
        ["DC_BUS_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "DC_BUS_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["DC_BUS_FAILURE_TOTAL"]
        ["fs_damage_TransferPump"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fs_damage_TransferPump",
            ["mm"] = 0,
        }, -- end of ["fs_damage_TransferPump"]
        ["TACH_BREAK_CIRCUIT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TACH_BREAK_CIRCUIT",
            ["mm"] = 0,
        }, -- end of ["TACH_BREAK_CIRCUIT"]
        ["AC_BUS_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AC_BUS_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["AC_BUS_FAILURE_TOTAL"]
        ["TAIL_GEAR_D_LOCK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TAIL_GEAR_D_LOCK",
            ["mm"] = 0,
        }, -- end of ["TAIL_GEAR_D_LOCK"]
        ["hud"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "hud",
            ["mm"] = 0,
        }, -- end of ["hud"]
        ["EMMC_FAILURE_ECS_OFF"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_ECS_OFF",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_ECS_OFF"]
        ["ELEC_BOMBABWGERAT_RELEASE_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_BOMBABWGERAT_RELEASE_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_BOMBABWGERAT_RELEASE_FAILURE"]
        ["INS_DATA_DEGRADED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INS_DATA_DEGRADED",
            ["mm"] = 0,
        }, -- end of ["INS_DATA_DEGRADED"]
        ["MWMMC_FAILURE_1553B_EMMC"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MWMMC_FAILURE_1553B_EMMC",
            ["mm"] = 0,
        }, -- end of ["MWMMC_FAILURE_1553B_EMMC"]
        ["flap_left_stuck"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "flap_left_stuck",
            ["mm"] = 0,
        }, -- end of ["flap_left_stuck"]
        ["FLEX_S_MAIN_LAMP_DEFECTIVE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FLEX_S_MAIN_LAMP_DEFECTIVE",
            ["mm"] = 0,
        }, -- end of ["FLEX_S_MAIN_LAMP_DEFECTIVE"]
        ["engine_flameout_irrecoverable"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "engine_flameout_irrecoverable",
            ["mm"] = 0,
        }, -- end of ["engine_flameout_irrecoverable"]
        ["R_ENG_AFTC_PRI_FAILED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "R_ENG_AFTC_PRI_FAILED",
            ["mm"] = 0,
        }, -- end of ["R_ENG_AFTC_PRI_FAILED"]
        ["ppf_LeftOil"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ppf_LeftOil",
            ["mm"] = 0,
        }, -- end of ["ppf_LeftOil"]
        ["ENG_ALT_1_FAIL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG_ALT_1_FAIL",
            ["mm"] = 0,
        }, -- end of ["ENG_ALT_1_FAIL"]
        ["LGear_ext_fault"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "LGear_ext_fault",
            ["mm"] = 0,
        }, -- end of ["LGear_ext_fault"]
        ["fuel_intercom_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fuel_intercom_fail",
            ["mm"] = 0,
        }, -- end of ["fuel_intercom_fail"]
        ["NGear_ext_fault"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "NGear_ext_fault",
            ["mm"] = 0,
        }, -- end of ["NGear_ext_fault"]
        ["WEAP_GUN_00_DAMAGED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "WEAP_GUN_00_DAMAGED",
            ["mm"] = 0,
        }, -- end of ["WEAP_GUN_00_DAMAGED"]
        ["MWMMC_FAILURE_1553B_INS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "MWMMC_FAILURE_1553B_INS",
            ["mm"] = 0,
        }, -- end of ["MWMMC_FAILURE_1553B_INS"]
        ["sas_pitch_right"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "sas_pitch_right",
            ["mm"] = 0,
        }, -- end of ["sas_pitch_right"]
        ["RPMSENSOR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RPMSENSOR",
            ["mm"] = 0,
        }, -- end of ["RPMSENSOR"]
        ["L_ENG_AFTC_PRI_FAILED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "L_ENG_AFTC_PRI_FAILED",
            ["mm"] = 0,
        }, -- end of ["L_ENG_AFTC_PRI_FAILED"]
        ["triphase_inv_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "triphase_inv_fail",
            ["mm"] = 0,
        }, -- end of ["triphase_inv_fail"]
        ["CADC_FAILURE_IAS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CADC_FAILURE_IAS",
            ["mm"] = 0,
        }, -- end of ["CADC_FAILURE_IAS"]
        ["COMPASS_ERRATIC_INDIACATON"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "COMPASS_ERRATIC_INDIACATON",
            ["mm"] = 0,
        }, -- end of ["COMPASS_ERRATIC_INDIACATON"]
        ["VHF_CRYSTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "VHF_CRYSTAL",
            ["mm"] = 0,
        }, -- end of ["VHF_CRYSTAL"]
        ["D2_RIGHT_CYLINDER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "D2_RIGHT_CYLINDER",
            ["mm"] = 0,
        }, -- end of ["D2_RIGHT_CYLINDER"]
        ["L_ENG_COMPRESSOR_STALL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "L_ENG_COMPRESSOR_STALL",
            ["mm"] = 0,
        }, -- end of ["L_ENG_COMPRESSOR_STALL"]
        ["es_damage_Inverter115_1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "es_damage_Inverter115_1",
            ["mm"] = 0,
        }, -- end of ["es_damage_Inverter115_1"]
        ["FCS_FAILURE_LG_2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_LG_2",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_LG_2"]
        ["inverter2_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "inverter2_fail",
            ["mm"] = 0,
        }, -- end of ["inverter2_fail"]
        ["Vibration_RightEngine"] = 
        {
            ["hh"] = 0,
            ["enable"] = false,
            ["prob"] = 100,
            ["id"] = "Vibration_RightEngine",
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["Vibration_RightEngine"]
        ["EMMC_FAILURE_DC_GENERATOR_SUBSYSTEM"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "EMMC_FAILURE_DC_GENERATOR_SUBSYSTEM",
            ["mm"] = 0,
        }, -- end of ["EMMC_FAILURE_DC_GENERATOR_SUBSYSTEM"]
        ["ELEVONINNERRIGHT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEVONINNERRIGHT",
            ["mm"] = 0,
        }, -- end of ["ELEVONINNERRIGHT"]
        ["INS_GYROS_FAIL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INS_GYROS_FAIL",
            ["mm"] = 0,
        }, -- end of ["INS_GYROS_FAIL"]
        ["fs_damage_FuelBoosterPump"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fs_damage_FuelBoosterPump",
            ["mm"] = 0,
        }, -- end of ["fs_damage_FuelBoosterPump"]
        ["FCS_FAILURE_ROLL_AUGD_1"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_ROLL_AUGD_1",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_ROLL_AUGD_1"]
        ["FAILURE_SMS_PYLON_5"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAILURE_SMS_PYLON_5",
            ["mm"] = 0,
        }, -- end of ["FAILURE_SMS_PYLON_5"]
        ["PNEM_BRAKE_RELAY_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "PNEM_BRAKE_RELAY_FAILURE",
            ["mm"] = 0,
        }, -- end of ["PNEM_BRAKE_RELAY_FAILURE"]
        ["hydro_common"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "hydro_common",
            ["mm"] = 0,
        }, -- end of ["hydro_common"]
        ["CMDISP"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CMDISP",
            ["mm"] = 0,
        }, -- end of ["CMDISP"]
        ["GUN_RIGHT_CENTER_OPEN_CIRCUIT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_RIGHT_CENTER_OPEN_CIRCUIT",
            ["mm"] = 0,
        }, -- end of ["GUN_RIGHT_CENTER_OPEN_CIRCUIT"]
        ["INS_FAILURE_GPS_RECEIVER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INS_FAILURE_GPS_RECEIVER",
            ["mm"] = 0,
        }, -- end of ["INS_FAILURE_GPS_RECEIVER"]
        ["WEAP_GUN_07_DAMAGED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "WEAP_GUN_07_DAMAGED",
            ["mm"] = 0,
        }, -- end of ["WEAP_GUN_07_DAMAGED"]
        ["VDI_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "VDI_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["VDI_FAILURE_TOTAL"]
        ["ef_shutdown"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ef_shutdown",
            ["mm"] = 0,
        }, -- end of ["ef_shutdown"]
        ["RWR_FAILURE_RECEIVER_XX2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWR_FAILURE_RECEIVER_XX2",
            ["mm"] = 0,
        }, -- end of ["RWR_FAILURE_RECEIVER_XX2"]
        ["PITOT_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "PITOT_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["PITOT_FAILURE_TOTAL"]
        ["ELEC_C5_LAMP_1_POOR_CONTACT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_C5_LAMP_1_POOR_CONTACT",
            ["mm"] = 0,
        }, -- end of ["ELEC_C5_LAMP_1_POOR_CONTACT"]
        ["INST_TACH1_RESISTANCE_MISMATCH"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INST_TACH1_RESISTANCE_MISMATCH",
            ["mm"] = 0,
        }, -- end of ["INST_TACH1_RESISTANCE_MISMATCH"]
        ["RWR_FAILURE_LOW_BAND"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWR_FAILURE_LOW_BAND",
            ["mm"] = 0,
        }, -- end of ["RWR_FAILURE_LOW_BAND"]
        ["ppf_LeftGearbox"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ppf_LeftGearbox",
            ["mm"] = 0,
        }, -- end of ["ppf_LeftGearbox"]
        ["PNEM_MAIN_HOSE_PERFORATED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "PNEM_MAIN_HOSE_PERFORATED",
            ["mm"] = 0,
        }, -- end of ["PNEM_MAIN_HOSE_PERFORATED"]
        ["BOMBS_ARMING_NO_VOLATAGE_BOTH"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BOMBS_ARMING_NO_VOLATAGE_BOTH",
            ["mm"] = 0,
        }, -- end of ["BOMBS_ARMING_NO_VOLATAGE_BOTH"]
        ["LIGHTS_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "LIGHTS_FAILURE",
            ["mm"] = 0,
        }, -- end of ["LIGHTS_FAILURE"]
        ["Failure_Hyd_HYD2B_Leak"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Hyd_HYD2B_Leak",
            ["mm"] = 0,
        }, -- end of ["Failure_Hyd_HYD2B_Leak"]
        ["RWR_FAILURE_ANTENNA_REAR_LEFT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWR_FAILURE_ANTENNA_REAR_LEFT",
            ["mm"] = 0,
        }, -- end of ["RWR_FAILURE_ANTENNA_REAR_LEFT"]
        ["fuel_sys_right_transfer_pump"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fuel_sys_right_transfer_pump",
            ["mm"] = 0,
        }, -- end of ["fuel_sys_right_transfer_pump"]
        ["RDR_FAILURE_PROCESSOR_OVERHEAT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RDR_FAILURE_PROCESSOR_OVERHEAT",
            ["mm"] = 0,
        }, -- end of ["RDR_FAILURE_PROCESSOR_OVERHEAT"]
        ["COOLANT_RADIATOR_MOTOR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "COOLANT_RADIATOR_MOTOR",
            ["mm"] = 0,
        }, -- end of ["COOLANT_RADIATOR_MOTOR"]
        ["Failure_Ctrl_FCS_Ch4"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Ctrl_FCS_Ch4",
            ["mm"] = 0,
        }, -- end of ["Failure_Ctrl_FCS_Ch4"]
        ["AAR_47_FAILURE_SENSOR_TAIL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "AAR_47_FAILURE_SENSOR_TAIL",
            ["mm"] = 0,
        }, -- end of ["AAR_47_FAILURE_SENSOR_TAIL"]
        ["19"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "19",
            ["mm"] = 0,
        }, -- end of ["19"]
        ["GUN_LEFT_CENTER_OPEN_CIRCUIT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "GUN_LEFT_CENTER_OPEN_CIRCUIT",
            ["mm"] = 0,
        }, -- end of ["GUN_LEFT_CENTER_OPEN_CIRCUIT"]
        ["TailRotorControlFailure"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TailRotorControlFailure",
            ["mm"] = 0,
        }, -- end of ["TailRotorControlFailure"]
        ["RIGHT_WING_TANK_LEAK"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RIGHT_WING_TANK_LEAK",
            ["mm"] = 0,
        }, -- end of ["RIGHT_WING_TANK_LEAK"]
        ["ACCSENSOR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ACCSENSOR",
            ["mm"] = 0,
        }, -- end of ["ACCSENSOR"]
        ["WEAP_GUN_07_AMMO_BELT_SEVERED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "WEAP_GUN_07_AMMO_BELT_SEVERED",
            ["mm"] = 0,
        }, -- end of ["WEAP_GUN_07_AMMO_BELT_SEVERED"]
        ["es_damage_SpareInverter"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "es_damage_SpareInverter",
            ["mm"] = 0,
        }, -- end of ["es_damage_SpareInverter"]
        ["RWR_FAILURE_QUAD135"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "RWR_FAILURE_QUAD135",
            ["mm"] = 0,
        }, -- end of ["RWR_FAILURE_QUAD135"]
        ["Failure_Ctrl_FCS_Ch2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Ctrl_FCS_Ch2",
            ["mm"] = 0,
        }, -- end of ["Failure_Ctrl_FCS_Ch2"]
        ["ROCKETS_INTERVALOMETER_SEQ"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ROCKETS_INTERVALOMETER_SEQ",
            ["mm"] = 0,
        }, -- end of ["ROCKETS_INTERVALOMETER_SEQ"]
        ["ENG0_WATER_RADIATOR_0_PIERCED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ENG0_WATER_RADIATOR_0_PIERCED",
            ["mm"] = 0,
        }, -- end of ["ENG0_WATER_RADIATOR_0_PIERCED"]
        ["left_fuel_pump_fail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "left_fuel_pump_fail",
            ["mm"] = 0,
        }, -- end of ["left_fuel_pump_fail"]
        ["SAR_2_101"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SAR_2_101",
            ["mm"] = 0,
        }, -- end of ["SAR_2_101"]
        ["FUEL_TANK_00_FIRE_STOPPED"] = 
        {
            ["hh"] = 0,
            ["id"] = "FUEL_TANK_00_FIRE_STOPPED",
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["hidden"] = true,
            ["mm"] = 0,
        }, -- end of ["FUEL_TANK_00_FIRE_STOPPED"]
        ["CARBAIR_BREAK_LEADS"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CARBAIR_BREAK_LEADS",
            ["mm"] = 0,
        }, -- end of ["CARBAIR_BREAK_LEADS"]
        ["FAULTY_ROCKET_RIGHT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAULTY_ROCKET_RIGHT",
            ["mm"] = 0,
        }, -- end of ["FAULTY_ROCKET_RIGHT"]
        ["CICU_FAILURE_TOTAL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CICU_FAILURE_TOTAL",
            ["mm"] = 0,
        }, -- end of ["CICU_FAILURE_TOTAL"]
        ["WEAP_GUN_02_DAMAGED"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "WEAP_GUN_02_DAMAGED",
            ["mm"] = 0,
        }, -- end of ["WEAP_GUN_02_DAMAGED"]
        ["FCS_FAILURE_R_ELEVATOR_HYD_2"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_R_ELEVATOR_HYD_2",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_R_ELEVATOR_HYD_2"]
        ["FCS_FAILURE_Q_SENSOR_3"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_Q_SENSOR_3",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_Q_SENSOR_3"]
        ["SUPERCHARGER_LIGHT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "SUPERCHARGER_LIGHT",
            ["mm"] = 0,
        }, -- end of ["SUPERCHARGER_LIGHT"]
        ["BOMBS_RUST_RIGHT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BOMBS_RUST_RIGHT",
            ["mm"] = 0,
        }, -- end of ["BOMBS_RUST_RIGHT"]
        ["CLOGGED_FUEL_STRAINER"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CLOGGED_FUEL_STRAINER",
            ["mm"] = 0,
        }, -- end of ["CLOGGED_FUEL_STRAINER"]
        ["BOMBS_NO_VOLATAGE_AT_RACK_LEFT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BOMBS_NO_VOLATAGE_AT_RACK_LEFT",
            ["mm"] = 0,
        }, -- end of ["BOMBS_NO_VOLATAGE_AT_RACK_LEFT"]
        ["engfail"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "engfail",
            ["mm"] = 0,
        }, -- end of ["engfail"]
        ["ELEC_UC_LAMP_CU_BULB_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_UC_LAMP_CU_BULB_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_UC_LAMP_CU_BULB_FAILURE"]
        ["HYD_PUMP_2_FAIL_100"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "HYD_PUMP_2_FAIL_100",
            ["mm"] = 0,
        }, -- end of ["HYD_PUMP_2_FAIL_100"]
        ["ELEC_OIL_GAUGE_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_OIL_GAUGE_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_OIL_GAUGE_FAILURE"]
        ["TEMPSENSOR"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "TEMPSENSOR",
            ["mm"] = 0,
        }, -- end of ["TEMPSENSOR"]
        ["FAILURE_EXT_LIGHT_ANTICOL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAILURE_EXT_LIGHT_ANTICOL",
            ["mm"] = 0,
        }, -- end of ["FAILURE_EXT_LIGHT_ANTICOL"]
        ["CANARDFLAPRIGHT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "CANARDFLAPRIGHT",
            ["mm"] = 0,
        }, -- end of ["CANARDFLAPRIGHT"]
        ["ELEC_MSB_CONTROLS_FAILURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_MSB_CONTROLS_FAILURE",
            ["mm"] = 0,
        }, -- end of ["ELEC_MSB_CONTROLS_FAILURE"]
        ["fire_sys_fire_RE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fire_sys_fire_RE",
            ["mm"] = 0,
        }, -- end of ["fire_sys_fire_RE"]
        ["18"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "18",
            ["mm"] = 0,
        }, -- end of ["18"]
        ["FAILURE_SNS_CABLE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FAILURE_SNS_CABLE",
            ["mm"] = 0,
        }, -- end of ["FAILURE_SNS_CABLE"]
        ["LeftEngine_LowOilPressure"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "LeftEngine_LowOilPressure",
            ["mm"] = 0,
        }, -- end of ["LeftEngine_LowOilPressure"]
        ["HYDR_ACCUMULATOR_LOW_AIR_PRESSURE"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "HYDR_ACCUMULATOR_LOW_AIR_PRESSURE",
            ["mm"] = 0,
        }, -- end of ["HYDR_ACCUMULATOR_LOW_AIR_PRESSURE"]
        ["fsf_LeftBoostPump"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "fsf_LeftBoostPump",
            ["mm"] = 0,
        }, -- end of ["fsf_LeftBoostPump"]
        ["ELEC_DASHBOARD_HARNESS_CUT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "ELEC_DASHBOARD_HARNESS_CUT",
            ["mm"] = 0,
        }, -- end of ["ELEC_DASHBOARD_HARNESS_CUT"]
        ["INS_TOTAL_FAIL"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "INS_TOTAL_FAIL",
            ["mm"] = 0,
        }, -- end of ["INS_TOTAL_FAIL"]
        ["BOMBS_ARMING_NO_VOLATAGE_RIGHT"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "BOMBS_ARMING_NO_VOLATAGE_RIGHT",
            ["mm"] = 0,
        }, -- end of ["BOMBS_ARMING_NO_VOLATAGE_RIGHT"]
        ["csf_YawDamper"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "csf_YawDamper",
            ["mm"] = 0,
        }, -- end of ["csf_YawDamper"]
        ["Failure_Fuel_ExtTankTransferC"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "Failure_Fuel_ExtTankTransferC",
            ["mm"] = 0,
        }, -- end of ["Failure_Fuel_ExtTankTransferC"]
        ["FCS_FAILURE_R_ELEVATOR_ELEC_C"] = 
        {
            ["hh"] = 0,
            ["prob"] = 100,
            ["enable"] = false,
            ["mmint"] = 1,
            ["id"] = "FCS_FAILURE_R_ELEVATOR_ELEC_C",
            ["mm"] = 0,
        }, -- end of ["FCS_FAILURE_R_ELEVATOR_ELEC_C"]
    }, -- end of ["failures"]
    ["forcedOptions"] = 
    {
    }, -- end of ["forcedOptions"]
    ["start_time"] = 28800,
} -- end of mission
