diff --git a/Doc/DML Documentation.pdf b/Doc/DML Documentation.pdf index 067e420..a735dae 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 b590ae4..7a3e0f1 100644 Binary files a/Doc/DML Quick Reference.pdf and b/Doc/DML Quick Reference.pdf differ diff --git a/modules/cfxSSBClient.lua b/modules/cfxSSBClient.lua index aae7087..e7f92b6 100644 --- a/modules/cfxSSBClient.lua +++ b/modules/cfxSSBClient.lua @@ -273,7 +273,7 @@ function cfxSSBClient:onEvent(event) -- player entered unit? -- check if this is cloned impostor if not theUnit.getPlayerName then - trigger.action.outText("+++SSBC: non-player client " .. uName .. " detected, ignoring.", 30) + trigger.action.outText("+++SSBC: non-player 'client' " .. uName .. " detected, ignoring.", 30) return end local playerName = theUnit:getPlayerName() diff --git a/modules/cfxZones.lua b/modules/cfxZones.lua index 2a9a277..0f7e50c 100644 --- a/modules/cfxZones.lua +++ b/modules/cfxZones.lua @@ -6,7 +6,7 @@ -- cfxZones = {} -cfxZones.version = "2.7.4" +cfxZones.version = "2.7.5" --[[-- VERSION HISTORY - 2.2.4 - getCoalitionFromZoneProperty - getStringFromZoneProperty @@ -69,6 +69,7 @@ cfxZones.version = "2.7.4" - 2.7.3 - testZoneFlag returns mathodResult, lastVal - evalFlagMethodImmediate() - 2.7.4 - doPollFlag supports immediate number setting + - 2.7.5 - more QoL checks when mixing up ? and ! for attributes --]]-- cfxZones.verbose = false @@ -1655,11 +1656,16 @@ end function cfxZones.hasProperty(theZone, theProperty) local foundIt = cfxZones.getZoneProperty(theZone, theProperty) if not foundIt then - if string.sub(theProperty, -1) == "?" 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 @@ -1668,6 +1674,10 @@ function cfxZones.hasProperty(theZone, theProperty) 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 diff --git a/modules/changer.lua b/modules/changer.lua index 20cb473..4455090 100644 --- a/modules/changer.lua +++ b/modules/changer.lua @@ -1,5 +1,5 @@ changer = {} -changer.version = "0.0.0" +changer.version = "1.0.0" changer.verbose = false changer.ups = 1 changer.requiredLibs = { @@ -15,11 +15,7 @@ changer.changers = {} - not - bool - value - - rnd - - count (? multiple signals, better done with xFlags) - - min, max minmax 2,3, cap, - - + --]]-- function changer.addChanger(theZone) @@ -48,14 +44,17 @@ function changer.createChangerWithZone(theZone) -- triggerChangerMethod theZone.triggerChangerMethod = cfxZones.getStringFromZoneProperty(theZone, "triggerMethod", "change") - if cfxZones.hasProperty(theZone, "triggerChangerMethod") then - theZone.triggerChangerMethod = cfxZones.getStringFromZoneProperty(theZone, "triggerChangerMethod", "change") + if cfxZones.hasProperty(theZone, "triggerChangeMethod") then + theZone.triggerChangerMethod = cfxZones.getStringFromZoneProperty(theZone, "triggerChangeMethod", "change") end theZone.inEval = cfxZones.getBoolFromZoneProperty(theZone, "inEval", true) -- yes/no to pre-process, default is yes theZone.changeTo = cfxZones.getStringFromZoneProperty(theZone, "to", "val") -- val, not, bool + if cfxZones.hasProperty(theZone, "changeTo") then + theZone.changerTo = cfxZones.getStringFromZoneProperty(theZone, "changeTo", "val") + end theZone.changeTo = string.lower(theZone.changeTo) theZone.changeTo = dcsCommon.trim(theZone.changeTo) @@ -64,6 +63,28 @@ function changer.createChangerWithZone(theZone) theZone.changeOut = cfxZones.getStringFromZoneProperty(theZone, "changeOut!", "*none") end + -- pause / on / off commands + theZone.changerPaused = cfxZones.getBoolFromZoneProperty(theZone, "paused", false) -- we default to unpaused + if cfxZones.hasProperty(theZone, "changePaused") then + theZone.changerPaused = cfxZones.getBoolFromZoneProperty(theZone, "changePaused", false) + end + + if theZone.changerPaused and (changer.verbose or theZone.verbose) then + trigger.action.outText("+++chgr: <" .. theZone.name .. "> starts paused", 30) + end + + theZone.changerOn = cfxZones.getStringFromZoneProperty(theZone, "on?", "*") + if cfxZones.hasProperty(theZone, "changeOn?") then + theZone.changerOn = cfxZones.getStringFromZoneProperty(theZone, "changeOn?", "*") + end + theZone.lastChangerOnValue = cfxZones.getFlagValue(theZone.changerOn, theZone) + + theZone.changerOff = cfxZones.getStringFromZoneProperty(theZone, "off?", "*") + if cfxZones.hasProperty(theZone, "changeOff?") then + theZone.changerOff = cfxZones.getStringFromZoneProperty(theZone, "changeOff?", "*") + end + theZone.lastChangerOffValue = cfxZones.getFlagValue(theZone.changerOff, theZone) + if changer.verbose or theZone.verbose then trigger.action.outText("+++chgr: new changer zone <".. theZone.name ..">", 30) end @@ -90,11 +111,14 @@ function changer.process(theZone) -- process and write outflag if op == "bool" then if currVal == 0 then res = 0 else res = 1 end - elseif - op == "not" then + elseif op == "not" then if currVal == 0 then res = 1 else res = 0 end + elseif op == "val" or op == "direct" then + -- do nothing + else + trigger.action.outText("+++chgr: unsupported changeTo operation <" .. .. "> in zone <" .. .. ">, using 'val'", 30) end - -- all others drop through + -- illegal ops drop through after warning, functioning as 'val' -- write out cfxZones.setFlagValueMult(theZone.changeOut, res, theZone) @@ -116,7 +140,27 @@ function changer.update() timer.scheduleFunction(changer.update, {}, timer.getTime() + 1/changer.ups) for idx, aZone in pairs(changer.changers) do - changer.process(aZone) + + -- process the pause/unpause flags + -- see if we should suspend + if cfxZones.testZoneFlag(theZone, theZone.changerOn, "change", "lastChangerOnValue") then + if changer.verbose or theZone.verbose then + trigger.action.outText("+++chgr: enabling " .. theZone.name, 30) + end + theZone.changerPaused = false + end + + if cfxZones.testZoneFlag(theZone, theZone.changerOff, "change", "lastChangerOffValue") then + if changer.verbose or theZone.verbose then + trigger.action.outText("+++chgr: DISabling " .. theZone.name, 30) + end + theZone.changerPaused = true + end + + -- do processing if not paused + if not aZone.changerPaused then + changer.process(aZone) + end end end @@ -173,4 +217,11 @@ end if not changer.start() then trigger.action.outText("cfx changer aborted: missing libraries", 30) changer = nil -end \ No newline at end of file +end + +--[[-- + Possible expansions + - rnd + - min, max minmax 2,3, cap to left right values, + +--]]-- \ No newline at end of file diff --git a/modules/civAir.lua b/modules/civAir.lua index 6bd66f8..2c36df9 100644 --- a/modules/civAir.lua +++ b/modules/civAir.lua @@ -1,5 +1,5 @@ civAir = {} -civAir.version = "1.4.0" +civAir.version = "1.5.0" --[[-- 1.0.0 initial version 1.1.0 exclude list for airfields @@ -16,7 +16,12 @@ civAir.version = "1.4.0" all configs it finds module check removed obsolete civAirConfig module - + 1.5.0 process zones as in all other modules + verbose is part of config zone + reading type array from config corrected + massive simplifications: always between zoned airfieds + exclude list and include list + --]]-- @@ -34,52 +39,28 @@ civAir.aircraftTypes = {"Yak-40", "Yak-40", "C-130", "C-17A", "IL-76MD", "An-30 -- concurrently under way civAir.maxTraffic = 10 -- number of flights at the same time civAir.maxIdle = 8 * 60 -- seconds of ide time before it is removed after landing -civAir.trafficAirbases = { - randomized = 0, -- between any on map - localHubs = 1, -- between any two airfields inside the same random hub listed in trafficCenters - betweenHubs = 2 -- between any in random hub 1 to any in random hub 2 - } -civAir.trafficRange = 100 -- 120000 -- defines hub size, in meters. Make it 100 to make it only that airfield --- ABPickmethod determines how airfields are picked --- for air traffic -civAir.ABPickMethod = civAir.trafficAirbases.betweenHubs + civAir.trafficCenters = { - --"batu", - --"kobul", - --"senaki", - --"kutai", - } -- trafficCenters is used with hubs. Each entry defines a hub - -- where we collect airdromes etc based on range - -- simply add a string to identify the hub center - -- e.g. "senak" to define "Senaki Kolkhi" - -- to have planes only fly between airfields in 100 km range - -- around senaki kolkhi, enter only senaki as traffic center, set - -- trafficRange to 100000 and ABPickMethod to localHubs - -- to have traffic only between any airfields listed - -- in trafficCenters, set trafficRange to a small value - -- like 100 meters and set ABPickMethod to betweenHubs - -- to have flights that always cross the map with multiple - -- airfields, choose two or three hubs that are 300 km apart, - -- then set trafficRange to 150000 and ABPickMethod to betweenHubs - -- you can also place zones on the map and add a - -- civAir attribute. If the attribute value is anything - -- but "exclude", the closest airfield to the zone - -- is added to trafficCenters - -- if you leave this list empty, and do not add airfields - -- by zones, the list is automatically populated by all - -- airfields in the map + } +-- place zones on the map and add a "civAir" attribute. +-- If the attribute's value is anything +-- but "exclude", the closest airfield to the zone +-- is added to trafficCenters + +-- if you leave this list empty, and do not add airfields +-- by zones, the list is automatically populated with all +-- airfields in the map civAir.excludeAirfields = { - --"senaki", } - -- list all airfields that must NOT be included in - -- civilian activities. Will be used for neither landing - -- nor departure. overrides any airfield that was included - -- in trafficCenters. Here, Senaki is off limits for - -- civilian air traffic - -- can be populated by zone on the map that have the - -- 'civAir' attribute with value "exclude" +-- list all airfields that must NOT be included in +-- civilian activities. Will be used for neither landing +-- nor departure. overrides any airfield that was included +-- in trafficCenters. Here, Senaki is off limits for +-- civilian air traffic +-- can be populated by zone on the map that have the +-- 'civAir' attribute with value "exclude" civAir.requiredLibs = { "dcsCommon", -- common is of course needed for everything @@ -91,7 +72,7 @@ civAir.idlePlanes = {} function civAir.readConfigZone() -- note: must match exactly!!!! - local theZone = cfxZones.getZoneByName("CivAirConfig") + local theZone = cfxZones.getZoneByName("civAirConfig") if not theZone then trigger.action.outText("***civA: NO config zone!", 30) return @@ -101,7 +82,10 @@ function civAir.readConfigZone() -- ok, for each property, load it if it exists if cfxZones.hasProperty(theZone, "aircraftTypes") then - civAir.aircraftTypes = cfxZones.getStringFromZoneProperty(theZone, "aircraftTypes", "Yak-40") + local theTypes = cfxZones.getStringFromZoneProperty(theZone, "aircraftTypes", "Yak-40") + local typeArray = dcsCommon.splitString(theTypes, ",") + typeArray = dcsCommon.trimArray(typeArray) + civAir.aircraftTypes = typeArray end if cfxZones.hasProperty(theZone, "ups") then @@ -117,18 +101,27 @@ function civAir.readConfigZone() civAir.maxIdle = cfxZones.getNumberFromZoneProperty(theZone, "maxIdle", 8 * 60) end - if cfxZones.hasProperty(theZone, "trafficRange") then - civAir.trafficRange = cfxZones.getNumberFromZoneProperty(theZone, "trafficRange", 120000) -- 120 km - end - - if cfxZones.hasProperty(theZone, "ABPickMethod") then - civAir.ABPickMethod = cfxZones.getNumberFromZoneProperty(theZone, "ABPickMethod", 0) -- randomized any - end - if cfxZones.hasProperty(theZone, "initialAirSpawns") then civAir.initialAirSpawns = cfxZones.getBoolFromZoneProperty(theZone, "initialAirSpawns", true) + end + + civAir.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false) end +function civAir.processZone(theZone) + local value = cfxZones.getStringFromZoneProperty(theZone, "civAir", "") + local af = dcsCommon.getClosestAirbaseTo(theZone.point, 0) -- 0 = only airfields, not farp or ships + if af then + local afName = af:getName() + if value:lower() == "exclude" then + table.insert(civAir.excludeAirfields, afName) + else + table.insert(civAir.trafficCenters, afName) -- note that adding the same twice makes it more likely to be picked + end + else + trigger.action.outText("+++civA: unable to resolve airfield for <" .. theZone.name .. ">", 30) + end +end function civAir.addPlane(thePlaneUnit) -- warning: is actually a group @@ -158,122 +151,49 @@ function civAir.getPlane(aName) -- warning: returns GROUP! return civAir.activePlanes[aName] end --- get an air base, may exclude an airbase from choice --- method is dependent on -function civAir.getAnAirbase(excludeThisOne) - -- different methods to select a base - -- purely random from current list - local theAB; - if civAir.ABPickMethod == civAir.trafficAirbases.randomized then - repeat - local allAB = dcsCommon.getAirbasesWhoseNameContains("*", 0) -- all airfields, no Ships nor FABS - theAB = dcsCommon.pickRandom(allAB) - until theAB ~= excludeThisOne - return theAB - end - - if civAir.ABPickMethod == civAir.trafficAirbases.localHubs then - -- first, pick a hub name - end - - trigger.action.outText("civA: warning - unknown method <" .. civAir.ABPickMethod .. ">", 30) - return nil -end -function civAir.excludeAirbases(inList, excludeList) - if not inList then return {} end - if not excludeList then return inList end - if #excludeList < 1 then return inList end - - local theDict = {} - -- build dict - for idx, aBase in pairs(inList) do - theDict[aBase:getName()] = aBase - end - - -- now iterate through all excludes and remove them from dics - for idx, aName in pairs (excludeList) do - local allOfflimitAB = dcsCommon.getAirbasesWhoseNameContains(aName, 0) - for idx2, illegalBase in pairs (allOfflimitAB) do - theDict[illegalBase:getName()] = nil +function civAir.filterAirfields(inAll, inFilter) + local outList = {} + for idx, anItem in pairs(inAll) do + if dcsCommon.arrayContainsString(inFilter, anItem) then + -- filtered, do nothing. + else + -- not filtered + table.insert(outList, anItem) end end - -- now linearise (make array) from dict - local theArray = dcsCommon.enumerateTable(theDict) - return theArray + return outList end function civAir.getTwoAirbases() - local fAB - local sAB - -- get any two airbases on the map - if civAir.ABPickMethod == civAir.trafficAirbases.randomized then - local allAB = dcsCommon.getAirbasesWhoseNameContains("*", 0) -- all airfields, no Ships nor FABS, all coalitions - -- remove illegal source/dest airfields - allAB = civAir.excludeAirbases(allAB, civAir.excludeAirfields) + local fAB -- first airbase to depart + local sAB -- second airbase to fly to + + -- remove all currently excluded air bases from available + local filteredAB = civAir.filterAirfields(civAir.trafficCenters, civAir.excludeAirfields) + -- if none left, error + if #filteredAB < 1 then + trigger.action.outText("+++civA: too few airfields") + return nil, nil + end + + -- if one left use it twice, boring flight. + if #filteredAB < 2 then + local fabName = filteredAB[1] + fAB = dcsCommon.getAirbasesWhoseNameContains(fabName, 0) + return fAB, fAB -- same twice + end + + -- pick any two that are not the same + fAB = dcsCommon.pickRandom(filteredAB) + repeat + sAB = dcsCommon.pickRandom(filteredAB) + until fAB ~= sAB + fAB = dcsCommon.getFirstAirbaseWhoseNameContains(fAB, 0) + sAB = dcsCommon.getFirstAirbaseWhoseNameContains(sAB, 0) - fAB = dcsCommon.pickRandom(allAB) - repeat - sAB = dcsCommon.pickRandom(allAB) - until fAB ~= sAB or (#allAB < 2) - return fAB, sAB - end + return fAB, sAB - -- pick a hub, and then selct any two different airbases in the hub - if civAir.ABPickMethod == civAir.trafficAirbases.localHubs then - local hubName = dcsCommon.pickRandom(civAir.trafficCenters) - -- get the airfield that is identified by this - local theHub = dcsCommon.getFirstAirbaseWhoseNameContains(hubName, 0) -- only airfields, all coalitions - -- get all airbases that surround in range - local allAB = dcsCommon.getAirbasesInRangeOfAirbase( - theHub, -- centered on this base - true, -- include hub itself - civAir.trafficRange, -- hub size in meters - 0 -- only airfields - ) - allAB = civAir.excludeAirbases(allAB, civAir.excludeAirfields) - fAB = dcsCommon.pickRandom(allAB) - repeat - sAB = dcsCommon.pickRandom(allAB) - until fAB ~= sAB or (#allAB < 2) - return fAB, sAB - end - - -- pick two hubs: one for source, one for destination airfields, - -- then pick an airfield from each hub - if civAir.ABPickMethod == civAir.trafficAirbases.betweenHubs then - --trigger.action.outText("between", 30) - local sourceHubName = dcsCommon.pickRandom(civAir.trafficCenters) - --trigger.action.outText("picked " .. sourceHubName, 30) - local sourceHub = dcsCommon.getFirstAirbaseWhoseNameContains(sourceHubName, 0) - --trigger.action.outText("sourceHub " .. sourceHub:getName(), 30) - - local destHub - repeat destHubName = dcsCommon.pickRandom(civAir.trafficCenters) - until destHubName ~= sourceHubName or #civAir.trafficCenters < 2 - destHub = dcsCommon.getFirstAirbaseWhoseNameContains(destHubName, 0) - --trigger.action.outText("destHub " .. destHub:getName(), 30) - local allAB = dcsCommon.getAirbasesInRangeOfAirbase( - sourceHub, -- centered on this base - true, -- include hub itself - civAir.trafficRange, -- hub size in meters - 0 -- only airfields - ) - allAB = civAir.excludeAirbases(allAB, civAir.excludeAirfields) - fAB = dcsCommon.pickRandom(allAB) - allAB = dcsCommon.getAirbasesInRangeOfAirbase( - destHub, -- centered on this base - true, -- include hub itself - civAir.trafficRange, -- hub size in meters - 0 -- only airfields - ) - allAB = civAir.excludeAirbases(allAB, civAir.excludeAirfields) - sAB = dcsCommon.pickRandom(allAB) - return fAB, sAB - end - - - trigger.action.outText("civA: warning - unknown method <" .. civAir.ABPickMethod .. "> in getTwoAirbases()", 30) end function civAir.parkingIsFree(fromWP) @@ -370,6 +290,10 @@ function civAir.createNewFlight(inAirStart) civAir.flightCount = civAir.flightCount + 1 local fAB, sAB = civAir.getTwoAirbases() -- from AB + if not fAB or not sAB then + trigger.action.outText("+++civA: cannot create flight, no source or destination", 30) + return + end local name = fAB:getName() .. "-" .. sAB:getName().. "/" .. civAir.flightCount local TypeString = dcsCommon.pickRandom(civAir.aircraftTypes) @@ -500,17 +424,7 @@ function civAir.collectHubs() local pZones = cfxZones.zonesWithProperty("civAir") for k, aZone in pairs(pZones) do - local value = cfxZones.getStringFromZoneProperty(aZone, "civAir", "") - local af = dcsCommon.getClosestAirbaseTo(aZone.point, 0) -- 0 = only airfields, not farp or ships - if af then - local afName = af:getName() - if value:lower() == "exclude" then - table.insert(civAir.excludeAirfields, afName) - else - table.insert(civAir.trafficCenters, afName) - end - end - + civAir.processZone(aZone) end end @@ -536,17 +450,19 @@ function civAir.start() -- make sure there is something in trafficCenters if #civAir.trafficCenters < 1 then - trigger.action.outText("+++civTraffic: auto-populating", 30) + trigger.action.outText("+++civA: auto-populating", 30) -- simply add airfields on the map local allBases = dcsCommon.getAirbasesWhoseNameContains("*", 0) for idx, aBase in pairs(allBases) do local afName = aBase:getName() - --trigger.action.outText("+++civTraffic: adding " .. afName, 30) + table.insert(civAir.trafficCenters, afName) end end - civAir.listTrafficCenters() + if civAir.verbose then + civAir.listTrafficCenters() + end -- air-start half population if allowed if civAir.initialAirSpawns then @@ -557,7 +473,7 @@ function civAir.start() civAir.update() -- say hi! - trigger.action.outText("cf/x civTraffic v" .. civAir.version .. " started.", 30) + trigger.action.outText("cf/x civAir v" .. civAir.version .. " started.", 30) return true end @@ -568,5 +484,11 @@ end --[[-- Additional ideas - source to target method + + - border zones: ac can airstart in there and disappear in there + - callbacks for civ spawn / despawn + - add civkill callback / redCivKill blueCivKill flag bangers + - Helicopter support + - departure only, destination only + - add slot checking to see if other planes block it even though DCS claims the slot is free --]]-- \ No newline at end of file diff --git a/modules/cloneZone.lua b/modules/cloneZone.lua index 485cdf8..d192642 100644 --- a/modules/cloneZone.lua +++ b/modules/cloneZone.lua @@ -1,5 +1,5 @@ cloneZones = {} -cloneZones.version = "1.4.1" +cloneZones.version = "1.4.2" cloneZones.verbose = false cloneZones.requiredLibs = { "dcsCommon", -- always @@ -38,6 +38,8 @@ cloneZones.uniqueCounter = 9200000 -- we start group numbering here - trackWith: attribute 1.4.0 - Watchflags 1.4.1 - trackWith: accepts list of trackers + 1.4.2 - onstart delays for 0.1 s to prevent static stacking + - turn bug for statics (bug in dcsCommon, resolved) --]]-- @@ -688,7 +690,7 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone) -- now use raw data to spawn and see if it works outabox --local theCat = cfxMX.catText2ID(cat) -- will be "static" - -- move origin + trigger.action.outText("static object proccing", 30) rawData.x = rawData.x + zoneDelta.x rawData.y = rawData.y + zoneDelta.z -- !!! @@ -704,7 +706,7 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone) ctry = cloneZones.resolveOwnership(spawnZone, ctry) -- handle linkUnit if provided - if rawData.linkUnit then + if false and rawData.linkUnit then --trigger.action.outText("has link to " .. rawData.linkUnit, 30) local lU = cloneZones.resolveStaticLinkUnit(rawData.linkUnit) --trigger.action.outText("resolved to " .. lU, 30) @@ -869,14 +871,6 @@ function cloneZones.hasLiveUnits(theZone) return false end --- old code, deprecated ---[[-- -function cloneZones.pollFlag(flagNum, method) - -- we currently ignore method - local num = trigger.misc.getUserFlag(flagNum) - trigger.action.setUserFlag(flagNum, num+1) -end ---]]-- -- -- UPDATE -- @@ -903,20 +897,6 @@ function cloneZones.update() end cloneZones.spawnWithCloner(aZone) end - -- old code - --[[-- - if aZone.spawnFlag then - local currTriggerVal = cfxZones.getFlagValue(aZone.spawnFlag, aZone) -- trigger.misc.getUserFlag(aZone.spawnFlag) - if currTriggerVal ~= aZone.lastSpawnValue - then - if cloneZones.verbose then - trigger.action.outText("+++clnZ: spawn triggered for <" .. aZone.name .. ">", 30) - end - cloneZones.spawnWithCloner(aZone) - aZone.lastSpawnValue = currTriggerVal - end - end - --]]-- -- empty handling local isEmpty = cloneZones.countLiveUnits(aZone) < 1 and aZone.hasClones @@ -999,8 +979,12 @@ function cloneZones.start() cloneZones.addCloneZone(aZone) -- remember it so we can smoke it end - -- run through onStart - cloneZones.onStart() + -- run through onStart, but leave at least a few + -- cycles to go through object removal so statics + -- can spawn on ground. onStart is being deprecated, the + -- raiseFlag module covers this since the first time + -- raiseFlag is run is t0 + 0.5s + timer.scheduleFunction(cloneZones.onStart, {}, timer.getTime() + 0.1) -- start update cloneZones.update() @@ -1016,13 +1000,6 @@ if not cloneZones.start() then cloneZones = nil end ---[[-- callback testing -czcb = {} -function czcb.callback(theZone, reason, args) - trigger.action.outText("clone CB: " .. theZone.name .. " with " .. reason, 30) -end -cloneZones.addCallback(czcb.callback) ---]]-- --[[-- to resolve tasks @@ -1030,4 +1007,5 @@ cloneZones.addCallback(czcb.callback) - AFAC - FAC Assign group - set freq for unit + - embark / disembark --]]-- \ No newline at end of file diff --git a/modules/dcsCommon.lua b/modules/dcsCommon.lua index 9191c65..5218f08 100644 --- a/modules/dcsCommon.lua +++ b/modules/dcsCommon.lua @@ -1,5 +1,5 @@ dcsCommon = {} -dcsCommon.version = "2.5.9" +dcsCommon.version = "2.6.1" --[[-- VERSION HISTORY 2.2.6 - compassPositionOfARelativeToB - clockPositionOfARelativeToB @@ -70,6 +70,7 @@ dcsCommon.version = "2.5.9" 2.5.8 - string2GroupCat() 2.5.9 - string2ObjectCat() 2.6.0 - unified uuid, removed uuIdent + 2.6.1 - removed bug in rotateUnitData: cy --> cz param passing --]]-- @@ -1483,7 +1484,7 @@ dcsCommon.version = "2.5.9" return px, py end - function dcsCommon.rotateUnitData(theUnit, degrees, cx, cy) + function dcsCommon.rotateUnitData(theUnit, degrees, cx, cz) if not cx then cx = 0 end if not cz then cz = 0 end local cy = cz diff --git a/modules/delayFlags.lua b/modules/delayFlags.lua index c1b7360..8bc5224 100644 --- a/modules/delayFlags.lua +++ b/modules/delayFlags.lua @@ -1,5 +1,5 @@ delayFlag = {} -delayFlag.version = "1.2.1" +delayFlag.version = "1.2.2" delayFlag.verbose = false delayFlag.requiredLibs = { "dcsCommon", -- always @@ -31,6 +31,9 @@ delayFlag.flags = {} 1.2.0 - Watchflags 1.2.1 - method goes to dlyMethod - delay done is correctly inited + 1.2.2 - delayMethod defaults to inc + - zone-local verbosity + - code clean-up --]]-- @@ -58,7 +61,7 @@ end function delayFlag.createTimerWithZone(theZone) -- delay theZone.delayMin, theZone.delayMax = cfxZones.getPositiveRangeFromZoneProperty(theZone, "timeDelay", 1) -- same as zone signature - if delayFlag.verbose then + if delayFlag.verbose or theZone.verbose then trigger.action.outText("+++dlyF: time delay is <" .. theZone.delayMin .. ", " .. theZone.delayMax .. "> seconds", 30) end @@ -88,10 +91,10 @@ function delayFlag.createTimerWithZone(theZone) end - theZone.delayMethod = cfxZones.getStringFromZoneProperty(theZone, "method", "flip") + theZone.delayMethod = cfxZones.getStringFromZoneProperty(theZone, "method", "inc") if cfxZones.hasProperty(theZone, "delayMethod") then - theZone.delayMethod = cfxZones.getStringFromZoneProperty(theZone, "delayMethod", "flip") + theZone.delayMethod = cfxZones.getStringFromZoneProperty(theZone, "delayMethod", "inc") end -- out flag @@ -108,9 +111,6 @@ function delayFlag.createTimerWithZone(theZone) theZone.lastTriggerStopValue = cfxZones.getFlagValue(theZone.triggerStopDelay, theZone) end - - - -- init theZone.delayRunning = false theZone.timeLimit = -1 @@ -139,7 +139,7 @@ function delayFlag.startDelay(theZone) if delay > theZone.delayMax then delay = theZone.delayMax end if delay < 1 then delay = 1 end - if delayFlag.verbose then + if delayFlag.verbose or theZone.verbose then trigger.action.outText("+++dlyF: delay " .. theZone.name .. " range " .. delayMin .. "-" .. delayMax .. ": selected " .. delay, 30) end end @@ -157,25 +157,14 @@ function delayFlag.update() -- see if we need to stop if cfxZones.testZoneFlag(aZone, aZone.triggerStopDelay, aZone.delayTriggerMethod, "lastTriggerStopValue") then aZone.delayRunning = false -- simply stop. - if delayFlag.verbose then + if delayFlag.verbose or aZone.verbose then trigger.action.outText("+++dlyF: stopped delay " .. aZone.name, 30) end end - -- old code - --[[-- - if aZone.triggerStopDelay then - local currTriggerVal = cfxZones.getFlagValue(aZone.triggerStopDelay, aZone) - if currTriggerVal ~= lastTriggerStopValue then - aZone.delayRunning = false -- simply stop. - if delayFlag.verbose then - trigger.action.outText("+++dlyF: stopped delay " .. aZone.name, 30) - end - end - end - --]]-- + if cfxZones.testZoneFlag(aZone, aZone.triggerDelayFlag, aZone.delayTriggerMethod, "lastDelayTriggerValue") then - if delayFlag.verbose then + if delayFlag.verbose or aZone.verbose then if aZone.delayRunning then trigger.action.outText("+++dlyF: re-starting timer " .. aZone.name, 30) else @@ -184,25 +173,7 @@ function delayFlag.update() end delayFlag.startDelay(aZone) -- we restart even if running end - -- old code - --[[-- - -- make sure to re-start before reading time limit - if aZone.triggerDelayFlag then - local currTriggerVal = cfxZones.getFlagValue(aZone.triggerDelayFlag, aZone) -- trigger.misc.getUserFlag(aZone.triggerDelayFlag) - if currTriggerVal ~= aZone.lastDelayTriggerValue - then - if delayFlag.verbose then - if aZone.delayRunning then - trigger.action.outText("+++dlyF: re-starting timer " .. aZone.name, 30) - else - trigger.action.outText("+++dlyF: init timer for " .. aZone.name, 30) - end - end - delayFlag.startDelay(aZone) -- we restart even if running - aZone.lastDelayTriggerValue = currTriggerVal - end - end - --]]-- + if aZone.delayRunning then -- check expiry @@ -240,18 +211,7 @@ function delayFlag.readConfigZone() end end ---[[-- -function delayFlag.onStart() - for idx, theZone in pairs(delayFlag.flags) do - if theZone.onStart then - if delayFlag.verbose then - trigger.action.outText("+++dlyF: onStart for <"..theZone.name .. ">", 30) - end - delayFlag.startDelay(theZone) - end - end -end ---]]-- + function delayFlag.start() -- lib check diff --git a/modules/radioTrigger.lua b/modules/radioTrigger.lua index 85c0112..9914014 100644 --- a/modules/radioTrigger.lua +++ b/modules/radioTrigger.lua @@ -13,6 +13,7 @@ radioTrigger.radioTriggers = {} 1.0.0 - initial version --]]-- + function radioTrigger.addRadioTrigger(theZone) table.insert(radioTrigger.radioTriggers, theZone) end diff --git a/modules/unitZone.lua b/modules/unitZone.lua index 62c4986..2baabd1 100644 --- a/modules/unitZone.lua +++ b/modules/unitZone.lua @@ -1,5 +1,5 @@ unitZone={} -unitZone.version = "1.2.0" +unitZone.version = "1.2.1" unitZone.verbose = false unitZone.ups = 1 unitZone.requiredLibs = { @@ -12,6 +12,7 @@ unitZone.requiredLibs = { 1.1.0 - DML flag integration - method/uzMethod 1.2.0 - uzOn?, uzOff?, triggerMethod + 1.2.1 - uzDirect --]]-- @@ -96,6 +97,11 @@ function unitZone.createUnitZone(theZone) end end + -- uzDirect + if cfxZones.hasProperty(theZone, "uzDirect") then + theZone.uzDirect = cfxZones.getStringFromZoneProperty(theZone, "uzDirect", "*") + end + -- on/off flags theZone.uzPaused = false -- we are turned on theZone.triggerOnFlag = cfxZones.getStringFromZoneProperty(theZone, "uzOn?", "*") @@ -260,6 +266,15 @@ function unitZone.update() unitZone.bangState(aZone, newState) aZone.lastStatus = newState end + + -- output direct state + if aZone.uzDirect then + if newState then + cfxZones.setFlagValueMult(aZone.uzDirect, 1, aZone) + else + cfxZones.setFlagValueMult(aZone.uzDirect, 0, aZone) + end + end end end end diff --git a/modules/xFlags.lua b/modules/xFlags.lua index 02d7c8e..29508b7 100644 --- a/modules/xFlags.lua +++ b/modules/xFlags.lua @@ -1,6 +1,7 @@ xFlags = {} xFlags.version = "1.2.1" xFlags.verbose = false +xFlags.hiVerbose = false xFlags.ups = 1 -- overwritten in get config when configZone is present xFlags.requiredLibs = { "dcsCommon", -- always @@ -22,6 +23,9 @@ xFlags.requiredLibs = { - xSuccess optimizations - inc, dec, quoted flags - matchNum can carry flag + 1.2.2 - on/off/suspend commands + - hiVerbose option + - corrected bug in reset checksum --]]-- xFlags.xFlagZones = {} @@ -33,6 +37,7 @@ end -- create xFlag -- function xFlags.reset(theZone) + theZone.flagChecksum = "" -- reset checksum for i = 1, #theZone.flagNames do -- since the checksum is order dependent, -- we must preserve the order of the array @@ -116,6 +121,18 @@ function xFlags.createXFlagsWithZone(theZone) theZone.xOneShot = cfxZones.getBoolFromZoneProperty(theZone, "oneShot", true) + -- on / off commands + -- on/off flags + theZone.xSuspended = cfxZones.getBoolFromZoneProperty(theZone, "xSuspended", false) -- we are turned on + if theZone.xSuspended and (xFlags.verbose or theZone.verbose) then + trigger.action.outText("+++xFlg: <" .. theZone.name .. "> starts suspended", 30) + end + + theZone.xtriggerOnFlag = cfxZones.getStringFromZoneProperty(theZone, "xOn?", "*") + theZone.xlastTriggerOnValue = cfxZones.getFlagValue(theZone.xtriggerOnFlag, theZone) + + theZone.xtriggerOffFlag = cfxZones.getStringFromZoneProperty(theZone, "xOff?", "*") + theZone.xlastTriggerOffValue = cfxZones.getFlagValue(theZone.xtriggerOffFlag, theZone) end function xFlags.evaluateNumOrFlag(theAttribute, theZone) @@ -282,30 +299,41 @@ function xFlags.evaluateZone(theZone) -- supported any/or, all/and, moreThan, atLeast, exactly local op = theZone.inspect local evalResult = false - if (op == "or" or op == "any" or op == "some") and hits > 0 then - evalResult = true - elseif (op == "and" or op == "all") and hits == #theZone.flagNames then - evalResult = true - elseif (op == "morethan" or op == "more than") and hits > matchNum then - evalResult = true - elseif (op == "atleast" or op == "at least") and hits >= matchNum then - evalResult = true - elseif op == "exactly" and hits == matchNum then - evalResult = true - elseif (op == "none" or op == "nor") and hits == 0 then - evalResult = true - elseif (op == "not all" or op == "notall" or op == "nand") and hits < #theZone.flagNames then - evalResult = true - elseif (op == "most") and hits > (#theZone.flagNames / 2) then - evalResult = true - elseif (op == "half" or op == "at least half" or op == "half or more") and hits >= (#theZone.flagNames / 2) then - -- warning: 'half' means really 'at least half" - evalResult = true + if (op == "or" or op == "any" or op == "some") then + if hits > 0 then evalResult = true end + elseif (op == "and" or op == "all") then + if hits == #theZone.flagNames then evalResult = true end + + elseif (op == "morethan" or op == "more than") then + if hits > matchNum then evalResult = true end + + elseif (op == "atleast" or op == "at least") then + if hits >= matchNum then evalResult = true end + + elseif op == "exactly" then + if hits == matchNum then evalResult = true end + + elseif (op == "none" or op == "nor") then + if hits == 0 then evalResult = true end + + elseif (op == "not all" or op == "notall" or op == "nand") then + if hits < #theZone.flagNames then evalResult = true end + + elseif (op == "most") then + if hits > (#theZone.flagNames / 2) then evalResult = true end + + elseif (op == "half" or op == "at least half" or op == "half or more") then + if hits >= (#theZone.flagNames / 2) then + -- warning: 'half' means really 'at least half" + evalResult = true + end + else + trigger.action.outText("+++xFlg: WARNING: <" .. theZone.name .. "> has unknown requirement: <" .. op .. ">", 30) end - -- add "most" to more than 50% of flagnum + -- add "most" to more than 50% of flagnum - -- now check if changed and if result true + -- now check if changed and if result true if checkSum ~= theZone.flagChecksum then if xFlags.verbose or theZone.verbose then trigger.action.outText("+++xFlag: change detected for " .. theZone.name .. ": " .. theZone.flagChecksum .. "-->" ..checkSum, 30) @@ -319,7 +347,7 @@ function xFlags.evaluateZone(theZone) end theZone.flagChecksum = checkSum else - if xFlags.verbose or theZone.verbose then + if xFlags.hiVerbose and (xFlags.verbose or theZone.verbose) then trigger.action.outText("+++xFlag: no change, checksum is |" .. checkSum .. "| for <" .. theZone.name .. ">", 10) end end @@ -337,7 +365,7 @@ function xFlags.evaluateZone(theZone) -- now see if we bang the output according to method if evalResult then if xFlags.verbose or theZone.verbose then - trigger.action.outText("+++xFlag: success bang! on <" .. theZone.xSuccess .. "> for <" .. theZone.name .. ">", 30) + trigger.action.outText("+++xFlag: success bang! on <" .. theZone.xSuccess .. "> for <" .. theZone.name .. "> with method <" .. theZone.xMethod .. ">", 30) end cfxZones.pollFlag(theZone.xSuccess, theZone.xMethod, theZone) theZone.xHasFired = true @@ -351,8 +379,25 @@ function xFlags.update() timer.scheduleFunction(xFlags.update, {}, timer.getTime() + 1/xFlags.ups) for idx, theZone in pairs (xFlags.xFlagZones) do + -- see if we should suspend + if cfxZones.testZoneFlag(theZone, theZone.xtriggerOnFlag, "change", "xlastTriggerOnValue") then + if xFlags.verbose or theZone.verbose then + trigger.action.outText("+++xFlg: enabling " .. theZone.name, 30) + end + theZone.xSuspended = false + end + + if cfxZones.testZoneFlag(theZone, theZone.xtriggerOffFlag, "change", "xlastTriggerOffValue") then + if xFlags.verbose or theZone.verbose then + trigger.action.outText("+++xFlg: DISabling " .. theZone.name, 30) + end + theZone.xSuspended = true + end + -- see if they should fire - xFlags.evaluateZone(theZone) + if not theZone.xSuspended then + xFlags.evaluateZone(theZone) + end -- see if they should reset if theZone.xReset then diff --git a/tutorial & demo missions/demo - Air Caucasus II.miz b/tutorial & demo missions/demo - Air Caucasus II.miz new file mode 100644 index 0000000..f3c3237 Binary files /dev/null and b/tutorial & demo missions/demo - Air Caucasus II.miz differ diff --git a/tutorial & demo missions/demo - Virgin (Civ) Air.miz b/tutorial & demo missions/demo - Virgin (Civ) Air.miz new file mode 100644 index 0000000..b3b70ae Binary files /dev/null and b/tutorial & demo missions/demo - Virgin (Civ) Air.miz differ