Version 2.2.6

Reaper and Kiowa synch, sweeper
This commit is contained in:
Christian Franz 2024-06-20 12:52:21 +02:00
parent 2b6491b978
commit 0aca69967c
13 changed files with 415 additions and 213 deletions

Binary file not shown.

Binary file not shown.

View File

@ -1,5 +1,5 @@
autoCSAR = {}
autoCSAR.version = "2.1.0"
autoCSAR.version = "2.2.0"
autoCSAR.requiredLibs = {
"dcsCommon", -- always
"cfxZones", -- Zones, of course
@ -17,7 +17,26 @@ autoCSAR.trackedEjects = {} -- we start tracking on eject
2.0.1 - fix for coalition change when ejected player changes coas or is forced to neutral
- GC
2.1.0 - persistence support
2.2.0 - new noExploit option in config
- no csar mission if pilot lands too close to airbase or farp
and noExploit is on
--]]--
autoCSAR.forbidden = {} -- indexed by name, contains point
autoCSAR.killDist = 2100 -- meters from center of forbidden
function autoCSAR.collectForbiddenZones()
local allYourBase = world.getAirbases()
for idx, aBase in pairs(allYourBase) do
-- collect airbases and farps, ignore ships
local desc = aBase:getDesc()
local cat = desc.category
if cat == 0 or cat == 1 then
local name = aBase:getName()
local p = aBase:getPoint()
autoCSAR.forbidden[name] = p
end
end
end
function autoCSAR.removeGuy(args)
local theGuy = args.theGuy
@ -53,6 +72,28 @@ function autoCSAR.createNewCSAR(theUnit, coa)
if coa == 2 and not autoCSAR.blueCSAR then
return -- no blue rescue
end
-- noExploit burnup
if autoCSAR.noExploit then
local p = theUnit:getPoint()
local burned = false
for name, aPoint in pairs(autoCSAR.forbidden) do
local d = dcsCommon.distFlat(p, aPoint)
if d < autoCSAR.killDist then
if autoCSAR.verbose then
trigger.action.outText("+++aCSAR: BURNED ejection touchdown: too close to <" .. name .. ">", 30)
end
burned = true
end
end
if burned then
trigger.action.outText("Pilot made it safely to ground, and was taken into custody immediately", 30)
-- try and remove the guy now
Unit.destroy(theUnit)
return
end
end
-- end burnup code
-- for later expansion
local theGroup = theUnit:getGroup()
@ -130,7 +171,6 @@ function autoCSAR:onEvent(event)
if event.id == 6 then -- eject, start tracking, remember coa
local coa = event.initiator:getCoalition()
-- see if pilot has ejector seat and prepare to connect one with the other
local info = nil
if event.target and event.target:isExist() then
@ -161,9 +201,6 @@ function autoCSAR.readConfigZone()
local theZone = cfxZones.getZoneByName("autoCSARConfig")
if not theZone then
theZone = cfxZones.createSimpleZone("autoCSARConfig")
if autoCSAR.verbose then
trigger.action.outText("+++aCSAR: NO config zone!", 30)
end
end
autoCSAR.verbose = theZone.verbose
autoCSAR.redCSAR = theZone:getBoolFromZoneProperty("red", true)
@ -178,6 +215,9 @@ function autoCSAR.readConfigZone()
autoCSAR.seaCSAR = theZone:getBoolFromZoneProperty("seaCSAR", true)
autoCSAR.noExploit = theZone:getBoolFromZoneProperty("noExploit", false)
autoCSAR.killDist = theZone:getNumberFromZoneProperty("killDist", 2100)
if autoCSAR.verbose then
trigger.action.outText("+++aCSAR: read config", 30)
end
@ -249,6 +289,9 @@ function autoCSAR.start()
autoCSAR.loadData()
end
-- collect forbidden zones if noExploit is active
autoCSAR.collectForbiddenZones()
-- start GC
timer.scheduleFunction(autoCSAR.GC, {}, timer.getTime() + 1)

View File

@ -2189,4 +2189,6 @@ end
nameTest - optional safety / debug feature that will name-test each unit that is about to be spawned for replacement. Maybe auto turn on when verbose is set?
make example where transport can be different plane types but have same name
support 'orders' to complete replace routes, and pass to groundCommander like spawner. only for ground troops
--]]--

