Version 2.3.8

Usher
This commit is contained in:
Christian Franz 2024-12-05 10:11:24 +01:00
parent 491a9d0838
commit 88026bf851
12 changed files with 23401 additions and 21451 deletions

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -1,5 +1,5 @@
cfxMX = {}
cfxMX.version = "2.2.0"
cfxMX.version = "3.0.0"
cfxMX.verbose = false
--[[--
Mission data decoder. Access to ME-built mission structures
@ -19,8 +19,16 @@ cfxMX.verbose = false
- new isMEPlayer()
- new isMEPlayerGroup()
2.2.0 - new groupCatByName[]
3.0.0 - patch coalition.addGroup() to build unit table for wasUnit
- pre-populate spawnedUnits coa, cat from MX
- spawnedUnitGroupNameByName
--]]--
cfxMX.spawnedUnitCoaByName = {} -- reverse lookup for coas to reconstruct after kill
cfxMX.spawnedUnitCatByName = {} -- reverse lookup fir cat to recon after kill
cfxMX.spawnedUnitGroupNameByName = {}
cfxMX.groupNamesByID = {}
cfxMX.groupIDbyName = {}
cfxMX.unitIDbyName = {}
@ -43,6 +51,7 @@ 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= "",
@ -198,6 +207,15 @@ function cfxMX.createCrossReferences()
local countryID = cntry_data.id
if type(cntry_data) == 'table' then -- filter strings .id and .name
for obj_type_name, obj_type_data in pairs(cntry_data) do
local gCat = -1 -- "illegal"
if obj_type_name == "helicopter" then gCat = 1
elseif obj_type_name == "ship" then gCat = 3
elseif obj_type_name == "plane" then gCat = 0
elseif obj_type_name == "vehicle" then gCat = 2
else -- if obj_type_name == "static" -- what about "cargo"?
gCat = -1 -- just for safety. no cat for static, train, cargo
end
if obj_type_name == "helicopter" or
obj_type_name == "ship" or
obj_type_name == "plane" or
@ -293,6 +311,12 @@ function cfxMX.createCrossReferences()
end -- if unit skill client
end -- if has skill
cfxMX.unitIDbyName[unit_data.name] = unit_data.unitId
if gCat >= 0 then -- pre-populate table
cfxMX.spawnedUnitCoaByName[unit_data.name] = coaNum
cfxMX.spawnedUnitCatByName[unit_data.name] = gCat
end
cfxMX.spawnedUnitGroupNameByName[unit_data.name] = groupName
end -- for all units
local entry = {}
@ -426,6 +450,27 @@ function cfxMX.start()
trigger.action.outText("cfxMX v." .. cfxMX.version .. " started.", 30)
end
--
-- patch coalition.addGroup so we can record all units by name for their coalition
--
coalition.mxAddGroup = coalition.addGroup -- save old
function coalition.addGroup(cty, cat, data) -- patch addGroup to note all spawned units for DCS static switch-a-roo
local g = coalition.mxAddGroup(cty, cat, data)
if not g then return nil end
local coa = coalition.getCountryCoalition(cty)
local units = g:getUnits()
local gName = g:getName()
for idx, u in pairs(units) do
uName = u:getName()
cfxMX.spawnedUnitCoaByName[uName] = coa
cfxMX.spawnedUnitCatByName[uName] = cat
cfxMX.spawnedUnitGroupNameByName[uName] = gName
-- trigger.action.outText("MX: Unit <" .. uName .. "> spawned for coa <" .. coa .. ">", 30)
end
return g
end
-- start
cfxMX.start()

View File

@ -1707,7 +1707,7 @@ end
function cfxZones.doSetFlagValue(theFlag, theValue, theZone)
local zoneName = "<dummy>"
if not theZone then
trigger.action.outText("+++Zne: no zone on setFlagValue", 30) -- mod me for detector
trigger.action.outText("+++Zne: no zone on doSetFlagValue", 30) -- mod me for detector
else
zoneName = theZone.name -- for flag wildcards
end

View File

