DML/modules/aswGUI.lua
Christian Franz 5251773f6f Version 1.2.4
Minor updates
2023-03-02 08:41:03 +01:00

586 lines
18 KiB
Lua

aswGUI = {}
aswGUI.version = "1.0.1"
aswGUI.verbose = false
aswGUI.requiredLibs = {
"dcsCommon", -- always
"cfxZones", -- Zones, of course
"asw", -- needs asw module
"aswZones", -- also needs the asw zones
}
--[[--
Version History
1.0.0 - initial version
1.0.1 - env.info clean-up, verbosity clean-up
--]]--
aswGUI.ups = 1 -- = once every second
aswGUI.aswCraft = {}
--[[--
::::::::::::::::: ASSUMES SINGLE-UNIT GROUPS ::::::::::::::::::
--]]--
function aswGUI.resetConf(asc)
if asc.rootMenu then
missionCommands.removeItemForGroup(asc.groupID, asc.rootMenu)
end
asc.rootMenu = missionCommands.addSubMenuForGroup(asc.groupID, "ASW")
asc.buoyNum = 0
asc.torpedoNum = 0
asc.coolDown = 0 -- used when waiting, currently not used
end
-- we use lazy init whenever player enters
function aswGUI.initUnit(unitName) -- now this unit exists
local theUnit = Unit.getByName(unitName)
if not theUnit then
trigger.action.outText("+++aswGUI: <" .. unitName .. "> not a unit, aborting initUnit", 30)
return nil
end
local theGroup = theUnit:getGroup()
local asc = {} -- set up player craft config block
asc.groupName = theGroup:getName() -- groupData.name
asc.name = unitName
asc.groupID = theGroup:getID() -- groupData.groupId
aswGUI.resetConf(asc)
return asc
end
function aswGUI.processWeightFor(conf)
-- make total weight and handle all
-- cargo for this unit
-- hand off to DML cargo manager if implemented
if cargosuper then
trigger.action.outText("CargoSuper handling regquired, using none", 30)
return
end
local totalWeight = conf.buoyNum * aswGUI.buoyWeight
totalWeight = totalWeight + conf.torpedoNum * aswGUI.torpedoWeight
-- set cargo weight
trigger.action.setUnitInternalCargo(conf.name, totalWeight)
local theUnit = Unit.getByName(conf.name)
trigger.action.outTextForGroup(conf.groupID, "Total asw weight: " .. totalWeight .. "kg (" .. math.floor(totalWeight * 2.20462) .. "lbs)", 30)
return totalWeight
end
--
-- build unit menu
--
function aswGUI.getBuoyCapa(conf) -- returns capa per slot
-- warning: assumes two "slots" maximum
if conf.torpedoNum > aswGUI.torpedoesPerSlot then return 0 end -- both slots are filled with torpedoes
if conf.torpedoNum > 0 then -- one slot is taken up by torpedoes
return aswGUI.buoysPerSlot - conf.buoyNum
end
if conf.buoyNum >= aswGUI.buoysPerSlot then
return 2 * aswGUI.buoysPerSlot - conf.buoyNum
end
return aswGUI.buoysPerSlot - conf.buoyNum
end
function aswGUI.getTorpedoCapa(conf)
if conf.buoyNum > aswGUI.buoysPerSlot then return 0 end -- both slots are filled with buoys
if conf.buoyNum > 0 then -- one slot is taken up by torpedoes
return aswGUI.torpedoesPerSlot - conf.torpedoNum
end
if conf.torpedoNum >= aswGUI.torpedoesPerSlot then
return 2 * aswGUI.torpedoesPerSlot - conf.torpedoNum
end
return aswGUI.torpedoesPerSlot - conf.torpedoNum
end
function aswGUI.setGroundMenu(conf, theUnit)
-- build menu for load stores
local loc = theUnit:getPoint()
local closestAswZone = aswZones.getClosestASWZoneTo(loc)
local inZone = cfxZones.pointInZone(loc, closestAswZone)
local bStore = 0 -- available buoys
local tStore = 0 -- available torpedoes
-- ... but only if we are in an asw zone
-- calculate how much is available
if inZone then
bStore = closestAswZone.buoyNum
if bStore < 0 then bStore = aswGUI.buoysPerSlot end
tStore = closestAswZone.torpedoNum
if tStore < 0 then tStore = aswGUI.torpedoesPerSlot end
end
if bStore > 0 then
local bCapa = aswGUI.getBuoyCapa(conf)
if bCapa > 0 then
missionCommands.addCommandForGroup(conf.groupID, "Load <" .. bCapa .."> ASW Buoys", conf.rootMenu, aswGUI.xHandleLoadBuoys, conf)
else
missionCommands.addCommandForGroup(conf.groupID, "(No free Buoy stores)", conf.rootMenu, aswGUI.xHandleGeneric, conf)
end
else
missionCommands.addCommandForGroup(conf.groupID, "(Can't load ASW Buoys, no supplies in range)", conf.rootMenu, aswGUI.xHandleGeneric, conf)
end
if conf.buoyNum > 0 then
local toUnload = conf.buoyNum
if toUnload > aswGUI.buoysPerSlot then toUnload = aswGUI.buoysPerSlot end
missionCommands.addCommandForGroup(conf.groupID, "Unload <" .. toUnload .. "> ASW Buoys (" .. conf.buoyNum .. " on board)", conf.rootMenu, aswGUI.xHandleUnloadBuoys, conf)
end
-- torpedo proccing
if tStore > 0 then
local tCapa = aswGUI.getTorpedoCapa(conf)
if tCapa > 0 then
tCapa = 1 -- one at a time
missionCommands.addCommandForGroup(conf.groupID, "Load <" .. tCapa .."> ASW Torpedoes", conf.rootMenu, aswGUI.xHandleLoadTorpedoes, conf)
else
missionCommands.addCommandForGroup(conf.groupID, "All stores filled to capacity", conf.rootMenu, aswGUI.xHandleGeneric, conf)
end
else
missionCommands.addCommandForGroup(conf.groupID, "(Can't load ASW Torpedoes, no supplies in range)", conf.rootMenu, aswGUI.xHandleGeneric, conf)
end
if conf.torpedoNum > 0 then
local toUnload = conf.torpedoNum
if toUnload > aswGUI.torpedoesPerSlot then toUnload = aswGUI.buoysPerSlot end
missionCommands.addCommandForGroup(conf.groupID, "Unload <" .. toUnload .. "> ASW Torpedoes (" .. conf.torpedoNum .. " on board)", conf.rootMenu, aswGUI.xHandleUnloadTorpedoes, conf)
end
missionCommands.addCommandForGroup(conf.groupID, "[Stores: <" .. conf.buoyNum .. "> Buoys | <" .. conf.torpedoNum .. "> Torpedoes]", conf.rootMenu, aswGUI.xHandleGeneric, conf)
end
function aswGUI.setAirMenu(conf, theUnit)
-- build menu for load stores
local bStore = conf.buoyNum -- available buoys
local tStore = conf.torpedoNum -- available torpedoes
if bStore < 1 and tStore < 1 then
missionCommands.addCommandForGroup(conf.groupID, "No ASW munitions on board", conf.rootMenu, aswGUI.xHandleGeneric, conf)
return
end
if bStore > 0 then
missionCommands.addCommandForGroup(conf.groupID, "BUOY - Drop an ASW Buoy", conf.rootMenu, aswGUI.xHandleBuoyDropoff, conf)
else
missionCommands.addCommandForGroup(conf.groupID, "No ASW Buoys on board", conf.rootMenu, aswGUI.xHandleGeneric, conf)
end
if tStore > 0 then
missionCommands.addCommandForGroup(conf.groupID, "TORP - Drop an ASW Torpedo", conf.rootMenu, aswGUI.xHandleTorpedoDropoff, conf)
else
missionCommands.addCommandForGroup(conf.groupID, "No ASW Torpedoes on board", conf.rootMenu, aswGUI.xHandleGeneric, conf)
end
missionCommands.addCommandForGroup(conf.groupID, "[Stores: <" .. conf.buoyNum .. "> Buoys | <" .. conf.torpedoNum .. "> Torpedoes]", conf.rootMenu, aswGUI.xHandleGeneric, conf)
end
function aswGUI.setMenuForUnit(theUnit)
if not theUnit then return end
if not Unit.isExist(theUnit) then return end
local uName = theUnit:getName()
-- if we get here, the unit exists. fetch unit config
local conf = aswGUI.aswCraft[uName]
-- delete old, and create new root menu
missionCommands.removeItemForGroup(conf.groupID, conf.rootMenu)
conf.rootMenu = missionCommands.addSubMenuForGroup(conf.groupID, "ASW")
-- if we are in the air, we add menus to drop buoys or torpedoes
if theUnit:inAir() then
aswGUI.setAirMenu(conf, theUnit)
else
aswGUI.setGroundMenu(conf, theUnit)
end
end
--
-- comms callback handling
--
--
-- LOADING / UNLOADING
--
function aswGUI.xHandleGeneric(args)
timer.scheduleFunction(aswGUI.handleGeneric, args, timer.getTime() + 0.1)
end
function aswGUI.handleGeneric(args)
if not args then args = "*EMPTY*" end
-- do nothing
end
function aswGUI.xHandleLoadBuoys(args)
timer.scheduleFunction(aswGUI.handleLoadBuoys, args, timer.getTime() + 0.1)
end
function aswGUI.handleLoadBuoys(args)
local conf = args
local theUnit = Unit.getByName(conf.name)
if not theUnit then
trigger.action.outText("+++aswG: (load buoys) can't find unit <" .. conf.name .. ">", 30)
return
end
local loc = theUnit:getPoint()
local theZone = aswZones.getClosestASWZoneTo(loc)
local inZone = cfxZones.pointInZone(loc, theZone)
local bStore = 0 -- available buoys
if inZone then
bStore = theZone.buoyNum
if bStore < 0 then bStore = aswGUI.buoysPerSlot end
else
trigger.action.outTextForGroup(conf.groupID, "Nothing loaded. Return to ASW loading zone.", 30)
aswGUI.setMenuForUnit(theUnit)
return
end
if bStore < 1 then
trigger.action.outTextForGroup(conf.groupID, "ASW Buoy stock has run out. Sorry.", 30)
aswGUI.setMenuForUnit(theUnit)
return
end
local capa = aswGUI.getBuoyCapa(conf)
conf.buoyNum=conf.buoyNum + capa
if theZone.buoyNum >= 0 then
theZone.buoyNum = theZone.buoyNum - capa
if theZone.buoyNum < 0 then theZone.buoyNum = 0 end
-- proc new weight
end
aswGUI.processWeightFor(conf)
trigger.action.outTextForGroup(conf.groupID, "Loaded <" .. capa .. "> ASW Buoys.", 30)
aswGUI.setMenuForUnit(theUnit)
end
function aswGUI.xHandleUnloadBuoys(args)
timer.scheduleFunction(aswGUI.handleUnloadBuoys, args, timer.getTime() + 0.1)
end
function aswGUI.handleUnloadBuoys(args)
local conf = args
local theUnit = Unit.getByName(conf.name)
if not theUnit then
trigger.action.outText("+++aswG: (unload buoys) can't find unit <" .. conf.name .. ">", 30)
return
end
local loc = theUnit:getPoint()
local theZone = aswZones.getClosestASWZoneTo(loc)
local inZone = cfxZones.pointInZone(loc, theZone)
local amount = conf.buoyNum
while amount > aswGUI.buoysPerSlot do -- future proof, any # of slots
amount = amount - aswGUI.buoysPerSlot
end
conf.buoyNum = conf.buoyNum - amount
if inZone then
if theZone.buoyNum >= 0 then theZone.buoyNum = theZone.buoyNum + amount end
trigger.action.outTextForGroup(conf.groupID, "Returned <" .. amount .. "> ASW Buoys to storage.", 30)
else
-- simply drop them, irrecoverable
trigger.action.outTextForGroup(conf.groupID, "Discarded <" .. amount .. "> ASW Buoys.", 30)
end
aswGUI.processWeightFor(conf)
aswGUI.setMenuForUnit(theUnit)
end
function aswGUI.xHandleLoadTorpedoes(args)
timer.scheduleFunction(aswGUI.handleLoadTorpedoes, args, timer.getTime() + 0.1)
end
function aswGUI.handleLoadTorpedoes(args)
local conf = args
local theUnit = Unit.getByName(conf.name)
if not theUnit then
trigger.action.outText("+++aswG: (load torps) can't find unit <" .. conf.name .. ">", 30)
return
end
local loc = theUnit:getPoint()
local theZone = aswZones.getClosestASWZoneTo(loc)
local inZone = cfxZones.pointInZone(loc, theZone)
local tStore = 0 -- available torpedoes
if inZone then
tStore = theZone.torpedoNum
if tStore < 0 then tStore = aswGUI.torpedoesPerSlot end
else
trigger.action.outTextForGroup(conf.groupID, "Nothing loaded. Return to ASW loading zone.", 30)
aswGUI.setMenuForUnit(theUnit)
return
end
if tStore < 1 then
trigger.action.outTextForGroup(conf.groupID, "ASW Torpedo stock has run out. Sorry.", 30)
aswGUI.setMenuForUnit(theUnit)
return
end
local capa = aswGUI.getTorpedoCapa(conf)
capa = 1 -- load one at a time
conf.torpedoNum=conf.torpedoNum + capa
if theZone.torpedoNum >= 0 then
theZone.torpedoNum = theZone.torpedoNum - capa
if theZone.torpedoNum < 0 then theZone.torpedoNum = 0 end
end
aswGUI.processWeightFor(conf)
trigger.action.outTextForGroup(conf.groupID, "Loaded <" .. capa .. "> asw Torpedoes.", 30)
aswGUI.setMenuForUnit(theUnit)
end
function aswGUI.xHandleUnloadTorpedoes(args)
timer.scheduleFunction(aswGUI.handleUnloadTorpedoes, args, timer.getTime() + 0.1)
end
function aswGUI.handleUnloadTorpedoes(args)
local conf = args
local theUnit = Unit.getByName(conf.name)
if not theUnit then
trigger.action.outText("+++aswG: (unload torpedoes) can't find unit <" .. conf.name .. ">", 30)
return
end
local loc = theUnit:getPoint()
local theZone = aswZones.getClosestASWZoneTo(loc)
local inZone = cfxZones.pointInZone(loc, theZone)
local amount = conf.torpedoNum
while amount > aswGUI.torpedoesPerSlot do -- future proof, any # of slots
amount = amount - aswGUI.torpedoesPerSlot
end
conf.torpedoNum = conf.torpedoNum - amount
if inZone then
if theZone.torpedoNum >= 0 then theZone.torpedoNum = theZone.torpedoNum + amount end
trigger.action.outTextForGroup(conf.groupID, "Returned <" .. amount .. "> ASW Torpedoes to storage.", 30)
else
-- simply drop them, irrecoverable
trigger.action.outTextForGroup(conf.groupID, "Discarded <" .. amount .. "> ASW Torpedoes.", 30)
end
aswGUI.processWeightFor(conf)
aswGUI.setMenuForUnit(theUnit)
end
--
-- LIVE DROP
--
function aswGUI.xHandleBuoyDropoff(args)
timer.scheduleFunction(aswGUI.handleBuoyDropoff, args, timer.getTime() + 0.1)
end
function aswGUI.hasDropoffParams(conf)
-- to be added later, can be curtailed for units
return true
end
function aswGUI.handleBuoyDropoff(args)
local conf = args
local theUnit = Unit.getByName(conf.name)
if not theUnit or not Unit.isExist(theUnit) then
trigger.action.outText("+++aswG: (drop buoy) unit <" .. conf.name .. "> does not exits", 30)
return
end
-- we could now make height and speed checks, but dont really do
if not aswGUI.hasDropoffParams(conf) then
trigger.action.outTextForGroup(conf.groupID, "You need to be below xxx knots and yyy ft AGL to drop ASW munitions", 30)
return
end
-- check that we really have some buoys left
if conf.buoyNum < 1 then
trigger.action.outText("+++aswG: no buoys for <" .. conf.name .. ">.", 30)
return
end
conf.buoyNum = conf.buoyNum - 1
-- do the deed
asw.dropBuoyFrom(theUnit)
trigger.action.outTextForGroup(conf.groupID, "Dropping ASW Buoy...", 30)
-- wrap up
aswGUI.processWeightFor(conf)
aswGUI.setMenuForUnit(theUnit)
end
function aswGUI.xHandleTorpedoDropoff(args)
timer.scheduleFunction(aswGUI.handleTorpedoDropoff, args, timer.getTime() + 0.1)
end
function aswGUI.handleTorpedoDropoff(args)
local conf = args
local theUnit = Unit.getByName(conf.name)
if not theUnit or not Unit.isExist(theUnit) then
trigger.action.outText("+++aswG: (drop torpedo) unit <" .. conf.name .. "> does not exits", 30)
return
end
-- we could now make height and speed checks, but dont really do
if not aswGUI.hasDropoffParams(conf) then
trigger.action.outTextForGroup(conf.groupID, "You need to be below xxx knots and yyy ft AGL to drop ASW munitions", 30)
return
end
-- check that we really have some buoys left
if conf.torpedoNum < 1 then
trigger.action.outText("+++aswG: no torpedoes for <" .. conf.name .. ">.", 30)
return
end
conf.torpedoNum = conf.torpedoNum - 1
-- do the deed
asw.dropTorpedoFrom(theUnit)
trigger.action.outTextForGroup(conf.groupID, "Dropping ASW Torpedo...", 30)
-- wrap up
aswGUI.processWeightFor(conf)
aswGUI.setMenuForUnit(theUnit)
end
--
-- Event handling
--
function aswGUI:onEvent(theEvent)
if not theEvent then
trigger.action.outText("+++aswGUI: nil theEvent", 30)
return
end
local theID = theEvent.id
if not theID then
trigger.action.outText("+++aswGUI: nil event.ID", 30)
return
end
local initiator = theEvent.initiator
if not initiator then
return
end -- not interested
local theUnit = initiator
if not Unit.isExist(theUnit) then
if aswGUI.verbose then
trigger.action.outText("+++aswGUI: non-unit event filtered.", 30)
end
return
end
local name = theUnit:getName()
if not name then
trigger.action.outText("+++aswGUI: unable to access unit name in onEvent, aborting", 30)
return
end
-- see if this is a player aircraft
if not theUnit.getPlayerName then
return
end -- not a player
if not theUnit:getPlayerName() then
return
end -- not a player
-- this is a player unit. Is it ASW carrier?
local uType = theUnit:getTypeName()
if not dcsCommon.isTroopCarrierType(uType, aswGUI.aswCarriers) then
if aswGUI.verbose then
trigger.action.outText("+++aswGUI: Player <" .. theUnit:getPlayerName() .. ">'s unit <" .. name .. "> of type <" .. uType .. "> is not ASW-capable. ASW Types are:", 30)
for idx, aType in pairs(aswGUI.aswCarriers) do
trigger.action.outText(aType,30)
end
end
return
end
-- now let's access it if it was
-- used before
local conf = aswGUI.aswCraft[name]
if not conf then
-- let's init it
conf = aswGUI.initUnit(name)
if not conf then
-- something went wrong, abort
return
end
aswGUI.aswCraft[name] = conf
end
-- if we get here, theUnit is an asw craft
if theID == 4 or -- land
theID == 3 then -- take off
aswGUI.setMenuForUnit(theUnit)
return
end
if theID == 20 or -- player enter
theID == 15 then -- birth (server player enter)
-- reset
aswGUI.resetConf(conf)
-- set menus
aswGUI.setMenuForUnit(theUnit)
end
if theID == 21 then -- player leave
aswGUI.resetConf(conf)
end
end
--
-- Config & start
--
function aswGUI.readConfigZone()
local theZone = cfxZones.getZoneByName("aswGUIConfig")
if not theZone then
if aswGUI.verbose then
trigger.action.outText("+++aswGUI: no config zone!", 30)
end
theZone = cfxZones.createSimpleZone("aswGUIConfig")
end
aswGUI.verbose = theZone.verbose
-- read & set defaults
if cfxZones.hasProperty(theZone, "aswCarriers") then
local carr = cfxZones.getStringFromZoneProperty(theZone, "aswCarriers", "")
carr = dcsCommon.splitString(carr, ",")
aswGUI.aswCarriers = dcsCommon.trimArray(carr)
end
aswGUI.buoysPerSlot = 10
aswGUI.torpedoesPerSlot = 2
aswGUI.buoyWeight = 50 -- kg, 10x = 500, 20x = 1000
aswGUI.buoyWeight = cfxZones.getNumberFromZoneProperty(theZone, "buoyWeight", aswGUI.buoyWeight)
aswGUI.torpedoWeight = 700 -- kg
aswGUI.torpedoWeight = cfxZones.getNumberFromZoneProperty(theZone, "torpedoWeight", aswGUI.torpedoWeight)
if aswGUI.verbose then
trigger.action.outText("+++aswGUI: read config", 30)
end
end
function aswGUI.start()
if not dcsCommon.libCheck then
trigger.action.outText("cfx aswGUI requires dcsCommon", 30)
return false
end
if not dcsCommon.libCheck("cfx aswGUI", aswGUI.requiredLibs) then
return false
end
-- read config
aswGUI.readConfigZone()
-- subscribe to world events
world.addEventHandler(aswGUI)
-- say Hi
trigger.action.outText("cfx ASW GUI v" .. aswGUI.version .. " started.", 30)
return true
end
--
-- start up aswZones
--
if not aswGUI.start() then
trigger.action.outText("cfx aswGUI aborted: missing libraries", 30)
aswGUI = nil
end