Version 1.4.3

Airfield Module
This commit is contained in:
Christian Franz 2023-09-07 17:50:11 +02:00
parent 23830d47db
commit 0f1e814d22
11 changed files with 401 additions and 24 deletions

Binary file not shown.

Binary file not shown.

View File

@ -109,7 +109,11 @@ function rndFlags.createRNDWithZone(theZone)
if theZone.triggerFlag then
theZone.lastTriggerValue = cfxZones.getFlagValue(theZone.triggerFlag, theZone) --trigger.misc.getUserFlag(theZone.triggerFlag) -- save last value
theZone.lastTriggerValue = cfxZones.getFlagValue(theZone.triggerFlag, theZone)
if rndFlags.verbose or theZone.verbose then
trigger.action.outText("+++RND: randomizer in <" .. theZone:getName() .. "> triggers on flag <" .. theZone.triggerFlag .. ">", 30)
end
--trigger.misc.getUserFlag(theZone.triggerFlag) -- save last value
end
theZone.onStart = cfxZones.getBoolFromZoneProperty(theZone, "onStart", false)

314
modules/airfield.lua Normal file
View File

@ -0,0 +1,314 @@
airfield = {}
airfield.version = "1.0.0"
airfield.requiredLibs = {
"dcsCommon",
"cfxZones",
}
airfield.verbose = false
airfield.myAirfields = {} -- indexed by name
airfield.farps = false
--[[--
This module generates signals when the nearest airfield changes hands,
can force the coalition of an airfield, and always provides the
current owner as a value
Version History
1.0.0 - initial release
--]]--
--
-- setting up airfield
--
function airfield.createAirFieldFromZone(theZone)
local filterCat = 0
if airfield.farps then filterCat = {0, 1} end -- bases and farps
local p = theZone:getPoint()
local theBase = dcsCommon.getClosestAirbaseTo(p, filterCat)
theZone.airfield = theBase
theZone.afName = theBase:getName()
if airfield.verbose then
trigger.action.outText("+++airF: zone <" .. theZone:getName() .. "> associates with <" .. theZone.afName .. ">", 30)
end
-- methods
theZone.method = theZone:getStringFromZoneProperty("method", "inc")
theZone.triggerMethod = theZone:getStringFromZoneProperty("triggerMethod", "change")
-- set zone's owner
theZone.owner = theBase:getCoalition()
if theZone:hasProperty("red!") then
theZone.redCap = theZone:getStringFromZoneProperty("red!")
end
if theZone:hasProperty("blue!") then
theZone.blueCap = theZone:getStringFromZoneProperty("blue!")
end
-- controlled capture?
if theZone:hasProperty("makeRed?") then
theZone.makeRed = theZone:getStringFromZoneProperty("makeRed?", "<none>")
theZone.lastMakeRed = trigger.misc.getUserFlag(theZone.makeRed)
end
if theZone:hasProperty("makeBlue?") then
theZone.makeBlue = theZone:getStringFromZoneProperty("makeBlue?", "<none>")
theZone.lastMakeBlue = trigger.misc.getUserFlag(theZone.makeBlue)
end
if theZone:hasProperty("autoCap?") then
theZone.autoCap = theZone:getStringFromZoneProperty("autoCap?", "<none>")
theZone.lastAutoCap = trigger.misc.getUserFlag(theZone.autoCap)
end
theZone.directControl = theZone:getBoolFromZoneProperty("directControl", false)
if theZone.directControl then
airfield.assumeControl(theZone)
end
if theZone:hasProperty("ownedBy#") then
theZone.ownedBy = theZone:getStringFromZoneProperty("ownedBy#", "<none>")
trigger.action.setUserFlag(theZone.ownedBy, theZone.owner)
end
-- index by name, and warn if duplicate associate
if airfield.myAirfields[theZone.afName] then
trigger.action.outText("+++airF: WARNING - zone <" .. theZone:getName() .. "> redefines airfield <" .. theZone.afName .. ">, discarded!", 30)
else
airfield.myAirfields[theZone.afName] = theZone
end
end
function airfield.assumeControl(theZone)
theBase = theZone.airfield
if airfield.verbose or theZone.verbose then
trigger.action.outText("+++airF: assuming direct control and turning off auto-capture for <" .. theZone.afName .. ">, now controlled by zone <" .. theZone:getName() .. ">", 30)
end
theBase:autoCapture(false) -- turn off autocap
end
function airfield.relinquishControl(theZone)
theBase = theZone.airfield
if airfield.verbose or theZone.verbose then
trigger.action.outText("+++airF: zone <" .. theZone:getName() .. "> relinquishing ownership control over <" .. theZone.afName .. ">, can be captured normally", 30)
end
theBase:autoCapture(true) -- turn off autocap
end
--
-- event handling
--
function airfield.airfieldCaptured(theBase)
-- retrieve the zone that controls this airfield
local bName = theBase:getName()
local theZone = airfield.myAirfields[bName]
if not theZone then return end -- not handled
local newCoa = theBase:getCoalition()
theZone.owner = newCoa
if theZone.verbose or airfield.verbose then
trigger.action.outText("+++airF: handling capture event/command for airfield <" .. bName .. "> with zone <" .. theZone:getName() .. ">", 30)
end
-- outputs
if theZone.ownedBy then
trigger.action.setUserFlag(theZone.ownedBy, theZone.owner)
end
if theZone.redCap and newCoa == 1 then
theZone:pollFlag(theZone.redCap, theZone.method)
end
if theZone.blueCap and newCoa == 2 then
theZone:pollFlag(theZone.blueCap, theZone.method)
end
end
function airfield:onEvent(event)
if not event then return end
if event.id == 10 then -- S_EVENT_BASE_CAPTURED
local theBase = event.place
if not theBase then
trigger.action.outText("+++airF: error: cap event without base", 30)
return
end
-- get category
local desc = theBase:getDesc()
local bName = theBase:getName()
local cat = desc.category -- never get cat directly!
if cat == 1 then
if not airfield.farps then
if airfield.verbose then
trigger.action.outText("+++airF: ignored cap event for FARP <" .. bName .. ">", 30)
end
return
end
end
if airfield.verbose then
trigger.action.outText("+++airF: cap event for <" .. bName .. ">, cat = (" .. cat .. ")", 30)
end
airfield.airfieldCaptured(theBase)
end
end
--
-- update
--
function airfield.update()
timer.scheduleFunction(airfield.update, {}, timer.getTime() + 1)
for afName, theZone in pairs(airfield.myAirfields) do
local theAirfield = theZone.airfield
if theZone.makeRed and theZone:testZoneFlag(theZone.makeRed, theZone.triggerMethod, "lastMakeRed") then
if theZone.verbose or airfield.verbose then
trigger.action.outText("+++airF: 'makeRed' triggered for airfield <" .. afName .. "> in zone <" .. theZone:getName() .. ">", 30)
end
if theAirfield:autoCaptureIsOn() then
-- turn off autoCap
airfield.assumeControl(theZone)
end
theAirfield:setCoalition(1) -- make it red, doesn't trigger event
if theZone.owner ~= 1 then -- only send cap event when capped
airfield.airfieldCaptured(theAirfield)
end
end
if theZone.makeBlue and theZone:testZoneFlag(theZone.makeBlue, theZone.triggerMethod, "lastMakeBlue") then
if theZone.verbose or airfield.verbose then
trigger.action.outText("+++airF: 'makeBlue' triggered for airfield <" .. afName .. "> in zone <" .. theZone:getName() .. ">", 30)
end
if theAirfield:autoCaptureIsOn() then
-- turn off autoCap
airfield.assumeControl(theZone)
end
theAirfield:setCoalition(2) -- make it blue
if theZone.owner ~= 2 then -- only send cap event when capped
airfield.airfieldCaptured(theAirfield)
end
end
if theZone.autoCap and theZone:testZoneFlag(theZone.autoCap, theZone.triggerMethod, "lastAutoCap") then
if theAirfield:autoCaptureIsOn() then
-- do nothing
else
airfield.relinquishControl(theZone)
end
end
end
end
--
-- LOAD / SAVE
--
function airfield.saveData()
local theData = {}
local allAF = {}
for name, theZone in pairs(airfield.myAirfields) do
local theName = name
local theAirfield = theZone.airfield
local AFData = {}
AFData.autocapActive = theAirfield:autoCaptureIsOn()
AFData.owner = theZone.owner
allAF[theName] = AFData
end
theData.allAF = allAF
return theData
end
function airfield.loadData()
if not persistence then return end
local theData = persistence.getSavedDataForModule("airfield")
if not theData then
if airfield.verbose then
trigger.action.outText("+++airF persistence: no save data received, skipping.", 30)
end
return
end
local allAF = theData.allAF
if not allAF then
if airfield.verbose then
trigger.action.outText("+++airF persistence: no airfield data, skipping", 30)
end
return
end
for theName, AFData in pairs(allAF) do
local theZone = airfield.myAirfields[theName]
if theZone then
-- synch airfield ownership, and auto-capture status
local theAirfield = theZone.airfield
-- set current owner
theAirfield:autoCapture(false)
theAirfield:setCoalition(AFData.owner)
theZone.owner = AFData.owner
-- set ownedBy#
if theZone.ownedBy then
trigger.action.setUserFlag(theZone.ownedBy, theZone.owner)
end
-- set owning mode: autocap or direct
theAirfield:autoCapture(AFData.autocapActive)
else
trigger.action.outText("+++airF persistence: cannot synch airfield <" .. theName .. ">, skipping", 40)
end
end
end
--
-- start up
--
function airfield.readConfig()
local theZone = cfxZones.getZoneByName("airfieldConfig")
if not theZone then
theZone = cfxZones.createSimpleZone("airfieldConfig")
end
airfield.verbose = theZone.verbose
airfield.farps = theZone:getBoolFromZoneProperty("farps", false)
end
function airfield.start()
if not dcsCommon.libCheck("cfx airfield", airfield.requiredLibs)
then return false end
-- read config
airfield.readConfig()
-- read bases
local abZones = cfxZones.zonesWithProperty("airfield")
for idx, aZone in pairs(abZones) do
airfield.createAirFieldFromZone(aZone)
end
-- connect event handler
world.addEventHandler(airfield)
-- load any saved data
if persistence then
-- sign up for persistence
callbacks = {}
callbacks.persistData = airfield.saveData
persistence.registerModule("airfield", callbacks)
-- now load my data
airfield.loadData()
end
-- start update in 1 second
timer.scheduleFunction(airfield.update, {}, timer.getTime() + 1)
trigger.action.outText("cfx airfield v" .. airfield.version .. " loaded.", 30)
return true
end
if not airfield.start() then
trigger.action.outText("+++ aborted airfield v" .. airfield.version .. " -- startup failed", 30)
airfield = nil
end
--[[--
ideas: what if we made this airfield movable?
--]]--