@ -1,5 +1,5 @@
convoy = {}
convoy.version = "1.2.0"
convoy.version = "1.3.0"
convoy.requiredLibs = {
"dcsCommon",
"cfxZones",
@ -34,6 +34,12 @@ VERSION HISTORY
- removed destination attribute
1.2.0 - convoyMethod
- convoyTriggerMethod
1.3.0 - typo 'destination'
- start & arrival messages only with wpUpdates
- new 'listOwn' config attribute
- convoy destroyed message only if listOwn
- own convoys only listed if listOwn
--]]--
--[[-- CONVOY Structure
@ -544,18 +550,21 @@ function convoy.wpReached(gName, convName, idx, wpNum)
if idx == 1 then
local distk = math.floor(theConvoy.distance / 1000 + 1.5)
local distm = math.floor(0.621371 * theConvoy.distance/1000 + 1)
trigger.action.outTextForCoalition(coa, "Convoy " .. convName .. " has departed from rallying point " .. theZone.froms .. " towards their destination " .. theConvoy.dest .. " (for a total distance of " .. distk .. "km/" .. distm .. "nm).", 30)
trigger.action.outSoundForCoalition(coa, convoy.actionSound)
if theZone.wpUpdates or convoy.listOwn then
trigger.action.outTextForCoalition(coa, "Convoy " .. convName .. " has departed from rallying point " .. theZone.froms .. " towards their destination " .. theConvoy.dest .. " (for a total distance of " .. distk .. "km/" .. distm .. "nm).", 30)
trigger.action.outSoundForCoalition(coa, convoy.actionSound)
end
if convoy.listEnemy then
local msg = "Intelligence reports new enemy convoy " .. theConvoy.anon .. " enroute to " .. theConvoy.destObject:getName()
trigger.action.outTextForCoalition(enemy, msg, 30)
trigger.action.outSoundForCoalition(enemy, convoy.actionSound)
end
elseif idx == wpNum then
trigger.action.outTextForCoalition(coa, "Convoy " .. convName .. " has arrived at desitation (" .. theConvoy.dest .. ").", 30)
trigger.action.outSoundForCoalition(coa, convoy.actionSound)
elseif idx == wpNum then
if theZone.wpUpdates or convoy.listOwn then
trigger.action.outTextForCoalition(coa, "Convoy " .. convName .. " has arrived at destination (" .. theConvoy.dest .. ").", 30)
trigger.action.outSoundForCoalition(coa, convoy.actionSound)
end
if convoy.listEnemy then
local msg = "Enemy convoy " .. theConvoy.anon .. " arrived at " .. theConvoy.destObject:getName()
trigger.action.outTextForCoalition(enemy, msg, 30)
@ -779,7 +788,7 @@ function convoy.doListConvoys(args)
local msg = ""
local hasMsg = false
if #mine > 0 then
if convoy.listOwn and #mine > 0 then
-- report my own convoys with location
hasMsg = true
msg = msg .. "\nRUNNING ALLIED CONVOYS:\n"
@ -915,8 +924,10 @@ function convoy.statusUpdate() -- every 10 seconds
if theZone.deadOut then
theZone:pollFlag(theZone.deadOut, theZone.convoyMethod) -- "inc")
end
trigger.action.outTextForCoalition(theConvoy.coa, "Convoy " .. convName .. " enroute to " .. theConvoy.dest .. " was destroyed.", 30)
trigger.action.outSoundForCoalition(theConvoy.coa, convoy.actionSound)
if convoy.listOwn then
trigger.action.outTextForCoalition(theConvoy.coa, "Convoy " .. convName .. " enroute to " .. theConvoy.dest .. " was destroyed.", 30)
trigger.action.outSoundForCoalition(theConvoy.coa, convoy.actionSound)
end
if convoy.listEnemy then
local enemy = 1
if theConvoy.coa == 1 then enemy = 2 end
@ -976,6 +987,8 @@ function convoy.readConfigZone()
convoy.listEnemy = theZone:getBoolFromZoneProperty("listEnemy", true)
convoy.listNeutral = theZone:getBoolFromZoneProperty("listNeutral", true)
convoy.listOwn = theZone:getBoolFromZoneProperty("listOwn", true)
if theZone:hasProperty("attachTo:") then
local attachTo = theZone:getStringFromZoneProperty("attachTo:", "<none>")
if radioMenu then -- requires optional radio menu to have loaded

View File

@ -1,5 +1,5 @@
dcsCommon = {}
dcsCommon.version = "3.1.3"
dcsCommon.version = "3.1.4"
--[[-- VERSION HISTORY
3.0.0 - removed bad bug in stringStartsWith, only relevant if caseSensitive is false
- point2text new intsOnly option
@ -31,6 +31,8 @@ dcsCommon.version = "3.1.3"
3.1.2 - isTroopCarrier() hardening against DCS sillieness
3.1.3 - new dcsCommon.unitIsOfLegalType() analogue to isTroopCarrier
- new DCS Patch section
3.1.4 - new processStringWildcardsForUnit
- integrated into std wildcard proccing, unit optional
--]]--
-- dcsCommon is a library of common lua functions
@ -3391,7 +3393,7 @@ end
--
-- string wildcards
--
function dcsCommon.processStringWildcards(inMsg)
function dcsCommon.processStringWildcards(inMsg, theUnit)
-- Replace STATIC bits of message like CR and zone name
if not inMsg then return "<nil inMsg>" end
local formerType = type(inMsg)
@ -3401,9 +3403,40 @@ function dcsCommon.processStringWildcards(inMsg)
-- replace line feeds
outMsg = inMsg:gsub("<n>", "\n")
if theUnit then
outMsg = dcsCommon.processStringWildcardsForUnit(outMsg, theUnit)
end
return outMsg
end
function dcsCommon.processStringWildcardsForUnit(msg, theUnit)
local uName = theUnit:getName()
msg = msg:gsub("<u>", uName)
pName = "!AI!"
if dcsCommon.isPlayerUnit(theUnit) then
pName = theUnit:getPlayerName()
else
return
end
msg = msg:gsub("<p>", pName)
msg = msg:gsub("<t>", theUnit:getTypeName())
local theGroup = theUnit:getGroup()
local gName = theGroup:getName()
msg = msg:gsub("<g>", gName)
local coa = "NEUTRAL"; local e = "NOBODY"
local c = theGroup:getCoalition()
if c == 1 then coa = "RED"; e = "BLUE" end
if c == 2 then coa = "BLUE"; e = "RED" end
msg = msg:gsub("<C>", coa)
msg = msg:gsub ("<E>", e)
coa = coa:lower()
e = e:lower()
msg = msg:gsub("<c>", coa)
msg = msg:gsub ("<e>", e)
return msg
end
--
-- phonetic alphabet
--

View File

@ -1,12 +1,12 @@
fireFX = {}
fireFX.version = "2.0.1"
fireFX.version = "2.1.0"
fireFX.verbose = false
fireFX.ups = 1
fireFX.requiredLibs = {
"dcsCommon", -- always
"cfxZones", -- Zones, of course
}
fireFX.fx = {}
fireFX.fx = {} -- the fx zones
--[[--
Version History
@ -16,20 +16,24 @@ fireFX.fx = {}
2.0.0 - dmlZones OOP
- rndLoc
2.0.1 - fixed rndLoc determination
2.1.0 - supports rnd as fire size
- rewrote save, backwards compatible to old
- simplified zone access
--]]--
function fireFX.addFX(theZone)
table.insert(fireFX.fx, theZone)
-- table.insert(fireFX.fx, theZone)
fireFX.fx[theZone.name] = theZone
end
function fireFX.getFXByName(aName)
for idx, aZone in pairs(fireFX.fx) do
if aName == aZone.name then return aZone end
end
if fireFX.verbose then
trigger.action.outText("+++ffx: no fire FX with name <" .. aName ..">", 30)
end
return fireFX.fx[aName]
-- for idx, aZone in pairs(fireFX.fx) do
-- if aName == aZone.name then return aZone end
-- end
-- if fireFX.verbose then
-- trigger.action.outText("+++ffx: no fire FX with name <" .. aName ..">", 30)
-- end
end
--
@ -46,51 +50,34 @@ function fireFX.createFXWithZone(theZone)
if theSize == "L" or theSize == "LARGE" then fxCode = 3 end
if theSize == "H" or theSize == "HUGE" then fxCode = 4 end
if theSize == "XL" then fxCode = 4 end
if theSize == "RND" then fxCode = -1 end -- randomized
local theFire = cfxZones.getBoolFromZoneProperty(theZone, "flames", true)
if theFire then
-- code stays as it is
else
-- smoke only
fxCode = fxCode + 4
end
theZone.fxCode = fxCode
theZone.fxCode = fxCode -- raw, without flame code
if theZone.verbose or fireFX.verbose then
trigger.action.outText("+++ffx: new FX with code = <" .. fxCode .. ">", 30)
end
theZone.fxData = {} -- used when created
theZone.density = theZone:getNumberFromZoneProperty("density", 0.5)
theZone.agl = theZone:getNumberFromZoneProperty("AGL", 0)
theZone.min, theZone.max = theZone:getPositiveRangeFromZoneProperty("num", 1, 1)
if theZone:hasProperty("start?") then
theZone.fxStart = theZone:getStringFromZoneProperty("start?", "*<none>")
theZone.fxLastStart = theZone:getFlagValue(theZone.fxStart)
end
if theZone:hasProperty("stop?") then
theZone.fxStop = theZone:getStringFromZoneProperty("stop?", "*<none>")
theZone.fxLastStop = theZone:getFlagValue(theZone.fxStop)
end
theZone.fxOnStart = theZone:getBoolFromZoneProperty("onStart", false)
theZone.burning = false
if not theZone.fxOnStart and not theZone.fxStart then
trigger.action.outText("+++ffx: WARNING - fireFX Zone <" .. theZone.name .. "> can't be started, neither onStart nor 'start?' defined", 30)
end
-- output method (not needed)
-- trigger method
theZone.fxTriggerMethod = theZone:getStringFromZoneProperty( "fxTriggerMethod", "change")
if theZone:hasProperty("triggerMethod") then
theZone.fxTriggerMethod = theZone:getStringFromZoneProperty( "triggerMethod", "change")
end
end
theZone.rndLoc = theZone:getBoolFromZoneProperty("rndLoc", false)
if theZone.max > 1 and (not theZone.rndLoc) then
if theZone.verbose or fireFX.verbose then
@ -98,7 +85,6 @@ function fireFX.createFXWithZone(theZone)
end
theZone.rndLoc = true
end
if fireFX.verbose or theZone.verbose then
trigger.action.outText("+++ffx: new FX <".. theZone.name ..">", 30)
end
@ -111,6 +97,7 @@ end
function fireFX.startTheFire(theZone)
if not theZone.burning then
theZone.fireNames = {}
theZone.fxData = {}
local num = cfxZones.randomInRange(theZone.min, theZone.max)
for i = 1, num do
local p = theZone:getPoint()
@ -119,10 +106,22 @@ function fireFX.startTheFire(theZone)
end
p.y = land.getHeight({x = p.x, y = p.z}) + theZone.agl
local preset = theZone.fxCode
-- process randomization
if preset < 1 then
preset = math.random(4) -- 1..4
end
if theZone:getBoolFromZoneProperty("flames", true) then
--preset = preset
--trigger.action.outText("fire <" .. i .. "> with flame: <" .. preset .. ">", 30)
else
preset = preset + 4 -- smoke only
--trigger.action.outText("no flames for fire <" .. i .. ">: <" .. preset .. ">", 30)
end -- support for 'rnd' as bool
local density = theZone.density
local fireName = dcsCommon.uuid(theZone.name)
trigger.action.effectSmokeBig(p, preset, density, fireName)
theZone.fireNames[i] = fireName
theZone.fxData[i] = {p, preset, density, fireName}
end
theZone.burning = true
end
@ -134,6 +133,8 @@ function fireFX.extinguishFire(theZone)
trigger.action.effectSmokeStop(aFireName)
end
theZone.burning = false
theZone.fireNames = {}
theZone.fxData = {}
end
end
@ -170,7 +171,7 @@ function fireFX.saveData()
local theName = theFX.name
local FXData = {}
FXData.burning = theFX.burning
fxData.data = theFX.fxData
allFX[theName] = FXData
end
theData.allFX = allFX
@ -196,10 +197,29 @@ function fireFX.loadData()
end
for theName, theData in pairs(allFX) do
local theFX = fireFX.getFXByName(theName)
local theFX = fireFX.getFXByName(theName) -- get fx zone
if theFX then
if theData.burning then
fireFX.startTheFire(theFX)
if theData.data then
-- we have new save data, replicate flame fx
theZone.fireNames = {}
theZone.fxData = {}
for idx, fxData in pairs(theData.data) do
local p = fxData[1]
local preset = fxData[2]
local density = fxData[3]
local fireName = dcsCommon.uuid(theZone.name)
trigger.action.effectSmokeBig(p, preset, density, fireName)
theZone.fireNames[idx] = fireName
theZone.fxData[idx] = {p, preset, density, fireName}
end
else
fireFX.startTheFire(theFX) -- old save data
end
else
theZone.fireNames = {}
theZone.fxData = {}
theZone.burning = false
end
theFX.inited = true -- ensure no onStart overwrite
else

View File

@ -1,9 +1,6 @@
cfxPlayerScore = {}
cfxPlayerScore.version = "4.0.0"
cfxPlayerScore.version = "5.0.0"
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
3.0.0 - dmlFlags OOP
@ -20,7 +17,10 @@ cfxPlayerScore.firstSave = true -- to force overwrite
- cleanup
4.0.0 - own event handling, disco from dcsCommon
- early landing detection (unitSpawnTime)
5.0.0 - resolve killed units via cfxMX to patch DCS error
- reworked unit2score to use MX
- code cleanup
TODO: Kill event no longer invoked for map objetcs, attribute
to faction now, reverse invocation direction with PlayerScore
--]]--
@ -272,11 +272,12 @@ function cfxPlayerScore.object2score(inVictim, killSide) -- does not have group
end
function cfxPlayerScore.unit2score(inUnit)
local vicGroup = inUnit:getGroup()
local vicCat = vicGroup:getCategory()-- group cat, not 2.9 affected
local vicName = "*?*"
if inUnit.getName then vicName = inUnit:getName() end
-- local vicGroup = inUnit:getGroup()
local vicCat = cfxMX.spawnedUnitCatByName[vicName] -- now using MX
local vicType = inUnit:getTypeName()
local vicName
if inUnit.getName then vicName = inUnit:getName() else vicName = "*?*" end
if type(vicName) == "number" then vicName = tostring(vicName) end
-- simply extend by adding items to the typescore table.concat
@ -284,8 +285,8 @@ function cfxPlayerScore.unit2score(inUnit)
-- named hi-value targets to have individual scores
local uScore = cfxPlayerScore.typeScore[vicName:upper()]
-- see if all members of group score
if (not uScore) and vicGroup then
local grpName = vicGroup:getName()
if (not uScore) then -- and vicGroup then
local grpName = cfxMX.spawnedUnitGroupNameByName[vicName]--vicGroup:getName()
uScore = cfxPlayerScore.typeScore[grpName:upper()]
end
if not uScore then
@ -520,7 +521,6 @@ function cfxPlayerScore.scoreTextForAllPlayers(ranked)
theText = theText .. " (No score yet)\n"
end
if cfxPlayerScore.reportCoalition then
--theText = theText .. "\n"
theText = theText .. "\nRED total: " .. cfxPlayerScore.coalitionScore[1]
theText = theText .. "\nBLUE total: " .. cfxPlayerScore.coalitionScore[2]
end
@ -585,156 +585,32 @@ function cfxPlayerScore.unlinkUnit(theUnit)
local uName = theUnit:getName()
cfxPlayerScore.unit2player[uName] = nil
end
--[[--
function cfxPlayerScore.preProcessor(theEvent)
-- return true if the event should be processed
-- by us
if theEvent.initiator == nil then
return false
end
if cfxPlayerScore.verbose then
trigger.action.outText("Event preproc: " .. theEvent.id .. " (" .. dcsCommon.event2text(theEvent.id) .. ")", 30)
if theEvent.id == 8 or theEvent.id == 30 then -- dead or lost event
local who = theEvent.initiator
local name = "(nil ini)"
if who then
name = "(inval object)"
if who.getName then name = who:getName() end
end
trigger.action.outText("Dead/Lost subject: <" .. name .. ">", 30)
end
if theEvent.id == 2 then -- hit
local who = theEvent.initiator
local name = "(nil ini)"
if who then
name = "(inval initi)"
if who.getName then name = who:getName() end
if not name then -- WTF??? could be a weapon
name = "!nil getName!"
if who.getTypeName then name = who:getTypeName() end
if not name then
name = "WTFer"
end
end
end
local hit = theEvent.object
local hname = "(nil ini)"
if hit then
hname = "(inval object)"
if hit.getName then hname = hit:getName() end
end
trigger.action.outText("o:<" .. name .. "> hit <" .. hname .. ">", 30)
end
end
-- check if this was FORMERLY a player plane
local theUnit = theEvent.initiator
if not theUnit.getName then return end -- fix for DCS update bug
local uName = theUnit:getName()
if cfxPlayerScore.unit2player[uName] then
-- this requires special IMMEDIATE handling when event is
-- one of the below
if theEvent.id == 5 or -- crash
theEvent.id == 8 or -- dead
theEvent.id == 9 or -- pilot_dead
theEvent.id == 30 or -- unit loss
theEvent.id == 6 then -- eject
-- these can lead to a pilot demerit
-- event does NOT have a player
cfxPlayerScore.handlePlayerDeath(theEvent)
return false
end
end
-- initiator must be player
if not theUnit.getPlayerName or
not theUnit:getPlayerName() then
return false
end
if theEvent.id == 28 then
-- we only are interested in kill events where
-- there is a target
local killer = theEvent.initiator
if theEvent.target == nil then
if cfxPlayerScore.verbose then
trigger.action.outText("+++scr pre: nil TARGET", 30)
end
return false
end
-- if there are kill zones, we filter all kills that happen outside of kill zones
if #cfxPlayerScore.killZones > 0 then
local pLoc = theUnit:getPoint()
local tLoc = theEvent.target:getPoint()
local isIn, percent, dist, theZone = cfxZones.pointInOneOfZones(tLoc, cfxPlayerScore.killZones)
if not isIn then
if cfxPlayerScore.verbose then
trigger.action.outText("+++pScr: kill detected, but target <" .. theEvent.target:getName() .. "> was outside of any kill zones", 30)
end
return false
end
if theZone.duet and not cfxZones.pointInZone(pLoc, theZone) then
-- player must be in same zone but was not
if cfxPlayerScore.verbose then
trigger.action.outText("+++pScr: kill detected, but player <" .. theUnit:getPlayerName() .. "> was outside of kill zone <" .. theZone.name .. ">", 30)
end
return false
end
end
return true
end
-- birth event for players initializes score if
-- not existed, and nils the queue
if theEvent.id == 15 then
-- player birth
-- link player and unit
cfxPlayerScore.linkUnitWithPlayer(theUnit)
return true
end
-- take off. overwrites timestamp for last landing
-- so a blipping t/o does nor count. Pre-proc only
if theEvent.id == 3 or theEvent.id == 54 then
local now = timer.getTime()
local playerName = theUnit:getPlayerName()
cfxPlayerScore.lastPlayerLanding[playerName] = now -- overwrite
return false
end
-- landing can score. but only the first landing in x seconds
-- landing in safe zone promotes any queued scores to
-- permanent if enabled, then nils queue
if theEvent.id == 4 or theEvent.id == 55 then
-- player landed. filter multiple landed events
local now = timer.getTime()
local playerName = theUnit:getPlayerName()
local lastLanding = cfxPlayerScore.lastPlayerLanding[playerName]
cfxPlayerScore.lastPlayerLanding[playerName] = now -- overwrite
if lastLanding and lastLanding + cfxPlayerScore.delayBetweenLandings > now then
if cfxPlayerScore.verbose then
trigger.action.outText("+++pScr: Player <" .. playerName .. "> touch-down ignored: too soon after last.", 30)
trigger.action.outText("now is <" .. now .. ">, between is <" .. cfxPlayerScore.delayBetweenLandings .. ">, last + between is <" .. lastLanding + cfxPlayerScore.delayBetweenLandings .. ">", 30)
end
-- filter this event
return false
end
return true
end
return false
end
--]]--
--[[--
function cfxPlayerScore.postProcessor(theEvent)
-- don't do anything
end
--]]--
function cfxPlayerScore.isStaticObject(theUnit)
if not theUnit.getGroup then return true end
if not theUnit.getGroup then
if cfxPlayerScore.verbose then trigger.action.outText("isStatic: no <getGroup>", 30) end
return true
end
local aGroup = theUnit:getGroup()
if aGroup then return false end
if aGroup then
if cfxPlayerScore.verbose then trigger.action.outText("isStatic: returned group, all fine", 30) end
return false
end
-- now check if this WAS a unit, but has been turned to
-- a non-grouped static by DCS
if theUnit.getName and theUnit:getName() then
local uName = theUnit:getName()
if cfxMX.spawnedUnitCoaByName[uName] then
if cfxPlayerScore.verbose then trigger.action.outText("MX resolve for former unit, now static!", 30) end
return false
end
end
if cfxPlayerScore.verbose then trigger.action.outText("has getGroup method, returned none", 30) end
if cfxPlayerScore.verbose and theUnit.getName and theUnit:getName() then
trigger.action.outText("unit <" .. theUnit:getName() .. "> has getGroup method, returned none", 30)
end
return true
end
@ -789,12 +665,16 @@ function cfxPlayerScore.killDetected(theEvent)
end
return
end
-- was it fratricide?
-- if we get here, it CANT be a scenery object
-- but can be a static object, and stO have a coalition
local vicSide = victim:getCoalition()
local fraternicide = (killSide == vicSide)
local neutralKill = (vicSide == 0) -- neutral is 0
if cfxPlayerScore.verbose then
if fraternicide then trigger.action.outText("Fratricide detected.", 30) end
if neutralKill then trigger.action.outText("NEUTRAL KILL detected.", 30) end
end
local vicDesc = victim:getTypeName()
local scoreMod = 1 -- start at one
@ -803,6 +683,7 @@ function cfxPlayerScore.killDetected(theEvent)
local isStO = cfxPlayerScore.isStaticObject(victim)
--if not victim.getGroup then
if isStO then
if cfxPlayerScore.verbose then trigger.action.outText("Static object detected.", 30) end
-- static objects have no group
local staticName
if victim.getName then staticName = victim:getName() -- on statics, this returns
@ -812,6 +693,7 @@ function cfxPlayerScore.killDetected(theEvent)
if staticScore > 0 then
-- this was a named static, return the score - unless our own
-- we IGNORE neutral object kills here
if fraternicide then
scoreMod = cfxPlayerScore.ffMod * scoreMod -- blue on blue static kill
trigger.action.outSoundForCoalition(killSide, cfxPlayerScore.badSound)
@ -830,16 +712,24 @@ function cfxPlayerScore.killDetected(theEvent)
return
end
local vicGroup = victim:getGroup()
if not vicGroup then
local vicGroup = nil
local vicCat = nil
if victim.getGroup then vicGroup = victim:getGroup() end
if not vicGroup and victim.getName and victim:getName() then
vicCat = cfxMX.spawnedUnitCatByName[victim:getName()]
if cfxPlayerScore.verbose then trigger.action.outText("re-constitued cat for group", 30) end
else
if vicGroup.getCategory then vicCat = vicGroup:getCategory() end
end
if not vicCat then
trigger.action.outText("+++scr: strange stuff:group, outta here", 30)
return
end
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
end
-- 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
-- end
local unitScore = cfxPlayerScore.unit2score(victim)
if pk then -- player kill - add player's name
vicDesc = victim:getPlayerName() .. " in " .. vicDesc
@ -848,12 +738,22 @@ function cfxPlayerScore.killDetected(theEvent)
-- if fratricide, times ffMod (friedlyFire)
if fraternicide then
scoreMod = scoreMod * cfxPlayerScore.ffMod ---2
scoreMod = scoreMod * cfxPlayerScore.ffMod -- -2
if cfxPlayerScore.announcer then
trigger.action.outTextForCoalition(killSide, killerName .. " in " .. killVehicle .. " killed FRIENDLY " .. vicDesc .. "!", 30)
trigger.action.outSoundForCoalition(killSide, cfxPlayerScore.badSound)
end
else
elseif neutralKill then
if cfxPlayerScore.verbose then trigger.action.outText("Will apply neutral mod: " .. cfxPlayerScore.nMod, 30) end
scoreMod = scoreMod * cfxPlayerScore.nMod -- neutral mod
local neuStat = ""
if cfxPlayerScore.nMod < 1 then neuStat = " ILLEGALLY" end
if cfxPlayerScore.announcer then
trigger.action.outTextForCoalition(killSide, killerName .. " in " .. killVehicle .. neuStat.. " killed NEUTRAL " .. vicDesc .. "!", 30)
trigger.action.outSoundForCoalition(killSide, cfxPlayerScore.badSound)
-- no individual logging of kill
end
else
if cfxPlayerScore.announcer then
trigger.action.outText(killerName .. " in " .. killVehicle .." killed " .. vicDesc .. "!", 30)
trigger.action.outSoundForCoalition(vicSide, cfxPlayerScore.badSound)
@ -874,7 +774,7 @@ function cfxPlayerScore.killDetected(theEvent)
-- if the score is negative, awardScoreTo will automatically
-- make it immediate, else depending on deferred
cfxPlayerScore.awardScoreTo(killSide, totalScore, killerName)
if not fraternicide then
if not fraternicide or neutralKill then
-- only award kill feats for kills of the enemy
cfxPlayerScore.checkKillFeat(killerName, killer, victim, false)
end
@ -1200,7 +1100,6 @@ function cfxPlayerScore.isScoreEvent(theEvent)
-- link player and unit
cfxPlayerScore.linkUnitWithPlayer(theUnit)
cfxPlayerScore.unitSpawnTime[uName] = timer.getTime() -- to detect 'early landing'
-- trigger.action.outText("Birth event", 30)
return true
end
@ -1228,10 +1127,9 @@ function cfxPlayerScore.isScoreEvent(theEvent)
now - cfxPlayerScore.unitSpawnTime[uName] < 10
then
cfxPlayerScore.lastPlayerLanding[playerName] = now -- just for the sake of it
-- trigger.action.outText("(DCS early landing bug ignored)", 30)
return false
end
-- trigger.action.outText("Time since spawn: " .. now - cfxPlayerScore.unitSpawnTime[uName], 30)
local lastLanding = cfxPlayerScore.lastPlayerLanding[playerName]
cfxPlayerScore.lastPlayerLanding[playerName] = now -- overwrite
if lastLanding and lastLanding + cfxPlayerScore.delayBetweenLandings > now then
@ -1311,14 +1209,11 @@ function cfxPlayerScore.readConfigZone(theZone)
cfxPlayerScore.landing = theZone:getNumberFromZoneProperty("landing", 0) -- if > 0 then feat
cfxPlayerScore.pkMod = theZone:getNumberFromZoneProperty( "pkMod", 1) -- factor for killing a player
cfxPlayerScore.ffMod = theZone:getNumberFromZoneProperty( "ffMod", -2) -- factor for friendly fire
cfxPlayerScore.nMod = theZone:getNumberFromZoneProperty("nMod", 1) -- factor for neutral kill. Should be -100, defaults to 1
cfxPlayerScore.planeLoss = theZone:getNumberFromZoneProperty("planeLoss", -10) -- points added when player's plane crashes
cfxPlayerScore.announcer = theZone:getBoolFromZoneProperty("announcer", true)
if theZone:hasProperty("badSound") then
cfxPlayerScore.badSound = theZone:getStringFromZoneProperty("badSound", "<nosound>")
end
if theZone:hasProperty("scoreSound") then
cfxPlayerScore.scoreSound = theZone:getStringFromZoneProperty("scoreSound", "<nosound>")
end
cfxPlayerScore.badSound = theZone:getStringFromZoneProperty("badSound", "Death BRASS.wav")
cfxPlayerScore.scoreSound = theZone:getStringFromZoneProperty("scoreSound", "Quest Snare 3.wav")
-- triggering saving scores
if theZone:hasProperty("saveScore?") then
cfxPlayerScore.saveScore = theZone:getStringFromZoneProperty("saveScore?", "none")
@ -1614,10 +1509,6 @@ function cfxPlayerScore.start()
trigger.action.outText("+++pScr: WARNING - deferred scoring active but no 'scoreSafe' zones set!", 30)
end
-- subscribe to events and use dcsCommon's handler structure
-- dcsCommon.addEventHandler(cfxPlayerScore.handlePlayerEvent,
-- cfxPlayerScore.preProcessor,
-- cfxPlayerScore.postProcessor)
world.addEventHandler(cfxPlayerScore)
-- now load all save data and populate map with troops that
-- we deployed last save.

