Version 2.2.5

reaper, radioMainMenu
This commit is contained in:
Christian Franz 2024-06-07 13:58:13 +02:00
parent 6022372a58
commit 2b6491b978
23 changed files with 1255 additions and 118 deletions

Binary file not shown.

Binary file not shown.

View File

@ -148,3 +148,8 @@ if not bank.start() then
trigger.action.outText("bank aborted: missing libraries", 30)
bank = nil
end
--[[--
Add 'creditLine' input to directly load a value into the bank?
--]]--

View File

@ -1,5 +1,5 @@
bombRange = {}
bombRange.version = "1.1.3"
bombRange.version = "2.0.0"
bombRange.dh = 1 -- meters above ground level burst
bombRange.requiredLibs = {
@ -22,6 +22,11 @@ VERSION HISTORY
minor clean-up
1.1.2 - corrected bug when no bomb range is detected
1.1.3 - added meters/feet distance when reporting impact
1.1.4 - code hardening against CA interference
2.0.0 - support for radioMainMenu
- support for types
- types can have wild cards
--]]--
bombRange.bombs = {} -- live tracking
@ -170,6 +175,10 @@ function bombRange.showStatsForPlayer(pName, gID, unitName)
if bombRange.mustCheckIn then
local comms = bombRange.unitComms[unitName]
if not comms then
-- player controlled CA vehicle calling. go away.
return
end
if comms.checkedIn then
msg = msg .. "\nYou are checked in with weapons range command.\n"
else
@ -184,6 +193,11 @@ end
-- unit UI
--
function bombRange.initCommsForUnit(theUnit)
local mainMenu = nil
if bombRange.mainMenu then
mainMenu = radioMenu.getMainMenuFor(bombRange.mainMenu) -- nilling both next params will return menus[0]
end
local uName = theUnit:getName()
local pName = theUnit:getPlayerName()
local theGroup = theUnit:getGroup()
@ -199,7 +213,7 @@ function bombRange.initCommsForUnit(theUnit)
end
comms = {}
comms.checkedIn = false
comms.root = missionCommands.addSubMenuForGroup(gID, bombRange.menuTitle)
comms.root = missionCommands.addSubMenuForGroup(gID, bombRange.menuTitle, mainMenu)
comms.getStat = missionCommands.addCommandForGroup(gID, "Get statistics for " .. pName, comms.root, bombRange.redirectComms, {"getStat", uName, pName, gID})
comms.reset = missionCommands.addCommandForGroup(gID, "RESET statistics for " .. pName, comms.root, bombRange.redirectComms, {"reset", uName, pName, gID})
if bombRange.mustCheckIn then
@ -231,6 +245,10 @@ function bombRange.commsRequest(args)
if command == "check" then
comms = bombRange.unitComms[uName]
if not comms then
-- CA player here. we don't talk to you (yet)
return
end
if comms.checkedIn then
comms.checkedIn = false -- we are now checked out
missionCommands.removeItemForGroup(gID, comms.checkin)
@ -424,6 +442,10 @@ function bombRange:onEvent(event)
if event.id == 1 then -- shot event, from player
if not event.weapon then return end
local uComms = bombRange.unitComms[uName]
if not uComms then
-- this is a player-controlled CA vehicle. bye bye
return
end
if bombRange.mustCheckIn and (not uComms.checkedIn) then
if bombRange.verbose then
trigger.action.outText("+++bRng: Player <" .. pName .. "> not checked in.", 30)
@ -459,12 +481,39 @@ function bombRange:onEvent(event)
end
end
if event.id == 15 then
if event.id == 15 then -- birth
if bombRange.types then
if not bombRange.typeCheck(theUnit) then return end
end
-- we could add unit filtering by group here, e.g. only some
-- planes, defined in config
-- for example, bomb range is silly for most helicopter
bombRange.initCommsForUnit(theUnit)
end
end
function bombRange.typeCheck(theUnit)
local theGroup = theUnit:getGroup()
local cat = theGroup:getCategory() -- we use group, not unit. Airplane is 0, Heli is 1
-- check the type explicitly
local myType = theUnit:getTypeName()
if dcsCommon.wildArrayContainsString(bombRange.types, myType) then return true end
-- planes or plane perhaps?
-- if cat == 0 and dcsCommon.arrayContainsStringCaseInsensitive(bombRange.types, "planes") then return true end
if cat == 0 and dcsCommon.wildArrayContainsString(bombRange.types, "plan*") then return true end
-- helos?
if cat == 1 and dcsCommon.wildArrayContainsString(bombRange.types, "hel*") then return true end
-- if cat == 1 and dcsCommon.arrayContainsStringCaseInsensitive(bombRange.types, "helos") then return true end
-- if cat == 1 and dcsCommon.arrayContainsStringCaseInsensitive(bombRange.types, "helicopter") then return true end
return false
end
--
-- Update
--
@ -688,6 +737,25 @@ function bombRange.readConfigZone()
bombRange.signOut = theZone:getStringFromZoneProperty("signOut!", 30)
end
bombRange.method = theZone:getStringFromZoneProperty("method", "inc")
if theZone:hasProperty("types") then
bombRange.types = theZone:getListFromZoneProperty("types", "<none>")
end
if theZone:hasProperty("attachTo:") then
local attachTo = theZone:getStringFromZoneProperty("attachTo:", "<none>")
if radioMenu then
local mainMenu = radioMenu.mainMenus[attachTo]
if mainMenu then
bombRange.mainMenu = mainMenu
else
trigger.action.outText("+++bombRange: cannot find super menu <" .. attachTo .. ">", 30)
end
else
trigger.action.outText("+++bombRange: REQUIRES radioMenu to run before bombRange. 'AttachTo:' ignored.", 30)
end
end
bombRange.verbose = theZone.verbose
end

View File

@ -1,5 +1,5 @@
cfxZones = {}
cfxZones.version = "4.3.2"
cfxZones.version = "4.3.4"
-- cf/x zone management module
-- reads dcs zones and makes them accessible and mutable
@ -51,7 +51,8 @@ cfxZones.version = "4.3.2"
- 4.3.1 - new drawText() for zones
- dmlZones:getClosestZone() bridge
- 4.3.2 - new getListFromZoneProperty()
- 4.3.3 - hardened calculateZoneBounds
- 4.3.4 - rewrote zone bounds for poly zones
--]]--
--
@ -221,7 +222,7 @@ function cfxZones.calculateZoneBounds(theZone)
if not (theZone) then return
end
local bounds = theZone.bounds -- copy ref!
local bounds = theZone.bounds -- copy ref! -- DON'T BELIEVE THIS!
if theZone.isCircle then
-- aabb are easy: center +/- radius
@ -234,38 +235,32 @@ function cfxZones.calculateZoneBounds(theZone)
bounds.ll = dcsCommon.createPoint(center.x - radius, 0, center.z + radius)
bounds.lr = dcsCommon.createPoint(center.x + radius, 0, center.z + radius)
-- write back
theZone.bounds = bounds
elseif theZone.isPoly then
local poly = theZone.poly -- ref copy!
-- create the four points
local ll = cfxZones.createPointFromPoint(poly[1])
local lr = cfxZones.createPointFromPoint(poly[1])
local ul = cfxZones.createPointFromPoint(poly[1])
local ur = cfxZones.createPointFromPoint(poly[1])
local p = cfxZones.createPointFromPoint(poly[1])
local pRad = dcsCommon.dist(theZone.point, poly[1]) -- rRad is radius for polygon from theZone.point
-- now iterate through all points and adjust bounds accordingly
for v=2, #poly do
local vertex = poly[v]
if (vertex.x < ll.x) then ll.x = vertex.x; ul.x = vertex.x end
if (vertex.x > lr.x) then lr.x = vertex.x; ur.x = vertex.x end
if (vertex.z < ul.z) then ul.z = vertex.z; ur.z = vertex.z end
--if (vertex.z > ll.z) then ll.z = vertex.z; lr.z = vertex.z end
if (vertex.z > ur.z) then ur.z = vertex.z; ul.z = vertex.z end
local dp = dcsCommon.dist(theZone.point, vertex)
local lx, ly, mx, my = p.x, p.z, p.x, p.z
for vtx=1, #poly do
local v = poly[vtx]
if v.x < lx then lx = v.x end
if v.x > mx then mx = v.x end
if v.z < ly then ly = v.z end
if v.z > my then my = v.z end
local dp = dcsCommon.dist(theZone.point, v)
if dp > pRad then pRad = dp end -- find largst distance to vertex
end
-- now keep the new point references
-- and store them in the zone's bounds
bounds.ll = ll
bounds.lr = lr
bounds.ul = ul
bounds.ur = ur
-- we may need to ascertain why we need ul, ur, ll, lr instead of just ll and ur
theZone.bounds.ul = dcsCommon.createPoint(lx, 0, my)
theZone.bounds.ur = dcsCommon.createPoint(mx, 0, my)
theZone.bounds.ll = dcsCommon.createPoint(lx, 0, ly)
theZone.bounds.lr = dcsCommon.createPoint(mx, 0, ly)
-- store pRad
theZone.pRad = pRad -- not sure we'll ever need that, but at least we have it
else
-- huston, we have a problem
if cfxZones.verbose then

View File

