mirror of
https://github.com/weyne85/DML.git
synced 2025-10-29 16:57:49 +00:00
Version 2.0.0
Version II goes live!
This commit is contained in:
parent
663038db0a
commit
c9694c3176
Binary file not shown.
Binary file not shown.
@ -1,25 +1,22 @@
|
|||||||
cfxSSBSingleUse = {}
|
cfxSSBSingleUse = {}
|
||||||
cfxSSBSingleUse.version = "1.1.0"
|
|
||||||
|
|
||||||
--[[--
|
|
||||||
Version History
|
|
||||||
1.0.0 - Initial version
|
|
||||||
1.1.0 - importing dcsCommon, cfxGroup for simplicity
|
|
||||||
- save unit name on player enter unit as look-up
|
|
||||||
- determining ground-start
|
|
||||||
- place wreck in slot
|
|
||||||
1.1.1 - guarding against nil playerName
|
|
||||||
- using 15 (birth) instead of 20 (player enter)
|
|
||||||
WHAT IT IS
|
|
||||||
SSB Single Use is a script that blocks a player slot
|
|
||||||
after that plane crashes.
|
|
||||||
|
|
||||||
|
|
||||||
--]]--
|
|
||||||
|
|
||||||
cfxSSBSingleUse.enabledFlagValue = 0 -- DO NOT CHANGE, MUST MATCH SSB
|
cfxSSBSingleUse.enabledFlagValue = 0 -- DO NOT CHANGE, MUST MATCH SSB
|
||||||
cfxSSBSingleUse.disabledFlagValue = cfxSSBSingleUse.enabledFlagValue + 100 -- DO NOT CHANGE
|
cfxSSBSingleUse.disabledFlagValue = cfxSSBSingleUse.enabledFlagValue + 100 -- DO NOT CHANGE
|
||||||
|
|
||||||
|
cfxSSBSingleUse.version = "2.0.0"
|
||||||
|
cfxSSBSingleUse.verbose = true
|
||||||
|
cfxSSBSingleUse.useDebris = false
|
||||||
|
cfxSSBSingleUse.requiredLibs = {
|
||||||
|
"dcsCommon", -- no cfxZones. a rarity!
|
||||||
|
"cfxMX",
|
||||||
|
}
|
||||||
|
--[[--
|
||||||
|
Version History
|
||||||
|
2.0.0 - cfxMX homologization
|
||||||
|
- startup checks
|
||||||
|
- minimal verbosity
|
||||||
|
- useDebris switch, currently set off
|
||||||
|
- clean-up
|
||||||
|
--]]--
|
||||||
|
|
||||||
cfxSSBSingleUse.playerUnits = {}
|
cfxSSBSingleUse.playerUnits = {}
|
||||||
cfxSSBSingleUse.slotGroundActions = {
|
cfxSSBSingleUse.slotGroundActions = {
|
||||||
@ -29,7 +26,6 @@ cfxSSBSingleUse.slotGroundActions = {
|
|||||||
"From Ground Area",
|
"From Ground Area",
|
||||||
"From Ground Area Hot",
|
"From Ground Area Hot",
|
||||||
}
|
}
|
||||||
|
|
||||||
cfxSSBSingleUse.groundSlots = {} -- players that start on the ground
|
cfxSSBSingleUse.groundSlots = {} -- players that start on the ground
|
||||||
|
|
||||||
function cfxSSBSingleUse:onEvent(event)
|
function cfxSSBSingleUse:onEvent(event)
|
||||||
@ -40,7 +36,6 @@ function cfxSSBSingleUse:onEvent(event)
|
|||||||
-- if we get here, initiator is set
|
-- if we get here, initiator is set
|
||||||
local theUnit = event.initiator -- we know this exists
|
local theUnit = event.initiator -- we know this exists
|
||||||
|
|
||||||
|
|
||||||
-- write down player names and planes
|
-- write down player names and planes
|
||||||
if event.id == 15 then
|
if event.id == 15 then
|
||||||
local uName = theUnit:getName()
|
local uName = theUnit:getName()
|
||||||
@ -69,7 +64,9 @@ function cfxSSBSingleUse:onEvent(event)
|
|||||||
|
|
||||||
if not thePilot then
|
if not thePilot then
|
||||||
-- ignore. not a player plane
|
-- ignore. not a player plane
|
||||||
|
if cfxSSBSingleUse.verbose then
|
||||||
trigger.action.outText("+++singleUse: ignored crash for NPC unit <" .. uName .. ">", 30)
|
trigger.action.outText("+++singleUse: ignored crash for NPC unit <" .. uName .. ">", 30)
|
||||||
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -80,19 +77,24 @@ function cfxSSBSingleUse:onEvent(event)
|
|||||||
local theGroundSlot = cfxSSBSingleUse.groundSlots[gName]
|
local theGroundSlot = cfxSSBSingleUse.groundSlots[gName]
|
||||||
if theGroundSlot then
|
if theGroundSlot then
|
||||||
local unitType = theUnit:getTypeName()
|
local unitType = theUnit:getTypeName()
|
||||||
|
if cfxSSBSingleUse.verbose then
|
||||||
trigger.action.outText("+++singleUse: <" .. uName .. "> starts on Ground. Will place debris for " .. unitType .. " NOW!!!", 30)
|
trigger.action.outText("+++singleUse: <" .. uName .. "> starts on Ground. Will place debris for " .. unitType .. " NOW!!!", 30)
|
||||||
|
end
|
||||||
cfxSSBSingleUse.placeDebris(unitType, theGroundSlot)
|
cfxSSBSingleUse.placeDebris(unitType, theGroundSlot)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- block this slot.
|
-- block this slot.
|
||||||
trigger.action.setUserFlag(gName, cfxSSBSingleUse.disabledFlagValue)
|
trigger.action.setUserFlag(gName, cfxSSBSingleUse.disabledFlagValue)
|
||||||
|
if cfxSSBSingleUse.verbose then
|
||||||
trigger.action.outText("+++singleUse: blocked <" .. gName .. "> after " .. thePilot .. " crashed it.", 30)
|
trigger.action.outText("+++singleUse: blocked <" .. gName .. "> after " .. thePilot .. " crashed it.", 30)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function cfxSSBSingleUse.placeDebris(unitType, theGroundSlot)
|
function cfxSSBSingleUse.placeDebris(unitType, theGroundSlot)
|
||||||
if not unitType then return end
|
if not unitType then return end
|
||||||
|
if not cfxSSBSingleUse.useDebris then return end
|
||||||
|
|
||||||
-- access location one, we assume single-unit groups
|
-- access location one, we assume single-unit groups
|
||||||
-- or at least that the player sits in unit one
|
-- or at least that the player sits in unit one
|
||||||
local playerData = theGroundSlot.playerUnits
|
local playerData = theGroundSlot.playerUnits
|
||||||
@ -106,12 +108,14 @@ function cfxSSBSingleUse.placeDebris(unitType, theGroundSlot)
|
|||||||
wreckData.type = unitType
|
wreckData.type = unitType
|
||||||
|
|
||||||
coalition.addStaticObject(theGroundSlot.coaNum, wreckData )
|
coalition.addStaticObject(theGroundSlot.coaNum, wreckData )
|
||||||
|
if cfxSSBSingleUse.verbose then
|
||||||
trigger.action.outText("+++singleUse: wreck <" .. unitType .. "> at " .. wreckData.x .. ", " .. wreckData.y .. " for " .. wreckData.name, 30)
|
trigger.action.outText("+++singleUse: wreck <" .. unitType .. "> at " .. wreckData.x .. ", " .. wreckData.y .. " for " .. wreckData.name, 30)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function cfxSSBSingleUse.populateAirfieldSlots()
|
function cfxSSBSingleUse.populateAirfieldSlots()
|
||||||
local pGroups = cfxGroups.getPlayerGroup()
|
local pGroups = cfxMX.getPlayerGroup()
|
||||||
local groundStarters = {}
|
local groundStarters = {}
|
||||||
for idx, theGroup in pairs(pGroups) do
|
for idx, theGroup in pairs(pGroups) do
|
||||||
-- we always use the first player's plane as referenced
|
-- we always use the first player's plane as referenced
|
||||||
@ -122,33 +126,35 @@ function cfxSSBSingleUse.populateAirfieldSlots()
|
|||||||
if dcsCommon.arrayContainsString(cfxSSBSingleUse.slotGroundActions, action ) then
|
if dcsCommon.arrayContainsString(cfxSSBSingleUse.slotGroundActions, action ) then
|
||||||
-- ground starter, not from runway
|
-- ground starter, not from runway
|
||||||
groundStarters[theGroup.name] = theGroup
|
groundStarters[theGroup.name] = theGroup
|
||||||
|
if cfxSSBSingleUse.verbose then
|
||||||
trigger.action.outText("+++singleUse: <" .. theGroup.name .. "> is ground starter", 30)
|
trigger.action.outText("+++singleUse: <" .. theGroup.name .. "> is ground starter", 30)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
cfxSSBSingleUse.groundSlots = groundStarters
|
cfxSSBSingleUse.groundSlots = groundStarters
|
||||||
end
|
end
|
||||||
|
|
||||||
function cfxSSBSingleUse.start()
|
function cfxSSBSingleUse.start()
|
||||||
|
-- check libs
|
||||||
|
if not dcsCommon.libCheck("cfx SSB Single Use",
|
||||||
|
cfxSSBSingleUse.requiredLibs) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
-- install event monitor
|
-- install event monitor
|
||||||
world.addEventHandler(cfxSSBSingleUse)
|
world.addEventHandler(cfxSSBSingleUse)
|
||||||
|
|
||||||
-- get all groups and process them to find
|
|
||||||
-- all planes that are on the ground for
|
|
||||||
-- eye candy
|
|
||||||
cfxSSBSingleUse.populateAirfieldSlots()
|
cfxSSBSingleUse.populateAirfieldSlots()
|
||||||
|
|
||||||
-- turn on ssb
|
-- turn on ssb
|
||||||
trigger.action.setUserFlag("SSB",100)
|
trigger.action.setUserFlag("SSB",100)
|
||||||
|
|
||||||
trigger.action.outText("SSB Single use v" .. cfxSSBSingleUse.version .. " running", 30)
|
trigger.action.outText("SSB Single use v" .. cfxSSBSingleUse.version .. " running", 30)
|
||||||
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- let's go!
|
-- let's go!
|
||||||
cfxSSBSingleUse.start()
|
if not cfxSSBSingleUse.start()then
|
||||||
|
trigger.action.outText("SSB Single Use failed to start up.", 30)
|
||||||
--[[--
|
cfxSSBSingleUse = nil
|
||||||
Additional features (later):
|
end
|
||||||
- place a wreck in slot when blocking for eye candy
|
|
||||||
- record player when they enter a unit and only block player planes
|
|
||||||
|
|
||||||
--]]--
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
tdz = {}
|
tdz = {}
|
||||||
tdz.version = "1.0.1"
|
tdz.version = "1.0.2"
|
||||||
tdz.requiredLibs = {
|
tdz.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
"cfxZones", -- Zones, of course
|
"cfxZones", -- Zones, of course
|
||||||
@ -15,6 +15,8 @@ VERSION HISTORY
|
|||||||
multiple zone support
|
multiple zone support
|
||||||
hops detection improvement
|
hops detection improvement
|
||||||
helo attribute
|
helo attribute
|
||||||
|
1.0.2 - manual placement option
|
||||||
|
filters FARPs
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
@ -80,15 +82,31 @@ end
|
|||||||
--
|
--
|
||||||
function tdz.createTDZ(theZone)
|
function tdz.createTDZ(theZone)
|
||||||
local p = theZone:getPoint()
|
local p = theZone:getPoint()
|
||||||
local theBase = dcsCommon.getClosestAirbaseTo(p) -- never get FARPS
|
local theBase = dcsCommon.getClosestAirbaseTo(p, 0) -- never get FARPS
|
||||||
theZone.base = theBase
|
theZone.base = theBase
|
||||||
theZone.baseName = theBase:getName()
|
theZone.baseName = theBase:getName()
|
||||||
theZone.helos = false
|
theZone.helos = false
|
||||||
|
|
||||||
|
local nearestRwy = nil
|
||||||
|
-- see if this is a manually placed runway
|
||||||
|
if theZone:getBoolFromZoneProperty("manual", true) then
|
||||||
|
-- construct runway from trigger zone attributes
|
||||||
|
if theZone.verbose or tdz.verbose then
|
||||||
|
trigger.action.outText("+++TDZ: runway for <" .. theZone.name .. "> is manually placed", 30)
|
||||||
|
end
|
||||||
|
nearestRwy = {}
|
||||||
|
nearestRwy.length = theZone:getNumberFromZoneProperty("length", 2500)
|
||||||
|
nearestRwy.width = theZone:getNumberFromZoneProperty("width", 60)
|
||||||
|
local hdgRaw = theZone:getNumberFromZoneProperty("course", 0) -- in degrees
|
||||||
|
hdgRaw = hdgRaw * 0.0174533 -- rads
|
||||||
|
nearestRwy.course = hdgRaw * (-1) -- why? because DCS.
|
||||||
|
nearestRwy.position = theZone:getPoint(true)
|
||||||
|
theZone.baseName = theZone.name .. "(manual placement)"
|
||||||
|
else
|
||||||
-- get closest runway to TDZ
|
-- get closest runway to TDZ
|
||||||
-- may get a bit hairy, so let's find a good way
|
-- may get a bit hairy, so let's find a good way
|
||||||
local allRwys = theBase:getRunways()
|
local allRwys = theBase:getRunways()
|
||||||
local nearestRwy = nil
|
|
||||||
local minDist = math.huge
|
local minDist = math.huge
|
||||||
for idx, aRwy in pairs(allRwys) do
|
for idx, aRwy in pairs(allRwys) do
|
||||||
local rp = aRwy.position
|
local rp = aRwy.position
|
||||||
@ -98,6 +116,7 @@ function tdz.createTDZ(theZone)
|
|||||||
minDist = dist
|
minDist = dist
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
local bearing = nearestRwy.course * (-1)
|
local bearing = nearestRwy.course * (-1)
|
||||||
if bearing < 0 then bearing = bearing + math.pi * 2 end
|
if bearing < 0 then bearing = bearing + math.pi * 2 end
|
||||||
theZone.bearing = bearing
|
theZone.bearing = bearing
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
airfield = {}
|
airfield = {}
|
||||||
airfield.version = "1.1.2"
|
airfield.version = "2.0.0"
|
||||||
airfield.requiredLibs = {
|
airfield.requiredLibs = {
|
||||||
"dcsCommon",
|
"dcsCommon",
|
||||||
"cfxZones",
|
"cfxZones",
|
||||||
}
|
}
|
||||||
airfield.verbose = false
|
|
||||||
airfield.myAirfields = {} -- indexed by name
|
airfield.myAirfields = {} -- indexed by name
|
||||||
airfield.farps = false
|
|
||||||
airfield.gracePeriod = 3
|
airfield.gracePeriod = 3
|
||||||
|
airfield.allAirfields = {} -- inexed by name
|
||||||
|
|
||||||
--[[--
|
--[[--
|
||||||
This module generates signals when the nearest airfield changes hands,
|
This module generates signals when the nearest airfield changes hands,
|
||||||
can force the coalition of an airfield, and always provides the
|
can force the coalition of an airfield, and always provides the
|
||||||
@ -24,17 +24,47 @@ airfield.gracePeriod = 3
|
|||||||
1.1.2 - 'show' attribute
|
1.1.2 - 'show' attribute
|
||||||
line color attributes per zone
|
line color attributes per zone
|
||||||
line color defaults in config
|
line color defaults in config
|
||||||
|
2.0.0 - show all airfields option
|
||||||
|
- fully reworked show options
|
||||||
|
- unmanaged airfields are automatically updated
|
||||||
|
- full color support
|
||||||
|
-- support for FARPS as well
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
|
-- init all airfields DB
|
||||||
|
function airfield.collectAll()
|
||||||
|
local allBases = world.getAirbases() -- get all
|
||||||
|
local count = 0
|
||||||
|
local dropped = 0
|
||||||
|
for idx, aBase in pairs(allBases) do
|
||||||
|
local entry = {}
|
||||||
|
local cat = Airbase.getCategory(aBase) -- DCS 2.9 hardened
|
||||||
|
-- cats: 0 = airfield, 1 = farp, 2 = ship
|
||||||
|
if (cat == 0) or (cat == 1) then
|
||||||
|
local name = aBase:getName()
|
||||||
|
entry.base = aBase
|
||||||
|
entry.cat = cat
|
||||||
|
-- entry.linkedTo holds zone if linked to. that how we know
|
||||||
|
airfield.allAirfields[name] = entry
|
||||||
|
count = count + 1
|
||||||
|
else
|
||||||
|
dropped = dropped + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if airfield.verbose then
|
||||||
|
trigger.action.outText("+++airF: init - count = <" .. count .. ">, dropped = <" .. dropped .. ">", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
-- setting up airfield
|
-- setting up airfield
|
||||||
--
|
--
|
||||||
function airfield.createAirFieldFromZone(theZone)
|
function airfield.createAirFieldFromZone(theZone)
|
||||||
--trigger.action.outText("Enter airfield for <" .. theZone.name .. ">", 30)
|
|
||||||
theZone.farps = theZone:getBoolFromZoneProperty("farps", false)
|
theZone.farps = theZone:getBoolFromZoneProperty("farps", false)
|
||||||
|
|
||||||
local filterCat = 0
|
local filterCat = 0
|
||||||
if (airfield.farps or theZone.farps) then filterCat = {0, 1} end -- bases and farps
|
if (theZone.farps) then filterCat = {0, 1} end -- bases and farps
|
||||||
local p = theZone:getPoint()
|
local p = theZone:getPoint()
|
||||||
local theBase = dcsCommon.getClosestAirbaseTo(p, filterCat)
|
local theBase = dcsCommon.getClosestAirbaseTo(p, filterCat)
|
||||||
theZone.airfield = theBase
|
theZone.airfield = theBase
|
||||||
@ -114,10 +144,30 @@ function airfield.createAirFieldFromZone(theZone)
|
|||||||
|
|
||||||
airfield.showAirfield(theZone)
|
airfield.showAirfield(theZone)
|
||||||
|
|
||||||
--trigger.action.outText("Exit airfield for <" .. theZone.name .. ">", 30)
|
-- now mark this zone as handled
|
||||||
|
local entry = airfield.allAirfields[theZone.afName]
|
||||||
|
entry.linkedTo = theZone -- only remember last, but that's enough.
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function airfield.markAirfieldOnMap(theAirfield, lineColor, fillColor)
|
||||||
|
local markID = dcsCommon.numberUUID()
|
||||||
|
local radius = 2000 -- meters
|
||||||
|
local p = theAirfield:getPoint()
|
||||||
|
-- if there are runways, we center on first runway
|
||||||
|
local rws = theAirfield:getRunways()
|
||||||
|
if rws then -- all airfields and farps have runways defined, but that array isnt filled for FARPS
|
||||||
|
local rw1 = rws[1]
|
||||||
|
if rw1 then
|
||||||
|
p.x = rw1.position.x
|
||||||
|
p.z = rw1.position.z
|
||||||
|
end
|
||||||
|
end
|
||||||
|
p.y = 0
|
||||||
|
trigger.action.circleToAll(-1, markID, p, radius, lineColor, fillColor, 1, true, "")
|
||||||
|
return markID
|
||||||
|
end
|
||||||
|
|
||||||
function airfield.showAirfield(theZone)
|
function airfield.showAirfield(theZone)
|
||||||
if not theZone then return end
|
if not theZone then return end
|
||||||
if theZone.ownerMark then
|
if theZone.ownerMark then
|
||||||
@ -138,34 +188,8 @@ function airfield.showAirfield(theZone)
|
|||||||
fillColor = theZone.neutralFill -- {0.8, 0.8, 0.8, 0.2}
|
fillColor = theZone.neutralFill -- {0.8, 0.8, 0.8, 0.2}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- always center on airfield, always 2km radius
|
theZone.ownerMark = airfield.markAirfieldOnMap(theZone.airfield, lineColor, fillColor)
|
||||||
local markID = dcsCommon.numberUUID()
|
|
||||||
local radius = 2000 -- meters
|
|
||||||
local p = theZone.airfield:getPoint()
|
|
||||||
-- if there are runways, we center on first runway
|
|
||||||
local rws = theZone.airfield:getRunways()
|
|
||||||
if rws then -- all airfields and farps have runways, but that array isnt filled for FARPS
|
|
||||||
local rw1 = rws[1]
|
|
||||||
if rw1 then
|
|
||||||
p.x = rw1.position.x
|
|
||||||
p.z = rw1.position.z
|
|
||||||
if airfield.verbose or theZone.verbose then
|
|
||||||
trigger.action.outText("+++airF: zone <" .. theZone.name .. "> assoc airfield <" .. theZone.afName .. "> has rw1, x=" .. p.x .. ", z=" .. p.z, 30)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if airfield.verbose or theZone.verbose then
|
|
||||||
trigger.action.outText("+++airF: zone <" .. theZone.name .. "> assoc airfield <" .. theZone.afName .. "> has no rw1", 30)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if airfield.verbose or theZone.verbose then
|
|
||||||
trigger.action.outText("+++airF: zone <" .. theZone.name .. "> assoc airfield <" .. theZone.afName .. "> has no runways", 30)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
p.y = 0
|
|
||||||
|
|
||||||
trigger.action.circleToAll(-1, markID, p, radius, lineColor, fillColor, 1, true, "")
|
|
||||||
theZone.ownerMark = markID
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -188,12 +212,40 @@ end
|
|||||||
--
|
--
|
||||||
-- event handling
|
-- event handling
|
||||||
--
|
--
|
||||||
|
function airfield.untendedCapture(theName, theBase)
|
||||||
|
if airfield.showAll and airfield.allAirfields[theName] then
|
||||||
|
-- we draw and handle all airfields, even those
|
||||||
|
-- without an attached handler zone
|
||||||
|
local theEntry = airfield.allAirfields[theName]
|
||||||
|
if not theEntry.linkedTo then -- merely safety
|
||||||
|
if theEntry.ownerMark then
|
||||||
|
-- remove previous mark
|
||||||
|
trigger.action.removeMark(theEntry.ownerMark)
|
||||||
|
theEntry.ownerMark = nil
|
||||||
|
end
|
||||||
|
local owner = theBase:getCoalition()
|
||||||
|
local lineColor = airfield.redLine
|
||||||
|
local fillColor = airfield.redFill
|
||||||
|
if owner == 2 then
|
||||||
|
lineColor = airfield.blueLine
|
||||||
|
fillColor = airfield.blueFill
|
||||||
|
elseif owner == 0 or owner == 3 then
|
||||||
|
lineColor = airfield.neutralLine
|
||||||
|
fillColor = airfield.neutralFill
|
||||||
|
end
|
||||||
|
theEntry.ownerMark = airfield.markAirfieldOnMap(theBase, lineColor, fillColor)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function airfield.airfieldCaptured(theBase)
|
function airfield.airfieldCaptured(theBase)
|
||||||
-- retrieve the zone that controls this airfield
|
-- retrieve the zone that controls this airfield
|
||||||
local bName = theBase:getName()
|
local bName = theBase:getName()
|
||||||
local theZone = airfield.myAirfields[bName]
|
local theZone = airfield.myAirfields[bName]
|
||||||
if not theZone then return end -- not handled
|
if not theZone then
|
||||||
|
airfield.untendedCapture(bName, theBase)
|
||||||
|
return
|
||||||
|
end -- not attached to a zone
|
||||||
local newCoa = theBase:getCoalition()
|
local newCoa = theBase:getCoalition()
|
||||||
theZone.owner = newCoa
|
theZone.owner = newCoa
|
||||||
|
|
||||||
@ -227,15 +279,8 @@ function airfield:onEvent(event)
|
|||||||
-- get category
|
-- get category
|
||||||
local desc = theBase:getDesc()
|
local desc = theBase:getDesc()
|
||||||
local bName = theBase:getName()
|
local bName = theBase:getName()
|
||||||
local cat = desc.category -- never get cat directly!
|
local cat = desc.category -- never get cat directly! DCS 2.0 safe
|
||||||
--[[-- if cat == 1 then
|
|
||||||
if not airfield.farps then
|
|
||||||
if airfield.verbose then
|
|
||||||
trigger.action.outText("+++airF: ignored cap event for FARP <" .. bName .. ">", 30)
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end --]]
|
|
||||||
if airfield.verbose then
|
if airfield.verbose then
|
||||||
trigger.action.outText("+++airF: cap event for <" .. bName .. ">, cat = (" .. cat .. ")", 30)
|
trigger.action.outText("+++airF: cap event for <" .. bName .. ">, cat = (" .. cat .. ")", 30)
|
||||||
end
|
end
|
||||||
@ -391,7 +436,7 @@ function airfield.readConfig()
|
|||||||
theZone = cfxZones.createSimpleZone("airfieldConfig")
|
theZone = cfxZones.createSimpleZone("airfieldConfig")
|
||||||
end
|
end
|
||||||
airfield.verbose = theZone.verbose
|
airfield.verbose = theZone.verbose
|
||||||
airfield.farps = theZone:getBoolFromZoneProperty("farps", false)
|
-- airfield.farps = theZone:getBoolFromZoneProperty("farps", false)
|
||||||
|
|
||||||
-- colors for line and fill
|
-- colors for line and fill
|
||||||
airfield.redLine = theZone:getRGBAVectorFromZoneProperty("redLine", {1.0, 0, 0, 1.0})
|
airfield.redLine = theZone:getRGBAVectorFromZoneProperty("redLine", {1.0, 0, 0, 1.0})
|
||||||
@ -400,12 +445,25 @@ function airfield.readConfig()
|
|||||||
airfield.blueFill = theZone:getRGBAVectorFromZoneProperty("blueFill", {0.0, 0, 1.0, 0.2})
|
airfield.blueFill = theZone:getRGBAVectorFromZoneProperty("blueFill", {0.0, 0, 1.0, 0.2})
|
||||||
airfield.neutralLine = theZone:getRGBAVectorFromZoneProperty("neutralLine", {0.8, 0.8, 0.8, 1.0})
|
airfield.neutralLine = theZone:getRGBAVectorFromZoneProperty("neutralLine", {0.8, 0.8, 0.8, 1.0})
|
||||||
airfield.neutralFill = theZone:getRGBAVectorFromZoneProperty("neutralFill", {0.8, 0.8, 0.8, 0.2})
|
airfield.neutralFill = theZone:getRGBAVectorFromZoneProperty("neutralFill", {0.8, 0.8, 0.8, 0.2})
|
||||||
|
|
||||||
|
airfield.showAll = theZone:getBoolFromZoneProperty("show", false)
|
||||||
|
end
|
||||||
|
|
||||||
|
function airfield.showUnlinked()
|
||||||
|
for name, entry in pairs(airfield.allAirfields) do
|
||||||
|
if not entry.linkedTo then
|
||||||
|
airfield.untendedCapture(name, entry.base)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function airfield.start()
|
function airfield.start()
|
||||||
if not dcsCommon.libCheck("cfx airfield", airfield.requiredLibs)
|
if not dcsCommon.libCheck("cfx airfield", airfield.requiredLibs)
|
||||||
then return false end
|
then return false end
|
||||||
|
|
||||||
|
-- set up DB
|
||||||
|
airfield.collectAll()
|
||||||
|
|
||||||
-- read config
|
-- read config
|
||||||
airfield.readConfig()
|
airfield.readConfig()
|
||||||
|
|
||||||
@ -415,6 +473,9 @@ function airfield.start()
|
|||||||
airfield.createAirFieldFromZone(aZone)
|
airfield.createAirFieldFromZone(aZone)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- show all unlinked
|
||||||
|
if airfield.showAll then airfield.showUnlinked() end
|
||||||
|
|
||||||
-- connect event handler
|
-- connect event handler
|
||||||
world.addEventHandler(airfield)
|
world.addEventHandler(airfield)
|
||||||
|
|
||||||
@ -442,7 +503,3 @@ if not airfield.start() then
|
|||||||
trigger.action.outText("+++ aborted airfield v" .. airfield.version .. " -- startup failed", 30)
|
trigger.action.outText("+++ aborted airfield v" .. airfield.version .. " -- startup failed", 30)
|
||||||
airfield = nil
|
airfield = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[--
|
|
||||||
ideas: what if we made this airfield movable?
|
|
||||||
--]]--
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
cfxArtilleryUI = {}
|
cfxArtilleryUI = {}
|
||||||
cfxArtilleryUI.version = "1.1.0"
|
cfxArtilleryUI.version = "2.0.0"
|
||||||
cfxArtilleryUI.requiredLibs = {
|
cfxArtilleryUI.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
"cfxPlayer", -- get all players
|
"cfxPlayer", -- get all players
|
||||||
@ -8,7 +8,7 @@ cfxArtilleryUI.requiredLibs = {
|
|||||||
}
|
}
|
||||||
--
|
--
|
||||||
-- UI for ArtilleryZones module, implements LOS, Observer, SMOKE
|
-- UI for ArtilleryZones module, implements LOS, Observer, SMOKE
|
||||||
-- Copyright (c) 2021, 2022 by Christian Franz and cf/x AG
|
-- Copyright (c) 2021 - 2024 by Christian Franz and cf/x AG
|
||||||
|
|
||||||
--[[-- VERSION HISTORY
|
--[[-- VERSION HISTORY
|
||||||
- 1.0.0 - based on jtacGrpUI
|
- 1.0.0 - based on jtacGrpUI
|
||||||
@ -21,6 +21,8 @@ cfxArtilleryUI.requiredLibs = {
|
|||||||
- collect zones recognizes moving zones, updates landHeight
|
- collect zones recognizes moving zones, updates landHeight
|
||||||
- allSeeing god mode attribute: always observing.
|
- allSeeing god mode attribute: always observing.
|
||||||
- allRanging god mode attribute: always in range.
|
- allRanging god mode attribute: always in range.
|
||||||
|
- 2.0.0 - dmlZones, OOP
|
||||||
|
cleanup
|
||||||
|
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
@ -272,21 +274,18 @@ function cfxArtilleryUI.populateTargetMenu(conf)
|
|||||||
-- now compare old control string with new, and only
|
-- now compare old control string with new, and only
|
||||||
-- re-populate if the old is different
|
-- re-populate if the old is different
|
||||||
if tgtCheckSum == conf.tgtCheckSum then
|
if tgtCheckSum == conf.tgtCheckSum then
|
||||||
-- trigger.action.outText("*** yeah old targets", 30)
|
|
||||||
return
|
return
|
||||||
elseif not conf.tgtCheckSum then
|
elseif not conf.tgtCheckSum then
|
||||||
-- trigger.action.outText("+++ new target menu", 30)
|
|
||||||
else
|
else
|
||||||
trigger.action.outTextForGroup(conf.id, "Artillery target updates", 30)
|
trigger.action.outTextForGroup(conf.id, "Artillery target updates", 30)
|
||||||
trigger.action.outSoundForGroup(conf.id, cfxArtilleryUI.updateSound)
|
trigger.action.outSoundForGroup(conf.id, cfxArtilleryUI.updateSound)
|
||||||
-- trigger.action.outText("!!! target update ", 30)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- we need to re-populate. erase old values
|
-- we need to re-populate. erase old values
|
||||||
cfxArtilleryUI.clearCommsTargets(conf)
|
cfxArtilleryUI.clearCommsTargets(conf)
|
||||||
conf.tgtCheckSum = tgtCheckSum -- remember for last time
|
conf.tgtCheckSum = tgtCheckSum -- remember for last time
|
||||||
--trigger.action.outText("new targets", 30)
|
|
||||||
|
|
||||||
if #filteredTargets < 1 then
|
if #filteredTargets < 1 then
|
||||||
-- simply put one-line dummy in there
|
-- simply put one-line dummy in there
|
||||||
local commandTxt = "(No unobscured target areas)"
|
local commandTxt = "(No unobscured target areas)"
|
||||||
@ -307,7 +306,6 @@ function cfxArtilleryUI.populateTargetMenu(conf)
|
|||||||
for i=1, numTargets do
|
for i=1, numTargets do
|
||||||
-- make a target command for each
|
-- make a target command for each
|
||||||
local aTarget = filteredTargets[i]
|
local aTarget = filteredTargets[i]
|
||||||
|
|
||||||
commandTxt = "Fire at: <" .. aTarget.name .. ">"
|
commandTxt = "Fire at: <" .. aTarget.name .. ">"
|
||||||
theCommand = missionCommands.addCommandForGroup(
|
theCommand = missionCommands.addCommandForGroup(
|
||||||
conf.id,
|
conf.id,
|
||||||
@ -444,7 +442,6 @@ function cfxArtilleryUI.doCommandListTargets(args)
|
|||||||
local conf = args[1] -- < conf in here
|
local conf = args[1] -- < conf in here
|
||||||
local what = args[2] -- < second argument in here
|
local what = args[2] -- < second argument in here
|
||||||
local theGroup = conf.theGroup
|
local theGroup = conf.theGroup
|
||||||
-- trigger.action.outTextForGroup(conf.id, "+++ groupUI: processing comms menu for <" .. what .. ">", 30)
|
|
||||||
local targetList = cfxArtilleryUI.collectArtyTargets(conf)
|
local targetList = cfxArtilleryUI.collectArtyTargets(conf)
|
||||||
-- iterate the list
|
-- iterate the list
|
||||||
if #targetList < 1 then
|
if #targetList < 1 then
|
||||||
@ -453,7 +450,6 @@ function cfxArtilleryUI.doCommandListTargets(args)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local desc = "Artillery Targets:\n"
|
local desc = "Artillery Targets:\n"
|
||||||
-- trigger.action.outTextForGroup(conf.id, "Target Report:", 30)
|
|
||||||
for i=1, #targetList do
|
for i=1, #targetList do
|
||||||
local aTarget = targetList[i]
|
local aTarget = targetList[i]
|
||||||
local inRange = cfxArtilleryUI.allRanging or aTarget.range * 1000 < aTarget.spotRange
|
local inRange = cfxArtilleryUI.allRanging or aTarget.range * 1000 < aTarget.spotRange
|
||||||
@ -475,7 +471,6 @@ end
|
|||||||
function cfxArtilleryUI.collectArtyTargets(conf)
|
function cfxArtilleryUI.collectArtyTargets(conf)
|
||||||
-- iterate all target zones, for those that are on my side
|
-- iterate all target zones, for those that are on my side
|
||||||
-- calculate range, bearing, and then order by distance
|
-- calculate range, bearing, and then order by distance
|
||||||
|
|
||||||
local theTargets = {}
|
local theTargets = {}
|
||||||
for idx, aZone in pairs(cfxArtilleryZones.artilleryZones) do
|
for idx, aZone in pairs(cfxArtilleryZones.artilleryZones) do
|
||||||
if aZone.coalition == conf.coalition then
|
if aZone.coalition == conf.coalition then
|
||||||
@ -498,7 +493,7 @@ function cfxArtilleryUI.collectArtyTargets(conf)
|
|||||||
--aTarget.targetName = aZone.name
|
--aTarget.targetName = aZone.name
|
||||||
aTarget.spotRange = aZone.spotRange
|
aTarget.spotRange = aZone.spotRange
|
||||||
-- get the target we are lazing
|
-- get the target we are lazing
|
||||||
local zP = cfxZones.getPoint(aZone) -- zone can move!
|
local zP = aZone:getPoint() -- zone can move!
|
||||||
aZone.landHeight = land.getHeight({x = zP.x, y= zP.z})
|
aZone.landHeight = land.getHeight({x = zP.x, y= zP.z})
|
||||||
local there = {x = zP.x, y = aZone.landHeight + 1, z=zP.z}
|
local there = {x = zP.x, y = aZone.landHeight + 1, z=zP.z}
|
||||||
aTarget.there = there
|
aTarget.there = there
|
||||||
@ -654,18 +649,17 @@ function cfxArtilleryUI.readConfigZone()
|
|||||||
-- note: must match exactly!!!!
|
-- note: must match exactly!!!!
|
||||||
local theZone = cfxZones.getZoneByName("ArtilleryUIConfig")
|
local theZone = cfxZones.getZoneByName("ArtilleryUIConfig")
|
||||||
if not theZone then
|
if not theZone then
|
||||||
trigger.action.outText("+++A-UI: no config zone!", 30)
|
--trigger.action.outText("+++A-UI: no config zone!", 30)
|
||||||
return
|
--return
|
||||||
|
theZone = cfxZones.createSimpleZone("ArtilleryUIConfig")
|
||||||
end
|
end
|
||||||
|
|
||||||
trigger.action.outText("+++A-UI: found config zone!", 30)
|
cfxArtilleryUI.verbose = theZone.verbose
|
||||||
|
cfxArtilleryUI.allowPlanes = theZone:getBoolFromZoneProperty("allowPlanes", false)
|
||||||
cfxArtilleryUI.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
cfxArtilleryUI.smokeColor = theZone:getSmokeColorStringFromZoneProperty("smokeColor", "red")
|
||||||
cfxArtilleryUI.allowPlanes = cfxZones.getBoolFromZoneProperty(theZone, "allowPlanes", false)
|
cfxArtilleryUI.allSeeing = theZone:getBoolFromZoneProperty("allSeeing", false)
|
||||||
cfxArtilleryUI.smokeColor = cfxZones.getSmokeColorStringFromZoneProperty(theZone, "smokeColor", "red")
|
cfxArtilleryUI.allRanging = theZone:getBoolFromZoneProperty("allRanging", false)
|
||||||
cfxArtilleryUI.allSeeing = cfxZones.getBoolFromZoneProperty(theZone, "allSeeing", false)
|
cfxArtilleryUI.allTiming = theZone:getBoolFromZoneProperty("allTiming", false)
|
||||||
cfxArtilleryUI.allRanging = cfxZones.getBoolFromZoneProperty(theZone, "allRanging", false)
|
|
||||||
cfxArtilleryUI.allTiming = cfxZones.getBoolFromZoneProperty(theZone, "allTiming", false)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
@ -681,7 +675,6 @@ function cfxArtilleryUI.start()
|
|||||||
cfxArtilleryUI.readConfigZone()
|
cfxArtilleryUI.readConfigZone()
|
||||||
|
|
||||||
-- iterate existing groups so we have a start situation
|
-- iterate existing groups so we have a start situation
|
||||||
-- now iterate through all player groups and install the Assault Troop Menu
|
|
||||||
local allPlayerGroups = cfxPlayerGroups -- cfxPlayerGroups is a global, don't fuck with it!
|
local allPlayerGroups = cfxPlayerGroups -- cfxPlayerGroups is a global, don't fuck with it!
|
||||||
-- contains per group player record. Does not resolve on unit level!
|
-- contains per group player record. Does not resolve on unit level!
|
||||||
for gname, pgroup in pairs(allPlayerGroups) do
|
for gname, pgroup in pairs(allPlayerGroups) do
|
||||||
@ -710,5 +703,5 @@ end
|
|||||||
|
|
||||||
--[[--
|
--[[--
|
||||||
TODO: transition times based on distance - requires real bound arty first
|
TODO: transition times based on distance - requires real bound arty first
|
||||||
DONE: ui for smoking target zone: list ten closest zones, and provide menu to smoke zone
|
TODO: remove dependency on cfxPlayer
|
||||||
--]]--
|
--]]--
|
||||||
@ -1,5 +1,5 @@
|
|||||||
cfxArtilleryZones = {}
|
cfxArtilleryZones = {}
|
||||||
cfxArtilleryZones.version = "2.2.2"
|
cfxArtilleryZones.version = "3.0.0"
|
||||||
cfxArtilleryZones.requiredLibs = {
|
cfxArtilleryZones.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
"cfxZones", -- Zones, of course
|
"cfxZones", -- Zones, of course
|
||||||
@ -31,28 +31,12 @@ cfxArtilleryZones.verbose = false
|
|||||||
2.2.0 - DML Watchflag integration
|
2.2.0 - DML Watchflag integration
|
||||||
2.2.1 - minor code clean-up
|
2.2.1 - minor code clean-up
|
||||||
2.2.2 - new doParametricFireAt()
|
2.2.2 - new doParametricFireAt()
|
||||||
|
3.0.0 - dmlZones, OOP
|
||||||
|
- cleanup
|
||||||
|
|
||||||
Artillery Target Zones *** EXTENDS ZONES ***
|
Artillery Target Zones *** EXTENDS ZONES ***
|
||||||
Target Zones for artillery. Can determine which zones are in range and visible and then handle artillery barrage to this zone
|
Target Zones for artillery. Can determine which zones are in range and visible and then handle artillery barrage to this zone
|
||||||
Copyright (c) 2021, 2022 by Christian Franz and cf/x AG
|
Copyright (c) 2021 - 2024 by Christian Franz and cf/x AG
|
||||||
|
|
||||||
USAGE
|
|
||||||
Via ME: Add the relevant attributes to the zone
|
|
||||||
Via Script: Use createArtilleryTarget()
|
|
||||||
|
|
||||||
|
|
||||||
Callbacks
|
|
||||||
when fire at target is invoked, a callback can be
|
|
||||||
invoked so your code knows that fire control has been
|
|
||||||
given a command or that projectiles are impacting.
|
|
||||||
Signature
|
|
||||||
callback(rason, zone, data), with
|
|
||||||
reason: 'firing' - fire command given for zone
|
|
||||||
'impact' a projectile has hit
|
|
||||||
zone: artilleryZone
|
|
||||||
data: empty on 'fire'
|
|
||||||
.point where impact point
|
|
||||||
.strength power of explosion
|
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
cfxArtilleryZones.artilleryZones = {}
|
cfxArtilleryZones.artilleryZones = {}
|
||||||
@ -117,42 +101,39 @@ function cfxArtilleryZones.createArtilleryTarget(name, point, coalition, spotRan
|
|||||||
end
|
end
|
||||||
|
|
||||||
function cfxArtilleryZones.processArtilleryZone(aZone)
|
function cfxArtilleryZones.processArtilleryZone(aZone)
|
||||||
aZone.artilleryTarget = cfxZones.getStringFromZoneProperty(aZone, "artilleryTarget", aZone.name)
|
aZone.artilleryTarget = aZone:getStringFromZoneProperty( "artilleryTarget", aZone.name)
|
||||||
aZone.coalition = cfxZones.getCoalitionFromZoneProperty(aZone, "coalition", 0) -- side that marks it on map, and who fires arty
|
aZone.coalition = aZone:getCoalitionFromZoneProperty("coalition", 0) -- side that marks it on map, and who fires arty
|
||||||
aZone.spotRange = cfxZones.getNumberFromZoneProperty(aZone, "spotRange", 3000) -- FO max range to direct fire
|
aZone.spotRange = aZone:getNumberFromZoneProperty("spotRange", 3000) -- FO max range to direct fire
|
||||||
aZone.shellStrength = cfxZones.getNumberFromZoneProperty(aZone, "shellStrength", 500) -- power of shells (strength)
|
aZone.shellStrength = aZone:getNumberFromZoneProperty("shellStrength", 500) -- power of shells (strength)
|
||||||
|
|
||||||
aZone.shellNum = cfxZones.getNumberFromZoneProperty(aZone, "shellNum", 17) -- number of shells in bombardment
|
aZone.shellNum = aZone:getNumberFromZoneProperty("shellNum", 17) -- number of shells in bombardment
|
||||||
aZone.transitionTime = cfxZones.getNumberFromZoneProperty(aZone, "transitionTime", 20) -- average time of travel for projectiles
|
aZone.transitionTime = aZone:getNumberFromZoneProperty( "transitionTime", 20) -- average time of travel for projectiles
|
||||||
aZone.addMark = cfxZones.getBoolFromZoneProperty(aZone, "addMark", true) -- note: defaults to true
|
aZone.addMark = aZone:getBoolFromZoneProperty("addMark", true) -- note: defaults to true
|
||||||
aZone.shellVariance = cfxZones.getNumberFromZoneProperty(aZone, "shellVariance", 0.2) -- strength of explosion can vary by +/- this amount
|
aZone.shellVariance = aZone:getNumberFromZoneProperty( "shellVariance", 0.2) -- strength of explosion can vary by +/- this amount
|
||||||
|
|
||||||
-- watchflag:
|
-- watchflag:
|
||||||
-- triggerMethod
|
-- triggerMethod
|
||||||
aZone.artyTriggerMethod = cfxZones.getStringFromZoneProperty(aZone, "artyTriggerMethod", "change")
|
aZone.artyTriggerMethod = aZone:getStringFromZoneProperty( "artyTriggerMethod", "change")
|
||||||
|
|
||||||
if cfxZones.hasProperty(aZone, "triggerMethod") then
|
if aZone:hasProperty("triggerMethod") then
|
||||||
aZone.artyTriggerMethod = cfxZones.getStringFromZoneProperty(aZone, "triggerMethod", "change")
|
aZone.artyTriggerMethod = aZone:getStringFromZoneProperty("triggerMethod", "change")
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(aZone, "f?") then
|
if aZone:hasProperty("f?") then
|
||||||
aZone.artyTriggerFlag = cfxZones.getStringFromZoneProperty(aZone, "f?", "none")
|
aZone.artyTriggerFlag = cfxZones.getStringFromZoneProperty("f?", "none")
|
||||||
end
|
elseif aZone:hasProperty("artillery?") then
|
||||||
|
aZone.artyTriggerFlag = aZone:getStringFromZoneProperty("artillery?", "none")
|
||||||
if cfxZones.hasProperty(aZone, "artillery?") then
|
elseif aZone:hasProperty("in?") then
|
||||||
aZone.artyTriggerFlag = cfxZones.getStringFromZoneProperty(aZone, "artillery?", "none")
|
aZone.artyTriggerFlag = aZone:getStringFromZoneProperty("in?", "none")
|
||||||
end
|
|
||||||
if cfxZones.hasProperty(aZone, "in?") then
|
|
||||||
aZone.artyTriggerFlag = cfxZones.getStringFromZoneProperty(aZone, "in?", "none")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if aZone.artyTriggerFlag then
|
if aZone.artyTriggerFlag then
|
||||||
aZone.lastTriggerValue = trigger.misc.getUserFlag(aZone.artyTriggerFlag) -- save last value
|
aZone.lastTriggerValue = trigger.misc.getUserFlag(aZone.artyTriggerFlag) -- save last value
|
||||||
end
|
end
|
||||||
aZone.cooldown =cfxZones.getNumberFromZoneProperty(aZone, "cooldown", 120) -- seconds
|
aZone.cooldown = aZone:getNumberFromZoneProperty("cooldown", 120) -- seconds
|
||||||
aZone.baseAccuracy = cfxZones.getNumberFromZoneProperty(aZone, "baseAccuracy", aZone.radius) -- meters from center radius shell impact
|
aZone.baseAccuracy = aZone:getNumberFromZoneProperty( "baseAccuracy", aZone.radius) -- meters from center radius shell impact
|
||||||
-- use zone radius as mase accuracy for simple placement
|
-- use zone radius as mase accuracy for simple placement
|
||||||
aZone.silent = cfxZones.getBoolFromZoneProperty(aZone, "silent", false)
|
aZone.silent = aZone:getBoolFromZoneProperty("silent", false)
|
||||||
end
|
end
|
||||||
|
|
||||||
function cfxArtilleryZones.addArtilleryZone(aZone)
|
function cfxArtilleryZones.addArtilleryZone(aZone)
|
||||||
@ -200,7 +181,7 @@ function cfxArtilleryZones.artilleryZonesInRangeOfUnit(theUnit)
|
|||||||
-- is it one of mine?
|
-- is it one of mine?
|
||||||
if aZone.coalition == myCoalition then
|
if aZone.coalition == myCoalition then
|
||||||
-- is it close enough?
|
-- is it close enough?
|
||||||
local zP = cfxZones.getPoint(aZone)
|
local zP = aZone:getPoint()
|
||||||
aZone.landHeight = land.getHeight({x = zP.x, y= zP.z})
|
aZone.landHeight = land.getHeight({x = zP.x, y= zP.z})
|
||||||
local zonePoint = {x = zP.x, y = aZone.landHeight, z = zP.z}
|
local zonePoint = {x = zP.x, y = aZone.landHeight, z = zP.z}
|
||||||
local d = dcsCommon.dist(p,zonePoint)
|
local d = dcsCommon.dist(p,zonePoint)
|
||||||
@ -249,7 +230,7 @@ end
|
|||||||
--
|
--
|
||||||
|
|
||||||
--
|
--
|
||||||
-- BOOM command
|
-- BOOM method
|
||||||
--
|
--
|
||||||
function cfxArtilleryZones.doBoom(args)
|
function cfxArtilleryZones.doBoom(args)
|
||||||
trigger.action.explosion(args.point, args.strength)
|
trigger.action.explosion(args.point, args.strength)
|
||||||
@ -356,7 +337,6 @@ function cfxArtilleryZones.simFireAtZone(aZone, aGroup, dist)
|
|||||||
|
|
||||||
trigger.action.outTextForCoalition(aGroup:getCoalition(), "Artillery firing on ".. aZone.name .. addInfo, 30)
|
trigger.action.outTextForCoalition(aGroup:getCoalition(), "Artillery firing on ".. aZone.name .. addInfo, 30)
|
||||||
end
|
end
|
||||||
--trigger.action.smoke(center, 2) -- mark location visually
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function cfxArtilleryZones.simSmokeZone(aZone, aGroup, aColor)
|
function cfxArtilleryZones.simSmokeZone(aZone, aGroup, aColor)
|
||||||
@ -367,7 +347,7 @@ function cfxArtilleryZones.simSmokeZone(aZone, aGroup, aColor)
|
|||||||
if type(aColor) == "string" then
|
if type(aColor) == "string" then
|
||||||
aColor = dcsCommon.smokeColor2Num(aColor)
|
aColor = dcsCommon.smokeColor2Num(aColor)
|
||||||
end
|
end
|
||||||
local zP = cfxZones.getPoint(aZone)
|
local zP = aZone:getPoint(aZone)
|
||||||
aZone.landHeight = land.getHeight({x = zP.x, y= zP.z})
|
aZone.landHeight = land.getHeight({x = zP.x, y= zP.z})
|
||||||
|
|
||||||
local transitionTime = aZone.transitionTime --17 -- seconds until phosphor lands
|
local transitionTime = aZone.transitionTime --17 -- seconds until phosphor lands
|
||||||
@ -404,7 +384,7 @@ function cfxArtilleryZones.update()
|
|||||||
|
|
||||||
-- iterate all zones to see if a trigger has changed
|
-- iterate all zones to see if a trigger has changed
|
||||||
for idx, aZone in pairs(cfxArtilleryZones.artilleryZones) do
|
for idx, aZone in pairs(cfxArtilleryZones.artilleryZones) do
|
||||||
if cfxZones.testZoneFlag(aZone, aZone.artyTriggerFlag, aZone.artyTriggerMethod, "lastTriggerValue") then
|
if aZone:testZoneFlag(aZone.artyTriggerFlag, aZone.artyTriggerMethod, "lastTriggerValue") then
|
||||||
-- a triggered release!
|
-- a triggered release!
|
||||||
cfxArtilleryZones.doFireAt(aZone) -- all from zone vars!
|
cfxArtilleryZones.doFireAt(aZone) -- all from zone vars!
|
||||||
if cfxArtilleryZones.verbose then
|
if cfxArtilleryZones.verbose then
|
||||||
@ -413,10 +393,10 @@ function cfxArtilleryZones.update()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- old code
|
-- old code
|
||||||
|
--[[--
|
||||||
if aZone.artyTriggerFlag then
|
if aZone.artyTriggerFlag then
|
||||||
local currTriggerVal = cfxZones.getFlagValue(aZone.artyTriggerFlag, aZone) -- trigger.misc.getUserFlag(aZone.artyTriggerFlag)
|
local currTriggerVal = cfxZones.getFlagValue(aZone.artyTriggerFlag, aZone)
|
||||||
if currTriggerVal ~= aZone.lastTriggerValue
|
if currTriggerVal ~= aZone.lastTriggerValue
|
||||||
then
|
then
|
||||||
-- a triggered release!
|
-- a triggered release!
|
||||||
@ -430,6 +410,7 @@ function cfxArtilleryZones.update()
|
|||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
--]]--
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -446,8 +427,6 @@ function cfxArtilleryZones.start()
|
|||||||
-- collect all spawn zones
|
-- collect all spawn zones
|
||||||
local attrZones = cfxZones.getZonesWithAttributeNamed("artilleryTarget")
|
local attrZones = cfxZones.getZonesWithAttributeNamed("artilleryTarget")
|
||||||
|
|
||||||
-- now create a spawner for all, add them to the spawner updater, and spawn for all zones that are not
|
|
||||||
-- paused
|
|
||||||
for k, aZone in pairs(attrZones) do
|
for k, aZone in pairs(attrZones) do
|
||||||
cfxArtilleryZones.processArtilleryZone(aZone) -- process attribute and add to zone
|
cfxArtilleryZones.processArtilleryZone(aZone) -- process attribute and add to zone
|
||||||
cfxArtilleryZones.addArtilleryZone(aZone) -- remember it so we can smoke it
|
cfxArtilleryZones.addArtilleryZone(aZone) -- remember it so we can smoke it
|
||||||
@ -1,25 +1,17 @@
|
|||||||
cfxCargoReceiver = {}
|
cfxCargoReceiver = {}
|
||||||
cfxCargoReceiver.version = "1.2.2"
|
cfxCargoReceiver.version = "2.0.0"
|
||||||
cfxCargoReceiver.ups = 1 -- once a second
|
cfxCargoReceiver.ups = 1 -- once a second
|
||||||
cfxCargoReceiver.maxDirectionRange = 500 -- in m. distance when cargo manager starts talking to pilots who are carrying that cargo
|
cfxCargoReceiver.maxDirectionRange = 500 -- in m. distance when cargo manager starts talking to pilots who are carrying that cargo
|
||||||
cfxCargoReceiver.requiredLibs = {
|
cfxCargoReceiver.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
"cfxPlayer", -- for directions
|
|
||||||
"cfxZones", -- Zones, of course
|
"cfxZones", -- Zones, of course
|
||||||
"cfxCargoManager", -- will notify me on a cargo event
|
"cfxCargoManager", -- will notify me on a cargo event
|
||||||
}
|
}
|
||||||
--[[--
|
--[[--
|
||||||
Version history
|
Version history
|
||||||
- 1.0.0 initial vbersion
|
- 2.0.0 no more cfxPlayer Dependency
|
||||||
- 1.1.0 added flag manipulation options
|
dmlZones, OOP
|
||||||
no negative agl on announcement
|
clean-up
|
||||||
silent attribute
|
|
||||||
- 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
|
CargoReceiver is a zone enhancement you use to be automatically
|
||||||
@ -28,13 +20,6 @@ cfxCargoReceiver.requiredLibs = {
|
|||||||
|
|
||||||
*** EXTENDS ZONES
|
*** EXTENDS ZONES
|
||||||
|
|
||||||
Callback signature:
|
|
||||||
cb(event, obj, name, zone) with
|
|
||||||
- event being string, currently defined: 'deliver'
|
|
||||||
- obj being the cargo object
|
|
||||||
- name being cargo object name
|
|
||||||
- zone in which cargo was dropped (if dropped)
|
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
cfxCargoReceiver.receiverZones = {}
|
cfxCargoReceiver.receiverZones = {}
|
||||||
function cfxCargoReceiver.processReceiverZone(aZone) -- process attribute and add to zone
|
function cfxCargoReceiver.processReceiverZone(aZone) -- process attribute and add to zone
|
||||||
@ -42,50 +27,22 @@ function cfxCargoReceiver.processReceiverZone(aZone) -- process attribute and ad
|
|||||||
-- isCargoReceiver flag and we are good
|
-- isCargoReceiver flag and we are good
|
||||||
aZone.isCargoReceiver = true
|
aZone.isCargoReceiver = true
|
||||||
-- we can add additional processing here
|
-- we can add additional processing here
|
||||||
aZone.autoRemove = cfxZones.getBoolFromZoneProperty(aZone, "autoRemove", false) -- maybe add a removeDelay
|
aZone.autoRemove = aZone:getBoolFromZoneProperty("autoRemove", false) -- maybe add a removeDelay
|
||||||
aZone.removeDelay = cfxZones.getNumberFromZoneProperty(aZone, "removeDelay", 1)
|
aZone.removeDelay = aZone:getNumberFromZoneProperty("removeDelay", 1)
|
||||||
if aZone.removeDelay < 1 then aZone.removeDelay = 1 end
|
if aZone.removeDelay < 1 then aZone.removeDelay = 1 end
|
||||||
aZone.silent = cfxZones.getBoolFromZoneProperty(aZone, "silent", false)
|
aZone.silent = aZone:getBoolFromZoneProperty("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
|
|
||||||
if cfxZones.hasProperty(aZone, "f=1") then
|
|
||||||
aZone.setFlag = cfxZones.getStringFromZoneProperty(aZone, "f=1", "999")
|
|
||||||
end
|
|
||||||
if cfxZones.hasProperty(aZone, "clearFlag") then
|
|
||||||
aZone.clearFlag = cfxZones.getStringFromZoneProperty(aZone, "clearFlag", "999")
|
|
||||||
end
|
|
||||||
if cfxZones.hasProperty(aZone, "f=0") then
|
|
||||||
aZone.clearFlag = cfxZones.getStringFromZoneProperty(aZone, "f=0", "999")
|
|
||||||
end
|
|
||||||
if cfxZones.hasProperty(aZone, "increaseFlag") then
|
|
||||||
aZone.increaseFlag = cfxZones.getStringFromZoneProperty(aZone, "increaseFlag", "999")
|
|
||||||
end
|
|
||||||
if cfxZones.hasProperty(aZone, "f+1") then
|
|
||||||
aZone.increaseFlag = cfxZones.getStringFromZoneProperty(aZone, "f+1", "999")
|
|
||||||
end
|
|
||||||
if cfxZones.hasProperty(aZone, "decreaseFlag") then
|
|
||||||
aZone.decreaseFlag = cfxZones.getStringFromZoneProperty(aZone, "decreaseFlag", "999")
|
|
||||||
end
|
|
||||||
if cfxZones.hasProperty(aZone, "f-1") then
|
|
||||||
aZone.decreaseFlag = cfxZones.getStringFromZoneProperty(aZone, "f-1", "999")
|
|
||||||
end
|
|
||||||
--]]--
|
|
||||||
-- new method support
|
-- new method support
|
||||||
aZone.cargoMethod = cfxZones.getStringFromZoneProperty(aZone, "method", "inc")
|
aZone.cargoMethod = aZone:getStringFromZoneProperty("method", "inc")
|
||||||
if cfxZones.hasProperty(aZone, "cargoMethod") then
|
if aZone:hasProperty("cargoMethod") then
|
||||||
aZone.cargoMethod = cfxZones.getStringFromZoneProperty(aZone, "cargoMethod", "inc")
|
aZone.cargoMethod = aZone:getStringFromZoneProperty("cargoMethod", "inc")
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(aZone, "f!") then
|
if aZone:hasProperty("f!") then
|
||||||
aZone.outReceiveFlag = cfxZones.getStringFromZoneProperty(aZone, "f!", "*<none>")
|
aZone.outReceiveFlag = aZone:getStringFromZoneProperty("f!", "*<none>")
|
||||||
elseif cfxZones.hasProperty(aZone, "cargoReceived!") then
|
elseif aZone:hasProperty("cargoReceived!") then
|
||||||
aZone.outReceiveFlag = cfxZones.getStringFromZoneProperty(aZone, "cargoReceived!", "*<none>")
|
aZone.outReceiveFlag = aZone:getStringFromZoneProperty( "cargoReceived!", "*<none>")
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@ -134,17 +91,14 @@ end
|
|||||||
|
|
||||||
function cfxCargoReceiver.cargoEvent(event, object, name)
|
function cfxCargoReceiver.cargoEvent(event, object, name)
|
||||||
-- usually called from cargomanager
|
-- usually called from cargomanager
|
||||||
--trigger.action.outText("Cargo Receiver: event <" .. event .. "> for " .. name, 30)
|
|
||||||
if not event then return end
|
if not event then return end
|
||||||
if event == "grounded" then
|
if event == "grounded" then
|
||||||
--trigger.action.outText("+++rcv: grounded for " .. name, 30)
|
|
||||||
-- this is actually the only one that interests us
|
-- this is actually the only one that interests us
|
||||||
if not object then
|
if not object then
|
||||||
--trigger.action.outText("+++rcv: " .. name .. " has null object", 30)
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if not object:isExist() then
|
if not Object.isExist(object) then
|
||||||
--trigger.action.outText("+++rcv: " .. name .. " no longer exists", 30)
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
loc = object:getPoint()
|
loc = object:getPoint()
|
||||||
@ -156,28 +110,10 @@ function cfxCargoReceiver.cargoEvent(event, object, name)
|
|||||||
cfxCargoReceiver.invokeCallback("deliver", object, name, aZone)
|
cfxCargoReceiver.invokeCallback("deliver", object, name, aZone)
|
||||||
|
|
||||||
-- set flags as indicated
|
-- set flags as indicated
|
||||||
--[[--
|
|
||||||
if aZone.setFlag then
|
|
||||||
trigger.action.setUserFlag(aZone.setFlag, 1)
|
|
||||||
end
|
|
||||||
if aZone.clearFlag then
|
|
||||||
trigger.action.setUserFlag(aZone.clearFlag, 0)
|
|
||||||
end
|
|
||||||
if aZone.increaseFlag then
|
|
||||||
local val = trigger.misc.getUserFlag(aZone.increaseFlag) + 1
|
|
||||||
trigger.action.setUserFlag(aZone.increaseFlag, val)
|
|
||||||
end
|
|
||||||
if aZone.decreaseFlag then
|
|
||||||
local val = trigger.misc.getUserFlag(aZone.decreaseFlag) - 1
|
|
||||||
trigger.action.setUserFlag(aZone.decreaseFlag, val)
|
|
||||||
end
|
|
||||||
--]]--
|
|
||||||
if aZone.outReceiveFlag then
|
if aZone.outReceiveFlag then
|
||||||
cfxZones.pollFlag(aZone.outReceiveFlag, aZone.cargoMethod, aZone)
|
cfxZones.pollFlag(aZone.outReceiveFlag, aZone.cargoMethod, aZone)
|
||||||
end
|
end
|
||||||
|
|
||||||
--trigger.action.outText("+++rcv: " .. name .. " delivered in zone " .. aZone.name, 30)
|
|
||||||
--trigger.action.outSound("Quest Snare 3.wav")
|
|
||||||
if aZone.autoRemove then
|
if aZone.autoRemove then
|
||||||
-- schedule this for in a few seconds?
|
-- schedule this for in a few seconds?
|
||||||
local args = {}
|
local args = {}
|
||||||
@ -218,12 +154,12 @@ function cfxCargoReceiver.update()
|
|||||||
-- this cargo can be talked down.
|
-- this cargo can be talked down.
|
||||||
-- find the player unit that is closest to in in hopes
|
-- find the player unit that is closest to in in hopes
|
||||||
-- that that is the one carrying it
|
-- that that is the one carrying it
|
||||||
local allPlayers = cfxPlayer.getAllPlayers() -- idx by name
|
local allPlayers = dcsCommon.getAllExistingPlayersAndUnits() -- idx by name
|
||||||
for pname, info in pairs(allPlayers) do
|
for pname, theUnit in pairs(allPlayers) do
|
||||||
-- iterate all player units
|
-- iterate all player units
|
||||||
local closestUnit = nil
|
local closestUnit = nil
|
||||||
local minDelta = math.huge
|
local minDelta = math.huge
|
||||||
local theUnit = info.unit
|
--local theUnit = info.unit
|
||||||
if theUnit:isExist() then
|
if theUnit:isExist() then
|
||||||
local uPoint = theUnit:getPoint()
|
local uPoint = theUnit:getPoint()
|
||||||
local currDelta = dcsCommon.distFlat(thePoint, uPoint)
|
local currDelta = dcsCommon.distFlat(thePoint, uPoint)
|
||||||
@ -305,4 +241,4 @@ if not cfxCargoReceiver.start() then
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- TODO: config zone for talking down pilots
|
-- TODO: config zone for talking down pilots
|
||||||
-- TODO: f+/f-/f=1/f=0
|
-- detect all pilots in zone (not clear: are all detected or only one)
|
||||||
@ -1,411 +0,0 @@
|
|||||||
cfxArtilleryDemon = {}
|
|
||||||
cfxArtilleryDemon.version = "1.0.3"
|
|
||||||
-- based on cfx stage demon v 1.0.2
|
|
||||||
--[[--
|
|
||||||
Version History
|
|
||||||
1.0.2 - taken from stageDemon
|
|
||||||
1.0.3 - corrected 'messageOut' bug
|
|
||||||
|
|
||||||
--]]--
|
|
||||||
cfxArtilleryDemon.messageToAll = true -- set to false if messages should be sent only to the group that set the mark
|
|
||||||
cfxArtilleryDemon.messageTime = 30 -- how long a message stays on the sceeen
|
|
||||||
|
|
||||||
-- cfxArtillery hooks into DCS's mark system to intercept user
|
|
||||||
-- transactions with the mark system and uses that for arty targeting
|
|
||||||
-- used to interactively add ArtilleryZones during gameplay
|
|
||||||
|
|
||||||
-- Copyright (c) 2021 by Christian Franz and cf/x AG
|
|
||||||
|
|
||||||
cfxArtilleryDemon.autostart = true -- start automatically
|
|
||||||
|
|
||||||
-- whenever you begin a Mark with the string below, it will be taken as a command
|
|
||||||
-- and run through the command parser, stripping the mark, and then splitting
|
|
||||||
-- by blanks
|
|
||||||
cfxArtilleryDemon.markOfDemon = "-" -- all commands must start with this sequence
|
|
||||||
cfxArtilleryDemon.splitDelimiter = " "
|
|
||||||
|
|
||||||
cfxArtilleryDemon.unitFilterMethod = nil -- optional user filtering redirection. currently
|
|
||||||
-- set to allow all users use cfxArtillery
|
|
||||||
cfxArtilleryDemon.processCommandMethod = nil -- optional initial command processing redirection
|
|
||||||
-- currently set to cfxArtillery's own processor
|
|
||||||
cfxArtilleryDemon.commandTable = {} -- key, value pair for command processing per keyword
|
|
||||||
-- all commands cfxArtillery understands are used as keys and
|
|
||||||
-- the functions that process them are used as values
|
|
||||||
-- making the parser a trivial table :)
|
|
||||||
|
|
||||||
cfxArtilleryDemon.demonID = nil -- used only for suspending the event callback
|
|
||||||
|
|
||||||
-- unit authorization. You return false to disallow this unit access
|
|
||||||
-- to commands
|
|
||||||
-- simple authorization checks would be to allow only players
|
|
||||||
-- on neutral side, or players in range of location with Lino of sight
|
|
||||||
-- to that point
|
|
||||||
--
|
|
||||||
function cfxArtilleryDemon.authorizeAllUnits(event)
|
|
||||||
-- units/groups that are allowed to give a command can be filtered.
|
|
||||||
-- return true if the unit/group may give commands
|
|
||||||
-- cfxArtillery allows anyone to give it commands
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function cfxArtilleryDemon.hasMark(theString)
|
|
||||||
-- check if the string begins with the sequece to identify commands
|
|
||||||
if not theString then return false end
|
|
||||||
return theString:find(cfxArtilleryDemon.markOfDemon) == 1
|
|
||||||
end
|
|
||||||
|
|
||||||
function cfxArtilleryDemon.splitString(inputstr, sep)
|
|
||||||
if sep == nil then
|
|
||||||
sep = "%s"
|
|
||||||
end
|
|
||||||
if inputstr == nil then
|
|
||||||
inputstr = ""
|
|
||||||
end
|
|
||||||
|
|
||||||
local t={}
|
|
||||||
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
|
|
||||||
table.insert(t, str)
|
|
||||||
end
|
|
||||||
return t
|
|
||||||
end
|
|
||||||
|
|
||||||
function cfxArtilleryDemon.str2num(inVal, default)
|
|
||||||
if not default then default = 0 end
|
|
||||||
if not inVal then return default end
|
|
||||||
if type(inVal) == "number" then return inVal end
|
|
||||||
local num = nil
|
|
||||||
if type(inVal) == "string" then num = tonumber(inVal) end
|
|
||||||
if not num then return default end
|
|
||||||
return num
|
|
||||||
end
|
|
||||||
|
|
||||||
--
|
|
||||||
-- output method. can be customized, so we have a central place where we
|
|
||||||
-- can control how output is handled. Is currently outText and outTextToGroup
|
|
||||||
--
|
|
||||||
function cfxArtilleryDemon.outMessage(theMessage, args)
|
|
||||||
if not args then
|
|
||||||
args = {}
|
|
||||||
end
|
|
||||||
local toAll = args.toAll -- will only be true if defined and set to true
|
|
||||||
|
|
||||||
if not args.group then
|
|
||||||
toAll = true
|
|
||||||
else
|
|
||||||
if not args.group:isExist() then
|
|
||||||
toAll = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
toAll = toAll or cfxArtilleryDemon.messageToAll
|
|
||||||
if not toAll then
|
|
||||||
trigger.action.outTextToGroup(args.group, theMessage, cfxArtilleryDemon.messageTime)
|
|
||||||
else
|
|
||||||
trigger.action.outText(theMessage, cfxArtilleryDemon.messageTime)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--
|
|
||||||
-- get all player groups - since there is no getGroupByIndex in DCS (yet)
|
|
||||||
-- we simply collect all player groups (since only palyers can place marks)
|
|
||||||
-- and try to match their group ID to the one given by mark
|
|
||||||
function cfxArtilleryDemon.getAllPayerGroups()
|
|
||||||
local coalitionSides = {0, 1, 2} -- we currently have neutral, red, blue
|
|
||||||
local playerGroups = {}
|
|
||||||
for i=1, #coalitionSides do
|
|
||||||
local theSide = coalitionSides[i]
|
|
||||||
-- get all players for this side
|
|
||||||
local thePlayers = coalition.getPlayers(theSide)
|
|
||||||
for p=1, #thePlayers do
|
|
||||||
aPlayerUnit = thePlayers[p] -- docs say this is a unit table, not a person!
|
|
||||||
if aPlayerUnit:isExist() then
|
|
||||||
local theGroup = aPlayerUnit:getGroup()
|
|
||||||
if theGroup:isExist() then
|
|
||||||
local gID = theGroup:getID()
|
|
||||||
playerGroups[gID] = theGroup -- multiple players per group results in one group
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return playerGroups
|
|
||||||
end
|
|
||||||
|
|
||||||
function cfxArtilleryDemon.retrieveGroupFromEvent(theEvent)
|
|
||||||
-- DEBUG CODE
|
|
||||||
if theEvent.initiator then
|
|
||||||
trigger.action.outText("EVENT: initiator set to " .. theEvent.initiator:getName(), 30)
|
|
||||||
else
|
|
||||||
trigger.action.outText("EVENT: NO INITIATOR", 30)
|
|
||||||
end
|
|
||||||
|
|
||||||
trigger.action.outText("EVENT: groupID = " .. theEvent.groupID, 30)
|
|
||||||
|
|
||||||
-- trivial case: initiator is set, and we can access the group
|
|
||||||
if theEvent.initiator then
|
|
||||||
if theEvent.initiator:isExist() then
|
|
||||||
return theEvent.initiator:getGroup()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- ok, bad news: initiator wasn't filled. let's try the fallback: event.groupID
|
|
||||||
if theEvent.groupID and theEvent.groupID > 0 then
|
|
||||||
local playerGroups = cfxArtilleryDemon.getAllPayerGroups()
|
|
||||||
if playerGroups[theEvent.groupID] then
|
|
||||||
return palyerGroups[theEvent.groupID]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- nope, return nil
|
|
||||||
return nil
|
|
||||||
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. Note that you can hook your own command
|
|
||||||
-- processor in by changing the value of processCommandMethod
|
|
||||||
function cfxArtilleryDemon:onEvent(theEvent)
|
|
||||||
-- 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
|
|
||||||
|
|
||||||
-- build the messageOut() arg table
|
|
||||||
local args = {}
|
|
||||||
args.toAll = cfxArtilleryDemon.toAll
|
|
||||||
--
|
|
||||||
--
|
|
||||||
args.toAll = false -- FORCE GROUPS FOR DEBUGGING OF NEW CODE
|
|
||||||
--
|
|
||||||
--
|
|
||||||
if not args.toAll then
|
|
||||||
-- we want group-targeted messaging
|
|
||||||
-- so we need to retrieve the group
|
|
||||||
local theGroup = cfxArtilleryDemon.retrieveGroupFromEvent(theEvent)
|
|
||||||
if not theGroup then
|
|
||||||
args.toAll = true
|
|
||||||
trigger.action.outText("*** WARNING: cfxArtilleryDemon can't find group for command", 30)
|
|
||||||
else
|
|
||||||
args.group = theGroup
|
|
||||||
end
|
|
||||||
end
|
|
||||||
cfxArtilleryDemon.args = args -- copy reference so we can easily use it in messageOut
|
|
||||||
|
|
||||||
-- when we get here, we have a mark event
|
|
||||||
-- see if the unit filter lets it pass
|
|
||||||
if not cfxArtilleryDemon.unitFilterMethod(theEvent) then
|
|
||||||
return -- unit is not allowed to give demon orders. bye bye
|
|
||||||
end
|
|
||||||
|
|
||||||
if theEvent.id == world.event.S_EVENT_MARK_ADDED then
|
|
||||||
-- add mark is quite useless for us as we are called when the user clicks, with no
|
|
||||||
-- text in the description yet. Later abilities may want to use it though
|
|
||||||
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 cfxArtilleryDemon.hasMark(theEvent.text) then
|
|
||||||
-- strip the mark
|
|
||||||
local commandString = theEvent.text:sub(1+cfxArtilleryDemon.markOfDemon:len())
|
|
||||||
-- break remainder apart into <command> <arg1> ... <argn>
|
|
||||||
local commands = cfxArtilleryDemon.splitString(commandString, cfxArtilleryDemon.splitDelimiter)
|
|
||||||
|
|
||||||
-- this is a command. process it and then remove it if it was executed successfully
|
|
||||||
local success = cfxArtilleryDemon.processCommandMethod(commands, theEvent)
|
|
||||||
|
|
||||||
-- remove this mark after successful execution
|
|
||||||
if success then
|
|
||||||
trigger.action.removeMark(theEvent.idx)
|
|
||||||
cfxArtilleryDemon.outMessage("executed command <" .. commandString .. "> from unit" .. theEvent.initiator:getName(), args)
|
|
||||||
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 cfxArtillerys vocabulary
|
|
||||||
--
|
|
||||||
function cfxArtilleryDemon.addCommndProcessor(command, processor)
|
|
||||||
cfxArtilleryDemon.commandTable[command:upper()] = processor
|
|
||||||
end
|
|
||||||
|
|
||||||
function cfxArtilleryDemon.removeCommandProcessor(command)
|
|
||||||
cfxArtilleryDemon.commandTable[command:upper()] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
--
|
|
||||||
-- process input arguments. Here we simply move them
|
|
||||||
-- up by one.
|
|
||||||
--
|
|
||||||
function cfxArtilleryDemon.getArgs(theCommands)
|
|
||||||
local args = {}
|
|
||||||
for i=2, #theCommands do
|
|
||||||
table.insert(args, theCommands[i])
|
|
||||||
end
|
|
||||||
return args
|
|
||||||
end
|
|
||||||
|
|
||||||
--
|
|
||||||
-- stage 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 cfxArtilleryDemon.executeCommand(theCommands, event)
|
|
||||||
-- trigger.action.outText("executor: *" .. theCommands[1] .. "*", 30)
|
|
||||||
-- see if theCommands[1] exists in the command table
|
|
||||||
local cmd = theCommands[1]
|
|
||||||
local arguments = cfxArtilleryDemon.getArgs(theCommands)
|
|
||||||
if not cmd then return false end
|
|
||||||
|
|
||||||
-- use the command as index into the table of functions
|
|
||||||
-- that handle them.
|
|
||||||
if cfxArtilleryDemon.commandTable[cmd:upper()] then
|
|
||||||
local theInvoker = cfxArtilleryDemon.commandTable[cmd:upper()]
|
|
||||||
local success = theInvoker(arguments, event)
|
|
||||||
return success
|
|
||||||
else
|
|
||||||
trigger.action.outText("***error: unknown command <".. cmd .. ">", 30)
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
--
|
|
||||||
-- SMOKE COMMAND
|
|
||||||
--
|
|
||||||
|
|
||||||
-- known commands and their processors
|
|
||||||
function cfxArtilleryDemon.smokeColor2Index (theColor)
|
|
||||||
local color = theColor:lower()
|
|
||||||
if color == "red" then return 1 end
|
|
||||||
if color == "white" then return 2 end
|
|
||||||
if color == "orange" then return 3 end
|
|
||||||
if color == "blue" then return 4 end
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
-- this is the command processing template for your own commands
|
|
||||||
-- when you add a command processor via addCommndProcessor()
|
|
||||||
-- smoke command syntax: '-smoke <color>' with optional color, color being red, green, blue, white or orange
|
|
||||||
function cfxArtilleryDemon.processSmokeCommand(args, event)
|
|
||||||
if not args[1] then args[1] = "red" end -- default to red color
|
|
||||||
local thePoint = event.pos
|
|
||||||
thePoint.y = land.getHeight({x = thePoint.x, y = thePoint.z}) +3 -- elevate to ground height
|
|
||||||
trigger.action.smoke(thePoint, cfxArtilleryDemon.smokeColor2Index(args[1]))
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
--
|
|
||||||
-- BOOM command
|
|
||||||
--
|
|
||||||
function cfxArtilleryDemon.doBoom(args)
|
|
||||||
--trigger.action.outText("sim shell str=" .. args.strength .. " x=" .. args.point.x .. " z = " .. args.point.z .. " Tdelta = " .. args.tDelta, 30)
|
|
||||||
-- trigger.action.smoke(args.point, 2)
|
|
||||||
trigger.action.explosion(args.point, args.strength)
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
function cfxArtilleryDemon.processBoomCommand(args, event)
|
|
||||||
if not args[1] then args[1] = "750" end -- default to 750 strength
|
|
||||||
local transitionTime = 20 -- seconds until shells hit
|
|
||||||
local shellNum = 17
|
|
||||||
local shellBaseStrength = 500
|
|
||||||
local shellvariance = 0.2 -- 10%
|
|
||||||
local center = event.pos -- center of where shells hit
|
|
||||||
center.y = land.getHeight({x = center.x, y = center.z}) + 3
|
|
||||||
-- we now can 'dirty' the position by something. not yet
|
|
||||||
for i=1, shellNum do
|
|
||||||
local thePoint = dcsCommon.randomPointInCircle(100, 0, center.x, center.z)
|
|
||||||
local boomArgs = {}
|
|
||||||
local strVar = shellBaseStrength * shellvariance
|
|
||||||
strVar = strVar * (2 * dcsCommon.randomPercent() - 1.0) -- go from -1 to 1
|
|
||||||
|
|
||||||
boomArgs.strength = shellBaseStrength + strVar
|
|
||||||
thePoint.y = land.getHeight({x = thePoint.x, y = thePoint.z}) + 1 -- elevate to ground height + 1
|
|
||||||
boomArgs.point = thePoint
|
|
||||||
local timeVar = 5 * (2 * dcsCommon.randomPercent() - 1.0) -- +/- 1.5 seconds
|
|
||||||
boomArgs.tDelta = timeVar
|
|
||||||
timer.scheduleFunction(cfxArtilleryDemon.doBoom, boomArgs, timer.getTime() + transitionTime + timeVar)
|
|
||||||
end
|
|
||||||
trigger.action.outText("Fire command confirmed. Artillery is firing at your designated co-ordinates.", 30)
|
|
||||||
trigger.action.smoke(center, 2) -- mark location visually
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
--
|
|
||||||
-- cfxArtilleryZones interface
|
|
||||||
--
|
|
||||||
|
|
||||||
function cfxArtilleryDemon.processTargetCommand(args, event)
|
|
||||||
-- get position
|
|
||||||
local center = event.pos -- center of where shells hit
|
|
||||||
center.y = land.getHeight({x = center.x, y = center.z})
|
|
||||||
|
|
||||||
if not event.initiator then
|
|
||||||
trigger.action.outText("Target entry aborted: no initiator.", 30)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
local theUnit = event.initiator
|
|
||||||
local theGroup = theUnit:getGroup()
|
|
||||||
local coalition = theGroup:getCoalition()
|
|
||||||
local spotRange = 3000
|
|
||||||
local autoAdd = true
|
|
||||||
local params = ""
|
|
||||||
|
|
||||||
for idx, param in pairs(args) do
|
|
||||||
if params == "" then params = ": "
|
|
||||||
else params = params .. " "
|
|
||||||
end
|
|
||||||
params = params .. param
|
|
||||||
end
|
|
||||||
|
|
||||||
local name = "TgtData".. params .. " (" .. theUnit:getName() .. ")@T+" .. math.floor(timer.getTime())
|
|
||||||
-- feed into arty zones
|
|
||||||
cfxArtilleryZones.createArtilleryZone(name, center, coalition, spotRange, 500, autoAdd) -- 500 is base strength
|
|
||||||
|
|
||||||
trigger.action.outTextForCoalition(coalition, "New ARTY coordinates received from " .. theUnit:getName() .. ", standing by", 30)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
--
|
|
||||||
-- cfxArtillery init and start
|
|
||||||
--
|
|
||||||
|
|
||||||
function cfxArtilleryDemon.init()
|
|
||||||
cfxArtilleryDemon.unitFilterMethod = cfxArtilleryDemon.authorizeAllUnits
|
|
||||||
cfxArtilleryDemon.processCommandMethod = cfxArtilleryDemon.executeCommand
|
|
||||||
|
|
||||||
-- now add known commands to interpreter. Add your own commands the same way
|
|
||||||
cfxArtilleryDemon.addCommndProcessor("smoke", cfxArtilleryDemon.processSmokeCommand)
|
|
||||||
|
|
||||||
cfxArtilleryDemon.addCommndProcessor("bumm", cfxArtilleryDemon.processBoomCommand)
|
|
||||||
|
|
||||||
cfxArtilleryDemon.addCommndProcessor("tgt",
|
|
||||||
cfxArtilleryDemon.processTargetCommand)
|
|
||||||
|
|
||||||
-- you can add and remove command the same way
|
|
||||||
trigger.action.outText("cf/x cfx Artillery Demon v" .. cfxArtilleryDemon.version .. " loaded", 30)
|
|
||||||
end
|
|
||||||
|
|
||||||
function cfxArtilleryDemon.start()
|
|
||||||
cfxArtilleryDemon.demonID = world.addEventHandler(cfxArtilleryDemon)
|
|
||||||
trigger.action.outText("cf/x cfxArtilleryDemon v" .. cfxArtilleryDemon.version .. " started", 30)
|
|
||||||
end
|
|
||||||
|
|
||||||
cfxArtilleryDemon.init()
|
|
||||||
if cfxArtilleryDemon.autostart then
|
|
||||||
cfxArtilleryDemon.start()
|
|
||||||
end
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
cfxMX = {}
|
cfxMX = {}
|
||||||
cfxMX.version = "1.2.6"
|
cfxMX.version = "2.0.0"
|
||||||
cfxMX.verbose = false
|
cfxMX.verbose = false
|
||||||
--[[--
|
--[[--
|
||||||
Mission data decoder. Access to ME-built mission structures
|
Mission data decoder. Access to ME-built mission structures
|
||||||
@ -7,26 +7,11 @@ cfxMX.verbose = false
|
|||||||
Copyright (c) 2022, 2023 by Christian Franz and cf/x AG
|
Copyright (c) 2022, 2023 by Christian Franz and cf/x AG
|
||||||
|
|
||||||
Version History
|
Version History
|
||||||
1.0.0 - initial version
|
|
||||||
1.0.1 - getStaticFromDCSbyName()
|
|
||||||
1.1.0 - getStaticFromDCSbyName also copies groupID when not fetching orig
|
|
||||||
- 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
|
|
||||||
1.2.1 - added countryByName
|
|
||||||
- added linkByName
|
|
||||||
1.2.2 - fixed ctry bug in countryByName
|
|
||||||
- playerGroupByName
|
|
||||||
- playerUnitByName
|
|
||||||
1.2.3 - groupTypeByName
|
|
||||||
- groupCoalitionByName
|
|
||||||
1.2.4 - playerUnit2Group cross index
|
|
||||||
1.2.5 - unitIDbyName index added
|
|
||||||
1.2.6 - cfxMX.allTrainsByName
|
1.2.6 - cfxMX.allTrainsByName
|
||||||
- train carve-outs for vehicles
|
- train carve-outs for vehicles
|
||||||
|
2.0.0 - clean-up
|
||||||
|
- harmonized with cfxGroups
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
cfxMX.groupNamesByID = {}
|
cfxMX.groupNamesByID = {}
|
||||||
cfxMX.groupIDbyName = {}
|
cfxMX.groupIDbyName = {}
|
||||||
@ -47,13 +32,21 @@ cfxMX.playerGroupByName = {} -- returns data only if a player is in group
|
|||||||
cfxMX.playerUnitByName = {} -- returns data only if this is a player unit
|
cfxMX.playerUnitByName = {} -- returns data only if this is a player unit
|
||||||
cfxMX.playerUnit2Group = {} -- returns a group data for player units.
|
cfxMX.playerUnit2Group = {} -- returns a group data for player units.
|
||||||
|
|
||||||
|
cfxMX.groups = {} -- all groups indexed b yname, cfxGroups folded into cfxMX
|
||||||
|
--[[-- group objects are
|
||||||
|
{
|
||||||
|
name= "",
|
||||||
|
coalition = "" (red, blue, neutral),
|
||||||
|
coanum = # (0, 1, 2 for neutral, red, blue)
|
||||||
|
category = "" (helicopter, ship, plane, vehicle, static),
|
||||||
|
hasPlayer = true/false,
|
||||||
|
playerUnits = {} (for each player unit in group: name, point, action)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
--]]--
|
||||||
function cfxMX.getGroupFromDCSbyName(aName, fetchOriginal)
|
function cfxMX.getGroupFromDCSbyName(aName, fetchOriginal)
|
||||||
if not fetchOriginal then fetchOriginal = false end
|
if not fetchOriginal then fetchOriginal = false end
|
||||||
-- fetch the group description for goup named aName (if exists)
|
|
||||||
-- returned structure must be parsed for useful information
|
|
||||||
-- returns data, category, countyID and coalitionID
|
|
||||||
-- unless fetchOriginal is true, creates a deep clone of
|
|
||||||
-- group data structure
|
|
||||||
|
|
||||||
for coa_name_miz, coa_data in pairs(env.mission.coalition) do -- iterate all coalitions
|
for coa_name_miz, coa_data in pairs(env.mission.coalition) do -- iterate all coalitions
|
||||||
local coa_name = coa_name_miz
|
local coa_name = coa_name_miz
|
||||||
@ -135,11 +128,6 @@ function cfxMX.getStaticFromDCSbyName(aName, fetchOriginal)
|
|||||||
if type(cntry_data) == 'table' then -- filter strings .id and .name
|
if type(cntry_data) == 'table' then -- filter strings .id and .name
|
||||||
for obj_type_name, obj_type_data in pairs(cntry_data) do
|
for obj_type_name, obj_type_data in pairs(cntry_data) do
|
||||||
if obj_type_name == "static"
|
if obj_type_name == "static"
|
||||||
-- obj_type_name == "helicopter" or
|
|
||||||
-- obj_type_name == "ship" or
|
|
||||||
-- obj_type_name == "plane" or
|
|
||||||
-- obj_type_name == "vehicle" or
|
|
||||||
-- obj_type_name == "static"
|
|
||||||
then -- (only look at statics)
|
then -- (only look at statics)
|
||||||
local category = obj_type_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 static in group!
|
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 static in group!
|
||||||
@ -149,7 +137,7 @@ function cfxMX.getStaticFromDCSbyName(aName, fetchOriginal)
|
|||||||
if group_data and group_data.route and group_data.route and group_data.route.points[1] then
|
if group_data and group_data.route and group_data.route and group_data.route.points[1] then
|
||||||
linkUnit = group_data.route.points[1].linkUnit
|
linkUnit = group_data.route.points[1].linkUnit
|
||||||
if linkUnit then
|
if linkUnit then
|
||||||
--trigger.action.outText("MX: found missing link to " .. linkUnit .. " in " .. group_data.name, 30)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -207,7 +195,7 @@ function cfxMX.createCrossReferences()
|
|||||||
obj_type_name == "plane" or
|
obj_type_name == "plane" or
|
||||||
obj_type_name == "vehicle" or
|
obj_type_name == "vehicle" or
|
||||||
obj_type_name == "static" -- what about "cargo"?
|
obj_type_name == "static" -- what about "cargo"?
|
||||||
-- not that trains appear as 'vehicle'
|
-- note that trains appear as 'vehicle'
|
||||||
then -- (so it's not id or name)
|
then -- (so it's not id or name)
|
||||||
local category = obj_type_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!
|
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!
|
||||||
@ -252,6 +240,12 @@ function cfxMX.createCrossReferences()
|
|||||||
end
|
end
|
||||||
-- now iterate all units in this group
|
-- now iterate all units in this group
|
||||||
-- for unit xref like player info and ID
|
-- for unit xref like player info and ID
|
||||||
|
local hasPlayer = false
|
||||||
|
local playerUnits = {}
|
||||||
|
local groupName = group_data.name
|
||||||
|
if env.mission.version > 7 then -- translate raw to actual
|
||||||
|
groupName = env.getValueDictByKey(groupName)
|
||||||
|
end
|
||||||
for unit_num, unit_data in pairs(group_data.units) do
|
for unit_num, unit_data in pairs(group_data.units) do
|
||||||
if unit_data.skill then
|
if unit_data.skill then
|
||||||
if unit_data.skill == "Client" or unit_data.skill == "Player" then
|
if unit_data.skill == "Client" or unit_data.skill == "Player" then
|
||||||
@ -259,10 +253,38 @@ function cfxMX.createCrossReferences()
|
|||||||
cfxMX.playerUnitByName[unit_data.name] = unit_data
|
cfxMX.playerUnitByName[unit_data.name] = unit_data
|
||||||
cfxMX.playerGroupByName[aName] = group_data -- inefficient, but works
|
cfxMX.playerGroupByName[aName] = group_data -- inefficient, but works
|
||||||
cfxMX.playerUnit2Group[unit_data.name] = group_data
|
cfxMX.playerUnit2Group[unit_data.name] = group_data
|
||||||
|
|
||||||
|
hasPlayer = true
|
||||||
|
local playerData = {}
|
||||||
|
playerData.name = unit_data.name
|
||||||
|
playerData.point = {}
|
||||||
|
playerData.point.x = unit_data.x
|
||||||
|
playerData.point.y = 0
|
||||||
|
playerData.point.z = unit_data.y
|
||||||
|
playerData.action = "none" -- default
|
||||||
|
|
||||||
|
-- access initial waypoint data by 'reaching up'
|
||||||
|
-- into group data and extract route.points[1]
|
||||||
|
if group_data.route and group_data.route.points and (#group_data.route.points > 0) then
|
||||||
|
playerData.action = group_data.route.points[1].action
|
||||||
|
end
|
||||||
|
table.insert(playerUnits, playerData)
|
||||||
|
|
||||||
end -- if unit skill client
|
end -- if unit skill client
|
||||||
end -- if has skill
|
end -- if has skill
|
||||||
cfxMX.unitIDbyName[unit_data.name] = unit_data.unitId
|
cfxMX.unitIDbyName[unit_data.name] = unit_data.unitId
|
||||||
end -- for all units
|
end -- for all units
|
||||||
|
|
||||||
|
local entry = {}
|
||||||
|
entry.name = groupName
|
||||||
|
entry.coalition = coa_name
|
||||||
|
entry.coaNum = coaNum
|
||||||
|
entry.category = category
|
||||||
|
entry.hasPlayer = hasPlayer
|
||||||
|
entry.playerUnits = playerUnits
|
||||||
|
-- add to db
|
||||||
|
cfxMX.groups[groupName] = entry
|
||||||
|
|
||||||
end -- for all groups
|
end -- for all groups
|
||||||
end --if has category data
|
end --if has category data
|
||||||
end --if plane, helo etc... category
|
end --if plane, helo etc... category
|
||||||
@ -274,6 +296,31 @@ function cfxMX.createCrossReferences()
|
|||||||
end --for all coalitions in mission
|
end --for all coalitions in mission
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- return all groups that can have players in them
|
||||||
|
-- includes groups that currently are not or not anymore alive
|
||||||
|
function cfxMX.getPlayerGroup()
|
||||||
|
local playerGroups = {}
|
||||||
|
for gName, gData in pairs (cfxMX.groups) do
|
||||||
|
if gData.hasPlayer then
|
||||||
|
table.insert(playerGroups, gData)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return playerGroups
|
||||||
|
end
|
||||||
|
|
||||||
|
-- return all group names that can have players in them
|
||||||
|
-- includes groups that currently are not or not anymore alive
|
||||||
|
function cfxMX.getPlayerGroupNames()
|
||||||
|
local playerGroups = {}
|
||||||
|
for gName, gData in pairs (cfxMX.groups) do
|
||||||
|
if gData.hasPlayer then
|
||||||
|
table.insert(playerGroups, gName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return playerGroups
|
||||||
|
end
|
||||||
|
|
||||||
function cfxMX.catText2ID(inText)
|
function cfxMX.catText2ID(inText)
|
||||||
local outCat = 0 -- airplane
|
local outCat = 0 -- airplane
|
||||||
local c = inText:lower()
|
local c = inText:lower()
|
||||||
@ -283,7 +330,7 @@ function cfxMX.catText2ID(inText)
|
|||||||
if c == "vehicle" then outCat = 2 end
|
if c == "vehicle" then outCat = 2 end
|
||||||
if c == "train" then outCat = 4 end
|
if c == "train" then outCat = 4 end
|
||||||
if c == "static" then outCat = -1 end
|
if c == "static" then outCat = -1 end
|
||||||
--trigger.action.outText("cat2text: in <" .. inText .. "> out <" .. outCat .. ">", 30)
|
|
||||||
return outCat
|
return outCat
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -292,6 +339,7 @@ function cfxMX.start()
|
|||||||
if cfxMX.verbose then
|
if cfxMX.verbose then
|
||||||
trigger.action.outText("cfxMX: "..#cfxMX.groupNamesByID .. " groups processed successfully", 30)
|
trigger.action.outText("cfxMX: "..#cfxMX.groupNamesByID .. " groups processed successfully", 30)
|
||||||
end
|
end
|
||||||
|
trigger.action.outText("cfxMX v." .. cfxMX.version .. " started.", 30)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- start
|
-- start
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
cfxZones = {}
|
cfxZones = {}
|
||||||
cfxZones.version = "4.0.10"
|
cfxZones.version = "4.1.1"
|
||||||
|
|
||||||
-- cf/x zone management module
|
-- cf/x zone management module
|
||||||
-- reads dcs zones and makes them accessible and mutable
|
-- reads dcs zones and makes them accessible and mutable
|
||||||
@ -9,31 +9,6 @@ cfxZones.version = "4.0.10"
|
|||||||
--
|
--
|
||||||
|
|
||||||
--[[-- VERSION HISTORY
|
--[[-- VERSION HISTORY
|
||||||
- 3.0.0 - support for DCS 2.8 linkUnit attribute, integration with
|
|
||||||
linedUnit and warning.
|
|
||||||
- initZoneVerbosity()
|
|
||||||
- 3.0.1 - updateMovingZones() better tracks linked units by name
|
|
||||||
- 3.0.2 - maxRadius for all zones, only differs from radius in polyZones
|
|
||||||
- re-factoring zone-base string processing from messenger module
|
|
||||||
- new processStringWildcards() that does almost all that messenger can
|
|
||||||
- 3.0.3 - new getLinkedUnit()
|
|
||||||
- 3.0.4 - new createRandomPointOnZoneBoundary()
|
|
||||||
- 3.0.5 - getPositiveRangeFromZoneProperty() now also supports upper bound (optional)
|
|
||||||
- 3.0.6 - new createSimplePolyZone()
|
|
||||||
- new createSimpleQuadZone()
|
|
||||||
- 3.0.7 - getPoint() can also get land y when passing true as second param
|
|
||||||
- 3.0.8 - new cfxZones.pointInOneOfZones(thePoint, zoneArray, useOrig)
|
|
||||||
- 3.0.9 - new getFlareColorStringFromZoneProperty()
|
|
||||||
- 3.1.0 - new getRGBVectorFromZoneProperty()
|
|
||||||
new getRGBAVectorFromZoneProperty()
|
|
||||||
- 3.1.1 - getRGBAVectorFromZoneProperty now supports #RRGGBBAA and #RRGGBB format
|
|
||||||
- owner for all, default 0
|
|
||||||
- 3.1.2 - getAllZoneProperties has numbersOnly option
|
|
||||||
- 3.1.3 - new numberArrayFromString()
|
|
||||||
- new declutterZone()
|
|
||||||
- new getZoneVolume()
|
|
||||||
- offsetZone also updates zone bounds when moving zones
|
|
||||||
- corrected bug in calculateZoneBounds()
|
|
||||||
- 4.0.0 - dmlZone OOP API started
|
- 4.0.0 - dmlZone OOP API started
|
||||||
- code revision / refactoring
|
- code revision / refactoring
|
||||||
- moved createPoint and copxPoint to dcsCommon, added bridging code
|
- moved createPoint and copxPoint to dcsCommon, added bridging code
|
||||||
@ -66,6 +41,9 @@ cfxZones.version = "4.0.10"
|
|||||||
- createPolyZone now correctly inits dcsOrigin
|
- createPolyZone now correctly inits dcsOrigin
|
||||||
- createCircleZone noew correctly inits dcsOrigin
|
- createCircleZone noew correctly inits dcsOrigin
|
||||||
- 4.0.10 - getBoolFromZoneProperty also supports "on" (=true) and "off" (=false)
|
- 4.0.10 - getBoolFromZoneProperty also supports "on" (=true) and "off" (=false)
|
||||||
|
- 4.1.0 - getBoolFromZoneProperty 'on/off' support for dml variant as well
|
||||||
|
- 4.1.1 - evalRemainder() updates
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
--
|
--
|
||||||
@ -99,40 +77,6 @@ cfxZones.ups = 1 -- updates per second. updates moving zones
|
|||||||
cfxZones.zones = {} -- these are the zone as retrieved from the mission.
|
cfxZones.zones = {} -- these are the zone as retrieved from the mission.
|
||||||
-- ALWAYS USE THESE, NEVER DCS's ZONES!!!!
|
-- ALWAYS USE THESE, NEVER DCS's ZONES!!!!
|
||||||
|
|
||||||
-- a zone has the following attributes
|
|
||||||
-- x, z -- coordinate of center. note they have correct x, 0, z coordinates so no y-->z mapping
|
|
||||||
-- radius (zero if quad zone)
|
|
||||||
-- isCircle (true if quad zone)
|
|
||||||
-- poly the quad coords are in the poly attribute and are a
|
|
||||||
-- 1..n, wound counter-clockwise as (currently) in DCS:
|
|
||||||
-- lower left, lower right upper left, upper right, all coords are x, 0, z
|
|
||||||
-- bounds - contain the AABB coords for the zone: ul (upper left), ur, ll (lower left), lr
|
|
||||||
-- for both circle and poly, all (x, 0, z)
|
|
||||||
|
|
||||||
-- zones can carry information in their names that can get processed into attributes
|
|
||||||
-- use
|
|
||||||
-- zones can also carry information in their 'properties' tag that ME allows to
|
|
||||||
-- edit. cfxZones provides an easy method to access these properties
|
|
||||||
-- - getZoneProperty (returns as string)
|
|
||||||
-- - getMinMaxFromZoneProperty
|
|
||||||
-- - getBoolFromZoneProperty
|
|
||||||
-- - getNumberFromZoneProperty
|
|
||||||
|
|
||||||
|
|
||||||
-- SUPPORTED PROPERTIES
|
|
||||||
-- - "linkedUnit" - zone moves with unit of that name. must be exact match
|
|
||||||
-- can be combined with other attributes that extend (e.g. scar manager and
|
|
||||||
-- limited pilots/airframes
|
|
||||||
--
|
|
||||||
|
|
||||||
--
|
|
||||||
-- readZonesFromDCS is executed exactly once at the beginning
|
|
||||||
-- from then on, use only the cfxZones.zones table
|
|
||||||
-- WARNING: cfxZones is NOT case-sensitive. All zone names are
|
|
||||||
-- indexed by upper case. If you have two zones with same name but
|
|
||||||
-- different case, one will be replaced
|
|
||||||
--
|
|
||||||
|
|
||||||
function cfxZones.readFromDCS(clearfirst)
|
function cfxZones.readFromDCS(clearfirst)
|
||||||
if (clearfirst) then
|
if (clearfirst) then
|
||||||
cfxZones.zones = {}
|
cfxZones.zones = {}
|
||||||
@ -618,8 +562,6 @@ function cfxZones.createRandomZoneInZone(name, inZone, targetRadius, entirelyIns
|
|||||||
-- inZone.
|
-- inZone.
|
||||||
-- entirelyInside is not guaranteed for polyzones
|
-- entirelyInside is not guaranteed for polyzones
|
||||||
|
|
||||||
-- trigger.action.outText("Zones: creating rZiZ with tr = " .. targetRadius .. " for " .. inZone.name .. " that as r = " .. inZone.radius, 10)
|
|
||||||
|
|
||||||
if inZone.isCircle then
|
if inZone.isCircle then
|
||||||
local sourceRadius = inZone.radius
|
local sourceRadius = inZone.radius
|
||||||
if entirelyInside and targetRadius > sourceRadius then targetRadius = sourceRadius end
|
if entirelyInside and targetRadius > sourceRadius then targetRadius = sourceRadius end
|
||||||
@ -1364,11 +1306,11 @@ end
|
|||||||
|
|
||||||
|
|
||||||
-- creating units in a zone
|
-- creating units in a zone
|
||||||
function cfxZones.createGroundUnitsInZoneForCoalition (theCoalition, groupName, theZone, theUnits, formation, heading)
|
function cfxZones.createGroundUnitsInZoneForCoalition (theCoalition, groupName, theZone, theUnits, formation, heading, liveries)
|
||||||
-- theUnits can be string or table of string
|
-- theUnits can be string or table of string
|
||||||
if not groupName then groupName = "G_"..theZone.name end
|
if not groupName then groupName = "G_"..theZone.name end
|
||||||
-- group name will be taken from zone name and prependend with "G_"
|
-- group name will be taken from zone name and prependend with "G_"
|
||||||
local theGroup = dcsCommon.createGroundGroupWithUnits(groupName, theUnits, theZone.radius, nil, formation)
|
local theGroup = dcsCommon.createGroundGroupWithUnits(groupName, theUnits, theZone.radius, nil, formation, nil, liveries)
|
||||||
|
|
||||||
-- turn the entire formation to heading
|
-- turn the entire formation to heading
|
||||||
if (not heading) then heading = 0 end
|
if (not heading) then heading = 0 end
|
||||||
@ -1380,7 +1322,6 @@ function cfxZones.createGroundUnitsInZoneForCoalition (theCoalition, groupName,
|
|||||||
theZone.point.x,
|
theZone.point.x,
|
||||||
theZone.point.z) -- watchit: Z!!!
|
theZone.point.z) -- watchit: Z!!!
|
||||||
|
|
||||||
|
|
||||||
-- create the group in the world and return it
|
-- create the group in the world and return it
|
||||||
-- first we need to translate the coalition to a legal
|
-- first we need to translate the coalition to a legal
|
||||||
-- country. we use UN for neutral, cjtf for red and blue
|
-- country. we use UN for neutral, cjtf for red and blue
|
||||||
@ -1446,7 +1387,7 @@ function cfxZones.unPulseFlag(args)
|
|||||||
cfxZones.setFlagValue(theFlag, newVal, theZone)
|
cfxZones.setFlagValue(theFlag, newVal, theZone)
|
||||||
end
|
end
|
||||||
|
|
||||||
function cfxZones.evalRemainder(remainder)
|
function cfxZones.evalRemainder(remainder, theZone)
|
||||||
local rNum = tonumber(remainder)
|
local rNum = tonumber(remainder)
|
||||||
if not rNum then
|
if not rNum then
|
||||||
-- we use remainder as name for flag
|
-- we use remainder as name for flag
|
||||||
@ -1475,6 +1416,10 @@ function cfxZones.evalRemainder(remainder)
|
|||||||
return rNum
|
return rNum
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function dmlZone:evalRemainder(remainder)
|
||||||
|
return cfxZones.evalRemainder(remainder, self)
|
||||||
|
end
|
||||||
|
|
||||||
function cfxZones.doPollFlag(theFlag, method, theZone) -- no OOP equivalent
|
function cfxZones.doPollFlag(theFlag, method, theZone) -- no OOP equivalent
|
||||||
-- WARNING:
|
-- WARNING:
|
||||||
-- if method is a number string, it will be interpreted as follows:
|
-- if method is a number string, it will be interpreted as follows:
|
||||||
@ -2381,7 +2326,7 @@ function cfxZones.getBoolFromZoneProperty(theZone, theProperty, defaultVal)
|
|||||||
if defaultVal == false then
|
if defaultVal == false then
|
||||||
-- only go true if exact match to yes or true
|
-- only go true if exact match to yes or true
|
||||||
theBool = false
|
theBool = false
|
||||||
theBool = (p == 'true') or (p == 'yes') or (p == "1") or (p == "on")
|
theBool = (p == 'true') or (p == 'yes') or (p == "1") or (p == 'on')
|
||||||
return theBool
|
return theBool
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -2407,13 +2352,13 @@ function dmlZone:getBoolFromZoneProperty(theProperty, defaultVal)
|
|||||||
if defaultVal == false then
|
if defaultVal == false then
|
||||||
-- only go true if exact match to yes or true
|
-- only go true if exact match to yes or true
|
||||||
theBool = false
|
theBool = false
|
||||||
theBool = (p == 'true') or (p == 'yes') or p == "1"
|
theBool = (p == 'true') or (p == 'yes') or (p == "1") or (p=="on")
|
||||||
return theBool
|
return theBool
|
||||||
end
|
end
|
||||||
|
|
||||||
local theBool = true
|
local theBool = true
|
||||||
-- only go false if exactly no or false or "0"
|
-- only go false if exactly no or false or "0"
|
||||||
theBool = (p ~= 'false') and (p ~= 'no') and (p ~= "0")
|
theBool = (p ~= 'false') and (p ~= 'no') and (p ~= "0") and (p ~= "off")
|
||||||
return theBool
|
return theBool
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -3576,8 +3521,6 @@ function cfxZones.init()
|
|||||||
|
|
||||||
-- pre-read zone owner for all zones
|
-- pre-read zone owner for all zones
|
||||||
-- much like verbose, all zones have owner
|
-- much like verbose, all zones have owner
|
||||||
-- local pZones = cfxZones.zonesWithProperty("owner")
|
|
||||||
-- for n, aZone in pairs(pZones) do
|
|
||||||
for n, aZone in pairs(cfxZones.zones) do
|
for n, aZone in pairs(cfxZones.zones) do
|
||||||
aZone.owner = cfxZones.getCoalitionFromZoneProperty(aZone, "owner", 0)
|
aZone.owner = cfxZones.getCoalitionFromZoneProperty(aZone, "owner", 0)
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,140 +0,0 @@
|
|||||||
cfxmon = {}
|
|
||||||
cfxmon.version = "1.0.1"
|
|
||||||
cfxmon.delay = 30 -- seconds for display
|
|
||||||
--[[--
|
|
||||||
Version History
|
|
||||||
1.0.0 - initial version
|
|
||||||
1.0.1 - better guard for even.initiator to check if unit exists
|
|
||||||
- and handle static objcects and other non-grouped objects
|
|
||||||
|
|
||||||
cfxmon is a monitor for all cfx events and callbacks
|
|
||||||
use monConfig to tell cfxmon which events and callbacks
|
|
||||||
to monitor. a Property with "no" or "false" will turn
|
|
||||||
that monitor OFF, else it will stay on
|
|
||||||
|
|
||||||
supported modules if loaded
|
|
||||||
dcsCommon
|
|
||||||
cfxPlayer
|
|
||||||
cfxGroundTroops
|
|
||||||
cfxObjectDestructDetector
|
|
||||||
cfxSpawnZones
|
|
||||||
|
|
||||||
--]]--
|
|
||||||
|
|
||||||
--
|
|
||||||
-- CALLBACKS
|
|
||||||
--
|
|
||||||
-- dcsCommon Callbacks
|
|
||||||
function cfxmon.pre(event)
|
|
||||||
trigger.action.outText("***mon - dcsPre: " .. event.id .. " (" .. dcsCommon.event2text(event.id) .. ")", cfxmon.delay)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function cfxmon.post(event)
|
|
||||||
trigger.action.outText("***mon - dcsPost: " .. event.id .. " (" .. dcsCommon.event2text(event.id) .. ")", cfxmon.delay)
|
|
||||||
end
|
|
||||||
|
|
||||||
function cfxmon.rejected(event)
|
|
||||||
trigger.action.outText("***mon - dcsReject: " .. event.id .. " (" .. dcsCommon.event2text(event.id) .. ")", cfxmon.delay)
|
|
||||||
end
|
|
||||||
|
|
||||||
function cfxmon.dcsCB(event) -- callback
|
|
||||||
local initiatorStat = ""
|
|
||||||
if event.initiator and Unit.isExist(event.initiator) then
|
|
||||||
local theUnit = event.initiator
|
|
||||||
-- we assume it is unit, but it can be a static object,
|
|
||||||
-- and may not have getGroup implemented!
|
|
||||||
if theUnit.getGroup then
|
|
||||||
local theGroup = theUnit:getGroup()
|
|
||||||
|
|
||||||
local theGroupName = "<none>"
|
|
||||||
if theGroup then theGroupName = theGroup:getName() end
|
|
||||||
initiatorStat = ", for " .. theUnit:getName()
|
|
||||||
initiatorStat = initiatorStat .. " of " .. theGroupName
|
|
||||||
else
|
|
||||||
initiatorStat = ", non-unit (static?) " .. theUnit:getName()
|
|
||||||
end
|
|
||||||
else
|
|
||||||
initiatorStat = ", NO Initiator"
|
|
||||||
end
|
|
||||||
trigger.action.outText("***mon - dcsMAIN: " .. event.id .. " (" .. dcsCommon.event2text(event.id) .. ")" .. initiatorStat, cfxmon.delay)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- cfxPlayer callback
|
|
||||||
function cfxmon.playerEventCB(evType, description, info, data)
|
|
||||||
trigger.action.outText("***mon - cfxPlayer: ".. evType ..": <" .. description .. ">", cfxmon.delay)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- cfxGroundTroops callback
|
|
||||||
function cfxmon.groundTroopsCB(reason, theGroup, orders, data)
|
|
||||||
trigger.action.outText("***mon - groundTroops: ".. reason ..": for group <" .. theGroup:getName() .. "> with orders " .. orders, cfxmon.delay)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- object destruct callbacks
|
|
||||||
function cfxmon.oDestructCB(zone, ObjectID, name)
|
|
||||||
trigger.action.outText("***mon - object destroyed: ".. ObjectID .." named <" .. name .. "> in zone " .. zone.name, cfxmon.delay)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- spawner callback
|
|
||||||
function cfxmon.spawnZoneCB(reason, theGroup, theSpawner)
|
|
||||||
local gName = "<nil>"
|
|
||||||
if theGroup then gName = theGroup:getName() end
|
|
||||||
trigger.action.outText("***mon - Spawner: ".. reason .." group <" .. gName .. "> in zone " .. theSpawner.name, cfxmon.delay)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- READ CONFIG AND SUBSCRIBE
|
|
||||||
function cfxmon.start ()
|
|
||||||
local theZone = cfxZones.getZoneByName("monConfig")
|
|
||||||
if not theZone then
|
|
||||||
trigger.action.outText("***mon: WARNING: NO config, defaulting", cfxmon.delay)
|
|
||||||
theZone = cfxZones.createSimpleZone("MONCONFIG")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- own config
|
|
||||||
cfxmon.delay = cfxZones.getNumberFromZoneProperty(theZone, "delay", 30)
|
|
||||||
trigger.action.outText("!!!mon: Delay is set to: " .. cfxmon.delay .. "seconds", 50)
|
|
||||||
|
|
||||||
-- dcsCommon
|
|
||||||
if cfxZones.getBoolFromZoneProperty(theZone, "dcsCommon", true) then
|
|
||||||
-- subscribe to dcs event handlers
|
|
||||||
-- note we have all, but only connect the main
|
|
||||||
dcsCommon.addEventHandler(cfxmon.dcsCB) -- we only connect one
|
|
||||||
trigger.action.outText("!!!mon: +dcsCommon", cfxmon.delay)
|
|
||||||
else
|
|
||||||
trigger.action.outText("***mon: -dcsCommon", cfxmon.delay)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- cfxPlayer
|
|
||||||
if cfxPlayer and cfxZones.getBoolFromZoneProperty(theZone, "cfxPlayer", true) then
|
|
||||||
cfxPlayer.addMonitor(cfxmon.playerEventCB)
|
|
||||||
trigger.action.outText("!!!mon: +cfxPlayer", cfxmon.delay)
|
|
||||||
else
|
|
||||||
trigger.action.outText("***mon: -cfxPlayer", cfxmon.delay)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- cfxGroundTroops
|
|
||||||
if cfxGroundTroops and cfxZones.getBoolFromZoneProperty(theZone, "cfxGroundTroops", true) then
|
|
||||||
cfxGroundTroops.addTroopsCallback(cfxmon.groundTroopsCB)
|
|
||||||
trigger.action.outText("!!!mon: +cfxGroundTroops", cfxmon.delay)
|
|
||||||
else
|
|
||||||
trigger.action.outText("***mon: -cfxGroundTroops", cfxmon.delay)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- objectDestructZones
|
|
||||||
if cfxObjectDestructDetector and cfxZones.getBoolFromZoneProperty(theZone, "cfxObjectDestructDetector", true) then
|
|
||||||
cfxObjectDestructDetector.addCallback(cfxmon.oDestructCB)
|
|
||||||
trigger.action.outText("!!!mon: +cfxObjectDestructDetector", cfxmon.delay)
|
|
||||||
else
|
|
||||||
trigger.action.outText("***mon: -cfxObjectDestructDetector", cfxmon.delay)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- spawnZones
|
|
||||||
if cfxSpawnZones and cfxZones.getBoolFromZoneProperty(theZone, "cfxSpawnZones", true) then
|
|
||||||
cfxSpawnZones.addCallback(cfxmon.spawnZoneCB)
|
|
||||||
trigger.action.outText("!!!mon: +cfxSpawnZones", cfxmon.delay)
|
|
||||||
else
|
|
||||||
trigger.action.outText("***mon: -cfxSpawnZones", cfxmon.delay)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
cfxmon.start()
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
cloneZones = {}
|
cloneZones = {}
|
||||||
cloneZones.version = "1.9.1"
|
cloneZones.version = "2.0.1"
|
||||||
cloneZones.verbose = false
|
cloneZones.verbose = false
|
||||||
cloneZones.requiredLibs = {
|
cloneZones.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
@ -27,79 +27,9 @@ cloneZones.respawnOnGroupID = true
|
|||||||
|
|
||||||
--[[--
|
--[[--
|
||||||
Clones Groups from ME mission data
|
Clones Groups from ME mission data
|
||||||
Copyright (c) 2022 by Christian Franz and cf/x AG
|
Copyright (c) 2022-2024 by Christian Franz and cf/x AG
|
||||||
|
|
||||||
Version History
|
Version History
|
||||||
1.0.0 - initial version
|
|
||||||
1.0.1 - preWipe attribute
|
|
||||||
1.1.0 - support for static objects
|
|
||||||
- despawn? attribute
|
|
||||||
1.1.1 - despawnAll: isExist guard
|
|
||||||
- map in? to f?
|
|
||||||
1.2.0 - Lua API integration: callbacks
|
|
||||||
- groupXlate struct
|
|
||||||
- unitXlate struct
|
|
||||||
- resolveReferences
|
|
||||||
- getGroupsInZone rewritten for data
|
|
||||||
- static resolve
|
|
||||||
- linkUnit resolve
|
|
||||||
- clone? synonym
|
|
||||||
- empty! and method attributes
|
|
||||||
1.3.0 - DML flag upgrade
|
|
||||||
1.3.1 - groupTracker interface
|
|
||||||
- trackWith: attribute
|
|
||||||
1.4.0 - Watchflags
|
|
||||||
1.4.1 - trackWith: accepts list of trackers
|
|
||||||
1.4.2 - onstart delays for 0.1 s to prevent static stacking
|
|
||||||
- turn bug for statics (bug in dcsCommon, resolved)
|
|
||||||
1.4.3 - embark/disembark now works with cloners
|
|
||||||
1.4.4 - removed some debugging verbosity
|
|
||||||
1.4.5 - randomizeLoc, rndLoc keyword
|
|
||||||
- cargo manager integration - pass cargo objects when present
|
|
||||||
1.4.6 - removed some verbosity for spawned aircraft with airfields on their routes
|
|
||||||
1.4.7 - DML watchflag and DML Flag polish, method-->cloneMethod
|
|
||||||
1.4.8 - added 'wipe?' synonym
|
|
||||||
1.4.9 - onRoad option
|
|
||||||
- rndHeading option
|
|
||||||
1.5.0 - persistence
|
|
||||||
1.5.1 - fixed static data cloning bug (load & save)
|
|
||||||
1.5.2 - fixed bug in trackWith: referencing wrong cloner
|
|
||||||
1.5.3 - centerOnly/wholeGroups attribute for rndLoc, rndHeading and onRoad
|
|
||||||
1.5.4 - parking for aircraft processing when cloning from template
|
|
||||||
1.5.5 - removed some verbosity
|
|
||||||
1.6.0 - fixed issues with cloning for zones with linked units
|
|
||||||
- cloning with useHeading
|
|
||||||
- major declutter
|
|
||||||
1.6.1 - removed some verbosity when not rotating routes
|
|
||||||
- updateTaskLocations ()
|
|
||||||
- cloning groups now also adjusts tasks like search and engage in zone
|
|
||||||
- cloning with rndLoc supports polygons
|
|
||||||
- corrected rndLoc without centerOnly to not include individual offsets
|
|
||||||
- ensure support of recovery tanker resolve cloned group
|
|
||||||
1.6.2 - optimization to hasLiveUnits()
|
|
||||||
1.6.3 - removed verbosity bug with rndLoc
|
|
||||||
uniqueNameGroupData has provisions for naming scheme
|
|
||||||
new uniqueNameStaticData() for naming scheme
|
|
||||||
1.6.4 - uniqueCounter is now configurable via config zone
|
|
||||||
new lclUniqueCounter config, also added to persistence
|
|
||||||
new globalCount
|
|
||||||
1.7.0 - wildcard "*" for masterOwner
|
|
||||||
- identical attribute: makes identical ID and name for unit and group as template
|
|
||||||
- new sameIDUnitData()
|
|
||||||
- nameScheme attribute: allow parametric names
|
|
||||||
- <o>, <z>, <s> wildcards
|
|
||||||
- <uid>, <lcl>, <i>, <g> wildcards
|
|
||||||
- identical=true overrides nameScheme
|
|
||||||
- masterOwner "*" convenience shortcut
|
|
||||||
1.7.1 - useDelicates handOff for delicates
|
|
||||||
- forcedRespawn passes zone instead of verbose
|
|
||||||
1.7.2 - onPerimeter attribute
|
|
||||||
1.7.3 - declutter option
|
|
||||||
1.8.0 - OOP cfxZones
|
|
||||||
- removed "empty+1" as planned
|
|
||||||
- upgraded config zone parsing
|
|
||||||
1.8.1 - clone zone definition now supports quads
|
|
||||||
1.8.2 - on pre-wipe, delay respawn by 0.5s to avoid 'dropping' statics
|
|
||||||
1.9.0 - minor clean-up for synonyms
|
1.9.0 - minor clean-up for synonyms
|
||||||
- spawnWithSpawner alias for HeloTroops etc requestable SPAWN
|
- spawnWithSpawner alias for HeloTroops etc requestable SPAWN
|
||||||
- requestable attribute
|
- requestable attribute
|
||||||
@ -107,6 +37,9 @@ cloneZones.respawnOnGroupID = true
|
|||||||
- cloner collects all types used
|
- cloner collects all types used
|
||||||
- groupScheme attribute
|
- groupScheme attribute
|
||||||
1.9.1 - useAI attribute
|
1.9.1 - useAI attribute
|
||||||
|
2.0.0 - clean-up
|
||||||
|
2.0.1 - improved empty! logic to account for deferred spawn
|
||||||
|
when pre-wipe is active
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
--
|
--
|
||||||
@ -136,16 +69,6 @@ function cloneZones.addCallback(theCallback)
|
|||||||
table.insert(cloneZones.callbacks, theCallback)
|
table.insert(cloneZones.callbacks, theCallback)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- reasons for callback
|
|
||||||
-- "will despawn group" - args is the group about to be despawned
|
|
||||||
-- "did spawn group" -- args is group that was spawned
|
|
||||||
-- "will despawn static"
|
|
||||||
-- "did spawn static"
|
|
||||||
-- "spawned" -- completed spawn cycle. args contains .groups and .statics spawned
|
|
||||||
-- "empty" -- all spawns have been killed, args is empty
|
|
||||||
-- "wiped" -- preWipe executed
|
|
||||||
-- "<none" -- something went wrong
|
|
||||||
|
|
||||||
function cloneZones.invokeCallbacks(theZone, reason, args)
|
function cloneZones.invokeCallbacks(theZone, reason, args)
|
||||||
if not theZone then return end
|
if not theZone then return end
|
||||||
if not reason then reason = "<none>" end
|
if not reason then reason = "<none>" end
|
||||||
@ -158,8 +81,6 @@ function cloneZones.invokeCallbacks(theZone, reason, args)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- group translation orig id
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- reading zones
|
-- reading zones
|
||||||
--
|
--
|
||||||
@ -217,7 +138,6 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
|
|||||||
theZone.cloner = true -- this is a cloner zoner
|
theZone.cloner = true -- this is a cloner zoner
|
||||||
theZone.mySpawns = {}
|
theZone.mySpawns = {}
|
||||||
theZone.myStatics = {}
|
theZone.myStatics = {}
|
||||||
-- update: getPoint is bad if it's a moving zone.
|
|
||||||
-- use getDCSOrigin instead
|
-- use getDCSOrigin instead
|
||||||
theZone.origin = theZone:getDCSOrigin()
|
theZone.origin = theZone:getDCSOrigin()
|
||||||
|
|
||||||
@ -247,7 +167,6 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
|
|||||||
-- iterate all units and save their individual types
|
-- iterate all units and save their individual types
|
||||||
for idy, aUnit in pairs(rawData.units) do
|
for idy, aUnit in pairs(rawData.units) do
|
||||||
local theType = aUnit.type
|
local theType = aUnit.type
|
||||||
-- trigger.action.outText("proccing type <" .. theType .. ">", 30)
|
|
||||||
if not theZone.allTypes[theType] then
|
if not theZone.allTypes[theType] then
|
||||||
theZone.allTypes[theType] = 1 -- first one
|
theZone.allTypes[theType] = 1 -- first one
|
||||||
else
|
else
|
||||||
@ -318,11 +237,8 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
|
|||||||
|
|
||||||
theZone.cooldown = theZone:getNumberFromZoneProperty("cooldown", -1) -- anything > 0 activates cd
|
theZone.cooldown = theZone:getNumberFromZoneProperty("cooldown", -1) -- anything > 0 activates cd
|
||||||
theZone.lastSpawnTimeStamp = -10000
|
theZone.lastSpawnTimeStamp = -10000
|
||||||
|
|
||||||
theZone.onStart = theZone:getBoolFromZoneProperty("onStart", false)
|
theZone.onStart = theZone:getBoolFromZoneProperty("onStart", false)
|
||||||
|
|
||||||
theZone.moveRoute = theZone:getBoolFromZoneProperty("moveRoute", false)
|
theZone.moveRoute = theZone:getBoolFromZoneProperty("moveRoute", false)
|
||||||
|
|
||||||
theZone.preWipe = theZone:getBoolFromZoneProperty("preWipe", false)
|
theZone.preWipe = theZone:getBoolFromZoneProperty("preWipe", false)
|
||||||
|
|
||||||
if theZone:hasProperty("empty!") then
|
if theZone:hasProperty("empty!") then
|
||||||
@ -383,7 +299,6 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
|
|||||||
theZone.rndHeading = theZone:getBoolFromZoneProperty("rndHeading", false)
|
theZone.rndHeading = theZone:getBoolFromZoneProperty("rndHeading", false)
|
||||||
|
|
||||||
theZone.onRoad = theZone:getBoolFromZoneProperty("onRoad", false)
|
theZone.onRoad = theZone:getBoolFromZoneProperty("onRoad", false)
|
||||||
|
|
||||||
theZone.onPerimeter = theZone:getBoolFromZoneProperty("onPerimeter", false)
|
theZone.onPerimeter = theZone:getBoolFromZoneProperty("onPerimeter", false)
|
||||||
|
|
||||||
-- check for name scheme and / or identical
|
-- check for name scheme and / or identical
|
||||||
@ -419,8 +334,6 @@ function cloneZones.despawnAll(theZone)
|
|||||||
trigger.action.outText("+++clnZ: despawn all - wiping zone <" .. theZone.name .. ">", 30)
|
trigger.action.outText("+++clnZ: despawn all - wiping zone <" .. theZone.name .. ">", 30)
|
||||||
end
|
end
|
||||||
for idx, aGroup in pairs(theZone.mySpawns) do
|
for idx, aGroup in pairs(theZone.mySpawns) do
|
||||||
--trigger.action.outText("++clnZ: despawn all " .. aGroup.name, 30)
|
|
||||||
|
|
||||||
if aGroup:isExist() then
|
if aGroup:isExist() then
|
||||||
if theZone.verbose then
|
if theZone.verbose then
|
||||||
trigger.action.outText("+++clnZ: will destroy <" .. aGroup:getName() .. ">", 30)
|
trigger.action.outText("+++clnZ: will destroy <" .. aGroup:getName() .. ">", 30)
|
||||||
@ -638,9 +551,6 @@ function cloneZones.nameFromSchema(schema, inName, theZone, sourceName, i)
|
|||||||
pos = string.find(outName, "<g>")
|
pos = string.find(outName, "<g>")
|
||||||
end
|
end
|
||||||
|
|
||||||
--if theZone.verbose then
|
|
||||||
-- trigger.action.outText("+++cln: schema [" .. schema .. "] for unit <" .. inName .. "> in zone <" .. theZone.name .. "> returns <" .. outName .. ">, iter = " .. iter, 30)
|
|
||||||
--end
|
|
||||||
return outName, iter
|
return outName, iter
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -866,7 +776,6 @@ function cloneZones.resolveWPReferences(rawData, theZone, dataTable)
|
|||||||
for grpIdx, gID in pairs(embarkers) do
|
for grpIdx, gID in pairs(embarkers) do
|
||||||
local resolvedID = cloneZones.resolveGroupID(gID, rawData, dataTable, "embark")
|
local resolvedID = cloneZones.resolveGroupID(gID, rawData, dataTable, "embark")
|
||||||
table.insert(newEmbarkers, resolvedID)
|
table.insert(newEmbarkers, resolvedID)
|
||||||
--trigger.action.outText("+++clnZ: resolved embark group id <" .. gID .. "> to <" .. resolvedID .. ">", 30)
|
|
||||||
end
|
end
|
||||||
-- replace old with new table
|
-- replace old with new table
|
||||||
taskData.params.groupsForEmbarking = newEmbarkers
|
taskData.params.groupsForEmbarking = newEmbarkers
|
||||||
@ -884,7 +793,6 @@ function cloneZones.resolveWPReferences(rawData, theZone, dataTable)
|
|||||||
-- translate old to new
|
-- translate old to new
|
||||||
local resolvedID = cloneZones.resolveGroupID(gID, rawData, dataTable, "embark")
|
local resolvedID = cloneZones.resolveGroupID(gID, rawData, dataTable, "embark")
|
||||||
table.insert(newEmbarkers, resolvedID)
|
table.insert(newEmbarkers, resolvedID)
|
||||||
--trigger.action.outText("+++clnZ: resolved distribute unit/group id <" .. aUnit .. "/" .. gID .. "> to <".. newUnit .. "/" .. resolvedID .. ">", 30)
|
|
||||||
end
|
end
|
||||||
-- store this as new group for
|
-- store this as new group for
|
||||||
-- translated transportID
|
-- translated transportID
|
||||||
@ -892,7 +800,6 @@ function cloneZones.resolveWPReferences(rawData, theZone, dataTable)
|
|||||||
end
|
end
|
||||||
-- replace old distribution with new
|
-- replace old distribution with new
|
||||||
taskData.params.distribution = newDist
|
taskData.params.distribution = newDist
|
||||||
--trigger.action.outText("+++clnZ: rebuilt distribution", 30)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- resolve selectedTransport unit reference
|
-- resolve selectedTransport unit reference
|
||||||
@ -900,7 +807,6 @@ function cloneZones.resolveWPReferences(rawData, theZone, dataTable)
|
|||||||
local tID = taskData.params.selectedTransport
|
local tID = taskData.params.selectedTransport
|
||||||
local newTID = cloneZones.resolveUnitID(tID, rawData, dataTable, "transportID")
|
local newTID = cloneZones.resolveUnitID(tID, rawData, dataTable, "transportID")
|
||||||
taskData.params.selectedTransport = newTID
|
taskData.params.selectedTransport = newTID
|
||||||
--trigger.action.outText("+++clnZ: resolved selected transport <" .. tID .. "> to <" .. newTID .. ">", 30)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- note: we may need to process x and y as well
|
-- note: we may need to process x and y as well
|
||||||
@ -932,7 +838,6 @@ function cloneZones.resolveReferences(theZone, dataTable)
|
|||||||
-- when an action refers to another group, we check if
|
-- when an action refers to another group, we check if
|
||||||
-- the group referred to is also a clone, and update
|
-- the group referred to is also a clone, and update
|
||||||
-- the reference to the newest incardnation
|
-- the reference to the newest incardnation
|
||||||
|
|
||||||
for idx, rawData in pairs(dataTable) do
|
for idx, rawData in pairs(dataTable) do
|
||||||
-- resolve references in waypoints
|
-- resolve references in waypoints
|
||||||
cloneZones.resolveWPReferences(rawData, theZone, dataTable)
|
cloneZones.resolveWPReferences(rawData, theZone, dataTable)
|
||||||
@ -946,7 +851,6 @@ function cloneZones.handoffTracking(theGroup, theZone)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
local trackerName = theZone.trackWith
|
local trackerName = theZone.trackWith
|
||||||
--if trackerName == "*" then trackerName = theZone.name end
|
|
||||||
-- now assemble a list of all trackers
|
-- now assemble a list of all trackers
|
||||||
if cloneZones.verbose or theZone.verbose then
|
if cloneZones.verbose or theZone.verbose then
|
||||||
trigger.action.outText("+++clnZ: clone pass-off: " .. trackerName, 30)
|
trigger.action.outText("+++clnZ: clone pass-off: " .. trackerName, 30)
|
||||||
@ -1065,8 +969,6 @@ function cloneZones.forcedRespawn(args)
|
|||||||
if newGroupID == theData.CZTargetID then
|
if newGroupID == theData.CZTargetID then
|
||||||
if verbose then
|
if verbose then
|
||||||
trigger.action.outText("GOOD REPLACEMENT new ID <" .. newGroupID .. "> matches target <" .. theData.CZTargetID .. "> for <" .. theData.name .. ">", 30)
|
trigger.action.outText("GOOD REPLACEMENT new ID <" .. newGroupID .. "> matches target <" .. theData.CZTargetID .. "> for <" .. theData.name .. ">", 30)
|
||||||
-- we can now remove the former group
|
|
||||||
-- and replace it with the new one
|
|
||||||
trigger.action.outText("will replace table entry at <" .. pos .. "> with new group", 30)
|
trigger.action.outText("will replace table entry at <" .. pos .. "> with new group", 30)
|
||||||
end
|
end
|
||||||
spawnedGroups[pos] = theGroup
|
spawnedGroups[pos] = theGroup
|
||||||
@ -1104,7 +1006,7 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
|
|||||||
local newCenter = spawnZone:getPoint() -- includes zone following updates
|
local newCenter = spawnZone:getPoint() -- includes zone following updates
|
||||||
local oCenter = theZone:getDCSOrigin() -- get original coords on map for cloning offsets
|
local oCenter = theZone:getDCSOrigin() -- get original coords on map for cloning offsets
|
||||||
-- calculate zoneDelta, is added to all vectors
|
-- calculate zoneDelta, is added to all vectors
|
||||||
local zoneDelta = dcsCommon.vSub(newCenter, theZone.origin) -- oCenter) --theZone.origin)
|
local zoneDelta = dcsCommon.vSub(newCenter, theZone.origin)
|
||||||
|
|
||||||
-- precalc turn value for linked rotation
|
-- precalc turn value for linked rotation
|
||||||
local dHeading = 0 -- for linked zones
|
local dHeading = 0 -- for linked zones
|
||||||
@ -1280,7 +1182,6 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
|
|||||||
|
|
||||||
-- make group, unit[1] and route point [1] all match up
|
-- make group, unit[1] and route point [1] all match up
|
||||||
if rawData.route and rawData.units[1] then
|
if rawData.route and rawData.units[1] then
|
||||||
-- trigger.action.outText("matching route point 1 and group with unit 1", 30)
|
|
||||||
rawData.route.points[1].x = rawData.units[1].x
|
rawData.route.points[1].x = rawData.units[1].x
|
||||||
rawData.route.points[1].y = rawData.units[1].y
|
rawData.route.points[1].y = rawData.units[1].y
|
||||||
rawData.x = rawData.units[1].x
|
rawData.x = rawData.units[1].x
|
||||||
@ -1439,7 +1340,6 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
|
|||||||
if not spawnZone.identical then
|
if not spawnZone.identical then
|
||||||
-- make sure static name is unique and remember original
|
-- make sure static name is unique and remember original
|
||||||
cloneZones.uniqueNameStaticData(rawData, spawnZone, theZone.name)
|
cloneZones.uniqueNameStaticData(rawData, spawnZone, theZone.name)
|
||||||
--rawData.name = dcsCommon.uuid(rawData.name)
|
|
||||||
rawData.unitId = cloneZones.uniqueID()
|
rawData.unitId = cloneZones.uniqueID()
|
||||||
end
|
end
|
||||||
rawData.CZTargetID = rawData.unitId
|
rawData.CZTargetID = rawData.unitId
|
||||||
@ -1529,7 +1429,6 @@ function cloneZones.turnOffAI(args)
|
|||||||
local theGroup = args[1]
|
local theGroup = args[1]
|
||||||
local theController = theGroup:getController()
|
local theController = theGroup:getController()
|
||||||
theController:setOnOff(false)
|
theController:setOnOff(false)
|
||||||
-- trigger.action.outText("turned off AI for group <" .. theGroup:getName() .. "> ", 30)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- retro-fit for helo troops and others to provide 'requestable' support
|
-- retro-fit for helo troops and others to provide 'requestable' support
|
||||||
@ -1721,7 +1620,6 @@ function cloneZones.getRequestableClonersInRange(aPoint, aRange, aSide)
|
|||||||
if aSide ~= 0 then
|
if aSide ~= 0 then
|
||||||
-- check if side is correct for owned zone
|
-- check if side is correct for owned zone
|
||||||
local resolved = cloneZones.resolveOwningCoalition(aZone)
|
local resolved = cloneZones.resolveOwningCoalition(aZone)
|
||||||
--if resolved ~= 0 and resolved ~= aSide then
|
|
||||||
if resolved == 0 or resolved ~= aSide then
|
if resolved == 0 or resolved ~= aSide then
|
||||||
-- failed ownership test. must match and not be zero
|
-- failed ownership test. must match and not be zero
|
||||||
hasMatch = false
|
hasMatch = false
|
||||||
@ -1749,7 +1647,7 @@ function cloneZones.update()
|
|||||||
for idx, aZone in pairs(cloneZones.cloners) do
|
for idx, aZone in pairs(cloneZones.cloners) do
|
||||||
-- see if deSpawn was pulled. Must run before spawn
|
-- see if deSpawn was pulled. Must run before spawn
|
||||||
if aZone.deSpawnFlag then
|
if aZone.deSpawnFlag then
|
||||||
local currTriggerVal = aZone:getFlagValue(aZone.deSpawnFlag) -- trigger.misc.getUserFlag(aZone.deSpawnFlag)
|
local currTriggerVal = aZone:getFlagValue(aZone.deSpawnFlag)
|
||||||
if currTriggerVal ~= aZone.lastDeSpawnValue then
|
if currTriggerVal ~= aZone.lastDeSpawnValue then
|
||||||
if cloneZones.verbose or aZone.verbose then
|
if cloneZones.verbose or aZone.verbose then
|
||||||
trigger.action.outText("+++clnZ: DEspawn triggered for <" .. aZone.name .. ">", 30)
|
trigger.action.outText("+++clnZ: DEspawn triggered for <" .. aZone.name .. ">", 30)
|
||||||
@ -1760,20 +1658,23 @@ function cloneZones.update()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- see if we got spawn? command
|
-- see if we got spawn? command
|
||||||
|
local willSpawn = false -- init to false.
|
||||||
if aZone:testZoneFlag(aZone.spawnFlag, aZone.cloneTriggerMethod, "lastSpawnValue") then
|
if aZone:testZoneFlag(aZone.spawnFlag, aZone.cloneTriggerMethod, "lastSpawnValue") then
|
||||||
if cloneZones.verbose then
|
if cloneZones.verbose or aZone.verbose then
|
||||||
trigger.action.outText("+++clnZ: spawn triggered for <" .. aZone.name .. ">", 30)
|
trigger.action.outText("+++clnZ: spawn triggered for <" .. aZone.name .. ">", 30)
|
||||||
end
|
end
|
||||||
cloneZones.spawnWithCloner(aZone)
|
cloneZones.spawnWithCloner(aZone)
|
||||||
|
willSpawn = true -- in case prewipe, we delay
|
||||||
|
-- can mess with empty, so we tell empty to skip
|
||||||
end
|
end
|
||||||
|
|
||||||
-- empty handling
|
-- empty handling
|
||||||
local isEmpty = cloneZones.countLiveUnits(aZone) < 1 and aZone.hasClones
|
local isEmpty = cloneZones.countLiveUnits(aZone) < 1 and aZone.hasClones
|
||||||
if isEmpty then
|
if isEmpty and (willSpawn == false) then
|
||||||
-- see if we need to bang a flag
|
-- see if we need to bang a flag
|
||||||
if aZone.emptyBangFlag then
|
if aZone.emptyBangFlag then
|
||||||
aZone:pollFlag(aZone.emptyBangFlag, aZone.cloneMethod)
|
aZone:pollFlag(aZone.emptyBangFlag, aZone.cloneMethod)
|
||||||
if cloneZones.verbose then
|
if cloneZones.verbose or aZone.verbose then
|
||||||
trigger.action.outText("+++clnZ: bang! on " .. aZone.emptyBangFlag, 30)
|
trigger.action.outText("+++clnZ: bang! on " .. aZone.emptyBangFlag, 30)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -1983,7 +1884,6 @@ function cloneZones.loadData()
|
|||||||
trigger.action.outText("+++clnZ: linked static <" .. oName .. "> to unit <" .. newStatic.linkUnit .. ">", 30)
|
trigger.action.outText("+++clnZ: linked static <" .. oName .. "> to unit <" .. newStatic.linkUnit .. ">", 30)
|
||||||
end
|
end
|
||||||
local cty = newStatic.cty
|
local cty = newStatic.cty
|
||||||
-- local cat = staticData.cat
|
|
||||||
-- spawn new one, replacing same.named old, dead if required
|
-- spawn new one, replacing same.named old, dead if required
|
||||||
gStatic = coalition.addStaticObject(cty, newStatic)
|
gStatic = coalition.addStaticObject(cty, newStatic)
|
||||||
|
|
||||||
@ -2004,7 +1904,7 @@ function cloneZones.loadData()
|
|||||||
for cName, cData in pairs(allCloners) do
|
for cName, cData in pairs(allCloners) do
|
||||||
local theCloner = cloneZones.getCloneZoneByName(cName)
|
local theCloner = cloneZones.getCloneZoneByName(cName)
|
||||||
if theCloner then
|
if theCloner then
|
||||||
theCloner.isStarted = true -- ALWAYS TRUE WHEN WE COME HERE! cData.isStarted
|
theCloner.isStarted = true
|
||||||
-- init myUniqueCounter if it exists
|
-- init myUniqueCounter if it exists
|
||||||
if cData.myUniqueCounter then
|
if cData.myUniqueCounter then
|
||||||
theCloner.myUniqueCounter = cData.myUniqueCounter
|
theCloner.myUniqueCounter = cData.myUniqueCounter
|
||||||
@ -2095,9 +1995,6 @@ function cloneZones.start()
|
|||||||
|
|
||||||
-- process cloner Zones
|
-- process cloner Zones
|
||||||
local attrZones = cfxZones.getZonesWithAttributeNamed("cloner")
|
local attrZones = cfxZones.getZonesWithAttributeNamed("cloner")
|
||||||
|
|
||||||
-- now create an rnd gen for each one and add them
|
|
||||||
-- to our watchlist
|
|
||||||
for k, aZone in pairs(attrZones) do
|
for k, aZone in pairs(attrZones) do
|
||||||
cloneZones.createClonerWithZone(aZone) -- process attribute and add to zone
|
cloneZones.createClonerWithZone(aZone) -- process attribute and add to zone
|
||||||
cloneZones.addCloneZone(aZone)
|
cloneZones.addCloneZone(aZone)
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
countDown = {}
|
countDown = {}
|
||||||
countDown.version = "1.3.2"
|
countDown.version = "2.0.0"
|
||||||
countDown.verbose = false
|
countDown.verbose = false
|
||||||
countDown.ups = 1
|
countDown.ups = 1
|
||||||
countDown.requiredLibs = {
|
countDown.requiredLibs = {
|
||||||
@ -9,26 +9,14 @@ countDown.requiredLibs = {
|
|||||||
|
|
||||||
--[[--
|
--[[--
|
||||||
count down on flags to generate new signal on out
|
count down on flags to generate new signal on out
|
||||||
Copyright (c) 2022, 2023 by Christian Franz and cf/x AG
|
Copyright (c) 2022 - 2024 by Christian Franz and cf/x AG
|
||||||
|
|
||||||
Version History
|
Version History
|
||||||
1.0.0 - initial version
|
2.0.0 - dmlZones, OOP upgrade
|
||||||
1.1.0 - Lua interface: callbacks
|
counterOut! --> counterOut#
|
||||||
- corrected verbose (erroneously always suppressed)
|
output method defaults to "inc"
|
||||||
- triggerFlag --> triggerCountFlag
|
better config parsing
|
||||||
1.1.1 - corrected bug in invokeCallback
|
cleanup
|
||||||
1.2.0 - DML Flags
|
|
||||||
- counterOut!
|
|
||||||
- ups config
|
|
||||||
1.2.1 - disableCounter?
|
|
||||||
1.3.0 - DML & Watchflags upgrade
|
|
||||||
- method --> ctdwnMethod
|
|
||||||
1.3.1 - clock? synonym
|
|
||||||
- new reset? input
|
|
||||||
- improved verbosity
|
|
||||||
- zone-local verbosity
|
|
||||||
1.3.2 - enableCounter? to balande disableCounter?
|
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
countDown.counters = {}
|
countDown.counters = {}
|
||||||
@ -77,7 +65,7 @@ end
|
|||||||
--
|
--
|
||||||
function countDown.createCountDownWithZone(theZone)
|
function countDown.createCountDownWithZone(theZone)
|
||||||
-- start val - a range
|
-- start val - a range
|
||||||
theZone.startMinVal, theZone.startMaxVal = cfxZones.getPositiveRangeFromZoneProperty(theZone, "countDown", 1) -- we know this exists
|
theZone.startMinVal, theZone.startMaxVal = theZone:getPositiveRangeFromZoneProperty("countDown", 1) -- we know this exists
|
||||||
theZone.currVal = dcsCommon.randomBetween(theZone.startMinVal, theZone.startMaxVal)
|
theZone.currVal = dcsCommon.randomBetween(theZone.startMinVal, theZone.startMaxVal)
|
||||||
|
|
||||||
if countDown.verbose then
|
if countDown.verbose then
|
||||||
@ -85,32 +73,32 @@ function countDown.createCountDownWithZone(theZone)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- loop
|
-- loop
|
||||||
theZone.loop = cfxZones.getBoolFromZoneProperty(theZone, "loop", false)
|
theZone.loop = theZone:getBoolFromZoneProperty("loop", false)
|
||||||
|
|
||||||
-- extend after zero
|
-- extend after zero
|
||||||
theZone.belowZero = cfxZones.getBoolFromZoneProperty(theZone, "belowZero", false)
|
theZone.belowZero = theZone:getBoolFromZoneProperty("belowZero", false)
|
||||||
|
|
||||||
-- out method
|
-- out method
|
||||||
theZone.ctdwnMethod = cfxZones.getStringFromZoneProperty(theZone, "method", "flip")
|
theZone.ctdwnMethod = theZone:getStringFromZoneProperty("method", "inc")
|
||||||
if cfxZones.hasProperty(theZone, "ctdwnMethod") then
|
if theZone:hasProperty("ctdwnMethod") then
|
||||||
theZone.ctdwnMethod = cfxZones.getStringFromZoneProperty(theZone, "ctdwnMethod", "flip")
|
theZone.ctdwnMethod = theZone:getStringFromZoneProperty( "ctdwnMethod", "inc")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- triggerMethod for inputs
|
-- triggerMethod for inputs
|
||||||
theZone.ctdwnTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "triggerMethod", "change")
|
theZone.ctdwnTriggerMethod = theZone:getStringFromZoneProperty( "triggerMethod", "change")
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "ctdwnTriggerMethod") then
|
if theZone:hasProperty("ctdwnTriggerMethod") then
|
||||||
theZone.ctdwnTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "ctdwnTriggerMethod", "change")
|
theZone.ctdwnTriggerMethod = theZone:getStringFromZoneProperty("ctdwnTriggerMethod", "change")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- trigger flag "count" / "start?"
|
-- trigger flag "count" / "start?"
|
||||||
if cfxZones.hasProperty(theZone, "count?") then
|
if theZone:hasProperty("count?") then
|
||||||
theZone.triggerCountFlag = cfxZones.getStringFromZoneProperty(theZone, "count?", "<none>")
|
theZone.triggerCountFlag = theZone:getStringFromZoneProperty("count?", "<none>")
|
||||||
elseif cfxZones.hasProperty(theZone, "clock?") then
|
elseif theZone:hasProperty("clock?") then
|
||||||
theZone.triggerCountFlag = cfxZones.getStringFromZoneProperty(theZone, "clock?", "<none>")
|
theZone.triggerCountFlag = theZone:getStringFromZoneProperty("clock?", "<none>")
|
||||||
-- can also use in? for counting. we always use triggerCountFlag
|
-- can also use in? for counting. we always use triggerCountFlag
|
||||||
elseif cfxZones.hasProperty(theZone, "in?") then
|
elseif theZone:hasProperty("in?") then
|
||||||
theZone.triggerCountFlag = cfxZones.getStringFromZoneProperty(theZone, "in?", "<none>")
|
theZone.triggerCountFlag = theZone:getStringFromZoneProperty("in?", "<none>")
|
||||||
end
|
end
|
||||||
|
|
||||||
if theZone.triggerCountFlag then
|
if theZone.triggerCountFlag then
|
||||||
@ -118,40 +106,40 @@ function countDown.createCountDownWithZone(theZone)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- reset
|
-- reset
|
||||||
if cfxZones.hasProperty(theZone, "reset?") then
|
if theZone:hasProperty("reset?") then
|
||||||
theZone.resetFlag = cfxZones.getStringFromZoneProperty(theZone, "reset?", "<none>")
|
theZone.resetFlag = theZone:getStringFromZoneProperty("reset?", "<none>")
|
||||||
theZone.resetFlagValue = cfxZones.getFlagValue(theZone.resetFlag, theZone)
|
theZone.resetFlagValue = cfxZones.getFlagValue(theZone.resetFlag, theZone)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- zero! bang
|
-- zero! bang
|
||||||
if cfxZones.hasProperty(theZone, "zero!") then
|
if theZone:hasProperty("zero!") then
|
||||||
theZone.zeroFlag = cfxZones.getStringFromZoneProperty(theZone, "zero!", "<none>")
|
theZone.zeroFlag = theZone:getStringFromZoneProperty("zero!", "<none>")
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "out!") then
|
if theZone:hasProperty("out!") then
|
||||||
theZone.zeroFlag = cfxZones.getStringFromZoneProperty(theZone, "out!", "<none>")
|
theZone.zeroFlag = theZone:getStringFromZoneProperty("out!", "<none>")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- TMinus! bang
|
-- TMinus! bang
|
||||||
if cfxZones.hasProperty(theZone, "tMinus!") then
|
if theZone:hasProperty("tMinus!") then
|
||||||
theZone.tMinusFlag = cfxZones.getStringFromZoneProperty(theZone, "tMinus!", "<none>")
|
theZone.tMinusFlag = theZone:getStringFromZoneProperty("tMinus!", "<none>")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- counterOut val
|
-- counterOut val
|
||||||
if cfxZones.hasProperty(theZone, "counterOut!") then
|
if theZone:hasProperty("counterOut#") then
|
||||||
theZone.counterOut = cfxZones.getStringFromZoneProperty(theZone, "counterOut!", "<none>")
|
theZone.counterOut = theZone:getStringFromZoneProperty( "counterOut#", "<none>")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- disableFlag/enableFlag
|
-- disableFlag/enableFlag
|
||||||
theZone.counterDisabled = false
|
theZone.counterDisabled = false
|
||||||
if cfxZones.hasProperty(theZone, "disableCounter?") then
|
if theZone:hasProperty("disableCounter?") then
|
||||||
theZone.disableCounterFlag = cfxZones.getStringFromZoneProperty(theZone, "disableCounter?", "<none>")
|
theZone.disableCounterFlag = theZone:getStringFromZoneProperty("disableCounter?", "<none>")
|
||||||
theZone.disableCounterFlagVal = cfxZones.getFlagValue(theZone.disableCounterFlag, theZone)
|
theZone.disableCounterFlagVal = theZone:getFlagValue(theZone.disableCounterFlag)
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "enableCounter?") then
|
if theZone:hasProperty("enableCounter?") then
|
||||||
theZone.enableCounterFlag = cfxZones.getStringFromZoneProperty(theZone, "enableCounter?", "<none>")
|
theZone.enableCounterFlag = theZone:getStringFromZoneProperty("enableCounter?", "<none>")
|
||||||
theZone.enableCounterFlagVal = cfxZones.getFlagValue(theZone.enableCounterFlag, theZone)
|
theZone.enableCounterFlagVal = theZone:getFlagValue(theZone.enableCounterFlag)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@ -170,7 +158,7 @@ function countDown.reset(theZone)
|
|||||||
cfxZones.setFlagValue(theZone.counterOut, val, theZone)
|
cfxZones.setFlagValue(theZone.counterOut, val, theZone)
|
||||||
end
|
end
|
||||||
-- read and ignore any pulling of the clock flag
|
-- read and ignore any pulling of the clock flag
|
||||||
local ignore = cfxZones.testZoneFlag(theZone, theZone.triggerCountFlag, theZone.ctdwnTriggerMethod, "lastCountTriggerValue")
|
local ignore = theZone:testZoneFlag(theZone.triggerCountFlag, theZone.ctdwnTriggerMethod, "lastCountTriggerValue")
|
||||||
-- simply updates lastTriggerValue to current clock value
|
-- simply updates lastTriggerValue to current clock value
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -186,7 +174,7 @@ function countDown.isTriggered(theZone)
|
|||||||
local looping = false
|
local looping = false
|
||||||
|
|
||||||
if theZone.counterOut then
|
if theZone.counterOut then
|
||||||
cfxZones.setFlagValue(theZone.counterOut, val, theZone)
|
theZone:setFlagValue(theZone.counterOut, val)
|
||||||
end
|
end
|
||||||
|
|
||||||
if val > 0 then
|
if val > 0 then
|
||||||
@ -196,7 +184,7 @@ function countDown.isTriggered(theZone)
|
|||||||
if countDown.verbose or theZone.verbose then
|
if countDown.verbose or theZone.verbose then
|
||||||
trigger.action.outText("+++cntD: <" .. theZone.name .. "> TMINUTS on flag <" .. theZone.tMinusFlag .. ">", 30)
|
trigger.action.outText("+++cntD: <" .. theZone.name .. "> TMINUTS on flag <" .. theZone.tMinusFlag .. ">", 30)
|
||||||
end
|
end
|
||||||
cfxZones.pollFlag(theZone.tMinusFlag, theZone.ctdwnMethod, theZone)
|
theZone:pollFlag(theZone.tMinusFlag, theZone.ctdwnMethod)
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif val == 0 then
|
elseif val == 0 then
|
||||||
@ -206,7 +194,7 @@ function countDown.isTriggered(theZone)
|
|||||||
if countDown.verbose or theZone.verbose then
|
if countDown.verbose or theZone.verbose then
|
||||||
trigger.action.outText("+++cntD: ZERO <" .. theZone.name .. "> on flag <" .. theZone.zeroFlag .. ">", 30)
|
trigger.action.outText("+++cntD: ZERO <" .. theZone.name .. "> on flag <" .. theZone.zeroFlag .. ">", 30)
|
||||||
end
|
end
|
||||||
cfxZones.pollFlag(theZone.zeroFlag, theZone.ctdwnMethod, theZone)
|
theZone:pollFlag(theZone.zeroFlag, theZone.ctdwnMethod)
|
||||||
end
|
end
|
||||||
|
|
||||||
if theZone.loop then
|
if theZone.loop then
|
||||||
@ -225,7 +213,7 @@ function countDown.isTriggered(theZone)
|
|||||||
if countDown.verbose or theZone.verbose then
|
if countDown.verbose or theZone.verbose then
|
||||||
trigger.action.outText("+++cntD: Below Zero", 30)
|
trigger.action.outText("+++cntD: Below Zero", 30)
|
||||||
end
|
end
|
||||||
cfxZones.pollFlag(theZone.zeroFlag, theZone.ctdwnMethod, theZone)
|
theZone:pollFlag(theZone.zeroFlag, theZone.ctdwnMethod)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@ -243,7 +231,7 @@ function countDown.update()
|
|||||||
|
|
||||||
for idx, aZone in pairs(countDown.counters) do
|
for idx, aZone in pairs(countDown.counters) do
|
||||||
if aZone.resetFlag then
|
if aZone.resetFlag then
|
||||||
if cfxZones.testZoneFlag(aZone, aZone.resetFlag, aZone.ctdwnTriggerMethod, "resetFlagValue") then
|
if aZone:testZoneFlag(aZone.resetFlag, aZone.ctdwnTriggerMethod, "resetFlagValue") then
|
||||||
-- reset pulled, reset the timer to start condition
|
-- reset pulled, reset the timer to start condition
|
||||||
countDown.reset(aZone)
|
countDown.reset(aZone)
|
||||||
end
|
end
|
||||||
@ -252,7 +240,7 @@ function countDown.update()
|
|||||||
-- make sure to re-start before reading time limit
|
-- make sure to re-start before reading time limit
|
||||||
-- if reset, lastTriggerValue is updated and will not trigger
|
-- if reset, lastTriggerValue is updated and will not trigger
|
||||||
if (not aZone.counterDisabled) and
|
if (not aZone.counterDisabled) and
|
||||||
cfxZones.testZoneFlag(aZone, aZone.triggerCountFlag, aZone.ctdwnTriggerMethod, "lastCountTriggerValue")
|
aZone:testZoneFlag(aZone.triggerCountFlag, aZone.ctdwnTriggerMethod, "lastCountTriggerValue")
|
||||||
then
|
then
|
||||||
if countDown.verbose then
|
if countDown.verbose then
|
||||||
trigger.action.outText("+++cntD: triggered on in?", 30)
|
trigger.action.outText("+++cntD: triggered on in?", 30)
|
||||||
@ -260,14 +248,14 @@ function countDown.update()
|
|||||||
countDown.isTriggered(aZone)
|
countDown.isTriggered(aZone)
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.testZoneFlag(aZone, aZone.disableCounterFlag, aZone.ctdwnTriggerMethod, "disableCounterFlagVal") then
|
if aZone:testZoneFlag(aZone.disableCounterFlag, aZone.ctdwnTriggerMethod, "disableCounterFlagVal") then
|
||||||
if countDown.verbose then
|
if countDown.verbose then
|
||||||
trigger.action.outText("+++cntD: disabling counter " .. aZone.name, 30)
|
trigger.action.outText("+++cntD: disabling counter " .. aZone.name, 30)
|
||||||
end
|
end
|
||||||
aZone.counterDisabled = true
|
aZone.counterDisabled = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.testZoneFlag(aZone, aZone.enableCounterFlag, aZone.ctdwnTriggerMethod, "enableCounterFlagVal") then
|
if aZone:testZoneFlag(aZone.enableCounterFlag, aZone.ctdwnTriggerMethod, "enableCounterFlagVal") then
|
||||||
if countDown.verbose then
|
if countDown.verbose then
|
||||||
trigger.action.outText("+++cntD: ENabling counter " .. aZone.name, 30)
|
trigger.action.outText("+++cntD: ENabling counter " .. aZone.name, 30)
|
||||||
end
|
end
|
||||||
@ -282,16 +270,13 @@ end
|
|||||||
function countDown.readConfigZone()
|
function countDown.readConfigZone()
|
||||||
local theZone = cfxZones.getZoneByName("countDownConfig")
|
local theZone = cfxZones.getZoneByName("countDownConfig")
|
||||||
if not theZone then
|
if not theZone then
|
||||||
if countDown.verbose then
|
theZone = cfxZones.createSimpleZone("countDownConfig")
|
||||||
trigger.action.outText("+++cntD: NO config zone!", 30)
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
end
|
||||||
|
|
||||||
countDown.ups = cfxZones.getNumberFromZoneProperty(theZone, "ups", 1)
|
countDown.ups = theZone:getNumberFromZoneProperty("ups", 1)
|
||||||
if countDown.ups < 0.001 then countDown.ups = 0.001 end
|
if countDown.ups < 0.001 then countDown.ups = 0.001 end
|
||||||
|
|
||||||
countDown.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
countDown.verbose = theZone.verbose
|
||||||
|
|
||||||
if countDown.verbose then
|
if countDown.verbose then
|
||||||
trigger.action.outText("+++cntD: read config", 30)
|
trigger.action.outText("+++cntD: read config", 30)
|
||||||
|
|||||||
@ -1,183 +1,13 @@
|
|||||||
dcsCommon = {}
|
dcsCommon = {}
|
||||||
dcsCommon.version = "2.9.8"
|
dcsCommon.version = "3.0.0"
|
||||||
--[[-- VERSION HISTORY
|
--[[-- VERSION HISTORY
|
||||||
2.2.6 - compassPositionOfARelativeToB
|
3.0.0 - removed bad bug in stringStartsWith, only relevant if caseSensitive is false
|
||||||
- clockPositionOfARelativeToB
|
- point2text new intsOnly option
|
||||||
2.2.7 - isTroopCarrier
|
- arrangeGroupDataIntoFormation minDist harden
|
||||||
- distFlat
|
- cleanup
|
||||||
2.2.8 - fixed event2text
|
- new pointInDirectionOfPointXYY()
|
||||||
2.2.9 - getUnitAGL
|
- createGroundGroupWithUnits now supports liveries
|
||||||
- getUnitAlt
|
- new getAllExistingPlayersAndUnits()
|
||||||
- getUnitSpeed
|
|
||||||
- getUnitHeading
|
|
||||||
- getUnitHeadingDegrees
|
|
||||||
- mag
|
|
||||||
- clockPositionOfARelativeToB with own heading
|
|
||||||
2.3.0 - unitIsInfantry
|
|
||||||
2.3.1 - bool2YesNo
|
|
||||||
- bool2Text
|
|
||||||
2.3.2 - getGroupAvgSpeed
|
|
||||||
- getGroupMaxSpeed
|
|
||||||
2.3.3 - getSizeOfTable
|
|
||||||
2.3.4 - isSceneryObject
|
|
||||||
coalition2county
|
|
||||||
2.3.5 - smallRandom
|
|
||||||
pickRandom uses smallRandom
|
|
||||||
airfield handling, parking
|
|
||||||
flight waypoint handling
|
|
||||||
landing waypoint creation
|
|
||||||
take-off waypoint creation
|
|
||||||
2.3.6 - createOverheadAirdromeRoutPintData(aerodrome)
|
|
||||||
2.3.7 - coalition2county - warning when creating UN
|
|
||||||
2.3.8 - improved headingOfBInDegrees, new getClockDirection
|
|
||||||
2.3.9 - getClosingVelocity
|
|
||||||
- dot product
|
|
||||||
- magSquare
|
|
||||||
- vMag
|
|
||||||
2.4.0 - libCheck
|
|
||||||
2.4.1 - grid/square/rect formation
|
|
||||||
- arrangeGroupInNColumns formation
|
|
||||||
- 2Columns formation deep and wide formation
|
|
||||||
2.4.2 - getAirbasesInRangeOfPoint
|
|
||||||
2.4.3 - lerp
|
|
||||||
2.4.4 - getClosestAirbaseTo
|
|
||||||
- fixed bug in containsString when strings equal
|
|
||||||
2.4.5 - added cargo and mass options to createStaticObjectData
|
|
||||||
2.4.6 - fixed randompercent
|
|
||||||
2.4.7 - smokeColor2Num(smokeColor)
|
|
||||||
2.4.8 - linkStaticDataToUnit()
|
|
||||||
2.4.9 - trim functions
|
|
||||||
- createGroundUnitData uses trim function to remove leading/trailing blanks
|
|
||||||
so now we can use blanks after comma to separate types
|
|
||||||
- dcsCommon.trimArray(
|
|
||||||
- createStaticObjectData uses trim for type
|
|
||||||
- getEnemyCoalitionFor understands strings, still returns number
|
|
||||||
- coalition2county also understands 'red' and 'blue'
|
|
||||||
2.5.0 - "Line" formation with one unit places unit at center
|
|
||||||
2.5.1 - vNorm(a)
|
|
||||||
2.5.1 - added SA-18 Igla manpad to unitIsInfantry()
|
|
||||||
2.5.2 - added copyArray method
|
|
||||||
- corrected heading in createStaticObjectData
|
|
||||||
2.5.3 - corrected rotateGroupData bug for cz
|
|
||||||
- removed forced error in failed pickRandom
|
|
||||||
2.5.4 - rotateUnitData()
|
|
||||||
- randomBetween()
|
|
||||||
2.5.5 - stringStartsWithDigit()
|
|
||||||
- stringStartsWithLetter()
|
|
||||||
- stringIsPositiveNumber()
|
|
||||||
2.5.6 - corrected stringEndsWith() bug with str
|
|
||||||
2.5.7 - point2text(p)
|
|
||||||
2.5.8 - string2GroupCat()
|
|
||||||
2.5.9 - string2ObjectCat()
|
|
||||||
2.6.0 - unified uuid, removed uuIdent
|
|
||||||
2.6.1 - removed bug in rotateUnitData: cy --> cz param passing
|
|
||||||
2.6.2 - new combineTables()
|
|
||||||
2.6.3 - new tacan2freq()
|
|
||||||
2.6.4 - new processHMS()
|
|
||||||
2.6.5 - new bearing2compass()
|
|
||||||
- new bearingdegrees2compass()
|
|
||||||
- new latLon2Text() - based on mist
|
|
||||||
2.6.6 - new nowString()
|
|
||||||
- new str2num()
|
|
||||||
- new stringRemainsStartingWith()
|
|
||||||
- new stripLF()
|
|
||||||
- new removeBlanks()
|
|
||||||
2.6.7 - new menu2text()
|
|
||||||
2.6.8 - new getMissionName()
|
|
||||||
- new flagArrayFromString()
|
|
||||||
2.6.9 - new getSceneryObjectsInZone()
|
|
||||||
- new getSceneryObjectInZoneByName()
|
|
||||||
2.7.0 - new synchGroupData()
|
|
||||||
clone, topClone and copyArray now all nil-trap
|
|
||||||
2.7.1 - new isPlayerUnit() -- moved from cfxPlayer
|
|
||||||
new getAllExistingPlayerUnitsRaw - from cfxPlayer
|
|
||||||
new typeIsInfantry()
|
|
||||||
2.7.2 - new rangeArrayFromString()
|
|
||||||
fixed leading blank bug in flagArrayFromString
|
|
||||||
new incFlag()
|
|
||||||
new decFlag()
|
|
||||||
nil trap in stringStartsWith()
|
|
||||||
new getClosestFreeSlotForCatInAirbaseTo()
|
|
||||||
2.7.3 - new string2Array()
|
|
||||||
- additional guard for isPlayerUnit
|
|
||||||
2.7.4 - new array2string()
|
|
||||||
2.7.5 - new bitAND32()
|
|
||||||
- new LSR()
|
|
||||||
- new num2bin()
|
|
||||||
2.7.6 - new getObjectsForCatAtPointWithRadius()
|
|
||||||
2.7.7 - clone() has new stripMeta option. pass true to remove all meta tables
|
|
||||||
- dumpVar2Str detects meta tables
|
|
||||||
- rotateGroupData kills unit's psi value if it existed since it messes with heading
|
|
||||||
- rotateGroupData - changes psi to -heading if it exists rather than nilling
|
|
||||||
2.7.8 - new getGeneralDirection()
|
|
||||||
- new getNauticalDirection()
|
|
||||||
- more robust guards for getUnitSpeed
|
|
||||||
2.7.9 - new bool2Num(theBool)
|
|
||||||
- new aspectByDirection()
|
|
||||||
- createGroundGroupWithUnits corrected spelling of minDist, crashed scattered formation
|
|
||||||
- randomPointInCircle fixed erroneous local for x, z
|
|
||||||
- "scattered" formation repaired
|
|
||||||
2.7.10- semaphore groundwork
|
|
||||||
2.8.0 - new collectMissionIDs at start-up
|
|
||||||
- new getUnitNameByID
|
|
||||||
- new getGroupNameByID
|
|
||||||
- bool2YesNo alsco can return NIL
|
|
||||||
- new getUnitStartPosByID
|
|
||||||
2.8.1 - arrayContainsString: type checking for theArray and warning
|
|
||||||
- processStringWildcards()
|
|
||||||
- new wildArrayContainsString()
|
|
||||||
- fix for stringStartsWith oddity with aircraft types
|
|
||||||
2.8.2 - better fixes for string.find() in stringStartsWith and containsString
|
|
||||||
- dcsCommon.isTroopCarrier(theUnit, carriers) new carriers optional param
|
|
||||||
- better guards for getUnitAlt and getUnitAGL
|
|
||||||
- new newPointAtDegreesRange()
|
|
||||||
- new newPointAtAngleRange()
|
|
||||||
- new isTroopCarrierType()
|
|
||||||
- stringStartsWith now supports case insensitive match
|
|
||||||
- isTroopCarrier() supports 'any' and 'all'
|
|
||||||
- made getEnemyCoalitionFor() more resilient
|
|
||||||
- fix to smallRandom for negative numbers
|
|
||||||
- isTroopCarrierType uses wildArrayContainsString
|
|
||||||
2.8.3 - small optimizations in bearingFromAtoB()
|
|
||||||
- new whichSideOfMine()
|
|
||||||
2.8.4 - new rotatePointAroundOriginRad()
|
|
||||||
- new rotatePointAroundPointDeg()
|
|
||||||
- new rotatePointAroundPointRad()
|
|
||||||
- getClosestAirbaseTo() now supports passing list of air bases
|
|
||||||
2.8.5 - better guard in getGroupUnit()
|
|
||||||
2.8.6 - phonetic helpers
|
|
||||||
new spellString()
|
|
||||||
2.8.7 - new flareColor2Num()
|
|
||||||
- new flareColor2Text()
|
|
||||||
- new iteratePlayers()
|
|
||||||
2.8.8 - new hexString2RGBA()
|
|
||||||
- new playerName2Coalition()
|
|
||||||
- new coalition2Text()
|
|
||||||
2.8.9 - vAdd supports xy and xyz
|
|
||||||
- vSub supports xy and xyz
|
|
||||||
- vMultScalar supports xy and xyz
|
|
||||||
2.8.10 - tacan2freq now integrated with module (blush)
|
|
||||||
- array2string cosmetic default
|
|
||||||
- vMultScalar corrected bug in accessing b.z
|
|
||||||
- new randomLetter()
|
|
||||||
- new getPlayerUnit()
|
|
||||||
- new getMapName()
|
|
||||||
- new getMagDeclForPoint()
|
|
||||||
2.9.0 - createPoint() moved from cfxZones
|
|
||||||
- copyPoint() moved from cfxZones
|
|
||||||
- numberArrayFromString() moved from cfxZones
|
|
||||||
2.9.1 - new createSimpleRoutePointData()
|
|
||||||
- createOverheadAirdromeRoutPintData corrected and legacy support added
|
|
||||||
- new bearingFromAtoBusingXY()
|
|
||||||
- corrected verbosity for bearingFromAtoB
|
|
||||||
- new getCountriesForCoalition()
|
|
||||||
2.9.2 - updated event2text
|
|
||||||
2.9.3 - getAirbasesWhoseNameContains now supports category tables for filtering
|
|
||||||
2.9.4 - new bearing2degrees()
|
|
||||||
2.9.5 - distanceOfPointPToLineXZ(p, p1, p2)
|
|
||||||
2.9.6 - new addToTableIfNew()
|
|
||||||
2.9.7 - createSimpleRoutePointData also accepts speed
|
|
||||||
2.9.8 - isSceneryObject(theUnit) optimization, DCS 2.9 safe
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
-- dcsCommon is a library of common lua functions
|
-- dcsCommon is a library of common lua functions
|
||||||
@ -511,7 +341,6 @@ dcsCommon.version = "2.9.8"
|
|||||||
-- a table containing categories, e.g. {0, 2} = airfields and ships but not farps
|
-- a table containing categories, e.g. {0, 2} = airfields and ships but not farps
|
||||||
-- if no name given or aName = "*", then all bases are returned prior to filtering
|
-- if no name given or aName = "*", then all bases are returned prior to filtering
|
||||||
function dcsCommon.getAirbasesWhoseNameContains(aName, filterCat, filterCoalition)
|
function dcsCommon.getAirbasesWhoseNameContains(aName, filterCat, filterCoalition)
|
||||||
--trigger.action.outText("getAB(name): enter with " .. aName, 30)
|
|
||||||
if not aName then aName = "*" end
|
if not aName then aName = "*" end
|
||||||
local allYourBase = world.getAirbases() -- get em all
|
local allYourBase = world.getAirbases() -- get em all
|
||||||
local areBelongToUs = {}
|
local areBelongToUs = {}
|
||||||
@ -520,9 +349,6 @@ dcsCommon.version = "2.9.8"
|
|||||||
local airBaseName = aBase:getName() -- get display name
|
local airBaseName = aBase:getName() -- get display name
|
||||||
if aName == "*" or dcsCommon.containsString(airBaseName, aName) then
|
if aName == "*" or dcsCommon.containsString(airBaseName, aName) then
|
||||||
-- containsString is case insesitive unless told otherwise
|
-- containsString is case insesitive unless told otherwise
|
||||||
--if aName ~= "*" then
|
|
||||||
-- trigger.action.outText("getAB(name): matched " .. airBaseName, 30)
|
|
||||||
--end
|
|
||||||
local doAdd = true
|
local doAdd = true
|
||||||
if filterCat then
|
if filterCat then
|
||||||
local aCat = dcsCommon.getAirbaseCat(aBase)
|
local aCat = dcsCommon.getAirbaseCat(aBase)
|
||||||
@ -628,13 +454,11 @@ dcsCommon.version = "2.9.8"
|
|||||||
for idx, aSlot in pairs(reallyFree) do
|
for idx, aSlot in pairs(reallyFree) do
|
||||||
local sp = {x = aSlot.vTerminalPos.x, y = 0, z = aSlot.vTerminalPos.z}
|
local sp = {x = aSlot.vTerminalPos.x, y = 0, z = aSlot.vTerminalPos.z}
|
||||||
local currDist = dcsCommon.distFlat(p, sp)
|
local currDist = dcsCommon.distFlat(p, sp)
|
||||||
--trigger.action.outText("slot <" .. aSlot.Term_Index .. "> has dist " .. math.floor(currDist) .. " and _0 of <" .. aSlot.Term_Index_0 .. ">", 30)
|
|
||||||
if currDist < closestDist then
|
if currDist < closestDist then
|
||||||
closestSlot = aSlot
|
closestSlot = aSlot
|
||||||
closestDist = currDist
|
closestDist = currDist
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
--trigger.action.outText("slot <" .. closestSlot.Term_Index .. "> has closest dist <" .. math.floor(closestDist) .. ">", 30)
|
|
||||||
return closestSlot
|
return closestSlot
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -888,12 +712,9 @@ dcsCommon.version = "2.9.8"
|
|||||||
if not A then return "***error:A***" end
|
if not A then return "***error:A***" end
|
||||||
if not B then return "***error:B***" end
|
if not B then return "***error:B***" end
|
||||||
if not headingOfBInDegrees then headingOfBInDegrees = 0 end
|
if not headingOfBInDegrees then headingOfBInDegrees = 0 end
|
||||||
|
|
||||||
local bearing = dcsCommon.bearingInDegreesFromAtoB(B, A) -- returns 0..360
|
local bearing = dcsCommon.bearingInDegreesFromAtoB(B, A) -- returns 0..360
|
||||||
-- trigger.action.outText("+++comm: oclock - bearing = " .. bearing .. " and inHeading = " .. headingOfBInDegrees, 30)
|
|
||||||
bearing = bearing - headingOfBInDegrees
|
bearing = bearing - headingOfBInDegrees
|
||||||
return dcsCommon.getClockDirection(bearing)
|
return dcsCommon.getClockDirection(bearing)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- given a heading, return clock with 0 being 12, 180 being 6 etc.
|
-- given a heading, return clock with 0 being 12, 180 being 6 etc.
|
||||||
@ -1079,7 +900,6 @@ dcsCommon.version = "2.9.8"
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- if we get here, there was no live unit
|
-- if we get here, there was no live unit
|
||||||
--trigger.action.outText("+++cmn: A group has no live units. returning nil", 10)
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
end
|
end
|
||||||
@ -1107,7 +927,6 @@ dcsCommon.version = "2.9.8"
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- if we get here, there was no live unit
|
-- if we get here, there was no live unit
|
||||||
--trigger.action.outText("+++cmn A group has no live units. returning nil", 10)
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
end
|
end
|
||||||
@ -1233,7 +1052,6 @@ dcsCommon.version = "2.9.8"
|
|||||||
-- when filtering occurs in pre, an alternative 'rejected' handler can be called
|
-- when filtering occurs in pre, an alternative 'rejected' handler can be called
|
||||||
function dcsCommon.addEventHandler(f, pre, post, rejected) -- returns ID
|
function dcsCommon.addEventHandler(f, pre, post, rejected) -- returns ID
|
||||||
local handler = {} -- build a wrapper and connect the onEvent
|
local handler = {} -- build a wrapper and connect the onEvent
|
||||||
--dcsCommon.cbID = dcsCommon.cbID + 1 -- increment unique count
|
|
||||||
handler.id = dcsCommon.uuid("eventHandler")
|
handler.id = dcsCommon.uuid("eventHandler")
|
||||||
handler.f = f -- the callback itself
|
handler.f = f -- the callback itself
|
||||||
if (rejected) then handler.rejected = rejected end
|
if (rejected) then handler.rejected = rejected end
|
||||||
@ -1245,7 +1063,6 @@ dcsCommon.version = "2.9.8"
|
|||||||
function handler:onEvent(event)
|
function handler:onEvent(event)
|
||||||
if not self.pre(event) then
|
if not self.pre(event) then
|
||||||
if dcsCommon.verbose then
|
if dcsCommon.verbose then
|
||||||
-- trigger.action.outText("event " .. event.id .. " discarded by pre-processor", 10)
|
|
||||||
end
|
end
|
||||||
if (self.rejected) then self.rejected(event) end
|
if (self.rejected) then self.rejected(event) end
|
||||||
return
|
return
|
||||||
@ -1428,8 +1245,8 @@ dcsCommon.version = "2.9.8"
|
|||||||
rp.action = "Turning Point"
|
rp.action = "Turning Point"
|
||||||
rp.type = "Turning Point"
|
rp.type = "Turning Point"
|
||||||
if action then rp.action = action; rp.type = action end -- warning: may not be correct, need to verify later
|
if action then rp.action = action; rp.type = action end -- warning: may not be correct, need to verify later
|
||||||
rp.alt = altitudeInFeet * 0.3048
|
rp.alt = altitudeInFeet * 0.3048 -- in m
|
||||||
rp.speed = knots * 0.514444 -- we use
|
rp.speed = knots * 0.514444 -- we use m/s
|
||||||
rp.alt_type = "BARO"
|
rp.alt_type = "BARO"
|
||||||
if (altType) then rp.alt_type = altType end
|
if (altType) then rp.alt_type = altType end
|
||||||
return rp
|
return rp
|
||||||
@ -1485,7 +1302,7 @@ dcsCommon.version = "2.9.8"
|
|||||||
rp.action = "From Parking Area"
|
rp.action = "From Parking Area"
|
||||||
rp.type = "TakeOffParking"
|
rp.type = "TakeOffParking"
|
||||||
|
|
||||||
rp.speed = 100; -- in m/s? If so, that's 360 km/h
|
rp.speed = 100 -- that's 360 km/h
|
||||||
rp.alt_type = "BARO"
|
rp.alt_type = "BARO"
|
||||||
return rp
|
return rp
|
||||||
end
|
end
|
||||||
@ -1673,7 +1490,6 @@ dcsCommon.version = "2.9.8"
|
|||||||
|
|
||||||
-- now do the formation stuff
|
-- now do the formation stuff
|
||||||
-- make sure that they keep minimum distance
|
-- make sure that they keep minimum distance
|
||||||
-- trigger.action.outText("dcsCommon - processing formation " .. formation .. " with radius = " .. radius, 30)
|
|
||||||
if formation == "LINE_V" then
|
if formation == "LINE_V" then
|
||||||
-- top to bottom in zone (heding 0). -- will run through x-coordinate
|
-- top to bottom in zone (heding 0). -- will run through x-coordinate
|
||||||
-- use entire radius top to bottom
|
-- use entire radius top to bottom
|
||||||
@ -1682,7 +1498,6 @@ dcsCommon.version = "2.9.8"
|
|||||||
for i=1, num do
|
for i=1, num do
|
||||||
|
|
||||||
local u = theNewGroup.units[i]
|
local u = theNewGroup.units[i]
|
||||||
-- trigger.action.outText("formation unit " .. u.name .. " currX = " .. currX, 30)
|
|
||||||
u.x = currX
|
u.x = currX
|
||||||
currX = currX + increment
|
currX = currX + increment
|
||||||
end
|
end
|
||||||
@ -1698,7 +1513,6 @@ dcsCommon.version = "2.9.8"
|
|||||||
local increment = radius * 2/(num - 1) -- MUST NOT TRY WITH 1 UNIT!
|
local increment = radius * 2/(num - 1) -- MUST NOT TRY WITH 1 UNIT!
|
||||||
for i=1, num do
|
for i=1, num do
|
||||||
local u = theNewGroup.units[i]
|
local u = theNewGroup.units[i]
|
||||||
-- trigger.action.outText("formation unit " .. u.name .. " currX = " .. currY, 30)
|
|
||||||
u.y = currY
|
u.y = currY
|
||||||
currY = currY + increment
|
currY = currY + increment
|
||||||
end
|
end
|
||||||
@ -1713,7 +1527,6 @@ dcsCommon.version = "2.9.8"
|
|||||||
local incrementX = radius * 2/(num - 1) -- MUST NOT TRY WITH 1 UNIT!
|
local incrementX = radius * 2/(num - 1) -- MUST NOT TRY WITH 1 UNIT!
|
||||||
for i=1, num do
|
for i=1, num do
|
||||||
local u = theNewGroup.units[i]
|
local u = theNewGroup.units[i]
|
||||||
-- trigger.action.outText("formation unit " .. u.name .. " currX = " .. currX .. " currY = " .. currY, 30)
|
|
||||||
u.x = currX
|
u.x = currX
|
||||||
u.y = currY
|
u.y = currY
|
||||||
-- calc coords for NEXT iteration
|
-- calc coords for NEXT iteration
|
||||||
@ -1731,6 +1544,7 @@ dcsCommon.version = "2.9.8"
|
|||||||
elseif formation == "SCATTERED" or formation == "RANDOM" then
|
elseif formation == "SCATTERED" or formation == "RANDOM" then
|
||||||
-- use randomPointInCircle and tehn iterate over all vehicles for mindelta
|
-- use randomPointInCircle and tehn iterate over all vehicles for mindelta
|
||||||
processedUnits = {}
|
processedUnits = {}
|
||||||
|
if not minDist then minDist = 10 end
|
||||||
for i=1, num do
|
for i=1, num do
|
||||||
local emergencyBreak = 1 -- prevent endless loop
|
local emergencyBreak = 1 -- prevent endless loop
|
||||||
local lowDist = 10000
|
local lowDist = 10000
|
||||||
@ -1760,7 +1574,6 @@ dcsCommon.version = "2.9.8"
|
|||||||
|
|
||||||
elseif dcsCommon.stringStartsWith(formation, "CIRCLE") then
|
elseif dcsCommon.stringStartsWith(formation, "CIRCLE") then
|
||||||
-- units are arranged on perimeter of circle defined by radius
|
-- units are arranged on perimeter of circle defined by radius
|
||||||
-- trigger.action.outText("formation circle detected", 30)
|
|
||||||
local currAngle = 0
|
local currAngle = 0
|
||||||
local angleInc = 2 * 3.14157 / num -- increase per spoke
|
local angleInc = 2 * 3.14157 / num -- increase per spoke
|
||||||
for i=1, num do
|
for i=1, num do
|
||||||
@ -1786,40 +1599,7 @@ dcsCommon.version = "2.9.8"
|
|||||||
-- calculate w
|
-- calculate w
|
||||||
local w = math.floor(num^(0.5) + 0.5)
|
local w = math.floor(num^(0.5) + 0.5)
|
||||||
dcsCommon.arrangeGroupInNColumns(theNewGroup, w, radius)
|
dcsCommon.arrangeGroupInNColumns(theNewGroup, w, radius)
|
||||||
--[[--
|
|
||||||
local h = math.floor(num / w)
|
|
||||||
--trigger.action.outText("AdcsC: num=" .. num .. " w=" .. w .. "h=" .. h .. " -- num%w=" .. num%w, 30)
|
|
||||||
if (num % w) > 0 then
|
|
||||||
h = h + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
--trigger.action.outText("BdcsC: num=" .. num .. " w=" .. w .. "h=" .. h, 30)
|
|
||||||
|
|
||||||
-- now w * h always >= num and num items fir in that grid
|
|
||||||
-- w is width, h is height, of course :)
|
|
||||||
-- now calculat xInc and yInc
|
|
||||||
local i = 1
|
|
||||||
local xInc = 0
|
|
||||||
if w > 1 then xInc = 2 * radius / (w-1) end
|
|
||||||
local yInc = 0
|
|
||||||
if h > 1 then yInc = 2 * radius / (h-1) end
|
|
||||||
local currY = radius
|
|
||||||
if h < 2 then currY = 0 end -- special:_ place in Y middle if only one row)
|
|
||||||
while h > 0 do
|
|
||||||
local currX = radius
|
|
||||||
local wCnt = w
|
|
||||||
while wCnt > 0 and (i <= num) do
|
|
||||||
local u = theNewGroup.units[i] -- get unit
|
|
||||||
u.x = currX
|
|
||||||
u.y = currY
|
|
||||||
currX = currX - xInc
|
|
||||||
wCnt = wCnt - 1
|
|
||||||
i = i + 1
|
|
||||||
end
|
|
||||||
currY = currY - yInc
|
|
||||||
h = h - 1
|
|
||||||
end
|
|
||||||
--]]--
|
|
||||||
elseif formation == "2DEEP" or formation == "2COLS" then
|
elseif formation == "2DEEP" or formation == "2COLS" then
|
||||||
if num < 2 then return end
|
if num < 2 then return end
|
||||||
-- arrange units in an 2 x h grid
|
-- arrange units in an 2 x h grid
|
||||||
@ -1896,11 +1676,14 @@ dcsCommon.version = "2.9.8"
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function dcsCommon.createGroundGroupWithUnits(name, theUnitTypes, radius, minDist, formation, innerRadius)
|
function dcsCommon.createGroundGroupWithUnits(name, theUnitTypes, radius, minDist, formation, innerRadius, liveries)
|
||||||
|
-- liveries is indexed by typeName and provides alternate livery names
|
||||||
|
-- from default.
|
||||||
if not minDist then minDist = 4 end -- meters
|
if not minDist then minDist = 4 end -- meters
|
||||||
if not formation then formation = "line" end
|
if not formation then formation = "line" end
|
||||||
if not radius then radius = 30 end -- meters
|
if not radius then radius = 30 end -- meters
|
||||||
if not innerRadius then innerRadius = 0 end
|
if not innerRadius then innerRadius = 0 end
|
||||||
|
if not liveries then liveries = {} end
|
||||||
formation = formation:upper()
|
formation = formation:upper()
|
||||||
-- theUnitTypes can be either a single string or a table of strings
|
-- theUnitTypes can be either a single string or a table of strings
|
||||||
-- see here for TypeName https://github.com/mrSkortch/DCS-miscScripts/tree/master/ObjectDB
|
-- see here for TypeName https://github.com/mrSkortch/DCS-miscScripts/tree/master/ObjectDB
|
||||||
@ -1920,13 +1703,8 @@ dcsCommon.version = "2.9.8"
|
|||||||
|
|
||||||
-- now add a single unit or multiple units
|
-- now add a single unit or multiple units
|
||||||
if type(theUnitTypes) ~= "table" then
|
if type(theUnitTypes) ~= "table" then
|
||||||
-- trigger.action.outText("dcsCommon - i am here", 30)
|
|
||||||
-- trigger.action.outText("dcsCommon - name " .. name, 30)
|
|
||||||
-- trigger.action.outText("dcsCommon - unit type " .. theUnitTypes, 30)
|
|
||||||
|
|
||||||
local aUnit = {}
|
local aUnit = {}
|
||||||
aUnit = dcsCommon.createGroundUnitData(name .. "-1", theUnitTypes, false)
|
aUnit = dcsCommon.createGroundUnitData(name .. "-1", theUnitTypes, false)
|
||||||
-- trigger.action.outText("dcsCommon - unit name retval " .. aUnit.name, 30)
|
|
||||||
dcsCommon.addUnitToGroupData(aUnit, theNewGroup, 0, 0) -- create with data at location (0,0)
|
dcsCommon.addUnitToGroupData(aUnit, theNewGroup, 0, 0) -- create with data at location (0,0)
|
||||||
return theNewGroup
|
return theNewGroup
|
||||||
end
|
end
|
||||||
@ -1937,6 +1715,10 @@ dcsCommon.version = "2.9.8"
|
|||||||
for key, theType in pairs(theUnitTypes) do
|
for key, theType in pairs(theUnitTypes) do
|
||||||
-- trigger.action.outText("+++dcsC: creating unit " .. name .. "-" .. num .. ": " .. theType, 30)
|
-- trigger.action.outText("+++dcsC: creating unit " .. name .. "-" .. num .. ": " .. theType, 30)
|
||||||
local aUnit = dcsCommon.createGroundUnitData(name .. "-"..num, theType, false)
|
local aUnit = dcsCommon.createGroundUnitData(name .. "-"..num, theType, false)
|
||||||
|
local theLivery = liveries[theType]
|
||||||
|
if theLivery then
|
||||||
|
aUnit.livery_id = theLivery
|
||||||
|
end
|
||||||
dcsCommon.addUnitToGroupData(aUnit, theNewGroup, 0, 0)
|
dcsCommon.addUnitToGroupData(aUnit, theNewGroup, 0, 0)
|
||||||
num = num + 1
|
num = num + 1
|
||||||
end
|
end
|
||||||
@ -1991,15 +1773,23 @@ dcsCommon.version = "2.9.8"
|
|||||||
elseif cat == Group.Category.GROUND then
|
elseif cat == Group.Category.GROUND then
|
||||||
-- we got all we need
|
-- we got all we need
|
||||||
else
|
else
|
||||||
-- trigger.action.outText("dcsCommon - unknown category: " .. cat, 30)
|
|
||||||
-- return nil
|
|
||||||
-- we also got all we need
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function dcsCommon.pointInDirectionOfPointXYY(dir, dist, p) -- dir in rad, p in XYZ returns XYY
|
||||||
|
local fx = math.cos(dir)
|
||||||
|
local fy = math.sin(dir)
|
||||||
|
local p2 = {}
|
||||||
|
p2.x = p.x + dist * fx
|
||||||
|
p2.y = p.z + dist * fy
|
||||||
|
p2.z = p2.y -- make p2 XYY vec2/3 upcast
|
||||||
|
return p2
|
||||||
|
end
|
||||||
|
|
||||||
function dcsCommon.rotatePointAroundOriginRad(inX, inY, angle) -- angle in degrees
|
function dcsCommon.rotatePointAroundOriginRad(inX, inY, angle) -- angle in degrees
|
||||||
local c = math.cos(angle)
|
local c = math.cos(angle)
|
||||||
local s = math.sin(angle)
|
local s = math.sin(angle)
|
||||||
@ -2046,7 +1836,6 @@ dcsCommon.version = "2.9.8"
|
|||||||
if not cx then cx = 0 end
|
if not cx then cx = 0 end
|
||||||
if not cz then cz = 0 end
|
if not cz then cz = 0 end
|
||||||
local cy = cz
|
local cy = cz
|
||||||
--trigger.action.outText("+++dcsC:rotGrp cy,cy = "..cx .. "," .. cy, 30)
|
|
||||||
|
|
||||||
local rads = degrees * 3.14152 / 180
|
local rads = degrees * 3.14152 / 180
|
||||||
do
|
do
|
||||||
@ -2066,7 +1855,6 @@ dcsCommon.version = "2.9.8"
|
|||||||
if not cx then cx = 0 end
|
if not cx then cx = 0 end
|
||||||
if not cz then cz = 0 end
|
if not cz then cz = 0 end
|
||||||
local cy = cz
|
local cy = cz
|
||||||
--trigger.action.outText("+++dcsC:rotGrp cy,cy = "..cx .. "," .. cy, 30)
|
|
||||||
|
|
||||||
local rads = degrees * 3.14152 / 180
|
local rads = degrees * 3.14152 / 180
|
||||||
-- turns all units in group around the group's center by degrees.
|
-- turns all units in group around the group's center by degrees.
|
||||||
@ -2080,9 +1868,6 @@ dcsCommon.version = "2.9.8"
|
|||||||
|
|
||||||
-- may also want to increase heading by degrees
|
-- may also want to increase heading by degrees
|
||||||
theUnit.heading = theUnit.heading + rads
|
theUnit.heading = theUnit.heading + rads
|
||||||
-- now kill psi if it existed before
|
|
||||||
-- theUnit.psi = nil
|
|
||||||
-- better code: psi is always -heading. Nobody knows what psi is, though
|
|
||||||
if theUnit.psi then
|
if theUnit.psi then
|
||||||
theUnit.psi = -theUnit.heading
|
theUnit.psi = -theUnit.heading
|
||||||
end
|
end
|
||||||
@ -2247,33 +2032,27 @@ end
|
|||||||
end
|
end
|
||||||
if not caseSensitive then theString = string.upper(theString) end
|
if not caseSensitive then theString = string.upper(theString) end
|
||||||
|
|
||||||
--trigger.action.outText("wildACS: theString = <" .. theString .. ">, theArray contains <" .. #theArray .. "> elements", 30)
|
|
||||||
local wildIn = dcsCommon.stringEndsWith(theString, "*")
|
local wildIn = dcsCommon.stringEndsWith(theString, "*")
|
||||||
if wildIn then dcsCommon.removeEnding(theString, "*") end
|
if wildIn then dcsCommon.removeEnding(theString, "*") end
|
||||||
for idx, theElement in pairs(theArray) do -- i = 1, #theArray do
|
for idx, theElement in pairs(theArray) do -- i = 1, #theArray do
|
||||||
--local theElement = theArray[i]
|
|
||||||
--trigger.action.outText("test e <" .. theElement .. "> against s <" .. theString .. ">", 30)
|
|
||||||
if not caseSensitive then theElement = string.upper(theElement) end
|
if not caseSensitive then theElement = string.upper(theElement) end
|
||||||
local wildEle = dcsCommon.stringEndsWith(theElement, "*")
|
local wildEle = dcsCommon.stringEndsWith(theElement, "*")
|
||||||
if wildEle then theElement = dcsCommon.removeEnding(theElement, "*") end
|
if wildEle then theElement = dcsCommon.removeEnding(theElement, "*") end
|
||||||
--trigger.action.outText("matching s=<" .. theString .. "> with e=<" .. theElement .. ">", 30)
|
|
||||||
if wildEle and wildIn then
|
if wildEle and wildIn then
|
||||||
-- both end on wildcards, partial match for both
|
-- both end on wildcards, partial match for both
|
||||||
if dcsCommon.stringStartsWith(theElement, theString) then return true end
|
if dcsCommon.stringStartsWith(theElement, theString) then return true end
|
||||||
if dcsCommon.stringStartsWith(theString, theElement) then return true end
|
if dcsCommon.stringStartsWith(theString, theElement) then return true end
|
||||||
--trigger.action.outText("match e* with s* failed.", 30)
|
|
||||||
elseif wildEle then
|
elseif wildEle then
|
||||||
-- Element is a wildcard, partial match
|
-- Element is a wildcard, partial match
|
||||||
if dcsCommon.stringStartsWith(theString, theElement) then return true end
|
if dcsCommon.stringStartsWith(theString, theElement) then return true end
|
||||||
--trigger.action.outText("startswith - match e* <" .. theElement .. "> with s <" .. theString .. "> failed.", 30)
|
|
||||||
elseif wildIn then
|
elseif wildIn then
|
||||||
-- theString is a wildcard. partial match
|
-- theString is a wildcard. partial match
|
||||||
if dcsCommon.stringStartsWith(theElement, theString) then return true end
|
if dcsCommon.stringStartsWith(theElement, theString) then return true end
|
||||||
--trigger.action.outText("match e with s* failed.", 30)
|
|
||||||
else
|
else
|
||||||
-- standard: no wildcards, full match
|
-- standard: no wildcards, full match
|
||||||
if theElement == theString then return true end
|
if theElement == theString then return true end
|
||||||
--trigger.action.outText("match e with s (straight) failed.", 30)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@ -2412,7 +2191,7 @@ end
|
|||||||
|
|
||||||
if caseInsensitive then
|
if caseInsensitive then
|
||||||
theString = string.upper(theString)
|
theString = string.upper(theString)
|
||||||
thePrefix = string.upper(theString)
|
thePrefix = string.upper(thePrefix)
|
||||||
end
|
end
|
||||||
-- superseded: string.find (s, pattern [, init [, plain]]) solves the problem
|
-- superseded: string.find (s, pattern [, init [, plain]]) solves the problem
|
||||||
local i, j = string.find(theString, thePrefix, 1, true)
|
local i, j = string.find(theString, thePrefix, 1, true)
|
||||||
@ -2467,12 +2246,19 @@ end
|
|||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
function dcsCommon.point2text(p)
|
function dcsCommon.point2text(p, intsOnly)
|
||||||
|
if not intsOnly then intsOnly = false end
|
||||||
if not p then return "<!NIL!>" end
|
if not p then return "<!NIL!>" end
|
||||||
local t = "[x="
|
local t = "[x="
|
||||||
|
if intsOnly then
|
||||||
|
if p.x then t = t .. math.floor(p.x) .. ", " else t = t .. "<nil>, " end
|
||||||
|
if p.y then t = t .. "y=" .. math.floor(p.y) .. ", " else t = t .. "y=<nil>, " end
|
||||||
|
if p.z then t = t .. "z=" .. math.floor(p.z) .. "]" else t = t .. "z=<nil>]" end
|
||||||
|
else
|
||||||
if p.x then t = t .. p.x .. ", " else t = t .. "<nil>, " end
|
if p.x then t = t .. p.x .. ", " else t = t .. "<nil>, " end
|
||||||
if p.y then t = t .. "y=" .. p.y .. ", " else t = t .. "y=<nil>, " end
|
if p.y then t = t .. "y=" .. p.y .. ", " else t = t .. "y=<nil>, " end
|
||||||
if p.z then t = t .. "z=" .. p.z .. "]" else t = t .. "z=<nil>]" end
|
if p.z then t = t .. "z=" .. p.z .. "]" else t = t .. "z=<nil>]" end
|
||||||
|
end
|
||||||
return t
|
return t
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -2605,7 +2391,6 @@ end
|
|||||||
if not inrecursion then
|
if not inrecursion then
|
||||||
-- output a marker to find in the log / screen
|
-- output a marker to find in the log / screen
|
||||||
trigger.action.outText("=== dcsCommon vardump end", 30)
|
trigger.action.outText("=== dcsCommon vardump end", 30)
|
||||||
--env.info("=== dcsCommon vardump end")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -2636,7 +2421,9 @@ end
|
|||||||
"BDA", "AI Abort Mission", "DayNight", "Flight Time", -- 40
|
"BDA", "AI Abort Mission", "DayNight", "Flight Time", -- 40
|
||||||
"Pilot Suicide", "player cap airfield", "emergency landing", "unit create task", -- 44
|
"Pilot Suicide", "player cap airfield", "emergency landing", "unit create task", -- 44
|
||||||
"unit delete task", "Simulation start", "weapon rearm", "weapon drop", -- 48
|
"unit delete task", "Simulation start", "weapon rearm", "weapon drop", -- 48
|
||||||
"unit task timeout", "unit task stage",
|
"unit task timeout", "unit task stage", -- 50
|
||||||
|
"subtask score", "extra score", "mission restart", "winner",
|
||||||
|
"postponed takeoff", "postponed land", -- 56
|
||||||
"max"}
|
"max"}
|
||||||
if id > #events then return "Unknown (ID=" .. id .. ")" end
|
if id > #events then return "Unknown (ID=" .. id .. ")" end
|
||||||
return events[id]
|
return events[id]
|
||||||
@ -2926,6 +2713,21 @@ function dcsCommon.getAllExistingPlayerUnitsRaw()
|
|||||||
return apu
|
return apu
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function dcsCommon.getAllExistingPlayersAndUnits() -- units indexed by player name
|
||||||
|
-- designed to replace cases for cfxPlayer.getAllPlayer invocations
|
||||||
|
local apu = {}
|
||||||
|
for idx, theSide in pairs(dcsCommon.coalitionSides) do
|
||||||
|
local thePlayers = coalition.getPlayers(theSide)
|
||||||
|
for idy, theUnit in pairs (thePlayers) do
|
||||||
|
if theUnit and theUnit:isExist() then
|
||||||
|
local pName = theUnit:getPlayerName()
|
||||||
|
apu[pName] = theUnit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return apu
|
||||||
|
end
|
||||||
|
|
||||||
function dcsCommon.getUnitAlt(theUnit)
|
function dcsCommon.getUnitAlt(theUnit)
|
||||||
if not theUnit then return 0 end
|
if not theUnit then return 0 end
|
||||||
if not Unit.isExist(theUnit) then return 0 end -- safer
|
if not Unit.isExist(theUnit) then return 0 end -- safer
|
||||||
@ -3028,16 +2830,6 @@ function dcsCommon.unitIsInfantry(theUnit)
|
|||||||
if not theUnit then return false end
|
if not theUnit then return false end
|
||||||
if not theUnit:isExist() then return end
|
if not theUnit:isExist() then return end
|
||||||
local theType = theUnit:getTypeName()
|
local theType = theUnit:getTypeName()
|
||||||
--[[--
|
|
||||||
local isInfantry =
|
|
||||||
dcsCommon.containsString(theType, "infantry", false) or
|
|
||||||
dcsCommon.containsString(theType, "paratrooper", false) or
|
|
||||||
dcsCommon.containsString(theType, "stinger", false) or
|
|
||||||
dcsCommon.containsString(theType, "manpad", false) or
|
|
||||||
dcsCommon.containsString(theType, "soldier", false) or
|
|
||||||
dcsCommon.containsString(theType, "SA-18 Igla", false)
|
|
||||||
return isInfantry
|
|
||||||
--]]--
|
|
||||||
return dcsCommon.typeIsInfantry(theType)
|
return dcsCommon.typeIsInfantry(theType)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
delayFlag = {}
|
delayFlag = {}
|
||||||
delayFlag.version = "1.4.0"
|
delayFlag.version = "2.0.0"
|
||||||
delayFlag.verbose = false
|
delayFlag.verbose = false
|
||||||
delayFlag.requiredLibs = {
|
delayFlag.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
@ -11,35 +11,12 @@ delayFlag.flags = {}
|
|||||||
delay flags - simple flag switch & delay, allows for randomize
|
delay flags - simple flag switch & delay, allows for randomize
|
||||||
and dead man switching
|
and dead man switching
|
||||||
|
|
||||||
Copyright (c) 2022 by Christian Franz and cf/x AG
|
Copyright (c) 2022-2024 by Christian Franz and cf/x AG
|
||||||
|
|
||||||
Version History
|
Version History
|
||||||
1.0.0 - Initial Version
|
|
||||||
1.0.1 - message attribute
|
|
||||||
1.0.2 - slight spelling correction
|
|
||||||
- using cfxZones for polling
|
|
||||||
- removed pollFlag
|
|
||||||
1.0.3 - bug fix for config zone name
|
|
||||||
- removed message attribute, moved to own module
|
|
||||||
- triggerFlag --> triggerDelayFlag
|
|
||||||
1.0.4 - startDelay
|
|
||||||
1.1.0 - DML flag upgrade
|
|
||||||
- removed onStart. use local raiseFlag instead
|
|
||||||
- delayDone! synonym
|
|
||||||
- pauseDelay?
|
|
||||||
- unpauseDelay?
|
|
||||||
1.2.0 - Watchflags
|
|
||||||
1.2.1 - method goes to dlyMethod
|
|
||||||
- delay done is correctly inited
|
|
||||||
1.2.2 - delayMethod defaults to inc
|
|
||||||
- zone-local verbosity
|
|
||||||
- code clean-up
|
|
||||||
1.2.3 - pauseDelay
|
|
||||||
- continueDelay
|
|
||||||
- delayLeft
|
|
||||||
1.3.0 - persistence
|
|
||||||
1.4.0 - dmlZones
|
1.4.0 - dmlZones
|
||||||
- delayLeft#
|
- delayLeft#
|
||||||
|
2.0.0 - clean-up
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
@ -317,7 +294,7 @@ function delayFlag.readConfigZone()
|
|||||||
theZone = cfxZones.createSimpleZone("delayFlagsConfig")
|
theZone = cfxZones.createSimpleZone("delayFlagsConfig")
|
||||||
end
|
end
|
||||||
|
|
||||||
delayFlag.verbose = theZone.verbose -- cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
delayFlag.verbose = theZone.verbose
|
||||||
|
|
||||||
if delayFlag.verbose then
|
if delayFlag.verbose then
|
||||||
trigger.action.outText("+++dlyF: read config", 30)
|
trigger.action.outText("+++dlyF: read config", 30)
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
factoryZone = {}
|
factoryZone = {}
|
||||||
factoryZone.version = "2.0.0"
|
factoryZone.version = "3.0.0"
|
||||||
factoryZone.verbose = false
|
factoryZone.verbose = false
|
||||||
factoryZone.name = "factoryZone"
|
factoryZone.name = "factoryZone"
|
||||||
|
|
||||||
@ -9,6 +9,8 @@ factoryZone.name = "factoryZone"
|
|||||||
- "production" and "defenders" simplification
|
- "production" and "defenders" simplification
|
||||||
- now optional specification for red/blue
|
- now optional specification for red/blue
|
||||||
- use maxRadius from zone for spawning to support quad zones
|
- use maxRadius from zone for spawning to support quad zones
|
||||||
|
3.0.0 - support for liveries via "factoryLiveries" zone
|
||||||
|
- OOP dmlZones
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
factoryZone.requiredLibs = {
|
factoryZone.requiredLibs = {
|
||||||
@ -20,6 +22,7 @@ factoryZone.requiredLibs = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
factoryZone.zones = {} -- my factory zones
|
factoryZone.zones = {} -- my factory zones
|
||||||
|
factoryZone.liveries = {} -- indexed by type name
|
||||||
factoryZone.ups = 1
|
factoryZone.ups = 1
|
||||||
factoryZone.initialized = false
|
factoryZone.initialized = false
|
||||||
factoryZone.defendingTime = 100 -- seconds until new defenders are produced
|
factoryZone.defendingTime = 100 -- seconds until new defenders are produced
|
||||||
@ -28,7 +31,7 @@ factoryZone.shockTime = 200 -- 'shocked' period of inactivity
|
|||||||
factoryZone.repairTime = 200 -- time until we raplace one lost unit, also repairs all other units to 100%
|
factoryZone.repairTime = 200 -- time until we raplace one lost unit, also repairs all other units to 100%
|
||||||
|
|
||||||
-- persistence: all attackers we ever sent out.
|
-- persistence: all attackers we ever sent out.
|
||||||
-- is regularly verified and cut to size
|
-- is regularly verified and cut to size via GC
|
||||||
factoryZone.spawnedAttackers = {}
|
factoryZone.spawnedAttackers = {}
|
||||||
|
|
||||||
-- factoryZone is a module that manages production of units
|
-- factoryZone is a module that manages production of units
|
||||||
@ -38,24 +41,6 @@ factoryZone.spawnedAttackers = {}
|
|||||||
--
|
--
|
||||||
-- *** EXTENTDS ZONES ***
|
-- *** EXTENTDS ZONES ***
|
||||||
--
|
--
|
||||||
-- zone attributes when owned
|
|
||||||
-- owner: coalition that owns the zone. Managed externally
|
|
||||||
-- status: FSM for spawning
|
|
||||||
-- defendersRED/BLUE - coma separated type string for the group to spawn on defense cycle completion
|
|
||||||
-- attackersRED/BLUE - as above for attack cycle.
|
|
||||||
-- timeStamp - time when zone switched into current state
|
|
||||||
-- spawnRadius - overrides zone's radius when placing defenders. can be use to place defenders inside or outside zone itself
|
|
||||||
-- formation - defender's formation
|
|
||||||
-- attackFormation - attackers formation
|
|
||||||
-- attackRadius - radius of circle in which attackers are spawned. informs formation
|
|
||||||
-- attackDelta - polar coord: r from zone center where attackers are spawned
|
|
||||||
-- attackPhi - polar degrees where attackers are to be spawned
|
|
||||||
-- paused - will not spawn. default is false
|
|
||||||
|
|
||||||
--
|
|
||||||
-- M I S C
|
|
||||||
--
|
|
||||||
|
|
||||||
|
|
||||||
function factoryZone.getFactoryZoneByName(zName)
|
function factoryZone.getFactoryZoneByName(zName)
|
||||||
for zKey, theZone in pairs (factoryZone.zones) do
|
for zKey, theZone in pairs (factoryZone.zones) do
|
||||||
@ -65,59 +50,58 @@ function factoryZone.getFactoryZoneByName(zName)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function factoryZone.addFactoryZone(aZone)
|
function factoryZone.addFactoryZone(aZone)
|
||||||
--aZone.worksFor = cfxZones.getCoalitionFromZoneProperty(aZone, "factory", 0) -- currently unused, have RED/BLUE separate types
|
|
||||||
aZone.state = "init"
|
aZone.state = "init"
|
||||||
aZone.timeStamp = timer.getTime()
|
aZone.timeStamp = timer.getTime()
|
||||||
|
|
||||||
-- set up production default
|
-- set up production default
|
||||||
local factory = cfxZones.getStringFromZoneProperty(aZone, "factory", "none")
|
local factory = aZone:getStringFromZoneProperty("factory", "none")
|
||||||
|
|
||||||
local production = cfxZones.getStringFromZoneProperty(aZone, "production", factory)
|
local production = aZone:getStringFromZoneProperty("production", factory)
|
||||||
|
|
||||||
local defenders = cfxZones.getStringFromZoneProperty(aZone, "defenders", factory)
|
local defenders = aZone:getStringFromZoneProperty("defenders", factory)
|
||||||
|
|
||||||
if cfxZones.hasProperty(aZone, "attackersRED") then
|
if aZone:hasProperty("attackersRED") then
|
||||||
-- legacy support
|
-- legacy support
|
||||||
aZone.attackersRED = cfxZones.getStringFromZoneProperty(aZone, "attackersRED", production)
|
aZone.attackersRED = aZone:getStringFromZoneProperty( "attackersRED", production)
|
||||||
else
|
else
|
||||||
aZone.attackersRED = cfxZones.getStringFromZoneProperty(aZone, "productionRED", production)
|
aZone.attackersRED = aZone:getStringFromZoneProperty( "productionRED", production)
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(aZone, "attackersBLUE") then
|
if aZone:hasProperty("attackersBLUE") then
|
||||||
-- legacy support
|
-- legacy support
|
||||||
aZone.attackersBLUE = cfxZones.getStringFromZoneProperty(aZone, "attackersBLUE", production)
|
aZone.attackersBLUE = aZone:getStringFromZoneProperty( "attackersBLUE", production)
|
||||||
else
|
else
|
||||||
aZone.attackersBLUE = cfxZones.getStringFromZoneProperty(aZone, "productionBLUE", production)
|
aZone.attackersBLUE = aZone:getStringFromZoneProperty( "productionBLUE", production)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- set up defenders default, or use production / factory
|
-- set up defenders default, or use production / factory
|
||||||
aZone.defendersRED = cfxZones.getStringFromZoneProperty(aZone, "defendersRED", defenders)
|
aZone.defendersRED = aZone:getStringFromZoneProperty("defendersRED", defenders)
|
||||||
aZone.defendersBLUE = cfxZones.getStringFromZoneProperty(aZone, "defendersBLUE", defenders)
|
aZone.defendersBLUE = aZone:getStringFromZoneProperty("defendersBLUE", defenders)
|
||||||
|
|
||||||
aZone.formation = cfxZones.getStringFromZoneProperty(aZone, "formation", "circle_out")
|
aZone.formation = aZone:getStringFromZoneProperty("formation", "circle_out")
|
||||||
aZone.attackFormation = cfxZones.getStringFromZoneProperty(aZone, "attackFormation", "circle_out") -- cfxZones.getZoneProperty(aZone, "attackFormation")
|
aZone.attackFormation = aZone:getStringFromZoneProperty( "attackFormation", "circle_out") -- cfxZones.getZoneProperty(aZone, "attackFormation")
|
||||||
aZone.spawnRadius = cfxZones.getNumberFromZoneProperty(aZone, "spawnRadius", aZone.maxRadius-5) -- "-5" so they remaininside radius
|
aZone.spawnRadius = aZone:getNumberFromZoneProperty("spawnRadius", aZone.maxRadius-5) -- "-5" so they remaininside radius
|
||||||
aZone.attackRadius = cfxZones.getNumberFromZoneProperty(aZone, "attackRadius", aZone.maxRadius)
|
aZone.attackRadius = aZone:getNumberFromZoneProperty("attackRadius", aZone.maxRadius)
|
||||||
aZone.attackDelta = cfxZones.getNumberFromZoneProperty(aZone, "attackDelta", 10) -- aZone.radius)
|
aZone.attackDelta = aZone:getNumberFromZoneProperty("attackDelta", 10) -- aZone.radius)
|
||||||
aZone.attackPhi = cfxZones.getNumberFromZoneProperty(aZone, "attackPhi", 0)
|
aZone.attackPhi = aZone:getNumberFromZoneProperty("attackPhi", 0)
|
||||||
|
|
||||||
aZone.paused = cfxZones.getBoolFromZoneProperty(aZone, "paused", false)
|
aZone.paused = aZone:getBoolFromZoneProperty("paused", false)
|
||||||
aZone.factoryOwner = aZone.owner -- copy so we can compare next round
|
aZone.factoryOwner = aZone.owner -- copy so we can compare next round
|
||||||
|
|
||||||
-- pause? and activate?
|
-- pause? and activate?
|
||||||
if cfxZones.hasProperty(aZone, "pause?") then
|
if aZone:hasProperty("pause?") then
|
||||||
aZone.pauseFlag = cfxZones.getStringFromZoneProperty(aZone, "pause?", "none")
|
aZone.pauseFlag = aZone:getStringFromZoneProperty("pause?", "none")
|
||||||
aZone.lastPauseValue = trigger.misc.getUserFlag(aZone.pauseFlag)
|
aZone.lastPauseValue = trigger.misc.getUserFlag(aZone.pauseFlag)
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(aZone, "activate?") then
|
if aZone:hasProperty("activate?") then
|
||||||
aZone.activateFlag = cfxZones.getStringFromZoneProperty(aZone, "activate?", "none")
|
aZone.activateFlag = aZone:getStringFromZoneProperty("activate?", "none")
|
||||||
aZone.lastActivateValue = trigger.misc.getUserFlag(aZone.activateFlag)
|
aZone.lastActivateValue = trigger.misc.getUserFlag(aZone.activateFlag)
|
||||||
end
|
end
|
||||||
|
|
||||||
aZone.factoryTriggerMethod = cfxZones.getStringFromZoneProperty(aZone, "triggerMethod", "change")
|
aZone.factoryTriggerMethod = aZone:getStringFromZoneProperty( "triggerMethod", "change")
|
||||||
if cfxZones.hasProperty(aZone, "factoryTriggerMethod") then
|
if aZone:hasProperty("factoryTriggerMethod") then
|
||||||
aZone.factoryTriggerMethod = cfxZones.getStringFromZoneProperty(aZone, "factoryTriggerMethod", "change")
|
aZone.factoryTriggerMethod = aZone:getStringFromZoneProperty( "factoryTriggerMethod", "change")
|
||||||
end
|
end
|
||||||
|
|
||||||
factoryZone.zones[aZone.name] = aZone
|
factoryZone.zones[aZone.name] = aZone
|
||||||
@ -159,11 +143,12 @@ function factoryZone.spawnAttackTroops(theTypes, aZone, aCoalition, aFormation)
|
|||||||
|
|
||||||
local theGroup, theData = cfxZones.createGroundUnitsInZoneForCoalition (
|
local theGroup, theData = cfxZones.createGroundUnitsInZoneForCoalition (
|
||||||
aCoalition, -- theCountry,
|
aCoalition, -- theCountry,
|
||||||
aZone.name .. " (A) " .. dcsCommon.numberUUID(), -- must be unique
|
aZone.name .. " (A) " .. dcsCommon.numberUUID(),
|
||||||
spawnZone,
|
spawnZone,
|
||||||
unitTypes,
|
unitTypes,
|
||||||
aFormation, -- outward facing
|
aFormation, -- outward facing
|
||||||
0)
|
0,
|
||||||
|
factoryZone.liveries)
|
||||||
return theGroup, theData
|
return theGroup, theData
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -184,10 +169,12 @@ function factoryZone.spawnDefensiveTroops(theTypes, aZone, aCoalition, aFormatio
|
|||||||
local spawnZone = cfxZones.createSimpleZone("spawnZone", aZone.point, aZone.spawnRadius)
|
local spawnZone = cfxZones.createSimpleZone("spawnZone", aZone.point, aZone.spawnRadius)
|
||||||
local theGroup, theData = cfxZones.createGroundUnitsInZoneForCoalition (
|
local theGroup, theData = cfxZones.createGroundUnitsInZoneForCoalition (
|
||||||
aCoalition, --theCountry,
|
aCoalition, --theCountry,
|
||||||
aZone.name .. " (D) " .. dcsCommon.numberUUID(), -- must be unique
|
aZone.name .. " (D) " .. dcsCommon.numberUUID(),
|
||||||
spawnZone, unitTypes,
|
spawnZone,
|
||||||
|
unitTypes,
|
||||||
aFormation, -- outward facing
|
aFormation, -- outward facing
|
||||||
0)
|
0,
|
||||||
|
factoryZone.liveries)
|
||||||
return theGroup, theData
|
return theGroup, theData
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -308,7 +295,8 @@ function factoryZone.repairDefenders(aZone)
|
|||||||
livingTypes,
|
livingTypes,
|
||||||
|
|
||||||
aZone.formation, -- outward facing
|
aZone.formation, -- outward facing
|
||||||
0)
|
0,
|
||||||
|
factoryZone.liveries)
|
||||||
aZone.defenders = theGroup
|
aZone.defenders = theGroup
|
||||||
aZone.lastDefenders = theGroup:getSize()
|
aZone.lastDefenders = theGroup:getSize()
|
||||||
end
|
end
|
||||||
@ -686,15 +674,26 @@ end
|
|||||||
function factoryZone.readConfigZone(theZone)
|
function factoryZone.readConfigZone(theZone)
|
||||||
if not theZone then theZone = cfxZones.createSimpleZone("factoryZoneConfig") end
|
if not theZone then theZone = cfxZones.createSimpleZone("factoryZoneConfig") end
|
||||||
factoryZone.name = "factoryZone" -- just in case, so we can access with cfxZones
|
factoryZone.name = "factoryZone" -- just in case, so we can access with cfxZones
|
||||||
factoryZone.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
factoryZone.verbose = theZone.verbose
|
||||||
factoryZone.defendingTime = cfxZones.getNumberFromZoneProperty(theZone, "defendingTime", 100)
|
factoryZone.defendingTime = theZone:getNumberFromZoneProperty( "defendingTime", 100)
|
||||||
factoryZone.attackingTime = cfxZones.getNumberFromZoneProperty(theZone, "attackingTime", 300)
|
factoryZone.attackingTime = theZone:getNumberFromZoneProperty( "attackingTime", 300)
|
||||||
factoryZone.shockTime = cfxZones.getNumberFromZoneProperty(theZone, "shockTime", 200)
|
factoryZone.shockTime = theZone:getNumberFromZoneProperty("shockTime", 200)
|
||||||
factoryZone.repairTime = cfxZones.getNumberFromZoneProperty(theZone, "repairTime", 200)
|
factoryZone.repairTime = theZone:getNumberFromZoneProperty( "repairTime", 200)
|
||||||
factoryZone.targetZones = "OWNED"
|
factoryZone.targetZones = "OWNED"
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function factoryZone.readLiveries()
|
||||||
|
theZone = cfxZones.getZoneByName("factoryLiveries")
|
||||||
|
if not theZone then return end
|
||||||
|
factoryZone.liveries = theZone:getAllZoneProperties()
|
||||||
|
trigger.action.outText("Custom liveries detected. All factories now use:", 30)
|
||||||
|
for aType, aLivery in pairs (factoryZone.liveries) do
|
||||||
|
trigger.action.outText(" type <" .. aType .. "> now uses livery <" .. aLivery .. ">", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
function factoryZone.init()
|
function factoryZone.init()
|
||||||
-- check libs
|
-- check libs
|
||||||
if not dcsCommon.libCheck("cfx Factory Zones",
|
if not dcsCommon.libCheck("cfx Factory Zones",
|
||||||
@ -706,12 +705,12 @@ function factoryZone.init()
|
|||||||
local theZone = cfxZones.getZoneByName("factoryZoneConfig")
|
local theZone = cfxZones.getZoneByName("factoryZoneConfig")
|
||||||
factoryZone.readConfigZone(theZone)
|
factoryZone.readConfigZone(theZone)
|
||||||
|
|
||||||
|
-- read livery presets for factory production
|
||||||
|
factoryZone.readLiveries()
|
||||||
|
|
||||||
-- collect all zones by their 'factory' property
|
-- collect all zones by their 'factory' property
|
||||||
-- start the process
|
-- start the process
|
||||||
local pZones = cfxZones.zonesWithProperty("factory")
|
local pZones = cfxZones.zonesWithProperty("factory")
|
||||||
|
|
||||||
-- now add all zones to my zones table, and convert the owner property into
|
|
||||||
-- a proper attribute
|
|
||||||
for k, aZone in pairs(pZones) do
|
for k, aZone in pairs(pZones) do
|
||||||
factoryZone.addFactoryZone(aZone)
|
factoryZone.addFactoryZone(aZone)
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
fireFX = {}
|
fireFX = {}
|
||||||
fireFX.version = "1.1.0"
|
fireFX.version = "2.0.1"
|
||||||
fireFX.verbose = false
|
fireFX.verbose = false
|
||||||
fireFX.ups = 1
|
fireFX.ups = 1
|
||||||
fireFX.requiredLibs = {
|
fireFX.requiredLibs = {
|
||||||
@ -15,7 +15,7 @@ fireFX.fx = {}
|
|||||||
1.1.1 - agl attribute
|
1.1.1 - agl attribute
|
||||||
2.0.0 - dmlZones OOP
|
2.0.0 - dmlZones OOP
|
||||||
- rndLoc
|
- rndLoc
|
||||||
|
2.0.1 - fixed rndLoc determination
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
function fireFX.addFX(theZone)
|
function fireFX.addFX(theZone)
|
||||||
@ -92,9 +92,9 @@ function fireFX.createFXWithZone(theZone)
|
|||||||
end
|
end
|
||||||
|
|
||||||
theZone.rndLoc = theZone:getBoolFromZoneProperty("rndLoc", false)
|
theZone.rndLoc = theZone:getBoolFromZoneProperty("rndLoc", false)
|
||||||
if theZone.max + 1 and (not theZone.rndLoc) then
|
if theZone.max > 1 and (not theZone.rndLoc) then
|
||||||
if theZone.verbose or fireFX.verbose then
|
if theZone.verbose or fireFX.verbose then
|
||||||
trigger.action.outText("+++ffx: more than 1 fires, will set to random loc")
|
trigger.action.outText("+++ffx: more than 1 fires, will set to random loc", 30)
|
||||||
end
|
end
|
||||||
theZone.rndLoc = true
|
theZone.rndLoc = true
|
||||||
end
|
end
|
||||||
@ -113,7 +113,7 @@ function fireFX.startTheFire(theZone)
|
|||||||
theZone.fireNames = {}
|
theZone.fireNames = {}
|
||||||
local num = cfxZones.randomInRange(theZone.min, theZone.max)
|
local num = cfxZones.randomInRange(theZone.min, theZone.max)
|
||||||
for i = 1, num do
|
for i = 1, num do
|
||||||
local p = cfxZones.getPoint(theZone)
|
local p = theZone:getPoint()
|
||||||
if theZone.rndLoc then
|
if theZone.rndLoc then
|
||||||
p = theZone:randomPointInZone()
|
p = theZone:randomPointInZone()
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
groupTracker = {}
|
groupTracker = {}
|
||||||
groupTracker.version = "1.2.1"
|
groupTracker.version = "2.0.0"
|
||||||
groupTracker.verbose = false
|
groupTracker.verbose = false
|
||||||
groupTracker.ups = 1
|
groupTracker.ups = 1
|
||||||
groupTracker.requiredLibs = {
|
groupTracker.requiredLibs = {
|
||||||
@ -10,28 +10,7 @@ groupTracker.trackers = {}
|
|||||||
|
|
||||||
--[[--
|
--[[--
|
||||||
Version History
|
Version History
|
||||||
1.0.0 - Initial version
|
2.0.0 - dmlZones, OOP, clean-up, legacy support
|
||||||
1.1.0 - filtering added
|
|
||||||
- array support for trackers
|
|
||||||
- array support for trackers
|
|
||||||
1.1.1 - corrected clone zone reference bug
|
|
||||||
1.1.2 - corrected naming (removed bang from flags), deprecated old
|
|
||||||
- more zone-local verbosity
|
|
||||||
1.1.3 - spellings
|
|
||||||
- addGroupToTrackerNamed bug removed accessing tracker
|
|
||||||
- new removeGroupNamedFromTrackerNamed()
|
|
||||||
1.1.4 - destroy? input
|
|
||||||
- allGone! output
|
|
||||||
- triggerMethod
|
|
||||||
- method
|
|
||||||
- isDead optimiz ation
|
|
||||||
1.2.0 - double detection
|
|
||||||
- numUnits output
|
|
||||||
- persistence
|
|
||||||
1.2.1 - allGone! bug removed
|
|
||||||
1.2.2 - new groupTrackedBy() method
|
|
||||||
- limbo for storing a unit in limbo so it is
|
|
||||||
- not counted as missing when being transported
|
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
@ -259,62 +238,7 @@ function groupTracker.removeGroupNamedFromTrackerNamed(gName, trackerName)
|
|||||||
end
|
end
|
||||||
|
|
||||||
groupTracker.removeGroupNamedFromTracker(gName, theTracker)
|
groupTracker.removeGroupNamedFromTracker(gName, theTracker)
|
||||||
--[[--
|
|
||||||
local filteredGroups = {}
|
|
||||||
local foundOne = false
|
|
||||||
local totalUnits = 0
|
|
||||||
if not theTracker.trackedGroups then theTracker.trackedGroups = {} end
|
|
||||||
for idx, aGroup in pairs(theTracker.trackedGroups) do
|
|
||||||
if aGroup:getName() == gName then
|
|
||||||
-- skip and remember
|
|
||||||
foundOne = true
|
|
||||||
else
|
|
||||||
table.insert(filteredGroups, aGroup)
|
|
||||||
if Group.isExist(aGroup) then
|
|
||||||
totalUnits = totalUnits + aGroup:getSize()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- also check limbo
|
|
||||||
for limboName, limboNum in pairs (theTracker.limbo) do
|
|
||||||
if gName == limboName then
|
|
||||||
-- don't count, but remember that it existed
|
|
||||||
foundOne = true
|
|
||||||
else
|
|
||||||
totalUnits = totalUnits + limboNum
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- remove from limbo
|
|
||||||
theTracker.limbo[gName] = nil
|
|
||||||
|
|
||||||
if (not foundOne) and (theTracker.verbose or groupTracker.verbose) then
|
|
||||||
trigger.action.outText("+++gTrk: Removal Request Note: group <" .. gName .. "> wasn't tracked by <" .. trackerName .. ">", 30)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- remember the new, cleanded set
|
|
||||||
theTracker.trackedGroups = filteredGroups
|
|
||||||
|
|
||||||
-- update number of tracked units. do it in any case
|
|
||||||
if theTracker.tNumUnits then
|
|
||||||
cfxZones.setFlagValue(theTracker.tNumUnits, totalUnits, theTracker)
|
|
||||||
end
|
|
||||||
|
|
||||||
if foundOne then
|
|
||||||
if theTracker.verbose or groupTracker.verbose then
|
|
||||||
trigger.action.outText("+++gTrk: removed group <" .. gName .. "> from tracker <" .. trackerName .. ">", 30)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- now bang/invoke addGroup!
|
|
||||||
if theTracker.tRemoveGroup then
|
|
||||||
cfxZones.pollFlag(theTracker.tRemoveGroup, "inc", theTracker)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- now set numGroups
|
|
||||||
if theTracker.tNumGroups then
|
|
||||||
cfxZones.setFlagValue(theTracker.tNumGroups, dcsCommon.getSizeOfTable(theTracker.limbo) + #theTracker.trackedGroups, theTracker)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
--]]--
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- groupTrackedBy - return trackers that track group theGroup
|
-- groupTrackedBy - return trackers that track group theGroup
|
||||||
@ -365,62 +289,56 @@ function groupTracker.createTrackerWithZone(theZone)
|
|||||||
theZone.limbo = {} -- name based, for groups that are tracked
|
theZone.limbo = {} -- name based, for groups that are tracked
|
||||||
-- although technically off the map (helo etc)
|
-- although technically off the map (helo etc)
|
||||||
|
|
||||||
theZone.trackerMethod = cfxZones.getStringFromZoneProperty(theZone, "method", "inc")
|
theZone.trackerMethod = theZone:getStringFromZoneProperty("method", "inc")
|
||||||
if cfxZones.hasProperty(theZone, "trackerMethod") then
|
if theZone:hasProperty("trackerMethod") then
|
||||||
theZone.trackerMethod = cfxZones.getStringFromZoneProperty(theZone, "trackerMethod", "inc")
|
theZone.trackerMethod = theZone:getStringFromZoneProperty( "trackerMethod", "inc")
|
||||||
end
|
end
|
||||||
|
|
||||||
theZone.trackerTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "triggerMethod", "change")
|
theZone.trackerTriggerMethod = theZone:getStringFromZoneProperty("triggerMethod", "change")
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "trackerTriggerMethod") then
|
if theZone:hasProperty("trackerTriggerMethod") then
|
||||||
theZone.trackerTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "trackerTriggerMethod", "change")
|
theZone.trackerTriggerMethod = theZone:getStringFromZoneProperty("trackerTriggerMethod", "change")
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "numGroups") then
|
if theZone:hasProperty("numGroups") then
|
||||||
theZone.tNumGroups = cfxZones.getStringFromZoneProperty(theZone, "numGroups", "*<none>")
|
theZone.tNumGroups = theZone:getStringFromZoneProperty("numGroups", "*<none>") -- legacy support
|
||||||
-- we may need to zero this flag
|
elseif theZone:hasProperty("numGroups#") then
|
||||||
elseif cfxZones.hasProperty(theZone, "numGroups!") then -- DEPRECATED!
|
theZone.tNumGroups = theZone:getStringFromZoneProperty( "numGroups#", "*<none>")
|
||||||
theZone.tNumGroups = cfxZones.getStringFromZoneProperty(theZone, "numGroups!", "*<none>")
|
|
||||||
-- we may need to zero this flag
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "numUnits") then
|
if theZone:hasProperty("numUnits") then
|
||||||
theZone.tNumUnits = cfxZones.getStringFromZoneProperty(theZone, "numUnits", "*<none>")
|
theZone.tNumUnits = theZOne:getStringFromZoneProperty("numUnits", "*<none>") -- legacy support
|
||||||
|
elseif theZone:hasProperty("numUnits#") then
|
||||||
|
theZone.tNumUnits = theZone:getStringFromZoneProperty("numUnits#", "*<none>")
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "addGroup") then
|
if theZone:hasProperty("addGroup") then
|
||||||
theZone.tAddGroup = cfxZones.getStringFromZoneProperty(theZone, "addGroup", "*<none>")
|
theZone.tAddGroup = theZone:getStringFromZoneProperty("addGroup", "*<none>") -- legacy support
|
||||||
-- we may need to zero this flag
|
elseif theZone:hasProperty("addGroup!") then
|
||||||
elseif cfxZones.hasProperty(theZone, "addGroup!") then -- DEPRECATED
|
theZone.tAddGroup = theZone:getStringFromZoneProperty("addGroup!", "*<none>")
|
||||||
theZone.tAddGroup = cfxZones.getStringFromZoneProperty(theZone, "addGroup!", "*<none>")
|
|
||||||
-- we may need to zero this flag
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "removeGroup") then
|
if theZone:hasProperty("removeGroup") then
|
||||||
theZone.tRemoveGroup = cfxZones.getStringFromZoneProperty(theZone, "removeGroup", "*<none>")
|
theZone.tRemoveGroup = theZone:getStringFromZoneProperty( "removeGroup", "*<none>") -- legacy support
|
||||||
-- we may need to zero this flag
|
elseif theZone:hasProperty("removeGroup!") then
|
||||||
elseif cfxZones.hasProperty(theZone, "removeGroup!") then -- DEPRECATED!
|
theZone.tRemoveGroup = theZone:getStringFromZoneProperty( "removeGroup!", "*<none>")
|
||||||
theZone.tRemoveGroup = cfxZones.getStringFromZoneProperty(theZone, "removeGroup!", "*<none>")
|
|
||||||
-- we may need to zero this flag
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if theZone:hasProperty("groupFilter") then
|
||||||
|
local filterString = theZone:getStringFromZoneProperty( "groupFilter", "2") -- ground
|
||||||
if cfxZones.hasProperty(theZone, "groupFilter") then
|
|
||||||
local filterString = cfxZones.getStringFromZoneProperty(theZone, "groupFilter", "2") -- ground
|
|
||||||
theZone.groupFilter = dcsCommon.string2GroupCat(filterString)
|
theZone.groupFilter = dcsCommon.string2GroupCat(filterString)
|
||||||
if groupTracker.verbose or theZone.verbose then
|
if groupTracker.verbose or theZone.verbose then
|
||||||
trigger.action.outText("+++gTrck: filtering " .. theZone.groupFilter .. " in " .. theZone.name, 30)
|
trigger.action.outText("+++gTrck: filtering " .. theZone.groupFilter .. " in " .. theZone.name, 30)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "destroy?") then
|
if theZone:hasProperty("destroy?") then
|
||||||
theZone.destroyFlag = cfxZones.getStringFromZoneProperty(theZone, "destroy?", "*<none>")
|
theZone.destroyFlag = theZone:getStringFromZoneProperty(theZone, "destroy?", "*<none>")
|
||||||
theZone.lastDestroyValue = cfxZones.getFlagValue(theZone.destroyFlag, theZone)
|
theZone.lastDestroyValue = cfxZones.getFlagValue(theZone.destroyFlag, theZone)
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "allGone!") then
|
if theZone:hasProperty("allGone!") then
|
||||||
theZone.allGoneFlag = cfxZones.getStringFromZoneProperty(theZone, "allGone!", "<None>") -- note string on number default
|
theZone.allGoneFlag = theZone:getStringFromZoneProperty("allGone!", "<None>") -- note string on number default
|
||||||
end
|
end
|
||||||
theZone.lastGroupCount = 0
|
theZone.lastGroupCount = 0
|
||||||
|
|
||||||
@ -652,15 +570,12 @@ end
|
|||||||
function groupTracker.readConfigZone()
|
function groupTracker.readConfigZone()
|
||||||
local theZone = cfxZones.getZoneByName("groupTrackerConfig")
|
local theZone = cfxZones.getZoneByName("groupTrackerConfig")
|
||||||
if not theZone then
|
if not theZone then
|
||||||
if groupTracker.verbose then
|
theZone = cfxZones.createSimpleZone("groupTrackerConfig")
|
||||||
trigger.action.outText("+++gTrk: NO config zone!", 30)
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
end
|
||||||
|
|
||||||
groupTracker.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
groupTracker.verbose = theZoneverbose
|
||||||
|
|
||||||
groupTracker.ups = cfxZones.getNumberFromZoneProperty(theZone, "ups", 1)
|
groupTracker.ups = theZone:getNumberFromZoneProperty("ups", 1)
|
||||||
|
|
||||||
if groupTracker.verbose then
|
if groupTracker.verbose then
|
||||||
trigger.action.outText("+++gTrk: read config", 30)
|
trigger.action.outText("+++gTrk: read config", 30)
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
messenger = {}
|
messenger = {}
|
||||||
messenger.version = "2.3.1"
|
messenger.version = "3.0.0"
|
||||||
messenger.verbose = false
|
messenger.verbose = false
|
||||||
messenger.requiredLibs = {
|
messenger.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
@ -8,68 +8,9 @@ messenger.requiredLibs = {
|
|||||||
messenger.messengers = {}
|
messenger.messengers = {}
|
||||||
--[[--
|
--[[--
|
||||||
Version History
|
Version History
|
||||||
1.0.0 - initial version
|
|
||||||
1.0.1 - messageOut? synonym
|
|
||||||
- spelling types in about
|
|
||||||
1.1.0 - DML flag support
|
|
||||||
- clearScreen option
|
|
||||||
- inValue?
|
|
||||||
- message preprocessor
|
|
||||||
1.1.1 - firewalled coalition to msgCoalition
|
|
||||||
- messageOn?
|
|
||||||
- messageOff?
|
|
||||||
1.2.0 - msgTriggerMethod (original Watchflag integration)
|
|
||||||
1.2.1 - qoL: <n> = newline, <z> = zone name, <v> = value
|
|
||||||
1.3.0 - messenger? saves messageOut? attribute
|
|
||||||
1.3.1 - message now can interpret value as time with <h> <m> <s> <:h> <:m> <:s>
|
|
||||||
1.3.2 - message interprets <t> as time in HH:MM:SS of current time
|
|
||||||
- can interpret <lat>, <lon>, <mgrs>
|
|
||||||
- zone-local verbosity
|
|
||||||
1.3.3 - mute/messageMute option to start messenger in mute
|
|
||||||
2.0.0 - re-engineered message wildcards
|
|
||||||
- corrected dynamic content for time and latlon (classic)
|
|
||||||
- new timeFormat attribute
|
|
||||||
- <v: flagname>
|
|
||||||
- <t: flagname>
|
|
||||||
- added <ele>
|
|
||||||
- added imperial
|
|
||||||
- <lat: unit/zone>
|
|
||||||
- <lon: unit/zone>
|
|
||||||
- <ele: unit/zone>
|
|
||||||
- <mgrs: unit/zone>
|
|
||||||
- <latlon: unit/zone>
|
|
||||||
- <lle: unit/zone>
|
|
||||||
- messageError
|
|
||||||
- unit
|
|
||||||
- group
|
|
||||||
2.0.1 - config optimization
|
|
||||||
2.1.0 - unit only: dynamicUnitProcessing with other units/zones
|
|
||||||
- <bae: u/z> bearing to unit/zone
|
|
||||||
- <rbae u/z> response mapped by unit's heading
|
|
||||||
- <clk: u/z> bearing in clock position to unit/zone
|
|
||||||
- <rng: u/z> range to unit/zone
|
|
||||||
- <hnd: u/z> bearing in left/right/ahead/behind
|
|
||||||
- <sde: u/z> bearing in starboard/port/ahead/aft
|
|
||||||
- added dynamicGroupProcessing to select unit 1
|
|
||||||
- responses attribute
|
|
||||||
- <rsp: flag>
|
|
||||||
- <rrnd> response randomized
|
|
||||||
- <rhdg: u/z> respons mapped by unit's heading
|
|
||||||
- <cls unit> closing speed
|
|
||||||
- <vel unit> velocity (speed)
|
|
||||||
- <asp unit> aspect
|
|
||||||
- fix to messageMute
|
|
||||||
- <type: unit>
|
|
||||||
2.1.1 - cosmetic: only output text if len>0 and not cls
|
|
||||||
2.2.0 - <player: unit>
|
|
||||||
- made dynamic string gen more portable in prep for move to cfxZones
|
|
||||||
- refactoring wildcard processing: moved to cfxZones
|
|
||||||
2.2.1 - when messenger is linked to a unit, it can use the linked
|
|
||||||
unit as reference point for relative wildcards. Always broadcasts to coalition. Can be used to broadcase 'eye in the sky' type information
|
|
||||||
- fixed verbosity bug
|
|
||||||
2.3.0 - cfxZones OOP switch
|
2.3.0 - cfxZones OOP switch
|
||||||
2.3.1 - triggering message AFTER the on/off switches are tested
|
2.3.1 - triggering message AFTER the on/off switches are tested
|
||||||
|
3.0.0 - removed messenger, in?, f? attributes, harmonized on messenger?
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
function messenger.addMessenger(theZone)
|
function messenger.addMessenger(theZone)
|
||||||
@ -255,11 +196,11 @@ function messenger.createMessengerWithZone(theZone)
|
|||||||
if theZone:hasProperty("in?") then
|
if theZone:hasProperty("in?") then
|
||||||
theZone.triggerMessagerFlag = theZone:getStringFromZoneProperty("in?", "none")
|
theZone.triggerMessagerFlag = theZone:getStringFromZoneProperty("in?", "none")
|
||||||
end
|
end
|
||||||
|
--[[--
|
||||||
if theZone:hasProperty("messageOut?") then
|
if theZone:hasProperty("messageOut?") then
|
||||||
theZone.triggerMessagerFlag = theZone:getStringFromZoneProperty("messageOut?", "none")
|
theZone.triggerMessagerFlag = theZone:getStringFromZoneProperty("messageOut?", "none")
|
||||||
end
|
end
|
||||||
|
--]]--
|
||||||
-- try default only if no other is set
|
-- try default only if no other is set
|
||||||
if not theZone.triggerMessagerFlag then
|
if not theZone.triggerMessagerFlag then
|
||||||
if not theZone:hasProperty("messenger?") then
|
if not theZone:hasProperty("messenger?") then
|
||||||
@ -505,12 +446,13 @@ function messenger.start()
|
|||||||
|
|
||||||
-- process messenger Zones
|
-- process messenger Zones
|
||||||
-- old style
|
-- old style
|
||||||
|
--[[--
|
||||||
local attrZones = cfxZones.getZonesWithAttributeNamed("messenger")
|
local attrZones = cfxZones.getZonesWithAttributeNamed("messenger")
|
||||||
for k, aZone in pairs(attrZones) do
|
for k, aZone in pairs(attrZones) do
|
||||||
messenger.createMessengerWithZone(aZone) -- process attributes
|
messenger.createMessengerWithZone(aZone) -- process attributes
|
||||||
messenger.addMessenger(aZone) -- add to list
|
messenger.addMessenger(aZone) -- add to list
|
||||||
end
|
end
|
||||||
|
--]]--
|
||||||
-- new style that saves messageOut? flag by reading flags
|
-- new style that saves messageOut? flag by reading flags
|
||||||
attrZones = cfxZones.getZonesWithAttributeNamed("messenger?")
|
attrZones = cfxZones.getZonesWithAttributeNamed("messenger?")
|
||||||
for k, aZone in pairs(attrZones) do
|
for k, aZone in pairs(attrZones) do
|
||||||
|
|||||||
@ -17,6 +17,7 @@ cfxObjectDestructDetector.requiredLibs = {
|
|||||||
re-wrote object determination to not be affected by
|
re-wrote object determination to not be affected by
|
||||||
ID changes (happens with map updates)
|
ID changes (happens with map updates)
|
||||||
fail addZone when name property is missing
|
fail addZone when name property is missing
|
||||||
|
2.0.1 check that the object is within the zone onEvent
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
cfxObjectDestructDetector.objectZones = {}
|
cfxObjectDestructDetector.objectZones = {}
|
||||||
@ -86,6 +87,8 @@ function cfxObjectDestructDetector:onEvent(event)
|
|||||||
if event.id == world.event.S_EVENT_DEAD then
|
if event.id == world.event.S_EVENT_DEAD then
|
||||||
if not event.initiator then return end
|
if not event.initiator then return end
|
||||||
local theObject = event.initiator
|
local theObject = event.initiator
|
||||||
|
-- check location
|
||||||
|
local pos = theObject:getPoint()
|
||||||
local desc = theObject:getDesc()
|
local desc = theObject:getDesc()
|
||||||
if not desc then return end
|
if not desc then return end
|
||||||
local matchMe = desc.typeName -- we home in on object's typeName
|
local matchMe = desc.typeName -- we home in on object's typeName
|
||||||
@ -93,7 +96,10 @@ function cfxObjectDestructDetector:onEvent(event)
|
|||||||
matchMe = string.upper(matchMe)
|
matchMe = string.upper(matchMe)
|
||||||
|
|
||||||
for idx, aZone in pairs(cfxObjectDestructDetector.objectZones) do
|
for idx, aZone in pairs(cfxObjectDestructDetector.objectZones) do
|
||||||
if (not aZone.isDestroyed) and aZone.objName == matchMe then
|
if (not aZone.isDestroyed)
|
||||||
|
and aZone.objName == matchMe
|
||||||
|
and aZone:pointInZone(pos) -- make sure it's not a dupe
|
||||||
|
then
|
||||||
if aZone.outDestroyFlag then
|
if aZone.outDestroyFlag then
|
||||||
aZone:pollFlag(aZone.outDestroyFlag, aZone.oddMethod)
|
aZone:pollFlag(aZone.outDestroyFlag, aZone.oddMethod)
|
||||||
end
|
end
|
||||||
@ -13,25 +13,6 @@ cfxObjectSpawnZones.verbose = false
|
|||||||
*** DOES NOT EXTEND ZONES ***
|
*** DOES NOT EXTEND ZONES ***
|
||||||
|
|
||||||
version history
|
version history
|
||||||
1.0.0 - based on 1.4.6 version from cfxSpawnZones
|
|
||||||
1.1.0 - uses linkedUnit, so spawnming can occur on ships
|
|
||||||
make sure you also enable useOffset to place the
|
|
||||||
statics away from the center of the ship
|
|
||||||
1.1.1 - also processes paused flag
|
|
||||||
- despawnRemaining(spawner)
|
|
||||||
1.1.2 - autoRemove option re-installed
|
|
||||||
- added possibility to autoUnlink
|
|
||||||
1.1.3 - ME-triggered flag via f? and triggerFlag
|
|
||||||
1.1.4 - activate?, pause? attributes
|
|
||||||
1.1.5 - spawn?, spawnObjects? synonyms
|
|
||||||
1.2.0 - DML flag upgrade
|
|
||||||
1.2.1 - config zone
|
|
||||||
- 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
|
|
||||||
1.3.1 - baseName can be set to zone's name by giving "*"
|
|
||||||
1.3.2 - delicateName supports '*' to refer to own zone
|
|
||||||
2.0.0 - dmlZones
|
2.0.0 - dmlZones
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
@ -1,5 +1,5 @@
|
|||||||
persistence = {}
|
persistence = {}
|
||||||
persistence.version = "1.0.7"
|
persistence.version = "2.0.0"
|
||||||
persistence.ups = 1 -- once every 1 seconds
|
persistence.ups = 1 -- once every 1 seconds
|
||||||
persistence.verbose = false
|
persistence.verbose = false
|
||||||
persistence.active = false
|
persistence.active = false
|
||||||
@ -10,39 +10,25 @@ persistence.saveDir = nil -- set at start
|
|||||||
persistence.name = "persistence" -- for cfxZones
|
persistence.name = "persistence" -- for cfxZones
|
||||||
persistence.missionData = {} -- loaded from file
|
persistence.missionData = {} -- loaded from file
|
||||||
persistence.requiredLibs = {
|
persistence.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon",
|
||||||
"cfxZones", -- Zones, of course
|
"cfxZones",
|
||||||
}
|
}
|
||||||
--[[--
|
--[[--
|
||||||
Version History
|
Version History
|
||||||
1.0.0 - initial version
|
2.0.0 - dml zones, OOP
|
||||||
1.0.1 - when available, module sets flag "cfxPersistence" to 1
|
cleanup
|
||||||
- when data availabe, cfxPersistenceHasData is set to 1
|
|
||||||
- spelling
|
|
||||||
- cfxZones interface
|
|
||||||
- always output save location
|
|
||||||
1.0.2 - QoL when verbosity is on
|
|
||||||
1.0.3 - no longer always tells " mission saved to"
|
|
||||||
new 'saveNotification" can be off
|
|
||||||
1.0.4 - new optional 'root' property
|
|
||||||
1.0.5 - desanitize check on readConfig to early-abort
|
|
||||||
1.0.6 - removed potential verbosity bug
|
|
||||||
1.0.7 - correct abort for sanitized DCS, when non-verbose
|
|
||||||
|
|
||||||
|
|
||||||
PROVIDES LOAD/SAVE ABILITY TO MODULES
|
PROVIDES LOAD/SAVE ABILITY TO MODULES
|
||||||
PROVIDES STANDALONE/HOSTED SERVER COMPATIBILITY
|
PROVIDES STANDALONE/HOSTED SERVER COMPATIBILITY
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
-- in order to work, Host must desanitize lfs and io
|
-- 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
|
-- flags to save. can be added to by saveFlags attribute
|
||||||
--
|
--
|
||||||
persistence.flagsToSave = {} -- simple table
|
persistence.flagsToSave = {} -- simple table
|
||||||
persistence.callbacks = {} -- cbblocks, dictionary
|
persistence.callbacks = {} -- cbblocks, dictionary by name
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
@ -417,13 +403,13 @@ end
|
|||||||
--
|
--
|
||||||
|
|
||||||
function persistence.collectFlagsFromZone(theZone)
|
function persistence.collectFlagsFromZone(theZone)
|
||||||
local theFlags = cfxZones.getStringFromZoneProperty(theZone, "saveFlags", "*dummy")
|
local theFlags = theZone:getStringFromZoneProperty("saveFlags", "*dummy")
|
||||||
persistence.registerFlagsToSave(theFlags, theZone)
|
persistence.registerFlagsToSave(theFlags, theZone)
|
||||||
end
|
end
|
||||||
|
|
||||||
function persistence.readConfigZone()
|
function persistence.readConfigZone()
|
||||||
if not _G["lfs"] then
|
if not _G["lfs"] then
|
||||||
trigger.action.outText("+++persistence: DCS not correctly desanitized. Persistence disabled", 30)
|
trigger.action.outText("+++persistence: DCS correctly not 'desanitized'. Persistence disabled", 30)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -436,10 +422,10 @@ function persistence.readConfigZone()
|
|||||||
|
|
||||||
-- serverDir is the path from the server save directory, usually "Missions/".
|
-- serverDir is the path from the server save directory, usually "Missions/".
|
||||||
-- will be added to lfs.writedir() unless given a root attribute
|
-- will be added to lfs.writedir() unless given a root attribute
|
||||||
if cfxZones.hasProperty(theZone, "root") then
|
if theZone:hasProperty("root") then
|
||||||
-- we split this to enable further processing down the
|
-- we split this to enable further processing down the
|
||||||
-- line if neccessary
|
-- line if neccessary
|
||||||
persistence.root = cfxZones.getStringFromZoneProperty(theZone, "root", lfs.writedir()) -- safe default
|
persistence.root = theZone:getStringFromZoneProperty("root", lfs.writedir()) -- safe default
|
||||||
if not dcsCommon.stringEndsWith(persistence.root, "\\") then
|
if not dcsCommon.stringEndsWith(persistence.root, "\\") then
|
||||||
persistence.root = persistence.root .. "\\"
|
persistence.root = persistence.root .. "\\"
|
||||||
end
|
end
|
||||||
@ -453,11 +439,11 @@ function persistence.readConfigZone()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
persistence.serverDir = cfxZones.getStringFromZoneProperty(theZone, "serverDir", "Missions\\")
|
persistence.serverDir = theZone:getStringFromZoneProperty("serverDir", "Missions\\")
|
||||||
|
|
||||||
if hasConfig then
|
if hasConfig then
|
||||||
if cfxZones.hasProperty(theZone, "saveDir") then
|
if theZone:hasProperty("saveDir") then
|
||||||
persistence.saveDir = cfxZones.getStringFromZoneProperty(theZone, "saveDir", "")
|
persistence.saveDir = theZone:getStringFromZoneProperty("saveDir", "")
|
||||||
else
|
else
|
||||||
-- local missname = net.dostring_in("gui", "return DCS.getMissionName()") .. " (data)"
|
-- local missname = net.dostring_in("gui", "return DCS.getMissionName()") .. " (data)"
|
||||||
persistence.saveDir = dcsCommon.getMissionName() .. " (data)"
|
persistence.saveDir = dcsCommon.getMissionName() .. " (data)"
|
||||||
@ -472,32 +458,32 @@ function persistence.readConfigZone()
|
|||||||
trigger.action.outText("*** WARNING: persistence is set to write to main mission directory!", 30)
|
trigger.action.outText("*** WARNING: persistence is set to write to main mission directory!", 30)
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "saveFileName") then
|
if theZone:hasProperty("saveFileName") then
|
||||||
persistence.saveFileName = cfxZones.getStringFromZoneProperty(theZone, "saveFileName", dcsCommon.getMissionName() .. " Data.txt")
|
persistence.saveFileName = theZone:getStringFromZoneProperty("saveFileName", dcsCommon.getMissionName() .. " Data.txt")
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "versionID") then
|
if theZone:hasProperty("versionID") then
|
||||||
persistence.versionID = cfxZones.getStringFromZoneProperty(theZone, "versionID", "") -- to check for full restart
|
persistence.versionID = theZone:getStringFromZoneProperty("versionID", "") -- to check for full restart
|
||||||
end
|
end
|
||||||
|
|
||||||
persistence.saveInterval = cfxZones.getNumberFromZoneProperty(theZone, "saveInterval", -1) -- default to manual save
|
persistence.saveInterval = theZone:getNumberFromZoneProperty("saveInterval", -1) -- default to manual save
|
||||||
if persistence.saveInterval > 0 then
|
if persistence.saveInterval > 0 then
|
||||||
persistence.saveTime = persistence.saveInterval * 60 + timer.getTime()
|
persistence.saveTime = persistence.saveInterval * 60 + timer.getTime()
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "cleanRestart?") then
|
if theZone:hasProperty("cleanRestart?") then
|
||||||
persistence.cleanRestart = cfxZones.getStringFromZoneProperty(theZone, "cleanRestart?", "*<none>")
|
persistence.cleanRestart = theZone:getStringFromZoneProperty("cleanRestart?", "*<none>")
|
||||||
persistence.lastCleanRestart = cfxZones.getFlagValue(persistence.cleanRestart, theZone)
|
persistence.lastCleanRestart = theZone:getFlagValue(persistence.cleanRestart)
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "saveMission?") then
|
if theZone:hasProperty("saveMission?") then
|
||||||
persistence.saveMission = cfxZones.getStringFromZoneProperty(theZone, "saveMission?", "*<none>")
|
persistence.saveMission = theZone:getStringFromZoneProperty("saveMission?", "*<none>")
|
||||||
persistence.lastSaveMission = cfxZones.getFlagValue(persistence.saveMission, theZone)
|
persistence.lastSaveMission = theZone:getFlagValue(persistence.saveMission)
|
||||||
end
|
end
|
||||||
|
|
||||||
persistence.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
persistence.verbose = theZone.verbose
|
||||||
|
|
||||||
persistence.saveNotification = cfxZones.getBoolFromZoneProperty(theZone, "saveNotification", true)
|
persistence.saveNotification = theZone:getBoolFromZoneProperty("saveNotification", true)
|
||||||
|
|
||||||
if persistence.verbose then
|
if persistence.verbose then
|
||||||
trigger.action.outText("+++persistence: read config", 30)
|
trigger.action.outText("+++persistence: read config", 30)
|
||||||
@ -534,14 +520,13 @@ function persistence.start()
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- local mainDir = lfs.writedir() .. persistence.serverDir
|
|
||||||
local mainDir = persistence.root .. persistence.serverDir
|
local mainDir = persistence.root .. persistence.serverDir
|
||||||
if not dcsCommon.stringEndsWith(mainDir, "\\") then
|
if not dcsCommon.stringEndsWith(mainDir, "\\") then
|
||||||
mainDir = mainDir .. "\\"
|
mainDir = mainDir .. "\\"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- lets see if we can access the server's mission directory and
|
-- lets see if we can access the server's mission directory and
|
||||||
-- save directory
|
-- save directory
|
||||||
|
|
||||||
if persistence.isDir(mainDir) then
|
if persistence.isDir(mainDir) then
|
||||||
if persistence.verbose then
|
if persistence.verbose then
|
||||||
trigger.action.outText("persistence: main dir is <" .. mainDir .. ">", 30)
|
trigger.action.outText("persistence: main dir is <" .. mainDir .. ">", 30)
|
||||||
@ -613,7 +598,6 @@ function persistence.start()
|
|||||||
-- and start updating
|
-- and start updating
|
||||||
persistence.update()
|
persistence.update()
|
||||||
|
|
||||||
|
|
||||||
return persistence.active
|
return persistence.active
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -627,5 +611,3 @@ if not persistence.start() then
|
|||||||
end
|
end
|
||||||
-- we do NOT remove the methods so we don't crash
|
-- we do NOT remove the methods so we don't crash
|
||||||
end
|
end
|
||||||
|
|
||||||
-- add zones for saveFlags so authors can easily save flag values
|
|
||||||
|
|||||||
@ -1,91 +1,17 @@
|
|||||||
cfxPlayerScore = {}
|
cfxPlayerScore = {}
|
||||||
cfxPlayerScore.version = "3.0.0"
|
cfxPlayerScore.version = "3.0.1"
|
||||||
cfxPlayerScore.name = "cfxPlayerScore" -- compatibility with flag bangers
|
cfxPlayerScore.name = "cfxPlayerScore" -- compatibility with flag bangers
|
||||||
cfxPlayerScore.badSound = "Death BRASS.wav"
|
cfxPlayerScore.badSound = "Death BRASS.wav"
|
||||||
cfxPlayerScore.scoreSound = "Quest Snare 3.wav"
|
cfxPlayerScore.scoreSound = "Quest Snare 3.wav"
|
||||||
cfxPlayerScore.announcer = true
|
cfxPlayerScore.announcer = true
|
||||||
cfxPlayerScore.firstSave = true -- to force overwrite
|
cfxPlayerScore.firstSave = true -- to force overwrite
|
||||||
--[[-- VERSION HISTORY
|
--[[-- VERSION HISTORY
|
||||||
1.0.1 - bug fixes to killDetected
|
|
||||||
1.0.2 - messaging clean-up, less verbose
|
|
||||||
1.1.0 - integrated score base system
|
|
||||||
- accepts configZones
|
|
||||||
- module validation
|
|
||||||
- isNamedUnit(theUnit)
|
|
||||||
- notify if named unit killed
|
|
||||||
- kill weapon reported
|
|
||||||
1.2.0 - score table
|
|
||||||
- announcer attribute
|
|
||||||
- badSound name
|
|
||||||
- scoreSound name
|
|
||||||
1.3.0 - object2score
|
|
||||||
- static objects also can score
|
|
||||||
- can now also score members of group by adding group name
|
|
||||||
- scenery objects are now supported. use the
|
|
||||||
number that is given under OBJECT ID when
|
|
||||||
using assign as...
|
|
||||||
1.3.1 - isStaticObject() to better detect buildings after Match 22 patch
|
|
||||||
1.3.2 - corrected ground default score
|
|
||||||
- removed dependency to cfxPlayer
|
|
||||||
1.4.0 - persistence support
|
|
||||||
- better unit-->static switch support for generic type kill
|
|
||||||
1.5.0 - added feats to score
|
|
||||||
- feats API
|
|
||||||
- logFeatForPlayer(playerName, theFeat, coa)
|
|
||||||
1.5.1 - init feats before reading
|
|
||||||
2.0.0 - sound name for good and bad corrected
|
|
||||||
- scoreOnly option
|
|
||||||
- saveScore? input in config
|
|
||||||
- incremental option in config for save
|
|
||||||
- preProcessor() added land and birth for players
|
|
||||||
- addSafeZone()
|
|
||||||
- landing score possible as feat
|
|
||||||
- also detect landing and birth events for players
|
|
||||||
- birth zeroes deferred scores and feats
|
|
||||||
- delayAfterLanding property
|
|
||||||
- delayBetweenLandings to filter landings and
|
|
||||||
space them properly from take offs
|
|
||||||
- ranking option
|
|
||||||
- scoreOnly option
|
|
||||||
- scoreFileName option
|
|
||||||
- update() loop
|
|
||||||
- read "scoreSafe" zones
|
|
||||||
- scoreaccu in playerScore
|
|
||||||
- landing feat added, enabled with landing > 0 score attribute
|
|
||||||
- delayed awards after landing
|
|
||||||
- save score to file
|
|
||||||
- show score to all
|
|
||||||
- feat zones
|
|
||||||
- awardLimit - feats can be limited in number total
|
|
||||||
- wildcard support for feature
|
|
||||||
- kill zones - limit kill score awards to inside zones
|
|
||||||
- feats can be limited to once per player
|
|
||||||
- persistence: featNum
|
|
||||||
- persistence: awardedTo
|
|
||||||
- always schedule
|
|
||||||
- hasAward logic
|
|
||||||
- unit2player
|
|
||||||
- detect player plane death
|
|
||||||
- ffMod attribute
|
|
||||||
- pkMod attribute
|
|
||||||
- pvp feat
|
|
||||||
- immediate awarding of all negative scores, even if deferred
|
|
||||||
2.0.1 - corrected access to nowString()
|
|
||||||
- more robust config reading
|
|
||||||
2.1.0 - coalition score
|
|
||||||
- reportCoalition switch
|
|
||||||
- persist coalition score
|
|
||||||
- add score to coalition when scoring player
|
|
||||||
2.1.1 - check ownership of scoreSafe zone upon touch-down
|
|
||||||
- new scoreSummaryForPlayersOfCoalition()
|
|
||||||
- new noGrief option in config
|
|
||||||
- improved guards when checking ownership (nil zone owner)
|
|
||||||
2.2.0 - score flags for red and blue
|
|
||||||
3.0.0 - dmlFlags OOP
|
3.0.0 - dmlFlags OOP
|
||||||
- redScore#
|
- redScore#
|
||||||
- blueScore#
|
- blueScore#
|
||||||
- sceneryObject detection improvements
|
- sceneryObject detection improvements
|
||||||
- DCS 2.9 safe
|
- DCS 2.9 safe
|
||||||
|
3.0.1 - cleanup
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
@ -109,12 +35,6 @@ cfxPlayerScore.killZones = {} -- when set, kills only count here
|
|||||||
cfxPlayerScore.typeScore = {}
|
cfxPlayerScore.typeScore = {}
|
||||||
cfxPlayerScore.lastPlayerLanding = {} -- timestamp, by player name
|
cfxPlayerScore.lastPlayerLanding = {} -- timestamp, by player name
|
||||||
cfxPlayerScore.delayBetweenLandings = 30 -- seconds to count as separate landings, also set during take-off to prevent janky t/o to count.
|
cfxPlayerScore.delayBetweenLandings = 30 -- seconds to count as separate landings, also set during take-off to prevent janky t/o to count.
|
||||||
--
|
|
||||||
-- we subscribe to the kill event. each time a unit
|
|
||||||
-- is killed, we check if it was killed by a player
|
|
||||||
-- and if so, that player record is updated and the side
|
|
||||||
-- whom the player belongs to is informed
|
|
||||||
--
|
|
||||||
cfxPlayerScore.aircraft = 50
|
cfxPlayerScore.aircraft = 50
|
||||||
cfxPlayerScore.helo = 40
|
cfxPlayerScore.helo = 40
|
||||||
cfxPlayerScore.ground = 10
|
cfxPlayerScore.ground = 10
|
||||||
@ -655,7 +575,6 @@ function cfxPlayerScore.linkUnitWithPlayer(theUnit)
|
|||||||
local uName = theUnit:getName()
|
local uName = theUnit:getName()
|
||||||
local pName = theUnit:getPlayerName()
|
local pName = theUnit:getPlayerName()
|
||||||
cfxPlayerScore.unit2player[uName] = pName
|
cfxPlayerScore.unit2player[uName] = pName
|
||||||
--cfxPlayerScore.player2unit[pName] = uName -- is this needed?
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function cfxPlayerScore.unlinkUnit(theUnit)
|
function cfxPlayerScore.unlinkUnit(theUnit)
|
||||||
@ -790,7 +709,6 @@ function cfxPlayerScore.checkKillFeat(name, killer, victim, fratricide)
|
|||||||
local killFeats = cfxPlayerScore.featsForLocation(name, theLoc, coa,"KILL", killer, victim)
|
local killFeats = cfxPlayerScore.featsForLocation(name, theLoc, coa,"KILL", killer, victim)
|
||||||
|
|
||||||
if (not fratricide) and #killFeats > 0 then
|
if (not fratricide) and #killFeats > 0 then
|
||||||
|
|
||||||
-- use the feat description
|
-- use the feat description
|
||||||
-- we may want to use closest, currently simply the first
|
-- we may want to use closest, currently simply the first
|
||||||
theFeatZone = killFeats[1]
|
theFeatZone = killFeats[1]
|
||||||
@ -826,7 +744,6 @@ function cfxPlayerScore.killDetected(theEvent)
|
|||||||
if wasBuilding then
|
if wasBuilding then
|
||||||
-- these objects have no coalition; we simply award the score if
|
-- these objects have no coalition; we simply award the score if
|
||||||
-- it exists in look-up table.
|
-- it exists in look-up table.
|
||||||
--trigger.action.outText("KILL SCENERY", 30)
|
|
||||||
local staticScore = cfxPlayerScore.object2score(victim)
|
local staticScore = cfxPlayerScore.object2score(victim)
|
||||||
if staticScore > 0 then
|
if staticScore > 0 then
|
||||||
trigger.action.outSoundForCoalition(killSide, cfxPlayerScore.scoreSound)
|
trigger.action.outSoundForCoalition(killSide, cfxPlayerScore.scoreSound)
|
||||||
@ -848,7 +765,6 @@ function cfxPlayerScore.killDetected(theEvent)
|
|||||||
--if not victim.getGroup then
|
--if not victim.getGroup then
|
||||||
if isStO then
|
if isStO then
|
||||||
-- static objects have no group
|
-- static objects have no group
|
||||||
|
|
||||||
local staticName = victim:getName() -- on statics, this returns
|
local staticName = victim:getName() -- on statics, this returns
|
||||||
-- name as entered in TOP LINE
|
-- name as entered in TOP LINE
|
||||||
local staticScore = cfxPlayerScore.object2score(victim)
|
local staticScore = cfxPlayerScore.object2score(victim)
|
||||||
@ -871,8 +787,6 @@ function cfxPlayerScore.killDetected(theEvent)
|
|||||||
if not fraternicide then
|
if not fraternicide then
|
||||||
cfxPlayerScore.checkKillFeat(killerName, killer, victim, false)
|
cfxPlayerScore.checkKillFeat(killerName, killer, victim, false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -881,7 +795,7 @@ function cfxPlayerScore.killDetected(theEvent)
|
|||||||
trigger.action.outText("+++scr: strange stuff:group, outta here", 30)
|
trigger.action.outText("+++scr: strange stuff:group, outta here", 30)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local vicCat = vicGroup:getCategory()
|
local vicCat = vicGroup:getCategory() -- group cat is DCS 2.9 safe
|
||||||
if not vicCat then
|
if not vicCat then
|
||||||
trigger.action.outText("+++scr: strange stuff:cat, outta here", 30)
|
trigger.action.outText("+++scr: strange stuff:cat, outta here", 30)
|
||||||
return
|
return
|
||||||
@ -889,25 +803,24 @@ function cfxPlayerScore.killDetected(theEvent)
|
|||||||
local unitScore = cfxPlayerScore.unit2score(victim)
|
local unitScore = cfxPlayerScore.unit2score(victim)
|
||||||
|
|
||||||
-- see which weapon was used. gun kills score 2x
|
-- see which weapon was used. gun kills score 2x
|
||||||
local killMeth = ""
|
-- local killMeth = "" -- meth is currently not defined
|
||||||
local killWeap = theEvent.weapon
|
-- local killWeap = theEvent.weapon -- not supported either
|
||||||
|
|
||||||
if pk then
|
if pk then -- player kill - add player's name
|
||||||
vicDesc = victim:getPlayerName() .. " in " .. vicDesc
|
vicDesc = victim:getPlayerName() .. " in " .. vicDesc
|
||||||
scoreMod = scoreMod * cfxPlayerScore.pkMod
|
scoreMod = scoreMod * cfxPlayerScore.pkMod
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- if fratricide, times ffMod (friedlyFire)
|
-- if fratricide, times ffMod (friedlyFire)
|
||||||
if fraternicide then
|
if fraternicide then
|
||||||
scoreMod = scoreMod * cfxPlayerScore.ffMod ---2
|
scoreMod = scoreMod * cfxPlayerScore.ffMod ---2
|
||||||
if cfxPlayerScore.announcer then
|
if cfxPlayerScore.announcer then
|
||||||
trigger.action.outTextForCoalition(killSide, killerName .. " in " .. killVehicle .. " killed FRIENDLY " .. vicDesc .. killMeth .. "!", 30)
|
trigger.action.outTextForCoalition(killSide, killerName .. " in " .. killVehicle .. " killed FRIENDLY " .. vicDesc .. "!", 30)
|
||||||
trigger.action.outSoundForCoalition(killSide, cfxPlayerScore.badSound)
|
trigger.action.outSoundForCoalition(killSide, cfxPlayerScore.badSound)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if cfxPlayerScore.announcer then
|
if cfxPlayerScore.announcer then
|
||||||
trigger.action.outText(killerName .. " in " .. killVehicle .." killed " .. vicDesc .. killMeth .."!", 30)
|
trigger.action.outText(killerName .. " in " .. killVehicle .." killed " .. vicDesc .. "!", 30)
|
||||||
trigger.action.outSoundForCoalition(vicSide, cfxPlayerScore.badSound)
|
trigger.action.outSoundForCoalition(vicSide, cfxPlayerScore.badSound)
|
||||||
trigger.action.outSoundForCoalition(killSide, cfxPlayerScore.scoreSound)
|
trigger.action.outSoundForCoalition(killSide, cfxPlayerScore.scoreSound)
|
||||||
end
|
end
|
||||||
@ -960,7 +873,6 @@ function cfxPlayerScore.handlePlayerLanding(theEvent)
|
|||||||
-- we may want to use closest, currently simply the first
|
-- we may want to use closest, currently simply the first
|
||||||
theFeatZone = landingFeats[1]
|
theFeatZone = landingFeats[1]
|
||||||
desc = cfxPlayerScore.evalFeatDescription(playerName, theFeatZone, thePlayerUnit) -- nil victim, defaults to player
|
desc = cfxPlayerScore.evalFeatDescription(playerName, theFeatZone, thePlayerUnit) -- nil victim, defaults to player
|
||||||
|
|
||||||
else
|
else
|
||||||
if theEvent.place then
|
if theEvent.place then
|
||||||
desc = desc .. " successfully (" .. theEvent.place:getName() .. ")"
|
desc = desc .. " successfully (" .. theEvent.place:getName() .. ")"
|
||||||
@ -1068,7 +980,6 @@ function cfxPlayerScore.scheduledAward(args)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local theScore = cfxPlayerScore.getPlayerScore(playerName)
|
local theScore = cfxPlayerScore.getPlayerScore(playerName)
|
||||||
local playerSide = dcsCommon.playerName2Coalition(playerName)
|
local playerSide = dcsCommon.playerName2Coalition(playerName)
|
||||||
if playerSide < 1 then
|
if playerSide < 1 then
|
||||||
@ -1214,59 +1125,58 @@ function cfxPlayerScore.handlePlayerEvent(theEvent)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function cfxPlayerScore.readConfigZone(theZone)
|
function cfxPlayerScore.readConfigZone(theZone)
|
||||||
cfxPlayerScore.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
cfxPlayerScore.verbose = theZone.verbose
|
||||||
-- default scores
|
-- default scores
|
||||||
cfxPlayerScore.aircraft = cfxZones.getNumberFromZoneProperty(theZone, "aircraft", 50)
|
cfxPlayerScore.aircraft = theZone:getNumberFromZoneProperty("aircraft", 50)
|
||||||
cfxPlayerScore.helo = cfxZones.getNumberFromZoneProperty(theZone, "helo", 40)
|
cfxPlayerScore.helo = theZone:getNumberFromZoneProperty("helo", 40)
|
||||||
cfxPlayerScore.ground = cfxZones.getNumberFromZoneProperty(theZone, "ground", 10)
|
cfxPlayerScore.ground = theZone:getNumberFromZoneProperty("ground", 10)
|
||||||
cfxPlayerScore.ship = cfxZones.getNumberFromZoneProperty(theZone, "ship", 80)
|
cfxPlayerScore.ship = theZone:getNumberFromZoneProperty("ship", 80)
|
||||||
cfxPlayerScore.train = cfxZones.getNumberFromZoneProperty(theZone, "train", 5)
|
cfxPlayerScore.train = theZone:getNumberFromZoneProperty( "train", 5)
|
||||||
cfxPlayerScore.landing = cfxZones.getNumberFromZoneProperty(theZone, "landing", 0) -- if > 0 then feat
|
cfxPlayerScore.landing = theZone:getNumberFromZoneProperty("landing", 0) -- if > 0 then feat
|
||||||
|
|
||||||
cfxPlayerScore.pkMod = cfxZones.getNumberFromZoneProperty(theZone, "pkMod", 1) -- factor for killing a player
|
cfxPlayerScore.pkMod = theZone:getNumberFromZoneProperty( "pkMod", 1) -- factor for killing a player
|
||||||
cfxPlayerScore.ffMod = cfxZones.getNumberFromZoneProperty(theZone, "ffMod", -2) -- factor for friendly fire
|
cfxPlayerScore.ffMod = theZone:getNumberFromZoneProperty( "ffMod", -2) -- factor for friendly fire
|
||||||
cfxPlayerScore.planeLoss = cfxZones.getNumberFromZoneProperty(theZone, "planeLoss", -10) -- points added when player's plane crashes
|
cfxPlayerScore.planeLoss = theZone:getNumberFromZoneProperty("planeLoss", -10) -- points added when player's plane crashes
|
||||||
|
|
||||||
cfxPlayerScore.announcer = cfxZones.getBoolFromZoneProperty(theZone, "announcer", true)
|
cfxPlayerScore.announcer = theZone:getBoolFromZoneProperty("announcer", true)
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "badSound") then
|
if theZone:hasProperty("badSound") then
|
||||||
cfxPlayerScore.badSound = cfxZones.getStringFromZoneProperty(theZone, "badSound", "<nosound>")
|
cfxPlayerScore.badSound = theZone:getStringFromZoneProperty("badSound", "<nosound>")
|
||||||
end
|
end
|
||||||
if cfxZones.hasProperty(theZone, "scoreSound") then
|
if theZone:hasProperty("scoreSound") then
|
||||||
cfxPlayerScore.scoreSound = cfxZones.getStringFromZoneProperty(theZone, "scoreSound", "<nosound>")
|
cfxPlayerScore.scoreSound = theZone:getStringFromZoneProperty("scoreSound", "<nosound>")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- triggering saving scores
|
-- triggering saving scores
|
||||||
if cfxZones.hasProperty(theZone, "saveScore?") then
|
if theZone:hasProperty("saveScore?") then
|
||||||
cfxPlayerScore.saveScore = cfxZones.getStringFromZoneProperty(theZone, "saveScore?", "none")
|
cfxPlayerScore.saveScore = theZone:getStringFromZoneProperty("saveScore?", "none")
|
||||||
cfxPlayerScore.lastSaveScore = trigger.misc.getUserFlag(cfxPlayerScore.saveScore)
|
cfxPlayerScore.lastSaveScore = trigger.misc.getUserFlag(cfxPlayerScore.saveScore)
|
||||||
cfxPlayerScore.incremental = cfxZones.getBoolFromZoneProperty(theZone, "incremental", false) -- incremental saves
|
cfxPlayerScore.incremental = theZone:getBoolFromZoneProperty("incremental", false) -- incremental saves
|
||||||
end
|
end
|
||||||
|
|
||||||
-- triggering show all scores
|
-- triggering show all scores
|
||||||
if cfxZones.hasProperty(theZone, "showScore?") then
|
if theZone:hasProperty("showScore?") then
|
||||||
cfxPlayerScore.showScore = cfxZones.getStringFromZoneProperty(theZone, "showScore?", "none")
|
cfxPlayerScore.showScore = theZone:getStringFromZoneProperty("showScore?", "none")
|
||||||
cfxPlayerScore.lastShowScore = trigger.misc.getUserFlag(cfxPlayerScore.showScore)
|
cfxPlayerScore.lastShowScore = trigger.misc.getUserFlag(cfxPlayerScore.showScore)
|
||||||
end
|
end
|
||||||
|
|
||||||
cfxPlayerScore.rankPlayers = cfxZones.getBoolFromZoneProperty(theZone, "rankPlayers", false)
|
cfxPlayerScore.rankPlayers = theZone:getBoolFromZoneProperty("rankPlayers", false)
|
||||||
|
|
||||||
cfxPlayerScore.scoreOnly = cfxZones.getBoolFromZoneProperty(theZone, "scoreOnly", true)
|
cfxPlayerScore.scoreOnly = theZone:getBoolFromZoneProperty("scoreOnly", true)
|
||||||
|
|
||||||
cfxPlayerScore.deferred = cfxZones.getBoolFromZoneProperty(theZone, "deferred", false)
|
cfxPlayerScore.deferred = theZone:getBoolFromZoneProperty("deferred", false)
|
||||||
|
|
||||||
cfxPlayerScore.delayAfterLanding = cfxZones.getNumberFromZoneProperty(theZone, "delayAfterLanding", 10)
|
cfxPlayerScore.delayAfterLanding = theZone:getNumberFromZoneProperty("delayAfterLanding", 10)
|
||||||
|
|
||||||
cfxPlayerScore.scoreFileName = cfxZones.getStringFromZoneProperty(theZone, "scoreFileName", "Player Scores")
|
cfxPlayerScore.scoreFileName = theZone:getStringFromZoneProperty("scoreFileName", "Player Scores")
|
||||||
|
|
||||||
cfxPlayerScore.reportScore = cfxZones.getBoolFromZoneProperty(theZone, "reportScore", true)
|
cfxPlayerScore.reportScore = theZone:getBoolFromZoneProperty("reportScore", true)
|
||||||
|
|
||||||
cfxPlayerScore.reportFeats = cfxZones.getBoolFromZoneProperty(theZone, "reportFeats", true)
|
cfxPlayerScore.reportFeats = theZone:getBoolFromZoneProperty("reportFeats", true)
|
||||||
|
|
||||||
cfxPlayerScore.reportCoalition = cfxZones.getBoolFromZoneProperty(
|
cfxPlayerScore.reportCoalition = theZone:getBoolFromZoneProperty("reportCoalition", false) -- also show coalition score
|
||||||
theZone, "reportCoalition", false) -- also show coalition score
|
|
||||||
|
|
||||||
cfxPlayerScore.noGrief = cfxZones.getBoolFromZoneProperty(theZone, "noGrief", true) -- noGrief = only add positive score
|
cfxPlayerScore.noGrief = theZone:getBoolFromZoneProperty( "noGrief", true) -- noGrief = only add positive score
|
||||||
|
|
||||||
if theZone:hasProperty("redScore#") then
|
if theZone:hasProperty("redScore#") then
|
||||||
cfxPlayerScore.redScoreOut = theZone:getStringFromZoneProperty("redScore#")
|
cfxPlayerScore.redScoreOut = theZone:getStringFromZoneProperty("redScore#")
|
||||||
@ -10,6 +10,7 @@ cfxPlayerScoreUI.verbose = false
|
|||||||
- 2.1.0 - soundfile cleanup
|
- 2.1.0 - soundfile cleanup
|
||||||
- score summary for side
|
- score summary for side
|
||||||
- allowAll
|
- allowAll
|
||||||
|
- 2.1.1 - minor cleanup
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
cfxPlayerScoreUI.requiredLibs = {
|
cfxPlayerScoreUI.requiredLibs = {
|
||||||
@ -116,7 +117,7 @@ function cfxPlayerScoreUI.start()
|
|||||||
world.addEventHandler(cfxPlayerScoreUI)
|
world.addEventHandler(cfxPlayerScoreUI)
|
||||||
-- process all existing players (late start)
|
-- process all existing players (late start)
|
||||||
dcsCommon.iteratePlayers(cfxPlayerScore.processPlayerUnit)
|
dcsCommon.iteratePlayers(cfxPlayerScore.processPlayerUnit)
|
||||||
trigger.action.outText("cf/x cfxPlayerScoreUI v" .. cfxPlayerScoreUI.version .. " started", 30)
|
trigger.action.outText("cf/x PlayerScoreUI v" .. cfxPlayerScoreUI.version .. " started", 30)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1,40 +1,39 @@
|
|||||||
playerZone = {}
|
playerZone = {}
|
||||||
playerZone.version = "1.0.0"
|
playerZone.version = "2.0.0"
|
||||||
playerZone.requiredLibs = {
|
playerZone.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon",
|
||||||
"cfxZones", -- Zones, of course
|
"cfxZones",
|
||||||
}
|
}
|
||||||
playerZone.playerZones = {}
|
playerZone.playerZones = {}
|
||||||
--[[--
|
--[[--
|
||||||
Version History
|
Version History
|
||||||
1.0.0 - Initial version
|
1.0.0 - Initial version
|
||||||
1.0.1 - pNum --> pNum#
|
1.0.1 - pNum --> pNum#
|
||||||
|
2.0.0 - dmlZones
|
||||||
|
red#, blue#, total#
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
function playerZone.createPlayerZone(theZone)
|
function playerZone.createPlayerZone(theZone)
|
||||||
-- start val - a range
|
-- start val - a range
|
||||||
theZone.pzCoalition = cfxZones.getCoalitionFromZoneProperty(theZone, "playerZone", 0)
|
theZone.pzCoalition = theZone:getCoalitionFromZoneProperty( "playerZone", 0)
|
||||||
|
|
||||||
|
|
||||||
-- Method for outputs
|
-- Method for outputs
|
||||||
theZone.pzMethod = cfxZones.getStringFromZoneProperty(theZone, "method", "inc")
|
|
||||||
if cfxZones.hasProperty(theZone, "pzMethod") then
|
theZone.pzMethod = theZone:getStringFromZoneProperty("method", "inc")
|
||||||
theZone.pzMethod = cfxZones.getStringFromZoneProperty(theZone, "pwMethod", "inc")
|
if theZone:hasProperty("pzMethod") then
|
||||||
|
theZone.pzMethod = theZone:getStringFromZoneProperty("pwMethod", "inc")
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "pNum#") then
|
if theZone:hasProperty("pNum#") then
|
||||||
theZone.pNum = cfxZones.getStringFromZoneProperty(theZone, "pNum#", "none")
|
theZone.pNum = theZone:getStringFromZoneProperty("pNum#", "none")
|
||||||
elseif cfxZones.hasProperty(theZone, "pNum") then
|
|
||||||
theZone.pNum = cfxZones.getStringFromZoneProperty(theZone, "pNum", "none")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "added!") then
|
if theZone:hasProperty("added!") then
|
||||||
theZone.pAdd = cfxZones.getStringFromZoneProperty(theZone, "added!", "none")
|
theZone.pAdd = theZone:getStringFromZoneProperty("added!", "none")
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "gone!") then
|
if theZone:hasProperty("gone!") then
|
||||||
theZone.pRemove = cfxZones.getStringFromZoneProperty(theZone, "gone!", "none")
|
theZone.pRemove = theZone:getStringFromZoneProperty("gone!", "none")
|
||||||
end
|
end
|
||||||
|
|
||||||
theZone.playersInZone = {} -- indexed by unit name
|
theZone.playersInZone = {} -- indexed by unit name
|
||||||
@ -49,7 +48,7 @@ function playerZone.collectPlayersForZone(theZone)
|
|||||||
local allPlayers = coalition.getPlayers(f)
|
local allPlayers = coalition.getPlayers(f)
|
||||||
for idy, theUnit in pairs (allPlayers) do
|
for idy, theUnit in pairs (allPlayers) do
|
||||||
local loc = theUnit:getPoint()
|
local loc = theUnit:getPoint()
|
||||||
if cfxZones.pointInZone(loc, theZone) then
|
if theZone:pointInZone(loc) then
|
||||||
zonePlayers[theUnit:getName()] = theUnit
|
zonePlayers[theUnit:getName()] = theUnit
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -86,21 +85,21 @@ function playerZone.processZone(theZone)
|
|||||||
|
|
||||||
-- flag handling and banging
|
-- flag handling and banging
|
||||||
if theZone.pNum then
|
if theZone.pNum then
|
||||||
cfxZones.setFlagValueMult(theZone.pNum, newCount, theZone)
|
theZone:setFlagValue(theZone.pNum, newCount)
|
||||||
end
|
end
|
||||||
|
|
||||||
if theZone.pAdd and hasNew then
|
if theZone.pAdd and hasNew then
|
||||||
if theZone.verbose or playerZone.verbose then
|
if theZone.verbose or playerZone.verbose then
|
||||||
trigger.action.outText("+++pZone: banging <" .. theZone.name .. ">'s 'added!' flags <" .. theZone.pAdd .. ">", 30)
|
trigger.action.outText("+++pZone: banging <" .. theZone.name .. ">'s 'added!' flags <" .. theZone.pAdd .. ">", 30)
|
||||||
end
|
end
|
||||||
cfxZones.pollFlag(theZone.pAdd, theZone.pzMethod, theZone)
|
theZone:pollFlag(theZone.pAdd, theZone.pzMethod)
|
||||||
end
|
end
|
||||||
|
|
||||||
if theZone.pRemove and hasGone then
|
if theZone.pRemove and hasGone then
|
||||||
if theZone.verbose or playerZone.verbose then
|
if theZone.verbose or playerZone.verbose then
|
||||||
trigger.action.outText("+++pZone: banging <" .. theZone.name .. ">'s 'gone' flags <" .. theZone.pRemove .. ">", 30)
|
trigger.action.outText("+++pZone: banging <" .. theZone.name .. ">'s 'gone' flags <" .. theZone.pRemove .. ">", 30)
|
||||||
end
|
end
|
||||||
cfxZones.pollFlag(theZone.pAdd, theZone.pzMethod, theZone)
|
theZone:pollFlag(theZone.pAdd, theZone.pzMethod)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -113,6 +112,25 @@ function playerZone.update()
|
|||||||
|
|
||||||
-- iterate all zones and check them
|
-- iterate all zones and check them
|
||||||
for idx, theZone in pairs(playerZone.playerZones) do
|
for idx, theZone in pairs(playerZone.playerZones) do
|
||||||
|
local neutrals = coalition.getPlayers(0)
|
||||||
|
local neutralNum = #neutrals
|
||||||
|
local reds = coalition.getPlayers(1)
|
||||||
|
local redNum = #reds
|
||||||
|
local blues = coalition.getPlayers(2)
|
||||||
|
local blueNum = #blues
|
||||||
|
local totalNum = neutralNum + redNum + blueNum
|
||||||
|
if playerZone.neutralNum then
|
||||||
|
cfxZones.setFlagValue(playerZone.neutralNum, neutralNum, playerZone)
|
||||||
|
end
|
||||||
|
if playerZone.redNum then
|
||||||
|
cfxZones.setFlagValue(playerZone.redNum, redNum, playerZone)
|
||||||
|
end
|
||||||
|
if playerZone.blueNum then
|
||||||
|
cfxZones.setFlagValue(playerZone.blueNum, blueNum, playerZone)
|
||||||
|
end
|
||||||
|
if playerZone.totalNum then
|
||||||
|
cfxZones.setFlagValue(playerZone.totalNum, totalNum, playerZone)
|
||||||
|
end
|
||||||
playerZone.processZone(theZone)
|
playerZone.processZone(theZone)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -121,7 +139,20 @@ end
|
|||||||
-- Read Config Zone
|
-- Read Config Zone
|
||||||
--
|
--
|
||||||
function playerZone.readConfigZone(theZone)
|
function playerZone.readConfigZone(theZone)
|
||||||
|
playerZone.name = "playerZoneConfig" -- cfxZones compat
|
||||||
-- currently nothing to do
|
-- currently nothing to do
|
||||||
|
if theZone:hasProperty("red#") then
|
||||||
|
playerZone.redNum = theZone:getStringFromZoneProperty("red#", "none")
|
||||||
|
end
|
||||||
|
if theZone:hasProperty("blue#") then
|
||||||
|
playerZone.blueNum = theZone:getStringFromZoneProperty("blue#", "none")
|
||||||
|
end
|
||||||
|
if theZone:hasProperty("neutral#") then
|
||||||
|
playerZone.neutralNum = theZone:getStringFromZoneProperty("neutral#", "none")
|
||||||
|
end
|
||||||
|
if theZone:hasProperty("total#") then
|
||||||
|
playerZone.totalNum = theZone:getStringFromZoneProperty("total#", "none")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
|
|||||||
@ -11,34 +11,6 @@ pulseFlags.requiredLibs = {
|
|||||||
Copyright 2022 by Christian Franz and cf/x
|
Copyright 2022 by Christian Franz and cf/x
|
||||||
|
|
||||||
Version History
|
Version History
|
||||||
- 1.0.0 Initial version
|
|
||||||
- 1.0.1 pause behavior debugged
|
|
||||||
- 1.0.2 zero pulse optional initial pulse suppress
|
|
||||||
- 1.0.3 pollFlag switched to cfxZones
|
|
||||||
uses randomDelayFromPositiveRange
|
|
||||||
flag! now is string
|
|
||||||
WARNING: still needs full alphaNum flag upgrade
|
|
||||||
- 1.1.0 Full DML flag integration
|
|
||||||
removed zone!
|
|
||||||
made pulse and pulse! the out flag carrier
|
|
||||||
done!
|
|
||||||
pulsesDone! synonym
|
|
||||||
pausePulse? synonym
|
|
||||||
pulseMethod synonym
|
|
||||||
startPulse? synonym
|
|
||||||
pulseStopped synonym
|
|
||||||
- 1.2.0 DML Watchflag integration
|
|
||||||
corrected bug in loading last pulse value for paused
|
|
||||||
- 1.2.1 pulseInterval synonym for time
|
|
||||||
pulses now supports range
|
|
||||||
zone-local verbosity
|
|
||||||
- 1.2.2 outputMethod synonym
|
|
||||||
- 1.2.3 deprecated paused/pulsePaused
|
|
||||||
returned onStart, defaulting to true
|
|
||||||
- 1.3.0 persistence
|
|
||||||
- 1.3.1 typos corrected
|
|
||||||
- 1.3.2 removed last pulse's timeID upon entry in doPulse
|
|
||||||
- 1.3.3 removed 'pulsing' when pausing, so we can restart
|
|
||||||
- 2.0.0 dmlZones / OOP
|
- 2.0.0 dmlZones / OOP
|
||||||
using method on all outputs
|
using method on all outputs
|
||||||
- 2.0.1 activateZoneFlag now works correctly
|
- 2.0.1 activateZoneFlag now works correctly
|
||||||
@ -165,7 +137,7 @@ function pulseFlags.doPulse(args)
|
|||||||
-- first, we only do an initial pulse if zeroPulse is set
|
-- first, we only do an initial pulse if zeroPulse is set
|
||||||
if theZone.hasPulsed or theZone.zeroPulse then
|
if theZone.hasPulsed or theZone.zeroPulse then
|
||||||
if pulseFlags.verbose or theZone.verbose then
|
if pulseFlags.verbose or theZone.verbose then
|
||||||
trigger.action.outText("+++pulF: will bang " .. theZone.pulseFlag, 30);
|
trigger.action.outText("+++pulF: will bang " .. theZone.pulseFlag .. " for <" .. theZone.name .. ">", 30);
|
||||||
end
|
end
|
||||||
|
|
||||||
theZone:pollFlag(theZone.pulseFlag, theZone.pulseMethod)
|
theZone:pollFlag(theZone.pulseFlag, theZone.pulseMethod)
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
radioMenu = {}
|
radioMenu = {}
|
||||||
radioMenu.version = "2.1.1"
|
radioMenu.version = "2.2.0"
|
||||||
radioMenu.verbose = false
|
radioMenu.verbose = false
|
||||||
radioMenu.ups = 1
|
radioMenu.ups = 1
|
||||||
radioMenu.requiredLibs = {
|
radioMenu.requiredLibs = {
|
||||||
@ -10,21 +10,6 @@ radioMenu.menus = {}
|
|||||||
|
|
||||||
--[[--
|
--[[--
|
||||||
Version History
|
Version History
|
||||||
1.0.0 - Initial version
|
|
||||||
1.0.1 - spelling corrections
|
|
||||||
1.1.0 - removeMenu
|
|
||||||
addMenu
|
|
||||||
menuVisible
|
|
||||||
2.0.0 - redesign: handles multiple receivers
|
|
||||||
optional MX module
|
|
||||||
group option
|
|
||||||
type option
|
|
||||||
multiple group names
|
|
||||||
multiple types
|
|
||||||
gereric helo type
|
|
||||||
generic plane type
|
|
||||||
type works with coalition
|
|
||||||
2.0.1 - corrections to installMenu(), as suggested by GumidekCZ
|
|
||||||
2.1.0 - valA/valB/valC/valD attributes
|
2.1.0 - valA/valB/valC/valD attributes
|
||||||
OOP cfxZones
|
OOP cfxZones
|
||||||
corrected CD setting for "D"
|
corrected CD setting for "D"
|
||||||
@ -32,6 +17,7 @@ radioMenu.menus = {}
|
|||||||
valA-D now define full method, not just values
|
valA-D now define full method, not just values
|
||||||
full wildcard support for ack and cooldown
|
full wildcard support for ack and cooldown
|
||||||
2.1.1 - outMessage now works correctly
|
2.1.1 - outMessage now works correctly
|
||||||
|
2.2.0 - clean-up
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
function radioMenu.addRadioMenu(theZone)
|
function radioMenu.addRadioMenu(theZone)
|
||||||
@ -63,8 +49,7 @@ function radioMenu.filterPlayerIDForType(theZone)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- now iterate all types, and include any player that matches
|
-- now iterate all types, and include any player that matches
|
||||||
-- note that a player may match twice, so we use a dict instead of an
|
-- note that players may match twice, so we use a dict
|
||||||
-- array. Since we later iterate ID by idx, that's not an issue
|
|
||||||
|
|
||||||
for idx, aType in pairs(allTypes) do
|
for idx, aType in pairs(allTypes) do
|
||||||
local theType = dcsCommon.trim(aType)
|
local theType = dcsCommon.trim(aType)
|
||||||
@ -145,7 +130,6 @@ function radioMenu.filterPlayerIDForGroup(theZone)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function radioMenu.installMenu(theZone)
|
function radioMenu.installMenu(theZone)
|
||||||
-- local theGroup = 0 -- was: nil
|
|
||||||
local gID = nil
|
local gID = nil
|
||||||
if theZone.menuGroup then
|
if theZone.menuGroup then
|
||||||
if not cfxMX then
|
if not cfxMX then
|
||||||
@ -570,6 +554,5 @@ if not radioMenu.start() then
|
|||||||
end
|
end
|
||||||
|
|
||||||
--[[--
|
--[[--
|
||||||
callbacks for the menus
|
|
||||||
check CD/standby code for multiple groups
|
check CD/standby code for multiple groups
|
||||||
--]]--
|
--]]--
|
||||||
@ -1,25 +1,12 @@
|
|||||||
cfxSmokeZone = {}
|
cfxSmokeZone = {}
|
||||||
cfxSmokeZone.version = "1.2.0"
|
cfxSmokeZone.version = "2.0.0"
|
||||||
cfxSmokeZone.requiredLibs = {
|
cfxSmokeZone.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
"cfxZones", -- Zones, of course
|
"cfxZones", -- Zones, of course
|
||||||
}
|
}
|
||||||
--[[--
|
--[[--
|
||||||
Version History
|
Version History
|
||||||
1.0.0 - initial version
|
2.0.0 - clean up
|
||||||
1.0.1 - added removeSmokeZone
|
|
||||||
1.0.2 - added altitude
|
|
||||||
1.0.3 - added paused attribute
|
|
||||||
- added f? attribute --> onFlag
|
|
||||||
- broke out startSmoke
|
|
||||||
1.0.4 - startSmoke? synonym
|
|
||||||
- alphanum DML flag upgrade
|
|
||||||
- random color support
|
|
||||||
1.1.0 - Watchflag upgrade
|
|
||||||
1.1.1 - stopSmoke? input
|
|
||||||
1.1.2 - 'agl', 'alt' synonymous for altitude to keep in line with fireFX
|
|
||||||
1.1.3 - corrected smokeTriggerMethod in zone definition
|
|
||||||
1.2.0 - first OOP guinea pig.
|
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
cfxSmokeZone.smokeZones = {}
|
cfxSmokeZone.smokeZones = {}
|
||||||
@ -48,7 +35,6 @@ function cfxSmokeZone.processSmokeZone(aZone)
|
|||||||
-- paused
|
-- paused
|
||||||
aZone.paused = aZone:getBoolFromZoneProperty("paused", false)
|
aZone.paused = aZone:getBoolFromZoneProperty("paused", false)
|
||||||
|
|
||||||
-- f? query flags
|
|
||||||
if aZone:hasProperty("f?") then
|
if aZone:hasProperty("f?") then
|
||||||
aZone.onFlag = aZone:getStringFromZoneProperty("f?", "*<none>")
|
aZone.onFlag = aZone:getStringFromZoneProperty("f?", "*<none>")
|
||||||
elseif aZone:hasProperty("startSmoke?") then
|
elseif aZone:hasProperty("startSmoke?") then
|
||||||
@ -64,8 +50,6 @@ function cfxSmokeZone.processSmokeZone(aZone)
|
|||||||
aZone.smkLastStopFlag = aZone:getFlagValue(aZone.smkStopFlag)
|
aZone.smkLastStopFlag = aZone:getFlagValue(aZone.smkStopFlag)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- watchflags:
|
|
||||||
-- triggerMethod
|
|
||||||
aZone.smokeTriggerMethod = aZone:getStringFromZoneProperty( "triggerMethod", "change")
|
aZone.smokeTriggerMethod = aZone:getStringFromZoneProperty( "triggerMethod", "change")
|
||||||
|
|
||||||
if aZone:hasProperty("smokeTriggerMethod") then
|
if aZone:hasProperty("smokeTriggerMethod") then
|
||||||
@ -165,14 +149,10 @@ function cfxSmokeZone.start()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- collect all zones with 'smoke' attribute
|
-- collect all zones with 'smoke' attribute
|
||||||
-- collect all spawn zones
|
|
||||||
local attrZones = cfxZones.getZonesWithAttributeNamed("smoke")
|
local attrZones = cfxZones.getZonesWithAttributeNamed("smoke")
|
||||||
|
|
||||||
-- now create a smoker for all, add them to updater,
|
|
||||||
-- smoke all that aren't paused
|
|
||||||
for k, aZone in pairs(attrZones) do
|
for k, aZone in pairs(attrZones) do
|
||||||
cfxSmokeZone.processSmokeZone(aZone) -- process attribute and add to zone
|
cfxSmokeZone.processSmokeZone(aZone)
|
||||||
cfxSmokeZone.addSmokeZone(aZone) -- remember it so we can smoke it
|
cfxSmokeZone.addSmokeZone(aZone)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- start update loop
|
-- start update loop
|
||||||
@ -22,52 +22,6 @@ cfxSpawnZones.spawnedGroups = {}
|
|||||||
--
|
--
|
||||||
--[[--
|
--[[--
|
||||||
-- version history
|
-- version history
|
||||||
1.3.0
|
|
||||||
- maxSpawn
|
|
||||||
- orders
|
|
||||||
- range
|
|
||||||
1.3.1 - spawnWithSpawner correct translation of country to coalition
|
|
||||||
- createSpawner - corrected reading from properties
|
|
||||||
1.3.2 - createSpawner - correct reading 'owner' from properties, now
|
|
||||||
directly reads coalition
|
|
||||||
1.4.0 - checks modules
|
|
||||||
- orders 'train' or 'training' - will make the
|
|
||||||
ground troops be issued HOLD WEAPS and
|
|
||||||
not added to any queue. 'Training' troops
|
|
||||||
are target dummies.
|
|
||||||
- optional heading attribute
|
|
||||||
- typeMult: repeate type this many time (can produce army in one call)
|
|
||||||
1.4.1 - 'requestable' attribute. will automatically set zone to
|
|
||||||
- paused, so troops can be produced on call
|
|
||||||
- getRequestableSpawnersInRange
|
|
||||||
1.4.2 - target attribute. used for
|
|
||||||
- orders: attackZone
|
|
||||||
- spawner internally copies name from cfxZone used for spawning (convenience only)
|
|
||||||
1.4.3 - can subscribe to callbacks. currently called when spawnForSpawner is invoked, reason is "spawned"
|
|
||||||
- masterOwner to link ownership to other zone
|
|
||||||
1.4.4 - autoRemove flag to instantly start CD and respawn
|
|
||||||
1.4.5 - verify that maxSpawns ~= 0 on initial spawn on start-up
|
|
||||||
1.4.6 - getSpawnerForZoneNamed(aName)
|
|
||||||
- nil-trapping orders before testing for 'training'
|
|
||||||
1.4.7 - defaulting orders to 'guard'
|
|
||||||
- also accept 'dummy' and 'dummies' as substitute for training
|
|
||||||
1.4.8 - spawnWithSpawner uses getPoint to support linked spawn zones
|
|
||||||
- update spawn count on initial spawn
|
|
||||||
1.5.0 - f? support to trigger spawn
|
|
||||||
- spawnWithSpawner made string compatible
|
|
||||||
1.5.1 - relaxed baseName and default to dcsCommon.uuid()
|
|
||||||
- verbose
|
|
||||||
1.5.2 - activate?, pause? flag
|
|
||||||
1.5.3 - spawn?, spawnUnits? flags
|
|
||||||
1.6.0 - trackwith interface for group tracker
|
|
||||||
1.7.0 - persistence support
|
|
||||||
1.7.1 - improved verbosity
|
|
||||||
- spelling check
|
|
||||||
1.7.2 - baseName now can can be set to zone name by issuing "*"
|
|
||||||
1.7.3 - ability to hand off to delicates, useDelicates attribute
|
|
||||||
1.7.4 - wait-attackZone fixes
|
|
||||||
1.7.5 - improved verbosity on spawning
|
|
||||||
- getRequestableSpawnersInRange() ignores height for distance
|
|
||||||
2.0.0 - dmlZones
|
2.0.0 - dmlZones
|
||||||
- moved "types" to spawner
|
- moved "types" to spawner
|
||||||
- baseName defaults to zone name, as it is safe for naming
|
- baseName defaults to zone name, as it is safe for naming
|
||||||
@ -324,6 +324,9 @@ function stopGap.update()
|
|||||||
busy = true
|
busy = true
|
||||||
-- count up for auto-release after n seconds
|
-- count up for auto-release after n seconds
|
||||||
trigger.action.setUserFlag(theGroup.sgName, sgState + 1)
|
trigger.action.setUserFlag(theGroup.sgName, sgState + 1)
|
||||||
|
if stopGap.verbose then
|
||||||
|
trigger.action.outText("+++StopG: [cooldown] cooldown for group <" .. name .. ">, val now is <" .. sgState .. ">.", 30)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if busy then
|
if busy then
|
||||||
@ -331,6 +334,9 @@ function stopGap.update()
|
|||||||
else
|
else
|
||||||
local theStaticGroup = stopGap.createStandInsForMXGroup(theGroup)
|
local theStaticGroup = stopGap.createStandInsForMXGroup(theGroup)
|
||||||
stopGap.standInGroups[name] = theStaticGroup
|
stopGap.standInGroups[name] = theStaticGroup
|
||||||
|
if stopGap.verbose then
|
||||||
|
trigger.action.outText("+++StopG: [server command] placing static stand-in for group <" .. name .. ">.", 30)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- plane is currently static and visible
|
-- plane is currently static and visible
|
||||||
|
|||||||
@ -52,7 +52,7 @@ stopGap.requiredLibs = {
|
|||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
stopGap.standInGroups = {}
|
stopGap.standInGroups = {} -- idx by name, if set has a static
|
||||||
stopGap.myGroups = {} -- for fast look-up of mx orig data
|
stopGap.myGroups = {} -- for fast look-up of mx orig data
|
||||||
stopGap.stopGapZones = {} -- DML only
|
stopGap.stopGapZones = {} -- DML only
|
||||||
|
|
||||||
|
|||||||
@ -12,24 +12,27 @@ debugger.log = ""
|
|||||||
|
|
||||||
--[[--
|
--[[--
|
||||||
Version History
|
Version History
|
||||||
1.0.0 - Initial version
|
|
||||||
1.0.1 - made ups available to config zone
|
|
||||||
- changed 'on' to 'active' in config zone
|
|
||||||
- merged debugger and debugDemon
|
|
||||||
- QoL check for 'debug' attribute (no '?')
|
|
||||||
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
|
|
||||||
1.1.2 - remove command
|
|
||||||
2.0.0 - dmlZones OOP
|
2.0.0 - dmlZones OOP
|
||||||
- eventmon command
|
- eventmon command
|
||||||
- all, off, event #
|
- eventmon all, off, event #
|
||||||
- standard events
|
- standard events
|
||||||
- adding events
|
- adding events via #
|
||||||
- events? attribute from any zone
|
- 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 = {
|
debugger.requiredLibs = {
|
||||||
@ -45,6 +48,7 @@ debugger.debugUnits = {}
|
|||||||
debugger.debugGroups = {}
|
debugger.debugGroups = {}
|
||||||
debugger.debugObjects = {}
|
debugger.debugObjects = {}
|
||||||
debugger.showEvents = {}
|
debugger.showEvents = {}
|
||||||
|
debugger.lastEvent = nil
|
||||||
|
|
||||||
debugDemon.eventList = {
|
debugDemon.eventList = {
|
||||||
["0"] = "S_EVENT_INVALID = 0",
|
["0"] = "S_EVENT_INVALID = 0",
|
||||||
@ -106,6 +110,26 @@ debugDemon.eventList = {
|
|||||||
["56"] = "S_EVENT_POSTPONED_LAND = 56",
|
["56"] = "S_EVENT_POSTPONED_LAND = 56",
|
||||||
["57"] = "S_EVENT_MAX = 57",
|
["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
|
-- Logging & saving
|
||||||
--
|
--
|
||||||
@ -235,7 +259,6 @@ function debugger.createEventMonWithZone(theZone)
|
|||||||
debugger.outText("*** monitoring events defined in <" .. theZone.name .. ">:", 30)
|
debugger.outText("*** monitoring events defined in <" .. theZone.name .. ">:", 30)
|
||||||
end
|
end
|
||||||
for idx, aFlag in pairs(flagArray) do
|
for idx, aFlag in pairs(flagArray) do
|
||||||
|
|
||||||
local evt = tonumber(aFlag)
|
local evt = tonumber(aFlag)
|
||||||
if evt and (debugger.verbose or theZone.verbose) then
|
if evt and (debugger.verbose or theZone.verbose) then
|
||||||
if evt < 0 then evt = 0 end
|
if evt < 0 then evt = 0 end
|
||||||
@ -288,7 +311,6 @@ function debugger.isObserving(flagName)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return observers
|
return observers
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -476,43 +498,62 @@ end
|
|||||||
function debugger.readConfigZone()
|
function debugger.readConfigZone()
|
||||||
local theZone = cfxZones.getZoneByName("debuggerConfig")
|
local theZone = cfxZones.getZoneByName("debuggerConfig")
|
||||||
if not theZone then
|
if not theZone then
|
||||||
if debugger.verbose then
|
|
||||||
debugger.outText("+++debug: NO config zone!", 30)
|
|
||||||
end
|
|
||||||
theZone = cfxZones.createSimpleZone("debuggerConfig")
|
theZone = cfxZones.createSimpleZone("debuggerConfig")
|
||||||
end
|
end
|
||||||
debugger.configZone = theZone
|
debugger.configZone = theZone
|
||||||
|
|
||||||
debugger.active = cfxZones.getBoolFromZoneProperty(theZone, "active", true)
|
debugger.active = theZone:getBoolFromZoneProperty("active", true)
|
||||||
debugger.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
debugger.verbose = theZone.verbose
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "on?") then
|
if theZone:hasProperty("on?") then
|
||||||
debugger.onFlag = cfxZones.getStringFromZoneProperty(theZone, "on?", "<none>")
|
debugger.onFlag = theZone:getStringFromZoneProperty("on?", "<none>")
|
||||||
debugger.lastOn = cfxZones.getFlagValue(debugger.onFlag, theZone)
|
debugger.lastOn = cfxZones.getFlagValue(debugger.onFlag, theZone)
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "off?") then
|
if theZone:hasProperty("off?") then
|
||||||
debugger.offFlag = cfxZones.getStringFromZoneProperty(theZone, "off?", "<none>")
|
debugger.offFlag = theZone:getStringFromZoneProperty("off?", "<none>")
|
||||||
debugger.lastOff = cfxZones.getFlagValue(debugger.offFlag, theZone)
|
debugger.lastOff = cfxZones.getFlagValue(debugger.offFlag, theZone)
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "reset?") then
|
if theZone:hasProperty("reset?") then
|
||||||
debugger.resetFlag = cfxZones.getStringFromZoneProperty(theZone, "reset?", "<none>")
|
debugger.resetFlag = theZone:getStringFromZoneProperty("reset?", "<none>")
|
||||||
debugger.lastReset = cfxZones.getFlagValue(debugger.resetFlag, theZone)
|
debugger.lastReset = cfxZones.getFlagValue(debugger.resetFlag, theZone)
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "state?") then
|
if theZone:hasProperty("state?") then
|
||||||
debugger.stateFlag = cfxZones.getStringFromZoneProperty(theZone, "state?", "<none>")
|
debugger.stateFlag = theZone:getStringFromZoneProperty("state?", "<none>")
|
||||||
debugger.lastState = cfxZones.getFlagValue(debugger.stateFlag, theZone)
|
debugger.lastState = cfxZones.getFlagValue(debugger.stateFlag, theZone)
|
||||||
end
|
end
|
||||||
|
|
||||||
debugger.ups = cfxZones.getNumberFromZoneProperty(theZone, "ups", 4)
|
debugger.ups = theZone:getNumberFromZoneProperty("ups", 4)
|
||||||
|
end
|
||||||
|
|
||||||
if debugger.verbose then
|
function debugger.readSpawnTypeZone()
|
||||||
debugger.outText("+++debug: read config", 30)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function debugger.start()
|
function debugger.start()
|
||||||
-- lib check
|
-- lib check
|
||||||
if not dcsCommon.libCheck then
|
if not dcsCommon.libCheck then
|
||||||
@ -526,6 +567,9 @@ function debugger.start()
|
|||||||
-- read config
|
-- read config
|
||||||
debugger.readConfigZone()
|
debugger.readConfigZone()
|
||||||
|
|
||||||
|
-- read spawn types
|
||||||
|
debugger.readSpawnTypeZone()
|
||||||
|
|
||||||
-- process debugger Zones
|
-- process debugger Zones
|
||||||
-- old style
|
-- old style
|
||||||
local attrZones = cfxZones.getZonesWithAttributeNamed("debug?")
|
local attrZones = cfxZones.getZonesWithAttributeNamed("debug?")
|
||||||
@ -536,7 +580,7 @@ function debugger.start()
|
|||||||
|
|
||||||
local attrZones = cfxZones.getZonesWithAttributeNamed("debug")
|
local attrZones = cfxZones.getZonesWithAttributeNamed("debug")
|
||||||
for k, aZone in pairs(attrZones) do
|
for k, aZone in pairs(attrZones) do
|
||||||
debugger.outText("***Warning: Zone <" .. aZone.name .. "> has a 'debug' flag. Are you perhaps missing a '?'", 30)
|
debugger.outText("***Warning: Zone <" .. aZone.name .. "> has a 'debug' attribute. Are you perhaps missing a '?'", 30)
|
||||||
end
|
end
|
||||||
|
|
||||||
local attrZones = cfxZones.getZonesWithAttributeNamed("events?")
|
local attrZones = cfxZones.getZonesWithAttributeNamed("events?")
|
||||||
@ -546,7 +590,7 @@ function debugger.start()
|
|||||||
|
|
||||||
local attrZones = cfxZones.getZonesWithAttributeNamed("events")
|
local attrZones = cfxZones.getZonesWithAttributeNamed("events")
|
||||||
for k, aZone in pairs(attrZones) do
|
for k, aZone in pairs(attrZones) do
|
||||||
debugger.outText("***Warning: Zone <" .. aZone.name .. "> has a 'debug' flag. Are you perhaps missing a '?'", 30)
|
debugger.outText("***Warning: Zone <" .. aZone.name .. "> has an 'events' attribute. Are you perhaps missing a '?'", 30)
|
||||||
end
|
end
|
||||||
-- events
|
-- events
|
||||||
|
|
||||||
@ -683,7 +727,7 @@ function debugDemon.getArgs(theCommands)
|
|||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
-- stage demon's main command interpreter.
|
-- debug demon's main command interpreter.
|
||||||
-- magic lies in using the keywords as keys into a
|
-- magic lies in using the keywords as keys into a
|
||||||
-- function table that holds all processing functions
|
-- function table that holds all processing functions
|
||||||
-- I wish we had that back in the Oberon days.
|
-- I wish we had that back in the Oberon days.
|
||||||
@ -743,7 +787,7 @@ debugger.outText("*** debugger: commands are:" ..
|
|||||||
"\n " .. debugDemon.markOfDemon .. "o <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 .. "forget <flagname> [with <observername>] -- stop observing a flag" ..
|
||||||
"\n " .. debugDemon.markOfDemon .. "new <observername> [[for] <condition>] -- create observer for flags" ..
|
"\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 .. "update <observername> [to] <condition> -- change observer's condition" ..
|
||||||
"\n " .. debugDemon.markOfDemon .. "drop <observername> -- remove observer from debugger" ..
|
"\n " .. debugDemon.markOfDemon .. "drop <observername> -- remove observer from debugger" ..
|
||||||
"\n " .. debugDemon.markOfDemon .. "list [<match>] -- list observers [name contains <match>]" ..
|
"\n " .. debugDemon.markOfDemon .. "list [<match>] -- list observers [name contains <match>]" ..
|
||||||
"\n " .. debugDemon.markOfDemon .. "who <flagname> -- all who observe <flagname>" ..
|
"\n " .. debugDemon.markOfDemon .. "who <flagname> -- all who observe <flagname>" ..
|
||||||
@ -752,10 +796,16 @@ debugger.outText("*** debugger: commands are:" ..
|
|||||||
"\n\n " .. debugDemon.markOfDemon .. "snap [<observername>] -- create new snapshot of flags" ..
|
"\n\n " .. debugDemon.markOfDemon .. "snap [<observername>] -- create new snapshot of flags" ..
|
||||||
"\n " .. debugDemon.markOfDemon .. "compare -- compare snapshot flag values with current" ..
|
"\n " .. debugDemon.markOfDemon .. "compare -- compare snapshot flag values with current" ..
|
||||||
"\n " .. debugDemon.markOfDemon .. "note <your note> -- add <your note> to the text log" ..
|
"\n " .. debugDemon.markOfDemon .. "note <your note> -- add <your note> to the text log" ..
|
||||||
"\n\n " .. debugDemon.markOfDemon .. "remove <group/unit/object name> -- remove named item from mission" ..
|
"\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\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\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 " .. debugDemon.markOfDemon .. "w <Lua Var> [=] <Lua Value> -- Write <Lua Value> to variable <Lua Var>" ..
|
||||||
"\n\n " .. debugDemon.markOfDemon .. "start -- starts debugger" ..
|
"\n\n " .. debugDemon.markOfDemon .. "start -- starts debugger" ..
|
||||||
"\n " .. debugDemon.markOfDemon .. "stop -- stop debugger" ..
|
"\n " .. debugDemon.markOfDemon .. "stop -- stop debugger" ..
|
||||||
@ -815,7 +865,7 @@ function debugDemon.processNewCommand(args, event)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function debugDemon.processUpdateCommand(args, event)
|
function debugDemon.processUpdateCommand(args, event)
|
||||||
-- syntax update <observername> [[to] <condition>]
|
-- syntax update <observername> [to] <condition>
|
||||||
local observerName = args[1]
|
local observerName = args[1]
|
||||||
if not observerName then
|
if not observerName then
|
||||||
debugger.outText("*** update: missing observer name.", 30)
|
debugger.outText("*** update: missing observer name.", 30)
|
||||||
@ -1271,6 +1321,7 @@ end
|
|||||||
|
|
||||||
function debugDemon.doEventMon(theEvent)
|
function debugDemon.doEventMon(theEvent)
|
||||||
if not theEvent then return end
|
if not theEvent then return end
|
||||||
|
if not debugger.active then return end
|
||||||
local ID = theEvent.id
|
local ID = theEvent.id
|
||||||
if debugger.showEvents[ID] then
|
if debugger.showEvents[ID] then
|
||||||
-- we show this event
|
-- we show this event
|
||||||
@ -1287,11 +1338,47 @@ function debugDemon.doEventMon(theEvent)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
debugger.outText(m, 30)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
function debugDemon.processEventMonCommand(args, event)
|
function debugDemon.processEventMonCommand(args, event)
|
||||||
-- turn event monito on/off
|
-- turn event monitor all/off/?/last
|
||||||
-- syntax: -eventmon on|off
|
-- syntax: -eventmon on|off
|
||||||
local aParam = dcsCommon.trim(event.remainder)
|
local aParam = dcsCommon.trim(event.remainder)
|
||||||
if not aParam or aParam:len() < 1 then
|
if not aParam or aParam:len() < 1 then
|
||||||
@ -1300,7 +1387,6 @@ function debugDemon.processEventMonCommand(args, event)
|
|||||||
aParam = string.upper(aParam)
|
aParam = string.upper(aParam)
|
||||||
evtNum = tonumber(aParam)
|
evtNum = tonumber(aParam)
|
||||||
if aParam == "ON" or aParam == "ALL" then
|
if aParam == "ON" or aParam == "ALL" then
|
||||||
-- debugger.eventmon = true
|
|
||||||
debugger.outText("*** eventmon: turned ON, showing ALL events", 30)
|
debugger.outText("*** eventmon: turned ON, showing ALL events", 30)
|
||||||
local events = {}
|
local events = {}
|
||||||
for idx,evt in pairs(debugDemon.eventList) do
|
for idx,evt in pairs(debugDemon.eventList) do
|
||||||
@ -1322,6 +1408,13 @@ function debugDemon.processEventMonCommand(args, event)
|
|||||||
m = m .. "\n" .. evt
|
m = m .. "\n" .. evt
|
||||||
end
|
end
|
||||||
debugger.outText(m .. "\n*** end of list", 30)
|
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
|
else
|
||||||
debugger.outText("*** eventmon: unknown parameter <" .. event.remainder .. ">", 30)
|
debugger.outText("*** eventmon: unknown parameter <" .. event.remainder .. ">", 30)
|
||||||
end
|
end
|
||||||
@ -1335,9 +1428,7 @@ end
|
|||||||
function debugDemon.processQueryCommand(args, event)
|
function debugDemon.processQueryCommand(args, event)
|
||||||
-- syntax -q <name> with name a (qualified) Lua table reference
|
-- syntax -q <name> with name a (qualified) Lua table reference
|
||||||
local theName = args[1]
|
local theName = args[1]
|
||||||
-- local p = args [2]
|
|
||||||
-- trigger.action.outText("args1 = " .. theName, 30)
|
|
||||||
-- if args[2] then trigger.action.outText("param = " .. args[2], 30) end
|
|
||||||
if not theName then
|
if not theName then
|
||||||
debugger.outText("*** q: missing Lua table/element name.", 30)
|
debugger.outText("*** q: missing Lua table/element name.", 30)
|
||||||
return false -- allows correction
|
return false -- allows correction
|
||||||
@ -1369,6 +1460,33 @@ function debugDemon.processQueryCommand(args, event)
|
|||||||
return true
|
return true
|
||||||
end
|
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)
|
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.
|
-- 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}
|
-- also, allow "=" semantic, -w p = {x=1, y=2}
|
||||||
@ -1402,6 +1520,324 @@ function debugDemon.processWriteCommand(args, event)
|
|||||||
return true
|
return true
|
||||||
end
|
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
|
-- init and start
|
||||||
@ -1430,6 +1866,7 @@ function debugDemon.readConfigZone()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function debugDemon.init()
|
function debugDemon.init()
|
||||||
if not dcsCommon.libCheck then
|
if not dcsCommon.libCheck then
|
||||||
trigger.action.outText("cfx interactive debugger requires dcsCommon", 30)
|
trigger.action.outText("cfx interactive debugger requires dcsCommon", 30)
|
||||||
@ -1473,10 +1910,15 @@ function debugDemon.init()
|
|||||||
debugDemon.addCommndProcessor("help", debugDemon.processHelpCommand)
|
debugDemon.addCommndProcessor("help", debugDemon.processHelpCommand)
|
||||||
|
|
||||||
debugDemon.addCommndProcessor("remove", debugDemon.processRemoveCommand)
|
debugDemon.addCommndProcessor("remove", debugDemon.processRemoveCommand)
|
||||||
|
debugDemon.addCommndProcessor("spawn", debugDemon.processSpawnCommand)
|
||||||
|
debugDemon.addCommndProcessor("add", debugDemon.processSpawnCommand)
|
||||||
|
|
||||||
debugDemon.addCommndProcessor("eventmon", debugDemon.processEventMonCommand)
|
debugDemon.addCommndProcessor("eventmon", debugDemon.processEventMonCommand)
|
||||||
debugDemon.addCommndProcessor("q", debugDemon.processQueryCommand)
|
debugDemon.addCommndProcessor("q", debugDemon.processQueryCommand)
|
||||||
debugDemon.addCommndProcessor("w", debugDemon.processWriteCommand)
|
debugDemon.addCommndProcessor("w", debugDemon.processWriteCommand)
|
||||||
|
debugDemon.addCommndProcessor("a", debugDemon.processAnalyzeCommand)
|
||||||
|
debugDemon.addCommndProcessor("smoke", debugDemon.processSmokeCommand)
|
||||||
|
debugDemon.addCommndProcessor("boom", debugDemon.processBoomCommand)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1513,13 +1955,8 @@ end
|
|||||||
|
|
||||||
- exec files. save all commands and then run them from script
|
- exec files. save all commands and then run them from script
|
||||||
|
|
||||||
- query objects: -q persistence.active returns boolean, true
|
|
||||||
-q x.y returns table, 12 elements
|
|
||||||
-q a.b.x returns number 12
|
|
||||||
-q d.e.f returns string "asdasda..."
|
|
||||||
-q sada returs <nil>
|
|
||||||
|
|
||||||
- xref: which zones/attributes reference a flag, g.g. '-xref go'
|
- xref: which zones/attributes reference a flag, g.g. '-xref go'
|
||||||
|
|
||||||
- dml version can config to start with events list, e.g. 1, 4, 7
|
- track lua vars for change in value
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
unitPersistence = {}
|
unitPersistence = {}
|
||||||
unitPersistence.version = '1.1.1'
|
unitPersistence.version = '2.0.0'
|
||||||
unitPersistence.verbose = false
|
unitPersistence.verbose = false
|
||||||
unitPersistence.updateTime = 60 -- seconds. Once every minute check statics
|
unitPersistence.updateTime = 60 -- seconds. Once every minute check statics
|
||||||
unitPersistence.requiredLibs = {
|
unitPersistence.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon",
|
||||||
"cfxZones", -- Zones, of course
|
"cfxZones",
|
||||||
"persistence",
|
"persistence",
|
||||||
"cfxMX",
|
"cfxMX",
|
||||||
}
|
}
|
||||||
@ -19,7 +19,8 @@ unitPersistence.requiredLibs = {
|
|||||||
1.1.0 - added air and sea units - for filtering destroyed units
|
1.1.0 - added air and sea units - for filtering destroyed units
|
||||||
1.1.1 - fixed static link (again)
|
1.1.1 - fixed static link (again)
|
||||||
- fixed air spawn (fixed wing)
|
- fixed air spawn (fixed wing)
|
||||||
|
2.0.0 - dmlZones, OOP
|
||||||
|
cleanup
|
||||||
|
|
||||||
REQUIRES PERSISTENCE AND MX
|
REQUIRES PERSISTENCE AND MX
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
williePete = {}
|
williePete = {}
|
||||||
williePete.version = "1.0.2"
|
williePete.version = "2.0.0"
|
||||||
williePete.ups = 10 -- we update at 10 fps, so accuracy of a
|
williePete.ups = 10 -- we update at 10 fps, so accuracy of a
|
||||||
-- missile moving at Mach 2 is within 33 meters,
|
-- missile moving at Mach 2 is within 33 meters,
|
||||||
-- with interpolation even at 3 meters
|
-- with interpolation even at 3 meters
|
||||||
@ -14,12 +14,16 @@ williePete.requiredLibs = {
|
|||||||
1.0.0 - Initial version
|
1.0.0 - Initial version
|
||||||
1.0.1 - update to suppress verbosity
|
1.0.1 - update to suppress verbosity
|
||||||
1.0.2 - added Gazelle WP
|
1.0.2 - added Gazelle WP
|
||||||
|
2.0.0 - dmlZones, OOP
|
||||||
|
- Guards for multi-unit player groups
|
||||||
|
- getFirstLivingPlayerInGroupNamed()
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
williePete.willies = {}
|
williePete.willies = {}
|
||||||
williePete.wpZones = {}
|
williePete.wpZones = {}
|
||||||
williePete.playerGUIs = {} -- used for unit guis
|
williePete.playerGUIs = {} -- used for unit guis
|
||||||
|
williePete.groupGUIs = {} -- because some people may want to install
|
||||||
|
-- multip-unit player groups
|
||||||
williePete.blastedObjects = {} -- used when we detonate something
|
williePete.blastedObjects = {} -- used when we detonate something
|
||||||
|
|
||||||
-- recognizes WP munitions. May require regular update when new
|
-- recognizes WP munitions. May require regular update when new
|
||||||
@ -90,30 +94,29 @@ end
|
|||||||
|
|
||||||
|
|
||||||
function williePete.createWPZone(aZone)
|
function williePete.createWPZone(aZone)
|
||||||
aZone.coalition = cfxZones.getCoalitionFromZoneProperty(aZone, "wpTarget", 0) -- side that marks it on map, and who fires arty
|
aZone.coalition = aZone:getCoalitionFromZoneProperty("wpTarget", 0) -- side that marks it on map, and who fires arty
|
||||||
aZone.shellStrength = cfxZones.getNumberFromZoneProperty(aZone, "shellStrength", 500) -- power of shells (strength)
|
aZone.shellStrength = aZone:getNumberFromZoneProperty( "shellStrength", 500) -- power of shells (strength)
|
||||||
aZone.shellNum = cfxZones.getNumberFromZoneProperty(aZone, "shellNum", 17) -- number of shells in bombardment
|
aZone.shellNum = aZone:getNumberFromZoneProperty("shellNum", 17) -- number of shells in bombardment
|
||||||
aZone.transitionTime = cfxZones.getNumberFromZoneProperty(aZone, "transitionTime", 20) -- average time of travel for projectiles
|
aZone.transitionTime = aZone:getNumberFromZoneProperty( "transitionTime", 20) -- average time of travel for projectiles
|
||||||
aZone.coolDown = cfxZones.getNumberFromZoneProperty(aZone, "coolDown", 180) -- cooldown after arty fire, used to set readyTime
|
aZone.coolDown = aZone:getNumberFromZoneProperty("coolDown", 180) -- cooldown after arty fire, used to set readyTime
|
||||||
aZone.baseAccuracy = cfxZones.getNumberFromZoneProperty(aZone, "baseAccuracy", 50)
|
aZone.baseAccuracy = aZone:getNumberFromZoneProperty( "baseAccuracy", 50)
|
||||||
|
|
||||||
aZone.readyTime = 0 -- if readyTime > now we are not ready
|
aZone.readyTime = 0 -- if readyTime > now we are not ready
|
||||||
aZone.trackingPlayer = nil -- name player's unit who is being tracked for wp. may not be neccessary
|
aZone.trackingPlayer = nil -- name player's unit who is being tracked for wp. may not be neccessary
|
||||||
aZone.checkedIn = {} -- dict of all planes currently checked in
|
aZone.checkedIn = {} -- dict of all planes currently checked in
|
||||||
|
|
||||||
aZone.wpMethod = cfxZones.getStringFromZoneProperty(aZone, "wpMethod", "change")
|
aZone.wpMethod = aZone:getStringFromZoneProperty("wpMethod", "change")
|
||||||
|
if aZone:hasProperty("method") then
|
||||||
aZone.checkInRange = cfxZones.getNumberFromZoneProperty(aZone, "checkInRange", williePete.checkInRange) -- default to my default
|
aZone.wpMethod = aZone:getStringFromZoneProperty("method", "change")
|
||||||
|
|
||||||
aZone.ackSound = cfxZones.getStringFromZoneProperty(aZone, "ackSound", williePete.ackSound)
|
|
||||||
aZone.guiSound = cfxZones.getStringFromZoneProperty(aZone, "guiSound", williePete.guiSound)
|
|
||||||
|
|
||||||
if cfxZones.hasProperty(aZone, "method") then
|
|
||||||
aZone.wpMethod = cfxZones.getStringFromZoneProperty(aZone, "method", "change")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(aZone, "wpFire!") then
|
aZone.checkInRange = aZone:getNumberFromZoneProperty( "checkInRange", williePete.checkInRange) -- default to my default
|
||||||
aZone.wpFire = cfxZones.getStringFromZoneProperty(aZone, "wpFire!", "<none)")
|
|
||||||
|
aZone.ackSound = aZone:getStringFromZoneProperty("ackSound", williePete.ackSound)
|
||||||
|
aZone.guiSound = aZone:getStringFromZoneProperty("guiSound", williePete.guiSound)
|
||||||
|
|
||||||
|
if aZone:hasProperty("wpFire!") then
|
||||||
|
aZone.wpFire = aZone:getStringFromZoneProperty("wpFire!", "<none)")
|
||||||
end
|
end
|
||||||
|
|
||||||
if aZone.verbose then
|
if aZone.verbose then
|
||||||
@ -142,6 +145,7 @@ function williePete.startPlayerGUI()
|
|||||||
end
|
end
|
||||||
|
|
||||||
unitInfo.name = uName -- needed for reverse-lookup
|
unitInfo.name = uName -- needed for reverse-lookup
|
||||||
|
unitInfo.gName = gName -- also needed for reverse lookup
|
||||||
unitInfo.coa = coa
|
unitInfo.coa = coa
|
||||||
unitInfo.gID = gData.groupId
|
unitInfo.gID = gData.groupId
|
||||||
unitInfo.uID = uData.unitId
|
unitInfo.uID = uData.unitId
|
||||||
@ -159,12 +163,20 @@ function williePete.startPlayerGUI()
|
|||||||
|
|
||||||
if pass then -- we install a menu for this group
|
if pass then -- we install a menu for this group
|
||||||
-- we may not want check in stuff, but it could be cool
|
-- we may not want check in stuff, but it could be cool
|
||||||
|
if williePete.playerGUIs[gName] then
|
||||||
|
trigger.action.outText("+++WP: Warning: we already have WP menu for unit <" .. uName .. "> in group <" .. gName .. ">. Skipped.", 30)
|
||||||
|
elseif williePete.groupGUIs[gName] then
|
||||||
|
trigger.action.outText("+++WP: Warning: POSSIBLE MULTI-PLAYER UNIT GROUP DETECTED. We already have WP menu for Player Group <" .. gName .. ">. Skipped, only first unit supported. ", 30)
|
||||||
|
else
|
||||||
unitInfo.root = missionCommands.addSubMenuForGroup(unitInfo.gID, "FAC")
|
unitInfo.root = missionCommands.addSubMenuForGroup(unitInfo.gID, "FAC")
|
||||||
unitInfo.checkIn = missionCommands.addCommandForGroup(unitInfo.gID, "Check In", unitInfo.root, williePete.redirectCheckIn, unitInfo)
|
unitInfo.checkIn = missionCommands.addCommandForGroup(unitInfo.gID, "Check In", unitInfo.root, williePete.redirectCheckIn, unitInfo)
|
||||||
|
williePete.groupGUIs[gName] = unitInfo
|
||||||
|
williePete.playerGUIs[gName] = unitInfo
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- store it
|
-- store it - WARNING: ASSUMES SINGLE-UNIT Player Groups
|
||||||
williePete.playerGUIs[uName] = unitInfo
|
--williePete.playerGUIs[uName] = unitInfo
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -177,7 +189,7 @@ function williePete.doBoom(args)
|
|||||||
-- note that unit who commânded fire may no longer be alive
|
-- note that unit who commânded fire may no longer be alive
|
||||||
-- so check it every time. unit must be alive
|
-- so check it every time. unit must be alive
|
||||||
-- to receive credits later
|
-- to receive credits later
|
||||||
local uName = unitInfo.name
|
local uName = unitInfo.gName
|
||||||
local blastRad = math.floor(math.sqrt(args.strength)) * 2
|
local blastRad = math.floor(math.sqrt(args.strength)) * 2
|
||||||
if blastRad < 10 then blastRad = 10 end
|
if blastRad < 10 then blastRad = 10 end
|
||||||
|
|
||||||
@ -187,7 +199,7 @@ function williePete.doBoom(args)
|
|||||||
if williePete.verbose then
|
if williePete.verbose then
|
||||||
trigger.action.outText("<" .. aName .. "> is in blast Radius (" .. blastRad .. "m) of shells for <" .. uName .. ">'s target coords", 30)
|
trigger.action.outText("<" .. aName .. "> is in blast Radius (" .. blastRad .. "m) of shells for <" .. uName .. ">'s target coords", 30)
|
||||||
end
|
end
|
||||||
williePete.blastedObjects[aName] = uName -- last one gets the kill
|
williePete.blastedObjects[aName] = unitInfo.name -- last one gets the kill
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
trigger.action.explosion(args.point, args.strength)
|
trigger.action.explosion(args.point, args.strength)
|
||||||
@ -237,16 +249,36 @@ function williePete.redirectCheckIn(unitInfo)
|
|||||||
timer.scheduleFunction(williePete.doCheckIn, unitInfo, timer.getTime() + 0.1)
|
timer.scheduleFunction(williePete.doCheckIn, unitInfo, timer.getTime() + 0.1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- fix for multi-unit player groups where only one of them is
|
||||||
|
-- alive: get first living player in group. Will be added to
|
||||||
|
-- dcsCommon soon
|
||||||
|
function williePete.getFirstLivingPlayerInGroupNamed(gName)
|
||||||
|
local theGroup = Group.getByName(gName)
|
||||||
|
if not theGroup then return nil end
|
||||||
|
local theUnits = theGroup:getUnits()
|
||||||
|
for idx, aUnit in pairs(theUnits) do
|
||||||
|
if Unit.isExist(aUnit) and aUnit.getPlayerName and
|
||||||
|
aUnit:getPlayerName() then
|
||||||
|
return aUnit -- return first living player unit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
function williePete.doCheckIn(unitInfo)
|
function williePete.doCheckIn(unitInfo)
|
||||||
--trigger.action.outText("check-in received", 30)
|
-- WARNING: unitInfo points to first processed player in
|
||||||
local theUnit = Unit.getByName(unitInfo.name)
|
-- group. May not work fully with multi-unit player groups
|
||||||
|
local gName = unitInfo.gName
|
||||||
|
local theUnit = williePete.getFirstLivingPlayerInGroupNamed(gName) --Unit.getByName(unitInfo.name)
|
||||||
if not theUnit then
|
if not theUnit then
|
||||||
-- dead man calling. Pilot dead but unit still alive
|
-- dead man calling. Pilot dead but unit still alive
|
||||||
|
-- OR second unit in multiplayer group, but unit 1
|
||||||
|
-- does not / no longer exists
|
||||||
trigger.action.outText("Calling station, say again, can't read you.", 30)
|
trigger.action.outText("Calling station, say again, can't read you.", 30)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local p = theUnit:getPoint()
|
local p = theUnit:getPoint() -- only react to first player unit
|
||||||
local theZone, dist = williePete.closestCheckInTgtZoneForCoa(p, unitInfo.coa)
|
local theZone, dist = williePete.closestCheckInTgtZoneForCoa(p, unitInfo.coa)
|
||||||
|
|
||||||
if not theZone then
|
if not theZone then
|
||||||
@ -256,21 +288,23 @@ function williePete.doCheckIn(unitInfo)
|
|||||||
trigger.action.outSoundForGroup(unitInfo.gID, williePete.guiSound)
|
trigger.action.outSoundForGroup(unitInfo.gID, williePete.guiSound)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
dist = math.floor(dist /100) / 10
|
||||||
trigger.action.outTextForGroup(unitInfo.gID, "Too far from target zone, closest target zone is " .. theZone.name, 30)
|
bearing = dcsCommon.bearingInDegreesFromAtoB(p, theZone:getPoint())
|
||||||
|
trigger.action.outTextForGroup(unitInfo.gID, unitInfo.gName .. ", you are too far from target zone, closest target zone is " .. theZone.name .. ", " .. dist .. "km at bearing " .. bearing .. "°", 30)
|
||||||
trigger.action.outSoundForGroup(unitInfo.gID, theZone.guiSound)
|
trigger.action.outSoundForGroup(unitInfo.gID, theZone.guiSound)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- we are now checked in to zone -- unless we are already checked in
|
-- we are now checked in to zone -- unless we are already checked in
|
||||||
if theZone.checkedIn[unitInfo.name] then
|
-- NOTE: we use group name, not unit name!
|
||||||
trigger.action.outTextForGroup(unitInfo.gID, unitInfo.name .. ", " .. theZone.name .. ", we heard you the first time, proceed.", 30)
|
if theZone.checkedIn[unitInfo.gName] then
|
||||||
|
trigger.action.outTextForGroup(unitInfo.gID, unitInfo.gName .. ", " .. theZone.name .. ", we heard you the first time, proceed.", 30)
|
||||||
trigger.action.outSoundForGroup(unitInfo.gID, theZone.guiSound)
|
trigger.action.outSoundForGroup(unitInfo.gID, theZone.guiSound)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- we now check in
|
-- we now check in
|
||||||
theZone.checkedIn[unitInfo.name] = unitInfo
|
theZone.checkedIn[unitInfo.gName] = unitInfo
|
||||||
|
|
||||||
-- add the 'Target marked' menu
|
-- add the 'Target marked' menu
|
||||||
unitInfo.targetMarked = missionCommands.addCommandForGroup(unitInfo.gID, "Target Marked, commence firing", unitInfo.root, williePete.redirectTargetMarked, unitInfo)
|
unitInfo.targetMarked = missionCommands.addCommandForGroup(unitInfo.gID, "Target Marked, commence firing", unitInfo.root, williePete.redirectTargetMarked, unitInfo)
|
||||||
@ -280,7 +314,7 @@ function williePete.doCheckIn(unitInfo)
|
|||||||
-- add 'check out'
|
-- add 'check out'
|
||||||
unitInfo.checkOut = missionCommands.addCommandForGroup(unitInfo.gID, "Check Out of " .. theZone.name, unitInfo.root, williePete.redirectCheckOut, unitInfo)
|
unitInfo.checkOut = missionCommands.addCommandForGroup(unitInfo.gID, "Check Out of " .. theZone.name, unitInfo.root, williePete.redirectCheckOut, unitInfo)
|
||||||
|
|
||||||
trigger.action.outTextForGroup(unitInfo.gID, "Roger " .. unitInfo.name .. ", " .. theZone.name .. " tracks you, standing by for target data.", 30)
|
trigger.action.outTextForGroup(unitInfo.gID, "Roger " .. unitInfo.gName .. ", " .. theZone.name .. " tracks you, standing by for target data.", 30)
|
||||||
trigger.action.outSoundForGroup(unitInfo.gID, theZone.guiSound)
|
trigger.action.outSoundForGroup(unitInfo.gID, theZone.guiSound)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -293,17 +327,17 @@ function williePete.doCheckOut(unitInfo)
|
|||||||
local wasCheckedIn = false
|
local wasCheckedIn = false
|
||||||
local fromZone = ""
|
local fromZone = ""
|
||||||
for idx, theZone in pairs(williePete.wpZones) do
|
for idx, theZone in pairs(williePete.wpZones) do
|
||||||
if theZone.checkedIn[unitInfo.name] then
|
if theZone.checkedIn[unitInfo.gName] then
|
||||||
wasCheckedIn = true
|
wasCheckedIn = true
|
||||||
fromZone = theZone.name
|
fromZone = theZone.name
|
||||||
end
|
end
|
||||||
theZone.checkedIn[unitInfo.name] = nil
|
theZone.checkedIn[unitInfo.gName] = nil
|
||||||
end
|
end
|
||||||
if not wasCheckedIn then
|
if not wasCheckedIn then
|
||||||
trigger.action.outTextForGroup(unitInfo.gID, unitInfo.name .. ", roger cecked-out. Good hunting!", 30)
|
trigger.action.outTextForGroup(unitInfo.gID, unitInfo.gName .. ", roger cecked-out. Good hunting!", 30)
|
||||||
trigger.action.outSoundForGroup(unitInfo.gID, williePete.guiSound)
|
trigger.action.outSoundForGroup(unitInfo.gID, williePete.guiSound)
|
||||||
else
|
else
|
||||||
trigger.action.outTextForGroup(unitInfo.gID, unitInfo.name .. "has checked out of " .. fromZone ..".", 30)
|
trigger.action.outTextForGroup(unitInfo.gID, unitInfo.gName .. " has checked out of " .. fromZone ..".", 30)
|
||||||
trigger.action.outSoundForGroup(unitInfo.gID, williePete.guiSound)
|
trigger.action.outSoundForGroup(unitInfo.gID, williePete.guiSound)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -326,7 +360,7 @@ function williePete.rogerDodger(args)
|
|||||||
local unitInfo = args[1]
|
local unitInfo = args[1]
|
||||||
local theZone = args[2]
|
local theZone = args[2]
|
||||||
|
|
||||||
trigger.action.outTextForCoalition(unitInfo.coa, "Roger " .. unitInfo.name .. ", good copy, firing.", 30)
|
trigger.action.outTextForCoalition(unitInfo.coa, "Roger " .. unitInfo.gName .. ", good copy, firing.", 30)
|
||||||
trigger.action.outSoundForCoalition(unitInfo.coa, theZone.ackSound)
|
trigger.action.outSoundForCoalition(unitInfo.coa, theZone.ackSound)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -357,9 +391,9 @@ function williePete.doTargetMarked(unitInfo)
|
|||||||
|
|
||||||
local tgtZone = unitInfo.wpInZone
|
local tgtZone = unitInfo.wpInZone
|
||||||
-- see if we are checked into that zone
|
-- see if we are checked into that zone
|
||||||
if not tgtZone.checkedIn[unitInfo.name] then
|
if not tgtZone.checkedIn[unitInfo.gName] then
|
||||||
-- zones don't match
|
-- zones don't match
|
||||||
trigger.action.outTextForGroup(unitInfo.gID, "Say again " .. unitInfo.name .. ", we have crosstalk. Try and reset coms", 30)
|
trigger.action.outTextForGroup(unitInfo.gID, "Say again " .. unitInfo.gName .. ", we have crosstalk. Try and reset coms", 30)
|
||||||
trigger.action.outSoundForGroup(unitInfo.gID, williePete.guiSound)
|
trigger.action.outSoundForGroup(unitInfo.gID, williePete.guiSound)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@ -368,7 +402,7 @@ function williePete.doTargetMarked(unitInfo)
|
|||||||
local timeRemaining = math.floor(tgtZone.readyTime - now)
|
local timeRemaining = math.floor(tgtZone.readyTime - now)
|
||||||
if timeRemaining > 0 then
|
if timeRemaining > 0 then
|
||||||
-- zone not ready
|
-- zone not ready
|
||||||
trigger.action.outTextForGroup(unitInfo.gID, "Stand by " .. unitInfo.name .. ", artillery not ready. Expect " .. timeRemaining + math.random(1, 5) .. " seconds.", 30)
|
trigger.action.outTextForGroup(unitInfo.gID, "Stand by " .. unitInfo.gName .. ", artillery not ready. Expect " .. timeRemaining + math.random(1, 5) .. " seconds.", 30)
|
||||||
trigger.action.outSoundForGroup(unitInfo.gID, tgtZone.guiSound)
|
trigger.action.outSoundForGroup(unitInfo.gID, tgtZone.guiSound)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@ -378,7 +412,7 @@ function williePete.doTargetMarked(unitInfo)
|
|||||||
local grid = coord.LLtoMGRS(coord.LOtoLL(unitInfo.pos))
|
local grid = coord.LLtoMGRS(coord.LOtoLL(unitInfo.pos))
|
||||||
local mgrs = grid.UTMZone .. ' ' .. grid.MGRSDigraph .. ' ' .. grid.Easting .. ' ' .. grid.Northing
|
local mgrs = grid.UTMZone .. ' ' .. grid.MGRSDigraph .. ' ' .. grid.Easting .. ' ' .. grid.Northing
|
||||||
local theLoc = mgrs
|
local theLoc = mgrs
|
||||||
trigger.action.outTextForCoalition(unitInfo.coa, tgtZone.name ..", " .. unitInfo.name .." is transmitting target location. Fire at " .. theLoc .. ", elevation " .. alt .. " meters, target marked.", 30)
|
trigger.action.outTextForCoalition(unitInfo.coa, tgtZone.name ..", " .. unitInfo.gName .." is transmitting target location. Fire at " .. theLoc .. ", elevation " .. alt .. " meters, target marked.", 30)
|
||||||
trigger.action.outSoundForCoalition(unitInfo.coa, tgtZone.guiSound)
|
trigger.action.outSoundForCoalition(unitInfo.coa, tgtZone.guiSound)
|
||||||
timer.scheduleFunction(williePete.rogerDodger, {unitInfo, tgtZone},timer.getTime() + math.random(2, 5))
|
timer.scheduleFunction(williePete.rogerDodger, {unitInfo, tgtZone},timer.getTime() + math.random(2, 5))
|
||||||
|
|
||||||
@ -401,10 +435,13 @@ end
|
|||||||
-- return true if a zone is actively tracking theUnit to place
|
-- return true if a zone is actively tracking theUnit to place
|
||||||
-- a wp
|
-- a wp
|
||||||
|
|
||||||
function williePete.zoneIsTracking(theUnit)
|
function williePete.zoneIsTracking(theUnit) -- group level!
|
||||||
local uName = theUnit:getName()
|
local uName = theUnit:getName()
|
||||||
|
local uGroup = theUnit:getGroup()
|
||||||
|
local gName = uGroup:getName()
|
||||||
|
|
||||||
for idx, theZone in pairs(williePete.wpZones) do
|
for idx, theZone in pairs(williePete.wpZones) do
|
||||||
if theZone.checkedIn[uName] then return true end
|
if theZone.checkedIn[gName] then return true end
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
@ -426,6 +463,8 @@ function williePete.zedsDead(theObject)
|
|||||||
|
|
||||||
local theName = theObject:getName()
|
local theName = theObject:getName()
|
||||||
-- now check if it's a registered blasted object:getSampleRate()
|
-- now check if it's a registered blasted object:getSampleRate()
|
||||||
|
-- in multi-unit player groups, this can can lead to
|
||||||
|
-- mis-attribution, beware!
|
||||||
local blaster = williePete.blastedObjects[theName]
|
local blaster = williePete.blastedObjects[theName]
|
||||||
if blaster then
|
if blaster then
|
||||||
local theUnit = Unit.getByName(blaster)
|
local theUnit = Unit.getByName(blaster)
|
||||||
@ -459,7 +498,7 @@ function williePete:onEvent(event)
|
|||||||
|
|
||||||
local theUnit = event.initiator
|
local theUnit = event.initiator
|
||||||
local pType = "(AI)"
|
local pType = "(AI)"
|
||||||
if theUnit.getPlayerName then pType = "(" .. theUnit:getName() .. ")" end
|
if theUnit.getPlayerName and theUnit:getPlayerName() then pType = "(" .. theUnit:getName() .. ")" end
|
||||||
|
|
||||||
if event.id == 1 then -- S_EVENT_SHOT
|
if event.id == 1 then -- S_EVENT_SHOT
|
||||||
-- initiator is who fired. maybe want to test if player
|
-- initiator is who fired. maybe want to test if player
|
||||||
@ -470,7 +509,7 @@ function williePete:onEvent(event)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- make sure that whoever fired it is being tracked by
|
-- make sure that whoever fired it is being tracked by
|
||||||
-- a zone
|
-- a zone. zoneIsTracking checks on GROUP level!
|
||||||
if not williePete.zoneIsTracking(theUnit) then
|
if not williePete.zoneIsTracking(theUnit) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@ -479,6 +518,8 @@ function williePete:onEvent(event)
|
|||||||
local theWillie = {}
|
local theWillie = {}
|
||||||
theWillie.firedBy = theUnit:getName()
|
theWillie.firedBy = theUnit:getName()
|
||||||
theWillie.theUnit = theUnit
|
theWillie.theUnit = theUnit
|
||||||
|
theWillie.theGroup = theUnit:getGroup()
|
||||||
|
theWillie.gName = theWillie.theGroup:getName()
|
||||||
theWillie.weapon = event.weapon
|
theWillie.weapon = event.weapon
|
||||||
theWillie.wt = theWillie.weapon:getTypeName()
|
theWillie.wt = theWillie.weapon:getTypeName()
|
||||||
theWillie.pos = theWillie.weapon:getPoint()
|
theWillie.pos = theWillie.weapon:getPoint()
|
||||||
@ -487,15 +528,6 @@ function williePete:onEvent(event)
|
|||||||
williePete.addWillie(theWillie)
|
williePete.addWillie(theWillie)
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[--
|
|
||||||
if event.id == 2 then -- hit
|
|
||||||
local what = "something"
|
|
||||||
if event.target then what = event.target:getName() end
|
|
||||||
--trigger.action.outText("Weapon " .. event.weapon:getTypeName() .. " fired by unit ".. theUnit:getName() .. " " .. pType .. " hit " .. what, 30)
|
|
||||||
-- may need to remove willie from willies
|
|
||||||
end
|
|
||||||
--]]--
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- test if a projectile has hit the ground inside a wp zone
|
-- test if a projectile has hit the ground inside a wp zone
|
||||||
@ -505,17 +537,15 @@ function williePete.isInside(theWillie)
|
|||||||
local theUnit = Unit.getByName(theUnitName)
|
local theUnit = Unit.getByName(theUnitName)
|
||||||
if not theUnit then return false end -- unit dead
|
if not theUnit then return false end -- unit dead
|
||||||
if not Unit.isExist(theUnit) then return false end -- dito
|
if not Unit.isExist(theUnit) then return false end -- dito
|
||||||
|
local theGroup = theUnit:getGroup()
|
||||||
local thePlayer = williePete.playerGUIs[theUnitName]
|
local gName = theGroup:getName()
|
||||||
if not thePlayer then return nil end
|
local unitInfo = williePete.groupGUIs[gName] -- returns unitInfo struct, contains group info
|
||||||
|
if not unitInfo then return nil end
|
||||||
for idx, theZone in pairs(williePete.wpZones) do
|
for idx, theZone in pairs(williePete.wpZones) do
|
||||||
if cfxZones.pointInZone(thePoint, theZone) then
|
if cfxZones.pointInZone(thePoint, theZone) then
|
||||||
-- we are inside. but is this the right coalition?
|
-- we are inside. but is this the right coalition?
|
||||||
if thePlayer.coa == theZone.coalition then
|
if unitInfo.coa == theZone.coalition then
|
||||||
--trigger.action.outText("Willie in " .. theZone.name, 30)
|
|
||||||
return theZone
|
return theZone
|
||||||
else
|
|
||||||
--trigger.action.outText("Willie wrong coa", 30)
|
|
||||||
end
|
end
|
||||||
-- if we want to allow neutral zones (doens't make sense)
|
-- if we want to allow neutral zones (doens't make sense)
|
||||||
-- add another guard below
|
-- add another guard below
|
||||||
@ -532,8 +562,9 @@ function williePete.projectileHit(theWillie)
|
|||||||
local vmod = dcsCommon.vMultScalar(theWillie.v, 0.5 / williePete.ups)
|
local vmod = dcsCommon.vMultScalar(theWillie.v, 0.5 / williePete.ups)
|
||||||
theWillie.pos = dcsCommon.vAdd(theWillie.pos, vmod)
|
theWillie.pos = dcsCommon.vAdd(theWillie.pos, vmod)
|
||||||
|
|
||||||
-- reset last mark for player
|
-- reset last mark for player's group
|
||||||
local thePlayer = williePete.playerGUIs[theWillie.firedBy]
|
-- access unitInfo
|
||||||
|
local thePlayer = williePete.playerGUIs[theWillie.gName]
|
||||||
thePlayer.pos = nil
|
thePlayer.pos = nil
|
||||||
thePlayer.wpInZone = nil
|
thePlayer.wpInZone = nil
|
||||||
|
|
||||||
@ -583,10 +614,14 @@ function williePete.playerUpdate()
|
|||||||
-- the zone that they checked in, or they are checked out
|
-- the zone that they checked in, or they are checked out
|
||||||
--local zp = cfxZones.getPoint(theZone)
|
--local zp = cfxZones.getPoint(theZone)
|
||||||
for idy, unitInfo in pairs(theZone.checkedIn) do
|
for idy, unitInfo in pairs(theZone.checkedIn) do
|
||||||
-- make sure unit still exists
|
-- make sure at least one unit still exists
|
||||||
local dropUnit = true
|
local dropUnit = true
|
||||||
local theUnit = Unit.getByName(unitInfo.name)
|
local theGroup = Group.getByName(unitInfo.gName)
|
||||||
if theUnit and Unit.isExist(theUnit) then
|
local allUnits = theGroup:getUnits()
|
||||||
|
for idx, theUnit in pairs(allUnits) do
|
||||||
|
--local theUnit = Unit.getByName(unitInfo.name)
|
||||||
|
if theUnit and Unit.isExist(theUnit) and
|
||||||
|
theUnit.getPlayerName and theUnit:getPlayerName() then
|
||||||
local up = theUnit:getPoint()
|
local up = theUnit:getPoint()
|
||||||
up.y = 0
|
up.y = 0
|
||||||
local isInside, dist = cfxZones.isPointInsideZone(up, theZone, theZone.checkInRange)
|
local isInside, dist = cfxZones.isPointInsideZone(up, theZone, theZone.checkInRange)
|
||||||
@ -595,8 +630,9 @@ function williePete.playerUpdate()
|
|||||||
dropUnit = false
|
dropUnit = false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
if dropUnit then
|
if dropUnit then
|
||||||
-- remove from zone check-in
|
-- all outside, remove from zone check-in
|
||||||
-- williePete.doCheckOut(unitInfo)
|
-- williePete.doCheckOut(unitInfo)
|
||||||
timer.scheduleFunction(williePete.doCheckOut, unitInfo, timer.getTime() + 0.1) -- to not muck up iteration
|
timer.scheduleFunction(williePete.doCheckOut, unitInfo, timer.getTime() + 0.1) -- to not muck up iteration
|
||||||
end
|
end
|
||||||
@ -612,13 +648,10 @@ end
|
|||||||
function williePete.readConfigZone()
|
function williePete.readConfigZone()
|
||||||
local theZone = cfxZones.getZoneByName("wpConfig")
|
local theZone = cfxZones.getZoneByName("wpConfig")
|
||||||
if not theZone then
|
if not theZone then
|
||||||
if williePete.verbose then
|
|
||||||
trigger.action.outText("+++wp: NO config zone!", 30)
|
|
||||||
end
|
|
||||||
theZone = cfxZones.createSimpleZone("wpConfig")
|
theZone = cfxZones.createSimpleZone("wpConfig")
|
||||||
end
|
end
|
||||||
|
|
||||||
local facTypes = cfxZones.getStringFromZoneProperty(theZone, "facTypes", "all")
|
local facTypes = theZone:getStringFromZoneProperty("facTypes", "all")
|
||||||
facTypes = string.upper(facTypes)
|
facTypes = string.upper(facTypes)
|
||||||
|
|
||||||
-- make this an array
|
-- make this an array
|
||||||
@ -631,19 +664,19 @@ function williePete.readConfigZone()
|
|||||||
williePete.facTypes = dcsCommon.trimArray(allTypes)
|
williePete.facTypes = dcsCommon.trimArray(allTypes)
|
||||||
|
|
||||||
-- how long a wp is active. must not be more than 5 minutes
|
-- how long a wp is active. must not be more than 5 minutes
|
||||||
williePete.wpMaxTime = cfxZones.getNumberFromZoneProperty(theZone, "wpMaxTime", 3 * 60)
|
williePete.wpMaxTime = theZone:getNumberFromZoneProperty( "wpMaxTime", 3 * 60)
|
||||||
|
|
||||||
-- default check-in range, added to target zone's range and used
|
-- default check-in range, added to target zone's range and used
|
||||||
-- for auto-check-out
|
-- for auto-check-out
|
||||||
williePete.checkInRange = cfxZones.getNumberFromZoneProperty(theZone, "checkInRange", 10000) -- 10 km outside
|
williePete.checkInRange = theZone:getNumberFromZoneProperty("checkInRange", 10000) -- 10 km outside
|
||||||
|
|
||||||
williePete.ackSound = cfxZones.getStringFromZoneProperty(theZone, "ackSound", "some")
|
williePete.ackSound = theZone:getStringFromZoneProperty( "ackSound", "some")
|
||||||
williePete.guiSound = cfxZones.getStringFromZoneProperty(theZone, "guiSound", "some")
|
williePete.guiSound = theZone:getStringFromZoneProperty( "guiSound", "some")
|
||||||
|
|
||||||
williePete.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
williePete.verbose = theZone.verbose
|
||||||
|
|
||||||
if williePete.verbose then
|
if williePete.verbose then
|
||||||
trigger.action.outText("+++msgr: read config", 30)
|
trigger.action.outText("+++wp: read config", 30)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
xFlags = {}
|
xFlags = {}
|
||||||
xFlags.version = "1.3.1"
|
xFlags.version = "2.0.0"
|
||||||
xFlags.verbose = false
|
xFlags.verbose = false
|
||||||
xFlags.hiVerbose = false
|
xFlags.hiVerbose = false
|
||||||
xFlags.ups = 1 -- overwritten in get config when configZone is present
|
xFlags.ups = 1 -- overwritten in get config when configZone is present
|
||||||
@ -11,30 +11,11 @@ xFlags.requiredLibs = {
|
|||||||
xFlags - flag array transmogrifier
|
xFlags - flag array transmogrifier
|
||||||
|
|
||||||
Version History
|
Version History
|
||||||
1.0.0 - Initial version
|
2.0.0 - dmlZones
|
||||||
1.0.1 - allow flags names for ops as well
|
- OOP
|
||||||
1.1.0 - Watchflags harmonization
|
- xDirect#
|
||||||
1.2.0 - xDirect flag,
|
- xCount#
|
||||||
- direct array support
|
- cleanup
|
||||||
1.2.1 - verbosity changes
|
|
||||||
- "most" operator
|
|
||||||
- "half or more" operator
|
|
||||||
- fixed reset
|
|
||||||
- xSuccess optimizations
|
|
||||||
- inc, dec, quoted flags
|
|
||||||
- matchNum can carry flag
|
|
||||||
1.2.2 - on/off/suspend commands
|
|
||||||
- hiVerbose option
|
|
||||||
- corrected bug in reset checksum
|
|
||||||
1.3.0 - xCount! flag
|
|
||||||
- "never" operator
|
|
||||||
1.3.1 - guards for xtriggerOffFlag and xtriggerOnFlag
|
|
||||||
to prevent QoL warnings
|
|
||||||
- guards for xDirect
|
|
||||||
- guards for xCount
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
xFlags.xFlagZones = {}
|
xFlags.xFlagZones = {}
|
||||||
|
|
||||||
@ -62,11 +43,7 @@ end
|
|||||||
|
|
||||||
function xFlags.createXFlagsWithZone(theZone)
|
function xFlags.createXFlagsWithZone(theZone)
|
||||||
local theArray = ""
|
local theArray = ""
|
||||||
if cfxZones.hasProperty(theZone, "xFlags") then
|
theArray = theZone:getStringFromZoneProperty("xFlags?", "<none>")
|
||||||
theArray = cfxZones.getStringFromZoneProperty(theZone, "xFlags", "<none>")
|
|
||||||
else
|
|
||||||
theArray = cfxZones.getStringFromZoneProperty(theZone, "xFlags?", "<none>")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- now process the array and create the value arrays
|
-- now process the array and create the value arrays
|
||||||
theZone.flagNames = cfxZones.flagArrayFromString(theArray)
|
theZone.flagNames = cfxZones.flagArrayFromString(theArray)
|
||||||
@ -86,95 +63,72 @@ function xFlags.createXFlagsWithZone(theZone)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
theZone.xHasFired = false
|
theZone.xHasFired = false
|
||||||
if cfxZones.hasProperty(theZone, "xSuccess!") then
|
if theZone:hasProperty("xSuccess!") then
|
||||||
theZone.xSuccess = cfxZones.getStringFromZoneProperty(theZone, "xSuccess!", "<none>")
|
theZone.xSuccess = theZone:getStringFromZoneProperty("xSuccess!", "<none>")
|
||||||
end
|
elseif theZone:hasProperty("out!") then
|
||||||
|
theZone.xSuccess = theZone:getStringFromZoneProperty("out!", "*<none>")
|
||||||
if cfxZones.hasProperty(theZone, "out!") then
|
|
||||||
theZone.xSuccess = cfxZones.getStringFromZoneProperty(theZone, "out!", "*<none>")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if not theZone.xSuccess then
|
if not theZone.xSuccess then
|
||||||
theZone.xSuccess = "*<none>"
|
theZone.xSuccess = "*<none>"
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "xChange!") then
|
if theZone:hasProperty("xChange!") then
|
||||||
theZone.xChange = cfxZones.getStringFromZoneProperty(theZone, "xChange!", "*<none>")
|
theZone.xChange = theZone:getStringFromZoneProperty("xChange!", "*<none>")
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "xDirect") then
|
if theZone:hasProperty("xDirect#") then
|
||||||
theZone.xDirect = cfxZones.getStringFromZoneProperty(theZone, "xDirect", "*<none>")
|
theZone.xDirect = theZone:getStringFromZoneProperty("xDirect#", "*<none>")
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "xCount") then
|
if theZone:hasProperty("xCount#") then
|
||||||
theZone.xCount = cfxZones.getStringFromZoneProperty(theZone, "xCount", "*<none>")
|
theZone.xCount = theZone:getStringFromZoneProperty("xCount#", "*<none>")
|
||||||
end
|
end
|
||||||
|
|
||||||
theZone.inspect = cfxZones.getStringFromZoneProperty(theZone, "require", "or") -- same as any
|
theZone.inspect = theZone:getStringFromZoneProperty("require", "or") -- same as any
|
||||||
-- supported any/or, all/and, moreThan, atLeast, exactly
|
-- supported any/or, all/and, moreThan, atLeast, exactly
|
||||||
theZone.inspect = string.lower(theZone.inspect)
|
theZone.inspect = string.lower(theZone.inspect)
|
||||||
theZone.inspect = dcsCommon.trim(theZone.inspect)
|
theZone.inspect = dcsCommon.trim(theZone.inspect)
|
||||||
|
|
||||||
theZone.matchNum = cfxZones.getStringFromZoneProperty(theZone, "#hits", "1")
|
theZone.matchNum = theZone:getStringFromZoneProperty("#hits", "1") -- string because can also be a flag ref
|
||||||
|
|
||||||
theZone.xTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "xFlagMethod", "change") -- (<>=[number or reference flag], off, on, yes, no, true, false, change
|
|
||||||
|
|
||||||
|
theZone.xTriggerMethod = theZone:getStringFromZoneProperty( "xFlagMethod", "change")
|
||||||
|
|
||||||
theZone.xTriggerMethod = string.lower(theZone.xTriggerMethod)
|
theZone.xTriggerMethod = string.lower(theZone.xTriggerMethod)
|
||||||
theZone.xTriggerMethod = dcsCommon.trim(theZone.xTriggerMethod)
|
theZone.xTriggerMethod = dcsCommon.trim(theZone.xTriggerMethod)
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "xReset?") then
|
if theZone:hasProperty("xReset?") then
|
||||||
theZone.xReset = cfxZones.getStringFromZoneProperty(theZone, "xReset?", "<none>")
|
theZone.xReset = theZone:getStringFromZoneProperty("xReset?", "<none>")
|
||||||
theZone.xLastReset = cfxZones.getFlagValue(theZone.xReset, theZone)
|
theZone.xLastReset = theZone:getFlagValue(theZone.xReset)
|
||||||
end
|
end
|
||||||
|
|
||||||
theZone.xMethod = cfxZones.getStringFromZoneProperty(theZone, "xMethod", "inc")
|
theZone.xMethod = theZone:getStringFromZoneProperty("xMethod", "inc")
|
||||||
if cfxZones.hasProperty(theZone, "method") then
|
if theZone:hasProperty("method") then
|
||||||
theZone.xMethod = cfxZones.getStringFromZoneProperty(theZone, "method", "inc")
|
theZone.xMethod = theZone:getStringFromZoneProperty("method", "inc")
|
||||||
end
|
end
|
||||||
|
|
||||||
theZone.xOneShot = cfxZones.getBoolFromZoneProperty(theZone, "oneShot", true)
|
theZone.xOneShot = theZone:getBoolFromZoneProperty("oneShot", true)
|
||||||
|
|
||||||
-- on / off commands
|
-- on / off commands
|
||||||
-- on/off flags
|
-- on/off flags
|
||||||
theZone.xSuspended = cfxZones.getBoolFromZoneProperty(theZone, "xSuspended", false) -- we are turned on
|
theZone.xSuspended = theZone:getBoolFromZoneProperty("xSuspended", false) -- we are turned on
|
||||||
if theZone.xSuspended and (xFlags.verbose or theZone.verbose) then
|
if theZone.xSuspended and (xFlags.verbose or theZone.verbose) then
|
||||||
trigger.action.outText("+++xFlg: <" .. theZone.name .. "> starts suspended", 30)
|
trigger.action.outText("+++xFlg: <" .. theZone.name .. "> starts suspended", 30)
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "xOn?") then
|
if theZone:hasProperty("xOn?") then
|
||||||
theZone.xtriggerOnFlag = cfxZones.getStringFromZoneProperty(theZone, "xOn?", "*<none1>")
|
theZone.xtriggerOnFlag = theZone:getStringFromZoneProperty("xOn?", "*<none1>")
|
||||||
theZone.xlastTriggerOnValue = cfxZones.getFlagValue(theZone.xtriggerOnFlag, theZone)
|
theZone.xlastTriggerOnValue = theZone:getFlagValue(theZone.xtriggerOnFlag)
|
||||||
end
|
end
|
||||||
if cfxZones.hasProperty(theZone, "xOff?") then
|
if theZone:hasProperty("xOff?") then
|
||||||
theZone.xtriggerOffFlag = cfxZones.getStringFromZoneProperty(theZone, "xOff?", "*<none2>")
|
theZone.xtriggerOffFlag = theZone:getStringFromZoneProperty( "xOff?", "*<none2>")
|
||||||
theZone.xlastTriggerOffValue = cfxZones.getFlagValue(theZone.xtriggerOffFlag, theZone)
|
theZone.xlastTriggerOffValue = theZone:getFlagValue(theZone.xtriggerOffFlag)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function xFlags.evaluateNumOrFlag(theAttribute, theZone)
|
function xFlags.evaluateNumOrFlag(theAttribute, theZone)
|
||||||
-- on entry, theAttribute contains a string
|
|
||||||
-- if it's a number, we return that, if it's a
|
|
||||||
-- string, we see if it's a quoted flag or
|
|
||||||
-- direct flag. in any way, we fetch and return
|
|
||||||
-- that flag's value
|
|
||||||
local aNum = tonumber(theAttribute)
|
|
||||||
if aNum then return aNum end
|
|
||||||
local remainder = dcsCommon.trim(theAttribute)
|
|
||||||
local esc = string.sub(remainder, 1, 1)
|
|
||||||
local last = string.sub(remainder, -1)
|
|
||||||
|
|
||||||
if esc == "(" and last == ")" and string.len(remainder) > 2 then
|
return cfxZones.evalRemainder(theAttribute, theZone)
|
||||||
remainder = string.sub(remainder, 2, -2)
|
|
||||||
remainder = dcsCommon.trim(remainder)
|
|
||||||
end
|
|
||||||
|
|
||||||
if esc == "\"" and last == "\"" and string.len(remainder) > 2 then
|
|
||||||
remainder = string.sub(remainder, 2, -2)
|
|
||||||
remainder = dcsCommon.trim(remainder)
|
|
||||||
end
|
|
||||||
|
|
||||||
rNum = cfxZones.getFlagValue(remainder, theZone)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function xFlags.evaluateFlags(theZone)
|
function xFlags.evaluateFlags(theZone)
|
||||||
@ -184,7 +138,7 @@ function xFlags.evaluateFlags(theZone)
|
|||||||
-- since the checksum is order dependent,
|
-- since the checksum is order dependent,
|
||||||
-- we must preserve the order of the array
|
-- we must preserve the order of the array
|
||||||
local flagName = theZone.flagNames[i]
|
local flagName = theZone.flagNames[i]
|
||||||
currVals[i] = cfxZones.getFlagValue(flagName, theZone)
|
currVals[i] = theZone:getFlagValue(flagName)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- now perform comparison flag by flag
|
-- now perform comparison flag by flag
|
||||||
@ -194,37 +148,10 @@ function xFlags.evaluateFlags(theZone)
|
|||||||
local firstChar = string.sub(op, 1, 1)
|
local firstChar = string.sub(op, 1, 1)
|
||||||
local remainder = string.sub(op, 2)
|
local remainder = string.sub(op, 2)
|
||||||
remainder = dcsCommon.trim(remainder) -- remove all leading and trailing spaces
|
remainder = dcsCommon.trim(remainder) -- remove all leading and trailing spaces
|
||||||
local rNum = tonumber(remainder)
|
|
||||||
if not rNum then
|
|
||||||
-- interpret remainder as flag name
|
|
||||||
-- so we can say >*killMax or "22" with 22 a flag name
|
|
||||||
|
|
||||||
-- we use remainder as name for flag
|
local rNum = theZone:evalRemainder(remainder)
|
||||||
-- PROCESS ESCAPE SEQUENCES
|
|
||||||
local esc = string.sub(remainder, 1, 1)
|
|
||||||
local last = string.sub(remainder, -1)
|
|
||||||
if esc == "@" then
|
|
||||||
remainder = string.sub(remainder, 2)
|
|
||||||
remainder = dcsCommon.trim(remainder)
|
|
||||||
end
|
|
||||||
|
|
||||||
if esc == "(" and last == ")" and string.len(remainder) > 2 then
|
-- the following mimics cfxZones.testFlagByMethodForZone method (and is
|
||||||
-- note: iisues with startswith("(") ???
|
|
||||||
remainder = string.sub(remainder, 2, -2)
|
|
||||||
remainder = dcsCommon.trim(remainder)
|
|
||||||
end
|
|
||||||
if esc == "\"" and last == "\"" and string.len(remainder) > 2 then
|
|
||||||
remainder = string.sub(remainder, 2, -2)
|
|
||||||
remainder = dcsCommon.trim(remainder)
|
|
||||||
end
|
|
||||||
if cfxZones.verbose then
|
|
||||||
trigger.action.outText("+++zne: accessing flag <" .. remainder .. ">", 30)
|
|
||||||
end
|
|
||||||
|
|
||||||
rNum = cfxZones.getFlagValue(remainder, theZone)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- this mimics cfxZones.testFlagByMethodForZone method (and is
|
|
||||||
-- that method's genesis), but is different enough not to invoke that
|
-- that method's genesis), but is different enough not to invoke that
|
||||||
-- method
|
-- method
|
||||||
for i = 1, #theZone.flagNames do
|
for i = 1, #theZone.flagNames do
|
||||||
@ -298,7 +225,6 @@ function xFlags.evaluateFlags(theZone)
|
|||||||
return 0, ""
|
return 0, ""
|
||||||
end
|
end
|
||||||
if xFlags.verbose and lastHits ~= hits then
|
if xFlags.verbose and lastHits ~= hits then
|
||||||
--trigger.action.outText("+++xF: hit detected for " .. theZone.flagNames[i] .. " in " .. theZone.name .. "(" .. op .. ")", 30)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return hits, checkSum
|
return hits, checkSum
|
||||||
@ -361,7 +287,7 @@ function xFlags.evaluateZone(theZone)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if theZone.xChange then
|
if theZone.xChange then
|
||||||
cfxZones.pollFlag(theZone.xChange, theZone.xMethod, theZone)
|
theZone:pollFlag(theZone.xChange, theZone.xMethod)
|
||||||
if xFlags.verbose then
|
if xFlags.verbose then
|
||||||
trigger.action.outText("+++xFlag: change bang! on " .. theZone.xChange .. " for " .. theZone.name, 30)
|
trigger.action.outText("+++xFlag: change bang! on " .. theZone.xChange .. " for " .. theZone.name, 30)
|
||||||
end
|
end
|
||||||
@ -378,15 +304,15 @@ function xFlags.evaluateZone(theZone)
|
|||||||
-- true (1)/false(0), no matter if changed or not
|
-- true (1)/false(0), no matter if changed or not
|
||||||
if theZone.xDirect then
|
if theZone.xDirect then
|
||||||
if evalResult then
|
if evalResult then
|
||||||
cfxZones.setFlagValueMult(theZone.xDirect, 1, theZone)
|
theZone:setFlagValue(theZone.xDirect, 1)
|
||||||
else
|
else
|
||||||
cfxZones.setFlagValueMult(theZone.xDirect, 0, theZone)
|
theZone:setFlagValue(theZone.xDirect, 0)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- directly set the xCount flag
|
-- directly set the xCount flag
|
||||||
if theZone.xCount then
|
if theZone.xCount then
|
||||||
cfxZones.setFlagValueMult(theZone.xCount, hits, theZone)
|
theZone:setFlagValueMult(theZone.xCount, hits)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- now see if we bang the output according to method
|
-- now see if we bang the output according to method
|
||||||
@ -394,7 +320,7 @@ function xFlags.evaluateZone(theZone)
|
|||||||
if xFlags.verbose or theZone.verbose then
|
if xFlags.verbose or theZone.verbose then
|
||||||
trigger.action.outText("+++xFlag: success bang! on <" .. theZone.xSuccess .. "> for <" .. theZone.name .. "> with method <" .. theZone.xMethod .. ">", 30)
|
trigger.action.outText("+++xFlag: success bang! on <" .. theZone.xSuccess .. "> for <" .. theZone.name .. "> with method <" .. theZone.xMethod .. ">", 30)
|
||||||
end
|
end
|
||||||
cfxZones.pollFlag(theZone.xSuccess, theZone.xMethod, theZone)
|
theZone:pollFlag(theZone.xSuccess, theZone.xMethod)
|
||||||
theZone.xHasFired = true
|
theZone.xHasFired = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -407,14 +333,14 @@ function xFlags.update()
|
|||||||
|
|
||||||
for idx, theZone in pairs (xFlags.xFlagZones) do
|
for idx, theZone in pairs (xFlags.xFlagZones) do
|
||||||
-- see if we should suspend
|
-- see if we should suspend
|
||||||
if theZone.xtriggerOnFlag and cfxZones.testZoneFlag(theZone, theZone.xtriggerOnFlag, "change", "xlastTriggerOnValue") then
|
if theZone.xtriggerOnFlag and theZone:testZoneFlag( theZone.xtriggerOnFlag, "change", "xlastTriggerOnValue") then
|
||||||
if xFlags.verbose or theZone.verbose then
|
if xFlags.verbose or theZone.verbose then
|
||||||
trigger.action.outText("+++xFlg: enabling " .. theZone.name, 30)
|
trigger.action.outText("+++xFlg: enabling " .. theZone.name, 30)
|
||||||
end
|
end
|
||||||
theZone.xSuspended = false
|
theZone.xSuspended = false
|
||||||
end
|
end
|
||||||
|
|
||||||
if theZone.xtriggerOffFlag and cfxZones.testZoneFlag(theZone, theZone.xtriggerOffFlag, "change", "xlastTriggerOffValue") then
|
if theZone.xtriggerOffFlag and theZone:testZoneFlag( theZone.xtriggerOffFlag, "change", "xlastTriggerOffValue") then
|
||||||
if xFlags.verbose or theZone.verbose then
|
if xFlags.verbose or theZone.verbose then
|
||||||
trigger.action.outText("+++xFlg: DISabling " .. theZone.name, 30)
|
trigger.action.outText("+++xFlg: DISabling " .. theZone.name, 30)
|
||||||
end
|
end
|
||||||
@ -428,7 +354,7 @@ function xFlags.update()
|
|||||||
|
|
||||||
-- see if they should reset
|
-- see if they should reset
|
||||||
if theZone.xReset then
|
if theZone.xReset then
|
||||||
local currVal = cfxZones.getFlagValue(theZone.xReset, theZone)
|
local currVal = theZone:getFlagValue(theZone.xReset)
|
||||||
if currVal ~= theZone.xLastReset then
|
if currVal ~= theZone.xLastReset then
|
||||||
theZone.xLastReset = currVal
|
theZone.xLastReset = currVal
|
||||||
if xFlags.verbose or theZone.verbose then
|
if xFlags.verbose or theZone.verbose then
|
||||||
@ -446,14 +372,11 @@ function xFlags.readConfigZone()
|
|||||||
-- note: must match exactly!!!!
|
-- note: must match exactly!!!!
|
||||||
local theZone = cfxZones.getZoneByName("xFlagsConfig")
|
local theZone = cfxZones.getZoneByName("xFlagsConfig")
|
||||||
if not theZone then
|
if not theZone then
|
||||||
if xFlags.verbose then
|
theZone = cfxZones.createSimpleZone("xFlagsConfig")
|
||||||
trigger.action.outText("***xFlag: NO config zone!", 30)
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
end
|
||||||
|
|
||||||
xFlags.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
xFlags.verbose = theZone.verbose
|
||||||
xFlags.ups = cfxZones.getNumberFromZoneProperty(theZone, "ups", 1)
|
xFlags.ups = theZone:getNumberFromZoneProperty("ups", 1)
|
||||||
|
|
||||||
if xFlags.verbose then
|
if xFlags.verbose then
|
||||||
trigger.action.outText("***xFlg: read config", 30)
|
trigger.action.outText("***xFlg: read config", 30)
|
||||||
@ -476,9 +399,6 @@ function xFlags.start()
|
|||||||
|
|
||||||
-- process RND Zones
|
-- process RND Zones
|
||||||
local attrZones = cfxZones.getZonesWithAttributeNamed("xFlags")
|
local attrZones = cfxZones.getZonesWithAttributeNamed("xFlags")
|
||||||
|
|
||||||
-- now create an rnd gen for each one and add them
|
|
||||||
-- to our watchlist
|
|
||||||
for k, aZone in pairs(attrZones) do
|
for k, aZone in pairs(attrZones) do
|
||||||
xFlags.createXFlagsWithZone(aZone) -- process attribute and add to zone
|
xFlags.createXFlagsWithZone(aZone) -- process attribute and add to zone
|
||||||
xFlags.addxFlags(aZone) -- remember it
|
xFlags.addxFlags(aZone) -- remember it
|
||||||
@ -509,4 +429,5 @@ end
|
|||||||
--[[--
|
--[[--
|
||||||
Additional features:
|
Additional features:
|
||||||
- make #hits compatible to flags and numbers
|
- make #hits compatible to flags and numbers
|
||||||
|
- autoReset -- can be done by short-circuiting xsuccess! into xReset?
|
||||||
--]]--
|
--]]--
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
tutorial & demo missions/demo - debug events and more.miz
Normal file
BIN
tutorial & demo missions/demo - debug events and more.miz
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user