diff --git a/Doc/DML Documentation.pdf b/Doc/DML Documentation.pdf index 30bd3dc..8040a9c 100644 Binary files a/Doc/DML Documentation.pdf and b/Doc/DML Documentation.pdf differ diff --git a/Doc/DML Quick Reference.pdf b/Doc/DML Quick Reference.pdf index 6146065..767b7ff 100644 Binary files a/Doc/DML Quick Reference.pdf and b/Doc/DML Quick Reference.pdf differ diff --git a/modules/cfxZones.lua b/modules/cfxZones.lua index f043aeb..fea283a 100644 --- a/modules/cfxZones.lua +++ b/modules/cfxZones.lua @@ -1,5 +1,5 @@ cfxZones = {} -cfxZones.version = "2.9.2" +cfxZones.version = "3.0.0" -- cf/x zone management module -- reads dcs zones and makes them accessible and mutable @@ -113,6 +113,9 @@ cfxZones.version = "2.9.2" - createRandomZoneInZone uses createRandomPointInPolyZone - new createRandomPointInZone() - new randomPointInZone() +- 3.0.0 - support for DCS 2.8 linkUnit attribute, integration with + linedUnit and warning. + - initZoneVerbosity() --]]-- @@ -216,8 +219,18 @@ function cfxZones.readFromDCS(clearfirst) -- 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. - newZone.point = cfxZones.createPoint(dcsZone.x, 0, dcsZone.y) - newZone.dcsOrigin = cfxZones.createPoint(dcsZone.x, 0, dcsZone.y) + -- 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 = cfxZones.createPoint(ux + dcsZone.x, 0, uy + dcsZone.y) + newZone.dcsOrigin = cfxZones.createPoint(ux + dcsZone.x, 0, uy + dcsZone.y) + else + newZone.point = cfxZones.createPoint(dcsZone.x, 0, dcsZone.y) + newZone.dcsOrigin = cfxZones.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 @@ -2319,22 +2332,29 @@ function cfxZones.updateMovingZones() end function cfxZones.initLink(theZone) +--trigger.action.outText("enter initlink for <" .. theZone.name .. ">", 30) +--trigger.action.outText("entry verbose check: <" .. theZone.name .. "> is verbose = " .. dcsCommon.bool2YesNo(theZone.verbose), 30) theZone.linkBroken = true theZone.linkedUnit = nil theUnit = Unit.getByName(theZone.linkName) if theUnit then + --trigger.action.outText("initlink has link to <" .. theZone.linkName .. "> for <" .. theZone.name .. ">", 30) local dx = 0 local dz = 0 if theZone.useOffset or theZone.useHeading then - local delta = dcsCommon.vSub(cfxZones.getDCSOrigin(theZone),theUnit:getPoint()) -- delta = B - A + 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 + --trigger.action.outText("verbose check: <" .. theZone.name .. "> is verbose = " .. dcsCommon.bool2YesNo(theZone.verbose), 30) 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 + --trigger.action.outText("done linking <" .. theZone.linkName .. "> to zone <" .. theZone.name .. ">", 30) else if theZone.verbose then trigger.action.outText("Linked unit: no unit <" .. theZone.linkName .. "> to link <" .. theZone.name .. "> to", 30) @@ -2343,49 +2363,48 @@ function cfxZones.initLink(theZone) end function cfxZones.startMovingZones() - -- read all zoness, and look for a property called 'linkedUnit' + -- 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 - if cfxZones.hasProperty(aZone, "linkedUnit") then + -- 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) ---[[-- - -- this zone is linked to a unit - theUnit = Unit.getByName(lU) - local useOffset = cfxZones.getBoolFromZoneProperty(aZone, "useOffset", false) - if useOffset then aZone.useOffset = true end - local useHeading = cfxZones.getBoolFromZoneProperty(aZone, "useHeading") - if useHeading then aZone.useHeading = true end - if theUnit then - local dx = 0 - local dz = 0 - if useOffset or useHeading then - local delta = dcsCommon.vSub(aZone.point,theUnit:getPoint()) -- delta = B - A - dx = delta.x - dz = delta.z - end - cfxZones.linkUnitToZone(theUnit, aZone, dx, dz) - --trigger.action.outText("Link setup: dx=<" .. dx .. ">, dz=<" .. dz .. ">", 30) - if useOffset then - end - else - trigger.action.outText("Linked unit: no unit to link <" .. aZone.name .. "> to", 30) - end ---]]-- + end + + end +end + +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 - -- -- init -- @@ -2405,6 +2424,9 @@ function cfxZones.init() 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() diff --git a/modules/cloneZone.lua b/modules/cloneZone.lua index 786423f..9a15389 100644 --- a/modules/cloneZone.lua +++ b/modules/cloneZone.lua @@ -1,5 +1,5 @@ cloneZones = {} -cloneZones.version = "1.6.1" +cloneZones.version = "1.6.3" cloneZones.verbose = false cloneZones.requiredLibs = { "dcsCommon", -- always @@ -71,7 +71,10 @@ cloneZones.allCObjects = {} -- all clones objects - cloning with rndLoc supports polygons - corrected rndLoc without centerOnly to not include individual offsets - ensure support of recovery tanker resolve cloned group - + 1.6.2 - optimization to hasLiveUnits() + 1.6.3 - removed verbosity bug with rndLoc + uniqueNameGroupData has provisions for naming scheme + new uniqueNameStaticData() for naming scheme --]]-- @@ -512,14 +515,23 @@ function cloneZones.uniqueID() return uid end -function cloneZones.uniqueNameGroupData(theData) +function cloneZones.uniqueNameGroupData(theData, theCloneZone) theData.name = dcsCommon.uuid(theData.name) local units = theData.units for idx, aUnit in pairs(units) do - aUnit.name = dcsCommon.uuid(aUnit.name) + if theCloneZone and theCloneZone.namingScheme then + else + -- default naming scheme: - + aUnit.name = dcsCommon.uuid(aUnit.name) + end end end +function cloneZones.uniqueNameStaticData(theData, spawnZone) + theData.name = dcsCommon.uuid(theData.name) + +end + function cloneZones.uniqueIDGroupData(theData) theData.groupId = cloneZones.uniqueID() end @@ -821,21 +833,12 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone) if spawnZone.rndLoc then -- calculate the entire group's displacement local units = rawData.units - --[[ - local r = math.random() * spawnZone.radius - local phi = 6.2831 * math.random() -- that's 2Pi, folx - local dx = r * math.cos(phi) - local dy = r * math.sin(phi) - --]] + local loc, dx, dy = cfxZones.createRandomPointInZone(spawnZone) -- also supports polygonal zones for idx, aUnit in pairs(units) do if not spawnZone.centerOnly then -- *every unit's displacement is randomized - -- r = math.random() * spawnZone.radius - -- phi = 6.2831 * math.random() -- that's 2Pi, folx - -- dx = r * math.cos(phi) - -- dy = r * math.sin(phi) loc, dx, dy = cfxZones.createRandomPointInZone(spawnZone) aUnit.x = loc.x aUnit.y = loc.z @@ -844,7 +847,7 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone) aUnit.y = aUnit.y + dy end if spawnZone.verbose or cloneZones.verbose then - trigger.action.outText("+++clnZ: <" .. spawnZone.name .. "> R = " .. spawnZone.radius .. ":G<" .. rawData.name .. "/" .. aUnit.name .. "> - rndLoc: r = " .. r .. ", dx = " .. dx .. ", dy= " .. dy .. ".", 30) + trigger.action.outText("+++clnZ: <" .. spawnZone.name .. "> R = " .. spawnZone.radius .. ":G<" .. rawData.name .. "/" .. aUnit.name .. "> - rndLoc: dx = " .. dx .. ", dy= " .. dy .. ".", 30) end end @@ -923,7 +926,7 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone) dcsCommon.rotateGroupData(rawData, spawnZone.turn + 57.2958 *dHeading, newCenter.x, newCenter.z) -- make sure unit and group names are unique - cloneZones.uniqueNameGroupData(rawData) + cloneZones.uniqueNameGroupData(rawData, spawnZone) -- see what country we spawn for ctry = cloneZones.resolveOwnership(spawnZone, ctry) @@ -1042,7 +1045,8 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone) dcsCommon.rotateUnitData(rawData, spawnZone.turn + 57.2958 * dHeading, newCenter.x, newCenter.z) -- make sure static name is unique and remember original - rawData.name = dcsCommon.uuid(rawData.name) + cloneZones.uniqueNameStaticData(rawData, spawnZone) + --rawData.name = dcsCommon.uuid(rawData.name) rawData.unitId = cloneZones.uniqueID() rawData.CZTargetID = rawData.unitId @@ -1210,12 +1214,18 @@ function cloneZones.hasLiveUnits(theZone) if theZone.mySpawns then for idx, aGroup in pairs(theZone.mySpawns) do if aGroup:isExist() then + -- an easier/faster method would be to invoke + -- aGroup:getSize() + local uNum = aGroup:getSize() + if uNum > 0 then return true end + --[[ local allUnits = aGroup:getUnits() for idy, aUnit in pairs(allUnits) do if aUnit:isExist() and aUnit:getLife() >= 1 then return true end end + --]]-- end end end diff --git a/modules/dcsCommon.lua b/modules/dcsCommon.lua index d5b4d20..06db73b 100644 --- a/modules/dcsCommon.lua +++ b/modules/dcsCommon.lua @@ -1,5 +1,5 @@ dcsCommon = {} -dcsCommon.version = "2.7.10" +dcsCommon.version = "2.8.0" --[[-- VERSION HISTORY 2.2.6 - compassPositionOfARelativeToB - clockPositionOfARelativeToB @@ -118,6 +118,11 @@ dcsCommon.version = "2.7.10" - randomPointInCircle fixed erroneous local for x, z - "scattered" formation repaired 2.7.10- semaphore groundwork + 2.8.0 - new collectMissionIDs at start-up + - new getUnitNameByID + - new getGroupNameByID + - bool2YesNo alsco can return NIL + - new getUnitStartPosByID --]]-- @@ -134,6 +139,12 @@ dcsCommon.version = "2.7.10" dcsCommon.troopCarriers = {"Mi-8MT", "UH-1H", "Mi-24P"} -- Ka-50 and Gazelle can't carry troops dcsCommon.coalitionSides = {0, 1, 2} + -- 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) @@ -147,6 +158,82 @@ dcsCommon.version = "2.7.10" 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 @@ -2042,7 +2129,10 @@ end end function dcsCommon.bool2YesNo(theBool) - if not theBool then theBool = false end + if not theBool then + theBool = false + return "NIL" + end if theBool then return "yes" end return "no" end @@ -2917,14 +3007,18 @@ function dcsCommon.setUserFlag(flagName, theValue) end trigger.action.setUserFlag(flagName, theValue) end + -- -- -- INIT -- -- - -- init any variables the lib requires internally + -- 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) @@ -2935,10 +3029,3 @@ end -- do init. dcsCommon.init() ---[[-- - -to do: -- formation 2Column -- formation 3Column - --]]--