@ -1,5 +1,5 @@
cloneZones = {}
cloneZones.version = "2.2.1"
cloneZones.version = "2.3.0"
cloneZones.verbose = false
cloneZones.requiredLibs = {
"dcsCommon", -- always
@ -52,6 +52,10 @@ cloneZones.respawnOnGroupID = true
- persistence: persist oSize and set lastSize
2.2.1 - verbosity updates for post-check
- if cloned group is late activation, turn it off
2.3.0 - added optional cWipe? attribute to resolve possible conflict
(undocumented, just to provide lazy people with a migration
path) with wiper module
- using "wipe?" will now create a warning
--]]--
--
@ -238,6 +242,11 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
theZone.deSpawnFlag = theZone:getStringFromZoneProperty( "deClone?", "none")
elseif theZone:hasProperty("wipe?") then
theZone.deSpawnFlag = theZone:getStringFromZoneProperty("wipe?", "none")
trigger.action.outText("+++clnZ: WARNING - Clone Zone <" .. theZone.name .. ">: attribute 'wipe?' is deprecated for clone zones!", 30)
-- note possible conflict with wiper module, so we add the new
-- cWipe? attribute
elseif theZone:hasProperty("cWipe?") then
theZone.deSpawnFlag = theZone:getStringFromZoneProperty("cWipe?", "none")
end
if theZone.deSpawnFlag then

View File

