mirror of
https://github.com/weyne85/DML.git
synced 2025-10-29 16:57:49 +00:00
Version 1.1.0
Persistence
This commit is contained in:
parent
780797aba3
commit
f741c511a5
Binary file not shown.
Binary file not shown.
@ -1,5 +1,5 @@
|
||||
rndFlags = {}
|
||||
rndFlags.version = "1.3.1"
|
||||
rndFlags.version = "1.3.2"
|
||||
rndFlags.verbose = false
|
||||
rndFlags.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
@ -29,8 +29,10 @@ rndFlags.requiredLibs = {
|
||||
- added zonal verbosity
|
||||
- added 'rndDone!' flag
|
||||
- rndMethod defaults to "inc"
|
||||
1.3.2 - moved flagArrayFromString to dcsCommon
|
||||
|
||||
--]]
|
||||
|
||||
rndFlags.rndGen = {}
|
||||
|
||||
function rndFlags.addRNDZone(aZone)
|
||||
@ -38,6 +40,8 @@ function rndFlags.addRNDZone(aZone)
|
||||
end
|
||||
|
||||
function rndFlags.flagArrayFromString(inString)
|
||||
return dcsCommon.flagArrayFromString(inString, rndFlags.verbose)
|
||||
--[[--
|
||||
if string.len(inString) < 1 then
|
||||
trigger.action.outText("+++RND: empty flags", 30)
|
||||
return {}
|
||||
@ -88,6 +92,7 @@ function rndFlags.flagArrayFromString(inString)
|
||||
trigger.action.outText("+++RND: <" .. #flags .. "> flags total", 30)
|
||||
end
|
||||
return flags
|
||||
--]]--
|
||||
end
|
||||
|
||||
--
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxCargoReceiver = {}
|
||||
cfxCargoReceiver.version = "1.2.1"
|
||||
cfxCargoReceiver.version = "1.2.2"
|
||||
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.requiredLibs = {
|
||||
@ -17,6 +17,9 @@ cfxCargoReceiver.requiredLibs = {
|
||||
- 1.2.0 method
|
||||
f!, cargoReceived!
|
||||
- 1.2.1 cargoMethod
|
||||
- 1.2.2 removed deprecated functions
|
||||
corrected pollFlag bug (not passing zone along)
|
||||
distance to receiver is given as distance to zone boundary
|
||||
|
||||
|
||||
CargoReceiver is a zone enhancement you use to be automatically
|
||||
@ -39,13 +42,15 @@ function cfxCargoReceiver.processReceiverZone(aZone) -- process attribute and ad
|
||||
-- isCargoReceiver flag and we are good
|
||||
aZone.isCargoReceiver = true
|
||||
-- we can add additional processing here
|
||||
aZone.autoRemove = cfxZones.getBoolFromZoneProperty(aZone, "autoRemove", false) -- maybe add a removedelay
|
||||
|
||||
aZone.autoRemove = cfxZones.getBoolFromZoneProperty(aZone, "autoRemove", false) -- maybe add a removeDelay
|
||||
aZone.removeDelay = cfxZones.getNumberFromZoneProperty(aZone, "removeDelay", 1)
|
||||
if aZone.removeDelay < 1 then aZone.removeDelay = 1 end
|
||||
aZone.silent = cfxZones.getBoolFromZoneProperty(aZone, "silent", false)
|
||||
|
||||
--trigger.action.outText("+++rcv: recognized receiver zone: " .. aZone.name , 30)
|
||||
|
||||
-- same integration as object destruct detector for flags
|
||||
--[[--
|
||||
if cfxZones.hasProperty(aZone, "setFlag") then
|
||||
aZone.setFlag = cfxZones.getStringFromZoneProperty(aZone, "setFlag", "999")
|
||||
end
|
||||
@ -70,7 +75,7 @@ function cfxCargoReceiver.processReceiverZone(aZone) -- process attribute and ad
|
||||
if cfxZones.hasProperty(aZone, "f-1") then
|
||||
aZone.decreaseFlag = cfxZones.getStringFromZoneProperty(aZone, "f-1", "999")
|
||||
end
|
||||
|
||||
--]]--
|
||||
-- new method support
|
||||
aZone.cargoMethod = cfxZones.getStringFromZoneProperty(aZone, "method", "inc")
|
||||
if cfxZones.hasProperty(aZone, "cargoMethod") then
|
||||
@ -79,9 +84,7 @@ function cfxCargoReceiver.processReceiverZone(aZone) -- process attribute and ad
|
||||
|
||||
if cfxZones.hasProperty(aZone, "f!") then
|
||||
aZone.outReceiveFlag = cfxZones.getStringFromZoneProperty(aZone, "f!", "*<none>")
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(aZone, "cargoReceived!") then
|
||||
elseif cfxZones.hasProperty(aZone, "cargoReceived!") then
|
||||
aZone.outReceiveFlag = cfxZones.getStringFromZoneProperty(aZone, "cargoReceived!", "*<none>")
|
||||
end
|
||||
|
||||
@ -112,7 +115,25 @@ end
|
||||
--
|
||||
-- cargo event happened. Called by Cargo Manager
|
||||
--
|
||||
function cfxCargoReceiver.removeCargo(args)
|
||||
-- asynch call
|
||||
if not args then return end
|
||||
local theObject = args.theObject
|
||||
local theZone = args.theZone
|
||||
if not theObject then return end
|
||||
if not theObject:isExist() then
|
||||
-- maybe blew up? anyway, we are done
|
||||
return
|
||||
end
|
||||
if args.theZone.verbose or cfxCargoReceiver.verbose then
|
||||
trigger.action.outText("+++crgR: removed object <" .. theObject.getName() .. "> from cargo zone <" .. theZone.name .. ">", 30)
|
||||
end
|
||||
|
||||
theObject:destroy()
|
||||
end
|
||||
|
||||
function cfxCargoReceiver.cargoEvent(event, object, name)
|
||||
-- usually called from cargomanager
|
||||
--trigger.action.outText("Cargo Receiver: event <" .. event .. "> for " .. name, 30)
|
||||
if not event then return end
|
||||
if event == "grounded" then
|
||||
@ -135,6 +156,7 @@ function cfxCargoReceiver.cargoEvent(event, object, name)
|
||||
cfxCargoReceiver.invokeCallback("deliver", object, name, aZone)
|
||||
|
||||
-- set flags as indicated
|
||||
--[[--
|
||||
if aZone.setFlag then
|
||||
trigger.action.setUserFlag(aZone.setFlag, 1)
|
||||
end
|
||||
@ -149,16 +171,20 @@ function cfxCargoReceiver.cargoEvent(event, object, name)
|
||||
local val = trigger.misc.getUserFlag(aZone.decreaseFlag) - 1
|
||||
trigger.action.setUserFlag(aZone.decreaseFlag, val)
|
||||
end
|
||||
|
||||
--]]--
|
||||
if aZone.outReceiveFlag then
|
||||
cfxZones.pollFlag(aZone.outReceiveFlag, aZone.cargoMethod)
|
||||
cfxZones.pollFlag(aZone.outReceiveFlag, aZone.cargoMethod, aZone)
|
||||
end
|
||||
|
||||
--trigger.action.outText("+++rcv: " .. name .. " delivered in zone " .. aZone.name, 30)
|
||||
--trigger.action.outSound("Quest Snare 3.wav")
|
||||
if aZone.autoRemove then
|
||||
-- maybe schedule this in a few seconds?
|
||||
object:destroy()
|
||||
-- schedule this for in a few seconds?
|
||||
local args = {}
|
||||
args.theObject = object
|
||||
args.theZone = aZone
|
||||
timer.scheduleFunction(cfxCargoReceiver.removeCargo, args, timer.getTime() + aZone.removeDelay)
|
||||
--object:destroy()
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -177,11 +203,16 @@ function cfxCargoReceiver.update()
|
||||
-- new we see if any of these are close to a delivery zone
|
||||
for idx, aCargo in pairs(liftedCargos) do
|
||||
local thePoint = aCargo:getPoint()
|
||||
local receiver, delta = cfxZones.getClosestZone(
|
||||
local receiver = cfxZones.getClosestZone(
|
||||
thePoint,
|
||||
cfxCargoReceiver.receiverZones -- must be indexed by name
|
||||
)
|
||||
-- we now check if we are in 'speaking range' and receiver can talk
|
||||
-- modify delta by distance to boundary, not
|
||||
-- center
|
||||
local delta = dcsCommon.distFlat(thePoint, cfxZones.getPoint(receiver))
|
||||
delta = delta - receiver.radius
|
||||
|
||||
if (receiver.silent == false) and
|
||||
(delta < cfxCargoReceiver.maxDirectionRange) then
|
||||
-- this cargo can be talked down.
|
||||
@ -195,7 +226,7 @@ function cfxCargoReceiver.update()
|
||||
local theUnit = info.unit
|
||||
if theUnit:isExist() then
|
||||
local uPoint = theUnit:getPoint()
|
||||
local currDelta = dcsCommon.dist(thePoint, uPoint)
|
||||
local currDelta = dcsCommon.distFlat(thePoint, uPoint)
|
||||
if currDelta < minDelta then
|
||||
minDelta = currDelta
|
||||
closestUnit = theUnit
|
||||
@ -217,7 +248,7 @@ function cfxCargoReceiver.update()
|
||||
receiver.point,
|
||||
thePoint,
|
||||
ownHeading) .. " o'clock"
|
||||
message = receiver.name .. " is " .. math.floor(delta) .. "m at your " .. oclock
|
||||
message = receiver.name .. " (r=" .. receiver.radius .. "m) is " .. math.floor(delta) .. "m at your " .. oclock
|
||||
end
|
||||
-- add agl
|
||||
local agl = dcsCommon.getUnitAGL(aCargo)
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
cfxMX = {}
|
||||
cfxMX.version = "1.1.0"
|
||||
cfxMX.version = "1.2.0"
|
||||
cfxMX.verbose = false
|
||||
--[[--
|
||||
Mission data decoder. Access to ME-built mission structures
|
||||
|
||||
@ -12,6 +13,10 @@ cfxMX.version = "1.1.0"
|
||||
- on start up collects a cross reference table of all
|
||||
original group id
|
||||
- add linkUnit for statics
|
||||
1.2.0 - added group name reference table
|
||||
- added group type reference
|
||||
- added references for allFixed, allHelo, allGround, allSea, allStatic
|
||||
|
||||
|
||||
|
||||
--]]--
|
||||
@ -19,6 +24,11 @@ cfxMX.groupNamesByID = {}
|
||||
cfxMX.groupIDbyName = {}
|
||||
cfxMX.groupDataByName = {}
|
||||
|
||||
cfxMX.allFixedByName = {}
|
||||
cfxMX.allHeloByName = {}
|
||||
cfxMX.allGroundByName = {}
|
||||
cfxMX.allSeaByName = {}
|
||||
cfxMX.allStaticByName ={}
|
||||
|
||||
function cfxMX.getGroupFromDCSbyName(aName, fetchOriginal)
|
||||
if not fetchOriginal then fetchOriginal = false end
|
||||
@ -154,7 +164,7 @@ function cfxMX.getStaticFromDCSbyName(aName, fetchOriginal)
|
||||
return nil, "<none>", "<none>", "<no group name>"
|
||||
end
|
||||
|
||||
function cfxMX.createCrossReference()
|
||||
function cfxMX.createCrossReferences()
|
||||
for coa_name_miz, coa_data in pairs(env.mission.coalition) do -- iterate all coalitions
|
||||
local coa_name = coa_name_miz
|
||||
if string.lower(coa_name_miz) == 'neutrals' then -- remove 's' at neutralS
|
||||
@ -178,7 +188,7 @@ function cfxMX.createCrossReference()
|
||||
obj_type_name == "ship" or
|
||||
obj_type_name == "plane" or
|
||||
obj_type_name == "vehicle" or
|
||||
obj_type_name == "static"
|
||||
obj_type_name == "static" -- what about "cargo"?
|
||||
then -- (so it's not id or name)
|
||||
local category = obj_type_name
|
||||
if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's at least one group!
|
||||
@ -188,6 +198,21 @@ function cfxMX.createCrossReference()
|
||||
cfxMX.groupNamesByID[aID] = aName
|
||||
cfxMX.groupIDbyName[aName] = aID
|
||||
cfxMX.groupDataByName[aName] = group_data
|
||||
-- now make the type-specific xrefs
|
||||
if obj_type_name == "helicopter" then
|
||||
cfxMX.allHeloByName[aName] = group_data
|
||||
elseif obj_type_name == "ship" then
|
||||
cfxMX.allSeaByName[aName] = group_data
|
||||
elseif obj_type_name == "plane" then
|
||||
cfxMX.allFixedByName[aName] = group_data
|
||||
elseif obj_type_name == "vehicle" then
|
||||
cfxMX.allGroundByName[aName] = group_data
|
||||
elseif obj_type_name == "static" then
|
||||
cfxMX.allStaticByName[aName] = group_data
|
||||
else
|
||||
-- should be impossible, but still
|
||||
trigger.action.outText("+++MX: <" .. obj_type_name .. "> unknown type for <" .. aName .. ">", 30)
|
||||
end
|
||||
end
|
||||
end --if has category data
|
||||
end --if plane, helo etc... category
|
||||
@ -212,8 +237,10 @@ function cfxMX.catText2ID(inText)
|
||||
end
|
||||
|
||||
function cfxMX.start()
|
||||
cfxMX.createCrossReference()
|
||||
cfxMX.createCrossReferences()
|
||||
if cfxMX.verbose then
|
||||
trigger.action.outText("cfxMX: "..#cfxMX.groupNamesByID .. " groups processed successfully", 30)
|
||||
end
|
||||
end
|
||||
|
||||
-- start
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxObjectSpawnZones = {}
|
||||
cfxObjectSpawnZones.version = "1.2.1"
|
||||
cfxObjectSpawnZones.version = "1.3.0"
|
||||
cfxObjectSpawnZones.requiredLibs = {
|
||||
"dcsCommon", -- common is of course needed for everything
|
||||
-- pretty stupid to check for this since we
|
||||
@ -26,7 +26,10 @@ cfxObjectSpawnZones.verbose = false
|
||||
-- 1.1.5 - spawn?, spawnObjects? synonyms
|
||||
-- 1.2.0 - DML flag upgrade
|
||||
-- 1.2.1 - config zone
|
||||
-- - autoLink bug (zone instead of spaneer accessed)
|
||||
-- - autoLink bug (zone instead of spawner accessed)
|
||||
-- 1.3.0 - better synonym handling
|
||||
-- - useDelicates link to delicate when spawned
|
||||
-- - spawned single and multi-objects can be made delicates
|
||||
|
||||
-- respawn currently happens after theSpawns is deleted and cooldown seconds have passed
|
||||
cfxObjectSpawnZones.allSpawners = {}
|
||||
@ -58,13 +61,9 @@ function cfxObjectSpawnZones.createSpawner(inZone)
|
||||
-- connect with ME if a trigger flag is given
|
||||
if cfxZones.hasProperty(inZone, "f?") then
|
||||
theSpawner.triggerFlag = cfxZones.getStringFromZoneProperty(inZone, "f?", "none")
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(inZone, "spawn?") then
|
||||
elseif cfxZones.hasProperty(inZone, "spawn?") then
|
||||
theSpawner.triggerFlag = cfxZones.getStringFromZoneProperty(inZone, "spawn?", "none")
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(inZone, "spawnObjects?") then
|
||||
elseif cfxZones.hasProperty(inZone, "spawnObjects?") then
|
||||
theSpawner.triggerFlag = cfxZones.getStringFromZoneProperty(inZone, "spawnObjects?", "none")
|
||||
end
|
||||
|
||||
@ -116,6 +115,11 @@ function cfxObjectSpawnZones.createSpawner(inZone)
|
||||
theSpawner.requestable = cfxZones.getBoolFromZoneProperty(inZone, "requestable", false)
|
||||
if theSpawner.requestable then theSpawner.paused = true end
|
||||
|
||||
-- see if the spawn can be made brittle/delicte
|
||||
if cfxZones.hasProperty(inZone, "useDelicates") then
|
||||
theSpawner.delicateName = cfxZones.getStringFromZoneProperty(inZone, "useDelicates", "<none>")
|
||||
end
|
||||
|
||||
-- see if it is linked to a ship to set realtive orig headiong
|
||||
|
||||
if inZone.linkedUnit then
|
||||
@ -258,6 +262,16 @@ function cfxObjectSpawnZones.spawnObjectNTimes(aSpawner, theType, n, container)
|
||||
end
|
||||
end
|
||||
|
||||
if aSpawner.delicateName and delicates then
|
||||
-- pass this object to the delicate zone mentioned
|
||||
local theDeli = delicates.getDelicatesByName(aSpawner.delicateName)
|
||||
if theDeli then
|
||||
delicates.addStaticObjectToInventoryForZone(theDeli, theObject)
|
||||
else
|
||||
trigger.action.outText("+++oSpwn: spawner <" .. aZone.name .. "> can't find delicates <" .. aSpawner.delicateName .. ">", 30)
|
||||
end
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
@ -302,6 +316,17 @@ function cfxObjectSpawnZones.spawnObjectNTimes(aSpawner, theType, n, container)
|
||||
cfxCargoManager.addCargo(theObject)
|
||||
end
|
||||
end
|
||||
|
||||
if aSpawner.delicateName and delicates then
|
||||
-- pass this object to the delicate zone mentioned
|
||||
local theDeli = delicates.getDelicatesByName(aSpawner.delicateName)
|
||||
if theDeli then
|
||||
delicates.addStaticObjectToInventoryForZone(theDeli, theObject)
|
||||
else
|
||||
trigger.action.outText("+++oSpwn: spawner <" .. aZone.name .. "> can't find delicates <" .. aSpawner.delicateName .. ">", 30)
|
||||
end
|
||||
end
|
||||
|
||||
-- update rotation
|
||||
currDegree = currDegree + degreeIncrement
|
||||
end
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxZones = {}
|
||||
cfxZones.version = "2.8.3"
|
||||
cfxZones.version = "2.8.4"
|
||||
|
||||
-- cf/x zone management module
|
||||
-- reads dcs zones and makes them accessible and mutable
|
||||
@ -88,6 +88,7 @@ cfxZones.version = "2.8.3"
|
||||
- 2.8.3 - new verifyMethod()
|
||||
- changed extractPropertyFromDCS() to also match attributes with blanks like "the Attr" to "theAttr"
|
||||
- new expandFlagName()
|
||||
- 2.8.4 - fixed bug in setFlagValue()
|
||||
|
||||
--]]--
|
||||
cfxZones.verbose = false
|
||||
@ -867,11 +868,12 @@ end
|
||||
-- get closest zone returns the zone that is closest to point
|
||||
function cfxZones.getClosestZone(point, theZones)
|
||||
if not theZones then theZones = cfxZones.zones end
|
||||
local lPoint = {x=point.x, y=0, z=point.z}
|
||||
local currDelta = math.huge
|
||||
local closestZone = nil
|
||||
for zName, zData in pairs(theZones) do
|
||||
local zPoint = cfxZones.getPoint(zData)
|
||||
local delta = dcsCommon.dist(point, zPoint)
|
||||
local delta = dcsCommon.dist(lPoint, zPoint) -- emulate flag compare
|
||||
if (delta < currDelta) then
|
||||
currDelta = delta
|
||||
closestZone = zData
|
||||
@ -1276,7 +1278,7 @@ end
|
||||
function cfxZones.setFlagValue(theFlag, theValue, theZone)
|
||||
local zoneName = "<dummy>"
|
||||
if not theZone then
|
||||
trigger.action.outText("+++Zne: no zone on setFlagValue")
|
||||
trigger.action.outText("+++Zne: no zone on setFlagValue", 30) -- mod me for detector
|
||||
else
|
||||
zoneName = theZone.name -- for flag wildcards
|
||||
end
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
dcsCommon = {}
|
||||
dcsCommon.version = "2.6.6"
|
||||
dcsCommon.version = "2.6.8"
|
||||
--[[-- VERSION HISTORY
|
||||
2.2.6 - compassPositionOfARelativeToB
|
||||
- clockPositionOfARelativeToB
|
||||
@ -82,7 +82,9 @@ dcsCommon.version = "2.6.6"
|
||||
- new stringRemainsStartingWith()
|
||||
- new stripLF()
|
||||
- new removeBlanks()
|
||||
|
||||
2.6.7 - new menu2text()
|
||||
2.6.8 - new getMissionName()
|
||||
- new flagArrayFromString()
|
||||
--]]--
|
||||
|
||||
-- dcsCommon is a library of common lua functions
|
||||
@ -1882,6 +1884,21 @@ dcsCommon.version = "2.6.6"
|
||||
return catNum
|
||||
end
|
||||
|
||||
function dcsCommon.menu2text(inMenu)
|
||||
if not inMenu then return "<nil>" end
|
||||
local s = ""
|
||||
for n, v in pairs(inMenu) do
|
||||
if type(v) == "string" then
|
||||
if s == "" then s = "[" .. v .. "]" else
|
||||
s = s .. " | [" .. type(v) .. "]" end
|
||||
else
|
||||
if s == "" then s = "[<" .. type(v) .. ">]" else
|
||||
s = s .. " | [<" .. type(v) .. ">]" end
|
||||
end
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
-- recursively show the contents of a variable
|
||||
function dcsCommon.dumpVar(key, value, prefix, inrecursion)
|
||||
if not inrecursion then
|
||||
@ -2361,6 +2378,68 @@ function dcsCommon.latLon2Text(lat, lon)
|
||||
return lat, lon
|
||||
end
|
||||
|
||||
-- get mission name. If mission file name without ".miz"
|
||||
function dcsCommon.getMissionName()
|
||||
local mn = net.dostring_in("gui", "return DCS.getMissionName()")
|
||||
return mn
|
||||
end
|
||||
|
||||
function dcsCommon.flagArrayFromString(inString, verbose)
|
||||
if not verbose then verbose = false end
|
||||
|
||||
if verbose then
|
||||
trigger.action.outText("+++flagArray: processing <" .. inString .. ">", 30)
|
||||
end
|
||||
|
||||
if string.len(inString) < 1 then
|
||||
trigger.action.outText("+++flagArray: empty flags", 30)
|
||||
return {}
|
||||
end
|
||||
|
||||
|
||||
local flags = {}
|
||||
local rawElements = dcsCommon.splitString(inString, ",")
|
||||
-- go over all elements
|
||||
for idx, anElement in pairs(rawElements) do
|
||||
if dcsCommon.stringStartsWithDigit(anElement) and dcsCommon.containsString(anElement, "-") then
|
||||
-- interpret this as a range
|
||||
local theRange = dcsCommon.splitString(anElement, "-")
|
||||
local lowerBound = theRange[1]
|
||||
lowerBound = tonumber(lowerBound)
|
||||
local upperBound = theRange[2]
|
||||
upperBound = tonumber(upperBound)
|
||||
if lowerBound and upperBound then
|
||||
-- swap if wrong order
|
||||
if lowerBound > upperBound then
|
||||
local temp = upperBound
|
||||
upperBound = lowerBound
|
||||
lowerBound = temp
|
||||
end
|
||||
-- now add add numbers to flags
|
||||
for f=lowerBound, upperBound do
|
||||
table.insert(flags, f)
|
||||
|
||||
end
|
||||
else
|
||||
-- bounds illegal
|
||||
trigger.action.outText("+++flagArray: ignored range <" .. anElement .. "> (range)", 30)
|
||||
end
|
||||
else
|
||||
-- single number
|
||||
f = dcsCommon.trim(anElement) -- DML flag upgrade: accept strings tonumber(anElement)
|
||||
if f then
|
||||
table.insert(flags, f)
|
||||
|
||||
else
|
||||
trigger.action.outText("+++flagArray: ignored element <" .. anElement .. "> (single)", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
if verbose then
|
||||
trigger.action.outText("+++flagArray: <" .. #flags .. "> flags total", 30)
|
||||
end
|
||||
return flags
|
||||
end
|
||||
--
|
||||
--
|
||||
-- INIT
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
delicates = {}
|
||||
delicates.version = "1.0.0"
|
||||
delicates.version = "1.1.0"
|
||||
delicates.verbose = false
|
||||
delicates.ups = 1
|
||||
delicates.requiredLibs = {
|
||||
@ -12,6 +12,11 @@ delicates.inventory = {}
|
||||
--[[--
|
||||
Version History
|
||||
1.0.0 - initial version
|
||||
1.1.0 - better synonym handling for f! and out!
|
||||
- addStaticObjectInventoryForZone
|
||||
- blowAll?
|
||||
- safetyMargin - safety margin. defaults to 10%
|
||||
|
||||
|
||||
--]]--
|
||||
function delicates.adddDelicates(theZone)
|
||||
@ -68,7 +73,7 @@ function delicates.makeZoneInventory(theZone)
|
||||
for idy, anObject in pairs(collector) do
|
||||
local oName = anObject:getName()
|
||||
if type(oName) == 'number' then oName = tostring(oName) end
|
||||
local oLife = anObject:getLife()
|
||||
local oLife = anObject:getLife() - anObject:getLife() * theZone.safetyMargin
|
||||
if theZone.verbose or delicates.verbose then
|
||||
trigger.action.outText("+++deli: cat=".. aCat .. ":<" .. oName .. "> Life=" .. oLife, 30)
|
||||
end
|
||||
@ -91,25 +96,47 @@ function delicates.makeZoneInventory(theZone)
|
||||
end
|
||||
end
|
||||
|
||||
function delicates.addStaticObjectToInventoryForZone(theZone, theStatic)
|
||||
if not theZone then return end
|
||||
if not theStatic then return end
|
||||
|
||||
local desc = {}
|
||||
desc.cat = theStatic:getCategory()
|
||||
desc.oLife = theStatic:getLife() - theStatic:getLife() * theZone.safetyMargin
|
||||
if desc.oLife < 0 then desc.oLife = 0 end
|
||||
desc.theZone = theZone
|
||||
desc.oName = theStatic:getName()
|
||||
delicates.inventory[desc.oName] = desc
|
||||
|
||||
if theZone.verbose or delicates.verbose then
|
||||
trigger.action.outText("+++deli: added static <" .. desc.oName .. "> to <" .. theZone.name .. "> with minimal life = <" .. desc.oLife .. "/" .. theStatic:getLife() .. "> = safety margin of " .. theZone.safetyMargin * 100 .. "%", 30)
|
||||
end
|
||||
end
|
||||
|
||||
function delicates.createDelicatesWithZone(theZone)
|
||||
theZone.power = cfxZones.getNumberFromZoneProperty(theZone, "power", 10)
|
||||
|
||||
if cfxZones.hasProperty(theZone, "delicatesHit!") then
|
||||
theZone.delicateHit = cfxZones.getStringFromZoneProperty(theZone, "delicatesHit!", "*<none>")
|
||||
end
|
||||
if cfxZones.hasProperty(theZone, "f!") then
|
||||
elseif cfxZones.hasProperty(theZone, "f!") then
|
||||
theZone.delicateHit = cfxZones.getStringFromZoneProperty(theZone, "f!", "*<none>")
|
||||
end
|
||||
if cfxZones.hasProperty(theZone, "out!") then
|
||||
elseif cfxZones.hasProperty(theZone, "out!") then
|
||||
theZone.delicateHit = cfxZones.getStringFromZoneProperty(theZone, "out!", "*<none>")
|
||||
end
|
||||
|
||||
-- safety margin
|
||||
theZone.safetyMargin = cfxZones.getNumberFromZoneProperty(theZone, "safetyMargin", 0)
|
||||
|
||||
-- DML Method
|
||||
theZone.delicateHitMethod = cfxZones.getStringFromZoneProperty(theZone, "method", "inc")
|
||||
if cfxZones.hasProperty(theZone, "delicateMethod") then
|
||||
theZone.delicateHitMethod = cfxZones.getStringFromZoneProperty(theZone, "delicatesMethod", "inc")
|
||||
end
|
||||
|
||||
theZone.delicateTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "thriggerMethod", "change")
|
||||
if cfxZones.hasProperty(theZone, "delicateTriggerMethod") then
|
||||
theZone.delicateTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "delicatesMethod", "change")
|
||||
end
|
||||
|
||||
theZone.delicateRemove = cfxZones.getBoolFromZoneProperty(theZone, "remove", true)
|
||||
|
||||
@ -117,6 +144,11 @@ function delicates.createDelicatesWithZone(theZone)
|
||||
-- may want to filter by objects, can be passed in delicates
|
||||
delicates.makeZoneInventory(theZone)
|
||||
|
||||
if cfxZones.hasProperty(theZone, "blowAll?") then
|
||||
theZone.blowAll = cfxZones.getStringFromZoneProperty(theZone, "blowAll?", "*<none>")
|
||||
theZone.lastBlowAll = cfxZones.getFlagValue(theZone.blowAll, theZone)
|
||||
end
|
||||
|
||||
if delicates.verbose or theZone.verbose then
|
||||
trigger.action.outText("+++deli: new delicates zone <".. theZone.name ..">", 30)
|
||||
end
|
||||
@ -185,14 +217,42 @@ function delicates:onEvent(theEvent)
|
||||
local oName = theObj:getName()
|
||||
local desc = delicates.inventory[oName]
|
||||
if desc then
|
||||
-- trigger.action.outText("+++deli: REGISTERED HIT -- removing!", 30)
|
||||
-- see if damage exceeds maximum
|
||||
local cLife = theObj:getLife()
|
||||
if cLife < desc.oLife then
|
||||
if desc.theZone.verbose or delicates.verbose then
|
||||
trigger.action.outText("+++deli: BRITTLE TRIGGER: life <" .. cLife .. "> below safety margin <" .. oDesc.oLife .. ">", 30)
|
||||
end
|
||||
delicates.blowUpObject(desc)
|
||||
-- remove it from further searches
|
||||
delicates.inventory[oName] = nil
|
||||
else
|
||||
if desc.theZone.verbose or delicates.verbose then
|
||||
trigger.action.outText("+++deli: CLOSE CALL, but life <" .. cLife .. "> within safety margin <" .. oDesc.oLife .. ">", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- trigger.action.outText("+++deli: we hit " .. oName, 30)
|
||||
end
|
||||
|
||||
--
|
||||
-- blow entire zone
|
||||
--
|
||||
function delicates.blowZone(theZone)
|
||||
if not theZone then return end
|
||||
local zName = theZone.name
|
||||
local newInventory = {}
|
||||
local delay = 0.7
|
||||
for oName, oDesc in pairs (delicates.inventory) do
|
||||
if oDesc.theZone.name == zName then
|
||||
delicates.blowUpObject(oDesc, delay)
|
||||
delay = delay + 0.2 -- stagger explosions
|
||||
else
|
||||
newInventory[oName] = oDesc
|
||||
end
|
||||
end
|
||||
|
||||
delicates.inventory = newInventory
|
||||
end
|
||||
|
||||
--
|
||||
@ -228,12 +288,12 @@ function delicates.update()
|
||||
if theObj then
|
||||
local cLife = theObj:getLife()
|
||||
if cLife >= oDesc.oLife then
|
||||
-- transfer to next iter
|
||||
-- all well, transfer to next iter
|
||||
newInventory[oName] = oDesc
|
||||
else
|
||||
-- blow stuff up
|
||||
-- health beneath min. blow stuff up
|
||||
if oDesc.theZone.verbose or delicates.verbose then
|
||||
trigger.action.outText(oName .. " was hit, will blow up, new health is at " .. oDesc.oLife .. ".", 30)
|
||||
trigger.action.outText(oName .. " was hit, will blow up, current health is <" .. cLife .. ">, min health was " .. oDesc.oLife .. ".", 30)
|
||||
end
|
||||
delicates.blowUpObject(oDesc)
|
||||
end
|
||||
@ -245,6 +305,13 @@ function delicates.update()
|
||||
end
|
||||
end
|
||||
delicates.inventory = newInventory
|
||||
|
||||
-- now scan all zones for signals
|
||||
for idx, theZone in pairs(delicates.theDelicates) do
|
||||
if theZone.blowAll and cfxZones.testZoneFlag(theZone, theZone.blowAll, theZone.delicateTriggerMethod, "lastBlowAll") then
|
||||
delicates.blowZone(theZone)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
|
||||
@ -566,5 +566,5 @@ end
|
||||
To do
|
||||
- reset? flag: will reset all to MX locationS
|
||||
- add a zone's follow ability to impostors by allowing linkedUnit to work with impostors
|
||||
|
||||
- impostor on idle option. when task of group goes to idle, the group turns into impostors
|
||||
--]]--
|
||||
584
modules/persistence.lua
Normal file
584
modules/persistence.lua
Normal file
@ -0,0 +1,584 @@
|
||||
persistence = {}
|
||||
persistence.version = "1.0.0"
|
||||
persistence.ups = 1 -- once every 1 seconds
|
||||
persistence.verbose = false
|
||||
persistence.active = false
|
||||
persistence.saveFileName = nil -- "mission data.txt"
|
||||
persistence.sharedDir = nil -- not yet implemented
|
||||
persistence.missionDir = nil -- set at start
|
||||
persistence.saveDir = nil -- set at start
|
||||
|
||||
persistence.missionData = {} -- loaded from file
|
||||
persistence.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxZones", -- Zones, of course
|
||||
}
|
||||
--[[--
|
||||
Version History
|
||||
1.0.0 - initial version
|
||||
|
||||
PROVIDES LOAD/SAVE ABILITY TO MODULES
|
||||
PROVIDES STANDALONE/HOSTED SERVER COMPATIOBILITY
|
||||
|
||||
--]]--
|
||||
|
||||
-- in order to work, Host must desanitize lfs and io
|
||||
-- only works when run as server
|
||||
|
||||
--
|
||||
-- flags to save. can be added to by saveFlags attribute
|
||||
--
|
||||
persistence.flagsToSave = {} -- simple table
|
||||
persistence.callbacks = {} -- cbblocks, dictionary
|
||||
|
||||
|
||||
--
|
||||
-- modules register here
|
||||
--
|
||||
function persistence.registerModule(name, callbacks)
|
||||
-- callbacks is a table with the following entries
|
||||
-- callbacks.persistData - method that returns a table
|
||||
-- note that name is also what the data is saved under
|
||||
-- and must be the one given when you retrieve it later
|
||||
persistence.callbacks[name] = callbacks
|
||||
if persistence.verbose then
|
||||
trigger.action.outText("+++persistence: module <" .. name .. "> registred itself", 30)
|
||||
end
|
||||
end
|
||||
|
||||
function persistence.registerFlagsToSave(flagNames, theZone)
|
||||
-- name can be single flag name or anything that
|
||||
-- a zone definition has to offer, including local
|
||||
-- flags.
|
||||
-- flags can be passed like this: "a, 4-19, 99, kills, *lcl"
|
||||
-- if you pass a local flag, you must pass the zone
|
||||
-- or "persisTEMP" will be used
|
||||
|
||||
if not theZone then theZone = cfxZones.createSimpleZone("persisTEMP") end
|
||||
local newFlags = dcsCommon.flagArrayFromString(flagNames, persistence.verbose)
|
||||
|
||||
-- mow process all new flags and add them to the list of flags
|
||||
-- to save
|
||||
for idx, flagName in pairs(newFlags) do
|
||||
if dcsCommon.stringStartsWith(flagName, "*") then
|
||||
flagName = theZone.name .. flagName
|
||||
end
|
||||
table.insert(persistence.flagsToSave, flagName)
|
||||
end
|
||||
end
|
||||
--
|
||||
-- registered modules call this to get their data
|
||||
--
|
||||
function persistence.getSavedDataForModule(name)
|
||||
if not persistence.active then return nil end
|
||||
if not persistence.hasData then return nil end
|
||||
if not persistence.missionData then return end
|
||||
|
||||
return persistence.missionData[name] -- simply get the modules data block
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- Shared Data API
|
||||
--
|
||||
function persistence.getSharedDataFor(name, item) -- not yet finalized
|
||||
end
|
||||
|
||||
function persistence.putSharedDataFor(data, name, item) -- not yet finalized
|
||||
end
|
||||
|
||||
--
|
||||
-- helper meths
|
||||
--
|
||||
|
||||
function persistence.hasFile(path) --check if file exists at path
|
||||
-- will also return true for a directory, follow up with isDir
|
||||
|
||||
local attr = lfs.attributes(path)
|
||||
if attr then
|
||||
return true, attr.mode
|
||||
end
|
||||
|
||||
if persistence.verbose then
|
||||
trigger.action.outText("isFile: attributes not found for <" .. path .. ">", 30)
|
||||
end
|
||||
|
||||
return false, "<err>"
|
||||
end
|
||||
|
||||
function persistence.isDir(path) -- check if path is a directory
|
||||
local success, mode = persistence.hasFile(path)
|
||||
if success then
|
||||
success = (mode == "directory")
|
||||
end
|
||||
return success
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- Main save meths
|
||||
--
|
||||
function persistence.saveText(theString, fileName, shared, append)
|
||||
if not persistence.active then return false end
|
||||
if not fileName then return false end
|
||||
if not shared then shared = flase end
|
||||
if not theString then theString = "" end
|
||||
|
||||
local path = persistence.missionDir .. fileName
|
||||
if shared then
|
||||
-- we would now change the path
|
||||
trigger.action.outText("+++persistence: NYI: shared", 30)
|
||||
return
|
||||
end
|
||||
|
||||
local theFile = nil
|
||||
|
||||
if append then
|
||||
theFile = io.open(path, "a")
|
||||
else
|
||||
theFile = io.open(path, "w")
|
||||
end
|
||||
|
||||
if not theFile then
|
||||
return false
|
||||
end
|
||||
|
||||
theFile:write(theString)
|
||||
|
||||
theFile:close()
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function persistence.saveTable(theTable, fileName, shared, append)
|
||||
if not persistence.active then return false end
|
||||
if not fileName then return false end
|
||||
if not theTable then return false end
|
||||
if not shared then shared = false end
|
||||
|
||||
local theString = net.lua2json(theTable)
|
||||
|
||||
if not theString then theString = "" end
|
||||
|
||||
local path = persistence.missionDir .. fileName
|
||||
if shared then
|
||||
-- we would now change the path
|
||||
trigger.action.outText("+++persistence: NYI: shared", 30)
|
||||
return
|
||||
end
|
||||
|
||||
local theFile = nil
|
||||
|
||||
if append then
|
||||
theFile = io.open(path, "a")
|
||||
else
|
||||
theFile = io.open(path, "w")
|
||||
end
|
||||
|
||||
if not theFile then
|
||||
return false
|
||||
end
|
||||
|
||||
theFile:write(theString)
|
||||
|
||||
theFile:close()
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
function persistence.loadText(fileName) -- load file as text
|
||||
if not persistence.active then return nil end
|
||||
if not fileName then return nil end
|
||||
|
||||
local path = persistence.missionDir .. fileName
|
||||
local theFile = io.open(path, "r")
|
||||
if not theFile then return nil end
|
||||
|
||||
local t = theFile:read("*a")
|
||||
|
||||
theFile:close()
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
function persistence.loadTable(fileName) -- load file as table
|
||||
if not persistence.active then return nil end
|
||||
if not fileName then return nil end
|
||||
|
||||
local t = persistence.loadText(fileName)
|
||||
|
||||
if not t then return nil end
|
||||
|
||||
local tab = net.json2lua(t)
|
||||
|
||||
return tab
|
||||
end
|
||||
|
||||
|
||||
|
||||
--
|
||||
-- Data Load on Start
|
||||
--
|
||||
function persistence.initFlagsFromData(theFlags)
|
||||
-- assumes that theFlags is a dictionary containing
|
||||
-- flag names
|
||||
local flagLog = ""
|
||||
local flagCount = 0
|
||||
for flagName, value in pairs(theFlags) do
|
||||
local val = tonumber(value) -- ensure number
|
||||
if not val then val = 0 end
|
||||
trigger.action.setUserFlag(flagName, val)
|
||||
if flagLog ~= "" then
|
||||
flagLog = flagLog .. ", " .. flagName .. "=" .. val
|
||||
else
|
||||
flagLog = flagName .. "=" .. val
|
||||
end
|
||||
flagCount = flagCount + 1
|
||||
end
|
||||
if persistence.verbose and flagCount > 0 then
|
||||
trigger.action.outText("+++persistence: loaded " .. flagCount .. " flags from storage:\n" .. flagLog .. "", 30)
|
||||
elseif persistence.verbose then
|
||||
trigger.action.outText("+++persistence: no flags loaded, commencing mission data load", 30)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
function persistence.missionStartDataLoad()
|
||||
-- check one: see if we have mission data
|
||||
local theData = persistence.loadTable(persistence.saveFileName)
|
||||
|
||||
if not theData then
|
||||
if persistence.verbose then
|
||||
trigger.action.outText("+++persistence: no saved data, fresh start.", 30)
|
||||
end
|
||||
return
|
||||
end -- there was no data to load
|
||||
|
||||
if theData["freshMaker"] then
|
||||
if persistence.verbose then
|
||||
trigger.action.outText("+++persistence: detected fresh start.", 30)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- when we get here, we got at least some data. check it
|
||||
if theData["versionID"] or persistence.versionID then
|
||||
local vid = theData.versionID -- note: either may be nil!
|
||||
if vid ~= persistence.versionID then
|
||||
-- we pretend load never happened.
|
||||
-- simply return
|
||||
if persistence.verbose then
|
||||
local curvid = persistence.versionID
|
||||
if not curvid then curvid = "<NIL>" end
|
||||
if not vid then vid = "<NIL>" end
|
||||
trigger.action.outText("+++persistence: version mismatch\n(saved = <" .. vid .. "> vs current = <" .. curvid .. ">) - fresh start.", 30)
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- we have valid data, and modules, after signing up
|
||||
-- can init from by data
|
||||
persistence.missionData = theData
|
||||
persistence.hasData = true
|
||||
|
||||
-- init my flags from last save
|
||||
local theFlags = theData["persistence.flagData"]
|
||||
if theFlags then
|
||||
persistence.initFlagsFromData(theFlags)
|
||||
end
|
||||
|
||||
-- we are done for now. modules check in
|
||||
-- after persistence and load their own data
|
||||
-- when they detect that there is data to load
|
||||
if persistence.verbose then
|
||||
trigger.action.outText("+++persistence: basic import complete.", 30)
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- MAIN DATA WRITE
|
||||
--
|
||||
function persistence.collectFlagData()
|
||||
local flagData = {}
|
||||
for idx, flagName in pairs (persistence.flagsToSave) do
|
||||
local theNum = trigger.misc.getUserFlag(flagName)
|
||||
flagData[flagName] = theNum
|
||||
|
||||
end
|
||||
return flagData
|
||||
end
|
||||
|
||||
function persistence.saveMissionData()
|
||||
local myData = {}
|
||||
|
||||
-- first, handle versionID and freshMaker
|
||||
if persistence.freshMaker then
|
||||
myData["freshMaker"] = true
|
||||
end
|
||||
|
||||
if persistence.versionID then
|
||||
myData["versionID"] = persistence.versionID
|
||||
end
|
||||
|
||||
-- now handle flags
|
||||
myData["persistence.flagData"] = persistence.collectFlagData()
|
||||
|
||||
-- now handle all other modules
|
||||
for moduleName, callbacks in pairs(persistence.callbacks) do
|
||||
local moduleData = callbacks.persistData()
|
||||
if moduleData then
|
||||
myData[moduleName] = moduleData
|
||||
if persistence.verbose then
|
||||
trigger.action.outText("+++persistence: gathered data from <" .. moduleName .. ">", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- now save data to file
|
||||
persistence.saveTable(myData, persistence.saveFileName)
|
||||
end
|
||||
|
||||
--
|
||||
-- UPDATE
|
||||
--
|
||||
function persistence.doSaveMission()
|
||||
-- main save entry, also from API
|
||||
if persistence.verbose then
|
||||
trigger.action.outText("+++persistence: starting save", 30)
|
||||
end
|
||||
|
||||
if persistence.active then
|
||||
persistence.saveMissionData()
|
||||
else
|
||||
if persistence.verbose then
|
||||
trigger.action.outText("+++persistence: not actice. skipping save", 30)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if persistence.verbose then
|
||||
trigger.action.outText("+++persistence: mission saved", 30)
|
||||
end
|
||||
end
|
||||
|
||||
function persistence.noteCleanRestart()
|
||||
persistence.freshMaker = true
|
||||
persistence.doSaveMission()
|
||||
trigger.action.outText("\n\nYou can re-start the mission for a fresh start.\n\n",30)
|
||||
|
||||
end
|
||||
|
||||
function persistence.update()
|
||||
-- call me in a second to poll triggers
|
||||
timer.scheduleFunction(persistence.update, {}, timer.getTime() + 1/persistence.ups)
|
||||
|
||||
-- check my trigger flag
|
||||
if persistence.saveMission and cfxZones.testZoneFlag(persistence, persistence.saveMission, "change", "lastSaveMission") then
|
||||
persistence.doSaveMission()
|
||||
end
|
||||
|
||||
if persistence.cleanRestart and cfxZones.testZoneFlag(persistence, persistence.cleanRestart, "change", "lastCleanRestart") then
|
||||
persistence.noteCleanRestart()
|
||||
end
|
||||
|
||||
-- check my timer
|
||||
if persistence.saveTime and persistence.saveTime < timer.getTime() then
|
||||
persistence.doSaveMission()
|
||||
-- start next cycle
|
||||
persistence.saveTime = persistence.saveInterval * 60 + timer.getTime()
|
||||
end
|
||||
end
|
||||
--
|
||||
-- config & start
|
||||
--
|
||||
|
||||
function persistence.collectFlagsFromZone(theZone)
|
||||
local theFlags = cfxZones.getStringFromZoneProperty(theZone, "saveFlags", "*dummy")
|
||||
persistence.registerFlagsToSave(theFlags, theZone)
|
||||
end
|
||||
|
||||
function persistence.readConfigZone()
|
||||
local theZone = cfxZones.getZoneByName("persistenceConfig")
|
||||
local hasConfig = true
|
||||
if not theZone then
|
||||
hasConfig = false
|
||||
theZone = cfxZones.createSimpleZone("persistenceConfig")
|
||||
end
|
||||
|
||||
-- serverDir is the path from the server save directory, usually "Missions/".
|
||||
-- will be added to lfs.writedir().
|
||||
persistence.serverDir = cfxZones.getStringFromZoneProperty(theZone, "serverDir", "Missions\\")
|
||||
|
||||
if hasConfig then
|
||||
if cfxZones.hasProperty(theZone, "saveDir") then
|
||||
persistence.saveDir = cfxZones.getStringFromZoneProperty(theZone, "saveDir", "")
|
||||
else
|
||||
-- local missname = net.dostring_in("gui", "return DCS.getMissionName()") .. " (data)"
|
||||
persistence.saveDir = dcsCommon.getMissionName() .. " (data)"
|
||||
end
|
||||
else
|
||||
persistence.saveDir = "" -- save dir is to main mission
|
||||
-- so that when no config is present (standalone debugger)
|
||||
-- this will not cause a separate save folder
|
||||
end
|
||||
|
||||
if persistence.saveDir == "" and persistence.verbose then
|
||||
trigger.action.outText("*** WARNING: persistence is set to write to main mission directory!", 30)
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "saveFileName") then
|
||||
persistence.saveFileName = cfxZones.getStringFromZoneProperty(theZone, "saveFileName", dcsCommon.getMissionName() .. " Data.txt")
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "versionID") then
|
||||
persistence.versionID = cfxZones.getStringFromZoneProperty(theZone, "versionID", "") -- to check for full restart
|
||||
end
|
||||
|
||||
persistence.saveInterval = cfxZones.getNumberFromZoneProperty(theZone, "saveInterval", -1) -- default to manual save
|
||||
if persistence.saveInterval > 0 then
|
||||
persistence.saveTime = persistence.saveInterval * 60 + timer.getTime()
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "cleanRestart?") then
|
||||
persistence.cleanRestart = cfxZones.getStringFromZoneProperty(theZone, "cleanRestart?", "*<none>")
|
||||
persistence.lastCleanRestart = cfxZones.getFlagValue(persistence.cleanRestart, theZone)
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "saveMission?") then
|
||||
persistence.saveMission = cfxZones.getStringFromZoneProperty(theZone, "saveMission?", "*<none>")
|
||||
persistence.lastSaveMission = cfxZones.getFlagValue(persistence.saveMission, theZone)
|
||||
end
|
||||
|
||||
persistence.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
||||
|
||||
if persistence.verbose then
|
||||
trigger.action.outText("+++persistence: read config", 30)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function persistence.start()
|
||||
-- lib check
|
||||
if not dcsCommon.libCheck then
|
||||
trigger.action.outText("persistence requires dcsCommon", 30)
|
||||
return false
|
||||
end
|
||||
if not dcsCommon.libCheck("persistence", persistence.requiredLibs) then
|
||||
return false
|
||||
end
|
||||
|
||||
-- read config
|
||||
persistence.saveFileName = dcsCommon.getMissionName() .. " Data.txt"
|
||||
persistence.readConfigZone()
|
||||
|
||||
-- let's see it lfs and io are online
|
||||
persistence.active = false
|
||||
if not _G["lfs"] then
|
||||
if persistence.verbose then
|
||||
trigger.action.outText("+++persistence requires 'lfs'", 30)
|
||||
return false
|
||||
end
|
||||
end
|
||||
if not _G["io"] then
|
||||
if persistence.verbose then
|
||||
trigger.action.outText("+++persistence requires 'io'", 30)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local mainDir = lfs.writedir() .. persistence.serverDir
|
||||
if not dcsCommon.stringEndsWith(mainDir, "\\") then
|
||||
mainDir = mainDir .. "\\"
|
||||
end
|
||||
-- lets see if we can access the server's mission directory and
|
||||
-- save directory
|
||||
-- we first try to access server's main mission directory, called "mainDir" which is usually <writeDir>/Missions/>
|
||||
|
||||
if persistence.isDir(mainDir) then
|
||||
if persistence.verbose then
|
||||
trigger.action.outText("persistence: main dir is <" .. mainDir .. ">", 30)
|
||||
end
|
||||
else
|
||||
if persistence.verbose then
|
||||
trigger.action.outText("+++persistence: Main directory <" .. mainDir .. "> not found or not a directory", 30)
|
||||
end
|
||||
return false
|
||||
end
|
||||
persistence.mainDir = mainDir
|
||||
|
||||
local missionDir = mainDir .. persistence.saveDir
|
||||
if not dcsCommon.stringEndsWith(missionDir, "\\") then
|
||||
missionDir = missionDir .. "\\"
|
||||
end
|
||||
|
||||
-- check if mission dir exists already
|
||||
local success, mode = persistence.hasFile(missionDir)
|
||||
if success and mode == "directory" then
|
||||
-- has been allocated, and is dir
|
||||
if persistence.verbose then
|
||||
trigger.action.outText("+++persistence: saving mission data to <" .. missionDir .. ">", 30)
|
||||
end
|
||||
elseif success then
|
||||
if persistence.verbose then
|
||||
trigger.action.outText("+++persistence: <" .. missionDir .. "> is not a directory", 30)
|
||||
end
|
||||
return false
|
||||
else
|
||||
-- does not exist, try to allocate it
|
||||
if persistence.verbose then
|
||||
trigger.action.outText("+++persistence: will now create <" .. missionDir .. ">", 30)
|
||||
end
|
||||
local ok, mkErr = lfs.mkdir(missionDir)
|
||||
if not ok then
|
||||
if persistence.verbose then
|
||||
trigger.action.outText("+++persistence: unable to create <" .. missionDir .. ">: <" .. mkErr .. ">", 30)
|
||||
end
|
||||
return false
|
||||
end
|
||||
if persistence.verbose then
|
||||
trigger.action.outText("+++persistence: created <" .. missionDir .. "> successfully, will save mission data here", 30)
|
||||
end
|
||||
end
|
||||
|
||||
persistence.missionDir = missionDir
|
||||
|
||||
persistence.active = true -- we can load and save data
|
||||
persistence.hasData = false -- we do not have save data
|
||||
|
||||
-- from here on we can read and write files in the missionDir
|
||||
-- read persistence attributes from all zones
|
||||
local attrZones = cfxZones.getZonesWithAttributeNamed("saveFlags")
|
||||
for k, aZone in pairs(attrZones) do
|
||||
persistence.collectFlagsFromZone(aZone) -- process attributes
|
||||
-- we do not retain the zone, it's job is done
|
||||
end
|
||||
|
||||
if persistence.verbose then
|
||||
trigger.action.outText("+++persistence is active", 30)
|
||||
end
|
||||
|
||||
-- we now see if we can and need load data
|
||||
persistence.missionStartDataLoad()
|
||||
|
||||
-- and start updating
|
||||
persistence.update()
|
||||
|
||||
|
||||
return persistence.active
|
||||
end
|
||||
|
||||
--
|
||||
-- go!
|
||||
--
|
||||
|
||||
if not persistence.start() then
|
||||
if persistence.verbose then
|
||||
trigger.action.outText("+++ persistence not available", 30)
|
||||
end
|
||||
-- we do NOT remove the methods so we don't crash
|
||||
end
|
||||
|
||||
-- add zones for saveFlags so authors can easily save flag values
|
||||
@ -1,5 +1,5 @@
|
||||
radioMenu = {}
|
||||
radioMenu.version = "1.0.1"
|
||||
radioMenu.version = "1.1.0"
|
||||
radioMenu.verbose = false
|
||||
radioMenu.ups = 1
|
||||
radioMenu.requiredLibs = {
|
||||
@ -12,6 +12,9 @@ radioMenu.menus = {}
|
||||
Version History
|
||||
1.0.0 Initial version
|
||||
1.0.1 spelling corrections
|
||||
1.1.0 removeMenu
|
||||
addMenu
|
||||
menuVisible
|
||||
|
||||
--]]--
|
||||
function radioMenu.addRadioMenu(theZone)
|
||||
@ -32,17 +35,60 @@ end
|
||||
--
|
||||
-- read zone
|
||||
--
|
||||
function radioMenu.installMenu(theZone)
|
||||
if theZone.coalition == 0 then
|
||||
theZone.rootMenu = missionCommands.addSubMenu(theZone.rootName, nil)
|
||||
else
|
||||
theZone.rootMenu = missionCommands.addSubMenuForCoalition(theZone.coalition, theZone.rootName, nil)
|
||||
end
|
||||
|
||||
local menuA = cfxZones.getStringFromZoneProperty(theZone, "itemA", "<no A submenu>")
|
||||
if theZone.coalition == 0 then
|
||||
theZone.menuA = missionCommands.addCommand(menuA, theZone.rootMenu, radioMenu.redirectMenuX, {theZone, "A"})
|
||||
else
|
||||
theZone.menuA = missionCommands.addCommandForCoalition(theZone.coalition, menuA, theZone.rootMenu, radioMenu.redirectMenuX, {theZone, "A"})
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "itemB") then
|
||||
local menuB = cfxZones.getStringFromZoneProperty(theZone, "itemB", "<no B submenu>")
|
||||
if theZone.coalition == 0 then
|
||||
theZone.menuB = missionCommands.addCommand(menuB, theZone.rootMenu, radioMenu.redirectMenuX, {theZone, "B"})
|
||||
else
|
||||
theZone.menuB = missionCommands.addCommandForCoalition(theZone.coalition, menuB, theZone.rootMenu, radioMenu.redirectMenuX, {theZone, "B"})
|
||||
end
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "itemC") then
|
||||
local menuC = cfxZones.getStringFromZoneProperty(theZone, "itemC", "<no C submenu>")
|
||||
if theZone.coalition == 0 then
|
||||
theZone.menuC = missionCommands.addCommand(menuC, theZone.rootMenu, radioMenu.redirectMenuX, {theZone, "C"})
|
||||
else
|
||||
theZone.menuC = missionCommands.addCommandForCoalition(theZone.coalition, menuC, theZone.rootMenu, radioMenu.redirectMenuX, {theZone, "C"})
|
||||
end
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "itemD") then
|
||||
local menuD = cfxZones.getStringFromZoneProperty(theZone, "itemD", "<no D submenu>")
|
||||
if theZone.coalition == 0 then
|
||||
theZone.menuD = missionCommands.addCommand(menuD, theZone.rootMenu, radioMenu.redirectMenuX, {theZone, "D"})
|
||||
else
|
||||
theZone.menuD = missionCommands.addCommandForCoalition(theZone.coalition, menuD, theZone.rootMenu, radioMenu.redirectMenuX, {theZone, "D"})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function radioMenu.createRadioMenuWithZone(theZone)
|
||||
local rootName = cfxZones.getStringFromZoneProperty(theZone, "radioMenu", "<No Name>")
|
||||
theZone.rootName = cfxZones.getStringFromZoneProperty(theZone, "radioMenu", "<No Name>")
|
||||
|
||||
theZone.coalition = cfxZones.getCoalitionFromZoneProperty(theZone, "coalition", 0)
|
||||
|
||||
if theZone.coalition == 0 then
|
||||
theZone.rootMenu = missionCommands.addSubMenu(rootName, nil)
|
||||
else
|
||||
theZone.rootMenu = missionCommands.addSubMenuForCoalition(theZone.coalition, rootName, nil)
|
||||
end
|
||||
theZone.menuVisible = cfxZones.getBoolFromZoneProperty(theZone, "menuVisible", true)
|
||||
|
||||
-- install menu if not hidden
|
||||
if theZone.menuVisible then
|
||||
radioMenu.installMenu(theZone)
|
||||
end
|
||||
--[[--
|
||||
-- now do the two options
|
||||
local menuA = cfxZones.getStringFromZoneProperty(theZone, "itemA", "<no A submenu>")
|
||||
if theZone.coalition == 0 then
|
||||
@ -78,6 +124,7 @@ function radioMenu.createRadioMenuWithZone(theZone)
|
||||
end
|
||||
end
|
||||
|
||||
--]]--
|
||||
|
||||
-- get the triggers & methods here
|
||||
theZone.radioMethod = cfxZones.getStringFromZoneProperty(theZone, "method", "inc")
|
||||
@ -85,6 +132,8 @@ function radioMenu.createRadioMenuWithZone(theZone)
|
||||
theZone.radioMethod = cfxZones.getStringFromZoneProperty(theZone, "radioMethod", "inc")
|
||||
end
|
||||
|
||||
theZone.radioTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "radioTriggerMethod", "change")
|
||||
|
||||
theZone.itemAChosen = cfxZones.getStringFromZoneProperty(theZone, "A!", "*<none>")
|
||||
theZone.cooldownA = cfxZones.getNumberFromZoneProperty(theZone, "cooldownA", 0)
|
||||
theZone.mcdA = 0
|
||||
@ -105,6 +154,16 @@ function radioMenu.createRadioMenuWithZone(theZone)
|
||||
theZone.mcdD = 0
|
||||
theZone.busyD = cfxZones.getStringFromZoneProperty(theZone, "busyD", "Please stand by (<s> seconds)")
|
||||
|
||||
if cfxZones.hasProperty(theZone, "removeMenu?") then
|
||||
theZone.removeMenu = cfxZones.getStringFromZoneProperty(theZone, "removeMenu?", "*<none>")
|
||||
theZone.lastRemoveMenu = cfxZones.getFlagValue(theZone.removeMenu, theZone)
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "addMenu?") then
|
||||
theZone.addMenu = cfxZones.getStringFromZoneProperty(theZone, "addMenu?", "*<none>")
|
||||
theZone.lastAddMenu = cfxZones.getFlagValue(theZone.addMenu, theZone)
|
||||
end
|
||||
|
||||
if radioMenu.verbose or theZone.verbose then
|
||||
trigger.action.outText("+++radioMenu: new radioMenu zone <".. theZone.name ..">", 30)
|
||||
end
|
||||
@ -189,13 +248,44 @@ end
|
||||
--
|
||||
-- Update -- required when we can enable/disable a zone's menu
|
||||
--
|
||||
--[[--
|
||||
|
||||
function radioMenu.update()
|
||||
-- call me in a second to poll triggers
|
||||
timer.scheduleFunction(radioMenu.update, {}, timer.getTime() + 1/radioMenu.ups)
|
||||
|
||||
-- iterate all menus
|
||||
for idx, theZone in pairs(radioMenu.menus) do
|
||||
if theZone.removeMenu
|
||||
and cfxZones.testZoneFlag(theZone, theZone.removeMenu, theZone.radioTriggerMethod, "lastRemoveMenu")
|
||||
and theZone.menuVisible
|
||||
then
|
||||
if theZone.verbose or radioMenu.verbose then
|
||||
trigger.action.outText("+++menu: removing <" .. dcsCommon.menu2text(theZone.rootMenu) .. "> for <" .. theZone.name .. ">", 30)
|
||||
end
|
||||
|
||||
if theZone.coalition == 0 then
|
||||
missionCommands.removeItem(theZone.rootMenu)
|
||||
else
|
||||
missionCommands.removeItemForCoalition(theZone.coalition, theZone.rootMenu)
|
||||
end
|
||||
|
||||
theZone.menuVisible = false
|
||||
end
|
||||
|
||||
if theZone.addMenu
|
||||
and cfxZones.testZoneFlag(theZone, theZone.addMenu, theZone.radioTriggerMethod, "lastAddMenu")
|
||||
and (not theZone.menuVisible)
|
||||
then
|
||||
if theZone.verbose or radioMenu.verbose then
|
||||
trigger.action.outText("+++menu: adding menu from <" .. theZone.name .. ">", 30)
|
||||
end
|
||||
|
||||
radioMenu.installMenu(theZone) -- auto-handles coalition
|
||||
theZone.menuVisible = true
|
||||
end
|
||||
end
|
||||
end
|
||||
--]]--
|
||||
|
||||
|
||||
--
|
||||
-- Config & Start
|
||||
@ -238,7 +328,7 @@ function radioMenu.start()
|
||||
end
|
||||
|
||||
-- start update
|
||||
--radioMenu.update()
|
||||
radioMenu.update()
|
||||
|
||||
trigger.action.outText("cfx radioMenu v" .. radioMenu.version .. " started.", 30)
|
||||
return true
|
||||
@ -251,7 +341,5 @@ if not radioMenu.start() then
|
||||
end
|
||||
|
||||
--[[--
|
||||
to do: turn on/off via flags
|
||||
callbacks for the menus
|
||||
one-shot items
|
||||
--]]--
|
||||
@ -1,13 +1,15 @@
|
||||
-- theDebugger
|
||||
debugger = {}
|
||||
debugger.version = "1.0.1"
|
||||
debugger.version = "1.1.1"
|
||||
debugDemon = {}
|
||||
debugDemon.version = "1.0.0"
|
||||
debugDemon.version = "1.1.1"
|
||||
|
||||
debugger.verbose = false
|
||||
debugger.ups = 4 -- every 0.25 second
|
||||
debugger.name = "DML Debugger" -- for compliance with cfxZones
|
||||
|
||||
debugger.log = ""
|
||||
|
||||
--[[--
|
||||
Version History
|
||||
1.0.0 - Initial version
|
||||
@ -15,14 +17,67 @@ debugger.name = "DML Debugger" -- for compliance with cfxZones
|
||||
- changed 'on' to 'active' in config zone
|
||||
- merged debugger and debugDemon
|
||||
- QoL check for 'debug' attribute (no '?')
|
||||
1.0.2 - contested! flag
|
||||
1.1.0 - logging
|
||||
- trigger.action --> debugger for outText
|
||||
- persistence of logs
|
||||
- save <name>
|
||||
1.1.1 - warning when trying to set a flag to a non-int
|
||||
|
||||
|
||||
--]]--
|
||||
|
||||
debugger.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxZones", -- Zones, of course
|
||||
}
|
||||
-- note: saving logs requires persistence module
|
||||
-- will auto-abort saving if not present
|
||||
|
||||
|
||||
debugger.debugZones = {}
|
||||
debugger.debugUnits = {}
|
||||
debugger.debugGroups = {}
|
||||
debugger.debugObjects = {}
|
||||
|
||||
--
|
||||
-- Logging & saving
|
||||
--
|
||||
|
||||
function debugger.outText(message, seconds, cls)
|
||||
if not message then message = "" end
|
||||
if not seconds then seconds = 20 end
|
||||
if not cls then cls = false end
|
||||
|
||||
-- append message to log, and add a lf
|
||||
if not debugger.log then debugger.log = "" end
|
||||
debugger.log = debugger.log .. message .. "\n"
|
||||
|
||||
-- now hand up to trigger
|
||||
trigger.action.outText(message, seconds, cls)
|
||||
end
|
||||
|
||||
function debugger.saveLog(name)
|
||||
if not _G["persistence"] then
|
||||
debugger.outText("+++debug: persistence module required to save log")
|
||||
return
|
||||
end
|
||||
|
||||
if not persistence.active then
|
||||
debugger.outText("+++debug: persistence module can't write. ensur you desanitize lfs and io")
|
||||
return
|
||||
end
|
||||
|
||||
if persistence.saveText(debugger.log, name) then
|
||||
debugger.outText("+++debug: log saved to <" .. persistence.missionDir .. name .. ">")
|
||||
else
|
||||
debugger.outText("+++debug: unable to save log to <" .. persistence.missionDir .. name .. ">")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- tracking flags
|
||||
--
|
||||
|
||||
function debugger.addDebugger(theZone)
|
||||
table.insert(debugger.debugZones, theZone)
|
||||
@ -33,7 +88,7 @@ function debugger.getDebuggerByName(aName)
|
||||
if aName == aZone.name then return aZone end
|
||||
end
|
||||
if debugger.verbose then
|
||||
trigger.action.outText("+++debug: no debug zone with name <" .. aName ..">", 30)
|
||||
debugger.outText("+++debug: no debug zone with name <" .. aName ..">", 30)
|
||||
end
|
||||
|
||||
return nil
|
||||
@ -65,7 +120,7 @@ function debugger.createDebuggerWithZone(theZone)
|
||||
|
||||
-- say who we are and what we are monitoring
|
||||
if debugger.verbose or theZone.verbose then
|
||||
trigger.action.outText("---debug: adding zone <".. theZone.name .."> to look for <value " .. theZone.debugInputMethod .. "> in flag(s):", 30)
|
||||
debugger.outText("---debug: adding zone <".. theZone.name .."> to look for <value " .. theZone.debugInputMethod .. "> in flag(s):", 30)
|
||||
end
|
||||
|
||||
-- read main debug array
|
||||
@ -77,7 +132,7 @@ function debugger.createDebuggerWithZone(theZone)
|
||||
for idx, aFlag in pairs(flagArray) do
|
||||
local fVal = cfxZones.getFlagValue(aFlag, theZone)
|
||||
if debugger.verbose or theZone.verbose then
|
||||
trigger.action.outText(" monitoring flag <" .. aFlag .. ">, inital value is <" .. fVal .. ">", 30)
|
||||
debugger.outText(" monitoring flag <" .. aFlag .. ">, inital value is <" .. fVal .. ">", 30)
|
||||
end
|
||||
valueArray[aFlag] = fVal
|
||||
end
|
||||
@ -226,7 +281,7 @@ function debugger.debugZone(theZone)
|
||||
-- generate the ouput message
|
||||
local msg = theZone.debugMsg
|
||||
msg = debugger.processDebugMsg(msg, theZone, aFlag, oldVal, newValue)
|
||||
trigger.action.outText(msg, 30)
|
||||
debugger.outText(msg, 30)
|
||||
end
|
||||
end
|
||||
|
||||
@ -239,7 +294,7 @@ function debugger.resetObserver(theZone)
|
||||
for idf, aFlag in pairs(theZone.flagArray) do
|
||||
local fVal = cfxZones.getFlagValue(aFlag, theZone)
|
||||
if debugger.verbose or theZone.verbose then
|
||||
trigger.action.outText("---debug: resetting flag <" .. aFlag .. ">, to <" .. fVal .. "> for zone <" .. theZone.name .. ">", 30)
|
||||
debugger.outText("---debug: resetting flag <" .. aFlag .. ">, to <" .. fVal .. "> for zone <" .. theZone.name .. ">", 30)
|
||||
end
|
||||
theZone.valueArray[aFlag] = fVal
|
||||
end
|
||||
@ -256,39 +311,39 @@ function debugger.showObserverState(theZone)
|
||||
for idf, aFlag in pairs(theZone.flagArray) do
|
||||
local fVal = cfxZones.getFlagValue(aFlag, theZone)
|
||||
if debugger.verbose or theZone.verbose then
|
||||
trigger.action.outText(" state of flag <" .. aFlag .. ">: <" .. theZone.valueArray[aFlag] .. ">", 30)
|
||||
debugger.outText(" state of flag <" .. aFlag .. ">: <" .. theZone.valueArray[aFlag] .. ">", 30)
|
||||
end
|
||||
theZone.valueArray[aFlag] = fVal
|
||||
end
|
||||
end
|
||||
|
||||
function debugger.showState()
|
||||
trigger.action.outText("---debug: CURRENT STATE <" .. dcsCommon.nowString() .. "> --- ", 30)
|
||||
debugger.outText("---debug: CURRENT STATE <" .. dcsCommon.nowString() .. "> --- ", 30)
|
||||
for idx, theZone in pairs(debugger.debugZones) do
|
||||
-- show this zone's state
|
||||
if #theZone.flagArray > 0 then
|
||||
trigger.action.outText(" state of observer <" .. theZone.name .. "> looking for <value " .. theZone.debugInputMethod .. ">:", 30)
|
||||
debugger.outText(" state of observer <" .. theZone.name .. "> looking for <value " .. theZone.debugInputMethod .. ">:", 30)
|
||||
debugger.showObserverState(theZone)
|
||||
else
|
||||
if theZone.verbose or debugger.verbose then
|
||||
trigger.action.outText(" (empty observer <" .. theZone.name .. ">)", 30)
|
||||
debugger.outText(" (empty observer <" .. theZone.name .. ">)", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
trigger.action.outText("---debug: end of state --- ", 30)
|
||||
debugger.outText("---debug: end of state --- ", 30)
|
||||
end
|
||||
|
||||
function debugger.doActivate()
|
||||
debugger.active = true
|
||||
if debugger.verbose or true then
|
||||
trigger.action.outText("+++ DM Debugger is now active", 30)
|
||||
debugger.outText("+++ DM Debugger is now active", 30)
|
||||
end
|
||||
end
|
||||
|
||||
function debugger.doDeactivate()
|
||||
debugger.active = false
|
||||
if debugger.verbose or true then
|
||||
trigger.action.outText("+++ debugger deactivated", 30)
|
||||
debugger.outText("+++ debugger deactivated", 30)
|
||||
end
|
||||
end
|
||||
|
||||
@ -339,7 +394,7 @@ function debugger.readConfigZone()
|
||||
local theZone = cfxZones.getZoneByName("debuggerConfig")
|
||||
if not theZone then
|
||||
if debugger.verbose then
|
||||
trigger.action.outText("+++debug: NO config zone!", 30)
|
||||
debugger.outText("+++debug: NO config zone!", 30)
|
||||
end
|
||||
theZone = cfxZones.createSimpleZone("debuggerConfig")
|
||||
end
|
||||
@ -371,7 +426,7 @@ function debugger.readConfigZone()
|
||||
debugger.ups = cfxZones.getNumberFromZoneProperty(theZone, "ups", 4)
|
||||
|
||||
if debugger.verbose then
|
||||
trigger.action.outText("+++debug: read config", 30)
|
||||
debugger.outText("+++debug: read config", 30)
|
||||
end
|
||||
end
|
||||
|
||||
@ -398,22 +453,22 @@ function debugger.start()
|
||||
|
||||
local attrZones = cfxZones.getZonesWithAttributeNamed("debug")
|
||||
for k, aZone in pairs(attrZones) do
|
||||
trigger.action.outText("***Warning: Zone <" .. aZone.name .. "> has a 'debug' flag. Are you perhaps missing a '?'", 30)
|
||||
debugger.outText("***Warning: Zone <" .. aZone.name .. "> has a 'debug' flag. Are you perhaps missing a '?'", 30)
|
||||
end
|
||||
|
||||
-- say if we are active
|
||||
if debugger.verbose then
|
||||
if debugger.active then
|
||||
trigger.action.outText("+++debugger loaded and active", 30)
|
||||
debugger.outText("+++debugger loaded and active", 30)
|
||||
else
|
||||
trigger.action.outText("+++ debugger: standing by for activation", 30)
|
||||
debugger.outText("+++ debugger: standing by for activation", 30)
|
||||
end
|
||||
end
|
||||
|
||||
-- start update
|
||||
debugger.update()
|
||||
|
||||
trigger.action.outText("cfx debugger v" .. debugger.version .. " started.", 30)
|
||||
debugger.outText("cfx debugger v" .. debugger.version .. " started.", 30)
|
||||
return true
|
||||
end
|
||||
|
||||
@ -441,6 +496,7 @@ debugDemon.verbose = false
|
||||
--[[--
|
||||
Version History
|
||||
1.0.0 - initial version
|
||||
1.1.0 - save command, requires persistence
|
||||
|
||||
--]]--
|
||||
|
||||
@ -559,7 +615,7 @@ function debugDemon.executeCommand(theCommands, event)
|
||||
local success = theInvoker(arguments, event)
|
||||
return success
|
||||
else
|
||||
trigger.action.outText("***error: unknown command <".. cmd .. ">", 30)
|
||||
debugger.outText("***error: unknown command <".. cmd .. ">", 30)
|
||||
return false
|
||||
end
|
||||
|
||||
@ -592,7 +648,7 @@ end
|
||||
-- COMMANDS
|
||||
--
|
||||
function debugDemon.processHelpCommand(args, event)
|
||||
trigger.action.outText("*** debugger: commands are:" ..
|
||||
debugger.outText("*** debugger: commands are:" ..
|
||||
"\n " .. debugDemon.markOfDemon .. "show <flagname/observername> -- show current values for flag or observer" ..
|
||||
"\n " .. debugDemon.markOfDemon .. "set <flagname> <number> -- set flag to value <number>" ..
|
||||
"\n " .. debugDemon.markOfDemon .. "inc <flagname> -- increase flag by 1, changing it" ..
|
||||
@ -614,6 +670,8 @@ trigger.action.outText("*** debugger: commands are:" ..
|
||||
"\n\n " .. debugDemon.markOfDemon .. "start -- starts debugger" ..
|
||||
"\n " .. debugDemon.markOfDemon .. "stop -- stop debugger" ..
|
||||
|
||||
"\n\n " .. debugDemon.markOfDemon .. "save [<filename>] -- saves debugger log to storage" ..
|
||||
|
||||
"\n\n " .. debugDemon.markOfDemon .. "? or -help -- this text", 30)
|
||||
return true
|
||||
end
|
||||
@ -622,14 +680,14 @@ function debugDemon.processNewCommand(args, event)
|
||||
-- syntax new <observername> [[for] <condition>]
|
||||
local observerName = args[1]
|
||||
if not observerName then
|
||||
trigger.action.outText("*** new: missing observer name.", 30)
|
||||
debugger.outText("*** new: missing observer name.", 30)
|
||||
return false -- allows correction
|
||||
end
|
||||
|
||||
-- see if this observer already existst
|
||||
local theObserver = debugger.getDebuggerByName(observerName)
|
||||
if theObserver then
|
||||
trigger.action.outText("*** new: observer <" .. observerName .. "> already exists.", 30)
|
||||
debugger.outText("*** new: observer <" .. observerName .. "> already exists.", 30)
|
||||
return false -- allows correction
|
||||
end
|
||||
|
||||
@ -638,7 +696,7 @@ function debugDemon.processNewCommand(args, event)
|
||||
local remainderName = event.remainder
|
||||
local rObserver = debugger.getDebuggerByName(remainderName)
|
||||
if rObserver then
|
||||
trigger.action.outText("*** new: observer <" .. remainderName .. "> already exists.", 30)
|
||||
debugger.outText("*** new: observer <" .. remainderName .. "> already exists.", 30)
|
||||
return false -- allows correction
|
||||
end
|
||||
|
||||
@ -655,14 +713,14 @@ function debugDemon.processNewCommand(args, event)
|
||||
if condition == "for" then condition = args[3] end
|
||||
if condition then
|
||||
if not cfxZones.verifyMethod(condition, theZone) then
|
||||
trigger.action.outText("*** new: illegal trigger condition <" .. condition .. "> for observer <" .. observerName .. ">", 30)
|
||||
debugger.outText("*** new: illegal trigger condition <" .. condition .. "> for observer <" .. observerName .. ">", 30)
|
||||
return false
|
||||
end
|
||||
theZone.debugInputMethod = condition
|
||||
end
|
||||
|
||||
debugger.addDebugger(theZone)
|
||||
trigger.action.outText("*** [" .. dcsCommon.nowString() .. "] debugger: new observer <" .. observerName .. "> for <" .. theZone.debugInputMethod .. ">", 30)
|
||||
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debugger: new observer <" .. observerName .. "> for <" .. theZone.debugInputMethod .. ">", 30)
|
||||
return true
|
||||
end
|
||||
|
||||
@ -670,14 +728,14 @@ function debugDemon.processUpdateCommand(args, event)
|
||||
-- syntax update <observername> [[to] <condition>]
|
||||
local observerName = args[1]
|
||||
if not observerName then
|
||||
trigger.action.outText("*** update: missing observer name.", 30)
|
||||
debugger.outText("*** update: missing observer name.", 30)
|
||||
return false -- allows correction
|
||||
end
|
||||
|
||||
-- see if this observer already existst
|
||||
local theZone = debugger.getDebuggerByName(observerName)
|
||||
if not theZone then
|
||||
trigger.action.outText("*** update: observer <" .. observerName .. "> does not exist exists.", 30)
|
||||
debugger.outText("*** update: observer <" .. observerName .. "> does not exist exists.", 30)
|
||||
return false -- allows correction
|
||||
end
|
||||
|
||||
@ -685,13 +743,13 @@ function debugDemon.processUpdateCommand(args, event)
|
||||
if condition == "to" then condition = args[3] end
|
||||
if condition then
|
||||
if not cfxZones.verifyMethod(condition, theZone) then
|
||||
trigger.action.outText("*** update: illegal trigger condition <" .. condition .. "> for observer <" .. observerName .. ">", 30)
|
||||
debugger.outText("*** update: illegal trigger condition <" .. condition .. "> for observer <" .. observerName .. ">", 30)
|
||||
return false
|
||||
end
|
||||
theZone.debugInputMethod = condition
|
||||
end
|
||||
|
||||
trigger.action.outText("*** [" .. dcsCommon.nowString() .. "] debugger: updated observer <" .. observerName .. "> to <" .. theZone.debugInputMethod .. ">", 30)
|
||||
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debugger: updated observer <" .. observerName .. "> to <" .. theZone.debugInputMethod .. ">", 30)
|
||||
return true
|
||||
end
|
||||
|
||||
@ -699,21 +757,21 @@ function debugDemon.processDropCommand(args, event)
|
||||
-- syntax drop <observername>
|
||||
local observerName = event.remainder -- remainder
|
||||
if not observerName then
|
||||
trigger.action.outText("*** drop: missing observer name.", 30)
|
||||
debugger.outText("*** drop: missing observer name.", 30)
|
||||
return false -- allows correction
|
||||
end
|
||||
|
||||
-- see if this observer already existst
|
||||
local theZone = debugger.getDebuggerByName(observerName)
|
||||
if not theZone then
|
||||
trigger.action.outText("*** drop: observer <" .. observerName .. "> does not exist exists.", 30)
|
||||
debugger.outText("*** drop: observer <" .. observerName .. "> does not exist exists.", 30)
|
||||
return false -- allows correction
|
||||
end
|
||||
|
||||
-- now simply and irrevocable remove the observer, unless it's home,
|
||||
-- in which case it's simply reset
|
||||
if theZone == debugDemon.observer then
|
||||
trigger.action.outText("*** drop: <" .. observerName .. "> is MY PRECIOUS and WILL NOT be dropped.", 30)
|
||||
debugger.outText("*** drop: <" .. observerName .. "> is MY PRECIOUS and WILL NOT be dropped.", 30)
|
||||
-- can't really happen since it contains blanks, but
|
||||
-- we've seen stranger things
|
||||
return false -- allows correction
|
||||
@ -721,7 +779,7 @@ function debugDemon.processDropCommand(args, event)
|
||||
|
||||
debugger.removeDebugger(theZone)
|
||||
|
||||
trigger.action.outText("*** [" .. dcsCommon.nowString() .. "] debugger: dropped observer <" .. observerName .. ">", 30)
|
||||
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debugger: dropped observer <" .. observerName .. ">", 30)
|
||||
return true
|
||||
end
|
||||
-- observe command: add a new flag to observe
|
||||
@ -730,7 +788,7 @@ function debugDemon.processObserveCommand(args, event)
|
||||
-- args[1] is the name of the flag
|
||||
local flagName = args[1]
|
||||
if not flagName then
|
||||
trigger.action.outText("*** observe: missing flag name.", 30)
|
||||
debugger.outText("*** observe: missing flag name.", 30)
|
||||
return false -- allows correction
|
||||
end
|
||||
|
||||
@ -738,7 +796,7 @@ function debugDemon.processObserveCommand(args, event)
|
||||
if args[2] == "with" then
|
||||
local aName = args[3]
|
||||
if not aName then
|
||||
trigger.action.outText("*** observe: missing <observer name> after 'with'.", 30)
|
||||
debugger.outText("*** observe: missing <observer name> after 'with'.", 30)
|
||||
return false -- allows correction
|
||||
end
|
||||
aName = dcsCommon.stringRemainsStartingWith(event.remainder, aName)
|
||||
@ -746,12 +804,12 @@ function debugDemon.processObserveCommand(args, event)
|
||||
if not withTracker then
|
||||
-- withTracker = debugDemon.createObserver(aName)
|
||||
-- debugger.addDebugger(withTracker)
|
||||
trigger.action.outText("*** observe: no observer <" .. aName .. "> exists", 30)
|
||||
debugger.outText("*** observe: no observer <" .. aName .. "> exists", 30)
|
||||
return false -- allows correction
|
||||
end
|
||||
else -- not with as arg 2
|
||||
if #args > 1 then
|
||||
trigger.action.outText("*** observe: unknown command after flag name '" .. flagName .. "'.", 30)
|
||||
debugger.outText("*** observe: unknown command after flag name '" .. flagName .. "'.", 30)
|
||||
return false -- allows correction
|
||||
end
|
||||
-- use own observer
|
||||
@ -759,13 +817,13 @@ function debugDemon.processObserveCommand(args, event)
|
||||
end
|
||||
|
||||
if debugger.isObservingWithObserver(flagName, withTracker) then
|
||||
trigger.action.outText("*** observe: already observing " .. flagName .. " with <" .. withTracker.name .. ">" , 30)
|
||||
debugger.outText("*** observe: already observing " .. flagName .. " with <" .. withTracker.name .. ">" , 30)
|
||||
return true
|
||||
end
|
||||
|
||||
-- we add flag to tracker and init value
|
||||
debugger.addFlagToObserver(flagName, withTracker)
|
||||
trigger.action.outText("*** [" .. dcsCommon.nowString() .. "] debugger: now observing <" .. flagName .. "> for value " .. withTracker.debugInputMethod .. " with <" .. withTracker.name .. ">.", 30)
|
||||
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debugger: now observing <" .. flagName .. "> for value " .. withTracker.debugInputMethod .. " with <" .. withTracker.name .. ">.", 30)
|
||||
return true
|
||||
end
|
||||
|
||||
@ -774,7 +832,7 @@ function debugDemon.processShowCommand(args, event)
|
||||
-- observer has precendce over flag
|
||||
local theName = args[1]
|
||||
if not theName then
|
||||
trigger.action.outText("*** show: missing observer/flag name.", 30)
|
||||
debugger.outText("*** show: missing observer/flag name.", 30)
|
||||
return false -- allows correction
|
||||
end
|
||||
|
||||
@ -785,12 +843,12 @@ function debugDemon.processShowCommand(args, event)
|
||||
if not theObserver then
|
||||
-- we directly use trigger.misc
|
||||
local fVal = trigger.misc.getUserFlag(theName)
|
||||
trigger.action.outText("[" .. dcsCommon.nowString() .. "] flag <" .. theName .. "> : value <".. fVal .. ">", 30)
|
||||
debugger.outText("[" .. dcsCommon.nowString() .. "] flag <" .. theName .. "> : value <".. fVal .. ">", 30)
|
||||
return true
|
||||
end
|
||||
|
||||
-- if we get here, we want to show an entire observer
|
||||
trigger.action.outText("*** [" .. dcsCommon.nowString() .. "] flags observed by <" .. theName .. "> looking for <value ".. theObserver.debugInputMethod .. ">:", 30)
|
||||
debugger.outText("*** [" .. dcsCommon.nowString() .. "] flags observed by <" .. theName .. "> looking for <value ".. theObserver.debugInputMethod .. ">:", 30)
|
||||
local flags = theObserver.flagArray
|
||||
local values = theObserver.valueArray
|
||||
for idx, flagName in pairs(flags) do
|
||||
@ -804,7 +862,7 @@ function debugDemon.processShowCommand(args, event)
|
||||
theMark = " ! "
|
||||
trailer = ", HIT!"
|
||||
end
|
||||
trigger.action.outText(theMark .. "f:<" .. flagName .. "> = <".. fVal .. "> [current, state = <" .. values[flagName] .. ">" .. trailer .. "]", 30)
|
||||
debugger.outText(theMark .. "f:<" .. flagName .. "> = <".. fVal .. "> [current, state = <" .. values[flagName] .. ">" .. trailer .. "]", 30)
|
||||
end
|
||||
|
||||
return true
|
||||
@ -836,7 +894,7 @@ function debugDemon.processSnapCommand(args, event)
|
||||
theName = dcsCommon.stringRemainsStartingWith(event.remainder, theName)
|
||||
theObserver = debugger.getDebuggerByName(theName)
|
||||
if not theObserver then
|
||||
trigger.action.outText("*** snap: unknown observer name <" .. theName .. ">.", 30)
|
||||
debugger.outText("*** snap: unknown observer name <" .. theName .. ">.", 30)
|
||||
return false -- allows correction
|
||||
end
|
||||
end
|
||||
@ -861,26 +919,26 @@ function debugDemon.processSnapCommand(args, event)
|
||||
|
||||
local sz = dcsCommon.getSizeOfTable(snapshot)
|
||||
debugDemon.snapshot = snapshot
|
||||
trigger.action.outText("*** [" .. dcsCommon.nowString() .. "] debug: new snapshot created, " .. sz .. " flags.", 30)
|
||||
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debug: new snapshot created, " .. sz .. " flags.", 30)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function debugDemon.processCompareCommand(args, event)
|
||||
trigger.action.outText("*** [" .. dcsCommon.nowString() .. "] debug: comparing snapshot with current flag values", 30)
|
||||
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debug: comparing snapshot with current flag values", 30)
|
||||
for flagName, val in pairs (debugDemon.snapshot) do
|
||||
local cVal = trigger.misc.getUserFlag(flagName)
|
||||
local mark = ' '
|
||||
if cVal ~= val then mark = ' ! ' end
|
||||
trigger.action.outText(mark .. "<" .. flagName .. "> snap = <" .. val .. ">, now = <" .. cVal .. "> " .. mark, 30)
|
||||
debugger.outText(mark .. "<" .. flagName .. "> snap = <" .. val .. ">, now = <" .. cVal .. "> " .. mark, 30)
|
||||
end
|
||||
trigger.action.outText("*** END", 30)
|
||||
debugger.outText("*** END", 30)
|
||||
return true
|
||||
end
|
||||
|
||||
function debugDemon.processNoteCommand(args, event)
|
||||
local n = event.remainder
|
||||
trigger.action.outText("*** [" .. dcsCommon.nowString() .. "]: " .. n, 30)
|
||||
debugger.outText("*** [" .. dcsCommon.nowString() .. "]: " .. n, 30)
|
||||
return true
|
||||
end
|
||||
|
||||
@ -888,7 +946,7 @@ function debugDemon.processSetCommand(args, event)
|
||||
-- syntax set <flagname> <value>
|
||||
local theName = args[1]
|
||||
if not theName then
|
||||
trigger.action.outText("*** set: missing flag name.", 30)
|
||||
debugger.outText("*** set: missing flag name.", 30)
|
||||
return false -- allows correction
|
||||
end
|
||||
|
||||
@ -900,13 +958,21 @@ function debugDemon.processSetCommand(args, event)
|
||||
end
|
||||
|
||||
if not theVal or not (tonumber(theVal)) then
|
||||
trigger.action.outText("*** set: missing or illegal value for flag <" .. theName .. ">.", 30)
|
||||
debugger.outText("*** set: missing or illegal value for flag <" .. theName .. ">.", 30)
|
||||
return false -- allows correction
|
||||
end
|
||||
|
||||
-- we set directly, no cfxZones procing
|
||||
trigger.action.outText("*** [" .. dcsCommon.nowString() .. "] debug: set flag <" .. theName .. "> to <" .. theVal .. ">", 30)
|
||||
theVal = tonumber(theVal)
|
||||
trigger.action.setUserFlag(theName, theVal)
|
||||
-- we set directly, no cfxZones proccing
|
||||
local note =""
|
||||
-- flags are ints only?
|
||||
if theVal ~= math.floor(theVal) then
|
||||
note = " [int! " .. math.floor(theVal) .. "]"
|
||||
end
|
||||
|
||||
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debug: set flag <" .. theName .. "> to <" .. theVal .. ">" .. note, 30)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
@ -914,7 +980,7 @@ function debugDemon.processIncCommand(args, event)
|
||||
-- syntax inc <flagname>
|
||||
local theName = args[1]
|
||||
if not theName then
|
||||
trigger.action.outText("*** inc: missing flag name.", 30)
|
||||
debugger.outText("*** inc: missing flag name.", 30)
|
||||
return false -- allows correction
|
||||
end
|
||||
|
||||
@ -922,7 +988,7 @@ function debugDemon.processIncCommand(args, event)
|
||||
local nVal = cVal + 1
|
||||
|
||||
-- we set directly, no cfxZones procing
|
||||
trigger.action.outText("*** [" .. dcsCommon.nowString() .. "] debug: inc flag <" .. theName .. "> from <" .. cVal .. "> to <" .. nVal .. ">", 30)
|
||||
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debug: inc flag <" .. theName .. "> from <" .. cVal .. "> to <" .. nVal .. ">", 30)
|
||||
trigger.action.setUserFlag(theName, nVal)
|
||||
return true
|
||||
end
|
||||
@ -931,7 +997,7 @@ function debugDemon.processFlipCommand(args, event)
|
||||
-- syntax flip <flagname>
|
||||
local theName = args[1]
|
||||
if not theName then
|
||||
trigger.action.outText("*** flip: missing flag name.", 30)
|
||||
debugger.outText("*** flip: missing flag name.", 30)
|
||||
return false -- allows correction
|
||||
end
|
||||
|
||||
@ -939,7 +1005,7 @@ function debugDemon.processFlipCommand(args, event)
|
||||
if cVal == 0 then nVal = 1 else nVal = 0 end
|
||||
|
||||
-- we set directly, no cfxZones procing
|
||||
trigger.action.outText("*** [" .. dcsCommon.nowString() .. "] debug: flipped flag <" .. theName .. "> from <" .. cVal .. "> to <" .. nVal .. ">", 30)
|
||||
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debug: flipped flag <" .. theName .. "> from <" .. cVal .. "> to <" .. nVal .. ">", 30)
|
||||
trigger.action.setUserFlag(theName, nVal)
|
||||
return true
|
||||
end
|
||||
@ -952,9 +1018,9 @@ function debugDemon.processListCommand(args, event)
|
||||
prefix = event.remainder -- dcsCommon.stringRemainsStartingWith(event.text, prefix)
|
||||
end
|
||||
if prefix then
|
||||
trigger.action.outText("*** [" .. dcsCommon.nowString() .. "] listing observers whose name contains <" .. prefix .. ">:", 30)
|
||||
debugger.outText("*** [" .. dcsCommon.nowString() .. "] listing observers whose name contains <" .. prefix .. ">:", 30)
|
||||
else
|
||||
trigger.action.outText("*** [" .. dcsCommon.nowString() .. "] listing all observers:", 30)
|
||||
debugger.outText("*** [" .. dcsCommon.nowString() .. "] listing all observers:", 30)
|
||||
end
|
||||
|
||||
local allObservers = debugger.debugZones
|
||||
@ -966,7 +1032,7 @@ function debugDemon.processListCommand(args, event)
|
||||
end
|
||||
|
||||
if doList then
|
||||
trigger.action.outText(" <" .. theName .. "> for <value " .. theZone.debugInputMethod .. "> (" .. #theZone.flagArray .. " flags)", 30)
|
||||
debugger.outText(" <" .. theName .. "> for <value " .. theZone.debugInputMethod .. "> (" .. #theZone.flagArray .. " flags)", 30)
|
||||
end
|
||||
end
|
||||
return true
|
||||
@ -976,20 +1042,20 @@ function debugDemon.processWhoCommand(args, event)
|
||||
-- syntax: who <flagname>
|
||||
local flagName = event.remainder -- args[1]
|
||||
if not flagName or flagName:len()<1 then
|
||||
trigger.action.outText("*** who: missing flag name.", 30)
|
||||
debugger.outText("*** who: missing flag name.", 30)
|
||||
return false -- allows correction
|
||||
end
|
||||
|
||||
local observers = debugger.isObserving(flagName)
|
||||
|
||||
if not observers or #observers < 1 then
|
||||
trigger.action.outText("*** [" .. dcsCommon.nowString() .. "] flag <" .. flagName .. "> is currently not observed", 30)
|
||||
debugger.outText("*** [" .. dcsCommon.nowString() .. "] flag <" .. flagName .. "> is currently not observed", 30)
|
||||
return false
|
||||
end
|
||||
|
||||
trigger.action.outText("*** [" .. dcsCommon.nowString() .. "] flag <" .. flagName .. "> is currently observed by", 30)
|
||||
debugger.outText("*** [" .. dcsCommon.nowString() .. "] flag <" .. flagName .. "> is currently observed by", 30)
|
||||
for idx, theZone in pairs(observers) do
|
||||
trigger.action.outText(" <" .. theZone.name .. "> looking for <value " .. theZone.debugInputMethod .. ">", 30)
|
||||
debugger.outText(" <" .. theZone.name .. "> looking for <value " .. theZone.debugInputMethod .. ">", 30)
|
||||
end
|
||||
|
||||
return true
|
||||
@ -1001,7 +1067,7 @@ function debugDemon.processForgetCommand(args, event)
|
||||
|
||||
local flagName = args[1]
|
||||
if not flagName then
|
||||
trigger.action.outText("*** forget: missing flag name.", 30)
|
||||
debugger.outText("*** forget: missing flag name.", 30)
|
||||
return false -- allows correction
|
||||
end
|
||||
|
||||
@ -1009,19 +1075,19 @@ function debugDemon.processForgetCommand(args, event)
|
||||
if args[2] == "with" or args[2] == "from" then -- we also allow 'from'
|
||||
local aName = args[3]
|
||||
if not aName then
|
||||
trigger.action.outText("*** forget: missing <observer name> after 'with'.", 30)
|
||||
debugger.outText("*** forget: missing <observer name> after 'with'.", 30)
|
||||
return false -- allows correction
|
||||
end
|
||||
|
||||
aName = dcsCommon.stringRemainsStartingWith(event.remainder, aName)
|
||||
withTracker = debugger.getDebuggerByName(aName)
|
||||
if not withTracker then
|
||||
trigger.action.outText("*** forget: no observer named <" .. aName .. ">", 30)
|
||||
debugger.outText("*** forget: no observer named <" .. aName .. ">", 30)
|
||||
return false
|
||||
end
|
||||
else -- not with as arg 2
|
||||
if #args > 1 then
|
||||
trigger.action.outText("*** forget: unknown command after flag name '" .. flagName .. "'.", 30)
|
||||
debugger.outText("*** forget: unknown command after flag name '" .. flagName .. "'.", 30)
|
||||
return false -- allows correction
|
||||
end
|
||||
-- use own observer
|
||||
@ -1029,13 +1095,13 @@ function debugDemon.processForgetCommand(args, event)
|
||||
end
|
||||
|
||||
if not debugger.isObservingWithObserver(flagName, withTracker) then
|
||||
trigger.action.outText("*** forget: observer <" .. withTracker.name .. "> does not observe flag <" .. flagName .. ">", 30)
|
||||
debugger.outText("*** forget: observer <" .. withTracker.name .. "> does not observe flag <" .. flagName .. ">", 30)
|
||||
return false
|
||||
end
|
||||
|
||||
-- we add flag to tracker and init value
|
||||
debugger.removeFlagFromObserver(flagName, withTracker)
|
||||
trigger.action.outText("*** [" .. dcsCommon.nowString() .. "] debugger: no longer observing " .. flagName .. " with <" .. withTracker.name .. ">.", 30)
|
||||
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debugger: no longer observing " .. flagName .. " with <" .. withTracker.name .. ">.", 30)
|
||||
return true
|
||||
end
|
||||
|
||||
@ -1057,23 +1123,35 @@ function debugDemon.processResetCommand(args, event)
|
||||
local obsName = args[1]
|
||||
if not obsName then
|
||||
debugger.reset() -- reset all
|
||||
trigger.action.outText("*** [" .. dcsCommon.nowString() .. "] debug: reset complete.", 30)
|
||||
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debug: reset complete.", 30)
|
||||
return true -- allows correction
|
||||
end
|
||||
|
||||
local withTracker = nil
|
||||
--if args[2] == "with" then
|
||||
local aName = args[1]
|
||||
aName = event.remainder -- dcsCommon.stringRemainsStartingWith(event.text, aName)
|
||||
local aName = event.remainder
|
||||
withTracker = debugger.getDebuggerByName(aName)
|
||||
if not withTracker then
|
||||
trigger.action.outText("*** reset: no observer <" .. aName .. ">", 30)
|
||||
debugger.outText("*** reset: no observer <" .. aName .. ">", 30)
|
||||
return false
|
||||
end
|
||||
|
||||
debugger.resetObserver(withTracker)
|
||||
|
||||
trigger.action.outText("*** [" .. dcsCommon.nowString() .. "] debugger:reset observer <" .. withTracker.name .. ">", 30)
|
||||
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debugger:reset observer <" .. withTracker.name .. ">", 30)
|
||||
return true
|
||||
end
|
||||
|
||||
function debugDemon.processSaveCommand(args, event)
|
||||
-- save log to file, requires persistence module
|
||||
-- syntax: -save [<fileName>]
|
||||
local aName = event.remainder
|
||||
if not aName or aName:len() < 1 then
|
||||
aName = "DML Debugger Log"
|
||||
end
|
||||
if not dcsCommon.stringEndsWith(aName, ".txt") then
|
||||
aName = aName .. ".txt"
|
||||
end
|
||||
debugger.saveLog(aName)
|
||||
return true
|
||||
end
|
||||
--
|
||||
@ -1084,7 +1162,7 @@ function debugDemon.readConfigZone()
|
||||
local theZone = cfxZones.getZoneByName("debugDemonConfig")
|
||||
if not theZone then
|
||||
if debugDemon.verbose then
|
||||
trigger.action.outText("+++debug: NO config zone!", 30)
|
||||
debugger.outText("+++debug (daemon): NO config zone!", 30)
|
||||
end
|
||||
theZone = cfxZones.createSimpleZone("debugDemonConfig")
|
||||
end
|
||||
@ -1099,7 +1177,7 @@ function debugDemon.readConfigZone()
|
||||
|
||||
|
||||
if debugger.verbose then
|
||||
trigger.action.outText("+++debug (deamon): read config", 30)
|
||||
debugger.outText("+++debug (deamon): read config", 30)
|
||||
end
|
||||
end
|
||||
|
||||
@ -1140,6 +1218,8 @@ function debugDemon.init()
|
||||
debugDemon.addCommndProcessor("stop", debugDemon.processStopCommand)
|
||||
debugDemon.addCommndProcessor("reset", debugDemon.processResetCommand)
|
||||
|
||||
debugDemon.addCommndProcessor("save", debugDemon.processSaveCommand)
|
||||
|
||||
debugDemon.addCommndProcessor("?", debugDemon.processHelpCommand)
|
||||
debugDemon.addCommndProcessor("help", debugDemon.processHelpCommand)
|
||||
|
||||
@ -1159,7 +1239,11 @@ function debugDemon.start()
|
||||
debugDemon.snapshot = debugDemon.createSnapshot(debugger.debugZones)
|
||||
debugDemon.demonID = world.addEventHandler(debugDemon)
|
||||
|
||||
trigger.action.outText("interactive debugDemon v" .. debugDemon.version .. " started" .. "\n enter " .. debugDemon.markOfDemon .. "? in a map mark for help", 30)
|
||||
debugger.outText("interactive debugDemon v" .. debugDemon.version .. " started" .. "\n enter " .. debugDemon.markOfDemon .. "? in a map mark for help", 30)
|
||||
|
||||
if not _G["persistence"] then
|
||||
debugger.outText("\n note: '-save' disabled, no persistence module found", 30)
|
||||
end
|
||||
end
|
||||
|
||||
if debugDemon.init() then
|
||||
@ -1170,6 +1254,11 @@ else
|
||||
end
|
||||
|
||||
--[[--
|
||||
- track units/groups: health changes
|
||||
- track players: unit change
|
||||
- track units/groups/objects: health changes
|
||||
- track players: unit change, enter, exit
|
||||
- inspect objects, dumping category, life, if it's tasking, latLon, alt, speed, direction
|
||||
|
||||
- exec files. save all commands and then run them from script
|
||||
- remove units via delete and explode
|
||||
|
||||
--]]--
|
||||
|
||||
276
modules/unitPersistence.lua
Normal file
276
modules/unitPersistence.lua
Normal file
@ -0,0 +1,276 @@
|
||||
unitPersistence = {}
|
||||
unitPersistence.version = '1.0.0'
|
||||
unitPersistence.verbose = false
|
||||
unitPersistence.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxZones", -- Zones, of course
|
||||
"persistence",
|
||||
"cfxMX",
|
||||
}
|
||||
--[[--
|
||||
Version History
|
||||
1.0.0 - initial version
|
||||
|
||||
REQUIRES PERSISTENCE AND MX
|
||||
|
||||
Persist ME-placed ground units
|
||||
|
||||
--]]--
|
||||
unitPersistence.groundTroops = {} -- local buffered copy that we
|
||||
-- maintain from save to save
|
||||
unitPersistence.statics = {} -- locally unpacked and buffered static objects
|
||||
|
||||
--
|
||||
-- Save -- Callback
|
||||
--
|
||||
function unitPersistence.saveData()
|
||||
local theData = {}
|
||||
if unitPersistence.verbose then
|
||||
trigger.action.outText("+++unitPersistence: enter saveData", 30)
|
||||
end
|
||||
|
||||
-- theData contains last save
|
||||
-- we save GROUND units placed by ME on. we access a copy of MX data
|
||||
-- for ground troups, iterate through all groups, and create
|
||||
-- a replacement group here and now that is used to replace the one
|
||||
-- that is there when it was spawned
|
||||
for groupName, groupData in pairs(unitPersistence.groundTroops) do
|
||||
-- we update this record live and save it to file
|
||||
if not groupData.isDead then
|
||||
local gotALiveOne = false
|
||||
local allUnits = groupData.units
|
||||
for idx, theUnitData in pairs(allUnits) do
|
||||
if not theUnitData.isDead then
|
||||
local uName = theUnitData.name
|
||||
local gUnit = Unit.getByName(uName)
|
||||
if gUnit and gUnit:isExist() then
|
||||
-- got a live one!
|
||||
gotALiveOne = true
|
||||
-- update x and y and heading
|
||||
theUnitData.heading = dcsCommon.getUnitHeading(gUnit)
|
||||
pos = gUnit:getPoint()
|
||||
theUnitData.x = pos.x
|
||||
theUnitData.y = pos.z -- (!!)
|
||||
-- ground units do not use alt
|
||||
else
|
||||
theUnitData.isDead = true
|
||||
end -- is alive and exists?
|
||||
end -- unit not dead
|
||||
end -- iterate units in group
|
||||
groupData.isDead = not gotALiveOne
|
||||
end -- if group is not dead
|
||||
if unitPersistence.verbose then
|
||||
trigger.action.outText("unitPersistence: save - processed group <" .. groupName .. ">.", 30)
|
||||
end
|
||||
end
|
||||
|
||||
-- process all static objects placed with ME
|
||||
for oName, oData in pairs(unitPersistence.statics) do
|
||||
if not oData.isDead then
|
||||
-- fetch the object and see if it's still alive
|
||||
local theObject = StaticObject.getByName(oName)
|
||||
if theObject and theObject:isExist() then
|
||||
oData.heading = dcsCommon.getUnitHeading(theObject)
|
||||
pos = theObject:getPoint()
|
||||
oData.x = pos.x
|
||||
oData.y = pos.z -- (!!)
|
||||
oData.isDead = theObject:getLife() < 1
|
||||
-- trigger.action.outText("deadcheck: " .. oName .. " has health=" .. theObject:getLife(), 30)
|
||||
oData.dead = oData.isDead
|
||||
else
|
||||
oData.isDead = true
|
||||
oData.dead = true
|
||||
-- trigger.action.outText("deadcheck: " .. oName .. " certified dead", 30)
|
||||
end
|
||||
end
|
||||
if unitPersistence.verbose then
|
||||
local note = "(ok)"
|
||||
if oData.isDead then note = "(dead)" end
|
||||
trigger.action.outText("unitPersistence: save - processed group <" .. oName .. ">. " .. note, 30)
|
||||
end
|
||||
end
|
||||
|
||||
theData.version = unitPersistence.version
|
||||
theData.ground = unitPersistence.groundTroops
|
||||
theData.statics = unitPersistence.statics
|
||||
return theData
|
||||
end
|
||||
|
||||
--
|
||||
-- Load Mission Data
|
||||
--
|
||||
function unitPersistence.loadMission()
|
||||
local theData = persistence.getSavedDataForModule("unitPersistence")
|
||||
if not theData then
|
||||
if unitPersistence.verbose then
|
||||
trigger.action.outText("unitPersistence: no save date received, skipping.", 30)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if theData.version ~= unitPersistence.version then
|
||||
trigger.action.outText("\nWARNING!\nUnit data was saved with a different (older) version!\nProceed with caution, fresh start is recommended.\n", 30)
|
||||
end
|
||||
|
||||
-- we just loaded an updated version of unitPersistence.groundTroops
|
||||
-- now iterate all groups, update their positions and
|
||||
-- delete all dead groups or units
|
||||
-- because they currently should exist is the game
|
||||
-- note: if they don't exist in-game that is because mission was
|
||||
-- edited after last save
|
||||
local mismatchWarning = false
|
||||
if theData.ground then
|
||||
for groupName, groupData in pairs(theData.ground) do
|
||||
local theGroup = Group.getByName(groupName)
|
||||
if not theGroup then
|
||||
mismatchWarning = true
|
||||
elseif groupData.isDead then
|
||||
theGroup:destroy()
|
||||
else
|
||||
local newGroup = dcsCommon.clone(groupData)
|
||||
local newUnits = {}
|
||||
for idx, theUnitData in pairs(groupData.units) do
|
||||
-- filter all dead groups
|
||||
if theUnitData.isDead then
|
||||
-- skip it
|
||||
|
||||
else
|
||||
-- add it to new group
|
||||
table.insert(newUnits, theUnitData)
|
||||
end
|
||||
end
|
||||
-- replace old unit setup with new
|
||||
newGroup.units = newUnits
|
||||
|
||||
local cty = groupData.cty
|
||||
local cat = groupData.cat
|
||||
|
||||
-- destroy the old group
|
||||
--theGroup:destroy() -- will be replaced
|
||||
|
||||
-- spawn new one
|
||||
theGroup = coalition.addGroup(cty, cat, newGroup)
|
||||
if not theGroup then
|
||||
trigger.action.outText("+++ failed to add modified group <" .. groupName .. ">")
|
||||
end
|
||||
if unitPersistence.verbose then
|
||||
trigger.action.outText("+++unitPersistence: updated group <" .. groupName .. "> of cat <" .. cat .. "> for cty <" .. cty .. ">", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
if unitPersistence.verbose then
|
||||
trigger.action.outText("+++unitPersistence: no ground unit data.", 30)
|
||||
end
|
||||
end
|
||||
|
||||
-- and now the same for static objects
|
||||
if theData.statics then
|
||||
for name, staticData in pairs(theData.statics) do
|
||||
local theStatic = StaticObject.getByName(name)
|
||||
if not theStatic then
|
||||
mismatchWarning = true
|
||||
else
|
||||
local newStatic = dcsCommon.clone(staticData)
|
||||
local cty = staticData.cty
|
||||
local cat = staticData.cat
|
||||
-- spawn new one, replacing same.named old, dead if required
|
||||
gStatic = coalition.addStaticObject(cty, newStatic)
|
||||
if not gStatic then
|
||||
trigger.action.outText("+++ failed to add modified static <" .. name .. ">")
|
||||
end
|
||||
if unitPersistence.verbose then
|
||||
local note = ""
|
||||
if newStatic.dead then note = " (dead)" end
|
||||
trigger.action.outText("+++unitPersistence: updated static <" .. name .. "> for cty <" .. cty .. ">" .. note, 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if mismatchWarning then
|
||||
trigger.action.outText("\n+++WARNING: \nSaved data does not match mission. You should re-start from scratch\n", 30)
|
||||
end
|
||||
-- set mission according to data received from last save
|
||||
if unitPersistence.verbose then
|
||||
trigger.action.outText("unitPersistence: units set from save data.", 30)
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Start
|
||||
--
|
||||
function unitPersistence.start()
|
||||
-- lib check
|
||||
if (not dcsCommon) or (not dcsCommon.libCheck) then
|
||||
trigger.action.outText("unit persistence requires dcsCommon", 30)
|
||||
return false
|
||||
end
|
||||
if not dcsCommon.libCheck("unit persistence", unitPersistence.requiredLibs) then
|
||||
return false
|
||||
end
|
||||
|
||||
-- see if we even need to persist
|
||||
if not persistence.active then
|
||||
return true -- WARNING: true, but not really
|
||||
end
|
||||
|
||||
-- sign up for save callback
|
||||
callbacks = {}
|
||||
callbacks.persistData = unitPersistence.saveData
|
||||
persistence.registerModule("unitPersistence", callbacks)
|
||||
|
||||
-- create a local copy of the entire groundForces data that
|
||||
-- we maintain internally. It's fixed, and we work on our
|
||||
-- own copy for speed
|
||||
for gname, data in pairs(cfxMX.allGroundByName) do
|
||||
local gd = dcsCommon.clone(data) -- copy the record
|
||||
gd.isDead = false -- init new field to alive
|
||||
-- coalition and country
|
||||
gd.cat = cfxMX.catText2ID("vehicle")
|
||||
local gGroup = Group.getByName(gname)
|
||||
if not gGroup then
|
||||
trigger.action.outText("+++warning: group <" .. gname .. "> does not exist in-game!?", 30)
|
||||
else
|
||||
local firstUnit = gGroup:getUnit(1)
|
||||
gd.cty = firstUnit:getCountry()
|
||||
unitPersistence.groundTroops[gname] = gd
|
||||
end
|
||||
end
|
||||
|
||||
-- make local copies of all static MX objects
|
||||
-- that we also maintain internally, and convert them to game
|
||||
-- spawnable objects
|
||||
for name, mxData in pairs(cfxMX.allStaticByName) do
|
||||
-- statics in MX are built like groups, so we have to strip
|
||||
-- the outer shell and extract all 'units' which are actually
|
||||
-- objects. And there is usually only one
|
||||
for idx, staticData in pairs(mxData.units) do
|
||||
local theStatic = dcsCommon.clone(staticData)
|
||||
theStatic.isDead = false
|
||||
theStatic.groupId = mxData.groupId
|
||||
theStatic.cat = cfxMX.catText2ID("static")
|
||||
local gameOb = StaticObject.getByName(theStatic.name)
|
||||
if not gameOb then
|
||||
trigger.action.outText("+++warning: static object <" .. theStatic.name .. "> does not exist in-game!?", 30)
|
||||
else
|
||||
theStatic.cty = gameOb:getCountry()
|
||||
unitPersistence.statics[theStatic.name] = theStatic
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- when we run, persistence has run and may have data ready for us
|
||||
if persistence.hasData then
|
||||
unitPersistence.loadMission()
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
if not unitPersistence.start() then
|
||||
if unitPersistence.verbose then
|
||||
trigger.action.outText("+++ unit persistence not available", 30)
|
||||
end
|
||||
unitPersistence = nil
|
||||
end
|
||||
BIN
tutorial & demo missions/demo - Being persistent.miz
Normal file
BIN
tutorial & demo missions/demo - Being persistent.miz
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user