diff --git a/Doc/DML Documentation.pdf b/Doc/DML Documentation.pdf index 4b5aeec..cf5ba87 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 7ec5e87..37f6728 100644 Binary files a/Doc/DML Quick Reference.pdf and b/Doc/DML Quick Reference.pdf differ diff --git a/modules/LZ.lua b/modules/LZ.lua index 5561f73..25ea81d 100644 --- a/modules/LZ.lua +++ b/modules/LZ.lua @@ -1,5 +1,5 @@ LZ = {} -LZ.version = "0.0.0" +LZ.version = "1.0.0" LZ.verbose = false LZ.ups = 1 LZ.requiredLibs = { @@ -9,10 +9,12 @@ LZ.requiredLibs = { LZ.LZs = {} --[[-- + LZ - module to generate flag events when a unit lands to takes off inside + the zone. + Version History 1.0.0 - initial version - --]]-- function LZ.addLZ(theZone) @@ -27,22 +29,70 @@ function LZ.getLZByName(aName) trigger.action.outText("+++LZ: no LZ with name <" .. aName ..">", 30) end - return nil end -- -- read zone -- function LZ.createLZWithZone(theZone) - -- read main trigger - theZone.triggerLZFlag = cfxZones.getStringFromZoneProperty(theZone, "lz!", "*") - - -- TriggerMethod: common and specific synonym - theZone.lzMethod = cfxZones.getStringFromZoneProperty(theZone, "method", "inc") - - if cfxZones.hasProperty(theZone, "lzTriggerMethod") then - theZone.lzMethod = cfxZones.getStringFromZoneProperty(theZone, "lzMethod", "change") + if cfxZones.hasProperty(theZone, "landed!") then + theZone.lzLanded = cfxZones.getStringFromZoneProperty(theZone, "landed!", "*") end + + if cfxZones.hasProperty(theZone, "departed!") then + theZone.lzDeparted = cfxZones.getStringFromZoneProperty(theZone, "departed!", "*") + end + + -- who to look for + theZone.coalition = cfxZones.getCoalitionFromZoneProperty(theZone, "coalition", 0) + -- units / groups / types + if cfxZones.hasProperty(theZone, "group") then + theZone.lzGroups = cfxZones.getStringFromZoneProperty(theZone, "group", "") + theZone.lzGroups = dcsCommon.string2Array(theZone.lzGroups, ",", true) + elseif cfxZones.hasProperty(theZone, "groups") then + theZone.lzGroups = cfxZones.getStringFromZoneProperty(theZone, "groups", "") + theZone.lzGroups = dcsCommon.string2Array(theZone.lzGroups, ",", true) + elseif cfxZones.hasProperty(theZone, "type") then + theZone.lzTypes = cfxZones.getStringFromZoneProperty(theZone, "type", "ALL") + theZone.lzTypes = dcsCommon.string2Array(theZone.lzTypes, ",", true) + elseif cfxZones.hasProperty(theZone, "types") then + theZone.lzTypes = cfxZones.getStringFromZoneProperty(theZone, "types", "ALL") + theZone.lzTypes = dcsCommon.string2Array(theZone.lzTypes, ",", true) + elseif cfxZones.hasProperty(theZone, "unit") then + theZone.lzUnits = cfxZones.getStringFromZoneProperty(theZone, "unit", "none") + theZone.lzUnits = dcsCommon.string2Array(theZone.lzUnits, ",", true) + elseif cfxZones.hasProperty(theZone, "units") then + theZone.lzUnits = cfxZones.getStringFromZoneProperty(theZone, "units", "none") + theZone.lzUnits = dcsCommon.string2Array(theZone.lzUnits, ",", true) + end + + theZone.lzPlayerOnly = cfxZones.getBoolFromZoneProperty(theZone, "playerOnly", false) + + -- output method + theZone.lzMethod = cfxZones.getStringFromZoneProperty(theZone, "method", "inc") + if cfxZones.hasProperty(theZone, "outputMethod") then + theZone.lzMethod = cfxZones.getStringFromZoneProperty(theZone, "outputMethod", "inc") + end + + -- trigger method + theZone.lzTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "lzTriggerMethod", "change") + if cfxZones.hasProperty(theZone, "triggerMethod") then + theZone.lzTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "triggerMethod", "change") + end + + + -- pause / unpause + theZone.lzIsPaused = cfxZones.getBoolFromZoneProperty(theZone, "isPaused", false) + + if cfxZones.hasProperty(theZone, "pause?") then + theZone.lzPause = cfxZones.getStringFromZoneProperty(theZone, "pause?", "*") + theZone.lzLastPause = cfxZones.getFlagValue(theZone.lzPause, theZone) + end + + if cfxZones.hasProperty(theZone, "continue?") then + theZone.lzContinue = cfxZones.getStringFromZoneProperty(theZone, "continue?", "*") + theZone.lzLastContinue = cfxZones.getFlagValue(theZone.lzContinue, theZone) + end if LZ.verbose or theZone.verbose then trigger.action.outText("+++LZ: new LZ <".. theZone.name ..">", 30) @@ -50,45 +100,196 @@ function LZ.createLZWithZone(theZone) end --- --- MAIN ACTION --- -function LZ.processUpdate(theZone) +function LZ.nameMatchForArray(theName, theArray, wildcard) + theName = dcsCommon.trim(theName) + if not theName then return false end + if not theArray then return false end + theName = string.upper(theName) -- case insensitive + -- trigger.action.outText("enter name match with <" .. theName .. "> look for match in <" .. dcsCommon.array2string(theArray) .. "> and wc <" .. wildcard .. ">", 30) + for idx, entry in pairs(theArray) do + + if wildcard and dcsCommon.stringEndsWith(entry, wildcard) then + entry = dcsCommon.removeEnding(entry, wildcard) + -- trigger.action.outText("trying to WC-match <" .. theName .. "> with <" .. entry .. ">", 30) + if dcsCommon.stringStartsWith(theName, entry) then + -- theName "hi there" matches wildcarded entry "hi*" + return true + end + else + -- trigger.action.outText("trying to simple-match <" .. theName .. "> with <" .. entry .. ">", 30) + if theName == entry then + return true + end + end + end +-- trigger.action.outText ("no match for <" .. theName .. ">", 30) + return false end +-- +-- Misc Processing +-- +function LZ.unitIsInterestingForZone(theUnit, theZone) + --trigger.action.outText("enter isInterestingB4pause for <" .. theUnit:getName() .. ">", 40) + + -- see if zone is interested in this unit. + if theZone.isPaused then + return false + end +-- trigger.action.outText("enter isinteresting for <" .. theUnit:getName() .. ">", 40) + if theZone.lzPlayerOnly then + if not dcsCommon.isPlayerUnit(theUnit) then + if theZone.verbose or LZ.verbose then + trigger.action.outText("+++LZ: unit <" .. theUnit:getName() .. "> arriving/departing <" .. theZone.name .. "> is not a player unit", 30) + end + return false + else + -- trigger.action.outText("player match!", 30) + end + end + + if theZone.coalition > 0 then + local theGroup = theUnit:getGroup() + local coa = theGroup:getCoalition() + if coa ~= theZone.coalition then + if theZone.verbose or LZ.verbose then + trigger.action.outText("+++LZ: unit <" .. theUnit:getName() .. "> arriving/departing <" .. theZone.name .. "> does not match coa <" .. theZone.coalition .. ">", 30) + end + return false + end + end + -- if we get here, we are filtered for coa and player + if theZone.lzUnits then + local theName = theUnit:getName() + return LZ.nameMatchForArray(theName, theZone.lzUnits, "*") + + elseif theZone.lzGroups then + local theGroup = theUnit:getGroup() + local theName = theGroup:getName() + return LZ.nameMatchForArray(theName, theZone.lzGroups, "*") + + elseif theZone.lzTypes then + local theType = theUnit:getTypeName() + local theGroup = theUnit:getGroup() + local cat = theGroup:getCategory() -- can't trust unit:getCategory + local coa = theGroup:getCoalition() + for idx, aType in pairs (theZone.lzTypes) do + + if aType == "ANY" or aType == "ALL" then + return true + + elseif aType == "HELO" or aType == "HELICOPTER" or aType == "HELICOPTERS" or aType == "HELOS" then + if cat == 1 then + return true + end + elseif aType == "PLANE" or aType == "PLANES" then + if cat == 0 then + return true + end + else + if theType == aType then + return true + end + end + end -- for all types + + return false -- not a single match + else + -- we can return true since player and coa mismatch + -- have already been filtered +--[[-- -- neither type, unit, nor group + local theGroup = theUnit:getGroup() + local coa = theGroup:getCoalition() + -- +--]]-- + return true -- theZone.coalition == coa end + end + + trigger.action.outText("+++LZ: unknown attribute check for <" .. theZone.name .. ">", 30) + return false +end + + -- -- Event Handling -- function LZ:onEvent(event) - -- only interested in S_EVENT_BASE_CAPTURED events - if event.id ~= world.event.S_EVENT_BASE_CAPTURED then + -- make sure we have an initiator + if not event.initiator then return end + + -- only interested in S_EVENT_TAKEOFF and events + if event.id ~= world.event.S_EVENT_TAKEOFF and + event.id ~= world.event.S_EVENT_LAND then return end - + + --if LZ.verbose or true then + -- trigger.action.outText("+++LZ: on event proccing", 30) + --end + + local theUnit = event.initiator + if not Unit.isExist(theUnit) then return end + local p = theUnit:getPoint() + + --if LZ.verbose or true then + -- trigger.action.outText("+++LZ: before iterating zones", 30) + --end + for idx, aZone in pairs(LZ.LZs) do - -- check if landed inside and of correct type, colition, name whatever + -- see if inside the zone + local inZone, percent, dist = cfxZones.pointInZone(p, aZone) + if inZone then + -- see if this unit interests us at all + if LZ.unitIsInterestingForZone(theUnit, aZone) then + -- interesting unit in zone triggered the event + if aZone.lzDeparted and event.id == world.event.S_EVENT_TAKEOFF then + if LZ.verbose or aZone.verbose then + trigger.action.outText("+++LZ: detected departure from <" .. aZone.name .. ">", 30) + end + cfxZones.pollFlag(aZone.lzDeparted, aZone.lzMethod, aZone) + end + + if aZone.lzLanded and event.id == world.event.S_EVENT_LAND then + if LZ.verbose or aZone.verbose then + trigger.action.outText("+++LZ: detected landing in <" .. aZone.name .. ">", 30) + end + cfxZones.pollFlag(aZone.lzLanded, aZone.lzMethod, aZone) + end + end -- if interesting + else + if LZ.verbose or true then + -- trigger.action.outText("+++LZ: unit <" .. theUnit:getName() .. "> not in zone <" .. aZone.name .. ">", 30) + end - end + end -- if in zone + end -- end for end -- -- Update -- - function LZ.update() -- call me in a second to poll triggers timer.scheduleFunction(LZ.update, {}, timer.getTime() + 1/LZ.ups) for idx, aZone in pairs(LZ.LZs) do - -- see if we are triggered - if cfxZones.testZoneFlag(aZone, aZone.triggerLZFlag, aZone.LZTriggerMethod, "lastTriggerLZValue") then + -- see if we are being paused or unpaused + if cfxZones.testZoneFlag(aZone, aZone.lzPause, aZone.LZTriggerMethod, "lzLastPause") then if LZ.verbose or theZone.verbose then - trigger.action.outText("+++LZ: triggered on main? for <".. aZone.name ..">", 30) + trigger.action.outText("+++LZ: triggered pause? for <".. aZone.name ..">", 30) end - LZ.processUpdate(aZone) + aZone.isPaused = true + end + + if cfxZones.testZoneFlag(aZone, aZone.lzContinue, aZone.LZTriggerMethod, "lzLastContinue") then + if LZ.verbose or theZone.verbose then + trigger.action.outText("+++LZ: triggered continue? for <".. aZone.name ..">", 30) + end + aZone.isPaused = false end end + end -- @@ -97,12 +298,13 @@ end function LZ.readConfigZone() local theZone = cfxZones.getZoneByName("LZConfig") if not theZone then + theZone = cfxZones.createSimpleZone(LZConfig) if LZ.verbose then trigger.action.outText("+++LZ: NO config zone!", 30) end - return end + LZ.lzCooldown = cfxZones.getNumberFromZoneProperty(theZone, "cooldown", 20) LZ.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false) if LZ.verbose then @@ -125,12 +327,15 @@ function LZ.start() -- process LZ Zones -- old style - local attrZones = cfxZones.getZonesWithAttributeNamed("lz!") + local attrZones = cfxZones.getZonesWithAttributeNamed("lz") for k, aZone in pairs(attrZones) do LZ.createLZWithZone(aZone) -- process attributes LZ.addLZ(aZone) -- add to list end + -- connect event handler + world.addEventHandler(LZ) + -- start update LZ.update() diff --git a/modules/cloneZone.lua b/modules/cloneZone.lua index 4b61c2d..ccb3020 100644 --- a/modules/cloneZone.lua +++ b/modules/cloneZone.lua @@ -1,5 +1,5 @@ cloneZones = {} -cloneZones.version = "1.5.4" +cloneZones.version = "1.5.5" cloneZones.verbose = false cloneZones.requiredLibs = { "dcsCommon", -- always @@ -61,7 +61,7 @@ cloneZones.allCObjects = {} -- all clones objects 1.5.2 - fixed bug in trackWith: referencing wrong cloner 1.5.3 - centerOnly/wholeGroups attribute for rndLoc, rndHeading and onRoad 1.5.4 - parking for aircraft processing when cloning from template - + 1.5.5 - removed some verbosity --]]-- @@ -1053,7 +1053,7 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone) end function cloneZones.spawnWithCloner(theZone) - trigger.action.outText("+++clnZ: enter spawnWithCloner for <" .. theZone.name .. ">", 30) +-- trigger.action.outText("+++clnZ: enter spawnWithCloner for <" .. theZone.name .. ">", 30) if not theZone then trigger.action.outText("+++clnZ: nil zone on spawnWithCloner", 30) return @@ -1232,7 +1232,7 @@ end function cloneZones.doOnStart() for idx, theZone in pairs(cloneZones.cloners) do if theZone.onStart then - trigger.action.outText("+++clnZ: onStart true for <" .. theZone.name .. ">", 30) +-- trigger.action.outText("+++clnZ: onStart true for <" .. theZone.name .. ">", 30) if theZone.isStarted then if cloneZones.verbose or theZone.verbose then trigger.action.outText("+++clnz: onStart pre-empted for <" .. theZone.name .. "> by persistence", 30) diff --git a/modules/dcsCommon.lua b/modules/dcsCommon.lua index 878a7ca..9a3bb0e 100644 --- a/modules/dcsCommon.lua +++ b/modules/dcsCommon.lua @@ -1,5 +1,5 @@ dcsCommon = {} -dcsCommon.version = "2.7.2" +dcsCommon.version = "2.7.4" --[[-- VERSION HISTORY 2.2.6 - compassPositionOfARelativeToB - clockPositionOfARelativeToB @@ -98,6 +98,9 @@ dcsCommon.version = "2.7.2" new decFlag() nil trap in stringStartsWith() new getClosestFreeSlotForCatInAirbaseTo() + 2.7.3 - new string2Array() + - additional guard for isPlayerUnit + 2.7.4 - new array2string() --]]-- @@ -1853,6 +1856,33 @@ end return trimmedArray end + function dcsCommon.string2Array(inString, deli, uCase) + if not inString then return {} end + if not deli then return {} end + if not uCase then uCase = false end + if uCase then inString = string.upper(inString) end + inString = dcsCommon.trim(inString) + if dcsCommon.containsString(inString, deli) then + local a = dcsCommon.splitString(inString, deli) + a = dcsCommon.trimArray(a) + return a + else + return {inString} + end + end + + function dcsCommon.array2string(inArray, deli) + if not deli then deli = "," end + if type(inArray) ~= "table" then return "" end + local s = "" + local count = 0 + for idx, ele in pairs(inArray) do + if count > 0 then s = s .. deli .. " " end + s = s .. ele + end + return s + end + function dcsCommon.stripLF(theString) return theString:gsub("[\r\n]", "") end @@ -2320,6 +2350,7 @@ end function dcsCommon.isPlayerUnit(theUnit) -- new patch. simply check if getPlayerName returns something if not theUnit then return false end + if not Unit.isExist(theUnit) then return end if not theUnit.getPlayerName then return false end -- map/static object local pName = theUnit:getPlayerName() if pName then return true end diff --git a/modules/fireFX.lua b/modules/fireFX.lua new file mode 100644 index 0000000..a79baa7 --- /dev/null +++ b/modules/fireFX.lua @@ -0,0 +1,186 @@ +fireFX = {} +fireFX.version = "1.0.0" +fireFX.verbose = false +fireFX.ups = 1 +fireFX.requiredLibs = { + "dcsCommon", -- always + "cfxZones", -- Zones, of course +} +fireFX.fx = {} + +function fireFX.addFX(theZone) + table.insert(fireFX.fx, theZone) +end + +function fireFX.getFXByName(aName) + for idx, aZone in pairs(fireFX.fx) do + if aName == aZone.name then return aZone end + end + if fireFX.verbose then + trigger.action.outText("+++ffx: no fire FX with name <" .. aName ..">", 30) + end + +end + +-- +-- read zone +-- +function fireFX.createFXWithZone(theZone) + -- decode size and fire + local theSize = cfxZones.getStringFromZoneProperty(theZone, "fireFX", "none") + theSize = dcsCommon.trim(theSize) + theSize = string.upper(theSize) + local fxCode = 1 + if theSize == "S" or theSize == "SMALL" then fxCode = 1 end + if theSize == "M" or theSize == "MEDIUM" then fxCode = 2 end + if theSize == "L" or theSize == "LARGE" then fxCode = 3 end + if theSize == "H" or theSize == "HUGE" then fxCode = 4 end + if theSize == "XL" then fxCode = 4 end + + local theFire = cfxZones.getBoolFromZoneProperty(theZone, "flames", true) + + + if theFire then + -- code stays as it is + else + -- smoke only + fxCode = fxCode + 4 + end + theZone.fxCode = fxCode + if theZone.verbose or fireFX.verbose then + trigger.action.outText("+++ffx: new FX with code = <" .. fxCode .. ">", 30) + end + + theZone.density = cfxZones.getNumberFromZoneProperty(theZone, "density", 0.5) + + if cfxZones.hasProperty(theZone, "start?") then + theZone.fxStart = cfxZones.getStringFromZoneProperty(theZone, "start?", "*") + theZone.fxLastStart = cfxZones.getFlagValue(theZone.fxStart, theZone) + end + + if cfxZones.hasProperty(theZone, "stop?") then + theZone.fxStop = cfxZones.getStringFromZoneProperty(theZone, "stop?", "*") + theZone.fxLastStop = cfxZones.getFlagValue(theZone.fxStop, theZone) + end + + theZone.fxOnStart = cfxZones.getBoolFromZoneProperty(theZone, "onStart", false) + theZone.burning = false + + if not theZone.fxOnStart and not theZone.fxStart then + trigger.action.outText("+++ffx: WARNING - fireFX Zone <" .. theZone.name .. "> can't be started, neither onStart nor 'start?' defined", 30) + end + + -- output method (not needed) + + -- trigger method + theZone.fxTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "fxTriggerMethod", "change") + if cfxZones.hasProperty(theZone, "triggerMethod") then + theZone.fxTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "triggerMethod", "change") + end + + if fireFX.verbose or theZone.verbose then + trigger.action.outText("+++ffx: new FX <".. theZone.name ..">", 30) + end +end + +-- +-- Update +-- +function fireFX.startTheFire(theZone) + if not theZone.burning then + local p = cfxZones.getPoint(theZone) + p.y = land.getHeight({x = p.x, y = p.z}) + local preset = theZone.fxCode + local density = theZone.density + trigger.action.effectSmokeBig(p, preset, density, theZone.name) + theZone.burning = true + end +end + +function fireFX.extinguishFire(theZone) + if theZone.burning then + trigger.action.effectSmokeStop(theZone.name) + theZone.burning = false + end +end + +function fireFX.update() + -- call me in a second to poll triggers + timer.scheduleFunction(fireFX.update, {}, timer.getTime() + 1/fireFX.ups) + + for idx, aZone in pairs(fireFX.fx) do + -- see if we are being paused or unpaused + if cfxZones.testZoneFlag(aZone, aZone.fxStop, aZone.fxTriggerMethod, "fxLastStop") then + if fireFX.verbose or aZone.verbose then + trigger.action.outText("+++ffx: triggered 'stop?' for <".. aZone.name ..">", 30) + end + fireFX.extinguishFire(aZone) + end + + if cfxZones.testZoneFlag(aZone, aZone.fxStart, aZone.fxTriggerMethod, "fxLastStart") then + if fireFX.verbose or aZone.verbose then + trigger.action.outText("+++ffx: triggered 'start?' for <".. aZone.name ..">", 30) + end + fireFX.startTheFire(aZone) + end + end + +end + +-- +-- Config & Start +-- +function fireFX.readConfigZone() + local theZone = cfxZones.getZoneByName("fireFXConfig") + if not theZone then + theZone = cfxZones.createSimpleZone(LZConfig) + if fireFX.verbose then + trigger.action.outText("+++ffx: NO config zone!", 30) + end + end + + if fireFX.verbose then + trigger.action.outText("+++ffx: read config", 30) + end +end + +function fireFX.start() + -- lib check + if not dcsCommon.libCheck then + trigger.action.outText("cfx fire FX requires dcsCommon", 30) + return false + end + if not dcsCommon.libCheck("cfx fire FX", fireFX.requiredLibs) then + return false + end + + -- read config + fireFX.readConfigZone() + + -- process fireFX Zones + -- old style + local attrZones = cfxZones.getZonesWithAttributeNamed("fireFX") + for k, aZone in pairs(attrZones) do + fireFX.createFXWithZone(aZone) -- process attributes + fireFX.addFX(aZone) -- add to list + end + + -- handle onStart + for idx, theZone in pairs(fireFX.fx) do + if theZone.fxOnStart then + fireFX.startTheFire(theZone) + end + end + + -- start update + fireFX.update() + + trigger.action.outText("cfx fire FX v" .. fireFX.version .. " started.", 30) + return true +end + +-- let's go! +if not fireFX.start() then + trigger.action.outText("cfx fireFX aborted: missing libraries", 30) + fireFX = nil +end \ No newline at end of file diff --git a/modules/limitedAirframes.lua b/modules/limitedAirframes.lua index 7797d77..97371b6 100644 --- a/modules/limitedAirframes.lua +++ b/modules/limitedAirframes.lua @@ -1,5 +1,5 @@ limitedAirframes = {} -limitedAirframes.version = "1.5.0" +limitedAirframes.version = "1.5.1" limitedAirframes.verbose = false limitedAirframes.enabled = true -- can be turned off limitedAirframes.userCanToggle = true -- F10 menu? @@ -12,6 +12,7 @@ limitedAirframes.method = "inc" limitedAirframes.warningSound = "Quest Snare 3.wav" limitedAirframes.loseSound = "Death PIANO.wav" limitedAirframes.winSound = "Triumphant Victory.wav" +limitedAirframes.announcer = true limitedAirframes.requiredLibs = { "dcsCommon", -- common is of course needed for everything @@ -50,6 +51,7 @@ limitedAirframes.requiredLibs = { currRed - 1.4.1 - removed dependency to cfxPlayer - 1.5.0 - persistence support + - 1.5.1 - new "announcer" attribute --]]-- @@ -133,27 +135,17 @@ function limitedAirframes.readConfigZone() end -- ok, for each property, load it if it exists --- if cfxZones.hasProperty(theZone, "enabled") then - limitedAirframes.enabled = cfxZones.getBoolFromZoneProperty(theZone, "enabled", true) --- end + limitedAirframes.enabled = cfxZones.getBoolFromZoneProperty(theZone, "enabled", true) + + limitedAirframes.userCanToggle = cfxZones.getBoolFromZoneProperty(theZone, "userCanToggle", true) --- if cfxZones.hasProperty(theZone, "userCanToggle") then - limitedAirframes.userCanToggle = cfxZones.getBoolFromZoneProperty(theZone, "userCanToggle", true) --- end - - --- if cfxZones.hasProperty(theZone, "maxRed") then - limitedAirframes.maxRed = cfxZones.getNumberFromZoneProperty(theZone, "maxRed", -1) --- end - --- if cfxZones.hasProperty(theZone, "maxBlue") then - limitedAirframes.maxBlue = cfxZones.getNumberFromZoneProperty(theZone, "maxBlue", -1) --- end + limitedAirframes.maxRed = cfxZones.getNumberFromZoneProperty(theZone, "maxRed", -1) + + limitedAirframes.maxBlue = cfxZones.getNumberFromZoneProperty(theZone, "maxBlue", -1) limitedAirframes.numRed = cfxZones.getStringFromZoneProperty(theZone, "#red", "*none") limitedAirframes.numBlue = cfxZones.getStringFromZoneProperty(theZone, "#blue", "*none") - limitedAirframes.redWinsFlag = cfxZones.getStringFromZoneProperty(theZone, "redWins!", "*none") if cfxZones.hasProperty(theZone, "redWinsFlag!") then @@ -178,6 +170,8 @@ function limitedAirframes.readConfigZone() if cfxZones.hasProperty(theZone, "loseSound") then limitedAirframes.loseSound = cfxZones.getStringFromZoneProperty(theZone, "loseSound", "none") end + + limitedAirframes.announcer = cfxZones.getBoolFromZoneProperty(theZone, "announcer", true) end -- @@ -221,17 +215,18 @@ function limitedAirframes.addPlayerUnit(theUnit) end end limitedAirframes.playerUnits[uName] = pName - trigger.action.outTextForCoalition(theSide, desc, 30) + if limitedAirframes.announcer then + trigger.action.outTextForCoalition(theSide, desc, 30) + end end function limitedAirframes.killPlayer(pName) limitedAirframes.updatePlayer(pName, "dead") - --trigger.action.outText("+++lim: PILOT LOST: " .. pName .. ", NO CSAR", 30) + end function limitedAirframes.killPlayerInUnit(theUnit) limitedAirframes.updatePlayerInUnit(theUnit, "dead") - --trigger.action.outText("+++lim: PILOT LOST, NO CSAR", 30) end function limitedAirframes.updatePlayerInUnit(theUnit, status) @@ -476,7 +471,6 @@ function limitedAirframes.handlePlayerLeftUnit(event) local pName = limitedAirframes.getKnownUnitPilotByUnitName(theUnit:getName()) local pStatus = limitedAirframes.getStatusOfPlayerInUnit(theUnit) -- player was already dead and has been accounted for - --trigger.action.outText("limAir: Change Plane for player <" .. pName .. "> with status <" .. pStatus .. "> procced.", 30) return end @@ -485,11 +479,8 @@ function limitedAirframes.handlePlayerLeftUnit(event) local uPos = theUnit:getPoint() local meInside = cfxZones.getZonesContainingPoint(uPos, limitedAirframes.safeZones) local mySide = theUnit:getCoalition() - --local speed = dcsCommon.getUnitSpeed(theUnit) -- this can cause problems with carriers, so check if below - --local agl = dcsCommon.getUnitAGL(theUnit) -- this will cause problems with FARP and carriers. -- we now check the inAir local isInAir = theUnit:inAir() - --trigger.action.outTextForCoalition(mySide, "limAir: safe check for Pilot " .. theUnit:getPlayerName() .. ": agl=" .. agl .. ", speed = " .. speed .. ", air status = " .. dcsCommon.bool2YesNo(isInAir), 30) for i=1, #meInside do -- I'm inside all these zones. We look for the first @@ -527,19 +518,18 @@ function limitedAirframes.handlePlayerLeftUnit(event) if isInAir then isSafe = false end if isSafe then --- if limitedAirframes.verbose then - trigger.action.outTextForCoalition(mySide, "Pilot " .. theUnit:getPlayerName() .. " left unit " .. theUnit:getName() .. " legally in zone " .. theSafeZone.name, 30) +-- if limitedAirframes.announcer then +-- trigger.action.outTextForCoalition(mySide, "Pilot " .. theUnit:getPlayerName() .. " left unit " .. theUnit:getName() .. " legally in zone " .. theSafeZone.name, 30) -- end - -- remove from known player planes - -- no more limitedAirframes.removePlayerUnit(theUnit) + return; end end -- ditched outside safe harbour --- if limitedAirframes.verbose then + if limitedAirframes.announcer then trigger.action.outTextForCoalition(mySide, "Pilot " .. theUnit:getPlayerName() .. " DITCHED unit " .. theUnit:getName() .. " -- PILOT is considered MIA", 30) --- end + end limitedAirframes.pilotLost(theUnit) if csarManager and csarManager.airframeDitched then @@ -559,7 +549,9 @@ function limitedAirframes.pilotEjected(event) local theSide = theUnit:getCoalition() local pilot = limitedAirframes.getKnownUnitPilotByUnit(theUnit) local uName = theUnit:getName() - trigger.action.outTextForCoalition(theSide, "Pilot <" .. pilot .. "> ejected from " .. uName .. ", now MIA", 30) + if limitedAirframes.announcer then + trigger.action.outTextForCoalition(theSide, "Pilot <" .. pilot .. "> ejected from " .. uName .. ", now MIA", 30) + end local hasLostTheWar = limitedAirframes.pilotLost(theUnit) @@ -575,7 +567,9 @@ function limitedAirframes.pilotDied(theUnit) local theSide = theUnit:getCoalition() local pilot = limitedAirframes.getKnownUnitPilotByUnit(theUnit) local uName = theUnit:getName() - trigger.action.outTextForCoalition(theSide, "Pilot <" .. pilot .. "> is confirmed KIA while controlling " .. uName, 30) + if limitedAirframes.announcer then + trigger.action.outTextForCoalition(theSide, "Pilot <" .. pilot .. "> is confirmed KIA while controlling " .. uName, 30) + end limitedAirframes.pilotLost(theUnit) end @@ -593,7 +587,7 @@ function limitedAirframes.pilotLost(theUnit) local theSide = theUnit:getCoalition() local pilot = limitedAirframes.getKnownUnitPilotByUnit(theUnit) local uName = theUnit:getName() - --trigger.action.outTextForCoalition(theSide, "Pilot <" .. pilot .. "> is confirmed KIA while controlling " .. uName, 30) + if theSide == 1 then -- red theOtherSide = 2 @@ -605,16 +599,16 @@ function limitedAirframes.pilotLost(theUnit) if limitedAirframes.currRed == 0 then trigger.action.outTextForCoalition(theSide, "\nYou have lost almost all of your pilots.\n\nWARNING: Losing any more pilots WILL FAIL THE MISSION\n", 30) - trigger.action.outSoundForCoalition(theSide, limitedAirframes.warningSound)--"Quest Snare 3.wav") + trigger.action.outSoundForCoalition(theSide, limitedAirframes.warningSound) return false end if limitedAirframes.currRed < 0 then -- red have lost all airframes trigger.action.outText("\nREDFORCE has lost all of their pilots.\n\nBLUEFORCE WINS!\n", 30) - trigger.action.outSoundForCoalition(theSide, limitedAirframes.loseSound) --"Death PIANO.wav") - trigger.action.outSoundForCoalition(theOtherSide, limitedAirframes.winSound)--"Triumphant Victory.wav") --- trigger.action.setUserFlag(limitedAirframes.blueWinsFlag, 1 ) + trigger.action.outSoundForCoalition(theSide, limitedAirframes.loseSound) + trigger.action.outSoundForCoalition(theOtherSide, limitedAirframes.winSound) + cfxZones.pollFlag(limitedAirframes.blueWinsFlag, limitedAirframes.method, limitedAirframes.config) return true end @@ -629,20 +623,22 @@ function limitedAirframes.pilotLost(theUnit) if limitedAirframes.currBlue == 0 then trigger.action.outTextForCoalition(theSide, "\nYou have lost almost all of your pilots.\n\nWARNING: Losing any more pilots WILL FAIL THE MISSION\n", 30) - trigger.action.outSoundForCoalition(theSide, limitedAirframes.warningSound)--"Quest Snare 3.wav") + trigger.action.outSoundForCoalition(theSide, limitedAirframes.warningSound) return false end if limitedAirframes.currBlue < 0 then -- red have lost all airframes trigger.action.outText("\nBLUEFORCE has lost all of their pilots.\n\nREDFORCE WINS!\n", 30) --- trigger.action.setUserFlag(limitedAirframes.redWinsFlag, 1 ) + cfxZones.pollFlag(limitedAirframes.redWinsFlag, limitedAirframes.method, limitedAirframes.config) - trigger.action.outSoundForCoalition(theSide, limitedAirframes.loseSound)--"Death PIANO.wav") - trigger.action.outSoundForCoalition(theOtherSide, limitedAirframes.winSound)--"Triumphant Victory.wav") + trigger.action.outSoundForCoalition(theSide, limitedAirframes.loseSound) + trigger.action.outSoundForCoalition(theOtherSide, limitedAirframes.winSound) return true end - trigger.action.outSoundForCoalition(theSide, limitedAirframes.warningSound)--"Quest Snare 3.wav") - trigger.action.outTextForCoalition(theSide, "You have lost a pilot! Remaining: " .. limitedAirframes.currBlue, 30) + trigger.action.outSoundForCoalition(theSide, limitedAirframes.warningSound) +-- if limitedAirframes.announcer then + trigger.action.outTextForCoalition(theSide, "You have lost a pilot! Remaining: " .. limitedAirframes.currBlue, 30) +-- end end return false end diff --git a/modules/persistence.lua b/modules/persistence.lua index 8b55f41..ebd29e1 100644 --- a/modules/persistence.lua +++ b/modules/persistence.lua @@ -1,5 +1,5 @@ persistence = {} -persistence.version = "1.0.2" +persistence.version = "1.0.3" persistence.ups = 1 -- once every 1 seconds persistence.verbose = false persistence.active = false @@ -22,6 +22,8 @@ persistence.requiredLibs = { - cfxZones interface - always output save location 1.0.2 - QoL when verbosity is on + 1.0.3 - no longer always tells " mission saved to" + new 'saveNotification" can be off PROVIDES LOAD/SAVE ABILITY TO MODULES @@ -375,9 +377,9 @@ function persistence.doSaveMission() return end --- if persistence.verbose then + if persistence.saveNotification then trigger.action.outText("+++persistence: mission saved to\n" .. persistence.missionDir .. persistence.saveFileName, 30) --- end + end end function persistence.noteCleanRestart() @@ -470,6 +472,8 @@ function persistence.readConfigZone() persistence.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false) + persistence.saveNotification = cfxZones.getBoolFromZoneProperty(theZone, "saveNotification", true) + if persistence.verbose then trigger.action.outText("+++persistence: read config", 30) end diff --git a/server modules/smrGUI.lua b/server modules/smrGUI.lua new file mode 100644 index 0000000..c3f7f05 --- /dev/null +++ b/server modules/smrGUI.lua @@ -0,0 +1,50 @@ +smr = {} +smr.restartFlag = "simpleMissionRestart" +-- +-- smr: simple mission restart (server module) +-- in your mission, set flag "simpleMissionRestart" to a value < 0 (zero) +-- and the server restarts the mission within one second +-- +-- Created 20220902 by cfrag - version 1.0.0 +-- + +-- misc procs +function smr.getServerFlagValue(theFlag) + -- execute getUserFlag() in server space + local val, errNo = net.dostring_in('server', " return trigger.misc.getUserFlag(\""..theFlag.."\"); ") + if (not val) and errNo then + net.log("smr - can't access flag, dostring_in returned <".. errNo .. ">") + return 0 + else + -- dostring_in returns a string, so convert to number + return tonumber(val) + end +end + +function smr.restartMission() + local mn = DCS.getMissionFilename( ) + net.log("+++smr: restarting mission: ".. mn) + net.send_chat("+++smr: restarting mission: ".. mn, true) + local success = net.load_mission(mn) + if not success then + net.log("+++smr: FAILED to load <" .. mn .. ">") + net.send_chat("+++smr: FAILED to load <" .. mn .. ">", true) + end +end + +-- main update loop, checked once per secon +local lTime = DCS.getModelTime() +function smr.onSimulationFrame() + if lTime + 1 < DCS.getModelTime() then + -- set next time + lTime = DCS.getModelTime() + -- check to see if the restartFlag is set + if not DCS.isServer() then return end + if smr.getServerFlagValue(smr.restartFlag) > 0 then + smr.restartMission() + end + end +end + +-- install smr in hooks +DCS.setUserCallbacks(smr) diff --git a/tutorial & demo missions/demo - Depatures and Landings.miz b/tutorial & demo missions/demo - Depatures and Landings.miz new file mode 100644 index 0000000..0dfe922 Binary files /dev/null and b/tutorial & demo missions/demo - Depatures and Landings.miz differ