------------------------------------------------------------------ -- VEAF CAS (Close Air Support) command and functions for DCS World -- By zip (2018) -- -- Features: -- --------- -- * Listen to marker change events and creates a CAS training mission, with optional parameters -- * Create a CAS target group, protected by SAM, AAA and manpads, to use for CAS training -- -- See the documentation : https://veaf.github.io/documentation/ ------------------------------------------------------------------ veafCasMission = {} ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Global settings. Stores the script constants ------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Identifier. All output in DCS.log will start with this. veafCasMission.Id = "CASMISSION" --- Version. veafCasMission.Version = "1.15.2" -- trace level, specific to this module --veafCasMission.LogLevel = "trace" veaf.loggers.new(veafCasMission.Id, veafCasMission.LogLevel) --- Key phrase to look for in the mark text which triggers the command. veafCasMission.Keyphrase = "_cas" --- Number of seconds between each check of the CAS group watchdog function veafCasMission.SecondsBetweenWatchdogChecks = 15 --- Number of seconds between each smoke request on the CAS targets group veafCasMission.SecondsBetweenSmokeRequests = 180 --- Number of seconds between each flare request on the CAS targets group veafCasMission.SecondsBetweenFlareRequests = 120 --- Name of the CAS targets vehicles group veafCasMission.RedCasGroupName = "Red CAS Group" veafCasMission.BlueCasGroupName = "Blue CAS Group" veafCasMission.casGroupName = veafCasMission.RedCasGroupName veafCasMission.afacName = nil veafCasMission.RadioMenuName = "CAS MISSION" veafCasMission.TRANSPORT_TYPES = { [coalition.side.BLUE] = { [veaf.ERA.MODERN] = { [0] = {"LUV HMMWV Jeep", "M 818", "M978 HEMTT Tanker", "Land_Rover_101_FC", "Land_Rover_109_S3"}, [1] = {"LUV HMMWV Jeep", "M 818", "M978 HEMTT Tanker", "Land_Rover_101_FC", "Land_Rover_109_S3"}, [2] = {"LUV HMMWV Jeep", "M 818", "M978 HEMTT Tanker", "Land_Rover_101_FC", "Land_Rover_109_S3"}, [3] = {"LUV HMMWV Jeep", "M 818", "M978 HEMTT Tanker", "Land_Rover_101_FC", "Land_Rover_109_S3"}, [4] = {"LUV HMMWV Jeep", "M 818", "M978 HEMTT Tanker", "Land_Rover_101_FC", "Land_Rover_109_S3"}, [5] = {"LUV HMMWV Jeep", "M 818", "M978 HEMTT Tanker", "Land_Rover_101_FC", "Land_Rover_109_S3"}, }, [veaf.ERA.COLD_WAR] = { [0] = {"Truck Land Rover 101 FC", "LUV Land Rover 109", "Truck M939 Heavy"}, [1] = {"Truck Land Rover 101 FC", "LUV Land Rover 109", "Truck M939 Heavy"}, [2] = {"Truck Land Rover 101 FC", "LUV Land Rover 109", "Truck M939 Heavy"}, [3] = {"Truck Land Rover 101 FC", "LUV Land Rover 109", "Truck M939 Heavy"}, [4] = {"Truck Land Rover 101 FC", "LUV Land Rover 109", "Truck M939 Heavy"}, [5] = {"Truck Land Rover 101 FC", "LUV Land Rover 109", "Truck M939 Heavy"}, }, [veaf.ERA.WW2] = { [0] = {"Bedford_MWD", "CCKW_353", "Willys_MB"}, [1] = {"Bedford_MWD", "CCKW_353", "Willys_MB"}, [2] = {"Bedford_MWD", "CCKW_353", "Willys_MB"}, [3] = {"Bedford_MWD", "CCKW_353", "Willys_MB"}, [4] = {"Bedford_MWD", "CCKW_353", "Willys_MB"}, [5] = {"Bedford_MWD", "CCKW_353", "Willys_MB"}, } }, [coalition.side.RED] = { [veaf.ERA.MODERN] = { [0]= {"ATZ-60_Maz", "ZIL-135", "ATZ-5", "Ural-4320 APA-5D", "SKP-11", "GAZ-66", "KAMAZ Truck", "Ural-375", "KrAZ6322", "ZIL-131 KUNG", "Tigr_233036", "UAZ-469"}, [1]= {"ATZ-60_Maz", "ZIL-135", "ATZ-5", "Ural-4320 APA-5D", "SKP-11", "GAZ-66", "KAMAZ Truck", "Ural-375", "KrAZ6322", "ZIL-131 KUNG", "Tigr_233036", "UAZ-469"}, [2]= {"ATZ-60_Maz", "ZIL-135", "ATZ-5", "Ural-4320 APA-5D", "SKP-11", "GAZ-66", "KAMAZ Truck", "Ural-375", "KrAZ6322", "ZIL-131 KUNG", "Tigr_233036", "UAZ-469"}, [3]= {"ATZ-60_Maz", "ZIL-135", "ATZ-5", "Ural-4320 APA-5D", "SKP-11", "GAZ-66", "KAMAZ Truck", "Ural-375", "KrAZ6322", "ZIL-131 KUNG", "Tigr_233036", "UAZ-469"}, [4]= {"ATZ-60_Maz", "ZIL-135", "ATZ-5", "Ural-4320 APA-5D", "SKP-11", "GAZ-66", "KAMAZ Truck", "Ural-375", "KrAZ6322", "ZIL-131 KUNG", "Tigr_233036", "UAZ-469"}, [5]= {"ATZ-60_Maz", "ZIL-135", "ATZ-5", "Ural-4320 APA-5D", "SKP-11", "GAZ-66", "KAMAZ Truck", "Ural-375", "KrAZ6322", "ZIL-131 KUNG", "Tigr_233036", "UAZ-469"}, }, [veaf.ERA.COLD_WAR] = { [0]= {"LUV UAZ-469 Jeep", "Refueler ATMZ-5", "Refueler ATZ-10", "Refueler ATZ-5", "S-75 Tractor (ZIL-131)", "Truck GAZ-66", "Truck KAMAZ 43101", "Truck Ural-4320", "Truck Ural-4320-31 Arm'd", "Truck Ural-4320T", "Truck ZIL-131 (C2)", "Truck ZIL-135"}, [1]= {"LUV UAZ-469 Jeep", "Refueler ATMZ-5", "Refueler ATZ-10", "Refueler ATZ-5", "S-75 Tractor (ZIL-131)", "Truck GAZ-66", "Truck KAMAZ 43101", "Truck Ural-4320", "Truck Ural-4320-31 Arm'd", "Truck Ural-4320T", "Truck ZIL-131 (C2)", "Truck ZIL-135"}, [2]= {"LUV UAZ-469 Jeep", "Refueler ATMZ-5", "Refueler ATZ-10", "Refueler ATZ-5", "S-75 Tractor (ZIL-131)", "Truck GAZ-66", "Truck KAMAZ 43101", "Truck Ural-4320", "Truck Ural-4320-31 Arm'd", "Truck Ural-4320T", "Truck ZIL-131 (C2)", "Truck ZIL-135"}, [3]= {"LUV UAZ-469 Jeep", "Refueler ATMZ-5", "Refueler ATZ-10", "Refueler ATZ-5", "S-75 Tractor (ZIL-131)", "Truck GAZ-66", "Truck KAMAZ 43101", "Truck Ural-4320", "Truck Ural-4320-31 Arm'd", "Truck Ural-4320T", "Truck ZIL-131 (C2)", "Truck ZIL-135"}, [4]= {"LUV UAZ-469 Jeep", "Refueler ATMZ-5", "Refueler ATZ-10", "Refueler ATZ-5", "S-75 Tractor (ZIL-131)", "Truck GAZ-66", "Truck KAMAZ 43101", "Truck Ural-4320", "Truck Ural-4320-31 Arm'd", "Truck Ural-4320T", "Truck ZIL-131 (C2)", "Truck ZIL-135"}, [5]= {"LUV UAZ-469 Jeep", "Refueler ATMZ-5", "Refueler ATZ-10", "Refueler ATZ-5", "S-75 Tractor (ZIL-131)", "Truck GAZ-66", "Truck KAMAZ 43101", "Truck Ural-4320", "Truck Ural-4320-31 Arm'd", "Truck Ural-4320T", "Truck ZIL-131 (C2)", "Truck ZIL-135"}, }, [veaf.ERA.WW2] = { [0] = {"Blitz_36-6700A", "Horch_901_typ_40_kfz_21", "Kubelwagen_82", "Sd_Kfz_7", "Sd_Kfz_2" }, [1] = {"Blitz_36-6700A", "Horch_901_typ_40_kfz_21", "Kubelwagen_82", "Sd_Kfz_7", "Sd_Kfz_2" }, [2] = {"Blitz_36-6700A", "Horch_901_typ_40_kfz_21", "Kubelwagen_82", "Sd_Kfz_7", "Sd_Kfz_2" }, [3] = {"Blitz_36-6700A", "Horch_901_typ_40_kfz_21", "Kubelwagen_82", "Sd_Kfz_7", "Sd_Kfz_2" }, [4] = {"Blitz_36-6700A", "Horch_901_typ_40_kfz_21", "Kubelwagen_82", "Sd_Kfz_7", "Sd_Kfz_2" }, [5] = {"Blitz_36-6700A", "Horch_901_typ_40_kfz_21", "Kubelwagen_82", "Sd_Kfz_7", "Sd_Kfz_2" }, } } } veafCasMission.ARMOR_TYPES = { [coalition.side.BLUE] = { [veaf.ERA.MODERN] = { [0] = {}, [1] = {"IFV Marder", "MCV-80", "IFV LAV-25", "M1134 Stryker ATGM", "M-2 Bradley"}, [2] = {"IFV Marder", "MCV-80", "IFV LAV-25", "M1134 Stryker ATGM", "M-2 Bradley"}, [3] = {"IFV Marder", "VAB_Mephisto", "M-2 Bradley", "MBT Leopard 1A3", "Chieftain_mk3"}, [4] = {"M-2 Bradley", "MBT Leopard 1A3", "Merkava_Mk4", "M1128 Stryker MGS"}, [5] = {"Merkava_Mk4", "Challenger2", "Leclerc", "Leopard-2", "M-1 Abrams"}, }, [veaf.ERA.COLD_WAR] = { [0] = {}, [1] = {"APC M113", "APC TPz Fuchs", "APC AAV-7 Amphibious"}, [2] = {"APC M113", "APC TPz Fuchs", "APC AAV-7 Amphibious", "IFV Marder"}, [3] = {"APC M113", "APC TPz Fuchs", "APC AAV-7 Amphibious", "IFV Marder", "MBT M60A3 Patton"}, [4] = {"APC AAV-7 Amphibious", "IFV Marder", "MBT M60A3 Patton", "MBT Leopard 1A3", "MBT Chieftain Mk.3"}, [5] = {"APC AAV-7 Amphibious", "IFV Marder", "MBT M60A3 Patton", "MBT Leopard 1A3", "MBT Chieftain Mk.3"}, }, [veaf.ERA.WW2] = { [0] = {}, [1] = {"M30_CC", "M10_GMC"}, [2] = {"M30_CC", "M10_GMC"}, [3] = {"M30_CC", "M10_GMC", "Centaur_IV"}, [4] = {"Centaur_IV", "Churchill_VII", "Cromwell_IV"}, [5] = {"Centaur_IV", "Churchill_VII", "Cromwell_IV", "M4_Sherman", "M4A4_Sherman_FF"} } }, [coalition.side.RED] = { [veaf.ERA.MODERN] = { [0] = {}, [1] = {"BTR-82A", "BMP-1", "VAB_Mephisto"}, [2] = {"BTR-82A", "BMP-1", "VAB_Mephisto", "BMP-2"}, [3] = {"BTR-82A", "VAB_Mephisto", "BMP-2", "T-55", "Chieftain_mk3"}, [4] = {"BTR-82A", "BMP-3", "Chieftain_mk3", "T-72B"}, [5] = {"BMP-3", "ZTZ96B", "T-72B3", "T-80UD", "T-90"} }, [veaf.ERA.COLD_WAR] = { [0] = {}, [1] = {"Scout BRDM-2", "APC MTLB"}, [2] = {"Scout BRDM-2", "APC MTLB", "IFV BMD-1", "IFV BMP-1"}, [3] = {"Scout BRDM-2", "APC MTLB", "IFV BMD-1", "IFV BMP-1", "APC BTR-RD"}, [4] = {"APC MTLB", "IFV BMD-1", "IFV BMP-1", "APC BTR-RD", "LT PT-76"}, [5] = {"IFV BMD-1", "IFV BMP-1", "APC BTR-RD", "LT PT-76", "MBT T-55"}, }, [veaf.ERA.WW2] = { [0] = {}, [1] = {"Sd_Kfz_251", "Sd_Kfz_234_2_Puma"}, [2] = {"Sd_Kfz_251", "Sd_Kfz_234_2_Puma"}, [3] = {"Sd_Kfz_251", "Sd_Kfz_234_2_Puma", "Elefant_SdKfz_184"}, [4] = {"Pz_IV_H", "Tiger_I", "Tiger_II_H", "Stug_III", "Stug_IV"}, [5] = {"Pz_IV_H", "Tiger_I", "Tiger_II_H", "Stug_III", "Stug_IV", "JagdPz_IV", "Jagdpanther_G1", "Pz_V_Panther_G"} } } } veafCasMission.INFANTRY_TYPES = { [coalition.side.BLUE] = { [veaf.ERA.MODERN] = {"Soldier RPG", "Soldier M249", "Soldier M4 GRG"}, [veaf.ERA.COLD_WAR] = {"Soldier RPG", "Soldier M249", "Soldier M4 GRG"}, [veaf.ERA.WW2] = {"Soldier RPG", "Soldier M249", "Soldier M4 GRG"}, }, [coalition.side.RED] = { [veaf.ERA.MODERN] = {"Paratrooper RPG-16", "Infantry AK ver3", "Infantry AK ver2"}, [veaf.ERA.COLD_WAR] = {"Paratrooper RPG-16", "Infantry AK ver3", "Infantry AK ver2"}, [veaf.ERA.WW2] = {"Paratrooper RPG-16", "Infantry AK ver3", "Infantry AK ver2"}, } } veafCasMission.INFANTRY_IFV_TYPES = { [coalition.side.BLUE] = { [veaf.ERA.MODERN] = { [0] = {"Land_Rover_101_FC", "Land_Rover_109_S3"}, [1] = {"IFV Marder"}, [2] = {"IFV Marder"}, [3] = {"M-2 Bradley"}, [4] = {"M-2 Bradley"}, [5] = {"M-2 Bradley"}, }, [veaf.ERA.COLD_WAR] = { [0] = {"Truck M939 Heavy"}, [1] = {"APC M113", "APC TPz Fuchs", "APC AAV-7 Amphibious"}, [2] = {"APC M113", "APC TPz Fuchs", "APC AAV-7 Amphibious", "IFV Marder"}, [3] = {"APC M113", "APC TPz Fuchs", "APC AAV-7 Amphibious", "IFV Marder"}, [4] = {"APC AAV-7 Amphibious", "IFV Marder"}, [5] = {"APC AAV-7 Amphibious", "IFV Marder"}, }, [veaf.ERA.WW2] = { [0] = {"Bedford_MWD"}, [1] = {"APC M2A1 Halftrack"}, [2] = {"APC M2A1 Halftrack"}, [3] = {"M-2 Bradley"}, [4] = {"M-2 Bradley"}, [5] = {"M-2 Bradley"}, }, }, [coalition.side.RED] = { [veaf.ERA.MODERN] = { [0] = {"Ural-4320 APA-5D", "GAZ-66", "KAMAZ Truck"}, [1] = {"BMP-1"}, [2] = {"BMP-1"}, [3] = {"BMP-2"}, [4] = {"BMP-2"}, [5] = {"BMP-2"}, }, [veaf.ERA.COLD_WAR] = { [0] = {"Truck KAMAZ 43101", "Truck ZIL-135", "Truck Ural-4320-31 Arm'd"}, [1] = {"APC MTLB"}, [2] = {"APC MTLB", "APC BTR-RD"}, [3] = {"APC MTLB", "APC BTR-RD", "IFV BMD-1"}, [4] = {"APC MTLB", "APC BTR-RD", "IFV BMD-1", "IFV BMP-1"}, [5] = {"APC BTR-RD", "IFV BMD-1", "IFV BMP-1"}, }, [veaf.ERA.WW2] = { [0] = {"Blitz_36-6700A", "Horch_901_typ_40_kfz_21", "Kubelwagen_82", "Sd_Kfz_7", "Sd_Kfz_2"}, [1] = {"Sd_Kfz_251"}, [2] = {"Sd_Kfz_251"}, [3] = {"Sd_Kfz_234_2_Puma"}, [4] = {"Sd_Kfz_234_2_Puma"}, [5] = {"Sd_Kfz_234_2_Puma"}, } } } ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Do not change anything below unless you know what you are doing! ------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Radio menus paths veafCasMission.targetMarkersPath = nil veafCasMission.targetInfoPath = nil veafCasMission.rootPath = nil -- CAS Group watchdog function id veafCasMission.groupAliveCheckTaskID = 'none' -- Smoke reset function id veafCasMission.smokeResetTaskID = 'none' -- Flare reset function id veafCasMission.flareResetTaskID = 'none' veafCasMission.SIDE_RED = coalition.side.RED veafCasMission.SIDE_BLUE = coalition.side.BLUE ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Utility methods ------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Event handler functions. ------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Function executed when a mark has changed. This happens when text is entered or changed. function veafCasMission.onEventMarkChange(eventPos, event) veaf.loggers.get(veafCasMission.Id):trace(string.format("event = %s", veaf.p(event))) -- choose by default the coalition opposing the player who triggered the event local invertedCoalition = 1 if event.coalition == 1 then invertedCoalition = 2 end veaf.loggers.get(veafCasMission.Id):trace(string.format("event.idx = %s", veaf.p(event.idx))) if veafCasMission.executeCommand(eventPos, event.text, invertedCoalition, event.idx) then -- Delete old mark. veaf.loggers.get(veafCasMission.Id):trace(string.format("Removing mark # %d.", event.idx)) trigger.action.removeMark(event.idx) end end function veafCasMission.executeCommand(eventPos, eventText, coalition, markId, bypassSecurity) veaf.loggers.get(veafCasMission.Id):debug(string.format("veafCasMission.executeCommand(eventText=[%s])", eventText)) veaf.loggers.get(veafCasMission.Id):trace(string.format("coalition=%s", veaf.p(coalition))) veaf.loggers.get(veafCasMission.Id):trace(string.format("markId=%s", veaf.p(markId))) veaf.loggers.get(veafCasMission.Id):trace(string.format("bypassSecurity=%s", veaf.p(bypassSecurity))) -- Check if marker has a text and the veafCasMission.keyphrase keyphrase. if eventText ~= nil and eventText:lower():find(veafCasMission.Keyphrase) then -- Analyse the mark point text and extract the keywords. local options = veafCasMission.markTextAnalysis(eventText) if options then -- Check options commands if options.casmission then if not (bypassSecurity or veafSecurity.checkSecurity_L9(options.password, markId)) then return end if not options.side then if options.country then -- deduct the side from the country options.side = veaf.getCoalitionForCountry(options.country, true) else options.side = coalition end end if not options.country then -- deduct the country from the side options.country = veaf.getCountryForCoalition(options.side) end -- create the group veafCasMission.generateCasMission(eventPos, options.size, options.defense, options.armor, options.spacing, options.disperseOnAttack, options.side) return true end end end return false end ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Analyse the mark text and extract keywords. ------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Extract keywords from mark text. function veafCasMission.markTextAnalysis(text) -- Option parameters extracted from the mark text. local switch = {} switch.casmission = false -- size ; ranges from 1 to 5, 5 being the biggest. switch.size = 1 -- defenses force ; ranges from 1 to 5, 5 being the toughest. switch.defense = 1 -- armor force ; ranges from 1 to 5, 5 being the strongest and most modern. switch.armor = 1 -- spacing ; ranges from 1 to 5, 1 being the default and 5 being the widest spacing. switch.spacing = 1 -- disperse on attack ; self explanatory, if keyword is present the option will be set to true switch.disperseOnAttack = false -- password switch.password = nil -- coalition switch.side = nil -- Check for correct keywords. if text:lower():find(veafCasMission.Keyphrase) then switch.casmission = true else return nil end -- keywords are split by "," local keywords = veaf.split(text, ",") for _, keyphrase in pairs(keywords) do -- Split keyphrase by space. First one is the key and second, ... the parameter(s) until the next comma. local str = veaf.breakString(veaf.trim(keyphrase), " ") local key = str[1] local val = str[2] if key:lower() == "password" then -- Unlock the command veaf.loggers.get(veafCasMission.Id):debug(string.format("Keyword password", val)) switch.password = val end if switch.casmission and key:lower() == "size" then -- Set size. veaf.loggers.get(veafCasMission.Id):debug(string.format("Keyword size = %d", val)) local nVal = tonumber(val) if nVal <= 5 and nVal >= 1 then switch.size = nVal end end if switch.casmission and key:lower() == "defense" then -- Set defense. veaf.loggers.get(veafCasMission.Id):debug(string.format("Keyword defense = %d", val)) local nVal = tonumber(val) if nVal <= 5 and nVal >= 0 then switch.defense = nVal end end if switch.casmission and key:lower() == "armor" then -- Set armor. veaf.loggers.get(veafCasMission.Id):debug(string.format("Keyword armor = %d", val)) local nVal = tonumber(val) if nVal <= 5 and nVal >= 0 then switch.armor = nVal end end if switch.casmission and key:lower() == "spacing" then -- Set spacing. veaf.loggers.get(veafCasMission.Id):debug(string.format("Keyword spacing = %d", val)) local nVal = tonumber(val) if nVal <= 5 and nVal >= 1 then switch.spacing = nVal end end if key:lower() == "side" then -- Set side veaf.loggers.get(veafCasMission.Id):trace(string.format("Keyword side = %s", val)) if val:upper() == "BLUE" then switch.side = veafCasMission.SIDE_BLUE else switch.side = veafCasMission.SIDE_RED end end if switch.casmission and key:lower() == "disperse" then -- Set disperse on attack. veaf.loggers.get(veafCasMission.Id):debug("Keyword disperse = %s", val) if val ~= "" then local nVal = tonumber(val) if nVal then switch.disperseOnAttack = nVal end else switch.disperseOnAttack = 15 end end end return switch end ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- CAS target group generation and management ------------------------------------------------------------------------------------------------------------------------------------------------------------- local function _addDefenseForGroups(group, side, defense, multiple, forInfantry) veaf.loggers.get(veafCasMission.Id):trace(string.format("_addDefenseForGroups(defense=[%s], side=[%s], multiple=[%s], forInfantry=[%s])", veaf.p(defense), veaf.p(side), veaf.p(multiple), veaf.p(forInfantry))) local _actualDefense = defense if defense > 0 then -- roll a dice : 20% chance to get a -1 (lower) difficulty, 30% chance to get a +1 (higher) difficulty, and 50% to get what was asked for local _dice = math.random(100) veaf.loggers.get(veafCasMission.Id):trace("_dice = " .. _dice) if _dice <= 20 then _actualDefense = defense - 1 elseif _dice > 80 then _actualDefense = defense + 1 end end if _actualDefense > 5 then _actualDefense = 6 end if _actualDefense < 0 then _actualDefense = 0 end veaf.loggers.get(veafCasMission.Id):trace("_actualDefense = " .. _actualDefense) for _ = 1, multiple do if _actualDefense > 5 then if side == veafCasMission.SIDE_BLUE then if forInfantry then -- only spawn manpads for _ = 1, math.random(1,_actualDefense-2) do table.insert(group.units, { "Stinger comm", random=true }) table.insert(group.units, { "Soldier stinger", random=true }) end else table.insert(group.units, { "M1097 Avenger", random=true }) table.insert(group.units, { "Roland ADS", random=true }) table.insert(group.units, { "Gepard", random=true }) end else if forInfantry then -- only spawn manpads for _ = 1, math.random(1,_actualDefense-2) do -- for _actualDefense = 4-5, spawn a modern Igla-S team table.insert(group.units, { "SA-18 Igla-S comm", random=true }) table.insert(group.units, { "SA-18 Igla-S manpad", random=true }) end else table.insert(group.units, { veaf.randomlyChooseFrom({"2S6 Tunguska", "Tor 9A331", "Tor 9A331"}), random=true }) table.insert(group.units, { "Strela-10M3", random=true }) table.insert(group.units, { "ZSU-23-4 Shilka", random=true }) end end elseif _actualDefense == 5 then if side == veafCasMission.SIDE_BLUE then if forInfantry then -- only spawn manpads for _ = 1, math.random(1,_actualDefense-2) do table.insert(group.units, { "Stinger comm", random=true }) table.insert(group.units, { "Soldier stinger", random=true }) end else table.insert(group.units, { veaf.randomlyChooseFrom({"Gepard", "M1097 Avenger", "M1097 Avenger"}), random=true }) table.insert(group.units, { "Roland ADS", random=true }) end else if forInfantry then -- only spawn manpads for _ = 1, math.random(1,_actualDefense-2) do -- for _actualDefense = 4-5, spawn a modern Igla-S team table.insert(group.units, { "SA-18 Igla-S comm", random=true }) table.insert(group.units, { "SA-18 Igla-S manpad", random=true }) end else table.insert(group.units, { veaf.randomlyChooseFrom({"Osa 9A33 ln", "2S6 Tunguska"}), random=true }) table.insert(group.units, { veaf.randomlyChooseFrom({"ZSU-23-4 Shilka", "Strela-10M3"}), random=true }) end end elseif _actualDefense == 4 then if side == veafCasMission.SIDE_BLUE then if forInfantry then -- only spawn manpads for _ = 1, math.random(1,_actualDefense-2) do table.insert(group.units, { "Stinger comm", random=true }) table.insert(group.units, { "Soldier stinger", random=true }) end else table.insert(group.units, { "Gepard", random=true }) table.insert(group.units, { "Roland ADS", random=true }) end else if forInfantry then -- only spawn manpads for _ = 1, math.random(1,_actualDefense-2) do -- for _actualDefense = 4-5, spawn a modern Igla-S team table.insert(group.units, { "SA-18 Igla-S comm", random=true }) table.insert(group.units, { "SA-18 Igla-S manpad", random=true }) end else table.insert(group.units, { veaf.randomlyChooseFrom({"ZSU-23-4 Shilka", "ZSU-23-4 Shilka", "ZSU_57_2"}), random=true }) table.insert(group.units, { veaf.randomlyChooseFrom({"HQ-7_LN_EO", "HQ-7_LN_SP"}), random=true }) end end elseif _actualDefense == 3 then if side == veafCasMission.SIDE_BLUE then if forInfantry then -- only spawn manpads for _ = 1, math.random(1,_actualDefense-2) do table.insert(group.units, { "Stinger comm", random=true }) table.insert(group.units, { "Soldier stinger", random=true }) end else table.insert(group.units, { veaf.randomlyChooseFrom({"M48 Chaparral", "M6 Linebacker"}), random=true }) table.insert(group.units, { "Gepard", random=true }) end else if forInfantry then -- only spawn manpads for _ = 1, math.random(1,_actualDefense-2) do -- for _actualDefense = 3, spawn an older Igla team table.insert(group.units, { "SA-18 Igla comm", random=true }) table.insert(group.units, { "SA-18 Igla manpad", random=true }) end else table.insert(group.units, { veaf.randomlyChooseFrom({"Strela-1 9P31", "Strela-10M3"}), random=true }) table.insert(group.units, { veaf.randomlyChooseFrom({"ZSU-23-4 Shilka", "ZSU-23-4 Shilka", "ZSU_57_2"}), random=true }) end end elseif _actualDefense == 2 then if side == veafCasMission.SIDE_BLUE then table.insert(group.units, { "Gepard", random=true }) table.insert(group.units, { "Vulcan", random=true }) else table.insert(group.units, { veaf.randomlyChooseFrom({"ZSU-23-4 Shilka", "ZSU_57_2"}), random=true }) table.insert(group.units, { veaf.randomlyChooseFrom({"ZSU-23-4 Shilka", "ZSU_57_2"}), random=true }) end elseif _actualDefense == 1 then if side == veafCasMission.SIDE_BLUE then table.insert(group.units, { "Vulcan", random=true }) else table.insert(group.units, { veaf.randomlyChooseFrom({"Ural-375 ZU-23", "ZSU_57_2"}), random=true }) end end end --veaf.loggers.get(veafCasMission.Id):trace(string.format("group.units=%s", veaf.p(group.units))) end --- TODO/feat-era/ Generates an air defense group function veafCasMission.generateAirDefenseGroup(groupName, defense, side) side = side or veafCasMission.SIDE_RED -- generate a primary air defense platoon local _actualDefense = defense if defense > 0 then -- roll a dice : 20% chance to get a -1 (lower) difficulty, 30% chance to get a +1 (higher) difficulty, and 50% to get what was asked for local _dice = math.random(100) veaf.loggers.get(veafCasMission.Id):trace("_dice = " .. _dice) if _dice <= 20 then _actualDefense = defense - 1 elseif _dice > 80 then _actualDefense = defense + 1 end end if _actualDefense > 5 then _actualDefense = 5 end if _actualDefense < 0 then _actualDefense = 0 end veaf.loggers.get(veafCasMission.Id):trace("_actualDefense = " .. _actualDefense) local _groupDefinition = "generateAirDefenseGroup-BLUE-" if side == veafCasMission.SIDE_RED then _groupDefinition = "generateAirDefenseGroup-RED-" end _groupDefinition = _groupDefinition .. tostring(_actualDefense) veaf.loggers.get(veafCasMission.Id):trace("_groupDefinition = " .. _groupDefinition) local group = veafUnits.findGroup(_groupDefinition) if not group then veaf.loggers.get(veafCasMission.Id):error(string.format("veafCasMission.generateAirDefenseGroup cannot find group [%s]", _groupDefinition or "")) end group.description = groupName group.groupName = groupName veaf.loggers.get(veafCasMission.Id):trace("#group.units = " .. #group.units) return group end --- Generates a transport company and its air defenses function veafCasMission.generateTransportCompany(groupName, defense, side, size) veaf.loggers.get(veafCasMission.Id):trace(string.format("veafCasMission.generateTransportCompany(groupName=[%s], defense=[%s], side=[%s], size=[%s])", groupName or "", defense or "", side or "", size or "")) side = side or veafCasMission.SIDE_RED local groupCount = math.floor((size or math.random(10, 15))) veaf.loggers.get(veafCasMission.Id):trace(string.format("groupCount=%s", tostring(groupCount))) local group = { disposition = { h = groupCount, w = groupCount}, units = {}, description = groupName, groupName = groupName, } -- generate a transport company local chooseFrom = veafCasMission.TRANSPORT_TYPES[side][veaf.config.era][0] veaf.loggers.get(veafCasMission.Id):trace("chooseFrom=%s", chooseFrom) for _ = 1, groupCount do local transportType = veaf.randomlyChooseFrom(chooseFrom) table.insert(group.units, { transportType, random=true}) end -- TODO/feat-era/ add an air defense vehicle every 10 vehicles local nbDefense = groupCount / 10 + 1 if nbDefense == 0 then nbDefense = 1 end veaf.loggers.get(veafCasMission.Id):debug("nbDefense = " .. nbDefense) if not veaf.config.ww2 then _addDefenseForGroups(group, side, defense, nbDefense) else -- nothing, there are no mobile defense units in WW2 end return group end --- Generates an armor platoon and its air defenses function veafCasMission.generateArmorPlatoon(groupName, defense, armor, side, size) veaf.loggers.get(veafCasMission.Id):trace(string.format("veafCasMission.generateArmorPlatoon(groupName=[%s], defense=[%s], armor=[%s], side=[%s], size=[%s])", groupName or "", defense or "", armor or "", side or "", size or "")) side = side or veafCasMission.SIDE_RED -- generate an armor platoon local groupCount = math.floor((size or math.random(3, 6)) * (math.random(8, 12)/10)) veaf.loggers.get(veafCasMission.Id):trace(string.format("groupCount=%s", tostring(groupCount))) local group = { disposition = { h = groupCount, w = groupCount}, units = {}, description = groupName, groupName = groupName, } if group.disposition.h < 4 then group.disposition.h = 4 group.disposition.w = 4 end local armorBias = 0 if armor < 0 then armor = 0 end if armor > 5 then armorBias = armor - 5 armor = 5 end local chooseFrom = veafCasMission.ARMOR_TYPES[side][veaf.config.era][armor] veaf.loggers.get(veafCasMission.Id):trace("chooseFrom=%s", chooseFrom) for _ = 1, groupCount do local armorType = veaf.randomlyChooseFrom(chooseFrom, armorBias) if armorType then table.insert(group.units, { armorType, random=true }) end end -- TODO/feat-era/ add air defense vehicles if not veaf.config.ww2 then _addDefenseForGroups(group, side, defense, 1) else -- nothing, there are no mobile defense units in WW2 end return group end --- Generates an infantry group along with its manpad units and tranport vehicles function veafCasMission.generateInfantryGroup(groupName, defense, armor, side, size) side = side or veafCasMission.SIDE_RED veaf.loggers.get(veafCasMission.Id):trace(string.format("veafCasMission.generateInfantryGroup(groupName=%s, defense=%d, armor=%d)",groupName, defense, armor)) -- generate an infantry group local groupCount = math.floor((size or math.random(3, 6)) * (math.random(8, 12)/10)) veaf.loggers.get(veafCasMission.Id):trace(string.format("groupCount=%s", tostring(groupCount))) local group = { disposition = { h = groupCount, w = groupCount}, units = {}, description = groupName, groupName = groupName, } if group.disposition.h < 4 then group.disposition.h = 4 group.disposition.w = 4 end local chooseFrom = veafCasMission.INFANTRY_TYPES[side][veaf.config.era] veaf.loggers.get(veafCasMission.Id):trace("chooseFrom=%s", chooseFrom) for _ = 1, groupCount do local unitType = veaf.randomlyChooseFrom(chooseFrom) table.insert(group.units, { unitType }) end -- add a transport vehicle or an APC/IFV depending on the side and the era chooseFrom = veafCasMission.INFANTRY_IFV_TYPES[side][veaf.config.era][armor] veaf.loggers.get(veafCasMission.Id):trace("chooseFrom=%s", chooseFrom) local unitType = veaf.randomlyChooseFrom(chooseFrom) table.insert(group.units, { unitType, cell=11, random=true }) -- TODO/feat-era/ add air defense if not veaf.config.ww2 then _addDefenseForGroups(group, side, defense, 1, true) else -- nothing, there are no mobile defense units in WW2 end return group end function veafCasMission.placeGroup(groupDefinition, spawnPosition, spacing, resultTable, hasDest) if spawnPosition ~= nil and groupDefinition ~= nil then veaf.loggers.get(veafCasMission.Id):trace(string.format("veafCasMission.placeGroup(#groupDefinition.units=%d)",#groupDefinition.units)) -- process the group veaf.loggers.get(veafCasMission.Id):trace("process the group") local group = veafUnits.processGroup(groupDefinition) -- place its units local groupPosition = { x = spawnPosition.x, z = spawnPosition.y } local hdg = math.random(359) local group, cells = veafUnits.placeGroup(group, veaf.placePointOnLand(groupPosition), spacing+3, hdg, hasDest) if veaf.Trace then veafUnits.traceGroup(group, cells) end -- add the units to the result units list if not resultTable then resultTable = {} end for _,u in pairs(group.units) do table.insert(resultTable, u) end end veaf.loggers.get(veafCasMission.Id):trace(string.format("#resultTable=%d",#resultTable)) return resultTable end --- Generates a complete CAS target group function veafCasMission.generateCasGroup(casGroupName, spawnSpot, size, defense, armor, spacing, side) veaf.loggers.get(veafCasMission.Id):trace("side = " .. tostring(side)) side = side or veafCasMission.SIDE_RED local units = {} local zoneRadius = (size+spacing)*350 veaf.loggers.get(veafCasMission.Id):trace("zoneRadius = " .. zoneRadius) -- generate between size-2 and size+1 infantry groups local infantryGroupsCount = math.random(math.max(1, size-2), size + 1) veaf.loggers.get(veafCasMission.Id):trace("infantryGroupsCount = " .. infantryGroupsCount) for infantryGroupNumber = 1, infantryGroupsCount do local groupName = casGroupName .. " - Infantry Section " .. infantryGroupNumber local groupPosition = veaf.findPointInZone(spawnSpot, zoneRadius, false) veaf.loggers.get(veafCasMission.Id):trace(string.format("infantry group #%s position : %s", veaf.p(infantryGroupNumber), veaf.p(groupPosition))) local group = veafCasMission.generateInfantryGroup(groupName, defense, armor, side) veafCasMission.placeGroup(group, groupPosition, spacing, units) end if armor > 0 then -- generate between size-2 and size+1 armor platoons local armorPlatoonsCount = math.random(math.max(1, size-2), size + 1) veaf.loggers.get(veafCasMission.Id):trace("armorPlatoonsCount = " .. armorPlatoonsCount) for armorGroupNumber = 1, armorPlatoonsCount do local groupName = casGroupName .. " - Armor Platoon " .. armorGroupNumber local groupPosition = veaf.findPointInZone(spawnSpot, zoneRadius, false) veaf.loggers.get(veafCasMission.Id):trace(string.format("armor group #%s position : %s", veaf.p(armorGroupNumber), veaf.p(groupPosition))) local group = veafCasMission.generateArmorPlatoon(groupName, defense, armor, side) veafCasMission.placeGroup(group, groupPosition, spacing, units) end end if defense > 0 then -- generate between 1 and 2 air defense groups local airDefenseGroupsCount = 1 if defense > 3 then airDefenseGroupsCount = 2 end veaf.loggers.get(veafCasMission.Id):trace("airDefenseGroupsCount = " .. airDefenseGroupsCount) for airDefenseGroupNumber = 1, airDefenseGroupsCount do local groupName = casGroupName .. " - Air Defense Group ".. airDefenseGroupNumber local groupPosition = veaf.findPointInZone(spawnSpot, zoneRadius, false) veaf.loggers.get(veafCasMission.Id):trace(string.format("air defense group #%s position : %s", veaf.p(airDefenseGroupNumber), veaf.p(groupPosition))) local group = veafCasMission.generateAirDefenseGroup(groupName, defense, side) veafCasMission.placeGroup(group, groupPosition, spacing, units) end end -- generate between 1 and size transport companies local transportCompaniesCount = math.random(1, size) veaf.loggers.get(veafCasMission.Id):trace("transportCompaniesCount = " .. transportCompaniesCount) for transportCompanyGroupNumber = 1, transportCompaniesCount do local groupName = casGroupName .. " - Transport Company " .. transportCompanyGroupNumber local groupPosition = veaf.findPointInZone(spawnSpot, zoneRadius, false) veaf.loggers.get(veafCasMission.Id):trace(string.format("transport group #%s position : %s", veaf.p(transportCompanyGroupNumber), veaf.p(groupPosition))) local group = veafCasMission.generateTransportCompany(groupName, defense, side) veafCasMission.placeGroup(group, groupPosition, spacing, units) end return units end --- Generates a CAS mission function veafCasMission.generateCasMission(spawnSpot, size, defense, armor, spacing, disperseOnAttack, side) if veafCasMission.groupAliveCheckTaskID ~= 'none' then trigger.action.outText("A CAS target group already exists !", 15) return end if side == veafCasMission.SIDE_BLUE then veafCasMission.casGroupName = veafCasMission.BlueCasGroupName end local country = veaf.getCountryForCoalition(side) local units = veafCasMission.generateCasGroup(veafCasMission.casGroupName, spawnSpot, size, defense, armor, spacing, side) -- prepare the actual DCS units local dcsUnits = {} for i=1, #units do local unit = units[i] local unitType = unit.typeName local unitName = veafCasMission.casGroupName .. " / " .. unit.displayName .. " #" .. i local unitHdg = unit.hdg local spawnPosition = unit.spawnPoint -- check if position is correct for the unit type if veafUnits.checkPositionForUnit(spawnPosition, unit) then local toInsert = { ["x"] = spawnPosition.x, ["y"] = spawnPosition.z, ["alt"] = spawnPosition.y, ["type"] = unitType, ["name"] = unitName, ["speed"] = 0, ["skill"] = "Random", ["heading"] = unitHdg, } table.insert(dcsUnits, toInsert) end end -- actually spawn groups mist.dynAdd({country = country, category = "GROUND_UNIT", name = veafCasMission.casGroupName, hidden = false, units = dcsUnits}) -- set AI options local controller = Group.getByName(veafCasMission.casGroupName):getController() controller:setOption(9, 2) -- set alarm state to red controller:setOption(AI.Option.Ground.id.DISPERSE_ON_ATTACK, disperseOnAttack) -- set disperse on attack according to the option -- Spawn Reaper local opposing_side = coalition.side.BLUE if coalition.side.RED ~= side then opposing_side = coalition.side.RED end local avgPos = veaf.getAveragePosition(veafCasMission.casGroupName) veafCasMission.afacName = veafSpawn.spawnAFAC(avgPos, "mq9", veaf.getCountryForCoalition(opposing_side), nil, nil, nil, veafSpawn.convertLaserToFreq(1688), "FM", 1688, true, false, false) -- build menu for each player veafRadio.addCommandToSubmenu('Target information', veafCasMission.rootPath, veafCasMission.reportTargetInformation, nil, veafRadio.USAGE_ForGroup) -- add radio menus for commands veafRadio.addSecuredCommandToSubmenu('Skip current objective', veafCasMission.rootPath, veafCasMission.skipCasTarget) veafCasMission.targetMarkersPath = veafRadio.addSubMenu("Target markers", veafCasMission.rootPath) veafRadio.addCommandToSubmenu('Request smoke on target area', veafCasMission.targetMarkersPath, veafCasMission.smokeCasTargetGroup) veafRadio.addCommandToSubmenu('Request illumination flare over target area', veafCasMission.targetMarkersPath, veafCasMission.flareCasTargetGroup) local nbVehicles, nbInfantry = veafUnits.countInfantryAndVehicles(veafCasMission.casGroupName) local message = "TARGET: Group of " .. nbVehicles .. " vehicles and " .. nbInfantry .. " soldiers. See F10 radio menu for details\n" trigger.action.outText(message,5) veafRadio.refreshRadioMenu() -- start checking for targets destruction veafCasMission.casGroupWatchdog() end -- Ask a report -- @param int groupId function veafCasMission.reportTargetInformation(unitName) -- generate information dispatch local nbVehicles, nbInfantry = veafUnits.countInfantryAndVehicles(veafCasMission.casGroupName) local message = "TARGET: Group of " .. nbVehicles .. " vehicles and " .. nbInfantry .. " soldiers.\n" if veafCasMission.afacName then message = message .. "AFAC on station: " .. veafCasMission.afacName .. "\n" end message = message .. "\n" -- add coordinates and position from bullseye local averageGroupPosition = veaf.getAveragePosition(veafCasMission.casGroupName) local lat, lon = coord.LOtoLL(averageGroupPosition) local mgrsString = mist.tostringMGRS(coord.LLtoMGRS(lat, lon), 3) local bullseye = mist.utils.makeVec3(mist.DBs.missionData.bullseye.blue, 0) local vec = {x = averageGroupPosition.x - bullseye.x, y = averageGroupPosition.y - bullseye.y, z = averageGroupPosition.z - bullseye.z} local dir = mist.utils.round(mist.utils.toDegree(mist.utils.getDir(vec, bullseye)), 0) local dist = mist.utils.get2DDist(averageGroupPosition, bullseye) local distMetric = mist.utils.round(dist/1000, 0) local distImperial = mist.utils.round(mist.utils.metersToNM(dist), 0) local fromBullseye = string.format('%03d', dir) .. ' for ' .. distMetric .. 'km /' .. distImperial .. 'nm' message = message .. "LAT LON (decimal): " .. mist.tostringLL(lat, lon, 2) .. ".\n" message = message .. "LAT LON (DMS) : " .. mist.tostringLL(lat, lon, 0, true) .. ".\n" message = message .. "MGRS/UTM : " .. mgrsString .. ".\n" message = message .. "FROM BULLSEYE : " .. fromBullseye .. ".\n" message = message .. "\n" message = message .. veaf.weatherReport(averageGroupPosition, nil, true) -- send message only for the unit veaf.outTextForGroup(unitName, message, 30) end --- add a smoke marker over the target area function veafCasMission.smokeCasTargetGroup() veaf.loggers.get(veafCasMission.Id):trace("veafCasMission.smokeCasTargetGroup START") veafSpawn.spawnSmoke(veaf.getAveragePosition(veafCasMission.casGroupName), trigger.smokeColor.Red) trigger.action.outText('Copy smoke requested, RED smoke on the deck!',5) veafRadio.delCommand(veafCasMission.targetMarkersPath, 'Request smoke on target area') veafRadio.addCommandToSubmenu('Target is marked with red smoke', veafCasMission.targetMarkersPath, veaf.emptyFunction) veafCasMission.smokeResetTaskID = mist.scheduleFunction(veafCasMission.smokeReset,{},timer.getTime()+veafCasMission.SecondsBetweenSmokeRequests) veafRadio.refreshRadioMenu() end --- Reset the smoke request radio menu function veafCasMission.smokeReset() veafRadio.delCommand(veafCasMission.targetMarkersPath, 'Target is marked with red smoke') veafRadio.addCommandToSubmenu('Request smoke on target area', veafCasMission.targetMarkersPath, veafCasMission.smokeCasTargetGroup) trigger.action.outText('Smoke marker available',5) veafRadio.refreshRadioMenu() end --- add an illumination flare over the target area function veafCasMission.flareCasTargetGroup() veafSpawn.spawnIlluminationFlare(veaf.getAveragePosition(veafCasMission.casGroupName)) trigger.action.outText('Copy illumination flare requested, illumination flare over target area!',5) veafRadio.delCommand(veafCasMission.targetMarkersPath, 'Request illumination flare over target area') veafRadio.addCommandToSubmenu('Target area is marked with illumination flare', veafCasMission.targetMarkersPath, veaf.emptyFunction) veafCasMission.flareResetTaskID = mist.scheduleFunction(veafCasMission.flareReset,{},timer.getTime()+veafCasMission.SecondsBetweenFlareRequests) veafRadio.refreshRadioMenu() end --- Reset the flare request radio menu function veafCasMission.flareReset() veafRadio.delCommand(veafCasMission.targetMarkersPath, 'Target area is marked with illumination flare') veafRadio.addCommandToSubmenu('Request illumination flare over target area', veafCasMission.targetMarkersPath, veafCasMission.flareCasTargetGroup) trigger.action.outText('Target illumination available',5) veafRadio.refreshRadioMenu() end --- Checks if the vehicles group is still alive, and if not announces the end of the CAS mission function veafCasMission.casGroupWatchdog() local nbVehicles, nbInfantry = veafUnits.countInfantryAndVehicles(veafCasMission.casGroupName) if nbVehicles > 0 then veaf.loggers.get(veafCasMission.Id):trace("Group is still alive with "..nbVehicles.." vehicles and "..nbInfantry.." soldiers") veafCasMission.groupAliveCheckTaskID = mist.scheduleFunction(veafCasMission.casGroupWatchdog,{},timer.getTime()+veafCasMission.SecondsBetweenWatchdogChecks) else trigger.action.outText("CAS objective group destroyed!", 5) veafCasMission.cleanupAfterMission() end end --- Called from the "Skip target" radio menu : remove the current CAS target group function veafCasMission.skipCasTarget() veafCasMission.cleanupAfterMission() trigger.action.outText("CAS objective group cleaned up.", 5) end --- Cleanup after either mission is ended or aborted function veafCasMission.cleanupAfterMission() veaf.loggers.get(veafCasMission.Id):trace("skipCasTarget START") -- destroy vehicles and infantry groups veaf.loggers.get(veafCasMission.Id):trace("destroy CAS group") local group = Group.getByName(veafCasMission.casGroupName) if group and group:isExist() == true then group:destroy() end veaf.loggers.get(veafCasMission.Id):trace("destroy AFAC group") group = Group.getByName(veafCasMission.afacName) if group and group:isExist() == true then group:destroy() end veafCasMission.afacName = nil -- remove the watchdog function veaf.loggers.get(veafCasMission.Id):trace("remove the watchdog function") if veafCasMission.groupAliveCheckTaskID ~= 'none' then mist.removeFunction(veafCasMission.groupAliveCheckTaskID) end veafCasMission.groupAliveCheckTaskID = 'none' veaf.loggers.get(veafCasMission.Id):trace("update the radio menu 1") veafRadio.delCommand(veafCasMission.rootPath, 'Target information') veaf.loggers.get(veafCasMission.Id):trace("update the radio menu 2") veafRadio.delCommand(veafCasMission.rootPath, 'Skip current objective') veaf.loggers.get(veafCasMission.Id):trace("update the radio menu 3") veafRadio.delCommand(veafCasMission.rootPath, 'Get current objective situation') veaf.loggers.get(veafCasMission.Id):trace("update the radio menu 4") veafRadio.delSubmenu(veafCasMission.targetMarkersPath, veafCasMission.rootPath) veafRadio.refreshRadioMenu() veaf.loggers.get(veafCasMission.Id):trace("skipCasTarget DONE") end ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Radio menu and help ------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Build the initial radio menu function veafCasMission.buildRadioMenu() veafCasMission.rootPath = veafRadio.addSubMenu(veafCasMission.RadioMenuName) if not(veafRadio.skipHelpMenus) then veafRadio.addCommandToSubmenu("HELP", veafCasMission.rootPath, veafCasMission.help, nil, veafRadio.USAGE_ForGroup) end end function veafCasMission.help(unitName) local text = 'Create a marker and type "_cas" in the text\n' .. 'This will create a default CAS target group\n' .. 'You can add options (comma separated) :\n' .. ' "defense 0" completely disables air defenses\n' .. ' "defense [1-5]" specifies air defense cover (1 = light, 5 = heavy)\n' .. ' "size [1-5]" changes the group size (1 = small, 5 = huge)\n' .. ' "armor [1-5]" specifies armor presence (1 = light, 5 = heavy)\n' .. ' "spacing [1-5]" changes the groups spacing (1 = dense, 3 = default, 5 = sparse)' veaf.outTextForGroup(unitName, text, 30) end ------------------------------------------------------------------------------------------------------------------------------------------------------------- -- initialisation ------------------------------------------------------------------------------------------------------------------------------------------------------------- function veafCasMission.initialize() veafCasMission.buildRadioMenu() veafMarkers.registerEventHandler(veafMarkers.MarkerChange, veafCasMission.onEventMarkChange) end veaf.loggers.get(veafCasMission.Id):info(string.format("Loading version %s", veafCasMission.Version)) --- Enable/Disable error boxes displayed on screen. env.setErrorMessageBoxEnabled(false)