View File

@ -4,7 +4,7 @@
-- *** EXTENDS ZONES: 'pathing' attribute
--
cfxCommander = {}
cfxCommander.version = "2.0.0"
cfxCommander.version = "2.0.1"
--[[-- VERSION HISTORY
- 1.0.5 - createWPListForGroupToPointViaRoads: detect no road found
- 1.0.6 - build in more group checks in assign wp list
@ -35,6 +35,7 @@ cfxCommander.version = "2.0.0"
- hardened performCommands()
- createWPListForGroupToPoint() supports moveFormation
- makeGroupGoTherePreferringRoads() supports moveFormation
- 2.0.1 - hardened createBasicWaypoint when no formation given (default to echelonR
--]]--
cfxCommander.requiredLibs = {
@ -260,6 +261,8 @@ function cfxCommander.createBasicWaypoint(point, speed, formation)
if not speed then speed = 6 end -- 6 m/s = 20 kph
wp.speed = speed
if not formation then formation = "EchelonR" end
if cfxCommander.forceOffRoad then
formation = "Off Road"
end

View File

@ -1,5 +1,5 @@
reaper = {}
reaper.version = "1.0.0"
reaper.version = "1.1.0"
reaper.requiredLibs = {
"dcsCommon",
"cfxZones",
@ -8,7 +8,20 @@ reaper.requiredLibs = {
VERSION HISTORY
1.0.0 - Initial Version
1.1.0 - Individual status
- cycle target method
- cycle? attribute
- restructured menus
- added cycle target
- single status reprots full group
- drones have AFAC task instead of Reconnaissance
- Setting enroute task for group once target spotted
- compatible with Kiowa's L2MUM
- (undocumented) freq attribute for drones (in MHz)
- completely rewrote scanning method (performance)
- added FAC task
- split task generation from wp generation
- updated reaper naming, uniqueNames attribute (undocumented)
--]]--
@ -17,6 +30,12 @@ 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
reaper.uuidCnt = 0
function reaper.uuid(instring)
reaper.uuidCnt = reaper.uuidCnt + 1
return instring .. "-R" .. reaper.uuidCnt
end
-- reading reaper zones
function reaper.readReaperZone(theZone)
@ -25,6 +44,7 @@ function reaper.readReaperZone(theZone)
if theZone.myType == "MQ-9 Reaper" then
theZone.alt = 9500
else theZone.alt = 7500 end theZone.alt = theZone:getNumberFromZoneProperty("alt", theZone.alt)
theZone.freq = theZone:getNumberFromZoneProperty("freq", 133) * 1000000 -- in MHz
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)
@ -44,6 +64,7 @@ function reaper.readReaperZone(theZone)
theZone.autoRespawn = theZone:getBoolFromZoneProperty("autoRespawn", false)
theZone.launchUI = theZone:getBoolFromZoneProperty("launchUI", true)
theZone.statusUI = theZone:getBoolFromZoneProperty("statusUI", true)
theZone.uniqueNames = theZone:getBoolFromZoneProperty("uniqueNames", true) -- undocumented, leave true
if theZone:hasProperty("launch?") then
theZone.launch = theZone:getStringFromZoneProperty("launch?", "<none>")
theZone.launchVal = theZone:getFlagValue(theZone.launch)
@ -52,6 +73,11 @@ function reaper.readReaperZone(theZone)
theZone.status = theZone:getStringFromZoneProperty("status?", "<none>")
theZone.statusVal = theZone:getFlagValue(theZone.status)
end
if theZone:hasProperty("cycle?") then
theZone.cycle = theZone:getStringFromZoneProperty("cycle?", "<none>")
theZone.cycleVal = theZone:getFlagValue(theZone.cycle)
end
theZone.hasSpawned = false
if theZone.onStart then
@ -61,10 +87,24 @@ end
-- spawn a drone from a zone
function reaper.spawnForZone(theZone, ack)
-- delete any group with the same name
if not theZone.uniqueNames then
local exister = Group.getByName(theZone.name)
if exister then Group.destroy(exister) end
end
-- create spawn data
local gdata = dcsCommon.createEmptyGroundGroupData (dcsCommon.uuid(theZone.name))
gdata.task = "Reconnaissance"
local rName
if theZone.uniqueNames then
rName = reaper.uuid(theZone.name)
else
rName = theZone.name
end
local gdata = dcsCommon.createEmptyGroundGroupData (rName) -- warning: non-unique unit names, will replace previous
gdata.task = "AFAC"
gdata.route = {}
-- calculate left and right
local p = theZone:getPoint()
local left, right
@ -78,23 +118,31 @@ function reaper.spawnForZone(theZone, ack)
end
gdata.x = left.x
gdata.y = left.z
gdata.frequency = theZone.freq
-- build the unit data
local unit = {}
unit.name = dcsCommon.uuid(theZone.name)
unit.name = rName -- same as group
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
if theZone.uniqueNames then
else
if theZone.reaperGID and theZone.reaperUID then -- also re-use groupID
gdata.groupId = theZone.reaperGID
unit.unitId = theZone.reaperUID
trigger.action.outText("re-using data from old <" .. theZone.name .. ">", 30)
end
end
-- add to group
gdata.units[1] = unit
@ -112,7 +160,8 @@ function reaper.spawnForZone(theZone, ack)
trigger.action.outText("+++Reap: failed to spawn for zone <" .. theZone.name .. ">", 30)
return
end
theZone.reaperGID = theGroup:getID()
theZone.reaperUID = theGroup:getUnit(1):getID()
if theZone.verbose or reaper.verbose then
trigger.action.outText("+++reap: Spawned <" .. theGroup:getName() .. "> reaper", 30)
end
@ -145,24 +194,25 @@ function reaper.cleanUp(theZone)
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"] = {
function reaper.createReaperTask(alt, speed, target, theZone)
local task = {
["id"] = "ComboTask",
["params"] = {
["tasks"] = {
[1] = {
["enabled"] = true,
["auto"] = true,
["id"] = "WrappedAction",
["id"] = "FAC",
["number"] = 1,
["params"] =
{}, -- end of ["params"]
}, -- end of [1]
[2] = {
["enabled"] = true,
["auto"] = true,
["id"] = "WrappedAction",
["number"] = 2,
["params"] = {
["action"] = {
["id"] = "EPLRS",
@ -172,21 +222,56 @@ function reaper.createInitialWP(p, alt, speed)
}, -- end of ["params"]
}, -- end of ["action"]
}, -- end of ["params"]
}, -- end of [1]
[2] = {
}, -- end of [2]
[3] = {
["enabled"] = true,
["auto"] = false,
["id"] = "Orbit",
["number"] = 2,
["number"] = 3,
["params"] = {
["altitude"] = alt,
["pattern"] = "Race-Track",
["speed"] = speed,
}, -- end of ["params"]
}, -- end of [2]
}, -- end of [3]
}, -- end of ["tasks"]
}, -- end of ["params"]
}, -- end of ["task"]
} -- end of ["task"]
if theTarget and theZone then
-- local gID = theTarget:getGroup():getID()
local gID = theTarget:getID() -- NOTE: theTarget is a GROUP!!!!
local task4 = {
["enabled"] = true,
["auto"] = false,
["id"] = "FAC_AttackGroup",
["number"] = 4,
["params"] =
{
["number"] = 1,
["designation"] = "No",
["modulation"] = 0,
["groupId"] = gID,
-- ["callname"] = 1,
-- ["datalink"] = true,
["weaponType"] = 0, -- 9663676414,
["frequency"] = theZone.freq, -- 133000000,
}, -- end of ["params"]
} -- end of [4]
task.params.tasks[4] = task4
end
return task
end
function reaper.createInitialWP(p, alt, speed) -- warning: target must be a GROUP
local wp = {
["alt"] = alt,
["action"] = "Turning Point",
["alt_type"] = "BARO",
["properties"] = {
["addopt"] = {}, -- end of ["addopt"]
}, -- end of ["properties"]
["speed"] = speed,
["task"] = {}, -- will construct later
["type"] = "Turning Point",
["ETA"] = 0,
["ETA_locked"] = true,
@ -195,49 +280,12 @@ function reaper.createInitialWP(p, alt, speed)
["speed_locked"] = true,
["formation_template"] = "",
} -- end of wp
wp.task = reaper.createReaperTask(alt, speed) -- no zone, no target
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
function reaper.setTarget(theZone, theTarget, cycled)
-- add a laser tracker to this unit
local lp = theTarget:getPoint()
local lat, lon, alt = coord.LOtoLL(lp)
@ -255,9 +303,61 @@ function reaper.scan()
end
theZone.theSpot = theSpot
-- put me in track mode
reaper.tracking[name] = theZone
reaper.tracking[theZone.name] = theZone
if cycled then return end -- cycling inside group, no new tasking
-- now make tracking the group the drone's task
local theGroup = theTarget:getGroup()
local theTask = reaper.createReaperTask(theZone.alt, theZone.speed, theGroup, theZone) -- create full FAC task with orbit and group engage
local theController = theZone.theUav:getController()
if not theController then
trigger.action.outText("+++Rpr: UAV has no controller, getting group")
return
end
theController:setTask(theTask) -- replace with longer task
end
function reaper.selectFromDetectedTargets(visTargets, theZone)
-- use (permanent?) detectedTargetList
for idx, tData in pairs(visTargets) do
if tData then
local theTarget = tData.object
local nn = theTarget:getName()
if not nn or nn == "" then
trigger.action.outText("+++reaper: shortcut on startup", 30)
return nil
end
if theTarget and theTarget.getGroup then -- it's not a group or static object
local d = theTarget:getDesc()
if d.category == 2 then
if theZone.verbose then
trigger.action.outText("+++reap: identified <" .. tData.object:getName() .. "> as target for <" .. theZone.name .. ">")
end
return tData.object
end
end
end
end
return nil
end
function reaper.scanALT() -- alternative, more efficient (?) method using unit's controller
timer.scheduleFunction(reaper.scanALT, {}, timer.getTime() + reaper.scanInterval)
local filtered = {}
for name, theZone in pairs(reaper.scanning) do
local theUAV = theZone.theUav
if Unit.isExist(theUAV) then
-- get the controller
local theController = theUAV:getController()
local visTargets = theController:getDetectedTargets(1, 2)
local theTarget = reaper.selectFromDetectedTargets(visTargets, theZone)
if theTarget then
-- add a laser tracker to this unit
reaper.setTarget(theZone, theTarget)
else
-- will scan again
-- will scan again next round
filtered[name] = theZone
end
else
@ -276,6 +376,7 @@ function reaper.scan()
reaper.scanning = filtered
end
function reaper.track()
local filtered = {}
for name, theZone in pairs(reaper.tracking) do
@ -310,27 +411,73 @@ function reaper.track()
timer.scheduleFunction(reaper.track, {}, timer.getTime() + reaper.trackInterval)
end
function reaper.cycleTarget(theZone)
local coa = theZone.coa
-- try and advance to the next target
if not theZone.theUav or not Unit.isExist(theZone.theUav) then
trigger.action.outTextForCoalition(coa, "Reaper <" .. theZone.name .. "> not on station, requries launch first", 30)
trigger.action.outSoundForCoalition(theZone.coa, reaper.actionSound)
return
end
if not theZone.theSpot or
not theZone.theUav or
not theZone.theTarget then
trigger.action.outTextForCoalition(coa, "Reaper <" .. theZone.name .. "> is not tracking a target", 30)
trigger.action.outSoundForCoalition(theZone.coa, reaper.actionSound)
return
end
--when we get here, the reaper is tracking a target. get it's group
local theUnit = theZone.theTarget
if not theUnit.getGroup then return end -- safety first
local theGroup = theUnit:getGroup()
local allTargets = theGroup:getUnits()
local filtered = {}
local i = 1
local tIndex = 1
-- filter and find the target with it's index
for idx, aTgt in pairs(allTargets) do
if Unit.isExist(aTgt) then
if theUnit == aTgt then
if theZone.verbose then
trigger.action.outText("+++ reaper <" .. theZone.target .. ">: target index found : <" .. i .. ">", 30)
end
tIndex = i
end
table.insert(filtered, aTgt)
i = i + 1
end
end
local num = #filtered
if num < 2 then
-- nothing to do, simply ack
trigger.action.outTextForCoalition(coa, "<" .. theZone.name .. ">: Only one target left.", 30)
trigger.action.outSoundForCoalition(theZone.coa, reaper.actionSound)
return
end
-- increase tIndex
tIndex = tIndex + 1
if tIndex > #filtered then tIndex = 1 end
local newTarget = filtered[tIndex]
-- tell zone to target this new target
reaper.setTarget(theZone, newTarget, true) -- also outputs text and action sound, true = cycled
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
@ -340,6 +487,10 @@ function reaper.update()
args[2] = name -- = args[2]
reaper.doLaunch(args)
end
if theZone.cycle and theZone:testZoneFlag(theZone.cycle, "change", "cycleVal") then
reaper.cycleTarget(theZone)
end
end
-- now poll my (global) status flags
@ -356,43 +507,46 @@ end
--
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)
reaper.installDCForCoa(coa, root)
end
function reaper.installLaunchersForCoa(coa, root)
-- WARNING: we currently install commands, may overflow!
-- trigger.action.outText("enter launchers builder", 30)
function reaper.installDCForCoa(coa, root)
local filtered = {}
for name, theZone in pairs(reaper.zones) do
if theZone.coa == coa and theZone.launchUI then
if theZone.coa == coa and (theZone.statusUI or 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)
trigger.action.outText("+++reap: WARNING too many (" .. n .. ") drones for coa <" .. coa .. ">", 30)
return
end
for name, theZone in pairs(filtered) do
-- trigger.action.outText("proccing " .. name, 30)
mnu = theZone.name .. ": " .. theZone.myType
local mnu = theZone.name .. ": " .. theZone.myType
-- install menu for this drone
local r1 = missionCommands.addSubMenuForCoalition(coa, mnu, root)
-- install status and cycle target commands for this drone
local args = {coa, name, }
if theZone.launchUI then
mnu = "Launch " .. 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)
local r3 = missionCommands.addCommandForCoalition(coa, mnu, r1, reaper.redirectLaunch, args)
end
if theZone.statusUI then
local r2 = missionCommands.addCommandForCoalition(coa, "Status Update", r1, reaper.redirectSingleStatus, args)
end
local r2 = missionCommands.addCommandForCoalition(coa, "Cycle target", r1, reaper.redirectCycleTarget, args)
end
end
@ -404,6 +558,13 @@ function reaper.redirectLaunch(args)
timer.scheduleFunction(reaper.doLaunch, args, timer.getTime() + 0.1)
end
function reaper.redirectSingleStatus(args)
timer.scheduleFunction(reaper.doSingleStatusM, args, timer.getTime() + 0.1)
end
function reaper.redirectCycleTarget(args)
timer.scheduleFunction(reaper.doCylcleTarget, args, timer.getTime() + 0.1)
end
--
-- DML API for UI
--
@ -483,14 +644,12 @@ function reaper.doDroneStatus(args)
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
@ -503,9 +662,31 @@ function reaper.doSingleDroneStatus(theZone)
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]"
-- now add full group intelligence
local collector = {}
local theGroup = theTarget:getGroup()
local allTargets = theGroup:getUnits()
for idx, aTgt in pairs(allTargets) do
if Unit.isExist(aTgt) then
local tn = aTgt:getTypeName()
if collector[tn] then collector[tn] = collector[tn] + 1
else collector[tn] = 1 end
end
end
msg = msg .."\nGroup consists of: "
local i = 1
for name, count in pairs(collector) do
if i > 1 then msg = msg .. ", " end
msg = msg .. name
if count > 1 then msg = msg .. " (x" .. count .. ")" end
i = 2
end
msg = msg .. ".\n"
else
msg = msg .. "[signal failure, please try again later]"
end
trigger.action.outTextForCoalition(coa, msg, 30)
trigger.action.outSoundForCoalition(coa, reaper.actionSound)
return
@ -528,9 +709,25 @@ function reaper.doSingleDroneStatus(theZone)
trigger.action.outSoundForCoalition(coa, reaper.actionSound)
end
function reaper.doSingleStatusM(args)
local coa = args[1]
local name = args[2]
local theZone = reaper.zones[name]
if not theZone then end return
reaper.doSingleDroneStatus(theZone)
end
function reaper.doCylcleTarget(args)
local coa = args[1]
local name = args[2]
local theZone = reaper.zones[name]
if not theZone then end return
reaper.cycleTarget(theZone)
end
function reaper.doLaunch(args)
coa = args[1]
name = args[2]
local coa = args[1]
local name = args[2]
-- check if we can launch
local theZone = reaper.zones[name]
if not theZone then
@ -581,7 +778,7 @@ function reaper.readConfigZone()
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.hasUI = theZone:getBoolFromZoneProperty("UI", true)
reaper.menuName = theZone:getStringFromZoneProperty("menuName", "Drone Command")
reaper.useCost = theZone:getBoolFromZoneProperty("useCost", true)
if theZone:hasProperty("blueStatus?") then
@ -668,7 +865,7 @@ function reaper.start()
end
-- install UI if desired
if reaper.UI then
if reaper.hasUI then
local coas = {1, 2}
for idx, coa in pairs(coas) do
reaper.installFullUIForCoa(coa)
@ -689,9 +886,9 @@ function reaper.start()
timer.scheduleFunction(reaper.update, {}, timer.getTime() + 1)
-- schedule scan and track loops
timer.scheduleFunction(reaper.scan, {}, timer.getTime() + 1)
-- timer.scheduleFunction(reaper.scan, {}, timer.getTime() + 1)
timer.scheduleFunction(reaper.scanALT, {}, timer.getTime() + 1)
timer.scheduleFunction(reaper.track, {}, timer.getTime() + 1)
trigger.action.outText("reaper v " .. reaper.version .. " running.", 30)
return true
end
@ -702,7 +899,5 @@ end
--[[--
Idea: mobile launch vehicle, zone follows apc around. Can even be hauled along with hook
idea: prioritizing targets in a group
fix quad zone waypoints
filter targets for lasing by list?
--]]--

