mirror of
https://github.com/weyne85/DML.git
synced 2025-10-29 16:57:49 +00:00
Version 2.2.1
csarManager enhancements etc.
This commit is contained in:
parent
3111d2e804
commit
8f7371825d
Binary file not shown.
Binary file not shown.
@ -1,5 +1,5 @@
|
||||
cfxZones = {}
|
||||
cfxZones.version = "4.3.0"
|
||||
cfxZones.version = "4.3.1"
|
||||
|
||||
-- cf/x zone management module
|
||||
-- reads dcs zones and makes them accessible and mutable
|
||||
@ -46,6 +46,8 @@ cfxZones.version = "4.3.0"
|
||||
- 4.1.2 - hash property missing warning
|
||||
- 4.2.0 - new createRandomPointInPopulatedZone()
|
||||
- 4.3.0 - boolean supports maybe, random, rnd, ?
|
||||
- small optimization for randomInRange()
|
||||
- randomDelayFromPositiveRange also allows 0
|
||||
|
||||
--]]--
|
||||
|
||||
@ -977,7 +979,7 @@ function cfxZones.isGroupPartiallyInZone(aGroup, aZone)
|
||||
if not aGroup:isExist() then return false end
|
||||
local allUnits = aGroup:getUnits()
|
||||
for uk, aUnit in pairs (allUnits) do
|
||||
if aUnit:isExist() and aUnit:getLife() > 1 then
|
||||
if Unit.isExist(aUnit) and aUnit:getLife() > 1 then
|
||||
local p = aUnit:getPoint()
|
||||
local inzone, percent, dist = cfxZones.pointInZone(p, aZone)
|
||||
if inzone then
|
||||
@ -2244,8 +2246,9 @@ end
|
||||
function cfxZones.randomDelayFromPositiveRange(minVal, maxVal) -- should be moved to dcsCommon
|
||||
if not maxVal then return minVal end
|
||||
if not minVal then return maxVal end
|
||||
if minVal == maxVal then return minVal end
|
||||
local delay = maxVal
|
||||
if minVal > 0 and minVal < delay then
|
||||
if minVal >= 0 and minVal < delay then
|
||||
-- we want a randomized from time from minTime .. delay
|
||||
local varPart = delay - minVal + 1
|
||||
varPart = dcsCommon.smallRandom(varPart) - 1
|
||||
|
||||
199
modules/clients.lua
Normal file
199
modules/clients.lua
Normal file
@ -0,0 +1,199 @@
|
||||
clients = {}
|
||||
clients.version = "0.0.0"
|
||||
clients.ups = 1
|
||||
clients.verbose = false
|
||||
clients.netlog = true
|
||||
clients.players = {}
|
||||
-- player entry: indexed by name
|
||||
-- playerName - name of player, same as index
|
||||
-- uName = unit name
|
||||
-- coa = coalition
|
||||
-- connected = true/false is currently ingame
|
||||
|
||||
function clients.out(msg)
|
||||
-- do some preprocessing?
|
||||
if clients.verbose then
|
||||
trigger.action.outText(msg, 30)
|
||||
end
|
||||
-- add to own log?
|
||||
if clients.netlog then
|
||||
env.info(msg)
|
||||
end
|
||||
end
|
||||
|
||||
--[[--
|
||||
Event ID:
|
||||
1 = player enters mission for first time
|
||||
2 = player enters unit
|
||||
3 = player leaves unit
|
||||
4 = player changes unit
|
||||
5 = player changes coalition
|
||||
|
||||
|
||||
Sequence of events
|
||||
Player enters mission first time
|
||||
- player enters mission (new player) ID = 1
|
||||
- player enters unit ID = 2
|
||||
|
||||
Player is no longer active (their unit is gone)
|
||||
- player leaves unit ID = 3
|
||||
|
||||
Player enters unit after having already been in the mission
|
||||
- (player changes coalition) if unit belongs to different coa
|
||||
- (player changes unit if unit different than before)
|
||||
- player enters unit
|
||||
|
||||
--]]--
|
||||
--
|
||||
-- client events
|
||||
--
|
||||
clients.cb = {} -- profile = (id, this, last)
|
||||
function clients.invokeCallbacks(ID, this, last)
|
||||
for idx, cb in pairs(clients.cb) do
|
||||
cb(ID, this, last)
|
||||
end
|
||||
end
|
||||
|
||||
function clients.addCallback(theCB)
|
||||
table.insert(clients.cb, theCB)
|
||||
end
|
||||
|
||||
|
||||
function clients.playerEnteredMission(thisTime)
|
||||
clients.out("clients: Player <" .. thisTime.playerName .. "> enters mission for the first time")
|
||||
clients.invokeCallbacks(1, thisTime)
|
||||
end
|
||||
|
||||
function clients.playerEnteredUnit(thisTime)
|
||||
-- called when player enters a unit
|
||||
clients.out("clients: Player <" .. thisTime.playerName .. "> enters Unit <" .. thisTime.uName .. ">.")
|
||||
clients.invokeCallbacks(2, thisTime)
|
||||
end
|
||||
|
||||
function clients.playerLeavesUnit(lastTime)
|
||||
-- called when player leaves a unit
|
||||
clients.out("clients: Player <" .. lastTime.playerName .. "> leaves Unit <" .. lastTime.uName .. ">.")
|
||||
clients.invokeCallbacks(3, lastTime)
|
||||
end
|
||||
|
||||
function clients.playerChangedUnits(thisTime, lastTime)
|
||||
-- called when player enters a different unit
|
||||
clients.out("clients: Player <" .. thisTime.playerName .. "> changes from Unit <" .. lastTime.uName .. "> to NEW unit <" .. thisTime.uName .. ">.")
|
||||
clients.invokeCallbacks(4, thisTime, lastTime)
|
||||
end
|
||||
|
||||
function clients.playerChangedCoalition(thisTime, lastTime)
|
||||
-- called when player enters a different unit
|
||||
clients.out("clients: Player <" .. thisTime.playerName .. "> changes from coalition <" .. lastTime.coa .. "> to NEW coalition <" .. thisTime.coa .. ">.")
|
||||
clients.invokeCallbacks(4, thisTime, lastTime)
|
||||
end
|
||||
|
||||
-- check all connected player units
|
||||
function clients.compareStatus(thisTime, lastTime)
|
||||
if lastTime then
|
||||
-- they were known last time I checked. see if they were in-game
|
||||
if thisTime.connected == lastTime.connected then
|
||||
-- status is the same as before
|
||||
else
|
||||
-- player entered or left mission, and was known last time
|
||||
if thisTime.connected then
|
||||
-- player connected but was known, do nothing
|
||||
else
|
||||
-- player left mission. do we want to record this?
|
||||
end
|
||||
end
|
||||
-- check if they have the same unit name
|
||||
-- if not, check if they have changed coas
|
||||
if lastTime.uName == thisTime.uName then
|
||||
-- same unit, all is fine
|
||||
else
|
||||
-- new unit. check if same side
|
||||
if lastTime.coa == thisTime.coa then
|
||||
-- player stayed in same coa
|
||||
else
|
||||
-- player changed coalition
|
||||
clients.playerChangedCoalition(thisTime, lastTime)
|
||||
end
|
||||
clients.playerEnteredUnit(thisTime)
|
||||
clients.playerChangedUnits(thisTime, lastTime)
|
||||
end
|
||||
else
|
||||
-- player is new to mission
|
||||
clients.playerEnteredMission(thisTime)
|
||||
clients.playerEnteredUnit(thisTime)
|
||||
end
|
||||
end
|
||||
|
||||
function clients.checkPlayers()
|
||||
local connectedNow = {} -- players that are connected now
|
||||
local allCoas = {0, 1, 2}
|
||||
-- collect all currently connected players
|
||||
for idx, coa in pairs(allCoas) do
|
||||
local cPlayers = coalition.getPlayers(coa) -- gets UNITS!
|
||||
for idy, aPlayerUnit in pairs(cPlayers) do
|
||||
if aPlayerUnit and Unit.isExist(aPlayerUnit) then
|
||||
local entry = {}
|
||||
local playerName = aPlayerUnit:getPlayerName()
|
||||
entry.playerName = playerName
|
||||
entry.uName = aPlayerUnit:getName()
|
||||
entry.coa = coa
|
||||
entry.connected = true
|
||||
connectedNow[playerName] = entry
|
||||
|
||||
-- see if they were connected last time we checked
|
||||
local lastTime = clients.players[playerName]
|
||||
clients.compareStatus(entry, lastTime)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- now find players who are no longer represented and
|
||||
-- event them
|
||||
for aPlayerName, lastTime in pairs(clients.players) do
|
||||
local thisTime = connectedNow[aPlayerName]
|
||||
if thisTime then
|
||||
-- is also present now. skip
|
||||
else
|
||||
-- no longer active, see if they were active last time
|
||||
if lastTime.connected then
|
||||
-- they were active, generate disco event
|
||||
clients.playerLeavesUnit(lastTime)
|
||||
end
|
||||
lastTime.connected = false
|
||||
-- keep on roster
|
||||
connectedNow[aPlayerName] = lastTime
|
||||
end
|
||||
end
|
||||
|
||||
clients.players = connectedNow
|
||||
end
|
||||
|
||||
function clients.update()
|
||||
timer.scheduleFunction(clients.update, {}, timer.getTime() + 1)
|
||||
clients.checkPlayers()
|
||||
end
|
||||
|
||||
--
|
||||
-- Event handling
|
||||
--
|
||||
function clients:onEvent(theEvent)
|
||||
if not theEvent then return end
|
||||
local theUnit = theEvent.initiator
|
||||
if not theUnit then return end
|
||||
if not theUnit.getPlayerName or not theUnit:getPlayerName() then return end
|
||||
|
||||
-- we have a player birth. Simply invoke checkplayers
|
||||
clients.out("clients: detected player birth event.")
|
||||
clients.checkPlayers()
|
||||
end
|
||||
|
||||
--
|
||||
-- Start
|
||||
--
|
||||
function clients.start()
|
||||
world.addEventHandler(clients)
|
||||
timer.scheduleFunction(clients.update, {}, timer.getTime() + 1)
|
||||
trigger.action.outText("clients v" .. clients.version .. " running.", 30)
|
||||
end
|
||||
|
||||
clients.start()
|
||||
@ -98,8 +98,6 @@ function cloneZones.partOfGroupDataInZone(theZone, theUnits)
|
||||
uP.x = aUnit.x
|
||||
uP.y = 0
|
||||
uP.z = aUnit.y -- !! y-z
|
||||
--local dist = dcsCommon.dist(uP, zP)
|
||||
--if dist <= theZone.radius then return true end
|
||||
if theZone:pointInZone(uP) then return true end
|
||||
end
|
||||
return false
|
||||
@ -107,7 +105,6 @@ end
|
||||
|
||||
function cloneZones.allGroupsInZoneByData(theZone)
|
||||
local theGroupsInZone = {}
|
||||
local radius = theZone.radius
|
||||
for groupName, groupData in pairs(cfxMX.groupDataByName) do
|
||||
if groupData.units then
|
||||
if cloneZones.partOfGroupDataInZone(theZone, groupData.units) then
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
-- *** EXTENDS ZONES: 'pathing' attribute
|
||||
--
|
||||
cfxCommander = {}
|
||||
cfxCommander.version = "1.1.3"
|
||||
cfxCommander.version = "1.1.4"
|
||||
--[[-- VERSION HISTORY
|
||||
- 1.0.5 - createWPListForGroupToPointViaRoads: detect no road found
|
||||
- 1.0.6 - build in more group checks in assign wp list
|
||||
@ -29,6 +29,7 @@ cfxCommander.version = "1.1.3"
|
||||
- added delay defaulting for most scheduling functions
|
||||
- 1.1.3 - isExist() guard improvements for multiple methods
|
||||
- cleaned up comments
|
||||
- 1.1.4 - hardened makeGroupGoThere()
|
||||
|
||||
--]]--
|
||||
|
||||
@ -337,6 +338,18 @@ function cfxCommander.makeGroupGoThere(group, there, speed, formation, delay)
|
||||
if type(group) == 'string' then -- group name
|
||||
group = Group.getByName(group)
|
||||
end
|
||||
|
||||
if not Group.isExist(group) then
|
||||
trigger.action.outText("cmdr: makeGroupGoThere() - group does not exist", 30)
|
||||
return
|
||||
end
|
||||
|
||||
-- check that we can get a location for the group
|
||||
local here = dcsCommon.getGroupLocation(group)
|
||||
if not here then
|
||||
return
|
||||
end
|
||||
|
||||
local wp = cfxCommander.createWPListForGroupToPoint(group, there, speed, formation)
|
||||
|
||||
cfxCommander.assignWPListToGroup(group, wp, delay)
|
||||
|
||||
@ -40,6 +40,8 @@ csarManager.ups = 1
|
||||
3.2.4 - pass theZone with missionCreateCB when created from zone
|
||||
3.2.5 - smoke callbacks
|
||||
- useRanks option
|
||||
3.2.6 - inBuiltup analogon to cloner
|
||||
|
||||
|
||||
|
||||
INTEGRATES AUTOMATICALLY WITH playerScore
|
||||
@ -1277,7 +1279,7 @@ function csarManager.createCSARMissionFromZone(theZone)
|
||||
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, theZone.maxTries)
|
||||
local aPoint = theZone:createRandomPointInPopulatedZone(theZone.clearance) -- no more maxTries: theZone.maxTries)
|
||||
mPoint = aPoint -- safety in case we need to mod aPoint
|
||||
end
|
||||
local theMission = csarManager.createCSARMissionData(
|
||||
@ -1409,12 +1411,28 @@ function csarManager.readCSARZone(theZone)
|
||||
theZone.triggerMethod = theZone:getStringFromZoneProperty("triggerMethod", "change")
|
||||
theZone.rndLoc = theZone:getBoolFromZoneProperty("rndLoc", true)
|
||||
theZone.onRoad = theZone:getBoolFromZoneProperty("onRoad", false)
|
||||
theZone.inPopulated = theZone:getBoolFromZoneProperty("inPopulated", false)
|
||||
theZone.clearance = theZone:getNumberFromZoneProperty("clearance", 10)
|
||||
theZone.maxTries = theZone:getNumberFromZoneProperty("maxTries", 20)
|
||||
if theZone:hasProperty("onRoads") then
|
||||
theZone.onRoad = theZone:getBoolFromZoneProperty("onRoads", false)
|
||||
end
|
||||
|
||||
-- emulate inBuiltup from clone Zone
|
||||
if theZone:hasProperty("inPopulated") or theZone:hasProperty("clearance") or theZone:hasProperty("inBuiltup")then
|
||||
if theZone:hasProperty("inPopulated") then
|
||||
theZone.inPopulated = theZone:getBoolFromZoneProperty("inPopulated", false)
|
||||
else
|
||||
theZone.inPopulated = true -- presence of clearance forces it to true
|
||||
end
|
||||
theZone.clearance = theZone:getNumberFromZoneProperty("clearance", 10)
|
||||
if theZone:hasProperty("inBuiltUp") then
|
||||
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
|
||||
|
||||
-- add to list of startable csar
|
||||
@ -1423,27 +1441,6 @@ function csarManager.readCSARZone(theZone)
|
||||
end
|
||||
|
||||
if (not deferred) then
|
||||
--[[--
|
||||
local mPoint = theZone:getPoint()
|
||||
if theZone.rndLoc then mPoint = theZone:createRandomPointInZone() end
|
||||
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, theZone.maxTries)
|
||||
mPoint = aPoint -- safety in case we need to mod aPoint
|
||||
end
|
||||
local theMission = csarManager.createCSARMissionData(
|
||||
mPoint,
|
||||
theZone.csarSide,
|
||||
theZone.csarFreq,
|
||||
theZone.csarName,
|
||||
theZone.numCrew,
|
||||
theZone.timeLimit,
|
||||
theZone.csarMapMarker,
|
||||
0.1, -- theZone.radius,
|
||||
nil) -- parashoo unit
|
||||
csarManager.addMission(theMission, theZone)
|
||||
--]]--
|
||||
local theMission = csarManager.createCSARMissionFromZone(theZone)
|
||||
csarManager.addMission(theMission, theZone)
|
||||
end
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
dcsCommon = {}
|
||||
dcsCommon.version = "3.0.3"
|
||||
dcsCommon.version = "3.0.5"
|
||||
--[[-- VERSION HISTORY
|
||||
3.0.0 - removed bad bug in stringStartsWith, only relevant if caseSensitive is false
|
||||
- point2text new intsOnly option
|
||||
@ -13,6 +13,10 @@ dcsCommon.version = "3.0.3"
|
||||
3.0.3 - createStaticObjectForCoalitionInRandomRing() returns x and z
|
||||
- isTroopCarrier() also supports 'helo' keyword
|
||||
- new createTakeOffFromGroundRoutePointData()
|
||||
3.0.4 - getGroupLocation() hardened, optional verbose
|
||||
3.0.5 - new getNthItem()
|
||||
- new getFirstItem()
|
||||
|
||||
--]]--
|
||||
|
||||
-- dcsCommon is a library of common lua functions
|
||||
@ -184,6 +188,18 @@ dcsCommon.version = "3.0.3"
|
||||
return choices[rtnVal] -- return indexed
|
||||
end
|
||||
|
||||
function dcsCommon.getNthItem(theSet, n)
|
||||
local count = 1
|
||||
for key, value in pairs(theSet) do
|
||||
if count == n then return value end
|
||||
count = count + 1
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function dcsCommon.getFirstItem(theSet)
|
||||
return dcsCommon.getNthItem(theSet, 1)
|
||||
end
|
||||
|
||||
function dcsCommon.getSizeOfTable(theTable)
|
||||
local count = 0
|
||||
@ -888,7 +904,8 @@ dcsCommon.version = "3.0.3"
|
||||
|
||||
-- get group location: get the group's location by
|
||||
-- accessing the fist existing, alive member of the group that it finds
|
||||
function dcsCommon.getGroupLocation(group)
|
||||
function dcsCommon.getGroupLocation(group, verbose, gName)
|
||||
if not verbose then verbose = false end
|
||||
-- nifty trick from mist: make this work with group and group name
|
||||
if type(group) == 'string' then -- group name
|
||||
group = Group.getByName(group)
|
||||
@ -896,12 +913,18 @@ dcsCommon.version = "3.0.3"
|
||||
|
||||
-- get all units
|
||||
local allUnits = group:getUnits()
|
||||
if not allUnits then
|
||||
if verbose then
|
||||
trigger.action.outText("++++common: no group location for <" .. gName .. ">, skipping.", 30)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- iterate through all members of group until one is alive and exists
|
||||
for index, theUnit in pairs(allUnits) do
|
||||
if (theUnit:isExist() and theUnit:getLife() > 0) then
|
||||
return theUnit:getPosition().p
|
||||
end;
|
||||
end
|
||||
end
|
||||
|
||||
-- if we get here, there was no live unit
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
groundExplosion = {}
|
||||
groundExplosion.version = "1.0.0"
|
||||
groundExplosion.version = "1.1.0"
|
||||
groundExplosion.requiredLibs = {
|
||||
"dcsCommon",
|
||||
"cfxZones",
|
||||
@ -9,6 +9,8 @@ groundExplosion.zones = {}
|
||||
--[[--
|
||||
Version History
|
||||
1.0.0 - Initial version
|
||||
1.0.1 - fixed lib check for objectDestructDetector
|
||||
1.1.0 - new flares attribute
|
||||
|
||||
--]]--
|
||||
|
||||
@ -28,6 +30,9 @@ function groundExplosion.addExplosion(theZone)
|
||||
end
|
||||
theZone.duration = theZone:getNumberFromZoneProperty("duration", 0)
|
||||
theZone.aglMin, theZone.aglMax = theZone:getPositiveRangeFromZoneProperty("AGL", 1,1)
|
||||
if theZone:hasProperty("flares") then
|
||||
theZone.flareMin, theZone.flareMax = theZone:getPositiveRangeFromZoneProperty("flares", 0-3)
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
@ -38,6 +43,16 @@ function groundExplosion.doBoom(args)
|
||||
local power = args[2]
|
||||
local theZone = args[3]
|
||||
trigger.action.explosion(loc, power)
|
||||
if theZone.flareMin then
|
||||
local flareNum = cfxZones.randomInRange(theZone.flareMin, theZone.flareMax)
|
||||
if flareNum > 0 then
|
||||
for i=1, flareNum do
|
||||
local azimuth = math.random(360)
|
||||
azimuth = azimuth * 0.0174533 -- in rads
|
||||
trigger.action.signalFlare(loc, 2, azimuth) -- 2 = white
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function groundExplosion.startBoom(theZone)
|
||||
@ -82,7 +97,7 @@ end
|
||||
|
||||
function groundExplosion.start()
|
||||
if not dcsCommon.libCheck("cfx groundExplosion",
|
||||
cfxObjectDestructDetector.requiredLibs) then
|
||||
groundExplosion.requiredLibs) then
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxGroundTroops = {}
|
||||
cfxGroundTroops.version = "1.7.8"
|
||||
cfxGroundTroops.version = "2.0.0"
|
||||
cfxGroundTroops.ups = 1
|
||||
cfxGroundTroops.verbose = false
|
||||
cfxGroundTroops.requiredLibs = {
|
||||
@ -22,52 +22,15 @@ cfxGroundTroops.requiredLibs = {
|
||||
-- module
|
||||
|
||||
cfxGroundTroops.deployedTroops = {} -- indexed by group name
|
||||
cfxGroundTroops.jtacCB = {} -- jtac callbacks, to be implemented
|
||||
|
||||
--[[--
|
||||
version history
|
||||
1.3.0 - added "wait-" prefix to have toops do nothing
|
||||
- added lazing
|
||||
1.3.1 - sound for lazing msg is "UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav"
|
||||
- lazing --> lasing in text
|
||||
1.3.2 - set ups to 2
|
||||
1.4.0 - queued updates except for lazers
|
||||
1.4.1 - makeTroopsEngageZone now issues hold before moving on 5 seconds later
|
||||
- getTroopReport
|
||||
- include size of group
|
||||
1.4.2 - uses unitIsInfantry from dcsCommon
|
||||
1.5.0 - new scheduled updates per troop to reduce processor load
|
||||
- tiebreak code
|
||||
1.5.1 - small bugfix in scheduled code
|
||||
1.5.2 - checkSchedule
|
||||
- speed warning in scheduler
|
||||
- go off road when speed warning too much
|
||||
1.5.3 - monitor troops
|
||||
- managed queue for ground troops
|
||||
- on second switch to offroad now removed from MQ
|
||||
1.5.4 - removed debugging messages
|
||||
1.5.5 - removed bug in troop report reading nil destination
|
||||
1.6.0 - check modules
|
||||
1.6.1 - troopsCallback management so you can be informed if a
|
||||
troop you have added to the pool is dead or has achieved a goal.
|
||||
callback will list reasons "dead" and "arrived"
|
||||
updateAttackers
|
||||
1.6.2 - also accept 'lase' as 'laze', translate directly
|
||||
1.7.0 - now can use groundTroopsConfig zone
|
||||
1.7.1 - addTroopsDeadCallback() renamed to addTroopsCallback()
|
||||
- invokeCallbacksFor also accepts and passes on data block
|
||||
- troops is always passed in data block as .troops
|
||||
1.7.2 - callback when group is neutralized on guard orders
|
||||
- callback when group is being engaged under guard orders
|
||||
1.7.3 - callbacks for lase:tracking and lase:stop
|
||||
1.7.4 - verbose flag, warnings suppressed
|
||||
1.7.5 - some troop.group hardening with isExist()
|
||||
1.7.6 - fixed switchToOffroad
|
||||
1.7.7 - no longer case sensitive for orders
|
||||
1.7.7 - updateAttackers() now inspects 'moving' status and invokes makeTroopsEngageZone
|
||||
- makeTroopsEngageZone() sets 'moving' status to true
|
||||
- createGroundTroops() sets moving status to false
|
||||
- updateZoneAttackers() uses moving
|
||||
1.7.8 - better guards before invoking ownedZones
|
||||
|
||||
2.0.0 - dmlZones
|
||||
- jtacSound
|
||||
- clanup
|
||||
- jtacVerbose
|
||||
|
||||
an entry into the deployed troop table has the following attributes
|
||||
- group - the group
|
||||
@ -113,63 +76,47 @@ cfxGroundTroops.deployedTroops = {} -- indexed by group name
|
||||
-- queued will work one every pass (except for lazed), distributing the load much better
|
||||
-- schedueld installs a callback for each group separately and thus distributes the load over time much better
|
||||
|
||||
cfxGroundTroops.queuedUpdates = false -- set to true to process one group per turn. To work this way, scheduledUpdates must be false
|
||||
cfxGroundTroops.scheduledUpdates = true -- set to false to allow queing of standard updates. overrides queuedUpdates
|
||||
cfxGroundTroops.monitorNumbers = false -- set to true to debug managed group size
|
||||
function cfxGroundTroops.invokeCallbacks(ID, jtac, tgt, data)
|
||||
-- IS is aqui, lost, dead, jtac died. jtac is group, tgt is unit, data is rest
|
||||
for idx, cb in pairs(cfxGroundTroops.jtacCB) do
|
||||
cb(ID, jtac, tgt, data)
|
||||
end
|
||||
end
|
||||
|
||||
cfxGroundTroops.standardScheduleInterval = 30 -- 30 seconds between calls
|
||||
cfxGroundTroops.guardUpdateInterval = 30 -- every 30 seconds we check up on guards
|
||||
cfxGroundTroops.trackingUpdateInterval = 0.5 -- 0.5 seconds for lazer tracking etc
|
||||
function cfxGroundTroops.addJtacCB(theCB)
|
||||
table.insert(cfxGroundTroops.jtacCB, theCB)
|
||||
end
|
||||
|
||||
cfxGroundTroops.maxManagedTroops = 67 -- -1 is infinite, any positive number turn on cap on managed troops and palces excess troops in queue
|
||||
cfxGroundTroops.troopQueue = {} -- FIFO stack
|
||||
-- return the best tracking interval for this type of orders
|
||||
|
||||
--
|
||||
-- READ CONFIG ZONE TO OVERRIDE SETTING
|
||||
--
|
||||
function cfxGroundTroops.readConfigZone()
|
||||
-- note: must match exactly!!!!
|
||||
local theZone = cfxZones.getZoneByName("groundTroopsConfig")
|
||||
if not theZone then
|
||||
if cfxGroundTroops.verbose then
|
||||
trigger.action.outText("***gndT: NO config zone!", 30)
|
||||
end
|
||||
theZone = cfxZones.createSimpleZone("groundTroopsConfig")
|
||||
end
|
||||
|
||||
-- ok, for each property, load it if it exists
|
||||
if cfxZones.hasProperty(theZone, "queuedUpdates") then
|
||||
cfxGroundTroops.queuedUpdates = cfxZones.getBoolFromZoneProperty(theZone, "queuedUpdates", false)
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "scheduledUpdates") then
|
||||
cfxGroundTroops.scheduledUpdates = cfxZones.getBoolFromZoneProperty(theZone, "scheduledUpdates", false)
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "maxManagedTroops") then
|
||||
cfxGroundTroops.maxManagedTroops = cfxZones.getNumberFromZoneProperty(theZone, "maxManagedTroops", 65)
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "monitorNumbers") then
|
||||
cfxGroundTroops.monitorNumbers = cfxZones.getBoolFromZoneProperty(theZone, "monitorNumbers", false)
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "standardScheduleInterval") then
|
||||
cfxGroundTroops.standardScheduleInterval = cfxZones.getNumberFromZoneProperty(theZone, "standardScheduleInterval", 30)
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "guardUpdateInterval") then
|
||||
cfxGroundTroops.guardUpdateInterval = cfxZones.getNumberFromZoneProperty(theZone, "guardUpdateInterval", 30)
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "trackingUpdateInterval") then
|
||||
cfxGroundTroops.trackingUpdateInterval = cfxZones.getNumberFromZoneProperty(theZone, "trackingUpdateInterval", 0.5)
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "verbose") then
|
||||
cfxGroundTroops.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
||||
end
|
||||
cfxGroundTroops.queuedUpdates = theZone:getBoolFromZoneProperty("queuedUpdates", false)
|
||||
cfxGroundTroops.scheduledUpdates = theZone:getBoolFromZoneProperty("scheduledUpdates", false)
|
||||
cfxGroundTroops.maxManagedTroops = theZone:getNumberFromZoneProperty("maxManagedTroops", 67)
|
||||
cfxGroundTroops.monitorNumbers = theZone:getBoolFromZoneProperty("monitorNumbers", false)
|
||||
cfxGroundTroops.standardScheduleInterval = theZone:getNumberFromZoneProperty("standardScheduleInterval", 30)
|
||||
cfxGroundTroops.guardUpdateInterval = theZone:getNumberFromZoneProperty("guardUpdateInterval", 30)
|
||||
cfxGroundTroops.trackingUpdateInterval = theZone:getNumberFromZoneProperty("trackingUpdateInterval", 0.5)
|
||||
|
||||
cfxGroundTroops.jtacSound = theZone:getStringFromZoneProperty("jtacSound", "UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav")
|
||||
cfxGroundTroops.jtacVerbose = theZone:getBoolFromZoneProperty("jtacVerbose", true)
|
||||
cfxGroundTroops.laseCode = theZone:getNumberFromZoneProperty("jtacLaserCode", 1688)
|
||||
if theZone:hasProperty("lazeCode") then
|
||||
cfxGroundTroops.laseCode = theZone:getNumberFromZoneProperty("lazeCode", 1688)
|
||||
end
|
||||
if theZone:hasProperty("laseCode") then
|
||||
cfxGroundTroops.laseCode = theZone:getNumberFromZoneProperty("laseCode", 1688)
|
||||
end
|
||||
if theZone:hasProperty("laserCode") then
|
||||
cfxGroundTroops.laseCode = theZone:getNumberFromZoneProperty("laserCode", 1688)
|
||||
end
|
||||
cfxGroundTroops.verbose = theZone.verbose
|
||||
|
||||
if cfxGroundTroops.verbose then
|
||||
trigger.action.outText("+++gndT: read config zone!", 30)
|
||||
@ -355,8 +302,7 @@ function cfxGroundTroops.updateAttackers(troop)
|
||||
troop.orders = "guard"
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- if we get here, we need no change
|
||||
end
|
||||
|
||||
@ -437,7 +383,7 @@ function cfxGroundTroops.findLazeTarget(troop)
|
||||
|
||||
-- iterate through the list until we find the first target
|
||||
-- that fits the bill and return it
|
||||
-- trigger.action.outText("+++ looking at " .. #enemyGroups .. " laze groups", 30)
|
||||
|
||||
for i=1, #enemyGroups do
|
||||
-- get all units for this group
|
||||
local aGroup = enemyGroups[i].group -- remember, they are in a {dist, group} tuple
|
||||
@ -448,12 +394,9 @@ function cfxGroundTroops.findLazeTarget(troop)
|
||||
-- unit lives
|
||||
-- now, we need to filter infantry. we do this by
|
||||
-- pre-fetching the typeString
|
||||
--troop.lazeTargetType = aUnit:getTypeName()
|
||||
-- and checking if the name contains some infantry-
|
||||
-- typical strings. Idea taken from JTAC script
|
||||
local isInfantry = dcsCommon.unitIsInfantry(theUnit)
|
||||
|
||||
|
||||
if not isInfantry then
|
||||
-- this is a vehicle, is it in line of sight?
|
||||
-- raise the point 2m above ground for both points
|
||||
@ -466,16 +409,11 @@ function cfxGroundTroops.findLazeTarget(troop)
|
||||
-- the nearest group to us in range
|
||||
-- that is visible!
|
||||
return aUnit
|
||||
else
|
||||
--trigger.action.outText("+++ ".. aUnit:getName() .."cant be seen", 30)
|
||||
end -- if visible
|
||||
else
|
||||
-- trigger.action.outText("+++ ".. aUnit:getName() .." (".. troop.lazeTargetType .. ") is infantry", 30)
|
||||
end -- if not infantry
|
||||
end -- if infantry
|
||||
end -- if alive
|
||||
end -- for all units
|
||||
end -- for all enemy groups
|
||||
--trigger.action.outText("+++ find nearest laze target did not find anything to laze", 30)
|
||||
return nil -- no unit found
|
||||
end
|
||||
|
||||
@ -499,10 +437,12 @@ function cfxGroundTroops.trackLazer(troop)
|
||||
|
||||
if not troop.lazerPointer then
|
||||
local there = troop.lazeTarget:getPoint()
|
||||
troop.lazerPointer = Spot.createLaser(troop.lazingUnit,{x = 0, y = 2, z = 0}, there, 1688)
|
||||
troop.lazerPointer = Spot.createLaser(troop.lazingUnit,{x = 0, y = 2, z = 0}, there, cfxGroundTroops.laseCode)
|
||||
troop.lazeTargetType = troop.lazeTarget:getTypeName()
|
||||
trigger.action.outTextForCoalition(troop.side, troop.name .. " tally target - lasing " .. troop.lazeTargetType .. "!", 30)
|
||||
trigger.action.outSoundForCoalition(troop.side, "UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav")
|
||||
if cfxGroundTroops.jtacVerbose then
|
||||
trigger.action.outTextForCoalition(troop.side, troop.name .. " tally target - lasing " .. troop.lazeTargetType .. ", code " .. cfxGroundTroops.laseCode .. "!", 30)
|
||||
trigger.action.outSoundForCoalition(troop.side, cfxGroundTroops.jtacSound) -- "UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav")
|
||||
end
|
||||
troop.lastLazerSpot = there -- remember last spot
|
||||
local data = {}
|
||||
data.enemy = troop.lazeTarget
|
||||
@ -510,9 +450,7 @@ function cfxGroundTroops.trackLazer(troop)
|
||||
cfxGroundTroops.invokeCallbacksFor("lase:tracking", troop, data)
|
||||
return
|
||||
end
|
||||
|
||||
-- if true then return end
|
||||
|
||||
|
||||
-- if we get here, we update the lazerPointer
|
||||
local there = troop.lazeTarget:getPoint()
|
||||
-- we may only want to update the laser spot when dist > trigger
|
||||
@ -531,17 +469,17 @@ function cfxGroundTroops.updateLaze(troop)
|
||||
else
|
||||
cfxGroundTroops.lazerOff(troop)
|
||||
troop.lazeTarget = nil
|
||||
trigger.action.outTextForCoalition(troop.side, troop.name .. " reports lasing " .. troop.lazeTargetType .. " interrupted. Re-acquiring.", 30)
|
||||
trigger.action.outSoundForCoalition(troop.side, "UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav")
|
||||
if cfxGroundTroops.jtacVerbose then
|
||||
trigger.action.outTextForCoalition(troop.side, troop.name .. " reports lasing " .. troop.lazeTargetType .. " interrupted. Re-acquiring.", 30)
|
||||
trigger.action.outSoundForCoalition(troop.side, cfxGroundTroops.jtacSound) -- "UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav")
|
||||
end
|
||||
troop.lazingUnit = nil
|
||||
cfxGroundTroops.invokeCallbacksFor("lase:stop", troop)
|
||||
return -- we'll re-acquire through a new unit next round
|
||||
end
|
||||
end
|
||||
|
||||
-- if we get here, a lazing unit
|
||||
--local here = troop.lazingUnit:getPoint()
|
||||
|
||||
-- if we get here, a lazing unit
|
||||
if troop.lazeTarget then
|
||||
-- check if that target is alive and in range
|
||||
if troop.lazeTarget:isExist() and troop.lazeTarget:getLife() >= 1 then
|
||||
@ -551,8 +489,10 @@ function cfxGroundTroops.updateLaze(troop)
|
||||
local there = troop.lazeTarget:getPoint()
|
||||
if dcsCommon.dist(here, there) > troop.range then
|
||||
-- troop out of range
|
||||
trigger.action.outTextForCoalition(troop.side, troop.name .. " lost sight of lazed target " .. troop.lazeTargetType, 30)
|
||||
trigger.action.outSoundForCoalition(troop.side, "UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav")
|
||||
if cfxGroundTroops.jtacVerbose then
|
||||
trigger.action.outTextForCoalition(troop.side, troop.name .. " lost sight of lazed target " .. troop.lazeTargetType, 30)
|
||||
trigger.action.outSoundForCoalition(troop.side, cfxGroundTroops.jtacSound) -- "UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav")
|
||||
end
|
||||
troop.lazeTarget = nil
|
||||
cfxGroundTroops.lazerOff(troop)
|
||||
troop.lazingUnit = nil
|
||||
@ -565,8 +505,10 @@ function cfxGroundTroops.updateLaze(troop)
|
||||
return
|
||||
else
|
||||
-- target died
|
||||
trigger.action.outTextForCoalition(troop.side, troop.name .. " confirms kill for " .. troop.lazeTargetType, 30)
|
||||
trigger.action.outSoundForCoalition(troop.side, "UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav")
|
||||
if cfxGroundTroops.jtacVerbose then
|
||||
trigger.action.outTextForCoalition(troop.side, troop.name .. " confirms kill for " .. troop.lazeTargetType, 30)
|
||||
trigger.action.outSoundForCoalition(troop.side, cfxGroundTroops.jtacSound) -- "UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav")
|
||||
end
|
||||
troop.lazeTarget = nil
|
||||
cfxGroundTroops.lazerOff(troop)
|
||||
troop.lazingUnit = nil
|
||||
@ -584,8 +526,7 @@ end
|
||||
|
||||
|
||||
function cfxGroundTroops.updateWait(troop)
|
||||
-- currently nothing to do
|
||||
|
||||
-- currently nothing to do
|
||||
end
|
||||
|
||||
function cfxGroundTroops.updateTroops(troop)
|
||||
@ -616,12 +557,6 @@ function cfxGroundTroops.updateTroops(troop)
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- we have to systems to process during update:
|
||||
-- once all, and one per turn, with the exception
|
||||
-- of lazers, who get updated every turn
|
||||
--
|
||||
|
||||
--
|
||||
-- all at once
|
||||
--
|
||||
@ -754,14 +689,12 @@ function cfxGroundTroops.updateSingleScheduled(params)
|
||||
-- now, check if still alive
|
||||
if not dcsCommon.isGroupAlive(group) then
|
||||
-- group dead, no longer updates
|
||||
--trigger.action.outText("+++groundT NOTE: <".. troops.group:getName() .."> dead, removing", 30)
|
||||
cfxGroundTroops.invokeCallbacksFor("dead", troops) -- notify anyone who is interested that we are no longer proccing these
|
||||
cfxGroundTroops.removeTroopsFromPool(troops)
|
||||
return -- nothing else to do
|
||||
end
|
||||
|
||||
-- now, execute the update itself, standard update
|
||||
--trigger.action.outText("+++groundT: singleU troop <".. troops.group:getName() .."> with orders <" .. troops.orders .. ">", 30)
|
||||
cfxGroundTroops.updateTroops(troops)
|
||||
|
||||
-- check max speed of group. if < 0.1 then note and increase
|
||||
@ -775,7 +708,6 @@ function cfxGroundTroops.updateSingleScheduled(params)
|
||||
|
||||
if troops.speedWarning > 5 then -- make me 5
|
||||
lastOrder = timer.getTime() - troops.lastOrderDate
|
||||
--trigger.action.outText("+++groundT WARNING: <".. troops.group:getName() .."> (S:".. troops.side .. ") to " .. troops.destination.name .. ": stopped for " .. troops.speedWarning .. " iters, orderage=" .. lastOrder, 30)
|
||||
-- this may be a matter of too many waypoints.
|
||||
-- maybe issue orders to go to their destination directly?
|
||||
-- now force an order to go directly.
|
||||
@ -784,17 +716,15 @@ function cfxGroundTroops.updateSingleScheduled(params)
|
||||
-- we already switched to off-road. take me
|
||||
-- out of the managed queue, I'm not going
|
||||
-- anywhere
|
||||
-- trigger.action.outText("+++groundT <".. troops.group:getName() .."> is going nowhere. Removed from managed troops", 30)
|
||||
cfxGroundTroops.removeTroopsFromPool(troops)
|
||||
else
|
||||
cfxGroundTroops.switchToOffroad(troops)
|
||||
-- trigger.action.outText("+++groundT <".. troops.group:getName() .."> SWITCHED TO OFFROAD", 30)
|
||||
troops.isOffroad = true -- so we know that we already did that
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- now reschedule updte for my best time
|
||||
-- now reschedule update for my best time
|
||||
local updateTime = cfxGroundTroops.getScheduleInterval(troops.orders)
|
||||
troops.updateID = timer.scheduleFunction(cfxGroundTroops.updateSingleScheduled, params, timer.getTime() + updateTime)
|
||||
end
|
||||
@ -818,11 +748,9 @@ end
|
||||
|
||||
function cfxGroundTroops.checkPileUp()
|
||||
-- schedule my next call
|
||||
--trigger.action.outText("+++groundT: pileup check", 30)
|
||||
timer.scheduleFunction(cfxGroundTroops.checkPileUp, {}, timer.getTime() + 60)
|
||||
local thePiles = {}
|
||||
if not cfxOwnedZones then
|
||||
-- trigger.action.outText("+++groundT: pileUp - owned zones not yet ready", 30)
|
||||
return
|
||||
end
|
||||
|
||||
@ -945,11 +873,6 @@ function cfxGroundTroops.getTroopReport(theSide, ignoreInfantry)
|
||||
return report
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- CREATE / ADD / REMOVE
|
||||
--
|
||||
|
||||
--
|
||||
-- createGroundTroop
|
||||
-- use this to create a cfxGroundTroops from a dcs group
|
||||
@ -1100,13 +1023,12 @@ function cfxGroundTroops.manageQueues()
|
||||
|
||||
-- if we here, there are items waiting in the queue
|
||||
while dcsCommon.getSizeOfTable(cfxGroundTroops.deployedTroops) < cfxGroundTroops.maxManagedTroops and #cfxGroundTroops.troopQueue > 0 do
|
||||
-- trnasfer items from the front to the managed queue
|
||||
-- transfer items from the front to the managed queue
|
||||
local theTroops = cfxGroundTroops.troopQueue[1]
|
||||
table.remove(cfxGroundTroops.troopQueue, 1)
|
||||
if theTroops.group:isExist() then
|
||||
cfxGroundTroops.deployedTroops[theTroops.group:getName()] = theTroops
|
||||
end
|
||||
-- trigger.action.outText("+++gT: dequed and activaed " .. theTroops.group:getName(), 30)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -126,6 +126,7 @@ end
|
||||
function groupTracker.addGroupToTrackerNamed(theGroup, trackerName)
|
||||
if not trackerName then
|
||||
trigger.action.outText("+++gTrk: nil tracker in addGroupToTrackerNamed", 30)
|
||||
return
|
||||
end
|
||||
if not theGroup then
|
||||
trigger.action.outText("+++gTrk: no group in addGroupToTrackerNamed <" .. trackerName .. ">", 30)
|
||||
@ -543,6 +544,13 @@ function groupTracker.trackGroupsInZone(theZone)
|
||||
local trackerName = cfxZones.getStringFromZoneProperty(theZone, "addToTracker:", "<none>")
|
||||
|
||||
local theGroups = cfxZones.allGroupsInZone(theZone, nil)
|
||||
--[[-- trigger.action.outText("Groups in zone <" .. theZone.name .. ">:", 30)
|
||||
local msg = " :: "
|
||||
for idx, aGroup in pairs (theGroups) do
|
||||
msg = msg .. " <" .. aGroup:getName() .. ">"
|
||||
end
|
||||
trigger.action.outText(msg, 30)
|
||||
--]]--
|
||||
|
||||
-- now init array processing
|
||||
local trackerNames = {}
|
||||
@ -613,6 +621,18 @@ function groupTracker.start()
|
||||
groupTracker.trackGroupsInZone(aZone) -- process attributes
|
||||
end
|
||||
|
||||
-- verbose debugging:
|
||||
-- show who's tracking who
|
||||
for idx, theZone in pairs(groupTracker.trackers) do
|
||||
if groupTracker.verbose or theZone.verbose then
|
||||
local msg = " - Tracker <" .. theZone.name .. ">: "
|
||||
for idx, theGroup in pairs(theZone.trackedGroups) do
|
||||
msg = msg .. "<" .. theGroup:getName() .. "> "
|
||||
end
|
||||
trigger.action.outText(msg, 30)
|
||||
end
|
||||
end
|
||||
|
||||
-- update all cloners and spawned clones from file
|
||||
if persistence then
|
||||
-- sign up for persistence
|
||||
|
||||
@ -1,15 +1,24 @@
|
||||
jtacGrpUI = {}
|
||||
jtacGrpUI.version = "1.0.2"
|
||||
jtacGrpUI.version = "2.0.0"
|
||||
jtacGrpUI.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxZones",
|
||||
"cfxGroundTroops",
|
||||
}
|
||||
--[[-- VERSION HISTORY
|
||||
- 1.0.2 - also include idling JTACS
|
||||
- add positional info when using owned zones
|
||||
|
||||
- 2.0.0 - dmlZones
|
||||
- sanity checks upon load
|
||||
- eliminated cfxPlayer dependence
|
||||
- clean-up
|
||||
- jtacSound
|
||||
--]]--
|
||||
-- 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
|
||||
jtacGrpUI.groupConfig = {} -- all inited group private config data, indexed by group name.
|
||||
jtacGrpUI.simpleCommands = true -- if true, f10 other invokes directly
|
||||
|
||||
--
|
||||
@ -24,6 +33,9 @@ function jtacGrpUI.resetConfig(conf)
|
||||
end
|
||||
|
||||
function jtacGrpUI.createDefaultConfig(theGroup)
|
||||
if not theGroup then return nil end
|
||||
if not Group.isExist(theGroup) then return end
|
||||
|
||||
local conf = {}
|
||||
conf.theGroup = theGroup
|
||||
conf.name = theGroup:getName()
|
||||
@ -32,8 +44,8 @@ function jtacGrpUI.createDefaultConfig(theGroup)
|
||||
|
||||
jtacGrpUI.resetConfig(conf)
|
||||
|
||||
conf.mainMenu = nil; -- this is where we store the main menu if we branch
|
||||
conf.myCommands = nil; -- this is where we store the commands if we branch
|
||||
conf.mainMenu = nil; -- root
|
||||
conf.myCommands = nil; -- commands branch
|
||||
|
||||
return conf
|
||||
end
|
||||
@ -41,15 +53,15 @@ end
|
||||
-- getConfigFor group will allocate if doesn't exist in DB
|
||||
-- and add to it
|
||||
function jtacGrpUI.getConfigForGroup(theGroup)
|
||||
if not theGroup then
|
||||
if not theGroup or (not Group.isExist(theGroup))then
|
||||
trigger.action.outText("+++WARNING: jtacGrpUI nil group in getConfigForGroup!", 30)
|
||||
return nil
|
||||
end
|
||||
local theName = theGroup:getName()
|
||||
local c = jtacGrpUI.getConfigByGroupName(theName) -- we use central accessor
|
||||
local c = jtacGrpUI.getConfigByGroupName(theName)
|
||||
if not c then
|
||||
c = jtacGrpUI.createDefaultConfig(theGroup)
|
||||
jtacGrpUI.groupConfig[theName] = c -- should use central accessor...
|
||||
jtacGrpUI.groupConfig[theName] = c
|
||||
end
|
||||
return c
|
||||
end
|
||||
@ -66,16 +78,13 @@ function jtacGrpUI.getConfigForUnit(theUnit)
|
||||
trigger.action.outText("+++WARNING: jtacGrpUI nil unit in getConfigForUnit!", 30)
|
||||
return nil
|
||||
end
|
||||
|
||||
local theGroup = theUnit:getGroup()
|
||||
return getConfigForGroup(theGroup)
|
||||
end
|
||||
|
||||
--
|
||||
--
|
||||
-- M E N U H A N D L I N G
|
||||
-- =========================
|
||||
--
|
||||
--
|
||||
function jtacGrpUI.clearCommsSubmenus(conf)
|
||||
if conf.myCommands then
|
||||
@ -95,8 +104,7 @@ function jtacGrpUI.removeCommsFromConfig(conf)
|
||||
end
|
||||
end
|
||||
|
||||
-- this only works in single-unit groups. may want to check if group
|
||||
-- has disappeared
|
||||
-- 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
|
||||
@ -112,11 +120,21 @@ function jtacGrpUI.removeCommsForGroup(theGroup)
|
||||
jtacGrpUI.removeCommsFromConfig(conf)
|
||||
end
|
||||
|
||||
--
|
||||
-- set main root in F10 Other. All sub menus click into this
|
||||
--
|
||||
function jtacGrpUI.isEligibleForMenu(theGroup)
|
||||
return true
|
||||
if jtacGrpUI.jtacTypes == "all" or
|
||||
jtacGrpUI.jtacTypes == "any" then return true end
|
||||
if dcsCommon.stringStartsWith(jtacGrpUI.jtacTypes, "hel", true) then
|
||||
local cat = theGroup:getCategory()
|
||||
return cat == 1
|
||||
end
|
||||
if dcsCommon.stringStartsWith(jtacGrpUI.jtacTypes, "plan", true) then
|
||||
local cat = theGroup:getCategory()
|
||||
return cat == 0
|
||||
end
|
||||
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)
|
||||
@ -131,38 +149,25 @@ function jtacGrpUI.setCommsMenuForUnit(theUnit)
|
||||
end
|
||||
|
||||
function jtacGrpUI.setCommsMenu(theGroup)
|
||||
-- depending on own load state, we set the command structure
|
||||
-- it begins at 10-other, and has 'jtac' as main menu with submenus
|
||||
-- as required
|
||||
if not theGroup then return end
|
||||
if not theGroup:isExist() then return end
|
||||
|
||||
-- we test here if this group qualifies for
|
||||
-- the menu. if not, exit
|
||||
if not Group.isExist(theGroup) then return end
|
||||
if not jtacGrpUI.isEligibleForMenu(theGroup) then return end
|
||||
|
||||
local conf = jtacGrpUI.getConfigForGroup(theGroup)
|
||||
conf.id = theGroup:getID(); -- we do this ALWAYS so it is current even after a crash
|
||||
-- trigger.action.outText("+++ setting group <".. conf.theGroup:getName() .. "> jtac command", 30)
|
||||
conf.id = theGroup:getID(); -- we always do this ALWAYS
|
||||
|
||||
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,
|
||||
nil,
|
||||
jtacGrpUI.redirectCommandX,
|
||||
{conf, "lasing report"}
|
||||
)
|
||||
conf.id, commandTxt, nil, 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')
|
||||
@ -178,12 +183,6 @@ function jtacGrpUI.setCommsMenu(theGroup)
|
||||
end
|
||||
|
||||
function jtacGrpUI.addSubMenus(conf)
|
||||
-- add menu items to choose from after
|
||||
-- user clickedf on MAIN MENU. In this implementation
|
||||
-- they all result invoked methods
|
||||
|
||||
|
||||
|
||||
local commandTxt = "jtac Lasing Report"
|
||||
local theCommand = missionCommands.addCommandForGroup(
|
||||
conf.id,
|
||||
@ -193,24 +192,8 @@ function jtacGrpUI.addSubMenus(conf)
|
||||
{conf, "lasing report"}
|
||||
)
|
||||
table.insert(conf.myCommands, theCommand)
|
||||
--[[--
|
||||
commandTxt = "This is another important command"
|
||||
theCommand = missionCommands.addCommandForGroup(
|
||||
conf.id,
|
||||
commandTxt,
|
||||
conf.myMainMenu,
|
||||
jtacGrpUI.redirectCommandX,
|
||||
{conf, "Sub2"}
|
||||
)
|
||||
table.insert(conf.myCommands, theCommand)
|
||||
--]]--
|
||||
end
|
||||
|
||||
--
|
||||
-- each menu item has a redirect and timed invoke to divorce from the
|
||||
-- no-debug zone in the menu invocation. Delay is .1 seconds
|
||||
--
|
||||
|
||||
function jtacGrpUI.redirectCommandX(args)
|
||||
timer.scheduleFunction(jtacGrpUI.doCommandX, args, timer.getTime() + 0.1)
|
||||
end
|
||||
@ -219,25 +202,25 @@ function jtacGrpUI.doCommandX(args)
|
||||
local conf = args[1] -- < conf in here
|
||||
local what = args[2] -- < second argument in here
|
||||
local theGroup = conf.theGroup
|
||||
-- trigger.action.outTextForGroup(conf.id, "+++ groupUI: processing comms menu for <" .. what .. ">", 30)
|
||||
local targetList = jtacGrpUI.collectJTACtargets(conf, true)
|
||||
-- iterate the list
|
||||
if #targetList < 1 then
|
||||
trigger.action.outTextForGroup(conf.id, "No targets are currently being lased", 30)
|
||||
trigger.action.outSoundForGroup(conf.id, jtacGrpUI.jtacSound)
|
||||
return
|
||||
end
|
||||
|
||||
local desc = "JTAC Target Report:\n"
|
||||
-- trigger.action.outTextForGroup(conf.id, "Target Report:", 30)
|
||||
for i=1, #targetList do
|
||||
local aTarget = targetList[i]
|
||||
if aTarget.idle then
|
||||
desc = desc .. "\n" .. aTarget.jtacName .. aTarget.posInfo ..": no target"
|
||||
else
|
||||
desc = desc .. "\n" .. aTarget.jtacName .. aTarget.posInfo .." lasing " .. aTarget.lazeTargetType .. " [" .. aTarget.range .. "nm at " .. aTarget.bearing .. "°]"
|
||||
desc = desc .. "\n" .. aTarget.jtacName .. aTarget.posInfo .." lasing " .. aTarget.lazeTargetType .. " [" .. aTarget.range .. "nm at " .. aTarget.bearing .. "°]," .. " code=" .. cfxGroundTroops.laseCode
|
||||
end
|
||||
end
|
||||
trigger.action.outTextForGroup(conf.id, desc .. "\n", 30)
|
||||
trigger.action.outSoundForGroup(conf.id, jtacGrpUI.jtacSound)
|
||||
end
|
||||
|
||||
function jtacGrpUI.collectJTACtargets(conf, includeIdle)
|
||||
@ -249,14 +232,14 @@ function jtacGrpUI.collectJTACtargets(conf, includeIdle)
|
||||
local theJTACS = {}
|
||||
for idx, troop in pairs(cfxGroundTroops.deployedTroops) do
|
||||
if troop.coalition == conf.coalition
|
||||
and troop.orders == "laze"
|
||||
and troop.lazeTarget
|
||||
and troop.lazeTarget:isExist()
|
||||
and troop.orders == "laze"
|
||||
and troop.lazeTarget
|
||||
and troop.lazeTarget:isExist()
|
||||
then
|
||||
table.insert(theJTACS, troop)
|
||||
elseif troop.coalition == conf.coalition
|
||||
and troop.orders == "laze"
|
||||
and includeIdle
|
||||
and troop.orders == "laze"
|
||||
and includeIdle
|
||||
then
|
||||
-- we also include idlers
|
||||
table.insert(theJTACS, troop)
|
||||
@ -277,7 +260,7 @@ function jtacGrpUI.collectJTACtargets(conf, includeIdle)
|
||||
local jtacLoc = dcsCommon.getGroupLocation(troop.group)
|
||||
local nearestZone = cfxOwnedZones.getNearestOwnedZoneToPoint(jtacLoc)
|
||||
if nearestZone then
|
||||
local ozRange = dcsCommon.dist(jtacLoc, nearestZone.point) * 0.000621371
|
||||
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 .. ")"
|
||||
@ -295,7 +278,6 @@ function jtacGrpUI.collectJTACtargets(conf, includeIdle)
|
||||
aTarget.range = aTarget.range * 0.000621371 -- meter to miles
|
||||
aTarget.range = math.floor(aTarget.range * 10) / 10
|
||||
aTarget.bearing = dcsCommon.bearingInDegreesFromAtoB(here, there)
|
||||
--aTarget.jtacName = troop.name
|
||||
aTarget.lazeTargetType = troop.lazeTargetType
|
||||
end
|
||||
table.insert(targetList, aTarget)
|
||||
@ -309,87 +291,82 @@ function jtacGrpUI.collectJTACtargets(conf, includeIdle)
|
||||
end
|
||||
|
||||
--
|
||||
-- G R O U P M A N A G E M E N T
|
||||
-- event handler - simplified, only for player birth
|
||||
--
|
||||
-- Group Management is required to make sure all groups
|
||||
-- receive a comms menu and that they receive a clean-up
|
||||
-- when required
|
||||
--
|
||||
-- Callbacks are provided by cfxPlayer module to which we
|
||||
-- subscribe during init
|
||||
--
|
||||
function jtacGrpUI.playerChangeEvent(evType, description, player, data)
|
||||
--trigger.action.outText("+++ groupUI: received <".. evType .. "> Event", 30)
|
||||
if evType == "newGroup" then
|
||||
-- initialized attributes are in data as follows
|
||||
-- .group - new group
|
||||
-- .name - new group's name
|
||||
-- .primeUnit - the unit that trigggered new group appearing
|
||||
-- .primeUnitName - name of prime unit
|
||||
-- .id group ID
|
||||
--theUnit = data.primeUnit
|
||||
jtacGrpUI.setCommsMenu(data.group)
|
||||
-- trigger.action.outText("+++ groupUI: added " .. theUnit:getName() .. " to comms menu", 30)
|
||||
return
|
||||
end
|
||||
|
||||
if evType == "removeGroup" then
|
||||
-- data is the player record that no longer exists. it consists of
|
||||
-- .name
|
||||
-- we must remove the comms menu for this group else we try to add another one to this group later
|
||||
local conf = jtacGrpUI.getConfigByGroupName(data.name)
|
||||
|
||||
if conf then
|
||||
jtacGrpUI.removeCommsFromConfig(conf) -- remove menus
|
||||
jtacGrpUI.resetConfig(conf) -- re-init this group for when it re-appears
|
||||
else
|
||||
trigger.action.outText("+++ jtacUI: can't retrieve group <" .. data.name .. "> config: not found!", 30)
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if evType == "leave" then
|
||||
-- player unit left. we don't care since we only work on group level
|
||||
-- if they were the only, this is followed up by group disappeared
|
||||
|
||||
end
|
||||
|
||||
if evType == "unit" then
|
||||
-- player changed units. almost never in MP, but possible in solo
|
||||
-- because of 1 seconds timing loop
|
||||
-- will result in a new group appearing and a group disappearing, so we are good
|
||||
-- may need some logic to clean up old configs and/or menu items
|
||||
|
||||
end
|
||||
function jtacGrpUI:onEvent(theEvent)
|
||||
if not theEvent then return end
|
||||
local theUnit = theEvent.initiator
|
||||
if not theUnit then return end
|
||||
local uName = theUnit:getName()
|
||||
if not theUnit.getPlayerName then return end
|
||||
if not theUnit:getPlayerName() then return end
|
||||
-- we now have a player birth event.
|
||||
local pName = theUnit:getPlayerName()
|
||||
local theGroup = theUnit:getGroup()
|
||||
if not theGroup then return end
|
||||
local gName = theGroup:getName()
|
||||
if not gName then return end
|
||||
if jtacGrpUI.verbose then
|
||||
trigger.action.outText("+++jGUI: birth player. 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
|
||||
|
||||
|
||||
--
|
||||
-- Start
|
||||
--
|
||||
function jtacGrpUI.start()
|
||||
function jtacGrpUI.readConfigZone()
|
||||
local theZone = cfxZones.getZoneByName("jtacGrpUIConfig")
|
||||
if not theZone then
|
||||
theZone = cfxZones.createSimpleZone("jtacGrpUIConfig")
|
||||
end
|
||||
|
||||
jtacGrpUI.jtacTypes = theZone:getStringFromZoneProperty("jtacTypes", "all")
|
||||
jtacGrpUI.jtacTypes = string.lower(jtacGrpUI.jtacTypes)
|
||||
|
||||
jtacGrpUI.jtacSound = theZone:getStringFromZoneProperty("jtacSound", "UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav")
|
||||
|
||||
jtacGrpUI.verbose = theZone.verbose
|
||||
|
||||
-- iterate existing groups so we have a start situation
|
||||
-- now iterate through all player groups and install the Assault Troop Menu
|
||||
allPlayerGroups = cfxPlayerGroups -- cfxPlayerGroups is a global, don't fuck with it!
|
||||
-- contains per group player record. Does not resolve on unit level!
|
||||
for gname, pgroup in pairs(allPlayerGroups) do
|
||||
local theUnit = pgroup.primeUnit -- get any unit of that group
|
||||
jtacGrpUI.setCommsMenuForUnit(theUnit) -- set up
|
||||
end
|
||||
-- now install the new group notifier to install Assault Troops menu
|
||||
|
||||
cfxPlayer.addMonitor(jtacGrpUI.playerChangeEvent)
|
||||
trigger.action.outText("cf/x jtacGrpUI v" .. jtacGrpUI.version .. " started", 30)
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
function jtacGrpUI.start()
|
||||
if not dcsCommon.libCheck then
|
||||
trigger.action.outText("cfx jtac GUI requires dcsCommon", 30)
|
||||
return false
|
||||
end
|
||||
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
|
||||
end
|
||||
|
||||
-- GO GO GO
|
||||
--
|
||||
if not cfxGroundTroops then
|
||||
trigger.action.outText("cf/x jtacGrpUI REQUIRES cfxGroundTroops to work.", 30)
|
||||
else
|
||||
jtacGrpUI.start()
|
||||
end
|
||||
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
|
||||
--]]--
|
||||
333
modules/milHelo.lua
Normal file
333
modules/milHelo.lua
Normal file
@ -0,0 +1,333 @@
|
||||
milHelo = {}
|
||||
milHelo.version = "0.0.0"
|
||||
milHelo.requiredLibs = {
|
||||
"dcsCommon",
|
||||
"cfxZones",
|
||||
"cfxMX",
|
||||
}
|
||||
milHelo.zones = {}
|
||||
milHelo.targets = {}
|
||||
milHelo.ups = 1
|
||||
|
||||
function milHelo.addMilHeloZone(theZone)
|
||||
milHelo.zones[theZone.name] = theZone
|
||||
end
|
||||
|
||||
function milHelo.addMilTargetZone(theZone)
|
||||
milHelo.targets[theZone.name] = theZone
|
||||
end
|
||||
|
||||
function milHelo.partOfGroupDataInZone(theZone, theUnits) -- move to mx?
|
||||
local zP = cfxZones.getPoint(theZone)
|
||||
zP = theZone:getDCSOrigin() -- don't use getPoint now.
|
||||
zP.y = 0
|
||||
|
||||
for idx, aUnit in pairs(theUnits) do
|
||||
local uP = {}
|
||||
uP.x = aUnit.x
|
||||
uP.y = 0
|
||||
uP.z = aUnit.y -- !! y-z
|
||||
if theZone:pointInZone(uP) then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function milHelo.allGroupsInZoneByData(theZone) -- move to MX?
|
||||
local theGroupsInZone = {}
|
||||
local count = 0
|
||||
for groupName, groupData in pairs(cfxMX.groupDataByName) do
|
||||
if groupData.units then
|
||||
if milHelo.partOfGroupDataInZone(theZone, groupData.units) then
|
||||
theGroupsInZone[groupName] = groupData -- DATA! work on clones!
|
||||
count = count + 1
|
||||
if theZone.verbose then
|
||||
trigger.action.outText("+++milH: added group <" .. groupName .. "> for zone <" .. theZone.name .. ">", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return theGroupsInZone, count
|
||||
end
|
||||
|
||||
function milHelo.readMilHeloZone(theZone) -- process attributes
|
||||
-- get mission type. part of milHelo
|
||||
theZone.msnType = string.lower(theZone:getStringFromZoneProperty("milHelo", "cas"))
|
||||
-- get all groups inside me
|
||||
local myGroups, count = milHelo.allGroupsInZoneByData(theZone)
|
||||
theZone.myGroups = myGroups
|
||||
theZone.groupCount = count
|
||||
theZone.coa = theZone:getCoalitionFromZoneProperty("coalition", 0)
|
||||
theZone.hot = theZone:getBoolFromZoneProperty("hot", true)
|
||||
theZone.speed = theZone:getNumberFromZoneProperty("speed", 50) -- 110 mph
|
||||
theZone.alt = theZone:getNumberFromZoneProperty("alt", 100) -- we are always radar alt
|
||||
-- wipe all existing
|
||||
for groupName, data in pairs(myGroups) do
|
||||
local g = Group.getByName(groupName)
|
||||
if g then
|
||||
Group.destroy(g)
|
||||
end
|
||||
end
|
||||
if theZone.verbose or milHelo.verbose then
|
||||
trigger.action.outText("+++milH: processed milHelo zone <" .. theZone.name .. ">", 30)
|
||||
end
|
||||
end
|
||||
|
||||
function milHelo.readMilTargetZone(theZone)
|
||||
|
||||
if theZone.verbose or milHelo.verbose then
|
||||
trigger.action.outText("+++milH: processed TARGET zone <" .. theZone.name .. ">", 30)
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Spawning for a zone
|
||||
--
|
||||
--[[--
|
||||
function milHelo.getNthItem(theSet, n)
|
||||
local count = 1
|
||||
for key, value in pairs(theSet) do
|
||||
if count == n then return value end
|
||||
count = count + 1
|
||||
end
|
||||
return nil
|
||||
end
|
||||
--]]--
|
||||
|
||||
function milHelo.createCASTask(num, auto)
|
||||
if not auto then auto = false end
|
||||
if not num then num = 1 end
|
||||
local task = {}
|
||||
task.number = num
|
||||
task.key = "CAS"
|
||||
task.id = "EngageTargets"
|
||||
task.enabled = true
|
||||
task.auto = auto
|
||||
local params = {}
|
||||
params.priority = 0
|
||||
-- params.targetTypes = {"Helicopters", "Ground Units", "Light armed ships"}
|
||||
local targetTypes = {[1] = "Helicopters", [2] = "Ground Units", [3] = "Light armed ships",}
|
||||
params.targetTypes = targetTypes
|
||||
|
||||
task.params = params
|
||||
return task
|
||||
end
|
||||
|
||||
function milHelo.createROETask(num, roe)
|
||||
if not num then num = 1 end
|
||||
if not roe then roe = 0 end
|
||||
local task = {}
|
||||
task.number = num
|
||||
task.enabled = true
|
||||
task.auto = false
|
||||
task.id = "WrappedAction"
|
||||
local params = {}
|
||||
local action = {}
|
||||
action.id = "Option"
|
||||
local p2 = {}
|
||||
p2.value = roe -- 0 = Weapons free
|
||||
p2.name = 0 -- name 0 = ROE
|
||||
action.params = p2
|
||||
params.action = action
|
||||
task.params = params
|
||||
return task
|
||||
end
|
||||
|
||||
function milHelo.createOrbitTask(num, duration, theZone)
|
||||
if not num then num = 1 end
|
||||
local task = {}
|
||||
task.number = num
|
||||
task.auto = false
|
||||
task.id = "ControlledTask"
|
||||
task.enabled = true
|
||||
local params = {}
|
||||
local t2 = {}
|
||||
t2.id = "Orbit"
|
||||
local p2 = {}
|
||||
p2.altitude = theZone.alt
|
||||
p2.pattern = "Circle"
|
||||
p2.speed = theZone.speed
|
||||
p2.altitudeEdited = true
|
||||
t2.params = p2
|
||||
params.task = t2
|
||||
params.stopCondition = {}
|
||||
params.stopCondition.duration = duration
|
||||
task.params = params
|
||||
return task
|
||||
end
|
||||
|
||||
function milHelo.createTakeOffWP(theZone)
|
||||
local WP = {}
|
||||
WP.alt = theZone.alt
|
||||
WP.alt_type = "RADIO"
|
||||
WP.properties = {}
|
||||
WP.properties.addopt = {}
|
||||
WP.action = "From Ground Area"
|
||||
if theZone.hot then WP.action = "From Ground Area Hot" end
|
||||
WP.speed = theZone.speed
|
||||
WP.task = {}
|
||||
WP.task.id = "ComboTask"
|
||||
WP.task.params = {}
|
||||
local tasks = {}
|
||||
-- local casTask = milHelo.createCASTask(1)
|
||||
-- tasks[1] = casTask
|
||||
local roeTask = milHelo.createROETask(1,0) -- 0 = weapons free
|
||||
tasks[1] = roeTask
|
||||
WP.task.params.tasks = tasks
|
||||
--
|
||||
WP.type = "TakeOffGround"
|
||||
if theZone.hot then WP.type = "TakeOffGroundHot" end
|
||||
p = theZone:getPoint()
|
||||
WP.x = p.x
|
||||
WP.y = p.z
|
||||
WP.ETA = 0
|
||||
WP.ETA_locked = false
|
||||
WP.speed_locked = true
|
||||
WP.formation_template = ""
|
||||
return WP
|
||||
end
|
||||
|
||||
|
||||
|
||||
function milHelo.createOrbitWP(theZone, targetPoint)
|
||||
local WP = {}
|
||||
WP.alt = theZone.alt
|
||||
WP.alt_type = "RADIO"
|
||||
WP.properties = {}
|
||||
WP.properties.addopt = {}
|
||||
WP.action = "Turning Point"
|
||||
WP.speed = theZone.speed
|
||||
WP.task = {}
|
||||
WP.task.id = "ComboTask"
|
||||
WP.task.params = {}
|
||||
-- start params construct
|
||||
local tasks = {}
|
||||
local casTask = milHelo.createCASTask(1, false)
|
||||
tasks[1] = casTask
|
||||
local oTask = milHelo.createOrbitTask(2, 3600, theZone)
|
||||
tasks[2] = oTask
|
||||
WP.task.params.tasks = tasks
|
||||
WP.type = "Turning Point"
|
||||
|
||||
WP.x = targetPoint.x
|
||||
WP.y = targetPoint.z
|
||||
WP.ETA = 0
|
||||
WP.ETA_locked = false
|
||||
WP.speed_locked = true
|
||||
WP.formation_template = ""
|
||||
return WP
|
||||
end
|
||||
|
||||
function milHelo.spawnForZone(theZone, targetZone)
|
||||
local theRawData = dcsCommon.getNthItem(theZone.myGroups, 1)
|
||||
local gData = dcsCommon.clone(theRawData)
|
||||
--[[--
|
||||
-- pre-process gData: names, id etc
|
||||
gData.name = dcsCommon.uuid(gData.name)
|
||||
for idx, uData in pairs(gData.units) do
|
||||
uData.name = dcsCommon.uuid(uData.name)
|
||||
end
|
||||
gData.groupId = nil
|
||||
|
||||
-- change task according to missionType in Zone
|
||||
gData.task = "CAS"
|
||||
|
||||
-- create and process route
|
||||
local route = {}
|
||||
route.points = {}
|
||||
-- gData.route = route
|
||||
-- create take-off waypoint
|
||||
local wpTOff = milHelo.createTakeOffWP(theZone)
|
||||
-- depending on mission, create an orbit or land WP
|
||||
local dest = targetZone:getPoint()
|
||||
local wpDest = milHelo.createOrbitWP(theZone, dest)
|
||||
-- move group to WP1 and add WP1 and WP2 to route
|
||||
-- dcsCommon.moveGroupDataTo(theGroup,
|
||||
-- fromWP.x,
|
||||
-- fromWP.y)
|
||||
|
||||
----
|
||||
dcsCommon.addRoutePointForGroupData(gData, wpTOff)
|
||||
dcsCommon.addRoutePointForGroupData(gData, wpDest)
|
||||
--]]--
|
||||
dcsCommon.dumpVar2Str("route", gData.route)
|
||||
|
||||
-- make it a cty
|
||||
if theZone.coa == 0 then
|
||||
trigger.action.outText("+++milH: WARNING - zone <" .. theZone.name .. "> is NEUTRAL", 30)
|
||||
end
|
||||
local cty = dcsCommon.getACountryForCoalition(theZone.coa)
|
||||
-- spawn
|
||||
local groupCat = Group.Category.HELICOPTER
|
||||
local theSpawnedGroup = coalition.addGroup(cty, groupCat, gData)
|
||||
|
||||
return theSpawnedGroup, gData
|
||||
end
|
||||
--
|
||||
-- update and event
|
||||
--
|
||||
function milHelo.update()
|
||||
timer.scheduleFunction(milHelo.update, {}, timer.getTime() + 1)
|
||||
end
|
||||
|
||||
function milHelo.onEvent(theEvent)
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Config & start
|
||||
--
|
||||
function milHelo.readConfigZone()
|
||||
local theZone = cfxZones.getZoneByName("milHeloConfig")
|
||||
if not theZone then
|
||||
theZone = cfxZones.createSimpleZone("milHeloConfig")
|
||||
end
|
||||
milHelo.verbose = theZone.verbose
|
||||
end
|
||||
|
||||
|
||||
function milHelo.start()
|
||||
-- lib check
|
||||
if not dcsCommon.libCheck then
|
||||
trigger.action.outText("cfx civ helo requires dcsCommon", 30)
|
||||
return false
|
||||
end
|
||||
if not dcsCommon.libCheck("cfx mil helo", milHelo.requiredLibs) then
|
||||
return false
|
||||
end
|
||||
|
||||
-- read config
|
||||
milHelo.readConfigZone()
|
||||
|
||||
-- process milHelo Zones
|
||||
local attrZones = cfxZones.getZonesWithAttributeNamed("milHelo")
|
||||
for k, aZone in pairs(attrZones) do
|
||||
milHelo.readMilHeloZone(aZone) -- process attributes
|
||||
milHelo.addMilHeloZone(aZone) -- add to list
|
||||
end
|
||||
|
||||
attrZones = cfxZones.getZonesWithAttributeNamed("milTarget")
|
||||
for k, aZone in pairs(attrZones) do
|
||||
milHelo.readMilTargetZone(aZone) -- process attributes
|
||||
milHelo.addMilTargetZone(aZone) -- add to list
|
||||
end
|
||||
|
||||
-- start update in 5 seconds
|
||||
timer.scheduleFunction(milHelo.update, {}, timer.getTime() + 1/milHelo.ups)
|
||||
|
||||
-- install event handler
|
||||
world.addEventHandler(milHelo)
|
||||
|
||||
-- say hi
|
||||
trigger.action.outText("milHelo v" .. milHelo.version .. " started.", 30)
|
||||
return true
|
||||
end
|
||||
|
||||
if not milHelo.start() then
|
||||
trigger.action.outText("milHelo failed to start.", 30)
|
||||
milHelo = nil
|
||||
end
|
||||
|
||||
-- do some one-time stuff
|
||||
local theZone = dcsCommon.getFirstItem(milHelo.zones)
|
||||
local targetZone = dcsCommon.getFirstItem(milHelo.targets)
|
||||
milHelo.spawnForZone(theZone, targetZone)
|
||||
@ -1,5 +1,5 @@
|
||||
unGrief = {}
|
||||
unGrief.version = "1.2.0"
|
||||
unGrief.version = "2.0.0"
|
||||
unGrief.verbose = false
|
||||
unGrief.ups = 1
|
||||
unGrief.requiredLibs = {
|
||||
@ -21,6 +21,11 @@ unGrief.disabledFlagValue = unGrief.enabledFlagValue + 100 -- DO NOT CHANGE
|
||||
- strict rules
|
||||
- warnings on enter/exit
|
||||
- warnings optional
|
||||
2.0.0 - dmlZones
|
||||
- also trigger on birth event, more wrathful
|
||||
- auto-turn on ssb when retaliation is SSB
|
||||
- re-open slot after kick in 15 seconds
|
||||
|
||||
|
||||
--]]--
|
||||
|
||||
@ -55,13 +60,20 @@ function unGrief.createPvpWithZone(theZone)
|
||||
trigger.action.outText("+++uGrf: <" .. theZone.name .. "> is designated as PVP legal", 30)
|
||||
end
|
||||
|
||||
theZone.strictPVP = cfxZones.getBoolFromZoneProperty(theZone, "strict", false)
|
||||
theZone.strictPVP = theZone:getBoolFromZoneProperty("strict", false)
|
||||
|
||||
end
|
||||
|
||||
-- vengeance: if player killed before, they are no longer welcome
|
||||
function unGrief.reconcile(groupName)
|
||||
-- re-open slot after player was kicked
|
||||
trigger.action.setUserFlag(groupName, unGrief.enabledFlagValue)
|
||||
trigger.action.outText("Group <" .. groupName .. "> now available again after pest control action", 30)
|
||||
end
|
||||
|
||||
function unGrief.exactVengance(theEvent)
|
||||
if theEvent.id == 20 then -- S_EVENT_PLAYER_ENTER_UNIT
|
||||
if theEvent.id == 20 or -- S_EVENT_PLAYER_ENTER_UNIT
|
||||
theEvent.id == 15 then -- Birth
|
||||
if not theEvent.initiator then return end
|
||||
local theUnit = theEvent.initiator
|
||||
if not theUnit.getPlayerName then return end -- wierd stuff happening here
|
||||
@ -94,6 +106,7 @@ function unGrief.exactVengance(theEvent)
|
||||
-- tell ssb to kick now:
|
||||
trigger.action.setUserFlag(groupName, unGrief.disabledFlagValue)
|
||||
trigger.action.outText("Player <" .. playerName .. "> is not welcome here. Shoo! Shoo!", 30)
|
||||
timer.scheduleFunction(unGrief.reconcile, groupName, timer.getTime() + 15)
|
||||
return
|
||||
end
|
||||
|
||||
@ -200,6 +213,7 @@ function unGrief:onEvent(theEvent)
|
||||
local groupName = theGroup:getName()
|
||||
-- tell ssb to kick now:
|
||||
trigger.action.setUserFlag(groupName, unGrief.disabledFlagValue)
|
||||
timer.scheduleFunction(unGrief.reconcile, groupName, timer.getTime() + 15)
|
||||
return
|
||||
end
|
||||
-- aaand all your base are belong to us!
|
||||
@ -256,23 +270,27 @@ function unGrief.readConfigZone()
|
||||
theZone = cfxZone.createSimpleZone("unGriefConfig")
|
||||
end
|
||||
|
||||
unGrief.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
||||
unGrief.verbose = theZone.verbose
|
||||
|
||||
unGrief.graceKills = cfxZones.getNumberFromZoneProperty(theZone, "graceKills", 1)
|
||||
unGrief.retaliation = cfxZones.getStringFromZoneProperty(theZone, "retaliation", "boom") -- other possible methods: ssb
|
||||
unGrief.graceKills = theZone:getNumberFromZoneProperty("graceKills", 1)
|
||||
unGrief.retaliation = theZone:getStringFromZoneProperty("retaliation", "boom") -- other possible methods: ssb
|
||||
unGrief.retaliation = dcsCommon.trim(unGrief.retaliation:lower())
|
||||
|
||||
|
||||
unGrief.wrathful = cfxZones.getBoolFromZoneProperty(theZone, "wrathful", false)
|
||||
|
||||
unGrief.pve = cfxZones.getBoolFromZoneProperty(theZone, "pve", false)
|
||||
if cfxZones.hasProperty(theZone, "pveOnly") then
|
||||
unGrief.pve = cfxZones.getBoolFromZoneProperty(theZone, "pveOnly", false)
|
||||
if unGrief.retaliation == "ssb" then
|
||||
-- now turn on ssb
|
||||
trigger.action.setUserFlag("SSB",100)
|
||||
trigger.action.outText("unGrief: SSB enabled for retaliation.", 30)
|
||||
end
|
||||
|
||||
unGrief.ignoreAI = cfxZones.getBoolFromZoneProperty(theZone, "ignoreAI", false)
|
||||
unGrief.wrathful = theZone:getBoolFromZoneProperty("wrathful", false)
|
||||
|
||||
unGrief.PVPwarnings = cfxZones.getBoolFromZoneProperty(theZone, "warnings", true)
|
||||
unGrief.pve = theZone:getBoolFromZoneProperty("pve", false)
|
||||
if theZone:hasProperty("pveOnly") then
|
||||
unGrief.pve = theZone:getBoolFromZoneProperty("pveOnly", false)
|
||||
end
|
||||
|
||||
unGrief.ignoreAI = theZone:getBoolFromZoneProperty("ignoreAI", false)
|
||||
|
||||
unGrief.PVPwarnings = theZone:getBoolFromZoneProperty("warnings", true)
|
||||
|
||||
if unGrief.verbose then
|
||||
trigger.action.outText("+++uGrf: read config", 30)
|
||||
|
||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user