Version 1.1.9

new counter
massive upgrades to messenger
small upgrades to GuardianAngel
This commit is contained in:
Christian Franz 2022-11-10 09:03:15 +01:00
parent 5c1afa0c4b
commit 8f225cc30a
15 changed files with 622 additions and 59 deletions

Binary file not shown.

Binary file not shown.

View File

@ -1,5 +1,5 @@
baseCaptured={}
baseCaptured.version = "1.0.1"
baseCaptured.version = "1.0.2"
baseCaptured.verbose = false
baseCaptured.ups = 1
baseCaptured.requiredLibs = {
@ -15,6 +15,8 @@ baseCaptured.handleContested = true --
1.0.0 - Initial version based on cloose's code
1.0.1 - contested! flag
- update and handleContested
1.0.2 - expanded verbosity
- typo in defining redCaptured! corrected
--]]--
@ -47,7 +49,7 @@ function baseCaptured.createZone(theZone)
end
if cfxZones.hasProperty(theZone, "redCaptured!") then
theZone.redCap = cfxZones.getStringFromZoneProperty(theZone, "blueCaptured!", "*none")
theZone.redCap = cfxZones.getStringFromZoneProperty(theZone, "redCaptured!", "*none")
end
if cfxZones.hasProperty(theZone, "red!") then
@ -81,10 +83,16 @@ function baseCaptured.triggerZone(theZone)
if newOwner == 1 then -- red
if theZone.redCap then
cfxZones.pollFlag(theZone.redCap, theZone.capturedMethod, theZone)
if baseCaptured.verbose or theZone.verbose then
trigger.action.outText("+++bCap: banging redCap! with <" .. theZone.redCap .. "> for zone <" .. theZone.name .. ">", 30)
end
end
elseif newOwner == 2 then
if theZone.blueCap then
cfxZones.pollFlag(theZone.blueCap, theZone.capturedMethod, theZone)
if baseCaptured.verbose or theZone.verbose then
trigger.action.outText("+++bCap: banging blueCap! with <" .. theZone.blueCap .. "> for zone <" .. theZone.name .. ">", 30)
end
end
else
-- contested

View File

