mirror of
https://github.com/weyne85/DML.git
synced 2025-10-29 16:57:49 +00:00
Version 2.4.5
csarFX, dropFormation in heloTroops
This commit is contained in:
parent
2f80033077
commit
59d8bb30cf
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -1,5 +1,5 @@
|
||||
airtank = {}
|
||||
airtank.version = "1.0.1"
|
||||
airtank.version = "1.0.2"
|
||||
-- Module to extinguish fires controlled by the 'inferno' module.
|
||||
-- For 'airtank' fire extinguisher aircraft modules.
|
||||
airtank.requiredLibs = {
|
||||
@ -11,7 +11,7 @@ airtank.requiredLibs = {
|
||||
Version History
|
||||
1.0.0 - Initial release
|
||||
1.0.1 - removed attachTo: bug
|
||||
|
||||
1.0.2 - integration with fireCtrl.enabled
|
||||
--]]--
|
||||
|
||||
airtank.tanks = {} -- player data by GROUP name, will break with multi-unit groups
|
||||
@ -154,13 +154,16 @@ function airtank:onEvent(theEvent)
|
||||
if data.lastDeparture then -- and data.lastDeparture + 60 < now then
|
||||
return
|
||||
end
|
||||
data.lastDeparture = now
|
||||
if data.carrying < data.capacity * 0.5 then
|
||||
trigger.action.outTextForGroup(data.gID, "Good luck, " .. pName .. ", remember to top off your tanks before going in.", 30)
|
||||
else
|
||||
trigger.action.outTextForGroup(data.gID, "Good luck and godspeed, " .. pName .. "!", 30)
|
||||
end
|
||||
trigger.action.outSoundForGroup(data.gID, airtank.actionSound)
|
||||
data.lastDeparture = now
|
||||
if (not fireCtrl) or
|
||||
(fireCtrl and fireCtrl.enabled) then
|
||||
local msg = "Good luck, " .. pName .. ", remember to top off your tanks before going in."
|
||||
if data.carrying > data.capacity * 0.5 then
|
||||
msg = "Good luck and godspeed, " .. pName .. "!"
|
||||
end
|
||||
trigger.action.outTextForGroup(data.gID, msg, 30)
|
||||
trigger.action.outSoundForGroup(data.gID, airtank.actionSound)
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
358
modules/csarFX.lua
Normal file
358
modules/csarFX.lua
Normal file
@ -0,0 +1,358 @@
|
||||
csarFX = {}
|
||||
csarFX.version = "2.0.5"
|
||||
|
||||
--[[--
|
||||
VERSION HISTORY
|
||||
2.0.5 Initial Version
|
||||
|
||||
csarFX - makes better SAR and can turn SAR into CSAR
|
||||
Copyright (c) 2024-2025 by Christian Franz
|
||||
|
||||
WARNING:
|
||||
csarFX must run AFTER csarManager to install its callbacks, so
|
||||
any csar mission that runs earlier does NOT receive any csarFX adornments
|
||||
|
||||
--]]--
|
||||
|
||||
csarFX.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxZones", -- Zones, of course
|
||||
"csarManager",
|
||||
}
|
||||
-- static object type strings to add as debris csar missions placed on a road
|
||||
csarFX.roadDebbies = {"VAZ Car", "IKARUS Bus", "LAZ Bus", "LiAZ Bus", "GAZ-3307", "ZIL-131 KUNG", "MAZ-6303", "ZIL-4331", "Ural-375", "GAZ-3307"
|
||||
, "ZIL-131 KUNG", "MAZ-6303", "ZIL-4331", "Ural-375", "GAZ-3307",
|
||||
"VAZ Car",
|
||||
-- Massun's stuff
|
||||
"P20_01", "TugHarlan", "r11_volvo_drivable", }
|
||||
csarFX.landDebbies = {"Hummer", }
|
||||
csarFX.seaDebbies = {"speedboat","ZWEZDNY", "speedboat", --2x prob speed
|
||||
}
|
||||
csarFX.seaSpecials = {"Orca",}
|
||||
|
||||
--[[--
|
||||
theMission Data Structure:
|
||||
- zone: a trigger zone.
|
||||
- locations : {} array of locations for units in zone
|
||||
- name - mission name
|
||||
- group - all evacuees
|
||||
- side: coalition
|
||||
- missionID - id
|
||||
- timeStamp - when it was created
|
||||
- inPopulated - passed by csarManager zone creation
|
||||
--]]--
|
||||
|
||||
function csarFX.addRoadDebris(theMission, center) -- in vec2
|
||||
local cty = dcsCommon.getACountryForCoalition(0)
|
||||
local theStatic, x, z = dcsCommon.createStaticObjectForCoalitionInRandomRing(cty, dcsCommon.pickRandom(csarFX.roadDebbies), center.x, center.y, 5, 7, nil, true)
|
||||
table.insert(theMission.debris, theStatic)
|
||||
if math.random(1000) > 500 then
|
||||
-- two-car crash, high probability for fire
|
||||
local otherStatic = dcsCommon.createStaticObjectForCoalitionInRandomRing(cty, dcsCommon.pickRandom(csarFX.roadDebbies), x, z, 2, 4, nil, true)
|
||||
table.insert(theMission.debris, otherStatic)
|
||||
if math.random(1000) > 400 then
|
||||
local smokeType = 1
|
||||
if math.random(1000) > 500 then smokeType = 5 end
|
||||
local smokePoint = {x=x, y=land.getHeight({x=x, y=z}), z=z}
|
||||
trigger.action.effectSmokeBig(smokePoint, smokeType , 0.1 , theMission.name)
|
||||
table.insert(theMission.fires, theMission.name)
|
||||
end
|
||||
else
|
||||
if math.random(1000) > 800 then
|
||||
local smokeType = 1
|
||||
if math.random(1000) > 500 then smokeType = 5 end
|
||||
local smokePoint = {x=x, y=land.getHeight({x=x, y=z}), z=z}
|
||||
trigger.action.effectSmokeBig(smokePoint , smokeType , 0.1 , theMission.name)
|
||||
table.insert(theMission.fires, theMission.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function csarFX.addWaterDebris(theMission, center) -- in vec2
|
||||
local cty = dcsCommon.getACountryForCoalition(0)
|
||||
if math.random(1000) > 500 then -- 50% chance
|
||||
local theStatic, x, z = dcsCommon.createStaticObjectForCoalitionInRandomRing(cty, dcsCommon.pickRandom(csarFX.seaDebbies), center.x, center.y, 100, 200, nil, true)
|
||||
table.insert(theMission.debris, theStatic)
|
||||
end
|
||||
-- add an orca (very rare)
|
||||
if math.random(1000) < 10 then -- 1% chance for water
|
||||
local theStatic, x, z = dcsCommon.createStaticObjectForCoalitionInRandomRing(cty, dcsCommon.pickRandom(csarFX.seaSpecials), center.x, center.y, 30, 50, nil, true)
|
||||
table.insert(theMission.debris, theStatic)
|
||||
end
|
||||
end
|
||||
|
||||
function csarFX.localDebris(debris, center, dist, theZone, theMission)
|
||||
local cty = dcsCommon.getACountryForCoalition(theZone.csarSide)
|
||||
local theStatic, x, z = dcsCommon.createStaticObjectForCoalitionInRandomRing(cty, dcsCommon.pickRandom(debris), center.x, center.y, dist, 2*dist, nil, false) -- zed is ded
|
||||
table.insert(theMission.debris, theStatic)
|
||||
|
||||
local smokeType = 1
|
||||
if math.random(1000) > 500 then smokeType = 5 end
|
||||
local smokePoint = {x=x, y=land.getHeight({x=x, y=z}), z=z}
|
||||
trigger.action.effectSmokeBig(smokePoint, smokeType, 0.1 , theMission.name)
|
||||
table.insert(theMission.fires, theMission.name)
|
||||
end
|
||||
|
||||
function csarFX.addLandDebris(theMission, center) -- in vec2
|
||||
local cty = dcsCommon.getACountryForCoalition(0)
|
||||
if math.random(1000) > 500 then -- 50% chance
|
||||
local theStatic, x, z = dcsCommon.createStaticObjectForCoalitionInRandomRing(cty, dcsCommon.pickRandom(csarFX.landDebbies), center.x, center.y, 10, 15, nil, false) -- zed is ded
|
||||
table.insert(theMission.debris, theStatic)
|
||||
|
||||
if math.random(1000) > 500 then
|
||||
local smokeType = 1
|
||||
if math.random(1000) > 500 then smokeType = 5 end
|
||||
local smokePoint = {x=x, y=land.getHeight({x=x, y=z}), z=z}
|
||||
trigger.action.effectSmokeBig(smokePoint, smokeType, 0.1 , theMission.name)
|
||||
table.insert(theMission.fires, theMission.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function csarFX.addPopulatedDebris(theMission, center)
|
||||
if math.random(1000) > 500 then
|
||||
local rp = dcsCommon.randomPointOnPerimeter(5, center.x, center.z)
|
||||
local smokeType = 1
|
||||
if math.random(1000) > 500 then smokeType = 5 end
|
||||
local smokePoint = {x=rp.x, y=land.getHeight({x=rp.x, y=rp.z}), z=rp.z}
|
||||
trigger.action.effectSmokeBig(smokePoint, smokeType, 0.1 , theMission.name)
|
||||
table.insert(theMission.fires, theMission.name)
|
||||
end
|
||||
end
|
||||
--
|
||||
-- mission created callback
|
||||
--
|
||||
function csarFX.missionCreatedCB(theMission, theZone)
|
||||
-- get location of first (usually only) evacuee
|
||||
local loc = {}
|
||||
loc.x = theMission.locations[1].x
|
||||
loc.y = 0
|
||||
loc.z = theMission.locations[1].z
|
||||
|
||||
loc.y = loc.z -- loc is now vec2!
|
||||
theMission.debris = {}
|
||||
theMission.fires = {}
|
||||
theMission.enemies = {}
|
||||
theMission.enemyNames = {}
|
||||
if theZone then
|
||||
if theZone.enemies then
|
||||
-- generate enemies
|
||||
local coa = dcsCommon.getEnemyCoalitionFor(theZone.csarSide)
|
||||
local cty = dcsCommon.getACountryForCoalition(coa)
|
||||
local numEnemies = dcsCommon.randomBetween(theZone.emin, theZone.emax)
|
||||
for i=1, numEnemies do
|
||||
local gName = dcsCommon.uuid("cesar")
|
||||
local gData = dcsCommon.createEmptyGroundGroupData (gName)
|
||||
local theType = dcsCommon.pickRandom(theZone.enemies)
|
||||
local range = dcsCommon.randomBetween(theZone.rmin, theZone.rmax)
|
||||
local p = dcsCommon.randomPointOnPerimeter(range, 0, 0)local uData = dcsCommon.createGroundUnitData(gName .. "-e", theType, false)
|
||||
local heading = math.random(360) * 0.0174533
|
||||
dcsCommon.addUnitToGroupData(uData, gData, 0, 0, heading)
|
||||
dcsCommon.moveGroupDataTo(gData, loc.x + p.x, loc.z + p.z)
|
||||
local theEnemies = coalition.addGroup(cty, Group.Category.GROUND, gData)
|
||||
if theEnemies then
|
||||
table.insert(theMission.enemies, theEnemies)
|
||||
local gNameS = theEnemies:getName()
|
||||
table.insert(theMission.enemyNames, gNameS)
|
||||
end
|
||||
end
|
||||
-- add a nasty if defined
|
||||
if theZone.nasties then
|
||||
local gName = dcsCommon.uuid("cesar-n")
|
||||
local gData = dcsCommon.createEmptyGroundGroupData (gName)
|
||||
local theType = dcsCommon.pickRandom(theZone.nasties)
|
||||
local range = dcsCommon.randomBetween(theZone.rmin, theZone.rmax)
|
||||
local p = dcsCommon.randomPointOnPerimeter(range, 0, 0)local uData = dcsCommon.createGroundUnitData(gName .. "-n", theType, false)
|
||||
local heading = math.random(360) * 0.0174533
|
||||
dcsCommon.addUnitToGroupData(uData, gData, 0, 0, heading)
|
||||
dcsCommon.moveGroupDataTo(gData, loc.x + p.x, loc.z + p.z)
|
||||
local theEnemies = coalition.addGroup(cty, Group.Category.GROUND, gData)
|
||||
if theEnemies then
|
||||
table.insert(theMission.enemies, theEnemies)
|
||||
local gNameS = theEnemies:getName()
|
||||
table.insert(theMission.enemyNames, gNameS)
|
||||
end
|
||||
end
|
||||
-- generate debris
|
||||
if theZone.debris then -- theZone:hasProperty("debris") then
|
||||
csarFX.localDebris(theZone.debris, loc, 1000, theZone, theMission)
|
||||
end
|
||||
return -- no further adornments
|
||||
end
|
||||
end
|
||||
-- see if this is a land or sea mission?
|
||||
-- access first unit's location
|
||||
local landType = land.getSurfaceType(loc)
|
||||
-- init debris and fires for house cleaning
|
||||
if theMission.inPopulated then
|
||||
-- theMission calls for in populated. create some marker
|
||||
-- directly next to the guy
|
||||
csarFX.addPopulatedDebris(theMission, loc)
|
||||
elseif landType == 3 then -- deep water
|
||||
csarFX.addWaterDebris(theMission, loc)
|
||||
elseif landType == 4 then -- road
|
||||
csarFX.addRoadDebris(theMission, loc)
|
||||
else -- anywhere else. Includes shallow water
|
||||
csarFX.addLandDebris(theMission, loc)
|
||||
end
|
||||
end
|
||||
|
||||
function csarFX.makeEnemiesCongregate(theMission)
|
||||
if not theMission.enemies then return end
|
||||
for idx, theEnemyName in pairs(theMission.enemyNames) do
|
||||
local theEnemy = Group.getByName(theEnemyName)
|
||||
if theEnemy then
|
||||
local loc = theMission.locations[1]
|
||||
if not loc then return end
|
||||
local p = {}
|
||||
p.x = loc.x
|
||||
p.y = 0
|
||||
p.z = loc.z
|
||||
if Group.isExist(theEnemy) then
|
||||
local here = dcsCommon.getGroupLocation(theEnemy, true, theEnemyName)
|
||||
if not here then
|
||||
trigger.action.outText("+++csFx: no (here) for <" .. theEnemyName .. ">, skipping.", 30)
|
||||
else
|
||||
cfxCommander.makeGroupGoThere(theEnemy, p, 4, nil, 5) -- 5 m/s = 18 kmh, NIL FORMATION, 5 seconds in the future
|
||||
end
|
||||
end
|
||||
else -- enemy gone
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function csarFX.smokeStartedCB(theMission, uName)
|
||||
if not csarFX.congregateOnSmoke then return end
|
||||
-- start congregation of units
|
||||
if theMission.enemies then
|
||||
csarFX.makeEnemiesCongregate(theMission)
|
||||
end
|
||||
end
|
||||
--
|
||||
-- evacuee picked up callback
|
||||
--
|
||||
function csarFX.PickUpCB(theMission)
|
||||
end
|
||||
--
|
||||
-- Mission completed callback
|
||||
--
|
||||
function csarFX.missionCompleteCB(theCoalition, success, numRescued, notes, theMission)
|
||||
if not success then
|
||||
-- schedule cleanup in the future
|
||||
timer.scheduleFunction(csarFX.doCleanUpMission, theMission, timer.getTime() + csarFX.cleanupDelay)
|
||||
return
|
||||
end
|
||||
csarFX.doCleanUpMission(theMission) -- clean up now.
|
||||
end
|
||||
-- synch/asynch call for cleaning up after Mission
|
||||
-- if mission isn't successful, we wait some time before
|
||||
-- we remove smoke, debris, enemies
|
||||
function csarFX.doCleanUpMission(theMission)
|
||||
-- deallocate scenery fx
|
||||
if theMission.debris then
|
||||
for idx, theDeb in pairs(theMission.debris) do
|
||||
if theDeb and Object.isExist(theDeb) then
|
||||
Object.destroy(theDeb)
|
||||
end
|
||||
end
|
||||
end
|
||||
if theMission.fires then
|
||||
for idx, theFlame in pairs(theMission.fires) do
|
||||
trigger.action.effectSmokeStop(theFlame)
|
||||
end
|
||||
end
|
||||
if theMission.enemies then
|
||||
for idx, theGroup in pairs(theMission.enemies) do
|
||||
if theGroup and Group.isExist(theGroup) then
|
||||
Group.destroy(theGroup)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- start and hook into csarManager
|
||||
function csarFX.readConfig()
|
||||
local theZone = cfxZones.getZoneByName("csarFXConfig")
|
||||
if not theZone then
|
||||
theZone = cfxZones.createSimpleZone("csarFXConfig")
|
||||
end
|
||||
csarFX.verbose = theZone.verbose
|
||||
--csarFX.landDebris = theZone:getBoolFromZoneProperty("landDebris", true)
|
||||
--csarFX.seaDebris = theZone:getBoolFromZoneProperty("seaDebris", true)
|
||||
--csarFX.roadDebris = theZone:getBoolFromZoneProperty("roadDebris", true)
|
||||
csarFX.congregateOnSmoke = theZone:getBoolFromZoneProperty("congregate", true)
|
||||
csarFX.cleanupDelay = theZone:getNumberFromZoneProperty("cleanupDelay", 10 * 60)
|
||||
if theZone:hasProperty("landTypes") then
|
||||
local hTypes = theZone:getStringFromZoneProperty("landTypes", "xxx")
|
||||
local typeArray = dcsCommon.splitString(hTypes, ",")
|
||||
typeArray = dcsCommon.trimArray(typeArray)
|
||||
csarFX.landDebbies = typeArray
|
||||
end
|
||||
if theZone:hasProperty("roadTypes") then
|
||||
local hTypes = theZone:getStringFromZoneProperty("roadTypes", "xxx")
|
||||
local typeArray = dcsCommon.splitString(hTypes, ",")
|
||||
typeArray = dcsCommon.trimArray(typeArray)
|
||||
csarFX.roadDebbies = typeArray
|
||||
end
|
||||
if theZone:hasProperty("seaTypes") then
|
||||
local hTypes = theZone:getStringFromZoneProperty("seaTypes", "xxx")
|
||||
local typeArray = dcsCommon.splitString(hTypes, ",")
|
||||
typeArray = dcsCommon.trimArray(typeArray)
|
||||
csarFX.seaDebbies = typeArray
|
||||
end
|
||||
end
|
||||
|
||||
function csarFX.amendCSARZones()
|
||||
-- process csar zones and amend the attributes
|
||||
local csarBases = cfxZones.zonesWithProperty("CSAR")
|
||||
-- now add all zones to my zones table, and init additional info
|
||||
-- from properties
|
||||
for k, theZone in pairs(csarBases) do
|
||||
if theZone:hasProperty("enemies") then
|
||||
local hTypes = theZone:getStringFromZoneProperty("enemies", "xxx")
|
||||
local typeArray = dcsCommon.splitString(hTypes, ",")
|
||||
typeArray = dcsCommon.trimArray(typeArray)
|
||||
theZone.enemies = typeArray
|
||||
end
|
||||
if theZone:hasProperty("nasties") then
|
||||
local hTypes = theZone:getStringFromZoneProperty("nasties", "xxx")
|
||||
local typeArray = dcsCommon.splitString(hTypes, ",")
|
||||
typeArray = dcsCommon.trimArray(typeArray)
|
||||
theZone.nasties = typeArray
|
||||
end
|
||||
local dmin, dmax = theZone:getPositiveRangeFromZoneProperty("range", 1) -- range of enemies
|
||||
theZone.rmin = dmin * 1000
|
||||
theZone.rmax = dmax * 1000
|
||||
local emin, emax = theZone:getPositiveRangeFromZoneProperty("strength", 1) -- number of enemies
|
||||
theZone.emin = emin
|
||||
theZone.emax = emax
|
||||
if theZone:hasProperty("debris") then
|
||||
local hTypes = theZone:getStringFromZoneProperty("debris", "xxx")
|
||||
local typeArray = dcsCommon.splitString(hTypes, ",")
|
||||
typeArray = dcsCommon.trimArray(typeArray)
|
||||
theZone.debris = typeArray
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function csarFX.start()
|
||||
if not dcsCommon.libCheck("cfx CSAR FX", csarFX.requiredLibs) then
|
||||
trigger.action.outText("cf/x CSAR FX aborted: missing libraries", 30)
|
||||
return
|
||||
end
|
||||
-- read config
|
||||
csarFX.readConfig()
|
||||
-- amend csarZones
|
||||
csarFX.amendCSARZones()
|
||||
-- install callbacks
|
||||
csarManager.installNewMissionCallback(csarFX.missionCreatedCB)
|
||||
csarManager.installPickupCallback(csarFX.PickUpCB)
|
||||
csarManager.installCallback(csarFX.missionCompleteCB)
|
||||
csarManager.installSmokeCallback(csarFX.smokeStartedCB)
|
||||
trigger.action.outText("csarFX v" .. csarFX.version .. " started", 30)
|
||||
end
|
||||
|
||||
csarFX.start()
|
||||
|
||||
--[[--
|
||||
to do: integrate with autoCSAR so if a plane gets shot down over a CSAR zone, that zone is triggered, and csarFX gets invoked as well.
|
||||
--]]--
|
||||
@ -1,5 +1,5 @@
|
||||
csarManager = {}
|
||||
csarManager.version = "4.3.0"
|
||||
csarManager.version = "4.4.0"
|
||||
csarManager.ups = 1
|
||||
|
||||
--[[-- VERSION HISTORY
|
||||
@ -11,10 +11,14 @@ csarManager.ups = 1
|
||||
4.2.1 - added Chinook to csar default set (via common)
|
||||
4.3.0 - pilot's smoke can now extinguish immediately
|
||||
- keepSmoke option
|
||||
4.4.0 - csarMissions created by autoCSAR can now trigger
|
||||
CSAR zones, which can in turn integrate with csarFX
|
||||
for enemies etc.
|
||||
|
||||
INTEGRATES AUTOMATICALLY WITH playerScore
|
||||
INTEGRATES WITH LIMITED AIRFRAMES
|
||||
INTEGRATES AUTOMATICALLY WITH SCRIBE
|
||||
INTEGRATES AUTOMATICALL WITH CSARFX
|
||||
SUPPORTS PERSISTENCE
|
||||
|
||||
--]]--
|
||||
@ -511,10 +515,9 @@ function csarManager.heloLanded(theUnit)
|
||||
|
||||
-- handle smoke
|
||||
if csarManager.keepSmoke then
|
||||
-- trigger.action.outText("keeping smokeName <" .. theMission.smokeName .. "> running", 30)
|
||||
|
||||
else
|
||||
trigger.action.effectSmokeStop(theMission.smokeName)
|
||||
-- trigger.action.outText("smokeName <" .. theMission.smokeName .. "> removed", 30)
|
||||
end
|
||||
|
||||
local args = {}
|
||||
@ -566,8 +569,7 @@ function csarManager.heloDeparted(theUnit)
|
||||
if not dcsCommon.isTroopCarrier(theUnit, csarManager.troopCarriers) then return end
|
||||
-- if we have timed extractions (i.e. not instantaneous),
|
||||
-- then we need to check if we take off after the timer runs out
|
||||
|
||||
|
||||
|
||||
-- when we take off, all that needs to be done is to change the state
|
||||
-- to airborne, and then set the status flag
|
||||
local conf = csarManager.getUnitConfig(theUnit)
|
||||
@ -607,7 +609,6 @@ function csarManager.airframeCrashed(theUnit)
|
||||
local theGroup = theUnit:getGroup()
|
||||
conf.id = theGroup:getID()
|
||||
-- may want to do something, for now just nothing
|
||||
|
||||
end
|
||||
|
||||
function csarManager.airframeDitched(theUnit)
|
||||
@ -622,7 +623,6 @@ function csarManager.airframeDitched(theUnit)
|
||||
if #conf.troopsOnBoard > 0 then
|
||||
-- this is where we can create a new CSAR mission
|
||||
trigger.action.outTextForCoalition(theSide, theUnit:getName() .. " abandoned while evacuating " .. #conf.troopsOnBoard .. " pilots. There many be survivors.", 30)
|
||||
-- trigger.action.outSoundForCoalition(conf.id, "Quest Snare 3.wav")
|
||||
for i=1, #conf.troopsOnBoard do
|
||||
local msn = conf.troopsOnBoard[i] -- picked up unit(s)
|
||||
local theRescuedPilot = msn.name
|
||||
@ -636,7 +636,6 @@ function csarManager.airframeDitched(theUnit)
|
||||
local myName = conf.name
|
||||
cargoSuper.removeAllMassForCargo(myName, "Evacuees") -- will allocate new empty table
|
||||
end
|
||||
|
||||
--
|
||||
--
|
||||
-- M E N U H A N D L I N G & R E S P O N S E
|
||||
@ -653,7 +652,6 @@ end
|
||||
|
||||
function csarManager.removeCommsFromConfig(conf)
|
||||
csarManager.clearCommsSubmenus(conf)
|
||||
|
||||
if conf.myMainMenu then
|
||||
missionCommands.removeItemForGroup(conf.id, conf.myMainMenu)
|
||||
conf.myMainMenu = nil
|
||||
@ -663,13 +661,11 @@ end
|
||||
function csarManager.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 = csarManager.getUnitConfig(theUnit)
|
||||
conf.id = id
|
||||
conf.unit = theUnit
|
||||
|
||||
csarManager.removeCommsFromConfig(conf)
|
||||
end
|
||||
|
||||
@ -821,7 +817,6 @@ function csarManager.doListCSARRequests(args)
|
||||
report = report .. "\n\nWARNING: NO CSAR BASES TO DELIVER EVACUEES TO"
|
||||
end
|
||||
report = report .. "\n"
|
||||
|
||||
trigger.action.outTextForGroup(conf.id, report, 30)
|
||||
trigger.action.outSoundForGroup(conf.id, csarManager.actionSound)
|
||||
end
|
||||
@ -863,10 +858,7 @@ function csarManager.doStatusCarrying(args)
|
||||
|
||||
report = report .. "\n\nTotal added weigth: " .. 10 + #conf.troopsOnBoard * csarManager.pilotWeight .. "kg"
|
||||
end
|
||||
|
||||
|
||||
report = report .. "\n"
|
||||
|
||||
trigger.action.outTextForGroup(conf.id, report, 30)
|
||||
trigger.action.outSoundForGroup(conf.id, csarManager.actionSound)
|
||||
end
|
||||
@ -881,23 +873,19 @@ function csarManager.unloadOne(args)
|
||||
local theUnit = conf.unit
|
||||
if not theUnit then return end -- ??
|
||||
if not Unit.isExist(theUnit) then return end
|
||||
|
||||
local myName = theUnit:getName()
|
||||
|
||||
local report = "NYI: unload one"
|
||||
|
||||
if theUnit:inAir() then
|
||||
report = "STRONGLY recommend we land first, sir!"
|
||||
trigger.action.outTextForGroup(conf.id, report, 30)
|
||||
trigger.action.outSoundForGroup(conf.id, csarManager.actionSound) -- "Quest Snare 3.wav")
|
||||
trigger.action.outSoundForGroup(conf.id, csarManager.actionSound)
|
||||
return
|
||||
end
|
||||
|
||||
if #conf.troopsOnBoard < 1 then
|
||||
report = "No evacuees on board."
|
||||
trigger.action.outTextForGroup(conf.id, report, 30)
|
||||
trigger.action.outSoundForGroup(conf.id, csarManager.actionSound) -- "Quest Snare 3.wav")
|
||||
|
||||
trigger.action.outSoundForGroup(conf.id, csarManager.actionSound)
|
||||
else
|
||||
-- simulate a crash but for one unit
|
||||
local theSide = theUnit:getCoalition()
|
||||
@ -911,12 +899,11 @@ function csarManager.unloadOne(args)
|
||||
--TODO: remove weight for this pilot!
|
||||
|
||||
trigger.action.outTextForCoalition(theSide, myName .. " has aborted evacuating " .. msn.name .. ". New CSAR available.", 30)
|
||||
trigger.action.outSoundForCoalition(theSide, csarManager.actionSound) -- "Quest Snare 3.wav")
|
||||
trigger.action.outSoundForCoalition(theSide, csarManager.actionSound)
|
||||
|
||||
-- recalc weight
|
||||
local totalMass = 10 + #conf.troopsOnBoard * csarManager.pilotWeight
|
||||
trigger.action.setUnitInternalCargo(myName, totalMass) -- 10 kg as empty + per-unit time people
|
||||
--trigger.action.outText("unit <" .. myName .. ">, internal cargo now <" .. totalMass .. ">kg", 30)
|
||||
end
|
||||
|
||||
end
|
||||
@ -1207,10 +1194,8 @@ function csarManager.update() -- every second
|
||||
csarMission.group = nil -- no more evacuees
|
||||
-- turn off smoke?
|
||||
if csarManager.keepSmoke then
|
||||
-- trigger.action.outText("keeping smokeName <" .. csarMission.smokeName .. "> running", 30)
|
||||
else
|
||||
trigger.action.effectSmokeStop(csarMission.smokeName)
|
||||
-- trigger.action.outText("smokeName <" .. csarMission.smokeName .. "> removed", 30)
|
||||
end
|
||||
needsGC = true -- need filtering missions
|
||||
|
||||
@ -1232,11 +1217,7 @@ function csarManager.update() -- every second
|
||||
end
|
||||
|
||||
trigger.action.outSoundForGroup(uID, csarManager.pickupSound)
|
||||
|
||||
--return -- we only ever rescue one
|
||||
end -- hovered long enough
|
||||
|
||||
-- return -- only ever one winch op
|
||||
end -- hovered long enough
|
||||
else -- too high for hover
|
||||
hoverMsg = "Evacuee " .. d * 1 .. "m on your " .. oclock .. " o'clock; land or descend to between 10 and 90 AGL for winching"
|
||||
csarMission.hoveringUnits[uName] = nil -- reset timer
|
||||
@ -1246,7 +1227,6 @@ function csarManager.update() -- every second
|
||||
csarMission.hoveringUnits[uName] = nil
|
||||
end
|
||||
trigger.action.outTextForGroup(uID, hoverMsg, 30, true)
|
||||
--return -- only ever one winch op
|
||||
else
|
||||
-- remove the hover indicator for this unit
|
||||
csarMission.hoveringUnits[uName] = nil
|
||||
@ -1275,8 +1255,6 @@ function csarManager.update() -- every second
|
||||
-- check if their flag value has changed
|
||||
if theZone.startCSAR then
|
||||
-- this should always be true, but you never know
|
||||
-- local currVal = theZone:getFlagValue(theZone.startCSAR)
|
||||
-- if currVal ~= theZone.lastCSARVal then
|
||||
if theZone:testZoneFlag(theZone.startCSAR, theZone.triggerMethod, "lastCSARVal") then
|
||||
local theMission = csarManager.createCSARMissionFromZone(theZone)
|
||||
csarManager.addMission(theMission, theZone)
|
||||
@ -1289,21 +1267,25 @@ function csarManager.update() -- every second
|
||||
end
|
||||
end
|
||||
|
||||
function csarManager.createCSARMissionFromZone(theZone)
|
||||
-- orides pos, name from parachuted msn
|
||||
function csarManager.createCSARMissionFromZone(theZone, pos, name)
|
||||
-- set up random point in zone
|
||||
local mPoint = theZone:getPoint()
|
||||
if theZone.rndLoc then mPoint = theZone:createRandomPointInZone() end
|
||||
if pos then mPoint = pos end -- WILL respect onRoad and inPopulated
|
||||
|
||||
if theZone.onRoad then
|
||||
mPoint.x, mPoint.z = land.getClosestPointOnRoads('roads',mPoint.x, mPoint.z)
|
||||
elseif theZone.inPopulated then
|
||||
local aPoint = theZone:createRandomPointInPopulatedZone(theZone.clearance) -- no more maxTries: theZone.maxTries)
|
||||
mPoint = aPoint -- safety in case we need to mod aPoint
|
||||
end
|
||||
if not name then name = theZone.csarName end -- respect oride
|
||||
local theMission = csarManager.createCSARMissionData(
|
||||
mPoint,
|
||||
theZone.csarSide, -- theSide
|
||||
theZone.csarFreq, -- freq
|
||||
theZone.csarName, -- name
|
||||
name, -- theZone.csarName, -- name
|
||||
theZone.numCrew, -- numCrew
|
||||
theZone.timeLimit, -- timeLimit
|
||||
theZone.csarMapMarker, -- mapMarker
|
||||
@ -1312,7 +1294,6 @@ function csarManager.createCSARMissionFromZone(theZone)
|
||||
theMission.inPopulated = theZone.inPopulated -- transfer for csarFX
|
||||
return theMission
|
||||
end
|
||||
|
||||
--
|
||||
-- create a CSAR Mission for a unit
|
||||
--
|
||||
@ -1320,12 +1301,9 @@ function csarManager.createCSARforUnit(theUnit, pilotName, radius, silent, score
|
||||
if not silent then silent = false end
|
||||
if not radius then radius = 1000 end
|
||||
if not pilotName then pilotName = "Eddie" end
|
||||
|
||||
local point = theUnit:getPoint()
|
||||
local coal = theUnit:getCoalition()
|
||||
|
||||
local csarPoint = dcsCommon.randomPointInCircle(radius, radius/2, point.x, point.z)
|
||||
|
||||
csarPoint.y = csarPoint.z
|
||||
local surf = land.getSurfaceType(csarPoint)
|
||||
csarPoint.y = land.getHeight(csarPoint)
|
||||
@ -1343,15 +1321,54 @@ function csarManager.createCSARforUnit(theUnit, pilotName, radius, silent, score
|
||||
csarManager.addMission(theMission)
|
||||
if not silent then
|
||||
trigger.action.outTextForCoalition(coal, "MAYDAY MAYDAY MAYDAY! ".. pilotName .. " in " .. theUnit:getTypeName() .. " ejected, report good chute. Prepare CSAR!", 30)
|
||||
trigger.action.outSoundForGroup(coal, csarManager.actionSound) -- "Quest Snare 3.wav")
|
||||
trigger.action.outSoundForGroup(coal, csarManager.actionSound)
|
||||
end
|
||||
end
|
||||
|
||||
function csarManager.createCSARForParachutist(theUnit, name, coa) -- invoked with parachute guy on ground as theUnit
|
||||
function csarManager.getClosestInZoneForCoa(point, theZones, coa)
|
||||
if not theZones then return nil end
|
||||
local lPoint = {x=point.x, y=0, z=point.z}
|
||||
local currDelta = math.huge
|
||||
local closestZone = nil
|
||||
for zName, zData in pairs(theZones) do
|
||||
if zData.csarSide == coa and zData.autoTrigger and zData:pointInZone(lPoint) then
|
||||
local zPoint = cfxZones.getPoint(zData)
|
||||
local delta = dcsCommon.dist(lPoint, zPoint)
|
||||
trigger.action.outText("delta is <" .. delta .. ">", 30)
|
||||
if (delta < currDelta) then
|
||||
currDelta = delta
|
||||
closestZone = zData
|
||||
end
|
||||
end
|
||||
end
|
||||
return closestZone
|
||||
end
|
||||
|
||||
function csarManager.createCSARForParachutist(theUnit, name, coa) -- invoked with parachute guy on ground as theUnit, usually from autoCSAR
|
||||
trigger.action.outText("Enter createCSARForParachutist", 30)
|
||||
if not coa then coa = theUnit:getCoalition() end
|
||||
local pos = theUnit:getPoint()
|
||||
-- unit DOES NOT HAVE GROUP!!! (unless water splashdown)
|
||||
-- create a CSAR mission now
|
||||
-- see if pilot is down in CSAR zone
|
||||
local theCsarZone = csarManager.getClosestInZoneForCoa(pos, csarManager.csarZones, coa)
|
||||
|
||||
if theCsarZone then
|
||||
-- we use this CSAR zone to generate a CSR at spot pos for coa
|
||||
if csarManager.verbose or theCsarZone.verbose then
|
||||
trigger.action.outText("+++CSAR: generating CSAR mission via para drop in zone <" .. theCsarZone.name .. ">", 30)
|
||||
end
|
||||
|
||||
local theMission = csarManager.createCSARMissionFromZone(theCsarZone, pos, name)
|
||||
csarManager.addMission(theMission, theCsarZone)
|
||||
--theZone.lastCSARVal = currVal
|
||||
if csarManager.verbose or theCsarZone.verbose then
|
||||
trigger.action.outText("+++csar: started parachuted CSAR mission for <" .. theCsarZone.csarName .. ">", 30)
|
||||
end
|
||||
trigger.action.outTextForCoalition(coa, "MAYDAY MAYDAY MAYDAY! ".. name .. " requesting extraction from hostile territory!", 30)
|
||||
trigger.action.outSoundForGroup(coa, csarManager.actionSound)
|
||||
return
|
||||
end
|
||||
local theMission = csarManager.createCSARMissionData(pos, coa, nil, name, nil, nil, nil, 0.1, nil)
|
||||
csarManager.addMission(theMission)
|
||||
trigger.action.outTextForCoalition(coa, "MAYDAY MAYDAY MAYDAY! ".. name .. " requesting extraction after eject!", 30)
|
||||
@ -1373,13 +1390,15 @@ end
|
||||
|
||||
function csarManager.addCSARZone(theZone)
|
||||
table.insert(csarManager.csarZones, theZone)
|
||||
if csarManager.verbose or theZone.verbose then
|
||||
trigger.action.outText("+++csar: added csar zone <" .. theZone.name .. ">", 30)
|
||||
end
|
||||
end
|
||||
|
||||
function csarManager.readCSARZone(theZone)
|
||||
-- zones have attribute "CSAR"
|
||||
-- gather data, and then create a mission from this
|
||||
local mName = theZone:getStringFromZoneProperty("CSAR", theZone.name)
|
||||
-- if mName == "" then mName = theZone.name end
|
||||
local theSide = theZone:getCoalitionFromZoneProperty("coalition", 0)
|
||||
theZone.csarSide = theSide
|
||||
theZone.csarName = mName -- now deprecating name attributes
|
||||
@ -1443,17 +1462,17 @@ function csarManager.readCSARZone(theZone)
|
||||
theZone.clearance = theZone:getNumberFromZoneProperty("inBuiltup", 10)
|
||||
end
|
||||
end
|
||||
-- maxTries is decommed
|
||||
-- theZone.maxTries = theZone:getNumberFromZoneProperty("maxTries", 20)
|
||||
|
||||
if theZone.onRoad and theZone.inPopulated then
|
||||
trigger.action.outText("warning: competing 'onRoad' and 'inPopulated' attributes in zone <" .. theZone.name .. ">. Using 'onRoad'.", 30)
|
||||
theZone.inPopulated = false
|
||||
end
|
||||
|
||||
theZone.autoTrigger = theZone:getBoolFromZoneProperty("autoTrigger", true) -- autoCSAR coop
|
||||
local isAutoCSAR = theZone.autoTrigger
|
||||
if deferred and not theZone.startCSAR then isAutoCSAR = true end
|
||||
-- add to list of startable csar
|
||||
if theZone.startCSAR then
|
||||
if persistence and persistence.hasDate then
|
||||
if theZone.startCSAR or isAutoCSAR then
|
||||
if persistence and persistence.hasData then
|
||||
-- we load data instead of spawning on start
|
||||
else
|
||||
csarManager.addCSARZone(theZone)
|
||||
@ -1465,10 +1484,13 @@ function csarManager.readCSARZone(theZone)
|
||||
csarManager.addMission(theMission, theZone)
|
||||
end
|
||||
|
||||
|
||||
if deferred and not theZone.startCSAR then
|
||||
trigger.action.outText("+++csar: warning - CSAR Mission in Zone <" .. theZone.name .. "> can't be started", 30)
|
||||
if isAutoCSAR then
|
||||
trigger.action.outText("+++csar: warning - CSAR Mission in Zone <" .. theZone.name .. "> can only be started by autoCSAR", 30)
|
||||
end
|
||||
|
||||
if csarManager.verbose or theZone.verbose then
|
||||
trigger.action.outText("+++csar: processed CSAR zone <" .. theZone.name .. ">", 30)
|
||||
end
|
||||
end
|
||||
|
||||
function csarManager.processCSARZones()
|
||||
|
||||
@ -1630,7 +1630,7 @@ end
|
||||
-- formations:
|
||||
-- (default) "line" (left to right along x) -- that is Y direction
|
||||
-- "line_v" a line top to bottom -- that is X direction
|
||||
-- "chevron" - left to right middle too top
|
||||
-- "chevron" - left to right middle to top
|
||||
-- "scattered", "random" -- random, innerRadius used to clear area in center
|
||||
-- "circle", "circle_forward" -- circle, forward facing
|
||||
-- "circle_in" -- circle, inwarf facing
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
fireCtrl = {}
|
||||
fireCtrl.version = "1.1.0"
|
||||
fireCtrl.version = "1.2.0"
|
||||
fireCtrl.requiredLibs = {
|
||||
"dcsCommon",
|
||||
"cfxZones",
|
||||
@ -24,6 +24,9 @@ fireCtrl.roots = {}
|
||||
- notifications attribute
|
||||
- cleanup
|
||||
- UI attribute
|
||||
1.2.0 - onStart attribute
|
||||
ctrOn? attribute
|
||||
ctrOff? attribute
|
||||
--]]--
|
||||
|
||||
function fireCtrl.checkinPlayer(pName, gName, uName, uType)
|
||||
@ -124,7 +127,6 @@ function fireCtrl.redirectAction (args)
|
||||
end
|
||||
|
||||
function fireCtrl.doStatus(args)
|
||||
-- trigger.action.outText("status call", 30)
|
||||
local gName = args[1]
|
||||
local uName = args[2]
|
||||
local gID = args[3]
|
||||
@ -236,6 +238,25 @@ end
|
||||
|
||||
function fireCtrl.update()
|
||||
timer.scheduleFunction(fireCtrl.update, {}, timer.getTime() + 1/fireCtrl.ups)
|
||||
|
||||
-- see if on/off
|
||||
if fireCtrl.ctrOn and cfxZones.testZoneFlag(fireCtrl, fireCtrl.ctrOn, fireCtrl.method, "lastCtrOn") then
|
||||
fireCtrl.enabled = true
|
||||
if fireCtrl.verbose then
|
||||
trigger.action.outText("+++fCtrl: turning fire control on.", 30)
|
||||
end
|
||||
end
|
||||
|
||||
if fireCtrl.ctrOff and cfxZones.testZoneFlag(fireCtrl, fireCtrl.ctrOff, fireCtrl.method, "lastCtrOff") then
|
||||
fireCtrl.enabled = false
|
||||
if fireCtrl.verbose then
|
||||
trigger.action.outText("+++fCtrl: turning fire control OFF.", 30)
|
||||
end
|
||||
end
|
||||
|
||||
-- are we on?
|
||||
if not fireCtrl.enabled then return end
|
||||
|
||||
-- check the numbers of fires burning
|
||||
local f = 0
|
||||
local cells = 0
|
||||
@ -302,6 +323,8 @@ function fireCtrl.saveData()
|
||||
-- save current heroes. simple clone
|
||||
local theHeroes = dcsCommon.clone(fireCtrl.heroes)
|
||||
theData.theHeroes = theHeroes
|
||||
theData.hasEnabled = true
|
||||
theData.enabled = fireCtrl.enabled
|
||||
return theData, fireCtrl.sharedData -- second val only if shared
|
||||
end
|
||||
|
||||
@ -316,6 +339,9 @@ function fireCtrl.loadData()
|
||||
end
|
||||
local theHeroes = theData.theHeroes
|
||||
fireCtrl.heroes = theHeroes
|
||||
if theData.hasEnabled then
|
||||
fireCtrl.enabled = theData.enabled
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
@ -340,6 +366,21 @@ function fireCtrl.readConfigZone()
|
||||
fireCtrl.notifications = theZone:getBoolFromZoneProperty("notifications", true)
|
||||
fireCtrl.UI = theZone:getBoolFromZoneProperty("UI", true)
|
||||
|
||||
fireCtrl.enabled = theZone:getBoolFromZoneProperty("onStart", true)
|
||||
if theZone:hasProperty("ctrOn?") then
|
||||
fireCtrl.ctrOn = theZone:getStringFromZoneProperty("ctrOn?", "<none>")
|
||||
fireCtrl.lastCtrOn = trigger.misc.getUserFlag(fireCtrl.ctrOn)
|
||||
end
|
||||
if not fireCtrl.enabled and not fireCtrl.ctrOn then
|
||||
trigger.action.outText("***WARNING: fireCtrl cannot be turned on!", 30)
|
||||
end
|
||||
|
||||
if theZone:hasProperty("ctrOff?") then
|
||||
fireCtrl.ctrOff = theZone:getStringFromZoneProperty("ctrOff?", "<none>")
|
||||
fireCtrl.lastCtrOff = trigger.misc.getUserFlag(fireCtrl.ctrOff)
|
||||
end
|
||||
fireCtrl.method = theZone:getStringFromZoneProperty("method", "change")
|
||||
|
||||
if theZone:hasProperty("attachTo:") then
|
||||
local attachTo = theZone:getStringFromZoneProperty("attachTo:", "<none>")
|
||||
if radioMenu then -- requires optional radio menu to have loaded
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxHeloTroops = {}
|
||||
cfxHeloTroops.version = "4.2.3"
|
||||
cfxHeloTroops.version = "5.0.0"
|
||||
cfxHeloTroops.verbose = false
|
||||
cfxHeloTroops.autoDrop = true
|
||||
cfxHeloTroops.autoPickup = false
|
||||
@ -24,6 +24,13 @@ cfxHeloTroops.requestRange = 500 -- meters
|
||||
4.2.2 - support for attachTo:
|
||||
4.2.3 - dropZone supports 'keepWait' attribute
|
||||
- dropZone supports 'setWait' attribute
|
||||
4.2.4 - dropWait is always active outside of drop zones
|
||||
5.0.0 - drop options and formation menus. Supported formations
|
||||
circle_out
|
||||
chevron
|
||||
line left
|
||||
line right
|
||||
scattered behind
|
||||
|
||||
--]]--
|
||||
cfxHeloTroops.minTime = 3 -- seconds beween tandings
|
||||
@ -42,6 +49,27 @@ cfxHeloTroops.dropZones = {} -- dict
|
||||
-- persistence support
|
||||
cfxHeloTroops.deployedTroops = {}
|
||||
|
||||
--
|
||||
-- drop formation helpers
|
||||
--
|
||||
function cfxHeloTroops.formation2text(inFormation)
|
||||
if inFormation == "circle_out" then return "Circle Around" end
|
||||
if inFormation == "chevron" then return "Chevron in Front" end
|
||||
if inFormation == "lineLeft" then return "Line to Port" end
|
||||
if inFormation == "lineRight" then return "Line to Starboard" end
|
||||
if inFormation == "gaggle" then return "Gaggle Behind" end
|
||||
return "ErrFormation"
|
||||
end
|
||||
|
||||
function cfxHeloTroops.formation2dml(inFormation) -- returns formation, delta, phi
|
||||
if inFormation == "circle_out" then return "circle_out", 0, 0 end
|
||||
if inFormation == "chevron" then return "chevron", 0, 0 end
|
||||
if inFormation == "lineLeft" then return "line_v", 12, 4.71239 end -- 4.71239 is 270 degrees
|
||||
if inFormation == "lineRight" then return "line_v", 12, 1.57079633 end -- 1.57079633 is 90 degrees
|
||||
if inFormation == "gaggle" then return "scattered", 24, 3.14159265 end -- 3.14159265 is pi is 180 degrees
|
||||
trigger.action.outText("+++heloT: unknown drop formation <>, using circle_out, 0 ,0", 30)
|
||||
return "circle_out", 0, 0
|
||||
end
|
||||
--
|
||||
-- drop zones
|
||||
--
|
||||
@ -68,7 +96,7 @@ function cfxHeloTroops.resetConfig(conf)
|
||||
-- the other fields info for troops picked up
|
||||
conf.troopsOnBoard = {} -- table with the following
|
||||
conf.troopsOnBoard.name = "***reset***"
|
||||
conf.dropFormation = "circle_out" -- may be chosen later?
|
||||
conf.dropFormation = "circle_out" -- used to derive formation, delta, phi in formation2dml, use formation2text for text representation
|
||||
conf.timeStamp = timer.getTime() -- to avoid double-dipping
|
||||
end
|
||||
|
||||
@ -76,7 +104,7 @@ 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
|
||||
conf.myCommands = nil -- this is where we put all commands in. Why?
|
||||
return conf
|
||||
end
|
||||
|
||||
@ -256,13 +284,19 @@ function cfxHeloTroops.removeComms(theUnit)
|
||||
end
|
||||
|
||||
function cfxHeloTroops.addConfigMenu(conf)
|
||||
-- we add a menu showing current configs
|
||||
-- we add a menu for auto-drop-off and drop formation
|
||||
-- trigger.action.outText("enter addConfigMenu for <" .. conf.unit:getName() .. ">", 30)
|
||||
if conf.myDeployMenu then
|
||||
missionCommands.removeItemForGroup(conf.id, conf.myDeployMenu)
|
||||
end
|
||||
conf.myDeployMenu = missionCommands.addSubMenuForGroup(conf.id, 'Deployment Options', conf.myMainMenu)
|
||||
|
||||
local onOff = "OFF"
|
||||
if conf.autoDrop then onOff = "ON" end
|
||||
local theCommand = missionCommands.addCommandForGroup(
|
||||
conf.id,
|
||||
'Auto-Drop: ' .. onOff .. ' - Select to change',
|
||||
conf.myMainMenu,
|
||||
conf.myDeployMenu,
|
||||
cfxHeloTroops.redirectToggleConfig,
|
||||
{conf, "drop"}
|
||||
)
|
||||
@ -272,11 +306,53 @@ function cfxHeloTroops.addConfigMenu(conf)
|
||||
theCommand = missionCommands.addCommandForGroup(
|
||||
conf.id,
|
||||
'Auto-Pickup: ' .. onOff .. ' - Select to change',
|
||||
conf.myMainMenu,
|
||||
conf.myDeployMenu,
|
||||
cfxHeloTroops.redirectToggleConfig,
|
||||
{conf, "pickup"}
|
||||
)
|
||||
table.insert(conf.myCommands, theCommand)
|
||||
|
||||
if conf.myFormationMenu then
|
||||
missionCommands.removeItemForGroup(conf.id, conf.myFormationMenu)
|
||||
end
|
||||
conf.myFormationMenu = missionCommands.addSubMenuForGroup(conf.id, "Set Formation (" .. cfxHeloTroops.formation2text(conf.dropFormation) .. ")", conf.myDeployMenu)
|
||||
|
||||
theCommand = missionCommands.addCommandForGroup(
|
||||
conf.id,
|
||||
'Circle Around',
|
||||
conf.myFormationMenu,
|
||||
cfxHeloTroops.redirectDropFormation,
|
||||
{conf, "circle_out"}
|
||||
)
|
||||
theCommand = missionCommands.addCommandForGroup(
|
||||
conf.id,
|
||||
'Chevron in Front',
|
||||
conf.myFormationMenu,
|
||||
cfxHeloTroops.redirectDropFormation,
|
||||
{conf, "chevron"}
|
||||
)
|
||||
theCommand = missionCommands.addCommandForGroup(
|
||||
conf.id,
|
||||
'Line to Port',
|
||||
conf.myFormationMenu,
|
||||
cfxHeloTroops.redirectDropFormation,
|
||||
{conf, "lineLeft"}
|
||||
)
|
||||
theCommand = missionCommands.addCommandForGroup(
|
||||
conf.id,
|
||||
'Line to Starboard',
|
||||
conf.myFormationMenu,
|
||||
cfxHeloTroops.redirectDropFormation,
|
||||
{conf, "lineRight"}
|
||||
)
|
||||
theCommand = missionCommands.addCommandForGroup(
|
||||
conf.id,
|
||||
'Gaggle Behind',
|
||||
conf.myFormationMenu,
|
||||
cfxHeloTroops.redirectDropFormation,
|
||||
{conf, "gaggle"}
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
function cfxHeloTroops.setCommsMenu(theUnit)
|
||||
@ -618,6 +694,23 @@ function cfxHeloTroops.doToggleConfig(args)
|
||||
cfxHeloTroops.setCommsMenu(conf.unit)
|
||||
end
|
||||
|
||||
--
|
||||
-- set formation
|
||||
--
|
||||
function cfxHeloTroops.redirectDropFormation(args)
|
||||
timer.scheduleFunction(cfxHeloTroops.doDropFormation, args, timer.getTime() + 0.1)
|
||||
end
|
||||
|
||||
function cfxHeloTroops.doDropFormation(args)
|
||||
local conf = args[1]
|
||||
local newFormation = args[2]
|
||||
conf.dropFormation = newFormation
|
||||
if cfxHeloTroops.verbose then
|
||||
trigger.action.outText("Switching <" .. conf.unit:getName() .. ">'s troop deploy formation to <" .. newFormation .. ">", 40)
|
||||
end
|
||||
cfxHeloTroops.setCommsMenu(conf.unit)
|
||||
end
|
||||
|
||||
--
|
||||
-- Deploying Troops
|
||||
--
|
||||
@ -725,6 +818,8 @@ function cfxHeloTroops.deployTroopsFromHelicopter(conf)
|
||||
if closestDropZone.dropCoa == 0 or closestDropZone.dropCoa == theCoalition then
|
||||
if not closestDropZone.keepWait then dropWait = true end
|
||||
end
|
||||
else
|
||||
dropWait = true -- outside of any drop zones
|
||||
end
|
||||
-- see if we are in a drop zone
|
||||
if dropWait then
|
||||
@ -746,7 +841,16 @@ function cfxHeloTroops.deployTroopsFromHelicopter(conf)
|
||||
trigger.action.outTextForGroup(conf.id, "+++ <" .. conf.troopsOnBoard.name .. "> added 'wait' orders: <".. orders .. ">", 30)
|
||||
else trigger.action.outTextForGroup(conf.id, "+++ <" .. conf.troopsOnBoard.name .. "> keeping orders (".. orders .. ")", 30) end
|
||||
end
|
||||
|
||||
-- calculate drop point using delta, phi and unit heading
|
||||
local f = "circle_out"
|
||||
local delta = 0
|
||||
local phi = 0
|
||||
f, delta, phi = cfxHeloTroops.formation2dml(conf.dropFormation)
|
||||
if cfxHeloTroops.verbose then
|
||||
trigger.action.outText("formation: <" .. f .. ">, delta <" .. delta .. ">, phi <" .. phi .. ">", 30)
|
||||
end
|
||||
local uh = dcsCommon.getUnitHeading(theUnit)
|
||||
p = dcsCommon.pointInDirectionOfPointXYY(uh + phi, delta, p)
|
||||
local chopperZone = cfxZones.createSimpleZone("choppa", p, 12) -- 12 m radius around choppa
|
||||
|
||||
local theGroup, theData = cfxZones.createGroundUnitsInZoneForCoalition (
|
||||
@ -754,8 +858,8 @@ function cfxHeloTroops.deployTroopsFromHelicopter(conf)
|
||||
theName, -- group name, may be tracked
|
||||
chopperZone,
|
||||
unitTypes,
|
||||
conf.dropFormation,
|
||||
90,
|
||||
f, --conf.dropFormation,
|
||||
uh * 57.2958, -- heading in degrees, may need a formation offset like 90
|
||||
nil, -- liveries not yet supported
|
||||
canDrive)
|
||||
-- persistence management
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxPlayerScore = {}
|
||||
cfxPlayerScore.version = "5.2.1"
|
||||
cfxPlayerScore.version = "5.2.2"
|
||||
cfxPlayerScore.name = "cfxPlayerScore" -- compatibility with flag bangers
|
||||
cfxPlayerScore.firstSave = true -- to force overwrite
|
||||
--[[-- VERSION HISTORY
|
||||
@ -19,6 +19,8 @@ cfxPlayerScore.firstSave = true -- to force overwrite
|
||||
5.2.1 - Event 20 (CA join) corrected typo
|
||||
- wiping score on enter and birth
|
||||
- more robust initscore
|
||||
5.2.2 - fixed typo in feat zone
|
||||
|
||||
TODO: Kill event no longer invoked for map objetcs, attribute
|
||||
to faction now, reverse invocation direction with PlayerScore
|
||||
TODO: better wildcard support for kill events
|
||||
@ -81,7 +83,7 @@ function cfxPlayerScore.addFeatZone(theZone)
|
||||
theZone.featType = "KILL"
|
||||
end
|
||||
theZone.featDesc = theZone:getStringFromZoneProperty("description", "(some feat)")
|
||||
theZone.featNum = ctheZone:getNumberFromZoneProperty("awardLimit", -1) -- how many times this can be awarded, -1 is infinite
|
||||
theZone.featNum = theZone:getNumberFromZoneProperty("awardLimit", -1) -- how many times this can be awarded, -1 is infinite
|
||||
theZone.ppOnce = theZone:getBoolFromZoneProperty("awardOnce", false)
|
||||
theZone.awardedTo = {} -- by player name: true/false
|
||||
table.insert(cfxPlayerScore.featZones, theZone)
|
||||
|
||||
@ -1,9 +1,17 @@
|
||||
sittingDucks = {}
|
||||
sittingDucks.verbose = false
|
||||
sittingDucks.version = "1.0.0"
|
||||
sittingDucks.version = "1.0.1"
|
||||
sittingDucks.ssbDisabled = 100 -- must match the setting of SSB, usually 100
|
||||
sittingDucks.resupplyTime = -1 -- seconds until "reinforcements" reopen the slot, set to -1 to turn off, 3600 is one hour
|
||||
|
||||
--[[
|
||||
Version History
|
||||
|
||||
1.0.0 Initial Version
|
||||
1.0.1 DCS releases 2024-jul-11 and 2024-jul-22 bugs hardening
|
||||
|
||||
--]]--
|
||||
|
||||
--
|
||||
-- Destroying a client stand-in on an airfield will block that
|
||||
-- Slot for players. Multiplayer only
|
||||
@ -22,6 +30,7 @@ function sittingDucks:onEvent(event)
|
||||
-- home in on the kill event
|
||||
if event.id == 8 then -- dead event
|
||||
local theUnit = event.initiator
|
||||
if not theUnit.getName then return end -- dcs jul-11 and jul-22 bugs
|
||||
local deadName = theUnit:getName()
|
||||
if not deadName then return end
|
||||
-- look at stopGap's collection of stand-ins
|
||||
|
||||
@ -1,14 +1,15 @@
|
||||
cfxSmokeZone = {}
|
||||
cfxSmokeZone.version = "3.0.0"
|
||||
cfxSmokeZone.version = "3.0.1"
|
||||
cfxSmokeZone.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxZones", -- Zones, of course
|
||||
}
|
||||
--[[--
|
||||
--[[-- Copyright (c) 2021-2025 by Christian Franz
|
||||
Version History
|
||||
3.0.0 - now supports immediate smoke stop
|
||||
- supports persistence
|
||||
- code cleanup
|
||||
3.0.1 - fixed data load error
|
||||
--]]--
|
||||
cfxSmokeZone.smokeZones = {}
|
||||
cfxSmokeZone.updateDelay = 5 * 60 -- every 5 minutes
|
||||
@ -143,7 +144,7 @@ function cfxSmokeZone.start()
|
||||
callbacks.persistData = cfxSmokeZone.saveData
|
||||
persistence.registerModule("smokeZones", callbacks)
|
||||
-- now load my data
|
||||
persistence.loadData() -- will start with update
|
||||
cfxSmokeZone.loadData() -- will start with update
|
||||
end
|
||||
-- start update and checkflag loops
|
||||
cfxSmokeZone.update() -- also starts all unpaused
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
unitZone={}
|
||||
unitZone.version = "2.0.1"
|
||||
unitZone.version = "2.0.2"
|
||||
unitZone.verbose = false
|
||||
unitZone.ups = 1
|
||||
unitZone.requiredLibs = {
|
||||
@ -18,6 +18,7 @@ unitZone.requiredLibs = {
|
||||
- filter synonym
|
||||
- direct#, directInv# synonyms
|
||||
2.0.1 - code hardening
|
||||
2.0.2 - removed backward compat code for deprecated coalition, uzcoalition attributes
|
||||
--]]--
|
||||
|
||||
unitZone.unitZones = {}
|
||||
@ -81,13 +82,14 @@ function unitZone.createUnitZone(theZone)
|
||||
|
||||
-- coalition
|
||||
theZone.uzCoalition = theZone:getCoalitionFromZoneProperty("unitZone", 0) -- now with main attribute
|
||||
-- DEPRECATED 2023 SEPT: provided for legacy compatibility
|
||||
--[[-- DEPRECATED 2023 SEPT: provided for legacy compatibility
|
||||
if theZone:hasProperty("coalition") then
|
||||
theZone.uzCoalition = theZone:getCoalitionFromZoneProperty("coalition", 0) -- 0 = all
|
||||
elseif theZone:hasProperty("uzCoalition") then
|
||||
theZone.uzCoalition = theZone:getCoalitionFromZoneProperty("uzCoalition", 0)
|
||||
end
|
||||
|
||||
REMOVED 20250308
|
||||
--]]--
|
||||
-- DML Method
|
||||
theZone.uzMethod = theZone:getStringFromZoneProperty("method", "inc")
|
||||
if theZone:hasProperty("uzMethod") then
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user