mirror of
https://github.com/weyne85/DML.git
synced 2025-10-29 16:57:49 +00:00
Version 2.3.8
Usher
This commit is contained in:
parent
491a9d0838
commit
88026bf851
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -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()
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
--
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
259
modules/usher.lua
Normal 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
|
||||
@ -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
|
||||
|
||||
BIN
tutorial & demo missions/demo - usher me to my plane.miz
Normal file
BIN
tutorial & demo missions/demo - usher me to my plane.miz
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user