mirror of
https://github.com/weyne85/DML.git
synced 2025-10-29 16:57:49 +00:00
Version 2.4.2
Player Score Update, Guardian Angel Update, new planeGuard
This commit is contained in:
parent
4e78dfcb65
commit
3a87f7b784
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -1,5 +1,5 @@
|
||||
cfxMX = {}
|
||||
cfxMX.version = "3.0.0"
|
||||
cfxMX.version = "3.0.1"
|
||||
cfxMX.verbose = false
|
||||
--[[--
|
||||
Mission data decoder. Access to ME-built mission structures
|
||||
@ -22,6 +22,7 @@ cfxMX.verbose = false
|
||||
3.0.0 - patch coalition.addGroup() to build unit table for wasUnit
|
||||
- pre-populate spawnedUnits coa, cat from MX
|
||||
- spawnedUnitGroupNameByName
|
||||
3.0.1 - new getClosestUnitToPoint()
|
||||
|
||||
--]]--
|
||||
|
||||
@ -33,7 +34,7 @@ cfxMX.groupNamesByID = {}
|
||||
cfxMX.groupIDbyName = {}
|
||||
cfxMX.unitIDbyName = {}
|
||||
cfxMX.groupCatByName = {}
|
||||
cfxMX.groupDataByName = {}
|
||||
cfxMX.groupDataByName = {} -- includes static groups!
|
||||
cfxMX.groupTypeByName = {} -- category of group: "helicopter", "plane", "ship"...
|
||||
cfxMX.groupCoalitionByName = {}
|
||||
cfxMX.groupHotByName = {}
|
||||
@ -413,6 +414,34 @@ function cfxMX.allGroupsInZoneByData(theZone, cat) -- returns groups indexed by
|
||||
return theGroupsInZone, count
|
||||
end
|
||||
|
||||
function cfxMX.getClosestUnitToPoint(A, filter) -- uses MX!
|
||||
if not A then return nil end
|
||||
if not filter then filter = "all" end
|
||||
local theUnit = nil
|
||||
local uIdx = nil
|
||||
local theGroup = nil
|
||||
local ax = A.x
|
||||
local az = A.z
|
||||
local closest = math.huge
|
||||
for name, gData in pairs(cfxMX.groupDataByName) do
|
||||
if filter == "all" or filter == cfxMX.groupTypeByName[name] then
|
||||
for idx, uData in pairs(gData.units) do
|
||||
-- use square delta, immediate
|
||||
local dx = ax - uData.x
|
||||
local dz = az - uData.y -- !!
|
||||
local d = dx * dx + dz * dz
|
||||
if d < closest then
|
||||
theUnit = uData
|
||||
theGroup = gData
|
||||
closest = d
|
||||
uIdx = idx
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return theUnit, theGroup, uIdx, closest^0.5
|
||||
end
|
||||
|
||||
function cfxMX.isDynamicPlayer(theUnit)
|
||||
if not theUnit then return false end
|
||||
if not theUnit.getName then return false end
|
||||
|
||||
@ -1,23 +1,21 @@
|
||||
delayFlag = {}
|
||||
delayFlag.version = "2.0.0"
|
||||
delayFlag.version = "2.1.0"
|
||||
delayFlag.verbose = false
|
||||
delayFlag.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxZones", -- Zones, of course
|
||||
}
|
||||
delayFlag.flags = {}
|
||||
|
||||
--[[--
|
||||
delay flags - simple flag switch & delay, allows for randomize
|
||||
and dead man switching
|
||||
|
||||
Copyright (c) 2022-2024 by Christian Franz and cf/x AG
|
||||
Copyright (c) 2022-2025 by Christian Franz and cf/x AG
|
||||
|
||||
Version History
|
||||
1.4.0 - dmlZones
|
||||
- delayLeft#
|
||||
2.0.0 - clean-up
|
||||
|
||||
2.1.0 - deprecated old attributes
|
||||
- QoL: warnings when inputs/outputs missing
|
||||
--]]--
|
||||
|
||||
function delayFlag.addDelayZone(theZone)
|
||||
@ -31,98 +29,73 @@ function delayFlag.getDelayZoneByName(aName)
|
||||
if delayFlag.verbose then
|
||||
trigger.action.outText("+++dlyF: no delay flag with name <" .. aName ..">", 30)
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--
|
||||
-- read attributes
|
||||
--
|
||||
--
|
||||
-- create rnd gen from zone
|
||||
--
|
||||
function delayFlag.createTimerWithZone(theZone)
|
||||
-- delay
|
||||
theZone.delayMin, theZone.delayMax = theZone:getPositiveRangeFromZoneProperty("timeDelay", 1) -- same as zone signature
|
||||
if delayFlag.verbose or theZone.verbose then
|
||||
trigger.action.outText("+++dlyF: time delay is <" .. theZone.delayMin .. ", " .. theZone.delayMax .. "> seconds", 30)
|
||||
end
|
||||
|
||||
-- watchflags:
|
||||
-- triggerMethod
|
||||
theZone.delayTriggerMethod = theZone:getStringFromZoneProperty("triggerMethod", "change")
|
||||
|
||||
if theZone:hasProperty("delayTriggerMethod") then
|
||||
theZone.delayTriggerMethod = theZone:getStringFromZoneProperty("delayTriggerMethod", "change")
|
||||
end
|
||||
|
||||
-- trigger flag
|
||||
if theZone:hasProperty("f?") then
|
||||
theZone.triggerDelayFlag = theZone:getStringFromZoneProperty("f?", "none")
|
||||
elseif theZone:hasProperty("in?") then
|
||||
theZone.triggerDelayFlag = theZone:getStringFromZoneProperty("in?", "none")
|
||||
elseif theZone:hasProperty("startDelay?") then
|
||||
if theZone:hasProperty("startDelay?") then
|
||||
theZone.triggerDelayFlag = theZone:getStringFromZoneProperty("startDelay?", "none")
|
||||
end
|
||||
|
||||
if theZone.triggerDelayFlag then
|
||||
theZone.lastDelayTriggerValue = theZone:getFlagValue(theZone.triggerDelayFlag)
|
||||
else
|
||||
trigger.action.outText("+++dealyFlag: WARNING - zone <" .. theZone.name .. "> has no input connected.", 30)
|
||||
end
|
||||
|
||||
|
||||
theZone.delayMethod = theZone:getStringFromZoneProperty("method", "inc")
|
||||
|
||||
if theZone:hasProperty("delayMethod") then
|
||||
theZone.delayMethod = theZone:getStringFromZoneProperty( "delayMethod", "inc")
|
||||
end
|
||||
|
||||
-- out flag
|
||||
theZone.delayDoneFlag = theZone:getStringFromZoneProperty("out!", "*<none>")
|
||||
|
||||
|
||||
if theZone:hasProperty("delayDone!") then
|
||||
theZone.delayDoneFlag = theZone:getStringFromZoneProperty( "delayDone!", "*<none>")
|
||||
else
|
||||
trigger.action.outText("+++delayFlag: WARNING - zone <" .. theZone.name .. "> has no output connected.", 30)
|
||||
end
|
||||
|
||||
-- stop the press!
|
||||
if theZone:hasProperty("stopDelay?") then
|
||||
theZone.triggerStopDelay = theZone:getStringFromZoneProperty("stopDelay?", "none")
|
||||
theZone.lastTriggerStopValue = theZone:getFlagValue(theZone.triggerStopDelay)
|
||||
end
|
||||
|
||||
-- pause and continue
|
||||
if theZone:hasProperty("pauseDelay?") then
|
||||
theZone.triggerPauseDelay = theZone:getStringFromZoneProperty("pauseDelay?", "none")
|
||||
theZone.lastTriggerPauseValue = theZone:getFlagValue(theZone.triggerPauseDelay)
|
||||
end
|
||||
|
||||
if theZone:hasProperty("continueDelay?") then
|
||||
theZone.triggerContinueDelay = theZone:getStringFromZoneProperty("continueDelay?", "none")
|
||||
theZone.lastTriggerContinueValue = theZone:getFlagValue(theZone.triggerContinueDelay)
|
||||
end
|
||||
|
||||
-- timeInfo
|
||||
if theZone:hasProperty("delayLeft") then
|
||||
if theZone:hasProperty("delayLeft") then -- deprecated
|
||||
theZone.delayTimeLeft = theZone:getStringFromZoneProperty("delayLeft", "*cfxIgnored")
|
||||
else
|
||||
theZone.delayTimeLeft = theZone:getStringFromZoneProperty("delayLeft#", "*cfxIgnored")
|
||||
end
|
||||
|
||||
-- init
|
||||
theZone.delayRunning = false
|
||||
theZone.delayPaused = false
|
||||
theZone.timeLimit = -1 -- current trigger time as calculated relative to getTime()
|
||||
theZone.timeLeft = -1 -- in seconds, always kept up to date
|
||||
-- but not really used
|
||||
|
||||
theZone:setFlagValue(theZone.delayTimeLeft, -1)
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- update
|
||||
--
|
||||
|
||||
function delayFlag.startDelay(theZone)
|
||||
-- refresh timer
|
||||
theZone.delayRunning = true
|
||||
@ -139,12 +112,10 @@ function delayFlag.startDelay(theZone)
|
||||
delay = delay + delayMin
|
||||
if delay > theZone.delayMax then delay = theZone.delayMax end
|
||||
if delay < 1 then delay = 1 end
|
||||
|
||||
if delayFlag.verbose or theZone.verbose then
|
||||
trigger.action.outText("+++dlyF: delay " .. theZone.name .. " range " .. delayMin .. "-" .. delayMax .. ": selected " .. delay, 30)
|
||||
end
|
||||
end
|
||||
|
||||
theZone.timeLimit = timer.getTime() + delay
|
||||
theZone:setFlagValue(theZone.delayTimeLeft, delay)
|
||||
end
|
||||
@ -164,14 +135,11 @@ end
|
||||
function delayFlag.update()
|
||||
-- call me in a second to poll triggers
|
||||
timer.scheduleFunction(delayFlag.update, {}, timer.getTime() + 1)
|
||||
|
||||
local now = timer.getTime()
|
||||
|
||||
for idx, aZone in pairs(delayFlag.flags) do
|
||||
-- calculate remaining time on the timer
|
||||
local remaining = aZone.timeLimit - now
|
||||
if remaining < 0 then remaining = -1 end
|
||||
|
||||
-- see if we need to stop
|
||||
if aZone:testZoneFlag(aZone.triggerStopDelay, aZone.delayTriggerMethod, "lastTriggerStopValue") then
|
||||
aZone.delayRunning = false -- simply stop.
|
||||
@ -179,8 +147,6 @@ function delayFlag.update()
|
||||
trigger.action.outText("+++dlyF: stopped delay " .. aZone.name, 30)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if aZone:testZoneFlag(aZone.triggerDelayFlag, aZone.delayTriggerMethod, "lastDelayTriggerValue") then
|
||||
if delayFlag.verbose or aZone.verbose then
|
||||
if aZone.delayRunning then
|
||||
@ -194,14 +160,12 @@ function delayFlag.update()
|
||||
end
|
||||
|
||||
if not aZone.delayPaused then
|
||||
|
||||
if aZone.delayRunning and aZone:testZoneFlag( aZone.triggerPauseDelay, aZone.delayTriggerMethod, "lastTriggerPauseValue") then
|
||||
if delayFlag.verbose or aZone.verbose then
|
||||
trigger.action.outText("+++dlyF: pausing timer <" .. aZone.name .. "> with <" .. remaining .. "> remaining", 30)
|
||||
end
|
||||
delayFlag.pauseDelay(aZone)
|
||||
end
|
||||
|
||||
if aZone.delayRunning then
|
||||
-- check expiry
|
||||
if remaining < 0 then --now > aZone.timeLimit then
|
||||
@ -214,13 +178,12 @@ function delayFlag.update()
|
||||
aZone:pollFlag(aZone.delayDoneFlag, aZone.delayMethod)
|
||||
end
|
||||
end
|
||||
|
||||
aZone:setFlagValue(aZone.delayTimeLeft, remaining)
|
||||
else
|
||||
-- we are paused. Check for 'continue'
|
||||
if aZone.delayRunning and aZone:testZoneFlag( aZone.triggerContinueDelay, aZone.delayTriggerMethod, "lastTriggerContinueValue") then
|
||||
if delayFlag.verbose or aZone.verbose then
|
||||
trigger.action.outText("+++dlyF: continuing timer <" .. aZone.name .. "> with <" .. aZone.remainingTime .. "> seconds remaining", 30)
|
||||
trigger.action.outText("+++dlyF: continuing timer <" .. aZone.name .. "> with <" .. aZone.remainingTime .. "> seconds remaining", 30)
|
||||
end
|
||||
delayFlag.continueDelay(aZone)
|
||||
end
|
||||
@ -228,8 +191,6 @@ function delayFlag.update()
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- LOAD / SAVE
|
||||
--
|
||||
@ -247,7 +208,6 @@ function delayFlag.saveData()
|
||||
allTimers[theName] = timerData
|
||||
end
|
||||
theData.allTimers = allTimers
|
||||
|
||||
return theData
|
||||
end
|
||||
|
||||
@ -260,7 +220,6 @@ function delayFlag.loadData()
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local allTimers = theData.allTimers
|
||||
if not allTimers then
|
||||
if delayFlag.verbose then
|
||||
@ -268,7 +227,6 @@ function delayFlag.loadData()
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local now = timer.getTime()
|
||||
for theName, theData in pairs(allTimers) do
|
||||
local theTimer = delayFlag.getDelayZoneByName(theName)
|
||||
@ -293,16 +251,9 @@ function delayFlag.readConfigZone()
|
||||
if not theZone then
|
||||
theZone = cfxZones.createSimpleZone("delayFlagsConfig")
|
||||
end
|
||||
|
||||
delayFlag.verbose = theZone.verbose
|
||||
|
||||
if delayFlag.verbose then
|
||||
trigger.action.outText("+++dlyF: read config", 30)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
function delayFlag.start()
|
||||
-- lib check
|
||||
if not dcsCommon.libCheck then
|
||||
@ -313,17 +264,14 @@ function delayFlag.start()
|
||||
delayFlag.requiredLibs) then
|
||||
return false
|
||||
end
|
||||
|
||||
-- read config
|
||||
delayFlag.readConfigZone()
|
||||
|
||||
-- process cloner Zones
|
||||
local attrZones = cfxZones.getZonesWithAttributeNamed("timeDelay")
|
||||
for k, aZone in pairs(attrZones) do
|
||||
delayFlag.createTimerWithZone(aZone) -- process attributes
|
||||
delayFlag.addDelayZone(aZone) -- add to list
|
||||
end
|
||||
|
||||
-- load any saved data
|
||||
if persistence then
|
||||
-- sign up for persistence
|
||||
@ -333,10 +281,8 @@ function delayFlag.start()
|
||||
-- now load my data
|
||||
delayFlag.loadData()
|
||||
end
|
||||
|
||||
-- start update
|
||||
delayFlag.update()
|
||||
|
||||
trigger.action.outText("cfx Delay Flag v" .. delayFlag.version .. " started.", 30)
|
||||
return true
|
||||
end
|
||||
|
||||
@ -1,84 +1,37 @@
|
||||
guardianAngel = {}
|
||||
guardianAngel.version = "3.0.6"
|
||||
guardianAngel.ups = 10
|
||||
guardianAngel.version = "4.0.0"
|
||||
guardianAngel.ups = 10 -- hard-coded!! missile track
|
||||
guardianAngel.name = "Guardian Angel" -- just in case someone accesses .name
|
||||
guardianAngel.launchWarning = true -- detect launches and warn pilot
|
||||
guardianAngel.intervention = true -- remove missiles just before hitting
|
||||
guardianAngel.explosion = -1 -- small poof when missile explodes. -1 = off.
|
||||
guardianAngel.verbose = false -- debug info
|
||||
guardianAngel.announcer = true -- angel talks to you
|
||||
guardianAngel.private = false -- angel only talks to group
|
||||
guardianAngel.autoAddPlayers = true
|
||||
|
||||
guardianAngel.active = true -- can be turned on / off
|
||||
|
||||
guardianAngel.angelicZones = {}
|
||||
|
||||
guardianAngel.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxZones", -- Zones, of course
|
||||
}
|
||||
|
||||
-- Guardian Angel DML script (c) 2021-2025 by Charistian Franz
|
||||
--[[--
|
||||
Version History
|
||||
1.0.0 - Initial version
|
||||
2.0.0 - autoAddPlayer
|
||||
- verbose
|
||||
- lib check
|
||||
- config zone
|
||||
- sneaky detection logic
|
||||
- god intervention 100m
|
||||
- detect re-acquisition
|
||||
- addUnitToWatch supports string names
|
||||
- detect non-miss
|
||||
- announcer
|
||||
- intervene optional
|
||||
- launch warning optional
|
||||
2.0.1 - warnings go to group
|
||||
- invokeCallbacks added to module
|
||||
- reworked CB structure
|
||||
- private option
|
||||
2.0.2 - poof! explosion option to show explosion on intervention
|
||||
- can be dangerous
|
||||
2.0.3 - fxDistance
|
||||
- mea cupa capability
|
||||
3.0.0 - on/off and switch monitoring
|
||||
- active flag
|
||||
- zones to designate protected aircraft
|
||||
- zones to designate unprotected aircraft
|
||||
- improved gA logging
|
||||
- missilesAndTargets log
|
||||
- re-targeting detection
|
||||
- removed bubble check
|
||||
- retarget Item code
|
||||
- hardened missile disappear code
|
||||
- all missiles are now tracked regardless whom they aim for
|
||||
- removed item.wp
|
||||
3.0.1 - corrected error on collateral (missing delay)
|
||||
- Supporst cloned units
|
||||
- removed legacy code
|
||||
3.0.2 - added guardianAngel.name for those who use local flags on activate
|
||||
3.0.3 - monitorItem() guards against loss of target (nil)
|
||||
3.0.4 - launchSound attribute
|
||||
- interventionSound attribute
|
||||
3.0.5 - better missiole names, their object IDs seem to have disappeared, also storing launcher name
|
||||
- msgTime to control how long warnings remain on the screen
|
||||
- disappear message now only on verbose
|
||||
- dmlZones
|
||||
3.0.6 - Hardening of targets that aren't part of groups
|
||||
|
||||
|
||||
This script detects missiles launched against protected aircraft an
|
||||
removes them when they are about to hit
|
||||
|
||||
4.0.0 - code cleanup
|
||||
- DCS bug hardening
|
||||
- guardianAngel.autoAddPlayer implemented
|
||||
- removed preProcessor
|
||||
- removed postproc
|
||||
- removed isInterestingEvent
|
||||
- wired directly to onEvent
|
||||
- removed event id 20 (player enter)
|
||||
- mild performance tuning
|
||||
- new sanctuary zones
|
||||
- sancturay zones have floor and ceiling
|
||||
- once per second sanctuary calc
|
||||
- expanded getWatchedUnitByName to include inSanctuary
|
||||
- sanctuary zones use coalition
|
||||
--]]--
|
||||
|
||||
guardianAngel.minMissileDist = 50 -- m. below this distance the missile is killed by god, not the angel :)
|
||||
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.active = true -- can be turned on / off
|
||||
guardianAngel.angelicZones = {} -- angels be active here
|
||||
guardianAngel.sanctums = {} -- and here, but only as temps
|
||||
guardianAngel.minMissileDist = 50 -- m. below this distance the missile removed
|
||||
guardianAngel.safetyFactor = 1.8 -- for calculating dealloc range
|
||||
guardianAngel.unitsToWatchOver = {} -- I'll watch over these
|
||||
|
||||
guardianAngel.unitsToWatchOver = {} -- I'll watch over these units
|
||||
guardianAngel.inSanctuary = {} -- temporarily protected
|
||||
guardianAngel.missilesInTheAir = {} -- missiles in the air
|
||||
guardianAngel.missilesAndTargets = {} -- permanent log which missile was aimed at whom
|
||||
guardianAngel.callBacks = {} -- callbacks
|
||||
@ -96,45 +49,35 @@ function guardianAngel.invokeCallbacks(reason, targetName, weaponName)
|
||||
theCB(reason, targetName, weaponName)
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- units to watch
|
||||
-- units to watch (permanent protection list)
|
||||
--
|
||||
function guardianAngel.addUnitToWatch(aUnit)
|
||||
if type(aUnit) == "string" then
|
||||
aUnit = Unit.getByName(aUnit)
|
||||
end
|
||||
if not aUnit then return end
|
||||
local unitName = aUnit:getName()
|
||||
local isNew = guardianAngel.unitsToWatchOver[unitName] == nil
|
||||
guardianAngel.unitsToWatchOver[unitName] = aUnit
|
||||
if guardianAngel.verbose then
|
||||
if isNew then
|
||||
trigger.action.outText("+++gA: now watching unit " .. unitName, 30)
|
||||
else
|
||||
trigger.action.outText("+++gA: updating unit " .. unitName, 30)
|
||||
if isNew then trigger.action.outText("+++gA: now guarding unit " .. unitName, 30)
|
||||
else trigger.action.outText("+++gA: updating unit " .. unitName, 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function guardianAngel.removeUnitToWatch(aUnit)
|
||||
if type(aUnit) == "string" then
|
||||
aUnit = Unit.getByName(aUnit)
|
||||
end
|
||||
if not aUnit then return end
|
||||
local unitName = aUnit:getName()
|
||||
if not unitName then return end
|
||||
guardianAngel.unitsToWatchOver[unitName] = nil
|
||||
if guardianAngel.verbose then
|
||||
trigger.action.outText("+++gA: no longer watching " .. aUnit:getName(), 30)
|
||||
end
|
||||
if guardianAngel.verbose then trigger.action.outText("+++gA: no longer watching " .. aUnit:getName(), 30) end
|
||||
end
|
||||
|
||||
function guardianAngel.getWatchedUnitByName(aName)
|
||||
if not aName then return nil end
|
||||
return guardianAngel.unitsToWatchOver[aName]
|
||||
local u = guardianAngel.unitsToWatchOver[aName]
|
||||
if u then return u end -- always protected
|
||||
return guardianAngel.inSanctuary[aName] -- returns unit or nil
|
||||
end
|
||||
|
||||
--
|
||||
-- watch q items
|
||||
--
|
||||
@ -145,28 +88,22 @@ function guardianAngel.createQItem(theWeapon, theTarget, threat, launcher)
|
||||
if not threat then threat = false end
|
||||
-- if an item is not a 'threat' it means that we merely
|
||||
-- watch it for re-targeting purposes
|
||||
|
||||
local theItem = {}
|
||||
local oName = tostring(theWeapon:getName())
|
||||
if not oName or #oName < 1 then oName = dcsCommon.numberUUID() end
|
||||
local wName = ""
|
||||
if theWeapon.getDisplayName then
|
||||
wName = theWeapon:getDisplayName() -- does this even exist any more?
|
||||
elseif theWeapon.getTypeName then
|
||||
wName = theWeapon:getTypeName()
|
||||
else
|
||||
wName = "<Generic>"
|
||||
end
|
||||
elseif theWeapon.getTypeName then wName = theWeapon:getTypeName()
|
||||
else wName = "<Generic>" end
|
||||
wName = wName .. "-" .. oName
|
||||
local launcherName = launcher:getTypeName() .. " " .. launcher:getName()
|
||||
|
||||
theItem.theWeapon = theWeapon -- weapon that we are tracking
|
||||
theItem.weaponName = wName -- theWeapon:getName()
|
||||
-- usually weapons have no 'name' except an ID, so let's get the
|
||||
-- type or display name. Weapons often have no display name.
|
||||
if guardianAngel.verbose then
|
||||
trigger.action.outText("gA: tracking missile <" .. wName .. "> launched by <" .. launcherName .. ">", guardianAngel.msgTime)
|
||||
end
|
||||
-- usually weapons have no 'name' except an ID, so let's get
|
||||
-- type/display name. Weapons often have no display name.
|
||||
if guardianAngel.verbose then trigger.action.outText("gA: tracking missile <" .. wName .. "> launched by <" .. launcherName .. ">", guardianAngel.msgTime) end
|
||||
theItem.theTarget = theTarget
|
||||
if theTarget.getGroup then -- some targets may not have a group
|
||||
theItem.tGroup = theTarget:getGroup()
|
||||
@ -202,16 +139,12 @@ function guardianAngel.retargetItem(theItem, theTarget, threat)
|
||||
if not threat then threat = false end
|
||||
theItem.timeStamp = timer.getTime()
|
||||
theItem.threat = threat
|
||||
|
||||
theItem.theTarget = theTarget
|
||||
if not theTarget.getGroup then
|
||||
local theCat = theTarget:getCategory()
|
||||
if theCat ~= 2 then
|
||||
-- not a weapon / flare
|
||||
trigger.action.outText("*** gA: WARNING: <" .. theTarget:getName() .. "> has no getGroup and is of category <" .. theCat .. ">!!!", 30)
|
||||
|
||||
else
|
||||
-- target is a weapon (flare/chaff/decoy), all is well
|
||||
end
|
||||
else
|
||||
theItem.tGroup = theTarget:getGroup()
|
||||
@ -219,16 +152,13 @@ function guardianAngel.retargetItem(theItem, theTarget, threat)
|
||||
end
|
||||
theItem.targetName = theTarget:getName()
|
||||
theItem.lastDistance = math.huge
|
||||
--theItem.lostTrack = false
|
||||
theItem.missed = false
|
||||
theItem.lastDesc = "(retarget)"
|
||||
end
|
||||
|
||||
function guardianAngel.getQItemForWeaponNamed(theName)
|
||||
for idx, theItem in pairs (guardianAngel.missilesInTheAir) do
|
||||
if theItem.weaponName == theName then
|
||||
return theItem
|
||||
end
|
||||
if theItem.weaponName == theName then return theItem end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
@ -239,11 +169,9 @@ 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.monitorItem(theItem)
|
||||
local w = theItem.theWeapon
|
||||
local ID = theItem.tID
|
||||
@ -258,31 +186,14 @@ function guardianAngel.monitorItem(theItem)
|
||||
|
||||
local t = theItem.theTarget
|
||||
local currentTarget = w:getTarget()
|
||||
|
||||
-- Re-target check. did missile pick a new target?
|
||||
-- this can happen with any missile, even threat missiles,
|
||||
-- so do this always!
|
||||
local ctName = nil
|
||||
if currentTarget and Object.isExist(currentTarget) then
|
||||
-- get current name to check against last target name
|
||||
ctName = currentTarget:getName()
|
||||
else
|
||||
-- currentTarget has disappeared, kill the 'threat flag'
|
||||
-- theItem.threat = false
|
||||
ctName = "***guardianangel.not.set"
|
||||
end
|
||||
|
||||
if ctName and ctName ~= theItem.targetName then
|
||||
if guardianAngel.verbose then
|
||||
--trigger.action.outText("+++gA: RETARGETING for <" .. theItem.weaponName .. ">: from <" .. theItem.targetName .. "> to <" .. ctName .. ">", 30)
|
||||
end
|
||||
|
||||
local ctName = "***guardianAngel.not.set"
|
||||
if currentTarget and Object.isExist(currentTarget) then ctName = currentTarget:getName() end
|
||||
if ctName ~= theItem.targetName then
|
||||
-- see if it's a threat to us now
|
||||
local watchedUnit = guardianAngel.getWatchedUnitByName(ctName)
|
||||
|
||||
-- update the db who's seeking who
|
||||
guardianAngel.missilesAndTargets[theItem.weaponName] = ctName
|
||||
|
||||
-- should now update theItem to new target info
|
||||
isThreat = false
|
||||
if guardianAngel.getWatchedUnitByName(ctName) then
|
||||
@ -290,7 +201,6 @@ function guardianAngel.monitorItem(theItem)
|
||||
if guardianAngel.verbose then
|
||||
trigger.action.outText("+++gA: <" .. theItem.weaponName .. "> now targeting protected <" .. ctName .. ">!", 30)
|
||||
end
|
||||
|
||||
if isThreat and guardianAngel.announcer and guardianAngel.active then
|
||||
local desc = "Missile, missile, missile - now heading for " .. ctName .. "!"
|
||||
if guardianAngel.private and ID then
|
||||
@ -307,31 +217,21 @@ function guardianAngel.monitorItem(theItem)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
guardianAngel.retargetItem(theItem, currentTarget, isThreat)
|
||||
t = currentTarget
|
||||
else
|
||||
-- not ctName, or name as before.
|
||||
-- go on.
|
||||
end
|
||||
|
||||
-- we only progress here is the missile is a threat.
|
||||
-- if not, we keep it and check next time if it has
|
||||
-- retargeted a protegee
|
||||
if not theItem.threat then return true end
|
||||
|
||||
-- local oldWPos = theItem.wP
|
||||
local A = w:getPoint() -- A is new point of weapon
|
||||
-- theItem.wp = A -- update new position, old is in oldWPos
|
||||
|
||||
if not theItem.threat then return true end
|
||||
local A = w:getPoint() -- A is new point of weapon
|
||||
-- new code: safety check with ALL protected wings
|
||||
-- local bubbleThreat = guardianAngel.bubbleCheck(A, w)
|
||||
-- safety check removed, no benefit after new code
|
||||
|
||||
local B
|
||||
if currentTarget then B = currentTarget:getPoint() else B = A end
|
||||
|
||||
local d = math.floor(dcsCommon.dist(A, B))
|
||||
theItem.lastDistance = d -- save it for post mortem
|
||||
local desc = theItem.weaponName .. ": "
|
||||
@ -339,22 +239,18 @@ function guardianAngel.monitorItem(theItem)
|
||||
desc = desc .. "tracking " .. theItem.targetName .. ", d = " .. d .. "m"
|
||||
local vcc = dcsCommon.getClosingVelocity(t, w)
|
||||
desc = desc .. ", Vcc = " .. math.floor(vcc) .. "m/s"
|
||||
|
||||
-- now calculate lethal distance: vcc is in meters per second
|
||||
-- and we sample ups times per second
|
||||
-- making the missile cover vcc / ups meters in the next
|
||||
-- timer interval. If it now is closer than that, we have to
|
||||
-- destroy the missile
|
||||
-- calculate lethal distance: vcc is in meters per second
|
||||
-- we sample ups times per second
|
||||
-- making the missile move vcc / ups meters per
|
||||
-- timer interval. If it now is closer than that, we destroy msl
|
||||
local lethalRange = math.abs(vcc / guardianAngel.ups) * guardianAngel.safetyFactor
|
||||
desc = desc .. ", LR= " .. math.floor(lethalRange) .. "m"
|
||||
theItem.lastDesc = desc
|
||||
theItem.timeStamp = timer.getTime()
|
||||
|
||||
theItem.timeStamp = timer.getTime()
|
||||
if guardianAngel.intervention and
|
||||
d <= lethalRange + 10
|
||||
then
|
||||
desc = desc .. " ANGEL INTERVENTION"
|
||||
|
||||
if guardianAngel.announcer and ID then
|
||||
if guardianAngel.private then
|
||||
trigger.action.outTextForGroup(ID, desc, guardianAngel.msgTime)
|
||||
@ -372,24 +268,16 @@ function guardianAngel.monitorItem(theItem)
|
||||
end
|
||||
guardianAngel.invokeCallbacks("intervention", theItem.targetName, theItem.weaponName)
|
||||
w:destroy()
|
||||
|
||||
-- now add some showy explosion so the missile
|
||||
-- doesn't just disappear
|
||||
-- now add some showy explosion so the missile doesn't just disappear
|
||||
if guardianAngel.explosion > 0 then
|
||||
local xP = guardianAngel.calcSafeExplosionPoint(A,B, guardianAngel.fxDistance)
|
||||
trigger.action.explosion(xP, guardianAngel.explosion)
|
||||
end
|
||||
|
||||
return false -- remove from list
|
||||
end
|
||||
|
||||
if guardianAngel.intervention and
|
||||
d <= guardianAngel.minMissileDist -- god's override
|
||||
then
|
||||
desc = desc .. " GOD INTERVENTION"
|
||||
--if theItem.lostTrack then desc = desc .. " (little sneak!)" end
|
||||
--if theItem.missed then desc = desc .. " (missed you!)" end
|
||||
|
||||
if guardianAngel.intervention and d <= guardianAngel.minMissileDist then -- god's override
|
||||
desc = desc .. " GOD INTERVENTION"
|
||||
if guardianAngel.announcer and ID then
|
||||
if guardianAngel.private then
|
||||
trigger.action.outTextForGroup(ID, desc, guardianAngel.msgTime)
|
||||
@ -405,26 +293,16 @@ function guardianAngel.monitorItem(theItem)
|
||||
end
|
||||
return false -- remove from list
|
||||
end
|
||||
else
|
||||
|
||||
end
|
||||
|
||||
return true
|
||||
return true -- keep in list
|
||||
end
|
||||
|
||||
function guardianAngel.monitorMissiles()
|
||||
local newArray = {} -- we collect all still existing missiles here
|
||||
-- and replace missilesInTheAir with that for next round
|
||||
local newArray = {} -- monitor existing msl
|
||||
for idx, anItem in pairs (guardianAngel.missilesInTheAir) do
|
||||
-- we now have an item
|
||||
-- see about detection
|
||||
-- guardianAngel.detectItem(anItem)
|
||||
|
||||
-- see if the weapon is still in existence
|
||||
local stillAlive = guardianAngel.monitorItem(anItem)
|
||||
if stillAlive then
|
||||
table.insert(newArray, anItem)
|
||||
end
|
||||
if stillAlive then table.insert(newArray, anItem) end
|
||||
end
|
||||
guardianAngel.missilesInTheAir = newArray
|
||||
end
|
||||
@ -432,154 +310,60 @@ end
|
||||
function guardianAngel.filterItem(theItem)
|
||||
local w = theItem.theWeapon
|
||||
if not w then return false end
|
||||
if not w:isExist() then
|
||||
return false
|
||||
end
|
||||
if not w:isExist() then return false end
|
||||
return true -- missile still alive
|
||||
end
|
||||
|
||||
function guardianAngel.filterMissiles()
|
||||
local newArray = {} -- we collect all still existing missiles here
|
||||
-- and replace missilesInTheAir with that for next round
|
||||
local newArray = {} -- filter msl
|
||||
for idx, anItem in pairs (guardianAngel.missilesInTheAir) do
|
||||
-- we now have an item
|
||||
-- see about detection
|
||||
-- guardianAngel.detectItem(anItem)
|
||||
|
||||
-- see if the weapon is still in existence
|
||||
local stillAlive = guardianAngel.filterItem(anItem)
|
||||
if stillAlive then
|
||||
table.insert(newArray, anItem)
|
||||
end
|
||||
if stillAlive then table.insert(newArray, anItem) end
|
||||
end
|
||||
guardianAngel.missilesInTheAir = newArray
|
||||
end
|
||||
--
|
||||
-- E V E N T P R O C E S S I N G
|
||||
--
|
||||
function guardianAngel.isInteresting(eventID)
|
||||
-- return true if we are interested in this event, false else
|
||||
for key, evType in pairs(guardianAngel.myEvents) do
|
||||
if evType == eventID then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- event pre-proc: only return true if we need to process this event
|
||||
function guardianAngel.preProcessor(event)
|
||||
-- all events must have initiator set
|
||||
if not event.initiator then return false end
|
||||
-- see if the event ID is interesting for us
|
||||
local interesting = guardianAngel.isInteresting(event.id)
|
||||
return interesting
|
||||
end
|
||||
|
||||
function guardianAngel.postProcessor(event)
|
||||
-- don't do anything for now
|
||||
end
|
||||
|
||||
function guardianAngel.getAngelicZoneForUnit(theUnit)
|
||||
for idx, theZone in pairs(guardianAngel.angelicZones) do
|
||||
if cfxZones.unitInZone(theUnit, theZone) then
|
||||
return theZone
|
||||
end
|
||||
if cfxZones.unitInZone(theUnit, theZone) then return theZone end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- event callback from dcsCommon event handler. preProcessor has returned true
|
||||
function guardianAngel.somethingHappened(event)
|
||||
-- when this is invoked, the preprocessor guarantees that
|
||||
-- it's an interesting event and has initiator
|
||||
function guardianAngel:onEvent(event)
|
||||
if not event.initiator then return end
|
||||
local ID = event.id
|
||||
local theUnit = event.initiator
|
||||
-- make sure that this is a cat 0 or cat 1
|
||||
|
||||
local theUnit = event.initiator
|
||||
local playerName = nil
|
||||
if theUnit.getPlayerName then
|
||||
playerName = theUnit:getPlayerName() -- nil if not a player
|
||||
end
|
||||
|
||||
if theUnit.getPlayerName then playerName = theUnit:getPlayerName() end
|
||||
local mustProtect = false
|
||||
if ID == 15 and playerName then
|
||||
-- this is a player created unit
|
||||
if guardianAngel.verbose then
|
||||
trigger.action.outText("+++gA: player unit born " .. theUnit:getName(), 30)
|
||||
end
|
||||
if guardianAngel.autoAddPlayers then
|
||||
mustProtect = true
|
||||
end
|
||||
|
||||
theZone = guardianAngel.getAngelicZoneForUnit(theUnit)
|
||||
if theZone then
|
||||
mustProtect = theZone.angelic
|
||||
if theZone.verbose or guardianAngel.verbose then
|
||||
trigger.action.outText("+++gA: angelic zone " .. theZone.name .." -- protect: (" .. dcsCommon.bool2YesNo(mustProtect) .. ")", 30)
|
||||
end
|
||||
end
|
||||
|
||||
if mustProtect then
|
||||
guardianAngel.addUnitToWatch(theUnit)
|
||||
end
|
||||
|
||||
return
|
||||
elseif ID == 15 then
|
||||
-- AI spawn. check if it is an aircraft and in an angelic zone
|
||||
|
||||
if ID == 15 then
|
||||
-- AI/player spawn. check if it is an aircraft and in an angelic zone
|
||||
-- docs say that initiator is object. so let's see if when we
|
||||
-- get cat, this returns 1 for unit (as it should, so we can get
|
||||
-- group, or if it's really a unit, which returns 0 for aircraft
|
||||
local cat = theUnit:getCategory()
|
||||
--trigger.action.outText("birth event for " .. theUnit:getName() .. " with cat = " .. cat, 30)
|
||||
if cat ~= 1 then
|
||||
-- not a unit, bye bye
|
||||
return
|
||||
end
|
||||
if not theUnit.getCategory then return end
|
||||
local theGroup = theUnit:getGroup()
|
||||
if not theGroup then return end
|
||||
local gCat = theGroup:getCategory()
|
||||
if gCat == 0 or gCat == 1 then
|
||||
--trigger.action.outText("is aircraft cat " .. gCat, 30)
|
||||
|
||||
theZone = guardianAngel.getAngelicZoneForUnit(theUnit)
|
||||
if theZone then
|
||||
mustProtect = theZone.angelic
|
||||
if theZone.verbose or guardianAngel.verbose then
|
||||
trigger.action.outText("+++gA: angelic zone <" .. theZone.name .."> contains unit <" .. theUnit:getName() .. ">, protect it: " .. dcsCommon.bool2YesNo(mustProtect) .. ".", 30)
|
||||
end
|
||||
end
|
||||
|
||||
if mustProtect then
|
||||
guardianAngel.addUnitToWatch(theUnit)
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if ID == 20 and playerName then
|
||||
-- this is a player entered unit
|
||||
if guardianAngel.verbose then
|
||||
trigger.action.outText("+++gA: player seated in unit " .. theUnit:getName(), 30)
|
||||
end
|
||||
|
||||
if guardianAngel.autoAddPlayers then
|
||||
mustProtect = true
|
||||
end
|
||||
|
||||
if gCat ~= 0 and gCat ~= 1 then return end -- only fixed and rotor wing
|
||||
theZone = guardianAngel.getAngelicZoneForUnit(theUnit)
|
||||
if theZone then
|
||||
mustProtect = theZone.angelic
|
||||
if theZone.verbose or guardianAngel.verbose then
|
||||
trigger.action.outText("+++gA: angelic zone " .. theZone.name .." -- protect: (" .. dcsCommon.bool2YesNo(mustProtect) .. ")", 30)
|
||||
trigger.action.outText("+++gA: angelic zone <" .. theZone.name .."> contains unit <" .. theUnit:getName() .. ">, protect it: " .. dcsCommon.bool2YesNo(mustProtect) .. ".", 30)
|
||||
end
|
||||
end
|
||||
|
||||
if mustProtect then
|
||||
guardianAngel.addUnitToWatch(theUnit)
|
||||
end
|
||||
|
||||
if playerName and guardianAngel.autoAddPlayer then mustProtect = true end
|
||||
if mustProtect then guardianAngel.addUnitToWatch(theUnit) end
|
||||
return
|
||||
end
|
||||
|
||||
if ID == 21 and playerName then
|
||||
|
||||
if ID == 21 and playerName then -- player leave unit
|
||||
guardianAngel.removeUnitToWatch(theUnit)
|
||||
return
|
||||
end
|
||||
@ -589,7 +373,6 @@ function guardianAngel.somethingHappened(event)
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
if ID == 1 then
|
||||
-- even if not active, we collect missile data
|
||||
-- someone shot something. see if it is fire directed at me
|
||||
@ -597,12 +380,8 @@ function guardianAngel.somethingHappened(event)
|
||||
local theTarget
|
||||
if theWeapon then
|
||||
theTarget = theWeapon:getTarget()
|
||||
else
|
||||
return
|
||||
end
|
||||
if not theTarget then
|
||||
return
|
||||
end
|
||||
else return end
|
||||
if not theTarget then return end
|
||||
if not theTarget:isExist() then return end
|
||||
|
||||
-- if we get here, we have weapon aimed at a target
|
||||
@ -611,17 +390,13 @@ function guardianAngel.somethingHappened(event)
|
||||
local launcher = theUnit
|
||||
guardianAngel.missilesAndTargets[theWeapon:getName()] = targetName
|
||||
if not watchedUnit then
|
||||
-- we may still want to watch this if the missile
|
||||
-- can be re-targeted
|
||||
if guardianAngel.verbose then
|
||||
trigger.action.outText("+++gA: missile <" .. theWeapon:getName() .. "> targeting <" .. targetName .. ">, not a threat", 30)
|
||||
end
|
||||
if guardianAngel.verbose then trigger.action.outText("+++gA: missile <" .. theWeapon:getName() .. "> targeting <" .. targetName .. ">, not a threat", 30) end
|
||||
-- add it as no threat
|
||||
local theQItem = guardianAngel.createQItem(theWeapon, theTarget, false, launcher) -- this is not a threat, simply watch for re-target
|
||||
table.insert(guardianAngel.missilesInTheAir, theQItem)
|
||||
return
|
||||
end -- fired at some other poor sucker, we don't care
|
||||
|
||||
return -- fired at some other poor sucker, we don't care
|
||||
end
|
||||
-- if we get here, someone fired a guided weapon at my watched units
|
||||
-- create a new item for my queue
|
||||
local theQItem = guardianAngel.createQItem(theWeapon, theTarget, true, launcher) -- this is watched
|
||||
@ -632,12 +407,9 @@ function guardianAngel.somethingHappened(event)
|
||||
local A = theWeapon:getPoint()
|
||||
local B = theTarget:getPoint()
|
||||
local oclock = dcsCommon.clockPositionOfARelativeToB(A, B, unitHeading)
|
||||
|
||||
local grpID = theTarget:getGroup():getID()
|
||||
local vbInfo = ""
|
||||
if guardianAngel.verbose then
|
||||
vbInfo = ", <" .. theWeapon:getName() .. "> targeting <" .. targetName .. ">"
|
||||
end
|
||||
if guardianAngel.verbose then vbInfo = ", <" .. theWeapon:getName() .. "> targeting <" .. targetName .. ">" end
|
||||
if guardianAngel.launchWarning and guardianAngel.active then
|
||||
-- currently, we always detect immediately
|
||||
-- can be moved to update()
|
||||
@ -654,13 +426,12 @@ function guardianAngel.somethingHappened(event)
|
||||
trigger.action.outSound(fileName)
|
||||
end
|
||||
end
|
||||
|
||||
theQItem.detected = true -- remember: we detected and warned already
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if ID == 2 then
|
||||
if ID == 2 then -- hit
|
||||
if not guardianAngel.active then return end -- we aren't on watch.
|
||||
if not guardianAngel.intervention then return end -- we don't intervene
|
||||
if not event.weapon then return end -- no weapon, no interest
|
||||
@ -673,19 +444,14 @@ function guardianAngel.somethingHappened(event)
|
||||
local theProtegee = nil
|
||||
for idx, aProt in pairs(guardianAngel.unitsToWatchOver) do
|
||||
if aProt:isExist() then
|
||||
if tName == aProt:getName() then
|
||||
theProtegee = aProt
|
||||
end
|
||||
if tName == aProt:getName() then theProtegee = aProt end
|
||||
else
|
||||
if guardianAngel.verbose then
|
||||
trigger.action.outText("+++gA: whoops. Looks like I lost a wing there... sorry", 30)
|
||||
end
|
||||
if guardianAngel.verbose then trigger.action.outText("+++gA: Whoops. Looks like I lost a wing there... sorry", 30) end
|
||||
end
|
||||
end
|
||||
if not theProtegee then return end
|
||||
|
||||
-- one of our protegees was hit
|
||||
--trigger.action.outText("+++gA: Protegee " .. tName .. " was hit", 30)
|
||||
-- one of our protegees was hit
|
||||
trigger.action.outText("+++gA: I:" .. theUnit:getName() .. " hit " .. tName .. " with " .. wName, 30) -- note: theUnit is the LAUNCHER or the weapon!!!
|
||||
if guardianAngel.missilesAndTargets[wName] and guardianAngel.verbose then
|
||||
trigger.action.outText("+++gA: <" .. wName .. "> was originally aimed at <" .. guardianAngel.missilesAndTargets[wName] .. ">", 30)
|
||||
@ -696,63 +462,49 @@ function guardianAngel.somethingHappened(event)
|
||||
local wpnTgtName = "(none???)"
|
||||
if wpnTgt then wpnTgtName = wpnTgt:getName() end
|
||||
trigger.action.outText("+++gA: *current* weapon's target is <" .. wpnTgtName .. ">", 30)
|
||||
if wpnTgtName ~= tName then
|
||||
trigger.action.outText("+++gA: COLLATERAL DAMAGE!", 30)
|
||||
end
|
||||
if wpnTgtName ~= tName then trigger.action.outText("+++gA: COLLATERAL DAMAGE!", 30) end
|
||||
end
|
||||
else
|
||||
trigger.action.outText("***gA: no missile in the air for <" .. wName .. ">!!!!", 30)
|
||||
end
|
||||
-- let's see if the victim was in our list of protected
|
||||
-- units
|
||||
-- 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
|
||||
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)
|
||||
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)
|
||||
-- see if we can find the q item
|
||||
local missedItem = guardianAngel.getQItemForWeaponNamed(wName)
|
||||
if not missedItem then
|
||||
trigger.action.outText("Cannot retrieve item for <" .. wName .. ">", 30)
|
||||
if not missedItem then trigger.action.outText("Cannot retrieve item for <" .. wName .. ">", 30)
|
||||
else
|
||||
local now = timer.getTime()
|
||||
local delta = now - missedItem.timeStamp
|
||||
local wasThreat = dcsCommon.bool2YesNo(missedItem.threat)
|
||||
|
||||
trigger.action.outText("post: target was <" .. missedItem.targetName .. "> with last dist <" .. missedItem.lastDistance .. "> for weapon <" .. missedItem.weaponName .. ">, with dast desc = <" .. missedItem.lastDesc .. ">, <" .. delta .. "> s ago, Threat:(" .. wasThreat .. ")", 30)
|
||||
trigger.action.outText("postmortem: target was <" .. missedItem.targetName .. "> with last dist <" .. missedItem.lastDistance .. "> for weapon <" .. missedItem.weaponName .. ">, with dast desc = <" .. missedItem.lastDesc .. ">, <" .. delta .. "> s ago, Threat:(" .. wasThreat .. ")", 30)
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
local myType = theUnit:getTypeName()
|
||||
if guardianAngel.verbose then
|
||||
local myType = theUnit:getTypeName()
|
||||
trigger.action.outText("+++gA: event " .. ID .. " for unit " .. theUnit:getName() .. " of type " .. myType, 30)
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- U P D A T E L O O P
|
||||
--
|
||||
|
||||
|
||||
function guardianAngel.update()
|
||||
timer.scheduleFunction(guardianAngel.update, {}, timer.getTime() + 1/guardianAngel.ups)
|
||||
-- and break off if nothing to do
|
||||
@ -760,53 +512,85 @@ function guardianAngel.update()
|
||||
guardianAngel.filterMissiles()
|
||||
return
|
||||
end
|
||||
|
||||
guardianAngel.monitorMissiles()
|
||||
end
|
||||
|
||||
function guardianAngel.doActivate()
|
||||
guardianAngel.active = true
|
||||
if guardianAngel.verbose or guardianAngel.announcer then
|
||||
trigger.action.outText("Guardian Angel has activated", 30)
|
||||
end
|
||||
if guardianAngel.verbose or guardianAngel.announcer then trigger.action.outText("Guardian Angel has activated", 30) end
|
||||
end
|
||||
|
||||
function guardianAngel.doDeActivate()
|
||||
guardianAngel.active = false
|
||||
if guardianAngel.verbose or guardianAngel.announcer then
|
||||
trigger.action.outText("Guardian Angel NO LONGER ACTIVE", 30)
|
||||
end
|
||||
if guardianAngel.verbose or guardianAngel.announcer then trigger.action.outText("Guardian Angel NO LONGER ACTIVE", 30) end
|
||||
end
|
||||
|
||||
function guardianAngel.flagUpdate()
|
||||
timer.scheduleFunction(guardianAngel.flagUpdate, {}, timer.getTime() + 1) -- once every second
|
||||
|
||||
-- check the flags for on/off
|
||||
if guardianAngel.activate then
|
||||
if cfxZones.testZoneFlag(guardianAngel, guardianAngel.activate, "change","lastActivate") then
|
||||
if cfxZones.testZoneFlag(guardianAngel, guardianAngel.activate, "change","lastActivate") then
|
||||
guardianAngel.doActivate()
|
||||
end
|
||||
end
|
||||
|
||||
if guardianAngel.deactivate then
|
||||
if cfxZones.testZoneFlag(guardianAngel, guardianAngel.deactivate, "change","lastDeActivate") then
|
||||
if cfxZones.testZoneFlag(guardianAngel, guardianAngel.deactivate, "change","lastDeActivate") then
|
||||
guardianAngel.doDeActivate()
|
||||
end
|
||||
end
|
||||
|
||||
if #guardianAngel.sanctums > 0 then
|
||||
-- scan all planes against sanctums
|
||||
local filtered = {}
|
||||
for idx, theZone in pairs(guardianAngel.sanctums) do
|
||||
local f = theZone.floor
|
||||
local c = theZone.ceiling
|
||||
for coa=1, 2 do
|
||||
for cat=0, 1 do
|
||||
if theZone.coalition == 0 or theZone.coalition == coa then
|
||||
local allGroups = coalition.getGroups(coa, cat)
|
||||
for igp, aGroup in pairs(allGroups) do
|
||||
local allUnits = aGroup:getUnits()
|
||||
for iun, theUnit in pairs(allUnits) do
|
||||
local p = theUnit:getPoint()
|
||||
if f <= p.y and c >= p.y then
|
||||
if theZone:isPointInsideZone(p) then
|
||||
local name = theUnit:getName()
|
||||
filtered[name] = theUnit
|
||||
if theZone.verbose or guardianAngel.verbose then
|
||||
if filtered[name] ~= guardianAngel.inSanctuary[name] then
|
||||
if filtered[name] then
|
||||
trigger.action.outText("+++gA: <" .. name .. "> now in sanctuary <" .. theZone.name .. ">", 30)
|
||||
else
|
||||
trigger.action.outText("+++gA: <" .. name .. "> left sanctum <" .. theZone.name .. ">", 30)
|
||||
if guardianAngel.unitsToWatchOver[name] then
|
||||
trigger.action.outText("(still protected, though)", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end -- zone coa match
|
||||
end -- cat loop
|
||||
end -- coa loop
|
||||
end
|
||||
guardianAngel.inSanctuary = filtered
|
||||
end
|
||||
end
|
||||
|
||||
function guardianAngel.collectPlayerUnits()
|
||||
-- make sure we have all existing player units
|
||||
-- at start of game
|
||||
|
||||
for i=1, 2 do
|
||||
-- currently only two factions in dcs
|
||||
local factionUnits = coalition.getPlayers(i)
|
||||
for idx, theUnit in pairs(factionUnits) do
|
||||
local mustProtect = false
|
||||
if guardianAngel.autoAddPlayers then
|
||||
mustProtect = true
|
||||
end
|
||||
if guardianAngel.autoAddPlayer then mustProtect = true end
|
||||
|
||||
theZone = guardianAngel.getAngelicZoneForUnit(theUnit)
|
||||
if theZone then
|
||||
@ -815,11 +599,7 @@ function guardianAngel.collectPlayerUnits()
|
||||
trigger.action.outText("+++gA: angelic zone " .. theZone.name .." contains player unit <" .. theUnit:getName() .. "> -- protect: (" .. dcsCommon.bool2YesNo(mustProtect) .. ")", 30)
|
||||
end
|
||||
end
|
||||
|
||||
if mustProtect then
|
||||
guardianAngel.addUnitToWatch(theUnit)
|
||||
end
|
||||
|
||||
if mustProtect then guardianAngel.addUnitToWatch(theUnit) end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -844,10 +624,7 @@ function guardianAngel.collectAIUnits()
|
||||
trigger.action.outText("+++gA: angelic zone <" .. theZone.name .."> contains AI unit <" .. theUnit:getName() .. ">, protect it: " .. dcsCommon.bool2YesNo(mustProtect) .. ".", 30)
|
||||
end
|
||||
end
|
||||
|
||||
if mustProtect then
|
||||
guardianAngel.addUnitToWatch(theUnit)
|
||||
end
|
||||
if mustProtect then guardianAngel.addUnitToWatch(theUnit) end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -863,10 +640,7 @@ function guardianAngel.readConfigZone()
|
||||
if not theZone then
|
||||
theZone = cfxZones.createSimpleZone("guardianAngelConfig")
|
||||
end
|
||||
|
||||
|
||||
guardianAngel.verbose = theZone:getBoolFromZoneProperty("verbose", false)
|
||||
|
||||
guardianAngel.verbose = theZone.verbose
|
||||
guardianAngel.autoAddPlayer = theZone:getBoolFromZoneProperty("autoAddPlayer", true)
|
||||
guardianAngel.launchWarning = theZone:getBoolFromZoneProperty("launchWarning", true)
|
||||
guardianAngel.intervention = theZone:getBoolFromZoneProperty("intervention", true)
|
||||
@ -876,9 +650,7 @@ function guardianAngel.readConfigZone()
|
||||
guardianAngel.fxDistance = theZone:getNumberFromZoneProperty( "fxDistance", 500)
|
||||
|
||||
guardianAngel.active = theZone:getBoolFromZoneProperty("active", true)
|
||||
|
||||
guardianAngel.msgTime = theZone:getNumberFromZoneProperty("msgTime", 30)
|
||||
|
||||
if theZone:hasProperty("activate?") then
|
||||
guardianAngel.activate = theZone:getStringFromZoneProperty("activate?", "*<none>")
|
||||
guardianAngel.lastActivate = theZone:getFlagValue(guardianAngel.activate)
|
||||
@ -895,28 +667,17 @@ function guardianAngel.readConfigZone()
|
||||
guardianAngel.lastDeActivate = theZone:getFlagValue(guardianAngel.deactivate)
|
||||
end
|
||||
|
||||
if theZone:hasProperty("launchSound") then
|
||||
guardianAngel.launchSound = theZone:getStringFromZoneProperty("launchSound", "nosound")
|
||||
end
|
||||
if theZone:hasProperty("launchSound") then guardianAngel.launchSound = theZone:getStringFromZoneProperty("launchSound", "nosound") end
|
||||
|
||||
if theZone:hasProperty("interventionSound") then
|
||||
guardianAngel.interventionSound = theZone:getStringFromZoneProperty("interventionSound", "nosound")
|
||||
end
|
||||
if theZone:hasProperty("interventionSound") then guardianAngel.interventionSound = theZone:getStringFromZoneProperty("interventionSound", "nosound") end
|
||||
|
||||
guardianAngel.configZone = theZone
|
||||
if guardianAngel.verbose then
|
||||
trigger.action.outText("+++gA: processed config zone", 30)
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- guardian zones
|
||||
-- guardian/sanctuary zones
|
||||
--
|
||||
|
||||
function guardianAngel.processGuardianZone(theZone)
|
||||
theZone.angelic = theZone:getBoolFromZoneProperty("guardian", true)
|
||||
|
||||
|
||||
theZone.angelic = true -- theZone:getBoolFromZoneProperty("guardian", true)
|
||||
if theZone.verbose or guardianAngel.verbose then
|
||||
trigger.action.outText("+++gA: processed 'guardian' zone <" .. theZone.name .. ">", 30)
|
||||
end
|
||||
@ -926,50 +687,48 @@ end
|
||||
|
||||
function guardianAngel.readGuardianZones()
|
||||
local attrZones = cfxZones.getZonesWithAttributeNamed("guardian")
|
||||
for k, aZone in pairs(attrZones) do
|
||||
guardianAngel.processGuardianZone(aZone)
|
||||
end
|
||||
for k, aZone in pairs(attrZones) do guardianAngel.processGuardianZone(aZone) end
|
||||
end
|
||||
|
||||
function guardianAngel.processSanctuaryZone(theZone)
|
||||
theZone.floor = theZone:getNumberFromZoneProperty("floor", -1000)
|
||||
theZone.ceiling = theZone:getNumberFromZoneProperty("ceiling", 100000)
|
||||
theZone.coalition = theZone:getCoalitionFromZoneProperty("sanctuary", 0)
|
||||
if theZone.verbose or guardianAngel.verbose then
|
||||
trigger.action.outText("+++gA: processed 'sanctuary' zone <" .. theZone.name .. ">", 30)
|
||||
end
|
||||
-- add it to my sanctuaries
|
||||
table.insert(guardianAngel.sanctums, theZone)
|
||||
end
|
||||
|
||||
function guardianAngel.readSantuaryZones()
|
||||
local attrZones = cfxZones.getZonesWithAttributeNamed("sanctuary")
|
||||
for k, aZone in pairs(attrZones) do guardianAngel.processSanctuaryZone(aZone) end
|
||||
end
|
||||
--
|
||||
-- start
|
||||
--
|
||||
function guardianAngel.start()
|
||||
-- lib check
|
||||
if not dcsCommon.libCheck("cfx Guardian Angel",
|
||||
guardianAngel.requiredLibs) then
|
||||
return false
|
||||
end
|
||||
|
||||
if not dcsCommon.libCheck("cfx Guardian Angel", guardianAngel.requiredLibs) then return false end
|
||||
-- read config
|
||||
guardianAngel.readConfigZone()
|
||||
|
||||
-- read guarded zones
|
||||
guardianAngel.readGuardianZones()
|
||||
|
||||
-- install event monitor
|
||||
dcsCommon.addEventHandler(guardianAngel.somethingHappened,
|
||||
guardianAngel.preProcessor,
|
||||
guardianAngel.postProcessor)
|
||||
|
||||
guardianAngel.readSantuaryZones()
|
||||
-- insert into evet loop
|
||||
world.addEventHandler(guardianAngel)
|
||||
-- collect all units that are already in the game at this point
|
||||
guardianAngel.collectPlayerUnits()
|
||||
guardianAngel.collectPlayerUnits(guardianAngel)
|
||||
guardianAngel.collectAIUnits()
|
||||
|
||||
-- start update
|
||||
-- start update for missiles
|
||||
guardianAngel.update()
|
||||
|
||||
-- start flag check
|
||||
guardianAngel.flagUpdate()
|
||||
|
||||
-- start flag/sanctuary checks
|
||||
guardianAngel.flagUpdate()
|
||||
trigger.action.outText("Guardian Angel v" .. guardianAngel.version .. " running", 30)
|
||||
return true
|
||||
end
|
||||
|
||||
function guardianAngel.testCB(reason, targetName, weaponName)
|
||||
trigger.action.outText("gA - CB for ".. reason .. ": " .. targetName .. " w: " .. weaponName, guardianAngel.msgTime)
|
||||
end
|
||||
|
||||
-- go go go
|
||||
if not guardianAngel.start() then
|
||||
trigger.action.outText("Loading Guardian Angel failed.", 30)
|
||||
@ -979,4 +738,7 @@ end
|
||||
-- test callback
|
||||
--guardianAngel.addCallback(guardianAngel.testCB)
|
||||
--guardianAngel.invokeCallbacks("A", "B", "C")
|
||||
--function guardianAngel.testCB(reason, targetName, weaponName)
|
||||
-- trigger.action.outText("gA - CB for ".. reason .. ": " .. targetName .. " w: " .. weaponName, guardianAngel.msgTime)
|
||||
--end
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxHeloTroops = {}
|
||||
cfxHeloTroops.version = "4.2.0"
|
||||
cfxHeloTroops.version = "4.2.1"
|
||||
cfxHeloTroops.verbose = false
|
||||
cfxHeloTroops.autoDrop = true
|
||||
cfxHeloTroops.autoPickup = false
|
||||
@ -19,6 +19,8 @@ cfxHeloTroops.requestRange = 500 -- meters
|
||||
- code cleanup
|
||||
4.2.0 - support for individual lase codes
|
||||
- support for drivable
|
||||
4.2.1 - increased verbosity
|
||||
- also supports 'pickupRang" for reverse-compatibility with manual typo.
|
||||
|
||||
--]]--
|
||||
cfxHeloTroops.minTime = 3 -- seconds beween tandings
|
||||
@ -492,9 +494,11 @@ function cfxHeloTroops.addGroundMenu(conf)
|
||||
end
|
||||
|
||||
function cfxHeloTroops.filterTroopsByType(unitsToLoad)
|
||||
if cfxHeloTroops.verbose then trigger.action.outText("+++heloT: enter filterTroops", 30) end
|
||||
local filteredGroups = {}
|
||||
for idx, aTeam in pairs(unitsToLoad) do
|
||||
local group = aTeam.group
|
||||
if cfxHeloTroops.verbose then trigger.action.outText("+++heloT: testing group <" .. group:getName() .. ">", 30) end
|
||||
local theTypes = dcsCommon.getGroupTypeString(group)
|
||||
|
||||
local aT = dcsCommon.splitString(theTypes, ",")
|
||||
@ -504,11 +508,19 @@ function cfxHeloTroops.filterTroopsByType(unitsToLoad)
|
||||
if cfxHeloTroops.legalTroops then
|
||||
if not dcsCommon.arrayContainsString(cfxHeloTroops.legalTroops, sT) then
|
||||
pass = false
|
||||
if cfxHeloTroops.verbose then
|
||||
local gName = group:getName()
|
||||
trigger.action.outText("+++heloT[legal]: type <" .. sT .. "> not in legalTroops, group <" .. gName .. "> discarded.", 30)
|
||||
end
|
||||
break
|
||||
end
|
||||
else
|
||||
if not dcsCommon.typeIsInfantry(sT) then
|
||||
pass = false
|
||||
if cfxHeloTroops.verbose then
|
||||
local gName = group:getName()
|
||||
trigger.action.outText("+++heloT[common]: type <" .. sT .. "> not in dcsC.infantry, group <" .. gName .. "> discarded.", 30)
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
@ -519,13 +531,24 @@ function cfxHeloTroops.filterTroopsByType(unitsToLoad)
|
||||
-- this one is managed by csarManager,
|
||||
-- don't load it for helo troops
|
||||
pass = false
|
||||
if cfxHeloTroops.verbose then
|
||||
local gName = group:getName()
|
||||
trigger.action.outText("+++heloT[csar]: group <" .. gName .. "> filtered: is CSAR target.", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if pass then
|
||||
table.insert(filteredGroups, aTeam)
|
||||
if cfxHeloTroops.verbose then
|
||||
local gName = group:getName()
|
||||
trigger.action.outText("+++heloT[menu]: group <" .. gName .. "> added to available troops.", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
if cfxHeloTroops.verbose then
|
||||
trigger.action.outText("+++heloT[menu]: returning with <" .. #filteredGroups .. "> available groups", 30)
|
||||
end
|
||||
return filteredGroups
|
||||
end
|
||||
|
||||
@ -981,6 +1004,9 @@ function cfxHeloTroops.readConfigZone()
|
||||
cfxHeloTroops.autoDrop = theZone:getBoolFromZoneProperty("autoDrop", false)
|
||||
cfxHeloTroops.autoPickup = theZone:getBoolFromZoneProperty("autoPickup", false)
|
||||
cfxHeloTroops.pickupRange = theZone:getNumberFromZoneProperty("pickupRange", 100)
|
||||
if theZone:hasProperty("pickupRang") then
|
||||
cfxHeloTroops.pickupRange = theZone:getNumberFromZoneProperty("pickupRang", 100)
|
||||
end
|
||||
cfxHeloTroops.combatDropScore = theZone:getNumberFromZoneProperty( "combatDropScore", 200)
|
||||
|
||||
cfxHeloTroops.actionSound = theZone:getStringFromZoneProperty("actionSound", "Quest Snare 3.wav")
|
||||
|
||||
@ -144,7 +144,7 @@ function jtacGrpUI.doCommandX(args)
|
||||
local targetList = jtacGrpUI.collectJTACtargets(conf, true)
|
||||
-- iterate the list
|
||||
if #targetList < 1 then
|
||||
trigger.action.outTextForGroup(conf.id, "No targets are currently being lased", 30)
|
||||
trigger.action.outTextForGroup(conf.id, "No targets are currently being lased by ground forces", 30)
|
||||
trigger.action.outSoundForGroup(conf.id, jtacGrpUI.jtacSound)
|
||||
return
|
||||
end
|
||||
|
||||
188
modules/planeGuard.lua
Normal file
188
modules/planeGuard.lua
Normal file
@ -0,0 +1,188 @@
|
||||
planeGuard = {}
|
||||
planeGuard.version = "1.0.0"
|
||||
planeGuard.requiredLibs = {
|
||||
"dcsCommon",
|
||||
"cfxZones",
|
||||
"cfxMX"
|
||||
}
|
||||
planeGuard.zones = {} -- all zones, indexed by name
|
||||
|
||||
--[[--
|
||||
Version History
|
||||
1.0.0 - Initial version
|
||||
|
||||
--]]--
|
||||
|
||||
-- read zone
|
||||
function planeGuard.createPlaneGuardZone(theZone)
|
||||
theZone.hType = theZone:getStringFromZoneProperty("planeGuard", "CH-53E")
|
||||
theZone.alt = theZone:getNumberFromZoneProperty("alt", 40)
|
||||
theZone.count = 1
|
||||
-- immediately create the helo as plane guard
|
||||
planeGuard.createGroupForZone(theZone)
|
||||
end
|
||||
|
||||
function planeGuard.createGroupForZone(theZone)
|
||||
local A = theZone:getPoint()
|
||||
local theUnit, theGroup, idx = cfxMX.getClosestUnitToPoint(A, "ship")
|
||||
if not theUnit then
|
||||
trigger.action.outText("+++pGuard: can't find naval units for zone <" .. theUnit.name .. ">", 30)
|
||||
return
|
||||
end
|
||||
if theZone.verbose or planeGuard.verbose then trigger.action.outText("+++pGuard: attaching guard to unit <" .. theUnit.name .. "> in group <" .. theGroup.name .. "> for zone <" .. theZone.name .. ">", 30) end
|
||||
local h = theUnit.heading
|
||||
if not h then h = 0 end
|
||||
local ax = A.x
|
||||
local ay = A.z -- !!!
|
||||
local mainUnit = theGroup.units[1]
|
||||
-- calc point relative to MAIN unit and project onto
|
||||
-- rotated offsets
|
||||
local pgx = ax - mainUnit.x
|
||||
local pgy = ay - mainUnit.y
|
||||
pgx, pgy = dcsCommon.rotatePointAroundOriginRad(pgx, pgy, -h)
|
||||
local degrees = math.floor(h * 57.2958)
|
||||
local theName = theZone.name .. "-" .. theZone.count
|
||||
local sx = ax -- start coords
|
||||
local sy = ay
|
||||
theZone.count = theZone.count + 1
|
||||
local cty = cfxMX.countryByName[theGroup.name]
|
||||
local newGroupData = {
|
||||
["tasks"] = {},
|
||||
["radioSet"] = false,
|
||||
["task"] = "Transport",
|
||||
["route"] = {
|
||||
["points"] = {
|
||||
[1] = {
|
||||
["alt"] = 40,
|
||||
["action"] = "Turning Point",
|
||||
["alt_type"] = "BARO",
|
||||
["properties"] = {["addopt"] = {},},
|
||||
["speed"] = 7,
|
||||
["task"] = {
|
||||
["id"] = "ComboTask",
|
||||
["params"] = {
|
||||
["tasks"] = {
|
||||
[1] = {
|
||||
["enabled"] = true,
|
||||
["auto"] = false,
|
||||
["id"] = "WrappedAction",
|
||||
["number"] = 1,
|
||||
["params"] = {
|
||||
["action"] = {
|
||||
["id"] = "SetUnlimitedFuel",
|
||||
["params"] = { ["value"] = true, }, -- end of ["params"]
|
||||
}, -- end of ["action"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [1]
|
||||
}, -- end of ["tasks"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of ["task"]
|
||||
["type"] = "Turning Point",
|
||||
["ETA"] = 0,
|
||||
["ETA_locked"] = true,
|
||||
["y"] = sy + 100, -- start here
|
||||
["x"] = sx + 100, -- start here
|
||||
["speed_locked"] = true,
|
||||
}, -- end of [1]
|
||||
[2] = {
|
||||
["alt"] = 40,
|
||||
["action"] = "Turning Point",
|
||||
["alt_type"] = "BARO",
|
||||
["properties"] = {["addopt"] = {},},
|
||||
["speed"] = 7,
|
||||
["task"] = {
|
||||
["id"] = "ComboTask",
|
||||
["params"] = {
|
||||
["tasks"] = {
|
||||
[1] = {
|
||||
["number"] = 1,
|
||||
["auto"] = false,
|
||||
["id"] = "ControlledTask",
|
||||
["enabled"] = true,
|
||||
["params"] = {
|
||||
["task"] = {
|
||||
["id"] = "Follow",
|
||||
["params"] = {
|
||||
["lastWptIndexFlagChangedManually"] = true,
|
||||
["x"] = sx,
|
||||
["groupId"] = theGroup.groupId, --1, -- group to follow
|
||||
["lastWptIndex"] = 4,
|
||||
["lastWptIndexFlag"] = false,
|
||||
["y"] = sy,
|
||||
["pos"] = {
|
||||
["y"] = theZone.alt, -- alt
|
||||
["x"] = pgx, --(up/down)
|
||||
["z"] = pgy, --(left/right)
|
||||
}, -- end of ["pos"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of ["task"]
|
||||
["stopCondition"] = {["userFlag"] = "999",}, -- end of ["stopCondition"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [1]
|
||||
}, -- end of ["tasks"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of ["task"]
|
||||
["type"] = "Turning Point",
|
||||
["y"] = sy + 100,
|
||||
["x"] = sx + 100,
|
||||
}, -- end of [2]
|
||||
}, -- end of ["points"]
|
||||
}, -- end of ["route"]
|
||||
["hidden"] = false,
|
||||
["units"] = {
|
||||
[1] = {
|
||||
["alt"] = 40,
|
||||
["alt_type"] = "BARO",
|
||||
["skill"] = "High",
|
||||
["speed"] = 7,
|
||||
["type"] = theZone.hType,
|
||||
["y"] = sy + 100,
|
||||
["x"] = sx + 100,
|
||||
["name"] = theName .. "-1",
|
||||
["heading"] = 0,
|
||||
}, -- end of [1]
|
||||
}, -- end of ["units"]
|
||||
["y"] = sy + 100,
|
||||
["x"] = sx + 100,
|
||||
["name"] = theName,
|
||||
} -- end of theGroup
|
||||
-- allocate the new group. Always helo
|
||||
local pgGroup = coalition.addGroup(cty, 1, newGroupData)
|
||||
end
|
||||
|
||||
-- config
|
||||
function planeGuard.readConfigZone()
|
||||
local theZone = cfxZones.getZoneByName("planeGuardConfig")
|
||||
if not theZone then
|
||||
theZone = cfxZones.createSimpleZone("planeGuardConfig")
|
||||
end
|
||||
planeGuard.verbose = theZone.verbose
|
||||
end
|
||||
|
||||
-- go go go
|
||||
function planeGuard.start()
|
||||
-- lib check
|
||||
if not dcsCommon.libCheck then
|
||||
trigger.action.outText("planeGuard requires dcsCommon", 30)
|
||||
return false
|
||||
end
|
||||
if not dcsCommon.libCheck("planeGuard", planeGuard.requiredLibs) then
|
||||
return false
|
||||
end
|
||||
-- read config
|
||||
planeGuard.readConfigZone()
|
||||
-- process "planeGuard" Zones
|
||||
local attrZones = cfxZones.getZonesWithAttributeNamed("planeGuard")
|
||||
for k, aZone in pairs(attrZones) do
|
||||
planeGuard.createPlaneGuardZone(aZone)
|
||||
planeGuard.zones[aZone.name] = aZone
|
||||
end
|
||||
trigger.action.outText("planeGuard v" .. planeGuard.version .. " started.", 30)
|
||||
return true
|
||||
end
|
||||
|
||||
-- let's go!
|
||||
if not planeGuard.start() then
|
||||
trigger.action.outText("planeGuard aborted: error on start", 30)
|
||||
planeGuard = nil
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,29 +1,20 @@
|
||||
cfxPlayerScoreUI = {}
|
||||
cfxPlayerScoreUI.version = "3.0.1"
|
||||
cfxPlayerScoreUI.verbose = false
|
||||
cfxPlayerScoreUI.version = "3.1.0"
|
||||
|
||||
--[[-- VERSION HISTORY
|
||||
- 1.0.2 - initial version
|
||||
- 1.0.3 - module check
|
||||
- 2.0.0 - removed cfxPlayer dependency, handles own commands
|
||||
- 2.0.1 - late start capability
|
||||
- 2.1.0 - soundfile cleanup
|
||||
- score summary for side
|
||||
- allowAll
|
||||
- 2.1.1 - minor cleanup
|
||||
- 3.0.0 - compatible with dynamic groups/units in DCS 2.9.6
|
||||
- 3.0.1 - more hardening
|
||||
|
||||
- 3.1.0 - CA support
|
||||
- playerScoreUI correct ion for some methods
|
||||
- config zone support, attributes allowAll, soundFile, ranked
|
||||
- attachTo: support
|
||||
--]]--
|
||||
cfxPlayerScoreUI.requiredLibs = {
|
||||
"dcsCommon", -- this is doing score keeping
|
||||
"cfxZones", -- zones for config
|
||||
"cfxPlayerScore",
|
||||
}
|
||||
cfxPlayerScoreUI.soundFile = "Quest Snare 3.wav"
|
||||
cfxPlayerScoreUI.rootCommands = {} -- by unit's GROUP name, for player aircraft. stores command roots
|
||||
cfxPlayerScoreUI.allowAll = true
|
||||
cfxPlayerScoreUI.ranked = true
|
||||
|
||||
-- redirect: avoid the debug environ of missionCommands
|
||||
function cfxPlayerScoreUI.redirectCommandX(args)
|
||||
@ -38,20 +29,16 @@ function cfxPlayerScoreUI.doCommandX(args)
|
||||
if not theGroup then return end -- should not happen
|
||||
local gid = theGroup:getID()
|
||||
local coa = theGroup:getCoalition()
|
||||
|
||||
if not cfxPlayerScore.scoreTextForPlayerNamed then
|
||||
trigger.action.outText("***pSGUI: CANNOT FIND PlayerScore MODULE", 30)
|
||||
return
|
||||
end
|
||||
local desc = ""
|
||||
if what == "score" then
|
||||
desc = cfxPlayerScore.scoreTextForPlayerNamed(playerName)
|
||||
elseif what == "allMySide" then
|
||||
desc = cfxPlayerScore.scoreSummaryForPlayersOfCoalition(coa)
|
||||
if what == "score" then desc = cfxPlayerScore.scoreTextForPlayerNamed(playerName)
|
||||
elseif what == "allMySide" then desc = cfxPlayerScore.scoreSummaryForPlayersOfCoalition(coa)
|
||||
elseif what == "all" then
|
||||
desc = "Score Table For All Players:\n" .. cfxPlayerScore.scoreTextForAllPlayers(cfxPlayerScoreUI.ranked)
|
||||
else
|
||||
desc = "PlayerScore UI: unknown command <" .. what .. ">"
|
||||
else desc = "PlayerScore UI: unknown command <" .. what .. ">"
|
||||
end
|
||||
trigger.action.outTextForGroup(gid, desc, 30)
|
||||
trigger.action.outSoundForGroup(gid, cfxPlayerScoreUI.soundFile)
|
||||
@ -61,69 +48,83 @@ end
|
||||
-- event handling: we are only interested in birth events
|
||||
-- for player aircraft
|
||||
--
|
||||
function cfxPlayerScore.processPlayerUnit(theUnit)
|
||||
function cfxPlayerScoreUI.processPlayerUnit(theUnit)
|
||||
if not theUnit.getPlayerName then return end -- no player name, bye!
|
||||
local playerName = theUnit:getPlayerName()
|
||||
if not playerName then return end
|
||||
|
||||
-- so now we know it's a player plane. get group name
|
||||
-- now we know it's a player unit. get group name
|
||||
local theGroup = theUnit:getGroup()
|
||||
local groupName = theGroup:getName()
|
||||
local gid = theGroup:getID()
|
||||
|
||||
-- handle main menu
|
||||
local mainMenu = nil
|
||||
if cfxPlayerScoreUI.mainMenu then
|
||||
mainMenu = radioMenu.getMainMenuFor(cfxPlayerScoreUI.mainMenu)
|
||||
end
|
||||
-- see if this group already has a score command
|
||||
if cfxPlayerScoreUI.rootCommands[groupName] then
|
||||
-- need re-init to store new pilot name
|
||||
if cfxPlayerScoreUI.verbose then
|
||||
trigger.action.outText("++pSGui: group <" .. groupName .. "> already has score menu, removing.", 30)
|
||||
end
|
||||
if cfxPlayerScoreUI.verbose then trigger.action.outText("++pSGui: group <" .. groupName .. "> already has score menu, removing.", 30) end
|
||||
missionCommands.removeItemForGroup(gid, cfxPlayerScoreUI.rootCommands[groupName])
|
||||
cfxPlayerScoreUI.rootCommands[groupName] = nil
|
||||
end
|
||||
|
||||
-- we need to install a group menu item for scores.
|
||||
-- will persist through death
|
||||
-- we install a group menu item for scores.
|
||||
local commandTxt = "Show Score / Kills"
|
||||
local theMenu = missionCommands.addSubMenuForGroup(gid, "Show Score", nil)
|
||||
local theMenu = missionCommands.addSubMenuForGroup(gid, cfxPlayerScoreUI.menuName, mainMenu)
|
||||
local theCommand = missionCommands.addCommandForGroup(gid, commandTxt, theMenu, cfxPlayerScoreUI.redirectCommandX, {groupName, playerName, "score"})
|
||||
|
||||
commandTxt = "Show my Side Score / Kills"
|
||||
theCommand = missionCommands.addCommandForGroup(gid, commandTxt, theMenu, cfxPlayerScoreUI.redirectCommandX, {groupName, playerName, "allMySide"})
|
||||
|
||||
if cfxPlayerScoreUI.allowAll then
|
||||
commandTxt = "Show All Player Scores"
|
||||
theCommand = missionCommands.addCommandForGroup(gid, commandTxt, theMenu, cfxPlayerScoreUI.redirectCommandX, {groupName, playerName, "all"})
|
||||
end
|
||||
|
||||
cfxPlayerScoreUI.rootCommands[groupName] = theMenu
|
||||
|
||||
if cfxPlayerScoreUI.verbose then
|
||||
trigger.action.outText("++pSGui: installed player score menu for group <" .. groupName .. ">", 30)
|
||||
end
|
||||
end
|
||||
|
||||
function cfxPlayerScoreUI:onEvent(event)
|
||||
if event.id ~= 15 then return end -- only birth
|
||||
if event.id ~= 20 and -- S_EVENT_PLAYER_ENTER_UNIT -- CA support
|
||||
event.id ~= 15 then return end -- birth
|
||||
if not event.initiator then return end -- no initiator, no joy
|
||||
local theUnit = event.initiator
|
||||
cfxPlayerScore.processPlayerUnit(theUnit)
|
||||
cfxPlayerScoreUI.processPlayerUnit(theUnit)
|
||||
end
|
||||
|
||||
function cfxPlayerScoreUI.readConfig()
|
||||
cfxPlayerScoreUI.name = "playerScoreUIConfig"
|
||||
local theZone = cfxZones.getZoneByName("playerScoreUIConfig")
|
||||
if not theZone then
|
||||
theZone = cfxZones.createSimpleZone("playerScoreUIConfig")
|
||||
end
|
||||
if theZone:hasProperty("attachTo:") then
|
||||
local attachTo = theZone:getStringFromZoneProperty("attachTo:", "<none>")
|
||||
if radioMenu then -- requires optional radio menu to have loaded
|
||||
local mainMenu = radioMenu.mainMenus[attachTo]
|
||||
if mainMenu then cfxPlayerScoreUI.mainMenu = mainMenu
|
||||
else trigger.action.outText("+++PlayerScoreUI: cannot find super menu <" .. attachTo .. ">", 30) end
|
||||
else
|
||||
trigger.action.outText("+++PlayerScoreUI: REQUIRES radioMenu to run before PlayerScoreUI. 'AttachTo:' ignored.", 30)
|
||||
end
|
||||
end
|
||||
cfxPlayerScoreUI.menuName = theZone:getStringFromZoneProperty("menuName", "Show Score")
|
||||
cfxPlayerScoreUI.soundFile = theZone:getStringFromZoneProperty("SoundFile", "Quest Snare 3.wav")
|
||||
cfxPlayerScoreUI.allowAll = theZone:getBoolFromZoneProperty("allowAll", true)
|
||||
cfxPlayerScoreUI.ranked = theZone:getBoolFromZoneProperty("ranked", true)
|
||||
cfxPlayerScoreUI.verbose = theZone.verbose
|
||||
end
|
||||
--
|
||||
-- Start
|
||||
--
|
||||
function cfxPlayerScoreUI.start()
|
||||
if not dcsCommon.libCheck("cfx Player Score UI",
|
||||
cfxPlayerScoreUI.requiredLibs)
|
||||
then return false end
|
||||
-- install the event handler for new player planes
|
||||
if not dcsCommon.libCheck("cfx Player Score UI", cfxPlayerScoreUI.requiredLibs) then return false end
|
||||
-- install event handler for new player planes and CA
|
||||
world.addEventHandler(cfxPlayerScoreUI)
|
||||
-- process all existing players (late start)
|
||||
dcsCommon.iteratePlayers(cfxPlayerScore.processPlayerUnit)
|
||||
cfxPlayerScoreUI.readConfig()
|
||||
trigger.action.outText("cf/x PlayerScoreUI v" .. cfxPlayerScoreUI.version .. " started", 30)
|
||||
return true
|
||||
end
|
||||
|
||||
--
|
||||
-- GO GO GO
|
||||
--
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
pulseFlags = {}
|
||||
pulseFlags.version = "2.0.1"
|
||||
pulseFlags.version = "2.0.2"
|
||||
pulseFlags.verbose = false
|
||||
pulseFlags.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
@ -14,7 +14,7 @@ pulseFlags.requiredLibs = {
|
||||
- 2.0.0 dmlZones / OOP
|
||||
using method on all outputs
|
||||
- 2.0.1 activateZoneFlag now works correctly
|
||||
|
||||
- 2.0.2 fixed scheduledTime bug while persisting
|
||||
--]]--
|
||||
|
||||
pulseFlags.pulses = {}
|
||||
@ -264,7 +264,11 @@ function pulseFlags.saveData()
|
||||
pulseData.pulsePaused = thePulse.pulsePaused
|
||||
pulseData.pulsesLeft = thePulse.pulsesLeft
|
||||
pulseData.pulsing = thePulse.pulsing
|
||||
pulseData.scheduledTime = thePulse.scheduledTime - now
|
||||
if thePulse.scheduledTime then
|
||||
pulseData.scheduledTime = thePulse.scheduledTime - now
|
||||
else
|
||||
pulseData.scheduledTime = -1
|
||||
end
|
||||
pulseData.hasPulsed = thePulse.hasPulsed
|
||||
|
||||
allPulses[theName] = pulseData
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
radioMenu = {}
|
||||
radioMenu.version = "4.0.2"
|
||||
radioMenu.version = "4.1.0"
|
||||
radioMenu.verbose = false
|
||||
radioMenu.ups = 1
|
||||
radioMenu.requiredLibs = {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxSmokeZone = {}
|
||||
cfxSmokeZone.version = "2.0.0"
|
||||
cfxSmokeZone.version = "2.0.1"
|
||||
cfxSmokeZone.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxZones", -- Zones, of course
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxSpawnZones = {}
|
||||
cfxSpawnZones.version = "3.0.0"
|
||||
cfxSpawnZones.version = "3.0.1"
|
||||
cfxSpawnZones.requiredLibs = {
|
||||
"dcsCommon", -- common is of course needed for everything
|
||||
-- pretty stupid to check for this since we
|
||||
@ -19,11 +19,13 @@ cfxSpawnZones.spawnedGroups = {}
|
||||
--
|
||||
-- Zones that conform with this requirements spawn toops automatically
|
||||
-- *** DOES !NOT! EXTEND ZONES *** LINKED OWNER via masterOwner ***
|
||||
-- theSpawner.zone links back to dml zone that created spawner
|
||||
--
|
||||
--[[--
|
||||
-- version history
|
||||
3.0.0 - supports zone-individual laser code for "lase" orders
|
||||
- drivable attribute passed to groundTroops
|
||||
3.0.1 - increased vorbosity
|
||||
|
||||
--]]--
|
||||
|
||||
@ -214,7 +216,7 @@ function cfxSpawnZones.getRequestableSpawnersInRange(aPoint, aRange, aSide)
|
||||
local delta = dcsCommon.distFlat(aPoint, cfxZones.getPoint(aZone))
|
||||
if delta>aRange then
|
||||
hasMatch = false
|
||||
-- reasons = reasons .. "[distance " .. math.floor(delta) .. "]
|
||||
reasons = reasons .. "[distance " .. math.floor(delta) .. "] "
|
||||
end
|
||||
if aSide ~= 0 then
|
||||
-- check if side is correct for owned zone
|
||||
@ -222,7 +224,7 @@ function cfxSpawnZones.getRequestableSpawnersInRange(aPoint, aRange, aSide)
|
||||
-- failed ownership test. owner of master
|
||||
-- is not my own zone
|
||||
hasMatch = false
|
||||
-- reasons = reasons .. "[sawnOwnership] "
|
||||
reasons = reasons .. "[spawnOwnership] "
|
||||
end
|
||||
end
|
||||
|
||||
@ -230,7 +232,7 @@ function cfxSpawnZones.getRequestableSpawnersInRange(aPoint, aRange, aSide)
|
||||
-- only return spawners with this side
|
||||
-- note: this will NOT work with neutral players
|
||||
hasMatch = false
|
||||
-- reasons = reasons .. "[rawOwner] "
|
||||
reasons = reasons .. "[rawOwner] "
|
||||
end
|
||||
|
||||
if not aSpawner.requestable then
|
||||
@ -239,9 +241,13 @@ function cfxSpawnZones.getRequestableSpawnersInRange(aPoint, aRange, aSide)
|
||||
|
||||
if hasMatch then
|
||||
table.insert(theSpawners, aSpawner)
|
||||
-- trigger.action.outText("+++Spwn: ELIGIBLE spawner <" .. aSpawner.name .. ">", 30)
|
||||
-- else
|
||||
-- trigger.action.outText("+++Spwn: spawner <" .. aSpawner.name .. "> not eligible because " .. reasons, 30)
|
||||
if aSpawner.zone.verbose then
|
||||
trigger.action.outText("+++Spwn: ELIGIBLE spawner <" .. aSpawner.name .. ">", 30)
|
||||
end
|
||||
else
|
||||
if aSpawner.zone.verbose then
|
||||
trigger.action.outText("+++Spwn: spawner <" .. aSpawner.name .. "> not eligible because " .. reasons, 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
unitZone={}
|
||||
unitZone.version = "2.0.0"
|
||||
unitZone.version = "2.0.1"
|
||||
unitZone.verbose = false
|
||||
unitZone.ups = 1
|
||||
unitZone.requiredLibs = {
|
||||
@ -8,16 +8,6 @@ unitZone.requiredLibs = {
|
||||
}
|
||||
--[[--
|
||||
Version History
|
||||
1.0.0 - Initial Version
|
||||
1.1.0 - DML flag integration
|
||||
- method/uzMethod
|
||||
1.2.0 - uzOn?, uzOff?, triggerMethod
|
||||
1.2.1 - uzDirect
|
||||
1.2.2 - uzDirectInv
|
||||
1.2.3 - better guards for enterZone!, exitZone!, changeZone!
|
||||
- better guards for uzOn? and uzOff?
|
||||
1.2.4 - more verbosity on uzDirect
|
||||
1.2.5 - reading config improvement
|
||||
2.0.0 - matchAll option (internal, automatic look for "*" in names)
|
||||
- lookFor defaults to "*"
|
||||
- OOP dmlZones
|
||||
@ -27,6 +17,7 @@ unitZone.requiredLibs = {
|
||||
- unitZone now used to define the coalition, coalition DEPRECATED
|
||||
- filter synonym
|
||||
- direct#, directInv# synonyms
|
||||
2.0.1 - code hardening
|
||||
--]]--
|
||||
|
||||
unitZone.unitZones = {}
|
||||
@ -182,15 +173,11 @@ function unitZone.createUnitZone(theZone)
|
||||
|
||||
if unitZone.verbose or theZone.verbose then
|
||||
trigger.action.outText("+++uZne: processsed unit zone <" .. theZone.name .. "> with status = (" .. dcsCommon.bool2Text(theZone.lastStatus) .. ")", 30)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- process zone
|
||||
--
|
||||
|
||||
function unitZone.collectGroups(theZone)
|
||||
local collector = {} -- players: units, groups: groups
|
||||
if theZone.matching == "player" then
|
||||
@ -231,7 +218,6 @@ end
|
||||
function unitZone.checkZoneStatus(theZone)
|
||||
-- returns true (at least one unit found in zone)
|
||||
-- or false (no unit found in zone)
|
||||
|
||||
-- collect all groups to inspect
|
||||
local theGroups = unitZone.collectGroups(theZone)
|
||||
local lookFor = theZone.lookFor
|
||||
@ -241,40 +227,44 @@ function unitZone.checkZoneStatus(theZone)
|
||||
-- we check the names for players only
|
||||
-- collector holds units for players, not groups
|
||||
for idx, pUnit in pairs(theGroups) do
|
||||
local puName = pUnit:getName()
|
||||
local hasMatch = theZone.matchAll
|
||||
if not hasMatch then
|
||||
if theZone.lookForBeginsWith then
|
||||
hasMatch = dcsCommon.stringStartsWith(puName, lookFor)
|
||||
else
|
||||
hasMatch = puName == lookFor
|
||||
if Unit.isExist(pUnit) then
|
||||
local puName = pUnit:getName()
|
||||
local hasMatch = theZone.matchAll
|
||||
if not hasMatch then
|
||||
if theZone.lookForBeginsWith then
|
||||
hasMatch = dcsCommon.stringStartsWith(puName, lookFor)
|
||||
else
|
||||
hasMatch = puName == lookFor
|
||||
end
|
||||
end
|
||||
if hasMatch then
|
||||
if cfxZones.unitInZone(pUnit, theZone) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
if hasMatch then
|
||||
if cfxZones.unitInZone(pUnit, theZone) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
-- we perform group check.
|
||||
for idx, aGroup in pairs(theGroups) do
|
||||
local gName=aGroup:getName()
|
||||
local hasMatch = theZone.matchAll
|
||||
if not hasMatch then
|
||||
if theZone.lookForBeginsWith then
|
||||
hasMatch = dcsCommon.stringStartsWith(gName, lookFor)
|
||||
else
|
||||
hasMatch = gName == lookFor
|
||||
end
|
||||
end
|
||||
if hasMatch and aGroup:isExist() then
|
||||
-- check all living units in zone
|
||||
local gUnits = aGroup:getUnits()
|
||||
for idy, aUnit in pairs (gUnits) do
|
||||
if cfxZones.unitInZone(aUnit, theZone) then
|
||||
return true
|
||||
if Group.isExist(aGroup) then
|
||||
local gName=aGroup:getName()
|
||||
local hasMatch = theZone.matchAll
|
||||
if not hasMatch then
|
||||
if theZone.lookForBeginsWith then
|
||||
hasMatch = dcsCommon.stringStartsWith(gName, lookFor)
|
||||
else
|
||||
hasMatch = gName == lookFor
|
||||
end
|
||||
end
|
||||
if hasMatch and aGroup:isExist() then
|
||||
-- check all living units in zone
|
||||
local gUnits = aGroup:getUnits()
|
||||
for idy, aUnit in pairs (gUnits) do
|
||||
if cfxZones.unitInZone(aUnit, theZone) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -282,7 +272,6 @@ function unitZone.checkZoneStatus(theZone)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
--
|
||||
-- update
|
||||
--
|
||||
@ -365,7 +354,6 @@ function unitZone.update()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Config & Start
|
||||
--
|
||||
@ -375,11 +363,8 @@ function unitZone.readConfigZone()
|
||||
if not theZone then
|
||||
theZone = cfxZones.createSimpleZone("unitZoneConfig")
|
||||
end
|
||||
|
||||
unitZone.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
||||
|
||||
unitZone.ups = cfxZones.getNumberFromZoneProperty(theZone, "ups", 1)
|
||||
|
||||
if unitZone.verbose then
|
||||
trigger.action.outText("+++uZne: read config", 30)
|
||||
end
|
||||
@ -394,20 +379,16 @@ function unitZone.start()
|
||||
if not dcsCommon.libCheck("cfx Unit Zone", unitZone.requiredLibs) then
|
||||
return false
|
||||
end
|
||||
|
||||
-- read config
|
||||
unitZone.readConfigZone()
|
||||
|
||||
-- process cloner Zones
|
||||
local attrZones = cfxZones.getZonesWithAttributeNamed("unitZone")
|
||||
for k, aZone in pairs(attrZones) do
|
||||
unitZone.createUnitZone(aZone) -- process attributes
|
||||
unitZone.addUnitZone(aZone) -- add to list
|
||||
end
|
||||
|
||||
-- start update
|
||||
unitZone.update()
|
||||
|
||||
trigger.action.outText("cfx Unit Zone v" .. unitZone.version .. " started.", 30)
|
||||
return true
|
||||
end
|
||||
@ -418,6 +399,5 @@ if not unitZone.start() then
|
||||
unitZone = nil
|
||||
end
|
||||
|
||||
|
||||
--ToDo: add 'neutral' support and add 'both' option
|
||||
--ToDo: add API
|
||||
Binary file not shown.
BIN
tutorial & demo missions/demo - Guardians and Sanctuaries.miz
Normal file
BIN
tutorial & demo missions/demo - Guardians and Sanctuaries.miz
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
tutorial & demo missions/demo - plane guard.miz
Normal file
BIN
tutorial & demo missions/demo - plane guard.miz
Normal file
Binary file not shown.
BIN
tutorial & demo missions/demo - yes you CAn score.miz
Normal file
BIN
tutorial & demo missions/demo - yes you CAn score.miz
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user