mirror of
https://github.com/weyne85/DML.git
synced 2025-10-29 16:57:49 +00:00
Version 2.2.5
reaper, radioMainMenu
This commit is contained in:
parent
6022372a58
commit
2b6491b978
Binary file not shown.
Binary file not shown.
@ -148,3 +148,8 @@ if not bank.start() then
|
|||||||
trigger.action.outText("bank aborted: missing libraries", 30)
|
trigger.action.outText("bank aborted: missing libraries", 30)
|
||||||
bank = nil
|
bank = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[[--
|
||||||
|
Add 'creditLine' input to directly load a value into the bank?
|
||||||
|
|
||||||
|
--]]--
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
bombRange = {}
|
bombRange = {}
|
||||||
bombRange.version = "1.1.3"
|
bombRange.version = "2.0.0"
|
||||||
bombRange.dh = 1 -- meters above ground level burst
|
bombRange.dh = 1 -- meters above ground level burst
|
||||||
|
|
||||||
bombRange.requiredLibs = {
|
bombRange.requiredLibs = {
|
||||||
@ -22,6 +22,11 @@ VERSION HISTORY
|
|||||||
minor clean-up
|
minor clean-up
|
||||||
1.1.2 - corrected bug when no bomb range is detected
|
1.1.2 - corrected bug when no bomb range is detected
|
||||||
1.1.3 - added meters/feet distance when reporting impact
|
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
|
bombRange.bombs = {} -- live tracking
|
||||||
@ -170,6 +175,10 @@ function bombRange.showStatsForPlayer(pName, gID, unitName)
|
|||||||
|
|
||||||
if bombRange.mustCheckIn then
|
if bombRange.mustCheckIn then
|
||||||
local comms = bombRange.unitComms[unitName]
|
local comms = bombRange.unitComms[unitName]
|
||||||
|
if not comms then
|
||||||
|
-- player controlled CA vehicle calling. go away.
|
||||||
|
return
|
||||||
|
end
|
||||||
if comms.checkedIn then
|
if comms.checkedIn then
|
||||||
msg = msg .. "\nYou are checked in with weapons range command.\n"
|
msg = msg .. "\nYou are checked in with weapons range command.\n"
|
||||||
else
|
else
|
||||||
@ -184,6 +193,11 @@ end
|
|||||||
-- unit UI
|
-- unit UI
|
||||||
--
|
--
|
||||||
function bombRange.initCommsForUnit(theUnit)
|
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 uName = theUnit:getName()
|
||||||
local pName = theUnit:getPlayerName()
|
local pName = theUnit:getPlayerName()
|
||||||
local theGroup = theUnit:getGroup()
|
local theGroup = theUnit:getGroup()
|
||||||
@ -199,7 +213,7 @@ function bombRange.initCommsForUnit(theUnit)
|
|||||||
end
|
end
|
||||||
comms = {}
|
comms = {}
|
||||||
comms.checkedIn = false
|
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.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})
|
comms.reset = missionCommands.addCommandForGroup(gID, "RESET statistics for " .. pName, comms.root, bombRange.redirectComms, {"reset", uName, pName, gID})
|
||||||
if bombRange.mustCheckIn then
|
if bombRange.mustCheckIn then
|
||||||
@ -231,6 +245,10 @@ function bombRange.commsRequest(args)
|
|||||||
|
|
||||||
if command == "check" then
|
if command == "check" then
|
||||||
comms = bombRange.unitComms[uName]
|
comms = bombRange.unitComms[uName]
|
||||||
|
if not comms then
|
||||||
|
-- CA player here. we don't talk to you (yet)
|
||||||
|
return
|
||||||
|
end
|
||||||
if comms.checkedIn then
|
if comms.checkedIn then
|
||||||
comms.checkedIn = false -- we are now checked out
|
comms.checkedIn = false -- we are now checked out
|
||||||
missionCommands.removeItemForGroup(gID, comms.checkin)
|
missionCommands.removeItemForGroup(gID, comms.checkin)
|
||||||
@ -424,6 +442,10 @@ function bombRange:onEvent(event)
|
|||||||
if event.id == 1 then -- shot event, from player
|
if event.id == 1 then -- shot event, from player
|
||||||
if not event.weapon then return end
|
if not event.weapon then return end
|
||||||
local uComms = bombRange.unitComms[uName]
|
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.mustCheckIn and (not uComms.checkedIn) then
|
||||||
if bombRange.verbose then
|
if bombRange.verbose then
|
||||||
trigger.action.outText("+++bRng: Player <" .. pName .. "> not checked in.", 30)
|
trigger.action.outText("+++bRng: Player <" .. pName .. "> not checked in.", 30)
|
||||||
@ -459,12 +481,39 @@ function bombRange:onEvent(event)
|
|||||||
end
|
end
|
||||||
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)
|
bombRange.initCommsForUnit(theUnit)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
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
|
-- Update
|
||||||
--
|
--
|
||||||
@ -688,6 +737,25 @@ function bombRange.readConfigZone()
|
|||||||
bombRange.signOut = theZone:getStringFromZoneProperty("signOut!", 30)
|
bombRange.signOut = theZone:getStringFromZoneProperty("signOut!", 30)
|
||||||
end
|
end
|
||||||
bombRange.method = theZone:getStringFromZoneProperty("method", "inc")
|
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
|
bombRange.verbose = theZone.verbose
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
cfxZones = {}
|
cfxZones = {}
|
||||||
cfxZones.version = "4.3.2"
|
cfxZones.version = "4.3.4"
|
||||||
|
|
||||||
-- cf/x zone management module
|
-- cf/x zone management module
|
||||||
-- reads dcs zones and makes them accessible and mutable
|
-- 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
|
- 4.3.1 - new drawText() for zones
|
||||||
- dmlZones:getClosestZone() bridge
|
- dmlZones:getClosestZone() bridge
|
||||||
- 4.3.2 - new getListFromZoneProperty()
|
- 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
|
if not (theZone) then return
|
||||||
end
|
end
|
||||||
|
|
||||||
local bounds = theZone.bounds -- copy ref!
|
local bounds = theZone.bounds -- copy ref! -- DON'T BELIEVE THIS!
|
||||||
|
|
||||||
if theZone.isCircle then
|
if theZone.isCircle then
|
||||||
-- aabb are easy: center +/- radius
|
-- 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.ll = dcsCommon.createPoint(center.x - radius, 0, center.z + radius)
|
||||||
bounds.lr = 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
|
elseif theZone.isPoly then
|
||||||
local poly = theZone.poly -- ref copy!
|
local poly = theZone.poly -- ref copy!
|
||||||
-- create the four points
|
-- create the four points
|
||||||
local ll = cfxZones.createPointFromPoint(poly[1])
|
local p = cfxZones.createPointFromPoint(poly[1])
|
||||||
local lr = cfxZones.createPointFromPoint(poly[1])
|
|
||||||
local ul = cfxZones.createPointFromPoint(poly[1])
|
|
||||||
local ur = cfxZones.createPointFromPoint(poly[1])
|
|
||||||
|
|
||||||
local pRad = dcsCommon.dist(theZone.point, poly[1]) -- rRad is radius for polygon from theZone.point
|
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
|
-- now iterate through all points and adjust bounds accordingly
|
||||||
for v=2, #poly do
|
local lx, ly, mx, my = p.x, p.z, p.x, p.z
|
||||||
local vertex = poly[v]
|
for vtx=1, #poly do
|
||||||
if (vertex.x < ll.x) then ll.x = vertex.x; ul.x = vertex.x end
|
local v = poly[vtx]
|
||||||
if (vertex.x > lr.x) then lr.x = vertex.x; ur.x = vertex.x end
|
if v.x < lx then lx = v.x end
|
||||||
if (vertex.z < ul.z) then ul.z = vertex.z; ur.z = vertex.z end
|
if v.x > mx then mx = v.x end
|
||||||
--if (vertex.z > ll.z) then ll.z = vertex.z; lr.z = vertex.z end
|
if v.z < ly then ly = v.z end
|
||||||
if (vertex.z > ur.z) then ur.z = vertex.z; ul.z = vertex.z end
|
if v.z > my then my = v.z end
|
||||||
local dp = dcsCommon.dist(theZone.point, vertex)
|
local dp = dcsCommon.dist(theZone.point, v)
|
||||||
if dp > pRad then pRad = dp end -- find largst distance to vertex
|
if dp > pRad then pRad = dp end -- find largst distance to vertex
|
||||||
end
|
end
|
||||||
|
|
||||||
-- now keep the new point references
|
theZone.bounds.ul = dcsCommon.createPoint(lx, 0, my)
|
||||||
-- and store them in the zone's bounds
|
theZone.bounds.ur = dcsCommon.createPoint(mx, 0, my)
|
||||||
bounds.ll = ll
|
theZone.bounds.ll = dcsCommon.createPoint(lx, 0, ly)
|
||||||
bounds.lr = lr
|
theZone.bounds.lr = dcsCommon.createPoint(mx, 0, ly)
|
||||||
bounds.ul = ul
|
|
||||||
bounds.ur = ur
|
|
||||||
-- we may need to ascertain why we need ul, ur, ll, lr instead of just ll and ur
|
|
||||||
-- store pRad
|
-- store pRad
|
||||||
theZone.pRad = pRad -- not sure we'll ever need that, but at least we have it
|
theZone.pRad = pRad -- not sure we'll ever need that, but at least we have it
|
||||||
|
|
||||||
else
|
else
|
||||||
-- huston, we have a problem
|
-- huston, we have a problem
|
||||||
if cfxZones.verbose then
|
if cfxZones.verbose then
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
cloneZones = {}
|
cloneZones = {}
|
||||||
cloneZones.version = "2.2.1"
|
cloneZones.version = "2.3.0"
|
||||||
cloneZones.verbose = false
|
cloneZones.verbose = false
|
||||||
cloneZones.requiredLibs = {
|
cloneZones.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
@ -52,6 +52,10 @@ cloneZones.respawnOnGroupID = true
|
|||||||
- persistence: persist oSize and set lastSize
|
- persistence: persist oSize and set lastSize
|
||||||
2.2.1 - verbosity updates for post-check
|
2.2.1 - verbosity updates for post-check
|
||||||
- if cloned group is late activation, turn it off
|
- 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")
|
theZone.deSpawnFlag = theZone:getStringFromZoneProperty( "deClone?", "none")
|
||||||
elseif theZone:hasProperty("wipe?") then
|
elseif theZone:hasProperty("wipe?") then
|
||||||
theZone.deSpawnFlag = theZone:getStringFromZoneProperty("wipe?", "none")
|
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
|
end
|
||||||
|
|
||||||
if theZone.deSpawnFlag then
|
if theZone.deSpawnFlag then
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
csarManager = {}
|
csarManager = {}
|
||||||
csarManager.version = "3.4.0"
|
csarManager.version = "4.0.0"
|
||||||
csarManager.ups = 1
|
csarManager.ups = 1
|
||||||
|
|
||||||
--[[-- VERSION HISTORY
|
--[[-- VERSION HISTORY
|
||||||
@ -45,6 +45,7 @@ csarManager.ups = 1
|
|||||||
3.3.0 - persistence support
|
3.3.0 - persistence support
|
||||||
3.4.0 - global timeLimit option in config zone
|
3.4.0 - global timeLimit option in config zone
|
||||||
- fixes expiration bug when persisting data
|
- fixes expiration bug when persisting data
|
||||||
|
4.0.0 - support for mainMenu
|
||||||
|
|
||||||
|
|
||||||
INTEGRATES AUTOMATICALLY WITH playerScore
|
INTEGRATES AUTOMATICALLY WITH playerScore
|
||||||
@ -713,8 +714,13 @@ function csarManager.setCommsMenu(theUnit)
|
|||||||
-- reset all coms now
|
-- reset all coms now
|
||||||
csarManager.removeCommsFromConfig(conf)
|
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
|
-- 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.
|
-- now we have a menu without submenus.
|
||||||
-- add our own submenus
|
-- add our own submenus
|
||||||
@ -1619,6 +1625,21 @@ function csarManager.readConfigZone()
|
|||||||
csarManager.timeLimit = nil
|
csarManager.timeLimit = nil
|
||||||
end
|
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
|
if csarManager.verbose then
|
||||||
trigger.action.outText("+++csar: read config", 30)
|
trigger.action.outText("+++csar: read config", 30)
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
dcsCommon = {}
|
dcsCommon = {}
|
||||||
dcsCommon.version = "3.0.7"
|
dcsCommon.version = "3.0.8"
|
||||||
--[[-- VERSION HISTORY
|
--[[-- VERSION HISTORY
|
||||||
3.0.0 - removed bad bug in stringStartsWith, only relevant if caseSensitive is false
|
3.0.0 - removed bad bug in stringStartsWith, only relevant if caseSensitive is false
|
||||||
- point2text new intsOnly option
|
- point2text new intsOnly option
|
||||||
@ -20,6 +20,9 @@ dcsCommon.version = "3.0.7"
|
|||||||
- new pointXpercentYdegOffAB()
|
- new pointXpercentYdegOffAB()
|
||||||
3.0.6 - new arrayContainsStringCaseInsensitive()
|
3.0.6 - new arrayContainsStringCaseInsensitive()
|
||||||
3.0.7 - fixed small bug in wildArrayContainsString
|
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
|
-- globals
|
||||||
dcsCommon.cbID = 0 -- callback id for simple callback scheduling
|
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.coalitionSides = {0, 1, 2}
|
||||||
dcsCommon.maxCountry = 86 -- number of countries defined in total
|
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
|
-- clone is a recursive clone which will also clone
|
||||||
-- deeper levels, as used in units
|
-- deeper levels, as used in units
|
||||||
function dcsCommon.clone(orig, stripMeta)
|
function dcsCommon.clone(orig, stripMeta)
|
||||||
|
--[[-- code seems to break with DCS 2.9
|
||||||
if not orig then return nil end
|
if not orig then return nil end
|
||||||
local orig_type = type(orig)
|
local orig_type = type(orig)
|
||||||
local copy
|
local copy
|
||||||
@ -1226,7 +1230,57 @@ dcsCommon.version = "3.0.7"
|
|||||||
copy = orig
|
copy = orig
|
||||||
end
|
end
|
||||||
return copy
|
return copy
|
||||||
|
--]]--
|
||||||
|
if stripMeta then
|
||||||
|
trigger.action.outText("+++common: warning: stripmetatable no longer supported.", 30)
|
||||||
end
|
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)
|
function dcsCommon.copyArray(inArray)
|
||||||
if not inArray then return nil end
|
if not inArray then return nil end
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
flareZone = {}
|
flareZone = {}
|
||||||
flareZone.version = "1.1.0"
|
flareZone.version = "1.2.0"
|
||||||
flareZone.verbose = false
|
flareZone.verbose = false
|
||||||
flareZone.name = "flareZone"
|
flareZone.name = "flareZone"
|
||||||
|
|
||||||
@ -8,6 +8,7 @@ flareZone.name = "flareZone"
|
|||||||
1.1.0 - improvements to verbosity
|
1.1.0 - improvements to verbosity
|
||||||
- OOP
|
- OOP
|
||||||
- small bugfix in doFlare assignment
|
- small bugfix in doFlare assignment
|
||||||
|
1.2.0 - new rndLoc attribute
|
||||||
--]]--
|
--]]--
|
||||||
flareZone.requiredLibs = {
|
flareZone.requiredLibs = {
|
||||||
"dcsCommon",
|
"dcsCommon",
|
||||||
@ -50,6 +51,8 @@ function flareZone.addFlareZone(theZone)
|
|||||||
|
|
||||||
theZone.salvoDurationL, theZone.salvoDurationH = theZone:getPositiveRangeFromZoneProperty("duration", 1)
|
theZone.salvoDurationL, theZone.salvoDurationH = theZone:getPositiveRangeFromZoneProperty("duration", 1)
|
||||||
|
|
||||||
|
theZone.rndLoc = theZone:getBoolFromZoneProperty("rndLoc", flase)
|
||||||
|
|
||||||
if theZone.verbose or flareZone.verbose then
|
if theZone.verbose or flareZone.verbose then
|
||||||
trigger.action.outText("+++flrZ: new flare <" .. theZone.name .. ">, color (" .. theZone.flareColor .. ")", 30)
|
trigger.action.outText("+++flrZ: new flare <" .. theZone.name .. ">, color (" .. theZone.flareColor .. ")", 30)
|
||||||
end
|
end
|
||||||
@ -59,15 +62,20 @@ end
|
|||||||
function flareZone.launch(theZone)
|
function flareZone.launch(theZone)
|
||||||
local color = theZone.flareColor
|
local color = theZone.flareColor
|
||||||
if color < 0 then color = math.random(4) - 1 end
|
if color < 0 then color = math.random(4) - 1 end
|
||||||
|
local loc
|
||||||
local loc = cfxZones.getPoint(theZone, true) -- with height
|
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
|
loc.y = loc.y + theZone.flareAlt
|
||||||
-- calculate azimuth
|
-- calculate azimuth
|
||||||
local azimuth = cfxZones.randomInRange(theZone.azimuthL, theZone.azimuthH) -- in deg
|
local azimuth = cfxZones.randomInRange(theZone.azimuthL, theZone.azimuthH) -- in deg
|
||||||
|
|
||||||
if flareZone.verbose or theZone.verbose then
|
-- 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)
|
-- trigger.action.outText("+++flrZ: launching <" .. theZone.name .. ">, c = " .. color .. " (" .. dcsCommon.flareColor2Text(color) .. "), azi <" .. azimuth .. "> [" .. theZone.azimuthL .. "-" .. theZone.azimuthH .. "]", 30)
|
||||||
end
|
-- end
|
||||||
azimuth = azimuth * 0.0174533 -- in rads
|
azimuth = azimuth * 0.0174533 -- in rads
|
||||||
|
|
||||||
trigger.action.signalFlare(loc, color, azimuth)
|
trigger.action.signalFlare(loc, color, azimuth)
|
||||||
@ -80,6 +88,9 @@ function flareZone.update()
|
|||||||
-- launch if flag banged
|
-- launch if flag banged
|
||||||
for idx, theZone in pairs(flareZone.flares) do
|
for idx, theZone in pairs(flareZone.flares) do
|
||||||
if cfxZones.testZoneFlag(theZone, theZone.doFlare, theZone.flareTriggerMethod, "lastDoFlare") then
|
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)
|
local salvo = cfxZones.randomInRange(theZone.salvoSizeL, theZone.salvoSizeH)
|
||||||
if salvo < 2 then
|
if salvo < 2 then
|
||||||
-- one-shot
|
-- one-shot
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
cfxGroundTroops = {}
|
cfxGroundTroops = {}
|
||||||
cfxGroundTroops.version = "2.2.0"
|
cfxGroundTroops.version = "2.2.1"
|
||||||
cfxGroundTroops.ups = 0.25 -- every 4 seconds
|
cfxGroundTroops.ups = 0.25 -- every 4 seconds
|
||||||
cfxGroundTroops.verbose = false
|
cfxGroundTroops.verbose = false
|
||||||
cfxGroundTroops.requiredLibs = {
|
cfxGroundTroops.requiredLibs = {
|
||||||
@ -34,6 +34,7 @@ cfxGroundTroops.jtacCB = {} -- jtac callbacks, to be implemented
|
|||||||
2.0.1 - small fiex ti checkPileUp()
|
2.0.1 - small fiex ti checkPileUp()
|
||||||
2.1.0 - captureandhold - oneshot attackowned
|
2.1.0 - captureandhold - oneshot attackowned
|
||||||
2.2.0 - moveFormation support
|
2.2.0 - moveFormation support
|
||||||
|
2.2.1 - reduced verbosity
|
||||||
|
|
||||||
an entry into the deployed troop table has the following attributes
|
an entry into the deployed troop table has the following attributes
|
||||||
- group - the group
|
- group - the group
|
||||||
@ -926,7 +927,7 @@ function cfxGroundTroops.createGroundTroops(inGroup, range, orders, moveFormatio
|
|||||||
if orders:lower() == "lase" then
|
if orders:lower() == "lase" then
|
||||||
orders = "laze" -- we use WRONG spelling here, cause we're cool. yeah, right.
|
orders = "laze" -- we use WRONG spelling here, cause we're cool. yeah, right.
|
||||||
end
|
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.insideDestination = false
|
||||||
newTroops.unscheduleCount = 0 -- will count up as we aren't scheduled
|
newTroops.unscheduleCount = 0 -- will count up as we aren't scheduled
|
||||||
newTroops.speedWarning = 0
|
newTroops.speedWarning = 0
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
guardianAngel = {}
|
guardianAngel = {}
|
||||||
guardianAngel.version = "3.0.5"
|
guardianAngel.version = "3.0.6"
|
||||||
guardianAngel.ups = 10
|
guardianAngel.ups = 10
|
||||||
guardianAngel.name = "Guardian Angel" -- just in case someone accesses .name
|
guardianAngel.name = "Guardian Angel" -- just in case someone accesses .name
|
||||||
guardianAngel.launchWarning = true -- detect launches and warn pilot
|
guardianAngel.launchWarning = true -- detect launches and warn pilot
|
||||||
@ -65,6 +65,7 @@ guardianAngel.requiredLibs = {
|
|||||||
- msgTime to control how long warnings remain on the screen
|
- msgTime to control how long warnings remain on the screen
|
||||||
- disappear message now only on verbose
|
- disappear message now only on verbose
|
||||||
- dmlZones
|
- dmlZones
|
||||||
|
3.0.6 - Hardening of targets that aren't part of groups
|
||||||
|
|
||||||
|
|
||||||
This script detects missiles launched against protected aircraft an
|
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)
|
trigger.action.outText("gA: tracking missile <" .. wName .. "> launched by <" .. launcherName .. ">", guardianAngel.msgTime)
|
||||||
end
|
end
|
||||||
theItem.theTarget = theTarget
|
theItem.theTarget = theTarget
|
||||||
|
if theTarget.getGroup then -- some targets may not have a group
|
||||||
theItem.tGroup = theTarget:getGroup()
|
theItem.tGroup = theTarget:getGroup()
|
||||||
theItem.tID = theItem.tGroup:getID()
|
theItem.tID = theItem.tGroup:getID()
|
||||||
|
else
|
||||||
|
theItem.tGroup = nil
|
||||||
|
theItem.tID = nil
|
||||||
|
end
|
||||||
theItem.targetName = theTarget:getName()
|
theItem.targetName = theTarget:getName()
|
||||||
theItem.launchTimeStamp = timer.getTime()
|
theItem.launchTimeStamp = timer.getTime()
|
||||||
theItem.lastDistance = math.huge
|
theItem.lastDistance = math.huge
|
||||||
@ -244,24 +249,10 @@ function guardianAngel.monitorItem(theItem)
|
|||||||
local ID = theItem.tID
|
local ID = theItem.tID
|
||||||
if not w then return false end
|
if not w then return false end
|
||||||
if not w:isExist() then
|
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
|
if guardianAngel.verbose then
|
||||||
trigger.action.outText("+++gA: missile disappeared: <" .. theItem.weaponName .. ">, aimed at <" .. theItem.targetName .. ">",30)
|
trigger.action.outText("+++gA: missile disappeared: <" .. theItem.weaponName .. ">, aimed at <" .. theItem.targetName .. ">",30)
|
||||||
end
|
end
|
||||||
|
|
||||||
guardianAngel.invokeCallbacks("disappear", theItem.targetName, theItem.weaponName)
|
guardianAngel.invokeCallbacks("disappear", theItem.targetName, theItem.weaponName)
|
||||||
-- end
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -302,7 +293,7 @@ function guardianAngel.monitorItem(theItem)
|
|||||||
|
|
||||||
if isThreat and guardianAngel.announcer and guardianAngel.active then
|
if isThreat and guardianAngel.announcer and guardianAngel.active then
|
||||||
local desc = "Missile, missile, missile - now heading for " .. ctName .. "!"
|
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)
|
trigger.action.outTextForGroup(ID, desc, guardianAngel.msgTime)
|
||||||
if guardianAngel.launchSound then
|
if guardianAngel.launchSound then
|
||||||
local fileName = "l10n/DEFAULT/" .. guardianAngel.launchSound
|
local fileName = "l10n/DEFAULT/" .. guardianAngel.launchSound
|
||||||
@ -364,7 +355,7 @@ function guardianAngel.monitorItem(theItem)
|
|||||||
then
|
then
|
||||||
desc = desc .. " ANGEL INTERVENTION"
|
desc = desc .. " ANGEL INTERVENTION"
|
||||||
|
|
||||||
if guardianAngel.announcer then
|
if guardianAngel.announcer and ID then
|
||||||
if guardianAngel.private then
|
if guardianAngel.private then
|
||||||
trigger.action.outTextForGroup(ID, desc, guardianAngel.msgTime)
|
trigger.action.outTextForGroup(ID, desc, guardianAngel.msgTime)
|
||||||
if guardianAngel.interventionSound then
|
if guardianAngel.interventionSound then
|
||||||
@ -399,7 +390,7 @@ function guardianAngel.monitorItem(theItem)
|
|||||||
--if theItem.lostTrack then desc = desc .. " (little sneak!)" end
|
--if theItem.lostTrack then desc = desc .. " (little sneak!)" end
|
||||||
--if theItem.missed then desc = desc .. " (missed you!)" end
|
--if theItem.missed then desc = desc .. " (missed you!)" end
|
||||||
|
|
||||||
if guardianAngel.announcer then
|
if guardianAngel.announcer and ID then
|
||||||
if guardianAngel.private then
|
if guardianAngel.private then
|
||||||
trigger.action.outTextForGroup(ID, desc, guardianAngel.msgTime)
|
trigger.action.outTextForGroup(ID, desc, guardianAngel.msgTime)
|
||||||
else
|
else
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
jtacGrpUI = {}
|
jtacGrpUI = {}
|
||||||
jtacGrpUI.version = "2.0.0"
|
jtacGrpUI.version = "3.0.0"
|
||||||
jtacGrpUI.requiredLibs = {
|
jtacGrpUI.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
"cfxZones",
|
"cfxZones",
|
||||||
@ -11,6 +11,8 @@ jtacGrpUI.requiredLibs = {
|
|||||||
- eliminated cfxPlayer dependence
|
- eliminated cfxPlayer dependence
|
||||||
- clean-up
|
- clean-up
|
||||||
- jtacSound
|
- jtacSound
|
||||||
|
3.0.0 - support for attachTo:
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
-- find & command cfxGroundTroops-based jtacs
|
-- find & command cfxGroundTroops-based jtacs
|
||||||
-- UI installed via OTHER for all groups with players
|
-- 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 Group.isExist(theGroup) then return end
|
||||||
if not jtacGrpUI.isEligibleForMenu(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)
|
local conf = jtacGrpUI.getConfigForGroup(theGroup)
|
||||||
conf.id = theGroup:getID(); -- we always do this ALWAYS
|
conf.id = theGroup:getID(); -- we always do this ALWAYS
|
||||||
|
|
||||||
@ -151,7 +158,7 @@ function jtacGrpUI.setCommsMenu(theGroup)
|
|||||||
if not conf.myMainMenu then
|
if not conf.myMainMenu then
|
||||||
local commandTxt = "jtac Lasing Report"
|
local commandTxt = "jtac Lasing Report"
|
||||||
local theCommand = missionCommands.addCommandForGroup(
|
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
|
conf.myMainMenu = theCommand
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -160,7 +167,7 @@ function jtacGrpUI.setCommsMenu(theGroup)
|
|||||||
|
|
||||||
-- ok, first, if we don't have an F-10 menu, create one
|
-- ok, first, if we don't have an F-10 menu, create one
|
||||||
if not (conf.myMainMenu) then
|
if not (conf.myMainMenu) then
|
||||||
conf.myMainMenu = missionCommands.addSubMenuForGroup(conf.id, 'jtac')
|
conf.myMainMenu = missionCommands.addSubMenuForGroup(conf.id, 'jtac', mainMenu)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- clear out existing commands
|
-- clear out existing commands
|
||||||
@ -317,12 +324,27 @@ function jtacGrpUI.readConfigZone()
|
|||||||
if not theZone then
|
if not theZone then
|
||||||
theZone = cfxZones.createSimpleZone("jtacGrpUIConfig")
|
theZone = cfxZones.createSimpleZone("jtacGrpUIConfig")
|
||||||
end
|
end
|
||||||
|
jtacGrpUI.name = "jtacGrpUI"
|
||||||
|
|
||||||
jtacGrpUI.jtacTypes = theZone:getStringFromZoneProperty("jtacTypes", "all")
|
jtacGrpUI.jtacTypes = theZone:getStringFromZoneProperty("jtacTypes", "all")
|
||||||
jtacGrpUI.jtacTypes = string.lower(jtacGrpUI.jtacTypes)
|
jtacGrpUI.jtacTypes = string.lower(jtacGrpUI.jtacTypes)
|
||||||
|
|
||||||
jtacGrpUI.jtacSound = theZone:getStringFromZoneProperty("jtacSound", "UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav")
|
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
|
jtacGrpUI.verbose = theZone.verbose
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
launchPlatform = {}
|
launchPlatform = {}
|
||||||
launchPlatform.version = "0.0.0"
|
launchPlatform.version = "0.5.0"
|
||||||
launchPlatform.requiredLibs = {
|
launchPlatform.requiredLibs = {
|
||||||
"dcsCommon",
|
"dcsCommon",
|
||||||
"cfxZones",
|
"cfxZones",
|
||||||
@ -55,7 +55,9 @@ function launchPlatform.launchAtTargetZone(coa, tgtZone, theType) -- gets closes
|
|||||||
-- get closest launcher for target
|
-- get closest launcher for target
|
||||||
local tgtPoint = tgtZone:getPoint()
|
local tgtPoint = tgtZone:getPoint()
|
||||||
local src, dist = cfxZones.getClosestZone(tgtPoint, platforms)
|
local src, dist = cfxZones.getClosestZone(tgtPoint, platforms)
|
||||||
|
if launchPlatform.verbose then
|
||||||
trigger.action.outText("+++LP: chosen <" .. src.name .. "> as launch platform", 30)
|
trigger.action.outText("+++LP: chosen <" .. src.name .. "> as launch platform", 30)
|
||||||
|
end
|
||||||
|
|
||||||
local theLauncher = launchPlatform.launchForPlatform(coa, src, tgtPoint, tgtZone)
|
local theLauncher = launchPlatform.launchForPlatform(coa, src, tgtPoint, tgtZone)
|
||||||
if not theLauncher then
|
if not theLauncher then
|
||||||
@ -74,7 +76,9 @@ function launchPlatform.launchAtTargetZone(coa, tgtZone, theType) -- gets closes
|
|||||||
end
|
end
|
||||||
|
|
||||||
function launchPlatform.asynchRemovePlatform(args)
|
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)
|
local theGroup = Group.getByName(args)
|
||||||
if not theGroup then return end
|
if not theGroup then return end
|
||||||
Group.destroy(theGroup)
|
Group.destroy(theGroup)
|
||||||
@ -83,11 +87,11 @@ end
|
|||||||
function launchPlatform.createData(thePoint, theTarget, targetZone, radius, name, num, wType)
|
function launchPlatform.createData(thePoint, theTarget, targetZone, radius, name, num, wType)
|
||||||
-- if present, we can use targetZone with some intelligence
|
-- if present, we can use targetZone with some intelligence
|
||||||
if not thePoint then
|
if not thePoint then
|
||||||
trigger.action.outText("NO POINT", 30)
|
trigger.action.outText("+++LP: NO POINT", 30)
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
if not theTarget then
|
if not theTarget then
|
||||||
trigger.action.outText("NO TARGET", 30)
|
trigger.action.outText("+++LP: NO TARGET", 30)
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -168,7 +172,9 @@ function launchPlatform.createData(thePoint, theTarget, targetZone, radius, name
|
|||||||
-- if inside camp
|
-- if inside camp
|
||||||
local hiPrioTargets
|
local hiPrioTargets
|
||||||
if targetZone and targetZone.cloners and #targetZone.cloners > 0 then
|
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)
|
trigger.action.outText("+++LP: detected <" .. targetZone.name .. "> is camp with <" .. #targetZone.cloners .. "> res-points, re-targeting hi-prio", 30)
|
||||||
|
end
|
||||||
hiPrioTargets = targetZone.cloners
|
hiPrioTargets = targetZone.cloners
|
||||||
radius = radius / 10 -- much smaller error
|
radius = radius / 10 -- much smaller error
|
||||||
end
|
end
|
||||||
|
|||||||
@ -406,7 +406,7 @@ end
|
|||||||
-- Event Handler
|
-- Event Handler
|
||||||
--
|
--
|
||||||
function milWings:onEvent(theEvent)
|
function milWings:onEvent(theEvent)
|
||||||
if not theEvent then return end
|
if not theEvent then return end
|
||||||
if not theEvent.initiator then return end
|
if not theEvent.initiator then return end
|
||||||
local theUnit = theEvent.initiator
|
local theUnit = theEvent.initiator
|
||||||
if not theUnit.getGroup then return end
|
if not theUnit.getGroup then return end
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
cfxPlayerScore = {}
|
cfxPlayerScore = {}
|
||||||
cfxPlayerScore.version = "3.2.0"
|
cfxPlayerScore.version = "3.3.0"
|
||||||
cfxPlayerScore.name = "cfxPlayerScore" -- compatibility with flag bangers
|
cfxPlayerScore.name = "cfxPlayerScore" -- compatibility with flag bangers
|
||||||
cfxPlayerScore.badSound = "Death BRASS.wav"
|
cfxPlayerScore.badSound = "Death BRASS.wav"
|
||||||
cfxPlayerScore.scoreSound = "Quest Snare 3.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.0.2 - interface with ObjectDestructDetector for scoring scenery objects
|
||||||
3.1.0 - shared data for persistence
|
3.1.0 - shared data for persistence
|
||||||
3.2.0 - integration with bank
|
3.2.0 - integration with bank
|
||||||
|
3.3.0 - case INsensitivity for all typeScore objects
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
cfxPlayerScore.requiredLibs = {
|
cfxPlayerScore.requiredLibs = {
|
||||||
@ -34,7 +35,7 @@ cfxPlayerScore.killZones = {} -- when set, kills only count here
|
|||||||
-- typeScore: dictionary sorted by typeString for score
|
-- typeScore: dictionary sorted by typeString for score
|
||||||
-- extend to add more types. It is used by unitType2score to
|
-- extend to add more types. It is used by unitType2score to
|
||||||
-- determine the base unit score
|
-- determine the base unit score
|
||||||
cfxPlayerScore.typeScore = {}
|
cfxPlayerScore.typeScore = {} -- ALL UPPERCASE NOW!!!
|
||||||
cfxPlayerScore.lastPlayerLanding = {} -- timestamp, by player name
|
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.delayBetweenLandings = 30 -- seconds to count as separate landings, also set during take-off to prevent janky t/o to count.
|
||||||
cfxPlayerScore.aircraft = 50
|
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 inVictim then return 0 end
|
||||||
if not killSide then killSide = -1 end
|
if not killSide then killSide = -1 end
|
||||||
local inName = inVictim:getName()
|
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
|
if dcsCommon.isSceneryObject(inVictim) then
|
||||||
local desc = inVictim:getDesc()
|
local desc = inVictim:getDesc()
|
||||||
if not desc then return 0 end
|
if not desc then
|
||||||
|
return 0
|
||||||
|
end
|
||||||
-- same as object destruct detector to
|
-- same as object destruct detector to
|
||||||
-- avoid ID changes
|
-- avoid ID changes
|
||||||
inName = desc.typeName
|
inName = desc.typeName
|
||||||
@ -231,14 +237,19 @@ function cfxPlayerScore.object2score(inVictim, killSide) -- does not have group
|
|||||||
if type(inName) == "number" then
|
if type(inName) == "number" then
|
||||||
inName = tostring(inName)
|
inName = tostring(inName)
|
||||||
end
|
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
|
-- since 2.7x DCS turns units into static objects for
|
||||||
-- cooking off, so first thing we need to do is do a name check
|
-- 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
|
if not objectScore then
|
||||||
-- try the type desc
|
-- try the type desc
|
||||||
local theType = inVictim:getTypeName()
|
local theType = inVictim:getTypeName()
|
||||||
objectScore = cfxPlayerScore.typeScore[theType]
|
if theType then
|
||||||
|
objectScore = cfxPlayerScore.typeScore[theType:upper()]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if type(objectScore) == "string" then
|
if type(objectScore) == "string" then
|
||||||
@ -272,17 +283,17 @@ function cfxPlayerScore.unit2score(inUnit)
|
|||||||
-- simply extend by adding items to the typescore table.concat
|
-- simply extend by adding items to the typescore table.concat
|
||||||
-- we first try by unit name. This allows individual
|
-- we first try by unit name. This allows individual
|
||||||
-- named hi-value targets to have individual scores
|
-- 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
|
-- see if all members of group score
|
||||||
if not uScore then
|
if (not uScore) and vicGroup then
|
||||||
local grpName = vicGroup:getName()
|
local grpName = vicGroup:getName()
|
||||||
uScore = cfxPlayerScore.typeScore[grpName]
|
uScore = cfxPlayerScore.typeScore[grpName:upper()]
|
||||||
end
|
end
|
||||||
|
|
||||||
if uScore == nil then
|
if not uScore then
|
||||||
-- WE NOW TRY TO ACCESS BY VICTIM'S TYPE STRING
|
-- WE NOW TRY TO ACCESS BY VICTIM'S TYPE STRING
|
||||||
uScore = cfxPlayerScore.typeScore[vicType]
|
uScore = cfxPlayerScore.typeScore[vicType:upper()]
|
||||||
else
|
else
|
||||||
|
|
||||||
end
|
end
|
||||||
@ -291,7 +302,7 @@ function cfxPlayerScore.unit2score(inUnit)
|
|||||||
uScore = tonumber(uScore)
|
uScore = tonumber(uScore)
|
||||||
end
|
end
|
||||||
|
|
||||||
if uScore == nil then uScore = 0 end
|
if not uScore then uScore = 0 end
|
||||||
if uScore > 0 then return uScore end
|
if uScore > 0 then return uScore end
|
||||||
|
|
||||||
-- only apply base scores when the lookup did not give a result
|
-- only apply base scores when the lookup did not give a result
|
||||||
@ -546,13 +557,13 @@ function cfxPlayerScore.isNamedUnit(theUnit)
|
|||||||
local theName = "(cfx_none)"
|
local theName = "(cfx_none)"
|
||||||
if type(theUnit) == "string" then
|
if type(theUnit) == "string" then
|
||||||
theName = theUnit -- direct name assignment
|
theName = theUnit -- direct name assignment
|
||||||
-- WARNING: NO EXIST CHECK DONE!
|
|
||||||
else
|
else
|
||||||
|
-- WARNING: NO EXIST CHECK DONE!
|
||||||
-- after kill, unit is dead, so will no longer exist!
|
-- after kill, unit is dead, so will no longer exist!
|
||||||
theName = theUnit:getName()
|
theName = theUnit:getName()
|
||||||
if not theName then return false end
|
if not theName then return false end
|
||||||
end
|
end
|
||||||
if cfxPlayerScore.typeScore[theName] then
|
if cfxPlayerScore.typeScore[theName:upper()] then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
@ -743,6 +754,9 @@ function cfxPlayerScore.killDetected(theEvent)
|
|||||||
-- we are only getting called when and if
|
-- we are only getting called when and if
|
||||||
-- a kill occured and killer was a player
|
-- a kill occured and killer was a player
|
||||||
-- and target exists
|
-- and target exists
|
||||||
|
if cfxPlayerScore.verbose then
|
||||||
|
trigger.action.outText("+++PScr: enter kill detected", 30)
|
||||||
|
end
|
||||||
local killer = theEvent.initiator
|
local killer = theEvent.initiator
|
||||||
local killerName = killer:getPlayerName()
|
local killerName = killer:getPlayerName()
|
||||||
if not killerName then killerName = "<nil>" end
|
if not killerName then killerName = "<nil>" end
|
||||||
@ -757,6 +771,9 @@ function cfxPlayerScore.killDetected(theEvent)
|
|||||||
-- was it a scenery object?
|
-- was it a scenery object?
|
||||||
local wasBuilding = dcsCommon.isSceneryObject(victim)
|
local wasBuilding = dcsCommon.isSceneryObject(victim)
|
||||||
if wasBuilding then
|
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
|
-- these objects have no coalition; we simply award the score if
|
||||||
-- it exists in look-up table.
|
-- it exists in look-up table.
|
||||||
local staticScore = cfxPlayerScore.object2score(victim, killSide)
|
local staticScore = cfxPlayerScore.object2score(victim, killSide)
|
||||||
@ -1431,8 +1448,19 @@ function cfxPlayerScore.start()
|
|||||||
-- identify and process a score table zones
|
-- identify and process a score table zones
|
||||||
local theZone = cfxZones.getZoneByName("playerScoreTable")
|
local theZone = cfxZones.getZoneByName("playerScoreTable")
|
||||||
if theZone then
|
if theZone then
|
||||||
|
-- trigger.action.outText("Reading custom player score table", 30)
|
||||||
-- read all into my types registry, replacing whatever is there
|
-- 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
|
end
|
||||||
|
|
||||||
-- read score tiggers and values
|
-- read score tiggers and values
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
radioMenu = {}
|
radioMenu = {}
|
||||||
radioMenu.version = "2.3.0"
|
radioMenu.version = "3.0.0"
|
||||||
radioMenu.verbose = false
|
radioMenu.verbose = false
|
||||||
radioMenu.ups = 1
|
radioMenu.ups = 1
|
||||||
radioMenu.requiredLibs = {
|
radioMenu.requiredLibs = {
|
||||||
@ -7,6 +7,7 @@ radioMenu.requiredLibs = {
|
|||||||
"cfxZones", -- Zones, of course
|
"cfxZones", -- Zones, of course
|
||||||
}
|
}
|
||||||
radioMenu.menus = {}
|
radioMenu.menus = {}
|
||||||
|
radioMenu.mainMenus = {} -- dict
|
||||||
|
|
||||||
--[[--
|
--[[--
|
||||||
Version History
|
Version History
|
||||||
@ -21,12 +22,19 @@ radioMenu.menus = {}
|
|||||||
2.2.1 - corrected ackD
|
2.2.1 - corrected ackD
|
||||||
2.3.0 - added wildcard "*" ability for group name match
|
2.3.0 - added wildcard "*" ability for group name match
|
||||||
- added ackASnd .. ackDSnd sounds as options
|
- added ackASnd .. ackDSnd sounds as options
|
||||||
|
3.0.0 - new radioMainMenu and attachTo: mechanics
|
||||||
|
cascading radioMainMenu support
|
||||||
|
detect cyclic references
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
function radioMenu.addRadioMenu(theZone)
|
function radioMenu.addRadioMenu(theZone)
|
||||||
table.insert(radioMenu.menus, theZone)
|
table.insert(radioMenu.menus, theZone)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function radioMenu.addRadioMainMenu(theZone)
|
||||||
|
radioMenu.mainMenus[theZone.name] = theZone
|
||||||
|
end
|
||||||
|
|
||||||
function radioMenu.getRadioMenuByName(aName)
|
function radioMenu.getRadioMenuByName(aName)
|
||||||
for idx, aZone in pairs(radioMenu.menus) do
|
for idx, aZone in pairs(radioMenu.menus) do
|
||||||
if aName == aZone.name then return aZone end
|
if aName == aZone.name then return aZone end
|
||||||
@ -38,6 +46,10 @@ function radioMenu.getRadioMenuByName(aName)
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function radioMenu.getRadioMainMenuByName(theName)
|
||||||
|
return radioMenu.mainMenus[theName]
|
||||||
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
-- read zone
|
-- read zone
|
||||||
--
|
--
|
||||||
@ -169,6 +181,9 @@ function radioMenu.installMenu(theZone)
|
|||||||
end
|
end
|
||||||
|
|
||||||
theZone.rootMenu = {}
|
theZone.rootMenu = {}
|
||||||
|
theZone.mainRoot = nil -- can be altered with attachTo
|
||||||
|
-- see if this menu has an attachTo attribute
|
||||||
|
|
||||||
theZone.mcdA = {}
|
theZone.mcdA = {}
|
||||||
theZone.mcdB = {}
|
theZone.mcdB = {}
|
||||||
theZone.mcdC = {}
|
theZone.mcdC = {}
|
||||||
@ -180,7 +195,11 @@ function radioMenu.installMenu(theZone)
|
|||||||
|
|
||||||
if theZone.menuGroup or theZone.menuTypes then
|
if theZone.menuGroup or theZone.menuTypes then
|
||||||
for idx, grp in pairs(gID) do
|
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.rootMenu[grp] = aRoot
|
||||||
theZone.mcdA[grp] = 0
|
theZone.mcdA[grp] = 0
|
||||||
theZone.mcdB[grp] = 0
|
theZone.mcdB[grp] = 0
|
||||||
@ -188,9 +207,17 @@ function radioMenu.installMenu(theZone)
|
|||||||
theZone.mcdD[grp] = 0
|
theZone.mcdD[grp] = 0
|
||||||
end
|
end
|
||||||
elseif theZone.coalition == 0 then
|
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
|
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
|
end
|
||||||
|
|
||||||
if theZone:hasProperty("itemA") then
|
if theZone:hasProperty("itemA") then
|
||||||
@ -253,6 +280,18 @@ end
|
|||||||
function radioMenu.createRadioMenuWithZone(theZone)
|
function radioMenu.createRadioMenuWithZone(theZone)
|
||||||
theZone.rootName = theZone:getStringFromZoneProperty("radioMenu", "<No Name>")
|
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)
|
theZone.coalition = theZone:getCoalitionFromZoneProperty("coalition", 0)
|
||||||
-- groups / types
|
-- groups / types
|
||||||
if theZone:hasProperty("group") then
|
if theZone:hasProperty("group") then
|
||||||
@ -350,6 +389,116 @@ function radioMenu.createRadioMenuWithZone(theZone)
|
|||||||
end
|
end
|
||||||
|
|
||||||
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
|
-- Output processing
|
||||||
@ -546,17 +695,9 @@ end
|
|||||||
function radioMenu.readConfigZone()
|
function radioMenu.readConfigZone()
|
||||||
local theZone = cfxZones.getZoneByName("radioMenuConfig")
|
local theZone = cfxZones.getZoneByName("radioMenuConfig")
|
||||||
if not theZone then
|
if not theZone then
|
||||||
if radioMenu.verbose then
|
|
||||||
trigger.action.outText("+++radioMenu: NO config zone!", 30)
|
|
||||||
end
|
|
||||||
theZone = cfxZones.createSimpleZone("radioMenuConfig")
|
theZone = cfxZones.createSimpleZone("radioMenuConfig")
|
||||||
end
|
end
|
||||||
|
|
||||||
radioMenu.verbose = theZone:getBoolFromZoneProperty("verbose", false)
|
radioMenu.verbose = theZone:getBoolFromZoneProperty("verbose", false)
|
||||||
|
|
||||||
if radioMenu.verbose then
|
|
||||||
trigger.action.outText("+++radioMenu: read config", 30)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function radioMenu.start()
|
function radioMenu.start()
|
||||||
@ -572,10 +713,45 @@ function radioMenu.start()
|
|||||||
-- read config
|
-- read config
|
||||||
radioMenu.readConfigZone()
|
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
|
-- process radioMenu Zones
|
||||||
-- old style
|
-- old style
|
||||||
local attrZones = cfxZones.getZonesWithAttributeNamed("radioMenu")
|
local rmZones = cfxZones.getZonesWithAttributeNamed("radioMenu")
|
||||||
for k, aZone in pairs(attrZones) do
|
for k, aZone in pairs(rmZones) do
|
||||||
radioMenu.createRadioMenuWithZone(aZone) -- process attributes
|
radioMenu.createRadioMenuWithZone(aZone) -- process attributes
|
||||||
radioMenu.addRadioMenu(aZone) -- add to list
|
radioMenu.addRadioMenu(aZone) -- add to list
|
||||||
end
|
end
|
||||||
@ -595,4 +771,8 @@ end
|
|||||||
|
|
||||||
--[[--
|
--[[--
|
||||||
check CD/standby code for multiple groups
|
check CD/standby code for multiple groups
|
||||||
|
|
||||||
|
add/remove for mainmenu
|
||||||
|
visible/invisible for main menus
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
708
modules/reaper.lua
Normal file
708
modules/reaper.lua
Normal 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?
|
||||||
|
--]]--
|
||||||
@ -1,5 +1,5 @@
|
|||||||
scribe = {}
|
scribe = {}
|
||||||
scribe.version = "1.1.0"
|
scribe.version = "2.0.0"
|
||||||
scribe.requiredLibs = {
|
scribe.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
"cfxZones", -- Zones, of course
|
"cfxZones", -- Zones, of course
|
||||||
@ -12,6 +12,7 @@ VERSION HISTORY
|
|||||||
1.0.0 Initial Version
|
1.0.0 Initial Version
|
||||||
1.0.1 postponed land, postponed takeoff, unit_lost
|
1.0.1 postponed land, postponed takeoff, unit_lost
|
||||||
1.1.0 supports persistence's SHARED ability to share data across missions
|
1.1.0 supports persistence's SHARED ability to share data across missions
|
||||||
|
2.0.0 support for main menu
|
||||||
--]]--
|
--]]--
|
||||||
scribe.verbose = true
|
scribe.verbose = true
|
||||||
scribe.db = {} -- indexed by player name
|
scribe.db = {} -- indexed by player name
|
||||||
@ -488,6 +489,12 @@ function scribe.startPlayerGUI()
|
|||||||
-- note: currently assumes single-player groups
|
-- note: currently assumes single-player groups
|
||||||
-- in preparation of single-player 'commandForUnit'
|
-- in preparation of single-player 'commandForUnit'
|
||||||
-- ASSUMES SINGLE-UNIT PLAYER GROUPS!
|
-- 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
|
for uName, uData in pairs(cfxMX.playerUnitByName) do
|
||||||
local unitInfo = {}
|
local unitInfo = {}
|
||||||
-- try and access each unit even if we know that the
|
-- try and access each unit even if we know that the
|
||||||
@ -508,7 +515,7 @@ function scribe.startPlayerGUI()
|
|||||||
unitInfo.uID = uData.unitId
|
unitInfo.uID = uData.unitId
|
||||||
unitInfo.theType = theType
|
unitInfo.theType = theType
|
||||||
unitInfo.cat = cfxMX.groupTypeByName[gName]
|
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)
|
unitInfo.checkData = missionCommands.addCommandForGroup(unitInfo.gID, "Get Pilot's Statistics", unitInfo.root, scribe.redirectCheckData, unitInfo)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -524,6 +531,24 @@ function scribe.readConfigZone()
|
|||||||
scribe.verbose = theZone.verbose
|
scribe.verbose = theZone.verbose
|
||||||
scribe.hasGUI = theZone:getBoolFromZoneProperty("hasGUI", true)
|
scribe.hasGUI = theZone:getBoolFromZoneProperty("hasGUI", true)
|
||||||
scribe.uiMenu = theZone:getStringFromZoneProperty("uiMenu", "Mission Logbook")
|
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.greetPlayer = theZone:getBoolFromZoneProperty("greetPlayer", true)
|
||||||
scribe.byePlayer = theZone:getBoolFromZoneProperty("byebyePlayer", true)
|
scribe.byePlayer = theZone:getBoolFromZoneProperty("byebyePlayer", true)
|
||||||
scribe.landings = theZone:getBoolFromZoneProperty("landings", true)
|
scribe.landings = theZone:getBoolFromZoneProperty("landings", true)
|
||||||
|
|||||||
@ -467,7 +467,7 @@ function unitPersistence.loadMission()
|
|||||||
end
|
end
|
||||||
|
|
||||||
if mismatchWarning then
|
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
|
end
|
||||||
-- set mission according to data received from last save
|
-- set mission according to data received from last save
|
||||||
if unitPersistence.verbose then
|
if unitPersistence.verbose then
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
williePete = {}
|
williePete = {}
|
||||||
williePete.version = "2.0.2"
|
williePete.version = "2.0.3"
|
||||||
williePete.ups = 10 -- we update at 10 fps, so accuracy of a
|
williePete.ups = 10 -- we update at 10 fps, so accuracy of a
|
||||||
-- missile moving at Mach 2 is within 33 meters,
|
-- missile moving at Mach 2 is within 33 meters,
|
||||||
-- with interpolation even at 3 meters
|
-- with interpolation even at 3 meters
|
||||||
@ -19,6 +19,7 @@ williePete.requiredLibs = {
|
|||||||
- getFirstLivingPlayerInGroupNamed()
|
- getFirstLivingPlayerInGroupNamed()
|
||||||
2.0.1 - added Harrier's FFAR M156 WP
|
2.0.1 - added Harrier's FFAR M156 WP
|
||||||
2.0.2 - hardened playerUpdate()
|
2.0.2 - hardened playerUpdate()
|
||||||
|
2.0.3 - further hardened playerUpdate()
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
williePete.willies = {}
|
williePete.willies = {}
|
||||||
@ -635,7 +636,7 @@ function williePete.playerUpdate()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
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
|
end
|
||||||
if dropUnit then
|
if dropUnit then
|
||||||
-- all outside, remove from zone check-in
|
-- all outside, remove from zone check-in
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
wiper = {}
|
wiper = {}
|
||||||
wiper.version = "1.2.0"
|
wiper.version = "1.3.0"
|
||||||
wiper.verbose = false
|
wiper.verbose = false
|
||||||
wiper.ups = 1
|
wiper.ups = 1
|
||||||
wiper.requiredLibs = {
|
wiper.requiredLibs = {
|
||||||
@ -15,6 +15,9 @@ wiper.wipers = {}
|
|||||||
- categories can now be a list
|
- categories can now be a list
|
||||||
- declutter opetion
|
- declutter opetion
|
||||||
- if first category is 'none', zone will not wipe at all but may declutter
|
- 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)
|
function wiper.createWiperWithZone(theZone)
|
||||||
theZone.triggerWiperFlag = theZone:getStringFromZoneProperty("wipe?", "*<none>")
|
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
|
-- triggerWiperMethod
|
||||||
theZone.triggerWiperMethod = theZone:getStringFromZoneProperty("triggerMethod", "change")
|
theZone.triggerWiperMethod = theZone:getStringFromZoneProperty("triggerMethod", "change")
|
||||||
if theZone:hasProperty("triggerWiperMethod") then
|
if theZone:hasProperty("triggerWiperMethod") then
|
||||||
@ -98,7 +106,7 @@ function wiper.createWiperWithZone(theZone)
|
|||||||
end
|
end
|
||||||
theDict[shortName] = ew
|
theDict[shortName] = ew
|
||||||
if wiper.verbose or theZone.verbose then
|
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
|
||||||
end
|
end
|
||||||
theZone.wipeNamed = theDict
|
theZone.wipeNamed = theDict
|
||||||
@ -106,6 +114,10 @@ function wiper.createWiperWithZone(theZone)
|
|||||||
|
|
||||||
theZone.wipeInventory = theZone:getBoolFromZoneProperty("wipeInventory", false)
|
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
|
if wiper.verbose or theZone.verbose then
|
||||||
trigger.action.outText("+++wpr: new wiper zone <".. theZone.name ..">", 30)
|
trigger.action.outText("+++wpr: new wiper zone <".. theZone.name ..">", 30)
|
||||||
end
|
end
|
||||||
|
|||||||
BIN
tutorial & demo missions/demo - What's on the cascading menu.miz
Normal file
BIN
tutorial & demo missions/demo - What's on the cascading menu.miz
Normal file
Binary file not shown.
BIN
tutorial & demo missions/demo - reaper, man.miz
Normal file
BIN
tutorial & demo missions/demo - reaper, man.miz
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user