View File

@ -1,5 +1,5 @@
cfxZones = {}
cfxZones.version = "4.0.2"
cfxZones.version = "4.0.3"
-- cf/x zone management module
-- reads dcs zones and makes them accessible and mutable
@ -151,6 +151,8 @@ cfxZones.version = "4.0.2"
negative numbers, backwards compatibility with old (dysfunctional) method
- 4.0.1 - dmlZone:getName()
- 4.0.2 - removed verbosity from declutterZone (both versions)
- 4.0.3 - new processDynamicVZU()
- wildcard uses processDynamicVZU
--]]--
@ -3101,6 +3103,41 @@ function cfxZones.processDynamicLoc(inMsg, imperialUnits, responses)
return outMsg
end
-- process reference that can be flag, Zone, or unit.
-- i.e. <coa: xyz>
function cfxZones.processDynamicVZU(inMsg)
local locales = {"coa",}
local outMsg = inMsg
local uHead = 0
for idx, aLocale in pairs(locales) do
local pattern = "<" .. aLocale .. ":%s*[%s%w%*%d%.%-_]+>"
repeat -- iterate all patterns one by one
local startLoc, endLoc = string.find(outMsg, pattern)
if startLoc then
local theValParam = string.sub(outMsg, startLoc, endLoc)
-- strip lead and trailer
local param = string.gsub(theValParam, "<" .. aLocale .. ":%s*", "")
param = string.gsub(param, ">","")
-- find zone or unit
param = dcsCommon.trim(param)
local tZone = cfxZones.getZoneByName(param)
local tUnit = Unit.getByName(param)
local locString = "err"
if aLocale == "coa" then
coa = trigger.misc.getUserFlag(param)
if tZone then coa = tZone.owner end
if zUnit then coa = tUnit:getCoalition() end
locString = dcsCommon.coalition2Text(coa)
end
outMsg = string.gsub(outMsg, pattern, locString, 1) -- only one sub!
end -- if startloc
until not startLoc
end -- for all locales
return outMsg
end
function cfxZones.rspMapper360(directionInDegrees, numResponses)
-- maps responses around a clock. Clock has 12 'responses' (12, 1, .., 11),
-- with the first (12) also mapping to the last half arc
@ -3148,7 +3185,8 @@ function cfxZones.processStringWildcards(inMsg, theZone, timeFormat, imperialUni
theMsg = cfxZones.processDynamicTime(theMsg, theZone, timeFormat)
-- process <lat/lon/ele/mgrs/lle/latlon/alt/vel/hdg/rhdg/type/player: zone/unit>
theMsg = cfxZones.processDynamicLoc(theMsg, imperialUnits, responses)
-- process values that can be derived from flag (default), zone or unit
theMsg = cfxZones.processDynamicVZU(theMsg)
return theMsg
end

View File

@ -1,5 +1,5 @@
cloneZones = {}
cloneZones.version = "1.8.0"
cloneZones.version = "1.8.2"
cloneZones.verbose = false
cloneZones.requiredLibs = {
"dcsCommon", -- always
@ -98,6 +98,8 @@ cloneZones.respawnOnGroupID = true
1.8.0 - OOP cfxZones
- removed "empty+1" as planned
- upgraded config zone parsing
1.8.1 - clone zone definition now supports quads
1.8.2 - on pre-wipe, delay respawn by 0.5s to avoid 'dropping' statics
--]]--
@ -166,8 +168,9 @@ function cloneZones.partOfGroupDataInZone(theZone, theUnits)
uP.x = aUnit.x
uP.y = 0
uP.z = aUnit.y -- !! y-z
local dist = dcsCommon.dist(uP, zP)
if dist <= theZone.radius then return true end
--local dist = dcsCommon.dist(uP, zP)
--if dist <= theZone.radius then return true end
if theZone:pointInZone(uP) then return true end
end
return false
end
@ -314,14 +317,7 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
theZone.moveRoute = theZone:getBoolFromZoneProperty("moveRoute", false)
theZone.preWipe = theZone:getBoolFromZoneProperty("preWipe", false)
-- to be deprecated
--[[--
if theZone:hasProperty("empty+1") then
theZone.emptyFlag = theZone:getStringFromZoneProperty("empty+1", "<None>") -- note string on number default
end
--]]--
if theZone:hasProperty("empty!") then
theZone.emptyBangFlag = theZone:getStringFromZoneProperty("empty!", "<None>") -- note string on number default
end
@ -1542,9 +1538,11 @@ function cloneZones.spawnWithCloner(theZone)
end
-- pre-Wipe?
local didPrewipe = false
if theZone.preWipe then
cloneZones.despawnAll(theZone)
cloneZones.invokeCallbacks(theZone, "wiped", {})
didPrewipe = true
end
-- declutter?
@ -1555,6 +1553,19 @@ function cloneZones.spawnWithCloner(theZone)
end
end
local args = {theZone = theZone, templateZone = templateZone}
if didPrewipe then -- delay spawning to allow revoval to take place
timer.scheduleFunction(cloneZones.doClone, args, timer.getTime() + 0.5)
else -- can do immediately
cloneZones.doClone(args)
end
end
-- deferrable clone method.
function cloneZones.doClone(args)
local theZone = args.theZone
local templateZone = args.templateZone
local theClones, theStatics = cloneZones.spawnWithTemplateForZone(templateZone, theZone)
-- reset hasClones so we know our spawns are full and we can
-- detect complete destruction
@ -1569,7 +1580,7 @@ function cloneZones.spawnWithCloner(theZone)
theZone.mySpawns = {}
theZone.myStatics = {}
end
end
end
function cloneZones.countLiveUnits(theZone)
if not theZone then return 0 end

