Yeller Model, The Debugger -x !, heloTroops' dropZones
This commit is contained in:
Christian Franz 2024-11-07 07:35:36 +01:00
parent 0c1eb53a89
commit b498a4e803
13 changed files with 506 additions and 121 deletions

Binary file not shown.

Binary file not shown.

View File

@ -1,5 +1,5 @@
cfxCargoReceiver = {} cfxCargoReceiver = {}
cfxCargoReceiver.version = "2.0.0" cfxCargoReceiver.version = "2.1.0"
cfxCargoReceiver.ups = 1 -- once a second cfxCargoReceiver.ups = 1 -- once a second
cfxCargoReceiver.maxDirectionRange = 500 -- in m. distance when cargo manager starts talking to pilots who are carrying that cargo cfxCargoReceiver.maxDirectionRange = 500 -- in m. distance when cargo manager starts talking to pilots who are carrying that cargo
cfxCargoReceiver.requiredLibs = { cfxCargoReceiver.requiredLibs = {
@ -12,7 +12,10 @@ cfxCargoReceiver.requiredLibs = {
- 2.0.0 no more cfxPlayer Dependency - 2.0.0 no more cfxPlayer Dependency
dmlZones, OOP dmlZones, OOP
clean-up clean-up
- 2.1.0 code maintenance
cargo gets removed from mgr when touching ground
switch to cargoReceiver!
noBounce attribute
CargoReceiver is a zone enhancement you use to be automatically CargoReceiver is a zone enhancement you use to be automatically
notified if a cargo was delivered inside the zone. notified if a cargo was delivered inside the zone.
@ -25,26 +28,30 @@ cfxCargoReceiver.receiverZones = {}
function cfxCargoReceiver.processReceiverZone(aZone) -- process attribute and add to zone function cfxCargoReceiver.processReceiverZone(aZone) -- process attribute and add to zone
-- since the attribute is there, simply set the zones -- since the attribute is there, simply set the zones
-- isCargoReceiver flag and we are good -- isCargoReceiver flag and we are good
aZone.isCargoReceiver = true aZone.isCargoReceiver = true -- so all zones can detect this. Necessary?
-- we can add additional processing here aZone.autoRemove = aZone:getBoolFromZoneProperty("autoRemove", false)
aZone.autoRemove = aZone:getBoolFromZoneProperty("autoRemove", false) -- maybe add a removeDelay
aZone.removeDelay = aZone:getNumberFromZoneProperty("removeDelay", 1) aZone.removeDelay = aZone:getNumberFromZoneProperty("removeDelay", 1)
if aZone.removeDelay < 1 then aZone.removeDelay = 1 end if aZone.removeDelay < 1 then aZone.removeDelay = 1 end
aZone.silent = aZone:getBoolFromZoneProperty("silent", false) aZone.silent = aZone:getBoolFromZoneProperty("silent", false)
-- new method support
aZone.cargoMethod = aZone:getStringFromZoneProperty("method", "inc") aZone.cargoMethod = aZone:getStringFromZoneProperty("method", "inc")
if aZone:hasProperty("cargoMethod") then if aZone:hasProperty("cargoMethod") then
aZone.cargoMethod = aZone:getStringFromZoneProperty("cargoMethod", "inc") aZone.cargoMethod = aZone:getStringFromZoneProperty("cargoMethod", "inc")
end end
if aZone:hasProperty("f!") then -- save the names of cargos delivered to prevent 'bounce'
aZone.cargosDelivered = {} -- by cargo name
aZone.noBounce = aZone:getBoolFromZoneProperty("noBounce", true)
if aZone:hasProperty("cargoReceiver!") then
aZone.outReceiveFlag = aZone:getStringFromZoneProperty("cargoReceiver!", "*<none>")
elseif aZone:hasProperty("f!") then
aZone.outReceiveFlag = aZone:getStringFromZoneProperty("f!", "*<none>") aZone.outReceiveFlag = aZone:getStringFromZoneProperty("f!", "*<none>")
elseif aZone:hasProperty("cargoReceived!") then elseif aZone:hasProperty("cargoReceived!") then
aZone.outReceiveFlag = aZone:getStringFromZoneProperty( "cargoReceived!", "*<none>") aZone.outReceiveFlag = aZone:getStringFromZoneProperty( "cargoReceived!", "*<none>")
end end
if not aZone.outReceiveFlag then
trigger.action.outText("+++crgR: receiver <" .. aZone.name .. "> has no output!", 30)
end
end end
function cfxCargoReceiver.addReceiverZone(aZone) function cfxCargoReceiver.addReceiverZone(aZone)
@ -94,7 +101,7 @@ function cfxCargoReceiver.cargoEvent(event, object, name)
if not event then return end if not event then return end
if event == "grounded" then if event == "grounded" then
-- this is actually the only one that interests us -- this is the only one that interests us
if not object then if not object then
return return
end end
@ -106,21 +113,28 @@ function cfxCargoReceiver.cargoEvent(event, object, name)
-- now invoke callbacks for all zones -- now invoke callbacks for all zones
-- this is in -- this is in
for name, aZone in pairs(cfxCargoReceiver.receiverZones) do for name, aZone in pairs(cfxCargoReceiver.receiverZones) do
if cfxZones.pointInZone(loc, aZone) then if aZone:pointInZone(loc) then
cfxCargoReceiver.invokeCallback("deliver", object, name, aZone) cfxCargoReceiver.invokeCallback("deliver", object, name, aZone)
-- set flags as indicated -- set flags as indicated
if aZone.outReceiveFlag then if aZone.outReceiveFlag then
cfxZones.pollFlag(aZone.outReceiveFlag, aZone.cargoMethod, aZone) if aZone.noBounce then
-- we only do this once for every named object
if not aZone.cargosDelivered[name] then
aZone.cargosDelivered[name] = true
aZone:pollFlag(aZone.outReceiveFlag, aZone.cargoMethod)
end
else
aZone:pollFlag(aZone.outReceiveFlag, aZone.cargoMethod)
end
end end
if aZone.autoRemove then if aZone.autoRemove then
-- schedule this for in a few seconds? -- schedule this for in a few seconds
local args = {} local args = {}
args.theObject = object args.theObject = object
args.theZone = aZone args.theZone = aZone
timer.scheduleFunction(cfxCargoReceiver.removeCargo, args, timer.getTime() + aZone.removeDelay) timer.scheduleFunction(cfxCargoReceiver.removeCargo, args, timer.getTime() + aZone.removeDelay)
--object:destroy()
end end
end end
end end
@ -139,17 +153,13 @@ function cfxCargoReceiver.update()
-- new we see if any of these are close to a delivery zone -- new we see if any of these are close to a delivery zone
for idx, aCargo in pairs(liftedCargos) do for idx, aCargo in pairs(liftedCargos) do
local thePoint = aCargo:getPoint() local thePoint = aCargo:getPoint()
local receiver = cfxZones.getClosestZone( local receiver = cfxZones.getClosestZone(thePoint, cfxCargoReceiver.receiverZones) -- must be indexed by name
thePoint,
cfxCargoReceiver.receiverZones -- must be indexed by name
)
-- we now check if we are in 'speaking range' and receiver can talk -- we now check if we are in 'speaking range' and receiver can talk
-- modify delta by distance to boundary, not -- modify delta by distance to boundary, not center
-- center
local delta = dcsCommon.distFlat(thePoint, cfxZones.getPoint(receiver)) local delta = dcsCommon.distFlat(thePoint, cfxZones.getPoint(receiver))
delta = delta - receiver.radius delta = delta - receiver.radius
if (receiver.silent == false) and if (not receiver.silent) and
(delta < cfxCargoReceiver.maxDirectionRange) then (delta < cfxCargoReceiver.maxDirectionRange) then
-- this cargo can be talked down. -- this cargo can be talked down.
-- find the player unit that is closest to in in hopes -- find the player unit that is closest to in in hopes
@ -214,10 +224,14 @@ function cfxCargoReceiver.start()
end end
-- scan all zones for cargoReceiver flag -- scan all zones for cargoReceiver flag
local attrZones = cfxZones.getZonesWithAttributeNamed("cargoReceiver!")
for k, aZone in pairs(attrZones) do
cfxCargoReceiver.processReceiverZone(aZone)
cfxCargoReceiver.addReceiverZone(aZone)
end
-- old version, LEGACY
local attrZones = cfxZones.getZonesWithAttributeNamed("cargoReceiver") local attrZones = cfxZones.getZonesWithAttributeNamed("cargoReceiver")
-- now create a spawner for all, add them to the spawner updater, and spawn for all zones that are not
-- paused
for k, aZone in pairs(attrZones) do for k, aZone in pairs(attrZones) do
cfxCargoReceiver.processReceiverZone(aZone) -- process attribute and add to zone cfxCargoReceiver.processReceiverZone(aZone) -- process attribute and add to zone
cfxCargoReceiver.addReceiverZone(aZone) -- remember it so we can smoke it cfxCargoReceiver.addReceiverZone(aZone) -- remember it so we can smoke it

View File

@ -1,5 +1,5 @@
cfxZones = {} cfxZones = {}
cfxZones.version = "4.4.2" cfxZones.version = "4.4.3"
-- cf/x zone management module -- cf/x zone management module
-- reads dcs zones and makes them accessible and mutable -- reads dcs zones and makes them accessible and mutable
@ -32,6 +32,7 @@ cfxZones.version = "4.4.2"
- dmlZone:getCoalition() dereferences masterOwner once - dmlZone:getCoalition() dereferences masterOwner once
-4.4.1 - better verbosity for error in doPollFlag() -4.4.1 - better verbosity for error in doPollFlag()
-4.4.2 - twn support for wildcards <twn: > and <loc:> -4.4.2 - twn support for wildcards <twn: > and <loc:>
-4.4.3 - property name is trimmed (double check)
--]]-- --]]--
-- --
@ -123,7 +124,14 @@ function cfxZones.readFromDCS(clearfirst)
else else
newZone.properties = {} newZone.properties = {}
end -- WARNING: REF COPY. May need to clone end -- WARNING: REF COPY. May need to clone
--[[--
trigger.action.outText("zone <> properties:trimmed", 30)
local msg = "["
for idx, val in pairs(newZone.properties) do
msg = msg .. "<" .. val.key .. ">:<" .. dcsCommon.trim(val.key) .. ">, "
end
trigger.action.outText(msg, 30)
--]]--
local upperName = newZone.name:upper() local upperName = newZone.name:upper()
-- location as 'point' -- location as 'point'
@ -2158,7 +2166,14 @@ function cfxZones.extractPropertyFromDCS(theKey, theProperties)
for i=1, #theProperties do for i=1, #theProperties do
local theP = theProperties[i] local theP = theProperties[i]
local existingKey = dcsCommon.trim(theP.key) local existingKey = dcsCommon.trim(theP.key) -- does not work if ends on "#!?" - why?
--while len(existingKey) > 1 and string.sub(existingKey, -1) == " " do
-- existingKey = existingKey:sub(1, -2) -- trim manually
--end
if string.sub(existingKey, -1) == " " then
trigger.action.outText("+++ZONES: warning: unable to trim blanks from attribute name <" .. existingKey .. ">", 30)
end
-- trigger.action.outText("procced attribute name <" .. existingKey .. ">", 30)
if not cfxZones.caseSensitiveProperties then if not cfxZones.caseSensitiveProperties then
existingKey = string.lower(existingKey) existingKey = string.lower(existingKey)
end end
@ -2388,6 +2403,7 @@ function dmlZone:hasProperty(theProperty)
trigger.action.outText("+++zne: WARNING - hasProperty called with nil theProperty for zone <" .. self.name .. ">", 30) trigger.action.outText("+++zne: WARNING - hasProperty called with nil theProperty for zone <" .. self.name .. ">", 30)
return false return false
end end
theProperty = dcsCommon.trim(theProperty) -- strip leading and training blanks
local foundIt = self:getZoneProperty(theProperty) local foundIt = self:getZoneProperty(theProperty)
if not foundIt then if not foundIt then
-- check for possible forgotten or exchanged IO flags -- check for possible forgotten or exchanged IO flags

