mirror of
https://github.com/weyne85/DML.git
synced 2025-10-29 16:57:49 +00:00
Version 2.4.1
jtacGrpUI, lase code and CA integration. Fogger supports local alt
This commit is contained in:
parent
2274ba930d
commit
4e78dfcb65
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -1,11 +1,11 @@
|
||||
cfxZones = {}
|
||||
cfxZones.version = "4.5.0"
|
||||
cfxZones.version = "4.5.1"
|
||||
|
||||
-- cf/x zone management module
|
||||
-- reads dcs zones and makes them accessible and mutable
|
||||
-- by scripting.
|
||||
--
|
||||
-- Copyright (c) 2021 - 2024 by Christian Franz and cf/x AG
|
||||
-- Copyright (c) 2021 - 2025 by Christian Franz and cf/x AG
|
||||
--
|
||||
|
||||
--[[-- VERSION HISTORY
|
||||
@ -39,6 +39,7 @@ cfxZones.version = "4.5.0"
|
||||
-4.5.0 - corrected bug in getBoolFromZoneProperty for default = false and "rnd"
|
||||
- rnd in bool can have = xxx param for percentage
|
||||
- getSmokeColorNumberFromZoneProperty()
|
||||
-4.5.1 - moved processSimpleZoneDynamics to common
|
||||
|
||||
--]]--
|
||||
|
||||
@ -2934,6 +2935,9 @@ end
|
||||
|
||||
-- process <t>, <lat>, <lon>, <ele>, <mgrs>
|
||||
function cfxZones.processSimpleZoneDynamics(inMsg, theZone, timeFormat, imperialUnits)
|
||||
local p = theZone:getPoint()
|
||||
return dcsCommon.processTimeLocWildCards(inMsg, p, timeFormat, imperialUnits)
|
||||
--[[--
|
||||
if not inMsg then return "<nil inMsg>" end
|
||||
-- replace <t> with current mission time HMS
|
||||
local absSecs = timer.getAbsTime()-- + env.mission.start_time
|
||||
@ -2962,6 +2966,7 @@ function cfxZones.processSimpleZoneDynamics(inMsg, theZone, timeFormat, imperial
|
||||
local mgrs = grid.UTMZone .. ' ' .. grid.MGRSDigraph .. ' ' .. grid.Easting .. ' ' .. grid.Northing
|
||||
outMsg = outMsg:gsub("<mgrs>", mgrs)
|
||||
return outMsg
|
||||
--]]--
|
||||
end
|
||||
|
||||
-- process <v: flag>, <rsp: flag> <rrnd>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cloneZones = {}
|
||||
cloneZones.version = "2.5.0"
|
||||
cloneZones.version = "2.5.2"
|
||||
cloneZones.verbose = false
|
||||
cloneZones.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
@ -60,6 +60,7 @@ cloneZones.respawnOnGroupID = true
|
||||
2.5.0 - re-establish spawn zone in persistence to provide
|
||||
empty! detection through saves (missed hasClones)
|
||||
2.5.1 - f? and in? put on notice for depreciation
|
||||
2.5.2 - removed bug when checking damaged! and no units cloned
|
||||
--]]--
|
||||
|
||||
--
|
||||
@ -1715,9 +1716,10 @@ function cloneZones.update()
|
||||
end
|
||||
|
||||
-- handling of damaged! and #health
|
||||
if aZone.damaged or aZone.health then
|
||||
if aZone.hasClones and (aZone.damaged or aZone.health) then
|
||||
-- calculate current health
|
||||
local currSize = cloneZones.countLiveAIUnits(aZone)
|
||||
if not aZone.oSize then aZone.oSize = 0 end
|
||||
if aZone.oSize < 1 then
|
||||
if aZone.verbose or cloneZones.verbose then
|
||||
trigger.action.outText("+++clnZ: Warning: zero oZize for cloner <" .. aZone.name .. ">, no health info, no damage alert", 30)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
dcsCommon = {}
|
||||
dcsCommon.version = "3.1.5"
|
||||
dcsCommon.version = "3.2.0"
|
||||
--[[-- VERSION HISTORY
|
||||
3.0.0 - removed bad bug in stringStartsWith, only relevant if caseSensitive is false
|
||||
- point2text new intsOnly option
|
||||
@ -34,12 +34,16 @@ dcsCommon.version = "3.1.5"
|
||||
3.1.4 - new processStringWildcardsForUnit
|
||||
- integrated into std wildcard proccing, unit optional
|
||||
3.1.5 - more verbosity on unitID2X
|
||||
|
||||
3.2.0 - support for twn in processStringWildcardsForUnit()
|
||||
- new processTimeLocWildCards()
|
||||
- cfxZones links to processTimeLocWildCards
|
||||
- new processAtoBWildCards()
|
||||
- tons of new wildcards like <eleft>
|
||||
--]]--
|
||||
|
||||
-- dcsCommon is a library of common lua functions
|
||||
-- for easy access and simple mission programming
|
||||
-- (c) 2021 - 2024 by Christian Franz and cf/x AG
|
||||
-- (c) 2021 - 2025 by Christian Franz and cf/x AG
|
||||
|
||||
--
|
||||
-- DCS API PATCHES FOR DCS-INTERNAL BUGS
|
||||
@ -3396,7 +3400,7 @@ function dcsCommon.LSR(a, num)
|
||||
end
|
||||
|
||||
--
|
||||
-- string wildcards
|
||||
-- string wildcard processing
|
||||
--
|
||||
function dcsCommon.processStringWildcards(inMsg, theUnit)
|
||||
-- Replace STATIC bits of message like CR and zone name
|
||||
@ -3414,18 +3418,17 @@ function dcsCommon.processStringWildcards(inMsg, theUnit)
|
||||
|
||||
return outMsg
|
||||
end
|
||||
|
||||
-- <u>, <p>, <g>, <typ>, <c>, <e>, <twn>, <twnkm>, <twnnm>
|
||||
function dcsCommon.processStringWildcardsForUnit(msg, theUnit)
|
||||
local uName = theUnit:getName()
|
||||
msg = msg:gsub("<u>", uName)
|
||||
pName = "!AI!"
|
||||
pName = "AI"
|
||||
if dcsCommon.isPlayerUnit(theUnit) then
|
||||
pName = theUnit:getPlayerName()
|
||||
else
|
||||
return
|
||||
end
|
||||
msg = msg:gsub("<p>", pName)
|
||||
msg = msg:gsub("<t>", theUnit:getTypeName())
|
||||
msg = msg:gsub("<t>", theUnit:getTypeName()) -- WARNING! <t> is conflicted
|
||||
msg = msg:gsub("<typ>", theUnit:getTypeName())
|
||||
local theGroup = theUnit:getGroup()
|
||||
local gName = theGroup:getName()
|
||||
msg = msg:gsub("<g>", gName)
|
||||
@ -3439,9 +3442,73 @@ function dcsCommon.processStringWildcardsForUnit(msg, theUnit)
|
||||
e = e:lower()
|
||||
msg = msg:gsub("<c>", coa)
|
||||
msg = msg:gsub ("<e>", e)
|
||||
local locString = ""
|
||||
local locStingnm = ""
|
||||
local p = theUnit:getPoint()
|
||||
if twn and towns then locString = " " .. twn.closestTownTo(p) end
|
||||
msg = msg:gsub("<twn>", locString)
|
||||
if twn and towns then
|
||||
local name, data, dist = twn.closestTownTo(p)
|
||||
local mdist= dist * 0.539957
|
||||
dist = math.floor(dist/100) / 10
|
||||
mdist = math.floor(mdist/100) / 10
|
||||
local bear = dcsCommon.compassPositionOfARelativeToB(p, data.p)
|
||||
locString = " " .. dist .. "km " .. bear .. " of " .. name
|
||||
locStringnm = " " .. mdist .."nm " .. bear .. " of " .. name
|
||||
end
|
||||
msg = msg:gsub("<twnkm>", locString)
|
||||
msg = msg:gsub("<twnnm>", locString)
|
||||
return msg
|
||||
end
|
||||
|
||||
-- process <tme>, <t>, <lat>, <lon>, <ele>, <mgrs>
|
||||
function dcsCommon.processTimeLocWildCards(inMsg, p, timeFormat, imperialUnits)
|
||||
if not inMsg then return "<nil inMsg tloc>" end
|
||||
-- replace <t> with current mission time HMS
|
||||
local absSecs = timer.getAbsTime()-- + env.mission.start_time
|
||||
while absSecs > 86400 do
|
||||
absSecs = absSecs - 86400 -- subtract out all days
|
||||
end
|
||||
if not timeFormat then timeFormat = "<:h>:<:m>:<:s>" end
|
||||
local timeString = dcsCommon.processHMS(timeFormat, absSecs)
|
||||
local outMsg = inMsg:gsub("<tme>", timeString)
|
||||
local outMsg = inMsg:gsub("<t>", timeString) -- WARNING! <t> is conflicted
|
||||
-- replace <lat> with lat and <lon> with lon of point
|
||||
-- and <mgrs> with mgrs coords of point
|
||||
local currPoint = p
|
||||
local lat, lon = coord.LOtoLL(currPoint)
|
||||
lat, lon = dcsCommon.latLon2Text(lat, lon)
|
||||
local alt = land.getHeight({x = currPoint.x, y = currPoint.z})
|
||||
outMsg = outMsg:gsub("<elem>", alt)
|
||||
local altft = math.floor(alt * 3.28084) -- feet
|
||||
if imperialUnits then
|
||||
alt = math.floor(alt * 3.28084) -- feet
|
||||
else
|
||||
alt = math.floor(alt) -- meters
|
||||
end
|
||||
outMsg = outMsg:gsub("<lat>", lat)
|
||||
outMsg = outMsg:gsub("<lon>", lon)
|
||||
outMsg = outMsg:gsub("<ele>", alt)
|
||||
outMsg = outMsg:gsub("<eleft>", alt)
|
||||
local grid = coord.LLtoMGRS(coord.LOtoLL(currPoint))
|
||||
local mgrs = grid.UTMZone .. ' ' .. grid.MGRSDigraph .. ' ' .. grid.Easting .. ' ' .. grid.Northing
|
||||
outMsg = outMsg:gsub("<mgrs>", mgrs)
|
||||
return outMsg
|
||||
end
|
||||
|
||||
-- process A to B: <rng>, <rngnm>, <bea>
|
||||
function dcsCommon.processAtoBWildCards(inMsg, A, B)
|
||||
if not inMsg then return "<nil inMsg AtB>" end
|
||||
local outMsg = inMsg
|
||||
local bea = dcsCommon.bearingInDegreesFromAtoB(A, B)
|
||||
local range = dcsCommon.dist(A, B) / 1000 -- km
|
||||
local rangenm = math.floor(range * 5.39957) / 10
|
||||
range = math.floor(range * 10) / 10
|
||||
outMsg = outMsg:gsub("<bea>", bea)
|
||||
outMsg = outMsg:gsub("<rng>", range)
|
||||
outMsg = outMsg:gsub("<rngnm>", rangenm)
|
||||
return outMsg
|
||||
end
|
||||
--
|
||||
-- phonetic alphabet
|
||||
--
|
||||
|
||||
@ -7,7 +7,7 @@ fogger.requiredLibs = {
|
||||
fogger.zones = {}
|
||||
|
||||
--[[-- Version history
|
||||
A DML module (c) 2024 by Christian FRanz
|
||||
A DML module (c) 2024-25 by Christian FRanz
|
||||
|
||||
- 1.0.0 - Initial version
|
||||
- 1.1.0 - added lcl attribute
|
||||
@ -27,18 +27,13 @@ function fogger.createFogZone(theZone)
|
||||
theZone.lcl = theZone:getBoolFromZoneProperty("lcl", false)
|
||||
theZone.durMin, theZone.durMax = theZone:getPositiveRangeFromZoneProperty ("duration", 1, 1)
|
||||
if theZone:hasProperty("onStart") then
|
||||
--trigger.action.outText("+++fog: zone <" .. theZone.name .. "> HAS 'onStart' attribute", 30)
|
||||
theZone.onStart = theZone:getBoolFromZoneProperty("onStart", false)
|
||||
if theZone.onStart then
|
||||
if theZone.verbose then
|
||||
trigger.action.outText("+++fog: will schedule onStart fog in zone <" .. theZone.name .. ">", 30)
|
||||
end
|
||||
timer.scheduleFunction(fogger.doFog, theZone, timer.getTime() + 0.5)
|
||||
else
|
||||
--trigger.action.outText("+++ fog: onstart turned OFF", 30)
|
||||
end
|
||||
else
|
||||
--trigger.action.outText("+++fog: zone <" .. theZone.name .. "> no 'onStart' attribute, turned off", 30)
|
||||
end
|
||||
if theZone.verbose then
|
||||
trigger.action.outText("+++fog: zone <" .. theZone.name .. "> processed.", 30)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxGroundTroops = {}
|
||||
cfxGroundTroops.version = "2.2.1"
|
||||
cfxGroundTroops.version = "3.0.0"
|
||||
cfxGroundTroops.ups = 0.25 -- every 4 seconds
|
||||
cfxGroundTroops.verbose = false
|
||||
cfxGroundTroops.requiredLibs = {
|
||||
@ -26,15 +26,8 @@ cfxGroundTroops.jtacCB = {} -- jtac callbacks, to be implemented
|
||||
|
||||
--[[--
|
||||
version history
|
||||
|
||||
2.0.0 - dmlZones
|
||||
- jtacSound
|
||||
- cleanup
|
||||
- jtacVerbose
|
||||
2.0.1 - small fiex ti checkPileUp()
|
||||
2.1.0 - captureandhold - oneshot attackowned
|
||||
2.2.0 - moveFormation support
|
||||
2.2.1 - reduced verbosity
|
||||
3.0.0 - support for troop-individual laser codes
|
||||
- support for isDrivable CA troop spawns
|
||||
|
||||
an entry into the deployed troop table has the following attributes
|
||||
- group - the group
|
||||
@ -55,8 +48,10 @@ cfxGroundTroops.jtacCB = {} -- jtac callbacks, to be implemented
|
||||
- signature - "cfx" to tell apart from dcs groups
|
||||
- range = range to look for enemies. default is 300m. In "laze" orders, range to laze
|
||||
- lazeTarget - target currently lazing
|
||||
- lazeCode - laser code. default is 1688
|
||||
- ??lazeCode?? - laser code. default is 1688
|
||||
- moving - has been given orders to move somewhere already. used for first movement order with attack orders
|
||||
- code - laser code
|
||||
- canDrive - for spawning if CA supported
|
||||
-- reduced ups to 0.24, updating troops every 4 seconds is fast enough
|
||||
|
||||
|
||||
@ -469,11 +464,13 @@ function cfxGroundTroops.trackLazer(troop)
|
||||
end
|
||||
|
||||
if not troop.lazerPointer then
|
||||
local code = troop.code
|
||||
if not code then code = cfxGroundTroops.laseCode end -- default
|
||||
local there = troop.lazeTarget:getPoint()
|
||||
troop.lazerPointer = Spot.createLaser(troop.lazingUnit,{x = 0, y = 2, z = 0}, there, cfxGroundTroops.laseCode)
|
||||
troop.lazerPointer = Spot.createLaser(troop.lazingUnit,{x = 0, y = 2, z = 0}, there, code)
|
||||
troop.lazeTargetType = troop.lazeTarget:getTypeName()
|
||||
if cfxGroundTroops.jtacVerbose then
|
||||
trigger.action.outTextForCoalition(troop.side, troop.name .. " tally target - lasing " .. troop.lazeTargetType .. ", code " .. cfxGroundTroops.laseCode .. "!", 30)
|
||||
trigger.action.outTextForCoalition(troop.side, troop.name .. " tally target - lasing " .. troop.lazeTargetType .. ", code " .. code .. "!", 30)
|
||||
trigger.action.outSoundForCoalition(troop.side, cfxGroundTroops.jtacSound) -- "UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav")
|
||||
end
|
||||
troop.lastLazerSpot = there -- remember last spot
|
||||
@ -918,7 +915,7 @@ end
|
||||
-- createGroundTroop
|
||||
-- use this to create a cfxGroundTroops from a dcs group
|
||||
--
|
||||
function cfxGroundTroops.createGroundTroops(inGroup, range, orders, moveFormation)
|
||||
function cfxGroundTroops.createGroundTroops(inGroup, range, orders, moveFormation, code, canDrive)
|
||||
local newTroops = {}
|
||||
if not orders then
|
||||
orders = "guard"
|
||||
@ -927,7 +924,6 @@ function cfxGroundTroops.createGroundTroops(inGroup, range, orders, moveFormatio
|
||||
if orders:lower() == "lase" then
|
||||
orders = "laze" -- we use WRONG spelling here, cause we're cool. yeah, right.
|
||||
end
|
||||
-- trigger.action.outText("Enter createGT group <" .. inGroup:getName() .. "> with o=<" .. orders .. ">, mf=<" .. moveFormation .. ">", 30)
|
||||
newTroops.insideDestination = false
|
||||
newTroops.unscheduleCount = 0 -- will count up as we aren't scheduled
|
||||
newTroops.speedWarning = 0
|
||||
@ -942,6 +938,10 @@ function cfxGroundTroops.createGroundTroops(inGroup, range, orders, moveFormatio
|
||||
newTroops.signature = "cfx" -- to verify this is groundTroop group, not dcs groups
|
||||
if not range then range = 300 end
|
||||
newTroops.range = range
|
||||
if not code then code = cfxGroundTroops.laseCode end
|
||||
newTroops.code = code
|
||||
newTroops.canDrive = canDrive
|
||||
-- trigger.action.outText("createGndT with code = <" .. code .. ">", 30)
|
||||
return newTroops
|
||||
end
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxHeloTroops = {}
|
||||
cfxHeloTroops.version = "4.0.0"
|
||||
cfxHeloTroops.version = "4.2.0"
|
||||
cfxHeloTroops.verbose = false
|
||||
cfxHeloTroops.autoDrop = true
|
||||
cfxHeloTroops.autoPickup = false
|
||||
@ -8,28 +8,20 @@ cfxHeloTroops.requestRange = 500 -- meters
|
||||
--
|
||||
--[[--
|
||||
VERSION HISTORY
|
||||
3.0.0 - added requestable cloner support
|
||||
- harmonized spawning invocations across cloners and spawners
|
||||
- dmlZones
|
||||
- requestRange attribute
|
||||
3.0.1 - fixed a bug with legalTroops attribute
|
||||
3.0.2 - fixed a typo in in-air menu
|
||||
3.0.3 - pointInZone check for insertion rather than radius
|
||||
3.0.4 - also handles picking up troops with orders "captureandhold"
|
||||
3.0.5 - worked around a new issues accessing a unit's name
|
||||
3.1.0 - compatible with DCS 2.9.6 dynamic spawning
|
||||
3.1.1 - deployTroopsFromHelicopter() captureandhold
|
||||
3.1.2 - doLoadGroup - test if group is still alive edge case handling
|
||||
3.1.3 - decycled structures (destination zone) on save
|
||||
- upcycled structures (destination) on load
|
||||
- loadSound and disembarkSound
|
||||
3.1.4 - guarding destination access in save
|
||||
3.1.5 - more guarding of destination access
|
||||
4.0.0 - added dropZones
|
||||
- enforceDropZones
|
||||
- coalition for drop zones
|
||||
4.1.0 - troops dropped in dropZones with active autodespawn are
|
||||
filtered from load menu
|
||||
- updated eventhandler to new events and unitLost
|
||||
- timeStamp to avoid double-dipping
|
||||
- auto-pickup restricted as well
|
||||
- code cleanup
|
||||
4.2.0 - support for individual lase codes
|
||||
- support for drivable
|
||||
|
||||
--]]--
|
||||
|
||||
cfxHeloTroops.minTime = 3 -- seconds beween tandings
|
||||
|
||||
cfxHeloTroops.requiredLibs = {
|
||||
"dcsCommon", -- common is of course needed for everything
|
||||
@ -40,7 +32,7 @@ cfxHeloTroops.requiredLibs = {
|
||||
|
||||
cfxHeloTroops.unitConfigs = {} -- all configs are stored by unit's name
|
||||
cfxHeloTroops.troopWeight = 100 -- kg average weight per trooper
|
||||
cfxHeloTroops.dropZones = {}
|
||||
cfxHeloTroops.dropZones = {} -- dict
|
||||
|
||||
-- persistence support
|
||||
cfxHeloTroops.deployedTroops = {}
|
||||
@ -70,18 +62,17 @@ function cfxHeloTroops.resetConfig(conf)
|
||||
conf.troopsOnBoard = {} -- table with the following
|
||||
conf.troopsOnBoard.name = "***reset***"
|
||||
conf.dropFormation = "circle_out" -- may be chosen later?
|
||||
conf.timeStamp = timer.getTime() -- to avoid double-dipping
|
||||
end
|
||||
|
||||
function cfxHeloTroops.createDefaultConfig(theUnit)
|
||||
local conf = {}
|
||||
cfxHeloTroops.resetConfig(conf)
|
||||
|
||||
conf.myMainMenu = nil -- this is where the main menu for group will be stored
|
||||
conf.myCommands = nil -- this is where we put all teh commands in
|
||||
return conf
|
||||
end
|
||||
|
||||
|
||||
function cfxHeloTroops.getUnitConfig(theUnit) -- will create new config if not existing
|
||||
if not theUnit then
|
||||
trigger.action.outText("+++WARNING: nil unit in get config!", 30)
|
||||
@ -99,11 +90,10 @@ function cfxHeloTroops.getConfigForUnitNamed(aName)
|
||||
return cfxHeloTroops.unitConfigs[aName]
|
||||
end
|
||||
|
||||
--
|
||||
--
|
||||
-- LANDED
|
||||
--
|
||||
--
|
||||
|
||||
function cfxHeloTroops.loadClosestGroup(conf)
|
||||
local p = conf.unit:getPosition().p
|
||||
local cat = Group.Category.GROUND
|
||||
@ -113,6 +103,11 @@ function cfxHeloTroops.loadClosestGroup(conf)
|
||||
-- for now we only load troops with legal type strings
|
||||
unitsToLoad = cfxHeloTroops.filterTroopsByType(unitsToLoad)
|
||||
|
||||
-- filter all groups that are inside a dropZone with a
|
||||
-- positive autoDespawn attribute
|
||||
local mySide = conf.unit:getCoalition()
|
||||
unitsToLoad = cfxHeloTroops.filterTroopsFromDropZones(unitsToLoad, mySide)
|
||||
|
||||
-- now limit the options to the five closest legal groups
|
||||
local numUnits = #unitsToLoad
|
||||
if numUnits < 1 then return false end -- on false will drop through
|
||||
@ -127,23 +122,32 @@ end
|
||||
function cfxHeloTroops.heloLanded(theUnit)
|
||||
-- when we have landed,
|
||||
if not dcsCommon.isTroopCarrier(theUnit, cfxHeloTroops.troopCarriers) then return end
|
||||
|
||||
local conf = cfxHeloTroops.getUnitConfig(theUnit)
|
||||
-- prevent double-dipping on land and depart
|
||||
local now = timer.getTime()
|
||||
local diff = now - conf.timeStamp
|
||||
if diff < cfxHeloTroops.minTime then
|
||||
if cfxHeloTroops.verbose then
|
||||
trigger.action.outText("+++heloT-heloLanded: filtered for time restraint <" .. diff .. ">", 30)
|
||||
end
|
||||
return
|
||||
end
|
||||
if cfxHeloTroops.verbose then
|
||||
trigger.action.outText("+++heloT-heloLanded: resetting timeStamp for delta <" .. diff .. ">", 30)
|
||||
end
|
||||
conf.timeStamp = now
|
||||
|
||||
conf.unit = theUnit
|
||||
conf.currentState = 0
|
||||
|
||||
-- we look if we auto-unload
|
||||
-- auto-unload
|
||||
if conf.autoDrop then
|
||||
if conf.troopsOnBoardNum > 0 then
|
||||
cfxHeloTroops.doDeployTroops({conf, "autodrop"})
|
||||
-- already called set menu, can exit directly
|
||||
-- doDeployTroops() invokes set menu and empties troopsOnBoard
|
||||
return
|
||||
end
|
||||
-- when we get here, we have no troops to drop on board
|
||||
-- so nothing to do really except look if we can pick up troops
|
||||
-- set menu will do that for us
|
||||
-- no troops to drop on board
|
||||
end
|
||||
|
||||
if conf.autoPickup then
|
||||
if conf.troopsOnBoardNum < 1 then
|
||||
-- load the closest group
|
||||
@ -152,49 +156,46 @@ function cfxHeloTroops.heloLanded(theUnit)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- when we get here, we simply set the newest menus and are done
|
||||
-- reset menu
|
||||
cfxHeloTroops.removeComms(conf.unit)
|
||||
cfxHeloTroops.setCommsMenu(conf.unit)
|
||||
end
|
||||
|
||||
--
|
||||
--
|
||||
-- Helo took off
|
||||
--
|
||||
--
|
||||
|
||||
function cfxHeloTroops.heloDeparted(theUnit)
|
||||
if not dcsCommon.isTroopCarrier(theUnit, cfxHeloTroops.troopCarriers) then return end
|
||||
|
||||
-- when we take off, all that needs to be done is to change the state
|
||||
-- to airborne, and then set the status flag
|
||||
-- change the state to airborne, and update menus
|
||||
local conf = cfxHeloTroops.getUnitConfig(theUnit)
|
||||
-- prevent double-dipping on land and depart
|
||||
local now = timer.getTime()
|
||||
local diff = now - conf.timeStamp
|
||||
if cfxHeloTroops.verbose then
|
||||
trigger.action.outText("+++heloT-heloDeparted: resetting timeStamp for delta <" .. diff .. ">", 30)
|
||||
end
|
||||
conf.timeStamp = now
|
||||
conf.currentState = 1 -- in the air
|
||||
|
||||
cfxHeloTroops.removeComms(conf.unit)
|
||||
cfxHeloTroops.setCommsMenu(conf.unit)
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
--
|
||||
-- Helo Crashed
|
||||
--
|
||||
--
|
||||
|
||||
function cfxHeloTroops.cleanHelo(theUnit)
|
||||
-- clean up
|
||||
local conf = cfxHeloTroops.getUnitConfig(theUnit)
|
||||
conf.unit = theUnit
|
||||
conf.troopsOnBoardNum = 0 -- all dead
|
||||
conf.currentState = -1 -- (we don't know)
|
||||
|
||||
-- check if we need to interface with groupTracker
|
||||
if conf.troopsOnBoard.name and groupTracker then
|
||||
local theName = conf.troopsOnBoard.name
|
||||
-- there was (possibly) a group on board. see if it was tracked
|
||||
local isTracking, numTracking, trackers = groupTracker.groupNameTrackedBy(theName)
|
||||
|
||||
-- if so, remove it from limbo
|
||||
if isTracking then
|
||||
for idx, theTracker in pairs(trackers) do
|
||||
@ -215,11 +216,10 @@ function cfxHeloTroops.heloCrashed(theUnit)
|
||||
cfxHeloTroops.cleanHelo(theUnit)
|
||||
end
|
||||
|
||||
--
|
||||
--
|
||||
-- M E N U H A N D L I N G & R E S P O N S E
|
||||
--
|
||||
--
|
||||
|
||||
function cfxHeloTroops.clearCommsSubmenus(conf)
|
||||
if conf.myCommands then
|
||||
for i=1, #conf.myCommands do
|
||||
@ -231,7 +231,6 @@ end
|
||||
|
||||
function cfxHeloTroops.removeCommsFromConfig(conf)
|
||||
cfxHeloTroops.clearCommsSubmenus(conf)
|
||||
|
||||
if conf.myMainMenu then
|
||||
missionCommands.removeItemForGroup(conf.id, conf.myMainMenu)
|
||||
conf.myMainMenu = nil
|
||||
@ -241,20 +240,16 @@ end
|
||||
function cfxHeloTroops.removeComms(theUnit)
|
||||
if not theUnit then return end
|
||||
if not theUnit:isExist() then return end
|
||||
|
||||
local group = theUnit:getGroup()
|
||||
local id = group:getID()
|
||||
local conf = cfxHeloTroops.getUnitConfig(theUnit)
|
||||
conf.id = id
|
||||
conf.unit = theUnit
|
||||
|
||||
cfxHeloTroops.removeCommsFromConfig(conf)
|
||||
end
|
||||
|
||||
function cfxHeloTroops.addConfigMenu(conf)
|
||||
-- we add the a menu showing current state
|
||||
-- and the option to change fro auto drop
|
||||
-- and auto pickup
|
||||
-- we add a menu showing current configs
|
||||
local onOff = "OFF"
|
||||
if conf.autoDrop then onOff = "ON" end
|
||||
local theCommand = missionCommands.addCommandForGroup(
|
||||
@ -279,10 +274,12 @@ end
|
||||
|
||||
function cfxHeloTroops.setCommsMenu(theUnit)
|
||||
-- compatible with DCS 2.9.6 dynamic spawns
|
||||
-- set F10 Other.. menu for group
|
||||
if cfxHeloTroops.verbose then
|
||||
trigger.action.outText("+++heloT: setComms for player unit <" .. theUnit:getName() .. ">: ENTER.", 30)
|
||||
end
|
||||
|
||||
if not theUnit then return end
|
||||
if not theUnit:isExist() then return end
|
||||
|
||||
if not theUnit:isExist() then return end
|
||||
-- we only add this menu to troop carriers
|
||||
if not dcsCommon.isTroopCarrier(theUnit, cfxHeloTroops.troopCarriers) then
|
||||
if cfxHeloTroops.verbose then
|
||||
@ -294,19 +291,16 @@ function cfxHeloTroops.setCommsMenu(theUnit)
|
||||
local group = theUnit:getGroup()
|
||||
local id = group:getID()
|
||||
local conf = cfxHeloTroops.getUnitConfig(theUnit)
|
||||
-- set time stamp to avoid double-dipping later
|
||||
--conf.timeStamp = timer.getTime() -- to avoid double-dipping
|
||||
conf.id = id; -- we ALWAYS do this so it is current even after a crash
|
||||
conf.unit = theUnit -- link back
|
||||
|
||||
-- ok, first, if we don't have an F-10 menu, create one
|
||||
-- if we don't have an F-10 menu, create one
|
||||
if not (conf.myMainMenu) then
|
||||
conf.myMainMenu = missionCommands.addSubMenuForGroup(id, 'Airlift Troops')
|
||||
end
|
||||
|
||||
-- clear out existing commands
|
||||
-- clear out existing commands, add new
|
||||
cfxHeloTroops.clearCommsSubmenus(conf)
|
||||
|
||||
-- now we have a menu without submenus.
|
||||
-- add our own submenus
|
||||
cfxHeloTroops.addConfigMenu(conf)
|
||||
|
||||
-- now see if we are on the ground or in the air
|
||||
@ -317,22 +311,18 @@ function cfxHeloTroops.setCommsMenu(theUnit)
|
||||
conf.currentState = 1
|
||||
end
|
||||
end
|
||||
|
||||
if conf.currentState == 0 then
|
||||
cfxHeloTroops.addGroundMenu(conf)
|
||||
else
|
||||
cfxHeloTroops.addAirborneMenu(conf)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
function cfxHeloTroops.addAirborneMenu(conf)
|
||||
-- while we are airborne, there isn't much to do except add a status menu that does nothing
|
||||
-- but we can add some instructions
|
||||
-- let's begin by assuming no troops aboard
|
||||
-- while airborne, add a status menu
|
||||
local commandTxt = "(To load troops, land in proximity to them)"
|
||||
if conf.troopsOnBoardNum > 0 then
|
||||
commandTxt = "(You are carrying " .. conf.troopsOnBoardNum .. " Assault Troops. Land to deploy them)"
|
||||
commandTxt = "(You are carrying " .. conf.troopsOnBoardNum .. " Infantry. Land to deploy them)"
|
||||
end
|
||||
local theCommand = missionCommands.addCommandForGroup(
|
||||
conf.id,
|
||||
@ -345,15 +335,21 @@ function cfxHeloTroops.addAirborneMenu(conf)
|
||||
end
|
||||
|
||||
function cfxHeloTroops.redirectNoAction(args)
|
||||
-- actually, we do not redirect since there is nothing to do
|
||||
-- we do not redirect since there is nothing to do
|
||||
end
|
||||
|
||||
function cfxHeloTroops.addGroundMenu(conf)
|
||||
-- this is the most complex menu. Player can deploy troops when loaded
|
||||
-- and load troops when they are in proximity
|
||||
-- Player can deploy troops when loaded
|
||||
-- or load troops when they are in proximity
|
||||
if cfxHeloTroops.verbose then
|
||||
trigger.action.outText("+++heloT: ENTER addGroundMenu for unit <" .. conf.unit:getName() .. "> with <" .. conf.troopsOnBoardNum .. "> troops on board", 30)
|
||||
end
|
||||
|
||||
-- case 1: troops aboard
|
||||
if conf.troopsOnBoardNum > 0 then
|
||||
if cfxHeloTroops.verbose then
|
||||
trigger.action.outText("+++heloT: unit <" .. conf.unit:getName() .. "> has <" .. conf.troopsOnBoardNum .. "> troops on board", 30)
|
||||
end
|
||||
local theCommand = missionCommands.addCommandForGroup(
|
||||
conf.id,
|
||||
"Deploy Team <" .. conf.troopsOnBoard.name .. ">",
|
||||
@ -362,10 +358,10 @@ function cfxHeloTroops.addGroundMenu(conf)
|
||||
{conf, "deploy"}
|
||||
)
|
||||
table.insert(conf.myCommands, theCommand)
|
||||
return
|
||||
return -- no loading
|
||||
end
|
||||
|
||||
-- case 2A: no troops aboard, and requestable spawners/cloners in range
|
||||
-- case 2A: no troops aboard. requestable spawners/cloners in range?
|
||||
local p = conf.unit:getPosition().p
|
||||
local mySide = conf.unit:getCoalition()
|
||||
|
||||
@ -385,10 +381,12 @@ function cfxHeloTroops.addGroundMenu(conf)
|
||||
if cfxHeloTroops.legalTroops then
|
||||
if not dcsCommon.arrayContainsString(cfxHeloTroops.legalTroops, aType) then
|
||||
allLegal = false
|
||||
-- trigger.action.outText("spawner <" .. aSpawner.name .. ">: troop type <" .. aType .. "> is illegal", 30)
|
||||
end
|
||||
else
|
||||
if not dcsCommon.typeIsInfantry(aType) then
|
||||
allLegal = false
|
||||
-- trigger.action.outText("spawner <" .. aSpawner.name .. ">: troop type <" .. aType .. "> is not infantry", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -431,11 +429,10 @@ function cfxHeloTroops.addGroundMenu(conf)
|
||||
local numSpawners = #availableSpawners
|
||||
if numSpawners > 5 then numSpawners = 5 end
|
||||
while numSpawners > 0 do
|
||||
-- for each spawner in range, create a
|
||||
-- spawn menu item
|
||||
-- for each spawner in range, create a menu item
|
||||
local spawner = availableSpawners[numSpawners]
|
||||
local theName = spawner.baseName
|
||||
local comm = "Request <" .. theName .. "> troops for transport" -- .. math.floor(aTeam.dist) .. "m away"
|
||||
local comm = "Request <" .. theName .. "> troops for transport"
|
||||
local theCommand = missionCommands.addCommandForGroup(
|
||||
conf.id,
|
||||
comm,
|
||||
@ -447,23 +444,25 @@ function cfxHeloTroops.addGroundMenu(conf)
|
||||
numSpawners = numSpawners - 1
|
||||
end
|
||||
|
||||
-- case 2B: no troops aboard. see if there are troops around
|
||||
-- that we can load up
|
||||
|
||||
-- Collect troops in range that we can load up
|
||||
local cat = Group.Category.GROUND
|
||||
local unitsToLoad = dcsCommon.getLivingGroupsAndDistInRangeToPoint(p, conf.pickupRange, conf.unit:getCoalition(), cat)
|
||||
|
||||
-- now, the groups may contain units that are not for transport.
|
||||
-- the groups may contain units that are not for transport.
|
||||
-- later we can filter this by weight, or other cool stuff
|
||||
-- for now we simply only troopy with legal type strings
|
||||
-- TODO: add weight filtering
|
||||
unitsToLoad = cfxHeloTroops.filterTroopsByType(unitsToLoad)
|
||||
|
||||
-- filter all groups that are inside a dropZone with a
|
||||
-- positive autoDespawn attribute
|
||||
unitsToLoad = cfxHeloTroops.filterTroopsFromDropZones(unitsToLoad, mySide)
|
||||
|
||||
-- now limit the options to the five closest legal groups
|
||||
local numUnits = #unitsToLoad
|
||||
if numUnits > 5 then numUnits = 5 end
|
||||
if numUnits < 1 then
|
||||
local theCommand = missionCommands.addCommandForGroup(
|
||||
local theCommand = missionCommands.addCommandForGroup(
|
||||
conf.id,
|
||||
"(No units in range)",
|
||||
conf.myMainMenu,
|
||||
@ -480,7 +479,7 @@ function cfxHeloTroops.addGroundMenu(conf)
|
||||
local dist = aTeam.dist
|
||||
local group = aTeam.group
|
||||
local tNum = group:getSize()
|
||||
local comm = "Load <" .. group:getName() .. "> " .. tNum .. " Members" -- .. math.floor(aTeam.dist) .. "m away"
|
||||
local comm = "Load <" .. group:getName() .. "> " .. tNum .. " Members"
|
||||
local theCommand = missionCommands.addCommandForGroup(
|
||||
conf.id,
|
||||
comm,
|
||||
@ -530,10 +529,38 @@ function cfxHeloTroops.filterTroopsByType(unitsToLoad)
|
||||
return filteredGroups
|
||||
end
|
||||
|
||||
function cfxHeloTroops.filterTroopsFromDropZones(allTroops, mySide)
|
||||
-- quick-out: no dropZones
|
||||
if dcsCommon.getSizeOfTable(cfxHeloTroops.dropZones) < 1 then return allTroops end
|
||||
local filtered = {}
|
||||
for idx, theTeam in pairs(allTroops) do
|
||||
-- theTeam is a table {group, dist}
|
||||
local theGroup = theTeam.group
|
||||
local firstUnit = theGroup:getUnit(1)
|
||||
local include = true
|
||||
if firstUnit and Unit.isExist(firstUnit) then
|
||||
local p = firstUnit:getPoint()
|
||||
for idy, theZone in pairs(cfxHeloTroops.dropZones) do
|
||||
if theZone.autoDespawn > 0 and
|
||||
(theZone:getCoalition() == 0 or theZone:getCoalition() == mySide)
|
||||
then
|
||||
-- see if the unit is inside this zone
|
||||
if theZone:isPointInsideZone(p) then
|
||||
include = false -- filter out
|
||||
if theZone.verbose then
|
||||
trigger.action.outText("+++helo: filtered group <" .. theGroup:getName() .. "> from 'load' menu. Reason: autoDespawn active in deploy zone <" .. theZone.name .. ">", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if include then table.insert(filtered, theTeam) end
|
||||
end
|
||||
return filtered
|
||||
end
|
||||
--
|
||||
-- T O G G L E S
|
||||
--
|
||||
|
||||
function cfxHeloTroops.redirectToggleConfig(args)
|
||||
timer.scheduleFunction(cfxHeloTroops.doToggleConfig, args, timer.getTime() + 0.1)
|
||||
end
|
||||
@ -555,13 +582,10 @@ function cfxHeloTroops.doToggleConfig(args)
|
||||
else
|
||||
trigger.action.outTextForGroup(conf.id, "Troops will now board only after being ordered to do so", 30)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
cfxHeloTroops.setCommsMenu(conf.unit)
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- Deploying Troops
|
||||
--
|
||||
@ -571,14 +595,13 @@ end
|
||||
|
||||
function cfxHeloTroops.scoreWhenCapturing(theUnit)
|
||||
if theUnit and Unit.isExist(theUnit) and theUnit.getPlayerName then
|
||||
-- see if wer are inside a non-alinged zone
|
||||
-- and this includes a neutral zone
|
||||
-- see if we are inside a non-alinged zone (incl. neutral)
|
||||
local coa = theUnit:getCoalition()
|
||||
local p = theUnit:getPoint()
|
||||
local theGroup = theUnit:getGroup()
|
||||
local ID = theGroup:getID()
|
||||
local nearestZone, dist = cfxOwnedZones.getNearestOwnedZoneToPoint(p)
|
||||
if nearestZone and nearestZone:pointInZone(p) then -- dist < nearestZone.radius then
|
||||
if nearestZone and nearestZone:pointInZone(p) then
|
||||
-- we are inside an owned zone!
|
||||
if nearestZone.owner ~= coa then
|
||||
-- yup, combat drop!
|
||||
@ -615,7 +638,6 @@ function cfxHeloTroops.doDeployTroops(args)
|
||||
|
||||
-- deploy the troops I have on board
|
||||
cfxHeloTroops.deployTroopsFromHelicopter(conf)
|
||||
|
||||
-- interface with playerscore if we dropped
|
||||
-- inside an enemy-owned zone
|
||||
if cfxPlayerScore and cfxOwnedZones then
|
||||
@ -627,7 +649,10 @@ function cfxHeloTroops.doDeployTroops(args)
|
||||
conf.troopsOnBoardNum = 0
|
||||
conf.troopsOnBoard = {}
|
||||
conf.troopsOnBoard.name = "***wasdeployed***"
|
||||
|
||||
cfxHeloTroops.unitConfigs[theUnit:getName()] = conf -- forced write-back (strange...)
|
||||
if cfxHeloTroops.verbose then
|
||||
trigger.action.outText("+++heloT: doDeployTroops unit <" .. conf.unit:getName() .. "> reset to <" .. conf.troopsOnBoardNum .. "> troops on board", 30)
|
||||
end
|
||||
-- reset menu
|
||||
cfxHeloTroops.removeComms(conf.unit)
|
||||
cfxHeloTroops.setCommsMenu(conf.unit)
|
||||
@ -635,15 +660,13 @@ end
|
||||
|
||||
|
||||
function cfxHeloTroops.deployTroopsFromHelicopter(conf)
|
||||
-- we have troops, drop them now
|
||||
local unitTypes = {} -- build type names
|
||||
local theUnit = conf.unit
|
||||
local p = theUnit:getPoint()
|
||||
|
||||
-- split the conf.troopsOnBoardTypes into an array of types
|
||||
unitTypes = dcsCommon.splitString(conf.troopsOnBoard.types, ",")
|
||||
if #unitTypes < 1 then
|
||||
table.insert(unitTypes, "Soldier M4") -- make it one m4 trooper as fallback
|
||||
table.insert(unitTypes, "Soldier M4") -- fallback
|
||||
end
|
||||
|
||||
local range = conf.troopsOnBoard.range
|
||||
@ -651,6 +674,8 @@ function cfxHeloTroops.deployTroopsFromHelicopter(conf)
|
||||
local dest = conf.troopsOnBoard.destination
|
||||
local theName = conf.troopsOnBoard.name
|
||||
local moveFormation = conf.troopsOnBoard.moveFormation
|
||||
local code = conf.troopsOnBoard.code
|
||||
local canDrive = conf.troopsOnBoard.canDrive
|
||||
|
||||
if not orders then orders = "guard" end
|
||||
orders = string.lower(orders)
|
||||
@ -666,12 +691,14 @@ function cfxHeloTroops.deployTroopsFromHelicopter(conf)
|
||||
local chopperZone = cfxZones.createSimpleZone("choppa", p, 12) -- 12 m radius around choppa
|
||||
local theCoalition = theUnit:getGroup():getCoalition() -- make it chopper's COALITION
|
||||
local theGroup, theData = cfxZones.createGroundUnitsInZoneForCoalition (
|
||||
theCoalition,
|
||||
theCoalition,
|
||||
theName, -- group name, may be tracked
|
||||
chopperZone,
|
||||
unitTypes,
|
||||
unitTypes,
|
||||
conf.dropFormation,
|
||||
90)
|
||||
90,
|
||||
nil, -- liveries not yet supported
|
||||
canDrive)
|
||||
-- persistence management
|
||||
local troopData = {}
|
||||
troopData.groupData = theData
|
||||
@ -681,7 +708,7 @@ function cfxHeloTroops.deployTroopsFromHelicopter(conf)
|
||||
troopData.destination = dest -- only for attackzone orders
|
||||
cfxHeloTroops.deployedTroops[theData.name] = troopData
|
||||
|
||||
local troop = cfxGroundTroops.createGroundTroops(theGroup, range, orders, moveFormation)
|
||||
local troop = cfxGroundTroops.createGroundTroops(theGroup, range, orders, moveFormation, code, canDrive)
|
||||
if orders == "captureandhold" then
|
||||
-- we get the target zone NOW!!! before we flip the zone and
|
||||
-- and make them run to the wrong zone
|
||||
@ -698,8 +725,7 @@ function cfxHeloTroops.deployTroopsFromHelicopter(conf)
|
||||
cfxGroundTroops.addGroundTroopsToPool(troop) -- will schedule move orders
|
||||
trigger.action.outTextForGroup(conf.id, "<" .. theGroup:getName() .. "> have deployed to the ground with orders " .. orders .. "!", 30)
|
||||
trigger.action.outSoundForGroup(conf.id, cfxHeloTroops.disembarkSound)
|
||||
-- see if this is tracked by a tracker, and pass them back so
|
||||
-- they can un-limbo
|
||||
-- if tracked by a tracker, and pass them back for un-limbo
|
||||
if groupTracker then
|
||||
local isTracking, numTracking, trackers = groupTracker.groupNameTrackedBy(theName)
|
||||
if isTracking then
|
||||
@ -712,7 +738,7 @@ function cfxHeloTroops.deployTroopsFromHelicopter(conf)
|
||||
end
|
||||
end
|
||||
|
||||
-- bang on all dropZones that we can find
|
||||
-- bang on dropZones
|
||||
for name, theZone in pairs(cfxHeloTroops.dropZones) do
|
||||
-- can employ coalition test here as well, maybe later?
|
||||
if theZone:isPointInsideZone(p) then
|
||||
@ -766,9 +792,9 @@ function cfxHeloTroops.doLoadGroup(args)
|
||||
conf.troopsOnBoard.name = gName
|
||||
-- and put it all into the helicopter config
|
||||
|
||||
-- now we need to destroy the group. Let's prepare:
|
||||
-- destroy the group:
|
||||
-- if it was tracked, tell tracker to move it to limbo
|
||||
-- to remember it even if it's destroyed
|
||||
-- to remember it
|
||||
if groupTracker then
|
||||
-- only if groupTracker is active
|
||||
local isTracking, numTracking, trackers = groupTracker.groupTrackedBy(group)
|
||||
@ -786,7 +812,7 @@ function cfxHeloTroops.doLoadGroup(args)
|
||||
-- then, remove it from the pool
|
||||
local pooledGroup = cfxGroundTroops.getGroundTroopsForGroup(group)
|
||||
if pooledGroup then
|
||||
-- copy some important info from the troops
|
||||
-- copy important info from the troops
|
||||
-- if they are set
|
||||
conf.troopsOnBoard.orders = pooledGroup.orders
|
||||
conf.troopsOnBoard.range = pooledGroup.range
|
||||
@ -795,29 +821,26 @@ function cfxHeloTroops.doLoadGroup(args)
|
||||
if pooledGroup.orders and pooledGroup.orders == "captureandhold" then
|
||||
conf.troopsOnBoard.destination = nil -- forget last destination so they can be helo-redeployed
|
||||
end
|
||||
conf.troopsOnBoard.code = pooledGroup.code
|
||||
conf.troopsOnBoard.canDrive = pooledGroup.canDrive
|
||||
|
||||
cfxGroundTroops.removeTroopsFromPool(pooledGroup)
|
||||
trigger.action.outTextForGroup(conf.id, "Team '".. conf.troopsOnBoard.name .."' loaded and has orders <" .. conf.troopsOnBoard.orders .. ">", 30)
|
||||
-- trigger.action.outText("and mf = <" .. conf.troopsOnBoard.moveFormation .. ">", 30)
|
||||
--trigger.action.outSoundForGroup(conf.id, cfxHeloTroops.actionSound) -- "Quest Snare 3.wav")
|
||||
else
|
||||
if cfxHeloTroops.verbose then
|
||||
trigger.action.outText("+++heloT: ".. conf.troopsOnBoard.name .." was not committed to ground troops", 30)
|
||||
end
|
||||
end
|
||||
|
||||
-- now simply destroy the group
|
||||
-- we'll re-assemble it when we deploy it
|
||||
-- TODO: add weight changing code
|
||||
-- TODO: ensure compatibility with CSAR module
|
||||
group:destroy()
|
||||
|
||||
group:destroy()
|
||||
-- now immediately run a GC so this group is removed
|
||||
-- from any save data
|
||||
cfxHeloTroops.GC()
|
||||
|
||||
-- say so
|
||||
trigger.action.outTextForGroup(conf.id, "Team '".. conf.troopsOnBoard.name .."' aboard, ready to go!", 30)
|
||||
trigger.action.outSoundForGroup(conf.id, cfxHeloTroops.loadSound) -- "Quest Snare 3.wav")
|
||||
trigger.action.outSoundForGroup(conf.id, cfxHeloTroops.loadSound)
|
||||
|
||||
-- reset menu
|
||||
cfxHeloTroops.removeComms(conf.unit)
|
||||
@ -840,9 +863,8 @@ end
|
||||
function cfxHeloTroops.doSpawnGroup(args)
|
||||
local conf = args[1]
|
||||
local theSpawner = args[2]
|
||||
-- NOTE: theSpawner can be of type cfxSpawnZone !!!OR!!! cfxCloneZones
|
||||
-- make sure cooldown on spawner has timed out, else
|
||||
-- notify that you have to wait
|
||||
-- theSpawner can be of type cfxSpawnZone !!!OR!!! cfxCloneZones
|
||||
-- make sure cooldown on spawner has timed out, else notify that you have to wait
|
||||
local now = timer.getTime()
|
||||
if now < (theSpawner.lastSpawnTimeStamp + theSpawner.cooldown) then
|
||||
local delta = math.floor(theSpawner.lastSpawnTimeStamp + theSpawner.cooldown - now)
|
||||
@ -850,15 +872,13 @@ function cfxHeloTroops.doSpawnGroup(args)
|
||||
return
|
||||
end
|
||||
|
||||
--cfxSpawnZones.spawnWithSpawner(theSpawner) -- old code
|
||||
theSpawner.spawnWithSpawner(theSpawner) -- can be both spawner and cloner
|
||||
theSpawner.spawnWithSpawner(theSpawner) -- can be both spawner and cloner (Lua "polymorphism"
|
||||
trigger.action.outTextForGroup(conf.id, "Deploying <" .. theSpawner.baseName .. "> now...", 30)
|
||||
|
||||
-- reset all comms so we can include new troops
|
||||
-- into load menu
|
||||
timer.scheduleFunction(cfxHeloTroops.delayedCommsResetForUnit, {conf.unit, "ignore"}, now + 1.0)
|
||||
end
|
||||
|
||||
--
|
||||
-- handle events
|
||||
--
|
||||
@ -871,25 +891,21 @@ function cfxHeloTroops:onEvent(theEvent)
|
||||
if not theUnit.getPlayerName then return end -- not a player
|
||||
if not theUnit:getPlayerName() then return end -- not a player
|
||||
local name = theUnit:getName() -- moved to a later
|
||||
|
||||
-- only for helicopters -- overridedden by troop carriers
|
||||
-- we don't check for cat any more, so any airframe
|
||||
-- can be used as long as it's ok with isTroopCarrier()
|
||||
|
||||
-- only for troop carriers
|
||||
|
||||
-- only for troop carriers (not just helos any more)
|
||||
if not dcsCommon.isTroopCarrier(theUnit, cfxHeloTroops.troopCarriers) then
|
||||
return
|
||||
end
|
||||
|
||||
if theID == 4 then -- land
|
||||
if theID == 4 or theID == 55 then -- land
|
||||
cfxHeloTroops.heloLanded(theUnit)
|
||||
end
|
||||
|
||||
if theID == 3 then -- take off
|
||||
if theID == 3 or theID == 54 then -- take off
|
||||
cfxHeloTroops.heloDeparted(theUnit)
|
||||
end
|
||||
|
||||
if theID == 5 then -- crash
|
||||
if theID == 5 or theID == 30 then -- crash or unitLost
|
||||
cfxHeloTroops.heloCrashed(theUnit)
|
||||
end
|
||||
|
||||
@ -1030,6 +1046,18 @@ function cfxHeloTroops.loadData()
|
||||
local cty = gData.cty
|
||||
local cat = gData.cat
|
||||
local dest = nil
|
||||
local code = gdTroop.code
|
||||
local canDrive = gdTroop.canDrive
|
||||
local formation = gdTroop.moveFormation
|
||||
local code = gdTroop.code
|
||||
local canDrive = gdTroop.canDrive
|
||||
|
||||
if canDrive then -- restore canDrive to all units
|
||||
local units = gData.units
|
||||
for idx, theUnit in pairs(units) do
|
||||
theUnit.playerCanDrive = drivable
|
||||
end
|
||||
end
|
||||
|
||||
-- synch destination from name to real zone
|
||||
if gdTroop.destination then
|
||||
@ -1045,7 +1073,7 @@ function cfxHeloTroops.loadData()
|
||||
-- post-proccing for cfxGroundTroops
|
||||
|
||||
-- add to groundTroops
|
||||
local newTroops = cfxGroundTroops.createGroundTroops(theGroup, range, orders)
|
||||
local newTroops = cfxGroundTroops.createGroundTroops(theGroup, range, orders, moveFormation, code, canDrive)
|
||||
newTroops.destination = dest
|
||||
cfxGroundTroops.addGroundTroopsToPool(newTroops)
|
||||
end
|
||||
|
||||
@ -1,26 +1,25 @@
|
||||
jtacGrpUI = {}
|
||||
jtacGrpUI.version = "3.1.0"
|
||||
jtacGrpUI.version = "4.0.0"
|
||||
jtacGrpUI.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxZones",
|
||||
"cfxGroundTroops",
|
||||
}
|
||||
--[[-- VERSION HISTORY
|
||||
- 2.0.0 - dmlZones
|
||||
- sanity checks upon load
|
||||
- eliminated cfxPlayer dependence
|
||||
- clean-up
|
||||
- jtacSound
|
||||
3.0.0 - support for attachTo:
|
||||
3.1.0 - support for DCS 2.0.6 dynamic player spwans
|
||||
|
||||
3.1.0 - support for DCS 2.9.6 jul-11 2024 dynamic player spwans
|
||||
3.2.0 - better guarding access to ownedZones in collectJTACtargets()
|
||||
4.0.0 - added support for twn when present
|
||||
- made report more clear that all pos are requestor-relative
|
||||
- support for CA (event 20 (enter unit) on ground vehicle)
|
||||
- report now supports wildcards
|
||||
- reports inm two parts: why and what
|
||||
- "no target" for right side when no "what"
|
||||
- comms mechanic simplification
|
||||
- lase code from spawner support via ground troops
|
||||
--]]--
|
||||
-- find & command cfxGroundTroops-based jtacs
|
||||
-- UI installed via OTHER for all groups with players
|
||||
-- module based on xxxGrpUI
|
||||
|
||||
jtacGrpUI.groupConfig = {} -- all inited group private config data, indexed by group name.
|
||||
jtacGrpUI.simpleCommands = true -- if true, f10 other invokes directly
|
||||
|
||||
function jtacGrpUI.resetConfig(conf)
|
||||
end
|
||||
@ -43,8 +42,7 @@ function jtacGrpUI.createDefaultConfig(theGroup)
|
||||
return conf
|
||||
end
|
||||
|
||||
-- getConfigFor group will allocate if doesn't exist in DB
|
||||
-- and add to it
|
||||
-- lazy init: allocate if doesn't exist in DB
|
||||
function jtacGrpUI.getConfigForGroup(theGroup)
|
||||
if not theGroup or (not Group.isExist(theGroup))then
|
||||
trigger.action.outText("+++WARNING: jtacGrpUI nil group in getConfigForGroup!", 30)
|
||||
@ -65,8 +63,7 @@ function jtacGrpUI.getConfigByGroupName(theName) -- DOES NOT allocate when not e
|
||||
end
|
||||
|
||||
|
||||
function jtacGrpUI.getConfigForUnit(theUnit)
|
||||
-- simple one-off step by accessing the group
|
||||
function jtacGrpUI.getConfigForUnit(theUnit) -- lazy alloc
|
||||
if not theUnit then
|
||||
trigger.action.outText("+++WARNING: jtacGrpUI nil unit in getConfigForUnit!", 30)
|
||||
return nil
|
||||
@ -90,105 +87,49 @@ end
|
||||
|
||||
function jtacGrpUI.removeCommsFromConfig(conf)
|
||||
jtacGrpUI.clearCommsSubmenus(conf)
|
||||
|
||||
if conf.myMainMenu then
|
||||
missionCommands.removeItemForGroup(conf.id, conf.myMainMenu)
|
||||
conf.myMainMenu = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- this only works in single-unit player groups.
|
||||
function jtacGrpUI.removeCommsForUnit(theUnit)
|
||||
if not theUnit then return end
|
||||
if not theUnit:isExist() then return end
|
||||
-- perhaps add code: check if group is empty
|
||||
local conf = jtacGrpUI.getConfigForUnit(theUnit)
|
||||
jtacGrpUI.removeCommsFromConfig(conf)
|
||||
end
|
||||
|
||||
function jtacGrpUI.removeCommsForGroup(theGroup)
|
||||
if not theGroup then return end
|
||||
if not theGroup:isExist() then return end
|
||||
local conf = jtacGrpUI.getConfigForGroup(theGroup)
|
||||
jtacGrpUI.removeCommsFromConfig(conf)
|
||||
end
|
||||
|
||||
function jtacGrpUI.isEligibleForMenu(theGroup)
|
||||
if jtacGrpUI.jtacTypes == "all" or
|
||||
jtacGrpUI.jtacTypes == "any" then return true end
|
||||
if dcsCommon.stringStartsWith(jtacGrpUI.jtacTypes, "hel", true) then
|
||||
if dcsCommon.stringStartsWith(jtacGrpUI.jtacTypes, "hel", true) then
|
||||
local cat = theGroup:getCategory()
|
||||
return cat == 1
|
||||
end
|
||||
if dcsCommon.stringStartsWith(jtacGrpUI.jtacTypes, "plan", true) then
|
||||
if dcsCommon.stringStartsWith(jtacGrpUI.jtacTypes, "plan", true) then
|
||||
local cat = theGroup:getCategory()
|
||||
return cat == 0
|
||||
end
|
||||
-- note: no option for ground troops now
|
||||
if jtacGrpUI.verbose then
|
||||
trigger.action.outText("+++jGUI: unknown jtacTypes <" .. jtacGrpUI.jtacTypes .. "> -- allowing access to group <" .. theGroup:getName() ..">", 30)
|
||||
end
|
||||
return true -- for later expansion
|
||||
end
|
||||
|
||||
function jtacGrpUI.setCommsMenuForUnit(theUnit)
|
||||
if not theUnit then
|
||||
trigger.action.outText("+++WARNING: jtacGrpUI nil UNIT in setCommsMenuForUnit!", 30)
|
||||
return
|
||||
end
|
||||
if not theUnit:isExist() then return end
|
||||
|
||||
local theGroup = theUnit:getGroup()
|
||||
jtacGrpUI.setCommsMenu(theGroup)
|
||||
end
|
||||
|
||||
function jtacGrpUI.setCommsMenu(theGroup)
|
||||
if not theGroup then return end
|
||||
if not Group.isExist(theGroup) then return end
|
||||
if not jtacGrpUI.isEligibleForMenu(theGroup) then return end
|
||||
|
||||
local mainMenu = nil
|
||||
if jtacGrpUI.mainMenu then
|
||||
mainMenu = radioMenu.getMainMenuFor(jtacGrpUI.mainMenu) -- nilling both next params will return menus[0]
|
||||
mainMenu = radioMenu.getMainMenuFor(jtacGrpUI.mainMenu)
|
||||
end
|
||||
|
||||
local conf = jtacGrpUI.getConfigForGroup(theGroup)
|
||||
conf.id = theGroup:getID(); -- we always do this
|
||||
|
||||
if jtacGrpUI.simpleCommands then
|
||||
-- we install directly in F-10 other
|
||||
if not conf.myMainMenu then
|
||||
local commandTxt = "jtac Lasing Report"
|
||||
local theCommand = missionCommands.addCommandForGroup(
|
||||
conf.id, commandTxt, mainMenu, jtacGrpUI.redirectCommandX, {conf, "lasing report"})
|
||||
conf.myMainMenu = theCommand
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
-- ok, first, if we don't have an F-10 menu, create one
|
||||
if not (conf.myMainMenu) then
|
||||
conf.myMainMenu = missionCommands.addSubMenuForGroup(conf.id, 'jtac', mainMenu)
|
||||
end
|
||||
|
||||
-- clear out existing commands
|
||||
jtacGrpUI.clearCommsSubmenus(conf)
|
||||
|
||||
-- now we have a menu without submenus.
|
||||
-- add our own submenus
|
||||
jtacGrpUI.addSubMenus(conf)
|
||||
|
||||
local commandTxt = jtacGrpUI.menuName -- "jtac Lasing Report"
|
||||
local theCommand = missionCommands.addCommandForGroup(conf.id, commandTxt, mainMenu, jtacGrpUI.redirectCommandX, {conf, "lasing report"})
|
||||
conf.myMainMenu = theCommand
|
||||
end
|
||||
|
||||
function jtacGrpUI.addSubMenus(conf)
|
||||
local commandTxt = "jtac Lasing Report"
|
||||
local theCommand = missionCommands.addCommandForGroup(
|
||||
conf.id,
|
||||
commandTxt,
|
||||
conf.myMainMenu,
|
||||
jtacGrpUI.redirectCommandX,
|
||||
{conf, "lasing report"}
|
||||
)
|
||||
local theCommand = missionCommands.addCommandForGroup(conf.id, commandTxt, conf.myMainMenu, jtacGrpUI.redirectCommandX, {conf, "lasing report"})
|
||||
table.insert(conf.myCommands, theCommand)
|
||||
end
|
||||
|
||||
@ -197,8 +138,8 @@ function jtacGrpUI.redirectCommandX(args)
|
||||
end
|
||||
|
||||
function jtacGrpUI.doCommandX(args)
|
||||
local conf = args[1] -- < conf in here
|
||||
local what = args[2] -- < second argument in here
|
||||
local conf = args[1] -- conf
|
||||
local what = args[2] -- "xyz" -- not used here
|
||||
local theGroup = conf.theGroup
|
||||
local targetList = jtacGrpUI.collectJTACtargets(conf, true)
|
||||
-- iterate the list
|
||||
@ -207,14 +148,33 @@ function jtacGrpUI.doCommandX(args)
|
||||
trigger.action.outSoundForGroup(conf.id, jtacGrpUI.jtacSound)
|
||||
return
|
||||
end
|
||||
local here = dcsCommon.getGroupLocation(conf.theGroup) -- pos of pilot!
|
||||
|
||||
local desc = "JTAC Target Report:\n"
|
||||
local desc = "JTAC Target Report:\nTargets being laser-designated for " .. conf.name .. ":\n"
|
||||
for i=1, #targetList do
|
||||
local aTarget = targetList[i]
|
||||
if aTarget.idle then
|
||||
desc = desc .. "\n" .. aTarget.jtacName .. aTarget.posInfo ..": no target"
|
||||
local theUnit = aTarget.source -- lazing unit
|
||||
local code = aTarget.code
|
||||
if not Unit.isExist(theUnit) then
|
||||
desc = desc .. "\n" .. aTarget.jtacName .. ": lost contact."
|
||||
elseif aTarget.idle then
|
||||
local lWho = jtacGrpUI.who
|
||||
lWho = dcsCommon.processStringWildcardsForUnit(lWho, theUnit)
|
||||
local there = theUnit:getPoint()
|
||||
lWho = dcsCommon.processAtoBWildCards(lWho, here, there)
|
||||
lWho = lWho:gsub("<code>", code)
|
||||
desc = desc .. "\n" .. lWho ..": no target"
|
||||
else
|
||||
desc = desc .. "\n" .. aTarget.jtacName .. aTarget.posInfo .." lasing " .. aTarget.lazeTargetType .. " [" .. aTarget.range .. "nm at " .. aTarget.bearing .. "°]," .. " code=" .. cfxGroundTroops.laseCode
|
||||
local lWho = dcsCommon.processStringWildcardsForUnit(jtacGrpUI.who, theUnit)
|
||||
local there = theUnit:getPoint()
|
||||
lWho = dcsCommon.processAtoBWildCards(lWho, here, there)
|
||||
lWho = lWho:gsub("<code>", code)
|
||||
local lWhat = dcsCommon.processStringWildcardsForUnit(jtacGrpUI.what, aTarget.lazeTarget) -- does unit type
|
||||
there = aTarget.lazeTarget:getPoint()
|
||||
lWhat = dcsCommon.processTimeLocWildCards(lWhat, there)
|
||||
lWhat = dcsCommon.processAtoBWildCards(lWhat, here, there)
|
||||
lWhat = lWhat:gsub("<code>", code)
|
||||
desc = desc .. "\n" .. lWho .. lWhat
|
||||
end
|
||||
end
|
||||
trigger.action.outTextForGroup(conf.id, desc .. "\n", 30)
|
||||
@ -226,7 +186,6 @@ function jtacGrpUI.collectJTACtargets(conf, includeIdle)
|
||||
-- troops that are lazing. 'Lazing' are all groups that
|
||||
-- have an active (non-nil) lazeTarget and 'laze' orders
|
||||
if not includeIdle then includeIdle = false end
|
||||
|
||||
local theJTACS = {}
|
||||
for idx, troop in pairs(cfxGroundTroops.deployedTroops) do
|
||||
if troop.coalition == conf.coalition
|
||||
@ -252,31 +211,20 @@ function jtacGrpUI.collectJTACtargets(conf, includeIdle)
|
||||
for idx, troop in pairs (theJTACS) do
|
||||
local aTarget = {}
|
||||
-- establish our location
|
||||
aTarget.jtacName = troop.name
|
||||
aTarget.posInfo = ""
|
||||
if cfxOwnedZones and cfxOwnedZones.hasOwnedZones() then
|
||||
local jtacLoc = dcsCommon.getGroupLocation(troop.group)
|
||||
local nearestZone = cfxOwnedZones.getNearestOwnedZoneToPoint(jtacLoc)
|
||||
if nearestZone then
|
||||
local ozRange = dcsCommon.dist(jtacLoc, nearestZone.point) * 0.000621371 -- meters to nm
|
||||
ozRange = math.floor(ozRange * 10) / 10
|
||||
local relPos = dcsCommon.compassPositionOfARelativeToB(jtacLoc, nearestZone.point)
|
||||
aTarget.posInfo = " (" .. ozRange .. "nm " .. relPos .. " of " .. nearestZone.name .. ")"
|
||||
end
|
||||
end
|
||||
aTarget.jtacName = troop.name -- group name
|
||||
aTarget.code = troop.code
|
||||
aTarget.source = dcsCommon.getFirstLivingUnit(troop.group)
|
||||
-- we may get idlers, catch them now
|
||||
if not troop.lazeTarget then
|
||||
aTarget.idle = true
|
||||
aTarget.range = math.huge
|
||||
else
|
||||
-- get the target we are lazing
|
||||
-- proc target
|
||||
local there = troop.lazeTarget:getPoint()
|
||||
aTarget.idle = false
|
||||
aTarget.range = dcsCommon.dist(here, there)
|
||||
aTarget.range = aTarget.range * 0.000621371 -- meter to miles
|
||||
aTarget.range = dcsCommon.dist(here, there) * 0.000621371 -- meter to miles
|
||||
aTarget.range = math.floor(aTarget.range * 10) / 10
|
||||
aTarget.bearing = dcsCommon.bearingInDegreesFromAtoB(here, there)
|
||||
aTarget.lazeTargetType = troop.lazeTargetType
|
||||
aTarget.lazeTarget = troop.lazeTarget
|
||||
end
|
||||
table.insert(targetList, aTarget)
|
||||
end
|
||||
@ -289,7 +237,7 @@ function jtacGrpUI.collectJTACtargets(conf, includeIdle)
|
||||
end
|
||||
|
||||
--
|
||||
-- event handler - simplified, only for player birth
|
||||
-- event handler
|
||||
--
|
||||
function jtacGrpUI:onEvent(theEvent)
|
||||
if not theEvent then return end
|
||||
@ -315,12 +263,29 @@ function jtacGrpUI:onEvent(theEvent)
|
||||
jtacGrpUI.removeCommsFromConfig(conf) -- remove menus
|
||||
jtacGrpUI.resetConfig(conf) -- re-init this group for when it re-appears
|
||||
end
|
||||
|
||||
jtacGrpUI.setCommsMenu(theGroup)
|
||||
end
|
||||
-- maybe collapse event 15 into 20?
|
||||
if id == 20 then -- CA player enter event???
|
||||
local theGroup = theUnit:getGroup()
|
||||
if not theGroup then return end
|
||||
local gName = theGroup:getName()
|
||||
if not gName then return end
|
||||
local cat = theGroup:getCategory()
|
||||
if cat == 2 then -- ground! we are in a CA unit!
|
||||
local pName = theUnit:getPlayerName()
|
||||
if jtacGrpUI.verbose then
|
||||
trigger.action.outText("+++jGUI: CA player unit take-over. installing JTAC for <" .. pName .. "> on unit <" .. uName .. ">", 30)
|
||||
end
|
||||
local conf = jtacGrpUI.getConfigByGroupName(gName)
|
||||
if conf then
|
||||
jtacGrpUI.removeCommsFromConfig(conf) -- remove menus
|
||||
jtacGrpUI.resetConfig(conf) -- re-init this group for when it re-appears
|
||||
end
|
||||
jtacGrpUI.setCommsMenu(theGroup)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- Start
|
||||
--
|
||||
@ -335,6 +300,7 @@ function jtacGrpUI.readConfigZone()
|
||||
jtacGrpUI.jtacTypes = string.lower(jtacGrpUI.jtacTypes)
|
||||
|
||||
jtacGrpUI.jtacSound = theZone:getStringFromZoneProperty("jtacSound", "UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav")
|
||||
jtacGrpUI.menuName = theZone:getStringFromZoneProperty("menuName", "jtac Lasing Report")
|
||||
|
||||
if theZone:hasProperty("attachTo:") then
|
||||
local attachTo = theZone:getStringFromZoneProperty("attachTo:", "<none>")
|
||||
@ -350,6 +316,9 @@ function jtacGrpUI.readConfigZone()
|
||||
end
|
||||
end
|
||||
|
||||
jtacGrpUI.who = theZone:getStringFromZoneProperty("who", "<g><twnnm>")
|
||||
--jtacGrpUI.what = theZone:getStringFromZoneProperty("what", " - lasing <typ> [<lat>:<lon>], code=<code>")
|
||||
jtacGrpUI.what = theZone:getStringFromZoneProperty("what", " - lasing <typ> [<rngnm>nm, bearing <bea>°], code=<code>")
|
||||
jtacGrpUI.verbose = theZone.verbose
|
||||
|
||||
end
|
||||
@ -362,15 +331,7 @@ function jtacGrpUI.start()
|
||||
if not dcsCommon.libCheck("cfx jtac GUI", jtacGrpUI.requiredLibs) then
|
||||
return false
|
||||
end
|
||||
|
||||
jtacGrpUI.readConfigZone()
|
||||
|
||||
local allPlayerUnits = dcsCommon.getAllExistingPlayerUnitsRaw()
|
||||
for unitName, theUnit in pairs(allPlayerUnits) do
|
||||
jtacGrpUI.setCommsMenuForUnit(theUnit)
|
||||
end
|
||||
|
||||
-- now install event handler
|
||||
world.addEventHandler(jtacGrpUI)
|
||||
trigger.action.outText("cf/x jtacGrpUI v" .. jtacGrpUI.version .. " started", 30)
|
||||
return true
|
||||
@ -381,9 +342,3 @@ if not jtacGrpUI.start() then
|
||||
trigger.action.outText("JTAC GUI failed to start up.", 30)
|
||||
jtacGrpUI = nil
|
||||
end
|
||||
|
||||
--[[--
|
||||
TODO:
|
||||
callback into GroundTroops lazing
|
||||
what is 'simpleCommand' really for? remove or refine
|
||||
--]]--
|
||||
@ -607,7 +607,7 @@ end
|
||||
|
||||
-- getting closest owned zones etc
|
||||
-- required for groundTroops and factory attackers
|
||||
-- methods provided only for other modules (e.g. cfxGroundTroops or
|
||||
-- methods provided only for other modules (e.g. cfxGroundTroops, jtacGrpGUI or
|
||||
-- factoryZone
|
||||
--
|
||||
|
||||
@ -658,7 +658,7 @@ function cfxOwnedZones.collectZones(mode)
|
||||
end
|
||||
end
|
||||
|
||||
-- getNearestOwnedZoneToPoint invoked by heloTroops
|
||||
-- getNearestOwnedZoneToPoint invoked by heloTroops and jtacGUI
|
||||
function cfxOwnedZones.getNearestOwnedZoneToPoint(p)
|
||||
local allZones = cfxOwnedZones.collectZones()
|
||||
return cfxZones.getClosestZone(p, allZones)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxSpawnZones = {}
|
||||
cfxSpawnZones.version = "2.2.0"
|
||||
cfxSpawnZones.version = "3.0.0"
|
||||
cfxSpawnZones.requiredLibs = {
|
||||
"dcsCommon", -- common is of course needed for everything
|
||||
-- pretty stupid to check for this since we
|
||||
@ -18,20 +18,12 @@ cfxSpawnZones.spawnedGroups = {}
|
||||
|
||||
--
|
||||
-- Zones that conform with this requirements spawn toops automatically
|
||||
-- *** DOES NOT EXTEND ZONES *** LINKED OWNER via masterOwner ***
|
||||
-- *** DOES !NOT! EXTEND ZONES *** LINKED OWNER via masterOwner ***
|
||||
--
|
||||
--[[--
|
||||
-- version history
|
||||
2.0.0 - dmlZones
|
||||
- moved "types" to spawner
|
||||
- baseName defaults to zone name, as it is safe for naming
|
||||
- spawnWithSpawner direct link in spawner to spawnZones
|
||||
2.0.1 - fix in verifySpawnOwnership() when not master zone found
|
||||
2.0.2 - new "moveFormation" attribute
|
||||
2.0.3 - corrected type in spawnUnits? attribute
|
||||
2.1.0 - masterOwner update for dmlZones.
|
||||
since spawners don't extend zones, this is still old-school
|
||||
2.2.0 - "drivable" spawner attribute
|
||||
3.0.0 - supports zone-individual laser code for "lase" orders
|
||||
- drivable attribute passed to groundTroops
|
||||
|
||||
--]]--
|
||||
|
||||
@ -176,6 +168,12 @@ function cfxSpawnZones.createSpawner(inZone)
|
||||
end
|
||||
end
|
||||
|
||||
if inZone:hasProperty("code") then -- lase code
|
||||
theSpawner.code = inZone:getNumberFromZoneProperty("code", 1688)
|
||||
elseif inZone:hasProperty("lcode") then -- lase code
|
||||
theSpawner.code = inZone:getNumberFromZoneProperty("lcode", 1688)
|
||||
end
|
||||
|
||||
if cfxSpawnZones.verbose or inZone.verbose then
|
||||
trigger.action.outText("+++spwn: created spawner for <" .. inZone.name .. ">", 30)
|
||||
end
|
||||
@ -207,19 +205,24 @@ function cfxSpawnZones.getRequestableSpawnersInRange(aPoint, aRange, aSide)
|
||||
if not aSide then aSide = 0 end
|
||||
if not aRange then aRange = 200 end
|
||||
if not aPoint then return {} end
|
||||
|
||||
|
||||
local theSpawners = {}
|
||||
for aZone, aSpawner in pairs(cfxSpawnZones.allSpawners) do
|
||||
-- iterate all zones and collect those that match
|
||||
local hasMatch = true
|
||||
local reasons = ""
|
||||
local delta = dcsCommon.distFlat(aPoint, cfxZones.getPoint(aZone))
|
||||
if delta>aRange then hasMatch = false end
|
||||
if delta>aRange then
|
||||
hasMatch = false
|
||||
-- reasons = reasons .. "[distance " .. math.floor(delta) .. "]
|
||||
end
|
||||
if aSide ~= 0 then
|
||||
-- check if side is correct for owned zone
|
||||
if not cfxSpawnZones.verifySpawnOwnership(aSpawner) then
|
||||
-- failed ownership test. owner of master
|
||||
-- is not my own zone
|
||||
hasMatch = false
|
||||
-- reasons = reasons .. "[sawnOwnership] "
|
||||
end
|
||||
end
|
||||
|
||||
@ -227,6 +230,7 @@ function cfxSpawnZones.getRequestableSpawnersInRange(aPoint, aRange, aSide)
|
||||
-- only return spawners with this side
|
||||
-- note: this will NOT work with neutral players
|
||||
hasMatch = false
|
||||
-- reasons = reasons .. "[rawOwner] "
|
||||
end
|
||||
|
||||
if not aSpawner.requestable then
|
||||
@ -235,6 +239,9 @@ function cfxSpawnZones.getRequestableSpawnersInRange(aPoint, aRange, aSide)
|
||||
|
||||
if hasMatch then
|
||||
table.insert(theSpawners, aSpawner)
|
||||
-- trigger.action.outText("+++Spwn: ELIGIBLE spawner <" .. aSpawner.name .. ">", 30)
|
||||
-- else
|
||||
-- trigger.action.outText("+++Spwn: spawner <" .. aSpawner.name .. "> not eligible because " .. reasons, 30)
|
||||
end
|
||||
end
|
||||
|
||||
@ -327,6 +334,9 @@ function cfxSpawnZones.spawnWithSpawner(aSpawner)
|
||||
troopData.target = aSpawner.target -- can be nil!
|
||||
troopData.tracker = theZone.trackWith -- taken from ZONE!!, can be nil
|
||||
troopData.range = aSpawner.range
|
||||
troopData.code = aSpawner.code
|
||||
troopData.drivable = aSpawner.drivable
|
||||
|
||||
cfxSpawnZones.spawnedGroups[theData.name] = troopData
|
||||
|
||||
-- remember: orders are always lower case only
|
||||
@ -345,7 +355,7 @@ function cfxSpawnZones.spawnWithSpawner(aSpawner)
|
||||
AI.Option.Ground.val.ROE.WEAPON_HOLD,
|
||||
1.0)
|
||||
else
|
||||
local newTroops = cfxGroundTroops.createGroundTroops(theGroup, aSpawner.range, aSpawner.orders, aSpawner.moveFormation)
|
||||
local newTroops = cfxGroundTroops.createGroundTroops(theGroup, aSpawner.range, aSpawner.orders, aSpawner.moveFormation, aSpawner.code, aSpawner.drivable)
|
||||
cfxGroundTroops.addGroundTroopsToPool(newTroops)
|
||||
|
||||
-- see if we have defined a target zone as destination
|
||||
@ -610,6 +620,7 @@ function cfxSpawnZones.loadData()
|
||||
local range = gdTroop.range
|
||||
local cty = gData.cty
|
||||
local cat = gData.cat
|
||||
local code = gdTroop.code
|
||||
|
||||
-- now spawn, but first
|
||||
-- add to my own attacker queue so we can save later
|
||||
|
||||
Binary file not shown.
BIN
tutorial & demo missions/demo - lazie troops.miz
Normal file
BIN
tutorial & demo missions/demo - lazie troops.miz
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user