mirror of
https://github.com/weyne85/DML.git
synced 2025-10-29 16:57:49 +00:00
1963 lines
64 KiB
Lua
1963 lines
64 KiB
Lua
-- theDebugger
|
|
debugger = {}
|
|
debugger.version = "2.0.0"
|
|
debugDemon = {}
|
|
debugDemon.version = "2.0.0"
|
|
|
|
debugger.verbose = false
|
|
debugger.ups = 4 -- every 0.25 second
|
|
debugger.name = "DML Debugger" -- for compliance with cfxZones
|
|
|
|
debugger.log = ""
|
|
|
|
--[[--
|
|
Version History
|
|
2.0.0 - dmlZones OOP
|
|
- eventmon command
|
|
- eventmon all, off, event #
|
|
- standard events
|
|
- adding events via #
|
|
- events? attribute from any zone
|
|
- eventmon last command
|
|
- q - query MSE Lua variables
|
|
- w - write/overwrite MSE Lua variables
|
|
- a - analyse Lua tables / variables
|
|
- smoke
|
|
- spawn system with predefines
|
|
- spawn coalition
|
|
- spawn number
|
|
- spawn heading
|
|
- spawn types
|
|
- spawn aircraft: add waypoints
|
|
- spawn "?"
|
|
- debuggerSpawnTypes zone
|
|
- reading debuggerSpawnTypes
|
|
- removed some silly bugs / inconsistencies
|
|
--]]--
|
|
|
|
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 = {}
|
|
debugger.showEvents = {}
|
|
debugger.lastEvent = nil
|
|
|
|
debugDemon.eventList = {
|
|
["0"] = "S_EVENT_INVALID = 0",
|
|
["1"] = "S_EVENT_SHOT = 1",
|
|
["2"] = "S_EVENT_HIT = 2",
|
|
["3"] = "S_EVENT_TAKEOFF = 3",
|
|
["4"] = "S_EVENT_LAND = 4",
|
|
["5"] = "S_EVENT_CRASH = 5",
|
|
["6"] = "S_EVENT_EJECTION = 6",
|
|
["7"] = "S_EVENT_REFUELING = 7",
|
|
["8"] = "S_EVENT_DEAD = 8",
|
|
["9"] = "S_EVENT_PILOT_DEAD = 9",
|
|
["10"] = "S_EVENT_BASE_CAPTURED = 10",
|
|
["11"] = "S_EVENT_MISSION_START = 11",
|
|
["12"] = "S_EVENT_MISSION_END = 12",
|
|
["13"] = "S_EVENT_TOOK_CONTROL = 13",
|
|
["14"] = "S_EVENT_REFUELING_STOP = 14",
|
|
["15"] = "S_EVENT_BIRTH = 15",
|
|
["16"] = "S_EVENT_HUMAN_FAILURE = 16",
|
|
["17"] = "S_EVENT_DETAILED_FAILURE = 17",
|
|
["18"] = "S_EVENT_ENGINE_STARTUP = 18",
|
|
["19"] = "S_EVENT_ENGINE_SHUTDOWN = 19",
|
|
["20"] = "S_EVENT_PLAYER_ENTER_UNIT = 20",
|
|
["21"] = "S_EVENT_PLAYER_LEAVE_UNIT = 21",
|
|
["22"] = "S_EVENT_PLAYER_COMMENT = 22",
|
|
["23"] = "S_EVENT_SHOOTING_START = 23",
|
|
["24"] = "S_EVENT_SHOOTING_END = 24",
|
|
["25"] = "S_EVENT_MARK_ADDED = 25",
|
|
["26"] = "S_EVENT_MARK_CHANGE = 26",
|
|
["27"] = "S_EVENT_MARK_REMOVED = 27",
|
|
["28"] = "S_EVENT_KILL = 28",
|
|
["29"] = "S_EVENT_SCORE = 29",
|
|
["30"] = "S_EVENT_UNIT_LOST = 30",
|
|
["31"] = "S_EVENT_LANDING_AFTER_EJECTION = 31",
|
|
["32"] = "S_EVENT_PARATROOPER_LENDING = 32",
|
|
["33"] = "S_EVENT_DISCARD_CHAIR_AFTER_EJECTION = 33",
|
|
["34"] = "S_EVENT_WEAPON_ADD = 34",
|
|
["35"] = "S_EVENT_TRIGGER_ZONE = 35",
|
|
["36"] = "S_EVENT_LANDING_QUALITY_MARK = 36",
|
|
["37"] = "S_EVENT_BDA = 37",
|
|
["38"] = "S_EVENT_AI_ABORT_MISSION = 38",
|
|
["39"] = "S_EVENT_DAYNIGHT = 39",
|
|
["40"] = "S_EVENT_FLIGHT_TIME = 40",
|
|
["41"] = "S_EVENT_PLAYER_SELF_KILL_PILOT = 41",
|
|
["42"] = "S_EVENT_PLAYER_CAPTURE_AIRFIELD = 42",
|
|
["43"] = "S_EVENT_EMERGENCY_LANDING = 43",
|
|
["44"] = "S_EVENT_UNIT_CREATE_TASK = 44",
|
|
["45"] = "S_EVENT_UNIT_DELETE_TASK = 45",
|
|
["46"] = "S_EVENT_SIMULATION_START = 46",
|
|
["47"] = "S_EVENT_WEAPON_REARM = 47",
|
|
["48"] = "S_EVENT_WEAPON_DROP = 48",
|
|
["49"] = "S_EVENT_UNIT_TASK_TIMEOUT = 49",
|
|
["50"] = "S_EVENT_UNIT_TASK_STAGE = 50",
|
|
["51"] = "S_EVENT_MAC_SUBTASK_SCORE = 51",
|
|
["52"] = "S_EVENT_MAC_EXTRA_SCORE = 52",
|
|
["53"] = "S_EVENT_MISSION_RESTART = 53",
|
|
["54"] = "S_EVENT_MISSION_WINNER = 54",
|
|
["55"] = "S_EVENT_POSTPONED_TAKEOFF = 55",
|
|
["56"] = "S_EVENT_POSTPONED_LAND = 56",
|
|
["57"] = "S_EVENT_MAX = 57",
|
|
}
|
|
|
|
debugger.spawnTypes = {
|
|
["inf"] = "Soldier M4",
|
|
["ifv"] = "BTR-80",
|
|
["tank"] = "T-90",
|
|
["ship"] = "PERRY",
|
|
["helo"] = "AH-1W",
|
|
["jet"] = "MiG-21Bis",
|
|
["awacs"] = "A-50",
|
|
["ww2"] = "SpitfireLFMkIX",
|
|
["bomber"] = "B-52H",
|
|
["cargo"] = "ammo_cargo",
|
|
["sam"] = "Roland ADS",
|
|
["aaa"] = "ZSU-23-4 Shilka",
|
|
["arty"] = "M-109",
|
|
["truck"] = "KAMAZ Truck",
|
|
["drone"] = "MQ-9 Reaper",
|
|
["manpad"] = "Soldier stinger",
|
|
["obj"] = "house2arm"
|
|
}
|
|
--
|
|
-- 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)
|
|
end
|
|
|
|
function debugger.getDebuggerByName(aName)
|
|
for idx, aZone in pairs(debugger.debugZones) do
|
|
if aName == aZone.name then return aZone end
|
|
end
|
|
if debugger.verbose then
|
|
debugger.outText("+++debug: no debug zone with name <" .. aName ..">", 30)
|
|
end
|
|
|
|
return nil
|
|
end
|
|
|
|
function debugger.removeDebugger(theZone)
|
|
local filtered = {}
|
|
for idx, dZone in pairs(debugger.debugZones) do
|
|
if dZone == theZone then
|
|
else
|
|
table.insert(filtered, dZone)
|
|
end
|
|
end
|
|
debugger.debugZones = filtered
|
|
end
|
|
--
|
|
-- read zone
|
|
--
|
|
function debugger.createDebuggerWithZone(theZone)
|
|
-- watchflag input trigger
|
|
theZone.debugInputMethod = theZone:getStringFromZoneProperty( "triggerMethod", "change")
|
|
if theZone.hasProperty("debugTriggerMethod") then
|
|
theZone.debugInputMethod = theZone:getStringFromZoneProperty("debugTriggerMethod", "change")
|
|
elseif theZone:hasProperty("inputMethod") then
|
|
theZone.debugInputMethod = theZone:getStringFromZoneProperty(theZone, "inputMethod", "change")
|
|
elseif theZone:hasProperty("sayWhen") then
|
|
theZone.debugInputMethod = theZone:getStringFromZoneProperty("sayWhen", "change")
|
|
end
|
|
|
|
-- say who we are and what we are monitoring
|
|
if debugger.verbose or theZone.verbose then
|
|
debugger.outText("---debug: adding zone <".. theZone.name .."> to look for <value " .. theZone.debugInputMethod .. "> in flag(s):", 30)
|
|
end
|
|
|
|
-- read main debug array
|
|
local theFlags = theZone:getStringFromZoneProperty("debug?", "<none>")
|
|
-- now, create an array from that
|
|
local flagArray = cfxZones.flagArrayFromString(theFlags)
|
|
local valueArray = {}
|
|
-- now establish current values
|
|
for idx, aFlag in pairs(flagArray) do
|
|
local fVal = theZone:getFlagValue(aFlag)
|
|
if debugger.verbose or theZone.verbose then
|
|
debugger.outText(" monitoring flag <" .. aFlag .. ">, inital value is <" .. fVal .. ">", 30)
|
|
end
|
|
valueArray[aFlag] = fVal
|
|
end
|
|
theZone.flagArray = flagArray
|
|
theZone.valueArray = valueArray
|
|
|
|
-- DML output method
|
|
theZone.debugOutputMethod = theZone:getStringFromZoneProperty("method", "inc")
|
|
if theZone:hasProperty("outputMethod") then
|
|
theZone.debugOutputMethod = theZone:getStringFromZoneProperty("outputMethod", "inc")
|
|
end
|
|
if theZone:hasProperty("debugMethod") then
|
|
theZone.debugOutputMethod = theZone:getStringFromZoneProperty("debugMethod", "inc")
|
|
end
|
|
|
|
-- notify!
|
|
if theZone:hasProperty("notify!") then
|
|
theZone.debugNotify = theZone:getStringFromZoneProperty("notify!", "<none>")
|
|
end
|
|
|
|
-- debug message, can use all messenger vals plus <f> for flag name
|
|
-- we use out own default
|
|
-- with <f> meaning flag name, <p> previous value, <c> current value
|
|
theZone.debugMsg = theZone:getStringFromZoneProperty("debugMsg", "---debug: <t> -- Flag <f> changed from <p> to <c> [<z>]")
|
|
end
|
|
|
|
function debugger.createEventMonWithZone(theZone)
|
|
local theFlags = theZone:getStringFromZoneProperty("events?", "<none>")
|
|
local flagArray = cfxZones.flagArrayFromString(theFlags)
|
|
local valueArray = {}
|
|
-- now establish current values
|
|
if debugger.verbose or theZone.verbose then
|
|
debugger.outText("*** monitoring events defined in <" .. theZone.name .. ">:", 30)
|
|
end
|
|
for idx, aFlag in pairs(flagArray) do
|
|
local evt = tonumber(aFlag)
|
|
if evt and (debugger.verbose or theZone.verbose) then
|
|
if evt < 0 then evt = 0 end
|
|
if evt > 57 then evt = 57 end
|
|
debugger.showEvents[evt] = debugDemon.eventList[tostring(evt)]
|
|
debugger.outText(" monitoring event <" .. debugger.showEvents[evt] .. ">", 30)
|
|
end
|
|
end
|
|
end
|
|
|
|
--
|
|
-- Misc
|
|
--
|
|
function debugger.addFlagToObserver(flagName, theZone)
|
|
table.insert(theZone.flagArray, flagName)
|
|
local fVal = cfxZones.getFlagValue(flagName, theZone)
|
|
theZone.valueArray[flagName] = fVal
|
|
end
|
|
|
|
function debugger.removeFlagFromObserver(flagName, theZone)
|
|
local filtered = {}
|
|
for idy, aName in pairs(theZone.flagArray) do
|
|
if aName == flagName then
|
|
else
|
|
table.insert(filtered, aName)
|
|
end
|
|
end
|
|
theZone.flagArray = filtered
|
|
-- no need to clean up values, they are name-indexed. do it anyway
|
|
theZone.valueArray[flagName] = nil
|
|
end
|
|
|
|
function debugger.isObservingWithObserver(flagName, theZone)
|
|
for idy, aName in pairs(theZone.flagArray) do
|
|
if aName == flagName then
|
|
local val = theZone.valueArray[flagName]
|
|
return true, val
|
|
end
|
|
end
|
|
end
|
|
|
|
function debugger.isObserving(flagName)
|
|
-- scan all zones for flag, and return
|
|
-- zone, and flag value if observing
|
|
local observers = {}
|
|
for idx, theZone in pairs(debugger.debugZones) do
|
|
for idy, aName in pairs(theZone.flagArray) do
|
|
if aName == flagName then
|
|
table.insert(observers, theZone)
|
|
end
|
|
end
|
|
end
|
|
return observers
|
|
end
|
|
|
|
--
|
|
-- Update
|
|
--
|
|
function debugger.processDebugMsg(inMsg, theZone, theFlag, oldVal, currVal)
|
|
if not inMsg then return "<nil inMsg>" end
|
|
if not oldVal then oldVal = "<no val!>" else oldVal = tostring(oldVal) end
|
|
if not currVal then currVal = "<no val!>" else currVal = tostring(currVal) end
|
|
if not theFlag then theFlag = "<no flag!>" end
|
|
|
|
local formerType = type(inMsg)
|
|
if formerType ~= "string" then inMsg = tostring(inMsg) end
|
|
if not inMsg then inMsg = "<inMsg is incompatible type " .. formerType .. ">" end
|
|
|
|
-- build message by relacing wildcards
|
|
local outMsg = ""
|
|
|
|
-- replace line feeds
|
|
outMsg = inMsg:gsub("<n>", "\n")
|
|
if theZone then
|
|
outMsg = outMsg:gsub("<z>", theZone.name)
|
|
end
|
|
|
|
-- replace <C>, <p>, <f> with currVal, oldVal, flag
|
|
outMsg = outMsg:gsub("<c>", currVal)
|
|
outMsg = outMsg:gsub("<p>", oldVal)
|
|
outMsg = outMsg:gsub("<o>", oldVal) -- just for QoL
|
|
outMsg = outMsg:gsub("<f>", theFlag)
|
|
|
|
-- replace <t> with current mission time HMS
|
|
local absSecs = timer.getAbsTime()-- + env.mission.start_time
|
|
while absSecs > 86400 do
|
|
absSecs = absSecs - 86400 -- subtract out all days
|
|
end
|
|
local timeString = dcsCommon.processHMS("<:h>:<:m>:<:s>", absSecs)
|
|
outMsg = outMsg:gsub("<t>", timeString)
|
|
|
|
-- replace <lat> with lat of zone point and <lon> with lon of zone point
|
|
-- and <mgrs> with mgrs coords of zone point
|
|
if theZone then
|
|
local currPoint = cfxZones.getPoint(theZone)
|
|
local lat, lon, alt = coord.LOtoLL(currPoint)
|
|
lat, lon = dcsCommon.latLon2Text(lat, lon)
|
|
outMsg = outMsg:gsub("<lat>", lat)
|
|
outMsg = outMsg:gsub("<lon>", lon)
|
|
currPoint = cfxZones.getPoint(theZone)
|
|
local grid = coord.LLtoMGRS(coord.LOtoLL(currPoint))
|
|
local mgrs = grid.UTMZone .. ' ' .. grid.MGRSDigraph .. ' ' .. grid.Easting .. ' ' .. grid.Northing
|
|
outMsg = outMsg:gsub("<mgrs>", mgrs)
|
|
end
|
|
|
|
return outMsg
|
|
end
|
|
|
|
function debugger.debugZone(theZone)
|
|
-- check every flag of this zone
|
|
for idx, aFlag in pairs(theZone.flagArray) do
|
|
local oldVal = theZone.valueArray[aFlag]
|
|
local oldVal = theZone.valueArray[aFlag]
|
|
theZone.debugLastVal = oldVal
|
|
local hasChanged, newValue = cfxZones.testZoneFlag(
|
|
theZone,
|
|
aFlag,
|
|
theZone.debugInputMethod,
|
|
"debugLastVal")
|
|
-- we ALWAYS transfer latch back
|
|
theZone.valueArray[aFlag] = newValue
|
|
|
|
if hasChanged then
|
|
-- we are triggered
|
|
-- generate the ouput message
|
|
local msg = theZone.debugMsg
|
|
msg = debugger.processDebugMsg(msg, theZone, aFlag, oldVal, newValue)
|
|
debugger.outText(msg, 30)
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
--
|
|
-- reset debugger
|
|
--
|
|
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
|
|
debugger.outText("---debug: resetting flag <" .. aFlag .. ">, to <" .. fVal .. "> for zone <" .. theZone.name .. ">", 30)
|
|
end
|
|
theZone.valueArray[aFlag] = fVal
|
|
end
|
|
end
|
|
|
|
function debugger.reset()
|
|
for idx, theZone in pairs(debugger.debugZones) do
|
|
-- reset this zone
|
|
debugger.resetObserver(theZone)
|
|
end
|
|
end
|
|
|
|
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
|
|
debugger.outText(" state of flag <" .. aFlag .. ">: <" .. theZone.valueArray[aFlag] .. ">", 30)
|
|
end
|
|
theZone.valueArray[aFlag] = fVal
|
|
end
|
|
end
|
|
|
|
function debugger.showState()
|
|
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
|
|
debugger.outText(" state of observer <" .. theZone.name .. "> looking for <value " .. theZone.debugInputMethod .. ">:", 30)
|
|
debugger.showObserverState(theZone)
|
|
else
|
|
if theZone.verbose or debugger.verbose then
|
|
debugger.outText(" (empty observer <" .. theZone.name .. ">)", 30)
|
|
end
|
|
end
|
|
end
|
|
debugger.outText("---debug: end of state --- ", 30)
|
|
end
|
|
|
|
function debugger.doActivate()
|
|
debugger.active = true
|
|
if debugger.verbose or true then
|
|
debugger.outText("+++ DM Debugger is now active", 30)
|
|
end
|
|
end
|
|
|
|
function debugger.doDeactivate()
|
|
debugger.active = false
|
|
if debugger.verbose or true then
|
|
debugger.outText("+++ debugger deactivated", 30)
|
|
end
|
|
end
|
|
|
|
function debugger.update()
|
|
-- call me in a second to poll triggers
|
|
timer.scheduleFunction(debugger.update, {}, timer.getTime() + 1/debugger.ups)
|
|
|
|
-- first check for switch on or off
|
|
if debugger.onFlag then
|
|
if cfxZones.testZoneFlag(debugger, debugger.onFlag, "change","lastOn") then
|
|
debugger.doActivate()
|
|
end
|
|
end
|
|
|
|
if debugger.offFlag then
|
|
if cfxZones.testZoneFlag(debugger, debugger.offFlag, "change","lastOff") then
|
|
debugger.doDeactivate()
|
|
end
|
|
end
|
|
|
|
-- ALWAYS check for reset & state.
|
|
if debugger.resetFlag then
|
|
if cfxZones.testZoneFlag(debugger, debugger.resetFlag, "change","lastReset") then
|
|
debugger.reset()
|
|
end
|
|
end
|
|
|
|
if debugger.stateFlag then
|
|
if cfxZones.testZoneFlag(debugger, debugger.stateFlag, "change","lastState") then
|
|
debugger.showState()
|
|
end
|
|
end
|
|
|
|
-- only progress if we are on
|
|
if not debugger.active then return end
|
|
|
|
for idx, aZone in pairs(debugger.debugZones) do
|
|
-- see if we are triggered
|
|
debugger.debugZone(aZone)
|
|
end
|
|
end
|
|
|
|
|
|
--
|
|
-- Config & Start
|
|
--
|
|
function debugger.readConfigZone()
|
|
local theZone = cfxZones.getZoneByName("debuggerConfig")
|
|
if not theZone then
|
|
theZone = cfxZones.createSimpleZone("debuggerConfig")
|
|
end
|
|
debugger.configZone = theZone
|
|
|
|
debugger.active = theZone:getBoolFromZoneProperty("active", true)
|
|
debugger.verbose = theZone.verbose
|
|
|
|
if theZone:hasProperty("on?") then
|
|
debugger.onFlag = theZone:getStringFromZoneProperty("on?", "<none>")
|
|
debugger.lastOn = cfxZones.getFlagValue(debugger.onFlag, theZone)
|
|
end
|
|
|
|
if theZone:hasProperty("off?") then
|
|
debugger.offFlag = theZone:getStringFromZoneProperty("off?", "<none>")
|
|
debugger.lastOff = cfxZones.getFlagValue(debugger.offFlag, theZone)
|
|
end
|
|
|
|
if theZone:hasProperty("reset?") then
|
|
debugger.resetFlag = theZone:getStringFromZoneProperty("reset?", "<none>")
|
|
debugger.lastReset = cfxZones.getFlagValue(debugger.resetFlag, theZone)
|
|
end
|
|
|
|
if theZone:hasProperty("state?") then
|
|
debugger.stateFlag = theZone:getStringFromZoneProperty("state?", "<none>")
|
|
debugger.lastState = cfxZones.getFlagValue(debugger.stateFlag, theZone)
|
|
end
|
|
|
|
debugger.ups = theZone:getNumberFromZoneProperty("ups", 4)
|
|
end
|
|
|
|
function debugger.readSpawnTypeZone()
|
|
local theZone = cfxZones.getZoneByName("debuggerSpawnTypes")
|
|
if not theZone then
|
|
theZone = cfxZones.createSimpleZone("debuggerSpawnTypes")
|
|
end
|
|
local allAttribuites = theZone:getAllZoneProperties()
|
|
for attrName, aValue in pairs(allAttribuites) do
|
|
local theLow = string.lower(attrName)
|
|
local before = debugger.spawnTypes[theLow]
|
|
if before then
|
|
debugger.spawnTypes[theLow] = aValue
|
|
if theZone.verbose or debugger.verbose then
|
|
trigger.action.outText("+++debug: changed generic '" .. theLow .. "' from <" .. before .. "> to <" .. aValue .. ">", 30)
|
|
end
|
|
else
|
|
if theZone.verbose or debugger.verbose then
|
|
if theLow == "verbose" then -- filtered
|
|
else
|
|
trigger.action.outText("+++debug: generic '" .. theLow .. "' unknown, not replaced.", 30)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
function debugger.start()
|
|
-- lib check
|
|
if not dcsCommon.libCheck then
|
|
trigger.action.outText("cfx debugger requires dcsCommon", 30)
|
|
return false
|
|
end
|
|
if not dcsCommon.libCheck("cfx debugger", debugger.requiredLibs) then
|
|
return false
|
|
end
|
|
|
|
-- read config
|
|
debugger.readConfigZone()
|
|
|
|
-- read spawn types
|
|
debugger.readSpawnTypeZone()
|
|
|
|
-- process debugger Zones
|
|
-- old style
|
|
local attrZones = cfxZones.getZonesWithAttributeNamed("debug?")
|
|
for k, aZone in pairs(attrZones) do
|
|
debugger.createDebuggerWithZone(aZone) -- process attributes
|
|
debugger.addDebugger(aZone) -- add to list
|
|
end
|
|
|
|
local attrZones = cfxZones.getZonesWithAttributeNamed("debug")
|
|
for k, aZone in pairs(attrZones) do
|
|
debugger.outText("***Warning: Zone <" .. aZone.name .. "> has a 'debug' attribute. Are you perhaps missing a '?'", 30)
|
|
end
|
|
|
|
local attrZones = cfxZones.getZonesWithAttributeNamed("events?")
|
|
for k, aZone in pairs(attrZones) do
|
|
debugger.createEventMonWithZone(aZone) -- process attributes
|
|
end
|
|
|
|
local attrZones = cfxZones.getZonesWithAttributeNamed("events")
|
|
for k, aZone in pairs(attrZones) do
|
|
debugger.outText("***Warning: Zone <" .. aZone.name .. "> has an 'events' attribute. Are you perhaps missing a '?'", 30)
|
|
end
|
|
-- events
|
|
|
|
-- say if we are active
|
|
if debugger.verbose then
|
|
if debugger.active then
|
|
debugger.outText("+++debugger loaded and active", 30)
|
|
else
|
|
debugger.outText("+++ debugger: standing by for activation", 30)
|
|
end
|
|
end
|
|
|
|
-- start update
|
|
debugger.update()
|
|
|
|
debugger.outText("cfx debugger v" .. debugger.version .. " started.", 30)
|
|
return true
|
|
end
|
|
|
|
-- let's go!
|
|
if not debugger.start() then
|
|
trigger.action.outText("cfx debugger aborted: missing libraries", 30)
|
|
debugger = nil
|
|
end
|
|
|
|
|
|
--
|
|
-- DEBUG DEMON
|
|
--
|
|
|
|
debugDemon.myObserverName = "+DML Debugger+"
|
|
-- interactive interface for DML debugger
|
|
|
|
debugDemon.verbose = false
|
|
-- based on cfx stage demon
|
|
--[[--
|
|
Version History
|
|
1.0.0 - initial version
|
|
1.1.0 - save command, requires persistence
|
|
2.0.0 - eventmon
|
|
- dml zones OOP
|
|
--]]--
|
|
|
|
debugDemon.requiredLibs = {
|
|
"dcsCommon", -- always
|
|
"cfxZones", -- Zones, of course
|
|
"debugger",
|
|
}
|
|
debugDemon.markOfDemon = "-" -- all commands must start with this sequence
|
|
debugDemon.splitDelimiter = " "
|
|
debugDemon.commandTable = {} -- key, value pair for command processing per keyword
|
|
debugDemon.keepOpen = false -- keep mark open after a successful command
|
|
debugDemon.snapshot = {}
|
|
|
|
function debugDemon.hasMark(theString)
|
|
-- check if the string begins with the sequece to identify commands
|
|
if not theString then return false end
|
|
return theString:find(debugDemon.markOfDemon) == 1
|
|
end
|
|
|
|
|
|
-- main hook into DCS. Called whenever a Mark-related event happens
|
|
-- very simple: look if text begins with special sequence, and if so,
|
|
-- call the command processor.
|
|
function debugDemon:onEvent(theEvent)
|
|
-- first order of business: call the event monitor
|
|
debugDemon.doEventMon(theEvent)
|
|
|
|
-- now process our own
|
|
-- while we can hook into any of the three events,
|
|
-- we curently only utilize CHANGE Mark
|
|
if not (theEvent.id == world.event.S_EVENT_MARK_ADDED) and
|
|
not (theEvent.id == world.event.S_EVENT_MARK_CHANGE) and
|
|
not (theEvent.id == world.event.S_EVENT_MARK_REMOVED) then
|
|
-- not of interest for us, bye bye
|
|
return
|
|
end
|
|
|
|
-- when we get here, we have a mark event
|
|
|
|
if theEvent.id == world.event.S_EVENT_MARK_ADDED then
|
|
-- add mark is quite useless
|
|
end
|
|
|
|
if theEvent.id == world.event.S_EVENT_MARK_CHANGE then
|
|
-- when changed, the mark's text is examined for a command
|
|
-- if it starts with the 'mark' string ("-" by default) it is processed
|
|
-- by the command processor
|
|
-- if it is processed succesfully, the mark is immediately removed
|
|
-- else an error is displayed and the mark remains.
|
|
if debugDemon.hasMark(theEvent.text) then
|
|
-- strip the mark
|
|
local commandString = theEvent.text:sub(1+debugDemon.markOfDemon:len())
|
|
-- break remainder apart into <command> <arg1> ... <argn>
|
|
local commands = dcsCommon.splitString(commandString, debugDemon.splitDelimiter)
|
|
|
|
-- this is a command. process it and then remove it if it was executed successfully
|
|
local success = debugDemon.executeCommand(commands, theEvent)
|
|
|
|
-- remove this mark after successful execution
|
|
if success then
|
|
trigger.action.removeMark(theEvent.idx)
|
|
else
|
|
-- we could play some error sound
|
|
end
|
|
end
|
|
end
|
|
|
|
if theEvent.id == world.event.S_EVENT_MARK_REMOVED then
|
|
end
|
|
end
|
|
|
|
--
|
|
-- add / remove commands to/from vocabulary
|
|
--
|
|
function debugDemon.addCommndProcessor(command, processor)
|
|
debugDemon.commandTable[command:upper()] = processor
|
|
end
|
|
|
|
function debugDemon.removeCommandProcessor(command)
|
|
debugDemon.commandTable[command:upper()] = nil
|
|
end
|
|
|
|
--
|
|
-- process input arguments. Here we simply move them
|
|
-- up by one.
|
|
--
|
|
function debugDemon.getArgs(theCommands)
|
|
local args = {}
|
|
for i=2, #theCommands do
|
|
table.insert(args, theCommands[i])
|
|
end
|
|
return args
|
|
end
|
|
|
|
--
|
|
-- debug demon's main command interpreter.
|
|
-- magic lies in using the keywords as keys into a
|
|
-- function table that holds all processing functions
|
|
-- I wish we had that back in the Oberon days.
|
|
--
|
|
function debugDemon.executeCommand(theCommands, event)
|
|
local cmd = theCommands[1]
|
|
local arguments = debugDemon.getArgs(theCommands)
|
|
if not cmd then return false end
|
|
|
|
-- since we have a command in cmd, we remove this from
|
|
-- the string, and pass the remainder back
|
|
local remainder = event.text:sub(1 + debugDemon.markOfDemon:len())
|
|
remainder = dcsCommon.stripLF(remainder)
|
|
remainder = dcsCommon.trim(remainder)
|
|
remainder = remainder:sub(1+cmd:len())
|
|
remainder = dcsCommon.trim(remainder)
|
|
|
|
event.remainder = remainder
|
|
-- use the command as index into the table of functions
|
|
-- that handle them.
|
|
if debugDemon.commandTable[cmd:upper()] then
|
|
local theInvoker = debugDemon.commandTable[cmd:upper()]
|
|
local success = theInvoker(arguments, event)
|
|
return success
|
|
else
|
|
debugger.outText("***error: unknown command <".. cmd .. ">", 30)
|
|
return false
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
--
|
|
-- Helpers
|
|
--
|
|
|
|
function debugDemon.createObserver(aName)
|
|
local observer = cfxZones.createSimpleZone(aName)
|
|
observer.verbose = debugDemon.verbose
|
|
observer.debugInputMethod = "change"
|
|
observer.flagArray = {}
|
|
observer.valueArray = {}
|
|
observer.debugMsg = "---debug: <t> -- Flag <f> changed from <p> to <c> [<z>]"
|
|
return observer
|
|
end
|
|
--
|
|
-- COMMANDS
|
|
--
|
|
function debugDemon.processHelpCommand(args, event)
|
|
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" ..
|
|
"\n " .. debugDemon.markOfDemon .. "flip <flagname> -- when flag's value is 0, set it to 1, else to 0" ..
|
|
|
|
"\n\n " .. debugDemon.markOfDemon .. "observe <flagname> [with <observername>] -- observe a flag for change" ..
|
|
"\n " .. debugDemon.markOfDemon .. "o <flagname> [with <observername>] -- observe a flag for change" ..
|
|
"\n " .. debugDemon.markOfDemon .. "forget <flagname> [with <observername>] -- stop observing a flag" ..
|
|
"\n " .. debugDemon.markOfDemon .. "new <observername> [[for] <condition>] -- create observer for flags" ..
|
|
"\n " .. debugDemon.markOfDemon .. "update <observername> [to] <condition> -- change observer's condition" ..
|
|
"\n " .. debugDemon.markOfDemon .. "drop <observername> -- remove observer from debugger" ..
|
|
"\n " .. debugDemon.markOfDemon .. "list [<match>] -- list observers [name contains <match>]" ..
|
|
"\n " .. debugDemon.markOfDemon .. "who <flagname> -- all who observe <flagname>" ..
|
|
"\n " .. debugDemon.markOfDemon .. "reset [<observername>] -- reset all or only the named observer" ..
|
|
|
|
"\n\n " .. debugDemon.markOfDemon .. "snap [<observername>] -- create new snapshot of flags" ..
|
|
"\n " .. debugDemon.markOfDemon .. "compare -- compare snapshot flag values with current" ..
|
|
"\n " .. debugDemon.markOfDemon .. "note <your note> -- add <your note> to the text log" ..
|
|
"\n\n " .. debugDemon.markOfDemon .. "spawn [<number>] [<coalition>] <type> [heading=<number>] | [?] -- spawn" ..
|
|
"\n units/aircraft/objects (? for help)" ..
|
|
"\n " .. debugDemon.markOfDemon .. "remove <group/unit/object name> -- remove named item from mission" ..
|
|
"\n " .. debugDemon.markOfDemon .. "smoke <color> -- place colored smoke on the ground" ..
|
|
"\n " .. debugDemon.markOfDemon .. "boom <number> -- place explosion of strenght <number> on the ground" ..
|
|
|
|
"\n\n " .. debugDemon.markOfDemon .. "eventmon [all | off | <number> | ?] -- show events for all | none | event <number> | list" ..
|
|
"\n " .. debugDemon.markOfDemon .. "eventmon last -- analyse last reported event" ..
|
|
"\n\n " .. debugDemon.markOfDemon .. "q <Lua Var> -- Query value of Lua variable <Lua Var>" ..
|
|
"\n " .. debugDemon.markOfDemon .. "a <Lua Var> -- Analyse structure of Lua variable <Lua Var>" ..
|
|
"\n " .. debugDemon.markOfDemon .. "w <Lua Var> [=] <Lua Value> -- Write <Lua Value> to variable <Lua Var>" ..
|
|
"\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
|
|
|
|
function debugDemon.processNewCommand(args, event)
|
|
-- syntax new <observername> [[for] <condition>]
|
|
local observerName = args[1]
|
|
if not observerName then
|
|
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
|
|
debugger.outText("*** new: observer <" .. observerName .. "> already exists.", 30)
|
|
return false -- allows correction
|
|
end
|
|
|
|
-- little pitfall: what if the name contains blanks?
|
|
-- also check against remainder!
|
|
local remainderName = event.remainder
|
|
local rObserver = debugger.getDebuggerByName(remainderName)
|
|
if rObserver then
|
|
debugger.outText("*** new: observer <" .. remainderName .. "> already exists.", 30)
|
|
return false -- allows correction
|
|
end
|
|
|
|
local theZone = nil
|
|
local condition = args[2] -- may need remainder instead
|
|
if condition == "for" then
|
|
-- we use arg[2]
|
|
else
|
|
observerName = remainderName -- we use entire rest of line
|
|
end
|
|
|
|
theZone = debugDemon.createObserver(observerName)
|
|
|
|
if condition == "for" then condition = args[3] end
|
|
if condition then
|
|
if not cfxZones.verifyMethod(condition, theZone) then
|
|
debugger.outText("*** new: illegal trigger condition <" .. condition .. "> for observer <" .. observerName .. ">", 30)
|
|
return false
|
|
end
|
|
theZone.debugInputMethod = condition
|
|
end
|
|
|
|
debugger.addDebugger(theZone)
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debugger: new observer <" .. observerName .. "> for <" .. theZone.debugInputMethod .. ">", 30)
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processUpdateCommand(args, event)
|
|
-- syntax update <observername> [to] <condition>
|
|
local observerName = args[1]
|
|
if not observerName then
|
|
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
|
|
debugger.outText("*** update: observer <" .. observerName .. "> does not exist exists.", 30)
|
|
return false -- allows correction
|
|
end
|
|
|
|
local condition = args[2] -- may need remainder instead
|
|
if condition == "to" then condition = args[3] end
|
|
if condition then
|
|
if not cfxZones.verifyMethod(condition, theZone) then
|
|
debugger.outText("*** update: illegal trigger condition <" .. condition .. "> for observer <" .. observerName .. ">", 30)
|
|
return false
|
|
end
|
|
theZone.debugInputMethod = condition
|
|
end
|
|
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debugger: updated observer <" .. observerName .. "> to <" .. theZone.debugInputMethod .. ">", 30)
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processDropCommand(args, event)
|
|
-- syntax drop <observername>
|
|
local observerName = event.remainder -- remainder
|
|
if not observerName then
|
|
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
|
|
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
|
|
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
|
|
end
|
|
|
|
debugger.removeDebugger(theZone)
|
|
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debugger: dropped observer <" .. observerName .. ">", 30)
|
|
return true
|
|
end
|
|
-- observe command: add a new flag to observe
|
|
function debugDemon.processObserveCommand(args, event)
|
|
-- syntax: observe <flagname> [with <observername>]
|
|
-- args[1] is the name of the flag
|
|
local flagName = args[1]
|
|
if not flagName then
|
|
debugger.outText("*** observe: missing flag name.", 30)
|
|
return false -- allows correction
|
|
end
|
|
|
|
local withTracker = nil
|
|
if args[2] == "with" then
|
|
local aName = args[3]
|
|
if not aName then
|
|
debugger.outText("*** observe: 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
|
|
-- withTracker = debugDemon.createObserver(aName)
|
|
-- debugger.addDebugger(withTracker)
|
|
debugger.outText("*** observe: no observer <" .. aName .. "> exists", 30)
|
|
return false -- allows correction
|
|
end
|
|
else -- not with as arg 2
|
|
if #args > 1 then
|
|
debugger.outText("*** observe: unknown command after flag name '" .. flagName .. "'.", 30)
|
|
return false -- allows correction
|
|
end
|
|
-- use own observer
|
|
withTracker = debugDemon.observer
|
|
end
|
|
|
|
if debugger.isObservingWithObserver(flagName, withTracker) then
|
|
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)
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debugger: now observing <" .. flagName .. "> for value " .. withTracker.debugInputMethod .. " with <" .. withTracker.name .. ">.", 30)
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processShowCommand(args, event)
|
|
-- syntax -show <name> with name either flag or observer
|
|
-- observer has precendce over flag
|
|
local theName = args[1]
|
|
if not theName then
|
|
debugger.outText("*** show: missing observer/flag name.", 30)
|
|
return false -- allows correction
|
|
end
|
|
|
|
-- now see if we have an observer
|
|
theName = dcsCommon.stringRemainsStartingWith(event.remainder, theName)
|
|
local theObserver = debugger.getDebuggerByName(theName)
|
|
|
|
if not theObserver then
|
|
-- we directly use trigger.misc
|
|
local fVal = trigger.misc.getUserFlag(theName)
|
|
debugger.outText("[" .. dcsCommon.nowString() .. "] flag <" .. theName .. "> : value <".. fVal .. ">", 30)
|
|
return true
|
|
end
|
|
|
|
-- if we get here, we want to show an entire observer
|
|
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
|
|
local lastVal = values[flagName]
|
|
local fVal = cfxZones.getFlagValue(flagName, theObserver)
|
|
-- add code to detect if it would trigger here
|
|
local hit = cfxZones.testFlagByMethodForZone(fVal, lastVal, theObserver.debugInputMethod, theObserver)
|
|
local theMark = " "
|
|
local trailer = ""
|
|
if hit then
|
|
theMark = " ! "
|
|
trailer = ", HIT!"
|
|
end
|
|
debugger.outText(theMark .. "f:<" .. flagName .. "> = <".. fVal .. "> [current, state = <" .. values[flagName] .. ">" .. trailer .. "]", 30)
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
function debugDemon.createSnapshot(allObservers)
|
|
if not allObservers then allObservers = {debugDemon.observer} end
|
|
local snapshot = {}
|
|
for idx, theZone in pairs(allObservers) do
|
|
|
|
-- iterate each observer
|
|
for idy, flagName in pairs (theZone.flagArray) do
|
|
local fullName = cfxZones.expandFlagName(flagName, theZone)
|
|
local fVal = trigger.misc.getUserFlag(fullName)
|
|
snapshot[fullName] = fVal
|
|
end
|
|
end
|
|
return snapshot
|
|
end
|
|
|
|
function debugDemon.processSnapCommand(args, event)
|
|
-- syntax snap [<observername>]
|
|
local allObservers = debugger.debugZones -- default: all zones
|
|
local theObserver = nil
|
|
|
|
local theName = args[1]
|
|
if theName then
|
|
-- now see if we have an observer
|
|
theName = dcsCommon.stringRemainsStartingWith(event.remainder, theName)
|
|
theObserver = debugger.getDebuggerByName(theName)
|
|
if not theObserver then
|
|
debugger.outText("*** snap: unknown observer name <" .. theName .. ">.", 30)
|
|
return false -- allows correction
|
|
end
|
|
end
|
|
|
|
if theObserver then
|
|
allObservers = {}
|
|
table.insert(allObservers, theObserver)
|
|
end
|
|
|
|
-- set up snapshot
|
|
local snapshot = debugDemon.createSnapshot(allObservers)
|
|
|
|
local sz = dcsCommon.getSizeOfTable(snapshot)
|
|
debugDemon.snapshot = snapshot
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debug: new snapshot created, " .. sz .. " flags.", 30)
|
|
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processCompareCommand(args, event)
|
|
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
|
|
debugger.outText(mark .. "<" .. flagName .. "> snap = <" .. val .. ">, now = <" .. cVal .. "> " .. mark, 30)
|
|
end
|
|
debugger.outText("*** END", 30)
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processNoteCommand(args, event)
|
|
local n = event.remainder
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "]: " .. n, 30)
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processSetCommand(args, event)
|
|
-- syntax set <flagname> <value>
|
|
local theName = args[1]
|
|
if not theName then
|
|
debugger.outText("*** set: missing flag name.", 30)
|
|
return false -- allows correction
|
|
end
|
|
|
|
local theVal = args[2]
|
|
if theVal and type(theVal) == "string" then
|
|
theVal = theVal:upper()
|
|
if theVal == "YES" or theVal == "TRUE" then theVal = "1" end
|
|
if theVal == "NO" or theVal == "FALSE" then theVal = "0" end
|
|
end
|
|
|
|
if not theVal or not (tonumber(theVal)) then
|
|
debugger.outText("*** set: missing or illegal value for flag <" .. theName .. ">.", 30)
|
|
return false -- allows correction
|
|
end
|
|
|
|
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
|
|
|
|
function debugDemon.processIncCommand(args, event)
|
|
-- syntax inc <flagname>
|
|
local theName = args[1]
|
|
if not theName then
|
|
debugger.outText("*** inc: missing flag name.", 30)
|
|
return false -- allows correction
|
|
end
|
|
|
|
local cVal = trigger.misc.getUserFlag(theName)
|
|
local nVal = cVal + 1
|
|
|
|
-- we set directly, no cfxZones procing
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debug: inc flag <" .. theName .. "> from <" .. cVal .. "> to <" .. nVal .. ">", 30)
|
|
trigger.action.setUserFlag(theName, nVal)
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processFlipCommand(args, event)
|
|
-- syntax flip <flagname>
|
|
local theName = args[1]
|
|
if not theName then
|
|
debugger.outText("*** flip: missing flag name.", 30)
|
|
return false -- allows correction
|
|
end
|
|
|
|
local cVal = trigger.misc.getUserFlag(theName)
|
|
if cVal == 0 then nVal = 1 else nVal = 0 end
|
|
|
|
-- we set directly, no cfxZones procing
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debug: flipped flag <" .. theName .. "> from <" .. cVal .. "> to <" .. nVal .. ">", 30)
|
|
trigger.action.setUserFlag(theName, nVal)
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processListCommand(args, event)
|
|
-- syntax list or list <prefix>
|
|
local prefix = nil
|
|
prefix = args[1]
|
|
if prefix then
|
|
prefix = event.remainder -- dcsCommon.stringRemainsStartingWith(event.text, prefix)
|
|
end
|
|
if prefix then
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] listing observers whose name contains <" .. prefix .. ">:", 30)
|
|
else
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] listing all observers:", 30)
|
|
end
|
|
|
|
local allObservers = debugger.debugZones
|
|
for idx, theZone in pairs(allObservers) do
|
|
local theName = theZone.name
|
|
local doList = true
|
|
if prefix then
|
|
doList = dcsCommon.containsString(theName, prefix, false)
|
|
end
|
|
|
|
if doList then
|
|
debugger.outText(" <" .. theName .. "> for <value " .. theZone.debugInputMethod .. "> (" .. #theZone.flagArray .. " flags)", 30)
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processWhoCommand(args, event)
|
|
-- syntax: who <flagname>
|
|
local flagName = event.remainder -- args[1]
|
|
if not flagName or flagName:len()<1 then
|
|
debugger.outText("*** who: missing flag name.", 30)
|
|
return false -- allows correction
|
|
end
|
|
|
|
local observers = debugger.isObserving(flagName)
|
|
|
|
if not observers or #observers < 1 then
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] flag <" .. flagName .. "> is currently not observed", 30)
|
|
return false
|
|
end
|
|
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] flag <" .. flagName .. "> is currently observed by", 30)
|
|
for idx, theZone in pairs(observers) do
|
|
debugger.outText(" <" .. theZone.name .. "> looking for <value " .. theZone.debugInputMethod .. ">", 30)
|
|
end
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
function debugDemon.processForgetCommand(args, event)
|
|
-- syntax: forget <flagname> [with <observername>]
|
|
|
|
local flagName = args[1]
|
|
if not flagName then
|
|
debugger.outText("*** forget: missing flag name.", 30)
|
|
return false -- allows correction
|
|
end
|
|
|
|
local withTracker = nil
|
|
if args[2] == "with" or args[2] == "from" then -- we also allow 'from'
|
|
local aName = args[3]
|
|
if not aName then
|
|
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
|
|
debugger.outText("*** forget: no observer named <" .. aName .. ">", 30)
|
|
return false
|
|
end
|
|
else -- not with as arg 2
|
|
if #args > 1 then
|
|
debugger.outText("*** forget: unknown command after flag name '" .. flagName .. "'.", 30)
|
|
return false -- allows correction
|
|
end
|
|
-- use own observer
|
|
withTracker = debugDemon.observer
|
|
end
|
|
|
|
if not debugger.isObservingWithObserver(flagName, withTracker) then
|
|
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)
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debugger: no longer observing " .. flagName .. " with <" .. withTracker.name .. ">.", 30)
|
|
return true
|
|
end
|
|
|
|
|
|
function debugDemon.processStartCommand(args, event)
|
|
debugger.doActivate()
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processStopCommand(args, event)
|
|
debugger.doDeactivate()
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processResetCommand(args, event)
|
|
-- supports reset <observer>
|
|
-- syntax: forget <flagname> [with <observername>]
|
|
|
|
local obsName = args[1]
|
|
if not obsName then
|
|
debugger.reset() -- reset all
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debug: reset complete.", 30)
|
|
return true -- allows correction
|
|
end
|
|
|
|
local withTracker = nil
|
|
local aName = event.remainder
|
|
withTracker = debugger.getDebuggerByName(aName)
|
|
if not withTracker then
|
|
debugger.outText("*** reset: no observer <" .. aName .. ">", 30)
|
|
return false
|
|
end
|
|
|
|
debugger.resetObserver(withTracker)
|
|
|
|
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
|
|
|
|
function debugDemon.processRemoveCommand(args, event)
|
|
-- remove a group, unit or object
|
|
-- try group first
|
|
local aName = event.remainder
|
|
if not aName or aName:len() < 1 then
|
|
debugger.outText("*** remove: no remove target", 30)
|
|
return false
|
|
end
|
|
|
|
aName = dcsCommon.trim(aName)
|
|
local theGroup = Group.getByName(aName)
|
|
if theGroup and theGroup:isExist() then
|
|
theGroup:destroy()
|
|
debugger.outText("*** remove: removed group <" .. aName .. ">", 30)
|
|
return true
|
|
end
|
|
|
|
local theUnit = Unit.getByName(aName)
|
|
if theUnit and theUnit:isExist() then
|
|
theUnit:destroy()
|
|
debugger.outText("*** remove: removed unit <" .. aName .. ">", 30)
|
|
return true
|
|
end
|
|
|
|
local theStatic = StaticObject.getByName(aName)
|
|
if theStatic and theStatic:isExist() then
|
|
theStatic:destroy()
|
|
debugger.outText("*** remove: removed static object <" .. aName .. ">", 30)
|
|
return true
|
|
end
|
|
debugger.outText("*** remove: did not find anything called <" .. aName .. "> to remove", 30)
|
|
return true
|
|
end
|
|
|
|
function debugDemon.doEventMon(theEvent)
|
|
if not theEvent then return end
|
|
if not debugger.active then return end
|
|
local ID = theEvent.id
|
|
if debugger.showEvents[ID] then
|
|
-- we show this event
|
|
m = "*** event <" .. debugger.showEvents[ID] .. ">"
|
|
-- see if we have initiator
|
|
if theEvent.initiator then
|
|
local theUnit = theEvent.initiator
|
|
if Unit.isExist(theUnit) then
|
|
m = m .. " for "
|
|
if theUnit.getPlayerName and theUnit:getPlayerName() then
|
|
m = m .. "player = " .. theUnit:getPlayerName() .. " in "
|
|
end
|
|
m = m .. "unit <" .. theUnit:getName() .. ">"
|
|
end
|
|
end
|
|
debugger.outText(m, 30)
|
|
-- save it to lastevent so we can analyse
|
|
debugger.lastEvent = theEvent
|
|
end
|
|
end
|
|
|
|
debugDemon.m = ""
|
|
-- dumpVar2m, invoke externally dumpVar2m(varname, var)
|
|
function debugDemon.dumpVar2m(key, value, prefix, inrecursion)
|
|
-- based on common's dumpVar, appends to var "m"
|
|
if not inrecursion then
|
|
-- start, init m
|
|
debugDemon.m = "analysis of <" .. key .. ">\n==="
|
|
end
|
|
if not value then value = "nil" end
|
|
if not prefix then prefix = "" end
|
|
prefix = " " .. prefix
|
|
if type(value) == "table" then
|
|
debugDemon.m = debugDemon.m .. "\n" .. prefix .. key .. ": [ "
|
|
-- iterate through all kvp
|
|
for k,v in pairs (value) do
|
|
debugDemon.dumpVar2m(k, v, prefix, true)
|
|
end
|
|
debugDemon.m = debugDemon.m .. "\n" .. prefix .. " ] - end " .. key
|
|
|
|
elseif type(value) == "boolean" then
|
|
local b = "false"
|
|
if value then b = "true" end
|
|
debugDemon.m = debugDemon.m .. "\n" .. prefix .. key .. ": " .. b
|
|
|
|
else -- simple var, show contents, ends recursion
|
|
debugDemon.m = debugDemon.m .. "\n" .. prefix .. key .. ": " .. value
|
|
end
|
|
|
|
if not inrecursion then
|
|
-- output a marker to find in the log / screen
|
|
debugDemon.m = debugDemon.m .. "\n" .. "=== analysis end\n"
|
|
end
|
|
end
|
|
|
|
function debugDemon.processEventMonCommand(args, event)
|
|
-- turn event monitor all/off/?/last
|
|
-- syntax: -eventmon on|off
|
|
local aParam = dcsCommon.trim(event.remainder)
|
|
if not aParam or aParam:len() < 1 then
|
|
aParam = "all"
|
|
end
|
|
aParam = string.upper(aParam)
|
|
evtNum = tonumber(aParam)
|
|
if aParam == "ON" or aParam == "ALL" then
|
|
debugger.outText("*** eventmon: turned ON, showing ALL events", 30)
|
|
local events = {}
|
|
for idx,evt in pairs(debugDemon.eventList) do
|
|
events[tonumber(idx)] = evt
|
|
end
|
|
debugger.showEvents = events
|
|
elseif evtNum then -- add the numbered to
|
|
debugger.eventmon = false
|
|
if evtNum <= 0 then evtNum = 0 end
|
|
if evtNum >= 57 then evtNum = 35 end
|
|
debugger.showEvents[evtNum] = debugDemon.eventList[tostring(evtNum)]
|
|
debugger.outText("*** eventmon: added event <" .. debugger.showEvents[evtNum] .. ">", 30)
|
|
elseif aParam == "OFF" then
|
|
debugger.showEvents = {}
|
|
debugger.outText("*** eventmon: removed all events from monitor list", 30)
|
|
elseif aParam == "?" then
|
|
local m = "*** eventmon: currently tracking these events:"
|
|
for idx, evt in pairs(debugger.showEvents) do
|
|
m = m .. "\n" .. evt
|
|
end
|
|
debugger.outText(m .. "\n*** end of list", 30)
|
|
elseif aParam == "LAST" then
|
|
if debugger.lastEvent then
|
|
debugDemon.dumpVar2m("event", debugger.lastEvent)
|
|
debugger.outText(debugDemon.m, 39)
|
|
else
|
|
debugger.outText("*** eventmon: no event on record", 39)
|
|
end
|
|
else
|
|
debugger.outText("*** eventmon: unknown parameter <" .. event.remainder .. ">", 30)
|
|
end
|
|
return true
|
|
end
|
|
|
|
--
|
|
-- read and write directly to Lua tables
|
|
--
|
|
|
|
function debugDemon.processQueryCommand(args, event)
|
|
-- syntax -q <name> with name a (qualified) Lua table reference
|
|
local theName = args[1]
|
|
|
|
if not theName then
|
|
debugger.outText("*** q: missing Lua table/element name.", 30)
|
|
return false -- allows correction
|
|
end
|
|
theName = dcsCommon.stringRemainsStartingWith(event.remainder, theName)
|
|
|
|
-- put this into a string, and execute it
|
|
local exec = "return " .. theName
|
|
local f = loadstring(exec)
|
|
local res
|
|
if pcall(f) then
|
|
res = f()
|
|
if type(res) == "boolean" then
|
|
res = "[BOOL FALSE]"
|
|
if res then res = "[BOOL TRUE]" end
|
|
elseif type(res) == "table" then res = "[Lua Table]"
|
|
elseif type(res) == "nil" then res = "[NIL]"
|
|
elseif type(res) == "function" then res = "[Lua Function]"
|
|
elseif type(res) == "number" or type(res) == "string" then
|
|
res = res .. " (a " .. type(res) .. ")"
|
|
else res = "[Lua " .. type(res) .. "]"
|
|
end
|
|
else
|
|
res = "[Lua error]"
|
|
end
|
|
|
|
debugger.outText("[" .. dcsCommon.nowString() .. "] <" .. theName .. "> = ".. res, 30)
|
|
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processAnalyzeCommand(args, event)
|
|
-- syntax -a <name> with name a (qualified) Lua table reference
|
|
local theName = args[1]
|
|
|
|
if not theName then
|
|
debugger.outText("*** a: missing Lua table/element name.", 30)
|
|
return false -- allows correction
|
|
end
|
|
theName = dcsCommon.stringRemainsStartingWith(event.remainder, theName)
|
|
|
|
-- put this into a string, and execute it
|
|
local exec = "return " .. theName
|
|
local f = loadstring(exec)
|
|
local res
|
|
if pcall(f) then
|
|
res = f()
|
|
debugDemon.dumpVar2m(theName, res)
|
|
res = debugDemon.m
|
|
else
|
|
res = "[Lua error]"
|
|
end
|
|
|
|
debugger.outText("[" .. dcsCommon.nowString() .. "] <" .. theName .. "> = ".. res, 30)
|
|
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processWriteCommand(args, event)
|
|
-- syntax -w <name> <value> with name a (qualified) Lua table reference and value a Lua value (including strings, with quotes of course). {} means an empty set etc. you CAN call into DCS MSE with this, and create a lot of havoc.
|
|
-- also, allow "=" semantic, -w p = {x=1, y=2}
|
|
|
|
local theName = args[1]
|
|
if not theName then
|
|
debugger.outText("*** w: missing Lua table/element name.", 30)
|
|
return false -- allows correction
|
|
end
|
|
local param = args [2]
|
|
if param == "=" then param = args[3] end
|
|
if not param then
|
|
debugger.outText("*** w: missing value to set to")
|
|
return false
|
|
end
|
|
|
|
param = dcsCommon.stringRemainsStartingWith(event.remainder, param)
|
|
|
|
-- put this into a string, and execute it
|
|
local exec = theName .. " = " .. param
|
|
local f = loadstring(exec)
|
|
local res
|
|
if pcall(f) then
|
|
res = "<" .. theName .. "> set to <" .. param .. ">"
|
|
else
|
|
res = "[Unable to set - Lua error]"
|
|
end
|
|
|
|
debugger.outText("[" .. dcsCommon.nowString() .. "] " .. res, 30)
|
|
|
|
return true
|
|
end
|
|
|
|
--
|
|
-- smoke & boom
|
|
--
|
|
|
|
function debugDemon.processSmokeCommand(args, event)
|
|
-- syntax -color
|
|
local color = 0 -- green default
|
|
local colorCom = args[1]
|
|
if colorCom then
|
|
colorCom = colorCom:lower()
|
|
if colorCom == "red" or colorCom == "1" then color = 1
|
|
elseif colorCom == "white" or colorCom == "2" then color = 2
|
|
elseif colorCom == "orange" or colorCom == "3" then color = 3
|
|
elseif colorCom == "blue" or colorCom == "4" then color = 4
|
|
elseif colorCom == "green" or colorCom == "0" then color = 0
|
|
else
|
|
debugger.outText("*** smoke: unknown color <" .. colorCom .. ">, using green.", 30)
|
|
end
|
|
local pos = event.pos
|
|
local h = land.getHeight({x = pos.x, y = pos.z}) + 1
|
|
local p = { x = event.pos.x, y = h, z = event.pos.z}
|
|
trigger.action.smoke(p, color)
|
|
debugger.outText("*** smoke: placed smoke at <" .. dcsCommon.point2text(p, true) .. ">.", 30)
|
|
end
|
|
end
|
|
|
|
function debugDemon.processBoomCommand(args, event)
|
|
-- syntax -color
|
|
local power = 1 -- boom default
|
|
local powerCom = args[1]
|
|
if powerCom then
|
|
powerCom = tonumber(powerCom)
|
|
if powerCom then
|
|
power = powerCom
|
|
end
|
|
end
|
|
local pos = event.pos
|
|
local h = land.getHeight({x = pos.x, y = pos.z}) + 1
|
|
local p = { x = event.pos.x, y = h, z = event.pos.z}
|
|
trigger.action.explosion(p, power)
|
|
debugger.outText("*** boom: placed <" .. power .. "> explosion at <" .. dcsCommon.point2text(p, true) .. ">.", 30)
|
|
end
|
|
|
|
--
|
|
-- spawning units at the location of the mark
|
|
--
|
|
|
|
function debugDemon.getCoaFromCommand(args)
|
|
for i=1, #args do
|
|
local aParam = args[i]
|
|
if dcsCommon.stringStartsWith(aParam, "red", true) then return 1, i end
|
|
if dcsCommon.stringStartsWith(aParam, "blu", true) then return 2, i end
|
|
if dcsCommon.stringStartsWith(aParam, "neu", true) then return 0, i end
|
|
end
|
|
return 0, nil
|
|
end
|
|
|
|
function debugDemon.getAirFromCommand(args)
|
|
for i=1, #args do
|
|
local aParam = args[i]
|
|
if aParam:lower() == "inair" then return true, i end
|
|
if aParam:lower() == "air" then return true, i end
|
|
end
|
|
return false, nil
|
|
end
|
|
|
|
function debugDemon.getHeadingFromCommand(args)
|
|
for i=1, #args do
|
|
local aParam = args[i]
|
|
if dcsCommon.stringStartsWith(aParam, "heading=", true) then
|
|
local parts = dcsCommon.splitString(aParam, "=")
|
|
local num = parts[2]
|
|
if num and tonumber(num) then
|
|
return tonumber(num), i
|
|
end
|
|
end
|
|
end
|
|
return 0, nil
|
|
end
|
|
|
|
function debugDemon.getNumFromCommand(args)
|
|
for i=1, #args do
|
|
local aParam = args[i]
|
|
local num = tonumber(aParam)
|
|
if num then return num, i end
|
|
end
|
|
return 1, nil
|
|
end
|
|
|
|
function debugDemon.processSpawnCommand(args, event)
|
|
-- complex syntax:
|
|
-- spawn [red|blue|neutral] [number] <type> [heading=<number>] | "?"
|
|
local params = dcsCommon.clone(args)
|
|
-- for i=1, #params do
|
|
-- trigger.action.outText("arg[" .. i .."] = <" .. params[i] .. ">", 30)
|
|
-- end
|
|
|
|
-- get coalition from input
|
|
|
|
local coa, idx = debugDemon.getCoaFromCommand(params)
|
|
if idx then table.remove(params, idx) end
|
|
local inAir, idy = debugDemon.getAirFromCommand(params)
|
|
if idy then table.remove(params, idy) end
|
|
local num, idz = debugDemon.getNumFromCommand(params)
|
|
if idz then table.remove(params, idz) end
|
|
local heading, idk = debugDemon.getHeadingFromCommand(params)
|
|
if idk then table.remove(params, idk) end
|
|
|
|
local class = params[1]
|
|
if not class then
|
|
debugger.outText("*** spawn: missing keyword (what to spawn).", 30)
|
|
return
|
|
end
|
|
|
|
class = class:lower()
|
|
|
|
-- when we are here, we have reduced all params, so class is [1]
|
|
-- trigger.action.outText("spawn with class <" .. class .. ">, num <" .. num .. ">, inAir <" .. dcsCommon.bool2Text(inAir) .. ">, coa <" .. coa .. ">, hdg <" .. heading .. ">", 30)
|
|
heading = heading * 0.0174533 -- in rad
|
|
|
|
local pos = event.pos
|
|
local h = land.getHeight({x = pos.x, y = pos.z}) + 1
|
|
local p = { x = event.pos.x, y = h, z = event.pos.z}
|
|
|
|
if class == "tank" or class == "tanks" then
|
|
-- spawn the 'tank' class
|
|
local theType = debugger.spawnTypes["tank"]
|
|
return debugDemon.spawnTypeWithCat(theType, coa, num, p, nil,heading)
|
|
elseif class == "man" or class == "soldier" or class == "men" then
|
|
local theType = debugger.spawnTypes["inf"]
|
|
return debugDemon.spawnTypeWithCat(theType, coa, num, p, nil,heading)
|
|
|
|
elseif class == "inf" or class == "ifv" or class == "sam" or
|
|
class == "arty" or class == "aaa" then
|
|
local theType = debugger.spawnTypes[class]
|
|
return debugDemon.spawnTypeWithCat(theType, coa, num, p, nil,heading)
|
|
elseif class == "truck" or class == "trucks" then
|
|
local theType = debugger.spawnTypes["truck"]
|
|
return debugDemon.spawnTypeWithCat(theType, coa, num, p, nil,heading)
|
|
elseif class == "manpad" or class == "manpads" or class == "pad" or class == "pads" then
|
|
local theType = debugger.spawnTypes["manpad"]
|
|
return debugDemon.spawnTypeWithCat(theType, coa, num, p, nil,heading)
|
|
|
|
elseif class == "ship" or class == "ships" then
|
|
local theType = debugger.spawnTypes["ship"]
|
|
return debugDemon.spawnTypeWithCat(theType, coa, num, p, Group.Category.SHIP, heading)
|
|
|
|
elseif class == "jet" or class == "jets" then
|
|
local theType = debugger.spawnTypes["jet"]
|
|
return debugDemon.spawnAirWIthCat(theType, coa, num, p, nil, 1000, 160, heading)
|
|
|
|
elseif class == "ww2" then
|
|
local theType = debugger.spawnTypes[class]
|
|
return debugDemon.spawnAirWIthCat(theType, coa, num, p, nil, 1000, 100, heading)
|
|
|
|
elseif class == "bomber" or class == "awacs" then
|
|
local theType = debugger.spawnTypes[class]
|
|
return debugDemon.spawnAirWIthCat(theType, coa, num, p, nil, 8000, 200, heading)
|
|
|
|
elseif class == "drone" then
|
|
local theType = debugger.spawnTypes[class]
|
|
return debugDemon.spawnAirWIthCat(theType, coa, num, p, nil, 3000, 77, heading)
|
|
|
|
elseif class == "helo" or class == "helos" then
|
|
local theType = debugger.spawnTypes["helo"]
|
|
return debugDemon.spawnAirWIthCat(theType, coa, num, p, Group.Category.HELICOPTER, 200, 40, heading)
|
|
|
|
elseif class == "cargo" or class == "obj" then
|
|
local isCargo = (class == "cargo")
|
|
local theType = debugger.spawnTypes[class]
|
|
return debugDemon.spawnObjects(theType, coa, num, p, isCargo, heading)
|
|
|
|
elseif class == "?" then
|
|
local m = " spawn: invoke '-spawn [number] [coalition] <type> [heading]' with \n" ..
|
|
" number = any number, default is 1\n" ..
|
|
" coalition = 'red' | 'blue' | 'neutral', default is neutral\n" ..
|
|
" heading = 'heading=<number>' - direction to face, in degrees, no blanks\n" ..
|
|
" <type> = what to spawn, any of the following pre-defined (no quotes)\n" ..
|
|
" 'tank' - a tank " .. debugDemon.tellType("tank") .. "\n" ..
|
|
" 'ifv' - an IFV " .. debugDemon.tellType("ifv") .. "\n" ..
|
|
" 'inf' - an infantry soldier " .. debugDemon.tellType("inf") .. "\n" ..
|
|
" 'sam' - a SAM vehicle " .. debugDemon.tellType("sam") .. "\n" ..
|
|
" 'aaa' - a AAA vehicle " .. debugDemon.tellType("aaa") .. "\n" ..
|
|
" 'arty' - artillery vehicle " .. debugDemon.tellType("arty") .. "\n" ..
|
|
" 'manpad' - a soldier with SAM " .. debugDemon.tellType("manpad") .. "\n" ..
|
|
" 'truck' - a truck " .. debugDemon.tellType("truck") .. "\n\n" ..
|
|
" 'jet' - a fast aircraft " .. debugDemon.tellType("jet") .. "\n" ..
|
|
" 'ww2' - a warbird " .. debugDemon.tellType("ww2") .. "\n" ..
|
|
" 'bomber' - a heavy bomber " .. debugDemon.tellType("bomber") .. "\n" ..
|
|
" 'awacs' - an AWACS plane " .. debugDemon.tellType("awacs") .. "\n" ..
|
|
" 'drone' - a drone " .. debugDemon.tellType("drone") .. "\n" ..
|
|
" 'helo' - a helicopter " .. debugDemon.tellType("helo") .. "\n\n" ..
|
|
" 'ship' - a naval unit" .. debugDemon.tellType("ship") .. "\n\n" ..
|
|
" 'cargo' - some helicopter cargo " .. debugDemon.tellType("cargo") .. "\n" ..
|
|
" 'obj' - a static object " .. debugDemon.tellType("obj") .. "\n"
|
|
|
|
debugger.outText(m, 30)
|
|
return true
|
|
else
|
|
debugger.outText("*** spawn: unknown kind <" .. class .. ">.", 30)
|
|
return false
|
|
end
|
|
end
|
|
|
|
function debugDemon.tellType(theType)
|
|
return " [" .. debugger.spawnTypes[theType] .. "]"
|
|
end
|
|
|
|
function debugDemon.spawnTypeWithCat(theType, coa, num, p, cat, heading)
|
|
trigger.action.outText("heading is <" .. heading .. ">", 30)
|
|
if not cat then cat = Group.Category.GROUND end
|
|
if not heading then heading = 0 end
|
|
|
|
local xOff = 0
|
|
local yOff = 0
|
|
-- build group
|
|
local groupName = dcsCommon.uuid(theType)
|
|
local gData = dcsCommon.createEmptyGroundGroupData(groupName)
|
|
for i=1, num do
|
|
local aUnit = {}
|
|
aUnit = dcsCommon.createGroundUnitData(groupName .. "-" .. i, theType)
|
|
--aUnit.heading = heading
|
|
dcsCommon.addUnitToGroupData(aUnit, gData, xOff, yOff, heading)
|
|
xOff = xOff + 10
|
|
yOff = yOff + 10
|
|
end
|
|
|
|
-- arrange in a grid formation
|
|
local radius = math.floor(math.sqrt(num) * 10)
|
|
if cat == Group.Category.SHIP then
|
|
radius = math.floor(math.sqrt(num) * 100)
|
|
end
|
|
|
|
dcsCommon.arrangeGroupDataIntoFormation(gData, radius, 10, "GRID")
|
|
|
|
-- move to destination
|
|
dcsCommon.moveGroupDataTo(gData, p.x, p.z)
|
|
|
|
-- spawn
|
|
local cty = dcsCommon.getACountryForCoalition(coa)
|
|
local theGroup = coalition.addGroup(cty, cat, gData)
|
|
if theGroup then
|
|
debugger.outText("[" .. dcsCommon.nowString() .. "] created units at " .. dcsCommon.point2text(p, true), 30)
|
|
return true
|
|
else
|
|
debugger.outText("[" .. dcsCommon.nowString() .. "] failed to created units", 30)
|
|
return false
|
|
end
|
|
return false
|
|
end
|
|
|
|
function debugDemon.spawnAirWIthCat(theType, coa, num, p, cat, alt, speed, heading)
|
|
if not cat then cat = Group.Category.AIRPLANE end
|
|
local xOff = 0
|
|
local yOff = 0
|
|
-- build group
|
|
local groupName = dcsCommon.uuid(theType)
|
|
local gData = dcsCommon.createEmptyAircraftGroupData(groupName)
|
|
for i=1, num do
|
|
local aUnit = {}
|
|
aUnit = dcsCommon.createAircraftUnitData(groupName .. "-" .. i, theType, false, alt, speed)
|
|
--aUnit.heading = heading
|
|
dcsCommon.addUnitToGroupData(aUnit, gData, xOff, yOff, heading)
|
|
xOff = xOff + 30
|
|
yOff = yOff + 30
|
|
end
|
|
-- move to destination
|
|
dcsCommon.moveGroupDataTo(gData, p.x, p.z)
|
|
|
|
-- make waypoints: initial point and 200 km away in direction heading
|
|
local p2 = dcsCommon.pointInDirectionOfPointXYY(heading, 200000, p)
|
|
local wp1 = dcsCommon.createSimpleRoutePointData(p, alt, speed)
|
|
local wp2 = dcsCommon.createSimpleRoutePointData(p2, alt, speed)
|
|
-- add waypoints
|
|
dcsCommon.addRoutePointForGroupData(gData, wp1)
|
|
dcsCommon.addRoutePointForGroupData(gData, wp2)
|
|
|
|
-- spawn
|
|
local cty = dcsCommon.getACountryForCoalition(coa)
|
|
local theGroup = coalition.addGroup(cty, cat, gData)
|
|
if theGroup then
|
|
debugger.outText("[" .. dcsCommon.nowString() .. "] created air units at " .. dcsCommon.point2text(p, true), 30)
|
|
return true
|
|
else
|
|
debugger.outText("[" .. dcsCommon.nowString() .. "] failed to created air units", 30)
|
|
return false
|
|
end
|
|
end
|
|
|
|
function debugDemon.spawnObjects(theType, coa, num, p, cargo, heading)
|
|
if not cargo then cargo = false end
|
|
local cty = dcsCommon.getACountryForCoalition(coa)
|
|
local xOff = 0
|
|
local yOff = 0
|
|
local success = false
|
|
-- build static objects and spawn individually
|
|
for i=1, num do
|
|
local groupName = dcsCommon.uuid(theType)
|
|
local gData = dcsCommon.createStaticObjectData(groupName, theType, 0, false, cargo, 1000)
|
|
gData.x = xOff + p.x
|
|
gData.y = yOff + p.z
|
|
gData.heading = heading
|
|
local theGroup = coalition.addStaticObject(cty, gData)
|
|
success = theGroup
|
|
xOff = xOff + 10 -- stagger by 10m, 10m
|
|
yOff = yOff + 10
|
|
end
|
|
|
|
-- was it worth it?
|
|
if success then
|
|
debugger.outText("[" .. dcsCommon.nowString() .. "] created objects at " .. dcsCommon.point2text(p, true), 30)
|
|
return true
|
|
else
|
|
debugger.outText("[" .. dcsCommon.nowString() .. "] failed to create objects", 30)
|
|
return false
|
|
end
|
|
end
|
|
|
|
|
|
--
|
|
-- init and start
|
|
--
|
|
|
|
function debugDemon.readConfigZone()
|
|
local theZone = cfxZones.getZoneByName("debugDemonConfig")
|
|
if not theZone then
|
|
if debugDemon.verbose then
|
|
debugger.outText("+++debug (daemon): NO config zone!", 30)
|
|
end
|
|
theZone = cfxZones.createSimpleZone("debugDemonConfig")
|
|
end
|
|
debugDemon.configZone = theZone
|
|
|
|
debugDemon.keepOpen = cfxZones.getBoolFromZoneProperty(theZone, "keepOpen", false)
|
|
|
|
debugDemon.markOfDemon = cfxZones.getStringFromZoneProperty(theZone,"mark", "-") -- all commands must start with this sequence
|
|
|
|
|
|
debugDemon.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
|
|
|
|
|
if debugger.verbose then
|
|
debugger.outText("+++debug (deamon): read config", 30)
|
|
end
|
|
end
|
|
|
|
|
|
function debugDemon.init()
|
|
if not dcsCommon.libCheck then
|
|
trigger.action.outText("cfx interactive debugger requires dcsCommon", 30)
|
|
return false
|
|
end
|
|
if not dcsCommon.libCheck("cfx interactive debugger", debugDemon.requiredLibs) then
|
|
return false
|
|
end
|
|
|
|
-- config
|
|
debugDemon.readConfigZone()
|
|
|
|
-- now add known commands to interpreter.
|
|
debugDemon.addCommndProcessor("observe", debugDemon.processObserveCommand)
|
|
debugDemon.addCommndProcessor("o", debugDemon.processObserveCommand) -- synonym
|
|
|
|
debugDemon.addCommndProcessor("forget", debugDemon.processForgetCommand)
|
|
|
|
debugDemon.addCommndProcessor("show", debugDemon.processShowCommand)
|
|
debugDemon.addCommndProcessor("set", debugDemon.processSetCommand)
|
|
debugDemon.addCommndProcessor("inc", debugDemon.processIncCommand)
|
|
debugDemon.addCommndProcessor("flip", debugDemon.processFlipCommand)
|
|
debugDemon.addCommndProcessor("list", debugDemon.processListCommand)
|
|
debugDemon.addCommndProcessor("who", debugDemon.processWhoCommand)
|
|
|
|
debugDemon.addCommndProcessor("new", debugDemon.processNewCommand)
|
|
debugDemon.addCommndProcessor("update", debugDemon.processUpdateCommand)
|
|
debugDemon.addCommndProcessor("drop", debugDemon.processDropCommand)
|
|
|
|
debugDemon.addCommndProcessor("snap", debugDemon.processSnapCommand)
|
|
debugDemon.addCommndProcessor("compare", debugDemon.processCompareCommand)
|
|
debugDemon.addCommndProcessor("note", debugDemon.processNoteCommand)
|
|
|
|
debugDemon.addCommndProcessor("start", debugDemon.processStartCommand)
|
|
debugDemon.addCommndProcessor("stop", debugDemon.processStopCommand)
|
|
debugDemon.addCommndProcessor("reset", debugDemon.processResetCommand)
|
|
|
|
debugDemon.addCommndProcessor("save", debugDemon.processSaveCommand)
|
|
|
|
debugDemon.addCommndProcessor("?", debugDemon.processHelpCommand)
|
|
debugDemon.addCommndProcessor("help", debugDemon.processHelpCommand)
|
|
|
|
debugDemon.addCommndProcessor("remove", debugDemon.processRemoveCommand)
|
|
debugDemon.addCommndProcessor("spawn", debugDemon.processSpawnCommand)
|
|
debugDemon.addCommndProcessor("add", debugDemon.processSpawnCommand)
|
|
|
|
debugDemon.addCommndProcessor("eventmon", debugDemon.processEventMonCommand)
|
|
debugDemon.addCommndProcessor("q", debugDemon.processQueryCommand)
|
|
debugDemon.addCommndProcessor("w", debugDemon.processWriteCommand)
|
|
debugDemon.addCommndProcessor("a", debugDemon.processAnalyzeCommand)
|
|
debugDemon.addCommndProcessor("smoke", debugDemon.processSmokeCommand)
|
|
debugDemon.addCommndProcessor("boom", debugDemon.processBoomCommand)
|
|
return true
|
|
end
|
|
|
|
function debugDemon.start()
|
|
-- add my own debug zones to debugger so it can
|
|
-- track any changes
|
|
|
|
local observer = debugDemon.createObserver(debugDemon.myObserverName)
|
|
debugDemon.observer = observer
|
|
debugger.addDebugger(observer)
|
|
|
|
-- create initial snapshot
|
|
debugDemon.snapshot = debugDemon.createSnapshot(debugger.debugZones)
|
|
debugDemon.demonID = world.addEventHandler(debugDemon)
|
|
|
|
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
|
|
debugDemon.start()
|
|
else
|
|
trigger.action.outText("*** interactive debugger failed to initialize.", 30)
|
|
debugDemon = {}
|
|
end
|
|
|
|
--[[--
|
|
- 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
|
|
|
|
- xref: which zones/attributes reference a flag, g.g. '-xref go'
|
|
|
|
- track lua vars for change in value
|
|
|
|
--]]--
|