View File

@ -1,5 +1,5 @@
civAir = {} civAir = {}
civAir.version = "3.0.2" civAir.version = "3.1.0"
--[[-- --[[--
3.0.0 liveries support 3.0.0 liveries support
default liveries for Yak-50 (main test case) default liveries for Yak-50 (main test case)
@ -15,7 +15,12 @@ civAir.version = "3.0.2"
protest action protest action
spawning now works correctly for groupType spawning now works correctly for groupType
3.0.2 clean-up 3.0.2 clean-up
3.1.0 better verbosity for startup info
dicts for departOnly and landingOnly
correctly filter depart only, land only, closed at start
Syria multiple "H" airfields DCS bug
filter airfields with no runways
getAirfieldNamed
--]]-- --]]--
civAir.ups = 0.05 -- updates per second. 0.05 = once every 20 seconds civAir.ups = 0.05 -- updates per second. 0.05 = once every 20 seconds
@ -60,8 +65,11 @@ civAir.maxIdle = 8 * 60 -- seconds of ide time before it is removed after landin
civAir.trafficCenters = {} civAir.trafficCenters = {}
civAir.excludeAirfields = {} civAir.excludeAirfields = {}
civAir.excludeAirfieldsDict = {}
civAir.departOnly = {} -- use only to start from civAir.departOnly = {} -- use only to start from
civAir.departOnlyDict = {} -- as above, by af name
civAir.landingOnly = {} -- use only to land at civAir.landingOnly = {} -- use only to land at
civAir.landingOnlyDict = {} -- as above, by af name
civAir.inoutZones = {} -- off-map connector zones civAir.inoutZones = {} -- off-map connector zones
civAir.requiredLibs = { civAir.requiredLibs = {
@ -180,7 +188,7 @@ end
function civAir.processZone(theZone) function civAir.processZone(theZone)
local value = theZone:getStringFromZoneProperty("civAir", "") local value = theZone:getStringFromZoneProperty("civAir", "")
local af = dcsCommon.getClosestAirbaseTo(theZone.point, 0) -- 0 = only airfields, not farp or ships local af = dcsCommon.getClosestAirbaseTo(theZone.point, 0) -- 0 = only airfields, not farp nor ships
local inoutName = "***" .. theZone:getName() local inoutName = "***" .. theZone:getName()
if af then if af then
@ -188,10 +196,13 @@ function civAir.processZone(theZone)
value = value:lower() value = value:lower()
if value == "exclude" or value == "closed" then if value == "exclude" or value == "closed" then
table.insert(civAir.excludeAirfields, afName) table.insert(civAir.excludeAirfields, afName)
civAir.excludeAirfieldsDict[afName] = afName
elseif dcsCommon.stringStartsWith(value, "depart") or dcsCommon.stringStartsWith(value, "start") or dcsCommon.stringStartsWith(value, "take") then elseif dcsCommon.stringStartsWith(value, "depart") or dcsCommon.stringStartsWith(value, "start") or dcsCommon.stringStartsWith(value, "take") then
table.insert(civAir.departOnly, afName) table.insert(civAir.departOnly, afName)
civAir.departOnlyDict[afName] = afName -- can fold many in one
elseif dcsCommon.stringStartsWith(value, "land") or dcsCommon.stringStartsWith(value, "arriv") then elseif dcsCommon.stringStartsWith(value, "land") or dcsCommon.stringStartsWith(value, "arriv") then
table.insert(civAir.landingOnly, afName) table.insert(civAir.landingOnly, afName)
civAir.landingOnlyDict[afName] = afName -- can fold many on one
elseif dcsCommon.stringStartsWith(value, "inb") then elseif dcsCommon.stringStartsWith(value, "inb") then
table.insert(civAir.departOnly, inoutName) -- start in inbound zone table.insert(civAir.departOnly, inoutName) -- start in inbound zone
civAir.inoutZones[inoutName] = theZone civAir.inoutZones[inoutName] = theZone
@ -253,8 +264,8 @@ function civAir.filterAirfields(inAll, inFilter)
end end
function civAir.getTwoAirbases() function civAir.getTwoAirbases()
local fAB -- first airbase to depart local fAB -- name of first airbase to depart from
local sAB -- second airbase to fly to local sAB -- name second airbase to fly to
local departAB = dcsCommon.combineTables(civAir.trafficCenters, civAir.departOnly) local departAB = dcsCommon.combineTables(civAir.trafficCenters, civAir.departOnly)
-- remove all currently excluded air bases from departure -- remove all currently excluded air bases from departure
@ -263,11 +274,14 @@ function civAir.getTwoAirbases()
if #filteredAB < 1 then if #filteredAB < 1 then
trigger.action.outText("+++civA: too few departure airfields", 30) trigger.action.outText("+++civA: too few departure airfields", 30)
return nil, nil return nil, nil
else
-- trigger.action.outText("+++civA: source (first) fields available: " .. #filteredAB, 30)
end end
-- now pick the departure airfield -- now pick the departure airfield
fAB = dcsCommon.pickRandom(filteredAB) fAB = dcsCommon.pickRandom(filteredAB) -- returns an airfield name
-- trigger.action.outText("+++civA: picked first: <" .. fAB .. ">", 30)
-- now generate list of landing airfields -- now generate list of landing airfields
local arriveAB = dcsCommon.combineTables(civAir.trafficCenters, civAir.landingOnly) local arriveAB = dcsCommon.combineTables(civAir.trafficCenters, civAir.landingOnly)
-- remove all currently excluded air bases from arrival -- remove all currently excluded air bases from arrival
@ -277,6 +291,8 @@ function civAir.getTwoAirbases()
if #filteredAB < 1 then if #filteredAB < 1 then
trigger.action.outText("+++civA: too few arrival airfields", 30) trigger.action.outText("+++civA: too few arrival airfields", 30)
return nil, nil return nil, nil
else
-- trigger.action.outText("+++civA: second (ARV) fields available: " .. #filteredAB, 30)
end end
-- pick any second that are not the same -- pick any second that are not the same
@ -285,10 +301,13 @@ function civAir.getTwoAirbases()
sAB = dcsCommon.pickRandom(filteredAB) sAB = dcsCommon.pickRandom(filteredAB)
tries = tries + 1 -- only try 10 times tries = tries + 1 -- only try 10 times
until fAB ~= sAB or tries > 10 until fAB ~= sAB or tries > 10
-- trigger.action.outText("+++civA: picked SECOND: <" .. sAB .. ">", 30)
local civA = {} local civA = {}
if not (dcsCommon.stringStartsWith(fAB, '***')) then if not (dcsCommon.stringStartsWith(fAB, '***')) then
civA.AB = dcsCommon.getFirstAirbaseWhoseNameContains(fAB, 0) civA.AB = civAir.getAirfieldNamed(fAB) -- dcsCommon.getFirstAirbaseWhoseNameContains(fAB, 0)
civA.name = civA.AB:getName() civA.name = civA.AB:getName()
else else
civA.zone = civAir.inoutZones[fAB] civA.zone = civAir.inoutZones[fAB]
@ -296,16 +315,22 @@ function civAir.getTwoAirbases()
end end
local civB = {} local civB = {}
if not (dcsCommon.stringStartsWith(sAB, '***')) then if not (dcsCommon.stringStartsWith(sAB, '***')) then
civB.AB = dcsCommon.getFirstAirbaseWhoseNameContains(sAB, 0) civB.AB = civAir.getAirfieldNamed(sAB) -- dcsCommon.getFirstAirbaseWhoseNameContains(sAB, 0)
civB.name = civB.AB:getName() civB.name = civB.AB:getName()
else else
civB.zone = civAir.inoutZones[sAB] civB.zone = civAir.inoutZones[sAB]
civB.name = civB.zone:getName() civB.name = civB.zone:getName()
end end
-- trigger.action.outText("+++civA: picked source AF <" .. civA.name .. "> and dest <" .. civB.name .. ">", 30)
return civA, civB -- fAB, sAB return civA, civB -- fAB, sAB
end end
function civAir.getAirfieldNamed(name)
return Airbase.getByName(name)
end
function civAir.parkingIsFree(fromWP) function civAir.parkingIsFree(fromWP)
-- iterate over all currently registred flights and make -- iterate over all currently registred flights and make
-- sure that their location isn't closer than 10m to my new parking -- sure that their location isn't closer than 10m to my new parking
@ -684,24 +709,48 @@ function civAir.collectHubs()
end end
function civAir.listTrafficCenters() function civAir.listTrafficCenters()
trigger.action.outText("Traffic Centers", 30) local msg = "Traffic Centers:\n"
local other = false
for idx, aName in pairs(civAir.trafficCenters) do for idx, aName in pairs(civAir.trafficCenters) do
trigger.action.outText(aName, 30) if other then msg = msg .. ", " end
msg = msg .. aName
other = true
end end
trigger.action.outText(msg .. "\n", 30)
msg = "Departure-Only:\n"
other = false
if #civAir.departOnly > 0 then if #civAir.departOnly > 0 then
trigger.action.outText("Departure-Only:", 30)
for idx, aName in pairs(civAir.departOnly) do for idx, aName in pairs(civAir.departOnly) do
trigger.action.outText(aName, 30) if other then msg = msg .. ", " end
msg = msg .. aName
other = true
end end
end end
trigger.action.outText(msg .. "\n", 30)
msg = "Landing-Only:\n"
other = false
if #civAir.landingOnly > 0 then if #civAir.landingOnly > 0 then
trigger.action.outText("Arrival/Landing-Only:", 30)
for idx, aName in pairs(civAir.landingOnly) do for idx, aName in pairs(civAir.landingOnly) do
trigger.action.outText(aName, 30) if other then msg = msg .. ", " end
msg = msg .. aName
other = true
end end
end end
trigger.action.outText(msg .. "\n", 30)
msg = "Closed:\n"
other = false
if #civAir.excludeAirfields > 0 then
for idx, aName in pairs(civAir.excludeAirfields) do
if other then msg = msg .. ", " end
msg = msg .. aName
other = true
end
end
trigger.action.outText(msg .. "\n", 30)
end end
-- start -- start
@ -721,13 +770,27 @@ function civAir.start()
if (#civAir.trafficCenters + #civAir.departOnly < 1) or if (#civAir.trafficCenters + #civAir.departOnly < 1) or
(#civAir.trafficCenters + #civAir.landingOnly < 1) (#civAir.trafficCenters + #civAir.landingOnly < 1)
then then
trigger.action.outText("+++civA: marked traffic centers:" .. #civAir.trafficCenters .. "\n depart-only:" .. #civAir.departOnly .. "\n landing-only: " .. #civAir.landingOnly .. "\n", 30 )
trigger.action.outText("+++civA: auto-populating", 30) trigger.action.outText("+++civA: auto-populating", 30)
-- simply add airfields on the map -- simply add airfields on the map
-- and filter those that are excluded, land-only or depart only
local allBases = dcsCommon.getAirbasesWhoseNameContains("*", 0) local allBases = dcsCommon.getAirbasesWhoseNameContains("*", 0)
for idx, aBase in pairs(allBases) do for idx, aBase in pairs(allBases) do
local afName = aBase:getName() local afName = aBase:getName()
if civAir.departOnlyDict[afName] or
table.insert(civAir.trafficCenters, afName) civAir.landingOnlyDict[afName] or
civAir.excludeAirfieldsDict[afName]
then
trigger.action.outText("+++civA: filtering base " .. afName .. " for single-exception", 30)
else
-- syria special proccing
local rw = aBase:getRunways()
if #rw < 1 then
-- trigger.action.outText(afName .. " has no runways, filtered", 30)
else
table.insert(civAir.trafficCenters, afName)
end
end
end end
end end

View File

@ -1,5 +1,5 @@
convoy = {} convoy = {}
convoy.version = "1.1.0" convoy.version = "1.2.0"
convoy.requiredLibs = { convoy.requiredLibs = {
"dcsCommon", "dcsCommon",
"cfxZones", "cfxZones",
@ -32,7 +32,8 @@ VERSION HISTORY
- warning when not onStart and no start? - warning when not onStart and no start?
- say hi - say hi
- removed destination attribute - removed destination attribute
1.2.0 - convoyMethod
- convoyTriggerMethod
--]]-- --]]--
--[[-- CONVOY Structure --[[-- CONVOY Structure
@ -174,6 +175,9 @@ function convoy.readConvoyZone(theZone)
trigger.action.outText("+++CVY: Warning: Convoy zone <" .. theZone.name .. "> has disabled 'onStart' and has no 'spawn?' input. This convoy zone can't send out any convoys,", 30) trigger.action.outText("+++CVY: Warning: Convoy zone <" .. theZone.name .. "> has disabled 'onStart' and has no 'spawn?' input. This convoy zone can't send out any convoys,", 30)
end end
end end
theZone.convoyMethod = theZone:getStringFromZoneProperty("convoyMethod", "inc")
theZone.convoyTriggerMethod = theZone:getStringFromZoneProperty("convoyTriggerMethod", "change")
--[[-- --[[--
if theZone:hasProperty("destination") then -- remove me if theZone:hasProperty("destination") then -- remove me
theZone.destination = theZone:getStringFromZoneProperty("destination", "none") theZone.destination = theZone:getStringFromZoneProperty("destination", "none")
@ -560,7 +564,7 @@ function convoy.wpReached(gName, convName, idx, wpNum)
convoy.invokeArrivedCallbacks(theConvoy) convoy.invokeArrivedCallbacks(theConvoy)
-- hit the output flag if defined -- hit the output flag if defined
if theZone.arrivedOut then if theZone.arrivedOut then
theZone:pollFlag(theZone.arrivedOut, "inc") theZone:pollFlag(theZone.arrivedOut, theZone.convoyMethod) -- "inc")
end end
-- remove convoy from watchlist -- remove convoy from watchlist
convoy.convoys[convName] = nil convoy.convoys[convName] = nil
@ -845,7 +849,7 @@ function convoy.update()
-- check for flags -- check for flags
for idx, theZone in pairs (convoy.zones) do for idx, theZone in pairs (convoy.zones) do
if theZone.spawnFlag and if theZone.spawnFlag and
theZone:testZoneFlag(theZone.spawnFlag, "change", "lastSpawnFlag") then theZone:testZoneFlag(theZone.spawnFlag, theZone.convoyTriggerMethod, "lastSpawnFlag") then
convoy.startConvoy(theZone) convoy.startConvoy(theZone)
end end
end end
@ -900,7 +904,7 @@ function convoy.statusUpdate() -- every 10 seconds
convoy.invokeAttackedCallbacks(theConvoy) convoy.invokeAttackedCallbacks(theConvoy)
theZone = theConvoy.origin theZone = theConvoy.origin
if theZone.attackedOut then if theZone.attackedOut then
theZone:pollFlag(theZone.attackedOut, "inc") theZone:pollFlag(theZone.attackedOut, theZone.convoyMethod) -- "inc")
end end
end end
@ -909,7 +913,7 @@ function convoy.statusUpdate() -- every 10 seconds
convoy.invokeDestroyedCallbacks(theConvoy) convoy.invokeDestroyedCallbacks(theConvoy)
theZone = theConvoy.origin theZone = theConvoy.origin
if theZone.deadOut then if theZone.deadOut then
theZone:pollFlag(theZone.deadOut, "inc") theZone:pollFlag(theZone.deadOut, theZone.convoyMethod) -- "inc")
end end
trigger.action.outTextForCoalition(theConvoy.coa, "Convoy " .. convName .. " enroute to " .. theConvoy.dest .. " was destroyed.", 30) trigger.action.outTextForCoalition(theConvoy.coa, "Convoy " .. convName .. " enroute to " .. theConvoy.dest .. " was destroyed.", 30)
trigger.action.outSoundForCoalition(theConvoy.coa, convoy.actionSound) trigger.action.outSoundForCoalition(theConvoy.coa, convoy.actionSound)

View File

@ -1,5 +1,5 @@
cfxHeloTroops = {} cfxHeloTroops = {}
cfxHeloTroops.version = "3.1.5" cfxHeloTroops.version = "4.0.0"
cfxHeloTroops.verbose = false cfxHeloTroops.verbose = false
cfxHeloTroops.autoDrop = true cfxHeloTroops.autoDrop = true
cfxHeloTroops.autoPickup = false cfxHeloTroops.autoPickup = false
@ -25,6 +25,9 @@ cfxHeloTroops.requestRange = 500 -- meters
- loadSound and disembarkSound - loadSound and disembarkSound
3.1.4 - guarding destination access in save 3.1.4 - guarding destination access in save
3.1.5 - more guarding of destination access 3.1.5 - more guarding of destination access
4.0.0 - added dropZones
- enforceDropZones
- coalition for drop zones
--]]-- --]]--
@ -37,10 +40,24 @@ cfxHeloTroops.requiredLibs = {
cfxHeloTroops.unitConfigs = {} -- all configs are stored by unit's name cfxHeloTroops.unitConfigs = {} -- all configs are stored by unit's name
cfxHeloTroops.troopWeight = 100 -- kg average weight per trooper cfxHeloTroops.troopWeight = 100 -- kg average weight per trooper
cfxHeloTroops.dropZones = {}
-- persistence support -- persistence support
cfxHeloTroops.deployedTroops = {} cfxHeloTroops.deployedTroops = {}
--
-- drop zones
--
function cfxHeloTroops.processDropZone(theZone)
theZone.droppedFlag = theZone:getStringFromZoneProperty("dropZone!", "cfxNone")
theZone.dropMethod = theZone:getStringFromZoneProperty("dropMethod", "inc")
theZone.dropCoa = theZone:getCoalitionFromZoneProperty("coalition", 0)
theZone.autoDespawn = theZone:getNumberFromZoneProperty("autoDespawn", -1)
end
--
-- comms
--
function cfxHeloTroops.resetConfig(conf) function cfxHeloTroops.resetConfig(conf)
conf.autoDrop = cfxHeloTroops.autoDrop --if true, will drop troops on-board upon touchdown conf.autoDrop = cfxHeloTroops.autoDrop --if true, will drop troops on-board upon touchdown
conf.autoPickup = cfxHeloTroops.autoPickup -- if true will load nearest troops upon touchdown conf.autoPickup = cfxHeloTroops.autoPickup -- if true will load nearest troops upon touchdown
@ -576,16 +593,33 @@ function cfxHeloTroops.scoreWhenCapturing(theUnit)
end end
end end
function cfxHeloTroops.isInsideDropZone(theUnit)
local p = theUnit:getPoint()
for idx, theZone in pairs (cfxHeloTroops.dropZones) do
if theZone:isPointInsideZone(p) then return true end
end
return false
end
function cfxHeloTroops.doDeployTroops(args) function cfxHeloTroops.doDeployTroops(args)
local conf = args[1] local conf = args[1]
local what = args[2] local what = args[2]
-- deploy the troops I have on board in formation local theUnit = conf.unit
local theGroup = theUnit:getGroup()
local gid = theGroup:getID()
local inside = cfxHeloTroops.isInsideDropZone(theUnit)
if (not inside) and cfxHeloTroops.enforceDropZones then
trigger.action.outTextForGroup(gid, "You are outside an disembark/drop zone.", 30)
return
end
-- deploy the troops I have on board
cfxHeloTroops.deployTroopsFromHelicopter(conf) cfxHeloTroops.deployTroopsFromHelicopter(conf)
-- interface with playerscore if we dropped -- interface with playerscore if we dropped
-- inside an enemy-owned zone -- inside an enemy-owned zone
if cfxPlayerScore and cfxOwnedZones then if cfxPlayerScore and cfxOwnedZones then
local theUnit = conf.unit --local theUnit = conf.unit
cfxHeloTroops.scoreWhenCapturing(theUnit) cfxHeloTroops.scoreWhenCapturing(theUnit)
end end
@ -630,7 +664,7 @@ function cfxHeloTroops.deployTroopsFromHelicopter(conf)
end end
local chopperZone = cfxZones.createSimpleZone("choppa", p, 12) -- 12 m radius around choppa local chopperZone = cfxZones.createSimpleZone("choppa", p, 12) -- 12 m radius around choppa
local theCoalition = theUnit:getGroup():getCoalition() -- make it choppers COALITION local theCoalition = theUnit:getGroup():getCoalition() -- make it chopper's COALITION
local theGroup, theData = cfxZones.createGroundUnitsInZoneForCoalition ( local theGroup, theData = cfxZones.createGroundUnitsInZoneForCoalition (
theCoalition, theCoalition,
theName, -- group name, may be tracked theName, -- group name, may be tracked
@ -677,8 +711,39 @@ function cfxHeloTroops.deployTroopsFromHelicopter(conf)
end end
end end
end end
-- bang on all dropZones that we can find
for name, theZone in pairs(cfxHeloTroops.dropZones) do
-- can employ coalition test here as well, maybe later?
if theZone:isPointInsideZone(p) then
if theZone.dropCoa == 0 or theCoalition == theZone.dropCoa then
if cfxHeloTroops.verbose or theZone.verbose then
trigger.action.outText("+++Helo: will bang! on dropZone <" .. theZone.name .. "> output dropZone! <" .. theZone.droppedFlag .. "> with method <" .. theZone.dropMethod .. ">", 30)
end
theZone:pollFlag(theZone.droppedFlag, theZone.dropMethod)
end
if theZone.autoDespawn and theZone.autoDespawn > 0 then
args = {}
args.theZone = theZone
args.theGroup = theGroup
timer.scheduleFunction(cfxHeloTroops.autoDespawn, args, timer.getTime() + theZone.autoDespawn)
end
end
end
end end
function cfxHeloTroops.autoDespawn(args)
if not args then return end
local theZone = args.theZone
local theGroup = args.theGroup
if theZone.verbose then
trigger.action.outText("+++Helo: auto-despawning drop in drop zone <" .. theZone.name .. ">", 30)
end
if not theGroup then return end
if Group.isExist(theGroup) then
Group.destroy(theGroup)
end
end
-- --
-- Loading Troops -- Loading Troops
-- --
@ -882,7 +947,7 @@ function cfxHeloTroops.readConfigZone()
theZone = cfxZones.createSimpleZone("heloTroopsConfig") theZone = cfxZones.createSimpleZone("heloTroopsConfig")
end end
cfxHeloTroops.verbose = theZone:getBoolFromZoneProperty("verbose", false) cfxHeloTroops.verbose = theZone.verbose
if theZone:hasProperty("legalTroops") then if theZone:hasProperty("legalTroops") then
local theTypesString = theZone:getStringFromZoneProperty("legalTroops", "") local theTypesString = theZone:getStringFromZoneProperty("legalTroops", "")
@ -907,6 +972,7 @@ function cfxHeloTroops.readConfigZone()
cfxHeloTroops.disembarkSound = theZone:getStringFromZoneProperty("disembarkSound", cfxHeloTroops.actionSound) cfxHeloTroops.disembarkSound = theZone:getStringFromZoneProperty("disembarkSound", cfxHeloTroops.actionSound)
cfxHeloTroops.requestRange = theZone:getNumberFromZoneProperty("requestRange", 500) cfxHeloTroops.requestRange = theZone:getNumberFromZoneProperty("requestRange", 500)
cfxHeloTroops.enforceDropZones = theZone:getBoolFromZoneProperty("enforceDropZones", false)
-- add own troop carriers -- add own troop carriers
if theZone:hasProperty("troopCarriers") then if theZone:hasProperty("troopCarriers") then
local tc = theZone:getStringFromZoneProperty("troopCarriers", "UH-1D") local tc = theZone:getStringFromZoneProperty("troopCarriers", "UH-1D")
@ -999,6 +1065,13 @@ function cfxHeloTroops.start()
-- read config zone -- read config zone
cfxHeloTroops.readConfigZone() cfxHeloTroops.readConfigZone()
-- read drop zones
local attrZones = cfxZones.getZonesWithAttributeNamed("dropZone!")
for k, aZone in pairs(attrZones) do
cfxHeloTroops.processDropZone(aZone)
cfxHeloTroops.dropZones[aZone.name] = aZone
end
-- start housekeeping -- start housekeeping
cfxHeloTroops.houseKeeping() cfxHeloTroops.houseKeeping()

View File

@ -1,5 +1,5 @@
cfxPlayerScore = {} cfxPlayerScore = {}
cfxPlayerScore.version = "3.3.1" cfxPlayerScore.version = "4.0.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"
@ -18,7 +18,9 @@ cfxPlayerScore.firstSave = true -- to force overwrite
3.3.0 - case INsensitivity for all typeScore objects 3.3.0 - case INsensitivity for all typeScore objects
3.3.1 - fixes for DCS oddity in events after update 3.3.1 - fixes for DCS oddity in events after update
- cleanup - cleanup
4.0.0 - own event handling, disco from dcsCommon
- early landing detection (unitSpawnTime)
TODO: Kill event no longer invoked for map objetcs, attribute TODO: Kill event no longer invoked for map objetcs, attribute
to faction now, reverse invocation direction with PlayerScore to faction now, reverse invocation direction with PlayerScore
--]]-- --]]--
@ -51,7 +53,7 @@ cfxPlayerScore.train = 5
cfxPlayerScore.landing = 0 -- if > 0 it scores as feat cfxPlayerScore.landing = 0 -- if > 0 it scores as feat
cfxPlayerScore.unit2player = {} -- lookup and reverse look-up cfxPlayerScore.unit2player = {} -- lookup and reverse look-up
cfxPlayerScore.unitSpawnTime = {} -- lookup by unit name to prevent early landing
function cfxPlayerScore.addSafeZone(theZone) function cfxPlayerScore.addSafeZone(theZone)
theZone.scoreSafe = theZone:getCoalitionFromZoneProperty("scoreSafe", 0) theZone.scoreSafe = theZone:getCoalitionFromZoneProperty("scoreSafe", 0)
table.insert(cfxPlayerScore.safeZones, theZone) table.insert(cfxPlayerScore.safeZones, theZone)
@ -570,7 +572,7 @@ function cfxPlayerScore.awardScoreTo(killSide, theScore, killerName)
end end
-- --
-- EVENT HANDLING -- EVENT PROCESSING / HANDLING
-- --
function cfxPlayerScore.linkUnitWithPlayer(theUnit) function cfxPlayerScore.linkUnitWithPlayer(theUnit)
-- create the entries for lookup and reverseLooup tables -- create the entries for lookup and reverseLooup tables
@ -583,7 +585,7 @@ function cfxPlayerScore.unlinkUnit(theUnit)
local uName = theUnit:getName() local uName = theUnit:getName()
cfxPlayerScore.unit2player[uName] = nil cfxPlayerScore.unit2player[uName] = nil
end end
--[[--
function cfxPlayerScore.preProcessor(theEvent) function cfxPlayerScore.preProcessor(theEvent)
-- return true if the event should be processed -- return true if the event should be processed
-- by us -- by us
@ -721,10 +723,13 @@ function cfxPlayerScore.preProcessor(theEvent)
return false return false
end end
--]]--
--[[--
function cfxPlayerScore.postProcessor(theEvent) function cfxPlayerScore.postProcessor(theEvent)
-- don't do anything -- don't do anything
end end
--]]--
function cfxPlayerScore.isStaticObject(theUnit) function cfxPlayerScore.isStaticObject(theUnit)
if not theUnit.getGroup then return true end if not theUnit.getGroup then return true end
@ -1091,7 +1096,165 @@ function cfxPlayerScore.handlePlayerDeath(theEvent)
end end
end end
function cfxPlayerScore.handlePlayerEvent(theEvent) --
-- event detection
--
function cfxPlayerScore.isScoreEvent(theEvent)
-- return true if the event results in a score event
if theEvent.initiator == nil then
return false
end
if cfxPlayerScore.verbose then
trigger.action.outText("Event preproc: " .. theEvent.id .. " (" .. dcsCommon.event2text(theEvent.id) .. ")", 30)
if theEvent.id == 8 or theEvent.id == 30 then -- dead or lost event
local who = theEvent.initiator
local name = "(nil ini)"
if who then
name = "(inval object)"
if who.getName then name = who:getName() end
end
trigger.action.outText("Dead/Lost subject: <" .. name .. ">", 30)
end
if theEvent.id == 2 then -- hit
local who = theEvent.initiator
local name = "(nil ini)"
if who then
name = "(inval initi)"
if who.getName then name = who:getName() end
if not name then -- WTF??? could be a weapon
name = "!nil getName!"
if who.getTypeName then name = who:getTypeName() end
if not name then
name = "WTFer"
end
end
end
local hit = theEvent.object
local hname = "(nil ini)"
if hit then
hname = "(inval object)"
if hit.getName then hname = hit:getName() end
end
trigger.action.outText("o:<" .. name .. "> hit <" .. hname .. ">", 30)
end
end
-- check if this was FORMERLY a player plane
local theUnit = theEvent.initiator
if not theUnit.getName then return end -- fix for DCS update bug
local uName = theUnit:getName()
if cfxPlayerScore.unit2player[uName] then
-- this requires special IMMEDIATE handling when event is
-- one of the below
if theEvent.id == 5 or -- crash
theEvent.id == 8 or -- dead
theEvent.id == 9 or -- pilot_dead
theEvent.id == 30 or -- unit loss
theEvent.id == 6 then -- eject
-- these can lead to a pilot demerit
-- event does NOT have a player
cfxPlayerScore.handlePlayerDeath(theEvent)
return false -- false = no score event (any more)
end
end
-- from here on, initiator must be player
if not theUnit.getPlayerName or
not theUnit:getPlayerName() then
return false
end
if theEvent.id == 28 then -- kill, but only with target
local killer = theEvent.initiator
if not theEvent.target then
if cfxPlayerScore.verbose then
trigger.action.outText("+++scr kill nil TARGET", 30)
end
return false
end
-- if there are kill zones, we filter all kills that happen outside of kill zones
if #cfxPlayerScore.killZones > 0 then
local pLoc = theUnit:getPoint()
local tLoc = theEvent.target:getPoint()
local isIn, percent, dist, theZone = cfxZones.pointInOneOfZones(tLoc, cfxPlayerScore.killZones)
if not isIn then
if cfxPlayerScore.verbose then
trigger.action.outText("+++pScr: kill detected, but target <" .. theEvent.target:getName() .. "> was outside of any kill zones", 30)
end
return false
end
if theZone.duet and not cfxZones.pointInZone(pLoc, theZone) then
-- player must be in same zone but was not
if cfxPlayerScore.verbose then
trigger.action.outText("+++pScr: kill detected, but player <" .. theUnit:getPlayerName() .. "> was outside of kill zone <" .. theZone.name .. ">", 30)
end
return false
end
end
return true
end
-- birth event for players initializes score if
-- not existed, and nils the queue
if theEvent.id == 15 then -- player birth
-- link player and unit
cfxPlayerScore.linkUnitWithPlayer(theUnit)
cfxPlayerScore.unitSpawnTime[uName] = timer.getTime() -- to detect 'early landing'
-- trigger.action.outText("Birth event", 30)
return true
end
-- take off. overwrites timestamp for last landing
-- so a blipping t/o does nor count. Pre-proc only
if theEvent.id == 3 or theEvent.id == 54 then
local now = timer.getTime()
local playerName = theUnit:getPlayerName()
cfxPlayerScore.lastPlayerLanding[playerName] = now -- overwrite
return false
end
-- landing can score. but only the first landing in x seconds
-- and has spawned more than 10 seconds before
-- landing in safe zone promotes any queued scores to
-- permanent if enabled, then nils queue
if theEvent.id == 4 or theEvent.id == 55 then
-- trigger.action.outText("LANDING event", 30)
-- player landed. filter multiple landed events
local now = timer.getTime()
local playerName = theUnit:getPlayerName()
-- if player spawns on ground, DCS now can post a
-- "landing" event. filter
if cfxPlayerScore.unitSpawnTime[uName] and
now - cfxPlayerScore.unitSpawnTime[uName] < 10
then
cfxPlayerScore.lastPlayerLanding[playerName] = now -- just for the sake of it
-- trigger.action.outText("(DCS early landing bug ignored)", 30)
return false
end
-- trigger.action.outText("Time since spawn: " .. now - cfxPlayerScore.unitSpawnTime[uName], 30)
local lastLanding = cfxPlayerScore.lastPlayerLanding[playerName]
cfxPlayerScore.lastPlayerLanding[playerName] = now -- overwrite
if lastLanding and lastLanding + cfxPlayerScore.delayBetweenLandings > now then
if cfxPlayerScore.verbose then
trigger.action.outText("+++pScr: Player <" .. playerName .. "> touch-down ignored: too soon after last.", 30)
trigger.action.outText("now is <" .. now .. ">, between is <" .. cfxPlayerScore.delayBetweenLandings .. ">, last + between is <" .. lastLanding + cfxPlayerScore.delayBetweenLandings .. ">", 30)
end
-- filter this event, too soon
return false
end
return true
end
return false
end
function cfxPlayerScore:onEvent(theEvent)
if cfxPlayerScore.isScoreEvent(theEvent) then
cfxPlayerScore.handleScoreEvent(theEvent)
end
end
function cfxPlayerScore.handleScoreEvent(theEvent)
if theEvent.id == 28 then if theEvent.id == 28 then
-- kill from player detected. -- kill from player detected.
cfxPlayerScore.killDetected(theEvent) cfxPlayerScore.killDetected(theEvent)
@ -1134,7 +1297,9 @@ function cfxPlayerScore.handlePlayerEvent(theEvent)
end end
end end
end end
--
-- Config handling
--
function cfxPlayerScore.readConfigZone(theZone) function cfxPlayerScore.readConfigZone(theZone)
cfxPlayerScore.verbose = theZone.verbose cfxPlayerScore.verbose = theZone.verbose
-- default scores -- default scores
@ -1450,9 +1615,10 @@ function cfxPlayerScore.start()
end end
-- subscribe to events and use dcsCommon's handler structure -- subscribe to events and use dcsCommon's handler structure
dcsCommon.addEventHandler(cfxPlayerScore.handlePlayerEvent, -- dcsCommon.addEventHandler(cfxPlayerScore.handlePlayerEvent,
cfxPlayerScore.preProcessor, -- cfxPlayerScore.preProcessor,
cfxPlayerScore.postProcessor) -- cfxPlayerScore.postProcessor)
world.addEventHandler(cfxPlayerScore)
-- now load all save data and populate map with troops that -- now load all save data and populate map with troops that
-- we deployed last save. -- we deployed last save.
if persistence then if persistence then

View File

@ -21,6 +21,7 @@ radioMenu.lateGroups = {} -- dict by ID
- added dynamic player support - added dynamic player support
4.0.1 - MX no longer optional, so ask for it 4.0.1 - MX no longer optional, so ask for it
4.0.2 - ackSnd now also broadcasts to all if no group 4.0.2 - ackSnd now also broadcasts to all if no group
4.1.0 - outX --> valX verification. putting back in optional outX
--]]-- --]]--
function radioMenu.addRadioMenu(theZone) function radioMenu.addRadioMenu(theZone)
@ -437,6 +438,11 @@ function radioMenu.createRadioMenuWithZone(theZone)
if theZone:hasProperty("radioMethod") then if theZone:hasProperty("radioMethod") then
theZone.radioMethod = theZone:getStringFromZoneProperty( "radioMethod", "inc") theZone.radioMethod = theZone:getStringFromZoneProperty( "radioMethod", "inc")
end end
-- note: outX currently overridden with valX
theZone.outMethA = theZone:getStringFromZoneProperty("outA", theZone.radioMethod)
theZone.outMethB = theZone:getStringFromZoneProperty("outB", theZone.radioMethod)
theZone.outMethC = theZone:getStringFromZoneProperty("outC", theZone.radioMethod)
theZone.outMethD = theZone:getStringFromZoneProperty("outD", theZone.radioMethod)
theZone.radioTriggerMethod = theZone:getStringFromZoneProperty("radioTriggerMethod", "change") theZone.radioTriggerMethod = theZone:getStringFromZoneProperty("radioTriggerMethod", "change")
@ -731,6 +737,7 @@ function radioMenu.doMenuX(args)
local outVal = theZone.outValA local outVal = theZone.outValA
local ack = theZone.ackA local ack = theZone.ackA
local ackSnd = theZone.ackASnd local ackSnd = theZone.ackASnd
local meth = theZone.outMethA -- currently not used
-- decode A..X -- decode A..X
if theItemIndex == "B"then if theItemIndex == "B"then
@ -740,6 +747,7 @@ function radioMenu.doMenuX(args)
outVal = theZone.outValB outVal = theZone.outValB
ack = theZone.ackB ack = theZone.ackB
ackSnd = theZone.ackBSnd ackSnd = theZone.ackBSnd
meth = theZone.outMethB
elseif theItemIndex == "C" then elseif theItemIndex == "C" then
cd = radioMenu.cdByGID(theZone.mcdC, theZone, theGroup) -- theZone.mcdC cd = radioMenu.cdByGID(theZone.mcdC, theZone, theGroup) -- theZone.mcdC
busy = theZone.busyC busy = theZone.busyC
@ -747,6 +755,7 @@ function radioMenu.doMenuX(args)
outVal = theZone.outValC outVal = theZone.outValC
ack = theZone.ackC ack = theZone.ackC
ackSnd = theZone.ackCSnd ackSnd = theZone.ackCSnd
meth = theZone.outMethC
elseif theItemIndex == "D" then elseif theItemIndex == "D" then
cd = radioMenu.cdByGID(theZone.mcdD, theZone, theGroup) -- theZone.mcdD cd = radioMenu.cdByGID(theZone.mcdD, theZone, theGroup) -- theZone.mcdD
busy = theZone.busyD busy = theZone.busyD
@ -754,6 +763,7 @@ function radioMenu.doMenuX(args)
outVal = theZone.outValD outVal = theZone.outValD
ack = theZone.ackD ack = theZone.ackD
ackSnd = theZone.ackDSnd ackSnd = theZone.ackDSnd
meth = theZone.outMethD
end end
-- see if we are on cooldown -- see if we are on cooldown
@ -793,6 +803,7 @@ function radioMenu.doMenuX(args)
-- poll flag, override with outVal if set -- poll flag, override with outVal if set
if outVal then if outVal then
--outVal = "#"..outVal -- we force immediate mode --outVal = "#"..outVal -- we force immediate mode
-- now replaced by 'valX' attribute
theZone:pollFlag(theFlag, outVal) theZone:pollFlag(theFlag, outVal)
if theZone.verbose or radioMenu.verbose then if theZone.verbose or radioMenu.verbose then
trigger.action.outText("+++menu: overriding index " .. theItemIndex .. " output method <" .. theZone.radioMethod .. "> with immediate value <" .. outVal .. ">", 30) trigger.action.outText("+++menu: overriding index " .. theItemIndex .. " output method <" .. theZone.radioMethod .. "> with immediate value <" .. outVal .. ">", 30)

View File

@ -1,5 +1,5 @@
debugger = {} debugger = {}
debugger.version = "3.0.0" debugger.version = "3.1.0"
debugDemon = {} debugDemon = {}
debugDemon.version = "2.1.0" debugDemon.version = "2.1.0"
@ -45,7 +45,8 @@ debugger.log = ""
- x *f - x *f
- x *z - x *z
- x ? - x ?
3.1.0 - new DCS world events 57-61
- x ! xref hanging/unconnected
--]]-- --]]--
@ -122,7 +123,11 @@ debugDemon.eventList = {
["54"] = "S_EVENT_MISSION_WINNER = 54", ["54"] = "S_EVENT_MISSION_WINNER = 54",
["55"] = "S_EVENT_POSTPONED_TAKEOFF = 55", ["55"] = "S_EVENT_POSTPONED_TAKEOFF = 55",
["56"] = "S_EVENT_POSTPONED_LAND = 56", ["56"] = "S_EVENT_POSTPONED_LAND = 56",
["57"] = "S_EVENT_MAX = 57", ["57"] = "S_EVENT_SIMULATION_FREEZE = 57",
["58"] = "S_EVENT_SIMULATION_UNFREEZE = 58",
["59"] = "S_EVENT_HUMAN_AIRCRAFT_REPAIR_START = 59",
["60"] = "S_EVENT_HUMAN_AIRCRAFT_REPAIR_FINISH = 60",
["61"] = "S_EVENT_MAX = 61",
} }
debugger.spawnTypes = { debugger.spawnTypes = {
@ -150,7 +155,7 @@ debugger.spawnTypes = {
-- XREF MODULE -- XREF MODULE
-- --
xref = {} xref = {}
xref.version = "1.0.0" xref.version = "1.1.0"
xref.dmlObjects = {} -- dict by zone name:upper() xref.dmlObjects = {} -- dict by zone name:upper()
-- has inputs: dict of string for each '?' input, contains input flag name -- has inputs: dict of string for each '?' input, contains input flag name
-- has output: dict of array for each output '!', contains output flag names as array -- has output: dict of array for each output '!', contains output flag names as array
@ -158,7 +163,7 @@ xref.flags = {} -- dict by flag name
-- has froms: dict of zone of attributes that can write to this flags -- has froms: dict of zone of attributes that can write to this flags
-- has tos: dict of zones of attributes that read from flag -- has tos: dict of zones of attributes that read from flag
function xref.getDmlObject(name) function xref.getDmlObject(name) -- lazy alloc
name = name:upper() name = name:upper()
local theObject = xref.dmlObjects[name] local theObject = xref.dmlObjects[name]
if not theObject then if not theObject then
@ -170,7 +175,7 @@ function xref.getDmlObject(name)
return theObject return theObject
end end
function xref.getFlag(name, theZone) function xref.getFlag(name, theZone) -- lazy alloc
if theZone and dcsCommon.stringStartsWith(name, "*") then if theZone and dcsCommon.stringStartsWith(name, "*") then
-- local name conversion -- local name conversion
name = theZone.name .. name name = theZone.name .. name
@ -178,8 +183,8 @@ function xref.getFlag(name, theZone)
local theFlag = xref.flags[name] local theFlag = xref.flags[name]
if not theFlag then if not theFlag then
theFlag = {} theFlag = {}
theFlag.froms = {} -- dict by zone name/output that write to this flag theFlag.froms = {} -- dict by zone name: OUTPUTS that write to this flag
theFlag.tos = {} -- dict by zone name / inputs that reads from this flag theFlag.tos = {} -- dict by zone name: INPUTS that read from this flag
xref.flags[name] = theFlag xref.flags[name] = theFlag
end end
return theFlag return theFlag
@ -236,20 +241,21 @@ function xref.scanMissionZones()
-- iterate all properties -- iterate all properties
local attributes = theZone:getAllZoneProperties() local attributes = theZone:getAllZoneProperties()
for name, value in pairs(attributes) do for name, value in pairs(attributes) do
local n2 = dcsCommon.trim(name)
-- find inputs -- find inputs
if dcsCommon.stringEndsWith(name, "?") then if dcsCommon.stringEndsWith(n2, "?") then
xref.addInput(theZone, name, value) xref.addInput(theZone, n2, value)
end end
-- find outputs -- find outputs
if dcsCommon.stringEndsWith(name, "!") then if dcsCommon.stringEndsWith(n2, "!") then
xref.addOutput(theZone, name, value) xref.addOutput(theZone, n2, value)
end end
-- other stuff, e.g. "#" -- other stuff, e.g. "#"
-- find outputs -- find outputs
if dcsCommon.stringEndsWith(name, "#") then if dcsCommon.stringEndsWith(n2, "#") then
xref.addOutput(theZone, name, value) xref.addOutput(theZone, n2, value)
end end
end end
end end
@ -289,8 +295,6 @@ function xref.xrefFlag(name)
end end
end end
end end
-- trigger.action.outText(msg, 30)
return msg return msg
end end
@ -323,9 +327,7 @@ function xref.xrefZone(name, theObject)
end end
end end
-- trigger.action.outText(msg, 30) return msg
return msg
end end
function xref.xrefName(name) function xref.xrefName(name)
@ -362,41 +364,93 @@ function xref.allZones()
end end
function xref.xall() function xref.xall()
msg = "" local msg = ""
-- now dump all flags -- now dump all flags
for flagName, data in pairs (xref.flags) do for flagName, data in pairs (xref.flags) do
-- msg = msg .. xref.xrefFlag(flagName)
msg = msg .. xref.xrefName(flagName) msg = msg .. xref.xrefName(flagName)
end end
-- dump all zones -- dump all zones
for zoneName, data in pairs(xref.dmlObjects) do for zoneName, data in pairs(xref.dmlObjects) do
-- msg = msg .. xref.xrefZone(zoneName, data)
msg = msg .. xref.xrefName(zoneName) msg = msg .. xref.xrefName(zoneName)
end end
return msg return msg
-- trigger.action.outText(msg, 30)
end end
function xref.unconnected()
local msg = "xref: unconnected flags/commands:\n"
local badFroms = {}
local badTos = {}
for name, theFlag in pairs(xref.flags) do
-- look for single-direction flags, i.e those that have
-- no froms or no tos.
if dcsCommon.getSizeOfTable(theFlag.froms) < 1 then
-- this flag has output referencing it, so the input hangs
badFroms[name] = theFlag
end
if dcsCommon.getSizeOfTable(theFlag.tos) < 1 then
-- this flag has output referencing it, so the input hangs
badTos[name] = theFlag
end
end
local hasUnconnected = false
if dcsCommon.getSizeOfTable(badFroms) > 0 then
msg = msg .. "<Flag/Command> used at [zone]:INPUT that listens for/to nothing:\n"
for name, theFlag in pairs(badFroms) do
msg = msg .. " <" .. name .. ">: "
local froms = theFlag.tos -- crossing from and to here!
for zName, outList in pairs(froms) do
msg = msg .. "[" .. zName .. "]:"
local c = 0
for idx, outName in pairs(outList) do
if c > 0 then msg = msg .. ", " end
c = 1
msg = msg .. outName
end
msg = msg .. "\n"
end
end
msg = msg .. " -- END OF LIST --\n\n"
hasUnconnected = true
end
if dcsCommon.getSizeOfTable(badTos) > 0 then
msg = msg .. "<Flag/Command> used at [zone]:OUTPUT that sends to nobody:\n"
for name, theFlag in pairs(badTos) do
msg = msg .. " <" .. name .. ">: "
local tos = theFlag.froms -- crossing!
for zName, inList in pairs(tos) do
msg = msg .. "[" .. zName .. "]:"
local c = 0
for idx, inName in pairs(inList) do
if c > 0 then msg = msg .. ", " end
c = 1
msg = msg .. inName
end
end
msg = msg .. "\n"
end
hasUnconnected = true
msg = msg .. " -- END OF LIST --\n\n"
end
if not hasUnconnected then
msg = msg .. "\n -- NONE --\n"
end
return msg
end
function xref.start() function xref.start()
xref.scanMissionZones() xref.scanMissionZones()
local flagNum = dcsCommon.getSizeOfTable(xref.flags) local flagNum = dcsCommon.getSizeOfTable(xref.flags)
local dmlObNum = dcsCommon.getSizeOfTable(xref.dmlObjects) local dmlObNum = dcsCommon.getSizeOfTable(xref.dmlObjects)
trigger.action.outText("XRef v" .. xref.version .. " full DML object scan on mission complete:\n<" .. flagNum .. "> flags are referenced in <" .. dmlObNum .. "> DML zones", 30) trigger.action.outText("XRef v" .. xref.version .. " full DML object scan on mission complete:\n<" .. flagNum .. "> flags are referenced in <" .. dmlObNum .. "> DML zones", 30)
-- trigger.action.outText(xref.xall(), 30)
end end
-- run the xref -- run the xref
xref.start() xref.start()
--[[--
to do
scan messenger and wildcards for flag access
--]]--
-- --
-- DEBUGGER MAIn -- DEBUGGER MAIn
-- --
@ -436,7 +490,6 @@ function debugger.saveLog(name)
end end
end end
-- --
-- tracking flags -- tracking flags
-- --
@ -764,7 +817,6 @@ function debugger.update()
end end
end end
-- --
-- Config & Start -- Config & Start
-- --
@ -950,7 +1002,6 @@ function debugDemon:onEvent(theEvent)
end end
if theEvent.id == world.event.S_EVENT_MARK_CHANGE then if theEvent.id == world.event.S_EVENT_MARK_CHANGE then
-- trigger.action.outText("debugger: Mark Change event received", 30)
-- when changed, the mark's text is examined for a command -- when changed, the mark's text is examined for a command
-- if it starts with the 'mark' string ("-" by default) it is processed -- if it starts with the 'mark' string ("-" by default) it is processed
-- by the command processor -- by the command processor
@ -958,42 +1009,26 @@ function debugDemon:onEvent(theEvent)
-- else an error is displayed and the mark remains. -- else an error is displayed and the mark remains.
if debugDemon.hasMark(theEvent.text) then if debugDemon.hasMark(theEvent.text) then
-- strip the mark -- strip the mark
local cCommand = dcsCommon.clone(theEvent.text, true) local cCommand = dcsCommon.clone(theEvent.text)
local commandString = cCommand:sub(1+debugDemon.markOfDemon:len()) local commandString = cCommand:sub(1+debugDemon.markOfDemon:len())
-- break remainder apart into <command> <arg1> ... <argn> -- break remainder apart into <command> <arg1> ... <argn>
local commands = dcsCommon.splitString(commandString, debugDemon.splitDelimiter) local commands = dcsCommon.splitString(commandString, debugDemon.splitDelimiter)
-- this is a command. process it and then remove it if it was executed successfully -- this is a command. process it and then remove it if it was executed successfully
local cTheEvent = dcsCommon.clone(theEvent, true) -- strip meta tables local cTheEvent = dcsCommon.clone(theEvent) -- strip meta tables
local args = {commands, cTheEvent} local args = {commands, cTheEvent}
-- defer execution for 0.1s to get out of trx bracked -- defer execution for 0.1s to get out of trx bracked
timer.scheduleFunction(debugDemon.deferredDebug, args, timer.getTime() + 0.1) timer.scheduleFunction(debugDemon.deferredDebug, args, timer.getTime() + 0.1)
debugDemon.activeIdx = cTheEvent.idx debugDemon.activeIdx = cTheEvent.idx
--[[--
local success = debugDemon.executeCommand(commands, cTheEvent) -- execute on a clone, not original
-- remove this mark after successful execution
if success then
trigger.action.removeMark(theEvent.idx)
else
-- we could play some error sound
end
--]]--
end end
end end
if theEvent.id == world.event.S_EVENT_MARK_REMOVED then if theEvent.id == world.event.S_EVENT_MARK_REMOVED then
-- trigger.action.outText("Mark Remove received, removing idx <" .. theEvent.idx .. ">.", 30)
debugDemon.activeIdx = nil debugDemon.activeIdx = nil
end end
end end
function debugDemon.deferredDebug(args) function debugDemon.deferredDebug(args)
-- trigger.action.outText("enter deferred debug command", 30)
-- if not debugDemon.activeIdx then
-- trigger.action.outText("Debugger: window was closed, debug command ignored.", 30)
-- return
-- end
local commands = args[1] local commands = args[1]
local cTheEvent = args[2] local cTheEvent = args[2]
local success = debugDemon.executeCommand(commands, cTheEvent) -- execute on a clone, not original local success = debugDemon.executeCommand(commands, cTheEvent) -- execute on a clone, not original
@ -1884,8 +1919,11 @@ function debugDemon.processXrefCommand(args, event)
if not larg or larg == "" then larg = "?" end if not larg or larg == "" then larg = "?" end
larg = larg:lower() larg = larg:lower()
if larg == "?" then if larg == "?" then
debugger.outText("*** xRef: ? = help (this), * = xref all, *f = list all DML flags, *z = list all DML zones, <name> xref flag or zone", 30) debugger.outText("*** xRef: ? = help (this), ! = xref all 'hanging' or unconnected flags/commands, * = xref all, *f = list all DML flags, *z = list all DML zones, <name> xref flag or zone", 30)
return true -- leave up return true -- leave up
elseif larg == "!" then
debugger.outText(xref.unconnected(), 30)
return true
elseif larg == "*" then elseif larg == "*" then
debugger.outText(xref.xall(), 30) debugger.outText(xref.xall(), 30)
return true return true

Binary file not shown.

Binary file not shown.