View File

@ -1,5 +1,5 @@
dcsCommon = {}
dcsCommon.version = "2.9.2"
dcsCommon.version = "2.9.3"
--[[-- VERSION HISTORY
2.2.6 - compassPositionOfARelativeToB
- clockPositionOfARelativeToB
@ -171,8 +171,8 @@ dcsCommon.version = "2.9.2"
- new bearingFromAtoBusingXY()
- corrected verbosity for bearingFromAtoB
- new getCountriesForCoalition()
2.9.2 - updated task2text
2.9.2 - updated event2text
2.9.3 - getAirbasesWhoseNameContains now supports category tables for filtering
--]]--
-- dcsCommon is a library of common lua functions
@ -495,7 +495,8 @@ dcsCommon.version = "2.9.2"
-- getAirbasesWhoseNameContains - get all airbases containing
-- a name. filterCat is optional and can be aerodrome (0), farp (1), ship (2)
-- filterCoalition is optional and can be 0 (neutral), 1 (red), 2 (blue)
-- filterCoalition is optional and can be 0 (neutral), 1 (red), 2 (blue) or
-- a table containing categories, e.g. {0, 2} = airfields and ships but not farps
-- if no name given or aName = "*", then all bases are returned prior to filtering
function dcsCommon.getAirbasesWhoseNameContains(aName, filterCat, filterCoalition)
--trigger.action.outText("getAB(name): enter with " .. aName, 30)
@ -510,11 +511,20 @@ dcsCommon.version = "2.9.2"
--if aName ~= "*" then
-- trigger.action.outText("getAB(name): matched " .. airBaseName, 30)
--end
local doAdd = true
local doAdd = true
if filterCat then
-- make sure the airbase is of that category
local airCat = dcsCommon.getAirbaseCat(aBase)
doAdd = doAdd and airCat == filterCat
local aCat = dcsCommon.getAirbaseCat(aBase)
if type(filterCat) == "table" then
local hit = false
for idx, fCat in pairs(filterCat) do
if fCat == aCat then hit = true end
end
doAdd = doAdd and hit
else
-- make sure the airbase is of that category
local airCat = aCat
doAdd = doAdd and airCat == filterCat
end
end
if filterCoalition then

View File

@ -389,7 +389,7 @@ function noGap.start()
-- connect event handler
world.addEventHandler(noGap)
-- start update in 10 seconds
-- start update in 1 second
timer.scheduleFunction(noGap.update, {}, timer.getTime() + 1)
-- say hi!

Binary file not shown.

Binary file not shown.