mirror of
https://github.com/weyne85/DML.git
synced 2025-10-29 16:57:49 +00:00
Version 2.4.3
New FireCtrl,, lua2json bug work-around
This commit is contained in:
parent
3a87f7b784
commit
dff5faa06e
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -1,5 +1,5 @@
|
|||||||
FARPZones = {}
|
FARPZones = {}
|
||||||
FARPZones.version = "2.2.0"
|
FARPZones.version = "2.3.0"
|
||||||
FARPZones.verbose = false
|
FARPZones.verbose = false
|
||||||
--[[--
|
--[[--
|
||||||
Version History
|
Version History
|
||||||
@ -26,6 +26,8 @@ FARPZones.verbose = false
|
|||||||
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
|
2.2.0 - changing a FARP's owner invokes SSBClient if it is loaded
|
||||||
|
2.3.0 - new attributes redCap!, blueCap! captured! and farpMethod
|
||||||
|
- send out signals
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
@ -209,6 +211,19 @@ function FARPZones.createFARPFromZone(aZone)
|
|||||||
theFarp.hidden = aZone:getBoolFromZoneProperty("hidden", false)
|
theFarp.hidden = aZone:getBoolFromZoneProperty("hidden", false)
|
||||||
theFarp.showTitle = aZone:getBoolFromZoneProperty("showTitle", true)
|
theFarp.showTitle = aZone:getBoolFromZoneProperty("showTitle", true)
|
||||||
theFarp.neutralProduction = aZone:getBoolFromZoneProperty("neutralProduction", false)
|
theFarp.neutralProduction = aZone:getBoolFromZoneProperty("neutralProduction", false)
|
||||||
|
|
||||||
|
-- capture signals
|
||||||
|
if aZone:hasProperty("redCap!") then
|
||||||
|
theFarp.redCap = aZone:getStringFromZoneProperty("redCap!", "none")
|
||||||
|
end
|
||||||
|
if aZone:hasProperty("blueCap!") then
|
||||||
|
theFarp.blueCap = aZone:getStringFromZoneProperty("blueCap!")
|
||||||
|
end
|
||||||
|
if aZone:hasProperty("captured!") then
|
||||||
|
theFarp.captured = aZone:getStringFromZoneProperty("captured!", "none")
|
||||||
|
end
|
||||||
|
theFarp.outMethod = aZone:getStringFromZoneProperty("farpMethod", "inc")
|
||||||
|
|
||||||
return theFarp
|
return theFarp
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -415,7 +430,7 @@ function FARPZones.somethingHappened(event)
|
|||||||
local ID = event.id
|
local ID = event.id
|
||||||
|
|
||||||
--trigger.action.outText("FZ: something happened", 30)
|
--trigger.action.outText("FZ: something happened", 30)
|
||||||
local aFarp = event.place
|
local aFarp = event.place -- place is type Airbase
|
||||||
local zonedFarp = FARPZones.getFARPZoneForFARP(aFarp)
|
local zonedFarp = FARPZones.getFARPZoneForFARP(aFarp)
|
||||||
|
|
||||||
if not zonedFarp then
|
if not zonedFarp then
|
||||||
@ -447,6 +462,18 @@ function FARPZones.somethingHappened(event)
|
|||||||
if newOwner == 2 then blueRed = "Blue" end
|
if newOwner == 2 then blueRed = "Blue" end
|
||||||
trigger.action.outText("FARP " .. zonedFarp.zone.name .. " captured by " .. blueRed .."!", 30)
|
trigger.action.outText("FARP " .. zonedFarp.zone.name .. " captured by " .. blueRed .."!", 30)
|
||||||
trigger.action.outSound("Quest Snare 3.wav")
|
trigger.action.outSound("Quest Snare 3.wav")
|
||||||
|
|
||||||
|
-- send out signals
|
||||||
|
if zonedFarp.redCap and newOwner == 1 then
|
||||||
|
cfxZones.pollFlag(zonedFarp.redCap, zonedFarp.outMethod, zonedFarp.zone)
|
||||||
|
end
|
||||||
|
if zonedFarp.blueCap and newOwner == 2 then
|
||||||
|
cfxZones.pollFlag(zonedFarp.blueCap, zonedFarp.outMethod, zonedFarp.zone)
|
||||||
|
end
|
||||||
|
if zonedFarp.captured then
|
||||||
|
cfxZones.pollFlag(zonedFarp.captured, zonedFarp.outMethod, zonedFarp.zone)
|
||||||
|
end
|
||||||
|
|
||||||
zonedFarp.owner = newOwner
|
zonedFarp.owner = newOwner
|
||||||
zonedFarp.zone.owner = newOwner
|
zonedFarp.zone.owner = newOwner
|
||||||
-- update color in map
|
-- update color in map
|
||||||
|
|||||||
@ -100,6 +100,7 @@ function LZ.createLZWithZone(theZone)
|
|||||||
trigger.action.outText("+++LZ: new LZ <".. theZone.name ..">", 30)
|
trigger.action.outText("+++LZ: new LZ <".. theZone.name ..">", 30)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
trigger.action.outText("zone <" .. theZone.name .. "> type of radius is <" .. type(theZone.radius) .. ">, val = " .. tonumber(theZone.radius), 30)
|
||||||
end
|
end
|
||||||
|
|
||||||
function LZ.nameMatchForArray(theName, theArray, wildcard)
|
function LZ.nameMatchForArray(theName, theArray, wildcard)
|
||||||
@ -229,6 +230,10 @@ function LZ:onEvent(event)
|
|||||||
|
|
||||||
for idx, aZone in pairs(LZ.LZs) do
|
for idx, aZone in pairs(LZ.LZs) do
|
||||||
-- see if inside the zone
|
-- see if inside the zone
|
||||||
|
if LZ.verbose then
|
||||||
|
trigger.action.outText("+++LZ: zone <" .. aZone.name .. "> for unit <" .. theUnit:getName() .. "> proccing", 30)
|
||||||
|
end
|
||||||
|
if true then return end
|
||||||
local inZone, percent, dist = cfxZones.pointInZone(p, aZone)
|
local inZone, percent, dist = cfxZones.pointInZone(p, aZone)
|
||||||
if inZone then
|
if inZone then
|
||||||
-- see if this unit interests us at all
|
-- see if this unit interests us at all
|
||||||
|
|||||||
@ -1,15 +1,75 @@
|
|||||||
WHpersistence = {}
|
WHpersistence = {}
|
||||||
WHpersistence.version = "1.0.0"
|
WHpersistence.version = "1.1.0"
|
||||||
WHpersistence.requiredLibs = {
|
WHpersistence.requiredLibs = {
|
||||||
"dcsCommon",
|
"dcsCommon",
|
||||||
"cfxZones",
|
"cfxZones",
|
||||||
"persistence",
|
"persistence",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
--[[--
|
||||||
|
Version History
|
||||||
|
1.0.0 - Initial version
|
||||||
|
1.1.0 - fixed an issue with net.lua2json
|
||||||
|
- enable dynamic spawns can ferry to other airfields
|
||||||
|
|
||||||
|
--]]--
|
||||||
|
|
||||||
--
|
--
|
||||||
-- load / save (game data)
|
-- Update & monitor
|
||||||
--
|
--
|
||||||
function WHpersistence.saveData()
|
WHpersistence.lastState = nil
|
||||||
local theData = {}
|
|
||||||
|
function WHpersistence.update()
|
||||||
|
timer.scheduleFunction(WHpersistence.update, nil, timer.getTime() + 1/WHpersistence.ups)
|
||||||
|
trigger.action.outText("+++WHp: start update", 30)
|
||||||
|
local newState = WHpersistence.getCurrentState()
|
||||||
|
-- now look for discrepacies to last state
|
||||||
|
local oldState = WHpersistence.lastState
|
||||||
|
-- iterate all bases
|
||||||
|
local hasChange = false
|
||||||
|
for name, inv in pairs(newState) do
|
||||||
|
local oldBaseInv = oldState[name]
|
||||||
|
-- WH has three entries: liquids, weapon and aircraft
|
||||||
|
-- compare iarcraft stats
|
||||||
|
if not inv.aircraft then
|
||||||
|
trigger.action.outText("+++WHp: NEW STATE: no aircraft data for <" .. name .. ">", 30)
|
||||||
|
else
|
||||||
|
for ref, num in pairs(inv.aircraft) do
|
||||||
|
oldNum = oldBaseInv.aircraft[ref]
|
||||||
|
trigger.action.outText("AF <" .. name .. ">, AC: <" .. ref .. "> -- " .. num .. ".", 30)
|
||||||
|
if oldNum ~= num then
|
||||||
|
if not oldNum then oldNum = "NIL" end
|
||||||
|
trigger.action.outText("+++WHp: WH <" .. name .. ">: aircraft type <" .. ref .. "> old num <" .. num .. ">, new <" .. oldNum .. ">", 30)
|
||||||
|
hasChange = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- reverse: has a plane left?
|
||||||
|
for name, inv in pairs(oldState) do
|
||||||
|
local oldBaseInv = newState[name]
|
||||||
|
-- WH has three entries: liquids, weapon and aircraft
|
||||||
|
-- compare iarcraft stats
|
||||||
|
if not inv.aircraft then
|
||||||
|
trigger.action.outText("+++WHp: OLD STATE: no aircraft data for <" .. name .. ">", 30)
|
||||||
|
else
|
||||||
|
for ref, num in pairs(inv.aircraft) do
|
||||||
|
oldNum = oldBaseInv.aircraft[ref]
|
||||||
|
if not oldNum then
|
||||||
|
trigger.action.outText("+++WHp: WH <" .. name .. ">: aircraft type <" .. ref .. "> REMOVED ENTIRELY", 30)
|
||||||
|
hasChange = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if hasChange then
|
||||||
|
trigger.action.outText("+++WHp: has change", 30)
|
||||||
|
end
|
||||||
|
WHpersistence.lastState = newState
|
||||||
|
end
|
||||||
|
|
||||||
|
function WHpersistence.getCurrentState()
|
||||||
local theWH = {}
|
local theWH = {}
|
||||||
-- generate all WH data from all my airfields
|
-- generate all WH data from all my airfields
|
||||||
local allMyBase = world:getAirbases()
|
local allMyBase = world:getAirbases()
|
||||||
@ -17,9 +77,33 @@ function WHpersistence.saveData()
|
|||||||
local name = theBase:getName()
|
local name = theBase:getName()
|
||||||
local WH = theBase:getWarehouse()
|
local WH = theBase:getWarehouse()
|
||||||
local inv = WH:getInventory()
|
local inv = WH:getInventory()
|
||||||
|
-- transcribe for bug in lua2json
|
||||||
|
local l0 = 0; local l1 = 0; local l2 = 0; local l3 = 0
|
||||||
|
if inv.liquids[0] then l0 = inv.liquids[0] end
|
||||||
|
if inv.liquids[1] then l1 = inv.liquids[1] end
|
||||||
|
if inv.liquids[2] then l2 = inv.liquids[2] end
|
||||||
|
if inv.liquids[3] then l3 = inv.liquids[3] end
|
||||||
|
bLiq = {["A"] = l0, -- lua2json can't handle a[0]
|
||||||
|
["B"] = l1,
|
||||||
|
["C"] = l2,
|
||||||
|
["D"] = l3,}
|
||||||
|
inv.bLiq = bLiq
|
||||||
theWH[name] = inv
|
theWH[name] = inv
|
||||||
end
|
end
|
||||||
theData.theWH = theWH
|
trigger.action.outText("+++WHp: read state", 30)
|
||||||
|
return theWH
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- load / save (game data)
|
||||||
|
--
|
||||||
|
function WHpersistence.saveData()
|
||||||
|
local theData = {}
|
||||||
|
local theWH = {}
|
||||||
|
theData.theWH = WHpersistence.getCurrentState() -- theWH
|
||||||
|
if WHpersistence.verbose then
|
||||||
|
trigger.action.outText("+++WHp: saving data", 30)
|
||||||
|
end
|
||||||
return theData, WHpersistence.sharedData -- second val currently nil
|
return theData, WHpersistence.sharedData -- second val currently nil
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -33,23 +117,36 @@ function WHpersistence.loadData()
|
|||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
if WHpersistence.verbose then
|
||||||
|
trigger.action.outText("+++WHp: restoring from file", 30)
|
||||||
|
end
|
||||||
|
local origState = WHpersistence.getCurrentState()
|
||||||
-- set up all warehouses from data loaded
|
-- set up all warehouses from data loaded
|
||||||
|
-- WARNING: if original was set, but saved emptpy,
|
||||||
|
-- we must now erase the original!
|
||||||
for name, inv in pairs(theData.theWH) do
|
for name, inv in pairs(theData.theWH) do
|
||||||
trigger.action.outText("+++restoring <" .. name .. ">", 30)
|
-- trigger.action.outText("+++restoring WH <" .. name .. ">", 30)
|
||||||
local theBase = Airbase.getByName(name)
|
local theBase = Airbase.getByName(name)
|
||||||
if theBase then
|
if theBase then
|
||||||
local theWH = theBase:getWarehouse()
|
local theWH = theBase:getWarehouse()
|
||||||
if theWH then
|
if theWH then
|
||||||
-- we go through weapon, liquids and aircraft
|
-- we go through weapon, liquids and aircraft
|
||||||
for idx, liq in pairs(inv.liquids) do
|
-- do liqids "manually"
|
||||||
theWH:setLiquidAmount(idx, liq)
|
if inv.bLiq then
|
||||||
trigger.action.outText(name .. ": Liq <" .. idx .. "> : <" .. liq .. ">", 30)
|
theWH:setLiquidAmount(0, inv.bLiq["A"])
|
||||||
|
theWH:setLiquidAmount(1, inv.bLiq["B"])
|
||||||
|
theWH:setLiquidAmount(2, inv.bLiq["C"])
|
||||||
|
theWH:setLiquidAmount(3, inv.bLiq["D"])
|
||||||
|
else
|
||||||
|
trigger.action.outText("+++WHPersistence: WARNING - legacy save data, will not set liquids for <" .. name .. ">", 30)
|
||||||
end
|
end
|
||||||
|
|
||||||
for ref, num in pairs(inv.weapon) do
|
for ref, num in pairs(inv.weapon) do
|
||||||
theWH:setItem(ref, num)
|
theWH:setItem(ref, num)
|
||||||
end
|
end
|
||||||
for ref, num in pairs(inv.aircraft) do
|
for ref, num in pairs(inv.aircraft) do
|
||||||
theWH:setItem(ref, num)
|
theWH:setItem(ref, num)
|
||||||
|
if WHpersistence.verbose then trigger.action.outText(name .. ": Setting # of Aircraft <" .. ref .. "> to <" .. num .. ">", 30) end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
trigger.action.outText(name .. ": no warehouse")
|
trigger.action.outText(name .. ": no warehouse")
|
||||||
@ -58,6 +155,28 @@ function WHpersistence.loadData()
|
|||||||
trigger.action.outText(name .. ": no airbase")
|
trigger.action.outText(name .. ": no airbase")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
local newInv = theData.theWH
|
||||||
|
for name, inv in pairs(origState) do
|
||||||
|
local bInv = newInv[name] -- from file!
|
||||||
|
local theBase = Airbase.getByName(name)
|
||||||
|
local theWH = theBase:getWarehouse() -- in case we neet to change
|
||||||
|
if theWH then
|
||||||
|
for ref, num in pairs(inv.weapon) do
|
||||||
|
if not bInv.weapon[ref] then
|
||||||
|
theWH:setItem(ref, 0)
|
||||||
|
if WHpersistence.verbose then trigger.action.outText(name .. ": Weapon <" .. ref .. "> : REMOVED", 30) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for ref, num in pairs(inv.aircraft) do
|
||||||
|
if not bInv.aircraft[ref] then
|
||||||
|
theWH:setItem(ref, 0)
|
||||||
|
if WHpersistence.verbose then trigger.action.outText(name .. ": Aircraft <" .. ref .. "> removed", 30) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
trigger.action.outText(name .. " can't access this airbase", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
--
|
--
|
||||||
-- config
|
-- config
|
||||||
@ -68,6 +187,8 @@ function WHpersistence.readConfigZone()
|
|||||||
theZone = cfxZones.createSimpleZone("WHpersistenceConfig")
|
theZone = cfxZones.createSimpleZone("WHpersistenceConfig")
|
||||||
end
|
end
|
||||||
WHpersistence.verbose = theZone.verbose
|
WHpersistence.verbose = theZone.verbose
|
||||||
|
WHpersistence.monitor = theZone:getBoolFromZoneProperty("monitor", false)
|
||||||
|
WHpersistence.ups = theZone:getNumberFromZoneProperty("ups", 0.1) -- every 10 seconds
|
||||||
end
|
end
|
||||||
--
|
--
|
||||||
-- GO
|
-- GO
|
||||||
@ -77,7 +198,7 @@ function WHpersistence.start()
|
|||||||
trigger.action.outText("cfx WHpersistence requires dcsCommon", 30)
|
trigger.action.outText("cfx WHpersistence requires dcsCommon", 30)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
if not dcsCommon.libCheck("cfx Raise Flag", WHpersistence.requiredLibs)then return false end
|
if not dcsCommon.libCheck("cfx WH Persistence", WHpersistence.requiredLibs)then return false end
|
||||||
WHpersistence.readConfigZone()
|
WHpersistence.readConfigZone()
|
||||||
if persistence then
|
if persistence then
|
||||||
callbacks = {}
|
callbacks = {}
|
||||||
@ -86,6 +207,11 @@ function WHpersistence.start()
|
|||||||
-- now load my data
|
-- now load my data
|
||||||
WHpersistence.loadData()
|
WHpersistence.loadData()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if WHpersistence.monitor then
|
||||||
|
WHpersistence.lastState = WHpersistence.getCurrentState()
|
||||||
|
timer.scheduleFunction(WHpersistence.update, nil, timer.getTime() + 1/WHpersistence.ups)
|
||||||
|
end
|
||||||
trigger.action.outText("cfx WHpersistence v" .. WHpersistence.version .. " started.", 30)
|
trigger.action.outText("cfx WHpersistence v" .. WHpersistence.version .. " started.", 30)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,11 +1,19 @@
|
|||||||
airtank = {}
|
airtank = {}
|
||||||
airtank.version = "1.0.0"
|
airtank.version = "1.0.1"
|
||||||
-- Module to extinguish fires controlled by the 'inferno' module.
|
-- Module to extinguish fires controlled by the 'inferno' module.
|
||||||
-- For 'airtank' fire extinguisher aircraft modules.
|
-- For 'airtank' fire extinguisher aircraft modules.
|
||||||
airtank.requiredLibs = {
|
airtank.requiredLibs = {
|
||||||
"dcsCommon",
|
"dcsCommon",
|
||||||
"cfxZones",
|
"cfxZones",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
--[[--
|
||||||
|
Version History
|
||||||
|
1.0.0 - Initial release
|
||||||
|
1.0.1 - removed attachTo: bug
|
||||||
|
|
||||||
|
--]]--
|
||||||
|
|
||||||
airtank.tanks = {} -- player data by GROUP name, will break with multi-unit groups
|
airtank.tanks = {} -- player data by GROUP name, will break with multi-unit groups
|
||||||
-- tank attributes
|
-- tank attributes
|
||||||
-- pumpArmed -- for hovering fills.
|
-- pumpArmed -- for hovering fills.
|
||||||
@ -192,9 +200,20 @@ function airtank.installMenusForUnit(theUnit) -- assumes all unfit types are wee
|
|||||||
local pRoot = airtank.roots[gName]
|
local pRoot = airtank.roots[gName]
|
||||||
if pRoot then
|
if pRoot then
|
||||||
missionCommands.removeItemForGroup(gID, pRoot)
|
missionCommands.removeItemForGroup(gID, pRoot)
|
||||||
|
pRoot = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- handle main menu
|
||||||
|
local mainMenu = nil
|
||||||
|
if airtank.mainMenu then
|
||||||
|
mainMenu = radioMenu.getMainMenuFor(airtank.mainMenu)
|
||||||
|
end
|
||||||
|
|
||||||
-- now add the airtank menu
|
-- now add the airtank menu
|
||||||
pRoot = missionCommands.addSubMenuForGroup(gID, airtank.menuName, airtank.mainMenu)
|
pRoot = missionCommands.addSubMenuForGroup(gID, airtank.menuName, mainMenu)
|
||||||
|
if airtank.verbose and airtank.mainMenu then
|
||||||
|
trigger.action.outText("+++airT: attaching airtank menu of group <" .. gName .. "> to existing root", 30)
|
||||||
|
end
|
||||||
airtank.roots[gName] = pRoot -- save for later
|
airtank.roots[gName] = pRoot -- save for later
|
||||||
local args = {gName, uName, gID, uType, pName}
|
local args = {gName, uName, gID, uType, pName}
|
||||||
-- menus:
|
-- menus:
|
||||||
@ -535,7 +554,10 @@ function airtank.readConfigZone()
|
|||||||
if radioMenu then -- requires optional radio menu to have loaded
|
if radioMenu then -- requires optional radio menu to have loaded
|
||||||
local mainMenu = radioMenu.mainMenus[attachTo]
|
local mainMenu = radioMenu.mainMenus[attachTo]
|
||||||
if mainMenu then
|
if mainMenu then
|
||||||
airtank.mainMenu = mainMenu
|
airtank.mainMenu = mainMenu -- the zone.
|
||||||
|
if airtank.verbose or theZone.verbose then
|
||||||
|
trigger.action.outText("+++airT: attached to menu from zone <" .. theZone.name .. ">", 30)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
trigger.action.outText("+++airtank: cannot find super menu <" .. attachTo .. ">", 30)
|
trigger.action.outText("+++airtank: cannot find super menu <" .. attachTo .. ">", 30)
|
||||||
end
|
end
|
||||||
|
|||||||
@ -22,7 +22,7 @@ VERSION HISTORY
|
|||||||
--]]--
|
--]]--
|
||||||
--
|
--
|
||||||
-- CURRENTLY REQUIRES SINGLE-UNIT PLAYER GROUPS
|
-- CURRENTLY REQUIRES SINGLE-UNIT PLAYER GROUPS
|
||||||
-- REQUIRES CLONEZONES MODULE TO BE RUNNING, BUT NOT TO BE LOADED ON START
|
-- REQUIRES CLONEZONES MODULE
|
||||||
--
|
--
|
||||||
camp.camps = {} -- all camps on the map
|
camp.camps = {} -- all camps on the map
|
||||||
camp.roots = {} -- all player group comms roots
|
camp.roots = {} -- all player group comms roots
|
||||||
@ -35,9 +35,7 @@ function camp.getMyCurrentCamp(theUnit) -- returns first hit player is in
|
|||||||
local coa = theUnit:getCoalition()
|
local coa = theUnit:getCoalition()
|
||||||
local p = theUnit:getPoint()
|
local p = theUnit:getPoint()
|
||||||
for idx, theCamp in pairs(camp.camps) do
|
for idx, theCamp in pairs(camp.camps) do
|
||||||
if theCamp.owner == coa and theCamp:pointInZone(p) then
|
if theCamp.owner == coa and theCamp:pointInZone(p) then return theCamp end
|
||||||
return theCamp
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
@ -45,18 +43,14 @@ end
|
|||||||
function camp.getCampsForCoa(coa)
|
function camp.getCampsForCoa(coa)
|
||||||
local myCamps = {}
|
local myCamps = {}
|
||||||
for idx, theCamp in pairs(camp.camps) do
|
for idx, theCamp in pairs(camp.camps) do
|
||||||
if theCamp.owner == coa then
|
if theCamp.owner == coa then table.insert(myCamps, theCamp) end
|
||||||
table.insert(myCamps, theCamp)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
return myCamps
|
return myCamps
|
||||||
end
|
end
|
||||||
|
|
||||||
function camp.createCampWithZone(theZone)
|
function camp.createCampWithZone(theZone)
|
||||||
-- look for all cloners inside my zone
|
-- look for all cloners inside my zone
|
||||||
if theZone.verbose or camp.verbose then
|
if theZone.verbose or camp.verbose then trigger.action.outText("+++camp: processing <" .. theZone.name .. ">, owner is <" .. theZone.owner .. ">", 30) end
|
||||||
trigger.action.outText("+++camp: processing <" .. theZone.name .. ">, owner is <" .. theZone.owner .. ">", 30)
|
|
||||||
end
|
|
||||||
|
|
||||||
local allZones = cfxZones.getAllZonesInsideZone(theZone)
|
local allZones = cfxZones.getAllZonesInsideZone(theZone)
|
||||||
local cloners = {}
|
local cloners = {}
|
||||||
@ -65,19 +59,12 @@ function camp.createCampWithZone(theZone)
|
|||||||
for idx, aZone in pairs(allZones) do
|
for idx, aZone in pairs(allZones) do
|
||||||
if aZone:hasProperty("nocamp") then
|
if aZone:hasProperty("nocamp") then
|
||||||
-- this zone cannot be part of a camp
|
-- this zone cannot be part of a camp
|
||||||
|
|
||||||
elseif aZone:hasProperty("cloner") then
|
elseif aZone:hasProperty("cloner") then
|
||||||
-- this is a clone zone and part of my camp
|
-- this is a clone zone and part of my camp
|
||||||
table.insert(cloners, aZone)
|
table.insert(cloners, aZone)
|
||||||
if not aZone:hasProperty("blueOnly") then
|
if not aZone:hasProperty("blueOnly") then table.insert(redCloners, aZone) end
|
||||||
table.insert(redCloners, aZone)
|
if not aZone:hasProperty("redOnly") then table.insert(blueCloners, aZone) end
|
||||||
end
|
if theZone.verbose or camp.verbose then trigger.action.outText("Cloner <" .. aZone.name .. "> is part of camp <" .. theZone.name .. ">", 30) end
|
||||||
if not aZone:hasProperty("redOnly") then
|
|
||||||
table.insert(blueCloners, aZone)
|
|
||||||
end
|
|
||||||
if theZone.verbose or camp.verbose then
|
|
||||||
trigger.action.outText("Cloner <" .. aZone.name .. "> is part of camp <" .. theZone.name .. ">", 30)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if #cloners < 1 then
|
if #cloners < 1 then
|
||||||
@ -96,9 +83,7 @@ function camp.createCampWithZone(theZone)
|
|||||||
theZone.upgradeCost = theZone:getNumberFromZoneProperty("upgradeCost", 3 * theZone.repairCost)
|
theZone.upgradeCost = theZone:getNumberFromZoneProperty("upgradeCost", 3 * theZone.repairCost)
|
||||||
if theZone:hasProperty("FARP") then
|
if theZone:hasProperty("FARP") then
|
||||||
theZone.isAlsoFARP = true
|
theZone.isAlsoFARP = true
|
||||||
if theZone.verbose or camp.verbose then
|
if theZone.verbose or camp.verbose then trigger.action.outText("+++camp: <" .. theZone.name .. "> has FARP attached", 30) end
|
||||||
trigger.action.outText("+++camp: <" .. theZone.name .. "> has FARP attached", 30)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -107,22 +92,18 @@ end
|
|||||||
--
|
--
|
||||||
function camp.update()
|
function camp.update()
|
||||||
-- call me in a second to poll triggers
|
-- call me in a second to poll triggers
|
||||||
timer.scheduleFunction(camp.update, {}, timer.getTime() + 1/camp.ups)
|
-- timer.scheduleFunction(camp.update, {}, timer.getTime() + 1/camp.ups)
|
||||||
end
|
end
|
||||||
|
|
||||||
function camp:onEvent(theEvent)
|
function camp:onEvent(theEvent)
|
||||||
if not theEvent then return end
|
if not theEvent then return end
|
||||||
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.getName then return end
|
|
||||||
-- if not theUnit.getPlayerName then return end
|
|
||||||
if not cfxMX.isDynamicPlayer(theUnit) then return end
|
if not cfxMX.isDynamicPlayer(theUnit) then return end
|
||||||
local id = theEvent.id
|
local id = theEvent.id
|
||||||
if id == 15 then -- birth
|
if id == 15 then -- birth
|
||||||
camp.lateProcessPlayer(theUnit)
|
camp.lateProcessPlayer(theUnit)
|
||||||
if camp.verbose then
|
if camp.verbose then trigger.action.outText("camp: late player processing for <" .. theUnit:getName() .. ">", 30) end
|
||||||
trigger.action.outText("camp: late player processing for <" .. theUnit:getName() .. ">", 30)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -153,12 +134,6 @@ function camp.processPlayers()
|
|||||||
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")
|
|
||||||
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"}) --]]--
|
|
||||||
camp.installComsFor(gID, gName)
|
camp.installComsFor(gID, gName)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -199,40 +174,22 @@ function camp.doTFunds(args)
|
|||||||
|
|
||||||
if theZone.repairable and theZone.upgradable then
|
if theZone.repairable and theZone.upgradable then
|
||||||
msg = msg .. " (§" .. theZone.repairCost .. "/§" .. theZone.upgradeCost .. ")"
|
msg = msg .. " (§" .. theZone.repairCost .. "/§" .. theZone.upgradeCost .. ")"
|
||||||
if camp.zoneNeedsRepairs(theZone, coa) then
|
if camp.zoneNeedsRepairs(theZone, coa) then msg = msg .. " requests repairs and" else msg = msg .. " is running and" end
|
||||||
msg = msg .. " requests repairs and"
|
|
||||||
else
|
|
||||||
msg = msg .. " is running and"
|
|
||||||
end
|
|
||||||
|
|
||||||
if camp.zoneNeedsUpgrades(theZone, coa) then
|
if camp.zoneNeedsUpgrades(theZone, coa) then msg = msg .. " can be upgraded" else msg = msg .. " is fully upgraded" end
|
||||||
msg = msg .. " can be upgraded"
|
|
||||||
else
|
|
||||||
msg = msg .. " is fully upgraded"
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif theZone.repairable then
|
elseif theZone.repairable then
|
||||||
if camp.zoneNeedsRepairs(theZone, coa) then
|
if camp.zoneNeedsRepairs(theZone, coa) then msg = msg .. " needs repairs (§" .. theZone.repairCost .. ")" else msg = msg .. " is fully operational" end
|
||||||
msg = msg .. " needs repairs (§" .. theZone.repairCost .. ")"
|
|
||||||
else
|
|
||||||
msg = msg .. " is fully operational"
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif theZone.upgradable then
|
elseif theZone.upgradable then
|
||||||
if camp.zoneNeedsUpgrades(theZone, coa) then
|
if camp.zoneNeedsUpgrades(theZone, coa) then msg = msg .. " can be upgraded (§" .. theZone.upgradeCost .. ")" else msg = msg .. " is fully upgraded" end
|
||||||
msg = msg .. " can be upgraded (§" .. theZone.upgradeCost .. ")"
|
|
||||||
else
|
|
||||||
msg = msg .. " is fully upgraded"
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
-- can be neither repaired nor upgraded
|
-- can be neither repaired nor upgraded
|
||||||
msg = msg .. " is owned"
|
msg = msg .. " is owned"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if income > 0 then
|
if income > 0 then msg = msg .. "\n\nTotal Income: §" .. income end
|
||||||
msg = msg .. "\n\nTotal Income: §" .. income
|
|
||||||
end
|
|
||||||
msg = msg .. "\n"
|
msg = msg .. "\n"
|
||||||
trigger.action.outTextForGroup(gID, msg, 30)
|
trigger.action.outTextForGroup(gID, msg, 30)
|
||||||
trigger.action.outSoundForGroup(gID, camp.actionSound)
|
trigger.action.outSoundForGroup(gID, camp.actionSound)
|
||||||
@ -262,13 +219,8 @@ function camp.doFunds(args)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if camp.zoneNeedsRepairs(theZone, coa) then
|
if camp.zoneNeedsRepairs(theZone, coa) then msg = msg .. "\nZone <" .. theZone.name .. "> needs repairs (§" .. theZone.repairCost .. " per repair)\n"
|
||||||
msg = msg .. "\nZone <" .. theZone.name .. "> needs repairs (§" .. theZone.repairCost .. " per repair)\n"
|
elseif theZone.repairable then msg = msg .. "\nZone <" .. theZone.name .. "> has no outstanding repairs.\n" end
|
||||||
elseif theZone.repairable then
|
|
||||||
msg = msg .. "\nZone <" .. theZone.name .. "> has no outstanding repairs.\n"
|
|
||||||
else
|
|
||||||
-- say nothing
|
|
||||||
end
|
|
||||||
if camp.zoneNeedsUpgrades(theZone, coa) then
|
if camp.zoneNeedsUpgrades(theZone, coa) then
|
||||||
msg = msg .. "\nZone <" .. theZone.name .. "> can be upgraded (§" .. theZone.upgradeCost .. " per upgrade)\n"
|
msg = msg .. "\nZone <" .. theZone.name .. "> can be upgraded (§" .. theZone.upgradeCost .. " per upgrade)\n"
|
||||||
elseif theZone.upgradable then
|
elseif theZone.upgradable then
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
cfxZones = {}
|
cfxZones = {}
|
||||||
cfxZones.version = "4.5.1"
|
cfxZones.version = "4.5.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
|
||||||
@ -40,7 +40,8 @@ cfxZones.version = "4.5.1"
|
|||||||
- rnd in bool can have = xxx param for percentage
|
- rnd in bool can have = xxx param for percentage
|
||||||
- getSmokeColorNumberFromZoneProperty()
|
- getSmokeColorNumberFromZoneProperty()
|
||||||
-4.5.1 - moved processSimpleZoneDynamics to common
|
-4.5.1 - moved processSimpleZoneDynamics to common
|
||||||
|
-4.5.2 - NEW getAllZoneProperties()
|
||||||
|
- guard agains DCS radius stored as sting (WTF, ED?)
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
--
|
--
|
||||||
@ -170,13 +171,13 @@ function cfxZones.readFromDCS(clearfirst)
|
|||||||
if zoneType == 0 then
|
if zoneType == 0 then
|
||||||
-- circular zone
|
-- circular zone
|
||||||
newZone.isCircle = true
|
newZone.isCircle = true
|
||||||
newZone.radius = dcsZone.radius
|
newZone.radius = tonumber(dcsZone.radius)
|
||||||
newZone.maxRadius = newZone.radius -- same for circular
|
newZone.maxRadius = newZone.radius -- same for circular
|
||||||
|
|
||||||
elseif zoneType == 2 then
|
elseif zoneType == 2 then
|
||||||
-- polyZone
|
-- polyZone
|
||||||
newZone.isPoly = true
|
newZone.isPoly = true
|
||||||
newZone.radius = dcsZone.radius -- radius is still written in DCS, may change later. The radius has no meaning and is the last radius written before zone changed to poly.
|
newZone.radius = tonumber(dcsZone.radius) -- radius is still written in DCS, may change later. The radius has no meaning and is the last radius written before zone changed to poly.
|
||||||
-- note that newZone.point is only inside the tone for
|
-- note that newZone.point is only inside the tone for
|
||||||
-- convex polys, and DML only correctly works with convex polys
|
-- convex polys, and DML only correctly works with convex polys
|
||||||
-- now transfer all point in the poly
|
-- now transfer all point in the poly
|
||||||
@ -460,24 +461,6 @@ function cfxZones.createRandomPointInPopulatedZone(theZone, radius, maxTries)
|
|||||||
return p, dx, dz
|
return p, dx, dz
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[--
|
|
||||||
function dmlZone:createRandomPointInPopulatedZone(radius, maxTries)
|
|
||||||
if not maxTries then maxTries = 20 end
|
|
||||||
local cnt = 0
|
|
||||||
local p, dx, dz
|
|
||||||
p, dx, dz = self:createRandomPointInZone() -- p is x, 0, z
|
|
||||||
repeat
|
|
||||||
local hits = cfxZones.objectsInRange(p, radius)
|
|
||||||
if hits < 1 then return p, dx, dz end
|
|
||||||
-- move to the right by radius
|
|
||||||
p.z = p.z + radius
|
|
||||||
dz = dz + radius
|
|
||||||
cnt = cnt + 1
|
|
||||||
trigger.action.outText("failed try " .. cnt, 30)
|
|
||||||
until cnt > maxTries
|
|
||||||
return p, dx, dz
|
|
||||||
end
|
|
||||||
--]]--
|
|
||||||
function cfxZones.objectHandler(theObject, theCollector) -- for world.search
|
function cfxZones.objectHandler(theObject, theCollector) -- for world.search
|
||||||
table.insert(theCollector, theObject)
|
table.insert(theCollector, theObject)
|
||||||
return true
|
return true
|
||||||
@ -1635,7 +1618,6 @@ function cfxZones.doPollFlag(theFlag, method, theZone) -- no OOP equivalent
|
|||||||
end
|
end
|
||||||
|
|
||||||
function cfxZones.pollFlag(theFlag, method, theZone)
|
function cfxZones.pollFlag(theFlag, method, theZone)
|
||||||
--trigger.action.outText("enter pollflag for flag <" .. theFlag .. "> of zone <" .. theZone.name .. ">", 30)
|
|
||||||
local allFlags = {}
|
local allFlags = {}
|
||||||
if dcsCommon.containsString(theFlag, ",") then
|
if dcsCommon.containsString(theFlag, ",") then
|
||||||
if cfxZones.verbose then
|
if cfxZones.verbose then
|
||||||
@ -2157,6 +2139,7 @@ function cfxZones.getAllZoneProperties(theZone, caseInsensitive, numbersOnly) --
|
|||||||
local theKey = "dummy"
|
local theKey = "dummy"
|
||||||
if string.len(theProp.key) > 0 then theKey = theProp.key end
|
if string.len(theProp.key) > 0 then theKey = theProp.key end
|
||||||
if caseInsensitive then theKey = theKey:upper() end
|
if caseInsensitive then theKey = theKey:upper() end
|
||||||
|
theKey = dcsCommon.trim(theKey)
|
||||||
local v = theProp.value
|
local v = theProp.value
|
||||||
if numbersOnly then
|
if numbersOnly then
|
||||||
v = tonumber(v)
|
v = tonumber(v)
|
||||||
@ -2171,6 +2154,29 @@ function dmlZone:getAllZoneProperties(caseInsensitive, numbersOnly)
|
|||||||
return cfxZones.getAllZoneProperties(self, caseInsensitive, numbersOnly)
|
return cfxZones.getAllZoneProperties(self, caseInsensitive, numbersOnly)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function cfxZones.getAllPropertyNames(theZone, caseInsensitive) -- return as array
|
||||||
|
if not caseInsensitive then caseInsensitive = false end
|
||||||
|
if not theZone then return {} end
|
||||||
|
local dcsProps = theZone.properties -- zone properties in dcs format
|
||||||
|
local props = {}
|
||||||
|
-- dcs has all properties as array with values .key and .value
|
||||||
|
-- so convert them into a dictionary
|
||||||
|
for i=1, #dcsProps do
|
||||||
|
local theProp = dcsProps[i]
|
||||||
|
local theKey = "dummy"
|
||||||
|
if string.len(theProp.key) > 0 then theKey = tostring(theProp.key) end
|
||||||
|
if caseInsensitive then theKey = theKey:upper() end
|
||||||
|
theKey = dcsCommon.trim(theKey)
|
||||||
|
table.insert(props, theKey)
|
||||||
|
end
|
||||||
|
return props
|
||||||
|
end
|
||||||
|
|
||||||
|
function dmlZone:getAllPropertyNames(caseInsensitive)
|
||||||
|
return cfxZones.getAllZoneProperties(self, caseInsensitive)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
function cfxZones.extractPropertyFromDCS(theKey, theProperties)
|
function cfxZones.extractPropertyFromDCS(theKey, theProperties)
|
||||||
-- trim
|
-- trim
|
||||||
theKey = dcsCommon.trim(theKey)
|
theKey = dcsCommon.trim(theKey)
|
||||||
@ -2815,26 +2821,6 @@ end
|
|||||||
|
|
||||||
function dmlZone:getSmokeColorStringFromZoneProperty(theProperty, default) -- smoke as 'red', 'green', or 1..5
|
function dmlZone:getSmokeColorStringFromZoneProperty(theProperty, default) -- smoke as 'red', 'green', or 1..5
|
||||||
return cfxZones.getSmokeColorStringFromZoneProperty(self, theProperty, default)
|
return cfxZones.getSmokeColorStringFromZoneProperty(self, theProperty, default)
|
||||||
--[[
|
|
||||||
if not default then default = "red" end
|
|
||||||
local s = self:getStringFromZoneProperty(theProperty, default)
|
|
||||||
s = s:lower()
|
|
||||||
s = dcsCommon.trim(s)
|
|
||||||
-- check numbers
|
|
||||||
if (s == "0") then return "green" end
|
|
||||||
if (s == "1") then return "red" end
|
|
||||||
if (s == "2") then return "white" end
|
|
||||||
if (s == "3") then return "orange" end
|
|
||||||
if (s == "4") then return "blue" end
|
|
||||||
|
|
||||||
if s == "green" or
|
|
||||||
s == "red" or
|
|
||||||
s == "white" or
|
|
||||||
s == "orange" or
|
|
||||||
s == "blue" then return s end
|
|
||||||
|
|
||||||
return default
|
|
||||||
--]]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function cfxZones.getSmokeColorNumberFromZoneProperty(theZone, theProperty, default) -- smoke as 'red', 'green', or 1..5
|
function cfxZones.getSmokeColorNumberFromZoneProperty(theZone, theProperty, default) -- smoke as 'red', 'green', or 1..5
|
||||||
@ -2892,28 +2878,7 @@ end
|
|||||||
|
|
||||||
function dmlZone:getFlareColorStringFromZoneProperty(theProperty, default) -- smoke as 'red', 'green', or 1..5
|
function dmlZone:getFlareColorStringFromZoneProperty(theProperty, default) -- smoke as 'red', 'green', or 1..5
|
||||||
return cfxZones.getFlareColorStringFromZoneProperty(self, theProperty, default)
|
return cfxZones.getFlareColorStringFromZoneProperty(self, theProperty, default)
|
||||||
--[[--
|
|
||||||
if not default then default = "red" end
|
|
||||||
local s = self:getStringFromZoneProperty(theProperty, default)
|
|
||||||
s = s:lower()
|
|
||||||
s = dcsCommon.trim(s)
|
|
||||||
-- check numbers
|
|
||||||
if (s == "rnd") then return "random" end
|
|
||||||
if (s == "0") then return "green" end
|
|
||||||
if (s == "1") then return "red" end
|
|
||||||
if (s == "2") then return "white" end
|
|
||||||
if (s == "3") then return "yellow" end
|
|
||||||
if (s == "-1") then return "random" end
|
|
||||||
|
|
||||||
if s == "green" or
|
|
||||||
s == "red" or
|
|
||||||
s == "white" or
|
|
||||||
s == "yellow" or
|
|
||||||
s == "random" then
|
|
||||||
return s end
|
|
||||||
|
|
||||||
return default
|
|
||||||
--]]--
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
@ -2937,36 +2902,7 @@ end
|
|||||||
function cfxZones.processSimpleZoneDynamics(inMsg, theZone, timeFormat, imperialUnits)
|
function cfxZones.processSimpleZoneDynamics(inMsg, theZone, timeFormat, imperialUnits)
|
||||||
local p = theZone:getPoint()
|
local p = theZone:getPoint()
|
||||||
return dcsCommon.processTimeLocWildCards(inMsg, p, timeFormat, imperialUnits)
|
return dcsCommon.processTimeLocWildCards(inMsg, p, timeFormat, imperialUnits)
|
||||||
--[[--
|
|
||||||
if not inMsg then return "<nil inMsg>" end
|
|
||||||
-- replace <t> with current mission time HMS
|
|
||||||
local absSecs = timer.getAbsTime()-- + env.mission.start_time
|
|
||||||
while absSecs > 86400 do
|
|
||||||
absSecs = absSecs - 86400 -- subtract out all days
|
|
||||||
end
|
|
||||||
if not timeFormat then timeFormat = "<:h>:<:m>:<:s>" end
|
|
||||||
local timeString = dcsCommon.processHMS(timeFormat, absSecs)
|
|
||||||
local outMsg = inMsg:gsub("<t>", timeString)
|
|
||||||
|
|
||||||
-- replace <lat> with lat of zone point and <lon> with lon of zone point
|
|
||||||
-- and <mgrs> with mgrs coords of zone point
|
|
||||||
local currPoint = cfxZones.getPoint(theZone)
|
|
||||||
local lat, lon = coord.LOtoLL(currPoint)
|
|
||||||
lat, lon = dcsCommon.latLon2Text(lat, lon)
|
|
||||||
local alt = land.getHeight({x = currPoint.x, y = currPoint.z})
|
|
||||||
if imperialUnits then
|
|
||||||
alt = math.floor(alt * 3.28084) -- feet
|
|
||||||
else
|
|
||||||
alt = math.floor(alt) -- meters
|
|
||||||
end
|
|
||||||
outMsg = outMsg:gsub("<lat>", lat)
|
|
||||||
outMsg = outMsg:gsub("<lon>", lon)
|
|
||||||
outMsg = outMsg:gsub("<ele>", alt)
|
|
||||||
local grid = coord.LLtoMGRS(coord.LOtoLL(currPoint))
|
|
||||||
local mgrs = grid.UTMZone .. ' ' .. grid.MGRSDigraph .. ' ' .. grid.Easting .. ' ' .. grid.Northing
|
|
||||||
outMsg = outMsg:gsub("<mgrs>", mgrs)
|
|
||||||
return outMsg
|
|
||||||
--]]--
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- process <v: flag>, <rsp: flag> <rrnd>
|
-- process <v: flag>, <rsp: flag> <rrnd>
|
||||||
|
|||||||
409
modules/fireCtrl.lua
Normal file
409
modules/fireCtrl.lua
Normal file
@ -0,0 +1,409 @@
|
|||||||
|
fireCtrl = {}
|
||||||
|
fireCtrl.version = "1.1.0"
|
||||||
|
fireCtrl.requiredLibs = {
|
||||||
|
"dcsCommon",
|
||||||
|
"cfxZones",
|
||||||
|
"airtank",
|
||||||
|
"inferno",
|
||||||
|
}
|
||||||
|
|
||||||
|
fireCtrl.heroes = {} -- dict by pname: score
|
||||||
|
fireCtrl.checkins = {} -- dict by pname: checked in if we persist
|
||||||
|
fireCtrl.roots = {}
|
||||||
|
--[[--
|
||||||
|
Version History
|
||||||
|
1.0.0 - Initial version (unreleased)
|
||||||
|
1.1.0 - Added attachTo:
|
||||||
|
- Added checkIn
|
||||||
|
- center name
|
||||||
|
- newFire sound
|
||||||
|
- bail out if not enough fires
|
||||||
|
- re-liting with sparks
|
||||||
|
- sparks attribute
|
||||||
|
- various sound attributes, default to action sound
|
||||||
|
- notifications attribute
|
||||||
|
- cleanup
|
||||||
|
- UI attribute
|
||||||
|
--]]--
|
||||||
|
|
||||||
|
function fireCtrl.checkinPlayer(pName, gName, uName, uType)
|
||||||
|
local theGroup = Group.getByName(gName)
|
||||||
|
if not theGroup then return end
|
||||||
|
gID = theGroup:getID()
|
||||||
|
local msg = ""
|
||||||
|
if not fireCtrl.checkins[pName] then
|
||||||
|
msg = "\nWelcome "
|
||||||
|
if fireCtrl.heroes[pName] then
|
||||||
|
msg = msg .. "back "
|
||||||
|
else
|
||||||
|
fireCtrl.heroes[pName] = 0
|
||||||
|
end
|
||||||
|
msg = msg .. "to " .. dcsCommon.getMapName() .. ", " .. pName .. ", your " .. uType .. " is ready.\nGood luck and godspeed!\n"
|
||||||
|
fireCtrl.checkins[pName] = timer.getTime()
|
||||||
|
else
|
||||||
|
msg = "\n" .. pName .. ", your " .. uType .. " is ready.\n"
|
||||||
|
end
|
||||||
|
if fireCtrl.checkIn then
|
||||||
|
trigger.action.outTextForGroup(gID, msg, 30, true)
|
||||||
|
trigger.action.outSoundForGroup(gID, fireCtrl.actionSound)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function fireCtrl:onEvent(theEvent)
|
||||||
|
-- catch birth events of helos
|
||||||
|
if not theEvent then return end
|
||||||
|
local theUnit = theEvent.initiator
|
||||||
|
if not theUnit then return end
|
||||||
|
if not theUnit.getPlayerName then return end
|
||||||
|
local pName = theUnit:getPlayerName()
|
||||||
|
if not pName then return end
|
||||||
|
-- we have a player unit
|
||||||
|
if not dcsCommon.unitIsOfLegalType(theUnit, airtank.types) then
|
||||||
|
if fireCtrl.verbose then
|
||||||
|
trigger.action.outText("fireCtrl: unit <" .. theUnit:getName() .. ">, type <" .. theUnit:getTypeName() .. "> not an airtank.", 30)
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local uName = theUnit:getName()
|
||||||
|
local uType = theUnit:getTypeName()
|
||||||
|
local theGroup = theUnit:getGroup()
|
||||||
|
local gName = theGroup:getName()
|
||||||
|
if theEvent.id == 15 then -- birth
|
||||||
|
-- make sure this aircraft is legit
|
||||||
|
fireCtrl.installMenusForUnit(theUnit)
|
||||||
|
if fireCtrl.verbose then
|
||||||
|
trigger.action.outText("+++fCtl: new player airtank <" .. uName .. "> type <" .. uType .. "> for <" .. pName .. ">", 30)
|
||||||
|
end
|
||||||
|
fireCtrl.checkinPlayer(pName, gName, uName, uType)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function fireCtrl.installMenusForUnit(theUnit) -- assumes all unit types are weeded out
|
||||||
|
if not fireCtrl.UI then return end
|
||||||
|
|
||||||
|
-- if already exists, remove old
|
||||||
|
if not theUnit then return end
|
||||||
|
if not Unit.isExist(theUnit) then return end
|
||||||
|
local theGroup = theUnit:getGroup()
|
||||||
|
local uName = theUnit:getName()
|
||||||
|
local uType = theUnit:getTypeName()
|
||||||
|
local pName = theUnit:getPlayerName()
|
||||||
|
local gName = theGroup:getName()
|
||||||
|
local gID = theGroup:getID()
|
||||||
|
local pRoot = fireCtrl.roots[gName]
|
||||||
|
if pRoot then
|
||||||
|
missionCommands.removeItemForGroup(gID, pRoot)
|
||||||
|
pRoot = nil
|
||||||
|
end
|
||||||
|
-- handle main menu
|
||||||
|
local mainMenu = nil
|
||||||
|
if fireCtrl.mainMenu then
|
||||||
|
mainMenu = radioMenu.getMainMenuFor(fireCtrl.mainMenu)
|
||||||
|
end
|
||||||
|
-- now add fireCtrl menu
|
||||||
|
pRoot = missionCommands.addSubMenuForGroup(gID, fireCtrl.menuName, mainMenu)
|
||||||
|
fireCtrl.roots[gName] = pRoot -- save for later
|
||||||
|
local args = {gName, uName, gID, uType, pName}
|
||||||
|
-- menus:
|
||||||
|
-- report - all current open fires
|
||||||
|
-- action - scoreboard
|
||||||
|
-- arm release -- get ready to drop. auto-release when alt is below 30m
|
||||||
|
local m1 = missionCommands.addCommandForGroup(gID , "Fire Report" , pRoot, fireCtrl.redirectStatus, args)
|
||||||
|
local mx2 = missionCommands.addCommandForGroup(gID , "Action Report" , pRoot, fireCtrl.redirectAction, args)
|
||||||
|
end
|
||||||
|
--
|
||||||
|
-- comms
|
||||||
|
--
|
||||||
|
function fireCtrl.redirectStatus (args)
|
||||||
|
timer.scheduleFunction(fireCtrl.doStatus, args, timer.getTime() + 0.1)
|
||||||
|
end
|
||||||
|
|
||||||
|
function fireCtrl.redirectAction (args)
|
||||||
|
timer.scheduleFunction(fireCtrl.doAction, args, timer.getTime() + 0.1)
|
||||||
|
end
|
||||||
|
|
||||||
|
function fireCtrl.doStatus(args)
|
||||||
|
-- trigger.action.outText("status call", 30)
|
||||||
|
local gName = args[1]
|
||||||
|
local uName = args[2]
|
||||||
|
local gID = args[3]
|
||||||
|
local uType = args[4]
|
||||||
|
local pName = args[5]
|
||||||
|
local theUnit = Unit.getByName(uName)
|
||||||
|
if not theUnit then return end
|
||||||
|
local up = theUnit:getPoint()
|
||||||
|
up.y = 0
|
||||||
|
local msg = "\nFire emergencies requesting aerial support:\n"
|
||||||
|
local count = 0
|
||||||
|
for name, theZone in pairs(inferno.zones) do
|
||||||
|
if theZone.burning then
|
||||||
|
local p = theZone:getPoint()
|
||||||
|
local level = theZone.maxSpread
|
||||||
|
if level > 1000 or level < 0 then level = 1 end
|
||||||
|
count = count + 1
|
||||||
|
if count < 10 then msg = msg .. " " end
|
||||||
|
msg = msg .. count .. ". Type " .. level .. " "
|
||||||
|
if twn and towns then
|
||||||
|
local name, data, dist = twn.closestTownTo(p)
|
||||||
|
local mdist= dist * 0.539957
|
||||||
|
dist = math.floor(dist/100) / 10
|
||||||
|
mdist = math.floor(mdist/100) / 10
|
||||||
|
local bear = dcsCommon.compassPositionOfARelativeToB(p, data.p)
|
||||||
|
msg = msg .. dist .. "km/" .. mdist .."nm " .. bear .. " of " .. name
|
||||||
|
success = true
|
||||||
|
else
|
||||||
|
msg = msg .. "***TWN ERR***"
|
||||||
|
end
|
||||||
|
local b = dcsCommon.bearingInDegreesFromAtoB(up, p)
|
||||||
|
local d = dcsCommon.distFlat(up, p) * 0.000539957
|
||||||
|
d = math.floor(d * 10) / 10
|
||||||
|
msg = msg .. ", bearing " .. b .. ", " .. d .. "nm\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if count < 1 then
|
||||||
|
msg = msg .. "\n All is well, blue skies, no fires.\n"
|
||||||
|
end
|
||||||
|
msg = msg .. "\n"
|
||||||
|
trigger.action.outTextForGroup(gID, msg, 30)
|
||||||
|
trigger.action.outSoundForGroup(gID, fireCtrl.listSound)
|
||||||
|
end
|
||||||
|
|
||||||
|
function fireCtrl.doAction(args)
|
||||||
|
-- sort heroes by their points, and rank them
|
||||||
|
local h = {}
|
||||||
|
for name, num in pairs(fireCtrl.heroes) do
|
||||||
|
local ele = {}
|
||||||
|
ele.name = name
|
||||||
|
ele.num = num
|
||||||
|
ele.rank = fireCtrl.num2rank(num)
|
||||||
|
table.insert(h, ele)
|
||||||
|
end
|
||||||
|
-- table.sort(table, function (e1, e2) return e1.atr < e2.atr end )
|
||||||
|
table.sort(h, function (e1, e2) return e1.num > e2.num end)
|
||||||
|
-- now create the top twenty
|
||||||
|
local msg = "\nThe Book Of Embers recognizes:\n"
|
||||||
|
local count = 0
|
||||||
|
for idx, ele in pairs(h) do
|
||||||
|
count = count + 1
|
||||||
|
if count < 21 then
|
||||||
|
if count < 10 then msg = msg .. " " end
|
||||||
|
msg = msg .. count .. ". " .. ele.rank .. " " .. ele.name .. " (" .. ele.num .. ")\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if count < 1 then
|
||||||
|
msg = msg .. "\n *** The Book Is Empty ***\n"
|
||||||
|
end
|
||||||
|
trigger.action.outTextForGroup(gID, msg, 30)
|
||||||
|
trigger.action.outSoundForGroup(gID, fireCtrl.scoreSound)
|
||||||
|
end
|
||||||
|
|
||||||
|
function fireCtrl.num2rank(num)
|
||||||
|
if num < 10 then return "Probie" end
|
||||||
|
if num < 25 then return "Firefighter" end
|
||||||
|
if num < 50 then return "Elltee" end
|
||||||
|
if num < 100 then return "Chief" end
|
||||||
|
if num < 200 then return "Local Hero" end
|
||||||
|
if num < 1000 then return "Fire Hero of " .. dcsCommon.getMapName() .. "(" .. math.floor (num/200) .. ")" end
|
||||||
|
return "Avatar of Hephaestus"
|
||||||
|
end
|
||||||
|
--
|
||||||
|
-- update
|
||||||
|
--
|
||||||
|
function fireCtrl.pickUnlit()
|
||||||
|
local linearTable = dcsCommon.enumerateTable(inferno.zones)
|
||||||
|
local theZone
|
||||||
|
local tries = 0
|
||||||
|
repeat
|
||||||
|
theZone = dcsCommon.pickRandom(linearTable)
|
||||||
|
tries = tries + 1
|
||||||
|
until (not theZone.burning) or (tries > 100)
|
||||||
|
if tries > 100 then
|
||||||
|
trigger.action.outText("fireCtrl: no unlit zones available", 30)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return theZone
|
||||||
|
end
|
||||||
|
|
||||||
|
function fireCtrl.startFire()
|
||||||
|
local theZone = fireCtrl.pickUnlit()
|
||||||
|
if not theZone then return end
|
||||||
|
inferno.ignite(theZone)
|
||||||
|
if fireCtrl.verbose or theZone.verbose then
|
||||||
|
trigger.action.outText("+++fCtl: started fire in <" .. theZone.name .. ">", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function fireCtrl.update()
|
||||||
|
timer.scheduleFunction(fireCtrl.update, {}, timer.getTime() + 1/fireCtrl.ups)
|
||||||
|
-- check the numbers of fires burning
|
||||||
|
local f = 0
|
||||||
|
local cells = 0
|
||||||
|
for idx, theZone in pairs(inferno.zones) do
|
||||||
|
if theZone.burning then f = f + 1 end
|
||||||
|
if theZone.maxSpread > 1000 then
|
||||||
|
cells = cells + #theZone.goodCells
|
||||||
|
else
|
||||||
|
cells = cells + theZone.maxSpread + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if f < fireCtrl.minFires then -- start 3!!!! fires
|
||||||
|
fireCtrl.startFire() -- start at least one fire
|
||||||
|
if fireCtrl.sparks > 1 then
|
||||||
|
for i=1, fireCtrl.sparks-1 do
|
||||||
|
if math.random(100) > 50 then fireCtrl.startFire() end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if fireCtrl.notifications then
|
||||||
|
trigger.action.outText("\nAll stations, " .. fireCtrl.centerName .. " One. Local fire dispatch have reported a large fire cell and are requesting aerial support. Please check in with " .. fireCtrl.centerName .. " for details.\n", 30)
|
||||||
|
trigger.action.outSound(fireCtrl.newFire)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
--
|
||||||
|
-- callbacks
|
||||||
|
--
|
||||||
|
function fireCtrl.extCB(theZone)
|
||||||
|
local p = theZone:getPoint()
|
||||||
|
local name = "<" .. theZone.name .. ">"
|
||||||
|
if twn and towns then
|
||||||
|
name = twn.closestTownTo(p)
|
||||||
|
end
|
||||||
|
local msg = "\nInferno at " .. name .. " has been extinguished. Our thanks go to\n"
|
||||||
|
local heroes = theZone.heroes
|
||||||
|
local hasOne = false
|
||||||
|
if heroes then
|
||||||
|
local awarded = theZone.maxSpread
|
||||||
|
if awarded < 1 or awarded > 9999 then
|
||||||
|
awarded = 1
|
||||||
|
end
|
||||||
|
for name, count in pairs(heroes) do
|
||||||
|
hasOne = true
|
||||||
|
msg = msg .. " - " .. name .. " (" .. count .. " successful drops)\n"
|
||||||
|
-- award everyone points based on maxSprad
|
||||||
|
if not fireCtrl.heroes[name] then
|
||||||
|
fireCtrl.heroes[name] = awarded
|
||||||
|
else
|
||||||
|
fireCtrl.heroes[name] = fireCtrl.heroes[name] + awarded
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not hasOne then msg = msg .. "\n(no one)\n" end
|
||||||
|
trigger.action.outText(msg, 30)
|
||||||
|
trigger.action.outSound(fireCtrl.actionSound)
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- load / save (game data)
|
||||||
|
--
|
||||||
|
function fireCtrl.saveData()
|
||||||
|
local theData = {}
|
||||||
|
-- save current heroes. simple clone
|
||||||
|
local theHeroes = dcsCommon.clone(fireCtrl.heroes)
|
||||||
|
theData.theHeroes = theHeroes
|
||||||
|
return theData, fireCtrl.sharedData -- second val only if shared
|
||||||
|
end
|
||||||
|
|
||||||
|
function fireCtrl.loadData()
|
||||||
|
if not persistence then return end
|
||||||
|
local theData = persistence.getSavedDataForModule("fireCtrl", fireCtrl.sharedData)
|
||||||
|
if not theData then
|
||||||
|
if fireCtrl.verbose then
|
||||||
|
trigger.action.outText("+++fireCtrl: no save date received, skipping.", 30)
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local theHeroes = theData.theHeroes
|
||||||
|
fireCtrl.heroes = theHeroes
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- start and config
|
||||||
|
--
|
||||||
|
function fireCtrl.readConfigZone()
|
||||||
|
fireCtrl.name = "fireCtrlConfig" -- make compatible with dml zones
|
||||||
|
local theZone = cfxZones.getZoneByName("fireCtrlConfig")
|
||||||
|
if not theZone then
|
||||||
|
theZone = cfxZones.createSimpleZone("fireCtrlConfig")
|
||||||
|
end
|
||||||
|
fireCtrl.verbose = theZone.verbose
|
||||||
|
fireCtrl.ups = theZone:getNumberFromZoneProperty("ups", 1/20)
|
||||||
|
fireCtrl.actionSound = theZone:getStringFromZoneProperty("actionSound", "none")
|
||||||
|
fireCtrl.listSound = theZone:getStringFromZoneProperty("listSound", fireCtrl.actionSound)
|
||||||
|
fireCtrl.scoreSound = theZone:getStringFromZoneProperty("scoreSound", fireCtrl.listSound)
|
||||||
|
fireCtrl.centerName = theZone:getStringFromZoneProperty("centerName", "Highperch")
|
||||||
|
fireCtrl.newFire = theZone:getStringFromZoneProperty("newFire", "firefight new fire alice.ogg")
|
||||||
|
fireCtrl.menuName = theZone:getStringFromZoneProperty("menuName", "Contact " .. fireCtrl.centerName)
|
||||||
|
fireCtrl.minFires = theZone:getNumberFromZoneProperty("minFires", 3)
|
||||||
|
fireCtrl.sparks = theZone:getNumberFromZoneProperty("sparks", 3)
|
||||||
|
fireCtrl.notifications = theZone:getBoolFromZoneProperty("notifications", true)
|
||||||
|
fireCtrl.UI = theZone:getBoolFromZoneProperty("UI", true)
|
||||||
|
|
||||||
|
if theZone:hasProperty("attachTo:") then
|
||||||
|
local attachTo = theZone:getStringFromZoneProperty("attachTo:", "<none>")
|
||||||
|
if radioMenu then -- requires optional radio menu to have loaded
|
||||||
|
local mainMenu = radioMenu.mainMenus[attachTo]
|
||||||
|
if mainMenu then
|
||||||
|
fireCtrl.mainMenu = mainMenu
|
||||||
|
else
|
||||||
|
trigger.action.outText("+++fireCtrl: cannot find super menu <" .. attachTo .. ">", 30)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
trigger.action.outText("+++fireCtrl: REQUIRES radioMenu to run before inferno. 'AttachTo:' ignored.", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
fireCtrl.checkIn = theZone:getBoolFromZoneProperty("checkIn", true)
|
||||||
|
|
||||||
|
-- shared data persistence interface
|
||||||
|
if theZone:hasProperty("sharedData") then
|
||||||
|
fireCtrl.sharedData = theZone:getStringFromZoneProperty("sharedData", "cfxNameMissing")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function fireCtrl.start()
|
||||||
|
if not dcsCommon.libCheck then
|
||||||
|
trigger.action.outText("cfx fireCtrl requires dcsCommon", 30)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if not dcsCommon.libCheck("cfx fireCtrl", fireCtrl.requiredLibs) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- read config
|
||||||
|
fireCtrl.readConfigZone()
|
||||||
|
if dcsCommon.getSizeOfTable(inferno.zones) < fireCtrl.minFires then
|
||||||
|
trigger.action.outText("+++fCtl: too few inferno zones (" .. fireCtrl.minFires .. " required). fireCtrl aborted.", 30)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- connect event handler
|
||||||
|
world.addEventHandler(fireCtrl)
|
||||||
|
|
||||||
|
-- install inferno extinguished CB
|
||||||
|
inferno.installExtinguishedCB(fireCtrl.extCB)
|
||||||
|
|
||||||
|
-- now load all save data
|
||||||
|
if persistence then
|
||||||
|
-- sign up for persistence
|
||||||
|
callbacks = {}
|
||||||
|
callbacks.persistData = fireCtrl.saveData
|
||||||
|
persistence.registerModule("fireCtrl", callbacks)
|
||||||
|
-- now load my data
|
||||||
|
fireCtrl.loadData()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- start update
|
||||||
|
timer.scheduleFunction(fireCtrl.update, {}, timer.getTime() + 3)
|
||||||
|
|
||||||
|
-- say Hi!
|
||||||
|
trigger.action.outText("cf/x fireCtrl v" .. fireCtrl.version .. " started.", 30)
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if not fireCtrl.start() then
|
||||||
|
trigger.action.outText("fireCtrl failed to start up", 30)
|
||||||
|
fireCtrl = nil
|
||||||
|
end
|
||||||
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
guardianAngel = {}
|
guardianAngel = {}
|
||||||
guardianAngel.version = "4.0.0"
|
guardianAngel.version = "4.0.1"
|
||||||
guardianAngel.ups = 10 -- hard-coded!! missile track
|
guardianAngel.ups = 10 -- hard-coded!! missile track
|
||||||
guardianAngel.name = "Guardian Angel" -- just in case someone accesses .name
|
guardianAngel.name = "Guardian Angel" -- just in case someone accesses .name
|
||||||
guardianAngel.requiredLibs = {
|
guardianAngel.requiredLibs = {
|
||||||
@ -23,6 +23,7 @@ guardianAngel.requiredLibs = {
|
|||||||
- once per second sanctuary calc
|
- once per second sanctuary calc
|
||||||
- expanded getWatchedUnitByName to include inSanctuary
|
- expanded getWatchedUnitByName to include inSanctuary
|
||||||
- sanctuary zones use coalition
|
- sanctuary zones use coalition
|
||||||
|
4.0.1 - code hardening (DCS)
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
guardianAngel.active = true -- can be turned on / off
|
guardianAngel.active = true -- can be turned on / off
|
||||||
@ -347,6 +348,7 @@ function guardianAngel:onEvent(event)
|
|||||||
-- get cat, this returns 1 for unit (as it should, so we can get
|
-- get cat, this returns 1 for unit (as it should, so we can get
|
||||||
-- group, or if it's really a unit, which returns 0 for aircraft
|
-- group, or if it's really a unit, which returns 0 for aircraft
|
||||||
if not theUnit.getCategory then return end
|
if not theUnit.getCategory then return end
|
||||||
|
if not theUnit.getGroup then return end -- ED silly stuff
|
||||||
local theGroup = theUnit:getGroup()
|
local theGroup = theUnit:getGroup()
|
||||||
if not theGroup then return end
|
if not theGroup then return end
|
||||||
local gCat = theGroup:getCategory()
|
local gCat = theGroup:getCategory()
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
cfxHeloTroops = {}
|
cfxHeloTroops = {}
|
||||||
cfxHeloTroops.version = "4.2.1"
|
cfxHeloTroops.version = "4.2.2"
|
||||||
cfxHeloTroops.verbose = false
|
cfxHeloTroops.verbose = false
|
||||||
cfxHeloTroops.autoDrop = true
|
cfxHeloTroops.autoDrop = true
|
||||||
cfxHeloTroops.autoPickup = false
|
cfxHeloTroops.autoPickup = false
|
||||||
@ -21,6 +21,7 @@ cfxHeloTroops.requestRange = 500 -- meters
|
|||||||
- support for drivable
|
- support for drivable
|
||||||
4.2.1 - increased verbosity
|
4.2.1 - increased verbosity
|
||||||
- also supports 'pickupRang" for reverse-compatibility with manual typo.
|
- also supports 'pickupRang" for reverse-compatibility with manual typo.
|
||||||
|
4.2.2 - support for attachTo:
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
cfxHeloTroops.minTime = 3 -- seconds beween tandings
|
cfxHeloTroops.minTime = 3 -- seconds beween tandings
|
||||||
@ -299,7 +300,11 @@ function cfxHeloTroops.setCommsMenu(theUnit)
|
|||||||
conf.unit = theUnit -- link back
|
conf.unit = theUnit -- link back
|
||||||
-- if we don't have an F-10 menu, create one
|
-- 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(id, 'Airlift Troops')
|
local mainMenu = nil
|
||||||
|
if cfxHeloTroops.mainMenu then
|
||||||
|
mainMenu = radioMenu.getMainMenuFor(cfxHeloTroops.mainMenu)
|
||||||
|
end
|
||||||
|
conf.myMainMenu = missionCommands.addSubMenuForGroup(id, 'Airlift Troops', mainMenu)
|
||||||
end
|
end
|
||||||
-- clear out existing commands, add new
|
-- clear out existing commands, add new
|
||||||
cfxHeloTroops.clearCommsSubmenus(conf)
|
cfxHeloTroops.clearCommsSubmenus(conf)
|
||||||
@ -1021,6 +1026,20 @@ function cfxHeloTroops.readConfigZone()
|
|||||||
tc = dcsCommon.splitString(tc, ",")
|
tc = dcsCommon.splitString(tc, ",")
|
||||||
cfxHeloTroops.troopCarriers = dcsCommon.trimArray(tc)
|
cfxHeloTroops.troopCarriers = dcsCommon.trimArray(tc)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if theZone:hasProperty("attachTo:") then
|
||||||
|
local attachTo = theZone:getStringFromZoneProperty("attachTo:", "<none>")
|
||||||
|
if radioMenu then -- requires optional radio menu to have loaded
|
||||||
|
local mainMenu = radioMenu.mainMenus[attachTo]
|
||||||
|
if mainMenu then
|
||||||
|
cfxHeloTroops.mainMenu = mainMenu
|
||||||
|
else
|
||||||
|
trigger.action.outText("+++heloT: cannot find super menu <" .. attachTo .. ">", 30)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
trigger.action.outText("+++heloT: REQUIRES radioMenu to run before cfxHeloTroops. 'AttachTo:' ignored.", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
|
|||||||
@ -1,9 +1,14 @@
|
|||||||
inferno = {}
|
inferno = {}
|
||||||
inferno.version = "1.0.0"
|
inferno.version = "1.0.1"
|
||||||
inferno.requiredLibs = {
|
inferno.requiredLibs = {
|
||||||
"dcsCommon",
|
"dcsCommon",
|
||||||
"cfxZones",
|
"cfxZones",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
--[[-- Version History
|
||||||
|
1.0.0 - Initial version
|
||||||
|
1.0.1 - cleanup
|
||||||
|
--]]--
|
||||||
--
|
--
|
||||||
-- Inferno models fires inside inferno zones. Fires can spread and
|
-- Inferno models fires inside inferno zones. Fires can spread and
|
||||||
-- be extinguished by aircraft from the airtank module
|
-- be extinguished by aircraft from the airtank module
|
||||||
@ -91,19 +96,9 @@ function inferno.buildGrid(theZone)
|
|||||||
local lp = {x=xc, y=zc}
|
local lp = {x=xc, y=zc}
|
||||||
local yc = land.getHeight(lp)
|
local yc = land.getHeight(lp)
|
||||||
ele.center = {x=xc, y=yc, z=zc}
|
ele.center = {x=xc, y=yc, z=zc}
|
||||||
--[[--
|
|
||||||
if theZone.markCell then
|
|
||||||
dcsCommon.createStaticObjectForCoalitionAtLocation(0, ele.center, dcsCommon.uuid(theZone.name), "Black_Tyre_RF", 0, false)
|
|
||||||
|
|
||||||
dcsCommon.createStaticObjectForCoalitionAtLocation(0, {x=ele.center.x - cellx/2, y=0, z=ele.center.z - cellz/2}, dcsCommon.uuid(theZone.name), "Windsock", 0, false)
|
|
||||||
dcsCommon.createStaticObjectForCoalitionAtLocation(0, {x=ele.center.x + cellx/2, y=0, z=ele.center.z - cellz/2}, dcsCommon.uuid(theZone.name), "Windsock", 0, false)
|
|
||||||
dcsCommon.createStaticObjectForCoalitionAtLocation(0, {x=ele.center.x - cellx/2, y=0, z=ele.center.z + cellz/2}, dcsCommon.uuid(theZone.name), "Windsock", 0, false)
|
|
||||||
dcsCommon.createStaticObjectForCoalitionAtLocation(0, {x=ele.center.x + cellx/2, y=0, z=ele.center.z + cellz/2}, dcsCommon.uuid(theZone.name), "Windsock", 0, false)
|
|
||||||
end
|
|
||||||
--]]--
|
|
||||||
ele.fxpos = {x=xf, y=yc, z=zf}
|
ele.fxpos = {x=xf, y=yc, z=zf}
|
||||||
ele.myType = land.getSurfaceType(lp) -- LAND=1, SHALLOW_WATER=2, WATER=3, ROAD=4, RUNWAY=5
|
ele.myType = land.getSurfaceType(lp) -- LAND=1, SHALLOW_WATER=2, WATER=3, ROAD=4, RUNWAY=5
|
||||||
-- we don not burn if a cell has shallow or deep water, or roads or runways
|
-- we do not burn if a cell has shallow or deep water, or roads or runways
|
||||||
ele.inside = theZone:pointInZone(ele.center)
|
ele.inside = theZone:pointInZone(ele.center)
|
||||||
if theZone.freeBorder then
|
if theZone.freeBorder then
|
||||||
if x == 1 or x == numX then ele.inside = false end
|
if x == 1 or x == numX then ele.inside = false end
|
||||||
@ -120,15 +115,6 @@ function inferno.buildGrid(theZone)
|
|||||||
until land.getSurfaceType(lp) == 1
|
until land.getSurfaceType(lp) == 1
|
||||||
ele.fxpos = {x=xf, y=yc, z=zf}
|
ele.fxpos = {x=xf, y=yc, z=zf}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- place a fire on the cell
|
|
||||||
if theZone.fullBlaze then
|
|
||||||
ele.fxname = dcsCommon.uuid(theZone.name)
|
|
||||||
trigger.action.effectSmokeBig(ele.fxpos, 4, 0.5 , ele.fxname)
|
|
||||||
ele.myStage = inferno.maxStages
|
|
||||||
ele.fxsize = 4
|
|
||||||
theZone.burning = true
|
|
||||||
end
|
|
||||||
local sparkable = {x=x, z=z}
|
local sparkable = {x=x, z=z}
|
||||||
table.insert(goodCells, sparkable)
|
table.insert(goodCells, sparkable)
|
||||||
fCount = fCount + 1
|
fCount = fCount + 1
|
||||||
@ -180,7 +166,7 @@ function inferno.readZone(theZone)
|
|||||||
theZone.freeBorder = theZone:getBoolFromZoneProperty("freeBorder", true) -- ring zone with non-burning zones
|
theZone.freeBorder = theZone:getBoolFromZoneProperty("freeBorder", true) -- ring zone with non-burning zones
|
||||||
theZone.eternal = theZone:getBoolFromZoneProperty("eternal", true)
|
theZone.eternal = theZone:getBoolFromZoneProperty("eternal", true)
|
||||||
theZone.stagger = theZone:getBoolFromZoneProperty("stagger", true) -- randomize inside cell
|
theZone.stagger = theZone:getBoolFromZoneProperty("stagger", true) -- randomize inside cell
|
||||||
theZone.fullBlaze = theZone:getBoolFromZoneProperty("fullBlaze", false )
|
-- theZone.fullBlaze = theZone:getBoolFromZoneProperty("fullBlaze", false )
|
||||||
theZone.canSpread = theZone:getBoolFromZoneProperty("canSpread", true)
|
theZone.canSpread = theZone:getBoolFromZoneProperty("canSpread", true)
|
||||||
theZone.maxSpread = theZone:getNumberFromZoneProperty("maxSpread", 999999)
|
theZone.maxSpread = theZone:getNumberFromZoneProperty("maxSpread", 999999)
|
||||||
theZone.impactSmoke = theZone:getBoolFromZoneProperty("impactSmoke", inferno.impactSmoke)
|
theZone.impactSmoke = theZone:getBoolFromZoneProperty("impactSmoke", inferno.impactSmoke)
|
||||||
@ -201,7 +187,6 @@ function inferno.readZone(theZone)
|
|||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
-- API for water droppers
|
-- API for water droppers
|
||||||
--
|
--
|
||||||
@ -234,7 +219,6 @@ function inferno.waterInZone(theZone, p, amount)
|
|||||||
end
|
end
|
||||||
local ele = row[zc]
|
local ele = row[zc]
|
||||||
|
|
||||||
-- local ele = theZone.grid[xc][zc]
|
|
||||||
if not ele then
|
if not ele then
|
||||||
trigger.action.outText("Inferno: no ele for <" .. theZone.name .. ">: x<" .. x .. ">z<" .. z .. ">", 30)
|
trigger.action.outText("Inferno: no ele for <" .. theZone.name .. ">: x<" .. x .. ">z<" .. z .. ">", 30)
|
||||||
trigger.action.outText("with xc = " .. xc .. ", numX 0 " .. theZone.numX .. ", zc = " .. zc .. ", numZ=" .. theZone.numZ, 30)
|
trigger.action.outText("with xc = " .. xc .. ", numX 0 " .. theZone.numX .. ", zc = " .. zc .. ", numZ=" .. theZone.numZ, 30)
|
||||||
@ -264,8 +248,6 @@ function inferno.waterInZone(theZone, p, amount)
|
|||||||
xc = xc + ofx
|
xc = xc + ofx
|
||||||
zc = zc + ofz
|
zc = zc + ofz
|
||||||
ele = theZone.grid[xc][zc]
|
ele = theZone.grid[xc][zc]
|
||||||
-- else
|
|
||||||
-- trigger.action.outText("inferno dropper: NO ALIGNMENT, beds are burning.", 30)
|
|
||||||
end
|
end
|
||||||
if theZone.impactSmoke then
|
if theZone.impactSmoke then
|
||||||
if inferno.verbose then
|
if inferno.verbose then
|
||||||
@ -332,16 +314,13 @@ function inferno.waterDropped(p, amount, data) -- if returns non-nil, has hit a
|
|||||||
end
|
end
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
-- IGNITE & DOUSE
|
-- IGNITE & DOUSE
|
||||||
--
|
--
|
||||||
function inferno.sparkCell(theZone, x, z)
|
function inferno.sparkCell(theZone, x, z)
|
||||||
local ele = theZone.grid[x][z]
|
local ele = theZone.grid[x][z]
|
||||||
if not ele.inside then
|
if not ele.inside then
|
||||||
if theZone.verbose then
|
if theZone.verbose then trigger.action.outText("ele x<" .. x .. ">z<" .. z .. "> is outside, no spark!", 30) end
|
||||||
trigger.action.outText("ele x<" .. x .. ">z<" .. z .. "> is outside, no spark!", 30)
|
|
||||||
end
|
|
||||||
return
|
return
|
||||||
false end
|
false end
|
||||||
ele.fxname = dcsCommon.uuid(theZone.name)
|
ele.fxname = dcsCommon.uuid(theZone.name)
|
||||||
@ -383,16 +362,11 @@ function inferno.ignite(theZone)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function inferno.startFire(theZone)
|
function inferno.startFire(theZone)
|
||||||
if theZone.burning then
|
if theZone.burning then return end
|
||||||
return
|
|
||||||
end
|
|
||||||
inferno.ignite(theZone)
|
inferno.ignite(theZone)
|
||||||
end
|
end
|
||||||
|
|
||||||
function inferno.douseFire(theZone)
|
function inferno.douseFire(theZone)
|
||||||
-- if not theZone.burning then
|
|
||||||
-- return
|
|
||||||
-- end
|
|
||||||
-- walk the grid, and kill all flames, set all eles
|
-- walk the grid, and kill all flames, set all eles
|
||||||
-- to end state
|
-- to end state
|
||||||
for x=1, theZone.numX do
|
for x=1, theZone.numX do
|
||||||
@ -660,24 +634,7 @@ function inferno.readConfigZone()
|
|||||||
inferno.ups = theZone:getNumberFromZoneProperty("ups", 1)
|
inferno.ups = theZone:getNumberFromZoneProperty("ups", 1)
|
||||||
inferno.fireTick = theZone:getNumberFromZoneProperty("fireTick", 10)
|
inferno.fireTick = theZone:getNumberFromZoneProperty("fireTick", 10)
|
||||||
inferno.cellSize = theZone:getNumberFromZoneProperty("cellSize", 100)
|
inferno.cellSize = theZone:getNumberFromZoneProperty("cellSize", 100)
|
||||||
--[[
|
|
||||||
inferno.menuName = theZone:getStringFromZoneProperty("menuName", "Firefighting")
|
|
||||||
inferno.impactSmoke = theZone:getBoolFromZoneProperty("impactSmoke", false)
|
|
||||||
|
|
||||||
if theZone:hasProperty("attachTo:") then
|
|
||||||
local attachTo = theZone:getStringFromZoneProperty("attachTo:", "<none>")
|
|
||||||
if radioMenu then -- requires optional radio menu to have loaded
|
|
||||||
local mainMenu = radioMenu.mainMenus[attachTo]
|
|
||||||
if mainMenu then
|
|
||||||
inferno.mainMenu = mainMenu
|
|
||||||
else
|
|
||||||
trigger.action.outText("+++inferno: cannot find super menu <" .. attachTo .. ">", 30)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
trigger.action.outText("+++inferno: REQUIRES radioMenu to run before inferno. 'AttachTo:' ignored.", 30)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
--]]--
|
|
||||||
if theZone:hasProperty("fire#") then
|
if theZone:hasProperty("fire#") then
|
||||||
inferno.fireNum = theZone:getStringFromZoneProperty("fire#", "none")
|
inferno.fireNum = theZone:getStringFromZoneProperty("fire#", "none")
|
||||||
end
|
end
|
||||||
|
|||||||
@ -175,7 +175,7 @@ function persistence.saveTable(theTable, fileName, shared, append)
|
|||||||
if not shared then shared = false end
|
if not shared then shared = false end
|
||||||
|
|
||||||
net.log("persistence: before json conversion")
|
net.log("persistence: before json conversion")
|
||||||
local theString = net.lua2json(theTable)
|
local theString = net.lua2json(theTable) -- WARNING! does not handle arrays with [0]!
|
||||||
net.log("persistence: json conversion complete")
|
net.log("persistence: json conversion complete")
|
||||||
|
|
||||||
if not theString then theString = "" end
|
if not theString then theString = "" end
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
cfxPlayerScore = {}
|
cfxPlayerScore = {}
|
||||||
cfxPlayerScore.version = "5.1.0"
|
cfxPlayerScore.version = "5.2.1"
|
||||||
cfxPlayerScore.name = "cfxPlayerScore" -- compatibility with flag bangers
|
cfxPlayerScore.name = "cfxPlayerScore" -- compatibility with flag bangers
|
||||||
cfxPlayerScore.firstSave = true -- to force overwrite
|
cfxPlayerScore.firstSave = true -- to force overwrite
|
||||||
--[[-- VERSION HISTORY
|
--[[-- VERSION HISTORY
|
||||||
@ -15,7 +15,10 @@ cfxPlayerScore.firstSave = true -- to force overwrite
|
|||||||
- improved wildcard support
|
- improved wildcard support
|
||||||
- event 20 for CA also supported
|
- event 20 for CA also supported
|
||||||
- "threading the needle" -- support for hit event and unit traceback
|
- "threading the needle" -- support for hit event and unit traceback
|
||||||
|
5.2.0 - PlayerScoreTable supports wildcards, e.g. "Batumi*"
|
||||||
|
5.2.1 - Event 20 (CA join) corrected typo
|
||||||
|
- wiping score on enter and birth
|
||||||
|
- more robust initscore
|
||||||
TODO: Kill event no longer invoked for map objetcs, attribute
|
TODO: Kill event no longer invoked for map objetcs, attribute
|
||||||
to faction now, reverse invocation direction with PlayerScore
|
to faction now, reverse invocation direction with PlayerScore
|
||||||
TODO: better wildcard support for kill events
|
TODO: better wildcard support for kill events
|
||||||
@ -42,7 +45,8 @@ cfxPlayerScore.killZones = {} -- when set, kills only count here
|
|||||||
-- typeScore: dictionary sorted by typeString for score
|
-- typeScore: dictionary sorted by typeString for score
|
||||||
-- extend to add more types. It is used by unitType2score to
|
-- extend to add more types. It is used by unitType2score to
|
||||||
-- determine the base unit score
|
-- determine the base unit score
|
||||||
cfxPlayerScore.typeScore = {} -- ALL UPPERCASE NOW!!!
|
cfxPlayerScore.typeScore = {} -- ALL UPPERCASE
|
||||||
|
cfxPlayerScore.wildTypes = {} -- ALL UPPERCASE
|
||||||
cfxPlayerScore.lastPlayerLanding = {} -- timestamp, by player name
|
cfxPlayerScore.lastPlayerLanding = {} -- timestamp, by player name
|
||||||
cfxPlayerScore.delayBetweenLandings = 30 -- seconds to count as separate landings, also set during take-off to prevent janky t/o to count.
|
cfxPlayerScore.delayBetweenLandings = 30 -- seconds to count as separate landings, also set during take-off to prevent janky t/o to count.
|
||||||
cfxPlayerScore.aircraft = 50
|
cfxPlayerScore.aircraft = 50
|
||||||
@ -191,8 +195,18 @@ function cfxPlayerScore.cat2BaseScore(inCat)
|
|||||||
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.wildMatch(inName)
|
||||||
|
-- if inName starts the same as any wildcard, return score
|
||||||
|
for wName, wScore in pairs (cfxPlayerScore.wildTypes) do
|
||||||
|
if dcsCommon.stringStartsWith(inName, wName, true) then
|
||||||
|
if cfxPlayerScore.verbose then trigger.action.outText("+++PScr: wildmatch <" .. inName .. "> to <" .. wName .. ">, score <" .. wScore .. ">", 30) end
|
||||||
|
return wScore
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
function cfxPlayerScore.object2score(inVictim, killSide) -- does not have group
|
function cfxPlayerScore.object2score(inVictim, killSide) -- does not have group, go by type
|
||||||
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
|
local inName
|
||||||
@ -224,6 +238,7 @@ function cfxPlayerScore.object2score(inVictim, killSide) -- does not have group
|
|||||||
-- try the type desc
|
-- try the type desc
|
||||||
local theType = inVictim:getTypeName()
|
local theType = inVictim:getTypeName()
|
||||||
if theType then objectScore = cfxPlayerScore.typeScore[theType:upper()] end
|
if theType then objectScore = cfxPlayerScore.typeScore[theType:upper()] end
|
||||||
|
if not objectScore then objectScore = cfxPlayerScore.wildMatch(theType) end
|
||||||
end
|
end
|
||||||
if type(objectScore) == "string" then objectScore = tonumber(objectScore)end
|
if type(objectScore) == "string" then objectScore = tonumber(objectScore)end
|
||||||
if objectScore then return objectScore end
|
if objectScore then return objectScore end
|
||||||
@ -277,16 +292,25 @@ function cfxPlayerScore.unit2score(inUnit)
|
|||||||
-- 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
|
local uScore = nil
|
||||||
if vicName then uScore = cfxPlayerScore.typeScore[vicName:upper()] end
|
if vicName then
|
||||||
|
uScore = cfxPlayerScore.typeScore[vicName:upper()]
|
||||||
|
if not uScore then uScore = cfxPlayerScore.wildMatch(vicName) end
|
||||||
|
end
|
||||||
-- see if all members of group score
|
-- see if all members of group score
|
||||||
if (not uScore) then -- and vicGroup then
|
if (not uScore) then -- and vicGroup then
|
||||||
local grpName = cfxMX.spawnedUnitGroupNameByName[vicName]--vicGroup:getName()
|
local grpName = cfxMX.spawnedUnitGroupNameByName[vicName]--vicGroup:getName()
|
||||||
if grpName then uScore = cfxPlayerScore.typeScore[grpName:upper()] end
|
if grpName then
|
||||||
|
uScore = cfxPlayerScore.typeScore[grpName:upper()]
|
||||||
|
if not uScore then uScore = cfxPlayerScore.wildMatch(grpName) end
|
||||||
|
end
|
||||||
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
|
||||||
if vicType then uScore = cfxPlayerScore.typeScore[vicType:upper()] end
|
if vicType then
|
||||||
|
uScore = cfxPlayerScore.typeScore[vicType:upper()]
|
||||||
|
if not uScore then uScore = cfxPlayerScore.wildMatch(vicType) end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
if type(uScore) == "string" then uScore = tonumber(uScore) end
|
if type(uScore) == "string" then uScore = tonumber(uScore) end
|
||||||
if not uScore then uScore = 0 end
|
if not uScore then uScore = 0 end
|
||||||
@ -299,7 +323,8 @@ end
|
|||||||
function cfxPlayerScore.getPlayerScore(playerName)
|
function cfxPlayerScore.getPlayerScore(playerName)
|
||||||
local thePlayerScore = cfxPlayerScore.playerScore[playerName]
|
local thePlayerScore = cfxPlayerScore.playerScore[playerName]
|
||||||
if not thePlayerScore then
|
if not thePlayerScore then
|
||||||
thePlayerScore = {}
|
thePlayerScore = cfxPlayerScore.createNewPlayerScore(playerName)
|
||||||
|
--[[-- thePlayerScore = {}
|
||||||
thePlayerScore.name = playerName
|
thePlayerScore.name = playerName
|
||||||
thePlayerScore.score = 0 -- score
|
thePlayerScore.score = 0 -- score
|
||||||
thePlayerScore.scoreaccu = 0 -- for deferred
|
thePlayerScore.scoreaccu = 0 -- for deferred
|
||||||
@ -309,10 +334,42 @@ function cfxPlayerScore.getPlayerScore(playerName)
|
|||||||
thePlayerScore.featTypes = {} -- dict <featname> <number> of other things player did
|
thePlayerScore.featTypes = {} -- dict <featname> <number> of other things player did
|
||||||
thePlayerScore.featQueue = {} -- when using deferred
|
thePlayerScore.featQueue = {} -- when using deferred
|
||||||
thePlayerScore.totalFeats = 0
|
thePlayerScore.totalFeats = 0
|
||||||
|
--]]--
|
||||||
end
|
end
|
||||||
return thePlayerScore
|
return thePlayerScore
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function cfxPlayerScore.createNewPlayerScore(playerName)
|
||||||
|
local thePlayerScore = {}
|
||||||
|
thePlayerScore.name = playerName
|
||||||
|
thePlayerScore.score = 0 -- score
|
||||||
|
thePlayerScore.scoreaccu = 0 -- for deferred
|
||||||
|
thePlayerScore.killTypes = {} -- the type strings killed, dict <typename> <numkilla>
|
||||||
|
thePlayerScore.killQueue = {} -- when using deferred
|
||||||
|
thePlayerScore.totalKills = 0 -- number of kills total
|
||||||
|
thePlayerScore.featTypes = {} -- dict <featname> <number> of other things player did
|
||||||
|
thePlayerScore.featQueue = {} -- when using deferred
|
||||||
|
thePlayerScore.totalFeats = 0
|
||||||
|
return thePlayerScore
|
||||||
|
end
|
||||||
|
|
||||||
|
function cfxPlayerScore.wipeScore(playerName)
|
||||||
|
if cfxPlayerScore.verbose then trigger.action.outText("+++pScr: enter wipe score for player <" .. playerName .. ">", 30) end
|
||||||
|
if not cfxPlayerScore.playerScore[playerName] then return end
|
||||||
|
thePlayerScore = cfxPlayerScore.getPlayerScore(playerName)
|
||||||
|
local loss = false
|
||||||
|
if thePlayerScore.scoreaccu > 0 then loss = true end
|
||||||
|
if dcsCommon.getSizeOfTable(thePlayerScore.featQueue) > 0 then loss = true end
|
||||||
|
if dcsCommon.getSizeOfTable(thePlayerScore.killQueue) > 0 then loss = true end
|
||||||
|
thePlayerScore.scoreaccu = 0
|
||||||
|
thePlayerScore.killQueue = {}
|
||||||
|
thePlayerScore.featQueue = {}
|
||||||
|
cfxPlayerScore.setPlayerScore(playerName, thePlayerScore) -- write back
|
||||||
|
if loss then
|
||||||
|
trigger.action.outText("Player " .. playerName .. " lost score.", 30) -- everyone sees this
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function cfxPlayerScore.setPlayerScore(playerName, thePlayerScore)
|
function cfxPlayerScore.setPlayerScore(playerName, thePlayerScore)
|
||||||
cfxPlayerScore.playerScore[playerName] = thePlayerScore
|
cfxPlayerScore.playerScore[playerName] = thePlayerScore
|
||||||
end
|
end
|
||||||
@ -511,6 +568,7 @@ function cfxPlayerScore.isNamedUnit(theUnit)
|
|||||||
if not theName then return false end
|
if not theName then return false end
|
||||||
end
|
end
|
||||||
if cfxPlayerScore.typeScore[theName:upper()] then return true end
|
if cfxPlayerScore.typeScore[theName:upper()] then return true end
|
||||||
|
if cfxPlayerScore.wildMatch(theName) then return true end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -806,9 +864,7 @@ function cfxPlayerScore.scheduledAward(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)) then
|
||||||
not Unit.isExist(theUnit)
|
|
||||||
then
|
|
||||||
-- unit is gone
|
-- unit is gone
|
||||||
trigger.action.outText("Player <" .. playerName .. "> lost score.", 30)
|
trigger.action.outText("Player <" .. playerName .. "> lost score.", 30)
|
||||||
return
|
return
|
||||||
@ -890,6 +946,7 @@ function cfxPlayerScore.scheduledAward(args)
|
|||||||
-- output score
|
-- output score
|
||||||
desc = desc .. "\n"
|
desc = desc .. "\n"
|
||||||
if hasAward then trigger.action.outTextForCoalition(coa, desc, 30) end
|
if hasAward then trigger.action.outTextForCoalition(coa, desc, 30) end
|
||||||
|
-- do NOT kill the entire record, or accu kills and feats are gone too
|
||||||
end
|
end
|
||||||
|
|
||||||
function cfxPlayerScore.handlePlayerDeath(theEvent)
|
function cfxPlayerScore.handlePlayerDeath(theEvent)
|
||||||
@ -903,6 +960,9 @@ function cfxPlayerScore.handlePlayerDeath(theEvent)
|
|||||||
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.
|
||||||
|
-- cancel all scores accumulated
|
||||||
|
cfxPlayerScore.wipeScore(pName)
|
||||||
|
|
||||||
if cfxPlayerScore.planeLoss ~= 0 then
|
if cfxPlayerScore.planeLoss ~= 0 then
|
||||||
-- plane loss has IMMEDIATE consequences
|
-- plane loss has IMMEDIATE consequences
|
||||||
cfxPlayerScore.updateScoreForPlayerImmediate(pName, cfxPlayerScore.planeLoss)
|
cfxPlayerScore.updateScoreForPlayerImmediate(pName, cfxPlayerScore.planeLoss)
|
||||||
@ -943,10 +1003,10 @@ function cfxPlayerScore.isScoreEvent(theEvent)
|
|||||||
if who then
|
if who then
|
||||||
name = "(inval initi)"
|
name = "(inval initi)"
|
||||||
if who.getName then name = who:getName() end
|
if who.getName then name = who:getName() end
|
||||||
if not name then -- WTF??? could be a weapon
|
if not name or (#name < 1) then -- WTF??? could be a weapon
|
||||||
name = "!nil getName!"
|
name = "!no getName!"
|
||||||
if who.getTypeName then name = who:getTypeName() end
|
if who.getTypeName then name = who:getTypeName() end
|
||||||
if not name then name = "WTFer" end
|
if not name or (#name < 1) then name = "WTFer" end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -963,7 +1023,7 @@ function cfxPlayerScore.isScoreEvent(theEvent)
|
|||||||
-- a hit event will save the last player to hit
|
-- a hit event will save the last player to hit
|
||||||
-- so we can attribute with unit lost
|
-- so we can attribute with unit lost
|
||||||
if theEvent.id == 2 then -- hit processing
|
if theEvent.id == 2 then -- hit processing
|
||||||
if cfxPlayerScore.verbose then trigger.action.outText("enter hit pre-processing", 30) end
|
-- if cfxPlayerScore.verbose then trigger.action.outText("enter hit pre-processing", 30) end
|
||||||
local who = theEvent.initiator
|
local who = theEvent.initiator
|
||||||
if not who.getPlayerName then return false end -- non-player originator
|
if not who.getPlayerName then return false end -- non-player originator
|
||||||
local pName = who:getPlayerName()
|
local pName = who:getPlayerName()
|
||||||
@ -981,7 +1041,10 @@ function cfxPlayerScore.isScoreEvent(theEvent)
|
|||||||
|
|
||||||
-- 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
|
if not theUnit.getName then
|
||||||
|
-- trigger.action.outText("+++pScr: no unit name, DCS err - abort.", 30)
|
||||||
|
return false
|
||||||
|
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
|
||||||
@ -1017,6 +1080,7 @@ function cfxPlayerScore.isScoreEvent(theEvent)
|
|||||||
-- credit unit and current person driving that unit
|
-- credit unit and current person driving that unit
|
||||||
-- there is a small percentage that this is wrong
|
-- there is a small percentage that this is wrong
|
||||||
-- player, but that's an edge case
|
-- player, but that's an edge case
|
||||||
|
-- if no player inhabits killing unit any more, no score attributed
|
||||||
cfxPlayerScore.processKill(theUnit, who)
|
cfxPlayerScore.processKill(theUnit, who)
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
@ -1054,8 +1118,11 @@ function cfxPlayerScore.isScoreEvent(theEvent)
|
|||||||
|
|
||||||
-- enter unit / birth event for players initializes score if
|
-- enter unit / birth event for players initializes score if
|
||||||
-- not existed, and nils the queue. 20 creates compat with CA
|
-- not existed, and nils the queue. 20 creates compat with CA
|
||||||
if theEvent == 20 or -- enter unit
|
if theEvent.id == 20 or -- enter unit
|
||||||
theEvent.id == 15 then -- player birth
|
theEvent.id == 15 then -- player birth
|
||||||
|
-- since we get into a new plane, erase all pending score
|
||||||
|
local pName = theUnit:getPlayerName()
|
||||||
|
cfxPlayerScore.wipeScore(pName)
|
||||||
-- link player with their unit
|
-- link player with their unit
|
||||||
cfxPlayerScore.linkUnitWithPlayer(theUnit)
|
cfxPlayerScore.linkUnitWithPlayer(theUnit)
|
||||||
cfxPlayerScore.unitSpawnTime[uName] = timer.getTime() -- to detect 'early landing'
|
cfxPlayerScore.unitSpawnTime[uName] = timer.getTime() -- to detect 'early landing'
|
||||||
@ -1110,6 +1177,7 @@ end
|
|||||||
|
|
||||||
function cfxPlayerScore.handleScoreEvent(theEvent)
|
function cfxPlayerScore.handleScoreEvent(theEvent)
|
||||||
cfxPlayerScore.currentEventUnit = theEvent.initiator
|
cfxPlayerScore.currentEventUnit = theEvent.initiator
|
||||||
|
if cfxPlayerScore.verbose then trigger.action.outText("Set currentEventUnit to <" .. theEvent.initiator:getName() .. ">", 30) end
|
||||||
if theEvent.id == 28 then
|
if theEvent.id == 28 then
|
||||||
-- kill from player detected.
|
-- kill from player detected.
|
||||||
cfxPlayerScore.killDetected(theEvent)
|
cfxPlayerScore.killDetected(theEvent)
|
||||||
@ -1361,12 +1429,30 @@ function cfxPlayerScore.start()
|
|||||||
if not dcsCommon.libCheck("cfx Player Score", cfxPlayerScore.requiredLibs)
|
if not dcsCommon.libCheck("cfx Player Score", cfxPlayerScore.requiredLibs)
|
||||||
then return false end
|
then return false end
|
||||||
|
|
||||||
|
-- only read verbose flag
|
||||||
|
-- now read my config zone
|
||||||
|
local theZone = cfxZones.getZoneByName("playerScoreConfig")
|
||||||
|
if theZone then cfxPlayerScore.verbose = theZone.verbose end
|
||||||
|
|
||||||
-- read my score table
|
-- read my score table
|
||||||
-- 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
|
||||||
|
if cfxPlayerScore.verbose then trigger.action.outText("+++pScr: has playerSocreTable", 30) end
|
||||||
-- 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
|
||||||
|
-- CASE INSENSITIVE!!!!!
|
||||||
|
-- now process all wildcarded types and add them to my wildTypes
|
||||||
|
cfxPlayerScore.wildTypes = {}
|
||||||
|
for theType, theScore in pairs(cfxPlayerScore.typeScore) do
|
||||||
|
if dcsCommon.stringEndsWith(theType, "*") then
|
||||||
|
local wcType = dcsCommon.removeEnding(theType, "*")
|
||||||
|
cfxPlayerScore.wildTypes[wcType] = theScore
|
||||||
|
if cfxPlayerScore.verbose then
|
||||||
|
trigger.action.outText("+++PScr: wildcard type/name <" .. wcType .. "> with score <" .. theScore .. "> registered", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- read score tiggers and values
|
-- read score tiggers and values
|
||||||
@ -1398,10 +1484,11 @@ function cfxPlayerScore.start()
|
|||||||
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. reading late
|
||||||
local theZone = cfxZones.getZoneByName("playerScoreConfig")
|
local theZone = cfxZones.getZoneByName("playerScoreConfig")
|
||||||
if not theZone then theZone = cfxZones.createSimpleZone("playerScoreConfig") end
|
if not theZone then theZone = cfxZones.createSimpleZone("playerScoreConfig") 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
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
pulseFlags = {}
|
pulseFlags = {}
|
||||||
pulseFlags.version = "2.0.2"
|
pulseFlags.version = "2.0.3"
|
||||||
pulseFlags.verbose = false
|
pulseFlags.verbose = false
|
||||||
pulseFlags.requiredLibs = {
|
pulseFlags.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
@ -15,6 +15,7 @@ pulseFlags.requiredLibs = {
|
|||||||
using method on all outputs
|
using method on all outputs
|
||||||
- 2.0.1 activateZoneFlag now works correctly
|
- 2.0.1 activateZoneFlag now works correctly
|
||||||
- 2.0.2 fixed scheduledTime bug while persisting
|
- 2.0.2 fixed scheduledTime bug while persisting
|
||||||
|
- 2.0.3 now setting -1 (infinite) as pulses works correctly
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
pulseFlags.pulses = {}
|
pulseFlags.pulses = {}
|
||||||
@ -56,14 +57,20 @@ function pulseFlags.createPulseWithZone(theZone)
|
|||||||
|
|
||||||
theZone.pulses = -1 -- set to infinite
|
theZone.pulses = -1 -- set to infinite
|
||||||
if theZone:hasProperty("pulses") then
|
if theZone:hasProperty("pulses") then
|
||||||
local minP, maxP = theZone:getPositiveRangeFromZoneProperty("pulses", 1)
|
local tt = theZone:getStringFromZoneProperty("pulses", -1)
|
||||||
if minP == maxP then theZone.pulses = minP
|
tn = tonumber(tt) -- returns nil for a range
|
||||||
|
if tn then
|
||||||
|
if tn < 1 then theZone.pulses = -1 else theZone.pulses = tn end
|
||||||
else
|
else
|
||||||
theZone.pulses = cfxZones.randomInRange(minP, maxP)
|
local minP, maxP = theZone:getPositiveRangeFromZoneProperty("pulses", 1)
|
||||||
|
if minP == maxP then theZone.pulses = minP
|
||||||
|
else
|
||||||
|
theZone.pulses = cfxZones.randomInRange(minP, maxP)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if pulseFlags.verbose or theZone.verbose then
|
if theZone.verbose or pulseFlag.verbose then
|
||||||
trigger.action.outText("+++pulF: zone <" .. theZone.name .. "> set to <" .. theZone.pulses .. "> pulses", 30)
|
trigger.action.outText("+++pulF: set pulses in <" .. theZone.name .. "> to <" .. theZone.pulses .. ">", 30)
|
||||||
end
|
end
|
||||||
|
|
||||||
theZone.pulsesLeft = 0 -- will start new cycle
|
theZone.pulsesLeft = 0 -- will start new cycle
|
||||||
|
|||||||
@ -164,7 +164,8 @@ function cfxSmokeZone.start()
|
|||||||
timer.scheduleFunction(cfxSmokeZone.checkFlags, {}, timer.getTime() + 1)
|
timer.scheduleFunction(cfxSmokeZone.checkFlags, {}, timer.getTime() + 1)
|
||||||
|
|
||||||
-- say hi
|
-- say hi
|
||||||
trigger.action.outText("cfx Smoke Zones v" .. cfxSmokeZone.version .. " started.", 30)
|
|
||||||
|
trigger.action.outText("cfx smoke zones v" .. cfxSmokeZone.version .. " started.", 30)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -232,7 +232,7 @@ function cfxSpawnZones.getRequestableSpawnersInRange(aPoint, aRange, aSide)
|
|||||||
-- only return spawners with this side
|
-- only return spawners with this side
|
||||||
-- note: this will NOT work with neutral players
|
-- note: this will NOT work with neutral players
|
||||||
hasMatch = false
|
hasMatch = false
|
||||||
reasons = reasons .. "[rawOwner] "
|
reasons = reasons .. "[rawOwner is <" .. aSpawner.rawOwner .. ">, need <" .. aSide .. ">] "
|
||||||
end
|
end
|
||||||
|
|
||||||
if not aSpawner.requestable then
|
if not aSpawner.requestable then
|
||||||
@ -258,21 +258,14 @@ end
|
|||||||
--
|
--
|
||||||
function cfxSpawnZones.verifySpawnOwnership(spawner)
|
function cfxSpawnZones.verifySpawnOwnership(spawner)
|
||||||
-- returns false ONLY if masterSpawn disagrees
|
-- returns false ONLY if masterSpawn disagrees
|
||||||
|
-- i.e. when masterOwner is not the same as spawner.rawOwner
|
||||||
if not spawner.masterZoneName then
|
if not spawner.masterZoneName then
|
||||||
--trigger.action.outText("spawner " .. spawner.name .. " no master, go!", 30)
|
if spawner.zone.verbose then trigger.action.outText("spawner " .. spawner.name .. " no masterOwner, go!", 30) end
|
||||||
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)
|
|
||||||
-- if not masterZone then
|
|
||||||
-- trigger.action.outText("spawner " .. spawner.name .. " DID NOT FIND MASTER ZONE <" .. spawner.masterZoneName .. ">", 30)
|
|
||||||
-- return false
|
|
||||||
-- end
|
|
||||||
local masterZone = spawner.masterZone
|
local masterZone = spawner.masterZone
|
||||||
-- if not masterZone.owner then
|
if spawner.zone.verbose then trigger.action.outText("spawner " .. spawner.name .. " has masterOwner <" .. masterZone.name .. ">", 30) end
|
||||||
--trigger.action.outText("spawner " .. spawner.name .. " - masterZone " .. masterZone.name .. " HAS NO OWNER????", 30)
|
|
||||||
-- return true
|
|
||||||
-- end
|
|
||||||
|
|
||||||
if (myCoalition ~= masterZone:getCoalition()) then
|
if (myCoalition ~= masterZone:getCoalition()) then
|
||||||
-- can't spawn, surrounding area owned by enemy
|
-- can't spawn, surrounding area owned by enemy
|
||||||
|
|||||||
Binary file not shown.
BIN
tutorial & demo missions/demo - fire starter.miz
Normal file
BIN
tutorial & demo missions/demo - fire starter.miz
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user