mirror of
https://github.com/weyne85/DML.git
synced 2025-10-29 16:57:49 +00:00
Version 2.2.3
Many changes, most relating to "Expansion", some community requests. Little Documentation of those changes available yet, though.
This commit is contained in:
parent
2422d89a32
commit
07a32bd051
Binary file not shown.
Binary file not shown.
@ -1,5 +1,5 @@
|
||||
FARPZones = {}
|
||||
FARPZones.version = "2.0.0"
|
||||
FARPZones.version = "2.1.0"
|
||||
FARPZones.verbose = false
|
||||
--[[--
|
||||
Version History
|
||||
@ -16,6 +16,15 @@ FARPZones.verbose = false
|
||||
1.2.1 - now gracefully handles a FARP Zone that does not
|
||||
contain a FARP, but is placed beside it
|
||||
2.0.0 - dmlZones
|
||||
2.0.1 - locking up FARPS for the first five seconds
|
||||
loadMission handles lockup
|
||||
FARPs now can show their name
|
||||
showTitle attribute
|
||||
refresh attribute (config)
|
||||
2.0.2 - clean-up
|
||||
verbosity enhancements
|
||||
2.1.0 - integration with camp: needs repairs, produceResourceVehicles()
|
||||
|
||||
|
||||
--]]--
|
||||
|
||||
@ -23,6 +32,7 @@ FARPZones.requiredLibs = {
|
||||
"dcsCommon",
|
||||
"cfxZones", -- Zones, of course
|
||||
}
|
||||
FARPZones.lockup = {} -- for the first 5 seconds of the game, released later
|
||||
|
||||
-- *** DOES NOT EXTEND ZONES, USES OWN STRUCT ***
|
||||
-- *** SETS ZONE.OWNER IF PRESENT, POSSIBLE CONFLICT
|
||||
@ -77,11 +87,11 @@ FARPZones.resourceTypes = {
|
||||
FARPZones.spinUpDelay = 30 -- seconds until FARP becomes operational after capture
|
||||
|
||||
|
||||
FARPZones.allFARPZones = {}
|
||||
FARPZones.allFARPZones = {} -- indexed by zone, returns dmlFARP struct
|
||||
FARPZones.startingUp = false -- not needed / read anywhere
|
||||
|
||||
-- FARP ZONE ACCESS
|
||||
function FARPZones.addFARPZone(aFARP)
|
||||
function FARPZones.addFARPZone(aFARP) -- entry with dmlFARP struct
|
||||
FARPZones.allFARPZones[aFARP.zone] = aFARP
|
||||
end
|
||||
|
||||
@ -89,8 +99,8 @@ function FARPZones.removeFARPZone(aFARP)
|
||||
FARPZones.allFARPZones[aFARP.zone] = nil
|
||||
end
|
||||
|
||||
function FARPZones.getFARPForZone(aZone)
|
||||
return FARPZones.allFARPZones[aZone]
|
||||
function FARPZones.getFARPForZone(aZone) -- enter with zone
|
||||
return FARPZones.allFARPZones[aZone] -- returns dmlFARP
|
||||
end
|
||||
|
||||
function FARPZones.getFARPZoneByName(aName)
|
||||
@ -153,7 +163,12 @@ function FARPZones.createFARPFromZone(aZone)
|
||||
theFarp.point = theFarp.mainFarp:getPoint() -- this is FARP, not zone!!!
|
||||
theFarp.owner = theFarp.mainFarp:getCoalition()
|
||||
aZone.owner = theFarp.owner
|
||||
-- end
|
||||
-- lock up the faction until release in a few seconds after mission start
|
||||
theFarp.mainFarp:autoCapture(false)
|
||||
table.insert(FARPZones.lockup, theFarp.mainFarp) -- will be released in 5 seconds
|
||||
if aZone.verbose then
|
||||
trigger.action.outText("FARPzone <" .. aZone.name .. "> currently has owner <" .. aZone.owner .. ">", 30)
|
||||
end
|
||||
|
||||
-- get r and phi for defenders
|
||||
local rPhi = aZone:getVectorFromZoneProperty("rPhiHDef",3)
|
||||
@ -191,7 +206,7 @@ function FARPZones.createFARPFromZone(aZone)
|
||||
theFarp.hideBlue = aZone:getBoolFromZoneProperty("hideBlue", false)
|
||||
theFarp.hideGrey = aZone:getBoolFromZoneProperty("hideGrey", false)
|
||||
theFarp.hidden = aZone:getBoolFromZoneProperty("hidden", false)
|
||||
|
||||
theFarp.showTitle = aZone:getBoolFromZoneProperty("showTitle", true)
|
||||
theFarp.neutralProduction = aZone:getBoolFromZoneProperty("neutralProduction", false)
|
||||
return theFarp
|
||||
end
|
||||
@ -206,6 +221,12 @@ function FARPZones.drawFARPCircleInMap(theFarp)
|
||||
theFarp.zone.markID = nil
|
||||
end
|
||||
|
||||
if theFarp.zone and theFarp.zone.titleID then
|
||||
-- remove previous mark
|
||||
trigger.action.removeMark(theFarp.zone.titleID)
|
||||
theFarp.zone.titleID = nil
|
||||
end
|
||||
|
||||
if theFarp.hideRed and
|
||||
theFarp.owner == 1 then
|
||||
-- hide only when red
|
||||
@ -251,39 +272,12 @@ function FARPZones.drawFARPCircleInMap(theFarp)
|
||||
|
||||
trigger.action.circleToAll(-1, markID, thePoint, 2000, lineColor, fillColor, 1, true, "")
|
||||
aZone.markID = markID
|
||||
|
||||
end
|
||||
--[[--
|
||||
function FARPZones.drawZoneInMap(aZone, owner)
|
||||
-- owner is 0 = neutral, 1 = red, 2 = blue
|
||||
-- will save markID in zone's markID
|
||||
-- should be moved to cfxZones
|
||||
-- should be able to only show owned
|
||||
|
||||
if aZone.markID then
|
||||
trigger.action.removeMark(aZone.markID)
|
||||
if theFarp.showTitle then
|
||||
aZone.titleID = aZone:drawText(aZone.name, 16, lineColor, {0.8, 0.8, 0.8, 0.0})
|
||||
end
|
||||
|
||||
|
||||
local lineColor = {1.0, 0, 0, 1.0} -- red
|
||||
local fillColor = {1.0, 0, 0, 0.2} -- red
|
||||
|
||||
if owner == 2 then
|
||||
lineColor = {0.0, 0, 1.0, 1.0}
|
||||
fillColor = {0.0, 0, 1.0, 0.2}
|
||||
elseif owner == 0 then
|
||||
lineColor = {0.8, 0.8, 0.8, 1.0}
|
||||
fillColor = {0.8, 0.8, 0.8, 0.2}
|
||||
end
|
||||
|
||||
local theShape = 2 -- circle
|
||||
local markID = dcsCommon.numberUUID()
|
||||
|
||||
trigger.action.circleToAll(-1, markID, aZone.point, aZone.radius, lineColor, fillColor, 1, true, "")
|
||||
aZone.markID = markID
|
||||
|
||||
end
|
||||
--]]--
|
||||
|
||||
function FARPZones.scheduedProduction(args)
|
||||
-- args contain [aFarp, owner]
|
||||
@ -306,6 +300,35 @@ function FARPZones.scheduedProduction(args)
|
||||
end
|
||||
end
|
||||
|
||||
function FARPZones.serviceNeedsRepair(theFarp)
|
||||
if theFarp.owner == 0 then return false end
|
||||
if not theFarp.resources then return false end -- no group yet
|
||||
if not Group.isExist(theFarp.resources) then return true end
|
||||
if theFarp.resources:getSize() < #FARPZones.resourceTypes then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function FARPZones.produceResourceVehicles(theFarp, coa)
|
||||
if theFarp.resources and Group.isExist(theFarp.resources) then --theFarp.resources:isExist() then
|
||||
Group.destroy(theFarp.resources) --theFarp.resources:destroy()
|
||||
theFarp.resources = nil
|
||||
end
|
||||
local unitTypes = FARPZones.resourceTypes -- an array
|
||||
local theGroup, theData = cfxZones.createGroundUnitsInZoneForCoalition (
|
||||
coa,
|
||||
theFarp.name .. "-R" .. theFarp.count, -- must be unique
|
||||
theFarp.resZone,
|
||||
unitTypes,
|
||||
"line_v",
|
||||
theFarp.resHeading)
|
||||
theFarp.resources = theGroup
|
||||
theFarp.resourceData = theData
|
||||
-- update unique counter
|
||||
theFarp.count = theFarp.count + 1
|
||||
end
|
||||
|
||||
function FARPZones.produceVehicles(theFarp)
|
||||
local theZone = theFarp.zone
|
||||
-- trigger.action.outText("entering veh prod run for farp zone <" .. theZone.name .. ">, owner is <" .. theFarp.owner .. ">", 30)
|
||||
@ -357,6 +380,9 @@ function FARPZones.produceVehicles(theFarp)
|
||||
theFarp.defenderData = theData
|
||||
end
|
||||
|
||||
-- spawn resource vehicles
|
||||
FARPZones.produceResourceVehicles(theFarp, theCoalition)
|
||||
--[[--
|
||||
unitTypes = FARPZones.resourceTypes
|
||||
local theGroup, theData = cfxZones.createGroundUnitsInZoneForCoalition (
|
||||
theCoalition,
|
||||
@ -367,6 +393,7 @@ function FARPZones.produceVehicles(theFarp)
|
||||
theFarp.resHeading)
|
||||
theFarp.resources = theGroup
|
||||
theFarp.resourceData = theData
|
||||
--]]--
|
||||
-- update unique counter
|
||||
theFarp.count = theFarp.count + 1
|
||||
end
|
||||
@ -453,6 +480,20 @@ function FARPZones.somethingHappened(event)
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Update / Refresh
|
||||
--
|
||||
|
||||
function FARPZones.refreshMap()
|
||||
timer.scheduleFunction(FARPZones.refreshMap, {}, timer.getTime() + FARPZones.refresh)
|
||||
if FARPZones.verbose then
|
||||
trigger.action.outText("+++Farp map refresh started", 30)
|
||||
end
|
||||
|
||||
for idx, theFARP in pairs(FARPZones.allFARPZones) do
|
||||
FARPZones.drawFARPCircleInMap(theFARP)
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- LOAD / SAVE
|
||||
@ -503,6 +544,8 @@ function FARPZones.loadMission()
|
||||
if theFARP then
|
||||
theFARP.owner = fData.owner
|
||||
theFARP.zone.owner = fData.owner
|
||||
local theAB = theFARP.mainFarp
|
||||
theAB:setCoalition(theFARP.owner) -- FARP is in lockup.
|
||||
theFARP.defenderData = dcsCommon.clone(fData.defenderData)
|
||||
local groupData = fData.defenderData
|
||||
if groupData and #groupData.units > 0 then
|
||||
@ -532,6 +575,14 @@ end
|
||||
--
|
||||
-- Start
|
||||
--
|
||||
function FARPZones.releaseFARPS()
|
||||
-- trigger.action.outText("Releasing hold on FARPS", 30)
|
||||
for idx, aFarp in pairs(FARPZones.lockup) do
|
||||
aFarp:autoCapture(true)
|
||||
-- trigger.action.outText("releasing farp <" .. aFarp:getName() .. ">", 30)
|
||||
end
|
||||
end
|
||||
|
||||
function FARPZones.readConfig()
|
||||
local theZone = cfxZones.getZoneByName("farpZonesConfig")
|
||||
if not theZone then
|
||||
@ -542,6 +593,8 @@ function FARPZones.readConfig()
|
||||
|
||||
FARPZones.spinUpDelay = theZone:getNumberFromZoneProperty( "spinUpDelay", 30)
|
||||
|
||||
FARPZones.refresh = theZone:getNumberFromZoneProperty("refresh", -1)
|
||||
|
||||
end
|
||||
|
||||
|
||||
@ -598,6 +651,12 @@ function FARPZones.start()
|
||||
|
||||
FARPZones.startingUp = false -- not needed / read anywhere
|
||||
|
||||
timer.scheduleFunction(FARPZones.releaseFARPS, {}, timer.getTime() + 5)
|
||||
|
||||
if FARPZones.refresh > 0 then
|
||||
timer.scheduleFunction(FARPZones.refreshMap, {}, timer.getTime() + FARPZones.refresh)
|
||||
end
|
||||
|
||||
trigger.action.outText("cf/x FARP Zones v" .. FARPZones.version .. " started", 30)
|
||||
return true
|
||||
end
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxSSBClient = {}
|
||||
cfxSSBClient.version = "3.0.1"
|
||||
cfxSSBClient.version = "4.0.0"
|
||||
cfxSSBClient.verbose = false
|
||||
cfxSSBClient.singleUse = false -- set to true to block crashed planes
|
||||
-- NOTE: singleUse (true) requires SSB to disable immediate respawn after kick
|
||||
@ -8,76 +8,16 @@ cfxSSBClient.reUseAfter = -1 -- seconds for re-use delay
|
||||
|
||||
cfxSSBClient.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxGroups", -- for slot access
|
||||
"cfxMX", --"cfxGroups", -- for slot access
|
||||
"cfxZones", -- Zones, of course
|
||||
}
|
||||
|
||||
--[[--
|
||||
Version History
|
||||
1.0.0 - initial version
|
||||
1.1.0 - detect airfield by action and location, not group name
|
||||
1.1.1 - performance tuning. only read player groups once
|
||||
- and remove in-air-start groups from scan. this requires
|
||||
- ssb (server) be not modified
|
||||
1.2.0 - API to close airfields: invoke openAirFieldNamed()
|
||||
and closeAirfieldNamed() with name as string (exact match required)
|
||||
to block an airfield for any player aircraft.
|
||||
Works for FARPS as well
|
||||
API to associate a player group with any airfied's status (nil for unbind):
|
||||
cfxSSBClient.bindGroupToAirfield(group, airfieldName)
|
||||
API shortcut to unbind groups: cfxSSBClient.unbindGroup(group)
|
||||
verbose messages now identify better: "+++SSB:"
|
||||
keepInAirGroups option
|
||||
2.0.0 - include single-use ability: crashed airplanes are blocked from further use
|
||||
- single-use can be turned off
|
||||
- getPlayerGroupForGroupNamed()
|
||||
- split setSlotAccess to single accessor
|
||||
and interator
|
||||
- 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 (initial)
|
||||
- added verbosity
|
||||
2.0.3 - getPlayerName nil-trap on cloned player planes guard
|
||||
in onEvent
|
||||
2.1.0 - slotState
|
||||
- persistence
|
||||
3.0.0 - closing an airfield will not kick players who are active
|
||||
- much better verbosity
|
||||
- open?
|
||||
- close?
|
||||
- also persists closed airfield list
|
||||
3.0.1 - ability to detect if an airfield doesn't exist (late activate)
|
||||
|
||||
|
||||
|
||||
WHAT IT IS
|
||||
SSB Client is a small script that forms the client-side counterpart to
|
||||
Ciribob's simple slot block. It will block slots for all client airframes
|
||||
that are on an airfield that does not belong to the faction that currently
|
||||
owns the airfield.
|
||||
|
||||
REQUIRES CIRIBOB's SIMPLE SLOT BLOCK (SSB) TO RUN ON THE SERVER
|
||||
|
||||
If run without SSB, your planes will not be blocked.
|
||||
|
||||
In order to work, a plane that should be blocked when the airfield or
|
||||
FARP doesn't belong to the player's faction, the group's first unit
|
||||
must be within 3000 meters of the airfield and on the ground.
|
||||
Previous versions of this script relied on group names. No longer.
|
||||
|
||||
|
||||
WARNING:
|
||||
If you modified ssb's flag values, this script will not work
|
||||
|
||||
YOU DO NOT NEED TO ACTIVATE SBB, THIS SCRIPT DOES SO AUTOMAGICALLY
|
||||
|
||||
|
||||
4.0.0 - dmlZones
|
||||
- cfxMX instead of cfxGroups
|
||||
--]]--
|
||||
|
||||
-- below value for enabled MUST BE THE SAME AS THE VALUE OF THE SAME NAME
|
||||
-- IN SSB. DEFAULT IS ZERO, AND THIS WILL WORK
|
||||
|
||||
cfxSSBClient.enabledFlagValue = 0 -- DO NOT CHANGE, MUST MATCH SSB
|
||||
cfxSSBClient.disabledFlagValue = cfxSSBClient.enabledFlagValue + 100 -- DO NOT CHANGE
|
||||
cfxSSBClient.allowNeutralFields = false -- set to FALSE if players can't spawn on neutral airfields
|
||||
@ -124,26 +64,26 @@ end
|
||||
-- read client zones
|
||||
--
|
||||
function cfxSSBClient.createClientZone(theZone)
|
||||
local thePoint = cfxZones.getPoint(theZone)
|
||||
local thePoint = theZone:getPoint()
|
||||
local theAF = cfxSSBClient.getClosestAirbaseTo(thePoint)
|
||||
local afName = theAF:getName()
|
||||
if cfxSSBClient.verbose or theZone.verbose then
|
||||
trigger.action.outText("+++ssbc: zone <" .. theZone.name .. "> linked to AF/FARP <" .. afName .. ">", 30)
|
||||
end
|
||||
theZone.afName = afName
|
||||
theZone.ssbTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "ssbTriggerMethod", "change")
|
||||
theZone.ssbTriggerMethod = theZone:getStringFromZoneProperty( "ssbTriggerMethod", "change")
|
||||
|
||||
if cfxZones.hasProperty(theZone, "open?") then
|
||||
theZone.ssbOpen = cfxZones.getStringFromZoneProperty(theZone, "open?", "none")
|
||||
if theZone:hasProperty("open?") then
|
||||
theZone.ssbOpen = theZone:getStringFromZoneProperty("open?", "none")
|
||||
theZone.lastSsbOpen = cfxZones.getFlagValue(theZone.ssbOpen, theZone)
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "close?") then
|
||||
theZone.ssbClose = cfxZones.getStringFromZoneProperty(theZone, "close?", "none")
|
||||
if theZone:hasProperty("close?") then
|
||||
theZone.ssbClose = theZone:getStringFromZoneProperty("close?", "none")
|
||||
theZone.lastSsbClose = cfxZones.getFlagValue(theZone.ssbClose, theZone)
|
||||
end
|
||||
|
||||
theZone.ssbOpenOnStart = cfxZones.getBoolFromZoneProperty(theZone, "openOnStart", true)
|
||||
theZone.ssbOpenOnStart = theZone:getBoolFromZoneProperty( "openOnStart", true)
|
||||
if not theZone.ssbOpenOnStart then
|
||||
cfxSSBClient.closeAirfieldNamed(theZone.afName)
|
||||
end
|
||||
@ -494,7 +434,7 @@ end
|
||||
-- pre-process static player data to minimize
|
||||
-- processor load on checks
|
||||
function cfxSSBClient.processPlayerData()
|
||||
cfxSSBClient.playerGroups = cfxGroups.getPlayerGroup()
|
||||
cfxSSBClient.playerGroups = cfxMX.getPlayerGroup()
|
||||
local pGroups = cfxSSBClient.playerGroups
|
||||
local filteredPlayers = {}
|
||||
for idx, theGroup in pairs(pGroups) do
|
||||
@ -511,7 +451,7 @@ end
|
||||
|
||||
-- add airfield information to each player group
|
||||
function cfxSSBClient.processGroupData()
|
||||
local pGroups = cfxGroups.getPlayerGroup() -- we want the group.name attribute
|
||||
local pGroups = cfxMX.getPlayerGroup() -- we want the group.name attribute
|
||||
for idx, theGroup in pairs(pGroups) do
|
||||
-- we always use the first player's plane as referenced
|
||||
local playerData = theGroup.playerUnits[1]
|
||||
|
||||
@ -16,6 +16,7 @@ asw.fixes = {} -- all subs that we have a fix on. indexed by sub name
|
||||
Version History
|
||||
1.0.0 - initial version
|
||||
1.0.1 - integration with playerScore
|
||||
1.0.2 - new useSmoke attribute
|
||||
|
||||
--]]--
|
||||
|
||||
@ -151,7 +152,9 @@ function asw.dropBuoyFrom(theUnit)
|
||||
local theBuoy = asw.createBuoyForUnit(theUnit)
|
||||
|
||||
-- mark point
|
||||
if asw.useSmoke then
|
||||
dcsCommon.markPointWithSmoke(theBuoy.point, theBuoy.smokeColor)
|
||||
end
|
||||
theBuoy.smokeTimer = now + 5 * 60
|
||||
|
||||
-- add buoy to my inventory
|
||||
@ -202,7 +205,9 @@ function asw.dropBuoyFromZone(theZone)
|
||||
local theBuoy = asw.createBuoyForZone(theZone)
|
||||
|
||||
-- mark point
|
||||
if asw.useSmoke then
|
||||
dcsCommon.markPointWithSmoke(theBuoy.point, theBuoy.smokeColor)
|
||||
end
|
||||
theBuoy.smokeTimer = now + 5 * 60
|
||||
|
||||
-- add buoy to my inventory
|
||||
@ -369,7 +374,9 @@ function asw.updateBuoy(theBuoy, allSubs)
|
||||
-- see if we need to resmoke
|
||||
if now > theBuoy.smokeTimer then
|
||||
--env.info(" resmoking buoy, continue")
|
||||
if asw.useSmoke then
|
||||
dcsCommon.markPointWithSmoke(theBuoy.point, theBuoy.smokeColor)
|
||||
end
|
||||
theBuoy.smokeTimer = now + 5 * 60
|
||||
--env.info(" resmoke done, continue")
|
||||
end
|
||||
@ -1042,6 +1049,7 @@ function asw.readConfigZone()
|
||||
|
||||
asw.smokeColor = cfxZones.getSmokeColorStringFromZoneProperty(theZone, "smokeColor", "red")
|
||||
asw.smokeColor = dcsCommon.smokeColor2Num(asw.smokeColor)
|
||||
asw.useSmoke = theZone:getBoolFromZoneProperty("useSmoke", true)
|
||||
|
||||
asw.killScore = cfxZones.getNumberFromZoneProperty(theZone, "killScore", 0)
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
autoCSAR = {}
|
||||
autoCSAR.version = "2.0.1"
|
||||
autoCSAR.version = "2.1.0"
|
||||
autoCSAR.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxZones", -- Zones, of course
|
||||
@ -16,6 +16,7 @@ autoCSAR.trackedEjects = {} -- we start tracking on eject
|
||||
2.0.0 - OOP, code clean-up
|
||||
2.0.1 - fix for coalition change when ejected player changes coas or is forced to neutral
|
||||
- GC
|
||||
2.1.0 - persistence support
|
||||
--]]--
|
||||
|
||||
function autoCSAR.removeGuy(args)
|
||||
@ -195,6 +196,33 @@ function autoCSAR.GC()
|
||||
autoCSAR.pilotInfo = filtered
|
||||
end
|
||||
|
||||
--
|
||||
-- load/save
|
||||
--
|
||||
|
||||
function autoCSAR.saveData()
|
||||
local theData = {}
|
||||
theData.counter = autoCSAR.counter
|
||||
return theData, autoCSAR.sharedData
|
||||
end
|
||||
|
||||
function autoCSAR.loadData()
|
||||
if not persistence then return end
|
||||
local theData = persistence.getSavedDataForModule("autoCSAR", autoCSAR.sharedData)
|
||||
if not theData then
|
||||
if autoCSAR.verbose then
|
||||
trigger.action.outText("+++autoCSAR: no save data received, skipping.", 30)
|
||||
end
|
||||
return
|
||||
end
|
||||
if theData.counter then
|
||||
autoCSAR.counter = theData.counter
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- GO!
|
||||
--
|
||||
function autoCSAR.start()
|
||||
-- lib check
|
||||
if not dcsCommon.libCheck then
|
||||
@ -211,6 +239,16 @@ function autoCSAR.start()
|
||||
-- connect event handler
|
||||
world.addEventHandler(autoCSAR)
|
||||
|
||||
-- do persistence
|
||||
if persistence then
|
||||
-- sign up for persistence
|
||||
callbacks = {}
|
||||
callbacks.persistData = autoCSAR.saveData
|
||||
persistence.registerModule("autoCSAR", callbacks)
|
||||
-- now load my data
|
||||
autoCSAR.loadData()
|
||||
end
|
||||
|
||||
-- start GC
|
||||
timer.scheduleFunction(autoCSAR.GC, {}, timer.getTime() + 1)
|
||||
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
bank = {}
|
||||
bank.version = "0.0.0"
|
||||
bank.version = "1.0.0"
|
||||
bank.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxZones", -- Zones, of course
|
||||
}
|
||||
bank.acts = {}
|
||||
bank.acts = {} -- 'accounts'
|
||||
|
||||
function bank.addFunds(act, amt)
|
||||
if not act then act = "!!NIL!!" end
|
||||
@ -57,9 +57,9 @@ function bank.getBalance(act)
|
||||
return true, curVal
|
||||
end
|
||||
|
||||
function bank.openAccount(act, amount)
|
||||
function bank.openAccount(act, amount, oride)
|
||||
if not amount then amount = 0 end
|
||||
if bank.acts[act] then return false end -- account exists
|
||||
if bank.acts[act] and not oride then return false end -- account exists
|
||||
bank.acts[act] = amount
|
||||
return true
|
||||
end
|
||||
@ -79,9 +79,43 @@ function bank.readConfigZone()
|
||||
bank.acts["blue"] = bank.blue
|
||||
bank.acts["neutral"] = bank.neutral
|
||||
|
||||
if theZone:hasProperty("sharedData") then -- future-proof
|
||||
bank.sharedData = theZone:getStringFromZoneProperty("sharedData", "cfxNameMissing")
|
||||
end
|
||||
|
||||
bank.verbose = theZone.verbose
|
||||
end
|
||||
|
||||
--
|
||||
-- load / save (persistence)
|
||||
--
|
||||
function bank.saveData()
|
||||
local theData = {}
|
||||
-- save current score list. simple clone
|
||||
local acts = dcsCommon.clone(bank.acts)
|
||||
theData.acts = acts
|
||||
|
||||
return theData, bank.sharedData
|
||||
end
|
||||
|
||||
|
||||
function bank.loadData()
|
||||
if not persistence then return end
|
||||
local theData = persistence.getSavedDataForModule("bank", bank.sharedData)
|
||||
if not theData then
|
||||
if bank.verbose then
|
||||
trigger.action.outText("+++bank: no save data received, skipping.", 30)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local acts = theData.acts
|
||||
bank.acts = acts
|
||||
end
|
||||
|
||||
--
|
||||
-- start
|
||||
--
|
||||
function bank.start()
|
||||
-- lib check
|
||||
if not dcsCommon.libCheck then
|
||||
@ -95,6 +129,17 @@ function bank.start()
|
||||
-- read config
|
||||
bank.readConfigZone()
|
||||
|
||||
-- load data if persisted
|
||||
if persistence then
|
||||
-- sign up for persistence
|
||||
callbacks = {}
|
||||
callbacks.persistData = bank.saveData
|
||||
persistence.registerModule("bank", callbacks)
|
||||
-- now load my data
|
||||
bank.loadData()
|
||||
end
|
||||
|
||||
|
||||
trigger.action.outText("bank v" .. bank.version .. " started.", 30)
|
||||
return true
|
||||
end
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
bombRange = {}
|
||||
bombRange.version = "1.1.1"
|
||||
bombRange.version = "1.1.2"
|
||||
bombRange.dh = 1 -- meters above ground level burst
|
||||
|
||||
bombRange.requiredLibs = {
|
||||
@ -20,6 +20,8 @@ VERSION HISTORY
|
||||
also sampling kill events
|
||||
1.1.1 - fixed reading smoke color for zone
|
||||
minor clean-up
|
||||
1.1.2 - corrected bug when no bomb range is detected
|
||||
|
||||
--]]--
|
||||
bombRange.bombs = {} -- live tracking
|
||||
bombRange.collector = {} -- post-impact collections for 0.5 secs
|
||||
@ -492,7 +494,7 @@ function bombRange.impacted(weapon, target, finalPass)
|
||||
|
||||
-- see if inside a range
|
||||
if #bombRange.ranges < 1 then
|
||||
trigger.action.outText("+++bRng: No Bomb Ranges detected!")
|
||||
trigger.action.outText("+++bRng: No Bomb Ranges detected!", 30)
|
||||
return -- no need to update anything
|
||||
end
|
||||
local minDist = math.huge
|
||||
|
||||
131
modules/camp.lua
131
modules/camp.lua
@ -1,15 +1,27 @@
|
||||
camp = {}
|
||||
camp.ups = 1
|
||||
camp.version = "0.0.0"
|
||||
camp.version = "1.0.2"
|
||||
camp.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxZones", -- Zones, of course
|
||||
"cfxMX",
|
||||
"bank"
|
||||
}
|
||||
|
||||
-- AUTOMATICALLY INTEGRATES WITH income MODULE IF PRESENT
|
||||
-- REQUIRES CLONEZONES TO RUN (BUT NOT TO START)
|
||||
--[[--
|
||||
VERSION HISTORY
|
||||
1.0.0 - initial version
|
||||
1.0.1 - changed "Ground Repairs / Upgrades" to "Funds / Repairs / Upgrades"
|
||||
- provided income info for camp if it exists
|
||||
- provide income total if exists
|
||||
- actionSound
|
||||
- output sound with communications
|
||||
1.0.2 - integration with FARPZones
|
||||
--]]--
|
||||
--
|
||||
-- CURRENTLY REQUIRES SINGLE-UNIT PLAYER GROUPS
|
||||
-- REQUIRES CLONEZONES MODULE TO BE RUNNING, BUT NOT TO BE LOADED ON START
|
||||
--
|
||||
camp.camps = {} -- all camps on the map
|
||||
camp.roots = {} -- all player group comms roots
|
||||
@ -18,16 +30,27 @@ function camp.addCamp(theZone)
|
||||
camp.camps[theZone.name] = theZone
|
||||
end
|
||||
|
||||
function camp.getMyCurrentCamp(theUnit) -- returns first hit plaayer is in
|
||||
function camp.getMyCurrentCamp(theUnit) -- returns first hit player is in
|
||||
local coa = theUnit:getCoalition()
|
||||
local p = theUnit:getPoint()
|
||||
for idx, theCamp in pairs(camp.camps) do
|
||||
if theCamp:pointInZone(p) then
|
||||
if theCamp.owner == coa and theCamp:pointInZone(p) then
|
||||
return theCamp
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function camp.getCampsForCoa(coa)
|
||||
local myCamps = {}
|
||||
for idx, theCamp in pairs(camp.camps) do
|
||||
if theCamp.owner == coa then
|
||||
table.insert(myCamps, theCamp)
|
||||
end
|
||||
end
|
||||
return myCamps
|
||||
end
|
||||
|
||||
function camp.createCampWithZone(theZone)
|
||||
-- look for all cloners inside my zone
|
||||
if theZone.verbose or camp.verbose then
|
||||
@ -70,6 +93,12 @@ function camp.createCampWithZone(theZone)
|
||||
theZone.upgradable = theZone:getBoolFromZoneProperty("upgrade", true)
|
||||
theZone.repairCost = theZone:getNumberFromZoneProperty("repairCost", 100)
|
||||
theZone.upgradeCost = theZone:getNumberFromZoneProperty("upgradeCost", 3 * theZone.repairCost)
|
||||
if theZone:hasProperty("FARP") then
|
||||
theZone.isAlsoFARP = true
|
||||
if theZone.verbose or camp.verbose then
|
||||
trigger.action.outText("+++camp: <" .. theZone.name .. "> has FARP attached", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
@ -93,7 +122,7 @@ function camp.processPlayers()
|
||||
for idx, gData in pairs(cfxMX.playerGroupByName) do
|
||||
gID = gData.groupId
|
||||
gName = gData.name
|
||||
local theRoot = missionCommands.addSubMenuForGroup(gID, "Ground Repairs / Upgrades")
|
||||
local theRoot = missionCommands.addSubMenuForGroup(gID, "Funds / Repairs / Upgrades")
|
||||
camp.roots[gName] = theRoot
|
||||
local c00 = missionCommands.addCommandForGroup(gID, "Theatre Overview", theRoot, camp.redirectTFunds, {gName, gID, "tfunds"})
|
||||
local c0 = missionCommands.addCommandForGroup(gID, "Local Funds & Status Overview", theRoot, camp.redirectFunds, {gName, gID, "funds"})
|
||||
@ -126,11 +155,15 @@ function camp.doTFunds(args)
|
||||
local hasBalance, amount = bank.getBalance(coa)
|
||||
if not hasBalance then return end
|
||||
local msg = "\nYour faction currently has §" .. amount .. " available for repairs/upgrades.\n"
|
||||
|
||||
local income = 0
|
||||
-- now iterate all camps that are on my side
|
||||
for idx, theZone in pairs(camp.camps) do
|
||||
if theZone.owner == coa then
|
||||
msg = msg .. "\n - <" .. theZone.name .. ">"
|
||||
if theZone.income then
|
||||
msg = msg .. " Income: §" .. theZone.income
|
||||
income = income + theZone.income
|
||||
end
|
||||
|
||||
if theZone.repairable and theZone.upgradable then
|
||||
msg = msg .. " (§" .. theZone.repairCost .. "/§" .. theZone.upgradeCost .. ")"
|
||||
@ -165,8 +198,12 @@ function camp.doTFunds(args)
|
||||
end
|
||||
end
|
||||
end
|
||||
if income > 0 then
|
||||
msg = msg .. "\n\nTotal Income: §" .. income
|
||||
end
|
||||
msg = msg .. "\n"
|
||||
trigger.action.outTextForGroup(gID, msg, 30)
|
||||
trigger.action.outSoundForGroup(gID, camp.actionSound)
|
||||
end
|
||||
|
||||
function camp.doFunds(args)
|
||||
@ -183,11 +220,13 @@ function camp.doFunds(args)
|
||||
if not Unit.isExist(theUnit) or theUnit:getLife() < 1 or
|
||||
theUnit:inAir() or dcsCommon.getUnitSpeed(theUnit) > 1 then
|
||||
trigger.action.outTextForGroup(gID, msg, 30)
|
||||
trigger.action.outSoundForGroup(gID, camp.actionSound)
|
||||
return
|
||||
end
|
||||
local theZone = camp.getMyCurrentCamp(theUnit)
|
||||
if not theZone or (not theZone.repairable) or theZone.owner ~= theUnit:getCoalition() then
|
||||
trigger.action.outTextForGroup(gID, msg, 30)
|
||||
trigger.action.outSoundForGroup(gID, camp.actionSound)
|
||||
return
|
||||
end
|
||||
|
||||
@ -204,17 +243,30 @@ function camp.doFunds(args)
|
||||
msg = msg .. "\nZone <" .. theZone.name .. "> is fully upgraded.\n"
|
||||
end
|
||||
trigger.action.outTextForGroup(gID, msg, 30)
|
||||
trigger.action.outSoundForGroup(gID, camp.actionSound)
|
||||
end
|
||||
|
||||
--
|
||||
-- REPAIRS
|
||||
--
|
||||
|
||||
function camp.zoneNeedsRepairs(theZone, coa)
|
||||
-- return true if this zone needs repairs, i.e. it has cloners that have a damaged clone set
|
||||
-- return true if this zone needs repairs, i.e. it has cloners that have a damaged clone set or FARP resource vehicles are incomplete
|
||||
if theZone.isAlsoFARP and FARPZones then
|
||||
local theFarp = FARPZones.getFARPForZone(theZone)
|
||||
if FARPZones.serviceNeedsRepair(theFarp) then
|
||||
if theZone.verbose or camp.verbose then
|
||||
trigger.action.outText("camp: <" .. theZone.name .. "> has FARP service is dinged up...", 30)
|
||||
end
|
||||
return true
|
||||
-- WARNING: RETURNS BOOLEAN, not a dmlZone!
|
||||
end
|
||||
end
|
||||
|
||||
local myCloners = theZone.cloners
|
||||
|
||||
if not coa then
|
||||
trigger.action.outText("+++camp: warning: no coa on zoneNeedsRepair for zone <" .. theZone.name .. ">", 30)
|
||||
trigger.action.outText("+++camp: warning: no coa on zoneNeedsRepairs for zone <" .. theZone.name .. ">", 30)
|
||||
elseif coa == 1 then
|
||||
myCloners = theZone.redCloners
|
||||
elseif coa == 2 then
|
||||
@ -251,19 +303,23 @@ function camp.doRepairs(args)
|
||||
if theUnit:getLife() < 1 then return end
|
||||
if theUnit:inAir() then
|
||||
trigger.action.outTextForGroup(gID, "\nPlease land inside a fortified zone to order repairs\n", 30)
|
||||
trigger.action.outSoundForGroup(gID, camp.actionSound)
|
||||
return
|
||||
end
|
||||
if dcsCommon.getUnitSpeed(theUnit) > 1 then
|
||||
trigger.action.outTextForGroup(gID, "\nYou must come to a complete stop before being able to order repairs\n", 30)
|
||||
trigger.action.outSoundForGroup(gID, camp.actionSound)
|
||||
return
|
||||
end
|
||||
local theZone = camp.getMyCurrentCamp(theUnit)
|
||||
if not theZone or not theZone.repairable then
|
||||
trigger.action.outTextForGroup(gID, "\nYou are not inside a zone that can be repaired.\n", 30)
|
||||
trigger.action.outSoundForGroup(gID, camp.actionSound)
|
||||
return
|
||||
end
|
||||
if theZone.owner ~= theUnit:getCoalition() then
|
||||
trigger.action.outTextForGroup(gID, "\nYou currently do not own zone <" .. theZone.name .. ">. Capture it first.\n", 30)
|
||||
trigger.action.outSoundForGroup(gID, camp.actionSound)
|
||||
return
|
||||
end
|
||||
|
||||
@ -274,6 +330,7 @@ function camp.doRepairs(args)
|
||||
msg = msg .. "\nZone <" .. theZone.name .. "> can be upgraded.\n"
|
||||
end
|
||||
trigger.action.outTextForGroup(gID, msg, 30)
|
||||
trigger.action.outSoundForGroup(gID, camp.actionSound)
|
||||
return
|
||||
end
|
||||
|
||||
@ -285,7 +342,9 @@ function camp.doRepairs(args)
|
||||
end
|
||||
|
||||
if amount < theZone.repairCost then
|
||||
trigger.action.outTextForGroup(gID, "\nYou curently cannot afford repairs here\n", 30)
|
||||
-- trigger.action.outTextForGroup(gID, "\nYou curently cannot afford repairs here\n", 30)
|
||||
trigger.action.outTextForGroup(gID, "\nYou curently cannot afford repairs here (§" .. theZone.repairCost .. " required, you have §" .. amount .. ")\n", 30)
|
||||
trigger.action.outSoundForGroup(gID, camp.actionSound)
|
||||
return
|
||||
end
|
||||
|
||||
@ -296,13 +355,23 @@ function camp.doRepairs(args)
|
||||
-- cloneZones.spawnWithCloner(theCloner)
|
||||
bank.withdawFunds(coa, theZone.repairCost)
|
||||
local ignore, remain = bank.getBalance(coa)
|
||||
trigger.action.outTextForCoalition(coa, "\nZone <" .. theZone.name .. "> was repaired by <" .. pName ..
|
||||
trigger.action.outTextForCoalition(coa, "\nZone <" .. theZone.name .. "> was ordered repaired by <" .. pName ..
|
||||
"> for §" .. theZone.repairCost .. ".\nFaction has §" .. remain .. " remaining funds.\n", 30)
|
||||
trigger.action.outSoundForCoalition(coa, camp.actionSound)
|
||||
end
|
||||
|
||||
function camp.repairZone(theZone, coa)
|
||||
theCloner = camp.zoneNeedsRepairs(theZone, coa)
|
||||
if not theCloner then return end
|
||||
if type(theCloner) == "boolean" then -- at least farp was dinged up
|
||||
local theFarp = FARPZones.getFARPForZone(theZone)
|
||||
FARPZones.produceResourceVehicles(theFarp, coa)
|
||||
if theZone.verbose or camp.verbose then
|
||||
trigger.action.outText("+++camp: repaired FARP in camp <" .. theZone.name .. ">", 30)
|
||||
end
|
||||
end
|
||||
theCloner = camp.zoneNeedsRepairs(theZone, coa) -- do again to see if other repairs are needed. FARP repairs come free with first fix
|
||||
if not theCloner then return end
|
||||
cloneZones.despawnAll(theCloner)
|
||||
cloneZones.spawnWithCloner(theCloner)
|
||||
end
|
||||
@ -311,7 +380,7 @@ end
|
||||
--
|
||||
|
||||
function camp.zoneNeedsUpgrades(theZone, coa)
|
||||
-- return true if this zone can be upgraded, i.e. it has cloners that have an empty clone set
|
||||
-- returns first cloner in this zone that can be upgraded, i.e. it has cloners that have an empty clone set
|
||||
if not theZone.upgradable then return nil end
|
||||
|
||||
local myCloners = theZone.cloners
|
||||
@ -352,30 +421,36 @@ function camp.doUpgrades(args)
|
||||
if not pName then pName = "<Big Err>" end
|
||||
if theUnit:inAir() then
|
||||
trigger.action.outTextForGroup(gID, "\nPlease land inside a fortified zone to order upgrades.\n", 30)
|
||||
trigger.action.outSoundForGroup(gID, camp.actionSound)
|
||||
return
|
||||
end
|
||||
if dcsCommon.getUnitSpeed(theUnit) > 1 then
|
||||
trigger.action.outTextForGroup(gID, "\nYou must come to a complete stop before being able to order upgrades\n", 30)
|
||||
trigger.action.outSoundForGroup(gID, camp.actionSound)
|
||||
return
|
||||
end
|
||||
local theZone = camp.getMyCurrentCamp(theUnit)
|
||||
if not theZone or not theZone.upgradable then
|
||||
trigger.action.outTextForGroup(gID, "\nYou are not inside a zone that can be upgraded.\n", 30)
|
||||
trigger.action.outSoundForGroup(gID, camp.actionSound)
|
||||
return
|
||||
end
|
||||
if theZone.owner ~= theUnit:getCoalition() then
|
||||
trigger.action.outTextForGroup(gID, "\nYou currently do not own zone <" .. theZone.name .. ">. Capture it first.\n", 30)
|
||||
trigger.action.outSoundForGroup(gID, camp.actionSound)
|
||||
return
|
||||
end
|
||||
|
||||
if camp.zoneNeedsRepairs(theZone, coa) then
|
||||
trigger.action.outTextForGroup(gID, "\nZone <" .. theZone.name .. "> requires repairs before it can be upgraded.\n", 30)
|
||||
trigger.action.outSoundForGroup(gID, camp.actionSound)
|
||||
return
|
||||
end
|
||||
|
||||
-- if we get here, we are inside a zone that can be upgraded. see if it needs upgrades and then get upgrade cost and see if we have enough fund to do it
|
||||
if not camp.zoneNeedsUpgrades(theZone, coa) then
|
||||
trigger.action.outTextForGroup(gID, "\nZone <" .. theZone.name .. "> has been fully upgraded.\n", 30)
|
||||
trigger.action.outSoundForGroup(gID, camp.actionSound)
|
||||
return
|
||||
end
|
||||
|
||||
@ -387,7 +462,8 @@ function camp.doUpgrades(args)
|
||||
end
|
||||
|
||||
if amount < theZone.upgradeCost then
|
||||
trigger.action.outTextForGroup(gID, "\nYou curently cannot afford an upgrade here\n", 30)
|
||||
trigger.action.outTextForGroup(gID, "\nYou curently cannot afford an upgrade here (§" .. theZone.upgradeCost .. " required, you have §" .. amount .. ")\n", 30)
|
||||
trigger.action.outSoundForGroup(gID, camp.actionSound)
|
||||
return
|
||||
end
|
||||
|
||||
@ -398,8 +474,9 @@ function camp.doUpgrades(args)
|
||||
-- bill it to side
|
||||
bank.withdawFunds(coa, theZone.upgradeCost)
|
||||
local ignore, remain = bank.getBalance(coa)
|
||||
trigger.action.outTextForCoalition(coa, "\nZone <" .. theZone.name .. "> was upgraded by <" .. pName ..
|
||||
trigger.action.outTextForCoalition(coa, "\nZone <" .. theZone.name .. "> was ordered upgraded by <" .. pName ..
|
||||
"> for §" .. theZone.upgradeCost .. ".\nFaction has §" .. remain .. " remaining funds.\n", 30)
|
||||
trigger.action.outSoundForCoalition(coa, camp.actionSound)
|
||||
end
|
||||
|
||||
-- can be called externally
|
||||
@ -408,6 +485,32 @@ function camp.upgradeZone(theZone, coa)
|
||||
if not theCloner then return end
|
||||
cloneZones.spawnWithCloner(theCloner)
|
||||
end
|
||||
|
||||
--
|
||||
-- API
|
||||
--
|
||||
function camp.campsThatNeedRepairs(coa) -- returns the zones that need repairs
|
||||
local repairs = {}
|
||||
for idx, theZone in pairs(camp.camps) do
|
||||
if theZone.repairable and theZone.owner == coa and camp.zoneNeedsRepairs(theZone, coa) then
|
||||
table.insert(repairs, theZone)
|
||||
end
|
||||
end
|
||||
|
||||
return repairs
|
||||
end
|
||||
|
||||
function camp.campsThatNeedUpgrades(coa) -- returns the zones that can be upgraded
|
||||
local repairs = {}
|
||||
for idx, theZone in pairs(camp.camps) do
|
||||
if theZone.upgradable and theZone.owner == coa and camp.zoneNeedsUpgrades(theZone, coa) then
|
||||
table.insert(repairs, theZone)
|
||||
end
|
||||
end
|
||||
|
||||
return repairs
|
||||
end
|
||||
|
||||
--
|
||||
-- Config & Go
|
||||
--
|
||||
@ -417,7 +520,7 @@ function camp.readConfigZone()
|
||||
if not theZone then
|
||||
theZone = cfxZones.createSimpleZone("campConfig")
|
||||
end
|
||||
|
||||
camp.actionSound = theZone:getStringFromZoneProperty("actionSound", "Quest Snare 3.wav")
|
||||
camp.verbose = theZone.verbose
|
||||
end
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxZones = {}
|
||||
cfxZones.version = "4.3.1"
|
||||
cfxZones.version = "4.3.2"
|
||||
|
||||
-- cf/x zone management module
|
||||
-- reads dcs zones and makes them accessible and mutable
|
||||
@ -50,6 +50,8 @@ cfxZones.version = "4.3.1"
|
||||
- randomDelayFromPositiveRange also allows 0
|
||||
- 4.3.1 - new drawText() for zones
|
||||
- dmlZones:getClosestZone() bridge
|
||||
- 4.3.2 - new getListFromZoneProperty()
|
||||
|
||||
--]]--
|
||||
|
||||
--
|
||||
@ -2097,7 +2099,7 @@ end
|
||||
function cfxZones.drawZone(theZone, lineColor, fillColor, markID)
|
||||
if not theZone then return 0 end
|
||||
if not lineColor then lineColor = {0.8, 0.8, 0.8, 1.0} end
|
||||
if not fillColor then fillColor = {0.8, 0.8, 0.8, 0.2} end
|
||||
if not fillColor then fillColor = {0.8, 0.8, 0.8, 0.0} end
|
||||
if not markID then markID = dcsCommon.numberUUID() end
|
||||
|
||||
if theZone.isCircle then
|
||||
@ -2118,7 +2120,7 @@ function cfxZones.drawText(theZone, theText, fSize, lineColor, fillColor)
|
||||
if not theZone then return end
|
||||
if not fSize then fSize = 12 end
|
||||
if not lineColor then lineColor = {0.8, 0.8, 0.8, 1.0} end
|
||||
if not fillColor then fillColor = lineColor end
|
||||
if not fillColor then fillColor = {0, 0, 0, 0} end
|
||||
local markID = dcsCommon.numberUUID()
|
||||
local p = theZone:getPoint()
|
||||
local offset = {x = p.x, y = 0, z = p.z}
|
||||
@ -2322,6 +2324,24 @@ function dmlZone:getPositiveRangeFromZoneProperty(theProperty, default, defaultm
|
||||
return lo, up
|
||||
end
|
||||
|
||||
function cfxZones.getListFromZoneProperty(theZone, theProperty, defaultItem) -- comma delimited
|
||||
if not defaultItem then defaultItem = "default" end
|
||||
|
||||
local theString = theZone:getStringFromZoneProperty(theProperty, defaultItem)
|
||||
if dcsCommon.containsString(theString, ",") then
|
||||
local theArray = dcsCommon.splitString(theString, ',')
|
||||
theArray = dcsCommon.trimArray(theArray)
|
||||
return theArray
|
||||
else
|
||||
return {theString}
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
function dmlZone:getListFromZoneProperty(theProperty, defaultItem)
|
||||
return cfxZones.getListFromZoneProperty(self, theProperty, defaultItem)
|
||||
end
|
||||
|
||||
function cfxZones.hasProperty(theZone, theProperty)
|
||||
if not theProperty then
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cloneZones = {}
|
||||
cloneZones.version = "2.2.0"
|
||||
cloneZones.version = "2.2.1"
|
||||
cloneZones.verbose = false
|
||||
cloneZones.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
@ -50,6 +50,8 @@ cloneZones.respawnOnGroupID = true
|
||||
- damaged! output
|
||||
- health# output
|
||||
- persistence: persist oSize and set lastSize
|
||||
2.2.1 - verbosity updates for post-check
|
||||
- if cloned group is late activation, turn it off
|
||||
--]]--
|
||||
|
||||
--
|
||||
@ -1054,7 +1056,12 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
|
||||
local dataToSpawn = {} -- temp save so we can connect in-group references
|
||||
|
||||
for idx, aGroupName in pairs(theZone.cloneNames) do
|
||||
local rawData, cat, ctry = cfxMX.getGroupFromDCSbyName(aGroupName)
|
||||
local rawData, cat, ctry = cfxMX.getGroupFromDCSbyName(aGroupName) -- fetches a clone!
|
||||
-- sanity checks: lateActivation etc
|
||||
if rawData.lateActivation then
|
||||
trigger.action.outText("+++clnZ: WARNING - clone group <" .. rawData.name .. "> in cloner <" .. theZone.name .. "> is set to 'late activation'. Ignored.", 30)
|
||||
rawData.lateActivation = false
|
||||
end
|
||||
rawData.CZorigName = rawData.name -- save original group name
|
||||
local origID = rawData.groupId -- save original group ID
|
||||
rawData.CZorigID = origID
|
||||
@ -1287,9 +1294,11 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
|
||||
end
|
||||
cloneZones.unitXlate[aUnit.CZorigID] = uID
|
||||
else
|
||||
if spawnZone.verbose then
|
||||
trigger.action.outText("clnZ: post-clone verifiaction failed for unit <" .. uName .. ">: not found", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- check if our assigned ID matches the one handed out by
|
||||
-- DCS. Mismatches can happen, and are only noted
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
csarManager = {}
|
||||
csarManager.version = "3.2.7"
|
||||
csarManager.version = "3.4.0"
|
||||
csarManager.ups = 1
|
||||
|
||||
--[[-- VERSION HISTORY
|
||||
@ -42,12 +42,15 @@ csarManager.ups = 1
|
||||
- useRanks option
|
||||
3.2.6 - inBuiltup analogon to cloner
|
||||
3.2.7 - createCSARForParachutist now supports optional coa (autoCSAR)
|
||||
|
||||
3.3.0 - persistence support
|
||||
3.4.0 - global timeLimit option in config zone
|
||||
- fixes expiration bug when persisting data
|
||||
|
||||
|
||||
INTEGRATES AUTOMATICALLY WITH playerScore
|
||||
INTEGRATES WITH LIMITED AIRFRAMES
|
||||
INTEGRATES AUTOMATICALLY WITH SCRIBE
|
||||
SUPPORTS PERSISTENCE
|
||||
|
||||
--]]--
|
||||
-- modules that need to be loaded BEFORE I run
|
||||
@ -201,10 +204,22 @@ function csarManager.createCSARMissionData(point, theSide, freq, name, numCrew,
|
||||
|
||||
newMission.timeStamp = timer.getTime() -- now
|
||||
|
||||
-- if no time limit given but csarManager's own global dictates it,
|
||||
-- set it now
|
||||
if not timeLimit and csarManager.timeLimit then
|
||||
if csarManager.verbose then
|
||||
trigger.action.outText("+++csar: setting GLOBAL csar time limit (" .. csarManager.timeLimit[1] .. "," .. csarManager.timeLimit[2] .. ") for new mission " .. name, 30)
|
||||
end
|
||||
timeLimit = csarManager.timeLimit
|
||||
end
|
||||
|
||||
-- set timeLimit if enabled
|
||||
if timeLimit then
|
||||
local theLimit = cfxZones.randomDelayFromPositiveRange(timeLimit[1], timeLimit[2]) * 60
|
||||
newMission.expires = timer.getTime() + theLimit
|
||||
if csarManager.verbose then
|
||||
trigger.action.outText("+++csar: setting time limit to expire in (" .. math.floor(theLimit/60) .. ") minutes for mission " .. name, 30)
|
||||
end
|
||||
end
|
||||
|
||||
-- update counter and return
|
||||
@ -794,8 +809,8 @@ function csarManager.doListCSARRequests(args)
|
||||
local status = "alive"
|
||||
if mission.expires then
|
||||
delta = math.floor ((mission.expires - now) / 60)
|
||||
if delta < 10 then status = "+deteriorating+" end
|
||||
if delta < 5 then status = "*critical*" end
|
||||
if delta < 30 then status = "+deteriorating+" end
|
||||
if delta < 15 then status = "*critical*" end
|
||||
if csarManager.verbose then
|
||||
status = status .. " [" .. delta .. "]" -- remove me
|
||||
end
|
||||
@ -841,9 +856,9 @@ function csarManager.doStatusCarrying(args)
|
||||
report = report .. "\n".. i .. ") " .. evacMission.name
|
||||
if evacMission.expires then
|
||||
delta = math.floor ((evacMission.expires - now) / 60)
|
||||
if delta > 20 then
|
||||
report = report .. " is hurt but stable"
|
||||
elseif delta > 10 then
|
||||
if delta > 30 then
|
||||
report = report .. " is hurt and stable"
|
||||
elseif delta > 15 then
|
||||
report = report .. " is badly hurt"
|
||||
else
|
||||
report = report .. " is in critical condition" -- or 'beat up, but will live'
|
||||
@ -1438,8 +1453,12 @@ function csarManager.readCSARZone(theZone)
|
||||
|
||||
-- add to list of startable csar
|
||||
if theZone.startCSAR then
|
||||
if persistence and persistence.hasDate then
|
||||
-- we load data instead of spawning on start
|
||||
else
|
||||
csarManager.addCSARZone(theZone)
|
||||
end
|
||||
end
|
||||
|
||||
if (not deferred) then
|
||||
local theMission = csarManager.createCSARMissionFromZone(theZone)
|
||||
@ -1593,11 +1612,87 @@ function csarManager.readConfigZone()
|
||||
typeArray = dcsCommon.trimArray(typeArray)
|
||||
csarManager.rescueTypes = typeArray
|
||||
|
||||
if theZone:hasProperty("timeLimit") then
|
||||
local tmin, tmax = theZone:getPositiveRangeFromZoneProperty("timeLimit", 1)
|
||||
csarManager.timeLimit = {tmin, tmax}
|
||||
else
|
||||
csarManager.timeLimit = nil
|
||||
end
|
||||
|
||||
if csarManager.verbose then
|
||||
trigger.action.outText("+++csar: read config", 30)
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Save and Load Data
|
||||
--
|
||||
function csarManager.saveData()
|
||||
local now = timer.getTime()
|
||||
local theData = {}
|
||||
local missions = {}
|
||||
-- gather dater from all currently open missions and
|
||||
-- place them in a new array
|
||||
for idx, aMission in pairs(csarManager.openMissions) do
|
||||
m = {}
|
||||
m.point = aMission.zone:getPoint()
|
||||
m.side = aMission.side
|
||||
m.freq = aMission.freq
|
||||
m.name = aMission.name
|
||||
m.score = aMission.score
|
||||
if aMission.expires then
|
||||
remains = (aMission.expires - now) / 60 -- limit in minutes!
|
||||
m.timeLimit = {remains, remains}
|
||||
end
|
||||
table.insert(missions, m)
|
||||
end
|
||||
theData.missions = missions
|
||||
theData.missionID = csarManager.missionID
|
||||
|
||||
return theData, csarManager.sharedData
|
||||
end
|
||||
|
||||
function csarManager.loadData()
|
||||
if not persistence then return end
|
||||
local theData = persistence.getSavedDataForModule("csarManager", csarManager.sharedData)
|
||||
if not theData then
|
||||
if csarManager.verbose then
|
||||
trigger.action.outText("+++csarManager: no save data received, skipping.", 30)
|
||||
end
|
||||
return
|
||||
end
|
||||
if theData.missionID then
|
||||
csarManager.missionID = theData.missionID
|
||||
end
|
||||
if theData.missions then
|
||||
for idx, m in pairs(theData.missions) do
|
||||
-- csarManager.createCSARMissionData(point, theSide, freq, name, numCrew, timeLimit, mapMarker, inRadius, parashootUnit)
|
||||
if m.timeLimit and csarManager.verbose then
|
||||
trigger.action.outText("+++csar: loadData - timelimit of [" .. m.timeLimit[1] .. "," .. m.timeLimit[2] .. "] read for csar <" .. m.name .. ">", 30)
|
||||
end
|
||||
local theMission = csarManager.createCSARMissionData(
|
||||
m.point, -- point,
|
||||
m.side, -- theSide,
|
||||
m.freq, -- freq,
|
||||
m.name, -- name,
|
||||
nil, -- numCrew,
|
||||
m.timeLimit, -- timeLimit, can be nil or {lower, upper}
|
||||
nil, --mapMarker,
|
||||
0.1, -- inRadius,
|
||||
nil -- parashootUnit)
|
||||
)
|
||||
theMission.score = m.score
|
||||
csarManager.addMission(theMission)
|
||||
if csarManager.verbose then
|
||||
trigger.action.outText("+++csarM (persitence): restored csar mission <" .. m.name .. ">", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Start
|
||||
--
|
||||
|
||||
function csarManager.start()
|
||||
-- make sure we have loaded all relevant libraries
|
||||
@ -1624,6 +1719,16 @@ function csarManager.start()
|
||||
csarManager.setCommsMenu(aUnit)
|
||||
end
|
||||
|
||||
-- connect to persistence if it exists
|
||||
if persistence then
|
||||
-- sign up for persistence
|
||||
callbacks = {}
|
||||
callbacks.persistData = csarManager.saveData
|
||||
persistence.registerModule("csarManager", callbacks)
|
||||
-- now load my data
|
||||
csarManager.loadData()
|
||||
end
|
||||
|
||||
-- start updating and track all helicopters in the air against missions
|
||||
csarManager.update()
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
dcsCommon = {}
|
||||
dcsCommon.version = "3.0.5"
|
||||
dcsCommon.version = "3.0.6"
|
||||
--[[-- VERSION HISTORY
|
||||
3.0.0 - removed bad bug in stringStartsWith, only relevant if caseSensitive is false
|
||||
- point2text new intsOnly option
|
||||
@ -18,6 +18,9 @@ dcsCommon.version = "3.0.5"
|
||||
- new getFirstItem()
|
||||
- arrayContainsString() can handle dicts
|
||||
- new pointXpercentYdegOffAB()
|
||||
3.0.6 - new arrayContainsStringCaseInsensitive()
|
||||
3.0.7 - fixed small bug in wildArrayContainsString
|
||||
|
||||
--]]--
|
||||
|
||||
-- dcsCommon is a library of common lua functions
|
||||
@ -2093,7 +2096,7 @@ end
|
||||
if not caseSensitive then theString = string.upper(theString) end
|
||||
|
||||
local wildIn = dcsCommon.stringEndsWith(theString, "*")
|
||||
if wildIn then dcsCommon.removeEnding(theString, "*") end
|
||||
if wildIn then theString = dcsCommon.removeEnding(theString, "*") end
|
||||
for idx, theElement in pairs(theArray) do -- i = 1, #theArray do
|
||||
if not caseSensitive then theElement = string.upper(theElement) end
|
||||
local wildEle = dcsCommon.stringEndsWith(theElement, "*")
|
||||
@ -2133,6 +2136,19 @@ end
|
||||
return false
|
||||
end
|
||||
|
||||
function dcsCommon.arrayContainsStringCaseInsensitive(theArray, theString) -- case insensitive
|
||||
if not theArray then return false end
|
||||
if not theString then return false end
|
||||
if type(theArray) ~= "table" then
|
||||
trigger.action.outText("***arrayContainsStringCI: theArray is not type <table> but <" .. type(theArray) .. ">", 30)
|
||||
end
|
||||
theString = string.upper(theString)
|
||||
for idx, item in pairs(theArray) do
|
||||
if string.upper(item) == theString then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function dcsCommon.splitString(inputstr, sep)
|
||||
if sep == nil then
|
||||
sep = "%s"
|
||||
|
||||
@ -35,25 +35,40 @@ function income.update()
|
||||
-- schedule next round
|
||||
timer.scheduleFunction(income.update, {}, timer.getTime() + income.interval)
|
||||
|
||||
local neuI, redI, blueI = income.neutral, income.red, income.blue
|
||||
-- base income
|
||||
bank.addFunds(0, income.neutral)
|
||||
bank.addFunds(1, income.red)
|
||||
bank.addFunds(2, income.blue)
|
||||
-- bank.addFunds(0, income.neutral)
|
||||
-- bank.addFunds(1, income.red)
|
||||
-- bank.addFunds(2, income.blue)
|
||||
|
||||
|
||||
for idx, theZone in pairs(income.sources) do
|
||||
bank.addFunds(0, income.getIncomeForZoneAndCoa(theZone, 0))
|
||||
bank.addFunds(1, income.getIncomeForZoneAndCoa(theZone, 1))
|
||||
bank.addFunds(2, income.getIncomeForZoneAndCoa(theZone, 2))
|
||||
local ni = income.getIncomeForZoneAndCoa(theZone, 0)
|
||||
local ri = income.getIncomeForZoneAndCoa(theZone, 1)
|
||||
local bi = income.getIncomeForZoneAndCoa(theZone, 2)
|
||||
redI = redI + ri
|
||||
blueI = blueI + bi
|
||||
neuI = neuI + ni
|
||||
end
|
||||
|
||||
bank.addFunds(0, neuI)
|
||||
bank.addFunds(1, redI)
|
||||
bank.addFunds(2, blueI)
|
||||
|
||||
|
||||
if income.announceTicks then
|
||||
-- trigger.action.outText(income.tickMessage, 30)
|
||||
local has, balance = bank.getBalance(0)
|
||||
trigger.action.outTextForCoalition(0, "\n" .. income.tickMessage .. "\nNew balance: §" .. balance .. "\n", 30)
|
||||
local tick = string.gsub(income.tickMessage, "<i>", neuI)
|
||||
trigger.action.outTextForCoalition(0, "\n" .. tick .. "\nNew balance: §" .. balance .. "\n", 30)
|
||||
|
||||
has, balance = bank.getBalance(1)
|
||||
trigger.action.outTextForCoalition(1, "\n" .. income.tickMessage .. "\nNew balance: §" .. balance .. "\n", 30)
|
||||
tick = string.gsub(income.tickMessage, "<i>", redI)
|
||||
trigger.action.outTextForCoalition(1, "\n" .. tick .. "\nNew balance: §" .. balance .. "\n", 30)
|
||||
|
||||
has, balance = bank.getBalance(2)
|
||||
trigger.action.outTextForCoalition(2, "\n" .. income.tickMessage .. "\nNew balance: §" .. balance .. "\n", 30)
|
||||
tick = string.gsub(income.tickMessage, "<i>", blueI)
|
||||
trigger.action.outTextForCoalition(2, "\n" .. tick .. "\nNew balance: §" .. balance .. "\n", 30)
|
||||
end
|
||||
|
||||
end
|
||||
@ -71,7 +86,7 @@ function income.readConfigZone()
|
||||
income.neutral = theZone:getNumberFromZoneProperty ("neutral", income.base)
|
||||
|
||||
income.interval = theZone:getNumberFromZoneProperty("interval", 10 * 60) -- every 10 minutes
|
||||
income.tickMessage = theZone:getStringFromZoneProperty("tickMessage", "New funds from income available.")
|
||||
income.tickMessage = theZone:getStringFromZoneProperty("tickMessage", "New funds from income available: §<i>")
|
||||
income.announceTicks = theZone:getBoolFromZoneProperty("announceTicks", true)
|
||||
income.verbose = theZone.verbose
|
||||
end
|
||||
|
||||
@ -6,8 +6,6 @@ jtacGrpUI.requiredLibs = {
|
||||
"cfxGroundTroops",
|
||||
}
|
||||
--[[-- VERSION HISTORY
|
||||
- 1.0.2 - also include idling JTACS
|
||||
- add positional info when using owned zones
|
||||
- 2.0.0 - dmlZones
|
||||
- sanity checks upon load
|
||||
- eliminated cfxPlayer dependence
|
||||
@ -21,14 +19,6 @@ jtacGrpUI.requiredLibs = {
|
||||
jtacGrpUI.groupConfig = {} -- all inited group private config data, indexed by group name.
|
||||
jtacGrpUI.simpleCommands = true -- if true, f10 other invokes directly
|
||||
|
||||
--
|
||||
-- C O N F I G H A N D L I N G
|
||||
-- =============================
|
||||
--
|
||||
-- Each group has their own config block that can be used to
|
||||
-- store group-private data and configuration items.
|
||||
--
|
||||
|
||||
function jtacGrpUI.resetConfig(conf)
|
||||
end
|
||||
|
||||
|
||||
126
modules/milGround.lua
Normal file
126
modules/milGround.lua
Normal file
@ -0,0 +1,126 @@
|
||||
milGround = {}
|
||||
milGround.version = "0.0.0"
|
||||
milGround.requiredLibs = {
|
||||
"dcsCommon",
|
||||
"cfxZones",
|
||||
"cfxMX",
|
||||
"cloneZones",
|
||||
}
|
||||
milGround.ups = 0.5 -- every 2 seconds is enough
|
||||
milGround.zones = {}
|
||||
|
||||
function milGround.addMilGroundZone(theZone)
|
||||
milGround.zones[theZone.name] = theZone
|
||||
end
|
||||
|
||||
--
|
||||
-- Reading zones
|
||||
--
|
||||
function milGround.readMilGroundZone(theZone)
|
||||
-- first, check if this zone is also a cloner
|
||||
if not theZone:hasProperty("cloner") then
|
||||
trigger.action.outText("mGnd: WARNING: milGround zone <" .. theZone.name .. "> has no 'cloner' interface, will fail!", 30)
|
||||
end
|
||||
-- now get the target zone. it's inside the milGround property
|
||||
local tzn = theZone:getStringFromZoneProperty("milGround", "cfxNone")
|
||||
local tz = cfxZones.getZoneByName(tzn)
|
||||
if not tz then
|
||||
trigger.action.outText("mGnd: target zone <" .. tzn .. "> not found for milGroundZone <" .. theZone.name .. ">, will fail!", 30)
|
||||
end
|
||||
theZone.targetZone = tz
|
||||
if theZone:hasProperty("coalition") then
|
||||
theZone.owner = theZone:getCoalitionFromZoneProperty("coalition")
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Update
|
||||
--
|
||||
function milGround.update()
|
||||
timer.scheduleFunction(milGround.update, {}, timer.getTime() + 1/milGround.ups)
|
||||
|
||||
for zName, theZone in pairs(milGround.zones) do
|
||||
-- synch owner and coa
|
||||
local mo = theZone.masterowner
|
||||
if mo then
|
||||
theZone.owner = mo.owner
|
||||
if theZone.isDynamic then
|
||||
theZone.coa = theZone.owner
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- config
|
||||
--
|
||||
function milGround.readConfigZone()
|
||||
local theZone = cfxZones.getZoneByName("milGroundConfig")
|
||||
if not theZone then
|
||||
theZone = cfxZones.createSimpleZone("milGroundConfig")
|
||||
end
|
||||
milGround.verbose = theZone.verbose
|
||||
end
|
||||
|
||||
--
|
||||
-- API
|
||||
--
|
||||
function milGround.getAttackersForEnemiesOfCoa(coa, addNeutral)
|
||||
-- return all milGround zones that attack zones that belong to
|
||||
-- the enemy of coa
|
||||
local theOtherSide = dcsCommon.getEnemyCoalitionFor(coa)
|
||||
local attackers = {}
|
||||
for zName, theZone in pairs(milGround.zones) do
|
||||
local tz = theZone.targetZone
|
||||
if tz.owner ~= coa then
|
||||
if addNeutral then
|
||||
table.insert(attackers, theZone)
|
||||
else
|
||||
if tz.owner == theOtherSide then
|
||||
table.insert(attackers, theZone)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return attackers
|
||||
end
|
||||
|
||||
function milGround.startAttackFrom(theZone)
|
||||
cloneZones.spawnWithCloner(theZone) -- that's all, folx
|
||||
end
|
||||
|
||||
--
|
||||
-- start up
|
||||
--
|
||||
function milGround.start()
|
||||
-- lib check
|
||||
if not dcsCommon.libCheck then
|
||||
trigger.action.outText("cfx mil ground requires dcsCommon", 30)
|
||||
return false
|
||||
end
|
||||
if not dcsCommon.libCheck("cfx mil ground", milGround.requiredLibs) then
|
||||
return false
|
||||
end
|
||||
|
||||
-- read config
|
||||
milGround.readConfigZone()
|
||||
|
||||
-- process milGround Zones
|
||||
local attrZones = cfxZones.getZonesWithAttributeNamed("milGround")
|
||||
for k, aZone in pairs(attrZones) do
|
||||
milGround.readMilGroundZone(aZone) -- process attributes
|
||||
milGround.addMilGroundZone(aZone) -- add to list
|
||||
end
|
||||
|
||||
-- start update in 5 seconds
|
||||
timer.scheduleFunction(milGround.update, {}, timer.getTime() + 1/milGround.ups)
|
||||
|
||||
-- say hi
|
||||
trigger.action.outText("milGround v" .. milGround.version .. " started.", 30)
|
||||
return true
|
||||
end
|
||||
|
||||
if not milGround.start() then
|
||||
trigger.action.outText("milGround failed to start.", 30)
|
||||
milGround = nil
|
||||
end
|
||||
@ -1,5 +1,5 @@
|
||||
milHelo = {}
|
||||
milHelo.version = "0.0.0"
|
||||
milHelo.version = "1.0.0"
|
||||
milHelo.requiredLibs = {
|
||||
"dcsCommon",
|
||||
"cfxZones",
|
||||
@ -7,7 +7,7 @@ milHelo.requiredLibs = {
|
||||
}
|
||||
milHelo.zones = {}
|
||||
milHelo.targetKeywords = {
|
||||
"milTarget", -- my own
|
||||
"milTarget", -- my own zone
|
||||
"camp", -- camps
|
||||
"airfield", -- airfields
|
||||
"FARP", -- FARPzones
|
||||
@ -74,6 +74,19 @@ function milHelo.readMilHeloZone(theZone) -- process attributes
|
||||
theZone.msnType = "cas"
|
||||
end
|
||||
|
||||
-- see if our ownership is tied to a master
|
||||
-- adds dynamic coalition capability
|
||||
if theZone:hasProperty("masterOwner") then
|
||||
local mo = theZone:getStringFromZoneProperty("masterOwner")
|
||||
local mz = cfxZones.getZoneByName(mo)
|
||||
if not mz then
|
||||
trigger.action.outText("+++milH: WARNING: Master Owner <" .. mo .. "> for zone <" .. theZone.name .. "> does not exist!", 30)
|
||||
else
|
||||
theZone.masterOwner = mz
|
||||
end
|
||||
theZone.isDynamic = theZone:getBoolFromZoneProperty("dynamic", true)
|
||||
end
|
||||
|
||||
-- get all groups inside me
|
||||
local myGroups, count = milHelo.allGroupsInZoneByData(theZone)
|
||||
theZone.myGroups = myGroups
|
||||
@ -179,6 +192,9 @@ function milHelo.createROETask(num, roe)
|
||||
end
|
||||
|
||||
function milHelo.createEngageIZTask(num, theZone)
|
||||
-- trigger.action.outText("Creating engage in zone task for zone <" .. theZone.name .. ">, marking on map", 30)
|
||||
-- theZone:drawZone()
|
||||
-- theZone:drawText("casz - " .. theZone.name, 20)
|
||||
local p = theZone:getPoint()
|
||||
if not num then num = 1 end
|
||||
local task = {}
|
||||
@ -266,7 +282,8 @@ function milHelo.createCommandTask(theCommand, num)
|
||||
return t
|
||||
end
|
||||
|
||||
function milHelo.createTakeOffWP(theZone, engageInZone, engageZone)
|
||||
function milHelo.createTakeOffWP(theZone, engageInZone, engageZone, ROE)
|
||||
if not ROE then ROE = 0 end -- wepons free
|
||||
local WP = {}
|
||||
WP.alt = 500 -- theZone.alt
|
||||
WP.alt_type = "BARO"
|
||||
@ -281,7 +298,7 @@ function milHelo.createTakeOffWP(theZone, engageInZone, engageZone)
|
||||
local tasks = {}
|
||||
local casTask = milHelo.createCASTask(1)
|
||||
tasks[1] = casTask
|
||||
local roeTask = milHelo.createROETask(2,0) -- 0 = weapons free
|
||||
local roeTask = milHelo.createROETask(2,ROE) -- 0 = weapons free, 4 = weapon hold
|
||||
tasks[2] = roeTask
|
||||
if engageInZone then
|
||||
if not engageZone then
|
||||
@ -353,7 +370,7 @@ function milHelo.createLandWP(gName, theZone, targetZone)
|
||||
return toWP
|
||||
end
|
||||
|
||||
function milHelo.createOMWCallbackWP(gName, number, pt, alt, speed, action) -- name is group name
|
||||
function milHelo.createOMWCallbackWP(gName, number, pt, alt, speed, action, ROE) -- name is group name
|
||||
if not action then action = "none" end
|
||||
local omwWP = dcsCommon.createSimpleRoutePointData(pt, alt, speed)
|
||||
omwWP.alt_type = "RADIO"
|
||||
@ -364,29 +381,23 @@ function milHelo.createOMWCallbackWP(gName, number, pt, alt, speed, action) -- n
|
||||
local ttsk = {}
|
||||
local command = "milHelo.reachedWP('" .. gName .. "', '" .. number .. "', '" .. action .."')"
|
||||
ttsk[1] = milHelo.createCommandTask(command,1)
|
||||
if ROE then
|
||||
ttsk[2] = milHelo.createROETask(2, ROE)
|
||||
end
|
||||
task.params.tasks = ttsk
|
||||
omwWP.task = task
|
||||
return omwWP
|
||||
end
|
||||
|
||||
-- a point yDegrees off the path from AB, xPercent of the total distance
|
||||
-- between A and B away from A
|
||||
--[[--
|
||||
function milHelo.pointXpercentYdegOffAB(A, B, xPer, yDeg) -- rets xzz point
|
||||
local bearingRad = dcsCommon.bearingFromAtoB(A, B)
|
||||
local dist = dcsCommon.dist(A, B)
|
||||
local deviation = bearingRad + yDeg * 0.0174533
|
||||
local newDist = dist * xPer/100
|
||||
local newPoint = dcsCommon.pointInDirectionOfPointXYY(deviation, newDist, A)
|
||||
return newPoint
|
||||
end
|
||||
--]]--
|
||||
|
||||
function milHelo.spawnForZone(theZone, targetZone)
|
||||
-- note that each zone only has a single msnType, so zone
|
||||
-- defines msn type
|
||||
local n = dcsCommon.randomBetween(1, theZone.hCount)
|
||||
local theRawData = dcsCommon.getNthItem(theZone.hGroups, n)
|
||||
local gData = dcsCommon.clone(theRawData)
|
||||
local oName = gData.name
|
||||
gData.lateActivation = false
|
||||
|
||||
-- pre-process gData: names, id etc
|
||||
gData.name = dcsCommon.uuid(gData.name)
|
||||
@ -414,41 +425,59 @@ function milHelo.spawnForZone(theZone, targetZone)
|
||||
trigger.action.outText("Setting up casZ for <" .. theZone.name .. "> to <" .. targetZone.name .. ">", 30)
|
||||
end
|
||||
|
||||
local wpTOff = milHelo.createTakeOffWP(theZone, casInZone, targetZone)
|
||||
local wpTOff = milHelo.createTakeOffWP(theZone, casInZone, targetZone) -- no ROE = weapons free
|
||||
|
||||
-- depending on mission, create an orbit or land WP
|
||||
local dest = targetZone:getPoint()
|
||||
local B = dest
|
||||
local A = theZone:getPoint()
|
||||
if theZone.msnType == "cas" or theZone.msnType == "patrol" then
|
||||
dcsCommon.addRoutePointForGroupData(gData, wpTOff)
|
||||
-- patrol and cas go straight to the target, they do not
|
||||
-- have an ingress. Meaning: they have the same route
|
||||
-- profile as 'insert'
|
||||
wpTOff = milHelo.createTakeOffWP(theZone, casInZone, targetZone, 4) -- 4 = weapons HOLD
|
||||
dcsCommon.addRoutePointForGroupData(gData, wpTOff) -- wp 1
|
||||
|
||||
-- on approach, 70% at target, go weapons hot
|
||||
local apr = dcsCommon.vLerp(A, B, 0.75)
|
||||
local omw2 = milHelo.createOMWCallbackWP(gName, 2, apr, theZone.alt, theZone.speed, "weapons free", 0) -- wp 2
|
||||
dcsCommon.addRoutePointForGroupData(gData, omw2)
|
||||
|
||||
-- possible expandion: if cas, we have an ingress point?
|
||||
local wpDest = milHelo.createOrbitWP(theZone, dest)
|
||||
dcsCommon.addRoutePointForGroupData(gData, wpDest)
|
||||
local retPt = milHelo.createLandWP(gName, theZone, theZone)
|
||||
dcsCommon.addRoutePointForGroupData(gData, retPt)
|
||||
--dcsCommon.dumpVar2Str("caser group", gData)
|
||||
dcsCommon.addRoutePointForGroupData(gData, wpDest) -- wp 3
|
||||
--local retPt = milHelo.createLandWP(gName, theZone, theZone)
|
||||
--dcsCommon.addRoutePointForGroupData(gData, retPt)
|
||||
local retpt = theZone:getPoint()
|
||||
local omw4 = milHelo.createOMWCallbackWP(gName, 4, retpt, theZone.alt, theZone.speed, "remove")
|
||||
dcsCommon.addRoutePointForGroupData(gData, omw4) -- wp 4
|
||||
elseif theZone.msnType == "casz" then
|
||||
dcsCommon.addRoutePointForGroupData(gData, wpTOff)
|
||||
wpTOff = milHelo.createTakeOffWP(theZone, casInZone, targetZone, 4) -- 4 = ROE weapons hold
|
||||
dcsCommon.addRoutePointForGroupData(gData, wpTOff) -- wp 1
|
||||
-- go to CAS destination with Engage in Zone active
|
||||
-- we may want to make ingress and egress wp before heading to
|
||||
-- the 'real' CASZ point
|
||||
-- make ingress point, in direction of target, 30 degrees to the right, half distance.
|
||||
local ingress = dcsCommon.pointXpercentYdegOffAB(A, B, math.random(50,80), math.random(20,50))
|
||||
--local pt = targetZone:getPoint()
|
||||
local omw1 = milHelo.createOMWCallbackWP(gName, 2, ingress, theZone.alt, theZone.speed, "none")
|
||||
local omw1 = milHelo.createOMWCallbackWP(gName, 2, ingress, theZone.alt, theZone.speed, "weapons free", 0) -- wp 2
|
||||
dcsCommon.addRoutePointForGroupData(gData, omw1)
|
||||
local omw2 = milHelo.createOMWCallbackWP(gName, 3, B, theZone.alt, theZone.speed, "none")
|
||||
dcsCommon.addRoutePointForGroupData(gData, omw2)
|
||||
dcsCommon.addRoutePointForGroupData(gData, omw2) -- wp 3
|
||||
-- egress point
|
||||
local egress = dcsCommon.pointXpercentYdegOffAB(B, A, math.random(20, 50), math.random(20,50))
|
||||
local omw3 = milHelo.createOMWCallbackWP(gName, 4, egress, theZone.alt, theZone.speed, "none")
|
||||
dcsCommon.addRoutePointForGroupData(gData, omw3)
|
||||
local retPt = milHelo.createLandWP(gName, theZone, theZone)
|
||||
dcsCommon.addRoutePointForGroupData(gData, retPt)
|
||||
dcsCommon.addRoutePointForGroupData(gData, omw3) -- wp 4
|
||||
-- return to aerodrome, deallocate
|
||||
local retpt = theZone:getPoint()
|
||||
local omw4 = milHelo.createOMWCallbackWP(gName, 5, retpt, theZone.alt, theZone.speed, "remove")
|
||||
dcsCommon.addRoutePointForGroupData(gData, omw4) -- wp 5
|
||||
|
||||
elseif theZone.msnType == "insert" then
|
||||
local wpDest = milHelo.createLandWP(gName, theZone, targetZone)
|
||||
dcsCommon.addRoutePointForGroupData(gData, wpTOff)
|
||||
dcsCommon.addRoutePointForGroupData(gData, wpDest)
|
||||
dcsCommon.addRoutePointForGroupData(gData, wpTOff) -- wp 1
|
||||
dcsCommon.addRoutePointForGroupData(gData, wpDest) -- wp 2
|
||||
-- will land and dealloc there after spawning troops
|
||||
end
|
||||
|
||||
-- make coa a cty
|
||||
@ -481,6 +510,7 @@ function milHelo.insertTroops(theUnit, targetZone, srcZone)
|
||||
end
|
||||
|
||||
local gData = dcsCommon.clone(theRawData)
|
||||
gData.lateActivation = false -- force false
|
||||
-- deploy in ring formation
|
||||
-- remove all routes
|
||||
-- mayhaps prepare for orders and formation
|
||||
@ -511,7 +541,7 @@ function milHelo.insertTroops(theUnit, targetZone, srcZone)
|
||||
local groupCat = Group.Category.GROUND
|
||||
local theSpawnedGroup = coalition.addGroup(cty, groupCat, gData)
|
||||
|
||||
trigger.action.outText("Inserted troops <" .. gName .. ">", 30)
|
||||
--trigger.action.outText("Inserted troops <" .. gName .. ">", 30)
|
||||
|
||||
return theSpawnedGroup, gData
|
||||
end
|
||||
@ -580,12 +610,24 @@ function milHelo.spawnImpostorsFromData(rawData, cat, ctry)
|
||||
end
|
||||
|
||||
function milHelo.reachedWP(gName, wpNum, action)
|
||||
trigger.action.outText("MilH group <" .. gName .. " reached wp #" .. wpNum .. ".", 30)
|
||||
if not action then action = "NIL" end
|
||||
if milHelo.verbose then
|
||||
trigger.action.outText("MilH group <" .. gName .. " reached wp #" .. wpNum .. " with action <" .. action .. ">.", 30)
|
||||
end
|
||||
if action == "remove" then
|
||||
theGroup = Group.getByName(gName)
|
||||
if theGroup and Group.isExist(theGroup) then
|
||||
if milHelo.verbose then
|
||||
trigger.action.outText("%%%%%%%%%% removing mil hel <" .. gName .. ">", 30)
|
||||
end
|
||||
Group.destroy(theGroup)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function milHelo.landedCB(who, where, from) -- who group name, where a zone
|
||||
trigger.action.outText("milhelo landed CB for group <" .. who .. ">", 30)
|
||||
-- trigger.action.outText("milhelo landed CB for group <" .. who .. ">", 30)
|
||||
-- step 1: remove the flight
|
||||
local theGroup = Group.getByName(who)
|
||||
if theGroup then
|
||||
@ -601,7 +643,8 @@ function milHelo.landedCB(who, where, from) -- who group name, where a zone
|
||||
local theFlight = milHelo.flights[who]
|
||||
local oName = theFlight.oName
|
||||
local theZone = theFlight.origin
|
||||
if theZone.msn == "insertion" then
|
||||
-- note: "insertion" is probably wrong, remove in line below
|
||||
if theZone.msnType == "insertion" or theZone.msnType == "insert" then
|
||||
-- create a static stand-in for scenery
|
||||
local rawData, cat, ctry = milHelo.getRawDataFromGroupNamed(who, oName)
|
||||
Group.destroy(aGroup)
|
||||
@ -620,6 +663,17 @@ end
|
||||
--
|
||||
function milHelo.update()
|
||||
timer.scheduleFunction(milHelo.update, {}, timer.getTime() + 1)
|
||||
-- update all master owners
|
||||
for idx, theZone in pairs (milHelo.zones) do
|
||||
local mo = theZone.masterOwner
|
||||
if mo then
|
||||
theZone.owner = mo.owner
|
||||
if theZone.isDynamic then
|
||||
theZone.coa = mo.owner
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function milHelo.GCcollected(gName)
|
||||
@ -672,10 +726,16 @@ function milHelo:onEvent(theEvent)
|
||||
else
|
||||
-- maybe its a return flight
|
||||
if srcZone:pointInZone(p) then
|
||||
trigger.action.outText("Flight <" .. gName .. "> originating from <" .. srcZone.name .. "> landed back home", 30)
|
||||
-- trigger.action.outText("Flight <" .. gName .. "> originating from <" .. srcZone.name .. "> landed back home", 30)
|
||||
else
|
||||
trigger.action.outText("Flight <" .. gName .. "> originating from <" .. srcZone.name .. "> landed OUTSIDE of src or target zone <" .. tgtZone.name .. ">", 30)
|
||||
-- trigger.action.outText("Flight <" .. gName .. "> originating from <" .. srcZone.name .. "> landed OUTSIDE of src or target zone <" .. tgtZone.name .. ">, clearing.", 30)
|
||||
end
|
||||
-- remove it now
|
||||
local theGroup = Group.getByName(gName)
|
||||
if theGroup and Group.isExist(theGroup) then
|
||||
Group.destroy(theGroup)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@ -690,7 +750,7 @@ function milHelo.getMilSources(side, msnType) -- msnType is optional
|
||||
if side == "blue" then side = 2 end
|
||||
local sources = {}
|
||||
for idx, theZone in pairs(milHelo.zones) do
|
||||
if theZone.owner == side then -- must be owned by same side
|
||||
if theZone.coa == side then -- coa must be same side, use masterOwner for dynamism
|
||||
if msnType then
|
||||
if theZone.msnType == msnType then
|
||||
table.insert(sources, theZone)
|
||||
@ -703,13 +763,18 @@ function milHelo.getMilSources(side, msnType) -- msnType is optional
|
||||
return sources -- an array, NOT dict so we can pickrandom
|
||||
end
|
||||
|
||||
function milHelo.getMilTargets(side) -- gets mil targets that DO NOT belong to side
|
||||
function milHelo.getMilTargets(side, ignoreNeutral) -- gets mil targets that DO NOT belong to side
|
||||
if side == "red" then side = 1 end -- better safe...
|
||||
if side == "blue" then side = 2 end
|
||||
local tgt = {}
|
||||
for idx, theZone in pairs(milHelo.targets) do
|
||||
-- we use OWNER, not COA here!
|
||||
if theZone.owner ~= side then -- must NOT be owned by same side
|
||||
if ignoreNeutral and theZone.owner == 0 then
|
||||
else
|
||||
table.insert(tgt, theZone)
|
||||
--trigger.action.outText("zone <" .. theZone.name .. "> owned by <" .. theZone.owner .. "> is possible target for coa <" .. side .. ">", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
return tgt
|
||||
@ -730,7 +795,7 @@ end
|
||||
function milHelo.start()
|
||||
-- lib check
|
||||
if not dcsCommon.libCheck then
|
||||
trigger.action.outText("cfx civ helo requires dcsCommon", 30)
|
||||
trigger.action.outText("cfx mil helo requires dcsCommon", 30)
|
||||
return false
|
||||
end
|
||||
if not dcsCommon.libCheck("cfx mil helo", milHelo.requiredLibs) then
|
||||
@ -758,6 +823,9 @@ function milHelo.start()
|
||||
-- start update in 5 seconds
|
||||
timer.scheduleFunction(milHelo.update, {}, timer.getTime() + 1/milHelo.ups)
|
||||
|
||||
-- start GC
|
||||
milHelo.GC()
|
||||
|
||||
-- install event handler
|
||||
world.addEventHandler(milHelo)
|
||||
|
||||
|
||||
572
modules/milWings.lua
Normal file
572
modules/milWings.lua
Normal file
@ -0,0 +1,572 @@
|
||||
milWings = {}
|
||||
milWings.version = "0.9.5"
|
||||
milWings.requiredLibs = {
|
||||
"dcsCommon",
|
||||
"cfxZones",
|
||||
"cfxMX",
|
||||
"wingTaskHelper",
|
||||
}
|
||||
milWings.zones = {} -- mil wings zones for flights. can have master owner
|
||||
milWings.targetKeywords = { -- same as mil helo plus x
|
||||
"wingTarget", -- my own zone
|
||||
"milTarget", -- milH zones
|
||||
"camp", -- camps
|
||||
"airfield", -- airfields
|
||||
"FARP", -- FARPzones
|
||||
}
|
||||
|
||||
milWings.targets = {} -- targets for mil wings. can have master owner
|
||||
-- includes wingTarget and other keywors
|
||||
milWings.pureTargets = {} -- targets with wingTarget keyword
|
||||
milWings.seadTargets = {}
|
||||
milWings.flights = {} -- all currently active mil helo flights
|
||||
milWings.ups = 1
|
||||
milWings.missionTypes = {
|
||||
"cas", -- standard cas
|
||||
"cap", -- orbit over zone for duration
|
||||
"sead", -- engage in zone for target zone's radius
|
||||
"bomb",
|
||||
}
|
||||
|
||||
function milWings.addMilWingsZone(theZone)
|
||||
milWings.zones[theZone.name] = theZone
|
||||
end
|
||||
|
||||
function milWings.addMilWingsTargetZone(theZone)
|
||||
milWings.targets[theZone.name] = theZone -- overwrite if duplicate
|
||||
if theZone:hasProperty("SEAD") then
|
||||
milWings.seadTargets[theZone.name] = theZone
|
||||
end
|
||||
if theZone:hasProperty("wingTarget") then
|
||||
milWings.pureTargets[theZone.name] = theZone
|
||||
theZone.wingTargetName = theZone:getStringFromZoneProperty("wingTarget", "<*" .. theZone.name .. ">")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Reading / Processing milWings Zones
|
||||
--
|
||||
function milWings.partOfGroupDataInZone(theZone, theUnits) -- move to mx?
|
||||
local zP = theZone:getDCSOrigin() -- don't use getPoint now.
|
||||
zP.y = 0
|
||||
for idx, aUnit in pairs(theUnits) do
|
||||
local uP = {}
|
||||
uP.x = aUnit.x
|
||||
uP.y = 0
|
||||
uP.z = aUnit.y -- !! y-z
|
||||
if theZone:pointInZone(uP) then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function milWings.allGroupsInZoneByData(theZone) -- move to MX?
|
||||
local theGroupsInZone = {}
|
||||
local count = 0
|
||||
for groupName, groupData in pairs(cfxMX.groupDataByName) do
|
||||
if groupData.units then
|
||||
if milWings.partOfGroupDataInZone(theZone, groupData.units) then
|
||||
theGroupsInZone[groupName] = groupData -- DATA! work on clones!
|
||||
count = count + 1
|
||||
if theZone.verbose then
|
||||
trigger.action.outText("+++milH: added group <" .. groupName .. "> for zone <" .. theZone.name .. ">", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return theGroupsInZone, count
|
||||
end
|
||||
|
||||
function milWings.readMilWingsZone(theZone) -- process attributes
|
||||
theZone.msnType = string.lower(theZone:getStringFromZoneProperty("milWings", "cas"))
|
||||
if dcsCommon.arrayContainsString(milWings.missionTypes, theZone.msnType) then
|
||||
-- great, mission type is known
|
||||
else
|
||||
trigger.action.outText("+++milW: zone <" .. theZone.name .. ">: unknown wing mission type <" .. theZone.msnType .. ">, defaulting to 'CAS'", 30)
|
||||
theZone.msnType = "cas"
|
||||
end
|
||||
|
||||
-- see if our ownership is tied to a master
|
||||
-- this adds dynamic coalition capability
|
||||
if theZone:hasProperty("masterOwner") then
|
||||
local mo = theZone:getStringFromZoneProperty("masterOwner")
|
||||
local mz = cfxZones.getZoneByName(mo)
|
||||
if not mz then
|
||||
trigger.action.outText("+++milW: WARNING: Master Owner <" .. mo .. "> for zone <" .. theZone.name .. "> does not exist!", 30)
|
||||
else
|
||||
theZone.masterOwner = mz
|
||||
end
|
||||
theZone.isDynamic = theZone:getBoolFromZoneProperty("dynamic", true)
|
||||
if theZone.verbose then
|
||||
trigger.action.outText("milwing target <" .. theZone.name .. "> has masterOwner <" .. theZone.mz.name .. ">", 30)
|
||||
if theZone.isDynamic then
|
||||
trigger.action.outText("and coa is dynamically linked to owner.", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- get all groups inside me
|
||||
local myGroups, count = milWings.allGroupsInZoneByData(theZone)
|
||||
theZone.fGroups = {}
|
||||
theZone.fCount = 0
|
||||
-- sort into ground, helo and fixed
|
||||
for groupName, data in pairs(myGroups) do
|
||||
local catRaw = cfxMX.groupTypeByName[groupName]
|
||||
if theZone.verbose then
|
||||
trigger.action.outText("Proccing zone <" .. theZone.name .. ">: group <" .. groupName .. "> - type <" .. catRaw .. ">", 30)
|
||||
end
|
||||
if catRaw == "plane" then
|
||||
theZone.fGroups[groupName] = data
|
||||
theZone.fCount = theZone.fCount + 1
|
||||
else
|
||||
trigger.action.outText("+++milH: ignored group <" .. groupName .. ">: wrong type <" .. catRaw .. ">", 30)
|
||||
end
|
||||
end
|
||||
theZone.coa = theZone:getCoalitionFromZoneProperty("coalition", 0)
|
||||
theZone.hot = theZone:getBoolFromZoneProperty("hot", false)
|
||||
theZone.inAir = theZone:getBoolFromZoneProperty("inAir", false)
|
||||
theZone.speed = theZone:getNumberFromZoneProperty("speed", 220) -- 800 kmh
|
||||
theZone.alt = theZone:getNumberFromZoneProperty("alt", 6000) -- we are always radar alt
|
||||
theZone.loiter = theZone:getNumberFromZoneProperty("loiter", 3600) -- 1 hour loiter default
|
||||
|
||||
-- wipe all existing
|
||||
for groupName, data in pairs(theZone.fGroups) do
|
||||
local g = Group.getByName(groupName)
|
||||
if g then
|
||||
Group.destroy(g)
|
||||
end
|
||||
end
|
||||
if theZone.verbose or milWings.verbose then
|
||||
trigger.action.outText("+++milW: processed milWings zone <" .. theZone.name .. ">", 30)
|
||||
end
|
||||
end
|
||||
|
||||
function milWings.readMilWingsTargetZone(theZone)
|
||||
-- can also be "camp", "farp", "airfield"
|
||||
theZone.wingRadius = theZone:getNumberFromZoneProperty("wingRadius", theZone.radius)
|
||||
if (not theZone.isCircle) and (not theZone:hasProperty("wingRadius")) then
|
||||
-- often when we have a camp there is no cas radius, use 60km
|
||||
-- and zone is ploygonal
|
||||
if theZone.verbose then
|
||||
trigger.action.outText("+++milH: Warning - milH target zone <" .. theZone.name .. "> is polygonal and has no CAS radius attribute. Defaulting to 80km", 30)
|
||||
end
|
||||
-- theZone.casRadius = 80000
|
||||
theZone.wingRadius = 80000
|
||||
end
|
||||
|
||||
if theZone:hasProperty("wingTypes") then -- if present, else all good
|
||||
theZone.wingTypes = theZone:getListFromZoneProperty("wingTypes", "empty")
|
||||
end
|
||||
|
||||
if theZone.verbose or milWings.verbose then
|
||||
trigger.action.outText("+++milH: processed milWings TARGET zone <" .. theZone.name .. ">", 30)
|
||||
end
|
||||
|
||||
if theZone:hasProperty("masterOwner") then
|
||||
local mo = theZone:getStringFromZoneProperty("masterOwner")
|
||||
local mz = cfxZones.getZoneByName(mo)
|
||||
if not mz then
|
||||
trigger.action.outText("+++milW: WARNING: Master Owner <" .. mo .. "> for zone <" .. theZone.name .. "> does not exist!", 30)
|
||||
else
|
||||
theZone.masterOwner = mz
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- creating flights
|
||||
--
|
||||
function milWings.createTakeOffWayPoint(theZone, targetZone)
|
||||
local wp = {}
|
||||
wp.alt = theZone.alt
|
||||
wp.action = "Turning Point" -- default. overrides hot
|
||||
wp.type = "Turning Point"
|
||||
if not theZone.inAir then
|
||||
if theZone.hot then
|
||||
wp.action = "From Parking Area Hot"
|
||||
wp.type = "TakeOffParkingHot"
|
||||
else
|
||||
wp.action = "From Parking Area"
|
||||
wp.type = "TakeOffParking"
|
||||
end
|
||||
wp.alt = 0
|
||||
wp.speed = 0
|
||||
local af = dcsCommon.getClosestAirbaseTo(theZone:getPoint(), 0)
|
||||
-- trigger.action.outText("closest airfield for this flight is <" .. af:getName() .. ">", 30)
|
||||
wp.airdromeId = af:getID()
|
||||
end
|
||||
-- trigger.action.outText("flight has action <" .. wp.action .. "> and type <" .. wp.type .. ">", 30)
|
||||
|
||||
wp.speed = theZone.speed
|
||||
local p = theZone:getPoint()
|
||||
wp.x = p.x
|
||||
wp.y = p.z
|
||||
wp.formation_template= ""
|
||||
if theZone.msnType == "cas" then
|
||||
wp.task = dcsCommon.clone(wingTaskHelper.casTOTask)
|
||||
-- now simply change some bits from the template
|
||||
p = targetZone:getPoint()
|
||||
wp.task.params.tasks[8].params.x = p.x
|
||||
wp.task.params.tasks[8].params.y = p.z
|
||||
if targetZone.wingRadius then -- should ALWAYS be true
|
||||
wp.task.params.tasks[8].params.zoneRadius = targetZone.wingRadius
|
||||
else
|
||||
wp.task.params.tasks[8].params.zoneRadius = 80000
|
||||
trigger.action.outText("WARNING: creating CAS flight <" .. theZone.name .. "> with no radius for target zone <" .. targetZone.name .. ">", 30)
|
||||
end
|
||||
elseif theZone.msnType == "cap" then
|
||||
wp.task = dcsCommon.clone(wingTaskHelper.capTOTask)
|
||||
p = targetZone:getPoint()
|
||||
wp.task.params.tasks[6].params.x = p.x
|
||||
wp.task.params.tasks[6].params.y = p.z
|
||||
-- wp.task.params.tasks[6].params.zoneRadius = theZone.capRadius
|
||||
if targetZone.wingRadius then -- should ALWAYS be true
|
||||
wp.task.params.tasks[6].params.zoneRadius = targetZone.wingRadius
|
||||
else
|
||||
wp.task.params.tasks[6].params.zoneRadius = 80000
|
||||
-- trigger.action.outText("WARNING: creating CAP flight <" .. theZone.name .. "> with no radius for target zone <" .. targetZone.name .. ">", 30)
|
||||
end
|
||||
elseif theZone.msnType == "sead" then
|
||||
wp.task = dcsCommon.clone(wingTaskHelper.seadTOTask)
|
||||
elseif theZone.msnType == "bomb" then
|
||||
wp.task = dcsCommon.clone(wingTaskHelper.bombTOTask)
|
||||
else
|
||||
trigger.action.outText("milW: unknown msnType <" .. theZone.msnType .. "> in zone <" .. theZone.name .. ">", 30)
|
||||
end
|
||||
|
||||
return wp
|
||||
end
|
||||
|
||||
function milWings.createActionWaypoint(theZone, targetZone)
|
||||
local p = targetZone:getPoint()
|
||||
local wp = dcsCommon.createSimpleRoutePointData(p, theZone.alt, theZone.speed)
|
||||
if theZone.msnType == "bomb" then
|
||||
local task = dcsCommon.clone(wingTaskHelper.bombActionTask)
|
||||
task.params.tasks[1].params.x = p.x
|
||||
task.params.tasks[1].params.y = p.z
|
||||
task.params.tasks[1].params.altitude = theZone.alt
|
||||
task.params.tasks[1].params.speed = theZone.speed
|
||||
wp.task = task
|
||||
end
|
||||
return wp
|
||||
end
|
||||
|
||||
function milWings.createCommandTask(theCommand, num)
|
||||
if not num then num = 1 end
|
||||
local t = {}
|
||||
t.enabled = true
|
||||
t.auto = false
|
||||
t.id = "WrappedAction"
|
||||
t.number = num
|
||||
local params = {}
|
||||
t.params = params
|
||||
local action = {}
|
||||
params.action = action
|
||||
action.id = "Script"
|
||||
local p2 = {}
|
||||
action.params = p2
|
||||
p2.command = theCommand
|
||||
return t
|
||||
end
|
||||
|
||||
function milWings.createCallbackWP(gName, number, pt, alt, speed, action) -- name is group name
|
||||
if not action then action = "none" end
|
||||
local omwWP = dcsCommon.createSimpleRoutePointData(pt, alt, speed)
|
||||
omwWP.alt_type = "BARO"
|
||||
-- create a command waypoint
|
||||
local task = {}
|
||||
task.id = "ComboTask"
|
||||
task.params = {}
|
||||
local ttsk = {}
|
||||
local command = "milWings.inFlightCB('" .. gName .. "', '" .. number .. "', '" .. action .."')"
|
||||
ttsk[1] = milWings.createCommandTask(command,1)
|
||||
task.params.tasks = ttsk
|
||||
omwWP.task = task
|
||||
return omwWP
|
||||
end
|
||||
|
||||
function milWings.spawnForZone(theZone, targetZone)
|
||||
-- pick one of the flight groups
|
||||
if not theZone.fCount or theZone.fCount < 1 then
|
||||
trigger.action.outText("+++milW: WARNING - no f-groups in zone <" .. theZone.name .. "> at spawnForZone", 30)
|
||||
return nil, nil
|
||||
end
|
||||
local n = dcsCommon.randomBetween(1, theZone.fCount)
|
||||
local theRawData = dcsCommon.getNthItem(theZone.fGroups, n)
|
||||
local gData = dcsCommon.clone(theRawData)
|
||||
if not gData then
|
||||
trigger.action.outText("+++milW: WARNING: NIL gData in spawnForZone for <" .. theZone.name .. ">", 30)
|
||||
return nil, nil
|
||||
end
|
||||
gData.lateActivation = false
|
||||
|
||||
local oName = gData.name
|
||||
|
||||
-- pre-process gData: names, id etc
|
||||
gData.name = dcsCommon.uuid(gData.name)
|
||||
local gName = gData.name
|
||||
for idx, uData in pairs(gData.units) do
|
||||
uData.name = dcsCommon.uuid(uData.name)
|
||||
uData.alt = theZone.alt
|
||||
uData.alt_type = "BARO"
|
||||
uData.speed = theZone.speed
|
||||
uData.unitId = nil
|
||||
end
|
||||
gData.groupId = nil
|
||||
|
||||
-- set task for group
|
||||
gData.task = "CAS" -- default
|
||||
if theZone.msnType == "cap" then
|
||||
gData.task = "CAP"
|
||||
elseif theZone.msnType == "sead" then
|
||||
gData.task = "SEAD"
|
||||
elseif theZone.msnType == "bomb" then
|
||||
gData.task = "Ground Attack"
|
||||
end
|
||||
-- trigger.action.outText("main task is " .. gData.task, 30)
|
||||
|
||||
-- create route
|
||||
local route = {}
|
||||
route.routeRelativeTOT = true
|
||||
route.points = {}
|
||||
gData.route = route
|
||||
|
||||
-- create take-off waypoint for this flight
|
||||
local wpTOff = milWings.createTakeOffWayPoint(theZone, targetZone)
|
||||
dcsCommon.addRoutePointForGroupData(gData, wpTOff)
|
||||
|
||||
-- ingress point
|
||||
local dest = targetZone:getPoint()
|
||||
local B = dest
|
||||
local A = theZone:getPoint()
|
||||
local ingress = dcsCommon.pointXpercentYdegOffAB(A, B, math.random(50,80), math.random(20,50))
|
||||
local omwWP = dcsCommon.createSimpleRoutePointData(ingress, theZone.alt, theZone.speed)
|
||||
dcsCommon.addRoutePointForGroupData(gData, omwWP)
|
||||
|
||||
-- action waypoint
|
||||
local awp = milWings.createActionWaypoint(theZone, targetZone)
|
||||
dcsCommon.addRoutePointForGroupData(gData, awp)
|
||||
|
||||
-- egress
|
||||
local egress = dcsCommon.pointXpercentYdegOffAB(B, A, math.random(20, 50), math.random(20,50))
|
||||
local egWP = dcsCommon.createSimpleRoutePointData(egress, theZone.alt, theZone.speed)
|
||||
dcsCommon.addRoutePointForGroupData(gData, egWP)
|
||||
|
||||
-- maybe add another to safety and then dealloc?
|
||||
local final = milWings.createCallbackWP(gData.name, 4, theZone:getPoint(), theZone.alt, theZone.speed, "delete")
|
||||
dcsCommon.addRoutePointForGroupData(gData, final)
|
||||
|
||||
-- spawn and return
|
||||
local cty = dcsCommon.getACountryForCoalition(theZone.coa)
|
||||
-- spawn
|
||||
local groupCat = Group.Category.AIRPLANE
|
||||
local theSpawnedGroup = coalition.addGroup(cty, groupCat, gData)
|
||||
local theFlight = {}
|
||||
theFlight.oName = oName
|
||||
theFlight.spawn = theSpawnedGroup
|
||||
theFlight.origin = theZone
|
||||
theFlight.destination = targetZone
|
||||
milWings.flights[gName] = theFlight --theSpawnedGroup
|
||||
return theSpawnedGroup, gData
|
||||
end
|
||||
|
||||
--
|
||||
-- Update
|
||||
--
|
||||
function milWings.update()
|
||||
timer.scheduleFunction(milWings.update, {}, timer.getTime() + 1)
|
||||
-- update all master owners
|
||||
for idx, theZone in pairs (milWings.zones) do
|
||||
local mo = theZone.masterOwner
|
||||
if mo then
|
||||
theZone.owner = mo.owner
|
||||
if theZone.isDynamic then
|
||||
theZone.coa = mo.owner
|
||||
end
|
||||
if theZone.verbose then
|
||||
trigger.action.outText("Copied master onwer <" .. mo.owner .. "> from <" .. mo.name .. "> to <" .. theZone.name .. ">", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for idx, theZone in pairs (milWings.targets) do
|
||||
local mo = theZone.masterOwner
|
||||
if mo then
|
||||
theZone.owner = mo.owner
|
||||
if theZone.verbose then
|
||||
trigger.action.outText("Copied master onwer <" .. mo.owner .. "> from <" .. mo.name .. "> to <" .. theZone.name .. ">", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Event Handler
|
||||
--
|
||||
function milWings:onEvent(theEvent)
|
||||
if not theEvent then return end
|
||||
if not theEvent.initiator then return end
|
||||
local theUnit = theEvent.initiator
|
||||
if not theUnit.getGroup then return end
|
||||
local theGroup = theUnit:getGroup()
|
||||
if not theGroup then return end
|
||||
local gName = theGroup:getName()
|
||||
if not gName then return end
|
||||
local theFlight = milWings.flights[gName]
|
||||
if not theFlight then return end -- none of ours
|
||||
|
||||
local id = theEvent.id
|
||||
if id == 4 then
|
||||
-- flight landed -- milFlights currently do not land
|
||||
-- except later transport flights -- we'll deal with those
|
||||
-- later
|
||||
|
||||
-- trigger.action.outText("+++milW: flight <> landed (and removed)", 30)
|
||||
if Group.isExist(theGroup) then
|
||||
-- maybe schedule in a few seconds?
|
||||
Group.destroy(theGroup)
|
||||
end
|
||||
end -- if landed
|
||||
end
|
||||
--
|
||||
-- callback from the flight
|
||||
--
|
||||
function milWings.inFlightCB(gName)
|
||||
-- trigger.action.outText("*****===***** callback in-flight for group <" .. gName .. ">", 30)
|
||||
local theGroup = Group.getByName(gName)
|
||||
if theGroup and Group.isExist(theGroup) then Group.destroy(theGroup) end
|
||||
end
|
||||
|
||||
--
|
||||
-- GC
|
||||
--
|
||||
function milWings.GCcollected(gName)
|
||||
-- do some housekeeping?
|
||||
if milWings.verbose then
|
||||
trigger.action.outText("removed MIL flight <" .. gName .. ">", 30)
|
||||
end
|
||||
end
|
||||
|
||||
function milWings.GC()
|
||||
timer.scheduleFunction(milWings.GC, {}, timer.getTime() + 5)
|
||||
local filtered = {}
|
||||
for gName, theFlight in pairs(milWings.flights) do
|
||||
local theGroup = Group.getByName(gName)
|
||||
if theGroup and Group.isExist(theGroup) then
|
||||
-- all fine, keep it
|
||||
filtered[gName] = theFlight
|
||||
else
|
||||
milWings.GCcollected(gName)
|
||||
end
|
||||
end
|
||||
milWings.flights = filtered
|
||||
end
|
||||
|
||||
--
|
||||
-- API
|
||||
--
|
||||
function milWings.getMilWingSources(side, msnType) -- msnType is optional
|
||||
if side == "red" then side = 1 end -- better safe...
|
||||
if side == "blue" then side = 2 end
|
||||
local sources = {}
|
||||
for idx, theZone in pairs(milWings.zones) do
|
||||
if theZone.coa == side then -- coa must be same side, use masterOwner for dynamism
|
||||
if msnType then
|
||||
if theZone.msnType == msnType then
|
||||
table.insert(sources, theZone)
|
||||
end
|
||||
else
|
||||
table.insert(sources, theZone)
|
||||
end
|
||||
end
|
||||
end
|
||||
return sources -- an array, NOT dict so we can pickrandom
|
||||
end
|
||||
|
||||
function milWings.getMilWingTargets(side, msnType, ignoreNeutral, pure) -- gets mil targets that DO NOT belong to side
|
||||
-- enter with side = -1 to get all
|
||||
local source = milWings.targets
|
||||
if pure then source = milWings.pureTargets end
|
||||
if side == "red" then side = 1 end -- better safe...
|
||||
if side == "blue" then side = 2 end
|
||||
local tgt = {}
|
||||
for idx, theZone in pairs(source) do
|
||||
if theZone.owner ~= side then -- must NOT be owned by same side
|
||||
if ignoreNeutral and theZone.owner == 0 then
|
||||
-- neutral ignored
|
||||
else
|
||||
-- now see if we need to filter by zone's msnType
|
||||
if msnType then
|
||||
if theZone.wingTypes and dcsCommon.arrayContainsStringCaseInsensitive(theZone.wingTypes, msnType) then
|
||||
table.insert(tgt, theZone)
|
||||
end
|
||||
else
|
||||
table.insert(tgt, theZone)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return tgt
|
||||
end
|
||||
|
||||
--
|
||||
-- config
|
||||
--
|
||||
function milWings.readConfigZone()
|
||||
local theZone = cfxZones.getZoneByName("milWingsConfig")
|
||||
if not theZone then
|
||||
theZone = cfxZones.createSimpleZone("milWingsConfig")
|
||||
end
|
||||
milWings.verbose = theZone.verbose
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Start
|
||||
--
|
||||
function milWings.start()
|
||||
-- lib check
|
||||
if not dcsCommon.libCheck then
|
||||
trigger.action.outText("cfx mil wings requires dcsCommon", 30)
|
||||
return false
|
||||
end
|
||||
if not dcsCommon.libCheck("cfx mil wings", milWings.requiredLibs) then
|
||||
return false
|
||||
end
|
||||
|
||||
-- read config
|
||||
milWings.readConfigZone()
|
||||
|
||||
-- process milWings Zones
|
||||
local attrZones = cfxZones.getZonesWithAttributeNamed("milWings")
|
||||
for k, aZone in pairs(attrZones) do
|
||||
milWings.readMilWingsZone(aZone) -- process attributes
|
||||
milWings.addMilWingsZone(aZone) -- add to list
|
||||
end
|
||||
|
||||
for idx, keyWord in pairs(milWings.targetKeywords) do
|
||||
attrZones = cfxZones.getZonesWithAttributeNamed(keyWord)
|
||||
for k, aZone in pairs(attrZones) do
|
||||
milWings.readMilWingsTargetZone(aZone) -- process attributes
|
||||
milWings.addMilWingsTargetZone(aZone) -- add to list
|
||||
end
|
||||
end
|
||||
|
||||
-- start update in 5 seconds
|
||||
timer.scheduleFunction(milWings.update, {}, timer.getTime() + 1/milWings.ups)
|
||||
|
||||
timer.scheduleFunction(milWings.GC, {}, timer.getTime() + 1/milWings.ups * 5)
|
||||
-- install event handler
|
||||
world.addEventHandler(milWings)
|
||||
|
||||
-- say hi
|
||||
trigger.action.outText("milWings v" .. milWings.version .. " started.", 30)
|
||||
return true
|
||||
end
|
||||
|
||||
if not milWings.start() then
|
||||
trigger.action.outText("milWings failed to start.", 30)
|
||||
milWings = nil
|
||||
end
|
||||
@ -1,5 +1,5 @@
|
||||
cfxOwnedZones = {}
|
||||
cfxOwnedZones.version = "2.3.0"
|
||||
cfxOwnedZones.version = "2.3.1"
|
||||
cfxOwnedZones.verbose = false
|
||||
cfxOwnedZones.announcer = true
|
||||
cfxOwnedZones.name = "cfxOwnedZones"
|
||||
@ -42,6 +42,7 @@ cfxOwnedZones.name = "cfxOwnedZones"
|
||||
- per-zone local numkeep
|
||||
- title attribute
|
||||
- code clean-up
|
||||
2.3.1 - restored getNearestOwnedZoneToPoint
|
||||
--]]--
|
||||
cfxOwnedZones.requiredLibs = {
|
||||
"dcsCommon",
|
||||
@ -250,7 +251,7 @@ function cfxOwnedZones.zoneConquered(aZone, theSide, formerOwner) -- 0 = neutral
|
||||
elseif theSide == 0 then who = "NEUTRAL" end
|
||||
aZone.owner = theSide -- just to be sure
|
||||
|
||||
if aZone.announcer then
|
||||
if cfxOwnedZones.announcer or aZone.announcer then
|
||||
if theSide == 0 then
|
||||
trigger.action.outText(aZone.name .. " has become NEUTRAL", 30)
|
||||
else
|
||||
@ -632,6 +633,7 @@ end
|
||||
-- collect zones can filter owned zones.
|
||||
-- by default it filters all zones that are in water
|
||||
-- includes all managed-owner zones
|
||||
-- called from external sources
|
||||
function cfxOwnedZones.collectZones(mode)
|
||||
if not mode then mode = "land" end
|
||||
if mode == "land" then
|
||||
@ -651,6 +653,12 @@ function cfxOwnedZones.collectZones(mode)
|
||||
end
|
||||
end
|
||||
|
||||
-- getNearestOwnedZoneToPoint invoked by heloTroops
|
||||
function cfxOwnedZones.getNearestOwnedZoneToPoint(p)
|
||||
local allZones = cfxOwnedZones.collectZones()
|
||||
return cfxZones.getClosestZone(p, allZones)
|
||||
end
|
||||
|
||||
-- getNearestEnemyOwnedZone invoked by cfxGroundTroops
|
||||
function cfxOwnedZones.getNearestEnemyOwnedZone(theZone, targetNeutral)
|
||||
if not targetNeutral then targetNeutral = false else targetNeutral = true end
|
||||
|
||||
@ -1023,6 +1023,7 @@ function cfxPlayerScore.scheduledAward(args)
|
||||
cfxPlayerScore.coalitionScore[playerSide] = cfxPlayerScore.coalitionScore[playerSide] + theScore.scoreaccu
|
||||
if bank and bank.addFunds then
|
||||
bank.addFunds(playerSide, cfxPlayerScore.score2finance * theScore.scoreaccu)
|
||||
desc = desc .. "(transferred §" .. cfxPlayerScore.score2finance * theScore.scoreaccu .. " to funding)\n"
|
||||
end
|
||||
theScore.scoreaccu = 0
|
||||
hasAward = true
|
||||
@ -1360,14 +1361,21 @@ function cfxPlayerScore.update()
|
||||
-- score!
|
||||
cfxPlayerScore.coalitionScore[coa] = cfxPlayerScore.coalitionScore[coa] + cfxPlayerScore.blueTriggerScore[tName]
|
||||
cfxPlayerScore.blueTriggerFlags[tName] = newVal
|
||||
-- bank it if exists
|
||||
if bank and bank.addFunds then
|
||||
bank.addFunds(coa, cfxPlayerScore.score2finance * cfxPlayerScore.blueTriggerScore[tName])
|
||||
end
|
||||
|
||||
if cfxPlayerScore.announcer then
|
||||
trigger.action.outTextForCoalition(coa, "BLUE goal [" .. tName .. "] achieved, new BLUE coalition score is " .. cfxPlayerScore.coalitionScore[coa], 30)
|
||||
trigger.action.outSoundForCoalition(coa, cfxPlayerScore.scoreSound)
|
||||
end
|
||||
|
||||
-- bank it if exists
|
||||
local amount
|
||||
if bank and bank.addFunds then
|
||||
amount = cfxPlayerScore.score2finance * cfxPlayerScore.blueTriggerScore[tName]
|
||||
bank.addFunds(coa, amount)
|
||||
if cfxPlayerScore.announcer then
|
||||
trigger.action.outTextForCoalition(coa, "Transferred §" .. amount .. " to funds.", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1380,13 +1388,23 @@ function cfxPlayerScore.update()
|
||||
|
||||
cfxPlayerScore.coalitionScore[coa] = cfxPlayerScore.coalitionScore[coa] + cfxPlayerScore.redTriggerScore[tName]
|
||||
cfxPlayerScore.redTriggerFlags[tName] = newVal
|
||||
if bank and bank.addFunds then
|
||||
bank.addFunds(coa, cfxPlayerScore.score2finance * cfxPlayerScore.blueTriggerScore[tName])
|
||||
end
|
||||
--if bank and bank.addFunds then
|
||||
-- bank.addFunds(coa, cfxPlayerScore.score2finance * cfxPlayerScore.blueTriggerScore[tName])
|
||||
--end
|
||||
if cfxPlayerScore.announcer then
|
||||
trigger.action.outTextForCoalition(coa, "RED goal [" .. tName .. "] achieved, new RED coalition score is " .. cfxPlayerScore.coalitionScore[coa], 30)
|
||||
trigger.action.outSoundForCoalition(coa, cfxPlayerScore.scoreSound)
|
||||
end
|
||||
|
||||
-- bank it if exists
|
||||
local amount
|
||||
if bank and bank.addFunds then
|
||||
amount = cfxPlayerScore.score2finance * cfxPlayerScore.redTriggerScore[tName]
|
||||
bank.addFunds(coa, amount)
|
||||
if cfxPlayerScore.announcer then
|
||||
trigger.action.outTextForCoalition(coa, "Transferred §" .. amount .. " to funds.", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
radioMenu = {}
|
||||
radioMenu.version = "2.2.1"
|
||||
radioMenu.version = "2.3.0"
|
||||
radioMenu.verbose = false
|
||||
radioMenu.ups = 1
|
||||
radioMenu.requiredLibs = {
|
||||
@ -19,6 +19,8 @@ radioMenu.menus = {}
|
||||
2.1.1 - outMessage now works correctly
|
||||
2.2.0 - clean-up
|
||||
2.2.1 - corrected ackD
|
||||
2.3.0 - added wildcard "*" ability for group name match
|
||||
- added ackASnd .. ackDSnd sounds as options
|
||||
--]]--
|
||||
|
||||
function radioMenu.addRadioMenu(theZone)
|
||||
@ -114,7 +116,24 @@ function radioMenu.filterPlayerIDForGroup(theZone)
|
||||
end
|
||||
|
||||
for idx, gName in pairs(allGroups) do
|
||||
-- if gName ends in wildcard "*" we process differently
|
||||
gName = dcsCommon.trim(gName)
|
||||
if dcsCommon.stringEndsWith(gName, "*") then
|
||||
-- we must check all group names if they start with the
|
||||
-- the same root. WARNING: CASE-SENSITIVE!!!!
|
||||
gName = dcsCommon.removeEnding(gName, "*")
|
||||
for mxName, theGroupData in pairs(cfxMX.playerGroupByName) do
|
||||
if dcsCommon.stringStartsWith(mxName, gName) then
|
||||
-- group match, install menu
|
||||
local gID = theGroupData.groupId
|
||||
table.insert(theIDs, gID)
|
||||
if theZone.verbose or radioMenu.verbose then
|
||||
trigger.action.outText("+++menu: WILDCARD Player Group <" .. gName .. "*> matched with <" .. mxName .. ">: gID = <" .. gID .. ">", 30)
|
||||
end
|
||||
else
|
||||
end
|
||||
end
|
||||
else
|
||||
local theGroup = cfxMX.playerGroupByName[gName]
|
||||
if theGroup then
|
||||
local gID = theGroup.groupId
|
||||
@ -126,6 +145,7 @@ function radioMenu.filterPlayerIDForGroup(theZone)
|
||||
trigger.action.outText("+++menu: Player Group <" .. gName .. "> does not exist", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return theIDs
|
||||
end
|
||||
@ -272,6 +292,9 @@ function radioMenu.createRadioMenuWithZone(theZone)
|
||||
if theZone:hasProperty("ackA") then
|
||||
theZone.ackA = theZone:getStringFromZoneProperty("ackA", "Acknowledged: A")
|
||||
end
|
||||
if theZone:hasProperty("ackASnd") then
|
||||
theZone.ackASnd = theZone:getStringFromZoneProperty("ackASnd", "<none>")
|
||||
end
|
||||
|
||||
theZone.itemBChosen = theZone:getStringFromZoneProperty("B!", "*<none>")
|
||||
theZone.cooldownB = theZone:getNumberFromZoneProperty("cooldownB", 0)
|
||||
@ -282,6 +305,9 @@ function radioMenu.createRadioMenuWithZone(theZone)
|
||||
if theZone:hasProperty("ackB") then
|
||||
theZone.ackB = theZone:getStringFromZoneProperty("ackB", "Acknowledged: B")
|
||||
end
|
||||
if theZone:hasProperty("ackBSnd") then
|
||||
theZone.ackBSnd = theZone:getStringFromZoneProperty("ackBSnd", "<none>")
|
||||
end
|
||||
|
||||
theZone.itemCChosen = theZone:getStringFromZoneProperty("C!", "*<none>")
|
||||
theZone.cooldownC = theZone:getNumberFromZoneProperty("cooldownC", 0)
|
||||
@ -292,6 +318,9 @@ function radioMenu.createRadioMenuWithZone(theZone)
|
||||
if theZone:hasProperty("ackC") then
|
||||
theZone.ackC = theZone:getStringFromZoneProperty("ackC", "Acknowledged: C")
|
||||
end
|
||||
if theZone:hasProperty("ackCSnd") then
|
||||
theZone.ackCSnd = theZone:getStringFromZoneProperty("ackCSnd", "<none>")
|
||||
end
|
||||
|
||||
theZone.itemDChosen = theZone:getStringFromZoneProperty("D!", "*<none>")
|
||||
theZone.cooldownD = theZone:getNumberFromZoneProperty("cooldownD", 0)
|
||||
@ -302,6 +331,9 @@ function radioMenu.createRadioMenuWithZone(theZone)
|
||||
if theZone:hasProperty("ackD") then
|
||||
theZone.ackD = theZone:getStringFromZoneProperty("ackD", "Acknowledged: D")
|
||||
end
|
||||
if theZone:hasProperty("ackDSnd") then
|
||||
theZone.ackDSnd = theZone:getStringFromZoneProperty("ackDSnd", "<none>")
|
||||
end
|
||||
|
||||
if theZone:hasProperty("removeMenu?") then
|
||||
theZone.removeMenu = theZone:getStringFromZoneProperty( "removeMenu?", "*<none>")
|
||||
@ -395,6 +427,7 @@ function radioMenu.doMenuX(args)
|
||||
local theFlag = theZone.itemAChosen
|
||||
local outVal = theZone.outValA
|
||||
local ack = theZone.ackA
|
||||
local ackSnd = theZone.ackASnd
|
||||
|
||||
-- decode A..X
|
||||
if theItemIndex == "B"then
|
||||
@ -403,18 +436,21 @@ function radioMenu.doMenuX(args)
|
||||
theFlag = theZone.itemBChosen
|
||||
outVal = theZone.outValB
|
||||
ack = theZone.ackB
|
||||
ackSnd = theZone.ackBSnd
|
||||
elseif theItemIndex == "C" then
|
||||
cd = radioMenu.cdByGID(theZone.mcdC, theZone, theGroup) -- theZone.mcdC
|
||||
busy = theZone.busyC
|
||||
theFlag = theZone.itemCChosen
|
||||
outVal = theZone.outValC
|
||||
ack = theZone.ackC
|
||||
ackSnd = theZone.ackCSnd
|
||||
elseif theItemIndex == "D" then
|
||||
cd = radioMenu.cdByGID(theZone.mcdD, theZone, theGroup) -- theZone.mcdD
|
||||
busy = theZone.busyD
|
||||
theFlag = theZone.itemDChosen
|
||||
outVal = theZone.outValD
|
||||
ack = theZone.ackD
|
||||
ackSnd = theZone.ackDSnd
|
||||
end
|
||||
|
||||
-- see if we are on cooldown
|
||||
@ -427,10 +463,13 @@ function radioMenu.doMenuX(args)
|
||||
return
|
||||
else
|
||||
-- see if we have an acknowledge
|
||||
if ack then
|
||||
local gid = theGroup
|
||||
if ack then
|
||||
radioMenu.radioOutMsg(ack, gid, theZone)
|
||||
end
|
||||
if ackSnd then
|
||||
trigger.action.outSoundForGroup(gid, ackSnd)
|
||||
end
|
||||
end
|
||||
|
||||
-- set new cooldown -- needs own decoder A..X
|
||||
|
||||
133
modules/sweeper.lua
Normal file
133
modules/sweeper.lua
Normal file
@ -0,0 +1,133 @@
|
||||
sweeper = {}
|
||||
sweeper.version = "1.0.0"
|
||||
sweeper.requiredLibs = {
|
||||
"dcsCommon",
|
||||
"cfxZones",
|
||||
}
|
||||
-- remove all units that are detected twice in a row in the same
|
||||
-- zone after a time interval. Used to remove deadlocked units.
|
||||
|
||||
sweeper.zones = {}
|
||||
sweeper.interval = 5 * 60 -- 5 mins (max 10 mins) in zone will kill you
|
||||
sweeper.verbose = false
|
||||
sweeper.flights = {}
|
||||
|
||||
function sweeper.addSweeperZone(theZone)
|
||||
sweeper.zones[theZone.name] = theZone
|
||||
end
|
||||
|
||||
function sweeper.readSweeperZone(theZone)
|
||||
theZone.aircraft = theZone:getBoolFromZoneProperty("aircraft", true)
|
||||
theZone.helos = theZone:getBoolFromZoneProperty("helos", false)
|
||||
end
|
||||
|
||||
function sweeper.update()
|
||||
timer.scheduleFunction(sweeper.update, {}, timer.getTime() + sweeper.interval)
|
||||
local toKill = {}
|
||||
local newFlights = {}
|
||||
for idx, theZone in pairs(sweeper.zones) do
|
||||
for i= 0, 2 do
|
||||
local allGroups = coalition.getGroups(i, 2) -- get all ground
|
||||
for idy, theGroup in pairs(allGroups) do
|
||||
local allUnits = theGroup:getUnits()
|
||||
for idz, theUnit in pairs(allUnits) do
|
||||
if theZone:unitInZone(theUnit) then
|
||||
table.insert(toKill, theUnit)
|
||||
end
|
||||
end
|
||||
end
|
||||
if theZone.aircraft then
|
||||
local allGroups = coalition.getGroups(i, 0) -- get all planes
|
||||
for idy, theGroup in pairs(allGroups) do
|
||||
local allUnits = theGroup:getUnits()
|
||||
for idz, theUnit in pairs(allUnits) do
|
||||
if theZone:unitInZone(theUnit) then
|
||||
-- see if this was was already noted
|
||||
uName = theUnit:getName()
|
||||
if sweeper.flights[uName] then
|
||||
table.insert(toKill, theUnit)
|
||||
if sweeper.verbose then
|
||||
trigger.action.outText("Sweeping aircraft <" .. uName .. "> off zone for obstruction", 30)
|
||||
end
|
||||
else
|
||||
newFlights[uName] = true
|
||||
if sweeper.verbose then
|
||||
trigger.action.outText("sweep: aircraft <" .. uName .. "> on notice", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if theZone.helos then
|
||||
local allGroups = coalition.getGroups(i, 1) -- get all helos
|
||||
for idy, theGroup in pairs(allGroups) do
|
||||
local allUnits = theGroup:getUnits()
|
||||
for idz, theUnit in pairs(allUnits) do
|
||||
if theZone:unitInZone(theUnit) then
|
||||
-- see if this was was already noted
|
||||
uName = theUnit:getName()
|
||||
if sweeper.flights[uName] then
|
||||
table.insert(toKill, theUnit)
|
||||
if sweeper.verbose then
|
||||
trigger.action.outText("Sweeping helicopter <" .. uName .. "> off zone for obstruction", 30)
|
||||
end
|
||||
else
|
||||
newFlights[uName] = true
|
||||
if sweeper.verbose then
|
||||
trigger.action.outText("sweep: helicopter <" .. uName .. "> on notice", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- remove all units in my kill list
|
||||
for idx, theUnit in pairs(toKill) do
|
||||
if theUnit.getPlayerName and theUnit:getPlayerName() then
|
||||
-- we do not sweep players
|
||||
else
|
||||
if sweeper.verbose then
|
||||
trigger.action.outText("*** sweeper: sweeping <" .. theUnit:getName() .. ">", 30)
|
||||
end
|
||||
if Unit.isExist(theUnit) then Unit.destroy(theUnit) end
|
||||
end
|
||||
end
|
||||
|
||||
-- remember new list, forget old
|
||||
sweeper.flights = newFlights
|
||||
end
|
||||
|
||||
function sweeper.start()
|
||||
-- lib check
|
||||
if not dcsCommon.libCheck then
|
||||
trigger.action.outText("cfx sweeper requires dcsCommon", 30)
|
||||
return false
|
||||
end
|
||||
if not dcsCommon.libCheck("cfx sweeper", sweeper.requiredLibs) then
|
||||
return false
|
||||
end
|
||||
|
||||
-- process sweeper Zones
|
||||
local attrZones = cfxZones.getZonesWithAttributeNamed("sweeper")
|
||||
for k, aZone in pairs(attrZones) do
|
||||
sweeper.readSweeperZone(aZone) -- process attributes
|
||||
sweeper.addSweeperZone(aZone) -- add to list
|
||||
end
|
||||
|
||||
-- start update in 5 seconds
|
||||
timer.scheduleFunction(sweeper.update, {}, timer.getTime() + sweeper.interval)
|
||||
|
||||
-- say hi
|
||||
trigger.action.outText("sweeper v" .. sweeper.version .. " started.", 30)
|
||||
return true
|
||||
end
|
||||
|
||||
if not sweeper.start() then
|
||||
trigger.action.outText("sweeper failed to start.", 30)
|
||||
sweeper = nil
|
||||
end
|
||||
@ -1,10 +1,13 @@
|
||||
tacan = {}
|
||||
tacan.version = "1.1.0"
|
||||
tacan.version = "1.2.2"
|
||||
--[[--
|
||||
Version History
|
||||
1.0.0 - initial version
|
||||
1.1.0 - OOP cfxZones
|
||||
|
||||
1.2.0 - desc attribute
|
||||
1.2.1 - actionSound config attribute
|
||||
- sound with output
|
||||
1.2.2 - corrected typo when reading sound file for actionSound
|
||||
--]]--
|
||||
tacan.verbose = false
|
||||
tacan.requiredLibs = {
|
||||
@ -61,6 +64,10 @@ function tacan.createTacanZone(theZone)
|
||||
|
||||
theZone.announcer = theZone:getBoolFromZoneProperty("announcer", false)
|
||||
|
||||
if theZone:hasProperty("desc") then
|
||||
theZone.desc = theZone:getStringFromZoneProperty("desc", "<none>")
|
||||
end
|
||||
|
||||
-- interface to groupTracker
|
||||
if theZone:hasProperty("trackWith:") then
|
||||
theZone.trackWith = theZone:getStringFromZoneProperty( "trackWith:", "<None>")
|
||||
@ -138,6 +145,7 @@ function tacan.createTacanInZone(theZone, channel, mode, callsign)
|
||||
t.activeChan = channel
|
||||
t.theGroup = theGroup
|
||||
t.theData = theCopy
|
||||
t.desc = theZone.desc
|
||||
table.insert(theZone.spawnedTACANS, t)
|
||||
|
||||
-- run a GC cycle
|
||||
@ -175,8 +183,10 @@ function tacan.TacanFromZone(theZone, silent)
|
||||
local str = "NOTAM: Deployed new TACAN " .. theZone.name .. " <" .. callsign .. ">, channel " .. channel .. mode .. ", active now"
|
||||
if theZone.coa == 0 then
|
||||
trigger.action.outText(str, 30)
|
||||
trigger.action.outSound(tacan.actionSound)
|
||||
else
|
||||
trigger.action.outTextForCoalition(theZone.coa, str, 30)
|
||||
trigger.action.outSoundForCoalition(theZone.coa, tacan.actionSound)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -193,6 +203,7 @@ function tacan.destroyTacan(theZone, announce)
|
||||
trigger.action.outText(str, 30)
|
||||
else
|
||||
trigger.action.outTextForCoalition(coa, str, 30)
|
||||
trigger.action.outSoundForCoalition(coa, tacan.actionSound)
|
||||
end
|
||||
end
|
||||
|
||||
@ -331,7 +342,6 @@ end
|
||||
function tacan.installComms()
|
||||
tacan.redC = missionCommands.addCommandForCoalition(1, "Available TACAN stations", nil, tacan.listTacan, 1)
|
||||
tacan.blueC = missionCommands.addCommandForCoalition(2, "Available TACAN stations", nil, tacan.listTacan, 2)
|
||||
|
||||
end
|
||||
|
||||
function tacan.listTacan(side)
|
||||
@ -353,6 +363,7 @@ function tacan.doListTacan(args)
|
||||
|
||||
if #theTs < 1 then
|
||||
trigger.action.outTextForCoalition(args, "No active TACAN.", 30)
|
||||
trigger.action.outSoundForCoalition(args, tacan.actionSound)
|
||||
return
|
||||
end
|
||||
|
||||
@ -360,9 +371,13 @@ function tacan.doListTacan(args)
|
||||
|
||||
for idx, aTacan in pairs(theTs) do
|
||||
msg = msg .. "\n - " .. aTacan.activeCallsign .. ": " .. aTacan.activeChan .. aTacan.activeMode
|
||||
if aTacan.desc then
|
||||
msg = msg .. " - " .. aTacan.desc
|
||||
end
|
||||
end
|
||||
msg = msg .. "\n"
|
||||
trigger.action.outTextForCoalition(args, msg, 30)
|
||||
trigger.action.outSoundForCoalition(args, tacan.actionSound)
|
||||
end
|
||||
|
||||
--
|
||||
@ -379,7 +394,7 @@ function tacan.readConfigZone()
|
||||
if theZone:hasProperty("GUI") then
|
||||
tacan.list = theZone:getBoolFromZoneProperty("GUI", false)
|
||||
end
|
||||
|
||||
tacan.actionSound = theZone:getStringFromZoneProperty("actionSound", "Quest Snare 3.wav")
|
||||
if tacan.verbose then
|
||||
trigger.action.outText("+++tcn: read config", 30)
|
||||
end
|
||||
|
||||
632
modules/wingTaskHelper.lua
Normal file
632
modules/wingTaskHelper.lua
Normal file
@ -0,0 +1,632 @@
|
||||
wingTaskHelper = {}
|
||||
|
||||
-- WP 1 Task for a CAS flight
|
||||
wingTaskHelper.casTOTask = {
|
||||
["id"] = "ComboTask",
|
||||
["params"] =
|
||||
{
|
||||
["tasks"] =
|
||||
{
|
||||
[1] =
|
||||
{
|
||||
["enabled"] = true,
|
||||
["auto"] = true,
|
||||
["id"] = "EngageTargets",
|
||||
["number"] = 1,
|
||||
["key"] = "CAS",
|
||||
["params"] =
|
||||
{
|
||||
["targetTypes"] =
|
||||
{
|
||||
[1] = "Helicopters",
|
||||
[2] = "Ground Units",
|
||||
[3] = "Light armed ships",
|
||||
}, -- end of ["targetTypes"]
|
||||
["priority"] = 0,
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [1]
|
||||
[2] =
|
||||
{
|
||||
["number"] = 2,
|
||||
["auto"] = true,
|
||||
["id"] = "WrappedAction",
|
||||
["enabled"] = true,
|
||||
["params"] =
|
||||
{
|
||||
["action"] =
|
||||
{
|
||||
["id"] = "Option",
|
||||
["params"] =
|
||||
{
|
||||
["value"] = 2,
|
||||
["name"] = 1,
|
||||
}, -- end of ["params"]
|
||||
}, -- end of ["action"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [2]
|
||||
[3] =
|
||||
{
|
||||
["number"] = 3,
|
||||
["auto"] = true,
|
||||
["id"] = "WrappedAction",
|
||||
["enabled"] = true,
|
||||
["params"] =
|
||||
{
|
||||
["action"] =
|
||||
{
|
||||
["id"] = "Option",
|
||||
["params"] =
|
||||
{
|
||||
["value"] = 1,
|
||||
["name"] = 3,
|
||||
}, -- end of ["params"]
|
||||
}, -- end of ["action"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [3]
|
||||
[4] =
|
||||
{
|
||||
["number"] = 4,
|
||||
["auto"] = true,
|
||||
["id"] = "WrappedAction",
|
||||
["enabled"] = true,
|
||||
["params"] =
|
||||
{
|
||||
["action"] =
|
||||
{
|
||||
["id"] = "Option",
|
||||
["params"] =
|
||||
{
|
||||
["variantIndex"] = 2,
|
||||
["name"] = 5,
|
||||
["formationIndex"] = 2,
|
||||
["value"] = 131074, -- trail formation
|
||||
}, -- end of ["params"]
|
||||
}, -- end of ["action"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [4]
|
||||
[5] =
|
||||
{
|
||||
["number"] = 5,
|
||||
["auto"] = true,
|
||||
["id"] = "WrappedAction",
|
||||
["enabled"] = true,
|
||||
["params"] =
|
||||
{
|
||||
["action"] =
|
||||
{
|
||||
["id"] = "Option",
|
||||
["params"] =
|
||||
{
|
||||
["value"] = true,
|
||||
["name"] = 15,
|
||||
}, -- end of ["params"]
|
||||
}, -- end of ["action"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [5]
|
||||
[6] =
|
||||
{
|
||||
["number"] = 6,
|
||||
["auto"] = true,
|
||||
["id"] = "WrappedAction",
|
||||
["enabled"] = true,
|
||||
["params"] =
|
||||
{
|
||||
["action"] =
|
||||
{
|
||||
["id"] = "Option",
|
||||
["params"] =
|
||||
{
|
||||
["targetTypes"] =
|
||||
{
|
||||
}, -- end of ["targetTypes"]
|
||||
["name"] = 21,
|
||||
["value"] = "none;",
|
||||
["noTargetTypes"] =
|
||||
{
|
||||
[1] = "Fighters",
|
||||
[2] = "Multirole fighters",
|
||||
[3] = "Bombers",
|
||||
[4] = "Helicopters",
|
||||
[5] = "Infantry",
|
||||
[6] = "Fortifications",
|
||||
[7] = "Tanks",
|
||||
[8] = "IFV",
|
||||
[9] = "APC",
|
||||
[10] = "Artillery",
|
||||
[11] = "Unarmed vehicles",
|
||||
[12] = "AAA",
|
||||
[13] = "SR SAM",
|
||||
[14] = "MR SAM",
|
||||
[15] = "LR SAM",
|
||||
[16] = "Aircraft Carriers",
|
||||
[17] = "Cruisers",
|
||||
[18] = "Destroyers",
|
||||
[19] = "Frigates",
|
||||
[20] = "Corvettes",
|
||||
[21] = "Light armed ships",
|
||||
[22] = "Unarmed ships",
|
||||
[23] = "Submarines",
|
||||
[24] = "Cruise missiles",
|
||||
[25] = "Antiship Missiles",
|
||||
[26] = "AA Missiles",
|
||||
[27] = "AG Missiles",
|
||||
[28] = "SA Missiles",
|
||||
[29] = "UAVs",
|
||||
}, -- end of ["noTargetTypes"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of ["action"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [6]
|
||||
[7] =
|
||||
{
|
||||
["number"] = 7,
|
||||
["auto"] = true,
|
||||
["id"] = "WrappedAction",
|
||||
["enabled"] = true,
|
||||
["params"] =
|
||||
{
|
||||
["action"] =
|
||||
{
|
||||
["id"] = "Option",
|
||||
["params"] =
|
||||
{
|
||||
["value"] = true,
|
||||
["name"] = 19,
|
||||
}, -- end of ["params"]
|
||||
}, -- end of ["action"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [7]
|
||||
[8] =
|
||||
{
|
||||
["number"] = 8,
|
||||
["auto"] = false,
|
||||
["id"] = "EngageTargetsInZone",
|
||||
["enabled"] = true,
|
||||
["params"] =
|
||||
{
|
||||
["targetTypes"] =
|
||||
{
|
||||
[1] = "All",
|
||||
}, -- end of ["targetTypes"]
|
||||
["x"] = 19632.860434462,
|
||||
["y"] = 323453.87978006,
|
||||
["value"] = "All;",
|
||||
["noTargetTypes"] =
|
||||
{
|
||||
}, -- end of ["noTargetTypes"]
|
||||
["priority"] = 0,
|
||||
["zoneRadius"] = 76200,
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [8]
|
||||
}, -- end of ["tasks"]
|
||||
}, -- end of ["params"]
|
||||
} -- end of ["task"]
|
||||
|
||||
wingTaskHelper.capTOTask = {
|
||||
["id"] = "ComboTask",
|
||||
["params"] =
|
||||
{
|
||||
["tasks"] =
|
||||
{
|
||||
[1] =
|
||||
{
|
||||
["number"] = 1,
|
||||
["key"] = "CAP",
|
||||
["id"] = "EngageTargets",
|
||||
["enabled"] = true,
|
||||
["auto"] = true,
|
||||
["params"] =
|
||||
{
|
||||
["targetTypes"] =
|
||||
{
|
||||
[1] = "Air",
|
||||
}, -- end of ["targetTypes"]
|
||||
["priority"] = 0,
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [1]
|
||||
[2] =
|
||||
{
|
||||
["number"] = 2,
|
||||
["auto"] = true,
|
||||
["id"] = "WrappedAction",
|
||||
["enabled"] = true,
|
||||
["params"] =
|
||||
{
|
||||
["action"] =
|
||||
{
|
||||
["id"] = "Option",
|
||||
["params"] =
|
||||
{
|
||||
["value"] = true,
|
||||
["name"] = 17, -- restrict ground attack
|
||||
}, -- end of ["params"]
|
||||
}, -- end of ["action"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [2]
|
||||
[3] =
|
||||
{
|
||||
["number"] = 3,
|
||||
["auto"] = true,
|
||||
["id"] = "WrappedAction",
|
||||
["enabled"] = true,
|
||||
["params"] =
|
||||
{
|
||||
["action"] =
|
||||
{
|
||||
["id"] = "Option",
|
||||
["params"] =
|
||||
{
|
||||
["value"] = 0,
|
||||
["name"] = 18, -- max range launch
|
||||
}, -- end of ["params"]
|
||||
}, -- end of ["action"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [3]
|
||||
[4] =
|
||||
{
|
||||
["number"] = 4,
|
||||
["auto"] = true,
|
||||
["id"] = "WrappedAction",
|
||||
["enabled"] = true,
|
||||
["params"] =
|
||||
{
|
||||
["action"] =
|
||||
{
|
||||
["id"] = "Option",
|
||||
["params"] =
|
||||
{
|
||||
["value"] = true,
|
||||
["name"] = 19, -- no reporting
|
||||
}, -- end of ["params"]
|
||||
}, -- end of ["action"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [4]
|
||||
[5] =
|
||||
{
|
||||
["number"] = 5,
|
||||
["auto"] = true,
|
||||
["id"] = "WrappedAction",
|
||||
["enabled"] = true,
|
||||
["params"] =
|
||||
{
|
||||
["action"] =
|
||||
{
|
||||
["id"] = "Option",
|
||||
["params"] =
|
||||
{
|
||||
["targetTypes"] =
|
||||
{
|
||||
}, -- end of ["targetTypes"]
|
||||
["name"] = 21,
|
||||
["value"] = "none;",
|
||||
["noTargetTypes"] =
|
||||
{
|
||||
[1] = "Fighters",
|
||||
[2] = "Multirole fighters",
|
||||
[3] = "Bombers",
|
||||
[4] = "Helicopters",
|
||||
[5] = "Infantry",
|
||||
[6] = "Fortifications",
|
||||
[7] = "Tanks",
|
||||
[8] = "IFV",
|
||||
[9] = "APC",
|
||||
[10] = "Artillery",
|
||||
[11] = "Unarmed vehicles",
|
||||
[12] = "AAA",
|
||||
[13] = "SR SAM",
|
||||
[14] = "MR SAM",
|
||||
[15] = "LR SAM",
|
||||
[16] = "Aircraft Carriers",
|
||||
[17] = "Cruisers",
|
||||
[18] = "Destroyers",
|
||||
[19] = "Frigates",
|
||||
[20] = "Corvettes",
|
||||
[21] = "Light armed ships",
|
||||
[22] = "Unarmed ships",
|
||||
[23] = "Submarines",
|
||||
[24] = "Cruise missiles",
|
||||
[25] = "Antiship Missiles",
|
||||
[26] = "AA Missiles",
|
||||
[27] = "AG Missiles",
|
||||
[28] = "SA Missiles",
|
||||
[29] = "UAVs",
|
||||
}, -- end of ["noTargetTypes"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of ["action"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [5]
|
||||
[6] =
|
||||
{
|
||||
["number"] = 6,
|
||||
["auto"] = false,
|
||||
["id"] = "EngageTargetsInZone",
|
||||
["enabled"] = true,
|
||||
["params"] =
|
||||
{
|
||||
["targetTypes"] =
|
||||
{
|
||||
[1] = "Planes",
|
||||
}, -- end of ["targetTypes"]
|
||||
["x"] = -1421.6419952991,
|
||||
["y"] = 311601.25461373,
|
||||
["value"] = "Planes;",
|
||||
["noTargetTypes"] =
|
||||
{
|
||||
}, -- end of ["noTargetTypes"]
|
||||
["priority"] = 0,
|
||||
["zoneRadius"] = 76200,
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [6]
|
||||
}, -- end of ["tasks"]
|
||||
}, -- end of ["params"]
|
||||
} -- end of ["task"]
|
||||
|
||||
wingTaskHelper.seadTOTask = {
|
||||
["id"] = "ComboTask",
|
||||
["params"] =
|
||||
{
|
||||
["tasks"] =
|
||||
{
|
||||
[1] =
|
||||
{
|
||||
["number"] = 1,
|
||||
["key"] = "SEAD",
|
||||
["id"] = "EngageTargets",
|
||||
["enabled"] = true,
|
||||
["auto"] = true,
|
||||
["params"] =
|
||||
{
|
||||
["targetTypes"] =
|
||||
{
|
||||
[1] = "Air Defence",
|
||||
}, -- end of ["targetTypes"]
|
||||
["priority"] = 0,
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [1]
|
||||
[2] =
|
||||
{
|
||||
["number"] = 2,
|
||||
["auto"] = true,
|
||||
["id"] = "WrappedAction",
|
||||
["enabled"] = true,
|
||||
["params"] =
|
||||
{
|
||||
["action"] =
|
||||
{
|
||||
["id"] = "Option",
|
||||
["params"] =
|
||||
{
|
||||
["value"] = 2,
|
||||
["name"] = 1,
|
||||
}, -- end of ["params"]
|
||||
}, -- end of ["action"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [2]
|
||||
[3] =
|
||||
{
|
||||
["number"] = 3,
|
||||
["auto"] = true,
|
||||
["id"] = "WrappedAction",
|
||||
["enabled"] = true,
|
||||
["params"] =
|
||||
{
|
||||
["action"] =
|
||||
{
|
||||
["id"] = "Option",
|
||||
["params"] =
|
||||
{
|
||||
["value"] = 2,
|
||||
["name"] = 13,
|
||||
}, -- end of ["params"]
|
||||
}, -- end of ["action"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [3]
|
||||
[4] =
|
||||
{
|
||||
["number"] = 4,
|
||||
["auto"] = true,
|
||||
["id"] = "WrappedAction",
|
||||
["enabled"] = true,
|
||||
["params"] =
|
||||
{
|
||||
["action"] =
|
||||
{
|
||||
["id"] = "Option",
|
||||
["params"] =
|
||||
{
|
||||
["value"] = true,
|
||||
["name"] = 19,
|
||||
}, -- end of ["params"]
|
||||
}, -- end of ["action"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [4]
|
||||
[5] =
|
||||
{
|
||||
["number"] = 5,
|
||||
["auto"] = true,
|
||||
["id"] = "WrappedAction",
|
||||
["enabled"] = true,
|
||||
["params"] =
|
||||
{
|
||||
["action"] =
|
||||
{
|
||||
["id"] = "Option",
|
||||
["params"] =
|
||||
{
|
||||
["targetTypes"] =
|
||||
{
|
||||
[1] = "Air Defence",
|
||||
}, -- end of ["targetTypes"]
|
||||
["name"] = 21,
|
||||
["value"] = "Air Defence;",
|
||||
["noTargetTypes"] =
|
||||
{
|
||||
[1] = "Fighters",
|
||||
[2] = "Multirole fighters",
|
||||
[3] = "Bombers",
|
||||
[4] = "Helicopters",
|
||||
[5] = "Infantry",
|
||||
[6] = "Fortifications",
|
||||
[7] = "Tanks",
|
||||
[8] = "IFV",
|
||||
[9] = "APC",
|
||||
[10] = "Artillery",
|
||||
[11] = "Unarmed vehicles",
|
||||
[12] = "Aircraft Carriers",
|
||||
[13] = "Cruisers",
|
||||
[14] = "Destroyers",
|
||||
[15] = "Frigates",
|
||||
[16] = "Corvettes",
|
||||
[17] = "Light armed ships",
|
||||
[18] = "Unarmed ships",
|
||||
[19] = "Submarines",
|
||||
[20] = "Cruise missiles",
|
||||
[21] = "Antiship Missiles",
|
||||
[22] = "AA Missiles",
|
||||
[23] = "AG Missiles",
|
||||
[24] = "SA Missiles",
|
||||
[25] = "UAVs",
|
||||
}, -- end of ["noTargetTypes"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of ["action"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [5]
|
||||
[6] =
|
||||
{
|
||||
["number"] = 6,
|
||||
["auto"] = true,
|
||||
["id"] = "WrappedAction",
|
||||
["enabled"] = true,
|
||||
["params"] =
|
||||
{
|
||||
["action"] =
|
||||
{
|
||||
["id"] = "EPLRS",
|
||||
["params"] =
|
||||
{
|
||||
["value"] = true,
|
||||
["groupId"] = 2,
|
||||
}, -- end of ["params"]
|
||||
}, -- end of ["action"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [6]
|
||||
[7] =
|
||||
{
|
||||
["number"] = 7,
|
||||
["auto"] = false,
|
||||
["id"] = "WrappedAction",
|
||||
["enabled"] = true,
|
||||
["params"] =
|
||||
{
|
||||
["action"] =
|
||||
{
|
||||
["id"] = "Option",
|
||||
["params"] =
|
||||
{
|
||||
["value"] = true,
|
||||
["name"] = 15,
|
||||
}, -- end of ["params"]
|
||||
}, -- end of ["action"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [7]
|
||||
}, -- end of ["tasks"]
|
||||
}, -- end of ["params"]
|
||||
} -- end of ["task"]
|
||||
|
||||
wingTaskHelper.bombTOTask = {
|
||||
["id"] = "ComboTask",
|
||||
["params"] =
|
||||
{
|
||||
["tasks"] =
|
||||
{
|
||||
[1] =
|
||||
{
|
||||
["number"] = 1,
|
||||
["auto"] = true,
|
||||
["id"] = "WrappedAction",
|
||||
["enabled"] = true,
|
||||
["params"] =
|
||||
{
|
||||
["action"] =
|
||||
{
|
||||
["id"] = "Option",
|
||||
["params"] =
|
||||
{
|
||||
["value"] = 2,
|
||||
["name"] = 1,
|
||||
}, -- end of ["params"]
|
||||
}, -- end of ["action"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [1]
|
||||
[2] =
|
||||
{
|
||||
["number"] = 2,
|
||||
["auto"] = true,
|
||||
["id"] = "WrappedAction",
|
||||
["enabled"] = true,
|
||||
["params"] =
|
||||
{
|
||||
["action"] =
|
||||
{
|
||||
["id"] = "Option",
|
||||
["params"] =
|
||||
{
|
||||
["value"] = true,
|
||||
["name"] = 15,
|
||||
}, -- end of ["params"]
|
||||
}, -- end of ["action"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [2]
|
||||
[3] =
|
||||
{
|
||||
["number"] = 3,
|
||||
["auto"] = true,
|
||||
["id"] = "WrappedAction",
|
||||
["enabled"] = true,
|
||||
["params"] =
|
||||
{
|
||||
["action"] =
|
||||
{
|
||||
["id"] = "EPLRS",
|
||||
["params"] =
|
||||
{
|
||||
["value"] = true,
|
||||
["groupId"] = 3,
|
||||
}, -- end of ["params"]
|
||||
}, -- end of ["action"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [3]
|
||||
}, -- end of ["tasks"]
|
||||
}, -- end of ["params"]
|
||||
} -- end of ["task"]
|
||||
|
||||
wingTaskHelper.bombActionTask = {
|
||||
["id"] = "ComboTask",
|
||||
["params"] =
|
||||
{
|
||||
["tasks"] =
|
||||
{
|
||||
[1] =
|
||||
{
|
||||
["number"] = 1,
|
||||
["auto"] = false,
|
||||
["id"] = "CarpetBombing",
|
||||
["enabled"] = true,
|
||||
["params"] =
|
||||
{
|
||||
["attackType"] = "Carpet",
|
||||
["attackQtyLimit"] = false,
|
||||
["attackQty"] = 1,
|
||||
["expend"] = "All", -- yay!
|
||||
["altitude"] = 7620,
|
||||
["x"] = -7222.3894291577,
|
||||
["carpetLength"] = 500,
|
||||
["y"] = 294267.9527197,
|
||||
["altitudeEnabled"] = false,
|
||||
["weaponType"] = 9663676414,
|
||||
["groupAttack"] = false,
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [1]
|
||||
}, -- end of ["tasks"]
|
||||
}, -- end of ["params"]
|
||||
} -- end of ["task"]
|
||||
Loading…
x
Reference in New Issue
Block a user