diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 6822d3f55..04fb227d2 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -22,37 +22,37 @@ -- @module Ops.CTLD -- @image OPS_CTLD.jpg --- Date: Dec 2021 +-- Date: Jan 2022 do - ------------------------------------------------------ - --- **CTLD_ENGINEERING** class, extends Core.Base#BASE - -- @type CTLD_ENGINEERING - -- @field #string ClassName - -- @field #string lid - -- @field #string Name - -- @field Wrapper.Group#GROUP Group - -- @field Wrapper.Unit#UNIT Unit - -- @field Wrapper.Group#GROUP HeliGroup - -- @field Wrapper.Unit#UNIT HeliUnit - -- @field #string State - -- @extends Core.Base#BASE - CTLD_ENGINEERING = { - ClassName = "CTLD_ENGINEERING", - lid = "", - Name = "none", - Group = nil, - Unit = nil, - -- C_Ops = nil, - HeliGroup = nil, - HeliUnit = nil, - State = "", +------------------------------------------------------ +--- **CTLD_ENGINEERING** class, extends Core.Base#BASE +-- @type CTLD_ENGINEERING +-- @field #string ClassName +-- @field #string lid +-- @field #string Name +-- @field Wrapper.Group#GROUP Group +-- @field Wrapper.Unit#UNIT Unit +-- @field Wrapper.Group#GROUP HeliGroup +-- @field Wrapper.Unit#UNIT HeliUnit +-- @field #string State +-- @extends Core.Base#BASE +CTLD_ENGINEERING = { + ClassName = "CTLD_ENGINEERING", + lid = "", + Name = "none", + Group = nil, + Unit = nil, + --C_Ops = nil, + HeliGroup = nil, + HeliUnit = nil, + State = "", } - + --- CTLD_ENGINEERING class version. -- @field #string version CTLD_ENGINEERING.Version = "0.0.3" - + --- Create a new instance. -- @param #CTLD_ENGINEERING self -- @param #string Name @@ -60,26 +60,26 @@ do -- @param Wrapper.Group#GROUP HeliGroup HeliGroup -- @param Wrapper.Unit#UNIT HeliUnit HeliUnit -- @return #CTLD_ENGINEERING self - function CTLD_ENGINEERING:New( Name, GroupName, HeliGroup, HeliUnit ) - - -- Inherit everything from BASE class. - local self = BASE:Inherit( self, BASE:New() ) -- #CTLD_ENGINEERING - - -- BASE:I({Name, GroupName}) - + function CTLD_ENGINEERING:New(Name, GroupName, HeliGroup, HeliUnit) + + -- Inherit everything from BASE class. + local self=BASE:Inherit(self, BASE:New()) -- #CTLD_ENGINEERING + + --BASE:I({Name, GroupName}) + self.Name = Name or "Engineer Squad" -- #string - self.Group = GROUP:FindByName( GroupName ) -- Wrapper.Group#GROUP - self.Unit = self.Group:GetUnit( 1 ) -- Wrapper.Unit#UNIT - -- self.C_Ops = C_Ops -- Ops.CTLD#CTLD + self.Group = GROUP:FindByName(GroupName) -- Wrapper.Group#GROUP + self.Unit = self.Group:GetUnit(1) -- Wrapper.Unit#UNIT + --self.C_Ops = C_Ops -- Ops.CTLD#CTLD self.HeliGroup = HeliGroup -- Wrapper.Group#GROUP self.HeliUnit = HeliUnit -- Wrapper.Unit#UNIT - -- self.distance = Distance or UTILS.NMToMeters(1) + --self.distance = Distance or UTILS.NMToMeters(1) self.currwpt = nil -- Core.Point#COORDINATE - self.lid = string.format( "%s (%s) | ", self.Name, self.Version ) - -- Start State. + self.lid = string.format("%s (%s) | ",self.Name, self.Version) + -- Start State. self.State = "Stopped" self.marktimer = 300 -- wait this many secs before trying a crate again - + --[[ Add FSM transitions. -- From State --> Event --> To State self:AddTransition("Stopped", "Start", "Running") -- Start FSM. @@ -89,145 +89,145 @@ do self:AddTransition("*", "Arrive", "Arrived") self:AddTransition("*", "Build", "Building") self:AddTransition("*", "Done", "Running") - self:AddTransition("*", "Stop", "Stopped") -- Stop FSM. - + self:AddTransition("*", "Stop", "Stopped") -- Stop FSM. + self:__Start(5) --]] self:Start() - local parent = self:GetParent( self ) + local parent = self:GetParent(self) return self end - + --- (Internal) Set the status -- @param #CTLD_ENGINEERING self -- @param #string State -- @return #CTLD_ENGINEERING self - function CTLD_ENGINEERING:SetStatus( State ) + function CTLD_ENGINEERING:SetStatus(State) self.State = State return self end - + --- (Internal) Get the status -- @param #CTLD_ENGINEERING self -- @return #string State function CTLD_ENGINEERING:GetStatus() return self.State end - + --- (Internal) Check the status -- @param #CTLD_ENGINEERING self -- @param #string State -- @return #boolean Outcome - function CTLD_ENGINEERING:IsStatus( State ) + function CTLD_ENGINEERING:IsStatus(State) return self.State == State end - + --- (Internal) Check the negative status -- @param #CTLD_ENGINEERING self -- @param #string State -- @return #boolean Outcome - function CTLD_ENGINEERING:IsNotStatus( State ) + function CTLD_ENGINEERING:IsNotStatus(State) return self.State ~= State end - + --- (Internal) Set start status. -- @param #CTLD_ENGINEERING self -- @return #CTLD_ENGINEERING self function CTLD_ENGINEERING:Start() - self:T( self.lid .. "Start" ) - self:SetStatus( "Running" ) + self:T(self.lid.."Start") + self:SetStatus("Running") return self end - + --- (Internal) Set stop status. -- @param #CTLD_ENGINEERING self -- @return #CTLD_ENGINEERING self function CTLD_ENGINEERING:Stop() - self:T( self.lid .. "Stop" ) - self:SetStatus( "Stopped" ) + self:T(self.lid.."Stop") + self:SetStatus("Stopped") return self end - + --- (Internal) Set build status. -- @param #CTLD_ENGINEERING self -- @return #CTLD_ENGINEERING self function CTLD_ENGINEERING:Build() - self:T( self.lid .. "Build" ) - self:SetStatus( "Building" ) + self:T(self.lid.."Build") + self:SetStatus("Building") return self end - + --- (Internal) Set done status. -- @param #CTLD_ENGINEERING self -- @return #CTLD_ENGINEERING self function CTLD_ENGINEERING:Done() - self:T( self.lid .. "Done" ) + self:T(self.lid.."Done") local grp = self.Group -- Wrapper.Group#GROUP - grp:RelocateGroundRandomInRadius( 7, 100, false, false, "Diamond" ) - self:SetStatus( "Running" ) + grp:RelocateGroundRandomInRadius(7,100,false,false,"Diamond") + self:SetStatus("Running") return self end - + --- (Internal) Search for crates in reach. -- @param #CTLD_ENGINEERING self -- @param #table crates Table of found crate Ops.CTLD#CTLD_CARGO objects. -- @param #number number Number of crates found. -- @return #CTLD_ENGINEERING self - function CTLD_ENGINEERING:Search( crates, number ) - self:T( self.lid .. "Search" ) - self:SetStatus( "Searching" ) + function CTLD_ENGINEERING:Search(crates,number) + self:T(self.lid.."Search") + self:SetStatus("Searching") -- find crates close by - -- local COps = self.C_Ops -- Ops.CTLD#CTLD + --local COps = self.C_Ops -- Ops.CTLD#CTLD local dist = self.distance -- #number local group = self.Group -- Wrapper.Group#GROUP - -- local crates,number = COps:_FindCratesNearby(group,nil, dist) -- #table + --local crates,number = COps:_FindCratesNearby(group,nil, dist) -- #table local ctable = {} local ind = 0 if number > 0 then -- get set of dropped only - for _, _cargo in pairs( crates ) do - local cgotype = _cargo:GetType() - if _cargo:WasDropped() and cgotype ~= CTLD_CARGO.Enum.STATIC then - local ok = false - local chalk = _cargo:GetMark() - if chalk == nil then + for _,_cargo in pairs (crates) do + local cgotype = _cargo:GetType() + if _cargo:WasDropped() and cgotype ~= CTLD_CARGO.Enum.STATIC then + local ok = false + local chalk = _cargo:GetMark() + if chalk == nil then + ok = true + else + -- have we tried this cargo recently? + local tag = chalk.tag or "none" + local timestamp = chalk.timestamp or 0 + --self:I({chalk}) + -- enough time gone? + local gone = timer.getAbsTime() - timestamp + --self:I({time=gone}) + if gone >= self.marktimer then ok = true - else - -- have we tried this cargo recently? - local tag = chalk.tag or "none" - local timestamp = chalk.timestamp or 0 - -- self:I({chalk}) - -- enough time gone? - local gone = timer.getAbsTime() - timestamp - -- self:I({time=gone}) - if gone >= self.marktimer then - ok = true - _cargo:WipeMark() - end -- end time check - end -- end chalk - if ok then - local chalk = {} - chalk.tag = "Engineers" - chalk.timestamp = timer.getAbsTime() - _cargo:AddMark( chalk ) - ind = ind + 1 - table.insert( ctable, ind, _cargo ) - end - end -- end dropped + _cargo:WipeMark() + end -- end time check + end -- end chalk + if ok then + local chalk = {} + chalk.tag = "Engineers" + chalk.timestamp = timer.getAbsTime() + _cargo:AddMark(chalk) + ind = ind + 1 + table.insert(ctable,ind,_cargo) + end + end -- end dropped end -- end for end -- end number - + if ind > 0 then local crate = ctable[1] -- Ops.CTLD#CTLD_CARGO local static = crate:GetPositionable() -- Wrapper.Static#STATIC local crate_pos = static:GetCoordinate() -- Core.Point#COORDINATE local gpos = group:GetCoordinate() -- Core.Point#COORDINATE -- see how far we are from the crate - local distance = self:_GetDistance( gpos, crate_pos ) - self:T( string.format( "%s Distance to crate: %d", self.lid, distance ) ) + local distance = self:_GetDistance(gpos,crate_pos) + self:T(string.format("%s Distance to crate: %d", self.lid, distance)) -- move there - if distance > 30 and distance ~= -1 and self:IsStatus( "Searching" ) then - group:RouteGroundTo( crate_pos, 15, "Line abreast", 1 ) + if distance > 30 and distance ~= -1 and self:IsStatus("Searching") then + group:RouteGroundTo(crate_pos,15,"Line abreast",1) self.currwpt = crate_pos -- Core.Point#COORDINATE self:Move() elseif distance <= 30 and distance ~= -1 then @@ -235,102 +235,101 @@ do self:Arrive() end else - self:T( self.lid .. "No crates in reach!" ) + self:T(self.lid.."No crates in reach!") end return self end - + --- (Internal) Move towards crates in reach. -- @param #CTLD_ENGINEERING self -- @return #CTLD_ENGINEERING self function CTLD_ENGINEERING:Move() - self:T( self.lid .. "Move" ) - self:SetStatus( "Moving" ) + self:T(self.lid.."Move") + self:SetStatus("Moving") -- check if we arrived on target - -- local COps = self.C_Ops -- Ops.CTLD#CTLD + --local COps = self.C_Ops -- Ops.CTLD#CTLD local group = self.Group -- Wrapper.Group#GROUP local tgtpos = self.currwpt -- Core.Point#COORDINATE local gpos = group:GetCoordinate() -- Core.Point#COORDINATE -- see how far we are from the crate - local distance = self:_GetDistance( gpos, tgtpos ) - self:T( string.format( "%s Distance remaining: %d", self.lid, distance ) ) + local distance = self:_GetDistance(gpos,tgtpos) + self:T(string.format("%s Distance remaining: %d", self.lid, distance)) if distance <= 30 and distance ~= -1 then - -- arrived - self:Arrive() + -- arrived + self:Arrive() end return self end - + --- (Internal) Arrived at crates in reach. Stop group. -- @param #CTLD_ENGINEERING self -- @return #CTLD_ENGINEERING self function CTLD_ENGINEERING:Arrive() - self:T( self.lid .. "Arrive" ) - self:SetStatus( "Arrived" ) + self:T(self.lid.."Arrive") + self:SetStatus("Arrived") self.currwpt = nil local Grp = self.Group -- Wrapper.Group#GROUP Grp:RouteStop() return self end - + --- (Internal) Return distance in meters between two coordinates. -- @param #CTLD_ENGINEERING self -- @param Core.Point#COORDINATE _point1 Coordinate one -- @param Core.Point#COORDINATE _point2 Coordinate two -- @return #number Distance in meters or -1 - function CTLD_ENGINEERING:_GetDistance( _point1, _point2 ) - self:T( self.lid .. " _GetDistance" ) + function CTLD_ENGINEERING:_GetDistance(_point1, _point2) + self:T(self.lid .. " _GetDistance") if _point1 and _point2 then - local distance1 = _point1:Get2DDistance( _point2 ) - local distance2 = _point1:DistanceFromPointVec2( _point2 ) - -- self:I({dist1=distance1, dist2=distance2}) - if distance1 and type( distance1 ) == "number" then + local distance1 = _point1:Get2DDistance(_point2) + local distance2 = _point1:DistanceFromPointVec2(_point2) + --self:I({dist1=distance1, dist2=distance2}) + if distance1 and type(distance1) == "number" then return distance1 - elseif distance2 and type( distance2 ) == "number" then + elseif distance2 and type(distance2) == "number" then return distance2 else - self:E( "*****Cannot calculate distance!" ) - self:E( { _point1, _point2 } ) + self:E("*****Cannot calculate distance!") + self:E({_point1,_point2}) return -1 end else - self:E( "******Cannot calculate distance!" ) - self:E( { _point1, _point2 } ) + self:E("******Cannot calculate distance!") + self:E({_point1,_point2}) return -1 end end - ------------------------------------------------------ - --- **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 CargoType 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. - -- @field #number PerCrateMass Mass in kg - -- @field #number Stock Number of builds available, -1 for unlimited - -- @extends Core.Base#BASE - CTLD_CARGO = { - ClassName = "CTLD_CARGO", - ID = 0, - Name = "none", - Templates = { - }, - CargoType = "none", - HasBeenMoved = false, - LoadDirectly = false, - CratesNeeded = 0, - Positionable = nil, - HasBeenDropped = false, - PerCrateMass = 0, - Stock = nil, - Mark = nil, +------------------------------------------------------ +--- **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 CargoType 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. +-- @field #number PerCrateMass Mass in kg +-- @field #number Stock Number of builds available, -1 for unlimited +-- @extends Core.Base#BASE +CTLD_CARGO = { + ClassName = "CTLD_CARGO", + ID = 0, + Name = "none", + Templates = {}, + CargoType = "none", + HasBeenMoved = false, + LoadDirectly = false, + CratesNeeded = 0, + Positionable = nil, + HasBeenDropped = false, + PerCrateMass = 0, + Stock = nil, + Mark = nil, } - + --- Define cargo types. -- @type CTLD_CARGO.Enum -- @field #string Type Type of Cargo. @@ -343,13 +342,13 @@ do ["ENGINEERS"] = "Engineers", -- #string engineers ["STATIC"] = "Static", -- #string engineers } - + --- 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 SortEnum Enumerator of Type. + -- @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. @@ -358,119 +357,119 @@ do -- @param #number PerCrateMass Mass in kg -- @param #number Stock Number of builds available, nil for unlimited -- @return #CTLD_CARGO self - function CTLD_CARGO:New( ID, Name, Templates, SortEnum, HasBeenMoved, LoadDirectly, CratesNeeded, Positionable, Dropped, PerCrateMass, Stock ) + function CTLD_CARGO:New(ID, Name, Templates, Sorte, HasBeenMoved, LoadDirectly, CratesNeeded, Positionable, Dropped, PerCrateMass, Stock) -- Inherit everything from BASE class. - local self = BASE:Inherit( self, BASE:New() ) -- #CTLD - self:T( { ID, Name, Templates, SortEnum, HasBeenMoved, LoadDirectly, CratesNeeded, Positionable, Dropped } ) - self.ID = ID or math.random( 100000, 1000000 ) + 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 = SortEnum or "type" -- #CTLD_CARGO.Enum + self.CargoType = Sorte or "type" -- #CTLD_CARGO.Enum self.HasBeenMoved = HasBeenMoved or false -- #boolean self.LoadDirectly = LoadDirectly or false -- #boolean self.CratesNeeded = CratesNeeded or 0 -- #number self.Positionable = Positionable or nil -- Wrapper.Positionable#POSITIONABLE - self.HasBeenDropped = Dropped or false -- #boolean + self.HasBeenDropped = Dropped or false --#boolean self.PerCrateMass = PerCrateMass or 0 -- #number - self.Stock = Stock or nil -- #number + self.Stock = Stock or nil --#number self.Mark = nil return self end - + --- Query ID. -- @param #CTLD_CARGO self -- @return #number ID function CTLD_CARGO:GetID() return self.ID end - + --- Query Mass. -- @param #CTLD_CARGO self -- @return #number Mass in kg function CTLD_CARGO:GetMass() return self.PerCrateMass - end + 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 ) + function CTLD_CARGO:SetHasMoved(moved) self.HasBeenMoved = moved or false end - - --- Query if cargo has been loaded. + + --- 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 + return false + end end - + --- Set WasDropped. -- @param #CTLD_CARGO self -- @param #boolean dropped - function CTLD_CARGO:SetWasDropped( dropped ) + function CTLD_CARGO:SetWasDropped(dropped) self.HasBeenDropped = dropped or false end - + --- Get Stock. -- @param #CTLD_CARGO self -- @return #number Stock @@ -481,1048 +480,1045 @@ do return -1 end end - + --- Add Stock. -- @param #CTLD_CARGO self -- @param #number Number to add, one if nil. -- @return #CTLD_CARGO self - function CTLD_CARGO:AddStock( Number ) + function CTLD_CARGO:AddStock(Number) if self.Stock then -- Stock nil? local number = Number or 1 self.Stock = self.Stock + number end return self end - + --- Remove Stock. -- @param #CTLD_CARGO self -- @param #number Number to reduce, one if nil. -- @return #CTLD_CARGO self - function CTLD_CARGO:RemoveStock( Number ) + function CTLD_CARGO:RemoveStock(Number) if self.Stock then -- Stock nil? local number = Number or 1 self.Stock = self.Stock - number - if self.Stock < 0 then - self.Stock = 0 - end + if self.Stock < 0 then self.Stock = 0 end end return self end - + --- Query crate type for REPAIR -- @param #CTLD_CARGO self -- @param #boolean function CTLD_CARGO:IsRepair() - if self.CargoType == "Repair" then - return true - else - return false - end + if self.CargoType == "Repair" then + return true + else + return false + end end - + --- Query crate type for STATIC -- @param #CTLD_CARGO self -- @param #boolean function CTLD_CARGO:IsStatic() - if self.CargoType == "Static" then - return true - else - return false - end + if self.CargoType == "Static" then + return true + else + return false + end end - - function CTLD_CARGO:AddMark( Mark ) + + function CTLD_CARGO:AddMark(Mark) self.Mark = Mark return self end - - function CTLD_CARGO:GetMark( Mark ) + + function CTLD_CARGO:GetMark(Mark) return self.Mark end - + function CTLD_CARGO:WipeMark() self.Mark = nil return self 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 +------------------------------------------------------------------------- +--- **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, crates, which have to be build on-location, or static like ammo chests. - -- * Option to persist (save&load) your dropped troops, crates and vehicles. - -- - -- ## 0. Prerequisites - -- - -- You need to load an .ogg sound file 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) - -- -- if you want to add weight to your Heli, troops can have a weight in kg **per person**. Currently no max weight checked. Fly carefully. - -- my_ctld:AddTroopsCargo("Anti-Tank Small",{"ATS"},CTLD_CARGO.Enum.TROOPS,3,80) - -- - -- -- add infantry unit called "Anti-Tank" using templates "AA" and "AA"", of type TROOP with size 4. No weight. We only have 2 in stock: - -- my_ctld:AddTroopsCargo("Anti-Air",{"AA","AA2"},CTLD_CARGO.Enum.TROOPS,4,nil,2) - -- - -- -- add an engineers unit called "Wrenches" using template "Engineers", of type ENGINEERS with size 2. Engineers can be loaded, dropped, - -- -- and extracted like troops. However, they will seek to build and/or repair crates found in a given radius. Handy if you can't stay - -- -- to build or repair or under fire. - -- my_ctld:AddTroopsCargo("Wrenches",{"Engineers"},CTLD_CARGO.Enum.ENGINEERS,4) - -- myctld.EngineerSearch = 2000 -- teams will search for crates in this radius. - -- - -- -- 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) - -- -- if you want to add weight to your Heli, crates can have a weight in kg **per crate**. Currently no max weight checked. Fly carefully. - -- my_ctld:AddCratesCargo("Humvee",{"Humvee"},CTLD_CARGO.Enum.VEHICLE,2,2775) - -- -- if you want to limit your stock, add a number (here: 10) as parameter after weight. No parameter / nil means unlimited stock. - -- my_ctld:AddCratesCargo("Humvee",{"Humvee"},CTLD_CARGO.Enum.VEHICLE,2,2775,10) - -- - -- -- 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) - -- - -- -- add crates to repair FOB or VEHICLE type units - the 2nd parameter needs to match the template you want to repair - -- my_ctld:AddCratesRepair("Humvee Repair","Humvee",CTLD_CARGO.Enum.REPAIR,1) - -- my_ctld.repairtime = 300 -- takes 300 seconds to repair something - -- - -- -- add static cargo objects, e.g ammo chests - the name needs to refer to a STATIC object in the mission editor, - -- -- here: it's the UNIT name (not the GROUP name!), the second parameter is the weight in kg. - -- my_ctld:AddStaticsCargo("Ammunition",500) - -- - -- ## 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 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) - -- - -- -- Add a zone of type SHIP to our setup. Players can load troops and crates from this ship - -- -- "Tarawa" is the unitname (callsign) of the ship from the ME. Players can load, if they are inside the zone. - -- -- The ship is 240 meters long and 20 meters wide. - -- -- Note that you need to adjust the max hover height to deck height plus 5 meters or so for loading to work. - -- -- When the ship is moving, forcing hoverload might not be a good idea. - -- my_ctld:AddCTLDZone("Tarawa",CTLD.CargoZoneType.SHIP,SMOKECOLOR.Blue,true,true,240,20) - -- - -- ## 2. Options - -- - -- The following options are available (with their defaults). Only set the ones you want changed: - -- - -- my_ctld.useprefix = true -- (DO NOT SWITCH THIS OFF UNLESS YOU KNOW WHAT YOU ARE DOING!) Adjust **before** starting CTLD. If set to false, *all* choppers of the coalition side will be enabled for CTLD. - -- my_ctld.CrateDistance = 35 -- List and Load crates in this radius only. - -- my_ctld.dropcratesanywhere = false -- Option to allow crates to be dropped anywhere. - -- 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) - -- my_ctld.suppressmessages = false -- Set to true if you want to script your own messages. - -- my_ctld.repairtime = 300 -- Number of seconds it takes to repair a unit. - -- my_ctld.cratecountry = country.id.GERMANY -- ID of crates. Will default to country.id.RUSSIA for RED coalition setups. - -- my_ctld.allowcratepickupagain = true -- allow re-pickup crates that were dropped. - -- my_ctld.enableslingload = false -- allow cargos to be slingloaded - might not work for all cargo types - -- my_ctld.pilotmustopendoors = false -- force opening of doors - -- my_ctld.SmokeColor = SMOKECOLOR.Red -- color to use when dropping smoke from heli - -- my_ctld.FlareColor = FLARECOLOR.Red -- color to use when flaring from heli - -- - -- ## 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, 12) - -- - -- -- Default unit type capabilities are: - -- - -- ["SA342Mistral"] = {type="SA342Mistral", crates=false, troops=true, cratelimit = 0, trooplimit = 4, length = 12}, - -- ["SA342L"] = {type="SA342L", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 12}, - -- ["SA342M"] = {type="SA342M", crates=false, troops=true, cratelimit = 0, trooplimit = 4, length = 12}, - -- ["SA342Minigun"] = {type="SA342Minigun", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 12}, - -- ["UH-1H"] = {type="UH-1H", crates=true, troops=true, cratelimit = 1, trooplimit = 8, length = 15}, - -- ["Mi-8MT"] = {type="Mi-8MTV2", crates=true, troops=true, cratelimit = 2, trooplimit = 12, length = 15}, - -- ["Ka-50"] = {type="Ka-50", crates=false, troops=false, cratelimit = 0, trooplimit = 0, length = 15}, - -- ["Mi-24P"] = {type="Mi-24P", crates=true, troops=true, cratelimit = 2, trooplimit = 8, length = 18}, - -- ["Mi-24V"] = {type="Mi-24V", crates=true, troops=true, cratelimit = 2, trooplimit = 8, length = 18}, - -- ["Hercules"] = {type="Hercules", crates=true, troops=true, cratelimit = 7, trooplimit = 64, length = 25}, - -- - -- ### 2.1.2 Activate and deactivate zones - -- - -- Activate a zone: - -- - -- -- Activate zone called Name of type #CTLD.CargoZoneType ZoneType: - -- my_ctld:ActivateZone(Name,CTLD.CargoZoneType.MOVE) - -- - -- Deactivate a zone: - -- - -- -- Deactivate zone called Name of type #CTLD.CargoZoneType ZoneType: - -- my_ctld:DeactivateZone(Name,CTLD.CargoZoneType.DROP) - -- - -- ## 2.1.3 Limit and manage available resources - -- - -- When adding generic cargo types, you can effectively limit how many units can be dropped/build by the players, e.g. - -- - -- -- if you want to limit your stock, add a number (here: 10) as parameter after weight. No parameter / nil means unlimited stock. - -- my_ctld:AddCratesCargo("Humvee",{"Humvee"},CTLD_CARGO.Enum.VEHICLE,2,2775,10) - -- - -- You can manually add or remove the available stock like so: - -- - -- -- Crates - -- my_ctld:AddStockCrates("Humvee", 2) - -- my_ctld:RemoveStockCrates("Humvee", 2) - -- - -- -- Troops - -- my_ctld:AddStockTroops("Anti-Air", 2) - -- my_ctld:RemoveStockTroops("Anti-Air", 2) - -- - -- Notes: - -- Troops dropped back into a LOAD zone will effectively be added to the stock. Crates lost in e.g. a heli crash are just that - lost. - -- - -- ## 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 my_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 my_ctld:OnAfterCratesPickedUp(From, Event, To, Group, Unit, Cargo) - -- ... your code here ... - -- end - -- - -- ## 3.3 OnAfterTroopsDeployed - -- - -- This function is called when a player has deployed troops into the field: - -- - -- function my_ctld:OnAfterTroopsDeployed(From, Event, To, Group, Unit, Troops) - -- ... your code here ... - -- end - -- - -- ## 3.4 OnAfterTroopsExtracted - -- - -- This function is called when a player has re-boarded already deployed troops from the field: - -- - -- function my_ctld:OnAfterTroopsExtracted(From, Event, To, Group, Unit, Troops) - -- ... your code here ... - -- end - -- - -- ## 3.5 OnAfterCratesDropped - -- - -- This function is called when a player has deployed crates to a DROP zone: - -- - -- function my_ctld:OnAfterCratesDropped(From, Event, To, Group, Unit, Cargotable) - -- ... your code here ... - -- end - -- - -- ## 3.6 OnAfterCratesBuild, OnAfterCratesRepaired - -- - -- This function is called when a player has build a vehicle or FOB: - -- - -- function my_ctld:OnAfterCratesBuild(From, Event, To, Group, Unit, Vehicle) - -- ... your code here ... - -- end - -- - -- function my_ctld:OnAfterCratesRepaired(From, Event, To, Group, Unit, Vehicle) - -- ... your code here ... - -- end - -- - -- ## 3.7 A simple SCORING example: - -- - -- To award player with points, using the SCORING Class (SCORING: my_Scoring, CTLD: CTLD_Cargotransport) - -- - -- function CTLD_Cargotransport:OnAfterCratesDropped(From, Event, To, Group, Unit, Cargotable) - -- local points = 10 - -- if Unit then - -- local PlayerName = Unit:GetPlayerName() - -- my_scoring:_AddPlayerFromUnit( Unit ) - -- my_scoring:AddGoalScore(Unit, "CTLD", string.format("Pilot %s has been awarded %d points for transporting cargo crates!", PlayerName, points), points) - -- end - -- end - -- - -- function CTLD_Cargotransport:OnAfterCratesBuild(From, Event, To, Group, Unit, Vehicle) - -- local points = 5 - -- if Unit then +--- *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, crates, which have to be build on-location, or static like ammo chests. +-- * Option to persist (save&load) your dropped troops, crates and vehicles. +-- +-- ## 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) +-- -- if you want to add weight to your Heli, troops can have a weight in kg **per person**. Currently no max weight checked. Fly carefully. +-- my_ctld:AddTroopsCargo("Anti-Tank Small",{"ATS"},CTLD_CARGO.Enum.TROOPS,3,80) +-- +-- -- add infantry unit called "Anti-Tank" using templates "AA" and "AA"", of type TROOP with size 4. No weight. We only have 2 in stock: +-- my_ctld:AddTroopsCargo("Anti-Air",{"AA","AA2"},CTLD_CARGO.Enum.TROOPS,4,nil,2) +-- +-- -- add an engineers unit called "Wrenches" using template "Engineers", of type ENGINEERS with size 2. Engineers can be loaded, dropped, +-- -- and extracted like troops. However, they will seek to build and/or repair crates found in a given radius. Handy if you can\'t stay +-- -- to build or repair or under fire. +-- my_ctld:AddTroopsCargo("Wrenches",{"Engineers"},CTLD_CARGO.Enum.ENGINEERS,4) +-- myctld.EngineerSearch = 2000 -- teams will search for crates in this radius. +-- +-- -- 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) +-- -- if you want to add weight to your Heli, crates can have a weight in kg **per crate**. Currently no max weight checked. Fly carefully. +-- my_ctld:AddCratesCargo("Humvee",{"Humvee"},CTLD_CARGO.Enum.VEHICLE,2,2775) +-- -- if you want to limit your stock, add a number (here: 10) as parameter after weight. No parameter / nil means unlimited stock. +-- my_ctld:AddCratesCargo("Humvee",{"Humvee"},CTLD_CARGO.Enum.VEHICLE,2,2775,10) +-- +-- -- 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) +-- +-- -- add crates to repair FOB or VEHICLE type units - the 2nd parameter needs to match the template you want to repair +-- my_ctld:AddCratesRepair("Humvee Repair","Humvee",CTLD_CARGO.Enum.REPAIR,1) +-- my_ctld.repairtime = 300 -- takes 300 seconds to repair something +-- +-- -- add static cargo objects, e.g ammo chests - the name needs to refer to a STATIC object in the mission editor, +-- -- here: it\'s the UNIT name (not the GROUP name!), the second parameter is the weight in kg. +-- my_ctld:AddStaticsCargo("Ammunition",500) +-- +-- ## 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 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) +-- +-- -- Add a zone of type SHIP to our setup. Players can load troops and crates from this ship +-- -- "Tarawa" is the unitname (callsign) of the ship from the ME. Players can load, if they are inside the zone. +-- -- The ship is 240 meters long and 20 meters wide. +-- -- Note that you need to adjust the max hover height to deck height plus 5 meters or so for loading to work. +-- -- When the ship is moving, forcing hoverload might not be a good idea. +-- my_ctld:AddCTLDZone("Tarawa",CTLD.CargoZoneType.SHIP,SMOKECOLOR.Blue,true,true,240,20) +-- +-- ## 2. Options +-- +-- The following options are available (with their defaults). Only set the ones you want changed: +-- +-- my_ctld.useprefix = true -- (DO NOT SWITCH THIS OFF UNLESS YOU KNOW WHAT YOU ARE DOING!) Adjust **before** starting CTLD. If set to false, *all* choppers of the coalition side will be enabled for CTLD. +-- my_ctld.CrateDistance = 35 -- List and Load crates in this radius only. +-- my_ctld.dropcratesanywhere = false -- Option to allow crates to be dropped anywhere. +-- 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) +-- my_ctld.suppressmessages = false -- Set to true if you want to script your own messages. +-- my_ctld.repairtime = 300 -- Number of seconds it takes to repair a unit. +-- my_ctld.cratecountry = country.id.GERMANY -- ID of crates. Will default to country.id.RUSSIA for RED coalition setups. +-- my_ctld.allowcratepickupagain = true -- allow re-pickup crates that were dropped. +-- my_ctld.enableslingload = false -- allow cargos to be slingloaded - might not work for all cargo types +-- my_ctld.pilotmustopendoors = false -- force opening of doors +-- my_ctld.SmokeColor = SMOKECOLOR.Red -- color to use when dropping smoke from heli +-- my_ctld.FlareColor = FLARECOLOR.Red -- color to use when flaring from heli +-- my_ctld.basetype = "container_cargo" -- default shape of the cargo container +-- +-- ## 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, 12) +-- +-- -- Default unit type capabilities are: +-- +-- ["SA342Mistral"] = {type="SA342Mistral", crates=false, troops=true, cratelimit = 0, trooplimit = 4, length = 12}, +-- ["SA342L"] = {type="SA342L", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 12}, +-- ["SA342M"] = {type="SA342M", crates=false, troops=true, cratelimit = 0, trooplimit = 4, length = 12}, +-- ["SA342Minigun"] = {type="SA342Minigun", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 12}, +-- ["UH-1H"] = {type="UH-1H", crates=true, troops=true, cratelimit = 1, trooplimit = 8, length = 15}, +-- ["Mi-8MT"] = {type="Mi-8MTV2", crates=true, troops=true, cratelimit = 2, trooplimit = 12, length = 15}, +-- ["Ka-50"] = {type="Ka-50", crates=false, troops=false, cratelimit = 0, trooplimit = 0, length = 15}, +-- ["Mi-24P"] = {type="Mi-24P", crates=true, troops=true, cratelimit = 2, trooplimit = 8, length = 18}, +-- ["Mi-24V"] = {type="Mi-24V", crates=true, troops=true, cratelimit = 2, trooplimit = 8, length = 18}, +-- ["Hercules"] = {type="Hercules", crates=true, troops=true, cratelimit = 7, trooplimit = 64, length = 25}, +-- +-- +-- ### 2.1.2 Activate and deactivate zones +-- +-- Activate a zone: +-- +-- -- Activate zone called Name of type #CTLD.CargoZoneType ZoneType: +-- my_ctld:ActivateZone(Name,CTLD.CargoZoneType.MOVE) +-- +-- Deactivate a zone: +-- +-- -- Deactivate zone called Name of type #CTLD.CargoZoneType ZoneType: +-- my_ctld:DeactivateZone(Name,CTLD.CargoZoneType.DROP) +-- +-- ## 2.1.3 Limit and manage available resources +-- +-- When adding generic cargo types, you can effectively limit how many units can be dropped/build by the players, e.g. +-- +-- -- if you want to limit your stock, add a number (here: 10) as parameter after weight. No parameter / nil means unlimited stock. +-- my_ctld:AddCratesCargo("Humvee",{"Humvee"},CTLD_CARGO.Enum.VEHICLE,2,2775,10) +-- +-- You can manually add or remove the available stock like so: +-- +-- -- Crates +-- my_ctld:AddStockCrates("Humvee", 2) +-- my_ctld:RemoveStockCrates("Humvee", 2) +-- +-- -- Troops +-- my_ctld:AddStockTroops("Anti-Air", 2) +-- my_ctld:RemoveStockTroops("Anti-Air", 2) +-- +-- Notes: +-- Troops dropped back into a LOAD zone will effectively be added to the stock. Crates lost in e.g. a heli crash are just that - lost. +-- +-- ## 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 my_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 my_ctld:OnAfterCratesPickedUp(From, Event, To, Group, Unit, Cargo) +-- ... your code here ... +-- end +-- +-- ## 3.3 OnAfterTroopsDeployed +-- +-- This function is called when a player has deployed troops into the field: +-- +-- function my_ctld:OnAfterTroopsDeployed(From, Event, To, Group, Unit, Troops) +-- ... your code here ... +-- end +-- +-- ## 3.4 OnAfterTroopsExtracted +-- +-- This function is called when a player has re-boarded already deployed troops from the field: +-- +-- function my_ctld:OnAfterTroopsExtracted(From, Event, To, Group, Unit, Troops) +-- ... your code here ... +-- end +-- +-- ## 3.5 OnAfterCratesDropped +-- +-- This function is called when a player has deployed crates to a DROP zone: +-- +-- function my_ctld:OnAfterCratesDropped(From, Event, To, Group, Unit, Cargotable) +-- ... your code here ... +-- end +-- +-- ## 3.6 OnAfterCratesBuild, OnAfterCratesRepaired +-- +-- This function is called when a player has build a vehicle or FOB: +-- +-- function my_ctld:OnAfterCratesBuild(From, Event, To, Group, Unit, Vehicle) +-- ... your code here ... +-- end +-- +-- function my_ctld:OnAfterCratesRepaired(From, Event, To, Group, Unit, Vehicle) +-- ... your code here ... +-- end + -- +-- ## 3.7 A simple SCORING example: +-- +-- To award player with points, using the SCORING Class (SCORING: my_Scoring, CTLD: CTLD_Cargotransport) +-- +-- function CTLD_Cargotransport:OnAfterCratesDropped(From, Event, To, Group, Unit, Cargotable) +-- local points = 10 +-- if Unit then +-- local PlayerName = Unit:GetPlayerName() +-- my_scoring:_AddPlayerFromUnit( Unit ) +-- my_scoring:AddGoalScore(Unit, "CTLD", string.format("Pilot %s has been awarded %d points for transporting cargo crates!", PlayerName, points), points) +-- end +-- end +-- +-- function CTLD_Cargotransport:OnAfterCratesBuild(From, Event, To, Group, Unit, Vehicle) +-- local points = 5 +-- if Unit then -- local PlayerName = Unit:GetPlayerName() -- my_scoring:_AddPlayerFromUnit( Unit ) -- my_scoring:AddGoalScore(Unit, "CTLD", string.format("Pilot %s has been awarded %d points for the construction of Units!", PlayerName, points), points) - -- end - -- 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, build and repair crates. Also see options. - -- - -- ## 4.2 Manage Troops - -- - -- Use this entry to load, drop and extract troops. NOTE - with extract you can only load troops from the field that were deployed prior. - -- Currently limited CTLD_CARGO troops, which are build from **one** template. Also, this will heal/complete your units as they are respawned. - -- - -- ## 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 or drop smoke or flare from Heli - -- - -- 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. - -- - -- ## 4.7 List Inventory - -- - -- Lists invetory of available units to drop or build. - -- - -- ## 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 - -- my_ctld.HercMaxSpeed = 77 -- 77mps or 270kph or 150kn - -- - -- Also, the following options need to be set to `true`: - -- - -- my_ctld.useprefix = true -- this is true by default and MUST BE ON. - -- - -- Standard transport capabilities as per the real Hercules are: - -- - -- ["Hercules"] = {type="Hercules", crates=true, troops=true, cratelimit = 7, trooplimit = 64}, -- 19t cargo, 64 paratroopers - -- - -- ## 6. Save and load back units - persistance - -- - -- You can save and later load back units dropped or build to make your mission persistent. - -- For this to work, you need to de-sanitize **io** and **lfs** in your MissionScripting.lua, which is located in your DCS installtion folder under Scripts. - -- There is a risk involved in doing that; if you do not know what that means, this is possibly not for you. - -- - -- Use the following options to manage your saves: - -- - -- my_ctld.enableLoadSave = true -- allow auto-saving and loading of files - -- my_ctld.saveinterval = 600 -- save every 10 minutes - -- my_ctld.filename = "missionsave.csv" -- example filename - -- my_ctld.filepath = "C:\\Users\\myname\\Saved Games\\DCS\Missions\\MyMission" -- example path - -- my_ctld.eventoninject = true -- fire OnAfterCratesBuild and OnAfterTroopsDeployed events when loading (uses Inject functions) - -- - -- Then use an initial load at the beginning of your mission: - -- - -- my_ctld:__Load(10) - -- - -- **Caveat:** - -- If you use units build by multiple templates, they will effectively double on loading. Dropped crates are not saved. Current stock is not saved. - -- - -- @field #CTLD - CTLD = { - ClassName = "CTLD", - verbose = 0, - 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, - 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 = 35, -- list crates in this radius - debug = false, - wpZones = {}, - dropOffZones = {}, - pickupZones = {}, - } +-- end +-- 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, build and repair crates. Also see options. +-- +-- ## 4.2 Manage Troops +-- +-- Use this entry to load, drop and extract troops. NOTE - with extract you can only load troops from the field that were deployed prior. +-- Currently limited CTLD_CARGO troops, which are build from **one** template. Also, this will heal/complete your units as they are respawned. +-- +-- ## 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 or drop smoke or flare from Heli +-- +-- 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. +-- +-- ## 4.7 List Inventory +-- +-- Lists invetory of available units to drop or build. +-- +-- ## 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 +-- my_ctld.HercMaxSpeed = 77 -- 77mps or 270kph or 150kn +-- +-- Also, the following options need to be set to `true`: +-- +-- my_ctld.useprefix = true -- this is true by default and MUST BE ON. +-- +-- Standard transport capabilities as per the real Hercules are: +-- +-- ["Hercules"] = {type="Hercules", crates=true, troops=true, cratelimit = 7, trooplimit = 64}, -- 19t cargo, 64 paratroopers +-- +-- ## 6. Save and load back units - persistance +-- +-- You can save and later load back units dropped or build to make your mission persistent. +-- For this to work, you need to de-sanitize **io** and **lfs** in your MissionScripting.lua, which is located in your DCS installtion folder under Scripts. +-- There is a risk involved in doing that; if you do not know what that means, this is possibly not for you. +-- +-- Use the following options to manage your saves: +-- +-- my_ctld.enableLoadSave = true -- allow auto-saving and loading of files +-- my_ctld.saveinterval = 600 -- save every 10 minutes +-- my_ctld.filename = "missionsave.csv" -- example filename +-- my_ctld.filepath = "C:\\Users\\myname\\Saved Games\\DCS\Missions\\MyMission" -- example path +-- my_ctld.eventoninject = true -- fire OnAfterCratesBuild and OnAfterTroopsDeployed events when loading (uses Inject functions) +-- +-- Then use an initial load at the beginning of your mission: +-- +-- my_ctld:__Load(10) +-- +-- **Caveat:** +-- If you use units build by multiple templates, they will effectively double on loading. Dropped crates are not saved. Current stock is not saved. +-- +-- @field #CTLD +CTLD = { + ClassName = "CTLD", + verbose = 0, + 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, + 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 = 35, -- list crates in this radius + debug = false, + wpZones = {}, + dropOffZones = {}, + pickupZones = {}, +} - ------------------------------ - -- DONE: Zone Checks - -- DONE: TEST Hover load and unload - -- DONE: Crate unload - -- DONE: Hover (auto-)load - -- DONE: (More) Housekeeping - -- DONE: Troops running to WP Zone - -- DONE: Zone Radio Beacons - -- DONE: Stats Running - -- DONE: Added support for Hercules - -- TODO: Possibly - either/or loading crates and troops - -- DONE: Make inject respect existing cargo types - -- DONE: Drop beacons or flares/smoke - -- DONE: Add statics as cargo - -- DONE: List cargo in stock - -- DONE: Limit of troops, crates buildable? - -- DONE: Allow saving of Troops & Vehicles - ------------------------------ +------------------------------ +-- DONE: Zone Checks +-- DONE: TEST Hover load and unload +-- DONE: Crate unload +-- DONE: Hover (auto-)load +-- DONE: (More) Housekeeping +-- DONE: Troops running to WP Zone +-- DONE: Zone Radio Beacons +-- DONE: Stats Running +-- DONE: Added support for Hercules +-- TODO: Possibly - either/or loading crates and troops +-- DONE: Make inject respect existing cargo types +-- DONE: Drop beacons or flares/smoke +-- DONE: Add statics as cargo +-- DONE: List cargo in stock +-- DONE: Limit of troops, crates buildable? +-- DONE: Allow saving of Troops & Vehicles +------------------------------ - --- 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 +--- 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,ship - -- @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 - -- @field #number shiplength For ships - length of ship - -- @field #number shipwidth For ships - width of ship +--- 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,ship +-- @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 +-- @field #number shiplength For ships - length of ship +-- @field #number shipwidth For ships - width of ship - --- Zone Type Info. - -- @type CTLD.CargoZoneType - CTLD.CargoZoneType = { - LOAD = "load", - DROP = "drop", - MOVE = "move", - SHIP = "ship", - } +--- Zone Type Info. +-- @type CTLD.CargoZoneType +CTLD.CargoZoneType = { + LOAD = "load", + DROP = "drop", + MOVE = "move", + SHIP = "ship", +} - --- 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). +--- 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, length = 12 }, - ["SA342L"] = { type = "SA342L", crates = false, troops = true, cratelimit = 0, trooplimit = 2, length = 12 }, - ["SA342M"] = { type = "SA342M", crates = false, troops = true, cratelimit = 0, trooplimit = 4, length = 12 }, - ["SA342Minigun"] = { type = "SA342Minigun", crates = false, troops = true, cratelimit = 0, trooplimit = 2, length = 12 }, - ["UH-1H"] = { type = "UH-1H", crates = true, troops = true, cratelimit = 1, trooplimit = 8, length = 15 }, - ["Mi-8MTV2"] = { type = "Mi-8MTV2", crates = true, troops = true, cratelimit = 2, trooplimit = 12, length = 15 }, - ["Mi-8MT"] = { type = "Mi-8MTV2", crates = true, troops = true, cratelimit = 2, trooplimit = 12, length = 15 }, - ["Ka-50"] = { type = "Ka-50", crates = false, troops = false, cratelimit = 0, trooplimit = 0, length = 15 }, - ["Mi-24P"] = { type = "Mi-24P", crates = true, troops = true, cratelimit = 2, trooplimit = 8, length = 18 }, - ["Mi-24V"] = { type = "Mi-24V", crates = true, troops = true, cratelimit = 2, trooplimit = 8, length = 18 }, - ["Hercules"] = { type = "Hercules", crates = true, troops = true, cratelimit = 7, trooplimit = 64, length = 25 }, -- 19t cargo, 64 paratroopers. Actually it's longer, but the center coord is off-center of the model. - } +--- 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, length = 12}, + ["SA342L"] = {type="SA342L", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 12}, + ["SA342M"] = {type="SA342M", crates=false, troops=true, cratelimit = 0, trooplimit = 4, length = 12}, + ["SA342Minigun"] = {type="SA342Minigun", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 12}, + ["UH-1H"] = {type="UH-1H", crates=true, troops=true, cratelimit = 1, trooplimit = 8, length = 15}, + ["Mi-8MTV2"] = {type="Mi-8MTV2", crates=true, troops=true, cratelimit = 2, trooplimit = 12, length = 15}, + ["Mi-8MT"] = {type="Mi-8MTV2", crates=true, troops=true, cratelimit = 2, trooplimit = 12, length = 15}, + ["Ka-50"] = {type="Ka-50", crates=false, troops=false, cratelimit = 0, trooplimit = 0, length = 15}, + ["Mi-24P"] = {type="Mi-24P", crates=true, troops=true, cratelimit = 2, trooplimit = 8, length = 18}, + ["Mi-24V"] = {type="Mi-24V", crates=true, troops=true, cratelimit = 2, trooplimit = 8, length = 18}, + ["Hercules"] = {type="Hercules", crates=true, troops=true, cratelimit = 7, trooplimit = 64, length = 25}, -- 19t cargo, 64 paratroopers. + --Actually it's longer, but the center coord is off-center of the model. +} - --- CTLD class version. - -- @field #string version - CTLD.version = "1.0.1" +--- CTLD class version. +-- @field #string version +CTLD.version="1.0.2" - --- 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 ) - -- 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 +--- 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) + -- 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.coalition = Coalition - self.coalitiontxt = string.lower( UTILS.GetCoalitionName( self.coalition ) ) + self:E("ERROR: Unknown coalition in CTLD!") 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 + 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") - -- 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 + -- 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("*", "TroopsExtracted", "*") -- CTLD extract event. - self:AddTransition("*", "CratesPickedUp", "*") -- CTLD pickup 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("*", "CratesRepaired", "*") -- CTLD repair event. - self:AddTransition("*", "Load", "*") -- CTLD load event. - self:AddTransition("*", "Save", "*") -- CTLD save event. + self:AddTransition("*", "TroopsPickedUp", "*") -- CTLD pickup event. + self:AddTransition("*", "TroopsExtracted", "*") -- CTLD extract event. + self:AddTransition("*", "CratesPickedUp", "*") -- CTLD pickup 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("*", "CratesRepaired", "*") -- CTLD repair event. + self:AddTransition("*", "Load", "*") -- CTLD load event. + self:AddTransition("*", "Save", "*") -- CTLD save 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 = {} - - -- radio beacons - self.RadioSound = "beacon.ogg" - - -- zones stuff - self.pickupZones = {} - self.dropOffZones = {} - self.wpZones = {} - self.shipZones = {} - - -- Cargo - self.Cargo_Crates = {} - self.Cargo_Troops = {} - self.Cargo_Statics = {} - self.Loaded_Cargo = {} - self.Spawned_Crates = {} - self.Spawned_Cargo = {} - self.MenusDone = {} - self.DroppedTroops = {} - self.DroppedCrates = {} - self.CargoCounter = 0 - self.CrateCounter = 0 - self.TroopCounter = 0 - - -- added engineering - self.Engineers = 0 -- #number use as counter - self.EngineersInField = {} -- #table holds #CTLD_ENGINEERING objects - self.EngineerSearch = 2000 -- #number search distance for crates to build or repair - - -- setup - self.CrateDistance = 35 -- list/load crates in this radius - self.ExtractFactor = 3.33 -- factor for troops extraction, i.e. CrateDistance * Extractfactor - self.prefixes = Prefixes or { "Cargoheli" } - -- self.I({prefixes = self.prefixes}) - self.useprefix = true - - self.maximumHoverHeight = 15 - self.minimumHoverHeight = 4 - self.forcehoverload = true - self.hoverautoloading = true - self.dropcratesanywhere = false -- #1570 - - self.smokedistance = 2000 - self.movetroopstowpzone = true - self.movetroopsdistance = 5000 - - -- added support Hercules Mod - self.enableHercules = false - self.HercMinAngels = 165 -- for troop/cargo drop via chute - self.HercMaxAngels = 2000 -- for troop/cargo drop via chute - self.HercMaxSpeed = 77 -- 280 kph or 150kn eq 77 mps - - -- message suppression - self.suppressmessages = false - - -- time to repair a unit/group - self.repairtime = 300 - - -- place spawned crates in front of aircraft - self.placeCratesAhead = false - - -- country of crates spawned - self.cratecountry = country.id.GERMANY - - -- for opening doors - self.pilotmustopendoors = false - - if self.coalition == coalition.side.RED then - self.cratecountry = country.id.RUSSIA - end - - -- load and save dropped TROOPS - self.enableLoadSave = false - self.filepath = nil - self.saveinterval = 600 - self.eventoninject = true - - local AliaS = string.gsub( self.alias, " ", "_" ) - self.filename = string.format( "CTLD_%s_Persist.csv", AliaS ) - - -- allow re-pickup crates - self.allowcratepickupagain = true - - -- slingload - self.enableslingload = false - - -- Smokes and Flares - self.SmokeColor = SMOKECOLOR.Red - self.FlareColor = FLARECOLOR.Red - - for i = 1, 100 do - math.random() - end - - self:_GenerateVHFrequencies() - self:_GenerateUHFrequencies() - self:_GenerateFMFrequencies() - - ------------------------ - --- Pseudo Functions --- - ------------------------ - + + -- tables + self.PilotGroups ={} + self.CtldUnits = {} + + -- Beacons + self.FreeVHFFrequencies = {} + self.FreeUHFFrequencies = {} + self.FreeFMFrequencies = {} + self.UsedVHFFrequencies = {} + self.UsedUHFFrequencies = {} + self.UsedFMFrequencies = {} + + -- radio beacons + self.RadioSound = "beacon.ogg" + + -- zones stuff + self.pickupZones = {} + self.dropOffZones = {} + self.wpZones = {} + self.shipZones = {} + + -- Cargo + self.Cargo_Crates = {} + self.Cargo_Troops = {} + self.Cargo_Statics = {} + self.Loaded_Cargo = {} + self.Spawned_Crates = {} + self.Spawned_Cargo = {} + self.MenusDone = {} + self.DroppedTroops = {} + self.DroppedCrates = {} + self.CargoCounter = 0 + self.CrateCounter = 0 + self.TroopCounter = 0 + + -- added engineering + self.Engineers = 0 -- #number use as counter + self.EngineersInField = {} -- #table holds #CTLD_ENGINEERING objects + self.EngineerSearch = 2000 -- #number search distance for crates to build or repair + + -- setup + self.CrateDistance = 35 -- list/load crates in this radius + self.ExtractFactor = 3.33 -- factor for troops extraction, i.e. CrateDistance * Extractfactor + self.prefixes = Prefixes or {"Cargoheli"} + --self.I({prefixes = self.prefixes}) + self.useprefix = true + + self.maximumHoverHeight = 15 + self.minimumHoverHeight = 4 + self.forcehoverload = true + self.hoverautoloading = true + self.dropcratesanywhere = false -- #1570 + + self.smokedistance = 2000 + self.movetroopstowpzone = true + self.movetroopsdistance = 5000 + + -- added support Hercules Mod + self.enableHercules = false + self.HercMinAngels = 165 -- for troop/cargo drop via chute + self.HercMaxAngels = 2000 -- for troop/cargo drop via chute + self.HercMaxSpeed = 77 -- 280 kph or 150kn eq 77 mps + + -- message suppression + self.suppressmessages = false + + -- time to repair a unit/group + self.repairtime = 300 + + -- place spawned crates in front of aircraft + self.placeCratesAhead = false + + -- country of crates spawned + self.cratecountry = country.id.GERMANY + + -- for opening doors + self.pilotmustopendoors = false + + if self.coalition == coalition.side.RED then + self.cratecountry = country.id.RUSSIA + end + + -- load and save dropped TROOPS + self.enableLoadSave = false + self.filepath = nil + self.saveinterval = 600 + self.eventoninject = true + + local AliaS = string.gsub(self.alias," ","_") + self.filename = string.format("CTLD_%s_Persist.csv",AliaS) + + -- allow re-pickup crates + self.allowcratepickupagain = true + + -- slingload + self.enableslingload = false + self.basetype = "container_cargo" -- shape of the container + + -- Smokes and Flares + self.SmokeColor = SMOKECOLOR.Red + self.FlareColor = FLARECOLOR.Red + + for i=1,100 do + math.random() + end + + self:_GenerateVHFrequencies() + self:_GenerateUHFrequencies() + self:_GenerateFMFrequencies() + + ------------------------ + --- 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. - - --- Triggers the FSM event "Load". - -- @function [parent=#CTLD] Load - -- @param #CTLD self - - --- Triggers the FSM event "Load" after a delay. - -- @function [parent=#CTLD] __Load - -- @param #CTLD self - -- @param #number delay Delay in seconds. - - --- Triggers the FSM event "Save". - -- @function [parent=#CTLD] Load - -- @param #CTLD self - - --- Triggers the FSM event "Save" after a delay. - -- @function [parent=#CTLD] __Save - -- @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 troops. - -- @return #CTLD self - - --- FSM Function OnAfterTroopsExtracted. - -- @function [parent=#CTLD] OnAfterTroopsExtracted - -- @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 troops. - -- @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 - - --- FSM Function OnAfterCratesRepaired. - -- @function [parent=#CTLD] OnAfterCratesRepaired - -- @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 repaired. - -- @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. - - --- FSM Function OnAfterLoad. - -- @function [parent=#CTLD] OnAfterLoad - -- @param #CTLD self - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - -- @param #string path (Optional) Path where the file is located. Default is the DCS root installation folder or your "Saved Games\\DCS" folder if the lfs module is desanitized. - -- @param #string filename (Optional) File name for loading. Default is "CTLD__Persist.csv". - - --- FSM Function OnAfterSave. - -- @function [parent=#CTLD] OnAfterSave - -- @param #CTLD self - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - -- @param #string path (Optional) Path where the file is saved. Default is the DCS root installation folder or your "Saved Games\\DCS" folder if the lfs module is desanitized. - -- @param #string filename (Optional) File name for saving. Default is "CTLD__Persist.csv". - - return self - end - - ------------------------------------------------------------------- - -- Helper and User Functions - ------------------------------------------------------------------- - - --- (Internal) Function to get capabilities of a chopper + -- @function [parent=#CTLD] Start -- @param #CTLD self - -- @param Wrapper.Unit#UNIT Unit The unit - -- @return #table Capabilities Table of caps - function CTLD:_GetUnitCapabilities( Unit ) - self:T( self.lid .. " _GetUnitCapabilities" ) - local _unit = Unit -- Wrapper.Unit#UNIT - local unittype = _unit:GetTypeName() - local capabilities = self.UnitTypes[unittype] -- #CTLD.UnitCapabilities - if not capabilities or capabilities == {} then - -- e.g. ["Ka-50"] = {type="Ka-50", crates=false, troops=false, cratelimit = 0, trooplimit = 0}, - capabilities = {} - capabilities.troops = false - capabilities.crates = false - capabilities.cratelimit = 0 - capabilities.trooplimit = 0 - capabilities.type = "generic" - capabilities.length = 20 - end - return capabilities - end - --- (Internal) Function to generate valid UHF Frequencies + --- Triggers the FSM event "Start" after a delay. Starts the CTLD. Initializes parameters and starts event handlers. + -- @function [parent=#CTLD] __Start -- @param #CTLD self - function CTLD:_GenerateUHFrequencies() - self:T( self.lid .. " _GenerateUHFrequencies" ) + -- @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. + + --- Triggers the FSM event "Load". + -- @function [parent=#CTLD] Load + -- @param #CTLD self + + --- Triggers the FSM event "Load" after a delay. + -- @function [parent=#CTLD] __Load + -- @param #CTLD self + -- @param #number delay Delay in seconds. + + --- Triggers the FSM event "Save". + -- @function [parent=#CTLD] Load + -- @param #CTLD self + + --- Triggers the FSM event "Save" after a delay. + -- @function [parent=#CTLD] __Save + -- @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 troops. + -- @return #CTLD self + + --- FSM Function OnAfterTroopsExtracted. + -- @function [parent=#CTLD] OnAfterTroopsExtracted + -- @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 troops. + -- @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 + + --- FSM Function OnAfterCratesRepaired. + -- @function [parent=#CTLD] OnAfterCratesRepaired + -- @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 repaired. + -- @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. + + --- FSM Function OnAfterLoad. + -- @function [parent=#CTLD] OnAfterLoad + -- @param #CTLD self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #string path (Optional) Path where the file is located. Default is the DCS root installation folder or your "Saved Games\\DCS" folder if the lfs module is desanitized. + -- @param #string filename (Optional) File name for loading. Default is "CTLD__Persist.csv". + + --- FSM Function OnAfterSave. + -- @function [parent=#CTLD] OnAfterSave + -- @param #CTLD self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #string path (Optional) Path where the file is saved. Default is the DCS root installation folder or your "Saved Games\\DCS" folder if the lfs module is desanitized. + -- @param #string filename (Optional) File name for saving. Default is "CTLD__Persist.csv". + + return self +end + +------------------------------------------------------------------- +-- Helper and User Functions +------------------------------------------------------------------- + +--- (Internal) Function to get capabilities of a chopper +-- @param #CTLD self +-- @param Wrapper.Unit#UNIT Unit The unit +-- @return #table Capabilities Table of caps +function CTLD:_GetUnitCapabilities(Unit) + self:T(self.lid .. " _GetUnitCapabilities") + local _unit = Unit -- Wrapper.Unit#UNIT + local unittype = _unit:GetTypeName() + local capabilities = self.UnitTypes[unittype] -- #CTLD.UnitCapabilities + if not capabilities or capabilities == {} then + -- e.g. ["Ka-50"] = {type="Ka-50", crates=false, troops=false, cratelimit = 0, trooplimit = 0}, + capabilities = {} + capabilities.troops = false + capabilities.crates = false + capabilities.cratelimit = 0 + capabilities.trooplimit = 0 + capabilities.type = "generic" + capabilities.length = 20 + end + return capabilities +end + + +--- (Internal) Function to generate valid UHF Frequencies +-- @param #CTLD self +function CTLD:_GenerateUHFrequencies() + self:T(self.lid .. " _GenerateUHFrequencies") self.FreeUHFFrequencies = {} - self.FreeUHFFrequencies = UTILS.GenerateUHFrequencies() + self.FreeUHFFrequencies = UTILS.GenerateUHFrequencies() return self - end +end - --- (Internal) Function to generate valid FM Frequencies - -- @param #CTLD self - function CTLD:_GenerateFMFrequencies() - self:T( self.lid .. " _GenerateFMrequencies" ) +--- (Internal) Function to generate valid FM Frequencies +-- @param #CTLD self +function CTLD:_GenerateFMFrequencies() + self:T(self.lid .. " _GenerateFMrequencies") self.FreeFMFrequencies = {} self.FreeFMFrequencies = UTILS.GenerateFMFrequencies() return self - end +end - --- (Internal) Populate table with available VHF beacon frequencies. - -- @param #CTLD self - function CTLD:_GenerateVHFrequencies() - self:T( self.lid .. " _GenerateVHFrequencies" ) - self.FreeVHFFrequencies = {} - self.UsedVHFFrequencies = {} - self.FreeVHFFrequencies = UTILS.GenerateVHFrequencies() - return self - end +--- (Internal) Populate table with available VHF beacon frequencies. +-- @param #CTLD self +function CTLD:_GenerateVHFrequencies() + self:T(self.lid .. " _GenerateVHFrequencies") + self.FreeVHFFrequencies = {} + self.UsedVHFFrequencies = {} + self.FreeVHFFrequencies = UTILS.GenerateVHFrequencies() + return self +end - --- (Internal) Event handler function - -- @param #CTLD self - -- @param Core.Event#EVENTDATA EventData - function CTLD:_EventHandler( EventData ) - 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 - local unitname = event.IniUnitName or "none" - self.Loaded_Cargo[unitname] = nil - self:_RefreshF10Menus() - end - -- Herc support - -- self:T_unit:GetTypeName()) - if _unit:GetTypeName() == "Hercules" and self.enableHercules then - self.Loaded_Cargo[unitname] = nil - self:_RefreshF10Menus() - end - return - elseif event.id == EVENTS.PlayerLeaveUnit then - -- remove from pilot table +--- (Internal) Event handler function +-- @param #CTLD self +-- @param Core.Event#EVENTDATA EventData +function CTLD:_EventHandler(EventData) + 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 local unitname = event.IniUnitName or "none" - self.CtldUnits[unitname] = nil self.Loaded_Cargo[unitname] = nil + self:_RefreshF10Menus() end + -- Herc support + --self:T_unit:GetTypeName()) + if _unit:GetTypeName() == "Hercules" and self.enableHercules then + self.Loaded_Cargo[unitname] = nil + self:_RefreshF10Menus() + end + return + elseif event.id == EVENTS.PlayerLeaveUnit then + -- remove from pilot table + local unitname = event.IniUnitName or "none" + self.CtldUnits[unitname] = nil + self.Loaded_Cargo[unitname] = nil + end + 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 +-- @param Wrapper.Unit#UNIT Unit +-- @param #CTLD_CARGO Cargotype +function CTLD:_LoadTroops(Group, Unit, Cargotype) + self:T(self.lid .. " _LoadTroops") + -- check if we have stock + local instock = Cargotype:GetStock() + local cgoname = Cargotype:GetName() + local cgotype = Cargotype:GetType() + if type(instock) == "number" and tonumber(instock) <= 0 and tonumber(instock) ~= -1 then + -- nothing left over + self:_SendMessage(string.format("Sorry, all %s are gone!", cgoname), 10, false, Group) 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 + -- landed or hovering over load zone? + local grounded = not self:IsUnitInAir(Unit) + local hoverload = self:CanHoverLoad(Unit) + --local dooropen = UTILS.IsLoadingDoorOpen(Unit:GetName()) and self.pilotmustopendoors + -- check if we are in LOAD zone + local inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD) + if not inzone then + inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP) end - - --- (Internal) 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" ) - -- check if we have stock - local instock = Cargotype:GetStock() - local cgoname = Cargotype:GetName() - local cgotype = Cargotype:GetType() - if type( instock ) == "number" and tonumber( instock ) <= 0 and tonumber( instock ) ~= -1 then - -- nothing left over - self:_SendMessage( string.format( "Sorry, all %s are gone!", cgoname ), 10, false, Group ) - return self - end - -- landed or hovering over load zone? - local grounded = not self:IsUnitInAir( Unit ) - local hoverload = self:CanHoverLoad( Unit ) - -- local dooropen = UTILS.IsLoadingDoorOpen(Unit:GetName()) and self.pilotmustopendoors - -- check if we are in LOAD zone - local inzone, zonename, zone, distance = self:IsUnitInZone( Unit, CTLD.CargoZoneType.LOAD ) - if not inzone then - inzone, zonename, zone, distance = self:IsUnitInZone( Unit, CTLD.CargoZoneType.SHIP ) - end - if not inzone then - 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 - self:_SendMessage( "You need to land or hover in position to load!", 10, false, Group ) - if not self.debug then - return self - end - elseif self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen( Unit:GetName() ) then - self:_SendMessage( "You need to open the door(s) to load troops!", 10, false, 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 - -- see if this heli can load troops - local unittype = unit:GetTypeName() - local capabilities = self:_GetUnitCapabilities( Unit ) - 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 - self:_SendMessage( "Sorry, we\'re crammed already!", 10, false, Group ) - return - else - self.CargoCounter = self.CargoCounter + 1 - local loadcargotype = CTLD_CARGO:New( self.CargoCounter, Cargotype.Name, Cargotype.Templates, cgotype, true, true, Cargotype.CratesNeeded, nil, nil, Cargotype.PerCrateMass ) - self:T( { cargotype = loadcargotype } ) - loaded.Troopsloaded = loaded.Troopsloaded + troopsize - table.insert( loaded.Cargo, loadcargotype ) - self.Loaded_Cargo[unitname] = loaded - self:_SendMessage( "Troops boarded!", 10, false, Group ) - self:__TroopsPickedUp( 1, Group, Unit, Cargotype ) - self:_UpdateUnitCargoMass( Unit ) - Cargotype:RemoveStock() - end - return self + if not inzone then + 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 + self:_SendMessage("You need to land or hover in position to load!", 10, false, Group) + if not self.debug then return self end + elseif self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then + self:_SendMessage("You need to open the door(s) to load troops!", 10, false, 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 + -- see if this heli can load troops + local unittype = unit:GetTypeName() + local capabilities = self:_GetUnitCapabilities(Unit) + 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 + self:_SendMessage("Sorry, we\'re crammed already!", 10, false, Group) + return + else + self.CargoCounter = self.CargoCounter + 1 + local loadcargotype = CTLD_CARGO:New(self.CargoCounter, Cargotype.Name, Cargotype.Templates, cgotype, true, true, Cargotype.CratesNeeded,nil,nil,Cargotype.PerCrateMass) + self:T({cargotype=loadcargotype}) + loaded.Troopsloaded = loaded.Troopsloaded + troopsize + table.insert(loaded.Cargo,loadcargotype) + self.Loaded_Cargo[unitname] = loaded + self:_SendMessage("Troops boarded!", 10, false, Group) + self:__TroopsPickedUp(1,Group, Unit, Cargotype) + self:_UpdateUnitCargoMass(Unit) + Cargotype:RemoveStock() + end + return self +end - function CTLD:_FindRepairNearby( Group, Unit, Repairtype ) - self:T( self.lid .. " _FindRepairNearby" ) +function CTLD:_FindRepairNearby(Group, Unit, Repairtype) + self:T(self.lid .. " _FindRepairNearby") local unitcoord = Unit:GetCoordinate() - + -- find nearest group of deployed groups local nearestGroup = nil local nearestGroupIndex = -1 local nearestDistance = 10000 - for k, v in pairs( self.DroppedTroops ) do - local distance = self:_GetDistance( v:GetCoordinate(), unitcoord ) - local unit = v:GetUnit( 1 ) -- Wrapper.Unit#UNIT + for k,v in pairs(self.DroppedTroops) do + local distance = self:_GetDistance(v:GetCoordinate(),unitcoord) + local unit = v:GetUnit(1) -- Wrapper.Unit#UNIT local desc = unit:GetDesc() or nil - -- self:I({desc = desc.attributes}) + --self:I({desc = desc.attributes}) if distance < nearestDistance and distance ~= -1 and not desc.attributes.Infantry then nearestGroup = v nearestGroupIndex = k @@ -1532,136 +1528,126 @@ do -- found one and matching distance? if nearestGroup == nil or nearestDistance > self.EngineerSearch then - self:_SendMessage( "No unit close enough to repair!", 10, false, Group ) + self:_SendMessage("No unit close enough to repair!", 10, false, Group) return nil, nil end - + local groupname = nearestGroup:GetName() - + -- helper to find matching template - local function matchstring( String, Table ) + local function matchstring(String,Table) local match = false - if type( Table ) == "table" then - for _, _name in pairs( Table ) do - if string.find( String, _name ) then + if type(Table) == "table" then + for _,_name in pairs (Table) do + if string.find(String,_name) then match = true break end end else - if type( String ) == "string" then - if string.find( String, Table ) then - match = true - end + if type(String) == "string" then + if string.find(String,Table) then match = true end end - end + end return match end - + -- walk through generics and find matching type local Cargotype = nil - for k, v in pairs( self.Cargo_Crates ) do - -- self:I({groupname,v.Templates}) - if matchstring( groupname, v.Templates ) and matchstring( groupname, Repairtype ) then + for k,v in pairs(self.Cargo_Crates) do + --self:I({groupname,v.Templates}) + if matchstring(groupname,v.Templates) and matchstring(groupname,Repairtype) then Cargotype = v -- #CTLD_CARGO break end end if Cargotype == nil then - -- self:_SendMessage("Can't find a matching group for " .. Repairtype, 10, false, Group) + --self:_SendMessage("Can't find a matching group for " .. Repairtype, 10, false, Group) return nil, nil else return nearestGroup, Cargotype end + +end - end - - --- (Internal) Function to repair an object. - -- @param #CTLD self - -- @param Wrapper.Group#GROUP Group - -- @param Wrapper.Unit#UNIT Unit - -- @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. - -- @param #boolean Engineering If true it is an Engineering repair. - function CTLD:_RepairObjectFromCrates( Group, Unit, Crates, Build, Number, Engineering ) - self:T( self.lid .. " _RepairObjectFromCrates" ) - local build = Build -- -- #CTLD.Buildable - -- self:I({Build=Build}) - local Repairtype = build.Template -- #string - local NearestGroup, CargoType = self:_FindRepairNearby( Group, Unit, Repairtype ) -- Wrapper.Group#GROUP, #CTLD_CARGO - -- self:I({Repairtype=Repairtype, CargoType=CargoType, NearestGroup=NearestGroup}) - if NearestGroup ~= nil then - if self.repairtime < 2 then - self.repairtime = 30 - end -- noob catch - if not Engineering then - self:_SendMessage( string.format( "Repair started using %s taking %d secs", build.Name, self.repairtime ), 10, false, Group ) - end - -- now we can build .... - -- NearestGroup:Destroy(false) - local name = CargoType:GetName() - local required = CargoType:GetCratesNeeded() - local template = CargoType:GetTemplates() - local ctype = CargoType:GetType() - local object = {} -- #CTLD.Buildable - object.Name = CargoType:GetName() - object.Required = required - object.Found = required - object.Template = template - object.CanBuild = true - object.Type = ctype -- #CTLD_CARGO.Enum - self:_CleanUpCrates( Crates, Build, Number ) - local desttimer = TIMER:New( function() - NearestGroup:Destroy( false ) - end, self ) - desttimer:Start( self.repairtime - 1 ) - local buildtimer = TIMER:New( self._BuildObjectFromCrates, self, Group, Unit, object, true, NearestGroup:GetCoordinate() ) - buildtimer:Start( self.repairtime ) - -- self:_BuildObjectFromCrates(Group,Unit,object) - else - if not Engineering then - self:_SendMessage( "Can't repair this unit with " .. build.Name, 10, false, Group ) - else - self:T( "Can't repair this unit with " .. build.Name ) - end +--- (Internal) Function to repair an object. +-- @param #CTLD self +-- @param Wrapper.Group#GROUP Group +-- @param Wrapper.Unit#UNIT Unit +-- @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. +-- @param #boolean Engineering If true it is an Engineering repair. +function CTLD:_RepairObjectFromCrates(Group,Unit,Crates,Build,Number,Engineering) + self:T(self.lid .. " _RepairObjectFromCrates") + local build = Build -- -- #CTLD.Buildable + --self:I({Build=Build}) + local Repairtype = build.Template -- #string + local NearestGroup, CargoType = self:_FindRepairNearby(Group,Unit,Repairtype) -- Wrapper.Group#GROUP, #CTLD_CARGO + --self:I({Repairtype=Repairtype, CargoType=CargoType, NearestGroup=NearestGroup}) + if NearestGroup ~= nil then + if self.repairtime < 2 then self.repairtime = 30 end -- noob catch + if not Engineering then + self:_SendMessage(string.format("Repair started using %s taking %d secs", build.Name, self.repairtime), 10, false, Group) + end + -- now we can build .... + --NearestGroup:Destroy(false) + local name = CargoType:GetName() + local required = CargoType:GetCratesNeeded() + local template = CargoType:GetTemplates() + local ctype = CargoType:GetType() + local object = {} -- #CTLD.Buildable + object.Name = CargoType:GetName() + object.Required = required + object.Found = required + object.Template = template + object.CanBuild = true + object.Type = ctype -- #CTLD_CARGO.Enum + self:_CleanUpCrates(Crates,Build,Number) + local desttimer = TIMER:New(function() NearestGroup:Destroy(false) end, self) + desttimer:Start(self.repairtime - 1) + local buildtimer = TIMER:New(self._BuildObjectFromCrates,self,Group,Unit,object,true,NearestGroup:GetCoordinate()) + buildtimer:Start(self.repairtime) + --self:_BuildObjectFromCrates(Group,Unit,object) + else + if not Engineering then + self:_SendMessage("Can't repair this unit with " .. build.Name, 10, false, Group) + else + self:T("Can't repair this unit with " .. build.Name) end - return self end + return self +end --- (Internal) Function to extract (load from the field) troops into a heli. -- @param #CTLD self -- @param Wrapper.Group#GROUP Group -- @param Wrapper.Unit#UNIT Unit - function CTLD:_ExtractTroops( Group, Unit ) -- #1574 thanks to @bbirchnz! - self:T( self.lid .. " _ExtractTroops" ) + function CTLD:_ExtractTroops(Group, Unit) -- #1574 thanks to @bbirchnz! + self:T(self.lid .. " _ExtractTroops") -- landed or hovering over load zone? - local grounded = not self:IsUnitInAir( Unit ) - local hoverload = self:CanHoverLoad( Unit ) - + local grounded = not self:IsUnitInAir(Unit) + local hoverload = self:CanHoverLoad(Unit) + if not grounded and not hoverload then - self:_SendMessage( "You need to land or hover in position to load!", 10, false, Group ) - if not self.debug then - return self - end + self:_SendMessage("You need to land or hover in position to load!", 10, false, Group) + if not self.debug then return self end end - if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen( Unit:GetName() ) then - self:_SendMessage( "You need to open the door(s) to extract troops!", 10, false, Group ) - if not self.debug then - return self - end + if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then + self:_SendMessage("You need to open the door(s) to extract troops!", 10, false, Group) + if not self.debug then return self end end -- load troops into heli local unit = Unit -- Wrapper.Unit#UNIT local unitname = unit:GetName() -- see if this heli can load troops local unittype = unit:GetTypeName() - local capabilities = self:_GetUnitCapabilities( Unit ) + local capabilities = self:_GetUnitCapabilities(Unit) local cantroops = capabilities.troops -- #boolean local trooplimit = capabilities.trooplimit -- #number local unitcoord = unit:GetCoordinate() - + -- find nearest group of deployed troops local nearestGroup = nil local nearestGroupIndex = -1 @@ -1669,48 +1655,44 @@ do local nearestList = {} local distancekeys = {} local extractdistance = self.CrateDistance * self.ExtractFactor - for k, v in pairs( self.DroppedTroops ) do - local distance = self:_GetDistance( v:GetCoordinate(), unitcoord ) + for k,v in pairs(self.DroppedTroops) do + local distance = self:_GetDistance(v:GetCoordinate(),unitcoord) if distance <= extractdistance and distance ~= -1 then nearestGroup = v nearestGroupIndex = k nearestDistance = distance - table.insert( nearestList, math.floor( distance ), v ) - distancekeys[#distancekeys + 1] = math.floor( distance ) + table.insert(nearestList, math.floor(distance), v) + distancekeys[#distancekeys+1] = math.floor(distance) end end - + if nearestGroup == nil or nearestDistance > extractdistance then - self:_SendMessage( "No units close enough to extract!", 10, false, Group ) + self:_SendMessage("No units close enough to extract!", 10, false, Group) return self end - + -- sort reference keys - table.sort( distancekeys ) - + table.sort(distancekeys) + local secondarygroups = {} - - for i = 1, #distancekeys do + + for i=1,#distancekeys do local nearestGroup = nearestList[distancekeys[i]] - -- find matching cargo type - local groupType = string.match( nearestGroup:GetName(), "(.+)-(.+)$" ) + -- find matching cargo type + local groupType = string.match(nearestGroup:GetName(), "(.+)-(.+)$") local Cargotype = nil - for k, v in pairs( self.Cargo_Troops ) do + for k,v in pairs(self.Cargo_Troops) do local comparison = "" - if type( v.Templates ) == "string" then - comparison = v.Templates - else - comparison = v.Templates[1] - end + if type(v.Templates) == "string" then comparison = v.Templates else comparison = v.Templates[1] end if comparison == groupType then Cargotype = v break end end if Cargotype == nil then - self:_SendMessage( "Can't onboard " .. groupType, 10, false, Group ) + self:_SendMessage("Can't onboard " .. groupType, 10, false, Group) else - + local troopsize = Cargotype:GetCratesNeeded() -- #number -- have we loaded stuff already? local numberonboard = 0 @@ -1725,38 +1707,38 @@ do loaded.Cargo = {} end if troopsize + numberonboard > trooplimit then - self:_SendMessage( "Sorry, we\'re crammed already!", 10, false, Group ) - -- return self + self:_SendMessage("Sorry, we\'re crammed already!", 10, false, Group) + --return self else self.CargoCounter = self.CargoCounter + 1 - local loadcargotype = CTLD_CARGO:New( self.CargoCounter, Cargotype.Name, Cargotype.Templates, Cargotype.CargoType, true, true, Cargotype.CratesNeeded, nil, nil, Cargotype.PerCrateMass ) - self:T( { cargotype = loadcargotype } ) + local loadcargotype = CTLD_CARGO:New(self.CargoCounter, Cargotype.Name, Cargotype.Templates, Cargotype.CargoType, true, true, Cargotype.CratesNeeded,nil,nil,Cargotype.PerCrateMass) + self:T({cargotype=loadcargotype}) loaded.Troopsloaded = loaded.Troopsloaded + troopsize - table.insert( loaded.Cargo, loadcargotype ) + table.insert(loaded.Cargo,loadcargotype) self.Loaded_Cargo[unitname] = loaded - self:_SendMessage( "Troops boarded!", 10, false, Group ) - self:_UpdateUnitCargoMass( Unit ) - self:__TroopsExtracted( 1, Group, Unit, nearestGroup ) - + self:_SendMessage("Troops boarded!", 10, false, Group) + self:_UpdateUnitCargoMass(Unit) + self:__TroopsExtracted(1,Group, Unit, nearestGroup) + -- clean up: - -- table.remove(self.DroppedTroops, nearestGroupIndex) - if type( Cargotype.Templates ) == "table" and Cargotype.Templates[2] then - -- self:I("*****This CargoType has multiple templates: "..Cargotype.Name) - for _, _key in pairs( Cargotype.Templates ) do - table.insert( secondarygroups, _key ) + --table.remove(self.DroppedTroops, nearestGroupIndex) + if type(Cargotype.Templates) == "table" and Cargotype.Templates[2] then + --self:I("*****This CargoType has multiple templates: "..Cargotype.Name) + for _,_key in pairs (Cargotype.Templates) do + table.insert(secondarygroups,_key) end end - nearestGroup:Destroy( false ) + nearestGroup:Destroy(false) end end end -- clean up secondary groups - for _, _name in pairs( secondarygroups ) do - for _, _group in pairs( nearestList ) do + for _,_name in pairs(secondarygroups) do + for _,_group in pairs(nearestList) do if _group and _group:IsAlive() then - local groupname = string.match( _group:GetName(), "(.+)-(.+)$" ) + local groupname = string.match(_group:GetName(), "(.+)-(.+)$") if _name == groupname then - _group:Destroy( false ) + _group:Destroy(false) end end end @@ -1764,1734 +1746,1712 @@ do self:CleanDroppedTroops() return self end - - --- (Internal) 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" ) - if not drop then - local cgoname = Cargo:GetName() - -- check if we have stock - local instock = Cargo:GetStock() - if type( instock ) == "number" and tonumber( instock ) <= 0 and tonumber( instock ) ~= -1 then - -- nothing left over - self:_SendMessage( string.format( "Sorry, we ran out of %s", cgoname ), 10, false, Group ) - return self - end - end - -- check if we are in LOAD zone - local inzone = false - local drop = drop or false - local ship = nil - local width = 20 - if not drop then - inzone = self:IsUnitInZone( Unit, CTLD.CargoZoneType.LOAD ) - if not inzone then - inzone, ship, zone, distance, width = self:IsUnitInZone( Unit, CTLD.CargoZoneType.SHIP ) - end - else - if self.dropcratesanywhere then -- #1570 - inzone = true - else - inzone = self:IsUnitInZone( Unit, CTLD.CargoZoneType.DROP ) - end - end - - if not inzone then - self:_SendMessage( "You are not close enough to a logistics zone!", 10, false, Group ) - if not self.debug then - return self - end - end - - -- avoid crate spam - local capabilities = self:_GetUnitCapabilities( Unit ) -- #CTLD.UnitCapabilities - local canloadcratesno = capabilities.cratelimit - local loaddist = self.CrateDistance or 35 - local nearcrates, numbernearby = self:_FindCratesNearby( Group, Unit, loaddist ) - if numbernearby >= canloadcratesno and not drop then - self:_SendMessage( "There are enough crates nearby already! Take care of those first!", 10, false, Group ) + +--- (Internal) 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") + if not drop then + local cgoname = Cargo:GetName() + -- check if we have stock + local instock = Cargo:GetStock() + if type(instock) == "number" and tonumber(instock) <= 0 and tonumber(instock) ~= -1 then + -- nothing left over + self:_SendMessage(string.format("Sorry, we ran out of %s", cgoname), 10, false, Group) return self end - -- spawn crates in front of helicopter - local IsHerc = self:IsHercules( Unit ) -- Herc - local cargotype = Cargo -- Ops.CTLD#CTLD_CARGO - local number = number or cargotype:GetCratesNeeded() -- #number - local cratesneeded = cargotype:GetCratesNeeded() -- #number - local cratename = cargotype:GetName() - local cratetemplate = "Container" -- #string - local cgotype = cargotype:GetType() - local cgomass = cargotype:GetMass() - local isstatic = false - if cgotype == CTLD_CARGO.Enum.STATIC then - cratetemplate = cargotype:GetTemplates() - isstatic = true + end + -- check if we are in LOAD zone + local inzone = false + local drop = drop or false + local ship = nil + local width = 20 + if not drop then + inzone = self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD) + if not inzone then + inzone, ship, zone, distance, width = self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP) end - -- get position and heading of heli - local position = Unit:GetCoordinate() - local heading = Unit:GetHeading() + 1 - local height = Unit:GetHeight() - local droppedcargo = {} - local cratedistance = 0 - local rheading = 0 - local angleOffNose = 0 - local addon = 0 - if IsHerc then - -- spawn behind the Herc - addon = 180 + else + if self.dropcratesanywhere then -- #1570 + inzone = true + else + inzone = self:IsUnitInZone(Unit,CTLD.CargoZoneType.DROP) end - -- loop crates needed - for i = 1, number do - local cratealias = string.format( "%s-%d", cratetemplate, math.random( 1, 100000 ) ) - if not self.placeCratesAhead then - cratedistance = (i - 1) * 2.5 + capabilities.length - if cratedistance > self.CrateDistance then - cratedistance = self.CrateDistance - end - -- altered heading logic - -- DONE: right standard deviation? - rheading = UTILS.RandomGaussian( 0, 30, -90, 90, 100 ) - rheading = math.fmod( (heading + rheading + addon), 360 ) - else - local initialSpacing = IsHerc and 16 or 12 -- initial spacing of the first crates - local crateSpacing = 4 -- further spacing of remaining crates - local lateralSpacing = 4 -- lateral spacing of crates - local nrSideBySideCrates = 3 -- number of crates that are placed side-by-side - - if cratesneeded == 1 then - -- single crate needed spawns straight ahead - cratedistance = initialSpacing - rheading = heading - else - if (i - 1) % nrSideBySideCrates == 0 then - cratedistance = i == 1 and initialSpacing or cratedistance + crateSpacing - angleOffNose = math.ceil( math.deg( math.atan( lateralSpacing / cratedistance ) ) ) - rheading = heading - angleOffNose - else - rheading = rheading + angleOffNose - end - end - end - local cratecoord = position:Translate( cratedistance, rheading ) - local cratevec2 = cratecoord:GetVec2() - self.CrateCounter = self.CrateCounter + 1 - local basetype = "container_cargo" - if isstatic then - basetype = cratetemplate - end - if type( ship ) == "string" then - self:T( "Spawning on ship " .. ship ) - local Ship = UNIT:FindByName( ship ) - local shipcoord = Ship:GetCoordinate() - local unitcoord = Unit:GetCoordinate() - local dist = shipcoord:Get2DDistance( unitcoord ) - dist = dist - (20 + math.random( 1, 10 )) - local width = width / 2 - local Offy = math.random( -width, width ) - self.Spawned_Crates[self.CrateCounter] = SPAWNSTATIC:NewFromType( basetype, "Cargos", self.cratecountry ) -- :InitCoordinate(cratecoord) - :InitCargoMass( cgomass ) - :InitCargo( self.enableslingload ) - :InitLinkToUnit( Ship, dist, Offy, 0 ) - :Spawn( 270, cratealias ) - else - self.Spawned_Crates[self.CrateCounter] = SPAWNSTATIC:NewFromType( basetype, "Cargos", self.cratecountry ) - :InitCoordinate( cratecoord ) - :InitCargoMass( cgomass ) - :InitCargo( self.enableslingload ) -- :InitLinkToUnit(Unit,OffsetX,OffsetY,OffsetAngle) - :Spawn( 270, cratealias ) - end - 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, cargotype.PerCrateMass ) - table.insert( droppedcargo, realcargo ) - else - realcargo = CTLD_CARGO:New( self.CargoCounter, cratename, templ, sorte, false, false, cratesneeded, self.Spawned_Crates[self.CrateCounter], true, cargotype.PerCrateMass ) - Cargo:RemoveStock() - 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 - self:_SendMessage( text, 10, false, Group ) - return self + end + + if not inzone then + self:_SendMessage("You are not close enough to a logistics zone!", 10, false, Group) + if not self.debug then return self end end - --- (Internal) Inject crates and static cargo objects. - -- @param #CTLD self - -- @param Core.Zone#ZONE Zone Zone to spawn in. - -- @param #CTLD_CARGO Cargo The cargo type to spawn. - -- @param #boolean RandomCoord Randomize coordinate. - -- @return #CTLD self - function CTLD:InjectStatics( Zone, Cargo, RandomCoord ) - self:T( self.lid .. " InjectStatics" ) - local cratecoord = Zone:GetCoordinate() - if RandomCoord then - cratecoord = Zone:GetRandomCoordinate( 5, 20 ) + -- avoid crate spam + local capabilities = self:_GetUnitCapabilities(Unit) -- #CTLD.UnitCapabilities + local canloadcratesno = capabilities.cratelimit + local loaddist = self.CrateDistance or 35 + local nearcrates, numbernearby = self:_FindCratesNearby(Group,Unit,loaddist) + if numbernearby >= canloadcratesno and not drop then + self:_SendMessage("There are enough crates nearby already! Take care of those first!", 10, false, Group) + return self + end + -- spawn crates in front of helicopter + local IsHerc = self:IsHercules(Unit) -- Herc + local cargotype = Cargo -- Ops.CTLD#CTLD_CARGO + local number = number or cargotype:GetCratesNeeded() --#number + local cratesneeded = cargotype:GetCratesNeeded() --#number + local cratename = cargotype:GetName() + local cratetemplate = "Container"-- #string + local cgotype = cargotype:GetType() + local cgomass = cargotype:GetMass() + local isstatic = false + if cgotype == CTLD_CARGO.Enum.STATIC then + cratetemplate = cargotype:GetTemplates() + isstatic = true + end + -- get position and heading of heli + local position = Unit:GetCoordinate() + local heading = Unit:GetHeading() + 1 + local height = Unit:GetHeight() + local droppedcargo = {} + local cratedistance = 0 + local rheading = 0 + local angleOffNose = 0 + local addon = 0 + if IsHerc then + -- spawn behind the Herc + addon = 180 + end + -- loop crates needed + for i=1,number do + local cratealias = string.format("%s-%d", cratetemplate, math.random(1,100000)) + if not self.placeCratesAhead then + cratedistance = (i-1)*2.5 + capabilities.length + if cratedistance > self.CrateDistance then cratedistance = self.CrateDistance end + -- altered heading logic + -- DONE: right standard deviation? + rheading = UTILS.RandomGaussian(0,30,-90,90,100) + rheading = math.fmod((heading + rheading + addon), 360) + else + local initialSpacing = IsHerc and 16 or 12 -- initial spacing of the first crates + local crateSpacing = 4 -- further spacing of remaining crates + local lateralSpacing = 4 -- lateral spacing of crates + local nrSideBySideCrates = 3 -- number of crates that are placed side-by-side + + if cratesneeded == 1 then + -- single crate needed spawns straight ahead + cratedistance = initialSpacing + rheading = heading + else + if (i - 1) % nrSideBySideCrates == 0 then + cratedistance = i == 1 and initialSpacing or cratedistance + crateSpacing + angleOffNose = math.ceil(math.deg(math.atan(lateralSpacing / cratedistance))) + rheading = heading - angleOffNose + else + rheading = rheading + angleOffNose + end + end end - local surface = cratecoord:GetSurfaceType() - if surface == land.SurfaceType.WATER then - return self - end - local cargotype = Cargo -- #CTLD_CARGO - -- local number = 1 - local cratesneeded = cargotype:GetCratesNeeded() -- #number - local cratetemplate = "Container" -- #string - local cratealias = string.format( "%s-%d", cratetemplate, math.random( 1, 100000 ) ) - local cratename = cargotype:GetName() - local cgotype = cargotype:GetType() - local cgomass = cargotype:GetMass() - local isstatic = false - if cgotype == CTLD_CARGO.Enum.STATIC then - cratetemplate = cargotype:GetTemplates() - isstatic = true - end - local basetype = "container_cargo" + local cratecoord = position:Translate(cratedistance,rheading) + local cratevec2 = cratecoord:GetVec2() + self.CrateCounter = self.CrateCounter + 1 + local basetype = self.basetype or "container_cargo" if isstatic then basetype = cratetemplate end - self.CrateCounter = self.CrateCounter + 1 - self.Spawned_Crates[self.CrateCounter] = SPAWNSTATIC:NewFromType( basetype, "Cargos", self.cratecountry ) - :InitCargoMass( cgomass ) - :InitCargo( self.enableslingload ) - :InitCoordinate( cratecoord ) - :Spawn( 270, cratealias ) + if type(ship) == "string" then + self:T("Spawning on ship "..ship) + local Ship = UNIT:FindByName(ship) + local shipcoord = Ship:GetCoordinate() + local unitcoord = Unit:GetCoordinate() + local dist = shipcoord:Get2DDistance(unitcoord) + dist = dist - (20 + math.random(1,10)) + local width = width / 2 + local Offy = math.random(-width,width) + self.Spawned_Crates[self.CrateCounter] = SPAWNSTATIC:NewFromType(basetype,"Cargos",self.cratecountry) + --:InitCoordinate(cratecoord) + :InitCargoMass(cgomass) + :InitCargo(self.enableslingload) + :InitLinkToUnit(Ship,dist,Offy,0) + :Spawn(270,cratealias) + else + self.Spawned_Crates[self.CrateCounter] = SPAWNSTATIC:NewFromType(basetype,"Cargos",self.cratecountry) + :InitCoordinate(cratecoord) + :InitCargoMass(cgomass) + :InitCargo(self.enableslingload) + --:InitLinkToUnit(Unit,OffsetX,OffsetY,OffsetAngle) + :Spawn(270,cratealias) + end local templ = cargotype:GetTemplates() local sorte = cargotype:GetType() self.CargoCounter = self.CargoCounter + 1 - cargotype.Positionable = self.Spawned_Crates[self.CrateCounter] - table.insert( self.Spawned_Cargo, cargotype ) - return self - end - - --- (User) Inject static cargo objects. - -- @param #CTLD self - -- @param Core.Zone#ZONE Zone Zone to spawn in. Will be a somewhat random coordinate. - -- @param #string Template Unit(!) name of the static cargo object to be used as template. - -- @param #number Mass Mass of the static in kg. - -- @return #CTLD self - function CTLD:InjectStaticFromTemplate( Zone, Template, Mass ) - self:T( self.lid .. " InjectStaticFromTemplate" ) - local cargotype = self:GetStaticsCargoFromTemplate( Template, Mass ) -- #CTLD_CARGO - self:InjectStatics( Zone, cargotype, true ) - return self - end - - --- (Internal) 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 35 - 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 - local dropped = entry:WasDropped() - if dropped then - text:Add( string.format( "Dropped crate for %s, %dkg", name, entry.PerCrateMass ) ) - else - text:Add( string.format( "Crate for %s, %dkg", name, entry.PerCrateMass ) ) - end - end - if text:GetCount() == 1 then - text:Add( " N O N E" ) - end - text:Add( "------------------------------------------------------------" ) - self:_SendMessage( text:Text(), 30, true, _group ) + local realcargo = nil + if drop then + realcargo = CTLD_CARGO:New(self.CargoCounter,cratename,templ,sorte,true,false,cratesneeded,self.Spawned_Crates[self.CrateCounter],true,cargotype.PerCrateMass) + table.insert(droppedcargo,realcargo) else - self:_SendMessage( string.format( "No (loadable) crates within %d meters!", finddist ), 10, false, _group ) + realcargo = CTLD_CARGO:New(self.CargoCounter,cratename,templ,sorte,false,false,cratesneeded,self.Spawned_Crates[self.CrateCounter],true,cargotype.PerCrateMass) + Cargo:RemoveStock() 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 + self:_SendMessage(text, 10, false, Group) + return self +end + +--- (Internal) Inject crates and static cargo objects. +-- @param #CTLD self +-- @param Core.Zone#ZONE Zone Zone to spawn in. +-- @param #CTLD_CARGO Cargo The cargo type to spawn. +-- @param #boolean RandomCoord Randomize coordinate. +-- @return #CTLD self +function CTLD:InjectStatics(Zone, Cargo, RandomCoord) + self:T(self.lid .. " InjectStatics") + local cratecoord = Zone:GetCoordinate() + if RandomCoord then + cratecoord = Zone:GetRandomCoordinate(5,20) + end + local surface = cratecoord:GetSurfaceType() + if surface == land.SurfaceType.WATER then return self end + local cargotype = Cargo -- #CTLD_CARGO + --local number = 1 + local cratesneeded = cargotype:GetCratesNeeded() --#number + local cratetemplate = "Container"-- #string + local cratealias = string.format("%s-%d", cratetemplate, math.random(1,100000)) + local cratename = cargotype:GetName() + local cgotype = cargotype:GetType() + local cgomass = cargotype:GetMass() + local isstatic = false + if cgotype == CTLD_CARGO.Enum.STATIC then + cratetemplate = cargotype:GetTemplates() + isstatic = true + end + local basetype = self.basetype or "container_cargo" + if isstatic then + basetype = cratetemplate + end + self.CrateCounter = self.CrateCounter + 1 + self.Spawned_Crates[self.CrateCounter] = SPAWNSTATIC:NewFromType(basetype,"Cargos",self.cratecountry) + :InitCargoMass(cgomass) + :InitCargo(self.enableslingload) + :InitCoordinate(cratecoord) + :Spawn(270,cratealias) + local templ = cargotype:GetTemplates() + local sorte = cargotype:GetType() + self.CargoCounter = self.CargoCounter + 1 + cargotype.Positionable = self.Spawned_Crates[self.CrateCounter] + table.insert(self.Spawned_Cargo, cargotype) + return self +end - --- (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 - -- @return #number Distance in meters - function CTLD:_GetDistance( _point1, _point2 ) - self:T( self.lid .. " _GetDistance" ) - if _point1 and _point2 then - local distance1 = _point1:Get2DDistance( _point2 ) - local distance2 = _point1:DistanceFromPointVec2( _point2 ) - -- self:I({dist1=distance1, dist2=distance2}) - if distance1 and type( distance1 ) == "number" then - return distance1 - elseif distance2 and type( distance2 ) == "number" then - return distance2 +--- (User) Inject static cargo objects. +-- @param #CTLD self +-- @param Core.Zone#ZONE Zone Zone to spawn in. Will be a somewhat random coordinate. +-- @param #string Template Unit(!) name of the static cargo object to be used as template. +-- @param #number Mass Mass of the static in kg. +-- @return #CTLD self +function CTLD:InjectStaticFromTemplate(Zone, Template, Mass) + self:T(self.lid .. " InjectStaticFromTemplate") + local cargotype = self:GetStaticsCargoFromTemplate(Template,Mass) -- #CTLD_CARGO + self:InjectStatics(Zone,cargotype,true) + return self +end + +--- (Internal) 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 35 + 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 + local dropped = entry:WasDropped() + if dropped then + text:Add(string.format("Dropped crate for %s, %dkg",name, entry.PerCrateMass)) else - self:E( "*****Cannot calculate distance!" ) - self:E( { _point1, _point2 } ) - return -1 + text:Add(string.format("Crate for %s, %dkg",name, entry.PerCrateMass)) end + end + if text:GetCount() == 1 then + text:Add(" N O N E") + end + text:Add("------------------------------------------------------------") + self:_SendMessage(text:Text(), 30, true, _group) + else + self:_SendMessage(string.format("No (loadable) crates within %d meters!",finddist), 10, false, _group) + end + return self +end + +--- (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 +-- @return #number Distance in meters +function CTLD:_GetDistance(_point1, _point2) + self:T(self.lid .. " _GetDistance") + if _point1 and _point2 then + local distance1 = _point1:Get2DDistance(_point2) + local distance2 = _point1:DistanceFromPointVec2(_point2) + --self:I({dist1=distance1, dist2=distance2}) + if distance1 and type(distance1) == "number" then + return distance1 + elseif distance2 and type(distance2) == "number" then + return distance2 else - self:E( "******Cannot calculate distance!" ) - self:E( { _point1, _point2 } ) + self:E("*****Cannot calculate distance!") + self:E({_point1,_point2}) return -1 end + else + self:E("******Cannot calculate distance!") + self:E({_point1,_point2}) + return -1 end +end - --- (Internal) 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 +--- (Internal) 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 - return found, index end + return found, index +end - --- (Internal) 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" ) +--- (Internal) 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:_GetUnitCapabilities( Unit ) -- #CTLD.UnitCapabilities - -- 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 - self:_SendMessage( "Sorry this chopper cannot carry crates!", 10, false, Group ) - elseif self.forcehoverload and not canhoverload then - self:_SendMessage( "Hover over the crates to pick them up!", 10, false, Group ) - elseif not grounded and not canhoverload then - self:_SendMessage( "Land or hover over the crates to pick them up!", 10, false, Group ) + 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:_GetUnitCapabilities(Unit) -- #CTLD.UnitCapabilities + --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 + self:_SendMessage("Sorry this chopper cannot carry crates!", 10, false, Group) + elseif self.forcehoverload and not canhoverload then + self:_SendMessage("Hover over the crates to pick them up!", 10, false, Group) + elseif not grounded and not canhoverload then + self:_SendMessage("Land or hover over the crates to pick them up!", 10, false, Group) + else + -- have we loaded stuff already? + local numberonboard = 0 + local massonboard = 0 + local loaded = {} + if self.Loaded_Cargo[unitname] then + loaded = self.Loaded_Cargo[unitname] -- #CTLD.LoadedCargo + numberonboard = loaded.Cratesloaded or 0 else - -- have we loaded stuff already? - local numberonboard = 0 - local massonboard = 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 35 - local nearcrates, number = self:_FindCratesNearby( Group, Unit, finddist ) -- #table - if number == 0 and self.hoverautoloading then - return self -- exit - elseif number == 0 then - self:_SendMessage( "Sorry no loadable crates nearby!", 10, false, Group ) - return self -- exit - elseif numberonboard == cratelimit then - self:_SendMessage( "Sorry no fully loaded!", 10, false, Group ) - return self -- exit - else - -- go through crates and load - local capacity = cratelimit - numberonboard - local crateidsloaded = {} - local loops = 0 - while loaded.Cratesloaded < cratelimit and loops < number do - loops = loops + 1 - local crateind = 0 - -- get crate with largest index - for _ind, _crate in pairs( nearcrates ) do - if self.allowcratepickupagain then - if _crate:GetID() > crateind and _crate.Positionable ~= nil then - crateind = _crate:GetID() - end - else - if not _crate:HasMoved() and _crate:WasDropped() and _crate:GetID() > crateind then - crateind = _crate:GetID() - end + loaded = {} -- #CTLD.LoadedCargo + loaded.Troopsloaded = 0 + loaded.Cratesloaded = 0 + loaded.Cargo = {} + end + -- get nearby crates + local finddist = self.CrateDistance or 35 + local nearcrates,number = self:_FindCratesNearby(Group,Unit,finddist) -- #table + if number == 0 and self.hoverautoloading then + return self -- exit + elseif number == 0 then + self:_SendMessage("Sorry no loadable crates nearby!", 10, false, Group) + return self -- exit + elseif numberonboard == cratelimit then + self:_SendMessage("Sorry no fully loaded!", 10, false, Group) + return self -- exit + else + -- go through crates and load + local capacity = cratelimit - numberonboard + local crateidsloaded = {} + local loops = 0 + while loaded.Cratesloaded < cratelimit and loops < number do + loops = loops + 1 + local crateind = 0 + -- get crate with largest index + for _ind,_crate in pairs (nearcrates) do + if self.allowcratepickupagain then + if _crate:GetID() > crateind and _crate.Positionable ~= nil then + crateind = _crate:GetID() + end + else + if not _crate:HasMoved() and _crate:WasDropped() and _crate:GetID() > crateind then + 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 ) - crate:SetWasDropped( false ) - table.insert( loaded.Cargo, crate ) - table.insert( crateidsloaded, crate:GetID() ) - -- destroy crate - crate:GetPositionable():Destroy( false ) - crate.Positionable = nil - self:_SendMessage( string.format( "Crate ID %d for %s loaded!", crate:GetID(), crate:GetName() ), 10, false, Group ) - table.remove( nearcrates, crate:GetID() ) - self:__CratesPickedUp( 1, Group, Unit, crate ) - end end - self.Loaded_Cargo[unitname] = loaded - self:_UpdateUnitCargoMass( Unit ) - -- clean up real world crates - self:_CleanupTrackedCrates( crateidsloaded ) + -- load one if we found one + if crateind > 0 then + local crate = nearcrates[crateind] -- #CTLD_CARGO + loaded.Cratesloaded = loaded.Cratesloaded + 1 + crate:SetHasMoved(true) + crate:SetWasDropped(false) + table.insert(loaded.Cargo, crate) + table.insert(crateidsloaded,crate:GetID()) + -- destroy crate + crate:GetPositionable():Destroy(false) + crate.Positionable = nil + self:_SendMessage(string.format("Crate ID %d for %s loaded!",crate:GetID(),crate:GetName()), 10, false, Group) + table.remove(nearcrates,crate:GetID()) + self:__CratesPickedUp(1, Group, Unit, crate) + end end + self.Loaded_Cargo[unitname] = loaded + self:_UpdateUnitCargoMass(Unit) + -- clean up real world crates + self:_CleanupTrackedCrates(crateidsloaded) end - return self end + return self +end - --- (Internal) Function to clean up tracked cargo crates - function CTLD:_CleanupTrackedCrates( crateIdsToRemove ) - local existingcrates = self.Spawned_Cargo -- #table - local newexcrates = {} - for _, _crate in pairs( existingcrates ) do - local excrate = _crate -- #CTLD_CARGO - local ID = excrate:GetID() - local keep = true - for _, _ID in pairs( crateIdsToRemove ) do - if ID == _ID then - keep = false - end - end - -- remove destroyed crates here too - local static = _crate:GetPositionable() -- Wrapper.Static#STATIC -- crates - if not static or not static:IsAlive() then +--- (Internal) Function to clean up tracked cargo crates +function CTLD:_CleanupTrackedCrates(crateIdsToRemove) + local existingcrates = self.Spawned_Cargo -- #table + local newexcrates = {} + for _,_crate in pairs(existingcrates) do + local excrate = _crate -- #CTLD_CARGO + local ID = excrate:GetID() + local keep = true + for _,_ID in pairs(crateIdsToRemove) do + if ID == _ID then keep = false end - if keep then - table.insert( newexcrates, _crate ) - end end - self.Spawned_Cargo = nil - self.Spawned_Cargo = newexcrates - return self - end - - --- (Internal) Function to get current loaded mass - -- @param #CTLD self - -- @param Wrapper.Unit#UNIT Unit - -- @return #number mass in kgs - function CTLD:_GetUnitCargoMass( Unit ) - self:T( self.lid .. " _GetUnitCargoMass" ) - local unitname = Unit:GetName() - local loadedcargo = self.Loaded_Cargo[unitname] or {} -- #CTLD.LoadedCargo - local loadedmass = 0 -- #number - if self.Loaded_Cargo[unitname] then - local cargotable = loadedcargo.Cargo or {} -- #table - for _, _cargo in pairs( cargotable ) do - local cargo = _cargo -- #CTLD_CARGO - local type = cargo:GetType() -- #CTLD_CARGO.Enum - if (type == CTLD_CARGO.Enum.TROOPS or type == CTLD_CARGO.Enum.ENGINEERS) and not cargo:WasDropped() then - loadedmass = loadedmass + (cargo.PerCrateMass * cargo:GetCratesNeeded()) - end - if type ~= CTLD_CARGO.Enum.TROOPS and type ~= CTLD_CARGO.Enum.ENGINEERS and not cargo:WasDropped() then - loadedmass = loadedmass + cargo.PerCrateMass - end - end + -- remove destroyed crates here too + local static = _crate:GetPositionable() -- Wrapper.Static#STATIC -- crates + if not static or not static:IsAlive() then + keep = false end - return loadedmass - end - - --- (Internal) Function to calculate and set Unit internal cargo mass - -- @param #CTLD self - -- @param Wrapper.Unit#UNIT Unit - function CTLD:_UpdateUnitCargoMass( Unit ) - self:T( self.lid .. " _UpdateUnitCargoMass" ) - local calculatedMass = self:_GetUnitCargoMass( Unit ) - Unit:SetUnitInternalCargo( calculatedMass ) - -- local report = REPORT:New("Loadmaster report") - -- report:Add("Carrying " .. calculatedMass .. "Kg") - -- self:_SendMessage(report:Text(),10,false,Unit:GetGroup()) - return self - end - - --- (Internal) 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:_GetUnitCapabilities( Unit ) -- #CTLD.UnitCapabilities - local trooplimit = capabilities.trooplimit -- #boolean - local cratelimit = capabilities.cratelimit -- #number - local loadedcargo = self.Loaded_Cargo[unitname] or {} -- #CTLD.LoadedCargo - local loadedmass = self:_GetUnitCargoMass( Unit ) -- #number - 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 or type == CTLD_CARGO.Enum.ENGINEERS) and (not cargo:WasDropped() or self.allowcratepickupagain) 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 and type ~= CTLD_CARGO.Enum.ENGINEERS) and (not cargo:WasDropped() or self.allowcratepickupagain) 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( "------------------------------------------------------------" ) - report:Add( "Total Mass: " .. loadedmass .. " kg" ) - local text = report:Text() - self:_SendMessage( text, 30, true, Group ) - else - self:_SendMessage( string.format( "Nothing loaded!\nTroop limit: %d | Crate limit %d", trooplimit, cratelimit ), 10, false, Group ) - end - return self - end - - --- (Internal) Function to list loaded cargo. - -- @param #CTLD self - -- @param Wrapper.Group#GROUP Group - -- @param Wrapper.Unit#UNIT Unit - -- @return #CTLD self - function CTLD:_ListInventory( Group, Unit ) - self:T( self.lid .. " _ListInventory" ) - local unitname = Unit:GetName() - local unittype = Unit:GetTypeName() - local cgotypes = self.Cargo_Crates - local trptypes = self.Cargo_Troops - local stctypes = self.Cargo_Statics - - local function countcargo( cgotable ) - local counter = 0 - for _, _cgo in pairs( cgotable ) do - counter = counter + 1 - end - return counter - end - - local crateno = countcargo( cgotypes ) - local troopno = countcargo( trptypes ) - local staticno = countcargo( stctypes ) - - if (crateno > 0 or troopno > 0 or staticno > 0) then - - local report = REPORT:New( "Inventory Sheet" ) - report:Add( "------------------------------------------------------------" ) - report:Add( string.format( "Troops: %d, Cratetypes: %d", troopno, crateno + staticno ) ) - report:Add( "------------------------------------------------------------" ) - report:Add( " -- TROOPS --" ) - for _, _cargo in pairs( trptypes ) do - local cargo = _cargo -- #CTLD_CARGO - local type = cargo:GetType() -- #CTLD_CARGO.Enum - if (type == CTLD_CARGO.Enum.TROOPS or type == CTLD_CARGO.Enum.ENGINEERS) and not cargo:WasDropped() then - local stockn = cargo:GetStock() - local stock = "none" - if stockn == -1 then - stock = "unlimited" - elseif stockn > 0 then - stock = tostring( stockn ) - end - report:Add( string.format( "Unit: %s | Soldiers: %d | Stock: %s", cargo:GetName(), cargo:GetCratesNeeded(), stock ) ) - 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( cgotypes ) do - local cargo = _cargo -- #CTLD_CARGO - local type = cargo:GetType() -- #CTLD_CARGO.Enum - if (type ~= CTLD_CARGO.Enum.TROOPS and type ~= CTLD_CARGO.Enum.ENGINEERS) and not cargo:WasDropped() then - local stockn = cargo:GetStock() - local stock = "none" - if stockn == -1 then - stock = "unlimited" - elseif stockn > 0 then - stock = tostring( stockn ) - end - report:Add( string.format( "Type: %s | Crates per Set: %d | Stock: %s", cargo:GetName(), cargo:GetCratesNeeded(), stock ) ) - cratecount = cratecount + 1 - end - end - -- Statics - for _, _cargo in pairs( stctypes ) do - local cargo = _cargo -- #CTLD_CARGO - local type = cargo:GetType() -- #CTLD_CARGO.Enum - if (type == CTLD_CARGO.Enum.STATIC) and not cargo:WasDropped() then - local stockn = cargo:GetStock() - local stock = "none" - if stockn == -1 then - stock = "unlimited" - elseif stockn > 0 then - stock = tostring( stockn ) - end - report:Add( string.format( "Type: %s | Stock: %s", cargo:GetName(), stock ) ) - cratecount = cratecount + 1 - end - end - if cratecount == 0 then - report:Add( " N O N E" ) - end - local text = report:Text() - self:_SendMessage( text, 30, true, Group ) - else - self:_SendMessage( string.format( "Nothing in stock!" ), 10, false, Group ) - end - 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 + if keep then + table.insert(newexcrates,_crate) end end + self.Spawned_Cargo = nil + self.Spawned_Cargo = newexcrates + return self +end - --- (Internal) 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 canunload = true - if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen( Unit:GetName() ) then - self:_SendMessage( "You need to open the door(s) to unload troops!", 10, false, Group ) - if not self.debug then - return self +--- (Internal) Function to get current loaded mass +-- @param #CTLD self +-- @param Wrapper.Unit#UNIT Unit +-- @return #number mass in kgs +function CTLD:_GetUnitCargoMass(Unit) + self:T(self.lid .. " _GetUnitCargoMass") + local unitname = Unit:GetName() + local loadedcargo = self.Loaded_Cargo[unitname] or {} -- #CTLD.LoadedCargo + local loadedmass = 0 -- #number + if self.Loaded_Cargo[unitname] then + local cargotable = loadedcargo.Cargo or {} -- #table + for _,_cargo in pairs(cargotable) do + local cargo = _cargo -- #CTLD_CARGO + local type = cargo:GetType() -- #CTLD_CARGO.Enum + if (type == CTLD_CARGO.Enum.TROOPS or type == CTLD_CARGO.Enum.ENGINEERS) and not cargo:WasDropped() then + loadedmass = loadedmass + (cargo.PerCrateMass * cargo:GetCratesNeeded()) + end + if type ~= CTLD_CARGO.Enum.TROOPS and type ~= CTLD_CARGO.Enum.ENGINEERS and not cargo:WasDropped() then + loadedmass = loadedmass + cargo.PerCrateMass end end - local inzone, zonename, zone, distance = self:IsUnitInZone( Unit, CTLD.CargoZoneType.LOAD ) - if not inzone then - inzone, zonename, zone, distance = self:IsUnitInZone( Unit, CTLD.CargoZoneType.SHIP ) - end - if inzone then - droppingatbase = true - 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 - 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 or type == CTLD_CARGO.Enum.ENGINEERS) and not cargo:WasDropped() then - -- unload troops - local name = cargo:GetName() or "none" - local temptable = cargo:GetTemplates() or {} - local position = Group:GetCoordinate() - 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 ) ) - self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias( _template, alias ) - :InitRandomizeUnits( true, 20, 2 ) - :InitDelayOff() - :SpawnFromVec2( randomcoord ) - if self.movetroopstowpzone and type ~= CTLD_CARGO.Enum.ENGINEERS then - self:_MoveGroupToZone( self.DroppedTroops[self.TroopCounter] ) - end - end -- template loop - cargo:SetWasDropped( true ) - -- engineering group? - -- self:I("Dropped Troop Type: "..type) - if type == CTLD_CARGO.Enum.ENGINEERS then - self.Engineers = self.Engineers + 1 - local grpname = self.DroppedTroops[self.TroopCounter]:GetName() - self.EngineersInField[self.Engineers] = CTLD_ENGINEERING:New( name, grpname ) - self:_SendMessage( string.format( "Dropped Engineers %s into action!", name ), 10, false, Group ) - else - self:_SendMessage( string.format( "Dropped Troops %s into action!", name ), 10, false, Group ) - end - self:__TroopsDeployed( 1, Group, Unit, self.DroppedTroops[self.TroopCounter] ) - end -- if type end - end -- cargotable loop - else -- droppingatbase - self:_SendMessage( "Troops have returned to base!", 10, false, Group ) - self:__TroopsRTB( 1, Group, Unit ) + end + return loadedmass +end + +--- (Internal) Function to calculate and set Unit internal cargo mass +-- @param #CTLD self +-- @param Wrapper.Unit#UNIT Unit +function CTLD:_UpdateUnitCargoMass(Unit) + self:T(self.lid .. " _UpdateUnitCargoMass") + local calculatedMass = self:_GetUnitCargoMass(Unit) + Unit:SetUnitInternalCargo(calculatedMass) + --local report = REPORT:New("Loadmaster report") + --report:Add("Carrying " .. calculatedMass .. "Kg") + --self:_SendMessage(report:Text(),10,false,Unit:GetGroup()) + return self +end + +--- (Internal) 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:_GetUnitCapabilities(Unit) -- #CTLD.UnitCapabilities + local trooplimit = capabilities.trooplimit -- #boolean + local cratelimit = capabilities.cratelimit -- #number + local loadedcargo = self.Loaded_Cargo[unitname] or {} -- #CTLD.LoadedCargo + local loadedmass = self:_GetUnitCargoMass(Unit) -- #number + 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 or type == CTLD_CARGO.Enum.ENGINEERS) and (not cargo:WasDropped() or self.allowcratepickupagain) then + report:Add(string.format("Troop: %s size %d",cargo:GetName(),cargo:GetCratesNeeded())) end - -- cleanup load list - local loaded = {} -- #CTLD.LoadedCargo - loaded.Troopsloaded = 0 - loaded.Cratesloaded = 0 - loaded.Cargo = {} + 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 and type ~= CTLD_CARGO.Enum.ENGINEERS) and (not cargo:WasDropped() or self.allowcratepickupagain) 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("------------------------------------------------------------") + report:Add("Total Mass: ".. loadedmass .. " kg") + local text = report:Text() + self:_SendMessage(text, 30, true, Group) + else + self:_SendMessage(string.format("Nothing loaded!\nTroop limit: %d | Crate limit %d",trooplimit,cratelimit), 10, false, Group) + end + return self +end + +--- (Internal) Function to list loaded cargo. +-- @param #CTLD self +-- @param Wrapper.Group#GROUP Group +-- @param Wrapper.Unit#UNIT Unit +-- @return #CTLD self +function CTLD:_ListInventory(Group, Unit) + self:T(self.lid .. " _ListInventory") + local unitname = Unit:GetName() + local unittype = Unit:GetTypeName() + local cgotypes = self.Cargo_Crates + local trptypes = self.Cargo_Troops + local stctypes = self.Cargo_Statics + + local function countcargo(cgotable) + local counter = 0 + for _,_cgo in pairs(cgotable) do + counter = counter + 1 + end + return counter + end + + local crateno = countcargo(cgotypes) + local troopno = countcargo(trptypes) + local staticno = countcargo(stctypes) + + if (crateno > 0 or troopno > 0 or staticno > 0) then + + local report = REPORT:New("Inventory Sheet") + report:Add("------------------------------------------------------------") + report:Add(string.format("Troops: %d, Cratetypes: %d",troopno,crateno+staticno)) + report:Add("------------------------------------------------------------") + report:Add(" -- TROOPS --") + for _,_cargo in pairs(trptypes) do + local cargo = _cargo -- #CTLD_CARGO + local type = cargo:GetType() -- #CTLD_CARGO.Enum + if (type == CTLD_CARGO.Enum.TROOPS or type == CTLD_CARGO.Enum.ENGINEERS) and not cargo:WasDropped() then + local stockn = cargo:GetStock() + local stock = "none" + if stockn == -1 then + stock = "unlimited" + elseif stockn > 0 then + stock = tostring(stockn) + end + report:Add(string.format("Unit: %s | Soldiers: %d | Stock: %s",cargo:GetName(),cargo:GetCratesNeeded(),stock)) + 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(cgotypes) do + local cargo = _cargo -- #CTLD_CARGO + local type = cargo:GetType() -- #CTLD_CARGO.Enum + if (type ~= CTLD_CARGO.Enum.TROOPS and type ~= CTLD_CARGO.Enum.ENGINEERS) and not cargo:WasDropped() then + local stockn = cargo:GetStock() + local stock = "none" + if stockn == -1 then + stock = "unlimited" + elseif stockn > 0 then + stock = tostring(stockn) + end + report:Add(string.format("Type: %s | Crates per Set: %d | Stock: %s",cargo:GetName(),cargo:GetCratesNeeded(),stock)) + cratecount = cratecount + 1 + end + end + -- Statics + for _,_cargo in pairs(stctypes) do + local cargo = _cargo -- #CTLD_CARGO + local type = cargo:GetType() -- #CTLD_CARGO.Enum + if (type == CTLD_CARGO.Enum.STATIC) and not cargo:WasDropped() then + local stockn = cargo:GetStock() + local stock = "none" + if stockn == -1 then + stock = "unlimited" + elseif stockn > 0 then + stock = tostring(stockn) + end + report:Add(string.format("Type: %s | Stock: %s",cargo:GetName(),stock)) + cratecount = cratecount + 1 + end + end + if cratecount == 0 then + report:Add(" N O N E") + end + local text = report:Text() + self:_SendMessage(text, 30, true, Group) + else + self:_SendMessage(string.format("Nothing in stock!"), 10, false, Group) + end + 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 +-- @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 canunload = true + if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then + self:_SendMessage("You need to open the door(s) to unload troops!", 10, false, Group) + if not self.debug then return self end + end + local inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD) + if not inzone then + inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP) + end + if inzone then + droppingatbase = true + 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 + 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 - 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() - if type ~= CTLD_CARGO.Enum.TROOPS and type ~= CTLD_CARGO.Enum.ENGINEERS and not dropped then - table.insert( loaded.Cargo, _cargo ) - loaded.Cratesloaded = loaded.Cratesloaded + 1 - else - -- add troops back to stock - if (type == CTLD_CARGO.Enum.TROOPS or type == CTLD_CARGO.Enum.ENGINEERS) and droppingatbase then - -- find right generic type - local name = cargo:GetName() - local gentroops = self.Cargo_Troops - for _id, _troop in pairs( gentroops ) do -- #number, #CTLD_CARGO - if _troop.Name == name then - local stock = _troop:GetStock() - -- avoid making unlimited stock limited - if stock and tonumber( stock ) >= 0 then - _troop:AddStock() - end - end - end - end - end - end - self.Loaded_Cargo[unitname] = nil - self.Loaded_Cargo[unitname] = loaded - self:_UpdateUnitCargoMass( Unit ) - else - if IsHerc then - self:_SendMessage( "Nothing loaded or not within airdrop parameters!", 10, false, Group ) - else - self:_SendMessage( "Nothing loaded or not hovering within parameters!", 10, false, Group ) - end - end - return self - end - - --- (Internal) Function to unload crates from heli. - -- @param #CTLD self - -- @param Wrapper.Group#GROUP Group - -- @param Wrapper.Unit#UNIT Unit - function CTLD:_UnloadCrates( Group, Unit ) - self:T( self.lid .. " _UnloadCrates" ) - - if not self.dropcratesanywhere then -- #1570 - -- check if we are in DROP zone - local inzone, zonename, zone, distance = self:IsUnitInZone( Unit, CTLD.CargoZoneType.DROP ) - if not inzone then - self:_SendMessage( "You are not close enough to a drop zone!", 10, false, Group ) - if not self.debug then - return self - end - end - 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 - 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 crate + -- looking for troops local cargotable = loadedcargo.Cargo - for _, _cargo in pairs( cargotable ) do + 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 type ~= CTLD_CARGO.Enum.ENGINEERS and (not cargo:WasDropped() or self.allowcratepickupagain) then - -- unload crates - self:_GetCrates( Group, Unit, cargo, 1, true ) - cargo:SetWasDropped( true ) - cargo:SetHasMoved( true ) - 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.TROOPS or type == CTLD_CARGO.Enum.ENGINEERS then - table.insert( loaded.Cargo, _cargo ) - loaded.Troopsloaded = loaded.Troopsloaded + size - end - end - self.Loaded_Cargo[unitname] = nil - self.Loaded_Cargo[unitname] = loaded - - self:_UpdateUnitCargoMass( Unit ) - else - if IsHerc then - self:_SendMessage( "Nothing loaded or not within airdrop parameters!", 10, false, Group ) + if (type == CTLD_CARGO.Enum.TROOPS or type == CTLD_CARGO.Enum.ENGINEERS) and not cargo:WasDropped() then + -- unload troops + local name = cargo:GetName() or "none" + local temptable = cargo:GetTemplates() or {} + local position = Group:GetCoordinate() + 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)) + self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias) + :InitRandomizeUnits(true,20,2) + :InitDelayOff() + :SpawnFromVec2(randomcoord) + if self.movetroopstowpzone and type ~= CTLD_CARGO.Enum.ENGINEERS then + self:_MoveGroupToZone(self.DroppedTroops[self.TroopCounter]) + end + end -- template loop + cargo:SetWasDropped(true) + -- engineering group? + --self:I("Dropped Troop Type: "..type) + if type == CTLD_CARGO.Enum.ENGINEERS then + self.Engineers = self.Engineers + 1 + local grpname = self.DroppedTroops[self.TroopCounter]:GetName() + self.EngineersInField[self.Engineers] = CTLD_ENGINEERING:New(name, grpname) + self:_SendMessage(string.format("Dropped Engineers %s into action!",name), 10, false, Group) + else + self:_SendMessage(string.format("Dropped Troops %s into action!",name), 10, false, Group) + end + self:__TroopsDeployed(1, Group, Unit, self.DroppedTroops[self.TroopCounter]) + end -- if type end + end -- cargotable loop + else -- droppingatbase + self:_SendMessage("Troops have returned to base!", 10, false, Group) + self:__TroopsRTB(1, Group, Unit) + 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() + if type ~= CTLD_CARGO.Enum.TROOPS and type ~= CTLD_CARGO.Enum.ENGINEERS and not dropped then + table.insert(loaded.Cargo,_cargo) + loaded.Cratesloaded = loaded.Cratesloaded + 1 else - self:_SendMessage( "Nothing loaded or not hovering within parameters!", 10, false, Group ) + -- add troops back to stock + if (type == CTLD_CARGO.Enum.TROOPS or type == CTLD_CARGO.Enum.ENGINEERS) and droppingatbase then + -- find right generic type + local name = cargo:GetName() + local gentroops = self.Cargo_Troops + for _id,_troop in pairs (gentroops) do -- #number, #CTLD_CARGO + if _troop.Name == name then + local stock = _troop:GetStock() + -- avoid making unlimited stock limited + if stock and tonumber(stock) >= 0 then _troop:AddStock() end + end + end + end end end - return self + self.Loaded_Cargo[unitname] = nil + self.Loaded_Cargo[unitname] = loaded + self:_UpdateUnitCargoMass(Unit) + else + if IsHerc then + self:_SendMessage("Nothing loaded or not within airdrop parameters!", 10, false, Group) + else + self:_SendMessage("Nothing loaded or not hovering within parameters!", 10, false, Group) + end end + return self +end - --- (Internal) Function to build nearby crates. - -- @param #CTLD self - -- @param Wrapper.Group#GROUP Group - -- @param Wrapper.Unit#UNIT Unit - -- @param #boolean Engineering If true build is by an engineering team. - function CTLD:_BuildCrates( Group, Unit, Engineering ) - self:T( self.lid .. " _BuildCrates" ) - -- avoid users trying to build from flying Hercs - local type = Unit:GetTypeName() - if type == "Hercules" and self.enableHercules and not Engineering then - local speed = Unit:GetVelocityKMH() - if speed > 1 then - self:_SendMessage( "You need to land / stop to build something, Pilot!", 10, false, Group ) - return self +--- (Internal) Function to unload crates from heli. +-- @param #CTLD self +-- @param Wrapper.Group#GROUP Group +-- @param Wrapper.Unit#UNIT Unit +function CTLD:_UnloadCrates(Group, Unit) + self:T(self.lid .. " _UnloadCrates") + + if not self.dropcratesanywhere then -- #1570 + -- check if we are in DROP zone + local inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.DROP) + if not inzone then + self:_SendMessage("You are not close enough to a drop zone!", 10, false, Group) + if not self.debug then + return self end end - -- get nearby crates - local finddist = self.CrateDistance or 35 - 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() and not Crate:IsRepair() and not Crate:IsStatic() 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 - foundbuilds = true - end - if buildables[name].Found >= buildables[name].Required then - buildables[name].CanBuild = true - canbuild = 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 - 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() - if not Engineering then - self:_SendMessage( text, 30, true, Group ) - else - self:T( text ) - end - -- 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 - if not Engineering then - self:_SendMessage( string.format( "No crates within %d meters!", finddist ), 10, false, Group ) - end - end -- number > 0 - return self end - - --- (Internal) Function to repair nearby vehicles / FOBs - -- @param #CTLD self - -- @param Wrapper.Group#GROUP Group - -- @param Wrapper.Unit#UNIT Unit - -- @param #boolean Engineering If true, this is an engineering role - function CTLD:_RepairCrates( Group, Unit, Engineering ) - self:T( self.lid .. " _RepairCrates" ) - -- get nearby crates - local finddist = self.CrateDistance or 35 - 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() and Crate:IsRepair() and not Crate:IsStatic() 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 - foundbuilds = true - end - if buildables[name].Found >= buildables[name].Required then - buildables[name].CanBuild = true - canbuild = true - end - self:T( { repair = buildables } ) - end -- end dropped - end -- end crate loop - -- ok let's list what we have - local report = REPORT:New( "Checklist Repairs" ) - 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 - local text = string.format( "Type: %s | Required %d | Found %d | Can Repair %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() - if not Engineering then - self:_SendMessage( text, 30, true, Group ) - else - self:T( text ) - end - -- 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:_RepairObjectFromCrates( Group, Unit, crates, build, number, Engineering ) - end - end - end - else - if not Engineering then - self:_SendMessage( string.format( "No crates within %d meters!", finddist ), 10, false, Group ) - end - end -- number > 0 - return self + -- 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 + 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 crate + 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 type ~= CTLD_CARGO.Enum.ENGINEERS and (not cargo:WasDropped() or self.allowcratepickupagain) then + -- unload crates + self:_GetCrates(Group, Unit, cargo, 1, true) + cargo:SetWasDropped(true) + cargo:SetHasMoved(true) + 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.TROOPS or type == CTLD_CARGO.Enum.ENGINEERS then + table.insert(loaded.Cargo,_cargo) + loaded.Troopsloaded = loaded.Troopsloaded + size + end + end + self.Loaded_Cargo[unitname] = nil + self.Loaded_Cargo[unitname] = loaded + + self:_UpdateUnitCargoMass(Unit) + else + if IsHerc then + self:_SendMessage("Nothing loaded or not within airdrop parameters!", 10, false, Group) + else + self:_SendMessage("Nothing loaded or not hovering within parameters!", 10, false, Group) + end + end + return self +end - --- (Internal) 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 - -- @param #boolean Repair If true this is a repair and not a new build - -- @param Core.Point#COORDINATE Coordinate Location for repair (e.g. where the destroyed unit was) - function CTLD:_BuildObjectFromCrates( Group, Unit, Build, Repair, RepairLocation ) - self:T( self.lid .. " _BuildObjectFromCrates" ) - -- Spawn-a-crate-content - if Group and Group:IsAlive() then - local position = Unit:GetCoordinate() or Group:GetCoordinate() - local unitname = Unit:GetName() or Group:GetName() - local name = Build.Name - local ctype = Build.Type -- #CTLD_CARGO.Enum - local canmove = false - if ctype == CTLD_CARGO.Enum.VEHICLE then - canmove = true - end - if ctype == CTLD_CARGO.Enum.STATIC then - return self - end - local temptable = Build.Template or {} - if type( temptable ) == "string" then - temptable = { temptable } - end - local zone = ZONE_GROUP:New( string.format( "Unload zone-%s", unitname ), Group, 100 ) - local randomcoord = zone:GetRandomCoordinate( 35 ):GetVec2() - if Repair then - randomcoord = RepairLocation:GetVec2() - end - for _, _template in pairs( temptable ) do - self.TroopCounter = self.TroopCounter + 1 - local alias = string.format( "%s-%d", _template, math.random( 1, 100000 ) ) - if canmove then - self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias( _template, alias ) - :InitRandomizeUnits( true, 20, 2 ) - :InitDelayOff() - :SpawnFromVec2( randomcoord ) - else -- don't random position of e.g. SAM units build as FOB - self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias( _template, alias ) - :InitDelayOff() - :SpawnFromVec2( randomcoord ) - end - if self.movetroopstowpzone and canmove then - self:_MoveGroupToZone( self.DroppedTroops[self.TroopCounter] ) - end - if Repair then - self:__CratesRepaired( 1, Group, Unit, self.DroppedTroops[self.TroopCounter] ) +--- (Internal) Function to build nearby crates. +-- @param #CTLD self +-- @param Wrapper.Group#GROUP Group +-- @param Wrapper.Unit#UNIT Unit +-- @param #boolean Engineering If true build is by an engineering team. +function CTLD:_BuildCrates(Group, Unit,Engineering) + self:T(self.lid .. " _BuildCrates") + -- avoid users trying to build from flying Hercs + local type = Unit:GetTypeName() + if type == "Hercules" and self.enableHercules and not Engineering then + local speed = Unit:GetVelocityKMH() + if speed > 1 then + self:_SendMessage("You need to land / stop to build something, Pilot!", 10, false, Group) + return self + end + end + -- get nearby crates + local finddist = self.CrateDistance or 35 + 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() and not Crate:IsRepair() and not Crate:IsStatic() 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 - self:__CratesBuild( 1, Group, Unit, self.DroppedTroops[self.TroopCounter] ) + buildables[name].Found = buildables[name].Found + 1 + foundbuilds = true end - end -- template loop - else - self:T( self.lid .. "Group KIA while building!" ) - end - return self - end - - --- (Internal) 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() - -- Get closest zone of type - local outcome, name, zone, distance = self:IsUnitInZone( Group, CTLD.CargoZoneType.MOVE ) - -- 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() - local zonecoord = zone:GetRandomCoordinate( 20, 125 ) -- Core.Point#COORDINATE - local coordinate = zonecoord:GetVec2() - Group:SetAIOn() - Group:OptionAlarmStateAuto() - Group:OptionDisperseOnAttack( 30 ) - Group:OptionROEOpenFirePossible() - Group:RouteToVec2( coordinate, 5 ) - end - return self - end - - --- (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. - -- @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 - 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() - local thisID = nowcrate:GetID() - if name == nametype then -- matching crate type - table.insert( destIDs, thisID ) - found = found + 1 - nowcrate:GetPositionable():Destroy( false ) - nowcrate.Positionable = nil - nowcrate.HasBeenDropped = false + if buildables[name].Found >= buildables[name].Required then + buildables[name].CanBuild = true + canbuild = 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 - if found == numberdest then - break - end -- got enough + 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() + if not Engineering then + self:_SendMessage(text, 30, true, Group) + else + self:T(text) end - -- loop and remove from real world representation - self:_CleanupTrackedCrates( destIDs ) - return self - end + -- 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 + if not Engineering then self:_SendMessage(string.format("No crates within %d meters!",finddist), 10, false, Group) end + end -- number > 0 + return self +end - --- (Internal) 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 ) -- Wrapper.Unit#UNIT Asume that there is only one unit in the flight for players +--- (Internal) Function to repair nearby vehicles / FOBs +-- @param #CTLD self +-- @param Wrapper.Group#GROUP Group +-- @param Wrapper.Unit#UNIT Unit +-- @param #boolean Engineering If true, this is an engineering role +function CTLD:_RepairCrates(Group, Unit, Engineering) + self:T(self.lid .. " _RepairCrates") + -- get nearby crates + local finddist = self.CrateDistance or 35 + 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() and Crate:IsRepair() and not Crate:IsStatic() 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 + foundbuilds = true + end + if buildables[name].Found >= buildables[name].Required then + buildables[name].CanBuild = true + canbuild = true + end + self:T({repair = buildables}) + end -- end dropped + end -- end crate loop + -- ok let\'s list what we have + local report = REPORT:New("Checklist Repairs") + 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 + local text = string.format("Type: %s | Required %d | Found %d | Can Repair %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() + if not Engineering then + self:_SendMessage(text, 30, true, Group) + else + self:T(text) + end + -- 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:_RepairObjectFromCrates(Group,Unit,crates,build,number,Engineering) + end + end + end + else + if not Engineering then self:_SendMessage(string.format("No crates within %d meters!",finddist), 10, false, Group) end + end -- number > 0 + return self +end + +--- (Internal) 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 +-- @param #boolean Repair If true this is a repair and not a new build +-- @param Core.Point#COORDINATE Coordinate Location for repair (e.g. where the destroyed unit was) +function CTLD:_BuildObjectFromCrates(Group,Unit,Build,Repair,RepairLocation) + self:T(self.lid .. " _BuildObjectFromCrates") + -- Spawn-a-crate-content + if Group and Group:IsAlive() then + local position = Unit:GetCoordinate() or Group:GetCoordinate() + local unitname = Unit:GetName() or Group:GetName() + local name = Build.Name + local ctype = Build.Type -- #CTLD_CARGO.Enum + local canmove = false + if ctype == CTLD_CARGO.Enum.VEHICLE then canmove = true end + if ctype == CTLD_CARGO.Enum.STATIC then + return self + end + local temptable = Build.Template or {} + if type(temptable) == "string" then + temptable = {temptable} + end + local zone = ZONE_GROUP:New(string.format("Unload zone-%s",unitname),Group,100) + local randomcoord = zone:GetRandomCoordinate(35):GetVec2() + if Repair then + randomcoord = RepairLocation:GetVec2() + end + for _,_template in pairs(temptable) do + self.TroopCounter = self.TroopCounter + 1 + local alias = string.format("%s-%d", _template, math.random(1,100000)) + if canmove then + self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias) + :InitRandomizeUnits(true,20,2) + :InitDelayOff() + :SpawnFromVec2(randomcoord) + else -- don't random position of e.g. SAM units build as FOB + self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias) + :InitDelayOff() + :SpawnFromVec2(randomcoord) + end + if self.movetroopstowpzone and canmove then + self:_MoveGroupToZone(self.DroppedTroops[self.TroopCounter]) + end + if Repair then + self:__CratesRepaired(1,Group,Unit,self.DroppedTroops[self.TroopCounter]) + else + self:__CratesBuild(1,Group,Unit,self.DroppedTroops[self.TroopCounter]) + end + end -- template loop + else + self:T(self.lid.."Group KIA while building!") + end + return self +end + +--- (Internal) 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() + -- Get closest zone of type + local outcome, name, zone, distance = self:IsUnitInZone(Group,CTLD.CargoZoneType.MOVE) + --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() + local zonecoord = zone:GetRandomCoordinate(20,125) -- Core.Point#COORDINATE + local coordinate = zonecoord:GetVec2() + Group:SetAIOn() + Group:OptionAlarmStateAuto() + Group:OptionDisperseOnAttack(30) + Group:OptionROEOpenFirePossible() + Group:RouteToVec2(coordinate,5) + end + return self +end + +--- (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. +-- @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 + 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() + local thisID = nowcrate:GetID() + if name == nametype then -- matching crate type + table.insert(destIDs,thisID) + found = found + 1 + nowcrate:GetPositionable():Destroy(false) + nowcrate.Positionable = nil + nowcrate.HasBeenDropped = false + end + if found == numberdest then break end -- got enough + end + -- loop and remove from real world representation + self:_CleanupTrackedCrates(destIDs) + return self +end + +--- (Internal) 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) -- Wrapper.Unit#UNIT Asume that there is only one unit in the flight for players + if _unit then + if _unit:IsAlive() and _unit:IsPlayer() then + if _unit:IsHelicopter() or (_unit:GetTypeName() == "Hercules" and self.enableHercules) then --ensure no stupid unit entries here + local unitName = _unit:GetName() + _UnitList[unitName] = unitName + end + 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 - if _unit:IsAlive() and _unit:IsPlayer() then - if _unit:IsHelicopter() or (_unit:GetTypeName() == "Hercules" and self.enableHercules) then -- ensure no stupid unit entries here - local unitName = _unit:GetName() - _UnitList[unitName] = unitName + local _group = _unit:GetGroup() -- Wrapper.Group#GROUP + if _group then + -- get chopper capabilities + local unittype = _unit:GetTypeName() + local capabilities = self:_GetUnitCapabilities(_unit) -- #CTLD.UnitCapabilities + local cantroops = capabilities.troops + local cancrates = capabilities.crates + -- top menu + local topmenu = MENU_GROUP:New(_group,"CTLD",nil) + local toptroops = MENU_GROUP:New(_group,"Manage Troops",topmenu) + local topcrates = MENU_GROUP:New(_group,"Manage Crates",topmenu) + local listmenu = MENU_GROUP_COMMAND:New(_group,"List boarded cargo",topmenu, self._ListCargo, self, _group, _unit) + local invtry = MENU_GROUP_COMMAND:New(_group,"Inventory",topmenu, self._ListInventory, self, _group, _unit) + local rbcns = MENU_GROUP_COMMAND:New(_group,"List active zone beacons",topmenu, self._ListRadioBeacons, self, _group, _unit) + local smoketopmenu = MENU_GROUP:New(_group,"Smokes & Flares",topmenu) + local smokemenu = MENU_GROUP_COMMAND:New(_group,"Smoke zones nearby",smoketopmenu, self.SmokeZoneNearBy, self, _unit, false) + local smokeself = MENU_GROUP_COMMAND:New(_group,"Drop smoke now",smoketopmenu, self.SmokePositionNow, self, _unit, false) + local flaremenu = MENU_GROUP_COMMAND:New(_group,"Flare zones nearby",smoketopmenu, self.SmokeZoneNearBy, self, _unit, true) + local flareself = MENU_GROUP_COMMAND:New(_group,"Fire flare now",smoketopmenu, self.SmokePositionNow, self, _unit, true):Refresh() + -- sub menus + -- 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() + local extractMenu1 = MENU_GROUP_COMMAND:New(_group, "Extract troops", toptroops, self._ExtractTroops, self, _group, _unit):Refresh() end - 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:_GetUnitCapabilities( _unit ) -- #CTLD.UnitCapabilities - local cantroops = capabilities.troops - local cancrates = capabilities.crates - -- top menu - local topmenu = MENU_GROUP:New( _group, "CTLD", nil ) - local toptroops = MENU_GROUP:New( _group, "Manage Troops", topmenu ) - local topcrates = MENU_GROUP:New( _group, "Manage Crates", topmenu ) - local listmenu = MENU_GROUP_COMMAND:New( _group, "List boarded cargo", topmenu, self._ListCargo, self, _group, _unit ) - local invtry = MENU_GROUP_COMMAND:New( _group, "Inventory", topmenu, self._ListInventory, self, _group, _unit ) - local rbcns = MENU_GROUP_COMMAND:New( _group, "List active zone beacons", topmenu, self._ListRadioBeacons, self, _group, _unit ) - local smoketopmenu = MENU_GROUP:New( _group, "Smokes & Flares", topmenu ) - local smokemenu = MENU_GROUP_COMMAND:New( _group, "Smoke zones nearby", smoketopmenu, self.SmokeZoneNearBy, self, _unit, false ) - local smokeself = MENU_GROUP_COMMAND:New( _group, "Drop smoke now", smoketopmenu, self.SmokePositionNow, self, _unit, false ) - local flaremenu = MENU_GROUP_COMMAND:New( _group, "Flare zones nearby", smoketopmenu, self.SmokeZoneNearBy, self, _unit, true ) - local flareself = MENU_GROUP_COMMAND:New( _group, "Fire flare now", smoketopmenu, self.SmokePositionNow, self, _unit, true ):Refresh() - -- sub menus - -- 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() - local extractMenu1 = MENU_GROUP_COMMAND:New( _group, "Extract troops", toptroops, self._ExtractTroops, self, _group, _unit ):Refresh() + -- 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("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0) + menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,cratesmenu,self._GetCrates, self, _group, _unit, entry) end - -- 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( "Crate %s (%dkg)", entry.Name, entry.PerCrateMass or 0 ) - menus[menucount] = MENU_GROUP_COMMAND:New( _group, menutext, cratesmenu, self._GetCrates, self, _group, _unit, entry ) - end - for _, _entry in pairs( self.Cargo_Statics ) do - local entry = _entry -- #CTLD_CARGO - menucount = menucount + 1 - local menutext = string.format( "Crate %s (%dkg)", entry.Name, entry.PerCrateMass or 0 ) - 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 ) - local repairmenu = MENU_GROUP_COMMAND:New( _group, "Repair", topcrates, self._RepairCrates, self, _group, _unit ):Refresh() + for _,_entry in pairs(self.Cargo_Statics) do + local entry = _entry -- #CTLD_CARGO + menucount = menucount + 1 + local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0) + menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,cratesmenu,self._GetCrates, self, _group, _unit, entry) end - 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 - 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 + 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) + local repairmenu = MENU_GROUP_COMMAND:New(_group,"Repair",topcrates, self._RepairCrates, self, _group, _unit):Refresh() + end + 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 + 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 #string 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). - -- @param #number PerTroopMass Mass in kg of each soldier - -- @param #number Stock Number of groups in stock. Nil for unlimited. - function CTLD:AddTroopsCargo( Name, Templates, Type, NoTroops, PerTroopMass, Stock ) - self:T( self.lid .. " AddTroopsCargo" ) - self:T( { Name, Templates, Type, NoTroops, PerTroopMass, Stock } ) - self.CargoCounter = self.CargoCounter + 1 - -- Troops are directly loadable - local cargo = CTLD_CARGO:New( self.CargoCounter, Name, Templates, Type, false, true, NoTroops, nil, nil, PerTroopMass, Stock ) - table.insert( self.Cargo_Troops, cargo ) - 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 #string 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). +-- @param #number PerTroopMass Mass in kg of each soldier +-- @param #number Stock Number of groups in stock. Nil for unlimited. +function CTLD:AddTroopsCargo(Name,Templates,Type,NoTroops,PerTroopMass,Stock) + self:T(self.lid .. " AddTroopsCargo") + self:T({Name,Templates,Type,NoTroops,PerTroopMass,Stock}) + self.CargoCounter = self.CargoCounter + 1 + -- Troops are directly loadable + local cargo = CTLD_CARGO:New(self.CargoCounter,Name,Templates,Type,false,true,NoTroops,nil,nil,PerTroopMass,Stock) + 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 #string 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. - -- @param #number PerCrateMass Mass in kg of each crate - -- @param #number Stock Number of groups in stock. Nil for unlimited. - function CTLD:AddCratesCargo( Name, Templates, Type, NoCrates, PerCrateMass, Stock ) - 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, nil, nil, PerCrateMass, Stock ) - table.insert( self.Cargo_Crates, 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 #string 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. +-- @param #number PerCrateMass Mass in kg of each crate +-- @param #number Stock Number of groups in stock. Nil for unlimited. +function CTLD:AddCratesCargo(Name,Templates,Type,NoCrates,PerCrateMass,Stock) + 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,nil,nil,PerCrateMass,Stock) + table.insert(self.Cargo_Crates,cargo) + return self +end - --- User function - Add *generic* static-type loadable as cargo. This type will create cargo that needs to be loaded, moved and dropped. - -- @param #CTLD self - -- @param #string Name Unique name of this type of cargo as set in the mission editor (note: UNIT name!), e.g. "Ammunition-1". - -- @param #number Mass Mass in kg of each static in kg, e.g. 100. - -- @param #number Stock Number of groups in stock. Nil for unlimited. - function CTLD:AddStaticsCargo( Name, Mass, Stock ) - self:T( self.lid .. " AddStaticsCargo" ) - self.CargoCounter = self.CargoCounter + 1 - local type = CTLD_CARGO.Enum.STATIC - local template = STATIC:FindByName( Name, true ):GetTypeName() - -- Crates are not directly loadable - local cargo = CTLD_CARGO:New( self.CargoCounter, Name, template, type, false, false, 1, nil, nil, Mass, Stock ) - table.insert( self.Cargo_Statics, cargo ) - return self - end +--- User function - Add *generic* static-type loadable as cargo. This type will create cargo that needs to be loaded, moved and dropped. +-- @param #CTLD self +-- @param #string Name Unique name of this type of cargo as set in the mission editor (note: UNIT name!), e.g. "Ammunition-1". +-- @param #number Mass Mass in kg of each static in kg, e.g. 100. +-- @param #number Stock Number of groups in stock. Nil for unlimited. +function CTLD:AddStaticsCargo(Name,Mass,Stock) + self:T(self.lid .. " AddStaticsCargo") + self.CargoCounter = self.CargoCounter + 1 + local type = CTLD_CARGO.Enum.STATIC + local template = STATIC:FindByName(Name,true):GetTypeName() + -- Crates are not directly loadable + local cargo = CTLD_CARGO:New(self.CargoCounter,Name,template,type,false,false,1,nil,nil,Mass,Stock) + table.insert(self.Cargo_Statics,cargo) + return self +end - --- User function - Get a *generic* static-type loadable as #CTLD_CARGO object. - -- @param #CTLD self - -- @param #string Name Unique Unit(!) name of this type of cargo as set in the mission editor (not: GROUP name!), e.g. "Ammunition-1". - -- @param #number Mass Mass in kg of each static in kg, e.g. 100. - -- @return #CTLD_CARGO Cargo object - function CTLD:GetStaticsCargoFromTemplate( Name, Mass ) - self:T( self.lid .. " GetStaticsCargoFromTemplate" ) - self.CargoCounter = self.CargoCounter + 1 - local type = CTLD_CARGO.Enum.STATIC - local template = STATIC:FindByName( Name, true ):GetTypeName() - -- Crates are not directly loadable - local cargo = CTLD_CARGO:New( self.CargoCounter, Name, template, type, false, false, 1, nil, nil, Mass, 1 ) - -- table.insert(self.Cargo_Statics,cargo) - return cargo - end +--- User function - Get a *generic* static-type loadable as #CTLD_CARGO object. +-- @param #CTLD self +-- @param #string Name Unique Unit(!) name of this type of cargo as set in the mission editor (not: GROUP name!), e.g. "Ammunition-1". +-- @param #number Mass Mass in kg of each static in kg, e.g. 100. +-- @return #CTLD_CARGO Cargo object +function CTLD:GetStaticsCargoFromTemplate(Name,Mass) + self:T(self.lid .. " GetStaticsCargoFromTemplate") + self.CargoCounter = self.CargoCounter + 1 + local type = CTLD_CARGO.Enum.STATIC + local template = STATIC:FindByName(Name,true):GetTypeName() + -- Crates are not directly loadable + local cargo = CTLD_CARGO:New(self.CargoCounter,Name,template,type,false,false,1,nil,nil,Mass,1) + --table.insert(self.Cargo_Statics,cargo) + return cargo +end - --- User function - Add *generic* repair crates loadable as cargo. This type will create crates that need to be loaded, moved, dropped and built. - -- @param #CTLD self - -- @param #string Name Unique name of this type of cargo. E.g. "Humvee". - -- @param #string Template Template of VEHICLE or FOB cargo that this can repair. - -- @param #CTLD_CARGO.Enum Type Type of cargo, here REPAIR. - -- @param #number NoCrates Number of crates needed to build this cargo. - -- @param #number PerCrateMass Mass in kg of each crate - -- @param #number Stock Number of groups in stock. Nil for unlimited. - function CTLD:AddCratesRepair( Name, Template, Type, NoCrates, PerCrateMass, Stock ) - self:T( self.lid .. " AddCratesRepair" ) - self.CargoCounter = self.CargoCounter + 1 - -- Crates are not directly loadable - local cargo = CTLD_CARGO:New( self.CargoCounter, Name, Template, Type, false, false, NoCrates, nil, nil, PerCrateMass, Stock ) - table.insert( self.Cargo_Crates, cargo ) - return self - end +--- User function - Add *generic* repair crates loadable as cargo. This type will create crates that need to be loaded, moved, dropped and built. +-- @param #CTLD self +-- @param #string Name Unique name of this type of cargo. E.g. "Humvee". +-- @param #string Template Template of VEHICLE or FOB cargo that this can repair. +-- @param #CTLD_CARGO.Enum Type Type of cargo, here REPAIR. +-- @param #number NoCrates Number of crates needed to build this cargo. +-- @param #number PerCrateMass Mass in kg of each crate +-- @param #number Stock Number of groups in stock. Nil for unlimited. +function CTLD:AddCratesRepair(Name,Template,Type,NoCrates, PerCrateMass,Stock) + self:T(self.lid .. " AddCratesRepair") + self.CargoCounter = self.CargoCounter + 1 + -- Crates are not directly loadable + local cargo = CTLD_CARGO:New(self.CargoCounter,Name,Template,Type,false,false,NoCrates,nil,nil,PerCrateMass,Stock) + 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 ) - elseif zone.type == CTLD.CargoZoneType.DROP then - table.insert( self.dropOffZones, zone ) - elseif zone.type == CTLD.CargoZoneType.SHIP then - table.insert( self.shipZones, zone ) - else - table.insert( self.wpZones, zone ) +--- 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) + elseif zone.type == CTLD.CargoZoneType.DROP then + table.insert(self.dropOffZones,zone) + elseif zone.type == CTLD.CargoZoneType.SHIP then + table.insert(self.shipZones,zone) + else + table.insert(self.wpZones,zone) + 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.CargoZoneType 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 NewState ~= nil then + newstate = NewState + end + + -- get correct table + local table = {} + if ZoneType == CTLD.CargoZoneType.LOAD then + table = self.pickupZones + elseif ZoneType == CTLD.CargoZoneType.DROP then + table = self.dropOffZones + elseif ZoneType == CTLD.CargoZoneType.SHIP then + table = self.shipZones + 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 - return self 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.CargoZoneType 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 NewState ~= nil then - newstate = NewState - end - -- get correct table - local table = {} - if ZoneType == CTLD.CargoZoneType.LOAD then - table = self.pickupZones - elseif ZoneType == CTLD.CargoZoneType.DROP then - table = self.dropOffZones - elseif ZoneType == CTLD.CargoZoneType.SHIP then - table = self.shipZones - 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.CargoZoneType 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 - --- 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.CargoZoneType 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 - - --- (Internal) 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 +--- (Internal) 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 + 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 - --- (Internal) 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 +--- (Internal) 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 + 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 + return beacon +end - --- (Internal) 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 +--- (Internal) 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 + --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? - -- @param #number Shiplength Length of Ship for shipzones - -- @param #number Shipwidth Width of Ship for shipzones - -- @return #CTLD self - function CTLD:AddCTLDZone( Name, Type, Color, Active, HasBeacon, Shiplength, Shipwidth ) - 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 +--- 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? +-- @param #number Shiplength Length of Ship for shipzones +-- @param #number Shipwidth Width of Ship for shipzones +-- @return #CTLD self +function CTLD:AddCTLDZone(Name, Type, Color, Active, HasBeacon, Shiplength, Shipwidth) + self:T(self.lid .. " AddCTLDZone") - 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 - - if Type == CTLD.CargoZoneType.SHIP then - ctldzone.shiplength = Shiplength or 100 - ctldzone.shipwidth = Shipwidth or 10 - end - - self:AddZone( ctldzone ) - return self + 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 + + if Type == CTLD.CargoZoneType.SHIP then + ctldzone.shiplength = Shiplength or 100 + ctldzone.shipwidth = Shipwidth or 10 + end + + self:AddZone(ctldzone) + return self +end - --- (Internal) 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, [4] = self.shipZones } - for i = 1, 4 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 +--- (Internal) 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, [4] = self.shipZones} + for i=1,4 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 - if report:GetCount() == 1 then - report:Add( " N O N E" ) - end - report:Add( "------------------------------------------------------------" ) - self:_SendMessage( report:Text(), 30, true, Group ) - return self end - - --- (Internal) 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. - -- @param #boolean IsShip If true zone is a ship. - function CTLD:_AddRadioBeacon( Name, Sound, Mhz, Modulation, IsShip ) - self:T( self.lid .. " _AddRadioBeacon" ) - local Zone = nil - if IsShip then - Zone = UNIT:FindByName( Name ) - else - Zone = ZONE:FindByName( Name ) - end - 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 + if report:GetCount() == 1 then + report:Add(" N O N E") end + report:Add("------------------------------------------------------------") + self:_SendMessage(report:Text(), 30, true, Group) + return self +end - --- (Internal) Function to refresh radio beacons - -- @param #CTLD self - function CTLD:_RefreshRadioBeacons() - self:T( self.lid .. " _RefreshRadioBeacons" ) +--- (Internal) 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. +-- @param #boolean IsShip If true zone is a ship. +function CTLD:_AddRadioBeacon(Name, Sound, Mhz, Modulation, IsShip) + self:T(self.lid .. " _AddRadioBeacon") + local Zone = nil + if IsShip then + Zone = UNIT:FindByName(Name) + else + Zone = ZONE:FindByName(Name) + end + 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 - local zones = { [1] = self.pickupZones, [2] = self.wpZones, [3] = self.dropOffZones, [4] = self.shipZones } - for i = 1, 4 do - local IsShip = false - if i == 4 then - IsShip = true - end - 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, IsShip ) - self:_AddRadioBeacon( Name, Sound, VHF, radio.modulation.FM, IsShip ) - self:_AddRadioBeacon( Name, Sound, UHF, radio.modulation.AM, IsShip ) - end +--- (Internal) Function to refresh radio beacons +-- @param #CTLD self +function CTLD:_RefreshRadioBeacons() + self:T(self.lid .. " _RefreshRadioBeacons") + + local zones = {[1] = self.pickupZones, [2] = self.wpZones, [3] = self.dropOffZones, [4] = self.shipZones} + for i=1,4 do + local IsShip = false + if i == 4 then IsShip = true end + 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, IsShip) + self:_AddRadioBeacon(Name,Sound,VHF,radio.modulation.FM, IsShip) + self:_AddRadioBeacon(Name,Sound,UHF,radio.modulation.AM, IsShip) end end - return self end + return self +end - --- (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 - -- @return #boolean Outcome Is in zone or not - -- @return #string name Closest zone name - -- @return Core.Zone#ZONE zone Closest Core.Zone#ZONE object - -- @return #number distance Distance to closest zone - -- @return #number width Radius of zone or width of ship - function CTLD:IsUnitInZone( Unit, Zonetype ) - self:T( self.lid .. " IsUnitInZone" ) - self:T( Zonetype ) - local unitname = Unit:GetName() - 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 - elseif Zonetype == CTLD.CargoZoneType.SHIP then - zonetable = self.shipZones -- #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 zonewret = nil - local zonenameret = nil - for _, _cargozone in pairs( zonetable ) do - local czone = _cargozone -- #CTLD.CargoZone - local unitcoord = Unit:GetCoordinate() - local zonename = czone.name - local active = czone.active - local color = czone.color - local zone = nil - local zoneradius = 100 - local zonewidth = 20 - if Zonetype == CTLD.CargoZoneType.SHIP then - self:T( "Checking Type Ship: " .. zonename ) - zone = UNIT:FindByName( zonename ) - zonecoord = zone:GetCoordinate() - zoneradius = czone.shiplength - zonewidth = czone.shipwidth - else - zone = ZONE:FindByName( zonename ) - zonecoord = zone:GetCoordinate() - zoneradius = zone:GetRadius() - zonewidth = zoneradius - end - local distance = self:_GetDistance( zonecoord, unitcoord ) - if distance <= zoneradius and active then - outcome = true - end - if maxdist > distance then - maxdist = distance - zoneret = zone - zonenameret = zonename - zonewret = zonewidth - colorret = color - end - end - if Zonetype == CTLD.CargoZoneType.SHIP then - return outcome, zonenameret, zoneret, maxdist, zonewret - else - return outcome, zonenameret, zoneret, maxdist - end +--- (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 +-- @return #boolean Outcome Is in zone or not +-- @return #string name Closest zone name +-- @return Core.Zone#ZONE zone Closest Core.Zone#ZONE object +-- @return #number distance Distance to closest zone +-- @return #number width Radius of zone or width of ship +function CTLD:IsUnitInZone(Unit,Zonetype) + self:T(self.lid .. " IsUnitInZone") + self:T(Zonetype) + local unitname = Unit:GetName() + 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 + elseif Zonetype == CTLD.CargoZoneType.SHIP then + zonetable = self.shipZones -- #table + else + zonetable = self.wpZones -- #table end - - --- User function - Drop a smoke or flare at current location. - -- @param #CTLD self - -- @param Wrapper.Unit#UNIT Unit The Unit. - -- @param #boolean Flare If true, flare instead. - function CTLD:SmokePositionNow( Unit, Flare ) - self:T( self.lid .. " SmokePositionNow" ) - local SmokeColor = self.SmokeColor or SMOKECOLOR.Red - local FlareColor = self.FlareColor or FLARECOLOR.Red - -- table of #CTLD.CargoZone table - local unitcoord = Unit:GetCoordinate() -- Core.Point#COORDINATE - local Group = Unit:GetGroup() - if Flare then - unitcoord:Flare( FlareColor, 90 ) - else - local height = unitcoord:GetLandHeight() + 2 - unitcoord.y = height - unitcoord:Smoke( SmokeColor ) - end - return self - end - - --- User function - Start smoke/flare 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 + --- now see if we\'re in + local zonecoord = nil + local colorret = nil + local maxdist = 1000000 -- 100km + local zoneret = nil + local zonewret = nil + local zonenameret = nil + for _,_cargozone in pairs(zonetable) do + local czone = _cargozone -- #CTLD.CargoZone 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, [4] = self.shipZones } - for i = 1, 4 do - for index, cargozone in pairs( zones[i] ) do - local CZone = cargozone -- #CTLD.CargoZone - local zonename = CZone.name - local zone = nil - if i == 4 then - zone = UNIT:FindByName( zonename ) + local zonename = czone.name + local active = czone.active + local color = czone.color + local zone = nil + local zoneradius = 100 + local zonewidth = 20 + if Zonetype == CTLD.CargoZoneType.SHIP then + self:T("Checking Type Ship: "..zonename) + zone = UNIT:FindByName(zonename) + zonecoord = zone:GetCoordinate() + zoneradius = czone.shiplength + zonewidth = czone.shipwidth + else + zone = ZONE:FindByName(zonename) + zonecoord = zone:GetCoordinate() + zoneradius = zone:GetRadius() + zonewidth = zoneradius + end + local distance = self:_GetDistance(zonecoord,unitcoord) + if distance <= zoneradius and active then + outcome = true + end + if maxdist > distance then + maxdist = distance + zoneret = zone + zonenameret = zonename + zonewret = zonewidth + colorret = color + end + end + if Zonetype == CTLD.CargoZoneType.SHIP then + return outcome, zonenameret, zoneret, maxdist, zonewret + else + return outcome, zonenameret, zoneret, maxdist + end +end + +--- User function - Drop a smoke or flare at current location. +-- @param #CTLD self +-- @param Wrapper.Unit#UNIT Unit The Unit. +-- @param #boolean Flare If true, flare instead. +function CTLD:SmokePositionNow(Unit, Flare) + self:T(self.lid .. " SmokePositionNow") + local SmokeColor = self.SmokeColor or SMOKECOLOR.Red + local FlareColor = self.FlareColor or FLARECOLOR.Red + -- table of #CTLD.CargoZone table + local unitcoord = Unit:GetCoordinate() -- Core.Point#COORDINATE + local Group = Unit:GetGroup() + if Flare then + unitcoord:Flare(FlareColor, 90) + else + local height = unitcoord:GetLandHeight() + 2 + unitcoord.y = height + unitcoord:Smoke(SmokeColor) + end + return self +end + +--- User function - Start smoke/flare 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, [4] = self.shipZones} + for i=1,4 do + for index,cargozone in pairs(zones[i]) do + local CZone = cargozone --#CTLD.CargoZone + local zonename = CZone.name + local zone = nil + if i == 4 then + zone = UNIT:FindByName(zonename) + else + zone = ZONE:FindByName(zonename) + end + 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 - zone = ZONE:FindByName( zonename ) - end - 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 - self:_SendMessage( string.format( "Roger, %s zone %s!", txt, zonename ), 10, false, Group ) - smoked = true + 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 + self:_SendMessage(string.format("Roger, %s zone %s!",txt, zonename), 10, false, Group) + smoked = true end end - if not smoked then - local distance = UTILS.MetersToNM( self.smokedistance ) - self:_SendMessage( string.format( "Negative, need to be closer than %dnm to a zone!", distance ), 10, false, Group ) - end - return self end + if not smoked then + local distance = UTILS.MetersToNM(self.smokedistance) + self:_SendMessage(string.format("Negative, need to be closer than %dnm to a zone!",distance), 10, false, Group) + end + return self +end --- User - Function to add/adjust unittype capabilities. -- @param #CTLD self @@ -3501,14 +3461,14 @@ do -- @param #number Cratelimit Unit can carry number of crates. Default 0. -- @param #number Trooplimit Unit can carry number of troops. Default 0. -- @param #number Length Unit lenght (in mteres) for the load radius. Default 20. - function CTLD:UnitCapabilities( Unittype, Cancrates, Cantroops, Cratelimit, Trooplimit, Length ) - self:T( self.lid .. " UnitCapabilities" ) - local unittype = nil + function CTLD:UnitCapabilities(Unittype, Cancrates, Cantroops, Cratelimit, Trooplimit, Length) + self:T(self.lid .. " UnitCapabilities") + local unittype = nil local unit = nil - if type( Unittype ) == "string" then + if type(Unittype) == "string" then unittype = Unittype - elseif type( Unittype ) == "table" then - unit = UNIT:FindByName( Unittype ) -- Wrapper.Unit#UNIT + elseif type(Unittype) == "table" then + unit = UNIT:FindByName(Unittype) -- Wrapper.Unit#UNIT unittype = unit:GetTypeName() else return self @@ -3518,22 +3478,22 @@ do capabilities.type = unittype capabilities.crates = Cancrates or false capabilities.troops = Cantroops or false - capabilities.cratelimit = Cratelimit or 0 + capabilities.cratelimit = Cratelimit or 0 capabilities.trooplimit = Trooplimit or 0 capabilities.length = Length or 20 self.UnitTypes[unittype] = capabilities return self end - + --- (Internal) 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" ) + 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 + if self:IsUnitInAir(Unit) then -- get speed and height local uspeed = Unit:GetVelocityMPS() local uheight = Unit:GetHeight() @@ -3541,113 +3501,108 @@ do local gheight = ucoord:GetLandHeight() local aheight = uheight - gheight -- height above ground local maxh = self.maximumHoverHeight -- 15 - local minh = self.minimumHoverHeight -- 5 + local minh = self.minimumHoverHeight -- 5 local mspeed = 2 -- 2 m/s - if (uspeed <= mspeed) 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. + + --- (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" ) + 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 + 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 maxspeed = self.HercMaxSpeed -- 77 mps + local maxh = self.HercMinAngels-- 1500m + local minh = self.HercMaxAngels -- 5000m + local maxspeed = self.HercMaxSpeed -- 77 mps -- DONE: TEST - Speed test for Herc, should not be above 280kph/150kn local kmspeed = uspeed * 3.6 local knspeed = kmspeed / 1.86 - self:T( string.format( "%s Unit parameters: at %dm AGL with %dmps | %dkph | %dkn", self.lid, aheight, uspeed, kmspeed, knspeed ) ) - if (aheight <= maxh) and (aheight >= minh) and (uspeed <= maxspeed) then + self:T(string.format("%s Unit parameters: at %dm AGL with %dmps | %dkph | %dkn",self.lid,aheight,uspeed,kmspeed,knspeed)) + if (aheight <= maxh) and (aheight >= minh) and (uspeed <= maxspeed) then -- yep within parameters outcome = true end end return outcome end - + --- (Internal) 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 ) + function CTLD:_ShowHoverParams(Group,Unit) + local inhover = self:IsCorrectHover(Unit) local htxt = "true" - if not inhover then - htxt = "false" - end + if not inhover then htxt = "false" end local text = "" if _SETTINGS:IsMetric() then - 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 ) + 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) else - local minheight = UTILS.MetersToFeet( self.minimumHoverHeight ) - local maxheight = UTILS.MetersToFeet( self.maximumHoverHeight ) - text = string.format( "Hover parameters (autoload/drop):\n - Min height %dm \n - Max height %dm \n - Max speed 6fts \n - In parameter: %s", minheight, maxheight, htxt ) + local minheight = UTILS.MetersToFeet(self.minimumHoverHeight) + local maxheight = UTILS.MetersToFeet(self.maximumHoverHeight) + text = string.format("Hover parameters (autoload/drop):\n - Min height %dm \n - Max height %dm \n - Max speed 6fts \n - In parameter: %s", minheight, maxheight, htxt) end - self:_SendMessage( text, 10, false, Group ) + self:_SendMessage(text, 10, false, Group) return self end - - --- (Internal) List if a Herc unit is flying *in parameters*. + + --- (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 ) + function CTLD:_ShowFlightParams(Group,Unit) + local inhover = self:IsCorrectFlightParameters(Unit) local htxt = "true" - if not inhover then - htxt = "false" - end + if not inhover then htxt = "false" end local text = "" if _SETTINGS:IsImperial() then - local minheight = UTILS.MetersToFeet( self.HercMinAngels ) - local maxheight = UTILS.MetersToFeet( self.HercMaxAngels ) - text = string.format( "Flight parameters (airdrop):\n - Min height %dft \n - Max height %dft \n - In parameter: %s", minheight, maxheight, htxt ) + local minheight = UTILS.MetersToFeet(self.HercMinAngels) + local maxheight = UTILS.MetersToFeet(self.HercMaxAngels) + text = string.format("Flight parameters (airdrop):\n - Min height %dft \n - Max height %dft \n - In parameter: %s", minheight, maxheight, htxt) else local minheight = self.HercMinAngels local maxheight = self.HercMaxAngels - text = string.format( "Flight parameters (airdrop):\n - Min height %dm \n - Max height %dm \n - In parameter: %s", minheight, maxheight, htxt ) + text = string.format("Flight parameters (airdrop):\n - Min height %dm \n - Max height %dm \n - In parameter: %s", minheight, maxheight, htxt) end - self:_SendMessage( text, 10, false, Group ) + self:_SendMessage(text, 10, false, 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 ) + 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) if not outcome then - outcome = self:IsUnitInZone( Unit, CTLD.CargoZoneType.SHIP ) -- and self:IsCorrectHover(Unit) + outcome = self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP) --and self:IsCorrectHover(Unit) end return outcome end - + --- (Internal) Check if a unit is above ground. -- @param #CTLD self -- @param Wrapper.Unit#UNIT Unit -- @return #boolean Outcome - function CTLD:IsUnitInAir( Unit ) + function CTLD:IsUnitInAir(Unit) -- get speed and height local minheight = self.minimumHoverHeight if self.enableHercules and Unit:GetTypeName() == "Hercules" then @@ -3663,18 +3618,18 @@ do return false end end - + --- (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 - function CTLD:AutoHoverLoad( Unit ) - self:T( self.lid .. " AutoHoverLoad" ) + 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:_GetUnitCapabilities( Unit ) -- #CTLD.UnitCapabilities + local capabilities = self:_GetUnitCapabilities(Unit) -- #CTLD.UnitCapabilities local cancrates = capabilities.crates -- #boolean local cratelimit = capabilities.cratelimit -- #number if cancrates then @@ -3686,29 +3641,27 @@ do numberonboard = loaded.Cratesloaded or 0 end local load = cratelimit - numberonboard - local canload = self:CanHoverLoad( Unit ) + local canload = self:CanHoverLoad(Unit) if canload and load > 0 then - self:_LoadCratesNearby( Group, Unit ) + self:_LoadCratesNearby(Group,Unit) end end return self end - + --- (Internal) 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 ) - if self:CanHoverLoad( Unit ) then - self:AutoHoverLoad( Unit ) - end + for _,_pilot in pairs (self.CtldUnits) do + local Unit = UNIT:FindByName(_pilot) + if self:CanHoverLoad(Unit) then self:AutoHoverLoad(Unit) end end end return self end - + --- (Internal) Run through DroppedTroops and capture alive units -- @param #CTLD self -- @return #CTLD self @@ -3716,8 +3669,8 @@ do -- Troops local troops = self.DroppedTroops local newtable = {} - for _index, _group in pairs( troops ) do - self:T( { _group.ClassName } ) + for _index, _group in pairs (troops) do + self:T({_group.ClassName}) if _group and _group.ClassName == "GROUP" then if _group:IsAlive() then newtable[_index] = _group @@ -3728,9 +3681,9 @@ do -- Engineers local engineers = self.EngineersInField local engtable = {} - for _index, _group in pairs( engineers ) do - self:T( { _group.ClassName } ) - if _group and _group:IsNotStatus( "Stopped" ) then + for _index, _group in pairs (engineers) do + self:T({_group.ClassName}) + if _group and _group:IsNotStatus("Stopped") then engtable[_index] = _group end end @@ -3743,91 +3696,91 @@ do -- @param #string Name Name as defined in the generic cargo. -- @param #number Number Number of units/groups to add. -- @return #CTLD self - function CTLD:AddStockTroops( Name, Number ) + function CTLD:AddStockTroops(Name, Number) local name = Name or "none" local number = Number or 1 -- find right generic type local gentroops = self.Cargo_Troops - for _id, _troop in pairs( gentroops ) do -- #number, #CTLD_CARGO + for _id,_troop in pairs (gentroops) do -- #number, #CTLD_CARGO if _troop.Name == name then - _troop:AddStock( number ) + _troop:AddStock(number) end end end - + --- User - function to add stock of a certain crates type -- @param #CTLD self -- @param #string Name Name as defined in the generic cargo. -- @param #number Number Number of units/groups to add. -- @return #CTLD self - function CTLD:AddStockCrates( Name, Number ) + function CTLD:AddStockCrates(Name, Number) local name = Name or "none" local number = Number or 1 -- find right generic type local gentroops = self.Cargo_Crates - for _id, _troop in pairs( gentroops ) do -- #number, #CTLD_CARGO + for _id,_troop in pairs (gentroops) do -- #number, #CTLD_CARGO if _troop.Name == name then - _troop:AddStock( number ) + _troop:AddStock(number) end end end - + --- User - function to remove stock of a certain troops type -- @param #CTLD self -- @param #string Name Name as defined in the generic cargo. -- @param #number Number Number of units/groups to add. -- @return #CTLD self - function CTLD:RemoveStockTroops( Name, Number ) + function CTLD:RemoveStockTroops(Name, Number) local name = Name or "none" local number = Number or 1 -- find right generic type local gentroops = self.Cargo_Troops - for _id, _troop in pairs( gentroops ) do -- #number, #CTLD_CARGO + for _id,_troop in pairs (gentroops) do -- #number, #CTLD_CARGO if _troop.Name == name then - _troop:RemoveStock( number ) + _troop:RemoveStock(number) end end end - + --- User - function to remove stock of a certain crates type -- @param #CTLD self -- @param #string Name Name as defined in the generic cargo. -- @param #number Number Number of units/groups to add. -- @return #CTLD self - function CTLD:RemoveStockCrates( Name, Number ) + function CTLD:RemoveStockCrates(Name, Number) local name = Name or "none" local number = Number or 1 -- find right generic type local gentroops = self.Cargo_Crates - for _id, _troop in pairs( gentroops ) do -- #number, #CTLD_CARGO + for _id,_troop in pairs (gentroops) do -- #number, #CTLD_CARGO if _troop.Name == name then - _troop:RemoveStock( number ) + _troop:RemoveStock(number) end end return self end - + --- (Internal) Check on engineering teams -- @param #CTLD self -- @return #CTLD self function CTLD:_CheckEngineers() - self:T( self.lid .. " CheckEngineers" ) + self:T(self.lid.." CheckEngineers") local engtable = self.EngineersInField - for _ind, _engineers in pairs( engtable ) do + for _ind,_engineers in pairs (engtable) do local engineers = _engineers -- #CTLD_ENGINEERING local wrenches = engineers.Group -- Wrapper.Group#GROUP - self:T( _engineers.lid .. _engineers:GetStatus() ) + self:T(_engineers.lid .. _engineers:GetStatus()) if wrenches and wrenches:IsAlive() then - if engineers:IsStatus( "Running" ) or engineers:IsStatus( "Searching" ) then - local crates, number = self:_FindCratesNearby( wrenches, nil, self.EngineerSearch ) -- #table - engineers:Search( crates, number ) - elseif engineers:IsStatus( "Moving" ) then + if engineers:IsStatus("Running") or engineers:IsStatus("Searching") then + local crates,number = self:_FindCratesNearby(wrenches,nil, self.EngineerSearch) -- #table + engineers:Search(crates,number) + elseif engineers:IsStatus("Moving") then engineers:Move() - elseif engineers:IsStatus( "Arrived" ) then + elseif engineers:IsStatus("Arrived") then engineers:Build() - local unit = wrenches:GetUnit( 1 ) - self:_BuildCrates( wrenches, unit, true ) - self:_RepairCrates( wrenches, unit, true ) + local unit = wrenches:GetUnit(1) + self:_BuildCrates(wrenches,unit,true) + self:_RepairCrates(wrenches,unit,true) engineers:Done() end else @@ -3836,7 +3789,7 @@ do end return self end - + --- (User) Pre-populate troops in the field. -- @param #CTLD self -- @param Core.Zone#ZONE Zone The zone where to drop the troops. @@ -3849,15 +3802,15 @@ do -- local dropzone = ZONE:New("InjectZone") -- Core.Zone#ZONE -- -- and go: -- my_ctld:InjectTroops(dropzone,InjectTroopsType) - function CTLD:InjectTroops( Zone, Cargo ) - self:T( self.lid .. " InjectTroops" ) + function CTLD:InjectTroops(Zone,Cargo) + self:T(self.lid.." InjectTroops") local cargo = Cargo -- #CTLD_CARGO - - local function IsTroopsMatch( cargo ) + + local function IsTroopsMatch(cargo) local match = false local cgotbl = self.Cargo_Troops local name = cargo:GetName() - for _, _cgo in pairs( cgotbl ) do + for _,_cgo in pairs (cgotbl) do local cname = _cgo:GetName() if name == cname then match = true @@ -3866,14 +3819,14 @@ do end return match end - - if not IsTroopsMatch( cargo ) then + + if not IsTroopsMatch(cargo) then self.CargoCounter = self.CargoCounter + 1 cargo.ID = self.CargoCounter cargo.Stock = 1 - table.insert( self.Cargo_Troops, cargo ) + table.insert(self.Cargo_Troops,cargo) end - + local type = cargo:GetType() -- #CTLD_CARGO.Enum if (type == CTLD_CARGO.Enum.TROOPS or type == CTLD_CARGO.Enum.ENGINEERS) then -- unload @@ -3881,37 +3834,37 @@ do local temptable = cargo:GetTemplates() or {} local factor = 1.5 local zone = Zone - - local randomcoord = zone:GetRandomCoordinate( 10, 30 * factor ):GetVec2() - for _, _template in pairs( temptable ) do + + 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 ) ) - self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias( _template, alias ) - :InitRandomizeUnits( true, 20, 2 ) + 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 ) + :SpawnFromVec2(randomcoord) if self.movetroopstowpzone and type ~= CTLD_CARGO.Enum.ENGINEERS then - self:_MoveGroupToZone( self.DroppedTroops[self.TroopCounter] ) + self:_MoveGroupToZone(self.DroppedTroops[self.TroopCounter]) end end -- template loop - cargo:SetWasDropped( true ) + cargo:SetWasDropped(true) -- engineering group? if type == CTLD_CARGO.Enum.ENGINEERS then self.Engineers = self.Engineers + 1 local grpname = self.DroppedTroops[self.TroopCounter]:GetName() - self.EngineersInField[self.Engineers] = CTLD_ENGINEERING:New( name, grpname ) - -- self:I(string.format("%s Injected Engineers %s into action!",self.lid, name)) + self.EngineersInField[self.Engineers] = CTLD_ENGINEERING:New(name, grpname) + --self:I(string.format("%s Injected Engineers %s into action!",self.lid, name)) else - -- self:I(string.format("%s Injected Troops %s into action!",self.lid, name)) + --self:I(string.format("%s Injected Troops %s into action!",self.lid, name)) end if self.eventoninject then - self:__TroopsDeployed( 1, nil, nil, self.DroppedTroops[self.TroopCounter] ) + self:__TroopsDeployed(1,nil,nil,self.DroppedTroops[self.TroopCounter]) end end -- if type end return self end - - --- (User) Pre-populate vehicles in the field. + + --- (User) Pre-populate vehicles in the field. -- @param #CTLD self -- @param Core.Zone#ZONE Zone The zone where to drop the troops. -- @param Ops.CTLD#CTLD_CARGO Cargo The #CTLD_CARGO object to spawn. @@ -3923,15 +3876,15 @@ do -- local dropzone = ZONE:New("InjectZone") -- Core.Zone#ZONE -- -- and go: -- my_ctld:InjectVehicles(dropzone,InjectVehicleType) - function CTLD:InjectVehicles( Zone, Cargo ) - self:T( self.lid .. " InjectVehicles" ) + function CTLD:InjectVehicles(Zone,Cargo) + self:T(self.lid.." InjectVehicles") local cargo = Cargo -- #CTLD_CARGO - - local function IsVehicMatch( cargo ) + + local function IsVehicMatch(cargo) local match = false local cgotbl = self.Cargo_Crates local name = cargo:GetName() - for _, _cgo in pairs( cgotbl ) do + for _,_cgo in pairs (cgotbl) do local cname = _cgo:GetName() if name == cname then match = true @@ -3940,14 +3893,14 @@ do end return match end - - if not IsVehicMatch( cargo ) then + + if not IsVehicMatch(cargo) then self.CargoCounter = self.CargoCounter + 1 cargo.ID = self.CargoCounter cargo.Stock = 1 - table.insert( self.Cargo_Crates, cargo ) + table.insert(self.Cargo_Crates,cargo) end - + local type = cargo:GetType() -- #CTLD_CARGO.Enum if (type == CTLD_CARGO.Enum.VEHICLE or type == CTLD_CARGO.Enum.FOB) then -- unload @@ -3955,39 +3908,37 @@ do local temptable = cargo:GetTemplates() or {} local factor = 1.5 local zone = Zone - local randomcoord = zone:GetRandomCoordinate( 10, 30 * factor ):GetVec2() - cargo:SetWasDropped( true ) + local randomcoord = zone:GetRandomCoordinate(10,30*factor):GetVec2() + cargo:SetWasDropped(true) local canmove = false - if type == CTLD_CARGO.Enum.VEHICLE then - canmove = true - end - for _, _template in pairs( temptable ) do + if type == CTLD_CARGO.Enum.VEHICLE then canmove = true end + for _,_template in pairs(temptable) do self.TroopCounter = self.TroopCounter + 1 - local alias = string.format( "%s-%d", _template, math.random( 1, 100000 ) ) + local alias = string.format("%s-%d", _template, math.random(1,100000)) if canmove then - self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias( _template, alias ) - :InitRandomizeUnits( true, 20, 2 ) + self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias) + :InitRandomizeUnits(true,20,2) :InitDelayOff() - :SpawnFromVec2( randomcoord ) + :SpawnFromVec2(randomcoord) else -- don't random position of e.g. SAM units build as FOB - self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias( _template, alias ) + self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias) :InitDelayOff() - :SpawnFromVec2( randomcoord ) + :SpawnFromVec2(randomcoord) end if self.movetroopstowpzone and canmove then - self:_MoveGroupToZone( self.DroppedTroops[self.TroopCounter] ) + self:_MoveGroupToZone(self.DroppedTroops[self.TroopCounter]) end if self.eventoninject then - self:__CratesBuild( 1, nil, nil, self.DroppedTroops[self.TroopCounter] ) + self:__CratesBuild(1,nil,nil,self.DroppedTroops[self.TroopCounter]) end end -- end loop end -- if type end return self end - - ------------------------------------------------------------------- - -- FSM functions - ------------------------------------------------------------------- + +------------------------------------------------------------------- +-- FSM functions +------------------------------------------------------------------- --- (Internal) FSM Function onafterStart. -- @param #CTLD self @@ -3995,31 +3946,31 @@ do -- @param #string Event Trigger. -- @param #string To State. -- @return #CTLD self - function CTLD:onafterStart( From, Event, To ) - self:T( { From, Event, To } ) - self:I( self.lid .. "Started (" .. self.version .. ")" ) + function CTLD:onafterStart(From, Event, To) + self:T({From, Event, To}) + self:I(self.lid .. "Started ("..self.version..")") if self.useprefix or self.enableHercules then local prefix = self.prefixes if self.enableHercules then - self.PilotGroups = SET_GROUP:New():FilterCoalitions( self.coalitiontxt ):FilterPrefixes( prefix ):FilterStart() + self.PilotGroups = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(prefix):FilterStart() else - self.PilotGroups = SET_GROUP:New():FilterCoalitions( self.coalitiontxt ):FilterPrefixes( prefix ):FilterCategories( "helicopter" ):FilterStart() + self.PilotGroups = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(prefix):FilterCategories("helicopter"):FilterStart() end else - self.PilotGroups = SET_GROUP:New():FilterCoalitions( self.coalitiontxt ):FilterCategories( "helicopter" ):FilterStart() + self.PilotGroups = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterCategories("helicopter"):FilterStart() end -- Events - self:HandleEvent( EVENTS.PlayerEnterAircraft, self._EventHandler ) - self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventHandler ) - self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventHandler ) - self:__Status( -5 ) - + self:HandleEvent(EVENTS.PlayerEnterAircraft, self._EventHandler) + self:HandleEvent(EVENTS.PlayerEnterUnit, self._EventHandler) + self:HandleEvent(EVENTS.PlayerLeaveUnit, self._EventHandler) + self:__Status(-5) + -- AutoSave if self.enableLoadSave then local interval = self.saveinterval local filename = self.filename local filepath = self.filepath - self:__Save( interval, filepath, filename ) + self:__Save(interval,filepath,filename) end return self end @@ -4030,8 +3981,8 @@ do -- @param #string Event Trigger. -- @param #string To State. -- @return #CTLD self - function CTLD:onbeforeStatus( From, Event, To ) - self:T( { From, Event, To } ) + function CTLD:onbeforeStatus(From, Event, To) + self:T({From, Event, To}) self:CleanDroppedTroops() self:_RefreshF10Menus() self:_RefreshRadioBeacons() @@ -4039,71 +3990,71 @@ do self:_CheckEngineers() return self end - + --- (Internal) 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:T( { From, Event, To } ) - -- gather some stats + function CTLD:onafterStatus(From, Event, To) + self:T({From, Event, To}) + -- gather some stats -- pilots local pilots = 0 - for _, _pilot in pairs( self.CtldUnits ) do - pilots = pilots + 1 + 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 + for _,_pilot in pairs (self.Spawned_Cargo) do + boxes = boxes + 1 end - - local cc = self.CargoCounter + + 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() + + 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() if self.verbose > 0 then - self:I( self.lid .. "Cargo and Troops in Stock:" ) - for _, _troop in pairs( self.Cargo_Crates ) do + self:I(self.lid.."Cargo and Troops in Stock:") + for _,_troop in pairs (self.Cargo_Crates) do local name = _troop:GetName() local stock = _troop:GetStock() - self:I( string.format( "-- %s \t\t\t %d", name, stock ) ) + self:I(string.format("-- %s \t\t\t %d", name, stock)) end - for _, _troop in pairs( self.Cargo_Statics ) do + for _,_troop in pairs (self.Cargo_Statics) do local name = _troop:GetName() local stock = _troop:GetStock() - self:I( string.format( "-- %s \t\t\t %d", name, stock ) ) + self:I(string.format("-- %s \t\t\t %d", name, stock)) end - for _, _troop in pairs( self.Cargo_Troops ) do + for _,_troop in pairs (self.Cargo_Troops) do local name = _troop:GetName() local stock = _troop:GetStock() - self:I( string.format( "-- %s \t\t %d", name, stock ) ) + self:I(string.format("-- %s \t\t %d", name, stock)) end end end - self:__Status( -30 ) + self:__Status(-30) return self end - + --- (Internal) 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 ) + 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 - + --- (Internal) FSM Function onbeforeTroopsPickedUp. -- @param #CTLD self -- @param #string From State. @@ -4113,12 +4064,12 @@ do -- @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:T( { From, Event, To } ) + function CTLD:onbeforeTroopsPickedUp(From, Event, To, Group, Unit, Cargo) + self:T({From, Event, To}) return self end - - --- (Internal) FSM Function onbeforeCratesPickedUp. + + --- (Internal) FSM Function onbeforeCratesPickedUp. -- @param #CTLD self -- @param #string From State . -- @param #string Event Trigger. @@ -4127,25 +4078,26 @@ do -- @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:T( { From, Event, To } ) + function CTLD:onbeforeCratesPickedUp(From, Event, To, Group, Unit, Cargo) + self:T({From, Event, To}) return self end - - --- (Internal) FSM Function onbeforeTroopsExtracted. - -- @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:onbeforeTroopsExtracted( From, Event, To, Group, Unit, Troops ) - self:T( { From, Event, To } ) - return self - end - + + --- (Internal) FSM Function onbeforeTroopsExtracted. + -- @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:onbeforeTroopsExtracted(From, Event, To, Group, Unit, Troops) + self:T({From, Event, To}) + return self + end + + --- (Internal) FSM Function onbeforeTroopsDeployed. -- @param #CTLD self -- @param #string From State. @@ -4155,11 +4107,11 @@ do -- @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:T( { From, Event, To } ) + function CTLD:onbeforeTroopsDeployed(From, Event, To, Group, Unit, Troops) + self:T({From, Event, To}) return self end - + --- (Internal) FSM Function onbeforeCratesDropped. -- @param #CTLD self -- @param #string From State. @@ -4169,11 +4121,11 @@ do -- @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:T( { From, Event, To } ) + function CTLD:onbeforeCratesDropped(From, Event, To, Group, Unit, Cargotable) + self:T({From, Event, To}) return self end - + --- (Internal) FSM Function onbeforeCratesBuild. -- @param #CTLD self -- @param #string From State. @@ -4183,11 +4135,11 @@ do -- @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:T( { From, Event, To } ) + function CTLD:onbeforeCratesBuild(From, Event, To, Group, Unit, Vehicle) + self:T({From, Event, To}) return self end - + --- (Internal) FSM Function onbeforeTroopsRTB. -- @param #CTLD self -- @param #string From State. @@ -4196,11 +4148,11 @@ do -- @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 } ) + function CTLD:onbeforeTroopsRTB(From, Event, To, Group, Unit) + self:T({From, Event, To}) return self end - + --- On before "Save" event. Checks if io and lfs are available. -- @param #CTLD self -- @param #string From From state. @@ -4208,26 +4160,26 @@ do -- @param #string To To state. -- @param #string path (Optional) Path where the file is saved. Default is the DCS root installation folder or your "Saved Games\\DCS" folder if the lfs module is desanitized. -- @param #string filename (Optional) File name for saving. Default is "CTLD__Persist.csv". - function CTLD:onbeforeSave( From, Event, To, path, filename ) - self:T( { From, Event, To, path, filename } ) + function CTLD:onbeforeSave(From, Event, To, path, filename) + self:T({From, Event, To, path, filename}) if not self.enableLoadSave then return self end -- Thanks to @FunkyFranky -- Check io module is available. if not io then - self:E( self.lid .. "ERROR: io not desanitized. Can't save current state." ) + self:E(self.lid.."ERROR: io not desanitized. Can't save current state.") return false end - + -- Check default path. - if path == nil and not lfs then - self:E( self.lid .. "WARNING: lfs not desanitized. State will be saved in DCS installation root directory rather than your \"Saved Games\\DCS\" folder." ) + if path==nil and not lfs then + self:E(self.lid.."WARNING: lfs not desanitized. State will be saved in DCS installation root directory rather than your \"Saved Games\\DCS\" folder.") end - + return true end - + --- On after "Save" event. Player data is saved to file. -- @param #CTLD self -- @param #string From From state. @@ -4235,90 +4187,89 @@ do -- @param #string To To state. -- @param #string path Path where the file is saved. If nil, file is saved in the DCS root installtion directory or your "Saved Games" folder if lfs was desanitized. -- @param #string filename (Optional) File name for saving. Default is Default is "CTLD__Persist.csv". - function CTLD:onafterSave( From, Event, To, path, filename ) - self:T( { From, Event, To, path, filename } ) + function CTLD:onafterSave(From, Event, To, path, filename) + self:T({From, Event, To, path, filename}) -- Thanks to @FunkyFranky if not self.enableLoadSave then return self end --- Function that saves data to file - local function _savefile( filename, data ) - local f = assert( io.open( filename, "wb" ) ) - f:write( data ) + local function _savefile(filename, data) + local f = assert(io.open(filename, "wb")) + f:write(data) f:close() end - + -- Set path or default. if lfs then - path = self.filepath or lfs.writedir() + path=self.filepath or lfs.writedir() end - + -- Set file name. - filename = filename or self.filename - + filename=filename or self.filename + -- Set path. - if path ~= nil then - filename = path .. "\\" .. filename + if path~=nil then + filename=path.."\\"..filename end - + local grouptable = self.DroppedTroops -- #table local cgovehic = self.Cargo_Crates local cgotable = self.Cargo_Troops local stcstable = self.Spawned_Cargo - + local statics = nil local statics = {} - self:I( self.lid .. "Bulding Statics Table for Saving" ) - for _, _cargo in pairs( stcstable ) do + self:T(self.lid.."Bulding Statics Table for Saving") + for _,_cargo in pairs (stcstable) do local cargo = _cargo -- #CTLD_CARGO local object = cargo:GetPositionable() -- Wrapper.Static#STATIC if object and object:IsAlive() and cargo:WasDropped() then - self:I( { _cargo } ) - statics[#statics + 1] = cargo + self:I({_cargo}) + statics[#statics+1] = cargo end end - + -- find matching cargo - local function FindCargoType( name, table ) + local function FindCargoType(name,table) -- name matching a template in the table local match = false local cargo = nil - for _ind, _cargo in pairs( table ) do + for _ind,_cargo in pairs (table) do local thiscargo = _cargo -- #CTLD_CARGO local template = thiscargo:GetTemplates() - if type( template ) == "string" then + if type(template) == "string" then template = { template } end - for _, _name in pairs( template ) do - -- self:I(string.format("*** Saving CTLD: Matching %s with %s",name,_name)) - if string.find( name, _name ) and _cargo:GetType() ~= CTLD_CARGO.Enum.REPAIR then + for _,_name in pairs (template) do + --self:I(string.format("*** Saving CTLD: Matching %s with %s",name,_name)) + if string.find(name,_name) and _cargo:GetType() ~= CTLD_CARGO.Enum.REPAIR then match = true cargo = thiscargo end end - if match then - break - end + if match then break end end return match, cargo end - - -- local data = "LoadedData = {\n" + + + --local data = "LoadedData = {\n" local data = "Group,x,y,z,CargoName,CargoTemplates,CargoType,CratesNeeded,CrateMass\n" local n = 0 - for _, _grp in pairs( grouptable ) do + for _,_grp in pairs(grouptable) do local group = _grp -- Wrapper.Group#GROUP if group and group:IsAlive() then -- get template name local name = group:GetName() - local template = string.gsub( name, "-(.+)$", "" ) - if string.find( template, "#" ) then - template = string.gsub( name, "#(%d+)$", "" ) + local template = string.gsub(name,"-(.+)$","") + if string.find(template,"#") then + template = string.gsub(name,"#(%d+)$","") end - - local match, cargo = FindCargoType( template, cgotable ) + + local match, cargo = FindCargoType(template,cgotable) if not match then - match, cargo = FindCargoType( template, cgovehic ) + match, cargo = FindCargoType(template,cgovehic) end if match then n = n + 1 @@ -4328,54 +4279,56 @@ do local cgotype = cargo.CargoType local cgoneed = cargo.CratesNeeded local cgomass = cargo.PerCrateMass - - if type( cgotemp ) == "table" then + + if type(cgotemp) == "table" then local templates = "{" - for _, _tmpl in pairs( cgotemp ) do + for _,_tmpl in pairs(cgotemp) do templates = templates .. _tmpl .. ";" end templates = templates .. "}" cgotemp = templates end - + local location = group:GetVec3() - local txt = string.format( "%s,%d,%d,%d,%s,%s,%s,%d,%d\n", template, location.x, location.y, location.z, cgoname, cgotemp, cgotype, cgoneed, cgomass ) + local txt = string.format("%s,%d,%d,%d,%s,%s,%s,%d,%d\n" + ,template,location.x,location.y,location.z,cgoname,cgotemp,cgotype,cgoneed,cgomass) data = data .. txt end end end - - for _, _cgo in pairs( statics ) do + + for _,_cgo in pairs(statics) do local object = _cgo -- #CTLD_CARGO local cgoname = object.Name local cgotemp = object.Templates - - if type( cgotemp ) == "table" then + + if type(cgotemp) == "table" then local templates = "{" - for _, _tmpl in pairs( cgotemp ) do + for _,_tmpl in pairs(cgotemp) do templates = templates .. _tmpl .. ";" end templates = templates .. "}" cgotemp = templates end - + local cgotype = object.CargoType local cgoneed = object.CratesNeeded local cgomass = object.PerCrateMass local crateobj = object.Positionable local location = crateobj:GetVec3() - local txt = string.format( "%s,%d,%d,%d,%s,%s,%s,%d,%d\n", "STATIC", location.x, location.y, location.z, cgoname, cgotemp, cgotype, cgoneed, cgomass ) + local txt = string.format("%s,%d,%d,%d,%s,%s,%s,%d,%d\n" + ,"STATIC",location.x,location.y,location.z,cgoname,cgotemp,cgotype,cgoneed,cgomass) data = data .. txt end - - _savefile( filename, data ) - + + _savefile(filename, data) + -- AutoSave if self.enableLoadSave then local interval = self.saveinterval local filename = self.filename local filepath = self.filepath - self:__Save( interval, filepath, filename ) + self:__Save(interval,filepath,filename) end return self end @@ -4387,58 +4340,58 @@ do -- @param #string To To state. -- @param #string path (Optional) Path where the file is located. Default is the DCS root installation folder or your "Saved Games\\DCS" folder if the lfs module is desanitized. -- @param #string filename (Optional) File name for loading. Default is "CTLD__Persist.csv". - function CTLD:onbeforeLoad( From, Event, To, path, filename ) - self:T( { From, Event, To, path, filename } ) + function CTLD:onbeforeLoad(From, Event, To, path, filename) + self:T({From, Event, To, path, filename}) if not self.enableLoadSave then return self end --- Function that check if a file exists. - local function _fileexists( name ) - local f = io.open( name, "r" ) - if f ~= nil then - io.close( f ) + local function _fileexists(name) + local f=io.open(name,"r") + if f~=nil then + io.close(f) return true else return false end end - + -- Set file name and path - filename = filename or self.filename + filename=filename or self.filename path = path or self.filepath - + -- Check io module is available. if not io then - self:E( self.lid .. "WARNING: io not desanitized. Cannot load file." ) + self:E(self.lid.."WARNING: io not desanitized. Cannot load file.") return false end - + -- Check default path. - if path == nil and not lfs then - self:E( self.lid .. "WARNING: lfs not desanitized. State will be saved in DCS installation root directory rather than your \"Saved Games\\DCS\" folder." ) + if path==nil and not lfs then + self:E(self.lid.."WARNING: lfs not desanitized. State will be saved in DCS installation root directory rather than your \"Saved Games\\DCS\" folder.") end - + -- Set path or default. if lfs then - path = path or lfs.writedir() + path=path or lfs.writedir() end - + -- Set path. - if path ~= nil then - filename = path .. "\\" .. filename + if path~=nil then + filename=path.."\\"..filename end - + -- Check if file exists. - local exists = _fileexists( filename ) - + local exists=_fileexists(filename) + if exists then return true else - self:E( self.lid .. string.format( "WARNING: State file %s might not exist.", filename ) ) + self:E(self.lid..string.format("WARNING: State file %s might not exist.", filename)) return false - -- return self + --return self end - + end --- On after "Load" event. Loads dropped units from file. @@ -4448,98 +4401,98 @@ do -- @param #string To To state. -- @param #string path (Optional) Path where the file is located. Default is the DCS root installation folder or your "Saved Games\\DCS" folder if the lfs module is desanitized. -- @param #string filename (Optional) File name for loading. Default is "CTLD__Persist.csv". - function CTLD:onafterLoad( From, Event, To, path, filename ) - self:T( { From, Event, To, path, filename } ) + function CTLD:onafterLoad(From, Event, To, path, filename) + self:T({From, Event, To, path, filename}) if not self.enableLoadSave then return self end --- Function that loads data from a file. - local function _loadfile( filename ) - local f = assert( io.open( filename, "rb" ) ) - local data = f:read( "*all" ) + local function _loadfile(filename) + local f=assert(io.open(filename, "rb")) + local data=f:read("*all") f:close() return data end - + -- Set file name and path - filename = filename or self.filename + filename=filename or self.filename path = path or self.filepath - + -- Set path or default. if lfs then - path = path or lfs.writedir() + path=path or lfs.writedir() end - + -- Set path. - if path ~= nil then - filename = path .. "\\" .. filename + if path~=nil then + filename=path.."\\"..filename end - + -- Info message. - local text = string.format( "Loading CTLD state from file %s", filename ) - MESSAGE:New( text, 10 ):ToAllIf( self.Debug ) - self:I( self.lid .. text ) - - local file = assert( io.open( filename, "rb" ) ) - + local text=string.format("Loading CTLD state from file %s", filename) + MESSAGE:New(text,10):ToAllIf(self.Debug) + self:I(self.lid..text) + + local file=assert(io.open(filename, "rb")) + local loadeddata = {} for line in file:lines() do - -- self:I({line=type(line)}) - loadeddata[#loadeddata + 1] = line + --self:I({line=type(line)}) + loadeddata[#loadeddata+1] = line end file:close() - + -- remove header - table.remove( loadeddata, 1 ) - - for _id, _entry in pairs( loadeddata ) do - local dataset = UTILS.Split( _entry, "," ) + table.remove(loadeddata, 1) + + for _id,_entry in pairs (loadeddata) do + local dataset = UTILS.Split(_entry,",") -- 1=Group,2=x,3=y,4=z,5=CargoName,6=CargoTemplates,7=CargoType,8=CratesNeeded,9=CrateMass local groupname = dataset[1] local vec2 = {} - vec2.x = tonumber( dataset[2] ) - vec2.y = tonumber( dataset[4] ) + vec2.x = tonumber(dataset[2]) + vec2.y = tonumber(dataset[4]) local cargoname = dataset[5] local cargotype = dataset[7] - if type( groupname ) == "string" and groupname ~= "STATIC" then + if type(groupname) == "string" and groupname ~= "STATIC" then local cargotemplates = dataset[6] - cargotemplates = string.gsub( cargotemplates, "{", "" ) - cargotemplates = string.gsub( cargotemplates, "}", "" ) - cargotemplates = UTILS.Split( cargotemplates, ";" ) - local size = tonumber( dataset[8] ) - local mass = tonumber( dataset[9] ) - -- self:I({groupname,vec3,cargoname,cargotemplates,cargotype,size,mass}) + cargotemplates = string.gsub(cargotemplates,"{","") + cargotemplates = string.gsub(cargotemplates,"}","") + cargotemplates = UTILS.Split(cargotemplates,";") + local size = tonumber(dataset[8]) + local mass = tonumber(dataset[9]) + --self:I({groupname,vec3,cargoname,cargotemplates,cargotype,size,mass}) -- inject at Vec2 - local dropzone = ZONE_RADIUS:New( "DropZone", vec2, 20 ) + local dropzone = ZONE_RADIUS:New("DropZone",vec2,20) if cargotype == CTLD_CARGO.Enum.VEHICLE or cargotype == CTLD_CARGO.Enum.FOB then - local injectvehicle = CTLD_CARGO:New( nil, cargoname, cargotemplates, cargotype, true, true, size, nil, true, mass ) - self:InjectVehicles( dropzone, injectvehicle ) + local injectvehicle = CTLD_CARGO:New(nil,cargoname,cargotemplates,cargotype,true,true,size,nil,true,mass) + self:InjectVehicles(dropzone,injectvehicle) elseif cargotype == CTLD_CARGO.Enum.TROOPS or cargotype == CTLD_CARGO.Enum.ENGINEERS then - local injecttroops = CTLD_CARGO:New( nil, cargoname, cargotemplates, cargotype, true, true, size, nil, true, mass ) - self:InjectTroops( dropzone, injecttroops ) + local injecttroops = CTLD_CARGO:New(nil,cargoname,cargotemplates,cargotype,true,true,size,nil,true,mass) + self:InjectTroops(dropzone,injecttroops) end - elseif (type( groupname ) == "string" and groupname == "STATIC") or cargotype == CTLD_CARGO.Enum.REPAIR then + elseif (type(groupname) == "string" and groupname == "STATIC") or cargotype == CTLD_CARGO.Enum.REPAIR then local cargotemplates = dataset[6] - local size = tonumber( dataset[8] ) - local mass = tonumber( dataset[9] ) - local dropzone = ZONE_RADIUS:New( "DropZone", vec2, 20 ) + local size = tonumber(dataset[8]) + local mass = tonumber(dataset[9]) + local dropzone = ZONE_RADIUS:New("DropZone",vec2,20) -- STATIC,-84037,154,834021,Humvee,{Humvee;},Vehicle,1,100 -- STATIC,-84036,154,834018,Ammunition-1,ammo_cargo,Static,1,500 local injectstatic = nil if cargotype == CTLD_CARGO.Enum.VEHICLE or cargotype == CTLD_CARGO.Enum.FOB then - cargotemplates = string.gsub( cargotemplates, "{", "" ) - cargotemplates = string.gsub( cargotemplates, "}", "" ) - cargotemplates = UTILS.Split( cargotemplates, ";" ) - injectstatic = CTLD_CARGO:New( nil, cargoname, cargotemplates, cargotype, true, true, size, nil, true, mass ) + cargotemplates = string.gsub(cargotemplates,"{","") + cargotemplates = string.gsub(cargotemplates,"}","") + cargotemplates = UTILS.Split(cargotemplates,";") + injectstatic = CTLD_CARGO:New(nil,cargoname,cargotemplates,cargotype,true,true,size,nil,true,mass) elseif cargotype == CTLD_CARGO.Enum.STATIC or cargotype == CTLD_CARGO.Enum.REPAIR then - injectstatic = CTLD_CARGO:New( nil, cargoname, cargotemplates, cargotype, true, true, size, nil, true, mass ) + injectstatic = CTLD_CARGO:New(nil,cargoname,cargotemplates,cargotype,true,true,size,nil,true,mass) end if injectstatic then - self:InjectStatics( dropzone, injectstatic ) + self:InjectStatics(dropzone,injectstatic) end - end + end end - + return self end end -- end do