View File

@ -1,5 +1,5 @@
cfxReconMode = {}
cfxReconMode.version = "2.3.1"
cfxReconMode.version = "2.4.0"
cfxReconMode.verbose = false -- set to true for debug info
cfxReconMode.reconSound = "UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav" -- to be played when somethiong discovered
@ -62,7 +62,12 @@ VERSION HISTORY
- clean-up
2.3.0 - support for towns/twn when present
2.3.1 - simplified reading config
2.4.0 - added "ground" and "naval" attributes
- SALT diffs between vehicles and vessels
- SALT "s" for plural if > 1 vehicle/vessel
- de-optimized naval visibility check
- optimization for group check if seen before
- new bearing and distance callout from pilot
--]]--
cfxReconMode.detectionMinRange = 3000 -- meters at ground level
@ -268,12 +273,13 @@ function cfxReconMode.canDetect(scoutPos, theGroup, visRange)
-- determine if a member of theGroup can be seen from
-- scoutPos at visRange
-- returns true and pos when detected
local cat = theGroup:getCategory()
local allUnits = theGroup:getUnits()
for idx, aUnit in pairs(allUnits) do
if Unit.isExist(aUnit) and aUnit:isActive() and aUnit:getLife() >= 1 then
local uPos = aUnit:getPoint()
uPos.y = uPos.y + 3 -- raise my 3 meters
local d = dcsCommon.distFlat(scoutPos, uPos)
local d = math.floor(dcsCommon.distFlat(scoutPos, uPos))
if d < visRange then
-- is in visual range. do we have LOS?
if land.isVisible(scoutPos, uPos) then
@ -285,7 +291,11 @@ function cfxReconMode.canDetect(scoutPos, theGroup, visRange)
-- detect range, we assume that entire group
-- is, since they are bunched together
-- edge cases may get lucky tests
return false, nil
-- only for land units, not naval since they are
-- usually dispersed
if cat == 2 then
return false, nil
end
end
end
end
@ -329,6 +339,7 @@ function cfxReconMode.removeMarkForArgs(args)
end
function cfxReconMode.getSit(theGroup)
local cat = theGroup:getCategory()
local msg = ""
-- analyse the group we just discovered. We know it's a ground troop, so simply differentiate between vehicles and infantry
local theUnits = theGroup:getUnits()
@ -343,13 +354,16 @@ function cfxReconMode.getSit(theGroup)
end
if numInf > 0 and numVehicles > 0 then
-- mixed infantry and vehicles
msg = numInf .. " infantry and " .. numVehicles .. " vehicles"
msg = numInf .. " infantry and " .. numVehicles
if cat == 2 then msg = msg .. " vehicles" else msg = msg .. " vessels" end
elseif numInf > 0 then
-- only infantry
msg = numInf .. " infantry"
else
-- only vehicles
msg = numVehicles .. " vehicles"
msg = numVehicles --.. " vehicles"
if cat == 2 then msg = msg .. " vehicle" else msg = msg .. " vessel" end
if numVehicles > 1 then msg = msg .. "s" end
end
return msg
end
@ -422,9 +436,19 @@ function cfxReconMode.getTimeData()
end
function cfxReconMode.generateSALT(theScout, theGroup)
local msg = theScout:getName() .. " reports new ground contact"
local cat = theGroup:getCategory() -- 2 (gnd) or 3 (naval)
local msg = theScout:getName() .. " reports new "
if cat == 2 then msg = msg .. "ground contact" else msg = msg .. "surface contact" end
if cfxReconMode.groupNames then msg = msg .. " " .. theGroup:getName() end
msg = msg .. ":\n"
-- at bearing and dist
local p = theScout:getPoint()
local theUnit = dcsCommon.getFirstLivingUnit(theGroup)
local up = theUnit:getPoint()
-- local d = math.floor(dcsCommon.dist(p, up)/1000)
local dg = math.floor(dcsCommon.distFlat(p, up)/1000)
local b = dcsCommon.bearingInDegreesFromAtoB(p, up)
msg = msg .. ", bearing " .. b .. "°, " .. dg .. "km:\n"
-- msg = msg .. ":\n"
-- SALT: S = Situation or number of units A = action they are doing L = Location T = Time
msg = msg .. cfxReconMode.getSit(theGroup) .. ", "-- S
msg = msg .. cfxReconMode.getAction(theGroup) .. ", " -- A
@ -490,7 +514,7 @@ function cfxReconMode.detectedGroup(mySide, theScout, theGroup, theLoc)
local zInfo = cfxReconMode.zoneInfo[gName]
silent = zInfo.silent
end
-- put a mark on the map
if (not silent) and cfxReconMode.applyMarks then
local theID = cfxReconMode.placeMarkForUnit(theLoc, mySide, theGroup)
@ -564,38 +588,40 @@ function cfxReconMode.performReconForUnit(theScout)
if not theScout then return end
if not theScout:isExist() then return end -- will be gc'd soon
-- get altitude above ground to calculate visual range
local alt = dcsCommon.getUnitAGL(theScout)
local visRange = dcsCommon.lerp(cfxReconMode.detectionMinRange, cfxReconMode.detectionMaxRange, alt/cfxReconMode.maxAlt)
local alt = math.floor(dcsCommon.getUnitAGL(theScout))
local visRange = math.floor(dcsCommon.lerp(cfxReconMode.detectionMinRange, cfxReconMode.detectionMaxRange, alt/cfxReconMode.maxAlt))
local scoutPos = theScout:getPoint()
-- figure out which groups we are looking for
local myCoal = theScout:getCoalition()
local enemyCoal = 1
if myCoal == 1 then enemyCoal = 2 end
-- trigger.action.outText("scout <" .. theScout:getName() .."> at alt <" .. alt .. "> has visRange <" .. visRange .. ">", 30)
-- iterate all enemy units until we find one
-- and then stop this iteration (can only detect one
-- group per pass)
local enemyGroups = coalition.getGroups(enemyCoal)
for idx, theGroup in pairs (enemyGroups) do
-- make sure it's a ground unit
local isGround = theGroup:getCategory() == 2
if theGroup:isExist() and isGround then
local cat = theGroup:getCategory()
local isGround = cfxReconMode.rGround and cat == 2
local isNaval = cfxReconMode.rNaval and cat == 3
local found = isGround or isNaval
local groupName = theGroup:getName()
found = found and (not cfxReconMode.detectedGroups[groupName]) -- optimization: skip if already detected
if found then
local visible, location = cfxReconMode.canDetect(scoutPos, theGroup, visRange)
if visible then
-- see if we already detected this one
local groupName = theGroup:getName()
if cfxReconMode.detectedGroups[groupName] == nil then
-- only now check against blackList
local inList, gName = cfxReconMode.isStringInList(groupName, cfxReconMode.blackList)
if not inList then
-- visible and not yet seen
-- perhaps add some percent chance now
-- remember that we know this group
cfxReconMode.detectedGroups[groupName] = theGroup
cfxReconMode.detectedGroup(myCoal, theScout, theGroup, location)
return -- stop, as we only detect one group per pass
end
end
-- blacklist check
local inList, gName = cfxReconMode.isStringInList(groupName, cfxReconMode.blackList)
if not inList then
-- visible, not yet seen, not blacklisted
-- perhaps add some percent chance now
-- remember that we know this group
cfxReconMode.detectedGroups[groupName] = theGroup
cfxReconMode.detectedGroup(myCoal, theScout, theGroup, location)
return -- stop, as we only detect one group per pass
end
end
end
end
@ -1000,6 +1026,8 @@ function cfxReconMode.readConfigZone()
cfxReconMode.imperialUnits = theZone:getBoolFromZoneProperty( "imperialUnits", false)
end
cfxReconMode.groupNames = theZone:getBoolFromZoneProperty( "groupNames", true)
cfxReconMode.rGround = theZone:getBoolFromZoneProperty("ground", true)
cfxReconMode.rNaval = theZone:getBoolFromZoneProperty("naval", false)
cfxReconMode.theZone = theZone -- save this zone
end

