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.version = "2.2.0"
|
||||
FARPZones.version = "2.3.0"
|
||||
FARPZones.verbose = false
|
||||
--[[--
|
||||
Version History
|
||||
@ -26,6 +26,8 @@ FARPZones.verbose = false
|
||||
2.1.0 - integration with camp: needs repairs, produceResourceVehicles()
|
||||
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.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.showTitle = aZone:getBoolFromZoneProperty("showTitle", true)
|
||||
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
|
||||
end
|
||||
|
||||
@ -415,7 +430,7 @@ function FARPZones.somethingHappened(event)
|
||||
local ID = event.id
|
||||
|
||||
--trigger.action.outText("FZ: something happened", 30)
|
||||
local aFarp = event.place
|
||||
local aFarp = event.place -- place is type Airbase
|
||||
local zonedFarp = FARPZones.getFARPZoneForFARP(aFarp)
|
||||
|
||||
if not zonedFarp then
|
||||
@ -447,6 +462,18 @@ function FARPZones.somethingHappened(event)
|
||||
if newOwner == 2 then blueRed = "Blue" end
|
||||
trigger.action.outText("FARP " .. zonedFarp.zone.name .. " captured by " .. blueRed .."!", 30)
|
||||
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.zone.owner = newOwner
|
||||
-- update color in map
|
||||
|
||||
@ -100,6 +100,7 @@ function LZ.createLZWithZone(theZone)
|
||||
trigger.action.outText("+++LZ: new LZ <".. theZone.name ..">", 30)
|
||||
end
|
||||
|
||||
trigger.action.outText("zone <" .. theZone.name .. "> type of radius is <" .. type(theZone.radius) .. ">, val = " .. tonumber(theZone.radius), 30)
|
||||
end
|
||||
|
||||
function LZ.nameMatchForArray(theName, theArray, wildcard)
|
||||
@ -229,6 +230,10 @@ function LZ:onEvent(event)
|
||||
|
||||
for idx, aZone in pairs(LZ.LZs) do
|
||||
-- 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)
|
||||
if inZone then
|
||||
-- see if this unit interests us at all
|
||||
|
||||
@ -1,15 +1,75 @@
|
||||
WHpersistence = {}
|
||||
WHpersistence.version = "1.0.0"
|
||||
WHpersistence.version = "1.1.0"
|
||||
WHpersistence.requiredLibs = {
|
||||
"dcsCommon",
|
||||
"cfxZones",
|
||||
"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()
|
||||
local theData = {}
|
||||
WHpersistence.lastState = nil
|
||||
|
||||
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 = {}
|
||||
-- generate all WH data from all my airfields
|
||||
local allMyBase = world:getAirbases()
|
||||
@ -17,9 +77,33 @@ function WHpersistence.saveData()
|
||||
local name = theBase:getName()
|
||||
local WH = theBase:getWarehouse()
|
||||
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
|
||||
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
|
||||
end
|
||||
|
||||
@ -33,23 +117,36 @@ function WHpersistence.loadData()
|
||||
end
|
||||
return
|
||||
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
|
||||
-- WARNING: if original was set, but saved emptpy,
|
||||
-- we must now erase the original!
|
||||
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)
|
||||
if theBase then
|
||||
local theWH = theBase:getWarehouse()
|
||||
if theWH then
|
||||
-- we go through weapon, liquids and aircraft
|
||||
for idx, liq in pairs(inv.liquids) do
|
||||
theWH:setLiquidAmount(idx, liq)
|
||||
trigger.action.outText(name .. ": Liq <" .. idx .. "> : <" .. liq .. ">", 30)
|
||||
-- do liqids "manually"
|
||||
if inv.bLiq then
|
||||
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
|
||||
|
||||
for ref, num in pairs(inv.weapon) do
|
||||
theWH:setItem(ref, num)
|
||||
end
|
||||
for ref, num in pairs(inv.aircraft) do
|
||||
theWH:setItem(ref, num)
|
||||
if WHpersistence.verbose then trigger.action.outText(name .. ": Setting # of Aircraft <" .. ref .. "> to <" .. num .. ">", 30) end
|
||||
end
|
||||
else
|
||||
trigger.action.outText(name .. ": no warehouse")
|
||||
@ -58,6 +155,28 @@ function WHpersistence.loadData()
|
||||
trigger.action.outText(name .. ": no airbase")
|
||||
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
|
||||
--
|
||||
-- config
|
||||
@ -68,6 +187,8 @@ function WHpersistence.readConfigZone()
|
||||
theZone = cfxZones.createSimpleZone("WHpersistenceConfig")
|
||||
end
|
||||
WHpersistence.verbose = theZone.verbose
|
||||
WHpersistence.monitor = theZone:getBoolFromZoneProperty("monitor", false)
|
||||
WHpersistence.ups = theZone:getNumberFromZoneProperty("ups", 0.1) -- every 10 seconds
|
||||
end
|
||||
--
|
||||
-- GO
|
||||
@ -77,7 +198,7 @@ function WHpersistence.start()
|
||||
trigger.action.outText("cfx WHpersistence requires dcsCommon", 30)
|
||||
return false
|
||||
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()
|
||||
if persistence then
|
||||
callbacks = {}
|
||||
@ -86,6 +207,11 @@ function WHpersistence.start()
|
||||
-- now load my data
|
||||
WHpersistence.loadData()
|
||||
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)
|
||||
return true
|
||||
end
|
||||
|
||||
@ -1,11 +1,19 @@
|
||||
airtank = {}
|
||||
airtank.version = "1.0.0"
|
||||
airtank.version = "1.0.1"
|
||||
-- Module to extinguish fires controlled by the 'inferno' module.
|
||||
-- For 'airtank' fire extinguisher aircraft modules.
|
||||
airtank.requiredLibs = {
|
||||
"dcsCommon",
|
||||
"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
|
||||
-- tank attributes
|
||||
-- pumpArmed -- for hovering fills.
|
||||
@ -192,9 +200,20 @@ function airtank.installMenusForUnit(theUnit) -- assumes all unfit types are wee
|
||||
local pRoot = airtank.roots[gName]
|
||||
if pRoot then
|
||||
missionCommands.removeItemForGroup(gID, pRoot)
|
||||
pRoot = nil
|
||||
end
|
||||
|
||||
-- handle main menu
|
||||
local mainMenu = nil
|
||||
if airtank.mainMenu then
|
||||
mainMenu = radioMenu.getMainMenuFor(airtank.mainMenu)
|
||||
end
|
||||
|
||||
-- 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
|
||||
local args = {gName, uName, gID, uType, pName}
|
||||
-- menus:
|
||||
@ -535,7 +554,10 @@ function airtank.readConfigZone()
|
||||
if radioMenu then -- requires optional radio menu to have loaded
|
||||
local mainMenu = radioMenu.mainMenus[attachTo]
|
||||
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
|
||||
trigger.action.outText("+++airtank: cannot find super menu <" .. attachTo .. ">", 30)
|
||||
end
|
||||
|
||||
@ -22,7 +22,7 @@ VERSION HISTORY
|
||||
--]]--
|
||||
--
|
||||
-- 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.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 p = theUnit:getPoint()
|
||||
for idx, theCamp in pairs(camp.camps) do
|
||||
if theCamp.owner == coa and theCamp:pointInZone(p) then
|
||||
return theCamp
|
||||
end
|
||||
if theCamp.owner == coa and theCamp:pointInZone(p) then return theCamp end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
@ -45,18 +43,14 @@ end
|
||||
function camp.getCampsForCoa(coa)
|
||||
local myCamps = {}
|
||||
for idx, theCamp in pairs(camp.camps) do
|
||||
if theCamp.owner == coa then
|
||||
table.insert(myCamps, theCamp)
|
||||
end
|
||||
if theCamp.owner == coa then table.insert(myCamps, theCamp) end
|
||||
end
|
||||
return myCamps
|
||||
end
|
||||
|
||||
function camp.createCampWithZone(theZone)
|
||||
-- look for all cloners inside my zone
|
||||
if theZone.verbose or camp.verbose then
|
||||
trigger.action.outText("+++camp: processing <" .. theZone.name .. ">, owner is <" .. theZone.owner .. ">", 30)
|
||||
end
|
||||
if theZone.verbose or camp.verbose then trigger.action.outText("+++camp: processing <" .. theZone.name .. ">, owner is <" .. theZone.owner .. ">", 30) end
|
||||
|
||||
local allZones = cfxZones.getAllZonesInsideZone(theZone)
|
||||
local cloners = {}
|
||||
@ -65,19 +59,12 @@ function camp.createCampWithZone(theZone)
|
||||
for idx, aZone in pairs(allZones) do
|
||||
if aZone:hasProperty("nocamp") then
|
||||
-- this zone cannot be part of a camp
|
||||
|
||||
elseif aZone:hasProperty("cloner") then
|
||||
-- this is a clone zone and part of my camp
|
||||
table.insert(cloners, aZone)
|
||||
if not aZone:hasProperty("blueOnly") then
|
||||
table.insert(redCloners, aZone)
|
||||
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
|
||||
if not aZone:hasProperty("blueOnly") then table.insert(redCloners, aZone) 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
|
||||
if #cloners < 1 then
|
||||
@ -96,9 +83,7 @@ function camp.createCampWithZone(theZone)
|
||||
theZone.upgradeCost = theZone:getNumberFromZoneProperty("upgradeCost", 3 * theZone.repairCost)
|
||||
if theZone:hasProperty("FARP") then
|
||||
theZone.isAlsoFARP = true
|
||||
if theZone.verbose or camp.verbose then
|
||||
trigger.action.outText("+++camp: <" .. theZone.name .. "> has FARP attached", 30)
|
||||
end
|
||||
if theZone.verbose or camp.verbose then trigger.action.outText("+++camp: <" .. theZone.name .. "> has FARP attached", 30) end
|
||||
end
|
||||
end
|
||||
|
||||
@ -107,22 +92,18 @@ end
|
||||
--
|
||||
function camp.update()
|
||||
-- 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
|
||||
|
||||
function camp:onEvent(theEvent)
|
||||
if not theEvent then return end
|
||||
if not theEvent.initiator then return end
|
||||
local theUnit = theEvent.initiator
|
||||
-- if not theUnit.getName then return end
|
||||
-- if not theUnit.getPlayerName then return end
|
||||
if not cfxMX.isDynamicPlayer(theUnit) then return end
|
||||
local id = theEvent.id
|
||||
if id == 15 then -- birth
|
||||
camp.lateProcessPlayer(theUnit)
|
||||
if camp.verbose then
|
||||
trigger.action.outText("camp: late player processing for <" .. theUnit:getName() .. ">", 30)
|
||||
end
|
||||
if camp.verbose then trigger.action.outText("camp: late player processing for <" .. theUnit:getName() .. ">", 30) end
|
||||
end
|
||||
end
|
||||
|
||||
@ -153,12 +134,6 @@ function camp.processPlayers()
|
||||
for idx, gData in pairs(cfxMX.playerGroupByName) do
|
||||
gID = gData.groupId
|
||||
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)
|
||||
end
|
||||
end
|
||||
@ -199,40 +174,22 @@ function camp.doTFunds(args)
|
||||
|
||||
if theZone.repairable and theZone.upgradable then
|
||||
msg = msg .. " (§" .. theZone.repairCost .. "/§" .. theZone.upgradeCost .. ")"
|
||||
if camp.zoneNeedsRepairs(theZone, coa) then
|
||||
msg = msg .. " requests repairs and"
|
||||
else
|
||||
msg = msg .. " is running and"
|
||||
end
|
||||
if camp.zoneNeedsRepairs(theZone, coa) then msg = msg .. " requests repairs and" else msg = msg .. " is running and" end
|
||||
|
||||
if camp.zoneNeedsUpgrades(theZone, coa) then
|
||||
msg = msg .. " can be upgraded"
|
||||
else
|
||||
msg = msg .. " is fully upgraded"
|
||||
end
|
||||
if camp.zoneNeedsUpgrades(theZone, coa) then msg = msg .. " can be upgraded" else msg = msg .. " is fully upgraded" end
|
||||
|
||||
elseif theZone.repairable then
|
||||
if camp.zoneNeedsRepairs(theZone, coa) then
|
||||
msg = msg .. " needs repairs (§" .. theZone.repairCost .. ")"
|
||||
else
|
||||
msg = msg .. " is fully operational"
|
||||
end
|
||||
if camp.zoneNeedsRepairs(theZone, coa) then msg = msg .. " needs repairs (§" .. theZone.repairCost .. ")" else msg = msg .. " is fully operational" end
|
||||
|
||||
elseif theZone.upgradable then
|
||||
if camp.zoneNeedsUpgrades(theZone, coa) then
|
||||
msg = msg .. " can be upgraded (§" .. theZone.upgradeCost .. ")"
|
||||
else
|
||||
msg = msg .. " is fully upgraded"
|
||||
end
|
||||
if camp.zoneNeedsUpgrades(theZone, coa) then msg = msg .. " can be upgraded (§" .. theZone.upgradeCost .. ")" else msg = msg .. " is fully upgraded" end
|
||||
else
|
||||
-- can be neither repaired nor upgraded
|
||||
msg = msg .. " is owned"
|
||||
end
|
||||
end
|
||||
end
|
||||
if income > 0 then
|
||||
msg = msg .. "\n\nTotal Income: §" .. income
|
||||
end
|
||||
if income > 0 then msg = msg .. "\n\nTotal Income: §" .. income end
|
||||
msg = msg .. "\n"
|
||||
trigger.action.outTextForGroup(gID, msg, 30)
|
||||
trigger.action.outSoundForGroup(gID, camp.actionSound)
|
||||
@ -262,13 +219,8 @@ function camp.doFunds(args)
|
||||
return
|
||||
end
|
||||
|
||||
if camp.zoneNeedsRepairs(theZone, coa) then
|
||||
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"
|
||||
else
|
||||
-- say nothing
|
||||
end
|
||||
if camp.zoneNeedsRepairs(theZone, coa) then 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
|
||||
if camp.zoneNeedsUpgrades(theZone, coa) then
|
||||
msg = msg .. "\nZone <" .. theZone.name .. "> can be upgraded (§" .. theZone.upgradeCost .. " per upgrade)\n"
|
||||
elseif theZone.upgradable then
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxZones = {}
|
||||
cfxZones.version = "4.5.1"
|
||||
cfxZones.version = "4.5.2"
|
||||
|
||||
-- cf/x zone management module
|
||||
-- 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
|
||||
- getSmokeColorNumberFromZoneProperty()
|
||||
-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
|
||||
-- circular zone
|
||||
newZone.isCircle = true
|
||||
newZone.radius = dcsZone.radius
|
||||
newZone.radius = tonumber(dcsZone.radius)
|
||||
newZone.maxRadius = newZone.radius -- same for circular
|
||||
|
||||
elseif zoneType == 2 then
|
||||
-- polyZone
|
||||
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
|
||||
-- convex polys, and DML only correctly works with convex polys
|
||||
-- now transfer all point in the poly
|
||||
@ -460,24 +461,6 @@ function cfxZones.createRandomPointInPopulatedZone(theZone, radius, maxTries)
|
||||
return p, dx, dz
|
||||
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
|
||||
table.insert(theCollector, theObject)
|
||||
return true
|
||||
@ -1635,7 +1618,6 @@ function cfxZones.doPollFlag(theFlag, method, theZone) -- no OOP equivalent
|
||||
end
|
||||
|
||||
function cfxZones.pollFlag(theFlag, method, theZone)
|
||||
--trigger.action.outText("enter pollflag for flag <" .. theFlag .. "> of zone <" .. theZone.name .. ">", 30)
|
||||
local allFlags = {}
|
||||
if dcsCommon.containsString(theFlag, ",") then
|
||||
if cfxZones.verbose then
|
||||
@ -2157,6 +2139,7 @@ function cfxZones.getAllZoneProperties(theZone, caseInsensitive, numbersOnly) --
|
||||
local theKey = "dummy"
|
||||
if string.len(theProp.key) > 0 then theKey = theProp.key end
|
||||
if caseInsensitive then theKey = theKey:upper() end
|
||||
theKey = dcsCommon.trim(theKey)
|
||||
local v = theProp.value
|
||||
if numbersOnly then
|
||||
v = tonumber(v)
|
||||
@ -2171,6 +2154,29 @@ function dmlZone:getAllZoneProperties(caseInsensitive, numbersOnly)
|
||||
return cfxZones.getAllZoneProperties(self, caseInsensitive, numbersOnly)
|
||||
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)
|
||||
-- trim
|
||||
theKey = dcsCommon.trim(theKey)
|
||||
@ -2815,26 +2821,6 @@ end
|
||||
|
||||
function dmlZone:getSmokeColorStringFromZoneProperty(theProperty, default) -- smoke as 'red', 'green', or 1..5
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
--
|
||||
@ -2937,36 +2902,7 @@ end
|
||||
function cfxZones.processSimpleZoneDynamics(inMsg, theZone, timeFormat, imperialUnits)
|
||||
local p = theZone:getPoint()
|
||||
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
|
||||
|
||||
-- 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.version = "4.0.0"
|
||||
guardianAngel.version = "4.0.1"
|
||||
guardianAngel.ups = 10 -- hard-coded!! missile track
|
||||
guardianAngel.name = "Guardian Angel" -- just in case someone accesses .name
|
||||
guardianAngel.requiredLibs = {
|
||||
@ -23,6 +23,7 @@ guardianAngel.requiredLibs = {
|
||||
- once per second sanctuary calc
|
||||
- expanded getWatchedUnitByName to include inSanctuary
|
||||
- sanctuary zones use coalition
|
||||
4.0.1 - code hardening (DCS)
|
||||
--]]--
|
||||
|
||||
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
|
||||
-- group, or if it's really a unit, which returns 0 for aircraft
|
||||
if not theUnit.getCategory then return end
|
||||
if not theUnit.getGroup then return end -- ED silly stuff
|
||||
local theGroup = theUnit:getGroup()
|
||||
if not theGroup then return end
|
||||
local gCat = theGroup:getCategory()
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxHeloTroops = {}
|
||||
cfxHeloTroops.version = "4.2.1"
|
||||
cfxHeloTroops.version = "4.2.2"
|
||||
cfxHeloTroops.verbose = false
|
||||
cfxHeloTroops.autoDrop = true
|
||||
cfxHeloTroops.autoPickup = false
|
||||
@ -21,6 +21,7 @@ cfxHeloTroops.requestRange = 500 -- meters
|
||||
- support for drivable
|
||||
4.2.1 - increased verbosity
|
||||
- also supports 'pickupRang" for reverse-compatibility with manual typo.
|
||||
4.2.2 - support for attachTo:
|
||||
|
||||
--]]--
|
||||
cfxHeloTroops.minTime = 3 -- seconds beween tandings
|
||||
@ -299,7 +300,11 @@ function cfxHeloTroops.setCommsMenu(theUnit)
|
||||
conf.unit = theUnit -- link back
|
||||
-- if we don't have an F-10 menu, create one
|
||||
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
|
||||
-- clear out existing commands, add new
|
||||
cfxHeloTroops.clearCommsSubmenus(conf)
|
||||
@ -1021,6 +1026,20 @@ function cfxHeloTroops.readConfigZone()
|
||||
tc = dcsCommon.splitString(tc, ",")
|
||||
cfxHeloTroops.troopCarriers = dcsCommon.trimArray(tc)
|
||||
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
|
||||
|
||||
--
|
||||
|
||||
@ -1,9 +1,14 @@
|
||||
inferno = {}
|
||||
inferno.version = "1.0.0"
|
||||
inferno.version = "1.0.1"
|
||||
inferno.requiredLibs = {
|
||||
"dcsCommon",
|
||||
"cfxZones",
|
||||
}
|
||||
|
||||
--[[-- Version History
|
||||
1.0.0 - Initial version
|
||||
1.0.1 - cleanup
|
||||
--]]--
|
||||
--
|
||||
-- Inferno models fires inside inferno zones. Fires can spread and
|
||||
-- be extinguished by aircraft from the airtank module
|
||||
@ -91,19 +96,9 @@ function inferno.buildGrid(theZone)
|
||||
local lp = {x=xc, y=zc}
|
||||
local yc = land.getHeight(lp)
|
||||
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.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)
|
||||
if theZone.freeBorder then
|
||||
if x == 1 or x == numX then ele.inside = false end
|
||||
@ -120,15 +115,6 @@ function inferno.buildGrid(theZone)
|
||||
until land.getSurfaceType(lp) == 1
|
||||
ele.fxpos = {x=xf, y=yc, z=zf}
|
||||
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}
|
||||
table.insert(goodCells, sparkable)
|
||||
fCount = fCount + 1
|
||||
@ -180,7 +166,7 @@ function inferno.readZone(theZone)
|
||||
theZone.freeBorder = theZone:getBoolFromZoneProperty("freeBorder", true) -- ring zone with non-burning zones
|
||||
theZone.eternal = theZone:getBoolFromZoneProperty("eternal", true)
|
||||
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.maxSpread = theZone:getNumberFromZoneProperty("maxSpread", 999999)
|
||||
theZone.impactSmoke = theZone:getBoolFromZoneProperty("impactSmoke", inferno.impactSmoke)
|
||||
@ -201,7 +187,6 @@ function inferno.readZone(theZone)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- API for water droppers
|
||||
--
|
||||
@ -234,7 +219,6 @@ function inferno.waterInZone(theZone, p, amount)
|
||||
end
|
||||
local ele = row[zc]
|
||||
|
||||
-- local ele = theZone.grid[xc][zc]
|
||||
if not ele then
|
||||
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)
|
||||
@ -264,8 +248,6 @@ function inferno.waterInZone(theZone, p, amount)
|
||||
xc = xc + ofx
|
||||
zc = zc + ofz
|
||||
ele = theZone.grid[xc][zc]
|
||||
-- else
|
||||
-- trigger.action.outText("inferno dropper: NO ALIGNMENT, beds are burning.", 30)
|
||||
end
|
||||
if theZone.impactSmoke then
|
||||
if inferno.verbose then
|
||||
@ -332,16 +314,13 @@ function inferno.waterDropped(p, amount, data) -- if returns non-nil, has hit a
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
--
|
||||
-- IGNITE & DOUSE
|
||||
--
|
||||
function inferno.sparkCell(theZone, x, z)
|
||||
local ele = theZone.grid[x][z]
|
||||
if not ele.inside then
|
||||
if theZone.verbose then
|
||||
trigger.action.outText("ele x<" .. x .. ">z<" .. z .. "> is outside, no spark!", 30)
|
||||
end
|
||||
if theZone.verbose then trigger.action.outText("ele x<" .. x .. ">z<" .. z .. "> is outside, no spark!", 30) end
|
||||
return
|
||||
false end
|
||||
ele.fxname = dcsCommon.uuid(theZone.name)
|
||||
@ -383,16 +362,11 @@ function inferno.ignite(theZone)
|
||||
end
|
||||
|
||||
function inferno.startFire(theZone)
|
||||
if theZone.burning then
|
||||
return
|
||||
end
|
||||
if theZone.burning then return end
|
||||
inferno.ignite(theZone)
|
||||
end
|
||||
|
||||
function inferno.douseFire(theZone)
|
||||
-- if not theZone.burning then
|
||||
-- return
|
||||
-- end
|
||||
-- walk the grid, and kill all flames, set all eles
|
||||
-- to end state
|
||||
for x=1, theZone.numX do
|
||||
@ -660,24 +634,7 @@ function inferno.readConfigZone()
|
||||
inferno.ups = theZone:getNumberFromZoneProperty("ups", 1)
|
||||
inferno.fireTick = theZone:getNumberFromZoneProperty("fireTick", 10)
|
||||
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
|
||||
inferno.fireNum = theZone:getStringFromZoneProperty("fire#", "none")
|
||||
end
|
||||
|
||||
@ -175,7 +175,7 @@ function persistence.saveTable(theTable, fileName, shared, append)
|
||||
if not shared then shared = false end
|
||||
|
||||
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")
|
||||
|
||||
if not theString then theString = "" end
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxPlayerScore = {}
|
||||
cfxPlayerScore.version = "5.1.0"
|
||||
cfxPlayerScore.version = "5.2.1"
|
||||
cfxPlayerScore.name = "cfxPlayerScore" -- compatibility with flag bangers
|
||||
cfxPlayerScore.firstSave = true -- to force overwrite
|
||||
--[[-- VERSION HISTORY
|
||||
@ -15,7 +15,10 @@ cfxPlayerScore.firstSave = true -- to force overwrite
|
||||
- improved wildcard support
|
||||
- event 20 for CA also supported
|
||||
- "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
|
||||
to faction now, reverse invocation direction with PlayerScore
|
||||
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
|
||||
-- extend to add more types. It is used by unitType2score to
|
||||
-- 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.delayBetweenLandings = 30 -- seconds to count as separate landings, also set during take-off to prevent janky t/o to count.
|
||||
cfxPlayerScore.aircraft = 50
|
||||
@ -191,8 +195,18 @@ function cfxPlayerScore.cat2BaseScore(inCat)
|
||||
trigger.action.outText("+++scr c2bs: unknown category for lookup: <" .. inCat .. ">, returning 1", 30)
|
||||
return 1
|
||||
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 killSide then killSide = -1 end
|
||||
local inName
|
||||
@ -224,6 +238,7 @@ function cfxPlayerScore.object2score(inVictim, killSide) -- does not have group
|
||||
-- try the type desc
|
||||
local theType = inVictim:getTypeName()
|
||||
if theType then objectScore = cfxPlayerScore.typeScore[theType:upper()] end
|
||||
if not objectScore then objectScore = cfxPlayerScore.wildMatch(theType) end
|
||||
end
|
||||
if type(objectScore) == "string" then objectScore = tonumber(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
|
||||
-- we first try by unit name. This allows individual
|
||||
-- named hi-value targets to have individual scores
|
||||
local uScore
|
||||
if vicName then uScore = cfxPlayerScore.typeScore[vicName:upper()] end
|
||||
local uScore = nil
|
||||
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
|
||||
if (not uScore) then -- and vicGroup then
|
||||
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
|
||||
if not uScore then
|
||||
-- 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
|
||||
if type(uScore) == "string" then uScore = tonumber(uScore) end
|
||||
if not uScore then uScore = 0 end
|
||||
@ -299,7 +323,8 @@ end
|
||||
function cfxPlayerScore.getPlayerScore(playerName)
|
||||
local thePlayerScore = cfxPlayerScore.playerScore[playerName]
|
||||
if not thePlayerScore then
|
||||
thePlayerScore = {}
|
||||
thePlayerScore = cfxPlayerScore.createNewPlayerScore(playerName)
|
||||
--[[-- thePlayerScore = {}
|
||||
thePlayerScore.name = playerName
|
||||
thePlayerScore.score = 0 -- score
|
||||
thePlayerScore.scoreaccu = 0 -- for deferred
|
||||
@ -309,10 +334,42 @@ function cfxPlayerScore.getPlayerScore(playerName)
|
||||
thePlayerScore.featTypes = {} -- dict <featname> <number> of other things player did
|
||||
thePlayerScore.featQueue = {} -- when using deferred
|
||||
thePlayerScore.totalFeats = 0
|
||||
--]]--
|
||||
end
|
||||
return thePlayerScore
|
||||
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)
|
||||
cfxPlayerScore.playerScore[playerName] = thePlayerScore
|
||||
end
|
||||
@ -511,6 +568,7 @@ function cfxPlayerScore.isNamedUnit(theUnit)
|
||||
if not theName then return false end
|
||||
end
|
||||
if cfxPlayerScore.typeScore[theName:upper()] then return true end
|
||||
if cfxPlayerScore.wildMatch(theName) then return true end
|
||||
return false
|
||||
end
|
||||
|
||||
@ -806,9 +864,7 @@ function cfxPlayerScore.scheduledAward(args)
|
||||
local playerName = args[1]
|
||||
local unitName = args[2]
|
||||
local theUnit = Unit.getByName(unitName)
|
||||
if not theUnit or
|
||||
not Unit.isExist(theUnit)
|
||||
then
|
||||
if not theUnit or (not Unit.isExist(theUnit)) then
|
||||
-- unit is gone
|
||||
trigger.action.outText("Player <" .. playerName .. "> lost score.", 30)
|
||||
return
|
||||
@ -890,6 +946,7 @@ function cfxPlayerScore.scheduledAward(args)
|
||||
-- output score
|
||||
desc = desc .. "\n"
|
||||
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
|
||||
|
||||
function cfxPlayerScore.handlePlayerDeath(theEvent)
|
||||
@ -903,6 +960,9 @@ function cfxPlayerScore.handlePlayerDeath(theEvent)
|
||||
local pName = cfxPlayerScore.unit2player[uName]
|
||||
if pName then
|
||||
-- this was a player name with link still live.
|
||||
-- cancel all scores accumulated
|
||||
cfxPlayerScore.wipeScore(pName)
|
||||
|
||||
if cfxPlayerScore.planeLoss ~= 0 then
|
||||
-- plane loss has IMMEDIATE consequences
|
||||
cfxPlayerScore.updateScoreForPlayerImmediate(pName, cfxPlayerScore.planeLoss)
|
||||
@ -943,10 +1003,10 @@ function cfxPlayerScore.isScoreEvent(theEvent)
|
||||
if who then
|
||||
name = "(inval initi)"
|
||||
if who.getName then name = who:getName() end
|
||||
if not name then -- WTF??? could be a weapon
|
||||
name = "!nil getName!"
|
||||
if not name or (#name < 1) then -- WTF??? could be a weapon
|
||||
name = "!no getName!"
|
||||
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
|
||||
|
||||
@ -963,7 +1023,7 @@ function cfxPlayerScore.isScoreEvent(theEvent)
|
||||
-- a hit event will save the last player to hit
|
||||
-- so we can attribute with unit lost
|
||||
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
|
||||
if not who.getPlayerName then return false end -- non-player originator
|
||||
local pName = who:getPlayerName()
|
||||
@ -981,7 +1041,10 @@ function cfxPlayerScore.isScoreEvent(theEvent)
|
||||
|
||||
-- check if this was FORMERLY a player plane
|
||||
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()
|
||||
if cfxPlayerScore.unit2player[uName] then
|
||||
-- this requires special IMMEDIATE handling when event is
|
||||
@ -1017,6 +1080,7 @@ function cfxPlayerScore.isScoreEvent(theEvent)
|
||||
-- credit unit and current person driving that unit
|
||||
-- there is a small percentage that this is wrong
|
||||
-- player, but that's an edge case
|
||||
-- if no player inhabits killing unit any more, no score attributed
|
||||
cfxPlayerScore.processKill(theUnit, who)
|
||||
end
|
||||
return false
|
||||
@ -1054,8 +1118,11 @@ function cfxPlayerScore.isScoreEvent(theEvent)
|
||||
|
||||
-- enter unit / birth event for players initializes score if
|
||||
-- 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
|
||||
-- since we get into a new plane, erase all pending score
|
||||
local pName = theUnit:getPlayerName()
|
||||
cfxPlayerScore.wipeScore(pName)
|
||||
-- link player with their unit
|
||||
cfxPlayerScore.linkUnitWithPlayer(theUnit)
|
||||
cfxPlayerScore.unitSpawnTime[uName] = timer.getTime() -- to detect 'early landing'
|
||||
@ -1110,6 +1177,7 @@ end
|
||||
|
||||
function cfxPlayerScore.handleScoreEvent(theEvent)
|
||||
cfxPlayerScore.currentEventUnit = theEvent.initiator
|
||||
if cfxPlayerScore.verbose then trigger.action.outText("Set currentEventUnit to <" .. theEvent.initiator:getName() .. ">", 30) end
|
||||
if theEvent.id == 28 then
|
||||
-- kill from player detected.
|
||||
cfxPlayerScore.killDetected(theEvent)
|
||||
@ -1361,12 +1429,30 @@ function cfxPlayerScore.start()
|
||||
if not dcsCommon.libCheck("cfx Player Score", cfxPlayerScore.requiredLibs)
|
||||
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
|
||||
-- identify and process a score table zones
|
||||
local theZone = cfxZones.getZoneByName("playerScoreTable")
|
||||
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
|
||||
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
|
||||
|
||||
-- read score tiggers and values
|
||||
@ -1398,10 +1484,11 @@ function cfxPlayerScore.start()
|
||||
cfxPlayerScore.blueTriggerFlags[tName] = trigger.misc.getUserFlag(tName)
|
||||
end
|
||||
end
|
||||
-- now read my config zone
|
||||
-- now read my config zone. reading late
|
||||
local theZone = cfxZones.getZoneByName("playerScoreConfig")
|
||||
if not theZone then theZone = cfxZones.createSimpleZone("playerScoreConfig") end
|
||||
cfxPlayerScore.readConfigZone(theZone)
|
||||
|
||||
-- read all scoreSafe zones
|
||||
local safeZones = cfxZones.zonesWithProperty("scoreSafe")
|
||||
for k, aZone in pairs(safeZones) do
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
pulseFlags = {}
|
||||
pulseFlags.version = "2.0.2"
|
||||
pulseFlags.version = "2.0.3"
|
||||
pulseFlags.verbose = false
|
||||
pulseFlags.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
@ -15,6 +15,7 @@ pulseFlags.requiredLibs = {
|
||||
using method on all outputs
|
||||
- 2.0.1 activateZoneFlag now works correctly
|
||||
- 2.0.2 fixed scheduledTime bug while persisting
|
||||
- 2.0.3 now setting -1 (infinite) as pulses works correctly
|
||||
--]]--
|
||||
|
||||
pulseFlags.pulses = {}
|
||||
@ -56,14 +57,20 @@ function pulseFlags.createPulseWithZone(theZone)
|
||||
|
||||
theZone.pulses = -1 -- set to infinite
|
||||
if theZone:hasProperty("pulses") then
|
||||
local tt = theZone:getStringFromZoneProperty("pulses", -1)
|
||||
tn = tonumber(tt) -- returns nil for a range
|
||||
if tn then
|
||||
if tn < 1 then theZone.pulses = -1 else theZone.pulses = tn end
|
||||
else
|
||||
local minP, maxP = theZone:getPositiveRangeFromZoneProperty("pulses", 1)
|
||||
if minP == maxP then theZone.pulses = minP
|
||||
else
|
||||
theZone.pulses = cfxZones.randomInRange(minP, maxP)
|
||||
end
|
||||
end
|
||||
if pulseFlags.verbose or theZone.verbose then
|
||||
trigger.action.outText("+++pulF: zone <" .. theZone.name .. "> set to <" .. theZone.pulses .. "> pulses", 30)
|
||||
end
|
||||
if theZone.verbose or pulseFlag.verbose then
|
||||
trigger.action.outText("+++pulF: set pulses in <" .. theZone.name .. "> to <" .. theZone.pulses .. ">", 30)
|
||||
end
|
||||
|
||||
theZone.pulsesLeft = 0 -- will start new cycle
|
||||
|
||||
@ -164,7 +164,8 @@ function cfxSmokeZone.start()
|
||||
timer.scheduleFunction(cfxSmokeZone.checkFlags, {}, timer.getTime() + 1)
|
||||
|
||||
-- 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
|
||||
end
|
||||
|
||||
|
||||
@ -232,7 +232,7 @@ function cfxSpawnZones.getRequestableSpawnersInRange(aPoint, aRange, aSide)
|
||||
-- only return spawners with this side
|
||||
-- note: this will NOT work with neutral players
|
||||
hasMatch = false
|
||||
reasons = reasons .. "[rawOwner] "
|
||||
reasons = reasons .. "[rawOwner is <" .. aSpawner.rawOwner .. ">, need <" .. aSide .. ">] "
|
||||
end
|
||||
|
||||
if not aSpawner.requestable then
|
||||
@ -258,21 +258,14 @@ end
|
||||
--
|
||||
function cfxSpawnZones.verifySpawnOwnership(spawner)
|
||||
-- returns false ONLY if masterSpawn disagrees
|
||||
-- i.e. when masterOwner is not the same as spawner.rawOwner
|
||||
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
|
||||
end -- no master owner, all ok
|
||||
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
|
||||
-- if not masterZone.owner then
|
||||
--trigger.action.outText("spawner " .. spawner.name .. " - masterZone " .. masterZone.name .. " HAS NO OWNER????", 30)
|
||||
-- return true
|
||||
-- end
|
||||
if spawner.zone.verbose then trigger.action.outText("spawner " .. spawner.name .. " has masterOwner <" .. masterZone.name .. ">", 30) end
|
||||
|
||||
if (myCoalition ~= masterZone:getCoalition()) then
|
||||
-- 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