mirror of
https://github.com/weyne85/DML.git
synced 2025-10-29 16:57:49 +00:00
Version 2.3.0
DCS Jul-11, Jul-22, Aug-09 code hardening. NEW: Convoy and TWN
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -1,5 +1,5 @@
|
|||||||
FARPZones = {}
|
FARPZones = {}
|
||||||
FARPZones.version = "2.1.1"
|
FARPZones.version = "2.2.0"
|
||||||
FARPZones.verbose = false
|
FARPZones.verbose = false
|
||||||
--[[--
|
--[[--
|
||||||
Version History
|
Version History
|
||||||
@@ -24,7 +24,8 @@ FARPZones.verbose = false
|
|||||||
2.0.2 - clean-up
|
2.0.2 - clean-up
|
||||||
verbosity enhancements
|
verbosity enhancements
|
||||||
2.1.0 - integration with camp: needs repairs, produceResourceVehicles()
|
2.1.0 - integration with camp: needs repairs, produceResourceVehicles()
|
||||||
2.1.1 - loading a farp from data respaws all defenders and resource vehicles
|
2.1.1 - loading a farp from data respaws all defenders and resource vehicles
|
||||||
|
2.2.0 - changing a FARP's owner invokes SSBClient if it is loaded
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
@@ -331,9 +332,7 @@ end
|
|||||||
|
|
||||||
function FARPZones.produceVehicles(theFarp)
|
function FARPZones.produceVehicles(theFarp)
|
||||||
local theZone = theFarp.zone
|
local theZone = theFarp.zone
|
||||||
-- trigger.action.outText("entering veh prod run for farp zone <" .. theZone.name .. ">, owner is <" .. theFarp.owner .. ">", 30)
|
|
||||||
--end
|
|
||||||
|
|
||||||
-- abort production if farp is owned by neutral and
|
-- abort production if farp is owned by neutral and
|
||||||
-- neutralproduction is false
|
-- neutralproduction is false
|
||||||
if theFarp.owner == 0 and not theFarp.neutralProduction then
|
if theFarp.owner == 0 and not theFarp.neutralProduction then
|
||||||
@@ -382,18 +381,7 @@ function FARPZones.produceVehicles(theFarp)
|
|||||||
|
|
||||||
-- spawn resource vehicles
|
-- spawn resource vehicles
|
||||||
FARPZones.produceResourceVehicles(theFarp, theCoalition)
|
FARPZones.produceResourceVehicles(theFarp, theCoalition)
|
||||||
--[[--
|
|
||||||
unitTypes = FARPZones.resourceTypes
|
|
||||||
local theGroup, theData = cfxZones.createGroundUnitsInZoneForCoalition (
|
|
||||||
theCoalition,
|
|
||||||
theFarp.name .. "-R" .. theFarp.count, -- must be unique
|
|
||||||
theFarp.resZone,
|
|
||||||
unitTypes,
|
|
||||||
"line_v",
|
|
||||||
theFarp.resHeading)
|
|
||||||
theFarp.resources = theGroup
|
|
||||||
theFarp.resourceData = theData
|
|
||||||
--]]--
|
|
||||||
-- update unique counter
|
-- update unique counter
|
||||||
theFarp.count = theFarp.count + 1
|
theFarp.count = theFarp.count + 1
|
||||||
end
|
end
|
||||||
@@ -508,7 +496,6 @@ function FARPZones.saveData()
|
|||||||
-- iterate all farp data and put them into a container each
|
-- iterate all farp data and put them into a container each
|
||||||
for theZone, theFARP in pairs(FARPZones.allFARPZones) do
|
for theZone, theFARP in pairs(FARPZones.allFARPZones) do
|
||||||
fName = theZone.name
|
fName = theZone.name
|
||||||
--trigger.action.outText("frpZ persistence: processing FARP <" .. fName .. ">", 30)
|
|
||||||
local fData = {}
|
local fData = {}
|
||||||
fData.owner = theFARP.owner
|
fData.owner = theFARP.owner
|
||||||
fData.defenderData = dcsCommon.clone(theFARP.defenderData)
|
fData.defenderData = dcsCommon.clone(theFARP.defenderData)
|
||||||
@@ -528,6 +515,17 @@ function FARPZones.saveData()
|
|||||||
return theData
|
return theData
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function FARPZones.delayedSSB()
|
||||||
|
-- invoke SSBClient to re-scan all airfields
|
||||||
|
-- if it is loaded in the mission
|
||||||
|
if FARPZones.verbose then
|
||||||
|
trigger.action.outText("FARPz: delayed SSB invocation", 30)
|
||||||
|
end
|
||||||
|
if cfxSSBClient then
|
||||||
|
cfxSSBClient.setSlotAccessByAirfieldOwner()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function FARPZones.loadMission()
|
function FARPZones.loadMission()
|
||||||
local theData = persistence.getSavedDataForModule("FARPZones")
|
local theData = persistence.getSavedDataForModule("FARPZones")
|
||||||
if not theData then
|
if not theData then
|
||||||
@@ -548,27 +546,9 @@ function FARPZones.loadMission()
|
|||||||
theAB:setCoalition(theFARP.owner) -- FARP is in lockup.
|
theAB:setCoalition(theFARP.owner) -- FARP is in lockup.
|
||||||
theFARP.defenderData = dcsCommon.clone(fData.defenderData)
|
theFARP.defenderData = dcsCommon.clone(fData.defenderData)
|
||||||
|
|
||||||
--[[--
|
|
||||||
local groupData = fData.defenderData
|
|
||||||
if groupData and #groupData.units > 0 then
|
|
||||||
local cty = groupData.cty
|
|
||||||
local cat = groupData.cat
|
|
||||||
theFARP.defenders = coalition.addGroup(cty, cat, groupData)
|
|
||||||
end
|
|
||||||
|
|
||||||
groupData = fData.resourceData
|
|
||||||
if groupData and #groupData.units > 0 then
|
|
||||||
local cty = groupData.cty
|
|
||||||
local cat = groupData.cat
|
|
||||||
theFARP.resources = coalition.addGroup(cty, cat, groupData)
|
|
||||||
end
|
|
||||||
--]]--
|
|
||||||
FARPZones.produceVehicles(theFARP) -- do full defender and resource cycle
|
FARPZones.produceVehicles(theFARP) -- do full defender and resource cycle
|
||||||
FARPZones.drawFARPCircleInMap(theFARP) -- mark in map
|
FARPZones.drawFARPCircleInMap(theFARP) -- mark in map
|
||||||
-- if (not theFARP.defenders) and (not theFARP.resources) then
|
|
||||||
-- we instigate a resource and defender drop
|
|
||||||
-- FARPZones.produceVehicles(theFARP)
|
|
||||||
-- end
|
|
||||||
else
|
else
|
||||||
trigger.action.outText("frpZ: persistence: FARP <" .. fName .. "> no longer exists in mission, skipping", 30)
|
trigger.action.outText("frpZ: persistence: FARP <" .. fName .. "> no longer exists in mission, skipping", 30)
|
||||||
end
|
end
|
||||||
@@ -580,10 +560,8 @@ end
|
|||||||
-- Start
|
-- Start
|
||||||
--
|
--
|
||||||
function FARPZones.releaseFARPS()
|
function FARPZones.releaseFARPS()
|
||||||
-- trigger.action.outText("Releasing hold on FARPS", 30)
|
|
||||||
for idx, aFarp in pairs(FARPZones.lockup) do
|
for idx, aFarp in pairs(FARPZones.lockup) do
|
||||||
aFarp:autoCapture(true)
|
aFarp:autoCapture(true)
|
||||||
-- trigger.action.outText("releasing farp <" .. aFarp:getName() .. ">", 30)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -592,13 +570,10 @@ function FARPZones.readConfig()
|
|||||||
if not theZone then
|
if not theZone then
|
||||||
theZone = cfxZones.createSimpleZone("farpZonesConfig")
|
theZone = cfxZones.createSimpleZone("farpZonesConfig")
|
||||||
end
|
end
|
||||||
|
|
||||||
FARPZones.verbose = theZone.verbose
|
FARPZones.verbose = theZone.verbose
|
||||||
|
|
||||||
FARPZones.spinUpDelay = theZone:getNumberFromZoneProperty( "spinUpDelay", 30)
|
FARPZones.spinUpDelay = theZone:getNumberFromZoneProperty( "spinUpDelay", 30)
|
||||||
|
|
||||||
FARPZones.refresh = theZone:getNumberFromZoneProperty("refresh", -1)
|
FARPZones.refresh = theZone:getNumberFromZoneProperty("refresh", -1)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -635,8 +610,6 @@ function FARPZones.start()
|
|||||||
for k, aZone in pairs(theZones) do
|
for k, aZone in pairs(theZones) do
|
||||||
local aFARP = FARPZones.createFARPFromZone(aZone) -- read attributes from DCS
|
local aFARP = FARPZones.createFARPFromZone(aZone) -- read attributes from DCS
|
||||||
FARPZones.addFARPZone(aFARP) -- add to managed zones
|
FARPZones.addFARPZone(aFARP) -- add to managed zones
|
||||||
-- moved FARPZones.drawFARPCircleInMap(aFARP) -- mark in map
|
|
||||||
-- moved FARPZones.produceVehicles(aFARP) -- allocate initial vehicles
|
|
||||||
if FARPZones.verbose then
|
if FARPZones.verbose then
|
||||||
trigger.action.outText("processed FARP <" .. aZone.name .. "> now owned by " .. aZone.owner, 30)
|
trigger.action.outText("processed FARP <" .. aZone.name .. "> now owned by " .. aZone.owner, 30)
|
||||||
end
|
end
|
||||||
@@ -656,6 +629,7 @@ function FARPZones.start()
|
|||||||
FARPZones.startingUp = false -- not needed / read anywhere
|
FARPZones.startingUp = false -- not needed / read anywhere
|
||||||
|
|
||||||
timer.scheduleFunction(FARPZones.releaseFARPS, {}, timer.getTime() + 5)
|
timer.scheduleFunction(FARPZones.releaseFARPS, {}, timer.getTime() + 5)
|
||||||
|
timer.scheduleFunction(FARPZones.delayedSSB, {}, timer.getTime() + 10)
|
||||||
|
|
||||||
if FARPZones.refresh > 0 then
|
if FARPZones.refresh > 0 then
|
||||||
timer.scheduleFunction(FARPZones.refreshMap, {}, timer.getTime() + FARPZones.refresh)
|
timer.scheduleFunction(FARPZones.refreshMap, {}, timer.getTime() + FARPZones.refresh)
|
||||||
|
|||||||
@@ -75,13 +75,9 @@ function rndFlags.createRNDWithZone(theZone)
|
|||||||
-- trigger flag
|
-- trigger flag
|
||||||
if theZone:hasProperty("f?") then
|
if theZone:hasProperty("f?") then
|
||||||
theZone.triggerFlag = theZone:getStringFromZoneProperty("f?", "none")
|
theZone.triggerFlag = theZone:getStringFromZoneProperty("f?", "none")
|
||||||
end
|
elseif theZone:hasProperty("in?") then
|
||||||
|
|
||||||
if theZone:hasProperty("in?") then
|
|
||||||
theZone.triggerFlag = theZone:getStringFromZoneProperty("in?", "none")
|
theZone.triggerFlag = theZone:getStringFromZoneProperty("in?", "none")
|
||||||
end
|
elseif theZone:hasProperty("rndPoll?") then
|
||||||
|
|
||||||
if theZone:hasProperty("rndPoll?") then
|
|
||||||
theZone.triggerFlag = theZone:getStringFromZoneProperty("rndPoll?", "none")
|
theZone.triggerFlag = theZone:getStringFromZoneProperty("rndPoll?", "none")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
cfxSSBClient = {}
|
cfxSSBClient = {}
|
||||||
cfxSSBClient.version = "4.0.1"
|
cfxSSBClient.version = "5.0.0"
|
||||||
cfxSSBClient.verbose = false
|
cfxSSBClient.verbose = false
|
||||||
cfxSSBClient.singleUse = false -- set to true to block crashed planes
|
cfxSSBClient.singleUse = false -- set to true to block crashed planes
|
||||||
-- NOTE: singleUse (true) requires SSB to disable immediate respawn after kick
|
-- NOTE: singleUse (true) requires SSB to disable immediate respawn after kick
|
||||||
@@ -8,7 +8,7 @@ cfxSSBClient.reUseAfter = -1 -- seconds for re-use delay
|
|||||||
|
|
||||||
cfxSSBClient.requiredLibs = {
|
cfxSSBClient.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
"cfxMX", --"cfxGroups", -- for slot access
|
"cfxMX", -- for ME Player data access
|
||||||
"cfxZones", -- Zones, of course
|
"cfxZones", -- Zones, of course
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,7 +17,13 @@ Version History
|
|||||||
4.0.0 - dmlZones
|
4.0.0 - dmlZones
|
||||||
- cfxMX instead of cfxGroups
|
- cfxMX instead of cfxGroups
|
||||||
4.0.1 - check slot availability immediately upon start
|
4.0.1 - check slot availability immediately upon start
|
||||||
- ssb autoenable option
|
- ssb auto-enable option
|
||||||
|
5.0.0 - re-write: support for DCS new dynamic spawns
|
||||||
|
- work-around for DCS bug that passes dead / deallocated objects
|
||||||
|
- SINGLE-USE and dynamic spawns are mutually exclusive
|
||||||
|
as single-use cannot cover dynamic slots, so authors must
|
||||||
|
disable or limit the amount of planes manually
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
cfxSSBClient.enabledFlagValue = 0 -- DO NOT CHANGE, MUST MATCH SSB
|
cfxSSBClient.enabledFlagValue = 0 -- DO NOT CHANGE, MUST MATCH SSB
|
||||||
@@ -36,9 +42,9 @@ cfxSSBClient.slotActions = {
|
|||||||
cfxSSBClient.keepInAirGroups = false -- if false we only look at planes starting on the ground
|
cfxSSBClient.keepInAirGroups = false -- if false we only look at planes starting on the ground
|
||||||
-- setting this to true only makes sense if you plan to bind in-air starts to airfields
|
-- setting this to true only makes sense if you plan to bind in-air starts to airfields
|
||||||
|
|
||||||
cfxSSBClient.playerGroups = {}
|
cfxSSBClient.playerGroups = {} -- indexed by groupName. group data with .airfield attribute
|
||||||
cfxSSBClient.closedAirfields = {} -- list that closes airfields for any aircrafts
|
cfxSSBClient.closedAirfields = {} -- list that closes airfields for all aircrafts
|
||||||
cfxSSBClient.playerPlanes = {} -- names of units that a player is flying
|
cfxSSBClient.playerPlanes = {} -- names of unit that a player is flying indexed by unit name
|
||||||
cfxSSBClient.crashedGroups = {} -- names of groups to block after crash of their player-flown plane
|
cfxSSBClient.crashedGroups = {} -- names of groups to block after crash of their player-flown plane
|
||||||
cfxSSBClient.slotState = {} -- keeps a record of which slot has which value. For persistence and debugging
|
cfxSSBClient.slotState = {} -- keeps a record of which slot has which value. For persistence and debugging
|
||||||
cfxSSBClient.occupiedUnits = {} -- by unit name if occupied to prevent kicking. clears after crash or leaving plane
|
cfxSSBClient.occupiedUnits = {} -- by unit name if occupied to prevent kicking. clears after crash or leaving plane
|
||||||
@@ -154,6 +160,7 @@ end
|
|||||||
function cfxSSBClient.setSlotAccessForGroup(theGroup)
|
function cfxSSBClient.setSlotAccessForGroup(theGroup)
|
||||||
if not theGroup then return end
|
if not theGroup then return end
|
||||||
-- WARNING: theGroup is cfxGroup record
|
-- WARNING: theGroup is cfxGroup record
|
||||||
|
-- amended for dynamic groups
|
||||||
local theName = theGroup.name
|
local theName = theGroup.name
|
||||||
|
|
||||||
-- we now check if any plane of that group is still
|
-- we now check if any plane of that group is still
|
||||||
@@ -163,15 +170,27 @@ function cfxSSBClient.setSlotAccessForGroup(theGroup)
|
|||||||
|
|
||||||
-- we now iterate all playerUnits in theGroup.
|
-- we now iterate all playerUnits in theGroup.
|
||||||
-- theGroup is cfxGroup
|
-- theGroup is cfxGroup
|
||||||
for idx, playerData in pairs (theGroup.playerUnits) do
|
if theGroup.playerUnits then
|
||||||
local uName = playerData.name
|
-- this is a ME unit
|
||||||
if cfxSSBClient.occupiedUnits[uName] then
|
for idx, playerData in pairs (theGroup.playerUnits) do
|
||||||
|
local uName = playerData.name
|
||||||
|
if cfxSSBClient.occupiedUnits[uName] then
|
||||||
|
if cfxSSBClient.verbose then
|
||||||
|
trigger.action.outText("+++ssbc: unit <" .. uName .. "> of group <" .. theName .. "> is occupied, no airfield check", 30)
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- this is a dynamic unit
|
||||||
|
if cfxSSBClient.occupiedUnits[theGroup.uName] then
|
||||||
|
local uName = theGroup.uName
|
||||||
if cfxSSBClient.verbose then
|
if cfxSSBClient.verbose then
|
||||||
trigger.action.outText("+++ssbc: unit <" .. uName .. "> of group <" .. theName .. "> is occupied, no airfield check", 30)
|
trigger.action.outText("+++ssbc: DYNAMIC unit <" .. uName .. "> of group <" .. theName .. "> is occupied, no airfield check", 30)
|
||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- when we get here, no unit in the entire group is occupied
|
-- when we get here, no unit in the entire group is occupied
|
||||||
local theMatchingAirfield = theGroup.airfield
|
local theMatchingAirfield = theGroup.airfield
|
||||||
@@ -230,8 +249,6 @@ function cfxSSBClient.setSlotAccessForGroup(theGroup)
|
|||||||
end
|
end
|
||||||
trigger.action.setUserFlag(theName, blockState)
|
trigger.action.setUserFlag(theName, blockState)
|
||||||
cfxSSBClient.slotState[theName] = blockState
|
cfxSSBClient.slotState[theName] = blockState
|
||||||
--if cfxSSBClient.verbose then
|
|
||||||
--end
|
|
||||||
else
|
else
|
||||||
if cfxSSBClient.verbose then
|
if cfxSSBClient.verbose then
|
||||||
trigger.action.outText("+++SSB: group ".. theName .. " no bound airfield: available", 30)
|
trigger.action.outText("+++SSB: group ".. theName .. " no bound airfield: available", 30)
|
||||||
@@ -252,18 +269,22 @@ function cfxSSBClient.setSlotAccessForUnit(theUnit) -- calls setSlotAccessForGro
|
|||||||
end
|
end
|
||||||
|
|
||||||
function cfxSSBClient.getPlayerGroupForGroupNamed(aName)
|
function cfxSSBClient.getPlayerGroupForGroupNamed(aName)
|
||||||
|
--is now indexed !!
|
||||||
|
return cfxSSBClient.playerGroups[aName]
|
||||||
|
--[[--
|
||||||
local pGroups = cfxSSBClient.playerGroups
|
local pGroups = cfxSSBClient.playerGroups
|
||||||
for idx, theGroup in pairs(pGroups) do
|
for idx, theGroup in pairs(pGroups) do
|
||||||
if theGroup.name == aName then return theGroup end
|
if theGroup.name == aName then return theGroup end
|
||||||
end
|
end
|
||||||
return nil
|
return nil
|
||||||
|
--]]--
|
||||||
end
|
end
|
||||||
|
|
||||||
function cfxSSBClient.setSlotAccessByAirfieldOwner()
|
function cfxSSBClient.setSlotAccessByAirfieldOwner()
|
||||||
-- get all groups that have a player-controlled aircraft
|
-- get all groups that have a player-controlled aircraft
|
||||||
-- now uses cached, reduced set of player planes
|
-- now uses cached, reduced set of player planes
|
||||||
local pGroups = cfxSSBClient.playerGroups
|
local pGroups = cfxSSBClient.playerGroups -- indexed by name
|
||||||
for idx, theGroup in pairs(pGroups) do
|
for gName, theGroup in pairs(pGroups) do
|
||||||
cfxSSBClient.setSlotAccessForGroup(theGroup)
|
cfxSSBClient.setSlotAccessForGroup(theGroup)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -294,14 +315,8 @@ function cfxSSBClient:onEvent(event)
|
|||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local curH = theUnit:getLife()
|
if not theUnit.getName then return end -- WTF???
|
||||||
local maxH = theUnit:getLife0()
|
|
||||||
local uName = theUnit:getName()
|
local uName = theUnit:getName()
|
||||||
if cfxSSBClient.verbose then
|
|
||||||
trigger.action.outText("+++SSB: Player leaves unit <" .. uName .. ">", 30)
|
|
||||||
trigger.action.outText("+++SSB: unit health check: " .. curH .. " of " .. maxH, 30)
|
|
||||||
end
|
|
||||||
|
|
||||||
cfxSSBClient.occupiedUnits[uName] = nil -- forget I was occupied
|
cfxSSBClient.occupiedUnits[uName] = nil -- forget I was occupied
|
||||||
cfxSSBClient.setSlotAccessForUnit(theUnit) -- prevent re-slotting if airfield lost
|
cfxSSBClient.setSlotAccessForUnit(theUnit) -- prevent re-slotting if airfield lost
|
||||||
return
|
return
|
||||||
@@ -317,9 +332,10 @@ function cfxSSBClient:onEvent(event)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- write down player names and planes
|
-- write down player names and planes
|
||||||
if event.id == 15 then -- birth
|
if event.id == 15 then -- birth / spawn
|
||||||
if not event.initiator then return end
|
if not event.initiator then return end
|
||||||
local theUnit = event.initiator -- we know this exists
|
local theUnit = event.initiator -- we know this exists
|
||||||
|
if not theUnit.getName then return end -- hardening
|
||||||
local uName = theUnit:getName()
|
local uName = theUnit:getName()
|
||||||
if not uName then return end
|
if not uName then return end
|
||||||
-- player entered unit?
|
-- player entered unit?
|
||||||
@@ -336,6 +352,12 @@ function cfxSSBClient:onEvent(event)
|
|||||||
end
|
end
|
||||||
-- remember this unit as player controlled plane
|
-- remember this unit as player controlled plane
|
||||||
-- because player and plane can easily disconnect
|
-- because player and plane can easily disconnect
|
||||||
|
-- find out if this is a dynamic spawn
|
||||||
|
local isDynamic = cfxMX.isDynamicPlayer(theUnit)
|
||||||
|
if isDynamic then
|
||||||
|
trigger.action.outText("+++SSBC: detected dynamic player spawn for unit <" .. uName .. ">, id <" .. theUnit:getID() .. ">, group <" .. theUnit:getGroup():getName() .. ">, player <" .. playerName .. ">", 30)
|
||||||
|
cfxSSBClient.amendPlayerData(theUnit) -- get airport and add to managed slots
|
||||||
|
end
|
||||||
cfxSSBClient.playerPlanes[uName] = playerName
|
cfxSSBClient.playerPlanes[uName] = playerName
|
||||||
if cfxSSBClient.verbose then
|
if cfxSSBClient.verbose then
|
||||||
trigger.action.outText("+++SSBC:SU: noted " .. playerName .. " piloting player unit " .. uName, 30)
|
trigger.action.outText("+++SSBC:SU: noted " .. playerName .. " piloting player unit " .. uName, 30)
|
||||||
@@ -349,16 +371,21 @@ function cfxSSBClient:onEvent(event)
|
|||||||
if event.id == 5 then -- crash PRE-processing
|
if event.id == 5 then -- crash PRE-processing
|
||||||
if not event.initiator then return end
|
if not event.initiator then return end
|
||||||
local theUnit = event.initiator
|
local theUnit = event.initiator
|
||||||
|
if not theUnit.getName then return end
|
||||||
local uName = theUnit:getName()
|
local uName = theUnit:getName()
|
||||||
cfxSSBClient.occupiedUnits[uName] = nil -- no longer occupied
|
cfxSSBClient.occupiedUnits[uName] = nil -- no longer occupied
|
||||||
cfxSSBClient.setSlotAccessForUnit(theUnit) -- prevent re-slotting if airfield lost
|
cfxSSBClient.setSlotAccessForUnit(theUnit) -- prevent re-slotting if airfield lost
|
||||||
|
-- DO NOT RETURN NOW!!! singleuse proccing follows
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxSSBClient.singleUse and event.id == 5 then -- crash
|
if cfxSSBClient.singleUse and event.id == 5 then -- crash
|
||||||
--if not event.initiator then return end
|
--if not event.initiator then return end
|
||||||
local theUnit = event.initiator
|
local theUnit = event.initiator
|
||||||
|
if not theUnit then return end
|
||||||
|
if not theUnit.getName then return end
|
||||||
local uName = theUnit:getName()
|
local uName = theUnit:getName()
|
||||||
if not uName then return end
|
if not uName then return end
|
||||||
|
if not theUnit.getGroup then return end
|
||||||
local theGroup = theUnit:getGroup()
|
local theGroup = theUnit:getGroup()
|
||||||
if not theGroup then return end
|
if not theGroup then return end
|
||||||
-- see if a player plane
|
-- see if a player plane
|
||||||
@@ -371,6 +398,7 @@ function cfxSSBClient:onEvent(event)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
-- if we get here, a player-owned plane has crashed
|
-- if we get here, a player-owned plane has crashed
|
||||||
|
if not theGroup.getName then return end -- better safe than sorry
|
||||||
local gName = theGroup:getName()
|
local gName = theGroup:getName()
|
||||||
if not gName then return end
|
if not gName then return end
|
||||||
|
|
||||||
@@ -435,27 +463,34 @@ end
|
|||||||
|
|
||||||
-- pre-process static player data to minimize
|
-- pre-process static player data to minimize
|
||||||
-- processor load on checks
|
-- processor load on checks
|
||||||
function cfxSSBClient.processPlayerData()
|
function cfxSSBClient.processSSBPlayerData()
|
||||||
cfxSSBClient.playerGroups = cfxMX.getPlayerGroup()
|
--cfxSSBClient.playerGroups = cfxMX.getPlayerGroup()
|
||||||
local pGroups = cfxSSBClient.playerGroups
|
local pGroups = cfxSSBClient.SSBPlayerData -- cfxSSBClient.playerGroups
|
||||||
local filteredPlayers = {}
|
local filteredPlayers = {}
|
||||||
for idx, theGroup in pairs(pGroups) do
|
for gName, theGroup in pairs(pGroups) do
|
||||||
if theGroup.airfield ~= nil or cfxSSBClient.keepInAirGroups or
|
if theGroup.airfield ~= nil or cfxSSBClient.keepInAirGroups or
|
||||||
cfxSSBClient.singleUse then
|
cfxSSBClient.singleUse then
|
||||||
-- only transfer groups that have airfields (or also keepInAirGroups or when single-use)
|
-- only transfer groups that have airfields (or also keepInAirGroups or when single-use)
|
||||||
-- attached. Ignore the rest as they are
|
-- attached. Ignore the rest as they are
|
||||||
-- always fine
|
-- always fine
|
||||||
table.insert(filteredPlayers, theGroup)
|
--table.insert(filteredPlayers, theGroup)
|
||||||
|
filteredPlayers[gName] = theGroup
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
cfxSSBClient.playerGroups = filteredPlayers
|
cfxSSBClient.playerGroups = filteredPlayers
|
||||||
|
-- we can now relinquish SSBPlayerData
|
||||||
|
cfxSSBClient.SSBPlayerData = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
-- add airfield information to each player group
|
-- add airfield information to each player group
|
||||||
|
-- WARNING: AMENDS/MODIFIES DATA IN MX TO CONTAIN AIRFIELDS
|
||||||
|
-- now changed to internal clones
|
||||||
function cfxSSBClient.processGroupData()
|
function cfxSSBClient.processGroupData()
|
||||||
local pGroups = cfxMX.getPlayerGroup() -- we want the group.name attribute
|
local pGroups = cfxMX.getPlayerGroup() -- we want the group.name attribute
|
||||||
|
local processed = {}
|
||||||
for idx, theGroup in pairs(pGroups) do
|
for idx, theGroup in pairs(pGroups) do
|
||||||
-- we always use the first player's plane as referenced
|
-- we always use the first player's plane as referenced
|
||||||
|
local cGroup = dcsCommon.clone(theGroup)
|
||||||
local playerData = theGroup.playerUnits[1]
|
local playerData = theGroup.playerUnits[1]
|
||||||
local theAirfield = nil
|
local theAirfield = nil
|
||||||
local delta = -1
|
local delta = -1
|
||||||
@@ -471,20 +506,52 @@ function cfxSSBClient.processGroupData()
|
|||||||
end
|
end
|
||||||
if delta > cfxSSBClient.maxAirfieldRange then
|
if delta > cfxSSBClient.maxAirfieldRange then
|
||||||
-- forget airfield
|
-- forget airfield
|
||||||
theAirfield = nil
|
theAirfield = nil
|
||||||
if cfxSSBClient.verbose then
|
if cfxSSBClient.verbose then
|
||||||
trigger.action.outText("+++SSB: group: " .. theGroup.name .. " unlinked - too far from airfield" , 30)
|
trigger.action.outText("+++SSB: group: " .. theGroup.name .. " unlinked - too far from airfield" , 30)
|
||||||
end
|
end
|
||||||
|
-- end
|
||||||
|
else
|
||||||
|
cGroup.airfield = theAirfield -- we update the clone
|
||||||
|
-- add to my player groups, indexed by group name
|
||||||
|
processed[theGroup.name] = cGroup -- we keep the clone
|
||||||
end
|
end
|
||||||
theGroup.airfield = theAirfield
|
|
||||||
else
|
else
|
||||||
if cfxSSBClient.verbose then
|
if cfxSSBClient.verbose then
|
||||||
trigger.action.outText("+++SSB: group: " .. theGroup.name .. " start option " .. action .. " does not concern SSB", 30)
|
trigger.action.outText("+++SSB: group: " .. theGroup.name .. " start option " .. action .. " does not concern SSB", 30)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
cfxSSBClient.SSBPlayerData = processed -- remember all relevant clones for post-processing
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function cfxSSBClient.amendPlayerData(theUnit)
|
||||||
|
-- enter with single, dynamically spawning unit and add an
|
||||||
|
-- entry for cfxSSBClient.SSBPlayerData to allow entry for
|
||||||
|
-- group's airbase / FARP
|
||||||
|
local dynGroup = theUnit:getGroup()
|
||||||
|
local theGroup = {} -- entry into db
|
||||||
|
theGroup.name = dynGroup:getName()
|
||||||
|
local thePoint = theUnit:getPoint()
|
||||||
|
local theAirfield, delta = cfxSSBClient.getClosestAirbaseTo(thePoint)
|
||||||
|
local afName = theAirfield:getName()
|
||||||
|
if cfxSSBClient.verbose then
|
||||||
|
trigger.action.outText("+++SSB: DYNAMIC group: " .. theGroup.name .. " closest to AF " .. afName .. ": " .. math.floor(delta) .. "m" , 30)
|
||||||
|
end
|
||||||
|
if delta > cfxSSBClient.maxAirfieldRange then
|
||||||
|
-- forget airfield
|
||||||
|
--theAirfield = nil
|
||||||
|
if cfxSSBClient.verbose then
|
||||||
|
trigger.action.outText("+++SSB: DYNAMIC group: " .. theGroup.name .. " unlinked - too far from airfield (???)" , 30 )
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if cfxSSBClient.verbose then
|
||||||
|
trigger.action.outText("+++SSB: DYNAMIC group: " .. theGroup.name .. " added to SSBPlayerData for slot management" , 30)
|
||||||
|
end
|
||||||
|
theGroup.uName = theUnit:getName()
|
||||||
|
cfxSSBClient.playerGroups[theGroup.name] = theGroup
|
||||||
|
end
|
||||||
|
end
|
||||||
--
|
--
|
||||||
-- read config zone
|
-- read config zone
|
||||||
--
|
--
|
||||||
@@ -589,7 +656,7 @@ function cfxSSBClient.start()
|
|||||||
|
|
||||||
-- process player data to minimize effort and build cache
|
-- process player data to minimize effort and build cache
|
||||||
-- into cfxSSBClient.playerGroups
|
-- into cfxSSBClient.playerGroups
|
||||||
cfxSSBClient.processPlayerData()
|
cfxSSBClient.processSSBPlayerData() -- processPlayerData()
|
||||||
|
|
||||||
-- process ssbc zones
|
-- process ssbc zones
|
||||||
-- for in-mission DML interface
|
-- for in-mission DML interface
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
tdz = {}
|
tdz = {}
|
||||||
tdz.version = "1.0.3"
|
tdz.version = "1.1.0"
|
||||||
tdz.requiredLibs = {
|
tdz.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
"cfxZones", -- Zones, of course
|
"cfxZones", -- Zones, of course
|
||||||
@@ -18,6 +18,7 @@ VERSION HISTORY
|
|||||||
1.0.2 - manual placement option
|
1.0.2 - manual placement option
|
||||||
filters FARPs
|
filters FARPs
|
||||||
1.0.3 - "manual" now defaults to false
|
1.0.3 - "manual" now defaults to false
|
||||||
|
1.1.0 - now supports event 55 (runway touch)
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
@@ -326,7 +327,7 @@ function tdz:onEvent(event)
|
|||||||
if not theUnit.getPlayerName then return end
|
if not theUnit.getPlayerName then return end
|
||||||
local playerName = theUnit:getPlayerName()
|
local playerName = theUnit:getPlayerName()
|
||||||
if not playerName then return end
|
if not playerName then return end
|
||||||
if event.id == 4 then
|
if event.id == 4 or event.id == 55 then
|
||||||
-- player landed
|
-- player landed
|
||||||
tdz.playerLanded(theUnit, playerName)
|
tdz.playerLanded(theUnit, playerName)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
airfield = {}
|
airfield = {}
|
||||||
airfield.version = "2.1.1"
|
airfield.version = "2.2.0"
|
||||||
airfield.requiredLibs = {
|
airfield.requiredLibs = {
|
||||||
"dcsCommon",
|
"dcsCommon",
|
||||||
"cfxZones",
|
"cfxZones",
|
||||||
@@ -21,7 +21,7 @@ airfield.allAirfields = {} -- inexed by af name, db entries: base, cat
|
|||||||
-- support for FARPS as well
|
-- support for FARPS as well
|
||||||
2.1.0 - added support for makeNeutral?
|
2.1.0 - added support for makeNeutral?
|
||||||
2.1.1 - bug fixing for DCS 2.9x airfield retrofit
|
2.1.1 - bug fixing for DCS 2.9x airfield retrofit
|
||||||
|
2.2.0 - dmlZone:getCoalition() / masterowner adaptation for owner
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
-- init all airfields DB
|
-- init all airfields DB
|
||||||
@@ -109,11 +109,6 @@ function airfield.createAirFieldFromZone(theZone)
|
|||||||
airfield.assumeControl(theZone)
|
airfield.assumeControl(theZone)
|
||||||
end
|
end
|
||||||
|
|
||||||
if theZone:hasProperty("ownedBy#") then
|
|
||||||
theZone.ownedBy = theZone:getStringFromZoneProperty("ownedBy#", "<none>")
|
|
||||||
trigger.action.setUserFlag(theZone.ownedBy, theZone.owner)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- if fixed attribute, we switch to that color and keep it fixed.
|
-- if fixed attribute, we switch to that color and keep it fixed.
|
||||||
-- can be overridden by either makeXX or autoCap.
|
-- can be overridden by either makeXX or autoCap.
|
||||||
if theZone:hasProperty("fixed") then
|
if theZone:hasProperty("fixed") then
|
||||||
@@ -124,6 +119,11 @@ function airfield.createAirFieldFromZone(theZone)
|
|||||||
theZone.owner = theFixed
|
theZone.owner = theFixed
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if theZone:hasProperty("ownedBy#") then
|
||||||
|
theZone.ownedBy = theZone:getStringFromZoneProperty("ownedBy#", "<none>")
|
||||||
|
trigger.action.setUserFlag(theZone.ownedBy, theZone.owner)
|
||||||
|
end
|
||||||
|
|
||||||
-- index by name, and warn if duplicate associate
|
-- index by name, and warn if duplicate associate
|
||||||
if airfield.myAirfields[theZone.afName] then
|
if airfield.myAirfields[theZone.afName] then
|
||||||
trigger.action.outText("+++airF: WARNING - zone <" .. theZone.name .. "> redefines airfield <" .. theZone.afName .. ">, discarded!", 30)
|
trigger.action.outText("+++airF: WARNING - zone <" .. theZone.name .. "> redefines airfield <" .. theZone.afName .. ">, discarded!", 30)
|
||||||
@@ -182,7 +182,7 @@ function airfield.showAirfield(theZone)
|
|||||||
|
|
||||||
local lineColor = theZone.redLine -- {1.0, 0, 0, 1.0} -- red
|
local lineColor = theZone.redLine -- {1.0, 0, 0, 1.0} -- red
|
||||||
local fillColor = theZone.redFill -- {1.0, 0, 0, 0.2} -- red
|
local fillColor = theZone.redFill -- {1.0, 0, 0, 0.2} -- red
|
||||||
local owner = theZone.owner
|
local owner = theZone:getCoalition() -- .owner
|
||||||
if owner == 2 then
|
if owner == 2 then
|
||||||
lineColor = theZone.blueLine -- {0.0, 0, 1.0, 1.0}
|
lineColor = theZone.blueLine -- {0.0, 0, 1.0, 1.0}
|
||||||
fillColor = theZone.blueFill -- {0.0, 0, 1.0, 0.2}
|
fillColor = theZone.blueFill -- {0.0, 0, 1.0, 0.2}
|
||||||
@@ -315,6 +315,7 @@ function airfield.update()
|
|||||||
if theZone.owner ~= 1 then -- only send cap event when capped
|
if theZone.owner ~= 1 then -- only send cap event when capped
|
||||||
airfield.airfieldCaptured(theAirfield)
|
airfield.airfieldCaptured(theAirfield)
|
||||||
end
|
end
|
||||||
|
theZone.owner = 1
|
||||||
end
|
end
|
||||||
|
|
||||||
if theZone.makeBlue and theZone:testZoneFlag(theZone.makeBlue, theZone.triggerMethod, "lastMakeBlue") then
|
if theZone.makeBlue and theZone:testZoneFlag(theZone.makeBlue, theZone.triggerMethod, "lastMakeBlue") then
|
||||||
@@ -329,6 +330,7 @@ function airfield.update()
|
|||||||
if theZone.owner ~= 2 then -- only send cap event when capped
|
if theZone.owner ~= 2 then -- only send cap event when capped
|
||||||
airfield.airfieldCaptured(theAirfield)
|
airfield.airfieldCaptured(theAirfield)
|
||||||
end
|
end
|
||||||
|
theZone.owner = 2
|
||||||
end
|
end
|
||||||
|
|
||||||
if theZone.makeNeutral and theZone:testZoneFlag(theZone.makeNeutral, theZone.triggerMethod, "lastMakeNeutral") then
|
if theZone.makeNeutral and theZone:testZoneFlag(theZone.makeNeutral, theZone.triggerMethod, "lastMakeNeutral") then
|
||||||
@@ -343,6 +345,7 @@ function airfield.update()
|
|||||||
if theZone.owner ~= 0 then -- only send cap event when capped
|
if theZone.owner ~= 0 then -- only send cap event when capped
|
||||||
airfield.airfieldCaptured(theAirfield) -- 0 cap will not cause any signals, but we do this anyway
|
airfield.airfieldCaptured(theAirfield) -- 0 cap will not cause any signals, but we do this anyway
|
||||||
end
|
end
|
||||||
|
theZone.owner = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
bombRange = {}
|
bombRange = {}
|
||||||
bombRange.version = "2.0.0"
|
bombRange.version = "2.0.2"
|
||||||
bombRange.dh = 1 -- meters above ground level burst
|
bombRange.dh = 1 -- meters above ground level burst
|
||||||
|
|
||||||
bombRange.requiredLibs = {
|
bombRange.requiredLibs = {
|
||||||
@@ -13,7 +13,7 @@ VERSION HISTORY
|
|||||||
*after* impact on high-resolution scans (30fps)
|
*after* impact on high-resolution scans (30fps)
|
||||||
set resolution to 30 ups by default
|
set resolution to 30 ups by default
|
||||||
order of events: check kills against dropping projectiles
|
order of events: check kills against dropping projectiles
|
||||||
collecd dead, and compare against missing erdnance while they are fresh
|
collect dead, and compare against missing erdnance while they are fresh
|
||||||
GC
|
GC
|
||||||
interpolate hits on dead when looking at kills and projectile does
|
interpolate hits on dead when looking at kills and projectile does
|
||||||
not exist
|
not exist
|
||||||
@@ -26,6 +26,9 @@ VERSION HISTORY
|
|||||||
2.0.0 - support for radioMainMenu
|
2.0.0 - support for radioMainMenu
|
||||||
- support for types
|
- support for types
|
||||||
- types can have wild cards
|
- types can have wild cards
|
||||||
|
2.0.1 - says hi! on start
|
||||||
|
- fixes for DCS Jul 11 bugs
|
||||||
|
2.0.2 - fixes for DCS Jul 22 bugs
|
||||||
|
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
@@ -197,7 +200,7 @@ function bombRange.initCommsForUnit(theUnit)
|
|||||||
if bombRange.mainMenu then
|
if bombRange.mainMenu then
|
||||||
mainMenu = radioMenu.getMainMenuFor(bombRange.mainMenu) -- nilling both next params will return menus[0]
|
mainMenu = radioMenu.getMainMenuFor(bombRange.mainMenu) -- nilling both next params will return menus[0]
|
||||||
end
|
end
|
||||||
|
if not theUnit.getName then return end -- Jul-22 DCS bug
|
||||||
local uName = theUnit:getName()
|
local uName = theUnit:getName()
|
||||||
local pName = theUnit:getPlayerName()
|
local pName = theUnit:getPlayerName()
|
||||||
local theGroup = theUnit:getGroup()
|
local theGroup = theUnit:getGroup()
|
||||||
@@ -274,6 +277,7 @@ end
|
|||||||
-- Event Proccing
|
-- Event Proccing
|
||||||
--
|
--
|
||||||
function bombRange.suspectedHit(weapon, target)
|
function bombRange.suspectedHit(weapon, target)
|
||||||
|
if not Object.isExist(weapon) then return end -- DCS july 11 2024 issue
|
||||||
local wType = weapon:getTypeName()
|
local wType = weapon:getTypeName()
|
||||||
if not target then return end
|
if not target then return end
|
||||||
if target:getCategory() == 5 then -- scenery
|
if target:getCategory() == 5 then -- scenery
|
||||||
@@ -407,15 +411,19 @@ end
|
|||||||
function bombRange:onEvent(event)
|
function bombRange:onEvent(event)
|
||||||
if not event.initiator then return end
|
if not event.initiator then return end
|
||||||
local theUnit = event.initiator
|
local theUnit = event.initiator
|
||||||
|
if not Unit.isExist(theUnit) then return end -- DCS issue Jul-11
|
||||||
|
if not theUnit.getName then return end -- DCS issue Jul-22
|
||||||
|
|
||||||
if event.id == 2 then -- hit: weapon still exists
|
if event.id == 2 then -- hit: weapon still exists
|
||||||
if not event.weapon then return end
|
if not event.weapon then return end
|
||||||
|
if not Object.isExist(event.weapon) then return end -- Jul-11 issue
|
||||||
bombRange.suspectedHit(event.weapon, event.target)
|
bombRange.suspectedHit(event.weapon, event.target)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if event.id == 28 then -- kill: similar to hit, but due to new mechanics not reliable
|
if event.id == 28 then -- kill: similar to hit, but due to new mechanics not reliable
|
||||||
if not event.weapon then return end
|
if not event.weapon then return end
|
||||||
|
if not Object.isExist(event.weapon) then return end -- Jul-11 issue
|
||||||
bombRange.suspectedHit(event.weapon, event.target)
|
bombRange.suspectedHit(event.weapon, event.target)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@@ -425,6 +433,7 @@ function bombRange:onEvent(event)
|
|||||||
-- these events can come *before* weapon disappears
|
-- these events can come *before* weapon disappears
|
||||||
local killDat = {}
|
local killDat = {}
|
||||||
killDat.victim = event.initiator
|
killDat.victim = event.initiator
|
||||||
|
if not Object.isExist(event.initiator) then return end -- Jul-11 issue
|
||||||
killDat.p = event.initiator:getPoint()
|
killDat.p = event.initiator:getPoint()
|
||||||
killDat.when = timer.getTime()
|
killDat.when = timer.getTime()
|
||||||
killDat.name = dcsCommon.uuid("vic")
|
killDat.name = dcsCommon.uuid("vic")
|
||||||
@@ -454,7 +463,8 @@ function bombRange:onEvent(event)
|
|||||||
end
|
end
|
||||||
local w = event.weapon
|
local w = event.weapon
|
||||||
local b = {}
|
local b = {}
|
||||||
local bName = w:getName()
|
local bName = "unknown"
|
||||||
|
if w.getName then bName = w:getName() end -- Jul-22 DCS bug
|
||||||
b.name = bName
|
b.name = bName
|
||||||
b.type = w:getTypeName()
|
b.type = w:getTypeName()
|
||||||
-- may need to verify type: how do we handle clusters or flares?
|
-- may need to verify type: how do we handle clusters or flares?
|
||||||
@@ -793,6 +803,8 @@ function bombRange.start()
|
|||||||
-- start GC
|
-- start GC
|
||||||
bombRange.GC()
|
bombRange.GC()
|
||||||
|
|
||||||
|
-- say hi!
|
||||||
|
trigger.action.outText("cf/x Bomb Range v" .. bombRange.version .. " started.", 30)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
camp = {}
|
camp = {}
|
||||||
camp.ups = 1
|
camp.ups = 1
|
||||||
camp.version = "1.0.2"
|
camp.version = "1.1.0"
|
||||||
camp.requiredLibs = {
|
camp.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
"cfxZones", -- Zones, of course
|
"cfxZones", -- Zones, of course
|
||||||
@@ -18,6 +18,7 @@ VERSION HISTORY
|
|||||||
- actionSound
|
- actionSound
|
||||||
- output sound with communications
|
- output sound with communications
|
||||||
1.0.2 - integration with FARPZones
|
1.0.2 - integration with FARPZones
|
||||||
|
1.1.0 - support for DCS 2.9.6 dynamic spawns
|
||||||
--]]--
|
--]]--
|
||||||
--
|
--
|
||||||
-- CURRENTLY REQUIRES SINGLE-UNIT PLAYER GROUPS
|
-- CURRENTLY REQUIRES SINGLE-UNIT PLAYER GROUPS
|
||||||
@@ -110,24 +111,55 @@ function camp.update()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function camp:onEvent(theEvent)
|
function camp:onEvent(theEvent)
|
||||||
|
if not theEvent then return end
|
||||||
|
if not theEvent.initiator then return end
|
||||||
|
local theUnit = theEvent.initiator
|
||||||
|
-- if not theUnit.getName then return end
|
||||||
|
-- if not theUnit.getPlayerName then return end
|
||||||
|
if not cfxMX.isDynamicPlayer(theUnit) then return end
|
||||||
|
local id = theEvent.id
|
||||||
|
if id == 15 then -- birth
|
||||||
|
camp.lateProcessPlayer(theUnit)
|
||||||
|
if camp.verbose then
|
||||||
|
trigger.action.outText("camp: late player processing for <" .. theUnit:getName() .. ">", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Comms
|
-- Comms
|
||||||
--
|
--
|
||||||
|
function camp.lateProcessPlayer(theUnit)
|
||||||
|
if not theUnit then return end
|
||||||
|
if not theUnit.getGroup then return end
|
||||||
|
local theGroup = theUnit:getGroup()
|
||||||
|
local gName = theGroup:getName()
|
||||||
|
local gID = theGroup:getID()
|
||||||
|
camp.installComsFor(gID, gName)
|
||||||
|
end
|
||||||
|
|
||||||
|
function camp.installComsFor(gID, gName)
|
||||||
|
local theRoot = missionCommands.addSubMenuForGroup(gID, "Funds / Repairs / Upgrades")
|
||||||
|
camp.roots[gName] = theRoot
|
||||||
|
local c00 = missionCommands.addCommandForGroup(gID, "Theatre Overview", theRoot, camp.redirectTFunds, {gName, gID, "tfunds"})
|
||||||
|
local c0 = missionCommands.addCommandForGroup(gID, "Local Funds & Status Overview", theRoot, camp.redirectFunds, {gName, gID, "funds"})
|
||||||
|
local c1 = missionCommands.addCommandForGroup(gID, "REPAIRS: Purchase local repairs", theRoot, camp.redirectRepairs, {gName, gID, "repair"})
|
||||||
|
local c2 = missionCommands.addCommandForGroup(gID, "UPGRADE: Purchase local upgrades", theRoot, camp.redirectUpgrades, {gName, gID, "upgrade"})
|
||||||
|
end
|
||||||
|
|
||||||
function camp.processPlayers()
|
function camp.processPlayers()
|
||||||
-- install coms stump for all players. they will be switched in/out
|
-- install coms stump for all players. they will be switched in/out
|
||||||
-- whenever it is apropriate
|
-- whenever it is apropriate
|
||||||
for idx, gData in pairs(cfxMX.playerGroupByName) do
|
for idx, gData in pairs(cfxMX.playerGroupByName) do
|
||||||
gID = gData.groupId
|
gID = gData.groupId
|
||||||
gName = gData.name
|
gName = gData.name
|
||||||
local theRoot = missionCommands.addSubMenuForGroup(gID, "Funds / Repairs / Upgrades")
|
--[[-- local theRoot = missionCommands.addSubMenuForGroup(gID, "Funds / Repairs / Upgrades")
|
||||||
camp.roots[gName] = theRoot
|
camp.roots[gName] = theRoot
|
||||||
local c00 = missionCommands.addCommandForGroup(gID, "Theatre Overview", theRoot, camp.redirectTFunds, {gName, gID, "tfunds"})
|
local c00 = missionCommands.addCommandForGroup(gID, "Theatre Overview", theRoot, camp.redirectTFunds, {gName, gID, "tfunds"})
|
||||||
local c0 = missionCommands.addCommandForGroup(gID, "Local Funds & Status Overview", theRoot, camp.redirectFunds, {gName, gID, "funds"})
|
local c0 = missionCommands.addCommandForGroup(gID, "Local Funds & Status Overview", theRoot, camp.redirectFunds, {gName, gID, "funds"})
|
||||||
local c1 = missionCommands.addCommandForGroup(gID, "REPAIRS: Purchase local repairs", theRoot, camp.redirectRepairs, {gName, gID, "repair"})
|
local c1 = missionCommands.addCommandForGroup(gID, "REPAIRS: Purchase local repairs", theRoot, camp.redirectRepairs, {gName, gID, "repair"})
|
||||||
local c2 = missionCommands.addCommandForGroup(gID, "UPGRADE: Purchase local upgrades", theRoot, camp.redirectUpgrades, {gName, gID, "upgrade"})
|
local c2 = missionCommands.addCommandForGroup(gID, "UPGRADE: Purchase local upgrades", theRoot, camp.redirectUpgrades, {gName, gID, "upgrade"}) --]]--
|
||||||
|
camp.installComsFor(gID, gName)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
cfxMX = {}
|
cfxMX = {}
|
||||||
cfxMX.version = "2.0.2"
|
cfxMX.version = "2.1.0"
|
||||||
cfxMX.verbose = false
|
cfxMX.verbose = false
|
||||||
--[[--
|
--[[--
|
||||||
Mission data decoder. Access to ME-built mission structures
|
Mission data decoder. Access to ME-built mission structures
|
||||||
@@ -13,7 +13,12 @@ cfxMX.verbose = false
|
|||||||
- harmonized with cfxGroups
|
- harmonized with cfxGroups
|
||||||
2.0.1 - groupHotByName
|
2.0.1 - groupHotByName
|
||||||
2.0.2 - partOfGroupDataInZone(), allGroupsInZoneByData() from milHelo
|
2.0.2 - partOfGroupDataInZone(), allGroupsInZoneByData() from milHelo
|
||||||
|
2.0.3 - allGroupsInZoneByData supports type filtering
|
||||||
|
2.1.0 - support for dynamically spawning player unit detection
|
||||||
|
- new isDynamicPlayer()
|
||||||
|
- new isMEPlayer()
|
||||||
|
- new isMEPlayerGroup()
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
cfxMX.groupNamesByID = {}
|
cfxMX.groupNamesByID = {}
|
||||||
cfxMX.groupIDbyName = {}
|
cfxMX.groupIDbyName = {}
|
||||||
@@ -37,16 +42,14 @@ cfxMX.playerUnit2Group = {} -- returns a group data for player units.
|
|||||||
|
|
||||||
cfxMX.groups = {} -- all groups indexed b yname, cfxGroups folded into cfxMX
|
cfxMX.groups = {} -- all groups indexed b yname, cfxGroups folded into cfxMX
|
||||||
--[[-- group objects are
|
--[[-- group objects are
|
||||||
{
|
{
|
||||||
name= "",
|
name= "",
|
||||||
coalition = "" (red, blue, neutral),
|
coalition = "" (red, blue, neutral),
|
||||||
coanum = # (0, 1, 2 for neutral, red, blue)
|
coanum = # (0, 1, 2 for neutral, red, blue)
|
||||||
category = "" (helicopter, ship, plane, vehicle, static),
|
category = "" (helicopter, ship, plane, vehicle, static),
|
||||||
hasPlayer = true/false,
|
hasPlayer = true/false,
|
||||||
playerUnits = {} (for each player unit in group: name, point, action)
|
playerUnits = {} (for each player unit in group: name, point, action)
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
function cfxMX.getGroupFromDCSbyName(aName, fetchOriginal)
|
function cfxMX.getGroupFromDCSbyName(aName, fetchOriginal)
|
||||||
if not fetchOriginal then fetchOriginal = false end
|
if not fetchOriginal then fetchOriginal = false end
|
||||||
@@ -359,11 +362,14 @@ function cfxMX.partOfGroupDataInZone(theZone, theUnits) -- move to mx?
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
function cfxMX.allGroupsInZoneByData(theZone) -- returns groups indexed by name and count
|
function cfxMX.allGroupsInZoneByData(theZone, cat) -- returns groups indexed by name and count
|
||||||
|
if not cat then cat = {"helicopter", "ship", "plane", "vehicle" } end
|
||||||
|
if type(cat) == "string" then cat = {cat} end
|
||||||
local theGroupsInZone = {}
|
local theGroupsInZone = {}
|
||||||
local count = 0
|
local count = 0
|
||||||
for groupName, groupData in pairs(cfxMX.groupDataByName) do
|
for groupName, groupData in pairs(cfxMX.groupDataByName) do
|
||||||
if groupData.units then
|
local gType = cfxMX.groupTypeByName[groupName]
|
||||||
|
if dcsCommon.arrayContainsString(cat, gType) and groupData.units then
|
||||||
if cfxMX.partOfGroupDataInZone(theZone, groupData.units) then
|
if cfxMX.partOfGroupDataInZone(theZone, groupData.units) then
|
||||||
theGroupsInZone[groupName] = groupData -- DATA! work on clones!
|
theGroupsInZone[groupName] = groupData -- DATA! work on clones!
|
||||||
count = count + 1
|
count = count + 1
|
||||||
@@ -375,7 +381,36 @@ function cfxMX.allGroupsInZoneByData(theZone) -- returns groups indexed by name
|
|||||||
end
|
end
|
||||||
return theGroupsInZone, count
|
return theGroupsInZone, count
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function cfxMX.isDynamicPlayer(theUnit)
|
||||||
|
if not theUnit then return false end
|
||||||
|
if not theUnit.getName then return false end
|
||||||
|
if not theUnit.getPlayerName then return false end
|
||||||
|
if not theUnit:getPlayerName() then return false end
|
||||||
|
local uName = theUnit:getName()
|
||||||
|
if cfxMX.playerUnitByName[uName] then return false end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function cfxMX.isMEPlayer(theUnit)
|
||||||
|
if not theUnit then return false end
|
||||||
|
if not theUnit.getName then return false end
|
||||||
|
if not theUnit.getPlayerName then return false end
|
||||||
|
if not theUnit:getPlayerName() then return false end
|
||||||
|
local uName = theUnit:getName()
|
||||||
|
if cfxMX.playerUnitByName[uName] then return true end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function cfxMX.isMEPlayerGroup(theUnit)
|
||||||
|
if not theUnit then return false end
|
||||||
|
if not theUnit.getName then return end
|
||||||
|
if not theUnit.getPlayerName then return end
|
||||||
|
local uName = theUnit:getName()
|
||||||
|
if cfxMX.playerUnitByName[uName] then return true end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
function cfxMX.start()
|
function cfxMX.start()
|
||||||
cfxMX.createCrossReferences()
|
cfxMX.createCrossReferences()
|
||||||
if cfxMX.verbose then
|
if cfxMX.verbose then
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
cfxZones = {}
|
cfxZones = {}
|
||||||
cfxZones.version = "4.3.6"
|
cfxZones.version = "4.4.2"
|
||||||
|
|
||||||
-- cf/x zone management module
|
-- cf/x zone management module
|
||||||
-- reads dcs zones and makes them accessible and mutable
|
-- reads dcs zones and makes them accessible and mutable
|
||||||
@@ -9,38 +9,6 @@ cfxZones.version = "4.3.6"
|
|||||||
--
|
--
|
||||||
|
|
||||||
--[[-- VERSION HISTORY
|
--[[-- VERSION HISTORY
|
||||||
- 4.0.0 - dmlZone OOP API started
|
|
||||||
- code revision / refactoring
|
|
||||||
- moved createPoint and copxPoint to dcsCommon, added bridging code
|
|
||||||
- re-routed all createPoint() invocations to dcsCommon
|
|
||||||
- removed anyPlayerInZone() because of cfxPlayer dependency
|
|
||||||
- numberArrayFromString() moved to dcsCommon, bridged
|
|
||||||
- flagArrayFromString() moved to dcsCommon, bridged
|
|
||||||
- doPollFlag() can differentiate between number method and string method
|
|
||||||
to enable passing an immediate negative value
|
|
||||||
- getNumberFromZoneProperty() enforces number return even on default
|
|
||||||
- immediate method switched to preceeding '#', to resolve conflict witzh
|
|
||||||
negative numbers, backwards compatibility with old (dysfunctional) method
|
|
||||||
- 4.0.1 - dmlZone:getName()
|
|
||||||
- 4.0.2 - removed verbosity from declutterZone (both versions)
|
|
||||||
- 4.0.3 - new processDynamicVZU()
|
|
||||||
- wildcard uses processDynamicVZU
|
|
||||||
- 4.0.4 - setFlagValue now supports multiple flags (OOP and classic)
|
|
||||||
- doSetFlagValue optimizations
|
|
||||||
- 4.0.5 - dynamicAB wildcard
|
|
||||||
- processDynamicValueVU
|
|
||||||
- 4.0.6 - hash mark forgotten QoL
|
|
||||||
- 4.0.7 - drawZone()
|
|
||||||
- 4.0.8 - markZoneWithObjects()
|
|
||||||
- cleanup
|
|
||||||
- markCenterWithObject
|
|
||||||
- markPointWithObject
|
|
||||||
- 4.0.9 - createPolyZone now correctly returns new zone
|
|
||||||
- createSimplePolyZone correctly passes location to createPolyZone
|
|
||||||
- createPolyZone now correctly sets zone.point
|
|
||||||
- createPolyZone now correctly inits dcsOrigin
|
|
||||||
- createCircleZone noew correctly inits dcsOrigin
|
|
||||||
- 4.0.10 - getBoolFromZoneProperty also supports "on" (=true) and "off" (=false)
|
|
||||||
- 4.1.0 - getBoolFromZoneProperty 'on/off' support for dml variant as well
|
- 4.1.0 - getBoolFromZoneProperty 'on/off' support for dml variant as well
|
||||||
- 4.1.1 - evalRemainder() updates
|
- 4.1.1 - evalRemainder() updates
|
||||||
- 4.1.2 - hash property missing warning
|
- 4.1.2 - hash property missing warning
|
||||||
@@ -49,7 +17,7 @@ cfxZones.version = "4.3.6"
|
|||||||
- small optimization for randomInRange()
|
- small optimization for randomInRange()
|
||||||
- randomDelayFromPositiveRange also allows 0
|
- randomDelayFromPositiveRange also allows 0
|
||||||
- 4.3.1 - new drawText() for zones
|
- 4.3.1 - new drawText() for zones
|
||||||
- dmlZones:getClosestZone() bridge
|
- dmlZone:getClosestZone() bridge
|
||||||
- 4.3.2 - new getListFromZoneProperty()
|
- 4.3.2 - new getListFromZoneProperty()
|
||||||
- 4.3.3 - hardened calculateZoneBounds
|
- 4.3.3 - hardened calculateZoneBounds
|
||||||
- 4.3.4 - rewrote zone bounds for poly zones
|
- 4.3.4 - rewrote zone bounds for poly zones
|
||||||
@@ -57,7 +25,13 @@ cfxZones.version = "4.3.6"
|
|||||||
- 4.3.6 - tiny optimization in isPointInsideQuad
|
- 4.3.6 - tiny optimization in isPointInsideQuad
|
||||||
- moving zone - hardening code for static objects
|
- moving zone - hardening code for static objects
|
||||||
- moving zones - now deriving dx, dy,uHeading from dcsCommon xref for linked zones
|
- moving zones - now deriving dx, dy,uHeading from dcsCommon xref for linked zones
|
||||||
|
- 4.3.7 - corrected bug in processDynamicValues for lookup table
|
||||||
|
- 4.4.0 - dmlZone:getCoalition()
|
||||||
|
- dmlZone:getTypeName()
|
||||||
|
- dmlZone supports masterOwner by default
|
||||||
|
- dmlZone:getCoalition() dereferences masterOwner once
|
||||||
|
-4.4.1 - better verbosity for error in doPollFlag()
|
||||||
|
-4.4.2 - twn support for wildcards <twn: > and <loc:>
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
--
|
--
|
||||||
@@ -81,6 +55,13 @@ function dmlZone:new(o)
|
|||||||
return o
|
return o
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- dmlZone compatibility with DCS MSE objects:
|
||||||
|
-- dmlZone:getName() -- returns zone.name attribute (from ME)
|
||||||
|
-- dmlZone:getPoint() -- returns current point or dmlPoint
|
||||||
|
-- dmlZone:getTypeName() -- returns "dmlZone"
|
||||||
|
-- dmlZone:getCoalition -- returns owner
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- CLASSIC INTERFACE
|
-- CLASSIC INTERFACE
|
||||||
--
|
--
|
||||||
@@ -1615,7 +1596,7 @@ function cfxZones.doPollFlag(theFlag, method, theZone) -- no OOP equivalent
|
|||||||
|
|
||||||
else
|
else
|
||||||
if method ~= "on" and method ~= "f=1" then
|
if method ~= "on" and method ~= "f=1" then
|
||||||
trigger.action.outText("+++zones: unknown method <" .. method .. "> - using 'on'", 30)
|
trigger.action.outText("+++zones: unknown method <" .. method .. "> for flag <" .. theFlag .. "> in zone <" .. theZone.name .. "> - setting to 1", 30)
|
||||||
end
|
end
|
||||||
-- default: on.
|
-- default: on.
|
||||||
-- trigger.action.setUserFlag(theFlag, 1)
|
-- trigger.action.setUserFlag(theFlag, 1)
|
||||||
@@ -2922,7 +2903,7 @@ function cfxZones.processDynamicValues(inMsg, theZone, msgResponses)
|
|||||||
-- access flag
|
-- access flag
|
||||||
local val = cfxZones.getFlagValue(param, theZone)
|
local val = cfxZones.getFlagValue(param, theZone)
|
||||||
if not val or (val < 1) then val = 1 end
|
if not val or (val < 1) then val = 1 end
|
||||||
if val > msgResponses then val = msgResponses end
|
if val > #msgResponses then val = #msgResponses end
|
||||||
|
|
||||||
val = msgResponses[val]
|
val = msgResponses[val]
|
||||||
val = dcsCommon.trim(val)
|
val = dcsCommon.trim(val)
|
||||||
@@ -2968,7 +2949,7 @@ end
|
|||||||
|
|
||||||
-- process <lat/lon/ele/mgrs/lle/latlon/alt/vel/hdg/rhdg/type/player: zone/unit>
|
-- process <lat/lon/ele/mgrs/lle/latlon/alt/vel/hdg/rhdg/type/player: zone/unit>
|
||||||
function cfxZones.processDynamicLoc(inMsg, imperialUnits, responses)
|
function cfxZones.processDynamicLoc(inMsg, imperialUnits, responses)
|
||||||
local locales = {"lat", "lon", "ele", "mgrs", "lle", "latlon", "alt", "vel", "hdg", "rhdg", "type", "player"}
|
local locales = {"lat", "lon", "ele", "mgrs", "lle", "latlon", "alt", "vel", "hdg", "rhdg", "type", "player", "twn", "loc"}
|
||||||
local outMsg = inMsg
|
local outMsg = inMsg
|
||||||
local uHead = 0
|
local uHead = 0
|
||||||
for idx, aLocale in pairs(locales) do
|
for idx, aLocale in pairs(locales) do
|
||||||
@@ -3053,6 +3034,23 @@ function cfxZones.processDynamicLoc(inMsg, imperialUnits, responses)
|
|||||||
elseif aLocale == "rhdg" and (responses) then
|
elseif aLocale == "rhdg" and (responses) then
|
||||||
local offset = cfxZones.rspMapper360(uHead, #responses)
|
local offset = cfxZones.rspMapper360(uHead, #responses)
|
||||||
locString = dcsCommon.trim(responses[offset])
|
locString = dcsCommon.trim(responses[offset])
|
||||||
|
elseif aLocale == "twn" then
|
||||||
|
if twn and towns then locString = twn.closestTownTo(thePoint)
|
||||||
|
else locString = "!twn!" end
|
||||||
|
elseif aLocale == "loc" then
|
||||||
|
if twn and towns then
|
||||||
|
local name, data, dist = twn.closestTownTo(thePoint)
|
||||||
|
local units = "km"
|
||||||
|
local mdist= dist * 0.539957
|
||||||
|
dist = math.floor(dist/100) / 10
|
||||||
|
mdist = math.floor(mdist/100) / 10
|
||||||
|
if imperialUnits then
|
||||||
|
dist = mdist
|
||||||
|
units = "nm"
|
||||||
|
end
|
||||||
|
local bear = dcsCommon.compassPositionOfARelativeToB(thePoint, data.p)
|
||||||
|
locString = dist .. units .. " " .. bear .. " of " .. name
|
||||||
|
else locString = "!twn!" end
|
||||||
else
|
else
|
||||||
-- we have mgrs
|
-- we have mgrs
|
||||||
local grid = coord.LLtoMGRS(coord.LOtoLL(thePoint))
|
local grid = coord.LLtoMGRS(coord.LOtoLL(thePoint))
|
||||||
@@ -3345,6 +3343,24 @@ function dmlZone:getName() -- no cfxZones.bridge!
|
|||||||
return self.name
|
return self.name
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function dmlZone:getCoalition()
|
||||||
|
-- automatically support masterOwner. Warning: cloners etc can reference itself!
|
||||||
|
if self.masterOwner then return self.masterOwner.owner end -- zone must exist
|
||||||
|
return self.owner
|
||||||
|
end
|
||||||
|
|
||||||
|
function cfxZones.getCoalition(theZone)
|
||||||
|
return theZone:getCoalition()
|
||||||
|
end
|
||||||
|
|
||||||
|
function dmlZone:getTypeName()
|
||||||
|
return "dmlZone"
|
||||||
|
end
|
||||||
|
|
||||||
|
function cfxZones.getTypeName(theZone)
|
||||||
|
return theZone:getTypeName()
|
||||||
|
end
|
||||||
|
|
||||||
function cfxZones.linkUnitToZone(theUnit, theZone, dx, dy) -- note: dy is really Z, don't get confused!!!!
|
function cfxZones.linkUnitToZone(theUnit, theZone, dx, dy) -- note: dy is really Z, don't get confused!!!!
|
||||||
theZone.linkedUnit = theUnit
|
theZone.linkedUnit = theUnit
|
||||||
if not dx then dx = 0 end
|
if not dx then dx = 0 end
|
||||||
@@ -3693,6 +3709,20 @@ function cfxZones.init()
|
|||||||
-- much like verbose, all zones have owner
|
-- much like verbose, all zones have owner
|
||||||
for n, aZone in pairs(cfxZones.zones) do
|
for n, aZone in pairs(cfxZones.zones) do
|
||||||
aZone.owner = cfxZones.getCoalitionFromZoneProperty(aZone, "owner", 0)
|
aZone.owner = cfxZones.getCoalitionFromZoneProperty(aZone, "owner", 0)
|
||||||
|
|
||||||
|
if aZone:hasProperty("masterOwner") then
|
||||||
|
local mo = aZone:getStringFromZoneProperty("masterOwner", "forgotten master")
|
||||||
|
mo = dcsCommon.trim(mo)
|
||||||
|
if mo == "*" then mo = aZone.name end
|
||||||
|
local mz = cfxZones.getZoneByName(mo)
|
||||||
|
if not mz then
|
||||||
|
trigger.action.outText("+++fcxZones: WARNING: Master Owner <" .. mo .. "> for zone <" .. aZone.name .. "> does not exist!", 30)
|
||||||
|
else
|
||||||
|
aZone.masterOwner = mz
|
||||||
|
aZone.owner = mz.owner
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- enable all zone's verbose flags if present
|
-- enable all zone's verbose flags if present
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
cloneZones = {}
|
cloneZones = {}
|
||||||
cloneZones.version = "2.3.0"
|
cloneZones.version = "2.4.0"
|
||||||
cloneZones.verbose = false
|
cloneZones.verbose = false
|
||||||
cloneZones.requiredLibs = {
|
cloneZones.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
@@ -56,6 +56,7 @@ cloneZones.respawnOnGroupID = true
|
|||||||
(undocumented, just to provide lazy people with a migration
|
(undocumented, just to provide lazy people with a migration
|
||||||
path) with wiper module
|
path) with wiper module
|
||||||
- using "wipe?" will now create a warning
|
- using "wipe?" will now create a warning
|
||||||
|
2.4.0 - reworked masterOwner to fit with dmlZone
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
--
|
--
|
||||||
@@ -268,6 +269,7 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
|
|||||||
theZone.cloneMethod = theZone:getStringFromZoneProperty("method", "inc") -- note string on number default
|
theZone.cloneMethod = theZone:getStringFromZoneProperty("method", "inc") -- note string on number default
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[[--
|
||||||
if theZone:hasProperty("masterOwner") then
|
if theZone:hasProperty("masterOwner") then
|
||||||
theZone.masterOwner = theZone:getStringFromZoneProperty( "masterOwner", "*")
|
theZone.masterOwner = theZone:getStringFromZoneProperty( "masterOwner", "*")
|
||||||
theZone.masterOwner = dcsCommon.trim(theZone.masterOwner)
|
theZone.masterOwner = dcsCommon.trim(theZone.masterOwner)
|
||||||
@@ -285,7 +287,9 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
|
|||||||
if not theMaster then
|
if not theMaster then
|
||||||
trigger.action.outText("clnZ: WARNING: cloner's <" .. theZone.name .. "> master owner named <" .. theZone.masterOwner .. "> does not exist!", 30)
|
trigger.action.outText("clnZ: WARNING: cloner's <" .. theZone.name .. "> master owner named <" .. theZone.masterOwner .. "> does not exist!", 30)
|
||||||
end
|
end
|
||||||
|
theZone.masterOwner = theMaster
|
||||||
end
|
end
|
||||||
|
--]]--
|
||||||
|
|
||||||
theZone.turn = theZone:getNumberFromZoneProperty("turn", 0)
|
theZone.turn = theZone:getNumberFromZoneProperty("turn", 0)
|
||||||
|
|
||||||
@@ -692,7 +696,8 @@ function cloneZones.sameIDUnitData(theData)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function cloneZones.resolveOwnership(spawnZone, ctry)
|
function cloneZones.resolveOwnership(spawnZone, ctry)
|
||||||
if not spawnZone.masterOwner then return ctry end
|
if not spawnZone.masterOwner then return ctry end -- old code
|
||||||
|
--[[--
|
||||||
local masterZone = cfxZones.getZoneByName(spawnZone.masterOwner)
|
local masterZone = cfxZones.getZoneByName(spawnZone.masterOwner)
|
||||||
if not masterZone then
|
if not masterZone then
|
||||||
trigger.action.outText("+++clnZ: cloner " .. spawnZone.name .. " could not find master owner <" .. spawnZone.masterOwner .. ">", 30)
|
trigger.action.outText("+++clnZ: cloner " .. spawnZone.name .. " could not find master owner <" .. spawnZone.masterOwner .. ">", 30)
|
||||||
@@ -702,8 +707,10 @@ function cloneZones.resolveOwnership(spawnZone, ctry)
|
|||||||
if not masterZone.owner then
|
if not masterZone.owner then
|
||||||
return ctry
|
return ctry
|
||||||
end
|
end
|
||||||
|
--]]--
|
||||||
|
|
||||||
ctry = dcsCommon.getACountryForCoalition(masterZone.owner)
|
-- ctry = dcsCommon.getACountryForCoalition(masterZone.owner)
|
||||||
|
ctry = dcsCommon.getACountryForCoalition(spawnZone:getCoalition())
|
||||||
return ctry
|
return ctry
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1694,6 +1701,8 @@ function cloneZones.hasLiveUnits(theZone)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function cloneZones.resolveOwningCoalition(theZone)
|
function cloneZones.resolveOwningCoalition(theZone)
|
||||||
|
return theZone:getCoalition()
|
||||||
|
--[[--
|
||||||
if not theZone.masterOwner then return theZone.owner end
|
if not theZone.masterOwner then return theZone.owner end
|
||||||
local masterZone = cfxZones.getZoneByName(theZone.masterOwner)
|
local masterZone = cfxZones.getZoneByName(theZone.masterOwner)
|
||||||
if not masterZone then
|
if not masterZone then
|
||||||
@@ -1701,6 +1710,7 @@ function cloneZones.resolveOwningCoalition(theZone)
|
|||||||
return theZone.owner
|
return theZone.owner
|
||||||
end
|
end
|
||||||
return masterZone.owner
|
return masterZone.owner
|
||||||
|
--]]--
|
||||||
end
|
end
|
||||||
|
|
||||||
function cloneZones.getRequestableClonersInRange(aPoint, aRange, aSide)
|
function cloneZones.getRequestableClonersInRange(aPoint, aRange, aSide)
|
||||||
|
|||||||
1017
modules/convoy.lua
1017
modules/convoy.lua
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
csarManager = {}
|
csarManager = {}
|
||||||
csarManager.version = "4.0.0"
|
csarManager.version = "4.2.1"
|
||||||
csarManager.ups = 1
|
csarManager.ups = 1
|
||||||
|
|
||||||
--[[-- VERSION HISTORY
|
--[[-- VERSION HISTORY
|
||||||
@@ -46,7 +46,11 @@ csarManager.ups = 1
|
|||||||
3.4.0 - global timeLimit option in config zone
|
3.4.0 - global timeLimit option in config zone
|
||||||
- fixes expiration bug when persisting data
|
- fixes expiration bug when persisting data
|
||||||
4.0.0 - support for mainMenu
|
4.0.0 - support for mainMenu
|
||||||
|
4.0.1 - increased verbosity
|
||||||
|
- fix for Jul-11 2024 DCS bugs
|
||||||
|
4.1.0 - support for DCS 2.9.6 dynamic spawns
|
||||||
|
4.2.0 - automatically support twn if present
|
||||||
|
4.2.1 - added Chinook to csar default set (via common)
|
||||||
|
|
||||||
INTEGRATES AUTOMATICALLY WITH playerScore
|
INTEGRATES AUTOMATICALLY WITH playerScore
|
||||||
INTEGRATES WITH LIMITED AIRFRAMES
|
INTEGRATES WITH LIMITED AIRFRAMES
|
||||||
@@ -64,6 +68,8 @@ csarManager.requiredLibs = {
|
|||||||
|
|
||||||
-- unitConfigs contain the config data for any helicopter
|
-- unitConfigs contain the config data for any helicopter
|
||||||
-- currently in the game. The Array is indexed by unit name
|
-- currently in the game. The Array is indexed by unit name
|
||||||
|
-- requires single-unit player groups
|
||||||
|
-- compatible with DCS 2.9.6 dcs dynamic spawns
|
||||||
csarManager.unitConfigs = {}
|
csarManager.unitConfigs = {}
|
||||||
|
|
||||||
--
|
--
|
||||||
@@ -181,7 +187,6 @@ function csarManager.createCSARMissionData(point, theSide, freq, name, numCrew,
|
|||||||
end
|
end
|
||||||
|
|
||||||
if csarManager.useRanks then
|
if csarManager.useRanks then
|
||||||
-- local ranks = csarManager.ranks -- {"Lt", "Lt", "Lt", "Col", "Cpt", "WO", "WO"}
|
|
||||||
local myRank = dcsCommon.pickRandom(csarManager.ranks)
|
local myRank = dcsCommon.pickRandom(csarManager.ranks)
|
||||||
name = myRank .. " " .. name
|
name = myRank .. " " .. name
|
||||||
end
|
end
|
||||||
@@ -243,7 +248,6 @@ function csarManager.removeMission(theMission, pickup)
|
|||||||
if aMission ~= theMission then
|
if aMission ~= theMission then
|
||||||
table.insert(newMissions, aMission)
|
table.insert(newMissions, aMission)
|
||||||
else
|
else
|
||||||
-- csarManager.invokeRemovedMissionCallbacks(theMission)
|
|
||||||
if pickup then
|
if pickup then
|
||||||
csarManager.invokePickUpCallbacks(theMission)
|
csarManager.invokePickUpCallbacks(theMission)
|
||||||
end
|
end
|
||||||
@@ -296,6 +300,7 @@ end
|
|||||||
|
|
||||||
|
|
||||||
function csarManager.getUnitConfig(theUnit) -- will create new config if not existing
|
function csarManager.getUnitConfig(theUnit) -- will create new config if not existing
|
||||||
|
-- compatible with dynamic spawns for DCS 2.9.6
|
||||||
if not theUnit then
|
if not theUnit then
|
||||||
trigger.action.outText("+++csar: nil unit in get config!", 30)
|
trigger.action.outText("+++csar: nil unit in get config!", 30)
|
||||||
return nil
|
return nil
|
||||||
@@ -331,15 +336,22 @@ function csarManager:onEvent(event)
|
|||||||
|
|
||||||
if not dcsCommon.isPlayerUnit(theUnit) then return end -- not a player unit
|
if not dcsCommon.isPlayerUnit(theUnit) then return end -- not a player unit
|
||||||
|
|
||||||
|
if csarManager.verbose then
|
||||||
|
trigger.action.outText("csarM: player event <" .. event.id .. "> -- (=<" .. dcsCommon.event2text(event.id) .. ">)", 30)
|
||||||
|
end
|
||||||
|
|
||||||
-- only proceed if troop carrier (no more helo checks, all troop carriers, so osprey and harrier can be used if so desired)
|
-- only proceed if troop carrier (no more helo checks, all troop carriers, so osprey and harrier can be used if so desired)
|
||||||
if not dcsCommon.isTroopCarrier(theUnit, csarManager.troopCarriers) then return end
|
if not dcsCommon.isTroopCarrier(theUnit, csarManager.troopCarriers) then return end
|
||||||
|
|
||||||
local ID = event.id
|
local ID = event.id
|
||||||
if ID == 4 then -- landed
|
if ID == 4 or ID == 55 then -- landed, runway touch
|
||||||
|
if csarManager.verbose then
|
||||||
|
trigger.action.outText("land event " .. ID .. "received.", 30)
|
||||||
|
end
|
||||||
csarManager.heloLanded(theUnit)
|
csarManager.heloLanded(theUnit)
|
||||||
end
|
end
|
||||||
|
|
||||||
if ID == 3 or ID == 55 then -- take off, postponed take-off
|
if ID == 3 or ID == 54 then -- take off, runway take-off
|
||||||
csarManager.heloDeparted(theUnit)
|
csarManager.heloDeparted(theUnit)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -354,18 +366,15 @@ function csarManager:onEvent(event)
|
|||||||
csarManager.setCommsMenu(theUnit)
|
csarManager.setCommsMenu(theUnit)
|
||||||
-- we also need to make sure that there are no
|
-- we also need to make sure that there are no
|
||||||
-- more troopsOnBoard
|
-- more troopsOnBoard
|
||||||
|
|
||||||
local myName = theUnit:getName()
|
local myName = theUnit:getName()
|
||||||
local conf = csarManager.getUnitConfig(theUnit)
|
local conf = csarManager.getUnitConfig(theUnit)
|
||||||
conf.unit = theUnit
|
conf.unit = theUnit
|
||||||
conf.troopsOnBoard = {}
|
conf.troopsOnBoard = {}
|
||||||
local totalMass = cargoSuper.calculateTotalMassFor(myName)
|
local totalMass = cargoSuper.calculateTotalMassFor(myName)
|
||||||
|
|
||||||
-- now also set cargo weight for the unit
|
-- now also set cargo weight for the unit
|
||||||
cargoSuper.removeAllMassForCargo(myName, "Evacuees") -- will allocate new empty table
|
cargoSuper.removeAllMassForCargo(myName, "Evacuees") -- will allocate new empty table
|
||||||
totalMass = cargoSuper.calculateTotalMassFor(myName)
|
totalMass = cargoSuper.calculateTotalMassFor(myName)
|
||||||
trigger.action.setUnitInternalCargo(myName, totalMass) -- super recalcs
|
trigger.action.setUnitInternalCargo(myName, totalMass) -- super recalcs
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -698,6 +707,8 @@ end
|
|||||||
|
|
||||||
|
|
||||||
function csarManager.setCommsMenu(theUnit)
|
function csarManager.setCommsMenu(theUnit)
|
||||||
|
-- add menu for this aircraft/group when it spawns
|
||||||
|
-- compatible with dynamic spawns for DCS 2.9.6
|
||||||
if not theUnit then return end
|
if not theUnit then return end
|
||||||
if not theUnit:isExist() then return end
|
if not theUnit:isExist() then return end
|
||||||
|
|
||||||
@@ -821,11 +832,19 @@ function csarManager.doListCSARRequests(args)
|
|||||||
status = status .. " [" .. delta .. "]" -- remove me
|
status = status .. " [" .. delta .. "]" -- remove me
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
local locinfo = ""
|
||||||
|
if twn and towns then
|
||||||
|
local village, data, dist = twn.closestTownTo(mission.zone.point)
|
||||||
|
dist = dist * 0.539957 -- nm conversion
|
||||||
|
dist = math.floor(dist/100) / 10
|
||||||
|
local bear = dcsCommon.compassPositionOfARelativeToB(mission.zone.point, data.p)
|
||||||
|
locinfo = ", " .. dist .. "nm " .. bear .. " of " .. village
|
||||||
|
end
|
||||||
if csarManager.vectoring then
|
if csarManager.vectoring then
|
||||||
report = report .. "\n".. mission.name .. ", bearing " .. b .. ", " ..mission.dist .."nm, " .. " ADF " .. mission.freq * 10 .. " kHz - " .. status
|
report = report .. "\n".. mission.name .. locinfo .. ", bearing " .. b .. ", " ..mission.dist .."nm, " .. " ADF " .. mission.freq * 10 .. " kHz - " .. status
|
||||||
else
|
else
|
||||||
-- leave out vectoring
|
-- leave out vectoring
|
||||||
report = report .. "\n".. mission.name .. " ADF " .. mission.freq * 10 .. " kHz - " .. status
|
report = report .. "\n".. mission.name .. locinfo .. " ADF " .. mission.freq * 10 .. " kHz - " .. status
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
dcsCommon = {}
|
dcsCommon = {}
|
||||||
dcsCommon.version = "3.0.9"
|
dcsCommon.version = "3.1.2"
|
||||||
--[[-- VERSION HISTORY
|
--[[-- VERSION HISTORY
|
||||||
3.0.0 - removed bad bug in stringStartsWith, only relevant if caseSensitive is false
|
3.0.0 - removed bad bug in stringStartsWith, only relevant if caseSensitive is false
|
||||||
- point2text new intsOnly option
|
- point2text new intsOnly option
|
||||||
@@ -26,7 +26,9 @@ dcsCommon.version = "3.0.9"
|
|||||||
3.0.9 - new getOrigPositionByID()
|
3.0.9 - new getOrigPositionByID()
|
||||||
- unitName2ID[] reverse lookup
|
- unitName2ID[] reverse lookup
|
||||||
- unitName2Heading
|
- unitName2Heading
|
||||||
|
3.1.0 - updates to events, DCS update 7-11 2024 hardening
|
||||||
|
3.1.1 - added Chinook to troop carriers
|
||||||
|
3.1.2 - isTroopCarrier() hardening against DCS sillieness
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
-- dcsCommon is a library of common lua functions
|
-- dcsCommon is a library of common lua functions
|
||||||
@@ -39,7 +41,7 @@ dcsCommon.version = "3.0.9"
|
|||||||
|
|
||||||
-- globals
|
-- globals
|
||||||
dcsCommon.cbID = 0 -- callback id for simple callback scheduling
|
dcsCommon.cbID = 0 -- callback id for simple callback scheduling
|
||||||
dcsCommon.troopCarriers = {"Mi-8MT", "UH-1H", "Mi-24P", "OH58D"} -- Ka-50, Apache and Gazelle can't carry troops
|
dcsCommon.troopCarriers = {"Mi-8MT", "UH-1H", "Mi-24P", "OH58D", "CH-47Fbl1"} -- Ka-50, Apache and Gazelle can't carry troops
|
||||||
dcsCommon.coalitionSides = {0, 1, 2}
|
dcsCommon.coalitionSides = {0, 1, 2}
|
||||||
dcsCommon.maxCountry = 86 -- number of countries defined in total
|
dcsCommon.maxCountry = 86 -- number of countries defined in total
|
||||||
|
|
||||||
@@ -707,7 +709,7 @@ dcsCommon.version = "3.0.9"
|
|||||||
end
|
end
|
||||||
|
|
||||||
function dcsCommon.compassPositionOfARelativeToB(A, B)
|
function dcsCommon.compassPositionOfARelativeToB(A, B)
|
||||||
-- warning: is REVERSE in order for bearing, returns a string like 'Sorth', 'Southwest'
|
-- warning: is REVERSE in order for bearing, returns a string like 'North', 'Southwest'
|
||||||
if not A then return "***error:A***" end
|
if not A then return "***error:A***" end
|
||||||
if not B then return "***error:B***" end
|
if not B then return "***error:B***" end
|
||||||
local bearing = dcsCommon.bearingInDegreesFromAtoB(B, A) -- returns 0..360
|
local bearing = dcsCommon.bearingInDegreesFromAtoB(B, A) -- returns 0..360
|
||||||
@@ -2566,9 +2568,10 @@ end
|
|||||||
"Pilot Suicide", "player cap airfield", "emergency landing", "unit create task", -- 44
|
"Pilot Suicide", "player cap airfield", "emergency landing", "unit create task", -- 44
|
||||||
"unit delete task", "Simulation start", "weapon rearm", "weapon drop", -- 48
|
"unit delete task", "Simulation start", "weapon rearm", "weapon drop", -- 48
|
||||||
"unit task timeout", "unit task stage", -- 50
|
"unit task timeout", "unit task stage", -- 50
|
||||||
"subtask score", "extra score", "mission restart", "winner",
|
"subtask score", "mission restart", "winner", -- 53
|
||||||
"postponed takeoff", "postponed land", -- 56
|
"runway takeoff", "runway touchdown", "LMS Restart", -- 56
|
||||||
"max"}
|
"sim freeze", "sum unfreeze", "player start repair", "player end repair", --60
|
||||||
|
"max",} -- 61
|
||||||
if id > #events then return "Unknown (ID=" .. id .. ")" end
|
if id > #events then return "Unknown (ID=" .. id .. ")" end
|
||||||
return events[id]
|
return events[id]
|
||||||
end
|
end
|
||||||
@@ -2839,9 +2842,9 @@ end
|
|||||||
function dcsCommon.isTroopCarrier(theUnit, carriers)
|
function dcsCommon.isTroopCarrier(theUnit, carriers)
|
||||||
-- return true if conf can carry troups
|
-- return true if conf can carry troups
|
||||||
if not theUnit then return false end
|
if not theUnit then return false end
|
||||||
|
if not theUnit.getTypeName then return false end -- hardening against DCS sillieness
|
||||||
-- see if carriers contains "helo" and theUnit is a helo
|
-- see if carriers contains "helo" and theUnit is a helo
|
||||||
if dcsCommon.arrayContainsString(carriers, "helo") or dcsCommon.arrayContainsString(carriers, "helos")then
|
if dcsCommon.arrayContainsString(carriers, "helo") or dcsCommon.arrayContainsString(carriers, "helos") then
|
||||||
local grp = theUnit:getGroup()
|
local grp = theUnit:getGroup()
|
||||||
if grp:getCategory() == 1 then -- NOT category bug prone, is a group check
|
if grp:getCategory() == 1 then -- NOT category bug prone, is a group check
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
cfxHeloTroops = {}
|
cfxHeloTroops = {}
|
||||||
cfxHeloTroops.version = "3.0.4"
|
cfxHeloTroops.version = "3.1.0"
|
||||||
cfxHeloTroops.verbose = false
|
cfxHeloTroops.verbose = false
|
||||||
cfxHeloTroops.autoDrop = true
|
cfxHeloTroops.autoDrop = true
|
||||||
cfxHeloTroops.autoPickup = false
|
cfxHeloTroops.autoPickup = false
|
||||||
@@ -8,31 +8,6 @@ cfxHeloTroops.requestRange = 500 -- meters
|
|||||||
--
|
--
|
||||||
--[[--
|
--[[--
|
||||||
VERSION HISTORY
|
VERSION HISTORY
|
||||||
1.1.3 - repaired forgetting 'wait-' when loading/disembarking
|
|
||||||
1.1.4 - corrected coalition bug in deployTroopsFromHelicopter
|
|
||||||
2.0.0 - added weight change when troops enter and leave the helicopter
|
|
||||||
- idividual troop capa max per helicopter
|
|
||||||
2.0.1 - lib loader verification
|
|
||||||
- uses dcsCommon.isTroopCarrier(theUnit)
|
|
||||||
2.0.2 - can now deploy from spawners with "requestable" attribute
|
|
||||||
2.1.0 - supports config zones
|
|
||||||
- check spawner legality by types
|
|
||||||
- updated types to include 2.7.6 additions to infantry
|
|
||||||
- updated types to include stinger/manpads
|
|
||||||
2.2.0 - minor maintenance (dcsCommon)
|
|
||||||
- (re?) connected readConfigZone (wtf?)
|
|
||||||
- persistence support
|
|
||||||
- made legalTroops entrirely optional and defer to dcsComon else
|
|
||||||
2.3.0 - interface with owned zones and playerScore when
|
|
||||||
- combat-dropping troops into non-owned owned zone.
|
|
||||||
- prevent auto-load from pre-empting loading csar troops
|
|
||||||
2.3.1 - added ability to self-define troopCarriers via config
|
|
||||||
2.4.0 - added missing support for attackZone orders (destination)
|
|
||||||
- eliminated cfxPlayer module import and all dependencies
|
|
||||||
- added support for groupTracker / limbo
|
|
||||||
- removed restriction to only apply to helicopters in anticipation of the C-130 Hercules appearing in the game
|
|
||||||
2.4.1 - new actionSound attribute, sound plays to group whenever
|
|
||||||
troops have boarded or disembarked
|
|
||||||
3.0.0 - added requestable cloner support
|
3.0.0 - added requestable cloner support
|
||||||
- harmonized spawning invocations across cloners and spawners
|
- harmonized spawning invocations across cloners and spawners
|
||||||
- dmlZones
|
- dmlZones
|
||||||
@@ -41,14 +16,10 @@ cfxHeloTroops.requestRange = 500 -- meters
|
|||||||
3.0.2 - fixed a typo in in-air menu
|
3.0.2 - fixed a typo in in-air menu
|
||||||
3.0.3 - pointInZone check for insertion rather than radius
|
3.0.3 - pointInZone check for insertion rather than radius
|
||||||
3.0.4 - also handles picking up troops with orders "captureandhold"
|
3.0.4 - also handles picking up troops with orders "captureandhold"
|
||||||
|
3.0.5 - worked around a new issues accessing a unit's name
|
||||||
|
3.1.0 - compatible with DCS 2.9.6 dynamic spawning
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
--
|
|
||||||
-- cfxHeloTroops -- a module to pick up and drop infantry.
|
|
||||||
-- Can be used with ANY aircraft, configured by default to be
|
|
||||||
-- restricted to troop-carrying helicopters.
|
|
||||||
-- might be configure to apply to any type you want using the
|
|
||||||
-- configuration zone.
|
|
||||||
|
|
||||||
|
|
||||||
cfxHeloTroops.requiredLibs = {
|
cfxHeloTroops.requiredLibs = {
|
||||||
@@ -284,9 +255,8 @@ function cfxHeloTroops.addConfigMenu(conf)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function cfxHeloTroops.setCommsMenu(theUnit)
|
function cfxHeloTroops.setCommsMenu(theUnit)
|
||||||
-- depending on own load state, we set the command structure
|
-- compatible with DCS 2.9.6 dynamic spawns
|
||||||
-- it begins at 10-other, and has 'Assault Troops' as main menu with submenus
|
-- set F10 Other.. menu for group
|
||||||
-- as required
|
|
||||||
if not theUnit then return end
|
if not theUnit then return end
|
||||||
if not theUnit:isExist() then return end
|
if not theUnit:isExist() then return end
|
||||||
|
|
||||||
@@ -301,7 +271,7 @@ function cfxHeloTroops.setCommsMenu(theUnit)
|
|||||||
local group = theUnit:getGroup()
|
local group = theUnit:getGroup()
|
||||||
local id = group:getID()
|
local id = group:getID()
|
||||||
local conf = cfxHeloTroops.getUnitConfig(theUnit)
|
local conf = cfxHeloTroops.getUnitConfig(theUnit)
|
||||||
conf.id = id; -- we do this ALWAYS to it is current even after a crash
|
conf.id = id; -- we ALWAYS do this so it is current even after a crash
|
||||||
conf.unit = theUnit -- link back
|
conf.unit = theUnit -- link back
|
||||||
|
|
||||||
-- ok, first, if we don't have an F-10 menu, create one
|
-- ok, first, if we don't have an F-10 menu, create one
|
||||||
@@ -819,10 +789,10 @@ function cfxHeloTroops:onEvent(theEvent)
|
|||||||
local initiator = theEvent.initiator
|
local initiator = theEvent.initiator
|
||||||
if not initiator then return end -- not interested
|
if not initiator then return end -- not interested
|
||||||
local theUnit = initiator
|
local theUnit = initiator
|
||||||
local name = theUnit:getName()
|
|
||||||
-- see if this is a player aircraft
|
-- see if this is a player aircraft
|
||||||
if not theUnit.getPlayerName then return end -- not a player
|
if not theUnit.getPlayerName then return end -- not a player
|
||||||
if not theUnit:getPlayerName() then return end -- not a player
|
if not theUnit:getPlayerName() then return end -- not a player
|
||||||
|
local name = theUnit:getName() -- moved to a later
|
||||||
|
|
||||||
-- only for helicopters -- overridedden by troop carriers
|
-- only for helicopters -- overridedden by troop carriers
|
||||||
-- we don't check for cat any more, so any airframe
|
-- we don't check for cat any more, so any airframe
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
jtacGrpUI = {}
|
jtacGrpUI = {}
|
||||||
jtacGrpUI.version = "3.0.0"
|
jtacGrpUI.version = "3.1.0"
|
||||||
jtacGrpUI.requiredLibs = {
|
jtacGrpUI.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
"cfxZones",
|
"cfxZones",
|
||||||
@@ -12,6 +12,7 @@ jtacGrpUI.requiredLibs = {
|
|||||||
- clean-up
|
- clean-up
|
||||||
- jtacSound
|
- jtacSound
|
||||||
3.0.0 - support for attachTo:
|
3.0.0 - support for attachTo:
|
||||||
|
3.1.0 - support for DCS 2.0.6 dynamic player spwans
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
-- find & command cfxGroundTroops-based jtacs
|
-- find & command cfxGroundTroops-based jtacs
|
||||||
@@ -151,7 +152,7 @@ function jtacGrpUI.setCommsMenu(theGroup)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local conf = jtacGrpUI.getConfigForGroup(theGroup)
|
local conf = jtacGrpUI.getConfigForGroup(theGroup)
|
||||||
conf.id = theGroup:getID(); -- we always do this ALWAYS
|
conf.id = theGroup:getID(); -- we always do this
|
||||||
|
|
||||||
if jtacGrpUI.simpleCommands then
|
if jtacGrpUI.simpleCommands then
|
||||||
-- we install directly in F-10 other
|
-- we install directly in F-10 other
|
||||||
@@ -294,25 +295,29 @@ function jtacGrpUI:onEvent(theEvent)
|
|||||||
if not theEvent then return end
|
if not theEvent then return end
|
||||||
local theUnit = theEvent.initiator
|
local theUnit = theEvent.initiator
|
||||||
if not theUnit then return end
|
if not theUnit then return end
|
||||||
|
if not theUnit.getName then return end -- dcs 2.9.6 jul-11 fix
|
||||||
local uName = theUnit:getName()
|
local uName = theUnit:getName()
|
||||||
if not theUnit.getPlayerName then return end
|
if not theUnit.getPlayerName then return end
|
||||||
if not theUnit:getPlayerName() then return end
|
if not theUnit:getPlayerName() then return end
|
||||||
-- we now have a player birth event.
|
local id = theEvent.id
|
||||||
local pName = theUnit:getPlayerName()
|
if id == 15 then
|
||||||
local theGroup = theUnit:getGroup()
|
-- we now have a player birth event.
|
||||||
if not theGroup then return end
|
local pName = theUnit:getPlayerName()
|
||||||
local gName = theGroup:getName()
|
local theGroup = theUnit:getGroup()
|
||||||
if not gName then return end
|
if not theGroup then return end
|
||||||
if jtacGrpUI.verbose then
|
local gName = theGroup:getName()
|
||||||
trigger.action.outText("+++jGUI: birth player. installing JTAC for <" .. pName .. "> on unit <" .. uName .. ">", 30)
|
if not gName then return end
|
||||||
|
if jtacGrpUI.verbose then
|
||||||
|
trigger.action.outText("+++jGUI: birth player. installing JTAC for <" .. pName .. "> on unit <" .. uName .. ">", 30)
|
||||||
|
end
|
||||||
|
local conf = jtacGrpUI.getConfigByGroupName(gName)
|
||||||
|
if conf then
|
||||||
|
jtacGrpUI.removeCommsFromConfig(conf) -- remove menus
|
||||||
|
jtacGrpUI.resetConfig(conf) -- re-init this group for when it re-appears
|
||||||
|
end
|
||||||
|
|
||||||
|
jtacGrpUI.setCommsMenu(theGroup)
|
||||||
end
|
end
|
||||||
local conf = jtacGrpUI.getConfigByGroupName(gName)
|
|
||||||
if conf then
|
|
||||||
jtacGrpUI.removeCommsFromConfig(conf) -- remove menus
|
|
||||||
jtacGrpUI.resetConfig(conf) -- re-init this group for when it re-appears
|
|
||||||
end
|
|
||||||
|
|
||||||
jtacGrpUI.setCommsMenu(theGroup)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
messenger = {}
|
messenger = {}
|
||||||
messenger.version = "3.1.0"
|
messenger.version = "3.2.0"
|
||||||
messenger.verbose = false
|
messenger.verbose = false
|
||||||
messenger.requiredLibs = {
|
messenger.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
@@ -13,6 +13,8 @@ messenger.messengers = {}
|
|||||||
3.0.0 - removed messenger, in?, f? attributes, harmonized on messenger?
|
3.0.0 - removed messenger, in?, f? attributes, harmonized on messenger?
|
||||||
3.1.0 - msgGroup supports multiple groups, separated by comma
|
3.1.0 - msgGroup supports multiple groups, separated by comma
|
||||||
- msgUnit supports multiple units, separated by comma
|
- msgUnit supports multiple units, separated by comma
|
||||||
|
3.2.0 - loc and twn wildcard support (inherited from zones)
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
function messenger.addMessenger(theZone)
|
function messenger.addMessenger(theZone)
|
||||||
@@ -165,7 +167,7 @@ end
|
|||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- reat attributes
|
-- read attributes
|
||||||
--
|
--
|
||||||
function messenger.createMessengerWithZone(theZone)
|
function messenger.createMessengerWithZone(theZone)
|
||||||
local aMessage = theZone:getStringFromZoneProperty("message", "")
|
local aMessage = theZone:getStringFromZoneProperty("message", "")
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
ownAll = {}
|
ownAll = {}
|
||||||
ownAll.version = "1.0.0"
|
ownAll.version = "1.1.0"
|
||||||
ownAll.verbose = false
|
ownAll.verbose = false
|
||||||
ownAll.requiredLibs = {
|
ownAll.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
@@ -9,7 +9,7 @@ ownAll.requiredLibs = {
|
|||||||
--[[--
|
--[[--
|
||||||
VERSION HISTORY
|
VERSION HISTORY
|
||||||
- 1.0.0 - Initial version
|
- 1.0.0 - Initial version
|
||||||
|
- 1.1.0 - dml:masterOwner / getCoalition() updates
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
ownAll.zones = {}
|
ownAll.zones = {}
|
||||||
@@ -69,13 +69,13 @@ function ownAll.calcState(theZone)
|
|||||||
local blueNum = 0
|
local blueNum = 0
|
||||||
local allSame = true
|
local allSame = true
|
||||||
if #theZone.zones < 1 then return -1, 0, 0 end
|
if #theZone.zones < 1 then return -1, 0, 0 end
|
||||||
local s = theZone.zones[1].owner
|
local s = theZone.zones[1]:getCoalition() -- owner
|
||||||
if not s then
|
if not s then
|
||||||
trigger.action.outText("+++oAll: zone <" .. theZone.zones[1].name .."> has no owner (?)", 30)
|
trigger.action.outText("+++oAll: zone <" .. theZone.zones[1].name .."> has no owner (?)", 30)
|
||||||
s = -1
|
s = -1
|
||||||
end
|
end
|
||||||
for idx, aZone in pairs (theZone.zones) do
|
for idx, aZone in pairs (theZone.zones) do
|
||||||
local s2 = aZone.owner
|
local s2 = aZone:getCoalition() -- .owner
|
||||||
if not s2 then
|
if not s2 then
|
||||||
trigger.action.outText("+++oAll: zone <" .. aZone.name .."> has no owner (?)", 30)
|
trigger.action.outText("+++oAll: zone <" .. aZone.name .."> has no owner (?)", 30)
|
||||||
s2 = -1
|
s2 = -1
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
cfxOwnedZones = {}
|
cfxOwnedZones = {}
|
||||||
cfxOwnedZones.version = "2.3.1"
|
cfxOwnedZones.version = "2.4.0"
|
||||||
cfxOwnedZones.verbose = false
|
cfxOwnedZones.verbose = false
|
||||||
cfxOwnedZones.announcer = true
|
cfxOwnedZones.announcer = true
|
||||||
cfxOwnedZones.name = "cfxOwnedZones"
|
cfxOwnedZones.name = "cfxOwnedZones"
|
||||||
@@ -43,6 +43,8 @@ cfxOwnedZones.name = "cfxOwnedZones"
|
|||||||
- title attribute
|
- title attribute
|
||||||
- code clean-up
|
- code clean-up
|
||||||
2.3.1 - restored getNearestOwnedZoneToPoint
|
2.3.1 - restored getNearestOwnedZoneToPoint
|
||||||
|
2.4.0 - dmlZones masterOwner update
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
cfxOwnedZones.requiredLibs = {
|
cfxOwnedZones.requiredLibs = {
|
||||||
"dcsCommon",
|
"dcsCommon",
|
||||||
@@ -180,6 +182,7 @@ function cfxOwnedZones.addOwnedZone(aZone)
|
|||||||
aZone.neutralFill = aZone:getRGBAVectorFromZoneProperty("neutralFill", cfxOwnedZones.neutralFill)
|
aZone.neutralFill = aZone:getRGBAVectorFromZoneProperty("neutralFill", cfxOwnedZones.neutralFill)
|
||||||
|
|
||||||
-- masterOwner
|
-- masterOwner
|
||||||
|
--[[--
|
||||||
if aZone:hasProperty("masterOwner") then
|
if aZone:hasProperty("masterOwner") then
|
||||||
local masterZone = aZone:getStringFromZoneProperty("masterOwner", "cfxNoneErr")
|
local masterZone = aZone:getStringFromZoneProperty("masterOwner", "cfxNoneErr")
|
||||||
local theMaster = cfxZones.getZoneByName(masterZone)
|
local theMaster = cfxZones.getZoneByName(masterZone)
|
||||||
@@ -193,6 +196,7 @@ function cfxOwnedZones.addOwnedZone(aZone)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
--]]--
|
||||||
|
|
||||||
aZone.announcer = aZone:getBoolFromZoneProperty("announcer", cfxZones.announcer)
|
aZone.announcer = aZone:getBoolFromZoneProperty("announcer", cfxZones.announcer)
|
||||||
if aZone:hasProperty("announce") then
|
if aZone:hasProperty("announce") then
|
||||||
@@ -348,7 +352,7 @@ function cfxOwnedZones.update()
|
|||||||
for idz, theZone in pairs(cfxOwnedZones.zones) do
|
for idz, theZone in pairs(cfxOwnedZones.zones) do
|
||||||
theZone.numRed = 0
|
theZone.numRed = 0
|
||||||
theZone.numBlue = 0
|
theZone.numBlue = 0
|
||||||
local lastOwner = theZone.owner
|
local lastOwner = theZone.owner -- do NOT use dml:getCoalition()!
|
||||||
if not lastOwner then
|
if not lastOwner then
|
||||||
trigger.action.outText("+++owdZ: WARNING - zone <" .. theZone.name .. "> has NIL owner", 30)
|
trigger.action.outText("+++owdZ: WARNING - zone <" .. theZone.name .. "> has NIL owner", 30)
|
||||||
return
|
return
|
||||||
@@ -472,7 +476,7 @@ function cfxOwnedZones.update()
|
|||||||
-- we do nothing
|
-- we do nothing
|
||||||
elseif theZone.masterOwner then
|
elseif theZone.masterOwner then
|
||||||
-- inherit from my master
|
-- inherit from my master
|
||||||
newOwner = theZone.masterOwner.owner
|
newOwner = theZone:getCoalition() -- theZone.masterOwner.owner
|
||||||
elseif theZone.numRed < 1 and theZone.numBlue < 1 then
|
elseif theZone.numRed < 1 and theZone.numBlue < 1 then
|
||||||
-- no troops here. Become neutral?
|
-- no troops here. Become neutral?
|
||||||
if theZone.numKeep < 1 then
|
if theZone.numKeep < 1 then
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
cfxPlayerScore = {}
|
cfxPlayerScore = {}
|
||||||
cfxPlayerScore.version = "3.3.0"
|
cfxPlayerScore.version = "3.3.1"
|
||||||
cfxPlayerScore.name = "cfxPlayerScore" -- compatibility with flag bangers
|
cfxPlayerScore.name = "cfxPlayerScore" -- compatibility with flag bangers
|
||||||
cfxPlayerScore.badSound = "Death BRASS.wav"
|
cfxPlayerScore.badSound = "Death BRASS.wav"
|
||||||
cfxPlayerScore.scoreSound = "Quest Snare 3.wav"
|
cfxPlayerScore.scoreSound = "Quest Snare 3.wav"
|
||||||
@@ -16,6 +16,8 @@ cfxPlayerScore.firstSave = true -- to force overwrite
|
|||||||
3.1.0 - shared data for persistence
|
3.1.0 - shared data for persistence
|
||||||
3.2.0 - integration with bank
|
3.2.0 - integration with bank
|
||||||
3.3.0 - case INsensitivity for all typeScore objects
|
3.3.0 - case INsensitivity for all typeScore objects
|
||||||
|
3.3.1 - fixes for DCS oddity in events after update
|
||||||
|
- cleanup
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
cfxPlayerScore.requiredLibs = {
|
cfxPlayerScore.requiredLibs = {
|
||||||
@@ -104,15 +106,12 @@ function cfxPlayerScore.featsForLocation(name, loc, coa, featType, killer, victi
|
|||||||
if theZone.featNum == 0 then
|
if theZone.featNum == 0 then
|
||||||
canAward = false
|
canAward = false
|
||||||
end
|
end
|
||||||
|
|
||||||
if theZone.featType ~= featType then
|
if theZone.featType ~= featType then
|
||||||
canAward = false
|
canAward = false
|
||||||
end
|
end
|
||||||
|
|
||||||
if not (theZone.coalition == 0 or theZone.coalition == coa) then
|
if not (theZone.coalition == 0 or theZone.coalition == coa) then
|
||||||
canAward = false
|
canAward = false
|
||||||
end
|
end
|
||||||
|
|
||||||
if featType == "PVP" then
|
if featType == "PVP" then
|
||||||
-- make sure kill is pvp kill
|
-- make sure kill is pvp kill
|
||||||
if not victim then canAward = false
|
if not victim then canAward = false
|
||||||
@@ -122,22 +121,17 @@ function cfxPlayerScore.featsForLocation(name, loc, coa, featType, killer, victi
|
|||||||
canAward = false
|
canAward = false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if not cfxZones.pointInZone(loc, theZone) then
|
if not cfxZones.pointInZone(loc, theZone) then
|
||||||
canAward = false
|
canAward = false
|
||||||
end
|
end
|
||||||
|
|
||||||
if theZone.ppOnce then
|
if theZone.ppOnce then
|
||||||
if theZone.awardedTo[name] then
|
if theZone.awardedTo[name] then
|
||||||
canAward = false
|
canAward = false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if canAward then
|
if canAward then
|
||||||
table.insert(theFeats, theZone) -- jupp, add it
|
table.insert(theFeats, theZone) -- jupp, add it
|
||||||
else
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
return theFeats
|
return theFeats
|
||||||
end
|
end
|
||||||
@@ -166,7 +160,11 @@ function cfxPlayerScore.preprocessWildcards(inMsg, aUnit, aVictim)
|
|||||||
theMsg = theMsg:gsub("<kplayer>", "unknown AI")
|
theMsg = theMsg:gsub("<kplayer>", "unknown AI")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
theMsg = theMsg:gsub("<unit>", aVictim:getName())
|
if aVictim.getName then
|
||||||
|
theMsg = theMsg:gsub("<unit>", aVictim:getName())
|
||||||
|
else
|
||||||
|
theMsg = theMsg:gsub("<unit>", "*?*") -- dcs oddity
|
||||||
|
end
|
||||||
theMsg = theMsg:gsub("<type>", aVictim:getTypeName())
|
theMsg = theMsg:gsub("<type>", aVictim:getTypeName())
|
||||||
-- victim may not have group. guard against that
|
-- victim may not have group. guard against that
|
||||||
-- happens if unit 'cooks off'
|
-- happens if unit 'cooks off'
|
||||||
@@ -205,16 +203,15 @@ function cfxPlayerScore.cat2BaseScore(inCat)
|
|||||||
if inCat == 2 then return cfxPlayerScore.ground end -- ground
|
if inCat == 2 then return cfxPlayerScore.ground end -- ground
|
||||||
if inCat == 3 then return cfxPlayerScore.ship end -- ship
|
if inCat == 3 then return cfxPlayerScore.ship end -- ship
|
||||||
if inCat == 4 then return cfxPlayerScore.train end -- train
|
if inCat == 4 then return cfxPlayerScore.train end -- train
|
||||||
|
|
||||||
trigger.action.outText("+++scr c2bs: unknown category for lookup: <" .. inCat .. ">, returning 1", 30)
|
trigger.action.outText("+++scr c2bs: unknown category for lookup: <" .. inCat .. ">, returning 1", 30)
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
||||||
function cfxPlayerScore.object2score(inVictim, killSide) -- does not have group
|
function cfxPlayerScore.object2score(inVictim, killSide) -- does not have group
|
||||||
if not inVictim then return 0 end
|
if not inVictim then return 0 end
|
||||||
if not killSide then killSide = -1 end
|
if not killSide then killSide = -1 end
|
||||||
local inName = inVictim:getName()
|
local inName
|
||||||
|
if inVictim.getName then inName = inVictim:getName() else inName = "*?*" end -- dcs oddity
|
||||||
if cfxPlayerScore.verbose then
|
if cfxPlayerScore.verbose then
|
||||||
trigger.action.outText("+++PScr: ob2sc entry to resolve name <" .. inName .. ">", 30)
|
trigger.action.outText("+++PScr: ob2sc entry to resolve name <" .. inName .. ">", 30)
|
||||||
end
|
end
|
||||||
@@ -251,13 +248,10 @@ function cfxPlayerScore.object2score(inVictim, killSide) -- does not have group
|
|||||||
objectScore = cfxPlayerScore.typeScore[theType:upper()]
|
objectScore = cfxPlayerScore.typeScore[theType:upper()]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if type(objectScore) == "string" then
|
if type(objectScore) == "string" then
|
||||||
objectScore = tonumber(objectScore)
|
objectScore = tonumber(objectScore)
|
||||||
end
|
end
|
||||||
|
|
||||||
if objectScore then return objectScore end
|
if objectScore then return objectScore end
|
||||||
|
|
||||||
-- we now try and get the general type of the killed object
|
-- we now try and get the general type of the killed object
|
||||||
local desc = inVictim:getDesc() -- Object.getDesc(inVictim)
|
local desc = inVictim:getDesc() -- Object.getDesc(inVictim)
|
||||||
local attributes = desc.attributes
|
local attributes = desc.attributes
|
||||||
@@ -268,7 +262,6 @@ function cfxPlayerScore.object2score(inVictim, killSide) -- does not have group
|
|||||||
if attributes["Ships"] then return cfxPlayerScore.ship end
|
if attributes["Ships"] then return cfxPlayerScore.ship end
|
||||||
-- trains can't be detected
|
-- trains can't be detected
|
||||||
end
|
end
|
||||||
|
|
||||||
if not objectScore then return 0 end
|
if not objectScore then return 0 end
|
||||||
return objectScore
|
return objectScore
|
||||||
end
|
end
|
||||||
@@ -277,34 +270,29 @@ function cfxPlayerScore.unit2score(inUnit)
|
|||||||
local vicGroup = inUnit:getGroup()
|
local vicGroup = inUnit:getGroup()
|
||||||
local vicCat = vicGroup:getCategory()-- group cat, not 2.9 affected
|
local vicCat = vicGroup:getCategory()-- group cat, not 2.9 affected
|
||||||
local vicType = inUnit:getTypeName()
|
local vicType = inUnit:getTypeName()
|
||||||
local vicName = inUnit:getName()
|
local vicName
|
||||||
|
if inUnit.getName then vicName = inUnit:getName() else vicName = "*?*" end
|
||||||
if type(vicName) == "number" then vicName = tostring(vicName) end
|
if type(vicName) == "number" then vicName = tostring(vicName) end
|
||||||
|
|
||||||
-- simply extend by adding items to the typescore table.concat
|
-- simply extend by adding items to the typescore table.concat
|
||||||
-- we first try by unit name. This allows individual
|
-- we first try by unit name. This allows individual
|
||||||
-- named hi-value targets to have individual scores
|
-- named hi-value targets to have individual scores
|
||||||
local uScore = cfxPlayerScore.typeScore[vicName:upper()]
|
local uScore = cfxPlayerScore.typeScore[vicName:upper()]
|
||||||
|
|
||||||
-- see if all members of group score
|
-- see if all members of group score
|
||||||
if (not uScore) and vicGroup then
|
if (not uScore) and vicGroup then
|
||||||
local grpName = vicGroup:getName()
|
local grpName = vicGroup:getName()
|
||||||
uScore = cfxPlayerScore.typeScore[grpName:upper()]
|
uScore = cfxPlayerScore.typeScore[grpName:upper()]
|
||||||
end
|
end
|
||||||
|
|
||||||
if not uScore then
|
if not uScore then
|
||||||
-- WE NOW TRY TO ACCESS BY VICTIM'S TYPE STRING
|
-- WE NOW TRY TO ACCESS BY VICTIM'S TYPE STRING
|
||||||
uScore = cfxPlayerScore.typeScore[vicType:upper()]
|
uScore = cfxPlayerScore.typeScore[vicType:upper()]
|
||||||
else
|
|
||||||
|
|
||||||
end
|
end
|
||||||
if type(uScore) == "string" then
|
if type(uScore) == "string" then
|
||||||
-- convert string to number
|
-- convert string to number
|
||||||
uScore = tonumber(uScore)
|
uScore = tonumber(uScore)
|
||||||
end
|
end
|
||||||
|
|
||||||
if not uScore then uScore = 0 end
|
if not uScore then uScore = 0 end
|
||||||
if uScore > 0 then return uScore end
|
if uScore > 0 then return uScore end
|
||||||
|
|
||||||
-- only apply base scores when the lookup did not give a result
|
-- only apply base scores when the lookup did not give a result
|
||||||
uScore = cfxPlayerScore.cat2BaseScore(vicCat)
|
uScore = cfxPlayerScore.cat2BaseScore(vicCat)
|
||||||
return uScore
|
return uScore
|
||||||
@@ -312,7 +300,7 @@ end
|
|||||||
|
|
||||||
function cfxPlayerScore.getPlayerScore(playerName)
|
function cfxPlayerScore.getPlayerScore(playerName)
|
||||||
local thePlayerScore = cfxPlayerScore.playerScore[playerName]
|
local thePlayerScore = cfxPlayerScore.playerScore[playerName]
|
||||||
if thePlayerScore == nil then
|
if not thePlayerScore then
|
||||||
thePlayerScore = {}
|
thePlayerScore = {}
|
||||||
thePlayerScore.name = playerName
|
thePlayerScore.name = playerName
|
||||||
thePlayerScore.score = 0 -- score
|
thePlayerScore.score = 0 -- score
|
||||||
@@ -379,7 +367,6 @@ function cfxPlayerScore.doLogTypeKill(playerName, thePlayerScore, theType)
|
|||||||
killCount = killCount + 1
|
killCount = killCount + 1
|
||||||
thePlayerScore.totalKills = thePlayerScore.totalKills + 1
|
thePlayerScore.totalKills = thePlayerScore.totalKills + 1
|
||||||
thePlayerScore.killTypes[theType] = killCount
|
thePlayerScore.killTypes[theType] = killCount
|
||||||
|
|
||||||
cfxPlayerScore.setPlayerScore(playerName, thePlayerScore)
|
cfxPlayerScore.setPlayerScore(playerName, thePlayerScore)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -390,14 +377,12 @@ function cfxPlayerScore.logKillForPlayer(playerName, theUnit)
|
|||||||
if not playerName then return end
|
if not playerName then return end
|
||||||
local thePlayerScore = cfxPlayerScore.getPlayerScore(playerName)
|
local thePlayerScore = cfxPlayerScore.getPlayerScore(playerName)
|
||||||
local theType = theUnit:getTypeName()
|
local theType = theUnit:getTypeName()
|
||||||
|
|
||||||
if cfxPlayerScore.deferred then
|
if cfxPlayerScore.deferred then
|
||||||
-- just queue it
|
-- just queue it
|
||||||
table.insert(thePlayerScore.killQueue, theType)
|
table.insert(thePlayerScore.killQueue, theType)
|
||||||
cfxPlayerScore.setPlayerScore(playerName, thePlayerScore) -- write-through. why? because it may be a new entry.
|
cfxPlayerScore.setPlayerScore(playerName, thePlayerScore) -- write-through. why? because it may be a new entry.
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
cfxPlayerScore.doLogTypeKill(playerName, thePlayerScore, theType)
|
cfxPlayerScore.doLogTypeKill(playerName, thePlayerScore, theType)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -410,9 +395,7 @@ function cfxPlayerScore.doLogFeat(playerName, thePlayerScore, theFeat)
|
|||||||
featCount = featCount + 1
|
featCount = featCount + 1
|
||||||
thePlayerScore.totalFeats = thePlayerScore.totalFeats + 1
|
thePlayerScore.totalFeats = thePlayerScore.totalFeats + 1
|
||||||
thePlayerScore.featTypes[theFeat] = featCount
|
thePlayerScore.featTypes[theFeat] = featCount
|
||||||
|
|
||||||
cfxPlayerScore.setPlayerScore(playerName, thePlayerScore)
|
cfxPlayerScore.setPlayerScore(playerName, thePlayerScore)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function cfxPlayerScore.logFeatForPlayer(playerName, theFeat, coa)
|
function cfxPlayerScore.logFeatForPlayer(playerName, theFeat, coa)
|
||||||
@@ -421,34 +404,29 @@ function cfxPlayerScore.logFeatForPlayer(playerName, theFeat, coa)
|
|||||||
if not theFeat then return end
|
if not theFeat then return end
|
||||||
if not playerName then return end
|
if not playerName then return end
|
||||||
-- access player's record. will alloc if new by itself
|
-- access player's record. will alloc if new by itself
|
||||||
|
|
||||||
if coa then
|
if coa then
|
||||||
local disclaim = ""
|
local disclaim = ""
|
||||||
if cfxPlayerScore.deferred then disclaim = " (award pending)" end
|
if cfxPlayerScore.deferred then disclaim = " (award pending)" end
|
||||||
trigger.action.outTextForCoalition(coa, playerName .. " achieved " .. theFeat .. disclaim, 30)
|
trigger.action.outTextForCoalition(coa, playerName .. " achieved " .. theFeat .. disclaim, 30)
|
||||||
trigger.action.outSoundForCoalition(coa, cfxPlayerScore.scoreSound)
|
trigger.action.outSoundForCoalition(coa, cfxPlayerScore.scoreSound)
|
||||||
end
|
end
|
||||||
|
|
||||||
local thePlayerScore = cfxPlayerScore.getPlayerScore(playerName)
|
local thePlayerScore = cfxPlayerScore.getPlayerScore(playerName)
|
||||||
if cfxPlayerScore.deferred then
|
if cfxPlayerScore.deferred then
|
||||||
table.insert(thePlayerScore.featQueue, theFeat)
|
table.insert(thePlayerScore.featQueue, theFeat)
|
||||||
cfxPlayerScore.setPlayerScore(playerName, thePlayerScore)
|
cfxPlayerScore.setPlayerScore(playerName, thePlayerScore)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
cfxPlayerScore.doLogFeat(playerName, thePlayerScore, theFeat)
|
cfxPlayerScore.doLogFeat(playerName, thePlayerScore, theFeat)
|
||||||
end
|
end
|
||||||
|
|
||||||
function cfxPlayerScore.playerScore2text(thePlayerScore, scoreOnly)
|
function cfxPlayerScore.playerScore2text(thePlayerScore, scoreOnly)
|
||||||
if not scoreOnly then scoreOnly = false end
|
if not scoreOnly then scoreOnly = false end
|
||||||
local desc = thePlayerScore.name .. " statistics:\n"
|
local desc = thePlayerScore.name .. " statistics:\n"
|
||||||
|
|
||||||
if cfxPlayerScore.reportScore then
|
if cfxPlayerScore.reportScore then
|
||||||
desc = desc .. " - score: ".. thePlayerScore.score .. " - total kills: " .. thePlayerScore.totalKills .. "\n"
|
desc = desc .. " - score: ".. thePlayerScore.score .. " - total kills: " .. thePlayerScore.totalKills .. "\n"
|
||||||
if scoreOnly then
|
if scoreOnly then
|
||||||
return desc
|
return desc
|
||||||
end
|
end
|
||||||
|
|
||||||
-- now go through all kills
|
-- now go through all kills
|
||||||
desc = desc .. "\nKills by type:\n"
|
desc = desc .. "\nKills by type:\n"
|
||||||
if dcsCommon.getSizeOfTable(thePlayerScore.killTypes) < 1 then
|
if dcsCommon.getSizeOfTable(thePlayerScore.killTypes) < 1 then
|
||||||
@@ -474,16 +452,13 @@ function cfxPlayerScore.playerScore2text(thePlayerScore, scoreOnly)
|
|||||||
desc = desc .. "\n"
|
desc = desc .. "\n"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxPlayerScore.reportScore and thePlayerScore.scoreaccu > 0 then
|
if cfxPlayerScore.reportScore and thePlayerScore.scoreaccu > 0 then
|
||||||
desc = desc .. "\n - unclaimed score: " .. thePlayerScore.scoreaccu .."\n"
|
desc = desc .. "\n - unclaimed score: " .. thePlayerScore.scoreaccu .."\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
local featCount = dcsCommon.getSizeOfTable(thePlayerScore.featQueue)
|
local featCount = dcsCommon.getSizeOfTable(thePlayerScore.featQueue)
|
||||||
if cfxPlayerScore.reportFeats and featCount > 0 then
|
if cfxPlayerScore.reportFeats and featCount > 0 then
|
||||||
desc = desc .. " - unclaimed feats: " .. featCount .."\n"
|
desc = desc .. " - unclaimed feats: " .. featCount .."\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
return desc
|
return desc
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -508,7 +483,6 @@ function cfxPlayerScore.scoreSummaryForPlayersOfCoalition(side)
|
|||||||
if count < 1 then
|
if count < 1 then
|
||||||
desc = desc .. " (No score yet)"
|
desc = desc .. " (No score yet)"
|
||||||
end
|
end
|
||||||
|
|
||||||
desc = desc .. "\n"
|
desc = desc .. "\n"
|
||||||
return desc
|
return desc
|
||||||
end
|
end
|
||||||
@@ -537,17 +511,14 @@ function cfxPlayerScore.scoreTextForAllPlayers(ranked)
|
|||||||
isFirst = false
|
isFirst = false
|
||||||
rank = rank + 1
|
rank = rank + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
if dcsCommon.getSizeOfTable(theScores) < 1 then
|
if dcsCommon.getSizeOfTable(theScores) < 1 then
|
||||||
theText = theText .. " (No score yet)\n"
|
theText = theText .. " (No score yet)\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxPlayerScore.reportCoalition then
|
if cfxPlayerScore.reportCoalition then
|
||||||
--theText = theText .. "\n"
|
--theText = theText .. "\n"
|
||||||
theText = theText .. "\nRED total: " .. cfxPlayerScore.coalitionScore[1]
|
theText = theText .. "\nRED total: " .. cfxPlayerScore.coalitionScore[1]
|
||||||
theText = theText .. "\nBLUE total: " .. cfxPlayerScore.coalitionScore[2]
|
theText = theText .. "\nBLUE total: " .. cfxPlayerScore.coalitionScore[2]
|
||||||
end
|
end
|
||||||
|
|
||||||
return theText
|
return theText
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -560,7 +531,11 @@ function cfxPlayerScore.isNamedUnit(theUnit)
|
|||||||
else
|
else
|
||||||
-- WARNING: NO EXIST CHECK DONE!
|
-- WARNING: NO EXIST CHECK DONE!
|
||||||
-- after kill, unit is dead, so will no longer exist!
|
-- after kill, unit is dead, so will no longer exist!
|
||||||
theName = theUnit:getName()
|
if theUnit.getName then
|
||||||
|
theName = theUnit:getName()
|
||||||
|
else
|
||||||
|
theName = "*?*"
|
||||||
|
end
|
||||||
if not theName then return false end
|
if not theName then return false end
|
||||||
end
|
end
|
||||||
if cfxPlayerScore.typeScore[theName:upper()] then
|
if cfxPlayerScore.typeScore[theName:upper()] then
|
||||||
@@ -576,9 +551,7 @@ function cfxPlayerScore.awardScoreTo(killSide, theScore, killerName)
|
|||||||
else
|
else
|
||||||
playerScore = cfxPlayerScore.updateScoreForPlayer(killerName, theScore)
|
playerScore = cfxPlayerScore.updateScoreForPlayer(killerName, theScore)
|
||||||
end
|
end
|
||||||
|
|
||||||
if not cfxPlayerScore.reportScore then return end
|
if not cfxPlayerScore.reportScore then return end
|
||||||
|
|
||||||
if cfxPlayerScore.announcer then
|
if cfxPlayerScore.announcer then
|
||||||
if (theScore > 0) and cfxPlayerScore.deferred then
|
if (theScore > 0) and cfxPlayerScore.deferred then
|
||||||
thePlayerRecord = cfxPlayerScore.getPlayerScore(killerName) -- re-read after write
|
thePlayerRecord = cfxPlayerScore.getPlayerScore(killerName) -- re-read after write
|
||||||
@@ -614,9 +587,9 @@ function cfxPlayerScore.preProcessor(theEvent)
|
|||||||
if theEvent.initiator == nil then
|
if theEvent.initiator == nil then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- check if this was FORMERLY a player plane
|
-- check if this was FORMERLY a player plane
|
||||||
local theUnit = theEvent.initiator
|
local theUnit = theEvent.initiator
|
||||||
|
if not theUnit.getName then return end -- fix for DCS update bug
|
||||||
local uName = theUnit:getName()
|
local uName = theUnit:getName()
|
||||||
if cfxPlayerScore.unit2player[uName] then
|
if cfxPlayerScore.unit2player[uName] then
|
||||||
-- this requires special IMMEDIATE handling when event is
|
-- this requires special IMMEDIATE handling when event is
|
||||||
@@ -627,19 +600,16 @@ function cfxPlayerScore.preProcessor(theEvent)
|
|||||||
theEvent.id == 30 or -- unit loss
|
theEvent.id == 30 or -- unit loss
|
||||||
theEvent.id == 6 then -- eject
|
theEvent.id == 6 then -- eject
|
||||||
-- these can lead to a pilot demerit
|
-- these can lead to a pilot demerit
|
||||||
--trigger.action.outText("PREPROC plane player extra event - possible death", 30)
|
|
||||||
-- event does NOT have a player
|
-- event does NOT have a player
|
||||||
cfxPlayerScore.handlePlayerDeath(theEvent)
|
cfxPlayerScore.handlePlayerDeath(theEvent)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- initiator must be player
|
-- initiator must be player
|
||||||
if not theUnit.getPlayerName or
|
if not theUnit.getPlayerName or
|
||||||
not theUnit:getPlayerName() then
|
not theUnit:getPlayerName() then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
if theEvent.id == 28 then
|
if theEvent.id == 28 then
|
||||||
-- we only are interested in kill events where
|
-- we only are interested in kill events where
|
||||||
-- there is a target
|
-- there is a target
|
||||||
@@ -650,20 +620,17 @@ function cfxPlayerScore.preProcessor(theEvent)
|
|||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- if there are kill zones, we filter all kills that happen outside of kill zones
|
-- if there are kill zones, we filter all kills that happen outside of kill zones
|
||||||
if #cfxPlayerScore.killZones > 0 then
|
if #cfxPlayerScore.killZones > 0 then
|
||||||
local pLoc = theUnit:getPoint()
|
local pLoc = theUnit:getPoint()
|
||||||
local tLoc = theEvent.target:getPoint()
|
local tLoc = theEvent.target:getPoint()
|
||||||
local isIn, percent, dist, theZone = cfxZones.pointInOneOfZones(tLoc, cfxPlayerScore.killZones)
|
local isIn, percent, dist, theZone = cfxZones.pointInOneOfZones(tLoc, cfxPlayerScore.killZones)
|
||||||
|
|
||||||
if not isIn then
|
if not isIn then
|
||||||
if cfxPlayerScore.verbose then
|
if cfxPlayerScore.verbose then
|
||||||
trigger.action.outText("+++pScr: kill detected, but target <" .. theEvent.target:getName() .. "> was outside of any kill zones", 30)
|
trigger.action.outText("+++pScr: kill detected, but target <" .. theEvent.target:getName() .. "> was outside of any kill zones", 30)
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
if theZone.duet and not cfxZones.pointInZone(pLoc, theZone) then
|
if theZone.duet and not cfxZones.pointInZone(pLoc, theZone) then
|
||||||
-- player must be in same zone but was not
|
-- player must be in same zone but was not
|
||||||
if cfxPlayerScore.verbose then
|
if cfxPlayerScore.verbose then
|
||||||
@@ -686,7 +653,7 @@ function cfxPlayerScore.preProcessor(theEvent)
|
|||||||
|
|
||||||
-- take off. overwrites timestamp for last landing
|
-- take off. overwrites timestamp for last landing
|
||||||
-- so a blipping t/o does nor count. Pre-proc only
|
-- so a blipping t/o does nor count. Pre-proc only
|
||||||
if theEvent.id == 3 then
|
if theEvent.id == 3 or theEvent.id == 54 then
|
||||||
local now = timer.getTime()
|
local now = timer.getTime()
|
||||||
local playerName = theUnit:getPlayerName()
|
local playerName = theUnit:getPlayerName()
|
||||||
cfxPlayerScore.lastPlayerLanding[playerName] = now -- overwrite
|
cfxPlayerScore.lastPlayerLanding[playerName] = now -- overwrite
|
||||||
@@ -696,7 +663,7 @@ function cfxPlayerScore.preProcessor(theEvent)
|
|||||||
-- landing can score. but only the first landing in x seconds
|
-- landing can score. but only the first landing in x seconds
|
||||||
-- landing in safe zone promotes any queued scores to
|
-- landing in safe zone promotes any queued scores to
|
||||||
-- permanent if enabled, then nils queue
|
-- permanent if enabled, then nils queue
|
||||||
if theEvent.id == 4 then
|
if theEvent.id == 4 or theEvent.id == 55 then
|
||||||
-- player landed. filter multiple landed events
|
-- player landed. filter multiple landed events
|
||||||
local now = timer.getTime()
|
local now = timer.getTime()
|
||||||
local playerName = theUnit:getPlayerName()
|
local playerName = theUnit:getPlayerName()
|
||||||
@@ -704,7 +671,7 @@ function cfxPlayerScore.preProcessor(theEvent)
|
|||||||
cfxPlayerScore.lastPlayerLanding[playerName] = now -- overwrite
|
cfxPlayerScore.lastPlayerLanding[playerName] = now -- overwrite
|
||||||
if lastLanding and lastLanding + cfxPlayerScore.delayBetweenLandings > now then
|
if lastLanding and lastLanding + cfxPlayerScore.delayBetweenLandings > now then
|
||||||
if cfxPlayerScore.verbose then
|
if cfxPlayerScore.verbose then
|
||||||
trigger.action.outText("+++pScr: Player <" .. playerName .. "> touch-down ignored: too soon.", 30)
|
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)
|
trigger.action.outText("now is <" .. now .. ">, between is <" .. cfxPlayerScore.delayBetweenLandings .. ">, last + between is <" .. lastLanding + cfxPlayerScore.delayBetweenLandings .. ">", 30)
|
||||||
end
|
end
|
||||||
-- filter this event
|
-- filter this event
|
||||||
@@ -731,23 +698,18 @@ function cfxPlayerScore.checkKillFeat(name, killer, victim, fratricide)
|
|||||||
if not fratricide then fratricide = false end
|
if not fratricide then fratricide = false end
|
||||||
local theLoc = victim:getPoint() -- vic's loc is relevant for zone check
|
local theLoc = victim:getPoint() -- vic's loc is relevant for zone check
|
||||||
local coa = killer:getCoalition()
|
local coa = killer:getCoalition()
|
||||||
|
|
||||||
local killFeats = cfxPlayerScore.featsForLocation(name, theLoc, coa,"KILL", killer, victim)
|
local killFeats = cfxPlayerScore.featsForLocation(name, theLoc, coa,"KILL", killer, victim)
|
||||||
|
|
||||||
if (not fratricide) and #killFeats > 0 then
|
if (not fratricide) and #killFeats > 0 then
|
||||||
-- use the feat description
|
-- use the feat description
|
||||||
-- we may want to use closest, currently simply the first
|
-- we may want to use closest, currently simply the first
|
||||||
theFeatZone = killFeats[1]
|
theFeatZone = killFeats[1]
|
||||||
local desc = cfxPlayerScore.evalFeatDescription(name, theFeatZone, killer, victim) -- updates awardedTo
|
local desc = cfxPlayerScore.evalFeatDescription(name, theFeatZone, killer, victim) -- updates awardedTo
|
||||||
|
|
||||||
cfxPlayerScore.logFeatForPlayer(name, desc, playerSide)
|
cfxPlayerScore.logFeatForPlayer(name, desc, playerSide)
|
||||||
theScore = cfxPlayerScore.getPlayerScore(name) -- re-read after write
|
theScore = cfxPlayerScore.getPlayerScore(name) -- re-read after write
|
||||||
|
|
||||||
if cfxPlayerScore.verbose then
|
if cfxPlayerScore.verbose then
|
||||||
trigger.action.outText("Kill feat awarded/queued for <" .. name .. ">", 30)
|
trigger.action.outText("Kill feat awarded/queued for <" .. name .. ">", 30)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function cfxPlayerScore.killDetected(theEvent)
|
function cfxPlayerScore.killDetected(theEvent)
|
||||||
@@ -767,7 +729,6 @@ function cfxPlayerScore.killDetected(theEvent)
|
|||||||
|
|
||||||
-- was it a player kill?
|
-- was it a player kill?
|
||||||
local pk = dcsCommon.isPlayerUnit(victim)
|
local pk = dcsCommon.isPlayerUnit(victim)
|
||||||
|
|
||||||
-- was it a scenery object?
|
-- was it a scenery object?
|
||||||
local wasBuilding = dcsCommon.isSceneryObject(victim)
|
local wasBuilding = dcsCommon.isSceneryObject(victim)
|
||||||
if wasBuilding then
|
if wasBuilding then
|
||||||
@@ -799,7 +760,9 @@ function cfxPlayerScore.killDetected(theEvent)
|
|||||||
--if not victim.getGroup then
|
--if not victim.getGroup then
|
||||||
if isStO then
|
if isStO then
|
||||||
-- static objects have no group
|
-- static objects have no group
|
||||||
local staticName = victim:getName() -- on statics, this returns
|
local staticName
|
||||||
|
if victim.getName then staticName = victim:getName() -- on statics, this returns
|
||||||
|
else staticName = "*?*" end
|
||||||
-- name as entered in TOP LINE
|
-- name as entered in TOP LINE
|
||||||
local staticScore = cfxPlayerScore.object2score(victim, killSide)
|
local staticScore = cfxPlayerScore.object2score(victim, killSide)
|
||||||
|
|
||||||
@@ -817,7 +780,6 @@ function cfxPlayerScore.killDetected(theEvent)
|
|||||||
else
|
else
|
||||||
-- no score, no mentions
|
-- no score, no mentions
|
||||||
end
|
end
|
||||||
|
|
||||||
if not fraternicide then
|
if not fraternicide then
|
||||||
cfxPlayerScore.checkKillFeat(killerName, killer, victim, false)
|
cfxPlayerScore.checkKillFeat(killerName, killer, victim, false)
|
||||||
end
|
end
|
||||||
@@ -834,12 +796,7 @@ function cfxPlayerScore.killDetected(theEvent)
|
|||||||
trigger.action.outText("+++scr: strange stuff:cat, outta here", 30)
|
trigger.action.outText("+++scr: strange stuff:cat, outta here", 30)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local unitScore = cfxPlayerScore.unit2score(victim)
|
local unitScore = cfxPlayerScore.unit2score(victim)
|
||||||
|
|
||||||
-- see which weapon was used. gun kills score 2x
|
|
||||||
-- local killMeth = "" -- meth is currently not defined
|
|
||||||
-- local killWeap = theEvent.weapon -- not supported either
|
|
||||||
|
|
||||||
if pk then -- player kill - add player's name
|
if pk then -- player kill - add player's name
|
||||||
vicDesc = victim:getPlayerName() .. " in " .. vicDesc
|
vicDesc = victim:getPlayerName() .. " in " .. vicDesc
|
||||||
scoreMod = scoreMod * cfxPlayerScore.pkMod
|
scoreMod = scoreMod * cfxPlayerScore.pkMod
|
||||||
@@ -869,17 +826,14 @@ function cfxPlayerScore.killDetected(theEvent)
|
|||||||
trigger.action.outTextForCoalition(killSide, killerName .. " reports killing strategic unit '" .. victim:getName() .. "'", 30)
|
trigger.action.outTextForCoalition(killSide, killerName .. " reports killing strategic unit '" .. victim:getName() .. "'", 30)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local totalScore = unitScore * scoreMod
|
local totalScore = unitScore * scoreMod
|
||||||
-- if the score is negative, awardScoreTo will automatically
|
-- if the score is negative, awardScoreTo will automatically
|
||||||
-- make it immediate, else depending on deferred
|
-- make it immediate, else depending on deferred
|
||||||
cfxPlayerScore.awardScoreTo(killSide, totalScore, killerName)
|
cfxPlayerScore.awardScoreTo(killSide, totalScore, killerName)
|
||||||
|
|
||||||
if not fraternicide then
|
if not fraternicide then
|
||||||
-- only award kill feats for kills of the enemy
|
-- only award kill feats for kills of the enemy
|
||||||
cfxPlayerScore.checkKillFeat(killerName, killer, victim, false)
|
cfxPlayerScore.checkKillFeat(killerName, killer, victim, false)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function cfxPlayerScore.handlePlayerLanding(theEvent)
|
function cfxPlayerScore.handlePlayerLanding(theEvent)
|
||||||
@@ -890,13 +844,11 @@ function cfxPlayerScore.handlePlayerLanding(theEvent)
|
|||||||
if cfxPlayerScore.verbose then
|
if cfxPlayerScore.verbose then
|
||||||
trigger.action.outText("+++pScr: Player <" .. playerName .. "> landed", 30)
|
trigger.action.outText("+++pScr: Player <" .. playerName .. "> landed", 30)
|
||||||
end
|
end
|
||||||
|
|
||||||
local theScore = cfxPlayerScore.getPlayerScore(playerName)
|
local theScore = cfxPlayerScore.getPlayerScore(playerName)
|
||||||
|
|
||||||
-- see if a feat is available for this landing
|
-- see if a feat is available for this landing
|
||||||
local landingFeats = cfxPlayerScore.featsForLocation(playerName, theLoc, playerSide,"LANDING")
|
local landingFeats = cfxPlayerScore.featsForLocation(playerName, theLoc, playerSide,"LANDING")
|
||||||
|
|
||||||
-- first, scheck if landing is awardable, and if so,
|
-- first, check if landing is awardable, and if so,
|
||||||
-- award the landing
|
-- award the landing
|
||||||
if cfxPlayerScore.landing > 0 or #landingFeats > 0 then
|
if cfxPlayerScore.landing > 0 or #landingFeats > 0 then
|
||||||
-- yes, landings are awarded a score. do before
|
-- yes, landings are awarded a score. do before
|
||||||
@@ -914,7 +866,6 @@ function cfxPlayerScore.handlePlayerLanding(theEvent)
|
|||||||
desc = desc .. " aircraft"
|
desc = desc .. " aircraft"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
cfxPlayerScore.updateScoreForPlayer(playerName, cfxPlayerScore.landing)
|
cfxPlayerScore.updateScoreForPlayer(playerName, cfxPlayerScore.landing)
|
||||||
cfxPlayerScore.logFeatForPlayer(playerName, desc, playerSide)
|
cfxPlayerScore.logFeatForPlayer(playerName, desc, playerSide)
|
||||||
theScore = cfxPlayerScore.getPlayerScore(playerName) -- re-read after write
|
theScore = cfxPlayerScore.getPlayerScore(playerName) -- re-read after write
|
||||||
@@ -922,7 +873,6 @@ function cfxPlayerScore.handlePlayerLanding(theEvent)
|
|||||||
trigger.action.outText("Landing feat awarded/queued for <" .. playerName .. ">", 30)
|
trigger.action.outText("Landing feat awarded/queued for <" .. playerName .. ">", 30)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- see if we are using deferred scoring, else can end right now
|
-- see if we are using deferred scoring, else can end right now
|
||||||
if not cfxPlayerScore.deferred then
|
if not cfxPlayerScore.deferred then
|
||||||
return
|
return
|
||||||
@@ -930,7 +880,6 @@ function cfxPlayerScore.handlePlayerLanding(theEvent)
|
|||||||
-- only continue if there is anything to award
|
-- only continue if there is anything to award
|
||||||
local killSize = dcsCommon.getSizeOfTable(theScore.killQueue)
|
local killSize = dcsCommon.getSizeOfTable(theScore.killQueue)
|
||||||
local featSize = dcsCommon.getSizeOfTable(theScore.featQueue)
|
local featSize = dcsCommon.getSizeOfTable(theScore.featQueue)
|
||||||
|
|
||||||
if cfxPlayerScore.verbose then
|
if cfxPlayerScore.verbose then
|
||||||
trigger.action.outText("+++pScr: prepping deferred score for <" .. playerName ..">", 30)
|
trigger.action.outText("+++pScr: prepping deferred score for <" .. playerName ..">", 30)
|
||||||
end
|
end
|
||||||
@@ -948,15 +897,17 @@ function cfxPlayerScore.handlePlayerLanding(theEvent)
|
|||||||
if (theZone.owner == coa) or (theZone.owner == 0) or (theZone.owner == nil) then
|
if (theZone.owner == coa) or (theZone.owner == 0) or (theZone.owner == nil) then
|
||||||
if cfxZones.pointInZone(loc, theZone) then
|
if cfxZones.pointInZone(loc, theZone) then
|
||||||
isSafe = true
|
isSafe = true
|
||||||
|
if cfxPlayerScore.verbose then
|
||||||
|
trigger.action.outText("+++pScr: Zone <" .. theZone.name .. ">: owner=<" .. theZone.owner .. ">, my coa=<" .. coa .. ">, LANDED SAFELY", 30)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if cfxPlayerScore.verbose then
|
if cfxPlayerScore.verbose and cfxZones.pointInZone(loc, theZone) then
|
||||||
trigger.action.outText("+++pSc: Zone <" .. theZone.name .. ">: owner=<" .. theZone.owner .. ">, my coa=<" .. coa .. ">, no owner match")
|
trigger.action.outText("+++pScr: Zone <" .. theZone.name .. ">: owner=<" .. theZone.owner .. ">, player unit <" .. theUnit:getName() .. ">, my coa=<" .. coa .. ">, no owner match", 30)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if not isSafe then
|
if not isSafe then
|
||||||
if cfxPlayerScore.verbose then
|
if cfxPlayerScore.verbose then
|
||||||
trigger.action.outText("+++pScr: deferred, but not inside score safe zone.", 30)
|
trigger.action.outText("+++pScr: deferred, but not inside score safe zone.", 30)
|
||||||
@@ -975,7 +926,6 @@ function cfxPlayerScore.scheduledAward(args)
|
|||||||
-- called with player name and unit name in args
|
-- called with player name and unit name in args
|
||||||
local playerName = args[1]
|
local playerName = args[1]
|
||||||
local unitName = args[2]
|
local unitName = args[2]
|
||||||
|
|
||||||
local theUnit = Unit.getByName(unitName)
|
local theUnit = Unit.getByName(unitName)
|
||||||
if not theUnit or
|
if not theUnit or
|
||||||
not Unit.isExist(theUnit)
|
not Unit.isExist(theUnit)
|
||||||
@@ -984,18 +934,15 @@ function cfxPlayerScore.scheduledAward(args)
|
|||||||
trigger.action.outText("Player <" .. playerName .. "> lost score.", 30)
|
trigger.action.outText("Player <" .. playerName .. "> lost score.", 30)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local uid = theUnit:getID()
|
local uid = theUnit:getID()
|
||||||
if theUnit:inAir() then
|
if theUnit:inAir() then
|
||||||
trigger.action.outTextForUnit(uid, "Can't award score to <" .. playerName .. ">: unit not on the ground.", 30)
|
trigger.action.outTextForUnit(uid, "Can't award score to <" .. playerName .. ">: unit not on the ground.", 30)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if theUnit:getLife() < 1 then
|
if theUnit:getLife() < 1 then
|
||||||
trigger.action.outTextForUnit(uid, "Can't award score to <" .. playerName .. ">: unit did not survive landing.", 30)
|
trigger.action.outTextForUnit(uid, "Can't award score to <" .. playerName .. ">: unit did not survive landing.", 30)
|
||||||
return -- needs to reslot, don't have to nil player score
|
return -- needs to reslot, don't have to nil player score
|
||||||
end
|
end
|
||||||
|
|
||||||
-- see if player is *still* within a scoreSafe zone
|
-- see if player is *still* within a scoreSafe zone
|
||||||
local loc = theUnit:getPoint()
|
local loc = theUnit:getPoint()
|
||||||
local coa = theUnit:getCoalition()
|
local coa = theUnit:getCoalition()
|
||||||
@@ -1008,12 +955,10 @@ function cfxPlayerScore.scheduledAward(args)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if not isSafe then
|
if not isSafe then
|
||||||
trigger.action.outTextForUnit(uid, "Can't award score for <" .. playerName .. ">, not in safe zone.", 30)
|
trigger.action.outTextForUnit(uid, "Can't award score for <" .. playerName .. ">, not in safe zone.", 30)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local theScore = cfxPlayerScore.getPlayerScore(playerName)
|
local theScore = cfxPlayerScore.getPlayerScore(playerName)
|
||||||
local playerSide = dcsCommon.playerName2Coalition(playerName)
|
local playerSide = dcsCommon.playerName2Coalition(playerName)
|
||||||
if playerSide < 1 then
|
if playerSide < 1 then
|
||||||
@@ -1023,14 +968,12 @@ function cfxPlayerScore.scheduledAward(args)
|
|||||||
if dcsCommon.getSizeOfTable(theScore.killQueue) < 1 and
|
if dcsCommon.getSizeOfTable(theScore.killQueue) < 1 and
|
||||||
dcsCommon.getSizeOfTable(theScore.featQueue) < 1 and
|
dcsCommon.getSizeOfTable(theScore.featQueue) < 1 and
|
||||||
theScore.scoreaccu < 1 then
|
theScore.scoreaccu < 1 then
|
||||||
-- player changed planes or
|
-- player changed planes or there was nothing to award
|
||||||
-- there was nothing to award
|
|
||||||
trigger.action.outTextForUnit(uid, "Thank you, " .. playerName .. ", no scores or feats pending.", 30)
|
trigger.action.outTextForUnit(uid, "Thank you, " .. playerName .. ", no scores or feats pending.", 30)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local hasAward = false
|
local hasAward = false
|
||||||
|
|
||||||
-- when we get here we award all scores, kills, and feats
|
-- when we get here we award all scores, kills, and feats
|
||||||
local desc = "\nPlayer " .. playerName .. " is awarded:\n"
|
local desc = "\nPlayer " .. playerName .. " is awarded:\n"
|
||||||
-- score and total score
|
-- score and total score
|
||||||
@@ -1044,8 +987,7 @@ function cfxPlayerScore.scheduledAward(args)
|
|||||||
end
|
end
|
||||||
theScore.scoreaccu = 0
|
theScore.scoreaccu = 0
|
||||||
hasAward = true
|
hasAward = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxPlayerScore.verbose then
|
if cfxPlayerScore.verbose then
|
||||||
trigger.action.outText("Iterating kill q <" .. dcsCommon.getSizeOfTable(theScore.killQueue) .. "> and feat q <" .. dcsCommon.getSizeOfTable(theScore.featQueue) .. ">", 30)
|
trigger.action.outText("Iterating kill q <" .. dcsCommon.getSizeOfTable(theScore.killQueue) .. "> and feat q <" .. dcsCommon.getSizeOfTable(theScore.featQueue) .. ">", 30)
|
||||||
end
|
end
|
||||||
@@ -1059,7 +1001,6 @@ function cfxPlayerScore.scheduledAward(args)
|
|||||||
hasAward = true
|
hasAward = true
|
||||||
end
|
end
|
||||||
theScore.killQueue = {}
|
theScore.killQueue = {}
|
||||||
|
|
||||||
-- iterate feats
|
-- iterate feats
|
||||||
if dcsCommon.getSizeOfTable(theScore.featQueue) > 0 then
|
if dcsCommon.getSizeOfTable(theScore.featQueue) > 0 then
|
||||||
desc = desc .. " confirmed feats:\n"
|
desc = desc .. " confirmed feats:\n"
|
||||||
@@ -1070,11 +1011,9 @@ function cfxPlayerScore.scheduledAward(args)
|
|||||||
hasAward = true
|
hasAward = true
|
||||||
end
|
end
|
||||||
theScore.featQueue = {}
|
theScore.featQueue = {}
|
||||||
|
|
||||||
if cfxPlayerScore.reportCoalition then
|
if cfxPlayerScore.reportCoalition then
|
||||||
desc = desc .. "\nCoalition Total: " .. cfxPlayerScore.coalitionScore[playerSide]
|
desc = desc .. "\nCoalition Total: " .. cfxPlayerScore.coalitionScore[playerSide]
|
||||||
end
|
end
|
||||||
|
|
||||||
-- output score
|
-- output score
|
||||||
desc = desc .. "\n"
|
desc = desc .. "\n"
|
||||||
if hasAward then
|
if hasAward then
|
||||||
@@ -1089,11 +1028,9 @@ function cfxPlayerScore.handlePlayerDeath(theEvent)
|
|||||||
-- only counts once
|
-- only counts once
|
||||||
local theUnit = theEvent.initiator
|
local theUnit = theEvent.initiator
|
||||||
local uName = theUnit:getName()
|
local uName = theUnit:getName()
|
||||||
|
|
||||||
if cfxPlayerScore.verbose then
|
if cfxPlayerScore.verbose then
|
||||||
trigger.action.outText("+++pScr: LOA/player death handler entry for <" .. uName .. ">", 30)
|
trigger.action.outText("+++pScr: LOA/player death handler entry for <" .. uName .. ">", 30)
|
||||||
end
|
end
|
||||||
|
|
||||||
local pName = cfxPlayerScore.unit2player[uName]
|
local pName = cfxPlayerScore.unit2player[uName]
|
||||||
if pName then
|
if pName then
|
||||||
-- this was a player name with link still live.
|
-- this was a player name with link still live.
|
||||||
@@ -1113,14 +1050,12 @@ function cfxPlayerScore.handlePlayerDeath(theEvent)
|
|||||||
trigger.action.outText("+++pScr - no action for LOA", 30)
|
trigger.action.outText("+++pScr - no action for LOA", 30)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function cfxPlayerScore.handlePlayerEvent(theEvent)
|
function cfxPlayerScore.handlePlayerEvent(theEvent)
|
||||||
if theEvent.id == 28 then
|
if theEvent.id == 28 then
|
||||||
-- kill from player detected.
|
-- kill from player detected.
|
||||||
cfxPlayerScore.killDetected(theEvent)
|
cfxPlayerScore.killDetected(theEvent)
|
||||||
|
|
||||||
elseif theEvent.id == 15 then -- birth
|
elseif theEvent.id == 15 then -- birth
|
||||||
-- access player score for player. this will
|
-- access player score for player. this will
|
||||||
-- allocate if doesn't exist. Any player ever
|
-- allocate if doesn't exist. Any player ever
|
||||||
@@ -1130,7 +1065,6 @@ function cfxPlayerScore.handlePlayerEvent(theEvent)
|
|||||||
local playerName = thePlayerUnit:getPlayerName()
|
local playerName = thePlayerUnit:getPlayerName()
|
||||||
local theScore = cfxPlayerScore.getPlayerScore(playerName)
|
local theScore = cfxPlayerScore.getPlayerScore(playerName)
|
||||||
-- now re-init feat and score queues
|
-- now re-init feat and score queues
|
||||||
|
|
||||||
if theScore.scoreaccu and theScore.scoreaccu > 0 then
|
if theScore.scoreaccu and theScore.scoreaccu > 0 then
|
||||||
trigger.action.outTextForCoalition(playerSide, "Player " .. playerName .. ", score of <" .. theScore.scoreaccu .. "> points discarded.", 30)
|
trigger.action.outTextForCoalition(playerSide, "Player " .. playerName .. ", score of <" .. theScore.scoreaccu .. "> points discarded.", 30)
|
||||||
end
|
end
|
||||||
@@ -1146,9 +1080,10 @@ function cfxPlayerScore.handlePlayerEvent(theEvent)
|
|||||||
-- write back
|
-- write back
|
||||||
cfxPlayerScore.setPlayerScore(playerName, theScore)
|
cfxPlayerScore.setPlayerScore(playerName, theScore)
|
||||||
|
|
||||||
elseif theEvent.id == 4 then -- land
|
elseif theEvent.id == 4 or theEvent.id == 55 then -- land
|
||||||
-- see if plane is still connected to player
|
-- see if plane is still connected to player
|
||||||
local theUnit = theEvent.initiator
|
local theUnit = theEvent.initiator
|
||||||
|
if not theUnit.getName then return end -- dcs oddity precaution
|
||||||
local uName = theUnit:getName()
|
local uName = theUnit:getName()
|
||||||
if cfxPlayerScore.unit2player[uName] then
|
if cfxPlayerScore.unit2player[uName] then
|
||||||
-- is filtered if too soon after last take-off/landing
|
-- is filtered if too soon after last take-off/landing
|
||||||
@@ -1158,7 +1093,6 @@ function cfxPlayerScore.handlePlayerEvent(theEvent)
|
|||||||
trigger.action.outText("+++pScr: filtered landing for <" .. uName .. ">: player no longer linked to unit", 30)
|
trigger.action.outText("+++pScr: filtered landing for <" .. uName .. ">: player no longer linked to unit", 30)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1167,69 +1101,51 @@ function cfxPlayerScore.readConfigZone(theZone)
|
|||||||
-- default scores
|
-- default scores
|
||||||
cfxPlayerScore.aircraft = theZone:getNumberFromZoneProperty("aircraft", 50)
|
cfxPlayerScore.aircraft = theZone:getNumberFromZoneProperty("aircraft", 50)
|
||||||
cfxPlayerScore.helo = theZone:getNumberFromZoneProperty("helo", 40)
|
cfxPlayerScore.helo = theZone:getNumberFromZoneProperty("helo", 40)
|
||||||
cfxPlayerScore.ground = theZone:getNumberFromZoneProperty("ground", 10)
|
cfxPlayerScore.ground = theZone:getNumberFromZoneProperty("ground", 10)
|
||||||
cfxPlayerScore.ship = theZone:getNumberFromZoneProperty("ship", 80)
|
cfxPlayerScore.ship = theZone:getNumberFromZoneProperty("ship", 80)
|
||||||
cfxPlayerScore.train = theZone:getNumberFromZoneProperty( "train", 5)
|
cfxPlayerScore.train = theZone:getNumberFromZoneProperty( "train", 5)
|
||||||
cfxPlayerScore.landing = theZone:getNumberFromZoneProperty("landing", 0) -- if > 0 then feat
|
cfxPlayerScore.landing = theZone:getNumberFromZoneProperty("landing", 0) -- if > 0 then feat
|
||||||
|
|
||||||
cfxPlayerScore.pkMod = theZone:getNumberFromZoneProperty( "pkMod", 1) -- factor for killing a player
|
cfxPlayerScore.pkMod = theZone:getNumberFromZoneProperty( "pkMod", 1) -- factor for killing a player
|
||||||
cfxPlayerScore.ffMod = theZone:getNumberFromZoneProperty( "ffMod", -2) -- factor for friendly fire
|
cfxPlayerScore.ffMod = theZone:getNumberFromZoneProperty( "ffMod", -2) -- factor for friendly fire
|
||||||
cfxPlayerScore.planeLoss = theZone:getNumberFromZoneProperty("planeLoss", -10) -- points added when player's plane crashes
|
cfxPlayerScore.planeLoss = theZone:getNumberFromZoneProperty("planeLoss", -10) -- points added when player's plane crashes
|
||||||
|
|
||||||
cfxPlayerScore.announcer = theZone:getBoolFromZoneProperty("announcer", true)
|
cfxPlayerScore.announcer = theZone:getBoolFromZoneProperty("announcer", true)
|
||||||
|
|
||||||
if theZone:hasProperty("badSound") then
|
if theZone:hasProperty("badSound") then
|
||||||
cfxPlayerScore.badSound = theZone:getStringFromZoneProperty("badSound", "<nosound>")
|
cfxPlayerScore.badSound = theZone:getStringFromZoneProperty("badSound", "<nosound>")
|
||||||
end
|
end
|
||||||
if theZone:hasProperty("scoreSound") then
|
if theZone:hasProperty("scoreSound") then
|
||||||
cfxPlayerScore.scoreSound = theZone:getStringFromZoneProperty("scoreSound", "<nosound>")
|
cfxPlayerScore.scoreSound = theZone:getStringFromZoneProperty("scoreSound", "<nosound>")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- triggering saving scores
|
-- triggering saving scores
|
||||||
if theZone:hasProperty("saveScore?") then
|
if theZone:hasProperty("saveScore?") then
|
||||||
cfxPlayerScore.saveScore = theZone:getStringFromZoneProperty("saveScore?", "none")
|
cfxPlayerScore.saveScore = theZone:getStringFromZoneProperty("saveScore?", "none")
|
||||||
cfxPlayerScore.lastSaveScore = trigger.misc.getUserFlag(cfxPlayerScore.saveScore)
|
cfxPlayerScore.lastSaveScore = trigger.misc.getUserFlag(cfxPlayerScore.saveScore)
|
||||||
cfxPlayerScore.incremental = theZone:getBoolFromZoneProperty("incremental", false) -- incremental saves
|
cfxPlayerScore.incremental = theZone:getBoolFromZoneProperty("incremental", false) -- incremental saves
|
||||||
end
|
end
|
||||||
|
|
||||||
-- triggering show all scores
|
-- triggering show all scores
|
||||||
if theZone:hasProperty("showScore?") then
|
if theZone:hasProperty("showScore?") then
|
||||||
cfxPlayerScore.showScore = theZone:getStringFromZoneProperty("showScore?", "none")
|
cfxPlayerScore.showScore = theZone:getStringFromZoneProperty("showScore?", "none")
|
||||||
cfxPlayerScore.lastShowScore = trigger.misc.getUserFlag(cfxPlayerScore.showScore)
|
cfxPlayerScore.lastShowScore = trigger.misc.getUserFlag(cfxPlayerScore.showScore)
|
||||||
end
|
end
|
||||||
|
|
||||||
cfxPlayerScore.rankPlayers = theZone:getBoolFromZoneProperty("rankPlayers", false)
|
cfxPlayerScore.rankPlayers = theZone:getBoolFromZoneProperty("rankPlayers", false)
|
||||||
|
|
||||||
cfxPlayerScore.scoreOnly = theZone:getBoolFromZoneProperty("scoreOnly", true)
|
cfxPlayerScore.scoreOnly = theZone:getBoolFromZoneProperty("scoreOnly", true)
|
||||||
|
|
||||||
cfxPlayerScore.deferred = theZone:getBoolFromZoneProperty("deferred", false)
|
cfxPlayerScore.deferred = theZone:getBoolFromZoneProperty("deferred", false)
|
||||||
|
|
||||||
cfxPlayerScore.delayAfterLanding = theZone:getNumberFromZoneProperty("delayAfterLanding", 10)
|
cfxPlayerScore.delayAfterLanding = theZone:getNumberFromZoneProperty("delayAfterLanding", 10)
|
||||||
|
|
||||||
cfxPlayerScore.scoreFileName = theZone:getStringFromZoneProperty("scoreFileName", "Player Scores")
|
cfxPlayerScore.scoreFileName = theZone:getStringFromZoneProperty("scoreFileName", "Player Scores")
|
||||||
|
|
||||||
cfxPlayerScore.reportScore = theZone:getBoolFromZoneProperty("reportScore", true)
|
cfxPlayerScore.reportScore = theZone:getBoolFromZoneProperty("reportScore", true)
|
||||||
|
|
||||||
cfxPlayerScore.reportFeats = theZone:getBoolFromZoneProperty("reportFeats", true)
|
cfxPlayerScore.reportFeats = theZone:getBoolFromZoneProperty("reportFeats", true)
|
||||||
|
|
||||||
cfxPlayerScore.reportCoalition = theZone:getBoolFromZoneProperty("reportCoalition", false) -- also show coalition score
|
cfxPlayerScore.reportCoalition = theZone:getBoolFromZoneProperty("reportCoalition", false) -- also show coalition score
|
||||||
|
|
||||||
cfxPlayerScore.noGrief = theZone:getBoolFromZoneProperty( "noGrief", true) -- noGrief = only add positive score
|
cfxPlayerScore.noGrief = theZone:getBoolFromZoneProperty( "noGrief", true) -- noGrief = only add positive score
|
||||||
|
|
||||||
if theZone:hasProperty("redScore#") then
|
if theZone:hasProperty("redScore#") then
|
||||||
cfxPlayerScore.redScoreOut = theZone:getStringFromZoneProperty("redScore#")
|
cfxPlayerScore.redScoreOut = theZone:getStringFromZoneProperty("redScore#")
|
||||||
theZone:setFlagValue(cfxPlayerScore.redScoreOut, cfxPlayerScore.coalitionScore[1])
|
theZone:setFlagValue(cfxPlayerScore.redScoreOut, cfxPlayerScore.coalitionScore[1])
|
||||||
end
|
end
|
||||||
|
|
||||||
if theZone:hasProperty("blueScore#") then
|
if theZone:hasProperty("blueScore#") then
|
||||||
cfxPlayerScore.blueScoreOut = theZone:getStringFromZoneProperty("blueScore#")
|
cfxPlayerScore.blueScoreOut = theZone:getStringFromZoneProperty("blueScore#")
|
||||||
theZone:setFlagValue(cfxPlayerScore.blueScoreOut, cfxPlayerScore.coalitionScore[2])
|
theZone:setFlagValue(cfxPlayerScore.blueScoreOut, cfxPlayerScore.coalitionScore[2])
|
||||||
end
|
end
|
||||||
|
|
||||||
if theZone:hasProperty("sharedData") then
|
if theZone:hasProperty("sharedData") then
|
||||||
cfxPlayerScore.sharedData = theZone:getStringFromZoneProperty("sharedData", "cfxNameMissing")
|
cfxPlayerScore.sharedData = theZone:getStringFromZoneProperty("sharedData", "cfxNameMissing")
|
||||||
end
|
end
|
||||||
|
|
||||||
cfxPlayerScore.score2finance = theZone:getNumberFromZoneProperty("score2finance", 1) -- factor to convert points to bank finance
|
cfxPlayerScore.score2finance = theZone:getNumberFromZoneProperty("score2finance", 1) -- factor to convert points to bank finance
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1287,11 +1203,9 @@ function cfxPlayerScore.loadData()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
-- save scores (text file)
|
-- save scores (text file)
|
||||||
--
|
--
|
||||||
|
|
||||||
function cfxPlayerScore.saveScores(theText, name)
|
function cfxPlayerScore.saveScores(theText, name)
|
||||||
if not _G["persistence"] then
|
if not _G["persistence"] then
|
||||||
trigger.action.outText("+++pScr: persistence module required to save scores. Here are the scores that I would have saved to <" .. name .. ">:\n", 30)
|
trigger.action.outText("+++pScr: persistence module required to save scores. Here are the scores that I would have saved to <" .. name .. ">:\n", 30)
|
||||||
@@ -1331,7 +1245,6 @@ function cfxPlayerScore.saveScoreToFile()
|
|||||||
-- local built score table
|
-- local built score table
|
||||||
local ranked = cfxPlayerScore.rankPlayers
|
local ranked = cfxPlayerScore.rankPlayers
|
||||||
local theText = cfxPlayerScore.scoreTextForAllPlayers(ranked)
|
local theText = cfxPlayerScore.scoreTextForAllPlayers(ranked)
|
||||||
|
|
||||||
-- save to disk
|
-- save to disk
|
||||||
cfxPlayerScore.saveScores(theText, cfxPlayerScore.scoreFileName)
|
cfxPlayerScore.saveScores(theText, cfxPlayerScore.scoreFileName)
|
||||||
end
|
end
|
||||||
@@ -1341,14 +1254,12 @@ function cfxPlayerScore.showScoreToAll()
|
|||||||
local theText = cfxPlayerScore.scoreTextForAllPlayers(ranked)
|
local theText = cfxPlayerScore.scoreTextForAllPlayers(ranked)
|
||||||
trigger.action.outText(theText, 30)
|
trigger.action.outText(theText, 30)
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Update
|
-- Update
|
||||||
--
|
--
|
||||||
function cfxPlayerScore.update()
|
function cfxPlayerScore.update()
|
||||||
-- re-invoke in 1 second
|
-- re-invoke in 1 second
|
||||||
timer.scheduleFunction(cfxPlayerScore.update, {}, timer.getTime() + 1)
|
timer.scheduleFunction(cfxPlayerScore.update, {}, timer.getTime() + 1)
|
||||||
|
|
||||||
-- see if someone banged on saveScore
|
-- see if someone banged on saveScore
|
||||||
if cfxPlayerScore.saveScore then
|
if cfxPlayerScore.saveScore then
|
||||||
if cfxZones.testZoneFlag(cfxPlayerScore, cfxPlayerScore.saveScore, "change", "lastSaveScore") then
|
if cfxZones.testZoneFlag(cfxPlayerScore, cfxPlayerScore.saveScore, "change", "lastSaveScore") then
|
||||||
@@ -1358,7 +1269,6 @@ function cfxPlayerScore.update()
|
|||||||
cfxPlayerScore.saveScoreToFile()
|
cfxPlayerScore.saveScoreToFile()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- showScore perhaps?
|
-- showScore perhaps?
|
||||||
if cfxPlayerScore.showScore then
|
if cfxPlayerScore.showScore then
|
||||||
if cfxZones.testZoneFlag(cfxPlayerScore, cfxPlayerScore.showScore, "change", "lastShowScore") then
|
if cfxZones.testZoneFlag(cfxPlayerScore, cfxPlayerScore.showScore, "change", "lastShowScore") then
|
||||||
@@ -1368,7 +1278,6 @@ function cfxPlayerScore.update()
|
|||||||
cfxPlayerScore.showScoreToAll()
|
cfxPlayerScore.showScoreToAll()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- check score flags
|
-- check score flags
|
||||||
if cfxPlayerScore.blueTriggerFlags then
|
if cfxPlayerScore.blueTriggerFlags then
|
||||||
local coa = 2
|
local coa = 2
|
||||||
@@ -1377,13 +1286,11 @@ function cfxPlayerScore.update()
|
|||||||
if tVal ~= newVal then
|
if tVal ~= newVal then
|
||||||
-- score!
|
-- score!
|
||||||
cfxPlayerScore.coalitionScore[coa] = cfxPlayerScore.coalitionScore[coa] + cfxPlayerScore.blueTriggerScore[tName]
|
cfxPlayerScore.coalitionScore[coa] = cfxPlayerScore.coalitionScore[coa] + cfxPlayerScore.blueTriggerScore[tName]
|
||||||
cfxPlayerScore.blueTriggerFlags[tName] = newVal
|
cfxPlayerScore.blueTriggerFlags[tName] = newVal
|
||||||
|
|
||||||
if cfxPlayerScore.announcer then
|
if cfxPlayerScore.announcer then
|
||||||
trigger.action.outTextForCoalition(coa, "BLUE goal [" .. tName .. "] achieved, new BLUE coalition score is " .. cfxPlayerScore.coalitionScore[coa], 30)
|
trigger.action.outTextForCoalition(coa, "BLUE goal [" .. tName .. "] achieved, new BLUE coalition score is " .. cfxPlayerScore.coalitionScore[coa], 30)
|
||||||
trigger.action.outSoundForCoalition(coa, cfxPlayerScore.scoreSound)
|
trigger.action.outSoundForCoalition(coa, cfxPlayerScore.scoreSound)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- bank it if exists
|
-- bank it if exists
|
||||||
local amount
|
local amount
|
||||||
if bank and bank.addFunds then
|
if bank and bank.addFunds then
|
||||||
@@ -1402,17 +1309,12 @@ function cfxPlayerScore.update()
|
|||||||
local newVal = trigger.misc.getUserFlag(tName)
|
local newVal = trigger.misc.getUserFlag(tName)
|
||||||
if tVal ~= newVal then
|
if tVal ~= newVal then
|
||||||
-- score!
|
-- score!
|
||||||
|
|
||||||
cfxPlayerScore.coalitionScore[coa] = cfxPlayerScore.coalitionScore[coa] + cfxPlayerScore.redTriggerScore[tName]
|
cfxPlayerScore.coalitionScore[coa] = cfxPlayerScore.coalitionScore[coa] + cfxPlayerScore.redTriggerScore[tName]
|
||||||
cfxPlayerScore.redTriggerFlags[tName] = newVal
|
cfxPlayerScore.redTriggerFlags[tName] = newVal
|
||||||
--if bank and bank.addFunds then
|
|
||||||
-- bank.addFunds(coa, cfxPlayerScore.score2finance * cfxPlayerScore.blueTriggerScore[tName])
|
|
||||||
--end
|
|
||||||
if cfxPlayerScore.announcer then
|
if cfxPlayerScore.announcer then
|
||||||
trigger.action.outTextForCoalition(coa, "RED goal [" .. tName .. "] achieved, new RED coalition score is " .. cfxPlayerScore.coalitionScore[coa], 30)
|
trigger.action.outTextForCoalition(coa, "RED goal [" .. tName .. "] achieved, new RED coalition score is " .. cfxPlayerScore.coalitionScore[coa], 30)
|
||||||
trigger.action.outSoundForCoalition(coa, cfxPlayerScore.scoreSound)
|
trigger.action.outSoundForCoalition(coa, cfxPlayerScore.scoreSound)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- bank it if exists
|
-- bank it if exists
|
||||||
local amount
|
local amount
|
||||||
if bank and bank.addFunds then
|
if bank and bank.addFunds then
|
||||||
@@ -1429,7 +1331,6 @@ function cfxPlayerScore.update()
|
|||||||
if cfxPlayerScore.redScoreOut then
|
if cfxPlayerScore.redScoreOut then
|
||||||
cfxZones.setFlagValue(cfxPlayerScore.redScoreOut, cfxPlayerScore.coalitionScore[1], cfxPlayerScore)
|
cfxZones.setFlagValue(cfxPlayerScore.redScoreOut, cfxPlayerScore.coalitionScore[1], cfxPlayerScore)
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxPlayerScore.blueScoreOut then
|
if cfxPlayerScore.blueScoreOut then
|
||||||
cfxZones.setFlagValue(cfxPlayerScore.blueScoreOut, cfxPlayerScore.coalitionScore[2], cfxPlayerScore)
|
cfxZones.setFlagValue(cfxPlayerScore.blueScoreOut, cfxPlayerScore.coalitionScore[2], cfxPlayerScore)
|
||||||
end
|
end
|
||||||
@@ -1448,19 +1349,8 @@ function cfxPlayerScore.start()
|
|||||||
-- identify and process a score table zones
|
-- identify and process a score table zones
|
||||||
local theZone = cfxZones.getZoneByName("playerScoreTable")
|
local theZone = cfxZones.getZoneByName("playerScoreTable")
|
||||||
if theZone then
|
if theZone then
|
||||||
-- trigger.action.outText("Reading custom player score table", 30)
|
|
||||||
-- read all into my types registry, replacing whatever is there
|
-- read all into my types registry, replacing whatever is there
|
||||||
cfxPlayerScore.typeScore = theZone:getAllZoneProperties(true) -- true = get all properties in UPPER case
|
cfxPlayerScore.typeScore = theZone:getAllZoneProperties(true) -- true = get all properties in UPPER case
|
||||||
-- local n = dcsCommon.getSizeOfTable(cfxPlayerScore.typeScore)
|
|
||||||
-- trigger.action.outText("Table has <" .. n .. "> entries:", 30)
|
|
||||||
if true then
|
|
||||||
--trigger.action.outText("Custom PlayerScore Type Score Table:", 30)
|
|
||||||
for name, val in pairs (cfxPlayerScore.typeScore) do
|
|
||||||
-- trigger.action.outText("ps[" .. name .. "]=<" .. val .. ">", 30)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
--trigger.action.outText("No custom score defined", 30)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- read score tiggers and values
|
-- read score tiggers and values
|
||||||
@@ -1493,43 +1383,37 @@ function cfxPlayerScore.start()
|
|||||||
end
|
end
|
||||||
cfxPlayerScore.blueTriggerFlags[tName] = trigger.misc.getUserFlag(tName)
|
cfxPlayerScore.blueTriggerFlags[tName] = trigger.misc.getUserFlag(tName)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- now read my config zone
|
-- now read my config zone
|
||||||
local theZone = cfxZones.getZoneByName("playerScoreConfig")
|
local theZone = cfxZones.getZoneByName("playerScoreConfig")
|
||||||
if not theZone then
|
if not theZone then
|
||||||
theZone = cfxZones.createSimpleZone("playerScoreConfig")
|
theZone = cfxZones.createSimpleZone("playerScoreConfig")
|
||||||
end
|
end
|
||||||
cfxPlayerScore.readConfigZone(theZone)
|
cfxPlayerScore.readConfigZone(theZone)
|
||||||
|
|
||||||
-- read all scoreSafe zones
|
-- read all scoreSafe zones
|
||||||
local safeZones = cfxZones.zonesWithProperty("scoreSafe")
|
local safeZones = cfxZones.zonesWithProperty("scoreSafe")
|
||||||
for k, aZone in pairs(safeZones) do
|
for k, aZone in pairs(safeZones) do
|
||||||
cfxPlayerScore.addSafeZone(aZone)
|
cfxPlayerScore.addSafeZone(aZone)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- read all feat zones
|
-- read all feat zones
|
||||||
local featZones = cfxZones.zonesWithProperty("feat")
|
local featZones = cfxZones.zonesWithProperty("feat")
|
||||||
for k, aZone in pairs(featZones) do
|
for k, aZone in pairs(featZones) do
|
||||||
cfxPlayerScore.addFeatZone(aZone)
|
cfxPlayerScore.addFeatZone(aZone)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- read all kill zones
|
-- read all kill zones
|
||||||
local killZones = cfxZones.zonesWithProperty("killZone")
|
local killZones = cfxZones.zonesWithProperty("killZone")
|
||||||
for k, aZone in pairs(killZones) do
|
for k, aZone in pairs(killZones) do
|
||||||
cfxPlayerScore.addKillZone(aZone)
|
cfxPlayerScore.addKillZone(aZone)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- check that deferred has scoreSafe zones
|
-- check that deferred has scoreSafe zones
|
||||||
if cfxPlayerScore.deferred and dcsCommon.getSizeOfTable(cfxPlayerScore.safeZones) < 1 then
|
if cfxPlayerScore.deferred and dcsCommon.getSizeOfTable(cfxPlayerScore.safeZones) < 1 then
|
||||||
trigger.action.outText("+++pScr: WARNING - deferred scoring active but no 'scoreSafe' zones set!", 30)
|
trigger.action.outText("+++pScr: WARNING - deferred scoring active but no 'scoreSafe' zones set!", 30)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- subscribe to events and use dcsCommon's handler structure
|
-- subscribe to events and use dcsCommon's handler structure
|
||||||
dcsCommon.addEventHandler(cfxPlayerScore.handlePlayerEvent,
|
dcsCommon.addEventHandler(cfxPlayerScore.handlePlayerEvent,
|
||||||
cfxPlayerScore.preProcessor,
|
cfxPlayerScore.preProcessor,
|
||||||
cfxPlayerScore.postProcessor)
|
cfxPlayerScore.postProcessor)
|
||||||
|
|
||||||
-- now load all save data and populate map with troops that
|
-- now load all save data and populate map with troops that
|
||||||
-- we deployed last save.
|
-- we deployed last save.
|
||||||
if persistence then
|
if persistence then
|
||||||
@@ -1543,7 +1427,6 @@ function cfxPlayerScore.start()
|
|||||||
|
|
||||||
-- start update
|
-- start update
|
||||||
cfxPlayerScore.update()
|
cfxPlayerScore.update()
|
||||||
|
|
||||||
trigger.action.outText("cfxPlayerScore v" .. cfxPlayerScore.version .. " started", 30)
|
trigger.action.outText("cfxPlayerScore v" .. cfxPlayerScore.version .. " started", 30)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
cfxPlayerScoreUI = {}
|
cfxPlayerScoreUI = {}
|
||||||
cfxPlayerScoreUI.version = "2.1.0"
|
cfxPlayerScoreUI.version = "3.0.0"
|
||||||
cfxPlayerScoreUI.verbose = false
|
cfxPlayerScoreUI.verbose = false
|
||||||
|
|
||||||
--[[-- VERSION HISTORY
|
--[[-- VERSION HISTORY
|
||||||
@@ -11,6 +11,7 @@ cfxPlayerScoreUI.verbose = false
|
|||||||
- score summary for side
|
- score summary for side
|
||||||
- allowAll
|
- allowAll
|
||||||
- 2.1.1 - minor cleanup
|
- 2.1.1 - minor cleanup
|
||||||
|
- 3.0.0 - compatible with dynamic groups/units in DCS 2.9.6
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
cfxPlayerScoreUI.requiredLibs = {
|
cfxPlayerScoreUI.requiredLibs = {
|
||||||
@@ -19,7 +20,7 @@ cfxPlayerScoreUI.requiredLibs = {
|
|||||||
"cfxPlayerScore",
|
"cfxPlayerScore",
|
||||||
}
|
}
|
||||||
cfxPlayerScoreUI.soundFile = "Quest Snare 3.wav"
|
cfxPlayerScoreUI.soundFile = "Quest Snare 3.wav"
|
||||||
cfxPlayerScoreUI.rootCommands = {} -- by unit's GROUP name, for player aircraft
|
cfxPlayerScoreUI.rootCommands = {} -- by unit's GROUP name, for player aircraft. stores command roots
|
||||||
cfxPlayerScoreUI.allowAll = true
|
cfxPlayerScoreUI.allowAll = true
|
||||||
cfxPlayerScoreUI.ranked = true
|
cfxPlayerScoreUI.ranked = true
|
||||||
|
|
||||||
|
|||||||
@@ -1,30 +1,23 @@
|
|||||||
radioMenu = {}
|
radioMenu = {}
|
||||||
radioMenu.version = "3.0.0"
|
radioMenu.version = "4.0.0"
|
||||||
radioMenu.verbose = false
|
radioMenu.verbose = false
|
||||||
radioMenu.ups = 1
|
radioMenu.ups = 1
|
||||||
radioMenu.requiredLibs = {
|
radioMenu.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
"cfxZones", -- Zones, of course
|
"cfxZones", -- Zones, of course
|
||||||
}
|
}
|
||||||
|
-- note: cfxMX is optional unless using types or groups attributes
|
||||||
radioMenu.menus = {}
|
radioMenu.menus = {}
|
||||||
radioMenu.mainMenus = {} -- dict
|
radioMenu.mainMenus = {} -- dict
|
||||||
|
radioMenu.lateGroups = {} -- dict by ID
|
||||||
|
|
||||||
--[[--
|
--[[--
|
||||||
Version History
|
Version History
|
||||||
2.1.0 - valA/valB/valC/valD attributes
|
|
||||||
OOP cfxZones
|
|
||||||
corrected CD setting for "D"
|
|
||||||
ackA, ackB, ackC, ackD attributes
|
|
||||||
valA-D now define full method, not just values
|
|
||||||
full wildcard support for ack and cooldown
|
|
||||||
2.1.1 - outMessage now works correctly
|
|
||||||
2.2.0 - clean-up
|
|
||||||
2.2.1 - corrected ackD
|
|
||||||
2.3.0 - added wildcard "*" ability for group name match
|
|
||||||
- added ackASnd .. ackDSnd sounds as options
|
|
||||||
3.0.0 - new radioMainMenu and attachTo: mechanics
|
3.0.0 - new radioMainMenu and attachTo: mechanics
|
||||||
cascading radioMainMenu support
|
cascading radioMainMenu support
|
||||||
detect cyclic references
|
detect cyclic references
|
||||||
|
4.0.0 - added infrastructure for dynamic players (DCS 2.9.6)
|
||||||
|
- added dynamic player support
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
function radioMenu.addRadioMenu(theZone)
|
function radioMenu.addRadioMenu(theZone)
|
||||||
@@ -53,15 +46,33 @@ end
|
|||||||
--
|
--
|
||||||
-- read zone
|
-- read zone
|
||||||
--
|
--
|
||||||
|
function radioMenu.lateFilterPlayerForType(theUnit, theZone)
|
||||||
|
-- returns true if theUnit matches zone's group criterium
|
||||||
|
-- false otherwise
|
||||||
|
if not theUnit then return false end
|
||||||
|
local theGroup = theUnit:getGroup()
|
||||||
|
local theCat = theGroup:getCategory() -- 0 == aircraft 1 = plance
|
||||||
|
local lateGroupName = theGroup:getName()
|
||||||
|
local lateType = theUnit:getTypeName()
|
||||||
|
local allTypes = theZone.menuTypes -- {}
|
||||||
|
for idx, aType in pairs(allTypes) do
|
||||||
|
local lowerType = string.lower(aType)
|
||||||
|
if dcsCommon.stringStartsWith(lowerType, "helo") or dcsCommon.stringStartsWith(lowerType, "heli") or
|
||||||
|
aType == "helicopter" then
|
||||||
|
if theCat == 1 then return true end
|
||||||
|
elseif lowerType == "plane" or lowerType == "planes" then
|
||||||
|
if theCat == 0 then return true end
|
||||||
|
else
|
||||||
|
if aType == lateType then return true end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
function radioMenu.filterPlayerIDForType(theZone)
|
function radioMenu.filterPlayerIDForType(theZone)
|
||||||
-- note: we currently ignore coalition
|
-- note: we currently ignore coalition
|
||||||
local theIDs = {}
|
local theIDs = {}
|
||||||
local allTypes = {}
|
local allTypes = theZone.menuTypes -- {}
|
||||||
if dcsCommon.containsString(theZone.menuTypes, ",") then
|
|
||||||
allTypes = dcsCommon.splitString(theZone.menuTypes, ",")
|
|
||||||
else
|
|
||||||
table.insert(allTypes, theZone.menuTypes)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- now iterate all types, and include any player that matches
|
-- now iterate all types, and include any player that matches
|
||||||
-- note that players may match twice, so we use a dict
|
-- note that players may match twice, so we use a dict
|
||||||
@@ -116,16 +127,47 @@ function radioMenu.filterPlayerIDForType(theZone)
|
|||||||
return theIDs
|
return theIDs
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function radioMenu.lateFilterPlayerForGroup(theUnit, theZone)
|
||||||
|
-- returns true if theUnit matches zone's group criterium
|
||||||
|
-- false otherwise. NO COA CHECK
|
||||||
|
if not theUnit then return false end
|
||||||
|
local theGroup = theUnit:getGroup()
|
||||||
|
local lateGroupName = theGroup:getName()
|
||||||
|
for idx, gName in pairs(theZone.menuGroup) do
|
||||||
|
if dcsCommon.stringEndsWith(gName, "*") then
|
||||||
|
-- we must check all group names if they start with the
|
||||||
|
-- the same root. WARNING: CASE-SENSITIVE!!!!
|
||||||
|
gName = dcsCommon.removeEnding(gName, "*")
|
||||||
|
if dcsCommon.stringStartsWith(lateGroupName, gName) then
|
||||||
|
-- group match, install menu
|
||||||
|
if theZone.verbose or radioMenu.verbose then
|
||||||
|
trigger.action.outText("+++menu: WILDCARD Player Group <" .. gName .. "*> matched with <" .. mxName .. ">: gID = <" .. gID .. ">", 30)
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if lateGroupName == gName then
|
||||||
|
if theZone.verbose or radioMenu.verbose then
|
||||||
|
trigger.action.outText("+++menu: Player Group <" .. gName .. "> found: <" .. gID .. ">", 30)
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function radioMenu.lateFilterCoaForUnit(theUnit, theZone)
|
||||||
|
if theZone.coalition == 0 then return true end
|
||||||
|
if theZone.coalition == theUnit:getCoalition() then return true end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
function radioMenu.filterPlayerIDForGroup(theZone)
|
function radioMenu.filterPlayerIDForGroup(theZone)
|
||||||
-- create an iterable list of groups, separated by commas
|
-- create an iterable list of groups, separated by commas
|
||||||
-- note that we could introduce wildcards for groups later
|
-- note that we could introduce wildcards for groups later
|
||||||
local theIDs = {}
|
local theIDs = {}
|
||||||
local allGroups = {}
|
local allGroups = theZone.menuGroup
|
||||||
if dcsCommon.containsString(theZone.menuGroup, ",") then
|
|
||||||
allGroups = dcsCommon.splitString(theZone.menuGroup, ",")
|
|
||||||
else
|
|
||||||
table.insert(allGroups, theZone.menuGroup)
|
|
||||||
end
|
|
||||||
|
|
||||||
for idx, gName in pairs(allGroups) do
|
for idx, gName in pairs(allGroups) do
|
||||||
-- if gName ends in wildcard "*" we process differently
|
-- if gName ends in wildcard "*" we process differently
|
||||||
@@ -162,6 +204,45 @@ function radioMenu.filterPlayerIDForGroup(theZone)
|
|||||||
return theIDs
|
return theIDs
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function radioMenu.lateInstallMenu(theUnit, theZone)
|
||||||
|
-- we only add the group-individual menus (type/group).
|
||||||
|
-- all higher-level menus have been installed already
|
||||||
|
local theGroup = theUnit:getGroup()
|
||||||
|
local grp = theGroup:getID()
|
||||||
|
local gName = theGroup:getName()
|
||||||
|
radioMenu.lateGroups[grp] = gName
|
||||||
|
if not theZone.rootMenu then theZone.rootMenu = {} end
|
||||||
|
if theZone.attachTo then
|
||||||
|
local mainMenu = theZone.attachTo
|
||||||
|
theZone.mainRoot = radioMenu.getMainMenuFor(mainMenu, theZone, grp)
|
||||||
|
end
|
||||||
|
if theZone.menuGroup or theZone.menuTypes then
|
||||||
|
-- install menu, drop through to below
|
||||||
|
else
|
||||||
|
--trigger.action.outText("late-skipped menu for <" .. theZone.name .. ">, no group or type dependency", 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local aRoot = missionCommands.addSubMenuForGroup(grp, theZone.rootName, theZone.mainRoot)
|
||||||
|
theZone.rootMenu[grp] = aRoot
|
||||||
|
theZone.mcdA[grp] = 0
|
||||||
|
theZone.mcdB[grp] = 0
|
||||||
|
theZone.mcdC[grp] = 0
|
||||||
|
theZone.mcdD[grp] = 0
|
||||||
|
if theZone.itemA then
|
||||||
|
theZone.menuA[grp] = missionCommands.addCommandForGroup(grp, theZone.itemA, theZone.rootMenu[grp], radioMenu.redirectMenuX, {theZone, "A", grp})
|
||||||
|
end
|
||||||
|
if theZone.itemB then
|
||||||
|
theZone.menuB[grp] = missionCommands.addCommandForGroup(grp, theZone.itemB, theZone.rootMenu[grp], radioMenu.redirectMenuX, {theZone, "B", grp})
|
||||||
|
end
|
||||||
|
if theZone.itemC then
|
||||||
|
theZone.menuC[grp] = missionCommands.addCommandForGroup(grp, theZone.itemC, theZone.rootMenu[grp], radioMenu.redirectMenuX, {theZone, "C", grp})
|
||||||
|
end
|
||||||
|
if theZone.itemD then
|
||||||
|
theZone.menuD[grp] = missionCommands.addCommandForGroup(grp, theZone.itemD, theZone.rootMenu[grp], radioMenu.redirectMenuX, {theZone, "D", grp})
|
||||||
|
end
|
||||||
|
--trigger.action.outText("completed late-add menu for <" .. theZone.name .. "> to <" .. theUnit:getName() .. ">", 30)
|
||||||
|
end
|
||||||
|
|
||||||
function radioMenu.installMenu(theZone)
|
function radioMenu.installMenu(theZone)
|
||||||
local gID = nil
|
local gID = nil
|
||||||
if theZone.menuGroup then
|
if theZone.menuGroup then
|
||||||
@@ -220,8 +301,8 @@ function radioMenu.installMenu(theZone)
|
|||||||
theZone.rootMenu[0] = missionCommands.addSubMenuForCoalition(theZone.coalition, theZone.rootName, theZone.mainRoot)
|
theZone.rootMenu[0] = missionCommands.addSubMenuForCoalition(theZone.coalition, theZone.rootName, theZone.mainRoot)
|
||||||
end
|
end
|
||||||
|
|
||||||
if theZone:hasProperty("itemA") then
|
if theZone.itemA then -- :hasProperty("itemA") then
|
||||||
local menuA = theZone:getStringFromZoneProperty("itemA", "<no A submenu>")
|
local menuA = theZone.itemA -- theZone:getStringFromZoneProperty("itemA", "<no A submenu>")
|
||||||
if theZone.menuGroup or theZone.menuTypes then
|
if theZone.menuGroup or theZone.menuTypes then
|
||||||
theZone.menuA = {}
|
theZone.menuA = {}
|
||||||
for idx, grp in pairs(gID) do
|
for idx, grp in pairs(gID) do
|
||||||
@@ -234,8 +315,8 @@ function radioMenu.installMenu(theZone)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if theZone:hasProperty("itemB") then
|
if theZone.itemB then --:hasProperty("itemB") then
|
||||||
local menuB = theZone:getStringFromZoneProperty("itemB", "<no B submenu>")
|
local menuB = theZone.itemB -- :getStringFromZoneProperty("itemB", "<no B submenu>")
|
||||||
if theZone.menuGroup or theZone.menuTypes then
|
if theZone.menuGroup or theZone.menuTypes then
|
||||||
theZone.menuB = {}
|
theZone.menuB = {}
|
||||||
for idx, grp in pairs(gID) do
|
for idx, grp in pairs(gID) do
|
||||||
@@ -248,8 +329,8 @@ function radioMenu.installMenu(theZone)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if theZone:hasProperty("itemC") then
|
if theZone.itemC then --:hasProperty("itemC") then
|
||||||
local menuC = theZone:getStringFromZoneProperty("itemC", "<no C submenu>")
|
local menuC = theZone.itemC -- :getStringFromZoneProperty("itemC", "<no C submenu>")
|
||||||
if theZone.menuGroup or theZone.menuTypes then
|
if theZone.menuGroup or theZone.menuTypes then
|
||||||
theZone.menuC = {}
|
theZone.menuC = {}
|
||||||
for idx, grp in pairs(gID) do
|
for idx, grp in pairs(gID) do
|
||||||
@@ -262,8 +343,8 @@ function radioMenu.installMenu(theZone)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if theZone:hasProperty("itemD") then
|
if theZone.itemD then -- :hasProperty("itemD") then
|
||||||
local menuD = theZone:getStringFromZoneProperty("itemD", "<no D submenu>")
|
local menuD = theZone.itemD -- :getStringFromZoneProperty("itemD", "<no D submenu>")
|
||||||
if theZone.menuGroup or theZone.menuTypes then
|
if theZone.menuGroup or theZone.menuTypes then
|
||||||
theZone.menuD = {}
|
theZone.menuD = {}
|
||||||
for idx, grp in pairs(gID) do
|
for idx, grp in pairs(gID) do
|
||||||
@@ -292,6 +373,20 @@ function radioMenu.createRadioMenuWithZone(theZone)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- read items and their stuff
|
||||||
|
if theZone:hasProperty("itemA") then
|
||||||
|
theZone.itemA = theZone:getStringFromZoneProperty("itemA")
|
||||||
|
end
|
||||||
|
if theZone:hasProperty("itemB") then
|
||||||
|
theZone.itemB = theZone:getStringFromZoneProperty("itemB")
|
||||||
|
end
|
||||||
|
if theZone:hasProperty("itemC") then
|
||||||
|
theZone.itemC = theZone:getStringFromZoneProperty("itemC")
|
||||||
|
end
|
||||||
|
if theZone:hasProperty("itemD") then
|
||||||
|
theZone.itemD = theZone:getStringFromZoneProperty("itemD")
|
||||||
|
end
|
||||||
|
|
||||||
theZone.coalition = theZone:getCoalitionFromZoneProperty("coalition", 0)
|
theZone.coalition = theZone:getCoalitionFromZoneProperty("coalition", 0)
|
||||||
-- groups / types
|
-- groups / types
|
||||||
if theZone:hasProperty("group") then
|
if theZone:hasProperty("group") then
|
||||||
@@ -306,6 +401,27 @@ function radioMenu.createRadioMenuWithZone(theZone)
|
|||||||
theZone.menuTypes = theZone:getStringFromZoneProperty("types", "none")
|
theZone.menuTypes = theZone:getStringFromZoneProperty("types", "none")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- now process menugroups and create sets to improve later speed
|
||||||
|
if theZone.menuGroup then
|
||||||
|
if dcsCommon.containsString(theZone.menuGroup, ",") then
|
||||||
|
local allGroups = dcsCommon.splitString(theZone.menuGroup, ",")
|
||||||
|
allGroups = dcsCommon.trimArray(allGroups)
|
||||||
|
theZone.menuGroup = allGroups
|
||||||
|
else
|
||||||
|
theZone.menuGroup = {theZone.menuGroup} --table.insert(allGroups, theZone.menuGroup)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if theZone.menuTypes then
|
||||||
|
if dcsCommon.containsString(theZone.menuTypes, ",") then
|
||||||
|
local allTypes = dcsCommon.splitString(theZone.menuTypes, ",")
|
||||||
|
allTypes = dcsCommon.trimArray(allTypes)
|
||||||
|
theZone.menuTypes = allTypes
|
||||||
|
else
|
||||||
|
theZone.menuTypes = {theZone.menuTypes}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
theZone.menuVisible = theZone:getBoolFromZoneProperty("menuVisible", true)
|
theZone.menuVisible = theZone:getBoolFromZoneProperty("menuVisible", true)
|
||||||
|
|
||||||
-- install menu if not hidden
|
-- install menu if not hidden
|
||||||
@@ -392,13 +508,25 @@ end
|
|||||||
function radioMenu.getMainMenuFor(mainMenu, theZone, idx)
|
function radioMenu.getMainMenuFor(mainMenu, theZone, idx)
|
||||||
if not idx then idx = 0 end
|
if not idx then idx = 0 end
|
||||||
if not mainMenu.rootMenu[idx] then
|
if not mainMenu.rootMenu[idx] then
|
||||||
-- trigger.action.outText("main <" .. mainMenu.name .. "> for zone <" .. theZone.name .. ">: forcing idx to 0", 30)
|
|
||||||
return mainMenu.rootMenu[0]
|
return mainMenu.rootMenu[0]
|
||||||
end
|
end
|
||||||
-- trigger.action.outText("good main <" .. mainMenu.name .. "> for zone <" .. theZone.name .. ">", 30)
|
|
||||||
return mainMenu.rootMenu[idx]
|
return mainMenu.rootMenu[idx]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function radioMenu.lateInstallMainMenu(theUnit, theZone)
|
||||||
|
local theGroup = theUnit:getGroup()
|
||||||
|
local grp = theGroup:getID()
|
||||||
|
local mainRoot = nil
|
||||||
|
if not theZone.rootMenu then theZone.rootMenu = {} end
|
||||||
|
if theZone.attachTo then
|
||||||
|
local mainMenu = theZone.attachTo
|
||||||
|
mainRoot = radioMenu.getMainMenuFor(mainMenu, theZone, grp)
|
||||||
|
end
|
||||||
|
local aRoot = missionCommands.addSubMenuForGroup(grp, theZone.rootName, mainRoot)
|
||||||
|
theZone.rootMenu[grp] = aRoot
|
||||||
|
--trigger.action.outText("menu: late-attached main menu <" .. theZone.name .. "> for unit <" .. theUnit:getName() .. ">", 30)
|
||||||
|
end
|
||||||
|
|
||||||
function radioMenu.installMainMenu(theZone)
|
function radioMenu.installMainMenu(theZone)
|
||||||
local gID = nil -- set of all groups this menu applies to
|
local gID = nil -- set of all groups this menu applies to
|
||||||
if theZone.menuGroup then
|
if theZone.menuGroup then
|
||||||
@@ -490,6 +618,27 @@ function radioMenu.createRadioMainMenuWithZone(theZone)
|
|||||||
elseif theZone:hasProperty("types") then
|
elseif theZone:hasProperty("types") then
|
||||||
theZone.menuTypes = theZone:getStringFromZoneProperty("types", "none")
|
theZone.menuTypes = theZone:getStringFromZoneProperty("types", "none")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- now process menugroups and create sets to improve later speed
|
||||||
|
if theZone.menuGroup then
|
||||||
|
if dcsCommon.containsString(theZone.menuGroup, ",") then
|
||||||
|
local allGroups = dcsCommon.splitString(theZone.menuGroup, ",")
|
||||||
|
allGroups = dcsCommon.trimArray(allGroups)
|
||||||
|
theZone.menuGroup = allGroups
|
||||||
|
else
|
||||||
|
theZone.menuGroup = {theZone.menuGroup} --table.insert(allGroups, theZone.menuGroup)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if theZone.menuTypes then
|
||||||
|
if dcsCommon.containsString(theZone.menuTypes, ",") then
|
||||||
|
local allTypes = dcsCommon.splitString(theZone.menuTypes, ",")
|
||||||
|
allTypes = dcsCommon.trimArray(allTypes)
|
||||||
|
theZone.menuTypes = allTypes
|
||||||
|
else
|
||||||
|
theZone.menuTypes = {theZone.menuTypes}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- always install this one
|
-- always install this one
|
||||||
radioMenu.installMainMenu(theZone)
|
radioMenu.installMainMenu(theZone)
|
||||||
@@ -523,6 +672,8 @@ function radioMenu.radioOutMsg(ack, gid, theZone)
|
|||||||
local theMsg = ack
|
local theMsg = ack
|
||||||
if (gid > 0) and cfxMX then
|
if (gid > 0) and cfxMX then
|
||||||
local gName = cfxMX.groupNamesByID[gid]
|
local gName = cfxMX.groupNamesByID[gid]
|
||||||
|
if not gName then gName = radioMenu.lateGroups[gid] end
|
||||||
|
if not gName then gName = "?*?*?" end
|
||||||
theMsg = theMsg:gsub("<group>", gName)
|
theMsg = theMsg:gsub("<group>", gName)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -566,9 +717,9 @@ function radioMenu.setCDByGID(cd, theZone, gID, newVal)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function radioMenu.doMenuX(args)
|
function radioMenu.doMenuX(args)
|
||||||
theZone = args[1]
|
local theZone = args[1]
|
||||||
theItemIndex = args[2] -- A, B , C .. ?
|
local theItemIndex = args[2] -- A, B , C .. ?
|
||||||
theGroup = args[3] -- can be nil or groupID
|
local theGroup = args[3] -- can be nil or groupID
|
||||||
if not theGroup then theGroup = 0 end
|
if not theGroup then theGroup = 0 end
|
||||||
|
|
||||||
local cd = radioMenu.cdByGID(theZone.mcdA, theZone, theGroup) --theZone.mcdA
|
local cd = radioMenu.cdByGID(theZone.mcdA, theZone, theGroup) --theZone.mcdA
|
||||||
@@ -688,6 +839,74 @@ function radioMenu.update()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- onEvent - late dynamic spawns
|
||||||
|
--
|
||||||
|
function radioMenu.lateMenuAddForUnit(theUnit)
|
||||||
|
-- iterate all menu zones and determine if this menu is to
|
||||||
|
-- install
|
||||||
|
local uName = theUnit:getName()
|
||||||
|
for zName, theZone in pairs(radioMenu.mainMenus) do
|
||||||
|
-- determine if this applies to us, and if so, install
|
||||||
|
-- for unit. only do this if menuGroups or menuTypes present
|
||||||
|
-- all others are set on coa level or higher
|
||||||
|
--trigger.action.outText("late-proccing MAIN menu <" .. zName ..">", 30)
|
||||||
|
if theZone.menuGroup then
|
||||||
|
if radioMenu.lateFilterCoaForUnit(theUnit, theZone) and
|
||||||
|
radioMenu.lateFilterPlayerForGroup(theUnit, theZone) then
|
||||||
|
radioMenu.lateInstallMainMenu(theUnit, theZone)
|
||||||
|
else
|
||||||
|
--trigger.action.outText("menu: skipped GROUP main menu <" .. zName .. "> add for <" .. uName .. ">", 30)
|
||||||
|
end
|
||||||
|
elseif theZone.menuTypes then
|
||||||
|
if radioMenu.lateFilterCoaForUnit(theUnit, theZone) and
|
||||||
|
radioMenu.lateFilterPlayerForType(theUnit, theZone) then
|
||||||
|
radioMenu.lateInstallMainMenu(theUnit, theZone)
|
||||||
|
else
|
||||||
|
--trigger.action.outText("menu: skipped TYPE main menu <" .. zName .. "> add for <" .. uName .. ">", 30)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- nothing to do, was attached for coalition or higher
|
||||||
|
--trigger.action.outText("menu: did not late-install main menu <" .. zName .. ">, no group or type restrictions", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for idx, theZone in pairs(radioMenu.menus) do
|
||||||
|
-- again, does this apply to us?
|
||||||
|
--trigger.action.outText("late-proccing menu <" .. theZone.name ..">", 30)
|
||||||
|
if theZone.menuGroup then
|
||||||
|
if radioMenu.lateFilterCoaForUnit(theUnit, theZone) and
|
||||||
|
radioMenu.lateFilterPlayerForGroup(theUnit, theZone) then
|
||||||
|
radioMenu.lateInstallMenu(theUnit, theZone)
|
||||||
|
else
|
||||||
|
--trigger.action.outText("menu: skipped GROUP main menu <" .. theZone.name .. "> add for <" .. uName .. ">", 30)
|
||||||
|
end
|
||||||
|
elseif theZone.menuTypes then
|
||||||
|
if radioMenu.lateFilterCoaForUnit(theUnit, theZone) and
|
||||||
|
radioMenu.lateFilterPlayerForType(theUnit, theZone) then
|
||||||
|
radioMenu.lateInstallMenu(theUnit, theZone)
|
||||||
|
else
|
||||||
|
--trigger.action.outText("menu: skipped TYPE main menu <" .. theZone.name .. "> add for <" .. uName .. ">", 30)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- nothing to do, was attached for coalition or higher
|
||||||
|
--trigger.action.outText("menu: did not late-install STD menu <" .. theZone.name .. ">, no group or type restrictions", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function radioMenu:onEvent(event)
|
||||||
|
if not event then return end
|
||||||
|
if not event.initiator then return end
|
||||||
|
local theUnit = event.initiator
|
||||||
|
if event.id == 15 then
|
||||||
|
if not cfxMX.isDynamicPlayer(theUnit) then return end
|
||||||
|
-- we have a dynamic unit spawn
|
||||||
|
--trigger.action.outText("menu: detected dynamic spawn <" .. theUnit:getName() .. ">", 30)
|
||||||
|
radioMenu.lateMenuAddForUnit(theUnit)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Config & Start
|
-- Config & Start
|
||||||
@@ -756,6 +975,9 @@ function radioMenu.start()
|
|||||||
radioMenu.addRadioMenu(aZone) -- add to list
|
radioMenu.addRadioMenu(aZone) -- add to list
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- install late spawn detector
|
||||||
|
world.addEventHandler(radioMenu)
|
||||||
|
|
||||||
-- start update
|
-- start update
|
||||||
radioMenu.update()
|
radioMenu.update()
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
reaper = {}
|
reaper = {}
|
||||||
reaper.version = "1.1.0"
|
reaper.version = "1.2.0"
|
||||||
reaper.requiredLibs = {
|
reaper.requiredLibs = {
|
||||||
"dcsCommon",
|
"dcsCommon",
|
||||||
"cfxZones",
|
"cfxZones",
|
||||||
@@ -22,6 +22,7 @@ VERSION HISTORY
|
|||||||
- added FAC task
|
- added FAC task
|
||||||
- split task generation from wp generation
|
- split task generation from wp generation
|
||||||
- updated reaper naming, uniqueNames attribute (undocumented)
|
- updated reaper naming, uniqueNames attribute (undocumented)
|
||||||
|
1.2.0 - support twn when present
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
@@ -290,12 +291,21 @@ function reaper.setTarget(theZone, theTarget, cycled)
|
|||||||
local lp = theTarget:getPoint()
|
local lp = theTarget:getPoint()
|
||||||
local lat, lon, alt = coord.LOtoLL(lp)
|
local lat, lon, alt = coord.LOtoLL(lp)
|
||||||
lat, lon = dcsCommon.latLon2Text(lat, lon)
|
lat, lon = dcsCommon.latLon2Text(lat, lon)
|
||||||
|
local twnLoc = ""
|
||||||
|
if twn and towns then
|
||||||
|
local name, data, dist = twn.closestTownTo(lp)
|
||||||
|
local mdist= dist * 0.539957
|
||||||
|
dist = math.floor(dist/100) / 10
|
||||||
|
mdist = math.floor(mdist/100) / 10
|
||||||
|
local bear = dcsCommon.compassPositionOfARelativeToB(lp, data.p)
|
||||||
|
twnLoc = " (" ..dist .. "km/" .. mdist .."nm " .. bear .. " of " .. name .. ")"
|
||||||
|
end
|
||||||
|
|
||||||
local theSpot = Spot.createLaser(theZone.theUav, {0, 2, 0}, lp, theZone.code)
|
local theSpot = Spot.createLaser(theZone.theUav, {0, 2, 0}, lp, theZone.code)
|
||||||
if theZone.doSmoke then
|
if theZone.doSmoke then
|
||||||
trigger.action.smoke(lp , theZone.smokeColor )
|
trigger.action.smoke(lp , theZone.smokeColor )
|
||||||
end
|
end
|
||||||
trigger.action.outTextForCoalition(theZone.coa, "Drone <" .. theZone.name .. "> is tracking a <" .. theTarget:getTypeName() .. "> at " .. lat .. " " .. lon .. ", code " .. theZone.code, 30)
|
trigger.action.outTextForCoalition(theZone.coa, "Drone <" .. theZone.name .. "> is tracking a <" .. theTarget:getTypeName() .. "> at " .. lat .. " " .. lon .. twnLoc ..", code " .. theZone.code, 30)
|
||||||
trigger.action.outSoundForCoalition(theZone.coa, reaper.actionSound)
|
trigger.action.outSoundForCoalition(theZone.coa, reaper.actionSound)
|
||||||
theZone.theTarget = theTarget
|
theZone.theTarget = theTarget
|
||||||
if theZone.theSpot then
|
if theZone.theSpot then
|
||||||
@@ -599,14 +609,23 @@ function reaper.doDroneStatus(args)
|
|||||||
local lat, lon, alt = coord.LOtoLL(lp)
|
local lat, lon, alt = coord.LOtoLL(lp)
|
||||||
lat, lon = dcsCommon.latLon2Text(lat, lon)
|
lat, lon = dcsCommon.latLon2Text(lat, lon)
|
||||||
local ut = theTarget:getTypeName()
|
local ut = theTarget:getTypeName()
|
||||||
msg = msg .. ut .. " at " .. lat .. ", " .. lon .. " code " .. theZone.code
|
local twnLoc = ""
|
||||||
|
if twn and towns then
|
||||||
|
local tname, data, dist = twn.closestTownTo(lp)
|
||||||
|
local mdist= dist * 0.539957
|
||||||
|
dist = math.floor(dist/100) / 10
|
||||||
|
mdist = math.floor(mdist/100) / 10
|
||||||
|
local bear = dcsCommon.compassPositionOfARelativeToB(lp, data.p)
|
||||||
|
twnLoc = " (" ..dist .. "km/" .. mdist .."nm " .. bear .. " of " .. tname .. ") "
|
||||||
|
end
|
||||||
|
msg = msg .. ut .. " at " .. lat .. ", " .. lon .. twnLoc .. " code " .. theZone.code
|
||||||
else
|
else
|
||||||
msg = msg .. "<signal failure, please try later>"
|
msg = msg .. "<signal failure, please try later>"
|
||||||
end
|
end
|
||||||
done[name] = true
|
done[name] = true
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
msg = msg .. "\n(No drones are tracking a target)\n"
|
msg = msg .. "\n(No drones are tracking targets)\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- collect loitering drones
|
-- collect loitering drones
|
||||||
@@ -661,7 +680,16 @@ function reaper.doSingleDroneStatus(theZone)
|
|||||||
local lat, lon, alt = coord.LOtoLL(lp)
|
local lat, lon, alt = coord.LOtoLL(lp)
|
||||||
lat, lon = dcsCommon.latLon2Text(lat, lon)
|
lat, lon = dcsCommon.latLon2Text(lat, lon)
|
||||||
local ut = theTarget:getTypeName()
|
local ut = theTarget:getTypeName()
|
||||||
msg = msg .. ut .. " at " .. lat .. ", " .. lon .. " code " .. theZone.code
|
local twnLoc = ""
|
||||||
|
if twn and towns then
|
||||||
|
local tname, data, dist = twn.closestTownTo(lp)
|
||||||
|
local mdist= dist * 0.539957
|
||||||
|
dist = math.floor(dist/100) / 10
|
||||||
|
mdist = math.floor(mdist/100) / 10
|
||||||
|
local bear = dcsCommon.compassPositionOfARelativeToB(lp, data.p)
|
||||||
|
twnLoc = " (" ..dist .. "km/" .. mdist .."nm " .. bear .. " of " .. tname .. ") "
|
||||||
|
end
|
||||||
|
msg = msg .. ut .. " at " .. lat .. ", " .. lon .. twnLoc .. " code " .. theZone.code
|
||||||
|
|
||||||
-- now add full group intelligence
|
-- now add full group intelligence
|
||||||
local collector = {}
|
local collector = {}
|
||||||
@@ -886,7 +914,6 @@ function reaper.start()
|
|||||||
timer.scheduleFunction(reaper.update, {}, timer.getTime() + 1)
|
timer.scheduleFunction(reaper.update, {}, timer.getTime() + 1)
|
||||||
|
|
||||||
-- schedule scan and track loops
|
-- schedule scan and track loops
|
||||||
-- timer.scheduleFunction(reaper.scan, {}, timer.getTime() + 1)
|
|
||||||
timer.scheduleFunction(reaper.scanALT, {}, timer.getTime() + 1)
|
timer.scheduleFunction(reaper.scanALT, {}, timer.getTime() + 1)
|
||||||
timer.scheduleFunction(reaper.track, {}, timer.getTime() + 1)
|
timer.scheduleFunction(reaper.track, {}, timer.getTime() + 1)
|
||||||
trigger.action.outText("reaper v " .. reaper.version .. " running.", 30)
|
trigger.action.outText("reaper v " .. reaper.version .. " running.", 30)
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
cfxReconGUI = {}
|
cfxReconGUI = {}
|
||||||
cfxReconGUI.version = "1.0.0"
|
cfxReconGUI.version = "2.0.0"
|
||||||
--[[-- VERSION HISTORY
|
--[[-- VERSION HISTORY
|
||||||
- 1.0.0 - initial version
|
- 1.0.0 - initial version
|
||||||
|
- 2.0.0 - removed dependence on cfxPlayer
|
||||||
|
- compatible with dynamically spawning players
|
||||||
|
- cleanup
|
||||||
--]]--
|
--]]--
|
||||||
-- find & command cfxGroundTroops-based jtacs
|
|
||||||
-- UI installed via OTHER for all groups with players
|
|
||||||
-- module based on xxxGrpUI
|
|
||||||
|
|
||||||
cfxReconGUI.groupConfig = {} -- all inited group private config data
|
cfxReconGUI.groupConfig = {} -- all inited group private config data
|
||||||
cfxReconGUI.simpleCommands = true -- if true, f10 other invokes directly
|
cfxReconGUI.simpleCommands = true -- if true, f10 other invokes directly
|
||||||
|
cfxReconGUI.requiredLibs = {
|
||||||
|
"dcsCommon", -- always
|
||||||
|
"cfxZones", -- Zones, of course
|
||||||
|
}
|
||||||
--
|
--
|
||||||
-- C O N F I G H A N D L I N G
|
-- C O N F I G H A N D L I N G
|
||||||
-- =============================
|
-- =============================
|
||||||
@@ -38,7 +40,6 @@ function cfxReconGUI.createDefaultConfig(theGroup)
|
|||||||
local groupUnits = theGroup:getUnits()
|
local groupUnits = theGroup:getUnits()
|
||||||
conf.unit = groupUnits[1] -- WARNING: ASSUMES ONE-UNIT GROUPS
|
conf.unit = groupUnits[1] -- WARNING: ASSUMES ONE-UNIT GROUPS
|
||||||
cfxReconGUI.resetConfig(conf)
|
cfxReconGUI.resetConfig(conf)
|
||||||
|
|
||||||
conf.mainMenu = nil; -- this is where we store the main menu if we branch
|
conf.mainMenu = nil; -- this is where we store the main menu if we branch
|
||||||
conf.myCommands = nil; -- this is where we store the commands if we branch
|
conf.myCommands = nil; -- this is where we store the commands if we branch
|
||||||
|
|
||||||
@@ -80,12 +81,10 @@ function cfxReconGUI.getConfigForUnit(theUnit)
|
|||||||
return conf
|
return conf
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
|
||||||
--
|
--
|
||||||
-- M E N U H A N D L I N G
|
-- M E N U H A N D L I N G
|
||||||
-- =========================
|
|
||||||
--
|
|
||||||
--
|
--
|
||||||
|
|
||||||
function cfxReconGUI.clearCommsSubmenus(conf)
|
function cfxReconGUI.clearCommsSubmenus(conf)
|
||||||
if conf.myCommands then
|
if conf.myCommands then
|
||||||
for i=1, #conf.myCommands do
|
for i=1, #conf.myCommands do
|
||||||
@@ -96,16 +95,14 @@ end
|
|||||||
end
|
end
|
||||||
|
|
||||||
function cfxReconGUI.removeCommsFromConfig(conf)
|
function cfxReconGUI.removeCommsFromConfig(conf)
|
||||||
cfxReconGUI.clearCommsSubmenus(conf)
|
cfxReconGUI.clearCommsSubmenus(conf)
|
||||||
|
|
||||||
if conf.myMainMenu then
|
if conf.myMainMenu then
|
||||||
missionCommands.removeItemForGroup(conf.id, conf.myMainMenu)
|
missionCommands.removeItemForGroup(conf.id, conf.myMainMenu)
|
||||||
conf.myMainMenu = nil
|
conf.myMainMenu = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- this only works in single-unit groups. may want to check if group
|
-- this only works in single-unit groups
|
||||||
-- has disappeared
|
|
||||||
function cfxReconGUI.removeCommsForUnit(theUnit)
|
function cfxReconGUI.removeCommsForUnit(theUnit)
|
||||||
if not theUnit then return end
|
if not theUnit then return end
|
||||||
if not theUnit:isExist() then return end
|
if not theUnit:isExist() then return end
|
||||||
@@ -121,9 +118,6 @@ function cfxReconGUI.removeCommsForGroup(theGroup)
|
|||||||
cfxReconGUI.removeCommsFromConfig(conf)
|
cfxReconGUI.removeCommsFromConfig(conf)
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
|
||||||
-- set main root in F10 Other. All sub menus click into this
|
|
||||||
--
|
|
||||||
function cfxReconGUI.isEligibleForMenu(theGroup)
|
function cfxReconGUI.isEligibleForMenu(theGroup)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@@ -144,7 +138,7 @@ end
|
|||||||
|
|
||||||
function cfxReconGUI.setCommsMenu(theGroup)
|
function cfxReconGUI.setCommsMenu(theGroup)
|
||||||
-- depending on own load state, we set the command structure
|
-- depending on own load state, we set the command structure
|
||||||
-- it begins at 10-other, and has 'jtac' as main menu with submenus
|
-- it begins at F10-Other, and has 'Recon' as main menu with submenus
|
||||||
-- as required
|
-- as required
|
||||||
if not theGroup then return end
|
if not theGroup then return end
|
||||||
if not theGroup:isExist() then return end
|
if not theGroup:isExist() then return end
|
||||||
@@ -154,8 +148,7 @@ function cfxReconGUI.setCommsMenu(theGroup)
|
|||||||
if not cfxReconGUI.isEligibleForMenu(theGroup) then return end
|
if not cfxReconGUI.isEligibleForMenu(theGroup) then return end
|
||||||
|
|
||||||
local conf = cfxReconGUI.getConfigForGroup(theGroup)
|
local conf = cfxReconGUI.getConfigForGroup(theGroup)
|
||||||
conf.id = theGroup:getID(); -- we do this ALWAYS so it is current even after a crash
|
conf.id = theGroup:getID(); -- we ALWAYSdo this
|
||||||
-- trigger.action.outText("+++ setting group <".. conf.theGroup:getName() .. "> jtac command", 30)
|
|
||||||
|
|
||||||
if cfxReconGUI.simpleCommands then
|
if cfxReconGUI.simpleCommands then
|
||||||
-- we install directly in F-10 other
|
-- we install directly in F-10 other
|
||||||
@@ -190,26 +183,17 @@ function cfxReconGUI.setCommsMenu(theGroup)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- if we don't have an F-10 menu, create one
|
||||||
-- ok, first, if we don't have an F-10 menu, create one
|
|
||||||
if not (conf.myMainMenu) then
|
if not (conf.myMainMenu) then
|
||||||
conf.myMainMenu = missionCommands.addSubMenuForGroup(conf.id, 'Recon')
|
conf.myMainMenu = missionCommands.addSubMenuForGroup(conf.id, 'Recon')
|
||||||
end
|
end
|
||||||
|
|
||||||
-- clear out existing commands
|
-- clear out existing commands
|
||||||
cfxReconGUI.clearCommsSubmenus(conf)
|
cfxReconGUI.clearCommsSubmenus(conf)
|
||||||
|
|
||||||
-- now we have a menu without submenus.
|
|
||||||
-- add our own submenus
|
-- add our own submenus
|
||||||
cfxReconGUI.addSubMenus(conf)
|
cfxReconGUI.addSubMenus(conf)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function cfxReconGUI.addSubMenus(conf)
|
function cfxReconGUI.addSubMenus(conf)
|
||||||
-- add menu items to choose from after
|
|
||||||
-- user clickedf on MAIN MENU. In this implementation
|
|
||||||
-- they all result invoked methods
|
|
||||||
|
|
||||||
local commandTxt = "Recon"
|
local commandTxt = "Recon"
|
||||||
local unitName = "bogus"
|
local unitName = "bogus"
|
||||||
if conf.unit and conf.unit:getName()then
|
if conf.unit and conf.unit:getName()then
|
||||||
@@ -219,32 +203,10 @@ function cfxReconGUI.addSubMenus(conf)
|
|||||||
commandTxt = commandTxt .. "***"
|
commandTxt = commandTxt .. "***"
|
||||||
end
|
end
|
||||||
|
|
||||||
local theCommand = missionCommands.addCommandForGroup(
|
local theCommand = missionCommands.addCommandForGroup(conf.id, commandTxt, conf.myMainMenu, cfxReconGUI.redirectCommandX, {conf, "recon", unitName})
|
||||||
conf.id,
|
|
||||||
commandTxt,
|
|
||||||
conf.myMainMenu,
|
|
||||||
cfxReconGUI.redirectCommandX,
|
|
||||||
{conf, "recon", unitName}
|
|
||||||
)
|
|
||||||
table.insert(conf.myCommands, theCommand)
|
table.insert(conf.myCommands, theCommand)
|
||||||
--[[--
|
|
||||||
commandTxt = "This is another important command"
|
|
||||||
theCommand = missionCommands.addCommandForGroup(
|
|
||||||
conf.id,
|
|
||||||
commandTxt,
|
|
||||||
conf.myMainMenu,
|
|
||||||
cfxReconGUI.redirectCommandX,
|
|
||||||
{conf, "Sub2"}
|
|
||||||
)
|
|
||||||
table.insert(conf.myCommands, theCommand)
|
|
||||||
--]]--
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
|
||||||
-- each menu item has a redirect and timed invoke to divorce from the
|
|
||||||
-- no-debug zone in the menu invocation. Delay is .1 seconds
|
|
||||||
--
|
|
||||||
|
|
||||||
function cfxReconGUI.redirectCommandX(args)
|
function cfxReconGUI.redirectCommandX(args)
|
||||||
timer.scheduleFunction(cfxReconGUI.doCommandX, args, timer.getTime() + 0.1)
|
timer.scheduleFunction(cfxReconGUI.doCommandX, args, timer.getTime() + 0.1)
|
||||||
end
|
end
|
||||||
@@ -260,10 +222,8 @@ function cfxReconGUI.doCommandX(args)
|
|||||||
trigger.action.outText("+++ reconUI: doCommand: BOGUS unitName!", 30)
|
trigger.action.outText("+++ reconUI: doCommand: BOGUS unitName!", 30)
|
||||||
end
|
end
|
||||||
|
|
||||||
local theGroup = conf.theGroup
|
local theGroup = conf.theGroup
|
||||||
-- trigger.action.outTextForGroup(conf.id, "+++ groupUI: processing comms menu for <" .. what .. ">", 30)
|
-- when we get here, we toggle the recon mode
|
||||||
|
|
||||||
-- whenever we get here, we toggle the recon mode
|
|
||||||
local theUnit = conf.unit
|
local theUnit = conf.unit
|
||||||
local message = "Scout ".. unitName .. " has stopped reporting."
|
local message = "Scout ".. unitName .. " has stopped reporting."
|
||||||
local theSide = conf.coalition
|
local theSide = conf.coalition
|
||||||
@@ -285,94 +245,50 @@ function cfxReconGUI.doCommandX(args)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
trigger.action.outTextForCoalition(theSide, message, 30)
|
trigger.action.outTextForCoalition(theSide, message, 30)
|
||||||
|
|
||||||
-- reset comms
|
-- reset comms
|
||||||
cfxReconGUI.removeCommsForGroup(theGroup)
|
cfxReconGUI.removeCommsForGroup(theGroup)
|
||||||
cfxReconGUI.setCommsMenu(theGroup)
|
cfxReconGUI.setCommsMenu(theGroup)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function cfxReconGUI:onEvent(theEvent)
|
||||||
|
if not theEvent then return end
|
||||||
--
|
if not theEvent.initiator then return end
|
||||||
-- G R O U P M A N A G E M E N T
|
local theUnit = theEvent.initiator
|
||||||
--
|
if not Unit.isExist(theUnit) then return end
|
||||||
-- Group Management is required to make sure all groups
|
if not theUnit.getName then return end
|
||||||
-- receive a comms menu and that they receive a clean-up
|
if not theUnit.getGroup then return end
|
||||||
-- when required
|
if not theUnit.getPlayerName then return end
|
||||||
--
|
if not theUnit:getPlayerName() then return end
|
||||||
-- Callbacks are provided by cfxPlayer module to which we
|
local theGroup = theUnit:getGroup()
|
||||||
-- subscribe during init
|
if theEvent.id == 15 then
|
||||||
--
|
-- BIRTH EVENT PLAYER
|
||||||
function cfxReconGUI.playerChangeEvent(evType, description, player, data)
|
local conf = cfxReconGUI.getConfigForGroup(theGroup)
|
||||||
--trigger.action.outText("+++ groupUI: received <".. evType .. "> Event", 30)
|
conf.unit = theUnit --data.primeUnit
|
||||||
if evType == "newGroup" then
|
conf.unitName = theUnit:getName()
|
||||||
-- initialized attributes are in data as follows
|
cfxReconGUI.setCommsMenu(theGroup)
|
||||||
-- .group - new group
|
|
||||||
-- .name - new group's name
|
|
||||||
-- .primeUnit - the unit that trigggered new group appearing
|
|
||||||
-- .primeUnitName - name of prime unit
|
|
||||||
-- .id group ID
|
|
||||||
--theUnit = data.primeUnit
|
|
||||||
-- ensure group data exists and is updated
|
|
||||||
local conf = cfxReconGUI.getConfigForGroup(data.group)
|
|
||||||
conf.unit = data.primeUnit
|
|
||||||
conf.unitName = conf.unit:getName() -- will break if no exist
|
|
||||||
|
|
||||||
cfxReconGUI.setCommsMenu(data.group)
|
|
||||||
-- trigger.action.outText("+++ groupUI: added " .. theUnit:getName() .. " to comms menu", 30)
|
|
||||||
return
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if evType == "removeGroup" then
|
|
||||||
-- data is the player record that no longer exists. it consists of
|
|
||||||
-- .name
|
|
||||||
-- we must remove the comms menu for this group else we try to add another one to this group later
|
|
||||||
local conf = cfxReconGUI.getConfigByGroupName(data.name)
|
|
||||||
|
|
||||||
if conf then
|
|
||||||
cfxReconGUI.removeCommsFromConfig(conf) -- remove menus
|
|
||||||
cfxReconGUI.resetConfig(conf) -- re-init this group for when it re-appears
|
|
||||||
else
|
|
||||||
trigger.action.outText("+++ reconUI: can't retrieve group <" .. data.name .. "> config: not found!", 30)
|
|
||||||
end
|
|
||||||
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if evType == "leave" then
|
|
||||||
-- player unit left. we don't care since we only work on group level
|
|
||||||
-- if they were the only, this is followed up by group disappeared
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
if evType == "unit" then
|
|
||||||
-- player changed units. almost never in MP, but possible in solo
|
|
||||||
-- because of 1 seconds timing loop
|
|
||||||
-- will result in a new group appearing and a group disappearing, so we are good
|
|
||||||
-- may need some logic to clean up old configs and/or menu items
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Start
|
-- Start
|
||||||
--
|
--
|
||||||
function cfxReconGUI.start()
|
function cfxReconGUI.start()
|
||||||
|
-- lib check
|
||||||
-- iterate existing groups so we have a start situation
|
if not dcsCommon.libCheck("cfx Recon Mode",
|
||||||
-- now iterate through all player groups and install the Assault Troop Menu
|
cfxReconMode.requiredLibs) then
|
||||||
allPlayerGroups = cfxPlayerGroups -- cfxPlayerGroups is a global, don't fuck with it!
|
return false
|
||||||
-- contains per group player record. Does not resolve on unit level!
|
|
||||||
for gname, pgroup in pairs(allPlayerGroups) do
|
|
||||||
local theUnit = pgroup.primeUnit -- get any unit of that group
|
|
||||||
cfxReconGUI.setCommsMenuForUnit(theUnit) -- set up
|
|
||||||
end
|
end
|
||||||
-- now install the new group notifier to install Assault Troops menu
|
|
||||||
|
|
||||||
cfxPlayer.addMonitor(cfxReconGUI.playerChangeEvent)
|
-- iterate existing groups so we have a start situation
|
||||||
|
local allPlayerUnits = dcsCommon.getAllExistingPlayerUnitsRaw()
|
||||||
|
for idx, theUnit in pairs(allPlayerUnits) do
|
||||||
|
cfxReconGUI.setCommsMenuForUnit(theUnit)
|
||||||
|
end
|
||||||
|
|
||||||
|
world.addEventHandler(cfxReconGUI)
|
||||||
|
|
||||||
trigger.action.outText("cf/x cfxReconGUI v" .. cfxReconGUI.version .. " started", 30)
|
trigger.action.outText("cf/x cfxReconGUI v" .. cfxReconGUI.version .. " started", 30)
|
||||||
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
cfxReconMode = {}
|
cfxReconMode = {}
|
||||||
cfxReconMode.version = "2.2.2"
|
cfxReconMode.version = "2.3.0"
|
||||||
cfxReconMode.verbose = false -- set to true for debug info
|
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
|
cfxReconMode.reconSound = "UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav" -- to be played when somethiong discovered
|
||||||
|
|
||||||
@@ -60,7 +60,8 @@ VERSION HISTORY
|
|||||||
2.2.1 - fixed "cfxReconSMode" typo
|
2.2.1 - fixed "cfxReconSMode" typo
|
||||||
2.2.2 - added groupNames attribute
|
2.2.2 - added groupNames attribute
|
||||||
- clean-up
|
- clean-up
|
||||||
|
2.3.0 - support for towns/twn when present
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
cfxReconMode.detectionMinRange = 3000 -- meters at ground level
|
cfxReconMode.detectionMinRange = 3000 -- meters at ground level
|
||||||
@@ -394,6 +395,18 @@ function cfxReconMode.getLocation(theGroup)
|
|||||||
lat, lon = dcsCommon.latLon2Text(lat, lon)
|
lat, lon = dcsCommon.latLon2Text(lat, lon)
|
||||||
msg = "Lat " .. lat .. " Lon " .. lon .. " Ele " .. ele ..units
|
msg = "Lat " .. lat .. " Lon " .. lon .. " Ele " .. ele ..units
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if twn and towns then
|
||||||
|
units = "km"
|
||||||
|
local village, data, dist = twn.closestTownTo(currPoint)
|
||||||
|
if cfxReconMode.imperialUnits then
|
||||||
|
dist = dist * 0.539957 -- nm conversion
|
||||||
|
units = "nm"
|
||||||
|
end
|
||||||
|
dist = math.floor(dist/100) / 10
|
||||||
|
local bear = dcsCommon.compassPositionOfARelativeToB(currPoint, data.p)
|
||||||
|
msg = msg .. ", " .. dist .. units .. " " .. bear .. " of " .. village
|
||||||
|
end
|
||||||
return msg
|
return msg
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
scribe = {}
|
scribe = {}
|
||||||
scribe.version = "2.0.0"
|
scribe.version = "2.0.2"
|
||||||
scribe.requiredLibs = {
|
scribe.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
"cfxZones", -- Zones, of course
|
"cfxZones", -- Zones, of course
|
||||||
@@ -13,10 +13,15 @@ VERSION HISTORY
|
|||||||
1.0.1 postponed land, postponed takeoff, unit_lost
|
1.0.1 postponed land, postponed takeoff, unit_lost
|
||||||
1.1.0 supports persistence's SHARED ability to share data across missions
|
1.1.0 supports persistence's SHARED ability to share data across missions
|
||||||
2.0.0 support for main menu
|
2.0.0 support for main menu
|
||||||
|
2.0.1 Hardening for DCS Jul 11 patch issues
|
||||||
|
2.0.2 Secondary landing events correction
|
||||||
|
support for DCS dynamic player spawns
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
scribe.verbose = true
|
scribe.verbose = true
|
||||||
scribe.db = {} -- indexed by player name
|
scribe.db = {} -- indexed by player name
|
||||||
scribe.playerUnits = {} -- indexed by unit name. for crash detection
|
scribe.playerUnits = {} -- indexed by unit name. for crash detection
|
||||||
|
scribe.dynamicPlayers = {}
|
||||||
|
|
||||||
--[[--
|
--[[--
|
||||||
unitEntry:
|
unitEntry:
|
||||||
@@ -157,12 +162,22 @@ end
|
|||||||
-- Event handling
|
-- Event handling
|
||||||
--
|
--
|
||||||
function scribe.playerBirthedIn(playerName, theUnit)
|
function scribe.playerBirthedIn(playerName, theUnit)
|
||||||
-- access db
|
|
||||||
local theEntry = scribe.getPlayerNamed(playerName) -- can be new
|
|
||||||
local myType = theUnit:getTypeName()
|
local myType = theUnit:getTypeName()
|
||||||
local uName = theUnit:getName()
|
local uName = theUnit:getName()
|
||||||
local theGroup = theUnit:getGroup()
|
local theGroup = theUnit:getGroup()
|
||||||
local gID = theGroup:getID()
|
local gID = theGroup:getID()
|
||||||
|
-- install menu if dynamic plane and not defined already
|
||||||
|
if cfxMX.isDynamicPlayer(theUnit) then
|
||||||
|
local gName = theGroup:getName()
|
||||||
|
if not scribe.dynamicPlayers[gName] then
|
||||||
|
scribe.installDynamicPlayerMenu(theUnit)
|
||||||
|
scribe.dynamicPlayers[gName] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- access db
|
||||||
|
local theEntry = scribe.getPlayerNamed(playerName) -- can be new
|
||||||
|
|
||||||
-- check if this player is still active
|
-- check if this player is still active
|
||||||
if theEntry.isActive then
|
if theEntry.isActive then
|
||||||
-- do something to remedy this
|
-- do something to remedy this
|
||||||
@@ -281,15 +296,15 @@ function scribe.playerLanded(playerName)
|
|||||||
-- see if last landing is at least xx seconds old
|
-- see if last landing is at least xx seconds old
|
||||||
local now = timer.getTime()
|
local now = timer.getTime()
|
||||||
delta = now - uEntry.lastLanding
|
delta = now - uEntry.lastLanding
|
||||||
if delta > scribe.landingCD or delta < 0 then
|
if delta > scribe.landingCD then -- or delta < 0 then
|
||||||
uEntry.landings = uEntry.landings + 1
|
uEntry.landings = uEntry.landings + 1
|
||||||
|
-- trigger.action.outText("+++scrb: added landing for " .. playerName .. ", delta is <" .. delta .. ">.", 30)
|
||||||
else
|
else
|
||||||
if scribe.verbose then
|
if scribe.verbose then
|
||||||
trigger.action.outText("+++scb: landing ignored: cooldown active", 30)
|
trigger.action.outText("+++scb: landing ignored: cooldown active", 30)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
uEntry.lastLanding = now
|
uEntry.lastLanding = now
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function scribe.playerDeparted(playerName)
|
function scribe.playerDeparted(playerName)
|
||||||
@@ -342,6 +357,7 @@ function scribe:onEvent(theEvent)
|
|||||||
if not theEvent.initiator then return end
|
if not theEvent.initiator then return end
|
||||||
local theUnit = theEvent.initiator
|
local theUnit = theEvent.initiator
|
||||||
if not theUnit then return end
|
if not theUnit then return end
|
||||||
|
if not theUnit.getName then return end -- DCS bug hardening
|
||||||
local uName = theUnit:getName()
|
local uName = theUnit:getName()
|
||||||
if scribe.playerUnits[uName] and scribe.verbose then
|
if scribe.playerUnits[uName] and scribe.verbose then
|
||||||
trigger.action.outText("+++scb: event <" .. theEvent.id .. " = " .. dcsCommon.event2text(theEvent.id) .. ">, concerns player unit named <" .. uName .. ">.", 30)
|
trigger.action.outText("+++scb: event <" .. theEvent.id .. " = " .. dcsCommon.event2text(theEvent.id) .. ">, concerns player unit named <" .. uName .. ">.", 30)
|
||||||
@@ -362,10 +378,13 @@ function scribe:onEvent(theEvent)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
-- when we get here we have a player event
|
-- when we get here we have a player event
|
||||||
|
-- trigger.action.outText("+++scrb: player event <" .. theEvent.id .. ">", 30)
|
||||||
-- players can only ever activate by birth event
|
-- players can only ever activate by birth event
|
||||||
if theEvent.id == 15 then -- birth
|
if theEvent.id == 15
|
||||||
scribe.playerBirthedIn(playerName, theUnit)
|
or theEvent == 20
|
||||||
|
then -- birth / enter unit
|
||||||
|
-- trigger.action.outText("+++scrb: player <" .. playerName .. "> entered unit.", 30)
|
||||||
|
scribe.playerBirthedIn(playerName, theUnit) -- reset timer for landings / take-off
|
||||||
scribe.playerUnits[uName] = playerName -- for crash helo detection
|
scribe.playerUnits[uName] = playerName -- for crash helo detection
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -384,12 +403,12 @@ function scribe:onEvent(theEvent)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if theEvent.id == 4 or -- landed
|
if theEvent.id == 4 or -- landed
|
||||||
theEvent.id == 56 then
|
theEvent.id == 55 then -- corrected to 55
|
||||||
scribe.playerLanded(playerName)
|
scribe.playerLanded(playerName)
|
||||||
end
|
end
|
||||||
|
|
||||||
if theEvent.id == 3 or -- take-off
|
if theEvent.id == 3 or -- take-off
|
||||||
theEvent.id == 55 then -- postponed take-off
|
theEvent.id == 54 then -- postponed take-off, corrected to 54
|
||||||
scribe.playerDeparted(playerName)
|
scribe.playerDeparted(playerName)
|
||||||
-- trigger.action.outText("departure detected", 30)
|
-- trigger.action.outText("departure detected", 30)
|
||||||
end
|
end
|
||||||
@@ -484,6 +503,31 @@ end
|
|||||||
--
|
--
|
||||||
-- start
|
-- start
|
||||||
--
|
--
|
||||||
|
function scribe.installDynamicPlayerMenu(theUnit)
|
||||||
|
local mainMenu = nil
|
||||||
|
if scribe.mainMenu then
|
||||||
|
mainMenu = radioMenu.getMainMenuFor(scribe.mainMenu) -- nilling both next params will return menus[0]
|
||||||
|
end
|
||||||
|
local unitInfo = {}
|
||||||
|
local theGroup = theUnit:getGroup()
|
||||||
|
local coa = theGroup:getCoalition()
|
||||||
|
local theType = theUnit:getTypeName()
|
||||||
|
local gName = theGroup:getName()
|
||||||
|
local uName = theUnit:getName()
|
||||||
|
if scribe.verbose then
|
||||||
|
trigger.action.outText("DYNAMIC unit <" .. uName .. ">: type <" .. theType .. "> coa <" .. coa .. ">, group <" .. gName .. ">", 30)
|
||||||
|
end
|
||||||
|
unitInfo.uName = uName -- needed for reverse-lookup
|
||||||
|
unitInfo.gName = gName -- also needed for reverse lookup
|
||||||
|
unitInfo.coa = coa
|
||||||
|
unitInfo.gID = theGroup:getID()
|
||||||
|
unitInfo.uID = theUnit:getID()
|
||||||
|
unitInfo.theType = theType
|
||||||
|
-- unitInfo.cat = cfxMX.groupTypeByName[gName]
|
||||||
|
unitInfo.root = missionCommands.addSubMenuForGroup(unitInfo.gID, scribe.uiMenu, mainMenu)
|
||||||
|
unitInfo.checkData = missionCommands.addCommandForGroup(unitInfo.gID, "Get Pilot's Statistics", unitInfo.root, scribe.redirectCheckData, unitInfo)
|
||||||
|
end
|
||||||
|
|
||||||
function scribe.startPlayerGUI()
|
function scribe.startPlayerGUI()
|
||||||
-- scan all mx players
|
-- scan all mx players
|
||||||
-- note: currently assumes single-player groups
|
-- note: currently assumes single-player groups
|
||||||
@@ -514,7 +558,7 @@ function scribe.startPlayerGUI()
|
|||||||
unitInfo.gID = gData.groupId
|
unitInfo.gID = gData.groupId
|
||||||
unitInfo.uID = uData.unitId
|
unitInfo.uID = uData.unitId
|
||||||
unitInfo.theType = theType
|
unitInfo.theType = theType
|
||||||
unitInfo.cat = cfxMX.groupTypeByName[gName]
|
-- unitInfo.cat = cfxMX.groupTypeByName[gName]
|
||||||
unitInfo.root = missionCommands.addSubMenuForGroup(unitInfo.gID, scribe.uiMenu, mainMenu)
|
unitInfo.root = missionCommands.addSubMenuForGroup(unitInfo.gID, scribe.uiMenu, mainMenu)
|
||||||
unitInfo.checkData = missionCommands.addCommandForGroup(unitInfo.gID, "Get Pilot's Statistics", unitInfo.root, scribe.redirectCheckData, unitInfo)
|
unitInfo.checkData = missionCommands.addCommandForGroup(unitInfo.gID, "Get Pilot's Statistics", unitInfo.root, scribe.redirectCheckData, unitInfo)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
cfxSpawnZones = {}
|
cfxSpawnZones = {}
|
||||||
cfxSpawnZones.version = "2.0.3"
|
cfxSpawnZones.version = "2.1.0"
|
||||||
cfxSpawnZones.requiredLibs = {
|
cfxSpawnZones.requiredLibs = {
|
||||||
"dcsCommon", -- common is of course needed for everything
|
"dcsCommon", -- common is of course needed for everything
|
||||||
-- pretty stupid to check for this since we
|
-- pretty stupid to check for this since we
|
||||||
@@ -29,6 +29,8 @@ cfxSpawnZones.spawnedGroups = {}
|
|||||||
2.0.1 - fix in verifySpawnOwnership() when not master zone found
|
2.0.1 - fix in verifySpawnOwnership() when not master zone found
|
||||||
2.0.2 - new "moveFormation" attribute
|
2.0.2 - new "moveFormation" attribute
|
||||||
2.0.3 - corrected type in spawnUnits? attribute
|
2.0.3 - corrected type in spawnUnits? attribute
|
||||||
|
2.1.0 - masterOwner update for dmlZones.
|
||||||
|
since spawners don't extend zones, this is still old-school
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
@@ -111,7 +113,17 @@ function cfxSpawnZones.createSpawner(inZone)
|
|||||||
theSpawner.country = inZone:getNumberFromZoneProperty("country", 0)
|
theSpawner.country = inZone:getNumberFromZoneProperty("country", 0)
|
||||||
if inZone:hasProperty("masterOwner") then
|
if inZone:hasProperty("masterOwner") then
|
||||||
theSpawner.masterZoneName = inZone:getStringFromZoneProperty("masterOwner", "")
|
theSpawner.masterZoneName = inZone:getStringFromZoneProperty("masterOwner", "")
|
||||||
if theSpawner.masterZoneName == "" then theSpawner.masterZoneName = nil end
|
if theSpawner.masterZoneName == "" then
|
||||||
|
theSpawner.masterZoneName = nil
|
||||||
|
else
|
||||||
|
local masterZone = cfxZones.getZoneByName(theSpawner.masterZoneName)
|
||||||
|
if not masterZone then
|
||||||
|
trigger.action.outText("spawner " .. theSpawner.name .. " DID NOT FIND MASTER ZONE <" .. theSpawner.masterZoneName .. ">", 30)
|
||||||
|
theSpawner.masterZoneName = nil
|
||||||
|
else
|
||||||
|
theSpawner.masterZone = masterZone
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
theSpawner.rawOwner = coalition.getCountryCoalition(theSpawner.country)
|
theSpawner.rawOwner = coalition.getCountryCoalition(theSpawner.country)
|
||||||
@@ -237,18 +249,18 @@ function cfxSpawnZones.verifySpawnOwnership(spawner)
|
|||||||
return true
|
return true
|
||||||
end -- no master owner, all ok
|
end -- no master owner, all ok
|
||||||
local myCoalition = spawner.rawOwner
|
local myCoalition = spawner.rawOwner
|
||||||
local masterZone = cfxZones.getZoneByName(spawner.masterZoneName)
|
-- local masterZone = cfxZones.getZoneByName(spawner.masterZoneName)
|
||||||
if not masterZone then
|
-- if not masterZone then
|
||||||
trigger.action.outText("spawner " .. spawner.name .. " DID NOT FIND MASTER ZONE <" .. spawner.masterZoneName .. ">", 30)
|
-- trigger.action.outText("spawner " .. spawner.name .. " DID NOT FIND MASTER ZONE <" .. spawner.masterZoneName .. ">", 30)
|
||||||
return false
|
-- return false
|
||||||
end
|
-- end
|
||||||
|
local masterZone = spawner.masterZone
|
||||||
if not masterZone.owner then
|
-- if not masterZone.owner then
|
||||||
--trigger.action.outText("spawner " .. spawner.name .. " - masterZone " .. masterZone.name .. " HAS NO OWNER????", 30)
|
--trigger.action.outText("spawner " .. spawner.name .. " - masterZone " .. masterZone.name .. " HAS NO OWNER????", 30)
|
||||||
return true
|
-- return true
|
||||||
end
|
-- end
|
||||||
|
|
||||||
if (myCoalition ~= masterZone.owner) then
|
if (myCoalition ~= masterZone:getCoalition()) then
|
||||||
-- can't spawn, surrounding area owned by enemy
|
-- can't spawn, surrounding area owned by enemy
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
stopGap = {}
|
stopGap = {}
|
||||||
stopGap.version = "1.1.1 STANDALONE"
|
stopGap.version = "1.2.0 STANDALONE"
|
||||||
stopGap.verbose = false
|
stopGap.verbose = false
|
||||||
stopGap.ssbEnabled = true
|
stopGap.ssbEnabled = true
|
||||||
stopGap.ignoreMe = "-sg"
|
stopGap.ignoreMe = "-sg"
|
||||||
@@ -8,6 +8,7 @@ stopGap.isMP = false
|
|||||||
stopGap.running = true
|
stopGap.running = true
|
||||||
stopGap.refreshInterval = -1 -- seconds to refresh all statics. -1 = never, 3600 = once every hour
|
stopGap.refreshInterval = -1 -- seconds to refresh all statics. -1 = never, 3600 = once every hour
|
||||||
stopGap.kickTheDead = true -- kick players to spectators on death to prevent re-entry issues
|
stopGap.kickTheDead = true -- kick players to spectators on death to prevent re-entry issues
|
||||||
|
stopGap.allNeutral = false -- make all stand-ins neutral
|
||||||
|
|
||||||
--[[--
|
--[[--
|
||||||
Written and (c) 2023 by Christian Franz
|
Written and (c) 2023 by Christian Franz
|
||||||
@@ -37,6 +38,8 @@ stopGap.kickTheDead = true -- kick players to spectators on death to prevent re-
|
|||||||
1.0.9 - optimization when turning on stopgap
|
1.0.9 - optimization when turning on stopgap
|
||||||
1.1.0 - kickTheDead option
|
1.1.0 - kickTheDead option
|
||||||
1.1.1 - filter "from runway" clients
|
1.1.1 - filter "from runway" clients
|
||||||
|
1.2.0 - compatibility with DCS dynamic spawns
|
||||||
|
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
@@ -115,8 +118,12 @@ function stopGap.staticMXFromUnitMX(theGroup, theUnit)
|
|||||||
theStatic.heading = theUnit.heading -- may need some attention
|
theStatic.heading = theUnit.heading -- may need some attention
|
||||||
theStatic.type = theUnit.type
|
theStatic.type = theUnit.type
|
||||||
theStatic.name = theUnit.name -- will magically be replaced with player unit
|
theStatic.name = theUnit.name -- will magically be replaced with player unit
|
||||||
|
theStatic.payload = theUnit.payload -- not supported (yet) by DCS
|
||||||
|
theStatic.onboard_num = theUnit.onboard_num -- not supported
|
||||||
theStatic.cty = cfxMX.countryByName[theGroup.name]
|
theStatic.cty = cfxMX.countryByName[theGroup.name]
|
||||||
|
if stopGap.allNeutral then
|
||||||
|
theStatic.cty = 82 -- UN Peache keepers, assign to neutral
|
||||||
|
end
|
||||||
return theStatic
|
return theStatic
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
stopGap = {}
|
stopGap = {}
|
||||||
stopGap.version = "1.1.2"
|
stopGap.version = "1.2.0"
|
||||||
stopGap.verbose = false
|
stopGap.verbose = false
|
||||||
stopGap.ssbEnabled = true
|
stopGap.ssbEnabled = true
|
||||||
stopGap.ignoreMe = "-sg"
|
stopGap.ignoreMe = "-sg"
|
||||||
@@ -52,6 +52,9 @@ stopGap.requiredLibs = {
|
|||||||
1.1.0 - kickTheDead option
|
1.1.0 - kickTheDead option
|
||||||
1.1.1 - filter "from runway" clients
|
1.1.1 - filter "from runway" clients
|
||||||
1.1.2 - allNeutral (DML only)
|
1.1.2 - allNeutral (DML only)
|
||||||
|
1.2.0 - DCS dynamic player spawn compatibility
|
||||||
|
stopGaps only works with MX data, so we are good, no changes
|
||||||
|
required
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
@@ -77,6 +80,8 @@ function stopGap.staticMXFromUnitMX(theGroup, theUnit)
|
|||||||
theStatic.type = theUnit.type
|
theStatic.type = theUnit.type
|
||||||
theStatic.name = theUnit.name -- will magically be replaced with player unit
|
theStatic.name = theUnit.name -- will magically be replaced with player unit
|
||||||
theStatic.cty = cfxMX.countryByName[theGroup.name]
|
theStatic.cty = cfxMX.countryByName[theGroup.name]
|
||||||
|
theStatic.payload = theUnit.payload -- not supported (yet) by DCS
|
||||||
|
theStatic.onboard_num = theUnit.onboard_num -- not supported
|
||||||
-- DML only: allNeutral
|
-- DML only: allNeutral
|
||||||
if stopGap.allNeutral then
|
if stopGap.allNeutral then
|
||||||
theStatic.cty = dcsCommon.getACountryForCoalition(0)
|
theStatic.cty = dcsCommon.getACountryForCoalition(0)
|
||||||
@@ -499,6 +504,4 @@ if not stopGap.start() then
|
|||||||
trigger.action.outText("+++ aborted stopGap v" .. stopGap.version .. " -- startup failed", 30)
|
trigger.action.outText("+++ aborted stopGap v" .. stopGap.version .. " -- startup failed", 30)
|
||||||
stopGap = nil
|
stopGap = nil
|
||||||
end
|
end
|
||||||
--[[-- TODO
|
|
||||||
- allNeutral: spawn all player aircraft as neutral
|
|
||||||
--]]--
|
|
||||||
|
|||||||
76
modules/twn.lua
Normal file
76
modules/twn.lua
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
twn = {}
|
||||||
|
twn.version = "1.0.1"
|
||||||
|
twn.verbose = false
|
||||||
|
|
||||||
|
--[[--
|
||||||
|
A DML unicorn - doesn't require any other scripts to function
|
||||||
|
(C) 2024 by Christian Franz
|
||||||
|
|
||||||
|
VERSION HISTORY
|
||||||
|
1.0.0 - Initial version
|
||||||
|
1.0.1 - Sinai // SinaiMap switcharoo
|
||||||
|
|
||||||
|
--]]--
|
||||||
|
|
||||||
|
function twn.closestTownTo(p) -- returns name, data, distance
|
||||||
|
if not towns then
|
||||||
|
trigger.action.outText("+++twn: Towns undefined", 30)
|
||||||
|
return nil, nil, nil
|
||||||
|
end
|
||||||
|
local closest = nil
|
||||||
|
local theName = nil
|
||||||
|
local smallest = math.huge
|
||||||
|
local x = p.x
|
||||||
|
local z = p.z
|
||||||
|
for name, entry in pairs(towns) do
|
||||||
|
local dx = x - entry.p.x
|
||||||
|
local dz = z - entry.p.z
|
||||||
|
local d = dx * dx + dz * dz -- no need to take square root
|
||||||
|
if d < smallest then
|
||||||
|
smallest = d
|
||||||
|
closest = entry
|
||||||
|
theName = name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return theName, closest, smallest^0.5 -- root it!
|
||||||
|
end
|
||||||
|
|
||||||
|
function twn.start()
|
||||||
|
-- get my theater
|
||||||
|
local theater = env.mission.theatre
|
||||||
|
-- map naming oddities
|
||||||
|
if theater == "SinaiMap" then theater = "Sinai" end
|
||||||
|
if twn.verbose then
|
||||||
|
trigger.action.outText("theater is <" .. theater .. ">", 30)
|
||||||
|
end
|
||||||
|
local path = "./Mods/terrains/" .. theater .. "/map/towns.lua"
|
||||||
|
-- assemble command
|
||||||
|
local command = 't = loadfile("' .. path .. '"); if t then t(); return net.lua2json(towns); else return nil end'
|
||||||
|
if twn.verbose then
|
||||||
|
trigger.action.outText("will run command <" .. command .. ">", 30)
|
||||||
|
end
|
||||||
|
local json = net.dostring_in("gui", command)
|
||||||
|
|
||||||
|
if json then
|
||||||
|
towns = {}
|
||||||
|
traw = net.json2lua(json)
|
||||||
|
local count = 0
|
||||||
|
for name, entry in pairs (traw) do
|
||||||
|
local p = coord.LLtoLO(entry.latitude, entry.longitude,0)
|
||||||
|
entry.p = p
|
||||||
|
towns[name] = entry
|
||||||
|
count = count + 1
|
||||||
|
end
|
||||||
|
if twn.verbose then
|
||||||
|
trigger.action.outText("+++twn: <" .. count .. "> town records processed", 30)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
trigger.action.outText("+++twn: no towns accessible.", 30)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
trigger.action.outText("twn (towns importer) v " .. twn.version .. " started.", 30)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
twn.start()
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
valet = {}
|
valet = {}
|
||||||
valet.version = "1.1.0"
|
valet.version = "1.1.1"
|
||||||
valet.verbose = false
|
valet.verbose = false
|
||||||
valet.requiredLibs = {
|
valet.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
@@ -14,6 +14,7 @@ valet.valets = {}
|
|||||||
1.0.2 - also scan birth events
|
1.0.2 - also scan birth events
|
||||||
1.0.3 - outSoundFile now working correctly
|
1.0.3 - outSoundFile now working correctly
|
||||||
1.1.0 - hysteresis is now time-based (10 seconds)
|
1.1.0 - hysteresis is now time-based (10 seconds)
|
||||||
|
1.1.1 - hardening against DCS July-11 update issues
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
function valet.addValet(theZone)
|
function valet.addValet(theZone)
|
||||||
@@ -361,6 +362,7 @@ function valet.checkPlayerSpawn(playerName, theUnit)
|
|||||||
-- see if player spawned in a valet zone
|
-- see if player spawned in a valet zone
|
||||||
if not playerName then return end
|
if not playerName then return end
|
||||||
if not theUnit then return end
|
if not theUnit then return end
|
||||||
|
if not theUnit.getName then return end
|
||||||
|
|
||||||
local pos = theUnit:getPoint()
|
local pos = theUnit:getPoint()
|
||||||
--trigger.action.outText("+++valet: spawn event", 30)
|
--trigger.action.outText("+++valet: spawn event", 30)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
williePete = {}
|
williePete = {}
|
||||||
williePete.version = "2.0.5"
|
williePete.version = "2.1.0"
|
||||||
williePete.ups = 10 -- we update at 10 fps, so accuracy of a
|
williePete.ups = 10 -- we update at 10 fps, so accuracy of a
|
||||||
-- missile moving at Mach 2 is within 33 meters,
|
-- missile moving at Mach 2 is within 33 meters,
|
||||||
-- with interpolation even at 3 meters
|
-- with interpolation even at 3 meters
|
||||||
@@ -22,6 +22,8 @@ williePete.requiredLibs = {
|
|||||||
2.0.3 - further hardened playerUpdate()
|
2.0.3 - further hardened playerUpdate()
|
||||||
2.0.4 - support for the Kiowa's Hydra M259
|
2.0.4 - support for the Kiowa's Hydra M259
|
||||||
2.0.5 - support for Mirage F1 WP that differ from Gazelle (?)
|
2.0.5 - support for Mirage F1 WP that differ from Gazelle (?)
|
||||||
|
2.0.6 - DCS Update 7-11 2024 weapon name bug
|
||||||
|
2.1.0 - DCS update 2.9.6 dynamic spawn support
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
williePete.willies = {}
|
williePete.willies = {}
|
||||||
@@ -134,6 +136,53 @@ end
|
|||||||
--
|
--
|
||||||
-- PLAYER MANAGEMENT
|
-- PLAYER MANAGEMENT
|
||||||
--
|
--
|
||||||
|
function williePete.latePlayerGUI(theUnit)
|
||||||
|
local unitInfo = {}
|
||||||
|
unitInfo.name = theUnit:getName() -- needed for reverse-lookup
|
||||||
|
local theGroup = theUnit:getGroup()
|
||||||
|
unitInfo.gName = theGroup:getName() -- gName -- also needed for reverse lookup
|
||||||
|
unitInfo.coa = theGroup:getCoalition() -- coa
|
||||||
|
unitInfo.gID = theGroup:getID() -- gData.groupId
|
||||||
|
unitInfo.uID = theUnit:getID() -- uData.unitId
|
||||||
|
unitInfo.theType = theUnit:getTypeName() -- theType
|
||||||
|
cat = theGroup:getCategory()
|
||||||
|
if cat == 0 then unitInfo.cat = "plane"
|
||||||
|
elseif cat == 1 then unitInfo.cat = "helicopter"
|
||||||
|
else
|
||||||
|
return -- whatever player is controlling, it's not for WP
|
||||||
|
end
|
||||||
|
|
||||||
|
williePete.doGUIforUnitInfo(unitInfo)
|
||||||
|
end
|
||||||
|
|
||||||
|
function williePete.doGUIforUnitInfo(unitInfo)
|
||||||
|
local pass = false
|
||||||
|
local uName = unitInfo.name
|
||||||
|
local gName = unitInfo.gName
|
||||||
|
|
||||||
|
for idx, aType in pairs(williePete.facTypes) do
|
||||||
|
if aType == "ALL" then pass = true end
|
||||||
|
if aType == "ANY" then pass = true end
|
||||||
|
if aType == theType then pass = true end
|
||||||
|
if dcsCommon.stringStartsWith(aType, "HEL") and unitInfo.cat == "helicopter" then pass = true end
|
||||||
|
if dcsCommon.stringStartsWith(aType, "PLAN") and unitInfo.cat == "plane" then pass = true end
|
||||||
|
end
|
||||||
|
|
||||||
|
if pass then -- we install a menu for this group
|
||||||
|
-- we may not want check in stuff, but it could be cool
|
||||||
|
if williePete.playerGUIs[gName] then
|
||||||
|
trigger.action.outText("+++WP: Warning: we already have WP menu for unit <" .. uName .. "> in group <" .. gName .. ">. Skipped.", 30)
|
||||||
|
elseif williePete.groupGUIs[gName] then
|
||||||
|
trigger.action.outText("+++WP: Warning: POSSIBLE MULTI-PLAYER UNIT GROUP DETECTED. We already have WP menu for Player Group <" .. gName .. ">. Skipped, only first unit supported. ", 30)
|
||||||
|
else
|
||||||
|
unitInfo.root = missionCommands.addSubMenuForGroup(unitInfo.gID, "FAC")
|
||||||
|
unitInfo.checkIn = missionCommands.addCommandForGroup(unitInfo.gID, "Check In", unitInfo.root, williePete.redirectCheckIn, unitInfo)
|
||||||
|
williePete.groupGUIs[gName] = unitInfo
|
||||||
|
williePete.playerGUIs[gName] = unitInfo
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function williePete.startPlayerGUI()
|
function williePete.startPlayerGUI()
|
||||||
-- scan all mx players
|
-- scan all mx players
|
||||||
-- note: currently assumes single-player groups
|
-- note: currently assumes single-player groups
|
||||||
@@ -159,7 +208,7 @@ function williePete.startPlayerGUI()
|
|||||||
unitInfo.theType = theType
|
unitInfo.theType = theType
|
||||||
unitInfo.cat = cfxMX.groupTypeByName[gName]
|
unitInfo.cat = cfxMX.groupTypeByName[gName]
|
||||||
-- now check type against willie pete config for allowable types
|
-- now check type against willie pete config for allowable types
|
||||||
local pass = false
|
--[[-- local pass = false
|
||||||
for idx, aType in pairs(williePete.facTypes) do
|
for idx, aType in pairs(williePete.facTypes) do
|
||||||
if aType == "ALL" then pass = true end
|
if aType == "ALL" then pass = true end
|
||||||
if aType == "ANY" then pass = true end
|
if aType == "ANY" then pass = true end
|
||||||
@@ -181,7 +230,8 @@ function williePete.startPlayerGUI()
|
|||||||
williePete.playerGUIs[gName] = unitInfo
|
williePete.playerGUIs[gName] = unitInfo
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
--]]--
|
||||||
|
williePete.doGUIforUnitInfo(unitInfo)
|
||||||
-- store it - WARNING: ASSUMES SINGLE-UNIT Player Groups
|
-- store it - WARNING: ASSUMES SINGLE-UNIT Player Groups
|
||||||
--williePete.playerGUIs[uName] = unitInfo
|
--williePete.playerGUIs[uName] = unitInfo
|
||||||
end
|
end
|
||||||
@@ -467,7 +517,7 @@ end
|
|||||||
|
|
||||||
function williePete.zedsDead(theObject)
|
function williePete.zedsDead(theObject)
|
||||||
if not theObject then return end
|
if not theObject then return end
|
||||||
|
if not theObject.getName then return end -- DCS July-11 oddity.
|
||||||
local theName = theObject:getName()
|
local theName = theObject:getName()
|
||||||
-- now check if it's a registered blasted object:getSampleRate()
|
-- now check if it's a registered blasted object:getSampleRate()
|
||||||
-- in multi-unit player groups, this can can lead to
|
-- in multi-unit player groups, this can can lead to
|
||||||
@@ -493,6 +543,14 @@ function williePete:onEvent(event)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- see if a dynamic spawn
|
||||||
|
if event.id == 15 then
|
||||||
|
local theUnit = event.initiator
|
||||||
|
if not cfxMX.isDynamicPlayer(theUnit) then return end
|
||||||
|
williePete.latePlayerGUI(theUnit)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
-- check if it's a dead event
|
-- check if it's a dead event
|
||||||
if event.id == 8 then
|
if event.id == 8 then
|
||||||
-- death event
|
-- death event
|
||||||
|
|||||||
Binary file not shown.
BIN
tutorial & demo missions/demo - escort a convoy to town.miz
Normal file
BIN
tutorial & demo missions/demo - escort a convoy to town.miz
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user