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.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.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.slotGroundActions = {
|
||||
@ -29,7 +26,6 @@ cfxSSBSingleUse.slotGroundActions = {
|
||||
"From Ground Area",
|
||||
"From Ground Area Hot",
|
||||
}
|
||||
|
||||
cfxSSBSingleUse.groundSlots = {} -- players that start on the ground
|
||||
|
||||
function cfxSSBSingleUse:onEvent(event)
|
||||
@ -40,7 +36,6 @@ function cfxSSBSingleUse:onEvent(event)
|
||||
-- if we get here, initiator is set
|
||||
local theUnit = event.initiator -- we know this exists
|
||||
|
||||
|
||||
-- write down player names and planes
|
||||
if event.id == 15 then
|
||||
local uName = theUnit:getName()
|
||||
@ -69,7 +64,9 @@ function cfxSSBSingleUse:onEvent(event)
|
||||
|
||||
if not thePilot then
|
||||
-- ignore. not a player plane
|
||||
trigger.action.outText("+++singleUse: ignored crash for NPC unit <" .. uName .. ">", 30)
|
||||
if cfxSSBSingleUse.verbose then
|
||||
trigger.action.outText("+++singleUse: ignored crash for NPC unit <" .. uName .. ">", 30)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
@ -80,19 +77,24 @@ function cfxSSBSingleUse:onEvent(event)
|
||||
local theGroundSlot = cfxSSBSingleUse.groundSlots[gName]
|
||||
if theGroundSlot then
|
||||
local unitType = theUnit:getTypeName()
|
||||
trigger.action.outText("+++singleUse: <" .. uName .. "> starts on Ground. Will place debris for " .. unitType .. " NOW!!!", 30)
|
||||
if cfxSSBSingleUse.verbose then
|
||||
trigger.action.outText("+++singleUse: <" .. uName .. "> starts on Ground. Will place debris for " .. unitType .. " NOW!!!", 30)
|
||||
end
|
||||
cfxSSBSingleUse.placeDebris(unitType, theGroundSlot)
|
||||
end
|
||||
|
||||
-- block this slot.
|
||||
trigger.action.setUserFlag(gName, cfxSSBSingleUse.disabledFlagValue)
|
||||
|
||||
trigger.action.outText("+++singleUse: blocked <" .. gName .. "> after " .. thePilot .. " crashed it.", 30)
|
||||
if cfxSSBSingleUse.verbose then
|
||||
trigger.action.outText("+++singleUse: blocked <" .. gName .. "> after " .. thePilot .. " crashed it.", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function cfxSSBSingleUse.placeDebris(unitType, theGroundSlot)
|
||||
if not unitType then return end
|
||||
if not cfxSSBSingleUse.useDebris then return end
|
||||
|
||||
-- access location one, we assume single-unit groups
|
||||
-- or at least that the player sits in unit one
|
||||
local playerData = theGroundSlot.playerUnits
|
||||
@ -106,12 +108,14 @@ function cfxSSBSingleUse.placeDebris(unitType, theGroundSlot)
|
||||
wreckData.type = unitType
|
||||
|
||||
coalition.addStaticObject(theGroundSlot.coaNum, wreckData )
|
||||
trigger.action.outText("+++singleUse: wreck <" .. unitType .. "> at " .. wreckData.x .. ", " .. wreckData.y .. " for " .. wreckData.name, 30)
|
||||
if cfxSSBSingleUse.verbose then
|
||||
trigger.action.outText("+++singleUse: wreck <" .. unitType .. "> at " .. wreckData.x .. ", " .. wreckData.y .. " for " .. wreckData.name, 30)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function cfxSSBSingleUse.populateAirfieldSlots()
|
||||
local pGroups = cfxGroups.getPlayerGroup()
|
||||
local pGroups = cfxMX.getPlayerGroup()
|
||||
local groundStarters = {}
|
||||
for idx, theGroup in pairs(pGroups) do
|
||||
-- we always use the first player's plane as referenced
|
||||
@ -122,33 +126,35 @@ function cfxSSBSingleUse.populateAirfieldSlots()
|
||||
if dcsCommon.arrayContainsString(cfxSSBSingleUse.slotGroundActions, action ) then
|
||||
-- ground starter, not from runway
|
||||
groundStarters[theGroup.name] = theGroup
|
||||
trigger.action.outText("+++singleUse: <" .. theGroup.name .. "> is ground starter", 30)
|
||||
if cfxSSBSingleUse.verbose then
|
||||
trigger.action.outText("+++singleUse: <" .. theGroup.name .. "> is ground starter", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
cfxSSBSingleUse.groundSlots = groundStarters
|
||||
end
|
||||
|
||||
function cfxSSBSingleUse.start()
|
||||
-- check libs
|
||||
if not dcsCommon.libCheck("cfx SSB Single Use",
|
||||
cfxSSBSingleUse.requiredLibs) then
|
||||
return false
|
||||
end
|
||||
|
||||
-- install event monitor
|
||||
world.addEventHandler(cfxSSBSingleUse)
|
||||
|
||||
-- get all groups and process them to find
|
||||
-- all planes that are on the ground for
|
||||
-- eye candy
|
||||
cfxSSBSingleUse.populateAirfieldSlots()
|
||||
|
||||
-- turn on ssb
|
||||
trigger.action.setUserFlag("SSB",100)
|
||||
|
||||
trigger.action.outText("SSB Single use v" .. cfxSSBSingleUse.version .. " running", 30)
|
||||
return true
|
||||
end
|
||||
|
||||
-- let's go!
|
||||
cfxSSBSingleUse.start()
|
||||
|
||||
--[[--
|
||||
Additional features (later):
|
||||
- place a wreck in slot when blocking for eye candy
|
||||
- record player when they enter a unit and only block player planes
|
||||
|
||||
--]]--
|
||||
if not cfxSSBSingleUse.start()then
|
||||
trigger.action.outText("SSB Single Use failed to start up.", 30)
|
||||
cfxSSBSingleUse = nil
|
||||
end
|
||||
@ -1,5 +1,5 @@
|
||||
tdz = {}
|
||||
tdz.version = "1.0.1"
|
||||
tdz.version = "1.0.2"
|
||||
tdz.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxZones", -- Zones, of course
|
||||
@ -15,6 +15,8 @@ VERSION HISTORY
|
||||
multiple zone support
|
||||
hops detection improvement
|
||||
helo attribute
|
||||
1.0.2 - manual placement option
|
||||
filters FARPs
|
||||
|
||||
--]]--
|
||||
|
||||
@ -80,24 +82,41 @@ end
|
||||
--
|
||||
function tdz.createTDZ(theZone)
|
||||
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.baseName = theBase:getName()
|
||||
theZone.helos = false
|
||||
|
||||
-- get closest runway to TDZ
|
||||
-- may get a bit hairy, so let's find a good way
|
||||
local allRwys = theBase:getRunways()
|
||||
local nearestRwy = nil
|
||||
local minDist = math.huge
|
||||
for idx, aRwy in pairs(allRwys) do
|
||||
local rp = aRwy.position
|
||||
local dist = dcsCommon.distFlat(p, rp)
|
||||
if dist < minDist then
|
||||
nearestRwy = aRwy
|
||||
minDist = dist
|
||||
-- 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
|
||||
-- may get a bit hairy, so let's find a good way
|
||||
local allRwys = theBase:getRunways()
|
||||
|
||||
local minDist = math.huge
|
||||
for idx, aRwy in pairs(allRwys) do
|
||||
local rp = aRwy.position
|
||||
local dist = dcsCommon.distFlat(p, rp)
|
||||
if dist < minDist then
|
||||
nearestRwy = aRwy
|
||||
minDist = dist
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local bearing = nearestRwy.course * (-1)
|
||||
if bearing < 0 then bearing = bearing + math.pi * 2 end
|
||||
theZone.bearing = bearing
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
airfield = {}
|
||||
airfield.version = "1.1.2"
|
||||
airfield.version = "2.0.0"
|
||||
airfield.requiredLibs = {
|
||||
"dcsCommon",
|
||||
"cfxZones",
|
||||
}
|
||||
airfield.verbose = false
|
||||
airfield.myAirfields = {} -- indexed by name
|
||||
airfield.farps = false
|
||||
airfield.gracePeriod = 3
|
||||
airfield.allAirfields = {} -- inexed by name
|
||||
|
||||
--[[--
|
||||
This module generates signals when the nearest airfield changes hands,
|
||||
can force the coalition of an airfield, and always provides the
|
||||
@ -24,17 +24,47 @@ airfield.gracePeriod = 3
|
||||
1.1.2 - 'show' attribute
|
||||
line color attributes per zone
|
||||
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
|
||||
--
|
||||
function airfield.createAirFieldFromZone(theZone)
|
||||
--trigger.action.outText("Enter airfield for <" .. theZone.name .. ">", 30)
|
||||
theZone.farps = theZone:getBoolFromZoneProperty("farps", false)
|
||||
|
||||
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 theBase = dcsCommon.getClosestAirbaseTo(p, filterCat)
|
||||
theZone.airfield = theBase
|
||||
@ -113,9 +143,29 @@ function airfield.createAirFieldFromZone(theZone)
|
||||
theZone.neutralFill = theZone:getRGBAVectorFromZoneProperty("neutralFill", airfield.neutralFill)
|
||||
|
||||
airfield.showAirfield(theZone)
|
||||
|
||||
-- now mark this zone as handled
|
||||
local entry = airfield.allAirfields[theZone.afName]
|
||||
entry.linkedTo = theZone -- only remember last, but that's enough.
|
||||
|
||||
--trigger.action.outText("Exit airfield for <" .. theZone.name .. ">", 30)
|
||||
|
||||
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)
|
||||
@ -138,35 +188,9 @@ function airfield.showAirfield(theZone)
|
||||
fillColor = theZone.neutralFill -- {0.8, 0.8, 0.8, 0.2}
|
||||
end
|
||||
|
||||
-- always center on airfield, always 2km radius
|
||||
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
|
||||
theZone.ownerMark = airfield.markAirfieldOnMap(theZone.airfield, lineColor, fillColor)
|
||||
|
||||
|
||||
end
|
||||
|
||||
function airfield.assumeControl(theZone)
|
||||
@ -188,12 +212,40 @@ end
|
||||
--
|
||||
-- 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)
|
||||
-- retrieve the zone that controls this airfield
|
||||
local bName = theBase:getName()
|
||||
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()
|
||||
theZone.owner = newCoa
|
||||
|
||||
@ -227,15 +279,8 @@ function airfield:onEvent(event)
|
||||
-- get category
|
||||
local desc = theBase:getDesc()
|
||||
local bName = theBase:getName()
|
||||
local cat = desc.category -- never get cat directly!
|
||||
--[[-- if cat == 1 then
|
||||
if not airfield.farps then
|
||||
if airfield.verbose then
|
||||
trigger.action.outText("+++airF: ignored cap event for FARP <" .. bName .. ">", 30)
|
||||
end
|
||||
return
|
||||
end
|
||||
end --]]
|
||||
local cat = desc.category -- never get cat directly! DCS 2.0 safe
|
||||
|
||||
if airfield.verbose then
|
||||
trigger.action.outText("+++airF: cap event for <" .. bName .. ">, cat = (" .. cat .. ")", 30)
|
||||
end
|
||||
@ -391,7 +436,7 @@ function airfield.readConfig()
|
||||
theZone = cfxZones.createSimpleZone("airfieldConfig")
|
||||
end
|
||||
airfield.verbose = theZone.verbose
|
||||
airfield.farps = theZone:getBoolFromZoneProperty("farps", false)
|
||||
-- airfield.farps = theZone:getBoolFromZoneProperty("farps", false)
|
||||
|
||||
-- colors for line and fill
|
||||
airfield.redLine = theZone:getRGBAVectorFromZoneProperty("redLine", {1.0, 0, 0, 1.0})
|
||||
@ -400,11 +445,24 @@ function airfield.readConfig()
|
||||
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.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
|
||||
|
||||
function airfield.start()
|
||||
if not dcsCommon.libCheck("cfx airfield", airfield.requiredLibs)
|
||||
then return false end
|
||||
|
||||
-- set up DB
|
||||
airfield.collectAll()
|
||||
|
||||
-- read config
|
||||
airfield.readConfig()
|
||||
@ -415,6 +473,9 @@ function airfield.start()
|
||||
airfield.createAirFieldFromZone(aZone)
|
||||
end
|
||||
|
||||
-- show all unlinked
|
||||
if airfield.showAll then airfield.showUnlinked() end
|
||||
|
||||
-- connect event handler
|
||||
world.addEventHandler(airfield)
|
||||
|
||||
@ -442,7 +503,3 @@ if not airfield.start() then
|
||||
trigger.action.outText("+++ aborted airfield v" .. airfield.version .. " -- startup failed", 30)
|
||||
airfield = nil
|
||||
end
|
||||
|
||||
--[[--
|
||||
ideas: what if we made this airfield movable?
|
||||
--]]--
|
||||
@ -1,5 +1,5 @@
|
||||
cfxArtilleryUI = {}
|
||||
cfxArtilleryUI.version = "1.1.0"
|
||||
cfxArtilleryUI.version = "2.0.0"
|
||||
cfxArtilleryUI.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxPlayer", -- get all players
|
||||
@ -8,7 +8,7 @@ cfxArtilleryUI.requiredLibs = {
|
||||
}
|
||||
--
|
||||
-- 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
|
||||
- 1.0.0 - based on jtacGrpUI
|
||||
@ -21,6 +21,8 @@ cfxArtilleryUI.requiredLibs = {
|
||||
- collect zones recognizes moving zones, updates landHeight
|
||||
- allSeeing god mode attribute: always observing.
|
||||
- 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
|
||||
-- re-populate if the old is different
|
||||
if tgtCheckSum == conf.tgtCheckSum then
|
||||
-- trigger.action.outText("*** yeah old targets", 30)
|
||||
return
|
||||
elseif not conf.tgtCheckSum then
|
||||
-- trigger.action.outText("+++ new target menu", 30)
|
||||
|
||||
else
|
||||
trigger.action.outTextForGroup(conf.id, "Artillery target updates", 30)
|
||||
trigger.action.outSoundForGroup(conf.id, cfxArtilleryUI.updateSound)
|
||||
-- trigger.action.outText("!!! target update ", 30)
|
||||
|
||||
end
|
||||
|
||||
-- we need to re-populate. erase old values
|
||||
cfxArtilleryUI.clearCommsTargets(conf)
|
||||
conf.tgtCheckSum = tgtCheckSum -- remember for last time
|
||||
--trigger.action.outText("new targets", 30)
|
||||
|
||||
conf.tgtCheckSum = tgtCheckSum -- remember for last time
|
||||
if #filteredTargets < 1 then
|
||||
-- simply put one-line dummy in there
|
||||
local commandTxt = "(No unobscured target areas)"
|
||||
@ -307,7 +306,6 @@ function cfxArtilleryUI.populateTargetMenu(conf)
|
||||
for i=1, numTargets do
|
||||
-- make a target command for each
|
||||
local aTarget = filteredTargets[i]
|
||||
|
||||
commandTxt = "Fire at: <" .. aTarget.name .. ">"
|
||||
theCommand = missionCommands.addCommandForGroup(
|
||||
conf.id,
|
||||
@ -444,7 +442,6 @@ function cfxArtilleryUI.doCommandListTargets(args)
|
||||
local conf = args[1] -- < conf in here
|
||||
local what = args[2] -- < second argument in here
|
||||
local theGroup = conf.theGroup
|
||||
-- trigger.action.outTextForGroup(conf.id, "+++ groupUI: processing comms menu for <" .. what .. ">", 30)
|
||||
local targetList = cfxArtilleryUI.collectArtyTargets(conf)
|
||||
-- iterate the list
|
||||
if #targetList < 1 then
|
||||
@ -453,7 +450,6 @@ function cfxArtilleryUI.doCommandListTargets(args)
|
||||
end
|
||||
|
||||
local desc = "Artillery Targets:\n"
|
||||
-- trigger.action.outTextForGroup(conf.id, "Target Report:", 30)
|
||||
for i=1, #targetList do
|
||||
local aTarget = targetList[i]
|
||||
local inRange = cfxArtilleryUI.allRanging or aTarget.range * 1000 < aTarget.spotRange
|
||||
@ -475,7 +471,6 @@ end
|
||||
function cfxArtilleryUI.collectArtyTargets(conf)
|
||||
-- iterate all target zones, for those that are on my side
|
||||
-- calculate range, bearing, and then order by distance
|
||||
|
||||
local theTargets = {}
|
||||
for idx, aZone in pairs(cfxArtilleryZones.artilleryZones) do
|
||||
if aZone.coalition == conf.coalition then
|
||||
@ -498,7 +493,7 @@ function cfxArtilleryUI.collectArtyTargets(conf)
|
||||
--aTarget.targetName = aZone.name
|
||||
aTarget.spotRange = aZone.spotRange
|
||||
-- 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})
|
||||
local there = {x = zP.x, y = aZone.landHeight + 1, z=zP.z}
|
||||
aTarget.there = there
|
||||
@ -654,18 +649,17 @@ function cfxArtilleryUI.readConfigZone()
|
||||
-- note: must match exactly!!!!
|
||||
local theZone = cfxZones.getZoneByName("ArtilleryUIConfig")
|
||||
if not theZone then
|
||||
trigger.action.outText("+++A-UI: no config zone!", 30)
|
||||
return
|
||||
--trigger.action.outText("+++A-UI: no config zone!", 30)
|
||||
--return
|
||||
theZone = cfxZones.createSimpleZone("ArtilleryUIConfig")
|
||||
end
|
||||
|
||||
trigger.action.outText("+++A-UI: found config zone!", 30)
|
||||
|
||||
cfxArtilleryUI.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
||||
cfxArtilleryUI.allowPlanes = cfxZones.getBoolFromZoneProperty(theZone, "allowPlanes", false)
|
||||
cfxArtilleryUI.smokeColor = cfxZones.getSmokeColorStringFromZoneProperty(theZone, "smokeColor", "red")
|
||||
cfxArtilleryUI.allSeeing = cfxZones.getBoolFromZoneProperty(theZone, "allSeeing", false)
|
||||
cfxArtilleryUI.allRanging = cfxZones.getBoolFromZoneProperty(theZone, "allRanging", false)
|
||||
cfxArtilleryUI.allTiming = cfxZones.getBoolFromZoneProperty(theZone, "allTiming", false)
|
||||
|
||||
cfxArtilleryUI.verbose = theZone.verbose
|
||||
cfxArtilleryUI.allowPlanes = theZone:getBoolFromZoneProperty("allowPlanes", false)
|
||||
cfxArtilleryUI.smokeColor = theZone:getSmokeColorStringFromZoneProperty("smokeColor", "red")
|
||||
cfxArtilleryUI.allSeeing = theZone:getBoolFromZoneProperty("allSeeing", false)
|
||||
cfxArtilleryUI.allRanging = theZone:getBoolFromZoneProperty("allRanging", false)
|
||||
cfxArtilleryUI.allTiming = theZone:getBoolFromZoneProperty("allTiming", false)
|
||||
end
|
||||
|
||||
--
|
||||
@ -681,7 +675,6 @@ function cfxArtilleryUI.start()
|
||||
cfxArtilleryUI.readConfigZone()
|
||||
|
||||
-- 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!
|
||||
-- contains per group player record. Does not resolve on unit level!
|
||||
for gname, pgroup in pairs(allPlayerGroups) do
|
||||
@ -710,5 +703,5 @@ end
|
||||
|
||||
--[[--
|
||||
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.version = "2.2.2"
|
||||
cfxArtilleryZones.version = "3.0.0"
|
||||
cfxArtilleryZones.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxZones", -- Zones, of course
|
||||
@ -31,28 +31,12 @@ cfxArtilleryZones.verbose = false
|
||||
2.2.0 - DML Watchflag integration
|
||||
2.2.1 - minor code clean-up
|
||||
2.2.2 - new doParametricFireAt()
|
||||
|
||||
3.0.0 - dmlZones, OOP
|
||||
- cleanup
|
||||
|
||||
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
|
||||
Copyright (c) 2021, 2022 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
|
||||
Copyright (c) 2021 - 2024 by Christian Franz and cf/x AG
|
||||
|
||||
--]]--
|
||||
cfxArtilleryZones.artilleryZones = {}
|
||||
@ -117,42 +101,39 @@ function cfxArtilleryZones.createArtilleryTarget(name, point, coalition, spotRan
|
||||
end
|
||||
|
||||
function cfxArtilleryZones.processArtilleryZone(aZone)
|
||||
aZone.artilleryTarget = cfxZones.getStringFromZoneProperty(aZone, "artilleryTarget", aZone.name)
|
||||
aZone.coalition = cfxZones.getCoalitionFromZoneProperty(aZone, "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.shellStrength = cfxZones.getNumberFromZoneProperty(aZone, "shellStrength", 500) -- power of shells (strength)
|
||||
aZone.artilleryTarget = aZone:getStringFromZoneProperty( "artilleryTarget", aZone.name)
|
||||
aZone.coalition = aZone:getCoalitionFromZoneProperty("coalition", 0) -- side that marks it on map, and who fires arty
|
||||
aZone.spotRange = aZone:getNumberFromZoneProperty("spotRange", 3000) -- FO max range to direct fire
|
||||
aZone.shellStrength = aZone:getNumberFromZoneProperty("shellStrength", 500) -- power of shells (strength)
|
||||
|
||||
aZone.shellNum = cfxZones.getNumberFromZoneProperty(aZone, "shellNum", 17) -- number of shells in bombardment
|
||||
aZone.transitionTime = cfxZones.getNumberFromZoneProperty(aZone, "transitionTime", 20) -- average time of travel for projectiles
|
||||
aZone.addMark = cfxZones.getBoolFromZoneProperty(aZone, "addMark", true) -- note: defaults to true
|
||||
aZone.shellVariance = cfxZones.getNumberFromZoneProperty(aZone, "shellVariance", 0.2) -- strength of explosion can vary by +/- this amount
|
||||
aZone.shellNum = aZone:getNumberFromZoneProperty("shellNum", 17) -- number of shells in bombardment
|
||||
aZone.transitionTime = aZone:getNumberFromZoneProperty( "transitionTime", 20) -- average time of travel for projectiles
|
||||
aZone.addMark = aZone:getBoolFromZoneProperty("addMark", true) -- note: defaults to true
|
||||
aZone.shellVariance = aZone:getNumberFromZoneProperty( "shellVariance", 0.2) -- strength of explosion can vary by +/- this amount
|
||||
|
||||
-- watchflag:
|
||||
-- triggerMethod
|
||||
aZone.artyTriggerMethod = cfxZones.getStringFromZoneProperty(aZone, "artyTriggerMethod", "change")
|
||||
aZone.artyTriggerMethod = aZone:getStringFromZoneProperty( "artyTriggerMethod", "change")
|
||||
|
||||
if cfxZones.hasProperty(aZone, "triggerMethod") then
|
||||
aZone.artyTriggerMethod = cfxZones.getStringFromZoneProperty(aZone, "triggerMethod", "change")
|
||||
if aZone:hasProperty("triggerMethod") then
|
||||
aZone.artyTriggerMethod = aZone:getStringFromZoneProperty("triggerMethod", "change")
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(aZone, "f?") then
|
||||
aZone.artyTriggerFlag = cfxZones.getStringFromZoneProperty(aZone, "f?", "none")
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(aZone, "artillery?") then
|
||||
aZone.artyTriggerFlag = cfxZones.getStringFromZoneProperty(aZone, "artillery?", "none")
|
||||
end
|
||||
if cfxZones.hasProperty(aZone, "in?") then
|
||||
aZone.artyTriggerFlag = cfxZones.getStringFromZoneProperty(aZone, "in?", "none")
|
||||
if aZone:hasProperty("f?") then
|
||||
aZone.artyTriggerFlag = cfxZones.getStringFromZoneProperty("f?", "none")
|
||||
elseif aZone:hasProperty("artillery?") then
|
||||
aZone.artyTriggerFlag = aZone:getStringFromZoneProperty("artillery?", "none")
|
||||
elseif aZone:hasProperty("in?") then
|
||||
aZone.artyTriggerFlag = aZone:getStringFromZoneProperty("in?", "none")
|
||||
end
|
||||
|
||||
if aZone.artyTriggerFlag then
|
||||
aZone.lastTriggerValue = trigger.misc.getUserFlag(aZone.artyTriggerFlag) -- save last value
|
||||
end
|
||||
aZone.cooldown =cfxZones.getNumberFromZoneProperty(aZone, "cooldown", 120) -- seconds
|
||||
aZone.baseAccuracy = cfxZones.getNumberFromZoneProperty(aZone, "baseAccuracy", aZone.radius) -- meters from center radius shell impact
|
||||
aZone.cooldown = aZone:getNumberFromZoneProperty("cooldown", 120) -- seconds
|
||||
aZone.baseAccuracy = aZone:getNumberFromZoneProperty( "baseAccuracy", aZone.radius) -- meters from center radius shell impact
|
||||
-- use zone radius as mase accuracy for simple placement
|
||||
aZone.silent = cfxZones.getBoolFromZoneProperty(aZone, "silent", false)
|
||||
aZone.silent = aZone:getBoolFromZoneProperty("silent", false)
|
||||
end
|
||||
|
||||
function cfxArtilleryZones.addArtilleryZone(aZone)
|
||||
@ -200,7 +181,7 @@ function cfxArtilleryZones.artilleryZonesInRangeOfUnit(theUnit)
|
||||
-- is it one of mine?
|
||||
if aZone.coalition == myCoalition then
|
||||
-- is it close enough?
|
||||
local zP = cfxZones.getPoint(aZone)
|
||||
local zP = aZone:getPoint()
|
||||
aZone.landHeight = land.getHeight({x = zP.x, y= zP.z})
|
||||
local zonePoint = {x = zP.x, y = aZone.landHeight, z = zP.z}
|
||||
local d = dcsCommon.dist(p,zonePoint)
|
||||
@ -249,7 +230,7 @@ end
|
||||
--
|
||||
|
||||
--
|
||||
-- BOOM command
|
||||
-- BOOM method
|
||||
--
|
||||
function cfxArtilleryZones.doBoom(args)
|
||||
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)
|
||||
end
|
||||
--trigger.action.smoke(center, 2) -- mark location visually
|
||||
end
|
||||
|
||||
function cfxArtilleryZones.simSmokeZone(aZone, aGroup, aColor)
|
||||
@ -367,7 +347,7 @@ function cfxArtilleryZones.simSmokeZone(aZone, aGroup, aColor)
|
||||
if type(aColor) == "string" then
|
||||
aColor = dcsCommon.smokeColor2Num(aColor)
|
||||
end
|
||||
local zP = cfxZones.getPoint(aZone)
|
||||
local zP = aZone:getPoint(aZone)
|
||||
aZone.landHeight = land.getHeight({x = zP.x, y= zP.z})
|
||||
|
||||
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
|
||||
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!
|
||||
cfxArtilleryZones.doFireAt(aZone) -- all from zone vars!
|
||||
if cfxArtilleryZones.verbose then
|
||||
@ -413,10 +393,10 @@ function cfxArtilleryZones.update()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- old code
|
||||
--[[--
|
||||
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
|
||||
then
|
||||
-- a triggered release!
|
||||
@ -430,6 +410,7 @@ function cfxArtilleryZones.update()
|
||||
end
|
||||
|
||||
end
|
||||
--]]--
|
||||
end
|
||||
end
|
||||
|
||||
@ -446,8 +427,6 @@ function cfxArtilleryZones.start()
|
||||
-- collect all spawn zones
|
||||
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
|
||||
cfxArtilleryZones.processArtilleryZone(aZone) -- process attribute and add to zone
|
||||
cfxArtilleryZones.addArtilleryZone(aZone) -- remember it so we can smoke it
|
||||
@ -1,25 +1,17 @@
|
||||
cfxCargoReceiver = {}
|
||||
cfxCargoReceiver.version = "1.2.2"
|
||||
cfxCargoReceiver.version = "2.0.0"
|
||||
cfxCargoReceiver.ups = 1 -- once a second
|
||||
cfxCargoReceiver.maxDirectionRange = 500 -- in m. distance when cargo manager starts talking to pilots who are carrying that cargo
|
||||
cfxCargoReceiver.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxPlayer", -- for directions
|
||||
"cfxZones", -- Zones, of course
|
||||
"cfxCargoManager", -- will notify me on a cargo event
|
||||
}
|
||||
--[[--
|
||||
Version history
|
||||
- 1.0.0 initial vbersion
|
||||
- 1.1.0 added flag manipulation options
|
||||
no negative agl on announcement
|
||||
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
|
||||
- 2.0.0 no more cfxPlayer Dependency
|
||||
dmlZones, OOP
|
||||
clean-up
|
||||
|
||||
|
||||
CargoReceiver is a zone enhancement you use to be automatically
|
||||
@ -27,14 +19,7 @@ cfxCargoReceiver.requiredLibs = {
|
||||
It also provides BRA when in range to a cargo receiver
|
||||
|
||||
*** 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 = {}
|
||||
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
|
||||
aZone.isCargoReceiver = true
|
||||
-- we can add additional processing here
|
||||
aZone.autoRemove = cfxZones.getBoolFromZoneProperty(aZone, "autoRemove", false) -- maybe add a removeDelay
|
||||
aZone.removeDelay = cfxZones.getNumberFromZoneProperty(aZone, "removeDelay", 1)
|
||||
aZone.autoRemove = aZone:getBoolFromZoneProperty("autoRemove", false) -- maybe add a removeDelay
|
||||
aZone.removeDelay = aZone:getNumberFromZoneProperty("removeDelay", 1)
|
||||
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
|
||||
aZone.cargoMethod = cfxZones.getStringFromZoneProperty(aZone, "method", "inc")
|
||||
if cfxZones.hasProperty(aZone, "cargoMethod") then
|
||||
aZone.cargoMethod = cfxZones.getStringFromZoneProperty(aZone, "cargoMethod", "inc")
|
||||
aZone.cargoMethod = aZone:getStringFromZoneProperty("method", "inc")
|
||||
if aZone:hasProperty("cargoMethod") then
|
||||
aZone.cargoMethod = aZone:getStringFromZoneProperty("cargoMethod", "inc")
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(aZone, "f!") then
|
||||
aZone.outReceiveFlag = cfxZones.getStringFromZoneProperty(aZone, "f!", "*<none>")
|
||||
elseif cfxZones.hasProperty(aZone, "cargoReceived!") then
|
||||
aZone.outReceiveFlag = cfxZones.getStringFromZoneProperty(aZone, "cargoReceived!", "*<none>")
|
||||
if aZone:hasProperty("f!") then
|
||||
aZone.outReceiveFlag = aZone:getStringFromZoneProperty("f!", "*<none>")
|
||||
elseif aZone:hasProperty("cargoReceived!") then
|
||||
aZone.outReceiveFlag = aZone:getStringFromZoneProperty( "cargoReceived!", "*<none>")
|
||||
end
|
||||
|
||||
end
|
||||
@ -134,17 +91,14 @@ end
|
||||
|
||||
function cfxCargoReceiver.cargoEvent(event, object, name)
|
||||
-- usually called from cargomanager
|
||||
--trigger.action.outText("Cargo Receiver: event <" .. event .. "> for " .. name, 30)
|
||||
|
||||
if not event then return end
|
||||
if event == "grounded" then
|
||||
--trigger.action.outText("+++rcv: grounded for " .. name, 30)
|
||||
-- this is actually the only one that interests us
|
||||
if not object then
|
||||
--trigger.action.outText("+++rcv: " .. name .. " has null object", 30)
|
||||
return
|
||||
end
|
||||
if not object:isExist() then
|
||||
--trigger.action.outText("+++rcv: " .. name .. " no longer exists", 30)
|
||||
if not Object.isExist(object) then
|
||||
return
|
||||
end
|
||||
loc = object:getPoint()
|
||||
@ -156,28 +110,10 @@ function cfxCargoReceiver.cargoEvent(event, object, name)
|
||||
cfxCargoReceiver.invokeCallback("deliver", object, name, aZone)
|
||||
|
||||
-- set flags as indicated
|
||||
--[[--
|
||||
if aZone.setFlag then
|
||||
trigger.action.setUserFlag(aZone.setFlag, 1)
|
||||
end
|
||||
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
|
||||
cfxZones.pollFlag(aZone.outReceiveFlag, aZone.cargoMethod, aZone)
|
||||
end
|
||||
|
||||
--trigger.action.outText("+++rcv: " .. name .. " delivered in zone " .. aZone.name, 30)
|
||||
--trigger.action.outSound("Quest Snare 3.wav")
|
||||
if aZone.autoRemove then
|
||||
-- schedule this for in a few seconds?
|
||||
local args = {}
|
||||
@ -218,12 +154,12 @@ function cfxCargoReceiver.update()
|
||||
-- this cargo can be talked down.
|
||||
-- find the player unit that is closest to in in hopes
|
||||
-- that that is the one carrying it
|
||||
local allPlayers = cfxPlayer.getAllPlayers() -- idx by name
|
||||
for pname, info in pairs(allPlayers) do
|
||||
local allPlayers = dcsCommon.getAllExistingPlayersAndUnits() -- idx by name
|
||||
for pname, theUnit in pairs(allPlayers) do
|
||||
-- iterate all player units
|
||||
local closestUnit = nil
|
||||
local minDelta = math.huge
|
||||
local theUnit = info.unit
|
||||
--local theUnit = info.unit
|
||||
if theUnit:isExist() then
|
||||
local uPoint = theUnit:getPoint()
|
||||
local currDelta = dcsCommon.distFlat(thePoint, uPoint)
|
||||
@ -305,4 +241,4 @@ if not cfxCargoReceiver.start() then
|
||||
end
|
||||
|
||||
-- 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.version = "1.2.6"
|
||||
cfxMX.version = "2.0.0"
|
||||
cfxMX.verbose = false
|
||||
--[[--
|
||||
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
|
||||
|
||||
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
|
||||
- train carve-outs for vehicles
|
||||
2.0.0 - clean-up
|
||||
- harmonized with cfxGroups
|
||||
|
||||
--]]--
|
||||
cfxMX.groupNamesByID = {}
|
||||
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.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)
|
||||
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
|
||||
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
|
||||
for obj_type_name, obj_type_data in pairs(cntry_data) do
|
||||
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)
|
||||
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!
|
||||
@ -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
|
||||
linkUnit = group_data.route.points[1].linkUnit
|
||||
if linkUnit then
|
||||
--trigger.action.outText("MX: found missing link to " .. linkUnit .. " in " .. group_data.name, 30)
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@ -207,7 +195,7 @@ function cfxMX.createCrossReferences()
|
||||
obj_type_name == "plane" or
|
||||
obj_type_name == "vehicle" or
|
||||
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)
|
||||
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!
|
||||
@ -252,6 +240,12 @@ function cfxMX.createCrossReferences()
|
||||
end
|
||||
-- now iterate all units in this group
|
||||
-- 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
|
||||
if unit_data.skill 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.playerGroupByName[aName] = group_data -- inefficient, but works
|
||||
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 has skill
|
||||
cfxMX.unitIDbyName[unit_data.name] = unit_data.unitId
|
||||
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 --if has category data
|
||||
end --if plane, helo etc... category
|
||||
@ -274,6 +296,31 @@ function cfxMX.createCrossReferences()
|
||||
end --for all coalitions in mission
|
||||
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)
|
||||
local outCat = 0 -- airplane
|
||||
local c = inText:lower()
|
||||
@ -283,7 +330,7 @@ function cfxMX.catText2ID(inText)
|
||||
if c == "vehicle" then outCat = 2 end
|
||||
if c == "train" then outCat = 4 end
|
||||
if c == "static" then outCat = -1 end
|
||||
--trigger.action.outText("cat2text: in <" .. inText .. "> out <" .. outCat .. ">", 30)
|
||||
|
||||
return outCat
|
||||
end
|
||||
|
||||
@ -292,6 +339,7 @@ function cfxMX.start()
|
||||
if cfxMX.verbose then
|
||||
trigger.action.outText("cfxMX: "..#cfxMX.groupNamesByID .. " groups processed successfully", 30)
|
||||
end
|
||||
trigger.action.outText("cfxMX v." .. cfxMX.version .. " started.", 30)
|
||||
end
|
||||
|
||||
-- start
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxZones = {}
|
||||
cfxZones.version = "4.0.10"
|
||||
cfxZones.version = "4.1.1"
|
||||
|
||||
-- cf/x zone management module
|
||||
-- reads dcs zones and makes them accessible and mutable
|
||||
@ -9,31 +9,6 @@ cfxZones.version = "4.0.10"
|
||||
--
|
||||
|
||||
--[[-- 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
|
||||
- code revision / refactoring
|
||||
- moved createPoint and copxPoint to dcsCommon, added bridging code
|
||||
@ -66,6 +41,9 @@ cfxZones.version = "4.0.10"
|
||||
- createPolyZone now correctly inits dcsOrigin
|
||||
- createCircleZone noew correctly inits dcsOrigin
|
||||
- 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.
|
||||
-- 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)
|
||||
if (clearfirst) then
|
||||
cfxZones.zones = {}
|
||||
@ -617,9 +561,7 @@ function cfxZones.createRandomZoneInZone(name, inZone, targetRadius, entirelyIns
|
||||
-- if entirelyInside is false, only the zone's center is guaranteed to be inside
|
||||
-- inZone.
|
||||
-- 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
|
||||
local sourceRadius = inZone.radius
|
||||
if entirelyInside and targetRadius > sourceRadius then targetRadius = sourceRadius end
|
||||
@ -1364,11 +1306,11 @@ end
|
||||
|
||||
|
||||
-- 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
|
||||
if not groupName then groupName = "G_"..theZone.name end
|
||||
-- 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
|
||||
if (not heading) then heading = 0 end
|
||||
@ -1380,7 +1322,6 @@ function cfxZones.createGroundUnitsInZoneForCoalition (theCoalition, groupName,
|
||||
theZone.point.x,
|
||||
theZone.point.z) -- watchit: Z!!!
|
||||
|
||||
|
||||
-- create the group in the world and return it
|
||||
-- first we need to translate the coalition to a legal
|
||||
-- country. we use UN for neutral, cjtf for red and blue
|
||||
@ -1446,7 +1387,7 @@ function cfxZones.unPulseFlag(args)
|
||||
cfxZones.setFlagValue(theFlag, newVal, theZone)
|
||||
end
|
||||
|
||||
function cfxZones.evalRemainder(remainder)
|
||||
function cfxZones.evalRemainder(remainder, theZone)
|
||||
local rNum = tonumber(remainder)
|
||||
if not rNum then
|
||||
-- we use remainder as name for flag
|
||||
@ -1475,6 +1416,10 @@ function cfxZones.evalRemainder(remainder)
|
||||
return rNum
|
||||
end
|
||||
|
||||
function dmlZone:evalRemainder(remainder)
|
||||
return cfxZones.evalRemainder(remainder, self)
|
||||
end
|
||||
|
||||
function cfxZones.doPollFlag(theFlag, method, theZone) -- no OOP equivalent
|
||||
-- WARNING:
|
||||
-- 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
|
||||
-- only go true if exact match to yes or true
|
||||
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
|
||||
end
|
||||
|
||||
@ -2407,13 +2352,13 @@ function dmlZone:getBoolFromZoneProperty(theProperty, defaultVal)
|
||||
if defaultVal == false then
|
||||
-- only go true if exact match to yes or true
|
||||
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
|
||||
end
|
||||
|
||||
local theBool = true
|
||||
-- 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
|
||||
end
|
||||
|
||||
@ -3576,8 +3521,6 @@ function cfxZones.init()
|
||||
|
||||
-- pre-read zone owner for all zones
|
||||
-- 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
|
||||
aZone.owner = cfxZones.getCoalitionFromZoneProperty(aZone, "owner", 0)
|
||||
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.version = "1.9.1"
|
||||
cloneZones.version = "2.0.1"
|
||||
cloneZones.verbose = false
|
||||
cloneZones.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
@ -27,79 +27,9 @@ cloneZones.respawnOnGroupID = true
|
||||
|
||||
--[[--
|
||||
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
|
||||
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
|
||||
- spawnWithSpawner alias for HeloTroops etc requestable SPAWN
|
||||
- requestable attribute
|
||||
@ -107,6 +37,9 @@ cloneZones.respawnOnGroupID = true
|
||||
- cloner collects all types used
|
||||
- groupScheme 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)
|
||||
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)
|
||||
if not theZone then return end
|
||||
if not reason then reason = "<none>" end
|
||||
@ -158,8 +81,6 @@ function cloneZones.invokeCallbacks(theZone, reason, args)
|
||||
end
|
||||
end
|
||||
|
||||
-- group translation orig id
|
||||
|
||||
--
|
||||
-- reading zones
|
||||
--
|
||||
@ -217,7 +138,6 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
|
||||
theZone.cloner = true -- this is a cloner zoner
|
||||
theZone.mySpawns = {}
|
||||
theZone.myStatics = {}
|
||||
-- update: getPoint is bad if it's a moving zone.
|
||||
-- use getDCSOrigin instead
|
||||
theZone.origin = theZone:getDCSOrigin()
|
||||
|
||||
@ -247,7 +167,6 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
|
||||
-- iterate all units and save their individual types
|
||||
for idy, aUnit in pairs(rawData.units) do
|
||||
local theType = aUnit.type
|
||||
-- trigger.action.outText("proccing type <" .. theType .. ">", 30)
|
||||
if not theZone.allTypes[theType] then
|
||||
theZone.allTypes[theType] = 1 -- first one
|
||||
else
|
||||
@ -318,11 +237,8 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
|
||||
|
||||
theZone.cooldown = theZone:getNumberFromZoneProperty("cooldown", -1) -- anything > 0 activates cd
|
||||
theZone.lastSpawnTimeStamp = -10000
|
||||
|
||||
theZone.onStart = theZone:getBoolFromZoneProperty("onStart", false)
|
||||
|
||||
theZone.moveRoute = theZone:getBoolFromZoneProperty("moveRoute", false)
|
||||
|
||||
theZone.preWipe = theZone:getBoolFromZoneProperty("preWipe", false)
|
||||
|
||||
if theZone:hasProperty("empty!") then
|
||||
@ -383,7 +299,6 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
|
||||
theZone.rndHeading = theZone:getBoolFromZoneProperty("rndHeading", false)
|
||||
|
||||
theZone.onRoad = theZone:getBoolFromZoneProperty("onRoad", false)
|
||||
|
||||
theZone.onPerimeter = theZone:getBoolFromZoneProperty("onPerimeter", false)
|
||||
|
||||
-- check for name scheme and / or identical
|
||||
@ -418,9 +333,7 @@ function cloneZones.despawnAll(theZone)
|
||||
if cloneZones.verbose or theZone.verbose then
|
||||
trigger.action.outText("+++clnZ: despawn all - wiping zone <" .. theZone.name .. ">", 30)
|
||||
end
|
||||
for idx, aGroup in pairs(theZone.mySpawns) do
|
||||
--trigger.action.outText("++clnZ: despawn all " .. aGroup.name, 30)
|
||||
|
||||
for idx, aGroup in pairs(theZone.mySpawns) do
|
||||
if aGroup:isExist() then
|
||||
if theZone.verbose then
|
||||
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>")
|
||||
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
|
||||
end
|
||||
|
||||
@ -866,7 +776,6 @@ function cloneZones.resolveWPReferences(rawData, theZone, dataTable)
|
||||
for grpIdx, gID in pairs(embarkers) do
|
||||
local resolvedID = cloneZones.resolveGroupID(gID, rawData, dataTable, "embark")
|
||||
table.insert(newEmbarkers, resolvedID)
|
||||
--trigger.action.outText("+++clnZ: resolved embark group id <" .. gID .. "> to <" .. resolvedID .. ">", 30)
|
||||
end
|
||||
-- replace old with new table
|
||||
taskData.params.groupsForEmbarking = newEmbarkers
|
||||
@ -884,7 +793,6 @@ function cloneZones.resolveWPReferences(rawData, theZone, dataTable)
|
||||
-- translate old to new
|
||||
local resolvedID = cloneZones.resolveGroupID(gID, rawData, dataTable, "embark")
|
||||
table.insert(newEmbarkers, resolvedID)
|
||||
--trigger.action.outText("+++clnZ: resolved distribute unit/group id <" .. aUnit .. "/" .. gID .. "> to <".. newUnit .. "/" .. resolvedID .. ">", 30)
|
||||
end
|
||||
-- store this as new group for
|
||||
-- translated transportID
|
||||
@ -892,7 +800,6 @@ function cloneZones.resolveWPReferences(rawData, theZone, dataTable)
|
||||
end
|
||||
-- replace old distribution with new
|
||||
taskData.params.distribution = newDist
|
||||
--trigger.action.outText("+++clnZ: rebuilt distribution", 30)
|
||||
end
|
||||
|
||||
-- resolve selectedTransport unit reference
|
||||
@ -900,7 +807,6 @@ function cloneZones.resolveWPReferences(rawData, theZone, dataTable)
|
||||
local tID = taskData.params.selectedTransport
|
||||
local newTID = cloneZones.resolveUnitID(tID, rawData, dataTable, "transportID")
|
||||
taskData.params.selectedTransport = newTID
|
||||
--trigger.action.outText("+++clnZ: resolved selected transport <" .. tID .. "> to <" .. newTID .. ">", 30)
|
||||
end
|
||||
|
||||
-- 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
|
||||
-- the group referred to is also a clone, and update
|
||||
-- the reference to the newest incardnation
|
||||
|
||||
for idx, rawData in pairs(dataTable) do
|
||||
-- resolve references in waypoints
|
||||
cloneZones.resolveWPReferences(rawData, theZone, dataTable)
|
||||
@ -946,7 +851,6 @@ function cloneZones.handoffTracking(theGroup, theZone)
|
||||
return
|
||||
end
|
||||
local trackerName = theZone.trackWith
|
||||
--if trackerName == "*" then trackerName = theZone.name end
|
||||
-- now assemble a list of all trackers
|
||||
if cloneZones.verbose or theZone.verbose then
|
||||
trigger.action.outText("+++clnZ: clone pass-off: " .. trackerName, 30)
|
||||
@ -1065,8 +969,6 @@ function cloneZones.forcedRespawn(args)
|
||||
if newGroupID == theData.CZTargetID then
|
||||
if verbose then
|
||||
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)
|
||||
end
|
||||
spawnedGroups[pos] = theGroup
|
||||
@ -1104,7 +1006,7 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
|
||||
local newCenter = spawnZone:getPoint() -- includes zone following updates
|
||||
local oCenter = theZone:getDCSOrigin() -- get original coords on map for cloning offsets
|
||||
-- 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
|
||||
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
|
||||
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].y = rawData.units[1].y
|
||||
rawData.x = rawData.units[1].x
|
||||
@ -1439,7 +1340,6 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
|
||||
if not spawnZone.identical then
|
||||
-- make sure static name is unique and remember original
|
||||
cloneZones.uniqueNameStaticData(rawData, spawnZone, theZone.name)
|
||||
--rawData.name = dcsCommon.uuid(rawData.name)
|
||||
rawData.unitId = cloneZones.uniqueID()
|
||||
end
|
||||
rawData.CZTargetID = rawData.unitId
|
||||
@ -1529,7 +1429,6 @@ function cloneZones.turnOffAI(args)
|
||||
local theGroup = args[1]
|
||||
local theController = theGroup:getController()
|
||||
theController:setOnOff(false)
|
||||
-- trigger.action.outText("turned off AI for group <" .. theGroup:getName() .. "> ", 30)
|
||||
end
|
||||
|
||||
-- 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
|
||||
-- check if side is correct for owned zone
|
||||
local resolved = cloneZones.resolveOwningCoalition(aZone)
|
||||
--if resolved ~= 0 and resolved ~= aSide then
|
||||
if resolved == 0 or resolved ~= aSide then
|
||||
-- failed ownership test. must match and not be zero
|
||||
hasMatch = false
|
||||
@ -1749,7 +1647,7 @@ function cloneZones.update()
|
||||
for idx, aZone in pairs(cloneZones.cloners) do
|
||||
-- see if deSpawn was pulled. Must run before spawn
|
||||
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 cloneZones.verbose or aZone.verbose then
|
||||
trigger.action.outText("+++clnZ: DEspawn triggered for <" .. aZone.name .. ">", 30)
|
||||
@ -1760,20 +1658,23 @@ function cloneZones.update()
|
||||
end
|
||||
|
||||
-- see if we got spawn? command
|
||||
local willSpawn = false -- init to false.
|
||||
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)
|
||||
end
|
||||
cloneZones.spawnWithCloner(aZone)
|
||||
willSpawn = true -- in case prewipe, we delay
|
||||
-- can mess with empty, so we tell empty to skip
|
||||
end
|
||||
|
||||
-- empty handling
|
||||
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
|
||||
if aZone.emptyBangFlag then
|
||||
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)
|
||||
end
|
||||
end
|
||||
@ -1983,7 +1884,6 @@ function cloneZones.loadData()
|
||||
trigger.action.outText("+++clnZ: linked static <" .. oName .. "> to unit <" .. newStatic.linkUnit .. ">", 30)
|
||||
end
|
||||
local cty = newStatic.cty
|
||||
-- local cat = staticData.cat
|
||||
-- spawn new one, replacing same.named old, dead if required
|
||||
gStatic = coalition.addStaticObject(cty, newStatic)
|
||||
|
||||
@ -2004,7 +1904,7 @@ function cloneZones.loadData()
|
||||
for cName, cData in pairs(allCloners) do
|
||||
local theCloner = cloneZones.getCloneZoneByName(cName)
|
||||
if theCloner then
|
||||
theCloner.isStarted = true -- ALWAYS TRUE WHEN WE COME HERE! cData.isStarted
|
||||
theCloner.isStarted = true
|
||||
-- init myUniqueCounter if it exists
|
||||
if cData.myUniqueCounter then
|
||||
theCloner.myUniqueCounter = cData.myUniqueCounter
|
||||
@ -2094,10 +1994,7 @@ function cloneZones.start()
|
||||
cloneZones.readConfigZone()
|
||||
|
||||
-- process cloner Zones
|
||||
local attrZones = cfxZones.getZonesWithAttributeNamed("cloner")
|
||||
|
||||
-- now create an rnd gen for each one and add them
|
||||
-- to our watchlist
|
||||
local attrZones = cfxZones.getZonesWithAttributeNamed("cloner")
|
||||
for k, aZone in pairs(attrZones) do
|
||||
cloneZones.createClonerWithZone(aZone) -- process attribute and add to zone
|
||||
cloneZones.addCloneZone(aZone)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
countDown = {}
|
||||
countDown.version = "1.3.2"
|
||||
countDown.version = "2.0.0"
|
||||
countDown.verbose = false
|
||||
countDown.ups = 1
|
||||
countDown.requiredLibs = {
|
||||
@ -9,26 +9,14 @@ countDown.requiredLibs = {
|
||||
|
||||
--[[--
|
||||
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
|
||||
1.0.0 - initial version
|
||||
1.1.0 - Lua interface: callbacks
|
||||
- corrected verbose (erroneously always suppressed)
|
||||
- triggerFlag --> triggerCountFlag
|
||||
1.1.1 - corrected bug in invokeCallback
|
||||
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?
|
||||
|
||||
2.0.0 - dmlZones, OOP upgrade
|
||||
counterOut! --> counterOut#
|
||||
output method defaults to "inc"
|
||||
better config parsing
|
||||
cleanup
|
||||
--]]--
|
||||
|
||||
countDown.counters = {}
|
||||
@ -77,7 +65,7 @@ end
|
||||
--
|
||||
function countDown.createCountDownWithZone(theZone)
|
||||
-- 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)
|
||||
|
||||
if countDown.verbose then
|
||||
@ -85,32 +73,32 @@ function countDown.createCountDownWithZone(theZone)
|
||||
end
|
||||
|
||||
-- loop
|
||||
theZone.loop = cfxZones.getBoolFromZoneProperty(theZone, "loop", false)
|
||||
theZone.loop = theZone:getBoolFromZoneProperty("loop", false)
|
||||
|
||||
-- extend after zero
|
||||
theZone.belowZero = cfxZones.getBoolFromZoneProperty(theZone, "belowZero", false)
|
||||
theZone.belowZero = theZone:getBoolFromZoneProperty("belowZero", false)
|
||||
|
||||
-- out method
|
||||
theZone.ctdwnMethod = cfxZones.getStringFromZoneProperty(theZone, "method", "flip")
|
||||
if cfxZones.hasProperty(theZone, "ctdwnMethod") then
|
||||
theZone.ctdwnMethod = cfxZones.getStringFromZoneProperty(theZone, "ctdwnMethod", "flip")
|
||||
theZone.ctdwnMethod = theZone:getStringFromZoneProperty("method", "inc")
|
||||
if theZone:hasProperty("ctdwnMethod") then
|
||||
theZone.ctdwnMethod = theZone:getStringFromZoneProperty( "ctdwnMethod", "inc")
|
||||
end
|
||||
|
||||
-- triggerMethod for inputs
|
||||
theZone.ctdwnTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "triggerMethod", "change")
|
||||
theZone.ctdwnTriggerMethod = theZone:getStringFromZoneProperty( "triggerMethod", "change")
|
||||
|
||||
if cfxZones.hasProperty(theZone, "ctdwnTriggerMethod") then
|
||||
theZone.ctdwnTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "ctdwnTriggerMethod", "change")
|
||||
if theZone:hasProperty("ctdwnTriggerMethod") then
|
||||
theZone.ctdwnTriggerMethod = theZone:getStringFromZoneProperty("ctdwnTriggerMethod", "change")
|
||||
end
|
||||
|
||||
-- trigger flag "count" / "start?"
|
||||
if cfxZones.hasProperty(theZone, "count?") then
|
||||
theZone.triggerCountFlag = cfxZones.getStringFromZoneProperty(theZone, "count?", "<none>")
|
||||
elseif cfxZones.hasProperty(theZone, "clock?") then
|
||||
theZone.triggerCountFlag = cfxZones.getStringFromZoneProperty(theZone, "clock?", "<none>")
|
||||
if theZone:hasProperty("count?") then
|
||||
theZone.triggerCountFlag = theZone:getStringFromZoneProperty("count?", "<none>")
|
||||
elseif theZone:hasProperty("clock?") then
|
||||
theZone.triggerCountFlag = theZone:getStringFromZoneProperty("clock?", "<none>")
|
||||
-- can also use in? for counting. we always use triggerCountFlag
|
||||
elseif cfxZones.hasProperty(theZone, "in?") then
|
||||
theZone.triggerCountFlag = cfxZones.getStringFromZoneProperty(theZone, "in?", "<none>")
|
||||
elseif theZone:hasProperty("in?") then
|
||||
theZone.triggerCountFlag = theZone:getStringFromZoneProperty("in?", "<none>")
|
||||
end
|
||||
|
||||
if theZone.triggerCountFlag then
|
||||
@ -118,40 +106,40 @@ function countDown.createCountDownWithZone(theZone)
|
||||
end
|
||||
|
||||
-- reset
|
||||
if cfxZones.hasProperty(theZone, "reset?") then
|
||||
theZone.resetFlag = cfxZones.getStringFromZoneProperty(theZone, "reset?", "<none>")
|
||||
if theZone:hasProperty("reset?") then
|
||||
theZone.resetFlag = theZone:getStringFromZoneProperty("reset?", "<none>")
|
||||
theZone.resetFlagValue = cfxZones.getFlagValue(theZone.resetFlag, theZone)
|
||||
end
|
||||
|
||||
-- zero! bang
|
||||
if cfxZones.hasProperty(theZone, "zero!") then
|
||||
theZone.zeroFlag = cfxZones.getStringFromZoneProperty(theZone, "zero!", "<none>")
|
||||
if theZone:hasProperty("zero!") then
|
||||
theZone.zeroFlag = theZone:getStringFromZoneProperty("zero!", "<none>")
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "out!") then
|
||||
theZone.zeroFlag = cfxZones.getStringFromZoneProperty(theZone, "out!", "<none>")
|
||||
if theZone:hasProperty("out!") then
|
||||
theZone.zeroFlag = theZone:getStringFromZoneProperty("out!", "<none>")
|
||||
end
|
||||
|
||||
-- TMinus! bang
|
||||
if cfxZones.hasProperty(theZone, "tMinus!") then
|
||||
theZone.tMinusFlag = cfxZones.getStringFromZoneProperty(theZone, "tMinus!", "<none>")
|
||||
if theZone:hasProperty("tMinus!") then
|
||||
theZone.tMinusFlag = theZone:getStringFromZoneProperty("tMinus!", "<none>")
|
||||
end
|
||||
|
||||
-- counterOut val
|
||||
if cfxZones.hasProperty(theZone, "counterOut!") then
|
||||
theZone.counterOut = cfxZones.getStringFromZoneProperty(theZone, "counterOut!", "<none>")
|
||||
if theZone:hasProperty("counterOut#") then
|
||||
theZone.counterOut = theZone:getStringFromZoneProperty( "counterOut#", "<none>")
|
||||
end
|
||||
|
||||
-- disableFlag/enableFlag
|
||||
theZone.counterDisabled = false
|
||||
if cfxZones.hasProperty(theZone, "disableCounter?") then
|
||||
theZone.disableCounterFlag = cfxZones.getStringFromZoneProperty(theZone, "disableCounter?", "<none>")
|
||||
theZone.disableCounterFlagVal = cfxZones.getFlagValue(theZone.disableCounterFlag, theZone)
|
||||
if theZone:hasProperty("disableCounter?") then
|
||||
theZone.disableCounterFlag = theZone:getStringFromZoneProperty("disableCounter?", "<none>")
|
||||
theZone.disableCounterFlagVal = theZone:getFlagValue(theZone.disableCounterFlag)
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "enableCounter?") then
|
||||
theZone.enableCounterFlag = cfxZones.getStringFromZoneProperty(theZone, "enableCounter?", "<none>")
|
||||
theZone.enableCounterFlagVal = cfxZones.getFlagValue(theZone.enableCounterFlag, theZone)
|
||||
if theZone:hasProperty("enableCounter?") then
|
||||
theZone.enableCounterFlag = theZone:getStringFromZoneProperty("enableCounter?", "<none>")
|
||||
theZone.enableCounterFlagVal = theZone:getFlagValue(theZone.enableCounterFlag)
|
||||
end
|
||||
|
||||
end
|
||||
@ -170,7 +158,7 @@ function countDown.reset(theZone)
|
||||
cfxZones.setFlagValue(theZone.counterOut, val, theZone)
|
||||
end
|
||||
-- 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
|
||||
end
|
||||
|
||||
@ -186,7 +174,7 @@ function countDown.isTriggered(theZone)
|
||||
local looping = false
|
||||
|
||||
if theZone.counterOut then
|
||||
cfxZones.setFlagValue(theZone.counterOut, val, theZone)
|
||||
theZone:setFlagValue(theZone.counterOut, val)
|
||||
end
|
||||
|
||||
if val > 0 then
|
||||
@ -196,7 +184,7 @@ function countDown.isTriggered(theZone)
|
||||
if countDown.verbose or theZone.verbose then
|
||||
trigger.action.outText("+++cntD: <" .. theZone.name .. "> TMINUTS on flag <" .. theZone.tMinusFlag .. ">", 30)
|
||||
end
|
||||
cfxZones.pollFlag(theZone.tMinusFlag, theZone.ctdwnMethod, theZone)
|
||||
theZone:pollFlag(theZone.tMinusFlag, theZone.ctdwnMethod)
|
||||
end
|
||||
|
||||
elseif val == 0 then
|
||||
@ -206,7 +194,7 @@ function countDown.isTriggered(theZone)
|
||||
if countDown.verbose or theZone.verbose then
|
||||
trigger.action.outText("+++cntD: ZERO <" .. theZone.name .. "> on flag <" .. theZone.zeroFlag .. ">", 30)
|
||||
end
|
||||
cfxZones.pollFlag(theZone.zeroFlag, theZone.ctdwnMethod, theZone)
|
||||
theZone:pollFlag(theZone.zeroFlag, theZone.ctdwnMethod)
|
||||
end
|
||||
|
||||
if theZone.loop then
|
||||
@ -225,7 +213,7 @@ function countDown.isTriggered(theZone)
|
||||
if countDown.verbose or theZone.verbose then
|
||||
trigger.action.outText("+++cntD: Below Zero", 30)
|
||||
end
|
||||
cfxZones.pollFlag(theZone.zeroFlag, theZone.ctdwnMethod, theZone)
|
||||
theZone:pollFlag(theZone.zeroFlag, theZone.ctdwnMethod)
|
||||
end
|
||||
|
||||
end
|
||||
@ -243,7 +231,7 @@ function countDown.update()
|
||||
|
||||
for idx, aZone in pairs(countDown.counters) do
|
||||
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
|
||||
countDown.reset(aZone)
|
||||
end
|
||||
@ -252,7 +240,7 @@ function countDown.update()
|
||||
-- make sure to re-start before reading time limit
|
||||
-- if reset, lastTriggerValue is updated and will not trigger
|
||||
if (not aZone.counterDisabled) and
|
||||
cfxZones.testZoneFlag(aZone, aZone.triggerCountFlag, aZone.ctdwnTriggerMethod, "lastCountTriggerValue")
|
||||
aZone:testZoneFlag(aZone.triggerCountFlag, aZone.ctdwnTriggerMethod, "lastCountTriggerValue")
|
||||
then
|
||||
if countDown.verbose then
|
||||
trigger.action.outText("+++cntD: triggered on in?", 30)
|
||||
@ -260,14 +248,14 @@ function countDown.update()
|
||||
countDown.isTriggered(aZone)
|
||||
end
|
||||
|
||||
if cfxZones.testZoneFlag(aZone, aZone.disableCounterFlag, aZone.ctdwnTriggerMethod, "disableCounterFlagVal") then
|
||||
if aZone:testZoneFlag(aZone.disableCounterFlag, aZone.ctdwnTriggerMethod, "disableCounterFlagVal") then
|
||||
if countDown.verbose then
|
||||
trigger.action.outText("+++cntD: disabling counter " .. aZone.name, 30)
|
||||
end
|
||||
aZone.counterDisabled = true
|
||||
end
|
||||
|
||||
if cfxZones.testZoneFlag(aZone, aZone.enableCounterFlag, aZone.ctdwnTriggerMethod, "enableCounterFlagVal") then
|
||||
if aZone:testZoneFlag(aZone.enableCounterFlag, aZone.ctdwnTriggerMethod, "enableCounterFlagVal") then
|
||||
if countDown.verbose then
|
||||
trigger.action.outText("+++cntD: ENabling counter " .. aZone.name, 30)
|
||||
end
|
||||
@ -282,16 +270,13 @@ end
|
||||
function countDown.readConfigZone()
|
||||
local theZone = cfxZones.getZoneByName("countDownConfig")
|
||||
if not theZone then
|
||||
if countDown.verbose then
|
||||
trigger.action.outText("+++cntD: NO config zone!", 30)
|
||||
end
|
||||
return
|
||||
theZone = cfxZones.createSimpleZone("countDownConfig")
|
||||
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
|
||||
|
||||
countDown.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
||||
countDown.verbose = theZone.verbose
|
||||
|
||||
if countDown.verbose then
|
||||
trigger.action.outText("+++cntD: read config", 30)
|
||||
|
||||
@ -1,183 +1,13 @@
|
||||
dcsCommon = {}
|
||||
dcsCommon.version = "2.9.8"
|
||||
--[[-- VERSION HISTORY
|
||||
2.2.6 - compassPositionOfARelativeToB
|
||||
- clockPositionOfARelativeToB
|
||||
2.2.7 - isTroopCarrier
|
||||
- distFlat
|
||||
2.2.8 - fixed event2text
|
||||
2.2.9 - getUnitAGL
|
||||
- getUnitAlt
|
||||
- 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.version = "3.0.0"
|
||||
--[[-- VERSION HISTORY
|
||||
3.0.0 - removed bad bug in stringStartsWith, only relevant if caseSensitive is false
|
||||
- point2text new intsOnly option
|
||||
- arrangeGroupDataIntoFormation minDist harden
|
||||
- cleanup
|
||||
- new pointInDirectionOfPointXYY()
|
||||
- createGroundGroupWithUnits now supports liveries
|
||||
- new getAllExistingPlayersAndUnits()
|
||||
--]]--
|
||||
|
||||
-- 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
|
||||
-- if no name given or aName = "*", then all bases are returned prior to filtering
|
||||
function dcsCommon.getAirbasesWhoseNameContains(aName, filterCat, filterCoalition)
|
||||
--trigger.action.outText("getAB(name): enter with " .. aName, 30)
|
||||
if not aName then aName = "*" end
|
||||
local allYourBase = world.getAirbases() -- get em all
|
||||
local areBelongToUs = {}
|
||||
@ -520,9 +349,6 @@ dcsCommon.version = "2.9.8"
|
||||
local airBaseName = aBase:getName() -- get display name
|
||||
if aName == "*" or dcsCommon.containsString(airBaseName, aName) then
|
||||
-- containsString is case insesitive unless told otherwise
|
||||
--if aName ~= "*" then
|
||||
-- trigger.action.outText("getAB(name): matched " .. airBaseName, 30)
|
||||
--end
|
||||
local doAdd = true
|
||||
if filterCat then
|
||||
local aCat = dcsCommon.getAirbaseCat(aBase)
|
||||
@ -628,13 +454,11 @@ dcsCommon.version = "2.9.8"
|
||||
for idx, aSlot in pairs(reallyFree) do
|
||||
local sp = {x = aSlot.vTerminalPos.x, y = 0, z = aSlot.vTerminalPos.z}
|
||||
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
|
||||
closestSlot = aSlot
|
||||
closestDist = currDist
|
||||
end
|
||||
end
|
||||
--trigger.action.outText("slot <" .. closestSlot.Term_Index .. "> has closest dist <" .. math.floor(closestDist) .. ">", 30)
|
||||
return closestSlot
|
||||
end
|
||||
|
||||
@ -888,12 +712,9 @@ dcsCommon.version = "2.9.8"
|
||||
if not A then return "***error:A***" end
|
||||
if not B then return "***error:B***" end
|
||||
if not headingOfBInDegrees then headingOfBInDegrees = 0 end
|
||||
|
||||
local bearing = dcsCommon.bearingInDegreesFromAtoB(B, A) -- returns 0..360
|
||||
-- trigger.action.outText("+++comm: oclock - bearing = " .. bearing .. " and inHeading = " .. headingOfBInDegrees, 30)
|
||||
local bearing = dcsCommon.bearingInDegreesFromAtoB(B, A) -- returns 0..360
|
||||
bearing = bearing - headingOfBInDegrees
|
||||
return dcsCommon.getClockDirection(bearing)
|
||||
|
||||
end
|
||||
|
||||
-- given a heading, return clock with 0 being 12, 180 being 6 etc.
|
||||
@ -1079,7 +900,6 @@ dcsCommon.version = "2.9.8"
|
||||
end
|
||||
|
||||
-- if we get here, there was no live unit
|
||||
--trigger.action.outText("+++cmn: A group has no live units. returning nil", 10)
|
||||
return nil
|
||||
|
||||
end
|
||||
@ -1107,7 +927,6 @@ dcsCommon.version = "2.9.8"
|
||||
end
|
||||
|
||||
-- if we get here, there was no live unit
|
||||
--trigger.action.outText("+++cmn A group has no live units. returning nil", 10)
|
||||
return nil
|
||||
|
||||
end
|
||||
@ -1233,7 +1052,6 @@ dcsCommon.version = "2.9.8"
|
||||
-- when filtering occurs in pre, an alternative 'rejected' handler can be called
|
||||
function dcsCommon.addEventHandler(f, pre, post, rejected) -- returns ID
|
||||
local handler = {} -- build a wrapper and connect the onEvent
|
||||
--dcsCommon.cbID = dcsCommon.cbID + 1 -- increment unique count
|
||||
handler.id = dcsCommon.uuid("eventHandler")
|
||||
handler.f = f -- the callback itself
|
||||
if (rejected) then handler.rejected = rejected end
|
||||
@ -1245,7 +1063,6 @@ dcsCommon.version = "2.9.8"
|
||||
function handler:onEvent(event)
|
||||
if not self.pre(event) then
|
||||
if dcsCommon.verbose then
|
||||
-- trigger.action.outText("event " .. event.id .. " discarded by pre-processor", 10)
|
||||
end
|
||||
if (self.rejected) then self.rejected(event) end
|
||||
return
|
||||
@ -1428,8 +1245,8 @@ dcsCommon.version = "2.9.8"
|
||||
rp.action = "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
|
||||
rp.alt = altitudeInFeet * 0.3048
|
||||
rp.speed = knots * 0.514444 -- we use
|
||||
rp.alt = altitudeInFeet * 0.3048 -- in m
|
||||
rp.speed = knots * 0.514444 -- we use m/s
|
||||
rp.alt_type = "BARO"
|
||||
if (altType) then rp.alt_type = altType end
|
||||
return rp
|
||||
@ -1485,7 +1302,7 @@ dcsCommon.version = "2.9.8"
|
||||
rp.action = "From Parking Area"
|
||||
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"
|
||||
return rp
|
||||
end
|
||||
@ -1673,7 +1490,6 @@ dcsCommon.version = "2.9.8"
|
||||
|
||||
-- now do the formation stuff
|
||||
-- make sure that they keep minimum distance
|
||||
-- trigger.action.outText("dcsCommon - processing formation " .. formation .. " with radius = " .. radius, 30)
|
||||
if formation == "LINE_V" then
|
||||
-- top to bottom in zone (heding 0). -- will run through x-coordinate
|
||||
-- use entire radius top to bottom
|
||||
@ -1682,7 +1498,6 @@ dcsCommon.version = "2.9.8"
|
||||
for i=1, num do
|
||||
|
||||
local u = theNewGroup.units[i]
|
||||
-- trigger.action.outText("formation unit " .. u.name .. " currX = " .. currX, 30)
|
||||
u.x = currX
|
||||
currX = currX + increment
|
||||
end
|
||||
@ -1698,7 +1513,6 @@ dcsCommon.version = "2.9.8"
|
||||
local increment = radius * 2/(num - 1) -- MUST NOT TRY WITH 1 UNIT!
|
||||
for i=1, num do
|
||||
local u = theNewGroup.units[i]
|
||||
-- trigger.action.outText("formation unit " .. u.name .. " currX = " .. currY, 30)
|
||||
u.y = currY
|
||||
currY = currY + increment
|
||||
end
|
||||
@ -1713,7 +1527,6 @@ dcsCommon.version = "2.9.8"
|
||||
local incrementX = radius * 2/(num - 1) -- MUST NOT TRY WITH 1 UNIT!
|
||||
for i=1, num do
|
||||
local u = theNewGroup.units[i]
|
||||
-- trigger.action.outText("formation unit " .. u.name .. " currX = " .. currX .. " currY = " .. currY, 30)
|
||||
u.x = currX
|
||||
u.y = currY
|
||||
-- calc coords for NEXT iteration
|
||||
@ -1731,6 +1544,7 @@ dcsCommon.version = "2.9.8"
|
||||
elseif formation == "SCATTERED" or formation == "RANDOM" then
|
||||
-- use randomPointInCircle and tehn iterate over all vehicles for mindelta
|
||||
processedUnits = {}
|
||||
if not minDist then minDist = 10 end
|
||||
for i=1, num do
|
||||
local emergencyBreak = 1 -- prevent endless loop
|
||||
local lowDist = 10000
|
||||
@ -1760,7 +1574,6 @@ dcsCommon.version = "2.9.8"
|
||||
|
||||
elseif dcsCommon.stringStartsWith(formation, "CIRCLE") then
|
||||
-- units are arranged on perimeter of circle defined by radius
|
||||
-- trigger.action.outText("formation circle detected", 30)
|
||||
local currAngle = 0
|
||||
local angleInc = 2 * 3.14157 / num -- increase per spoke
|
||||
for i=1, num do
|
||||
@ -1786,40 +1599,7 @@ dcsCommon.version = "2.9.8"
|
||||
-- calculate w
|
||||
local w = math.floor(num^(0.5) + 0.5)
|
||||
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
|
||||
if num < 2 then return end
|
||||
-- arrange units in an 2 x h grid
|
||||
@ -1896,11 +1676,14 @@ dcsCommon.version = "2.9.8"
|
||||
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 formation then formation = "line" end
|
||||
if not radius then radius = 30 end -- meters
|
||||
if not innerRadius then innerRadius = 0 end
|
||||
if not liveries then liveries = {} end
|
||||
formation = formation:upper()
|
||||
-- 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
|
||||
@ -1919,14 +1702,9 @@ dcsCommon.version = "2.9.8"
|
||||
local theNewGroup = dcsCommon.createEmptyGroundGroupData(name)
|
||||
|
||||
-- now add a single unit or multiple units
|
||||
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)
|
||||
|
||||
if type(theUnitTypes) ~= "table" then
|
||||
local aUnit = {}
|
||||
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)
|
||||
return theNewGroup
|
||||
end
|
||||
@ -1937,6 +1715,10 @@ dcsCommon.version = "2.9.8"
|
||||
for key, theType in pairs(theUnitTypes) do
|
||||
-- trigger.action.outText("+++dcsC: creating unit " .. name .. "-" .. num .. ": " .. theType, 30)
|
||||
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)
|
||||
num = num + 1
|
||||
end
|
||||
@ -1991,15 +1773,23 @@ dcsCommon.version = "2.9.8"
|
||||
elseif cat == Group.Category.GROUND then
|
||||
-- we got all we need
|
||||
else
|
||||
-- trigger.action.outText("dcsCommon - unknown category: " .. cat, 30)
|
||||
-- return nil
|
||||
-- we also got all we need
|
||||
|
||||
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
|
||||
local c = math.cos(angle)
|
||||
local s = math.sin(angle)
|
||||
@ -2046,7 +1836,6 @@ dcsCommon.version = "2.9.8"
|
||||
if not cx then cx = 0 end
|
||||
if not cz then cz = 0 end
|
||||
local cy = cz
|
||||
--trigger.action.outText("+++dcsC:rotGrp cy,cy = "..cx .. "," .. cy, 30)
|
||||
|
||||
local rads = degrees * 3.14152 / 180
|
||||
do
|
||||
@ -2066,7 +1855,6 @@ dcsCommon.version = "2.9.8"
|
||||
if not cx then cx = 0 end
|
||||
if not cz then cz = 0 end
|
||||
local cy = cz
|
||||
--trigger.action.outText("+++dcsC:rotGrp cy,cy = "..cx .. "," .. cy, 30)
|
||||
|
||||
local rads = degrees * 3.14152 / 180
|
||||
-- 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
|
||||
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
|
||||
theUnit.psi = -theUnit.heading
|
||||
end
|
||||
@ -2247,33 +2032,27 @@ end
|
||||
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, "*")
|
||||
if wildIn then dcsCommon.removeEnding(theString, "*") end
|
||||
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
|
||||
local wildEle = dcsCommon.stringEndsWith(theElement, "*")
|
||||
if wildEle then theElement = dcsCommon.removeEnding(theElement, "*") end
|
||||
--trigger.action.outText("matching s=<" .. theString .. "> with e=<" .. theElement .. ">", 30)
|
||||
|
||||
if wildEle and wildIn then
|
||||
-- both end on wildcards, partial match for both
|
||||
if dcsCommon.stringStartsWith(theElement, theString) 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
|
||||
-- Element is a wildcard, partial match
|
||||
if dcsCommon.stringStartsWith(theString, theElement) then return true end
|
||||
--trigger.action.outText("startswith - match e* <" .. theElement .. "> with s <" .. theString .. "> failed.", 30)
|
||||
|
||||
elseif wildIn then
|
||||
-- theString is a wildcard. partial match
|
||||
if dcsCommon.stringStartsWith(theElement, theString) then return true end
|
||||
--trigger.action.outText("match e with s* failed.", 30)
|
||||
else
|
||||
-- standard: no wildcards, full match
|
||||
if theElement == theString then return true end
|
||||
--trigger.action.outText("match e with s (straight) failed.", 30)
|
||||
end
|
||||
|
||||
end
|
||||
@ -2412,7 +2191,7 @@ end
|
||||
|
||||
if caseInsensitive then
|
||||
theString = string.upper(theString)
|
||||
thePrefix = string.upper(theString)
|
||||
thePrefix = string.upper(thePrefix)
|
||||
end
|
||||
-- superseded: string.find (s, pattern [, init [, plain]]) solves the problem
|
||||
local i, j = string.find(theString, thePrefix, 1, true)
|
||||
@ -2467,12 +2246,19 @@ end
|
||||
return 0
|
||||
end
|
||||
|
||||
function dcsCommon.point2text(p)
|
||||
function dcsCommon.point2text(p, intsOnly)
|
||||
if not intsOnly then intsOnly = false end
|
||||
if not p then return "<!NIL!>" end
|
||||
local t = "[x="
|
||||
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.z then t = t .. "z=" .. p.z .. "]" else t = t .. "z=<nil>]" end
|
||||
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.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
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
@ -2605,7 +2391,6 @@ end
|
||||
if not inrecursion then
|
||||
-- output a marker to find in the log / screen
|
||||
trigger.action.outText("=== dcsCommon vardump end", 30)
|
||||
--env.info("=== dcsCommon vardump end")
|
||||
end
|
||||
end
|
||||
|
||||
@ -2636,7 +2421,9 @@ end
|
||||
"BDA", "AI Abort Mission", "DayNight", "Flight Time", -- 40
|
||||
"Pilot Suicide", "player cap airfield", "emergency landing", "unit create task", -- 44
|
||||
"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"}
|
||||
if id > #events then return "Unknown (ID=" .. id .. ")" end
|
||||
return events[id]
|
||||
@ -2926,6 +2713,21 @@ function dcsCommon.getAllExistingPlayerUnitsRaw()
|
||||
return apu
|
||||
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)
|
||||
if not theUnit then return 0 end
|
||||
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:isExist() then return end
|
||||
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)
|
||||
end
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
delayFlag = {}
|
||||
delayFlag.version = "1.4.0"
|
||||
delayFlag.version = "2.0.0"
|
||||
delayFlag.verbose = false
|
||||
delayFlag.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
@ -11,35 +11,12 @@ delayFlag.flags = {}
|
||||
delay flags - simple flag switch & delay, allows for randomize
|
||||
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
|
||||
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
|
||||
- delayLeft#
|
||||
2.0.0 - clean-up
|
||||
|
||||
--]]--
|
||||
|
||||
@ -317,7 +294,7 @@ function delayFlag.readConfigZone()
|
||||
theZone = cfxZones.createSimpleZone("delayFlagsConfig")
|
||||
end
|
||||
|
||||
delayFlag.verbose = theZone.verbose -- cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
||||
delayFlag.verbose = theZone.verbose
|
||||
|
||||
if delayFlag.verbose then
|
||||
trigger.action.outText("+++dlyF: read config", 30)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
factoryZone = {}
|
||||
factoryZone.version = "2.0.0"
|
||||
factoryZone.version = "3.0.0"
|
||||
factoryZone.verbose = false
|
||||
factoryZone.name = "factoryZone"
|
||||
|
||||
@ -9,6 +9,8 @@ factoryZone.name = "factoryZone"
|
||||
- "production" and "defenders" simplification
|
||||
- now optional specification for red/blue
|
||||
- use maxRadius from zone for spawning to support quad zones
|
||||
3.0.0 - support for liveries via "factoryLiveries" zone
|
||||
- OOP dmlZones
|
||||
|
||||
--]]--
|
||||
factoryZone.requiredLibs = {
|
||||
@ -20,6 +22,7 @@ factoryZone.requiredLibs = {
|
||||
}
|
||||
|
||||
factoryZone.zones = {} -- my factory zones
|
||||
factoryZone.liveries = {} -- indexed by type name
|
||||
factoryZone.ups = 1
|
||||
factoryZone.initialized = false
|
||||
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%
|
||||
|
||||
-- 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 is a module that manages production of units
|
||||
@ -38,24 +41,6 @@ factoryZone.spawnedAttackers = {}
|
||||
--
|
||||
-- *** 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)
|
||||
for zKey, theZone in pairs (factoryZone.zones) do
|
||||
@ -65,59 +50,58 @@ function factoryZone.getFactoryZoneByName(zName)
|
||||
end
|
||||
|
||||
function factoryZone.addFactoryZone(aZone)
|
||||
--aZone.worksFor = cfxZones.getCoalitionFromZoneProperty(aZone, "factory", 0) -- currently unused, have RED/BLUE separate types
|
||||
aZone.state = "init"
|
||||
aZone.timeStamp = timer.getTime()
|
||||
|
||||
-- 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
|
||||
aZone.attackersRED = cfxZones.getStringFromZoneProperty(aZone, "attackersRED", production)
|
||||
aZone.attackersRED = aZone:getStringFromZoneProperty( "attackersRED", production)
|
||||
else
|
||||
aZone.attackersRED = cfxZones.getStringFromZoneProperty(aZone, "productionRED", production)
|
||||
aZone.attackersRED = aZone:getStringFromZoneProperty( "productionRED", production)
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(aZone, "attackersBLUE") then
|
||||
if aZone:hasProperty("attackersBLUE") then
|
||||
-- legacy support
|
||||
aZone.attackersBLUE = cfxZones.getStringFromZoneProperty(aZone, "attackersBLUE", production)
|
||||
aZone.attackersBLUE = aZone:getStringFromZoneProperty( "attackersBLUE", production)
|
||||
else
|
||||
aZone.attackersBLUE = cfxZones.getStringFromZoneProperty(aZone, "productionBLUE", production)
|
||||
aZone.attackersBLUE = aZone:getStringFromZoneProperty( "productionBLUE", production)
|
||||
end
|
||||
|
||||
-- set up defenders default, or use production / factory
|
||||
aZone.defendersRED = cfxZones.getStringFromZoneProperty(aZone, "defendersRED", defenders)
|
||||
aZone.defendersBLUE = cfxZones.getStringFromZoneProperty(aZone, "defendersBLUE", defenders)
|
||||
aZone.defendersRED = aZone:getStringFromZoneProperty("defendersRED", defenders)
|
||||
aZone.defendersBLUE = aZone:getStringFromZoneProperty("defendersBLUE", defenders)
|
||||
|
||||
aZone.formation = cfxZones.getStringFromZoneProperty(aZone, "formation", "circle_out")
|
||||
aZone.attackFormation = cfxZones.getStringFromZoneProperty(aZone, "attackFormation", "circle_out") -- cfxZones.getZoneProperty(aZone, "attackFormation")
|
||||
aZone.spawnRadius = cfxZones.getNumberFromZoneProperty(aZone, "spawnRadius", aZone.maxRadius-5) -- "-5" so they remaininside radius
|
||||
aZone.attackRadius = cfxZones.getNumberFromZoneProperty(aZone, "attackRadius", aZone.maxRadius)
|
||||
aZone.attackDelta = cfxZones.getNumberFromZoneProperty(aZone, "attackDelta", 10) -- aZone.radius)
|
||||
aZone.attackPhi = cfxZones.getNumberFromZoneProperty(aZone, "attackPhi", 0)
|
||||
aZone.formation = aZone:getStringFromZoneProperty("formation", "circle_out")
|
||||
aZone.attackFormation = aZone:getStringFromZoneProperty( "attackFormation", "circle_out") -- cfxZones.getZoneProperty(aZone, "attackFormation")
|
||||
aZone.spawnRadius = aZone:getNumberFromZoneProperty("spawnRadius", aZone.maxRadius-5) -- "-5" so they remaininside radius
|
||||
aZone.attackRadius = aZone:getNumberFromZoneProperty("attackRadius", aZone.maxRadius)
|
||||
aZone.attackDelta = aZone:getNumberFromZoneProperty("attackDelta", 10) -- aZone.radius)
|
||||
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
|
||||
|
||||
-- pause? and activate?
|
||||
if cfxZones.hasProperty(aZone, "pause?") then
|
||||
aZone.pauseFlag = cfxZones.getStringFromZoneProperty(aZone, "pause?", "none")
|
||||
if aZone:hasProperty("pause?") then
|
||||
aZone.pauseFlag = aZone:getStringFromZoneProperty("pause?", "none")
|
||||
aZone.lastPauseValue = trigger.misc.getUserFlag(aZone.pauseFlag)
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(aZone, "activate?") then
|
||||
aZone.activateFlag = cfxZones.getStringFromZoneProperty(aZone, "activate?", "none")
|
||||
if aZone:hasProperty("activate?") then
|
||||
aZone.activateFlag = aZone:getStringFromZoneProperty("activate?", "none")
|
||||
aZone.lastActivateValue = trigger.misc.getUserFlag(aZone.activateFlag)
|
||||
end
|
||||
|
||||
aZone.factoryTriggerMethod = cfxZones.getStringFromZoneProperty(aZone, "triggerMethod", "change")
|
||||
if cfxZones.hasProperty(aZone, "factoryTriggerMethod") then
|
||||
aZone.factoryTriggerMethod = cfxZones.getStringFromZoneProperty(aZone, "factoryTriggerMethod", "change")
|
||||
aZone.factoryTriggerMethod = aZone:getStringFromZoneProperty( "triggerMethod", "change")
|
||||
if aZone:hasProperty("factoryTriggerMethod") then
|
||||
aZone.factoryTriggerMethod = aZone:getStringFromZoneProperty( "factoryTriggerMethod", "change")
|
||||
end
|
||||
|
||||
factoryZone.zones[aZone.name] = aZone
|
||||
@ -159,11 +143,12 @@ function factoryZone.spawnAttackTroops(theTypes, aZone, aCoalition, aFormation)
|
||||
|
||||
local theGroup, theData = cfxZones.createGroundUnitsInZoneForCoalition (
|
||||
aCoalition, -- theCountry,
|
||||
aZone.name .. " (A) " .. dcsCommon.numberUUID(), -- must be unique
|
||||
spawnZone,
|
||||
unitTypes,
|
||||
aZone.name .. " (A) " .. dcsCommon.numberUUID(),
|
||||
spawnZone,
|
||||
unitTypes,
|
||||
aFormation, -- outward facing
|
||||
0)
|
||||
0,
|
||||
factoryZone.liveries)
|
||||
return theGroup, theData
|
||||
end
|
||||
|
||||
@ -184,10 +169,12 @@ function factoryZone.spawnDefensiveTroops(theTypes, aZone, aCoalition, aFormatio
|
||||
local spawnZone = cfxZones.createSimpleZone("spawnZone", aZone.point, aZone.spawnRadius)
|
||||
local theGroup, theData = cfxZones.createGroundUnitsInZoneForCoalition (
|
||||
aCoalition, --theCountry,
|
||||
aZone.name .. " (D) " .. dcsCommon.numberUUID(), -- must be unique
|
||||
spawnZone, unitTypes,
|
||||
aZone.name .. " (D) " .. dcsCommon.numberUUID(),
|
||||
spawnZone,
|
||||
unitTypes,
|
||||
aFormation, -- outward facing
|
||||
0)
|
||||
0,
|
||||
factoryZone.liveries)
|
||||
return theGroup, theData
|
||||
end
|
||||
|
||||
@ -308,7 +295,8 @@ function factoryZone.repairDefenders(aZone)
|
||||
livingTypes,
|
||||
|
||||
aZone.formation, -- outward facing
|
||||
0)
|
||||
0,
|
||||
factoryZone.liveries)
|
||||
aZone.defenders = theGroup
|
||||
aZone.lastDefenders = theGroup:getSize()
|
||||
end
|
||||
@ -686,15 +674,26 @@ end
|
||||
function factoryZone.readConfigZone(theZone)
|
||||
if not theZone then theZone = cfxZones.createSimpleZone("factoryZoneConfig") end
|
||||
factoryZone.name = "factoryZone" -- just in case, so we can access with cfxZones
|
||||
factoryZone.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
||||
factoryZone.defendingTime = cfxZones.getNumberFromZoneProperty(theZone, "defendingTime", 100)
|
||||
factoryZone.attackingTime = cfxZones.getNumberFromZoneProperty(theZone, "attackingTime", 300)
|
||||
factoryZone.shockTime = cfxZones.getNumberFromZoneProperty(theZone, "shockTime", 200)
|
||||
factoryZone.repairTime = cfxZones.getNumberFromZoneProperty(theZone, "repairTime", 200)
|
||||
factoryZone.verbose = theZone.verbose
|
||||
factoryZone.defendingTime = theZone:getNumberFromZoneProperty( "defendingTime", 100)
|
||||
factoryZone.attackingTime = theZone:getNumberFromZoneProperty( "attackingTime", 300)
|
||||
factoryZone.shockTime = theZone:getNumberFromZoneProperty("shockTime", 200)
|
||||
factoryZone.repairTime = theZone:getNumberFromZoneProperty( "repairTime", 200)
|
||||
factoryZone.targetZones = "OWNED"
|
||||
|
||||
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()
|
||||
-- check libs
|
||||
if not dcsCommon.libCheck("cfx Factory Zones",
|
||||
@ -706,12 +705,12 @@ function factoryZone.init()
|
||||
local theZone = cfxZones.getZoneByName("factoryZoneConfig")
|
||||
factoryZone.readConfigZone(theZone)
|
||||
|
||||
-- read livery presets for factory production
|
||||
factoryZone.readLiveries()
|
||||
|
||||
-- collect all zones by their 'factory' property
|
||||
-- start the process
|
||||
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
|
||||
factoryZone.addFactoryZone(aZone)
|
||||
end
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
fireFX = {}
|
||||
fireFX.version = "1.1.0"
|
||||
fireFX.version = "2.0.1"
|
||||
fireFX.verbose = false
|
||||
fireFX.ups = 1
|
||||
fireFX.requiredLibs = {
|
||||
@ -15,7 +15,7 @@ fireFX.fx = {}
|
||||
1.1.1 - agl attribute
|
||||
2.0.0 - dmlZones OOP
|
||||
- rndLoc
|
||||
|
||||
2.0.1 - fixed rndLoc determination
|
||||
--]]--
|
||||
|
||||
function fireFX.addFX(theZone)
|
||||
@ -92,9 +92,9 @@ function fireFX.createFXWithZone(theZone)
|
||||
end
|
||||
|
||||
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
|
||||
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
|
||||
theZone.rndLoc = true
|
||||
end
|
||||
@ -113,7 +113,7 @@ function fireFX.startTheFire(theZone)
|
||||
theZone.fireNames = {}
|
||||
local num = cfxZones.randomInRange(theZone.min, theZone.max)
|
||||
for i = 1, num do
|
||||
local p = cfxZones.getPoint(theZone)
|
||||
local p = theZone:getPoint()
|
||||
if theZone.rndLoc then
|
||||
p = theZone:randomPointInZone()
|
||||
end
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
groupTracker = {}
|
||||
groupTracker.version = "1.2.1"
|
||||
groupTracker.version = "2.0.0"
|
||||
groupTracker.verbose = false
|
||||
groupTracker.ups = 1
|
||||
groupTracker.requiredLibs = {
|
||||
@ -10,28 +10,7 @@ groupTracker.trackers = {}
|
||||
|
||||
--[[--
|
||||
Version History
|
||||
1.0.0 - Initial version
|
||||
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
|
||||
2.0.0 - dmlZones, OOP, clean-up, legacy support
|
||||
|
||||
--]]--
|
||||
|
||||
@ -259,62 +238,7 @@ function groupTracker.removeGroupNamedFromTrackerNamed(gName, trackerName)
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
-- groupTrackedBy - return trackers that track group theGroup
|
||||
@ -365,62 +289,56 @@ function groupTracker.createTrackerWithZone(theZone)
|
||||
theZone.limbo = {} -- name based, for groups that are tracked
|
||||
-- although technically off the map (helo etc)
|
||||
|
||||
theZone.trackerMethod = cfxZones.getStringFromZoneProperty(theZone, "method", "inc")
|
||||
if cfxZones.hasProperty(theZone, "trackerMethod") then
|
||||
theZone.trackerMethod = cfxZones.getStringFromZoneProperty(theZone, "trackerMethod", "inc")
|
||||
theZone.trackerMethod = theZone:getStringFromZoneProperty("method", "inc")
|
||||
if theZone:hasProperty("trackerMethod") then
|
||||
theZone.trackerMethod = theZone:getStringFromZoneProperty( "trackerMethod", "inc")
|
||||
end
|
||||
|
||||
theZone.trackerTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "triggerMethod", "change")
|
||||
theZone.trackerTriggerMethod = theZone:getStringFromZoneProperty("triggerMethod", "change")
|
||||
|
||||
if cfxZones.hasProperty(theZone, "trackerTriggerMethod") then
|
||||
theZone.trackerTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "trackerTriggerMethod", "change")
|
||||
if theZone:hasProperty("trackerTriggerMethod") then
|
||||
theZone.trackerTriggerMethod = theZone:getStringFromZoneProperty("trackerTriggerMethod", "change")
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "numGroups") then
|
||||
theZone.tNumGroups = cfxZones.getStringFromZoneProperty(theZone, "numGroups", "*<none>")
|
||||
-- we may need to zero this flag
|
||||
elseif cfxZones.hasProperty(theZone, "numGroups!") then -- DEPRECATED!
|
||||
theZone.tNumGroups = cfxZones.getStringFromZoneProperty(theZone, "numGroups!", "*<none>")
|
||||
-- we may need to zero this flag
|
||||
if theZone:hasProperty("numGroups") then
|
||||
theZone.tNumGroups = theZone:getStringFromZoneProperty("numGroups", "*<none>") -- legacy support
|
||||
elseif theZone:hasProperty("numGroups#") then
|
||||
theZone.tNumGroups = theZone:getStringFromZoneProperty( "numGroups#", "*<none>")
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "numUnits") then
|
||||
theZone.tNumUnits = cfxZones.getStringFromZoneProperty(theZone, "numUnits", "*<none>")
|
||||
|
||||
if theZone:hasProperty("numUnits") then
|
||||
theZone.tNumUnits = theZOne:getStringFromZoneProperty("numUnits", "*<none>") -- legacy support
|
||||
elseif theZone:hasProperty("numUnits#") then
|
||||
theZone.tNumUnits = theZone:getStringFromZoneProperty("numUnits#", "*<none>")
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "addGroup") then
|
||||
theZone.tAddGroup = cfxZones.getStringFromZoneProperty(theZone, "addGroup", "*<none>")
|
||||
-- we may need to zero this flag
|
||||
elseif cfxZones.hasProperty(theZone, "addGroup!") then -- DEPRECATED
|
||||
theZone.tAddGroup = cfxZones.getStringFromZoneProperty(theZone, "addGroup!", "*<none>")
|
||||
-- we may need to zero this flag
|
||||
if theZone:hasProperty("addGroup") then
|
||||
theZone.tAddGroup = theZone:getStringFromZoneProperty("addGroup", "*<none>") -- legacy support
|
||||
elseif theZone:hasProperty("addGroup!") then
|
||||
theZone.tAddGroup = theZone:getStringFromZoneProperty("addGroup!", "*<none>")
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "removeGroup") then
|
||||
theZone.tRemoveGroup = cfxZones.getStringFromZoneProperty(theZone, "removeGroup", "*<none>")
|
||||
-- we may need to zero this flag
|
||||
elseif cfxZones.hasProperty(theZone, "removeGroup!") then -- DEPRECATED!
|
||||
theZone.tRemoveGroup = cfxZones.getStringFromZoneProperty(theZone, "removeGroup!", "*<none>")
|
||||
-- we may need to zero this flag
|
||||
if theZone:hasProperty("removeGroup") then
|
||||
theZone.tRemoveGroup = theZone:getStringFromZoneProperty( "removeGroup", "*<none>") -- legacy support
|
||||
elseif theZone:hasProperty("removeGroup!") then
|
||||
theZone.tRemoveGroup = theZone:getStringFromZoneProperty( "removeGroup!", "*<none>")
|
||||
end
|
||||
|
||||
|
||||
|
||||
if cfxZones.hasProperty(theZone, "groupFilter") then
|
||||
local filterString = cfxZones.getStringFromZoneProperty(theZone, "groupFilter", "2") -- ground
|
||||
|
||||
if theZone:hasProperty("groupFilter") then
|
||||
local filterString = theZone:getStringFromZoneProperty( "groupFilter", "2") -- ground
|
||||
theZone.groupFilter = dcsCommon.string2GroupCat(filterString)
|
||||
if groupTracker.verbose or theZone.verbose then
|
||||
trigger.action.outText("+++gTrck: filtering " .. theZone.groupFilter .. " in " .. theZone.name, 30)
|
||||
end
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "destroy?") then
|
||||
theZone.destroyFlag = cfxZones.getStringFromZoneProperty(theZone, "destroy?", "*<none>")
|
||||
if theZone:hasProperty("destroy?") then
|
||||
theZone.destroyFlag = theZone:getStringFromZoneProperty(theZone, "destroy?", "*<none>")
|
||||
theZone.lastDestroyValue = cfxZones.getFlagValue(theZone.destroyFlag, theZone)
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "allGone!") then
|
||||
theZone.allGoneFlag = cfxZones.getStringFromZoneProperty(theZone, "allGone!", "<None>") -- note string on number default
|
||||
if theZone:hasProperty("allGone!") then
|
||||
theZone.allGoneFlag = theZone:getStringFromZoneProperty("allGone!", "<None>") -- note string on number default
|
||||
end
|
||||
theZone.lastGroupCount = 0
|
||||
|
||||
@ -652,15 +570,12 @@ end
|
||||
function groupTracker.readConfigZone()
|
||||
local theZone = cfxZones.getZoneByName("groupTrackerConfig")
|
||||
if not theZone then
|
||||
if groupTracker.verbose then
|
||||
trigger.action.outText("+++gTrk: NO config zone!", 30)
|
||||
end
|
||||
return
|
||||
theZone = cfxZones.createSimpleZone("groupTrackerConfig")
|
||||
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
|
||||
trigger.action.outText("+++gTrk: read config", 30)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
messenger = {}
|
||||
messenger.version = "2.3.1"
|
||||
messenger.version = "3.0.0"
|
||||
messenger.verbose = false
|
||||
messenger.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
@ -8,68 +8,9 @@ messenger.requiredLibs = {
|
||||
messenger.messengers = {}
|
||||
--[[--
|
||||
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.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)
|
||||
@ -255,11 +196,11 @@ function messenger.createMessengerWithZone(theZone)
|
||||
if theZone:hasProperty("in?") then
|
||||
theZone.triggerMessagerFlag = theZone:getStringFromZoneProperty("in?", "none")
|
||||
end
|
||||
|
||||
--[[--
|
||||
if theZone:hasProperty("messageOut?") then
|
||||
theZone.triggerMessagerFlag = theZone:getStringFromZoneProperty("messageOut?", "none")
|
||||
end
|
||||
|
||||
--]]--
|
||||
-- try default only if no other is set
|
||||
if not theZone.triggerMessagerFlag then
|
||||
if not theZone:hasProperty("messenger?") then
|
||||
@ -505,12 +446,13 @@ function messenger.start()
|
||||
|
||||
-- process messenger Zones
|
||||
-- old style
|
||||
--[[--
|
||||
local attrZones = cfxZones.getZonesWithAttributeNamed("messenger")
|
||||
for k, aZone in pairs(attrZones) do
|
||||
messenger.createMessengerWithZone(aZone) -- process attributes
|
||||
messenger.addMessenger(aZone) -- add to list
|
||||
end
|
||||
|
||||
--]]--
|
||||
-- new style that saves messageOut? flag by reading flags
|
||||
attrZones = cfxZones.getZonesWithAttributeNamed("messenger?")
|
||||
for k, aZone in pairs(attrZones) do
|
||||
|
||||
@ -17,6 +17,7 @@ cfxObjectDestructDetector.requiredLibs = {
|
||||
re-wrote object determination to not be affected by
|
||||
ID changes (happens with map updates)
|
||||
fail addZone when name property is missing
|
||||
2.0.1 check that the object is within the zone onEvent
|
||||
--]]--
|
||||
|
||||
cfxObjectDestructDetector.objectZones = {}
|
||||
@ -86,6 +87,8 @@ function cfxObjectDestructDetector:onEvent(event)
|
||||
if event.id == world.event.S_EVENT_DEAD then
|
||||
if not event.initiator then return end
|
||||
local theObject = event.initiator
|
||||
-- check location
|
||||
local pos = theObject:getPoint()
|
||||
local desc = theObject:getDesc()
|
||||
if not desc then return end
|
||||
local matchMe = desc.typeName -- we home in on object's typeName
|
||||
@ -93,7 +96,10 @@ function cfxObjectDestructDetector:onEvent(event)
|
||||
matchMe = string.upper(matchMe)
|
||||
|
||||
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
|
||||
aZone:pollFlag(aZone.outDestroyFlag, aZone.oddMethod)
|
||||
end
|
||||
@ -13,25 +13,6 @@ cfxObjectSpawnZones.verbose = false
|
||||
*** DOES NOT EXTEND ZONES ***
|
||||
|
||||
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
|
||||
|
||||
--]]--
|
||||
@ -1,5 +1,5 @@
|
||||
persistence = {}
|
||||
persistence.version = "1.0.7"
|
||||
persistence.version = "2.0.0"
|
||||
persistence.ups = 1 -- once every 1 seconds
|
||||
persistence.verbose = false
|
||||
persistence.active = false
|
||||
@ -10,39 +10,25 @@ persistence.saveDir = nil -- set at start
|
||||
persistence.name = "persistence" -- for cfxZones
|
||||
persistence.missionData = {} -- loaded from file
|
||||
persistence.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxZones", -- Zones, of course
|
||||
"dcsCommon",
|
||||
"cfxZones",
|
||||
}
|
||||
--[[--
|
||||
Version History
|
||||
1.0.0 - initial version
|
||||
1.0.1 - when available, module sets flag "cfxPersistence" to 1
|
||||
- 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
|
||||
|
||||
2.0.0 - dml zones, OOP
|
||||
cleanup
|
||||
|
||||
PROVIDES LOAD/SAVE ABILITY TO MODULES
|
||||
PROVIDES STANDALONE/HOSTED SERVER COMPATIBILITY
|
||||
|
||||
--]]--
|
||||
|
||||
-- in order to work, Host must desanitize lfs and io
|
||||
-- only works when run as server
|
||||
-- in order to work, HOST MUST DESANITIZE lfs and io
|
||||
|
||||
--
|
||||
-- flags to save. can be added to by saveFlags attribute
|
||||
--
|
||||
persistence.flagsToSave = {} -- simple table
|
||||
persistence.callbacks = {} -- cbblocks, dictionary
|
||||
persistence.callbacks = {} -- cbblocks, dictionary by name
|
||||
|
||||
|
||||
--
|
||||
@ -417,13 +403,13 @@ end
|
||||
--
|
||||
|
||||
function persistence.collectFlagsFromZone(theZone)
|
||||
local theFlags = cfxZones.getStringFromZoneProperty(theZone, "saveFlags", "*dummy")
|
||||
local theFlags = theZone:getStringFromZoneProperty("saveFlags", "*dummy")
|
||||
persistence.registerFlagsToSave(theFlags, theZone)
|
||||
end
|
||||
|
||||
function persistence.readConfigZone()
|
||||
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
|
||||
end
|
||||
|
||||
@ -436,10 +422,10 @@ function persistence.readConfigZone()
|
||||
|
||||
-- serverDir is the path from the server save directory, usually "Missions/".
|
||||
-- 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
|
||||
-- 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
|
||||
persistence.root = persistence.root .. "\\"
|
||||
end
|
||||
@ -453,11 +439,11 @@ function persistence.readConfigZone()
|
||||
end
|
||||
end
|
||||
|
||||
persistence.serverDir = cfxZones.getStringFromZoneProperty(theZone, "serverDir", "Missions\\")
|
||||
persistence.serverDir = theZone:getStringFromZoneProperty("serverDir", "Missions\\")
|
||||
|
||||
if hasConfig then
|
||||
if cfxZones.hasProperty(theZone, "saveDir") then
|
||||
persistence.saveDir = cfxZones.getStringFromZoneProperty(theZone, "saveDir", "")
|
||||
if theZone:hasProperty("saveDir") then
|
||||
persistence.saveDir = theZone:getStringFromZoneProperty("saveDir", "")
|
||||
else
|
||||
-- local missname = net.dostring_in("gui", "return DCS.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)
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "saveFileName") then
|
||||
persistence.saveFileName = cfxZones.getStringFromZoneProperty(theZone, "saveFileName", dcsCommon.getMissionName() .. " Data.txt")
|
||||
if theZone:hasProperty("saveFileName") then
|
||||
persistence.saveFileName = theZone:getStringFromZoneProperty("saveFileName", dcsCommon.getMissionName() .. " Data.txt")
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "versionID") then
|
||||
persistence.versionID = cfxZones.getStringFromZoneProperty(theZone, "versionID", "") -- to check for full restart
|
||||
if theZone:hasProperty("versionID") then
|
||||
persistence.versionID = theZone:getStringFromZoneProperty("versionID", "") -- to check for full restart
|
||||
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
|
||||
persistence.saveTime = persistence.saveInterval * 60 + timer.getTime()
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "cleanRestart?") then
|
||||
persistence.cleanRestart = cfxZones.getStringFromZoneProperty(theZone, "cleanRestart?", "*<none>")
|
||||
persistence.lastCleanRestart = cfxZones.getFlagValue(persistence.cleanRestart, theZone)
|
||||
if theZone:hasProperty("cleanRestart?") then
|
||||
persistence.cleanRestart = theZone:getStringFromZoneProperty("cleanRestart?", "*<none>")
|
||||
persistence.lastCleanRestart = theZone:getFlagValue(persistence.cleanRestart)
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "saveMission?") then
|
||||
persistence.saveMission = cfxZones.getStringFromZoneProperty(theZone, "saveMission?", "*<none>")
|
||||
persistence.lastSaveMission = cfxZones.getFlagValue(persistence.saveMission, theZone)
|
||||
if theZone:hasProperty("saveMission?") then
|
||||
persistence.saveMission = theZone:getStringFromZoneProperty("saveMission?", "*<none>")
|
||||
persistence.lastSaveMission = theZone:getFlagValue(persistence.saveMission)
|
||||
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
|
||||
trigger.action.outText("+++persistence: read config", 30)
|
||||
@ -534,14 +520,13 @@ function persistence.start()
|
||||
return false
|
||||
end
|
||||
|
||||
-- local mainDir = lfs.writedir() .. persistence.serverDir
|
||||
local mainDir = persistence.root .. persistence.serverDir
|
||||
if not dcsCommon.stringEndsWith(mainDir, "\\") then
|
||||
mainDir = mainDir .. "\\"
|
||||
end
|
||||
|
||||
-- lets see if we can access the server's mission directory and
|
||||
-- save directory
|
||||
|
||||
-- save directory
|
||||
if persistence.isDir(mainDir) then
|
||||
if persistence.verbose then
|
||||
trigger.action.outText("persistence: main dir is <" .. mainDir .. ">", 30)
|
||||
@ -613,7 +598,6 @@ function persistence.start()
|
||||
-- and start updating
|
||||
persistence.update()
|
||||
|
||||
|
||||
return persistence.active
|
||||
end
|
||||
|
||||
@ -627,5 +611,3 @@ if not persistence.start() then
|
||||
end
|
||||
-- we do NOT remove the methods so we don't crash
|
||||
end
|
||||
|
||||
-- add zones for saveFlags so authors can easily save flag values
|
||||
|
||||
@ -1,91 +1,17 @@
|
||||
cfxPlayerScore = {}
|
||||
cfxPlayerScore.version = "3.0.0"
|
||||
cfxPlayerScore.version = "3.0.1"
|
||||
cfxPlayerScore.name = "cfxPlayerScore" -- compatibility with flag bangers
|
||||
cfxPlayerScore.badSound = "Death BRASS.wav"
|
||||
cfxPlayerScore.scoreSound = "Quest Snare 3.wav"
|
||||
cfxPlayerScore.announcer = true
|
||||
cfxPlayerScore.firstSave = true -- to force overwrite
|
||||
--[[-- 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
|
||||
- redScore#
|
||||
- blueScore#
|
||||
- sceneryObject detection improvements
|
||||
- DCS 2.9 safe
|
||||
3.0.1 - cleanup
|
||||
|
||||
--]]--
|
||||
|
||||
@ -109,12 +35,6 @@ cfxPlayerScore.killZones = {} -- when set, kills only count here
|
||||
cfxPlayerScore.typeScore = {}
|
||||
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.
|
||||
--
|
||||
-- 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.helo = 40
|
||||
cfxPlayerScore.ground = 10
|
||||
@ -655,7 +575,6 @@ function cfxPlayerScore.linkUnitWithPlayer(theUnit)
|
||||
local uName = theUnit:getName()
|
||||
local pName = theUnit:getPlayerName()
|
||||
cfxPlayerScore.unit2player[uName] = pName
|
||||
--cfxPlayerScore.player2unit[pName] = uName -- is this needed?
|
||||
end
|
||||
|
||||
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)
|
||||
|
||||
if (not fratricide) and #killFeats > 0 then
|
||||
|
||||
-- use the feat description
|
||||
-- we may want to use closest, currently simply the first
|
||||
theFeatZone = killFeats[1]
|
||||
@ -826,7 +744,6 @@ function cfxPlayerScore.killDetected(theEvent)
|
||||
if wasBuilding then
|
||||
-- these objects have no coalition; we simply award the score if
|
||||
-- it exists in look-up table.
|
||||
--trigger.action.outText("KILL SCENERY", 30)
|
||||
local staticScore = cfxPlayerScore.object2score(victim)
|
||||
if staticScore > 0 then
|
||||
trigger.action.outSoundForCoalition(killSide, cfxPlayerScore.scoreSound)
|
||||
@ -847,10 +764,9 @@ function cfxPlayerScore.killDetected(theEvent)
|
||||
local isStO = cfxPlayerScore.isStaticObject(victim)
|
||||
--if not victim.getGroup then
|
||||
if isStO then
|
||||
-- static objects have no group
|
||||
|
||||
-- static objects have no group
|
||||
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)
|
||||
|
||||
if staticScore > 0 then
|
||||
@ -870,9 +786,7 @@ function cfxPlayerScore.killDetected(theEvent)
|
||||
|
||||
if not fraternicide then
|
||||
cfxPlayerScore.checkKillFeat(killerName, killer, victim, false)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
@ -881,7 +795,7 @@ function cfxPlayerScore.killDetected(theEvent)
|
||||
trigger.action.outText("+++scr: strange stuff:group, outta here", 30)
|
||||
return
|
||||
end
|
||||
local vicCat = vicGroup:getCategory()
|
||||
local vicCat = vicGroup:getCategory() -- group cat is DCS 2.9 safe
|
||||
if not vicCat then
|
||||
trigger.action.outText("+++scr: strange stuff:cat, outta here", 30)
|
||||
return
|
||||
@ -889,25 +803,24 @@ function cfxPlayerScore.killDetected(theEvent)
|
||||
local unitScore = cfxPlayerScore.unit2score(victim)
|
||||
|
||||
-- see which weapon was used. gun kills score 2x
|
||||
local killMeth = ""
|
||||
local killWeap = theEvent.weapon
|
||||
-- local killMeth = "" -- meth is currently not defined
|
||||
-- local killWeap = theEvent.weapon -- not supported either
|
||||
|
||||
if pk then
|
||||
if pk then -- player kill - add player's name
|
||||
vicDesc = victim:getPlayerName() .. " in " .. vicDesc
|
||||
scoreMod = scoreMod * cfxPlayerScore.pkMod
|
||||
end
|
||||
|
||||
|
||||
-- if fratricide, times ffMod (friedlyFire)
|
||||
if fraternicide then
|
||||
scoreMod = scoreMod * cfxPlayerScore.ffMod ---2
|
||||
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)
|
||||
end
|
||||
else
|
||||
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(killSide, cfxPlayerScore.scoreSound)
|
||||
end
|
||||
@ -960,7 +873,6 @@ function cfxPlayerScore.handlePlayerLanding(theEvent)
|
||||
-- we may want to use closest, currently simply the first
|
||||
theFeatZone = landingFeats[1]
|
||||
desc = cfxPlayerScore.evalFeatDescription(playerName, theFeatZone, thePlayerUnit) -- nil victim, defaults to player
|
||||
|
||||
else
|
||||
if theEvent.place then
|
||||
desc = desc .. " successfully (" .. theEvent.place:getName() .. ")"
|
||||
@ -1067,8 +979,7 @@ function cfxPlayerScore.scheduledAward(args)
|
||||
trigger.action.outTextForUnit(uid, "Can't award score for <" .. playerName .. ">, not in safe zone.", 30)
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
|
||||
local theScore = cfxPlayerScore.getPlayerScore(playerName)
|
||||
local playerSide = dcsCommon.playerName2Coalition(playerName)
|
||||
if playerSide < 1 then
|
||||
@ -1214,59 +1125,58 @@ function cfxPlayerScore.handlePlayerEvent(theEvent)
|
||||
end
|
||||
|
||||
function cfxPlayerScore.readConfigZone(theZone)
|
||||
cfxPlayerScore.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
||||
cfxPlayerScore.verbose = theZone.verbose
|
||||
-- default scores
|
||||
cfxPlayerScore.aircraft = cfxZones.getNumberFromZoneProperty(theZone, "aircraft", 50)
|
||||
cfxPlayerScore.helo = cfxZones.getNumberFromZoneProperty(theZone, "helo", 40)
|
||||
cfxPlayerScore.ground = cfxZones.getNumberFromZoneProperty(theZone, "ground", 10)
|
||||
cfxPlayerScore.ship = cfxZones.getNumberFromZoneProperty(theZone, "ship", 80)
|
||||
cfxPlayerScore.train = cfxZones.getNumberFromZoneProperty(theZone, "train", 5)
|
||||
cfxPlayerScore.landing = cfxZones.getNumberFromZoneProperty(theZone, "landing", 0) -- if > 0 then feat
|
||||
cfxPlayerScore.aircraft = theZone:getNumberFromZoneProperty("aircraft", 50)
|
||||
cfxPlayerScore.helo = theZone:getNumberFromZoneProperty("helo", 40)
|
||||
cfxPlayerScore.ground = theZone:getNumberFromZoneProperty("ground", 10)
|
||||
cfxPlayerScore.ship = theZone:getNumberFromZoneProperty("ship", 80)
|
||||
cfxPlayerScore.train = theZone:getNumberFromZoneProperty( "train", 5)
|
||||
cfxPlayerScore.landing = theZone:getNumberFromZoneProperty("landing", 0) -- if > 0 then feat
|
||||
|
||||
cfxPlayerScore.pkMod = cfxZones.getNumberFromZoneProperty(theZone, "pkMod", 1) -- factor for killing a player
|
||||
cfxPlayerScore.ffMod = cfxZones.getNumberFromZoneProperty(theZone, "ffMod", -2) -- factor for friendly fire
|
||||
cfxPlayerScore.planeLoss = cfxZones.getNumberFromZoneProperty(theZone, "planeLoss", -10) -- points added when player's plane crashes
|
||||
cfxPlayerScore.pkMod = theZone:getNumberFromZoneProperty( "pkMod", 1) -- factor for killing a player
|
||||
cfxPlayerScore.ffMod = theZone:getNumberFromZoneProperty( "ffMod", -2) -- factor for friendly fire
|
||||
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
|
||||
cfxPlayerScore.badSound = cfxZones.getStringFromZoneProperty(theZone, "badSound", "<nosound>")
|
||||
if theZone:hasProperty("badSound") then
|
||||
cfxPlayerScore.badSound = theZone:getStringFromZoneProperty("badSound", "<nosound>")
|
||||
end
|
||||
if cfxZones.hasProperty(theZone, "scoreSound") then
|
||||
cfxPlayerScore.scoreSound = cfxZones.getStringFromZoneProperty(theZone, "scoreSound", "<nosound>")
|
||||
if theZone:hasProperty("scoreSound") then
|
||||
cfxPlayerScore.scoreSound = theZone:getStringFromZoneProperty("scoreSound", "<nosound>")
|
||||
end
|
||||
|
||||
-- triggering saving scores
|
||||
if cfxZones.hasProperty(theZone, "saveScore?") then
|
||||
cfxPlayerScore.saveScore = cfxZones.getStringFromZoneProperty(theZone, "saveScore?", "none")
|
||||
if theZone:hasProperty("saveScore?") then
|
||||
cfxPlayerScore.saveScore = theZone:getStringFromZoneProperty("saveScore?", "none")
|
||||
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
|
||||
|
||||
-- triggering show all scores
|
||||
if cfxZones.hasProperty(theZone, "showScore?") then
|
||||
cfxPlayerScore.showScore = cfxZones.getStringFromZoneProperty(theZone, "showScore?", "none")
|
||||
if theZone:hasProperty("showScore?") then
|
||||
cfxPlayerScore.showScore = theZone:getStringFromZoneProperty("showScore?", "none")
|
||||
cfxPlayerScore.lastShowScore = trigger.misc.getUserFlag(cfxPlayerScore.showScore)
|
||||
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(
|
||||
theZone, "reportCoalition", false) -- also show coalition score
|
||||
cfxPlayerScore.reportCoalition = theZone:getBoolFromZoneProperty("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
|
||||
cfxPlayerScore.redScoreOut = theZone:getStringFromZoneProperty("redScore#")
|
||||
@ -10,6 +10,7 @@ cfxPlayerScoreUI.verbose = false
|
||||
- 2.1.0 - soundfile cleanup
|
||||
- score summary for side
|
||||
- allowAll
|
||||
- 2.1.1 - minor cleanup
|
||||
|
||||
--]]--
|
||||
cfxPlayerScoreUI.requiredLibs = {
|
||||
@ -116,7 +117,7 @@ function cfxPlayerScoreUI.start()
|
||||
world.addEventHandler(cfxPlayerScoreUI)
|
||||
-- process all existing players (late start)
|
||||
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
|
||||
end
|
||||
|
||||
@ -1,40 +1,39 @@
|
||||
playerZone = {}
|
||||
playerZone.version = "1.0.0"
|
||||
playerZone.version = "2.0.0"
|
||||
playerZone.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxZones", -- Zones, of course
|
||||
"dcsCommon",
|
||||
"cfxZones",
|
||||
}
|
||||
playerZone.playerZones = {}
|
||||
--[[--
|
||||
Version History
|
||||
1.0.0 - Initial version
|
||||
1.0.1 - pNum --> pNum#
|
||||
2.0.0 - dmlZones
|
||||
red#, blue#, total#
|
||||
|
||||
--]]--
|
||||
|
||||
function playerZone.createPlayerZone(theZone)
|
||||
-- start val - a range
|
||||
theZone.pzCoalition = cfxZones.getCoalitionFromZoneProperty(theZone, "playerZone", 0)
|
||||
|
||||
|
||||
theZone.pzCoalition = theZone:getCoalitionFromZoneProperty( "playerZone", 0)
|
||||
-- Method for outputs
|
||||
theZone.pzMethod = cfxZones.getStringFromZoneProperty(theZone, "method", "inc")
|
||||
if cfxZones.hasProperty(theZone, "pzMethod") then
|
||||
theZone.pzMethod = cfxZones.getStringFromZoneProperty(theZone, "pwMethod", "inc")
|
||||
|
||||
theZone.pzMethod = theZone:getStringFromZoneProperty("method", "inc")
|
||||
if theZone:hasProperty("pzMethod") then
|
||||
theZone.pzMethod = theZone:getStringFromZoneProperty("pwMethod", "inc")
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "pNum#") then
|
||||
theZone.pNum = cfxZones.getStringFromZoneProperty(theZone, "pNum#", "none")
|
||||
elseif cfxZones.hasProperty(theZone, "pNum") then
|
||||
theZone.pNum = cfxZones.getStringFromZoneProperty(theZone, "pNum", "none")
|
||||
if theZone:hasProperty("pNum#") then
|
||||
theZone.pNum = theZone:getStringFromZoneProperty("pNum#", "none")
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "added!") then
|
||||
theZone.pAdd = cfxZones.getStringFromZoneProperty(theZone, "added!", "none")
|
||||
if theZone:hasProperty("added!") then
|
||||
theZone.pAdd = theZone:getStringFromZoneProperty("added!", "none")
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "gone!") then
|
||||
theZone.pRemove = cfxZones.getStringFromZoneProperty(theZone, "gone!", "none")
|
||||
if theZone:hasProperty("gone!") then
|
||||
theZone.pRemove = theZone:getStringFromZoneProperty("gone!", "none")
|
||||
end
|
||||
|
||||
theZone.playersInZone = {} -- indexed by unit name
|
||||
@ -49,7 +48,7 @@ function playerZone.collectPlayersForZone(theZone)
|
||||
local allPlayers = coalition.getPlayers(f)
|
||||
for idy, theUnit in pairs (allPlayers) do
|
||||
local loc = theUnit:getPoint()
|
||||
if cfxZones.pointInZone(loc, theZone) then
|
||||
if theZone:pointInZone(loc) then
|
||||
zonePlayers[theUnit:getName()] = theUnit
|
||||
end
|
||||
end
|
||||
@ -86,21 +85,21 @@ function playerZone.processZone(theZone)
|
||||
|
||||
-- flag handling and banging
|
||||
if theZone.pNum then
|
||||
cfxZones.setFlagValueMult(theZone.pNum, newCount, theZone)
|
||||
theZone:setFlagValue(theZone.pNum, newCount)
|
||||
end
|
||||
|
||||
if theZone.pAdd and hasNew then
|
||||
if theZone.verbose or playerZone.verbose then
|
||||
trigger.action.outText("+++pZone: banging <" .. theZone.name .. ">'s 'added!' flags <" .. theZone.pAdd .. ">", 30)
|
||||
end
|
||||
cfxZones.pollFlag(theZone.pAdd, theZone.pzMethod, theZone)
|
||||
theZone:pollFlag(theZone.pAdd, theZone.pzMethod)
|
||||
end
|
||||
|
||||
if theZone.pRemove and hasGone then
|
||||
if theZone.verbose or playerZone.verbose then
|
||||
trigger.action.outText("+++pZone: banging <" .. theZone.name .. ">'s 'gone' flags <" .. theZone.pRemove .. ">", 30)
|
||||
end
|
||||
cfxZones.pollFlag(theZone.pAdd, theZone.pzMethod, theZone)
|
||||
theZone:pollFlag(theZone.pAdd, theZone.pzMethod)
|
||||
end
|
||||
end
|
||||
|
||||
@ -113,6 +112,25 @@ function playerZone.update()
|
||||
|
||||
-- iterate all zones and check them
|
||||
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)
|
||||
end
|
||||
end
|
||||
@ -121,7 +139,20 @@ end
|
||||
-- Read Config Zone
|
||||
--
|
||||
function playerZone.readConfigZone(theZone)
|
||||
playerZone.name = "playerZoneConfig" -- cfxZones compat
|
||||
-- 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
|
||||
|
||||
--
|
||||
|
||||
@ -11,34 +11,6 @@ pulseFlags.requiredLibs = {
|
||||
Copyright 2022 by Christian Franz and cf/x
|
||||
|
||||
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
|
||||
using method on all outputs
|
||||
- 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
|
||||
if theZone.hasPulsed or theZone.zeroPulse 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
|
||||
|
||||
theZone:pollFlag(theZone.pulseFlag, theZone.pulseMethod)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
radioMenu = {}
|
||||
radioMenu.version = "2.1.1"
|
||||
radioMenu.version = "2.2.0"
|
||||
radioMenu.verbose = false
|
||||
radioMenu.ups = 1
|
||||
radioMenu.requiredLibs = {
|
||||
@ -10,21 +10,6 @@ radioMenu.menus = {}
|
||||
|
||||
--[[--
|
||||
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
|
||||
OOP cfxZones
|
||||
corrected CD setting for "D"
|
||||
@ -32,6 +17,7 @@ radioMenu.menus = {}
|
||||
valA-D now define full method, not just values
|
||||
full wildcard support for ack and cooldown
|
||||
2.1.1 - outMessage now works correctly
|
||||
2.2.0 - clean-up
|
||||
--]]--
|
||||
|
||||
function radioMenu.addRadioMenu(theZone)
|
||||
@ -63,8 +49,7 @@ function radioMenu.filterPlayerIDForType(theZone)
|
||||
end
|
||||
|
||||
-- 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
|
||||
-- array. Since we later iterate ID by idx, that's not an issue
|
||||
-- note that players may match twice, so we use a dict
|
||||
|
||||
for idx, aType in pairs(allTypes) do
|
||||
local theType = dcsCommon.trim(aType)
|
||||
@ -145,7 +130,6 @@ function radioMenu.filterPlayerIDForGroup(theZone)
|
||||
end
|
||||
|
||||
function radioMenu.installMenu(theZone)
|
||||
-- local theGroup = 0 -- was: nil
|
||||
local gID = nil
|
||||
if theZone.menuGroup then
|
||||
if not cfxMX then
|
||||
@ -570,6 +554,5 @@ if not radioMenu.start() then
|
||||
end
|
||||
|
||||
--[[--
|
||||
callbacks for the menus
|
||||
check CD/standby code for multiple groups
|
||||
--]]--
|
||||
@ -1,25 +1,12 @@
|
||||
cfxSmokeZone = {}
|
||||
cfxSmokeZone.version = "1.2.0"
|
||||
cfxSmokeZone.version = "2.0.0"
|
||||
cfxSmokeZone.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxZones", -- Zones, of course
|
||||
}
|
||||
--[[--
|
||||
Version History
|
||||
1.0.0 - initial version
|
||||
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.
|
||||
2.0.0 - clean up
|
||||
|
||||
--]]--
|
||||
cfxSmokeZone.smokeZones = {}
|
||||
@ -48,7 +35,6 @@ function cfxSmokeZone.processSmokeZone(aZone)
|
||||
-- paused
|
||||
aZone.paused = aZone:getBoolFromZoneProperty("paused", false)
|
||||
|
||||
-- f? query flags
|
||||
if aZone:hasProperty("f?") then
|
||||
aZone.onFlag = aZone:getStringFromZoneProperty("f?", "*<none>")
|
||||
elseif aZone:hasProperty("startSmoke?") then
|
||||
@ -64,8 +50,6 @@ function cfxSmokeZone.processSmokeZone(aZone)
|
||||
aZone.smkLastStopFlag = aZone:getFlagValue(aZone.smkStopFlag)
|
||||
end
|
||||
|
||||
-- watchflags:
|
||||
-- triggerMethod
|
||||
aZone.smokeTriggerMethod = aZone:getStringFromZoneProperty( "triggerMethod", "change")
|
||||
|
||||
if aZone:hasProperty("smokeTriggerMethod") then
|
||||
@ -165,14 +149,10 @@ function cfxSmokeZone.start()
|
||||
end
|
||||
|
||||
-- collect all zones with 'smoke' attribute
|
||||
-- collect all spawn zones
|
||||
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
|
||||
cfxSmokeZone.processSmokeZone(aZone) -- process attribute and add to zone
|
||||
cfxSmokeZone.addSmokeZone(aZone) -- remember it so we can smoke it
|
||||
for k, aZone in pairs(attrZones) do
|
||||
cfxSmokeZone.processSmokeZone(aZone)
|
||||
cfxSmokeZone.addSmokeZone(aZone)
|
||||
end
|
||||
|
||||
-- start update loop
|
||||
@ -21,53 +21,7 @@ cfxSpawnZones.spawnedGroups = {}
|
||||
-- *** DOES NOT EXTEND ZONES *** LINKED OWNER via masterOwner ***
|
||||
--
|
||||
--[[--
|
||||
-- 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
|
||||
-- version history
|
||||
2.0.0 - dmlZones
|
||||
- moved "types" to spawner
|
||||
- baseName defaults to zone name, as it is safe for naming
|
||||
@ -324,6 +324,9 @@ function stopGap.update()
|
||||
busy = true
|
||||
-- count up for auto-release after n seconds
|
||||
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
|
||||
|
||||
if busy then
|
||||
@ -331,6 +334,9 @@ function stopGap.update()
|
||||
else
|
||||
local theStaticGroup = stopGap.createStandInsForMXGroup(theGroup)
|
||||
stopGap.standInGroups[name] = theStaticGroup
|
||||
if stopGap.verbose then
|
||||
trigger.action.outText("+++StopG: [server command] placing static stand-in for group <" .. name .. ">.", 30)
|
||||
end
|
||||
end
|
||||
else
|
||||
-- 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.stopGapZones = {} -- DML only
|
||||
|
||||
|
||||
@ -12,24 +12,27 @@ debugger.log = ""
|
||||
|
||||
--[[--
|
||||
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
|
||||
- eventmon command
|
||||
- all, off, event #
|
||||
- eventmon all, off, event #
|
||||
- standard events
|
||||
- adding events
|
||||
- adding events via #
|
||||
- events? attribute from any zone
|
||||
|
||||
- eventmon last command
|
||||
- q - query MSE Lua variables
|
||||
- w - write/overwrite MSE Lua variables
|
||||
- a - analyse Lua tables / variables
|
||||
- smoke
|
||||
- spawn system with predefines
|
||||
- spawn coalition
|
||||
- spawn number
|
||||
- spawn heading
|
||||
- spawn types
|
||||
- spawn aircraft: add waypoints
|
||||
- spawn "?"
|
||||
- debuggerSpawnTypes zone
|
||||
- reading debuggerSpawnTypes
|
||||
- removed some silly bugs / inconsistencies
|
||||
--]]--
|
||||
|
||||
debugger.requiredLibs = {
|
||||
@ -45,6 +48,7 @@ debugger.debugUnits = {}
|
||||
debugger.debugGroups = {}
|
||||
debugger.debugObjects = {}
|
||||
debugger.showEvents = {}
|
||||
debugger.lastEvent = nil
|
||||
|
||||
debugDemon.eventList = {
|
||||
["0"] = "S_EVENT_INVALID = 0",
|
||||
@ -106,6 +110,26 @@ debugDemon.eventList = {
|
||||
["56"] = "S_EVENT_POSTPONED_LAND = 56",
|
||||
["57"] = "S_EVENT_MAX = 57",
|
||||
}
|
||||
|
||||
debugger.spawnTypes = {
|
||||
["inf"] = "Soldier M4",
|
||||
["ifv"] = "BTR-80",
|
||||
["tank"] = "T-90",
|
||||
["ship"] = "PERRY",
|
||||
["helo"] = "AH-1W",
|
||||
["jet"] = "MiG-21Bis",
|
||||
["awacs"] = "A-50",
|
||||
["ww2"] = "SpitfireLFMkIX",
|
||||
["bomber"] = "B-52H",
|
||||
["cargo"] = "ammo_cargo",
|
||||
["sam"] = "Roland ADS",
|
||||
["aaa"] = "ZSU-23-4 Shilka",
|
||||
["arty"] = "M-109",
|
||||
["truck"] = "KAMAZ Truck",
|
||||
["drone"] = "MQ-9 Reaper",
|
||||
["manpad"] = "Soldier stinger",
|
||||
["obj"] = "house2arm"
|
||||
}
|
||||
--
|
||||
-- Logging & saving
|
||||
--
|
||||
@ -235,7 +259,6 @@ function debugger.createEventMonWithZone(theZone)
|
||||
debugger.outText("*** monitoring events defined in <" .. theZone.name .. ">:", 30)
|
||||
end
|
||||
for idx, aFlag in pairs(flagArray) do
|
||||
|
||||
local evt = tonumber(aFlag)
|
||||
if evt and (debugger.verbose or theZone.verbose) then
|
||||
if evt < 0 then evt = 0 end
|
||||
@ -288,7 +311,6 @@ function debugger.isObserving(flagName)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return observers
|
||||
end
|
||||
|
||||
@ -476,43 +498,62 @@ end
|
||||
function debugger.readConfigZone()
|
||||
local theZone = cfxZones.getZoneByName("debuggerConfig")
|
||||
if not theZone then
|
||||
if debugger.verbose then
|
||||
debugger.outText("+++debug: NO config zone!", 30)
|
||||
end
|
||||
theZone = cfxZones.createSimpleZone("debuggerConfig")
|
||||
end
|
||||
debugger.configZone = theZone
|
||||
|
||||
debugger.active = cfxZones.getBoolFromZoneProperty(theZone, "active", true)
|
||||
debugger.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
||||
debugger.active = theZone:getBoolFromZoneProperty("active", true)
|
||||
debugger.verbose = theZone.verbose
|
||||
|
||||
if cfxZones.hasProperty(theZone, "on?") then
|
||||
debugger.onFlag = cfxZones.getStringFromZoneProperty(theZone, "on?", "<none>")
|
||||
if theZone:hasProperty("on?") then
|
||||
debugger.onFlag = theZone:getStringFromZoneProperty("on?", "<none>")
|
||||
debugger.lastOn = cfxZones.getFlagValue(debugger.onFlag, theZone)
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "off?") then
|
||||
debugger.offFlag = cfxZones.getStringFromZoneProperty(theZone, "off?", "<none>")
|
||||
if theZone:hasProperty("off?") then
|
||||
debugger.offFlag = theZone:getStringFromZoneProperty("off?", "<none>")
|
||||
debugger.lastOff = cfxZones.getFlagValue(debugger.offFlag, theZone)
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "reset?") then
|
||||
debugger.resetFlag = cfxZones.getStringFromZoneProperty(theZone, "reset?", "<none>")
|
||||
if theZone:hasProperty("reset?") then
|
||||
debugger.resetFlag = theZone:getStringFromZoneProperty("reset?", "<none>")
|
||||
debugger.lastReset = cfxZones.getFlagValue(debugger.resetFlag, theZone)
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "state?") then
|
||||
debugger.stateFlag = cfxZones.getStringFromZoneProperty(theZone, "state?", "<none>")
|
||||
if theZone:hasProperty("state?") then
|
||||
debugger.stateFlag = theZone:getStringFromZoneProperty("state?", "<none>")
|
||||
debugger.lastState = cfxZones.getFlagValue(debugger.stateFlag, theZone)
|
||||
end
|
||||
|
||||
debugger.ups = cfxZones.getNumberFromZoneProperty(theZone, "ups", 4)
|
||||
|
||||
if debugger.verbose then
|
||||
debugger.outText("+++debug: read config", 30)
|
||||
end
|
||||
debugger.ups = theZone:getNumberFromZoneProperty("ups", 4)
|
||||
end
|
||||
|
||||
function debugger.readSpawnTypeZone()
|
||||
local theZone = cfxZones.getZoneByName("debuggerSpawnTypes")
|
||||
if not theZone then
|
||||
theZone = cfxZones.createSimpleZone("debuggerSpawnTypes")
|
||||
end
|
||||
local allAttribuites = theZone:getAllZoneProperties()
|
||||
for attrName, aValue in pairs(allAttribuites) do
|
||||
local theLow = string.lower(attrName)
|
||||
local before = debugger.spawnTypes[theLow]
|
||||
if before then
|
||||
debugger.spawnTypes[theLow] = aValue
|
||||
if theZone.verbose or debugger.verbose then
|
||||
trigger.action.outText("+++debug: changed generic '" .. theLow .. "' from <" .. before .. "> to <" .. aValue .. ">", 30)
|
||||
end
|
||||
else
|
||||
if theZone.verbose or debugger.verbose then
|
||||
if theLow == "verbose" then -- filtered
|
||||
else
|
||||
trigger.action.outText("+++debug: generic '" .. theLow .. "' unknown, not replaced.", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function debugger.start()
|
||||
-- lib check
|
||||
if not dcsCommon.libCheck then
|
||||
@ -526,6 +567,9 @@ function debugger.start()
|
||||
-- read config
|
||||
debugger.readConfigZone()
|
||||
|
||||
-- read spawn types
|
||||
debugger.readSpawnTypeZone()
|
||||
|
||||
-- process debugger Zones
|
||||
-- old style
|
||||
local attrZones = cfxZones.getZonesWithAttributeNamed("debug?")
|
||||
@ -536,7 +580,7 @@ function debugger.start()
|
||||
|
||||
local attrZones = cfxZones.getZonesWithAttributeNamed("debug")
|
||||
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
|
||||
|
||||
local attrZones = cfxZones.getZonesWithAttributeNamed("events?")
|
||||
@ -546,7 +590,7 @@ function debugger.start()
|
||||
|
||||
local attrZones = cfxZones.getZonesWithAttributeNamed("events")
|
||||
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
|
||||
-- events
|
||||
|
||||
@ -683,7 +727,7 @@ function debugDemon.getArgs(theCommands)
|
||||
end
|
||||
|
||||
--
|
||||
-- stage demon's main command interpreter.
|
||||
-- debug demon's main command interpreter.
|
||||
-- magic lies in using the keywords as keys into a
|
||||
-- function table that holds all processing functions
|
||||
-- I wish we had that back in the Oberon days.
|
||||
@ -743,7 +787,7 @@ debugger.outText("*** debugger: commands are:" ..
|
||||
"\n " .. debugDemon.markOfDemon .. "o <flagname> [with <observername>] -- observe a flag for change" ..
|
||||
"\n " .. debugDemon.markOfDemon .. "forget <flagname> [with <observername>] -- stop observing a flag" ..
|
||||
"\n " .. debugDemon.markOfDemon .. "new <observername> [[for] <condition>] -- create observer for flags" ..
|
||||
"\n " .. debugDemon.markOfDemon .. "update <observername> [[to] <condition>] -- change observer's condition" ..
|
||||
"\n " .. debugDemon.markOfDemon .. "update <observername> [to] <condition> -- change observer's condition" ..
|
||||
"\n " .. debugDemon.markOfDemon .. "drop <observername> -- remove observer from debugger" ..
|
||||
"\n " .. debugDemon.markOfDemon .. "list [<match>] -- list observers [name contains <match>]" ..
|
||||
"\n " .. debugDemon.markOfDemon .. "who <flagname> -- all who observe <flagname>" ..
|
||||
@ -752,10 +796,16 @@ debugger.outText("*** debugger: commands are:" ..
|
||||
"\n\n " .. debugDemon.markOfDemon .. "snap [<observername>] -- create new snapshot of flags" ..
|
||||
"\n " .. debugDemon.markOfDemon .. "compare -- compare snapshot flag values with current" ..
|
||||
"\n " .. debugDemon.markOfDemon .. "note <your note> -- add <your note> to the text log" ..
|
||||
"\n\n " .. debugDemon.markOfDemon .. "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 " .. debugDemon.markOfDemon .. "eventmon last -- analyse last reported event" ..
|
||||
"\n\n " .. debugDemon.markOfDemon .. "q <Lua Var> -- Query value of Lua variable <Lua Var>" ..
|
||||
"\n " .. debugDemon.markOfDemon .. "a <Lua Var> -- Analyse structure of Lua variable <Lua Var>" ..
|
||||
"\n " .. debugDemon.markOfDemon .. "w <Lua Var> [=] <Lua Value> -- Write <Lua Value> to variable <Lua Var>" ..
|
||||
"\n\n " .. debugDemon.markOfDemon .. "start -- starts debugger" ..
|
||||
"\n " .. debugDemon.markOfDemon .. "stop -- stop debugger" ..
|
||||
@ -807,7 +857,7 @@ function debugDemon.processNewCommand(args, event)
|
||||
return false
|
||||
end
|
||||
theZone.debugInputMethod = condition
|
||||
end
|
||||
end
|
||||
|
||||
debugger.addDebugger(theZone)
|
||||
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debugger: new observer <" .. observerName .. "> for <" .. theZone.debugInputMethod .. ">", 30)
|
||||
@ -815,7 +865,7 @@ function debugDemon.processNewCommand(args, event)
|
||||
end
|
||||
|
||||
function debugDemon.processUpdateCommand(args, event)
|
||||
-- syntax update <observername> [[to] <condition>]
|
||||
-- syntax update <observername> [to] <condition>
|
||||
local observerName = args[1]
|
||||
if not observerName then
|
||||
debugger.outText("*** update: missing observer name.", 30)
|
||||
@ -1271,6 +1321,7 @@ end
|
||||
|
||||
function debugDemon.doEventMon(theEvent)
|
||||
if not theEvent then return end
|
||||
if not debugger.active then return end
|
||||
local ID = theEvent.id
|
||||
if debugger.showEvents[ID] then
|
||||
-- we show this event
|
||||
@ -1287,11 +1338,47 @@ function debugDemon.doEventMon(theEvent)
|
||||
end
|
||||
end
|
||||
debugger.outText(m, 30)
|
||||
-- save it to lastevent so we can analyse
|
||||
debugger.lastEvent = theEvent
|
||||
end
|
||||
end
|
||||
|
||||
debugDemon.m = ""
|
||||
-- dumpVar2m, invoke externally dumpVar2m(varname, var)
|
||||
function debugDemon.dumpVar2m(key, value, prefix, inrecursion)
|
||||
-- based on common's dumpVar, appends to var "m"
|
||||
if not inrecursion then
|
||||
-- start, init m
|
||||
debugDemon.m = "analysis of <" .. key .. ">\n==="
|
||||
end
|
||||
if not value then value = "nil" end
|
||||
if not prefix then prefix = "" end
|
||||
prefix = " " .. prefix
|
||||
if type(value) == "table" then
|
||||
debugDemon.m = debugDemon.m .. "\n" .. prefix .. key .. ": [ "
|
||||
-- iterate through all kvp
|
||||
for k,v in pairs (value) do
|
||||
debugDemon.dumpVar2m(k, v, prefix, true)
|
||||
end
|
||||
debugDemon.m = debugDemon.m .. "\n" .. prefix .. " ] - end " .. key
|
||||
|
||||
elseif type(value) == "boolean" then
|
||||
local b = "false"
|
||||
if value then b = "true" end
|
||||
debugDemon.m = debugDemon.m .. "\n" .. prefix .. key .. ": " .. b
|
||||
|
||||
else -- simple var, show contents, ends recursion
|
||||
debugDemon.m = debugDemon.m .. "\n" .. prefix .. key .. ": " .. value
|
||||
end
|
||||
|
||||
if not inrecursion then
|
||||
-- output a marker to find in the log / screen
|
||||
debugDemon.m = debugDemon.m .. "\n" .. "=== analysis end\n"
|
||||
end
|
||||
end
|
||||
|
||||
function debugDemon.processEventMonCommand(args, event)
|
||||
-- turn event monito on/off
|
||||
-- turn event monitor all/off/?/last
|
||||
-- syntax: -eventmon on|off
|
||||
local aParam = dcsCommon.trim(event.remainder)
|
||||
if not aParam or aParam:len() < 1 then
|
||||
@ -1300,7 +1387,6 @@ function debugDemon.processEventMonCommand(args, event)
|
||||
aParam = string.upper(aParam)
|
||||
evtNum = tonumber(aParam)
|
||||
if aParam == "ON" or aParam == "ALL" then
|
||||
-- debugger.eventmon = true
|
||||
debugger.outText("*** eventmon: turned ON, showing ALL events", 30)
|
||||
local events = {}
|
||||
for idx,evt in pairs(debugDemon.eventList) do
|
||||
@ -1322,6 +1408,13 @@ function debugDemon.processEventMonCommand(args, event)
|
||||
m = m .. "\n" .. evt
|
||||
end
|
||||
debugger.outText(m .. "\n*** end of list", 30)
|
||||
elseif aParam == "LAST" then
|
||||
if debugger.lastEvent then
|
||||
debugDemon.dumpVar2m("event", debugger.lastEvent)
|
||||
debugger.outText(debugDemon.m, 39)
|
||||
else
|
||||
debugger.outText("*** eventmon: no event on record", 39)
|
||||
end
|
||||
else
|
||||
debugger.outText("*** eventmon: unknown parameter <" .. event.remainder .. ">", 30)
|
||||
end
|
||||
@ -1335,9 +1428,7 @@ end
|
||||
function debugDemon.processQueryCommand(args, event)
|
||||
-- syntax -q <name> with name a (qualified) Lua table reference
|
||||
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
|
||||
debugger.outText("*** q: missing Lua table/element name.", 30)
|
||||
return false -- allows correction
|
||||
@ -1369,6 +1460,33 @@ function debugDemon.processQueryCommand(args, event)
|
||||
return true
|
||||
end
|
||||
|
||||
function debugDemon.processAnalyzeCommand(args, event)
|
||||
-- syntax -a <name> with name a (qualified) Lua table reference
|
||||
local theName = args[1]
|
||||
|
||||
if not theName then
|
||||
debugger.outText("*** a: missing Lua table/element name.", 30)
|
||||
return false -- allows correction
|
||||
end
|
||||
theName = dcsCommon.stringRemainsStartingWith(event.remainder, theName)
|
||||
|
||||
-- put this into a string, and execute it
|
||||
local exec = "return " .. theName
|
||||
local f = loadstring(exec)
|
||||
local res
|
||||
if pcall(f) then
|
||||
res = f()
|
||||
debugDemon.dumpVar2m(theName, res)
|
||||
res = debugDemon.m
|
||||
else
|
||||
res = "[Lua error]"
|
||||
end
|
||||
|
||||
debugger.outText("[" .. dcsCommon.nowString() .. "] <" .. theName .. "> = ".. res, 30)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function debugDemon.processWriteCommand(args, event)
|
||||
-- syntax -w <name> <value> with name a (qualified) Lua table reference and value a Lua value (including strings, with quotes of course). {} means an empty set etc. you CAN call into DCS MSE with this, and create a lot of havoc.
|
||||
-- also, allow "=" semantic, -w p = {x=1, y=2}
|
||||
@ -1402,6 +1520,324 @@ function debugDemon.processWriteCommand(args, event)
|
||||
return true
|
||||
end
|
||||
|
||||
--
|
||||
-- smoke & boom
|
||||
--
|
||||
|
||||
function debugDemon.processSmokeCommand(args, event)
|
||||
-- syntax -color
|
||||
local color = 0 -- green default
|
||||
local colorCom = args[1]
|
||||
if colorCom then
|
||||
colorCom = colorCom:lower()
|
||||
if colorCom == "red" or colorCom == "1" then color = 1
|
||||
elseif colorCom == "white" or colorCom == "2" then color = 2
|
||||
elseif colorCom == "orange" or colorCom == "3" then color = 3
|
||||
elseif colorCom == "blue" or colorCom == "4" then color = 4
|
||||
elseif colorCom == "green" or colorCom == "0" then color = 0
|
||||
else
|
||||
debugger.outText("*** smoke: unknown color <" .. colorCom .. ">, using green.", 30)
|
||||
end
|
||||
local pos = event.pos
|
||||
local h = land.getHeight({x = pos.x, y = pos.z}) + 1
|
||||
local p = { x = event.pos.x, y = h, z = event.pos.z}
|
||||
trigger.action.smoke(p, color)
|
||||
debugger.outText("*** smoke: placed smoke at <" .. dcsCommon.point2text(p, true) .. ">.", 30)
|
||||
end
|
||||
end
|
||||
|
||||
function debugDemon.processBoomCommand(args, event)
|
||||
-- syntax -color
|
||||
local power = 1 -- boom default
|
||||
local powerCom = args[1]
|
||||
if powerCom then
|
||||
powerCom = tonumber(powerCom)
|
||||
if powerCom then
|
||||
power = powerCom
|
||||
end
|
||||
end
|
||||
local pos = event.pos
|
||||
local h = land.getHeight({x = pos.x, y = pos.z}) + 1
|
||||
local p = { x = event.pos.x, y = h, z = event.pos.z}
|
||||
trigger.action.explosion(p, power)
|
||||
debugger.outText("*** boom: placed <" .. power .. "> explosion at <" .. dcsCommon.point2text(p, true) .. ">.", 30)
|
||||
end
|
||||
|
||||
--
|
||||
-- spawning units at the location of the mark
|
||||
--
|
||||
|
||||
function debugDemon.getCoaFromCommand(args)
|
||||
for i=1, #args do
|
||||
local aParam = args[i]
|
||||
if dcsCommon.stringStartsWith(aParam, "red", true) then return 1, i end
|
||||
if dcsCommon.stringStartsWith(aParam, "blu", true) then return 2, i end
|
||||
if dcsCommon.stringStartsWith(aParam, "neu", true) then return 0, i end
|
||||
end
|
||||
return 0, nil
|
||||
end
|
||||
|
||||
function debugDemon.getAirFromCommand(args)
|
||||
for i=1, #args do
|
||||
local aParam = args[i]
|
||||
if aParam:lower() == "inair" then return true, i end
|
||||
if aParam:lower() == "air" then return true, i end
|
||||
end
|
||||
return false, nil
|
||||
end
|
||||
|
||||
function debugDemon.getHeadingFromCommand(args)
|
||||
for i=1, #args do
|
||||
local aParam = args[i]
|
||||
if dcsCommon.stringStartsWith(aParam, "heading=", true) then
|
||||
local parts = dcsCommon.splitString(aParam, "=")
|
||||
local num = parts[2]
|
||||
if num and tonumber(num) then
|
||||
return tonumber(num), i
|
||||
end
|
||||
end
|
||||
end
|
||||
return 0, nil
|
||||
end
|
||||
|
||||
function debugDemon.getNumFromCommand(args)
|
||||
for i=1, #args do
|
||||
local aParam = args[i]
|
||||
local num = tonumber(aParam)
|
||||
if num then return num, i end
|
||||
end
|
||||
return 1, nil
|
||||
end
|
||||
|
||||
function debugDemon.processSpawnCommand(args, event)
|
||||
-- complex syntax:
|
||||
-- spawn [red|blue|neutral] [number] <type> [heading=<number>] | "?"
|
||||
local params = dcsCommon.clone(args)
|
||||
-- for i=1, #params do
|
||||
-- trigger.action.outText("arg[" .. i .."] = <" .. params[i] .. ">", 30)
|
||||
-- end
|
||||
|
||||
-- get coalition from input
|
||||
|
||||
local coa, idx = debugDemon.getCoaFromCommand(params)
|
||||
if idx then table.remove(params, idx) end
|
||||
local inAir, idy = debugDemon.getAirFromCommand(params)
|
||||
if idy then table.remove(params, idy) end
|
||||
local num, idz = debugDemon.getNumFromCommand(params)
|
||||
if idz then table.remove(params, idz) end
|
||||
local heading, idk = debugDemon.getHeadingFromCommand(params)
|
||||
if idk then table.remove(params, idk) end
|
||||
|
||||
local class = params[1]
|
||||
if not class then
|
||||
debugger.outText("*** spawn: missing keyword (what to spawn).", 30)
|
||||
return
|
||||
end
|
||||
|
||||
class = class:lower()
|
||||
|
||||
-- when we are here, we have reduced all params, so class is [1]
|
||||
-- trigger.action.outText("spawn with class <" .. class .. ">, num <" .. num .. ">, inAir <" .. dcsCommon.bool2Text(inAir) .. ">, coa <" .. coa .. ">, hdg <" .. heading .. ">", 30)
|
||||
heading = heading * 0.0174533 -- in rad
|
||||
|
||||
local pos = event.pos
|
||||
local h = land.getHeight({x = pos.x, y = pos.z}) + 1
|
||||
local p = { x = event.pos.x, y = h, z = event.pos.z}
|
||||
|
||||
if class == "tank" or class == "tanks" then
|
||||
-- spawn the 'tank' class
|
||||
local theType = debugger.spawnTypes["tank"]
|
||||
return debugDemon.spawnTypeWithCat(theType, coa, num, p, nil,heading)
|
||||
elseif class == "man" or class == "soldier" or class == "men" then
|
||||
local theType = debugger.spawnTypes["inf"]
|
||||
return debugDemon.spawnTypeWithCat(theType, coa, num, p, nil,heading)
|
||||
|
||||
elseif class == "inf" or class == "ifv" or class == "sam" or
|
||||
class == "arty" or class == "aaa" then
|
||||
local theType = debugger.spawnTypes[class]
|
||||
return debugDemon.spawnTypeWithCat(theType, coa, num, p, nil,heading)
|
||||
elseif class == "truck" or class == "trucks" then
|
||||
local theType = debugger.spawnTypes["truck"]
|
||||
return debugDemon.spawnTypeWithCat(theType, coa, num, p, nil,heading)
|
||||
elseif class == "manpad" or class == "manpads" or class == "pad" or class == "pads" then
|
||||
local theType = debugger.spawnTypes["manpad"]
|
||||
return debugDemon.spawnTypeWithCat(theType, coa, num, p, nil,heading)
|
||||
|
||||
elseif class == "ship" or class == "ships" then
|
||||
local theType = debugger.spawnTypes["ship"]
|
||||
return debugDemon.spawnTypeWithCat(theType, coa, num, p, Group.Category.SHIP, heading)
|
||||
|
||||
elseif class == "jet" or class == "jets" then
|
||||
local theType = debugger.spawnTypes["jet"]
|
||||
return debugDemon.spawnAirWIthCat(theType, coa, num, p, nil, 1000, 160, heading)
|
||||
|
||||
elseif class == "ww2" then
|
||||
local theType = debugger.spawnTypes[class]
|
||||
return debugDemon.spawnAirWIthCat(theType, coa, num, p, nil, 1000, 100, heading)
|
||||
|
||||
elseif class == "bomber" or class == "awacs" then
|
||||
local theType = debugger.spawnTypes[class]
|
||||
return debugDemon.spawnAirWIthCat(theType, coa, num, p, nil, 8000, 200, heading)
|
||||
|
||||
elseif class == "drone" then
|
||||
local theType = debugger.spawnTypes[class]
|
||||
return debugDemon.spawnAirWIthCat(theType, coa, num, p, nil, 3000, 77, heading)
|
||||
|
||||
elseif class == "helo" or class == "helos" then
|
||||
local theType = debugger.spawnTypes["helo"]
|
||||
return debugDemon.spawnAirWIthCat(theType, coa, num, p, Group.Category.HELICOPTER, 200, 40, heading)
|
||||
|
||||
elseif class == "cargo" or class == "obj" then
|
||||
local isCargo = (class == "cargo")
|
||||
local theType = debugger.spawnTypes[class]
|
||||
return debugDemon.spawnObjects(theType, coa, num, p, isCargo, heading)
|
||||
|
||||
elseif class == "?" then
|
||||
local m = " spawn: invoke '-spawn [number] [coalition] <type> [heading]' with \n" ..
|
||||
" number = any number, default is 1\n" ..
|
||||
" coalition = 'red' | 'blue' | 'neutral', default is neutral\n" ..
|
||||
" heading = 'heading=<number>' - direction to face, in degrees, no blanks\n" ..
|
||||
" <type> = what to spawn, any of the following pre-defined (no quotes)\n" ..
|
||||
" 'tank' - a tank " .. debugDemon.tellType("tank") .. "\n" ..
|
||||
" 'ifv' - an IFV " .. debugDemon.tellType("ifv") .. "\n" ..
|
||||
" 'inf' - an infantry soldier " .. debugDemon.tellType("inf") .. "\n" ..
|
||||
" 'sam' - a SAM vehicle " .. debugDemon.tellType("sam") .. "\n" ..
|
||||
" 'aaa' - a AAA vehicle " .. debugDemon.tellType("aaa") .. "\n" ..
|
||||
" 'arty' - artillery vehicle " .. debugDemon.tellType("arty") .. "\n" ..
|
||||
" 'manpad' - a soldier with SAM " .. debugDemon.tellType("manpad") .. "\n" ..
|
||||
" 'truck' - a truck " .. debugDemon.tellType("truck") .. "\n\n" ..
|
||||
" 'jet' - a fast aircraft " .. debugDemon.tellType("jet") .. "\n" ..
|
||||
" 'ww2' - a warbird " .. debugDemon.tellType("ww2") .. "\n" ..
|
||||
" 'bomber' - a heavy bomber " .. debugDemon.tellType("bomber") .. "\n" ..
|
||||
" 'awacs' - an AWACS plane " .. debugDemon.tellType("awacs") .. "\n" ..
|
||||
" 'drone' - a drone " .. debugDemon.tellType("drone") .. "\n" ..
|
||||
" 'helo' - a helicopter " .. debugDemon.tellType("helo") .. "\n\n" ..
|
||||
" 'ship' - a naval unit" .. debugDemon.tellType("ship") .. "\n\n" ..
|
||||
" 'cargo' - some helicopter cargo " .. debugDemon.tellType("cargo") .. "\n" ..
|
||||
" 'obj' - a static object " .. debugDemon.tellType("obj") .. "\n"
|
||||
|
||||
debugger.outText(m, 30)
|
||||
return true
|
||||
else
|
||||
debugger.outText("*** spawn: unknown kind <" .. class .. ">.", 30)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function debugDemon.tellType(theType)
|
||||
return " [" .. debugger.spawnTypes[theType] .. "]"
|
||||
end
|
||||
|
||||
function debugDemon.spawnTypeWithCat(theType, coa, num, p, cat, heading)
|
||||
trigger.action.outText("heading is <" .. heading .. ">", 30)
|
||||
if not cat then cat = Group.Category.GROUND end
|
||||
if not heading then heading = 0 end
|
||||
|
||||
local xOff = 0
|
||||
local yOff = 0
|
||||
-- build group
|
||||
local groupName = dcsCommon.uuid(theType)
|
||||
local gData = dcsCommon.createEmptyGroundGroupData(groupName)
|
||||
for i=1, num do
|
||||
local aUnit = {}
|
||||
aUnit = dcsCommon.createGroundUnitData(groupName .. "-" .. i, theType)
|
||||
--aUnit.heading = heading
|
||||
dcsCommon.addUnitToGroupData(aUnit, gData, xOff, yOff, heading)
|
||||
xOff = xOff + 10
|
||||
yOff = yOff + 10
|
||||
end
|
||||
|
||||
-- arrange in a grid formation
|
||||
local radius = math.floor(math.sqrt(num) * 10)
|
||||
if cat == Group.Category.SHIP then
|
||||
radius = math.floor(math.sqrt(num) * 100)
|
||||
end
|
||||
|
||||
dcsCommon.arrangeGroupDataIntoFormation(gData, radius, 10, "GRID")
|
||||
|
||||
-- move to destination
|
||||
dcsCommon.moveGroupDataTo(gData, p.x, p.z)
|
||||
|
||||
-- spawn
|
||||
local cty = dcsCommon.getACountryForCoalition(coa)
|
||||
local theGroup = coalition.addGroup(cty, cat, gData)
|
||||
if theGroup then
|
||||
debugger.outText("[" .. dcsCommon.nowString() .. "] created units at " .. dcsCommon.point2text(p, true), 30)
|
||||
return true
|
||||
else
|
||||
debugger.outText("[" .. dcsCommon.nowString() .. "] failed to created units", 30)
|
||||
return false
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function debugDemon.spawnAirWIthCat(theType, coa, num, p, cat, alt, speed, heading)
|
||||
if not cat then cat = Group.Category.AIRPLANE end
|
||||
local xOff = 0
|
||||
local yOff = 0
|
||||
-- build group
|
||||
local groupName = dcsCommon.uuid(theType)
|
||||
local gData = dcsCommon.createEmptyAircraftGroupData(groupName)
|
||||
for i=1, num do
|
||||
local aUnit = {}
|
||||
aUnit = dcsCommon.createAircraftUnitData(groupName .. "-" .. i, theType, false, alt, speed)
|
||||
--aUnit.heading = heading
|
||||
dcsCommon.addUnitToGroupData(aUnit, gData, xOff, yOff, heading)
|
||||
xOff = xOff + 30
|
||||
yOff = yOff + 30
|
||||
end
|
||||
-- move to destination
|
||||
dcsCommon.moveGroupDataTo(gData, p.x, p.z)
|
||||
|
||||
-- make waypoints: initial point and 200 km away in direction heading
|
||||
local p2 = dcsCommon.pointInDirectionOfPointXYY(heading, 200000, p)
|
||||
local wp1 = dcsCommon.createSimpleRoutePointData(p, alt, speed)
|
||||
local wp2 = dcsCommon.createSimpleRoutePointData(p2, alt, speed)
|
||||
-- add waypoints
|
||||
dcsCommon.addRoutePointForGroupData(gData, wp1)
|
||||
dcsCommon.addRoutePointForGroupData(gData, wp2)
|
||||
|
||||
-- spawn
|
||||
local cty = dcsCommon.getACountryForCoalition(coa)
|
||||
local theGroup = coalition.addGroup(cty, cat, gData)
|
||||
if theGroup then
|
||||
debugger.outText("[" .. dcsCommon.nowString() .. "] created air units at " .. dcsCommon.point2text(p, true), 30)
|
||||
return true
|
||||
else
|
||||
debugger.outText("[" .. dcsCommon.nowString() .. "] failed to created air units", 30)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function debugDemon.spawnObjects(theType, coa, num, p, cargo, heading)
|
||||
if not cargo then cargo = false end
|
||||
local cty = dcsCommon.getACountryForCoalition(coa)
|
||||
local xOff = 0
|
||||
local yOff = 0
|
||||
local success = false
|
||||
-- build static objects and spawn individually
|
||||
for i=1, num do
|
||||
local groupName = dcsCommon.uuid(theType)
|
||||
local gData = dcsCommon.createStaticObjectData(groupName, theType, 0, false, cargo, 1000)
|
||||
gData.x = xOff + p.x
|
||||
gData.y = yOff + p.z
|
||||
gData.heading = heading
|
||||
local theGroup = coalition.addStaticObject(cty, gData)
|
||||
success = theGroup
|
||||
xOff = xOff + 10 -- stagger by 10m, 10m
|
||||
yOff = yOff + 10
|
||||
end
|
||||
|
||||
-- was it worth it?
|
||||
if success then
|
||||
debugger.outText("[" .. dcsCommon.nowString() .. "] created objects at " .. dcsCommon.point2text(p, true), 30)
|
||||
return true
|
||||
else
|
||||
debugger.outText("[" .. dcsCommon.nowString() .. "] failed to create objects", 30)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- init and start
|
||||
@ -1430,6 +1866,7 @@ function debugDemon.readConfigZone()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function debugDemon.init()
|
||||
if not dcsCommon.libCheck then
|
||||
trigger.action.outText("cfx interactive debugger requires dcsCommon", 30)
|
||||
@ -1473,10 +1910,15 @@ function debugDemon.init()
|
||||
debugDemon.addCommndProcessor("help", debugDemon.processHelpCommand)
|
||||
|
||||
debugDemon.addCommndProcessor("remove", debugDemon.processRemoveCommand)
|
||||
debugDemon.addCommndProcessor("spawn", debugDemon.processSpawnCommand)
|
||||
debugDemon.addCommndProcessor("add", debugDemon.processSpawnCommand)
|
||||
|
||||
debugDemon.addCommndProcessor("eventmon", debugDemon.processEventMonCommand)
|
||||
debugDemon.addCommndProcessor("q", debugDemon.processQueryCommand)
|
||||
debugDemon.addCommndProcessor("w", debugDemon.processWriteCommand)
|
||||
debugDemon.addCommndProcessor("a", debugDemon.processAnalyzeCommand)
|
||||
debugDemon.addCommndProcessor("smoke", debugDemon.processSmokeCommand)
|
||||
debugDemon.addCommndProcessor("boom", debugDemon.processBoomCommand)
|
||||
return true
|
||||
end
|
||||
|
||||
@ -1512,14 +1954,9 @@ end
|
||||
- inspect objects, dumping category, life, if it's tasking, latLon, alt, speed, direction
|
||||
|
||||
- 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'
|
||||
|
||||
- track lua vars for change in value
|
||||
|
||||
- dml version can config to start with events list, e.g. 1, 4, 7
|
||||
--]]--
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
unitPersistence = {}
|
||||
unitPersistence.version = '1.1.1'
|
||||
unitPersistence.version = '2.0.0'
|
||||
unitPersistence.verbose = false
|
||||
unitPersistence.updateTime = 60 -- seconds. Once every minute check statics
|
||||
unitPersistence.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxZones", -- Zones, of course
|
||||
"dcsCommon",
|
||||
"cfxZones",
|
||||
"persistence",
|
||||
"cfxMX",
|
||||
}
|
||||
@ -19,7 +19,8 @@ unitPersistence.requiredLibs = {
|
||||
1.1.0 - added air and sea units - for filtering destroyed units
|
||||
1.1.1 - fixed static link (again)
|
||||
- fixed air spawn (fixed wing)
|
||||
|
||||
2.0.0 - dmlZones, OOP
|
||||
cleanup
|
||||
|
||||
REQUIRES PERSISTENCE AND MX
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
williePete = {}
|
||||
williePete.version = "1.0.2"
|
||||
williePete.version = "2.0.0"
|
||||
williePete.ups = 10 -- we update at 10 fps, so accuracy of a
|
||||
-- missile moving at Mach 2 is within 33 meters,
|
||||
-- with interpolation even at 3 meters
|
||||
@ -14,12 +14,16 @@ williePete.requiredLibs = {
|
||||
1.0.0 - Initial version
|
||||
1.0.1 - update to suppress verbosity
|
||||
1.0.2 - added Gazelle WP
|
||||
|
||||
2.0.0 - dmlZones, OOP
|
||||
- Guards for multi-unit player groups
|
||||
- getFirstLivingPlayerInGroupNamed()
|
||||
--]]--
|
||||
|
||||
williePete.willies = {}
|
||||
williePete.wpZones = {}
|
||||
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
|
||||
|
||||
-- recognizes WP munitions. May require regular update when new
|
||||
@ -90,30 +94,29 @@ end
|
||||
|
||||
|
||||
function williePete.createWPZone(aZone)
|
||||
aZone.coalition = cfxZones.getCoalitionFromZoneProperty(aZone, "wpTarget", 0) -- side that marks it on map, and who fires arty
|
||||
aZone.shellStrength = cfxZones.getNumberFromZoneProperty(aZone, "shellStrength", 500) -- power of shells (strength)
|
||||
aZone.shellNum = cfxZones.getNumberFromZoneProperty(aZone, "shellNum", 17) -- number of shells in bombardment
|
||||
aZone.transitionTime = cfxZones.getNumberFromZoneProperty(aZone, "transitionTime", 20) -- average time of travel for projectiles
|
||||
aZone.coolDown = cfxZones.getNumberFromZoneProperty(aZone, "coolDown", 180) -- cooldown after arty fire, used to set readyTime
|
||||
aZone.baseAccuracy = cfxZones.getNumberFromZoneProperty(aZone, "baseAccuracy", 50)
|
||||
aZone.coalition = aZone:getCoalitionFromZoneProperty("wpTarget", 0) -- side that marks it on map, and who fires arty
|
||||
aZone.shellStrength = aZone:getNumberFromZoneProperty( "shellStrength", 500) -- power of shells (strength)
|
||||
aZone.shellNum = aZone:getNumberFromZoneProperty("shellNum", 17) -- number of shells in bombardment
|
||||
aZone.transitionTime = aZone:getNumberFromZoneProperty( "transitionTime", 20) -- average time of travel for projectiles
|
||||
aZone.coolDown = aZone:getNumberFromZoneProperty("coolDown", 180) -- cooldown after arty fire, used to set readyTime
|
||||
aZone.baseAccuracy = aZone:getNumberFromZoneProperty( "baseAccuracy", 50)
|
||||
|
||||
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.checkedIn = {} -- dict of all planes currently checked in
|
||||
|
||||
aZone.wpMethod = cfxZones.getStringFromZoneProperty(aZone, "wpMethod", "change")
|
||||
|
||||
aZone.checkInRange = cfxZones.getNumberFromZoneProperty(aZone, "checkInRange", williePete.checkInRange) -- default to my default
|
||||
|
||||
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")
|
||||
aZone.wpMethod = aZone:getStringFromZoneProperty("wpMethod", "change")
|
||||
if aZone:hasProperty("method") then
|
||||
aZone.wpMethod = aZone:getStringFromZoneProperty("method", "change")
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(aZone, "wpFire!") then
|
||||
aZone.wpFire = cfxZones.getStringFromZoneProperty(aZone, "wpFire!", "<none)")
|
||||
aZone.checkInRange = aZone:getNumberFromZoneProperty( "checkInRange", williePete.checkInRange) -- default to my default
|
||||
|
||||
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
|
||||
|
||||
if aZone.verbose then
|
||||
@ -142,6 +145,7 @@ function williePete.startPlayerGUI()
|
||||
end
|
||||
|
||||
unitInfo.name = uName -- needed for reverse-lookup
|
||||
unitInfo.gName = gName -- also needed for reverse lookup
|
||||
unitInfo.coa = coa
|
||||
unitInfo.gID = gData.groupId
|
||||
unitInfo.uID = uData.unitId
|
||||
@ -159,12 +163,20 @@ function williePete.startPlayerGUI()
|
||||
|
||||
if pass then -- we install a menu for this group
|
||||
-- we may not want check in stuff, but it could be cool
|
||||
unitInfo.root = missionCommands.addSubMenuForGroup(unitInfo.gID, "FAC")
|
||||
unitInfo.checkIn = missionCommands.addCommandForGroup(unitInfo.gID, "Check In", unitInfo.root, williePete.redirectCheckIn, unitInfo)
|
||||
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.checkIn = missionCommands.addCommandForGroup(unitInfo.gID, "Check In", unitInfo.root, williePete.redirectCheckIn, unitInfo)
|
||||
williePete.groupGUIs[gName] = unitInfo
|
||||
williePete.playerGUIs[gName] = unitInfo
|
||||
end
|
||||
end
|
||||
|
||||
-- store it
|
||||
williePete.playerGUIs[uName] = unitInfo
|
||||
-- store it - WARNING: ASSUMES SINGLE-UNIT Player Groups
|
||||
--williePete.playerGUIs[uName] = unitInfo
|
||||
end
|
||||
end
|
||||
|
||||
@ -177,7 +189,7 @@ function williePete.doBoom(args)
|
||||
-- note that unit who commânded fire may no longer be alive
|
||||
-- so check it every time. unit must be alive
|
||||
-- to receive credits later
|
||||
local uName = unitInfo.name
|
||||
local uName = unitInfo.gName
|
||||
local blastRad = math.floor(math.sqrt(args.strength)) * 2
|
||||
if blastRad < 10 then blastRad = 10 end
|
||||
|
||||
@ -187,7 +199,7 @@ function williePete.doBoom(args)
|
||||
if williePete.verbose then
|
||||
trigger.action.outText("<" .. aName .. "> is in blast Radius (" .. blastRad .. "m) of shells for <" .. uName .. ">'s target coords", 30)
|
||||
end
|
||||
williePete.blastedObjects[aName] = uName -- last one gets the kill
|
||||
williePete.blastedObjects[aName] = unitInfo.name -- last one gets the kill
|
||||
end
|
||||
end
|
||||
trigger.action.explosion(args.point, args.strength)
|
||||
@ -237,16 +249,36 @@ function williePete.redirectCheckIn(unitInfo)
|
||||
timer.scheduleFunction(williePete.doCheckIn, unitInfo, timer.getTime() + 0.1)
|
||||
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)
|
||||
--trigger.action.outText("check-in received", 30)
|
||||
local theUnit = Unit.getByName(unitInfo.name)
|
||||
-- WARNING: unitInfo points to first processed player in
|
||||
-- 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
|
||||
-- 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)
|
||||
return
|
||||
end
|
||||
|
||||
local p = theUnit:getPoint()
|
||||
local p = theUnit:getPoint() -- only react to first player unit
|
||||
local theZone, dist = williePete.closestCheckInTgtZoneForCoa(p, unitInfo.coa)
|
||||
|
||||
if not theZone then
|
||||
@ -256,21 +288,23 @@ function williePete.doCheckIn(unitInfo)
|
||||
trigger.action.outSoundForGroup(unitInfo.gID, williePete.guiSound)
|
||||
return
|
||||
end
|
||||
|
||||
trigger.action.outTextForGroup(unitInfo.gID, "Too far from target zone, closest target zone is " .. theZone.name, 30)
|
||||
dist = math.floor(dist /100) / 10
|
||||
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)
|
||||
return
|
||||
end
|
||||
|
||||
-- we are now checked in to zone -- unless we are already checked in
|
||||
if theZone.checkedIn[unitInfo.name] then
|
||||
trigger.action.outTextForGroup(unitInfo.gID, unitInfo.name .. ", " .. theZone.name .. ", we heard you the first time, proceed.", 30)
|
||||
-- NOTE: we use group name, not unit name!
|
||||
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)
|
||||
return
|
||||
end
|
||||
|
||||
-- we now check in
|
||||
theZone.checkedIn[unitInfo.name] = unitInfo
|
||||
theZone.checkedIn[unitInfo.gName] = unitInfo
|
||||
|
||||
-- add the 'Target marked' menu
|
||||
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'
|
||||
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)
|
||||
end
|
||||
|
||||
@ -293,17 +327,17 @@ function williePete.doCheckOut(unitInfo)
|
||||
local wasCheckedIn = false
|
||||
local fromZone = ""
|
||||
for idx, theZone in pairs(williePete.wpZones) do
|
||||
if theZone.checkedIn[unitInfo.name] then
|
||||
if theZone.checkedIn[unitInfo.gName] then
|
||||
wasCheckedIn = true
|
||||
fromZone = theZone.name
|
||||
end
|
||||
theZone.checkedIn[unitInfo.name] = nil
|
||||
theZone.checkedIn[unitInfo.gName] = nil
|
||||
end
|
||||
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)
|
||||
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)
|
||||
end
|
||||
|
||||
@ -326,7 +360,7 @@ function williePete.rogerDodger(args)
|
||||
local unitInfo = args[1]
|
||||
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)
|
||||
end
|
||||
|
||||
@ -357,9 +391,9 @@ function williePete.doTargetMarked(unitInfo)
|
||||
|
||||
local tgtZone = unitInfo.wpInZone
|
||||
-- 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
|
||||
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)
|
||||
return
|
||||
end
|
||||
@ -368,7 +402,7 @@ function williePete.doTargetMarked(unitInfo)
|
||||
local timeRemaining = math.floor(tgtZone.readyTime - now)
|
||||
if timeRemaining > 0 then
|
||||
-- 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)
|
||||
return
|
||||
end
|
||||
@ -378,7 +412,7 @@ function williePete.doTargetMarked(unitInfo)
|
||||
local grid = coord.LLtoMGRS(coord.LOtoLL(unitInfo.pos))
|
||||
local mgrs = grid.UTMZone .. ' ' .. grid.MGRSDigraph .. ' ' .. grid.Easting .. ' ' .. grid.Northing
|
||||
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)
|
||||
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
|
||||
-- a wp
|
||||
|
||||
function williePete.zoneIsTracking(theUnit)
|
||||
function williePete.zoneIsTracking(theUnit) -- group level!
|
||||
local uName = theUnit:getName()
|
||||
local uGroup = theUnit:getGroup()
|
||||
local gName = uGroup:getName()
|
||||
|
||||
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
|
||||
return false
|
||||
end
|
||||
@ -426,6 +463,8 @@ function williePete.zedsDead(theObject)
|
||||
|
||||
local theName = theObject:getName()
|
||||
-- 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]
|
||||
if blaster then
|
||||
local theUnit = Unit.getByName(blaster)
|
||||
@ -459,7 +498,7 @@ function williePete:onEvent(event)
|
||||
|
||||
local theUnit = event.initiator
|
||||
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
|
||||
-- initiator is who fired. maybe want to test if player
|
||||
@ -470,7 +509,7 @@ function williePete:onEvent(event)
|
||||
end
|
||||
|
||||
-- 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
|
||||
return
|
||||
end
|
||||
@ -479,6 +518,8 @@ function williePete:onEvent(event)
|
||||
local theWillie = {}
|
||||
theWillie.firedBy = theUnit:getName()
|
||||
theWillie.theUnit = theUnit
|
||||
theWillie.theGroup = theUnit:getGroup()
|
||||
theWillie.gName = theWillie.theGroup:getName()
|
||||
theWillie.weapon = event.weapon
|
||||
theWillie.wt = theWillie.weapon:getTypeName()
|
||||
theWillie.pos = theWillie.weapon:getPoint()
|
||||
@ -486,15 +527,6 @@ function williePete:onEvent(event)
|
||||
|
||||
williePete.addWillie(theWillie)
|
||||
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
|
||||
|
||||
@ -505,17 +537,15 @@ function williePete.isInside(theWillie)
|
||||
local theUnit = Unit.getByName(theUnitName)
|
||||
if not theUnit then return false end -- unit dead
|
||||
if not Unit.isExist(theUnit) then return false end -- dito
|
||||
|
||||
local thePlayer = williePete.playerGUIs[theUnitName]
|
||||
if not thePlayer then return nil end
|
||||
local theGroup = theUnit:getGroup()
|
||||
local gName = theGroup:getName()
|
||||
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
|
||||
if cfxZones.pointInZone(thePoint, theZone) then
|
||||
-- we are inside. but is this the right coalition?
|
||||
if thePlayer.coa == theZone.coalition then
|
||||
--trigger.action.outText("Willie in " .. theZone.name, 30)
|
||||
if unitInfo.coa == theZone.coalition then
|
||||
return theZone
|
||||
else
|
||||
--trigger.action.outText("Willie wrong coa", 30)
|
||||
end
|
||||
-- if we want to allow neutral zones (doens't make sense)
|
||||
-- add another guard below
|
||||
@ -532,8 +562,9 @@ function williePete.projectileHit(theWillie)
|
||||
local vmod = dcsCommon.vMultScalar(theWillie.v, 0.5 / williePete.ups)
|
||||
theWillie.pos = dcsCommon.vAdd(theWillie.pos, vmod)
|
||||
|
||||
-- reset last mark for player
|
||||
local thePlayer = williePete.playerGUIs[theWillie.firedBy]
|
||||
-- reset last mark for player's group
|
||||
-- access unitInfo
|
||||
local thePlayer = williePete.playerGUIs[theWillie.gName]
|
||||
thePlayer.pos = nil
|
||||
thePlayer.wpInZone = nil
|
||||
|
||||
@ -583,20 +614,25 @@ function williePete.playerUpdate()
|
||||
-- the zone that they checked in, or they are checked out
|
||||
--local zp = cfxZones.getPoint(theZone)
|
||||
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 theUnit = Unit.getByName(unitInfo.name)
|
||||
if theUnit and Unit.isExist(theUnit) then
|
||||
local up = theUnit:getPoint()
|
||||
up.y = 0
|
||||
local isInside, dist = cfxZones.isPointInsideZone(up, theZone, theZone.checkInRange)
|
||||
|
||||
if isInside then
|
||||
dropUnit = false
|
||||
local theGroup = Group.getByName(unitInfo.gName)
|
||||
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()
|
||||
up.y = 0
|
||||
local isInside, dist = cfxZones.isPointInsideZone(up, theZone, theZone.checkInRange)
|
||||
|
||||
if isInside then
|
||||
dropUnit = false
|
||||
end
|
||||
end
|
||||
end
|
||||
if dropUnit then
|
||||
-- remove from zone check-in
|
||||
-- all outside, remove from zone check-in
|
||||
-- williePete.doCheckOut(unitInfo)
|
||||
timer.scheduleFunction(williePete.doCheckOut, unitInfo, timer.getTime() + 0.1) -- to not muck up iteration
|
||||
end
|
||||
@ -612,13 +648,10 @@ end
|
||||
function williePete.readConfigZone()
|
||||
local theZone = cfxZones.getZoneByName("wpConfig")
|
||||
if not theZone then
|
||||
if williePete.verbose then
|
||||
trigger.action.outText("+++wp: NO config zone!", 30)
|
||||
end
|
||||
theZone = cfxZones.createSimpleZone("wpConfig")
|
||||
end
|
||||
|
||||
local facTypes = cfxZones.getStringFromZoneProperty(theZone, "facTypes", "all")
|
||||
local facTypes = theZone:getStringFromZoneProperty("facTypes", "all")
|
||||
facTypes = string.upper(facTypes)
|
||||
|
||||
-- make this an array
|
||||
@ -631,19 +664,19 @@ function williePete.readConfigZone()
|
||||
williePete.facTypes = dcsCommon.trimArray(allTypes)
|
||||
|
||||
-- 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
|
||||
-- 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.guiSound = cfxZones.getStringFromZoneProperty(theZone, "guiSound", "some")
|
||||
williePete.ackSound = theZone:getStringFromZoneProperty( "ackSound", "some")
|
||||
williePete.guiSound = theZone:getStringFromZoneProperty( "guiSound", "some")
|
||||
|
||||
williePete.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
||||
williePete.verbose = theZone.verbose
|
||||
|
||||
if williePete.verbose then
|
||||
trigger.action.outText("+++msgr: read config", 30)
|
||||
trigger.action.outText("+++wp: read config", 30)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
xFlags = {}
|
||||
xFlags.version = "1.3.1"
|
||||
xFlags.version = "2.0.0"
|
||||
xFlags.verbose = false
|
||||
xFlags.hiVerbose = false
|
||||
xFlags.ups = 1 -- overwritten in get config when configZone is present
|
||||
@ -11,30 +11,11 @@ xFlags.requiredLibs = {
|
||||
xFlags - flag array transmogrifier
|
||||
|
||||
Version History
|
||||
1.0.0 - Initial version
|
||||
1.0.1 - allow flags names for ops as well
|
||||
1.1.0 - Watchflags harmonization
|
||||
1.2.0 - xDirect flag,
|
||||
- direct array support
|
||||
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
|
||||
|
||||
|
||||
|
||||
2.0.0 - dmlZones
|
||||
- OOP
|
||||
- xDirect#
|
||||
- xCount#
|
||||
- cleanup
|
||||
--]]--
|
||||
xFlags.xFlagZones = {}
|
||||
|
||||
@ -62,11 +43,7 @@ end
|
||||
|
||||
function xFlags.createXFlagsWithZone(theZone)
|
||||
local theArray = ""
|
||||
if cfxZones.hasProperty(theZone, "xFlags") then
|
||||
theArray = cfxZones.getStringFromZoneProperty(theZone, "xFlags", "<none>")
|
||||
else
|
||||
theArray = cfxZones.getStringFromZoneProperty(theZone, "xFlags?", "<none>")
|
||||
end
|
||||
theArray = theZone:getStringFromZoneProperty("xFlags?", "<none>")
|
||||
|
||||
-- now process the array and create the value arrays
|
||||
theZone.flagNames = cfxZones.flagArrayFromString(theArray)
|
||||
@ -86,95 +63,72 @@ function xFlags.createXFlagsWithZone(theZone)
|
||||
end
|
||||
end
|
||||
theZone.xHasFired = false
|
||||
if cfxZones.hasProperty(theZone, "xSuccess!") then
|
||||
theZone.xSuccess = cfxZones.getStringFromZoneProperty(theZone, "xSuccess!", "<none>")
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "out!") then
|
||||
theZone.xSuccess = cfxZones.getStringFromZoneProperty(theZone, "out!", "*<none>")
|
||||
if theZone:hasProperty("xSuccess!") then
|
||||
theZone.xSuccess = theZone:getStringFromZoneProperty("xSuccess!", "<none>")
|
||||
elseif theZone:hasProperty("out!") then
|
||||
theZone.xSuccess = theZone:getStringFromZoneProperty("out!", "*<none>")
|
||||
end
|
||||
|
||||
if not theZone.xSuccess then
|
||||
theZone.xSuccess = "*<none>"
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "xChange!") then
|
||||
theZone.xChange = cfxZones.getStringFromZoneProperty(theZone, "xChange!", "*<none>")
|
||||
if theZone:hasProperty("xChange!") then
|
||||
theZone.xChange = theZone:getStringFromZoneProperty("xChange!", "*<none>")
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "xDirect") then
|
||||
theZone.xDirect = cfxZones.getStringFromZoneProperty(theZone, "xDirect", "*<none>")
|
||||
if theZone:hasProperty("xDirect#") then
|
||||
theZone.xDirect = theZone:getStringFromZoneProperty("xDirect#", "*<none>")
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "xCount") then
|
||||
theZone.xCount = cfxZones.getStringFromZoneProperty(theZone, "xCount", "*<none>")
|
||||
if theZone:hasProperty("xCount#") then
|
||||
theZone.xCount = theZone:getStringFromZoneProperty("xCount#", "*<none>")
|
||||
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
|
||||
theZone.inspect = string.lower(theZone.inspect)
|
||||
theZone.inspect = dcsCommon.trim(theZone.inspect)
|
||||
|
||||
theZone.matchNum = cfxZones.getStringFromZoneProperty(theZone, "#hits", "1")
|
||||
|
||||
theZone.xTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "xFlagMethod", "change") -- (<>=[number or reference flag], off, on, yes, no, true, false, change
|
||||
|
||||
theZone.matchNum = theZone:getStringFromZoneProperty("#hits", "1") -- string because can also be a flag ref
|
||||
|
||||
theZone.xTriggerMethod = theZone:getStringFromZoneProperty( "xFlagMethod", "change")
|
||||
|
||||
theZone.xTriggerMethod = string.lower(theZone.xTriggerMethod)
|
||||
theZone.xTriggerMethod = dcsCommon.trim(theZone.xTriggerMethod)
|
||||
|
||||
if cfxZones.hasProperty(theZone, "xReset?") then
|
||||
theZone.xReset = cfxZones.getStringFromZoneProperty(theZone, "xReset?", "<none>")
|
||||
theZone.xLastReset = cfxZones.getFlagValue(theZone.xReset, theZone)
|
||||
if theZone:hasProperty("xReset?") then
|
||||
theZone.xReset = theZone:getStringFromZoneProperty("xReset?", "<none>")
|
||||
theZone.xLastReset = theZone:getFlagValue(theZone.xReset)
|
||||
end
|
||||
|
||||
theZone.xMethod = cfxZones.getStringFromZoneProperty(theZone, "xMethod", "inc")
|
||||
if cfxZones.hasProperty(theZone, "method") then
|
||||
theZone.xMethod = cfxZones.getStringFromZoneProperty(theZone, "method", "inc")
|
||||
theZone.xMethod = theZone:getStringFromZoneProperty("xMethod", "inc")
|
||||
if theZone:hasProperty("method") then
|
||||
theZone.xMethod = theZone:getStringFromZoneProperty("method", "inc")
|
||||
end
|
||||
|
||||
theZone.xOneShot = cfxZones.getBoolFromZoneProperty(theZone, "oneShot", true)
|
||||
theZone.xOneShot = theZone:getBoolFromZoneProperty("oneShot", true)
|
||||
|
||||
-- on / off commands
|
||||
-- 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
|
||||
trigger.action.outText("+++xFlg: <" .. theZone.name .. "> starts suspended", 30)
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "xOn?") then
|
||||
theZone.xtriggerOnFlag = cfxZones.getStringFromZoneProperty(theZone, "xOn?", "*<none1>")
|
||||
theZone.xlastTriggerOnValue = cfxZones.getFlagValue(theZone.xtriggerOnFlag, theZone)
|
||||
if theZone:hasProperty("xOn?") then
|
||||
theZone.xtriggerOnFlag = theZone:getStringFromZoneProperty("xOn?", "*<none1>")
|
||||
theZone.xlastTriggerOnValue = theZone:getFlagValue(theZone.xtriggerOnFlag)
|
||||
end
|
||||
if cfxZones.hasProperty(theZone, "xOff?") then
|
||||
theZone.xtriggerOffFlag = cfxZones.getStringFromZoneProperty(theZone, "xOff?", "*<none2>")
|
||||
theZone.xlastTriggerOffValue = cfxZones.getFlagValue(theZone.xtriggerOffFlag, theZone)
|
||||
if theZone:hasProperty("xOff?") then
|
||||
theZone.xtriggerOffFlag = theZone:getStringFromZoneProperty( "xOff?", "*<none2>")
|
||||
theZone.xlastTriggerOffValue = theZone:getFlagValue(theZone.xtriggerOffFlag)
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
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)
|
||||
return cfxZones.evalRemainder(theAttribute, theZone)
|
||||
end
|
||||
|
||||
function xFlags.evaluateFlags(theZone)
|
||||
@ -184,7 +138,7 @@ function xFlags.evaluateFlags(theZone)
|
||||
-- since the checksum is order dependent,
|
||||
-- we must preserve the order of the array
|
||||
local flagName = theZone.flagNames[i]
|
||||
currVals[i] = cfxZones.getFlagValue(flagName, theZone)
|
||||
currVals[i] = theZone:getFlagValue(flagName)
|
||||
end
|
||||
|
||||
-- now perform comparison flag by flag
|
||||
@ -194,37 +148,10 @@ function xFlags.evaluateFlags(theZone)
|
||||
local firstChar = string.sub(op, 1, 1)
|
||||
local remainder = string.sub(op, 2)
|
||||
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
|
||||
-- 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
|
||||
-- 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
|
||||
|
||||
local rNum = theZone:evalRemainder(remainder)
|
||||
|
||||
-- this mimics cfxZones.testFlagByMethodForZone method (and is
|
||||
-- the following mimics cfxZones.testFlagByMethodForZone method (and is
|
||||
-- that method's genesis), but is different enough not to invoke that
|
||||
-- method
|
||||
for i = 1, #theZone.flagNames do
|
||||
@ -298,7 +225,6 @@ function xFlags.evaluateFlags(theZone)
|
||||
return 0, ""
|
||||
end
|
||||
if xFlags.verbose and lastHits ~= hits then
|
||||
--trigger.action.outText("+++xF: hit detected for " .. theZone.flagNames[i] .. " in " .. theZone.name .. "(" .. op .. ")", 30)
|
||||
end
|
||||
end
|
||||
return hits, checkSum
|
||||
@ -361,7 +287,7 @@ function xFlags.evaluateZone(theZone)
|
||||
end
|
||||
|
||||
if theZone.xChange then
|
||||
cfxZones.pollFlag(theZone.xChange, theZone.xMethod, theZone)
|
||||
theZone:pollFlag(theZone.xChange, theZone.xMethod)
|
||||
if xFlags.verbose then
|
||||
trigger.action.outText("+++xFlag: change bang! on " .. theZone.xChange .. " for " .. theZone.name, 30)
|
||||
end
|
||||
@ -378,15 +304,15 @@ function xFlags.evaluateZone(theZone)
|
||||
-- true (1)/false(0), no matter if changed or not
|
||||
if theZone.xDirect then
|
||||
if evalResult then
|
||||
cfxZones.setFlagValueMult(theZone.xDirect, 1, theZone)
|
||||
theZone:setFlagValue(theZone.xDirect, 1)
|
||||
else
|
||||
cfxZones.setFlagValueMult(theZone.xDirect, 0, theZone)
|
||||
theZone:setFlagValue(theZone.xDirect, 0)
|
||||
end
|
||||
end
|
||||
|
||||
-- directly set the xCount flag
|
||||
if theZone.xCount then
|
||||
cfxZones.setFlagValueMult(theZone.xCount, hits, theZone)
|
||||
theZone:setFlagValueMult(theZone.xCount, hits)
|
||||
end
|
||||
|
||||
-- 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
|
||||
trigger.action.outText("+++xFlag: success bang! on <" .. theZone.xSuccess .. "> for <" .. theZone.name .. "> with method <" .. theZone.xMethod .. ">", 30)
|
||||
end
|
||||
cfxZones.pollFlag(theZone.xSuccess, theZone.xMethod, theZone)
|
||||
theZone:pollFlag(theZone.xSuccess, theZone.xMethod)
|
||||
theZone.xHasFired = true
|
||||
end
|
||||
end
|
||||
@ -407,14 +333,14 @@ function xFlags.update()
|
||||
|
||||
for idx, theZone in pairs (xFlags.xFlagZones) do
|
||||
-- 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
|
||||
trigger.action.outText("+++xFlg: enabling " .. theZone.name, 30)
|
||||
end
|
||||
theZone.xSuspended = false
|
||||
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
|
||||
trigger.action.outText("+++xFlg: DISabling " .. theZone.name, 30)
|
||||
end
|
||||
@ -428,7 +354,7 @@ function xFlags.update()
|
||||
|
||||
-- see if they should reset
|
||||
if theZone.xReset then
|
||||
local currVal = cfxZones.getFlagValue(theZone.xReset, theZone)
|
||||
local currVal = theZone:getFlagValue(theZone.xReset)
|
||||
if currVal ~= theZone.xLastReset then
|
||||
theZone.xLastReset = currVal
|
||||
if xFlags.verbose or theZone.verbose then
|
||||
@ -446,14 +372,11 @@ function xFlags.readConfigZone()
|
||||
-- note: must match exactly!!!!
|
||||
local theZone = cfxZones.getZoneByName("xFlagsConfig")
|
||||
if not theZone then
|
||||
if xFlags.verbose then
|
||||
trigger.action.outText("***xFlag: NO config zone!", 30)
|
||||
end
|
||||
return
|
||||
theZone = cfxZones.createSimpleZone("xFlagsConfig")
|
||||
end
|
||||
|
||||
xFlags.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
||||
xFlags.ups = cfxZones.getNumberFromZoneProperty(theZone, "ups", 1)
|
||||
xFlags.verbose = theZone.verbose
|
||||
xFlags.ups = theZone:getNumberFromZoneProperty("ups", 1)
|
||||
|
||||
if xFlags.verbose then
|
||||
trigger.action.outText("***xFlg: read config", 30)
|
||||
@ -476,9 +399,6 @@ function xFlags.start()
|
||||
|
||||
-- process RND Zones
|
||||
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
|
||||
xFlags.createXFlagsWithZone(aZone) -- process attribute and add to zone
|
||||
xFlags.addxFlags(aZone) -- remember it
|
||||
@ -509,4 +429,5 @@ end
|
||||
--[[--
|
||||
Additional features:
|
||||
- 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