diff --git a/Doc/DML Documentation.pdf b/Doc/DML Documentation.pdf index 6171b44..467630b 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 e16acb0..2ccf642 100644 Binary files a/Doc/DML Quick Reference.pdf and b/Doc/DML Quick Reference.pdf differ diff --git a/modules/cfxMX.lua b/modules/cfxMX.lua index bab4291..1d19b20 100644 --- a/modules/cfxMX.lua +++ b/modules/cfxMX.lua @@ -1,5 +1,5 @@ cfxMX = {} -cfxMX.version = "2.0.1" +cfxMX.version = "2.0.2" cfxMX.verbose = false --[[-- Mission data decoder. Access to ME-built mission structures @@ -12,7 +12,7 @@ cfxMX.verbose = false 2.0.0 - clean-up - harmonized with cfxGroups 2.0.1 - groupHotByName - + 2.0.2 - partOfGroupDataInZone(), allGroupsInZoneByData() from milHelo --]]-- cfxMX.groupNamesByID = {} @@ -343,6 +343,38 @@ function cfxMX.catText2ID(inText) return outCat end + +function cfxMX.partOfGroupDataInZone(theZone, theUnits) -- move to mx? + --local zP --= cfxZones.getPoint(theZone) + local zP = theZone:getDCSOrigin() -- don't use getPoint now. + zP.y = 0 + + for idx, aUnit in pairs(theUnits) do + local uP = {} + uP.x = aUnit.x + uP.y = 0 + uP.z = aUnit.y -- !! y-z + if theZone:pointInZone(uP) then return true end + end + return false +end + +function cfxMX.allGroupsInZoneByData(theZone) -- returns groups indexed by name and count + local theGroupsInZone = {} + local count = 0 + for groupName, groupData in pairs(cfxMX.groupDataByName) do + if groupData.units then + if cfxMX.partOfGroupDataInZone(theZone, groupData.units) then + theGroupsInZone[groupName] = groupData -- DATA! work on clones! + count = count + 1 + if theZone.verbose then + trigger.action.outText("+++cfxMX: added group <" .. groupName .. "> for zone <" .. theZone.name .. ">", 30) + end + end + end + end + return theGroupsInZone, count +end function cfxMX.start() cfxMX.createCrossReferences() diff --git a/modules/cfxZones.lua b/modules/cfxZones.lua index d782dd6..feaadb6 100644 --- a/modules/cfxZones.lua +++ b/modules/cfxZones.lua @@ -1,5 +1,5 @@ cfxZones = {} -cfxZones.version = "4.3.4" +cfxZones.version = "4.3.6" -- cf/x zone management module -- reads dcs zones and makes them accessible and mutable @@ -53,6 +53,11 @@ cfxZones.version = "4.3.4" - 4.3.2 - new getListFromZoneProperty() - 4.3.3 - hardened calculateZoneBounds - 4.3.4 - rewrote zone bounds for poly zones +- 4.3.5 - hardened getStringFromZoneProperty against number value returns (WebEd bug) +- 4.3.6 - tiny optimization in isPointInsideQuad + - moving zone - hardening code for static objects + - moving zones - now deriving dx, dy,uHeading from dcsCommon xref for linked zones + --]]-- -- @@ -208,7 +213,7 @@ function cfxZones.readFromDCS(clearfirst) -- 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) @@ -706,7 +711,7 @@ function cfxZones.isPointInsideQuad(thePoint, A, B, C, D) -- 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 + local 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 @@ -2220,6 +2225,9 @@ function cfxZones.getStringFromZoneProperty(theZone, theProperty, default) -- OOP heavy duty test here local p = theZone:getZoneProperty(theProperty) if not p then return default end + if type(p) == "number" then + p = tostring(p) + end if type(p) == "string" then p = dcsCommon.trim(p) if p == "" then p = default end @@ -2232,6 +2240,9 @@ 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) == "number" then + p = tostring(p) + end if type(p) == "string" then p = dcsCommon.trim(p) if p == "" then p = default end @@ -3341,7 +3352,9 @@ function cfxZones.linkUnitToZone(theUnit, theZone, dx, dy) -- note: dy is really theZone.dx = dx theZone.dy = dy theZone.rxy = math.sqrt(dx * dx + dy * dy) -- radius - local unitHeading = dcsCommon.getUnitHeading(theUnit) + local uName = theUnit:getName() +-- local unitHeading = dcsCommon.getUnitHeading(theUnit) + local unitHeading = dcsCommon.unitName2Heading[uName] -- get original unit's heading from ME local bearingOffset = math.atan2(dy, dx) -- rads if bearingOffset < 0 then bearingOffset = bearingOffset + 2 * 3.141592 end @@ -3431,8 +3444,9 @@ function cfxZones.updateMovingZones() cfxZones.initLink(aZone) else --if aZone.linkName then -- always re-acquire linkedUnit via Unit.getByName() - -- this way we gloss over any replacements via spawns + -- this way we gloss over any replacements via spawns/clones aZone.linkedUnit = Unit.getByName(aZone.linkName) + if not aZone.linkUnit then aZone.linkUnit = StaticObject.getByName(aZone.linkName) end end if aZone.linkedUnit then @@ -3465,14 +3479,15 @@ end function cfxZones.initLink(theZone) theZone.linkBroken = true theZone.linkedUnit = nil - theUnit = Unit.getByName(theZone.linkName) + theUnit = Unit.getByName(theZone.linkName) -- unit or static + if not theUnit then theUnit = StaticObject.getByName(theZone.linkName) end 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 B = dcsCommon.getOrigPositionByID(theZone.linkedUID) + local delta = dcsCommon.vSub(A,B) dx = delta.x dz = delta.z @@ -3483,7 +3498,6 @@ function cfxZones.initLink(theZone) 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) @@ -3494,14 +3508,14 @@ end function dmlZone:initLink() self.linkBroken = true self.linkedUnit = nil - theUnit = Unit.getByName(self.linkName) + theUnit = Unit.getByName(self.linkName) -- unit or static + if not theUnit then theUnit = StaticObject.getByName(self.linkName) end 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 B = dcsCommon.getOrigPositionByID(self.linkedUID) local delta = dcsCommon.vSub(A,B) dx = delta.x dz = delta.z @@ -3509,7 +3523,7 @@ function dmlZone:initLink() 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) + trigger.action.outText("DML: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 @@ -3531,27 +3545,33 @@ function cfxZones.startMovingZones() -- late 2022 with 2.8 if aZone.dcsZone.linkUnit then local theID = aZone.dcsZone.linkUnit - lU = dcsCommon.getUnitNameByID(theID) + lU = dcsCommon.getUnitNameByID(theID) -- can be unit OR STATIC OBJECT 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") + aZone.linkedUID = lU + elseif aZone:hasProperty("linkedUnit") then + lU = aZone:getZoneProperty("linkedUnit") -- getString: name of unit + local luid = dcsCommon.unitName2ID[lU] + if luid then + aZone.linkedUID = luid + else + trigger.action.outText("WARNING: zone <" .. aZone.name .. "> linked unit (by attribute) <" .. lU .. "> does not exist!", 30) + lU = nil + end end -- sanity check - if aZone.dcsZone.linkUnit and cfxZones.hasProperty(aZone, "linkedUnit") then + if aZone.dcsZone.linkUnit and aZone:hasProperty("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) - + aZone.useOffset = aZone:getBoolFromZoneProperty("useOffset", false) + aZone.useHeading = aZone:getBoolFromZoneProperty("useHeading", false) cfxZones.initLink(aZone) - end end diff --git a/modules/convoy.lua b/modules/convoy.lua new file mode 100644 index 0000000..3fd9a7c --- /dev/null +++ b/modules/convoy.lua @@ -0,0 +1,219 @@ +convoy = {} +convoy.version = "0.0.0" +convoy.requiredLibs = { + "dcsCommon", + "cfxZones", + "cfxMX", +} +convoy.zones = {} +convoy.running = {} +convoy.ups = 1 + +function convoy.addConvoyZone(theZone) + convoy.zones[theZone.name] = theZone +end + +function convoy.readConvoyZone(theZone) + theZone.coa = theZone:getCoalitionFromZoneProperty("coalition", 0) + if theZone:hasProperty("masterOwner") then + local mo = theZone:getStringFromZoneProperty("masterOwner") + local mz = cfxZones.getZoneByName(mo) + if not mz then + trigger.action.outText("+++cvoy: WARNING: Master Owner <" .. mo .. "> for zone <" .. theZone.name .. "> does not exist!", 30) + else + theZone.masterOwner = mz + end + theZone.isDynamic = theZone:getBoolFromZoneProperty("dynamic", true) + end + -- get groups inside me. + local myGroups, count = cfxMX.allGroupsInZoneByData(theZone) + trigger.action.outText("zone <" .. theZone.name .. ">: <" .. count .. "> convoy groups", 30) + theZone.myGroups = myGroups + theZone.unique = theZone:getBoolFromZoneProperty("unique", true) + theZone.preWipe = theZone:getBoolFromZoneProperty("preWipe", true) or theZone.unique + theZone.onStart = theZone:getBoolFromZoneProperty("onStart", false) + + -- wipe all existing + for groupName, data in pairs(myGroups) do + local g = Group.getByName(groupName) + if g then + Group.destroy(g) + end + end +end + +function convoy.startConvoy(theZone) + -- make sure my coa is set up correctly + local mo = theZone.masterOwner + if mo then + theZone.owner = mo.owner + if theZone.isDynamic then + theZone.coa = mo.owner + end + end + + -- iterate all groups + local spawns = {} + for gName, gOrig in pairs(theZone.myGroups) do + trigger.action.outText("convoy: startting group <" .. gName .. "> for zone <" .. theZone.name .. ">", 30) + local gData = dcsCommon.clone(gOrig) + -- make unique names for group and units if desired + if theZone.unique then + gData.name = dcsCommon.uuid(gOrig.name) + for idx, theUnit in pairs (gData.units) do + theUnit.name = dcsCommon.uuid(theUnit.name) + end + end + convoy.amendData(theZone, gData) -- add actions to route + -- wipe existing if requested + if theZone.preWipe then + + end + local catRaw = cfxMX.groupTypeByName[gName] + local gCat = Group.Category.GROUND + if catRaw == "helicopter" then + gCat = Group.Category.HELICOPTER + elseif catRaw == "plane" then + gCat = Group.Category.AIRPLANE + elseif catRaw == "vehicle" then + gCat = Group.Category.GROUND + else -- missing so far: ship + trigger.action.outText("+++milH: ignored group <" .. gName .. ">: unknown type <" .. catRaw .. ">", 30) + end + local cty = dcsCommon.getACountryForCoalition(theZone.coa) + local theSpawnedGroup = coalition.addGroup(cty, gCat, gData) + spawns[gData.name] = theSpawnedGroup + trigger.action.outText("convoy <" .. theSpawnedGroup:getName() .. "> spawned for <" .. theZone.name .. ">", 30) + end +end + +function convoy.amendData(theZone, theData) + -- place a callback action for each waypoint + -- in data block + if not theData.route then return end + local route = theData.route + if not route.points then return end + local points = route.points + local np = #points + if np < 1 then return end + trigger.action.outText("convoy: group <" .. theData.name .. ">, zone <" .. theZone.name .. ">, points=<" .. np .. ">", 30) + +-- for i=1, np do + local newPoints = {} + for idx, aPoint in pairs(points) do + local wp = dcsCommon.clone(aPoint) -- points[i] + local tasks = wp.task.params.tasks + --local i = idx +-- if not tasks then tasks = {} end +-- if tasks then +-- dcsCommon.dumpVar2Str("RAW tasks 1bc " .. idx, tasks) + local tnew = #tasks + 1 -- new number for this task + local t = { + ["number"] = tnew, + ["auto"] = false, + ["id"] = "WrappedAction", + ["enabled"] = true, + ["params"] = { + ["action"] = { + ["id"] = "Script", + ["params"] = { + ["command"] = "trigger.action.outText(\"convoy reached WP Index " .. idx .." = WP(" .. idx-1 .. ") of " .. np .. "\", 30)", + }, -- end of ["params"] + }, -- end of ["action"] + }, -- end of ["params"] + } -- end of task + -- add t to tasks + table.insert(tasks, t) +-- tasks[tnew] = t +-- dcsCommon.dumpVar2Str("tasks for modded 1bc " .. idx, tasks) + newPoints[idx] = wp + trigger.action.outText("convoy: added wp task to wp <" .. idx .. ">", 30) + -- end +-- dcsCommon.dumpVar2Str("modded point 1BC WP" .. idx, wp) + newPoints[idx] = wp + end + route.points = newPoints +-- dcsCommon.dumpVar2Str("points", points) + +end + +-- +-- UPDATE +-- +function convoy.update() + timer.scheduleFunction(convoy.update, {}, timer.getTime() + 1/convoy.ups) + -- update all master owners + for idx, theZone in pairs (convoy.zones) do +--[[-- local mo = theZone.masterOwner + if mo then + theZone.owner = mo.owner + if theZone.isDynamic then + theZone.coa = mo.owner + end + end --]]-- + end +end + +-- +-- START +-- + +function convoy.readConfigZone() + local theZone = cfxZones.getZoneByName("convoyConfig") + if not theZone then + theZone = cfxZones.createSimpleZone("convoyConfig") + end + convoy.verbose = theZone.verbose + convoy.ups = theZone:getNumberFromZoneProperty("ups", 1) +end + +function convoy.start() + if not dcsCommon.libCheck then + trigger.action.outText("cfx convoy requires dcsCommon", 30) + return false + end + if not dcsCommon.libCheck("cfx convoy", convoy.requiredLibs) then + return false + end + + -- read config + convoy.readConfigZone() + + -- process convoy Zones + local attrZones = cfxZones.getZonesWithAttributeNamed("convoy") + for k, aZone in pairs(attrZones) do + convoy.readConvoyZone(aZone) -- process attributes + convoy.addConvoyZone(aZone) -- add to list + end + + -- start update + timer.scheduleFunction(convoy.update, {}, timer.getTime() + 1/convoy.ups) + + -- start all zones that have onstart + for gName, theZone in pairs(convoy.zones) do + if theZone.onStart then + convoy.startConvoy(theZone) + end + end + return true +end + +if not convoy.start() then + trigger.action.outText("convoy failed to start up") + convoy = nil +end + +--[[-- +convoy module +place over a fully configured group, will clone on command (start?) +reportWaypoint option. Add small script to each and every waypoint, will create report +destinationReached! -- adds script to last waypoint to hit this signal, also inits cb +dead! signal and cb. only applies to ground troops? can they disembark troops when hit? +attacked signal each time a unit is destroyed +importantType - type that must survive= +coalition / masterOwner +isActive# 0/1 +can only have one active convoy +can it have helicopters? + +--]]-- \ No newline at end of file diff --git a/modules/dcsCommon.lua b/modules/dcsCommon.lua index 0961c2e..9c3f0db 100644 --- a/modules/dcsCommon.lua +++ b/modules/dcsCommon.lua @@ -1,5 +1,5 @@ dcsCommon = {} -dcsCommon.version = "3.0.8" +dcsCommon.version = "3.0.9" --[[-- VERSION HISTORY 3.0.0 - removed bad bug in stringStartsWith, only relevant if caseSensitive is false - point2text new intsOnly option @@ -20,9 +20,12 @@ dcsCommon.version = "3.0.8" - new pointXpercentYdegOffAB() 3.0.6 - new arrayContainsStringCaseInsensitive() 3.0.7 - fixed small bug in wildArrayContainsString -3.0.8 - deepCopy() and deepTableCopy() alternates to clone() to patch +3.0.9 - deepCopy() and deepTableCopy() alternates to clone() to patch around a strange DCS 2.9 issue Kiowa added to Troop Carriers +3.0.9 - new getOrigPositionByID() + - unitName2ID[] reverse lookup + - unitName2Heading --]]-- @@ -45,6 +48,8 @@ dcsCommon.version = "3.0.8" dcsCommon.unitID2Name = {} dcsCommon.unitID2X = {} dcsCommon.unitID2Y = {} + dcsCommon.unitName2ID = {} + dcsCommon.unitName2Heading = {} -- verify that a module is loaded. obviously not required -- for dcsCommon, but all higher-order modules @@ -97,15 +102,18 @@ dcsCommon.version = "3.0.8" local aID = group_data.groupId -- store this reference dcsCommon.groupID2Name[aID] = aName - +-- trigger.action.outText("cmn: group <" .. aName .. "> has id <" .. aID .. ">", 30) -- 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 +-- trigger.action.outText("cmn: unit/obj <" .. unit_data.name .. "> has id <" .. unit_data.unitId .. ">", 30) + dcsCommon.unitName2Heading[unit_data.name] = unit_data.heading dcsCommon.unitID2X[unit_data.unitId] = unit_data.x dcsCommon.unitID2Y[unit_data.unitId] = unit_data.y + dcsCommon.unitName2ID[unit_data.name] = unit_data.unitId end end -- for all units end -- for all groups @@ -121,9 +129,14 @@ dcsCommon.version = "3.0.8" function dcsCommon.getUnitNameByID(theID) -- accessor function for later expansion - return dcsCommon.unitID2Name[theID] + return dcsCommon.unitID2Name[theID] -- warning: can be unit or static object end + function dcsCommon.getOrigPositionByID(theID) + local p = {x=dcsCommon.unitID2X[theID], y=0, z=dcsCommon.unitID2Y[theID]} + return p + end + function dcsCommon.getGroupNameByID(theID) -- accessor function for later expansion return dcsCommon.groupID2Name[theID] diff --git a/modules/milHelo.lua b/modules/milHelo.lua index f4c04e1..4ddf61f 100644 --- a/modules/milHelo.lua +++ b/modules/milHelo.lua @@ -1,5 +1,5 @@ milHelo = {} -milHelo.version = "1.0.0" +milHelo.version = "1.0.2" milHelo.requiredLibs = { "dcsCommon", "cfxZones", @@ -31,10 +31,10 @@ end function milHelo.addMilTargetZone(theZone) milHelo.targets[theZone.name] = theZone -- overwrite if duplicate end - +--[[-- function milHelo.partOfGroupDataInZone(theZone, theUnits) -- move to mx? - local zP = cfxZones.getPoint(theZone) - zP = theZone:getDCSOrigin() -- don't use getPoint now. + --local zP --= cfxZones.getPoint(theZone) + local zP = theZone:getDCSOrigin() -- don't use getPoint now. zP.y = 0 for idx, aUnit in pairs(theUnits) do @@ -63,6 +63,7 @@ function milHelo.allGroupsInZoneByData(theZone) -- move to MX? end return theGroupsInZone, count end +--]]-- function milHelo.readMilHeloZone(theZone) -- process attributes -- get mission type. part of milHelo @@ -88,7 +89,7 @@ function milHelo.readMilHeloZone(theZone) -- process attributes end -- get all groups inside me - local myGroups, count = milHelo.allGroupsInZoneByData(theZone) + local myGroups, count = cfxMX.allGroupsInZoneByData(theZone) theZone.myGroups = myGroups theZone.groupCount = count theZone.hGroups = {} @@ -662,7 +663,7 @@ end -- update and event -- function milHelo.update() - timer.scheduleFunction(milHelo.update, {}, timer.getTime() + 1) + timer.scheduleFunction(milHelo.update, {}, timer.getTime() + 1/milHelo.ups) -- update all master owners for idx, theZone in pairs (milHelo.zones) do local mo = theZone.masterOwner @@ -791,6 +792,7 @@ function milHelo.readConfigZone() end milHelo.verbose = theZone.verbose milHelo.landingDuration = theZone:getNumberFromZoneProperty("landingDuration", 180) -- seconds = 3 minutes + milHelo.ups = theZone:getNumberFromZoneProperty("ups", 1) end diff --git a/modules/objectDestructDetector.lua b/modules/objectDestructDetector.lua index c9f0974..3a09edb 100644 --- a/modules/objectDestructDetector.lua +++ b/modules/objectDestructDetector.lua @@ -1,5 +1,5 @@ cfxObjectDestructDetector = {} -cfxObjectDestructDetector.version = "2.0.2" +cfxObjectDestructDetector.version = "2.0.3" cfxObjectDestructDetector.verbose = false cfxObjectDestructDetector.requiredLibs = { "dcsCommon", -- always @@ -22,6 +22,7 @@ cfxObjectDestructDetector.requiredLibs = { API for PlayerScore to pass back redScore/blueScore if objects was killed by player verbosity bug fixed after kill (ref to old ID) + 2.0.3 if no output! given,a warning and default are given --]]-- cfxObjectDestructDetector.objectZones = {} @@ -81,6 +82,9 @@ function cfxObjectDestructDetector.processObjectDestructZone(aZone) aZone.outDestroyFlag = aZone:getStringFromZoneProperty("destroyed!", "*none") elseif aZone:hasProperty("objectDestroyed!") then aZone.outDestroyFlag = aZone:getStringFromZoneProperty( "objectDestroyed!", "*none") + else + trigger.action.outText("+++ODD: WARNING: destrcut detector <" .. aZone.name .. "> has no output! attribute", 30) + aZone.outDestroyFlag = "sorryIforgot" -- so saving works end --PlayerScore interface (data) diff --git a/modules/spawnZones.lua b/modules/spawnZones.lua index f24ea57..a770306 100644 --- a/modules/spawnZones.lua +++ b/modules/spawnZones.lua @@ -1,5 +1,5 @@ cfxSpawnZones = {} -cfxSpawnZones.version = "2.0.2" +cfxSpawnZones.version = "2.0.3" cfxSpawnZones.requiredLibs = { "dcsCommon", -- common is of course needed for everything -- pretty stupid to check for this since we @@ -28,6 +28,7 @@ cfxSpawnZones.spawnedGroups = {} - spawnWithSpawner direct link in spawner to spawnZones 2.0.1 - fix in verifySpawnOwnership() when not master zone found 2.0.2 - new "moveFormation" attribute + 2.0.3 - corrected type in spawnUnits? attribute --]]-- @@ -76,7 +77,7 @@ function cfxSpawnZones.createSpawner(inZone) 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.triggerFlag = inZone:getStringFromZoneProperty( "spawnUnits?", "none") theSpawner.lastTriggerValue = trigger.misc.getUserFlag(theSpawner.triggerFlag) end diff --git a/modules/stopGaps.lua b/modules/stopGaps.lua index 5f986d2..e819fd6 100644 --- a/modules/stopGaps.lua +++ b/modules/stopGaps.lua @@ -1,5 +1,5 @@ stopGap = {} -stopGap.version = "1.1.1" +stopGap.version = "1.1.2" stopGap.verbose = false stopGap.ssbEnabled = true stopGap.ignoreMe = "-sg" @@ -51,6 +51,7 @@ stopGap.requiredLibs = { 1.0.10 - some more verbosity for spIgnore and sgIgnore zones (DML only) 1.1.0 - kickTheDead option 1.1.1 - filter "from runway" clients + 1.1.2 - allNeutral (DML only) --]]-- @@ -76,6 +77,10 @@ function stopGap.staticMXFromUnitMX(theGroup, theUnit) theStatic.type = theUnit.type theStatic.name = theUnit.name -- will magically be replaced with player unit theStatic.cty = cfxMX.countryByName[theGroup.name] + -- DML only: allNeutral + if stopGap.allNeutral then + theStatic.cty = dcsCommon.getACountryForCoalition(0) + end return theStatic end @@ -432,6 +437,7 @@ function stopGap.readConfigZone(theZone) stopGap.refreshInterval = theZone:getNumberFromZoneProperty("refresh", -1) -- default: no refresh stopGap.kickTheDead = theZone:getBoolFromZoneProperty("kickDead", true) + stopGap.allNeutral = theZone:getBoolFromZoneProperty("allNeutral", false) end -- @@ -492,4 +498,7 @@ end if not stopGap.start() then trigger.action.outText("+++ aborted stopGap v" .. stopGap.version .. " -- startup failed", 30) stopGap = nil -end \ No newline at end of file +end +--[[-- TODO + - allNeutral: spawn all player aircraft as neutral +--]]-- diff --git a/modules/theDebugger.lua b/modules/theDebugger.lua index 3e771fb..892f8f8 100644 --- a/modules/theDebugger.lua +++ b/modules/theDebugger.lua @@ -1,6 +1,5 @@ --- theDebugger 2.x debugger = {} -debugger.version = "2.1.1" +debugger.version = "3.0.0" debugDemon = {} debugDemon.version = "2.1.0" @@ -40,6 +39,13 @@ debugger.log = "" readback verification of flag set fixed getProperty() in debugger with zone 2.1.1 - removed bug that skipped events? when zone not verbose + 3.0.0 - xref module added + - x + - x * + - x *f + - x *z + - x ? + --]]-- @@ -138,6 +144,263 @@ debugger.spawnTypes = { ["manpad"] = "Soldier stinger", ["obj"] = "house2arm" } + + +-- +-- XREF MODULE +-- +xref = {} +xref.version = "1.0.0" +xref.dmlObjects = {} -- dict by zone name:upper() +-- has inputs: dict of string for each '?' input, contains input flag name +-- has output: dict of array for each output '!', contains output flag names as array +xref.flags = {} -- dict by flag name +-- has froms: dict of zone of attributes that can write to this flags +-- has tos: dict of zones of attributes that read from flag + +function xref.getDmlObject(name) + name = name:upper() + local theObject = xref.dmlObjects[name] + if not theObject then + theObject = {} + theObject.inputs = {} -- dict of string + theObject.outputs = {} -- dict of array + xref.dmlObjects[name] = theObject + end + return theObject +end + +function xref.getFlag(name, theZone) + if theZone and dcsCommon.stringStartsWith(name, "*") then + -- local name conversion + name = theZone.name .. name + end + local theFlag = xref.flags[name] + if not theFlag then + theFlag = {} + theFlag.froms = {} -- dict by zone name/output that write to this flag + theFlag.tos = {} -- dict by zone name / inputs that reads from this flag + xref.flags[name] = theFlag + end + return theFlag +end + +function xref.flagTo(flagName, inputName, theZone) -- connect flag to input in zone. Flag can connect to multiple inputs in same zone + local theFlag = xref.getFlag(flagName, theZone) + if not theFlag.tos[theZone.name] then + theFlag.tos[theZone.name] = {} + end + table.insert(theFlag.tos[theZone.name],inputName) +end + +function xref.flagFrom(flagName, outputName, theZone) -- connect flag to output in zone. multiple outputs per zone + local theFlag = xref.getFlag(flagName, theZone) + if not theFlag.froms[theZone.name] then + theFlag.froms[theZone.name] = {} + end + table.insert(theFlag.froms[theZone.name], outputName) +end + +function xref.addInput(theZone, inputName, inputFlagName) + local theObject = xref.getDmlObject(theZone.name) + local inputs = theObject.inputs + if not inputs then inputs = {} end + inputs[inputName] = inputFlagName -- each input connects one flag + xref.flagTo(inputFlagName, inputName, theZone) + theObject.inputs = inputs +end + +function xref.addOutput(theZone, outputName, outputFlags) + local theObject = xref.getDmlObject(theZone.name) + local outputs = theObject.outputs + if not outputs then outputs = {} end + if dcsCommon.containsString(outputFlags, ",") then + local theArray = dcsCommon.splitString(outputFlags, ',') + theArray = dcsCommon.trimArray(theArray) + for idx, aFlagName in pairs(theArray) do + xref.flagFrom(aFlagName, outputName, theZone) + end + outputs[outputName] = theArray + else + local outputFlagName = dcsCommon.trim(outputFlags) + outputs[outputName] = {outputFlagName} + xref.flagFrom(outputFlagName, outputName, theZone) + end + + theObject.outputs = outputs +end + +function xref.scanMissionZones() + -- iterate all trigger zones + for idx, theZone in pairs(cfxZones.zones) do + -- iterate all properties + local attributes = theZone:getAllZoneProperties() + for name, value in pairs(attributes) do + -- find inputs + if dcsCommon.stringEndsWith(name, "?") then + xref.addInput(theZone, name, value) + end + + -- find outputs + if dcsCommon.stringEndsWith(name, "!") then + xref.addOutput(theZone, name, value) + end + + -- other stuff, e.g. "#" + -- find outputs + if dcsCommon.stringEndsWith(name, "#") then + xref.addOutput(theZone, name, value) + end + end + end +end + +function xref.xrefFlag(name) + local msg = "\n<" .. name .. "> flag cross reference:" + local theFlag = xref.flags[name] + local tos = theFlag.tos + if dcsCommon.getSizeOfTable(tos) < 1 then + msg = msg .. "\n (NO INPUTS CONNECTED)" + else + msg = msg .. "\n '?' These zones/inputs? look at <" .. name .. ">:" + for zName, attributes in pairs (tos) do + msg = msg .. "\n " .. zName .. " - " + local c = 0 + for idx, anInput in pairs(attributes) do + if c > 0 then msg = msg .. ", " end + c = 1 + msg = msg .. anInput + end + end + end + + local froms = theFlag.froms + if dcsCommon.getSizeOfTable(froms) < 1 then + msg = msg .. "\n (NO OUTPUTS CONNECTED)" + else + msg = msg .. "\n '!' These zones/outputs! change <" .. name .. ">:" + for zName, attributes in pairs (froms) do + msg = msg .. "\n " .. zName .." - " + local c = 0 + for idx, anOutput in pairs(attributes) do + if c > 0 then msg = msg .. ", " end + c = 1 + msg = msg .. anOutput + end + end + end + +-- trigger.action.outText(msg, 30) + return msg + +end + +function xref.xrefZone(name, theObject) + local msg = "\nZone <" .. name .. "> :" + local ins = theObject.inputs + if dcsCommon.getSizeOfTable(ins) < 1 then + msg = msg .. "\n (NO INPUTS)" + else + msg = msg .. "\n has the following inputs:" + for iName, flagName in pairs (ins) do + msg = msg .. "\n " .. iName .. " : " .. flagName + end + end + + local outs = theObject.outputs + if dcsCommon.getSizeOfTable(outs) < 1 then + msg = msg .. "\n (NO OUTPUTS)" + else + msg = msg .. "\n has the following outputs:" + for oName, flagList in pairs (outs) do + msg = msg .. "\n " .. oName .." - " + local c = 0 + for idx, anOutput in pairs(flagList) do + if c > 0 then msg = msg .. ", " end + c = 1 + msg = msg .. anOutput + end + end + end + +-- trigger.action.outText(msg, 30) + return msg + +end + +function xref.xrefName(name) + if not name then name = "" end + if xref.flags[name] then + return xref.xrefFlag(name) + end + if xref.dmlObjects[name:upper()] then + return xref.xrefZone(name, xref.dmlObjects[name:upper()]) + end + return "*** xref: <" .. name .. "> NOT USED WITH DML" +end + +function xref.allFlags() + local msg = "xref: all flags used with DML in this mission:\n" + local c = 0 + for name, data in pairs(xref.flags) do + if c > 0 then msg = msg .. ", " end + c = 1 + msg = msg .. "<" .. name .. ">" + end + return msg +end + +function xref.allZones() + local msg = "xref: all DML Zones in this mission:\n" + local c = 0 + for name, data in pairs(xref.dmlObjects) do + if c > 0 then msg = msg .. ", " end + c = 1 + msg = msg .. "<" .. name .. ">" + end + return msg +end + +function xref.xall() + msg = "" + -- now dump all flags + for flagName, data in pairs (xref.flags) do + -- msg = msg .. xref.xrefFlag(flagName) + msg = msg .. xref.xrefName(flagName) + end + + -- dump all zones + for zoneName, data in pairs(xref.dmlObjects) do +-- msg = msg .. xref.xrefZone(zoneName, data) + msg = msg .. xref.xrefName(zoneName) + end + return msg +-- trigger.action.outText(msg, 30) +end + +function xref.start() + xref.scanMissionZones() + local flagNum = dcsCommon.getSizeOfTable(xref.flags) + local dmlObNum = dcsCommon.getSizeOfTable(xref.dmlObjects) + trigger.action.outText("XRef v" .. xref.version .. " full DML object scan on mission complete:\n<" .. flagNum .. "> flags are referenced in <" .. dmlObNum .. "> DML zones", 30) + +-- trigger.action.outText(xref.xall(), 30) +end + +-- run the xref +xref.start() + +--[[-- + to do + scan messenger and wildcards for flag access + +--]]-- + + +-- +-- DEBUGGER MAIn +-- + -- -- Logging & saving -- @@ -824,6 +1087,9 @@ debugger.outText("*** debugger: commands are:" .. "\n " .. debugDemon.markOfDemon .. "inc -- increase flag by 1, changing it" .. "\n " .. debugDemon.markOfDemon .. "flip -- when flag's value is 0, set it to 1, else to 0" .. + "\n\n " .. debugDemon.markOfDemon .. "x -- cross reference DML zone or flag" .. + + "\n\n " .. debugDemon.markOfDemon .. "observe [with ] -- observe a flag for change" .. "\n " .. debugDemon.markOfDemon .. "o [with ] -- observe a flag for change" .. "\n " .. debugDemon.markOfDemon .. "forget [with ] -- stop observing a flag" .. @@ -1609,6 +1875,36 @@ function debugDemon.processBoomCommand(args, event) debugger.outText("*** boom: placed <" .. power .. "> explosion at <" .. dcsCommon.point2text(p, true) .. ">.", 30) end +-- +-- xref +-- +function debugDemon.processXrefCommand(args, event) + -- syntax: -x | "*" | "*f" "*z" + local larg = args[1] + if not larg or larg == "" then larg = "?" end + larg = larg:lower() + if larg == "?" then + debugger.outText("*** xRef: ? = help (this), * = xref all, *f = list all DML flags, *z = list all DML zones, xref flag or zone", 30) + return true -- leave up + elseif larg == "*" then + debugger.outText(xref.xall(), 30) + return true + elseif larg == "*f" then + debugger.outText(xref.allFlags(), 30) + return true + elseif larg == "*z" then + debugger.outText(xref.allZones(), 30) + return true + else + larg = event.remainder + larg = dcsCommon.trim(larg) + if not larg then larg = "" end + local msg = xref.xrefName(larg) + debugger.outText(msg, 30) + return true + end + +end -- -- spawning units at the location of the mark -- @@ -1965,6 +2261,7 @@ function debugDemon.init() debugDemon.addCommndProcessor("a", debugDemon.processAnalyzeCommand) debugDemon.addCommndProcessor("smoke", debugDemon.processSmokeCommand) debugDemon.addCommndProcessor("boom", debugDemon.processBoomCommand) + debugDemon.addCommndProcessor("x", debugDemon.processXrefCommand) return true end diff --git a/modules/williePete.lua b/modules/williePete.lua index 0b74694..27e300d 100644 --- a/modules/williePete.lua +++ b/modules/williePete.lua @@ -1,5 +1,5 @@ williePete = {} -williePete.version = "2.0.4" +williePete.version = "2.0.5" williePete.ups = 10 -- we update at 10 fps, so accuracy of a -- missile moving at Mach 2 is within 33 meters, -- with interpolation even at 3 meters @@ -21,6 +21,7 @@ williePete.requiredLibs = { 2.0.2 - hardened playerUpdate() 2.0.3 - further hardened playerUpdate() 2.0.4 - support for the Kiowa's Hydra M259 + 2.0.5 - support for Mirage F1 WP that differ from Gazelle (?) --]]-- williePete.willies = {} @@ -32,7 +33,8 @@ williePete.blastedObjects = {} -- used when we detonate something -- recognizes WP munitions. May require regular update when new -- models come out. -williePete.smokeWeapons = {"HYDRA_70_M274","HYDRA_70_MK61","HYDRA_70_MK1","HYDRA_70_WTU1B","HYDRA_70_M156","HYDRA_70_M158","BDU_45B","BDU_33","BDU_45","BDU_45LGB","BDU_50HD","BDU_50LD","BDU_50LGB","C_8CM", "SNEB_TYPE254_H1_GREEN", "SNEB_TYPE254_H1_RED", "SNEB_TYPE254_H1_YELLOW", "FFAR M156 WP", +williePete.smokeWeapons = {"HYDRA_70_M274","HYDRA_70_MK61","HYDRA_70_MK1","HYDRA_70_WTU1B","HYDRA_70_M156","HYDRA_70_M158","BDU_45B","BDU_33","BDU_45","BDU_45LGB","BDU_50HD","BDU_50LD","BDU_50LGB","C_8CM", "SNEB_TYPE254_H1_GREEN", "SNEB_TYPE254_H1_RED", "SNEB_TYPE254_H1_YELLOW", +"SNEB_TYPE254_F1B_YELLOW", "SNEB_TYPE254_F1B_GREEN", "SNEB_TYPE254_F1B_RED", "FFAR M156 WP", "HYDRA_70_M259"} function williePete.addWillie(theWillie) diff --git a/tutorial & demo missions/demo - BFM Combat Trainer.miz b/tutorial & demo missions/demo - BFM Combat Trainer.miz index 202fd97..143eea4 100644 Binary files a/tutorial & demo missions/demo - BFM Combat Trainer.miz and b/tutorial & demo missions/demo - BFM Combat Trainer.miz differ diff --git a/tutorial & demo missions/demo - Willie Nillie.miz b/tutorial & demo missions/demo - Willie Nillie.miz index 4e10af8..35385be 100644 Binary files a/tutorial & demo missions/demo - Willie Nillie.miz and b/tutorial & demo missions/demo - Willie Nillie.miz differ