@ -1,5 +1,5 @@
cfxSpawnZones = {}
cfxSpawnZones.version = "1.7.0"
cfxSpawnZones.version = "1.7.1"
cfxSpawnZones.requiredLibs = {
"dcsCommon", -- common is of course needed for everything
-- pretty stupid to check for this since we
@ -61,6 +61,8 @@ cfxSpawnZones.spawnedGroups = {}
-- 1.5.3 - spawn?, spawnUnits? flags
-- 1.6.0 - trackwith interface for group tracker
-- 1.7.0 - persistence support
-- 1.7.1 - improved verbosity
-- - spelling check
--
-- new version requires cfxGroundTroops, where they are
--
@ -230,6 +232,10 @@ function cfxSpawnZones.createSpawner(inZone)
theSpawner.target = nil
end
if cfxSpawnZones.verbose or inZone.verbose then
trigger.action.outText("+++spwn: created spawner for <" .. inZone.name .. ">", 30)
end
return theSpawner
end
@ -326,6 +332,10 @@ function cfxSpawnZones.spawnWithSpawner(aSpawner)
if not aSpawner then return end
local theZone = aSpawner.zone -- retrieve the zone that defined me
if cfxSpawnZones.verbose or theZone.verbose then
trigger.action.outText("+++spwn: started spawn with spawner for <" .. theZone.name .. ">", 30)
end
-- will NOT check if conditions are met. This forces a spawn
local unitTypes = {} -- build type names
--local p = aSpawner.zone.point

View File

@ -1,5 +1,5 @@
cfxZones = {}
cfxZones.version = "2.9.0"
cfxZones.version = "2.9.1"
-- cf/x zone management module
-- reads dcs zones and makes them accessible and mutable
@ -106,6 +106,9 @@ cfxZones.version = "2.9.0"
- linkUnit works for late-activating units
- linkUnit now also works for player / clients, dynamic (re-)linking
- linkUnit uses zone's origin for all calculations
- 2.9.1 - new evalRemainder()
- pollFlag supports +/- for immediate numbers, flags, number flags in parantheses
- stronger guards in hasProperty
--]]--
cfxZones.verbose = false
@ -1186,6 +1189,35 @@ function cfxZones.unPulseFlag(args)
cfxZones.setFlagValue(theFlag, newVal, theZone)
end
function cfxZones.evalRemainder(remainder)
local rNum = tonumber(remainder)
if not rNum then
-- we use remainder as name for flag
-- PROCESS ESCAPE SEQUENCES
local esc = string.sub(remainder, 1, 1)
local last = string.sub(remainder, -1)
if esc == "@" then
remainder = string.sub(remainder, 2)
remainder = dcsCommon.trim(remainder)
end
if esc == "(" and last == ")" and string.len(remainder) > 2 then
-- note: iisues with startswith("(") ???
remainder = string.sub(remainder, 2, -2)
remainder = dcsCommon.trim(remainder)
end
if esc == "\"" and last == "\"" and string.len(remainder) > 2 then
remainder = string.sub(remainder, 2, -2)
remainder = dcsCommon.trim(remainder)
end
if cfxZones.verbose then
trigger.action.outText("+++zne: accessing flag <" .. remainder .. ">", 30)
end
rNum = cfxZones.getFlagValue(remainder, theZone)
end
return rNum
end
function cfxZones.doPollFlag(theFlag, method, theZone)
if cfxZones.verbose then
trigger.action.outText("+++zones: polling flag " .. theFlag .. " with " .. method, 30)
@ -1198,14 +1230,19 @@ function cfxZones.doPollFlag(theFlag, method, theZone)
method = method:lower()
method = dcsCommon.trim(method)
val = tonumber(method)
if val then
cfxZones.setFlagValue(theFlag, val, theZone)
if cfxZones.verbose or theZone.verbose then
trigger.action.outText("+++zones: flag <" .. theFlag .. "> changed to #" .. val, 30)
if dcsCommon.stringStartsWith(method, "+") or dcsCommon.stringStartsWith(method, "-")
then
-- skip this processing, a legal Lua val can start with "+" or "-"
-- but we interpret it as a method
else
if val then
cfxZones.setFlagValue(theFlag, val, theZone)
if cfxZones.verbose or theZone.verbose then
trigger.action.outText("+++zones: flag <" .. theFlag .. "> changed to #" .. val, 30)
end
return
end
return
end
--trigger.action.outText("+++zones: polling " .. theZone.name .. " method " .. method .. " flag " .. theFlag, 30)
local currVal = cfxZones.getFlagValue(theFlag, theZone)
if method == "inc" or method == "f+1" then
@ -1233,6 +1270,21 @@ function cfxZones.doPollFlag(theFlag, method, theZone)
elseif dcsCommon.stringStartsWith(method, "pulse") then
cfxZones.pulseFlag(theFlag, method, theZone)
elseif dcsCommon.stringStartsWith(method, "+") then
-- we add whatever is to the right
local remainder = dcsCommon.removePrefix(method, "+")
local adder = cfxZones.evalRemainder(remainder)
cfxZones.setFlagValue(theFlag, currVal+adder, theZone)
if theZone.verbose then
trigger.action.outText("+++zones: (poll) updating with '+' flag <" .. theFlag .. "> in <" .. theZone.name .. "> by <" .. adder .. "> to <" .. adder + currVal .. ">", 30)
end
elseif dcsCommon.stringStartsWith(method, "-") then
-- we subtract whatever is to the right
local remainder = dcsCommon.removePrefix(method, "-")
local adder = cfxZones.evalRemainder(remainder)
cfxZones.setFlagValue(theFlag, currVal-adder, theZone)
else
if method ~= "on" and method ~= "f=1" then
trigger.action.outText("+++zones: unknown method <" .. method .. "> - using 'on'", 30)
@ -1894,6 +1946,10 @@ function cfxZones.getPositiveRangeFromZoneProperty(theZone, theProperty, default
end
function cfxZones.hasProperty(theZone, theProperty)
if not theProperty then
trigger.action.outText("+++zne: WARNING - hasProperty called with nil theProperty for zone <" .. theZone.name .. ">", 30)
return false
end
local foundIt = cfxZones.getZoneProperty(theZone, theProperty)
if not foundIt then
-- check for possible forgotten or exchanged IO flags

View File

@ -1,9 +1,11 @@
cfxmon = {}
cfxmon.version = "1.0.0"
cfxmon.version = "1.0.1"
cfxmon.delay = 30 -- seconds for display
--[[--
Version History
1.0.0 - initial version
1.0.1 - better guard for even.initiator to check if unit exists
- and handle static objcects and other non-grouped objects
cfxmon is a monitor for all cfx events and callbacks
use monConfig to tell cfxmon which events and callbacks
@ -36,17 +38,22 @@ function cfxmon.rejected(event)
trigger.action.outText("***mon - dcsReject: " .. event.id .. " (" .. dcsCommon.event2text(event.id) .. ")", cfxmon.delay)
end
function cfxmon.dcsCB(event)
function cfxmon.dcsCB(event) -- callback
local initiatorStat = ""
if event.initiator then
if event.initiator and Unit.isExist(event.initiator) then
local theUnit = event.initiator
local theGroup = theUnit:getGroup()
-- we assume it is unit, but it can be a static object,
-- and may not have getGroup implemented!
if theUnit.getGroup then
local theGroup = theUnit:getGroup()
local theGroupName = "<none>"
if theGroup then theGroupName = theGroup:getName() end
initiatorStat = ", for " .. theUnit:getName()
initiatorStat = initiatorStat .. " of " .. theGroupName
local theGroupName = "<none>"
if theGroup then theGroupName = theGroup:getName() end
initiatorStat = ", for " .. theUnit:getName()
initiatorStat = initiatorStat .. " of " .. theGroupName
else
initiatorStat = ", non-unit (static?) " .. theUnit:getName()
end
else
initiatorStat = ", NO Initiator"
end

View File

@ -1,5 +1,5 @@
changer = {}
changer.version = "1.0.3"
changer.version = "1.0.4"
changer.verbose = false
changer.ups = 1
changer.requiredLibs = {
@ -13,6 +13,7 @@ changer.changers = {}
1.0.1 - Better guards in config to avoid <none> Zone getter warning
1.0.2 - on/off: verbosity
1.0.3 - NOT on/off
1.0.4 - a little bit more conversation
Transmogrify an incoming signal to an output signal
- not
@ -224,7 +225,7 @@ function changer.update()
changer.process(aZone)
else
if changer.verbose or aZone.verbose then
trigger.action.outText("+++chgr: " .. aZone.name .. " gate closed.", 30)
trigger.action.outText("+++chgr: " .. aZone.name .. " gate closed [flag <" .. aZone.changerOnOff .. "> is 0].", 30)
end
end
elseif aZone.changerOnOffINV then
@ -232,12 +233,16 @@ function changer.update()
changer.process(aZone)
else
if changer.verbose or aZone.verbose then
trigger.action.outText("+++chgr: " .. aZone.name .. " gate closed.", 30)
trigger.action.outText("+++chgr: " .. aZone.name .. " gate closed [INVflag <" .. aZone.changerOnOffINV .. "> is 1].", 30)
end
end
else
changer.process(aZone)
end
else
if aZone.verbose then
trigger.action.outText("+++chgr: <" .. aZone.name .. "> is paused.")
end
end
end
end

121
modules/counter.lua Normal file
View File

@ -0,0 +1,121 @@
counter = {}
counter.version = "1.0.0"
counter.verbose = false
counter.ups = 1
counter.requiredLibs = {
"dcsCommon", -- always
"cfxZones", -- Zones, of course
}
counter.counters = {}
--[[--
Version History
1.0.0 - Initial version
--]]--
function counter.addCounter(theZone)
table.insert(counter.counters, theZone)
end
function counter.getCounterByName(aName)
for idx, aZone in pairs(counter.counters) do
if aName == aZone.name then return aZone end
end
if counter.verbose then
trigger.action.outText("+++ctr: no counter with name <" .. aName ..">", 30)
end
return nil
end
function counter.createCounterWithZone(theZone)
theZone.counterInputFlag = cfxZones.getStringFromZoneProperty(theZone, "count?", "*<none>")
theZone.lastCounterInputFlag = cfxZones.getFlagValue(theZone.counterInputFlag, theZone)
-- triggerCounterMethod
theZone.triggerCounterMethod = cfxZones.getStringFromZoneProperty(theZone, "triggerMethod", "change")
if cfxZones.hasProperty(theZone, "triggerCountMethod") then
theZone.triggerCounterMethod = cfxZones.getStringFromZoneProperty(theZone, "triggerChangeMethod", "change")
end
theZone.countMethod = cfxZones.getStringFromZoneProperty(theZone, "countMethod", "+1")
if cfxZones.hasProperty(theZone, "method") then
theZone.countMethod = cfxZones.getStringFromZoneProperty(theZone, "method", "+1")
end
theZone.countOut = cfxZones.getStringFromZoneProperty(theZone, "out!", "<none>")
if cfxZones.hasProperty(theZone, "countOut!") then
theZone.countOut = cfxZones.getStringFromZoneProperty(theZone, "countOut!", "<none>")
end
end
--
-- Update
--
function counter.update()
-- call me in a second to poll triggers
timer.scheduleFunction(counter.update, {}, timer.getTime() + 1/counter.ups)
for idx, aZone in pairs(counter.counters) do
if cfxZones.testZoneFlag(aZone, aZone.counterInputFlag, aZone.triggerCounterMethod, "lastCounterInputFlag") then
cfxZones.pollFlag(aZone.countOut, aZone.countMethod, aZone)
end
end
end
--
-- Config & Start
--
function counter.readConfigZone()
local theZone = cfxZones.getZoneByName("counterConfig")
if not theZone then
if counter.verbose then
trigger.action.outText("+++ctr: NO config zone!", 30)
end
theZone = cfxZones.createSimpleZone("counterConfig") -- temp only
end
counter.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
counter.ups = cfxZones.getNumberFromZoneProperty(theZone, "ups", 1)
if counter.verbose then
trigger.action.outText("+++ctr: read config", 30)
end
end
function counter.start()
-- lib check
if not dcsCommon.libCheck then
trigger.action.outText("cfx counter requires dcsCommon", 30)
return false
end
if not dcsCommon.libCheck("cfx counter", counter.requiredLibs) then
return false
end
-- read config
counter.readConfigZone()
-- process counter Zones
local attrZones = cfxZones.getZonesWithAttributeNamed("count?")
for k, aZone in pairs(attrZones) do
counter.createCounterWithZone(aZone) -- process attributes
counter.addCounter(aZone) -- add to list
end
-- start update
counter.update()
trigger.action.outText("cfx counter v" .. counter.version .. " started.", 30)
return true
end
-- let's go!
if not counter.start() then
trigger.action.outText("cfx counter aborted: missing libraries", 30)
counter = nil
end

View File

@ -1,5 +1,5 @@
dcsCommon = {}
dcsCommon.version = "2.7.7"
dcsCommon.version = "2.7.9"
--[[-- VERSION HISTORY
2.2.6 - compassPositionOfARelativeToB
- clockPositionOfARelativeToB
@ -109,6 +109,15 @@ dcsCommon.version = "2.7.7"
- dumpVar2Str detects meta tables
- rotateGroupData kills unit's psi value if it existed since it messes with heading
- rotateGroupData - changes psi to -heading if it exists rather than nilling
2.7.8 - new getGeneralDirection()
- new getNauticalDirection()
- more robust guards for getUnitSpeed
2.7.9 - new bool2Num(theBool)
- new aspectByDirection()
- createGroundGroupWithUnits corrected spelling of minDist, crashed scattered formation
- randomPointInCircle fixed erroneous local for x, z
- "scattered" formation repaired
--]]--
-- dcsCommon is a library of common lua functions
@ -663,7 +672,9 @@ dcsCommon.version = "2.7.7"
while direction < 0 do
direction = direction + 360
end
while direction >= 360 do
direction = direction - 360
end
if direction < 15 then -- special case 12 o'clock past 12 o'clock
return 12
end
@ -673,6 +684,54 @@ dcsCommon.version = "2.7.7"
end
function dcsCommon.getGeneralDirection(direction) -- inspired by cws, improvements my own
if not direction then return "unkown" end
direction = math.fmod (direction, 360)
while direction < 0 do
direction = direction + 360
end
while direction >= 360 do
direction = direction - 360
end
if direction < 45 then return "ahead" end
if direction < 135 then return "right" end
if direction < 225 then return "behind" end
if direction < 315 then return "left" end
return "ahead"
end
function dcsCommon.getNauticalDirection(direction) -- inspired by cws, improvements my own
if not direction then return "unkown" end
direction = math.fmod (direction, 360)
while direction < 0 do
direction = direction + 360
end
while direction >= 360 do
direction = direction - 360
end
if direction < 45 then return "ahead" end
if direction < 135 then return "starboard" end
if direction < 225 then return "aft" end
if direction < 315 then return "port" end
return "ahead"
end
function dcsCommon.aspectByDirection(direction) -- inspired by cws, improvements my own
if not direction then return "unkown" end
direction = math.fmod (direction, 360)
while direction < 0 do
direction = direction + 360
end
while direction >= 360 do
direction = direction - 360
end
if direction < 45 then return "hot" end
if direction < 135 then return "beam" end
if direction < 225 then return "drag" end
if direction < 315 then return "beam" end
return "hot"
end
function dcsCommon.randomDegrees()
local degrees = math.random(360) * 3.14152 / 180
@ -690,6 +749,8 @@ dcsCommon.version = "2.7.7"
function dcsCommon.randomPointInCircle(sourceRadius, innerRadius, x, z)
if not x then x = 0 end
if not z then z = 0 end
--local y = 0
if not innerRadius then innerRadius = 0 end
if innerRadius < 0 then innerRadius = 0 end
@ -698,8 +759,8 @@ dcsCommon.version = "2.7.7"
-- now lets get a random degree
local degrees = dcsCommon.randomDegrees() -- math.random(360) * 3.14152 / 180 -- ok, it's actually radiants.
local r = (sourceRadius-innerRadius) * percent
local x = x + (innerRadius + r) * math.cos(degrees)
local z = z + (innerRadius + r) * math.sin(degrees)
x = x + (innerRadius + r) * math.cos(degrees)
z = z + (innerRadius + r) * math.sin(degrees)
local thePoint = {}
thePoint.x = x
@ -1351,12 +1412,15 @@ dcsCommon.version = "2.7.7"
local lowDist = 10000
local uPoint = {}
local thePoint = {}
repeat -- get random point intil mindistance to all is kept or emergencybreak
repeat -- get random point until mindistance to all is kept or emergencybreak
thePoint = dcsCommon.randomPointInCircle(radius, innerRadius) -- returns x, 0, z
-- check if too close to others
for idx, rUnit in pairs(processedUnits) do -- get min dist to all positioned units
thePoint = dcsCommon.randomPointInCircle(radius, innerRadius) -- returns x, 0, z
--trigger.action.outText("rPnt: thePoint = " .. dcsCommon.point2text(thePoint), 30)
uPoint.x = rUnit.x
uPoint.y = 0
uPoint.z = rUnit.y
--trigger.action.outText("rPnt: uPoint = " .. dcsCommon.point2text(uPoint), 30)
local dist = dcsCommon.dist(thePoint, uPoint) -- measure distance to unit
if (dist < lowDist) then lowDist = dist end
end
@ -1509,7 +1573,7 @@ dcsCommon.version = "2.7.7"
function dcsCommon.createGroundGroupWithUnits(name, theUnitTypes, radius, minDist, formation, innerRadius)
if not minDist then mindist = 4 end -- meters
if not minDist then minDist = 4 end -- meters
if not formation then formation = "line" end
if not radius then radius = 30 end -- meters
if not innerRadius then innerRadius = 0 end
@ -1982,6 +2046,12 @@ end
return "no"
end
function dcsCommon.bool2Num(theBool)
if not theBool then theBool = false end
if theBool then return 1 end
return 0
end
function dcsCommon.point2text(p)
if not p then return "<!NIL!>" end
local t = "[x="
@ -2417,7 +2487,7 @@ end
function dcsCommon.getUnitSpeed(theUnit)
if not theUnit then return 0 end
if not theUnit:isExist() then return 0 end
if not Unit.isExist(theUnit) then return 0 end
local v = theUnit:getVelocity()
return dcsCommon.mag(v.x, v.y, v.z)
end

View File

@ -1,5 +1,5 @@
guardianAngel = {}
guardianAngel.version = "3.0.2"
guardianAngel.version = "3.0.4"
guardianAngel.ups = 10
guardianAngel.name = "Guardian Angel" -- just in case someone accesses .name
guardianAngel.launchWarning = true -- detect launches and warn pilot
@ -59,6 +59,8 @@ guardianAngel.requiredLibs = {
- removed legacy code
3.0.2 - added guardianAngel.name for those who use local flags on activate
3.0.3 - monitorItem() guards against loss of target (nil)
3.0.4 - launchSound attribute
- interventionSound attribute
This script detects missiles launched against protected aircraft an
@ -276,11 +278,20 @@ function guardianAngel.monitorItem(theItem)
local desc = "Missile, missile, missile - now heading for " .. ctName .. "!"
if guardianAngel.private then
trigger.action.outTextForGroup(ID, desc, 30)
if guardianAngel.launchSound then
local fileName = "l10n/DEFAULT/" .. guardianAngel.launchSound
trigger.action.outSoundForGroup(ID, fileName)
end
else
trigger.action.outText(desc, 30)
if guardianAngel.launchSound then
local fileName = "l10n/DEFAULT/" .. guardianAngel.launchSound
trigger.action.outSound(fileName)
end
end
end
end
guardianAngel.retargetItem(theItem, currentTarget, isThreat)
t = currentTarget
else
@ -326,14 +337,20 @@ function guardianAngel.monitorItem(theItem)
d <= lethalRange + 10
then
desc = desc .. " ANGEL INTERVENTION"
--if theItem.lostTrack then desc = desc .. " (little sneak!)" end
--if theItem.missed then desc = desc .. " (missed you!)" end
if guardianAngel.announcer then
if guardianAngel.private then
trigger.action.outTextForGroup(ID, desc, 30)
if guardianAngel.interventionSound then
local fileName = "l10n/DEFAULT/" .. guardianAngel.interventionSound
trigger.action.outSoundForGroup(ID, fileName)
end
else
trigger.action.outText(desc, 30)
if guardianAngel.interventionSound then
local fileName = "l10n/DEFAULT/" .. guardianAngel.interventionSound
trigger.action.outSound(fileName)
end
end
end
guardianAngel.invokeCallbacks("intervention", theItem.targetName, theItem.weaponName)
@ -608,8 +625,16 @@ function guardianAngel.somethingHappened(event)
-- can be moved to update()
if guardianAngel.private then
trigger.action.outTextForGroup(grpID, "Missile, missile, missile, " .. oclock .. " o clock" .. vbInfo, 30)
if guardianAngel.launchSound then
local fileName = "l10n/DEFAULT/" .. guardianAngel.launchSound
trigger.action.outSoundForGroup(grpID, fileName)
end
else
trigger.action.outText("Missile, missile, missile, " .. oclock .. " o clock" .. vbInfo, 30)
if guardianAngel.launchSound then
local fileName = "l10n/DEFAULT/" .. guardianAngel.launchSound
trigger.action.outSound(fileName)
end
end
theQItem.detected = true -- remember: we detected and warned already
@ -850,6 +875,14 @@ function guardianAngel.readConfigZone()
guardianAngel.lastDeActivate = cfxZones.getFlagValue(guardianAngel.deactivate, theZone)
end
if cfxZones.hasProperty(theZone, "launchSound") then
guardianAngel.launchSound = cfxZones.getStringFromZoneProperty(theZone, "launchSound", "nosound")
end
if cfxZones.hasProperty(theZone, "interventionSound") then
guardianAngel.interventionSound = cfxZones.getStringFromZoneProperty(theZone, "interventionSound", "nosound")
end
guardianAngel.configZone = theZone
if guardianAngel.verbose then
trigger.action.outText("+++gA: processed config zone", 30)

View File

@ -1,5 +1,5 @@
messenger = {}
messenger.version = "2.0.1"
messenger.version = "2.1.0"
messenger.verbose = false
messenger.requiredLibs = {
"dcsCommon", -- always
@ -43,6 +43,24 @@ messenger.messengers = {}
- unit
- group
2.0.1 - config optimization
2.1.0 - unit only: dynamicUnitProcessing for
- <bae: u/z> bearing to unit/zone
- <rbae u/z> response mapped by unit's heading
- <clk: u/z> bearing in clock position to unit/zone
- <rng: u/z> range to unit/zone
- <hnd: u/z> bearing in left/right/ahead/behind
- <sde: u/z> bearing in starboard/port/ahead/aft
- added dynamicGroupProcessing to select unit 1
- responses attribute
- <rsp: flag>
- <rrnd> response randomized
- <rhdg: u/z> respons mapped by unit's heading
- <cls unit> closing speed
- <vel unit> velocity (speed)
- <asp unit> aspect
- fix to messageMute
- <type: unit>
--]]--
@ -137,6 +155,38 @@ function messenger.processDynamicValues(inMsg, theZone)
outMsg = string.gsub(outMsg, pattern, val, 1) -- only one sub!
end
until not startLoc
-- now process rsp
pattern = "<rsp:%s*[%s%w%*%d%.%-_]+>" -- no list allowed but blanks and * and . and - and _ --> we fail on the other specials to keep this simple
if theZone.msgResponses and (#theZone.msgResponses > 0) then -- only if this zone has an array
--trigger.action.outText("enter response proccing", 30)
repeat -- iterate all patterns one by one
local startLoc, endLoc = string.find(outMsg, pattern)
if startLoc then
--trigger.action.outText("response: found an occurence", 30)
local theValParam = string.sub(outMsg, startLoc, endLoc)
-- strip lead and trailer
local param = string.gsub(theValParam, "<rsp:%s*", "")
param = string.gsub(param, ">","")
-- access flag
local val = cfxZones.getFlagValue(param, theZone)
if not val or (val < 1) then val = 1 end
if val > #theZone.msgResponses then val = #theZone.msgResponses end
val = theZone.msgResponses[val]
val = dcsCommon.trim(val)
-- replace pattern in original with new val
outMsg = string.gsub(outMsg, pattern, val, 1) -- only one sub!
end
until not startLoc
-- rnd response
local rndRsp = dcsCommon.pickRandom(theZone.msgResponses)
outMsg = outMsg:gsub ("<rrnd>", rndRsp)
end
return outMsg
end
@ -167,8 +217,13 @@ end
function messenger.processDynamicLoc(inMsg, theZone)
-- replace all occurences of <lat/lon/ele/mgrs: flagName> with their values
local locales = {"lat", "lon", "ele", "mgrs", "lle", "latlon"}
-- agl = angels
-- vel = velocity (speed)
-- hdg = heading
-- rhdg = heading, response-mapped
local locales = {"lat", "lon", "ele", "mgrs", "lle", "latlon", "alt", "vel", "hdg", "rhdg", "type"}
local outMsg = inMsg
local uHead = 0
for idx, aLocale in pairs(locales) do
local pattern = "<" .. aLocale .. ":%s*[%s%w%*%d%.%-_]+>"
repeat -- iterate all patterns one by one
@ -183,14 +238,32 @@ function messenger.processDynamicLoc(inMsg, theZone)
local thePoint = nil
local tZone = cfxZones.getZoneByName(param)
local tUnit = Unit.getByName(param)
local spd = 0
local angels = 0
local theType = "<errType>"
if tZone then
theType = "Zone"
thePoint = cfxZones.getPoint(tZone)
-- since zones always have elevation of 0,
-- now get the elevation from the map
thePoint.y = land.getHeight({x = thePoint.x, y = thePoint.z})
if tZone.linkedUnit and Unit.isExist(tZone.linkedUnit) then
local lU = tZone.linkedUnit
local masterPoint = lU:getPoint()
thePoint.y = masterPoint.y
spd = dcsCommon.getUnitSpeed(lU)
spd = math.floor(spd * 3.6)
uHead = math.floor(dcsCommon.getUnitHeading(tUnit) * 57.2958) -- to degrees.
else
-- since zones always have elevation of 0,
-- now get the elevation from the map
thePoint.y = land.getHeight({x = thePoint.x, y = thePoint.z})
end
elseif tUnit then
if Unit.isExist(tUnit) then
theType = tUnit:getTypeName()
thePoint = tUnit:getPoint()
spd = dcsCommon.getUnitSpeed(tUnit)
-- convert m/s to km/h
spd = math.floor(spd * 3.6)
uHead = math.floor(dcsCommon.getUnitHeading(tUnit) * 57.2958) -- to degrees.
end
else
-- nothing to do, remove me.
@ -202,16 +275,31 @@ function messenger.processDynamicLoc(inMsg, theZone)
-- processing. return result in locString
local lat, lon, alt = coord.LOtoLL(thePoint)
lat, lon = dcsCommon.latLon2Text(lat, lon)
angels = math.floor(thePoint.y)
if theZone.imperialUnits then
alt = math.floor(alt * 3.28084) -- feet
spd = math.floor(spd * 0.539957) -- km/h to knots
angels = math.floor(angels * 3.28084)
else
alt = math.floor(alt) -- meters
end
if angels > 1000 then
angels = math.floor(angels / 100) * 100
end
if aLocale == "lat" then locString = lat
elseif aLocale == "lon" then locString = lon
elseif aLocale == "ele" then locString = tostring(alt)
elseif aLocale == "lle" then locString = lat .. " " .. lon .. " ele " .. tostring(alt)
elseif aLocale == "latlon" then locString = lat .. " " .. lon
elseif aLocale == "alt" then locString = tostring(angels) -- don't confuse alt and angels, bad var naming here
elseif aLocale == "vel" then locString = tostring(spd)
elseif aLocale == "hdg" then locString = tostring(uHead)
elseif aLocale == "type" then locString = theType
elseif aLocale == "rhdg" and (theZone.msgResponses) then
local offset = messenger.rspMapper360(uHead, #theZone.msgResponses)
locString = dcsCommon.trim(theZone.msgResponses[offset])
else
-- we have mgrs
local grid = coord.LLtoMGRS(coord.LOtoLL(thePoint))
@ -226,6 +314,161 @@ function messenger.processDynamicLoc(inMsg, theZone)
return outMsg
end
function messenger.rspMapper360(directionInDegrees, numResponses)
-- maps responses like clock. Clock has 12 'responses' (12, 1, .., 11),
-- with the first (12) also mapping to the last half arc
-- this method dynamically 'winds' the responses around
-- a clock and returns the index of the message to display
if numResponses < 1 then numResponses = 1 end
directionInDegrees = math.floor(directionInDegrees)
while directionInDegrees < 0 do directionInDegrees = directionInDegrees + 360 end
while directionInDegrees >= 360 do directionInDegrees = directionInDegrees - 360 end
-- now we have 0..360
-- calculate arc per item
local arcPerItem = 360 / numResponses
local halfArc = arcPerItem / 2
-- we now map 0..360 to (0-halfArc..360-halfArc) by shifting
-- direction by half-arc and clipping back 0..360
-- and now we can directly derive the index of the response
directionInDegrees = directionInDegrees + halfArc
if directionInDegrees >= 360 then directionInDegrees = directionInDegrees - 360 end
local index = math.floor(directionInDegrees / arcPerItem) + 1 -- 1 .. numResponses
return index
end
function messenger.dynamicGroupProcessing(msg, theZone, theGroup)
if not theGroup then return msg end
-- access first unit
local theUnit = theGroup:getUnit(1)
if not theUnit then return msg end
if not Unit.isExist(theUnit) then return msg end
-- we always use unit 1 as reference
return messenger.dynamicUnitProcessing(msg, theZone, theUnit)
end
function messenger.dynamicUnitProcessing(inMsg, theZone, theUnit)
-- replace all occurences of <bae/rng/asp/cls/clk: unit/zone> with their values
-- bae = bearingInDegreesFromAtoB
-- rng = range
-- asp = aspect (not yet implemented)
-- cls = closing velocity (not yet implemented)
-- clk = o'clock
-- hnd = handedness (left/right/ahead/behind
-- sde = side (starboard / port / ahead / aft)
-- rbea = responses mapped to bearing. maps all responses like clock, with "12" being the first response. requires msgResponses set
local here = theUnit:getPoint()
local uHead = dcsCommon.getUnitHeading(theUnit) * 57.2958 -- to degrees.
local locales = {"bea", "rng", "clk", "hnd", "sde", "rbea", "cls", "pcls", "asp"}
local outMsg = inMsg
for idx, aLocale in pairs(locales) do
local pattern = "<" .. aLocale .. ":%s*[%s%w%*%d%.%-_]+>"
repeat -- iterate all patterns one by one
local startLoc, endLoc = string.find(outMsg, pattern)
if startLoc then
local theValParam = string.sub(outMsg, startLoc, endLoc)
-- strip lead and trailer
local param = string.gsub(theValParam, "<" .. aLocale .. ":%s*", "")
param = string.gsub(param, ">","")
-- find zone or unit
param = dcsCommon.trim(param)
local thePoint = nil
local cls = 0
local aspct = 0
local tZone = cfxZones.getZoneByName(param)
local tUnit = Unit.getByName(param)
local aspect = 0
local tHead = 0
if tZone then
thePoint = cfxZones.getPoint(tZone)
-- if this zone follows a unit, get the master units elevaltion
if tZone.linkedUnit and Unit.isExist(tZone.linkedUnit) then
local lU = tZone.linkedUnit
local masterPoint = lU:getPoint()
thePoint.y = masterPoint.y
cls = -dcsCommon.getClosingVelocity(theUnit, lU)
tHead = dcsCommon.getUnitHeading(lU) * 57.2958
else
-- since zones always have elevation of 0,
-- now get the elevation from the map
thePoint.y = land.getHeight({x = thePoint.x, y = thePoint.z})
end
elseif tUnit then
if Unit.isExist(tUnit) then
thePoint = tUnit:getPoint()
cls = -dcsCommon.getClosingVelocity(theUnit, tUnit)
tHead = dcsCommon.getUnitHeading(tUnit) * 57.2958
end
else
-- nothing to do, remove me.
end
local locString = theZone.errString
if thePoint then
-- now that we have a point, we can do locale-specific
-- processing. return result in locString
local pcls = cls
local r = dcsCommon.dist(here, thePoint)
--local alt = thePoint.y
local uSize = "m"
if theZone.imperialUnits then
r = math.floor(r * 3.28084) -- feet
uSize = "ft"
if r > 1000 then
-- convert to nautical mile
r = math.floor(r * 10 / 6076.12) / 10
uSize = "nm"
end
cls = math.floor(cls * 1.9438452) -- m/s to knots
pcls = math.floor(pcls * 32.8084) / 10 -- ft/s
else
r = math.floor(r) -- meters
if r > 1000 then
r = math.floor (r / 100) / 10
uSize = "km"
end
cls = math.floor(cls * 3.6) -- m/s to km/h
pcls = math.floor(pcls * 10) / 10 -- m/s
end
local bea = dcsCommon.bearingInDegreesFromAtoB(here, thePoint)
local beaInv = 360 - bea -- from tUnit to player
local direction = bea - uHead -- tUnit as seen from player heading uHead
if direction < 0 then direction = direction + 360 end
aspect = beaInv - tHead
-- set up locale exchange string
if aLocale == "bea" then locString = tostring(bea)
elseif aLocale == "asp" then locString = dcsCommon.aspectByDirection(aspect)
elseif aLocale == "clk" then
locString = tostring(dcsCommon.getClockDirection(direction))
elseif aLocale == "rng" then locString = tostring(r)..uSize
elseif aLocale == "cls" then locString = tostring(cls)
elseif aLocale == "pcls" then locString = tostring(pcls)
elseif aLocale == "hnd" then locString = dcsCommon.getGeneralDirection(direction)
elseif aLocale == "sde" then locString = dcsCommon.getNauticalDirection(direction)
elseif aLocale == "rbea" and (theZone.msgResponses) then
local offset = messenger.rspMapper360(direction, #theZone.msgResponses)
locString = dcsCommon.trim(theZone.msgResponses[offset])
else locString = "<locale " .. aLocale .. " err: undefined params>"
end
end
-- replace pattern in original with new val
outMsg = string.gsub(outMsg, pattern, locString, 1) -- only one sub!
end -- if startloc
until not startLoc
end -- for all locales
return outMsg
end
function messenger.dynamicFlagProcessing(inMsg, theZone)
if not inMsg then return "No in message" end
if not theZone then return "Nil zone" end
@ -296,10 +539,12 @@ function messenger.createMessengerWithZone(theZone)
-- end
theZone.messageOff = cfxZones.getBoolFromZoneProperty(theZone, "mute", false) --false
if cfxZones.hasProperty(theZone, "messageOff?") then
if cfxZones.hasProperty(theZone, "messageMute") then
theZone.messageOff = cfxZones.getBoolFromZoneProperty(theZone, "messageMute", false)
end
-- advisory: messageOff, messageOffFlag and lastMessageOff are all distinct
if cfxZones.hasProperty(theZone, "messageOff?") then
theZone.messageOffFlag = cfxZones.getStringFromZoneProperty(theZone, "messageOff?", "*none")
theZone.lastMessageOff = cfxZones.getFlagValue(theZone.messageOffFlag, theZone)
@ -313,23 +558,19 @@ function messenger.createMessengerWithZone(theZone)
-- reveiver: coalition, group, unit
if cfxZones.hasProperty(theZone, "coalition") then
theZone.msgCoalition = cfxZones.getCoalitionFromZoneProperty(theZone, "coalition", 0)
end
if cfxZones.hasProperty(theZone, "msgCoalition") then
elseif cfxZones.hasProperty(theZone, "msgCoalition") then
theZone.msgCoalition = cfxZones.getCoalitionFromZoneProperty(theZone, "msgCoalition", 0)
end
if cfxZones.hasProperty(theZone, "group") then
theZone.msgGroup = cfxZones.getStringFromZoneProperty(theZone, "group", "<none>")
end
if cfxZones.hasProperty(theZone, "msgGroup") then
elseif cfxZones.hasProperty(theZone, "msgGroup") then
theZone.msgGroup = cfxZones.getStringFromZoneProperty(theZone, "msgGroup", "<none>")
end
if cfxZones.hasProperty(theZone, "unit") then
theZone.msgUnit = cfxZones.getStringFromZoneProperty(theZone, "unit", "<none>")
end
if cfxZones.hasProperty(theZone, "msgUnit") then
elseif cfxZones.hasProperty(theZone, "msgUnit") then
theZone.msgUnit = cfxZones.getStringFromZoneProperty(theZone, "msgUnit", "<none>")
end
@ -358,6 +599,12 @@ function messenger.createMessengerWithZone(theZone)
theZone.errString = cfxZones.getStringFromZoneProperty(theZone, "messageError", "")
end
-- possible responses for mapping
if cfxZones.hasProperty(theZone, "responses") then
local resp = cfxZones.getStringFromZoneProperty(theZone, "responses", "none")
theZone.msgResponses = dcsCommon.string2Array(resp, ",")
end
if messenger.verbose or theZone.verbose then
trigger.action.outText("+++Msg: new zone <".. theZone.name .."> will say <".. theZone.message .. ">", 30)
end
@ -424,6 +671,7 @@ function messenger.isTriggered(theZone)
local theGroup = Group.getByName(theZone.msgGroup)
if theGroup and Group.isExist(theGroup) then
local ID = theGroup:getID()
msg = messenger.dynamicGroupProcessing(msg, theZone, theGroup)
trigger.action.outTextForGroup(ID, msg, theZone.duration, theZone.clearScreen)
trigger.action.outSoundForGroup(ID, fileName)
end
@ -431,6 +679,7 @@ function messenger.isTriggered(theZone)
local theUnit = Unit.getByName(theZone.msgUnit)
if theUnit and Unit.isExist(theUnit) then
local ID = theUnit:getID()
msg = messenger.dynamicUnitProcessing(msg, theZone, theUnit)
trigger.action.outTextForUnit(ID, msg, theZone.duration, theZone.clearScreen)
trigger.action.outSoundForUnit(ID, fileName)
end
@ -533,11 +782,6 @@ if not messenger.start() then
end
--[[--
Wildcard extension:
- general flag access <v flag name>
- <t: flag name>
- <lat: unit/zone name>
- <mrgs: unit/zone name>
--]]--

View File

@ -1,5 +1,5 @@
unitZone={}
unitZone.version = "1.2.3"
unitZone.version = "1.2.4"
unitZone.verbose = false
unitZone.ups = 1
unitZone.requiredLibs = {
@ -16,6 +16,7 @@ unitZone.requiredLibs = {
1.2.2 - uzDirectInv
1.2.3 - better guards for enterZone!, exitZone!, changeZone!
- better guards for uzOn? and uzOff?
1.2.4 - more verbosity on uzDirect
--]]--
@ -282,7 +283,7 @@ function unitZone.update()
-- scan all zones
if not aZone.uzPaused then
local newState = unitZone.checkZoneStatus(aZone)
local newState = unitZone.checkZoneStatus(aZone) -- returns true if at least one unit in zone
if newState ~= aZone.lastStatus then
-- bang on change!
@ -292,6 +293,9 @@ function unitZone.update()
-- output direct state suite
if aZone.uzDirect then
if aZone.verbose or unitZone.verbose then
trigger.action.outText("+++uZone: <" .. aZone.name .. "> setting uzDirect <" .. aZone.uzDirect .. "> to ".. dcsCommon.bool2Num(newState), 30)
end
if newState then
cfxZones.setFlagValueMult(aZone.uzDirect, 1, aZone)
else
@ -299,6 +303,10 @@ function unitZone.update()
end
end
if aZone.uzDirectInv then
local invState = not newState
if aZone.verbose or unitZone.verbose then
trigger.action.outText("+++uZone: <" .. aZone.name .. "> setting INVuzDirect <" .. aZone.uzDirectInv .. "> to ".. dcsCommon.bool2Num(invState), 30)
end
if newState then
cfxZones.setFlagValueMult(aZone.uzDirectInv, 0, aZone)
else
@ -363,5 +371,6 @@ if not unitZone.start() then
unitZone = nil
end
--ToDo: matching: name, name wildcard, type
--ToDo: add 'neutral' support and add 'both' option
--ToDo: add API