From 0cd1cd97a67eee08e266944a64029e1d9ff0100f Mon Sep 17 00:00:00 2001 From: Applevangelist <72444570+Applevangelist@users.noreply.github.com> Date: Tue, 22 Jun 2021 13:11:12 +0200 Subject: [PATCH 01/16] Update CSAR.lua (#1553) --- Moose Development/Moose/Ops/CSAR.lua | 98 +++++++++++++++------------- 1 file changed, 51 insertions(+), 47 deletions(-) diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index 8d6e7292c..ad6ae1a97 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -14,7 +14,7 @@ -- -- **Main Features:** -- --- * MOOSE based Helicopter CSAR Operations. +-- * MOOSE-based Helicopter CSAR Operations for Players. -- -- === -- @@ -41,13 +41,14 @@ -- -- # CSAR Concept -- --- * Object oriented refactoring of Ciribob's fantastic CSAR script. +-- * MOOSE-based Helicopter CSAR Operations for Players. +-- * Object oriented refactoring of Ciribob\'s fantastic CSAR script. -- * No need for extra MIST loading. -- * Additional events to tailor your mission. -- -- ## 0. Prerequisites -- --- You need to load an .ogg soundfile for the pilot's beacons into the mission, e.g. "beacon.ogg", use a once trigger, "sound to country" for that. +-- You need to load an .ogg soundfile for the pilot\'s beacons into the mission, e.g. "beacon.ogg", use a once trigger, "sound to country" for that. -- Create a late-activated single infantry unit as template in the mission editor and name it e.g. "Downed Pilot". -- -- ## 1. Basic Setup @@ -66,19 +67,19 @@ -- -- The following options are available (with their defaults). Only set the ones you want changed: -- --- self.allowDownedPilotCAcontrol = false -- Set to false if you don't want to allow control by Combined Arms. +-- self.allowDownedPilotCAcontrol = false -- Set to false if you don\'t want to allow control by Combined Arms. -- self.allowFARPRescue = true -- allows pilots to be rescued by landing at a FARP or Airbase. Else MASH only! --- self.autosmoke = false -- automatically smoke a downed pilot's location when a heli is near. +-- self.autosmoke = false -- automatically smoke a downed pilot\'s location when a heli is near. -- self.coordtype = 1 -- Use Lat/Long DDM (0), Lat/Long DMS (1), MGRS (2), Bullseye imperial (3) or Bullseye metric (4) for coordinates. --- self.csarOncrash = true -- (WIP) If set to true, will generate a downed pilot when a plane crashes as well. --- self.enableForAI = true -- set to false to disable AI pilots from being rescued. +-- self.csarOncrash = false -- (WIP) If set to true, will generate a downed pilot when a plane crashes as well. +-- self.enableForAI = false -- set to false to disable AI pilots from being rescued. -- self.pilotRuntoExtractPoint = true -- Downed pilot will run to the rescue helicopter up to self.extractDistance in meters. -- self.extractDistance = 500 -- Distance the downed pilot will start to run to the rescue helicopter. -- self.immortalcrew = true -- Set to true to make wounded crew immortal. -- self.invisiblecrew = false -- Set to true to make wounded crew insvisible. -- self.loadDistance = 75 -- configure distance for pilots to get into helicopter in meters. -- self.mashprefix = {"MASH"} -- prefixes of #GROUP objects used as MASHes. --- self.max_units = 6 -- number of pilots that can be carried if #CSAR.AircraftType is undefined. +-- self.max_units = 6 -- max number of pilots that can be carried if #CSAR.AircraftType is undefined. -- self.messageTime = 15 -- Time to show messages for in seconds. Doubled for long messages. -- self.radioSound = "beacon.ogg" -- the name of the sound file to use for the pilots\' radio beacons. -- self.smokecolor = 4 -- Color of smokemarker, 0 is green, 1 is red, 2 is white, 3 is orange and 4 is blue. @@ -202,18 +203,21 @@ CSAR = { -- @field Wrapper.Group#GROUP group Spawned group object. -- @field #number timestamp Timestamp for approach process ---- Known beacons from the available maps +--- Updated and sorted list of known NDB beacons (in kHz!) from the available maps. -- @field #CSAR.SkipFrequencies CSAR.SkipFrequencies = { - 745,381,384,300.50,312.5,1175,342,735,300.50,353.00, - 440,795,525,520,690,625,291.5,300.50, - 435,309.50,920,1065,274,312.50, - 580,602,297.50,750,485,950,214, - 1025,730,995,455,307,670,329,395,770, - 380,705,300.5,507,740,1030,515,330,309.5,348,462,905,352,1210,942,435, - 324,320,420,311,389,396,862,680,297.5,920,662,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,540,550,560,570, + 214,274,291.5,295,297.5, + 300.5,304,307,309.5,311,312,312.5,316, + 320,324,328,329,330,336,337, + 342,343,348,351,352,353,358, + 363,365,368,372.5,374, + 380,381,384,389,395,396, + 414,420,430,432,435,440,450,455,462,470,485, + 507,515,520,525,528,540,550,560,570,577,580,602,625,641,662,670,680,682,690, + 705,720,722,730,735,740,745,750,770,795, + 822,830,862,866, + 905,907,920,935,942,950,995, + 1000,1025,1030,1050,1065,1116,1175,1182,1210 } --- All slot / Limit settings @@ -231,7 +235,7 @@ CSAR.AircraftType["Mi-24V"] = 8 --- CSAR class version. -- @field #string version -CSAR.version="0.1.3r3" +CSAR.version="0.1.3r4" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -325,9 +329,9 @@ function CSAR:New(Coalition, Template, Alias) -- settings, counters etc self.rescues = 0 -- counter for successful rescue landings at FARP/AFB/MASH self.rescuedpilots = 0 -- counter for saved pilots - self.csarOncrash = true -- If set to true, will generate a csar when a plane crashes as well. - self.allowDownedPilotCAcontrol = false -- Set to false if you don't want to allow control by Combined arms. - self.enableForAI = true -- set to false to disable AI units from being rescued. + self.csarOncrash = false -- If set to true, will generate a csar when a plane crashes as well. + self.allowDownedPilotCAcontrol = false -- Set to false if you don\'t want to allow control by Combined arms. + self.enableForAI = false -- set to false to disable AI units from being rescued. self.smokecolor = 4 -- Color of smokemarker for blue side, 0 is green, 1 is red, 2 is white, 3 is orange and 4 is blue self.coordtype = 2 -- Use Lat/Long DDM (0), Lat/Long DMS (1), MGRS (2), Bullseye imperial (3) or Bullseye metric (4) for coordinates. self.immortalcrew = true -- Set to true to make wounded crew immortal @@ -339,18 +343,18 @@ function CSAR:New(Coalition, Template, Alias) self.loadtimemax = 135 -- seconds self.radioSound = "beacon.ogg" -- the name of the sound file to use for the Pilot radio beacons. If this isnt added to the mission BEACONS WONT WORK! self.allowFARPRescue = true --allows pilot to be rescued by landing at a FARP or Airbase - self.max_units = 6 --number of pilots that can be carried + self.max_units = 6 --max number of pilots that can be carried self.useprefix = true -- Use the Prefixed defined below, Requires Unit have the Prefix defined below - self.csarPrefix = { "helicargo", "MEDEVAC"} -- prefixes used for useprefix=true - DON'T use # in names! + self.csarPrefix = { "helicargo", "MEDEVAC"} -- prefixes used for useprefix=true - DON\'T use # in names! self.template = Template or "generic" -- template for downed pilot self.mashprefix = {"MASH"} -- prefixes used to find MASHes self.bluemash = SET_GROUP:New():FilterCoalitions(self.coalition):FilterPrefixes(self.mashprefix):FilterOnce() -- currently only GROUP objects, maybe support STATICs also? self.autosmoke = false -- automatically smoke location when heli is near - -- WARNING - here'll be dragons + -- WARNING - here\'ll be dragons -- for this to work you need to de-sanitize your mission environment in \Scripts\MissionScripting.lua -- needs SRS => 1.9.6 to work (works on the *server* side) - self.useSRS = false -- Use FF's SRS integration + self.useSRS = false -- Use FF\'s SRS integration self.SRSPath = "E:\\Program Files\\DCS-SimpleRadio-Standalone\\" -- adjust your own path in your server(!) self.SRSchannel = 300 -- radio channel self.SRSModulation = radio.modulation.AM -- modulation @@ -403,7 +407,7 @@ function CSAR:New(Coalition, Template, Alias) -- @param #string Event Event. -- @param #string To To state. -- @param #string Heliname Name of the helicopter group. - -- @param #string Woundedgroupname Name of the downed pilot's group. + -- @param #string Woundedgroupname Name of the downed pilot\'s group. --- On After "Boarded" event. Downed pilot boarded heli. -- @function [parent=#CSAR] OnAfterBoarded @@ -412,7 +416,7 @@ function CSAR:New(Coalition, Template, Alias) -- @param #string Event Event. -- @param #string To To state. -- @param #string Heliname Name of the helicopter group. - -- @param #string Woundedgroupname Name of the downed pilot's group. + -- @param #string Woundedgroupname Name of the downed pilot\'s group. --- On After "Returning" event. Heli can return home with downed pilot(s). -- @function [parent=#CSAR] OnAfterReturning @@ -421,7 +425,7 @@ function CSAR:New(Coalition, Template, Alias) -- @param #string Event Event. -- @param #string To To state. -- @param #string Heliname Name of the helicopter group. - -- @param #string Woundedgroupname Name of the downed pilot's group. + -- @param #string Woundedgroupname Name of the downed pilot\'s group. --- On After "Rescued" event. Pilot(s) have been brought to the MASH/FARP/AFB. -- @function [parent=#CSAR] OnAfterRescued @@ -628,7 +632,7 @@ end -- @param #number _coalition Coalition. -- @param #string _description (optional) Description. -- @param #boolean _randomPoint (optional) Random yes or no. --- @param #boolean _nomessage (optional) If true, don't send a message to SAR. +-- @param #boolean _nomessage (optional) If true, don\'t send a message to SAR. function CSAR:_SpawnCsarAtZone( _zone, _coalition, _description, _randomPoint, _nomessage) self:T(self.lid .. " _SpawnCsarAtZone") local freq = self:_GenerateADFFrequency() @@ -877,7 +881,7 @@ function CSAR:_RemoveNameFromDownedPilots(name,force) if _pilot.name == name then local group = _pilot.group -- Wrapper.Group#GROUP if group then - if (not group:IsAlive()) or ( force == true) then -- don't delete groups which still exist + if (not group:IsAlive()) or ( force == true) then -- don\'t delete groups which still exist found = true _pilot.desc = nil _pilot.frequency = nil @@ -933,13 +937,13 @@ function CSAR:_CheckWoundedGroupStatus(heliname,woundedgroupname) local _distance = self:_GetDistance(_heliCoord,_leaderCoord) if _distance < 3000 and _distance > 0 then if self:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedGroup, _woundedGroupName) == true then - -- we're close, reschedule + -- we\'re close, reschedule _downedpilot.timestamp = timer.getAbsTime() self:__Approach(-5,heliname,woundedgroupname) end else self.heliVisibleMessage[_lookupKeyHeli] = nil - --reschedule as units aren't dead yet , schedule for a bit slower though as we're far away + --reschedule as units aren\'t dead yet , schedule for a bit slower though as we\'re far away _downedpilot.timestamp = timer.getAbsTime() self:__Approach(-10,heliname,woundedgroupname) end @@ -949,7 +953,7 @@ function CSAR:_CheckWoundedGroupStatus(heliname,woundedgroupname) end end ---- Function to pop a smoke at a wounded pilot's positions. +--- Function to pop a smoke at a wounded pilot\'s positions. -- @param #CSAR self -- @param #string _woundedGroupName Name of the group. -- @param Wrapper.Group#GROUP _woundedLeader Object of the group. @@ -985,13 +989,13 @@ function CSAR:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupNam _groups = self.inTransitGroups[_heliName] end - -- if the heli can't pick them up, show a message and return + -- if the heli can\'t pick them up, show a message and return local _maxUnits = self.AircraftType[_heliUnit:GetTypeName()] if _maxUnits == nil then _maxUnits = self.max_units end if _unitsInHelicopter + 1 > _maxUnits then - self:_DisplayMessageToSAR(_heliUnit, string.format("%s, %s. We're already crammed with %d guys! Sorry!", _pilotName, _heliName, _unitsInHelicopter, _unitsInHelicopter), self.messageTime) + self:_DisplayMessageToSAR(_heliUnit, string.format("%s, %s. We\'re already crammed with %d guys! Sorry!", _pilotName, _heliName, _unitsInHelicopter, _unitsInHelicopter), self.messageTime) return true end @@ -1010,7 +1014,7 @@ function CSAR:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupNam _woundedGroup:Destroy() self:_RemoveNameFromDownedPilots(_woundedGroupName,true) - self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s I'm in! Get to the MASH ASAP! ", _heliName, _pilotName), self.messageTime,true,true) + self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s I\'m in! Get to the MASH ASAP! ", _heliName, _pilotName), self.messageTime,true,true) self:__Boarded(5,_heliName,_woundedGroupName) @@ -1068,9 +1072,9 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG if self.heliCloseMessage[_lookupKeyHeli] == nil then if self.autosmoke == true then - self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. You're close now! Land or hover at the smoke.", _heliName, _pilotName), self.messageTime,true,true) + self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. You\'re close now! Land or hover at the smoke.", _heliName, _pilotName), self.messageTime,true,true) else - self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. You're close now! Land in a safe place, I will go there ", _heliName, _pilotName), self.messageTime,true,true) + self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. You\'re close now! Land in a safe place, I will go there ", _heliName, _pilotName), self.messageTime,true,true) end --mark as shown for THIS heli and THIS group self.heliCloseMessage[_lookupKeyHeli] = true @@ -1079,7 +1083,7 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG -- have we landed close enough? if not _heliUnit:InAir() then - -- if you land on them, doesnt matter if they were heading to someone else as you're closer, you win! :) + -- if you land on them, doesnt matter if they were heading to someone else as you\'re closer, you win! :) if self.pilotRuntoExtractPoint == true then if (_distance < self.extractDistance) then local _time = self.landedStatus[_lookupKeyHeli] @@ -1134,7 +1138,7 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG end if _time > 0 then - self:_DisplayMessageToSAR(_heliUnit, "Hovering above " .. _pilotName .. ". \n\nHold hover for " .. _time .. " seconds to winch them up. \n\nIf the countdown stops you're too far away!", self.messageTime, true) + self:_DisplayMessageToSAR(_heliUnit, "Hovering above " .. _pilotName .. ". \n\nHold hover for " .. _time .. " seconds to winch them up. \n\nIf the countdown stops you\'re too far away!", self.messageTime, true) else self.hoverStatus[_lookupKeyHeli] = nil self:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupName) @@ -1290,7 +1294,7 @@ function CSAR:_DisplayMessageToSAR(_unit, _text, _time, _clear, _speak) end end ---- Function to get string of a group's position. +--- Function to get string of a group\'s position. -- @param #CSAR self -- @param Wrapper.Controllable#CONTROLLABLE _woundedGroup Group or Unit object. -- @return #string Coordinates as Text @@ -1672,7 +1676,7 @@ function CSAR:_GenerateVHFrequencies() -- third range _start = 850000 - while _start <= 1250000 do + while _start <= 999000 do -- updated for Gazelle -- skip existing NDB frequencies local _found = false @@ -1819,7 +1823,7 @@ function CSAR:onbeforeStatus(From, Event, To) local name = entry.name local timestamp = entry.timestamp or 0 local now = timer.getAbsTime() - if now - timestamp > 17 then -- only check if we're not in approach mode, which is iterations of 5 and 10. + if now - timestamp > 17 then -- only check if we\'re not in approach mode, which is iterations of 5 and 10. self:_CheckWoundedGroupStatus(_sar,name) end end @@ -1894,7 +1898,7 @@ end -- @param #string Event Event triggered. -- @param #string To To state. -- @param #string Heliname Name of the helicopter group. --- @param #string Woundedgroupname Name of the downed pilot's group. +-- @param #string Woundedgroupname Name of the downed pilot\'s group. function CSAR:onbeforeApproach(From, Event, To, Heliname, Woundedgroupname) self:T({From, Event, To, Heliname, Woundedgroupname}) self:_CheckWoundedGroupStatus(Heliname,Woundedgroupname) @@ -1907,7 +1911,7 @@ end -- @param #string Event Event triggered. -- @param #string To To state. -- @param #string Heliname Name of the helicopter group. --- @param #string Woundedgroupname Name of the downed pilot's group. +-- @param #string Woundedgroupname Name of the downed pilot\'s group. function CSAR:onbeforeBoarded(From, Event, To, Heliname, Woundedgroupname) self:T({From, Event, To, Heliname, Woundedgroupname}) self:_ScheduledSARFlight(Heliname,Woundedgroupname) @@ -1920,7 +1924,7 @@ end -- @param #string Event Event triggered. -- @param #string To To state. -- @param #string Heliname Name of the helicopter group. --- @param #string Woundedgroupname Name of the downed pilot's group. +-- @param #string Woundedgroupname Name of the downed pilot\'s group. function CSAR:onbeforeReturning(From, Event, To, Heliname, Woundedgroupname) self:T({From, Event, To, Heliname, Woundedgroupname}) self:_ScheduledSARFlight(Heliname,Woundedgroupname) From f235037cb9999beb6752d7de27b419c9bb235738 Mon Sep 17 00:00:00 2001 From: Applevangelist <72444570+Applevangelist@users.noreply.github.com> Date: Tue, 22 Jun 2021 18:24:11 +0200 Subject: [PATCH 02/16] Create CTLD.lua (#1555) Player Heli Cargo and Troops Ops :) --- Moose Development/Moose/Ops/CTLD.lua | 2322 ++++++++++++++++++++++++++ 1 file changed, 2322 insertions(+) create mode 100644 Moose Development/Moose/Ops/CTLD.lua diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua new file mode 100644 index 000000000..38640518f --- /dev/null +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -0,0 +1,2322 @@ +--- **Ops** -- Combat Troops & Logistics Deployment. +-- +-- === +-- +-- **CTLD** - MOOSE based Helicopter CTLD Operations. +-- +-- === +-- +-- ## Missions: +-- +-- ### [CTLD - Combat Troop & Logistics Deployment](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/) +-- +-- === +-- +-- **Main Features:** +-- +-- * MOOSE-based Helicopter CTLD Operations for Players. +-- +-- === +-- +-- ### Author: **Applevangelist** (Moose Version), ***Ciribob*** (original) +-- @module Ops.CTLD +-- @image OPS_CTLD.jpg + +-- Date: June 2021 + +do +------------------------------------------------------ +--- **CTLD_CARGO** class, extends #Core.Base#BASE +-- @type CTLD_CARGO +-- @field #number ID ID of this cargo. +-- @field #string Name Name for menu. +-- @field #table Templates Table of #POSITIONABLE objects. +-- @field #CTLD_CARGO.Enum Type Enumerator of Type. +-- @field #boolean HasBeenMoved Flag for moving. +-- @field #boolean LoadDirectly Flag for direct loading. +-- @field #number CratesNeeded Crates needed to build. +-- @field Wrapper.Positionable#POSITIONABLE Positionable Representation of cargo in the mission. +-- @field #boolean HasBeenDropped True if dropped from heli. +-- @extends Core.Fsm#FSM +CTLD_CARGO = { + ClassName = "CTLD_CARGO", + ID = 0, + Name = "none", + Templates = {}, + CargoType = "none", + HasBeenMoved = false, + LoadDirectly = false, + CratesNeeded = 0, + Positionable = nil, + HasBeenDropped = false, + } + + --- Define cargo types. + -- @type CTLD_CARGO.Enum + -- @field #string Type Type of Cargo. + CTLD_CARGO.Enum = { + VEHICLE = "Vehicle", -- #string vehicles + TROOPS = "Troops", -- #string troops + FOB = "FOB", -- #string FOB + CRATE = "CRATE", -- #string crate + } + + --- Function to create new CTLD_CARGO object. + -- @param #CTLD_CARGO self + -- @param #number ID ID of this #CTLD_CARGO + -- @param #string Name Name for menu. + -- @param #table Templates Table of #POSITIONABLE objects. + -- @param #CTLD_CARGO.Enum Sorte Enumerator of Type. + -- @param #boolean HasBeenMoved Flag for moving. + -- @param #boolean LoadDirectly Flag for direct loading. + -- @param #number CratesNeeded Crates needed to build. + -- @param Wrapper.Positionable#POSITIONABLE Positionable Representation of cargo in the mission. + -- @param #boolean Dropped Cargo/Troops have been unloaded from a chopper. + -- @return #CTLD_CARGO self + function CTLD_CARGO:New(ID, Name, Templates, Sorte, HasBeenMoved, LoadDirectly, CratesNeeded, Positionable, Dropped) + -- Inherit everything from BASE class. + local self=BASE:Inherit(self, BASE:New()) -- #CTLD + self:T({ID, Name, Templates, Sorte, HasBeenMoved, LoadDirectly, CratesNeeded, Positionable, Dropped}) + self.ID = ID or math.random(100000,1000000) + self.Name = Name or "none" -- #string + self.Templates = Templates or {} -- #table + self.CargoType = Sorte or "type" -- #CTLD_CARGO.Enum + self.HasBeenMoved = HasBeenMoved or false -- #booolean + self.LoadDirectly = LoadDirectly or false -- #booolean + self.CratesNeeded = CratesNeeded or 0 -- #number + self.Positionable = Positionable or nil -- Wrapper.Positionable#POSITIONABLE + self.HasBeenDropped = Dropped or false --#boolean + return self + end + + --- Query ID. + -- @param #CTLD_CARGO self + -- @return #number ID + function CTLD_CARGO:GetID() + return self.ID + end + + --- Query Name. + -- @param #CTLD_CARGO self + -- @return #string Name + function CTLD_CARGO:GetName() + return self.Name + end + + --- Query Templates. + -- @param #CTLD_CARGO self + -- @return #table Templates + function CTLD_CARGO:GetTemplates() + return self.Templates + end + + --- Query has moved. + -- @param #CTLD_CARGO self + -- @return #boolean Has moved + function CTLD_CARGO:HasMoved() + return self.HasBeenMoved + end + + --- Query was dropped. + -- @param #CTLD_CARGO self + -- @return #boolean Has been dropped. + function CTLD_CARGO:WasDropped() + return self.HasBeenDropped + end + + --- Query directly loadable. + -- @param #CTLD_CARGO self + -- @return #boolean loadable + function CTLD_CARGO:CanLoadDirectly() + return self.LoadDirectly + end + + --- Query number of crates or troopsize. + -- @param #CTLD_CARGO self + -- @return #number Crates or size of troops. + function CTLD_CARGO:GetCratesNeeded() + return self.CratesNeeded + end + + --- Query type. + -- @param #CTLD_CARGO self + -- @return #CTLD_CARGO.Enum Type + function CTLD_CARGO:GetType() + return self.CargoType + end + + --- Query type. + -- @param #CTLD_CARGO self + -- @return Wrapper.Positionable#POSITIONABLE Positionable + function CTLD_CARGO:GetPositionable() + return self.Positionable + end + + --- Set HasMoved. + -- @param #CTLD_CARGO self + -- @param #boolean moved + function CTLD_CARGO:SetHasMoved(moved) + self.HasBeenMoved = moved or false + end + + --- Query if cargo has been loaded. + -- @param #CTLD_CARGO self + -- @param #boolean loaded + function CTLD_CARGO:Isloaded() + if self.HasBeenMoved and not self.WasDropped() then + return true + else + return false + end + end + --- Set WasDropped. + -- @param #CTLD_CARGO self + -- @param #boolean dropped + function CTLD_CARGO:SetWasDropped(dropped) + self.HasBeenDropped = dropped or false + end + +end + +do +------------------------------------------------------------------------- +--- **CTLD** class, extends #Core.Base#BASE, #Core.Fsm#FSM +-- @type CTLD +-- @field #string ClassName Name of the class. +-- @field #number verbose Verbosity level. +-- @field #string lid Class id string for output to DCS log file. +-- @field #number coalition Coalition side number, e.g. `coalition.side.RED`. +-- @extends Core.Fsm#FSM + +--- *Combat Troop & Logistics Deployment (CTLD): Everyone wants to be a POG, until there\'s POG stuff to be done.* (Mil Saying) +-- +-- === +-- +-- ![Banner Image](OPS_CTLD.jpg) +-- +-- # CTLD Concept +-- +-- * MOOSE-based CTLD for Players. +-- * Object oriented refactoring of Ciribob\'s fantastic CTLD script. +-- * No need for extra MIST loading. +-- * Additional events to tailor your mission. +-- * ANY late activated group can serve as cargo, either as troops or crates, which have to be build on-location. +-- +-- ## 0. Prerequisites +-- +-- You need to load an .ogg soundfile for the pilot\'s beacons into the mission, e.g. "beacon.ogg", use a once trigger, "sound to country" for that. +-- Create the late-activated troops, vehicles (no statics at this point!) that will make up your deployable forces. +-- +-- ## 1. Basic Setup +-- +-- ## 1.1 Create and start a CTLD instance +-- +-- A basic setup example is the following: +-- +-- -- Instantiate and start a CTLD for the blue side, using helicopter groups named "Helicargo" and alias "Lufttransportbrigade I" +-- local my_ctld = CTLD:New(coalition.side.BLUE,{"Helicargo"},"Lufttransportbrigade I") +-- my_ctld:__Start(5) +-- +-- ## 1.2 Add cargo types available +-- +-- Add *generic* cargo types that you need for your missions, here infantry units, vehicles and a FOB. These need to be late-activated Wrapper.Group#GROUP objects: +-- +-- -- add infantry unit called "Anti-Tank Small" using template "ATS", of type TROOP with size 3 +-- -- infantry units will be loaded directly from LOAD zones into the heli (matching number of free seats needed) +-- my_ctld:AddTroopsCargo("Anti-Tank Small",{"ATS"},CTLD_CARGO.Enum.TROOPS,3) +-- +-- -- add infantry unit called "Anti-Tank" using templates "AA" and "AA"", of type TROOP with size 4 +-- my_ctld:AddTroopsCargo("Anti-Air",{"AA","AA2"},CTLD_CARGO.Enum.TROOPS,4) +-- +-- -- add vehicle called "Humvee" using template "Humvee", of type VEHICLE, size 2, i.e. needs two crates to be build +-- -- vehicles and FOB will be spawned as crates in a LOAD zone first. Once transported to DROP zones, they can be build into the objects +-- my_ctld:AddCratesCargo("Humvee",{"Humvee"},CTLD_CARGO.Enum.VEHICLE,2) +-- +-- -- add infantry unit called "Forward Ops Base" using template "FOB", of type FOB, size 4, i.e. needs four crates to be build: +-- my_ctld:AddCratesCargo("Forward Ops Base",{"FOB"},CTLD_CARGO.Enum.FOB,4) +-- +-- ## 1.3 Add logistics zones +-- +-- Add zones for loading troops and crates and dropping, building crates +-- +-- -- Add a zone of type LOAD to our setup. Players can load troops and crates. +-- -- "Loadzone" is the name of the zone from the ME. Players can load, if they are inside of the zone. +-- -- Smoke and Flare color for this zone is blue, it is active (can be used) and has a radio beacon. +-- my_ctld:AddCTLDZone("Loadzone",CTLD.CargoZoneType.LOAD,SMOKECOLOR.Blue,true,true) +-- +-- -- Add a zone of type DROP. Players can drop crates here. +-- -- Smoke and Flare color for this zone is blue, it is active (can be used) and has a radio beacon. +-- -- NOTE: Troops can be unloaded anywhere, also when hovering in parameters. +-- my_ctld:AddCTLDZone("Dropzone",CTLD.CargoZoneType.DROP,SMOKECOLOR.Red,true,true) +-- +-- -- Add two zones of type MOVE. Dropped troops and vehicles will move to the nearest one. See options. +-- -- Smoke and Flare color for this zone is blue, it is active (can be used) and has a radio beacon. +-- my_ctld:AddCTLDZone("Movezone",CTLD.CargoZoneType.MOVE,SMOKECOLOR.Orange,false,false) +-- +-- my_ctld:AddCTLDZone("Movezone2",CTLD.CargoZoneType.MOVE,SMOKECOLOR.White,true,true) +-- +-- +-- ## 2. Options +-- +-- The following options are available (with their defaults). Only set the ones you want changed: +-- +-- my_ctld.useprefix = true -- Adjust *before* starting CTLD. If set to false, *all* choppers of the coalition side will be enabled for CTLD. +-- my_ctld.CrateDistance = 30 -- List and Load crates in this radius only. +-- my_ctld.maximumHoverHeight = 15 -- Hover max this high to load. +-- my_ctld.minimumHoverHeight = 4 -- Hover min this low to load. +-- my_ctld.forcehoverload = true -- Crates (not: troops) can only be loaded while hovering. +-- my_ctld.hoverautoloading = true -- Crates in CrateDistance in a LOAD zone will be loaded automatically if space allows. +-- my_ctld.smokedistance = 2000 -- Smoke or flares can be request for zones this far away (in meters). +-- my_ctld.movetroopstowpzone = true -- Troops and vehicles will move to the nearest MOVE zone... +-- my_ctld.movetroopsdistance = 5000 -- .. but only if this far away (in meters) +-- my_ctld.smokedistance = 2000 -- Only smoke or flare zones if requesting player unit is this far away (in meters) +-- +-- ## 2.1 User functions +-- +-- ### 2.1.1 Adjust or add chopper unit-type capabilities +-- +-- Use this function to adjust what a heli type can or cannot do: +-- +-- -- E.g. update unit capabilities for testing. Please stay realistic in your mission design. +-- -- Make a Gazelle into a heavy truck, this type can load both crates and troops and eight of each type: +-- my_ctld:UnitCapabilities("SA342L", true, true, 8, 8) +-- +-- Default unit type capabilities are: +-- +-- ["SA342Mistral"] = {type="SA342Mistral", crates=false, troops=true, cratelimit = 0, trooplimit = 4}, +-- ["SA342L"] = {type="SA342L", crates=false, troops=true, cratelimit = 0, trooplimit = 2}, +-- ["SA342M"] = {type="SA342M", crates=false, troops=true, cratelimit = 0, trooplimit = 4}, +-- ["SA342Minigun"] = {type="SA342Minigun", crates=false, troops=true, cratelimit = 0, trooplimit = 2}, +-- ["UH-1H"] = {type="UH-1H", crates=true, troops=true, cratelimit = 1, trooplimit = 8}, +-- ["Mi-8MTV2"] = {type="Mi-8MTV2", crates=true, troops=true, cratelimit = 2, trooplimit = 12}, +-- ["Ka-50"] = {type="Ka-50", crates=false, troops=false, cratelimit = 0, trooplimit = 0}, +-- ["Mi-24P"] = {type="Mi-24P", crates=true, troops=true, cratelimit = 1, trooplimit = 8}, +-- ["Mi-24V"] = {type="Mi-24V", crates=true, troops=true, cratelimit = 1, trooplimit = 8}, +-- +-- +-- ### 2.1.2 Activate and deactivate zones +-- +-- Activate a zone: +-- +-- -- Activate zone called Name of type #CTLD.CargoZoneType ZoneType: +-- my_ctld:ActivateZone(Name,ZoneType) +-- +-- Deactivate a zone: +-- +-- -- Deactivate zone called Name of type #CTLD.CargoZoneType ZoneType: +-- my_ctld:DeactivateZone(Name,ZoneType) +-- +-- ## 3. Events +-- +-- The class comes with a number of FSM-based events that missions designers can use to shape their mission. +-- These are: +-- +-- ## 3.1 OnAfterTroopsPickedUp +-- +-- This function is called when a player has loaded Troops: +-- +-- function CTLD:OnAfterTroopsPickedUp(From, Event, To, Group, Unit, Cargo) +-- ... your code here ... +-- end +-- +-- ## 3.2 OnAfterCratesPickedUp +-- +-- This function is called when a player has picked up crates: +-- +-- function CTLD:OnAfterCratesPickedUp(From, Event, To, Group, Unit, Cargo) +-- ... your code here ... +-- end +-- +-- ## 3.3 OnAfterTroopsTroopsDeployed +-- +-- This function is called when a player has deployed troops into the field: +-- +-- function CTLD:OnAfterTroopsDeployed(From, Event, To, Group, Unit, Troops) +-- ... your code here ... +-- end +-- +-- ## 3.4 OnAfterTroopsCratesDropped +-- +-- This function is called when a player has deployed crates to a DROP zone: +-- +-- function CTLD:OnAfterCratesDropped(From, Event, To, Group, Unit, Cargotable) +-- ... your code here ... +-- end +-- +-- ## 3.5 OnAfterTroopsCratesBuild +-- +-- This function is called when a player has build a vehicle or FOB: +-- +-- function CTLD:OnAfterCratesBuild(From, Event, To, Group, Unit, Vehicle) +-- ... your code here ... +-- end +-- +-- ## 4. F10 Menu structure +-- +-- CTLD management menu is under the F10 top menu and called "CTLD" +-- +-- ## 4.1 Manage Crates +-- +-- Use this entry to get, load, list nearby, drop, and build crates. Also @see options. +-- +-- ## 4.2 Manage Troops +-- +-- Use this entry to load and drop troops. +-- +-- ## 4.3 List boarded cargo +-- +-- Lists what you have loaded. Shows load capabilities for number of crates and number of seats for troops. +-- +-- ## 4.4 Smoke & Flare zones nearby +-- +-- Does what it says. +-- +-- ## 4.5 List active zone beacons +-- +-- Lists active radio beacons for all zones, where zones are both active and have a beacon. @see `CTLD:AddCTLDZone()` +-- +-- ## 4.6 Show hover parameters +-- +-- Lists hover parameters and indicates if these are curently fulfilled. Also @see options on hover heights. +-- +-- @field #CTLD +CTLD = { + ClassName = "CTLD", + verbose = 2, + lid = "", + coalition = 1, + coalitiontxt = "blue", + PilotGroups = {}, -- #GROUP_SET of heli pilots + CtldUnits = {}, -- Table of helicopter #GROUPs + FreeVHFFrequencies = {}, -- Table of VHF + FreeUHFFrequencies = {}, -- Table of UHF + FreeFMFrequencies = {}, -- Table of FM + CargoCounter = 0, + dropOffZones = {}, + wpZones = {}, + Cargo_Troops = {}, -- generic troops objects + Cargo_Crates = {}, -- generic crate objects + Loaded_Cargo = {}, -- cargo aboard units + Spawned_Crates = {}, -- Holds objects for crates spawned generally + Spawned_Cargo = {}, -- Binds together spawned_crates and their CTLD_CARGO objects + CrateDistance = 30, -- list crates in this radius + debug = false, + wpZones = {}, + pickupZones = {}, + dropOffZones = {}, +} + +------------------------------ +-- DONE: Zone Checks +-- DONE: TEST Hover load and unload +-- DONE: Crate unload +-- DONE: Hover (auto-)load +-- TODO: (More) Housekeeping +-- DONE: Troops running to WP Zone +-- DONE: Zone Radio Beacons +-- DONE: Stats Running +------------------------------ + +--- Radio Beacons +-- @type CTLD.ZoneBeacon +-- @field #string name -- Name of zone for the coordinate +-- @field #number frequency -- in mHz +-- @field #number modulation -- i.e.radio.modulation.FM or radio.modulation.AM + +--- Zone Info. +-- @type CTLD.CargoZone +-- @field #string name Name of Zone. +-- @field #string color Smoke color for zone, e.g. SMOKECOLOR.Red. +-- @field #boolean active Active or not. +-- @field #string type Type of zone, i.e. load,drop,move +-- @field #boolean hasbeacon Create and run radio beacons if active. +-- @field #table fmbeacon Beacon info as #CTLD.ZoneBeacon +-- @field #table uhfbeacon Beacon info as #CTLD.ZoneBeacon +-- @field #table vhfbeacon Beacon info as #CTLD.ZoneBeacon + +--- Zone Type Info. +-- @type CTLD. +CTLD.CargoZoneType = { + LOAD = "load", + DROP = "drop", + MOVE = "move", +} + +--- Buildable table info. +-- @type CTLD.Buildable +-- @field #string Name Name of the object. +-- @field #number Required Required crates. +-- @field #number Found Found crates. +-- @field #table Template Template names for this build. +-- @field #boolean CanBuild Is buildable or not. +-- @field #CTLD_CARGO.Enum Type Type enumerator (for moves). + +--- Unit capabilities. +-- @type CTLD.UnitCapabilities +-- @field #string type Unit type. +-- @field #boolean crates Can transport crate. +-- @field #boolean troops Can transport troops. +-- @field #number cratelimit Number of crates transportable. +-- @field #number trooplimit Number of troop units transportable. +CTLD.UnitTypes = { + ["SA342Mistral"] = {type="SA342Mistral", crates=false, troops=true, cratelimit = 0, trooplimit = 4}, + ["SA342L"] = {type="SA342L", crates=false, troops=true, cratelimit = 0, trooplimit = 2}, + ["SA342M"] = {type="SA342M", crates=false, troops=true, cratelimit = 0, trooplimit = 4}, + ["SA342Minigun"] = {type="SA342Minigun", crates=false, troops=true, cratelimit = 0, trooplimit = 2}, + ["UH-1H"] = {type="UH-1H", crates=true, troops=true, cratelimit = 1, trooplimit = 8}, + ["Mi-8MTV2"] = {type="Mi-8MTV2", crates=true, troops=true, cratelimit = 2, trooplimit = 12}, + ["Ka-50"] = {type="Ka-50", crates=false, troops=false, cratelimit = 0, trooplimit = 0}, + ["Mi-24P"] = {type="Mi-24P", crates=true, troops=true, cratelimit = 2, trooplimit = 8}, + ["Mi-24V"] = {type="Mi-24V", crates=true, troops=true, cratelimit = 2, trooplimit = 8}, +} + +--- Updated and sorted known NDB beacons (in kHz!) from the available maps +-- @field #CTLD.SkipFrequencies +CTLD.SkipFrequencies = { + 214,274,291.5,295,297.5, + 300.5,304,307,309.5,311,312,312.5,316, + 320,324,328,329,330,336,337, + 342,343,348,351,352,353,358, + 363,365,368,372.5,374, + 380,381,384,389,395,396, + 414,420,430,432,435,440,450,455,462,470,485, + 507,515,520,525,528,540,550,560,570,577,580,602,625,641,662,670,680,682,690, + 705,720,722,730,735,740,745,750,770,795, + 822,830,862,866, + 905,907,920,935,942,950,995, + 1000,1025,1030,1050,1065,1116,1175,1182,1210 + } + +--- CTLD class version. +-- @field #string version +CTLD.version="0.1.1b1" + +--- Instantiate a new CTLD. +-- @param #CTLD self +-- @param #string Coalition Coalition of this CTLD. I.e. coalition.side.BLUE or coalition.side.RED or coalition.side.NEUTRAL +-- @param #table Prefixes Table of pilot prefixes. +-- @param #string Alias Alias of this CTLD for logging. +-- @return #CTLD self +function CTLD:New(Coalition, Prefixes, Alias) + -- TODO: CTLD Marker + -- Inherit everything from FSM class. + local self=BASE:Inherit(self, FSM:New()) -- #CTLD + + BASE:T({Coalition, Prefixes, Alias}) + + --set Coalition + if Coalition and type(Coalition)=="string" then + if Coalition=="blue" then + self.coalition=coalition.side.BLUE + self.coalitiontxt = Coalition + elseif Coalition=="red" then + self.coalition=coalition.side.RED + self.coalitiontxt = Coalition + elseif Coalition=="neutral" then + self.coalition=coalition.side.NEUTRAL + self.coalitiontxt = Coalition + else + self:E("ERROR: Unknown coalition in CTLD!") + end + else + self.coalition = Coalition + self.coalitiontxt = string.lower(UTILS.GetCoalitionName(self.coalition)) + end + + -- Set alias. + if Alias then + self.alias=tostring(Alias) + else + self.alias="UNHCR" + if self.coalition then + if self.coalition==coalition.side.RED then + self.alias="Red CTLD" + elseif self.coalition==coalition.side.BLUE then + self.alias="Blue CTLD" + end + end + end + + -- Set some string id for output to DCS.log file. + self.lid=string.format("%s (%s) | ", self.alias, self.coalition and UTILS.GetCoalitionName(self.coalition) or "unknown") + + -- Start State. + self:SetStartState("Stopped") + + -- Add FSM transitions. + -- From State --> Event --> To State + self:AddTransition("Stopped", "Start", "Running") -- Start FSM. + self:AddTransition("*", "Status", "*") -- CTLD status update. + self:AddTransition("*", "TroopsPickedUp", "*") -- CTLD pickup event. + self:AddTransition("*", "CratesPickedUp", "*") -- CTLD pickup event. + self:AddTransition("*", "TroopsDeployed", "*") -- CTLD deploy event. + self:AddTransition("*", "CratesDropped", "*") -- CTLD deploy event. + self:AddTransition("*", "CratesBuild", "*") -- CTLD build event. + self:AddTransition("*", "Stop", "Stopped") -- Stop FSM. + + -- tables + self.PilotGroups ={} + self.CtldUnits = {} + + -- Beacons + self.FreeVHFFrequencies = {} + self.FreeUHFFrequencies = {} + self.FreeFMFrequencies = {} + self.UsedVHFFrequencies = {} + self.UsedUHFFrequencies = {} + self.UsedFMFrequencies = {} + --self.jtacGeneratedLaserCodes = {} + + -- radio beacons + self.RadioSound = "beacon.ogg" + + -- zones stuff + self.pickupZones = {} + self.dropOffZones = {} + self.wpZones = {} + + -- Cargo + self.Cargo_Crates = {} + self.Cargo_Troops = {} + self.Loaded_Cargo = {} + self.Spawned_Crates = {} + self.Spawned_Cargo = {} + self.MenusDone = {} + self.DroppedTroops = {} + self.DroppedCrates = {} + self.CargoCounter = 0 + self.CrateCounter = 0 + self.TroopCounter = 0 + + -- setup + self.CrateDistance = 30 -- list/load crates in this radius + self.prefixes = Prefixes or {"cargoheli"} + self.useprefix = true + + self.maximumHoverHeight = 15 + self.minimumHoverHeight = 4 + self.forcehoverload = true + self.hoverautoloading = true + + self.smokedistance = 2000 + self.movetroopstowpzone = true + self.movetroopsdistance = 5000 + + for i=1,100 do + math.random() + end + + self:_GenerateVHFrequencies() + self:_GenerateUHFrequencies() + self:_GenerateFMFrequencies() + --self:_GenerateLaserCodes() -- curr unused + + ------------------------ + --- Pseudo Functions --- + ------------------------ + + --- Triggers the FSM event "Start". Starts the CTLD. Initializes parameters and starts event handlers. + -- @function [parent=#CTLD] Start + -- @param #CTLD self + + --- Triggers the FSM event "Start" after a delay. Starts the CTLD. Initializes parameters and starts event handlers. + -- @function [parent=#CTLD] __Start + -- @param #CTLD self + -- @param #number delay Delay in seconds. + + --- Triggers the FSM event "Stop". Stops the CTLD and all its event handlers. + -- @param #CTLD self + + --- Triggers the FSM event "Stop" after a delay. Stops the CTLD and all its event handlers. + -- @function [parent=#CTLD] __Stop + -- @param #CTLD self + -- @param #number delay Delay in seconds. + + --- Triggers the FSM event "Status". + -- @function [parent=#CTLD] Status + -- @param #CTLD self + + --- Triggers the FSM event "Status" after a delay. + -- @function [parent=#CTLD] __Status + -- @param #CTLD self + -- @param #number delay Delay in seconds. + + --- FSM Function OnAfterTroopsPickedUp. + -- @function [parent=#CTLD] OnAfterTroopsPickedUp + -- @param #CTLD self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Group#GROUP Group Group Object. + -- @param Wrapper.Unit#UNIT Unit Unit Object. + -- @param #CTLD_CARGO Cargo Cargo crate. + -- @return #CTLD self + + --- FSM Function OnAfterCratesPickedUp. + -- @function [parent=#CTLD] OnAfterCratesPickedUp + -- @param #CTLD self + -- @param #string From State . + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Group#GROUP Group Group Object. + -- @param Wrapper.Unit#UNIT Unit Unit Object. + -- @param #CTLD_CARGO Cargo Cargo crate. + -- @return #CTLD self + + --- FSM Function OnAfterTroopsDeployed. + -- @function [parent=#CTLD] OnAfterTroopsDeployed + -- @param #CTLD self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Group#GROUP Group Group Object. + -- @param Wrapper.Unit#UNIT Unit Unit Object. + -- @param Wrapper.Group#GROUP Troops Troops #GROUP Object. + -- @return #CTLD self + + --- FSM Function OnAfterCratesDropped. + -- @function [parent=#CTLD] OnAfterCratesDropped + -- @param #CTLD self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Group#GROUP Group Group Object. + -- @param Wrapper.Unit#UNIT Unit Unit Object. + -- @param #table Cargotable Table of #CTLD_CARGO objects dropped. + -- @return #CTLD self + + --- FSM Function OnAfterCratesBuild. + -- @function [parent=#CTLD] OnAfterCratesBuild + -- @param #CTLD self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Group#GROUP Group Group Object. + -- @param Wrapper.Unit#UNIT Unit Unit Object. + -- @param Wrapper.Group#GROUP Vehicle The #GROUP object of the vehicle or FOB build. + -- @return #CTLD self + + return self +end + +------------------------------------------------------------------- +-- Helper and User Functions +------------------------------------------------------------------- + +--- Function to generate valid UHF Frequencies +-- @param #CTLD self +function CTLD:_GenerateUHFrequencies() + self:T(self.lid .. " _GenerateUHFrequencies") + self.FreeUHFFrequencies = {} + local _start = 220000000 + + while _start < 399000000 do + table.insert(self.FreeUHFFrequencies, _start) + _start = _start + 500000 + end + + return self +end + +--- Function to generate valid FM Frequencies +-- @param #CTLD sel +function CTLD:_GenerateFMFrequencies() + self:T(self.lid .. " _GenerateFMrequencies") + self.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 + table.insert(self.FreeFMFrequencies, _frequency) + end + end + end + + return self +end + +--- Populate table with available VHF beacon frequencies. +-- @param #CTLD self +function CTLD:_GenerateVHFrequencies() + self:T(self.lid .. " _GenerateVHFrequencies") + local _skipFrequencies = self.SkipFrequencies + + self.FreeVHFFrequencies = {} + self.UsedVHFFrequencies = {} + + -- first range + local _start = 200000 + while _start < 400000 do + + -- skip existing NDB frequencies# + local _found = false + for _, value in pairs(_skipFrequencies) do + if value * 1000 == _start then + _found = true + break + end + end + if _found == false then + table.insert(self.FreeVHFFrequencies, _start) + end + _start = _start + 10000 + end + + -- second range + _start = 400000 + while _start < 850000 do + -- skip existing NDB frequencies + local _found = false + for _, value in pairs(_skipFrequencies) do + if value * 1000 == _start then + _found = true + break + end + end + if _found == false then + table.insert(self.FreeVHFFrequencies, _start) + end + _start = _start + 10000 + end + + -- third range + _start = 850000 + while _start <= 999000 do -- adjusted for Gazelle + -- skip existing NDB frequencies + local _found = false + for _, value in pairs(_skipFrequencies) do + if value * 1000 == _start then + _found = true + break + end + end + if _found == false then + table.insert(self.FreeVHFFrequencies, _start) + end + _start = _start + 50000 + end + + return self +end + +--- Function to generate valid laser codes. +-- @param #CTLD self +function CTLD:_GenerateLaserCodes() + self:T(self.lid .. " _GenerateLaserCodes") + self.jtacGeneratedLaserCodes = {} + -- generate list of laser codes + local _code = 1111 + local _count = 1 + while _code < 1777 and _count < 30 do + while true do + _code = _code + 1 + if not self:_ContainsDigit(_code, 8) + and not self:_ContainsDigit(_code, 9) + and not self:_ContainsDigit(_code, 0) then + table.insert(self.jtacGeneratedLaserCodes, _code) + break + end + end + _count = _count + 1 + end +end + +--- Helper function to generate laser codes. +-- @param #CTLD self +-- @param #number _number +-- @param #number _numberToFind +function CTLD:_ContainsDigit(_number, _numberToFind) + self:T(self.lid .. " _ContainsDigit") + local _thisNumber = _number + local _thisDigit = 0 + while _thisNumber ~= 0 do + _thisDigit = _thisNumber % 10 + _thisNumber = math.floor(_thisNumber / 10) + if _thisDigit == _numberToFind then + return true + end + end + return false +end + +--- Event handler function +-- @param #CTLD self +-- @param Core.Event#EVENTDATA EventData +function CTLD:_EventHandler(EventData) + -- TODO: events dead and playerleaveunit - nil table entries + self:T(string.format("%s Event = %d",self.lid, EventData.id)) + local event = EventData -- Core.Event#EVENTDATA + if event.id == EVENTS.PlayerEnterAircraft or event.id == EVENTS.PlayerEnterUnit then + local _coalition = event.IniCoalition + if _coalition ~= self.coalition then + return --ignore! + end + -- check is Helicopter + local _unit = event.IniUnit + local _group = event.IniGroup + if _unit:IsHelicopter() or _group:IsHelicopter() then + self:_RefreshF10Menus() + end + return + elseif event.id == EVENTS.PlayerLeaveUnit then + -- remove from pilot table + local unitname = event.IniUnitName or "none" + self.CtldUnits[unitname] = nil + end + return self +end + +--- Function to load troops into a heli. +-- @param #CTLD self +-- @param Wrapper.Group#GROUP Group +-- @param Wrapper.Unit#UNIT Unit +-- @param #CTLD_CARGO Cargotype +function CTLD:_LoadTroops(Group, Unit, Cargotype) + self:T(self.lid .. " _LoadTroops") + -- landed or hovering over load zone? + local grounded = not self:IsUnitInAir(Unit) + local hoverload = self:CanHoverLoad(Unit) + -- check if we are in LOAD zone + local inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD) + if not inzone then + local m = MESSAGE:New("You are not close enough to a logistics zone!",15,"CTLD"):ToGroup(Group) + if not self.debug then return self end + elseif not grounded and not hoverload then + local m = MESSAGE:New("You need to land or hover in position to load!",15,"CTLD"):ToGroup(Group) + if not self.debug then return self end + end + -- load troops into heli + local group = Group -- Wrapper.Group#GROUP + local unit = Unit -- Wrapper.Unit#UNIT + local unitname = unit:GetName() + local cargotype = Cargotype -- #CTLD_CARGO + local cratename = cargotype:GetName() -- #string + self:T(self.lid .. string.format("Troops %s requested", cratename)) + -- see if this heli can load troops + local unittype = unit:GetTypeName() + local capabilities = self.UnitTypes[unittype] -- #CTLD.UnitCapabilities + local cantroops = capabilities.troops -- #boolean + local trooplimit = capabilities.trooplimit -- #number + local troopsize = cargotype:GetCratesNeeded() -- #number + -- have we loaded stuff already? + local numberonboard = 0 + local loaded = {} + if self.Loaded_Cargo[unitname] then + loaded = self.Loaded_Cargo[unitname] -- #CTLD.LoadedCargo + numberonboard = loaded.Troopsloaded or 0 + else + loaded = {} -- #CTLD.LoadedCargo + loaded.Troopsloaded = 0 + loaded.Cratesloaded = 0 + loaded.Cargo = {} + end + if troopsize + numberonboard > trooplimit then + local m = MESSAGE:New("Sorry, we\'re crammed already!",10,"CTLD",true):ToGroup(group) + return + else + loaded.Troopsloaded = loaded.Troopsloaded + troopsize + table.insert(loaded.Cargo,Cargotype) + self.Loaded_Cargo[unitname] = loaded + local m = MESSAGE:New("Troops boarded!",10,"CTLD",true):ToGroup(group) + self:__TroopsPickedUp(1,Group, Unit, Cargotype) + end + return self +end + +--- Function to spawn crates in front of the heli. +-- @param #CTLD self +-- @param Wrapper.Group#GROUP Group +-- @param Wrapper.Unit#UNIT Unit +-- @param #CTLD_CARGO Cargo +-- @param #number number Number of crates to generate (for dropping) +-- @param #boolean drop If true we\'re dropping from heli rather than loading. +function CTLD:_GetCrates(Group, Unit, Cargo, number, drop) + self:T(self.lid .. " _GetCrates") + local cgoname = Cargo:GetName() + self:T({cgoname, number, drop}) + -- check if we are in LOAD zone + local inzone = true + + if drop then + local inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.DROP) + else + local inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD) + end + + if not inzone then + local m = MESSAGE:New("You are not close enough to a logistics zone!",15,"CTLD"):ToGroup(Group) + if not self.debug then return self end + end + + -- avoid crate spam + local capabilities = self.UnitTypes[Unit:GetTypeName()] -- #CTLD.UnitCapabilities + local canloadcratesno = capabilities.cratelimit + local loaddist = self.CrateDistance or 30 + local nearcrates, numbernearby = self:_FindCratesNearby(Group,Unit,loaddist) + if numbernearby >= canloadcratesno and not drop then + local m = MESSAGE:New("There are enough crates nearby already! Take care of those first!",15,"CTLD"):ToGroup(Group) + return self + end + -- spawn crates in front of helicopter + local cargotype = Cargo -- #CTLD_CARGO + local number = number or cargotype:GetCratesNeeded() --#number + local cratesneeded = cargotype:GetCratesNeeded() --#number + local cratename = cargotype:GetName() + self:T(self.lid .. string.format("Crate %s requested", cratename)) + local cratetemplate = "Container"-- #string + -- get position and heading of heli + local position = Unit:GetCoordinate() + local heading = Unit:GetHeading() + 1 + local height = Unit:GetHeight() + local droppedcargo = {} + -- loop crates needed + for i=1,number do + local cratealias = string.format("%s-%d", cratetemplate, math.random(1,100000)) + local cratedistance = i*4 + 6 + local rheading = math.floor(math.random(90,270) * heading + 1 / 360) + local rheading = rheading + 180 -- mirror + if rheading > 360 then rheading = rheading - 360 end -- catch > 360 + local cratecoord = position:Translate(cratedistance,rheading) + local cratevec2 = cratecoord:GetVec2() + self.CrateCounter = self.CrateCounter + 1 + self.Spawned_Crates[self.CrateCounter] = SPAWNSTATIC:NewFromType("container_cargo","Cargos",country.id.GERMANY) + :InitCoordinate(cratecoord) + :Spawn(270,cratealias) + + local templ = cargotype:GetTemplates() + local sorte = cargotype:GetType() + self.CargoCounter = self.CargoCounter +1 + local realcargo = nil + if drop then + realcargo = CTLD_CARGO:New(self.CargoCounter,cratename,templ,sorte,true,false,cratesneeded,self.Spawned_Crates[self.CrateCounter],true) + table.insert(droppedcargo,realcargo) + else + realcargo = CTLD_CARGO:New(self.CargoCounter,cratename,templ,sorte,false,false,cratesneeded,self.Spawned_Crates[self.CrateCounter]) + end + table.insert(self.Spawned_Cargo, realcargo) + end + local text = string.format("Crates for %s have been positioned near you!",cratename) + if drop then + text = string.format("Crates for %s have been dropped!",cratename) + self:__CratesDropped(1, Group, Unit, droppedcargo) + end + local m = MESSAGE:New(text,15,"CTLD",true):ToGroup(Group) + return self +end + +--- Function to find and list nearby crates. +-- @param #CTLD self +-- @param Wrapper.Group#GROUP Group +-- @param Wrapper.Unit#UNIT Unit +-- @return #CTLD self +function CTLD:_ListCratesNearby( _group, _unit) + self:T(self.lid .. " _ListCratesNearby") + local finddist = self.CrateDistance or 30 + local crates,number = self:_FindCratesNearby(_group,_unit, finddist) -- #table + if number > 0 then + local text = REPORT:New("Crates Found Nearby:") + text:Add("------------------------------------------------------------") + for _,_entry in pairs (crates) do + local entry = _entry -- #CTLD_CARGO + local name = entry:GetName() --#string + -- TODO Meaningful sorting/aggregation + local dropped = entry:WasDropped() + if dropped then + text:Add(string.format("Dropped crate for %s",name)) + else + text:Add(string.format("Crate for %s",name)) + end + end + if text:GetCount() == 1 then + text:Add("--------- N O N E ------------") + end + text:Add("------------------------------------------------------------") + local m = MESSAGE:New(text:Text(),15,"CTLD",true):ToGroup(_group) + else + local m = MESSAGE:New(string.format("No (loadable) crates within %d meters!",finddist),15,"CTLD",true):ToGroup(_group) + end + return self +end + +--- Return distance in meters between two coordinates. +-- @param #CTLD self +-- @param Core.Point#COORDINATE _point1 Coordinate one +-- @param Core.Point#COORDINATE _point2 Coordinate two +-- @return #number Distance in meters +function CTLD:_GetDistance(_point1, _point2) + self:T(self.lid .. " _GetDistance") + if _point1 and _point2 then + local distance = _point1:DistanceFromPointVec2(_point2) + return distance + else + return -1 + end +end + +--- Function to find and return nearby crates. +-- @param #CTLD self +-- @param Wrapper.Group#GROUP _group Group +-- @param Wrapper.Unit#UNIT _unit Unit +-- @param #number _dist Distance +-- @return #table Table of crates +-- @return #number Number Number of crates found +function CTLD:_FindCratesNearby( _group, _unit, _dist) + self:T(self.lid .. " _FindCratesNearby") + local finddist = _dist + local location = _group:GetCoordinate() + local existingcrates = self.Spawned_Cargo -- #table + -- cycle + local index = 0 + local found = {} + for _,_cargoobject in pairs (existingcrates) do + local cargo = _cargoobject -- #CTLD_CARGO + local static = cargo:GetPositionable() -- Wrapper.Static#STATIC -- crates + local staticid = cargo:GetID() + if static and static:IsAlive() then + local staticpos = static:GetCoordinate() + local distance = self:_GetDistance(location,staticpos) + if distance <= finddist and static then + index = index + 1 + table.insert(found, staticid, cargo) + end + end + end + self:T(string.format("Found crates = %d",index)) + -- table.sort(found) + --self:T({found}) + return found, index +end + +--- Function to get and load nearby crates. +-- @param #CTLD self +-- @param Wrapper.Group#GROUP Group +-- @param Wrapper.Unit#UNIT Unit +-- @return #CTLD self +function CTLD:_LoadCratesNearby(Group, Unit) + self:T(self.lid .. " _LoadCratesNearby") + -- load crates into heli + local group = Group -- Wrapper.Group#GROUP + local unit = Unit -- Wrapper.Unit#UNIT + local unitname = unit:GetName() + -- see if this heli can load crates + local unittype = unit:GetTypeName() + local capabilities = self.UnitTypes[unittype] -- #CTLD.UnitCapabilities + local cancrates = capabilities.crates -- #boolean + local cratelimit = capabilities.cratelimit -- #number + local grounded = not self:IsUnitInAir(Unit) + local canhoverload = self:CanHoverLoad(Unit) + --- cases ------------------------------- + -- Chopper can\'t do crates - bark & return + -- Chopper can do crates - + -- --> hover if forcedhover or bark and return + -- --> hover or land if not forcedhover + ----------------------------------------- + if not cancrates then + local m = MESSAGE:New("Sorry this chopper cannot carry crates!",10,"CTLD"):ToGroup(Group) + elseif self.forcehoverload and not canhoverload then + local m = MESSAGE:New("Hover over the crates to pick them up!",10,"CTLD"):ToGroup(Group) + elseif not grounded and not canhoverload then + local m = MESSAGE:New("Land or hover over the crates to pick them up!",10,"CTLD"):ToGroup(Group) + else + -- have we loaded stuff already? + local numberonboard = 0 + local loaded = {} + if self.Loaded_Cargo[unitname] then + loaded = self.Loaded_Cargo[unitname] -- #CTLD.LoadedCargo + numberonboard = loaded.Cratesloaded or 0 + else + loaded = {} -- #CTLD.LoadedCargo + loaded.Troopsloaded = 0 + loaded.Cratesloaded = 0 + loaded.Cargo = {} + end + -- get nearby crates + local finddist = self.CrateDistance or 30 + local nearcrates,number = self:_FindCratesNearby(Group,Unit,finddist) -- #table + if number == 0 or numberonboard == cratelimit then + local m = MESSAGE:New("Sorry no loadable crates nearby or fully loaded!",10,"CTLD"):ToGroup(Group) + return -- exit + else + -- go through crates and load + local capacity = cratelimit - numberonboard + local crateidsloaded = {} + local loops = 0 + while loaded.Cratesloaded < cratelimit and loops < number do + --for _ind,_crate in pairs (nearcrates) do + loops = loops + 1 + local crateind = 0 + -- get crate with largest index + for _ind,_crate in pairs (nearcrates) do + if not _crate:HasMoved() and not _crate:WasDropped() and _crate:GetID() > crateind then + --crate = _crate + crateind = _crate:GetID() + end + end + -- load one if we found one + if crateind > 0 then + local crate = nearcrates[crateind] -- #CTLD_CARGO + loaded.Cratesloaded = loaded.Cratesloaded + 1 + crate:SetHasMoved(true) + table.insert(loaded.Cargo, crate) + table.insert(crateidsloaded,crate:GetID()) + -- destroy crate + crate:GetPositionable():Destroy() + crate.Positionable = nil + local m = MESSAGE:New(string.format("Crate ID %d for %s loaded!",crate:GetID(),crate:GetName()),10,"CTLD"):ToGroup(Group) + self:__CratesPickedUp(1, Group, Unit, crate) + end + --if loaded.Cratesloaded == cratelimit then break end + end + self.Loaded_Cargo[unitname] = loaded + -- clean up real world crates + local existingcrates = self.Spawned_Cargo -- #table + local newexcrates = {} + for _,_crate in pairs(existingcrates) do + local excrate = _crate -- #CTLD_CARGO + local ID = excrate:GetID() + for _,_ID in pairs(crateidsloaded) do + if ID ~= _ID then + table.insert(newexcrates,_crate) + end + end + end + self.Spawned_Cargo = nil + self.Spawned_Cargo = newexcrates + end + end + return self +end + +--- Function to list loaded cargo. +-- @param #CTLD self +-- @param Wrapper.Group#GROUP Group +-- @param Wrapper.Unit#UNIT Unit +-- @return #CTLD self +function CTLD:_ListCargo(Group, Unit) + self:T(self.lid .. " _ListCargo") + local unitname = Unit:GetName() + local unittype = Unit:GetTypeName() + local capabilities = self.UnitTypes[unittype] -- #CTLD.UnitCapabilities + local trooplimit = capabilities.trooplimit -- #boolean + local cratelimit = capabilities.cratelimit -- #numbe + local loadedcargo = self.Loaded_Cargo[unitname] or {} -- #CTLD.LoadedCargo + if self.Loaded_Cargo[unitname] then + local no_troops = loadedcargo.Troopsloaded or 0 + local no_crates = loadedcargo.Cratesloaded or 0 + local cargotable = loadedcargo.Cargo or {} -- #table + local report = REPORT:New("Transport Checkout Sheet") + report:Add("------------------------------------------------------------") + report:Add(string.format("Troops: %d(%d), Crates: %d(%d)",no_troops,trooplimit,no_crates,cratelimit)) + report:Add("------------------------------------------------------------") + report:Add("-- TROOPS --") + for _,_cargo in pairs(cargotable) do + local cargo = _cargo -- #CTLD_CARGO + local type = cargo:GetType() -- #CTLD_CARGO.Enum + if type == CTLD_CARGO.Enum.TROOPS and not cargo:WasDropped() then + report:Add(string.format("Troop: %s size %d",cargo:GetName(),cargo:GetCratesNeeded())) + end + end + if report:GetCount() == 4 then + report:Add("--------- N O N E ------------") + end + report:Add("------------------------------------------------------------") + report:Add("-- CRATES --") + local cratecount = 0 + for _,_cargo in pairs(cargotable) do + local cargo = _cargo -- #CTLD_CARGO + local type = cargo:GetType() -- #CTLD_CARGO.Enum + if type ~= CTLD_CARGO.Enum.TROOPS then + report:Add(string.format("Crate: %s size 1",cargo:GetName())) + cratecount = cratecount + 1 + end + end + if cratecount == 0 then + report:Add("--------- N O N E ------------") + end + report:Add("------------------------------------------------------------") + local text = report:Text() + local m = MESSAGE:New(text,30,"CTLD",true):ToGroup(Group) + else + local m = MESSAGE:New(string.format("Nothing loaded!\nTroop limit: %d | Crate limit %d",trooplimit,cratelimit),10,"CTLD"):ToGroup(Group) + end + return self +end + +--- Function to unload troops from heli. +-- @param #CTLD self +-- @param Wrapper.Group#GROUP Group +-- @param Wrapper.Unit#UNIT Unit +function CTLD:_UnloadTroops(Group, Unit) + self:T(self.lid .. " _UnloadTroops") + -- check if we are in LOAD zone + local droppingatbase = false + local inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD) + if inzone then + droppingatbase = true + end + -- check for hover unload + local hoverunload = self:IsCorrectHover(Unit) --if true we\'re hovering in parameters + -- check if we\'re landed + local grounded = not self:IsUnitInAir(Unit) + -- Get what we have loaded + local unitname = Unit:GetName() + if self.Loaded_Cargo[unitname] and (grounded or hoverunload) then + if not droppingatbase or self.debug then + local loadedcargo = self.Loaded_Cargo[unitname] or {} -- #CTLD.LoadedCargo + -- looking for troops + local cargotable = loadedcargo.Cargo + for _,_cargo in pairs (cargotable) do + local cargo = _cargo -- #CTLD_CARGO + local type = cargo:GetType() -- #CTLD_CARGO.Enum + if type == CTLD_CARGO.Enum.TROOPS and not cargo:WasDropped() then + -- unload troops + local name = cargo:GetName() or "none" + local temptable = cargo:GetTemplates() or {} + local position = Group:GetCoordinate() + local zone = ZONE_GROUP:New(string.format("Unload zone-%s",unitname),Group,100) + local randomcoord = zone:GetRandomCoordinate(10,30):GetVec2() + for _,_template in pairs(temptable) do + self.TroopCounter = self.TroopCounter + 1 + local alias = string.format("%s-%d", _template, math.random(1,100000)) + self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias) + :InitRandomizeUnits(true,20,2) + :InitDelayOff() + :SpawnFromVec2(randomcoord) + if self.movetroopstowpzone then + self:_MoveGroupToZone(self.DroppedTroops[self.TroopCounter]) + end + end -- template loop + cargo:SetWasDropped(true) + local m = MESSAGE:New(string.format("Dropped Troops %s into action!",name),10,"CTLD"):ToGroup(Group) + self:__TroopsDeployed(1, Group, Unit, name, self.DroppedTroops[self.TroopCounter]) + end -- if type end + end -- cargotable loop + else -- droppingatbase + local m = MESSAGE:New("Troops have returned to base!",15,"CTLD"):ToGroup(Group) + end + -- cleanup load list + local loaded = {} -- #CTLD.LoadedCargo + loaded.Troopsloaded = 0 + loaded.Cratesloaded = 0 + loaded.Cargo = {} + local loadedcargo = self.Loaded_Cargo[unitname] or {} -- #CTLD.LoadedCargo + local cargotable = loadedcargo.Cargo or {} + for _,_cargo in pairs (cargotable) do + local cargo = _cargo -- #CTLD_CARGO + local type = cargo:GetType() -- #CTLD_CARGO.Enum + local dropped = cargo:WasDropped() + --local moved = cargo:HasMoved() + if type ~= CTLD_CARGO.Enum.TROOP and not dropped then + table.insert(loaded.Cargo,_cargo) + loaded.Cratesloaded = loaded.Cratesloaded + 1 + end + end + self.Loaded_Cargo[unitname] = nil + self.Loaded_Cargo[unitname] = loaded + else + local m = MESSAGE:New("Nothing loaded or not hovering within parameters!",10,"CTLD"):ToGroup(Group) + end + return self +end + +--- Function to unload crates from heli. +-- @param #CTLD self +-- @param Wrapper.Group#GROUP Group +-- @param Wrappe.Unit#UNIT Unit +function CTLD:_UnloadCrates(Group, Unit) + self:T(self.lid .. " _UnloadCrates") + -- check if we are in DROP zone + local inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.DROP) + if not inzone then + local m = MESSAGE:New("You are not close enough to a drop zone!",15,"CTLD"):ToGroup(Group) + if not self.debug then + return self + end + end + -- check for hover unload + local hoverunload = self:IsCorrectHover(Unit) --if true we\'re hovering in parameters + -- check if we\'re landed + local grounded = not self:IsUnitInAir(Unit) + -- Get what we have loaded + local unitname = Unit:GetName() + if self.Loaded_Cargo[unitname] and (grounded or hoverunload) then + local loadedcargo = self.Loaded_Cargo[unitname] or {} -- #CTLD.LoadedCargo + -- looking for troops + local cargotable = loadedcargo.Cargo + for _,_cargo in pairs (cargotable) do + local cargo = _cargo -- #CTLD_CARGO + local type = cargo:GetType() -- #CTLD_CARGO.Enum + if type ~= CTLD_CARGO.Enum.TROOPS and not cargo:WasDropped() then + -- unload crates + self:_GetCrates(Group, Unit, cargo, 1, true) + cargo:SetWasDropped(true) + cargo:SetHasMoved(true) + --local name cargo:GetName() + --local m = MESSAGE:New(string.format("Dropped Crate for %s!",name),10,"CTLD"):ToGroup(Group) + end + end + -- cleanup load list + local loaded = {} -- #CTLD.LoadedCargo + loaded.Troopsloaded = 0 + loaded.Cratesloaded = 0 + loaded.Cargo = {} + for _,_cargo in pairs (cargotable) do + local cargo = _cargo -- #CTLD_CARGO + local type = cargo:GetType() -- #CTLD_CARGO.Enum + local size = cargo:GetCratesNeeded() + if type == CTLD_CARGO.Enum.TROOP then + table.insert(loaded.Cargo,_cargo) + loaded.Cratesloaded = loaded.Troopsloaded + size + end + end + self.Loaded_Cargo[unitname] = nil + self.Loaded_Cargo[unitname] = loaded + else + local m = MESSAGE:New("Nothing loaded or not hovering within parameters!",10,"CTLD"):ToGroup(Group) + end + return self +end + +--- Function to build nearby crates. +-- @param #CTLD self +-- @param Wrapper.Group#GROUP Group +-- @param Wrappe.Unit#UNIT Unit +function CTLD:_BuildCrates(Group, Unit) + self:T(self.lid .. " _BuildCrates") + -- get nearby crates + local finddist = self.CrateDistance or 30 + local crates,number = self:_FindCratesNearby(Group,Unit, finddist) -- #table + local buildables = {} + local foundbuilds = false + local canbuild = false + if number > 0 then + -- get dropped crates + for _,_crate in pairs(crates) do + local Crate = _crate -- #CTLD_CARGO + if Crate:WasDropped() then + -- we can build these - maybe + local name = Crate:GetName() + local required = Crate:GetCratesNeeded() + local template = Crate:GetTemplates() + local ctype = Crate:GetType() + if not buildables[name] then + local object = {} -- #CTLD.Buildable + object.Name = name + object.Required = required + object.Found = 1 + object.Template = template + object.CanBuild = false + object.Type = ctype -- #CTLD_CARGO.Enum + buildables[name] = object + foundbuilds = true + else + buildables[name].Found = buildables[name].Found + 1 + if buildables[name].Found >= buildables[name].Required then + buildables[name].CanBuild = true + canbuild = true + end + foundbuilds = true + end + self:T({buildables = buildables}) + end -- end dropped + end -- end crate loop + -- ok let\'s list what we have + local report = REPORT:New("Checklist Buildable Crates") + report:Add("------------------------------------------------------------") + for _,_build in pairs(buildables) do + local build = _build -- Object table from above + local name = build.Name + local needed = build.Required + local found = build.Found + local txtok = "NO" + if build.CanBuild then + txtok = "YES" + end + self:T({name,needed,found,txtok}) + local text = string.format("Type: %s | Required %d | Found %d | Can Build %s", name, needed, found, txtok) + report:Add(text) + end -- end list buildables + if not foundbuilds then report:Add(" --- None Found ---") end + report:Add("------------------------------------------------------------") + local text = report:Text() + local m = MESSAGE:New(text,30,"CTLD",true):ToGroup(Group) + -- let\'s get going + if canbuild then + -- loop again + for _,_build in pairs(buildables) do + local build = _build -- #CTLD.Buildable + if build.CanBuild then + self:_CleanUpCrates(crates,build,number) + self:_BuildObjectFromCrates(Group,Unit,build) + end + end + end + else + local m = MESSAGE:New(string.format("No crates within %d meters!",finddist),15,"CTLD",true):ToGroup(Group) + end -- number > 0 + return self +end + +--- Function to actually SPAWN buildables in the mission. +-- @param #CTLD self +-- @param Wrapper.Group#GROUP Group +-- @param Wrapper.Group#UNIT Unit +-- @param #CTLD.Buildable Build +function CTLD:_BuildObjectFromCrates(Group,Unit,Build) + self:T(self.lid .. " _BuildObjectFromCrates") + -- Spawn-a-crate-content + local position = Unit:GetCoordinate() or Group:GetCoordinate() + local unitname = Unit:GetName() or Group:GetName() + local name = Build.Name + local type = Build.Type -- #CTLD_CARGO.Enum + local canmove = false + if type == CTLD_CARGO.Enum.VEHICLE then canmove = true end + local temptable = Build.Template or {} + local zone = ZONE_GROUP:New(string.format("Unload zone-%s",unitname),Group,100) + local randomcoord = zone:GetRandomCoordinate(20,50):GetVec2() + for _,_template in pairs(temptable) do + self.TroopCounter = self.TroopCounter + 1 + local alias = string.format("%s-%d", _template, math.random(1,100000)) + self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias) + :InitRandomizeUnits(true,20,2) + :InitDelayOff() + :SpawnFromVec2(randomcoord) + if self.movetroopstowpzone and canmove then + self:_MoveGroupToZone(self.DroppedTroops[self.TroopCounter]) + end + self:__CratesBuild(1,Group,Unit,self.DroppedTroops[self.TroopCounter]) + end -- template loop + return self +end + +--- Function to move group to WP zone. +-- @param #CTLD self +-- @param Wrapper.Group#GROUP Group The Group to move. +function CTLD:_MoveGroupToZone(Group) + self:T(self.lid .. " _MoveGroupToZone") + local groupname = Group:GetName() or "none" + local groupcoord = Group:GetCoordinate() + self:T(self.lid .. " _MoveGroupToZone for " .. groupname) + -- Get closest zone of type + local outcome, name, zone, distance = self:IsUnitInZone(Group,CTLD.CargoZoneType.MOVE) + self:T(string.format("Closest WP zone %s is %d meters",name,distance)) + if (distance <= self.movetroopsdistance) and zone then + -- yes, we can ;) + local groupname = Group:GetName() + self:T(string.format("Moving troops %s to zone %s, distance %d!",groupname,name,distance)) + local zonecoord = zone:GetRandomCoordinate(20,125) -- Core.Point#COORDINATE + local coordinate = zonecoord:GetVec2() + self:T({coordinate=coordinate}) + Group:SetAIOn() + Group:OptionAlarmStateAuto() + Group:OptionDisperseOnAttack(30) + Group:OptionROEOpenFirePossible() + Group:RouteToVec2(coordinate,5) + end + return self +end + +--- Housekeeping - Cleanup crates when build +-- @param #CTLD self +-- @param #table Crates Table of #CTLD_CARGO objects near the unit. +-- @param #CTLD.Buildable Build Table build object. +-- @param #number Number Number of objects in Crates (found) to limit search. +function CTLD:_CleanUpCrates(Crates,Build,Number) + self:T(self.lid .. " _CleanUpCrates") + -- clean up real world crates + local build = Build -- #CTLD.Buildable + self:T({Build = Build}) + local existingcrates = self.Spawned_Cargo -- #table of exising crates + local newexcrates = {} + -- get right number of crates to destroy + local numberdest = Build.Required + local nametype = Build.Name + local found = 0 + local rounds = Number + local destIDs = {} + + -- loop and find matching IDs in the set + for _,_crate in pairs(Crates) do + local nowcrate = _crate -- #CTLD_CARGO + local name = nowcrate:GetName() + self:T(string.format("Looking for Crate for %s", name)) + local thisID = nowcrate:GetID() + if name == nametype then -- matching crate type + table.insert(destIDs,thisID) + found = found + 1 + nowcrate:GetPositionable():Destroy() + nowcrate.Positionable = nil + self:T(string.format("%s Found %d Need %d", name, found, numberdest)) + end + if found == numberdest then break end -- got enough + end + self:T({destIDs}) + -- loop and remove from real world representation + for _,_crate in pairs(existingcrates) do + local excrate = _crate -- #CTLD_CARGO + local ID = excrate:GetID() + for _,_ID in pairs(destIDs) do + if ID ~= _ID then + table.insert(newexcrates,_crate) + end + end + end + + -- reset Spawned_Cargo + self.Spawned_Cargo = nil + self.Spawned_Cargo = newexcrates + return self +end + +--- Housekeeping - Function to refresh F10 menus. +-- @param #CTLD self +-- @return #CTLD self +function CTLD:_RefreshF10Menus() + self:T(self.lid .. " _RefreshF10Menus") + local PlayerSet = self.PilotGroups -- Core.Set#SET_GROUP + local PlayerTable = PlayerSet:GetSetObjects() -- #table of #GROUP objects + + -- rebuild units table + local _UnitList = {} + for _key, _group in pairs (PlayerTable) do + local _unit = _group:GetUnit(1) -- Asume that there is only one unit in the flight for players + if _unit then + if _unit:IsAlive() then + local unitName = _unit:GetName() + _UnitList[unitName] = unitName + end -- end isAlive + end -- end if _unit + end -- end for + self.CtldUnits = _UnitList + + -- build unit menus + + local menucount = 0 + local menus = {} + for _, _unitName in pairs(self.CtldUnits) do + if not self.MenusDone[_unitName] then + local _unit = UNIT:FindByName(_unitName) -- Wrapper.Unit#UNIT + if _unit then + local _group = _unit:GetGroup() -- Wrapper.Group#GROUP + if _group then + -- get chopper capabilities + local unittype = _unit:GetTypeName() + local capabilities = self.UnitTypes[unittype] -- #CTLD.UnitCapabilities + local cantroops = capabilities.troops + local cancrates = capabilities.crates + -- top menu + local topmenu = MENU_GROUP:New(_group,"CTLD",nil) + local topcrates = MENU_GROUP:New(_group,"Manage Crates",topmenu) + local toptroops = MENU_GROUP:New(_group,"Manage Troops",topmenu) + local listmenu = MENU_GROUP_COMMAND:New(_group,"List boarded cargo",topmenu, self._ListCargo, self, _group, _unit) + local smokemenu = MENU_GROUP_COMMAND:New(_group,"Smoke zones nearby",topmenu, self.SmokeZoneNearBy, self, _unit, false) + local smokemenu = MENU_GROUP_COMMAND:New(_group,"Flare zones nearby",topmenu, self.SmokeZoneNearBy, self, _unit, true):Refresh() + -- sub menus + -- sub menu crates management + if cancrates then + local loadmenu = MENU_GROUP_COMMAND:New(_group,"Load crates",topcrates, self._LoadCratesNearby, self, _group, _unit) + local cratesmenu = MENU_GROUP:New(_group,"Get Crates",topcrates) + for _,_entry in pairs(self.Cargo_Crates) do + local entry = _entry -- #CTLD_CARGO + menucount = menucount + 1 + local menutext = string.format("Get crate for %s",entry.Name) + menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,cratesmenu,self._GetCrates, self, _group, _unit, entry) + end + listmenu = MENU_GROUP_COMMAND:New(_group,"List crates nearby",topcrates, self._ListCratesNearby, self, _group, _unit) + local unloadmenu = MENU_GROUP_COMMAND:New(_group,"Drop crates",topcrates, self._UnloadCrates, self, _group, _unit) + local buildmenu = MENU_GROUP_COMMAND:New(_group,"Build crates",topcrates, self._BuildCrates, self, _group, _unit):Refresh() + end + -- sub menu troops management + if cantroops then + local troopsmenu = MENU_GROUP:New(_group,"Load troops",toptroops) + for _,_entry in pairs(self.Cargo_Troops) do + local entry = _entry -- #CTLD_CARGO + menucount = menucount + 1 + menus[menucount] = MENU_GROUP_COMMAND:New(_group,entry.Name,troopsmenu,self._LoadTroops, self, _group, _unit, entry) + end + local unloadmenu1 = MENU_GROUP_COMMAND:New(_group,"Drop troops",toptroops, self._UnloadTroops, self, _group, _unit):Refresh() + end + local rbcns = MENU_GROUP_COMMAND:New(_group,"List active zone beacons",topmenu, self._ListRadioBeacons, self, _group, _unit) + local hoverpars = MENU_GROUP_COMMAND:New(_group,"Show hover parameters",topmenu, self._ShowHoverParams, self, _group, _unit):Refresh() + self.MenusDone[_unitName] = true + end -- end group + end -- end unit + else -- menu build check + self:T(self.lid .. " Menus already done for this group!") + end -- end menu build check + end -- end for + return self + end + +--- User function - Add *generic* troop type loadable as cargo. This type will load directly into the heli without crates. +-- @param #CTLD self +-- @param #Name Name Unique name of this type of troop. E.g. "Anti-Air Small". +-- @param #Table Templates Table of #string names of late activated Wrapper.Group#GROUP making up this troop. +-- @param #CTLD_CARGO.Enum Type Type of cargo, here TROOPS - these will move to a nearby destination zone when dropped/build. +-- @param #number NoTroops Size of the group in number of Units across combined templates (for loading). +function CTLD:AddTroopsCargo(Name,Templates,Type,NoTroops) + self:T(self.lid .. " AddTroopsCargo") + self.CargoCounter = self.CargoCounter + 1 + -- Troops are directly loadable + local cargo = CTLD_CARGO:New(self.CargoCounter,Name,Templates,Type,false,true,NoTroops) + table.insert(self.Cargo_Troops,cargo) + return self +end + +--- User function - Add *generic* crate-type loadable as cargo. This type will create crates that need to be loaded, moved, dropped and built. +-- @param #CTLD self +-- @param #Name Name Unique name of this type of cargo. E.g. "Humvee". +-- @param #Table Templates Table of #string names of late activated Wrapper.Group#GROUP building this cargo. +-- @param #CTLD_CARGO.Enum Type Type of cargo. I.e. VEHICLE or FOB. VEHICLE will move to destination zones when dropped/build, FOB stays put. +-- @param #number NoCrates Number of crates needed to build this cargo. +function CTLD:AddCratesCargo(Name,Templates,Type,NoCrates) + self:T(self.lid .. " AddCratesCargo") + self.CargoCounter = self.CargoCounter + 1 + -- Crates are not directly loadable + local cargo = CTLD_CARGO:New(self.CargoCounter,Name,Templates,Type,false,false,NoCrates) + table.insert(self.Cargo_Crates,cargo) + return self +end + +--- User function - Add a #CTLD.CargoZoneType zone for this CTLD instance. +-- @param #CTLD self +-- @param #CTLD.CargoZone Zone Zone #CTLD.CargoZone describing the zone. +function CTLD:AddZone(Zone) + self:T(self.lid .. " AddZone") + local zone = Zone -- #CTLD.CargoZone + if zone.type == CTLD.CargoZoneType.LOAD then + table.insert(self.pickupZones,zone) + self:T("Registered LOAD zone " .. zone.name) + elseif zone.type == CTLD.CargoZoneType.DROP then + table.insert(self.dropOffZones,zone) + self:T("Registered DROP zone " .. zone.name) + else + table.insert(self.wpZones,zone) + self:T("Registered MOVE zone " .. zone.name) + end + return self +end + +--- User function - Activate Name #CTLD.CargoZone.Type ZoneType for this CTLD instance. +-- @param #CTLD self +-- @param #string Name Name of the zone to change in the ME. +-- @param #CTLD.CargoZoneTyp ZoneType Type of zone this belongs to. +-- @param #boolean NewState (Optional) Set to true to activate, false to switch off. +function CTLD:ActivateZone(Name,ZoneType,NewState) + self:T(self.lid .. " AddZone") + local newstate = true + -- set optional in case we\'re deactivating + if not NewState or NewState == false then + newstate = false + end + -- get correct table + local zone = ZoneType -- #CTLD.CargoZone + local table = {} + if zone.type == CTLD.CargoZoneType.LOAD then + table = self.pickupZones + elseif zone.type == CTLD.CargoZoneType.DROP then + table = self.dropOffZones + else + table = self.wpZones + end + -- loop table + for _,_zone in pairs(table) do + local thiszone = _zone --#CTLD.CargoZone + if thiszone.name == Name then + thiszone.active = newstate + break + end + end + return self +end + +--- User function - Deactivate Name #CTLD.CargoZoneType ZoneType for this CTLD instance. +-- @param #CTLD self +-- @param #string Name Name of the zone to change in the ME. +-- @param #CTLD.CargoZoneTyp ZoneType Type of zone this belongs to. +function CTLD:DeactivateZone(Name,ZoneType) + self:T(self.lid .. " AddZone") + self:ActivateZone(Name,ZoneType,false) + return self +end + +--- Function to obtain a valid FM frequency. +-- @param #CTLD self +-- @param #string Name Name of zone. +-- @return #CTLD.ZoneBeacon Beacon Beacon table. +function CTLD:_GetFMBeacon(Name) + self:T(self.lid .. " _GetFMBeacon") + local beacon = {} -- #CTLD.ZoneBeacon + if #self.FreeFMFrequencies <= 1 then + self.FreeFMFrequencies = self.UsedFMFrequencies + self.UsedFMFrequencies = {} + end + --random + local FM = table.remove(self.FreeFMFrequencies, math.random(#self.FreeFMFrequencies)) + table.insert(self.UsedFMFrequencies, FM) + beacon.name = Name + beacon.frequency = FM / 1000000 + beacon.modulation = radio.modulation.FM + + return beacon +end + +--- Function to obtain a valid UHF frequency. +-- @param #CTLD self +-- @param #string Name Name of zone. +-- @return #CTLD.ZoneBeacon Beacon Beacon table. +function CTLD:_GetUHFBeacon(Name) + self:T(self.lid .. " _GetUHFBeacon") + local beacon = {} -- #CTLD.ZoneBeacon + if #self.FreeUHFFrequencies <= 1 then + self.FreeUHFFrequencies = self.UsedUHFFrequencies + self.UsedUHFFrequencies = {} + end + --random + local UHF = table.remove(self.FreeUHFFrequencies, math.random(#self.FreeUHFFrequencies)) + table.insert(self.UsedUHFFrequencies, UHF) + beacon.name = Name + beacon.frequency = UHF / 1000000 + beacon.modulation = radio.modulation.AM + + return beacon +end + +--- Function to obtain a valid VHF frequency. +-- @param #CTLD self +-- @param #string Name Name of zone. +-- @return #CTLD.ZoneBeacon Beacon Beacon table. +function CTLD:_GetVHFBeacon(Name) + self:T(self.lid .. " _GetVHFBeacon") + local beacon = {} -- #CTLD.ZoneBeacon + if #self.FreeVHFFrequencies <= 3 then + self.FreeVHFFrequencies = self.UsedVHFFrequencies + self.UsedVHFFrequencies = {} + end + --get random + local VHF = table.remove(self.FreeVHFFrequencies, math.random(#self.FreeVHFFrequencies)) + table.insert(self.UsedVHFFrequencies, VHF) + beacon.name = Name + beacon.frequency = VHF / 1000000 + beacon.modulation = radio.modulation.FM + return beacon +end + + +--- User function - Crates and adds a #CTLD.CargoZone zone for this CTLD instance. +-- Zones of type LOAD: Players load crates and troops here. +-- Zones of type DROP: Players can drop crates here. Note that troops can be unloaded anywhere. +-- Zone of type MOVE: Dropped troops and vehicles will start moving to the nearest zone of this type (also see options). +-- @param #CTLD self +-- @param #string Name Name of this zone, as in Mission Editor. +-- @param #string Type Type of this zone, #CTLD.CargoZoneType +-- @param #number Color Smoke/Flare color e.g. #SMOKECOLOR.Red +-- @param #string Active Is this zone currently active? +-- @param #string HasBeacon Does this zone have a beacon if it is active? +-- @return #CTLD self +function CTLD:AddCTLDZone(Name, Type, Color, Active, HasBeacon) + self:T(self.lid .. " AddCTLDZone") + + local ctldzone = {} -- #CTLD.CargoZone + ctldzone.active = Active or false + ctldzone.color = Color or SMOKECOLOR.Red + ctldzone.name = Name or "NONE" + ctldzone.type = Type or CTLD.CargoZoneType.MOVE -- #CTLD.CargoZoneType + ctldzone.hasbeacon = HasBeacon or false + + if HasBeacon then + ctldzone.fmbeacon = self:_GetFMBeacon(Name) + ctldzone.uhfbeacon = self:_GetUHFBeacon(Name) + ctldzone.vhfbeacon = self:_GetVHFBeacon(Name) + else + ctldzone.fmbeacon = nil + ctldzone.uhfbeacon = nil + ctldzone.vhfbeacon = nil + end + + self:AddZone(ctldzone) + return self +end + +--- Function to show list of radio beacons +-- @param #CTLD self +-- @param Wrapper.Group#GROUP Group +-- @param Wrapper.Unit#UNIT Unit +function CTLD:_ListRadioBeacons(Group, Unit) + self:T(self.lid .. " _ListRadioBeacons") + local report = REPORT:New("Active Zone Beacons") + report:Add("------------------------------------------------------------") + local zones = {[1] = self.pickupZones, [2] = self.wpZones, [3] = self.dropOffZones} + for i=1,3 do + for index,cargozone in pairs(zones[i]) do + -- Get Beacon object from zone + local czone = cargozone -- #CTLD.CargoZone + if czone.active and czone.hasbeacon then + local FMbeacon = czone.fmbeacon -- #CTLD.ZoneBeacon + local VHFbeacon = czone.vhfbeacon -- #CTLD.ZoneBeacon + local UHFbeacon = czone.uhfbeacon -- #CTLD.ZoneBeacon + local Name = czone.name + local FM = FMbeacon.frequency -- MHz + local VHF = VHFbeacon.frequency * 1000 -- KHz + local UHF = UHFbeacon.frequency -- MHz + report:AddIndent(string.format(" %s | FM %s Mhz | VHF %s KHz | UHF %s Mhz ", Name, FM, VHF, UHF),"|") + end + end + end + if report:GetCount() == 1 then + report:Add("--------- N O N E ------------") + end + report:Add("------------------------------------------------------------") + local m = MESSAGE:New(report:Text(),30,"CTLD",true):ToGroup(Group) + return self +end + +--- Add radio beacon to zone. Runs 30 secs. +-- @param #CTLD self +-- @param #string Name Name of zone. +-- @param #string Sound Name of soundfile. +-- @param #number Mhz Frequency in Mhz. +-- @param #number Modulation Modulation AM or FM. +function CTLD:_AddRadioBeacon(Name, Sound, Mhz, Modulation) + self:T(self.lid .. " _AddRadioBeacon") + local Zone = ZONE:FindByName(Name) + local Sound = Sound or "beacon.ogg" + if Zone then + local ZoneCoord = Zone:GetCoordinate() + local ZoneVec3 = ZoneCoord:GetVec3() + local Frequency = Mhz * 1000000 -- Freq in Hertz + local Sound = "l10n/DEFAULT/"..Sound + trigger.action.radioTransmission(Sound, ZoneVec3, Modulation, false, Frequency, 1000) -- Beacon in MP only runs for 30secs straight + end + return self +end + +--- Function to refresh radio beacons +-- @param #CTLD self +function CTLD:_RefreshRadioBeacons() + self:I(self.lid .. " _RefreshRadioBeacons") + + local zones = {[1] = self.pickupZones, [2] = self.wpZones, [3] = self.dropOffZones} + for i=1,3 do + for index,cargozone in pairs(zones[i]) do + -- Get Beacon object from zone + local czone = cargozone -- #CTLD.CargoZone + local Sound = self.RadioSound + if czone.active and czone.hasbeacon then + local FMbeacon = czone.fmbeacon -- #CTLD.ZoneBeacon + local VHFbeacon = czone.vhfbeacon -- #CTLD.ZoneBeacon + local UHFbeacon = czone.uhfbeacon -- #CTLD.ZoneBeacon + local Name = czone.name + local FM = FMbeacon.frequency -- MHz + local VHF = VHFbeacon.frequency -- KHz + local UHF = UHFbeacon.frequency -- MHz + self:_AddRadioBeacon(Name,Sound,FM,radio.modulation.FM) + self:_AddRadioBeacon(Name,Sound,VHF,radio.modulation.FM) + self:_AddRadioBeacon(Name,Sound,UHF,radio.modulation.AM) + end + end + end + return self +end + +--- function to see if a unit is in a specific zone type. +-- @param #CTLD self +-- @param Wrapper.Unit#UNIT Unit Unit +-- @param #CTLD.CargoZoneType Zonetype Zonetype +-- @return #boolean Outcome Is in zone or not +-- @return #string name Closest zone name +-- @return #string zone Closest Core.Zone#ZONE object +-- @return #number distance Distance to closest zone +function CTLD:IsUnitInZone(Unit,Zonetype) + self:T(self.lid .. " IsUnitInZone") + local unitname = Unit:GetName() + self:T(string.format("%s | Zone search for %s | Type %s",self.lid,unitname,Zonetype)) + local zonetable = {} + local outcome = false + if Zonetype == CTLD.CargoZoneType.LOAD then + zonetable = self.pickupZones -- #table + elseif Zonetype == CTLD.CargoZoneType.DROP then + zonetable = self.dropOffZones -- #table + else + zonetable = self.wpZones -- #table + end + --- now see if we\'re in + local zonecoord = nil + local colorret = nil + local maxdist = 1000000 -- 100km + local zoneret = nil + local zonenameret = nil + for _,_cargozone in pairs(zonetable) do + local czone = _cargozone -- #CTLD.CargoZone + local unitcoord = Unit:GetCoordinate() + local zonename = czone.name + local zone = ZONE:FindByName(zonename) + zonecoord = zone:GetCoordinate() + local active = czone.active + local color = czone.color + local zoneradius = zone:GetRadius() + local distance = self:_GetDistance(zonecoord,unitcoord) + self:T(string.format("Check distance: %d",distance)) + if distance <= zoneradius and active then + outcome = true + end + if maxdist > distance then + maxdist = distance + zoneret = zone + zonenameret = zonename + colorret = color + end + end + self:T({outcome, zonenameret, zoneret, maxdist}) + return outcome, zonenameret, zoneret, maxdist +end + +--- Userfunction - Start smoke in a zone close to the Unit. +-- @param #CTLD self +-- @param Wrapper.Unit#UNIT Unit The Unit. +-- @param #boolean Flare If true, flare instead. +function CTLD:SmokeZoneNearBy(Unit, Flare) + self:T(self.lid .. " SmokeZoneNearBy") + -- table of #CTLD.CargoZone table + local unitcoord = Unit:GetCoordinate() + local Group = Unit:GetGroup() + local smokedistance = self.smokedistance + local smoked = false + local zones = {[1] = self.pickupZones, [2] = self.wpZones, [3] = self.dropOffZones} + for i=1,3 do + for index,cargozone in pairs(zones[i]) do + local CZone = cargozone --#CTLD.CargoZone + local zonename = CZone.name + local zone = ZONE:FindByName(zonename) + local zonecoord = zone:GetCoordinate() + local active = CZone.active + local color = CZone.color + local distance = self:_GetDistance(zonecoord,unitcoord) + if distance < smokedistance and active then + -- smoke zone since we\'re nearby + if not Flare then + zonecoord:Smoke(color or SMOKECOLOR.White) + else + if color == SMOKECOLOR.Blue then color = FLARECOLOR.White end + zonecoord:Flare(color or FLARECOLOR.White) + end + local txt = "smoking" + if Flare then txt = "flaring" end + local m = MESSAGE:New(string.format("Roger, %s zone %s!",txt, zonename),10,"CTLD"):ToGroup(Group) + smoked = true + end + end + end + if not smoked then + local distance = UTILS.MetersToNM(self.smkedistance) + local m = MESSAGE:New(string.format("Negative, need to be closer than %dnm to a zone!",distance),10,"CTLD"):ToGroup(Group) + end + return self +end + --- User - Function to add/adjust unittype capabilities. + -- @param #CTLD self + -- @param #string Unittype The unittype to adjust. If passed as Wrapper.Unit#UNIT, it will search for the unit in the mission. + -- @param #boolean Cancrates Unit can load crates. + -- @param #boolean Cantroops Unit can load troops. + -- @param #number Cratelimit Unit can carry number of crates. + -- @param #number Trooplimit Unit can carry number of troops. + function CTLD:UnitCapabilities(Unittype, Cancrates, Cantroops, Cratelimit, Trooplimit) + self:T(self.lid .. " UnitCapabilities") + local unittype = nil + local unit = nil + if type(Unittype) == "string" then + unittype = Unittype + elseif type(Unittype) == "table" then + unit = UNIT:FindByName(Unittype) -- Wrapper.Unit#UNIT + unittype = unit:GetTypeName() + else + return self + end + -- set capabilities + local capabilities = {} -- #CTLD.UnitCapabilities + capabilities.type = unittype + capabilities.crates = Cancrates or false + capabilities.troops = Cantroops or false + capabilities.cratelimit = Cratelimit or 0 + capabilities.trooplimit = Trooplimit or 0 + self.UnitTypes[unittype] = capabilities + return self + end + + --- Check if a unit is hovering *in parameters*. + -- @param #CTLD self + -- @param Wrapper.Unit#UNIT Unit + -- @return #boolean Outcome + function CTLD:IsCorrectHover(Unit) + self:T(self.lid .. " IsCorrectHover") + local outcome = false + -- see if we are in air and within parameters. + if self:IsUnitInAir(Unit) then + -- get speed and height + local uspeed = Unit:GetVelocityMPS() + local uheight = Unit:GetHeight() + local ucoord = Unit:GetCoordinate() + local gheight = ucoord:GetLandHeight() + local aheight = uheight - gheight -- height above ground + local maxh = self.maximumHoverHeight -- 15 + local minh = self.minimumHoverHeight -- 5 + local mspeed = 2 -- 2 m/s + self:T(string.format("%s Unit parameters: at %dm AGL with %dmps",self.lid,aheight,uspeed)) + if (uspeed <= maxh) and (aheight <= maxh) and (aheight >= minh) then + -- yep within parameters + outcome = true + end + end + return outcome + end + + --- List if a unit is hovering *in parameters*. + -- @param #CTLD self + -- @param Wrapper.Group#GROUP Group + -- @param Wrapper.Unit#UNIT Unit + function CTLD:_ShowHoverParams(Group,Unit) + local inhover = self:IsCorrectHover(Unit) + local htxt = "true" + if not inhover then htxt = "false" end + local text = string.format("Hover parameter (autoload):\n - Min height %dm \n - Max height %dm \n - Max speed 2mps \n - In parameter: %s", self.minimumHoverHeight, self.maximumHoverHeight, htxt) + local m = MESSAGE:New(text,10,"CTLD",false):ToGroup(Group) + return self + end + + --- Check if a unit is in a load zone and is hovering in parameters. + -- @param #CTLD self + -- @param Wrapper.Unit#UNIT Unit + -- @return #boolean Outcome + function CTLD:CanHoverLoad(Unit) + self:T(self.lid .. " CanHoverLoad") + local outcome = self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD) and self:IsCorrectHover(Unit) + return outcome + end + + --- Check if a unit is above ground. + -- @param #CTLD self + -- @param Wrapper.Unit#UNIT Unit + -- @return #boolean Outcome + function CTLD:IsUnitInAir(Unit) + -- get speed and height + local uheight = Unit:GetHeight() + local ucoord = Unit:GetCoordinate() + local gheight = ucoord:GetLandHeight() + local aheight = uheight - gheight -- height above ground + if aheight >= self.minimumHoverHeight then + return true + else + return false + end + end + + --- Autoload if we can do crates, have capacity free and are in a load zone. + -- @param #CTLD self + -- @param Wrapper.Unit#UNIT Unit + -- @return #CTLD self + function CTLD:AutoHoverLoad(Unit) + self:T(self.lid .. " AutoHoverLoad") + -- get capabilities and current load + local unittype = Unit:GetTypeName() + local unitname = Unit:GetName() + local Group = Unit:GetGroup() + local capabilities = self.UnitTypes[unittype] -- #CTLD.UnitCapabilities + local cancrates = capabilities.crates -- #boolean + local cratelimit = capabilities.cratelimit -- #number + if cancrates then + -- get load + local numberonboard = 0 + local loaded = {} + if self.Loaded_Cargo[unitname] then + loaded = self.Loaded_Cargo[unitname] -- #CTLD.LoadedCargo + numberonboard = loaded.Cratesloaded or 0 + end + local load = cratelimit - numberonboard + local canload = self:CanHoverLoad(Unit) + if canload and load > 0 then + self:_LoadCratesNearby(Group,Unit) + end + end + return self + end + + --- Run through all pilots and see if we autoload. + -- @param #CTLD self + -- @return #CTLD self + function CTLD:CheckAutoHoverload() + if self.hoverautoloading then + for _,_pilot in pairs (self.CtldUnits) do + local Unit = UNIT:FindByName(_pilot) + self:AutoHoverLoad(Unit) + end + end + return self + end + +------------------------------------------------------------------- +-- FSM functions +------------------------------------------------------------------- + + --- FSM Function onafterStart. + -- @param #CTLD self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @return #CTLD self + function CTLD:onafterStart(From, Event, To) + self:I({From, Event, To}) + if self.useprefix then + self.PilotGroups = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(self.prefixes):FilterCategoryHelicopter():FilterStart() + else + self.PilotGroups = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterCategoryHelicopter():FilterStart() + end + -- Events + self:HandleEvent(EVENTS.PlayerEnterAircraft, self._EventHandler) + self:HandleEvent(EVENTS.PlayerEnterUnit, self._EventHandler) + self:HandleEvent(EVENTS.PlayerLeaveUnit, self._EventHandler) + self:__Status(-5) + return self + end + + --- FSM Function onbeforeStatus. + -- @param #CTLD self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @return #CTLD self + function CTLD:onbeforeStatus(From, Event, To) + self:T({From, Event, To}) + self:_RefreshF10Menus() + self:_RefreshRadioBeacons() + self:CheckAutoHoverload() + return self + end + + --- FSM Function onafterStatus. + -- @param #CTLD self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @return #CTLD self + function CTLD:onafterStatus(From, Event, To) + self:I({From, Event, To}) + -- gather some stats + -- pilots + local pilots = 0 + for _,_pilot in pairs (self.CtldUnits) do + + pilots = pilots + 1 + end + + -- spawned cargo boxes curr in field + local boxes = 0 + for _,_pilot in pairs (self.Spawned_Cargo) do + boxes = boxes + 1 + end + + local cc = self.CargoCounter + local tc = self.TroopCounter + + if self.debug or self.verbose > 0 then + local text = string.format("%s Pilots %d | Live Crates %d |\nCargo Counter %d | Troop Counter %d", self.lid, pilots, boxes, cc, tc) + local m = MESSAGE:New(text,10,"CTLD"):ToAll() + end + self:__Status(-30) + return self + end + + --- FSM Function onafterStop. + -- @param #CTLD self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @return #CTLD self + function CTLD:onafterStop(From, Event, To) + self:T({From, Event, To}) + self:UnhandleEvent(EVENTS.PlayerEnterAircraft) + self:UnhandleEvent(EVENTS.PlayerEnterUnit) + self:UnhandleEvent(EVENTS.PlayerLeaveUnit) + return self + end + + --- FSM Function onbeforeTroopsPickedUp. + -- @param #CTLD self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Group#GROUP Group Group Object. + -- @param Wrapper.Unit#UNIT Unit Unit Object. + -- @param #CTLD_CARGO Cargo Cargo crate. + -- @return #CTLD self + function CTLD:onbeforeTroopsPickedUp(From, Event, To, Group, Unit, Cargo) + self:I({From, Event, To}) + return self + end + + --- FSM Function onbeforeCratesPickedUp. + -- @param #CTLD self + -- @param #string From State . + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Group#GROUP Group Group Object. + -- @param Wrapper.Unit#UNIT Unit Unit Object. + -- @param #CTLD_CARGO Cargo Cargo crate. + -- @return #CTLD self + function CTLD:onbeforeCratesPickedUp(From, Event, To, Group, Unit, Cargo) + self:I({From, Event, To}) + return self + end + + --- FSM Function onbeforeTroopsDeployed. + -- @param #CTLD self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Group#GROUP Group Group Object. + -- @param Wrapper.Unit#UNIT Unit Unit Object. + -- @param Wrapper.Group#GROUP Troops Troops #GROUP Object. + -- @return #CTLD self + function CTLD:onbeforeTroopsDeployed(From, Event, To, Group, Unit, Troops) + self:I({From, Event, To}) + return self + end + + --- FSM Function onbeforeCratesDropped. + -- @param #CTLD self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Group#GROUP Group Group Object. + -- @param Wrapper.Unit#UNIT Unit Unit Object. + -- @param #table Cargotable Table of #CTLD_CARGO objects dropped. + -- @return #CTLD self + function CTLD:onbeforeCratesDropped(From, Event, To, Group, Unit, Cargotable) + self:I({From, Event, To}) + return self + end + + --- FSM Function onbeforeCratesBuild. + -- @param #CTLD self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Group#GROUP Group Group Object. + -- @param Wrapper.Unit#UNIT Unit Unit Object. + -- @param Wrapper.Group#GROUP Vehicle The #GROUP object of the vehicle or FOB build. + -- @return #CTLD self + function CTLD:onbeforeCratesBuild(From, Event, To, Group, Unit, Vehicle) + self:I({From, Event, To}) + return self + end + +end -- end do +------------------------------------------------------------------- +-- End Ops.CTLD.lua +------------------------------------------------------------------- From ee503a378ee711e2009858db73559d50c833e321 Mon Sep 17 00:00:00 2001 From: Applevangelist <72444570+Applevangelist@users.noreply.github.com> Date: Tue, 22 Jun 2021 18:24:24 +0200 Subject: [PATCH 03/16] Update Modules.lua (#1557) added CTLD --- Moose Development/Moose/Modules.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/Moose Development/Moose/Modules.lua b/Moose Development/Moose/Modules.lua index fef4fc982..7ad1b1829 100644 --- a/Moose Development/Moose/Modules.lua +++ b/Moose Development/Moose/Modules.lua @@ -85,6 +85,7 @@ __Moose.Include( 'Scripts/Moose/Ops/Squadron.lua' ) __Moose.Include( 'Scripts/Moose/Ops/AirWing.lua' ) __Moose.Include( 'Scripts/Moose/Ops/Intelligence.lua' ) __Moose.Include( 'Scripts/Moose/Ops/CSAR.lua' ) +__Moose.Include( 'Scripts/Moose/Ops/CTLD.lua' ) __Moose.Include( 'Scripts/Moose/AI/AI_Balancer.lua' ) __Moose.Include( 'Scripts/Moose/AI/AI_Air.lua' ) From b84c0aba593eb366dcb4a67320f610a14f718ff7 Mon Sep 17 00:00:00 2001 From: Applevangelist <72444570+Applevangelist@users.noreply.github.com> Date: Tue, 22 Jun 2021 18:24:36 +0200 Subject: [PATCH 04/16] Update Moose.files (#1556) --- Moose Setup/Moose.files | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index e7bec2087..077e75168 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -81,6 +81,8 @@ Ops/NavyGroup.lua Ops/Squadron.lua Ops/AirWing.lua Ops/Intelligence.lua +Ops/CSAR.lua +Ops/CTLD.lua AI/AI_Balancer.lua AI/AI_Air.lua From 576281a61212c8440fb939bf38bea5c5a129b691 Mon Sep 17 00:00:00 2001 From: Applevangelist <72444570+Applevangelist@users.noreply.github.com> Date: Tue, 22 Jun 2021 18:24:46 +0200 Subject: [PATCH 05/16] Update CSAR.lua (#1554) Added options to limit number of downed pilots via Events (mission designers can still "inject" downed pilots): `self.limitmaxdownedpilots = true self.maxdownedpilots = 10` --- Moose Development/Moose/Ops/CSAR.lua | 62 ++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index ad6ae1a97..61c3c58bc 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -59,7 +59,7 @@ -- local my_csar = CSAR:New(coalition.side.BLUE,"Downed Pilot","Luftrettung") -- -- options -- my_csar.immortalcrew = true -- downed pilot spawn is immortal --- my_csar.invisiblevrew = false -- downed pilot spawn is visible +-- my_csar.invisiblecrew = false -- downed pilot spawn is visible -- -- start the FSM -- my_csar:__Start(5) -- @@ -86,6 +86,9 @@ -- self.useprefix = true -- Requires CSAR helicopter #GROUP names to have the prefix(es) defined below. -- self.csarPrefix = { "helicargo", "MEDEVAC"} -- #GROUP name prefixes used for useprefix=true - DO NOT use # in helicopter names in the Mission Editor! -- self.verbose = 0 -- set to > 1 for stats output for debugging. +-- -- (added 0.1.4) limit amount of downed pilots spawned by ejection events +-- self.limitmaxdownedpilots = true, +-- self.maxdownedpilots = 10, -- -- ## 2.1 Experimental Features -- @@ -188,6 +191,8 @@ CSAR = { smokecolor = 4, rescues = 0, rescuedpilots = 0, + limitmaxdownedpilots = true, + maxdownedpilots = 10, } --- Downed pilots info. @@ -235,7 +240,7 @@ CSAR.AircraftType["Mi-24V"] = 8 --- CSAR class version. -- @field #string version -CSAR.version="0.1.3r4" +CSAR.version="0.1.4r1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -350,6 +355,9 @@ function CSAR:New(Coalition, Template, Alias) self.mashprefix = {"MASH"} -- prefixes used to find MASHes self.bluemash = SET_GROUP:New():FilterCoalitions(self.coalition):FilterPrefixes(self.mashprefix):FilterOnce() -- currently only GROUP objects, maybe support STATICs also? self.autosmoke = false -- automatically smoke location when heli is near + -- added 0.1.4 + self.limitmaxdownedpilots = true + self.maxdownedpilots = 25 -- WARNING - here\'ll be dragons -- for this to work you need to de-sanitize your mission environment in \Scripts\MissionScripting.lua @@ -774,7 +782,13 @@ function CSAR:_EventHandler(EventData) if self:_DoubleEjection(_unitname) then return end - + + -- limit no of pilots in the field. + if self.limitmaxdownedpilots and self:_ReachedPilotLimit() then + return + end + + -- all checks passed, get going. local _freq = self:_GenerateADFFrequency() self:_AddCsar(_coalition, _unit:GetCountry(), _unit:GetCoordinate() , _unit:GetTypeName(), _unit:GetName(), _event.IniPlayerName, _freq, false, 0) @@ -1776,6 +1790,36 @@ function CSAR:_RefreshRadioBeacons() end end +--- Helper function to count active downed pilots. +-- @param #CSAR self +-- @return #number Number of pilots in the field. +function CSAR:_CountActiveDownedPilots() + self:T(self.lid .. " _CountActiveDownedPilots") + local PilotsInFieldN = 0 + for _, _unitName in pairs(self.downedPilots) do + self:T({_unitName}) + if _unitName.name ~= nil then + PilotsInFieldN = PilotsInFieldN + 1 + end + end + return PilotsInFieldN +end + +--- Helper to decide if we're over max limit. +-- @param #CSAR self +-- @return #boolean True or false. +function CSAR:_ReachedPilotLimit() + self:T(self.lid .. " _ReachedPilotLimit") + local limit = self.maxdownedpilots + local islimited = self.limitmaxdownedpilots + local count = self:_CountActiveDownedPilots() + if islimited and (count >= limit) then + return true + else + return false + end +end + ------------------------------ --- FSM internal Functions --- ------------------------------ @@ -1844,13 +1888,7 @@ function CSAR:onafterStatus(From, Event, To) NumberOfSARPilots = NumberOfSARPilots + 1 end - local PilotsInFieldN = 0 - for _, _unitName in pairs(self.downedPilots) do - self:T({_unitName}) - if _unitName.name ~= nil then - PilotsInFieldN = PilotsInFieldN + 1 - end - end + local PilotsInFieldN = self:_CountActiveDownedPilots() local PilotsBoarded = 0 for _, _unitName in pairs(self.inTransitGroups) do @@ -1860,8 +1898,8 @@ function CSAR:onafterStatus(From, Event, To) end if self.verbose > 0 then - local text = string.format("%s Active SAR: %d | Downed Pilots in field: %d | Pilots boarded: %d | Landings: %d | Pilots rescued: %d", - self.lid,NumberOfSARPilots,PilotsInFieldN,PilotsBoarded,self.rescues,self.rescuedpilots) + local text = string.format("%s Active SAR: %d | Downed Pilots in field: %d (max %d) | Pilots boarded: %d | Landings: %d | Pilots rescued: %d", + self.lid,NumberOfSARPilots,PilotsInFieldN,self.maxdownedpilots,PilotsBoarded,self.rescues,self.rescuedpilots) self:T(text) if self.verbose < 2 then self:I(text) From 5021a1e1f3d1a833f2b97ed921b0ec2f0671ed71 Mon Sep 17 00:00:00 2001 From: Applevangelist <72444570+Applevangelist@users.noreply.github.com> Date: Tue, 22 Jun 2021 18:26:44 +0200 Subject: [PATCH 06/16] Update CTLD.lua --- Moose Development/Moose/Ops/CTLD.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 38640518f..4d34aabe8 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -22,7 +22,7 @@ -- @module Ops.CTLD -- @image OPS_CTLD.jpg --- Date: June 2021 +-- Date: 22 June 2021 do ------------------------------------------------------ @@ -382,7 +382,7 @@ do -- @field #CTLD CTLD = { ClassName = "CTLD", - verbose = 2, + verbose = 0, lid = "", coalition = 1, coalitiontxt = "blue", From 5123ab0720d5efb27dd93a0518fec501d47a40c8 Mon Sep 17 00:00:00 2001 From: Applevangelist <72444570+Applevangelist@users.noreply.github.com> Date: Wed, 23 Jun 2021 09:47:05 +0200 Subject: [PATCH 07/16] Update CTLD.lua (#1558) corrected fsm function name headlines in documentation --- Moose Development/Moose/Ops/CTLD.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 4d34aabe8..211932703 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -327,7 +327,7 @@ do -- ... your code here ... -- end -- --- ## 3.3 OnAfterTroopsTroopsDeployed +-- ## 3.3 OnAfterTroopsDeployed -- -- This function is called when a player has deployed troops into the field: -- @@ -335,7 +335,7 @@ do -- ... your code here ... -- end -- --- ## 3.4 OnAfterTroopsCratesDropped +-- ## 3.4 OnAfterCratesDropped -- -- This function is called when a player has deployed crates to a DROP zone: -- @@ -343,7 +343,7 @@ do -- ... your code here ... -- end -- --- ## 3.5 OnAfterTroopsCratesBuild +-- ## 3.5 OnAfterCratesBuild -- -- This function is called when a player has build a vehicle or FOB: -- From d4cdfcc48c6d05a79c7a48558631c751a7be6d48 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 23 Jun 2021 14:08:34 +0200 Subject: [PATCH 08/16] Corrected Mi-8MTV2 Unit Type Name --- Moose Development/Moose/Ops/CSAR.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index 61c3c58bc..6c6245ef4 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -234,7 +234,7 @@ CSAR.AircraftType["SA342Minigun"] = 2 CSAR.AircraftType["SA342L"] = 4 CSAR.AircraftType["SA342M"] = 4 CSAR.AircraftType["UH-1H"] = 8 -CSAR.AircraftType["Mi-8MT"] = 12 +CSAR.AircraftType["Mi-8MTV2"] = 12 CSAR.AircraftType["Mi-24P"] = 8 CSAR.AircraftType["Mi-24V"] = 8 From 3b44aba34173931d4ecd1f47e09d0b6391d0a777 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 24 Jun 2021 08:34:14 +0200 Subject: [PATCH 09/16] Updated frequency test --- Moose Development/Moose/Ops/CSAR.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index 6c6245ef4..1f9d22f64 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -240,7 +240,7 @@ CSAR.AircraftType["Mi-24V"] = 8 --- CSAR class version. -- @field #string version -CSAR.version="0.1.4r1" +CSAR.version="0.1.4r3" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -1783,7 +1783,7 @@ function CSAR:_RefreshRadioBeacons() for _,_pilot in pairs (PilotTable) do local pilot = _pilot -- #CSAR.DownedPilot local group = pilot.group - local frequency = pilot.frequency + local frequency = pilot.frequency or 0 -- thanks to @Thrud if frequency and frequency > 0 then self:_AddBeaconToGroup(group,frequency) end From 2ff128f184b7a83b4834c6ee68c4003d8d696e40 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 25 Jun 2021 12:39:02 +0200 Subject: [PATCH 10/16] Added Docu for functions --- Moose Development/Moose/Ops/CSAR.lua | 116 +++++++++++++++------------ Moose Development/Moose/Ops/CTLD.lua | 93 ++++++++++----------- 2 files changed, 113 insertions(+), 96 deletions(-) diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index 1f9d22f64..7be6de481 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -25,7 +25,7 @@ -- Date: June 2021 ------------------------------------------------------------------------- ---- **CSAR** class, extends #Core.Base#BASE, #Core.Fsm#FSM +--- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM -- @type CSAR -- @field #string ClassName Name of the class. -- @field #number verbose Verbosity level. @@ -157,7 +157,7 @@ -- If missions designers want to spawn downed pilots into the field, e.g. at mission begin to give the helicopter guys works, they can do this like so: -- -- -- Create downed "Pilot Wagner" in #ZONE "CSAR_Start_1" at a random point for the blue coalition --- my_csar:_SpawnCsarAtZone( "CSAR_Start_1", coalition.side.BLUE, "Pilot Wagner", true ) +-- my_csar:SpawnCSARAtZone( "CSAR_Start_1", coalition.side.BLUE, "Pilot Wagner", true ) -- -- -- @field #CSAR @@ -240,7 +240,7 @@ CSAR.AircraftType["Mi-24V"] = 8 --- CSAR class version. -- @field #string version -CSAR.version="0.1.4r3" +CSAR.version="0.1.4r4" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -452,7 +452,7 @@ end --- Helper Functions --- ------------------------ ---- Function to insert downed pilot tracker object. +--- (Internal) Function to insert downed pilot tracker object. -- @param #CSAR self -- @param Wrapper.Group#GROUP Group The #GROUP object -- @param #string Groupname Name of the spawned group. @@ -490,7 +490,7 @@ function CSAR:_CreateDownedPilotTrack(Group,Groupname,Side,OriginalUnit,Descript self.downedpilotcounter = self.downedpilotcounter+1 end ---- Count pilots on board. +--- (Internal) Count pilots on board. -- @param #CSAR self -- @param #string _heliName -- @return #number count @@ -505,7 +505,7 @@ function CSAR:_PilotsOnboard(_heliName) return count end ---- Function to check for dupe eject events. +--- (Internal) Function to check for dupe eject events. -- @param #CSAR self -- @param #string _unitname Name of unit. -- @return #boolean Outcome @@ -521,7 +521,7 @@ function CSAR:_DoubleEjection(_unitname) return false end ---- Spawn a downed pilot +--- (Internal) Spawn a downed pilot -- @param #CSAR self -- @param #number country Country for template. -- @param Core.Point#COORDINATE point Coordinate to spawn at. @@ -547,7 +547,7 @@ function CSAR:_SpawnPilotInField(country,point) return _spawnedGroup, alias -- Wrapper.Group#GROUP object end ---- Add options to a downed pilot +--- (Internal) Add options to a downed pilot -- @param #CSAR self -- @param Wrapper.Group#GROUP group Group to use. function CSAR:_AddSpecialOptions(group) @@ -581,7 +581,7 @@ function CSAR:_AddSpecialOptions(group) end ---- Function to spawn a CSAR object into the scene. +--- (Internal) Function to spawn a CSAR object into the scene. -- @param #CSAR self -- @param #number _coalition Coalition -- @param DCS#country.id _country Country ID @@ -634,7 +634,7 @@ function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _pla end ---- Function to add a CSAR object into the scene at a zone coordinate. For mission designers wanting to add e.g. PoWs to the scene. +--- (Internal) Function to add a CSAR object into the scene at a zone coordinate. For mission designers wanting to add e.g. PoWs to the scene. -- @param #CSAR self -- @param #string _zone Name of the zone. -- @param #number _coalition Coalition. @@ -672,8 +672,24 @@ function CSAR:_SpawnCsarAtZone( _zone, _coalition, _description, _randomPoint, _ self:_AddCsar(_coalition, _country, pos, "PoW", "Unknown", nil, freq, _nomessage, _description) end +--- Function to add a CSAR object into the scene at a zone coordinate. For mission designers wanting to add e.g. PoWs to the scene. +-- @param #CSAR self +-- @param #string Zone Name of the zone. +-- @param #number Coalition Coalition. +-- @param #string Description (optional) Description. +-- @param #boolean RandomPoint (optional) Random yes or no. +-- @param #boolean Nomessage (optional) If true, don\'t send a message to SAR. +-- @usage If missions designers want to spawn downed pilots into the field, e.g. at mission begin, to give the helicopter guys works, they can do this like so: +-- +-- -- Create downed "Pilot Wagner" in #ZONE "CSAR_Start_1" at a random point for the blue coalition +-- my_csar:SpawnCSARAtZone( "CSAR_Start_1", coalition.side.BLUE, "Pilot Wagner", true ) +function CSAR:SpawnCSARAtZone(Zone, Coalition, Description, RandomPoint, Nomessage) + self:_SpawnCsarAtZone(Zone, Coalition, Description, RandomPoint, Nomessage) + return self +end + -- TODO: Split in functions per Event type ---- Event handler. +--- (Internal) Event handler. -- @param #CSAR self function CSAR:_EventHandler(EventData) self:T(self.lid .. " _EventHandler") @@ -836,7 +852,7 @@ function CSAR:_EventHandler(EventData) end ---- Initialize the action for a pilot. +--- (Internal) Initialize the action for a pilot. -- @param #CSAR self -- @param Wrapper.Group#GROUP _downedGroup The group to rescue. -- @param #string _GroupName Name of the Group @@ -864,7 +880,7 @@ function CSAR:_InitSARForPilot(_downedGroup, _GroupName, _freq, _nomessage) self:__PilotDown(2,_downedGroup, _freqk, _leadername, _coordinatesText) end ---- Check if a name is in downed pilot table +--- (Internal) Check if a name is in downed pilot table -- @param #CSAR self -- @param #string name Name to search for. -- @return #boolean Outcome. @@ -883,7 +899,7 @@ function CSAR:_CheckNameInDownedPilots(name) return found, table end ---- Check if a name is in downed pilot table and remove it. +--- (Internal) Check if a name is in downed pilot table and remove it. -- @param #CSAR self -- @param #string name Name to search for. -- @param #boolean force Force removal. @@ -914,7 +930,7 @@ function CSAR:_RemoveNameFromDownedPilots(name,force) return found end ---- Check state of wounded group. +--- (Internal) Check state of wounded group. -- @param #CSAR self -- @param #string heliname heliname -- @param #string woundedgroupname woundedgroupname @@ -967,7 +983,7 @@ function CSAR:_CheckWoundedGroupStatus(heliname,woundedgroupname) end end ---- Function to pop a smoke at a wounded pilot\'s positions. +--- (Internal) Function to pop a smoke at a wounded pilot\'s positions. -- @param #CSAR self -- @param #string _woundedGroupName Name of the group. -- @param Wrapper.Group#GROUP _woundedLeader Object of the group. @@ -984,7 +1000,7 @@ function CSAR:_PopSmokeForGroup(_woundedGroupName, _woundedLeader) end end ---- Function to pickup the wounded pilot from the ground. +--- (Internal) Function to pickup the wounded pilot from the ground. -- @param #CSAR self -- @param Wrapper.Unit#UNIT _heliUnit Object of the group. -- @param #string _pilotName Name of the pilot. @@ -1035,7 +1051,7 @@ function CSAR:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupNam return true end ---- Move group to destination. +--- (Internal) Move group to destination. -- @param #CSAR self -- @param Wrapper.Group#GROUP _leader -- @param Core.Point#COORDINATE _destination @@ -1048,7 +1064,7 @@ function CSAR:_OrderGroupToMoveToPoint(_leader, _destination) group:RouteToVec2(coordinate,5) end ---- Function to check if heli is close to group. +--- (Internal) Function to check if heli is close to group. -- @param #CSAR self -- @param #number _distance -- @param Wrapper.Unit#UNIT _heliUnit @@ -1179,7 +1195,7 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG end end ---- Check if group not KIA. +--- (Internal) Check if group not KIA. -- @param #CSAR self -- @param Wrapper.Group#GROUP _woundedGroup -- @param #string _woundedGroupName @@ -1209,7 +1225,7 @@ function CSAR:_CheckGroupNotKIA(_woundedGroup, _woundedGroupName, _heliUnit, _he return inTransit end ---- Monitor in-flight returning groups. +--- (Internal) Monitor in-flight returning groups. -- @param #CSAR self -- @param #string heliname Heli name -- @param #string groupname Group name @@ -1245,7 +1261,7 @@ function CSAR:_ScheduledSARFlight(heliname,groupname) self:__Returning(-5,heliname,_woundedGroupName) end ---- Mark pilot as rescued and remove from tables. +--- (Internal) Mark pilot as rescued and remove from tables. -- @param #CSAR self -- @param Wrapper.Unit#UNIT _heliUnit function CSAR:_RescuePilots(_heliUnit) @@ -1270,7 +1286,7 @@ function CSAR:_RescuePilots(_heliUnit) self:__Rescued(-1,_heliUnit,_heliName, PilotsSaved) end ---- Check and return Wrappe.Unit#UNIT based on the name if alive. +--- (Internal) Check and return Wrappe.Unit#UNIT based on the name if alive. -- @param #CSAR self -- @param #string _unitname Name of Unit -- @return #UNIT or nil @@ -1284,7 +1300,7 @@ function CSAR:_GetSARHeli(_unitName) end end ---- Display message to single Unit. +--- (Internal) Display message to single Unit. -- @param #CSAR self -- @param Wrapper.Unit#UNIT _unit Unit #UNIT to display to. -- @param #string _text Text of message. @@ -1308,7 +1324,7 @@ function CSAR:_DisplayMessageToSAR(_unit, _text, _time, _clear, _speak) end end ---- Function to get string of a group\'s position. +--- (Internal) Function to get string of a group\'s position. -- @param #CSAR self -- @param Wrapper.Controllable#CONTROLLABLE _woundedGroup Group or Unit object. -- @return #string Coordinates as Text @@ -1334,7 +1350,7 @@ function CSAR:_GetPositionOfWounded(_woundedGroup) return _coordinatesText end ---- Display active SAR tasks to player. +--- (Internal) Display active SAR tasks to player. -- @param #CSAR self -- @param #string _unitName Unit to display to function CSAR:_DisplayActiveSAR(_unitName) @@ -1386,7 +1402,7 @@ function CSAR:_DisplayActiveSAR(_unitName) self:_DisplayMessageToSAR(_heli, _msg, self.messageTime*2) end ---- Find the closest downed pilot to a heli. +--- (Internal) Find the closest downed pilot to a heli. -- @param #CSAR self -- @param Wrapper.Unit#UNIT _heli Helicopter #UNIT -- @return #table Table of results @@ -1420,7 +1436,7 @@ function CSAR:_GetClosestDownedPilot(_heli) return { pilot = _closestGroup, distance = _shortestDistance, groupInfo = _closestGroupInfo } end ---- Fire a flare at the point of a downed pilot. +--- (Internal) Fire a flare at the point of a downed pilot. -- @param #CSAR self -- @param #string _unitName Name of the unit. function CSAR:_SignalFlare(_unitName) @@ -1455,7 +1471,7 @@ function CSAR:_SignalFlare(_unitName) end end ---- Display info to all SAR groups. +--- (Internal) Display info to all SAR groups. -- @param #CSAR self -- @param #string _message Message to display. -- @param #number _side Coalition of message. @@ -1472,7 +1488,7 @@ function CSAR:_DisplayToAllSAR(_message, _side, _messagetime) end end ----Request smoke at closest downed pilot. +---(Internal) Request smoke at closest downed pilot. --@param #CSAR self --@param #string _unitName Name of the helicopter function CSAR:_Reqsmoke( _unitName ) @@ -1504,7 +1520,7 @@ function CSAR:_Reqsmoke( _unitName ) end end ---- Determine distance to closest MASH. +--- (Internal) Determine distance to closest MASH. -- @param #CSAR self -- @param Wrapper.Unit#UNIT _heli Helicopter #UNIT -- @retunr @@ -1559,7 +1575,7 @@ function CSAR:_GetClosestMASH(_heli) end end ---- Display onboarded rescued pilots. +--- (Internal) Display onboarded rescued pilots. -- @param #CSAR self -- @param #string _unitName Name of the chopper function CSAR:_CheckOnboard(_unitName) @@ -1581,7 +1597,7 @@ function CSAR:_CheckOnboard(_unitName) end end ---- Populate F10 menu for CSAR players. +--- (Internal) Populate F10 menu for CSAR players. -- @param #CSAR self function CSAR:_AddMedevacMenuItem() self:T(self.lid .. " _AddMedevacMenuItem") @@ -1624,7 +1640,7 @@ function CSAR:_AddMedevacMenuItem() return end ---- Return distance in meters between two coordinates. +--- (Internal) Return distance in meters between two coordinates. -- @param #CSAR self -- @param Core.Point#COORDINATE _point1 Coordinate one -- @param Core.Point#COORDINATE _point2 Coordinate two @@ -1639,7 +1655,7 @@ function CSAR:_GetDistance(_point1, _point2) end end ---- Populate table with available beacon frequencies. +--- (Internal) Populate table with available beacon frequencies. -- @param #CSAR self function CSAR:_GenerateVHFrequencies() self:T(self.lid .. " _GenerateVHFrequencies") @@ -1710,7 +1726,7 @@ function CSAR:_GenerateVHFrequencies() self.FreeVHFFrequencies = FreeVHFFrequencies end ---- Pop frequency from prepopulated table. +--- (Internal) Pop frequency from prepopulated table. -- @param #CSAR self -- @return #number frequency function CSAR:_GenerateADFFrequency() @@ -1724,7 +1740,7 @@ function CSAR:_GenerateADFFrequency() return _vhf end ---- Function to determine clockwise direction for flares. +--- (Internal) Function to determine clockwise direction for flares. -- @param #CSAR self -- @param Wrapper.Unit#UNIT _heli The Helicopter -- @param Wrapper.Group#GROUP _group The downed Group @@ -1748,7 +1764,7 @@ function CSAR:_GetClockDirection(_heli, _group) return clock end ---- Function to add beacon to downed pilot. +--- (Internal) Function to add beacon to downed pilot. -- @param #CSAR self -- @param Wrapper.Group#GROUP _group Group #GROUP object. -- @param #number _freq Frequency to use @@ -1774,7 +1790,7 @@ function CSAR:_AddBeaconToGroup(_group, _freq) end end ---- Helper function to (re-)add beacon to downed pilot. +--- (Internal) Helper function to (re-)add beacon to downed pilot. -- @param #CSAR self -- @param #table _args Arguments function CSAR:_RefreshRadioBeacons() @@ -1790,7 +1806,7 @@ function CSAR:_RefreshRadioBeacons() end end ---- Helper function to count active downed pilots. +--- (Internal) Helper function to count active downed pilots. -- @param #CSAR self -- @return #number Number of pilots in the field. function CSAR:_CountActiveDownedPilots() @@ -1805,7 +1821,7 @@ function CSAR:_CountActiveDownedPilots() return PilotsInFieldN end ---- Helper to decide if we're over max limit. +--- (Internal) Helper to decide if we're over max limit. -- @param #CSAR self -- @return #boolean True or false. function CSAR:_ReachedPilotLimit() @@ -1824,7 +1840,7 @@ end --- FSM internal Functions --- ------------------------------ ---- Function called after Start() event. +--- (Internal) Function called after Start() event. -- @param #CSAR self. -- @param #string From From state. -- @param #string Event Event triggered. @@ -1850,7 +1866,7 @@ function CSAR:onafterStart(From, Event, To) return self end ---- Function called before Status() event. +--- (Internal) Function called before Status() event. -- @param #CSAR self. -- @param #string From From state. -- @param #string Event Event triggered. @@ -1875,7 +1891,7 @@ function CSAR:onbeforeStatus(From, Event, To) return self end ---- Function called after Status() event. +--- (Internal) Function called after Status() event. -- @param #CSAR self. -- @param #string From From state. -- @param #string Event Event triggered. @@ -1912,7 +1928,7 @@ function CSAR:onafterStatus(From, Event, To) return self end ---- Function called after Stop() event. +--- (Internal) Function called after Stop() event. -- @param #CSAR self. -- @param #string From From state. -- @param #string Event Event triggered. @@ -1930,7 +1946,7 @@ function CSAR:onafterStop(From, Event, To) return self end ---- Function called before Approach() event. +--- (Internal) Function called before Approach() event. -- @param #CSAR self. -- @param #string From From state. -- @param #string Event Event triggered. @@ -1943,7 +1959,7 @@ function CSAR:onbeforeApproach(From, Event, To, Heliname, Woundedgroupname) return self end ---- Function called before Boarded() event. +--- (Internal) Function called before Boarded() event. -- @param #CSAR self. -- @param #string From From state. -- @param #string Event Event triggered. @@ -1956,7 +1972,7 @@ function CSAR:onbeforeBoarded(From, Event, To, Heliname, Woundedgroupname) return self end ---- Function called before Returning() event. +--- (Internal) Function called before Returning() event. -- @param #CSAR self. -- @param #string From From state. -- @param #string Event Event triggered. @@ -1969,7 +1985,7 @@ function CSAR:onbeforeReturning(From, Event, To, Heliname, Woundedgroupname) return self end ---- Function called before Rescued() event. +--- (Internal) Function called before Rescued() event. -- @param #CSAR self. -- @param #string From From state. -- @param #string Event Event triggered. @@ -1984,7 +2000,7 @@ function CSAR:onbeforeRescued(From, Event, To, HeliUnit, HeliName, PilotsSaved) return self end ---- Function called before PilotDown() event. +--- (Internal) Function called before PilotDown() event. -- @param #CSAR self. -- @param #string From From state. -- @param #string Event Event triggered. diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 211932703..6c62d5046 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -8,7 +8,7 @@ -- -- ## Missions: -- --- ### [CTLD - Combat Troop & Logistics Deployment](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/) +-- ### [CTLD - Combat Troop & Logistics Deployment](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20CTLD) -- -- === -- @@ -180,7 +180,7 @@ end do ------------------------------------------------------------------------- ---- **CTLD** class, extends #Core.Base#BASE, #Core.Fsm#FSM +--- **CTLD** class, extends Core.Base#BASE, Core.Fsm#FSM -- @type CTLD -- @field #string ClassName Name of the class. -- @field #number verbose Verbosity level. @@ -489,7 +489,7 @@ CTLD.SkipFrequencies = { --- CTLD class version. -- @field #string version -CTLD.version="0.1.1b1" +CTLD.version="0.1.1b2" --- Instantiate a new CTLD. -- @param #CTLD self @@ -703,7 +703,7 @@ end -- Helper and User Functions ------------------------------------------------------------------- ---- Function to generate valid UHF Frequencies +--- (Internal) Function to generate valid UHF Frequencies -- @param #CTLD self function CTLD:_GenerateUHFrequencies() self:T(self.lid .. " _GenerateUHFrequencies") @@ -718,7 +718,7 @@ function CTLD:_GenerateUHFrequencies() return self end ---- Function to generate valid FM Frequencies +--- (Internal) Function to generate valid FM Frequencies -- @param #CTLD sel function CTLD:_GenerateFMFrequencies() self:T(self.lid .. " _GenerateFMrequencies") @@ -742,7 +742,7 @@ function CTLD:_GenerateFMFrequencies() return self end ---- Populate table with available VHF beacon frequencies. +--- (Internal) Populate table with available VHF beacon frequencies. -- @param #CTLD self function CTLD:_GenerateVHFrequencies() self:T(self.lid .. " _GenerateVHFrequencies") @@ -806,7 +806,7 @@ function CTLD:_GenerateVHFrequencies() return self end ---- Function to generate valid laser codes. +--- (Internal) Function to generate valid laser codes. -- @param #CTLD self function CTLD:_GenerateLaserCodes() self:T(self.lid .. " _GenerateLaserCodes") @@ -828,7 +828,7 @@ function CTLD:_GenerateLaserCodes() end end ---- Helper function to generate laser codes. +--- (Internal) Helper function to generate laser codes. -- @param #CTLD self -- @param #number _number -- @param #number _numberToFind @@ -846,7 +846,7 @@ function CTLD:_ContainsDigit(_number, _numberToFind) return false end ---- Event handler function +--- (Internal) Event handler function -- @param #CTLD self -- @param Core.Event#EVENTDATA EventData function CTLD:_EventHandler(EventData) @@ -873,7 +873,7 @@ function CTLD:_EventHandler(EventData) return self end ---- Function to load troops into a heli. +--- (Internal) Function to load troops into a heli. -- @param #CTLD self -- @param Wrapper.Group#GROUP Group -- @param Wrapper.Unit#UNIT Unit @@ -930,7 +930,7 @@ function CTLD:_LoadTroops(Group, Unit, Cargotype) return self end ---- Function to spawn crates in front of the heli. +--- (Internal) Function to spawn crates in front of the heli. -- @param #CTLD self -- @param Wrapper.Group#GROUP Group -- @param Wrapper.Unit#UNIT Unit @@ -1011,7 +1011,7 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop) return self end ---- Function to find and list nearby crates. +--- (Internal) Function to find and list nearby crates. -- @param #CTLD self -- @param Wrapper.Group#GROUP Group -- @param Wrapper.Unit#UNIT Unit @@ -1045,7 +1045,7 @@ function CTLD:_ListCratesNearby( _group, _unit) return self end ---- Return distance in meters between two coordinates. +--- (Internal) Return distance in meters between two coordinates. -- @param #CTLD self -- @param Core.Point#COORDINATE _point1 Coordinate one -- @param Core.Point#COORDINATE _point2 Coordinate two @@ -1060,7 +1060,7 @@ function CTLD:_GetDistance(_point1, _point2) end end ---- Function to find and return nearby crates. +--- (Internal) Function to find and return nearby crates. -- @param #CTLD self -- @param Wrapper.Group#GROUP _group Group -- @param Wrapper.Unit#UNIT _unit Unit @@ -1094,7 +1094,7 @@ function CTLD:_FindCratesNearby( _group, _unit, _dist) return found, index end ---- Function to get and load nearby crates. +--- (Internal) Function to get and load nearby crates. -- @param #CTLD self -- @param Wrapper.Group#GROUP Group -- @param Wrapper.Unit#UNIT Unit @@ -1194,7 +1194,7 @@ function CTLD:_LoadCratesNearby(Group, Unit) return self end ---- Function to list loaded cargo. +--- (Internal) Function to list loaded cargo. -- @param #CTLD self -- @param Wrapper.Group#GROUP Group -- @param Wrapper.Unit#UNIT Unit @@ -1249,7 +1249,7 @@ function CTLD:_ListCargo(Group, Unit) return self end ---- Function to unload troops from heli. +--- (Internal) Function to unload troops from heli. -- @param #CTLD self -- @param Wrapper.Group#GROUP Group -- @param Wrapper.Unit#UNIT Unit @@ -1326,7 +1326,7 @@ function CTLD:_UnloadTroops(Group, Unit) return self end ---- Function to unload crates from heli. +--- (Internal) Function to unload crates from heli. -- @param #CTLD self -- @param Wrapper.Group#GROUP Group -- @param Wrappe.Unit#UNIT Unit @@ -1384,7 +1384,7 @@ function CTLD:_UnloadCrates(Group, Unit) return self end ---- Function to build nearby crates. +--- (Internal) Function to build nearby crates. -- @param #CTLD self -- @param Wrapper.Group#GROUP Group -- @param Wrappe.Unit#UNIT Unit @@ -1464,7 +1464,7 @@ function CTLD:_BuildCrates(Group, Unit) return self end ---- Function to actually SPAWN buildables in the mission. +--- (Internal) Function to actually SPAWN buildables in the mission. -- @param #CTLD self -- @param Wrapper.Group#GROUP Group -- @param Wrapper.Group#UNIT Unit @@ -1496,7 +1496,7 @@ function CTLD:_BuildObjectFromCrates(Group,Unit,Build) return self end ---- Function to move group to WP zone. +--- (Internal) Function to move group to WP zone. -- @param #CTLD self -- @param Wrapper.Group#GROUP Group The Group to move. function CTLD:_MoveGroupToZone(Group) @@ -1523,7 +1523,7 @@ function CTLD:_MoveGroupToZone(Group) return self end ---- Housekeeping - Cleanup crates when build +--- (Internal) Housekeeping - Cleanup crates when build -- @param #CTLD self -- @param #table Crates Table of #CTLD_CARGO objects near the unit. -- @param #CTLD.Buildable Build Table build object. @@ -1575,7 +1575,7 @@ function CTLD:_CleanUpCrates(Crates,Build,Number) return self end ---- Housekeeping - Function to refresh F10 menus. +--- (Internal) Housekeeping - Function to refresh F10 menus. -- @param #CTLD self -- @return #CTLD self function CTLD:_RefreshF10Menus() @@ -1747,7 +1747,7 @@ function CTLD:DeactivateZone(Name,ZoneType) return self end ---- Function to obtain a valid FM frequency. +--- (Internal) Function to obtain a valid FM frequency. -- @param #CTLD self -- @param #string Name Name of zone. -- @return #CTLD.ZoneBeacon Beacon Beacon table. @@ -1768,7 +1768,7 @@ function CTLD:_GetFMBeacon(Name) return beacon end ---- Function to obtain a valid UHF frequency. +--- (Internal) Function to obtain a valid UHF frequency. -- @param #CTLD self -- @param #string Name Name of zone. -- @return #CTLD.ZoneBeacon Beacon Beacon table. @@ -1789,7 +1789,7 @@ function CTLD:_GetUHFBeacon(Name) return beacon end ---- Function to obtain a valid VHF frequency. +--- (Internal) Function to obtain a valid VHF frequency. -- @param #CTLD self -- @param #string Name Name of zone. -- @return #CTLD.ZoneBeacon Beacon Beacon table. @@ -1845,7 +1845,7 @@ function CTLD:AddCTLDZone(Name, Type, Color, Active, HasBeacon) return self end ---- Function to show list of radio beacons +--- (Internal) Function to show list of radio beacons -- @param #CTLD self -- @param Wrapper.Group#GROUP Group -- @param Wrapper.Unit#UNIT Unit @@ -1878,7 +1878,7 @@ function CTLD:_ListRadioBeacons(Group, Unit) return self end ---- Add radio beacon to zone. Runs 30 secs. +--- (Internal) Add radio beacon to zone. Runs 30 secs. -- @param #CTLD self -- @param #string Name Name of zone. -- @param #string Sound Name of soundfile. @@ -1898,7 +1898,7 @@ function CTLD:_AddRadioBeacon(Name, Sound, Mhz, Modulation) return self end ---- Function to refresh radio beacons +--- (Internal) Function to refresh radio beacons -- @param #CTLD self function CTLD:_RefreshRadioBeacons() self:I(self.lid .. " _RefreshRadioBeacons") @@ -1926,7 +1926,7 @@ function CTLD:_RefreshRadioBeacons() return self end ---- function to see if a unit is in a specific zone type. +--- (Internal) Function to see if a unit is in a specific zone type. -- @param #CTLD self -- @param Wrapper.Unit#UNIT Unit Unit -- @param #CTLD.CargoZoneType Zonetype Zonetype @@ -1978,7 +1978,7 @@ function CTLD:IsUnitInZone(Unit,Zonetype) return outcome, zonenameret, zoneret, maxdist end ---- Userfunction - Start smoke in a zone close to the Unit. +--- User function - Start smoke in a zone close to the Unit. -- @param #CTLD self -- @param Wrapper.Unit#UNIT Unit The Unit. -- @param #boolean Flare If true, flare instead. @@ -2020,6 +2020,7 @@ function CTLD:SmokeZoneNearBy(Unit, Flare) end return self end + --- User - Function to add/adjust unittype capabilities. -- @param #CTLD self -- @param #string Unittype The unittype to adjust. If passed as Wrapper.Unit#UNIT, it will search for the unit in the mission. @@ -2050,7 +2051,7 @@ end return self end - --- Check if a unit is hovering *in parameters*. + --- (Internal) Check if a unit is hovering *in parameters*. -- @param #CTLD self -- @param Wrapper.Unit#UNIT Unit -- @return #boolean Outcome @@ -2077,7 +2078,7 @@ end return outcome end - --- List if a unit is hovering *in parameters*. + --- (Internal) List if a unit is hovering *in parameters*. -- @param #CTLD self -- @param Wrapper.Group#GROUP Group -- @param Wrapper.Unit#UNIT Unit @@ -2090,7 +2091,7 @@ end return self end - --- Check if a unit is in a load zone and is hovering in parameters. + --- (Internal) Check if a unit is in a load zone and is hovering in parameters. -- @param #CTLD self -- @param Wrapper.Unit#UNIT Unit -- @return #boolean Outcome @@ -2100,7 +2101,7 @@ end return outcome end - --- Check if a unit is above ground. + --- (Internal) Check if a unit is above ground. -- @param #CTLD self -- @param Wrapper.Unit#UNIT Unit -- @return #boolean Outcome @@ -2117,7 +2118,7 @@ end end end - --- Autoload if we can do crates, have capacity free and are in a load zone. + --- (Internal) Autoload if we can do crates, have capacity free and are in a load zone. -- @param #CTLD self -- @param Wrapper.Unit#UNIT Unit -- @return #CTLD self @@ -2147,7 +2148,7 @@ end return self end - --- Run through all pilots and see if we autoload. + --- (Internal) Run through all pilots and see if we autoload. -- @param #CTLD self -- @return #CTLD self function CTLD:CheckAutoHoverload() @@ -2164,7 +2165,7 @@ end -- FSM functions ------------------------------------------------------------------- - --- FSM Function onafterStart. + --- (Internal) FSM Function onafterStart. -- @param #CTLD self -- @param #string From State. -- @param #string Event Trigger. @@ -2185,7 +2186,7 @@ end return self end - --- FSM Function onbeforeStatus. + --- (Internal) FSM Function onbeforeStatus. -- @param #CTLD self -- @param #string From State. -- @param #string Event Trigger. @@ -2199,7 +2200,7 @@ end return self end - --- FSM Function onafterStatus. + --- (Internal) FSM Function onafterStatus. -- @param #CTLD self -- @param #string From State. -- @param #string Event Trigger. @@ -2232,7 +2233,7 @@ end return self end - --- FSM Function onafterStop. + --- (Internal) FSM Function onafterStop. -- @param #CTLD self -- @param #string From State. -- @param #string Event Trigger. @@ -2246,7 +2247,7 @@ end return self end - --- FSM Function onbeforeTroopsPickedUp. + --- (Internal) FSM Function onbeforeTroopsPickedUp. -- @param #CTLD self -- @param #string From State. -- @param #string Event Trigger. @@ -2260,7 +2261,7 @@ end return self end - --- FSM Function onbeforeCratesPickedUp. + --- (Internal) FSM Function onbeforeCratesPickedUp. -- @param #CTLD self -- @param #string From State . -- @param #string Event Trigger. @@ -2274,7 +2275,7 @@ end return self end - --- FSM Function onbeforeTroopsDeployed. + --- (Internal) FSM Function onbeforeTroopsDeployed. -- @param #CTLD self -- @param #string From State. -- @param #string Event Trigger. @@ -2288,7 +2289,7 @@ end return self end - --- FSM Function onbeforeCratesDropped. + --- (Internal) FSM Function onbeforeCratesDropped. -- @param #CTLD self -- @param #string From State. -- @param #string Event Trigger. @@ -2302,7 +2303,7 @@ end return self end - --- FSM Function onbeforeCratesBuild. + --- (Internal) FSM Function onbeforeCratesBuild. -- @param #CTLD self -- @param #string From State. -- @param #string Event Trigger. From 3f5e322948ee7a4b1567482d62b41a63f5458d1e Mon Sep 17 00:00:00 2001 From: Celso Dantas Date: Sat, 26 Jun 2021 07:33:51 -0400 Subject: [PATCH 11/16] Update Airboss debug msg with BRC/Final heading (#1559) Including the BRC and Final Heading on the debug message is useful for squadrons relying on that msg to know what is the end heading of the carrier. The carrier tends to turns a few times before ending up on the final heading as it adjust into its track. Thus, having the BRC/Final Heading on the last message "Starting aircraft recovery Case %d ops." is useful. --- Moose Development/Moose/Ops/Airboss.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index a6924682d..f2ad2df53 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -15526,8 +15526,12 @@ function AIRBOSS:_MarshalCallRecoveryStart(case) -- Debug output. local text=string.format("Starting aircraft recovery Case %d ops.", case) - if case>1 then - text=text..string.format(" Marshal radial %03d°.", radial) + if case==1 then + text=text..string.format(" BRC %03d°.", self:GetBRC()) + elseif case==2 then + text=text..string.format(" Marshal radial %03d°. BRC %03d°.", radial, self:GetBRC()) + elseif case==3 then + text=text..string.format(" Marshal radial %03d°. Final heading %03d°.", radial, self:GetFinalBearing(false)) end self:T(self.lid..text) From 5a022a2246a76a6b8186f540ec5c29ef06d376e0 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 29 Jun 2021 08:50:46 +0200 Subject: [PATCH 12/16] Messages going to SAR flights only --- Moose Development/Moose/Ops/CSAR.lua | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index 7be6de481..07b8e103e 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -240,7 +240,7 @@ CSAR.AircraftType["Mi-24V"] = 8 --- CSAR class version. -- @field #string version -CSAR.version="0.1.4r4" +CSAR.version="0.1.5r1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -601,7 +601,8 @@ function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _pla local _spawnedGroup, _alias = self:_SpawnPilotInField(_country,_point) local _typeName = _typeName or "PoW" if not noMessage then - local m = MESSAGE:New("MAYDAY MAYDAY! " .. _typeName .. " is down. ",10,"INFO"):ToCoalition(self.coalition) + self:_DisplayToAllSAR("MAYDAY MAYDAY! " .. _typeName .. " is down. ", self.coalition, 10) + --local m = MESSAGE:New("MAYDAY MAYDAY! " .. _typeName .. " is down. ",10,"INFO"):ToCoalition(self.coalition) end if not _freq then @@ -759,8 +760,9 @@ function CSAR:_EventHandler(EventData) if self.takenOff[_event.IniUnitName] == true or _group:IsAirborne() then if self:_DoubleEjection(_unitname) then return - end - local m = MESSAGE:New("MAYDAY MAYDAY! " .. _unit:GetTypeName() .. " shot down. No Chute!",10,"Info"):ToCoalition(self.coalition) + end + self:_DisplayToAllSAR("MAYDAY MAYDAY! " .. _unit:GetTypeName() .. " shot down. No Chute!", self.coalition, 10) + --local m = MESSAGE:New("MAYDAY MAYDAY! " .. _unit:GetTypeName() .. " shot down. No Chute!",10,"Info"):ToCoalition(self.coalition) else self:T(self.lid .. " Pilot has not taken off, ignore") end From 3289ad281794f15089caf43d2ff3a7f0aa4f93e9 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 29 Jun 2021 17:49:42 +0200 Subject: [PATCH 13/16] Added basic support for Hercules mod --- Moose Development/Moose/Ops/CTLD.lua | 173 ++++++++++++++++++++++++--- 1 file changed, 156 insertions(+), 17 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 6c62d5046..4adaa0c24 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -22,7 +22,7 @@ -- @module Ops.CTLD -- @image OPS_CTLD.jpg --- Date: 22 June 2021 +-- Date: July 2021 do ------------------------------------------------------ @@ -379,6 +379,27 @@ do -- -- Lists hover parameters and indicates if these are curently fulfilled. Also @see options on hover heights. -- +-- ## 5. Support for Hercules mod by Anubis +-- +-- Basic support for the Hercules mod By Anubis has been build into CTLD. Currently this does **not** cover objects and troops which can +-- be loaded from the Rearm/Refuel menu, i.e. you can drop them into the field, but you cannot use them in functions scripted with this class. +-- +-- local my_ctld = CTLD:New(coalition.side.BLUE,{"Helicargo", "Hercules"},"Lufttransportbrigade I") +-- +-- Enable these options for Hercules support: +-- +-- my_ctld.enableHercules = true +-- my_ctld.HercMinAngels = 155 -- for troop/cargo drop via chute in meters, ca 470 ft +-- my_ctld.HercMaxAngels = 2000 -- for troop/cargo drop via chute in meters, ca 6000 ft +-- +-- Also, the following options need to be set to `true`: +-- +-- my_ctld.useprefix = true -- this is true by default +-- +-- Standard transport capabilities as per the real Hercules are: +-- +-- ["Hercules"] = {type="Hercules", crates=true, troops=true, cratelimit = 7, trooplimit = 64}, -- 19t cargo, 64 paratroopers +-- -- @field #CTLD CTLD = { ClassName = "CTLD", @@ -415,6 +436,7 @@ CTLD = { -- DONE: Troops running to WP Zone -- DONE: Zone Radio Beacons -- DONE: Stats Running +-- DONE: Added support for Hercules ------------------------------ --- Radio Beacons @@ -468,6 +490,7 @@ CTLD.UnitTypes = { ["Ka-50"] = {type="Ka-50", crates=false, troops=false, cratelimit = 0, trooplimit = 0}, ["Mi-24P"] = {type="Mi-24P", crates=true, troops=true, cratelimit = 2, trooplimit = 8}, ["Mi-24V"] = {type="Mi-24V", crates=true, troops=true, cratelimit = 2, trooplimit = 8}, + ["Hercules"] = {type="Hercules", crates=true, troops=true, cratelimit = 7, trooplimit = 64}, -- 19t cargo, 64 paratroopers } --- Updated and sorted known NDB beacons (in kHz!) from the available maps @@ -590,7 +613,8 @@ function CTLD:New(Coalition, Prefixes, Alias) -- setup self.CrateDistance = 30 -- list/load crates in this radius - self.prefixes = Prefixes or {"cargoheli"} + self.prefixes = Prefixes or {"Cargoheli"} + --self.I({prefixes = self.prefixes}) self.useprefix = true self.maximumHoverHeight = 15 @@ -602,6 +626,11 @@ function CTLD:New(Coalition, Prefixes, Alias) self.movetroopstowpzone = true self.movetroopsdistance = 5000 + -- added support Hercules Mod + self.enableHercules = true + self.HercMinAngels = 165 -- for troop/cargo drop via chute + self.HercMaxAngels = 2000 -- for troop/cargo drop via chute + for i=1,100 do math.random() end @@ -863,7 +892,12 @@ function CTLD:_EventHandler(EventData) local _group = event.IniGroup if _unit:IsHelicopter() or _group:IsHelicopter() then self:_RefreshF10Menus() - end + end + -- Herc support + self:I(_unit:GetTypeName()) + if _unit:GetTypeName() == "Hercules" and self.enableHercules then + self:_RefreshF10Menus() + end return elseif event.id == EVENTS.PlayerLeaveUnit then -- remove from pilot table @@ -965,6 +999,7 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop) return self end -- spawn crates in front of helicopter + local IsHerc = self:IsHercules(Unit) -- Herc local cargotype = Cargo -- #CTLD_CARGO local number = number or cargotype:GetCratesNeeded() --#number local cratesneeded = cargotype:GetCratesNeeded() --#number @@ -980,8 +1015,14 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop) for i=1,number do local cratealias = string.format("%s-%d", cratetemplate, math.random(1,100000)) local cratedistance = i*4 + 6 + if IsHerc then + -- wider radius + cratedistance = i*4 + 12 + end local rheading = math.floor(math.random(90,270) * heading + 1 / 360) - local rheading = rheading + 180 -- mirror + if not IsHerc then + rheading = rheading + 180 -- mirror for Helis + end if rheading > 360 then rheading = rheading - 360 end -- catch > 360 local cratecoord = position:Translate(cratedistance,rheading) local cratevec2 = cratecoord:GetVec2() @@ -1249,6 +1290,18 @@ function CTLD:_ListCargo(Group, Unit) return self end +--- (Internal) Function to check if a unit is a Hercules C-130. +-- @param #CTLD self +-- @param Wrapper.Unit#UNIT Unit +-- @return #boolean Outcome +function CTLD:IsHercules(Unit) + if Unit:GetTypeName() == "Hercules" then + return true + else + return false + end +end + --- (Internal) Function to unload troops from heli. -- @param #CTLD self -- @param Wrapper.Group#GROUP Group @@ -1263,6 +1316,11 @@ function CTLD:_UnloadTroops(Group, Unit) end -- check for hover unload local hoverunload = self:IsCorrectHover(Unit) --if true we\'re hovering in parameters + local IsHerc = self:IsHercules(Unit) + if IsHerc then + -- no hover but airdrop here + hoverunload = self:IsCorrectFlightParameters(Unit) + end -- check if we\'re landed local grounded = not self:IsUnitInAir(Unit) -- Get what we have loaded @@ -1280,8 +1338,14 @@ function CTLD:_UnloadTroops(Group, Unit) local name = cargo:GetName() or "none" local temptable = cargo:GetTemplates() or {} local position = Group:GetCoordinate() - local zone = ZONE_GROUP:New(string.format("Unload zone-%s",unitname),Group,100) - local randomcoord = zone:GetRandomCoordinate(10,30):GetVec2() + local zoneradius = 100 -- drop zone radius + local factor = 1 + if IsHerc then + factor = cargo:GetCratesNeeded() or 1 -- spread a bit more if airdropping + zoneradius = Unit:GetVelocityMPS() or 100 + end + local zone = ZONE_GROUP:New(string.format("Unload zone-%s",unitname),Group,zoneradius*factor) + local randomcoord = zone:GetRandomCoordinate(10,30*factor):GetVec2() for _,_template in pairs(temptable) do self.TroopCounter = self.TroopCounter + 1 local alias = string.format("%s-%d", _template, math.random(1,100000)) @@ -1321,7 +1385,11 @@ function CTLD:_UnloadTroops(Group, Unit) self.Loaded_Cargo[unitname] = nil self.Loaded_Cargo[unitname] = loaded else - local m = MESSAGE:New("Nothing loaded or not hovering within parameters!",10,"CTLD"):ToGroup(Group) + if IsHerc then + local m = MESSAGE:New("Nothing loaded or not within airdrop parameters!",10,"CTLD"):ToGroup(Group) + else + local m = MESSAGE:New("Nothing loaded or not hovering within parameters!",10,"CTLD"):ToGroup(Group) + end end return self end @@ -1342,6 +1410,11 @@ function CTLD:_UnloadCrates(Group, Unit) end -- check for hover unload local hoverunload = self:IsCorrectHover(Unit) --if true we\'re hovering in parameters + local IsHerc = self:IsHercules(Unit) + if IsHerc then + -- no hover but airdrop here + hoverunload = self:IsCorrectFlightParameters(Unit) + end -- check if we\'re landed local grounded = not self:IsUnitInAir(Unit) -- Get what we have loaded @@ -1379,7 +1452,11 @@ function CTLD:_UnloadCrates(Group, Unit) self.Loaded_Cargo[unitname] = nil self.Loaded_Cargo[unitname] = loaded else - local m = MESSAGE:New("Nothing loaded or not hovering within parameters!",10,"CTLD"):ToGroup(Group) + if IsHerc then + local m = MESSAGE:New("Nothing loaded or not within airdrop parameters!",10,"CTLD"):ToGroup(Group) + else + local m = MESSAGE:New("Nothing loaded or not hovering within parameters!",10,"CTLD"):ToGroup(Group) + end end return self end @@ -1579,10 +1656,10 @@ end -- @param #CTLD self -- @return #CTLD self function CTLD:_RefreshF10Menus() - self:T(self.lid .. " _RefreshF10Menus") + self:I(self.lid .. " _RefreshF10Menus") local PlayerSet = self.PilotGroups -- Core.Set#SET_GROUP local PlayerTable = PlayerSet:GetSetObjects() -- #table of #GROUP objects - + --self:I({PlayerTable=PlayerTable}) -- rebuild units table local _UnitList = {} for _key, _group in pairs (PlayerTable) do @@ -1644,7 +1721,11 @@ function CTLD:_RefreshF10Menus() local unloadmenu1 = MENU_GROUP_COMMAND:New(_group,"Drop troops",toptroops, self._UnloadTroops, self, _group, _unit):Refresh() end local rbcns = MENU_GROUP_COMMAND:New(_group,"List active zone beacons",topmenu, self._ListRadioBeacons, self, _group, _unit) - local hoverpars = MENU_GROUP_COMMAND:New(_group,"Show hover parameters",topmenu, self._ShowHoverParams, self, _group, _unit):Refresh() + if unittype == "Hercules" then + local hoverpars = MENU_GROUP_COMMAND:New(_group,"Show flight parameters",topmenu, self._ShowFlightParams, self, _group, _unit):Refresh() + else + local hoverpars = MENU_GROUP_COMMAND:New(_group,"Show hover parameters",topmenu, self._ShowHoverParams, self, _group, _unit):Refresh() + end self.MenusDone[_unitName] = true end -- end group end -- end unit @@ -1901,7 +1982,7 @@ end --- (Internal) Function to refresh radio beacons -- @param #CTLD self function CTLD:_RefreshRadioBeacons() - self:I(self.lid .. " _RefreshRadioBeacons") + self:T(self.lid .. " _RefreshRadioBeacons") local zones = {[1] = self.pickupZones, [2] = self.wpZones, [3] = self.dropOffZones} for i=1,3 do @@ -2070,7 +2151,35 @@ end local minh = self.minimumHoverHeight -- 5 local mspeed = 2 -- 2 m/s self:T(string.format("%s Unit parameters: at %dm AGL with %dmps",self.lid,aheight,uspeed)) - if (uspeed <= maxh) and (aheight <= maxh) and (aheight >= minh) then + if (uspeed <= mspeed) and (aheight <= maxh) and (aheight >= minh) then + -- yep within parameters + outcome = true + end + end + return outcome + end + + --- (Internal) Check if a Hercules is flying *in parameters* for air drops. + -- @param #CTLD self + -- @param Wrapper.Unit#UNIT Unit + -- @return #boolean Outcome + function CTLD:IsCorrectFlightParameters(Unit) + self:T(self.lid .. " IsCorrectFlightParameters") + local outcome = false + -- see if we are in air and within parameters. + if self:IsUnitInAir(Unit) then + -- get speed and height + local uspeed = Unit:GetVelocityMPS() + local uheight = Unit:GetHeight() + local ucoord = Unit:GetCoordinate() + local gheight = ucoord:GetLandHeight() + local aheight = uheight - gheight -- height above ground + local maxh = self.HercMinAngels-- 1500m + local minh = self.HercMaxAngels -- 5000m + local mspeed = 2 -- 2 m/s + -- TODO:Add speed test for Herc, should not be above 280kph/150kn + self:T(string.format("%s Unit parameters: at %dm AGL with %dmps",self.lid,aheight,uspeed)) + if (aheight <= maxh) and (aheight >= minh) then -- yep within parameters outcome = true end @@ -2086,17 +2195,34 @@ end local inhover = self:IsCorrectHover(Unit) local htxt = "true" if not inhover then htxt = "false" end - local text = string.format("Hover parameter (autoload):\n - Min height %dm \n - Max height %dm \n - Max speed 2mps \n - In parameter: %s", self.minimumHoverHeight, self.maximumHoverHeight, htxt) + local text = string.format("Hover parameters (autoload/drop):\n - Min height %dm \n - Max height %dm \n - Max speed 2mps \n - In parameter: %s", self.minimumHoverHeight, self.maximumHoverHeight, htxt) local m = MESSAGE:New(text,10,"CTLD",false):ToGroup(Group) return self end + --- (Internal) List if a Herc unit is flying *in parameters*. + -- @param #CTLD self + -- @param Wrapper.Group#GROUP Group + -- @param Wrapper.Unit#UNIT Unit + function CTLD:_ShowFlightParams(Group,Unit) + local inhover = self:IsCorrectFlightParameters(Unit) + local htxt = "true" + if not inhover then htxt = "false" end + local minheight = UTILS.MetersToFeet(self.HercMinAngels) + local maxheight = UTILS.MetersToFeet(self.HercMaxAngels) + local text = string.format("Flight parameters (airdrop):\n - Min height %dft \n - Max height %dft \n - In parameter: %s", minheight, maxheight, htxt) + local m = MESSAGE:New(text,10,"CTLD",false):ToGroup(Group) + return self + end + + --- (Internal) Check if a unit is in a load zone and is hovering in parameters. -- @param #CTLD self -- @param Wrapper.Unit#UNIT Unit -- @return #boolean Outcome function CTLD:CanHoverLoad(Unit) self:T(self.lid .. " CanHoverLoad") + if self:IsHercules(Unit) then return false end local outcome = self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD) and self:IsCorrectHover(Unit) return outcome end @@ -2107,11 +2233,15 @@ end -- @return #boolean Outcome function CTLD:IsUnitInAir(Unit) -- get speed and height + local minheight = self.minimumHoverHeight + if self.enableHercules and Unit:GetTypeName() == "Hercules" then + minheight = 5.1 -- herc is 5m AGL on the ground + end local uheight = Unit:GetHeight() local ucoord = Unit:GetCoordinate() local gheight = ucoord:GetLandHeight() local aheight = uheight - gheight -- height above ground - if aheight >= self.minimumHoverHeight then + if aheight >= minheight then return true else return false @@ -2174,8 +2304,17 @@ end function CTLD:onafterStart(From, Event, To) self:I({From, Event, To}) if self.useprefix then - self.PilotGroups = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(self.prefixes):FilterCategoryHelicopter():FilterStart() + local prefix = self.prefixes + self:I({prefix=prefix}) + if self.enableHercules then + --self:I("CTLD with prefixes and Hercules") + self.PilotGroups = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(prefix):FilterStart() + else + --self:I("CTLD with prefixes NO Hercules") + self.PilotGroups = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(prefix):FilterCategoryHelicopter():FilterStart() + end else + --self:I("CTLD NO prefixes NO Hercules") self.PilotGroups = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterCategoryHelicopter():FilterStart() end -- Events @@ -2320,4 +2459,4 @@ end end -- end do ------------------------------------------------------------------- -- End Ops.CTLD.lua -------------------------------------------------------------------- +------------------------------------------------------------------- \ No newline at end of file From dca626bbcbabf1edf7e9bbd622b6e700416dd70e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 30 Jun 2021 17:59:43 +0200 Subject: [PATCH 14/16] CTLD - added option to suppress messaging, added event for `OnAfterTroopsRTB` CSAR - minor bugfix --- Moose Development/Moose/Ops/CSAR.lua | 9 +- Moose Development/Moose/Ops/CTLD.lua | 224 ++++++++++++++++++--------- 2 files changed, 152 insertions(+), 81 deletions(-) diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index 07b8e103e..4a9344a54 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -187,7 +187,7 @@ CSAR = { useprefix = true, -- Use the Prefixed defined below, Requires Unit have the Prefix defined below csarPrefix = {}, template = nil, - bluemash = {}, + mash = {}, smokecolor = 4, rescues = 0, rescuedpilots = 0, @@ -240,13 +240,14 @@ CSAR.AircraftType["Mi-24V"] = 8 --- CSAR class version. -- @field #string version -CSAR.version="0.1.5r1" +CSAR.version="0.1.5r2" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- DONE: SRS Integration (to be tested) +-- TODO: Maybe - add option to smoke/flare closest MASH ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor @@ -353,7 +354,7 @@ function CSAR:New(Coalition, Template, Alias) self.csarPrefix = { "helicargo", "MEDEVAC"} -- prefixes used for useprefix=true - DON\'T use # in names! self.template = Template or "generic" -- template for downed pilot self.mashprefix = {"MASH"} -- prefixes used to find MASHes - self.bluemash = SET_GROUP:New():FilterCoalitions(self.coalition):FilterPrefixes(self.mashprefix):FilterOnce() -- currently only GROUP objects, maybe support STATICs also? + self.mash = SET_GROUP:New():FilterCoalitions(self.coalition):FilterPrefixes(self.mashprefix):FilterOnce() -- currently only GROUP objects, maybe support STATICs also? self.autosmoke = false -- automatically smoke location when heli is near -- added 0.1.4 self.limitmaxdownedpilots = true @@ -1528,7 +1529,7 @@ end -- @retunr function CSAR:_GetClosestMASH(_heli) self:T(self.lid .. " _GetClosestMASH") - local _mashset = self.bluemash -- Core.Set#SET_GROUP + local _mashset = self.mash -- Core.Set#SET_GROUP local _mashes = _mashset:GetSetObjects() -- #table local _shortestDistance = -1 local _distance = 0 diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 4adaa0c24..78edc5a75 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -270,6 +270,7 @@ do -- my_ctld.movetroopstowpzone = true -- Troops and vehicles will move to the nearest MOVE zone... -- my_ctld.movetroopsdistance = 5000 -- .. but only if this far away (in meters) -- my_ctld.smokedistance = 2000 -- Only smoke or flare zones if requesting player unit is this far away (in meters) +-- my_ctld.suppressmessages = false -- Set to true if you want to script your own messages. -- -- ## 2.1 User functions -- @@ -512,7 +513,7 @@ CTLD.SkipFrequencies = { --- CTLD class version. -- @field #string version -CTLD.version="0.1.1b2" +CTLD.version="0.1.2b1" --- Instantiate a new CTLD. -- @param #CTLD self @@ -521,7 +522,6 @@ CTLD.version="0.1.1b2" -- @param #string Alias Alias of this CTLD for logging. -- @return #CTLD self function CTLD:New(Coalition, Prefixes, Alias) - -- TODO: CTLD Marker -- Inherit everything from FSM class. local self=BASE:Inherit(self, FSM:New()) -- #CTLD @@ -568,14 +568,15 @@ function CTLD:New(Coalition, Prefixes, Alias) -- Add FSM transitions. -- From State --> Event --> To State - self:AddTransition("Stopped", "Start", "Running") -- Start FSM. - self:AddTransition("*", "Status", "*") -- CTLD status update. + self:AddTransition("Stopped", "Start", "Running") -- Start FSM. + self:AddTransition("*", "Status", "*") -- CTLD status update. self:AddTransition("*", "TroopsPickedUp", "*") -- CTLD pickup event. self:AddTransition("*", "CratesPickedUp", "*") -- CTLD pickup event. - self:AddTransition("*", "TroopsDeployed", "*") -- CTLD deploy event. + self:AddTransition("*", "TroopsDeployed", "*") -- CTLD deploy event. + self:AddTransition("*", "TroopsRTB", "*") -- CTLD deploy event. self:AddTransition("*", "CratesDropped", "*") -- CTLD deploy event. self:AddTransition("*", "CratesBuild", "*") -- CTLD build event. - self:AddTransition("*", "Stop", "Stopped") -- Stop FSM. + self:AddTransition("*", "Stop", "Stopped") -- Stop FSM. -- tables self.PilotGroups ={} @@ -627,10 +628,13 @@ function CTLD:New(Coalition, Prefixes, Alias) self.movetroopsdistance = 5000 -- added support Hercules Mod - self.enableHercules = true + self.enableHercules = false self.HercMinAngels = 165 -- for troop/cargo drop via chute self.HercMaxAngels = 2000 -- for troop/cargo drop via chute + -- message suppression + self.suppressmessages = false + for i=1,100 do math.random() end @@ -678,7 +682,7 @@ function CTLD:New(Coalition, Prefixes, Alias) -- @param #string To State. -- @param Wrapper.Group#GROUP Group Group Object. -- @param Wrapper.Unit#UNIT Unit Unit Object. - -- @param #CTLD_CARGO Cargo Cargo crate. + -- @param #CTLD_CARGO Cargo Cargo troops. -- @return #CTLD self --- FSM Function OnAfterCratesPickedUp. @@ -725,6 +729,15 @@ function CTLD:New(Coalition, Prefixes, Alias) -- @param Wrapper.Group#GROUP Vehicle The #GROUP object of the vehicle or FOB build. -- @return #CTLD self + --- FSM Function OnAfterTroopsRTB. + -- @function [parent=#CTLD] OnAfterTroopsRTB + -- @param #CTLD self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Group#GROUP Group Group Object. + -- @param Wrapper.Unit#UNIT Unit Unit Object. + return self end @@ -894,7 +907,7 @@ function CTLD:_EventHandler(EventData) self:_RefreshF10Menus() end -- Herc support - self:I(_unit:GetTypeName()) + --self:T_unit:GetTypeName()) if _unit:GetTypeName() == "Hercules" and self.enableHercules then self:_RefreshF10Menus() end @@ -907,6 +920,20 @@ function CTLD:_EventHandler(EventData) return self end +--- (Internal) Function to message a group. +-- @param #CTLD self +-- @param #string Text The text to display. +-- @param #number Time Number of seconds to display the message. +-- @param #boolean Clearscreen Clear screen or not. +-- @param Wrapper.Group#GROUP Group The group receiving the message. +function CTLD:_SendMessage(Text, Time, Clearscreen, Group) + self:T(self.lid .. " _SendMessage") + if not self.suppressmessages then + local m = MESSAGE:New(Text,Time,"CTLD",Clearscreen):ToGroup(Group) + end + return self +end + --- (Internal) Function to load troops into a heli. -- @param #CTLD self -- @param Wrapper.Group#GROUP Group @@ -920,10 +947,11 @@ function CTLD:_LoadTroops(Group, Unit, Cargotype) -- check if we are in LOAD zone local inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD) if not inzone then - local m = MESSAGE:New("You are not close enough to a logistics zone!",15,"CTLD"):ToGroup(Group) + self:_SendMessage("You are not close enough to a logistics zone!", 10, false, Group) if not self.debug then return self end elseif not grounded and not hoverload then - local m = MESSAGE:New("You need to land or hover in position to load!",15,"CTLD"):ToGroup(Group) + self:_SendMessage("You need to land or hover in position to load!", 10, false, Group) + --local m = MESSAGE:New("You need to land or hover in position to load!",15,"CTLD"):ToGroup(Group) if not self.debug then return self end end -- load troops into heli @@ -932,7 +960,7 @@ function CTLD:_LoadTroops(Group, Unit, Cargotype) local unitname = unit:GetName() local cargotype = Cargotype -- #CTLD_CARGO local cratename = cargotype:GetName() -- #string - self:T(self.lid .. string.format("Troops %s requested", cratename)) + --self:Tself.lid .. string.format("Troops %s requested", cratename)) -- see if this heli can load troops local unittype = unit:GetTypeName() local capabilities = self.UnitTypes[unittype] -- #CTLD.UnitCapabilities @@ -952,13 +980,15 @@ function CTLD:_LoadTroops(Group, Unit, Cargotype) loaded.Cargo = {} end if troopsize + numberonboard > trooplimit then - local m = MESSAGE:New("Sorry, we\'re crammed already!",10,"CTLD",true):ToGroup(group) + self:_SendMessage("Sorry, we\'re crammed already!", 10, false, Group) + --local m = MESSAGE:New("Sorry, we\'re crammed already!",10,"CTLD",true):ToGroup(group) return else loaded.Troopsloaded = loaded.Troopsloaded + troopsize table.insert(loaded.Cargo,Cargotype) self.Loaded_Cargo[unitname] = loaded - local m = MESSAGE:New("Troops boarded!",10,"CTLD",true):ToGroup(group) + self:_SendMessage("Troops boarded!", 10, false, Group) + --local m = MESSAGE:New("Troops boarded!",10,"CTLD",true):ToGroup(group) self:__TroopsPickedUp(1,Group, Unit, Cargotype) end return self @@ -974,7 +1004,7 @@ end function CTLD:_GetCrates(Group, Unit, Cargo, number, drop) self:T(self.lid .. " _GetCrates") local cgoname = Cargo:GetName() - self:T({cgoname, number, drop}) + --self:T{cgoname, number, drop}) -- check if we are in LOAD zone local inzone = true @@ -985,7 +1015,8 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop) end if not inzone then - local m = MESSAGE:New("You are not close enough to a logistics zone!",15,"CTLD"):ToGroup(Group) + self:_SendMessage("You are not close enough to a logistics zone!", 10, false, Group) + --local m = MESSAGE:New("You are not close enough to a logistics zone!",15,"CTLD"):ToGroup(Group) if not self.debug then return self end end @@ -995,7 +1026,8 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop) local loaddist = self.CrateDistance or 30 local nearcrates, numbernearby = self:_FindCratesNearby(Group,Unit,loaddist) if numbernearby >= canloadcratesno and not drop then - local m = MESSAGE:New("There are enough crates nearby already! Take care of those first!",15,"CTLD"):ToGroup(Group) + self:_SendMessage("There are enough crates nearby already! Take care of those first!", 10, false, Group) + --local m = MESSAGE:New("There are enough crates nearby already! Take care of those first!",15,"CTLD"):ToGroup(Group) return self end -- spawn crates in front of helicopter @@ -1004,7 +1036,7 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop) local number = number or cargotype:GetCratesNeeded() --#number local cratesneeded = cargotype:GetCratesNeeded() --#number local cratename = cargotype:GetName() - self:T(self.lid .. string.format("Crate %s requested", cratename)) + --self:Tself.lid .. string.format("Crate %s requested", cratename)) local cratetemplate = "Container"-- #string -- get position and heading of heli local position = Unit:GetCoordinate() @@ -1047,8 +1079,9 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop) if drop then text = string.format("Crates for %s have been dropped!",cratename) self:__CratesDropped(1, Group, Unit, droppedcargo) - end - local m = MESSAGE:New(text,15,"CTLD",true):ToGroup(Group) + end + self:_SendMessage(text, 10, false, Group) + --local m = MESSAGE:New(text,15,"CTLD",true):ToGroup(Group) return self end @@ -1079,9 +1112,11 @@ function CTLD:_ListCratesNearby( _group, _unit) text:Add("--------- N O N E ------------") end text:Add("------------------------------------------------------------") - local m = MESSAGE:New(text:Text(),15,"CTLD",true):ToGroup(_group) + self:_SendMessage(text:Text(), 30, true, _group) + --local m = MESSAGE:New(text:Text(),15,"CTLD",true):ToGroup(_group) else - local m = MESSAGE:New(string.format("No (loadable) crates within %d meters!",finddist),15,"CTLD",true):ToGroup(_group) + self:_SendMessage(string.format("No (loadable) crates within %d meters!",finddist), 10, false, _group) + --local m = MESSAGE:New(string.format("No (loadable) crates within %d meters!",finddist),15,"CTLD",true):ToGroup(_group) end return self end @@ -1129,7 +1164,7 @@ function CTLD:_FindCratesNearby( _group, _unit, _dist) end end end - self:T(string.format("Found crates = %d",index)) + --self:Tstring.format("Found crates = %d",index)) -- table.sort(found) --self:T({found}) return found, index @@ -1160,11 +1195,14 @@ function CTLD:_LoadCratesNearby(Group, Unit) -- --> hover or land if not forcedhover ----------------------------------------- if not cancrates then - local m = MESSAGE:New("Sorry this chopper cannot carry crates!",10,"CTLD"):ToGroup(Group) + self:_SendMessage("Sorry this chopper cannot carry crates!", 10, false, Group) + --local m = MESSAGE:New("Sorry this chopper cannot carry crates!",10,"CTLD"):ToGroup(Group) elseif self.forcehoverload and not canhoverload then - local m = MESSAGE:New("Hover over the crates to pick them up!",10,"CTLD"):ToGroup(Group) + self:_SendMessage("Hover over the crates to pick them up!", 10, false, Group) + --local m = MESSAGE:New("Hover over the crates to pick them up!",10,"CTLD"):ToGroup(Group) elseif not grounded and not canhoverload then - local m = MESSAGE:New("Land or hover over the crates to pick them up!",10,"CTLD"):ToGroup(Group) + self:_SendMessage("Land or hover over the crates to pick them up!", 10, false, Group) + --local m = MESSAGE:New("Land or hover over the crates to pick them up!",10,"CTLD"):ToGroup(Group) else -- have we loaded stuff already? local numberonboard = 0 @@ -1182,7 +1220,8 @@ function CTLD:_LoadCratesNearby(Group, Unit) local finddist = self.CrateDistance or 30 local nearcrates,number = self:_FindCratesNearby(Group,Unit,finddist) -- #table if number == 0 or numberonboard == cratelimit then - local m = MESSAGE:New("Sorry no loadable crates nearby or fully loaded!",10,"CTLD"):ToGroup(Group) + self:_SendMessage("Sorry no loadable crates nearby or fully loaded!", 10, false, Group) + --local m = MESSAGE:New("Sorry no loadable crates nearby or fully loaded!",10,"CTLD"):ToGroup(Group) return -- exit else -- go through crates and load @@ -1210,7 +1249,8 @@ function CTLD:_LoadCratesNearby(Group, Unit) -- destroy crate crate:GetPositionable():Destroy() crate.Positionable = nil - local m = MESSAGE:New(string.format("Crate ID %d for %s loaded!",crate:GetID(),crate:GetName()),10,"CTLD"):ToGroup(Group) + self:_SendMessage(string.format("Crate ID %d for %s loaded!",crate:GetID(),crate:GetName()), 10, false, Group) + --local m = MESSAGE:New(string.format("Crate ID %d for %s loaded!",crate:GetID(),crate:GetName()),10,"CTLD"):ToGroup(Group) self:__CratesPickedUp(1, Group, Unit, crate) end --if loaded.Cratesloaded == cratelimit then break end @@ -1283,9 +1323,11 @@ function CTLD:_ListCargo(Group, Unit) end report:Add("------------------------------------------------------------") local text = report:Text() - local m = MESSAGE:New(text,30,"CTLD",true):ToGroup(Group) + self:_SendMessage(text, 30, true, Group) + --local m = MESSAGE:New(text,30,"CTLD",true):ToGroup(Group) else - local m = MESSAGE:New(string.format("Nothing loaded!\nTroop limit: %d | Crate limit %d",trooplimit,cratelimit),10,"CTLD"):ToGroup(Group) + self:_SendMessage(string.format("Nothing loaded!\nTroop limit: %d | Crate limit %d",trooplimit,cratelimit), 10, false, Group) + --local m = MESSAGE:New(string.format("Nothing loaded!\nTroop limit: %d | Crate limit %d",trooplimit,cratelimit),10,"CTLD"):ToGroup(Group) end return self end @@ -1358,12 +1400,15 @@ function CTLD:_UnloadTroops(Group, Unit) end end -- template loop cargo:SetWasDropped(true) - local m = MESSAGE:New(string.format("Dropped Troops %s into action!",name),10,"CTLD"):ToGroup(Group) - self:__TroopsDeployed(1, Group, Unit, name, self.DroppedTroops[self.TroopCounter]) + self:_SendMessage(string.format("Dropped Troops %s into action!",name), 10, false, Group) + --local m = MESSAGE:New(string.format("Dropped Troops %s into action!",name),10,"CTLD"):ToGroup(Group) + self:__TroopsDeployed(1, Group, Unit, self.DroppedTroops[self.TroopCounter]) end -- if type end end -- cargotable loop else -- droppingatbase - local m = MESSAGE:New("Troops have returned to base!",15,"CTLD"):ToGroup(Group) + self:_SendMessage("Troops have returned to base!", 10, false, Group) + --local m = MESSAGE:New("Troops have returned to base!",15,"CTLD"):ToGroup(Group) + self:__TroopsRTB(1, Group, Unit) end -- cleanup load list local loaded = {} -- #CTLD.LoadedCargo @@ -1386,9 +1431,11 @@ function CTLD:_UnloadTroops(Group, Unit) self.Loaded_Cargo[unitname] = loaded else if IsHerc then - local m = MESSAGE:New("Nothing loaded or not within airdrop parameters!",10,"CTLD"):ToGroup(Group) + self:_SendMessage("Nothing loaded or not within airdrop parameters!", 10, false, Group) + --local m = MESSAGE:New("Nothing loaded or not within airdrop parameters!",10,"CTLD"):ToGroup(Group) else - local m = MESSAGE:New("Nothing loaded or not hovering within parameters!",10,"CTLD"):ToGroup(Group) + self:_SendMessage("Nothing loaded or not hovering within parameters!", 10, false, Group) + --local m = MESSAGE:New("Nothing loaded or not hovering within parameters!",10,"CTLD"):ToGroup(Group) end end return self @@ -1403,7 +1450,8 @@ function CTLD:_UnloadCrates(Group, Unit) -- check if we are in DROP zone local inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.DROP) if not inzone then - local m = MESSAGE:New("You are not close enough to a drop zone!",15,"CTLD"):ToGroup(Group) + self:_SendMessage("You are not close enough to a drop zone!", 10, false, Group) + --local m = MESSAGE:New("You are not close enough to a drop zone!",15,"CTLD"):ToGroup(Group) if not self.debug then return self end @@ -1453,9 +1501,11 @@ function CTLD:_UnloadCrates(Group, Unit) self.Loaded_Cargo[unitname] = loaded else if IsHerc then - local m = MESSAGE:New("Nothing loaded or not within airdrop parameters!",10,"CTLD"):ToGroup(Group) + self:_SendMessage("Nothing loaded or not within airdrop parameters!", 10, false, Group) + --local m = MESSAGE:New("Nothing loaded or not within airdrop parameters!",10,"CTLD"):ToGroup(Group) else - local m = MESSAGE:New("Nothing loaded or not hovering within parameters!",10,"CTLD"):ToGroup(Group) + self:_SendMessage("Nothing loaded or not hovering within parameters!", 10, false, Group) + --local m = MESSAGE:New("Nothing loaded or not hovering within parameters!",10,"CTLD"):ToGroup(Group) end end return self @@ -1501,7 +1551,7 @@ function CTLD:_BuildCrates(Group, Unit) end foundbuilds = true end - self:T({buildables = buildables}) + --self:T{buildables = buildables}) end -- end dropped end -- end crate loop -- ok let\'s list what we have @@ -1516,14 +1566,15 @@ function CTLD:_BuildCrates(Group, Unit) if build.CanBuild then txtok = "YES" end - self:T({name,needed,found,txtok}) + --self:T{name,needed,found,txtok}) local text = string.format("Type: %s | Required %d | Found %d | Can Build %s", name, needed, found, txtok) report:Add(text) end -- end list buildables if not foundbuilds then report:Add(" --- None Found ---") end report:Add("------------------------------------------------------------") local text = report:Text() - local m = MESSAGE:New(text,30,"CTLD",true):ToGroup(Group) + self:_SendMessage(text, 30, true, Group) + --local m = MESSAGE:New(text,30,"CTLD",true):ToGroup(Group) -- let\'s get going if canbuild then -- loop again @@ -1536,7 +1587,8 @@ function CTLD:_BuildCrates(Group, Unit) end end else - local m = MESSAGE:New(string.format("No crates within %d meters!",finddist),15,"CTLD",true):ToGroup(Group) + self:_SendMessage(string.format("No crates within %d meters!",finddist), 10, false, Group) + --local m = MESSAGE:New(string.format("No crates within %d meters!",finddist),15,"CTLD",true):ToGroup(Group) end -- number > 0 return self end @@ -1580,17 +1632,17 @@ function CTLD:_MoveGroupToZone(Group) self:T(self.lid .. " _MoveGroupToZone") local groupname = Group:GetName() or "none" local groupcoord = Group:GetCoordinate() - self:T(self.lid .. " _MoveGroupToZone for " .. groupname) + --self:Tself.lid .. " _MoveGroupToZone for " .. groupname) -- Get closest zone of type local outcome, name, zone, distance = self:IsUnitInZone(Group,CTLD.CargoZoneType.MOVE) - self:T(string.format("Closest WP zone %s is %d meters",name,distance)) + --self:Tstring.format("Closest WP zone %s is %d meters",name,distance)) if (distance <= self.movetroopsdistance) and zone then -- yes, we can ;) local groupname = Group:GetName() - self:T(string.format("Moving troops %s to zone %s, distance %d!",groupname,name,distance)) + --self:Tstring.format("Moving troops %s to zone %s, distance %d!",groupname,name,distance)) local zonecoord = zone:GetRandomCoordinate(20,125) -- Core.Point#COORDINATE local coordinate = zonecoord:GetVec2() - self:T({coordinate=coordinate}) + --self:T{coordinate=coordinate}) Group:SetAIOn() Group:OptionAlarmStateAuto() Group:OptionDisperseOnAttack(30) @@ -1609,7 +1661,7 @@ function CTLD:_CleanUpCrates(Crates,Build,Number) self:T(self.lid .. " _CleanUpCrates") -- clean up real world crates local build = Build -- #CTLD.Buildable - self:T({Build = Build}) + --self:T{Build = Build}) local existingcrates = self.Spawned_Cargo -- #table of exising crates local newexcrates = {} -- get right number of crates to destroy @@ -1623,18 +1675,18 @@ function CTLD:_CleanUpCrates(Crates,Build,Number) for _,_crate in pairs(Crates) do local nowcrate = _crate -- #CTLD_CARGO local name = nowcrate:GetName() - self:T(string.format("Looking for Crate for %s", name)) + --self:Tstring.format("Looking for Crate for %s", name)) local thisID = nowcrate:GetID() if name == nametype then -- matching crate type table.insert(destIDs,thisID) found = found + 1 nowcrate:GetPositionable():Destroy() nowcrate.Positionable = nil - self:T(string.format("%s Found %d Need %d", name, found, numberdest)) + --self:Tstring.format("%s Found %d Need %d", name, found, numberdest)) end if found == numberdest then break end -- got enough end - self:T({destIDs}) + --self:T{destIDs}) -- loop and remove from real world representation for _,_crate in pairs(existingcrates) do local excrate = _crate -- #CTLD_CARGO @@ -1656,10 +1708,10 @@ end -- @param #CTLD self -- @return #CTLD self function CTLD:_RefreshF10Menus() - self:I(self.lid .. " _RefreshF10Menus") + self:T(self.lid .. " _RefreshF10Menus") local PlayerSet = self.PilotGroups -- Core.Set#SET_GROUP local PlayerTable = PlayerSet:GetSetObjects() -- #table of #GROUP objects - --self:I({PlayerTable=PlayerTable}) + --self:T({PlayerTable=PlayerTable}) -- rebuild units table local _UnitList = {} for _key, _group in pairs (PlayerTable) do @@ -1774,13 +1826,13 @@ function CTLD:AddZone(Zone) local zone = Zone -- #CTLD.CargoZone if zone.type == CTLD.CargoZoneType.LOAD then table.insert(self.pickupZones,zone) - self:T("Registered LOAD zone " .. zone.name) + --self:T"Registered LOAD zone " .. zone.name) elseif zone.type == CTLD.CargoZoneType.DROP then table.insert(self.dropOffZones,zone) - self:T("Registered DROP zone " .. zone.name) + --self:T"Registered DROP zone " .. zone.name) else table.insert(self.wpZones,zone) - self:T("Registered MOVE zone " .. zone.name) + --self:T"Registered MOVE zone " .. zone.name) end return self end @@ -1955,7 +2007,8 @@ function CTLD:_ListRadioBeacons(Group, Unit) report:Add("--------- N O N E ------------") end report:Add("------------------------------------------------------------") - local m = MESSAGE:New(report:Text(),30,"CTLD",true):ToGroup(Group) + self:_SendMessage(report:Text(), 30, true, Group) + --local m = MESSAGE:New(report:Text(),30,"CTLD",true):ToGroup(Group) return self end @@ -2018,7 +2071,7 @@ end function CTLD:IsUnitInZone(Unit,Zonetype) self:T(self.lid .. " IsUnitInZone") local unitname = Unit:GetName() - self:T(string.format("%s | Zone search for %s | Type %s",self.lid,unitname,Zonetype)) + --self:Tstring.format("%s | Zone search for %s | Type %s",self.lid,unitname,Zonetype)) local zonetable = {} local outcome = false if Zonetype == CTLD.CargoZoneType.LOAD then @@ -2044,7 +2097,7 @@ function CTLD:IsUnitInZone(Unit,Zonetype) local color = czone.color local zoneradius = zone:GetRadius() local distance = self:_GetDistance(zonecoord,unitcoord) - self:T(string.format("Check distance: %d",distance)) + --self:Tstring.format("Check distance: %d",distance)) if distance <= zoneradius and active then outcome = true end @@ -2055,7 +2108,7 @@ function CTLD:IsUnitInZone(Unit,Zonetype) colorret = color end end - self:T({outcome, zonenameret, zoneret, maxdist}) + --self:T{outcome, zonenameret, zoneret, maxdist}) return outcome, zonenameret, zoneret, maxdist end @@ -2090,14 +2143,16 @@ function CTLD:SmokeZoneNearBy(Unit, Flare) end local txt = "smoking" if Flare then txt = "flaring" end - local m = MESSAGE:New(string.format("Roger, %s zone %s!",txt, zonename),10,"CTLD"):ToGroup(Group) + self:_SendMessage(string.format("Roger, %s zone %s!",txt, zonename), 10, false, Group) + --local m = MESSAGE:New(string.format("Roger, %s zone %s!",txt, zonename),10,"CTLD"):ToGroup(Group) smoked = true end end end if not smoked then - local distance = UTILS.MetersToNM(self.smkedistance) - local m = MESSAGE:New(string.format("Negative, need to be closer than %dnm to a zone!",distance),10,"CTLD"):ToGroup(Group) + local distance = UTILS.MetersToNM(self.smokedistance) + self:_SendMessage(string.format("Negative, need to be closer than %dnm to a zone!",distance), 10, false, Group) + --local m = MESSAGE:New(string.format("Negative, need to be closer than %dnm to a zone!",distance),10,"CTLD"):ToGroup(Group) end return self end @@ -2150,7 +2205,7 @@ end local maxh = self.maximumHoverHeight -- 15 local minh = self.minimumHoverHeight -- 5 local mspeed = 2 -- 2 m/s - self:T(string.format("%s Unit parameters: at %dm AGL with %dmps",self.lid,aheight,uspeed)) + --self:Tstring.format("%s Unit parameters: at %dm AGL with %dmps",self.lid,aheight,uspeed)) if (uspeed <= mspeed) and (aheight <= maxh) and (aheight >= minh) then -- yep within parameters outcome = true @@ -2178,7 +2233,7 @@ end local minh = self.HercMaxAngels -- 5000m local mspeed = 2 -- 2 m/s -- TODO:Add speed test for Herc, should not be above 280kph/150kn - self:T(string.format("%s Unit parameters: at %dm AGL with %dmps",self.lid,aheight,uspeed)) + --self:Tstring.format("%s Unit parameters: at %dm AGL with %dmps",self.lid,aheight,uspeed)) if (aheight <= maxh) and (aheight >= minh) then -- yep within parameters outcome = true @@ -2196,7 +2251,8 @@ end local htxt = "true" if not inhover then htxt = "false" end local text = string.format("Hover parameters (autoload/drop):\n - Min height %dm \n - Max height %dm \n - Max speed 2mps \n - In parameter: %s", self.minimumHoverHeight, self.maximumHoverHeight, htxt) - local m = MESSAGE:New(text,10,"CTLD",false):ToGroup(Group) + self:_SendMessage(text, 10, false, Group) + --local m = MESSAGE:New(text,10,"CTLD",false):ToGroup(Group) return self end @@ -2211,7 +2267,8 @@ end local minheight = UTILS.MetersToFeet(self.HercMinAngels) local maxheight = UTILS.MetersToFeet(self.HercMaxAngels) local text = string.format("Flight parameters (airdrop):\n - Min height %dft \n - Max height %dft \n - In parameter: %s", minheight, maxheight, htxt) - local m = MESSAGE:New(text,10,"CTLD",false):ToGroup(Group) + self:_SendMessage(text, 10, false, Group) + --local m = MESSAGE:New(text,15,"CTLD",false):ToGroup(Group) return self end @@ -2302,19 +2359,19 @@ end -- @param #string To State. -- @return #CTLD self function CTLD:onafterStart(From, Event, To) - self:I({From, Event, To}) + self:T({From, Event, To}) if self.useprefix then local prefix = self.prefixes - self:I({prefix=prefix}) + --self:T{prefix=prefix}) if self.enableHercules then - --self:I("CTLD with prefixes and Hercules") + --self:T("CTLD with prefixes and Hercules") self.PilotGroups = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(prefix):FilterStart() else - --self:I("CTLD with prefixes NO Hercules") + --self:T("CTLD with prefixes NO Hercules") self.PilotGroups = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(prefix):FilterCategoryHelicopter():FilterStart() end else - --self:I("CTLD NO prefixes NO Hercules") + --self:T("CTLD NO prefixes NO Hercules") self.PilotGroups = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterCategoryHelicopter():FilterStart() end -- Events @@ -2346,7 +2403,7 @@ end -- @param #string To State. -- @return #CTLD self function CTLD:onafterStatus(From, Event, To) - self:I({From, Event, To}) + self:T({From, Event, To}) -- gather some stats -- pilots local pilots = 0 @@ -2396,7 +2453,7 @@ end -- @param #CTLD_CARGO Cargo Cargo crate. -- @return #CTLD self function CTLD:onbeforeTroopsPickedUp(From, Event, To, Group, Unit, Cargo) - self:I({From, Event, To}) + self:T({From, Event, To}) return self end @@ -2410,7 +2467,7 @@ end -- @param #CTLD_CARGO Cargo Cargo crate. -- @return #CTLD self function CTLD:onbeforeCratesPickedUp(From, Event, To, Group, Unit, Cargo) - self:I({From, Event, To}) + self:T({From, Event, To}) return self end @@ -2424,7 +2481,7 @@ end -- @param Wrapper.Group#GROUP Troops Troops #GROUP Object. -- @return #CTLD self function CTLD:onbeforeTroopsDeployed(From, Event, To, Group, Unit, Troops) - self:I({From, Event, To}) + self:T({From, Event, To}) return self end @@ -2438,7 +2495,7 @@ end -- @param #table Cargotable Table of #CTLD_CARGO objects dropped. -- @return #CTLD self function CTLD:onbeforeCratesDropped(From, Event, To, Group, Unit, Cargotable) - self:I({From, Event, To}) + self:T({From, Event, To}) return self end @@ -2452,7 +2509,20 @@ end -- @param Wrapper.Group#GROUP Vehicle The #GROUP object of the vehicle or FOB build. -- @return #CTLD self function CTLD:onbeforeCratesBuild(From, Event, To, Group, Unit, Vehicle) - self:I({From, Event, To}) + self:T({From, Event, To}) + return self + end + + --- (Internal) FSM Function onbeforeTroopsRTB. + -- @param #CTLD self + -- @param #string From State. + -- @param #string Event Trigger. + -- @param #string To State. + -- @param Wrapper.Group#GROUP Group Group Object. + -- @param Wrapper.Unit#UNIT Unit Unit Object. + -- @return #CTLD self + function CTLD:onbeforeTroopsRTB(From, Event, To, Group, Unit) + self:T({From, Event, To}) return self end From 76a53ab1543c70c89026bf73a4f0571ed65b01bb Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 1 Jul 2021 09:07:01 +0200 Subject: [PATCH 15/16] Added extra checks for Beacon refresh --- Moose Development/Moose/Ops/CSAR.lua | 86 +++++++++++++++++----------- 1 file changed, 54 insertions(+), 32 deletions(-) diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index 4a9344a54..a42384db2 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -240,7 +240,7 @@ CSAR.AircraftType["Mi-24V"] = 8 --- CSAR class version. -- @field #string version -CSAR.version="0.1.5r2" +CSAR.version="0.1.5r3" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -489,6 +489,7 @@ function CSAR:_CreateDownedPilotTrack(Group,Groupname,Side,OriginalUnit,Descript self.downedPilots = PilotTable -- Increase counter self.downedpilotcounter = self.downedpilotcounter+1 + return self end --- (Internal) Count pilots on board. @@ -579,7 +580,7 @@ function CSAR:_AddSpecialOptions(group) group:OptionAlarmStateGreen() group:OptionROEHoldFire() - + return self end --- (Internal) Function to spawn a CSAR object into the scene. @@ -634,6 +635,7 @@ function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _pla self:_InitSARForPilot(_spawnedGroup, _GroupName, _freq, noMessage) + return self end --- (Internal) Function to add a CSAR object into the scene at a zone coordinate. For mission designers wanting to add e.g. PoWs to the scene. @@ -672,6 +674,8 @@ function CSAR:_SpawnCsarAtZone( _zone, _coalition, _description, _randomPoint, _ end self:_AddCsar(_coalition, _country, pos, "PoW", "Unknown", nil, freq, _nomessage, _description) + + return self end --- Function to add a CSAR object into the scene at a zone coordinate. For mission designers wanting to add e.g. PoWs to the scene. @@ -852,7 +856,7 @@ function CSAR:_EventHandler(EventData) return true end - + return self end --- (Internal) Initialize the action for a pilot. @@ -881,6 +885,8 @@ function CSAR:_InitSARForPilot(_downedGroup, _GroupName, _freq, _nomessage) -- trigger FSM event self:__PilotDown(2,_downedGroup, _freqk, _leadername, _coordinatesText) + + return self end --- (Internal) Check if a name is in downed pilot table @@ -984,6 +990,7 @@ function CSAR:_CheckWoundedGroupStatus(heliname,woundedgroupname) self:T("...Downed Pilot KIA?!") self:_RemoveNameFromDownedPilots(_downedpilot.name) end + return self end --- (Internal) Function to pop a smoke at a wounded pilot\'s positions. @@ -1001,6 +1008,7 @@ function CSAR:_PopSmokeForGroup(_woundedGroupName, _woundedLeader) _smokecoord:Smoke(_smokecolor) self.smokeMarkers[_woundedGroupName] = timer.getTime() + 300 -- next smoke time end + return self end --- (Internal) Function to pickup the wounded pilot from the ground. @@ -1065,6 +1073,7 @@ function CSAR:_OrderGroupToMoveToPoint(_leader, _destination) group:SetAIOn() group:RouteToVec2(coordinate,5) + return self end --- (Internal) Function to check if heli is close to group. @@ -1235,33 +1244,34 @@ end function CSAR:_ScheduledSARFlight(heliname,groupname) self:T(self.lid .. " _ScheduledSARFlight") self:T({heliname,groupname}) - local _heliUnit = self:_GetSARHeli(heliname) - local _woundedGroupName = groupname + local _heliUnit = self:_GetSARHeli(heliname) + local _woundedGroupName = groupname - if (_heliUnit == nil) then - --helicopter crashed? - self.inTransitGroups[heliname] = nil - return - end + if (_heliUnit == nil) then + --helicopter crashed? + self.inTransitGroups[heliname] = nil + return + end - if self.inTransitGroups[heliname] == nil or self.inTransitGroups[heliname][_woundedGroupName] == nil then - -- Groups already rescued - return - end + if self.inTransitGroups[heliname] == nil or self.inTransitGroups[heliname][_woundedGroupName] == nil then + -- Groups already rescued + return + end - local _dist = self:_GetClosestMASH(_heliUnit) + local _dist = self:_GetClosestMASH(_heliUnit) - if _dist == -1 then - return - end + if _dist == -1 then + return + end - if _dist < 200 and _heliUnit:InAir() == false then - self:_RescuePilots(_heliUnit) - return - end + if _dist < 200 and _heliUnit:InAir() == false then + self:_RescuePilots(_heliUnit) + return + end - --queue up - self:__Returning(-5,heliname,_woundedGroupName) + --queue up + self:__Returning(-5,heliname,_woundedGroupName) + return self end --- (Internal) Mark pilot as rescued and remove from tables. @@ -1287,6 +1297,7 @@ function CSAR:_RescuePilots(_heliUnit) self:_DisplayMessageToSAR(_heliUnit, _txt, self.messageTime) -- trigger event self:__Rescued(-1,_heliUnit,_heliName, PilotsSaved) + return self end --- (Internal) Check and return Wrappe.Unit#UNIT based on the name if alive. @@ -1325,6 +1336,7 @@ function CSAR:_DisplayMessageToSAR(_unit, _text, _time, _clear, _speak) local msrs = MSRS:New(path,channel,modulation) msrs:PlaySoundText(srstext, 2) end + return self end --- (Internal) Function to get string of a group\'s position. @@ -1403,6 +1415,7 @@ function CSAR:_DisplayActiveSAR(_unitName) end self:_DisplayMessageToSAR(_heli, _msg, self.messageTime*2) + return self end --- (Internal) Find the closest downed pilot to a heli. @@ -1472,6 +1485,7 @@ function CSAR:_SignalFlare(_unitName) end self:_DisplayMessageToSAR(_heli, string.format("No Pilots within %s",disttext), self.messageTime) end + return self end --- (Internal) Display info to all SAR groups. @@ -1489,6 +1503,7 @@ function CSAR:_DisplayToAllSAR(_message, _side, _messagetime) end end end + return self end ---(Internal) Request smoke at closest downed pilot. @@ -1521,6 +1536,7 @@ function CSAR:_Reqsmoke( _unitName ) end self:_DisplayMessageToSAR(_heli, string.format("No Pilots within %s",disttext), self.messageTime) end + return self end --- (Internal) Determine distance to closest MASH. @@ -1598,6 +1614,7 @@ function CSAR:_CheckOnboard(_unitName) end self:_DisplayMessageToSAR(_unit, _text, self.messageTime*2) end + return self end --- (Internal) Populate F10 menu for CSAR players. @@ -1640,7 +1657,7 @@ function CSAR:_AddMedevacMenuItem() end end end - return + return self end --- (Internal) Return distance in meters between two coordinates. @@ -1727,6 +1744,7 @@ function CSAR:_GenerateVHFrequencies() _start = _start + 50000 end self.FreeVHFFrequencies = FreeVHFFrequencies + return self end --- (Internal) Pop frequency from prepopulated table. @@ -1791,6 +1809,7 @@ function CSAR:_AddBeaconToGroup(_group, _freq) local Sound = "l10n/DEFAULT/"..self.radioSound trigger.action.radioTransmission(Sound, _radioUnit:GetPositionVec3(), 0, false, Frequency, 1000) -- Beacon in MP only runs for exactly 30secs straight end + return self end --- (Internal) Helper function to (re-)add beacon to downed pilot. @@ -1798,15 +1817,18 @@ end -- @param #table _args Arguments function CSAR:_RefreshRadioBeacons() self:T(self.lid .. " _RefreshRadioBeacons") - local PilotTable = self.downedPilots - for _,_pilot in pairs (PilotTable) do - local pilot = _pilot -- #CSAR.DownedPilot - local group = pilot.group - local frequency = pilot.frequency or 0 -- thanks to @Thrud - if frequency and frequency > 0 then - self:_AddBeaconToGroup(group,frequency) + if self:_CountActiveDownedPilots() > 0 then + local PilotTable = self.downedPilots + for _,_pilot in pairs (PilotTable) do + local pilot = _pilot -- #CSAR.DownedPilot + local group = pilot.group + local frequency = pilot.frequency or 0.0 -- thanks to @Thrud + if group:IsAlive() and frequency > 0.0 then + self:_AddBeaconToGroup(group,frequency) + end end end + return self end --- (Internal) Helper function to count active downed pilots. From c80cebb82485a1f7f8ee79f8e2faaaad9f50b45f Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 1 Jul 2021 23:54:24 +0200 Subject: [PATCH 16/16] WAREHOUSE, AIRWING & SQUADRON - Added function for hot start to SQUADRON - Addressed bug #1560 --- Moose Development/Moose/Core/Point.lua | 8 +- .../Moose/Functional/Warehouse.lua | 45 +++-- Moose Development/Moose/Ops/AirWing.lua | 170 ++++++++---------- Moose Development/Moose/Ops/Squadron.lua | 39 +++- 4 files changed, 148 insertions(+), 114 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 061d61ed5..354b584c1 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -1607,8 +1607,12 @@ do -- COORDINATE roadtype="railroads" end local x,y = land.getClosestPointOnRoads(roadtype, self.x, self.z) - local vec2={ x = x, y = y } - return COORDINATE:NewFromVec2(vec2) + local coord=nil + if x and y then + local vec2={ x = x, y = y } + coord=COORDINATE:NewFromVec2(vec2) + end + return coord end diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 0d4f074c4..1d4e76d2e 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -5790,21 +5790,32 @@ end -- @param #WAREHOUSE.Queueitem request Request belonging to this asset. Needed for the name/alias. -- @param #table parking Parking data for this asset. -- @param #boolean uncontrolled Spawn aircraft in uncontrolled state. --- @param #boolean hotstart Spawn aircraft with engines already on. Default is a cold start with engines off. -- @return Wrapper.Group#GROUP The spawned group or nil if the group could not be spawned. -function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrolled, hotstart) +function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrolled) if asset and asset.category==Group.Category.AIRPLANE or asset.category==Group.Category.HELICOPTER then -- Prepare the spawn template. local template=self:_SpawnAssetPrepareTemplate(asset, alias) + -- Cold start (default). + local _type=COORDINATE.WaypointType.TakeOffParking + local _action=COORDINATE.WaypointAction.FromParkingArea + + -- Hot start. + if asset.takeoffType and asset.takeoffType==COORDINATE.WaypointType.TakeOffParkingHot then + _type=COORDINATE.WaypointType.TakeOffParkingHot + _action=COORDINATE.WaypointAction.FromParkingAreaHot + uncontrolled=false + end + + -- Set route points. if request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then -- Get flight path if the group goes to another warehouse by itself. if request.toself then - local wp=self.airbase:GetCoordinate():WaypointAir("RADIO", COORDINATE.WaypointType.TakeOffParking, COORDINATE.WaypointAction.FromParkingArea, 0, false, self.airbase, {}, "Parking") + local wp=self.airbase:GetCoordinate():WaypointAir("RADIO", _type, _action, 0, false, self.airbase, {}, "Parking") template.route.points={wp} else template.route.points=self:_GetFlightplan(asset, self.airbase, request.warehouse.airbase) @@ -5812,18 +5823,8 @@ function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrol else - -- Cold start (default). - local _type=COORDINATE.WaypointType.TakeOffParking - local _action=COORDINATE.WaypointAction.FromParkingArea - - -- Hot start. - if hotstart then - _type=COORDINATE.WaypointType.TakeOffParkingHot - _action=COORDINATE.WaypointAction.FromParkingAreaHot - end - -- First route point is the warehouse airbase. - template.route.points[1]=self.airbase:GetCoordinate():WaypointAir("BARO",_type,_action, 0, true, self.airbase, nil, "Spawnpoint") + template.route.points[1]=self.airbase:GetCoordinate():WaypointAir("BARO", _type, _action, 0, true, self.airbase, nil, "Spawnpoint") end @@ -9040,9 +9041,23 @@ function WAREHOUSE:_GetFlightplan(asset, departure, destination) local wp={} local c={} + -- Cold start (default). + local _type=COORDINATE.WaypointType.TakeOffParking + local _action=COORDINATE.WaypointAction.FromParkingArea + + -- Hot start. + if asset.takeoffType and asset.takeoffType==COORDINATE.WaypointType.TakeOffParkingHot then + env.info("FF hot") + _type=COORDINATE.WaypointType.TakeOffParkingHot + _action=COORDINATE.WaypointAction.FromParkingAreaHot + else + env.info("FF cold") + end + + --- Departure/Take-off c[#c+1]=Pdeparture - wp[#wp+1]=Pdeparture:WaypointAir("RADIO", COORDINATE.WaypointType.TakeOffParking, COORDINATE.WaypointAction.FromParkingArea, VxClimb*3.6, true, departure, nil, "Departure") + wp[#wp+1]=Pdeparture:WaypointAir("RADIO", _type, _action, VxClimb*3.6, true, departure, nil, "Departure") --- Begin of Cruise local Pcruise=Pdeparture:Translate(d_climb, heading) diff --git a/Moose Development/Moose/Ops/AirWing.lua b/Moose Development/Moose/Ops/AirWing.lua index 202eb631d..4493264c9 100644 --- a/Moose Development/Moose/Ops/AirWing.lua +++ b/Moose Development/Moose/Ops/AirWing.lua @@ -32,6 +32,7 @@ -- @field #table pointsCAP Table of CAP points. -- @field #table pointsTANKER Table of Tanker points. -- @field #table pointsAWACS Table of AWACS points. +-- @field #boolean markpoints Display markers on the F10 map. -- @field Ops.WingCommander#WINGCOMMANDER wingcommander The wing commander responsible for this airwing. -- -- @field Ops.RescueHelo#RESCUEHELO rescuehelo The rescue helo. @@ -153,7 +154,7 @@ AIRWING = { --- AIRWING class version. -- @field #string version -AIRWING.version="0.5.1" +AIRWING.version="0.5.2" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -210,7 +211,7 @@ function AIRWING:New(warehousename, airwingname) self.nflightsTANKERprobe=0 self.nflightsRecoveryTanker=0 self.nflightsRescueHelo=0 - self.markpoints = false + self.markpoints=false ------------------------ --- Pseudo Functions --- @@ -1551,6 +1552,9 @@ function AIRWING:onafterNewAsset(From, Event, To, asset, assignment) asset.nunits=squad.ngrouping end + + -- Set takeoff type. + asset.takeoffType=squad.takeoffType -- Create callsign and modex (needs to be after grouping). squad:GetCallsign(asset) @@ -1616,84 +1620,86 @@ function AIRWING:onafterAssetSpawned(From, Event, To, group, asset, request) -- Call parent warehouse function first. self:GetParent(self).onafterAssetSpawned(self, From, Event, To, group, asset, request) - - -- Create a flight group. - local flightgroup=self:_CreateFlightGroup(asset) - - - --- - -- Asset - --- - - -- Set asset flightgroup. - asset.flightgroup=flightgroup - - -- Not requested any more. - asset.requested=nil - - -- Did not return yet. - asset.Treturned=nil - - --- - -- Squadron - --- -- Get the SQUADRON of the asset. local squadron=self:GetSquadronOfAsset(asset) - -- Get TACAN channel. - local Tacan=squadron:FetchTacan() - if Tacan then - asset.tacan=Tacan - end - - -- Set radio frequency and modulation - local radioFreq, radioModu=squadron:GetRadio() - if radioFreq then - flightgroup:SwitchRadio(radioFreq, radioModu) - end + -- Check if we have a squadron or if this was some other request. + if squadron then + + -- Create a flight group. + local flightgroup=self:_CreateFlightGroup(asset) + + --- + -- Asset + --- - if squadron.fuellow then - flightgroup:SetFuelLowThreshold(squadron.fuellow) - end - - if squadron.fuellowRefuel then - flightgroup:SetFuelLowRefuel(squadron.fuellowRefuel) - end - - --- - -- Mission - --- - - -- Get Mission (if any). - local mission=self:GetMissionByID(request.assignment) - - -- Add mission to flightgroup queue. - if mission then + -- Set asset flightgroup. + asset.flightgroup=flightgroup + + -- Not requested any more. + asset.requested=nil + + -- Did not return yet. + asset.Treturned=nil + --- + -- Squadron + --- + + -- Get TACAN channel. + local Tacan=squadron:FetchTacan() if Tacan then - mission:SetTACAN(Tacan, Morse, UnitName, Band) + asset.tacan=Tacan + end + + -- Set radio frequency and modulation + local radioFreq, radioModu=squadron:GetRadio() + if radioFreq then + flightgroup:SwitchRadio(radioFreq, radioModu) end - -- Add mission to flightgroup queue. - asset.flightgroup:AddMission(mission) - - -- Trigger event. - self:FlightOnMission(flightgroup, mission) - - else - - if Tacan then - flightgroup:SwitchTACAN(Tacan, Morse, UnitName, Band) + if squadron.fuellow then + flightgroup:SetFuelLowThreshold(squadron.fuellow) end - - end - + if squadron.fuellowRefuel then + flightgroup:SetFuelLowRefuel(squadron.fuellowRefuel) + end - -- Add group to the detection set of the WINGCOMMANDER. - if self.wingcommander and self.wingcommander.chief then - self.wingcommander.chief.detectionset:AddGroup(asset.flightgroup.group) + --- + -- Mission + --- + + -- Get Mission (if any). + local mission=self:GetMissionByID(request.assignment) + + -- Add mission to flightgroup queue. + if mission then + + if Tacan then + mission:SetTACAN(Tacan, Morse, UnitName, Band) + end + + -- Add mission to flightgroup queue. + asset.flightgroup:AddMission(mission) + + -- Trigger event. + self:FlightOnMission(flightgroup, mission) + + else + + if Tacan then + flightgroup:SwitchTACAN(Tacan, Morse, UnitName, Band) + end + + end + + -- Add group to the detection set of the WINGCOMMANDER. + if self.wingcommander and self.wingcommander.chief then + self.wingcommander.chief.detectionset:AddGroup(asset.flightgroup.group) + end + end end @@ -1822,34 +1828,6 @@ function AIRWING:_CreateFlightGroup(asset) -- Set home base. flightgroup.homebase=self.airbase - --[[ - - --- Check if out of missiles. For A2A missions ==> RTB. - function flightgroup:OnAfterOutOfMissiles() - local airwing=flightgroup:GetAirWing() - - end - - --- Check if out of missiles. For A2G missions ==> RTB. But need to check A2G missiles, rockets as well. - function flightgroup:OnAfterOutOfBombs() - local airwing=flightgroup:GetAirWing() - - end - - --- Mission started. - function flightgroup:OnAfterMissionStart(From, Event, To, Mission) - local airwing=flightgroup:GetAirWing() - - end - - --- Flight is DEAD. - function flightgroup:OnAfterFlightDead(From, Event, To) - local airwing=flightgroup:GetAirWing() - - end - - ]] - return flightgroup end diff --git a/Moose Development/Moose/Ops/Squadron.lua b/Moose Development/Moose/Ops/Squadron.lua index 5ed71fba7..c0f53d960 100644 --- a/Moose Development/Moose/Ops/Squadron.lua +++ b/Moose Development/Moose/Ops/Squadron.lua @@ -45,6 +45,7 @@ -- @field #table tacanChannel List of TACAN channels available to the squadron. -- @field #number radioFreq Radio frequency in MHz the squad uses. -- @field #number radioModu Radio modulation the squad uses. +-- @field #number takeoffType Take of type. -- @extends Core.Fsm#FSM --- *It is unbelievable what a squadron of twelve aircraft did to tip the balance.* -- Adolf Galland @@ -87,12 +88,13 @@ SQUADRON = { --- SQUADRON class version. -- @field #string version -SQUADRON.version="0.5.0" +SQUADRON.version="0.5.2" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- TODO: Parking spots for squadrons? -- DONE: Engage radius. -- DONE: Modex. -- DONE: Call signs. @@ -282,6 +284,41 @@ function SQUADRON:SetGrouping(nunits) return self end + +--- Set takeoff type. All assets of this squadron will be spawned with cold (default) or hot engines. +-- Spawning on runways is not supported. +-- @param #SQUADRON self +-- @param #string TakeoffType Take off type: "Cold" (default) or "Hot" with engines on. +-- @return #SQUADRON self +function SQUADRON:SetTakeoffType(TakeoffType) + TakeoffType=TakeoffType or "Cold" + if TakeoffType:lower()=="hot" then + self.takeoffType=COORDINATE.WaypointType.TakeOffParkingHot + elseif TakeoffType:lower()=="cold" then + self.takeoffType=COORDINATE.WaypointType.TakeOffParking + else + self.takeoffType=COORDINATE.WaypointType.TakeOffParking + end + return self +end + +--- Set takeoff type cold (default). +-- @param #SQUADRON self +-- @return #SQUADRON self +function SQUADRON:SetTakeoffCold() + self:SetTakeoffType("Cold") + return self +end + +--- Set takeoff type hot. +-- @param #SQUADRON self +-- @return #SQUADRON self +function SQUADRON:SetTakeoffHot() + self:SetTakeoffType("Hot") + return self +end + + --- Set mission types this squadron is able to perform. -- @param #SQUADRON self -- @param #table MissionTypes Table of mission types. Can also be passed as a #string if only one type.