259
modules/usher.lua Normal file
View File

@ -0,0 +1,259 @@
usher = {}
usher.version = "1.0.0"
usher.requiredLibs = {
"dcsCommon", -- always
"cfxZones", -- Zones, of course
}
usher.players = {} -- dicts by name, holds a number
usher.types = {}
usher.units = {}
usher.groups = {}
usher.coas = {}
usher.lastPNum = 0
function usher.incFlag(name)
cfxZones.pollFlag(name, "inc", usher) -- support multiple flags and local
local v = trigger.misc.getUserFlag(name)
if usher.verbose then
trigger.action.outText("+++Ush: banged <" .. name .. "> with <" .. v .. ">", 30)
end
end
-- wildcard proccing
function usher.processStringWildcardsForUnit(inMsg, theUnit)
return dcsCommon.processStringWildcards(inMsg, theUnit)
--[[--
local msg = dcsCommon.processStringWildcards(inMsg)
local uName = theUnit:getName()
msg = msg:gsub("<u>", uName)
pName = "!AI!"
if dcsCommon.isPlayerUnit(theUnit) then
pName = theUnit:getPlayerName()
else
return
end
msg = msg:gsub("<p>", pName)
msg = msg:gsub("<t>", theUnit:getTypeName())
local theGroup = theUnit:getGroup()
local gName = theGroup:getName()
msg = msg:gsub("<g>", gName)
local coa = "NEUTRAL"; local e = "NOBODY"
local c = theGroup:getCoalition()
if c == 1 then coa = "RED"; e = "BLUE" end
if c == 2 then coa = "BLUE"; e = "RED" end
msg = msg:gsub("<C>", coa)
msg = msg:gsub ("<E>", e)
coa = coa:lower()
e = e:lower()
msg = msg:gsub("<c>", coa)
msg = msg:gsub ("<e>", e)
return msg
--]]--
end
-- event handler
function usher:onEvent(theEvent)
if not theEvent then return end
if theEvent.id == 15 then
local theUnit = theEvent.initiator
if not theUnit then return end
if not theUnit.getGroup then return end
if not theUnit.getName then return end
if not theUnit.getPlayerName then return end
local pName = theUnit:getPlayerName()
if not pName then return end
-- when we get here, we have a player
local uName = theUnit:getName()
local theGroup = theUnit:getGroup()
local gID = theGroup:getID()
local uID = theUnit:getID()
local gName = theGroup:getName()
local uType = theUnit:getTypeName()
local coa = theGroup:getCoalition()
local f
-- now see what events to generate if set up that way
if usher.coas[coa] then usher.coas[coa] = usher.coas[coa] + 1
else usher.coas[coa] = 1 end
-- separation for future expansion
if usher.coaEvent and usher.coas[coa] == 1 then -- only first
-- bang that flag, but only first time
f = usher.coaNeutralFlag
if coa == 1 then f = usher.coaRedFlag elseif coa == 2 then f = coaBlueFlag end
local msg = usher.processStringWildcardsForUnit(usher.coaMsg, theUnit)
usher.incFlag(f)
if #msg > 9 then -- empty message will suppress entirely
trigger.action.outTextForCoalition(coa, msg, 30)
end
end
if usher.players[pName] then usher.players[pName] = usher.players[pName] + 1 else usher.players[pName] = 1 end
if usher.playerEvent and usher.players[pName] == 1 then
local msg = usher.processStringWildcardsForUnit(usher.playerMsg, theUnit)
if #msg > 0 then
trigger.action.outTextForUnit(uID, msg, 30)
end
f = usher.prefix .. pName
usher.incFlag(f)
usher.incFlag(usher.playerCommand)
trigger.action.outSoundForUnit(uID, usher.playerSound)
end
if usher.units[uName] then usher.units[uName] = usher.units[uName] + 1 else usher.units[uName] = 1 end
if usher.unitEvent and usher.units[uName] == 1 then
local msg = usher.processStringWildcardsForUnit(usher.unitMsg, theUnit)
if #msg>0 then
trigger.action.outTextForUnit(uID, msg, 30)
end
f = usher.prefix .. uName
usher.incFlag(f)
end
if usher.groups[gName] then usher.groups[gName] = usher.groups[gName] + 1 else usher.groups[gName] = 1 end
if usher.groupEvent and usher.groups[gName] == 1 then
local msg = usher.processStringWildcardsForUnit(usher.groupMsg, theUnit)
if #msg > 0 then
trigger.action.outTextForGroup(gID, msg, 30)
end
f = usher.prefix .. gName
usher.incFlag(f)
end
if usher.types[uType] then usher.types[uType] = usher.types[uType] + 1 else usher.types[uType] = 1 end
if usher.typeEvent and usher.types[uType] == 1 then
local msg = usher.processStringWildcardsForUnit(usher.typeMsg, theUnit)
if #msg > 0 then
trigger.action.outTextForCoalition(coa, msg, 30)
end
f = usher.prefix .. uType
usher.incFlag(f)
end
end
end
-- config
function usher.readConfigZone()
-- note: must match exactly!!!!
local theZone = cfxZones.getZoneByName("usherConfig")
if not theZone then
theZone = cfxZones.createSimpleZone("usherConfig")
end
usher.coaMsg = theZone:getStringFromZoneProperty("coaMsg", "Welcome, <p>, your <t> is the first to join <c>.")
usher.coaEvent = theZone:getBoolFromZoneProperty("coaEvent", false)
usher.coaRedFlag = theZone:getStringFromZoneProperty("coaRed!", "cfxxx")
usher.coaBlueFlag = theZone:getStringFromZoneProperty("coaBlue!", "cfxxx")
usher.coaNeutralFlag = theZone:getStringFromZoneProperty("coaNeutral!", "cfxxx")
usher.playerEvent = theZone:getBoolFromZoneProperty("playerEvent", true)
usher.playerMsg = theZone:getStringFromZoneProperty("playerMsg", "Welcome <p>!")
usher.playerSound = theZone:getStringFromZoneProperty("playerSound", "none")
usher.playerCommand = theZone:getStringFromZoneProperty("player!", "cfxNone")
usher.unitEvent = theZone:getBoolFromZoneProperty("unitEvent", false)
usher.unitMsg = theZone:getStringFromZoneProperty("unitMsg", "Welcome <p>, you are <u>, part of <c> <g> (a flight of <t>).")
usher.groupEvent = theZone:getBoolFromZoneProperty("groupEvent", false)
usher.groupMsg = theZone:getStringFromZoneProperty("groupMsg", "Welcome <p>, you are part of <c> <g> (a flight of <t>).")
usher.typeEvent = theZone:getBoolFromZoneProperty("typeEvent", false)
usher.typeMsg = theZone:getStringFromZoneProperty("typeMsg", "Welcome to your <t>, <p>!")
usher.prefix = theZone:getStringFromZoneProperty("prefix", "u:")
usher.redNum = theZone:getStringFromZoneProperty("red#", "cfxNone")
usher.blueNum = theZone:getStringFromZoneProperty("blue#", "cfxNone")
usher.neuNum = theZone:getStringFromZoneProperty("neutral#", "cfxNone")
usher.pNum = theZone:getStringFromZoneProperty("pNum#", "cfxNone")
usher.pJoin = theZone:getStringFromZoneProperty("join!", "cfxNone")
usher.pLeave = theZone:getStringFromZoneProperty("leave!", "cfxNone")
usher.ups = theZone:getNumberFromZoneProperty("ups", 0.1) -- every 10 secs
usher.method = theZone:getStringFromZoneProperty("method", "inc")
usher.verbose = theZone.verbose
usher.name = "usherConfig"
end
-- update
function usher.update()
timer.scheduleFunction(usher.update, nil, timer.getTime() + 1/usher.ups)
-- count players per side and set # outputs
local numPlayers = coalition.getPlayers(2)
local total = #numPlayers
cfxZones.setFlagValue(usher.blueNum, #numPlayers, usher)
numPlayers = coalition.getPlayers(1)
total = total + #numPlayers
cfxZones.setFlagValue(usher.redNum, #numPlayers, usher)
numPlayers = coalition.getPlayers(0)
cfxZones.setFlagValue(usher.neuNum, #numPlayers, usher)
total = total + #numPlayers
cfxZones.setFlagValue(usher.pNum, total, usher)
if total < usher.lastPNum then
cfxZones.pollFlag(usher.pLeave, usher.method, usher)
elseif total > usher.lastPNum then
cfxZones.pollFlag(usher.pJoin, usher.method, usher)
end
usher.lastPNum = total
end
-- load/save
function usher.saveData()
local theData = {}
theData.players = usher.players
theData.types = usher.types
thaData.units = usher.units
theData.groups = usher.groups
theData.coas = usher.coas
return theData
end
function usher.loadData()
if not persistence then return end
local theData = persistence.getSavedDataForModule("usher")
if not theData then
if usher.verbose then
trigger.action.outText("+++ush Persistence: no save data received, skipping.", 30)
end
return
end
usher.players = theData.players -- dicts by name, holds a number
usher.types = theData.types
usher.units = theData.units
usher.groups = theData.groups
usher.coas = theData.coas
end
-- go go go
function usher.start()
-- lib check
if not dcsCommon.libCheck then
trigger.action.outText("usher requires dcsCommon", 30)
return false
end
if not dcsCommon.libCheck("usher", usher.requiredLibs) then
return false
end
-- read config
usher.readConfigZone()
-- load any saved data
if persistence then
-- sign up for persistence
callbacks = {}
callbacks.persistData = usher.saveData
persistence.registerModule("usher", callbacks)
-- now load my data
usher.loadData()
end
-- connect event handler
world.addEventHandler(usher)
-- invoke update
usher.update()
trigger.action.outText("Usher v" .. usher.version .. " started.", 30)
return true
end
-- let's go!
if not usher.start() then
trigger.action.outText("usher aborted: error on start", 30)
usher = nil
end

View File

@ -1,5 +1,5 @@
valet = {}
valet.version = "1.1.1"
valet.version = "1.1.2"
valet.verbose = false
valet.requiredLibs = {
"dcsCommon", -- always
@ -15,6 +15,7 @@ valet.valets = {}
1.0.3 - outSoundFile now working correctly
1.1.0 - hysteresis is now time-based (10 seconds)
1.1.1 - hardening against DCS July-11 update issues
1.1.2 - <u>, <p> and <g>
--]]--
function valet.addValet(theZone)
@ -134,9 +135,13 @@ function valet.preprocessWildcards(inMsg, aUnit, theDesc)
if pN then pName = pN end
end
theMsg = theMsg:gsub("<player>", pName)
theMsg = theMsg:gsub("<p>", pName)
theMsg = theMsg:gsub("<unit>", aUnit:getName())
theMsg = theMsg:gsub("<u>", aUnit:getName())
theMsg = theMsg:gsub("<type>", aUnit:getTypeName())
--theMsg = theMsg:gsub("<t>", aUnit:getTypeName())
theMsg = theMsg:gsub("<group>", aUnit:getGroup():getName())
theMsg = theMsg:gsub("<g>", aUnit:getGroup():getName())
theMsg = theMsg:gsub("<in>", tostring(theDesc.greets + 1) )
theMsg = theMsg:gsub("<out>", tostring(theDesc.byes + 1))
return theMsg