mirror of
https://github.com/weyne85/DML.git
synced 2025-10-29 16:57:49 +00:00
Version 2.2.2
Maintenance update
This commit is contained in:
parent
8f7371825d
commit
7e4c147071
Binary file not shown.
Binary file not shown.
@ -1,5 +1,5 @@
|
|||||||
FARPZones = {}
|
FARPZones = {}
|
||||||
FARPZones.version = "1.2.1"
|
FARPZones.version = "2.0.0"
|
||||||
FARPZones.verbose = false
|
FARPZones.verbose = false
|
||||||
--[[--
|
--[[--
|
||||||
Version History
|
Version History
|
||||||
@ -15,7 +15,7 @@ FARPZones.verbose = false
|
|||||||
- handles contested state
|
- handles contested state
|
||||||
1.2.1 - now gracefully handles a FARP Zone that does not
|
1.2.1 - now gracefully handles a FARP Zone that does not
|
||||||
contain a FARP, but is placed beside it
|
contain a FARP, but is placed beside it
|
||||||
|
2.0.0 - dmlZones
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
@ -120,9 +120,9 @@ function FARPZones.createFARPFromZone(aZone)
|
|||||||
local theFarp = {}
|
local theFarp = {}
|
||||||
theFarp.zone = aZone
|
theFarp.zone = aZone
|
||||||
theFarp.name = aZone.name
|
theFarp.name = aZone.name
|
||||||
theFarp.point = cfxZones.getPoint(aZone) -- failsafe
|
theFarp.point = aZone:getPoint() -- failsafe
|
||||||
-- find the FARPS that belong to this zone
|
-- find the FARPS that belong to this zone
|
||||||
local thePoint = cfxZones.getPoint(aZone)
|
local thePoint = aZone:getPoint()
|
||||||
local mapFarps = dcsCommon.getAirbasesInRangeOfPoint(
|
local mapFarps = dcsCommon.getAirbasesInRangeOfPoint(
|
||||||
thePoint,
|
thePoint,
|
||||||
aZone.radius,
|
aZone.radius,
|
||||||
@ -156,10 +156,7 @@ function FARPZones.createFARPFromZone(aZone)
|
|||||||
-- end
|
-- end
|
||||||
|
|
||||||
-- get r and phi for defenders
|
-- get r and phi for defenders
|
||||||
local rPhi = cfxZones.getVectorFromZoneProperty(
|
local rPhi = aZone:getVectorFromZoneProperty("rPhiHDef",3)
|
||||||
aZone,
|
|
||||||
"rPhiHDef",
|
|
||||||
3)
|
|
||||||
|
|
||||||
-- get r and phi for facilities
|
-- get r and phi for facilities
|
||||||
-- create a new defenderzone for this
|
-- create a new defenderzone for this
|
||||||
@ -167,17 +164,14 @@ function FARPZones.createFARPFromZone(aZone)
|
|||||||
local phi = rPhi[2] * 0.0174533 -- 1 degree = 0.0174533 rad
|
local phi = rPhi[2] * 0.0174533 -- 1 degree = 0.0174533 rad
|
||||||
local dx = aZone.point.x + r * math.cos(phi)
|
local dx = aZone.point.x + r * math.cos(phi)
|
||||||
local dz = aZone.point.z + r * math.sin(phi)
|
local dz = aZone.point.z + r * math.sin(phi)
|
||||||
local formRad = cfxZones.getNumberFromZoneProperty(aZone, "rFormation", 100)
|
local formRad = aZone:getNumberFromZoneProperty("rFormation", 100)
|
||||||
|
|
||||||
theFarp.defZone = cfxZones.createSimpleZone(aZone.name .. "-Def", {x=dx, y = 0, z=dz}, formRad)
|
theFarp.defZone = cfxZones.createSimpleZone(aZone.name .. "-Def", {x=dx, y = 0, z=dz}, formRad)
|
||||||
theFarp.defHeading = rPhi[3]
|
theFarp.defHeading = rPhi[3]
|
||||||
|
|
||||||
rPhi = {}
|
rPhi = {}
|
||||||
rPhi = cfxZones.getVectorFromZoneProperty(
|
rPhi = aZone:getVectorFromZoneProperty("rPhiHRes", 3)
|
||||||
aZone,
|
|
||||||
"rPhiHRes",
|
|
||||||
3)
|
|
||||||
--trigger.action.outText("*** RES rPhi are " .. rPhi[1] .. " and " .. rPhi[2] .. " heading " .. rPhi[3], 30)
|
|
||||||
r = rPhi[1]
|
r = rPhi[1]
|
||||||
phi = rPhi[2] * 0.0174533 -- 1 degree = 0.0174533 rad
|
phi = rPhi[2] * 0.0174533 -- 1 degree = 0.0174533 rad
|
||||||
dx = aZone.point.x + r * math.cos(phi)
|
dx = aZone.point.x + r * math.cos(phi)
|
||||||
@ -187,17 +181,18 @@ function FARPZones.createFARPFromZone(aZone)
|
|||||||
theFarp.resHeading = rPhi[3]
|
theFarp.resHeading = rPhi[3]
|
||||||
|
|
||||||
-- get redDefenders - defenders produced when red owned
|
-- get redDefenders - defenders produced when red owned
|
||||||
theFarp.redDefenders = cfxZones.getStringFromZoneProperty(aZone, "redDefenders", "none")
|
theFarp.redDefenders = aZone:getStringFromZoneProperty( "redDefenders", "none")
|
||||||
-- get blueDefenders - defenders produced when blue owned
|
-- get blueDefenders - defenders produced when blue owned
|
||||||
theFarp.blueDefenders = cfxZones.getStringFromZoneProperty(aZone, "blueDefenders", "none")
|
theFarp.blueDefenders = aZone:getStringFromZoneProperty( "blueDefenders", "none")
|
||||||
-- get formation for defenders
|
-- get formation for defenders
|
||||||
theFarp.formation = cfxZones.getStringFromZoneProperty(aZone, "formation", "circle_out")
|
theFarp.formation = aZone:getStringFromZoneProperty("formation", "circle_out")
|
||||||
theFarp.count = 0 -- for uniqueness
|
theFarp.count = 0 -- for uniqueness
|
||||||
theFarp.hideRed = cfxZones.getBoolFromZoneProperty(aZone, "hideRed")
|
theFarp.hideRed = aZone:getBoolFromZoneProperty("hideRed", false)
|
||||||
theFarp.hideBlue = cfxZones.getBoolFromZoneProperty(aZone, "hideBlue")
|
theFarp.hideBlue = aZone:getBoolFromZoneProperty("hideBlue", false)
|
||||||
theFarp.hideGrey = cfxZones.getBoolFromZoneProperty(aZone, "hideGrey")
|
theFarp.hideGrey = aZone:getBoolFromZoneProperty("hideGrey", false)
|
||||||
theFarp.hidden = cfxZones.getBoolFromZoneProperty(aZone, "hidden")
|
theFarp.hidden = aZone:getBoolFromZoneProperty("hidden", false)
|
||||||
|
|
||||||
|
theFarp.neutralProduction = aZone:getBoolFromZoneProperty("neutralProduction", false)
|
||||||
return theFarp
|
return theFarp
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -225,7 +220,7 @@ function FARPZones.drawFARPCircleInMap(theFarp)
|
|||||||
|
|
||||||
if theFarp.hideGrey and
|
if theFarp.hideGrey and
|
||||||
theFarp.owner == 0 then
|
theFarp.owner == 0 then
|
||||||
-- hide only when blue
|
-- hide only when grey
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -258,7 +253,7 @@ function FARPZones.drawFARPCircleInMap(theFarp)
|
|||||||
aZone.markID = markID
|
aZone.markID = markID
|
||||||
|
|
||||||
end
|
end
|
||||||
|
--[[--
|
||||||
function FARPZones.drawZoneInMap(aZone, owner)
|
function FARPZones.drawZoneInMap(aZone, owner)
|
||||||
-- owner is 0 = neutral, 1 = red, 2 = blue
|
-- owner is 0 = neutral, 1 = red, 2 = blue
|
||||||
-- will save markID in zone's markID
|
-- will save markID in zone's markID
|
||||||
@ -288,6 +283,7 @@ function FARPZones.drawZoneInMap(aZone, owner)
|
|||||||
aZone.markID = markID
|
aZone.markID = markID
|
||||||
|
|
||||||
end
|
end
|
||||||
|
--]]--
|
||||||
|
|
||||||
function FARPZones.scheduedProduction(args)
|
function FARPZones.scheduedProduction(args)
|
||||||
-- args contain [aFarp, owner]
|
-- args contain [aFarp, owner]
|
||||||
@ -311,6 +307,16 @@ function FARPZones.scheduedProduction(args)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function FARPZones.produceVehicles(theFarp)
|
function FARPZones.produceVehicles(theFarp)
|
||||||
|
local theZone = theFarp.zone
|
||||||
|
-- trigger.action.outText("entering veh prod run for farp zone <" .. theZone.name .. ">, owner is <" .. theFarp.owner .. ">", 30)
|
||||||
|
--end
|
||||||
|
|
||||||
|
-- abort production if farp is owned by neutral and
|
||||||
|
-- neutralproduction is false
|
||||||
|
if theFarp.owner == 0 and not theFarp.neutralProduction then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
-- first, remove anything that may still be there
|
-- first, remove anything that may still be there
|
||||||
if theFarp.defenders and theFarp.defenders:isExist() then
|
if theFarp.defenders and theFarp.defenders:isExist() then
|
||||||
theFarp.defenders:destroy()
|
theFarp.defenders:destroy()
|
||||||
@ -529,20 +535,13 @@ end
|
|||||||
function FARPZones.readConfig()
|
function FARPZones.readConfig()
|
||||||
local theZone = cfxZones.getZoneByName("farpZonesConfig")
|
local theZone = cfxZones.getZoneByName("farpZonesConfig")
|
||||||
if not theZone then
|
if not theZone then
|
||||||
if FARPZones.verbose then
|
theZone = cfxZones.createSimpleZone("farpZonesConfig")
|
||||||
trigger.action.outText("***frpZ: NO config zone!", 30)
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
end
|
||||||
|
|
||||||
FARPZones.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
FARPZones.verbose = theZone.verbose
|
||||||
|
|
||||||
FARPZones.spinUpDelay = cfxZones.getNumberFromZoneProperty(theZone, "spinUpDelay", 30)
|
FARPZones.spinUpDelay = theZone:getNumberFromZoneProperty( "spinUpDelay", 30)
|
||||||
|
|
||||||
|
|
||||||
if FARPZones.verbose then
|
|
||||||
trigger.action.outText("***frpZ: read config", 30)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -618,4 +617,5 @@ Improvements:
|
|||||||
|
|
||||||
make farps repair their service vehicles after a time, or simply refresh them every x minutes, to make the algo simpler
|
make farps repair their service vehicles after a time, or simply refresh them every x minutes, to make the algo simpler
|
||||||
|
|
||||||
|
allow for ownership control via the airfield module?
|
||||||
--]]--
|
--]]--
|
||||||
@ -1,5 +1,5 @@
|
|||||||
autoCSAR = {}
|
autoCSAR = {}
|
||||||
autoCSAR.version = "2.0.0"
|
autoCSAR.version = "2.0.1"
|
||||||
autoCSAR.requiredLibs = {
|
autoCSAR.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
"cfxZones", -- Zones, of course
|
"cfxZones", -- Zones, of course
|
||||||
@ -14,6 +14,8 @@ autoCSAR.trackedEjects = {} -- we start tracking on eject
|
|||||||
1.1.0 - allow open water CSAR, fake pilot with GRG Soldier
|
1.1.0 - allow open water CSAR, fake pilot with GRG Soldier
|
||||||
- can be disabled by seaCSAR = false
|
- can be disabled by seaCSAR = false
|
||||||
2.0.0 - OOP, code clean-up
|
2.0.0 - OOP, code clean-up
|
||||||
|
2.0.1 - fix for coalition change when ejected player changes coas or is forced to neutral
|
||||||
|
- GC
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
function autoCSAR.removeGuy(args)
|
function autoCSAR.removeGuy(args)
|
||||||
@ -30,13 +32,16 @@ function autoCSAR.isOverWater(theUnit)
|
|||||||
return surf == 2 or surf == 3
|
return surf == 2 or surf == 3
|
||||||
end
|
end
|
||||||
|
|
||||||
function autoCSAR.createNewCSAR(theUnit)
|
function autoCSAR.createNewCSAR(theUnit, coa)
|
||||||
if not csarManager then
|
if not csarManager then
|
||||||
trigger.action.outText("+++aCSAR: CSAR Manager not loaded, aborting", 30)
|
trigger.action.outText("+++aCSAR: CSAR Manager not loaded, aborting", 30)
|
||||||
end
|
end
|
||||||
-- enter with unit from landing_after_eject event
|
-- enter with unit from landing_after_eject event
|
||||||
-- unit has no group
|
-- unit has no group
|
||||||
local coa = theUnit:getCoalition()
|
if not coa then
|
||||||
|
trigger.action.outText("+++autoCSAR: unresolved coalition, assumed neutral", 30)
|
||||||
|
coa = 0
|
||||||
|
end
|
||||||
if coa == 0 then -- neutral
|
if coa == 0 then -- neutral
|
||||||
trigger.action.outText("Neutral Pilot made it safely to ground.", 30)
|
trigger.action.outText("Neutral Pilot made it safely to ground.", 30)
|
||||||
return
|
return
|
||||||
@ -75,7 +80,7 @@ function autoCSAR.createNewCSAR(theUnit)
|
|||||||
theUnit = allUnits[1] -- get first (and only) unit
|
theUnit = allUnits[1] -- get first (and only) unit
|
||||||
end
|
end
|
||||||
-- create a CSAR mission now
|
-- create a CSAR mission now
|
||||||
csarManager.createCSARForParachutist(theUnit, "Xray-" .. autoCSAR.counter)
|
csarManager.createCSARForParachutist(theUnit, "Xray-" .. autoCSAR.counter, coa)
|
||||||
autoCSAR.counter = autoCSAR.counter + 1
|
autoCSAR.counter = autoCSAR.counter + 1
|
||||||
|
|
||||||
-- schedule removal of pilot
|
-- schedule removal of pilot
|
||||||
@ -88,37 +93,67 @@ function autoCSAR.createNewCSAR(theUnit)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- we backtrack the pilot to their seat to their plane if they have ejector seat
|
||||||
|
autoCSAR.pilotInfo = {}
|
||||||
function autoCSAR:onEvent(event)
|
function autoCSAR:onEvent(event)
|
||||||
|
if not event.initiator then return end
|
||||||
|
local initiator = event.initiator
|
||||||
if event.id == 31 then -- landing_after_eject, does not happen at sea
|
if event.id == 31 then -- landing_after_eject, does not happen at sea
|
||||||
-- to prevent double invocations for same process
|
-- to prevent double invocations for same process
|
||||||
-- check that we are still tracking this ejection
|
-- check that we are still tracking this ejection
|
||||||
if event.initiator then
|
local uid = tonumber(initiator:getID())
|
||||||
local uid = tonumber(event.initiator:getID())
|
if autoCSAR.trackedEjects[uid] then
|
||||||
if autoCSAR.trackedEjects[uid] then
|
trigger.action.outText("aCSAR: filtered double sea csar (player) event for uid = <" .. uid .. ">", 30)
|
||||||
trigger.action.outText("aCSAR: filtered double sea csar (player) event for uid = <" .. uid .. ">", 30)
|
autoCSAR.trackedEjects[uid] = nil -- reset
|
||||||
autoCSAR.trackedEjects[uid] = nil -- reset
|
return
|
||||||
return
|
|
||||||
end
|
|
||||||
autoCSAR.createNewCSAR(event.initiator)
|
|
||||||
end
|
end
|
||||||
|
-- now get the coalition of the pilot.
|
||||||
|
-- if pilot had an ejection seat, we need to get the seat's coa
|
||||||
|
local coa = initiator:getCoalition()
|
||||||
|
for idx, info in pairs(autoCSAR.pilotInfo) do
|
||||||
|
if info.pilot == initiator then
|
||||||
|
coa = info.coa
|
||||||
|
info.matched = true -- for GC
|
||||||
|
end
|
||||||
|
end
|
||||||
|
autoCSAR.createNewCSAR(initiator, coa)
|
||||||
end
|
end
|
||||||
|
|
||||||
if event.id == 6 and autoCSAR.seaCSAR then -- eject, start tracking
|
if event.id == 33 then -- discard chair, connect pilot with seat
|
||||||
if event.initiator then
|
for idx, info in pairs(autoCSAR.pilotInfo) do
|
||||||
|
if info.seat == event.target then
|
||||||
|
info.pilot = initiator
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if event.id == 6 then -- eject, start tracking, remember coa
|
||||||
|
local coa = event.initiator:getCoalition()
|
||||||
|
|
||||||
|
-- see if pilot has ejector seat and prepare to connect one with the other
|
||||||
|
local info = nil
|
||||||
|
if event.target and event.target:isExist() then
|
||||||
|
info = {}
|
||||||
|
info.coa = coa
|
||||||
|
info.seat = event.target
|
||||||
|
table.insert(autoCSAR.pilotInfo, info)
|
||||||
|
end
|
||||||
|
|
||||||
|
local uid = tonumber(event.initiator:getID())
|
||||||
|
autoCSAR.trackedEjects[uid] = nil -- set to not handled (yet)
|
||||||
|
|
||||||
|
if autoCSAR.seaCSAR then
|
||||||
-- see if this happened over open water and immediately
|
-- see if this happened over open water and immediately
|
||||||
-- create a seaCSAR
|
-- create a seaCSAR immediately
|
||||||
|
if autoCSAR.isOverWater(initiator) then
|
||||||
if autoCSAR.isOverWater(event.initiator) then
|
autoCSAR.createNewCSAR(initiator, initiator:getCoalition())
|
||||||
autoCSAR.createNewCSAR(event.initiator)
|
-- mark this one as completed
|
||||||
|
autoCSAR.trackedEjects[uid] = "processed" -- remember, so to not proc again
|
||||||
|
if info then info.matched = true end -- discard this one too in next GC
|
||||||
end
|
end
|
||||||
|
|
||||||
-- also mark this one as completed
|
|
||||||
local uid = tonumber(event.initiator:getID())
|
|
||||||
autoCSAR.trackedEjects[uid] = "processed"
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function autoCSAR.readConfigZone()
|
function autoCSAR.readConfigZone()
|
||||||
@ -147,6 +182,19 @@ function autoCSAR.readConfigZone()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function autoCSAR.GC()
|
||||||
|
timer.scheduleFunction(autoCSAR.GC, {}, timer.getTime() + 30 * 60) -- once every half hour
|
||||||
|
local filtered = {}
|
||||||
|
for idx, info in pairs(autoCSAR.pilotInfo) do
|
||||||
|
if info.matched then
|
||||||
|
-- skip it for next round
|
||||||
|
else
|
||||||
|
table.insert(filtered, info)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
autoCSAR.pilotInfo = filtered
|
||||||
|
end
|
||||||
|
|
||||||
function autoCSAR.start()
|
function autoCSAR.start()
|
||||||
-- lib check
|
-- lib check
|
||||||
if not dcsCommon.libCheck then
|
if not dcsCommon.libCheck then
|
||||||
@ -163,6 +211,9 @@ function autoCSAR.start()
|
|||||||
-- connect event handler
|
-- connect event handler
|
||||||
world.addEventHandler(autoCSAR)
|
world.addEventHandler(autoCSAR)
|
||||||
|
|
||||||
|
-- start GC
|
||||||
|
timer.scheduleFunction(autoCSAR.GC, {}, timer.getTime() + 1)
|
||||||
|
|
||||||
trigger.action.outText("cfx autoCSAR v" .. autoCSAR.version .. " started.", 30)
|
trigger.action.outText("cfx autoCSAR v" .. autoCSAR.version .. " started.", 30)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|||||||
105
modules/bank.lua
Normal file
105
modules/bank.lua
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
bank = {}
|
||||||
|
bank.version = "0.0.0"
|
||||||
|
bank.requiredLibs = {
|
||||||
|
"dcsCommon", -- always
|
||||||
|
"cfxZones", -- Zones, of course
|
||||||
|
}
|
||||||
|
bank.acts = {}
|
||||||
|
|
||||||
|
function bank.addFunds(act, amt)
|
||||||
|
if not act then act = "!!NIL!!" end
|
||||||
|
if act == 1 then act = "red" end
|
||||||
|
if act == 2 then act = "blue" end
|
||||||
|
if act == 0 then act = "neutral" end
|
||||||
|
act = string.lower(act)
|
||||||
|
|
||||||
|
local curVal = bank.acts[act]
|
||||||
|
if not curVal then
|
||||||
|
trigger.action.outText("+++Bank: no account <" .. act .. "> found. No transaction", 30)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
bank.acts[act] = curVal + amt
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function bank.withdawFunds(act, amt)
|
||||||
|
if not act then act = "!!NIL!!" end
|
||||||
|
if act == 1 then act = "red" end
|
||||||
|
if act == 2 then act = "blue" end
|
||||||
|
if act == 0 then act = "neutral" end
|
||||||
|
act = string.lower(act)
|
||||||
|
|
||||||
|
local curVal = bank.acts[act]
|
||||||
|
if not curVal then
|
||||||
|
trigger.action.outText("+++Bank: no account <" .. act .. "> found. No transaction", 30)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if amt > curVal then return false end
|
||||||
|
|
||||||
|
bank.acts[act] = curVal - amt
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function bank.getBalance(act)
|
||||||
|
if not act then act = "!!NIL!!" end
|
||||||
|
if act == 1 then act = "red" end
|
||||||
|
if act == 2 then act = "blue" end
|
||||||
|
if act == 0 then act = "neutral" end
|
||||||
|
act = string.lower(act)
|
||||||
|
|
||||||
|
local curVal = bank.acts[act]
|
||||||
|
if not curVal then
|
||||||
|
trigger.action.outText("+++Bank: no account <" .. act .. "> found. No transaction", 30)
|
||||||
|
return false, 0
|
||||||
|
end
|
||||||
|
|
||||||
|
return true, curVal
|
||||||
|
end
|
||||||
|
|
||||||
|
function bank.openAccount(act, amount)
|
||||||
|
if not amount then amount = 0 end
|
||||||
|
if bank.acts[act] then return false end -- account exists
|
||||||
|
bank.acts[act] = amount
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function bank.readConfigZone()
|
||||||
|
local theZone = cfxZones.getZoneByName("bankConfig")
|
||||||
|
if not theZone then
|
||||||
|
theZone = cfxZones.createSimpleZone("bankConfig")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set initial balances
|
||||||
|
bank.red = theZone:getNumberFromZoneProperty ("red", 1000)
|
||||||
|
bank.blue = theZone:getNumberFromZoneProperty ("blue", 1000)
|
||||||
|
bank.neutral = theZone:getNumberFromZoneProperty ("neutral", 1000)
|
||||||
|
|
||||||
|
bank.acts["red"] = bank.red
|
||||||
|
bank.acts["blue"] = bank.blue
|
||||||
|
bank.acts["neutral"] = bank.neutral
|
||||||
|
|
||||||
|
bank.verbose = theZone.verbose
|
||||||
|
end
|
||||||
|
|
||||||
|
function bank.start()
|
||||||
|
-- lib check
|
||||||
|
if not dcsCommon.libCheck then
|
||||||
|
trigger.action.outText("bank requires dcsCommon", 30)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if not dcsCommon.libCheck("bank", bank.requiredLibs) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- read config
|
||||||
|
bank.readConfigZone()
|
||||||
|
|
||||||
|
trigger.action.outText("bank v" .. bank.version .. " started.", 30)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if not bank.start() then
|
||||||
|
trigger.action.outText("bank aborted: missing libraries", 30)
|
||||||
|
bank = nil
|
||||||
|
end
|
||||||
467
modules/camp.lua
Normal file
467
modules/camp.lua
Normal file
@ -0,0 +1,467 @@
|
|||||||
|
camp = {}
|
||||||
|
camp.ups = 1
|
||||||
|
camp.version = "0.0.0"
|
||||||
|
camp.requiredLibs = {
|
||||||
|
"dcsCommon", -- always
|
||||||
|
"cfxZones", -- Zones, of course
|
||||||
|
"cfxMX",
|
||||||
|
"bank"
|
||||||
|
}
|
||||||
|
|
||||||
|
--
|
||||||
|
-- CURRENTLY REQUIRES SINGLE-UNIT PLAYER GROUPS
|
||||||
|
--
|
||||||
|
camp.camps = {} -- all camps on the map
|
||||||
|
camp.roots = {} -- all player group comms roots
|
||||||
|
|
||||||
|
function camp.addCamp(theZone)
|
||||||
|
camp.camps[theZone.name] = theZone
|
||||||
|
end
|
||||||
|
|
||||||
|
function camp.getMyCurrentCamp(theUnit) -- returns first hit plaayer is in
|
||||||
|
local p = theUnit:getPoint()
|
||||||
|
for idx, theCamp in pairs(camp.camps) do
|
||||||
|
if theCamp:pointInZone(p) then
|
||||||
|
return theCamp
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function camp.createCampWithZone(theZone)
|
||||||
|
-- look for all cloners inside my zone
|
||||||
|
if theZone.verbose or camp.verbose then
|
||||||
|
trigger.action.outText("+++camp: processing <" .. theZone.name .. ">, owner is <" .. theZone.owner .. ">", 30)
|
||||||
|
end
|
||||||
|
|
||||||
|
local allZones = cfxZones.getAllZonesInsideZone(theZone)
|
||||||
|
local cloners = {}
|
||||||
|
local redCloners = {}
|
||||||
|
local blueCloners = {}
|
||||||
|
for idx, aZone in pairs(allZones) do
|
||||||
|
if aZone:hasProperty("nocamp") then
|
||||||
|
-- this zone cannot be part of a camp
|
||||||
|
|
||||||
|
elseif aZone:hasProperty("cloner") then
|
||||||
|
-- this is a clone zone and part of my camp
|
||||||
|
table.insert(cloners, aZone)
|
||||||
|
if not aZone:hasProperty("blueOnly") then
|
||||||
|
table.insert(redCloners, aZone)
|
||||||
|
end
|
||||||
|
if not aZone:hasProperty("redOnly") then
|
||||||
|
table.insert(blueCloners, aZone)
|
||||||
|
end
|
||||||
|
if theZone.verbose or camp.verbose then
|
||||||
|
trigger.action.outText("Cloner <" .. aZone.name .. "> is part of camp <" .. theZone.name .. ">", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if #cloners < 1 then
|
||||||
|
trigger.action.outText("+++camp: warning: camp <" .. theZone.name .. "> has no cloners, can't be improved or repaired", 30)
|
||||||
|
else
|
||||||
|
if camp.verbose or theZone.verbose then
|
||||||
|
trigger.action.outText("Camp <" .. theZone.name .. ">: <" .. #cloners .. "> reinforcable points, <" .. #redCloners .. "> for red and <" .. #blueCloners .. "> blue", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
theZone.cloners = cloners
|
||||||
|
theZone.redCloners = redCloners
|
||||||
|
theZone.blueCloners = blueCloners
|
||||||
|
theZone.repairable = theZone:getBoolFromZoneProperty("repair", true)
|
||||||
|
theZone.upgradable = theZone:getBoolFromZoneProperty("upgrade", true)
|
||||||
|
theZone.repairCost = theZone:getNumberFromZoneProperty("repairCost", 100)
|
||||||
|
theZone.upgradeCost = theZone:getNumberFromZoneProperty("upgradeCost", 3 * theZone.repairCost)
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- update and event
|
||||||
|
--
|
||||||
|
function camp.update()
|
||||||
|
-- call me in a second to poll triggers
|
||||||
|
timer.scheduleFunction(camp.update, {}, timer.getTime() + 1/camp.ups)
|
||||||
|
end
|
||||||
|
|
||||||
|
function camp:onEvent(theEvent)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Comms
|
||||||
|
--
|
||||||
|
function camp.processPlayers()
|
||||||
|
-- install coms stump for all players. they will be switched in/out
|
||||||
|
-- whenever it is apropriate
|
||||||
|
for idx, gData in pairs(cfxMX.playerGroupByName) do
|
||||||
|
gID = gData.groupId
|
||||||
|
gName = gData.name
|
||||||
|
local theRoot = missionCommands.addSubMenuForGroup(gID, "Ground Repairs / Upgrades")
|
||||||
|
camp.roots[gName] = theRoot
|
||||||
|
local c00 = missionCommands.addCommandForGroup(gID, "Theatre Overview", theRoot, camp.redirectTFunds, {gName, gID, "tfunds"})
|
||||||
|
local c0 = missionCommands.addCommandForGroup(gID, "Local Funds & Status Overview", theRoot, camp.redirectFunds, {gName, gID, "funds"})
|
||||||
|
local c1 = missionCommands.addCommandForGroup(gID, "REPAIRS: Purchase local repairs", theRoot, camp.redirectRepairs, {gName, gID, "repair"})
|
||||||
|
local c2 = missionCommands.addCommandForGroup(gID, "UPGRADE: Purchase local upgrades", theRoot, camp.redirectUpgrades, {gName, gID, "upgrade"})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function camp.redirectRepairs(args)
|
||||||
|
timer.scheduleFunction(camp.doRepairs, args, timer.getTime() + 0.1)
|
||||||
|
end
|
||||||
|
|
||||||
|
function camp.redirectUpgrades(args)
|
||||||
|
timer.scheduleFunction(camp.doUpgrades, args, timer.getTime() + 0.1)
|
||||||
|
end
|
||||||
|
|
||||||
|
function camp.redirectTFunds(args)
|
||||||
|
timer.scheduleFunction(camp.doTFunds, args, timer.getTime() + 0.1 )
|
||||||
|
end
|
||||||
|
|
||||||
|
function camp.redirectFunds(args)
|
||||||
|
timer.scheduleFunction(camp.doFunds, args, timer.getTime() + 0.1 )
|
||||||
|
end
|
||||||
|
|
||||||
|
function camp.doTFunds(args)
|
||||||
|
local gName = args[1]
|
||||||
|
local gID = args[2]
|
||||||
|
local theGroup = Group.getByName(gName)
|
||||||
|
local coa = theGroup:getCoalition()
|
||||||
|
local hasBalance, amount = bank.getBalance(coa)
|
||||||
|
if not hasBalance then return end
|
||||||
|
local msg = "\nYour faction currently has §" .. amount .. " available for repairs/upgrades.\n"
|
||||||
|
|
||||||
|
-- now iterate all camps that are on my side
|
||||||
|
for idx, theZone in pairs(camp.camps) do
|
||||||
|
if theZone.owner == coa then
|
||||||
|
msg = msg .. "\n - <" .. theZone.name .. ">"
|
||||||
|
|
||||||
|
if theZone.repairable and theZone.upgradable then
|
||||||
|
msg = msg .. " (§" .. theZone.repairCost .. "/§" .. theZone.upgradeCost .. ")"
|
||||||
|
if camp.zoneNeedsRepairs(theZone, coa) then
|
||||||
|
msg = msg .. " requests repairs and"
|
||||||
|
else
|
||||||
|
msg = msg .. " is running and"
|
||||||
|
end
|
||||||
|
|
||||||
|
if camp.zoneNeedsUpgrades(theZone, coa) then
|
||||||
|
msg = msg .. " can be upgraded"
|
||||||
|
else
|
||||||
|
msg = msg .. " is fully upgraded"
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif theZone.repairable then
|
||||||
|
if camp.zoneNeedsRepairs(theZone, coa) then
|
||||||
|
msg = msg .. " needs repairs (§" .. theZone.repairCost .. ")"
|
||||||
|
else
|
||||||
|
msg = msg .. " is fully operational"
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif theZone.upgradable then
|
||||||
|
if camp.zoneNeedsUpgrades(theZone, coa) then
|
||||||
|
msg = msg .. " can be upgraded (§" .. theZone.upgradeCost .. ")"
|
||||||
|
else
|
||||||
|
msg = msg .. " is fully upgraded"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- can be neither repaired nor upgraded
|
||||||
|
msg = msg .. " is owned"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
msg = msg .. "\n"
|
||||||
|
trigger.action.outTextForGroup(gID, msg, 30)
|
||||||
|
end
|
||||||
|
|
||||||
|
function camp.doFunds(args)
|
||||||
|
local gName = args[1]
|
||||||
|
local gID = args[2]
|
||||||
|
local theGroup = Group.getByName(gName)
|
||||||
|
local coa = theGroup:getCoalition()
|
||||||
|
local hasBalance, amount = bank.getBalance(coa)
|
||||||
|
if not hasBalance then return end
|
||||||
|
local msg = "\nYour faction currently has §" .. amount .. " available for repairs/upgrades.\n"
|
||||||
|
|
||||||
|
local allUnits = theGroup:getUnits()
|
||||||
|
local theUnit = allUnits[1] -- always first unit until we get playerCommands
|
||||||
|
if not Unit.isExist(theUnit) or theUnit:getLife() < 1 or
|
||||||
|
theUnit:inAir() or dcsCommon.getUnitSpeed(theUnit) > 1 then
|
||||||
|
trigger.action.outTextForGroup(gID, msg, 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local theZone = camp.getMyCurrentCamp(theUnit)
|
||||||
|
if not theZone or (not theZone.repairable) or theZone.owner ~= theUnit:getCoalition() then
|
||||||
|
trigger.action.outTextForGroup(gID, msg, 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if camp.zoneNeedsRepairs(theZone, coa) then
|
||||||
|
msg = msg .. "\nZone <" .. theZone.name .. "> needs repairs (§" .. theZone.repairCost .. " per repair)\n"
|
||||||
|
elseif theZone.repairable then
|
||||||
|
msg = msg .. "\nZone <" .. theZone.name .. "> has no outstanding repairs.\n"
|
||||||
|
else
|
||||||
|
-- say nothing
|
||||||
|
end
|
||||||
|
if camp.zoneNeedsUpgrades(theZone, coa) then
|
||||||
|
msg = msg .. "\nZone <" .. theZone.name .. "> can be upgraded (§" .. theZone.upgradeCost .. " per upgrade)\n"
|
||||||
|
elseif theZone.upgradable then
|
||||||
|
msg = msg .. "\nZone <" .. theZone.name .. "> is fully upgraded.\n"
|
||||||
|
end
|
||||||
|
trigger.action.outTextForGroup(gID, msg, 30)
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- REPAIRS
|
||||||
|
--
|
||||||
|
function camp.zoneNeedsRepairs(theZone, coa)
|
||||||
|
-- return true if this zone needs repairs, i.e. it has cloners that have a damaged clone set
|
||||||
|
local myCloners = theZone.cloners
|
||||||
|
|
||||||
|
if not coa then
|
||||||
|
trigger.action.outText("+++camp: warning: no coa on zoneNeedsRepair for zone <" .. theZone.name .. ">", 30)
|
||||||
|
elseif coa == 1 then
|
||||||
|
myCloners = theZone.redCloners
|
||||||
|
elseif coa == 2 then
|
||||||
|
myCloners = theZone.blueCloners
|
||||||
|
end
|
||||||
|
|
||||||
|
if not theZone.repairable then return nil end
|
||||||
|
for idx, theCloner in pairs(myCloners) do
|
||||||
|
if theCloner.oSize and theCloner.oSize > 0 then
|
||||||
|
local currSize = cloneZones.countLiveAIUnits(theCloner)
|
||||||
|
if currSize > 0 and currSize < theCloner.oSize then
|
||||||
|
if theZone.verbose then
|
||||||
|
trigger.action.outText("+++camp: camp <" .. theZone.name .. "> has point <" .. theCloner.name .. "> that needs repair.", 30)
|
||||||
|
end
|
||||||
|
return theCloner
|
||||||
|
else
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function camp.doRepairs(args)
|
||||||
|
local gName = args[1]
|
||||||
|
local gID = args[2]
|
||||||
|
local theGroup = Group.getByName(gName)
|
||||||
|
local coa = theGroup:getCoalition()
|
||||||
|
local allUnits = theGroup:getUnits()
|
||||||
|
local theUnit = allUnits[1] -- always first unit until we get playerCommands
|
||||||
|
if not Unit.isExist(theUnit) then return end
|
||||||
|
local pName = "<Error>"
|
||||||
|
if theUnit.getPlayerName then pName = theUnit:getPlayerName() end
|
||||||
|
if not pName then pName = "<Big Err>" end
|
||||||
|
if theUnit:getLife() < 1 then return end
|
||||||
|
if theUnit:inAir() then
|
||||||
|
trigger.action.outTextForGroup(gID, "\nPlease land inside a fortified zone to order repairs\n", 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if dcsCommon.getUnitSpeed(theUnit) > 1 then
|
||||||
|
trigger.action.outTextForGroup(gID, "\nYou must come to a complete stop before being able to order repairs\n", 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local theZone = camp.getMyCurrentCamp(theUnit)
|
||||||
|
if not theZone or not theZone.repairable then
|
||||||
|
trigger.action.outTextForGroup(gID, "\nYou are not inside a zone that can be repaired.\n", 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if theZone.owner ~= theUnit:getCoalition() then
|
||||||
|
trigger.action.outTextForGroup(gID, "\nYou currently do not own zone <" .. theZone.name .. ">. Capture it first.\n", 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- if we get here, we are inside a zone that can be repaired. see if it needs repair and then get repair cost and see if we have enough fund to repair
|
||||||
|
if not camp.zoneNeedsRepairs(theZone, coa) then
|
||||||
|
local msg = "\nZone <" .. theZone.name .. "> is already fully repaired.\n"
|
||||||
|
if camp.zoneNeedsUpgrades(theZone, coa) then
|
||||||
|
msg = msg .. "\nZone <" .. theZone.name .. "> can be upgraded.\n"
|
||||||
|
end
|
||||||
|
trigger.action.outTextForGroup(gID, msg, 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- see if we have enough funds
|
||||||
|
local hasBalance, amount = bank.getBalance(coa)
|
||||||
|
if not hasBalance then
|
||||||
|
trigger.action.outText("+++camp: no balance for upgrade!", 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if amount < theZone.repairCost then
|
||||||
|
trigger.action.outTextForGroup(gID, "\nYou curently cannot afford repairs here\n", 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- finally, let's repair
|
||||||
|
camp.repairZone(theZone, coa)
|
||||||
|
-- theCloner = camp.zoneNeedsRepairs(theZone)
|
||||||
|
-- cloneZones.despawnAll(theCloner)
|
||||||
|
-- cloneZones.spawnWithCloner(theCloner)
|
||||||
|
bank.withdawFunds(coa, theZone.repairCost)
|
||||||
|
local ignore, remain = bank.getBalance(coa)
|
||||||
|
trigger.action.outTextForCoalition(coa, "\nZone <" .. theZone.name .. "> was repaired by <" .. pName ..
|
||||||
|
"> for §" .. theZone.repairCost .. ".\nFaction has §" .. remain .. " remaining funds.\n", 30)
|
||||||
|
end
|
||||||
|
|
||||||
|
function camp.repairZone(theZone, coa)
|
||||||
|
theCloner = camp.zoneNeedsRepairs(theZone, coa)
|
||||||
|
if not theCloner then return end
|
||||||
|
cloneZones.despawnAll(theCloner)
|
||||||
|
cloneZones.spawnWithCloner(theCloner)
|
||||||
|
end
|
||||||
|
--
|
||||||
|
-- UPGRADES
|
||||||
|
--
|
||||||
|
|
||||||
|
function camp.zoneNeedsUpgrades(theZone, coa)
|
||||||
|
-- return true if this zone can be upgraded, i.e. it has cloners that have an empty clone set
|
||||||
|
if not theZone.upgradable then return nil end
|
||||||
|
|
||||||
|
local myCloners = theZone.cloners
|
||||||
|
|
||||||
|
if not coa then
|
||||||
|
trigger.action.outText("+++camp: warning: no coa on zoneNeedsUpgrades for zone <" .. theZone.name .. ">", 30)
|
||||||
|
elseif coa == 1 then
|
||||||
|
myCloners = theZone.redCloners
|
||||||
|
elseif coa == 2 then
|
||||||
|
myCloners = theZone.blueCloners
|
||||||
|
end
|
||||||
|
|
||||||
|
for idx, theCloner in pairs(myCloners) do
|
||||||
|
local currSize = cloneZones.countLiveAIUnits(theCloner)
|
||||||
|
if currSize < 1 then
|
||||||
|
if theZone.verbose then
|
||||||
|
trigger.action.outText("+++camp: camp <" .. theZone.name .. "> has point <" .. theCloner.name .. "> that can be upgraded.", 30)
|
||||||
|
end
|
||||||
|
return theCloner
|
||||||
|
else
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function camp.doUpgrades(args)
|
||||||
|
local gName = args[1]
|
||||||
|
local gID = args[2]
|
||||||
|
local theGroup = Group.getByName(gName)
|
||||||
|
local coa = theGroup:getCoalition()
|
||||||
|
local allUnits = theGroup:getUnits()
|
||||||
|
local theUnit = allUnits[1] -- always first unit until we get playerCommands
|
||||||
|
if not Unit.isExist(theUnit) then return end
|
||||||
|
if theUnit:getLife() < 1 then return end
|
||||||
|
local pName = "<Error>"
|
||||||
|
if theUnit.getPlayerName then pName = theUnit:getPlayerName() end
|
||||||
|
if not pName then pName = "<Big Err>" end
|
||||||
|
if theUnit:inAir() then
|
||||||
|
trigger.action.outTextForGroup(gID, "\nPlease land inside a fortified zone to order upgrades.\n", 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if dcsCommon.getUnitSpeed(theUnit) > 1 then
|
||||||
|
trigger.action.outTextForGroup(gID, "\nYou must come to a complete stop before being able to order upgrades\n", 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local theZone = camp.getMyCurrentCamp(theUnit)
|
||||||
|
if not theZone or not theZone.upgradable then
|
||||||
|
trigger.action.outTextForGroup(gID, "\nYou are not inside a zone that can be upgraded.\n", 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if theZone.owner ~= theUnit:getCoalition() then
|
||||||
|
trigger.action.outTextForGroup(gID, "\nYou currently do not own zone <" .. theZone.name .. ">. Capture it first.\n", 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if camp.zoneNeedsRepairs(theZone, coa) then
|
||||||
|
trigger.action.outTextForGroup(gID, "\nZone <" .. theZone.name .. "> requires repairs before it can be upgraded.\n", 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- if we get here, we are inside a zone that can be upgraded. see if it needs upgrades and then get upgrade cost and see if we have enough fund to do it
|
||||||
|
if not camp.zoneNeedsUpgrades(theZone, coa) then
|
||||||
|
trigger.action.outTextForGroup(gID, "\nZone <" .. theZone.name .. "> has been fully upgraded.\n", 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- see if we have enough funds
|
||||||
|
local hasBalance, amount = bank.getBalance(coa)
|
||||||
|
if not hasBalance then
|
||||||
|
trigger.action.outText("+++camp: no balance for upgrade!", 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if amount < theZone.upgradeCost then
|
||||||
|
trigger.action.outTextForGroup(gID, "\nYou curently cannot afford an upgrade here\n", 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- finally, let's upgrade
|
||||||
|
--theCloner = camp.zoneNeedsUpgrades(theZone)
|
||||||
|
--cloneZones.spawnWithCloner(theCloner)
|
||||||
|
camp.upgradeZone(theZone, coa)
|
||||||
|
-- bill it to side
|
||||||
|
bank.withdawFunds(coa, theZone.upgradeCost)
|
||||||
|
local ignore, remain = bank.getBalance(coa)
|
||||||
|
trigger.action.outTextForCoalition(coa, "\nZone <" .. theZone.name .. "> was upgraded by <" .. pName ..
|
||||||
|
"> for §" .. theZone.upgradeCost .. ".\nFaction has §" .. remain .. " remaining funds.\n", 30)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- can be called externally
|
||||||
|
function camp.upgradeZone(theZone, coa)
|
||||||
|
theCloner = camp.zoneNeedsUpgrades(theZone, coa)
|
||||||
|
if not theCloner then return end
|
||||||
|
cloneZones.spawnWithCloner(theCloner)
|
||||||
|
end
|
||||||
|
--
|
||||||
|
-- Config & Go
|
||||||
|
--
|
||||||
|
|
||||||
|
function camp.readConfigZone()
|
||||||
|
local theZone = cfxZones.getZoneByName("campConfig")
|
||||||
|
if not theZone then
|
||||||
|
theZone = cfxZones.createSimpleZone("campConfig")
|
||||||
|
end
|
||||||
|
|
||||||
|
camp.verbose = theZone.verbose
|
||||||
|
end
|
||||||
|
|
||||||
|
function camp.start()
|
||||||
|
-- lib check
|
||||||
|
if not dcsCommon.libCheck then
|
||||||
|
trigger.action.outText("camp requires dcsCommon", 30)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if not dcsCommon.libCheck("camp", camp.requiredLibs) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- read config
|
||||||
|
camp.readConfigZone()
|
||||||
|
|
||||||
|
-- read zones
|
||||||
|
local attrZones = cfxZones.getZonesWithAttributeNamed("camp")
|
||||||
|
for k, aZone in pairs(attrZones) do
|
||||||
|
camp.createCampWithZone(aZone) -- process attributes
|
||||||
|
camp.addCamp(aZone) -- add to list
|
||||||
|
end
|
||||||
|
|
||||||
|
-- process all players
|
||||||
|
camp.processPlayers()
|
||||||
|
|
||||||
|
-- start update
|
||||||
|
camp.update()
|
||||||
|
|
||||||
|
-- connect event handler
|
||||||
|
world.addEventHandler(camp)
|
||||||
|
|
||||||
|
trigger.action.outText("camp v" .. camp.version .. " started.", 30)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if not camp.start() then
|
||||||
|
trigger.action.outText("camp aborted: missing libraries", 30)
|
||||||
|
camp = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[--
|
||||||
|
Ideas:
|
||||||
|
re-supply: will restore REMAINING units at all points with fresh
|
||||||
|
units so that they can have full mags
|
||||||
|
costs as much as a full upgrade? hald way between upgrade and repair
|
||||||
|
--]]--
|
||||||
@ -48,7 +48,8 @@ cfxZones.version = "4.3.1"
|
|||||||
- 4.3.0 - boolean supports maybe, random, rnd, ?
|
- 4.3.0 - boolean supports maybe, random, rnd, ?
|
||||||
- small optimization for randomInRange()
|
- small optimization for randomInRange()
|
||||||
- randomDelayFromPositiveRange also allows 0
|
- randomDelayFromPositiveRange also allows 0
|
||||||
|
- 4.3.1 - new drawText() for zones
|
||||||
|
- dmlZones:getClosestZone() bridge
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
--
|
--
|
||||||
@ -1208,6 +1209,11 @@ function cfxZones.getClosestZone(point, theZones)
|
|||||||
return closestZone, currDelta
|
return closestZone, currDelta
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function dmlZones:getClosestZone(theZones)
|
||||||
|
local closestZone, currDelta = cfxZones.getClosestZone(self:getPoint(), theZones)
|
||||||
|
return closestZone, currDelta
|
||||||
|
end
|
||||||
|
|
||||||
-- return a random zone from the table passed in zones
|
-- return a random zone from the table passed in zones
|
||||||
function cfxZones.pickRandomZoneFrom(zones)
|
function cfxZones.pickRandomZoneFrom(zones)
|
||||||
if not zones then zones = cfxZones.zones end
|
if not zones then zones = cfxZones.zones end
|
||||||
@ -2108,6 +2114,21 @@ function dmlZone:drawZone(lineColor, fillColor, markID)
|
|||||||
return cfxZones.drawZone(self, lineColor, fillColor, markID)
|
return cfxZones.drawZone(self, lineColor, fillColor, markID)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function cfxZones.drawText(theZone, theText, fSize, lineColor, fillColor)
|
||||||
|
if not theZone then return end
|
||||||
|
if not fSize then fSize = 12 end
|
||||||
|
if not lineColor then lineColor = {0.8, 0.8, 0.8, 1.0} end
|
||||||
|
if not fillColor then fillColor = lineColor end
|
||||||
|
local markID = dcsCommon.numberUUID()
|
||||||
|
local p = theZone:getPoint()
|
||||||
|
local offset = {x = p.x, y = 0, z = p.z}
|
||||||
|
trigger.action.textToAll(-1, markID, offset, lineColor , fillColor , fSize, true , theText)
|
||||||
|
return markID
|
||||||
|
end
|
||||||
|
|
||||||
|
function dmlZone:drawText(theText, fSize, lineColor, fillColor)
|
||||||
|
return cfxZones.drawText(self, theText, fSize, lineColor, fillColor)
|
||||||
|
end
|
||||||
--
|
--
|
||||||
-- ===================
|
-- ===================
|
||||||
-- PROPERTY PROCESSING
|
-- PROPERTY PROCESSING
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
cloneZones = {}
|
cloneZones = {}
|
||||||
cloneZones.version = "2.1.0"
|
cloneZones.version = "2.2.0"
|
||||||
cloneZones.verbose = false
|
cloneZones.verbose = false
|
||||||
cloneZones.requiredLibs = {
|
cloneZones.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
@ -44,6 +44,12 @@ cloneZones.respawnOnGroupID = true
|
|||||||
when pre-wipe is active
|
when pre-wipe is active
|
||||||
2.1.0 - despawnIn option
|
2.1.0 - despawnIn option
|
||||||
- inBuiltup option for rndLoc
|
- inBuiltup option for rndLoc
|
||||||
|
2.2.0 - oSize
|
||||||
|
- countLiveUnits() performace optimization
|
||||||
|
- new countLiveAIUnits()
|
||||||
|
- damaged! output
|
||||||
|
- health# output
|
||||||
|
- persistence: persist oSize and set lastSize
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
--
|
--
|
||||||
@ -337,7 +343,16 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
|
|||||||
if theZone:hasProperty("despawnIn") then
|
if theZone:hasProperty("despawnIn") then
|
||||||
theZone.despawnInMin, theZone.despawnInMax = theZone:getPositiveRangeFromZoneProperty("despawnIn", 2,2)
|
theZone.despawnInMin, theZone.despawnInMax = theZone:getPositiveRangeFromZoneProperty("despawnIn", 2,2)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- damaged and health interface
|
||||||
|
if theZone:hasProperty("damaged!") then
|
||||||
|
theZone.damaged = theZone:getStringFromZoneProperty("damaged!")
|
||||||
|
end
|
||||||
|
if theZone:hasProperty("health#") then
|
||||||
|
theZone.health = theZone:getStringFromZoneProperty("health#")
|
||||||
|
end
|
||||||
-- we end with clear plate
|
-- we end with clear plate
|
||||||
|
theZone.lastSize = 0 -- no units here
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
@ -348,6 +363,7 @@ function cloneZones.despawnAll(theZone)
|
|||||||
if cloneZones.verbose or theZone.verbose then
|
if cloneZones.verbose or theZone.verbose then
|
||||||
trigger.action.outText("+++clnZ: despawn all - wiping zone <" .. theZone.name .. ">", 30)
|
trigger.action.outText("+++clnZ: despawn all - wiping zone <" .. theZone.name .. ">", 30)
|
||||||
end
|
end
|
||||||
|
theZone.oSize = 0 -- original spawn size
|
||||||
for idx, aGroup in pairs(theZone.mySpawns) do
|
for idx, aGroup in pairs(theZone.mySpawns) do
|
||||||
if aGroup:isExist() then
|
if aGroup:isExist() then
|
||||||
if theZone.verbose then
|
if theZone.verbose then
|
||||||
@ -1017,7 +1033,7 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
|
|||||||
trigger.action.outText("+++clnZ: spawning with template <" .. theZone.name .. "> for spawner <" .. spawnZone.name .. ">", 30)
|
trigger.action.outText("+++clnZ: spawning with template <" .. theZone.name .. "> for spawner <" .. spawnZone.name .. ">", 30)
|
||||||
end
|
end
|
||||||
-- theZone is the cloner with the TEMPLATE (source)
|
-- theZone is the cloner with the TEMPLATE (source)
|
||||||
-- spawnZone is the spawner with SETTINGS and DESTINATION (target location) where the clones are poofed into existence
|
-- spawnZone is the actual spawner with SETTINGS and DESTINATION (target location) where the clones are poofed into existence
|
||||||
local newCenter = spawnZone:getPoint() -- includes zone following updates
|
local newCenter = spawnZone:getPoint() -- includes zone following updates
|
||||||
local oCenter = theZone:getDCSOrigin() -- get original coords on map for cloning offsets
|
local oCenter = theZone:getDCSOrigin() -- get original coords on map for cloning offsets
|
||||||
-- calculate zoneDelta, is added to all vectors
|
-- calculate zoneDelta, is added to all vectors
|
||||||
@ -1190,6 +1206,7 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
|
|||||||
cloneZones.resolveReferences(theZone, dataToSpawn)
|
cloneZones.resolveReferences(theZone, dataToSpawn)
|
||||||
|
|
||||||
-- now spawn all raw data
|
-- now spawn all raw data
|
||||||
|
spawnZone.oSize = 0 -- original size reset
|
||||||
local groupCollector = {} -- to detect cross-group conflicts
|
local groupCollector = {} -- to detect cross-group conflicts
|
||||||
local unitCollector = {} -- to detect cross-group conflicts
|
local unitCollector = {} -- to detect cross-group conflicts
|
||||||
local theGroup = nil -- init to empty, on this level
|
local theGroup = nil -- init to empty, on this level
|
||||||
@ -1219,6 +1236,8 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
|
|||||||
-- SPAWN NOW!!!!
|
-- SPAWN NOW!!!!
|
||||||
theGroup = coalition.addGroup(rawData.CZctry, rawData.CZtheCat, rawData)
|
theGroup = coalition.addGroup(rawData.CZctry, rawData.CZtheCat, rawData)
|
||||||
table.insert(spawnedGroups, theGroup)
|
table.insert(spawnedGroups, theGroup)
|
||||||
|
-- increment oSize by number of spawns
|
||||||
|
spawnZone.oSize = spawnZone.oSize + theGroup:getSize()
|
||||||
|
|
||||||
-- see if this is an auto-despawner
|
-- see if this is an auto-despawner
|
||||||
if spawnZone.despawnInMin then
|
if spawnZone.despawnInMin then
|
||||||
@ -1465,6 +1484,8 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
-- reset lastSize to oSize
|
||||||
|
spawnZone.lastSize = spawnZone.oSize
|
||||||
local args = {}
|
local args = {}
|
||||||
args.groups = spawnedGroups
|
args.groups = spawnedGroups
|
||||||
args.statics = spawnedStatics
|
args.statics = spawnedStatics
|
||||||
@ -1593,19 +1614,30 @@ function cloneZones.doClone(args)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function cloneZones.countLiveAIUnits(theZone)
|
||||||
|
-- like countLiveUnits, but disregards statics
|
||||||
|
if not theZone then return 0 end
|
||||||
|
local count = 0
|
||||||
|
if not theZone.mySpawns then return 0 end
|
||||||
|
-- count units
|
||||||
|
if theZone.mySpawns then
|
||||||
|
for idx, aGroup in pairs(theZone.mySpawns) do
|
||||||
|
if Group.isExist(aGroup) then
|
||||||
|
count = count + aGroup:getSize()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return count
|
||||||
|
end
|
||||||
|
|
||||||
function cloneZones.countLiveUnits(theZone)
|
function cloneZones.countLiveUnits(theZone)
|
||||||
if not theZone then return 0 end
|
if not theZone then return 0 end
|
||||||
local count = 0
|
local count = 0
|
||||||
-- count units
|
-- count units
|
||||||
if theZone.mySpawns then
|
if theZone.mySpawns then
|
||||||
for idx, aGroup in pairs(theZone.mySpawns) do
|
for idx, aGroup in pairs(theZone.mySpawns) do
|
||||||
if aGroup:isExist() then
|
if Group.isExist(aGroup) then --aGroup:isExist() then
|
||||||
local allUnits = aGroup:getUnits()
|
count = count + aGroup:getSize()
|
||||||
for idy, aUnit in pairs(allUnits) do
|
|
||||||
if aUnit:isExist() and aUnit:getLife() >= 1 then
|
|
||||||
count = count + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -1715,6 +1747,28 @@ function cloneZones.update()
|
|||||||
-- can mess with empty, so we tell empty to skip
|
-- can mess with empty, so we tell empty to skip
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- handling of damaged! and #health
|
||||||
|
if aZone.damaged or aZone.health then
|
||||||
|
-- calculate current health
|
||||||
|
local currSize = cloneZones.countLiveAIUnits(aZone)
|
||||||
|
if aZone.oSize < 1 then
|
||||||
|
if aZone.verbose or cloneZones.verbose then
|
||||||
|
trigger.action.outText("+++clnZ: Warning: zero oZize for cloner <" .. aZone.name .. ">, no health info, no damage alert", 30)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local percent = math.floor(currSize * 100 / aZone.oSize)
|
||||||
|
if aZone.health then
|
||||||
|
aZone:setFlagValue(aZone.health, percent)
|
||||||
|
end
|
||||||
|
if aZone.lastSize > currSize then
|
||||||
|
if aZone.damaged then
|
||||||
|
aZone:pollFlag(aZone.damaged, aZone.cloneMethod)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
aZone.lastSize = currSize
|
||||||
|
end
|
||||||
|
|
||||||
-- empty handling
|
-- empty handling
|
||||||
local isEmpty = cloneZones.countLiveUnits(aZone) < 1 and aZone.hasClones
|
local isEmpty = cloneZones.countLiveUnits(aZone) < 1 and aZone.hasClones
|
||||||
if isEmpty and (willSpawn == false) then
|
if isEmpty and (willSpawn == false) then
|
||||||
@ -1885,7 +1939,8 @@ function cloneZones.saveData()
|
|||||||
local cData = {}
|
local cData = {}
|
||||||
local cName = theCloner.name
|
local cName = theCloner.name
|
||||||
cData.myUniqueCounter = theCloner.myUniqueCounter
|
cData.myUniqueCounter = theCloner.myUniqueCounter
|
||||||
|
cData.oSize = theCloner.oSize
|
||||||
|
cData.lastSize = theCloner.lastSize
|
||||||
-- mySpawns: all groups i'm curently observing for empty!
|
-- mySpawns: all groups i'm curently observing for empty!
|
||||||
-- myStatics: dto for objects
|
-- myStatics: dto for objects
|
||||||
local mySpawns = {}
|
local mySpawns = {}
|
||||||
@ -1980,6 +2035,8 @@ function cloneZones.loadData()
|
|||||||
if cData.myUniqueCounter then
|
if cData.myUniqueCounter then
|
||||||
theCloner.myUniqueCounter = cData.myUniqueCounter
|
theCloner.myUniqueCounter = cData.myUniqueCounter
|
||||||
end
|
end
|
||||||
|
if cData.oSize then theCloner.oSize = cData.oSize end
|
||||||
|
if cData.lastSize then theCloner.lastSize = cData.lastSize end
|
||||||
|
|
||||||
local mySpawns = {}
|
local mySpawns = {}
|
||||||
for idx, aName in pairs(cData.mySpawns) do
|
for idx, aName in pairs(cData.mySpawns) do
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
csarManager = {}
|
csarManager = {}
|
||||||
csarManager.version = "3.2.5"
|
csarManager.version = "3.2.7"
|
||||||
csarManager.ups = 1
|
csarManager.ups = 1
|
||||||
|
|
||||||
--[[-- VERSION HISTORY
|
--[[-- VERSION HISTORY
|
||||||
@ -41,6 +41,7 @@ csarManager.ups = 1
|
|||||||
3.2.5 - smoke callbacks
|
3.2.5 - smoke callbacks
|
||||||
- useRanks option
|
- useRanks option
|
||||||
3.2.6 - inBuiltup analogon to cloner
|
3.2.6 - inBuiltup analogon to cloner
|
||||||
|
3.2.7 - createCSARForParachutist now supports optional coa (autoCSAR)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1330,8 +1331,8 @@ function csarManager.createCSARforUnit(theUnit, pilotName, radius, silent, score
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function csarManager.createCSARForParachutist(theUnit, name) -- invoked with parachute guy on ground as theUnit
|
function csarManager.createCSARForParachutist(theUnit, name, coa) -- invoked with parachute guy on ground as theUnit
|
||||||
local coa = theUnit:getCoalition()
|
if not coa then coa = theUnit:getCoalition() end
|
||||||
local pos = theUnit:getPoint()
|
local pos = theUnit:getPoint()
|
||||||
-- unit DOES NOT HAVE GROUP!!! (unless water splashdown)
|
-- unit DOES NOT HAVE GROUP!!! (unless water splashdown)
|
||||||
-- create a CSAR mission now
|
-- create a CSAR mission now
|
||||||
|
|||||||
@ -16,12 +16,13 @@ dcsCommon.version = "3.0.5"
|
|||||||
3.0.4 - getGroupLocation() hardened, optional verbose
|
3.0.4 - getGroupLocation() hardened, optional verbose
|
||||||
3.0.5 - new getNthItem()
|
3.0.5 - new getNthItem()
|
||||||
- new getFirstItem()
|
- new getFirstItem()
|
||||||
|
- arrayContainsString() can handle dicts
|
||||||
|
- new pointXpercentYdegOffAB()
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
-- dcsCommon is a library of common lua functions
|
-- dcsCommon is a library of common lua functions
|
||||||
-- for easy access and simple mission programming
|
-- for easy access and simple mission programming
|
||||||
-- (c) 2021 - 2023 by Chritian Franz and cf/x AG
|
-- (c) 2021 - 2024 by Christian Franz and cf/x AG
|
||||||
|
|
||||||
dcsCommon.verbose = false -- set to true to see debug messages. Lots of them
|
dcsCommon.verbose = false -- set to true to see debug messages. Lots of them
|
||||||
dcsCommon.uuidStr = "uuid-"
|
dcsCommon.uuidStr = "uuid-"
|
||||||
@ -1830,7 +1831,7 @@ dcsCommon.version = "3.0.5"
|
|||||||
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function dcsCommon.pointInDirectionOfPointXYY(dir, dist, p) -- dir in rad, p in XYZ returns XYY
|
function dcsCommon.pointInDirectionOfPointXYY(dir, dist, p) -- dir in rad, p in XYZ returns XZZ
|
||||||
local fx = math.cos(dir)
|
local fx = math.cos(dir)
|
||||||
local fy = math.sin(dir)
|
local fy = math.sin(dir)
|
||||||
local p2 = {}
|
local p2 = {}
|
||||||
@ -1840,6 +1841,15 @@ dcsCommon.version = "3.0.5"
|
|||||||
return p2
|
return p2
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function dcsCommon.pointXpercentYdegOffAB(A, B, xPer, yDeg) -- rets xzz point
|
||||||
|
local bearingRad = dcsCommon.bearingFromAtoB(A, B)
|
||||||
|
local dist = dcsCommon.dist(A, B)
|
||||||
|
local deviation = bearingRad + yDeg * 0.0174533
|
||||||
|
local newDist = dist * xPer/100
|
||||||
|
local newPoint = dcsCommon.pointInDirectionOfPointXYY(deviation, newDist, A)
|
||||||
|
return newPoint
|
||||||
|
end
|
||||||
|
|
||||||
function dcsCommon.rotatePointAroundOriginRad(inX, inY, angle) -- angle in degrees
|
function dcsCommon.rotatePointAroundOriginRad(inX, inY, angle) -- angle in degrees
|
||||||
local c = math.cos(angle)
|
local c = math.cos(angle)
|
||||||
local s = math.sin(angle)
|
local s = math.sin(angle)
|
||||||
@ -2078,7 +2088,7 @@ end
|
|||||||
if not theString then return false end
|
if not theString then return false end
|
||||||
if not caseSensitive then caseSensitive = false end
|
if not caseSensitive then caseSensitive = false end
|
||||||
if type(theArray) ~= "table" then
|
if type(theArray) ~= "table" then
|
||||||
trigger.action.outText("***arrayContainsString: theArray is not type table but <" .. type(theArray) .. ">", 30)
|
trigger.action.outText("***wildArrayContainsString: theArray is not type table but <" .. type(theArray) .. ">", 30)
|
||||||
end
|
end
|
||||||
if not caseSensitive then theString = string.upper(theString) end
|
if not caseSensitive then theString = string.upper(theString) end
|
||||||
|
|
||||||
@ -2114,10 +2124,11 @@ end
|
|||||||
if not theArray then return false end
|
if not theArray then return false end
|
||||||
if not theString then return false end
|
if not theString then return false end
|
||||||
if type(theArray) ~= "table" then
|
if type(theArray) ~= "table" then
|
||||||
trigger.action.outText("***arrayContainsString: theArray is not type table but <" .. type(theArray) .. ">", 30)
|
trigger.action.outText("***arrayContainsString: theArray is not type <table> but <" .. type(theArray) .. ">", 30)
|
||||||
end
|
end
|
||||||
for i = 1, #theArray do
|
for idx, item in pairs(theArray) do
|
||||||
if theArray[i] == theString then return true end
|
-- for i = 1, #theArray do
|
||||||
|
if item == theString then return true end
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
@ -2418,7 +2429,7 @@ end
|
|||||||
if getmetatable(value) then
|
if getmetatable(value) then
|
||||||
if type(value) == "string" then
|
if type(value) == "string" then
|
||||||
else
|
else
|
||||||
trigger.action.outText(prefix .. key (" .. type(value) .. ") .. " HAS META", 30)
|
trigger.action.outText(prefix .. key .. (" .. type(value) .. ") .. " HAS META", 30)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if type(value) == "table" then
|
if type(value) == "table" then
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
factoryZone = {}
|
factoryZone = {}
|
||||||
factoryZone.version = "3.1.1"
|
factoryZone.version = "3.1.2"
|
||||||
factoryZone.verbose = false
|
factoryZone.verbose = false
|
||||||
factoryZone.name = "factoryZone"
|
factoryZone.name = "factoryZone"
|
||||||
|
|
||||||
@ -18,6 +18,7 @@ factoryZone.name = "factoryZone"
|
|||||||
- defendMe? attribute
|
- defendMe? attribute
|
||||||
- triggered 'shocked' mode via defendMe
|
- triggered 'shocked' mode via defendMe
|
||||||
3.1.1 - fixed a big with persistence
|
3.1.1 - fixed a big with persistence
|
||||||
|
3.1.2 - fixed a verbosity bug
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
factoryZone.requiredLibs = {
|
factoryZone.requiredLibs = {
|
||||||
@ -253,14 +254,14 @@ function factoryZone.sendOutAttackers(aZone)
|
|||||||
-- bang on xxxP!
|
-- bang on xxxP!
|
||||||
if aZone.owner == 1 and aZone.redP then
|
if aZone.owner == 1 and aZone.redP then
|
||||||
if aZone.verbose or factoryZone.verbose then
|
if aZone.verbose or factoryZone.verbose then
|
||||||
trigger.action.outText("+++factZ: polling redP! <" .. aZone.redP .. "> for factrory <" .. aZone.name .. ">")
|
trigger.action.outText("+++factZ: polling redP! <" .. aZone.redP .. "> for factrory <" .. aZone.name .. ">", 30)
|
||||||
end
|
end
|
||||||
aZone:pollFlag(aZone.redP, aZone.factoryMethod)
|
aZone:pollFlag(aZone.redP, aZone.factoryMethod)
|
||||||
end
|
end
|
||||||
|
|
||||||
if aZone.owner == 2 and aZone.blueP then
|
if aZone.owner == 2 and aZone.blueP then
|
||||||
if aZone.verbose or factoryZone.verbose then
|
if aZone.verbose or factoryZone.verbose then
|
||||||
trigger.action.outText("+++factZ: polling blueP! <" .. aZone.blueP .. "> for factrory <" .. aZone.name .. ">")
|
trigger.action.outText("+++factZ: polling blueP! <" .. aZone.blueP .. "> for factrory <" .. aZone.name .. ">", 30)
|
||||||
end
|
end
|
||||||
aZone:pollFlag(aZone.blueP, aZone.factoryMethod)
|
aZone:pollFlag(aZone.blueP, aZone.factoryMethod)
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
cfxGroundTroops = {}
|
cfxGroundTroops = {}
|
||||||
cfxGroundTroops.version = "2.0.0"
|
cfxGroundTroops.version = "2.0.1"
|
||||||
cfxGroundTroops.ups = 1
|
cfxGroundTroops.ups = 1
|
||||||
cfxGroundTroops.verbose = false
|
cfxGroundTroops.verbose = false
|
||||||
cfxGroundTroops.requiredLibs = {
|
cfxGroundTroops.requiredLibs = {
|
||||||
@ -31,6 +31,8 @@ cfxGroundTroops.jtacCB = {} -- jtac callbacks, to be implemented
|
|||||||
- jtacSound
|
- jtacSound
|
||||||
- clanup
|
- clanup
|
||||||
- jtacVerbose
|
- jtacVerbose
|
||||||
|
2.0.1 - small fiex ti checkPileUp()
|
||||||
|
|
||||||
|
|
||||||
an entry into the deployed troop table has the following attributes
|
an entry into the deployed troop table has the following attributes
|
||||||
- group - the group
|
- group - the group
|
||||||
@ -755,7 +757,8 @@ function cfxGroundTroops.checkPileUp()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- create a list of all piles
|
-- create a list of all piles
|
||||||
for idx, oz in pairs(cfxOwnedZones.zones) do
|
-- for idx, oz in pairs(cfxOwnedZones.zones) do
|
||||||
|
for idx, oz in pairs(cfxOwnedZones.allManagedOwnedZones) do
|
||||||
local newPile = {}
|
local newPile = {}
|
||||||
newPile[1] = 0 -- no red inZone here
|
newPile[1] = 0 -- no red inZone here
|
||||||
newPile[2] = 0 -- no blue inZone here
|
newPile[2] = 0 -- no blue inZone here
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
cfxHeloTroops = {}
|
cfxHeloTroops = {}
|
||||||
cfxHeloTroops.version = "3.0.2"
|
cfxHeloTroops.version = "3.0.3"
|
||||||
cfxHeloTroops.verbose = false
|
cfxHeloTroops.verbose = false
|
||||||
cfxHeloTroops.autoDrop = true
|
cfxHeloTroops.autoDrop = true
|
||||||
cfxHeloTroops.autoPickup = false
|
cfxHeloTroops.autoPickup = false
|
||||||
@ -39,6 +39,7 @@ cfxHeloTroops.requestRange = 500 -- meters
|
|||||||
- requestRange attribute
|
- requestRange attribute
|
||||||
3.0.1 - fixed a bug with legalTroops attribute
|
3.0.1 - fixed a bug with legalTroops attribute
|
||||||
3.0.2 - fixed a typo in in-air menu
|
3.0.2 - fixed a typo in in-air menu
|
||||||
|
3.0.3 - pointInZone check for insertion rather than radius
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
--
|
--
|
||||||
@ -585,7 +586,7 @@ function cfxHeloTroops.scoreWhenCapturing(theUnit)
|
|||||||
local theGroup = theUnit:getGroup()
|
local theGroup = theUnit:getGroup()
|
||||||
local ID = theGroup:getID()
|
local ID = theGroup:getID()
|
||||||
local nearestZone, dist = cfxOwnedZones.getNearestOwnedZoneToPoint(p)
|
local nearestZone, dist = cfxOwnedZones.getNearestOwnedZoneToPoint(p)
|
||||||
if nearestZone and dist < nearestZone.radius then
|
if nearestZone and nearestZone:pointInZone(p) then -- dist < nearestZone.radius then
|
||||||
-- we are inside an owned zone!
|
-- we are inside an owned zone!
|
||||||
if nearestZone.owner ~= coa then
|
if nearestZone.owner ~= coa then
|
||||||
-- yup, combat drop!
|
-- yup, combat drop!
|
||||||
|
|||||||
110
modules/income.lua
Normal file
110
modules/income.lua
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
income = {}
|
||||||
|
income.version = "0.0.0"
|
||||||
|
income.requiredLibs = {
|
||||||
|
"dcsCommon", -- always
|
||||||
|
"cfxZones", -- Zones, of course
|
||||||
|
"bank"
|
||||||
|
}
|
||||||
|
|
||||||
|
income.sources = {}
|
||||||
|
|
||||||
|
|
||||||
|
function income.addIncomeZone(theZone)
|
||||||
|
income.sources[theZone.name] = theZone
|
||||||
|
end
|
||||||
|
|
||||||
|
function income.createIncomeWithZone(theZone)
|
||||||
|
theZone.income = theZone:getNumberFromZoneProperty("income")
|
||||||
|
-- we may add enablers and prohibitors and shared income
|
||||||
|
-- for example a building or upgrade must exist in order
|
||||||
|
-- to provide income
|
||||||
|
end
|
||||||
|
|
||||||
|
function income.getIncomeForZoneAndCoa(theZone, coa)
|
||||||
|
-- process this zone's status (see which upgrades exist)
|
||||||
|
-- and return the amount of income for this zone and coa
|
||||||
|
-- currently very primitive: own it, get it
|
||||||
|
if theZone.owner == coa then
|
||||||
|
return theZone.income
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function income.update()
|
||||||
|
-- schedule next round
|
||||||
|
timer.scheduleFunction(income.update, {}, timer.getTime() + income.interval)
|
||||||
|
|
||||||
|
-- base income
|
||||||
|
bank.addFunds(0, income.neutral)
|
||||||
|
bank.addFunds(1, income.red)
|
||||||
|
bank.addFunds(2, income.blue)
|
||||||
|
|
||||||
|
for idx, theZone in pairs(income.sources) do
|
||||||
|
bank.addFunds(0, income.getIncomeForZoneAndCoa(theZone, 0))
|
||||||
|
bank.addFunds(1, income.getIncomeForZoneAndCoa(theZone, 1))
|
||||||
|
bank.addFunds(2, income.getIncomeForZoneAndCoa(theZone, 2))
|
||||||
|
end
|
||||||
|
|
||||||
|
if income.announceTicks then
|
||||||
|
-- trigger.action.outText(income.tickMessage, 30)
|
||||||
|
local has, balance = bank.getBalance(0)
|
||||||
|
trigger.action.outTextForCoalition(0, "\n" .. income.tickMessage .. "\nNew balance: §" .. balance .. "\n", 30)
|
||||||
|
has, balance = bank.getBalance(1)
|
||||||
|
trigger.action.outTextForCoalition(1, "\n" .. income.tickMessage .. "\nNew balance: §" .. balance .. "\n", 30)
|
||||||
|
has, balance = bank.getBalance(2)
|
||||||
|
trigger.action.outTextForCoalition(2, "\n" .. income.tickMessage .. "\nNew balance: §" .. balance .. "\n", 30)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function income.readConfigZone()
|
||||||
|
local theZone = cfxZones.getZoneByName("incomeConfig")
|
||||||
|
if not theZone then
|
||||||
|
theZone = cfxZones.createSimpleZone("incomeConfig")
|
||||||
|
end
|
||||||
|
|
||||||
|
income.base = theZone:getNumberFromZoneProperty ("base", 10)
|
||||||
|
income.red = theZone:getNumberFromZoneProperty ("red", income.base)
|
||||||
|
income.blue = theZone:getNumberFromZoneProperty ("blue", income.base)
|
||||||
|
income.neutral = theZone:getNumberFromZoneProperty ("neutral", income.base)
|
||||||
|
|
||||||
|
income.interval = theZone:getNumberFromZoneProperty("interval", 10 * 60) -- every 10 minutes
|
||||||
|
income.tickMessage = theZone:getStringFromZoneProperty("tickMessage", "New funds from income available.")
|
||||||
|
income.announceTicks = theZone:getBoolFromZoneProperty("announceTicks", true)
|
||||||
|
income.verbose = theZone.verbose
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function income.start()
|
||||||
|
-- lib check
|
||||||
|
if not dcsCommon.libCheck then
|
||||||
|
trigger.action.outText("income requires dcsCommon", 30)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if not dcsCommon.libCheck("income", income.requiredLibs) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- read config
|
||||||
|
income.readConfigZone()
|
||||||
|
|
||||||
|
-- read income zones
|
||||||
|
local attrZones = cfxZones.getZonesWithAttributeNamed("income")
|
||||||
|
for k, aZone in pairs(attrZones) do
|
||||||
|
income.createIncomeWithZone(aZone) -- process attributes
|
||||||
|
income.addIncomeZone(aZone) -- add to list
|
||||||
|
end
|
||||||
|
|
||||||
|
-- schedule first tick
|
||||||
|
timer.scheduleFunction(income.update, {}, timer.getTime() + income.interval)
|
||||||
|
|
||||||
|
trigger.action.outText("income v" .. income.version .. " started.", 30)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if not income.start() then
|
||||||
|
trigger.action.outText("income aborted: missing libraries", 30)
|
||||||
|
income = nil
|
||||||
|
end
|
||||||
@ -6,15 +6,30 @@ milHelo.requiredLibs = {
|
|||||||
"cfxMX",
|
"cfxMX",
|
||||||
}
|
}
|
||||||
milHelo.zones = {}
|
milHelo.zones = {}
|
||||||
|
milHelo.targetKeywords = {
|
||||||
|
"milTarget", -- my own
|
||||||
|
"camp", -- camps
|
||||||
|
"airfield", -- airfields
|
||||||
|
"FARP", -- FARPzones
|
||||||
|
}
|
||||||
|
|
||||||
milHelo.targets = {}
|
milHelo.targets = {}
|
||||||
|
milHelo.flights = {} -- all currently active mil helo flights
|
||||||
milHelo.ups = 1
|
milHelo.ups = 1
|
||||||
|
milHelo.missionTypes = {
|
||||||
|
"cas", -- standard cas
|
||||||
|
"patrol", -- orbit over zone for duration
|
||||||
|
"insert", -- insert one of the ground groups in the src zone after landing
|
||||||
|
"casz", -- engage in zone for target zone's radius
|
||||||
|
-- missing csar
|
||||||
|
}
|
||||||
|
|
||||||
function milHelo.addMilHeloZone(theZone)
|
function milHelo.addMilHeloZone(theZone)
|
||||||
milHelo.zones[theZone.name] = theZone
|
milHelo.zones[theZone.name] = theZone
|
||||||
end
|
end
|
||||||
|
|
||||||
function milHelo.addMilTargetZone(theZone)
|
function milHelo.addMilTargetZone(theZone)
|
||||||
milHelo.targets[theZone.name] = theZone
|
milHelo.targets[theZone.name] = theZone -- overwrite if duplicate
|
||||||
end
|
end
|
||||||
|
|
||||||
function milHelo.partOfGroupDataInZone(theZone, theUnits) -- move to mx?
|
function milHelo.partOfGroupDataInZone(theZone, theUnits) -- move to mx?
|
||||||
@ -51,15 +66,48 @@ end
|
|||||||
|
|
||||||
function milHelo.readMilHeloZone(theZone) -- process attributes
|
function milHelo.readMilHeloZone(theZone) -- process attributes
|
||||||
-- get mission type. part of milHelo
|
-- get mission type. part of milHelo
|
||||||
theZone.msnType = string.lower(theZone:getStringFromZoneProperty("milHelo", "cas"))
|
theZone.msnType = string.lower(theZone:getStringFromZoneProperty("milHelo", "cas"))
|
||||||
|
if dcsCommon.arrayContainsString(milHelo.missionTypes, theZone.msnType) then
|
||||||
|
-- great, mission type is known
|
||||||
|
else
|
||||||
|
trigger.action.outText("+++milH: zone <" .. theZone.name .. ">: unknown mission type <" .. theZone.msnType .. ">, defaulting to 'CAS'", 30)
|
||||||
|
theZone.msnType = "cas"
|
||||||
|
end
|
||||||
|
|
||||||
-- get all groups inside me
|
-- get all groups inside me
|
||||||
local myGroups, count = milHelo.allGroupsInZoneByData(theZone)
|
local myGroups, count = milHelo.allGroupsInZoneByData(theZone)
|
||||||
theZone.myGroups = myGroups
|
theZone.myGroups = myGroups
|
||||||
theZone.groupCount = count
|
theZone.groupCount = count
|
||||||
|
theZone.hGroups = {}
|
||||||
|
theZone.hCount = 0
|
||||||
|
theZone.gGroups = {}
|
||||||
|
theZone.gCount = 0
|
||||||
|
theZone.fGroups = {}
|
||||||
|
theZone.fCount = 0
|
||||||
|
-- sort into ground, helo and fixed
|
||||||
|
for groupName, data in pairs(myGroups) do
|
||||||
|
local catRaw = cfxMX.groupTypeByName[groupName]
|
||||||
|
if theZone.verbose then
|
||||||
|
trigger.action.outText("Proccing zone <" .. theZone.name .. ">: group <" .. groupName .. "> - type <" .. catRaw .. ">", 30)
|
||||||
|
end
|
||||||
|
if catRaw == "helicopter" then
|
||||||
|
theZone.hGroups[groupName] = data
|
||||||
|
theZone.hCount = theZone.hCount + 1
|
||||||
|
elseif catRaw == "plane" then
|
||||||
|
theZone.fGroups[groupName] = data
|
||||||
|
theZone.fCount = theZone.fCount + 1
|
||||||
|
elseif catRaw == "vehicle" then
|
||||||
|
theZone.gGroups[groupName] = data
|
||||||
|
theZone.gCount = theZone.gCount + 1
|
||||||
|
else
|
||||||
|
trigger.action.outText("+++milH: ignored group <" .. groupName .. ">: unknown type <" .. catRaw .. ">", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
theZone.coa = theZone:getCoalitionFromZoneProperty("coalition", 0)
|
theZone.coa = theZone:getCoalitionFromZoneProperty("coalition", 0)
|
||||||
theZone.hot = theZone:getBoolFromZoneProperty("hot", true)
|
theZone.hot = theZone:getBoolFromZoneProperty("hot", false)
|
||||||
theZone.speed = theZone:getNumberFromZoneProperty("speed", 50) -- 110 mph
|
theZone.speed = theZone:getNumberFromZoneProperty("speed", 50) -- 110 mph
|
||||||
theZone.alt = theZone:getNumberFromZoneProperty("alt", 100) -- we are always radar alt
|
theZone.alt = theZone:getNumberFromZoneProperty("alt", 100) -- we are always radar alt
|
||||||
|
theZone.loiter = theZone:getNumberFromZoneProperty("loiter", 3600) -- 1 hour loiter default
|
||||||
-- wipe all existing
|
-- wipe all existing
|
||||||
for groupName, data in pairs(myGroups) do
|
for groupName, data in pairs(myGroups) do
|
||||||
local g = Group.getByName(groupName)
|
local g = Group.getByName(groupName)
|
||||||
@ -73,25 +121,24 @@ function milHelo.readMilHeloZone(theZone) -- process attributes
|
|||||||
end
|
end
|
||||||
|
|
||||||
function milHelo.readMilTargetZone(theZone)
|
function milHelo.readMilTargetZone(theZone)
|
||||||
|
-- can also be "camp", "farp", "airfield"
|
||||||
|
theZone.casRadius = theZone:getNumberFromZoneProperty("casRadius", theZone.radius)
|
||||||
|
if (not theZone.isCircle) and not theZone:hasProperty("casRadius") then
|
||||||
|
-- often when we have a camp there is no cas radius, use 10km
|
||||||
|
-- and zone is ploygonal
|
||||||
|
if theZone.verbose then
|
||||||
|
trigger.action.outText("+++milH: Warning - milH target zone <" .. theZone.name .. "> is polygonal and has no CAS radius attribute. Defaulting to 10km", 30)
|
||||||
|
end
|
||||||
|
theZone.casRadius = 10000
|
||||||
|
end
|
||||||
if theZone.verbose or milHelo.verbose then
|
if theZone.verbose or milHelo.verbose then
|
||||||
trigger.action.outText("+++milH: processed TARGET zone <" .. theZone.name .. ">", 30)
|
trigger.action.outText("+++milH: processed milHelo TARGET zone <" .. theZone.name .. ">", 30)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Spawning for a zone
|
-- 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)
|
function milHelo.createCASTask(num, auto)
|
||||||
if not auto then auto = false end
|
if not auto then auto = false end
|
||||||
@ -104,7 +151,6 @@ function milHelo.createCASTask(num, auto)
|
|||||||
task.auto = auto
|
task.auto = auto
|
||||||
local params = {}
|
local params = {}
|
||||||
params.priority = 0
|
params.priority = 0
|
||||||
-- params.targetTypes = {"Helicopters", "Ground Units", "Light armed ships"}
|
|
||||||
local targetTypes = {[1] = "Helicopters", [2] = "Ground Units", [3] = "Light armed ships",}
|
local targetTypes = {[1] = "Helicopters", [2] = "Ground Units", [3] = "Light armed ships",}
|
||||||
params.targetTypes = targetTypes
|
params.targetTypes = targetTypes
|
||||||
|
|
||||||
@ -132,6 +178,29 @@ function milHelo.createROETask(num, roe)
|
|||||||
return task
|
return task
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function milHelo.createEngageIZTask(num, theZone)
|
||||||
|
local p = theZone:getPoint()
|
||||||
|
if not num then num = 1 end
|
||||||
|
local task = {}
|
||||||
|
task.number = num
|
||||||
|
task.enabled = true
|
||||||
|
task.auto = false
|
||||||
|
task.id = "EngageTargetsInZone"
|
||||||
|
local params = {}
|
||||||
|
targetTypes = {}
|
||||||
|
targetTypes[1] = "All"
|
||||||
|
params.targetTypes = targetTypes
|
||||||
|
params.x = p.x
|
||||||
|
params.y = p.z -- !!!!
|
||||||
|
params.value = "All;"
|
||||||
|
params.noTargetTypes = {}
|
||||||
|
params.priority = 0
|
||||||
|
local radius = theZone.casRadius
|
||||||
|
params.zoneRadius = radius
|
||||||
|
task.params = params
|
||||||
|
return task
|
||||||
|
end
|
||||||
|
|
||||||
function milHelo.createOrbitTask(num, duration, theZone)
|
function milHelo.createOrbitTask(num, duration, theZone)
|
||||||
if not num then num = 1 end
|
if not num then num = 1 end
|
||||||
local task = {}
|
local task = {}
|
||||||
@ -155,23 +224,72 @@ function milHelo.createOrbitTask(num, duration, theZone)
|
|||||||
return task
|
return task
|
||||||
end
|
end
|
||||||
|
|
||||||
function milHelo.createTakeOffWP(theZone)
|
function milHelo.createLandTask(p, duration, num)
|
||||||
|
if not num then num = 1 end
|
||||||
|
local t = {}
|
||||||
|
t.enabled = true
|
||||||
|
t.auto = false
|
||||||
|
t.id = "ControlledTask"
|
||||||
|
t.number = num
|
||||||
|
local params = {}
|
||||||
|
t.params = params
|
||||||
|
local ptsk = {}
|
||||||
|
params.task = ptsk
|
||||||
|
ptsk.id = "Land"
|
||||||
|
local ptp = {}
|
||||||
|
ptsk.params = ptp
|
||||||
|
ptp.x = p.x
|
||||||
|
ptp.y = p.z
|
||||||
|
ptp.duration = "300" -- not sure why
|
||||||
|
ptp.durationFlag = false -- off anyway
|
||||||
|
local stopCon = {}
|
||||||
|
stopCon.duration = duration
|
||||||
|
params.stopCondition = stopCon
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
function milHelo.createCommandTask(theCommand, num)
|
||||||
|
if not num then num = 1 end
|
||||||
|
local t = {}
|
||||||
|
t.enabled = true
|
||||||
|
t.auto = false
|
||||||
|
t.id = "WrappedAction"
|
||||||
|
t.number = num
|
||||||
|
local params = {}
|
||||||
|
t.params = params
|
||||||
|
local action = {}
|
||||||
|
params.action = action
|
||||||
|
action.id = "Script"
|
||||||
|
local p2 = {}
|
||||||
|
action.params = p2
|
||||||
|
p2.command = theCommand
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
function milHelo.createTakeOffWP(theZone, engageInZone, engageZone)
|
||||||
local WP = {}
|
local WP = {}
|
||||||
WP.alt = theZone.alt
|
WP.alt = 500 -- theZone.alt
|
||||||
WP.alt_type = "RADIO"
|
WP.alt_type = "BARO"
|
||||||
WP.properties = {}
|
WP.properties = {}
|
||||||
WP.properties.addopt = {}
|
WP.properties.addopt = {}
|
||||||
WP.action = "From Ground Area"
|
WP.action = "From Ground Area"
|
||||||
if theZone.hot then WP.action = "From Ground Area Hot" end
|
if theZone.hot then WP.action = "From Ground Area Hot" end
|
||||||
WP.speed = theZone.speed
|
WP.speed = 0 -- theZone.speed
|
||||||
WP.task = {}
|
WP.task = {}
|
||||||
WP.task.id = "ComboTask"
|
WP.task.id = "ComboTask"
|
||||||
WP.task.params = {}
|
WP.task.params = {}
|
||||||
local tasks = {}
|
local tasks = {}
|
||||||
-- local casTask = milHelo.createCASTask(1)
|
local casTask = milHelo.createCASTask(1)
|
||||||
-- tasks[1] = casTask
|
tasks[1] = casTask
|
||||||
local roeTask = milHelo.createROETask(1,0) -- 0 = weapons free
|
local roeTask = milHelo.createROETask(2,0) -- 0 = weapons free
|
||||||
tasks[1] = roeTask
|
tasks[2] = roeTask
|
||||||
|
if engageInZone then
|
||||||
|
if not engageZone then
|
||||||
|
trigger.action.outText("+++milH: Warning - caz task with no engage zone!", 30)
|
||||||
|
end
|
||||||
|
local eiz = milHelo.createEngageIZTask(3, engageZone)
|
||||||
|
tasks[3] = eiz
|
||||||
|
end
|
||||||
WP.task.params.tasks = tasks
|
WP.task.params.tasks = tasks
|
||||||
--
|
--
|
||||||
WP.type = "TakeOffGround"
|
WP.type = "TakeOffGround"
|
||||||
@ -180,7 +298,7 @@ function milHelo.createTakeOffWP(theZone)
|
|||||||
WP.x = p.x
|
WP.x = p.x
|
||||||
WP.y = p.z
|
WP.y = p.z
|
||||||
WP.ETA = 0
|
WP.ETA = 0
|
||||||
WP.ETA_locked = false
|
WP.ETA_locked = true
|
||||||
WP.speed_locked = true
|
WP.speed_locked = true
|
||||||
WP.formation_template = ""
|
WP.formation_template = ""
|
||||||
return WP
|
return WP
|
||||||
@ -201,9 +319,9 @@ function milHelo.createOrbitWP(theZone, targetPoint)
|
|||||||
WP.task.params = {}
|
WP.task.params = {}
|
||||||
-- start params construct
|
-- start params construct
|
||||||
local tasks = {}
|
local tasks = {}
|
||||||
local casTask = milHelo.createCASTask(1, false)
|
local casTask = milHelo.createCASTask(1)
|
||||||
tasks[1] = casTask
|
tasks[1] = casTask
|
||||||
local oTask = milHelo.createOrbitTask(2, 3600, theZone)
|
local oTask = milHelo.createOrbitTask(2, theZone.loiter, theZone)
|
||||||
tasks[2] = oTask
|
tasks[2] = oTask
|
||||||
WP.task.params.tasks = tasks
|
WP.task.params.tasks = tasks
|
||||||
WP.type = "Turning Point"
|
WP.type = "Turning Point"
|
||||||
@ -217,41 +335,123 @@ function milHelo.createOrbitWP(theZone, targetPoint)
|
|||||||
return WP
|
return WP
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function milHelo.createLandWP(gName, theZone, targetZone)
|
||||||
|
local toWP
|
||||||
|
toWP = dcsCommon.createSimpleRoutePointData(targetZone:getPoint(), theZone.alt, theZone.speed)
|
||||||
|
toWP.alt_type = "RADIO"
|
||||||
|
|
||||||
|
local task = {}
|
||||||
|
task.id = "ComboTask"
|
||||||
|
task.params = {}
|
||||||
|
local ttsk = {}
|
||||||
|
local p = targetZone:getPoint()
|
||||||
|
ttsk[1] = milHelo.createLandTask(p, milHelo.landingDuration, 1)
|
||||||
|
local command = "milHelo.landedCB('" .. gName .. "', '" .. targetZone:getName() .. "', '" .. theZone:getName() .. "')"
|
||||||
|
ttsk[2] = milHelo.createCommandTask(command,2)
|
||||||
|
task.params.tasks = ttsk
|
||||||
|
toWP.task = task
|
||||||
|
return toWP
|
||||||
|
end
|
||||||
|
|
||||||
|
function milHelo.createOMWCallbackWP(gName, number, pt, alt, speed, action) -- name is group name
|
||||||
|
if not action then action = "none" end
|
||||||
|
local omwWP = dcsCommon.createSimpleRoutePointData(pt, alt, speed)
|
||||||
|
omwWP.alt_type = "RADIO"
|
||||||
|
-- create a command waypoint
|
||||||
|
local task = {}
|
||||||
|
task.id = "ComboTask"
|
||||||
|
task.params = {}
|
||||||
|
local ttsk = {}
|
||||||
|
local command = "milHelo.reachedWP('" .. gName .. "', '" .. number .. "', '" .. action .."')"
|
||||||
|
ttsk[1] = milHelo.createCommandTask(command,1)
|
||||||
|
task.params.tasks = ttsk
|
||||||
|
omwWP.task = task
|
||||||
|
return omwWP
|
||||||
|
end
|
||||||
|
|
||||||
|
-- a point yDegrees off the path from AB, xPercent of the total distance
|
||||||
|
-- between A and B away from A
|
||||||
|
--[[--
|
||||||
|
function milHelo.pointXpercentYdegOffAB(A, B, xPer, yDeg) -- rets xzz point
|
||||||
|
local bearingRad = dcsCommon.bearingFromAtoB(A, B)
|
||||||
|
local dist = dcsCommon.dist(A, B)
|
||||||
|
local deviation = bearingRad + yDeg * 0.0174533
|
||||||
|
local newDist = dist * xPer/100
|
||||||
|
local newPoint = dcsCommon.pointInDirectionOfPointXYY(deviation, newDist, A)
|
||||||
|
return newPoint
|
||||||
|
end
|
||||||
|
--]]--
|
||||||
|
|
||||||
function milHelo.spawnForZone(theZone, targetZone)
|
function milHelo.spawnForZone(theZone, targetZone)
|
||||||
local theRawData = dcsCommon.getNthItem(theZone.myGroups, 1)
|
local n = dcsCommon.randomBetween(1, theZone.hCount)
|
||||||
|
local theRawData = dcsCommon.getNthItem(theZone.hGroups, n)
|
||||||
local gData = dcsCommon.clone(theRawData)
|
local gData = dcsCommon.clone(theRawData)
|
||||||
--[[--
|
local oName = gData.name
|
||||||
|
|
||||||
-- pre-process gData: names, id etc
|
-- pre-process gData: names, id etc
|
||||||
gData.name = dcsCommon.uuid(gData.name)
|
gData.name = dcsCommon.uuid(gData.name)
|
||||||
|
local gName = gData.name
|
||||||
for idx, uData in pairs(gData.units) do
|
for idx, uData in pairs(gData.units) do
|
||||||
uData.name = dcsCommon.uuid(uData.name)
|
uData.name = dcsCommon.uuid(uData.name)
|
||||||
|
uData.alt = 10
|
||||||
|
uData.alt_type = "RADIO"
|
||||||
|
uData.speed = 0
|
||||||
|
uData.unitId = nil
|
||||||
end
|
end
|
||||||
gData.groupId = nil
|
gData.groupId = nil
|
||||||
|
|
||||||
-- change task according to missionType in Zone
|
-- change task according to missionType in Zone
|
||||||
|
-- we currently use CAS for all
|
||||||
gData.task = "CAS"
|
gData.task = "CAS"
|
||||||
|
|
||||||
-- create and process route
|
-- create and process route
|
||||||
local route = {}
|
local route = {}
|
||||||
route.points = {}
|
route.points = {}
|
||||||
-- gData.route = route
|
gData.route = route
|
||||||
-- create take-off waypoint
|
-- create take-off waypoint
|
||||||
local wpTOff = milHelo.createTakeOffWP(theZone)
|
local casInZone = theZone.msnType == "casz"
|
||||||
|
if theZone.verbose and casInZone then
|
||||||
|
trigger.action.outText("Setting up casZ for <" .. theZone.name .. "> to <" .. targetZone.name .. ">", 30)
|
||||||
|
end
|
||||||
|
|
||||||
|
local wpTOff = milHelo.createTakeOffWP(theZone, casInZone, targetZone)
|
||||||
|
|
||||||
-- depending on mission, create an orbit or land WP
|
-- depending on mission, create an orbit or land WP
|
||||||
local dest = targetZone:getPoint()
|
local dest = targetZone:getPoint()
|
||||||
local wpDest = milHelo.createOrbitWP(theZone, dest)
|
local B = dest
|
||||||
-- move group to WP1 and add WP1 and WP2 to route
|
local A = theZone:getPoint()
|
||||||
-- dcsCommon.moveGroupDataTo(theGroup,
|
if theZone.msnType == "cas" or theZone.msnType == "patrol" then
|
||||||
-- fromWP.x,
|
dcsCommon.addRoutePointForGroupData(gData, wpTOff)
|
||||||
-- fromWP.y)
|
local wpDest = milHelo.createOrbitWP(theZone, dest)
|
||||||
|
dcsCommon.addRoutePointForGroupData(gData, wpDest)
|
||||||
----
|
local retPt = milHelo.createLandWP(gName, theZone, theZone)
|
||||||
dcsCommon.addRoutePointForGroupData(gData, wpTOff)
|
dcsCommon.addRoutePointForGroupData(gData, retPt)
|
||||||
dcsCommon.addRoutePointForGroupData(gData, wpDest)
|
--dcsCommon.dumpVar2Str("caser group", gData)
|
||||||
--]]--
|
elseif theZone.msnType == "casz" then
|
||||||
dcsCommon.dumpVar2Str("route", gData.route)
|
dcsCommon.addRoutePointForGroupData(gData, wpTOff)
|
||||||
|
-- go to CAS destination with Engage in Zone active
|
||||||
|
-- we may want to make ingress and egress wp before heading to
|
||||||
|
-- the 'real' CASZ point
|
||||||
|
-- make ingress point, in direction of target, 30 degrees to the right, half distance.
|
||||||
|
local ingress = dcsCommon.pointXpercentYdegOffAB(A, B, math.random(50,80), math.random(20,50))
|
||||||
|
--local pt = targetZone:getPoint()
|
||||||
|
local omw1 = milHelo.createOMWCallbackWP(gName, 2, ingress, theZone.alt, theZone.speed, "none")
|
||||||
|
dcsCommon.addRoutePointForGroupData(gData, omw1)
|
||||||
|
local omw2 = milHelo.createOMWCallbackWP(gName, 3, B, theZone.alt, theZone.speed, "none")
|
||||||
|
dcsCommon.addRoutePointForGroupData(gData, omw2)
|
||||||
|
-- egress point
|
||||||
|
local egress = dcsCommon.pointXpercentYdegOffAB(B, A, math.random(20, 50), math.random(20,50))
|
||||||
|
local omw3 = milHelo.createOMWCallbackWP(gName, 4, egress, theZone.alt, theZone.speed, "none")
|
||||||
|
dcsCommon.addRoutePointForGroupData(gData, omw3)
|
||||||
|
local retPt = milHelo.createLandWP(gName, theZone, theZone)
|
||||||
|
dcsCommon.addRoutePointForGroupData(gData, retPt)
|
||||||
|
elseif theZone.msnType == "insert" then
|
||||||
|
local wpDest = milHelo.createLandWP(gName, theZone, targetZone)
|
||||||
|
dcsCommon.addRoutePointForGroupData(gData, wpTOff)
|
||||||
|
dcsCommon.addRoutePointForGroupData(gData, wpDest)
|
||||||
|
end
|
||||||
|
|
||||||
-- make it a cty
|
-- make coa a cty
|
||||||
if theZone.coa == 0 then
|
if theZone.coa == 0 then
|
||||||
trigger.action.outText("+++milH: WARNING - zone <" .. theZone.name .. "> is NEUTRAL", 30)
|
trigger.action.outText("+++milH: WARNING - zone <" .. theZone.name .. "> is NEUTRAL", 30)
|
||||||
end
|
end
|
||||||
@ -259,9 +459,162 @@ function milHelo.spawnForZone(theZone, targetZone)
|
|||||||
-- spawn
|
-- spawn
|
||||||
local groupCat = Group.Category.HELICOPTER
|
local groupCat = Group.Category.HELICOPTER
|
||||||
local theSpawnedGroup = coalition.addGroup(cty, groupCat, gData)
|
local theSpawnedGroup = coalition.addGroup(cty, groupCat, gData)
|
||||||
|
local theFlight = {}
|
||||||
|
theFlight.oName = oName
|
||||||
|
theFlight.spawn = theSpawnedGroup
|
||||||
|
theFlight.origin = theZone
|
||||||
|
theFlight.destination = targetZone
|
||||||
|
milHelo.flights[gName] = theFlight --theSpawnedGroup
|
||||||
|
return theSpawnedGroup, gData
|
||||||
|
end
|
||||||
|
--
|
||||||
|
-- mil helo landed callback (insertion)
|
||||||
|
--
|
||||||
|
function milHelo.insertTroops(theUnit, targetZone, srcZone)
|
||||||
|
local theZone = srcZone
|
||||||
|
local n = dcsCommon.randomBetween(1, theZone.gCount)
|
||||||
|
local theRawData = dcsCommon.getNthItem(theZone.gGroups, n)
|
||||||
|
-- local theRawData = dcsCommon.getNthItem(srcZone.gGroups, 1)
|
||||||
|
if not theRawData then
|
||||||
|
trigger.action.outText("+++milH: WARNING: no troops to insert for zone <" .. srcZone.name .. ">", 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local gData = dcsCommon.clone(theRawData)
|
||||||
|
-- deploy in ring formation
|
||||||
|
-- remove all routes
|
||||||
|
-- mayhaps prepare for orders and formation
|
||||||
|
|
||||||
|
local p = theUnit:getPoint()
|
||||||
|
gData.route = nil -- no more route. stand in place
|
||||||
|
gData.name = dcsCommon.uuid(gData.name)
|
||||||
|
local gName = gData.name
|
||||||
|
for idx, uData in pairs(gData.units) do
|
||||||
|
uData.name = dcsCommon.uuid(uData.name)
|
||||||
|
uData.speed = 0
|
||||||
|
uData.heading = 0
|
||||||
|
uData.unitId = nil
|
||||||
|
end
|
||||||
|
gData.groupId = nil
|
||||||
|
dcsCommon.moveGroupDataTo(gData, 0, 0) -- move to origin so we can arrange them
|
||||||
|
|
||||||
|
dcsCommon.arrangeGroupDataIntoFormation(gData, 20, nil, "CIRCLE_OUT")
|
||||||
|
|
||||||
|
dcsCommon.moveGroupDataTo(gData, p.x, p.z) -- move arranged group to helo
|
||||||
|
|
||||||
|
-- make coa 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.GROUND
|
||||||
|
local theSpawnedGroup = coalition.addGroup(cty, groupCat, gData)
|
||||||
|
|
||||||
|
trigger.action.outText("Inserted troops <" .. gName .. ">", 30)
|
||||||
|
|
||||||
return theSpawnedGroup, gData
|
return theSpawnedGroup, gData
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function milHelo.replaceUnitsWithStatics(gName)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function milHelo.getRawDataFromGroupNamed(gName, oName)
|
||||||
|
local theGroup = Group.getByName(gName)
|
||||||
|
local groupName = gName
|
||||||
|
local cat = theGroup:getCategory()
|
||||||
|
-- access mxdata for livery because getDesc does not return the livery
|
||||||
|
local liveries = {}
|
||||||
|
local mxData = cfxMX.getGroupFromDCSbyName(oName)
|
||||||
|
for idx, theUnit in pairs (mxData.units) do
|
||||||
|
liveries[theUnit.name] = theUnit.livery_id
|
||||||
|
end
|
||||||
|
|
||||||
|
local ctry
|
||||||
|
local gID = theGroup:getID()
|
||||||
|
local allUnits = theGroup:getUnits()
|
||||||
|
local rawGroup = {}
|
||||||
|
rawGroup.name = groupName
|
||||||
|
local rawUnits = {}
|
||||||
|
for idx, theUnit in pairs(allUnits) do
|
||||||
|
local ir = {}
|
||||||
|
local unitData = theUnit:getDesc()
|
||||||
|
-- build record
|
||||||
|
ir.heading = dcsCommon.getUnitHeading(theUnit)
|
||||||
|
ir.name = theUnit:getName()
|
||||||
|
ir.type = unitData.typeName -- warning: fields are called differently! typename vs type
|
||||||
|
ir.livery_id = liveries[ir.name] -- getDesc does not return livery
|
||||||
|
ir.groupId = gID
|
||||||
|
ir.unitId = theUnit:getID()
|
||||||
|
local up = theUnit:getPoint()
|
||||||
|
ir.x = up.x
|
||||||
|
ir.y = up.z -- !!! warning!
|
||||||
|
-- see if any zones are linked to this unit
|
||||||
|
ir.linkedZones = cfxZones.zonesLinkedToUnit(theUnit)
|
||||||
|
|
||||||
|
table.insert(rawUnits, ir)
|
||||||
|
ctry = theUnit:getCountry()
|
||||||
|
end
|
||||||
|
rawGroup.ctry = ctry
|
||||||
|
rawGroup.cat = cat
|
||||||
|
rawGroup.units = rawUnits
|
||||||
|
return rawGroup, cat, ctry
|
||||||
|
end
|
||||||
|
|
||||||
|
function milHelo.spawnImpostorsFromData(rawData, cat, ctry)
|
||||||
|
for idx, unitData in pairs(rawData.units) do
|
||||||
|
-- build impostor record
|
||||||
|
local ir = {}
|
||||||
|
ir.heading = unitData.heading
|
||||||
|
ir.type = unitData.type
|
||||||
|
ir.name = dcsCommon.uuid(rawData.name) -- .. "-" .. tostring(impostors.uniqueID())
|
||||||
|
ir.groupID = nil -- impostors.uniqueID()
|
||||||
|
ir.unitId = nil -- impostors.uniqueID()
|
||||||
|
ir.x = unitData.x
|
||||||
|
ir.y = unitData.y
|
||||||
|
ir.livery_id = unitData.livery_id
|
||||||
|
-- spawn the impostor
|
||||||
|
local theImp = coalition.addStaticObject(ctry, ir)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function milHelo.reachedWP(gName, wpNum, action)
|
||||||
|
trigger.action.outText("MilH group <" .. gName .. " reached wp #" .. wpNum .. ".", 30)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function milHelo.landedCB(who, where, from) -- who group name, where a zone
|
||||||
|
trigger.action.outText("milhelo landed CB for group <" .. who .. ">", 30)
|
||||||
|
-- step 1: remove the flight
|
||||||
|
local theGroup = Group.getByName(who)
|
||||||
|
if theGroup then
|
||||||
|
if Group.isExist(theGroup) then
|
||||||
|
Group.destroy(theGroup)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
trigger.action.outText("+++milH: cannot find group <" .. who .. ">", 30)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- step 3: replace with static helo
|
||||||
|
local aGroup = theGroup
|
||||||
|
local theFlight = milHelo.flights[who]
|
||||||
|
local oName = theFlight.oName
|
||||||
|
local theZone = theFlight.origin
|
||||||
|
if theZone.msn == "insertion" then
|
||||||
|
-- create a static stand-in for scenery
|
||||||
|
local rawData, cat, ctry = milHelo.getRawDataFromGroupNamed(who, oName)
|
||||||
|
Group.destroy(aGroup)
|
||||||
|
milHelo.spawnImpostorsFromData(rawData, cat, ctry)
|
||||||
|
else
|
||||||
|
-- remove group
|
||||||
|
Group.destroy(aGroup)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- remove flight from list of active flights
|
||||||
|
milHelo.flights[who] = nil
|
||||||
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
-- update and event
|
-- update and event
|
||||||
--
|
--
|
||||||
@ -269,10 +622,98 @@ function milHelo.update()
|
|||||||
timer.scheduleFunction(milHelo.update, {}, timer.getTime() + 1)
|
timer.scheduleFunction(milHelo.update, {}, timer.getTime() + 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
function milHelo.onEvent(theEvent)
|
function milHelo.GCcollected(gName)
|
||||||
|
-- do some housekeeping?
|
||||||
|
trigger.action.outText("removed flight <" .. gName .. ">", 30)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function milHelo.GC()
|
||||||
|
timer.scheduleFunction(milHelo.GC, {}, timer.getTime() + 1)
|
||||||
|
local filtered = {}
|
||||||
|
for gName, theFlight in pairs(milHelo.flights) do
|
||||||
|
local theGroup = Group.getByName(gName)
|
||||||
|
if theGroup and Group.isExist(theGroup) then
|
||||||
|
-- all fine, keep it
|
||||||
|
filtered[gName] = theFlight
|
||||||
|
else
|
||||||
|
milHelo.GCcollected(gName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
milHelo.flights = filtered
|
||||||
|
end
|
||||||
|
|
||||||
|
function milHelo:onEvent(theEvent)
|
||||||
|
if not theEvent then return end
|
||||||
|
if not theEvent.initiator then return end
|
||||||
|
local theUnit = theEvent.initiator
|
||||||
|
if not theUnit.getGroup then return end
|
||||||
|
local theGroup = theUnit:getGroup()
|
||||||
|
if not theGroup then
|
||||||
|
-- trigger.action.outText("event <" .. theEvent.id .. ">: group shenenigans for unit detected", 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local gName = theGroup:getName()
|
||||||
|
local theFlight = milHelo.flights[gName]
|
||||||
|
if not theFlight then return end
|
||||||
|
|
||||||
|
local id = theEvent.id
|
||||||
|
if id == 4 then
|
||||||
|
-- flight landed
|
||||||
|
-- did it land in target zone?
|
||||||
|
local p = theUnit:getPoint()
|
||||||
|
local srcZone = theFlight.origin
|
||||||
|
local tgtZone = theFlight.destination
|
||||||
|
if tgtZone:pointInZone(p) then
|
||||||
|
trigger.action.outText("Flight <" .. gName .. "> originating from <" .. srcZone.name .. "> landed in zone <" .. tgtZone.name .. ">", 30)
|
||||||
|
if srcZone.msnType == "insert" then
|
||||||
|
trigger.action.outText("Commencing Troop Insertion", 30)
|
||||||
|
milHelo.insertTroops(theUnit, tgtZone, srcZone)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- maybe its a return flight
|
||||||
|
if srcZone:pointInZone(p) then
|
||||||
|
trigger.action.outText("Flight <" .. gName .. "> originating from <" .. srcZone.name .. "> landed back home", 30)
|
||||||
|
else
|
||||||
|
trigger.action.outText("Flight <" .. gName .. "> originating from <" .. srcZone.name .. "> landed OUTSIDE of src or target zone <" .. tgtZone.name .. ">", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- trigger.action.outText("Event <" .. theEvent.id .. "> for milHelo flight <" .. gName .. ">", 30)
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- API
|
||||||
|
--
|
||||||
|
function milHelo.getMilSources(side, msnType) -- msnType is optional
|
||||||
|
if side == "red" then side = 1 end -- better safe...
|
||||||
|
if side == "blue" then side = 2 end
|
||||||
|
local sources = {}
|
||||||
|
for idx, theZone in pairs(milHelo.zones) do
|
||||||
|
if theZone.owner == side then -- must be owned by same side
|
||||||
|
if msnType then
|
||||||
|
if theZone.msnType == msnType then
|
||||||
|
table.insert(sources, theZone)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
table.insert(sources, theZone)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return sources -- an array, NOT dict so we can pickrandom
|
||||||
|
end
|
||||||
|
|
||||||
|
function milHelo.getMilTargets(side) -- gets mil targets that DO NOT belong to side
|
||||||
|
if side == "red" then side = 1 end -- better safe...
|
||||||
|
if side == "blue" then side = 2 end
|
||||||
|
local tgt = {}
|
||||||
|
for idx, theZone in pairs(milHelo.targets) do
|
||||||
|
if theZone.owner ~= side then -- must NOT be owned by same side
|
||||||
|
table.insert(tgt, theZone)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return tgt
|
||||||
|
end
|
||||||
--
|
--
|
||||||
-- Config & start
|
-- Config & start
|
||||||
--
|
--
|
||||||
@ -282,6 +723,7 @@ function milHelo.readConfigZone()
|
|||||||
theZone = cfxZones.createSimpleZone("milHeloConfig")
|
theZone = cfxZones.createSimpleZone("milHeloConfig")
|
||||||
end
|
end
|
||||||
milHelo.verbose = theZone.verbose
|
milHelo.verbose = theZone.verbose
|
||||||
|
milHelo.landingDuration = theZone:getNumberFromZoneProperty("landingDuration", 180) -- seconds = 3 minutes
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -305,11 +747,13 @@ function milHelo.start()
|
|||||||
milHelo.addMilHeloZone(aZone) -- add to list
|
milHelo.addMilHeloZone(aZone) -- add to list
|
||||||
end
|
end
|
||||||
|
|
||||||
attrZones = cfxZones.getZonesWithAttributeNamed("milTarget")
|
for idx, keyWord in pairs(milHelo.targetKeywords) do
|
||||||
for k, aZone in pairs(attrZones) do
|
attrZones = cfxZones.getZonesWithAttributeNamed(keyWord)
|
||||||
milHelo.readMilTargetZone(aZone) -- process attributes
|
for k, aZone in pairs(attrZones) do
|
||||||
milHelo.addMilTargetZone(aZone) -- add to list
|
milHelo.readMilTargetZone(aZone) -- process attributes
|
||||||
end
|
milHelo.addMilTargetZone(aZone) -- add to list
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- start update in 5 seconds
|
-- start update in 5 seconds
|
||||||
timer.scheduleFunction(milHelo.update, {}, timer.getTime() + 1/milHelo.ups)
|
timer.scheduleFunction(milHelo.update, {}, timer.getTime() + 1/milHelo.ups)
|
||||||
@ -327,7 +771,21 @@ if not milHelo.start() then
|
|||||||
milHelo = nil
|
milHelo = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
function milHelo.latestuff()
|
||||||
|
trigger.action.outText("doing stuff", 30)
|
||||||
|
local theZone = cfxZones.getZoneByName("milCAS") --dcsCommon.getFirstItem(milHelo.zones)
|
||||||
|
local targetZone = cfxZones.getZoneByName("mh Target") -- dcsCommon.getFirstItem(milHelo.targets)
|
||||||
|
milHelo.spawnForZone(theZone, targetZone)
|
||||||
|
theZone = cfxZones.getZoneByName("milInsert") --dcsCommon.getNthItem(milHelo.zones, 2)
|
||||||
|
milHelo.spawnForZone(theZone, targetZone)
|
||||||
|
theZone = cfxZones.getZoneByName("doCASZ")
|
||||||
|
targetZone = cfxZones.getZoneByName("milTarget Z")
|
||||||
|
if not theZone then trigger.action.outText("Not theZone", 30) end
|
||||||
|
if not targetZone then trigger.action.OutText("Not targetZone", 30) end
|
||||||
|
milHelo.spawnForZone(theZone, targetZone)
|
||||||
|
end
|
||||||
|
|
||||||
-- do some one-time stuff
|
-- do some one-time stuff
|
||||||
local theZone = dcsCommon.getFirstItem(milHelo.zones)
|
timer.scheduleFunction(milHelo.latestuff, {}, timer.getTime() + 1)
|
||||||
local targetZone = dcsCommon.getFirstItem(milHelo.targets)
|
--]]--
|
||||||
milHelo.spawnForZone(theZone, targetZone)
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
cfxOwnedZones = {}
|
cfxOwnedZones = {}
|
||||||
cfxOwnedZones.version = "2.2.0"
|
cfxOwnedZones.version = "2.3.0"
|
||||||
cfxOwnedZones.verbose = false
|
cfxOwnedZones.verbose = false
|
||||||
cfxOwnedZones.announcer = true
|
cfxOwnedZones.announcer = true
|
||||||
cfxOwnedZones.name = "cfxOwnedZones"
|
cfxOwnedZones.name = "cfxOwnedZones"
|
||||||
@ -30,25 +30,30 @@ cfxOwnedZones.name = "cfxOwnedZones"
|
|||||||
- method support for global (config) output
|
- method support for global (config) output
|
||||||
- moved drawZone to cfxZones
|
- moved drawZone to cfxZones
|
||||||
2.2.0 - excludedTypes option in config
|
2.2.0 - excludedTypes option in config
|
||||||
|
2.3.0 - include airfield zones (module) in collectZones()
|
||||||
|
- if airfield is defined.
|
||||||
|
- allManagedOwnedZones
|
||||||
|
- gatherAllManagedOwnedZones()
|
||||||
|
- commented out unused (?) methods
|
||||||
|
- optmized getNearestEnemyOwnedZone
|
||||||
|
- collectZones now uses gatherAllManagedOwnedZones
|
||||||
|
- sideOwnsAll can use allManagedOwnedZones
|
||||||
|
- per-zone local numCap
|
||||||
|
- per-zone local numkeep
|
||||||
|
- title attribute
|
||||||
|
- code clean-up
|
||||||
--]]--
|
--]]--
|
||||||
cfxOwnedZones.requiredLibs = {
|
cfxOwnedZones.requiredLibs = {
|
||||||
"dcsCommon",
|
"dcsCommon",
|
||||||
"cfxZones",
|
"cfxZones",
|
||||||
}
|
}
|
||||||
|
|
||||||
cfxOwnedZones.zones = {}
|
cfxOwnedZones.zones = {} -- ownedZones FROM THIS module
|
||||||
|
cfxOwnedZones.allManagedOwnedZones = {} -- superset, indexed by name
|
||||||
cfxOwnedZones.ups = 1
|
cfxOwnedZones.ups = 1
|
||||||
cfxOwnedZones.initialized = false
|
cfxOwnedZones.initialized = false
|
||||||
--[[--
|
|
||||||
owned zones is a module that manages conquerable zones and keeps a record
|
|
||||||
of who owns the zone based on rules
|
|
||||||
|
|
||||||
*** EXTENTDS ZONES ***
|
-- *** EXTENTDS ZONES *** --
|
||||||
|
|
||||||
when a zone changes hands, a callback can be installed to be told of that fact
|
|
||||||
callback has the format (zone, newOwner, formerOwner) with zone being the Zone, and new owner and former owners
|
|
||||||
--]]--
|
|
||||||
|
|
||||||
cfxOwnedZones.conqueredCallbacks = {}
|
cfxOwnedZones.conqueredCallbacks = {}
|
||||||
|
|
||||||
@ -100,11 +105,13 @@ function cfxOwnedZones.drawZoneInMap(aZone)
|
|||||||
if aZone.markID then
|
if aZone.markID then
|
||||||
trigger.action.removeMark(aZone.markID)
|
trigger.action.removeMark(aZone.markID)
|
||||||
end
|
end
|
||||||
if aZone.hidden then return end
|
if aZone.titleID then
|
||||||
|
trigger.action.removeMark(aZone.titleID)
|
||||||
|
end
|
||||||
|
|
||||||
local lineColor = aZone.redLine -- {1.0, 0, 0, 1.0} -- red
|
local lineColor = aZone.redLine -- {1.0, 0, 0, 1.0} -- red
|
||||||
local fillColor = aZone.redFill -- {1.0, 0, 0, 0.2} -- red
|
local fillColor = aZone.redFill -- {1.0, 0, 0, 0.2} -- red
|
||||||
local owner = aZone.owner -- cfxOwnedZones.getOwnerForZone(aZone)
|
local owner = aZone.owner
|
||||||
if owner == 2 then
|
if owner == 2 then
|
||||||
lineColor = aZone.blueLine -- {0.0, 0, 1.0, 1.0}
|
lineColor = aZone.blueLine -- {0.0, 0, 1.0, 1.0}
|
||||||
fillColor = aZone.blueFill -- {0.0, 0, 1.0, 0.2}
|
fillColor = aZone.blueFill -- {0.0, 0, 1.0, 0.2}
|
||||||
@ -112,7 +119,12 @@ function cfxOwnedZones.drawZoneInMap(aZone)
|
|||||||
lineColor = aZone.neutralLine -- {0.8, 0.8, 0.8, 1.0}
|
lineColor = aZone.neutralLine -- {0.8, 0.8, 0.8, 1.0}
|
||||||
fillColor = aZone.neutralFill -- {0.8, 0.8, 0.8, 0.2}
|
fillColor = aZone.neutralFill -- {0.8, 0.8, 0.8, 0.2}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if aZone.title then
|
||||||
|
aZone.titleID = aZone:drawText(aZone.title, 18, lineColor, {0, 0, 0, 0})
|
||||||
|
end
|
||||||
|
|
||||||
|
if aZone.hidden then return end
|
||||||
aZone.markID = aZone:drawZone(lineColor, fillColor) -- markID
|
aZone.markID = aZone:drawZone(lineColor, fillColor) -- markID
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -154,6 +166,9 @@ function cfxOwnedZones.addOwnedZone(aZone)
|
|||||||
aZone.untargetable = aZone:getBoolFromZoneProperty("untargetable", false)
|
aZone.untargetable = aZone:getBoolFromZoneProperty("untargetable", false)
|
||||||
|
|
||||||
aZone.hidden = aZone:getBoolFromZoneProperty("hidden", false)
|
aZone.hidden = aZone:getBoolFromZoneProperty("hidden", false)
|
||||||
|
-- numCap, numKeep
|
||||||
|
aZone.numCap = aZone:getNumberFromZoneProperty("numCap", cfxOwnedZones.numCap)
|
||||||
|
aZone.numKeep = aZone:getNumberFromZoneProperty("numKeep", cfxOwnedZones.numKeep)
|
||||||
|
|
||||||
-- individual colors, else default from config
|
-- individual colors, else default from config
|
||||||
aZone.redLine = aZone:getRGBAVectorFromZoneProperty("redLine", cfxOwnedZones.redLine)
|
aZone.redLine = aZone:getRGBAVectorFromZoneProperty("redLine", cfxOwnedZones.redLine)
|
||||||
@ -163,6 +178,31 @@ function cfxOwnedZones.addOwnedZone(aZone)
|
|||||||
aZone.neutralLine = aZone:getRGBAVectorFromZoneProperty("neutralLine", cfxOwnedZones.neutralLine)
|
aZone.neutralLine = aZone:getRGBAVectorFromZoneProperty("neutralLine", cfxOwnedZones.neutralLine)
|
||||||
aZone.neutralFill = aZone:getRGBAVectorFromZoneProperty("neutralFill", cfxOwnedZones.neutralFill)
|
aZone.neutralFill = aZone:getRGBAVectorFromZoneProperty("neutralFill", cfxOwnedZones.neutralFill)
|
||||||
|
|
||||||
|
-- masterOwner
|
||||||
|
if aZone:hasProperty("masterOwner") then
|
||||||
|
local masterZone = aZone:getStringFromZoneProperty("masterOwner", "cfxNoneErr")
|
||||||
|
local theMaster = cfxZones.getZoneByName(masterZone)
|
||||||
|
if not theMaster then
|
||||||
|
trigger.action.outText("+++owdZ: WARNING: owned zone <" .. aZone.name .. ">'s masterOwner <" .. masterZone .. "> does not exist, not connecting!", 30)
|
||||||
|
else
|
||||||
|
aZone.masterOwner = theMaster
|
||||||
|
aZone.owner = theMaster.owner
|
||||||
|
if aZone.verbose or cfxOwnedZones.verbose then
|
||||||
|
trigger.action.outText("+++OwdZ: owned zone <" .. aZone.name .. "> inherits ownership from master zone <" .. masterZone .. ">", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
aZone.announcer = aZone:getBoolFromZoneProperty("announcer", cfxZones.announcer)
|
||||||
|
if aZone:hasProperty("announce") then
|
||||||
|
aZone.announcer = aZone:getBoolFromZoneProperty("announce", cfxZones.announcer)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- title
|
||||||
|
if aZone:hasProperty("title") then
|
||||||
|
aZone.title = aZone:getStringFromZoneProperty("title")
|
||||||
|
if aZone.title == "*" then aZone.title = aZone.name end
|
||||||
|
end
|
||||||
aZone.method = aZone:getStringFromZoneProperty("method", "inc")
|
aZone.method = aZone:getStringFromZoneProperty("method", "inc")
|
||||||
|
|
||||||
cfxOwnedZones.zones[aZone] = aZone
|
cfxOwnedZones.zones[aZone] = aZone
|
||||||
@ -178,23 +218,17 @@ end
|
|||||||
|
|
||||||
function cfxOwnedZones.bangNeutral(value)
|
function cfxOwnedZones.bangNeutral(value)
|
||||||
if not cfxOwnedZones.neutralTriggerFlag then return end
|
if not cfxOwnedZones.neutralTriggerFlag then return end
|
||||||
--local newVal = trigger.misc.getUserFlag(cfxOwnedZones.neutralTriggerFlag) + value
|
|
||||||
--trigger.action.setUserFlag(cfxOwnedZones.neutralTriggerFlag, newVal)
|
|
||||||
cfxZones.pollFlag(cfxOwnedZones.neutralTriggerFlag, cfxOwnedZones.method, cfxOwnedZones)
|
cfxZones.pollFlag(cfxOwnedZones.neutralTriggerFlag, cfxOwnedZones.method, cfxOwnedZones)
|
||||||
end
|
end
|
||||||
|
|
||||||
function cfxOwnedZones.bangRed(value, theZone)
|
function cfxOwnedZones.bangRed(value, theZone)
|
||||||
if not cfxOwnedZones.redTriggerFlag then return end
|
if not cfxOwnedZones.redTriggerFlag then return end
|
||||||
--local newVal = trigger.misc.getUserFlag(cfxOwnedZones.redTriggerFlag) + value
|
|
||||||
--trigger.action.setUserFlag(cfxOwnedZones.redTriggerFlag, newVal)
|
|
||||||
cfxZones.pollFlag(cfxOwnedZones.redTriggerFlag, cfxOwnedZones.method, cfxOwnedZones)
|
cfxZones.pollFlag(cfxOwnedZones.redTriggerFlag, cfxOwnedZones.method, cfxOwnedZones)
|
||||||
end
|
end
|
||||||
|
|
||||||
function cfxOwnedZones.bangBlue(value, theZone)
|
function cfxOwnedZones.bangBlue(value, theZone)
|
||||||
if not cfxOwnedZones.blueTriggerFlag then return end
|
if not cfxOwnedZones.blueTriggerFlag then return end
|
||||||
local newVal = trigger.misc.getUserFlag(cfxOwnedZones.blueTriggerFlag) + value
|
local newVal = trigger.misc.getUserFlag(cfxOwnedZones.blueTriggerFlag) + value
|
||||||
-- trigger.action.setUserFlag(cfxOwnedZones.blueTriggerFlag, newVal)
|
|
||||||
-- cfxZones.setFlagValue(cfxOwnedZones.blueTriggerFlag, newVal, cfxOwnedZones)
|
|
||||||
cfxZones.pollFlag(cfxOwnedZones.blueTriggerFlag, cfxOwnedZones.method, cfxOwnedZones)
|
cfxZones.pollFlag(cfxOwnedZones.blueTriggerFlag, cfxOwnedZones.method, cfxOwnedZones)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -214,14 +248,15 @@ function cfxOwnedZones.zoneConquered(aZone, theSide, formerOwner) -- 0 = neutral
|
|||||||
local who = "REDFORCE"
|
local who = "REDFORCE"
|
||||||
if theSide == 2 then who = "BLUEFORCE"
|
if theSide == 2 then who = "BLUEFORCE"
|
||||||
elseif theSide == 0 then who = "NEUTRAL" end
|
elseif theSide == 0 then who = "NEUTRAL" end
|
||||||
|
aZone.owner = theSide -- just to be sure
|
||||||
|
|
||||||
if cfxOwnedZones.announcer then
|
if aZone.announcer then
|
||||||
if theSide == 0 then
|
if theSide == 0 then
|
||||||
trigger.action.outText(aZone.name .. " has become NEUTRAL", 30)
|
trigger.action.outText(aZone.name .. " has become NEUTRAL", 30)
|
||||||
else
|
else
|
||||||
trigger.action.outText(who .. " have secured zone " .. aZone.name, 30)
|
trigger.action.outText(who .. " have secured zone " .. aZone.name, 30)
|
||||||
end
|
end
|
||||||
aZone.owner = theSide -- just to be sure
|
|
||||||
-- play different sounds depending on who's won
|
-- play different sounds depending on who's won
|
||||||
if theSide == 1 then
|
if theSide == 1 then
|
||||||
trigger.action.outSoundForCoalition(1, cfxOwnedZones.winSound)
|
trigger.action.outSoundForCoalition(1, cfxOwnedZones.winSound)
|
||||||
@ -307,47 +342,38 @@ function cfxOwnedZones.update()
|
|||||||
if cfxOwnedZones.fixWingCap then
|
if cfxOwnedZones.fixWingCap then
|
||||||
allBlue = dcsCommon.combineTables(allBlue, coalition.getGroups(2, Group.Category.AIRPLANE))
|
allBlue = dcsCommon.combineTables(allBlue, coalition.getGroups(2, Group.Category.AIRPLANE))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- WARNING: we only proc ownedZones, NOT airfield nor FARP or other
|
||||||
for idz, theZone in pairs(cfxOwnedZones.zones) do
|
for idz, theZone in pairs(cfxOwnedZones.zones) do
|
||||||
theZone.numRed = 0
|
theZone.numRed = 0
|
||||||
theZone.numBlue = 0
|
theZone.numBlue = 0
|
||||||
|
local lastOwner = theZone.owner
|
||||||
|
if not lastOwner then
|
||||||
|
trigger.action.outText("+++owdZ: WARNING - zone <" .. theZone.name .. "> has NIL owner", 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if theZone.verbose then
|
||||||
|
trigger.action.outText("Zone <" .. theZone.name .. "> lastOwner is <" .. lastOwner .. ">", 30)
|
||||||
|
end
|
||||||
|
local newOwner = 0 -- neutral is default
|
||||||
-- count red units in zone
|
-- count red units in zone
|
||||||
for idx, aGroup in pairs(allRed) do
|
if not theZone.masterOwner then
|
||||||
if Group.isExist(aGroup) then
|
for idx, aGroup in pairs(allRed) do
|
||||||
if cfxOwnedZones.fastEval then
|
if Group.isExist(aGroup) then
|
||||||
-- we only check first unit that is alive
|
if cfxOwnedZones.fastEval then
|
||||||
local theUnit = dcsCommon.getGroupUnit(aGroup)
|
-- we only check first unit that is alive
|
||||||
if theUnit and (not theUnit:inAir()) and theZone:unitInZone(theUnit) then
|
local theUnit = dcsCommon.getGroupUnit(aGroup)
|
||||||
if cfxOwnedZones.excludedTypes then
|
if theUnit and (not theUnit:inAir()) and theZone:unitInZone(theUnit) then
|
||||||
-- special carve-out for exclduding some
|
|
||||||
-- unit types to prevent them from capping
|
|
||||||
local uType = theUnit:getTypeName()
|
|
||||||
local forbidden = false
|
|
||||||
for idx, aType in pairs(cfxOwnedZones.excludedTypes) do
|
|
||||||
if uType == aType then
|
|
||||||
forbidden = true
|
|
||||||
else
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if not forbidden then
|
|
||||||
theZone.numRed = theZone.numRed + aGroup:getSize()
|
|
||||||
end
|
|
||||||
else
|
|
||||||
theZone.numRed = theZone.numRed + aGroup:getSize()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else -- full eval
|
|
||||||
local allUnits = aGroup:getUnits()
|
|
||||||
for idy, theUnit in pairs(allUnits) do
|
|
||||||
if (not theUnit:inAir()) and theZone:unitInZone(theUnit) then
|
|
||||||
-- theZone.numRed = theZone.numRed + 1
|
|
||||||
if cfxOwnedZones.excludedTypes then
|
if cfxOwnedZones.excludedTypes then
|
||||||
-- special carve-out for exclduding some
|
-- special carve-out for exclduding some
|
||||||
-- unit types to prevent them from capping
|
-- unit types to prevent them from capping
|
||||||
local uType = theUnit:getTypeName()
|
local uType = theUnit:getTypeName()
|
||||||
local forbidden = false
|
local forbidden = false
|
||||||
for idx, aType in pairs(cfxOwnedZones.excludedTypes) do
|
for idx, aType in pairs(cfxOwnedZones.excludedTypes) do
|
||||||
if uType == aType then forbidden = true end
|
if uType == aType then
|
||||||
|
forbidden = true
|
||||||
|
else
|
||||||
|
end
|
||||||
end
|
end
|
||||||
if not forbidden then
|
if not forbidden then
|
||||||
theZone.numRed = theZone.numRed + aGroup:getSize()
|
theZone.numRed = theZone.numRed + aGroup:getSize()
|
||||||
@ -356,48 +382,47 @@ function cfxOwnedZones.update()
|
|||||||
theZone.numRed = theZone.numRed + aGroup:getSize()
|
theZone.numRed = theZone.numRed + aGroup:getSize()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
else -- full eval
|
||||||
|
local allUnits = aGroup:getUnits()
|
||||||
|
for idy, theUnit in pairs(allUnits) do
|
||||||
|
if (not theUnit:inAir()) and theZone:unitInZone(theUnit) then
|
||||||
|
if cfxOwnedZones.excludedTypes then
|
||||||
|
-- special carve-out for exclduding some
|
||||||
|
-- unit types to prevent them from capping
|
||||||
|
local uType = theUnit:getTypeName()
|
||||||
|
local forbidden = false
|
||||||
|
for idx, aType in pairs(cfxOwnedZones.excludedTypes) do
|
||||||
|
if uType == aType then forbidden = true end
|
||||||
|
end
|
||||||
|
if not forbidden then
|
||||||
|
theZone.numRed = theZone.numRed + aGroup:getSize()
|
||||||
|
end
|
||||||
|
else
|
||||||
|
theZone.numRed = theZone.numRed + aGroup:getSize()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
-- count blue units
|
-- count blue units
|
||||||
for idx, aGroup in pairs(allBlue) do
|
for idx, aGroup in pairs(allBlue) do
|
||||||
if Group.isExist(aGroup) then
|
if Group.isExist(aGroup) then
|
||||||
if cfxOwnedZones.fastEval then
|
if cfxOwnedZones.fastEval then
|
||||||
-- we only check first unit that is alive
|
-- we only check first unit that is alive
|
||||||
local theUnit = dcsCommon.getGroupUnit(aGroup)
|
local theUnit = dcsCommon.getGroupUnit(aGroup)
|
||||||
if theUnit and (not theUnit:inAir()) and theZone:unitInZone(theUnit) then
|
if theUnit and (not theUnit:inAir()) and theZone:unitInZone(theUnit) then
|
||||||
if cfxOwnedZones.excludedTypes then
|
|
||||||
-- special carve-out for exclduding some
|
|
||||||
-- unit types to prevent them from capping
|
|
||||||
local uType = theUnit:getTypeName()
|
|
||||||
local forbidden = false
|
|
||||||
for idx, aType in pairs(cfxOwnedZones.excludedTypes) do
|
|
||||||
if uType == aType then
|
|
||||||
forbidden = true
|
|
||||||
else
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if not forbidden then
|
|
||||||
theZone.numBlue = theZone.numBlue + aGroup:getSize()
|
|
||||||
end
|
|
||||||
else
|
|
||||||
theZone.numBlue = theZone.numBlue + aGroup:getSize()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local allUnits = aGroup:getUnits()
|
|
||||||
for idy, theUnit in pairs(allUnits) do
|
|
||||||
if (not theUnit:inAir()) and theZone:unitInZone(theUnit) then
|
|
||||||
-- theZone.numBlue = theZone.numBlue + 1
|
|
||||||
if cfxOwnedZones.excludedTypes then
|
if cfxOwnedZones.excludedTypes then
|
||||||
-- special carve-out for exclduding some
|
-- special carve-out for exclduding some
|
||||||
-- unit types to prevent them from capping
|
-- unit types to prevent them from capping
|
||||||
local uType = theUnit:getTypeName()
|
local uType = theUnit:getTypeName()
|
||||||
local forbidden = false
|
local forbidden = false
|
||||||
for idx, aType in pairs(cfxOwnedZones.excludedTypes) do
|
for idx, aType in pairs(cfxOwnedZones.excludedTypes) do
|
||||||
if uType == aType then forbidden = true end
|
if uType == aType then
|
||||||
|
forbidden = true
|
||||||
|
else
|
||||||
|
end
|
||||||
end
|
end
|
||||||
if not forbidden then
|
if not forbidden then
|
||||||
theZone.numBlue = theZone.numBlue + aGroup:getSize()
|
theZone.numBlue = theZone.numBlue + aGroup:getSize()
|
||||||
@ -406,28 +431,50 @@ function cfxOwnedZones.update()
|
|||||||
theZone.numBlue = theZone.numBlue + aGroup:getSize()
|
theZone.numBlue = theZone.numBlue + aGroup:getSize()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
local allUnits = aGroup:getUnits()
|
||||||
|
for idy, theUnit in pairs(allUnits) do
|
||||||
|
if (not theUnit:inAir()) and theZone:unitInZone(theUnit) then
|
||||||
|
if cfxOwnedZones.excludedTypes then
|
||||||
|
-- special carve-out for exclduding some
|
||||||
|
-- unit types to prevent them from capping
|
||||||
|
local uType = theUnit:getTypeName()
|
||||||
|
local forbidden = false
|
||||||
|
for idx, aType in pairs(cfxOwnedZones.excludedTypes) do
|
||||||
|
if uType == aType then forbidden = true end
|
||||||
|
end
|
||||||
|
if not forbidden then
|
||||||
|
theZone.numBlue = theZone.numBlue + aGroup:getSize()
|
||||||
|
end
|
||||||
|
else
|
||||||
|
theZone.numBlue = theZone.numBlue + aGroup:getSize()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
if theZone.verbose then
|
||||||
|
trigger.action.outText("+++owdZ: zone <" .. theZone.name .. ">: red inside: <" .. theZone.numRed .. ">, blue inside: <>" .. theZone.numBlue, 30)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- zone has master owner, no counting done
|
||||||
|
end
|
||||||
|
|
||||||
if theZone.verbose then
|
|
||||||
trigger.action.outText("+++owdZ: zone <" .. theZone.name .. ">: red inside: <" .. theZone.numRed .. ">, blue inside: <>" .. theZone.numBlue, 30)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- trigger.action.outText(theZone.name .. " blue: " .. theZone.numBlue .. " red " .. theZone.numRed, 30)
|
|
||||||
local lastOwner = theZone.owner
|
|
||||||
local newOwner = 0 -- neutral is default
|
|
||||||
if theZone.unbeatable then -- Parker Lewis can't lose. Neither this zone.
|
if theZone.unbeatable then -- Parker Lewis can't lose. Neither this zone.
|
||||||
newOwner = lastOwner
|
newOwner = lastOwner
|
||||||
end
|
end
|
||||||
|
|
||||||
-- determine new owner
|
-- determine new owner
|
||||||
if theZone.unbeatable then
|
if theZone.unbeatable then
|
||||||
-- we do nothing
|
-- we do nothing
|
||||||
|
elseif theZone.masterOwner then
|
||||||
|
-- inherit from my master
|
||||||
|
newOwner = theZone.masterOwner.owner
|
||||||
elseif theZone.numRed < 1 and theZone.numBlue < 1 then
|
elseif theZone.numRed < 1 and theZone.numBlue < 1 then
|
||||||
-- no troops here. Become neutral?
|
-- no troops here. Become neutral?
|
||||||
if cfxOwnedZones.numKeep < 1 then
|
if theZone.numKeep < 1 then
|
||||||
newOwner = lastOwner -- keep it, else turns neutral
|
newOwner = lastOwner -- keep it, else turns neutral
|
||||||
else
|
else
|
||||||
-- noone here, zone becomes neutral
|
-- noone here, zone becomes neutral
|
||||||
@ -435,9 +482,9 @@ function cfxOwnedZones.update()
|
|||||||
end
|
end
|
||||||
elseif theZone.numRed < 1 then
|
elseif theZone.numRed < 1 then
|
||||||
-- only blue here. enough to keep?
|
-- only blue here. enough to keep?
|
||||||
if theZone.numBlue >= cfxOwnedZones.numCap then
|
if theZone.numBlue >= theZone.numCap then
|
||||||
newOwner = 2 -- blue owns it
|
newOwner = 2 -- blue owns it
|
||||||
elseif lastOwner == 2 and theZone.numBlue >= cfxOwnedZones.numKeep then
|
elseif lastOwner == 2 and theZone.numBlue >= theZone.numKeep then
|
||||||
-- enough to keep if owned before
|
-- enough to keep if owned before
|
||||||
newOwner = 2
|
newOwner = 2
|
||||||
else
|
else
|
||||||
@ -445,9 +492,9 @@ function cfxOwnedZones.update()
|
|||||||
end
|
end
|
||||||
elseif theZone.numBlue < 1 then
|
elseif theZone.numBlue < 1 then
|
||||||
-- only red here. enough to keep?
|
-- only red here. enough to keep?
|
||||||
if theZone.numRed >= cfxOwnedZones.numCap then
|
if theZone.numRed >= theZone.numCap then
|
||||||
newOwner = 1
|
newOwner = 1
|
||||||
elseif lastOwner == 1 and theZone.numRed >= cfxOwnedZones.numKeep then
|
elseif lastOwner == 1 and theZone.numRed >= theZone.numKeep then
|
||||||
newOwner = 1
|
newOwner = 1
|
||||||
else
|
else
|
||||||
newOwner = 0
|
newOwner = 0
|
||||||
@ -459,18 +506,18 @@ function cfxOwnedZones.update()
|
|||||||
if cfxOwnedZones.easyContest then
|
if cfxOwnedZones.easyContest then
|
||||||
-- this zone is immediately contested
|
-- this zone is immediately contested
|
||||||
newOwner = 0 -- just to be explicit
|
newOwner = 0 -- just to be explicit
|
||||||
elseif cfxOwnedZones.numKeep < 1 then
|
elseif theZone.numKeep < 1 then
|
||||||
-- old owner keeps it until none left
|
-- old owner keeps it until none left
|
||||||
newOwner = lastOwner
|
newOwner = lastOwner
|
||||||
else
|
else
|
||||||
if lastOwner == 1 then
|
if lastOwner == 1 then
|
||||||
-- red can keep it as long as enough units here
|
-- red can keep it as long as enough units here
|
||||||
if theZone.numRed >= cfxOwnedZones.numKeep then
|
if theZone.numRed >= theZone.numKeep then
|
||||||
newOwner = 1
|
newOwner = 1
|
||||||
end -- else 0
|
end -- else 0
|
||||||
elseif lastOwner == 2 then
|
elseif lastOwner == 2 then
|
||||||
-- blue can keep it if enough units here
|
-- blue can keep it if enough units here
|
||||||
if theZone.numBlue >= cfxOwnedZones.numKeep then
|
if theZone.numBlue >= theZone.numKeep then
|
||||||
newOwner = 2
|
newOwner = 2
|
||||||
end -- else 0
|
end -- else 0
|
||||||
else -- stay 0
|
else -- stay 0
|
||||||
@ -525,14 +572,14 @@ function cfxOwnedZones.update()
|
|||||||
|
|
||||||
-- see if one side owns all and bang the flags if requiredLibs
|
-- see if one side owns all and bang the flags if requiredLibs
|
||||||
if cfxOwnedZones.allBlue and not cfxOwnedZones.hasAllBlue then
|
if cfxOwnedZones.allBlue and not cfxOwnedZones.hasAllBlue then
|
||||||
if cfxOwnedZones.sideOwnsAll(2) then
|
if cfxOwnedZones.sideOwnsAll(2) then -- ignores other owner-managed zones
|
||||||
cfxZones.pollFlag(cfxOwnedZones.allBlue, cfxOwnedZones.method, cfxOwnedZones)
|
cfxZones.pollFlag(cfxOwnedZones.allBlue, cfxOwnedZones.method, cfxOwnedZones)
|
||||||
cfxOwnedZones.hasAllBlue = true
|
cfxOwnedZones.hasAllBlue = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxOwnedZones.allRed and not cfxOwnedZones.hasAllRed then
|
if cfxOwnedZones.allRed and not cfxOwnedZones.hasAllRed then
|
||||||
if cfxOwnedZones.sideOwnsAll(1) then
|
if cfxOwnedZones.sideOwnsAll(1) then -- ignores other managed owner zones
|
||||||
cfxZones.pollFlag(cfxOwnedZones.allRed, cfxOwnedZones.method, cfxOwnedZones)
|
cfxZones.pollFlag(cfxOwnedZones.allRed, cfxOwnedZones.method, cfxOwnedZones)
|
||||||
cfxOwnedZones.hasAllRed = true
|
cfxOwnedZones.hasAllRed = true
|
||||||
end
|
end
|
||||||
@ -540,8 +587,10 @@ function cfxOwnedZones.update()
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function cfxOwnedZones.sideOwnsAll(theSide)
|
function cfxOwnedZones.sideOwnsAll(theSide, useAllManaged)
|
||||||
for key, aZone in pairs(cfxOwnedZones.zones) do
|
local themAll = cfxOwnedZones.zones
|
||||||
|
if useAllManaged then themAll = cfxZones.allManagedOwnedZones end
|
||||||
|
for key, aZone in pairs(themAll) do
|
||||||
if aZone.owner ~= theSide then
|
if aZone.owner ~= theSide then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
@ -550,27 +599,44 @@ function cfxOwnedZones.sideOwnsAll(theSide)
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function cfxOwnedZones.hasOwnedZones()
|
|
||||||
for idx, zone in pairs (cfxOwnedZones.zones) do
|
|
||||||
return true -- even the first returns true
|
|
||||||
end
|
|
||||||
-- no owned zones
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- getting closest owned zones etc
|
-- getting closest owned zones etc
|
||||||
-- required for groundTroops and factory attackers
|
-- required for groundTroops and factory attackers
|
||||||
-- methods provided only for other modules (e.g. cfxGroundTroops or
|
-- methods provided only for other modules (e.g. cfxGroundTroops or
|
||||||
-- factoryZone
|
-- factoryZone
|
||||||
--
|
--
|
||||||
|
|
||||||
|
function cfxOwnedZones.gatherAllManagedOwnedZones()
|
||||||
|
-- we collect all zones with 'owner'
|
||||||
|
local all = {}
|
||||||
|
local pZones = cfxZones.zonesWithProperty("owner")
|
||||||
|
for k, theZone in pairs(pZones) do
|
||||||
|
all[theZone.name] = theZone
|
||||||
|
end
|
||||||
|
|
||||||
|
-- and add all zones with airfield
|
||||||
|
local pZones = cfxZones.zonesWithProperty("airfield")
|
||||||
|
for k, theZone in pairs(pZones) do
|
||||||
|
all[theZone.name] = theZone
|
||||||
|
end
|
||||||
|
-- and all zones with 'FARP'
|
||||||
|
local pZones = cfxZones.zonesWithProperty("FARP")
|
||||||
|
for k, theZone in pairs(pZones) do
|
||||||
|
all[theZone.name] = theZone
|
||||||
|
end
|
||||||
|
|
||||||
|
-- and all with ownAll?
|
||||||
|
-- not yet
|
||||||
|
cfxOwnedZones.allManagedOwnedZones = all
|
||||||
|
end
|
||||||
|
|
||||||
-- collect zones can filter owned zones.
|
-- collect zones can filter owned zones.
|
||||||
-- by default it filters all zones that are in water
|
-- by default it filters all zones that are in water
|
||||||
|
-- includes all managed-owner zones
|
||||||
function cfxOwnedZones.collectZones(mode)
|
function cfxOwnedZones.collectZones(mode)
|
||||||
if not mode then mode = "land" end
|
if not mode then mode = "land" end
|
||||||
if mode == "land" then
|
if mode == "land" then
|
||||||
local landZones = {}
|
local landZones = {}
|
||||||
for idx, theZone in pairs(cfxOwnedZones.zones) do
|
for idx, theZone in pairs(cfxOwnedZones.allManagedOwnedZones) do
|
||||||
p = theZone:getPoint()
|
p = theZone:getPoint()
|
||||||
p.y = p.z
|
p.y = p.z
|
||||||
local surfType = land.getSurfaceType(p)
|
local surfType = land.getSurfaceType(p)
|
||||||
@ -580,64 +646,12 @@ function cfxOwnedZones.collectZones(mode)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
return landZones
|
return landZones
|
||||||
|
else
|
||||||
|
return cfxOwnedZones.allManagedOwnedZones
|
||||||
end
|
end
|
||||||
|
end
|
||||||
-- return all zones
|
|
||||||
return cfxOwnedZones.zones
|
|
||||||
--if not mode then mode = "OWNED" end
|
|
||||||
-- Note: since cfxGroundTroops currently simply uses owner flag
|
|
||||||
-- we cannot migrate to a differentiation between factory and
|
|
||||||
-- owned. All produced attackers always attack owned zones.
|
|
||||||
end
|
|
||||||
|
|
||||||
function cfxOwnedZones.getEnemyZonesFor(aCoalition)
|
|
||||||
local enemyZones = {}
|
|
||||||
local allZones = cfxOwnedZones.collectZones()
|
|
||||||
local ourEnemy = dcsCommon.getEnemyCoalitionFor(aCoalition)
|
|
||||||
for zKey, aZone in pairs(allZones) do
|
|
||||||
if aZone.owner == ourEnemy then -- only check enemy owned zones
|
|
||||||
-- note: will include untargetable zones
|
|
||||||
table.insert(enemyZones, aZone)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return enemyZones
|
|
||||||
end
|
|
||||||
|
|
||||||
function cfxOwnedZones.getNearestOwnedZoneToPoint(aPoint)
|
|
||||||
local shortestDist = math.huge
|
|
||||||
local closestZone = nil
|
|
||||||
local allZones = cfxOwnedZones.collectZones()
|
|
||||||
|
|
||||||
for zKey, aZone in pairs(allZones) do
|
|
||||||
local zPoint = aZone:getPoint()
|
|
||||||
currDist = dcsCommon.dist(zPoint, aPoint)
|
|
||||||
if aZone.untargetable ~= true and
|
|
||||||
currDist < shortestDist then
|
|
||||||
shortestDist = currDist
|
|
||||||
closestZone = aZone
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return closestZone, shortestDist
|
|
||||||
end
|
|
||||||
|
|
||||||
function cfxOwnedZones.getNearestOwnedZone(theZone)
|
|
||||||
local shortestDist = math.huge
|
|
||||||
local closestZone = nil
|
|
||||||
local aPoint = theZone:getPoint()
|
|
||||||
local allZones = cfxOwnedZones.collectZones()
|
|
||||||
for zKey, aZone in pairs(allZones) do
|
|
||||||
local zPoint = aZone:getPoint()
|
|
||||||
currDist = dcsCommon.dist(zPoint, aPoint)
|
|
||||||
if aZone.untargetable ~= true and currDist < shortestDist then
|
|
||||||
shortestDist = currDist
|
|
||||||
closestZone = aZone
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return closestZone, shortestDist
|
|
||||||
end
|
|
||||||
|
|
||||||
|
-- getNearestEnemyOwnedZone invoked by cfxGroundTroops
|
||||||
function cfxOwnedZones.getNearestEnemyOwnedZone(theZone, targetNeutral)
|
function cfxOwnedZones.getNearestEnemyOwnedZone(theZone, targetNeutral)
|
||||||
if not targetNeutral then targetNeutral = false else targetNeutral = true end
|
if not targetNeutral then targetNeutral = false else targetNeutral = true end
|
||||||
local shortestDist = math.huge
|
local shortestDist = math.huge
|
||||||
@ -650,56 +664,20 @@ function cfxOwnedZones.getNearestEnemyOwnedZone(theZone, targetNeutral)
|
|||||||
for zKey, aZone in pairs(allZones) do
|
for zKey, aZone in pairs(allZones) do
|
||||||
if targetNeutral then
|
if targetNeutral then
|
||||||
-- return all zones that do not belong to us
|
-- return all zones that do not belong to us
|
||||||
if aZone.owner ~= theZone.owner then
|
if aZone.owner ~= theZone.owner and not aZone.untargetable then
|
||||||
local aPoint = aZone:getPoint()
|
local aPoint = aZone:getPoint()
|
||||||
currDist = dcsCommon.dist(aPoint, zPoint)
|
currDist = dcsCommon.dist(aPoint, zPoint)
|
||||||
if aZone.untargetable ~= true and currDist < shortestDist then
|
if currDist < shortestDist then
|
||||||
shortestDist = currDist
|
shortestDist = currDist
|
||||||
closestZone = aZone
|
closestZone = aZone
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- return zones that are taken by the Enenmy
|
-- return zones that are taken by the Enenmy
|
||||||
if aZone.owner == ourEnemy then -- only check own zones
|
if aZone.owner == ourEnemy and not aZone.untargetable then -- only check own zones
|
||||||
local aPoint = aZone:getPoint()
|
local aPoint = aZone:getPoint()
|
||||||
currDist = dcsCommon.dist(zPoint, aPoint)
|
currDist = dcsCommon.dist(zPoint, aPoint)
|
||||||
if aZone.untargetable ~= true and currDist < shortestDist then
|
if currDist < shortestDist then
|
||||||
shortestDist = currDist
|
|
||||||
closestZone = aZone
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return closestZone, shortestDist
|
|
||||||
end
|
|
||||||
|
|
||||||
function cfxOwnedZones.getNearestFriendlyZone(theZone, targetNeutral)
|
|
||||||
if not targetNeutral then targetNeutral = false else targetNeutral = true end
|
|
||||||
local shortestDist = math.huge
|
|
||||||
local closestZone = nil
|
|
||||||
local ourEnemy = dcsCommon.getEnemyCoalitionFor(theZone.owner)
|
|
||||||
if not ourEnemy then return nil end -- we called for a neutral zone. they have no enemies nor friends, all zones would be legal.
|
|
||||||
local zPoint = theZone:getPoint()
|
|
||||||
local allZones = cfxOwnedZones.collectZones()
|
|
||||||
|
|
||||||
for zKey, aZone in pairs(allZones) do
|
|
||||||
if targetNeutral then
|
|
||||||
-- target all zones that do not belong to the enemy
|
|
||||||
if aZone.owner ~= ourEnemy then
|
|
||||||
local aPoint = aZone:getPoint()
|
|
||||||
currDist = dcsCommon.dist(zPoint, aPoint)
|
|
||||||
if aZone.untargetable ~= true and currDist < shortestDist then
|
|
||||||
shortestDist = currDist
|
|
||||||
closestZone = aZone
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- only target zones that are taken by us
|
|
||||||
if aZone.owner == theZone.owner then -- only check own zones
|
|
||||||
local aPoint = aZone:getPoint()
|
|
||||||
currDist = dcsCommon.dist(zPoint, aPoint)
|
|
||||||
if aZone.untargetable ~= true and currDist < shortestDist then
|
|
||||||
shortestDist = currDist
|
shortestDist = currDist
|
||||||
closestZone = aZone
|
closestZone = aZone
|
||||||
end
|
end
|
||||||
@ -710,11 +688,13 @@ function cfxOwnedZones.getNearestFriendlyZone(theZone, targetNeutral)
|
|||||||
return closestZone, shortestDist
|
return closestZone, shortestDist
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- invoked by factory
|
||||||
function cfxOwnedZones.enemiesRemaining(aZone)
|
function cfxOwnedZones.enemiesRemaining(aZone)
|
||||||
if cfxOwnedZones.getNearestEnemyOwnedZone(aZone) then return true end
|
if cfxOwnedZones.getNearestEnemyOwnedZone(aZone) then return true end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- load / save data
|
-- load / save data
|
||||||
--
|
--
|
||||||
@ -796,6 +776,9 @@ function cfxOwnedZones.readConfigZone(theZone)
|
|||||||
cfxOwnedZones.name = "cfxOwnedZones" -- just in case, so we can access with cfxZones
|
cfxOwnedZones.name = "cfxOwnedZones" -- just in case, so we can access with cfxZones
|
||||||
cfxOwnedZones.verbose = theZone.verbose -- cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
cfxOwnedZones.verbose = theZone.verbose -- cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
||||||
cfxOwnedZones.announcer = theZone:getBoolFromZoneProperty("announcer", true)
|
cfxOwnedZones.announcer = theZone:getBoolFromZoneProperty("announcer", true)
|
||||||
|
if theZone:hasProperty("announce") then
|
||||||
|
cfxZones.announcer = theZone:getBoolFromZoneProperty("announce", true)
|
||||||
|
end
|
||||||
|
|
||||||
if theZone:hasProperty("r!") then
|
if theZone:hasProperty("r!") then
|
||||||
cfxOwnedZones.redTriggerFlag = theZone:getStringFromZoneProperty("r!", "*<cfxnone>")
|
cfxOwnedZones.redTriggerFlag = theZone:getStringFromZoneProperty("r!", "*<cfxnone>")
|
||||||
@ -892,6 +875,9 @@ function cfxOwnedZones.init()
|
|||||||
cfxOwnedZones.addOwnedZone(aZone)
|
cfxOwnedZones.addOwnedZone(aZone)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- gather ALL managed owner zones
|
||||||
|
cfxOwnedZones.gatherAllManagedOwnedZones()
|
||||||
|
|
||||||
if persistence then
|
if persistence then
|
||||||
-- sign up for persistence
|
-- sign up for persistence
|
||||||
callbacks = {}
|
callbacks = {}
|
||||||
@ -917,6 +903,11 @@ end
|
|||||||
masterOwner input for zones, overrides all else when not neutral
|
masterOwner input for zones, overrides all else when not neutral
|
||||||
|
|
||||||
dont count zones that cant be conquered for allBlue/allRed
|
dont count zones that cant be conquered for allBlue/allRed
|
||||||
|
|
||||||
|
noRed, noBlue options to prevent a zone to become that color
|
||||||
|
|
||||||
|
black color for dead. dead status to be defined. dead can't be capped and do not attact
|
||||||
|
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
cfxPlayerScore = {}
|
cfxPlayerScore = {}
|
||||||
cfxPlayerScore.version = "3.1.0"
|
cfxPlayerScore.version = "3.2.0"
|
||||||
cfxPlayerScore.name = "cfxPlayerScore" -- compatibility with flag bangers
|
cfxPlayerScore.name = "cfxPlayerScore" -- compatibility with flag bangers
|
||||||
cfxPlayerScore.badSound = "Death BRASS.wav"
|
cfxPlayerScore.badSound = "Death BRASS.wav"
|
||||||
cfxPlayerScore.scoreSound = "Quest Snare 3.wav"
|
cfxPlayerScore.scoreSound = "Quest Snare 3.wav"
|
||||||
@ -14,7 +14,7 @@ cfxPlayerScore.firstSave = true -- to force overwrite
|
|||||||
3.0.1 - cleanup
|
3.0.1 - cleanup
|
||||||
3.0.2 - interface with ObjectDestructDetector for scoring scenery objects
|
3.0.2 - interface with ObjectDestructDetector for scoring scenery objects
|
||||||
3.1.0 - shared data for persistence
|
3.1.0 - shared data for persistence
|
||||||
|
3.2.0 - integration with bank
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
cfxPlayerScore.requiredLibs = {
|
cfxPlayerScore.requiredLibs = {
|
||||||
@ -333,10 +333,16 @@ function cfxPlayerScore.updateScoreForPlayerImmediate(playerName, score)
|
|||||||
-- only on positive score
|
-- only on positive score
|
||||||
if (score > 0) and pFaction > 0 then
|
if (score > 0) and pFaction > 0 then
|
||||||
cfxPlayerScore.coalitionScore[pFaction] = cfxPlayerScore.coalitionScore[pFaction] + score
|
cfxPlayerScore.coalitionScore[pFaction] = cfxPlayerScore.coalitionScore[pFaction] + score
|
||||||
|
if bank and bank.addFunds then
|
||||||
|
bank.addFunds(pFaction, cfxPlayerScore.score2finance * score)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if pFaction > 0 then
|
if pFaction > 0 then
|
||||||
cfxPlayerScore.coalitionScore[pFaction] = cfxPlayerScore.coalitionScore[pFaction] + score
|
cfxPlayerScore.coalitionScore[pFaction] = cfxPlayerScore.coalitionScore[pFaction] + score
|
||||||
|
if bank and bank.addFunds then
|
||||||
|
bank.addFunds(pFaction, cfxPlayerScore.score2finance * score)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return thePlayerScore.score
|
return thePlayerScore.score
|
||||||
@ -1015,6 +1021,9 @@ function cfxPlayerScore.scheduledAward(args)
|
|||||||
theScore.score = theScore.score + theScore.scoreaccu
|
theScore.score = theScore.score + theScore.scoreaccu
|
||||||
desc = desc .. " score: " .. theScore.scoreaccu .. " for a new total of " .. theScore.score .. "\n"
|
desc = desc .. " score: " .. theScore.scoreaccu .. " for a new total of " .. theScore.score .. "\n"
|
||||||
cfxPlayerScore.coalitionScore[playerSide] = cfxPlayerScore.coalitionScore[playerSide] + theScore.scoreaccu
|
cfxPlayerScore.coalitionScore[playerSide] = cfxPlayerScore.coalitionScore[playerSide] + theScore.scoreaccu
|
||||||
|
if bank and bank.addFunds then
|
||||||
|
bank.addFunds(playerSide, cfxPlayerScore.score2finance * theScore.scoreaccu)
|
||||||
|
end
|
||||||
theScore.scoreaccu = 0
|
theScore.scoreaccu = 0
|
||||||
hasAward = true
|
hasAward = true
|
||||||
end
|
end
|
||||||
@ -1202,6 +1211,8 @@ function cfxPlayerScore.readConfigZone(theZone)
|
|||||||
if theZone:hasProperty("sharedData") then
|
if theZone:hasProperty("sharedData") then
|
||||||
cfxPlayerScore.sharedData = theZone:getStringFromZoneProperty("sharedData", "cfxNameMissing")
|
cfxPlayerScore.sharedData = theZone:getStringFromZoneProperty("sharedData", "cfxNameMissing")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
cfxPlayerScore.score2finance = theZone:getNumberFromZoneProperty("score2finance", 1) -- factor to convert points to bank finance
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
@ -1349,6 +1360,10 @@ function cfxPlayerScore.update()
|
|||||||
-- score!
|
-- score!
|
||||||
cfxPlayerScore.coalitionScore[coa] = cfxPlayerScore.coalitionScore[coa] + cfxPlayerScore.blueTriggerScore[tName]
|
cfxPlayerScore.coalitionScore[coa] = cfxPlayerScore.coalitionScore[coa] + cfxPlayerScore.blueTriggerScore[tName]
|
||||||
cfxPlayerScore.blueTriggerFlags[tName] = newVal
|
cfxPlayerScore.blueTriggerFlags[tName] = newVal
|
||||||
|
-- bank it if exists
|
||||||
|
if bank and bank.addFunds then
|
||||||
|
bank.addFunds(coa, cfxPlayerScore.score2finance * cfxPlayerScore.blueTriggerScore[tName])
|
||||||
|
end
|
||||||
if cfxPlayerScore.announcer then
|
if cfxPlayerScore.announcer then
|
||||||
trigger.action.outTextForCoalition(coa, "BLUE goal [" .. tName .. "] achieved, new BLUE coalition score is " .. cfxPlayerScore.coalitionScore[coa], 30)
|
trigger.action.outTextForCoalition(coa, "BLUE goal [" .. tName .. "] achieved, new BLUE coalition score is " .. cfxPlayerScore.coalitionScore[coa], 30)
|
||||||
trigger.action.outSoundForCoalition(coa, cfxPlayerScore.scoreSound)
|
trigger.action.outSoundForCoalition(coa, cfxPlayerScore.scoreSound)
|
||||||
@ -1365,6 +1380,9 @@ function cfxPlayerScore.update()
|
|||||||
|
|
||||||
cfxPlayerScore.coalitionScore[coa] = cfxPlayerScore.coalitionScore[coa] + cfxPlayerScore.redTriggerScore[tName]
|
cfxPlayerScore.coalitionScore[coa] = cfxPlayerScore.coalitionScore[coa] + cfxPlayerScore.redTriggerScore[tName]
|
||||||
cfxPlayerScore.redTriggerFlags[tName] = newVal
|
cfxPlayerScore.redTriggerFlags[tName] = newVal
|
||||||
|
if bank and bank.addFunds then
|
||||||
|
bank.addFunds(coa, cfxPlayerScore.score2finance * cfxPlayerScore.blueTriggerScore[tName])
|
||||||
|
end
|
||||||
if cfxPlayerScore.announcer then
|
if cfxPlayerScore.announcer then
|
||||||
trigger.action.outTextForCoalition(coa, "RED goal [" .. tName .. "] achieved, new RED coalition score is " .. cfxPlayerScore.coalitionScore[coa], 30)
|
trigger.action.outTextForCoalition(coa, "RED goal [" .. tName .. "] achieved, new RED coalition score is " .. cfxPlayerScore.coalitionScore[coa], 30)
|
||||||
trigger.action.outSoundForCoalition(coa, cfxPlayerScore.scoreSound)
|
trigger.action.outSoundForCoalition(coa, cfxPlayerScore.scoreSound)
|
||||||
|
|||||||
@ -286,7 +286,11 @@ end
|
|||||||
|
|
||||||
stopGap.kicks = {}
|
stopGap.kicks = {}
|
||||||
function stopGap.kickplayer(args)
|
function stopGap.kickplayer(args)
|
||||||
if not stopGap.kickTheDead then return end
|
if not stopGap.kickTheDead then
|
||||||
|
-- trigger.action.outText("+++sg: Let em rest, no kick", 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
-- trigger.action.outText("Kick'em while they are down!", 30)
|
||||||
local pName = args
|
local pName = args
|
||||||
for i,slot in pairs(net.get_player_list()) do
|
for i,slot in pairs(net.get_player_list()) do
|
||||||
local nn = net.get_name(slot)
|
local nn = net.get_name(slot)
|
||||||
|
|||||||
@ -256,7 +256,11 @@ function stopGap:onEvent(event)
|
|||||||
-- is now slotted into
|
-- is now slotted into
|
||||||
trigger.action.setUserFlag("SG"..gName, 0)
|
trigger.action.setUserFlag("SG"..gName, 0)
|
||||||
end
|
end
|
||||||
|
if id == 6 then -- eject
|
||||||
|
-- trigger.action.outText("+++SG: not handled: Eject Eject Eject!", 30)
|
||||||
|
end
|
||||||
if (id == 9) or (id == 30) or (id == 5) then -- dead, lost, crash
|
if (id == 9) or (id == 30) or (id == 5) then -- dead, lost, crash
|
||||||
|
-- trigger.action.outText("+++sg: event <" .. id .. ">, handing off to kicker", 30)
|
||||||
local pName = theUnit:getPlayerName()
|
local pName = theUnit:getPlayerName()
|
||||||
timer.scheduleFunction(stopGap.kickplayer, pName, timer.getTime() + 1)
|
timer.scheduleFunction(stopGap.kickplayer, pName, timer.getTime() + 1)
|
||||||
end
|
end
|
||||||
@ -265,6 +269,7 @@ end
|
|||||||
|
|
||||||
stopGap.kicks = {}
|
stopGap.kicks = {}
|
||||||
function stopGap.kickplayer(args)
|
function stopGap.kickplayer(args)
|
||||||
|
-- trigger.action.outText("+++sg: enter kicker!", 30)
|
||||||
if not stopGap.kickTheDead then return end
|
if not stopGap.kickTheDead then return end
|
||||||
local pName = args
|
local pName = args
|
||||||
for i,slot in pairs(net.get_player_list()) do
|
for i,slot in pairs(net.get_player_list()) do
|
||||||
|
|||||||
173
modules/xpStrat.lua
Normal file
173
modules/xpStrat.lua
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
xpStrat = {}
|
||||||
|
-- AI strategy module for expansion.
|
||||||
|
xpStrat.version = "0.0.0"
|
||||||
|
xpStrat.requiredLibs = {
|
||||||
|
"dcsCommon", -- always
|
||||||
|
"cfxZones", -- Zones, of course
|
||||||
|
"milHelo", -- for helo attack and capture missions
|
||||||
|
}
|
||||||
|
xpStrat.zones = {} -- all the zones that interest me
|
||||||
|
xpStrat.AI = {} -- red, blue -- if true, that side has pure ai
|
||||||
|
|
||||||
|
function xpStrat.addXPZone(theZone)
|
||||||
|
xpStrat.zones[theZone.name] = theZone
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Strategy
|
||||||
|
--
|
||||||
|
function xpStrat.fullAIStrategy(coa, cname)
|
||||||
|
if xpStrat.verbose then
|
||||||
|
trigger.action.outText("FULL AI Strategy for (" .. coa .. "/" .. cname .. ")", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function xpStrat.getMilHSource(coa, msnType, nearZone)
|
||||||
|
if not msnType then
|
||||||
|
msnType = dcsCommon.pickRandom(milHelo.missionTypes)
|
||||||
|
elseif msnType == "*" then
|
||||||
|
msnType = nil -- get all.
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get all sources for coa and msnTypes
|
||||||
|
local sources = milHelo.getMilSources(coa, msnType)
|
||||||
|
if #sources < 1 then
|
||||||
|
trigger.action.outText("Strat: no sources found for <" .. msnType .. "> helo mission", 30)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local theSource = nil
|
||||||
|
if nearZone then
|
||||||
|
theSource = nearZone:getClosestZone(sources)
|
||||||
|
else
|
||||||
|
-- pick one by random
|
||||||
|
theSource = dcsCommon.pickRandom(sources)
|
||||||
|
end
|
||||||
|
return theSource
|
||||||
|
end
|
||||||
|
|
||||||
|
function xpStrat.createHeloMission(coa, msnType, theSource, theTarget) -- msnType = "*" means any, nil means pick random
|
||||||
|
if not theSource then
|
||||||
|
theSource = xpStrat.getMilHSource(coa, msnType)
|
||||||
|
end
|
||||||
|
if not theSource then
|
||||||
|
trigger.action.outText("Strat: cannot find coa <" .. coa .. "> source for <" .. msnType .. "> mission", 30)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
msnType = theSource.msnType
|
||||||
|
|
||||||
|
-- now gather all destinations
|
||||||
|
if not theTarget then
|
||||||
|
local targets = milHelo.getMilTargets(coa)
|
||||||
|
if #targets < 1 then
|
||||||
|
trigger.action.outText("Strat: no destinations for side " .. side, 30)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- TODO: choose nearest target to source
|
||||||
|
-- and prefer neutral
|
||||||
|
theTarget = theSource:getClosestZone(targets)--targets[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- if we get here, we have a source and target
|
||||||
|
|
||||||
|
trigger.action.outText("Strat: identified Coa <" .. coa .. "> - Starting <" .. msnType .. "> mission from <" .. theSource.name .. "> to <" .. theTarget.name .. ">)", 30)
|
||||||
|
|
||||||
|
return theSource, theTarget, msnType
|
||||||
|
end
|
||||||
|
|
||||||
|
function xpStrat.playerAIStrategy(coa, cname)
|
||||||
|
if xpStrat.verbose then
|
||||||
|
trigger.action.outText("Player-assisted AI Strategy for (" .. coa .. "/" .. cname .. ")", 30)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- strategy in general (ha!, pun)
|
||||||
|
-- check which missions are done first
|
||||||
|
-- select one mission unless still running and initiate own support flights
|
||||||
|
-- select one aggressive flight and start it, no matter what
|
||||||
|
end
|
||||||
|
|
||||||
|
function xpStrat.update()
|
||||||
|
-- schedule next round
|
||||||
|
timer.scheduleFunction(xpStrat.update, {}, timer.getTime() + xpStrat.interval)
|
||||||
|
|
||||||
|
local sides = {"red", "blue"}
|
||||||
|
for idx, sideName in pairs(sides) do
|
||||||
|
local coa = 1
|
||||||
|
if sideName == "blue" then coa = 2 end
|
||||||
|
|
||||||
|
if xpStrat.AI[sideName] then
|
||||||
|
xpStrat.fullAIStrategy(coa, sideName)
|
||||||
|
else
|
||||||
|
xpStrat.playerAIStrategy(coa, sideName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function xpStrat.readConfigZone()
|
||||||
|
local theZone = cfxZones.getZoneByName("expansionConfig")
|
||||||
|
if not theZone then
|
||||||
|
theZone = cfxZones.createSimpleZone("expansionConfig")
|
||||||
|
end
|
||||||
|
xpStrat.redAI = theZone:getBoolFromZoneProperty("redAI", true) -- is red player or AI?
|
||||||
|
xpStrat.AI["red"] = xpStrat.redAI
|
||||||
|
xpStrat.blueAI = theZone:getBoolFromZoneProperty("blueAI", true) -- is red player or AI?
|
||||||
|
xpStrat.AI["blue"] = xpStrat.blueAI
|
||||||
|
|
||||||
|
xpStrat.interval = theZone:getNumberFromZoneProperty("interval", 600)
|
||||||
|
xpStrat.difficulty = theZone:getNumberFromZoneProperty("difficulty", 1)
|
||||||
|
|
||||||
|
xpStrat.verbose = theZone.verbose
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function xpStrat.init()
|
||||||
|
-- gather data etc
|
||||||
|
|
||||||
|
trigger.action.outText("'Expansion' Core v" .. xpStrat.version .. " (" .. dcsCommon.getMapName() .. ") started.", 30)
|
||||||
|
local msg = ""
|
||||||
|
if xpStrat.redAI then msg = msg .. "\nRed side controlled by AI General"
|
||||||
|
else msg = msg .. "\nRed side controlled by Player-Assisted General" end
|
||||||
|
if xpStrat.blueAI then msg = msg .. "\nBlue side controlled by AI General"
|
||||||
|
else msg = msg .. "\nBlue side controlled by Player-Assisted General" end
|
||||||
|
msg = msg .. "\ndifficulty level set to " .. xpStrat.difficulty
|
||||||
|
msg = msg .. "\n"
|
||||||
|
trigger.action.outText(msg, 30)
|
||||||
|
|
||||||
|
-- schedule first round of AI in 10 seconds
|
||||||
|
timer.scheduleFunction(xpStrat.update, {}, timer.getTime() + 10)
|
||||||
|
end
|
||||||
|
|
||||||
|
function xpStrat.start()
|
||||||
|
-- lib check
|
||||||
|
if not dcsCommon.libCheck then
|
||||||
|
trigger.action.outText("xpStrat requires dcsCommon", 30)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if not dcsCommon.libCheck("xpStrat", xpStrat.requiredLibs) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- read config
|
||||||
|
xpStrat.readConfigZone()
|
||||||
|
|
||||||
|
-- read income zones
|
||||||
|
--[[-- local attrZones = cfxZones.getZonesWithAttributeNamed("income")
|
||||||
|
for k, aZone in pairs(attrZones) do
|
||||||
|
income.createIncomeWithZone(aZone) -- process attributes
|
||||||
|
income.addIncomeZone(aZone) -- add to list
|
||||||
|
end
|
||||||
|
--]]--
|
||||||
|
-- schedule init for 5 seconds after mission start
|
||||||
|
timer.scheduleFunction(xpStrat.init, {}, timer.getTime() + 5)
|
||||||
|
|
||||||
|
trigger.action.outText("xpStrat v" .. xpStrat.version .. " started.", 30)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if not xpStrat.start() then
|
||||||
|
trigger.action.outText("xpStrat aborted: missing libraries", 30)
|
||||||
|
xpStrat = nil
|
||||||
|
end
|
||||||
Loading…
x
Reference in New Issue
Block a user