diff --git a/Doc/DML Documentation.pdf b/Doc/DML Documentation.pdf index 3807a5c..30bd3dc 100644 Binary files a/Doc/DML Documentation.pdf and b/Doc/DML Documentation.pdf differ diff --git a/modules/cfxOwnedZones.lua b/modules/cfxOwnedZones.lua index f38baa6..f5cb3aa 100644 --- a/modules/cfxOwnedZones.lua +++ b/modules/cfxOwnedZones.lua @@ -1,5 +1,5 @@ cfxOwnedZones = {} -cfxOwnedZones.version = "1.2.2" +cfxOwnedZones.version = "1.2.3" cfxOwnedZones.verbose = false cfxOwnedZones.announcer = true cfxOwnedZones.name = "cfxOwnedZones" @@ -47,6 +47,7 @@ cfxOwnedZones.name = "cfxOwnedZones" - no cfxGroundTroop bug (no delay) 1.2.1 - fix in load to correctly re-establish all attackers for subsequent save 1.2.2 - redCap! and blueCap! +1.2.3 - fix for persistence bug when not using conquered flag --]]-- @@ -901,7 +902,9 @@ function cfxOwnedZones.saveData() zoneData.defenderData = dcsCommon.clone(theZone.defenderData) dcsCommon.synchGroupData(zoneData.defenderData) end - zoneData.conquered = cfxZones.getFlagValue(theZone.conqueredFlag, theZone) + if theZone.conqueredFlag then + zoneData.conquered = cfxZones.getFlagValue(theZone.conqueredFlag, theZone) + end zoneData.owner = theZone.owner zoneData.state = theZone.state -- will prevent immediate spawn -- since new zones are spawned with 'init' @@ -972,7 +975,9 @@ function cfxOwnedZones.loadData() end theZone.owner = zData.owner theZone.state = zData.state - cfxZones.setFlagValue(theZone.conqueredFlag, zData.conquered, theZone) + if zData.conquered then + cfxZones.setFlagValue(theZone.conqueredFlag, zData.conquered, theZone) + end -- update mark in map cfxOwnedZones.drawZoneInMap(theZone) else diff --git a/modules/cfxZones.lua b/modules/cfxZones.lua index 6c22ecb..f043aeb 100644 --- a/modules/cfxZones.lua +++ b/modules/cfxZones.lua @@ -1,5 +1,5 @@ cfxZones = {} -cfxZones.version = "2.9.1" +cfxZones.version = "2.9.2" -- cf/x zone management module -- reads dcs zones and makes them accessible and mutable @@ -109,6 +109,11 @@ cfxZones.version = "2.9.1" - 2.9.1 - new evalRemainder() - pollFlag supports +/- for immediate numbers, flags, number flags in parantheses - stronger guards in hasProperty +- 2.9.2 - new createRandomPointInPolyZone() + - createRandomZoneInZone uses createRandomPointInPolyZone + - new createRandomPointInZone() + - new randomPointInZone() + --]]-- cfxZones.verbose = false @@ -207,7 +212,7 @@ function cfxZones.readFromDCS(clearfirst) local upperName = newZone.name:upper() -- location as 'point' - -- WARNING: zones locs are 2D (x,y) pairs, whily y in DCS is altitude. + -- 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. @@ -229,10 +234,12 @@ function cfxZones.readFromDCS(clearfirst) elseif zoneType == 2 then -- polyZone newZone.isPoly = true - newZone.radius = dcsZone.radius -- radius is still written in DCS, may change later + 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 vor this + -- correct for this local verts = {} if dcsZone.verticies then verts = dcsZone.verticies else @@ -242,7 +249,7 @@ function cfxZones.readFromDCS(clearfirst) for v=1, #verts do local dcsPoint = verts[v] - local polyPoint = cfxZones.createPointFromDCSPoint(dcsPoint) -- (x, y) -- (x, 0, y-->z) + local polyPoint = cfxZones.createPointFromDCSPoint(dcsPoint) -- (x, y) --> (x, 0, y-->z) newZone.poly[v] = polyPoint end else @@ -290,15 +297,18 @@ function cfxZones.calculateZoneBounds(theZone) 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 - + 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 + 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 @@ -307,6 +317,9 @@ function cfxZones.calculateZoneBounds(theZone) bounds.lr = lr bounds.ul = ul bounds.ur = ur + -- store pRad + theZone.pRad = pRad -- not sure we'll ever need that, but at least we have it +-- trigger.action.outText("+++Zones: poly zone <" .. theZone.name .. "> has pRad = " .. pRad, 30) -- remember to remove me else -- huston, we have a problem if cfxZones.verbose then @@ -351,11 +364,82 @@ 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 cfxZones.createPoint(x, 0, z) 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 cfxZones.randomPointInZone(theZone) + local loc, dx, dy = cfxZones.createRandomPointInZone(theZone) + return loc, dx, dy +end + +function cfxZones.createRandomPointInCircleZone(theZone) + 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 * math.random() + 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 cfxZones.createRandomPointInPolyZone(theZone) + if not theZone.isPoly then + trigger.action.outText("+++Zones: warning - createRandomPointInPolyZone called for non-poly zone <" .. theZone.name .. ">", 30) + return cfxZones.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) + + -- 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 cfxZones.addZoneToManagedZones(theZone) local upperName = string.upper(theZone.name) -- newZone.name:upper() cfxZones.zones[upperName] = theZone @@ -435,6 +519,7 @@ function cfxZones.createRandomZoneInZone(name, inZone, targetRadius, entirelyIns -- 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 -- trigger.action.outText("Zones: creating rZiZ with tr = " .. targetRadius .. " for " .. inZone.name .. " that as r = " .. inZone.radius, 10) @@ -458,6 +543,8 @@ function cfxZones.createRandomZoneInZone(name, inZone, targetRadius, entirelyIns -- we have a poly zone. the way we do this is simple: -- generate random x, z with ranges of the bounding box -- until the point falls within the polygon. + --[[ replaced by new code + local newPoint = {} local emergencyBrake = 0 repeat @@ -465,11 +552,12 @@ function cfxZones.createRandomZoneInZone(name, inZone, targetRadius, entirelyIns emergencyBrake = emergencyBrake + 1 if (emergencyBrake > 100) then newPoint = cfxZones.copyPoint(inZone.Point) - trigger.action.outText("CreateZoneInZone: mergency brake for inZone" .. inZone.name, 10) + trigger.action.outText("CreateZoneInZone: emergency brake for inZone" .. inZone.name, 10) break end until cfxZones.isPointInsidePoly(newPoint, inZone.poly) - + --]]-- + local newPoint = cfxZones.createRandomPointInPolyZone(inZone) -- construct new zone local newZone = cfxZones.createCircleZone(name, newPoint.x, newPoint.z, targetRadius) return newZone diff --git a/modules/cloneZone.lua b/modules/cloneZone.lua index f39d37e..786423f 100644 --- a/modules/cloneZone.lua +++ b/modules/cloneZone.lua @@ -1,5 +1,5 @@ cloneZones = {} -cloneZones.version = "1.6.0" +cloneZones.version = "1.6.1" cloneZones.verbose = false cloneZones.requiredLibs = { "dcsCommon", -- always @@ -65,6 +65,12 @@ cloneZones.allCObjects = {} -- all clones objects 1.6.0 - fixed issues with cloning for zones with linked units - cloning with useHeading - major declutter + 1.6.1 - removed some verbosity when not rotating routes + - updateTaskLocations () + - cloning groups now also adjusts tasks like search and engage in zone + - cloning with rndLoc supports polygons + - corrected rndLoc without centerOnly to not include individual offsets + - ensure support of recovery tanker resolve cloned group --]]-- @@ -402,6 +408,21 @@ function cloneZones.rotateWPAroundCenter(thePoint, center, angle) thePoint.y = py + center.z -- !! end +function cloneZones.updateTaskLocations(thePoint, zoneDelta) + -- parse tasks for x and y and update them by zoneDelta + if thePoint and thePoint.task and thePoint.task.params and thePoint.task.params.tasks then + local theTasks = thePoint.task.params.tasks + for idx, aTask in pairs(theTasks) do + -- EngageTargetsInZone task has x & y in params + if aTask.params and aTask.params.x and aTask.params.y then + aTask.params.x = aTask.params.x + zoneDelta.x + aTask.params.y = aTask.params.y + zoneDelta.z --!! +-- trigger.action.outText("moved search & engage zone", 30) + end + end + end +end + function cloneZones.updateLocationsInGroupData(theData, zoneDelta, adjustAllWaypoints, center, angle) -- enter with theData being group's data block -- remember that zoneDelta's [z] modifies theData's y!! @@ -427,8 +448,9 @@ function cloneZones.updateLocationsInGroupData(theData, zoneDelta, adjustAllWayp if center and angle then cloneZones.rotateWPAroundCenter(thePoints[i], center, angle) else - trigger.action.outText("not rotating route", 30) +-- trigger.action.outText("not rotating route", 30) end + cloneZones.updateTaskLocations(thePoints[i], zoneDelta) end else -- only first point @@ -437,6 +459,7 @@ function cloneZones.updateLocationsInGroupData(theData, zoneDelta, adjustAllWayp if center and angle then cloneZones.rotateWPAroundCenter(thePoints[1], center, angle) end + cloneZones.updateTaskLocations(thePoints[i], zoneDelta) end -- if there is an airodrome id given in first waypoint, @@ -617,8 +640,10 @@ function cloneZones.resolveWPReferences(rawData, theZone, dataTable) local task = aPoint.task if task and task.params and task.params.tasks then local tasks = task.params.tasks + -- iterate all tasks for this waypoint for idy, taskData in pairs(tasks) do -- resolve group references in TASKS + -- also covers recovery tanke etc if taskData.id and taskData.params and taskData.params.groupId then -- we resolve group reference @@ -628,7 +653,7 @@ function cloneZones.resolveWPReferences(rawData, theZone, dataTable) end - -- resolve EMBARK/DISEMBARK groupd references + -- resolve EMBARK/DISEMBARK group references if taskData.id and taskData.params and taskData.params.groupsForEmbarking then -- build new groupsForEmbarking @@ -686,6 +711,7 @@ function cloneZones.resolveWPReferences(rawData, theZone, dataTable) end -- resolve unit references in ACTIONS + -- for example TACAN if taskData.params and taskData.params.action and taskData.params.action.params and taskData.params.action.params.unitId then local uID = taskData.params.action.params.unitId @@ -795,24 +821,32 @@ 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) + -- 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 + else + aUnit.x = aUnit.x + dx + 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) end - aUnit.x = aUnit.x + dx - aUnit.y = aUnit.y + dy + end end @@ -982,10 +1016,11 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone) -- randomize if enabled if spawnZone.rndLoc then - 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 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 rawData.x = rawData.x + dx rawData.y = rawData.y + dy end diff --git a/modules/dcsCommon.lua b/modules/dcsCommon.lua index f685782..d5b4d20 100644 --- a/modules/dcsCommon.lua +++ b/modules/dcsCommon.lua @@ -1,5 +1,5 @@ dcsCommon = {} -dcsCommon.version = "2.7.9" +dcsCommon.version = "2.7.10" --[[-- VERSION HISTORY 2.2.6 - compassPositionOfARelativeToB - clockPositionOfARelativeToB @@ -117,6 +117,7 @@ dcsCommon.version = "2.7.9" - createGroundGroupWithUnits corrected spelling of minDist, crashed scattered formation - randomPointInCircle fixed erroneous local for x, z - "scattered" formation repaired + 2.7.10- semaphore groundwork --]]-- @@ -2892,7 +2893,30 @@ function dcsCommon.LSR(a, num) return a 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 diff --git a/modules/messenger.lua b/modules/messenger.lua index 7050bf2..3a95ad2 100644 --- a/modules/messenger.lua +++ b/modules/messenger.lua @@ -43,24 +43,24 @@ messenger.messengers = {} - unit - group 2.0.1 - config optimization - 2.1.0 - unit only: dynamicUnitProcessing for - - bearing to unit/zone - - response mapped by unit's heading - - bearing in clock position to unit/zone - - range to unit/zone - - bearing in left/right/ahead/behind - - bearing in starboard/port/ahead/aft - - added dynamicGroupProcessing to select unit 1 - - responses attribute - - - - response randomized - - respons mapped by unit's heading - - closing speed - - velocity (speed) - - aspect - - fix to messageMute - - - + 2.1.0 - unit only: dynamicUnitProcessing with other units/zones + - bearing to unit/zone + - response mapped by unit's heading + - bearing in clock position to unit/zone + - range to unit/zone + - bearing in left/right/ahead/behind + - bearing in starboard/port/ahead/aft + - added dynamicGroupProcessing to select unit 1 + - responses attribute + - + - response randomized + - respons mapped by unit's heading + - closing speed + - velocity (speed) + - aspect + - fix to messageMute + - + 2.1.1 - cosmetic: only output text if len>0 and not cls --]]-- @@ -665,14 +665,18 @@ function messenger.isTriggered(theZone) if theZone.spaceAfter then msg = msg .. "\n" end if theZone.msgCoalition then - trigger.action.outTextForCoalition(theZone.msgCoalition, msg, theZone.duration, theZone.clearScreen) + if #msg > 0 or theZone.clearScreen then + trigger.action.outTextForCoalition(theZone.msgCoalition, msg, theZone.duration, theZone.clearScreen) + end trigger.action.outSoundForCoalition(theZone.msgCoalition, fileName) elseif theZone.msgGroup then local theGroup = Group.getByName(theZone.msgGroup) if theGroup and Group.isExist(theGroup) then local ID = theGroup:getID() msg = messenger.dynamicGroupProcessing(msg, theZone, theGroup) - trigger.action.outTextForGroup(ID, msg, theZone.duration, theZone.clearScreen) + if #msg > 0 or theZone.clearScreen then + trigger.action.outTextForGroup(ID, msg, theZone.duration, theZone.clearScreen) + end trigger.action.outSoundForGroup(ID, fileName) end elseif theZone.msgUnit then @@ -680,12 +684,16 @@ function messenger.isTriggered(theZone) if theUnit and Unit.isExist(theUnit) then local ID = theUnit:getID() msg = messenger.dynamicUnitProcessing(msg, theZone, theUnit) - trigger.action.outTextForUnit(ID, msg, theZone.duration, theZone.clearScreen) + if #msg > 0 or theZone.clearScreen then + trigger.action.outTextForUnit(ID, msg, theZone.duration, theZone.clearScreen) + end trigger.action.outSoundForUnit(ID, fileName) end else -- out to all - trigger.action.outText(msg, theZone.duration, theZone.clearScreen) + if #msg > 0 or theZone.clearScreen then + trigger.action.outText(msg, theZone.duration, theZone.clearScreen) + end trigger.action.outSound(fileName) end end diff --git a/modules/persistence.lua b/modules/persistence.lua index a2f8484..8199596 100644 --- a/modules/persistence.lua +++ b/modules/persistence.lua @@ -25,6 +25,7 @@ persistence.requiredLibs = { 1.0.3 - no longer always tells " mission saved to" new 'saveNotification" can be off 1.0.4 - new optional 'root' property + 1.0.5 - desanitize check on readConfig to early-abort PROVIDES LOAD/SAVE ABILITY TO MODULES @@ -419,6 +420,11 @@ function persistence.collectFlagsFromZone(theZone) end function persistence.readConfigZone() + if not _G["lfs"] then + trigger.action.outText("+++persistence: DCS not correctly desanitized. Persistence disabled", 30) + return + end + local theZone = cfxZones.getZoneByName("persistenceConfig") local hasConfig = true if not theZone then diff --git a/modules/radioMenus.lua b/modules/radioMenus.lua index cee7bca..d984029 100644 --- a/modules/radioMenus.lua +++ b/modules/radioMenus.lua @@ -1,5 +1,5 @@ radioMenu = {} -radioMenu.version = "2.0.0" +radioMenu.version = "2.0.1" radioMenu.verbose = false radioMenu.ups = 1 radioMenu.requiredLibs = { @@ -24,6 +24,8 @@ radioMenu.menus = {} gereric helo type generic plane type type works with coalition + 2.0.1 corrections to installMenu(), as suggested by GumidekCZ + --]]-- @@ -198,8 +200,9 @@ function radioMenu.installMenu(theZone) if cfxZones.hasProperty(theZone, "itemB") then local menuB = cfxZones.getStringFromZoneProperty(theZone, "itemB", "") if theZone.menuGroup or theZone.menuTypes then + theZone.menuB = {} for idx, grp in pairs(gID) do - theZone.menuB[grp] = missionCommands.addCommandForGroup(grp, menuB, theZone.rootMenu[grp], radioMenu.redirectMenuX, {theZone, "B"}) + theZone.menuB[grp] = missionCommands.addCommandForGroup(grp, menuB, theZone.rootMenu[grp], radioMenu.redirectMenuX, {theZone, "B", grp}) end elseif theZone.coalition == 0 then theZone.menuB = missionCommands.addCommand(menuB, theZone.rootMenu[0], radioMenu.redirectMenuX, {theZone, "B"}) @@ -211,8 +214,9 @@ function radioMenu.installMenu(theZone) if cfxZones.hasProperty(theZone, "itemC") then local menuC = cfxZones.getStringFromZoneProperty(theZone, "itemC", "") if theZone.menuGroup or theZone.menuTypes then + theZone.menuC = {} for idx, grp in pairs(gID) do - theZone.menuC[grp] = missionCommands.addCommandForGroup(grp, menuC, theZone.rootMenu[grp], radioMenu.redirectMenuX, {theZone, "C"}) + theZone.menuC[grp] = missionCommands.addCommandForGroup(grp, menuC, theZone.rootMenu[grp], radioMenu.redirectMenuX, {theZone, "C", grp}) end elseif theZone.coalition == 0 then theZone.menuC = missionCommands.addCommand(menuC, theZone.rootMenu[0], radioMenu.redirectMenuX, {theZone, "C"}) @@ -224,8 +228,9 @@ function radioMenu.installMenu(theZone) if cfxZones.hasProperty(theZone, "itemD") then local menuD = cfxZones.getStringFromZoneProperty(theZone, "itemD", "") if theZone.menuGroup or theZone.menuTypes then + theZone.menuD = {} for idx, grp in pairs(gID) do - theZone.menuD[grp] = missionCommands.addCommandForGroup(grp, menuD, theZone.rootMenu[grp], radioMenu.redirectMenuX, {theZone, "D"}) + theZone.menuD[grp] = missionCommands.addCommandForGroup(grp, menuD, theZone.rootMenu[grp], radioMenu.redirectMenuX, {theZone, "D", grp}) end elseif theZone.coalition == 0 then theZone.menuD = missionCommands.addCommand(menuD, theZone.rootMenu[0], radioMenu.redirectMenuX, {theZone, "D"})