View File

@ -1,5 +1,5 @@
cfxReconMode = {}
cfxReconMode.version = "2.2.1"
cfxReconMode.version = "2.2.2"
cfxReconMode.verbose = false -- set to true for debug info
cfxReconMode.reconSound = "UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav" -- to be played when somethiong discovered
@ -22,38 +22,6 @@ cfxReconMode.name = "cfxReconMode" -- to be compatible with test flags
--[[--
VERSION HISTORY
1.0.0 - initial version
1.0.1 - removeScoutByName()
1.0.2 - garbage collection
1.1.0 - autoRecon - any aircraft taking off immediately
signs up, no message when signing up or closing down
standalone - copied common procs lerp, agl, dist, distflat
from dcsCommon
report numbers
verbose flag
1.2.0 - queued recons. One scout per second for more even
performance
removed gc since it's now integrated into
update queue
removeScout optimization when directly passing name
playerOnlyRecon for autoRecon
red, blue, grey side filtering on auto scout
1.2.1 - parametrized report sound
1.3.0 - added black list, prio list functionality
1.3.1 - callbacks now also push name, as group can be dead
- removed bug when removing dead groups from map
1.4.0 - import dcsCommon, cfxZones etc
- added lib check
- config zone
- prio+
- detect+
1.4.1 - invocation no longer happen twice for prio.
- recon sound
- read all flight groups at start to get rid of the
- late activation work-around
1.5.0 - removeWhenDestroyed()
- autoRemove()
- readConfigZone creates default config zone so we get correct defaulting
2.0.0 - DML integration prio+-->prio! detect+ --> detect!
and method
- changed access to prio and blacklist to hash
@ -90,24 +58,9 @@ VERSION HISTORY
- new marksFadeAfter config attribute to control mark time
- dmlZones OOP upgrade
2.2.1 - fixed "cfxReconSMode" typo
2.2.2 - added groupNames attribute
- clean-up
cfxReconMode is a script that allows units to perform reconnaissance
missions and, after detecting units, marks them on the map with
markers for their coalition and some text
Also, a callback is initiated for scouts as follows
signature: (reason, theSide, theSout, theGroup) with
reason a string
'detected' a group was detected
'removed' a mark for a group timed out
'priority' a member of prio group was detected
'start' a scout started scouting
'end' a scout stopped scouting
'dead' a scout has died and was removed from pool
theSide - side of the SCOUT that detected units
theScout - the scout that detected the group
theGroup - the group that is detected
theName - the group's name
--]]--
cfxReconMode.detectionMinRange = 3000 -- meters at ground level
@ -137,9 +90,6 @@ cfxReconMode.marksFadeAfter = 30*60 -- after detection, marks disappear after
cfxReconMode.callbacks = {} -- sig: cb(reason, side, scout, group)
cfxReconMode.uuidCount = 0 -- for unique marks
-- end standalone dcsCommon extract
function cfxReconMode.uuid()
cfxReconMode.uuidCount = cfxReconMode.uuidCount + 1
return cfxReconMode.uuidCount
@ -163,7 +113,6 @@ function cfxReconMode.addToPrioList(aGroup, dynamic)
aGroup = aGroup:getName()
end
if type(aGroup) == "string" then
-- table.insert(cfxReconMode.prioList, aGroup)
cfxReconMode.prioList[aGroup] = aGroup
cfxReconMode.dynamics[aGroup] = dynamic
end
@ -176,7 +125,6 @@ function cfxReconMode.addToBlackList(aGroup, dynamic)
aGroup = aGroup:getName()
end
if type(aGroup) == "string" then
--table.insert(cfxReconMode.blackList, aGroup)
cfxReconMode.blackList[aGroup] = aGroup
cfxReconMode.dynamics[aGroup] = dynamic
end
@ -230,15 +178,9 @@ function cfxReconMode.isStringInList(theString, theList)
end
-- addScout directly adds a scout unit. Use from external
-- to manually add a unit (e.g. via GUI when autoscout isExist
-- off, or to force a scout unit (e.g. when scouts for a side
-- are not allowed but you still want a unit from that side
-- to scout
-- since we use a queue for scouts, also always check the
-- processed queue before adding to make sure a scout isn't
-- entered multiple times
function cfxReconMode.addScout(theUnit)
if not theUnit then
trigger.action.outText("+++cfxRecon: WARNING - nil Unit on add", 30)
@ -350,9 +292,9 @@ end
function cfxReconMode.placeMarkForUnit(location, theSide, theGroup)
local theID = cfxReconMode.uuid()
local theDesc = "Contact: "..theGroup:getName()
local theDesc = "Contact"
if cfxReconMode.groupNames then theDesc = theDesc .. ": " ..theGroup:getName() end
if cfxReconMode.reportNumbers then
-- theDesc = theDesc .. " (" .. theGroup:getSize() .. " units)"
theDesc = theDesc .. " - " .. cfxReconMode.getSit(theGroup) .. ", " .. cfxReconMode.getAction(theGroup) .. "."
end
trigger.action.markToCoalition(
@ -466,7 +408,9 @@ function cfxReconMode.getTimeData()
end
function cfxReconMode.generateSALT(theScout, theGroup)
local msg = theScout:getName() .. " reports new ground contact " .. theGroup:getName() .. ":\n"
local msg = theScout:getName() .. " reports new ground contact"
if cfxReconMode.groupNames then msg = msg .. " " .. theGroup:getName() end
msg = msg .. ":\n"
-- SALT: S = Situation or number of units A = action they are doing L = Location T = Time
msg = msg .. cfxReconMode.getSit(theGroup) .. ", "-- S
msg = msg .. cfxReconMode.getAction(theGroup) .. ", " -- A
@ -559,7 +503,6 @@ function cfxReconMode.detectedGroup(mySide, theScout, theGroup, theLoc)
-- see if it was a prio target
if inList then
-- if cfxReconMode.announcer then
if cfxReconMode.verbose then
trigger.action.outText("+++rcn: Priority target spotted", 30)
end
@ -1049,7 +992,7 @@ function cfxReconMode.readConfigZone()
if theZone:hasProperty("imperialUnits") then
cfxReconMode.imperialUnits = theZone:getBoolFromZoneProperty( "imperialUnits", false)
end
cfxReconMode.groupNames = theZone:getBoolFromZoneProperty( "groupNames", true)
cfxReconMode.theZone = theZone -- save this zone
end
@ -1200,9 +1143,6 @@ if not cfxReconMode.start() then
cfxReconMode = nil
end
-- debug: wire up my own callback
-- cfxReconMode.addCallback(cfxReconMode.demoReconCB)
--[[--

View File

@ -1,11 +1,16 @@
sweeper = {}
sweeper.version = "1.0.0"
sweeper.version = "1.0.1"
sweeper.requiredLibs = {
"dcsCommon",
"cfxZones",
}
-- remove all units that are detected twice in a row in the same
-- zone after a time interval. Used to remove deadlocked units.
--[[--
VERSION HISTORY
1.0.1 - Initial version
--]]--
sweeper.zones = {}
sweeper.interval = 5 * 60 -- 5 mins (max 10 mins) in zone will kill you
@ -18,7 +23,7 @@ end
function sweeper.readSweeperZone(theZone)
theZone.aircraft = theZone:getBoolFromZoneProperty("aircraft", true)
theZone.helos = theZone:getBoolFromZoneProperty("helos", false)
theZone.helos = theZone:getBoolFromZoneProperty("helos", true)
end
function sweeper.update()
@ -102,6 +107,16 @@ function sweeper.update()
sweeper.flights = newFlights
end
function sweeper.readConfig()
local theZone = cfxZones.getZoneByName("sweeperConfig")
if not theZone then
theZone = cfxZones.createSimpleZone("sweeperConfig")
end
sweeper.name = "sweeperConfig" -- zones comaptibility
sweeper.interval = theZone:getNumberFromZoneProperty("interval", 5 * 60)
sewwper.verbose = theZone.verbose
end
function sweeper.start()
-- lib check
if not dcsCommon.libCheck then
@ -112,6 +127,8 @@ function sweeper.start()
return false
end
sweeper.readConfig()
-- process sweeper Zones
local attrZones = cfxZones.getZonesWithAttributeNamed("sweeper")
for k, aZone in pairs(attrZones) do
@ -119,7 +136,7 @@ function sweeper.start()
sweeper.addSweeperZone(aZone) -- add to list
end
-- start update in 5 seconds
-- start update in (interval)
timer.scheduleFunction(sweeper.update, {}, timer.getTime() + sweeper.interval)
-- say hi

View File

@ -1,5 +1,5 @@
williePete = {}
williePete.version = "2.0.3"
williePete.version = "2.0.4"
williePete.ups = 10 -- we update at 10 fps, so accuracy of a
-- missile moving at Mach 2 is within 33 meters,
-- with interpolation even at 3 meters
@ -20,6 +20,7 @@ williePete.requiredLibs = {
2.0.1 - added Harrier's FFAR M156 WP
2.0.2 - hardened playerUpdate()
2.0.3 - further hardened playerUpdate()
2.0.4 - support for the Kiowa's Hydra M259
--]]--
williePete.willies = {}
@ -31,7 +32,8 @@ williePete.blastedObjects = {} -- used when we detonate something
-- recognizes WP munitions. May require regular update when new
-- models come out.
williePete.smokeWeapons = {"HYDRA_70_M274","HYDRA_70_MK61","HYDRA_70_MK1","HYDRA_70_WTU1B","HYDRA_70_M156","HYDRA_70_M158","BDU_45B","BDU_33","BDU_45","BDU_45LGB","BDU_50HD","BDU_50LD","BDU_50LGB","C_8CM", "SNEB_TYPE254_H1_GREEN", "SNEB_TYPE254_H1_RED", "SNEB_TYPE254_H1_YELLOW", "FFAR M156 WP"}
williePete.smokeWeapons = {"HYDRA_70_M274","HYDRA_70_MK61","HYDRA_70_MK1","HYDRA_70_WTU1B","HYDRA_70_M156","HYDRA_70_M158","BDU_45B","BDU_33","BDU_45","BDU_45LGB","BDU_50HD","BDU_50LD","BDU_50LGB","C_8CM", "SNEB_TYPE254_H1_GREEN", "SNEB_TYPE254_H1_RED", "SNEB_TYPE254_H1_YELLOW", "FFAR M156 WP",
"HYDRA_70_M259"}
function williePete.addWillie(theWillie)
table.insert(williePete.willies, theWillie)