@ -1,5 +1,5 @@
csarManager = {}
csarManager.version = "3.4.0"
csarManager.version = "4.0.0"
csarManager.ups = 1
--[[-- VERSION HISTORY
@ -45,6 +45,7 @@ csarManager.ups = 1
3.3.0 - persistence support
3.4.0 - global timeLimit option in config zone
- fixes expiration bug when persisting data
4.0.0 - support for mainMenu
INTEGRATES AUTOMATICALLY WITH playerScore
@ -713,8 +714,13 @@ function csarManager.setCommsMenu(theUnit)
-- reset all coms now
csarManager.removeCommsFromConfig(conf)
local mainMenu = nil
if csarManager.mainMenu then
mainMenu = radioMenu.getMainMenuFor(csarManager.mainMenu) -- nilling both next params will return menus[0]
end
-- ok, first, if we don't have an F-10 menu, create one
conf.myMainMenu = missionCommands.addSubMenuForGroup(id, 'CSAR Missions')
conf.myMainMenu = missionCommands.addSubMenuForGroup(id, 'CSAR Missions', mainMenu)
-- now we have a menu without submenus.
-- add our own submenus
@ -1619,6 +1625,21 @@ function csarManager.readConfigZone()
csarManager.timeLimit = nil
end
if theZone:hasProperty("attachTo:") then
local attachTo = theZone:getStringFromZoneProperty("attachTo:", "<none>")
if radioMenu then
local mainMenu = radioMenu.mainMenus[attachTo]
if mainMenu then
csarManager.mainMenu = mainMenu
else
trigger.action.outText("+++csarManager: cannot find super menu <" .. attachTo .. ">", 30)
end
else
trigger.action.outText("+++csarManager: REQUIRES radioMenu to run before csarManager. 'AttachTo:' ignored.", 30)
end
end
if csarManager.verbose then
trigger.action.outText("+++csar: read config", 30)
end

View File

@ -1,5 +1,5 @@
dcsCommon = {}
dcsCommon.version = "3.0.7"
dcsCommon.version = "3.0.8"
--[[-- VERSION HISTORY
3.0.0 - removed bad bug in stringStartsWith, only relevant if caseSensitive is false
- point2text new intsOnly option
@ -20,6 +20,9 @@ dcsCommon.version = "3.0.7"
- new pointXpercentYdegOffAB()
3.0.6 - new arrayContainsStringCaseInsensitive()
3.0.7 - fixed small bug in wildArrayContainsString
3.0.8 - deepCopy() and deepTableCopy() alternates to clone() to patch
around a strange DCS 2.9 issue
Kiowa added to Troop Carriers
--]]--
@ -33,7 +36,7 @@ dcsCommon.version = "3.0.7"
-- globals
dcsCommon.cbID = 0 -- callback id for simple callback scheduling
dcsCommon.troopCarriers = {"Mi-8MT", "UH-1H", "Mi-24P"} -- Ka-50, Apache and Gazelle can't carry troops
dcsCommon.troopCarriers = {"Mi-8MT", "UH-1H", "Mi-24P", "OH58D"} -- Ka-50, Apache and Gazelle can't carry troops
dcsCommon.coalitionSides = {0, 1, 2}
dcsCommon.maxCountry = 86 -- number of countries defined in total
@ -1200,6 +1203,7 @@ dcsCommon.version = "3.0.7"
-- clone is a recursive clone which will also clone
-- deeper levels, as used in units
function dcsCommon.clone(orig, stripMeta)
--[[-- code seems to break with DCS 2.9
if not orig then return nil end
local orig_type = type(orig)
local copy
@ -1226,7 +1230,57 @@ dcsCommon.version = "3.0.7"
copy = orig
end
return copy
--]]--
if stripMeta then
trigger.action.outText("+++common: warning: stripmetatable no longer supported.", 30)
end
return dcsCommon.deepTableCopy(orig)
end
-- deepCopy from http://lua-users.org/wiki/CopyTable
function dcsCommon.deepcopy(orig, copies)
if not orig then return nil end
copies = copies or {}
local orig_type = type(orig)
local copy
if orig_type == 'table' then
if copies[orig] then
copy = copies[orig]
else
copy = {}
copies[orig] = copy
for orig_key, orig_value in next, orig, nil do
copy[deepcopy(orig_key, copies)] = deepcopy(orig_value, copies)
end
setmetatable(copy, deepcopy(getmetatable(orig), copies))
end
else -- number, string, boolean, etc
copy = orig
end
return copy
end
-- deepTableCopy from Mist (thanks Grimes!)
function dcsCommon.deepTableCopy(object)
if not object then return nil end
local lookup_table = {}
local function _copy(object)
if type(object) ~= "table" then
return object
elseif lookup_table[object] then
return lookup_table[object] -- break cycles
end
local new_table = {}
lookup_table[object] = new_table
for index, value in pairs(object) do
new_table[_copy(index)] = _copy(value)
end
return setmetatable(new_table, getmetatable(object))
end
return _copy(object)
end
function dcsCommon.copyArray(inArray)
if not inArray then return nil end

View File

@ -1,5 +1,5 @@
flareZone = {}
flareZone.version = "1.1.0"
flareZone.version = "1.2.0"
flareZone.verbose = false
flareZone.name = "flareZone"
@ -8,6 +8,7 @@ flareZone.name = "flareZone"
1.1.0 - improvements to verbosity
- OOP
- small bugfix in doFlare assignment
1.2.0 - new rndLoc attribute
--]]--
flareZone.requiredLibs = {
"dcsCommon",
@ -50,6 +51,8 @@ function flareZone.addFlareZone(theZone)
theZone.salvoDurationL, theZone.salvoDurationH = theZone:getPositiveRangeFromZoneProperty("duration", 1)
theZone.rndLoc = theZone:getBoolFromZoneProperty("rndLoc", flase)
if theZone.verbose or flareZone.verbose then
trigger.action.outText("+++flrZ: new flare <" .. theZone.name .. ">, color (" .. theZone.flareColor .. ")", 30)
end
@ -59,15 +62,20 @@ end
function flareZone.launch(theZone)
local color = theZone.flareColor
if color < 0 then color = math.random(4) - 1 end
local loc = cfxZones.getPoint(theZone, true) -- with height
local loc
if theZone.rndLoc then
locFlat = theZone:randomPointInZone()
loc = {x = locFlat.x, y = land.getHeight({x = locFlat.x, y = locFlat.z}), z = locFlat.z}
else
loc = cfxZones.getPoint(theZone, true) -- with height
end
loc.y = loc.y + theZone.flareAlt
-- calculate azimuth
local azimuth = cfxZones.randomInRange(theZone.azimuthL, theZone.azimuthH) -- in deg
if flareZone.verbose or theZone.verbose then
trigger.action.outText("+++flrZ: launching <" .. theZone.name .. ">, c = " .. color .. " (" .. dcsCommon.flareColor2Text(color) .. "), azi <" .. azimuth .. "> [" .. theZone.azimuthL .. "-" .. theZone.azimuthH .. "]", 30)
end
-- if flareZone.verbose or theZone.verbose then
-- trigger.action.outText("+++flrZ: launching <" .. theZone.name .. ">, c = " .. color .. " (" .. dcsCommon.flareColor2Text(color) .. "), azi <" .. azimuth .. "> [" .. theZone.azimuthL .. "-" .. theZone.azimuthH .. "]", 30)
-- end
azimuth = azimuth * 0.0174533 -- in rads
trigger.action.signalFlare(loc, color, azimuth)
@ -80,6 +88,9 @@ function flareZone.update()
-- launch if flag banged
for idx, theZone in pairs(flareZone.flares) do
if cfxZones.testZoneFlag(theZone, theZone.doFlare, theZone.flareTriggerMethod, "lastDoFlare") then
if flareZone.verbose or theZone.verbose then
trigger.action.outText("+++flr: triggerd flares for <" .. theZone.name .. "> on input? <" .. theZone.doFlare .. ">", 30)
end
local salvo = cfxZones.randomInRange(theZone.salvoSizeL, theZone.salvoSizeH)
if salvo < 2 then
-- one-shot

View File

@ -1,5 +1,5 @@
cfxGroundTroops = {}
cfxGroundTroops.version = "2.2.0"
cfxGroundTroops.version = "2.2.1"
cfxGroundTroops.ups = 0.25 -- every 4 seconds
cfxGroundTroops.verbose = false
cfxGroundTroops.requiredLibs = {
@ -34,6 +34,7 @@ cfxGroundTroops.jtacCB = {} -- jtac callbacks, to be implemented
2.0.1 - small fiex ti checkPileUp()
2.1.0 - captureandhold - oneshot attackowned
2.2.0 - moveFormation support
2.2.1 - reduced verbosity
an entry into the deployed troop table has the following attributes
- group - the group
@ -926,7 +927,7 @@ function cfxGroundTroops.createGroundTroops(inGroup, range, orders, moveFormatio
if orders:lower() == "lase" then
orders = "laze" -- we use WRONG spelling here, cause we're cool. yeah, right.
end
trigger.action.outText("Enter createGT group <" .. inGroup:getName() .. "> with o=<" .. orders .. ">, mf=<" .. moveFormation .. ">", 30)
-- trigger.action.outText("Enter createGT group <" .. inGroup:getName() .. "> with o=<" .. orders .. ">, mf=<" .. moveFormation .. ">", 30)
newTroops.insideDestination = false
newTroops.unscheduleCount = 0 -- will count up as we aren't scheduled
newTroops.speedWarning = 0

View File

@ -1,5 +1,5 @@
guardianAngel = {}
guardianAngel.version = "3.0.5"
guardianAngel.version = "3.0.6"
guardianAngel.ups = 10
guardianAngel.name = "Guardian Angel" -- just in case someone accesses .name
guardianAngel.launchWarning = true -- detect launches and warn pilot
@ -65,6 +65,7 @@ guardianAngel.requiredLibs = {
- msgTime to control how long warnings remain on the screen
- disappear message now only on verbose
- dmlZones
3.0.6 - Hardening of targets that aren't part of groups
This script detects missiles launched against protected aircraft an
@ -167,9 +168,13 @@ function guardianAngel.createQItem(theWeapon, theTarget, threat, launcher)
trigger.action.outText("gA: tracking missile <" .. wName .. "> launched by <" .. launcherName .. ">", guardianAngel.msgTime)
end
theItem.theTarget = theTarget
if theTarget.getGroup then -- some targets may not have a group
theItem.tGroup = theTarget:getGroup()
theItem.tID = theItem.tGroup:getID()
else
theItem.tGroup = nil
theItem.tID = nil
end
theItem.targetName = theTarget:getName()
theItem.launchTimeStamp = timer.getTime()
theItem.lastDistance = math.huge
@ -244,24 +249,10 @@ function guardianAngel.monitorItem(theItem)
local ID = theItem.tID
if not w then return false end
if not w:isExist() then
--if (not theItem.missed) and (not theItem.lostTrack) then
--[[--
if guardianAngel.announcer and theItem.threat then
local desc = theItem.weaponName .. ": DISAPPEARED"
if guardianAngel.private then
trigger.action.outTextForGroup(ID, desc, guardianAngel.msgTime)
else
trigger.action.outText(desc, guardianAngel.msgTime)
end
end
--]]--
if guardianAngel.verbose then
trigger.action.outText("+++gA: missile disappeared: <" .. theItem.weaponName .. ">, aimed at <" .. theItem.targetName .. ">",30)
end
guardianAngel.invokeCallbacks("disappear", theItem.targetName, theItem.weaponName)
-- end
return false
end
@ -302,7 +293,7 @@ function guardianAngel.monitorItem(theItem)
if isThreat and guardianAngel.announcer and guardianAngel.active then
local desc = "Missile, missile, missile - now heading for " .. ctName .. "!"
if guardianAngel.private then
if guardianAngel.private and ID then
trigger.action.outTextForGroup(ID, desc, guardianAngel.msgTime)
if guardianAngel.launchSound then
local fileName = "l10n/DEFAULT/" .. guardianAngel.launchSound
@ -364,7 +355,7 @@ function guardianAngel.monitorItem(theItem)
then
desc = desc .. " ANGEL INTERVENTION"
if guardianAngel.announcer then
if guardianAngel.announcer and ID then
if guardianAngel.private then
trigger.action.outTextForGroup(ID, desc, guardianAngel.msgTime)
if guardianAngel.interventionSound then
@ -399,7 +390,7 @@ function guardianAngel.monitorItem(theItem)
--if theItem.lostTrack then desc = desc .. " (little sneak!)" end
--if theItem.missed then desc = desc .. " (missed you!)" end
if guardianAngel.announcer then
if guardianAngel.announcer and ID then
if guardianAngel.private then
trigger.action.outTextForGroup(ID, desc, guardianAngel.msgTime)
else

View File

@ -1,5 +1,5 @@
jtacGrpUI = {}
jtacGrpUI.version = "2.0.0"
jtacGrpUI.version = "3.0.0"
jtacGrpUI.requiredLibs = {
"dcsCommon", -- always
"cfxZones",
@ -11,6 +11,8 @@ jtacGrpUI.requiredLibs = {
- eliminated cfxPlayer dependence
- clean-up
- jtacSound
3.0.0 - support for attachTo:
--]]--
-- find & command cfxGroundTroops-based jtacs
-- UI installed via OTHER for all groups with players
@ -143,6 +145,11 @@ function jtacGrpUI.setCommsMenu(theGroup)
if not Group.isExist(theGroup) then return end
if not jtacGrpUI.isEligibleForMenu(theGroup) then return end
local mainMenu = nil
if jtacGrpUI.mainMenu then
mainMenu = radioMenu.getMainMenuFor(jtacGrpUI.mainMenu) -- nilling both next params will return menus[0]
end
local conf = jtacGrpUI.getConfigForGroup(theGroup)
conf.id = theGroup:getID(); -- we always do this ALWAYS
@ -151,7 +158,7 @@ function jtacGrpUI.setCommsMenu(theGroup)
if not conf.myMainMenu then
local commandTxt = "jtac Lasing Report"
local theCommand = missionCommands.addCommandForGroup(
conf.id, commandTxt, nil, jtacGrpUI.redirectCommandX, {conf, "lasing report"})
conf.id, commandTxt, mainMenu, jtacGrpUI.redirectCommandX, {conf, "lasing report"})
conf.myMainMenu = theCommand
end
@ -160,7 +167,7 @@ function jtacGrpUI.setCommsMenu(theGroup)
-- ok, first, if we don't have an F-10 menu, create one
if not (conf.myMainMenu) then
conf.myMainMenu = missionCommands.addSubMenuForGroup(conf.id, 'jtac')
conf.myMainMenu = missionCommands.addSubMenuForGroup(conf.id, 'jtac', mainMenu)
end
-- clear out existing commands
@ -317,12 +324,27 @@ function jtacGrpUI.readConfigZone()
if not theZone then
theZone = cfxZones.createSimpleZone("jtacGrpUIConfig")
end
jtacGrpUI.name = "jtacGrpUI"
jtacGrpUI.jtacTypes = theZone:getStringFromZoneProperty("jtacTypes", "all")
jtacGrpUI.jtacTypes = string.lower(jtacGrpUI.jtacTypes)
jtacGrpUI.jtacSound = theZone:getStringFromZoneProperty("jtacSound", "UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav")
if theZone:hasProperty("attachTo:") then
local attachTo = theZone:getStringFromZoneProperty("attachTo:", "<none>")
if radioMenu then
local mainMenu = radioMenu.mainMenus[attachTo]
if mainMenu then
jtacGrpUI.mainMenu = mainMenu
else
trigger.action.outText("+++jtacGrpUI: cannot find super menu <" .. attachTo .. ">", 30)
end
else
trigger.action.outText("+++jtacGrpUI: REQUIRES radioMenu to run before jtacGrpUI. 'AttachTo:' ignored.", 30)
end
end
jtacGrpUI.verbose = theZone.verbose
end

View File

@ -1,5 +1,5 @@
launchPlatform = {}
launchPlatform.version = "0.0.0"
launchPlatform.version = "0.5.0"
launchPlatform.requiredLibs = {
"dcsCommon",
"cfxZones",
@ -55,7 +55,9 @@ function launchPlatform.launchAtTargetZone(coa, tgtZone, theType) -- gets closes
-- get closest launcher for target
local tgtPoint = tgtZone:getPoint()
local src, dist = cfxZones.getClosestZone(tgtPoint, platforms)
if launchPlatform.verbose then
trigger.action.outText("+++LP: chosen <" .. src.name .. "> as launch platform", 30)
end
local theLauncher = launchPlatform.launchForPlatform(coa, src, tgtPoint, tgtZone)
if not theLauncher then
@ -74,7 +76,9 @@ function launchPlatform.launchAtTargetZone(coa, tgtZone, theType) -- gets closes
end
function launchPlatform.asynchRemovePlatform(args)
trigger.action.outText("LP: asynch remove for group <" .. args .. ">", 30)
if launchPlatform.verbose then
trigger.action.outText("+++LP: asynch remove for group <" .. args .. ">", 30)
end
local theGroup = Group.getByName(args)
if not theGroup then return end
Group.destroy(theGroup)
@ -83,11 +87,11 @@ end
function launchPlatform.createData(thePoint, theTarget, targetZone, radius, name, num, wType)
-- if present, we can use targetZone with some intelligence
if not thePoint then
trigger.action.outText("NO POINT", 30)
trigger.action.outText("+++LP: NO POINT", 30)
return nil
end
if not theTarget then
trigger.action.outText("NO TARGET", 30)
trigger.action.outText("+++LP: NO TARGET", 30)
return nil
end
@ -168,7 +172,9 @@ function launchPlatform.createData(thePoint, theTarget, targetZone, radius, name
-- if inside camp
local hiPrioTargets
if targetZone and targetZone.cloners and #targetZone.cloners > 0 then
if launchPlatform.verbose then
trigger.action.outText("+++LP: detected <" .. targetZone.name .. "> is camp with <" .. #targetZone.cloners .. "> res-points, re-targeting hi-prio", 30)
end
hiPrioTargets = targetZone.cloners
radius = radius / 10 -- much smaller error
end

View File

@ -406,7 +406,7 @@ end
-- Event Handler
--
function milWings:onEvent(theEvent)
if not theEvent then return end
if not theEvent then return end
if not theEvent.initiator then return end
local theUnit = theEvent.initiator
if not theUnit.getGroup then return end

View File

@ -1,5 +1,5 @@
cfxPlayerScore = {}
cfxPlayerScore.version = "3.2.0"
cfxPlayerScore.version = "3.3.0"
cfxPlayerScore.name = "cfxPlayerScore" -- compatibility with flag bangers
cfxPlayerScore.badSound = "Death BRASS.wav"
cfxPlayerScore.scoreSound = "Quest Snare 3.wav"
@ -15,6 +15,7 @@ cfxPlayerScore.firstSave = true -- to force overwrite
3.0.2 - interface with ObjectDestructDetector for scoring scenery objects
3.1.0 - shared data for persistence
3.2.0 - integration with bank
3.3.0 - case INsensitivity for all typeScore objects
--]]--
cfxPlayerScore.requiredLibs = {
@ -34,7 +35,7 @@ cfxPlayerScore.killZones = {} -- when set, kills only count here
-- typeScore: dictionary sorted by typeString for score
-- extend to add more types. It is used by unitType2score to
-- determine the base unit score
cfxPlayerScore.typeScore = {}
cfxPlayerScore.typeScore = {} -- ALL UPPERCASE NOW!!!
cfxPlayerScore.lastPlayerLanding = {} -- timestamp, by player name
cfxPlayerScore.delayBetweenLandings = 30 -- seconds to count as separate landings, also set during take-off to prevent janky t/o to count.
cfxPlayerScore.aircraft = 50
@ -214,9 +215,14 @@ function cfxPlayerScore.object2score(inVictim, killSide) -- does not have group
if not inVictim then return 0 end
if not killSide then killSide = -1 end
local inName = inVictim:getName()
if cfxPlayerScore.verbose then
trigger.action.outText("+++PScr: ob2sc entry to resolve name <" .. inName .. ">", 30)
end
if dcsCommon.isSceneryObject(inVictim) then
local desc = inVictim:getDesc()
if not desc then return 0 end
if not desc then
return 0
end
-- same as object destruct detector to
-- avoid ID changes
inName = desc.typeName
@ -231,14 +237,19 @@ function cfxPlayerScore.object2score(inVictim, killSide) -- does not have group
if type(inName) == "number" then
inName = tostring(inName)
end
if cfxPlayerScore.verbose then
trigger.action.outText("+++PScr: stage II inName: <" .. inName .. ">", 30)
end
-- since 2.7x DCS turns units into static objects for
-- cooking off, so first thing we need to do is do a name check
local objectScore = cfxPlayerScore.typeScore[inName]
local objectScore = cfxPlayerScore.typeScore[inName:upper()]
if not objectScore then
-- try the type desc
local theType = inVictim:getTypeName()
objectScore = cfxPlayerScore.typeScore[theType]
if theType then
objectScore = cfxPlayerScore.typeScore[theType:upper()]
end
end
if type(objectScore) == "string" then
@ -272,17 +283,17 @@ function cfxPlayerScore.unit2score(inUnit)
-- simply extend by adding items to the typescore table.concat
-- we first try by unit name. This allows individual
-- named hi-value targets to have individual scores
local uScore = cfxPlayerScore.typeScore[vicName]
local uScore = cfxPlayerScore.typeScore[vicName:upper()]
-- see if all members of group score
if not uScore then
if (not uScore) and vicGroup then
local grpName = vicGroup:getName()
uScore = cfxPlayerScore.typeScore[grpName]
uScore = cfxPlayerScore.typeScore[grpName:upper()]
end
if uScore == nil then
if not uScore then
-- WE NOW TRY TO ACCESS BY VICTIM'S TYPE STRING
uScore = cfxPlayerScore.typeScore[vicType]
uScore = cfxPlayerScore.typeScore[vicType:upper()]
else
end
@ -291,7 +302,7 @@ function cfxPlayerScore.unit2score(inUnit)
uScore = tonumber(uScore)
end
if uScore == nil then uScore = 0 end
if not uScore then uScore = 0 end
if uScore > 0 then return uScore end
-- only apply base scores when the lookup did not give a result
@ -546,13 +557,13 @@ function cfxPlayerScore.isNamedUnit(theUnit)
local theName = "(cfx_none)"
if type(theUnit) == "string" then
theName = theUnit -- direct name assignment
-- WARNING: NO EXIST CHECK DONE!
else
-- WARNING: NO EXIST CHECK DONE!
-- after kill, unit is dead, so will no longer exist!
theName = theUnit:getName()
if not theName then return false end
end
if cfxPlayerScore.typeScore[theName] then
if cfxPlayerScore.typeScore[theName:upper()] then
return true
end
return false
@ -743,6 +754,9 @@ function cfxPlayerScore.killDetected(theEvent)
-- we are only getting called when and if
-- a kill occured and killer was a player
-- and target exists
if cfxPlayerScore.verbose then
trigger.action.outText("+++PScr: enter kill detected", 30)
end
local killer = theEvent.initiator
local killerName = killer:getPlayerName()
if not killerName then killerName = "<nil>" end
@ -757,6 +771,9 @@ function cfxPlayerScore.killDetected(theEvent)
-- was it a scenery object?
local wasBuilding = dcsCommon.isSceneryObject(victim)
if wasBuilding then
if cfxPlayerScore.verbose then
trigger.action.outText("+++PScr: killed objectz was a map/scenery object", 30)
end
-- these objects have no coalition; we simply award the score if
-- it exists in look-up table.
local staticScore = cfxPlayerScore.object2score(victim, killSide)
@ -1431,8 +1448,19 @@ function cfxPlayerScore.start()
-- identify and process a score table zones
local theZone = cfxZones.getZoneByName("playerScoreTable")
if theZone then
-- trigger.action.outText("Reading custom player score table", 30)
-- read all into my types registry, replacing whatever is there
cfxPlayerScore.typeScore = cfxZones.getAllZoneProperties(theZone)
cfxPlayerScore.typeScore = theZone:getAllZoneProperties(true) -- true = get all properties in UPPER case
-- local n = dcsCommon.getSizeOfTable(cfxPlayerScore.typeScore)
-- trigger.action.outText("Table has <" .. n .. "> entries:", 30)
if true then
--trigger.action.outText("Custom PlayerScore Type Score Table:", 30)
for name, val in pairs (cfxPlayerScore.typeScore) do
-- trigger.action.outText("ps[" .. name .. "]=<" .. val .. ">", 30)
end
end
else
--trigger.action.outText("No custom score defined", 30)
end
-- read score tiggers and values

View File

@ -1,5 +1,5 @@
radioMenu = {}
radioMenu.version = "2.3.0"
radioMenu.version = "3.0.0"
radioMenu.verbose = false
radioMenu.ups = 1
radioMenu.requiredLibs = {
@ -7,6 +7,7 @@ radioMenu.requiredLibs = {
"cfxZones", -- Zones, of course
}
radioMenu.menus = {}
radioMenu.mainMenus = {} -- dict
--[[--
Version History
@ -21,12 +22,19 @@ radioMenu.menus = {}
2.2.1 - corrected ackD
2.3.0 - added wildcard "*" ability for group name match
- added ackASnd .. ackDSnd sounds as options
3.0.0 - new radioMainMenu and attachTo: mechanics
cascading radioMainMenu support
detect cyclic references
--]]--
function radioMenu.addRadioMenu(theZone)
table.insert(radioMenu.menus, theZone)
end
function radioMenu.addRadioMainMenu(theZone)
radioMenu.mainMenus[theZone.name] = theZone
end
function radioMenu.getRadioMenuByName(aName)
for idx, aZone in pairs(radioMenu.menus) do
if aName == aZone.name then return aZone end
@ -38,6 +46,10 @@ function radioMenu.getRadioMenuByName(aName)
return nil
end
function radioMenu.getRadioMainMenuByName(theName)
return radioMenu.mainMenus[theName]
end
--
-- read zone
--
@ -169,6 +181,9 @@ function radioMenu.installMenu(theZone)
end
theZone.rootMenu = {}
theZone.mainRoot = nil -- can be altered with attachTo
-- see if this menu has an attachTo attribute
theZone.mcdA = {}
theZone.mcdB = {}
theZone.mcdC = {}
@ -180,7 +195,11 @@ function radioMenu.installMenu(theZone)
if theZone.menuGroup or theZone.menuTypes then
for idx, grp in pairs(gID) do
local aRoot = missionCommands.addSubMenuForGroup(grp, theZone.rootName, nil)
if theZone.attachTo then
local mainMenu = theZone.attachTo
theZone.mainRoot = radioMenu.getMainMenuFor(mainMenu, theZone, grp)
end
local aRoot = missionCommands.addSubMenuForGroup(grp, theZone.rootName, theZone.mainRoot)
theZone.rootMenu[grp] = aRoot
theZone.mcdA[grp] = 0
theZone.mcdB[grp] = 0
@ -188,9 +207,17 @@ function radioMenu.installMenu(theZone)
theZone.mcdD[grp] = 0
end
elseif theZone.coalition == 0 then
theZone.rootMenu[0] = missionCommands.addSubMenu(theZone.rootName, nil)
if theZone.attachTo then
local mainMenu = theZone.attachTo
theZone.mainRoot = radioMenu.getMainMenuFor(mainMenu, theZone, 0)
end
theZone.rootMenu[0] = missionCommands.addSubMenu(theZone.rootName, theZone.mainRoot)
else
theZone.rootMenu[0] = missionCommands.addSubMenuForCoalition(theZone.coalition, theZone.rootName, nil)
if theZone.attachTo then
local mainMenu = theZone.attachTo
theZone.mainRoot = radioMenu.getMainMenuFor(mainMenu, theZone, 0)
end
theZone.rootMenu[0] = missionCommands.addSubMenuForCoalition(theZone.coalition, theZone.rootName, theZone.mainRoot)
end
if theZone:hasProperty("itemA") then
@ -253,6 +280,18 @@ end
function radioMenu.createRadioMenuWithZone(theZone)
theZone.rootName = theZone:getStringFromZoneProperty("radioMenu", "<No Name>")
if theZone:hasProperty("attachTo:") then
local attachTo = theZone:getStringFromZoneProperty("attachTo:", "<none>")
if radioMenu.verbose or theZone.verbose then
trigger.action.outText("Menu <" .. theZone.name .. "> will attach to <" .. attachTo .. ">", 30)
end
if not radioMenu.mainMenus[attachTo] then
trigger.action.outText("+++rdoM: menu <" .. theZone.name .. "> tries to attachTo unknown radioMainMenu <" .. attachTo .. "> - cancelled.", 30)
else
theZone.attachTo = radioMenu.mainMenus[attachTo]
end
end
theZone.coalition = theZone:getCoalitionFromZoneProperty("coalition", 0)
-- groups / types
if theZone:hasProperty("group") then
@ -350,6 +389,116 @@ function radioMenu.createRadioMenuWithZone(theZone)
end
end
function radioMenu.getMainMenuFor(mainMenu, theZone, idx)
if not idx then idx = 0 end
if not mainMenu.rootMenu[idx] then
-- trigger.action.outText("main <" .. mainMenu.name .. "> for zone <" .. theZone.name .. ">: forcing idx to 0", 30)
return mainMenu.rootMenu[0]
end
-- trigger.action.outText("good main <" .. mainMenu.name .. "> for zone <" .. theZone.name .. ">", 30)
return mainMenu.rootMenu[idx]
end
function radioMenu.installMainMenu(theZone)
local gID = nil -- set of all groups this menu applies to
if theZone.menuGroup then
if not cfxMX then
trigger.action.outText("WARNING: radioMenu's group attribute requires the 'cfxMX' module", 30)
return
end
-- access cfxMX player info for group ID
gID = radioMenu.filterPlayerIDForGroup(theZone)
elseif theZone.menuTypes then
if not cfxMX then
trigger.action.outText("WARNING: radioMenu's type attribute requires the 'cfxMX' module", 30)
return
end
-- access cxfMX player infor with type match for ID
gID = radioMenu.filterPlayerIDForType(theZone)
end
theZone.rootMenu = {} -- roots by many different things
local mainRoot = nil
if theZone.menuGroup or theZone.menuTypes then
for idx, grp in pairs(gID) do
if theZone.attachTo then
local mainMenu = theZone.attachTo
mainRoot = radioMenu.getMainMenuFor(mainMenu, theZone, grp)
end
local aRoot = missionCommands.addSubMenuForGroup(grp, theZone.rootName, mainRoot)
theZone.rootMenu[grp] = aRoot
end
elseif theZone.coalition == 0 then
if theZone.attachTo then
local mainMenu = theZone.attachTo
mainRoot = radioMenu.getMainMenuFor(mainMenu, theZone, grp)
end
theZone.rootMenu[0] = missionCommands.addSubMenu(theZone.rootName, mainRoot)
else
if theZone.attachTo then
local mainMenu = theZone.attachTo
mainRoot = radioMenu.getMainMenuFor(mainMenu, theZone, grp)
end
theZone.rootMenu[0] = missionCommands.addSubMenuForCoalition(theZone.coalition, theZone.rootName, mainRoot)
end
end
function radioMenu.createRadioMainMenuWithZone(theZone)
theZone.rootName = theZone:getStringFromZoneProperty("radioMainMenu", "<No Name>")
if theZone:hasProperty("radioMenu") then
trigger.action.outText("+++radM: ERROR: main menu <" .. theZone.name .. "> also has conflicting 'radioMenu' entry", 30)
end
-- CASCADING SUPPORT. LOOP DETECTION
if theZone:hasProperty("attachTo:") then
local attachTo = theZone:getStringFromZoneProperty("attachTo:", "<none>")
if radioMenu.verbose or theZone.verbose then
trigger.action.outText("MAIN Menu <" .. theZone.name .. "> wants to attach to <" .. attachTo .. ">", 30)
end
if not radioMenu.mainMenus[attachTo] then
trigger.action.outText("+++radioMM: MAIN menu <" .. theZone.name .. "> tries to 'attachTo:' unknown radioMainMenu <" .. attachTo .. "> - cancelled.", 30)
else
-- make sure that this zone has been processed
local super = radioMenu.mainMenus[attachTo]
if super.mainDone then
theZone.attachTo = super
else
-- we need other zones to be processed before
if radioMenu.verbose or theZone.verbose then
trigger.action.outText("Main menu <" .. theZone.name .. "> refers to unprocessed zone <" .. attachTo .. ">, deferring.", 30)
end
return false
end
end
end
-- we now create the ROOT menu that all other menu
-- items attach to that have this as main menu
theZone.coalition = theZone:getCoalitionFromZoneProperty("coalition", 0)
-- groups / types
if theZone:hasProperty("group") then
theZone.menuGroup = theZone:getStringFromZoneProperty("group", "<none>")
theZone.menuGroup = dcsCommon.trim(theZone.menuGroup)
elseif theZone:hasProperty("groups") then
theZone.menuGroup = theZone:getStringFromZoneProperty("groups", "<none>")
theZone.menuGroup = dcsCommon.trim(theZone.menuGroup)
elseif theZone:hasProperty("type") then
theZone.menuTypes = theZone:getStringFromZoneProperty("type", "none")
elseif theZone:hasProperty("types") then
theZone.menuTypes = theZone:getStringFromZoneProperty("types", "none")
end
-- always install this one
radioMenu.installMainMenu(theZone)
theZone.mainDone = true
if radioMenu.verbose or theZone.verbose then
trigger.action.outText("Main menu <" .. theZone.name .. "> processed", 30)
end
return true
end
--
-- Output processing
@ -546,17 +695,9 @@ end
function radioMenu.readConfigZone()
local theZone = cfxZones.getZoneByName("radioMenuConfig")
if not theZone then
if radioMenu.verbose then
trigger.action.outText("+++radioMenu: NO config zone!", 30)
end
theZone = cfxZones.createSimpleZone("radioMenuConfig")
end
radioMenu.verbose = theZone:getBoolFromZoneProperty("verbose", false)
if radioMenu.verbose then
trigger.action.outText("+++radioMenu: read config", 30)
end
end
function radioMenu.start()
@ -572,10 +713,45 @@ function radioMenu.start()
-- read config
radioMenu.readConfigZone()
-- process radioMainMenu top-level zones
--local filtered = {}
local tries = 0
local attrZones = cfxZones.getZonesWithAttributeNamed("radioMainMenu")
-- set up all zones so they can 'reach up' even if not yet procced
for k, aZone in pairs (attrZones) do
radioMenu.addRadioMainMenu(aZone)
end
-- now process, and detect/break cyclic references
repeat
local filtered = {}
for k, aZone in pairs(attrZones) do
radioMenu.createRadioMainMenuWithZone(aZone) -- process attributes
if aZone.mainDone then
-- all good
else
-- wait for next round
table.insert(filtered, aZone)
end
end
done = dcsCommon.getSizeOfTable(filtered) < 1
tries = tries + 1
attrZones = filtered
until done or tries > 20
if tries > 20 then
local msg = "+++radioMenu: ERROR: Cyclic references in menu structure, can't fully process main menus. Unresolved main menus are:\n "
local c = 0
for idx, theZone in pairs(attrZones) do
if c > 0 then msg = msg .. ", " else c = 1 end
msg = msg .. theZone.name
end
trigger.action.outText(msg .. ".", 30)
end
-- process radioMenu Zones
-- old style
local attrZones = cfxZones.getZonesWithAttributeNamed("radioMenu")
for k, aZone in pairs(attrZones) do
local rmZones = cfxZones.getZonesWithAttributeNamed("radioMenu")
for k, aZone in pairs(rmZones) do
radioMenu.createRadioMenuWithZone(aZone) -- process attributes
radioMenu.addRadioMenu(aZone) -- add to list
end
@ -595,4 +771,8 @@ end
--[[--
check CD/standby code for multiple groups
add/remove for mainmenu
visible/invisible for main menus
--]]--

708
modules/reaper.lua Normal file
View File

@ -0,0 +1,708 @@
reaper = {}
reaper.version = "1.0.0"
reaper.requiredLibs = {
"dcsCommon",
"cfxZones",
}
--[[--
VERSION HISTORY
1.0.0 - Initial Version
--]]--
reaper.zones = {}-- all zones
reaper.scanning = {} -- zones that are scanning (looking for tgt). by zone name
reaper.tracking = {} -- zones that are tracking tgt. by zone name
reaper.scanInterval = 10 -- seconds
reaper.trackInterval = 0.3 -- seconds
-- reading reaper zones
function reaper.readReaperZone(theZone)
theZone.myType = string.lower(theZone:getStringFromZoneProperty("reaper", "reaper"))
if dcsCommon.stringStartsWith(theZone.myType, "pre") then theZone.myType = "RQ-1A Predator" else theZone.myType = "MQ-9 Reaper" end
if theZone.myType == "MQ-9 Reaper" then
theZone.alt = 9500
else theZone.alt = 7500 end theZone.alt = theZone:getNumberFromZoneProperty("alt", theZone.alt)
theZone.coa = theZone:getCoalitionFromZoneProperty("coalition", 2)
if theZone.coa == 0 then
trigger.action.outText("+++Reap: Zone <" .. theZone.name .. "> is of coalition NEUTRAL. Switched to BLUE", 30)
theZone.coa = 2
end
theZone.enemy = dcsCommon.getEnemyCoalitionFor(theZone.coa)
theZone.onStart = theZone:getBoolFromZoneProperty("onStart", true)
theZone.code = theZone:getNumberFromZoneProperty("code", 1688)
theZone.theTarget = nil
theZone.theSpot = nil
theZone.theUav = nil
theZone.theGroup = nil
theZone.doSmoke = theZone:getBoolFromZoneProperty("doSmoke", false)
theZone.smokeColor = theZone:getSmokeColorStringFromZoneProperty("smokeColor", "red")
theZone.smokeColor = dcsCommon.smokeColor2Num(theZone.smokeColor)
theZone.cost = theZone:getNumberFromZoneProperty("cost", 700) -- for bank integration
theZone.autoRespawn = theZone:getBoolFromZoneProperty("autoRespawn", false)
theZone.launchUI = theZone:getBoolFromZoneProperty("launchUI", true)
theZone.statusUI = theZone:getBoolFromZoneProperty("statusUI", true)
if theZone:hasProperty("launch?") then
theZone.launch = theZone:getStringFromZoneProperty("launch?", "<none>")
theZone.launchVal = theZone:getFlagValue(theZone.launch)
end
if theZone:hasProperty("status?") then
theZone.status = theZone:getStringFromZoneProperty("status?", "<none>")
theZone.statusVal = theZone:getFlagValue(theZone.status)
end
theZone.hasSpawned = false
if theZone.onStart then
reaper.spawnForZone(theZone)
end
end
-- spawn a drone from a zone
function reaper.spawnForZone(theZone, ack)
-- create spawn data
local gdata = dcsCommon.createEmptyGroundGroupData (dcsCommon.uuid(theZone.name))
gdata.task = "Reconnaissance"
gdata.route = {}
-- calculate left and right
local p = theZone:getPoint()
local left, right
-- use dml zone bounds to get upper left and lower right
if theZone.isPoly then
left = dcsCommon.clone(theZone.bounds.ll) -- tried ll
right = dcsCommon.clone(theZone.bounds.ur) --
else
left = dcsCommon.clone(theZone.bounds.ul)
right = dcsCommon.clone(theZone.bounds.lr)
end
gdata.x = left.x
gdata.y = left.z
-- build the unit data
local unit = {}
unit.name = dcsCommon.uuid(theZone.name)
unit.x = left.x
unit.y = left.z
unit.type = theZone.myType
unit.skill = "High"
if theZone.myType == "MQ-9 Reaper" then
unit.speed = 55
-- unit.alt = 9500
else
-- unit.alt = 7500
unit.speed = 33
end
unit.alt = theZone.alt
-- add to group
gdata.units[1] = unit
-- now create and add waypoints to route
gdata.route.points = {}
local wp1 = reaper.createInitialWP(left, unit.alt, unit.speed)
gdata.route.points[1] = wp1
local wp2 = dcsCommon.createSimpleRoutePointData(right, unit.alt, unit.speed)
gdata.route.points[2] = wp2
-- spawn the group
local cty = dcsCommon.getACountryForCoalition(theZone.coa)
local theGroup = coalition.addGroup(cty, 0, gdata)
if not theGroup then
trigger.action.outText("+++Reap: failed to spawn for zone <" .. theZone.name .. ">", 30)
return
end
if theZone.verbose or reaper.verbose then
trigger.action.outText("+++reap: Spawned <" .. theGroup:getName() .. "> reaper", 30)
end
if ack then
trigger.action.outTextForCoalition(theZone.coa, "Drone <" .. theZone.name .. "> on station", 30)
trigger.action.outSoundForCoalition(theZone.coa, reaper.actionSound)
end
reaper.cleanUp(theZone) -- dealloc anything still there
theZone.theGroup = theGroup
local uavs = theGroup:getUnits()
theZone.theUav = uavs[1]
theZone.theTarget = nil
theZone.theSpot = nil
theZone.hasSpawned = true
reaper.scanning[theZone.name] = theZone
end
function reaper.cleanUp(theZone)
if theZone.theUav and Unit.isExist(theZone.theUav) then
Unit.destroy(theZone.theUav)
end
theZone.theUav = nil
theZone.theTarget = nil
theZone.theGroup = nil
if theZone.theSpot then
Spot.destroy(theZone.theSpot)
end
theZone.theSpot = nil
end
function reaper.createInitialWP(p, alt, speed)
local wp = {
["alt"] = alt,
["action"] = "Turning Point",
["alt_type"] = "BARO",
["properties"] = {
["addopt"] = {}, -- end of ["addopt"]
}, -- end of ["properties"]
["speed"] = speed,
["task"] = {
["id"] = "ComboTask",
["params"] = {
["tasks"] = {
[1] = {
["enabled"] = true,
["auto"] = true,
["id"] = "WrappedAction",
["number"] = 1,
["params"] = {
["action"] = {
["id"] = "EPLRS",
["params"] = {
["value"] = true,
["groupId"] = 1,
}, -- end of ["params"]
}, -- end of ["action"]
}, -- end of ["params"]
}, -- end of [1]
[2] = {
["enabled"] = true,
["auto"] = false,
["id"] = "Orbit",
["number"] = 2,
["params"] = {
["altitude"] = alt,
["pattern"] = "Race-Track",
["speed"] = speed,
}, -- end of ["params"]
}, -- end of [2]
}, -- end of ["tasks"]
}, -- end of ["params"]
}, -- end of ["task"]
["type"] = "Turning Point",
["ETA"] = 0,
["ETA_locked"] = true,
["y"] = p.z,
["x"] = p.x,
["speed_locked"] = true,
["formation_template"] = "",
} -- end of wp
return wp
end
-- scanning & tracking
-- scanning looks for vehicles to track, and exectues much less often
-- tracking tracks a single vehicle and places a pointer on it
function reaper.findFirstEnemyUnitVisible(enemies, theZone)
local p = theZone.theUav:getPoint()
-- we assume a flat altitude of 7000m
local visRange = theZone.alt * 1 -- based on tan(45) = 1 --> range = alt
for idx, aGroup in pairs(enemies) do
local theUnits = aGroup:getUnits()
-- optimization: only scan the first vehicle in group if it's in range local theUnit = theUnits[1]
local theUnit = theUnits[1]
if theUnit and Unit.isExist(theUnit) then
up = theUnit:getPoint()
d = dcsCommon.distFlat(up, p)
if d < visRange then
-- try each unit if it is visible from drone
for idy, aUnit in pairs(theUnits) do
local up = aUnit:getPoint()
up.y = up.y + 2
if land.isVisible(p, up) then return aUnit end
end
end
end
end
end
function reaper.scan()
-- how far can the drone see? we calculate with a 120 degree opening
-- camera lens, making half angle = 45 --> tan(45) = 1
-- so the radius of the visible circle on the ground is 1 * altidude
timer.scheduleFunction(reaper.scan, {}, timer.getTime() + reaper.scanInterval)
filtered = {}
local redEnemies = coalition.getGroups(2, 2) -- blue ground vehicles
local blueEnemeis = coalition.getGroups(1, 2) -- get ground vehicles
for name, theZone in pairs(reaper.scanning) do
local enemies = redEnemies
if theZone.coa == 2 then enemies = blueEnemeis end
if Unit.isExist(theZone.theUav) then
local theTarget = reaper.findFirstEnemyUnitVisible(enemies, theZone)
if theTarget then
-- add a laser tracker to this unit
local lp = theTarget:getPoint()
local lat, lon, alt = coord.LOtoLL(lp)
lat, lon = dcsCommon.latLon2Text(lat, lon)
local theSpot = Spot.createLaser(theZone.theUav, {0, 2, 0}, lp, theZone.code)
if theZone.doSmoke then
trigger.action.smoke(lp , theZone.smokeColor )
end
trigger.action.outTextForCoalition(theZone.coa, "Drone <" .. theZone.name .. "> is tracking a <" .. theTarget:getTypeName() .. "> at " .. lat .. " " .. lon .. ", code " .. theZone.code, 30)
trigger.action.outSoundForCoalition(theZone.coa, reaper.actionSound)
theZone.theTarget = theTarget
if theZone.theSpot then
theZone.theSpot:destroy()
end
theZone.theSpot = theSpot
-- put me in track mode
reaper.tracking[name] = theZone
else
-- will scan again
filtered[name] = theZone
end
else
-- does not remain
if theZone.verbose or reaper.verbose then
trigger.action.outText("+++reap: drone from <" .. theZone.name .. "> no longer exists", 30)
end
trigger.action.outTextForCoalition(theZone.coa, "Drone <" .. theZone.name .. "> lost.", 30)
trigger.action.outSoundForCoalition(theZone.coa, reaper.actionSound)
theZone.theUav = nil
theZone.theSpot = nil
theZone.theTarget = nil
theZone.theGroup = nil
end
end
reaper.scanning = filtered
end
function reaper.track()
local filtered = {}
for name, theZone in pairs(reaper.tracking) do
-- check if uav still alive
if Unit.isExist(theZone.theUav) then
if Unit.isExist(theZone.theTarget) then
-- update stop
local d = theZone.theTarget:getPoint()
theZone.theSpot:setPoint(d)
filtered[name] = theZone
else
trigger.action.outTextForCoalition(theZone.coa, "Drone <" .. theZone.name .. "> searching for new targets", 30)
trigger.action.outSoundForCoalition(theZone.coa, reaper.actionSound)
if theZone.theSpot then
theZone.theSpot:destroy()
end
theZone.theSpot = nil
reaper.scanning[name] = theZone -- back to scanning
end
else
trigger.action.outTextForCoalition(theZone.coa, "Drone <" .. theZone.name .. "> lost", 30)
trigger.action.outSoundForCoalition(theZone.coa, reaper.actionSound)
if theZone.theSpot then
theZone.theSpot:destroy()
end
theZone.theSpot = nil
theZone.theUav = nil
theZone.theGroup = nil
end
end
reaper.tracking = filtered
timer.scheduleFunction(reaper.track, {}, timer.getTime() + reaper.trackInterval)
end
function reaper.update()
timer.scheduleFunction(reaper.update, {}, timer.getTime() + 1)
-- go through all my zones, and respawn those that have no
-- uav but have autoRespawn active
for name, theZone in pairs(reaper.zones) do
if theZone.autoRespawn and not theZone.theUav and theZone.hasSpawned then
-- auto-respawn needs to kick in
reaper.scanning[name] = nil
reaper.tracking[name] = nil
if reaper.verbose or theZone.verbose then
trigger.action.outText("+++reap: respawning for <" .. name .. ">", 30)
end
reaper.spawnForZone(theZone)
end
if theZone.status and theZone:testZoneFlag(theZone.status, "change", "statusVal") then
if theZone.verbose then
trigger.action.outText("+++reap: Triggered status for zone <" .. name .. "> on <" .. theZone.status .. ">", 30)
end
reaper.doSingleDroneStatus(theZone)
end
if theZone.launch and theZone:testZoneFlag(theZone.launch, "change", "launchVal") then
args = {}
args[1] = theZone.coa -- = args[1]
args[2] = name -- = args[2]
reaper.doLaunch(args)
end
end
-- now poll my (global) status flags
if reaper.blueStatus and cfxZones.testZoneFlag(reaper, reaper.blueStatus, "change", "blueStatusVal") then
reaper.doDroneStatusBlue()
end
if reaper.redStatus and cfxZones.testZoneFlag(reaper, reaper.redStatus, "change", "redStatusVal") then
reaper.doDroneStatusRed()
end
end
--
-- UI
--
function reaper.installFullUIForCoa(coa)
-- install "Drone Control" as root for red and blue
local mainMenu = nil
if reaper.mainMenu then
mainMenu = radioMenu.getMainMenuFor(reaper.mainMenu) -- nilling both next params will return menus[0]
end
local root = missionCommands.addSubMenuForCoalition(coa, reaper.menuName, mainMenu)
-- now install submenus
local c1 = missionCommands.addCommandForCoalition(coa, "Drone Status", root, reaper.redirectDroneStatus, {coa,})
local r2 = missionCommands.addSubMenuForCoalition(coa, "Launch Drones", root)
reaper.installLaunchersForCoa(coa, r2)
end
function reaper.installLaunchersForCoa(coa, root)
-- WARNING: we currently install commands, may overflow!
-- trigger.action.outText("enter launchers builder", 30)
local filtered = {}
for name, theZone in pairs(reaper.zones) do
if theZone.coa == coa and theZone.launchUI then
filtered[name] = theZone
end
end
local n = dcsCommon.getSizeOfTable(filtered)
if n > 10 then
trigger.action.outText("+++reap: WARNING too many (" .. n .. ") launchers for coa <" .. coa .. ">", 30)
return
end
for name, theZone in pairs(filtered) do
-- trigger.action.outText("proccing " .. name, 30)
mnu = theZone.name .. ": " .. theZone.myType
if bank and reaper.useCost then
-- requires bank module
mnu = mnu .. "" .. theZone.cost .. ")"
end
local args = {coa, name, }
local r3 = missionCommands.addCommandForCoalition(coa, mnu, root, reaper.redirectLaunch, args)
end
end
function reaper.redirectDroneStatus(args)
timer.scheduleFunction(reaper.doDroneStatus, args, timer.getTime() + 0.1)
end
function reaper.redirectLaunch(args)
timer.scheduleFunction(reaper.doLaunch, args, timer.getTime() + 0.1)
end
--
-- DML API for UI
--
function reaper.doDroneStatusRed()
reaper.doDroneStatus({1,})
end
function reaper.doDroneStatusBlue()
reaper.doDroneStatus({2,})
end
function reaper.doDroneStatus(args)
local coa = args[1]
-- trigger.action.outText("enter do drone status for coa " .. coa, 30)
local done = {}
local msg = ""
local filtered = {}
for name, theZone in pairs(reaper.tracking) do
if theZone.coa == coa and theZone.statusUI then
filtered[name] = theZone
end
end
local n = dcsCommon.getSizeOfTable(filtered)
-- collect tracking drones
if n > 0 then
msg = msg .. "\nThe following drones are tracking targets:"
for name, theZone in pairs(filtered) do
msg = msg .. "\n <" .. name .. ">: "
local theTarget = theZone.theTarget
if theTarget and Unit.isExist(theTarget) then
local lp = theTarget:getPoint()
local lat, lon, alt = coord.LOtoLL(lp)
lat, lon = dcsCommon.latLon2Text(lat, lon)
local ut = theTarget:getTypeName()
msg = msg .. ut .. " at " .. lat .. ", " .. lon .. " code " .. theZone.code
else
msg = msg .. "<signal failure, please try later>"
end
done[name] = true
end
else
msg = msg .. "\n(No drones are tracking a target)\n"
end
-- collect loitering drones
filtered = {}
for name, theZone in pairs(reaper.scanning) do
if theZone.coa == coa and theZone.statusUI then filtered[name] = theZone end
end
n = dcsCommon.getSizeOfTable(filtered)
if n > 0 then
msg = msg .. "\n\nThe following drones are loitering on-station"
for name, theZone in pairs(filtered) do
msg = msg .. "\n <" .. name .. ">: (" .. theZone.myType .. ")"
done[name] = true
end
else
msg = msg .. "\n\n(No drones are loitering)\n"
end
filtered = {}
for name, theZone in pairs(reaper.zones) do
if theZone.coa == coa and theZone.statusUI and not done[name] then
filtered[name] = theZone
end
end
n = dcsCommon.getSizeOfTable(filtered)
if n > 0 then
msg = msg .. "\n\nThe following drones are ready to launch"
for name, theZone in pairs(filtered) do
msg = msg .. "\n <" .. name .. ">: " .. theZone.myType .. " "
if bank and reaper.useCost then
msg = msg .. "" .. theZone.cost .. ")"
end
end
msg = msg .. "\n"
else
msg = msg .. "\n\n(All drones have launched)\n"
end
trigger.action.outTextForCoalition(coa, msg, 30)
trigger.action.outSoundForCoalition(coa, reaper.actionSound)
end
function reaper.doSingleDroneStatus(theZone)
local coa = theZone.coa
-- trigger.action.outText("enter SINGLE drone status for coa " .. coa, 30)
local msg = ""
local name = theZone.name
-- see if drone is tracking
if reaper.tracking[name] and theZone.theTarget then
msg = "<" .. name .. ">: "
local theTarget = theZone.theTarget
if theTarget and Unit.isExist(theTarget) then
local lp = theTarget:getPoint()
local lat, lon, alt = coord.LOtoLL(lp)
lat, lon = dcsCommon.latLon2Text(lat, lon)
local ut = theTarget:getTypeName()
msg = msg .. ut .. " at " .. lat .. ", " .. lon .. " code " .. theZone.code
else
msg = msg .. "[signal failure, please try later]"
end
trigger.action.outTextForCoalition(coa, msg, 30)
trigger.action.outSoundForCoalition(coa, reaper.actionSound)
return
end
-- see if drone is loitering
if reaper.scanning[name] then
msg = "<" .. name .. ">: (" .. theZone.myType .. ") loitering, scanning for targets"
trigger.action.outTextForCoalition(coa, msg, 30)
trigger.action.outSoundForCoalition(coa, reaper.actionSound)
return
end
msg = "<" .. name .. ">: " .. theZone.myType .. " "
if bank and reaper.useCost then
msg = msg .. "" .. theZone.cost .. ") "
end
msg = msg .. "ready to launch"
trigger.action.outTextForCoalition(coa, msg, 30)
trigger.action.outSoundForCoalition(coa, reaper.actionSound)
end
function reaper.doLaunch(args)
coa = args[1]
name = args[2]
-- check if we can launch
local theZone = reaper.zones[name]
if not theZone then
trigger.action.outText("+++reap: something strange happened with launcher <" .. name .. ">", 30)
return
end
if theZone.theUav and Unit.isExist(theZone.theUav) then
trigger.action.outTextForCoalition(coa, "Drone <" .. name .. "> is already on-station", 30)
trigger.action.outSoundForCoalition(coa, reaper.actionSound)
return
end
local hasBalance, amount = 0, 0
-- money check if enabled
if bank and reaper.useCost then
hasBalance, amount = bank.getBalance(coa)
if not hasBalance then
amount = 0
end
if amount < theZone.cost then
trigger.action.outTextForCoalition(coa, "Insufficient funds (§" .. theZone.cost .. " required, you have §" .. amount, 30)
trigger.action.outSoundForCoalition(coa, reaper.actionSound)
return
end
end
-- ok, go for launch
reaper.spawnForZone(theZone)
-- subtract funds
if bank and reaper.useCost then
trigger.action.outTextForCoalition(coa, "Launching <" .. theZone.myType .. "> drone for §" .. theZone.cost .. ", §" .. amount - theZone.cost .. " remaining.", 30)
bank.withdawFunds(coa, theZone.cost)
else
trigger.action.outTextForCoalition(coa, "Launching <" .. theZone.myType .. "> drone.", 30)
end
trigger.action.outSoundForCoalition(coa, reaper.actionSound)
end
--
-- config
--
function reaper.readConfigZone()
local theZone = cfxZones.getZoneByName("reaperConfig")
if not theZone then
theZone = cfxZones.createSimpleZone("reaperConfig")
end
reaper.name = "reaperConfig" -- zones comaptibility
reaper.actionSound = theZone:getStringFromZoneProperty("actionSound", "UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav")
reaper.UI = theZone:getBoolFromZoneProperty("UI", true)
reaper.menuName = theZone:getStringFromZoneProperty("menuName", "Drone Command")
reaper.useCost = theZone:getBoolFromZoneProperty("useCost", true)
if theZone:hasProperty("blueStatus?") then
reaper.blueStatus = theZone:getStringFromZoneProperty("blueStatus?", "<none>")
reaper.blueStatusVal = theZone:getFlagValue(reaper.blueStatus) -- save last value
end
if theZone:hasProperty("redStatus?") then
reaper.redStatus = theZone:getStringFromZoneProperty("redStatus?", "<none>")
reaper.redStatusVal = theZone:getFlagValue(reaper.redStatus) -- save last value
end
if theZone:hasProperty("attachTo:") then
local attachTo = theZone:getStringFromZoneProperty("attachTo:", "<none>")
if radioMenu then
local mainMenu = radioMenu.mainMenus[attachTo]
if mainMenu then
reaper.mainMenu = mainMenu
else
trigger.action.outText("+++reaper: cannot find super menu <" .. attachTo .. ">", 30)
end
else
trigger.action.outText("+++reaper: REQUIRES radioMenu to run before reaper. 'AttachTo:' ignored.", 30)
end
end
reaper.verbose = theZone.verbose
end
-- persistence
function reaper.saveData()
local theData = {}
-- save all non-self-starting, yet running reapers
local running = {}
for name, theZone in pairs (reaper.zones) do
if (not theZone.onStart) and (theZone.theUav)
and Unit.isExist(theZone.theUav) then
running[name] = true
end
end
theData.running = running
return theData, reaper.sharedData
end
function reaper.loadData()
if not persistence then return end
local theData = persistence.getSavedDataForModule("reaper", reaper.sharedData)
if not theData then
if reaper.verbose then
trigger.action.outText("+++reaper: no save data received, skipping.", 30)
end
return
end
local running = theData.running
if theData.running then
for name, ignore in pairs (running) do
local theZone = reaper.zones[name]
if theZone then
reaper.spawnForZone(theZone)
else
trigger.action.outText("+++reaper - persistence: zone <" .. name .. "> does not exist", 30)
end
end
end
end
-- go go go
function reaper.start()
if not dcsCommon.libCheck then
trigger.action.outText("cfx reaper requires dcsCommon", 30)
return false
end
if not dcsCommon.libCheck("cfx reaper", reaper.requiredLibs) then
return false
end
-- read config
reaper.readConfigZone()
-- read reaper zones
local rZones = cfxZones.zonesWithProperty("reaper")
for k, aZone in pairs(rZones) do
reaper.readReaperZone(aZone)
reaper.zones[aZone.name] = aZone
end
-- install UI if desired
if reaper.UI then
local coas = {1, 2}
for idx, coa in pairs(coas) do
reaper.installFullUIForCoa(coa)
end
end
-- load data if persisted
if persistence then
-- sign up for persistence
callbacks = {}
callbacks.persistData = reaper.saveData
persistence.registerModule("reaper", callbacks)
-- now load my data
reaper.loadData()
end
-- schedule first update
timer.scheduleFunction(reaper.update, {}, timer.getTime() + 1)
-- schedule scan and track loops
timer.scheduleFunction(reaper.scan, {}, timer.getTime() + 1)
timer.scheduleFunction(reaper.track, {}, timer.getTime() + 1)
trigger.action.outText("reaper v " .. reaper.version .. " running.", 30)
return true
end
if not reaper.start() then
trigger.action.outText("Reaper failed to start", 30)
end
--[[--
Idea: mobile launch vehicle, zone follows apc around. Can even be hauled along with hook
idea: prioritizing targets in a group
fix quad zone waypoints
filter targets for lasing by list?
--]]--

View File

@ -1,5 +1,5 @@
scribe = {}
scribe.version = "1.1.0"
scribe.version = "2.0.0"
scribe.requiredLibs = {
"dcsCommon", -- always
"cfxZones", -- Zones, of course
@ -12,6 +12,7 @@ VERSION HISTORY
1.0.0 Initial Version
1.0.1 postponed land, postponed takeoff, unit_lost
1.1.0 supports persistence's SHARED ability to share data across missions
2.0.0 support for main menu
--]]--
scribe.verbose = true
scribe.db = {} -- indexed by player name
@ -488,6 +489,12 @@ function scribe.startPlayerGUI()
-- note: currently assumes single-player groups
-- in preparation of single-player 'commandForUnit'
-- ASSUMES SINGLE-UNIT PLAYER GROUPS!
local mainMenu = nil
if scribe.mainMenu then
mainMenu = radioMenu.getMainMenuFor(scribe.mainMenu) -- nilling both next params will return menus[0]
end
for uName, uData in pairs(cfxMX.playerUnitByName) do
local unitInfo = {}
-- try and access each unit even if we know that the
@ -508,7 +515,7 @@ function scribe.startPlayerGUI()
unitInfo.uID = uData.unitId
unitInfo.theType = theType
unitInfo.cat = cfxMX.groupTypeByName[gName]
unitInfo.root = missionCommands.addSubMenuForGroup(unitInfo.gID, scribe.uiMenu)
unitInfo.root = missionCommands.addSubMenuForGroup(unitInfo.gID, scribe.uiMenu, mainMenu)
unitInfo.checkData = missionCommands.addCommandForGroup(unitInfo.gID, "Get Pilot's Statistics", unitInfo.root, scribe.redirectCheckData, unitInfo)
end
end
@ -524,6 +531,24 @@ function scribe.readConfigZone()
scribe.verbose = theZone.verbose
scribe.hasGUI = theZone:getBoolFromZoneProperty("hasGUI", true)
scribe.uiMenu = theZone:getStringFromZoneProperty("uiMenu", "Mission Logbook")
scribe.name = "scribeConfig" -- zones comaptibility
if theZone:hasProperty("attachTo:") then
local attachTo = theZone:getStringFromZoneProperty("attachTo:", "<none>")
if radioMenu then
local mainMenu = radioMenu.mainMenus[attachTo]
if mainMenu then
scribe.mainMenu = mainMenu
else
trigger.action.outText("+++scribe: cannot find super menu <" .. attachTo .. ">", 30)
end
else
trigger.action.outText("+++scribe: REQUIRES radioMenu to run before scribe. 'AttachTo:' ignored.", 30)
end
end
scribe.greetPlayer = theZone:getBoolFromZoneProperty("greetPlayer", true)
scribe.byePlayer = theZone:getBoolFromZoneProperty("byebyePlayer", true)
scribe.landings = theZone:getBoolFromZoneProperty("landings", true)

View File

@ -467,7 +467,7 @@ function unitPersistence.loadMission()
end
if mismatchWarning then
trigger.action.outText("\n+++WARNING: \nSaved data does not match mission. You should re-start from scratch\n", 30)
trigger.action.outText("\n+++WARNING: \nSaved unit data does not match mission. You should re-start from scratch\n", 30)
end
-- set mission according to data received from last save
if unitPersistence.verbose then

View File

@ -1,5 +1,5 @@
williePete = {}
williePete.version = "2.0.2"
williePete.version = "2.0.3"
williePete.ups = 10 -- we update at 10 fps, so accuracy of a
-- missile moving at Mach 2 is within 33 meters,
-- with interpolation even at 3 meters
@ -19,6 +19,7 @@ williePete.requiredLibs = {
- getFirstLivingPlayerInGroupNamed()
2.0.1 - added Harrier's FFAR M156 WP
2.0.2 - hardened playerUpdate()
2.0.3 - further hardened playerUpdate()
--]]--
williePete.willies = {}
@ -635,7 +636,7 @@ function williePete.playerUpdate()
end
end
else
trigger.action.outText("+++wp: strange issues with group <" .. gName .. ">, does not exist. Skipped in playerUpdate()", 30)
trigger.action.outText("+++wp: strange issues with group <" .. unitInfo.gName .. ">, does not exist. Skipped in playerUpdate()", 30)
end
if dropUnit then
-- all outside, remove from zone check-in

View File

@ -1,5 +1,5 @@
wiper = {}
wiper.version = "1.2.0"
wiper.version = "1.3.0"
wiper.verbose = false
wiper.ups = 1
wiper.requiredLibs = {
@ -15,6 +15,9 @@ wiper.wipers = {}
- categories can now be a list
- declutter opetion
- if first category is 'none', zone will not wipe at all but may declutter
1.3.0 - now warns when wiper zone is polygonal
- enhanced verbosity for dictionary setups
- now warns of possible incompatibility with cloners
--]]--
@ -39,6 +42,11 @@ end
function wiper.createWiperWithZone(theZone)
theZone.triggerWiperFlag = theZone:getStringFromZoneProperty("wipe?", "*<none>")
-- see if the zone also has a 'cloner' attribute, and warn
if theZone:hasProperty("cloner") then
trigger.action.outText("+++Wpr: Zone <" .. theZone.name .. "> is also a CLONER - check usage of 'wipe?' attribute.", 30)
end
-- triggerWiperMethod
theZone.triggerWiperMethod = theZone:getStringFromZoneProperty("triggerMethod", "change")
if theZone:hasProperty("triggerWiperMethod") then
@ -98,7 +106,7 @@ function wiper.createWiperWithZone(theZone)
end
theDict[shortName] = ew
if wiper.verbose or theZone.verbose then
trigger.action.outText("+++wpr: dict [".. shortName .."], '*' = " .. dcsCommon.bool2Text(ew) .. " for <" .. theZone:getName() .. ">",30)
trigger.action.outText("+++wpr: dict [".. shortName .."], '*' = " .. dcsCommon.bool2Text(ew) .. " successful for <" .. theZone:getName() .. ">",30)
end
end
theZone.wipeNamed = theDict
@ -106,6 +114,10 @@ function wiper.createWiperWithZone(theZone)
theZone.wipeInventory = theZone:getBoolFromZoneProperty("wipeInventory", false)
if theZone.isPoly then
trigger.action.outText("+++wpr: WARNING: wiper zone <" .. theZone.name .. "> is NOT CIRCULAR, but quad-based. Expect erratic behavior!", 30)
end
if wiper.verbose or theZone.verbose then
trigger.action.outText("+++wpr: new wiper zone <".. theZone.name ..">", 30)
end

Binary file not shown.