diff --git a/CTLD-i18n.lua b/CTLD-i18n.lua index 33475e6..651fe1f 100644 --- a/CTLD-i18n.lua +++ b/CTLD-i18n.lua @@ -18,7 +18,7 @@ ctld.i18n = {} --======== FRENCH - FRANCAIS ===================================================================================== ctld.i18n["fr"] = {} -ctld.i18n["fr"].translation_version = "1.4" -- make sure that this translation is compatible with the current version of the english language texts (ctld.i18n["en"].translation_version) +ctld.i18n["fr"].translation_version = "1.6" -- make sure that this translation is compatible with the current version of the english language texts (ctld.i18n["en"].translation_version) local lang="fr";env.info(string.format("I - CTLD.i18n_translate: Loading %s language version %s", lang, tostring(ctld.i18n[lang].translation_version))) --- groups names @@ -158,8 +158,6 @@ ctld.i18n["fr"]["FOB Crate dropped back to base"] = "Caisse FOB ramenée à la b ctld.i18n["fr"]["FOB Crate Loaded"] = "Caisse FOB chargée" ctld.i18n["fr"]["%1 loaded a FOB Crate ready for delivery!"] = "%1 a chargé une caisse FOB prête à être livrée !" ctld.i18n["fr"]["There are no friendly logistic units nearby to load a FOB crate from!"] = "Il n'y a pas d'unités logistiques alliée à proximité pour charger une caisse FOB !" -ctld.i18n["fr"]["You already have troops onboard."] = "Vous avez déjà des troupes à bord." -ctld.i18n["fr"]["You already have vehicles onboard."] = "Vous avez déjà des véhicules à bord." ctld.i18n["fr"]["This area has no more reinforcements available!"] = "Cette zone n'a plus de renforts disponibles !" ctld.i18n["fr"]["You are not in a pickup zone and no one is nearby to extract"] = "Vous n'êtes pas dans une zone d'embarquement et personne n'est à proximité pour être extrait." ctld.i18n["fr"]["You are not in a pickup zone"] = "Vous n'êtes pas dans une zone d'embarquement" @@ -167,6 +165,7 @@ ctld.i18n["fr"]["No one to unload"] = "Personne à débarquer" ctld.i18n["fr"]["Dropped troops back to base"] = "Troupes larguées à la base" ctld.i18n["fr"]["Dropped vehicles back to base"] = "Véhicules largués à la base" ctld.i18n["fr"]["You already have troops onboard."] = "Vous avez déjà des troupes à bord." +ctld.i18n["fr"]["Count Infantries limit in the mission reached, you can't load more troops"] = "Nombre maximum de troupes sur mission atteint, vous ne pouvez pas charger plus de troupes" ctld.i18n["fr"]["You already have vehicles onboard."] = "Vous avez déjà des véhicules à bord." ctld.i18n["fr"]["Sorry - The group of %1 is too large to fit. \n\nLimit is %2 for %3"] = "Désolé - Le groupe de %1 est trop important. \n\nLa limite est de %2 pour %3" ctld.i18n["fr"]["%1 extracted troops in %2 from combat"] = "%1 troupes extraites du combat en %2" @@ -265,10 +264,11 @@ ctld.i18n["fr"]["Unload / Extract Troops"] = "Débarqt / Embarqt Troupes" ctld.i18n["fr"]["Next page"] = "page suiv." ctld.i18n["fr"]["Load "] = "Charger " ctld.i18n["fr"]["Vehicle / FOB Transport"] = "Transport Vehicule / FOB" -ctld.i18n["fr"]["Vehicle / FOB Crates / Drone"] = "Caisses Vehicule / FOB / Drone" +ctld.i18n["fr"]["Crates: Vehicle / FOB / Drone"] = "Caisses Vehicule / FOB / Drone" ctld.i18n["fr"]["Unload Vehicles"] = "Décharger Vehicles" ctld.i18n["fr"]["Load / Extract Vehicles"] = "Chargt / Déchargt Vehicules" ctld.i18n["fr"]["Load / Unload FOB Crate"] = "Chargt / Déchargt Caisse FOB" +ctld.i18n["fr"]["Repack Vehicles"] = "Ré-emballer véhicules" ctld.i18n["fr"]["CTLD Commands"] = "Commandes CTLD" ctld.i18n["fr"]["CTLD"] = "CTLD" ctld.i18n["fr"]["Check Cargo"] = "Vérif° chargement" @@ -301,7 +301,7 @@ ctld.i18n["fr"]["STOP autoRefresh targets in LOS"] = "Stopper suivi automatique --====== SPANISH : ESPAÑOL==================================================================================== ctld.i18n["es"] = {} -ctld.i18n["es"].translation_version = "1.4" -- make sure that this translation is compatible with the current version of the english language texts (ctld.i18n["en"].translation_version) +ctld.i18n["es"].translation_version = "1.6" -- make sure that this translation is compatible with the current version of the english language texts (ctld.i18n["en"].translation_version) local lang="es";env.info(string.format("I - CTLD.i18n_translate: Loading %s language version %s", lang, tostring(ctld.i18n[lang].translation_version))) --- groups names @@ -323,98 +323,98 @@ ctld.i18n["es"]["5x - Mortar Squad"] = "5x - Grupos de morteros" ctld.i18n["es"]["Mortar Squad Red"] = "Grupo mortero rojo" --- crates names -ctld.i18n["es"]["Humvee - MG"] = "" -ctld.i18n["es"]["Humvee - TOW"] = "" -ctld.i18n["es"]["Light Tank - MRAP"] = "" -ctld.i18n["es"]["Med Tank - LAV-25"] = "" -ctld.i18n["es"]["Heavy Tank - Abrams"] = "" -ctld.i18n["es"]["BTR-D"] = "" -ctld.i18n["es"]["BRDM-2"] = "" -ctld.i18n["es"]["Hummer - JTAC"] = "" -ctld.i18n["es"]["M-818 Ammo Truck"] = "" -ctld.i18n["es"]["M-978 Tanker"] = "" -ctld.i18n["es"]["SKP-11 - JTAC"] = "" -ctld.i18n["es"]["Ural-375 Ammo Truck"] = "" -ctld.i18n["es"]["KAMAZ Ammo Truck"] = "" -ctld.i18n["es"]["EWR Radar"] = "" -ctld.i18n["es"]["FOB Crate - Small"] = "" -ctld.i18n["es"]["MQ-9 Repear - JTAC"] = "" -ctld.i18n["es"]["RQ-1A Predator - JTAC"] = "" -ctld.i18n["es"]["MLRS"] = "" -ctld.i18n["es"]["SpGH DANA"] = "" -ctld.i18n["es"]["T155 Firtina"] = "" -ctld.i18n["es"]["Howitzer"] = "" -ctld.i18n["es"]["SPH 2S19 Msta"] = "" -ctld.i18n["es"]["M1097 Avenger"] = "" -ctld.i18n["es"]["M48 Chaparral"] = "" -ctld.i18n["es"]["Roland ADS"] = "" -ctld.i18n["es"]["Gepard AAA"] = "" -ctld.i18n["es"]["LPWS C-RAM"] = "" -ctld.i18n["es"]["9K33 Osa"] = "" -ctld.i18n["es"]["9P31 Strela-1"] = "" -ctld.i18n["es"]["9K35M Strela-10"] = "" -ctld.i18n["es"]["9K331 Tor"] = "" -ctld.i18n["es"]["2K22 Tunguska"] = "" -ctld.i18n["es"]["HAWK Launcher"] = "" -ctld.i18n["es"]["HAWK Search Radar"] = "" -ctld.i18n["es"]["HAWK Track Radar"] = "" -ctld.i18n["es"]["HAWK PCP"] = "" -ctld.i18n["es"]["HAWK CWAR"] = "" -ctld.i18n["es"]["HAWK Repair"] = "" -ctld.i18n["es"]["NASAMS Launcher 120C"] = "" -ctld.i18n["es"]["NASAMS Search/Track Radar"] = "" -ctld.i18n["es"]["NASAMS Command Post"] = "" -ctld.i18n["es"]["NASAMS Repair"] = "" -ctld.i18n["es"]["KUB Launcher"] = "" -ctld.i18n["es"]["KUB Radar"] = "" -ctld.i18n["es"]["KUB Repair"] = "" -ctld.i18n["es"]["BUK Launcher"] = "" -ctld.i18n["es"]["BUK Search Radar"] = "" -ctld.i18n["es"]["BUK CC Radar"] = "" -ctld.i18n["es"]["BUK Repair"] = "" -ctld.i18n["es"]["Patriot Launcher"] = "" -ctld.i18n["es"]["Patriot Radar"] = "" -ctld.i18n["es"]["Patriot ECS"] = "" -ctld.i18n["es"]["Patriot ICC"] = "" -ctld.i18n["es"]["Patriot EPP"] = "" +ctld.i18n["es"]["Humvee - MG"] = "Humvee - Antipersonal .50 cal" +ctld.i18n["es"]["Humvee - TOW"] = "Humvee - Antitanque TOW" +ctld.i18n["es"]["Light Tank - MRAP"] = "Tanque ligero - MRAP" +ctld.i18n["es"]["Med Tank - LAV-25"] = "Tanque Med - LAV-25" +ctld.i18n["es"]["Heavy Tank - Abrams"] = "Tanque pesado - Abrams" +ctld.i18n["es"]["BTR-D"] = "BTR-D - Transporte de tropas" +ctld.i18n["es"]["BRDM-2"] = "BRDM-2 - Reconocimiento" +ctld.i18n["es"]["Hummer - JTAC"] = "JTAC Hummer" +ctld.i18n["es"]["M-818 Ammo Truck"] = "Camión M-818 de municiones" +ctld.i18n["es"]["M-978 Tanker"] = "Camión cisterna M-978" +ctld.i18n["es"]["SKP-11 - JTAC"] = "JTAC SKP-11" +ctld.i18n["es"]["Ural-375 Ammo Truck"] = "Camión Ural-375 de municiones" +ctld.i18n["es"]["KAMAZ Ammo Truck"] = "Camión KAMAZ de municiones" +ctld.i18n["es"]["EWR Radar"] = "Radar Alerta Temprana" +ctld.i18n["es"]["FOB Crate - Small"] = "Caja FOB - Pequeña" +ctld.i18n["es"]["MQ-9 Repear - JTAC"] = "JTAC MQ-9 Repear" +ctld.i18n["es"]["RQ-1A Predator - JTAC"] = "JTAC RQ-1A Predator" +ctld.i18n["es"]["MLRS"] = "MLRS - Artilleria de cohetes" +ctld.i18n["es"]["SpGH DANA"] = "Obus autopropulsado SpGH DANA" +ctld.i18n["es"]["T155 Firtina"] = "Obus autopropulsado T155 Firtina" +ctld.i18n["es"]["Howitzer"] = "Obus autopropulsado M109A6 Paladin" +ctld.i18n["es"]["SPH 2S19 Msta"] = "SPH 2S19 Msta - Obus Autopropulsado" +ctld.i18n["es"]["M1097 Avenger"] = "M1097 Avenger - SAM Corta Distancia" +ctld.i18n["es"]["M48 Chaparral"] = "M48 Chaparral - SAM Corta Distancia" +ctld.i18n["es"]["Roland ADS"] = "Roland ADS - Lanzador" +ctld.i18n["es"]["Gepard AAA"] = "Gepard AAA - AAA" +ctld.i18n["es"]["LPWS C-RAM"] = "LPWS C-RAM - AAA" +ctld.i18n["es"]["9K33 Osa"] = "9K33 Osa - SA-8 Gecko" +ctld.i18n["es"]["9P31 Strela-1"] = "9P31 Strela-1 - SA-9 Gaskin" +ctld.i18n["es"]["9K35M Strela-10"] = "9K35M Strela-10 - SA-13 Gopher" +ctld.i18n["es"]["9K331 Tor"] = "9K331 Tor - SA-15 Tor" +ctld.i18n["es"]["2K22 Tunguska"] = "2K22 Tunguska - SA-19 Tunguska" +ctld.i18n["es"]["HAWK Launcher"] = "HAWK - Lanzador" +ctld.i18n["es"]["HAWK Search Radar"] = "HAWK - Radar de Búsqueda" +ctld.i18n["es"]["HAWK Track Radar"] = "HAWK - Radar de Seguimiento" +ctld.i18n["es"]["HAWK PCP"] = "HAWK - Puesto de Comando" +ctld.i18n["es"]["HAWK CWAR"] = "HAWK - Sistema de Control de Guerra" +ctld.i18n["es"]["HAWK Repair"] = "Reparar HAWK" +ctld.i18n["es"]["NASAMS Launcher 120C"] = "NASAMS - Lanzador 120C" +ctld.i18n["es"]["NASAMS Search/Track Radar"] = "NASAMS - Radar de Búsqueda/Seguimiento" +ctld.i18n["es"]["NASAMS Command Post"] = "NASAMS - Puesto de Mando" +ctld.i18n["es"]["NASAMS Repair"] = "Reparar NASAMS" +ctld.i18n["es"]["KUB Launcher"] = "KUB - Lanzador" +ctld.i18n["es"]["KUB Radar"] = "KUB - Radar" +ctld.i18n["es"]["KUB Repair"] = "Reparar KUB" +ctld.i18n["es"]["BUK Launcher"] = "BUK - Lanzador" +ctld.i18n["es"]["BUK Search Radar"] = "BUK - Radar de Búsqueda" +ctld.i18n["es"]["BUK CC Radar"] = "BUK - Radar de Control de Combate" +ctld.i18n["es"]["BUK Repair"] = "Reparar BUK" +ctld.i18n["es"]["Patriot Launcher"] = "Patriot - Lanzador" +ctld.i18n["es"]["Patriot Radar"] = "Patriot - Radar de Búsqueda" +ctld.i18n["es"]["Patriot ECS"] = "Patriot - Puesto de Mando" +ctld.i18n["es"]["Patriot ICC"] = "Patriot - Sistema de Control de Fuego" +ctld.i18n["es"]["Patriot EPP"] = "Patriot - Generador" ctld.i18n["es"]["Patriot AMG (optional)"] = "" -ctld.i18n["es"]["Patriot Repair"] = "" -ctld.i18n["es"]["S-300 Grumble TEL C"] = "" -ctld.i18n["es"]["S-300 Grumble Flap Lid-A TR"] = "" -ctld.i18n["es"]["S-300 Grumble Clam Shell SR"] = "" -ctld.i18n["es"]["S-300 Grumble Big Bird SR"] = "" -ctld.i18n["es"]["S-300 Grumble C2"] = "" -ctld.i18n["es"]["S-300 Repair"] = "" -ctld.i18n["es"]["Humvee - TOW - All crates"] = "Humvee - TOW - Todas cajas" -ctld.i18n["es"]["Light Tank - MRAP - All crates"] = "Light Tank - MRAP - Todas cajas" -ctld.i18n["es"]["Med Tank - LAV-25 - All crates"] = "Med Tank - LAV-25 - Todas cajas" -ctld.i18n["es"]["Heavy Tank - Abrams - All crates"] = "Heavy Tank - Abrams - Todas cajas" -ctld.i18n["es"]["Hummer - JTAC - All crates"] = "Hummer - JTAC - Todas cajas" -ctld.i18n["es"]["M-818 Ammo Truck - All crates"] = "M-818 Ammo Truck - Todas cajas" -ctld.i18n["es"]["M-978 Tanker - All crates"] = "M-978 Tanker - Todas cajas" -ctld.i18n["es"]["Ural-375 Ammo Truck - All crates"] = "Ural-375 Ammo Truck - Todas cajas" -ctld.i18n["es"]["EWR Radar - All crates"] = "EWR Radar - Todas cajas" -ctld.i18n["es"]["MLRS - All crates"] = "MLRS - Todas cajas" -ctld.i18n["es"]["SpGH DANA - All crates"] = "SpGH DANA - Todas cajas" -ctld.i18n["es"]["T155 Firtina - All crates"] = "T155 Firtina - Todas cajas" -ctld.i18n["es"]["Howitzer - All crates"] = "Howitzer - Todas cajas" -ctld.i18n["es"]["SPH 2S19 Msta - All crates"] = "SPH 2S19 Msta - Todas cajas" -ctld.i18n["es"]["M1097 Avenger - All crates"] = "M1097 Avenger - Todas cajas" -ctld.i18n["es"]["M48 Chaparral - All crates"] = "M48 Chaparral - Todas cajas" -ctld.i18n["es"]["Roland ADS - All crates"] = "Roland ADS - Todas cajas" -ctld.i18n["es"]["Gepard AAA - All crates"] = "Gepard AAA - Todas cajas" -ctld.i18n["es"]["LPWS C-RAM - All crates"] = "LPWS C-RAM - Todas cajas" -ctld.i18n["es"]["9K33 Osa - All crates"] = "9K33 Osa - Todas cajas" -ctld.i18n["es"]["9P31 Strela-1 - All crates"] = "9P31 Strela-1 - Todas cajas" -ctld.i18n["es"]["9K35M Strela-10 - All crates"] = "9K35M Strela-10 - Todas cajas" -ctld.i18n["es"]["9K331 Tor - All crates"] = "9K331 Tor - Todas cajas" -ctld.i18n["es"]["2K22 Tunguska - All crates"] = "2K22 Tunguska - Todas cajas" -ctld.i18n["es"]["HAWK - All crates"] = "HAWK - Todas cajas" -ctld.i18n["es"]["NASAMS - All crates"] = "NASAMS - Todas cajas" -ctld.i18n["es"]["KUB - All crates"] = "KUB - Todas cajas" -ctld.i18n["es"]["BUK - All crates"] = "BUK - Todas cajas" -ctld.i18n["es"]["Patriot - All crates"] = "Patriot - Todas cajas" -ctld.i18n["es"]["Patriot - All crates"] = "Patriot - Todas cajas" +ctld.i18n["es"]["Patriot Repair"] = "Reparar Patriot" +ctld.i18n["es"]["S-300 Grumble TEL C"] = "S-300 Grumble TEL C - Lanzador" +ctld.i18n["es"]["S-300 Grumble Flap Lid-A TR"] = "S-300 Grumble Flap Lid-A TR - Radar de Seguimiento" +ctld.i18n["es"]["S-300 Grumble Clam Shell SR"] = "S-300 Grumble Clam Shell SR - Radar de Búsqueda" +ctld.i18n["es"]["S-300 Grumble Big Bird SR"] = "S-300 Grumble Big Bird SR - Radar de Búsqueda" +ctld.i18n["es"]["S-300 Grumble C2"] = "S-300 Grumble C2 - Puesto de Mando" +ctld.i18n["es"]["S-300 Repair"] = "Reparar S-300" +ctld.i18n["es"]["Humvee - TOW - All crates"] = "Humvee - TOW - Todas las cajas" +ctld.i18n["es"]["Light Tank - MRAP - All crates"] = "Light Tank - MRAP - Todas las cajas" +ctld.i18n["es"]["Med Tank - LAV-25 - All crates"] = "Med Tank - LAV-25 - Todas las cajas" +ctld.i18n["es"]["Heavy Tank - Abrams - All crates"] = "Heavy Tank - Abrams - Todas las cajas" +ctld.i18n["es"]["Hummer - JTAC - All crates"] = "Hummer - JTAC - Todas las cajas" +ctld.i18n["es"]["M-818 Ammo Truck - All crates"] = "M-818 Ammo Truck - Todas las cajas" +ctld.i18n["es"]["M-978 Tanker - All crates"] = "M-978 Tanker - Todas las cajas" +ctld.i18n["es"]["Ural-375 Ammo Truck - All crates"] = "Ural-375 Ammo Truck - Todas las cajas" +ctld.i18n["es"]["EWR Radar - All crates"] = "EWR Radar - Todas las cajas" +ctld.i18n["es"]["MLRS - All crates"] = "MLRS - Todas las cajas" +ctld.i18n["es"]["SpGH DANA - All crates"] = "SpGH DANA - Todas las cajas" +ctld.i18n["es"]["T155 Firtina - All crates"] = "T155 Firtina - Todas las cajas" +ctld.i18n["es"]["Howitzer - All crates"] = "Howitzer - Todas las cajas" +ctld.i18n["es"]["SPH 2S19 Msta - All crates"] = "SPH 2S19 Msta - Todas las cajas" +ctld.i18n["es"]["M1097 Avenger - All crates"] = "M1097 Avenger - Todas las cajas" +ctld.i18n["es"]["M48 Chaparral - All crates"] = "M48 Chaparral - Todas las cajas" +ctld.i18n["es"]["Roland ADS - All crates"] = "Roland ADS - Todas las cajas" +ctld.i18n["es"]["Gepard AAA - All crates"] = "Gepard AAA - Todas las cajas" +ctld.i18n["es"]["LPWS C-RAM - All crates"] = "LPWS C-RAM - Todas las cajas" +ctld.i18n["es"]["9K33 Osa - All crates"] = "9K33 Osa - Todas las cajas" +ctld.i18n["es"]["9P31 Strela-1 - All crates"] = "9P31 Strela-1 - Todas las cajas" +ctld.i18n["es"]["9K35M Strela-10 - All crates"] = "9K35M Strela-10 - Todas las cajas" +ctld.i18n["es"]["9K331 Tor - All crates"] = "9K331 Tor - Todas las cajas" +ctld.i18n["es"]["2K22 Tunguska - All crates"] = "2K22 Tunguska - Todas las cajas" +ctld.i18n["es"]["HAWK - All crates"] = "HAWK - Todas clas ajas" +ctld.i18n["es"]["NASAMS - All crates"] = "NASAMS - Todas las cajas" +ctld.i18n["es"]["KUB - All crates"] = "KUB - Todas las cajas" +ctld.i18n["es"]["BUK - All crates"] = "BUK - Todas las cajas" +ctld.i18n["es"]["Patriot - All crates"] = "Patriot - Todas las cajas" +ctld.i18n["es"]["Patriot - All crates"] = "Patriot - Todas las cajas" --- mission design error messages ctld.i18n["es"]["CTLD.lua ERROR: Can't find trigger called %1"] = "CTLD.lua ERROR : Imposible encontrar el activador llamado %1" @@ -423,34 +423,33 @@ ctld.i18n["es"]["CTLD.lua ERROR: Can't find zone or ship called %1"] = "CTLD.lua ctld.i18n["es"]["CTLD.lua ERROR: Can't find crate with weight %1"] = "CTLD.lua ERROR : Imposible encontrar una caja con un peso de %1" --- runtime messages -ctld.i18n["es"]["You are not close enough to friendly logistics to get a crate!"] = "¡No estás lo suficientemente cerca de la logística amigable para conseguir una caja!" +ctld.i18n["es"]["You are not close enough to friendly logistics to get a crate!"] = "¡No estás lo suficientemente cerca de la logística aliada para solicitar una caja!" ctld.i18n["es"]["No more JTAC Crates Left!"] = "¡No hay más cajas JTAC disponibles!" -ctld.i18n["es"]["Sorry you must wait %1 seconds before you can get another crate"] = "Lo sentimos, debes esperar %1 segundos antes de poder conseguir otra caja" -ctld.i18n["es"]["A %1 crate weighing %2 kg has been brought out and is at your %3 o'clock "] = "Una caja %1 que pesa %2 kg ha sido sacada y está a tus %3 horas " -ctld.i18n["es"]["%1 fast-ropped troops from %2 into combat"] = "%1 lanzó rápidamente tropas de %2 al combate" -ctld.i18n["es"]["%1 dropped troops from %2 into combat"] = "%1 arrojó tropas de %2 al combate" -ctld.i18n["es"]["%1 fast-ropped troops from %2 into %3"] = "%1 lanzó tropas rápidamente de %2 a %3" +ctld.i18n["es"]["Sorry you must wait %1 seconds before you can get another crate"] = "Lo sentimos, debes esperar %1 segundos antes de poder solicitar otra caja" +ctld.i18n["es"]["A %1 crate weighing %2 kg has been brought out and is at your %3 o'clock "] = "Una caja %1 pesando %2 kg ha sido preparada y está a tus %3 en punto " +ctld.i18n["es"]["%1 fast-ropped troops from %2 into combat"] = "%1 descolgo tropas con cuerdas de %2 al combate" +ctld.i18n["es"]["%1 dropped troops from %2 into combat"] = "%1 descargo tropas de %2 al combate" +ctld.i18n["es"]["%1 fast-ropped troops from %2 into %3"] = "%1 descolgo tropas con cuerdas de %2 a %3" ctld.i18n["es"]["%1 dropped troops from %2 into %3"] = "%1 arrojó tropas de %2 a %3" -ctld.i18n["es"]["Too high or too fast to drop troops into combat! Hover below %1 feet or land."] = "¡Demasiado alto o demasiado rápido para lanzar tropas al combate! Coloca el cursor por debajo de % 1 pies o aterriza." -ctld.i18n["es"]["%1 dropped vehicles from %2 into combat"] = "%1 descargo vehículos de %2 en combate" +ctld.i18n["es"]["Too high or too fast to drop troops into combat! Hover below %1 feet or land."] = "¡Demasiado alto o rápido para lanzar tropas al combate! Manten estacionario por debajo de % 1 pies o aterriza." +ctld.i18n["es"]["%1 dropped vehicles from %2 into combat"] = "%1 descargo vehículos de %2 al combate" ctld.i18n["es"]["%1 loaded troops into %2"] = "%1 cargó tropas en %2" ctld.i18n["es"]["%1 loaded %2 vehicles into %3"] = "%1 cargó %2 vehículos en %3" ctld.i18n["es"]["%1 delivered a FOB Crate"] = "%1 entregó una caja FOB" -ctld.i18n["es"]["Delivered FOB Crate 60m at 6'oclock to you"] = "Se le entregó la caja FOB de 60 m a sus 6 horas" -ctld.i18n["es"]["FOB Crate dropped back to base"] = "La caja FOB volvió a la base" +ctld.i18n["es"]["Delivered FOB Crate 60m at 6 o'clock to you"] = "Se le entregó la caja FOB de 60 m a sus 6 en punto" +ctld.i18n["es"]["FOB Crate dropped back to base"] = "Caja FOB devuelta a la base" ctld.i18n["es"]["FOB Crate Loaded"] = "Caja FOB cargada" ctld.i18n["es"]["%1 loaded a FOB Crate ready for delivery!"] = "%1 cargó una caja FOB lista para su entrega!" ctld.i18n["es"]["There are no friendly logistic units nearby to load a FOB crate from!"] = "¡No hay unidades logísticas amigas cerca para cargar una caja FOB!" -ctld.i18n["es"]["You already have troops onboard."] = "Ya tienes tropas a bordo." -ctld.i18n["es"]["You already have vehicles onboard."] = "Ya tiene vehículos a bordo." ctld.i18n["es"]["This area has no more reinforcements available!"] = "¡Esta área no tiene más refuerzos disponibles!" -ctld.i18n["es"]["You are not in a pickup zone and no one is nearby to extract"] = "No estás en una zona de recogida y no hay nadie cerca para extraerlo" -ctld.i18n["es"]["You are not in a pickup zone"] = "No estás en una zona de recogida" -ctld.i18n["es"]["No one to unload"] = "Nadie para descargar" -ctld.i18n["es"]["Dropped troops back to base"] = "Tropas arrojadas a la base" -ctld.i18n["es"]["Dropped vehicles back to base"] = "Vehículos arrojados a la base" +ctld.i18n["es"]["You are not in a pickup zone and no one is nearby to extract"] = "No estás en una zona de carga y/o no hay nadie cerca para extraccion" +ctld.i18n["es"]["You are not in a pickup zone"] = "No estás en una zona de carga" +ctld.i18n["es"]["No one to unload"] = "Nadie / Nada para descargar" +ctld.i18n["es"]["Dropped troops back to base"] = "Tropas descargados de vuelta a la base" +ctld.i18n["es"]["Dropped vehicles back to base"] = "Vehículos descargados de vuelta a la base" ctld.i18n["es"]["You already have troops onboard."] = "Ya tienes tropas a bordo." -ctld.i18n["es"]["You already have vehicles onboard."] = "Ya tiene vehículos a bordo." +ctld.i18n["es"]["Count Infantries limit in the mission reached, you can't load more troops"] = "Se alcanzó el límite de infantería en la misión, no puedes cargar más tropas" +ctld.i18n["es"]["You already have vehicles onboard."] = "Ya tienes vehículos a bordo." ctld.i18n["es"]["Sorry - The group of %1 is too large to fit. \n\nLimit is %2 for %3"] = "Lo sentimos, el grupo de %1 es demasiado grande. \n \nEl límite es %2 para %3" ctld.i18n["es"]["%1 extracted troops in %2 from combat"] = "%1 tropas extraídas del combate en %2" ctld.i18n["es"]["No extractable troops nearby!"] = "¡No hay tropas extraíbles cerca!" @@ -461,69 +460,69 @@ ctld.i18n["es"]["%1 vehicles onboard (%2)\n"] = "%1 vehículos a bordo (%2)\n" ctld.i18n["es"]["1 FOB Crate oboard (%1 kg)\n"] = "1 caja FOB a bordo (%1 kg)\n" ctld.i18n["es"]["%1 crate onboard (%2 kg)\n"] = "%1 caja a bordo (%2 kg)\n" ctld.i18n["es"]["Total weight of cargo : %1 kg\n"] = "Peso total de la carga: %1 kg\n" -ctld.i18n["es"]["No cargo."] = "Aucune cargaison." -ctld.i18n["es"]["Hovering above %1 crate. \n\nHold hover for %2 seconds! \n\nIf the countdown stops you're too far away!"] = "Flotando sobre %1 caja. \n\n¡Manténte flotando sobre ella durante %2 segundos! \n\n¡Si la cuenta regresiva se detiene, estás demasiado lejos!" +ctld.i18n["es"]["No cargo."] = "Sin carga." +ctld.i18n["es"]["Hovering above %1 crate. \n\nHold hover for %2 seconds! \n\nIf the countdown stops you're too far away!"] = "En estacionario sobre la caja %1 \n\n¡Mantenlo durante %2 segundos! \n\n¡Si la cuenta atras se detiene, estás demasiado lejos!" ctld.i18n["es"]["Loaded %1 crate!"] = "Caja %1 cargada !" -ctld.i18n["es"]["Too low to hook %1 crate.\n\nHold hover for %2 seconds"] = "Demasiado bajo para enganchar la caja %1.\n\nManténte flotando sobre ella durante %2 segundos" -ctld.i18n["es"]["Too high to hook %1 crate.\n\nHold hover for %2 seconds"] = "Demasiado alto para enganchar la caja %1.\n\nManténte flotando sobre ella durante %2 segundos" +ctld.i18n["es"]["Too low to hook %1 crate.\n\nHold hover for %2 seconds"] = "Demasiado bajo para enganchar la caja %1.\n\nMantén el estacionario durante %2 segundos" +ctld.i18n["es"]["Too high to hook %1 crate.\n\nHold hover for %2 seconds"] = "Demasiado alto para enganchar la caja %1.\n\nMantén el estacionario durante %2 segundos" ctld.i18n["es"]["You must land before you can load a crate!"] = "¡Debes aterrizar antes de poder cargar una caja!" -ctld.i18n["es"]["No Crates within 50m to load!"] = "¡No hay cajas para cargar a 50 m a la redonda!" +ctld.i18n["es"]["No Crates within 50m to load!"] = "¡No hay cajas para cargar en un radio de 50 m!" ctld.i18n["es"]["Maximum number of crates are on board!"] = "¡El número máximo de cajas está a bordo!" -ctld.i18n["es"]["%1\n%2 crate - kg %3 - %4 m - %5 o'clock"] = "%1\n%2 caja - kg %3 - %4 m - %5 horas" -ctld.i18n["es"]["FOB Crate - %1 m - %2 o'clock\n"] = "Caja FOB - %1 m - %2 horas\n" +ctld.i18n["es"]["%1\n%2 crate - kg %3 - %4 m - %5 o'clock"] = "%1\n%2 caja - kg %3 - %4 m - a tus %5 en punto" +ctld.i18n["es"]["FOB Crate - %1 m - %2 o'clock\n"] = "Caja FOB - %1 m - a las %2 en punto\n" ctld.i18n["es"]["No Nearby Crates"] = "Ninguna caja de proximidad" ctld.i18n["es"]["Nearby Crates:\n%1"] = "Cajas cercanas:\n%1" -ctld.i18n["es"]["Nearby FOB Crates (Not Slingloadable):\n%1"] = "Cajas FOB cercanas (no transportable por cable):\n%1" +ctld.i18n["es"]["Nearby FOB Crates (Not Slingloadable):\n%1"] = "Cajas FOB cercanas (no se pueden cargar con eslinga):\n%1" ctld.i18n["es"]["FOB Positions:"] = "Posiciones FOB:" ctld.i18n["es"]["%1\nFOB @ %2"] = "%1\nFOB @ %2" ctld.i18n["es"]["Sorry, there are no active FOBs!"] = "¡Lo sentimos, no hay FOB activos!" ctld.i18n["es"]["No cargo."] = "Sin carga." -ctld.i18n["es"]["Hovering above %1 crate. \n\nHold hover for %2 seconds! \n\nIf the countdown stops you're too far away!"] = "Estacionario sobre la caja %1 \n\n¡Mantenlo flotando durante %2 segundos! \n\n¡Si la cuenta regresiva se detiene, estás demasiado lejos!" +ctld.i18n["es"]["Hovering above %1 crate. \n\nHold hover for %2 seconds! \n\nIf the countdown stops you're too far away!"] = "En estacionario sobre la caja %1 \n\n¡Mantenlo durante %2 segundos! \n\n¡Si la cuenta atras se detiene, estás demasiado lejos!" ctld.i18n["es"]["Loaded %1 crate!"] = "¡Caja %1 cargada!" ctld.i18n["es"]["Too low to hook %1 crate.\n\nHold hover for %2 seconds"] = "Demasiado bajo para enganchar la caja %1.\n\nMantén el estacionario durante %2 segundos" ctld.i18n["es"]["Too high to hook %1 crate.\n\nHold hover for %2 seconds"] = "Demasiado alto para enganchar la caja %1.\n\nMantén el estacionario durante %2 segundos" ctld.i18n["es"]["You must land before you can load a crate!"] = "¡Debes aterrizar antes de poder cargar una caja!" ctld.i18n["es"]["No Crates within 50m to load!"] = "¡No hay cajas para cargar en un radio de 50 m!" ctld.i18n["es"]["Maximum number of crates are on board!"] = "¡Número máximo de cajas a bordo!" -ctld.i18n["es"]["%1\n%2 crate - kg %3 - %4 m - %5 o'clock"] = "%1\n%2 caja - kg %3 - %4 m - %5 horas" -ctld.i18n["es"]["FOB Crate - %1 m - %2 o'clock\n"] = "Caja FOB - %1 m - %2 horas\n" +ctld.i18n["es"]["%1\n%2 crate - kg %3 - %4 m - %5 o'clock"] = "%1\n%2 caja - kg %3 - %4 m - a tus %5 en punto" +ctld.i18n["es"]["FOB Crate - %1 m - %2 o'clock\n"] = "Caja FOB - %1 m - a tus %2 en punto\n" ctld.i18n["es"]["No Nearby Crates"] = "No hay cajas cerca" ctld.i18n["es"]["Nearby Crates:\n%1"] = "Cajas cercanas:\n%1" ctld.i18n["es"]["Nearby FOB Crates (Not Slingloadable):\n%1"] = "Cajas FOB cercanas (no se pueden cargar con eslinga):\n%1" ctld.i18n["es"]["FOB Positions:"] = "Posiciones FOB:" ctld.i18n["es"]["%1\nFOB @ %2"] = "%1\nFOB @ %2" ctld.i18n["es"]["Sorry, there are no active FOBs!"] = "¡Lo sentimos, no hay FOB activos!" -ctld.i18n["es"]["You can't unpack that here! Take it to where it's needed!"] = "¡No puedes desembalar eso aquí! ¡Llévalo a donde lo necesites!" -ctld.i18n["es"]["Sorry you must move this crate before you unpack it!"] = "¡Lo siento, debes mover esta caja antes de desempacarla!" -ctld.i18n["es"]["%1 successfully deployed %2 to the field"] = "%1 implementó exitosamente %2 en el campo." -ctld.i18n["es"]["No friendly crates close enough to unpack, or crate too close to aircraft."] = "No hay cajas amigas lo suficientemente cerca para desempacar, o la caja está demasiado cerca de un avión" +ctld.i18n["es"]["You can't unpack that here! Take it to where it's needed!"] = "¡No puedes desembalar eso aquí! ¡Llévalo a donde lo necesiten!" +ctld.i18n["es"]["Sorry you must move this crate before you unpack it!"] = "¡Lo siento, debes mover esta caja antes de desembalar!" +ctld.i18n["es"]["%1 successfully deployed %2 to the field"] = "%1 Desplego %2 con exito en el campo." +ctld.i18n["es"]["No friendly crates close enough to unpack, or crate too close to aircraft."] = "No hay cajas amigas lo suficientemente cerca por desembalar, o la caja está demasiado cerca de un avión" ctld.i18n["es"]["Finished building FOB! Crates and Troops can now be picked up."] = "¡Construcción FOB completada! Ahora se pueden recoger cajas y tropas" -ctld.i18n["es"]["Finished building FOB! Crates can now be picked up."] ="¡Construcción FOB completada! Las cajas ahora se pueden recoger." -ctld.i18n["es"]["%1 started building FOB using %2 FOB crates, it will be finished in %3 seconds.\nPosition marked with smoke."] = "%1 comenzó a construir FOB usando %2 cajas FOB , estará terminado en %3 segundos.\nPosición marcada por la bomba de humo." +ctld.i18n["es"]["Finished building FOB! Crates can now be picked up."] ="¡Construcción FOB completada! Ahora se pueden recoger cajas." +ctld.i18n["es"]["%1 started building FOB using %2 FOB crates, it will be finished in %3 seconds.\nPosition marked with smoke."] = "%1 comenzó a construir FOB usando %2 cajas FOB , estará terminado en %3 segundos.\nPosición marcada con bomba de humo." ctld.i18n["es"]["Cannot build FOB!\n\nIt requires %1 Large FOB crates ( 3 small FOB crates equal 1 large FOB Crate) and there are the equivalent of %2 large FOB crates nearby\n\nOr the crates are not within 750m of each other"] = "¡No se puede construir el FOB!\n\nSe requiere %1 cajas FOB grandes (3 cajas FOB pequeñas equivalente a 1 caja FOB grande) y hay el equivalente a %2 cajas FOB grandes cerca\n\nO las cajas no están a menos de 750 m una de otra" -ctld.i18n["es"]["You are not currently transporting any crates. \n\nTo Pickup a crate, hover for %1 seconds above the crate or land and use F10 Crate Commands."] = "Actualmente no estás transportando ninguna caja.\n\nPara cargar una caja, flota sobre la caja durante %1 segundos o aterrice y use los comandos de caja F10." -ctld.i18n["es"]["You are not currently transporting any crates. \n\nTo Pickup a crate, hover for %1 seconds above the crate."] = "Actualmente no estás transportando ninguna caja. \n\nPour recoge una caja, flota sobre la caja durante %1 segundos." +ctld.i18n["es"]["You are not currently transporting any crates. \n\nTo Pickup a crate, hover for %1 seconds above the crate or land and use F10 Crate Commands."] = "Actualmente no estás transportando ninguna caja.\n\nPara cargar una caja, realiza un estacionario sobre la caja durante %1 segundos o aterrice y use los comandos de caja F10." +ctld.i18n["es"]["You are not currently transporting any crates. \n\nTo Pickup a crate, hover for %1 seconds above the crate."] = "Actualmente no estás transportando ninguna caja. \n\nPara cargar una caja, realiza un estacionario sobre la caja durante %1 segundos." ctld.i18n["es"]["You are not currently transporting any crates. \n\nTo Pickup a crate, land and use F10 Crate Commands to load one."] = "Actualmente no estás transportando ninguna caja. \n\nPara cargar una caja, aterriza y usa los controles de la caja F10." -ctld.i18n["es"]["%1 crate has been safely unhooked and is at your %2 o'clock"] = "%1 caja ha sido desenganchada de forma segura y está en tus %2 horas" -ctld.i18n["es"]["%1 crate has been safely dropped below you"] = "%1 caja se ha dejado caer de forma segura debajo de ti" -ctld.i18n["es"]["You were too high! The crate has been destroyed"] = "¡Estabas demasiado drogado! La caja ha sido destruida" +ctld.i18n["es"]["%1 crate has been safely unhooked and is at your %2 o'clock"] = "%1 caja desenganchada de forma segura y está en tus %2 en punto" +ctld.i18n["es"]["%1 crate has been safely dropped below you"] = "%1 caja ha soltado de forma segura debajo de ti" +ctld.i18n["es"]["You were too high! The crate has been destroyed"] = "¡Estabas demasiado alto! La caja ha sido destruida" ctld.i18n["es"]["Radio Beacons:\n%1"] = "Balizas de radio:\n%1" ctld.i18n["es"]["No Active Radio Beacons"] = "No hay radiobalizas activas" -ctld.i18n["es"]["%1 deployed a Radio Beacon.\n\n%2"] = "%1 implementó una radiobaliza.\n\n%2" +ctld.i18n["es"]["%1 deployed a Radio Beacon.\n\n%2"] = "%1 Despliega una radiobaliza.\n\n%2" ctld.i18n["es"]["You need to land before you can deploy a Radio Beacon!"] = "¡Debes aterrizar antes de poder desplegar una radiobaliza!" ctld.i18n["es"]["%1 removed a Radio Beacon.\n\n%2"] = "%1 eliminó una radiobaliza.\n\n%2" ctld.i18n["es"]["No Radio Beacons within 500m."] = "No hay radiobalizas a menos de 500 m." ctld.i18n["es"]["You need to land before remove a Radio Beacon"] = "Es necesario aterrizar antes de eliminar una radiobaliza" -ctld.i18n["es"]["%1 successfully rearmed a full %2 in the field"] = "%1 rearmó exitosamente un %2 completo en el campo" -ctld.i18n["es"]["Missing %1\n"] = "%1 falta\n" +ctld.i18n["es"]["%1 successfully rearmed a full %2 in the field"] = "%1 rearmó con exito un %2 completo en el campo" +ctld.i18n["es"]["Missing %1\n"] = "Faltan: %1\n" ctld.i18n["es"]["Out of parts for AA Systems. Current limit is %1\n"] = "Sin piezas para sistemas AA. El límite actual es %1\n" -ctld.i18n["es"]["Cannot build %1\n%2\n\nOr the crates are not close enough together"] = "Imposible construir %1\n%2\n\nO las cajas no están lo suficientemente cerca el uno del otro." -ctld.i18n["es"]["%1 successfully deployed a full %2 in the field. \n\nAA Active System limit is: %3\nActive: %4"] = "%1 implementó exitosamente un % 2 completo en el campo \n\nEl límite AA del sistema activo es: %3\nActivo: %4" -ctld.i18n["es"]["%1 successfully repaired a full %2 in the field."] = "%1 reparó exitosamente un %2 completo en el campo." -ctld.i18n["es"]["Cannot repair %1. No damaged %2 within 300m"] = "Imposible de reparar %1. No hay daños en %2 dentro de 300 m" -ctld.i18n["es"]["%1 successfully deployed %2 to the field using %3 crates."] = "%1 implementó exitosamente %2 en el campo usando %3 cajas." -ctld.i18n["es"]["Cannot build %1!\n\nIt requires %2 crates and there are %3 \n\nOr the crates are not within 300m of each other"] = "Imposible construir %1 !\n\nNecesitamos %2 cajas y hay %3 \n\nO las cajas están a no menos de 300 m una de otra" -ctld.i18n["es"]["%1 dropped %2 smoke."] = "%1 arrojó un %2 humo." +ctld.i18n["es"]["Cannot build %1\n%2\n\nOr the crates are not close enough together"] = "Imposible construir %1\n%2\n\nO las cajas no están lo suficientemente cerca unas de otras." +ctld.i18n["es"]["%1 successfully deployed a full %2 in the field. \n\nAA Active System limit is: %3\nActive: %4"] = "%1 Despliegue con exito un % 2 completo en el campo \n\nEl límite AA del sistema activo es: %3\nActivo: %4" +ctld.i18n["es"]["%1 successfully repaired a full %2 in the field."] = "%1 reparó con exito un %2 completo en el campo." +ctld.i18n["es"]["Cannot repair %1. No damaged %2 within 300m"] = "Imposible reparar %1. No hay daños en %2 en 300 m al rededor" +ctld.i18n["es"]["%1 successfully deployed %2 to the field using %3 crates."] = "%1 Despliegue con exito de %2 en el campo usando %3 cajas." +ctld.i18n["es"]["Cannot build %1!\n\nIt requires %2 crates and there are %3 \n\nOr the crates are not within 300m of each other"] = "Imposible construir %1 !\n\nNecesita %2 cajas y hay %3 \n\nO las cajas están a no menos de 300 m una de otra" +ctld.i18n["es"]["%1 dropped %2 smoke."] = "%1 lanzo humo %2." --- JTAC messages ctld.i18n["es"]["JTAC Group %1 KIA!"] = "¡Grupo JTAC %1 KIA!" @@ -541,14 +540,14 @@ ctld.i18n["es"]["%1 %2 target destroyed."] = "%1 %2 objetivo destruido." ctld.i18n["es"]["JTAC STATUS: \n\n"] = "ESTADO JTAC: \n\n" ctld.i18n["es"][", available on %1 %2,"] = ", disponible en %1 %2," ctld.i18n["es"]["UNKNOWN"] = "DESCONOCIDO" -ctld.i18n["es"][" targeting "] = " apuntación " -ctld.i18n["es"][" targeting selected unit "] = " apuntando a la unidad seleccionada" -ctld.i18n["es"][" attempting to find selected unit, temporarily targeting "] = " intento de encontrar la unidad seleccionada, objetivo temporal " +ctld.i18n["es"][" targeting "] = " apuntando " +ctld.i18n["es"][" targeting selected unit "] = " apuntando a la unidad indicada" +ctld.i18n["es"][" attempting to find selected unit, temporarily targeting "] = " intentando encontrar la unidad indicada, laser activo " ctld.i18n["es"]["(Laser OFF) "] = "(Láser INACTIVO) " ctld.i18n["es"]["Visual On: "] = "Visual activado: " ctld.i18n["es"][" searching for targets %1\n"] = " buscando objetivos %1\n" -ctld.i18n["es"]["No Active JTACs"] = "No hay JTAC activos" -ctld.i18n["es"][", targeting selected unit, %1"] = ", apuntando a la unidad seleccionada, %1" +ctld.i18n["es"]["No Active JTACs"] = "Sin JTAC activos" +ctld.i18n["es"][", targeting selected unit, %1"] = ", apuntando a la unidad indicada, %1" ctld.i18n["es"][". CODE: %1. POSITION: %2"] = ". CÓDIGO: %1. POSICIÓN: %2" ctld.i18n["es"][", target selection reset."] = ", reinicio de selección de objetivo." ctld.i18n["es"]["%1, laser and smokes enabled"] = "%1, láser y humo habilitados" @@ -563,34 +562,35 @@ ctld.i18n["es"]["Troop Transport"] = "Transporte de tropas" ctld.i18n["es"]["Unload / Extract Troops"] = "Descargar/Extraer tropas" ctld.i18n["es"]["Next page"] = "Página siguiente" ctld.i18n["es"]["Load "] = "Cargar " -ctld.i18n["es"]["Vehicle / FOB Transport"] = "Transporte Vehículo / FOB" -ctld.i18n["es"]["Vehicle / FOB Crates / Drone"] = "Cajas Vehículo / FOB / Dron" +ctld.i18n["es"]["Vehicle / FOB Transport"] = "Transporte de Vehículo / FOB" +ctld.i18n["es"]["Vehicle / FOB Crates / Drone"] = "Cajas de Vehículo / FOB / Dron" ctld.i18n["es"]["Unload Vehicles"] = "Descargar vehículos" ctld.i18n["es"]["Load / Extract Vehicles"] = "Cargar/Extraer vehículos" ctld.i18n["es"]["Load / Unload FOB Crate"] = "Cargar/Descargar caja FOB" +ctld.i18n["es"]["Repack Vehicles"] = "Reenvolver vehículos" ctld.i18n["es"]["CTLD Commands"] = "Comandos CTLD" ctld.i18n["es"]["CTLD"] = "CTLD" ctld.i18n["es"]["Check Cargo"] = "Verificar carga" ctld.i18n["es"]["Load Nearby Crate(s)"] = "Cargar caja(s) cercana(s)" -ctld.i18n["es"]["Unpack Any Crate"] = "Desempaquetar las cajas" -ctld.i18n["es"]["Drop Crate(s)"] = "Descargar caja(s)" +ctld.i18n["es"]["Unpack Any Crate"] = "Desempaquetar cajas" +ctld.i18n["es"]["Drop Crate(s)"] = "Soltar caja(s)" ctld.i18n["es"]["List Nearby Crates"] = "Enumerar cajas cercanas" ctld.i18n["es"]["List FOBs"] = "Enumerar FOBs" ctld.i18n["es"]["List Beacons"] = "Enumerar balizas" ctld.i18n["es"]["List Radio Beacons"] = "Enumerar radiobalizas" ctld.i18n["es"]["Smoke Markers"] = "Marcadores de humo" -ctld.i18n["es"]["Drop Red Smoke"] = "Soltar humo rojo" -ctld.i18n["es"]["Drop Blue Smoke"] = "Soltar humo azul" -ctld.i18n["es"]["Drop Orange Smoke"] = "Soltar humo naranja" -ctld.i18n["es"]["Drop Green Smoke"] = "Soltar humo verde" -ctld.i18n["es"]["Drop Beacon"] = "Soltar baliza" +ctld.i18n["es"]["Drop Red Smoke"] = "Lanzar humo rojo" +ctld.i18n["es"]["Drop Blue Smoke"] = "Lanzar humo azul" +ctld.i18n["es"]["Drop Orange Smoke"] = "Lanzar humo naranja" +ctld.i18n["es"]["Drop Green Smoke"] = "Lanzar humo verde" +ctld.i18n["es"]["Drop Beacon"] = "Desplegar baliza" ctld.i18n["es"]["Radio Beacons"] = "Balizas de radio" -ctld.i18n["es"]["Remove Closest Beacon"] = "Quitar baliza cercana" +ctld.i18n["es"]["Remove Closest Beacon"] = "Quitar la baliza mas cercana" ctld.i18n["es"]["JTAC Status"] = "Estado de JTAC" ctld.i18n["es"]["DISABLE "] = "DESHABILITAR " ctld.i18n["es"]["ENABLE "] = "HABILITAR " ctld.i18n["es"]["REQUEST "] = "SOLICITUD " -ctld.i18n["es"]["Reset TGT Selection"] = "Restablecer selección TGT" +ctld.i18n["es"]["Reset TGT Selection"] = "Restablecer selección de objetivo" -- F10 RECON menus ctld.i18n["es"]["RECON"] = "RECONOCIMIENTO" ctld.i18n["es"]["Show targets in LOS (refresh)"] = "Marcar objetivos visibles en el mapa F10" diff --git a/CTLD.lua b/CTLD.lua index c821cd0..34d5130 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1,44 +1,46 @@ --[[ - Combat Troop and Logistics Drop + Combat Troop and Logistics Drop - Allows Huey, Mi-8 and C130 to transport troops internally and Helicopters to transport Logistic / Vehicle units to the field via sling-loads - without requiring external mods. + Allows Huey, Mi-8 and C130 to transport troops internally and Helicopters to transport Logistic / Vehicle units to the field via sling-loads + without requiring external mods. - Supports all of the original CTTS functionality such as AI auto troop load and unload as well as group spawning and preloading of troops into units. + Supports all of the original CTTS functionality such as AI auto troop load and unload as well as group spawning and preloading of troops into units. - Supports deployment of Auto Lasing JTAC to the field + Supports deployment of Auto Lasing JTAC to the field - See https://github.com/ciribob/DCS-CTLD for a user manual and the latest version + See https://github.com/ciribob/DCS-CTLD for a user manual and the latest version - Contributors: - - Steggles - https://github.com/Bob7heBuilder - - mvee - https://github.com/mvee - - jmontleon - https://github.com/jmontleon - - emilianomolina - https://github.com/emilianomolina - - davidp57 - https://github.com/veaf - - Queton1-1 - https://github.com/Queton1-1 - - Proxy404 - https://github.com/Proxy404 - - atcz - https://github.com/atcz - - marcos2221- https://github.com/marcos2221 - - FullGas1 - https://github.com/FullGas1 (i18n concept, FR and SP translations) + Contributors: + - Steggles - https://github.com/Bob7heBuilder + - mvee - https://github.com/mvee + - jmontleon - https://github.com/jmontleon + - emilianomolina - https://github.com/emilianomolina + - davidp57 - https://github.com/veaf + - Queton1-1 - https://github.com/Queton1-1 + - Proxy404 - https://github.com/Proxy404 + - atcz - https://github.com/atcz + - marcos2221- https://github.com/marcos2221 + - FullGas1 - https://github.com/FullGas1 (i18n concept, FR and SP translations) - Add [issues](https://github.com/ciribob/DCS-CTLD/issues) to the GitHub repository if you want to report a bug or suggest a new feature. + Add [issues](https://github.com/ciribob/DCS-CTLD/issues) to the GitHub repository if you want to report a bug or suggest a new feature. - Contact Zip [on Discord](https://discordapp.com/users/421317390807203850) or [on Github](https://github.com/davidp57) if you need help or want to have a friendly chat. + Contact Zip [on Discord](https://discordapp.com/users/421317390807203850) or [on Github](https://github.com/davidp57) if you need help or want to have a friendly chat. - Send beers (or kind messages) to Ciribob [on Discord](https://discordapp.com/users/204712384747536384), he's the reason we have CTLD ^^ + Send beers (or kind messages) to Ciribob [on Discord](https://discordapp.com/users/204712384747536384), he's the reason we have CTLD ^^ ]] -if not ctld then -- should be defined first by CTLD-i18n.lua, but just in case it's an old mission, let's keep it here - trigger.action.outText("\n\n** HEY MISSION-DESIGNER! **\n\nCTLD-i18n has not been loaded!\n\nMake sure CTLD-i18n is loaded\n*before* running this script!\n\nIt contains all the translations!\n", 10) - ctld = {} -- DONT REMOVE! +if not ctld then -- should be defined first by CTLD-i18n.lua, but just in case it's an old mission, let's keep it here + trigger.action.outText( + "\n\n** HEY MISSION-DESIGNER! **\n\nCTLD-i18n has not been loaded!\n\nMake sure CTLD-i18n is loaded\n*before* running this script!\n\nIt contains all the translations!\n", + 10) + ctld = {} -- DONT REMOVE! end --- Identifier. All output in DCS.log will start with this. ctld.Id = "CTLD - " --- Version. -ctld.Version = "1.4.0" +ctld.Version = "1.5.0" -- To add debugging messages to dcs.log, change the following log levels to `true`; `Debug` is less detailed than `Trace` ctld.Debug = false @@ -52,25 +54,26 @@ ctld.dontInitialize = false -- if true, ctld.initialize() will not run; instead, -- If you want to change the language replace "en" with the language you want to use ---======== ENGLISH - the reference =========================================================================== +--======== ENGLISH - the reference =========================================================================== ctld.i18n_lang = "en" ---======== FRENCH - FRANCAIS ================================================================================= +--======== FRENCH - FRANCAIS ================================================================================= --ctld.i18n_lang = "fr" ---====== SPANISH : ESPAÑOL ==================================================================================== +--====== SPANISH : ESPAÑOL ==================================================================================== --ctld.i18n_lang = "es" ---====== Korean : 한국어 ==================================================================================== +--====== Korean : 한국어 ==================================================================================== --ctld.i18n_lang = "ko" -if not ctld.i18n then -- should be defined first by CTLD-i18n.lua, but just in case it's an old mission, let's keep it here - ctld.i18n = {} -- DONT REMOVE! +if not ctld.i18n then -- should be defined first by CTLD-i18n.lua, but just in case it's an old mission, let's keep it here + ctld.i18n = {} -- DONT REMOVE! end -- This is the default language -- If a string is not found in the current language then it will default to this language -- Note that no translation is provided for this language (obviously) but that we'll maintain this table to help the translators. ctld.i18n["en"] = {} -ctld.i18n["en"].translation_version = "1.4" -- make sure that all the translations are compatible with this version of the english language texts -local lang="en";env.info(string.format("I - CTLD.i18n_translate: Loading %s language version %s", lang, tostring(ctld.i18n[lang].translation_version))) +ctld.i18n["en"].translation_version = "1.6" -- make sure that all the translations are compatible with this version of the english language texts +local lang = "en"; env.info(string.format("I - CTLD.i18n_translate: Loading %s language version %s", lang, + tostring(ctld.i18n[lang].translation_version))) --- groups names ctld.i18n["en"]["Standard Group"] = "" @@ -209,8 +212,6 @@ ctld.i18n["en"]["FOB Crate dropped back to base"] = "" ctld.i18n["en"]["FOB Crate Loaded"] = "" ctld.i18n["en"]["%1 loaded a FOB Crate ready for delivery!"] = "" ctld.i18n["en"]["There are no friendly logistic units nearby to load a FOB crate from!"] = "" -ctld.i18n["en"]["You already have troops onboard."] = "" -ctld.i18n["en"]["You already have vehicles onboard."] = "" ctld.i18n["en"]["This area has no more reinforcements available!"] = "" ctld.i18n["en"]["You are not in a pickup zone and no one is nearby to extract"] = "" ctld.i18n["en"]["You are not in a pickup zone"] = "" @@ -218,6 +219,7 @@ ctld.i18n["en"]["No one to unload"] = "" ctld.i18n["en"]["Dropped troops back to base"] = "" ctld.i18n["en"]["Dropped vehicles back to base"] = "" ctld.i18n["en"]["You already have troops onboard."] = "" +ctld.i18n["en"]["Count Infantries limit in the mission reached, you can't load more troops"] = "" ctld.i18n["en"]["You already have vehicles onboard."] = "" ctld.i18n["en"]["Sorry - The group of %1 is too large to fit. \n\nLimit is %2 for %3"] = "" ctld.i18n["en"]["%1 extracted troops in %2 from combat"] = "" @@ -230,7 +232,8 @@ ctld.i18n["en"]["1 FOB Crate oboard (%1 kg)\n"] = "" ctld.i18n["en"]["%1 crate onboard (%2 kg)\n"] = "" ctld.i18n["en"]["Total weight of cargo : %1 kg\n"] = "" ctld.i18n["en"]["No cargo."] = "" -ctld.i18n["en"]["Hovering above %1 crate. \n\nHold hover for %2 seconds! \n\nIf the countdown stops you're too far away!"] = "" +ctld.i18n["en"]["Hovering above %1 crate. \n\nHold hover for %2 seconds! \n\nIf the countdown stops you're too far away!"] = +"" ctld.i18n["en"]["Loaded %1 crate!"] = "" ctld.i18n["en"]["Too low to hook %1 crate.\n\nHold hover for %2 seconds"] = "" ctld.i18n["en"]["Too high to hook %1 crate.\n\nHold hover for %2 seconds"] = "" @@ -251,11 +254,16 @@ ctld.i18n["en"]["%1 successfully deployed %2 to the field"] = "" ctld.i18n["en"]["No friendly crates close enough to unpack, or crate too close to aircraft."] = "" ctld.i18n["en"]["Finished building FOB! Crates and Troops can now be picked up."] = "" ctld.i18n["en"]["Finished building FOB! Crates can now be picked up."] = "" -ctld.i18n["en"]["%1 started building FOB using %2 FOB crates, it will be finished in %3 seconds.\nPosition marked with smoke."] = "" -ctld.i18n["en"]["Cannot build FOB!\n\nIt requires %1 Large FOB crates ( 3 small FOB crates equal 1 large FOB Crate) and there are the equivalent of %2 large FOB crates nearby\n\nOr the crates are not within 750m of each other"] = "" -ctld.i18n["en"]["You are not currently transporting any crates. \n\nTo Pickup a crate, hover for %1 seconds above the crate or land and use F10 Crate Commands."] = "" -ctld.i18n["en"]["You are not currently transporting any crates. \n\nTo Pickup a crate, hover for %1 seconds above the crate."] = "" -ctld.i18n["en"]["You are not currently transporting any crates. \n\nTo Pickup a crate, land and use F10 Crate Commands to load one."] = "" +ctld.i18n["en"]["%1 started building FOB using %2 FOB crates, it will be finished in %3 seconds.\nPosition marked with smoke."] = +"" +ctld.i18n["en"]["Cannot build FOB!\n\nIt requires %1 Large FOB crates ( 3 small FOB crates equal 1 large FOB Crate) and there are the equivalent of %2 large FOB crates nearby\n\nOr the crates are not within 750m of each other"] = +"" +ctld.i18n["en"]["You are not currently transporting any crates. \n\nTo Pickup a crate, hover for %1 seconds above the crate or land and use F10 Crate Commands."] = +"" +ctld.i18n["en"]["You are not currently transporting any crates. \n\nTo Pickup a crate, hover for %1 seconds above the crate."] = +"" +ctld.i18n["en"]["You are not currently transporting any crates. \n\nTo Pickup a crate, land and use F10 Crate Commands to load one."] = +"" ctld.i18n["en"]["%1 crate has been safely unhooked and is at your %2 o'clock"] = "" ctld.i18n["en"]["%1 crate has been safely dropped below you"] = "" ctld.i18n["en"]["You were too high! The crate has been destroyed"] = "" @@ -274,7 +282,8 @@ ctld.i18n["en"]["%1 successfully deployed a full %2 in the field. \n\nAA Active ctld.i18n["en"]["%1 successfully repaired a full %2 in the field."] = "" ctld.i18n["en"]["Cannot repair %1. No damaged %2 within 300m"] = "" ctld.i18n["en"]["%1 successfully deployed %2 to the field using %3 crates."] = "" -ctld.i18n["en"]["Cannot build %1!\n\nIt requires %2 crates and there are %3 \n\nOr the crates are not within 300m of each other"] = "" +ctld.i18n["en"]["Cannot build %1!\n\nIt requires %2 crates and there are %3 \n\nOr the crates are not within 300m of each other"] = +"" ctld.i18n["en"]["%1 dropped %2 smoke."] = "" --- JTAC messages @@ -316,10 +325,11 @@ ctld.i18n["en"]["Unload / Extract Troops"] = "" ctld.i18n["en"]["Next page"] = "" ctld.i18n["en"]["Load "] = "" ctld.i18n["en"]["Vehicle / FOB Transport"] = "" -ctld.i18n["en"]["Vehicle / FOB Crates / Drone"] = "" +ctld.i18n["en"]["Crates: Vehicle / FOB / Drone"] = "" ctld.i18n["en"]["Unload Vehicles"] = "" ctld.i18n["en"]["Load / Extract Vehicles"] = "" ctld.i18n["en"]["Load / Unload FOB Crate"] = "" +ctld.i18n["en"]["Repack Vehicles"] = "" ctld.i18n["en"]["CTLD Commands"] = "" ctld.i18n["en"]["CTLD"] = "" ctld.i18n["en"]["Check Cargo"] = "" @@ -358,7 +368,8 @@ function ctld.i18n_translate(text, ...) local _text if not ctld.i18n[ctld.i18n_lang] then - env.info(string.format(" E - CTLD.i18n_translate: Language %s not found, defaulting to 'en'", tostring(ctld.i18n_lang))) + env.info(string.format(" E - CTLD.i18n_translate: Language %s not found, defaulting to 'en'", + tostring(ctld.i18n_lang))) _text = ctld.i18n["en"][text] else _text = ctld.i18n[ctld.i18n_lang][text] @@ -376,7 +387,7 @@ function ctld.i18n_translate(text, ...) if arg and arg.n and arg.n > 0 then local _args = {} - for i=1,arg.n do + for i = 1, arg.n do _args[i] = tostring(arg[i]) or "" end for i = 1, #_args do @@ -388,35 +399,37 @@ function ctld.i18n_translate(text, ...) end -- ************************************************************************ --- ********************* USER CONFIGURATION ****************************** +-- ********************* USER CONFIGURATION ****************************** -- ************************************************************************ -ctld.staticBugWorkaround = false -- DCS had a bug where destroying statics would cause a crash. If this happens again, set this to TRUE +ctld.staticBugWorkaround = false -- DCS had a bug where destroying statics would cause a crash. If this happens again, set this to TRUE -ctld.disableAllSmoke = false -- if true, all smoke is diabled at pickup and drop off zones regardless of settings below. Leave false to respect settings below +ctld.disableAllSmoke = false -- if true, all smoke is diabled at pickup and drop off zones regardless of settings below. Leave false to respect settings below -- Allow units to CTLD by aircraft type and not by pilot name - this is done everytime a player enters a new unit ctld.addPlayerAircraftByType = true -ctld.hoverPickup = true -- if set to false you can load crates with the F10 menu instead of hovering... Only if not using real crates! +ctld.hoverPickup = true -- if set to false you can load crates with the F10 menu instead of hovering... Only if not using real crates! ctld.loadCrateFromMenu = true -- if set to true, you can load crates with the F10 menu OR hovering, in case of using choppers and planes for example. -ctld.enableCrates = true -- if false, Helis will not be able to spawn or unpack crates so will be normal CTTS -ctld.enableAllCrates = true -- if false, the "all crates" menu items will not be displayed -ctld.slingLoad = false -- if false, crates can be used WITHOUT slingloading, by hovering above the crate, simulating slingloading but not the weight... +ctld.enableCrates = true -- if false, Helis will not be able to spawn or unpack crates so will be normal CTTS +ctld.enableAllCrates = true -- if false, the "all crates" menu items will not be displayed +ctld.slingLoad = false -- if false, crates can be used WITHOUT slingloading, by hovering above the crate, simulating slingloading but not the weight... -- There are some bug with Sling-loading that can cause crashes, if these occur set slingLoad to false -- to use the other method. --- Set staticBugFix to FALSE if use set ctld.slingLoad to TRUE -ctld.enableSmokeDrop = true -- if false, helis and c-130 will not be able to drop smoke -ctld.maxExtractDistance = 125 -- max distance from vehicle to troops to allow a group extraction -ctld.maximumDistanceLogistic = 200 -- max distance from vehicle to logistics to allow a loading or spawning operation -ctld.maximumSearchDistance = 4000 -- max distance for troops to search for enemy -ctld.maximumMoveDistance = 2000 -- max distance for troops to move from drop point if no enemy is nearby -ctld.minimumDeployDistance = 1000 -- minimum distance from a friendly pickup zone where you can deploy a crate -ctld.numberOfTroops = 10 -- default number of troops to load on a transport heli or C-130 - -- also works as maximum size of group that'll fit into a helicopter unless overridden -ctld.enableFastRopeInsertion = true -- allows you to drop troops by fast rope -ctld.fastRopeMaximumHeight = 18.28 -- in meters which is 60 ft max fast rope (not rappell) safe height -ctld.vehiclesForTransportRED = { "BRDM-2", "BTR_D" } -- vehicles to load onto Il-76 - Alternatives {"Strela-1 9P31","BMP-1"} +-- Set staticBugFix to FALSE if use set ctld.slingLoad to TRUE +ctld.enableSmokeDrop = true -- if false, helis and c-130 will not be able to drop smoke +ctld.maxExtractDistance = 125 -- max distance from vehicle to troops to allow a group extraction +ctld.maximumDistanceLogistic = 200 -- max distance from vehicle to logistics to allow a loading or spawning operation +ctld.enableRepackingVehicles = true -- if true, vehicles can be repacked into crates +ctld.maximumDistanceRepackableUnitsSearch = 200 -- max distance from transportUnit to search force repackable units in meters +ctld.maximumSearchDistance = 4000 -- max distance for troops to search for enemy +ctld.maximumMoveDistance = 2000 -- max distance for troops to move from drop point if no enemy is nearby +ctld.minimumDeployDistance = 1000 -- minimum distance from a friendly pickup zone where you can deploy a crate +ctld.numberOfTroops = 10 -- default number of troops to load on a transport heli or C-130 +-- also works as maximum size of group that'll fit into a helicopter unless overridden +ctld.enableFastRopeInsertion = true -- allows you to drop troops by fast rope +ctld.fastRopeMaximumHeight = 18.28 -- in meters which is 60 ft max fast rope (not rappell) safe height +ctld.vehiclesForTransportRED = { "BRDM-2", "BTR_D" } -- vehicles to load onto Il-76 - Alternatives {"Strela-1 9P31","BMP-1"} ctld.vehiclesForTransportBLUE = { "M1045 HMMWV TOW", "M1043 HMMWV Armament" } -- vehicles to load onto c130 - Alternatives {"M1128 Stryker MGS","M1097 Avenger"} ctld.vehiclesWeight = { ["BRDM-2"] = 7000, @@ -426,30 +439,33 @@ ctld.vehiclesWeight = { } ctld.spawnRPGWithCoalition = true --spawns a friendly RPG unit with Coalition forces -ctld.spawnStinger = false -- spawns a stinger / igla soldier with a group of 6 or more soldiers! -ctld.enabledFOBBuilding = true -- if true, you can load a crate INTO a C-130 than when unpacked creates a Forward Operating Base (FOB) which is a new place to spawn (crates) and carry crates from - -- In future i'd like it to be a FARP but so far that seems impossible... - -- You can also enable troop Pickup at FOBS +ctld.spawnStinger = false -- spawns a stinger / igla soldier with a group of 6 or more soldiers! +ctld.enabledFOBBuilding = true -- if true, you can load a crate INTO a C-130 than when unpacked creates a Forward Operating Base (FOB) which is a new place to spawn (crates) and carry crates from +-- In future i'd like it to be a FARP but so far that seems impossible... +-- You can also enable troop Pickup at FOBS ctld.cratesRequiredForFOB = 3 -- The amount of crates required to build a FOB. Once built, helis can spawn crates at this outpost to be carried and deployed in another area. -- The large crates can only be loaded and dropped by large aircraft, like the C-130 and listed in ctld.vehicleTransportEnabled -- Small FOB crates can be moved by helicopter. The FOB will require ctld.cratesRequiredForFOB larges crates and small crates are 1/3 of a large fob crate -- To build the FOB entirely out of small crates you will need ctld.cratesRequiredForFOB * 3 -ctld.troopPickupAtFOB = true -- if true, troops can also be picked up at a created FOB -ctld.buildTimeFOB = 120 --time in seconds for the FOB to be built -ctld.crateWaitTime = 40 -- time in seconds to wait before you can spawn another crate -ctld.forceCrateToBeMoved = true -- a crate must be picked up at least once and moved before it can be unpacked. Helps to reduce crate spam -ctld.radioSound = "beacon.ogg" -- the name of the sound file to use for the FOB radio beacons. If this isnt added to the mission BEACONS WONT WORK! -ctld.radioSoundFC3 = "beaconsilent.ogg" -- name of the second silent radio file, used so FC3 aircraft dont hear ALL the beacon noises... :) -ctld.deployedBeaconBattery = 30 -- the battery on deployed beacons will last for this number minutes before needing to be re-deployed -ctld.enabledRadioBeaconDrop = true -- if its set to false then beacons cannot be dropped by units -ctld.allowRandomAiTeamPickups = false -- Allows the AI to randomize the loading of infantry teams (specified below) at pickup zones +ctld.troopPickupAtFOB = true -- if true, troops can also be picked up at a created FOB +ctld.buildTimeFOB = 120 --time in seconds for the FOB to be built +ctld.crateWaitTime = 40 -- time in seconds to wait before you can spawn another crate +ctld.forceCrateToBeMoved = true -- a crate must be picked up at least once and moved before it can be unpacked. Helps to reduce crate spam +ctld.radioSound = "beacon.ogg" -- the name of the sound file to use for the FOB radio beacons. If this isnt added to the mission BEACONS WONT WORK! +ctld.radioSoundFC3 ="beaconsilent.ogg" -- name of the second silent radio file, used so FC3 aircraft dont hear ALL the beacon noises... :) +ctld.deployedBeaconBattery = 30 -- the battery on deployed beacons will last for this number minutes before needing to be re-deployed +ctld.enabledRadioBeaconDrop = true -- if its set to false then beacons cannot be dropped by units +ctld.allowRandomAiTeamPickups = false -- Allows the AI to randomize the loading of infantry teams (specified below) at pickup zones +-- Limit the dropping of infantry teams -- this limit control is inactive if ctld.nbLimitSpawnedTroops = {0, 0} ---- +ctld.nbLimitSpawnedTroops = {0, 0} -- {redLimitInfantryCount, blueLimitInfantryCount} when this cumulative number of troops is reached, no more troops can be loaded onboard +ctld.InfantryInGameCount = {0, 0} -- {redCoaInfantryCount, blueCoaInfantryCount} -- Simulated Sling load configuration -ctld.minimumHoverHeight = 7.5 -- Lowest allowable height for crate hover -ctld.maximumHoverHeight = 12.0 -- Highest allowable height for crate hover +ctld.minimumHoverHeight = 7.5 -- Lowest allowable height for crate hover +ctld.maximumHoverHeight = 12.0 -- Highest allowable height for crate hover ctld.maxDistanceFromCrate = 5.5 -- Maximum distance from from crate for hover -ctld.hoverTime = 10 -- Time to hold hover above a crate for loading in seconds +ctld.hoverTime = 10 -- Time to hold hover above a crate for loading in seconds -- end of Simulated Sling load configuration @@ -461,7 +477,7 @@ ctld.aaLaunchers = 3 -- controls how many launchers to add to the AA systems whe -- When this limit is hit, a player will still be able to get crates for an AA system, just unable -- to unpack them -ctld.AASystemLimitRED = 20 -- Red side limit +ctld.AASystemLimitRED = 20 -- Red side limit ctld.AASystemLimitBLUE = 20 -- Blue side limit -- Allows players to create systems using as many crates as they like @@ -471,26 +487,26 @@ ctld.AASystemCrateStacking = false --END AA SYSTEM CONFIG ------------------------------------ -- ***************** JTAC CONFIGURATION ***************** -ctld.JTAC_LIMIT_RED = 10 -- max number of JTAC Crates for the RED Side -ctld.JTAC_LIMIT_BLUE = 10 -- max number of JTAC Crates for the BLUE Side -ctld.JTAC_dropEnabled = true -- allow JTAC Crate spawn from F10 menu -ctld.JTAC_maxDistance = 10000 -- How far a JTAC can "see" in meters (with Line of Sight) -ctld.JTAC_smokeOn_RED = true -- enables marking of target with smoke for RED forces -ctld.JTAC_smokeOn_BLUE = true -- enables marking of target with smoke for BLUE forces -ctld.JTAC_smokeColour_RED = 4 -- RED side smoke colour -- Green = 0 , Red = 1, White = 2, Orange = 3, Blue = 4 -ctld.JTAC_smokeColour_BLUE = 1 -- BLUE side smoke colour -- Green = 0 , Red = 1, White = 2, Orange = 3, Blue = 4 -ctld.JTAC_smokeMarginOfError = 50 -- error that the JTAC is allowed to make when popping a smoke (in meters) -ctld.JTAC_smokeOffset_x = 0.0 -- distance in the X direction from target to smoke (meters) -ctld.JTAC_smokeOffset_y = 2.0 -- distance in the Y direction from target to smoke (meters) -ctld.JTAC_smokeOffset_z = 0.0 -- distance in the z direction from target to smoke (meters) -ctld.JTAC_jtacStatusF10 = true -- enables F10 JTAC Status menu -ctld.JTAC_location = true -- shows location of target in JTAC message -ctld.location_DMS = false -- shows coordinates as Degrees Minutes Seconds instead of Degrees Decimal minutes -ctld.JTAC_lock = "all" -- "vehicle" OR "troop" OR "all" forces JTAC to only lock vehicles or troops or all ground units -ctld.JTAC_allowStandbyMode = true -- if true, allow players to toggle lasing on/off +ctld.JTAC_LIMIT_RED = 10 -- max number of JTAC Crates for the RED Side +ctld.JTAC_LIMIT_BLUE = 10 -- max number of JTAC Crates for the BLUE Side +ctld.JTAC_dropEnabled = true -- allow JTAC Crate spawn from F10 menu +ctld.JTAC_maxDistance = 10000 -- How far a JTAC can "see" in meters (with Line of Sight) +ctld.JTAC_smokeOn_RED = true -- enables marking of target with smoke for RED forces +ctld.JTAC_smokeOn_BLUE = true -- enables marking of target with smoke for BLUE forces +ctld.JTAC_smokeColour_RED = 4 -- RED side smoke colour -- Green = 0 , Red = 1, White = 2, Orange = 3, Blue = 4 +ctld.JTAC_smokeColour_BLUE = 1 -- BLUE side smoke colour -- Green = 0 , Red = 1, White = 2, Orange = 3, Blue = 4 +ctld.JTAC_smokeMarginOfError = 50 -- error that the JTAC is allowed to make when popping a smoke (in meters) +ctld.JTAC_smokeOffset_x = 0.0 -- distance in the X direction from target to smoke (meters) +ctld.JTAC_smokeOffset_y = 2.0 -- distance in the Y direction from target to smoke (meters) +ctld.JTAC_smokeOffset_z = 0.0 -- distance in the z direction from target to smoke (meters) +ctld.JTAC_jtacStatusF10 = true -- enables F10 JTAC Status menu +ctld.JTAC_location = true -- shows location of target in JTAC message +ctld.location_DMS = false -- shows coordinates as Degrees Minutes Seconds instead of Degrees Decimal minutes +ctld.JTAC_lock = "all" -- "vehicle" OR "troop" OR "all" forces JTAC to only lock vehicles or troops or all ground units +ctld.JTAC_allowStandbyMode = true -- if true, allow players to toggle lasing on/off ctld.JTAC_laseSpotCorrections = true -- if true, each JTAC will have a special option (toggle on/off) available in it's menu to attempt to lead the target, taking into account current wind conditions and the speed of the target (particularily useful against moving heavy armor) -ctld.JTAC_allowSmokeRequest = true -- if true, allow players to request a smoke on target (temporary) -ctld.JTAC_allow9Line = true -- if true, allow players to ask for a 9Line (individual) for a specific JTAC's target +ctld.JTAC_allowSmokeRequest = true -- if true, allow players to request a smoke on target (temporary) +ctld.JTAC_allow9Line = true -- if true, allow players to ask for a 9Line (individual) for a specific JTAC's target -- ***************** Pickup, dropoff and waypoint zones ***************** @@ -505,110 +521,110 @@ ctld.JTAC_allow9Line = true -- if true, allow players to ask for a 9Line (indivi -- Flag Number - Optional last field. If set the current number of groups remaining can be obtained from the flag value --pickupZones = { "Zone name or Ship Unit Name", "smoke color", "limit (-1 unlimited)", "ACTIVE (yes/no)", "side (0 = Both sides / 1 = Red / 2 = Blue )", flag number (optional) } ctld.pickupZones = { - { "pickzone1", "blue", -1, "yes", 0 }, - { "pickzone2", "red", -1, "yes", 0 }, - { "pickzone3", "none", -1, "yes", 0 }, - { "pickzone4", "none", -1, "yes", 0 }, - { "pickzone5", "none", -1, "yes", 0 }, - { "pickzone6", "none", -1, "yes", 0 }, - { "pickzone7", "none", -1, "yes", 0 }, - { "pickzone8", "none", -1, "yes", 0 }, - { "pickzone9", "none", 5, "yes", 1 }, -- limits pickup zone 9 to 5 groups of soldiers or vehicles, only red can pick up - { "pickzone10", "none", 10, "yes", 2 }, -- limits pickup zone 10 to 10 groups of soldiers or vehicles, only blue can pick up + { "pickzone1", "blue", -1, "yes", 0 }, + { "pickzone2", "red", -1, "yes", 0 }, + { "pickzone3", "none", -1, "yes", 0 }, + { "pickzone4", "none", -1, "yes", 0 }, + { "pickzone5", "none", -1, "yes", 0 }, + { "pickzone6", "none", -1, "yes", 0 }, + { "pickzone7", "none", -1, "yes", 0 }, + { "pickzone8", "none", -1, "yes", 0 }, + { "pickzone9", "none", 5, "yes", 1 }, -- limits pickup zone 9 to 5 groups of soldiers or vehicles, only red can pick up + { "pickzone10", "none", 10, "yes", 2 }, -- limits pickup zone 10 to 10 groups of soldiers or vehicles, only blue can pick up - { "pickzone11", "blue", 20, "no", 2 }, -- limits pickup zone 11 to 20 groups of soldiers or vehicles, only blue can pick up. Zone starts inactive! - { "pickzone12", "red", 20, "no", 1 }, -- limits pickup zone 11 to 20 groups of soldiers or vehicles, only blue can pick up. Zone starts inactive! - { "pickzone13", "none", -1, "yes", 0 }, - { "pickzone14", "none", -1, "yes", 0 }, - { "pickzone15", "none", -1, "yes", 0 }, - { "pickzone16", "none", -1, "yes", 0 }, - { "pickzone17", "none", -1, "yes", 0 }, - { "pickzone18", "none", -1, "yes", 0 }, - { "pickzone19", "none", 5, "yes", 0 }, - { "pickzone20", "none", 10, "yes", 0, 1000 }, -- optional extra flag number to store the current number of groups available in + { "pickzone11", "blue", 20, "no", 2 }, -- limits pickup zone 11 to 20 groups of soldiers or vehicles, only blue can pick up. Zone starts inactive! + { "pickzone12", "red", 20, "no", 1 }, -- limits pickup zone 11 to 20 groups of soldiers or vehicles, only blue can pick up. Zone starts inactive! + { "pickzone13", "none", -1, "yes", 0 }, + { "pickzone14", "none", -1, "yes", 0 }, + { "pickzone15", "none", -1, "yes", 0 }, + { "pickzone16", "none", -1, "yes", 0 }, + { "pickzone17", "none", -1, "yes", 0 }, + { "pickzone18", "none", -1, "yes", 0 }, + { "pickzone19", "none", 5, "yes", 0 }, + { "pickzone20", "none", 10, "yes", 0, 1000 }, -- optional extra flag number to store the current number of groups available in - { "USA Carrier", "blue", 10, "yes", 0, 1001 }, -- instead of a Zone Name you can also use the UNIT NAME of a ship + { "USA Carrier", "blue", 10, "yes", 0, 1001 }, -- instead of a Zone Name you can also use the UNIT NAME of a ship } -- dropOffZones = {"name","smoke colour",0,side 1 = Red or 2 = Blue or 0 = Both sides} ctld.dropOffZones = { - { "dropzone1", "green", 2 }, - { "dropzone2", "blue", 2 }, - { "dropzone3", "orange", 2 }, - { "dropzone4", "none", 2 }, - { "dropzone5", "none", 1 }, - { "dropzone6", "none", 1 }, - { "dropzone7", "none", 1 }, - { "dropzone8", "none", 1 }, - { "dropzone9", "none", 1 }, - { "dropzone10", "none", 1 }, + { "dropzone1", "green", 2 }, + { "dropzone2", "blue", 2 }, + { "dropzone3", "orange", 2 }, + { "dropzone4", "none", 2 }, + { "dropzone5", "none", 1 }, + { "dropzone6", "none", 1 }, + { "dropzone7", "none", 1 }, + { "dropzone8", "none", 1 }, + { "dropzone9", "none", 1 }, + { "dropzone10", "none", 1 }, } ---wpZones = { "Zone name", "smoke color", "ACTIVE (yes/no)", "side (0 = Both sides / 1 = Red / 2 = Blue )", } +--wpZones = { "Zone name", "smoke color", "ACTIVE (yes/no)", "side (0 = Both sides / 1 = Red / 2 = Blue )", } ctld.wpZones = { - { "wpzone1", "green","yes", 2 }, - { "wpzone2", "blue","yes", 2 }, - { "wpzone3", "orange","yes", 2 }, - { "wpzone4", "none","yes", 2 }, - { "wpzone5", "none","yes", 2 }, - { "wpzone6", "none","yes", 1 }, - { "wpzone7", "none","yes", 1 }, - { "wpzone8", "none","yes", 1 }, - { "wpzone9", "none","yes", 1 }, - { "wpzone10", "none","no", 0 }, -- Both sides as its set to 0 + { "wpzone1", "green", "yes", 2 }, + { "wpzone2", "blue", "yes", 2 }, + { "wpzone3", "orange", "yes", 2 }, + { "wpzone4", "none", "yes", 2 }, + { "wpzone5", "none", "yes", 2 }, + { "wpzone6", "none", "yes", 1 }, + { "wpzone7", "none", "yes", 1 }, + { "wpzone8", "none", "yes", 1 }, + { "wpzone9", "none", "yes", 1 }, + { "wpzone10", "none", "no", 0 }, -- Both sides as its set to 0 } -- ******************** Transports names ********************** -- If ctld.addPlayerAircraftByType = True, comment or uncomment lines to allow aircraft's type carry CTLD ctld.aircraftTypeTable = { --%%%%% MODS %%%%% - --"Bronco-OV-10A", - --"Hercules", - --"SK-60", - --"UH-60L", - --"T-45", + --"Bronco-OV-10A", + --"Hercules", + --"SK-60", + --"UH-60L", + --"T-45", --%%%%% CHOPPERS %%%%% - --"Ka-50", - --"Ka-50_3", - "Mi-8MT", - "Mi-24P", - --"SA342L", - --"SA342M", - --"SA342Mistral", - --"SA342Minigun", - "UH-1H", - "CH-47Fbl1", + --"Ka-50", + --"Ka-50_3", + "Mi-8MT", + "Mi-24P", + --"SA342L", + --"SA342M", + --"SA342Mistral", + --"SA342Minigun", + "UH-1H", + "CH-47Fbl1", --%%%%% AIRCRAFTS %%%%% - --"C-101EB", - --"C-101CC", - --"Christen Eagle II", - --"L-39C", - --"L-39ZA", - --"MB-339A", - --"MB-339APAN", - --"Mirage-F1B", - --"Mirage-F1BD", - --"Mirage-F1BE", - --"Mirage-F1BQ", - --"Mirage-F1DDA", - --"Su-25T", - --"Yak-52", + --"C-101EB", + --"C-101CC", + --"Christen Eagle II", + --"L-39C", + --"L-39ZA", + --"MB-339A", + --"MB-339APAN", + --"Mirage-F1B", + --"Mirage-F1BD", + --"Mirage-F1BE", + --"Mirage-F1BQ", + --"Mirage-F1DDA", + --"Su-25T", + --"Yak-52", --%%%%% WARBIRDS %%%%% - --"Bf-109K-4", - --"Fw 190A8", - --"FW-190D9", - --"I-16", - --"MosquitoFBMkVI", - --"P-47D-30", - --"P-47D-40", - --"P-51D", - --"P-51D-30-NA", - --"SpitfireLFMkIX", - --"SpitfireLFMkIXCW", - --"TF-51D", + --"Bf-109K-4", + --"Fw 190A8", + --"FW-190D9", + --"I-16", + --"MosquitoFBMkVI", + --"P-47D-30", + --"P-47D-40", + --"P-51D", + --"P-51D-30-NA", + --"SpitfireLFMkIX", + --"SpitfireLFMkIXCW", + --"TF-51D", } -- Use any of the predefined names or set your own ones @@ -771,6 +787,7 @@ ctld.extractableGroups = { -- Use any of the predefined names or set your own ones -- When a logistic unit is destroyed, you will no longer be able to spawn crates +ctld.dynamicLogisticUnitsIndex = 0 -- This is the unit that will be spawned first and then subsequent units will be from the next in the list ctld.logisticUnits = { "logistic1", "logistic2", @@ -789,8 +806,9 @@ ctld.logisticUnits = { -- units db has all the names or you can extract a mission.miz file by making it a zip and looking -- in the contained mission file ctld.vehicleTransportEnabled = { - "76MD", -- the il-76 mod doesnt use a normal - sign so il-76md wont match... !!!! GRR + "76MD", -- the il-76 mod doesnt use a normal - sign so il-76md wont match... !!!! GRR "Hercules", + --"CH-47Fbl1", } -- ************** Units able to use DCS dynamic cargo system ****************** @@ -798,7 +816,8 @@ ctld.vehicleTransportEnabled = { -- Units listed here will spawn a cargo static that can be loaded with the standard DCS cargo system -- We will also use this to make modifications to the menu and other checks and messages ctld.dynamicCargoUnits = { - "CH-47Fbl1", + "CH-47Fbl1", + --"UH-1H", } -- ************** Maximum Units SETUP for UNITS ****************** @@ -816,51 +835,51 @@ ctld.unitLoadLimits = { -- ["SA342M"] = 4, --%%%%% MODS %%%%% - --["Bronco-OV-10A"] = 4, - ["Hercules"] = 30, - --["SK-60"] = 1, - ["UH-60L"] = 12, - --["T-45"] = 1, + --["Bronco-OV-10A"] = 4, + ["Hercules"] = 30, + --["SK-60"] = 1, + ["UH-60L"] = 12, + --["T-45"] = 1, --%%%%% CHOPPERS %%%%% - ["Mi-8MT"] = 16, - ["Mi-24P"] = 10, - --["SA342L"] = 4, - --["SA342M"] = 4, - --["SA342Mistral"] = 4, - --["SA342Minigun"] = 3, - ["UH-1H"] = 8, - ["CH-47Fbl1"] = 33, + ["Mi-8MT"] = 16, + ["Mi-24P"] = 10, + --["SA342L"] = 4, + --["SA342M"] = 4, + --["SA342Mistral"] = 4, + --["SA342Minigun"] = 3, + ["UH-1H"] = 8, + ["CH-47Fbl1"] = 33, --%%%%% AIRCRAFTS %%%%% - --["C-101EB"] = 1, - --["C-101CC"] = 1, - --["Christen Eagle II"] = 1, - --["L-39C"] = 1, - --["L-39ZA"] = 1, - --["MB-339A"] = 1, - --["MB-339APAN"] = 1, - --["Mirage-F1B"] = 1, - --["Mirage-F1BD"] = 1, - --["Mirage-F1BE"] = 1, - --["Mirage-F1BQ"] = 1, - --["Mirage-F1DDA"] = 1, - --["Su-25T"] = 1, - --["Yak-52"] = 1, + --["C-101EB"] = 1, + --["C-101CC"] = 1, + --["Christen Eagle II"] = 1, + --["L-39C"] = 1, + --["L-39ZA"] = 1, + --["MB-339A"] = 1, + --["MB-339APAN"] = 1, + --["Mirage-F1B"] = 1, + --["Mirage-F1BD"] = 1, + --["Mirage-F1BE"] = 1, + --["Mirage-F1BQ"] = 1, + --["Mirage-F1DDA"] = 1, + --["Su-25T"] = 1, + --["Yak-52"] = 1, --%%%%% WARBIRDS %%%%% - --["Bf-109K-4"] = 1, - --["Fw 190A8"] = 1, - --["FW-190D9"] = 1, - --["I-16"] = 1, - --["MosquitoFBMkVI"] = 1, - --["P-47D-30"] = 1, - --["P-47D-40"] = 1, - --["P-51D"] = 1, - --["P-51D-30-NA"] = 1, - --["SpitfireLFMkIX"] = 1, - --["SpitfireLFMkIXCW"] = 1, - --["TF-51D"] = 1, + --["Bf-109K-4"] = 1, + --["Fw 190A8"] = 1, + --["FW-190D9"] = 1, + --["I-16"] = 1, + --["MosquitoFBMkVI"] = 1, + --["P-47D-30"] = 1, + --["P-47D-40"] = 1, + --["P-51D"] = 1, + --["P-51D-30-NA"] = 1, + --["SpitfireLFMkIX"] = 1, + --["SpitfireLFMkIXCW"] = 1, + --["TF-51D"] = 1, } -- Put the name of the Unit you want to enable loading multiple crates @@ -893,53 +912,53 @@ ctld.unitActions = { -- ["SA342M"] = {crates=false, troops=true}, --%%%%% MODS %%%%% - --["Bronco-OV-10A"] = {crates=true, troops=true}, - ["Hercules"] = {crates=true, troops=true}, - ["SK-60"] = {crates=true, troops=true}, - ["UH-60L"] = {crates=true, troops=true}, - --["T-45"] = {crates=true, troops=true}, + --["Bronco-OV-10A"] = {crates=true, troops=true}, + ["Hercules"] = { crates = true, troops = true }, + ["SK-60"] = { crates = true, troops = true }, + ["UH-60L"] = { crates = true, troops = true }, + --["T-45"] = {crates=true, troops=true}, --%%%%% CHOPPERS %%%%% - --["Ka-50"] = {crates=true, troops=false}, - --["Ka-50_3"] = {crates=true, troops=false}, - ["Mi-8MT"] = {crates=true, troops=true}, - ["Mi-24P"] = {crates=true, troops=true}, - --["SA342L"] = {crates=false, troops=true}, - --["SA342M"] = {crates=false, troops=true}, - --["SA342Mistral"] = {crates=false, troops=true}, - --["SA342Minigun"] = {crates=false, troops=true}, - ["UH-1H"] = {crates=true, troops=true}, - ["CH-47Fbl1"] = {crates=true, troops=true}, + --["Ka-50"] = {crates=true, troops=false}, + --["Ka-50_3"] = {crates=true, troops=false}, + ["Mi-8MT"] = { crates = true, troops = true }, + ["Mi-24P"] = { crates = true, troops = true }, + --["SA342L"] = {crates=false, troops=true}, + --["SA342M"] = {crates=false, troops=true}, + --["SA342Mistral"] = {crates=false, troops=true}, + --["SA342Minigun"] = {crates=false, troops=true}, + ["UH-1H"] = { crates = true, troops = true }, + ["CH-47Fbl1"] = { crates = true, troops = true }, --%%%%% AIRCRAFTS %%%%% - --["C-101EB"] = {crates=true, troops=true}, - --["C-101CC"] = {crates=true, troops=true}, - --["Christen Eagle II"] = {crates=true, troops=true}, - --["L-39C"] = {crates=true, troops=true}, - --["L-39ZA"] = {crates=true, troops=true}, - --["MB-339A"] = {crates=true, troops=true}, - --["MB-339APAN"] = {crates=true, troops=true}, - --["Mirage-F1B"] = {crates=true, troops=true}, - --["Mirage-F1BD"] = {crates=true, troops=true}, - --["Mirage-F1BE"] = {crates=true, troops=true}, - --["Mirage-F1BQ"] = {crates=true, troops=true}, - --["Mirage-F1DDA"] = {crates=true, troops=true}, - --["Su-25T"]= {crates=true, troops=false}, - --["Yak-52"] = {crates=true, troops=true}, + --["C-101EB"] = {crates=true, troops=true}, + --["C-101CC"] = {crates=true, troops=true}, + --["Christen Eagle II"] = {crates=true, troops=true}, + --["L-39C"] = {crates=true, troops=true}, + --["L-39ZA"] = {crates=true, troops=true}, + --["MB-339A"] = {crates=true, troops=true}, + --["MB-339APAN"] = {crates=true, troops=true}, + --["Mirage-F1B"] = {crates=true, troops=true}, + --["Mirage-F1BD"] = {crates=true, troops=true}, + --["Mirage-F1BE"] = {crates=true, troops=true}, + --["Mirage-F1BQ"] = {crates=true, troops=true}, + --["Mirage-F1DDA"] = {crates=true, troops=true}, + --["Su-25T"]= {crates=true, troops=false}, + --["Yak-52"] = {crates=true, troops=true}, --%%%%% WARBIRDS %%%%% - --["Bf-109K-4"] = {crates=true, troops=false}, - --["Fw 190A8"] = {crates=true, troops=false}, - --["FW-190D9"] = {crates=true, troops=false}, - --["I-16"] = {crates=true, troops=false}, - --["MosquitoFBMkVI"] = {crates=true, troops=true}, - --["P-47D-30"] = {crates=true, troops=false}, - --["P-47D-40"] = {crates=true, troops=false}, - --["P-51D"] = {crates=true, troops=false}, - --["P-51D-30-NA"] = {crates=true, troops=false}, - --["SpitfireLFMkIX"] = {crates=true, troops=false}, - --["SpitfireLFMkIXCW"] = {crates=true, troops=false}, - --["TF-51D"] = {crates=true, troops=true}, + --["Bf-109K-4"] = {crates=true, troops=false}, + --["Fw 190A8"] = {crates=true, troops=false}, + --["FW-190D9"] = {crates=true, troops=false}, + --["I-16"] = {crates=true, troops=false}, + --["MosquitoFBMkVI"] = {crates=true, troops=true}, + --["P-47D-30"] = {crates=true, troops=false}, + --["P-47D-40"] = {crates=true, troops=false}, + --["P-51D"] = {crates=true, troops=false}, + --["P-51D-30-NA"] = {crates=true, troops=false}, + --["SpitfireLFMkIX"] = {crates=true, troops=false}, + --["SpitfireLFMkIXCW"] = {crates=true, troops=false}, + --["TF-51D"] = {crates=true, troops=true}, } -- ************** WEIGHT CALCULATIONS FOR INFANTRY GROUPS ****************** @@ -954,13 +973,13 @@ ctld.unitActions = { -- Mortar servants carry their tube and a few rounds (ctld.MORTAR_WEIGHT) ctld.SOLDIER_WEIGHT = 80 -- kg, will be randomized between 90% and 120% -ctld.KIT_WEIGHT = 20 -- kg -ctld.RIFLE_WEIGHT = 5 -- kg -ctld.MANPAD_WEIGHT = 18 -- kg -ctld.RPG_WEIGHT = 7.6 -- kg -ctld.MG_WEIGHT = 10 -- kg -ctld.MORTAR_WEIGHT = 26 -- kg -ctld.JTAC_WEIGHT = 15 -- kg +ctld.KIT_WEIGHT = 20 -- kg +ctld.RIFLE_WEIGHT = 5 -- kg +ctld.MANPAD_WEIGHT = 18 -- kg +ctld.RPG_WEIGHT = 7.6 -- kg +ctld.MG_WEIGHT = 10 -- kg +ctld.MORTAR_WEIGHT = 26 -- kg +ctld.JTAC_WEIGHT = 15 -- kg -- ************** INFANTRY GROUPS FOR PICKUP ****************** -- Unit Types @@ -974,21 +993,21 @@ ctld.JTAC_WEIGHT = 15 -- kg -- You can also add an optional coalition side to limit the group to one side -- for the side - 2 is BLUE and 1 is RED ctld.loadableGroups = { - {name = ctld.i18n_translate("Standard Group"), inf = 6, mg = 2, at = 2 }, -- will make a loadable group with 6 infantry, 2 MGs and 2 anti-tank for both coalitions - {name = ctld.i18n_translate("Anti Air"), inf = 2, aa = 3 }, - {name = ctld.i18n_translate("Anti Tank"), inf = 2, at = 6 }, - {name = ctld.i18n_translate("Mortar Squad"), mortar = 6 }, - {name = ctld.i18n_translate("JTAC Group"), inf = 4, jtac = 1 }, -- will make a loadable group with 4 infantry and a JTAC soldier for both coalitions - {name = ctld.i18n_translate("Single JTAC"), jtac = 1 }, -- will make a loadable group witha single JTAC soldier for both coalitions - {name = ctld.i18n_translate("2x - Standard Groups"), inf = 12, mg = 4, at = 4 }, - {name = ctld.i18n_translate("2x - Anti Air"), inf = 4, aa = 6 }, - {name = ctld.i18n_translate("2x - Anti Tank"), inf = 4, at = 12 }, - {name = ctld.i18n_translate("2x - Standard Groups + 2x Mortar"), inf = 12, mg = 4, at = 4, mortar = 12 }, - {name = ctld.i18n_translate("3x - Standard Groups"), inf = 18, mg = 6, at = 6 }, - {name = ctld.i18n_translate("3x - Anti Air"), inf = 6, aa = 9 }, - {name = ctld.i18n_translate("3x - Anti Tank"), inf = 6, at = 18 }, - {name = ctld.i18n_translate("3x - Mortar Squad"), mortar = 18}, - {name = ctld.i18n_translate("5x - Mortar Squad"), mortar = 30}, + { name = ctld.i18n_translate("Standard Group"), inf = 6, mg = 2, at = 2 }, -- will make a loadable group with 6 infantry, 2 MGs and 2 anti-tank for both coalitions + { name = ctld.i18n_translate("Anti Air"), inf = 2, aa = 3 }, + { name = ctld.i18n_translate("Anti Tank"), inf = 2, at = 6 }, + { name = ctld.i18n_translate("Mortar Squad"), mortar = 6 }, + { name = ctld.i18n_translate("JTAC Group"), inf = 4, jtac = 1 }, -- will make a loadable group with 4 infantry and a JTAC soldier for both coalitions + { name = ctld.i18n_translate("Single JTAC"), jtac = 1 }, -- will make a loadable group witha single JTAC soldier for both coalitions + { name = ctld.i18n_translate("2x - Standard Groups"), inf = 12, mg = 4, at = 4 }, + { name = ctld.i18n_translate("2x - Anti Air"), inf = 4, aa = 6 }, + { name = ctld.i18n_translate("2x - Anti Tank"), inf = 4, at = 12 }, + { name = ctld.i18n_translate("2x - Standard Groups + 2x Mortar"), inf = 12, mg = 4, at = 4, mortar = 12 }, + { name = ctld.i18n_translate("3x - Standard Groups"), inf = 18, mg = 6, at = 6 }, + { name = ctld.i18n_translate("3x - Anti Air"), inf = 6, aa = 9 }, + { name = ctld.i18n_translate("3x - Anti Tank"), inf = 6, at = 18 }, + { name = ctld.i18n_translate("3x - Mortar Squad"), mortar = 18 }, + { name = ctld.i18n_translate("5x - Mortar Squad"), mortar = 30 }, -- {name = ctld.i18n_translate("Mortar Squad Red"), inf = 2, mortar = 5, side =1 }, --would make a group loadable by RED only } @@ -1009,145 +1028,145 @@ ctld.spawnableCrates = { -- Some descriptions are filtered to determine if JTAC or not! --- BLUE - { weight = 1000.01, desc = ctld.i18n_translate("Humvee - MG"), unit = "M1043 HMMWV Armament", side = 2 }, --careful with the names as the script matches the desc to JTAC types - { weight = 1000.02, desc = ctld.i18n_translate("Humvee - TOW"), unit = "M1045 HMMWV TOW", side = 2, cratesRequired = 2 }, - { multiple = {1000.02, 1000.02}, desc = ctld.i18n_translate("Humvee - TOW - All crates"), side = 2 }, - { weight = 1000.03, desc = ctld.i18n_translate("Light Tank - MRAP"), unit="MaxxPro_MRAP", side = 2, cratesRequired = 2 }, - { multiple = {1000.03, 1000.03}, desc = ctld.i18n_translate("Light Tank - MRAP - All crates"), side = 2 }, - { weight = 1000.04, desc = ctld.i18n_translate("Med Tank - LAV-25"), unit="LAV-25", side = 2, cratesRequired = 3 }, - { multiple = {1000.04, 1000.04, 1000.04}, desc = ctld.i18n_translate("Med Tank - LAV-25 - All crates"), side = 2 }, - { weight = 1000.05, desc = ctld.i18n_translate("Heavy Tank - Abrams"), unit="M1A2C_SEP_V3", side = 2, cratesRequired = 4 }, - { multiple = {1000.05, 1000.05, 1000.05, 1000.05}, desc = ctld.i18n_translate("Heavy Tank - Abrams - All crates"), side = 2 }, + { weight = 1000.01, desc = ctld.i18n_translate("Humvee - MG"), unit = "M1043 HMMWV Armament", side = 2 }, --careful with the names as the script matches the desc to JTAC types + { weight = 1000.02, desc = ctld.i18n_translate("Humvee - TOW"), unit = "M1045 HMMWV TOW", side = 2, cratesRequired = 2 }, + { multiple = { 1000.02,1000.02 }, desc = ctld.i18n_translate("Humvee - TOW - All crates"), side = 2 }, + { weight = 1000.03, desc = ctld.i18n_translate("Light Tank - MRAP"), unit = "MaxxPro_MRAP", side = 2, cratesRequired = 2 }, + { multiple = { 1000.03, 1000.03 }, desc = ctld.i18n_translate("Light Tank - MRAP - All crates"), side = 2 }, + { weight = 1000.04, desc = ctld.i18n_translate("Med Tank - LAV-25"), unit = "LAV-25", side = 2, cratesRequired = 3 }, + { multiple = { 1000.04, 1000.04, 1000.04 }, desc = ctld.i18n_translate("Med Tank - LAV-25 - All crates"), side = 2 }, + { weight = 1000.05, desc = ctld.i18n_translate("Heavy Tank - Abrams"), unit = "M-1 Abrams", side = 2, cratesRequired = 4 }, + { multiple = { 1000.05, 1000.05, 1000.05, 1000.05 }, desc = ctld.i18n_translate("Heavy Tank - Abrams - All crates"), side = 2 }, --- RED - { weight = 1000.11, desc = ctld.i18n_translate("BTR-D"), unit = "BTR_D", side = 1 }, - { weight = 1000.12, desc = ctld.i18n_translate("BRDM-2"), unit = "BRDM-2", side = 1 }, + { weight = 1000.11, desc = ctld.i18n_translate("BTR-D"), unit = "BTR_D", side = 1 }, + { weight = 1000.12, desc = ctld.i18n_translate("BRDM-2"), unit = "BRDM-2", side = 1 }, -- need more redfor! }, ["Support"] = { --- BLUE - { weight = 1001.01, desc = ctld.i18n_translate("Hummer - JTAC"), unit = "Hummer", side = 2, cratesRequired = 2 }, -- used as jtac and unarmed, not on the crate list if JTAC is disabled - { multiple = {1001.01, 1001.01}, desc = ctld.i18n_translate("Hummer - JTAC - All crates"), side = 2 }, - { weight = 1001.02, desc = ctld.i18n_translate("M-818 Ammo Truck"), unit = "M 818", side = 2, cratesRequired = 2 }, - { multiple = {1001.02, 1001.02}, desc = ctld.i18n_translate("M-818 Ammo Truck - All crates"), side = 2 }, - { weight = 1001.03, desc = ctld.i18n_translate("M-978 Tanker"), unit = "M978 HEMTT Tanker", side = 2, cratesRequired = 2 }, - { multiple = {1001.03, 1001.03}, desc = ctld.i18n_translate("M-978 Tanker - All crates"), side = 2 }, + { weight = 1001.01, desc = ctld.i18n_translate("Hummer - JTAC"), unit = "Hummer", side = 2, cratesRequired = 2 }, -- used as jtac and unarmed, not on the crate list if JTAC is disabled + { multiple = { 1001.01, 1001.01 }, desc = ctld.i18n_translate("Hummer - JTAC - All crates"), side = 2 }, + { weight = 1001.02, desc = ctld.i18n_translate("M-818 Ammo Truck"), unit = "M 818", side = 2, cratesRequired = 2 }, + { multiple = { 1001.02, 1001.02 }, desc = ctld.i18n_translate("M-818 Ammo Truck - All crates"), side = 2 }, + { weight = 1001.03, desc = ctld.i18n_translate("M-978 Tanker"), unit = "M978 HEMTT Tanker", side = 2, cratesRequired = 2 }, + { multiple = { 1001.03, 1001.03 }, desc = ctld.i18n_translate("M-978 Tanker - All crates"), side = 2 }, - --- RED - { weight = 1001.11, desc = ctld.i18n_translate("SKP-11 - JTAC"), unit = "SKP-11", side = 1 }, -- used as jtac and unarmed, not on the crate list if JTAC is disabled - { weight = 1001.12, desc = ctld.i18n_translate("Ural-375 Ammo Truck"), unit = "Ural-375", side = 1, cratesRequired = 2 }, - { multiple = {1001.12, 1001.12}, desc = ctld.i18n_translate("Ural-375 Ammo Truck - All crates"), side = 1 }, - { weight = 1001.13, desc = ctld.i18n_translate("KAMAZ Ammo Truck"), unit = "KAMAZ Truck", side = 1, cratesRequired = 2 }, + --- RED + { weight = 1001.11, desc = ctld.i18n_translate("SKP-11 - JTAC"), unit = "SKP-11", side = 1 }, -- used as jtac and unarmed, not on the crate list if JTAC is disabled + { weight = 1001.12, desc = ctld.i18n_translate("Ural-375 Ammo Truck"), unit = "Ural-375", side = 1, cratesRequired = 2 }, + { multiple = { 1001.12, 1001.12 }, desc = ctld.i18n_translate("Ural-375 Ammo Truck - All crates"), side = 1 }, + { weight = 1001.13, desc = ctld.i18n_translate("KAMAZ Ammo Truck"), unit = "KAMAZ Truck", side = 1, cratesRequired = 2 }, --- Both - { weight = 1001.21, desc = ctld.i18n_translate("EWR Radar"), unit="FPS-117", cratesRequired = 3 }, - { multiple = {1001.21, 1001.21, 1001.21}, desc = ctld.i18n_translate("EWR Radar - All crates") }, - { weight = 1001.22, desc = ctld.i18n_translate("FOB Crate - Small"), unit = "FOB-SMALL" }, -- Builds a FOB! - requires 3 * ctld.cratesRequiredForFOB + { weight = 1001.21, desc = ctld.i18n_translate("EWR Radar"), unit = "FPS-117", cratesRequired = 3 }, + { multiple = { 1001.21, 1001.21, 1001.21 }, desc = ctld.i18n_translate("EWR Radar - All crates") }, + { weight = 1001.22, desc = ctld.i18n_translate("FOB Crate - Small"), unit = "FOB-SMALL" }, -- Builds a FOB! - requires 3 * ctld.cratesRequiredForFOB }, ["Artillery"] = { --- BLUE - { weight = 1002.01, desc = ctld.i18n_translate("MLRS"), unit = "MLRS", side=2, cratesRequired = 3 }, - { multiple = {1002.01, 1002.01, 1002.01}, desc = ctld.i18n_translate("MLRS - All crates"), side=2 }, - { weight = 1002.02, desc = ctld.i18n_translate("SpGH DANA"), unit = "SpGH_Dana", side=2, cratesRequired = 3 }, - { multiple = {1002.02, 1002.02, 1002.02}, desc = ctld.i18n_translate("SpGH DANA - All crates"), side=2 }, - { weight = 1002.03, desc = ctld.i18n_translate("T155 Firtina"), unit = "T155_Firtina", side=2, cratesRequired = 3 }, - { multiple = {1002.03, 1002.03, 1002.03}, desc = ctld.i18n_translate("T155 Firtina - All crates"), side=2 }, - { weight = 1002.04, desc = ctld.i18n_translate("Howitzer"), unit = "M-109", side=2, cratesRequired = 3 }, - { multiple = {1002.04, 1002.04, 1002.04}, desc = ctld.i18n_translate("Howitzer - All crates"), side=2 }, + { weight = 1002.01, desc = ctld.i18n_translate("MLRS"), unit = "MLRS", side = 2, cratesRequired = 3 }, + { multiple = { 1002.01, 1002.01, 1002.01 }, desc = ctld.i18n_translate("MLRS - All crates"), side = 2 }, + { weight = 1002.02, desc = ctld.i18n_translate("SpGH DANA"), unit = "SpGH_Dana", side = 2, cratesRequired = 3 }, + { multiple = { 1002.02, 1002.02, 1002.02 }, desc = ctld.i18n_translate("SpGH DANA - All crates"), side = 2 }, + { weight = 1002.03, desc = ctld.i18n_translate("T155 Firtina"), unit = "T155_Firtina", side = 2, cratesRequired = 3 }, + { multiple = { 1002.03, 1002.03, 1002.03 }, desc = ctld.i18n_translate("T155 Firtina - All crates"), side = 2 }, + { weight = 1002.04, desc = ctld.i18n_translate("Howitzer"), unit = "M-109", side = 2, cratesRequired = 3 }, + { multiple = { 1002.04, 1002.04, 1002.04 }, desc = ctld.i18n_translate("Howitzer - All crates"), side = 2 }, --- RED - { weight = 1002.11, desc = ctld.i18n_translate("SPH 2S19 Msta"), unit = "SAU Msta", side = 1, cratesRequired = 3 }, - { multiple = {1002.11, 1002.11, 1002.11}, desc = ctld.i18n_translate("SPH 2S19 Msta - All crates"), side=1 }, + { weight = 1002.11, desc = ctld.i18n_translate("SPH 2S19 Msta"), unit = "SAU Msta", side = 1, cratesRequired = 3 }, + { multiple = { 1002.11, 1002.11, 1002.11 }, desc = ctld.i18n_translate("SPH 2S19 Msta - All crates"), side = 1 }, }, ["SAM short range"] = { --- BLUE - { weight = 1003.01, desc = ctld.i18n_translate("M1097 Avenger"), unit = "M1097 Avenger", side = 2, cratesRequired = 3 }, - { multiple = {1003.01, 1003.01, 1003.01}, desc = ctld.i18n_translate("M1097 Avenger - All crates"), side=2 }, - { weight = 1003.02, desc = ctld.i18n_translate("M48 Chaparral"), unit = "M48 Chaparral", side = 2, cratesRequired = 2 }, - { multiple = {1003.02, 1003.02}, desc = ctld.i18n_translate("M48 Chaparral - All crates"), side=2 }, - { weight = 1003.03, desc = ctld.i18n_translate("Roland ADS"), unit = "Roland ADS", side = 2, cratesRequired = 3 }, - { multiple = {1003.03, 1003.03, 1003.03}, desc = ctld.i18n_translate("Roland ADS - All crates"), side=2 }, - { weight = 1003.04, desc = ctld.i18n_translate("Gepard AAA"), unit = "Gepard", side = 2, cratesRequired = 3 }, - { multiple = {1003.04, 1003.04, 1003.04}, desc = ctld.i18n_translate("Gepard AAA - All crates"), side=2 }, - { weight = 1003.05, desc = ctld.i18n_translate("LPWS C-RAM"), unit = "HEMTT_C-RAM_Phalanx", side = 2, cratesRequired = 3 }, - { multiple = {1003.05, 1003.05, 1003.05}, desc = ctld.i18n_translate("LPWS C-RAM - All crates"), side=2 }, + { weight = 1003.01, desc = ctld.i18n_translate("M1097 Avenger"), unit = "M1097 Avenger", side = 2, cratesRequired = 3 }, + { multiple = { 1003.01, 1003.01, 1003.01 }, desc = ctld.i18n_translate("M1097 Avenger - All crates"), side = 2 }, + { weight = 1003.02, desc = ctld.i18n_translate("M48 Chaparral"), unit = "M48 Chaparral", side = 2, cratesRequired = 2 }, + { multiple = { 1003.02, 1003.02 }, desc = ctld.i18n_translate("M48 Chaparral - All crates"), side = 2 }, + { weight = 1003.03, desc = ctld.i18n_translate("Roland ADS"), unit = "Roland ADS", side = 2, cratesRequired = 3 }, + { multiple = { 1003.03, 1003.03, 1003.03 }, desc = ctld.i18n_translate("Roland ADS - All crates"), side = 2 }, + { weight = 1003.04, desc = ctld.i18n_translate("Gepard AAA"), unit = "Gepard", side = 2, cratesRequired = 3 }, + { multiple = { 1003.04, 1003.04, 1003.04 }, desc = ctld.i18n_translate("Gepard AAA - All crates"), side = 2 }, + { weight = 1003.05, desc = ctld.i18n_translate("LPWS C-RAM"), unit = "HEMTT_C-RAM_Phalanx", side = 2, cratesRequired = 3 }, + { multiple = { 1003.05, 1003.05, 1003.05 }, desc = ctld.i18n_translate("LPWS C-RAM - All crates"), side = 2 }, --- RED - { weight = 1003.11, desc = ctld.i18n_translate("9K33 Osa"), unit = "Osa 9A33 ln", side = 1, cratesRequired = 3 }, - { multiple = {1003.11, 1003.11, 1003.11}, desc = ctld.i18n_translate("9K33 Osa - All crates"), side=1 }, - { weight = 1003.12, desc = ctld.i18n_translate("9P31 Strela-1"), unit = "Strela-1 9P31", side = 1, cratesRequired = 3 }, - { multiple = {1003.12, 1003.12, 1003.12}, desc = ctld.i18n_translate("9P31 Strela-1 - All crates"), side=1 }, - { weight = 1003.13, desc = ctld.i18n_translate("9K35M Strela-10"), unit = "Strela-10M3", side = 1, cratesRequired = 3 }, - { multiple = {1003.13, 1003.13, 1003.13}, desc = ctld.i18n_translate("9K35M Strela-10 - All crates"), side=1 }, - { weight = 1003.14, desc = ctld.i18n_translate("9K331 Tor"), unit = "Tor 9A331", side = 1, cratesRequired = 3 }, - { multiple = {1003.14, 1003.14, 1003.14}, desc = ctld.i18n_translate("9K331 Tor - All crates"), side=1 }, - { weight = 1003.15, desc = ctld.i18n_translate("2K22 Tunguska"), unit = "2S6 Tunguska", side = 1, cratesRequired = 3 }, - { multiple = {1003.15, 1003.15, 1003.15}, desc = ctld.i18n_translate("2K22 Tunguska - All crates"), side=1 }, + { weight = 1003.11, desc = ctld.i18n_translate("9K33 Osa"), unit = "Osa 9A33 ln", side = 1, cratesRequired = 3 }, + { multiple = { 1003.11, 1003.11, 1003.11 }, desc = ctld.i18n_translate("9K33 Osa - All crates"), side = 1 }, + { weight = 1003.12, desc = ctld.i18n_translate("9P31 Strela-1"), unit = "Strela-1 9P31", side = 1, cratesRequired = 3 }, + { multiple = { 1003.12, 1003.12, 1003.12 }, desc = ctld.i18n_translate("9P31 Strela-1 - All crates"), side = 1 }, + { weight = 1003.13, desc = ctld.i18n_translate("9K35M Strela-10"), unit = "Strela-10M3", side = 1, cratesRequired = 3 }, + { multiple = { 1003.13, 1003.13, 1003.13 }, desc = ctld.i18n_translate("9K35M Strela-10 - All crates"), side = 1 }, + { weight = 1003.14, desc = ctld.i18n_translate("9K331 Tor"), unit = "Tor 9A331", side = 1, cratesRequired = 3 }, + { multiple = { 1003.14, 1003.14, 1003.14 }, desc = ctld.i18n_translate("9K331 Tor - All crates"), side = 1 }, + { weight = 1003.15, desc = ctld.i18n_translate("2K22 Tunguska"), unit = "2S6 Tunguska", side = 1, cratesRequired = 3 }, + { multiple = { 1003.15, 1003.15, 1003.15 }, desc = ctld.i18n_translate("2K22 Tunguska - All crates"), side = 1 }, }, ["SAM mid range"] = { --- BLUE -- HAWK System - { weight = 1004.01, desc = ctld.i18n_translate("HAWK Launcher"), unit = "Hawk ln", side = 2}, - { weight = 1004.02, desc = ctld.i18n_translate("HAWK Search Radar"), unit = "Hawk sr", side = 2 }, - { weight = 1004.03, desc = ctld.i18n_translate("HAWK Track Radar"), unit = "Hawk tr", side = 2 }, - { weight = 1004.04, desc = ctld.i18n_translate("HAWK PCP"), unit = "Hawk pcp" , side = 2 }, - { weight = 1004.05, desc = ctld.i18n_translate("HAWK CWAR"), unit = "Hawk cwar" , side = 2 }, - { weight = 1004.06, desc = ctld.i18n_translate("HAWK Repair"), unit = "HAWK Repair" , side = 2 }, - { multiple = {1004.01, 1004.02, 1004.03}, desc = ctld.i18n_translate("HAWK - All crates"), side = 2 }, + { weight = 1004.01, desc = ctld.i18n_translate("HAWK Launcher"), unit = "Hawk ln", side = 2 }, + { weight = 1004.02, desc = ctld.i18n_translate("HAWK Search Radar"), unit = "Hawk sr", side = 2 }, + { weight = 1004.03, desc = ctld.i18n_translate("HAWK Track Radar"), unit = "Hawk tr", side = 2 }, + { weight = 1004.04, desc = ctld.i18n_translate("HAWK PCP"), unit = "Hawk pcp", side = 2 }, + { weight = 1004.05, desc = ctld.i18n_translate("HAWK CWAR"), unit = "Hawk cwar", side = 2 }, + { weight = 1004.06, desc = ctld.i18n_translate("HAWK Repair"), unit = "HAWK Repair", side = 2 }, + { multiple = { 1004.01, 1004.02, 1004.03 }, desc = ctld.i18n_translate("HAWK - All crates"), side = 2 }, -- End of HAWK -- NASAMS Sysyem - { weight = 1004.11, desc = ctld.i18n_translate("NASAMS Launcher 120C"), unit = "NASAMS_LN_C", side = 2}, - { weight = 1004.12, desc = ctld.i18n_translate("NASAMS Search/Track Radar"), unit = "NASAMS_Radar_MPQ64F1", side = 2 }, - { weight = 1004.13, desc = ctld.i18n_translate("NASAMS Command Post"), unit = "NASAMS_Command_Post", side = 2 }, - { weight = 1004.14, desc = ctld.i18n_translate("NASAMS Repair"), unit = "NASAMS Repair", side = 2 }, - { multiple = {1004.11, 1004.12, 1004.13}, desc = ctld.i18n_translate("NASAMS - All crates"), side = 2 }, + { weight = 1004.11, desc = ctld.i18n_translate("NASAMS Launcher 120C"), unit = "NASAMS_LN_C", side = 2 }, + { weight = 1004.12, desc = ctld.i18n_translate("NASAMS Search/Track Radar"), unit = "NASAMS_Radar_MPQ64F1", side = 2 }, + { weight = 1004.13, desc = ctld.i18n_translate("NASAMS Command Post"), unit = "NASAMS_Command_Post", side = 2 }, + { weight = 1004.14, desc = ctld.i18n_translate("NASAMS Repair"), unit = "NASAMS Repair", side = 2 }, + { multiple = { 1004.11, 1004.12, 1004.13 }, desc = ctld.i18n_translate("NASAMS - All crates"), side = 2 }, -- End of NASAMS --- RED -- KUB SYSTEM - { weight = 1004.21, desc = ctld.i18n_translate("KUB Launcher"), unit = "Kub 2P25 ln", side = 1}, - { weight = 1004.22, desc = ctld.i18n_translate("KUB Radar"), unit = "Kub 1S91 str", side = 1 }, - { weight = 1004.23, desc = ctld.i18n_translate("KUB Repair"), unit = "KUB Repair", side = 1}, - { multiple = {1004.21, 1004.22}, desc = ctld.i18n_translate("KUB - All crates"), side = 1 }, + { weight = 1004.21, desc = ctld.i18n_translate("KUB Launcher"), unit = "Kub 2P25 ln", side = 1 }, + { weight = 1004.22, desc = ctld.i18n_translate("KUB Radar"), unit = "Kub 1S91 str", side = 1 }, + { weight = 1004.23, desc = ctld.i18n_translate("KUB Repair"), unit = "KUB Repair", side = 1 }, + { multiple = { 1004.21, 1004.22 }, desc = ctld.i18n_translate("KUB - All crates"), side = 1 }, -- End of KUB -- BUK System - { weight = 1004.31, desc = ctld.i18n_translate("BUK Launcher"), unit = "SA-11 Buk LN 9A310M1", side = 1}, - { weight = 1004.32, desc = ctld.i18n_translate("BUK Search Radar"), unit = "SA-11 Buk SR 9S18M1", side = 1}, - { weight = 1004.33, desc = ctld.i18n_translate("BUK CC Radar"), unit = "SA-11 Buk CC 9S470M1", side = 1}, - { weight = 1004.34, desc = ctld.i18n_translate("BUK Repair"), unit = "BUK Repair", side = 1}, - { multiple = {1004.31, 1004.32, 1004.33}, desc = ctld.i18n_translate("BUK - All crates"), side = 1 }, + { weight = 1004.31, desc = ctld.i18n_translate("BUK Launcher"), unit = "SA-11 Buk LN 9A310M1", side = 1 }, + { weight = 1004.32, desc = ctld.i18n_translate("BUK Search Radar"), unit = "SA-11 Buk SR 9S18M1", side = 1 }, + { weight = 1004.33, desc = ctld.i18n_translate("BUK CC Radar"), unit = "SA-11 Buk CC 9S470M1", side = 1 }, + { weight = 1004.34, desc = ctld.i18n_translate("BUK Repair"), unit = "BUK Repair", side = 1 }, + { multiple = { 1004.31, 1004.32, 1004.33 }, desc = ctld.i18n_translate("BUK - All crates"), side = 1 }, -- END of BUK }, ["SAM long range"] = { --- BLUE -- Patriot System - { weight = 1005.01, desc = ctld.i18n_translate("Patriot Launcher"), unit = "Patriot ln", side = 2 }, - { weight = 1005.02, desc = ctld.i18n_translate("Patriot Radar"), unit = "Patriot str" , side = 2 }, - { weight = 1005.03, desc = ctld.i18n_translate("Patriot ECS"), unit = "Patriot ECS", side = 2 }, + { weight = 1005.01, desc = ctld.i18n_translate("Patriot Launcher"), unit = "Patriot ln", side = 2 }, + { weight = 1005.02, desc = ctld.i18n_translate("Patriot Radar"), unit = "Patriot str", side = 2 }, + { weight = 1005.03, desc = ctld.i18n_translate("Patriot ECS"), unit = "Patriot ECS", side = 2 }, -- { weight = 1005.04, desc = ctld.i18n_translate("Patriot ICC"), unit = "Patriot cp", side = 2 }, -- { weight = 1005.05, desc = ctld.i18n_translate("Patriot EPP"), unit = "Patriot EPP", side = 2 }, - { weight = 1005.06, desc = ctld.i18n_translate("Patriot AMG (optional)"), unit = "Patriot AMG" , side = 2 }, - { weight = 1005.07, desc = ctld.i18n_translate("Patriot Repair"), unit = "Patriot Repair" , side = 2 }, - { multiple = {1005.01, 1005.02, 1005.03}, desc = ctld.i18n_translate("Patriot - All crates"), side = 2 }, + { weight = 1005.06, desc = ctld.i18n_translate("Patriot AMG (optional)"), unit = "Patriot AMG", side = 2 }, + { weight = 1005.07, desc = ctld.i18n_translate("Patriot Repair"), unit = "Patriot Repair", side = 2 }, + { multiple = { 1005.01, 1005.02, 1005.03 }, desc = ctld.i18n_translate("Patriot - All crates"), side = 2 }, -- End of Patriot -- S-300 SYSTEM - { weight = 1005.11, desc = ctld.i18n_translate("S-300 Grumble TEL C"), unit = "S-300PS 5P85C ln", side = 1 }, - { weight = 1005.12, desc = ctld.i18n_translate("S-300 Grumble Flap Lid-A TR"), unit = "S-300PS 40B6M tr", side = 1 }, - { weight = 1005.13, desc = ctld.i18n_translate("S-300 Grumble Clam Shell SR"), unit = "S-300PS 40B6MD sr", side = 1 }, - { weight = 1005.14, desc = ctld.i18n_translate("S-300 Grumble Big Bird SR"), unit = "S-300PS 64H6E sr", side = 1 }, - { weight = 1005.15, desc = ctld.i18n_translate("S-300 Grumble C2"), unit = "S-300PS 54K6 cp", side = 1 }, - { weight = 1005.16, desc = ctld.i18n_translate("S-300 Repair"), unit = "S-300 Repair", side = 1 }, - { multiple = {1005.11, 1005.12, 1005.13, 1005.14, 1005.15}, desc = ctld.i18n_translate("Patriot - All crates"), side = 1 }, + { weight = 1005.11, desc = ctld.i18n_translate("S-300 Grumble TEL C"), unit = "S-300PS 5P85C ln", side = 1 }, + { weight = 1005.12, desc = ctld.i18n_translate("S-300 Grumble Flap Lid-A TR"), unit = "S-300PS 40B6M tr", side = 1 }, + { weight = 1005.13, desc = ctld.i18n_translate("S-300 Grumble Clam Shell SR"), unit = "S-300PS 40B6MD sr", side = 1 }, + { weight = 1005.14, desc = ctld.i18n_translate("S-300 Grumble Big Bird SR"), unit = "S-300PS 64H6E sr", side = 1 }, + { weight = 1005.15, desc = ctld.i18n_translate("S-300 Grumble C2"), unit = "S-300PS 54K6 cp", side = 1 }, + { weight = 1005.16, desc = ctld.i18n_translate("S-300 Repair"), unit = "S-300 Repair", side = 1 }, + { multiple = { 1005.11, 1005.12, 1005.13, 1005.14, 1005.15 }, desc = ctld.i18n_translate("Patriot - All crates"), side = 1 }, -- End of S-300 }, ["Drone"] = { --- BLUE MQ-9 Repear - { weight = 1006.01, desc = ctld.i18n_translate("MQ-9 Repear - JTAC"), unit = "MQ-9 Reaper", side = 2 }, + { weight = 1006.01, desc = ctld.i18n_translate("MQ-9 Repear - JTAC"), unit = "MQ-9 Reaper", side = 2 }, -- End of BLUE MQ-9 Repear --- RED MQ-1A Predator @@ -1158,8 +1177,8 @@ ctld.spawnableCrates = { ctld.spawnableCratesModels = { ["load"] = { - ["category"] = "Fortifications", - ["type"] = "Cargo04", + ["category"] = "Cargos", --"Fortifications" + ["type"] = "ammo_cargo", --"uh1h_cargo" --"Cargo04" ["canCargo"] = false, }, ["sling"] = { @@ -1169,64 +1188,64 @@ ctld.spawnableCratesModels = { ["canCargo"] = true }, ["dynamic"] = { - ["category"] = "Cargos", - ["type"] = "ammo_cargo", + ["category"] = "Cargos", + ["type"] = "ammo_cargo", ["canCargo"] = true } } --[[ Placeholder for different type of cargo containers. Let's say pipes and trunks, fuel for FOB building - ["shape_name"] = "ab-212_cargo", - ["type"] = "uh1h_cargo" --new type for the container previously used + ["shape_name"] = "ab-212_cargo", + ["type"] = "uh1h_cargo" --new type for the container previously used - ["shape_name"] = "ammo_box_cargo", - ["type"] = "ammo_cargo", + ["shape_name"] = "ammo_box_cargo", + ["type"] = "ammo_cargo", - ["shape_name"] = "barrels_cargo", - ["type"] = "barrels_cargo", + ["shape_name"] = "barrels_cargo", + ["type"] = "barrels_cargo", - ["shape_name"] = "bw_container_cargo", - ["type"] = "container_cargo", + ["shape_name"] = "bw_container_cargo", + ["type"] = "container_cargo", - ["shape_name"] = "f_bar_cargo", - ["type"] = "f_bar_cargo", + ["shape_name"] = "f_bar_cargo", + ["type"] = "f_bar_cargo", - ["shape_name"] = "fueltank_cargo", - ["type"] = "fueltank_cargo", + ["shape_name"] = "fueltank_cargo", + ["type"] = "fueltank_cargo", - ["shape_name"] = "iso_container_cargo", - ["type"] = "iso_container", + ["shape_name"] = "iso_container_cargo", + ["type"] = "iso_container", - ["shape_name"] = "iso_container_small_cargo", - ["type"] = "iso_container_small", + ["shape_name"] = "iso_container_small_cargo", + ["type"] = "iso_container_small", - ["shape_name"] = "oiltank_cargo", - ["type"] = "oiltank_cargo", + ["shape_name"] = "oiltank_cargo", + ["type"] = "oiltank_cargo", - ["shape_name"] = "pipes_big_cargo", - ["type"] = "pipes_big_cargo", + ["shape_name"] = "pipes_big_cargo", + ["type"] = "pipes_big_cargo", - ["shape_name"] = "pipes_small_cargo", - ["type"] = "pipes_small_cargo", + ["shape_name"] = "pipes_small_cargo", + ["type"] = "pipes_small_cargo", - ["shape_name"] = "tetrapod_cargo", - ["type"] = "tetrapod_cargo", + ["shape_name"] = "tetrapod_cargo", + ["type"] = "tetrapod_cargo", - ["shape_name"] = "trunks_long_cargo", - ["type"] = "trunks_long_cargo", + ["shape_name"] = "trunks_long_cargo", + ["type"] = "trunks_long_cargo", - ["shape_name"] = "trunks_small_cargo", - ["type"] = "trunks_small_cargo", -]]-- + ["shape_name"] = "trunks_small_cargo", + ["type"] = "trunks_small_cargo", +]] -- -- if the unit is on this list, it will be made into a JTAC when deployed -ctld.jtacUnitTypes = { - "SKP", "Hummer", -- there are some wierd encoding issues so if you write SKP-11 it wont match as the - sign is encoded differently... - "MQ", "RQ" --"MQ-9 Repear", "RQ-1A Predator"} +ctld.jtacUnitTypes = { + "SKP", "Hummer", -- there are some wierd encoding issues so if you write SKP-11 it wont match as the - sign is encoded differently... + "MQ", "RQ" --"MQ-9 Repear", "RQ-1A Predator"} } -ctld.jtacDroneRadius = 1000 -- JTAC offset radius in meters for orbiting drones -ctld.jtacDroneAltitude = 7000 -- JTAC altitude in meters for orbiting drones +ctld.jtacDroneRadius = 1000 -- JTAC offset radius in meters for orbiting drones +ctld.jtacDroneAltitude = 7000 -- JTAC altitude in meters for orbiting drones -- *************************************************************** -- **************** Mission Editor Functions ********************* -- *************************************************************** @@ -1250,10 +1269,10 @@ ctld.jtacDroneAltitude = 7000 -- JTAC altitude in meters for orbiting drones -- Spawns 1 machine gun, 2 anti tank, 3 anti air, 4 standard soldiers and 5 mortars -- function ctld.spawnGroupAtTrigger(_groupSide, _number, _triggerName, _searchRadius) - local _spawnTrigger = trigger.misc.getZone(_triggerName) -- trigger to use as reference position + local _spawnTrigger = trigger.misc.getZone(_triggerName) -- trigger to use as reference position if _spawnTrigger == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find trigger called %1",_triggerName), 10) + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find trigger called %1", _triggerName), 10) return end @@ -1285,7 +1304,6 @@ function ctld.spawnGroupAtTrigger(_groupSide, _number, _triggerName, _searchRadi end end - ----------------------------------------------------------------- -- Spawn group at a Vec3 Point and set them as extractable. Usage: -- ctld.spawnGroupAtPoint("groupside", number,Vec3 Point, radius) @@ -1304,7 +1322,6 @@ end -- ctld.spawnGroupAtPoint("blue", {mg=1,at=2,aa=3,inf=4,mortar=5}, {x=1,y=2,z=3}, 2000) -- Spawns 1 machine gun, 2 anti tank, 3 anti air, 4 standard soldiers and 5 mortars function ctld.spawnGroupAtPoint(_groupSide, _number, _point, _searchRadius) - local _country if _groupSide == "red" then _groupSide = 1 @@ -1329,23 +1346,19 @@ function ctld.spawnGroupAtPoint(_groupSide, _number, _point, _searchRadius) end end - -- Preloads a transport with troops or vehicles -- replaces any troops currently on board function ctld.preLoadTransport(_unitName, _number, _troops) - local _unit = ctld.getTransportUnit(_unitName) if _unit ~= nil then - -- will replace any units currently on board - -- if not ctld.troopsOnboard(_unit,_troops) then + -- if not ctld.troopsOnboard(_unit,_troops) then ctld.loadTroops(_unit, _troops, _number) - -- end + -- end end end - -- Continuously counts the number of crates in a zone and sets the value of the passed in flag -- to the count amount -- This means you can trigger actions based on the count and also trigger messages before the count is reached @@ -1353,7 +1366,7 @@ end -- This will now work for Mission Editor and Spawned Crates -- e.g. ctld.cratesInZone("DropZone1", 5) function ctld.cratesInZone(_zone, _flagNumber) - local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position + local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position if _triggerZone == nil then trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) @@ -1368,16 +1381,13 @@ function ctld.cratesInZone(_zone, _flagNumber) local _crateCount = 0 for _, _crates in pairs(_crateTables) do - for _crateName, _dontUse in pairs(_crates) do - --get crate local _crate = ctld.getCrateObject(_crateName) --in air seems buggy with crates so if in air is true, get the height above ground and the speed magnitude if _crate ~= nil and _crate:getLife() > 0 - and (ctld.inAir(_crate) == false) then - + and (ctld.inAir(_crate) == false) then local _dist = ctld.getDistance(_crate:getPoint(), _zonePos) if _dist <= _triggerZone.radius then @@ -1394,7 +1404,6 @@ function ctld.cratesInZone(_zone, _flagNumber) --retrigger in 5 seconds timer.scheduleFunction(function(_args) - ctld.cratesInZone(_args[1], _args[2]) end, { _zone, _flagNumber }, timer.getTime() + 5) end @@ -1415,7 +1424,7 @@ end -- -- function ctld.createExtractZone(_zone, _flagNumber, _smoke) - local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position + local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position if _triggerZone == nil then trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) @@ -1426,19 +1435,17 @@ function ctld.createExtractZone(_zone, _flagNumber, _smoke) local _alt = land.getHeight(_pos2) local _pos3 = { x = _pos2.x, y = _alt, z = _pos2.y } - trigger.action.setUserFlag(_flagNumber, 0) --start at 0 + trigger.action.setUserFlag(_flagNumber, 0) --start at 0 - local _details = { point = _pos3, name = _zone, smoke = _smoke, flag = _flagNumber, radius = _triggerZone.radius} + local _details = { point = _pos3, name = _zone, smoke = _smoke, flag = _flagNumber, radius = _triggerZone.radius } - ctld.extractZones[_zone.."-".._flagNumber] = _details + ctld.extractZones[_zone .. "-" .. _flagNumber] = _details if _smoke ~= nil and _smoke > -1 then - local _smokeFunction _smokeFunction = function(_args) - - local _extractDetails = ctld.extractZones[_zone.."-".._flagNumber] + local _extractDetails = ctld.extractZones[_zone .. "-" .. _flagNumber] -- check zone is still active if _extractDetails == nil then -- stop refreshing smoke, zone is done @@ -1456,7 +1463,6 @@ function ctld.createExtractZone(_zone, _flagNumber, _smoke) end end - -- Removes an extraction zone -- -- The smoke will take up to 5 minutes to disappear depending on the last time the smoke was activated @@ -1468,14 +1474,12 @@ end -- -- -- -function ctld.removeExtractZone(_zone,_flagNumber) - - local _extractDetails = ctld.extractZones[_zone.."-".._flagNumber] +function ctld.removeExtractZone(_zone, _flagNumber) + local _extractDetails = ctld.extractZones[_zone .. "-" .. _flagNumber] if _extractDetails ~= nil then --remove zone - ctld.extractZones[_zone.."-".._flagNumber] = nil - + ctld.extractZones[_zone .. "-" .. _flagNumber] = nil end end @@ -1486,8 +1490,7 @@ end -- is in the zone -- Use: ctld.countDroppedGroupsInZone("Zone Name", flagBlue, flagRed) function ctld.countDroppedGroupsInZone(_zone, _blueFlag, _redFlag) - - local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position + local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position if _triggerZone == nil then trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) @@ -1499,9 +1502,10 @@ function ctld.countDroppedGroupsInZone(_zone, _blueFlag, _redFlag) local _redCount = 0; local _blueCount = 0; - local _allGroups = {ctld.droppedTroopsRED,ctld.droppedTroopsBLUE,ctld.droppedVehiclesRED,ctld.droppedVehiclesBLUE} + local _allGroups = { ctld.droppedTroopsRED, ctld.droppedTroopsBLUE, ctld.droppedVehiclesRED, ctld + .droppedVehiclesBLUE } for _, _extractGroups in pairs(_allGroups) do - for _,_groupName in pairs(_extractGroups) do + for _, _groupName in pairs(_extractGroups) do local _groupUnits = ctld.getGroup(_groupName) if #_groupUnits > 0 then @@ -1509,7 +1513,6 @@ function ctld.countDroppedGroupsInZone(_zone, _blueFlag, _redFlag) local _dist = ctld.getDistance(_groupUnits[1]:getPoint(), _zonePos) if _dist <= _triggerZone.radius then - if (_groupUnits[1]:getCoalition() == 1) then _redCount = _redCount + 1; else @@ -1523,8 +1526,7 @@ function ctld.countDroppedGroupsInZone(_zone, _blueFlag, _redFlag) trigger.action.setUserFlag(_blueFlag, _blueCount) trigger.action.setUserFlag(_redFlag, _redCount) - -- env.info("Groups in zone ".._blueCount.." ".._redCount) - + -- env.info("Groups in zone ".._blueCount.." ".._redCount) end -- CONTINUOUS TRIGGER FUNCTION @@ -1533,8 +1535,7 @@ end -- Use: ctld.countDroppedUnitsInZone("Zone Name", flagBlue, flagRed) function ctld.countDroppedUnitsInZone(_zone, _blueFlag, _redFlag) - - local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position + local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position if _triggerZone == nil then trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) @@ -1546,20 +1547,19 @@ function ctld.countDroppedUnitsInZone(_zone, _blueFlag, _redFlag) local _redCount = 0; local _blueCount = 0; - local _allGroups = {ctld.droppedTroopsRED,ctld.droppedTroopsBLUE,ctld.droppedVehiclesRED,ctld.droppedVehiclesBLUE} + local _allGroups = { ctld.droppedTroopsRED, ctld.droppedTroopsBLUE, ctld.droppedVehiclesRED, ctld + .droppedVehiclesBLUE } for _, _extractGroups in pairs(_allGroups) do - for _,_groupName in pairs(_extractGroups) do + for _, _groupName in pairs(_extractGroups) do local _groupUnits = ctld.getGroup(_groupName) if #_groupUnits > 0 then - local _zonePos = mist.utils.zoneToVec3(_zone) - for _,_unit in pairs(_groupUnits) do + for _, _unit in pairs(_groupUnits) do local _dist = ctld.getDistance(_unit:getPoint(), _zonePos) if _dist <= _triggerZone.radius then - if (_unit:getCoalition() == 1) then _redCount = _redCount + 1; else @@ -1576,9 +1576,14 @@ function ctld.countDroppedUnitsInZone(_zone, _blueFlag, _redFlag) trigger.action.setUserFlag(_blueFlag, _blueCount) trigger.action.setUserFlag(_redFlag, _redCount) - -- env.info("Units in zone ".._blueCount.." ".._redCount) + -- env.info("Units in zone ".._blueCount.." ".._redCount) end +--*************************************************************** +function ctld.getNextDynamicLogisticUnitIndex() + ctld.dynamicLogisticUnitsIndex = ctld.dynamicLogisticUnitsIndex + 1 + return ctld.dynamicLogisticUnitsIndex +end -- Creates a radio beacon on a random UHF - VHF and HF/FM frequency for homing -- This WILL NOT WORK if you dont add beacon.ogg and beaconsilent.ogg to the mission!!! @@ -1588,7 +1593,7 @@ end -- e.g. ctld.createRadioBeaconAtZone("beaconZoneBlue","blue", 20) will create a beacon at trigger zone "beaconZoneBlue" for the Blue side -- that will last 20 minutes function ctld.createRadioBeaconAtZone(_zone, _coalition, _batteryLife, _name) - local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position + local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position if _triggerZone == nil then trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) @@ -1604,20 +1609,18 @@ function ctld.createRadioBeaconAtZone(_zone, _coalition, _batteryLife, _name) end if _coalition == "red" then - ctld.createRadioBeacon(_zonePos, 1, 0, _name, _batteryLife) --1440 + ctld.createRadioBeacon(_zonePos, 1, 0, _name, _batteryLife) --1440 else - ctld.createRadioBeacon(_zonePos, 2, 2, _name, _batteryLife) --1440 + ctld.createRadioBeacon(_zonePos, 2, 2, _name, _batteryLife) --1440 end end - -- Activates a pickup zone -- Activates a pickup zone when called from a trigger -- EG: ctld.activatePickupZone("pickzone3") -- This is enable pickzone3 to be used as a pickup zone for the team set function ctld.activatePickupZone(_zoneName) - - local _triggerZone = trigger.misc.getZone(_zoneName) -- trigger to use as reference position + local _triggerZone = trigger.misc.getZone(_zoneName) -- trigger to use as reference position if _triggerZone == nil then local _ship = ctld.getTransportUnit(_triggerZone) @@ -1627,31 +1630,27 @@ function ctld.activatePickupZone(_zoneName) _triggerZone = {} _triggerZone.point = _point end - end - if _triggerZone == nil then + if _triggerZone == nil then trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone or ship called %1", _zoneName), 10) end for _, _zoneDetails in pairs(ctld.pickupZones) do - if _zoneName == _zoneDetails[1] then - --smoke could get messy if designer keeps calling this on an active zone, check its not active first if _zoneDetails[4] == 1 then -- they might have a continuous trigger so i've hidden the warning return end - _zoneDetails[4] = 1 --activate zone + _zoneDetails[4] = 1 --activate zone - if ctld.disableAllSmoke == true then --smoke disabled - return + if ctld.disableAllSmoke == true then --smoke disabled + return end if _zoneDetails[2] >= 0 then - -- Trigger smoke marker -- This will cause an overlapping smoke marker on next refreshsmoke call -- but will only happen once @@ -1665,7 +1664,6 @@ function ctld.activatePickupZone(_zoneName) end end - -- Deactivates a pickup zone -- Deactivates a pickup zone when called from a trigger -- EG: ctld.deactivatePickupZone("pickzone3") @@ -1673,8 +1671,7 @@ end -- These functions can be called by triggers, like if a set of buildings is used, you can trigger the zone to be 'not operational' -- once they are destroyed function ctld.deactivatePickupZone(_zoneName) - - local _triggerZone = trigger.misc.getZone(_zoneName) -- trigger to use as reference position + local _triggerZone = trigger.misc.getZone(_zoneName) -- trigger to use as reference position if _triggerZone == nil then local _ship = ctld.getTransportUnit(_triggerZone) @@ -1684,7 +1681,6 @@ function ctld.deactivatePickupZone(_zoneName) _triggerZone = {} _triggerZone.point = _point end - end if _triggerZone == nil then @@ -1693,10 +1689,9 @@ function ctld.deactivatePickupZone(_zoneName) end for _, _zoneDetails in pairs(ctld.pickupZones) do - if _zoneName == _zoneDetails[1] then -- i'd just ignore it if its already been deactivated - _zoneDetails[4] = 0 --deactivate zone + _zoneDetails[4] = 0 --deactivate zone end end end @@ -1705,7 +1700,7 @@ end -- e.g. ctld.changeRemainingGroupsForPickupZone("pickup1", 5) -- adds 5 groups -- ctld.changeRemainingGroupsForPickupZone("pickup1", -3) -- remove 3 groups function ctld.changeRemainingGroupsForPickupZone(_zoneName, _amount) - local _triggerZone = trigger.misc.getZone(_zoneName) -- trigger to use as reference position + local _triggerZone = trigger.misc.getZone(_zoneName) -- trigger to use as reference position if _triggerZone == nil then local _ship = ctld.getTransportUnit(_triggerZone) @@ -1715,22 +1710,18 @@ function ctld.changeRemainingGroupsForPickupZone(_zoneName, _amount) _triggerZone = {} _triggerZone.point = _point end - end - if _triggerZone == nil then + if _triggerZone == nil then trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zoneName), 10) return end for _, _zoneDetails in pairs(ctld.pickupZones) do - if _zoneName == _zoneDetails[1] then ctld.updateZoneCounter(_zoneName, _amount) end end - - end -- Activates a Waypoint zone @@ -1739,33 +1730,30 @@ end -- This means that troops dropped within the radius of the zone will head to the center -- of the zone instead of searching for troops function ctld.activateWaypointZone(_zoneName) - local _triggerZone = trigger.misc.getZone(_zoneName) -- trigger to use as reference position + local _triggerZone = trigger.misc.getZone(_zoneName) -- trigger to use as reference position - if _triggerZone == nil then + if _triggerZone == nil then trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zoneName), 10) return end for _, _zoneDetails in pairs(ctld.wpZones) do - if _zoneName == _zoneDetails[1] then - --smoke could get messy if designer keeps calling this on an active zone, check its not active first if _zoneDetails[3] == 1 then -- they might have a continuous trigger so i've hidden the warning return end - _zoneDetails[3] = 1 --activate zone + _zoneDetails[3] = 1 --activate zone - if ctld.disableAllSmoke == true then --smoke disabled - return + if ctld.disableAllSmoke == true then --smoke disabled + return end if _zoneDetails[2] >= 0 then - -- Trigger smoke marker -- This will cause an overlapping smoke marker on next refreshsmoke call -- but will only happen once @@ -1779,14 +1767,12 @@ function ctld.activateWaypointZone(_zoneName) end end - -- Deactivates a Waypoint zone -- Deactivates a Waypoint zone when called from a trigger -- EG: ctld.deactivateWaypointZone("wpzone3") --- This disables wpzone3 so that troops dropped in this zone will search for troops as normal +-- This disables wpzone3 so that troops dropped in this zone will search for troops as normal -- These functions can be called by triggers function ctld.deactivateWaypointZone(_zoneName) - local _triggerZone = trigger.misc.getZone(_zoneName) if _triggerZone == nil then @@ -1795,10 +1781,8 @@ function ctld.deactivateWaypointZone(_zoneName) end for _, _zoneDetails in pairs(ctld.pickupZones) do - if _zoneName == _zoneDetails[1] then - - _zoneDetails[3] = 0 --deactivate zone + _zoneDetails[3] = 0 --deactivate zone end end end @@ -1807,19 +1791,16 @@ end -- Causes an AI unit with the specified name to unload troops / vehicles when -- an enemy is detected within a specified distance -- The enemy must have Line or Sight to the unit to be detected -function ctld.unloadInProximityToEnemy(_unitName,_distance) - +function ctld.unloadInProximityToEnemy(_unitName, _distance) local _unit = ctld.getTransportUnit(_unitName) if _unit ~= nil and _unit:getPlayerName() == nil then - -- no player name means AI! -- the findNearest visible enemy you'd want to modify as it'll find enemies quite far away - -- limited by ctld.JTAC_maxDistance - local _nearestEnemy = ctld.findNearestVisibleEnemy(_unit,"all",_distance) + -- limited by ctld.JTAC_maxDistance + local _nearestEnemy = ctld.findNearestVisibleEnemy(_unit, "all", _distance) if _nearestEnemy ~= nil then - if ctld.troopsOnboard(_unit, true) then ctld.deployTroops(_unit, true) return true @@ -1833,52 +1814,40 @@ function ctld.unloadInProximityToEnemy(_unitName,_distance) end return false - end - - -- Unit will unload any units onboard if the unit is on the ground -- when this function is called function ctld.unloadTransport(_unitName) - local _unit = ctld.getTransportUnit(_unitName) - if _unit ~= nil then - + if _unit ~= nil then if ctld.troopsOnboard(_unit, true) then - ctld.unloadTroops({_unitName,true}) + ctld.unloadTroops({ _unitName, true }) end if ctld.unitCanCarryVehicles(_unit) and ctld.troopsOnboard(_unit, false) then - ctld.unloadTroops({_unitName,false}) + ctld.unloadTroops({ _unitName, false }) end end - end -- Loads Troops and Vehicles from a zone or picks up nearby troops or vehicles function ctld.loadTransport(_unitName) - local _unit = ctld.getTransportUnit(_unitName) - if _unit ~= nil then + if _unit ~= nil then + ctld.loadTroopsFromZone({ _unitName, true, "", true }) - ctld.loadTroopsFromZone({ _unitName, true,"",true }) - - if ctld.unitCanCarryVehicles(_unit) then - ctld.loadTroopsFromZone({ _unitName, false,"",true }) + if ctld.unitCanCarryVehicles(_unit) then + ctld.loadTroopsFromZone({ _unitName, false, "", true }) end - end - end -- adds a callback that will be called for many actions ingame function ctld.addCallback(_callback) - - table.insert(ctld.callbacks,_callback) - + table.insert(ctld.callbacks, _callback) end -- Spawns a sling loadable crate at a Trigger Zone @@ -1887,8 +1856,8 @@ end -- e.g. ctld.spawnCrateAtZone("red", 500,"triggerzone1") -- spawn a humvee at triggerzone 1 for red side -- e.g. ctld.spawnCrateAtZone("blue", 505,"triggerzone1") -- spawn a tow humvee at triggerzone1 for blue side -- -function ctld.spawnCrateAtZone(_side, _weight,_zone) - local _spawnTrigger = trigger.misc.getZone(_zone) -- trigger to use as reference position +function ctld.spawnCrateAtZone(_side, _weight, _zone) + local _spawnTrigger = trigger.misc.getZone(_zone) -- trigger to use as reference position if _spawnTrigger == nil then trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) @@ -1920,20 +1889,17 @@ function ctld.spawnCrateAtZone(_side, _weight,_zone) local _name = string.format("%s #%i", _crateType.desc, _unitId) ctld.spawnCrateStatic(_country, _unitId, _point, _name, _crateType.weight, _side) - end -- Spawns a sling loadable crate at a Point -- -- Weights can be found in the ctld.spawnableCrates list -- Points can be made by hand or obtained from a Unit position by Unit.getByName("PilotName"):getPoint() --- e.g. ctld.spawnCrateAtZone("red", 500,{x=1,y=2,z=3}) -- spawn a humvee at triggerzone 1 for red side at a specified point --- e.g. ctld.spawnCrateAtZone("blue", 505,{x=1,y=2,z=3}) -- spawn a tow humvee at triggerzone1 for blue side at a specified point +-- e.g. ctld.spawnCrateAtPoint("red", 500,{x=1,y=2,z=3}) -- spawn a humvee at triggerzone 1 for red side at a specified point +-- e.g. ctld.spawnCrateAtPoint("blue", 505,{x=1,y=2,z=3}) -- spawn a tow humvee at triggerzone1 for blue side at a specified point -- -- -function ctld.spawnCrateAtPoint(_side, _weight, _point,_hdg) - - +function ctld.spawnCrateAtPoint(_side, _weight, _point, _hdg) local _crateType = ctld.crateLookupTable[tostring(_weight)] if _crateType == nil then @@ -1955,7 +1921,274 @@ function ctld.spawnCrateAtPoint(_side, _weight, _point,_hdg) local _name = string.format("%s #%i", _crateType.desc, _unitId) ctld.spawnCrateStatic(_country, _unitId, _point, _name, _crateType.weight, _side, _hdg) +end +-- *************************************************************** +function ctld.getSecureDistanceFromUnit(_unitName) -- return a distance between the center of unitName, to be sure not touch the unitName + local rotorDiameter = 0 --19 -- meters -- õk for UH & CH47 + if Unit.getByName(_unitName) then + local unitUserBox = Unit.getByName(_unitName):getDesc().box + local SecureDistanceFromUnit = 0 + if math.abs(unitUserBox.max.x) >= math.abs(unitUserBox.min.x) then + SecureDistanceFromUnit = math.abs(unitUserBox.max.x) + (rotorDiameter/2) + else + SecureDistanceFromUnit = math.abs(unitUserBox.min.x) + (rotorDiameter/2) + end + return SecureDistanceFromUnit + end + return nil +end + +-- *************************************************************** +-- Repack vehicules crates functions +-- *************************************************************** +ctld.repackRequestsStack = {} -- table to store the repack request +ctld.inAirMemorisation = {} -- last helico state of InAir() +function ctld.updateRepackMenuOnlanding(p, t) -- update helo repack menu when a helo landing is detected + if t == nil then t = timer.getTime() + 1; end + if ctld.transportPilotNames then + for _, _unitName in pairs(ctld.transportPilotNames) do + if Unit.getByName(_unitName) ~= nil and Unit.getByName(_unitName):isActive() == true then + if ctld.inAirMemorisation[_unitName] == nil then ctld.inAirMemorisation[_unitName] = false end -- init InAir() state + local _heli = Unit.getByName(_unitName) + if ctld.inAir(_heli) == false then + if ctld.inAirMemorisation[_unitName] == true then -- if transition from inAir to Landed => updateRepackMenu + ctld.updateRepackMenu(_unitName) + end + ctld.inAirMemorisation[_unitName] = false + else + ctld.inAirMemorisation[_unitName] = true + end + end + end + end + return t + 5 -- reschdule each 5 seconds +end + +-- *************************************************************** +function ctld.getUnitsInRepackRadius(_PlayerTransportUnitName, _radius) + if _radius == nil then + _radius = ctld.maximumDistanceRepackableUnitsSearch + end + + local unit = ctld.getTransportUnit(_PlayerTransportUnitName) + if unit == nil then + return + end + + local unitsNamesList = ctld.getNearbyUnits(unit:getPoint(), _radius, unit:getCoalition()) + + local repackableUnits = {} + for i = 1, #unitsNamesList do + local unitObject = Unit.getByName(unitsNamesList[i]) + local repackableUnit = ctld.isRepackableUnit(unitsNamesList[i]) + if repackableUnit then + repackableUnit["repackableUnitGroupID"] = unitObject:getGroup():getID() + table.insert(repackableUnits, mist.utils.deepCopy(repackableUnit)) + end + end + return repackableUnits +end + +-- *************************************************************** +function ctld.getNearbyUnits(_point, _radius, _coalition) + if _coalition == nil then + _coalition = 4 -- all coalitions + end + local unitsByDistance = {} + local cpt = 1 + local _units = {} + local _unitList = mist.DBs.unitsByName + for _unitName, _unit in pairs(mist.DBs.unitsByName) do + local u = Unit.getByName(_unitName) + if u and u:isExist() and (_coalition == 4 or u:getCoalition() == _coalition) then + --local _dist = ctld.getDistance(u:getPoint(), _point) + local _dist = mist.utils.get2DDist(u:getPoint(), _point) + if _dist <= _radius then + unitsByDistance[cpt] = {id =cpt, dist = _dist, unit = _unitName, typeName = u:getTypeName()} + cpt = cpt + 1 + end + end + end + + --table.sort(unitsByDistance, function(a,b) return a.dist < b.dist end) -- sort the table by distance (the nearest first) + table.sort(unitsByDistance, function(a,b) return a.typeName < b.typeName end) -- sort the table by typeNAme + for i, v in ipairs(unitsByDistance) do + table.insert(_units, v.unit) -- insert nearby unitName + end + return _units +end + +-- *************************************************************** +function ctld.isRepackableUnit(_unitName) + local unitObject = Unit.getByName(_unitName) + local unitType = unitObject:getTypeName() + for k, v in pairs(ctld.spawnableCrates) do + for i = 1, #ctld.spawnableCrates[k] do + if _unitName then + if ctld.spawnableCrates[k][i].unit == unitType then + local repackableUnit = mist.utils.deepCopy(ctld.spawnableCrates[k][i]) + repackableUnit["repackableUnitName"] = _unitName + return repackableUnit + end + end + end + end + return nil +end + +-- *************************************************************** +function ctld.getCrateDesc(_crateWeight) + for k, v in pairs(ctld.spawnableCrates) do + for i = 1, #ctld.spawnableCrates[k] do + if _crateWeight then + if ctld.spawnableCrates[k][i].weight == _crateWeight then + return ctld.spawnableCrates[k][i] + end + end + end + end + return nil +end + +-- *************************************************************** +function ctld.repackVehicleRequest(_params) -- update rrs table 'repackRequestsStack' with the request + --ctld.logTrace("FG_ ctld.repackVehicleRequest._params = " .. ctld.p(_params)) + ctld.repackRequestsStack[#ctld.repackRequestsStack + 1] = _params +end + +-- *************************************************************** +function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' to process each request + --ctld.logTrace("FG_ XXXXXXXXXXXXXXXXXXXXXXXXXXX ctld.repackVehicle.ctld.repackRequestsStack XXXXXXXXXXXXXXXXXXXXXXXXXXX") + if t == nil then + t = timer.getTime() + end + if #ctld.repackRequestsStack ~= 0 then + ctld.logTrace("FG_ ctld.repackVehicle.ctld.repackRequestsStack = %s", ctld.p(ctld.repackRequestsStack)) + end + for ii, v in ipairs(ctld.repackRequestsStack) do + ctld.logTrace("FG_ ctld.repackVehicle.v[%s] = %s", ii, ctld.p(v)) + local repackableUnitName = v.repackableUnitName + local repackableUnit = Unit.getByName(repackableUnitName) + local crateWeight = v.weight + local playerUnitName = v.playerUnitName + if repackableUnit then + if repackableUnit:isExist() then + local PlayerTransportUnit = Unit.getByName(playerUnitName) + local playerCoa = PlayerTransportUnit:getCoalition() + local refCountry = PlayerTransportUnit:getCountry() + -- calculate the heading of the spawns to be carried out + local playerHeading = mist.getHeading(PlayerTransportUnit) + local playerPoint = PlayerTransportUnit:getPoint() + local offset = 5 + local randomHeading = ctld.RandomReal(playerHeading - math.pi/4, playerHeading + math.pi/4) + if ctld.unitDynamicCargoCapable(PlayerTransportUnit) ~= false then + randomHeading = ctld.RandomReal(playerHeading + math.pi - math.pi/4, playerHeading + math.pi + math.pi/4) + end + repackableUnit:destroy() -- destroy repacked unit + for i = 1, v.cratesRequired or 1 do + -- see to spawn the crate at random position heading the transport unit + local _unitId = ctld.getNextUnitId() + local _name = string.format("%s_%i", v.desc, _unitId) + local secureDistance = ctld.getSecureDistanceFromUnit(playerUnitName) or 10 + local relativePoint = ctld.getRelativePoint(playerPoint, secureDistance + (i * offset), randomHeading) -- 7 meters from the transport unit + + if ctld.unitDynamicCargoCapable(PlayerTransportUnit) == false then + ctld.spawnCrateStatic(refCountry, _unitId, relativePoint, _name, crateWeight, playerCoa, playerHeading, nil) + else + ctld.spawnCrateStatic(refCountry, _unitId, relativePoint, _name, crateWeight, playerCoa, playerHeading, "dynamic") + end + end + end + timer.scheduleFunction(ctld.autoUpdateRepackMenu, { reschedule = false }, timer.getTime() + 1) -- for add unpacked unit in repack menu + end + ctld.repackRequestsStack[ii] = nil -- remove the processed request from the stacking table + end + + + + if ctld.enableRepackingVehicles == true then + return t + 3 -- reschedule the function in 3 seconds + else + return nil --stop scheduling + end +end + +-- *************************************************************** +function ctld.addStaticLogisticUnit(_point, _country) -- create a temporary logistic unit with a Windsock object + local dynamicLogisticUnitName = "%dynLogisticName_" .. tostring(ctld.getNextDynamicLogisticUnitIndex()) + ctld.logisticUnits[#ctld.logisticUnits + 1] = dynamicLogisticUnitName + local LogUnit = { + ["category"] = "Fortifications", + ["shape_name"] = "H-Windsock_RW", + ["type"] = "Windsock", + ["y"] = _point.z, + ["x"] = _point.x, + ["name"] = dynamicLogisticUnitName, + ["canCargo"] = false, + ["heading"] = 0, + } + LogUnit["country"] = _country + mist.dynAddStatic(LogUnit) + return StaticObject.getByName(LogUnit["name"]) +end + +-- *************************************************************** +function ctld.updateDynamicLogisticUnitsZones() -- remove Dynamic Logistic Units if no statics units (crates) are in the zone + local _units = {} + for i, logUnit in ipairs(ctld.logisticUnits) do + if string.sub(logUnit, 1, 17) == "%dynLogisticName_" then -- check if the unit is a dynamic logistic unit + local unitsInLogisticUnitZone = ctld.getUnitsInLogisticZone(logUnit) + if #unitsInLogisticUnitZone == 0 then + local _logUnit = StaticObject.getByName(logUnit) + if _logUnit then + _logUnit:destroy() -- destroy the dynamic Logistic unit object from map + ctld.logisticUnits[i] = nil -- remove the dynamic Logistic unit from the list + end + end + end + end + return 5 -- reschedule the function in 5 seconds +end + +-- *************************************************************** +function ctld.getUnitsInLogisticZone(_logisticUnitName, _coalition) + local _unit = StaticObject.getByName(_logisticUnitName) + if _unit then + local _point = _unit:getPoint() + local _unitList = ctld.getNearbyUnits(_point, ctld.maximumDistanceLogistic, _coalition) + return _unitList + end + return {} +end + +-- *************************************************************** +function ctld.isUnitInNamedLogisticZone(_unitName, _logisticUnitName) -- check if a unit is in the named logistic zone + --ctld.logTrace("FG_ ctld.isUnitInNamedLogisticZone._logisticUnitName = %s", ctld.p(_logisticUnitName)) + local _unit = Unit.getByName(_unitName) + if _unit == nil then + return false + end + local unitPoint = _unit:getPoint() + if StaticObject.getByName(_logisticUnitName) then + local logisticUnitPoint = StaticObject.getByName(_logisticUnitName):getPoint() + local _dist = ctld.getDistance(unitPoint, logisticUnitPoint) + if _dist <= ctld.maximumDistanceLogistic then + return true + end + end + return false +end + +-- *************************************************************** +function ctld.isUnitInALogisticZone(_unitName) -- check if a unit is in a logistic zone if true then return the logisticUnitName of the zone + --ctld.logTrace("FG_ ctld.isUnitInALogisticZone._unitName = %s", ctld.p(_unitName)) + for i, logUnit in ipairs(ctld.logisticUnits) do + if ctld.isUnitInNamedLogisticZone(_unitName, logUnit) then + return logUnit + end + end + return nil end -- *************************************************************** @@ -1972,11 +2205,11 @@ ctld.AASystemTemplate = { name = "HAWK AA System", count = 5, parts = { - {name = "Hawk ln", desc = "HAWK Launcher", launcher = true}, - {name = "Hawk tr", desc = "HAWK Track Radar", amount = 2}, - {name = "Hawk sr", desc = "HAWK Search Radar", amount = 2}, - {name = "Hawk pcp", desc = "HAWK PCP", NoCrate = true}, - {name = "Hawk cwar", desc = "HAWK CWAR", amount = 2, NoCrate = true}, + { name = "Hawk ln", desc = "HAWK Launcher", launcher = true }, + { name = "Hawk tr", desc = "HAWK Track Radar", amount = 2 }, + { name = "Hawk sr", desc = "HAWK Search Radar", amount = 2 }, + { name = "Hawk pcp", desc = "HAWK PCP", NoCrate = true }, + { name = "Hawk cwar", desc = "HAWK CWAR", amount = 2, NoCrate = true }, }, repair = "HAWK Repair", }, @@ -1984,12 +2217,12 @@ ctld.AASystemTemplate = { name = "Patriot AA System", count = 4, parts = { - {name = "Patriot ln", desc = "Patriot Launcher", launcher = true, amount = 8}, - {name = "Patriot ECS", desc = "Patriot Control Unit"}, - {name = "Patriot str", desc = "Patriot Search and Track Radar", amount = 2}, + { name = "Patriot ln", desc = "Patriot Launcher", launcher = true, amount = 8 }, + { name = "Patriot ECS", desc = "Patriot Control Unit" }, + { name = "Patriot str", desc = "Patriot Search and Track Radar", amount = 2 }, --{name = "Patriot cp", desc = "Patriot ICC", NoCrate = true}, --{name = "Patriot EPP", desc = "Patriot EPP", NoCrate = true}, - {name = "Patriot AMG", desc = "Patriot AMG DL relay", NoCrate = true}, + { name = "Patriot AMG", desc = "Patriot AMG DL relay", NoCrate = true }, }, repair = "Patriot Repair", }, @@ -1997,19 +2230,19 @@ ctld.AASystemTemplate = { name = "NASAMS AA System", count = 3, parts = { - {name = "NASAMS_LN_C", desc = "NASAMS Launcher 120C", launcher = true}, - {name = "NASAMS_Radar_MPQ64F1", desc = "NASAMS Search/Track Radar"}, - {name = "NASAMS_Command_Post", desc = "NASAMS Command Post"}, + { name = "NASAMS_LN_C", desc = "NASAMS Launcher 120C", launcher = true }, + { name = "NASAMS_Radar_MPQ64F1", desc = "NASAMS Search/Track Radar" }, + { name = "NASAMS_Command_Post", desc = "NASAMS Command Post" }, }, repair = "NASAMS Repair", }, - { + { name = "BUK AA System", count = 3, parts = { - {name = "SA-11 Buk LN 9A310M1", desc = "BUK Launcher" , launcher = true}, - {name = "SA-11 Buk CC 9S470M1", desc = "BUK CC Radar"}, - {name = "SA-11 Buk SR 9S18M1", desc = "BUK Search Radar"}, + { name = "SA-11 Buk LN 9A310M1", desc = "BUK Launcher", launcher = true }, + { name = "SA-11 Buk CC 9S470M1", desc = "BUK CC Radar" }, + { name = "SA-11 Buk SR 9S18M1", desc = "BUK Search Radar" }, }, repair = "BUK Repair", }, @@ -2017,8 +2250,8 @@ ctld.AASystemTemplate = { name = "KUB AA System", count = 2, parts = { - {name = "Kub 2P25 ln", desc = "KUB Launcher", launcher = true}, - {name = "Kub 1S91 str", desc = "KUB Radar"}, + { name = "Kub 2P25 ln", desc = "KUB Launcher", launcher = true }, + { name = "Kub 1S91 str", desc = "KUB Radar" }, }, repair = "KUB Repair", }, @@ -2026,12 +2259,12 @@ ctld.AASystemTemplate = { name = "S-300 AA System", count = 6, parts = { - { desc = "S-300 Grumble TEL C", name = "S-300PS 5P85C ln", launcher = true, amount = 1 }, - { desc = "S-300 Grumble TEL D", name = "S-300PS 5P85D ln", NoCrate = true, amount = 2 }, - { desc = "S-300 Grumble Flap Lid-A TR", name = "S-300PS 40B6M tr"}, - { desc = "S-300 Grumble Clam Shell SR", name = "S-300PS 40B6MD sr"}, - { desc = "S-300 Grumble Big Bird SR", name = "S-300PS 64H6E sr"}, - { desc = "S-300 Grumble C2", name = "S-300PS 54K6 cp"}, + { desc = "S-300 Grumble TEL C", name = "S-300PS 5P85C ln", launcher = true, amount = 1 }, + { desc = "S-300 Grumble TEL D", name = "S-300PS 5P85D ln", NoCrate = true, amount = 2 }, + { desc = "S-300 Grumble Flap Lid-A TR", name = "S-300PS 40B6M tr" }, + { desc = "S-300 Grumble Clam Shell SR", name = "S-300PS 40B6MD sr" }, + { desc = "S-300 Grumble Big Bird SR", name = "S-300PS 64H6E sr" }, + { desc = "S-300 Grumble C2", name = "S-300PS 54K6 cp" }, }, repair = "S-300 Repair", }, @@ -2053,17 +2286,17 @@ function ctld.p(o, level) local MAX_LEVEL = 20 if level == nil then level = 0 end if level > MAX_LEVEL then - ctld.logError("max depth reached in ctld.p : "..tostring(MAX_LEVEL)) + ctld.logError("max depth reached in ctld.p : " .. tostring(MAX_LEVEL)) return "" end local text = "" if (type(o) == "table") then text = "\n" - for key,value in pairs(o) do - for i=0, level do + for key, value in pairs(o) do + for i = 0, level do text = text .. " " end - text = text .. ".".. key.."="..ctld.p(value, level+1) .. "\n" + text = text .. "." .. key .. "=" .. ctld.p(value, level + 1) .. "\n" end elseif (type(o) == "function") then text = "[function]" @@ -2093,12 +2326,12 @@ function ctld.formatText(text, ...) local args = ... if args and args.n and args.n > 0 then local pArgs = {} - for i=1,args.n do + for i = 1, args.n do pArgs[i] = ctld.p(args[i]) end - text = text:format(unpack(pArgs)) - end + text = text:format(unpack(pArgs)) end + end local fName = nil local cLine = nil if debug and debug.getinfo then @@ -2120,7 +2353,6 @@ function ctld.logError(message, ...) env.info(" E - " .. ctld.Id .. message) end - function ctld.logWarning(message, ...) message = ctld.formatText(message, arg) env.info(" W - " .. ctld.Id .. message) @@ -2161,23 +2393,19 @@ ctld.getNextGroupId = function() end function ctld.getTransportUnit(_unitName) - if _unitName == nil then return nil end - local _heli = Unit.getByName(_unitName) + local transportUnitObject = Unit.getByName(_unitName) - if _heli ~= nil and _heli:isActive() and _heli:getLife() > 0 then - - return _heli + if transportUnitObject ~= nil and transportUnitObject:isActive() and transportUnitObject:getLife() > 0 then + return transportUnitObject end - return nil end function ctld.spawnCrateStatic(_country, _unitId, _point, _name, _weight, _side, _hdg, _model_type) - local _crate local _spawnedCrate @@ -2185,20 +2413,20 @@ function ctld.spawnCrateStatic(_country, _unitId, _point, _name, _weight, _side, if ctld.staticBugWorkaround and ctld.slingLoad == false then local _groupId = ctld.getNextGroupId() - local _groupName = "Crate Group #".._groupId + local _groupName = "Crate Group #" .. _groupId local _group = { ["visible"] = false, - -- ["groupId"] = _groupId, + -- ["groupId"] = _groupId, ["hidden"] = false, ["units"] = {}, - -- ["y"] = _positions[1].z, - -- ["x"] = _positions[1].x, + -- ["y"] = _positions[1].z, + -- ["x"] = _positions[1].x, ["name"] = _groupName, ["task"] = {}, } - _group.units[1] = ctld.createUnit(_point.x , _point.z , hdg, {type="UAZ-469",name=_name,unitId=_unitId}) + _group.units[1] = ctld.createUnit(_point.x, _point.z, hdg, { type = "UAZ-469", name = _name, unitId = _unitId }) --switch to MIST _group.category = Group.Category.GROUND; @@ -2211,7 +2439,6 @@ function ctld.spawnCrateStatic(_country, _unitId, _point, _name, _weight, _side, _spawnedCrate = Unit.getByName(_name) else - if _model_type ~= nil then _crate = mist.utils.deepCopy(ctld.spawnableCratesModels[_model_type]) elseif ctld.slingLoad then @@ -2236,7 +2463,7 @@ function ctld.spawnCrateStatic(_country, _unitId, _point, _name, _weight, _side, local _crateType = ctld.crateLookupTable[tostring(_weight)] if _side == 1 then - ctld.spawnedCratesRED[_name] =_crateType + ctld.spawnedCratesRED[_name] = _crateType else ctld.spawnedCratesBLUE[_name] = _crateType end @@ -2245,12 +2472,11 @@ function ctld.spawnCrateStatic(_country, _unitId, _point, _name, _weight, _side, end function ctld.spawnFOBCrateStatic(_country, _unitId, _point, _name) - local _crate = { ["category"] = "Fortifications", ["shape_name"] = "konteiner_red1", ["type"] = "Container red 1", - -- ["unitId"] = _unitId, + -- ["unitId"] = _unitId, ["y"] = _point.z, ["x"] = _point.x, ["name"] = _name, @@ -2268,13 +2494,11 @@ function ctld.spawnFOBCrateStatic(_country, _unitId, _point, _name) return _spawnedCrate end - function ctld.spawnFOB(_country, _unitId, _point, _name) - local _crate = { ["category"] = "Fortifications", ["type"] = "outpost", - -- ["unitId"] = _unitId, + -- ["unitId"] = _unitId, ["y"] = _point.z, ["x"] = _point.x, ["name"] = _name, @@ -2290,7 +2514,7 @@ function ctld.spawnFOB(_country, _unitId, _point, _name) local _id = ctld.getNextUnitId() local _tower = { ["type"] = "house2arm", - -- ["unitId"] = _id, + -- ["unitId"] = _id, ["rate"] = 100, ["y"] = _point.z + -36.57142857, ["x"] = _point.x + 14.85714286, @@ -2307,22 +2531,20 @@ function ctld.spawnFOB(_country, _unitId, _point, _name) return _spawnedCrate end - function ctld.spawnCrate(_arguments, bypassCrateWaitTime) - local _status, _err = pcall(function(_args) - -- use the cargo weight to guess the type of unit as no way to add description :( local _crateType = ctld.crateLookupTable[tostring(_args[2])] - local _heli = ctld.getTransportUnit(_args[1]) if not _heli then return end -- check crate spam - if not(bypassCrateWaitTime) and _heli:getPlayerName() ~= nil and ctld.crateWait[_heli:getPlayerName()] and ctld.crateWait[_heli:getPlayerName()] > timer.getTime() then - ctld.displayMessageToGroup(_heli,ctld.i18n_translate("Sorry you must wait %1 seconds before you can get another crate", (ctld.crateWait[_heli:getPlayerName()] - timer.getTime())), 20) + if not (bypassCrateWaitTime) and _heli:getPlayerName() ~= nil and ctld.crateWait[_heli:getPlayerName()] and ctld.crateWait[_heli:getPlayerName()] > timer.getTime() then + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate("Sorry you must wait %1 seconds before you can get another crate", + (ctld.crateWait[_heli:getPlayerName()] - timer.getTime())), 20) return end @@ -2330,27 +2552,23 @@ function ctld.spawnCrate(_arguments, bypassCrateWaitTime) for _, weight in pairs(_crateType.multiple) do local _aCrateType = ctld.crateLookupTable[tostring(weight)] if _aCrateType then - ctld.spawnCrate({_args[1], _aCrateType.weight}, true) + ctld.spawnCrate({ _args[1], _aCrateType.weight }, true) end end return end if _crateType ~= nil and _heli ~= nil and ctld.inAir(_heli) == false then - if ctld.inLogisticsZone(_heli) == false then - - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You are not close enough to friendly logistics to get a crate!"), 10) - + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate("You are not close enough to friendly logistics to get a crate!"), 10) return end if ctld.isJTACUnitType(_crateType.unit) then - local _limitHit = false if _heli:getCoalition() == 1 then - if ctld.JTAC_LIMIT_RED == 0 then _limitHit = true else @@ -2378,7 +2596,7 @@ function ctld.spawnCrate(_arguments, bypassCrateWaitTime) local _model_type = nil - local _point = ctld.getPointAt12Oclock(_heli, 30) + local _point = ctld.getPointAt12Oclock(_heli, 15) local _position = "12" if ctld.unitDynamicCargoCapable(_heli) then @@ -2398,8 +2616,9 @@ function ctld.spawnCrate(_arguments, bypassCrateWaitTime) -- add to move table ctld.crateMove[_name] = _name - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("A %1 crate weighing %2 kg has been brought out and is at your %3 o'clock ", _crateType.desc, _crateType.weight, _position), 20) - + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate("A %1 crate weighing %2 kg has been brought out and is at your %3 o'clock ", + _crateType.desc, _crateType.weight, _position), 20) else env.info("Couldn't find crate item to spawn") end @@ -2410,7 +2629,8 @@ function ctld.spawnCrate(_arguments, bypassCrateWaitTime) end end -ctld.randomCrateSpacing = 12 -- meters +--*************************************************************** +ctld.randomCrateSpacing = 15 -- meters function ctld.getPointAt12Oclock(_unit, _offset) return ctld.getPointAtDirection(_unit, _offset, 0) end @@ -2419,44 +2639,73 @@ function ctld.getPointAt6Oclock(_unit, _offset) return ctld.getPointAtDirection(_unit, _offset, math.pi) end +function ctld.getPointInFrontSector(_unit, _offset) + if _unit then + local playerHeading = mist.getHeading(_unit) + local randomHeading = ctld.RandomReal(playerHeading - math.pi/4, playerHeading + math.pi/4) + if _offset == nil then + _offset = 20 + end + return ctld.getPointAtDirection(_unit, _offset, randomHeading) + end +end + +function ctld.getPointInRearSector(_unit, _offset) + if _unit then + local playerHeading = mist.getHeading(_unit) + local randomHeading = ctld.RandomReal(playerHeading + math.pi - math.pi/4, playerHeading + math.pi + math.pi/4) + if _offset == nil then + _offset = 30 + end + return ctld.getPointAtDirection(_unit, _offset, randomHeading) + end +end + function ctld.getPointAtDirection(_unit, _offset, _directionInRadian) - - ctld.logTrace("_offset = %s", ctld.p(_offset)) - local _randomOffsetX = math.random(ctld.randomCrateSpacing * 2) - ctld.randomCrateSpacing + if _offset == nil then + _offset = ctld.getSecureDistanceFromUnit(_unit:getName()) + end + --ctld.logTrace("_offset = %s", ctld.p(_offset)) + local _randomOffsetX = math.random(0, ctld.randomCrateSpacing * 2) - ctld.randomCrateSpacing local _randomOffsetZ = math.random(0, ctld.randomCrateSpacing) - ctld.logTrace("_randomOffsetX = %s", ctld.p(_randomOffsetX)) - ctld.logTrace("_randomOffsetZ = %s", ctld.p(_randomOffsetZ)) + --ctld.logTrace("_randomOffsetX = %s", ctld.p(_randomOffsetX)) + --ctld.logTrace("_randomOffsetZ = %s", ctld.p(_randomOffsetZ)) local _position = _unit:getPosition() - local _angle = math.atan2(_position.x.z, _position.x.x) + _directionInRadian - local _xOffset = math.cos(_angle) * _offset + _randomOffsetX - local _zOffset = math.sin(_angle) * _offset + _randomOffsetZ - - local _point = _unit:getPoint() + local _angle = math.atan(_position.x.z, _position.x.x) + _directionInRadian + local _xOffset = math.cos(_angle) * (_offset + _randomOffsetX) + local _zOffset = math.sin(_angle) * (_offset + _randomOffsetZ) + local _point = _unit:getPoint() return { x = _point.x + _xOffset, z = _point.z + _zOffset, y = _point.y } end +function ctld.getRelativePoint(_refPointXZTable, _distance, _angle_radians) -- return coord point at distance and angle from _refPointXZTable + local relativePoint = {} + relativePoint.x = _refPointXZTable.x + _distance * math.cos(_angle_radians) + if _refPointXZTable.z == nil then + relativePoint.y = _refPointXZTable.y + _distance * math.sin(_angle_radians) + else + relativePoint.z = _refPointXZTable.z + _distance * math.sin(_angle_radians) + end + return relativePoint +end + function ctld.troopsOnboard(_heli, _troops) - if ctld.inTransitTroops[_heli:getName()] ~= nil then - local _onboard = ctld.inTransitTroops[_heli:getName()] if _troops then - if _onboard.troops ~= nil and _onboard.troops.units ~= nil and #_onboard.troops.units > 0 then return true else return false end else - if _onboard.vehicles ~= nil and _onboard.vehicles.units ~= nil and #_onboard.vehicles.units > 0 then return true else return false end end - else return false end @@ -2464,9 +2713,7 @@ end -- if its dropped by AI then there is no player name so return the type of unit function ctld.getPlayerNameOrType(_heli) - if _heli:getPlayerName() == nil then - return _heli:getTypeName() else return _heli:getPlayerName() @@ -2474,11 +2721,9 @@ function ctld.getPlayerNameOrType(_heli) end function ctld.inExtractZone(_heli) - local _heliPoint = _heli:getPoint() for _, _zoneDetails in pairs(ctld.extractZones) do - --get distance to center local _dist = ctld.getDistance(_heliPoint, _zoneDetails.point) @@ -2492,7 +2737,6 @@ end -- safe to fast rope if speed is less than 0.5 Meters per second function ctld.safeToFastRope(_heli) - if ctld.enableFastRopeInsertion == false then return false end @@ -2504,14 +2748,12 @@ function ctld.safeToFastRope(_heli) end function ctld.metersToFeet(_meters) - local _feet = _meters * 3.2808399 return mist.utils.round(_feet) end function ctld.inAir(_heli) - if _heli:inAir() == false then return false end @@ -2525,19 +2767,16 @@ function ctld.inAir(_heli) end function ctld.deployTroops(_heli, _troops) - local _onboard = ctld.inTransitTroops[_heli:getName()] -- deloy troops if _troops then if _onboard.troops ~= nil and #_onboard.troops.units > 0 then if ctld.inAir(_heli) == false or ctld.safeToFastRope(_heli) then - -- check we're not in extract zone local _extractZone = ctld.inExtractZone(_heli) if _extractZone == false then - local _droppedTroops = ctld.spawnDroppedGroup(_heli:getPoint(), _onboard.troops, false) if _onboard.troops.jtac or _droppedTroops:getName():lower():find("jtac") then local _code = table.remove(ctld.jtacGeneratedLaserCodes, 1) @@ -2546,10 +2785,8 @@ function ctld.deployTroops(_heli, _troops) end if _heli:getCoalition() == 1 then - table.insert(ctld.droppedTroopsRED, _droppedTroops:getName()) else - table.insert(ctld.droppedTroopsBLUE, _droppedTroops:getName()) end @@ -2557,14 +2794,16 @@ function ctld.deployTroops(_heli, _troops) ctld.adaptWeightToCargo(_heli:getName()) if ctld.inAir(_heli) then - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 fast-ropped troops from %2 into combat", ctld.getPlayerNameOrType(_heli), _heli:getTypeName()), 10) + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 fast-ropped troops from %2 into combat", + ctld.getPlayerNameOrType(_heli), _heli:getTypeName()), 10) else - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 dropped troops from %2 into combat", ctld.getPlayerNameOrType(_heli), _heli:getTypeName()), 10) + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 dropped troops from %2 into combat", ctld.getPlayerNameOrType(_heli), + _heli:getTypeName()), 10) end - ctld.processCallback({unit = _heli, unloaded = _droppedTroops, action = "dropped_troops"}) - - + ctld.processCallback({ unit = _heli, unloaded = _droppedTroops, action = "dropped_troops" }) else --extract zone! local _droppedCount = trigger.misc.getUserFlag(_extractZone.flag) @@ -2577,53 +2816,56 @@ function ctld.deployTroops(_heli, _troops) ctld.adaptWeightToCargo(_heli:getName()) if ctld.inAir(_heli) then - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 fast-ropped troops from %2 into %3", ctld.getPlayerNameOrType(_heli), _heli:getTypeName(), _extractZone.name), 10) + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 fast-ropped troops from %2 into %3", ctld.getPlayerNameOrType(_heli), + _heli:getTypeName(), _extractZone.name), 10) else - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 dropped troops from %2 into %3", ctld.getPlayerNameOrType(_heli), _heli:getTypeName(), _extractZone.name), 10) + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 dropped troops from %2 into %3", ctld.getPlayerNameOrType(_heli), + _heli:getTypeName(), _extractZone.name), 10) end end else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Too high or too fast to drop troops into combat! Hover below %1 feet or land.", ctld.metersToFeet(ctld.fastRopeMaximumHeight)), 10) + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate("Too high or too fast to drop troops into combat! Hover below %1 feet or land.", + ctld.metersToFeet(ctld.fastRopeMaximumHeight)), 10) end end - else if ctld.inAir(_heli) == false then if _onboard.vehicles ~= nil and #_onboard.vehicles.units > 0 then - local _droppedVehicles = ctld.spawnDroppedGroup(_heli:getPoint(), _onboard.vehicles, true) if _heli:getCoalition() == 1 then - table.insert(ctld.droppedVehiclesRED, _droppedVehicles:getName()) else - table.insert(ctld.droppedVehiclesBLUE, _droppedVehicles:getName()) end ctld.inTransitTroops[_heli:getName()].vehicles = nil ctld.adaptWeightToCargo(_heli:getName()) - ctld.processCallback({unit = _heli, unloaded = _droppedVehicles, action = "dropped_vehicles"}) + ctld.processCallback({ unit = _heli, unloaded = _droppedVehicles, action = "dropped_vehicles" }) - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 dropped vehicles from %2 into combat", ctld.getPlayerNameOrType(_heli), _heli:getTypeName()), 10) + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 dropped vehicles from %2 into combat", ctld.getPlayerNameOrType(_heli), + _heli:getTypeName()), 10) end end end end -function ctld.insertIntoTroopsArray(_troopType,_count,_troopArray,_troopName) - +function ctld.insertIntoTroopsArray(_troopType, _count, _troopArray, _troopName) for _i = 1, _count do local _unitId = ctld.getNextUnitId() - table.insert(_troopArray, { type = _troopType, unitId = _unitId, name = string.format("Dropped %s #%i", _troopName or _troopType, _unitId) }) + table.insert(_troopArray, + { type = _troopType, unitId = _unitId, name = string.format("Dropped %s #%i", _troopName or _troopType, + _unitId) }) end return _troopArray - end - function ctld.generateTroopTypes(_side, _countOrTemplate, _country) local _troops = {} local _weight = 0 @@ -2639,61 +2881,58 @@ function ctld.generateTroopTypes(_side, _countOrTemplate, _country) end if type(_countOrTemplate) == "table" then - if _countOrTemplate.aa then if _side == 2 then - _troops = ctld.insertIntoTroopsArray("Soldier stinger",_countOrTemplate.aa,_troops) + _troops = ctld.insertIntoTroopsArray("Soldier stinger", _countOrTemplate.aa, _troops) else - _troops = ctld.insertIntoTroopsArray("SA-18 Igla manpad",_countOrTemplate.aa,_troops) + _troops = ctld.insertIntoTroopsArray("SA-18 Igla manpad", _countOrTemplate.aa, _troops) end _weight = _weight + getSoldiersWeight(_countOrTemplate.aa, ctld.MANPAD_WEIGHT) end if _countOrTemplate.inf then if _side == 2 then - _troops = ctld.insertIntoTroopsArray("Soldier M4 GRG",_countOrTemplate.inf,_troops) + _troops = ctld.insertIntoTroopsArray("Soldier M4 GRG", _countOrTemplate.inf, _troops) else - _troops = ctld.insertIntoTroopsArray("Infantry AK",_countOrTemplate.inf,_troops) + _troops = ctld.insertIntoTroopsArray("Infantry AK", _countOrTemplate.inf, _troops) end _weight = _weight + getSoldiersWeight(_countOrTemplate.inf, ctld.RIFLE_WEIGHT) end if _countOrTemplate.mg then if _side == 2 then - _troops = ctld.insertIntoTroopsArray("Soldier M249",_countOrTemplate.mg,_troops) + _troops = ctld.insertIntoTroopsArray("Soldier M249", _countOrTemplate.mg, _troops) else - _troops = ctld.insertIntoTroopsArray("Paratrooper AKS-74",_countOrTemplate.mg,_troops) + _troops = ctld.insertIntoTroopsArray("Paratrooper AKS-74", _countOrTemplate.mg, _troops) end _weight = _weight + getSoldiersWeight(_countOrTemplate.mg, ctld.MG_WEIGHT) end if _countOrTemplate.at then - _troops = ctld.insertIntoTroopsArray("Paratrooper RPG-16",_countOrTemplate.at,_troops) + _troops = ctld.insertIntoTroopsArray("Paratrooper RPG-16", _countOrTemplate.at, _troops) _weight = _weight + getSoldiersWeight(_countOrTemplate.at, ctld.RPG_WEIGHT) end if _countOrTemplate.mortar then - _troops = ctld.insertIntoTroopsArray("2B11 mortar",_countOrTemplate.mortar,_troops) + _troops = ctld.insertIntoTroopsArray("2B11 mortar", _countOrTemplate.mortar, _troops) _weight = _weight + getSoldiersWeight(_countOrTemplate.mortar, ctld.MORTAR_WEIGHT) end if _countOrTemplate.jtac then if _side == 2 then - _troops = ctld.insertIntoTroopsArray("Soldier M4 GRG",_countOrTemplate.jtac,_troops, "JTAC") + _troops = ctld.insertIntoTroopsArray("Soldier M4 GRG", _countOrTemplate.jtac, _troops, "JTAC") else - _troops = ctld.insertIntoTroopsArray("Infantry AK",_countOrTemplate.jtac,_troops, "JTAC") + _troops = ctld.insertIntoTroopsArray("Infantry AK", _countOrTemplate.jtac, _troops, "JTAC") end _hasJTAC = true _weight = _weight + getSoldiersWeight(_countOrTemplate.jtac, ctld.JTAC_WEIGHT + ctld.RIFLE_WEIGHT) end - else for _i = 1, _countOrTemplate do - local _unitType = "Infantry AK" if _side == 2 then - if _i <=2 then + if _i <= 2 then _unitType = "Soldier M249" _weight = _weight + getSoldiersWeight(1, ctld.MG_WEIGHT) elseif ctld.spawnRPGWithCoalition and _i > 2 and _i <= 4 then @@ -2707,7 +2946,7 @@ function ctld.generateTroopTypes(_side, _countOrTemplate, _country) _weight = _weight + getSoldiersWeight(1, ctld.RIFLE_WEIGHT) end else - if _i <=2 then + if _i <= 2 then _unitType = "Paratrooper AKS-74" _weight = _weight + getSoldiersWeight(1, ctld.MG_WEIGHT) elseif ctld.spawnRPGWithCoalition and _i > 2 and _i <= 4 then @@ -2733,14 +2972,14 @@ function ctld.generateTroopTypes(_side, _countOrTemplate, _country) if _hasJTAC then _groupName = "Dropped JTAC Group" end - local _details = { units = _troops, groupId = _groupId, groupName = string.format("%s %i", _groupName, _groupId), side = _side, country = _country, weight = _weight, jtac = _hasJTAC } + local _details = { units = _troops, groupId = _groupId, groupName = string.format("%s %i", _groupName, _groupId), side = + _side, country = _country, weight = _weight, jtac = _hasJTAC } return _details end --Special F10 function for players for troops function ctld.unloadExtractTroops(_args) - local _heli = ctld.getTransportUnit(_args[1]) if _heli == nil then @@ -2755,29 +2994,25 @@ function ctld.unloadExtractTroops(_args) else _extract = ctld.findNearestGroup(_heli, ctld.droppedTroopsBLUE) end - end if _extract ~= nil and not ctld.troopsOnboard(_heli, true) then -- search for nearest troops to pickup - return ctld.extractTroops({_heli:getName(), true}) + return ctld.extractTroops({ _heli:getName(), true }) else - return ctld.unloadTroops({_heli:getName(),true,true}) + return ctld.unloadTroops({ _heli:getName(), true, true }) end - - end -- load troops onto vehicle function ctld.loadTroops(_heli, _troops, _numberOrTemplate) - -- load troops + vehicles if c130 or herc -- "M1045 HMMWV TOW" -- "M1043 HMMWV Armament" local _onboard = ctld.inTransitTroops[_heli:getName()] --number doesnt apply to vehicles - if _numberOrTemplate == nil or (type(_numberOrTemplate) ~= "table" and type(_numberOrTemplate) ~= "number") then + if _numberOrTemplate == nil or (type(_numberOrTemplate) ~= "table" and type(_numberOrTemplate) ~= "number") then _numberOrTemplate = ctld.getTransportLimit(_heli:getTypeName()) end @@ -2794,18 +3029,20 @@ function ctld.loadTroops(_heli, _troops, _numberOrTemplate) if _troops then _onboard.troops = ctld.generateTroopTypes(_heli:getCoalition(), _numberOrTemplate, _heli:getCountry()) - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 loaded troops into %2", ctld.getPlayerNameOrType(_heli), _heli:getTypeName()), 10) + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 loaded troops into %2", ctld.getPlayerNameOrType(_heli), _heli:getTypeName()), 10) - ctld.processCallback({unit = _heli, onboard = _onboard.troops, action = "load_troops"}) + ctld.processCallback({ unit = _heli, onboard = _onboard.troops, action = "load_troops" }) else - _onboard.vehicles = ctld.generateVehiclesForTransport(_heli:getCoalition(), _heli:getCountry()) local _count = #_list - ctld.processCallback({unit = _heli, onboard = _onboard.vehicles, action = "load_vehicles"}) + ctld.processCallback({ unit = _heli, onboard = _onboard.vehicles, action = "load_vehicles" }) - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 loaded %2 vehicles into %3", ctld.getPlayerNameOrType(_heli), _count, _heli:getTypeName()), 10) + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 loaded %2 vehicles into %3", ctld.getPlayerNameOrType(_heli), _count, + _heli:getTypeName()), 10) end ctld.inTransitTroops[_heli:getName()] = _onboard @@ -2813,7 +3050,6 @@ function ctld.loadTroops(_heli, _troops, _numberOrTemplate) end function ctld.generateVehiclesForTransport(_side, _country) - local _vehicles = {} local _list if _side == 1 then @@ -2824,21 +3060,21 @@ function ctld.generateVehiclesForTransport(_side, _country) for _i, _type in ipairs(_list) do - local _unitId = ctld.getNextUnitId() local _weight = ctld.vehiclesWeight[_type] or 2500 - _vehicles[_i] = { type = _type, unitId = _unitId, name = string.format("Dropped %s #%i", _type, _unitId), weight = _weight } + _vehicles[_i] = { type = _type, unitId = _unitId, name = string.format("Dropped %s #%i", _type, _unitId), weight = + _weight } end local _groupId = ctld.getNextGroupId() - local _details = { units = _vehicles, groupId = _groupId, groupName = string.format("Dropped Group %i", _groupId), side = _side, country = _country } + local _details = { units = _vehicles, groupId = _groupId, groupName = string.format("Dropped Group %i", _groupId), side = + _side, country = _country } return _details end function ctld.loadUnloadFOBCrate(_args) - local _heli = ctld.getTransportUnit(_args[1]) local _troops = _args[2] @@ -2857,7 +3093,6 @@ function ctld.loadUnloadFOBCrate(_args) local _crateOnboard = ctld.inTransitFOBCrates[_heli:getName()] ~= nil if _inZone == false and _crateOnboard == true then - ctld.inTransitFOBCrates[_heli:getName()] = nil local _position = _heli:getPosition() @@ -2875,7 +3110,8 @@ function ctld.loadUnloadFOBCrate(_args) local _name = string.format("FOB Crate #%i", _unitId) - local _spawnedCrate = ctld.spawnFOBCrateStatic(_heli:getCountry(), ctld.getNextUnitId(), { x = _point.x + _xOffset, z = _point.z + _yOffset }, _name) + local _spawnedCrate = ctld.spawnFOBCrateStatic(_heli:getCountry(), ctld.getNextUnitId(), + { x = _point.x + _xOffset, z = _point.z + _yOffset }, _name) if _side == 1 then ctld.droppedFOBCratesRED[_name] = _name @@ -2883,35 +3119,32 @@ function ctld.loadUnloadFOBCrate(_args) ctld.droppedFOBCratesBLUE[_name] = _name end - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 delivered a FOB Crate", ctld.getPlayerNameOrType(_heli)), 10) + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 delivered a FOB Crate", ctld.getPlayerNameOrType(_heli)), 10) ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Delivered FOB Crate 60m at 6'oclock to you"), 10) - elseif _inZone == true and _crateOnboard == true then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("FOB Crate dropped back to base"), 10) ctld.inTransitFOBCrates[_heli:getName()] = nil - elseif _inZone == true and _crateOnboard == false then ctld.displayMessageToGroup(_heli, ctld.i18n_translate("FOB Crate Loaded"), 10) ctld.inTransitFOBCrates[_heli:getName()] = true - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 loaded a FOB Crate ready for delivery!", ctld.getPlayerNameOrType(_heli)), 10) - + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 loaded a FOB Crate ready for delivery!", ctld.getPlayerNameOrType(_heli)), 10) else - -- nearest Crate local _crates = ctld.getCratesAndDistance(_heli) local _nearestCrate = ctld.getClosestCrate(_heli, _crates, "FOB") if _nearestCrate ~= nil and _nearestCrate.dist < 150 then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("FOB Crate Loaded"), 10) ctld.inTransitFOBCrates[_heli:getName()] = true - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 loaded a FOB Crate ready for delivery!", ctld.getPlayerNameOrType(_heli)), 10) + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 loaded a FOB Crate ready for delivery!", ctld.getPlayerNameOrType(_heli)), 10) if _side == 1 then ctld.droppedFOBCratesRED[_nearestCrate.crateUnit:getName()] = nil @@ -2921,15 +3154,29 @@ function ctld.loadUnloadFOBCrate(_args) --remove _nearestCrate.crateUnit:destroy() - else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("There are no friendly logistic units nearby to load a FOB crate from!"), 10) + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate("There are no friendly logistic units nearby to load a FOB crate from!"), 10) end end end -function ctld.loadTroopsFromZone(_args) +function ctld.updateTroopsInGame(params, t) -- return count of troops in game by Coalition + if t == nil then t = timer.getTime() + 1; end + ctld.InfantryInGameCount = {0, 0} + for coalitionId=1, 2 do -- for each CoaId + for k,v in ipairs(coalition.getGroups(coalitionId, Group.Category.GROUND)) do -- for each GROUND type group + for index, unitObj in pairs(v:getUnits()) do -- for each unit in group + if unitObj:getDesc().attributes.Infantry then + ctld.InfantryInGameCount[coalitionId] = ctld.InfantryInGameCount[coalitionId] + 1 + end + end + end + end + return 5 -- reschedule each 5" +end +function ctld.loadTroopsFromZone(_args) local _heli = ctld.getTransportUnit(_args[1]) local _troops = _args[2] local _groupTemplate = _args[3] or "" @@ -2942,13 +3189,11 @@ function ctld.loadTroopsFromZone(_args) local _zone = ctld.inPickupZone(_heli) if ctld.troopsOnboard(_heli, _troops) then - if _troops then ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You already have troops onboard."), 10) else ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You already have vehicles onboard."), 10) end - return false end @@ -2976,20 +3221,27 @@ function ctld.loadTroopsFromZone(_args) -- search for nearest troops to pickup return ctld.extractTroops({_heli:getName(), _troops}) elseif _zone.inZone == true then - + local heloCoa = _heli:getCoalition() + ctld.logTrace("FG_ heloCoa = %s", ctld.p(heloCoa)) + ctld.logTrace("FG_ (ctld.nbLimitSpawnedTroops[1]~=0 or ctld.nbLimitSpawnedTroops[2]~=0) = %s", ctld.p(ctld.nbLimitSpawnedTroops[1]~=0 or ctld.nbLimitSpawnedTroops[2]~=0)) + ctld.logTrace("FG_ ctld.InfantryInGameCount[heloCoa] = %s", ctld.p(ctld.InfantryInGameCount[heloCoa])) + ctld.logTrace("FG_ _groupTemplate.total = %s", ctld.p(_groupTemplate.total)) + ctld.logTrace("FG_ ctld.nbLimitSpawnedTroops[%s].total = %s", ctld.p(heloCoa), ctld.p(ctld.nbLimitSpawnedTroops[heloCoa])) + + local limitReached = true + if (ctld.nbLimitSpawnedTroops[1]~=0 or ctld.nbLimitSpawnedTroops[2]~=0) and (ctld.InfantryInGameCount[heloCoa] + _groupTemplate.total > ctld.nbLimitSpawnedTroops[heloCoa]) then -- load troops only if Coa limit not reached + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Count Infantries limit in the mission reached, you can't load more troops"), 10) + return false + end if _zone.limit - 1 >= 0 then -- decrease zone counter by 1 ctld.updateZoneCounter(_zone.index, -1) - ctld.loadTroops(_heli, _troops,_groupTemplate) - return true else ctld.displayMessageToGroup(_heli, ctld.i18n_translate("This area has no more reinforcements available!"), 20) - return false end - else if _allowExtract then ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You are not in a pickup zone and no one is nearby to extract"), 10) @@ -3001,10 +3253,7 @@ function ctld.loadTroopsFromZone(_args) end end - - function ctld.unloadTroops(_args) - local _heli = ctld.getTransportUnit(_args[1]) local _troops = _args[2] @@ -3013,27 +3262,25 @@ function ctld.unloadTroops(_args) end local _zone = ctld.inPickupZone(_heli) - if not ctld.troopsOnboard(_heli, _troops) then - + if not ctld.troopsOnboard(_heli, _troops) then ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No one to unload"), 10) return false else - -- troops must be onboard to get here if _zone.inZone == true then - if _troops then ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Dropped troops back to base"), 20) - ctld.processCallback({unit = _heli, unloaded = ctld.inTransitTroops[_heli:getName()].troops, action = "unload_troops_zone"}) + ctld.processCallback({ unit = _heli, unloaded = ctld.inTransitTroops[_heli:getName()].troops, action = + "unload_troops_zone" }) ctld.inTransitTroops[_heli:getName()].troops = nil - else ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Dropped vehicles back to base"), 20) - ctld.processCallback({unit = _heli, unloaded = ctld.inTransitTroops[_heli:getName()].vehicles, action = "unload_vehicles_zone"}) + ctld.processCallback({ unit = _heli, unloaded = ctld.inTransitTroops[_heli:getName()].vehicles, action = + "unload_vehicles_zone" }) ctld.inTransitTroops[_heli:getName()].vehicles = nil end @@ -3044,17 +3291,13 @@ function ctld.unloadTroops(_args) ctld.updateZoneCounter(_zone.index, 1) return true - - elseif ctld.troopsOnboard(_heli, _troops) then - + elseif ctld.troopsOnboard(_heli, _troops) then return ctld.deployTroops(_heli, _troops) end end - end function ctld.extractTroops(_args) - local _heli = ctld.getTransportUnit(_args[1]) local _troops = _args[2] @@ -3066,7 +3309,7 @@ function ctld.extractTroops(_args) return false end - if ctld.troopsOnboard(_heli, _troops) then + if ctld.troopsOnboard(_heli, _troops) then if _troops then ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You already have troops onboard."), 10) else @@ -3085,7 +3328,6 @@ function ctld.extractTroops(_args) local _extracted = false if _troops then - local _extractTroops if _heli:getCoalition() == 1 then @@ -3096,26 +3338,28 @@ function ctld.extractTroops(_args) if _extractTroops ~= nil then - local _limit = ctld.getTransportLimit(_heli:getTypeName()) - local _size = #_extractTroops.group:getUnits() + local _size = #_extractTroops.group:getUnits() if _limit < #_extractTroops.group:getUnits() then - - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Sorry - The group of %1 is too large to fit. \n\nLimit is %2 for %3", _size, _limit, _heli:getTypeName()), 20) + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate("Sorry - The group of %1 is too large to fit. \n\nLimit is %2 for %3", _size, + _limit, _heli:getTypeName()), 20) return end _onboard.troops = _extractTroops.details - _onboard.troops.weight = #_extractTroops.group:getUnits() * 130 -- default to 130kg per soldier + _onboard.troops.weight = #_extractTroops.group:getUnits() * 130 -- default to 130kg per soldier if _extractTroops.group:getName():lower():find("jtac") then _onboard.troops.jtac = true end - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 extracted troops in %2 from combat", ctld.getPlayerNameOrType(_heli), _heli:getTypeName()), 10) + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 extracted troops in %2 from combat", ctld.getPlayerNameOrType(_heli), + _heli:getTypeName()), 10) if _heli:getCoalition() == 1 then ctld.droppedTroopsRED[_extractTroops.group:getName()] = nil @@ -3123,7 +3367,7 @@ function ctld.extractTroops(_args) ctld.droppedTroopsBLUE[_extractTroops.group:getName()] = nil end - ctld.processCallback({unit = _heli, extracted = _extractTroops, action = "extract_troops"}) + ctld.processCallback({ unit = _heli, extracted = _extractTroops, action = "extract_troops" }) --remove _extractTroops.group:destroy() @@ -3133,17 +3377,13 @@ function ctld.extractTroops(_args) _onboard.troops = nil ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No extractable troops nearby!"), 20) end - else - local _extractVehicles if _heli:getCoalition() == 1 then - _extractVehicles = ctld.findNearestGroup(_heli, ctld.droppedVehiclesRED) else - _extractVehicles = ctld.findNearestGroup(_heli, ctld.droppedVehiclesBLUE) end @@ -3151,20 +3391,19 @@ function ctld.extractTroops(_args) _onboard.vehicles = _extractVehicles.details if _heli:getCoalition() == 1 then - ctld.droppedVehiclesRED[_extractVehicles.group:getName()] = nil else - ctld.droppedVehiclesBLUE[_extractVehicles.group:getName()] = nil end - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 extracted vehicles in %2 from combat", ctld.getPlayerNameOrType(_heli), _heli:getTypeName()), 10) + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 extracted vehicles in %2 from combat", ctld.getPlayerNameOrType(_heli), + _heli:getTypeName()), 10) - ctld.processCallback({unit = _heli, extracted = _extractVehicles, action = "extract_vehicles"}) + ctld.processCallback({ unit = _heli, extracted = _extractVehicles, action = "extract_vehicles" }) --remove _extractVehicles.group:destroy() _extracted = true - else _onboard.vehicles = nil ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No extractable vehicles nearby!"), 20) @@ -3177,7 +3416,6 @@ function ctld.extractTroops(_args) return _extracted end - function ctld.checkTroopStatus(_args) local _unitName = _args[1] --list onboard troops, if c130 @@ -3195,11 +3433,9 @@ end -- Removes troops from transport when it dies function ctld.checkTransportStatus() - timer.scheduleFunction(ctld.checkTransportStatus, nil, timer.getTime() + 3) for _, _name in ipairs(ctld.transportPilotNames) do - local _transUnit = ctld.getTransportUnit(_name) if _transUnit == nil then @@ -3217,7 +3453,6 @@ function ctld.adaptWeightToCargo(unitName) end function ctld.getWeightOfCargo(unitName) - local FOB_CRATE_WEIGHT = 800 local _weight = 0 local _description = "" @@ -3230,7 +3465,8 @@ function ctld.getWeightOfCargo(unitName) if _inTransit then local _troops = _inTransit.troops if _troops and _troops.units then - _description = _description .. ctld.i18n_translate("%1 troops onboard (%2 kg)\n", #_troops.units, _troops.weight) + _description = _description .. + ctld.i18n_translate("%1 troops onboard (%2 kg)\n", #_troops.units, _troops.weight) _weight = _weight + _troops.weight end local _vehicles = _inTransit.vehicles @@ -3238,7 +3474,8 @@ function ctld.getWeightOfCargo(unitName) for _, _unit in pairs(_vehicles.units) do _weight = _weight + _unit.weight end - _description = _description .. ctld.i18n_translate("%1 vehicles onboard (%2)\n", #_vehicles.units, _weight) + _description = _description .. + ctld.i18n_translate("%1 vehicles onboard (%2)\n", #_vehicles.units, _weight) end end end @@ -3270,9 +3507,7 @@ function ctld.checkHoverStatus() timer.scheduleFunction(ctld.checkHoverStatus, nil, timer.getTime() + 1.0) local _status, _result = pcall(function() - for _, _name in ipairs(ctld.transportPilotNames) do - local _reset = true local _transUnit = ctld.getTransportUnit(_name) local _transUnitTypeName = _transUnit and _transUnit:getTypeName() @@ -3281,18 +3516,14 @@ function ctld.checkHoverStatus() --only check transports that are hovering and not planes if _transUnit ~= nil and #ctld.inTransitSlingLoadCrates[_name] < _cargoCapacity and ctld.inAir(_transUnit) and ctld.unitCanCarryVehicles(_transUnit) == false and not ctld.unitDynamicCargoCapable(_transUnit) then - - local _crates = ctld.getCratesAndDistance(_transUnit) for _, _crate in pairs(_crates) do local _crateUnitName = _crate.crateUnit:getName() if _crate.dist < ctld.maxDistanceFromCrate and _crate.details.unit ~= "FOB" then - --check height! local _height = _transUnit:getPoint().y - _crate.crateUnit:getPoint().y if _height > ctld.minimumHoverHeight and _height <= ctld.maximumHoverHeight then - local _time = ctld.hoverStatus[_name] if _time == nil then @@ -3304,10 +3535,14 @@ function ctld.checkHoverStatus() end if _time > 0 then - ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("Hovering above %1 crate. \n\nHold hover for %2 seconds! \n\nIf the countdown stops you're too far away!", _crate.details.desc, _time), 10,true) + ctld.displayMessageToGroup(_transUnit, + ctld.i18n_translate( + "Hovering above %1 crate. \n\nHold hover for %2 seconds! \n\nIf the countdown stops you're too far away!", + _crate.details.desc, _time), 10, true) else ctld.hoverStatus[_name] = nil - ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("Loaded %1 crate!", _crate.details.desc), 10,true) + ctld.displayMessageToGroup(_transUnit, + ctld.i18n_translate("Loaded %1 crate!", _crate.details.desc), 10, true) --crates been moved once! ctld.crateMove[_crateUnitName] = nil @@ -3330,10 +3565,14 @@ function ctld.checkHoverStatus() break elseif _height <= ctld.minimumHoverHeight then - ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("Too low to hook %1 crate.\n\nHold hover for %2 seconds", _crate.details.desc, ctld.hoverTime), 5,true) + ctld.displayMessageToGroup(_transUnit, + ctld.i18n_translate("Too low to hook %1 crate.\n\nHold hover for %2 seconds", + _crate.details.desc, ctld.hoverTime), 5, true) break else - ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("Too high to hook %1 crate.\n\nHold hover for %2 seconds", _crate.details.desc, ctld.hoverTime), 5,true) + ctld.displayMessageToGroup(_transUnit, + ctld.i18n_translate("Too high to hook %1 crate.\n\nHold hover for %2 seconds", + _crate.details.desc, ctld.hoverTime), 5, true) break end end @@ -3354,23 +3593,23 @@ end function ctld.loadNearbyCrate(_name) local _transUnit = ctld.getTransportUnit(_name) - if _transUnit ~= nil then - + if _transUnit ~= nil then local _cargoCapacity = ctld.internalCargoLimits[_transUnit:getTypeName()] or 1 ctld.inTransitSlingLoadCrates[_name] = ctld.inTransitSlingLoadCrates[_name] or {} if ctld.inAir(_transUnit) then - ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("You must land before you can load a crate!"), 10,true) + ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("You must land before you can load a crate!"), 10, + true) return end local _crates = ctld.getCratesAndDistance(_transUnit) local loaded = false for _, _crate in pairs(_crates) do - if _crate.dist < 50.0 then if #ctld.inTransitSlingLoadCrates[_name] < _cargoCapacity then - ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("Loaded %1 crate!", _crate.details.desc), 10) + ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("Loaded %1 crate!", _crate.details.desc), + 10) if _transUnit:getCoalition() == 1 then ctld.spawnedCratesRED[_crate.crateUnit:getName()] = nil @@ -3393,13 +3632,13 @@ function ctld.loadNearbyCrate(_name) for i = 1, _cargoCapacity do outputMsg = outputMsg .. "\n" .. ctld.inTransitSlingLoadCrates[_name][i].desc end - ctld.displayMessageToGroup(_transUnit, outputMsg, 10,true) + ctld.displayMessageToGroup(_transUnit, outputMsg, 10, true) return end end end if not loaded then - ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("No Crates within 50m to load!"), 10,true) + ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("No Crates within 50m to load!"), 10, true) end end end @@ -3407,19 +3646,15 @@ end --check each minute if the beacons' batteries have failed, and stop them accordingly --there's no more need to actually refresh the beacons, since we set "loop" to true. function ctld.refreshRadioBeacons() - timer.scheduleFunction(ctld.refreshRadioBeacons, nil, timer.getTime() + 60) for _index, _beaconDetails in ipairs(ctld.deployedRadioBeacons) do - if ctld.updateRadioBeacon(_beaconDetails) == false then - --search used frequencies + remove, add back to unused for _i, _freq in ipairs(ctld.usedUHFFrequencies) do if _freq == _beaconDetails.uhf then - table.insert(ctld.freeUHFFrequencies, _freq) table.remove(ctld.usedUHFFrequencies, _i) end @@ -3427,7 +3662,6 @@ function ctld.refreshRadioBeacons() for _i, _freq in ipairs(ctld.usedVHFFrequencies) do if _freq == _beaconDetails.vhf then - table.insert(ctld.freeVHFFrequencies, _freq) table.remove(ctld.usedVHFFrequencies, _i) end @@ -3435,7 +3669,6 @@ function ctld.refreshRadioBeacons() for _i, _freq in ipairs(ctld.usedFMFrequencies) do if _freq == _beaconDetails.fm then - table.insert(ctld.freeFMFrequencies, _freq) table.remove(ctld.usedFMFrequencies, _i) end @@ -3448,18 +3681,18 @@ function ctld.refreshRadioBeacons() end function ctld.getClockDirection(_heli, _crate) - -- Source: Helicopter Script - Thanks! - local _position = _crate:getPosition().p -- get position of crate - local _playerPosition = _heli:getPosition().p -- get position of helicopter + local _position = _crate:getPosition().p -- get position of crate + local _playerPosition = _heli:getPosition().p -- get position of helicopter local _relativePosition = mist.vec.sub(_position, _playerPosition) - local _playerHeading = mist.getHeading(_heli) -- the rest of the code determines the 'o'clock' bearing of the missile relative to the helicopter + local _playerHeading = mist.getHeading(_heli) -- the rest of the code determines the 'o'clock' bearing of the missile relative to the helicopter local _headingVector = { x = math.cos(_playerHeading), y = 0, z = math.sin(_playerHeading) } - local _headingVectorPerpendicular = { x = math.cos(_playerHeading + math.pi / 2), y = 0, z = math.sin(_playerHeading + math.pi / 2) } + local _headingVectorPerpendicular = { x = math.cos(_playerHeading + math.pi / 2), y = 0, z = math.sin(_playerHeading + + math.pi / 2) } local _forwardDistance = mist.vec.dp(_relativePosition, _headingVector) @@ -3478,11 +3711,9 @@ function ctld.getClockDirection(_heli, _crate) return _angle end - function ctld.getCompassBearing(_ref, _unitPos) - - _ref = mist.utils.makeVec3(_ref, 0) -- turn it into Vec3 if it is not already. - _unitPos = mist.utils.makeVec3(_unitPos, 0) -- turn it into Vec3 if it is not already. + _ref = mist.utils.makeVec3(_ref, 0) -- turn it into Vec3 if it is not already. + _unitPos = mist.utils.makeVec3(_unitPos, 0) -- turn it into Vec3 if it is not already. local _vec = { x = _unitPos.x - _ref.x, y = _unitPos.y - _ref.y, z = _unitPos.z - _ref.z } @@ -3494,41 +3725,39 @@ function ctld.getCompassBearing(_ref, _unitPos) end function ctld.listNearbyCrates(_args) - local _message = "" local _heli = ctld.getTransportUnit(_args[1]) if _heli == nil then - - return -- no heli! + return -- no heli! end local _crates = ctld.getCratesAndDistance(_heli) --sort - local _sort = function( a,b ) return a.dist < b.dist end - table.sort(_crates,_sort) + local _sort = function(a, b) return a.dist < b.dist end + table.sort(_crates, _sort) for _, _crate in pairs(_crates) do - if _crate.dist < 1000 and _crate.details.unit ~= "FOB" then - _message = ctld.i18n_translate("%1\n%2 crate - kg %3 - %4 m - %5 o'clock", _message, _crate.details.desc, _crate.details.weight, _crate.dist, ctld.getClockDirection(_heli, _crate.crateUnit)) + _message = ctld.i18n_translate("%1\n%2 crate - kg %3 - %4 m - %5 o'clock", _message, _crate.details.desc, + _crate.details.weight, _crate.dist, ctld.getClockDirection(_heli, _crate.crateUnit)) end end local _fobMsg = "" for _, _fobCrate in pairs(_crates) do - if _fobCrate.dist < 1000 and _fobCrate.details.unit == "FOB" then - _fobMsg = _fobMsg .. ctld.i18n_translate("FOB Crate - %1 m - %2 o'clock\n", _fobCrate.dist, ctld.getClockDirection(_heli, _fobCrate.crateUnit)) + _fobMsg = _fobMsg .. + ctld.i18n_translate("FOB Crate - %1 m - %2 o'clock\n", _fobCrate.dist, + ctld.getClockDirection(_heli, _fobCrate.crateUnit)) end end local _txt = ctld.i18n_translate("No Nearby Crates") if _message ~= "" or _fobMsg ~= "" then - _txt = "" if _message ~= "" then @@ -3536,7 +3765,6 @@ function ctld.listNearbyCrates(_args) end if _fobMsg ~= "" then - if _txt ~= "" then _txt = _txt .. "\n\n" end @@ -3547,16 +3775,13 @@ function ctld.listNearbyCrates(_args) ctld.displayMessageToGroup(_heli, _txt, 20) end - function ctld.listFOBS(_args) - local _msg = ctld.i18n_translate("FOB Positions:") local _heli = ctld.getTransportUnit(_args[1]) if _heli == nil then - - return -- no heli! + return -- no heli! end -- get fob positions @@ -3574,12 +3799,11 @@ function ctld.listFOBS(_args) end function ctld.getFOBPositionString(_fob) - local _lat, _lon = coord.LOtoLL(_fob:getPosition().p) local _latLngStr = mist.tostringLL(_lat, _lon, 3, ctld.location_DMS) - -- local _mgrsString = mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(_fob:getPosition().p)), 5) + -- local _mgrsString = mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(_fob:getPosition().p)), 5) local _message = _latLngStr @@ -3594,13 +3818,11 @@ function ctld.getFOBPositionString(_fob) return _message end - -function ctld.displayMessageToGroup(_unit, _text, _time,_clear) - +function ctld.displayMessageToGroup(_unit, _text, _time, _clear) local _groupId = ctld.getGroupId(_unit) if _groupId then if _clear == true then - trigger.action.outTextForGroup(_groupId, _text, _time,_clear) + trigger.action.outTextForGroup(_groupId, _text, _time, _clear) else trigger.action.outTextForGroup(_groupId, _text, _time) end @@ -3608,7 +3830,6 @@ function ctld.displayMessageToGroup(_unit, _text, _time,_clear) end function ctld.heightDiff(_unit) - local _point = _unit:getPoint() -- env.info("heightunit " .. _point.y) @@ -3619,7 +3840,6 @@ end --includes fob crates! function ctld.getCratesAndDistance(_heli) - local _crates = {} local _allCrates @@ -3630,14 +3850,12 @@ function ctld.getCratesAndDistance(_heli) end for _crateName, _details in pairs(_allCrates) do - --get crate local _crate = ctld.getCrateObject(_crateName) --in air seems buggy with crates so if in air is true, get the height above ground and the speed magnitude if _crate ~= nil and _crate:getLife() > 0 - and (ctld.inAir(_crate) == false) then - + and (ctld.inAir(_crate) == false) then local _dist = ctld.getDistance(_crate:getPoint(), _heli:getPoint()) local _crateDetails = { crateUnit = _crate, dist = _dist, details = _details } @@ -3654,12 +3872,10 @@ function ctld.getCratesAndDistance(_heli) end for _crateName, _details in pairs(_fobCrates) do - --get crate local _crate = ctld.getCrateObject(_crateName) if _crate ~= nil and _crate:getLife() > 0 then - local _dist = ctld.getDistance(_crate:getPoint(), _heli:getPoint()) local _crateDetails = { crateUnit = _crate, dist = _dist, details = { unit = "FOB" }, } @@ -3671,20 +3887,17 @@ function ctld.getCratesAndDistance(_heli) return _crates end - function ctld.getClosestCrate(_heli, _crates, _type) - local _closetCrate = nil local _shortestDistance = -1 local _distance = 0 - local _minimumDistance = 5 -- prevents dynamic cargo crates from unpacking while in cargo hold - + local _minimumDistance = 5 -- prevents dynamic cargo crates from unpacking while in cargo hold + local _maxDistance = 20 -- prevents onboard dynamic cargo crates from unpacking requested by other helo for _, _crate in pairs(_crates) do - if (_crate.details.unit == _type or _type == nil) then _distance = _crate.dist - if _distance ~= nil and (_shortestDistance == -1 or _distance < _shortestDistance) and _distance > _minimumDistance then + if _distance ~= nil and (_shortestDistance == -1 or _distance < _shortestDistance) and _distance > _minimumDistance and _distance < _maxDistance then _shortestDistance = _distance _closetCrate = _crate end @@ -3694,25 +3907,20 @@ function ctld.getClosestCrate(_heli, _crates, _type) return _closetCrate end -function ctld.findNearestAASystem(_heli,_aaSystem) - +function ctld.findNearestAASystem(_heli, _aaSystem) local _closestHawkGroup = nil local _shortestDistance = -1 local _distance = 0 for _groupName, _hawkDetails in pairs(ctld.completeAASystems) do - local _hawkGroup = Group.getByName(_groupName) - -- env.info(_groupName..": "..mist.utils.tableShow(_hawkDetails)) + -- env.info(_groupName..": "..mist.utils.tableShow(_hawkDetails)) if _hawkGroup ~= nil and _hawkGroup:getCoalition() == _heli:getCoalition() and _hawkDetails[1].system.name == _aaSystem.name then - local _units = _hawkGroup:getUnits() for _, _leader in pairs(_units) do - if _leader ~= nil and _leader:getLife() > 0 then - _distance = ctld.getDistance(_leader:getPoint(), _heli:getPoint()) if _distance ~= nil and (_shortestDistance == -1 or _distance < _shortestDistance) then @@ -3727,7 +3935,6 @@ function ctld.findNearestAASystem(_heli,_aaSystem) end if _closestHawkGroup ~= nil then - return { group = _closestHawkGroup, dist = _shortestDistance } end return nil @@ -3737,47 +3944,38 @@ function ctld.getCrateObject(_name) local _crate if ctld.staticBugWorkaround then - _crate = Unit.getByName(_name) + _crate = Unit.getByName(_name) else _crate = StaticObject.getByName(_name) end return _crate end - - function ctld.unpackCrates(_arguments) - + ctld.logTrace("FG_ ctld.unpackCrates._arguments = %s", ctld.p(_arguments)) local _status, _err = pcall(function(_args) - local _heli = ctld.getTransportUnit(_args[1]) - + ctld.logTrace("FG_ ctld.unpackCrates._args = %s", ctld.p(_args)) if _heli ~= nil and ctld.inAir(_heli) == false then - local _crates = ctld.getCratesAndDistance(_heli) local _crate = ctld.getClosestCrate(_heli, _crates) - - - if ctld.inLogisticsZone(_heli) == true or ctld.farEnoughFromLogisticZone(_heli) == false then - - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You can't unpack that here! Take it to where it's needed!"), 20) - + ctld.logTrace("FG_ ctld.unpackCrates._crate = %s", ctld.p(_crate)) + + if ctld.inLogisticsZone(_heli) == true or ctld.farEnoughFromLogisticZone(_heli) == false then + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate("You can't unpack that here! Take it to where it's needed!"), 20) return end - - if _crate ~= nil and _crate.dist < 750 - and (_crate.details.unit == "FOB" or _crate.details.unit == "FOB-SMALL") then - + and (_crate.details.unit == "FOB" or _crate.details.unit == "FOB-SMALL") then ctld.unpackFOBCrates(_crates, _heli) return - elseif _crate ~= nil and _crate.dist < 200 then - if ctld.forceCrateToBeMoved and ctld.crateMove[_crate.crateUnit:getName()] and not ctld.unitDynamicCargoCapable(_heli) then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Sorry you must move this crate before you unpack it!"), 20) + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate("Sorry you must move this crate before you unpack it!"), 20) return end @@ -3785,14 +3983,13 @@ function ctld.unpackCrates(_arguments) local _aaTemplate = ctld.getAATemplate(_crate.details.unit) if _aaTemplate then - if _crate.details.unit == _aaTemplate.repair then - ctld.repairAASystem(_heli, _crate,_aaTemplate) + ctld.repairAASystem(_heli, _crate, _aaTemplate) else - ctld.unpackAASystem(_heli, _crate, _crates,_aaTemplate) + ctld.unpackAASystem(_heli, _crate, _crates, _aaTemplate) end - return -- stop processing + return -- stop processing -- is multi crate? elseif _crate.details.cratesRequired ~= nil and _crate.details.cratesRequired > 1 then -- multicrate @@ -3800,50 +3997,55 @@ function ctld.unpackCrates(_arguments) ctld.unpackMultiCrate(_heli, _crate, _crates) return - else + ctld.logTrace("single crate = %s", ctld.p(_arguments)) -- single crate - local _cratePoint = _crate.crateUnit:getPoint() - local _crateName = _crate.crateUnit:getName() - local _crateHdg = mist.getHeading(_crate.crateUnit, true) + --local _cratePoint = _crate.crateUnit:getPoint() + local _point = ctld.getPointInFrontSector(_heli, ctld.getSecureDistanceFromUnit(_heli:getName())) + if ctld.unitDynamicCargoCapable(_heli) == true then + _point = ctld.getPointInRearSector(_heli, ctld.getSecureDistanceFromUnit(_heli:getName())) + end + local _crateName = _crate.crateUnit:getName() + local _crateHdg = mist.getHeading(_crate.crateUnit, true) --remove crate - -- if ctld.slingLoad == false then - _crate.crateUnit:destroy() - -- end - - local _spawnedGroups = ctld.spawnCrateGroup(_heli, { _cratePoint }, { _crate.details.unit }, { _crateHdg }) - + -- if ctld.slingLoad == false then + _crate.crateUnit:destroy() + -- end + ctld.logTrace("_crate = %s", ctld.p(_crate)) + local _spawnedGroups = ctld.spawnCrateGroup(_heli, { _point }, { _crate.details.unit }, { _crateHdg }) + ctld.logTrace("_spawnedGroups = %s", ctld.p(_spawnedGroups)) + if _heli:getCoalition() == 1 then ctld.spawnedCratesRED[_crateName] = nil else ctld.spawnedCratesBLUE[_crateName] = nil end - ctld.processCallback({unit = _heli, crate = _crate , spawnedGroup = _spawnedGroups, action = "unpack"}) + ctld.processCallback({ unit = _heli, crate = _crate, spawnedGroup = _spawnedGroups, action = "unpack" }) if _crate.details.unit == "1L13 EWR" then ctld.addEWRTask(_spawnedGroups) - -- env.info("Added EWR") + -- env.info("Added EWR") end - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 successfully deployed %2 to the field", ctld.getPlayerNameOrType(_heli), _crate.details.desc), 10) - + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 successfully deployed %2 to the field", ctld.getPlayerNameOrType(_heli), + _crate.details.desc), 10) + timer.scheduleFunction(ctld.autoUpdateRepackMenu, { reschedule = false }, timer.getTime() + 1) -- for add unpacked unit in repack menu if ctld.isJTACUnitType(_crate.details.unit) and ctld.JTAC_dropEnabled then - local _code = table.remove(ctld.jtacGeneratedLaserCodes, 1) --put to the end table.insert(ctld.jtacGeneratedLaserCodes, _code) - ctld.JTACStart(_spawnedGroups:getName(), _code) --(_jtacGroupName, _laserCode, _smoke, _lock, _colour) + ctld.JTACStart(_spawnedGroups:getName(), _code) --(_jtacGroupName, _laserCode, _smoke, _lock, _colour) end end - else - - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No friendly crates close enough to unpack, or crate too close to aircraft."), 20) + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate("No friendly crates close enough to unpack, or crate too close to aircraft."), 20) end end end, _arguments) @@ -3853,13 +4055,11 @@ function ctld.unpackCrates(_arguments) end end - -- builds a fob! function ctld.unpackFOBCrates(_crates, _heli) - if ctld.inLogisticsZone(_heli) == true then - - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You can't unpack that here! Take it to where it's needed!"), 20) + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate("You can't unpack that here! Take it to where it's needed!"), 20) return end @@ -3872,10 +4072,8 @@ function ctld.unpackFOBCrates(_crates, _heli) local _totalCrates = 0 for _, _nearbyCrate in pairs(_crates) do - - if _nearbyCrate.dist < 750 then - - if _nearbyCrate.details.unit == "FOB" then + if _nearbyCrate.dist < 750 then + if _nearbyCrate.details.unit == "FOB" then _bigFobCrates = _bigFobCrates + 1 table.insert(_nearbyMultiCrates, _nearbyCrate) elseif _nearbyCrate.details.unit == "FOB-SMALL" then @@ -3885,7 +4083,7 @@ function ctld.unpackFOBCrates(_crates, _heli) --catch divide by 0 if _smallFobCrates > 0 then - _totalCrates = _bigFobCrates + (_smallFobCrates/3.0) + _totalCrates = _bigFobCrates + (_smallFobCrates / 3.0) else _totalCrates = _bigFobCrates end @@ -3898,13 +4096,11 @@ function ctld.unpackFOBCrates(_crates, _heli) --- check crate count if _totalCrates >= ctld.cratesRequiredForFOB then - -- destroy crates local _points = {} for _, _crate in pairs(_nearbyMultiCrates) do - if _heli:getCoalition() == 1 then ctld.droppedFOBCratesRED[_crate.crateUnit:getName()] = nil ctld.spawnedCratesRED[_crate.crateUnit:getName()] = nil @@ -3922,7 +4118,6 @@ function ctld.unpackFOBCrates(_crates, _heli) local _centroid = ctld.getCentroid(_points) timer.scheduleFunction(function(_args) - local _unitId = ctld.getNextUnitId() local _name = "Deployed FOB #" .. _unitId @@ -3937,24 +4132,32 @@ function ctld.unpackFOBCrates(_crates, _heli) local _radioBeaconDetails = ctld.createRadioBeacon(_args[1], _args[3], _args[2], _radioBeaconName, nil, true) - ctld.fobBeacons[_name] = { vhf = _radioBeaconDetails.vhf, uhf = _radioBeaconDetails.uhf, fm = _radioBeaconDetails.fm } + ctld.fobBeacons[_name] = { vhf = _radioBeaconDetails.vhf, uhf = _radioBeaconDetails.uhf, fm = + _radioBeaconDetails.fm } if ctld.troopPickupAtFOB == true then table.insert(ctld.builtFOBS, _fob:getName()) - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("Finished building FOB! Crates and Troops can now be picked up."), 10) + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("Finished building FOB! Crates and Troops can now be picked up."), 10) else - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("Finished building FOB! Crates can now be picked up."), 10) + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("Finished building FOB! Crates can now be picked up."), 10) end end, { _centroid, _heli:getCountry(), _heli:getCoalition() }, timer.getTime() + ctld.buildTimeFOB) - ctld.processCallback({unit = _heli, position = _centroid, action = "fob"}) + ctld.processCallback({ unit = _heli, position = _centroid, action = "fob" }) trigger.action.smoke(_centroid, trigger.smokeColor.Green) - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 started building FOB using %2 FOB crates, it will be finished in %3 seconds.\nPosition marked with smoke.", ctld.getPlayerNameOrType(_heli), _totalCrates, ctld.buildTimeFOB, 10)) + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate( + "%1 started building FOB using %2 FOB crates, it will be finished in %3 seconds.\nPosition marked with smoke.", + ctld.getPlayerNameOrType(_heli), _totalCrates, ctld.buildTimeFOB, 10)) else - local _txt = ctld.i18n_translate("Cannot build FOB!\n\nIt requires %1 Large FOB crates ( 3 small FOB crates equal 1 large FOB Crate) and there are the equivalent of %2 large FOB crates nearby\n\nOr the crates are not within 750m of each other", ctld.cratesRequiredForFOB, _totalCrates) + local _txt = ctld.i18n_translate( + "Cannot build FOB!\n\nIt requires %1 Large FOB crates ( 3 small FOB crates equal 1 large FOB Crate) and there are the equivalent of %2 large FOB crates nearby\n\nOr the crates are not within 750m of each other", + ctld.cratesRequiredForFOB, _totalCrates) ctld.displayMessageToGroup(_heli, _txt, 20) end end @@ -3966,18 +4169,27 @@ function ctld.dropSlingCrate(_args) ctld.inTransitSlingLoadCrates[_unitName] = ctld.inTransitSlingLoadCrates[_unitName] or {} if _heli == nil then - return -- no heli! + return -- no heli! end local _currentCrate = ctld.inTransitSlingLoadCrates[_unitName][#ctld.inTransitSlingLoadCrates[_unitName]] if _currentCrate == nil then if ctld.hoverPickup and ctld.loadCrateFromMenu then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You are not currently transporting any crates. \n\nTo Pickup a crate, hover for %1 seconds above the crate or land and use F10 Crate Commands.", ctld.hoverTime), 10) + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate( + "You are not currently transporting any crates. \n\nTo Pickup a crate, hover for %1 seconds above the crate or land and use F10 Crate Commands.", + ctld.hoverTime), 10) elseif ctld.hoverPickup then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You are not currently transporting any crates. \n\nTo Pickup a crate, hover for %1 seconds above the crate.", ctld.hoverTime), 10) + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate( + "You are not currently transporting any crates. \n\nTo Pickup a crate, hover for %1 seconds above the crate.", + ctld.hoverTime), 10) else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You are not currently transporting any crates. \n\nTo Pickup a crate, land and use F10 Crate Commands to load one."), 10) + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate( + "You are not currently transporting any crates. \n\nTo Pickup a crate, land and use F10 Crate Commands to load one."), + 10) end else local _point = _heli:getPoint() @@ -3986,7 +4198,7 @@ function ctld.dropSlingCrate(_args) local _heightDiff = ctld.heightDiff(_heli) if _heightDiff > 40.0 then - table.remove(ctld.inTransitSlingLoadCrates[_unitName],#ctld.inTransitSlingLoadCrates[_unitName]) + table.remove(ctld.inTransitSlingLoadCrates[_unitName], #ctld.inTransitSlingLoadCrates[_unitName]) ctld.adaptWeightToCargo(_unitName) ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You were too high! The crate has been destroyed"), 10) return @@ -4001,19 +4213,22 @@ function ctld.dropSlingCrate(_args) local _name = string.format("%s #%i", _crate.desc, _unitId) local _model_type = nil if ctld.inAir(_heli) == false or _heightDiff <= 7.5 then - _point = ctld.getPointAt12Oclock(_heli, 30) + _point = ctld.getPointAt12Oclock(_heli, 15) local _position = "12" if ctld.unitDynamicCargoCapable(_heli) then _model_type = "dynamic" _point = ctld.getPointAt6Oclock(_heli, 15) _position = "6" end - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("%1 crate has been safely unhooked and is at your %2 o'clock", _crate.desc, _position), 10) + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate("%1 crate has been safely unhooked and is at your %2 o'clock", _crate.desc, + _position), 10) elseif _heightDiff > 7.5 and _heightDiff <= 40.0 then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("%1 crate has been safely dropped below you", _crate.desc), 10) + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate("%1 crate has been safely dropped below you", _crate.desc), 10) end --remove crate from cargo - table.remove(ctld.inTransitSlingLoadCrates[_unitName],#ctld.inTransitSlingLoadCrates[_unitName]) + table.remove(ctld.inTransitSlingLoadCrates[_unitName], #ctld.inTransitSlingLoadCrates[_unitName]) ctld.spawnCrateStatic(_heli:getCountry(), _unitId, _point, _name, _crate.weight, _side, _hdg, _model_type) end ctld.adaptWeightToCargo(_unitName) @@ -4024,7 +4239,6 @@ end -- one for VHF and one for UHF -- The units are set to to NOT engage function ctld.createRadioBeacon(_point, _coalition, _country, _name, _batteryTime, _isFOB) - local _freq = ctld.generateADFFrequencies() --create timeout @@ -4045,8 +4259,8 @@ function ctld.createRadioBeacon(_point, _coalition, _country, _name, _batteryTim local _freqsText = _name if _isFOB then - -- _message = "FOB " .. _message - _battery = -1 --never run out of power! + -- _message = "FOB " .. _message + _battery = -1 --never run out of power! end _freqsText = _freqsText .. " - " .. _latLngStr @@ -4078,7 +4292,6 @@ function ctld.createRadioBeacon(_point, _coalition, _country, _name, _batteryTim end function ctld.generateADFFrequencies() - if #ctld.freeUHFFrequencies <= 3 then ctld.freeUHFFrequencies = ctld.usedUHFFrequencies ctld.usedUHFFrequencies = {} @@ -4109,33 +4322,30 @@ function ctld.generateADFFrequencies() --- return {uhf=_uhf,vhf=_vhf} end - - function ctld.spawnRadioBeaconUnit(_point, _country, _name, _freqsText) - local _groupId = ctld.getNextGroupId() local _unitId = ctld.getNextUnitId() local _radioGroup = { ["visible"] = false, - -- ["groupId"] = _groupId, + -- ["groupId"] = _groupId, ["hidden"] = false, ["units"] = { [1] = { ["y"] = _point.z, ["type"] = "TACAN_beacon", ["name"] = "Unit #" .. _unitId .. " - " .. _name .. " [" .. _freqsText .. "]", - -- ["unitId"] = _unitId, + -- ["unitId"] = _unitId, ["heading"] = 0, ["playerCanDrive"] = true, ["skill"] = "Excellent", ["x"] = _point.x, } }, - -- ["y"] = _positions[1].z, - -- ["x"] = _positions[1].x, - ["name"] = "Group #" .. _groupId .. " - " .. _name, + -- ["y"] = _positions[1].z, + -- ["x"] = _positions[1].x, + ["name"] = "Group #" .. _groupId .. " - " .. _name, ["task"] = {}, --added two fields below for MIST ["category"] = Group.Category.GROUND, @@ -4147,7 +4357,6 @@ function ctld.spawnRadioBeaconUnit(_point, _country, _name, _freqsText) end function ctld.updateRadioBeacon(_beaconDetails) - local _vhfGroup = Group.getByName(_beaconDetails.vhfGroup) local _uhfGroup = Group.getByName(_beaconDetails.uhfGroup) @@ -4189,12 +4398,11 @@ function ctld.updateRadioBeacon(_beaconDetails) end --fobs have unlimited battery life - -- if _battery ~= -1 then - -- _text = _text.." "..mist.utils.round(_batLife).." seconds of battery" - -- end + -- if _battery ~= -1 then + -- _text = _text.." "..mist.utils.round(_batLife).." seconds of battery" + -- end for _, _radio in pairs(_radioLoop) do - local _groupController = _radio.group:getController() local _sound = ctld.radioSound @@ -4202,7 +4410,7 @@ function ctld.updateRadioBeacon(_beaconDetails) _sound = ctld.radioSoundFC3 end - _sound = "l10n/DEFAULT/".._sound + _sound = "l10n/DEFAULT/" .. _sound _groupController:setOption(AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.WEAPON_HOLD) @@ -4212,21 +4420,19 @@ function ctld.updateRadioBeacon(_beaconDetails) -- restart it as the battery is still up -- the transmission is set to loop and has the name of the transmitting DCS group (that includes the type - i.e. FM, UHF, VHF) - trigger.action.radioTransmission(_sound, _radio.group:getUnit(1):getPoint(), _radio.mode, true, _radio.freq, 1000, _radio.group:getName()) + trigger.action.radioTransmission(_sound, _radio.group:getUnit(1):getPoint(), _radio.mode, true, _radio.freq, 1000, + _radio.group:getName()) end return true end function ctld.listRadioBeacons(_args) - local _heli = ctld.getTransportUnit(_args[1]) local _message = "" if _heli ~= nil then - for _x, _details in pairs(ctld.deployedRadioBeacons) do - if _details.coalition == _heli:getCoalition() then _message = _message .. _details.text .. "\n" end @@ -4241,12 +4447,10 @@ function ctld.listRadioBeacons(_args) end function ctld.dropRadioBeacon(_args) - local _heli = ctld.getTransportUnit(_args[1]) local _message = "" if _heli ~= nil and ctld.inAir(_heli) == false then - --deploy 50 m infront --try to spawn at 12 oclock to us local _point = ctld.getPointAt12Oclock(_heli, 50) @@ -4254,25 +4458,26 @@ function ctld.dropRadioBeacon(_args) ctld.beaconCount = ctld.beaconCount + 1 local _name = "Beacon #" .. ctld.beaconCount - local _radioBeaconDetails = ctld.createRadioBeacon(_point, _heli:getCoalition(), _heli:getCountry(), _name, nil, false) + local _radioBeaconDetails = ctld.createRadioBeacon(_point, _heli:getCoalition(), _heli:getCountry(), _name, nil, + false) -- mark with flare? - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 deployed a Radio Beacon.\n\n%2", ctld.getPlayerNameOrType(_heli), _radioBeaconDetails.text, 20)) - + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 deployed a Radio Beacon.\n\n%2", ctld.getPlayerNameOrType(_heli), + _radioBeaconDetails.text, 20)) else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You need to land before you can deploy a Radio Beacon!"), 20) + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You need to land before you can deploy a Radio Beacon!"), + 20) end end --remove closet radio beacon function ctld.removeRadioBeacon(_args) - local _heli = ctld.getTransportUnit(_args[1]) local _message = "" if _heli ~= nil and ctld.inAir(_heli) == false then - -- mark with flare? local _closestBeacon = nil @@ -4280,13 +4485,10 @@ function ctld.removeRadioBeacon(_args) local _distance = 0 for _x, _details in pairs(ctld.deployedRadioBeacons) do - if _details.coalition == _heli:getCoalition() then - local _group = Group.getByName(_details.vhfGroup) if _group ~= nil and #_group:getUnits() == 1 then - _distance = ctld.getDistance(_heli:getPoint(), _group:getUnit(1):getPoint()) if _distance ~= nil and (_shortestDistance == -1 or _distance < _shortestDistance) then _shortestDistance = _distance @@ -4316,11 +4518,12 @@ function ctld.removeRadioBeacon(_args) _fmGroup:destroy() end - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 removed a Radio Beacon.\n\n%2", ctld.getPlayerNameOrType(_heli), _closestBeacon.text, 20)) + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 removed a Radio Beacon.\n\n%2", ctld.getPlayerNameOrType(_heli), + _closestBeacon.text, 20)) else ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No Radio Beacons within 500m."), 20) end - else ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You need to land before remove a Radio Beacon"), 20) end @@ -4345,28 +4548,23 @@ function ctld.getCentroid(_points) end function ctld.getAATemplate(_unitName) - - for _,_system in pairs(ctld.AASystemTemplate) do - + for _, _system in pairs(ctld.AASystemTemplate) do if _system.repair == _unitName then return _system end - for _,_part in pairs(_system.parts) do - - if _unitName == _part.name then + for _, _part in pairs(_system.parts) do + if _unitName == _part.name then return _system end end end return nil - end function ctld.getLauncherUnitFromAATemplate(_aaTemplate) - for _,_part in pairs(_aaTemplate.parts) do - + for _, _part in pairs(_aaTemplate.parts) do if _part.launcher then return _part.name end @@ -4376,17 +4574,14 @@ function ctld.getLauncherUnitFromAATemplate(_aaTemplate) end function ctld.rearmAASystem(_heli, _nearestCrate, _nearbyCrates, _aaSystemTemplate) - -- are we adding to existing aa system? -- check to see if the crate is a launcher if ctld.getLauncherUnitFromAATemplate(_aaSystemTemplate) == _nearestCrate.details.unit then - -- find nearest COMPLETE AA system local _nearestSystem = ctld.findNearestAASystem(_heli, _aaSystemTemplate) if _nearestSystem ~= nil and _nearestSystem.dist < 300 then - - local _uniqueTypes = {} -- stores each unique part of system + local _uniqueTypes = {} -- stores each unique part of system local _types = {} local _points = {} local _hdgs = {} @@ -4394,10 +4589,8 @@ function ctld.rearmAASystem(_heli, _nearestCrate, _nearbyCrates, _aaSystemTempla local _units = _nearestSystem.group:getUnits() if _units ~= nil and #_units > 0 then - for x = 1, #_units do if _units[x]:getLife() > 0 then - --this allows us to count each type once _uniqueTypes[_units[x]:getTypeName()] = _units[x]:getTypeName() @@ -4410,7 +4603,6 @@ function ctld.rearmAASystem(_heli, _nearestCrate, _nearbyCrates, _aaSystemTempla -- do we have the correct number of unique pieces and do we have enough points for all the pieces if ctld.countTableEntries(_uniqueTypes) == _aaSystemTemplate.count and #_points >= _aaSystemTemplate.count then - -- rearm aa system -- destroy old group ctld.completeAASystems[_nearestSystem.group:getName()] = nil @@ -4419,11 +4611,15 @@ function ctld.rearmAASystem(_heli, _nearestCrate, _nearbyCrates, _aaSystemTempla local _spawnedGroup = ctld.spawnCrateGroup(_heli, _points, _types, _hdgs) - ctld.completeAASystems[_spawnedGroup:getName()] = ctld.getAASystemDetails(_spawnedGroup, _aaSystemTemplate) + ctld.completeAASystems[_spawnedGroup:getName()] = ctld.getAASystemDetails(_spawnedGroup, + _aaSystemTemplate) - ctld.processCallback({unit = _heli, crate = _nearestCrate , spawnedGroup = _spawnedGroup, action = "rearm"}) + ctld.processCallback({ unit = _heli, crate = _nearestCrate, spawnedGroup = _spawnedGroup, action = + "rearm" }) - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 successfully rearmed a full %2 in the field", ctld.getPlayerNameOrType(_heli), _aaSystemTemplate.name, 20)) + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 successfully rearmed a full %2 in the field", ctld.getPlayerNameOrType(_heli), + _aaSystemTemplate.name, 20)) if _heli:getCoalition() == 1 then ctld.spawnedCratesRED[_nearestCrate.crateUnit:getName()] = nil @@ -4432,11 +4628,11 @@ function ctld.rearmAASystem(_heli, _nearestCrate, _nearbyCrates, _aaSystemTempla end -- remove crate - -- if ctld.slingLoad == false then - _nearestCrate.crateUnit:destroy() - -- end + -- if ctld.slingLoad == false then + _nearestCrate.crateUnit:destroy() + -- end - return true -- all done so quit + return true -- all done so quit end end end @@ -4444,21 +4640,21 @@ function ctld.rearmAASystem(_heli, _nearestCrate, _nearbyCrates, _aaSystemTempla return false end -function ctld.getAASystemDetails(_hawkGroup,_aaSystemTemplate) - +function ctld.getAASystemDetails(_hawkGroup, _aaSystemTemplate) local _units = _hawkGroup:getUnits() local _hawkDetails = {} for _, _unit in pairs(_units) do - table.insert(_hawkDetails, { point = _unit:getPoint(), unit = _unit:getTypeName(), name = _unit:getName(), system =_aaSystemTemplate, hdg = mist.getHeading(_unit, true) }) + table.insert(_hawkDetails, + { point = _unit:getPoint(), unit = _unit:getTypeName(), name = _unit:getName(), system = _aaSystemTemplate, hdg = + mist.getHeading(_unit, true) }) end return _hawkDetails end function ctld.countTableEntries(_table) - if _table == nil then return 0 end @@ -4467,19 +4663,18 @@ function ctld.countTableEntries(_table) local _count = 0 for _key, _value in pairs(_table) do - _count = _count + 1 end return _count end -function ctld.unpackAASystem(_heli, _nearestCrate, _nearbyCrates,_aaSystemTemplate) +function ctld.unpackAASystem(_heli, _nearestCrate, _nearbyCrates, _aaSystemTemplate) ctld.logTrace("_nearestCrate = %s", ctld.p(_nearestCrate)) ctld.logTrace("_nearbyCrates = %s", ctld.p(_nearbyCrates)) ctld.logTrace("_aaSystemTemplate = %s", ctld.p(_aaSystemTemplate)) - if ctld.rearmAASystem(_heli, _nearestCrate, _nearbyCrates,_aaSystemTemplate) then + if ctld.rearmAASystem(_heli, _nearestCrate, _nearbyCrates, _aaSystemTemplate) then -- rearmed system return end @@ -4487,8 +4682,9 @@ function ctld.unpackAASystem(_heli, _nearestCrate, _nearbyCrates,_aaSystemTempla local _systemParts = {} --initialise list of parts - for _,_part in pairs(_aaSystemTemplate.parts) do - local _systemPart = {name = _part.name, desc = _part.desc, launcher = _part.launcher, amount = _part.amount, NoCrate = _part.NoCrate, found = 0, required = 1} + for _, _part in pairs(_aaSystemTemplate.parts) do + local _systemPart = { name = _part.name, desc = _part.desc, launcher = _part.launcher, amount = _part.amount, NoCrate = + _part.NoCrate, found = 0, required = 1 } -- if the part is a NoCrate required, it's found by default if _systemPart.NoCrate ~= nil then _systemPart.found = 1 @@ -4505,12 +4701,10 @@ function ctld.unpackAASystem(_heli, _nearestCrate, _nearbyCrates,_aaSystemTempla for _, _nearbyCrate in pairs(_nearbyCrates) do ctld.logTrace("_nearbyCrate = %s", ctld.p(_nearbyCrate)) if _nearbyCrate.dist < crateDistance then - local _name = _nearbyCrate.details.unit ctld.logTrace("_name = %s", ctld.p(_name)) if _systemParts[_name] ~= nil then - local foundCount = _systemParts[_name].found ctld.logTrace("foundCount = %s", ctld.p(foundCount)) @@ -4530,7 +4724,7 @@ function ctld.unpackAASystem(_heli, _nearestCrate, _nearbyCrates,_aaSystemTempla -- store the number of crates required to compute how many crates will have to be removed later and to see if the system can be deployed local cratesRequired = _nearbyCrate.details.cratesRequired ctld.logTrace("cratesRequired = %s", ctld.p(cratesRequired)) - if cratesRequired ~= nil then + if cratesRequired ~= nil then _foundPart.required = cratesRequired end @@ -4574,7 +4768,7 @@ function ctld.unpackAASystem(_heli, _nearestCrate, _nearbyCrates,_aaSystemTempla end end - local spawnDistance = 50 -- circle radius to spawn units in a circle and randomize position relative to the crate location + local spawnDistance = 50 -- circle radius to spawn units in a circle and randomize position relative to the crate location local arcRad = math.pi * 2 local _txt = "" @@ -4584,17 +4778,17 @@ function ctld.unpackAASystem(_heli, _nearestCrate, _nearbyCrates,_aaSystemTempla local _typeArray = {} -- for each part of the system parts for _name, _systemPart in pairs(_systemParts) do - -- check if enough crates were found to build the part if _systemPart.found < _systemPart.required then - _txt = _txt..ctld.i18n_translate("Missing %1\n",_systemPart.desc) + _txt = _txt .. ctld.i18n_translate("Missing %1\n", _systemPart.desc) else -- use the centroid of the crates for this part as a spawn location local _point = _crateCentroids[_name] -- in the case this centroid does not exist (NoCrate), use the centroid of all crates found and add some randomness if _point == nil then _point = _crateCentroid - _point = { x = _point.x + math.random(0,3)*spawnDistance, y = _point.y, z = _point.z + math.random(0,3)*spawnDistance} + _point = { x = _point.x + math.random(0, 3) * spawnDistance, y = _point.y, z = _point.z + + math.random(0, 3) * spawnDistance } end -- use the average heading to spawn the part at @@ -4616,7 +4810,7 @@ function ctld.unpackAASystem(_heli, _nearestCrate, _nearbyCrates,_aaSystemTempla end -- if crate stacking is allowed, then find the multiplication factor for the amount depending on how many crates are required and how many were found if ctld.AASystemCrateStacking then - _systemPart.amountFactor = _systemPart.found - _systemPart.found%_systemPart.required + _systemPart.amountFactor = _systemPart.found - _systemPart.found % _systemPart.required else _systemPart.amountFactor = 1 end @@ -4624,16 +4818,15 @@ function ctld.unpackAASystem(_heli, _nearestCrate, _nearbyCrates,_aaSystemTempla --handle multiple units per part by spawning them in a circle around the crate if partAmount > 1 then - local angular_step = arcRad / partAmount for _i = 1, partAmount do - local _angle = (angular_step * (_i - 1) + _hdg)%arcRad + local _angle = (angular_step * (_i - 1) + _hdg) % arcRad local _xOffset = math.cos(_angle) * spawnDistance local _yOffset = math.sin(_angle) * spawnDistance table.insert(_posArray, { x = _point.x + _xOffset, y = _point.y, z = _point.z + _yOffset }) - table.insert(_hdgArray, _angle) -- also spawn them perpendicular to that point of the circle + table.insert(_hdgArray, _angle) -- also spawn them perpendicular to that point of the circle table.insert(_typeArray, _name) end else @@ -4648,24 +4841,26 @@ function ctld.unpackAASystem(_heli, _nearestCrate, _nearbyCrates,_aaSystemTempla local _allowed = ctld.getAllowedAASystems(_heli) - env.info("Active: ".._activeLaunchers.." Allowed: ".._allowed) + env.info("Active: " .. _activeLaunchers .. " Allowed: " .. _allowed) if _activeLaunchers + 1 > _allowed then - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("Out of parts for AA Systems. Current limit is %1\n", _allowed, 10)) + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("Out of parts for AA Systems. Current limit is %1\n", _allowed, 10)) return end - if _txt ~= "" then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Cannot build %1\n%2\n\nOr the crates are not close enough together", _aaSystemTemplate.name, _txt), 20) + if _txt ~= "" then + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate("Cannot build %1\n%2\n\nOr the crates are not close enough together", + _aaSystemTemplate.name, _txt), 20) return else - -- destroy crates for _name, _systemPart in pairs(_systemParts) do -- if there is a crate to delete in the first place - if _systemPart.NoCrate ~= true then + if _systemPart.NoCrate ~= true then -- figure out how many crates to delete since we searched for as many as possible, not all of them might have been used - local amountToDel = _systemPart.amountFactor*_systemPart.required + local amountToDel = _systemPart.amountFactor * _systemPart.required local DelCounter = 0 -- for each crate found for this part @@ -4680,8 +4875,8 @@ function ctld.unpackAASystem(_heli, _nearestCrate, _nearbyCrates,_aaSystemTempla --destroy -- if ctld.slingLoad == false then - _crate.crateUnit:destroy() - DelCounter = DelCounter + 1 -- count up for one more crate has been deleted + _crate.crateUnit:destroy() + DelCounter = DelCounter + 1 -- count up for one more crate has been deleted --end else break @@ -4693,53 +4888,47 @@ function ctld.unpackAASystem(_heli, _nearestCrate, _nearbyCrates,_aaSystemTempla -- HAWK / BUK READY! local _spawnedGroup = ctld.spawnCrateGroup(_heli, _posArray, _typeArray, _hdgArray) - ctld.completeAASystems[_spawnedGroup:getName()] = ctld.getAASystemDetails(_spawnedGroup,_aaSystemTemplate) + ctld.completeAASystems[_spawnedGroup:getName()] = ctld.getAASystemDetails(_spawnedGroup, _aaSystemTemplate) - ctld.processCallback({unit = _heli, crate = _nearestCrate , spawnedGroup = _spawnedGroup, action = "unpack"}) + ctld.processCallback({ unit = _heli, crate = _nearestCrate, spawnedGroup = _spawnedGroup, action = "unpack" }) - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 successfully deployed a full %2 in the field. \n\nAA Active System limit is: %3\nActive: %4", ctld.getPlayerNameOrType(_heli), _aaSystemTemplate.name, _allowed, (_activeLaunchers+1)), 10) + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate( + "%1 successfully deployed a full %2 in the field. \n\nAA Active System limit is: %3\nActive: %4", + ctld.getPlayerNameOrType(_heli), _aaSystemTemplate.name, _allowed, (_activeLaunchers + 1)), 10) end end --count the number of captured cities, sets the amount of allowed AA Systems function ctld.getAllowedAASystems(_heli) - if _heli:getCoalition() == 1 then return ctld.AASystemLimitBLUE else return ctld.AASystemLimitRED end - - end - function ctld.countCompleteAASystems(_heli) - local _count = 0 for _groupName, _hawkDetails in pairs(ctld.completeAASystems) do - local _hawkGroup = Group.getByName(_groupName) - -- env.info(_groupName..": "..mist.utils.tableShow(_hawkDetails)) + -- env.info(_groupName..": "..mist.utils.tableShow(_hawkDetails)) if _hawkGroup ~= nil and _hawkGroup:getCoalition() == _heli:getCoalition() then - local _units = _hawkGroup:getUnits() - if _units ~=nil and #_units > 0 then + if _units ~= nil and #_units > 0 then --get the system template local _aaSystemTemplate = _hawkDetails[1].system - local _uniqueTypes = {} -- stores each unique part of system + local _uniqueTypes = {} -- stores each unique part of system local _types = {} local _points = {} if _units ~= nil and #_units > 0 then - for x = 1, #_units do if _units[x]:getLife() > 0 then - --this allows us to count each type once _uniqueTypes[_units[x]:getTypeName()] = _units[x]:getTypeName() @@ -4751,7 +4940,7 @@ function ctld.countCompleteAASystems(_heli) -- do we have the correct number of unique pieces and do we have enough points for all the pieces if ctld.countTableEntries(_uniqueTypes) == _aaSystemTemplate.count and #_points >= _aaSystemTemplate.count then - _count = _count +1 + _count = _count + 1 end end end @@ -4760,16 +4949,13 @@ function ctld.countCompleteAASystems(_heli) return _count end - -function ctld.repairAASystem(_heli, _nearestCrate,_aaSystem) - +function ctld.repairAASystem(_heli, _nearestCrate, _aaSystem) -- find nearest COMPLETE AA system - local _nearestHawk = ctld.findNearestAASystem(_heli,_aaSystem) + local _nearestHawk = ctld.findNearestAASystem(_heli, _aaSystem) if _nearestHawk ~= nil and _nearestHawk.dist < 300 then - local _oldHawk = ctld.completeAASystems[_nearestHawk.group:getName()] --spawn new one @@ -4790,11 +4976,13 @@ function ctld.repairAASystem(_heli, _nearestCrate,_aaSystem) local _spawnedGroup = ctld.spawnCrateGroup(_heli, _points, _types, _hdgs) - ctld.completeAASystems[_spawnedGroup:getName()] = ctld.getAASystemDetails(_spawnedGroup,_aaSystem) + ctld.completeAASystems[_spawnedGroup:getName()] = ctld.getAASystemDetails(_spawnedGroup, _aaSystem) - ctld.processCallback({unit = _heli, crate = _nearestCrate , spawnedGroup = _spawnedGroup, action = "repair"}) + ctld.processCallback({ unit = _heli, crate = _nearestCrate, spawnedGroup = _spawnedGroup, action = "repair" }) - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 successfully repaired a full %2 in the field.", ctld.getPlayerNameOrType(_heli), _aaSystem.name), 10) + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 successfully repaired a full %2 in the field.", ctld.getPlayerNameOrType(_heli), + _aaSystem.name), 10) if _heli:getCoalition() == 1 then ctld.spawnedCratesRED[_nearestCrate.crateUnit:getName()] = nil @@ -4803,28 +4991,24 @@ function ctld.repairAASystem(_heli, _nearestCrate,_aaSystem) end -- remove crate - -- if ctld.slingLoad == false then - _nearestCrate.crateUnit:destroy() - -- end - + -- if ctld.slingLoad == false then + _nearestCrate.crateUnit:destroy() + -- end else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Cannot repair %1. No damaged %2 within 300m", _aaSystem.name, _aaSystem.name), 10) + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate("Cannot repair %1. No damaged %2 within 300m", _aaSystem.name, _aaSystem.name), 10) end end function ctld.unpackMultiCrate(_heli, _nearestCrate, _nearbyCrates) - + --ctld.logTrace("FG_ ctld.unpackMultiCrate, _nearestCrate = %s", ctld.p(_nearestCrate)) -- unpack multi crate local _nearbyMultiCrates = {} for _, _nearbyCrate in pairs(_nearbyCrates) do - if _nearbyCrate.dist < 300 then - if _nearbyCrate.details.unit == _nearestCrate.details.unit then - table.insert(_nearbyMultiCrates, _nearbyCrate) - if #_nearbyMultiCrates == _nearestCrate.details.cratesRequired then break end @@ -4834,13 +5018,19 @@ function ctld.unpackMultiCrate(_heli, _nearestCrate, _nearbyCrates) --- check crate count if #_nearbyMultiCrates == _nearestCrate.details.cratesRequired then - - local _point = _nearestCrate.crateUnit:getPoint() + --local _point = _nearestCrate.crateUnit:getPoint() + --local _point = _heli:getPoint() + --local secureDistanceFromUnit = ctld.getSecureDistanceFromUnit(_heli:getName()) + --_point.x = _point.x + secureDistanceFromUnit + local _point = ctld.getPointInFrontSector(_heli, ctld.getSecureDistanceFromUnit(_heli:getName())) + if ctld.unitDynamicCargoCapable(_heli) == true then + _point = ctld.getPointInRearSector(_heli, ctld.getSecureDistanceFromUnit(_heli:getName())) + end + local _crateHdg = mist.getHeading(_nearestCrate.crateUnit, true) -- destroy crates for _, _crate in pairs(_nearbyMultiCrates) do - if _point == nil then _point = _crate.crateUnit:getPoint() end @@ -4852,9 +5042,9 @@ function ctld.unpackMultiCrate(_heli, _nearestCrate, _nearbyCrates) end --destroy - -- if ctld.slingLoad == false then - _crate.crateUnit:destroy() - -- end + -- if ctld.slingLoad == false then + _crate.crateUnit:destroy() + -- end end @@ -4862,213 +5052,217 @@ function ctld.unpackMultiCrate(_heli, _nearestCrate, _nearbyCrates) if _spawnedGroup == nil then ctld.logError("ctld.unpackMultiCrate group was not spawned - skipping setGrpROE") else + timer.scheduleFunction(ctld.autoUpdateRepackMenu, { reschedule = false }, timer.getTime() + 1) -- for add unpacked unit in repack menu ctld.setGrpROE(_spawnedGroup) - ctld.processCallback({unit = _heli, crate = _nearestCrate , spawnedGroup = _spawnedGroup, action = "unpack"}) - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 successfully deployed %2 to the field using %3 crates.", ctld.getPlayerNameOrType(_heli), _nearestCrate.details.desc, #_nearbyMultiCrates), 10) + ctld.processCallback({ unit = _heli, crate = _nearestCrate, spawnedGroup = _spawnedGroup, action = "unpack" }) + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 successfully deployed %2 to the field using %3 crates.", + ctld.getPlayerNameOrType(_heli), _nearestCrate.details.desc, #_nearbyMultiCrates), 10) end else - - local _txt = ctld.i18n_translate("Cannot build %1!\n\nIt requires %2 crates and there are %3 \n\nOr the crates are not within 300m of each other", _nearestCrate.details.desc, _nearestCrate.details.cratesRequired, #_nearbyMultiCrates) + local _txt = ctld.i18n_translate( + "Cannot build %1!\n\nIt requires %2 crates and there are %3 \n\nOr the crates are not within 300m of each other", + _nearestCrate.details.desc, _nearestCrate.details.cratesRequired, #_nearbyMultiCrates) ctld.displayMessageToGroup(_heli, _txt, 20) end end - function ctld.spawnCrateGroup(_heli, _positions, _types, _hdgs) + -- ctld.logTrace("_heli = %s", ctld.p(_heli)) + -- ctld.logTrace("_positions = %s", ctld.p(_positions)) + -- ctld.logTrace("_types = %s", ctld.p(_types)) + -- ctld.logTrace("_hdgs = %s", ctld.p(_hdgs)) + local _id = ctld.getNextGroupId() - local _groupName = _types[1] .. " #" .. _id + local _groupName = _types[1] .. " #" .. _id local _side = _heli:getCoalition() local _group = { - ["visible"] = false, - -- ["groupId"] = _id, - ["hidden"] = false, - ["units"] = {}, - -- ["y"] = _positions[1].z, - -- ["x"] = _positions[1].x, - ["name"] = _groupName, - ["tasks"] = {}, + ["visible"] = false, + -- ["groupId"] = _id, + ["hidden"] = false, + ["units"] = {}, + -- ["y"] = _positions[1].z, + -- ["x"] = _positions[1].x, + ["name"] = _groupName, + ["tasks"] = {}, ["radioSet"] = false, - ["task"] = "Reconnaissance", - ["route"] = {}, + ["task"] = "Reconnaissance", + ["route"] = {}, } - local _hdg = 120 * math.pi / 180 -- radians = 120 degrees - if _types[1] ~= "MQ-9 Reaper" and _types[1] ~= "RQ-1A Predator" then -- non-drones - JTAC + local _hdg = 120 * math.pi / 180 -- radians = 120 degrees + if _types[1] ~= "MQ-9 Reaper" and _types[1] ~= "RQ-1A Predator" then -- non-drones - JTAC local _spreadMin = 5 local _spreadMax = 5 local _spreadMult = 1 for _i, _pos in ipairs(_positions) do local _unitId = ctld.getNextUnitId() local _details = { type = _types[_i], unitId = _unitId, name = string.format("Unpacked %s #%i", _types[_i], _unitId) } - + --ctld.logTrace("Group._details = %s", ctld.p(_details)) if _hdgs and _hdgs[_i] then _hdg = _hdgs[_i] end - - _group.units[_i] = ctld.createUnit( _pos.x +math.random(_spreadMin,_spreadMax)*_spreadMult, - _pos.z +math.random(_spreadMin,_spreadMax)*_spreadMult, - _hdg, - _details) + + _group.units[_i] = ctld.createUnit(_pos.x + math.random(_spreadMin, _spreadMax) * _spreadMult, + _pos.z + math.random(_spreadMin, _spreadMax) * _spreadMult, + _hdg, + _details) end _group.category = Group.Category.GROUND - else -- drones - JTAC + else -- drones - JTAC local _unitId = ctld.getNextUnitId() - local _details = { type = _types[1], - unitId = _unitId, - name = string.format("Unpacked %s #%i", _types[1], _unitId), - livery_id = "'camo' scheme", - skill = "High", - speed = 80, - payload = { pylons = {}, fuel = 1300, flare = 0, chaff = 0, gun = 100 } } - - _group.units[1] = ctld.createUnit( _positions[1].x, - _positions[1].z + ctld.jtacDroneRadius, - _hdg, - _details) - - _group.category = Group.Category.AIRPLANE -- for drones + local _details = { + type = _types[1], + unitId = _unitId, + name = string.format("Unpacked %s #%i", _types[1], _unitId), + livery_id = "'camo' scheme", + skill = "High", + speed = 80, + payload = { pylons = {}, fuel = 1300, flare = 0, chaff = 0, gun = 100 } + } - -- create drone orbiting route - local DroneRoute = { - ["points"] = - { - [1] = - { - ["alt"] = 2000, - ["action"] = "Turning Point", - ["alt_type"] = "BARO", - ["properties"] = - { - ["addopt"] = {}, - }, -- end of ["properties"] - ["speed"] = 80, - ["task"] = - { - ["id"] = "ComboTask", - ["params"] = - { - ["tasks"] = - { - [1] = - { - ["enabled"] = true, - ["auto"] = false, - ["id"] = "WrappedAction", - ["number"] = 1, - ["params"] = - { - ["action"] = - { - ["id"] = "EPLRS", - ["params"] = - { - ["value"] = true, - ["groupId"] = 0, - }, -- end of ["params"] - }, -- end of ["action"] - }, -- end of ["params"] - }, -- end of [1] - [2] = - { - ["number"] = 2, - ["auto"] = false, - ["id"] = "Orbit", - ["enabled"] = true, - ["params"] = - { - ["altitude"] = ctld.jtacDroneAltitude, - ["pattern"] = "Circle", - ["speed"] = 80, - }, -- end of ["params"] - }, -- end of [2] - [3] = - { - ["enabled"] = true, - ["auto"] = false, - ["id"] = "WrappedAction", - ["number"] = 3, - ["params"] = - { - ["action"] = - { - ["id"] = "Option", - ["params"] = - { - ["value"] = true, - ["name"] = 6, - }, -- end of ["params"] - }, -- end of ["action"] - }, -- end of ["params"] - }, -- end of [3] - }, -- end of ["tasks"] - }, -- end of ["params"] - }, -- end of ["task"] - ["type"] = "Turning Point", - ["ETA"] = 0, - ["ETA_locked"] = true, - ["y"] = _positions[1].z, - ["x"] = _positions[1].x, - ["speed_locked"] = true, - ["formation_template"] = "", - }, -- end of [1] - }, -- end of ["points"] - } -- end of ["route"] - --------------------------------------------------------------------------------- + _group.units[1] = ctld.createUnit(_positions[1].x, + _positions[1].z + ctld.jtacDroneRadius, + _hdg, + _details) + + _group.category = Group.Category.AIRPLANE -- for drones + + -- create drone orbiting route + local DroneRoute = { + ["points"] = + { + [1] = + { + ["alt"] = 2000, + ["action"] = "Turning Point", + ["alt_type"] = "BARO", + ["properties"] = + { + ["addopt"] = {}, + }, -- end of ["properties"] + ["speed"] = 80, + ["task"] = + { + ["id"] = "ComboTask", + ["params"] = + { + ["tasks"] = + { + [1] = + { + ["enabled"] = true, + ["auto"] = false, + ["id"] = "WrappedAction", + ["number"] = 1, + ["params"] = + { + ["action"] = + { + ["id"] = "EPLRS", + ["params"] = + { + ["value"] = true, + ["groupId"] = 0, + }, -- end of ["params"] + }, -- end of ["action"] + }, -- end of ["params"] + }, -- end of [1] + [2] = + { + ["number"] = 2, + ["auto"] = false, + ["id"] = "Orbit", + ["enabled"] = true, + ["params"] = + { + ["altitude"] = ctld.jtacDroneAltitude, + ["pattern"] = "Circle", + ["speed"] = 80, + }, -- end of ["params"] + }, -- end of [2] + [3] = + { + ["enabled"] = true, + ["auto"] = false, + ["id"] = "WrappedAction", + ["number"] = 3, + ["params"] = + { + ["action"] = + { + ["id"] = "Option", + ["params"] = + { + ["value"] = true, + ["name"] = 6, + }, -- end of ["params"] + }, -- end of ["action"] + }, -- end of ["params"] + }, -- end of [3] + }, -- end of ["tasks"] + }, -- end of ["params"] + }, -- end of ["task"] + ["type"] = "Turning Point", + ["ETA"] = 0, + ["ETA_locked"] = true, + ["y"] = _positions[1].z, + ["x"] = _positions[1].x, + ["speed_locked"] = true, + ["formation_template"] = "", + }, -- end of [1] + }, -- end of ["points"] + } -- end of ["route"] + --------------------------------------------------------------------------------- _group.route = DroneRoute end - + _group.country = _heli:getCountry() - local _spawnedGroup = Group.getByName(mist.dynAdd(_group).name) + local _spawnedGroup = Group.getByName(mist.dynAdd(_group).name) return _spawnedGroup end - - -- spawn normal group function ctld.spawnDroppedGroup(_point, _details, _spawnBehind, _maxSearch) - local _groupName = _details.groupName local _group = { ["visible"] = false, - -- ["groupId"] = _details.groupId, + -- ["groupId"] = _details.groupId, ["hidden"] = false, ["units"] = {}, - -- ["y"] = _positions[1].z, - -- ["x"] = _positions[1].x, + -- ["y"] = _positions[1].z, + -- ["x"] = _positions[1].x, ["name"] = _groupName, ["task"] = {}, } if _spawnBehind == false then - -- spawn in circle around heli local _pos = _point for _i, _detail in ipairs(_details.units) do - local _angle = math.pi * 2 * (_i - 1) / #_details.units local _xOffset = math.cos(_angle) * 30 local _yOffset = math.sin(_angle) * 30 _group.units[_i] = ctld.createUnit(_pos.x + _xOffset, _pos.z + _yOffset, _angle, _detail) end - else - local _pos = _point --try to spawn at 6 oclock to us - local _angle = math.atan2(_pos.z, _pos.x) + local _angle = math.atan(_pos.z, _pos.x) local _xOffset = math.cos(_angle) * -30 local _yOffset = math.sin(_angle) * -30 for _i, _detail in ipairs(_details.units) do - _group.units[_i] = ctld.createUnit(_pos.x + (_xOffset + 10 * _i), _pos.z + (_yOffset + 10 * _i), _angle, _detail) + _group.units[_i] = ctld.createUnit(_pos.x + (_xOffset + 10 * _i), _pos.z + (_yOffset + 10 * _i), _angle, + _detail) end end @@ -5086,11 +5280,11 @@ function ctld.spawnDroppedGroup(_point, _details, _spawnBehind, _maxSearch) _maxSearch = ctld.maximumSearchDistance end - local _wpZone = ctld.inWaypointZone(_point,_spawnedGroup:getCoalition()) + local _wpZone = ctld.inWaypointZone(_point, _spawnedGroup:getCoalition()) if _wpZone.inZone then ctld.orderGroupToMoveToPoint(_spawnedGroup:getUnit(1), _wpZone.point) - env.info("Heading to waypoint - In Zone ".._wpZone.name) + env.info("Heading to waypoint - In Zone " .. _wpZone.name) else local _enemyPos = ctld.findNearestEnemy(_details.side, _point, _maxSearch) @@ -5101,7 +5295,6 @@ function ctld.spawnDroppedGroup(_point, _details, _spawnBehind, _maxSearch) end function ctld.findNearestEnemy(_side, _point, _searchDistance) - local _closestEnemy = nil local _groups @@ -5117,12 +5310,10 @@ function ctld.findNearestEnemy(_side, _point, _searchDistance) end for _, _group in pairs(_groups) do - if _group ~= nil then local _units = _group:getUnits() if _units ~= nil and #_units > 0 then - local _leader = nil -- find alive leader @@ -5148,21 +5339,18 @@ function ctld.findNearestEnemy(_side, _point, _searchDistance) -- no enemy - move to random point if _closestEnemy ~= nil then - -- env.info("found enemy") return _closestEnemy else - local _x = _heliPoint.x + math.random(0, ctld.maximumMoveDistance) - math.random(0, ctld.maximumMoveDistance) local _z = _heliPoint.z + math.random(0, ctld.maximumMoveDistance) - math.random(0, ctld.maximumMoveDistance) local _y = _heliPoint.y + math.random(0, ctld.maximumMoveDistance) - math.random(0, ctld.maximumMoveDistance) - return { x = _x, z = _z,y=_y } + return { x = _x, z = _z, y = _y } end end function ctld.findNearestGroup(_heli, _groups) - local _closestGroupDetails = {} local _closestGroup = nil @@ -5171,29 +5359,28 @@ function ctld.findNearestGroup(_heli, _groups) local _heliPoint = _heli:getPoint() for _, _groupName in pairs(_groups) do - local _group = Group.getByName(_groupName) if _group ~= nil then local _units = _group:getUnits() if _units ~= nil and #_units > 0 then - local _leader = nil - local _groupDetails = { groupId = _group:getID(), groupName = _group:getName(), side = _group:getCoalition(), units = {} } + local _groupDetails = { groupId = _group:getID(), groupName = _group:getName(), side = _group + :getCoalition(), units = {} } -- find alive leader for x = 1, #_units do if _units[x]:getLife() > 0 then - if _leader == nil then _leader = _units[x] -- set country based on leader _groupDetails.country = _leader:getCountry() end - local _unitDetails = { type = _units[x]:getTypeName(), unitId = _units[x]:getID(), name = _units[x]:getName() } + local _unitDetails = { type = _units[x]:getTypeName(), unitId = _units[x]:getID(), name = _units + [x]:getName() } table.insert(_groupDetails.units, _unitDetails) end @@ -5214,22 +5401,18 @@ function ctld.findNearestGroup(_heli, _groups) if _closestGroup ~= nil then - return { group = _closestGroup, details = _closestGroupDetails } else - return nil end end - function ctld.createUnit(_x, _y, _angle, _details) - local _newUnit = { ["y"] = _y, ["type"] = _details.type, ["name"] = _details.name, - -- ["unitId"] = _details.unitId, + -- ["unitId"] = _details.unitId, ["heading"] = _angle, ["playerCanDrive"] = true, ["skill"] = "Excellent", @@ -5240,7 +5423,6 @@ function ctld.createUnit(_x, _y, _angle, _details) end function ctld.addEWRTask(_group) - -- delayed 2 second to work around bug timer.scheduleFunction(function(_ewrGroup) local _grp = ctld.getAliveGroup(_ewrGroup) @@ -5256,12 +5438,10 @@ function ctld.addEWRTask(_group) _controller:setTask(_EWR) end end - , _group:getName(), timer.getTime() + 2) - + , _group:getName(), timer.getTime() + 2) end function ctld.orderGroupToMoveToPoint(_leader, _destination) - local _group = _leader:getGroup() local _path = {} @@ -5272,7 +5452,7 @@ function ctld.orderGroupToMoveToPoint(_leader, _destination) id = 'Mission', params = { route = { - points =_path + points = _path }, }, } @@ -5289,13 +5469,11 @@ function ctld.orderGroupToMoveToPoint(_leader, _destination) _controller:setTask(_arg[2]) end end - , {_group:getName(), _mission}, timer.getTime() + 2) - + , { _group:getName(), _mission }, timer.getTime() + 2) end -- are we in pickup zone function ctld.inPickupZone(_heli) - if ctld.inAir(_heli) then return { inZone = false, limit = -1, index = -1 } end @@ -5303,7 +5481,6 @@ function ctld.inPickupZone(_heli) local _heliPoint = _heli:getPoint() for _i, _zoneDetails in pairs(ctld.pickupZones) do - local _triggerZone = trigger.misc.getZone(_zoneDetails[1]) if _triggerZone == nil then @@ -5313,13 +5490,11 @@ function ctld.inPickupZone(_heli) local _point = _ship:getPoint() _triggerZone = {} _triggerZone.point = _point - _triggerZone.radius = 200 -- should be big enough for ship + _triggerZone.radius = 200 -- should be big enough for ship end - end if _triggerZone ~= nil then - --get distance to center local _dist = ctld.getDistance(_heliPoint, _triggerZone.point) @@ -5336,7 +5511,6 @@ function ctld.inPickupZone(_heli) -- now check spawned fobs for _, _fob in ipairs(_fobs) do - --get distance to center local _dist = ctld.getDistance(_heliPoint, _fob:getPoint()) @@ -5352,15 +5526,12 @@ function ctld.inPickupZone(_heli) end function ctld.getSpawnedFobs(_heli) - local _fobs = {} for _, _fobName in ipairs(ctld.builtFOBS) do - local _fob = StaticObject.getByName(_fobName) if _fob ~= nil and _fob:isExist() and _fob:getCoalition() == _heli:getCoalition() and _fob:getLife() > 0 then - table.insert(_fobs, _fob) end end @@ -5370,7 +5541,6 @@ end -- are we in a dropoff zone function ctld.inDropoffZone(_heli) - if ctld.inAir(_heli) then return false end @@ -5378,11 +5548,9 @@ function ctld.inDropoffZone(_heli) local _heliPoint = _heli:getPoint() for _, _zoneDetails in pairs(ctld.dropOffZones) do - local _triggerZone = trigger.misc.getZone(_zoneDetails[1]) - if _triggerZone ~= nil and (_zoneDetails[3] == _heli:getCoalition() or _zoneDetails[3]== 0) then - + if _triggerZone ~= nil and (_zoneDetails[3] == _heli:getCoalition() or _zoneDetails[3] == 0) then --get distance to center local _dist = ctld.getDistance(_heliPoint, _triggerZone.point) @@ -5397,31 +5565,28 @@ function ctld.inDropoffZone(_heli) end -- are we in a waypoint zone -function ctld.inWaypointZone(_point,_coalition) - +function ctld.inWaypointZone(_point, _coalition) for _, _zoneDetails in pairs(ctld.wpZones) do - local _triggerZone = trigger.misc.getZone(_zoneDetails[1]) --right coalition and active? - if _triggerZone ~= nil and (_zoneDetails[4] == _coalition or _zoneDetails[4]== 0) and _zoneDetails[3] == 1 then - + if _triggerZone ~= nil and (_zoneDetails[4] == _coalition or _zoneDetails[4] == 0) and _zoneDetails[3] == 1 then --get distance to center local _dist = ctld.getDistance(_point, _triggerZone.point) if _dist <= _triggerZone.radius then - return {inZone = true, point = _triggerZone.point, name = _zoneDetails[1]} + return { inZone = true, point = _triggerZone.point, name = _zoneDetails[1] } end end end - return {inZone = false} + return { inZone = false } end -- are we near friendly logistics zone function ctld.inLogisticsZone(_heli) - ctld.logDebug("ctld.inLogisticsZone(), _heli = %s", ctld.p(_heli)) + --ctld.logDebug("ctld.inLogisticsZone(), _heli = %s", ctld.p(_heli)) if ctld.inAir(_heli) then return false @@ -5438,7 +5603,6 @@ function ctld.inLogisticsZone(_heli) if _logistic ~= nil and _logistic:getCoalition() == _heli:getCoalition() and _logistic:getLife() > 0 then --get distance local _dist = ctld.getDistance(_heliPoint, _logistic:getPoint()) - ctld.logDebug("_dist = %s", ctld.p(_dist)) if _dist <= ctld.maximumDistanceLogistic then return true end @@ -5448,10 +5612,8 @@ function ctld.inLogisticsZone(_heli) return false end - -- are far enough from a friendly logistics zone function ctld.farEnoughFromLogisticZone(_heli) - if ctld.inAir(_heli) then return false end @@ -5461,11 +5623,9 @@ function ctld.farEnoughFromLogisticZone(_heli) local _farEnough = true for _, _name in pairs(ctld.logisticUnits) do - local _logistic = StaticObject.getByName(_name) if _logistic ~= nil and _logistic:getCoalition() == _heli:getCoalition() then - --get distance local _dist = ctld.getDistance(_heliPoint, _logistic:getPoint()) -- env.info("DIST ".._dist) @@ -5480,15 +5640,12 @@ function ctld.farEnoughFromLogisticZone(_heli) end function ctld.refreshSmoke() - if ctld.disableAllSmoke == true then return end for _, _zoneGroup in pairs({ ctld.pickupZones, ctld.dropOffZones }) do - for _, _zoneDetails in pairs(_zoneGroup) do - local _triggerZone = trigger.misc.getZone(_zoneDetails[1]) if _triggerZone == nil then @@ -5499,13 +5656,11 @@ function ctld.refreshSmoke() _triggerZone = {} _triggerZone.point = _point end - end --only trigger if smoke is on AND zone is active if _triggerZone ~= nil and _zoneDetails[2] >= 0 and _zoneDetails[4] == 1 then - -- Trigger smoke markers local _pos2 = { x = _triggerZone.point.x, y = _triggerZone.point.z } @@ -5519,12 +5674,10 @@ function ctld.refreshSmoke() --waypoint zones for _, _zoneDetails in pairs(ctld.wpZones) do - local _triggerZone = trigger.misc.getZone(_zoneDetails[1]) --only trigger if smoke is on AND zone is active if _triggerZone ~= nil and _zoneDetails[2] >= 0 and _zoneDetails[3] == 1 then - -- Trigger smoke markers local _pos2 = { x = _triggerZone.point.x, y = _triggerZone.point.z } @@ -5541,24 +5694,18 @@ function ctld.refreshSmoke() end function ctld.dropSmoke(_args) - local _heli = ctld.getTransportUnit(_args[1]) if _heli ~= nil then - local _colour = "" if _args[2] == trigger.smokeColor.Red then - _colour = "RED" elseif _args[2] == trigger.smokeColor.Blue then - _colour = "BLUE" elseif _args[2] == trigger.smokeColor.Green then - _colour = "GREEN" elseif _args[2] == trigger.smokeColor.Orange then - _colour = "ORANGE" end @@ -5570,12 +5717,12 @@ function ctld.dropSmoke(_args) trigger.action.smoke(_pos3, _args[2]) - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 dropped %2 smoke.", ctld.getPlayerNameOrType(_heli), _colour), 10) + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 dropped %2 smoke.", ctld.getPlayerNameOrType(_heli), _colour), 10) end end function ctld.unitCanCarryVehicles(_unit) - local _type = string.lower(_unit:getTypeName()) for _, _name in ipairs(ctld.vehicleTransportEnabled) do @@ -5594,16 +5741,15 @@ function ctld.unitDynamicCargoCapable(_unit) local result = cache[_type] if result == nil then result = false - ctld.logDebug("ctld.unitDynamicCargoCapable(_type=[%s])", ctld.p(_type)) + --ctld.logDebug("ctld.unitDynamicCargoCapable(_type=[%s])", ctld.p(_type)) for _, _name in ipairs(ctld.dynamicCargoUnits) do local _nameLower = string.lower(_name) - if string.find(_type, _nameLower, 1, true) then --string.match does not work with patterns containing '-' as it is a magic character + if string.find(_type, _nameLower, 1, true) then --string.match does not work with patterns containing '-' as it is a magic character result = true break end end cache[_type] = result - ctld.logDebug("result=[%s]", ctld.p(result)) end return result end @@ -5622,9 +5768,7 @@ function ctld.isJTACUnitType(_type) end function ctld.updateZoneCounter(_index, _diff) - if ctld.pickupZones[_index] ~= nil then - ctld.pickupZones[_index][3] = ctld.pickupZones[_index][3] + _diff if ctld.pickupZones[_index][3] < 0 then @@ -5634,18 +5778,14 @@ function ctld.updateZoneCounter(_index, _diff) if ctld.pickupZones[_index][6] ~= nil then trigger.action.setUserFlag(ctld.pickupZones[_index][6], ctld.pickupZones[_index][3]) end - -- env.info(ctld.pickupZones[_index][1].." = " ..ctld.pickupZones[_index][3]) + -- env.info(ctld.pickupZones[_index][1].." = " ..ctld.pickupZones[_index][3]) end end function ctld.processCallback(_callbackArgs) - for _, _callback in pairs(ctld.callbacks) do - local _status, _result = pcall(function() - _callback(_callbackArgs) - end) if (not _status) then @@ -5654,54 +5794,46 @@ function ctld.processCallback(_callbackArgs) end end - -- checks the status of all AI troop carriers and auto loads and unloads troops -- as long as the troops are on the ground function ctld.checkAIStatus() - timer.scheduleFunction(ctld.checkAIStatus, nil, timer.getTime() + 2) for _, _unitName in pairs(ctld.transportPilotNames) do local status, error = pcall(function() - local _unit = ctld.getTransportUnit(_unitName) -- no player name means AI! if _unit ~= nil and _unit:getPlayerName() == nil then local _zone = ctld.inPickupZone(_unit) - -- env.error("Checking.. ".._unit:getName()) + -- env.error("Checking.. ".._unit:getName()) if _zone.inZone == true and not ctld.troopsOnboard(_unit, true) then - -- env.error("in zone, loading.. ".._unit:getName()) + -- env.error("in zone, loading.. ".._unit:getName()) if ctld.allowRandomAiTeamPickups == true then -- Random troop pickup implementation local _team = nil if _unit:getCoalition() == 1 then _team = math.floor((math.random(#ctld.redTeams * 100) / 100) + 1) - ctld.loadTroopsFromZone({ _unitName, true,ctld.loadableGroups[ctld.redTeams[_team]],true }) + ctld.loadTroopsFromZone({ _unitName, true, ctld.loadableGroups[ctld.redTeams[_team]], true }) else _team = math.floor((math.random(#ctld.blueTeams * 100) / 100) + 1) - ctld.loadTroopsFromZone({ _unitName, true,ctld.loadableGroups[ctld.blueTeams[_team]],true }) + ctld.loadTroopsFromZone({ _unitName, true, ctld.loadableGroups[ctld.blueTeams[_team]], true }) end else - ctld.loadTroopsFromZone({ _unitName, true,"",true }) + ctld.loadTroopsFromZone({ _unitName, true, "", true }) end - elseif ctld.inDropoffZone(_unit) and ctld.troopsOnboard(_unit, true) then - -- env.error("in dropoff zone, unloading.. ".._unit:getName()) - ctld.unloadTroops( { _unitName, true }) + -- env.error("in dropoff zone, unloading.. ".._unit:getName()) + ctld.unloadTroops({ _unitName, true }) end if ctld.unitCanCarryVehicles(_unit) then - if _zone.inZone == true and not ctld.troopsOnboard(_unit, false) then - - ctld.loadTroopsFromZone({ _unitName, false,"",true }) - + ctld.loadTroopsFromZone({ _unitName, false, "", true }) elseif ctld.inDropoffZone(_unit) and ctld.troopsOnboard(_unit, false) then - - ctld.unloadTroops( { _unitName, false }) + ctld.unloadTroops({ _unitName, false }) end end end @@ -5711,236 +5843,374 @@ function ctld.checkAIStatus() env.error(string.format("Error with ai status: %s", error), false) end end - - end function ctld.getTransportLimit(_unitType) - if ctld.unitLoadLimits[_unitType] then - return ctld.unitLoadLimits[_unitType] end return ctld.numberOfTroops - end function ctld.getUnitActions(_unitType) - if ctld.unitActions[_unitType] then return ctld.unitActions[_unitType] end - return {crates=true,troops=true} - + return { crates = true, troops = true } end -- Adds menuitem to a human unit function ctld.addTransportF10MenuOptions(_unitName) ctld.logDebug("ctld.addTransportF10MenuOptions(_unitName=[%s])", ctld.p(_unitName)) - local status, error = pcall(function() - local _unit = ctld.getTransportUnit(_unitName) ctld.logTrace("_unit = %s", ctld.p(_unit)) + if _unit then local _unitTypename = _unit:getTypeName() - local _groupId = ctld.getGroupId(_unit) + local _groupId = ctld.getGroupId(_unit) + if _groupId then + -- ctld.logTrace("_groupId = %s", ctld.p(_groupId)) + -- ctld.logTrace("ctld.addedTo = %s", ctld.p(ctld.addedTo[tostring(_groupId)])) + if ctld.addedTo[tostring(_groupId)] == nil then + ctld.logTrace("adding CTLD menu for _groupId = %s", ctld.p(_groupId)) + local _rootPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("CTLD")) + local _unitActions = ctld.getUnitActions(_unitTypename) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Check Cargo"), _rootPath, ctld.checkTroopStatus, { _unitName }) + if _unitActions.troops then + local _troopCommandsPath = missionCommands.addSubMenuForGroup(_groupId,ctld.i18n_translate("Troop Transport"), _rootPath) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Unload / Extract Troops"), + _troopCommandsPath, ctld.unloadExtractTroops, { _unitName }) - if _groupId then - ctld.logTrace("_groupId = %s", ctld.p(_groupId)) - ctld.logTrace("ctld.addedTo = %s", ctld.p(ctld.addedTo)) - if ctld.addedTo[tostring(_groupId)] == nil then - ctld.logTrace("adding CTLD menu for _groupId = %s", ctld.p(_groupId)) - local _rootPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("CTLD")) - - local _unitActions = ctld.getUnitActions(_unitTypename) - - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Check Cargo"), _rootPath, ctld.checkTroopStatus, { _unitName }) - - if _unitActions.troops then - - local _troopCommandsPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Troop Transport"), _rootPath) - - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Unload / Extract Troops"), _troopCommandsPath, ctld.unloadExtractTroops, { _unitName }) - - - -- local _loadPath = missionCommands.addSubMenuForGroup(_groupId, "Load From Zone", _troopCommandsPath) - local _transportLimit = ctld.getTransportLimit(_unitTypename) - local itemNb = 0 - local menuEntries = {} - local menuPath = _troopCommandsPath - for _,_loadGroup in pairs(ctld.loadableGroups) do - if not _loadGroup.side or _loadGroup.side == _unit:getCoalition() then - -- check size & unit - if _transportLimit >= _loadGroup.total then - table.insert(menuEntries, { text = ctld.i18n_translate("Load ").._loadGroup.name, group = _loadGroup }) - end + -- local _loadPath = missionCommands.addSubMenuForGroup(_groupId, "Load From Zone", _troopCommandsPath) + local _transportLimit = ctld.getTransportLimit(_unitTypename) + local itemNb = 0 + local menuEntries = {} + local menuPath = _troopCommandsPath + for _, _loadGroup in pairs(ctld.loadableGroups) do + if not _loadGroup.side or _loadGroup.side == _unit:getCoalition() then + -- check size & unit + if _transportLimit >= _loadGroup.total then + table.insert(menuEntries, + { text = ctld.i18n_translate("Load ") .. _loadGroup.name, group = _loadGroup }) end end - for _i, _menu in ipairs(menuEntries) do - -- add the menu item - itemNb = itemNb + 1 - if itemNb == 9 and _i < #menuEntries then -- page limit reached (first item is "unload") - menuPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Next page"), menuPath) - itemNb = 1 - end - missionCommands.addCommandForGroup(_groupId, _menu.text, menuPath, ctld.loadTroopsFromZone, { _unitName, true,_menu.group,false }) - end - - if ctld.unitCanCarryVehicles(_unit) then - - local _vehicleCommandsPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Vehicle / FOB Transport"), _rootPath) - - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Unload Vehicles"), _vehicleCommandsPath, ctld.unloadTroops, { _unitName, false }) - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Load / Extract Vehicles"), _vehicleCommandsPath, ctld.loadTroopsFromZone, { _unitName, false,"",true }) - - if ctld.enabledFOBBuilding and ctld.staticBugWorkaround == false then - - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Load / Unload FOB Crate"), _vehicleCommandsPath, ctld.loadUnloadFOBCrate, { _unitName, false }) - end - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Check Cargo"), _vehicleCommandsPath, ctld.checkTroopStatus, { _unitName }) - end - end + for _i, _menu in ipairs(menuEntries) do + -- add the menu item + itemNb = itemNb + 1 + if itemNb == 9 and _i < #menuEntries then -- page limit reached (first item is "unload") + menuPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Next page"), + menuPath) + itemNb = 1 + end + missionCommands.addCommandForGroup(_groupId, _menu.text, menuPath, ctld.loadTroopsFromZone, + { _unitName, true, _menu.group, false }) + end + if ctld.unitCanCarryVehicles(_unit) then + local _vehicleCommandsPath = missionCommands.addSubMenuForGroup(_groupId, + ctld.i18n_translate("Vehicle / FOB Transport"), _rootPath) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Unload Vehicles"), + _vehicleCommandsPath, ctld.unloadTroops, { _unitName, false }) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Load / Extract Vehicles"), + _vehicleCommandsPath, ctld.loadTroopsFromZone, { _unitName, false, "", true }) - if ctld.enableCrates and _unitActions.crates then - if ctld.unitCanCarryVehicles(_unit) == false then - -- sort the crate categories alphabetically - local crateCategories = {} - for category, _ in pairs(ctld.spawnableCrates) do - table.insert(crateCategories, category) + if ctld.enabledFOBBuilding and ctld.staticBugWorkaround == false then + missionCommands.addCommandForGroup(_groupId, + ctld.i18n_translate("Load / Unload FOB Crate"), _vehicleCommandsPath, + ctld.loadUnloadFOBCrate, { _unitName, false }) + end + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Check Cargo"), + _vehicleCommandsPath, ctld.checkTroopStatus, { _unitName }) + end + end + + if ctld.enableCrates and _unitActions.crates then + if ctld.unitCanCarryVehicles(_unit) == false then + -- sort the crate categories alphabetically + local crateCategories = {} + for category, _ in pairs(ctld.spawnableCrates) do + table.insert(crateCategories, category) + end + table.sort(crateCategories) + --ctld.logTrace("crateCategories = [%s]", ctld.p(crateCategories)) + + -- add menu for spawning crates + local itemNbMain = 0 + local _cratesMenuPath = missionCommands.addSubMenuForGroup(_groupId, + ctld.i18n_translate("Crates: Vehicle / FOB / Drone"), _rootPath) + + for _i, _category in ipairs(crateCategories) do + local _subMenuName = _category + local _crates = ctld.spawnableCrates[_subMenuName] + + -- add the submenu item + itemNbMain = itemNbMain + 1 + if itemNbMain == 10 and _i < #crateCategories then -- page limit reached + _cratesMenuPath = missionCommands.addSubMenuForGroup(_groupId, + ctld.i18n_translate("Next page"), _cratesMenuPath) + itemNbMain = 1 end - table.sort(crateCategories) - ctld.logTrace("crateCategories = [%s]", ctld.p(crateCategories)) - - -- add menu for spawning crates - local itemNbMain = 0 - local _cratesMenuPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Vehicle / FOB Crates / Drone"), _rootPath) - for _i, _category in ipairs(crateCategories) do - local _subMenuName = _category - local _crates = ctld.spawnableCrates[_subMenuName] - - -- add the submenu item - itemNbMain = itemNbMain + 1 - if itemNbMain == 10 and _i < #crateCategories then -- page limit reached - _cratesMenuPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Next page"), _cratesMenuPath) - itemNbMain = 1 - end - local itemNbSubmenu = 0 - local menuEntries = {} - local _subMenuPath = missionCommands.addSubMenuForGroup(_groupId, _subMenuName, _cratesMenuPath) - for _, _crate in pairs(_crates) do - ctld.logTrace("_crate = [%s]", ctld.p(_crate)) - if not(_crate.multiple) or ctld.enableAllCrates then - local isJTAC = ctld.isJTACUnitType(_crate.unit) - ctld.logTrace("isJTAC = [%s]", ctld.p(isJTAC)) - if not isJTAC or (isJTAC and ctld.JTAC_dropEnabled) then - if _crate.side == nil or (_crate.side == _unit:getCoalition()) then - local _crateRadioMsg = _crate.desc - --add in the number of crates required to build something - if _crate.cratesRequired ~= nil and _crate.cratesRequired > 1 then - _crateRadioMsg = _crateRadioMsg.." (".._crate.cratesRequired..")" - end - if _crate.multiple then - _crateRadioMsg = "* " .. _crateRadioMsg - end - local _menuEntry = { text = _crateRadioMsg, crate = _crate } - ctld.logTrace("_menuEntry = [%s]", ctld.p(_menuEntry)) - table.insert(menuEntries, _menuEntry) + local itemNbSubmenu = 0 + local menuEntries = {} + local _subMenuPath = missionCommands.addSubMenuForGroup(_groupId, _subMenuName, _cratesMenuPath) + for _, _crate in pairs(_crates) do + --ctld.logTrace("_crate = [%s]", ctld.p(_crate)) + if not (_crate.multiple) or ctld.enableAllCrates then + local isJTAC = ctld.isJTACUnitType(_crate.unit) + --ctld.logTrace("isJTAC = [%s]", ctld.p(isJTAC)) + if not isJTAC or (isJTAC and ctld.JTAC_dropEnabled) then + if _crate.side == nil or (_crate.side == _unit:getCoalition()) then + local _crateRadioMsg = _crate.desc + --add in the number of crates required to build something + if _crate.cratesRequired ~= nil and _crate.cratesRequired > 1 then + _crateRadioMsg = _crateRadioMsg .. " (" .. _crate.cratesRequired .. + ")" end + if _crate.multiple then + _crateRadioMsg = "* " .. _crateRadioMsg + end + local _menuEntry = { text = _crateRadioMsg, crate = _crate } + --ctld.logTrace("_menuEntry = [%s]", ctld.p(_menuEntry)) + table.insert(menuEntries, _menuEntry) end end end - for _i, _menu in ipairs(menuEntries) do - ctld.logTrace("_menu = [%s]", ctld.p(_menu)) - -- add the submenu item - itemNbSubmenu = itemNbSubmenu + 1 - if itemNbSubmenu == 10 and _i < #menuEntries then -- page limit reached - _subMenuPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Next page"), _subMenuPath) - itemNbSubmenu = 1 - end - missionCommands.addCommandForGroup(_groupId, _menu.text, _subMenuPath, ctld.spawnCrate, { _unitName, _menu.crate.weight }) + end + for _i, _menu in ipairs(menuEntries) do + --ctld.logTrace("_menu = [%s]", ctld.p(_menu)) + -- add the submenu item + itemNbSubmenu = itemNbSubmenu + 1 + if itemNbSubmenu == 10 and _i < #menuEntries then -- page limit reached + _subMenuPath = missionCommands.addSubMenuForGroup(_groupId, + ctld.i18n_translate("Next page"), _subMenuPath) + itemNbSubmenu = 1 end + missionCommands.addCommandForGroup(_groupId, _menu.text, _subMenuPath, + ctld.spawnCrate, { _unitName, _menu.crate.weight }) end end end - - if (ctld.enabledFOBBuilding or ctld.enableCrates) and _unitActions.crates then - - local _crateCommands = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("CTLD Commands"), _rootPath) - if ctld.hoverPickup == false or ctld.loadCrateFromMenu == true then - if ctld.loadCrateFromMenu then - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Load Nearby Crate(s)"), _crateCommands, ctld.loadNearbyCrate, _unitName ) - end - end - - if ctld.loadCrateFromMenu or ctld.hoverPickup then - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Drop Crate(s)"), _crateCommands, ctld.dropSlingCrate, { _unitName }) - end - - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Unpack Any Crate"), _crateCommands, ctld.unpackCrates, { _unitName }) - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("List Nearby Crates"), _crateCommands, ctld.listNearbyCrates, { _unitName }) - - if ctld.enabledFOBBuilding then - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("List FOBs"), _crateCommands, ctld.listFOBS, { _unitName }) - end - end - - if ctld.enableSmokeDrop then - local _smokeMenu = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Smoke Markers"), _rootPath) - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Drop Red Smoke"), _smokeMenu, ctld.dropSmoke, { _unitName, trigger.smokeColor.Red }) - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Drop Blue Smoke"), _smokeMenu, ctld.dropSmoke, { _unitName, trigger.smokeColor.Blue }) - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Drop Orange Smoke"), _smokeMenu, ctld.dropSmoke, { _unitName, trigger.smokeColor.Orange }) - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Drop Green Smoke"), _smokeMenu, ctld.dropSmoke, { _unitName, trigger.smokeColor.Green }) - end - - if ctld.enabledRadioBeaconDrop then - local _radioCommands = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Radio Beacons"), _rootPath) - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("List Beacons"), _radioCommands, ctld.listRadioBeacons, { _unitName }) - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Drop Beacon"), _radioCommands, ctld.dropRadioBeacon, { _unitName }) - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Remove Closest Beacon"), _radioCommands, ctld.removeRadioBeacon, { _unitName }) - elseif ctld.deployedRadioBeacons ~= {} then - local _radioCommands = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Radio Beacons"), _rootPath) - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("List Beacons"), _radioCommands, ctld.listRadioBeacons, { _unitName }) - end - - ctld.addedTo[tostring(_groupId)] = true - ctld.logTrace("ctld.addedTo = %s", ctld.p(ctld.addedTo)) - ctld.logTrace("done adding CTLD menu for _groupId = %s", ctld.p(_groupId)) end + + if (ctld.enabledFOBBuilding or ctld.enableCrates) and _unitActions.crates then + local _crateCommands = missionCommands.addSubMenuForGroup(_groupId, + ctld.i18n_translate("CTLD Commands"), _rootPath) + if ctld.vehicleCommandsPath[_unitName] == nil then + ctld.vehicleCommandsPath[_unitName] = mist.utils.deepCopy(_crateCommands) + end + if ctld.hoverPickup == false or ctld.loadCrateFromMenu == true then + if ctld.loadCrateFromMenu then + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Load Nearby Crate(s)"), + _crateCommands, ctld.loadNearbyCrate, _unitName) + end + end + + if ctld.loadCrateFromMenu or ctld.hoverPickup then + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Drop Crate(s)"), + _crateCommands, ctld.dropSlingCrate, { _unitName }) + end + + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Unpack Any Crate"), + _crateCommands, ctld.unpackCrates, { _unitName }) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("List Nearby Crates"), + _crateCommands, ctld.listNearbyCrates, { _unitName }) + + if ctld.enabledFOBBuilding then + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("List FOBs"), _crateCommands, + ctld.listFOBS, { _unitName }) + end + + if ctld.enableRepackingVehicles == true then + ctld.updateRepackMenu( _unitName ) -- add repack menu + end + end + + if ctld.enableSmokeDrop then + local _smokeMenu = missionCommands.addSubMenuForGroup(_groupId, + ctld.i18n_translate("Smoke Markers"), _rootPath) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Drop Red Smoke"), _smokeMenu, + ctld.dropSmoke, { _unitName, trigger.smokeColor.Red }) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Drop Blue Smoke"), _smokeMenu, + ctld.dropSmoke, { _unitName, trigger.smokeColor.Blue }) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Drop Orange Smoke"), _smokeMenu, + ctld.dropSmoke, { _unitName, trigger.smokeColor.Orange }) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Drop Green Smoke"), _smokeMenu, + ctld.dropSmoke, { _unitName, trigger.smokeColor.Green }) + end + + if ctld.enabledRadioBeaconDrop then + local _radioCommands = missionCommands.addSubMenuForGroup(_groupId, + ctld.i18n_translate("Radio Beacons"), _rootPath) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("List Beacons"), _radioCommands, + ctld.listRadioBeacons, { _unitName }) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Drop Beacon"), _radioCommands, + ctld.dropRadioBeacon, { _unitName }) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Remove Closest Beacon"), + _radioCommands, ctld.removeRadioBeacon, { _unitName }) + elseif ctld.deployedRadioBeacons ~= {} then + local _radioCommands = missionCommands.addSubMenuForGroup(_groupId, + ctld.i18n_translate("Radio Beacons"), _rootPath) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("List Beacons"), _radioCommands, + ctld.listRadioBeacons, { _unitName }) + end + + ctld.addedTo[tostring(_groupId)] = true + ctld.logTrace("ctld.addedTo = %s", ctld.p(ctld.addedTo)) + ctld.logTrace("done adding CTLD menu for _groupId = %s", ctld.p(_groupId)) end end - end) + end + end) if (not status) then ctld.logError(string.format("Error adding f10 to transport: %s", error)) end end +--****************************************************************************************************** +function ctld.buildPaginatedMenu(_menuEntries) --[[ params table : + { text = command name menu + groupId = playerUnit groupId, + subMenuPath = complet MenuPath clicked, + menuFunction = function name to run on clicked menu, + menuArgsTable = table with arguments for the function to run, + }]] + local nextSubMenuPath = {} + local itemNbSubmenu = 0 + for i, menu in ipairs(_menuEntries) do + if #nextSubMenuPath ~= 0 then + menu.subMenuPath = mist.utils.deepCopy(nextSubMenuPath) + --menu.subMenuPath = nextSubMenuPath + end + -- add the submenu item + itemNbSubmenu = itemNbSubmenu + 1 + if itemNbSubmenu == 10 and i < #_menuEntries then -- page limit reached + nextSubMenuPath = missionCommands.addSubMenuForGroup(menu.groupId, ctld.i18n_translate("Next page"), menu.subMenuPath) + itemNbSubmenu = 1 + end + menu.menuArgsTable.subMenuPath = mist.utils.deepCopy(menu.subMenuPath) -- copy the table to avoid overwriting the same table in the next loop + menu.menuArgsTable.subMenuLineIndex = itemNbSubmenu + ctld.logTrace("FG_ boucle[%s].groupId = %s", i, menu.groupId) + ctld.logTrace("FG_ boucle[%s].menu.text = %s", i, menu.text) + ctld.logTrace("FG_ boucle[%s].menu.subMenuPath = %s", i, menu.subMenuPath) + ctld.logTrace("FG_ boucle[%s].menu.menuFunction = %s", i, menu.menuFunction) + local r = missionCommands.addCommandForGroup(menu.groupId, menu.text, menu.subMenuPath, menu.menuFunction, mist.utils.deepCopy(menu.menuArgsTable)) + ctld.logTrace("FG_ boucle[%s].r = %s", i, r) + ctld.logTrace("FG_ boucle[%s].menu.menuArgsTable = %s", i, ctld.p(menu.menuArgsTable)) + end +end + +--****************************************************************************************************** +-- return true if _typeUnitDesc already exist in _MenuEntriesTable +-- ex: ctld.isUnitInrepackableVehicles(repackableTable, "Humvee - TOW") +function ctld.isUnitInMenuEntriesTable(_MenuEntriesTable, _typeUnitDesc) + for i=1, #_MenuEntriesTable do + if _MenuEntriesTable[i].menuArgsTable.desc == _typeUnitDesc then + return true + end + end + return false +end +--****************************************************************************************************** +function ctld.updateRepackMenu(_playerUnitName) + local playerUnit = ctld.getTransportUnit(_playerUnitName) + if playerUnit then + local _groupId = ctld.getGroupId(playerUnit) + if _groupId == nil then + return + end + if ctld.enableRepackingVehicles then + local repackableVehicles = ctld.getUnitsInRepackRadius(_playerUnitName, ctld.maximumDistanceRepackableUnitsSearch) + if repackableVehicles then + --ctld.logTrace("FG_ ctld.vehicleCommandsPath[_playerUnitName] = %s", ctld.p(ctld.vehicleCommandsPath[_playerUnitName])) + local RepackPreviousMenu = mist.utils.deepCopy(ctld.vehicleCommandsPath[_playerUnitName]) + local RepackCommandsPath = mist.utils.deepCopy(ctld.vehicleCommandsPath[_playerUnitName]) + local repackSubMenuText = ctld.i18n_translate("Repack Vehicles") + RepackCommandsPath[#RepackCommandsPath + 1] = repackSubMenuText -- add the submenu name to get the complet repack path + --ctld.logTrace("FG_ RepackCommandsPath = %s", ctld.p(RepackCommandsPath)) + missionCommands.removeItemForGroup(_groupId, RepackCommandsPath) -- remove existing "Repack Vehicles" menu + ctld.logTrace("FG_ RepackCommandsPath = %s", ctld.p(RepackCommandsPath)) + + ctld.logTrace("FG_ repackableVehicles = %s", ctld.p(repackableVehicles)) + ctld.logTrace("FG_ repackSubMenuText = %s", ctld.p(repackSubMenuText)) + ctld.logTrace("FG_ RepackPreviousMenu = %s", ctld.p(RepackPreviousMenu)) + local RepackMenuPath = missionCommands.addSubMenuForGroup(_groupId, repackSubMenuText, RepackPreviousMenu) + local menuEntries = {} + for i, _vehicle in ipairs(repackableVehicles) do + if ctld.isUnitInMenuEntriesTable(menuEntries, _vehicle.desc) == false then + _vehicle.playerUnitName = _playerUnitName + table.insert(menuEntries, { text = ctld.i18n_translate("repack ") .. _vehicle.unit, + groupId = _groupId, + subMenuPath = RepackMenuPath, + menuFunction = ctld.repackVehicleRequest, + menuArgsTable = mist.utils.deepCopy(_vehicle) + }) + end + end + --ctld.logTrace("FG_ menuEntries = %s", ctld.p(menuEntries)) + ctld.buildPaginatedMenu(menuEntries) + end + end + end +end + +--****************************************************************************************************** +function ctld.autoUpdateRepackMenu(p, t) -- auto update repack menus for each transport unit + if t == nil then t = timer.getTime() end + if p.reschedule == nil then p.reschedule = false end + ctld.logTrace("FG_ ctld.autoUpdateRepackMenu.p.reschedule = %s", p.reschedule) + if ctld.enableRepackingVehicles then + for _, _unitName in pairs(ctld.transportPilotNames) do + if ctld.vehicleCommandsPath[_unitName] ~= nil then + local status, error = pcall( + function() + local _unit = ctld.getTransportUnit(_unitName) + if _unit then + -- if transport unit landed => update repack menus + if (ctld.inAir(_unit) == false or (ctld.heightDiff(_unit) <= 0.1 + 3.0 and mist.vec.mag(_unit:getVelocity()) < 0.1)) then + local _unitTypename = _unit:getTypeName() + local _groupId = ctld.getGroupId(_unit) + if _groupId then + if ctld.addedTo[tostring(_groupId)] ~= nil then -- if groupMenu on loaded => add RepackMenus + ctld.updateRepackMenu(_unitName) + end + end + end + end + end) + if (not status) then + env.error(string.format("Error in ctld.autoUpdateRepackMenu : %s", error), false) + end + end + end + end + if p.reschedule == true or p.reschedule == nil then + return t + 5 -- reschedule every 5 seconds + end +end + +--****************************************************************************************************** function ctld.addOtherF10MenuOptions() - ctld.logDebug("ctld.addOtherF10MenuOptions") + --ctld.logDebug("ctld.addOtherF10MenuOptions") -- reschedule every 10 seconds timer.scheduleFunction(ctld.addOtherF10MenuOptions, nil, timer.getTime() + 10) local status, error = pcall(function() - -- now do any player controlled aircraft that ARENT transport units if ctld.enabledRadioBeaconDrop then - -- get all BLUE players - ctld.addRadioListCommand(2) - - -- get all RED players - ctld.addRadioListCommand(1) + ctld.addRadioListCommand(2) -- get all BLUE players + ctld.addRadioListCommand(1) -- get all RED players end if ctld.JTAC_jtacStatusF10 then - ctld.addJTACRadioCommand(2) -- get all BLUE players - ctld.addJTACRadioCommand(1) -- get all RED players + ctld.addJTACRadioCommand(2) -- get all BLUE players + ctld.addJTACRadioCommand(1) -- get all RED players end if ctld.reconF10Menu then - ctld.addReconRadioCommand(2) -- get all BLUE players - ctld.addReconRadioCommand(1) -- get all RED players + ctld.addReconRadioCommand(2) -- get all BLUE players + ctld.addReconRadioCommand(1) -- get all RED players end end) @@ -5951,21 +6221,18 @@ end --add to all players that arent transport function ctld.addRadioListCommand(_side) - local _players = coalition.getPlayers(_side) if _players ~= nil then - for _, _playerUnit in pairs(_players) do - local _groupId = ctld.getGroupId(_playerUnit) if _groupId then - - ctld.logTrace("ctld.addedTo = %s", ctld.p(ctld.addedTo)) + --ctld.logTrace("ctld.addedTo = %s", ctld.p(ctld.addedTo)) if ctld.addedTo[tostring(_groupId)] == nil then ctld.logTrace("adding List Radio Beacons for _groupId = %s", ctld.p(_groupId)) - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("List Radio Beacons"), nil, ctld.listRadioBeacons, { _playerUnit:getName() }) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("List Radio Beacons"), nil, + ctld.listRadioBeacons, { _playerUnit:getName() }) ctld.addedTo[tostring(_groupId)] = true end end @@ -5974,23 +6241,20 @@ function ctld.addRadioListCommand(_side) end function ctld.addJTACRadioCommand(_side) - local _players = coalition.getPlayers(_side) if _players ~= nil then - for _, _playerUnit in pairs(_players) do - local _groupId = ctld.getGroupId(_playerUnit) if _groupId then - local newGroup = false if ctld.jtacRadioAdded[tostring(_groupId)] == nil then - ctld.logDebug("ctld.addJTACRadioCommand - adding JTAC radio menu for unit [%s]", ctld.p(_playerUnit:getName())) + --ctld.logDebug("ctld.addJTACRadioCommand - adding JTAC radio menu for unit [%s]", ctld.p(_playerUnit:getName())) newGroup = true local JTACpath = missionCommands.addSubMenuForGroup(_groupId, ctld.jtacMenuName) - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("JTAC Status"), JTACpath, ctld.getJTACStatus, { _playerUnit:getName() }) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("JTAC Status"), JTACpath, + ctld.getJTACStatus, { _playerUnit:getName() }) ctld.jtacRadioAdded[tostring(_groupId)] = true end @@ -5999,35 +6263,34 @@ function ctld.addJTACRadioCommand(_side) --depending on the delay, this part of the radio menu will be refreshed less often or as often as the static JTAC status command, this is for better reliability for the user when navigating through the menus. New groups will get the lists regardless and if a new JTAC is added all lists will be refreshed regardless of the delay. if ctld.jtacLastRadioRefresh + ctld.jtacRadioRefreshDelay <= time or ctld.refreshJTACmenu[_side] or newGroup then - ctld.jtacLastRadioRefresh = time --build the path to the CTLD JTAC menu - local jtacCurrentPagePath = {[1]=ctld.jtacMenuName} + local jtacCurrentPagePath = { [1] = ctld.jtacMenuName } --build the path for the NextPage submenu on the first page of the CTLD JTAC menu local NextPageText = "Next Page" - local MainNextPagePath = {[1]=ctld.jtacMenuName, [2]=NextPageText} + local MainNextPagePath = { [1] = ctld.jtacMenuName, [2] = NextPageText } --remove it along with everything that's in it missionCommands.removeItemForGroup(_groupId, MainNextPagePath) --counter to know when to add the next page submenu to fit all of the JTAC group submenus local jtacCounter = 0 - for _jtacGroupName,jtacUnit in pairs(ctld.jtacUnits) do + for _jtacGroupName, jtacUnit in pairs(ctld.jtacUnits) do --ctld.logTrace(string.format("JTAC - MENU - [%s] - processing menu", ctld.p(_jtacGroupName))) --if the JTAC is on the same team as the group being considered local jtacCoalition = ctld.jtacUnits[_jtacGroupName].side if jtacCoalition and jtacCoalition == _side then --only bother removing the submenus on the first page of the CTLD JTAC menu as the other pages were deleted entirely above - if ctld.jtacGroupSubMenuPath[_jtacGroupName] and #ctld.jtacGroupSubMenuPath[_jtacGroupName]==2 then + if ctld.jtacGroupSubMenuPath[_jtacGroupName] and #ctld.jtacGroupSubMenuPath[_jtacGroupName] == 2 then missionCommands.removeItemForGroup(_groupId, ctld.jtacGroupSubMenuPath[_jtacGroupName]) end --ctld.logTrace(string.format("JTAC - MENU - [%s] - jtacTargetsList = %s", ctld.p(_jtacGroupName), ctld.p(ctld.jtacTargetsList[_jtacGroupName]))) --ctld.logTrace(string.format("JTAC - MENU - [%s] - jtacCurrentTargets = %s", ctld.p(_jtacGroupName), ctld.p(ctld.jtacCurrentTargets[_jtacGroupName]))) local jtacActionMenu = false - for _,_specialOptionTable in pairs(ctld.jtacSpecialOptions) do + for _, _specialOptionTable in pairs(ctld.jtacSpecialOptions) do if _specialOptionTable.globalToggle then jtacActionMenu = true break @@ -6036,17 +6299,18 @@ function ctld.addJTACRadioCommand(_side) --if JTAC has at least one other target in sight or (if special options are available (NOTE : accessed through the JTAC's own menu also) and the JTAC has at least one target) if (ctld.jtacTargetsList[_jtacGroupName] and #ctld.jtacTargetsList[_jtacGroupName] >= 1) or (ctld.jtacCurrentTargets[_jtacGroupName] and jtacActionMenu) then - local jtacGroupSubMenuName = string.format(_jtacGroupName .. " Selection") jtacCounter = jtacCounter + 1 --F2 through F10 makes 9 entries possible per page, with one being the NextMenu submenu. F1 is taken by JTAC status entry. if jtacCounter % 9 == 0 then --recover the path to the current page with space available for JTAC group submenus - jtacCurrentPagePath = missionCommands.addSubMenuForGroup(_groupId, NextPageText, jtacCurrentPagePath) + jtacCurrentPagePath = missionCommands.addSubMenuForGroup(_groupId, NextPageText, + jtacCurrentPagePath) end --add the JTAC group submenu to the current page - ctld.jtacGroupSubMenuPath[_jtacGroupName] = missionCommands.addSubMenuForGroup(_groupId, jtacGroupSubMenuName, jtacCurrentPagePath) + ctld.jtacGroupSubMenuPath[_jtacGroupName] = missionCommands.addSubMenuForGroup(_groupId, + jtacGroupSubMenuName, jtacCurrentPagePath) --ctld.logTrace(string.format("JTAC - MENU - [%s] - jtacGroupSubMenuPath = %s", ctld.p(_jtacGroupName), ctld.p(ctld.jtacGroupSubMenuPath[_jtacGroupName]))) --make a copy of the JTAC group submenu's path to insert the target's list on as many pages as required. The JTAC's group submenu path only leads to the first page @@ -6060,29 +6324,40 @@ function ctld.addJTACRadioCommand(_side) --special options local SpecialOptionsCounter = 0 - for _,_specialOption in pairs(ctld.jtacSpecialOptions) do + for _, _specialOption in pairs(ctld.jtacSpecialOptions) do if _specialOption.globalToggle then - if not jtacSpecialOptPagePath then - itemCounter = itemCounter + 1 --one item is added to the first JTAC target page - jtacSpecialOptPagePath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Actions"), jtacTargetPagePath) + itemCounter = itemCounter + + 1 --one item is added to the first JTAC target page + jtacSpecialOptPagePath = missionCommands.addSubMenuForGroup(_groupId, + ctld.i18n_translate("Actions"), jtacTargetPagePath) end - SpecialOptionsCounter = SpecialOptionsCounter+1 + SpecialOptionsCounter = SpecialOptionsCounter + 1 - if SpecialOptionsCounter%10 == 0 then - jtacSpecialOptPagePath = missionCommands.addSubMenuForGroup(_groupId, NextPageText, jtacSpecialOptPagePath) - SpecialOptionsCounter = SpecialOptionsCounter+1 --Added Next Page item + if SpecialOptionsCounter % 10 == 0 then + jtacSpecialOptPagePath = missionCommands.addSubMenuForGroup(_groupId, + NextPageText, jtacSpecialOptPagePath) + SpecialOptionsCounter = SpecialOptionsCounter + 1 --Added Next Page item end if _specialOption.jtacs then if _specialOption.jtacs[_jtacGroupName] then - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("DISABLE ") .. _specialOption.message, jtacSpecialOptPagePath, _specialOption.setter, {jtacGroupName = _jtacGroupName, value = false}) + missionCommands.addCommandForGroup(_groupId, + ctld.i18n_translate("DISABLE ") .. _specialOption.message, + jtacSpecialOptPagePath, _specialOption.setter, + { jtacGroupName = _jtacGroupName, value = false }) else - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("ENABLE ") .. _specialOption.message, jtacSpecialOptPagePath, _specialOption.setter, {jtacGroupName = _jtacGroupName, value = true}) + missionCommands.addCommandForGroup(_groupId, + ctld.i18n_translate("ENABLE ") .. _specialOption.message, + jtacSpecialOptPagePath, _specialOption.setter, + { jtacGroupName = _jtacGroupName, value = true }) end else - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("REQUEST ") .. _specialOption.message, jtacSpecialOptPagePath, _specialOption.setter, {jtacGroupName = _jtacGroupName, value = false}) --value is not used here + missionCommands.addCommandForGroup(_groupId, + ctld.i18n_translate("REQUEST ") .. _specialOption.message, + jtacSpecialOptPagePath, _specialOption.setter, + { jtacGroupName = _jtacGroupName, value = false }) --value is not used here end end end @@ -6092,13 +6367,15 @@ function ctld.addJTACRadioCommand(_side) --ctld.logTrace(string.format("JTAC - MENU - [%s] - adding targets menu", ctld.p(_jtacGroupName))) --add a reset targeting option to revert to automatic JTAC unit targeting - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Reset TGT Selection"), jtacTargetPagePath, ctld.setJTACTarget, {jtacGroupName = _jtacGroupName, targetName = nil}) + missionCommands.addCommandForGroup(_groupId, + ctld.i18n_translate("Reset TGT Selection"), jtacTargetPagePath, + ctld.setJTACTarget, { jtacGroupName = _jtacGroupName, targetName = nil }) - itemCounter = itemCounter + 1 --one item is added to the first JTAC target page + itemCounter = itemCounter + 1 --one item is added to the first JTAC target page --indicator table to know which unitType was already added to the radio submenu local typeNameList = {} - for _,target in pairs(ctld.jtacTargetsList[_jtacGroupName]) do + for _, target in pairs(ctld.jtacTargetsList[_jtacGroupName]) do local targetName = target.unit:getName() --check if the jtac has a current target before filtering it out if possible if (ctld.jtacCurrentTargets[_jtacGroupName] and targetName ~= ctld.jtacCurrentTargets[_jtacGroupName].name) then @@ -6106,28 +6383,33 @@ function ctld.addJTACRadioCommand(_side) if targetType_name then if typeNameList[targetType_name] then - typeNameList[targetType_name].amount = typeNameList[targetType_name].amount + 1 + typeNameList[targetType_name].amount = typeNameList[targetType_name] + .amount + 1 else typeNameList[targetType_name] = {} - typeNameList[targetType_name].targetName = targetName --store the first targetName + typeNameList[targetType_name].targetName = + targetName --store the first targetName typeNameList[targetType_name].amount = 1 end end end end - for typeName,info in pairs(typeNameList) do + for typeName, info in pairs(typeNameList) do local amount = info.amount local targetName = info.targetName itemCounter = itemCounter + 1 --F1 through F10 makes 10 entries possible per page, with one being the NextMenu submenu. - if itemCounter%10 == 0 then - jtacTargetPagePath = missionCommands.addSubMenuForGroup(_groupId, NextPageText, jtacTargetPagePath) - itemCounter = itemCounter + 1 --added the next page item + if itemCounter % 10 == 0 then + jtacTargetPagePath = missionCommands.addSubMenuForGroup(_groupId, + NextPageText, jtacTargetPagePath) + itemCounter = itemCounter + 1 --added the next page item end - missionCommands.addCommandForGroup(_groupId, string.format(typeName .. "(" .. amount .. ")"), jtacTargetPagePath, ctld.setJTACTarget, {jtacGroupName = _jtacGroupName, targetName = targetName}) + missionCommands.addCommandForGroup(_groupId, + string.format(typeName .. "(" .. amount .. ")"), jtacTargetPagePath, + ctld.setJTACTarget, { jtacGroupName = _jtacGroupName, targetName = targetName }) end end end @@ -6144,8 +6426,7 @@ function ctld.addJTACRadioCommand(_side) end function ctld.getGroupId(_unit) - - local _unitDB = mist.DBs.unitsById[tonumber(_unit:getID())] + local _unitDB = mist.DBs.unitsById[tonumber(_unit:getID())] if _unitDB ~= nil and _unitDB.groupId then return _unitDB.groupId end @@ -6155,7 +6436,6 @@ end --get distance in meters assuming a Flat world function ctld.getDistance(_point1, _point2) - local xUnit = _point1.x local yUnit = _point1.z local xZone = _point2.x @@ -6167,82 +6447,81 @@ function ctld.getDistance(_point1, _point2) return math.sqrt(xDiff * xDiff + yDiff * yDiff) end - ------------ JTAC ----------- ctld.jtacMenuName = "JTAC" --name of the CTLD JTAC radio menu ctld.jtacLaserPoints = {} ctld.jtacIRPoints = {} ctld.jtacSmokeMarks = {} -ctld.jtacUnits = {} -- list of JTAC units for f10 command -ctld.jtacStop = {} -- jtacs to tell to stop lasing +ctld.jtacUnits = {} -- list of JTAC units for f10 command +ctld.jtacStop = {} -- jtacs to tell to stop lasing ctld.jtacCurrentTargets = {} -ctld.jtacTargetsList = {} --current available targets to each JTAC for lasing (targets from other JTACs are filtered out). Contains DCS unit objects with their methods and the distance to the JTAC {unit, dist} +ctld.jtacTargetsList = {} --current available targets to each JTAC for lasing (targets from other JTACs are filtered out). Contains DCS unit objects with their methods and the distance to the JTAC {unit, dist} ctld.jtacSelectedTarget = {} --currently user selected target if it contains a unit's name, otherwise contains 1 or nil (if not initialized) -ctld.jtacSpecialOptions = { --list which contains the status of special options for each jtac, ordered for them to show up in the correct order in the corresponding radio menu - standbyMode = { --#1 - globalToggle = ctld.JTAC_allowStandbyMode; - message = "Standby Mode"; - setter = nil; --ctld.setStdbMode, will be set after declaration of said function +ctld.jtacSpecialOptions = { --list which contains the status of special options for each jtac, ordered for them to show up in the correct order in the corresponding radio menu + standbyMode = { --#1 + globalToggle = ctld.JTAC_allowStandbyMode, + message = "Standby Mode", + setter = nil, --ctld.setStdbMode, will be set after declaration of said function jtacs = { --enable flag for each JTAC - }; - }; --disable designation by the JTAC - smokeMarker = { --#4 - globalToggle = ctld.JTAC_allowSmokeRequest; - message = "Smoke on TGT"; - setter = nil; --ctld.setSmokeOnTarget - }; --smoke marker on target - laseSpotCorrections = { --#2 - globalToggle = ctld.JTAC_laseSpotCorrections; - message = "Speed Corrections"; - setter = nil; --ctld.setLaseCompensation + }, + }, --disable designation by the JTAC + smokeMarker = { --#4 + globalToggle = ctld.JTAC_allowSmokeRequest, + message = "Smoke on TGT", + setter = nil, --ctld.setSmokeOnTarget + }, --smoke marker on target + laseSpotCorrections = { --#2 + globalToggle = ctld.JTAC_laseSpotCorrections, + message = "Speed Corrections", + setter = nil, --ctld.setLaseCompensation jtacs = { --enable flag for each JTAC - }; - }; --target speed and wind compensation for laser spot - _9Line = { --#3 - globalToggle = ctld.JTAC_allow9Line; - message = "9 Line"; - setter = nil; --ctld.setJTAC9Line - }; --9Line message for JTAC + }, + }, --target speed and wind compensation for laser spot + _9Line = { --#3 + globalToggle = ctld.JTAC_allow9Line, + message = "9 Line", + setter = nil, --ctld.setJTAC9Line + }, --9Line message for JTAC } -ctld.jtacRadioAdded = {} --keeps track of who's had the radio command added -ctld.jtacGroupSubMenuPath = {} --keeps track of which submenu contains each JTAC's target selection menu -ctld.jtacRadioRefreshDelay = 120 --determines how often in seconds the dynamic parts of the jtac radio menu (target lists) will be refreshed -ctld.jtacLastRadioRefresh = 0 -- time at which the target lists were refreshed for everyone at least -ctld.refreshJTACmenu = {} --indicator to know when a new JTAC is added to a coalition in order to rebuild the corresponding target lists +ctld.jtacRadioAdded = {} --keeps track of who's had the radio command added +ctld.jtacGroupSubMenuPath = {} --keeps track of which submenu contains each JTAC's target selection menu +ctld.jtacRadioRefreshDelay = 120 --determines how often in seconds the dynamic parts of the jtac radio menu (target lists) will be refreshed +ctld.jtacLastRadioRefresh = 0 -- time at which the target lists were refreshed for everyone at least +ctld.refreshJTACmenu = {} --indicator to know when a new JTAC is added to a coalition in order to rebuild the corresponding target lists ctld.jtacGeneratedLaserCodes = {} -- keeps track of generated codes, cycles when they run out ctld.jtacLaserPointCodes = {} ctld.jtacRadioData = {} --[[ - Called when a new JTAC is spawned, it will wait one second for DCS to have time to fill the group with units, and then call ctld.JTACAutoLase. + Called when a new JTAC is spawned, it will wait one second for DCS to have time to fill the group with units, and then call ctld.JTACAutoLase. - The goal here is to correct a bug: when a group is respawned (i.e. when any group with the name of a previously existing group is spawned), - DCS spawns a group which exists (Group.getByName gets a valid table, and group:isExist returns true), but has no units (i.e. group:getUnits returns an empty table). - This causes JTACAutoLase to call cleanupJTAC because it does not find the JTAC unit, and the JTAC to be put out of the JTACAutoLase loop, and never processed again. - By waiting a bit, the group gets populated before JTACAutoLase is called, hence avoiding a trip to cleanupJTAC. + The goal here is to correct a bug: when a group is respawned (i.e. when any group with the name of a previously existing group is spawned), + DCS spawns a group which exists (Group.getByName gets a valid table, and group:isExist returns true), but has no units (i.e. group:getUnits returns an empty table). + This causes JTACAutoLase to call cleanupJTAC because it does not find the JTAC unit, and the JTAC to be put out of the JTACAutoLase loop, and never processed again. + By waiting a bit, the group gets populated before JTACAutoLase is called, hence avoiding a trip to cleanupJTAC. ]] function ctld.JTACStart(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio) - mist.scheduleFunction(ctld.JTACAutoLase, {_jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio}, timer.getTime()+1) + mist.scheduleFunction(ctld.JTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, + timer.getTime() + 1) end function ctld.JTACAutoLase(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio) - ctld.logDebug(string.format("ctld.JTACAutoLase(_jtacGroupName=%s, _laserCode=%s", ctld.p(_jtacGroupName), ctld.p(_laserCode))) - + --ctld.logDebug(string.format("ctld.JTACAutoLase(_jtacGroupName=%s, _laserCode=%s", ctld.p(_jtacGroupName), ctld.p(_laserCode))) local _radio = _radio if not _radio then _radio = {} if _laserCode then local _laserCode = tonumber(_laserCode) if _laserCode and _laserCode >= 1111 and _laserCode <= 1688 then - local _laserB = math.floor((_laserCode - 1000)/100) - local _laserCD = _laserCode - 1000 - _laserB*100 - local _frequency = tostring(30+_laserB+_laserCD*0.05) - ctld.logTrace(string.format("_laserB=%s", ctld.p(_laserB))) - ctld.logTrace(string.format("_laserCD=%s", ctld.p(_laserCD))) - ctld.logTrace(string.format("_frequency=%s", ctld.p(_frequency))) + local _laserB = math.floor((_laserCode - 1000) / 100) + local _laserCD = _laserCode - 1000 - _laserB * 100 + local _frequency = tostring(30 + _laserB + _laserCD * 0.05) + --ctld.logTrace(string.format("_laserB=%s", ctld.p(_laserB))) + --ctld.logTrace(string.format("_laserCD=%s", ctld.p(_laserCD))) + --ctld.logTrace(string.format("_frequency=%s", ctld.p(_frequency))) _radio.freq = _frequency _radio.mod = "fm" end @@ -6254,7 +6533,7 @@ function ctld.JTACAutoLase(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _ end if ctld.jtacStop[_jtacGroupName] == true then - ctld.jtacStop[_jtacGroupName] = nil -- allow it to be started again + ctld.jtacStop[_jtacGroupName] = nil -- allow it to be started again ctld.cleanupJTAC(_jtacGroupName) return end @@ -6270,18 +6549,19 @@ function ctld.JTACAutoLase(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _ local _jtacUnit if _jtacGroup == nil or #_jtacGroup == 0 then - --check not in a heli if ctld.inTransitTroops then for _, _onboard in pairs(ctld.inTransitTroops) do if _onboard ~= nil then if _onboard.troops ~= nil and _onboard.troops.groupName ~= nil and _onboard.troops.groupName == _jtacGroupName then - --jtac soldier being transported by heli ctld.cleanupJTAC(_jtacGroupName) - ctld.logTrace(string.format("JTAC - LASE - [%s] - in transport, waiting - scheduling JTACAutoLase in %ss at %s", ctld.p(_jtacGroupName), ctld.p(10), ctld.p(timer.getTime() + 10))) - timer.scheduleFunction(ctld.timerJTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, timer.getTime() + 10) + ctld.logTrace(string.format( + "JTAC - LASE - [%s] - in transport, waiting - scheduling JTACAutoLase in %ss at %s", + ctld.p(_jtacGroupName), ctld.p(10), ctld.p(timer.getTime() + 10))) + timer.scheduleFunction(ctld.timerJTACAutoLase, + { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, timer.getTime() + 10) return end @@ -6289,8 +6569,11 @@ function ctld.JTACAutoLase(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _ --jtac vehicle being transported by heli ctld.cleanupJTAC(_jtacGroupName) - ctld.logTrace(string.format("JTAC - LASE - [%s] - in transport, waiting - scheduling JTACAutoLase in %ss at %s", ctld.p(_jtacGroupName), ctld.p(10), ctld.p(timer.getTime() + 10))) - timer.scheduleFunction(ctld.timerJTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, timer.getTime() + 10) + ctld.logTrace(string.format( + "JTAC - LASE - [%s] - in transport, waiting - scheduling JTACAutoLase in %ss at %s", + ctld.p(_jtacGroupName), ctld.p(10), ctld.p(timer.getTime() + 10))) + timer.scheduleFunction(ctld.timerJTACAutoLase, + { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, timer.getTime() + 10) return end end @@ -6298,7 +6581,8 @@ function ctld.JTACAutoLase(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _ end if ctld.jtacUnits[_jtacGroupName] ~= nil then - ctld.notifyCoalition(ctld.i18n_translate("JTAC Group %1 KIA!", _jtacGroupName), 10, ctld.jtacUnits[_jtacGroupName].side, _radio) + ctld.notifyCoalition(ctld.i18n_translate("JTAC Group %1 KIA!", _jtacGroupName), 10, + ctld.jtacUnits[_jtacGroupName].side, _radio) end --remove from list @@ -6306,7 +6590,6 @@ function ctld.JTACAutoLase(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _ return else - _jtacUnit = _jtacGroup[1] local _jtacCoalition = _jtacUnit:getCoalition() --add to list @@ -6319,7 +6602,7 @@ function ctld.JTACAutoLase(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _ if _jtacCoalition then ctld.refreshJTACmenu[_jtacCoalition] = true end --Special Options - for _,_specialOption in pairs(ctld.jtacSpecialOptions) do + for _, _specialOption in pairs(ctld.jtacSpecialOptions) do if _specialOption.jtacs then _specialOption.jtacs[_jtacGroupName] = false end @@ -6332,7 +6615,6 @@ function ctld.JTACAutoLase(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _ -- work out smoke colour if _colour == nil then - if _jtacUnit:getCoalition() == 1 then _colour = ctld.JTAC_smokeColour_RED else @@ -6342,7 +6624,6 @@ function ctld.JTACAutoLase(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _ if _smoke == nil then - if _jtacUnit:getCoalition() == 1 then _smoke = ctld.JTAC_smokeOn_RED else @@ -6355,11 +6636,12 @@ function ctld.JTACAutoLase(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _ -- search for current unit if _jtacUnit:isActive() == false then - ctld.cleanupJTAC(_jtacGroupName) - ctld.logTrace(string.format("JTAC - LASE - [%s] - not active, scheduling JTACAutoLase in 30s at %s", ctld.p(_jtacGroupName), ctld.p(timer.getTime() + 30))) - timer.scheduleFunction(ctld.timerJTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, timer.getTime() + 30) + ctld.logTrace(string.format("JTAC - LASE - [%s] - not active, scheduling JTACAutoLase in 30s at %s", + ctld.p(_jtacGroupName), ctld.p(timer.getTime() + 30))) + timer.scheduleFunction(ctld.timerJTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, + timer.getTime() + 30) return end @@ -6370,18 +6652,20 @@ function ctld.JTACAutoLase(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _ -- if the JTAC sees a unit and a target was selected by users but is not the current unit, check if the selected target is in the targets list, if it is, then it's been reacquired if _enemyUnit and ctld.jtacSelectedTarget[_jtacGroupName] ~= 1 and ctld.jtacSelectedTarget[_jtacGroupName] ~= _enemyUnit:getName() then - for _,target in pairs(ctld.jtacTargetsList[_jtacGroupName]) do + for _, target in pairs(ctld.jtacTargetsList[_jtacGroupName]) do if target then local targetUnit = target.unit local targetName = targetUnit:getName() if ctld.jtacSelectedTarget[_jtacGroupName] == targetName then - - ctld.jtacCurrentTargets[_jtacGroupName] = { name = targetName, unitType = targetUnit:getTypeName(), unitId = targetUnit:getID() } + ctld.jtacCurrentTargets[_jtacGroupName] = { name = targetName, unitType = targetUnit:getTypeName(), unitId = + targetUnit:getID() } _enemyUnit = targetUnit - local message = ctld.i18n_translate("%1, selected target reacquired, %2", _jtacGroupName, _enemyUnit:getTypeName()) - local fullMessage = message .. ctld.i18n_translate(". CODE: %1. POSITION: %2", _laserCode, ctld.getPositionString(_enemyUnit)) + local message = ctld.i18n_translate("%1, selected target reacquired, %2", _jtacGroupName, + _enemyUnit:getTypeName()) + local fullMessage = message .. + ctld.i18n_translate(". CODE: %1. POSITION: %2", _laserCode, ctld.getPositionString(_enemyUnit)) ctld.notifyCoalition(fullMessage, 10, _jtacUnit:getCoalition(), _radio, message) end end @@ -6393,10 +6677,9 @@ function ctld.JTACAutoLase(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _ local wasSelected = false if _enemyUnit == nil and ctld.jtacCurrentTargets[_jtacGroupName] ~= nil then - local _tempUnitInfo = ctld.jtacCurrentTargets[_jtacGroupName] - -- env.info("TEMP UNIT INFO: " .. tempUnitInfo.name .. " " .. tempUnitInfo.unitType) + -- env.info("TEMP UNIT INFO: " .. tempUnitInfo.name .. " " .. tempUnitInfo.unitType) local _tempUnit = Unit.getByName(_tempUnitInfo.name) @@ -6425,9 +6708,9 @@ function ctld.JTACAutoLase(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _ if _enemyUnit == nil then if _defaultEnemyUnit ~= nil then - -- store current target for easy lookup - ctld.jtacCurrentTargets[_jtacGroupName] = { name = _defaultEnemyUnit:getName(), unitType = _defaultEnemyUnit:getTypeName(), unitId = _defaultEnemyUnit:getID() } + ctld.jtacCurrentTargets[_jtacGroupName] = { name = _defaultEnemyUnit:getName(), unitType = _defaultEnemyUnit + :getTypeName(), unitId = _defaultEnemyUnit:getID() } --add check for lasing or not local action = ctld.i18n_translate("new target, ") @@ -6435,7 +6718,7 @@ function ctld.JTACAutoLase(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _ if ctld.jtacSpecialOptions.standbyMode.jtacs[_jtacGroupName] then action = ctld.i18n_translate("standing by on %1", action) else - action = ctld.i18n_translate("lasing %1", action) + action = ctld.i18n_translate("lasing %1", action) end if wasSelected and targetLost then @@ -6460,15 +6743,15 @@ function ctld.JTACAutoLase(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _ targetLost = false local message = _jtacGroupName .. action .. _defaultEnemyUnit:getTypeName() - local fullMessage = message .. '. CODE: ' .. _laserCode .. ". POSITION: " .. ctld.getPositionString(_defaultEnemyUnit) + local fullMessage = message .. + '. CODE: ' .. _laserCode .. ". POSITION: " .. ctld.getPositionString(_defaultEnemyUnit) ctld.notifyCoalition(fullMessage, 10, _jtacUnit:getCoalition(), _radio, message) -- JTAC Unit stop his route ----------------- - trigger.action.groupStopMoving(Group.getByName(_jtacGroupName)) -- stop JTAC + trigger.action.groupStopMoving(Group.getByName(_jtacGroupName)) -- stop JTAC -- create smoke if _smoke == true then - --create first smoke ctld.createSmokeMarker(_defaultEnemyUnit, _colour) end @@ -6476,50 +6759,51 @@ function ctld.JTACAutoLase(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _ end if _enemyUnit ~= nil and not ctld.jtacSpecialOptions.standbyMode.jtacs[_jtacGroupName] then - - local refreshDelay = 15 --delay in between JTACAutoLase scheduled calls when a target is tracked + local refreshDelay = 15 --delay in between JTACAutoLase scheduled calls when a target is tracked local targetSpeedVec = _enemyUnit:getVelocity() - local targetSpeed = math.sqrt(targetSpeedVec.x^2+targetSpeedVec.y^2+targetSpeedVec.z^2) - local maxUpdateDist = 5 --maximum distance the unit will be allowed to travel before the lase spot is updated again - ctld.logTrace(string.format("targetSpeed=%s", ctld.p(targetSpeed))) + local targetSpeed = math.sqrt(targetSpeedVec.x ^ 2 + targetSpeedVec.y ^ 2 + targetSpeedVec.z ^ 2) + local maxUpdateDist = 5 --maximum distance the unit will be allowed to travel before the lase spot is updated again + --ctld.logTrace(string.format("targetSpeed=%s", ctld.p(targetSpeed))) ctld.laseUnit(_enemyUnit, _jtacUnit, _jtacGroupName, _laserCode) --if the target is going sufficiently fast for it to wander off futher than the maxUpdateDist, schedule laseUnit calls to update the lase spot only (we consider that the unit lives and drives on between JTACAutoLase calls) - if targetSpeed >= maxUpdateDist/refreshDelay then - local updateTimeStep = maxUpdateDist/targetSpeed --calculate the time step so that the target is never more than maxUpdateDist from it's last lased position - ctld.logTrace(string.format("JTAC - LASE - [%s] - target is moving at %s m/s, schedulting lasing steps every %ss", ctld.p(_jtacGroupName), ctld.p(targetSpeed), ctld.p(updateTimeStep))) + if targetSpeed >= maxUpdateDist / refreshDelay then + local updateTimeStep = maxUpdateDist / + targetSpeed --calculate the time step so that the target is never more than maxUpdateDist from it's last lased position + --ctld.logTrace(string.format("JTAC - LASE - [%s] - target is moving at %s m/s, schedulting lasing steps every %ss", ctld.p(_jtacGroupName), ctld.p(targetSpeed), ctld.p(updateTimeStep))) local i = 1 - while i*updateTimeStep <= refreshDelay - updateTimeStep do --while the scheduled time for the laseUnit call isn't greater than the time between two JTACAutoLase() calls minus one time step (because at the next time step JTACAutoLase() should have been called and this in term also calls laseUnit()) - timer.scheduleFunction(ctld.timerLaseUnit,{_enemyUnit, _jtacUnit, _jtacGroupName, _laserCode}, timer.getTime()+i*updateTimeStep) + while i * updateTimeStep <= refreshDelay - updateTimeStep do --while the scheduled time for the laseUnit call isn't greater than the time between two JTACAutoLase() calls minus one time step (because at the next time step JTACAutoLase() should have been called and this in term also calls laseUnit()) + timer.scheduleFunction(ctld.timerLaseUnit, { _enemyUnit, _jtacUnit, _jtacGroupName, _laserCode }, + timer.getTime() + i * updateTimeStep) i = i + 1 end - ctld.logTrace(string.format("JTAC - LASE - [%s] - scheduled %s moving target lasing steps", ctld.p(_jtacGroupName), ctld.p(i))) + --ctld.logTrace(string.format("JTAC - LASE - [%s] - scheduled %s moving target lasing steps", ctld.p(_jtacGroupName), ctld.p(i))) end - ctld.logTrace(string.format("JTAC - LASE - [%s] - scheduling JTACAutoLase in %ss at %s", ctld.p(_jtacGroupName), ctld.p(refreshDelay), ctld.p(timer.getTime() + refreshDelay))) - timer.scheduleFunction(ctld.timerJTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, timer.getTime() + refreshDelay) + --ctld.logTrace(string.format("JTAC - LASE - [%s] - scheduling JTACAutoLase in %ss at %s", ctld.p(_jtacGroupName), ctld.p(refreshDelay), ctld.p(timer.getTime() + refreshDelay))) + timer.scheduleFunction(ctld.timerJTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, + timer.getTime() + refreshDelay) if _smoke == true then local _nextSmokeTime = ctld.jtacSmokeMarks[_enemyUnit:getName()] --recreate smoke marker after 5 mins if _nextSmokeTime ~= nil and _nextSmokeTime < timer.getTime() then - ctld.createSmokeMarker(_enemyUnit, _colour) end end - else - ctld.logDebug(string.format("JTAC - MODE - [%s] - No Enemies Nearby / Standby mode", ctld.p(_jtacGroupName))) + --ctld.logDebug(string.format("JTAC - MODE - [%s] - No Enemies Nearby / Standby mode", ctld.p(_jtacGroupName))) -- stop lazing the old spot - ctld.logDebug(string.format("JTAC - LASE - [%s] - canceling lasing of the old spot", ctld.p(_jtacGroupName))) + --ctld.logDebug(string.format("JTAC - LASE - [%s] - canceling lasing of the old spot", ctld.p(_jtacGroupName))) ctld.cancelLase(_jtacGroupName) - ctld.logTrace(string.format("JTAC - LASE - [%s] - scheduling JTACAutoLase in %ss at %s", ctld.p(_jtacGroupName), ctld.p(5), ctld.p(timer.getTime() + 5))) - timer.scheduleFunction(ctld.timerJTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, timer.getTime() + 5) + --ctld.logTrace(string.format("JTAC - LASE - [%s] - scheduling JTACAutoLase in %ss at %s", ctld.p(_jtacGroupName), ctld.p(5), ctld.p(timer.getTime() + 5))) + timer.scheduleFunction(ctld.timerJTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, + timer.getTime() + 5) end local action = ", " @@ -6528,9 +6812,11 @@ function ctld.JTACAutoLase(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _ end if targetLost then - ctld.notifyCoalition(ctld.i18n_translate("%1 %2 target lost.", _jtacGroupName , action), 10, _jtacUnit:getCoalition(), _radio) + ctld.notifyCoalition(ctld.i18n_translate("%1 %2 target lost.", _jtacGroupName, action), 10, + _jtacUnit:getCoalition(), _radio) elseif targetDestroyed then - ctld.notifyCoalition(ctld.i18n_translate("%1 %2 target destroyed.", _jtacGroupName , action), 10, _jtacUnit:getCoalition(), _radio) + ctld.notifyCoalition(ctld.i18n_translate("%1 %2 target destroyed.", _jtacGroupName, action), 10, + _jtacUnit:getCoalition(), _radio) end end @@ -6540,7 +6826,6 @@ end -- used by the timer function function ctld.timerJTACAutoLase(_args) - ctld.JTACAutoLase(_args[1], _args[2], _args[3], _args[4], _args[5], _args[6]) end @@ -6555,7 +6840,7 @@ function ctld.cleanupJTAC(_jtacGroupName) ctld.jtacSelectedTarget[_jtacGroupName] = nil - for _,_specialOption in pairs(ctld.jtacSpecialOptions) do --delete jtac specific settings for all special options + for _, _specialOption in pairs(ctld.jtacSpecialOptions) do --delete jtac specific settings for all special options if _specialOption.jtacs then _specialOption.jtacs[_jtacGroupName] = nil end @@ -6568,9 +6853,7 @@ function ctld.cleanupJTAC(_jtacGroupName) local _players = coalition.getPlayers(ctld.jtacUnits[_jtacGroupName].side) if _players ~= nil then - for _, _playerUnit in pairs(_players) do - local _groupId = ctld.getGroupId(_playerUnit) if _groupId then @@ -6585,11 +6868,9 @@ function ctld.cleanupJTAC(_jtacGroupName) ctld.jtacGroupSubMenuPath[_jtacGroupName] = nil end - --- send a message to the coalition --- if _radio is set, the message will be read out loud via SRS function ctld.notifyCoalition(_message, _displayFor, _side, _radio, _shortMessage) - trigger.action.outTextForCoalition(_side, _message, _displayFor) local _shortMessage = _shortMessage @@ -6606,23 +6887,25 @@ function ctld.notifyCoalition(_message, _displayFor, _side, _radio, _shortMessag local _culture = _radio.culture or "en-US" local _voice = _radio.voice local _googleTTS = _radio.googleTTS or false - STTS.TextToSpeech(_shortMessage, _freq, _modulation, _volume, _name, _side, nil, 1, _gender, _culture, _voice, _googleTTS) + STTS.TextToSpeech(_shortMessage, _freq, _modulation, _volume, _name, _side, nil, 1, _gender, _culture, _voice, + _googleTTS) else trigger.action.outSoundForCoalition(_side, "radiobeep.ogg") end end function ctld.createSmokeMarker(_enemyUnit, _colour) - --recreate in 5 mins ctld.jtacSmokeMarks[_enemyUnit:getName()] = timer.getTime() + 300.0 local _enemyPoint = _enemyUnit:getPoint() - trigger.action.smoke({ x = _enemyPoint.x + math.random(-ctld.JTAC_smokeMarginOfError, ctld.JTAC_smokeMarginOfError) + ctld.JTAC_smokeOffset_x, y = _enemyPoint.y + ctld.JTAC_smokeOffset_y, z = _enemyPoint.z + math.random(-ctld.JTAC_smokeMarginOfError, ctld.JTAC_smokeMarginOfError) + ctld.JTAC_smokeOffset_z }, _colour) + trigger.action.smoke( + { x = _enemyPoint.x + math.random(-ctld.JTAC_smokeMarginOfError, ctld.JTAC_smokeMarginOfError) + + ctld.JTAC_smokeOffset_x, y = _enemyPoint.y + ctld.JTAC_smokeOffset_y, z = _enemyPoint.z + + math.random(-ctld.JTAC_smokeMarginOfError, ctld.JTAC_smokeMarginOfError) + ctld.JTAC_smokeOffset_z }, _colour) end function ctld.cancelLase(_jtacGroupName) - --local index = "JTAC_"..jtacUnit:getID() local _tempLase = ctld.jtacLaserPoints[_jtacGroupName] @@ -6631,7 +6914,7 @@ function ctld.cancelLase(_jtacGroupName) Spot.destroy(_tempLase) ctld.jtacLaserPoints[_jtacGroupName] = nil - -- env.info('Destroy laze '..index) + -- env.info('Destroy laze '..index) _tempLase = nil end @@ -6642,7 +6925,7 @@ function ctld.cancelLase(_jtacGroupName) Spot.destroy(_tempIR) ctld.jtacIRPoints[_jtacGroupName] = nil - -- env.info('Destroy laze '..index) + -- env.info('Destroy laze '..index) _tempIR = nil end @@ -6650,14 +6933,12 @@ end -- used by the timer function function ctld.timerLaseUnit(_args) - ctld.laseUnit(_args[1], _args[2], _args[3], _args[4]) end function ctld.laseUnit(_enemyUnit, _jtacUnit, _jtacGroupName, _laserCode) - --cancelLase(jtacGroupName) - ctld.logTrace("ctld.laseUnit()") + --ctld.logTrace("ctld.laseUnit()") local _spots = {} @@ -6674,7 +6955,7 @@ function ctld.laseUnit(_enemyUnit, _jtacUnit, _jtacGroupName, _laserCode) --if target speed is greater than 0, calculated using absolute value norm if math.abs(_enemySpeedVector.x) + math.abs(_enemySpeedVector.y) + math.abs(_enemySpeedVector.z) > 0 then - local CorrectionFactor = 1 --correction factor in seconds applied to the target speed components to determine the lasing spot for a direct hit on a moving vehicle + local CorrectionFactor = 1 --correction factor in seconds applied to the target speed components to determine the lasing spot for a direct hit on a moving vehicle --correct in the direction of the movement _enemyVectorUpdated.x = _enemyVectorUpdated.x + _enemySpeedVector.x * CorrectionFactor @@ -6684,11 +6965,13 @@ function ctld.laseUnit(_enemyUnit, _jtacUnit, _jtacGroupName, _laserCode) --if wind speed is greater than 0, calculated using absolute value norm if math.abs(_WindSpeedVector.x) + math.abs(_WindSpeedVector.y) + math.abs(_WindSpeedVector.z) > 0 then - local CorrectionFactor = 1.05 --correction factor in seconds applied to the wind speed components to determine the lasing spot for a direct hit in adverse conditions + local CorrectionFactor = 1.05 --correction factor in seconds applied to the wind speed components to determine the lasing spot for a direct hit in adverse conditions --correct to the opposite of the wind direction _enemyVectorUpdated.x = _enemyVectorUpdated.x - _WindSpeedVector.x * CorrectionFactor - _enemyVectorUpdated.y = _enemyVectorUpdated.y - _WindSpeedVector.y * CorrectionFactor --not sure about correcting altitude but that component is always 0 in testing + _enemyVectorUpdated.y = _enemyVectorUpdated.y - + _WindSpeedVector.y * + CorrectionFactor --not sure about correcting altitude but that component is always 0 in testing _enemyVectorUpdated.z = _enemyVectorUpdated.z - _WindSpeedVector.z * CorrectionFactor end --combination of both should result in near perfect accuracy if the bomb doesn't stall itself following fast vehicles or correcting for heavy winds, correction factors can be adjusted but should work up to 40kn of wind for vehicles moving at 90kph (beware to drop the bomb in a way to not stall it, facing which ever is larger, target speed or wind) @@ -6698,12 +6981,12 @@ function ctld.laseUnit(_enemyUnit, _jtacUnit, _jtacGroupName, _laserCode) local _oldIR = ctld.jtacIRPoints[_jtacGroupName] if _oldLase == nil or _oldIR == nil then - -- create lase local _status, _result = pcall(function() _spots['irPoint'] = Spot.createInfraRed(_jtacUnit, { x = 0, y = 2.0, z = 0 }, _enemyVectorUpdated) - _spots['laserPoint'] = Spot.createLaser(_jtacUnit, { x = 0, y = 2.0, z = 0 }, _enemyVectorUpdated, _laserCode) + _spots['laserPoint'] = Spot.createLaser(_jtacUnit, { x = 0, y = 2.0, z = 0 }, _enemyVectorUpdated, + _laserCode) return _spots end) @@ -6711,21 +6994,17 @@ function ctld.laseUnit(_enemyUnit, _jtacUnit, _jtacGroupName, _laserCode) env.error('ERROR: ' .. _result, false) else if _result.irPoint then + -- env.info(jtacUnit:getName() .. ' placed IR Pointer on '..enemyUnit:getName()) - -- env.info(jtacUnit:getName() .. ' placed IR Pointer on '..enemyUnit:getName()) - - ctld.jtacIRPoints[_jtacGroupName] = _result.irPoint --store so we can remove after + ctld.jtacIRPoints[_jtacGroupName] = _result.irPoint --store so we can remove after end if _result.laserPoint then - - -- env.info(jtacUnit:getName() .. ' is Lasing '..enemyUnit:getName()..'. CODE:'..laserCode) + -- env.info(jtacUnit:getName() .. ' is Lasing '..enemyUnit:getName()..'. CODE:'..laserCode) ctld.jtacLaserPoints[_jtacGroupName] = _result.laserPoint end end - else - -- update lase if _oldLase ~= nil then @@ -6741,7 +7020,6 @@ end -- get currently selected unit and check they're still in range function ctld.getCurrentUnit(_jtacUnit, _jtacGroupName) - local _unit = nil if ctld.jtacCurrentTargets[_jtacGroupName] ~= nil then @@ -6751,15 +7029,13 @@ function ctld.getCurrentUnit(_jtacUnit, _jtacGroupName) local _tempPoint = nil local _tempDist = nil local _tempPosition = nil - local _jtacPosition = _jtacUnit:getPosition() local _jtacPoint = _jtacUnit:getPoint() if _unit ~= nil and _unit:getLife() > 0 and _unit:isActive() == true then - -- calc distance _tempPoint = _unit:getPoint() - -- tempPosition = unit:getPosition() + -- tempPosition = unit:getPosition() _tempDist = ctld.getDistance(_unit:getPoint(), _jtacUnit:getPoint()) if _tempDist < ctld.JTAC_maxDistance then @@ -6777,20 +7053,14 @@ function ctld.getCurrentUnit(_jtacUnit, _jtacGroupName) return nil end - -- Find nearest enemy to JTAC that isn't blocked by terrain -function ctld.findNearestVisibleEnemy(_jtacUnit, _targetType,_distance) - +function ctld.findNearestVisibleEnemy(_jtacUnit, _targetType, _distance) --local startTime = os.clock() - local _maxDistance = _distance or ctld.JTAC_maxDistance - local _nearestDistance = _maxDistance - local _jtacGroupName = _jtacUnit:getGroup():getName() local _jtacPoint = _jtacUnit:getPoint() - local _coa = _jtacUnit:getCoalition() - + local _coa = _jtacUnit:getCoalition() local _offsetJTACPos = { x = _jtacPoint.x, y = _jtacPoint.y + 2.0, z = _jtacPoint.z } local _volume = { @@ -6803,27 +7073,22 @@ function ctld.findNearestVisibleEnemy(_jtacUnit, _targetType,_distance) local _unitList = {} - local _search = function(_unit, _coa) pcall(function() - if _unit ~= nil - and _unit:getLife() > 0 - and _unit:isActive() - and _unit:getCoalition() ~= _coa - and not _unit:inAir() - and not ctld.alreadyTarget(_jtacUnit,_unit) then - + and _unit:getLife() > 0 + and _unit:isActive() + and _unit:getCoalition() ~= _coa + and not _unit:inAir() + and not ctld.alreadyTarget(_jtacUnit, _unit) then local _tempPoint = _unit:getPoint() local _offsetEnemyPos = { x = _tempPoint.x, y = _tempPoint.y + 2.0, z = _tempPoint.z } - if land.isVisible(_offsetJTACPos,_offsetEnemyPos ) then - + if land.isVisible(_offsetJTACPos, _offsetEnemyPos) then local _dist = ctld.getDistance(_offsetJTACPos, _offsetEnemyPos) if _dist < _maxDistance then - table.insert(_unitList,{unit=_unit, dist=_dist}) - + table.insert(_unitList, { unit = _unit, dist = _dist }) end end end @@ -6848,8 +7113,8 @@ function ctld.findNearestVisibleEnemy(_jtacUnit, _targetType,_distance) ctld.jtacTargetsList[_jtacGroupName] = _unitList --from the units in range, build the targets list, unsorted as to keep consistency between radio menu refreshes - local _sort = function( a,b ) return a.dist < b.dist end - table.sort(_unitList,_sort) + local _sort = function(a, b) return a.dist < b.dist end + table.sort(_unitList, _sort) -- sort list -- check for hpriority @@ -6885,7 +7150,6 @@ function ctld.findNearestVisibleEnemy(_jtacUnit, _targetType,_distance) else result = _enemyUnit.unit end - elseif (_targetType == "troop" and ctld.isInfantry(_enemyUnit.unit)) or _targetType == "all" then if airdefense then return _enemyUnit.unit @@ -6896,16 +7160,13 @@ function ctld.findNearestVisibleEnemy(_jtacUnit, _targetType,_distance) end return result - end - function ctld.listNearbyEnemies(_jtacUnit) - - local _maxDistance = ctld.JTAC_maxDistance + local _maxDistance = ctld.JTAC_maxDistance local _jtacPoint = _jtacUnit:getPoint() - local _coa = _jtacUnit:getCoalition() + local _coa = _jtacUnit:getCoalition() local _offsetJTACPos = { x = _jtacPoint.x, y = _jtacPoint.y + 2.0, z = _jtacPoint.z } @@ -6920,24 +7181,20 @@ function ctld.listNearbyEnemies(_jtacUnit) local _search = function(_unit, _coa) pcall(function() - if _unit ~= nil - and _unit:getLife() > 0 - and _unit:isActive() - and _unit:getCoalition() ~= _coa - and not _unit:inAir() then - + and _unit:getLife() > 0 + and _unit:isActive() + and _unit:getCoalition() ~= _coa + and not _unit:inAir() then local _tempPoint = _unit:getPoint() local _offsetEnemyPos = { x = _tempPoint.x, y = _tempPoint.y + 2.0, z = _tempPoint.z } - if land.isVisible(_offsetJTACPos,_offsetEnemyPos ) then - + if land.isVisible(_offsetJTACPos, _offsetEnemyPos) then if not _enemies then _enemies = {} end _enemies[_unit:getTypeName()] = _unit:getTypeName() - end end end) @@ -6952,9 +7209,7 @@ end -- tests whether the unit is targeted by another JTAC function ctld.alreadyTarget(_jtacUnit, _enemyUnit) - for _, _jtacTarget in pairs(ctld.jtacCurrentTargets) do - if _jtacTarget.unitId == _enemyUnit:getID() then -- env.info("ALREADY TARGET") return true @@ -6964,29 +7219,27 @@ function ctld.alreadyTarget(_jtacUnit, _enemyUnit) return false end - -- Returns only alive units from group but the group / unit may not be active function ctld.getGroup(groupName) - local _group = Group.getByName(groupName) - local _filteredUnits = {} --contains alive units + local _filteredUnits = {} --contains alive units local _x = 1 if _group ~= nil then - ctld.logTrace(string.format("ctld.getGroup - %s - group ~= nil", ctld.p(groupName))) + --ctld.logTrace(string.format("ctld.getGroup - %s - group ~= nil", ctld.p(groupName))) if _group:isExist() then - ctld.logTrace(string.format("ctld.getGroup - %s - group:isExist()", ctld.p(groupName))) + --ctld.logTrace(string.format("ctld.getGroup - %s - group:isExist()", ctld.p(groupName))) local _groupUnits = _group:getUnits() if _groupUnits ~= nil and #_groupUnits > 0 then - ctld.logTrace(string.format("ctld.getGroup - %s - group has %s units", ctld.p(groupName), ctld.p(#_groupUnits))) + --ctld.logTrace(string.format("ctld.getGroup - %s - group has %s units", ctld.p(groupName), ctld.p(#_groupUnits))) for _x = 1, #_groupUnits do - if _groupUnits[_x]:getLife() > 0 then -- removed and _groupUnits[_x]:isExist() as isExist doesnt work on single units! + if _groupUnits[_x]:getLife() > 0 then -- removed and _groupUnits[_x]:isExist() as isExist doesnt work on single units! table.insert(_filteredUnits, _groupUnits[_x]) else - ctld.logTrace(string.format("ctld.getGroup - %s - dead unit %s", ctld.p(groupName), ctld.p(_groupUnits[_x]:getName()))) + --ctld.logTrace(string.format("ctld.getGroup - %s - dead unit %s", ctld.p(groupName), ctld.p(_groupUnits[_x]:getName()))) end end end @@ -6997,7 +7250,6 @@ function ctld.getGroup(groupName) end function ctld.getAliveGroup(_groupName) - local _group = Group.getByName(_groupName) if _group and _group:isExist() == true and #_group:getUnits() > 0 then @@ -7009,7 +7261,6 @@ end -- gets the JTAC status and displays to coalition units function ctld.getJTACStatus(_args) - --returns the status of all JTAC units unless the status of a single JTAC is asked for (by inserting it's groupName in _args[2]) local _playerUnit = ctld.getTransportUnit(_args[1]) @@ -7032,59 +7283,61 @@ function ctld.getJTACStatus(_args) local _message = ctld.i18n_translate("JTAC STATUS: \n\n") for _jtacGroupName, _jtacDetails in pairs(ctld.jtacUnits) do - --look up units - if _singleJtacGroupName == nil or (_singleJtacGroupName and _singleJtacGroupName == _jtacGroupName) then --if the status of a single JTAC or if the status of a single JTAC was asked and this is the correct JTAC we're going over in the loop - _jtacUnit = Unit.getByName(_jtacDetails.name) + if _singleJtacGroupName == nil or (_singleJtacGroupName and _singleJtacGroupName == _jtacGroupName) then --if the status of a single JTAC or if the status of a single JTAC was asked and this is the correct JTAC we're going over in the loop + _jtacUnit = Unit.getByName(_jtacDetails.name) - if _jtacUnit ~= nil and _jtacUnit:getLife() > 0 and _jtacUnit:isActive() == true and _jtacUnit:getCoalition() == _side then + if _jtacUnit ~= nil and _jtacUnit:getLife() > 0 and _jtacUnit:isActive() == true and _jtacUnit:getCoalition() == _side then + hasJTAC = true - hasJTAC = true + local _enemyUnit = ctld.getCurrentUnit(_jtacUnit, _jtacGroupName) - local _enemyUnit = ctld.getCurrentUnit(_jtacUnit, _jtacGroupName) + local _laserCode = ctld.jtacLaserPointCodes[_jtacGroupName] - local _laserCode = ctld.jtacLaserPointCodes[_jtacGroupName] + local _start = "->" .. _jtacGroupName + if (_jtacDetails.radio) then + _start = _start .. + ctld.i18n_translate(", available on %1 %2,", _jtacDetails.radio.freq, _jtacDetails.radio.mod) + end - local _start = "->" .. _jtacGroupName - if (_jtacDetails.radio) then - _start = _start .. ctld.i18n_translate(", available on %1 %2,", _jtacDetails.radio.freq, _jtacDetails.radio.mod) - end + if _laserCode == nil then + _laserCode = ctld.i18n_translate("UNKNOWN") + end - if _laserCode == nil then - _laserCode = ctld.i18n_translate("UNKNOWN") - end + if _enemyUnit ~= nil and _enemyUnit:getLife() > 0 and _enemyUnit:isActive() == true then + local action = ctld.i18n_translate(" targeting ") - if _enemyUnit ~= nil and _enemyUnit:getLife() > 0 and _enemyUnit:isActive() == true then + if ctld.jtacSelectedTarget[_jtacGroupName] == _enemyUnit:getName() then + action = ctld.i18n_translate(" targeting selected unit ") + else + if ctld.jtacSelectedTarget[_jtacGroupName] ~= 1 then + action = ctld.i18n_translate(" attempting to find selected unit, temporarily targeting ") + end + end - local action = ctld.i18n_translate(" targeting ") + if ctld.jtacSpecialOptions.standbyMode.jtacs[_jtacGroupName] then + action = action .. ctld.i18n_translate("(Laser OFF) ") + end - if ctld.jtacSelectedTarget[_jtacGroupName] == _enemyUnit:getName() then - action = ctld.i18n_translate(" targeting selected unit ") + _message = _message .. + "" .. + _start .. + action .. + _enemyUnit:getTypeName() .. " CODE: " .. _laserCode .. ctld.getPositionString(_enemyUnit) .. "\n" + + local _list = ctld.listNearbyEnemies(_jtacUnit) + + if _list then + _message = _message .. ctld.i18n_translate("Visual On: ") + + for _, _type in pairs(_list) do + _message = _message .. _type .. ", " + end + _message = _message .. "\n" + end else - if ctld.jtacSelectedTarget[_jtacGroupName] ~= 1 then - action = ctld.i18n_translate(" attempting to find selected unit, temporarily targeting ") - end - end - - if ctld.jtacSpecialOptions.standbyMode.jtacs[_jtacGroupName] then - action = action .. ctld.i18n_translate("(Laser OFF) ") - end - - _message = _message .. "" .. _start .. action .. _enemyUnit:getTypeName() .. " CODE: " .. _laserCode .. ctld.getPositionString(_enemyUnit) .. "\n" - - local _list = ctld.listNearbyEnemies(_jtacUnit) - - if _list then - _message = _message .. ctld.i18n_translate("Visual On: ") - - for _,_type in pairs(_list) do - _message = _message.._type..", " - end - _message = _message.."\n" - end - - else - _message = _message .. "" .. _start .. ctld.i18n_translate(" searching for targets %1\n", ctld.getPositionString(_jtacUnit)) + _message = _message .. + "" .. _start .. ctld.i18n_translate(" searching for targets %1\n", ctld.getPositionString(_jtacUnit)) end end end @@ -7103,23 +7356,24 @@ function ctld.setJTACTarget(_args) local targetName = _args.targetName if _jtacGroupName and targetName and ctld.jtacSelectedTarget[_jtacGroupName] and ctld.jtacTargetsList[_jtacGroupName] then - --look for the unit's (target) name in the Targets List, create the required data structure for jtacCurrentTargets and then assign it to the JTAC called _jtacGroupName for _, target in pairs(ctld.jtacTargetsList[_jtacGroupName]) do - if target then - local listedTargetUnit = target.unit local ListedTargetName = listedTargetUnit:getName() if ListedTargetName == targetName then - ctld.jtacSelectedTarget[_jtacGroupName] = targetName - ctld.jtacCurrentTargets[_jtacGroupName] = { name = targetName, unitType = listedTargetUnit:getTypeName(), unitId = listedTargetUnit:getID() } + ctld.jtacCurrentTargets[_jtacGroupName] = { name = targetName, unitType = listedTargetUnit + :getTypeName(), unitId = listedTargetUnit:getID() } - local message = _jtacGroupName .. ctld.i18n_translate(", targeting selected unit, %1",listedTargetUnit:getTypeName()) - local fullMessage = message .. ctld.i18n_translate(". CODE: %1. POSITION: %2", ctld.jtacLaserPointCodes[_jtacGroupName], ctld.getPositionString(listedTargetUnit)) - ctld.notifyCoalition(fullMessage, 10, ctld.jtacUnits[_jtacGroupName].side, ctld.jtacRadioData[_jtacGroupName], message) + local message = _jtacGroupName .. + ctld.i18n_translate(", targeting selected unit, %1", listedTargetUnit:getTypeName()) + local fullMessage = message .. + ctld.i18n_translate(". CODE: %1. POSITION: %2", ctld.jtacLaserPointCodes[_jtacGroupName], + ctld.getPositionString(listedTargetUnit)) + ctld.notifyCoalition(fullMessage, 10, ctld.jtacUnits[_jtacGroupName].side, + ctld.jtacRadioData[_jtacGroupName], message) end end end @@ -7131,11 +7385,11 @@ function ctld.setJTACTarget(_args) ctld.notifyCoalition(message, 10, ctld.jtacUnits[_jtacGroupName].side, ctld.jtacRadioData[_jtacGroupName]) if ctld.jtacSpecialOptions.laseSpotCorrections.jtacs[_jtacGroupName] then - ctld.setLaseCompensation({jtacGroupName = _jtacGroupName, value = false}) --disable laser spot corrections + ctld.setLaseCompensation({ jtacGroupName = _jtacGroupName, value = false }) --disable laser spot corrections end if ctld.jtacSpecialOptions.standbyMode.jtacs[_jtacGroupName] then - ctld.setStdbMode({jtacGroupName = _jtacGroupName, value = false}) --make the JTAC exit standby mode after either target selection or targeting selection reset + ctld.setStdbMode({ jtacGroupName = _jtacGroupName, value = false }) --make the JTAC exit standby mode after either target selection or targeting selection reset end end @@ -7147,11 +7401,11 @@ end function ctld.setSpecialOptionArgsCheck(_args) if _args then local _jtacGroupName = _args.jtacGroupName - local _value = _args.value --expected boolean - local _notOutput = _args.noOutput --expected boolean + local _value = _args.value --expected boolean + local _notOutput = _args.noOutput --expected boolean if _jtacGroupName then - return {jtacGroupName = _jtacGroupName, value = _value, noOutput = _notOutput} + return { jtacGroupName = _jtacGroupName, value = _value, noOutput = _notOutput } end end @@ -7161,7 +7415,6 @@ end function ctld.setStdbMode(_args) local parsedArgs = ctld.setSpecialOptionArgsCheck(_args) if parsedArgs then - local _jtacGroupName = parsedArgs.jtacGroupName local _value = parsedArgs.value local _noOutput = parsedArgs.noOutput @@ -7178,12 +7431,12 @@ function ctld.setStdbMode(_args) ctld.refreshJTACmenu[ctld.jtacUnits[_jtacGroupName].side] = true end end + ctld.jtacSpecialOptions.standbyMode.setter = ctld.setStdbMode function ctld.setLaseCompensation(_args) local parsedArgs = ctld.setSpecialOptionArgsCheck(_args) if parsedArgs then - local _jtacGroupName = parsedArgs.jtacGroupName local _value = parsedArgs.value local _noOutput = parsedArgs.noOutput @@ -7200,38 +7453,42 @@ function ctld.setLaseCompensation(_args) ctld.refreshJTACmenu[ctld.jtacUnits[_jtacGroupName].side] = true end end + ctld.jtacSpecialOptions.laseSpotCorrections.setter = ctld.setLaseCompensation function ctld.setSmokeOnTarget(_args) local parsedArgs = ctld.setSpecialOptionArgsCheck(_args) if parsedArgs then - local _jtacGroupName = parsedArgs.jtacGroupName local _noOutput = parsedArgs.noOutput local _enemyUnit = Unit.getByName(ctld.jtacCurrentTargets[_jtacGroupName].name) if _enemyUnit then if not _noOutput then - ctld.notifyCoalition(ctld.i18n_translate("%1, WHITE smoke deployed near target", _jtacGroupName), 10, ctld.jtacUnits[_jtacGroupName].side, ctld.jtacRadioData[_jtacGroupName]) + ctld.notifyCoalition(ctld.i18n_translate("%1, WHITE smoke deployed near target", _jtacGroupName), 10, + ctld.jtacUnits[_jtacGroupName].side, ctld.jtacRadioData[_jtacGroupName]) end local _enemyPoint = _enemyUnit:getPoint() local randomCircleDiam = 30; - trigger.action.smoke({ x = _enemyPoint.x + math.random(randomCircleDiam,-randomCircleDiam), y = _enemyPoint.y + 2.0, z = _enemyPoint.z + math.random(randomCircleDiam,-randomCircleDiam)}, 2) + trigger.action.smoke( + { x = _enemyPoint.x + math.random(randomCircleDiam, -randomCircleDiam), y = _enemyPoint.y + 2.0, z = + _enemyPoint.z + math.random(randomCircleDiam, -randomCircleDiam) }, 2) end end - end +end + ctld.jtacSpecialOptions.smokeMarker.setter = ctld.setSmokeOnTarget function ctld.setJTAC9Line(_args) local parsedArgs = ctld.setSpecialOptionArgsCheck(_args) if parsedArgs then - local _jtacGroupName = parsedArgs.jtacGroupName - ctld.getJTACStatus({nil, _jtacGroupName}) + ctld.getJTACStatus({ nil, _jtacGroupName }) end end + ctld.jtacSpecialOptions._9Line.setter = ctld.setJTAC9Line function ctld.setGrpROE(_grp, _ROE) @@ -7244,16 +7501,15 @@ function ctld.setGrpROE(_grp, _ROE) _ROE = AI.Option.Ground.val.ROE.OPEN_FIRE end - if _grp and _grp:isExist() == true and #_grp:getUnits() > 0 then -- check if the group truly exists + if _grp and _grp:isExist() == true and #_grp:getUnits() > 0 then -- check if the group truly exists local _controller = _grp:getController(); Controller.setOption(_controller, AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.AUTO) Controller.setOption(_controller, AI.Option.Ground.id.ROE, _ROE) - _controller:setTask(_grp) + --_controller:setTask(_grp) -- FG 250510 this line seems to be a bug end end function ctld.isInfantry(_unit) - local _typeName = _unit:getTypeName() --type coerce tostring @@ -7272,7 +7528,6 @@ end -- assume anything that isnt soldier is vehicle function ctld.isVehicle(_unit) - if ctld.isInfantry(_unit) then return false end @@ -7283,10 +7538,9 @@ end -- The entered value can range from 1111 - 1788, -- -- but the first digit of the series must be a 1 or 2 -- -- and the last three digits must be between 1 and 8. --- The range used to be bugged so its not 1 - 8 but 0 - 7. +-- The range used to be bugged so its not 1 - 8 but 0 - 7. -- function below will use the range 1-7 just incase function ctld.generateLaserCode() - ctld.jtacGeneratedLaserCodes = {} -- generate list of laser codes @@ -7295,15 +7549,12 @@ function ctld.generateLaserCode() local _count = 1 while _code < 1777 and _count < 30 do - while true do - _code = _code + 1 if not ctld.containsDigit(_code, 8) - and not ctld.containsDigit(_code, 9) - and not ctld.containsDigit(_code, 0) then - + and not ctld.containsDigit(_code, 9) + and not ctld.containsDigit(_code, 0) then table.insert(ctld.jtacGeneratedLaserCodes, _code) --env.info(_code.." Code") @@ -7315,12 +7566,10 @@ function ctld.generateLaserCode() end function ctld.containsDigit(_number, _numberToFind) - local _thisNumber = _number local _thisDigit = 0 while _thisNumber ~= 0 do - _thisDigit = _thisNumber % 10 _thisNumber = math.floor(_thisNumber / 10) @@ -7336,12 +7585,11 @@ end -- 400 - 850 in 10 KHz -- 850 - 1250 in 50 KHz function ctld.generateVHFrequencies() - --ignore list --list of all frequencies in KHZ that could conflict with -- 191 - 1290 KHz, beacon range local _skipFrequencies = { - 745, --Astrahan + 745, --Astrahan 381, 384, 300.50, @@ -7382,7 +7630,7 @@ function ctld.generateVHFrequencies() 866, 907, 309.5, 822, 515, 470, 342, 1182, 309.5, 720, 528, 337, 312.5, 830, 740, 309.5, 641, 312, 722, 682, 1050, 1116, 935, 1000, 430, 577, - 326 -- Nevada + 326 -- Nevada } ctld.freeVHFFrequencies = {} @@ -7390,7 +7638,6 @@ function ctld.generateVHFrequencies() -- first range while _start < 400000 do - -- skip existing NDB frequencies local _found = false for _, value in pairs(_skipFrequencies) do @@ -7411,7 +7658,6 @@ function ctld.generateVHFrequencies() _start = 400000 -- second range while _start < 850000 do - -- skip existing NDB frequencies local _found = false for _, value in pairs(_skipFrequencies) do @@ -7432,7 +7678,6 @@ function ctld.generateVHFrequencies() _start = 850000 -- third range while _start <= 1250000 do - -- skip existing NDB frequencies local _found = false for _, value in pairs(_skipFrequencies) do @@ -7452,7 +7697,6 @@ end -- 220 - 399 MHZ, increments of 0.5MHZ function ctld.generateUHFrequencies() - ctld.freeUHFFrequencies = {} local _start = 220000000 @@ -7462,28 +7706,26 @@ function ctld.generateUHFrequencies() end end - -- 220 - 399 MHZ, increments of 0.5MHZ --- -- first digit 3-7MHz --- -- second digit 0-5KHz --- -- third digit 0-9 --- -- fourth digit 0 or 5 --- -- times by 10000 +-- -- first digit 3-7MHz +-- -- second digit 0-5KHz +-- -- third digit 0-9 +-- -- fourth digit 0 or 5 +-- -- times by 10000 -- function ctld.generateFMFrequencies() - ctld.freeFMFrequencies = {} local _start = 220000000 while _start < 399000000 do - _start = _start + 500000 end for _first = 3, 7 do for _second = 0, 5 do for _third = 0, 9 do - local _frequency = ((100 * _first) + (10 * _second) + _third) * 100000 --extra 0 because we didnt bother with 4th digit + local _frequency = ((100 * _first) + (10 * _second) + _third) * + 100000 --extra 0 because we didnt bother with 4th digit table.insert(ctld.freeFMFrequencies, _frequency) end end @@ -7499,11 +7741,204 @@ function ctld.getPositionString(_unit) local _latLngStr = mist.tostringLL(_lat, _lon, 3, ctld.location_DMS) local _mgrsString = mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(_unit:getPosition().p)), 5) local _TargetAlti = land.getHeight(mist.utils.makeVec2(_unit:getPoint())) - return " @ " .. _latLngStr .. " - MGRS " .. _mgrsString .. " - ALTI: " .. mist.utils.round(_TargetAlti, 0) .. " m / " .. mist.utils.round(_TargetAlti/0.3048, 0) .. " ft" + return " @ " .. + _latLngStr .. + " - MGRS " .. + _mgrsString .. + " - ALTI: " .. mist.utils.round(_TargetAlti, 0) .. " m / " .. mist.utils.round(_TargetAlti / 0.3048, 0) .. " ft" +end +--********************************************************************** +-- Automaticaly put in orbit over his target a flying JTAC +-- +-- Objective : This script put in orbit each flying JTAC over his detected target +-- Associated with CTLD/JTAC function, you can assign a fly route to the JTAC (a drone for example), +-- this one follow it, and start orbiting when he detects a target. +-- As soon as it don't detect a target, it restart following its initial route at the nearest waypoint +-- Use : In mission editor: +-- 0> Set ctld.enableAutoOrbitingFlyingJtacOnTarget = true +-- 1> Load MIST + CTLD +-- 2> Create a TRIGGER (once) at Time sup à 6, and a ACTION.EXECUTE SCRIPT : +-- ctld.JTACAutoLase("gdrone1", 1688,false) -- défine group "gdrone1" as a JTAC +------------------------------------------------------------------------------------ +ctld.JTACInRoute = {} -- for each JTAC in route, indicates the time of the run +ctld.OrbitInUse = {} -- for each Orbit group in use, indicates the time of the run +ctld.enableAutoOrbitingFlyingJtacOnTarget = true -- if true activate the AutoOrbitingFlyinfJtacOnTarget function for all flying JTACS +------------------------------------------------------------------------------------ +-- Automatic JTAC orbit on target detect +function ctld.TreatOrbitJTAC(params, t) + if t == nil then t = timer.getTime() end + + for k,v in pairs(ctld.jtacUnits) do -- vérify state of each active JTAC + if ctld.isFlyingJtac(k) then + if ctld.JTACInRoute[k] == nil and ctld.OrbitInUse[k] == nil then -- if JTAC is in route + ctld.JTACInRoute[k] = timer.getTime() -- update time of the last run + end + + if ctld.jtacCurrentTargets[k] ~= nil then -- if target lased by JTAC + local droneAlti = Unit.getByName(k):getPoint().y + if ctld.OrbitInUse[k] == nil then -- if JTAC is not in orbit => start orbiting and update start time + ctld.StartOrbitGroup(k, ctld.jtacCurrentTargets[k].name, droneAlti, 100) -- do orbit JTAC + ctld.OrbitInUse[k] = timer.getTime() -- update time of the last orbit run + ctld.JTACInRoute[k] = nil -- JTAC is in orbit => reset the route time + else -- JTAC already orbiting => update coord for following the target mouvements each 60" + if timer.getTime() > (ctld.OrbitInUse[k] + 60) then -- each 60" update orbit coord + ctld.StartOrbitGroup(k, ctld.jtacCurrentTargets[k].name, droneAlti, 100) -- do orbit JTAC + ctld.OrbitInUse[k] = timer.getTime() -- update time of the last orbit run + end + end + else -- if JTAC have no target + if ctld.InOrbitList(k) == true then -- JTAC orbiting, without target => stop orbit + --Unit.getByName(k):getController():popTask() -- stop orbiting JTAC Task => return to route + ctld.backToRoute(k) -- return to route from the nearest WP + ctld.OrbitInUse[k] = nil -- Reset orbit + ctld.JTACInRoute[k] = timer.getTime() -- update time of the last start inroute + end + end + end + end + return t + 3 --reschedule in 3" +end +------------------------------------------------------------------------------------ +-- Make orbit the group "_grpName", on target "_unitTargetName". _alti in meters, speed in km/h +function ctld.StartOrbitGroup(_jtacUnitName, _unitTargetName, _alti, _speed) + if (Unit.getByName(_unitTargetName) ~= nil) and (Unit.getByName(_jtacUnitName) ~= nil) then -- si target unit and JTAC group exist + local orbit = { id = 'Orbit', + params = {pattern = 'Circle', + point = mist.utils.makeVec2(mist.getAvgPos(mist.makeUnitTable({_unitTargetName}))), + speed = _speed, + altitude = _alti + } + } + local jtacGroupName = Unit.getByName(_jtacUnitName):getGroup():getName() + Unit.getByName(_jtacUnitName):getController():popTask() -- stop current Task + Group.getByName(jtacGroupName):getController():pushTask(orbit) + end +end +------------------------------------------------------------------------------------------- +-- test if one unitName already is targeted by a JTAC +function ctld.InOrbitList(_grpName) + for k, v in pairs(ctld.OrbitInUse) do -- for each orbit in use + if k == _grpName then + return true + end + end + return false +end +------------------------------------------------------------------------------------------- +-- return the WayPoint number (on the JTAC route) the most near from the target +function ctld.getNearestWP(_referenceUnitName) + local WP = 0 + local memoDist = nil -- Lower distance checked + local refGroupName = Unit.getByName(_referenceUnitName):getGroup():getName() + local JTACRoute = mist.getGroupRoute(refGroupName, true) -- get the initial editor route of the current group + if Unit.getByName(_referenceUnitName) ~= nil then --JTAC et unit must exist + for i=1, #JTACRoute do + local ptWP = {x = JTACRoute[i].x, y = JTACRoute[i].y} + local ptRef = mist.utils.makeVec2(Unit.getByName(_referenceUnitName):getPoint()) + local dist = mist.utils.get2DDist(ptRef, ptWP) -- distance between 2 points + if memoDist == nil then + memoDist = dist + WP = i + elseif dist < memoDist then + memoDist = dist + WP = i + end + end + end + return WP +end +---------------------------------------------------------------------------- +-- Modify the route deleting all the WP before "firstWP" param, for aligne the orbit on the nearest WP of the target +function ctld.backToRoute(_jtacUnitName) + local jtacGroupName = Unit.getByName(_jtacUnitName):getGroup():getName() + --local JTACRoute = mist.getGroupRoute(jtacGroupName, true) -- get the initial editor route of the current group + local JTACRoute = mist.utils.deepCopy(mist.getGroupRoute(jtacGroupName, true)) -- get the initial editor route of the current group + local newJTACRoute = ctld.adjustRoute(JTACRoute, ctld.getNearestWP(_jtacUnitName)) + + local Mission = {} + Mission = { id = 'Mission', params = {route = {points = newJTACRoute}}} + + -- unactive orbit mode if it's on + if ctld.InOrbitList(_jtacUnitName) == true then -- if JTAC orbiting => stop it + ctld.OrbitInUse[_jtacUnitName] = nil + end + Unit.getByName(_jtacUnitName):getController():setTask(Mission) -- submit the new route + return Mission +end +---------------------------------------------------------------------------- +function ctld.adjustRoute(_initialRouteTable, _firstWpOfNewRoute) -- create a route based on inital one, starting at _firstWpOfNewRoute WP + if _firstWpOfNewRoute >=1 then + -- if the last WP switch to the first this cycle is recreated + local adjustedRoute = {} + local mappingWP = {} + local idx = 1 + for i =_firstWpOfNewRoute, #_initialRouteTable do -- load each WP route starting from _firstWpOfNewRoute to end + adjustedRoute[idx] = _initialRouteTable[i] + mappingWP[i] = idx + ctld.logDebug("ctld.adjustRoute - mappingWP[%s]=[%s]", ctld.p(i), ctld.p(idx)) + idx = idx + 1 + end + for i=1, _firstWpOfNewRoute - 1 do -- load each WP route starting from 1 to _firstWpOfNewRoute-1 + adjustedRoute[idx] = _initialRouteTable[i] + mappingWP[i] = idx + ctld.logDebug("ctld.adjustRoute - mappingWP[%s]=[%s]", ctld.p(i), ctld.p(idx)) + idx = idx + 1 + end + + -- apply offset (_firstWpOfNewRoute) to SwitchWaypoint tasks + local lastWpAsAlreadySwitchWaypoint = false + for idx = 1, #adjustedRoute do + for j=1, #adjustedRoute[idx].task.params.tasks do + if adjustedRoute[idx].task.params.tasks[j].id ~= "ControlledTask" then + if adjustedRoute[idx].task.params.tasks[j].params.action.id == "SwitchWaypoint" then + local goToWaypointIndex = adjustedRoute[idx].task.params.tasks[j].params.action.params.goToWaypointIndex + adjustedRoute[idx].task.params.tasks[j].params.action.params.fromWaypointIndex = idx + adjustedRoute[idx].task.params.tasks[j].params.action.params.goToWaypointIndex = mappingWP[goToWaypointIndex] + if idx == #adjustedRoute then + lastWpAsAlreadySwitchWaypoint = true + end + end + else -- for "ControlledTask" + if adjustedRoute[idx].task.params.tasks[j].params.task.params.action.id == "SwitchWaypoint" then + local goToWaypointIndex = adjustedRoute[idx].task.params.tasks[j].params.task.params.action.params.goToWaypointIndex + adjustedRoute[idx].task.params.tasks[j].params.task.params.action.params.fromWaypointIndex = idx + adjustedRoute[idx].task.params.tasks[j].params.task.params.action.params.goToWaypointIndex = mappingWP[goToWaypointIndex] + if idx == #adjustedRoute then + lastWpAsAlreadySwitchWaypoint = true + end + end + end + end + end + if lastWpAsAlreadySwitchWaypoint == false then + local newTaskIdx = #adjustedRoute[#adjustedRoute].task.params.tasks + 1 + adjustedRoute[#adjustedRoute].task.params.tasks[newTaskIdx] = {number = newTaskIdx, + auto = false, + enabled = true, + id = "WrappedAction", + params = {action = {}} + } + adjustedRoute[#adjustedRoute].task.params.tasks[newTaskIdx].params.action.id = "SwitchWaypoint" + adjustedRoute[#adjustedRoute].task.params.tasks[newTaskIdx].params.action.params = {fromWaypointIndex = #_initialRouteTable, + goToWaypointIndex = 1 } + end + --ctld.logDebug("ctld.adjustRoute - adjustedRoute = [%s]", ctld.p(adjustedRoute)) + return adjustedRoute + end + return nil +end +---------------------------------------------------------------------------- +function ctld.isFlyingJtac(_jtacUnitName) + if Unit.getByName(_jtacUnitName) then + if Unit.getByName(_jtacUnitName):getCategoryEx() == 0 then -- it's an airplane JTAC + return true + end + end + return false end --********************************************************************** --- RECOGNITION SUPPORT FUNCTIONS +-- RECOGNITION SUPPORT FUNCTIONS -- Shows/remove/refresh marks in F10 map on targets in LOS of a unit passed in params --------------------------------------------------------------------- -- examples --------------------------------------------------------- @@ -7511,22 +7946,22 @@ end --ctld.reconRemoveTargetsInLosOnF10Map(Unit.getByName("uh2-1")) --ctld.reconShowTargetsInLosOnF10Map(Unit.getByName("uh2-1"), 2000, 200) ---------------------------------------------------------------------- ---if ctld == nil then ctld = {} end +--if ctld == nil then ctld = {} end if ctld.lastMarkId == nil then ctld.lastMarkId = 0 end -- ***************** RECON CONFIGURATION ***************** -ctld.reconF10Menu = true -- enables F10 RECON menu -ctld.reconMenuName = ctld.i18n_translate("RECON") --name of the CTLD JTAC radio menu -ctld.reconRadioAdded = {} --stores the groups that have had the radio menu added -ctld.reconLosSearchRadius = 2000 -- search radius in meters -ctld.reconLosMarkRadius = 100 -- mark radius dimension in meters -ctld.reconAutoRefreshLosTargetMarks = true -- if true recon LOS marks are automaticaly refreshed on F10 map +ctld.reconF10Menu = true -- enables F10 RECON menu +ctld.reconMenuName = ctld.i18n_translate("RECON") --name of the CTLD JTAC radio menu +ctld.reconRadioAdded = {} --stores the groups that have had the radio menu added +ctld.reconLosSearchRadius = 2000 -- search radius in meters +ctld.reconLosMarkRadius = 100 -- mark radius dimension in meters +ctld.reconAutoRefreshLosTargetMarks = true -- if true recon LOS marks are automaticaly refreshed on F10 map ctld.reconLastScheduleIdAutoRefresh = 0 ---- F10 RECON Menus ------------------------------------------------------------------ -function ctld.addReconRadioCommand(_side) -- _side = 1 or 2 (red or blue) +function ctld.addReconRadioCommand(_side) -- _side = 1 or 2 (red or blue) if ctld.reconF10Menu then if _side == 1 or _side == 2 then local _players = coalition.getPlayers(_side) @@ -7535,7 +7970,7 @@ function ctld.addReconRadioCommand(_side) -- _side = 1 or 2 (red or blue) local _groupId = ctld.getGroupId(_playerUnit) if _groupId then if ctld.reconRadioAdded[tostring(_groupId)] == nil then - ctld.logDebug("ctld.addReconRadioCommand - adding RECON radio menu for unit [%s]", ctld.p(_playerUnit:getName())) + --ctld.logDebug("ctld.addReconRadioCommand - adding RECON radio menu for unit [%s]", ctld.p(_playerUnit:getName())) local RECONpath = missionCommands.addSubMenuForGroup(_groupId, ctld.reconMenuName) missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Show targets in LOS (refresh)"), RECONpath, @@ -7546,7 +7981,8 @@ function ctld.addReconRadioCommand(_side) -- _side = 1 or 2 (red or blue) _markRadius = ctld.reconLosMarkRadius, _boolRemove = true }) - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Hide targets in LOS"), RECONpath, ctld.reconRemoveTargetsInLosOnF10Map, _playerUnit) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Hide targets in LOS"), + RECONpath, ctld.reconRemoveTargetsInLosOnF10Map, _playerUnit) if ctld.reconAutoRefreshLosTargetMarks then missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("STOP autoRefresh targets in LOS"), RECONpath, @@ -7567,7 +8003,7 @@ function ctld.addReconRadioCommand(_side) -- _side = 1 or 2 (red or blue) true ) end - ctld.reconRadioAdded[tostring(_groupId)] = timer.getTime() --fetch the time to check for a regular refresh + ctld.reconRadioAdded[tostring(_groupId)] = timer.getTime() --fetch the time to check for a regular refresh end end end @@ -7575,112 +8011,131 @@ function ctld.addReconRadioCommand(_side) -- _side = 1 or 2 (red or blue) end end end + -------------------------------------------------------------------- function ctld.reconStopAutorefreshTargetsInLosOnF10Map(_groupId, _playerUnit, _searchRadius, _markRadius, _boolRemove) ctld.reconAutoRefreshLosTargetMarks = false if ctld.reconLastScheduleIdAutoRefresh ~= 0 then - timer.removeFunction(ctld.reconLastScheduleIdAutoRefresh) -- reset last schedule + timer.removeFunction(ctld.reconLastScheduleIdAutoRefresh) -- reset last schedule end ctld.reconRemoveTargetsInLosOnF10Map(_playerUnit) - missionCommands.removeItemForGroup(_groupId, {ctld.reconMenuName, ctld.i18n_translate("STOP autoRefresh targets in LOS")}) - missionCommands.addCommandForGroup( _groupId, ctld.i18n_translate("START autoRefresh targets in LOS"), {ctld.reconMenuName}, - ctld.reconStartAutorefreshTargetsInLosOnF10Map, - _groupId, - _playerUnit, - _searchRadius, - _markRadius, - _boolRemove) + missionCommands.removeItemForGroup(_groupId, + { ctld.reconMenuName, ctld.i18n_translate("STOP autoRefresh targets in LOS") }) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("START autoRefresh targets in LOS"), + { ctld.reconMenuName }, + ctld.reconStartAutorefreshTargetsInLosOnF10Map, + _groupId, + _playerUnit, + _searchRadius, + _markRadius, + _boolRemove) end + -------------------------------------------------------------------- function ctld.reconStartAutorefreshTargetsInLosOnF10Map(_groupId, _playerUnit, _searchRadius, _markRadius, _boolRemove) ctld.reconAutoRefreshLosTargetMarks = true - ctld.reconRefreshTargetsInLosOnF10Map({ _groupId = _groupId, - _playerUnit = _playerUnit, - _searchRadius = _searchRadius or ctld.reconLosSearchRadius, - _markRadius = _markRadius or ctld.reconLosMarkRadius, - _boolRemove = _boolRemove or true}, - timer.getTime()) - missionCommands.removeItemForGroup( _groupId, {ctld.reconMenuName, ctld.i18n_translate("START autoRefresh targets in LOS")}) - missionCommands.addCommandForGroup( _groupId, ctld.i18n_translate("STOP autoRefresh targets in LOS"), {ctld.reconMenuName}, - ctld.reconStopAutorefreshTargetsInLosOnF10Map, - _groupId, - _playerUnit, - _searchRadius, - _markRadius, - _boolRemove) + ctld.reconRefreshTargetsInLosOnF10Map({ + _groupId = _groupId, + _playerUnit = _playerUnit, + _searchRadius = _searchRadius or ctld.reconLosSearchRadius, + _markRadius = _markRadius or ctld.reconLosMarkRadius, + _boolRemove = _boolRemove or true + }, + timer.getTime()) + missionCommands.removeItemForGroup(_groupId, + { ctld.reconMenuName, ctld.i18n_translate("START autoRefresh targets in LOS") }) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("STOP autoRefresh targets in LOS"), + { ctld.reconMenuName }, + ctld.reconStopAutorefreshTargetsInLosOnF10Map, + _groupId, + _playerUnit, + _searchRadius, + _markRadius, + _boolRemove) end + -------------------------------------------------------------------- -function ctld.reconShowTargetsInLosOnF10Map(_playerUnit, _searchRadius, _markRadius) -- _groupId targeting - -- _searchRadius and _markRadius in meters +function ctld.reconShowTargetsInLosOnF10Map(_playerUnit, _searchRadius, _markRadius) -- _groupId targeting + -- _searchRadius and _markRadius in meters if _playerUnit then local TargetsInLOS = {} local enemyColor = "red" - local color = {1, 0, 0, 0.2} -- red + local color = { 1, 0, 0, 0.2 } -- red if _playerUnit:getCoalition() == 1 then enemyColor = "blue" - color = {51/255, 51/255, 1, 0.2} -- blue + color = { 51 / 255, 51 / 255, 1, 0.2 } -- blue end - local t = mist.getUnitsLOS({_playerUnit:getName()}, 180, mist.makeUnitTable({'['..enemyColor..'][vehicle]'}),180, _searchRadius) + local t = mist.getUnitsLOS({ _playerUnit:getName() }, 180, mist.makeUnitTable({ '[' .. enemyColor .. '][vehicle]' }), + 180, _searchRadius) local MarkIds = {} if t then - for i=1, #t do -- for each unit having los on enemies - for j=1, #t[i].vis do -- for each enemy unit in los - local targetPoint = t[i].vis[j]:getPoint() -- point of each target on LOS - ctld.lastMarkId = ctld.lastMarkId + 1 - trigger.action.circleToAll(_playerUnit:getCoalition(), ctld.lastMarkId, targetPoint, _markRadius , color, color, 1, false, nil) - MarkIds[#MarkIds+1] = ctld.lastMarkId - TargetsInLOS[#TargetsInLOS+1] = { targetObject = t[i].vis[j]:getName(), - targetTypeName = t[i].vis[j]:getTypeName(), - targetPoint = targetPoint} + for i = 1, #t do -- for each unit having los on enemies + for j = 1, #t[i].vis do -- for each enemy unit in los + local targetPoint = t[i].vis[j]:getPoint() -- point of each target on LOS + ctld.lastMarkId = ctld.lastMarkId + 1 + trigger.action.circleToAll(_playerUnit:getCoalition(), ctld.lastMarkId, targetPoint, _markRadius, + color, color, 1, false, nil) + MarkIds[#MarkIds + 1] = ctld.lastMarkId + TargetsInLOS[#TargetsInLOS + 1] = { + targetObject = t[i].vis[j]:getName(), + targetTypeName = t[i].vis[j]:getTypeName(), + targetPoint = targetPoint + } end end end - mist.DBs.humansByName[_playerUnit:getName()].losMarkIds = MarkIds -- store list of marksIds generated and showed on F10 map + mist.DBs.humansByName[_playerUnit:getName()].losMarkIds = + MarkIds -- store list of marksIds generated and showed on F10 map return TargetsInLOS else return nil end end + --------------------------------------------------------- function ctld.reconRemoveTargetsInLosOnF10Map(_playerUnit) local unitName = _playerUnit:getName() if mist.DBs.humansByName[unitName].losMarkIds then - for i=1, #mist.DBs.humansByName[unitName].losMarkIds do -- for each unit having los on enemies + for i = 1, #mist.DBs.humansByName[unitName].losMarkIds do -- for each unit having los on enemies trigger.action.removeMark(mist.DBs.humansByName[unitName].losMarkIds[i]) end mist.DBs.humansByName[unitName].losMarkIds = nil end end + --------------------------------------------------------- -function ctld.reconRefreshTargetsInLosOnF10Map(_params, _t) -- _params._playerUnit targeting - -- _params._searchRadius and _params._markRadius in meters - -- _params._boolRemove = true to remove previous marksIds +function ctld.reconRefreshTargetsInLosOnF10Map(_params, _t) -- _params._playerUnit targeting + -- _params._searchRadius and _params._markRadius in meters + -- _params._boolRemove = true to remove previous marksIds if _t == nil then _t = timer.getTime() end - if ctld.reconAutoRefreshLosTargetMarks then -- to follow mobile enemy targets - ctld.reconLastScheduleIdAutoRefresh = timer.scheduleFunction(ctld.reconRefreshTargetsInLosOnF10Map, {_groupId = _params._groupId, - _playerUnit = _params._playerUnit, - _searchRadius = _params._searchRadius, - _markRadius = _params._markRadius, - _boolRemove = _params._boolRemove - }, - timer.getTime() + 10) + if ctld.reconAutoRefreshLosTargetMarks then -- to follow mobile enemy targets + ctld.reconLastScheduleIdAutoRefresh = timer.scheduleFunction(ctld.reconRefreshTargetsInLosOnF10Map, + { + _groupId = _params._groupId, + _playerUnit = _params._playerUnit, + _searchRadius = _params._searchRadius, + _markRadius = _params._markRadius, + _boolRemove = _params._boolRemove + }, + timer.getTime() + 10) end if _params._boolRemove == true then ctld.reconRemoveTargetsInLosOnF10Map(_params._playerUnit) end - return ctld.reconShowTargetsInLosOnF10Map(_params._playerUnit, _params._searchRadius, _params._markRadius) -- returns TargetsInLOS table + return ctld.reconShowTargetsInLosOnF10Map(_params._playerUnit, _params._searchRadius, _params._markRadius) -- returns TargetsInLOS table end + --- test ------------------------------------------------------ ---local unitName = "uh2-1" --"uh1-1" --"uh2-1" +--local unitName = "uh2-1" --"uh1-1" --"uh2-1" --ctld.reconShowTargetsInLosOnF10Map(Unit.getByName(unitName),2000,200) @@ -7690,34 +8145,35 @@ end function ctld.initialize() ctld.logInfo(string.format("Initializing version %s", ctld.Version)) - assert(mist ~= nil, "\n\n** HEY MISSION-DESIGNER! **\n\nMiST has not been loaded!\n\nMake sure MiST 3.6 or higher is running\n*before* running this script!\n") + assert(mist ~= nil, + "\n\n** HEY MISSION-DESIGNER! **\n\nMiST has not been loaded!\n\nMake sure MiST 3.6 or higher is running\n*before* running this script!\n") ctld.addedTo = {} - ctld.spawnedCratesRED = {} -- use to store crates that have been spawned - ctld.spawnedCratesBLUE = {} -- use to store crates that have been spawned + ctld.spawnedCratesRED = {} -- use to store crates that have been spawned + ctld.spawnedCratesBLUE = {} -- use to store crates that have been spawned - ctld.droppedTroopsRED = {} -- stores dropped troop groups - ctld.droppedTroopsBLUE = {} -- stores dropped troop groups + ctld.droppedTroopsRED = {} -- stores dropped troop groups + ctld.droppedTroopsBLUE = {} -- stores dropped troop groups - ctld.droppedVehiclesRED = {} -- stores vehicle groups for c-130 / hercules - ctld.droppedVehiclesBLUE = {} -- stores vehicle groups for c-130 / hercules + ctld.droppedVehiclesRED = {} -- stores vehicle groups for c-130 / hercules + ctld.droppedVehiclesBLUE = {} -- stores vehicle groups for c-130 / hercules ctld.inTransitTroops = {} ctld.inTransitFOBCrates = {} - ctld.inTransitSlingLoadCrates = {} -- stores crates that are being transported by helicopters for alternative to real slingload + ctld.inTransitSlingLoadCrates = {} -- stores crates that are being transported by helicopters for alternative to real slingload ctld.droppedFOBCratesRED = {} ctld.droppedFOBCratesBLUE = {} - ctld.builtFOBS = {} -- stores fully built fobs + ctld.builtFOBS = {} -- stores fully built fobs - ctld.completeAASystems = {} -- stores complete spawned groups from multiple crates + ctld.completeAASystems = {} -- stores complete spawned groups from multiple crates - ctld.fobBeacons = {} -- stores FOB radio beacon details, refreshed every 60 seconds + ctld.fobBeacons = {} -- stores FOB radio beacon details, refreshed every 60 seconds - ctld.deployedRadioBeacons = {} -- stores details of deployed radio beacons + ctld.deployedRadioBeacons = {} -- stores details of deployed radio beacons ctld.beaconCount = 1 @@ -7732,36 +8188,35 @@ function ctld.initialize() --used to lookup what the crate will contain ctld.crateLookupTable = {} - ctld.extractZones = {} -- stored extract zones + ctld.extractZones = {} -- stored extract zones - ctld.missionEditorCargoCrates = {} --crates added by mission editor for triggering cratesinzone - ctld.hoverStatus = {} -- tracks status of a helis hover above a crate - - ctld.callbacks = {} -- function callback + ctld.missionEditorCargoCrates = {} -- crates added by mission editor for triggering cratesinzone + ctld.hoverStatus = {} -- tracks status of a helis hover above a crate + ctld.callbacks = {} -- function callback + ctld.vehicleCommandsPath = {} -- memory of F10 c=CTLD menu path bay unitNames -- Remove intransit troops when heli / cargo plane dies --ctld.eventHandler = {} --function ctld.eventHandler:onEvent(_event) -- - -- if _event == nil or _event.initiator == nil then - -- env.info("CTLD null event") - -- elseif _event.id == 9 then - -- -- Pilot dead - -- ctld.inTransitTroops[_event.initiator:getName()] = nil + -- if _event == nil or _event.initiator == nil then + -- env.info("CTLD null event") + -- elseif _event.id == 9 then + -- -- Pilot dead + -- ctld.inTransitTroops[_event.initiator:getName()] = nil -- - -- elseif world.event.S_EVENT_EJECTION == _event.id or _event.id == 8 then - -- -- env.info("Event unit - Pilot Ejected or Unit Dead") - -- ctld.inTransitTroops[_event.initiator:getName()] = nil + -- elseif world.event.S_EVENT_EJECTION == _event.id or _event.id == 8 then + -- -- env.info("Event unit - Pilot Ejected or Unit Dead") + -- ctld.inTransitTroops[_event.initiator:getName()] = nil -- - -- -- env.info(_event.initiator:getName()) - -- end + -- -- env.info(_event.initiator:getName()) + -- end -- --end -- create crate lookup table for _subMenuName, _crates in pairs(ctld.spawnableCrates) do - for _, _crate in pairs(_crates) do -- convert number to string otherwise we'll have a pointless giant -- table. String means 'hashmap' so it will only contain the right number of elements @@ -7779,7 +8234,6 @@ function ctld.initialize() --sort out pickup zones for _, _zone in pairs(ctld.pickupZones) do - local _zoneName = _zone[1] local _zoneColor = _zone[2] local _zoneActive = _zone[4] @@ -7795,7 +8249,7 @@ function ctld.initialize() elseif _zoneColor == "blue" then _zone[2] = trigger.smokeColor.Blue else - _zone[2] = -1 -- no smoke colour + _zone[2] = -1 -- no smoke colour end -- add in counter for troops or units @@ -7813,7 +8267,6 @@ function ctld.initialize() --sort out dropoff zones for _, _zone in pairs(ctld.dropOffZones) do - local _zoneColor = _zone[2] if _zoneColor == "green" then @@ -7827,7 +8280,7 @@ function ctld.initialize() elseif _zoneColor == "blue" then _zone[2] = trigger.smokeColor.Blue else - _zone[2] = -1 -- no smoke colour + _zone[2] = -1 -- no smoke colour end --mark as active for refresh smoke logic to work @@ -7836,7 +8289,6 @@ function ctld.initialize() --sort out waypoint zones for _, _zone in pairs(ctld.wpZones) do - local _zoneColor = _zone[2] if _zoneColor == "green" then @@ -7850,12 +8302,12 @@ function ctld.initialize() elseif _zoneColor == "blue" then _zone[2] = trigger.smokeColor.Blue else - _zone[2] = -1 -- no smoke colour + _zone[2] = -1 -- no smoke colour end --mark as active for refresh smoke logic to work -- change active to 1 / 0 - if _zone[3] == "yes" then + if _zone[3] == "yes" then _zone[3] = 1 else _zone[3] = 0 @@ -7864,11 +8316,9 @@ function ctld.initialize() -- Sort out extractable groups for _, _groupName in pairs(ctld.extractableGroups) do - local _group = Group.getByName(_groupName) if _group ~= nil then - if _group:getCoalition() == 1 then table.insert(ctld.droppedTroopsRED, _group:getName()) else @@ -7882,7 +8332,7 @@ function ctld.initialize() if ctld.allowRandomAiTeamPickups == true then ctld.redTeams = {} ctld.blueTeams = {} - for _,_loadGroup in pairs(ctld.loadableGroups) do + for _, _loadGroup in pairs(ctld.loadableGroups) do if not _loadGroup.side then table.insert(ctld.redTeams, _) table.insert(ctld.blueTeams, _) @@ -7896,8 +8346,7 @@ function ctld.initialize() -- add total count - for _,_loadGroup in pairs(ctld.loadableGroups) do - + for _, _loadGroup in pairs(ctld.loadableGroups) do _loadGroup.total = 0 if _loadGroup.aa then _loadGroup.total = _loadGroup.aa + _loadGroup.total @@ -7919,26 +8368,32 @@ function ctld.initialize() if _loadGroup.mortar then _loadGroup.total = _loadGroup.mortar + _loadGroup.total end - end - + --************************************************************************************************* -- Scheduled functions (run cyclically) -- but hold execution for a second so we can override parts - timer.scheduleFunction(ctld.checkAIStatus, nil, timer.getTime() + 1) timer.scheduleFunction(ctld.checkTransportStatus, nil, timer.getTime() + 5) timer.scheduleFunction(function() - timer.scheduleFunction(ctld.refreshRadioBeacons, nil, timer.getTime() + 5) timer.scheduleFunction(ctld.refreshSmoke, nil, timer.getTime() + 5) timer.scheduleFunction(ctld.addOtherF10MenuOptions, nil, timer.getTime() + 5) - + timer.scheduleFunction(ctld.updateDynamicLogisticUnitsZones, nil, timer.getTime() + 5) if ctld.enableCrates == true and ctld.hoverPickup == true then timer.scheduleFunction(ctld.checkHoverStatus, nil, timer.getTime() + 1) end - - end,nil, timer.getTime()+1 ) + if ctld.enableRepackingVehicles == true then + timer.scheduleFunction(ctld.updateRepackMenuOnlanding, nil, timer.getTime() + 1) -- update helo repack menu when a helo landing is detected + timer.scheduleFunction(ctld.repackVehicle, nil, timer.getTime() + 1) + end + if ctld.enableAutoOrbitingFlyingJtacOnTarget then + timer.scheduleFunction(ctld.TreatOrbitJTAC, {}, timer.getTime()+3) + end + if ctld.nbLimitSpawnedTroops[1]~=0 or ctld.nbLimitSpawnedTroops[2]~=0 then + timer.scheduleFunction(ctld.updateTroopsInGame, {}, timer.getTime()+1) + end + end, nil, timer.getTime() + 1) --event handler for deaths --world.addEventHandler(ctld.eventHandler) @@ -7953,7 +8408,7 @@ function ctld.initialize() env.info("Generating UHF Frequencies") ctld.generateUHFrequencies() - env.info("Generated UHF Frequencies") + env.info("Generated UHF Frequencies") env.info("Generating VHF Frequencies") ctld.generateVHFrequencies() @@ -7969,28 +8424,25 @@ function ctld.initialize() -- Search for crates in the mission editor instead env.info("Searching for Crates") for _coalitionName, _coalitionData in pairs(env.mission.coalition) do - if (_coalitionName == 'red' or _coalitionName == 'blue') - and type(_coalitionData) == 'table' then - if _coalitionData.country then --there is a country table - for _, _countryData in pairs(_coalitionData.country) do - - if type(_countryData) == 'table' then - for _objectTypeName, _objectTypeData in pairs(_countryData) do - if _objectTypeName == "static" then - - if ((type(_objectTypeData) == 'table') - and _objectTypeData.group - and (type(_objectTypeData.group) == 'table') - and (#_objectTypeData.group > 0)) then - - for _groupId, _group in pairs(_objectTypeData.group) do - if _group and _group.units and type(_group.units) == 'table' then - for _unitNum, _unit in pairs(_group.units) do - if _unit.canCargo == true then - local _cargoName = env.getValueDictByKey(_unit.name) - ctld.missionEditorCargoCrates[_cargoName] = _cargoName - env.info("Crate Found: " .. _unit.name.." - Unit: ".._cargoName) + and type(_coalitionData) == 'table' then + if _coalitionData.country then --there is a country table + for _, _countryData in pairs(_coalitionData.country) do + if type(_countryData) == 'table' then + for _objectTypeName, _objectTypeData in pairs(_countryData) do + if _objectTypeName == "static" then + if ((type(_objectTypeData) == 'table') + and _objectTypeData.group + and (type(_objectTypeData.group) == 'table') + and (#_objectTypeData.group > 0)) then + for _groupId, _group in pairs(_objectTypeData.group) do + if _group and _group.units and type(_group.units) == 'table' then + for _unitNum, _unit in pairs(_group.units) do + if _unit.canCargo == true then + local _cargoName = env.getValueDictByKey(_unit.name) + ctld.missionEditorCargoCrates[_cargoName] = _cargoName + env.info("Crate Found: " .. _unit.name .. " - Unit: " .. _cargoName) + end end end end @@ -8000,7 +8452,6 @@ function ctld.initialize() end end end - end end end env.info("END search for crates") @@ -8008,7 +8459,6 @@ function ctld.initialize() -- register event handler ctld.logInfo("registering event handler") world.addEventHandler(ctld.eventHandler) - env.info("CTLD READY") end @@ -8045,11 +8495,13 @@ function ctld.eventHandler:onEvent(event) end local function processHumanPlayer() - ctld.logTrace("in the 'processHumanPlayer' function") - if mist.DBs.humansByName[unitName] then -- it's a human unit + ctld.logTrace("in the 'processHumanPlayer' function processHumanPlayer()- unitName = %s", ctld.p(unitName)) + --ctld.logTrace("in the 'processHumanPlayer' function processHumanPlayer()- mist.DBs.humansByName[unitName] = %s", ctld.p(mist.DBs.humansByName[unitName])) + if mist.DBs.humansByName[unitName] then -- it's a human unit ctld.logDebug("caught event %s for human unit [%s]", ctld.p(eventName), ctld.p(unitName)) local _unit = Unit.getByName(unitName) if _unit ~= nil then + local _groupId = _unit:getGroup():getID() -- assign transport pilot ctld.logTrace("_unit = %s", ctld.p(_unit)) @@ -8058,22 +8510,23 @@ function ctld.eventHandler:onEvent(event) -- Allow units to CTLD by aircraft type and not by pilot name if ctld.addPlayerAircraftByType then - for _,aircraftType in pairs(ctld.aircraftTypeTable) do + for _, aircraftType in pairs(ctld.aircraftTypeTable) do if aircraftType == playerTypeName then ctld.logTrace("adding by aircraft type, unitName = %s", ctld.p(unitName)) - -- add transport unit to the list - table.insert(ctld.transportPilotNames, unitName) - -- add transport radio menu - ctld.addTransportF10MenuOptions(unitName) - break + if ctld.isValueInIpairTable(ctld.transportPilotNames, unitName) == false then + table.insert(ctld.transportPilotNames, unitName) -- add transport unit to the list + end + if ctld.addedTo[tostring(_groupId)] == nil then -- only if menu not already set up + ctld.addTransportF10MenuOptions(unitName) -- add transport radio menu + break + end end end else for _, _unitName in pairs(ctld.transportPilotNames) do if _unitName == unitName then ctld.logTrace("adding by transportPilotNames, unitName = %s", ctld.p(unitName)) - -- add transport radio menu - ctld.addTransportF10MenuOptions(unitName) + ctld.addTransportF10MenuOptions(unitName) -- add transport radio menu break end end @@ -8088,12 +8541,11 @@ function ctld.eventHandler:onEvent(event) timer.scheduleFunction(function() ctld.logTrace("calling the 'processHumanPlayer' function in a timer") processHumanPlayer() - end, nil, timer.getTime()+0.5) + end, nil, timer.getTime() + 2) --1.5 else ctld.logTrace("calling the 'processHumanPlayer' function immediately") processHumanPlayer() end - end function ctld.i18n_check(language, verbose) @@ -8106,18 +8558,20 @@ function ctld.i18n_check(language, verbose) local englishVersion = english.translation_version local tocheckVersion = tocheck.translation_version if englishVersion ~= tocheckVersion then - ctld.logError(string.format("CTLD.i18n_check: Language version mismatch: EN has version %s, %s has version %s", englishVersion, language, tocheckVersion)) + ctld.logError(string.format("CTLD.i18n_check: Language version mismatch: EN has version %s, %s has version %s", + englishVersion, language, tocheckVersion)) end --ctld.logTrace(string.format("english = %s", ctld.p(english))) for textRef, textEnglish in pairs(english) do if textRef ~= "translation_version" then local textTocheck = tocheck[textRef] if not textTocheck then - ctld.logError(string.format( "CTLD.i18n_check: NOT FOUND: checking %s text [%s]", language, textRef)) + ctld.logError(string.format("CTLD.i18n_check: NOT FOUND: checking %s text [%s]", language, textRef)) elseif textTocheck == textEnglish then - ctld.logWarning(string.format("CTLD.i18n_check: SAME: checking %s text [%s] as in EN", language, textRef)) + ctld.logWarning(string.format("CTLD.i18n_check: SAME: checking %s text [%s] as in EN", language, + textRef)) elseif verbose then - ctld.logInfo(string.format( "CTLD.i18n_check: OK: checking %s text [%s]", language, textRef)) + ctld.logInfo(string.format("CTLD.i18n_check: OK: checking %s text [%s]", language, textRef)) end end end @@ -8130,6 +8584,22 @@ end -- initialize the random number generator to make it almost random math.random(); math.random(); math.random() +function ctld.RandomReal(mini, maxi) + local rand = math.random() --random value between 0 and 1 + local result = mini + rand * (maxi - mini) -- scale the random value between [mini, maxi] + return result +end + +-- Tools +function ctld.isValueInIpairTable(tab, value) + for i, v in ipairs(tab) do + if v == value then + return true -- La valeur existe + end + end + return false -- La valeur n'existe pas + end + --- Enable/Disable error boxes displayed on screen. env.setErrorMessageBoxEnabled(false) @@ -8139,4 +8609,4 @@ if ctld.dontInitialize then ctld.logInfo(string.format("Skipping initializion of version %s because ctld.dontInitialize is true", ctld.Version)) else ctld.initialize() -end \ No newline at end of file +end diff --git a/README.md b/README.md index b34ffd6..d0b4d4b 100644 --- a/README.md +++ b/README.md @@ -44,12 +44,15 @@ This script is a rewrite of some of the functionality of the original Complete C * [Spawn Sling loadable crate at a Zone](#spawn-sling-loadable-crate-at-a-zone) * [Spawn Sling loadable crate at a Point](#spawn-sling-loadable-crate-at-a-point) * [JTAC Automatic Targeting and Laser](#jtac-automatic-targeting-and-laser) + * [JTAC Automatic Orbiting Over Lased Target](#jtac-automatic-orbiting-over-lased-target) * [In Game](#in-game) * [Troop Loading and Unloading](#troop-loading-and-unloading) +* [Limit troop Loading](#limit-troop-loading) * [Cargo Spawning and Sling Loading](#cargo-spawning-and-sling-loading) * [Simulated Sling Loading](#simulated-sling-loading) * [Real Sling Loading](#real-sling-loading) * [Crate Unpacking](#crate-unpacking) +* [Crate Repacking](#crate-repacking) * [Forward Operating Base (FOB) Construction](#forward-operating-base-fob-construction) * [Radio Beacon Deployment](#radio-beacon-deployment) * [A10\-C UHF ADF Radio Setup](#a10-c-uhf-adf-radio-setup) @@ -861,6 +864,13 @@ For example, if the laser code is *1688*, the frequency will be *40.40Mhz*. JTAC frequency is available through the "JTAC Status" radio menu +#### Jtac-automatic-orbiting-over-lased-target +By setting parameter ctld.enableAutoOrbitingFlyingJtacOnTarget = true, a script +dedicated script puts in orbit each flying JTAC over his detected target. +Associated with CTLD/JTAC functions, you can assign a fly route to the JTAC drone, +this one follow it, and start orbiting when he detects a target. +As soon as it don't detect a target, it restart following its initial route at the nearest waypoint + # In Game ## Troop Loading and Unloading @@ -915,7 +925,14 @@ ctld.MG_WEIGHT = 10 -- kg ctld.MORTAR_WEIGHT = 26 -- kg ctld.JTAC_WEIGHT = 15 -- kg ``` +## Limit troop Loading +The number of Infantries units in mission can be limited by setting the table below : +ctld.nbLimitSpwanedTroops = {0, 0} -- {redLimitInfantryCount, blueLimitInfantryCount} +When this cumulative number of troops is reached for a coalition, no more troops can be loaded onboard, and a message is sent to player. + +If ctld.nbLimitSpwanedTroops = {0, 0} (both values at 0) the limit control is disabled. +If either value is non-zero, limit control becomes active for both coalitions. ## Cargo Spawning and Sling Loading @@ -1006,6 +1023,15 @@ Rearming: You can also repair a partially destroyed HAWK / BUK or KUB system by dropping a repair crate next to it and unpacking. A repair crate will also re-arm the system. +## Crate Repacking +The F10 menu allows you to repack units having associated crate types in the "ctld.spawnableCrates" table. +Simply land near the unit you wish to repack and select it from the list presented by the "CTLD//Vehicle/FOB transport...//Repack Vehicles" menu. +The defined radius of vehicles detection is specified by the parameter + +ctld.maximumDistanceRepackableUnitsSearch = 200 -- max distance from transportUnit to search force repackable units in meters + +WARNING: Due to technical reasons related to the refresh time of the F10 menus, there may be inconsistencies between the type of vehicles requested and those provided. It is recommended to wait 5 to 10 seconds without moving after landing and opening the F10 menu for a packaging order. + ## Forward Operating Base (FOB) Construction FOBs can be built by loading special FOB crates from a **Logistics** unit into a C-130 or other large aircraft configured in the script. To load the crate use the F10 - Troop Commands Menu. The idea behind FOBs is to make player vs player missions even more dynamic as these can be deployed in most locations. Once destroyed the FOB can no longer be used.