mirror of
https://github.com/weyne85/DML.git
synced 2025-10-29 16:57:49 +00:00
Version 0.994
groupTracker unitZone CSAR of Georgia
This commit is contained in:
parent
a35087c234
commit
95e809b85c
Binary file not shown.
Binary file not shown.
@ -36,7 +36,8 @@ Version History
|
||||
- reUseAfter option for single-use
|
||||
- dcsCommon, cfxZones import
|
||||
2.0.1 - stricter verbosity: moved more comments to verbose only
|
||||
2.0.2 - health check code
|
||||
2.0.2 - health check code (initial)
|
||||
- added verbosity
|
||||
|
||||
WHAT IT IS
|
||||
SSB Client is a small script that forms the client-side counterpart to
|
||||
@ -235,15 +236,21 @@ end
|
||||
|
||||
function cfxSSBClient:onEvent(event)
|
||||
if event.id == 21 then -- S_EVENT_PLAYER_LEAVE_UNIT
|
||||
trigger.action.outText("+++SSB: Player leave unit", 30)
|
||||
if cfxSSBClient.verbose then
|
||||
trigger.action.outText("+++SSB: Player leave unit", 30)
|
||||
end
|
||||
local theUnit = event.initiator
|
||||
if not theUnit then
|
||||
trigger.action.outText("+++SSB: No unit left, abort", 30)
|
||||
if not theUnit then
|
||||
if cfxSSBClient.verbose then
|
||||
trigger.action.outText("+++SSB: No unit left, abort", 30)
|
||||
end
|
||||
return
|
||||
end
|
||||
local curH = theUnit:getLife()
|
||||
local maxH = theUnit:getLife0()
|
||||
trigger.action.outText("+++SSB: Health check: " .. curH .. " of " .. maxH, 30)
|
||||
if cfxSSBClient.verbose then
|
||||
trigger.action.outText("+++SSB: Health check: " .. curH .. " of " .. maxH, 30)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@ civAir.version = "1.4.0"
|
||||
|
||||
--]]--
|
||||
|
||||
civAir.ups = 0.05 -- updates per second
|
||||
civAir.ups = 0.05 -- updates per second. 0.05 = once every 20 seconds
|
||||
civAir.initialAirSpawns = true -- when true has population spawn in-air at start
|
||||
civAir.verbose = false
|
||||
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
cloneZones = {}
|
||||
cloneZones.version = "1.3.0"
|
||||
cloneZones.version = "1.3.1"
|
||||
cloneZones.verbose = false
|
||||
cloneZones.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxZones", -- Zones, of course
|
||||
"cfxMX",
|
||||
}
|
||||
-- groupTracker is OPTIONAL! and required only with trackWith attribute
|
||||
|
||||
cloneZones.cloners = {}
|
||||
cloneZones.callbacks = {}
|
||||
cloneZones.unitXlate = {}
|
||||
@ -32,6 +34,8 @@ cloneZones.uniqueCounter = 9200000 -- we start group numbering here
|
||||
- clone? synonym
|
||||
- empty! and method attributes
|
||||
1.3.0 - DML flag upgrade
|
||||
1.3.1 - groupTracker interface
|
||||
- trackWith: attribute
|
||||
|
||||
--]]--
|
||||
|
||||
@ -238,6 +242,11 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
|
||||
|
||||
theZone.turn = cfxZones.getNumberFromZoneProperty(theZone, "turn", 0)
|
||||
|
||||
-- interface to groupTracker
|
||||
if cfxZones.hasProperty(theZone, "trackWith:") then
|
||||
theZone.trackWith = cfxZones.getStringFromZoneProperty(theZone, "trackWith:", "<None>")
|
||||
end
|
||||
|
||||
-- we end with clear plate
|
||||
end
|
||||
|
||||
@ -523,6 +532,23 @@ function cloneZones.resolveReferences(theZone, dataTable)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function cloneZones.handoffTracking(theGroup, theZone)
|
||||
if not groupTracker then
|
||||
trigger.action.outText("+++clne: <" .. theZone.name .. "> trackWith requires groupTracker module", 30)
|
||||
return
|
||||
end
|
||||
local trackerName = theZone.trackWith
|
||||
if trackerName == "*" then trackerName = theZone.name end
|
||||
local theTracker = groupTracker.getTrackerByName(trackerName)
|
||||
if not theTracker then
|
||||
trigger.action.outText("+++clne: <" .. theZone.name .. ">: cannot find tracker named <".. trackerName .. ">", 30)
|
||||
return
|
||||
end
|
||||
|
||||
groupTracker.addGroupToTracker(theGroup, theTracker)
|
||||
end
|
||||
|
||||
function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
|
||||
-- theZone is the cloner with the template
|
||||
-- spawnZone is the spawner with settings
|
||||
@ -612,6 +638,10 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
|
||||
end
|
||||
|
||||
cloneZones.invokeCallbacks(theZone, "did spawn group", theGroup)
|
||||
-- interface to groupTracker
|
||||
if theZone.trackWith then
|
||||
cloneZones.handoffTracking(theGroup, theZone)
|
||||
end
|
||||
end
|
||||
|
||||
-- static spawns
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
csarManager = {}
|
||||
csarManager.version = "2.0.3"
|
||||
csarManager.version = "2.1.0"
|
||||
csarManager.verbose = false
|
||||
csarManager.ups = 1
|
||||
|
||||
--[[-- VERSION HISTORY
|
||||
- 1.0.0 initial version
|
||||
- 1.0.1 - smoke optional
|
||||
@ -20,6 +23,18 @@ csarManager.version = "2.0.3"
|
||||
- use hoverDuration
|
||||
- 2.0.3 - corrected bug in hoverDuration
|
||||
- 2.0.4 - guard in createCSARMission for cfxCommander
|
||||
- 2.1.0 - startCSAR?
|
||||
- deferrable missions
|
||||
- verbose
|
||||
- ups
|
||||
- useSmoke
|
||||
- smokeColor
|
||||
- reworked smoking the loc
|
||||
- config zone
|
||||
- csarRedDelivered
|
||||
- csarBlueDelivered
|
||||
- finally fixed smoke performance bug
|
||||
- csarManager.vectoring optional
|
||||
|
||||
--]]--
|
||||
-- modules that need to be loaded BEFORE I run
|
||||
@ -33,6 +48,7 @@ csarManager.requiredLibs = {
|
||||
}
|
||||
|
||||
-- *** DOES NOT EXTEND ZONES *** BUT USES OWN STRUCT
|
||||
-- *** extends zones for csarMission zones though
|
||||
|
||||
--[[--
|
||||
CSAR MANAGER
|
||||
@ -103,6 +119,7 @@ csarManager.requiredLibs = {
|
||||
-- OPTIONS
|
||||
--
|
||||
csarManager.useSmoke = false -- smoke is a performance killer, so you can turn it off
|
||||
csarManager.smokeColor = 4 -- when using smoke
|
||||
|
||||
|
||||
-- unitConfigs contain the config data for any helicopter
|
||||
@ -115,6 +132,7 @@ csarManager.myEvents = {3, 4, 5} -- 3 = take off, 4 = land, 5 = crash
|
||||
--
|
||||
csarManager.openMissions = {} -- all currently available missions
|
||||
csarManager.csarBases = {} -- all bases where we can drop off rescued pilots
|
||||
csarManager.csarZones = {} -- zones for spawning
|
||||
|
||||
csarManager.missionID = 1 -- to create uuid
|
||||
csarManager.rescueRadius = 70 -- must land within 50m to rescue
|
||||
@ -124,6 +142,7 @@ csarManager.hoverDuration = 20 -- must hover for this duration
|
||||
csarManager.rescueTriggerRange = 2000 -- when the unit pops smoke and radios
|
||||
csarManager.beaconSound = "Radio_beacon_of_distress_on_121,5_MHz.ogg"
|
||||
csarManager.pilotWeight = 120 -- kg for the rescued person. added to the unit's weight
|
||||
csarManager.vectoring = true -- provide bearing and range
|
||||
--
|
||||
-- callbacks
|
||||
--
|
||||
@ -176,7 +195,7 @@ function csarManager.createDownedPilot(theMission)
|
||||
cfxCommander.scheduleCommands(theCommands, 2) -- in 2 seconds, so unit has time to percolate through DCS
|
||||
end
|
||||
|
||||
function csarManager.createCSARMissionData(point, theSide, freq, name, numCrew, timeLimit, mapMarker)
|
||||
function csarManager.createCSARMissionData(point, theSide, freq, name, numCrew, timeLimit, mapMarker, inRadius)
|
||||
-- create a type
|
||||
if not timeLimit then timeLimit = -1 end
|
||||
if not point then return nil end
|
||||
@ -186,9 +205,10 @@ function csarManager.createCSARMissionData(point, theSide, freq, name, numCrew,
|
||||
-- remove "downed" - it will be added again later
|
||||
name = dcsCommon.removePrefix(name, "(downed) ")
|
||||
end
|
||||
if not inRadius then inRadius = csarManager.rescueRadius end
|
||||
|
||||
newMission.name = "(downed) " .. name .. "-" .. csarManager.missionID -- make it uuid-capable
|
||||
newMission.zone = cfxZones.createSimpleZone(newMission.name, point, csarManager.rescueRadius)
|
||||
newMission.zone = cfxZones.createSimpleZone(newMission.name, point, inRadius) --csarManager.rescueRadius)
|
||||
newMission.marker = mapMarker -- so it can be removed later
|
||||
newMission.isHot = false -- creating adversaries will make it hot, or when units are near. maybe implement a search later?
|
||||
-- detection and load stuff
|
||||
@ -354,10 +374,24 @@ function csarManager.successMission(who, where, theMission)
|
||||
-- now call callback for coalition side
|
||||
-- callback has format callback(coalition, success true/false, numberSaved, descriptionText)
|
||||
|
||||
for idx, callback in pairs(csarManager.csarCompleteCB) do
|
||||
callback(theMission.side, true, 1, "test")
|
||||
end
|
||||
csarManager.invokeCallbacks(theMission.side, true, 1, "success")
|
||||
-- for idx, callback in pairs(csarManager.csarCompleteCB) do
|
||||
-- callback(theMission.side, true, 1, "test")
|
||||
-- end
|
||||
trigger.action.outSoundForCoalition(theMission.side, "Quest Snare 3.wav")
|
||||
|
||||
if csarManager.csarRedDelivered and theMission.side == 1 then
|
||||
cfxZones.pollFlag(csarManager.csarRedDelivered, "inc", csarManager.configZone)
|
||||
end
|
||||
|
||||
if csarManager.csarBlueDelivered and theMission.side == 2 then
|
||||
cfxZones.pollFlag(csarManager.csarBlueDelivered, "inc", csarManager.configZone)
|
||||
end
|
||||
|
||||
if csarManager.csarDelivered then
|
||||
cfxZones.pollFlag(csarManager.csarDelivered, "inc", csarManager.configZone)
|
||||
trigger.action.outText("+++csar: banging csarDelivered: <" .. csarManager.csarDelivered .. ">", 30)
|
||||
end
|
||||
end
|
||||
|
||||
function csarManager.heloLanded(theUnit)
|
||||
@ -687,7 +721,12 @@ function csarManager.doListCSARRequests(args)
|
||||
d = math.floor(d * 10) / 10
|
||||
local b = dcsCommon.bearingInDegreesFromAtoB(point, mission.zone.point)
|
||||
local status = "alive"
|
||||
report = report .. "\n".. mission.name .. ", bearing " .. b .. ", " ..d .."nm, " .. " ADF " .. mission.freq .. "0 kHz - " .. status
|
||||
if csarManager.vectoring then
|
||||
report = report .. "\n".. mission.name .. ", bearing " .. b .. ", " ..d .."nm, " .. " ADF " .. mission.freq .. "0 kHz - " .. status
|
||||
else
|
||||
-- leave out vectoring
|
||||
report = report .. "\n".. mission.name .. " ADF " .. mission.freq .. "0 kHz - " .. status
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -879,7 +918,7 @@ end
|
||||
|
||||
function csarManager.update() -- every second
|
||||
-- schedule next invocation
|
||||
timer.scheduleFunction(csarManager.update, {}, timer.getTime() + 1)
|
||||
timer.scheduleFunction(csarManager.update, {}, timer.getTime() + 1/csarManager.ups)
|
||||
|
||||
-- first, check the health of all csar misions and update the table of live units
|
||||
csarManager.updateCSARMissions()
|
||||
@ -924,8 +963,11 @@ function csarManager.update() -- every second
|
||||
end
|
||||
-- also pop smoke if not popped already, or more than 3 minutes ago
|
||||
if csarManager.useSmoke and timer.getTime() - csarMission.lastSmokeTime > 179 then
|
||||
local smokePoint = cfxZones.createHeightCorrectedPoint(csarMission.zone.point)
|
||||
trigger.action.smoke(smokePoint, 4 )
|
||||
local smokePoint = dcsCommon.randomPointOnPerimeter(
|
||||
50, csarMission.zone.point.x, csarMission.zone.point.z) --cfxZones.createHeightCorrectedPoint(csarMission.zone.point)
|
||||
-- trigger.action.smoke(smokePoint, 4 )
|
||||
dcsCommon.markPointWithSmoke(smokePoint, csarManager.smokeColor)
|
||||
csarMission.lastSmokeTime = timer.getTime()
|
||||
end
|
||||
|
||||
-- now check if we are inside hover range and alt
|
||||
@ -990,6 +1032,32 @@ function csarManager.update() -- every second
|
||||
end -- if troop carrier
|
||||
end -- if exists
|
||||
end -- for all players
|
||||
|
||||
-- now see and check if we need to spawn from a csar zone
|
||||
-- that has been told to spawn
|
||||
for idx, theZone in pairs(csarManager.csarZones) do
|
||||
-- check if their flag value has changed
|
||||
if theZone.startCSAR then
|
||||
-- this should always be true, but you never know
|
||||
local currVal = cfxZones.getFlagValue(theZone.startCSAR, theZone)
|
||||
if currVal ~= theZone.lastCSARVal then
|
||||
local theMission = csarManager.createCSARMissionData(
|
||||
cfxZones.getPoint(theZone),
|
||||
theZone.csarSide,
|
||||
theZone.csarFreq,
|
||||
theZone.csarName,
|
||||
theZone.numCrew,
|
||||
theZone.timeLimit,
|
||||
theZone.csarMapMarker,
|
||||
theZone.radius)
|
||||
csarManager.addMission(theMission)
|
||||
theZone.lastCSARVal = currVal
|
||||
if csarManager.verbose then
|
||||
trigger.action.outText("+++csar: started CSAR mission " .. theZone.csarName, 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
@ -1036,10 +1104,9 @@ function csarManager.createCSARforUnit(theUnit, pilotName, radius, silent)
|
||||
end
|
||||
|
||||
|
||||
|
||||
--
|
||||
-- Init & Start
|
||||
--
|
||||
-- csar (mission) zones
|
||||
--
|
||||
|
||||
function csarManager.processCSARBASE()
|
||||
local csarBases = cfxZones.zonesWithProperty("CSARBASE")
|
||||
@ -1051,15 +1118,85 @@ function csarManager.processCSARBASE()
|
||||
end
|
||||
end
|
||||
|
||||
function csarManager.processCASRZones()
|
||||
function csarManager.addCSARZone(theZone)
|
||||
table.insert(csarManager.csarZones, theZone)
|
||||
end
|
||||
|
||||
function csarManager.readCSARZone(theZone)
|
||||
-- zones have attribute "CSAR"
|
||||
-- gather data, and then create a mission from this
|
||||
local theSide = cfxZones.getCoalitionFromZoneProperty(theZone, "coalition", 0)
|
||||
theZone.csarSide = theSide
|
||||
theZone.csarName = cfxZones.getStringFromZoneProperty(theZone, "name", "<none>")
|
||||
if cfxZones.hasProperty(theZone, "csarName") then
|
||||
theZone.csarName = cfxZones.getStringFromZoneProperty(theZone, "csarName", "<none>")
|
||||
end
|
||||
if cfxZones.hasProperty(theZone, "pilotName") then
|
||||
theZone.csarName = cfxZones.getStringFromZoneProperty(theZone, "pilotName", "<none>")
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "victimName") then
|
||||
theZone.csarName = cfxZones.getStringFromZoneProperty(theZone, "victimName", "<none>")
|
||||
end
|
||||
|
||||
theZone.csarFreq = cfxZones.getNumberFromZoneProperty(theZone, "freq", 0)
|
||||
if theZone.csarFreq == 0 then theZone.csarFreq = nil end
|
||||
theZone.numCrew = 1
|
||||
theZone.csarMapMarker = nil
|
||||
theZone.timeLimit = cfxZones.getNumberFromZoneProperty(theZone, "timeLimit", 0)
|
||||
if theZone.timeLimit == 0 then theZone.timeLimit = nil else theZone.timeLimit = timeLimit * 60 end
|
||||
|
||||
local deferred = cfxZones.getBoolFromZoneProperty(theZone, "deferred", false)
|
||||
|
||||
if cfxZones.hasProperty(theZone, "in?") then
|
||||
theZone.startCSAR = cfxZones.getStringFromZoneProperty(theZone, "in?", "*none")
|
||||
theZone.lastCSARVal = cfxZones.getFlagValue(theZone.startCSAR, theZone)
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "startCSAR?") then
|
||||
theZone.startCSAR = cfxZones.getStringFromZoneProperty(theZone, "startCSAR?", "*none")
|
||||
theZone.lastCSARVal = cfxZones.getFlagValue(theZone.startCSAR, theZone)
|
||||
end
|
||||
|
||||
|
||||
if (not deferred) then
|
||||
local theMission = csarManager.createCSARMissionData(
|
||||
theZone.point,
|
||||
theZone.csarSide,
|
||||
theZone.csarFreq,
|
||||
theZone.csarName,
|
||||
theZone.numCrew,
|
||||
theZone.timeLimit,
|
||||
theZone.csarMapMarker,
|
||||
theZone.radius)
|
||||
csarManager.addMission(theMission)
|
||||
end
|
||||
|
||||
-- add to list of startable csar
|
||||
if theZone.startCSAR then
|
||||
csarManager.addCSARZone(theZone)
|
||||
trigger.action.outText("csar: added <".. theZone.name .."> to deferred csar missions", 30)
|
||||
end
|
||||
|
||||
if deferred and not theZone.startCSAR then
|
||||
trigger.action.outText("+++csar: warning - CSAR Mission in Zone <" .. theZone.name .. "> can't be started", 30)
|
||||
end
|
||||
end
|
||||
|
||||
function csarManager.processCSARZones()
|
||||
local csarBases = cfxZones.zonesWithProperty("CSAR")
|
||||
|
||||
-- now add all zones to my zones table, and init additional info
|
||||
-- from properties
|
||||
for k, aZone in pairs(csarBases) do
|
||||
|
||||
csarManager.readCSARZone(aZone)
|
||||
--[[--
|
||||
-- gather data, and then create a mission from this
|
||||
local theSide = cfxZones.getCoalitionFromZoneProperty(aZone, "coalition", 0)
|
||||
aZone.csarSide = theSide
|
||||
local name = cfxZones.getZoneProperty(aZone, "name")
|
||||
aZone.
|
||||
local freq = cfxZones.getNumberFromZoneProperty(aZone, "freq", 0)
|
||||
if freq == 0 then freq = nil end
|
||||
local numCrew = 1
|
||||
@ -1075,9 +1212,17 @@ function csarManager.processCASRZones()
|
||||
timeLimit,
|
||||
mapMarker)
|
||||
csarManager.addMission(theMission)
|
||||
--]]--
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Init & Start
|
||||
--
|
||||
|
||||
|
||||
|
||||
|
||||
function csarManager.invokeCallbacks(theCoalition, success, numRescued, notes)
|
||||
-- invoke anyone who wants to know that a group
|
||||
@ -1091,6 +1236,53 @@ function csarManager.installCallback(theCB)
|
||||
table.insert(csarManager.csarCompleteCB, theCB)
|
||||
end
|
||||
|
||||
function csarManager.readConfigZone()
|
||||
local theZone = cfxZones.getZoneByName("csarManagerConfig")
|
||||
if not theZone then
|
||||
if csarManager.verbose then
|
||||
trigger.action.outText("+++csar: NO config zone!", 30)
|
||||
end
|
||||
return
|
||||
end
|
||||
csarManager.configZone = theZone -- save for flag banging compatibility
|
||||
|
||||
csarManager.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
||||
|
||||
csarManager.ups = cfxZones.getNumberFromZoneProperty(theZone, "ups", 1)
|
||||
|
||||
csarManager.useSmoke = cfxZones.getBoolFromZoneProperty(theZone, "useSmoke", true)
|
||||
csarManager.smokeColor = cfxZones.getSmokeColorStringFromZoneProperty(theZone, "smokeColor", "blue")
|
||||
csarManager.smokeColor = dcsCommon.smokeColor2Num(csarManager.smokeColor)
|
||||
|
||||
|
||||
if cfxZones.hasProperty(theZone, "csarRedDelivered!") then
|
||||
csarManager.csarRedDelivered = cfxZones.getStringFromZoneProperty(theZone, "csarRedDelivered!", "*<none>")
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "csarBlueDelivered!") then
|
||||
csarManager.csarBlueDelivered = cfxZones.getStringFromZoneProperty(theZone, "csarBlueDelivered!", "*<none>")
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "csarDelivered!") then
|
||||
csarManager.csarDelivered = cfxZones.getStringFromZoneProperty(theZone, "csarDelivered!", "*<none>")
|
||||
trigger.action.outText("+++csar: will bang csarDelivered: <" .. csarManager.csarDelivered .. ">", 30)
|
||||
end
|
||||
|
||||
csarManager.rescueRadius = cfxZones.getNumberFromZoneProperty(theZone, "rescueRadius", 70) --70 -- must land within 50m to rescue
|
||||
csarManager.hoverRadius = cfxZones.getNumberFromZoneProperty(theZone, "hoverRadius", 30) -- 30 -- must hover within 10m of unit
|
||||
csarManager.hoverAlt = cfxZones.getNumberFromZoneProperty(theZone, "hoverAlt", 40) -- 40 -- must hover below this alt
|
||||
csarManager.hoverDuration = cfxZones.getNumberFromZoneProperty(theZone, "hoverDuration", 20) -- 20 -- must hover for this duration
|
||||
csarManager.rescueTriggerRange = cfxZones.getNumberFromZoneProperty(theZone, "rescueTriggerRange", 2000) -- 2000 -- when the unit pops smoke and radios
|
||||
csarManager.beaconSound = cfxZones.getStringFromZoneProperty(theZone, "beaconSound", "Radio_beacon_of_distress_on_121.ogg") --"Radio_beacon_of_distress_on_121,5_MHz.ogg"
|
||||
csarManager.pilotWeight = cfxZones.getNumberFromZoneProperty(theZone, "pilotWeight", 120) -- 120
|
||||
|
||||
csarManager.vectoring = cfxZones.getBoolFromZoneProperty(theZone, "vectoring", true)
|
||||
|
||||
if csarManager.verbose then
|
||||
trigger.action.outText("+++csar: read config", 30)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function csarManager.start()
|
||||
-- make sure we have loaded all relevant libraries
|
||||
@ -1099,6 +1291,9 @@ function csarManager.start()
|
||||
return false
|
||||
end
|
||||
|
||||
-- read config
|
||||
csarManager.readConfigZone()
|
||||
|
||||
-- install callbacks for helo-relevant events
|
||||
dcsCommon.addEventHandler(csarManager.somethingHappened, csarManager.preProcessor, csarManager.postProcessor)
|
||||
|
||||
@ -1118,7 +1313,7 @@ function csarManager.start()
|
||||
|
||||
-- now scan all zones to create ME-placed CSAR missions
|
||||
-- and populate the available mission.
|
||||
csarManager.processCASRZones()
|
||||
csarManager.processCSARZones()
|
||||
|
||||
-- now call update so we can monitor progress of all helos, and alert them
|
||||
-- when they are close to a CSAR
|
||||
|
||||
242
modules/groupTrackers.lua
Normal file
242
modules/groupTrackers.lua
Normal file
@ -0,0 +1,242 @@
|
||||
groupTracker = {}
|
||||
groupTracker.version = "1.0.0"
|
||||
groupTracker.verbose = false
|
||||
groupTracker.ups = 1
|
||||
groupTracker.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxZones", -- Zones, of course
|
||||
}
|
||||
groupTracker.trackers = {}
|
||||
|
||||
--[[--
|
||||
Version History
|
||||
1.0.0 - Initial version
|
||||
|
||||
--]]--
|
||||
|
||||
function groupTracker.addTracker(theZone)
|
||||
table.insert(groupTracker.trackers, theZone)
|
||||
end
|
||||
|
||||
function groupTracker.getTrackerByName(aName)
|
||||
for idx, aZone in pairs(groupTracker.trackers) do
|
||||
if aName == aZone.name then return aZone end
|
||||
end
|
||||
if groupTracker.verbose then
|
||||
trigger.action.outText("+++msgr: no tracker with name <" .. aName ..">", 30)
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--
|
||||
-- read attributes
|
||||
--
|
||||
|
||||
--
|
||||
-- adding a group to a tracker - called by other modules and API
|
||||
--
|
||||
function groupTracker.addGroupToTracker(theGroup, theTracker)
|
||||
if groupTracker.verbose then
|
||||
trigger.action.outText("+++gTrk: will add group <" .. theGroup:getName() .. "> to tracker " .. theTracker.name, 30)
|
||||
end
|
||||
|
||||
-- we have the tracker, add the group
|
||||
if not theTracker.trackedGroups then theTracker.trackedGroups = {} end
|
||||
table.insert(theTracker.trackedGroups, theGroup)
|
||||
|
||||
-- now bang/invoke addGroup!
|
||||
if theTracker.tAddGroup then
|
||||
cfxZones.pollFlag(theTracker.tAddGroup, "inc", theTracker)
|
||||
end
|
||||
|
||||
-- now set numGroups
|
||||
if theTracker.tNumGroups then
|
||||
cfxZones.setFlagValue(theTracker.tNumGroups, #theTracker.trackedGroups, theTracker)
|
||||
end
|
||||
|
||||
-- invoke callbacks
|
||||
end
|
||||
|
||||
function groupTracker.addGroupToTrackerNamed(theGroup, trackerName)
|
||||
if not trackerName then
|
||||
trigger.action.outText("+++gTrk: nil tracker in addGroupToTrackerNamed", 30)
|
||||
end
|
||||
if not theGroup then
|
||||
trigger.action.outText("+++gTrk: no group in addGroupToTrackerNamed <" .. trackerName .. ">", 30)
|
||||
return
|
||||
end
|
||||
|
||||
if not theGroup:isExist() then
|
||||
trigger.action.outText("+++gTrk: group does not exist in when adding to tracker <" .. trackerName .. ">", 30)
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
groupTracker.addGroupToTracker(theGroup, theTracker)
|
||||
end
|
||||
|
||||
-- read zone
|
||||
function groupTracker.createTrackerWithZone(theZone)
|
||||
-- init group tracking set
|
||||
theZone.trackedGroups = {}
|
||||
|
||||
if cfxZones.hasProperty(theZone, "numGroups!") then
|
||||
theZone.tNumGroups = cfxZones.getStringFromZoneProperty(theZone, "numGroups!", "<none>")
|
||||
-- we may need to zero this flag
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "addGroup!") then
|
||||
theZone.tAddGroup = cfxZones.getStringFromZoneProperty(theZone, "addGroup!", "<none>")
|
||||
-- we may need to zero this flag
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "removeGroup!") then
|
||||
theZone.tRemoveGroup = cfxZones.getStringFromZoneProperty(theZone, "removeGroup!", "<none>")
|
||||
-- we may need to zero this flag
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- update
|
||||
--
|
||||
function groupTracker.checkGroups(theZone)
|
||||
local filteredGroups = {}
|
||||
for idx, theGroup in pairs(theZone.trackedGroups) do
|
||||
-- see if this group can be transferred
|
||||
local isDead = false
|
||||
if theGroup:isExist() then
|
||||
local allUnits = theGroup:getUnits()
|
||||
isDead = true
|
||||
for idy, aUnit in pairs(allUnits) do
|
||||
if aUnit:getLife() > 1 then
|
||||
isDead = false -- at least one living unit
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
isDead = true -- no longer exists
|
||||
end
|
||||
|
||||
if isDead then
|
||||
-- bang deceased
|
||||
if groupTracker.verbose then
|
||||
trigger.action.outText("+++gTrk: dead group detected in " .. theZone.name .. ", discarding.", 30)
|
||||
end
|
||||
if theZone.tRemoveGroup then
|
||||
cfxZones.pollFlag(theZone.tRemoveGroup, "inc", theZone)
|
||||
end
|
||||
else
|
||||
-- transfer alive group
|
||||
table.insert(filteredGroups, theGroup)
|
||||
end
|
||||
|
||||
end
|
||||
-- now exchange filtered for current
|
||||
theZone.trackedGroups = filteredGroups
|
||||
--set new group value
|
||||
-- now set numGroups if defined
|
||||
if theZone.tNumGroups then
|
||||
cfxZones.setFlagValue(theZone.tNumGroups, #filteredGroups, theZone)
|
||||
end
|
||||
end
|
||||
|
||||
function groupTracker.update()
|
||||
-- call me in a second to poll triggers
|
||||
timer.scheduleFunction(groupTracker.update, {}, timer.getTime() + 1/groupTracker.ups)
|
||||
|
||||
for idx, theZone in pairs(groupTracker.trackers) do
|
||||
groupTracker.checkGroups(theZone)
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Config & Start
|
||||
--
|
||||
function groupTracker.trackGroupsInZone(theZone)
|
||||
|
||||
local trackerName = cfxZones.getStringFromZoneProperty(theZone, "addToTracker:", "<none>")
|
||||
if trackerName == "*" then trackerName = theZone.name end
|
||||
|
||||
local theTracker = groupTracker.getTrackerByName(trackerName)
|
||||
if not theTracker then
|
||||
trigger.action.outText("+++gTrk: trackGroupsInZone - no zone named <" .. trackerName .. ">", 30 )
|
||||
return
|
||||
end
|
||||
|
||||
local theGroups = cfxZones.allGroupsInZone(theZone, nil)
|
||||
for idx, aGroup in pairs(theGroups) do
|
||||
if groupTracker.verbose then
|
||||
trigger.action.outText("+++gTrk: <" .. theZone.name .. "> passed off group <" .. aGroup:getName() .. "> to <" .. trackerName .. ">", 30)
|
||||
end
|
||||
groupTracker.addGroupToTracker(aGroup, theTracker)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
function groupTracker.readConfigZone()
|
||||
local theZone = cfxZones.getZoneByName("groupTrackerConfig")
|
||||
if not theZone then
|
||||
if groupTracker.verbose then
|
||||
trigger.action.outText("+++gTrk: NO config zone!", 30)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
groupTracker.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
||||
|
||||
groupTracker.ups = cfxZones.getNumberFromZoneProperty(theZone, "ups", 1)
|
||||
|
||||
if groupTracker.verbose then
|
||||
trigger.action.outText("+++gTrk: read config", 30)
|
||||
end
|
||||
end
|
||||
|
||||
function groupTracker.start()
|
||||
-- lib check
|
||||
if not dcsCommon.libCheck then
|
||||
trigger.action.outText("cfx Group Tracker requires dcsCommon", 30)
|
||||
return false
|
||||
end
|
||||
if not dcsCommon.libCheck("cfx Group Tracker", groupTracker.requiredLibs) then
|
||||
return false
|
||||
end
|
||||
|
||||
-- read config
|
||||
groupTracker.readConfigZone()
|
||||
|
||||
-- process tracker Zones
|
||||
local attrZones = cfxZones.getZonesWithAttributeNamed("tracker")
|
||||
for k, aZone in pairs(attrZones) do
|
||||
groupTracker.createTrackerWithZone(aZone) -- process attributes
|
||||
groupTracker.addTracker(aZone) -- add to list
|
||||
end
|
||||
|
||||
-- find and process all zones that want me to immediately add
|
||||
-- units to the tracker
|
||||
local attrZones = cfxZones.getZonesWithAttributeNamed("addToTracker:")
|
||||
for k, aZone in pairs(attrZones) do
|
||||
groupTracker.trackGroupsInZone(aZone) -- process attributes
|
||||
end
|
||||
|
||||
-- start update
|
||||
groupTracker.update()
|
||||
|
||||
trigger.action.outText("cfx Group Tracker v" .. groupTracker.version .. " started.", 30)
|
||||
return true
|
||||
end
|
||||
|
||||
-- let's go!
|
||||
if not groupTracker.start() then
|
||||
trigger.action.outText("cfx Group Tracker aborted: missing libraries", 30)
|
||||
messenger = nil
|
||||
end
|
||||
|
||||
--[[--
|
||||
add a pass to immediately add units in zones with 'addToTracker'
|
||||
after zone pass
|
||||
|
||||
add an output flag for the total number of units it watches, so when that changes, a change is instigated automatically
|
||||
--]]--
|
||||
@ -1,5 +1,5 @@
|
||||
guardianAngel = {}
|
||||
guardianAngel.version = "2.0.2"
|
||||
guardianAngel.version = "2.0.3"
|
||||
guardianAngel.ups = 10
|
||||
guardianAngel.launchWarning = true -- detect launches and warn pilot
|
||||
guardianAngel.intervention = true -- remove missiles just before hitting
|
||||
@ -35,6 +35,8 @@ guardianAngel.requiredLibs = {
|
||||
- private option
|
||||
2.0.2 - poof! explosion option to show explosion on intervention
|
||||
- can be dangerous
|
||||
2.0.3 - fxDistance
|
||||
- mea cupa capability
|
||||
|
||||
|
||||
This script detects missiles launched against protected aircraft an
|
||||
@ -43,7 +45,8 @@ removes them when they are about to hit
|
||||
--]]--
|
||||
|
||||
guardianAngel.minMissileDist = 50 -- m. below this distance the missile is killed by god, not the angel :)
|
||||
guardianAngel.myEvents = {1, 15, 20, 21, 23} -- 1 - shot, 15 - birth, 20 - enter unit, 21 - player leave unit, 23 - start shooting
|
||||
guardianAngel.myEvents = {1, 15, 20, 21, 23, 2} -- 1 - shot, 15 - birth, 20 - enter unit, 21 - player leave unit, 23 - start shooting
|
||||
-- added 2 (hit) event to see if angel was defeated
|
||||
guardianAngel.safetyFactor = 1.8 -- for calculating dealloc range
|
||||
guardianAngel.unitsToWatchOver = {} -- I'll watch over these
|
||||
|
||||
@ -123,13 +126,7 @@ function guardianAngel.createQItem(theWeapon, theTarget, detectProbability)
|
||||
theItem.missed = false -- just keep watching for re-ack
|
||||
return theItem
|
||||
end
|
||||
--[[--
|
||||
function guardianAngel.detectItem(theItem)
|
||||
if theItem.detected then return end
|
||||
-- perform detection calculations here
|
||||
|
||||
end
|
||||
--]]--
|
||||
|
||||
|
||||
-- calculate a point in direction from plane (pln) to weapon (wpn), dist meters
|
||||
function guardianAngel.calcSafeExplosionPoint(wpn, pln, dist)
|
||||
@ -137,9 +134,27 @@ function guardianAngel.calcSafeExplosionPoint(wpn, pln, dist)
|
||||
local v = dcsCommon.vNorm(dirToWpn) -- |v| = 1
|
||||
local v = dcsCommon.vMultScalar(v, dist) -- |v| = dist
|
||||
local newPoint = dcsCommon.vAdd(pln, v)
|
||||
--trigger.action.outText("+++ gA: safe dist is ".. dist, 30)
|
||||
return newPoint
|
||||
end
|
||||
|
||||
function guardianAngel.bubbleCheck(wPos, w)
|
||||
if true then return false end
|
||||
for idx, aProtectee in pairs (guardianAngel.unitsToWatchOver) do
|
||||
local uP = aProtectee:getPoint()
|
||||
local d = math.floor(dcsCommon.dist(wPos, uP))
|
||||
if d < guardianAngel.minMissileDist * 2 then
|
||||
trigger.action.outText("+++gA: gazing at w=" .. w:getName() .. " APR:" .. aProtectee:getName() .. ", d=" .. d .. ", cutoff=" .. guardianAngel.minMissileDist, 30)
|
||||
if w:getTarget() then
|
||||
trigger.action.outText("+++gA: w is targeting " .. w:getTarget():getName(), 30)
|
||||
else
|
||||
trigger.action.outText("+++gA: w is NOT targeting anything")
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function guardianAngel.monitorItem(theItem)
|
||||
local w = theItem.theWeapon
|
||||
local ID = theItem.tID
|
||||
@ -164,6 +179,10 @@ function guardianAngel.monitorItem(theItem)
|
||||
local oldWPos = theItem.wP
|
||||
local A = w:getPoint() -- A is new point of weapon
|
||||
theItem.wp = A -- update new position, old is in oldWPos
|
||||
|
||||
-- new code: safety check with ALL protected wings
|
||||
local bubbleThreat = guardianAngel.bubbleCheck(A, w)
|
||||
|
||||
local B
|
||||
if currentTarget then B = currentTarget:getPoint() else B = A end
|
||||
|
||||
@ -202,7 +221,7 @@ function guardianAngel.monitorItem(theItem)
|
||||
-- now add some showy explosion so the missile
|
||||
-- doesn't just disappear
|
||||
if guardianAngel.explosion > 0 then
|
||||
local xP = guardianAngel.calcSafeExplosionPoint(A,B, 500)
|
||||
local xP = guardianAngel.calcSafeExplosionPoint(A,B, guardianAngel.fxDistance)
|
||||
trigger.action.explosion(xP, guardianAngel.explosion)
|
||||
end
|
||||
|
||||
@ -226,7 +245,7 @@ function guardianAngel.monitorItem(theItem)
|
||||
guardianAngel.invokeCallbacks("intervention", theItem.targetName, theItem.weaponName)
|
||||
w:destroy()
|
||||
if guardianAngel.explosion > 0 then
|
||||
local xP = guardianAngel.calcSafeExplosionPoint(A,B, 500)
|
||||
local xP = guardianAngel.calcSafeExplosionPoint(A,B, guardianAngel.fxDistance)
|
||||
trigger.action.explosion(xP, guardianAngel.explosion)
|
||||
end
|
||||
return false -- remove from list
|
||||
@ -415,6 +434,54 @@ function guardianAngel.somethingHappened(event)
|
||||
return
|
||||
end
|
||||
|
||||
if ID == 2 then
|
||||
if not guardianAngel.intervention then return end -- we don't intervene
|
||||
if not event.weapon then return end -- no weapon, no interest
|
||||
local theWeapon = event.weapon
|
||||
local wName = theWeapon:getName()
|
||||
local theTarget = event.target
|
||||
if not theTarget then return end -- should not happen, better safe then dead
|
||||
local tName = theTarget:getName()
|
||||
|
||||
local theProtegee = nil
|
||||
for idx, aProt in pairs(guardianAngel.unitsToWatchOver) do
|
||||
if tName == aProt:getName() then
|
||||
theProtegee = aProt
|
||||
end
|
||||
end
|
||||
|
||||
if not theProtegee then return end
|
||||
|
||||
-- one of our protegees was hit
|
||||
--trigger.action.outText("+++gA: Protegee " .. tName .. " was hit", 30)
|
||||
trigger.action.outText("+++gA: I:" .. theUnit:getName() .. " hit " .. tName .. " with " .. wName, 30)
|
||||
-- let's see if the victim was in our list of protected
|
||||
-- units
|
||||
local thePerp = nil
|
||||
for idx, anItem in pairs(guardianAngel.missilesInTheAir) do
|
||||
if anItem.weaponName == wName then
|
||||
thePerp = anItem
|
||||
end
|
||||
end
|
||||
|
||||
if not thePerp then return end
|
||||
--trigger.action.outText("+++gA: offender was known to gA: " .. wName, 30)
|
||||
|
||||
-- stats only: do target and intended target match?
|
||||
local theWTarget = theWeapon:getTarget()
|
||||
if not theWTarget then return end -- no target no interest
|
||||
local wtName = theWTarget:getName()
|
||||
if wtName == tName then
|
||||
trigger.action.outText("+++gA: perp's ill intent confirmed", 30)
|
||||
else
|
||||
trigger.action.outText("+++gA: UNINTENDED CONSEQUENCES", 30)
|
||||
end
|
||||
|
||||
-- if we should have protected: mea maxima culpa
|
||||
trigger.action.outText("[+++gA: Angel hangs her head in shame. Mea Culpa, " .. tName.."]", 30)
|
||||
return
|
||||
end
|
||||
|
||||
local myType = theUnit:getTypeName()
|
||||
if guardianAngel.verbose then
|
||||
trigger.action.outText("+++gA: event " .. ID .. " for unit " .. theUnit:getName() .. " of type " .. myType, 30)
|
||||
@ -469,6 +536,7 @@ function guardianAngel.readConfigZone()
|
||||
guardianAngel.announcer = cfxZones.getBoolFromZoneProperty(theZone, "announcer", true)
|
||||
guardianAngel.private = cfxZones.getBoolFromZoneProperty(theZone, "private", false)
|
||||
guardianAngel.explosion = cfxZones.getNumberFromZoneProperty(theZone, "explosion", -1)
|
||||
guardianAngel.fxDistance = cfxZones.getNumberFromZoneProperty(theZone, "fxDistance", 500)
|
||||
end
|
||||
|
||||
|
||||
|
||||
@ -155,7 +155,7 @@ function unitZone.checkZoneStatus(theZone)
|
||||
end
|
||||
|
||||
else
|
||||
-- we perform group cehck
|
||||
-- we perform group check
|
||||
for idx, aGroup in pairs(theGroups) do
|
||||
local gName=aGroup:getName()
|
||||
local hasMatch = false
|
||||
|
||||
BIN
tutorial & demo missions/demo - CSAR of Georgia.miz
Normal file
BIN
tutorial & demo missions/demo - CSAR of Georgia.miz
Normal file
Binary file not shown.
Binary file not shown.
BIN
tutorial & demo missions/demo - track this!.miz
Normal file
BIN
tutorial & demo missions/demo - track this!.miz
Normal file
Binary file not shown.
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user