diff --git a/Moose Development/Moose/Core/Fsm.lua b/Moose Development/Moose/Core/Fsm.lua index af2e971fe..e86b88267 100644 --- a/Moose Development/Moose/Core/Fsm.lua +++ b/Moose Development/Moose/Core/Fsm.lua @@ -79,6 +79,7 @@ do -- FSM + --- FSM class -- @type FSM -- @field #string ClassName Name of the class. -- @field Core.Scheduler#SCHEDULER CallScheduler Call scheduler. diff --git a/Moose Development/Moose/Core/Pathline.lua b/Moose Development/Moose/Core/Pathline.lua index 8fdda2bc6..bc6e9305e 100644 --- a/Moose Development/Moose/Core/Pathline.lua +++ b/Moose Development/Moose/Core/Pathline.lua @@ -49,7 +49,7 @@ -- -- # Mark on F10 map -- --- The ponints of the PATHLINE can be marked on the F10 map with the @{#PATHLINE.MarkPoints}(`true`) function. The mark points contain information of the surface type, land height and +-- The points of the PATHLINE can be marked on the F10 map with the @{#PATHLINE.MarkPoints}(`true`) function. The mark points contain information of the surface type, land height and -- water depth. -- -- To remove the marks, use @{#PATHLINE.MarkPoints}(`false`). @@ -69,11 +69,12 @@ PATHLINE = { -- @field #number landHeight Land height in meters. -- @field #number depth Water depth in meters. -- @field #number markerID Marker ID. +-- @field #number lineID Line marker ID. --- PATHLINE class version. -- @field #string version -PATHLINE.version="0.1.1" +PATHLINE.version="0.2.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -301,18 +302,42 @@ function PATHLINE:GetPoint2DFromIndex(n) return nil end +--- Calculate the length of the line. +-- @param #PATHLINE self +-- @param #boolean Project2D Calculate 2D distance between points. +-- @return #number Length in meters. +function PATHLINE:GetLength(Project2D) + local l=0 + + local np=#self.points + + for i=1,np-1 do + local p1=self.points[i] --#PATHLINE.Point + local p2=self.points[i+1] --#PATHLINE.Point + + if Project2D then + l=l+UTILS.VecDist2D(p1.vec2, p2.vec2) + else + l=l+UTILS.VecDist3D(p1.vec3, p2.vec3) + end + + end + + return l +end + --- Mark points on F10 map. -- @param #PATHLINE self -- @param #boolean Switch If `true` or nil, set marks. If `false`, remove marks. --- @return List of DCS#Vec3 points. +-- @return #PATHLINE self function PATHLINE:MarkPoints(Switch) for i,_point in pairs(self.points) do local point=_point --#PATHLINE.Point if Switch==false then if point.markerID then - UTILS.RemoveMark(point.markerID, Delay) + UTILS.RemoveMark(point.markerID) end else @@ -329,6 +354,56 @@ function PATHLINE:MarkPoints(Switch) end end + return self +end + +--- Draw line on F10 map. +-- @param #PATHLINE self +-- @param #number Recipient Recipent of the line: -1=All. +-- @param #table Color Color as RGB table plus alpha value. Default {1, 0, 0, 1.0}. +-- @param #number LineType Line type: 1=Solid (default). +-- @return #PATHLINE self +function PATHLINE:DrawLine(Recipient, Color, LineType) + + -- Input + Recipient= Recipient or -1 + Color= Color or {1,0,0, 1.0} + LineType=LineType or 1 + local ReadOnly=false + + + local np=#self.points + + for i=1,np-1 do + local p1=self.points[i] --#PATHLINE.Point + local p2=self.points[i+1] --#PATHLINE.Point + + p1.lineID = UTILS.GetMarkID() + + trigger.action.lineToAll(Recipient, p1.lineID, p1.vec3, p2.vec3, Color, LineType, ReadOnly, "") + + end + + return self +end + +--- Remove line on F10 map. +-- @param #PATHLINE self +-- @param #number Delay Delay in seconds before line is removed. +-- @return #PATHLINE self +function PATHLINE:UnDrawLine(Delay) + + + local np=#self.points + + for _,_point in pairs(self.points) do + local p=_point --#PATHLINE.Point + if p.lineID then + UTILS.RemoveMark(p.lineID, Delay) + end + end + + return self end diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index fcf67ca02..729a5116d 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -2060,6 +2060,40 @@ do -- COORDINATE return Path, Way, GotPath end + + --- Returns a table of coordinates to a destination using only roads or railroads. + -- The first point is the closest point on road of the given coordinate. + -- By default, the last point is the closest point on road of the ToCoord. Hence, the coordinate itself and the final ToCoord are not necessarily included in the path. + -- @param #COORDINATE self + -- @param #COORDINATE ToCoord Coordinate of destination. + -- @param #boolean IncludeEndpoints (Optional) Include the coordinate itself and the ToCoordinate in the path. + -- @param #boolean Railroad (Optional) If true, path on railroad is returned. Default false. + -- @return Core.Pathline#PATHLINE Pathline containing the points on road. If no path on road can be found, nil is returned or just the endpoints. + function COORDINATE:GetPathlineOnRoad(ToCoord, IncludeEndpoints, Railroad) + + -- Set road type. + local RoadType="roads" + if Railroad==true then + RoadType="railroads" + end + + -- DCS API function returning a table of vec2. + local path = land.findPathOnRoads(RoadType, self.x, self.z, ToCoord.x, ToCoord.z) + + if IncludeEndpoints then + path=path or {} + table.insert(path, 1, self:GetVec2()) + table.insert(path, ToCoord:GetVec2()) + end + + local pathline=nil + if path then + pathline=PATHLINE:NewFromVec2Array(RoadType, path) + end + + return pathline + end + --- Gets the surface type at the coordinate. -- @param #COORDINATE self diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 2935391dc..f9134d6a5 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -89,11 +89,23 @@ do -- SET_BASE Index = {}, Database = nil, CallScheduler = nil, - + Filter = {}, + FilterCoalitionNumbers = { + [coalition.side.RED+1] = "red", + [coalition.side.BLUE+1] = "blue", + [coalition.side.NEUTRAL+1] = "neutral", + }, + FilterMeta = { + Coalitions = { + ["red"] = coalition.side.RED, + ["blue"] = coalition.side.BLUE, + ["neutral"] = coalition.side.NEUTRAL, + }, + }, } --- Filters - -- @type SET_BASE.Filters + -- @type SET_BASE.Filter -- @field #table Coalition Coalitions -- @field #table Prefix Prefixes. @@ -103,14 +115,14 @@ do -- SET_BASE -- @usage -- -- Define a new SET_BASE Object. This DBObject will contain a reference to all Group and Unit Templates defined within the ME and the DCSRTE. -- DBObject = SET_BASE:New() - function SET_BASE:New( Database ) + function SET_BASE:New(Database) -- Inherits from BASE - local self = BASE:Inherit( self, FSM:New() ) -- Core.Set#SET_BASE + local self = BASE:Inherit(self, FSM:New()) -- Core.Set#SET_BASE self.Database = Database - self:SetStartState( "Started" ) + self:SetStartState("Started") --- Added Handler OnAfter for SET_BASE -- @function [parent=#SET_BASE] OnAfterAdded @@ -121,7 +133,7 @@ do -- SET_BASE -- @param #string ObjectName The name of the object. -- @param Object The object. - self:AddTransition( "*", "Added", "*" ) + self:AddTransition("*", "Added", "*") --- Removed Handler OnAfter for SET_BASE -- @function [parent=#SET_BASE] OnAfterRemoved @@ -132,7 +144,7 @@ do -- SET_BASE -- @param #string ObjectName The name of the object. -- @param Object The object. - self:AddTransition( "*", "Removed", "*" ) + self:AddTransition("*", "Removed", "*") self.YieldInterval = 10 self.TimeInterval = 0.001 @@ -140,9 +152,9 @@ do -- SET_BASE self.Set = {} self.Index = {} - self.CallScheduler = SCHEDULER:New( self ) + self.CallScheduler = SCHEDULER:New(self) - self:SetEventPriority( 2 ) + self:SetEventPriority(2) return self end @@ -191,18 +203,44 @@ do -- SET_BASE -- @return #SET_BASE self function SET_BASE:Clear(TriggerEvent) - for Name, Object in pairs( self.Set ) do - self:Remove( Name, not TriggerEvent ) + for Name, Object in pairs(self.Set) do + self:Remove(Name, not TriggerEvent) end return self end + + --- Builds a set of objects of same coalitions. + -- Possible current coalitions are red, blue and neutral. + -- @param #SET_BASE self + -- @param #string Coalitions Can take the following values: "red", "blue", "neutral" and coalition.side.RED, coalition.side.BLUE,coalition.side.NEUTRAL + -- @param #boolean Clear If `true`, clear any previously defined filters. + -- @return #SET_BASE self + function SET_BASE:FilterCoalitions(Coalitions, Clear) + + if Clear or (not self.Filter.Coalitions) then + self.Filter.Coalitions = {} + end + + -- Ensure table. + if type(Coalitions) ~= "table" then Coalitions = {Coalitions} end + for CoalitionID, Coalition in pairs(Coalitions) do + local coalition = Coalition + if type(Coalition) == "number" then + coalition = self.FilterCoalitionNumbers[Coalition+1] or "unknown" + --self:I("Filter Coaltion for "..coalition) + end + self.Filter.Coalitions[coalition] = coalition + end + + return self + end --- Finds an @{Core.Base#BASE} object based on the object Name. -- @param #SET_BASE self -- @param #string ObjectName -- @return Core.Base#BASE The Object found. - function SET_BASE:_Find( ObjectName ) + function SET_BASE:_Find(ObjectName) local ObjectFound = self.Set[ObjectName] return ObjectFound @@ -225,8 +263,8 @@ do -- SET_BASE local Names = {} - for Name, Object in pairs( self.Set ) do - table.insert( Names, Name ) + for Name, Object in pairs(self.Set) do + table.insert(Names, Name) end return Names @@ -240,8 +278,8 @@ do -- SET_BASE local Objects = {} - for Name, Object in pairs( self.Set ) do - table.insert( Objects, Object ) + for Name, Object in pairs(self.Set) do + table.insert(Objects, Object) end return Objects @@ -251,8 +289,8 @@ do -- SET_BASE -- @param #SET_BASE self -- @param #string ObjectName -- @param #boolean NoTriggerEvent (Optional) When `true`, the :Remove() method will not trigger a **Removed** event. - function SET_BASE:Remove( ObjectName, NoTriggerEvent ) - --self:F2( { ObjectName = ObjectName } ) + function SET_BASE:Remove(ObjectName, NoTriggerEvent) + --self:F2({ ObjectName = ObjectName }) local TriggerEvent = true if NoTriggerEvent then @@ -265,9 +303,9 @@ do -- SET_BASE if Object then - for Index, Key in ipairs( self.Index ) do + for Index, Key in ipairs(self.Index) do if Key == ObjectName then - table.remove( self.Index, Index ) + table.remove(self.Index, Index) self.Set[ObjectName] = nil break end @@ -275,7 +313,7 @@ do -- SET_BASE -- When NoTriggerEvent is true, then no Removed event will be triggered. if TriggerEvent then - self:Removed( ObjectName, Object ) + self:Removed(ObjectName, Object) end end end @@ -285,10 +323,10 @@ do -- SET_BASE -- @param #string ObjectName The name of the object. -- @param Core.Base#BASE Object The object itself. -- @return Core.Base#BASE The added BASE Object. - function SET_BASE:Add( ObjectName, Object ) + function SET_BASE:Add(ObjectName, Object) -- Debug info. - --self:T2( { ObjectName = ObjectName, Object = Object } ) + --self:T2({ ObjectName = ObjectName, Object = Object }) -- Error ahndling if not ObjectName or ObjectName == "" then @@ -299,17 +337,17 @@ do -- SET_BASE -- Ensure that the existing element is removed from the Set before a new one is inserted to the Set if self.Set[ObjectName] then - self:Remove( ObjectName, true ) + self:Remove(ObjectName, true) end -- Add object to set. self.Set[ObjectName] = Object -- Add Object name to Index. - table.insert( self.Index, ObjectName ) + table.insert(self.Index, ObjectName) -- Trigger Added event. - self:Added( ObjectName, Object ) + self:Added(ObjectName, Object) return self end @@ -318,12 +356,12 @@ do -- SET_BASE -- @param #SET_BASE self -- @param Wrapper.Object#OBJECT Object -- @return Core.Base#BASE The added BASE Object. - function SET_BASE:AddObject( Object ) - --self:F2( Object.ObjectName ) + function SET_BASE:AddObject(Object) + --self:F2(Object.ObjectName) - --self:T( Object.UnitName ) - --self:T( Object.ObjectName ) - self:Add( Object.ObjectName, Object ) + --self:T(Object.UnitName) + --self:T(Object.ObjectName) + self:Add(Object.ObjectName, Object) end @@ -360,16 +398,16 @@ do -- SET_BASE -- @param #SET_BASE self -- @param Core.Set#SET_BASE SetB Set *B*. -- @return Core.Set#SET_BASE The union set, i.e. contains objects that are in set *A* **or** in set *B*. - function SET_BASE:GetSetUnion( SetB ) + function SET_BASE:GetSetUnion(SetB) local union = SET_BASE:New() - for _, ObjectA in pairs( self.Set ) do - union:AddObject( ObjectA ) + for _, ObjectA in pairs(self.Set) do + union:AddObject(ObjectA) end - for _, ObjectB in pairs( SetB.Set ) do - union:AddObject( ObjectB ) + for _, ObjectB in pairs(SetB.Set) do + union:AddObject(ObjectB) end return union @@ -398,7 +436,7 @@ do -- SET_BASE -- @param #SET_BASE self -- @param Core.Set#SET_BASE SetB Set other set, called *B*. -- @return Core.Set#SET_BASE The set of objects that are in set *B* but **not** in this set *A*. - function SET_BASE:GetSetComplement( SetB ) + function SET_BASE:GetSetComplement(SetB) local complement = self:GetSetUnion(SetB) local intersection = self:GetSetIntersection(SetB) @@ -415,11 +453,11 @@ do -- SET_BASE -- @param Core.Set#SET_BASE SetA First set. -- @param Core.Set#SET_BASE SetB Set to be merged into first set. -- @return Core.Set#SET_BASE The set of objects that are included in SetA and SetB. - function SET_BASE:CompareSets( SetA, SetB ) + function SET_BASE:CompareSets(SetA, SetB) - for _, ObjectB in pairs( SetB.Set ) do - if SetA:IsIncludeObject( ObjectB ) then - SetA:Add( ObjectB ) + for _, ObjectB in pairs(SetB.Set) do + if SetA:IsIncludeObject(ObjectB) then + SetA:Add(ObjectB) end end @@ -430,12 +468,12 @@ do -- SET_BASE -- @param #SET_BASE self -- @param #string ObjectName -- @return Core.Base#BASE - function SET_BASE:Get( ObjectName ) - --self:F( ObjectName ) + function SET_BASE:Get(ObjectName) + --self:F(ObjectName) local Object = self.Set[ObjectName] - --self:T3( { ObjectName, Object } ) + --self:T3({ ObjectName, Object }) return Object end @@ -445,7 +483,7 @@ do -- SET_BASE function SET_BASE:GetFirst() local ObjectName = self.Index[1] local FirstObject = self.Set[ObjectName] - --self:T3( { FirstObject } ) + --self:T3({ FirstObject }) return FirstObject end @@ -456,7 +494,7 @@ do -- SET_BASE local tablemax = table.maxn(self.Index) local ObjectName = self.Index[tablemax] local LastObject = self.Set[ObjectName] - --self:T3( { LastObject } ) + --self:T3({ LastObject }) return LastObject end @@ -470,7 +508,7 @@ do -- SET_BASE end --local tablemax = table.maxn(self.Index) local RandomItem = self.Set[self.Index[math.random(1,tablemax)]] - --self:T3( { RandomItem } ) + --self:T3({ RandomItem }) return RandomItem end @@ -487,7 +525,7 @@ do -- SET_BASE --local tablemax = table.maxn(self.Index) --local RandomItem = self.Set[self.Index[math.random(1,tablemax)]] local RandomItem = sorted[math.random(1,tablemax)] - --self:T3( { RandomItem } ) + --self:T3({ RandomItem }) return RandomItem end @@ -502,10 +540,10 @@ do -- SET_BASE -- @param #SET_BASE self -- @param #SET_BASE BaseSet -- @return #SET_BASE - function SET_BASE:SetDatabase( BaseSet ) + function SET_BASE:SetDatabase(BaseSet) -- Copy the filter criteria of the BaseSet - local OtherFilter = UTILS.DeepCopy( BaseSet.Filter ) + local OtherFilter = UTILS.DeepCopy(BaseSet.Filter) self.Filter = OtherFilter -- Now base the new Set on the BaseSet @@ -517,7 +555,7 @@ do -- SET_BASE -- @param #SET_BASE self -- @param #number Limit Defines how many objects are evaluated of the set as part of the Some iterators. The default is 1. -- @return #SET_BASE self - function SET_BASE:SetSomeIteratorLimit( Limit ) + function SET_BASE:SetSomeIteratorLimit(Limit) self.SomeIteratorLimit = Limit or 1 @@ -554,10 +592,10 @@ do -- SET_BASE --self:Clear() - for ObjectName, Object in pairs( self.Database ) do + for ObjectName, Object in pairs(self.Database) do - if self:IsIncludeObject( Object ) then - self:Add( ObjectName, Object ) + if self:IsIncludeObject(Object) then + self:Add(ObjectName, Object) else self:Remove(ObjectName, true) end @@ -583,16 +621,16 @@ do -- SET_BASE -- @return #SET_BASE self function SET_BASE:_FilterStart() - for ObjectName, Object in pairs( self.Database ) do + for ObjectName, Object in pairs(self.Database) do - if self:IsIncludeObject( Object ) then - self:Add( ObjectName, Object ) + if self:IsIncludeObject(Object) then + self:Add(ObjectName, Object) end end -- Follow alive players and clients - -- self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit ) - -- self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventOnPlayerLeaveUnit ) + -- self:HandleEvent(EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit) + -- self:HandleEvent(EVENTS.PlayerLeaveUnit, self._EventOnPlayerLeaveUnit) return self end @@ -602,7 +640,7 @@ do -- SET_BASE -- @return #SET_BASE self function SET_BASE:FilterDeads() -- R2.1 allow deads to be filtered to automatically handle deads in the collection. - self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) + self:HandleEvent(EVENTS.Dead, self._EventOnDeadOrCrash) return self end @@ -612,7 +650,7 @@ do -- SET_BASE -- @return #SET_BASE self function SET_BASE:FilterCrashes() -- R2.1 allow crashes to be filtered to automatically handle crashes in the collection. - self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) + self:HandleEvent(EVENTS.Crash, self._EventOnDeadOrCrash) return self end @@ -622,9 +660,9 @@ do -- SET_BASE -- @return #SET_BASE self function SET_BASE:FilterStop() - self:UnHandleEvent( EVENTS.Birth ) - self:UnHandleEvent( EVENTS.Dead ) - self:UnHandleEvent( EVENTS.Crash ) + self:UnHandleEvent(EVENTS.Birth) + self:UnHandleEvent(EVENTS.Dead) + self:UnHandleEvent(EVENTS.Crash) return self end @@ -634,19 +672,19 @@ do -- SET_BASE -- @param Core.Point#COORDINATE Coordinate A @{Core.Point#COORDINATE} object (but **not** a simple DCS#Vec2!) from where to evaluate the closest object in the set. -- @return Core.Base#BASE The closest object. -- @usage - -- myset:FindNearestObjectFromPointVec2( ZONE:New("Test Zone"):GetCoordinate() ) - function SET_BASE:FindNearestObjectFromPointVec2( Coordinate ) - --self:F2( Coordinate ) + -- myset:FindNearestObjectFromPointVec2(ZONE:New("Test Zone"):GetCoordinate()) + function SET_BASE:FindNearestObjectFromPointVec2(Coordinate) + --self:F2(Coordinate) local NearestObject = nil local ClosestDistance = nil - for ObjectID, ObjectData in pairs( self.Set ) do + for ObjectID, ObjectData in pairs(self.Set) do if NearestObject == nil then NearestObject = ObjectData - ClosestDistance = Coordinate:DistanceFromPointVec2( ObjectData:GetCoordinate() ) + ClosestDistance = Coordinate:DistanceFromPointVec2(ObjectData:GetCoordinate()) else - local Distance = Coordinate:DistanceFromPointVec2( ObjectData:GetCoordinate() ) + local Distance = Coordinate:DistanceFromPointVec2(ObjectData:GetCoordinate()) if Distance < ClosestDistance then NearestObject = ObjectData ClosestDistance = Distance @@ -662,15 +700,15 @@ do -- SET_BASE --- Handles the OnBirth event for the Set. -- @param #SET_BASE self -- @param Core.Event#EVENTDATA Event - function SET_BASE:_EventOnBirth( Event ) - --self:F3( { Event } ) + function SET_BASE:_EventOnBirth(Event) + --self:F3({ Event }) if Event.IniDCSUnit then - local ObjectName, Object = self:AddInDatabase( Event ) - --self:T3( ObjectName, Object ) - if Object and self:IsIncludeObject( Object ) then - self:Add( ObjectName, Object ) - -- self:_EventOnPlayerEnterUnit( Event ) + local ObjectName, Object = self:AddInDatabase(Event) + --self:T3(ObjectName, Object) + if Object and self:IsIncludeObject(Object) then + self:Add(ObjectName, Object) + -- self:_EventOnPlayerEnterUnit(Event) end end end @@ -678,13 +716,13 @@ do -- SET_BASE --- Handles the OnDead or OnCrash event for alive units set. -- @param #SET_BASE self -- @param Core.Event#EVENTDATA Event - function SET_BASE:_EventOnDeadOrCrash( Event ) - --self:F( { Event } ) + function SET_BASE:_EventOnDeadOrCrash(Event) + --self:F({ Event }) if Event.IniDCSUnit then - local ObjectName, Object = self:FindInDatabase( Event ) + local ObjectName, Object = self:FindInDatabase(Event) if ObjectName then - self:Remove( ObjectName ) + self:Remove(ObjectName) end end end @@ -692,15 +730,15 @@ do -- SET_BASE --- Handles the OnPlayerEnterUnit event to fill the active players table (with the unit filter applied). -- @param #SET_BASE self -- @param Core.Event#EVENTDATA Event - -- function SET_BASE:_EventOnPlayerEnterUnit( Event ) - -- --self:F3( { Event } ) + -- function SET_BASE:_EventOnPlayerEnterUnit(Event) + -- --self:F3({ Event }) -- -- if Event.IniDCSUnit then - -- local ObjectName, Object = self:AddInDatabase( Event ) - -- self:T3( ObjectName, Object ) - -- if self:IsIncludeObject( Object ) then - -- self:Add( ObjectName, Object ) - -- --self:_EventOnPlayerEnterUnit( Event ) + -- local ObjectName, Object = self:AddInDatabase(Event) + -- self:T3(ObjectName, Object) + -- if self:IsIncludeObject(Object) then + -- self:Add(ObjectName, Object) + -- --self:_EventOnPlayerEnterUnit(Event) -- end -- end -- end @@ -708,15 +746,15 @@ do -- SET_BASE --- Handles the OnPlayerLeaveUnit event to clean the active players table. -- @param #SET_BASE self -- @param Core.Event#EVENTDATA Event - -- function SET_BASE:_EventOnPlayerLeaveUnit( Event ) - -- --self:F3( { Event } ) + -- function SET_BASE:_EventOnPlayerLeaveUnit(Event) + -- --self:F3({ Event }) -- -- local ObjectName = Event.IniDCSUnit -- if Event.IniDCSUnit then -- if Event.IniDCSGroup then -- local GroupUnits = Event.IniDCSGroup:getUnits() -- local PlayerCount = 0 - -- for _, DCSUnit in pairs( GroupUnits ) do + -- for _, DCSUnit in pairs(GroupUnits) do -- if DCSUnit ~= Event.IniDCSUnit then -- if DCSUnit:getPlayerName() ~= nil then -- PlayerCount = PlayerCount + 1 @@ -725,7 +763,7 @@ do -- SET_BASE -- end -- self:E(PlayerCount) -- if PlayerCount == 0 then - -- self:Remove( Event.IniDCSGroupName ) + -- self:Remove(Event.IniDCSGroupName) -- end -- end -- end @@ -741,43 +779,43 @@ do -- SET_BASE -- @param #function Function (Optional) A function returning a #boolean true/false. Only if true, the IteratorFunction is called. -- @param #table FunctionArguments (Optional) Function arguments. -- @return #SET_BASE self - function SET_BASE:ForEach( IteratorFunction, arg, Set, Function, FunctionArguments ) - --self:F3( arg ) + function SET_BASE:ForEach(IteratorFunction, arg, Set, Function, FunctionArguments) + --self:F3(arg) Set = Set or self:GetSet() arg = arg or {} local function CoRoutine() local Count = 0 - for ObjectID, ObjectData in pairs( Set ) do + for ObjectID, ObjectData in pairs(Set) do local Object = ObjectData - --self:T3( Object ) + --self:T3(Object) if Function then - if Function( unpack( FunctionArguments or {} ), Object ) == true then - IteratorFunction( Object, unpack( arg ) ) + if Function(unpack(FunctionArguments or {}), Object) == true then + IteratorFunction(Object, unpack(arg)) end else - IteratorFunction( Object, unpack( arg ) ) + IteratorFunction(Object, unpack(arg)) end Count = Count + 1 -- if Count % self.YieldInterval == 0 then - -- coroutine.yield( false ) + -- coroutine.yield(false) -- end end return true end - -- local co = coroutine.create( CoRoutine ) + -- local co = coroutine.create(CoRoutine) local co = CoRoutine local function Schedule() - -- local status, res = coroutine.resume( co ) + -- local status, res = coroutine.resume(co) local status, res = co() - --self:T3( { status, res } ) + --self:T3({ status, res }) if status == false then - error( res ) + error(res) end if res == false then return true -- resume next time the loop @@ -786,7 +824,7 @@ do -- SET_BASE return false end - -- self.CallScheduler:Schedule( self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0 ) + -- self.CallScheduler:Schedule(self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0) Schedule() return self @@ -796,8 +834,8 @@ do -- SET_BASE -- @param #SET_BASE self -- @param #function IteratorFunction The function that will be called. -- @return #SET_BASE self - function SET_BASE:ForSome( IteratorFunction, arg, Set, Function, FunctionArguments ) - --self:F3( arg ) + function SET_BASE:ForSome(IteratorFunction, arg, Set, Function, FunctionArguments) + --self:F3(arg) Set = Set or self:GetSet() arg = arg or {} @@ -806,38 +844,38 @@ do -- SET_BASE local function CoRoutine() local Count = 0 - for ObjectID, ObjectData in pairs( Set ) do + for ObjectID, ObjectData in pairs(Set) do local Object = ObjectData - --self:T3( Object ) + --self:T3(Object) if Function then - if Function( unpack( FunctionArguments ), Object ) == true then - IteratorFunction( Object, unpack( arg ) ) + if Function(unpack(FunctionArguments), Object) == true then + IteratorFunction(Object, unpack(arg)) end else - IteratorFunction( Object, unpack( arg ) ) + IteratorFunction(Object, unpack(arg)) end Count = Count + 1 if Count >= Limit then break end -- if Count % self.YieldInterval == 0 then - -- coroutine.yield( false ) + -- coroutine.yield(false) -- end end return true end - -- local co = coroutine.create( CoRoutine ) + -- local co = coroutine.create(CoRoutine) local co = CoRoutine local function Schedule() - -- local status, res = coroutine.resume( co ) + -- local status, res = coroutine.resume(co) local status, res = co() - --self:T3( { status, res } ) + --self:T3({ status, res }) if status == false then - error( res ) + error(res) end if res == false then return true -- resume next time the loop @@ -846,7 +884,7 @@ do -- SET_BASE return false end - -- self.CallScheduler:Schedule( self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0 ) + -- self.CallScheduler:Schedule(self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0) Schedule() return self @@ -857,10 +895,10 @@ do -- SET_BASE -- @param #SET_BASE self -- @param #function IteratorFunction The function that will be called when there is an alive unit in the SET_BASE. The function needs to accept a UNIT parameter. ---- @return #SET_BASE self - -- function SET_BASE:ForEachDCSUnitAlive( IteratorFunction, ... ) - -- --self:F3( arg ) + -- function SET_BASE:ForEachDCSUnitAlive(IteratorFunction, ...) + -- --self:F3(arg) -- - -- self:ForEach( IteratorFunction, arg, self.DCSUnitsAlive ) + -- self:ForEach(IteratorFunction, arg, self.DCSUnitsAlive) -- -- return self -- end @@ -869,10 +907,10 @@ do -- SET_BASE -- @param #SET_BASE self -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_BASE. The function needs to accept a UNIT parameter. ---- @return #SET_BASE self - -- function SET_BASE:ForEachPlayer( IteratorFunction, ... ) - -- --self:F3( arg ) + -- function SET_BASE:ForEachPlayer(IteratorFunction, ...) + -- --self:F3(arg) -- - -- self:ForEach( IteratorFunction, arg, self.PlayersAlive ) + -- self:ForEach(IteratorFunction, arg, self.PlayersAlive) -- -- return self -- end @@ -882,10 +920,10 @@ do -- SET_BASE -- @param #SET_BASE self -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_BASE. The function needs to accept a CLIENT parameter. ---- @return #SET_BASE self - -- function SET_BASE:ForEachClient( IteratorFunction, ... ) - -- --self:F3( arg ) + -- function SET_BASE:ForEachClient(IteratorFunction, ...) + -- --self:F3(arg) -- - -- self:ForEach( IteratorFunction, arg, self.Clients ) + -- self:ForEach(IteratorFunction, arg, self.Clients) -- -- return self -- end @@ -894,8 +932,8 @@ do -- SET_BASE -- @param #SET_BASE self -- @param #table Object -- @return #SET_BASE self - function SET_BASE:IsIncludeObject( Object ) - --self:F3( Object ) + function SET_BASE:IsIncludeObject(Object) + --self:F3(Object) return true end @@ -904,8 +942,8 @@ do -- SET_BASE -- @param #SET_BASE self -- @param #table Object -- @return #boolean `true` if object is in set and `false` otherwise. - function SET_BASE:IsInSet( Object ) - --self:F3( Object ) + function SET_BASE:IsInSet(Object) + --self:F3(Object) local outcome = false local name = Object:GetName() --self:I("SET_BASE: Objectname = "..name) @@ -916,7 +954,7 @@ do -- SET_BASE outcome = true end end - ) + ) return outcome end @@ -924,8 +962,8 @@ do -- SET_BASE -- @param #SET_BASE self -- @param #table Object -- @return #SET_BASE self - function SET_BASE:IsNotInSet( Object ) - --self:F3( Object ) + function SET_BASE:IsNotInSet(Object) + --self:F3(Object) return not self:IsInSet(Object) end @@ -936,7 +974,7 @@ do -- SET_BASE --self:F3() local ObjectNames = "" - for ObjectName, Object in pairs( self.Set ) do + for ObjectName, Object in pairs(self.Set) do ObjectNames = ObjectNames .. ObjectName .. ", " end @@ -947,14 +985,14 @@ do -- SET_BASE -- @param #SET_BASE self -- @param Core.Base#BASE MasterObject (Optional) The master object as a reference. -- @return #string A string with the names of the objects. - function SET_BASE:Flush( MasterObject ) + function SET_BASE:Flush(MasterObject) --self:F3() local ObjectNames = "" - for ObjectName, Object in pairs( self.Set ) do + for ObjectName, Object in pairs(self.Set) do ObjectNames = ObjectNames .. ObjectName .. ", " end - --self:F( { MasterObject = MasterObject and MasterObject:GetClassNameAndID(), "Objects in Set:", ObjectNames } ) + --self:F({ MasterObject = MasterObject and MasterObject:GetClassNameAndID(), "Objects in Set:", ObjectNames }) return ObjectNames end @@ -967,7 +1005,7 @@ do -- SET_BASE local AliveSet = {} -- Clean the Set before returning with only the alive Objects. - for ObjectName, Object in pairs( self.Set ) do + for ObjectName, Object in pairs(self.Set) do if Object then if Object:IsAlive() then AliveSet[#AliveSet+1] = Object @@ -1060,12 +1098,12 @@ do -- -- -- Create the SetCarrier SET_GROUP collection. -- - -- local SetHelicopter = SET_GROUP:New():FilterPrefixes( "Helicopter" ):FilterStart() + -- local SetHelicopter = SET_GROUP:New():FilterPrefixes("Helicopter"):FilterStart() -- -- -- Put a Dead event handler on SetCarrier, to ensure that when a carrier is destroyed, that all internal parameters are reset. -- - -- function SetHelicopter:OnAfterDead( From, Event, To, GroupObject ) - -- --self:F( { GroupObject = GroupObject:GetName() } ) + -- function SetHelicopter:OnAfterDead(From, Event, To, GroupObject) + -- --self:F({ GroupObject = GroupObject:GetName() }) -- end -- -- While this is a good example, there is a catch. @@ -1077,15 +1115,15 @@ do -- -- Within that constructor, we want to set an enclosed event handler OnAfterDead for SetHelicopter. -- -- But within the OnAfterDead method, we want to refer to the self variable of the AI_CARGO_DISPATCHER. -- - -- function AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZones ) + -- function AI_CARGO_DISPATCHER:New(SetCarrier, SetCargo, SetDeployZones) -- - -- local self = BASE:Inherit( self, FSM:New() ) -- #AI_CARGO_DISPATCHER + -- local self = BASE:Inherit(self, FSM:New()) -- #AI_CARGO_DISPATCHER -- -- -- Put a Dead event handler on SetCarrier, to ensure that when a carrier is destroyed, that all internal parameters are reset. -- -- Note the "." notation, and the explicit declaration of SetHelicopter, which would be using the ":" notation the implicit self variable declaration. -- - -- function SetHelicopter.OnAfterDead( SetHelicopter, From, Event, To, GroupObject ) - -- SetHelicopter:F( { GroupObject = GroupObject:GetName() } ) + -- function SetHelicopter.OnAfterDead(SetHelicopter, From, Event, To, GroupObject) + -- SetHelicopter:F({ GroupObject = GroupObject:GetName() }) -- self.PickupCargo[GroupObject] = nil -- So here I clear the PickupCargo table entry of the self object AI_CARGO_DISPATCHER. -- self.CarrierHome[GroupObject] = nil -- end @@ -1130,9 +1168,9 @@ do function SET_GROUP:New() -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.GROUPS ) ) -- #SET_GROUP + local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.GROUPS)) -- #SET_GROUP - self:FilterActive( false ) + self:FilterActive(false) return self @@ -1153,7 +1191,7 @@ do --local AliveSet = SET_GROUP:New() local AliveSet = {} -- Clean the Set before returning with only the alive Groups. - for GroupName, GroupObject in pairs( self.Set ) do + for GroupName, GroupObject in pairs(self.Set) do local GroupObject = GroupObject -- Wrapper.Group#GROUP if GroupObject then if GroupObject:IsAlive() then @@ -1176,9 +1214,9 @@ do local ReportUnitTypes = REPORT:New() - for GroupID, GroupData in pairs( self:GetSet() ) do + for GroupID, GroupData in pairs(self:GetSet()) do local Units = GroupData:GetUnits() - for UnitID, UnitData in pairs( Units ) do + for UnitID, UnitData in pairs(Units) do if UnitData:IsAlive() then local UnitType = UnitData:GetTypeName() @@ -1191,8 +1229,8 @@ do end end - for UnitTypeID, UnitType in pairs( UnitTypes ) do - ReportUnitTypes:Add( UnitType .. " of " .. UnitTypeID ) + for UnitTypeID, UnitType in pairs(UnitTypes) do + ReportUnitTypes:Add(UnitType .. " of " .. UnitTypeID) end return ReportUnitTypes @@ -1204,14 +1242,14 @@ do -- @param Wrapper.Group#GROUP group The group which should be added to the set. -- @param #boolean DontSetCargoBayLimit If true, do not attempt to auto-add the cargo bay limit per unit in this group. -- @return Core.Set#SET_GROUP self - function SET_GROUP:AddGroup( group, DontSetCargoBayLimit ) + function SET_GROUP:AddGroup(group, DontSetCargoBayLimit) - self:Add( group:GetName(), group ) + self:Add(group:GetName(), group) if not DontSetCargoBayLimit then -- I set the default cargo bay weight limit each time a new group is added to the set. -- TODO Why is this here in the first place? - for UnitID, UnitData in pairs( group:GetUnits() or {} ) do + for UnitID, UnitData in pairs(group:GetUnits() or {}) do if UnitData and UnitData:IsAlive() then UnitData:SetCargoBayWeightLimit() end @@ -1225,12 +1263,12 @@ do -- @param Core.Set#SET_GROUP self -- @param #string AddGroupNames A single name or an array of GROUP names. -- @return Core.Set#SET_GROUP self - function SET_GROUP:AddGroupsByName( AddGroupNames ) + function SET_GROUP:AddGroupsByName(AddGroupNames) - local AddGroupNamesArray = (type( AddGroupNames ) == "table") and AddGroupNames or { AddGroupNames } + local AddGroupNamesArray = (type(AddGroupNames) == "table") and AddGroupNames or { AddGroupNames } - for AddGroupID, AddGroupName in pairs( AddGroupNamesArray ) do - self:Add( AddGroupName, GROUP:FindByName( AddGroupName ) ) + for AddGroupID, AddGroupName in pairs(AddGroupNamesArray) do + self:Add(AddGroupName, GROUP:FindByName(AddGroupName)) end return self @@ -1240,12 +1278,12 @@ do -- @param Core.Set#SET_GROUP self -- @param Wrapper.Group#GROUP RemoveGroupNames A single name or an array of GROUP names. -- @return Core.Set#SET_GROUP self - function SET_GROUP:RemoveGroupsByName( RemoveGroupNames ) + function SET_GROUP:RemoveGroupsByName(RemoveGroupNames) - local RemoveGroupNamesArray = (type( RemoveGroupNames ) == "table") and RemoveGroupNames or { RemoveGroupNames } + local RemoveGroupNamesArray = (type(RemoveGroupNames) == "table") and RemoveGroupNames or { RemoveGroupNames } - for RemoveGroupID, RemoveGroupName in pairs( RemoveGroupNamesArray ) do - self:Remove( RemoveGroupName ) + for RemoveGroupID, RemoveGroupName in pairs(RemoveGroupNamesArray) do + self:Remove(RemoveGroupName) end return self @@ -1255,7 +1293,7 @@ do -- @param #SET_GROUP self -- @param #string GroupName -- @return Wrapper.Group#GROUP The found Group. - function SET_GROUP:FindGroup( GroupName ) + function SET_GROUP:FindGroup(GroupName) local GroupFound = self.Set[GroupName] return GroupFound @@ -1265,20 +1303,20 @@ do -- @param #SET_GROUP self -- @param Core.Point#COORDINATE Coordinate A @{Core.Point#COORDINATE} object from where to evaluate the closest object in the set. -- @return Wrapper.Group#GROUP The closest group. - function SET_GROUP:FindNearestGroupFromPointVec2( Coordinate ) - --self:F2( Coordinate ) + function SET_GROUP:FindNearestGroupFromPointVec2(Coordinate) + --self:F2(Coordinate) local NearestGroup = nil -- Wrapper.Group#GROUP local ClosestDistance = nil local Set = self:GetAliveSet() - for ObjectID, ObjectData in pairs( Set ) do + for ObjectID, ObjectData in pairs(Set) do if NearestGroup == nil then NearestGroup = ObjectData - ClosestDistance = Coordinate:DistanceFromPointVec2( ObjectData:GetCoordinate() ) + ClosestDistance = Coordinate:DistanceFromPointVec2(ObjectData:GetCoordinate()) else - local Distance = Coordinate:DistanceFromPointVec2( ObjectData:GetCoordinate() ) + local Distance = Coordinate:DistanceFromPointVec2(ObjectData:GetCoordinate()) if Distance < ClosestDistance then NearestGroup = ObjectData ClosestDistance = Distance @@ -1294,7 +1332,7 @@ do -- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE -- @param #boolean Clear If `true`, clear any previously defined filters. -- @return #SET_GROUP self - function SET_GROUP:FilterZones( Zones, Clear ) + function SET_GROUP:FilterZones(Zones, Clear) if Clear or not self.Filter.Zones then self.Filter.Zones = {} @@ -1303,14 +1341,14 @@ do local zones = {} if Zones.ClassName and Zones.ClassName == "SET_ZONE" then zones = Zones.Set - elseif type( Zones ) ~= "table" or (type( Zones ) == "table" and Zones.ClassName) then - self:E( "***** FilterZones needs either a table of ZONE Objects or a SET_ZONE as parameter!" ) + elseif type(Zones) ~= "table" or (type(Zones) == "table" and Zones.ClassName) then + self:E("***** FilterZones needs either a table of ZONE Objects or a SET_ZONE as parameter!") return self else zones = Zones end - for _, Zone in pairs( zones ) do + for _, Zone in pairs(zones) do local zonename = Zone:GetName() self.Filter.Zones[zonename] = Zone end @@ -1333,7 +1371,7 @@ do -- if grp:GetName() == "Exclude Me" then isinclude = false end -- return isinclude -- end - -- ):FilterOnce() + -- ):FilterOnce() -- BASE:I(groundset:Flush()) @@ -1343,21 +1381,6 @@ do -- @param #string Coalitions Can take the following values: "red", "blue", "neutral". -- @param #boolean Clear If `true`, clear any previously defined filters. -- @return #SET_GROUP self - function SET_GROUP:FilterCoalitions( Coalitions, Clear ) - - if Clear or (not self.Filter.Coalitions) then - self.Filter.Coalitions = {} - end - - -- Ensure table. - Coalitions = UTILS.EnsureTable(Coalitions, false) - - for CoalitionID, Coalition in pairs( Coalitions ) do - self.Filter.Coalitions[Coalition] = Coalition - end - - return self - end --- Builds a set of groups out of categories. -- Possible current categories are plane, helicopter, ground, ship. @@ -1365,17 +1388,17 @@ do -- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship". -- @param #boolean Clear If `true`, clear any previously defined filters. -- @return #SET_GROUP self - function SET_GROUP:FilterCategories( Categories, Clear ) + function SET_GROUP:FilterCategories(Categories, Clear) if Clear or not self.Filter.Categories then self.Filter.Categories = {} end - if type( Categories ) ~= "table" then + if type(Categories) ~= "table" then Categories = { Categories } end - for CategoryID, Category in pairs( Categories ) do + for CategoryID, Category in pairs(Categories) do self.Filter.Categories[Category] = Category end @@ -1386,7 +1409,7 @@ do -- @param #SET_GROUP self -- @return #SET_GROUP self function SET_GROUP:FilterCategoryGround() - self:FilterCategories( "ground" ) + self:FilterCategories("ground") return self end @@ -1394,7 +1417,7 @@ do -- @param #SET_GROUP self -- @return #SET_GROUP self function SET_GROUP:FilterCategoryAirplane() - self:FilterCategories( "plane" ) + self:FilterCategories("plane") return self end @@ -1402,7 +1425,7 @@ do -- @param #SET_GROUP self -- @return #SET_GROUP self function SET_GROUP:FilterCategoryHelicopter() - self:FilterCategories( "helicopter" ) + self:FilterCategories("helicopter") return self end @@ -1410,7 +1433,7 @@ do -- @param #SET_GROUP self -- @return #SET_GROUP self function SET_GROUP:FilterCategoryShip() - self:FilterCategories( "ship" ) + self:FilterCategories("ship") return self end @@ -1418,7 +1441,7 @@ do -- @param #SET_GROUP self -- @return #SET_GROUP self function SET_GROUP:FilterCategoryStructure() - self:FilterCategories( "structure" ) + self:FilterCategories("structure") return self end @@ -1427,14 +1450,14 @@ do -- @param #SET_GROUP self -- @param #string Countries Can take those country strings known within DCS world. -- @return #SET_GROUP self - function SET_GROUP:FilterCountries( Countries ) + function SET_GROUP:FilterCountries(Countries) if not self.Filter.Countries then self.Filter.Countries = {} end - if type( Countries ) ~= "table" then + if type(Countries) ~= "table" then Countries = { Countries } end - for CountryID, Country in pairs( Countries ) do + for CountryID, Country in pairs(Countries) do self.Filter.Countries[Country] = Country end return self @@ -1445,14 +1468,14 @@ do -- @param #SET_GROUP self -- @param #string Prefixes The string pattern(s) that needs to be contained in the group name. Can also be passed as a `#table` of strings. -- @return #SET_GROUP self - function SET_GROUP:FilterPrefixes( Prefixes ) + function SET_GROUP:FilterPrefixes(Prefixes) if not self.Filter.GroupPrefixes then self.Filter.GroupPrefixes = {} end - if type( Prefixes ) ~= "table" then + if type(Prefixes) ~= "table" then Prefixes = { Prefixes } end - for PrefixID, Prefix in pairs( Prefixes ) do + for PrefixID, Prefix in pairs(Prefixes) do self.Filter.GroupPrefixes[Prefix] = Prefix end return self @@ -1465,10 +1488,10 @@ do local Database = _DATABASE.GROUPS - for ObjectName, Object in pairs( Database ) do - if self:IsIncludeObject( Object ) and self:IsNotInSet(Object) then - self:Add( ObjectName, Object ) - elseif (not self:IsIncludeObject( Object )) and self:IsInSet(Object) then + for ObjectName, Object in pairs(Database) do + if self:IsIncludeObject(Object) and self:IsNotInSet(Object) then + self:Add(ObjectName, Object) + elseif (not self:IsIncludeObject(Object)) and self:IsInSet(Object) then self:Remove(ObjectName) end end @@ -1489,15 +1512,15 @@ do -- GroupSet = SET_GROUP:New():FilterActive():FilterStart() -- -- -- Include only active groups to the set of the blue coalition, and filter one time. - -- GroupSet = SET_GROUP:New():FilterActive():FilterCoalition( "blue" ):FilterOnce() + -- GroupSet = SET_GROUP:New():FilterActive():FilterCoalition("blue"):FilterOnce() -- -- -- Include only active groups to the set of the blue coalition, and filter one time. -- -- Later, reset to include back inactive groups to the set. - -- GroupSet = SET_GROUP:New():FilterActive():FilterCoalition( "blue" ):FilterOnce() + -- GroupSet = SET_GROUP:New():FilterActive():FilterCoalition("blue"):FilterOnce() -- ... logic ... - -- GroupSet = SET_GROUP:New():FilterActive( false ):FilterCoalition( "blue" ):FilterOnce() + -- GroupSet = SET_GROUP:New():FilterActive(false):FilterCoalition("blue"):FilterOnce() -- - function SET_GROUP:FilterActive( Active ) + function SET_GROUP:FilterActive(Active) Active = Active or not (Active == false) self.Filter.Active = Active return self @@ -1518,12 +1541,12 @@ do if _DATABASE then self:_FilterStart() - self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) - self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.UnitLost, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventOnDeadOrCrash ) + self:HandleEvent(EVENTS.Birth, self._EventOnBirth) + self:HandleEvent(EVENTS.Dead, self._EventOnDeadOrCrash) + self:HandleEvent(EVENTS.Crash, self._EventOnDeadOrCrash) + self:HandleEvent(EVENTS.RemoveUnit, self._EventOnDeadOrCrash) + self:HandleEvent(EVENTS.UnitLost, self._EventOnDeadOrCrash) + self:HandleEvent(EVENTS.PlayerLeaveUnit, self._EventOnDeadOrCrash) if self.Filter.Zones then self.ZoneTimer = TIMER:New(self._ContinousZoneFilter,self) local timing = self.ZoneTimerInterval or 30 @@ -1570,11 +1593,11 @@ do -- Note: The GROUP object in the SET_GROUP collection will only be removed if the last unit is destroyed of the GROUP. -- @param #SET_GROUP self -- @param Core.Event#EVENTDATA Event - function SET_GROUP:_EventOnDeadOrCrash( Event ) - --self:F( { Event } ) + function SET_GROUP:_EventOnDeadOrCrash(Event) + --self:F({ Event }) if Event.IniDCSUnit then - local ObjectName, Object = self:FindInDatabase( Event ) + local ObjectName, Object = self:FindInDatabase(Event) if ObjectName then local size = 1 if Event.IniDCSGroup then @@ -1588,7 +1611,7 @@ do size = Object:CountAliveUnits() end if size == 1 then -- Only remove if the last unit of the group was destroyed. - self:Remove( ObjectName ) + self:Remove(ObjectName) end end end @@ -1600,13 +1623,13 @@ do -- @param Core.Event#EVENTDATA Event -- @return #string The name of the GROUP -- @return #table The GROUP - function SET_GROUP:AddInDatabase( Event ) - --self:F3( { Event } ) + function SET_GROUP:AddInDatabase(Event) + --self:F3({ Event }) if Event.IniObjectCategory == Object.Category.UNIT then if not self.Database[Event.IniDCSGroupName] then - self.Database[Event.IniDCSGroupName] = GROUP:Register( Event.IniDCSGroupName ) - --self:T(3( self.Database[Event.IniDCSGroupName] ) + self.Database[Event.IniDCSGroupName] = GROUP:Register(Event.IniDCSGroupName) + --self:T(3(self.Database[Event.IniDCSGroupName]) end end @@ -1619,8 +1642,8 @@ do -- @param Core.Event#EVENTDATA Event -- @return #string The name of the GROUP -- @return #table The GROUP - function SET_GROUP:FindInDatabase( Event ) - --self:F3( { Event } ) + function SET_GROUP:FindInDatabase(Event) + --self:F3({ Event }) return Event.IniDCSGroupName, self.Database[Event.IniDCSGroupName] end @@ -1629,10 +1652,10 @@ do -- @param #SET_GROUP self -- @param #function IteratorFunction The function that will be called for all GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. -- @return #SET_GROUP self - function SET_GROUP:ForEachGroup( IteratorFunction, ... ) - --self:F2( arg ) + function SET_GROUP:ForEachGroup(IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet() ) + self:ForEach(IteratorFunction, arg, self:GetSet()) return self end @@ -1641,10 +1664,10 @@ do -- @param #SET_GROUP self -- @param #function IteratorFunction The function that will be called for some GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. -- @return #SET_GROUP self - function SET_GROUP:ForSomeGroup( IteratorFunction, ... ) - --self:F2( arg ) + function SET_GROUP:ForSomeGroup(IteratorFunction, ...) + --self:F2(arg) - self:ForSome( IteratorFunction, arg, self:GetSet() ) + self:ForSome(IteratorFunction, arg, self:GetSet()) return self end @@ -1653,10 +1676,10 @@ do -- @param #SET_GROUP self -- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. -- @return #SET_GROUP self - function SET_GROUP:ForEachGroupAlive( IteratorFunction, ... ) - --self:F2( arg ) + function SET_GROUP:ForEachGroupAlive(IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetAliveSet() ) + self:ForEach(IteratorFunction, arg, self:GetAliveSet()) return self end @@ -1665,10 +1688,10 @@ do -- @param #SET_GROUP self -- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. -- @return #SET_GROUP self - function SET_GROUP:ForSomeGroupAlive( IteratorFunction, ... ) - --self:F2( arg ) + function SET_GROUP:ForSomeGroupAlive(IteratorFunction, ...) + --self:F2(arg) - self:ForSome( IteratorFunction, arg, self:GetAliveSet() ) + self:ForSome(IteratorFunction, arg, self:GetAliveSet()) return self end @@ -1694,19 +1717,19 @@ do -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. -- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. -- @return #SET_GROUP self - function SET_GROUP:ForEachGroupCompletelyInZone( ZoneObject, IteratorFunction, ... ) - --self:F2( arg ) + function SET_GROUP:ForEachGroupCompletelyInZone(ZoneObject, IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet(), + self:ForEach(IteratorFunction, arg, self:GetSet(), -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Group#GROUP GroupObject - function( ZoneObject, GroupObject ) - if GroupObject:IsCompletelyInZone( ZoneObject ) then + function(ZoneObject, GroupObject) + if GroupObject:IsCompletelyInZone(ZoneObject) then return true else return false end - end, { ZoneObject } ) + end, { ZoneObject }) return self end @@ -1716,19 +1739,19 @@ do -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. -- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. -- @return #SET_GROUP self - function SET_GROUP:ForEachGroupPartlyInZone( ZoneObject, IteratorFunction, ... ) - --self:F2( arg ) + function SET_GROUP:ForEachGroupPartlyInZone(ZoneObject, IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet(), + self:ForEach(IteratorFunction, arg, self:GetSet(), -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Group#GROUP GroupObject - function( ZoneObject, GroupObject ) - if GroupObject:IsPartlyInZone( ZoneObject ) then + function(ZoneObject, GroupObject) + if GroupObject:IsPartlyInZone(ZoneObject) then return true else return false end - end, { ZoneObject } ) + end, { ZoneObject }) return self end @@ -1738,19 +1761,19 @@ do -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. -- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. -- @return #SET_GROUP self - function SET_GROUP:ForEachGroupNotInZone( ZoneObject, IteratorFunction, ... ) - --self:F2( arg ) + function SET_GROUP:ForEachGroupNotInZone(ZoneObject, IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet(), + self:ForEach(IteratorFunction, arg, self:GetSet(), -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Group#GROUP GroupObject - function( ZoneObject, GroupObject ) - if GroupObject:IsNotInZone( ZoneObject ) then + function(ZoneObject, GroupObject) + if GroupObject:IsNotInZone(ZoneObject) then return true else return false end - end, { ZoneObject } ) + end, { ZoneObject }) return self end @@ -1769,11 +1792,11 @@ do -- else -- MESSAGE:New("Some or all SET's GROUP are outside zone !", 10):ToAll() -- end - function SET_GROUP:AllCompletelyInZone( Zone ) - --self:F2( Zone ) + function SET_GROUP:AllCompletelyInZone(Zone) + --self:F2(Zone) local Set = self:GetSet() - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP - if not GroupData:IsCompletelyInZone( Zone ) then + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if not GroupData:IsCompletelyInZone(Zone) then return false end end @@ -1785,19 +1808,19 @@ do -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. -- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. -- @return #SET_GROUP self - function SET_GROUP:ForEachGroupAnyInZone( ZoneObject, IteratorFunction, ... ) - --self:F2( arg ) + function SET_GROUP:ForEachGroupAnyInZone(ZoneObject, IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet(), + self:ForEach(IteratorFunction, arg, self:GetSet(), -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Group#GROUP GroupObject - function( ZoneObject, GroupObject ) - if GroupObject:IsAnyInZone( ZoneObject ) then + function(ZoneObject, GroupObject) + if GroupObject:IsAnyInZone(ZoneObject) then return true else return false end - end, { ZoneObject } ) + end, { ZoneObject }) return self end @@ -1816,11 +1839,11 @@ do -- else -- MESSAGE:New("No GROUP is completely in zone !", 10):ToAll() -- end - function SET_GROUP:AnyCompletelyInZone( Zone ) - --self:F2( Zone ) + function SET_GROUP:AnyCompletelyInZone(Zone) + --self:F2(Zone) local Set = self:GetSet() - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP - if GroupData:IsCompletelyInZone( Zone ) then + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if GroupData:IsCompletelyInZone(Zone) then return true end end @@ -1841,11 +1864,11 @@ do -- else -- MESSAGE:New("No UNIT of any GROUP is in zone !", 10):ToAll() -- end - function SET_GROUP:AnyInZone( Zone ) - --self:F2( Zone ) + function SET_GROUP:AnyInZone(Zone) + --self:F2(Zone) local Set = self:GetSet() - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP - if GroupData:IsPartlyInZone( Zone ) or GroupData:IsCompletelyInZone( Zone ) then + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if GroupData:IsPartlyInZone(Zone) or GroupData:IsCompletelyInZone(Zone) then return true end end @@ -1867,14 +1890,14 @@ do -- else -- MESSAGE:New("No GROUP are in zone, or one (or more) GROUP is completely in it !", 10):ToAll() -- end - function SET_GROUP:AnyPartlyInZone( Zone ) - --self:F2( Zone ) + function SET_GROUP:AnyPartlyInZone(Zone) + --self:F2(Zone) local IsPartlyInZone = false local Set = self:GetSet() - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP - if GroupData:IsCompletelyInZone( Zone ) then + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if GroupData:IsCompletelyInZone(Zone) then return false - elseif GroupData:IsPartlyInZone( Zone ) then + elseif GroupData:IsPartlyInZone(Zone) then IsPartlyInZone = true -- at least one GROUP is partly in zone end end @@ -1902,11 +1925,11 @@ do -- else -- MESSAGE:New("No UNIT of any GROUP is in zone !", 10):ToAll() -- end - function SET_GROUP:NoneInZone( Zone ) - --self:F2( Zone ) + function SET_GROUP:NoneInZone(Zone) + --self:F2(Zone) local Set = self:GetSet() - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP - if not GroupData:IsNotInZone( Zone ) then -- If the GROUP is in Zone in any way + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if not GroupData:IsNotInZone(Zone) then -- If the GROUP is in Zone in any way return false end end @@ -1925,12 +1948,12 @@ do -- MySetGroup:AddGroupsByName({"Group1", "Group2"}) -- -- MESSAGE:New("There are " .. MySetGroup:CountInZone(MyZone) .. " GROUPs in the Zone !", 10):ToAll() - function SET_GROUP:CountInZone( Zone ) - --self:F2( Zone ) + function SET_GROUP:CountInZone(Zone) + --self:F2(Zone) local Count = 0 local Set = self:GetSet() - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP - if GroupData:IsCompletelyInZone( Zone ) then + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if GroupData:IsCompletelyInZone(Zone) then Count = Count + 1 end end @@ -1947,12 +1970,12 @@ do -- MySetGroup:AddGroupsByName({"Group1", "Group2"}) -- -- MESSAGE:New("There are " .. MySetGroup:CountUnitInZone(MyZone) .. " UNITs in the Zone !", 10):ToAll() - function SET_GROUP:CountUnitInZone( Zone ) - --self:F2( Zone ) + function SET_GROUP:CountUnitInZone(Zone) + --self:F2(Zone) local Count = 0 local Set = self:GetSet() - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP - Count = Count + GroupData:CountInZone( Zone ) + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + Count = Count + GroupData:CountInZone(Zone) end return Count end @@ -1967,13 +1990,13 @@ do local Set = self:GetSet() - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP if GroupData and GroupData:IsAlive() then CountG = CountG + 1 -- Count Units. - for _, _unit in pairs( GroupData:GetUnits() ) do + for _, _unit in pairs(GroupData:GetUnits()) do local unit = _unit -- Wrapper.Unit#UNIT if unit and unit:IsAlive() then CountU = CountU + 1 @@ -1990,10 +2013,10 @@ do -- @param #SET_GROUP self -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_GROUP. The function needs to accept a GROUP parameter. ---- @return #SET_GROUP self - -- function SET_GROUP:ForEachPlayer( IteratorFunction, ... ) - -- --self:F2( arg ) + -- function SET_GROUP:ForEachPlayer(IteratorFunction, ...) + -- --self:F2(arg) -- - -- self:ForEach( IteratorFunction, arg, self.PlayersAlive ) + -- self:ForEach(IteratorFunction, arg, self.PlayersAlive) -- -- return self -- end @@ -2003,10 +2026,10 @@ do -- @param #SET_GROUP self -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_GROUP. The function needs to accept a CLIENT parameter. ---- @return #SET_GROUP self - -- function SET_GROUP:ForEachClient( IteratorFunction, ... ) - -- --self:F2( arg ) + -- function SET_GROUP:ForEachClient(IteratorFunction, ...) + -- --self:F2(arg) -- - -- self:ForEach( IteratorFunction, arg, self.Clients ) + -- self:ForEach(IteratorFunction, arg, self.Clients) -- -- return self -- end @@ -2015,13 +2038,13 @@ do -- @param #SET_GROUP self -- @param Wrapper.Group#GROUP MGroup The group that is checked for inclusion. -- @return #SET_GROUP self - function SET_GROUP:IsIncludeObject( MGroup ) - --self:F2( MGroup ) + function SET_GROUP:IsIncludeObject(MGroup) + --self:F2(MGroup) local MGroupInclude = true if self.Filter.Alive == true then local MGroupAlive = false - --self:F( { Active = self.Filter.Active } ) + --self:F({ Active = self.Filter.Active }) if MGroup and MGroup:IsAlive() then MGroupAlive = true end @@ -2030,7 +2053,7 @@ do if self.Filter.Active ~= nil then local MGroupActive = false - --self:F( { Active = self.Filter.Active } ) + --self:F({ Active = self.Filter.Active }) if self.Filter.Active == false or (self.Filter.Active == true and MGroup:IsActive() == true) then MGroupActive = true end @@ -2039,8 +2062,8 @@ do if self.Filter.Coalitions and MGroupInclude then local MGroupCoalition = false - for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do - --self:T3( { "Coalition:", MGroup:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) + for CoalitionID, CoalitionName in pairs(self.Filter.Coalitions) do + --self:T3({ "Coalition:", MGroup:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName }) if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == MGroup:GetCoalition() then MGroupCoalition = true end @@ -2050,8 +2073,8 @@ do if self.Filter.Categories and MGroupInclude then local MGroupCategory = false - for CategoryID, CategoryName in pairs( self.Filter.Categories ) do - --self:I( { "Category:", MGroup:GetCategory(), self.FilterMeta.Categories[CategoryName], CategoryName } ) + for CategoryID, CategoryName in pairs(self.Filter.Categories) do + --self:I({ "Category:", MGroup:GetCategory(), self.FilterMeta.Categories[CategoryName], CategoryName }) if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == MGroup:GetCategory() then MGroupCategory = true end @@ -2062,8 +2085,8 @@ do if self.Filter.Countries and MGroupInclude then local MGroupCountry = false - for CountryID, CountryName in pairs( self.Filter.Countries ) do - --self:T3( { "Country:", MGroup:GetCountry(), CountryName } ) + for CountryID, CountryName in pairs(self.Filter.Countries) do + --self:T3({ "Country:", MGroup:GetCountry(), CountryName }) if country.id[CountryName] == MGroup:GetCountry() then MGroupCountry = true end @@ -2073,8 +2096,8 @@ do if self.Filter.GroupPrefixes and MGroupInclude then local MGroupPrefix = false - for GroupPrefixId, GroupPrefix in pairs( self.Filter.GroupPrefixes ) do - --self:I( { "Prefix:", MGroup:GetName(), GroupPrefix } ) + for GroupPrefixId, GroupPrefix in pairs(self.Filter.GroupPrefixes) do + --self:I({ "Prefix:", MGroup:GetName(), GroupPrefix }) if string.find(MGroup:GetName(), string.gsub(GroupPrefix,"-","%%-"),1) then MGroupPrefix = true end @@ -2085,8 +2108,8 @@ do if self.Filter.Zones and MGroupInclude then local MGroupZone = false - for ZoneName, Zone in pairs( self.Filter.Zones ) do - --self:T( "Zone:", ZoneName ) + for ZoneName, Zone in pairs(self.Filter.Zones) do + --self:T("Zone:", ZoneName) if MGroup:IsInZone(Zone) then MGroupZone = true end @@ -2100,7 +2123,7 @@ do MGroupInclude = MGroupInclude and MGroupFunc end - --self:I( MGroupInclude ) + --self:I(MGroupInclude) return MGroupInclude end @@ -2117,7 +2140,7 @@ do local dmin=math.huge local gmin=nil - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP local group=GroupData --Wrapper.Group#GROUP if group and group:IsAlive() and (Coalitions==nil or UTILS.IsAnyInTable(Coalitions, group:GetCoalition())) then @@ -2151,8 +2174,8 @@ do -- MySetGroup:SetCargoBayWeightLimit() function SET_GROUP:SetCargoBayWeightLimit() local Set = self:GetSet() - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP - for UnitName, UnitData in pairs( GroupData:GetUnits() ) do + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + for UnitName, UnitData in pairs(GroupData:GetUnits()) do -- local UnitData = UnitData -- Wrapper.Unit#UNIT UnitData:SetCargoBayWeightLimit() end @@ -2238,12 +2261,12 @@ do -- SET_UNIT -- -- -- Create the SetCarrier SET_UNIT collection. -- - -- local SetHelicopter = SET_UNIT:New():FilterPrefixes( "Helicopter" ):FilterStart() + -- local SetHelicopter = SET_UNIT:New():FilterPrefixes("Helicopter"):FilterStart() -- -- -- Put a Dead event handler on SetCarrier, to ensure that when a carrier unit is destroyed, that all internal parameters are reset. -- - -- function SetHelicopter:OnAfterDead( From, Event, To, UnitObject ) - -- --self:F( { UnitObject = UnitObject:GetName() } ) + -- function SetHelicopter:OnAfterDead(From, Event, To, UnitObject) + -- --self:F({ UnitObject = UnitObject:GetName() }) -- end -- -- While this is a good example, there is a catch. @@ -2255,15 +2278,15 @@ do -- SET_UNIT -- -- Within that constructor, we want to set an enclosed event handler OnAfterDead for SetHelicopter. -- -- But within the OnAfterDead method, we want to refer to the self variable of the AI_CARGO_DISPATCHER. -- - -- function ACLASS:New( SetCarrier, SetCargo, SetDeployZones ) + -- function ACLASS:New(SetCarrier, SetCargo, SetDeployZones) -- - -- local self = BASE:Inherit( self, FSM:New() ) -- #AI_CARGO_DISPATCHER + -- local self = BASE:Inherit(self, FSM:New()) -- #AI_CARGO_DISPATCHER -- -- -- Put a Dead event handler on SetCarrier, to ensure that when a carrier is destroyed, that all internal parameters are reset. -- -- Note the "." notation, and the explicit declaration of SetHelicopter, which would be using the ":" notation the implicit self variable declaration. -- - -- function SetHelicopter.OnAfterDead( SetHelicopter, From, Event, To, UnitObject ) - -- SetHelicopter:F( { UnitObject = UnitObject:GetName() } ) + -- function SetHelicopter.OnAfterDead(SetHelicopter, From, Event, To, UnitObject) + -- SetHelicopter:F({ UnitObject = UnitObject:GetName() }) -- self.array[UnitObject] = nil -- So here I clear the array table entry of the self object ACLASS. -- end -- @@ -2312,9 +2335,9 @@ do -- SET_UNIT function SET_UNIT:New() -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.UNITS ) ) -- #SET_UNIT + local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.UNITS)) -- #SET_UNIT - self:FilterActive( false ) + self:FilterActive(false) --- Count Alive Units -- @function [parent=#SET_UNIT] CountAlive @@ -2328,10 +2351,10 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param Wrapper.Unit#UNIT Unit A single UNIT. -- @return #SET_UNIT self - function SET_UNIT:AddUnit( Unit ) - --self:F2( Unit:GetName() ) + function SET_UNIT:AddUnit(Unit) + --self:F2(Unit:GetName()) - self:Add( Unit:GetName(), Unit ) + self:Add(Unit:GetName(), Unit) if Unit:IsInstanceOf("UNIT") then -- Set the default cargo bay limit each time a new unit is added to the set. @@ -2345,13 +2368,13 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param #string AddUnitNames A single name or an array of UNIT names. -- @return #SET_UNIT self - function SET_UNIT:AddUnitsByName( AddUnitNames ) + function SET_UNIT:AddUnitsByName(AddUnitNames) - local AddUnitNamesArray = (type( AddUnitNames ) == "table") and AddUnitNames or { AddUnitNames } + local AddUnitNamesArray = (type(AddUnitNames) == "table") and AddUnitNames or { AddUnitNames } - --self:T( AddUnitNamesArray ) - for AddUnitID, AddUnitName in pairs( AddUnitNamesArray ) do - self:Add( AddUnitName, UNIT:FindByName( AddUnitName ) ) + --self:T(AddUnitNamesArray) + for AddUnitID, AddUnitName in pairs(AddUnitNamesArray) do + self:Add(AddUnitName, UNIT:FindByName(AddUnitName)) end return self @@ -2361,12 +2384,12 @@ do -- SET_UNIT -- @param Core.Set#SET_UNIT self -- @param #table RemoveUnitNames A single name or an array of UNIT names. -- @return Core.Set#SET_UNIT self - function SET_UNIT:RemoveUnitsByName( RemoveUnitNames ) + function SET_UNIT:RemoveUnitsByName(RemoveUnitNames) - local RemoveUnitNamesArray = (type( RemoveUnitNames ) == "table") and RemoveUnitNames or { RemoveUnitNames } + local RemoveUnitNamesArray = (type(RemoveUnitNames) == "table") and RemoveUnitNames or { RemoveUnitNames } - for RemoveUnitID, RemoveUnitName in pairs( RemoveUnitNamesArray ) do - self:Remove( RemoveUnitName ) + for RemoveUnitID, RemoveUnitName in pairs(RemoveUnitNamesArray) do + self:Remove(RemoveUnitName) end return self @@ -2376,44 +2399,32 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param #string UnitName -- @return Wrapper.Unit#UNIT The found Unit. - function SET_UNIT:FindUnit( UnitName ) + function SET_UNIT:FindUnit(UnitName) local UnitFound = self.Set[UnitName] return UnitFound end - + --- Builds a set of units of coalitions. -- Possible current coalitions are red, blue and neutral. -- @param #SET_UNIT self -- @param #string Coalitions Can take the following values: "red", "blue", "neutral". -- @return #SET_UNIT self - function SET_UNIT:FilterCoalitions( Coalitions ) - self.Filter.Coalitions = {} - if type( Coalitions ) ~= "table" then - Coalitions = { Coalitions } - end - - for CoalitionID, Coalition in pairs( Coalitions ) do - self.Filter.Coalitions[Coalition] = Coalition - end - - return self - end --- Builds a set of units out of categories. -- Possible current categories are plane, helicopter, ground, ship. -- @param #SET_UNIT self -- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship". -- @return #SET_UNIT self - function SET_UNIT:FilterCategories( Categories ) + function SET_UNIT:FilterCategories(Categories) if not self.Filter.Categories then self.Filter.Categories = {} end - if type( Categories ) ~= "table" then + if type(Categories) ~= "table" then Categories = { Categories } end - for CategoryID, Category in pairs( Categories ) do + for CategoryID, Category in pairs(Categories) do self.Filter.Categories[Category] = Category end return self @@ -2424,14 +2435,14 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param #string Types Can take those type strings known within DCS world. -- @return #SET_UNIT self - function SET_UNIT:FilterTypes( Types ) + function SET_UNIT:FilterTypes(Types) if not self.Filter.Types then self.Filter.Types = {} end - if type( Types ) ~= "table" then + if type(Types) ~= "table" then Types = { Types } end - for TypeID, Type in pairs( Types ) do + for TypeID, Type in pairs(Types) do self.Filter.Types[Type] = Type end return self @@ -2442,14 +2453,14 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param #string Countries Can take those country strings known within DCS world. -- @return #SET_UNIT self - function SET_UNIT:FilterCountries( Countries ) + function SET_UNIT:FilterCountries(Countries) if not self.Filter.Countries then self.Filter.Countries = {} end - if type( Countries ) ~= "table" then + if type(Countries) ~= "table" then Countries = { Countries } end - for CountryID, Country in pairs( Countries ) do + for CountryID, Country in pairs(Countries) do self.Filter.Countries[Country] = Country end return self @@ -2460,14 +2471,14 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param #string Prefixes The string pattern(s) that needs to be contained in the unit name. Can also be passed as a `#table` of strings. -- @return #SET_UNIT self - function SET_UNIT:FilterPrefixes( Prefixes ) + function SET_UNIT:FilterPrefixes(Prefixes) if not self.Filter.UnitPrefixes then self.Filter.UnitPrefixes = {} end - if type( Prefixes ) ~= "table" then + if type(Prefixes) ~= "table" then Prefixes = { Prefixes } end - for PrefixID, Prefix in pairs( Prefixes ) do + for PrefixID, Prefix in pairs(Prefixes) do self.Filter.UnitPrefixes[Prefix] = Prefix end return self @@ -2477,20 +2488,20 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE -- @return #SET_UNIT self - function SET_UNIT:FilterZones( Zones ) + function SET_UNIT:FilterZones(Zones) if not self.Filter.Zones then self.Filter.Zones = {} end local zones = {} if Zones.ClassName and Zones.ClassName == "SET_ZONE" then zones = Zones.Set - elseif type( Zones ) ~= "table" or (type( Zones ) == "table" and Zones.ClassName ) then + elseif type(Zones) ~= "table" or (type(Zones) == "table" and Zones.ClassName) then self:E("***** FilterZones needs either a table of ZONE Objects or a SET_ZONE as parameter!") return self else zones = Zones end - for _,Zone in pairs( zones ) do + for _,Zone in pairs(zones) do local zonename = Zone:GetName() self.Filter.Zones[zonename] = Zone end @@ -2509,15 +2520,15 @@ do -- SET_UNIT -- UnitSet = SET_UNIT:New():FilterActive():FilterStart() -- -- -- Include only active units to the set of the blue coalition, and filter one time. - -- UnitSet = SET_UNIT:New():FilterActive():FilterCoalition( "blue" ):FilterOnce() + -- UnitSet = SET_UNIT:New():FilterActive():FilterCoalition("blue"):FilterOnce() -- -- -- Include only active units to the set of the blue coalition, and filter one time. -- -- Later, reset to include back inactive units to the set. - -- UnitSet = SET_UNIT:New():FilterActive():FilterCoalition( "blue" ):FilterOnce() + -- UnitSet = SET_UNIT:New():FilterActive():FilterCoalition("blue"):FilterOnce() -- ... logic ... - -- UnitSet = SET_UNIT:New():FilterActive( false ):FilterCoalition( "blue" ):FilterOnce() + -- UnitSet = SET_UNIT:New():FilterActive(false):FilterCoalition("blue"):FilterOnce() -- - function SET_UNIT:FilterActive( Active ) + function SET_UNIT:FilterActive(Active) Active = Active or not (Active == false) self.Filter.Active = Active return self @@ -2535,7 +2546,7 @@ do -- SET_UNIT return false end end - ) + ) return self end @@ -2564,7 +2575,7 @@ do -- SET_UNIT end return outcome end, Prefixes - ) + ) return self end @@ -2573,13 +2584,13 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param #table RadarTypes The radar types. -- @return #SET_UNIT self - function SET_UNIT:FilterHasRadar( RadarTypes ) + function SET_UNIT:FilterHasRadar(RadarTypes) self.Filter.RadarTypes = self.Filter.RadarTypes or {} - if type( RadarTypes ) ~= "table" then + if type(RadarTypes) ~= "table" then RadarTypes = { RadarTypes } end - for RadarTypeID, RadarType in pairs( RadarTypes ) do + for RadarTypeID, RadarType in pairs(RadarTypes) do self.Filter.RadarTypes[RadarType] = RadarType end return self @@ -2602,7 +2613,7 @@ do -- SET_UNIT local Set = self:GetSet() local CountU = 0 - for UnitID, UnitData in pairs( Set ) do -- For each GROUP in SET_GROUP + for UnitID, UnitData in pairs(Set) do -- For each GROUP in SET_GROUP if UnitData and UnitData:IsAlive() then CountU = CountU + 1 end @@ -2637,10 +2648,10 @@ do -- SET_UNIT local Database = _DATABASE.UNITS - for ObjectName, Object in pairs( Database ) do - if self:IsIncludeObject( Object ) and self:IsNotInSet(Object) then - self:Add( ObjectName, Object ) - elseif (not self:IsIncludeObject( Object )) and self:IsInSet(Object) then + for ObjectName, Object in pairs(Database) do + if self:IsIncludeObject(Object) and self:IsNotInSet(Object) then + self:Add(ObjectName, Object) + elseif (not self:IsIncludeObject(Object)) and self:IsInSet(Object) then self:Remove(ObjectName) end end @@ -2686,11 +2697,11 @@ do -- SET_UNIT if _DATABASE then self:_FilterStart() - self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) - self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.UnitLost, self._EventOnDeadOrCrash ) + self:HandleEvent(EVENTS.Birth, self._EventOnBirth) + self:HandleEvent(EVENTS.Dead, self._EventOnDeadOrCrash) + self:HandleEvent(EVENTS.Crash, self._EventOnDeadOrCrash) + self:HandleEvent(EVENTS.RemoveUnit, self._EventOnDeadOrCrash) + self:HandleEvent(EVENTS.UnitLost, self._EventOnDeadOrCrash) if self.Filter.Zones then self.ZoneTimer = TIMER:New(self._ContinousZoneFilter,self) local timing = self.ZoneTimerInterval or 30 @@ -2716,7 +2727,7 @@ do -- SET_UNIT -- if unit:GetName() == "Exclude Me" then isinclude = false end -- return isinclude -- end - -- ):FilterOnce() + -- ):FilterOnce() -- BASE:I(groundset:Flush()) @@ -2726,13 +2737,13 @@ do -- SET_UNIT -- @param Core.Event#EVENTDATA Event -- @return #string The name of the UNIT -- @return #table The UNIT - function SET_UNIT:AddInDatabase( Event ) - --self:F3( { Event } ) + function SET_UNIT:AddInDatabase(Event) + --self:F3({ Event }) if Event.IniObjectCategory == Object.Category.UNIT then if not self.Database[Event.IniDCSUnitName] then - self.Database[Event.IniDCSUnitName] = UNIT:Register( Event.IniDCSUnitName ) - --self:T3( self.Database[Event.IniDCSUnitName] ) + self.Database[Event.IniDCSUnitName] = UNIT:Register(Event.IniDCSUnitName) + --self:T3(self.Database[Event.IniDCSUnitName]) end end @@ -2745,8 +2756,8 @@ do -- SET_UNIT -- @param Core.Event#EVENTDATA Event -- @return #string The name of the UNIT -- @return #table The UNIT - function SET_UNIT:FindInDatabase( Event ) - --self:F2( { Event.IniDCSUnitName, self.Set[Event.IniDCSUnitName], Event } ) + function SET_UNIT:FindInDatabase(Event) + --self:F2({ Event.IniDCSUnitName, self.Set[Event.IniDCSUnitName], Event }) return Event.IniDCSUnitName, self.Set[Event.IniDCSUnitName] end @@ -2757,24 +2768,24 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param Core.Zone#ZONE ZoneTest The Zone to be tested for. -- @return #boolean - function SET_UNIT:IsPartiallyInZone( ZoneTest ) + function SET_UNIT:IsPartiallyInZone(ZoneTest) local IsPartiallyInZone = false - local function EvaluateZone( ZoneUnit ) + local function EvaluateZone(ZoneUnit) local ZoneUnitName = ZoneUnit:GetName() - --self:F( { ZoneUnitName = ZoneUnitName } ) - if self:FindUnit( ZoneUnitName ) then + --self:F({ ZoneUnitName = ZoneUnitName }) + if self:FindUnit(ZoneUnitName) then IsPartiallyInZone = true - --self:F( { Found = true } ) + --self:F({ Found = true }) return false end return true end - ZoneTest:SearchZone( EvaluateZone ) + ZoneTest:SearchZone(EvaluateZone) return IsPartiallyInZone end @@ -2783,14 +2794,14 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param Core.Zone#ZONE Zone The Zone to be tested for. -- @return #boolean - function SET_UNIT:IsNotInZone( Zone ) + function SET_UNIT:IsNotInZone(Zone) local IsNotInZone = true - local function EvaluateZone( ZoneUnit ) + local function EvaluateZone(ZoneUnit) local ZoneUnitName = ZoneUnit:GetName() - if self:FindUnit( ZoneUnitName ) then + if self:FindUnit(ZoneUnitName) then IsNotInZone = false return false end @@ -2798,7 +2809,7 @@ do -- SET_UNIT return true end - Zone:SearchZone( EvaluateZone ) + Zone:SearchZone(EvaluateZone) return IsNotInZone end @@ -2810,10 +2821,10 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param #function IteratorFunction The function that will be called when there is an alive UNIT in the SET_UNIT. The function needs to accept a UNIT parameter. -- @return #SET_UNIT self - function SET_UNIT:ForEachUnit( IteratorFunction, ... ) - --self:F2( arg ) + function SET_UNIT:ForEachUnit(IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet() ) + self:ForEach(IteratorFunction, arg, self:GetSet()) return self end @@ -2824,20 +2835,20 @@ do -- SET_UNIT -- @param #number FromThreatLevel The TreatLevel to start the evaluation **From** (this must be a value between 0 and 10). -- @param #number ToThreatLevel The TreatLevel to stop the evaluation **To** (this must be a value between 0 and 10). -- @return #SET_UNIT self - function SET_UNIT:GetSetPerThreatLevel( FromThreatLevel, ToThreatLevel ) - --self:F2( arg ) + function SET_UNIT:GetSetPerThreatLevel(FromThreatLevel, ToThreatLevel) + --self:F2(arg) local ThreatLevelSet = {} if self:Count() ~= 0 then - for UnitName, UnitObject in pairs( self.Set ) do + for UnitName, UnitObject in pairs(self.Set) do local Unit = UnitObject -- Wrapper.Unit#UNIT local ThreatLevel = Unit:GetThreatLevel() ThreatLevelSet[ThreatLevel] = ThreatLevelSet[ThreatLevel] or {} ThreatLevelSet[ThreatLevel].Set = ThreatLevelSet[ThreatLevel].Set or {} ThreatLevelSet[ThreatLevel].Set[UnitName] = UnitObject - --self:F( { ThreatLevel = ThreatLevel, ThreatLevelSet = ThreatLevelSet[ThreatLevel].Set } ) + --self:F({ ThreatLevel = ThreatLevel, ThreatLevelSet = ThreatLevelSet[ThreatLevel].Set }) end local OrderedPerThreatLevelSet = {} @@ -2845,11 +2856,11 @@ do -- SET_UNIT local ThreatLevelIncrement = FromThreatLevel <= ToThreatLevel and 1 or -1 for ThreatLevel = FromThreatLevel, ToThreatLevel, ThreatLevelIncrement do - --self:F( { ThreatLevel = ThreatLevel } ) + --self:F({ ThreatLevel = ThreatLevel }) local ThreatLevelItem = ThreatLevelSet[ThreatLevel] if ThreatLevelItem then - for UnitName, UnitObject in pairs( ThreatLevelItem.Set ) do - table.insert( OrderedPerThreatLevelSet, UnitObject ) + for UnitName, UnitObject in pairs(ThreatLevelItem.Set) do + table.insert(OrderedPerThreatLevelSet, UnitObject) end end end @@ -2869,36 +2880,36 @@ do -- SET_UNIT -- @return #SET_UNIT self -- @usage -- - -- UnitSet:ForEachUnitPerThreatLevel( 10, 0, + -- UnitSet:ForEachUnitPerThreatLevel(10, 0, -- -- @param Wrapper.Unit#UNIT UnitObject The UNIT object in the UnitSet, that will be passed to the local function for evaluation. - -- function( UnitObject ) + -- function(UnitObject) -- .. logic .. -- end - -- ) + -- ) -- - function SET_UNIT:ForEachUnitPerThreatLevel( FromThreatLevel, ToThreatLevel, IteratorFunction, ... ) -- R2.1 Threat Level implementation - --self:F2( arg ) + function SET_UNIT:ForEachUnitPerThreatLevel(FromThreatLevel, ToThreatLevel, IteratorFunction, ...) -- R2.1 Threat Level implementation + --self:F2(arg) local ThreatLevelSet = {} if self:Count() ~= 0 then - for UnitName, UnitObject in pairs( self.Set ) do + for UnitName, UnitObject in pairs(self.Set) do local Unit = UnitObject -- Wrapper.Unit#UNIT local ThreatLevel = Unit:GetThreatLevel() ThreatLevelSet[ThreatLevel] = ThreatLevelSet[ThreatLevel] or {} ThreatLevelSet[ThreatLevel].Set = ThreatLevelSet[ThreatLevel].Set or {} ThreatLevelSet[ThreatLevel].Set[UnitName] = UnitObject - --self:F( { ThreatLevel = ThreatLevel, ThreatLevelSet = ThreatLevelSet[ThreatLevel].Set } ) + --self:F({ ThreatLevel = ThreatLevel, ThreatLevelSet = ThreatLevelSet[ThreatLevel].Set }) end local ThreatLevelIncrement = FromThreatLevel <= ToThreatLevel and 1 or -1 for ThreatLevel = FromThreatLevel, ToThreatLevel, ThreatLevelIncrement do - --self:F( { ThreatLevel = ThreatLevel } ) + --self:F({ ThreatLevel = ThreatLevel }) local ThreatLevelItem = ThreatLevelSet[ThreatLevel] if ThreatLevelItem then - self:ForEach( IteratorFunction, arg, ThreatLevelItem.Set ) + self:ForEach(IteratorFunction, arg, ThreatLevelItem.Set) end end end @@ -2911,19 +2922,19 @@ do -- SET_UNIT -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. -- @param #function IteratorFunction The function that will be called when there is an alive UNIT in the SET_UNIT. The function needs to accept a UNIT parameter. -- @return #SET_UNIT self - function SET_UNIT:ForEachUnitCompletelyInZone( ZoneObject, IteratorFunction, ... ) - --self:F2( arg ) + function SET_UNIT:ForEachUnitCompletelyInZone(ZoneObject, IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet(), + self:ForEach(IteratorFunction, arg, self:GetSet(), -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Unit#UNIT UnitObject - function( ZoneObject, UnitObject ) - if UnitObject:IsInZone( ZoneObject ) then + function(ZoneObject, UnitObject) + if UnitObject:IsInZone(ZoneObject) then return true else return false end - end, { ZoneObject } ) + end, { ZoneObject }) return self end @@ -2933,19 +2944,19 @@ do -- SET_UNIT -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. -- @param #function IteratorFunction The function that will be called when there is an alive UNIT in the SET_UNIT. The function needs to accept a UNIT parameter. -- @return #SET_UNIT self - function SET_UNIT:ForEachUnitNotInZone( ZoneObject, IteratorFunction, ... ) - --self:F2( arg ) + function SET_UNIT:ForEachUnitNotInZone(ZoneObject, IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet(), + self:ForEach(IteratorFunction, arg, self:GetSet(), -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Unit#UNIT UnitObject - function( ZoneObject, UnitObject ) - if UnitObject:IsNotInZone( ZoneObject ) then + function(ZoneObject, UnitObject) + if UnitObject:IsNotInZone(ZoneObject) then return true else return false end - end, { ZoneObject } ) + end, { ZoneObject }) return self end @@ -2959,7 +2970,7 @@ do -- SET_UNIT local MT = {} -- Message Text local UnitTypes = {} - for UnitID, UnitData in pairs( self:GetSet() ) do + for UnitID, UnitData in pairs(self:GetSet()) do local TextUnit = UnitData -- Wrapper.Unit#UNIT if TextUnit:IsAlive() then local UnitType = TextUnit:GetTypeName() @@ -2972,7 +2983,7 @@ do -- SET_UNIT end end - for UnitTypeID, UnitType in pairs( UnitTypes ) do + for UnitTypeID, UnitType in pairs(UnitTypes) do MT[#MT + 1] = UnitType .. " of " .. UnitTypeID end @@ -2988,11 +2999,11 @@ do -- SET_UNIT local MT = {} -- Message Text local UnitTypes = self:GetUnitTypes() - for UnitTypeID, UnitType in pairs( UnitTypes ) do + for UnitTypeID, UnitType in pairs(UnitTypes) do MT[#MT + 1] = UnitType .. " of " .. UnitTypeID end - return table.concat( MT, ", " ) + return table.concat(MT, ", ") end --- Returns map of unit threat levels. @@ -3003,7 +3014,7 @@ do -- SET_UNIT local UnitThreatLevels = {} - for UnitID, UnitData in pairs( self:GetSet() ) do + for UnitID, UnitData in pairs(self:GetSet()) do local ThreatUnit = UnitData -- Wrapper.Unit#UNIT if ThreatUnit:IsAlive() then local UnitThreatLevel, UnitThreatLevelText = ThreatUnit:GetThreatLevel() @@ -3026,7 +3037,7 @@ do -- SET_UNIT local MaxThreatLevelA2G = 0 local MaxThreatText = "" - for UnitName, UnitData in pairs( self:GetSet() ) do + for UnitName, UnitData in pairs(self:GetSet()) do local ThreatUnit = UnitData -- Wrapper.Unit#UNIT local ThreatLevelA2G, ThreatText = ThreatUnit:GetThreatLevel() if ThreatLevelA2G > MaxThreatLevelA2G then @@ -3035,7 +3046,7 @@ do -- SET_UNIT end end - --self:F( { MaxThreatLevelA2G = MaxThreatLevelA2G, MaxThreatText = MaxThreatText } ) + --self:F({ MaxThreatLevelA2G = MaxThreatLevelA2G, MaxThreatText = MaxThreatText }) return MaxThreatLevelA2G, MaxThreatText end @@ -3083,8 +3094,8 @@ do -- SET_UNIT if Coordinate then local heading = self:GetHeading() or 0 local velocity = self:GetVelocity() or 0 - Coordinate:SetHeading( heading ) - Coordinate:SetVelocity( velocity ) + Coordinate:SetHeading(heading) + Coordinate:SetVelocity(velocity) --self:T(UTILS.PrintTableToLog(Coordinate)) end @@ -3100,7 +3111,7 @@ do -- SET_UNIT local MaxVelocity = 0 - for UnitName, UnitData in pairs( self:GetSet() ) do + for UnitName, UnitData in pairs(self:GetSet()) do local Unit = UnitData -- Wrapper.Unit#UNIT local Coordinate = Unit:GetCoordinate() @@ -3111,7 +3122,7 @@ do -- SET_UNIT end end - --self:F( { MaxVelocity = MaxVelocity } ) + --self:F({ MaxVelocity = MaxVelocity }) return MaxVelocity end @@ -3124,7 +3135,7 @@ do -- SET_UNIT local HeadingSet = nil local MovingCount = 0 - for UnitName, UnitData in pairs( self:GetSet() ) do + for UnitName, UnitData in pairs(self:GetSet()) do local Unit = UnitData -- Wrapper.Unit#UNIT local Coordinate = Unit:GetCoordinate() @@ -3136,7 +3147,7 @@ do -- SET_UNIT HeadingSet = Heading else local HeadingDiff = (HeadingSet - Heading + 180 + 360) % 360 - 180 - HeadingDiff = math.abs( HeadingDiff ) + HeadingDiff = math.abs(HeadingDiff) if HeadingDiff > 5 then HeadingSet = nil break @@ -3153,19 +3164,19 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param DCS#Unit.RadarType RadarType -- @return #number The amount of radars in the Set with the given type - function SET_UNIT:HasRadar( RadarType ) - --self:F2( RadarType ) + function SET_UNIT:HasRadar(RadarType) + --self:F2(RadarType) local RadarCount = 0 - for UnitID, UnitData in pairs( self:GetSet() ) do + for UnitID, UnitData in pairs(self:GetSet()) do local UnitSensorTest = UnitData -- Wrapper.Unit#UNIT local HasSensors if RadarType then - HasSensors = UnitSensorTest:HasSensors( Unit.SensorType.RADAR, RadarType ) + HasSensors = UnitSensorTest:HasSensors(Unit.SensorType.RADAR, RadarType) else - HasSensors = UnitSensorTest:HasSensors( Unit.SensorType.RADAR ) + HasSensors = UnitSensorTest:HasSensors(Unit.SensorType.RADAR) end - --self:T3( HasSensors ) + --self:T3(HasSensors) if HasSensors then RadarCount = RadarCount + 1 end @@ -3181,14 +3192,14 @@ do -- SET_UNIT --self:F2() local SEADCount = 0 - for UnitID, UnitData in pairs( self:GetSet() ) do + for UnitID, UnitData in pairs(self:GetSet()) do local UnitSEAD = UnitData -- Wrapper.Unit#UNIT if UnitSEAD:IsAlive() then local UnitSEADAttributes = UnitSEAD:GetDesc().attributes local HasSEAD = UnitSEAD:HasSEAD() - --self:T3( HasSEAD ) + --self:T3(HasSEAD) if HasSEAD then SEADCount = SEADCount + 1 end @@ -3205,7 +3216,7 @@ do -- SET_UNIT --self:F2() local GroundUnitCount = 0 - for UnitID, UnitData in pairs( self:GetSet() ) do + for UnitID, UnitData in pairs(self:GetSet()) do local UnitTest = UnitData -- Wrapper.Unit#UNIT if UnitTest:IsGround() then GroundUnitCount = GroundUnitCount + 1 @@ -3222,7 +3233,7 @@ do -- SET_UNIT --self:F2() local AirUnitCount = 0 - for UnitID, UnitData in pairs( self:GetSet() ) do + for UnitID, UnitData in pairs(self:GetSet()) do local UnitTest = UnitData -- Wrapper.Unit#UNIT if UnitTest:IsAir() then AirUnitCount = AirUnitCount + 1 @@ -3235,13 +3246,13 @@ do -- SET_UNIT --- Returns if the @{Core.Set} has friendly ground units. -- @param #SET_UNIT self -- @return #number The amount of ground targets in the Set. - function SET_UNIT:HasFriendlyUnits( FriendlyCoalition ) + function SET_UNIT:HasFriendlyUnits(FriendlyCoalition) --self:F2() local FriendlyUnitCount = 0 - for UnitID, UnitData in pairs( self:GetSet() ) do + for UnitID, UnitData in pairs(self:GetSet()) do local UnitTest = UnitData -- Wrapper.Unit#UNIT - if UnitTest:IsFriendly( FriendlyCoalition ) then + if UnitTest:IsFriendly(FriendlyCoalition) then FriendlyUnitCount = FriendlyUnitCount + 1 end end @@ -3255,10 +3266,10 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_UNIT. The function needs to accept a UNIT parameter. ---- @return #SET_UNIT self - -- function SET_UNIT:ForEachPlayer( IteratorFunction, ... ) - -- --self:F2( arg ) + -- function SET_UNIT:ForEachPlayer(IteratorFunction, ...) + -- --self:F2(arg) -- - -- self:ForEach( IteratorFunction, arg, self.PlayersAlive ) + -- self:ForEach(IteratorFunction, arg, self.PlayersAlive) -- -- return self -- end @@ -3268,10 +3279,10 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_UNIT. The function needs to accept a CLIENT parameter. ---- @return #SET_UNIT self - -- function SET_UNIT:ForEachClient( IteratorFunction, ... ) - -- --self:F2( arg ) + -- function SET_UNIT:ForEachClient(IteratorFunction, ...) + -- --self:F2(arg) -- - -- self:ForEach( IteratorFunction, arg, self.Clients ) + -- self:ForEach(IteratorFunction, arg, self.Clients) -- -- return self -- end @@ -3280,8 +3291,8 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param Wrapper.Unit#UNIT MUnit -- @return #SET_UNIT self - function SET_UNIT:IsIncludeObject( MUnit ) - --self:F2( {MUnit} ) + function SET_UNIT:IsIncludeObject(MUnit) + --self:F2({MUnit}) local MUnitInclude = false @@ -3299,8 +3310,8 @@ do -- SET_UNIT if self.Filter.Coalitions and MUnitInclude then local MUnitCoalition = false - for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do - --self:F( { "Coalition:", MUnit:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) + for CoalitionID, CoalitionName in pairs(self.Filter.Coalitions) do + --self:F({ "Coalition:", MUnit:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName }) if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == MUnit:GetCoalition() then MUnitCoalition = true end @@ -3310,8 +3321,8 @@ do -- SET_UNIT if self.Filter.Categories and MUnitInclude then local MUnitCategory = false - for CategoryID, CategoryName in pairs( self.Filter.Categories ) do - --self:T3( { "Category:", MUnit:GetDesc().category, self.FilterMeta.Categories[CategoryName], CategoryName } ) + for CategoryID, CategoryName in pairs(self.Filter.Categories) do + --self:T3({ "Category:", MUnit:GetDesc().category, self.FilterMeta.Categories[CategoryName], CategoryName }) if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == MUnit:GetDesc().category then MUnitCategory = true end @@ -3321,8 +3332,8 @@ do -- SET_UNIT if self.Filter.Types and MUnitInclude then local MUnitType = false - for TypeID, TypeName in pairs( self.Filter.Types ) do - --self:T3( { "Type:", MUnit:GetTypeName(), TypeName } ) + for TypeID, TypeName in pairs(self.Filter.Types) do + --self:T3({ "Type:", MUnit:GetTypeName(), TypeName }) if TypeName == MUnit:GetTypeName() then MUnitType = true end @@ -3332,8 +3343,8 @@ do -- SET_UNIT if self.Filter.Countries and MUnitInclude then local MUnitCountry = false - for CountryID, CountryName in pairs( self.Filter.Countries ) do - --self:T3( { "Country:", MUnit:GetCountry(), CountryName } ) + for CountryID, CountryName in pairs(self.Filter.Countries) do + --self:T3({ "Country:", MUnit:GetCountry(), CountryName }) if country.id[CountryName] == MUnit:GetCountry() then MUnitCountry = true end @@ -3343,9 +3354,9 @@ do -- SET_UNIT if self.Filter.UnitPrefixes and MUnitInclude then local MUnitPrefix = false - for UnitPrefixId, UnitPrefix in pairs( self.Filter.UnitPrefixes ) do - --self:T3( { "Prefix:", string.find( MUnit:GetName(), UnitPrefix, 1 ), UnitPrefix } ) - if string.find( MUnit:GetName(), UnitPrefix, 1 ) then + for UnitPrefixId, UnitPrefix in pairs(self.Filter.UnitPrefixes) do + --self:T3({ "Prefix:", string.find(MUnit:GetName(), UnitPrefix, 1), UnitPrefix }) + if string.find(MUnit:GetName(), UnitPrefix, 1) then MUnitPrefix = true end end @@ -3354,11 +3365,11 @@ do -- SET_UNIT if self.Filter.RadarTypes and MUnitInclude then local MUnitRadar = false - for RadarTypeID, RadarType in pairs( self.Filter.RadarTypes ) do - --self:T3( { "Radar:", RadarType } ) - if MUnit:HasSensors( Unit.SensorType.RADAR, RadarType ) == true then + for RadarTypeID, RadarType in pairs(self.Filter.RadarTypes) do + --self:T3({ "Radar:", RadarType }) + if MUnit:HasSensors(Unit.SensorType.RADAR, RadarType) == true then if MUnit:GetRadar() == true then -- This call is necessary to evaluate the SEAD capability. - --self:T3( "RADAR Found" ) + --self:T3("RADAR Found") end MUnitRadar = true end @@ -3369,7 +3380,7 @@ do -- SET_UNIT if self.Filter.SEAD and MUnitInclude then local MUnitSEAD = false if MUnit:HasSEAD() == true then - --self:T3( "SEAD Found" ) + --self:T3("SEAD Found") MUnitSEAD = true end MUnitInclude = MUnitInclude and MUnitSEAD @@ -3378,8 +3389,8 @@ do -- SET_UNIT if self.Filter.Zones and MUnitInclude then local MGroupZone = false - for ZoneName, Zone in pairs( self.Filter.Zones ) do - --self:T3( "Zone:", ZoneName ) + for ZoneName, Zone in pairs(self.Filter.Zones) do + --self:T3("Zone:", ZoneName) if MUnit:IsInZone(Zone) then MGroupZone = true end @@ -3392,7 +3403,7 @@ do -- SET_UNIT MUnitInclude = MUnitInclude and MUnitFunc end - --self:T2( MUnitInclude ) + --self:T2(MUnitInclude) return MUnitInclude end @@ -3400,24 +3411,24 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param #string Delimiter (Optional) The delimiter, which is default a comma. -- @return #string The types of the @{Wrapper.Unit}s delimited. - function SET_UNIT:GetTypeNames( Delimiter ) + function SET_UNIT:GetTypeNames(Delimiter) Delimiter = Delimiter or ", " local TypeReport = REPORT:New() local Types = {} - for UnitName, UnitData in pairs( self:GetSet() ) do + for UnitName, UnitData in pairs(self:GetSet()) do local Unit = UnitData -- Wrapper.Unit#UNIT local UnitTypeName = Unit:GetTypeName() if not Types[UnitTypeName] then Types[UnitTypeName] = UnitTypeName - TypeReport:Add( UnitTypeName ) + TypeReport:Add(UnitTypeName) end end - return TypeReport:Text( Delimiter ) + return TypeReport:Text(Delimiter) end --- Iterate the SET_UNIT and set for each unit the default cargo bay weight limit. @@ -3428,7 +3439,7 @@ do -- SET_UNIT -- MySetUnit:SetCargoBayWeightLimit() function SET_UNIT:SetCargoBayWeightLimit() local Set = self:GetSet() - for UnitID, UnitData in pairs( Set ) do -- For each UNIT in SET_UNIT + for UnitID, UnitData in pairs(Set) do -- For each UNIT in SET_UNIT -- local UnitData = UnitData -- Wrapper.Unit#UNIT UnitData:SetCargoBayWeightLimit() end @@ -3540,7 +3551,7 @@ do -- SET_STATIC function SET_STATIC:New() -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.STATICS ) ) -- Core.Set#SET_STATIC + local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.STATICS)) -- Core.Set#SET_STATIC return self end @@ -3549,10 +3560,10 @@ do -- SET_STATIC -- @param #SET_STATIC self -- @param Wrapper.Static#STATIC AddStatic A single STATIC. -- @return #SET_STATIC self - function SET_STATIC:AddStatic( AddStatic ) - --self:F2( AddStatic:GetName() ) + function SET_STATIC:AddStatic(AddStatic) + --self:F2(AddStatic:GetName()) - self:Add( AddStatic:GetName(), AddStatic ) + self:Add(AddStatic:GetName(), AddStatic) return self end @@ -3561,13 +3572,13 @@ do -- SET_STATIC -- @param #SET_STATIC self -- @param #string AddStaticNames A single name or an array of STATIC names. -- @return #SET_STATIC self - function SET_STATIC:AddStaticsByName( AddStaticNames ) + function SET_STATIC:AddStaticsByName(AddStaticNames) - local AddStaticNamesArray = (type( AddStaticNames ) == "table") and AddStaticNames or { AddStaticNames } + local AddStaticNamesArray = (type(AddStaticNames) == "table") and AddStaticNames or { AddStaticNames } - --self:T(( AddStaticNamesArray ) - for AddStaticID, AddStaticName in pairs( AddStaticNamesArray ) do - self:Add( AddStaticName, STATIC:FindByName( AddStaticName ) ) + --self:T((AddStaticNamesArray) + for AddStaticID, AddStaticName in pairs(AddStaticNamesArray) do + self:Add(AddStaticName, STATIC:FindByName(AddStaticName)) end return self @@ -3577,12 +3588,12 @@ do -- SET_STATIC -- @param Core.Set#SET_STATIC self -- @param Wrapper.Static#STATIC RemoveStaticNames A single name or an array of STATIC names. -- @return self - function SET_STATIC:RemoveStaticsByName( RemoveStaticNames ) + function SET_STATIC:RemoveStaticsByName(RemoveStaticNames) - local RemoveStaticNamesArray = (type( RemoveStaticNames ) == "table") and RemoveStaticNames or { RemoveStaticNames } + local RemoveStaticNamesArray = (type(RemoveStaticNames) == "table") and RemoveStaticNames or { RemoveStaticNames } - for RemoveStaticID, RemoveStaticName in pairs( RemoveStaticNamesArray ) do - self:Remove( RemoveStaticName ) + for RemoveStaticID, RemoveStaticName in pairs(RemoveStaticNamesArray) do + self:Remove(RemoveStaticName) end return self @@ -3592,49 +3603,37 @@ do -- SET_STATIC -- @param #SET_STATIC self -- @param #string StaticName -- @return Wrapper.Static#STATIC The found Static. - function SET_STATIC:FindStatic( StaticName ) + function SET_STATIC:FindStatic(StaticName) local StaticFound = self.Set[StaticName] return StaticFound end - + --- Builds a set of units of coalitions. -- Possible current coalitions are red, blue and neutral. -- @param #SET_STATIC self -- @param #string Coalitions Can take the following values: "red", "blue", "neutral". -- @return #SET_STATIC self - function SET_STATIC:FilterCoalitions( Coalitions ) - if not self.Filter.Coalitions then - self.Filter.Coalitions = {} - end - if type( Coalitions ) ~= "table" then - Coalitions = { Coalitions } - end - for CoalitionID, Coalition in pairs( Coalitions ) do - self.Filter.Coalitions[Coalition] = Coalition - end - return self - end - + --- Builds a set of statics in zones. -- @param #SET_STATIC self -- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE -- @return #SET_STATIC self - function SET_STATIC:FilterZones( Zones ) + function SET_STATIC:FilterZones(Zones) if not self.Filter.Zones then self.Filter.Zones = {} end local zones = {} if Zones.ClassName and Zones.ClassName == "SET_ZONE" then zones = Zones.Set - elseif type( Zones ) ~= "table" or (type( Zones ) == "table" and Zones.ClassName ) then + elseif type(Zones) ~= "table" or (type(Zones) == "table" and Zones.ClassName) then self:E("***** FilterZones needs either a table of ZONE Objects or a SET_ZONE as parameter!") return self else zones = Zones end - for _,Zone in pairs( zones ) do + for _,Zone in pairs(zones) do local zonename = Zone:GetName() self.Filter.Zones[zonename] = Zone end @@ -3646,14 +3645,14 @@ do -- SET_STATIC -- @param #SET_STATIC self -- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship". -- @return #SET_STATIC self - function SET_STATIC:FilterCategories( Categories ) + function SET_STATIC:FilterCategories(Categories) if not self.Filter.Categories then self.Filter.Categories = {} end - if type( Categories ) ~= "table" then + if type(Categories) ~= "table" then Categories = { Categories } end - for CategoryID, Category in pairs( Categories ) do + for CategoryID, Category in pairs(Categories) do self.Filter.Categories[Category] = Category end return self @@ -3664,14 +3663,14 @@ do -- SET_STATIC -- @param #SET_STATIC self -- @param #string Types Can take those type strings known within DCS world. -- @return #SET_STATIC self - function SET_STATIC:FilterTypes( Types ) + function SET_STATIC:FilterTypes(Types) if not self.Filter.Types then self.Filter.Types = {} end - if type( Types ) ~= "table" then + if type(Types) ~= "table" then Types = { Types } end - for TypeID, Type in pairs( Types ) do + for TypeID, Type in pairs(Types) do self.Filter.Types[Type] = Type end return self @@ -3692,7 +3691,7 @@ do -- SET_STATIC -- if static:GetName() == "Exclude Me" then isinclude = false end -- return isinclude -- end - -- ):FilterOnce() + -- ):FilterOnce() -- BASE:I(groundset:Flush()) --- Builds a set of units of defined countries. @@ -3700,14 +3699,14 @@ do -- SET_STATIC -- @param #SET_STATIC self -- @param #string Countries Can take those country strings known within DCS world. -- @return #SET_STATIC self - function SET_STATIC:FilterCountries( Countries ) + function SET_STATIC:FilterCountries(Countries) if not self.Filter.Countries then self.Filter.Countries = {} end - if type( Countries ) ~= "table" then + if type(Countries) ~= "table" then Countries = { Countries } end - for CountryID, Country in pairs( Countries ) do + for CountryID, Country in pairs(Countries) do self.Filter.Countries[Country] = Country end return self @@ -3718,14 +3717,14 @@ do -- SET_STATIC -- @param #SET_STATIC self -- @param #string Prefixes The string pattern(s) that need to be contained in the static name. Can also be passed as a `#table` of strings. -- @return #SET_STATIC self - function SET_STATIC:FilterPrefixes( Prefixes ) + function SET_STATIC:FilterPrefixes(Prefixes) if not self.Filter.StaticPrefixes then self.Filter.StaticPrefixes = {} end - if type( Prefixes ) ~= "table" then + if type(Prefixes) ~= "table" then Prefixes = { Prefixes } end - for PrefixID, Prefix in pairs( Prefixes ) do + for PrefixID, Prefix in pairs(Prefixes) do self.Filter.StaticPrefixes[Prefix] = Prefix end return self @@ -3738,9 +3737,9 @@ do -- SET_STATIC if _DATABASE then self:_FilterStart() - self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) - self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.UnitLost, self._EventOnDeadOrCrash ) + self:HandleEvent(EVENTS.Birth, self._EventOnBirth) + self:HandleEvent(EVENTS.Dead, self._EventOnDeadOrCrash) + self:HandleEvent(EVENTS.UnitLost, self._EventOnDeadOrCrash) end return self @@ -3754,7 +3753,7 @@ do -- SET_STATIC local Set = self:GetSet() local CountU = 0 - for UnitID, UnitData in pairs( Set ) do + for UnitID, UnitData in pairs(Set) do if UnitData and UnitData:IsAlive() then CountU = CountU + 1 end @@ -3770,13 +3769,13 @@ do -- SET_STATIC -- @param Core.Event#EVENTDATA Event -- @return #string The name of the STATIC -- @return #table The STATIC - function SET_STATIC:AddInDatabase( Event ) - --self:F3( { Event } ) + function SET_STATIC:AddInDatabase(Event) + --self:F3({ Event }) if Event.IniObjectCategory == Object.Category.STATIC then if not self.Database[Event.IniDCSUnitName] then - self.Database[Event.IniDCSUnitName] = STATIC:Register( Event.IniDCSUnitName ) - --self:T(3( self.Database[Event.IniDCSUnitName] ) + self.Database[Event.IniDCSUnitName] = STATIC:Register(Event.IniDCSUnitName) + --self:T(3(self.Database[Event.IniDCSUnitName]) end end @@ -3789,8 +3788,8 @@ do -- SET_STATIC -- @param Core.Event#EVENTDATA Event -- @return #string The name of the STATIC -- @return #table The STATIC - function SET_STATIC:FindInDatabase( Event ) - --self:F2( { Event.IniDCSUnitName, self.Set[Event.IniDCSUnitName], Event } ) + function SET_STATIC:FindInDatabase(Event) + --self:F2({ Event.IniDCSUnitName, self.Set[Event.IniDCSUnitName], Event }) return Event.IniDCSUnitName, self.Set[Event.IniDCSUnitName] end @@ -3801,14 +3800,14 @@ do -- SET_STATIC -- @param #SET_STATIC self -- @param Core.Zone#ZONE Zone The Zone to be tested for. -- @return #boolean - function SET_STATIC:IsPartiallyInZone( Zone ) + function SET_STATIC:IsPartiallyInZone(Zone) local IsPartiallyInZone = false - local function EvaluateZone( ZoneStatic ) + local function EvaluateZone(ZoneStatic) local ZoneStaticName = ZoneStatic:GetName() - if self:FindStatic( ZoneStaticName ) then + if self:FindStatic(ZoneStaticName) then IsPartiallyInZone = true return false end @@ -3823,14 +3822,14 @@ do -- SET_STATIC -- @param #SET_STATIC self -- @param Core.Zone#ZONE Zone The Zone to be tested for. -- @return #boolean - function SET_STATIC:IsNotInZone( Zone ) + function SET_STATIC:IsNotInZone(Zone) local IsNotInZone = true - local function EvaluateZone( ZoneStatic ) + local function EvaluateZone(ZoneStatic) local ZoneStaticName = ZoneStatic:GetName() - if self:FindStatic( ZoneStaticName ) then + if self:FindStatic(ZoneStaticName) then IsNotInZone = false return false end @@ -3838,7 +3837,7 @@ do -- SET_STATIC return true end - Zone:Search( EvaluateZone ) + Zone:Search(EvaluateZone) return IsNotInZone end @@ -3847,10 +3846,10 @@ do -- SET_STATIC -- @param #SET_STATIC self -- @param #function IteratorFunction The function that will be called when there is an alive STATIC in the SET_STATIC. The function needs to accept a STATIC parameter. -- @return #SET_STATIC self - function SET_STATIC:ForEachStaticInZone( IteratorFunction, ... ) - --self:F2( arg ) + function SET_STATIC:ForEachStaticInZone(IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet() ) + self:ForEach(IteratorFunction, arg, self:GetSet()) return self end @@ -3862,10 +3861,10 @@ do -- SET_STATIC -- @param #SET_STATIC self -- @param #function IteratorFunction The function that will be called when there is an alive STATIC in the SET_STATIC. The function needs to accept a STATIC parameter. -- @return #SET_STATIC self - function SET_STATIC:ForEachStatic( IteratorFunction, ... ) - --self:F2( arg ) + function SET_STATIC:ForEachStatic(IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet() ) + self:ForEach(IteratorFunction, arg, self:GetSet()) return self end @@ -3875,19 +3874,19 @@ do -- SET_STATIC -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. -- @param #function IteratorFunction The function that will be called when there is an alive STATIC in the SET_STATIC. The function needs to accept a STATIC parameter. -- @return #SET_STATIC self - function SET_STATIC:ForEachStaticCompletelyInZone( ZoneObject, IteratorFunction, ... ) - --self:F2( arg ) + function SET_STATIC:ForEachStaticCompletelyInZone(ZoneObject, IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet(), + self:ForEach(IteratorFunction, arg, self:GetSet(), -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Static#STATIC StaticObject - function( ZoneObject, StaticObject ) - if StaticObject:IsInZone( ZoneObject ) then + function(ZoneObject, StaticObject) + if StaticObject:IsInZone(ZoneObject) then return true else return false end - end, { ZoneObject } ) + end, { ZoneObject }) return self end @@ -3897,19 +3896,19 @@ do -- SET_STATIC -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. -- @param #function IteratorFunction The function that will be called when there is an alive STATIC in the SET_STATIC. The function needs to accept a STATIC parameter. -- @return #SET_STATIC self - function SET_STATIC:ForEachStaticNotInZone( ZoneObject, IteratorFunction, ... ) - --self:F2( arg ) + function SET_STATIC:ForEachStaticNotInZone(ZoneObject, IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet(), + self:ForEach(IteratorFunction, arg, self:GetSet(), -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Static#STATIC StaticObject - function( ZoneObject, StaticObject ) - if StaticObject:IsNotInZone( ZoneObject ) then + function(ZoneObject, StaticObject) + if StaticObject:IsNotInZone(ZoneObject) then return true else return false end - end, { ZoneObject } ) + end, { ZoneObject }) return self end @@ -3923,7 +3922,7 @@ do -- SET_STATIC local MT = {} -- Message Text local StaticTypes = {} - for StaticID, StaticData in pairs( self:GetSet() ) do + for StaticID, StaticData in pairs(self:GetSet()) do local TextStatic = StaticData -- Wrapper.Static#STATIC if TextStatic:IsAlive() then local StaticType = TextStatic:GetTypeName() @@ -3936,7 +3935,7 @@ do -- SET_STATIC end end - for StaticTypeID, StaticType in pairs( StaticTypes ) do + for StaticTypeID, StaticType in pairs(StaticTypes) do MT[#MT + 1] = StaticType .. " of " .. StaticTypeID end @@ -3952,11 +3951,11 @@ do -- SET_STATIC local MT = {} -- Message Text local StaticTypes = self:GetStaticTypes() - for StaticTypeID, StaticType in pairs( StaticTypes ) do + for StaticTypeID, StaticType in pairs(StaticTypes) do MT[#MT + 1] = StaticType .. " of " .. StaticTypeID end - return table.concat( MT, ", " ) + return table.concat(MT, ", ") end --- Get the center coordinate of the SET_STATIC. @@ -3976,7 +3975,7 @@ do -- SET_STATIC local AvgHeading = nil local MovingCount = 0 - for StaticName, StaticData in pairs( self:GetSet() ) do + for StaticName, StaticData in pairs(self:GetSet()) do local Static = StaticData -- Wrapper.Static#STATIC local Coordinate = Static:GetCoordinate() @@ -4002,10 +4001,10 @@ do -- SET_STATIC Coordinate.x = (x2 - x1) / 2 + x1 Coordinate.y = (y2 - y1) / 2 + y1 Coordinate.z = (z2 - z1) / 2 + z1 - Coordinate:SetHeading( AvgHeading ) - Coordinate:SetVelocity( MaxVelocity ) + Coordinate:SetHeading(AvgHeading) + Coordinate:SetVelocity(MaxVelocity) - --self:F( { Coordinate = Coordinate } ) + --self:F({ Coordinate = Coordinate }) return Coordinate end @@ -4027,7 +4026,7 @@ do -- SET_STATIC local HeadingSet = nil local MovingCount = 0 - for StaticName, StaticData in pairs( self:GetSet() ) do + for StaticName, StaticData in pairs(self:GetSet()) do local Static = StaticData -- Wrapper.Static#STATIC local Coordinate = Static:GetCoordinate() @@ -4039,7 +4038,7 @@ do -- SET_STATIC HeadingSet = Heading else local HeadingDiff = (HeadingSet - Heading + 180 + 360) % 360 - 180 - HeadingDiff = math.abs( HeadingDiff ) + HeadingDiff = math.abs(HeadingDiff) if HeadingDiff > 5 then HeadingSet = nil break @@ -4059,7 +4058,7 @@ do -- SET_STATIC local MaxThreatLevelA2G = 0 local MaxThreatText = "" - for StaticName, StaticData in pairs( self:GetSet() ) do + for StaticName, StaticData in pairs(self:GetSet()) do local ThreatStatic = StaticData -- Wrapper.Static#STATIC local ThreatLevelA2G, ThreatText = ThreatStatic:GetThreatLevel() if ThreatLevelA2G > MaxThreatLevelA2G then @@ -4068,7 +4067,7 @@ do -- SET_STATIC end end - --self:F( { MaxThreatLevelA2G = MaxThreatLevelA2G, MaxThreatText = MaxThreatText } ) + --self:F({ MaxThreatLevelA2G = MaxThreatLevelA2G, MaxThreatText = MaxThreatText }) return MaxThreatLevelA2G, MaxThreatText end @@ -4077,14 +4076,14 @@ do -- SET_STATIC -- @param #SET_STATIC self -- @param Wrapper.Static#STATIC MStatic -- @return #SET_STATIC self - function SET_STATIC:IsIncludeObject( MStatic ) - --self:F2( MStatic ) + function SET_STATIC:IsIncludeObject(MStatic) + --self:F2(MStatic) local MStaticInclude = true if self.Filter.Coalitions then local MStaticCoalition = false - for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do - --self:T(3( { "Coalition:", MStatic:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) + for CoalitionID, CoalitionName in pairs(self.Filter.Coalitions) do + --self:T(3({ "Coalition:", MStatic:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName }) if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == MStatic:GetCoalition() then MStaticCoalition = true end @@ -4094,8 +4093,8 @@ do -- SET_STATIC if self.Filter.Categories then local MStaticCategory = false - for CategoryID, CategoryName in pairs( self.Filter.Categories ) do - --self:T(3( { "Category:", MStatic:GetDesc().category, self.FilterMeta.Categories[CategoryName], CategoryName } ) + for CategoryID, CategoryName in pairs(self.Filter.Categories) do + --self:T(3({ "Category:", MStatic:GetDesc().category, self.FilterMeta.Categories[CategoryName], CategoryName }) if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == MStatic:GetDesc().category then MStaticCategory = true end @@ -4105,8 +4104,8 @@ do -- SET_STATIC if self.Filter.Types then local MStaticType = false - for TypeID, TypeName in pairs( self.Filter.Types ) do - --self:T(3( { "Type:", MStatic:GetTypeName(), TypeName } ) + for TypeID, TypeName in pairs(self.Filter.Types) do + --self:T(3({ "Type:", MStatic:GetTypeName(), TypeName }) if TypeName == MStatic:GetTypeName() then MStaticType = true end @@ -4116,8 +4115,8 @@ do -- SET_STATIC if self.Filter.Countries then local MStaticCountry = false - for CountryID, CountryName in pairs( self.Filter.Countries ) do - --self:T(3( { "Country:", MStatic:GetCountry(), CountryName } ) + for CountryID, CountryName in pairs(self.Filter.Countries) do + --self:T(3({ "Country:", MStatic:GetCountry(), CountryName }) if country.id[CountryName] == MStatic:GetCountry() then MStaticCountry = true end @@ -4127,9 +4126,9 @@ do -- SET_STATIC if self.Filter.StaticPrefixes then local MStaticPrefix = false - for StaticPrefixId, StaticPrefix in pairs( self.Filter.StaticPrefixes ) do - --self:T(3( { "Prefix:", string.find( MStatic:GetName(), StaticPrefix, 1 ), StaticPrefix } ) - if string.find( MStatic:GetName(), StaticPrefix, 1 ) then + for StaticPrefixId, StaticPrefix in pairs(self.Filter.StaticPrefixes) do + --self:T(3({ "Prefix:", string.find(MStatic:GetName(), StaticPrefix, 1), StaticPrefix }) + if string.find(MStatic:GetName(), StaticPrefix, 1) then MStaticPrefix = true end end @@ -4138,8 +4137,8 @@ do -- SET_STATIC if self.Filter.Zones then local MStaticZone = false - for ZoneName, Zone in pairs( self.Filter.Zones ) do - --self:T(3( "Zone:", ZoneName ) + for ZoneName, Zone in pairs(self.Filter.Zones) do + --self:T(3("Zone:", ZoneName) if MStatic and MStatic:IsInZone(Zone) then MStaticZone = true end @@ -4152,7 +4151,7 @@ do -- SET_STATIC MStaticInclude = MStaticInclude and MClientFunc end - --self:T(2( MStaticInclude ) + --self:T(2(MStaticInclude) return MStaticInclude end @@ -4160,24 +4159,24 @@ do -- SET_STATIC -- @param #SET_STATIC self -- @param #string Delimiter (Optional) The delimiter, which is default a comma. -- @return #string The types of the @{Wrapper.Static}s delimited. - function SET_STATIC:GetTypeNames( Delimiter ) + function SET_STATIC:GetTypeNames(Delimiter) Delimiter = Delimiter or ", " local TypeReport = REPORT:New() local Types = {} - for StaticName, StaticData in pairs( self:GetSet() ) do + for StaticName, StaticData in pairs(self:GetSet()) do local Static = StaticData -- Wrapper.Static#STATIC local StaticTypeName = Static:GetTypeName() if not Types[StaticTypeName] then Types[StaticTypeName] = StaticTypeName - TypeReport:Add( StaticTypeName ) + TypeReport:Add(StaticTypeName) end end - return TypeReport:Text( Delimiter ) + return TypeReport:Text(Delimiter) end --- Get the closest static of the set with respect to a given reference coordinate. Optionally, only statics of given coalitions are considered in the search. @@ -4192,7 +4191,7 @@ do -- SET_STATIC local dmin=math.huge local gmin=nil - for GroupID, GroupData in pairs( Set ) do -- For each STATIC in SET_STATIC + for GroupID, GroupData in pairs(Set) do -- For each STATIC in SET_STATIC local group=GroupData --Wrapper.Static#STATIC if group and group:IsAlive() and (Coalitions==nil or UTILS.IsAnyInTable(Coalitions, group:GetCoalition())) then @@ -4310,9 +4309,9 @@ do -- SET_CLIENT -- DBObject = SET_CLIENT:New() function SET_CLIENT:New() -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.CLIENTS ) ) -- #SET_CLIENT + local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.CLIENTS)) -- #SET_CLIENT - self:FilterActive( false ) + self:FilterActive(false) return self end @@ -4321,12 +4320,12 @@ do -- SET_CLIENT -- @param Core.Set#SET_CLIENT self -- @param #string AddClientNames A single name or an array of CLIENT names. -- @return self - function SET_CLIENT:AddClientsByName( AddClientNames ) + function SET_CLIENT:AddClientsByName(AddClientNames) - local AddClientNamesArray = (type( AddClientNames ) == "table") and AddClientNames or { AddClientNames } + local AddClientNamesArray = (type(AddClientNames) == "table") and AddClientNames or { AddClientNames } - for AddClientID, AddClientName in pairs( AddClientNamesArray ) do - self:Add( AddClientName, CLIENT:FindByName( AddClientName ) ) + for AddClientID, AddClientName in pairs(AddClientNamesArray) do + self:Add(AddClientName, CLIENT:FindByName(AddClientName)) end return self @@ -4336,12 +4335,12 @@ do -- SET_CLIENT -- @param Core.Set#SET_CLIENT self -- @param Wrapper.Client#CLIENT RemoveClientNames A single object or an array of CLIENT objects. -- @return self - function SET_CLIENT:RemoveClientsByName( RemoveClientNames ) + function SET_CLIENT:RemoveClientsByName(RemoveClientNames) - local RemoveClientNamesArray = (type( RemoveClientNames ) == "table") and RemoveClientNames or { RemoveClientNames } + local RemoveClientNamesArray = (type(RemoveClientNames) == "table") and RemoveClientNames or { RemoveClientNames } - for RemoveClientID, RemoveClientName in pairs( RemoveClientNamesArray ) do - self:Remove( RemoveClientName.ClientName ) + for RemoveClientID, RemoveClientName in pairs(RemoveClientNamesArray) do + self:Remove(RemoveClientName.ClientName) end return self @@ -4351,7 +4350,7 @@ do -- SET_CLIENT -- @param #SET_CLIENT self -- @param #string ClientName -- @return Wrapper.Client#CLIENT The found Client. - function SET_CLIENT:FindClient( ClientName ) + function SET_CLIENT:FindClient(ClientName) local ClientFound = self.Set[ClientName] return ClientFound @@ -4361,14 +4360,14 @@ do -- SET_CLIENT -- @param #SET_CLIENT self -- @param #string Callsigns Can be a single string e.g. "Ford", or a table of strings e.g. {"Uzi","Enfield","Chevy"}. Refers to the callsigns as they can be set in the mission editor. -- @return #SET_CLIENT self - function SET_CLIENT:FilterCallsigns( Callsigns ) + function SET_CLIENT:FilterCallsigns(Callsigns) if not self.Filter.Callsigns then self.Filter.Callsigns = {} end - if type( Callsigns ) ~= "table" then + if type(Callsigns) ~= "table" then Callsigns = { Callsigns } end - for callsignID, callsign in pairs( Callsigns ) do + for callsignID, callsign in pairs(Callsigns) do self.Filter.Callsigns[callsign] = callsign end return self @@ -4378,50 +4377,38 @@ do -- SET_CLIENT -- @param #SET_CLIENT self -- @param #string Playernames Can be a single string e.g. "Apple", or a table of strings e.g. {"Walter","Hermann","Gonzo"}. Useful if you have e.g. a common squadron prefix. -- @return #SET_CLIENT self - function SET_CLIENT:FilterPlayernames( Playernames ) + function SET_CLIENT:FilterPlayernames(Playernames) if not self.Filter.Playernames then self.Filter.Playernames = {} end - if type( Playernames ) ~= "table" then + if type(Playernames) ~= "table" then Playernames = { Playernames } end - for PlayernameID, playername in pairs( Playernames ) do + for PlayernameID, playername in pairs(Playernames) do self.Filter.Playernames[playername] = playername end return self end - + --- Builds a set of clients of coalitions. -- Possible current coalitions are red, blue and neutral. -- @param #SET_CLIENT self -- @param #string Coalitions Can take the following values: "red", "blue", "neutral". -- @return #SET_CLIENT self - function SET_CLIENT:FilterCoalitions( Coalitions ) - if not self.Filter.Coalitions then - self.Filter.Coalitions = {} - end - if type( Coalitions ) ~= "table" then - Coalitions = { Coalitions } - end - for CoalitionID, Coalition in pairs( Coalitions ) do - self.Filter.Coalitions[Coalition] = Coalition - end - return self - end - + --- Builds a set of clients out of categories. -- Possible current categories are plane, helicopter, ground, ship. -- @param #SET_CLIENT self -- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship". -- @return #SET_CLIENT self - function SET_CLIENT:FilterCategories( Categories ) + function SET_CLIENT:FilterCategories(Categories) if not self.Filter.Categories then self.Filter.Categories = {} end - if type( Categories ) ~= "table" then + if type(Categories) ~= "table" then Categories = { Categories } end - for CategoryID, Category in pairs( Categories ) do + for CategoryID, Category in pairs(Categories) do self.Filter.Categories[Category] = Category end return self @@ -4432,14 +4419,14 @@ do -- SET_CLIENT -- @param #SET_CLIENT self -- @param #string Types Can take those type strings known within DCS world. -- @return #SET_CLIENT self - function SET_CLIENT:FilterTypes( Types ) + function SET_CLIENT:FilterTypes(Types) if not self.Filter.Types then self.Filter.Types = {} end - if type( Types ) ~= "table" then + if type(Types) ~= "table" then Types = { Types } end - for TypeID, Type in pairs( Types ) do + for TypeID, Type in pairs(Types) do self.Filter.Types[Type] = Type end return self @@ -4450,14 +4437,14 @@ do -- SET_CLIENT -- @param #SET_CLIENT self -- @param #string Countries Can take those country strings known within DCS world. -- @return #SET_CLIENT self - function SET_CLIENT:FilterCountries( Countries ) + function SET_CLIENT:FilterCountries(Countries) if not self.Filter.Countries then self.Filter.Countries = {} end - if type( Countries ) ~= "table" then + if type(Countries) ~= "table" then Countries = { Countries } end - for CountryID, Country in pairs( Countries ) do + for CountryID, Country in pairs(Countries) do self.Filter.Countries[Country] = Country end return self @@ -4468,14 +4455,14 @@ do -- SET_CLIENT -- @param #SET_CLIENT self -- @param #string Prefixes The string pattern(s) that needs to be contained in the unit/pilot name. Can also be passed as a `#table` of strings. -- @return #SET_CLIENT self - function SET_CLIENT:FilterPrefixes( Prefixes ) + function SET_CLIENT:FilterPrefixes(Prefixes) if not self.Filter.ClientPrefixes then self.Filter.ClientPrefixes = {} end - if type( Prefixes ) ~= "table" then + if type(Prefixes) ~= "table" then Prefixes = { Prefixes } end - for PrefixID, Prefix in pairs( Prefixes ) do + for PrefixID, Prefix in pairs(Prefixes) do self.Filter.ClientPrefixes[Prefix] = Prefix end return self @@ -4506,7 +4493,7 @@ do -- SET_CLIENT end return outcome end, Prefixes - ) + ) return self end @@ -4522,15 +4509,15 @@ do -- SET_CLIENT -- ClientSet = SET_CLIENT:New():FilterActive():FilterStart() -- -- -- Include only active clients to the set of the blue coalition, and filter one time. - -- ClientSet = SET_CLIENT:New():FilterActive():FilterCoalition( "blue" ):FilterOnce() + -- ClientSet = SET_CLIENT:New():FilterActive():FilterCoalition("blue"):FilterOnce() -- -- -- Include only active clients to the set of the blue coalition, and filter one time. -- -- Later, reset to include back inactive clients to the set. - -- ClientSet = SET_CLIENT:New():FilterActive():FilterCoalition( "blue" ):FilterOnce() + -- ClientSet = SET_CLIENT:New():FilterActive():FilterCoalition("blue"):FilterOnce() -- ... logic ... - -- ClientSet = SET_CLIENT:New():FilterActive( false ):FilterCoalition( "blue" ):FilterOnce() + -- ClientSet = SET_CLIENT:New():FilterActive(false):FilterCoalition("blue"):FilterOnce() -- - function SET_CLIENT:FilterActive( Active ) + function SET_CLIENT:FilterActive(Active) Active = Active or not (Active == false) self.Filter.Active = Active return self @@ -4548,7 +4535,7 @@ do -- SET_CLIENT return false end end - ) + ) return self end @@ -4557,20 +4544,20 @@ do -- SET_CLIENT -- @param #SET_CLIENT self -- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE -- @return #SET_CLIENT self - function SET_CLIENT:FilterZones( Zones ) + function SET_CLIENT:FilterZones(Zones) if not self.Filter.Zones then self.Filter.Zones = {} end local zones = {} if Zones.ClassName and Zones.ClassName == "SET_ZONE" then zones = Zones.Set - elseif type( Zones ) ~= "table" or (type( Zones ) == "table" and Zones.ClassName ) then + elseif type(Zones) ~= "table" or (type(Zones) == "table" and Zones.ClassName) then self:E("***** FilterZones needs either a table of ZONE Objects or a SET_ZONE as parameter!") return self else zones = Zones end - for _,Zone in pairs( zones ) do + for _,Zone in pairs(zones) do local zonename = Zone:GetName() self.Filter.Zones[zonename] = Zone end @@ -4584,10 +4571,10 @@ do -- SET_CLIENT local Database = _DATABASE.CLIENTS - for ObjectName, Object in pairs( Database ) do - if self:IsIncludeObject( Object ) and self:IsNotInSet(Object) then - self:Add( ObjectName, Object ) - elseif (not self:IsIncludeObject( Object )) and self:IsInSet(Object) then + for ObjectName, Object in pairs(Database) do + if self:IsIncludeObject(Object) and self:IsNotInSet(Object) then + self:Add(ObjectName, Object) + elseif (not self:IsIncludeObject(Object)) and self:IsInSet(Object) then self:Remove(ObjectName) end end @@ -4633,11 +4620,11 @@ do -- SET_CLIENT function SET_CLIENT:FilterStart() if _DATABASE then - self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) - self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) - --self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventPlayerEnterUnit) - self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventPlayerLeaveUnit) + self:HandleEvent(EVENTS.Birth, self._EventOnBirth) + self:HandleEvent(EVENTS.Dead, self._EventOnDeadOrCrash) + self:HandleEvent(EVENTS.Crash, self._EventOnDeadOrCrash) + --self:HandleEvent(EVENTS.PlayerEnterUnit, self._EventPlayerEnterUnit) + self:HandleEvent(EVENTS.PlayerLeaveUnit, self._EventPlayerLeaveUnit) --self:SetEventPriority(1) if self.Filter.Zones then self.ZoneTimer = TIMER:New(self._ContinousZoneFilter,self) @@ -4655,14 +4642,14 @@ do -- SET_CLIENT -- @param Core.Event#EVENTDATA Event -- @return #SET_CLIENT self function SET_CLIENT:_EventPlayerEnterUnit(Event) - --self:I( "_EventPlayerEnterUnit" ) + --self:I("_EventPlayerEnterUnit") if Event.IniDCSUnit then if Event.IniObjectCategory == Object.Category.UNIT and Event.IniGroup and Event.IniGroup:IsGround() then -- CA Slot entered - local ObjectName, Object = self:AddInDatabase( Event ) - --self:T(( ObjectName, UTILS.PrintTableToLog(Object) ) - if Object and self:IsIncludeObject( Object ) then - self:Add( ObjectName, Object ) + local ObjectName, Object = self:AddInDatabase(Event) + --self:T((ObjectName, UTILS.PrintTableToLog(Object)) + if Object and self:IsIncludeObject(Object) then + self:Add(ObjectName, Object) end end end @@ -4674,13 +4661,13 @@ do -- SET_CLIENT -- @param Core.Event#EVENTDATA Event -- @return #SET_CLIENT self function SET_CLIENT:_EventPlayerLeaveUnit(Event) - --self:I( "_EventPlayerLeaveUnit" ) + --self:I("_EventPlayerLeaveUnit") if Event.IniDCSUnit then if Event.IniObjectCategory == Object.Category.UNIT and Event.IniGroup then --and Event.IniGroup:IsGround() then -- CA Slot left - local ObjectName, Object = self:FindInDatabase( Event ) + local ObjectName, Object = self:FindInDatabase(Event) if ObjectName then - self:Remove( ObjectName ) + self:Remove(ObjectName) end end end @@ -4703,8 +4690,8 @@ do -- SET_CLIENT -- @param Core.Event#EVENTDATA Event -- @return #string The name of the CLIENT -- @return #table The CLIENT - function SET_CLIENT:AddInDatabase( Event ) - --self:F3( { Event } ) + function SET_CLIENT:AddInDatabase(Event) + --self:F3({ Event }) return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end @@ -4715,8 +4702,8 @@ do -- SET_CLIENT -- @param Core.Event#EVENTDATA Event -- @return #string The name of the CLIENT -- @return #table The CLIENT - function SET_CLIENT:FindInDatabase( Event ) - --self:F3( { Event } ) + function SET_CLIENT:FindInDatabase(Event) + --self:F3({ Event }) return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end @@ -4725,10 +4712,10 @@ do -- SET_CLIENT -- @param #SET_CLIENT self -- @param #function IteratorFunction The function that will be called when there is an alive CLIENT in the SET_CLIENT. The function needs to accept a CLIENT parameter. -- @return #SET_CLIENT self - function SET_CLIENT:ForEachClient( IteratorFunction, ... ) - --self:F2( arg ) + function SET_CLIENT:ForEachClient(IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet() ) + self:ForEach(IteratorFunction, arg, self:GetSet()) return self end @@ -4738,19 +4725,19 @@ do -- SET_CLIENT -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. -- @param #function IteratorFunction The function that will be called when there is an alive CLIENT in the SET_CLIENT. The function needs to accept a CLIENT parameter. -- @return #SET_CLIENT self - function SET_CLIENT:ForEachClientInZone( ZoneObject, IteratorFunction, ... ) - --self:F2( arg ) + function SET_CLIENT:ForEachClientInZone(ZoneObject, IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet(), + self:ForEach(IteratorFunction, arg, self:GetSet(), -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Client#CLIENT ClientObject - function( ZoneObject, ClientObject ) - if ClientObject:IsInZone( ZoneObject ) then + function(ZoneObject, ClientObject) + if ClientObject:IsInZone(ZoneObject) then return true else return false end - end, { ZoneObject } ) + end, { ZoneObject }) return self end @@ -4760,19 +4747,19 @@ do -- SET_CLIENT -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. -- @param #function IteratorFunction The function that will be called when there is an alive CLIENT in the SET_CLIENT. The function needs to accept a CLIENT parameter. -- @return #SET_CLIENT self - function SET_CLIENT:ForEachClientNotInZone( ZoneObject, IteratorFunction, ... ) - --self:F2( arg ) + function SET_CLIENT:ForEachClientNotInZone(ZoneObject, IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet(), + self:ForEach(IteratorFunction, arg, self:GetSet(), -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Client#CLIENT ClientObject - function( ZoneObject, ClientObject ) - if ClientObject:IsNotInZone( ZoneObject ) then + function(ZoneObject, ClientObject) + if ClientObject:IsNotInZone(ZoneObject) then return true else return false end - end, { ZoneObject } ) + end, { ZoneObject }) return self end @@ -4785,7 +4772,7 @@ do -- SET_CLIENT local Set = self:GetSet() local CountU = 0 - for UnitID, UnitData in pairs( Set ) do -- For each GROUP in SET_GROUP + for UnitID, UnitData in pairs(Set) do -- For each GROUP in SET_GROUP if UnitData and UnitData:IsAlive() then CountU = CountU + 1 end @@ -4828,7 +4815,7 @@ do -- SET_CLIENT -- if client:GetPlayerName() == "Exclude Me" then isinclude = false end -- return isinclude -- end - -- ):FilterOnce() + -- ):FilterOnce() -- BASE:I(groundset:Flush()) @@ -4836,8 +4823,8 @@ do -- SET_CLIENT -- @param #SET_CLIENT self -- @param Wrapper.Client#CLIENT MClient -- @return #SET_CLIENT self - function SET_CLIENT:IsIncludeObject( MClient ) - --self:F2( MClient ) + function SET_CLIENT:IsIncludeObject(MClient) + --self:F2(MClient) local MClientInclude = true @@ -4849,94 +4836,94 @@ do -- SET_CLIENT if self.Filter.Active == false or (self.Filter.Active == true and MClient:IsActive() == true and MClient:IsAlive() == true) then MClientActive = true end - --self:T( { "Evaluated Active", MClientActive } ) + --self:T({ "Evaluated Active", MClientActive }) MClientInclude = MClientInclude and MClientActive end if self.Filter.Coalitions and MClientInclude then local MClientCoalition = false - for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do - local ClientCoalitionID = _DATABASE:GetCoalitionFromClientTemplate( MClientName ) + for CoalitionID, CoalitionName in pairs(self.Filter.Coalitions) do + local ClientCoalitionID = _DATABASE:GetCoalitionFromClientTemplate(MClientName) if ClientCoalitionID==nil and MClient:IsAlive()~=nil then ClientCoalitionID=MClient:GetCoalition() end - --self:T3( { "Coalition:", ClientCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) + --self:T3({ "Coalition:", ClientCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName }) if self.FilterMeta.Coalitions[CoalitionName] and ClientCoalitionID and self.FilterMeta.Coalitions[CoalitionName] == ClientCoalitionID then MClientCoalition = true end end - --self:T( { "Evaluated Coalition", MClientCoalition } ) + --self:T({ "Evaluated Coalition", MClientCoalition }) MClientInclude = MClientInclude and MClientCoalition end if self.Filter.Categories and MClientInclude then local MClientCategory = false - for CategoryID, CategoryName in pairs( self.Filter.Categories ) do - local ClientCategoryID = _DATABASE:GetCategoryFromClientTemplate( MClientName ) + for CategoryID, CategoryName in pairs(self.Filter.Categories) do + local ClientCategoryID = _DATABASE:GetCategoryFromClientTemplate(MClientName) local UnitCategory = 0 if ClientCategoryID==nil and MClient:IsExist() then ClientCategoryID,UnitCategory=MClient:GetCategory() --self:T3("Applying Category Workaround .. Outcome: Obj is "..tostring(ClientCategoryID).." Unit is "..tostring(UnitCategory)) - --self:T(3( { "Category:", UnitCategory, self.FilterMeta.Categories[CategoryName], CategoryName } ) + --self:T(3({ "Category:", UnitCategory, self.FilterMeta.Categories[CategoryName], CategoryName }) if self.FilterMeta.Categories[CategoryName] and UnitCategory and self.FilterMeta.Categories[CategoryName] == UnitCategory then MClientCategory = true end --self:T3("Filter Outcome is "..tostring(MClientCategory)) else - --self:T3( { "Category:", ClientCategoryID, self.FilterMeta.Categories[CategoryName], CategoryName } ) + --self:T3({ "Category:", ClientCategoryID, self.FilterMeta.Categories[CategoryName], CategoryName }) if self.FilterMeta.Categories[CategoryName] and ClientCategoryID and self.FilterMeta.Categories[CategoryName] == ClientCategoryID then MClientCategory = true end end end - --self:T( { "Evaluated Category", MClientCategory } ) + --self:T({ "Evaluated Category", MClientCategory }) MClientInclude = MClientInclude and MClientCategory end if self.Filter.Types and MClientInclude then local MClientType = false - for TypeID, TypeName in pairs( self.Filter.Types ) do - --self:T3( { "Type:", MClient:GetTypeName(), TypeName } ) + for TypeID, TypeName in pairs(self.Filter.Types) do + --self:T3({ "Type:", MClient:GetTypeName(), TypeName }) if TypeName == MClient:GetTypeName() then MClientType = true end end - --self:T(( { "Evaluated Type", MClientType } ) + --self:T(({ "Evaluated Type", MClientType }) MClientInclude = MClientInclude and MClientType end if self.Filter.Countries and MClientInclude then local MClientCountry = false - for CountryID, CountryName in pairs( self.Filter.Countries ) do - local ClientCountryID = _DATABASE:GetCountryFromClientTemplate( MClientName ) + for CountryID, CountryName in pairs(self.Filter.Countries) do + local ClientCountryID = _DATABASE:GetCountryFromClientTemplate(MClientName) if ClientCountryID==nil and MClient:IsAlive()~=nil then ClientCountryID=MClient:GetCountry() end - --self:T(3( { "Country:", ClientCountryID, country.id[CountryName], CountryName } ) + --self:T(3({ "Country:", ClientCountryID, country.id[CountryName], CountryName }) if country.id[CountryName] and ClientCountryID and country.id[CountryName] == ClientCountryID then MClientCountry = true end end - --self:T(( { "Evaluated Country", MClientCountry } ) + --self:T(({ "Evaluated Country", MClientCountry }) MClientInclude = MClientInclude and MClientCountry end if self.Filter.ClientPrefixes and MClientInclude then local MClientPrefix = false - for ClientPrefixId, ClientPrefix in pairs( self.Filter.ClientPrefixes ) do - --self:T3( { "Prefix:", string.find( MClient.UnitName, ClientPrefix, 1 ), ClientPrefix } ) - if string.find( MClient.UnitName, ClientPrefix, 1 ) then + for ClientPrefixId, ClientPrefix in pairs(self.Filter.ClientPrefixes) do + --self:T3({ "Prefix:", string.find(MClient.UnitName, ClientPrefix, 1), ClientPrefix }) + if string.find(MClient.UnitName, ClientPrefix, 1) then MClientPrefix = true end end - --self:T( { "Evaluated Prefix", MClientPrefix } ) + --self:T({ "Evaluated Prefix", MClientPrefix }) MClientInclude = MClientInclude and MClientPrefix end if self.Filter.Zones and MClientInclude then local MClientZone = false - for ZoneName, Zone in pairs( self.Filter.Zones ) do - --self:T3( "Zone:", ZoneName ) + for ZoneName, Zone in pairs(self.Filter.Zones) do + --self:T3("Zone:", ZoneName) local unit = MClient:GetClientGroupUnit() if unit and unit:IsInZone(Zone) then MClientZone = true @@ -4954,7 +4941,7 @@ do -- SET_CLIENT MClientPlayername = true end end - --self:T( { "Evaluated Playername", MClientPlayername } ) + --self:T({ "Evaluated Playername", MClientPlayername }) MClientInclude = MClientInclude and MClientPlayername end @@ -4967,7 +4954,7 @@ do -- SET_CLIENT MClientCallsigns = true end end - --self:T( { "Evaluated Callsign", MClientCallsigns } ) + --self:T({ "Evaluated Callsign", MClientCallsigns }) MClientInclude = MClientInclude and MClientCallsigns end @@ -4977,7 +4964,7 @@ do -- SET_CLIENT end end - --self:T2( MClientInclude ) + --self:T2(MClientInclude) return MClientInclude end @@ -5062,7 +5049,7 @@ do -- SET_PLAYER -- DBObject = SET_PLAYER:New() function SET_PLAYER:New() -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.PLAYERS ) ) + local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.PLAYERS)) return self end @@ -5071,12 +5058,12 @@ do -- SET_PLAYER -- @param Core.Set#SET_PLAYER self -- @param #string AddClientNames A single name or an array of CLIENT names. -- @return self - function SET_PLAYER:AddClientsByName( AddClientNames ) + function SET_PLAYER:AddClientsByName(AddClientNames) - local AddClientNamesArray = (type( AddClientNames ) == "table") and AddClientNames or { AddClientNames } + local AddClientNamesArray = (type(AddClientNames) == "table") and AddClientNames or { AddClientNames } - for AddClientID, AddClientName in pairs( AddClientNamesArray ) do - self:Add( AddClientName, CLIENT:FindByName( AddClientName ) ) + for AddClientID, AddClientName in pairs(AddClientNamesArray) do + self:Add(AddClientName, CLIENT:FindByName(AddClientName)) end return self @@ -5086,12 +5073,12 @@ do -- SET_PLAYER -- @param Core.Set#SET_PLAYER self -- @param Wrapper.Client#CLIENT RemoveClientNames A single name or an array of CLIENT names. -- @return self - function SET_PLAYER:RemoveClientsByName( RemoveClientNames ) + function SET_PLAYER:RemoveClientsByName(RemoveClientNames) - local RemoveClientNamesArray = (type( RemoveClientNames ) == "table") and RemoveClientNames or { RemoveClientNames } + local RemoveClientNamesArray = (type(RemoveClientNames) == "table") and RemoveClientNames or { RemoveClientNames } - for RemoveClientID, RemoveClientName in pairs( RemoveClientNamesArray ) do - self:Remove( RemoveClientName.ClientName ) + for RemoveClientID, RemoveClientName in pairs(RemoveClientNamesArray) do + self:Remove(RemoveClientName.ClientName) end return self @@ -5101,48 +5088,36 @@ do -- SET_PLAYER -- @param #SET_PLAYER self -- @param #string PlayerName -- @return Wrapper.Client#CLIENT The found Client. - function SET_PLAYER:FindClient( PlayerName ) + function SET_PLAYER:FindClient(PlayerName) local ClientFound = self.Set[PlayerName] return ClientFound end - + --- Builds a set of clients of coalitions joined by specific players. -- Possible current coalitions are red, blue and neutral. -- @param #SET_PLAYER self -- @param #string Coalitions Can take the following values: "red", "blue", "neutral". -- @return #SET_PLAYER self - function SET_PLAYER:FilterCoalitions( Coalitions ) - if not self.Filter.Coalitions then - self.Filter.Coalitions = {} - end - if type( Coalitions ) ~= "table" then - Coalitions = { Coalitions } - end - for CoalitionID, Coalition in pairs( Coalitions ) do - self.Filter.Coalitions[Coalition] = Coalition - end - return self - end --- Builds a set of players in zones. -- @param #SET_PLAYER self -- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE -- @return #SET_PLAYER self - function SET_PLAYER:FilterZones( Zones ) + function SET_PLAYER:FilterZones(Zones) if not self.Filter.Zones then self.Filter.Zones = {} end local zones = {} if Zones.ClassName and Zones.ClassName == "SET_ZONE" then zones = Zones.Set - elseif type( Zones ) ~= "table" or (type( Zones ) == "table" and Zones.ClassName ) then + elseif type(Zones) ~= "table" or (type(Zones) == "table" and Zones.ClassName) then self:E("***** FilterZones needs either a table of ZONE Objects or a SET_ZONE as parameter!") return self else zones = Zones end - for _,Zone in pairs( zones ) do + for _,Zone in pairs(zones) do local zonename = Zone:GetName() self.Filter.Zones[zonename] = Zone end @@ -5155,14 +5130,14 @@ do -- SET_PLAYER -- @param #SET_PLAYER self -- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship". -- @return #SET_PLAYER self - function SET_PLAYER:FilterCategories( Categories ) + function SET_PLAYER:FilterCategories(Categories) if not self.Filter.Categories then self.Filter.Categories = {} end - if type( Categories ) ~= "table" then + if type(Categories) ~= "table" then Categories = { Categories } end - for CategoryID, Category in pairs( Categories ) do + for CategoryID, Category in pairs(Categories) do self.Filter.Categories[Category] = Category end return self @@ -5173,14 +5148,14 @@ do -- SET_PLAYER -- @param #SET_PLAYER self -- @param #string Types Can take those type strings known within DCS world. -- @return #SET_PLAYER self - function SET_PLAYER:FilterTypes( Types ) + function SET_PLAYER:FilterTypes(Types) if not self.Filter.Types then self.Filter.Types = {} end - if type( Types ) ~= "table" then + if type(Types) ~= "table" then Types = { Types } end - for TypeID, Type in pairs( Types ) do + for TypeID, Type in pairs(Types) do self.Filter.Types[Type] = Type end return self @@ -5191,14 +5166,14 @@ do -- SET_PLAYER -- @param #SET_PLAYER self -- @param #string Countries Can take those country strings known within DCS world. -- @return #SET_PLAYER self - function SET_PLAYER:FilterCountries( Countries ) + function SET_PLAYER:FilterCountries(Countries) if not self.Filter.Countries then self.Filter.Countries = {} end - if type( Countries ) ~= "table" then + if type(Countries) ~= "table" then Countries = { Countries } end - for CountryID, Country in pairs( Countries ) do + for CountryID, Country in pairs(Countries) do self.Filter.Countries[Country] = Country end return self @@ -5209,14 +5184,14 @@ do -- SET_PLAYER -- @param #SET_PLAYER self -- @param #string Prefixes The string pattern(s) that needs to be contained in the unit/pilot name. Can also be passed as a `#table` of strings. -- @return #SET_PLAYER self - function SET_PLAYER:FilterPrefixes( Prefixes ) + function SET_PLAYER:FilterPrefixes(Prefixes) if not self.Filter.ClientPrefixes then self.Filter.ClientPrefixes = {} end - if type( Prefixes ) ~= "table" then + if type(Prefixes) ~= "table" then Prefixes = { Prefixes } end - for PrefixID, Prefix in pairs( Prefixes ) do + for PrefixID, Prefix in pairs(Prefixes) do self.Filter.ClientPrefixes[Prefix] = Prefix end return self @@ -5229,10 +5204,10 @@ do -- SET_PLAYER if _DATABASE then self:_FilterStart() - self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) - self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventOnDeadOrCrash ) + self:HandleEvent(EVENTS.Birth, self._EventOnBirth) + self:HandleEvent(EVENTS.Dead, self._EventOnDeadOrCrash) + self:HandleEvent(EVENTS.Crash, self._EventOnDeadOrCrash) + self:HandleEvent(EVENTS.PlayerLeaveUnit, self._EventOnDeadOrCrash) end return self @@ -5244,8 +5219,8 @@ do -- SET_PLAYER -- @param Core.Event#EVENTDATA Event -- @return #string The name of the CLIENT -- @return #table The CLIENT - function SET_PLAYER:AddInDatabase( Event ) - --self:F3( { Event } ) + function SET_PLAYER:AddInDatabase(Event) + --self:F3({ Event }) return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end @@ -5256,8 +5231,8 @@ do -- SET_PLAYER -- @param Core.Event#EVENTDATA Event -- @return #string The name of the CLIENT -- @return #table The CLIENT - function SET_PLAYER:FindInDatabase( Event ) - --self:F3( { Event } ) + function SET_PLAYER:FindInDatabase(Event) + --self:F3({ Event }) return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end @@ -5266,10 +5241,10 @@ do -- SET_PLAYER -- @param #SET_PLAYER self -- @param #function IteratorFunction The function that will be called when there is an alive CLIENT in the SET_PLAYER. The function needs to accept a CLIENT parameter. -- @return #SET_PLAYER self - function SET_PLAYER:ForEachPlayer( IteratorFunction, ... ) - --self:F2( arg ) + function SET_PLAYER:ForEachPlayer(IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet() ) + self:ForEach(IteratorFunction, arg, self:GetSet()) return self end @@ -5279,19 +5254,19 @@ do -- SET_PLAYER -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. -- @param #function IteratorFunction The function that will be called when there is an alive CLIENT in the SET_PLAYER. The function needs to accept a CLIENT parameter. -- @return #SET_PLAYER self - function SET_PLAYER:ForEachPlayerInZone( ZoneObject, IteratorFunction, ... ) - --self:F2( arg ) + function SET_PLAYER:ForEachPlayerInZone(ZoneObject, IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet(), + self:ForEach(IteratorFunction, arg, self:GetSet(), -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Client#CLIENT ClientObject - function( ZoneObject, ClientObject ) - if ClientObject:IsInZone( ZoneObject ) then + function(ZoneObject, ClientObject) + if ClientObject:IsInZone(ZoneObject) then return true else return false end - end, { ZoneObject } ) + end, { ZoneObject }) return self end @@ -5301,19 +5276,19 @@ do -- SET_PLAYER -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. -- @param #function IteratorFunction The function that will be called when there is an alive CLIENT in the SET_PLAYER. The function needs to accept a CLIENT parameter. -- @return #SET_PLAYER self - function SET_PLAYER:ForEachPlayerNotInZone( ZoneObject, IteratorFunction, ... ) - --self:F2( arg ) + function SET_PLAYER:ForEachPlayerNotInZone(ZoneObject, IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet(), + self:ForEach(IteratorFunction, arg, self:GetSet(), -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Client#CLIENT ClientObject - function( ZoneObject, ClientObject ) - if ClientObject:IsNotInZone( ZoneObject ) then + function(ZoneObject, ClientObject) + if ClientObject:IsNotInZone(ZoneObject) then return true else return false end - end, { ZoneObject } ) + end, { ZoneObject }) return self end @@ -5322,8 +5297,8 @@ do -- SET_PLAYER -- @param #SET_PLAYER self -- @param Wrapper.Client#CLIENT MClient -- @return #SET_PLAYER self - function SET_PLAYER:IsIncludeObject( MClient ) - --self:F2( MClient ) + function SET_PLAYER:IsIncludeObject(MClient) + --self:F2(MClient) local MClientInclude = true @@ -5332,84 +5307,84 @@ do -- SET_PLAYER if self.Filter.Coalitions and MClientInclude then local MClientCoalition = false - for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do - local ClientCoalitionID = _DATABASE:GetCoalitionFromClientTemplate( MClientName ) + for CoalitionID, CoalitionName in pairs(self.Filter.Coalitions) do + local ClientCoalitionID = _DATABASE:GetCoalitionFromClientTemplate(MClientName) if ClientCoalitionID==nil and MClient:IsAlive()~=nil then ClientCoalitionID=MClient:GetCoalition() end - --self:T(3( { "Coalition:", ClientCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) + --self:T(3({ "Coalition:", ClientCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName }) if self.FilterMeta.Coalitions[CoalitionName] and ClientCoalitionID and self.FilterMeta.Coalitions[CoalitionName] == ClientCoalitionID then MClientCoalition = true end end - --self:T(( { "Evaluated Coalition", MClientCoalition } ) + --self:T(({ "Evaluated Coalition", MClientCoalition }) MClientInclude = MClientInclude and MClientCoalition end if self.Filter.Categories and MClientInclude then local MClientCategory = false - for CategoryID, CategoryName in pairs( self.Filter.Categories ) do - local ClientCategoryID = _DATABASE:GetCategoryFromClientTemplate( MClientName ) + for CategoryID, CategoryName in pairs(self.Filter.Categories) do + local ClientCategoryID = _DATABASE:GetCategoryFromClientTemplate(MClientName) local UnitCategory = 0 if ClientCategoryID==nil and MClient:IsExist() then ClientCategoryID,UnitCategory=MClient:GetCategory() - --self:T(3( { "Category:", UnitCategory, self.FilterMeta.Categories[CategoryName], CategoryName } ) + --self:T(3({ "Category:", UnitCategory, self.FilterMeta.Categories[CategoryName], CategoryName }) if self.FilterMeta.Categories[CategoryName] and UnitCategory and self.FilterMeta.Categories[CategoryName] == UnitCategory then MClientCategory = true end else - --self:T(3( { "Category:", ClientCategoryID, self.FilterMeta.Categories[CategoryName], CategoryName } ) + --self:T(3({ "Category:", ClientCategoryID, self.FilterMeta.Categories[CategoryName], CategoryName }) if self.FilterMeta.Categories[CategoryName] and ClientCategoryID and self.FilterMeta.Categories[CategoryName] == ClientCategoryID then MClientCategory = true end end end - --self:T(( { "Evaluated Category", MClientCategory } ) + --self:T(({ "Evaluated Category", MClientCategory }) MClientInclude = MClientInclude and MClientCategory end if self.Filter.Types then local MClientType = false - for TypeID, TypeName in pairs( self.Filter.Types ) do - --self:T(3( { "Type:", MClient:GetTypeName(), TypeName } ) + for TypeID, TypeName in pairs(self.Filter.Types) do + --self:T(3({ "Type:", MClient:GetTypeName(), TypeName }) if TypeName == MClient:GetTypeName() then MClientType = true end end - --self:T(( { "Evaluated Type", MClientType } ) + --self:T(({ "Evaluated Type", MClientType }) MClientInclude = MClientInclude and MClientType end if self.Filter.Countries then local MClientCountry = false - for CountryID, CountryName in pairs( self.Filter.Countries ) do - local ClientCountryID = _DATABASE:GetCountryFromClientTemplate( MClientName ) - --self:T(3( { "Country:", ClientCountryID, country.id[CountryName], CountryName } ) + for CountryID, CountryName in pairs(self.Filter.Countries) do + local ClientCountryID = _DATABASE:GetCountryFromClientTemplate(MClientName) + --self:T(3({ "Country:", ClientCountryID, country.id[CountryName], CountryName }) if country.id[CountryName] and country.id[CountryName] == ClientCountryID then MClientCountry = true end end - --self:T(( { "Evaluated Country", MClientCountry } ) + --self:T(({ "Evaluated Country", MClientCountry }) MClientInclude = MClientInclude and MClientCountry end if self.Filter.ClientPrefixes then local MClientPrefix = false - for ClientPrefixId, ClientPrefix in pairs( self.Filter.ClientPrefixes ) do - --self:T(3( { "Prefix:", string.find( MClient.UnitName, ClientPrefix, 1 ), ClientPrefix } ) - if string.find( MClient.UnitName, ClientPrefix, 1 ) then + for ClientPrefixId, ClientPrefix in pairs(self.Filter.ClientPrefixes) do + --self:T(3({ "Prefix:", string.find(MClient.UnitName, ClientPrefix, 1), ClientPrefix }) + if string.find(MClient.UnitName, ClientPrefix, 1) then MClientPrefix = true end end - --self:T(( { "Evaluated Prefix", MClientPrefix } ) + --self:T(({ "Evaluated Prefix", MClientPrefix }) MClientInclude = MClientInclude and MClientPrefix end end if self.Filter.Zones then local MClientZone = false - for ZoneName, Zone in pairs( self.Filter.Zones ) do - --self:T(3( "Zone:", ZoneName ) + for ZoneName, Zone in pairs(self.Filter.Zones) do + --self:T(3("Zone:", ZoneName) local unit = MClient:GetClientGroupUnit() if unit and unit:IsInZone(Zone) then MClientZone = true @@ -5423,7 +5398,7 @@ do -- SET_PLAYER MClientInclude = MClientInclude and MClientFunc end - --self:T(2( MClientInclude ) + --self:T(2(MClientInclude) return MClientInclude end @@ -5500,7 +5475,7 @@ do -- SET_AIRBASE -- DatabaseSet = SET_AIRBASE:New() function SET_AIRBASE:New() -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.AIRBASES ) ) + local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.AIRBASES)) return self end @@ -5509,9 +5484,9 @@ do -- SET_AIRBASE -- @param Core.Set#SET_AIRBASE self -- @param Wrapper.Airbase#AIRBASE airbase Airbase that should be added to the set. -- @return self - function SET_AIRBASE:AddAirbase( airbase ) + function SET_AIRBASE:AddAirbase(airbase) - self:Add( airbase:GetName(), airbase ) + self:Add(airbase:GetName(), airbase) return self end @@ -5520,12 +5495,12 @@ do -- SET_AIRBASE -- @param Core.Set#SET_AIRBASE self -- @param #string AddAirbaseNames A single name or an array of AIRBASE names. -- @return self - function SET_AIRBASE:AddAirbasesByName( AddAirbaseNames ) + function SET_AIRBASE:AddAirbasesByName(AddAirbaseNames) - local AddAirbaseNamesArray = (type( AddAirbaseNames ) == "table") and AddAirbaseNames or { AddAirbaseNames } + local AddAirbaseNamesArray = (type(AddAirbaseNames) == "table") and AddAirbaseNames or { AddAirbaseNames } - for AddAirbaseID, AddAirbaseName in pairs( AddAirbaseNamesArray ) do - self:Add( AddAirbaseName, AIRBASE:FindByName( AddAirbaseName ) ) + for AddAirbaseID, AddAirbaseName in pairs(AddAirbaseNamesArray) do + self:Add(AddAirbaseName, AIRBASE:FindByName(AddAirbaseName)) end return self @@ -5535,12 +5510,12 @@ do -- SET_AIRBASE -- @param Core.Set#SET_AIRBASE self -- @param Wrapper.Airbase#AIRBASE RemoveAirbaseNames A single name or an array of AIRBASE names. -- @return self - function SET_AIRBASE:RemoveAirbasesByName( RemoveAirbaseNames ) + function SET_AIRBASE:RemoveAirbasesByName(RemoveAirbaseNames) - local RemoveAirbaseNamesArray = (type( RemoveAirbaseNames ) == "table") and RemoveAirbaseNames or { RemoveAirbaseNames } + local RemoveAirbaseNamesArray = (type(RemoveAirbaseNames) == "table") and RemoveAirbaseNames or { RemoveAirbaseNames } - for RemoveAirbaseID, RemoveAirbaseName in pairs( RemoveAirbaseNamesArray ) do - self:Remove( RemoveAirbaseName ) + for RemoveAirbaseID, RemoveAirbaseName in pairs(RemoveAirbaseNamesArray) do + self:Remove(RemoveAirbaseName) end return self @@ -5550,7 +5525,7 @@ do -- SET_AIRBASE -- @param #SET_AIRBASE self -- @param #string AirbaseName -- @return Wrapper.Airbase#AIRBASE The found Airbase. - function SET_AIRBASE:FindAirbase( AirbaseName ) + function SET_AIRBASE:FindAirbase(AirbaseName) local AirbaseFound = self.Set[AirbaseName] return AirbaseFound @@ -5561,16 +5536,16 @@ do -- SET_AIRBASE -- @param Core.Point#COORDINATE Coordinate -- @param #number Range -- @return Wrapper.Airbase#AIRBASE The found Airbase. - function SET_AIRBASE:FindAirbaseInRange( Coordinate, Range ) + function SET_AIRBASE:FindAirbaseInRange(Coordinate, Range) local AirbaseFound = nil - for AirbaseName, AirbaseObject in pairs( self.Set ) do + for AirbaseName, AirbaseObject in pairs(self.Set) do local AirbaseCoordinate = AirbaseObject:GetCoordinate() - local Distance = Coordinate:Get2DDistance( AirbaseCoordinate ) + local Distance = Coordinate:Get2DDistance(AirbaseCoordinate) - --self:F( { Distance = Distance } ) + --self:F({ Distance = Distance }) if Distance <= Range then AirbaseFound = AirbaseObject @@ -5588,42 +5563,30 @@ do -- SET_AIRBASE function SET_AIRBASE:GetRandomAirbase() local RandomAirbase = self:GetRandom() - --self:F( { RandomAirbase = RandomAirbase:GetName() } ) + --self:F({ RandomAirbase = RandomAirbase:GetName() }) return RandomAirbase end - + --- Builds a set of airbases of coalitions. -- Possible current coalitions are red, blue and neutral. -- @param #SET_AIRBASE self -- @param #string Coalitions Can take the following values: "red", "blue", "neutral". -- @return #SET_AIRBASE self - function SET_AIRBASE:FilterCoalitions( Coalitions ) - if not self.Filter.Coalitions then - self.Filter.Coalitions = {} - end - if type( Coalitions ) ~= "table" then - Coalitions = { Coalitions } - end - for CoalitionID, Coalition in pairs( Coalitions ) do - self.Filter.Coalitions[Coalition] = Coalition - end - return self - end --- Builds a set of airbases out of categories. -- Possible current categories are plane, helicopter, ground, ship. -- @param #SET_AIRBASE self -- @param #string Categories Can take the following values: "airdrome", "helipad", "ship". -- @return #SET_AIRBASE self - function SET_AIRBASE:FilterCategories( Categories ) + function SET_AIRBASE:FilterCategories(Categories) if not self.Filter.Categories then self.Filter.Categories = {} end - if type( Categories ) ~= "table" then + if type(Categories) ~= "table" then Categories = { Categories } end - for CategoryID, Category in pairs( Categories ) do + for CategoryID, Category in pairs(Categories) do self.Filter.Categories[Category] = Category end return self @@ -5633,20 +5596,20 @@ do -- SET_AIRBASE -- @param #SET_AIRBASE self -- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE -- @return #SET_AIRBASE self - function SET_AIRBASE:FilterZones( Zones ) + function SET_AIRBASE:FilterZones(Zones) if not self.Filter.Zones then self.Filter.Zones = {} end local zones = {} if Zones.ClassName and Zones.ClassName == "SET_ZONE" then zones = Zones.Set - elseif type( Zones ) ~= "table" or (type( Zones ) == "table" and Zones.ClassName ) then + elseif type(Zones) ~= "table" or (type(Zones) == "table" and Zones.ClassName) then self:E("***** FilterZones needs either a table of ZONE Objects or a SET_ZONE as parameter!") return self else zones = Zones end - for _,Zone in pairs( zones ) do + for _,Zone in pairs(zones) do local zonename = Zone:GetName() --self:T((zonename) self.Filter.Zones[zonename] = Zone @@ -5662,15 +5625,15 @@ do -- SET_AIRBASE if _DATABASE then -- We use the BaseCaptured event, which is generated by DCS when a base got captured. - self:HandleEvent( EVENTS.BaseCaptured ) - self:HandleEvent( EVENTS.Dead ) + self:HandleEvent(EVENTS.BaseCaptured) + self:HandleEvent(EVENTS.Dead) -- We initialize the first set. - for ObjectName, Object in pairs( self.Database ) do - if self:IsIncludeObject( Object ) then - self:Add( ObjectName, Object ) + for ObjectName, Object in pairs(self.Database) do + if self:IsIncludeObject(Object) then + self:Add(ObjectName, Object) else - self:RemoveAirbasesByName( ObjectName ) + self:RemoveAirbasesByName(ObjectName) end end end @@ -5681,16 +5644,16 @@ do -- SET_AIRBASE --- Base capturing event. -- @param #SET_AIRBASE self -- @param Core.Event#EVENT EventData - function SET_AIRBASE:OnEventBaseCaptured( EventData ) + function SET_AIRBASE:OnEventBaseCaptured(EventData) -- When a base got captured, we reevaluate the set. - for ObjectName, Object in pairs( self.Database ) do - if self:IsIncludeObject( Object ) then + for ObjectName, Object in pairs(self.Database) do + if self:IsIncludeObject(Object) then -- We add captured bases on yet in the set. - self:Add( ObjectName, Object ) + self:Add(ObjectName, Object) else -- We remove captured bases that are not anymore part of the set. - self:RemoveAirbasesByName( ObjectName ) + self:RemoveAirbasesByName(ObjectName) end end @@ -5699,12 +5662,12 @@ do -- SET_AIRBASE --- Dead event. -- @param #SET_AIRBASE self -- @param Core.Event#EVENT EventData - function SET_AIRBASE:OnEventDead( EventData ) + function SET_AIRBASE:OnEventDead(EventData) - local airbaseName, airbase = self:FindInDatabase( EventData ) + local airbaseName, airbase = self:FindInDatabase(EventData) if airbase and (airbase:IsShip() or airbase:IsHelipad()) then - self:RemoveAirbasesByName( airbaseName ) + self:RemoveAirbasesByName(airbaseName) end end @@ -5715,7 +5678,7 @@ do -- SET_AIRBASE -- @param Core.Event#EVENTDATA Event Event data. -- @return #string The name of the AIRBASE. -- @return Wrapper.Airbase#AIRBASE The AIRBASE object. - function SET_AIRBASE:AddInDatabase( Event ) + function SET_AIRBASE:AddInDatabase(Event) return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end @@ -5725,8 +5688,8 @@ do -- SET_AIRBASE -- @param Core.Event#EVENTDATA Event Event data. -- @return #string The name of the AIRBASE. -- @return Wrapper.Airbase#AIRBASE The AIRBASE object. - function SET_AIRBASE:FindInDatabase( Event ) - --self:F3( { Event } ) + function SET_AIRBASE:FindInDatabase(Event) + --self:F3({ Event }) return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end @@ -5735,10 +5698,10 @@ do -- SET_AIRBASE -- @param #SET_AIRBASE self -- @param #function IteratorFunction The function that will be called when there is an alive AIRBASE in the SET_AIRBASE. The function needs to accept a AIRBASE parameter. -- @return #SET_AIRBASE self - function SET_AIRBASE:ForEachAirbase( IteratorFunction, ... ) - --self:F2( arg ) + function SET_AIRBASE:ForEachAirbase(IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet() ) + self:ForEach(IteratorFunction, arg, self:GetSet()) return self end @@ -5747,10 +5710,10 @@ do -- SET_AIRBASE -- @param #SET_AIRBASE self -- @param Core.Point#COORDINATE Coordinate A @{Core.Point#COORDINATE} object from where to evaluate the closest @{Wrapper.Airbase#AIRBASE}. -- @return Wrapper.Airbase#AIRBASE The closest @{Wrapper.Airbase#AIRBASE}. - function SET_AIRBASE:FindNearestAirbaseFromPointVec2( Coordinate ) - --self:F2( Coordinate ) + function SET_AIRBASE:FindNearestAirbaseFromPointVec2(Coordinate) + --self:F2(Coordinate) - local NearestAirbase = self:FindNearestObjectFromPointVec2( Coordinate ) + local NearestAirbase = self:FindNearestObjectFromPointVec2(Coordinate) return NearestAirbase end @@ -5758,8 +5721,8 @@ do -- SET_AIRBASE -- @param #SET_AIRBASE self -- @param Wrapper.Airbase#AIRBASE MAirbase -- @return #SET_AIRBASE self - function SET_AIRBASE:IsIncludeObject( MAirbase ) - --self:F2( MAirbase ) + function SET_AIRBASE:IsIncludeObject(MAirbase) + --self:F2(MAirbase) local MAirbaseInclude = true @@ -5768,39 +5731,39 @@ do -- SET_AIRBASE if self.Filter.Coalitions then local MAirbaseCoalition = false - for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do - local AirbaseCoalitionID = _DATABASE:GetCoalitionFromAirbase( MAirbaseName ) - --self:T(3( { "Coalition:", AirbaseCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) + for CoalitionID, CoalitionName in pairs(self.Filter.Coalitions) do + local AirbaseCoalitionID = _DATABASE:GetCoalitionFromAirbase(MAirbaseName) + --self:T(3({ "Coalition:", AirbaseCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName }) if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == AirbaseCoalitionID then MAirbaseCoalition = true end end - --self:T(( { "Evaluated Coalition", MAirbaseCoalition } ) + --self:T(({ "Evaluated Coalition", MAirbaseCoalition }) MAirbaseInclude = MAirbaseInclude and MAirbaseCoalition end if self.Filter.Categories and MAirbaseInclude then local MAirbaseCategory = false - for CategoryID, CategoryName in pairs( self.Filter.Categories ) do - local AirbaseCategoryID = _DATABASE:GetCategoryFromAirbase( MAirbaseName ) - --self:T(3( { "Category:", AirbaseCategoryID, self.FilterMeta.Categories[CategoryName], CategoryName } ) + for CategoryID, CategoryName in pairs(self.Filter.Categories) do + local AirbaseCategoryID = _DATABASE:GetCategoryFromAirbase(MAirbaseName) + --self:T(3({ "Category:", AirbaseCategoryID, self.FilterMeta.Categories[CategoryName], CategoryName }) if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == AirbaseCategoryID then MAirbaseCategory = true end end - --self:T(( { "Evaluated Category", MAirbaseCategory } ) + --self:T(({ "Evaluated Category", MAirbaseCategory }) MAirbaseInclude = MAirbaseInclude and MAirbaseCategory end if self.Filter.Zones and MAirbaseInclude then local MAirbaseZone = false - for ZoneName, Zone in pairs( self.Filter.Zones ) do - --self:T(( "Zone:", ZoneName ) + for ZoneName, Zone in pairs(self.Filter.Zones) do + --self:T(("Zone:", ZoneName) local coord = MAirbase:GetCoordinate() if coord and Zone:IsCoordinateInZone(coord) then MAirbaseZone = true end - --self:T(( { "Evaluated Zone", MSceneryZone } ) + --self:T(({ "Evaluated Zone", MSceneryZone }) end MAirbaseInclude = MAirbaseInclude and MAirbaseZone end @@ -5812,7 +5775,7 @@ do -- SET_AIRBASE MAirbaseInclude = MAirbaseInclude and MClientFunc end - --self:T(2( MAirbaseInclude ) + --self:T(2(MAirbaseInclude) return MAirbaseInclude end @@ -5891,111 +5854,99 @@ do -- SET_CARGO -- DatabaseSet = SET_CARGO:New() function SET_CARGO:New() -- R2.1 -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.CARGOS ) ) -- #SET_CARGO + local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.CARGOS)) -- #SET_CARGO return self end - --- (R2.1) Add CARGO to SET_CARGO. + --- Add CARGO to SET_CARGO. -- @param Core.Set#SET_CARGO self -- @param Cargo.Cargo#CARGO Cargo A single cargo. -- @return Core.Set#SET_CARGO self - function SET_CARGO:AddCargo( Cargo ) -- R2.4 + function SET_CARGO:AddCargo(Cargo) -- R2.4 - self:Add( Cargo:GetName(), Cargo ) + self:Add(Cargo:GetName(), Cargo) return self end - --- (R2.1) Add CARGOs to SET_CARGO. + --- Add CARGOs to SET_CARGO. -- @param Core.Set#SET_CARGO self -- @param #string AddCargoNames A single name or an array of CARGO names. -- @return Core.Set#SET_CARGO self - function SET_CARGO:AddCargosByName( AddCargoNames ) -- R2.1 + function SET_CARGO:AddCargosByName(AddCargoNames) -- R2.1 - local AddCargoNamesArray = (type( AddCargoNames ) == "table") and AddCargoNames or { AddCargoNames } + local AddCargoNamesArray = (type(AddCargoNames) == "table") and AddCargoNames or { AddCargoNames } - for AddCargoID, AddCargoName in pairs( AddCargoNamesArray ) do - self:Add( AddCargoName, CARGO:FindByName( AddCargoName ) ) + for AddCargoID, AddCargoName in pairs(AddCargoNamesArray) do + self:Add(AddCargoName, CARGO:FindByName(AddCargoName)) end return self end - --- (R2.1) Remove CARGOs from SET_CARGO. + --- Remove CARGOs from SET_CARGO. -- @param Core.Set#SET_CARGO self -- @param Cargo.Cargo#CARGO RemoveCargoNames A single name or an array of CARGO names. -- @return Core.Set#SET_CARGO self - function SET_CARGO:RemoveCargosByName( RemoveCargoNames ) -- R2.1 + function SET_CARGO:RemoveCargosByName(RemoveCargoNames) -- R2.1 - local RemoveCargoNamesArray = (type( RemoveCargoNames ) == "table") and RemoveCargoNames or { RemoveCargoNames } + local RemoveCargoNamesArray = (type(RemoveCargoNames) == "table") and RemoveCargoNames or { RemoveCargoNames } - for RemoveCargoID, RemoveCargoName in pairs( RemoveCargoNamesArray ) do - self:Remove( RemoveCargoName.CargoName ) + for RemoveCargoID, RemoveCargoName in pairs(RemoveCargoNamesArray) do + self:Remove(RemoveCargoName.CargoName) end return self end - --- (R2.1) Finds a Cargo based on the Cargo Name. + --- Finds a Cargo based on the Cargo Name. -- @param #SET_CARGO self -- @param #string CargoName -- @return Cargo.Cargo#CARGO The found Cargo. - function SET_CARGO:FindCargo( CargoName ) -- R2.1 + function SET_CARGO:FindCargo(CargoName) -- R2.1 local CargoFound = self.Set[CargoName] return CargoFound end - - --- (R2.1) Builds a set of cargos of coalitions. + + --- Builds a set of cargos of coalitions. -- Possible current coalitions are red, blue and neutral. -- @param #SET_CARGO self -- @param #string Coalitions Can take the following values: "red", "blue", "neutral". -- @return #SET_CARGO self - function SET_CARGO:FilterCoalitions( Coalitions ) -- R2.1 - if not self.Filter.Coalitions then - self.Filter.Coalitions = {} - end - if type( Coalitions ) ~= "table" then - Coalitions = { Coalitions } - end - for CoalitionID, Coalition in pairs( Coalitions ) do - self.Filter.Coalitions[Coalition] = Coalition - end - return self - end - - --- (R2.1) Builds a set of cargos of defined cargo types. + + --- Builds a set of cargos of defined cargo types. -- Possible current types are those types known within DCS world. -- @param #SET_CARGO self -- @param #string Types Can take those type strings known within DCS world. -- @return #SET_CARGO self - function SET_CARGO:FilterTypes( Types ) -- R2.1 + function SET_CARGO:FilterTypes(Types) -- R2.1 if not self.Filter.Types then self.Filter.Types = {} end - if type( Types ) ~= "table" then + if type(Types) ~= "table" then Types = { Types } end - for TypeID, Type in pairs( Types ) do + for TypeID, Type in pairs(Types) do self.Filter.Types[Type] = Type end return self end - --- (R2.1) Builds a set of cargos of defined countries. + --- Builds a set of cargos of defined countries. -- Possible current countries are those known within DCS world. -- @param #SET_CARGO self -- @param #string Countries Can take those country strings known within DCS world. -- @return #SET_CARGO self - function SET_CARGO:FilterCountries( Countries ) -- R2.1 + function SET_CARGO:FilterCountries(Countries) -- R2.1 if not self.Filter.Countries then self.Filter.Countries = {} end - if type( Countries ) ~= "table" then + if type(Countries) ~= "table" then Countries = { Countries } end - for CountryID, Country in pairs( Countries ) do + for CountryID, Country in pairs(Countries) do self.Filter.Countries[Country] = Country end return self @@ -6006,28 +5957,28 @@ do -- SET_CARGO -- @param #SET_CARGO self -- @param #string Prefixes The string pattern(s) that need to be in the cargo name. Can also be passed as a `#table` of strings. -- @return #SET_CARGO self - function SET_CARGO:FilterPrefixes( Prefixes ) -- R2.1 + function SET_CARGO:FilterPrefixes(Prefixes) -- R2.1 if not self.Filter.CargoPrefixes then self.Filter.CargoPrefixes = {} end - if type( Prefixes ) ~= "table" then + if type(Prefixes) ~= "table" then Prefixes = { Prefixes } end - for PrefixID, Prefix in pairs( Prefixes ) do + for PrefixID, Prefix in pairs(Prefixes) do self.Filter.CargoPrefixes[Prefix] = Prefix end return self end - --- (R2.1) Starts the filtering. + --- Starts the filtering. -- @param #SET_CARGO self -- @return #SET_CARGO self function SET_CARGO:FilterStart() -- R2.1 if _DATABASE then self:_FilterStart() - self:HandleEvent( EVENTS.NewCargo ) - self:HandleEvent( EVENTS.DeleteCargo ) + self:HandleEvent(EVENTS.NewCargo) + self:HandleEvent(EVENTS.DeleteCargo) end return self @@ -6038,67 +5989,67 @@ do -- SET_CARGO -- @return #SET_CARGO self function SET_CARGO:FilterStop() - self:UnHandleEvent( EVENTS.NewCargo ) - self:UnHandleEvent( EVENTS.DeleteCargo ) + self:UnHandleEvent(EVENTS.NewCargo) + self:UnHandleEvent(EVENTS.DeleteCargo) return self end - --- (R2.1) Handles the Database to check on an event (birth) that the Object was added in the Database. + --- Handles the Database to check on an event (birth) that the Object was added in the Database. -- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event! -- @param #SET_CARGO self -- @param Core.Event#EVENTDATA Event -- @return #string The name of the CARGO -- @return #table The CARGO - function SET_CARGO:AddInDatabase( Event ) -- R2.1 - --self:F3( { Event } ) + function SET_CARGO:AddInDatabase(Event) -- R2.1 + --self:F3({ Event }) return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end - --- (R2.1) Handles the Database to check on any event that Object exists in the Database. + --- Handles the Database to check on any event that Object exists in the Database. -- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa! -- @param #SET_CARGO self -- @param Core.Event#EVENTDATA Event -- @return #string The name of the CARGO -- @return #table The CARGO - function SET_CARGO:FindInDatabase( Event ) -- R2.1 - --self:F3( { Event } ) + function SET_CARGO:FindInDatabase(Event) -- R2.1 + --self:F3({ Event }) return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end - --- (R2.1) Iterate the SET_CARGO and call an iterator function for each CARGO, providing the CARGO and optional parameters. + --- Iterate the SET_CARGO and call an iterator function for each CARGO, providing the CARGO and optional parameters. -- @param #SET_CARGO self -- @param #function IteratorFunction The function that will be called when there is an alive CARGO in the SET_CARGO. The function needs to accept a CARGO parameter. -- @return #SET_CARGO self - function SET_CARGO:ForEachCargo( IteratorFunction, ... ) -- R2.1 - --self:F2( arg ) + function SET_CARGO:ForEachCargo(IteratorFunction, ...) -- R2.1 + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet() ) + self:ForEach(IteratorFunction, arg, self:GetSet()) return self end - --- (R2.1) Iterate the SET_CARGO while identifying the nearest @{Cargo.Cargo#CARGO} from a @{Core.Point#COORDINATE}. + --- Iterate the SET_CARGO while identifying the nearest @{Cargo.Cargo#CARGO} from a @{Core.Point#COORDINATE}. -- @param #SET_CARGO self -- @param Core.Point#COORDINATE Coordinate A @{Core.Point#COORDINATE} object from where to evaluate the closest @{Cargo.Cargo#CARGO}. -- @return Cargo.Cargo#CARGO The closest @{Cargo.Cargo#CARGO}. - function SET_CARGO:FindNearestCargoFromPointVec2( Coordinate ) -- R2.1 - --self:F2( Coordinate ) + function SET_CARGO:FindNearestCargoFromPointVec2(Coordinate) -- R2.1 + --self:F2(Coordinate) - local NearestCargo = self:FindNearestObjectFromPointVec2( Coordinate ) + local NearestCargo = self:FindNearestObjectFromPointVec2(Coordinate) return NearestCargo end --- -- @param #SET_CARGO self - function SET_CARGO:FirstCargoWithState( State ) + function SET_CARGO:FirstCargoWithState(State) local FirstCargo = nil - for CargoName, Cargo in pairs( self.Set ) do - if Cargo:Is( State ) then + for CargoName, Cargo in pairs(self.Set) do + if Cargo:Is(State) then FirstCargo = Cargo break end @@ -6109,12 +6060,12 @@ do -- SET_CARGO --- -- @param #SET_CARGO self - function SET_CARGO:FirstCargoWithStateAndNotDeployed( State ) + function SET_CARGO:FirstCargoWithStateAndNotDeployed(State) local FirstCargo = nil - for CargoName, Cargo in pairs( self.Set ) do - if Cargo:Is( State ) and not Cargo:IsDeployed() then + for CargoName, Cargo in pairs(self.Set) do + if Cargo:Is(State) and not Cargo:IsDeployed() then FirstCargo = Cargo break end @@ -6127,7 +6078,7 @@ do -- SET_CARGO -- @param #SET_CARGO self -- @return Cargo.Cargo#CARGO The first @{Cargo.Cargo#CARGO}. function SET_CARGO:FirstCargoUnLoaded() - local FirstCargo = self:FirstCargoWithState( "UnLoaded" ) + local FirstCargo = self:FirstCargoWithState("UnLoaded") return FirstCargo end @@ -6135,7 +6086,7 @@ do -- SET_CARGO -- @param #SET_CARGO self -- @return Cargo.Cargo#CARGO The first @{Cargo.Cargo#CARGO}. function SET_CARGO:FirstCargoUnLoadedAndNotDeployed() - local FirstCargo = self:FirstCargoWithStateAndNotDeployed( "UnLoaded" ) + local FirstCargo = self:FirstCargoWithStateAndNotDeployed("UnLoaded") return FirstCargo end @@ -6143,7 +6094,7 @@ do -- SET_CARGO -- @param #SET_CARGO self -- @return Cargo.Cargo#CARGO The first @{Cargo.Cargo#CARGO}. function SET_CARGO:FirstCargoLoaded() - local FirstCargo = self:FirstCargoWithState( "Loaded" ) + local FirstCargo = self:FirstCargoWithState("Loaded") return FirstCargo end @@ -6151,16 +6102,16 @@ do -- SET_CARGO -- @param #SET_CARGO self -- @return Cargo.Cargo#CARGO The first @{Cargo.Cargo#CARGO}. function SET_CARGO:FirstCargoDeployed() - local FirstCargo = self:FirstCargoWithState( "Deployed" ) + local FirstCargo = self:FirstCargoWithState("Deployed") return FirstCargo end - --- (R2.1) + --- -- @param #SET_CARGO self -- @param AI.AI_Cargo#AI_CARGO MCargo -- @return #SET_CARGO self - function SET_CARGO:IsIncludeObject( MCargo ) -- R2.1 - --self:F2( MCargo ) + function SET_CARGO:IsIncludeObject(MCargo) -- R2.1 + --self:F2(MCargo) local MCargoInclude = true @@ -6169,38 +6120,38 @@ do -- SET_CARGO if self.Filter.Coalitions then local MCargoCoalition = false - for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do + for CoalitionID, CoalitionName in pairs(self.Filter.Coalitions) do local CargoCoalitionID = MCargo:GetCoalition() - --self:T(3( { "Coalition:", CargoCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) + --self:T(3({ "Coalition:", CargoCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName }) if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == CargoCoalitionID then MCargoCoalition = true end end - --self:F( { "Evaluated Coalition", MCargoCoalition } ) + --self:F({ "Evaluated Coalition", MCargoCoalition }) MCargoInclude = MCargoInclude and MCargoCoalition end if self.Filter.Types then local MCargoType = false - for TypeID, TypeName in pairs( self.Filter.Types ) do - --self:T(3( { "Type:", MCargo:GetType(), TypeName } ) + for TypeID, TypeName in pairs(self.Filter.Types) do + --self:T(3({ "Type:", MCargo:GetType(), TypeName }) if TypeName == MCargo:GetType() then MCargoType = true end end - --self:F( { "Evaluated Type", MCargoType } ) + --self:F({ "Evaluated Type", MCargoType }) MCargoInclude = MCargoInclude and MCargoType end if self.Filter.CargoPrefixes then local MCargoPrefix = false - for CargoPrefixId, CargoPrefix in pairs( self.Filter.CargoPrefixes ) do - --self:T(3( { "Prefix:", string.find( MCargo.Name, CargoPrefix, 1 ), CargoPrefix } ) - if string.find( MCargo.Name, CargoPrefix, 1 ) then + for CargoPrefixId, CargoPrefix in pairs(self.Filter.CargoPrefixes) do + --self:T(3({ "Prefix:", string.find(MCargo.Name, CargoPrefix, 1), CargoPrefix }) + if string.find(MCargo.Name, CargoPrefix, 1) then MCargoPrefix = true end end - --self:F( { "Evaluated Prefix", MCargoPrefix } ) + --self:F({ "Evaluated Prefix", MCargoPrefix }) MCargoInclude = MCargoInclude and MCargoPrefix end end @@ -6210,32 +6161,32 @@ do -- SET_CARGO MCargoInclude = MCargoInclude and MClientFunc end - --self:T(2( MCargoInclude ) + --self:T(2(MCargoInclude) return MCargoInclude end - --- (R2.1) Handles the OnEventNewCargo event for the Set. + --- Handles the OnEventNewCargo event for the Set. -- @param #SET_CARGO self -- @param Core.Event#EVENTDATA EventData - function SET_CARGO:OnEventNewCargo( EventData ) -- R2.1 + function SET_CARGO:OnEventNewCargo(EventData) -- R2.1 - --self:F( { "New Cargo", EventData } ) + --self:F({ "New Cargo", EventData }) if EventData.Cargo then - if EventData.Cargo and self:IsIncludeObject( EventData.Cargo ) then - self:Add( EventData.Cargo.Name, EventData.Cargo ) + if EventData.Cargo and self:IsIncludeObject(EventData.Cargo) then + self:Add(EventData.Cargo.Name, EventData.Cargo) end end end - --- (R2.1) Handles the OnDead or OnCrash event for alive units set. + --- Handles the OnDead or OnCrash event for alive units set. -- @param #SET_CARGO self -- @param Core.Event#EVENTDATA EventData - function SET_CARGO:OnEventDeleteCargo( EventData ) -- R2.1 - --self:F3( { EventData } ) + function SET_CARGO:OnEventDeleteCargo(EventData) -- R2.1 + --self:F3({ EventData }) if EventData.Cargo then - local Cargo = _DATABASE:FindCargo( EventData.Cargo.Name ) + local Cargo = _DATABASE:FindCargo(EventData.Cargo.Name) if Cargo and Cargo.Name then -- When cargo was deleted, it may probably be because of an S_EVENT_DEAD. @@ -6244,10 +6195,10 @@ do -- SET_CARGO -- To prevent this from happening, the Cargo object has a flag NoDestroy. -- When true, the SET_CARGO won't Remove the Cargo object from the set. -- This flag is switched off after the event handlers have been called in the EVENT class. - --self:F( { CargoNoDestroy = Cargo.NoDestroy } ) + --self:F({ CargoNoDestroy = Cargo.NoDestroy }) if Cargo.NoDestroy then else - self:Remove( Cargo.Name ) + self:Remove(Cargo.Name) end end end @@ -6315,7 +6266,7 @@ do -- SET_ZONE -- DatabaseSet = SET_ZONE:New() function SET_ZONE:New() -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.ZONES ) ) + local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.ZONES)) return self end @@ -6324,12 +6275,12 @@ do -- SET_ZONE -- @param Core.Set#SET_ZONE self -- @param #string AddZoneNames A single name or an array of ZONE_BASE names. -- @return self - function SET_ZONE:AddZonesByName( AddZoneNames ) + function SET_ZONE:AddZonesByName(AddZoneNames) - local AddZoneNamesArray = (type( AddZoneNames ) == "table") and AddZoneNames or { AddZoneNames } + local AddZoneNamesArray = (type(AddZoneNames) == "table") and AddZoneNames or { AddZoneNames } - for AddAirbaseID, AddZoneName in pairs( AddZoneNamesArray ) do - self:Add( AddZoneName, ZONE:FindByName( AddZoneName ) ) + for AddAirbaseID, AddZoneName in pairs(AddZoneNamesArray) do + self:Add(AddZoneName, ZONE:FindByName(AddZoneName)) end return self @@ -6339,9 +6290,9 @@ do -- SET_ZONE -- @param Core.Set#SET_ZONE self -- @param Core.Zone#ZONE_BASE Zone A ZONE_BASE object. -- @return self - function SET_ZONE:AddZone( Zone ) + function SET_ZONE:AddZone(Zone) - self:Add( Zone:GetName(), Zone ) + self:Add(Zone:GetName(), Zone) return self end @@ -6350,12 +6301,12 @@ do -- SET_ZONE -- @param Core.Set#SET_ZONE self -- @param Core.Zone#ZONE_BASE RemoveZoneNames A single name or an array of ZONE_BASE names. -- @return self - function SET_ZONE:RemoveZonesByName( RemoveZoneNames ) + function SET_ZONE:RemoveZonesByName(RemoveZoneNames) - local RemoveZoneNamesArray = (type( RemoveZoneNames ) == "table") and RemoveZoneNames or { RemoveZoneNames } + local RemoveZoneNamesArray = (type(RemoveZoneNames) == "table") and RemoveZoneNames or { RemoveZoneNames } - for RemoveZoneID, RemoveZoneName in pairs( RemoveZoneNamesArray ) do - self:Remove( RemoveZoneName ) + for RemoveZoneID, RemoveZoneName in pairs(RemoveZoneNamesArray) do + self:Remove(RemoveZoneName) end return self @@ -6365,7 +6316,7 @@ do -- SET_ZONE -- @param #SET_ZONE self -- @param #string ZoneName -- @return Core.Zone#ZONE_BASE The found Zone. - function SET_ZONE:FindZone( ZoneName ) + function SET_ZONE:FindZone(ZoneName) local ZoneFound = self.Set[ZoneName] return ZoneFound @@ -6376,7 +6327,7 @@ do -- SET_ZONE -- @param #number margin Number of tries to find a zone -- @return Core.Zone#ZONE_BASE The random Zone. -- @return #nil if no zone in the collection. - function SET_ZONE:GetRandomZone( margin ) + function SET_ZONE:GetRandomZone(margin) local margin = margin or 100 if self:Count() ~= 0 then @@ -6389,7 +6340,7 @@ do -- SET_ZONE -- If the zone is not selected, then nil is returned by :GetZoneMaybe() and the loop continues! local counter = 0 while (not ZoneFound) or (counter < margin) do - local ZoneRandom = math.random( 1, #Index ) + local ZoneRandom = math.random(1, #Index) ZoneFound = self.Set[Index[ZoneRandom]]:GetZoneMaybe() counter = counter + 1 end @@ -6403,9 +6354,9 @@ do -- SET_ZONE --- Set a zone probability. -- @param #SET_ZONE self -- @param #string ZoneName The name of the zone. - function SET_ZONE:SetZoneProbability( ZoneName, ZoneProbability ) - local Zone = self:FindZone( ZoneName ) - Zone:SetZoneProbability( ZoneProbability ) + function SET_ZONE:SetZoneProbability(ZoneName, ZoneProbability) + local Zone = self:FindZone(ZoneName) + Zone:SetZoneProbability(ZoneProbability) end --- Builds a set of ZONEs that contain the given string in their name. @@ -6413,14 +6364,14 @@ do -- SET_ZONE -- @param #SET_ZONE self -- @param #string Prefixes The string pattern(s) that need to be contained in the zone name. Can also be passed as a `#table` of strings. -- @return #SET_ZONE self - function SET_ZONE:FilterPrefixes( Prefixes ) + function SET_ZONE:FilterPrefixes(Prefixes) if not self.Filter.Prefixes then self.Filter.Prefixes = {} end - if type( Prefixes ) ~= "table" then + if type(Prefixes) ~= "table" then Prefixes = { Prefixes } end - for PrefixID, Prefix in pairs( Prefixes ) do + for PrefixID, Prefix in pairs(Prefixes) do self.Filter.Prefixes[Prefix] = Prefix end return self @@ -6434,17 +6385,17 @@ do -- SET_ZONE if _DATABASE then -- We initialize the first set. - for ObjectName, Object in pairs( self.Database ) do - if self:IsIncludeObject( Object ) then - self:Add( ObjectName, Object ) + for ObjectName, Object in pairs(self.Database) do + if self:IsIncludeObject(Object) then + self:Add(ObjectName, Object) else - self:RemoveZonesByName( ObjectName ) + self:RemoveZonesByName(ObjectName) end end end - self:HandleEvent( EVENTS.NewZone ) - self:HandleEvent( EVENTS.DeleteZone ) + self:HandleEvent(EVENTS.NewZone) + self:HandleEvent(EVENTS.DeleteZone) return self end @@ -6454,8 +6405,8 @@ do -- SET_ZONE -- @return #SET_ZONE self function SET_ZONE:FilterStop() - self:UnHandleEvent( EVENTS.NewZone ) - self:UnHandleEvent( EVENTS.DeleteZone ) + self:UnHandleEvent(EVENTS.NewZone) + self:UnHandleEvent(EVENTS.DeleteZone) return self end @@ -6466,8 +6417,8 @@ do -- SET_ZONE -- @param Core.Event#EVENTDATA Event -- @return #string The name of the AIRBASE -- @return #table The AIRBASE - function SET_ZONE:AddInDatabase( Event ) - --self:F3( { Event } ) + function SET_ZONE:AddInDatabase(Event) + --self:F3({ Event }) return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end @@ -6478,8 +6429,8 @@ do -- SET_ZONE -- @param Core.Event#EVENTDATA Event -- @return #string The name of the AIRBASE -- @return #table The AIRBASE - function SET_ZONE:FindInDatabase( Event ) - --self:F3( { Event } ) + function SET_ZONE:FindInDatabase(Event) + --self:F3({ Event }) return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end @@ -6488,10 +6439,10 @@ do -- SET_ZONE -- @param #SET_ZONE self -- @param #function IteratorFunction The function that will be called when there is an alive ZONE in the SET_ZONE. The function needs to accept a AIRBASE parameter. -- @return #SET_ZONE self - function SET_ZONE:ForEachZone( IteratorFunction, ... ) - --self:F2( arg ) + function SET_ZONE:ForEachZone(IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet() ) + self:ForEach(IteratorFunction, arg, self:GetSet()) return self end @@ -6543,8 +6494,8 @@ do -- SET_ZONE -- @param #SET_ZONE self -- @param Core.Zone#ZONE_BASE MZone -- @return #SET_ZONE self - function SET_ZONE:IsIncludeObject( MZone ) - --self:F2( MZone ) + function SET_ZONE:IsIncludeObject(MZone) + --self:F2(MZone) local MZoneInclude = true @@ -6553,13 +6504,13 @@ do -- SET_ZONE if self.Filter.Prefixes then local MZonePrefix = false - for ZonePrefixId, ZonePrefix in pairs( self.Filter.Prefixes ) do - --self:T(2( { "Prefix:", string.find( MZoneName, ZonePrefix, 1 ), ZonePrefix } ) - if string.find( MZoneName, ZonePrefix, 1 ) then + for ZonePrefixId, ZonePrefix in pairs(self.Filter.Prefixes) do + --self:T(2({ "Prefix:", string.find(MZoneName, ZonePrefix, 1), ZonePrefix }) + if string.find(MZoneName, ZonePrefix, 1) then MZonePrefix = true end end - --self:T(( { "Evaluated Prefix", MZonePrefix } ) + --self:T(({ "Evaluated Prefix", MZonePrefix }) MZoneInclude = MZoneInclude and MZonePrefix end end @@ -6569,20 +6520,20 @@ do -- SET_ZONE MZoneInclude = MZoneInclude and MClientFunc end - --self:T(2( MZoneInclude ) + --self:T(2(MZoneInclude) return MZoneInclude end --- Handles the OnEventNewZone event for the Set. -- @param #SET_ZONE self -- @param Core.Event#EVENTDATA EventData - function SET_ZONE:OnEventNewZone( EventData ) -- R2.1 + function SET_ZONE:OnEventNewZone(EventData) -- R2.1 - --self:F( { "New Zone", EventData } ) + --self:F({ "New Zone", EventData }) if EventData.Zone then - if EventData.Zone and self:IsIncludeObject( EventData.Zone ) then - self:Add( EventData.Zone.ZoneName, EventData.Zone ) + if EventData.Zone and self:IsIncludeObject(EventData.Zone) then + self:Add(EventData.Zone.ZoneName, EventData.Zone) end end end @@ -6590,11 +6541,11 @@ do -- SET_ZONE --- Handles the OnDead or OnCrash event for alive units set. -- @param #SET_ZONE self -- @param Core.Event#EVENTDATA EventData - function SET_ZONE:OnEventDeleteZone( EventData ) -- R2.1 - --self:F3( { EventData } ) + function SET_ZONE:OnEventDeleteZone(EventData) -- R2.1 + --self:F3({ EventData }) if EventData.Zone then - local Zone = _DATABASE:FindZone( EventData.Zone.ZoneName ) + local Zone = _DATABASE:FindZone(EventData.Zone.ZoneName) if Zone and Zone.ZoneName then -- When cargo was deleted, it may probably be because of an S_EVENT_DEAD. @@ -6603,10 +6554,10 @@ do -- SET_ZONE -- To prevent this from happening, the Zone object has a flag NoDestroy. -- When true, the SET_ZONE won't Remove the Zone object from the set. -- This flag is switched off after the event handlers have been called in the EVENT class. - --self:F( { ZoneNoDestroy = Zone.NoDestroy } ) + --self:F({ ZoneNoDestroy = Zone.NoDestroy }) if Zone.NoDestroy then else - self:Remove( Zone.ZoneName ) + self:Remove(Zone.ZoneName) end end end @@ -6618,11 +6569,11 @@ do -- SET_ZONE -- @param #SET_ZONE self -- @param Core.Point#COORDINATE Coordinate The coordinate to be searched. -- @return Core.Zone#ZONE_BASE The zone (if any) that validates the coordinate location. - function SET_ZONE:IsCoordinateInZone( Coordinate ) + function SET_ZONE:IsCoordinateInZone(Coordinate) - for _, Zone in pairs( self:GetSet() ) do + for _, Zone in pairs(self:GetSet()) do local Zone = Zone -- Core.Zone#ZONE_BASE - if Zone:IsCoordinateInZone( Coordinate ) then + if Zone:IsCoordinateInZone(Coordinate) then return Zone end end @@ -6635,11 +6586,11 @@ do -- SET_ZONE -- @param Core.Point#COORDINATE Coordinate The reference coordinate from which the closest zone is determined. -- @return Core.Zone#ZONE_BASE The closest zone (if any). -- @return #number Distance to ref coordinate in meters. - function SET_ZONE:GetClosestZone( Coordinate ) + function SET_ZONE:GetClosestZone(Coordinate) local dmin=math.huge local zmin=nil - for _, Zone in pairs( self:GetSet() ) do + for _, Zone in pairs(self:GetSet()) do local Zone = Zone -- Core.Zone#ZONE_BASE local d=Zone:Get2DDistance(Coordinate) if d x2 ) and Coordinate.x or x2 - y1 = ( Coordinate.y < y1 ) and Coordinate.y or y1 - y2 = ( Coordinate.y > y2 ) and Coordinate.y or y2 - z1 = ( Coordinate.y < z1 ) and Coordinate.z or z1 - z2 = ( Coordinate.y > z2 ) and Coordinate.z or z2 + x1 = (Coordinate.x < x1) and Coordinate.x or x1 + x2 = (Coordinate.x > x2) and Coordinate.x or x2 + y1 = (Coordinate.y < y1) and Coordinate.y or y1 + y2 = (Coordinate.y > y2) and Coordinate.y or y2 + z1 = (Coordinate.y < z1) and Coordinate.z or z1 + z2 = (Coordinate.y > z2) and Coordinate.z or z2 end - Coordinate.x = ( x2 - x1 ) / 2 + x1 - Coordinate.y = ( y2 - y1 ) / 2 + y1 - Coordinate.z = ( z2 - z1 ) / 2 + z1 + Coordinate.x = (x2 - x1) / 2 + x1 + Coordinate.y = (y2 - y1) / 2 + y1 + Coordinate.z = (z2 - z1) / 2 + z1 - --self:F( { Coordinate = Coordinate } ) + --self:F({ Coordinate = Coordinate }) return Coordinate end @@ -8609,8 +8523,8 @@ do -- SET_SCENERY -- @param #SET_SCENERY self -- @param Wrapper.Scenery#SCENERY MScenery -- @return #SET_SCENERY self - function SET_SCENERY:IsIncludeObject( MScenery ) - --self:T(( MScenery.SceneryName ) + function SET_SCENERY:IsIncludeObject(MScenery) + --self:T((MScenery.SceneryName) local MSceneryInclude = true @@ -8620,25 +8534,25 @@ do -- SET_SCENERY -- Filter Prefixes if self.Filter.Prefixes then local MSceneryPrefix = false - for ZonePrefixId, ZonePrefix in pairs( self.Filter.Prefixes ) do - --self:T(( { "Prefix:", string.find( MSceneryName, ZonePrefix, 1 ), ZonePrefix } ) - if string.find( MSceneryName, ZonePrefix, 1 ) then + for ZonePrefixId, ZonePrefix in pairs(self.Filter.Prefixes) do + --self:T(({ "Prefix:", string.find(MSceneryName, ZonePrefix, 1), ZonePrefix }) + if string.find(MSceneryName, ZonePrefix, 1) then MSceneryPrefix = true end end - --self:T(( { "Evaluated Prefix", MSceneryPrefix } ) + --self:T(({ "Evaluated Prefix", MSceneryPrefix }) MSceneryInclude = MSceneryInclude and MSceneryPrefix end if self.Filter.Zones then local MSceneryZone = false - for ZoneName, Zone in pairs( self.Filter.Zones ) do - --self:T(( "Zone:", ZoneName ) + for ZoneName, Zone in pairs(self.Filter.Zones) do + --self:T(("Zone:", ZoneName) local coord = MScenery:GetCoordinate() if coord and Zone:IsCoordinateInZone(coord) then MSceneryZone = true end - --self:T(( { "Evaluated Zone", MSceneryZone } ) + --self:T(({ "Evaluated Zone", MSceneryZone }) end MSceneryInclude = MSceneryInclude and MSceneryZone end @@ -8647,13 +8561,13 @@ do -- SET_SCENERY if self.Filter.SceneryRoles then local MSceneryRole = false local Role = MScenery:GetProperty("ROLE") or "none" - for ZoneRoleId, ZoneRole in pairs( self.Filter.SceneryRoles ) do - --self:T(( { "Role:", ZoneRole, Role } ) + for ZoneRoleId, ZoneRole in pairs(self.Filter.SceneryRoles) do + --self:T(({ "Role:", ZoneRole, Role }) if ZoneRole == Role then MSceneryRole = true end end - --self:T(( { "Evaluated Role ", MSceneryRole } ) + --self:T(({ "Evaluated Role ", MSceneryRole }) MSceneryInclude = MSceneryInclude and MSceneryRole end end @@ -8663,7 +8577,7 @@ do -- SET_SCENERY MSceneryInclude = MSceneryInclude and MClientFunc end - --self:T(2( MSceneryInclude ) + --self:T(2(MSceneryInclude) return MSceneryInclude end @@ -8672,10 +8586,10 @@ do -- SET_SCENERY -- @return #SET_SCENERY self function SET_SCENERY:FilterOnce() - for ObjectName, Object in pairs( self:GetSet() ) do + for ObjectName, Object in pairs(self:GetSet()) do --self:T((ObjectName) - if self:IsIncludeObject( Object ) then - self:Add( ObjectName, Object ) + if self:IsIncludeObject(Object) then + self:Add(ObjectName, Object) else self:Remove(ObjectName, true) end @@ -8695,7 +8609,7 @@ do -- SET_SCENERY local Obj = obj -- Wrapper.Scenery#SCENERY life0 = life0 + Obj:GetLife0() end - ) + ) return life0 end @@ -8709,7 +8623,7 @@ do -- SET_SCENERY local Obj = obj -- Wrapper.Scenery#SCENERY life = life + Obj:GetLife() end - ) + ) return life end @@ -8845,7 +8759,7 @@ do -- SET_DYNAMICCARGO function SET_DYNAMICCARGO:New() --- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.DYNAMICCARGO ) ) -- Core.Set#SET_DYNAMICCARGO + local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.DYNAMICCARGO)) -- Core.Set#SET_DYNAMICCARGO return self end @@ -8854,14 +8768,14 @@ do -- SET_DYNAMICCARGO -- @param #SET_DYNAMICCARGO self -- @param Wrapper.DynamicCargo#DYNAMICCARGO DCargo -- @return #SET_DYNAMICCARGO self - function SET_DYNAMICCARGO:IsIncludeObject( DCargo ) - --self:F2( DCargo ) + function SET_DYNAMICCARGO:IsIncludeObject(DCargo) + --self:F2(DCargo) local DCargoInclude = true if self.Filter.Coalitions then local DCargoCoalition = false - for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do - --self:T2( { "Coalition:", DCargo:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) + for CoalitionID, CoalitionName in pairs(self.Filter.Coalitions) do + --self:T2({ "Coalition:", DCargo:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName }) if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == DCargo:GetCoalition() then DCargoCoalition = true end @@ -8871,8 +8785,8 @@ do -- SET_DYNAMICCARGO if self.Filter.Types then local DCargoType = false - for TypeID, TypeName in pairs( self.Filter.Types ) do - --self:T2( { "Type:", DCargo:GetTypeName(), TypeName } ) + for TypeID, TypeName in pairs(self.Filter.Types) do + --self:T2({ "Type:", DCargo:GetTypeName(), TypeName }) if TypeName == DCargo:GetTypeName() then DCargoType = true end @@ -8882,8 +8796,8 @@ do -- SET_DYNAMICCARGO if self.Filter.Countries then local DCargoCountry = false - for CountryID, CountryName in pairs( self.Filter.Countries ) do - --self:T2( { "Country:", DCargo:GetCountry(), CountryName } ) + for CountryID, CountryName in pairs(self.Filter.Countries) do + --self:T2({ "Country:", DCargo:GetCountry(), CountryName }) if country.id[CountryName] == DCargo:GetCountry() then DCargoCountry = true end @@ -8893,9 +8807,9 @@ do -- SET_DYNAMICCARGO if self.Filter.StaticPrefixes then local DCargoPrefix = false - for StaticPrefixId, StaticPrefix in pairs( self.Filter.StaticPrefixes ) do - --self:T2( { "Prefix:", string.find( DCargo:GetName(), StaticPrefix, 1 ), StaticPrefix } ) - if string.find( DCargo:GetName(), StaticPrefix, 1 ) then + for StaticPrefixId, StaticPrefix in pairs(self.Filter.StaticPrefixes) do + --self:T2({ "Prefix:", string.find(DCargo:GetName(), StaticPrefix, 1), StaticPrefix }) + if string.find(DCargo:GetName(), StaticPrefix, 1) then DCargoPrefix = true end end @@ -8904,8 +8818,8 @@ do -- SET_DYNAMICCARGO if self.Filter.Zones then local DCargoZone = false - for ZoneName, Zone in pairs( self.Filter.Zones ) do - --self:T2( "In zone: "..ZoneName ) + for ZoneName, Zone in pairs(self.Filter.Zones) do + --self:T2("In zone: "..ZoneName) if DCargo and DCargo:IsInZone(Zone) then DCargoZone = true end @@ -8918,40 +8832,28 @@ do -- SET_DYNAMICCARGO DCargoInclude = DCargoInclude and MClientFunc end - --self:T2( DCargoInclude ) + --self:T2(DCargoInclude) return DCargoInclude end - + --- Builds a set of dynamic cargo of defined coalitions. -- Possible current coalitions are red, blue and neutral. -- @param #SET_DYNAMICCARGO self -- @param #string Coalitions Can take the following values: "red", "blue", "neutral". -- @return #SET_DYNAMICCARGO self - function SET_DYNAMICCARGO:FilterCoalitions( Coalitions ) - if not self.Filter.Coalitions then - self.Filter.Coalitions = {} - end - if type( Coalitions ) ~= "table" then - Coalitions = { Coalitions } - end - for CoalitionID, Coalition in pairs( Coalitions ) do - self.Filter.Coalitions[Coalition] = Coalition - end - return self - end --- Builds a set of dynamic cargo of defined dynamic cargo type names. -- @param #SET_DYNAMICCARGO self -- @param #string Types Can take those type name strings known within DCS world. -- @return #SET_DYNAMICCARGO self - function SET_DYNAMICCARGO:FilterTypes( Types ) + function SET_DYNAMICCARGO:FilterTypes(Types) if not self.Filter.Types then self.Filter.Types = {} end - if type( Types ) ~= "table" then + if type(Types) ~= "table" then Types = { Types } end - for TypeID, Type in pairs( Types ) do + for TypeID, Type in pairs(Types) do self.Filter.Types[Type] = Type end return self @@ -8972,7 +8874,7 @@ do -- SET_DYNAMICCARGO -- if dynamiccargo:GetName() == "Exclude Me" then isinclude = false end -- return isinclude -- end - -- ):FilterOnce() + -- ):FilterOnce() -- BASE:I(cargoset:Flush()) --- Builds a set of dynamic cargo of defined countries. @@ -8980,14 +8882,14 @@ do -- SET_DYNAMICCARGO -- @param #SET_DYNAMICCARGO self -- @param #string Countries Can take those country strings known within DCS world. -- @return #SET_DYNAMICCARGO self - function SET_DYNAMICCARGO:FilterCountries( Countries ) + function SET_DYNAMICCARGO:FilterCountries(Countries) if not self.Filter.Countries then self.Filter.Countries = {} end - if type( Countries ) ~= "table" then + if type(Countries) ~= "table" then Countries = { Countries } end - for CountryID, Country in pairs( Countries ) do + for CountryID, Country in pairs(Countries) do self.Filter.Countries[Country] = Country end return self @@ -8998,14 +8900,14 @@ do -- SET_DYNAMICCARGO -- @param #SET_DYNAMICCARGO self -- @param #string Prefixes The string pattern(s) that need to be contained in the dynamic cargo name. Can also be passed as a `#table` of strings. -- @return #SET_DYNAMICCARGO self - function SET_DYNAMICCARGO:FilterPrefixes( Prefixes ) + function SET_DYNAMICCARGO:FilterPrefixes(Prefixes) if not self.Filter.StaticPrefixes then self.Filter.StaticPrefixes = {} end - if type( Prefixes ) ~= "table" then + if type(Prefixes) ~= "table" then Prefixes = { Prefixes } end - for PrefixID, Prefix in pairs( Prefixes ) do + for PrefixID, Prefix in pairs(Prefixes) do self.Filter.StaticPrefixes[Prefix] = Prefix end return self @@ -9016,7 +8918,7 @@ do -- SET_DYNAMICCARGO -- @param #SET_DYNAMICCARGO self -- @param #string Patterns The string pattern(s) that need to be contained in the dynamic cargo name. Can also be passed as a `#table` of strings. -- @return #SET_DYNAMICCARGO self - function SET_DYNAMICCARGO:FilterNamePattern( Patterns ) + function SET_DYNAMICCARGO:FilterNamePattern(Patterns) return self:FilterPrefixes(Patterns) end @@ -9032,7 +8934,7 @@ do -- SET_DYNAMICCARGO return false end end - ) + ) return self end @@ -9048,7 +8950,7 @@ do -- SET_DYNAMICCARGO return false end end - ) + ) return self end @@ -9064,7 +8966,7 @@ do -- SET_DYNAMICCARGO return false end end - ) + ) return self end @@ -9081,7 +8983,7 @@ do -- SET_DYNAMICCARGO return false end end - ) + ) return self end @@ -9089,20 +8991,20 @@ do -- SET_DYNAMICCARGO -- @param #SET_DYNAMICCARGO self -- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE -- @return #SET_DYNAMICCARGO self - function SET_DYNAMICCARGO:FilterZones( Zones ) + function SET_DYNAMICCARGO:FilterZones(Zones) if not self.Filter.Zones then self.Filter.Zones = {} end local zones = {} if Zones.ClassName and Zones.ClassName == "SET_ZONE" then zones = Zones.Set - elseif type( Zones ) ~= "table" or (type( Zones ) == "table" and Zones.ClassName ) then + elseif type(Zones) ~= "table" or (type(Zones) == "table" and Zones.ClassName) then self:E("***** FilterZones needs either a table of ZONE Objects or a SET_ZONE as parameter!") return self else zones = Zones end - for _,Zone in pairs( zones ) do + for _,Zone in pairs(zones) do local zonename = Zone:GetName() self.Filter.Zones[zonename] = Zone end @@ -9114,8 +9016,8 @@ do -- SET_DYNAMICCARGO -- @return #SET_DYNAMICCARGO self function SET_DYNAMICCARGO:FilterStart() if _DATABASE then - self:HandleEvent( EVENTS.NewDynamicCargo, self._EventHandlerDCAdd ) - self:HandleEvent( EVENTS.DynamicCargoRemoved, self._EventHandlerDCRemove ) + self:HandleEvent(EVENTS.NewDynamicCargo, self._EventHandlerDCAdd) + self:HandleEvent(EVENTS.DynamicCargoRemoved, self._EventHandlerDCRemove) if self.Filter.Zones then self.ZoneTimer = TIMER:New(self._ContinousZoneFilter,self) local timing = self.ZoneTimerInterval or 30 @@ -9132,8 +9034,8 @@ do -- SET_DYNAMICCARGO -- @return #SET_DYNAMICCARGO self function SET_DYNAMICCARGO:FilterStop() if _DATABASE then - self:UnHandleEvent( EVENTS.NewDynamicCargo) - self:UnHandleEvent( EVENTS.DynamicCargoRemoved ) + self:UnHandleEvent(EVENTS.NewDynamicCargo) + self:UnHandleEvent(EVENTS.DynamicCargoRemoved) if self.ZoneTimer and self.ZoneTimer:IsRunning() then self.ZoneTimer:Stop() end @@ -9147,10 +9049,10 @@ do -- SET_DYNAMICCARGO -- @return #SET_DYNAMICCARGO self function SET_DYNAMICCARGO:_ContinousZoneFilter() local Database = _DATABASE.DYNAMICCARGO - for ObjectName, Object in pairs( Database ) do - if self:IsIncludeObject( Object ) and self:IsNotInSet(Object) then - self:Add( ObjectName, Object ) - elseif (not self:IsIncludeObject( Object )) and self:IsInSet(Object) then + for ObjectName, Object in pairs(Database) do + if self:IsIncludeObject(Object) and self:IsNotInSet(Object) then + self:Add(ObjectName, Object) + elseif (not self:IsIncludeObject(Object)) and self:IsInSet(Object) then self:Remove(ObjectName) end end @@ -9161,14 +9063,14 @@ do -- SET_DYNAMICCARGO --- Handles the events for the Set. -- @param #SET_DYNAMICCARGO self -- @param Core.Event#EVENTDATA Event - function SET_DYNAMICCARGO:_EventHandlerDCAdd( Event ) + function SET_DYNAMICCARGO:_EventHandlerDCAdd(Event) if Event.IniDynamicCargo and Event.IniDynamicCargoName then if not _DATABASE.DYNAMICCARGO[Event.IniDynamicCargoName] then - _DATABASE:AddDynamicCargo( Event.IniDynamicCargoName ) + _DATABASE:AddDynamicCargo(Event.IniDynamicCargoName) end - local ObjectName, Object = self:FindInDatabase( Event ) - if Object and self:IsIncludeObject( Object ) then - self:Add( ObjectName, Object ) + local ObjectName, Object = self:FindInDatabase(Event) + if Object and self:IsIncludeObject(Object) then + self:Add(ObjectName, Object) end end @@ -9178,11 +9080,11 @@ do -- SET_DYNAMICCARGO --- Handles the remove event for dynamic cargo set. -- @param #SET_DYNAMICCARGO self -- @param Core.Event#EVENTDATA Event - function SET_DYNAMICCARGO:_EventHandlerDCRemove( Event ) + function SET_DYNAMICCARGO:_EventHandlerDCRemove(Event) if Event.IniDCSUnitName then - local ObjectName, Object = self:FindInDatabase( Event ) + local ObjectName, Object = self:FindInDatabase(Event) if ObjectName then - self:Remove( ObjectName ) + self:Remove(ObjectName) end end @@ -9195,7 +9097,7 @@ do -- SET_DYNAMICCARGO -- @param Core.Event#EVENTDATA Event -- @return #string The name of the DYNAMICCARGO -- @return Wrapper.DynamicCargo#DYNAMICCARGO The DYNAMICCARGO object - function SET_DYNAMICCARGO:FindInDatabase( Event ) + function SET_DYNAMICCARGO:FindInDatabase(Event) return Event.IniDCSUnitName, self.Set[Event.IniDCSUnitName] end @@ -9234,7 +9136,7 @@ do -- SET_DYNAMICCARGO table.insert(owners, cargo.Owner, cargo.Owner) end end - ) + ) return owners end @@ -9249,7 +9151,7 @@ do -- SET_DYNAMICCARGO table.insert(owners, cargo.StaticName, cargo.warehouse) end end - ) + ) return owners end @@ -9267,7 +9169,7 @@ do -- SET_DYNAMICCARGO end end end - ) + ) return owners end diff --git a/Moose Development/Moose/Core/Vector.lua b/Moose Development/Moose/Core/Vector.lua new file mode 100644 index 000000000..ce8ab3631 --- /dev/null +++ b/Moose Development/Moose/Core/Vector.lua @@ -0,0 +1,1381 @@ +--- **CORE** - Vector algebra. +-- +-- **Main Features:** +-- +-- * Easy vector algebra function +-- * Redefinition of `+`, `-`, `*`, `/`, `%` operators to be compatible with vectors +-- * Interface to DCS API functions +-- * Reduced confusion of DCS coordinate system +-- * Better performance than related classes (COORDINATE, POINT_VEC) +-- +-- === +-- +-- ## Example Missions: +-- +-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Core%20-%20Vector). +-- +-- === +-- +-- ### Author: **funkyfranky** +-- +-- === +-- @module Core.Vector +-- @image CORE_Vector.png + + +--- VECTOR class. +-- @type VECTOR +-- @field #string ClassName Name of the class. +-- @field #number verbose Verbosity of output. +-- @field #number x Component pointing North if > 0 and South if < 0. +-- @field #number y Component pointing up if >0 and down if < 0. This describes the altitude above main sea level. +-- @field #number z Component pointing East if > 0 and West if < 0. + +--- *Mathematics knows no races or geographic boundaries; for mathematics, the cultural world is one country.* --David Hilbert +-- +-- === +-- +-- # The VECTOR Concept +-- +-- The VECTOR class has a great concept! +-- +-- https://github.com/automattf/vector.lua/blob/master/vector.lua +-- +-- # The DCS Coordinate System +-- +-- DCS has a rather unconventional way to define the coordinate system. The definition even depends whether you work with a 2D vector or a 3D vector. +-- The good think is, that you usually do not need to worry about this unless you directly call the DCS API functions. Plus, this class tries to +-- hide the differences between 2D and 3D conventions as good as possible with internal if-cases. +-- +-- Still, it is important to unterstand the differences. So let us explain: +-- +-- Usually, we draw a coordinate system in 2D and label the horizonal axis (pointing right) as the x-axis. The vertical axis (poining up on a piece of paper) +-- +-- ## 3D +-- +-- The x-axis points North. The z-axis points East. The y-axis points upwards and defines the altitue with respect to the mean sea level. +-- +-- ## 2D +-- +-- The x-axis points North (just like in the 3D) case. The y-axis points East. +-- +-- # Constructors +-- +-- There are different ways to create a new instance of a VECTOR. All methods start with `New`. +-- +-- ## From Components +-- +-- ## From 2D or 3D Vectors +-- +-- ## From Polar Coordinates +-- +-- ## From Spherical Coordinates +-- +-- +-- # Operators +-- +-- ## Addition [MATH] `+` +-- +-- ## Subtraction [MATH] `-` +-- +-- ## Multiplication [MATH] `*` +-- +-- ## Devision [MATH] `/` +-- +-- ## Modulo [MATH] `%` +-- +-- +-- # DCS API Interface +-- +-- This class offers easy and convenient ways to access all vector related DCS API functions. +-- +-- ## Land and Surface +-- +-- ## Atmosphere +-- +-- ## Effects and Actions +-- +-- ## Map Markings +-- +-- ## Coordinates +-- +-- # Inferface to other MOOSE Classes +-- +-- Of course, this class is interfaced with other MOOSE classes in the sense that you can obtain and work with VECTOR instances from MOOSE objects, from which a position or direction +-- vector can be derived. +-- +-- ## GROUP +-- +-- ## UNIT +-- +-- ## STATIC +-- +-- ## SCENERY +-- +-- ## ZONE +-- +-- # Examples +-- +-- A new `VECTOR` object can be created with the @{#VECTOR.New} function. +-- Here we create two vectors, a and b, and add them to create a new vector c. +-- +-- local a=VECTOR:New(1, 0) +-- local b=VECTOR:New(0, 1) +-- local c=a+b +-- +-- This is how it works. +-- +-- @field #VECTOR +VECTOR = { + ClassName = "VECTOR", + verbose = 0, +} + +--- VECTOR class version. +-- @field #string version +VECTOR.version="0.1.0" + +--- VECTOR unique ID +_VECTORID=0 + +--- VECTOR private index. +-- @field #VECTOR __index +VECTOR.__index = VECTOR + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- ToDo list +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +-- TODO: 3D rotation +-- DONE: Markers +-- TODO: Documentation + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Constructor +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Create a new VECTOR class instance from given cartesian coordinates `x`, `y` and `z`, where `z` is optional. +-- **Note** that whether the `z` component is passed or nil has great impact on the interpretation of the `y` component. +-- If `z` is not given (`nil`), then `y` is used as `z` (the coordinate pointing East) because we always constuct a 3D vector. +-- If `z` is given (not `nil`), then `y` is used as coordinate pointing up (out of plane) as for all 3D DCS vectors. +-- @param #VECTOR self +-- @param #number x Component of vector along x-axis (pointing North in both 2D and 3D). +-- @param #number y Component of vector along y-axis (pointing East in 2D and Up in 3D). +-- @param #number z (Optional) Component of the z-axis (pointing East in 3D). +-- @return #VECTOR self +function VECTOR:New(x, y, z) + + if z==nil then + -- z-component is not given ==> 2D ==> we take y as z and set y=0. + self=setmetatable({x=x or 0, y=0, z=y or 0}, VECTOR) + else + self=setmetatable({x=x or 0, y=y or 0, z=z or 0}, VECTOR) + end + + _VECTORID=_VECTORID+1 + + self.uid=_VECTORID + + return self +end + +--- Create a new VECTOR class instance from given 2D or 3D vector object. +-- @param #VECTOR self +-- @param DCS#Vec3 Vec Vector object with `x`, `y` and optionally `z` components. Can be a DCS#Vec2, DCS#Vec3, `COORDINATE`, `VECTOR` object. +-- @return #VECTOR self +function VECTOR:NewFromVec(Vec) + + -- The :New function takes care whether this is a 2D or 3D vector. + local vector=VECTOR:New(Vec.x, Vec.y, Vec.z) + + return vector +end + +--- Create a new VECTOR class instance from polar coordinates (r, phi). +-- @param #VECTOR self +-- @param #number r Distance. +-- @param #number phi Angle in Degrees. Note that 0° corresponds to North, 90° East etc. +-- @return #VECTOR self +function VECTOR:NewFromPolar(r, phi) + + -- Convert deg to rad. + local Phi=math.rad(phi) + + -- Polar coordinates. What we want in DCS is: + -- North: Phi=0 ==> x= 1, y= 0 + -- East : Phi=90 ==> x= 0, y= 1 + -- South: Phi=180 ==> x=-1, y= 0 + -- West : Phi=270 ==> x= 0, y=-1 + + -- sin(0)=0 sin(90)=1, sin(180)=0, sin(270)=-1 ==> y + -- cos(0)=1 cos(90)=0, cos(180)=-1, cos(270)=0 ==> x + + local x=r*math.cos(phi) + local y=r*math.sin(phi) + + -- Create new vector. As z is nil, the 2D character is taken care of. + local v=VECTOR:New(x, y) + + return self +end + +--- Create a new VECTOR class instance from spherical coordinates (r, theta, phi). +-- @param #VECTOR self +-- @param #number r Distance in meters with r>=0. +-- @param #number theta Polar angle in Degrees measured from a fixed polar axis or zenith direction. This angle is in [0°, 180°]. +-- @param #number phi Azimuthal angle in Degrees. This angle is in [0°, 360°). +-- @return #VECTOR self +function VECTOR:NewFromSpherical(r, theta, phi) + + local sinPhi=math.sin(math.rad(phi)) + local cosPhi=math.cos(math.rad(phi)) + local sinTheta=math.sin(math.rad(theta)) + local cosTheta=math.cos(math.rad(theta)) + + --TODO: Check x,y,z convention for DCS. + local x=r*sinTheta*cosPhi + local y=r*sinTheta*sinPhi + local z=r*cosTheta + + local v=VECTOR:New(x, y, z) + + return self +end + +--- Get the directional vector that points from a given vector `a` to another given vector `b`. +-- The vector is `c=-a+b=b-a`. +-- @param #VECTOR self +-- @param #VECTOR a Vector a. This can also be given as any table with x, y and z components (z optional). +-- @param #VECTOR b Vector b. This can also be given as any table with x, y and z components (z optional). +-- @return #VECTOR Directional vector from a to b. +function VECTOR:NewDirectionalVector(a, b) + + local x=b.x-a.x + local y + local z + + if a.z and b.z then + -- Both given vectors are 3D + y=b.y-a.y + z=b.z-a.z + elseif b.z then + -- a is 2D and b is 3D + y=b.y-0 + z=b.z-a.y + elseif a.z then + -- a is 3D and b is 2D + y=0-a.y + z=b.y-a.z + else + -- a is 2D and b is 2D + y=b.y-a.y + z=nil --We leave z=nil, so the New function takes care of the 2D character. + end + + local c=VECTOR:New(x, y, z) + + return c +end + + +--- Creates a new VECTOR instance from given the latitude and longitude in decimal degrees (DD). +-- @param #VECTOR self +-- @param #number Latitude Latitude in decimal degrees. +-- @param #number Longitude Longitude in decimal degrees. +-- @param #number Altitude (Optional) Altitude in meters. Default is the land height at the 2D position. +-- @return #VECTOR self +function VECTOR:NewFromLLDD(Latitude, Longitude, Altitude) + + -- Returns a point from latitude and longitude in the vec3 format. + local vec3=coord.LLtoLO(Latitude, Longitude) + + -- Convert vec3 to coordinate object. + self=VECTOR:NewFromVec(vec3) + + -- Adjust height + if Altitude then + self.y=Altitude + end + + return self +end + +--- Creates a new VECTOR instance from given latitude and longitude in degrees, minutes and seconds (DMS). +-- **Note** that latitude and longitude are passed as strings and the characters `°`, `'` and `"` are important. +-- @param #VECTOR self +-- @param #string Latitude Latitude in DMS as string, e.g. "`42° 24' 14.3"`". +-- @param #string Longitude Longitude in DMS as string, e.g. "`42° 24' 14.3"`". +-- @param #number Altitude (Optional) Altitude in meters. Default is the land height at the coordinate. +-- @return #VECTOR +function VECTOR:NewFromLLDMS(Latitude, Longitude, Altitude) + + local lat=UTILS.LLDMSstringToDD(Latitude) + local lon=UTILS.LLDMSstringToDD(Longitude) + + self=VECTOR:NewFromLLDD(lat, lon, Altitude) + + return self +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- User Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Get 2D vector as simple table. +-- @param #VECTOR self +-- @return DCS#Vec2 2D array {x, y}. +function VECTOR:GetVec2() + + local vec={x=self.x, y=self.z} + + return vec +end + +--- Get 3D vector as simple table. +-- @param #VECTOR self +-- @param #boolean OnSurface If `true`, the `y` component is set the land height [m] of the 2D position. +-- @return DCS#Vec3 3D array {x=x, y=y, z=z}. +function VECTOR:GetVec3(OnSurface) + + local x=self.x + local y=OnSurface and land.getHeight({x=self.x, y=self.z}) or self.y + local z=self.z + + local vec={x=x, y=y, z=z} --DCS#Vec3 + + return vec +end + +--- Get a COORDINATE object. +-- @param #VECTOR self +-- @param #boolean OnSurface If `true`, the `y` component is set the land height [m] of the 2D position. +-- @return Core.Point#COORDINATE The COORDINATE object. +function VECTOR:GetCoordinate(OnSurface) + + local vec3=self:GetVec3(OnSurface) + + local coordinate=COORDINATE:NewFromVec3(vec3) + + return coordinate +end + +--- Get the distance from this vector to another vector. +-- @param #VECTOR self +-- @param #VECTOR Vector Vector to which the distance is requested. +-- @param #boolean Only2D If `true`, calculate only the projected 2D distance. +-- @return #number Distance in meters. +function VECTOR:GetDistance(Vector, Only2D) + + local dx=self.x-Vector.x + local dy=0 + local dz=0 + + if Vector.z then + if not Only2D then + dy=self.y-Vector.y + end + dz=self.z-Vector.z + else + -- Given vector is 2D. + dy=0 + dz=self.z-Vector.y + end + + -- Calculate the distance. + local dist=math.sqrt( dx*dx + dy*dy + dz*dz ) + + return dist +end + +--- Get the directional vector that points from this VECTOR to another VECTOR `a`. +-- @param #VECTOR self +-- @param #VECTOR a Vector `a`. This can also be given as any table with `x`, `y` and `z` components, where `z` is optional. +-- @return #VECTOR Directional vector from this vector to `a`. +function VECTOR:GetDirectionalVectorTo(a) + + local x=a.x-self.x + local y=0 + local z=nil + + if a.z then + -- a is 3D + y=a.y-self.y + z=a.z-self.z + else + -- a is 2D + y=a.y-self.z + z=nil --We leave z=nil, so the New function takes care of the 2D character. + end + + local c=VECTOR:New(x, y, z) + + return c +end + +--- Get the directional vector that points from another VECTOR `a` to this VECTOR. +-- @param #VECTOR self +-- @param #VECTOR a Vector a. This can also be given as any table with x,y and z components (z optional). +-- @return #VECTOR Directional vector from self to a. +function VECTOR:GetDirectionalVectorFrom(a) + + local x=self.x-a.x + local y + local z + + if a.z then + -- a is 3D + y=self.y-a.y + z=self.z-a.z + else + -- a is 2D ==> we work in 2D and take z component of self + y=self.z-a.y + z=nil --We leave z=nil, so the New function takes care of the 2D character. + end + + local c=VECTOR:New(x, y, z) + + return c +end + +--- Get length/norm/magnitude of this vector. +-- @param #VECTOR self +-- @return #number Length of vector. +function VECTOR:GetLength() + + local l=math.sqrt(self.x*self.x+self.y*self.y+self.z*self.z) + + return l +end + +--- Get heading of vector. +-- Note that a heading of +-- +-- * 000° = North +-- * 090° = East +-- * 180° = South +-- * 270° = West. +-- +-- @param #VECTOR self +-- @param #boolean To360 If `true` or `nil`, adjust heading to [0,360) range. If `false`, headings not in this range can occur. +-- @return #number Heading in degrees. +function VECTOR:GetHeading(To360) + + -- Get heading. + local heading=math.atan2(self.z, self.x) + + -- Convert to degrees. + heading=math.deg(heading) + + + if To360==nil or To360==true then + -- Adjust heading so it is in [0,360). + heading=UTILS.AdjustHeading360(heading) + else + -- Just make sure 360 is returned a 0. + if heading==360.0 then + heading=0.0 + end + end + + return heading +end + +--- Get the heading from this vector to another given vector. +-- @param #VECTOR self +-- @param #VECTOR Vector Vector to which the heading is requested. +-- @return #number Heading from this vector to the other vector in degrees. +function VECTOR:GetHeadingTo(Vector) + + -- Get directional vector from given vector to this vector. + local a=self:GetDirectionalVectorTo(Vector) + + -- Get heading of directional vector. + local heading=math.deg(math.atan2(a.z, a.x)) + + -- Adjust heading so it is in [0,360). + heading=UTILS.AdjustHeading360(heading) + + return heading +end + +--- Get the heading from a given vector to this vector. +-- @param #VECTOR self +-- @param #VECTOR Vector Vector from which the heading is requested. +-- @return #number Heading from the other vector to this vector in degrees. +function VECTOR:GetHeadingFrom(Vector) + + -- Get directional vector from given vector to this vector. + local a=self:GetDirectionalVectorFrom(Vector) + + -- Get heading of directional vector. + local heading=math.deg(math.atan2(a.z, a.x)) + + -- Adjust heading so it is in [0,360). + heading=UTILS.AdjustHeading360(heading) + + return heading +end + + +--- Get latitude and longitude of this vector. +-- @param #VECTOR self +-- @return #number Latitude in decimal degrees (DD). +-- @return #number Longitude in decimal degrees (DD). +function VECTOR:GetLatitudeLongitude() + + local vec3=self:GetVec3() + + local latitude, longitude, altitude=coord.LOtoLL(vec3) + + return latitude, longitude +end + + +--- Get MGRS information of this vector. +-- +-- `MGRS = {UTMZone = string, MGRSDigraph = string, Easting = number, Northing = number}` +-- +-- @param #VECTOR self +-- @return #table MGRS table with `UTMZone`, `MGRSDiGraph`, `Easting` and `Northing` keys. +function VECTOR:GetMGRS() + + local lat, long=self:GetLatitudeLongitude() + + local mgrs=coord.LLtoMGRS(lat, long) + + -- Example table returned by coord.LLtoMGRS + --[[ + MGRS = { + UTMZone = string, + MGRSDigraph = string, + Easting = number, + Northing = number + } + ]] + + return mgrs +end + +--- Get the difference of the heading of this vector w. +-- +-- **Example 1:** +-- This vector has a heading of 90° (pointing East) and the other vector has a heading of 225° (pointing South-West), +-- we would optain a delta of 225°-90°=135°. +-- +-- **Example 2:** +-- This vector has a heading of 180 (pointing South) and the other vector has a heading of 90° (pointing East), +-- we would optain a delta of 90°-180°=-90°. +-- +-- @param #VECTOR self +-- @param #VECTOR Vector Vector to which the heading is requested. +-- @return #number Heading from this vector to the other vector in degrees. +function VECTOR:GetHeadingDelta(Vector) + + local h1=self:GetHeading(false) + + local h2=Vector:GetHeading(false) + + local delta=h2-h1 + + return delta +end + +--- Return an intermediate VECTOR between this and another given vector. +-- @param #VECTOR self +-- @param #VECTOR Vector The destination vector. +-- @param #number Fraction The fraction (0,1) where the new vector is created. Default 0.5, *i.e.* in the middle. +-- @return #VECTOR Vector between this and the other vector. +function VECTOR:GetIntermediateVector(Vector, Fraction) + + local f=Fraction or 0.5 + + -- Get the directional vector to the given Vector. + local vec=self:GetDirectionalVectorTo(Vector) + + -- Get the length of the vector. + local length=vec:GetLength() + + -- Set/scale the length. + vec:SetLength(f*length) + + -- Get to the desired position. + vec=self+vec + + return vec +end + + + +--- Set length/norm/magnitude of this vector. +-- @param #VECTOR self +-- @param #number Length Desired length of vector. +function VECTOR:SetLength(Length) + + -- Normalize this vector to a length of 1. + self:Normalize() + + -- Scale the vector to the desired length. + local v=self*Length + + -- Replace with scaled version. + self:Replace(v) + + return self +end + +--- Set x-component of vector. The x-axis points to the North. +-- @param #VECTOR self +-- @param #number x Value of x. Default 0. +-- @return #VECTOR self +function VECTOR:SetX(x) + self.x=x or 0 + return self +end + +--- Set y-component of vector. The y-axis points to the upwards and describes the altitude above mean sea level. +-- @param #VECTOR self +-- @param #number y Value of y. Default land/surface height at this point. +-- @return #VECTOR self +function VECTOR:SetY(y) + + if y==nil then + y=self:GetSurfaceHeight() + end + self.y=y + return self +end + +--- Set z-component of vector. The z-axis points to the East. +-- @param #VECTOR self +-- @param #number z Value of z. Default 0. +-- @return #VECTOR self +function VECTOR:SetZ(z) + self.z=z or 0 + return self +end + + +--- Add a vector to this. This function works for DCS#Vec2, DCS#Vec3, VECTOR, COORDINATE objects. +-- Note that if you want to add a VECTOR, you can also simply use the `+` operator. +-- @param #VECTOR self +-- @param DCS#Vec3 Vec Vector to add. Can also be a DCS#Vec2, DCS#Vec3, COORDINATE or VECTOR object. +-- @return #VECTOR self +function VECTOR:AddVec(Vec) + + self.x=self.x+Vec.x + + if Vec.z then + self.y=self.y+Vec.y + self.z=self.z+Vec.z + else + -- Vec is 2D ==> we take its y-component. + self.z=self.z+Vec.y + end + + return self +end + +--- Subtract a vector from this one. This function works for DCS#Vec2, DCS#Vec3, VECTOR, COORDINATE objects. +-- +-- **Note** that if you want to add a VECTOR, you can also simply use the `-` operator. +-- @param #VECTOR self +-- @param DCS#Vec3 Vec Vector to substract. Can also be a DCS#Vec2, DCS#Vec3, COORDINATE or VECTOR object. +-- @return #VECTOR self +function VECTOR:SubVec(Vec) + + self.x=self.x-Vec.x + + if Vec.z then + self.y=self.y-Vec.y + self.z=self.z-Vec.z + else + -- Vec is 2D ==> we take its y-component. + self.z=self.z-Vec.y + end + + return self +end + +--- Calculate the dot product of this VECTOR with another vector. This function works for DCS#Vec2, DCS#Vec3, VECTOR, COORDINATE objects. +-- @param #VECTOR self +-- @param DCS#Vec3 Vec The other vector. Can also be a DCS#Vec2, DCS#Vec3, COORDINATE or VECTOR object. +-- @return #number Dot product Sum_i(a[i]*b[i]). Note that this is a **scalar** and not a vector any more! +function VECTOR:Dot(Vec) + + local dot=self.x*Vec.x + if Vec.z then + dot=dot+self.y*Vec.y+self.z*Vec.z + else + -- Vec is 2D ==> we take its y-component for z. + dot=dot+self.z*Vec.y + end + + return dot +end + +--- Calculate the rotation or cross product of this VECTOR with another vector. This function works for DCS#Vec2, DCS#Vec3, VECTOR, COORDINATE objects. +-- @param #VECTOR self +-- @param DCS#Vec3 Vec The other vector. Can also be a DCS#Vec2, DCS#Vec3, COORDINATE or VECTOR object. +-- @return #VECTOR The cross product vector. +function VECTOR:Rot(Vec) + + -- TODO: + local dot=self.x*Vec.x + if Vec.z then + dot=dot+self.y*Vec.y+self.z*Vec.z + else + -- Vec is 2D ==> we take its y-component for z. + dot=dot+self.z*Vec.y + end + + return dot +end + + +--- Get a clone (deep copy) of this vector. +-- @param #VECTOR self +-- @return #VECTOR Copy of the vector. +function VECTOR:Copy() + + local c=VECTOR:New(self.x, self.y, self.z) + + return c +end + +--- Replace this vector with another one. +-- If the given vector is 2D, we +-- @param #VECTOR self +-- @param #VECTOR Vector The vector that is used to replace this vector. +-- @param #boolean Project2D If `true` and the given vector is 2D, we project the updated vector to 2D (`y=0`). Otherwise, we leave `y` untouched. +-- @return #VECTOR self updated +function VECTOR:Replace(Vector, Project2D) + + self.x=Vector.x + + if Vector.z then + -- Given vector is 3D + self.y=Vector.y + self.z=Vector.z + else + -- Given vector is 2D + if Project2D then + self.y=0 + end + self.z=Vector.y + end + + return self +end + +--- Normalize this vector, so that has a length of 1. +-- @param #VECTOR self +-- @return #VECTOR self +function VECTOR:Normalize() + + local l=self:GetLength() + + if l~=0 then + self:Replace(self/l) + end + + return self +end + +--- Translate the vector by a given distance and angle. +-- @param #VECTOR self +-- @param #number Distance Distance in meters. Default 1000 meters. +-- @param #number Heading Heading angle in degrees. Default 0° = North. +-- @param #boolean Copy Create a copy of the VECTOR so the original stays unchanged. +-- @return #VECTOR The translated vector or a copy of it. +function VECTOR:Translate(Distance, Heading, Copy) + + -- Set default distance if not passed. + Distance=Distance or 1000 + + -- Angle in rad. + local alpha = math.rad(Heading or 0) + + -- Create a copy if requested. + local vector=Copy and self:Copy() or self + + -- Set new coordinates. + vector.x = Distance * math.cos(alpha) + vector.x -- New x + vector.z = Distance * math.sin(alpha) + vector.z -- New z + + return vector +end + +--- Rotate the VECTOR clockwise in the 2D (x,z) plane. +-- @param #VECTOR self +-- @param #number Angle Rotation angle in degrees). Default 0. +-- @param #boolean Copy Create a copy of the VECTOR so the original stays unchanged. +-- @return #VECTOR The translated vector or a copy of it. +function VECTOR:Rotate2D(Angle, Copy) + + -- Angle in rad. + local phi = -math.rad(Angle or 0) + + -- Sin/Cos of angle. + local sinPhi = math.sin(phi) + local cosPhi = math.cos(phi) + + -- Get more convenient notation. + local X=self.z + local Y=self.x + + -- Apply rotation matrix. + local z = X*cosPhi - Y*sinPhi + local x = X*sinPhi + Y*cosPhi + + -- Create new vector. + if Copy then + local vector=VECTOR:New(x, self.y, z) + return vector + else + self:SetX(x) + self:SetZ(z) + return self + end +end + + + +--- Provides an MGRS string. +-- @param #VECTOR self +-- @param Core.Settings#SETTINGS Settings (Optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object. +-- @return #string The MGRS text. +function VECTOR:ToStringMGRS(Settings) + + -- Get Accuracy. + local MGRS_Accuracy = Settings and Settings.MGRS_Accuracy or _SETTINGS.MGRS_Accuracy + + local lat, lon = coord.LOtoLL( self:GetVec3() ) + + local MGRS = coord.LLtoMGRS( lat, lon ) + + local text="MGRS " .. UTILS.tostringMGRS( MGRS, MGRS_Accuracy ) + + return text +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- DCS API Wrapper Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Get the surface type at the vector. +-- +-- * LAND = 1 +-- * SHALLOW_WATER = 2 +-- * WATER = 3 +-- * ROAD = 4 +-- * RUNWAY = 5 +-- +-- @param #VECTOR self +-- @return #number Surface type +function VECTOR:GetSurfaceType() + + local vec2=self:GetVec2() + + local s=land.getSurfaceType(vec2) + + return s +end + + +--- Get the name of surface type at the vector. +-- +-- * LAND = 1 +-- * SHALLOW_WATER = 2 +-- * WATER = 3 +-- * ROAD = 4 +-- * RUNWAY = 5 +-- +-- @param #VECTOR self +-- @return #string Surface type name +function VECTOR:GetSurfaceTypeName() + + local vec2=self:GetVec2() + + local s=land.getSurfaceType(vec2) + + for name,id in land.SurfaceType() do + if id==s then + return name + end + end + + return "unknown" +end + +--- Check if a given vector has line of sight with this vector. +-- @param #VECTOR self +-- @param #VECTOR Vec The other vector. +-- @return #number Surface Type +function VECTOR:IsVisible(Vec) + + local vec1=self:GetVec3() + + local vec2={x=Vec.x, Vec.y, Vec.z} + + local los=land.isVisible(vec1, vec2) + + return los +end + +--- Get a vector on the closest road. +-- @param #VECTOR self +-- @return #VECTOR Closest vector to a road. +function VECTOR:GetClosestRoad() + + local vec2=self:GetVec2() + + local x,y=land.getClosestPointOnRoads('roads', vec2.x, vec2.y) + + local road=nil + if x and y then + road=VECTOR:New(x, y) + end + + return road +end + +--- Get a vector on the closest railroad. +-- @param #VECTOR self +-- @return #VECTOR Closest vector to a railroad. +function VECTOR:GetClosestRailroad() + + local vec2=self:GetVec2() + + local x,y=land.getClosestPointOnRoads('railroads', vec2.x, vec2.y) + + local road=nil + if x and y then + road=VECTOR:New(x, y) + end + + return road +end + + +--- Get the path on road from this vector to a given other vector. +-- @param #VECTOR self +-- @param #VECTOR Vec The destination vector. +-- @return Core.Pathline#PATHLINE Pathline with points on road. +function VECTOR:GetPathOnRoad(Vec) + + local vec1=self:GetVec2() + local vec2=Vec:GetVec2() + + local vec2points=land.findPathOnRoads("roads", vec1.x , vec1.y, vec2.x, vec2.y) + + local path=nil + if vec2points then + path=PATHLINE:NewFromVec2Array("Road", vec2points) + end + + return path +end + + +--- Get profile of the land between the two passed points. +-- @param #VECTOR self +-- @param #VECTOR Vec3 The 3D destination vector. If a 2D vector is passed, `y` is set to the land height. +-- @return Core.Pathline#PATHLINE Pathline with points of the profile. +function VECTOR:GetProfile(Vec3) + + local vec3=self:GetVec3() + + -- Get profile + local vec3s=land.profile(vec3, Vec3) + + local profile=nil + if vec3s then + + profile=PATHLINE:NewFromVec3Array("Profile", vec3s) + + end + + return profile +end + +--- Returns an intercept point at which a ray drawn from the this vector in the passed normalized direction for a specified distance. +-- @param #VECTOR self +-- @param DCS#Vec3 DirectionVector Directional vector. +-- @param #number Distance Distance in meters. Default 1000 m. +-- @return #VECTOR Intercept vector. Can be `nil` if no intercept point is found. +function VECTOR:GetInterceptPoint(DirectionVector, Distance) + + local vec3=self:GetVec3() + + local ip3=land.getIP(vec3, DirectionVector, Distance or 1000) --DCS#Vec3 + + local ipvector=nil + + if ip3 then + ipvector=VECTOR:New(ip3.x, ip3.y , ip3.z) + end + + return ipvector +end + +--- Returns the distance from sea level at this vector. +-- @param #VECTOR self +-- @return #number Distance above sea leavel in meters. +function VECTOR:GetSurfaceHeight() + + local vec2=self:GetVec2() + + local h=land.getHeight(vec2) + + return h +end + + +--- Returns the distance from sea level at this vector. +-- @param #VECTOR self +-- @return #number Heigh above sea leavel in meters. +-- @return #number Depth (positive) at this point in meters. +function VECTOR:GetSurfaceHeightAndDepth() + + local vec2=self:GetVec2() + + local h,d=land.getSurfaceHeightWithSeabed(vec2) + + return h,d +end + +--- Returns a velocity vector of the wind at this vector. Turbolences can be optionally be included. +-- @param #VECTOR self +-- @param #boolean WithTurbulence If `true`, return wind including turbulence. +-- @return #VECTOR Velocity 3D vector [m/s] the wind is blowing to. +function VECTOR:GetWindVector(WithTurbulence) + + local vec3=self:GetVec3() + + local wind=nil + if WithTurbulence then + wind=atmosphere.getWindWithTurbulence(vec3) + + else + wind=atmosphere.getWind(vec3) + end + + local vector=VECTOR:New(wind) + + return vector +end + +--- Returns a temperature and pressure at this vector. +-- @param #VECTOR self +-- @return #number Temperatur in Kelvin. +-- @return #number Pressure in Pascals. +function VECTOR:GetTemperaturAndPressure() + + local vec3=self:GetVec3() + + local t,p=atmosphere.getTemperatureAndPressure(vec3) + + return t,p +end + +--- Creates a smoke at this vector. +-- @param #VECTOR self +-- @param #number Color Color of the smoke: 0=Green, 1=Red, 2=White, 3=Orange, 4=Blue. Default 0. +-- @param #number Duration (Optional) Duration of the smoke in seconds. Default nil. +-- @return #string Name of the smoke object. Can be used to stop it. +function VECTOR:Smoke(Color, Duration) + + local vec3=self:GetVec3() + + Color=Color or 0 + + -- Create a name for the smoke object + local name=string.format("Vector-Smoke-%d", self.uid) + + -- Create smoke at this position + trigger.action.smoke(vec3, Color, name) + + if Duration and Duration>0 then + self:StopSmoke(name, Duration) + end + + return name +end + + +--- Creates a large smoke and fire effect of a specified type and density at this vector. +-- +-- * 1 = small smoke and fire +-- * 2 = medium smoke and fire +-- * 3 = large smoke and fire +-- * 4 = huge smoke and fire +-- * 5 = small smoke +-- * 6 = medium smoke +-- * 7 = large smoke +-- * 8 = huge smoke +-- +-- @param #VECTOR self +-- @param #number Preset Preset of smoke. Default `BIGSMOKEPRESET.LargeSmokeAndFire`. +-- @param #number Density Density between [0,1]. Default 0.5. +-- @param #number Duration (Optional) Duration of the smoke and fire in seconds. +-- @return #string Name of the smoke. Can be used to stop it. +function VECTOR:SmokeAndFire(Preset, Density, Duration) + + Preset=Preset or BIGSMOKEPRESET.LargeSmokeAndFire + Density=Density or 0.5 + + local vec3=self:GetVec3() + + -- Get a name of this smoke & fire object + local name=string.format("Vector-Fire-%d", self.uid) + + trigger.action.effectSmokeBig(vec3, Preset, Density, name) + + if Duration and Duration>0 then + self:StopSmoke(name, Duration) + end + + return name +end + +--- Stop smoke or fire effect. +-- @param #VECTOR self +-- @param #string Name Name of the smoke object. +-- @param #number Delay Delay in seconds before the smoke is stopped. +-- @return #VECTOR self +function VECTOR:StopSmoke(Name, Delay) + + if Delay and Delay>0 then + TIMER:New(VECTOR.StopSmoke, self, Name):Start(Delay) + else + if Name then + trigger.action.effectSmokeStop(Name) + else + env.error(string.format("No name provided in VECTOR.StopSmoke function!")) + end + end + + return self +end + + +--- Creates an illumination bomb at the specified point. +-- @param #VECTOR self +-- @param #number Power The power in Candela (cd). Should be between 1 and 1000000. Default 1000 cd. +-- @param #number Altitude (Optional) Altitude [m] at which the illumination bomb is created. +-- @return #VECTOR self +function VECTOR:IlluminationBomb(Power, Altitude) + + local vec3=self:GetVec3() + + if Altitude then + vec3.y=Altitude + end + + -- Create an illumination bomb. + trigger.action.illuminationBomb(vec3, Power or 1000) + + return self +end + +--- Creates an explosion at a given point at the specified power. +-- @param #VECTOR self +-- @param #number Power The power in kg TNT. Default 100 kg. +-- @return #VECTOR self +function VECTOR:Explosion(Power) + + local vec3=self:GetVec3() + + trigger.action.explosion(vec3, Power or 100) + + return self +end + +--- Creates a signal flare at the given point in the specified color. The flare will be launched in the direction of the azimuth angle. +-- @param #VECTOR self +-- @param #number Color Color of flare. Default Green. +-- @param #number Azimuth Azimuth angle in degrees. Default 0. +-- @return #VECTOR self +function VECTOR:Flare(Color, Azimuth) + + local vec3=self:GetVec3() + + trigger.action.signalFlare(vec3, Color or 0, math.rad(Azimuth or 0)) + + return self +end + + +--- Creates a arrow from this VECTOR to another vector on the F10 map. +-- @param #VECTOR self +-- @param #VECTOR Vector The vector defining the endpoint. +-- @param #number Coalition Coalition Id: -1=All, 0=Neutral, 1=Red, 2=Blue. Default -1. +-- @param #table Color RGB color with alpha {r, g, b, alpha}. Default {1, 0, 0, 0.7}. +-- @param #table FillColor RGB color with alpha {r, g, b, alpha}. Default {1, 0, 0, 0.5}. +-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot Dash, 5=Long Dash, 6=Two Dash. Default 1. +-- @return #number Marker ID. Can be used to remove the drawing. +function VECTOR:ArrowTo(Vector, Coalition, Color, FillColor, LineType) + + local vec3End=self:GetVec3() + local vec3Start=Vector:GetVec3() + + local id=UTILS.GetMarkID() + + Coalition=Coalition or -1 + Color=Color or {1,0,0,0.7} + FillColor=FillColor or {1,0,0,0.5} + LineType=LineType or 1 + + local readOnly=false + + trigger.action.arrowToAll(Coalition , id, vec3Start, vec3End, Color, FillColor , LineType, readOnly, "") + + return id +end + + +--- Create mark on F10 map. +-- @param #VECTOR self +-- @param #string MarkText Free format text that shows the marking clarification. +-- @param #number Recipient Recipient of the mark: -1=All (default), 0=Neutral, 1=Red, 2=Blue. Can also be a `GROUP` object. +-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false. +-- @return #number Mark ID. +function VECTOR:Mark(MarkText, Recipient, ReadOnly) + + Recipient=Recipient or -1 + + if type(Recipient)=="number" then + + local MarkID = UTILS.GetMarkID() + + if Recipient==-1 then + trigger.action.markToAll(MarkID, MarkText, self:GetVec3(), ReadOnly, "") + elseif Recipient==0 then + trigger.action.markToCoalition(MarkID, MarkText, self:GetVec3(), coalition.side.NEUTRAL, ReadOnly, "") + elseif Recipient==1 then + trigger.action.markToCoalition(MarkID, MarkText, self:GetVec3(), coalition.side.RED, ReadOnly, "") + elseif Recipient==2 then + trigger.action.markToCoalition(MarkID, MarkText, self:GetVec3(), coalition.side.BLUE, ReadOnly, "") + end + + return MarkID + + elseif type(Recipient)=="table" then + + local MarkID = UTILS.GetMarkID() + + local group=Recipient --Wrapper.Group#GROUP + + trigger.action.markToGroup( MarkID, MarkText, self:GetVec3(), group:GetID(), ReadOnly, "") + + return MarkID + + end + + return nil +end + + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Private Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Check if passed object is a Vector. +-- @param #table t The object to be tested. +-- @return #boolean Returns `true` if `t` is a #VECTOR and `false` otherwise. +function VECTOR._IsVector(t) + return getmetatable(t) == VECTOR +end + +--- Meta function to add vectors together. +-- @param #VECTOR a Vector a. +-- @param #VECTOR b Vector b. +-- @return #VECTOR Returns a new VECTOR c with c[i]=a[i]+b[i] for i=x,y,z. +function VECTOR.__add(a,b) + + assert(VECTOR._IsVector(a) and VECTOR._IsVector(b), "ERROR in VECTOR.__add: wrong argument types! (expected and )") + + local c=VECTOR:New(a.x+b.x, a.y+b.y, a.z+b.z) + + return c +end + +--- Meta function to subtract vectors. +-- @param #VECTOR a Vector a. +-- @param #VECTOR b Vector b. +-- @return #VECTOR Returns a new VECTOR c with c[i]=a[i]-b[i] for i=x,y,z. +function VECTOR.__sub(a,b) + + assert(VECTOR._IsVector(a) and VECTOR._IsVector(b), "ERROR in VECTOR.__sub: wrong argument types: (expected and )") + + local c=VECTOR:New(a.x-b.x, a.y-b.y, a.z-b.z) + + return c +end + + +--- Meta function to multiplicate vector by another vector or a scalar. +-- @param #VECTOR a Vector a. Can also be a #number. +-- @param #VECTOR b Vector b. Can also be a #number. +-- @return #VECTOR Returns a new VECTOR c with c[i]=a[i]*b[i] for i=x,y,z. +function VECTOR.__mul(a, b) + + local c=nil --#VECTOR + + if type(a)=='number' then + c=VECTOR:New(a*b.x, a*b.y, a*b.z) + elseif type(b)=='number' then + c=VECTOR:New(b*a.x, b*a.y, b*a.z) + else + c=VECTOR:New(a.x*b.x, a.y*b.y, a.z*b.z) + end + + return c +end + +--- Meta function for dividing a vector by a scalar or by another vector. +-- @param #VECTOR a Vector a. +-- @param #VECTOR b Vector b. Can also be a #number. +-- @return #VECTOR Returns a new VECTOR c with c[i]=a[i]/b or c[i]=a[i]/b[i] for i=x,y,z. +function VECTOR.__div(a, b) + + assert(VECTOR._IsVector(a) and (type(b) == "number" or VECTOR._IsVector(b)), "div: wrong argument types (expected and ( or ))") + + local c=nil --#VECTOR + + if type(b) == "number" then + c=VECTOR:New(a.x/b, a.y/b, a.z/b) + else + c=VECTOR:New(a.x/b.x, a.y/b.y, a.z/b.z) + end + + return c +end + +--- Meta function to make vectors negative. +-- @param #VECTOR v Vector v. +function VECTOR.__unm(v) + local c=VECTOR:New(-v.x, -v.y, -v.z) + return c +end + +--- Meta function to check if two vectors are equal. +-- @param #VECTOR a Vector a. +-- @param #VECTOR b Vector b. +-- @return #boolean If `true`, both vectors are equal +function VECTOR.__eq(a, b) + return a.x==b.x and a.y==b.y and a.z==b.z +end + + +--- Meta function to change how vectors appear as string. +-- @param #VECTOR self +-- @return #string String representation of vector. +function VECTOR:__tostring() + local text=string.format("VECTOR: x=%.1f, y=%.1f, z=%.1f |v|=%.1f Phi=%4.1f°", self.x, self.y, self.z, self:GetLength(), self:GetHeading(false)) + return text +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 807f57f98..d5dbaec92 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -3340,7 +3340,6 @@ function WAREHOUSE:FindAssetInDB(group) if aid~=nil then local asset=_WAREHOUSEDB.Assets[aid] - self:T2({asset=asset}) if asset==nil then self:_ErrorMessage(string.format("ERROR: Asset for group %s not found in the data base!", group:GetName()), 0) end @@ -3918,7 +3917,7 @@ end -- @param #string assignment A free to choose string specifying an assignment for the asset. This can be used with the @{#WAREHOUSE.OnAfterNewAsset} function. -- @param #table other (Optional) Table of other useful data. Can be collected via WAREHOUSE.OnAfterNewAsset() function for example function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribute, forcecargobay, forceweight, loadradius, skill, liveries, assignment, other) - self:T({group=group, ngroups=ngroups, forceattribute=forceattribute, forcecargobay=forcecargobay, forceweight=forceweight}) + --self:T({group=group:GetName(), ngroups=ngroups, forceattribute=forceattribute, forcecargobay=forcecargobay, forceweight=forceweight}) -- Set default. local n=ngroups or 1 @@ -4446,7 +4445,6 @@ end -- @param #WAREHOUSE.Queueitem Request Information table of the request. -- @return #boolean If true, request is granted. function WAREHOUSE:onbeforeRequest(From, Event, To, Request) - self:T3({warehouse=self.alias, request=Request}) -- Distance from warehouse to requesting warehouse. local distance=self:GetCoordinate():Get2DDistance(Request.warehouse:GetCoordinate()) @@ -6153,9 +6151,6 @@ function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrol -- Uncontrolled spawning. template.uncontrolled=uncontrolled - -- Debug info. - self:T2({airtemplate=template}) - -- Spawn group. local group=_DATABASE:Spawn(template) --Wrapper.Group#GROUP @@ -8601,6 +8596,8 @@ function WAREHOUSE:_DeleteStockItem(stockitem) local item=self.stock[i] --#WAREHOUSE.Assetitem if item.uid==stockitem.uid then table.remove(self.stock,i) + -- remove also from warehouse DB + _WAREHOUSEDB.Assets[stockitem.uid]=nil break end end diff --git a/Moose Development/Moose/Globals.lua b/Moose Development/Moose/Globals.lua index 771547060..5108254d1 100644 --- a/Moose Development/Moose/Globals.lua +++ b/Moose Development/Moose/Globals.lua @@ -20,6 +20,13 @@ _DATABASE:_RegisterCargos() _DATABASE:_RegisterZones() _DATABASE:_RegisterAirbases() +--- Function that writes to DCS log file +-- @param #string text Formatted text. +-- @param ... Format passed to string.format(). +function printf(text, ...) + env.info(string.format(text, ...)) +end + --- Check if os etc is available. BASE:I("Checking de-sanitization of os, io and lfs:") local __na = false diff --git a/Moose Development/Moose/Modules.lua b/Moose Development/Moose/Modules.lua index 37c6ac45c..13f30e38f 100644 --- a/Moose Development/Moose/Modules.lua +++ b/Moose Development/Moose/Modules.lua @@ -32,6 +32,7 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/MarkerOps_Base.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/TextAndSound.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/Pathline.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/ClientMenu.lua') +__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/Vector.lua') __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Object.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Identifiable.lua' ) @@ -186,4 +187,9 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Cargo_Dispatcher __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Capture_Zone.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Capture_Dispatcher.lua' ) +__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Point.lua' ) +__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Beacons.lua' ) +__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Radios.lua' ) +__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Towns.lua' ) + __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Globals.lua' ) diff --git a/Moose Development/Moose/Modules_local.lua b/Moose Development/Moose/Modules_local.lua index 5a6b6c0e9..356464f50 100644 --- a/Moose Development/Moose/Modules_local.lua +++ b/Moose Development/Moose/Modules_local.lua @@ -32,6 +32,7 @@ __Moose.Include( 'Core\\MarkerOps_Base.lua' ) __Moose.Include( 'Core\\TextAndSound.lua' ) __Moose.Include( 'Core\\Condition.lua' ) __Moose.Include( 'Core\\ClientMenu.lua' ) +__Moose.Include( 'Core\\Vector.lua' ) __Moose.Include( 'Wrapper\\Object.lua' ) __Moose.Include( 'Wrapper\\Identifiable.lua' ) @@ -177,4 +178,9 @@ __Moose.Include( 'Tasking\\Task_Cargo_Dispatcher.lua' ) __Moose.Include( 'Tasking\\Task_Capture_Zone.lua' ) __Moose.Include( 'Tasking\\Task_Capture_Dispatcher.lua' ) +__Moose.Include( 'Navigation\\Point.lua' ) +__Moose.Include( 'Navigation\\Beacons.lua' ) +__Moose.Include( 'Navigation\\Radios.lua' ) +__Moose.Include( 'Navigation\\Towns.lua' ) + __Moose.Include( 'Globals.lua' ) diff --git a/Moose Development/Moose/Navigation/Beacons.lua b/Moose Development/Moose/Navigation/Beacons.lua new file mode 100644 index 000000000..9c7acba8b --- /dev/null +++ b/Moose Development/Moose/Navigation/Beacons.lua @@ -0,0 +1,422 @@ +--- **NAVIGATION** - Beacons of the map/theatre. +-- +-- **Main Features:** +-- +-- * Access beacons of the map +-- * Find closest beacon +-- * Get frequencies and channels +-- +-- === +-- +-- ## Example Missions: +-- +-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Navigation%20-%20Beacons). +-- +-- === +-- +-- ### Author: **funkyfranky** +-- +-- === +-- @module Navigation.Beacons +-- @image NAVIGATION_Beacons.png + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- BEACONS class. +-- @type BEACONS +-- +-- @field #string ClassName Name of the class. +-- @field #number verbose Verbosity of output. +-- @field #table beacons Beacons. +-- +-- @extends Core.Base#BASE + +--- *Hope is the beacon that guides lost ships back to the shore.* +-- +-- === +-- +-- # The BEACONS Concept +-- +-- This class is desinged to make information about beacons of a map/theatre easier accessible. The information contains location, type and frequencies of all or specific beacons of the map. +-- +-- **Note** that try to avoid hard coding stuff in Moose since DCS is updated frequently and things change. Therefore, the main source of information is either a file `beacons.lua` that can be +-- found in the installation directory of DCS for each map or a table that the user needs to provide. +-- +-- # Basic Setup +-- +-- A new `BEACONS` object can be created with the @{#BEACONS.NewFromFile}(*beacons_lua_file*) function. +-- +-- local beacons=BEACONS:NewFromFile("\Mods\terrains\\beacons.lua") +-- beacons:MarkerShow() +-- +-- This will load the beacons from the `` for the specific map and place markers on the F10 map. This is the first step you should do to ensure that the file +-- you provided is correct and all relevant beacons are present. +-- +-- # User Functions +-- +-- ## F10 Map Markers +-- +-- ## Position +-- +-- ## Get Closest Beacon +-- +-- +-- @field #BEACONS +BEACONS = { + ClassName = "BEACONS", + verbose = 1, + beacons = {}, +} + +--- Mission capability. +-- @type BEACONS.Beacon +-- @field #function display_name Function that returns the localized name. +-- @field #number type Beacon type. +-- @field #string beaconId Beacon ID. +-- @field #string callsign Call sign. +-- @field #number frequency Frequency in Hz. +-- @field #number channel TACAN, RSBN or PRMG channel depending on type. +-- @field #table position Position table. +-- @field #number direction Direction in degrees. +-- @field #table positionGeo Table with latitude and longitude. +-- @field #table sceneObjects Table with scenery objects, e.g. `{t:393396742}`. +-- @field #number chartOffsetX No idea what this offset is?! +-- @field DCS#Vec3 vec3 Position vector 3D. +-- @field #number markerID ID for the F10 marker. +-- @field #string typeName Name of becon type. +-- @field Wrapper.Scenery#SCENERY scenery The scenery object. + +--- BEACONS class version. +-- @field #string version +BEACONS.version="0.1.0" + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- ToDo list +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +-- TODO: A lot... +-- DONE: TACAN channel from frequency (was already in beacon.lua as channel) +-- DONE: Scenery object + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Constructor(s) +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Create a new BECAONS class instance from a given table. +-- @param #BEACONS self +-- @param #table BeaconTable Table with beacon info. +-- @return #BEACONS self +function BEACONS:NewFromTable(BeaconTable) + + -- Inherit everything from BASE class. + self=BASE:Inherit(self, BASE:New()) -- #BEACONS + + for _,_beacon in pairs(BeaconTable) do + local beacon=_beacon --#BEACONS.Beacon + + -- Get 3D vector + beacon.vec3={x=beacon.position[1], y=beacon.position[2], z=beacon.position[3]} + + -- Get coordinate + beacon.coordinate=COORDINATE:NewFromVec3(beacon.vec3) + + -- Get type name + beacon.typeName=self:_GetTypeName(beacon.type) + + -- Find closest scenery object from scan + beacon.scenery=beacon.coordinate:FindClosestScenery(20) + + -- Debug stuff for scenery object + if false then + if beacon.scenery then + env.info(string.format("FF Beacon %s %s %s got scenery object %s, %s", beacon.callsign, beacon.beaconId, beacon.typeName, beacon.scenery:GetName(), beacon.scenery:GetTypeName() )) + UTILS.PrintTableToLog(beacon.scenery.SceneryObject) + UTILS.PrintTableToLog(beacon.sceneObjects) + else + env.info(string.format("FF NO scenery object %s %s %s ", beacon.callsign, beacon.beaconId, beacon.typeName)) + end + end + + -- Add to table + table.insert(self.beacons, beacon) + end + + -- Debug output + self:I(string.format("Added %d beacons", #self.beacons)) + + if self.verbose > 0 then + local text="Beacon types:" + for typeName,typeID in pairs(BEACON.Type) do + local n=self:CountBeacons(typeID) + text=text..string.format("\n%s = %d", typeName, n) + end + self:I(text) + end + + return self +end + + +--- Create a new BECAONS class instance from a given file. +-- @param #BEACONS self +-- @param #string FileName Full path to the file containing the map beacons. +-- @return #BEACONS self +function BEACONS:NewFromFile(FileName) + + -- Inherit everything from BASE class. + self=BASE:Inherit(self, BASE:New()) -- #BEACONS + + local exists=UTILS.FileExists(FileName) + + if exists==false then + self:E(string.format("ERROR: file with beacon info does not exist!")) + return nil + end + + -- This will create a global table `beacons` + dofile(FileName) + + -- Get beacons from table. + self=self:NewFromTable(beacons) + + return self +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- User Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Get 3D position vector of a specific beacon. +-- @param #BEACONS self +-- @param #BEACONS.Beacon beacon The beacon data structure. +-- @return DCS#Vec3 Position vector. +function BEACONS:GetVec3(beacon) + return beacon.vec3 +end + +--- Get COORDINATE of a specific beacon. +-- @param #BEACONS self +-- @param #BEACONS.Beacon beacon The beacon data structure. +-- @return Core.Point#COORDINATE The coordinate. +function BEACONS:GetCoordinate(beacon) + local coordinate=COORDINATE:NewFromVec3(beacon.vec3) + return coordinate +end + +--- Find closest beacon to a given coordinate. +-- @param #BEACONS self +-- @param Core.Point#COORDINATE Coordinate The reference coordinate. +-- @param #number TypeID (Optional) Only search for specific beacon types, *e.g.* `BEACON.Type.TACAN`. +-- @param #number DistMax (Optional) Max search distance in meters. +-- @param #table ExcludeList (Optional) List of beacons to exclude. +-- @return #BEACONS.Beacon The closest beacon. +function BEACONS:GetClosestBeacon(Coordinate, TypeID, DistMax, ExcludeList) + + local beacon=nil --#BEACONS.Beacon + local distmin=math.huge + + ExcludeList=ExcludeList or {} + + for _,_beacon in pairs(self.beacons) do + local bc=_beacon --#BEACONS.Beacon + + if (TypeID==nil or TypeID==bc.type) and (not UTILS.IsInTable(ExcludeList, bc, "beaconId")) then + + local dist=Coordinate:Get2DDistance(bc.vec3) + + if dist=1e6 then + freq=freq/1e6 + unit="MHz" + elseif freq>=1e3 then + freq=freq/1e3 + unit="kHz" + end + + return freq, unit +end + +--- Get name of beacon type. +-- @param #BEACONS self +-- @param #number typeID Beacon type number. +-- @return #string Type name. +function BEACONS:_GetTypeName(typeID) + + if typeID~=nil then + for typeName,_typeID in pairs(BEACON.Type) do + if _typeID==typeID then + return typeName + end + end + end + + return "Unknown" +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Navigation/Point.lua b/Moose Development/Moose/Navigation/Point.lua new file mode 100644 index 000000000..2c8d1e378 --- /dev/null +++ b/Moose Development/Moose/Navigation/Point.lua @@ -0,0 +1,587 @@ +--- **NAVIGATION** - Navigation Airspace Points, Fixes and Aids. +-- +-- **Main Features:** +-- +-- * Navigation Fixes +-- * Navigation Aids +-- +-- === +-- +-- ## Example Missions: +-- +-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Navigation%20-%20NavFix). +-- +-- === +-- +-- ### Author: **funkyfranky** +-- +-- === +-- @module Navigation.Point +-- @image NAVIGATION_Point.png + + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- NAVFIX class. +-- @type NAVFIX +-- +-- @field #string ClassName Name of the class. +-- @field #number verbose Verbosity of output. +-- @field #string name Name of the point. +-- @field #string typePoint Type of the point, *e.g. "Intersection", "VOR", "Airport". +-- @field Core.Vector#VECTOR vector Position vector of the fix. +-- @field Wrapper.Marker#MARKER marker Marker on F10 map. +-- @field #number altMin Minimum altitude in meters. +-- @field #number altMax Maximum altitude in meters. +-- @field #number speedMin Minimum speed in knots. +-- @field #number speedMax Maximum speed in knots. +-- +-- @field #boolean isCompulsory Is this a compulsory fix. +-- @field #boolean isFlyover Is this a flyover fix (`true`) or turning point otherwise. +-- @field #boolean isFAF Is this a final approach fix. +-- @field #boolean isIAF Is this an initial approach fix. +-- @field #boolean isIF Is this an initial fix. +-- @field #boolean isMAF Is this an initial fix. +-- +-- @extends Core.Base#BASE + +--- *A fleet of British ships at war are the best negotiators.* -- Horatio Nelson +-- +-- === +-- +-- # The NAVFIX Concept +-- +-- The NAVFIX class has a great concept! +-- +-- A NAVFIX describes a geo position and can, *e.g.*, be part of a FLIGHTPLAN. It has a unique name and is of a certain type, *e.g.* "Intersection", "VOR", "Airbase" etc. +-- It can also have further properties as min/max altitudes and speeds that aircraft need to obey when they pass the point. +-- +-- # Basic Setup +-- +-- A new `NAVFIX` object can be created with the @{#NAVFIX.New}() function. +-- +-- myNavPoint=NAVFIX:New() +-- myTemplate:SetXYZ(X, Y, Z) +-- +-- This is how it works. +-- +-- @field #NAVFIX +NAVFIX = { + ClassName = "NAVFIX", + verbose = 0, +} + +--- Type of point. +-- @type NAVFIX.Type +-- @field #string POINT Waypoint. +-- @field #string INTERSECTION Intersection of airway. +-- @field #string AIRPORT Airport. +-- @field #string VOR Very High Frequency Omnidirectional Range Station. +-- @field #string DME Distance Measuring Equipment. +-- @field #string NDB Non-Directional Beacon. +-- @field #string VORDME Combined VHF omnidirectional range (VOR) with a distance-measuring equipment (DME). +-- @field #string LOC Localizer. +-- @field #string ILS Instrument Landing System. +-- @field #string TACAN TACtical Air Navigation System (TACAN). +NAVFIX.Type={ + POINT="Point", + INTERSECTION="Intersection", + AIRPORT="Airport", + NDB="NDB", + VOR="VOR", + DME="DME", + VORDME="VOR/DME", + LOC="Localizer", + ILS="ILS", + TACAN="TACAN" +} + +--- NAVFIX class version. +-- @field #string version +NAVFIX.version="0.1.0" + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- ToDo list +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +-- TODO: A lot... + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Constructor(s) +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Create a new NAVFIX class instance from a given VECTOR. +-- @param #NAVFIX self +-- @param #string Name Name/ident of the point. Should be unique! +-- @param #string Type Type of the point. Default `NAVFIX.Type.POINT`. +-- @param Core.Vector#VECTOR Vector Position vector of the navpoint. +-- @return #NAVFIX self +function NAVFIX:NewFromVector(Name, Type, Vector) + + -- Inherit everything from BASE class. + self=BASE:Inherit(self, BASE:New()) -- #NAVFIX + + -- Vector of point. + self.vector=Vector + + -- Name of point. + self.name=Name + + -- Type of the point. + self.typePoint=Type or NAVFIX.Type.POINT + + local coord=COORDINATE:NewFromVec3(self.vector) + + -- Marker on F10. + self.marker=MARKER:New(coord, self:_GetMarkerText()) + + -- Log ID string. + self.lid=string.format("NAVFIX %s [%s] | ", tostring(self.name), tostring(self.typePoint)) + + -- Debug info. + self:I(self.lid..string.format("Created NAVFIX")) + + return self +end + + +--- Create a new NAVFIX class instance from a given COORDINATE. +-- @param #NAVFIX self +-- @param #string Name Name of the fix. Should be unique! +-- @param #string Type Type of the point. Default `NAVFIX.Type.POINT`. +-- @param Core.Point#COORDINATE Coordinate Coordinate of the point. +-- @return #NAVFIX self +function NAVFIX:NewFromCoordinate(Name, Type, Coordinate) + + -- Create a VECTOR from the coordinate. + local Vector=VECTOR:NewFromVec(Coordinate) + + -- Create NAVFIX. + self=NAVFIX:NewFromVector(Name, Type, Vector) + + return self +end + + +--- Create a new NAVFIX instance from given latitude and longitude in degrees, minutes and seconds (DMS). +-- @param #NAVFIX self +-- @param #string Name Name of the fix. Should be unique! +-- @param #string Type Type of the point. Default `NAVFIX.Type.POINT`. +-- @param #string Latitude Latitude in DMS as string. +-- @param #string Longitude Longitude in DMS as string. +-- @return #NAVFIX self +function NAVFIX:NewFromLLDMS(Name, Type, Latitude, Longitude) + + -- Create a VECTOR from the coordinate. + local Vector=VECTOR:NewFromLLDMS(Latitude, Longitude) + + -- Create NAVFIX. + self=NAVFIX:NewFromVector(Name, Type, Vector) + + return self +end + +--- Create a new NAVFIX instance from given latitude and longitude in decimal degrees (DD). +-- @param #NAVFIX self +-- @param #string Name Name of the fix. Should be unique! +-- @param #string Type Type of the point. Default `NAVFIX.Type.POINT`. +-- @param #number Latitude Latitude in DD. +-- @param #number Longitude Longitude in DD. +-- @return #NAVFIX self +function NAVFIX:NewFromLLDD(Name, Type, Latitude, Longitude) + + -- Create a VECTOR from the coordinate. + local Vector=VECTOR:NewFromLLDD(Latitude, Longitude) + + -- Create NAVFIX. + self=NAVFIX:NewFromVector(Name, Type, Vector) + + return self +end + + +--- Create a new NAVFIX class instance relative to a given other NAVFIX. +-- You have to specify the distance and bearing from the new point to the given point. *E.g.*, for a distance of 5 NM and a bearing of 090° (West), the +-- new nav point is created 5 NM East of the given nav point. The reason is that this corresponts to convention used in most maps. +-- You can, however, use the `Reciprocal` switch to create the new point in the direction you specify. +-- @param #NAVFIX self +-- @param #string Name Name of the fix. Should be unique! +-- @param #string Type Type of navfix. +-- @param #NAVFIX NavFix The given/existing navigation fix relative to which the new fix is created. +-- @param #number Distance Distance from the given to the new point in nautical miles. +-- @param #number Bearing Bearing [Deg] from the new point to the given one. +-- @param #boolean Reciprocal If `true` the reciprocal `Bearing` is taken so it specifies the direction from the given point to the new one. +-- @return #NAVFIX self +function NAVFIX:NewFromNavFix(Name, Type, NavFix, Distance, Bearing, Reciprocal) + + -- Convert magnetic to true bearing by adding magnetic declination, e.g. mag. bearing 10°M ==> true bearing 16°M (for 6° variation on Caucasus map) + Bearing=Bearing+UTILS.GetMagneticDeclination() + + if Reciprocal then + Bearing=Bearing-180 + end + + -- Translate. + local Vector=NavFix.vector:Translate(UTILS.NMToMeters(Distance), Bearing, true) + + self=NAVFIX:NewFromVector(Name, Type, Vector) + + return self +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- User Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Set whether this is the intermediate fix (IF). +-- @param #NAVFIX self +-- @return #NAVFIX self +function NAVFIX:SetIntermediateFix(IntermediateFix) + self.isIF=IntermediateFix + return self +end + +--- Set whether this is an initial approach fix (IAF). +-- The IAF is the point where the initial approach segment of an instrument approach begins. +-- It is usually a designated intersection, VHF omidirectional range (VOR) non-directional beacon (NDB) +-- or distance measuring equipment (DME) fix. +-- The IAF may be collocated with the intermediate fix (IF) of the instrument apprach an in such case they designate the +-- beginning of the intermediate segment of the approach. When the IAF and the IF are combined, there is no inital approach segment. +-- @param #NAVFIX self +-- @param #boolean IntermediateFix If `true`, this is an intermediate fix. +-- @return #NAVFIX self +function NAVFIX:SetInitialApproachFix(IntermediateFix) + self.isIAF=IntermediateFix + return self +end + + +--- Set whether this is the final approach fix (FAF). +-- @param #NAVFIX self +-- @param #boolean FinalApproachFix If `true`, this is a final approach fix. +-- @return #NAVFIX self +function NAVFIX:SetFinalApproachFix(FinalApproachFix) + self.isFAF=FinalApproachFix + return self +end + +--- Set whether this is the final approach fix (FAF). +-- @param #NAVFIX self +-- @param #boolean FinalApproachFix If `true`, this is a final approach fix. +-- @return #NAVFIX self +function NAVFIX:SetMissedApproachFix(MissedApproachFix) + self.isMAF=MissedApproachFix + return self +end + + +--- Set minimum altitude. +-- @param #NAVFIX self +-- @param #number Altitude Min altitude in feet. +-- @return #NAVFIX self +function NAVFIX:SetAltMin(Altitude) + + self.altMin=Altitude + + return self +end + +--- Set maximum altitude. +-- @param #NAVFIX self +-- @param #number Altitude Max altitude in feet. +-- @return #NAVFIX self +function NAVFIX:SetAltMax(Altitude) + + self.altMax=Altitude + + return self +end + +--- Set mandatory altitude (min alt = max alt). +-- @param #NAVFIX self +-- @param #number Altitude Altitude in feet. +-- @return #NAVFIX self +function NAVFIX:SetAltMandatory(Altitude) + + self.altMin=Altitude + self.altMax=Altitude + + return self +end + +--- Set minimum allowed speed at this fix. +-- @param #NAVFIX self +-- @param #number Speed Min speed in knots. +-- @return #NAVFIX self +function NAVFIX:SetSpeedMin(Speed) + + self.speedMin=Speed + + return self +end + +--- Set maximum allowed speed at this fix. +-- @param #NAVFIX self +-- @param #number Speed Max speed in knots. +-- @return #NAVFIX self +function NAVFIX:SetSpeedMax(Speed) + + self.speedMax=Speed + + return self +end + +--- Set mandatory speed (min speed = max speed) at this fix. +-- @param #NAVFIX self +-- @param #number Speed Mandatory speed in knots. +-- @return #NAVFIX self +function NAVFIX:SetSpeedMandatory(Speed) + + self.speedMin=Speed + self.speedMax=Speed + + return self +end + + +--- Set whether this fix is compulsory. +-- @param #NAVFIX self +-- @param #boolean Compulsory If `true`, this is a compusory fix. If `false` or nil, it is non-compulsory. +-- @return #NAVFIX self +function NAVFIX:SetCompulsory(Compulsory) + self.isCompulsory=Compulsory + return self +end + +--- Set whether this is a fly-over fix fix. +-- @param #NAVFIX self +-- @param #boolean FlyOver If `true`, this is a fly over fix. If `false` or nil, it is not. +-- @return #NAVFIX self +function NAVFIX:SetFlyOver(FlyOver) + self.isFlyover=FlyOver + return self +end + + +--- Get the altitude in feet MSL. If min and max altitudes are set, it will return a random altitude between min and max. +-- @param #NAVFIX self +-- @return #number Altitude in feet MSL. Can be `nil`, if neither min nor max altitudes have beeen set. +function NAVFIX:GetAltitude() + + local alt=nil + if self.altMin and self.altMax and self.altMin~=self.altMax then + alt=math.random(self.altMin, self.altMax) + elseif self.altMin then + alt=self.altMin + elseif self.altMax then + alt=self.altMax + end + + return alt +end + + +--- Get the speed. If min and max speeds are set, it will return a random speed between min and max. +-- @param #NAVFIX self +-- @return #number Speed in knots. Can be `nil`, if neither min nor max speeds have beeen set. +function NAVFIX:GetSpeed() + + local speed=nil + if self.speedMin and self.speedMax and self.speedMin~=self.speedMax then + speed=math.random(self.speedMin, self.speedMax) + elseif self.speedMin then + speed=self.speedMin + elseif self.speedMax then + speed=self.speedMax + end + + return speed +end + + + +--- Add marker the NAVFIX on the F10 map. +-- @param #NAVFIX self +-- @return #NAVFIX self +function NAVFIX:MarkerShow() + + self.marker:ToAll() + + return self +end + +--- Remove marker of the NAVFIX from the F10 map. +-- @param #NAVFIX self +-- @return #NAVFIX self +function NAVFIX:MarkerRemove() + + self.marker:Remove() + + return self +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Private Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Get text displayed in the F10 marker. +-- @param #NAVFIX self +-- @return #string Marker text. +function NAVFIX:_GetMarkerText() + + local altmin=self.altMin and tostring(self.altMin) or "" + local altmax=self.altMax and tostring(self.altMax) or "" + local speedmin=self.speedMin and tostring(self.speedMin) or "" + local speedmax=self.speedMax and tostring(self.speedMax) or "" + + + local text=string.format("NAVFIX %s", self.name) + if self.isIAF then + text=text..string.format(" (IAF)") + end + if self.isIF then + text=text..string.format(" (IF)") + end + text=text..string.format("\nAltitude [ft]: %s - %s", altmin, altmax) + text=text..string.format("\nSpeed [knots]: %s - %s", speedmin, speedmax) + text=text..string.format("\nCompulsory: %s", tostring(self.isCompulsory)) + text=text..string.format("\nFly Over: %s", tostring(self.isFlyover)) + + return text +end + + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- NAVAID class. +-- @type NAVAID +-- @field #string ClassName Name of the class. +-- @field #number verbose Verbosity of output. +-- @extends Navigation.Point#NAVFIX + +--- *A fleet of British ships at war are the best negotiators.* -- Horatio Nelson +-- +-- === +-- +-- # The NAVAID Concept +-- +-- A NAVAID consists of one or multiple FLOTILLAs. These flotillas "live" in a WAREHOUSE that has a phyiscal struction (STATIC or UNIT) and can be captured or destroyed. +-- +-- # Basic Setup +-- +-- A new `NAVAID` object can be created with the @{#NAVAID.New}(`WarehouseName`, `FleetName`) function, where `WarehouseName` is the name of the static or unit object hosting the fleet +-- and `FleetName` is the name you want to give the fleet. This must be *unique*! +-- +-- myFleet=NAVAID:New("myWarehouseName", "1st Fleet") +-- myFleet:SetPortZone(ZonePort1stFleet) +-- myFleet:Start() +-- +-- A fleet needs a *port zone*, which is set via the @{#NAVAID.SetPortZone}(`PortZone`) function. This is the zone where the naval assets are spawned and return to. +-- +-- Finally, the fleet needs to be started using the @{#NAVAID.Start}() function. If the fleet is not started, it will not process any requests. +-- +-- @field #NAVAID +NAVAID = { + ClassName = "NAVAID", + verbose = 0, +} + +--- NAVAID class version. +-- @field #string version +NAVAID.version="0.1.0" + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- ToDo list +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +-- TODO: Add frequencies. Which unit MHz, kHz, Hz? +-- TODO: Add radial function + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Constructor +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Create a new NAVAID class instance. +-- @param #NAVAID self +-- @param #string Name Name/ident of this navaid. +-- @param #string Type Type of the point. Default `NAVFIX.Type.POINT`. +-- @param #string ZoneName Name of the zone to scan the scenery. +-- @param #string SceneryName Name of the scenery object. +-- @return #NAVAID self +function NAVAID:NewFromScenery(Name, Type, ZoneName, SceneryName) + + -- Get the zone. + local zone=ZONE:FindByName(ZoneName) + + -- Get coordinate. + local Coordinate=zone:GetCoordinate() + + -- Inherit everything from NAVFIX class. + self=BASE:Inherit(self, NAVFIX:NewFromCoordinate(Name, Type, Coordinate)) -- #NAVAID + + -- Set zone. + self.zone=ZONE:FindByName(ZoneName) + + -- Try to get the scenery object. Note not all can be found unfortunately. + if SceneryName then + self.scenery=SCENERY:FindByNameInZone(SceneryName, ZoneName) + if not self.scenery then + self:E(string.format("ERROR: Could not find scenery object %s in zone %s", SceneryName, ZoneName)) + end + end + + -- Alias. + self.alias=string.format("%s %s %s", tostring(ZoneName), tostring(SceneryName), tostring(Type)) + + -- Set some string id for output to DCS.log file. + self.lid=string.format("NAVAID %s | ", self.alias) + + -- Debug info. + self:I(self.lid..string.format("Created NAVAID!")) + + return self +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- User Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Set frequency the beacon transmits on. +-- @param #NAVAID self +-- @param #number Frequency Frequency in Hz. +-- @return #NAVAID self +function NAVAID:SetFrequency(Frequency) + + self.frequency=Frequency + + return self +end + +--- Set channel of, *e.g.*, TACAN beacons. +-- @param #NAVAID self +-- @param #number Channel The channel. +-- @param #string Band The band either `"X"` (default) or `"Y"`. +-- @return #NAVAID self +function NAVAID:SetChannel(Channel, Band) + + self.channel=Channel + self.band=Band or "X" + + return self +end + + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Private Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +-- Add private CLASS functions here. +-- No private NAVAID functions yet. + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Navigation/Radios.lua b/Moose Development/Moose/Navigation/Radios.lua new file mode 100644 index 000000000..910235109 --- /dev/null +++ b/Moose Development/Moose/Navigation/Radios.lua @@ -0,0 +1,422 @@ +--- **NAVIGATION** - Airbase radios. +-- +-- **Main Features:** +-- +-- * Get radio frequencies of airbases +-- * Find closest airbase radios +-- * Mark radio frequencies on F10 map +-- +-- === +-- +-- ## Example Missions: +-- +-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Navigation%20-%20Radios). +-- +-- === +-- +-- ### Author: **funkyfranky** +-- +-- === +-- @module Navigation.Radios +-- @image NAVIGATION_Radios.png + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- RADIOS class. +-- @type RADIOS +-- +-- @field #string ClassName Name of the class. +-- @field #number verbose Verbosity of output. +-- @field #table radios Radios. +-- +-- @extends Core.Base#BASE + +--- *It's not true I had nothing on, I had the radio on.* -- *Marilyn Monroe* +-- +-- === +-- +-- # The RADIOS Concept +-- +-- This class is desinged to make information about radios of a map/theatre easier accessible. The information contains mostly the frequencies of airbases of the map. +-- +-- **Note** that try to avoid hard coding stuff in Moose since DCS is updated frequently and things change. Therefore, the main source of information is either a file `radio.lua` that can be +-- found in the installation directory of DCS for each map or a table that the user needs to provide. +-- +-- # Basic Setup +-- +-- A new `RADIOS` object can be created with the @{#RADIOS.NewFromFile}(*radio_lua_file*) function. +-- +-- local radios=RADIOS:NewFromFile("\Mods\terrains\\radios.lua") +-- radios:MarkerShow() +-- +-- This will load the radios from the `` for the specific map and place markers on the F10 map. This is the first step you should do to ensure that the file +-- you provided is correct and all relevant radios are present. +-- +-- # User Functions +-- +-- ## F10 Map Markers +-- +-- ## Position +-- +-- ## Closest Radio +-- +-- +-- @field #RADIOS +RADIOS = { + ClassName = "RADIOS", + verbose = 0, + radios = {}, +} + +--- Radio item data structure. +-- @type RADIOS.Radio +-- @field #string radioId Radio ID. +-- @field #table role Roles of the radio (usually {"ground", "tower", "approach"}). +-- @field #table callsign Callsigns of the radio (usually the airbase name). +-- @field #table frequency Frequencies of the radios. +-- @field #table position Position table. +-- @field #table sceneObjects Scenery objects. +-- @field #string name Name of the airbase. +-- @field Wrapper.Airbase#AIRBASE airbase Airbase. +-- @field Core.Point#COORDINATE coordinate The COORDINATE of the radio. +-- @field DCS#Vec3 vec3 3D vector. +-- @field #number markerID Marker ID. + +--- Radio item data structure. +-- @type RADIOS.Frequency +-- @field #number modu Modulation type. +-- @field #number freq Frequency in Hz. + + +--- RADIOS class version. +-- @field #string version +RADIOS.version="0.1.0" + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- ToDo list +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +-- TODO: A lot... + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Constructor(s) +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Create a new RADIOS class instance from a given table. +-- @param #RADIOS self +-- @param #table RadioTable Table with radios info. +-- @return #RADIOS self +function RADIOS:NewFromTable(RadioTable) + + -- Inherit everything from BASE class. + self=BASE:Inherit(self, BASE:New()) -- #RADIOS + + --local airbasenames=AIRBASE.GetAllAirbaseNames() + + -- Get all airdromes + local airdromes=AIRBASE.GetAllAirbases(nil, Airbase.Category.AIRDROME) + + for _,_radio in pairs(RadioTable) do + local radio=_radio --#RADIOS.Radio + + -- The table structure of callsign is a bit awkward. We need to get the airbase name. + -- Note that unfortunately, the callsign does not always correspond to the airbase name. + if false then + local cs=radio.callsign[1] + if cs and cs.common then + radio.name=cs.common[1] + elseif cs and cs.nato then + radio.name=cs.nato[1] + else + radio.name="Unknown" + end + radio.name=self:_GetAirbaseName(airbasenames, radio.name) + radio.airbase=AIRBASE:FindByName(radio.name) + end + + -- Each radio item has a key radioId = 'airfield106_0', where 106 is the UID of the airbase. + -- So we can use that to get the airbase. + local aid = tonumber(string.match(radio.radioId, "airfield(%d+)_")) + + -- Get airbase + radio.airbase=self:_GetAirbaseByID(airdromes, aid) + + -- Set other stuff + if radio.airbase then + radio.coordinate=radio.airbase:GetCoordinate() + radio.vec3=radio.airbase:GetVec3() + radio.name=radio.airbase:GetName() + end + + -- Add to table + table.insert(self.radios, radio) + end + + -- Debug output + self:I(string.format("Added %d radios", #self.radios)) + + return self +end + + +--- Create a new RADIOS class instance from a given file. +-- @param #RADIOS self +-- @param #string FileName Full path to the file containing the map radios. +-- @return #RADIOS self +function RADIOS:NewFromFile(FileName) + + -- Inherit everything from BASE class. + self=BASE:Inherit(self, BASE:New()) -- #RADIOS + + local exists=UTILS.FileExists(FileName) + + if exists==false then + self:E(string.format("ERROR: file with radios info does not exist! File=%s", tostring(FileName))) + return nil + end + + -- Backup DCS radio table + local radiobak=UTILS.DeepCopy(radio) + + -- This will create a global table `radio` + dofile(FileName) + + -- Get radios from table. + self=self:NewFromTable(radio) + + -- Restore DCS radio table + radio=UTILS.DeepCopy(radiobak) + + return self +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- User Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Get 3D position vector of a specific radio. +-- @param #RADIOS self +-- @param #RADIOS.Radio radio The radio data structure. +-- @return DCS#Vec3 Position vector. +function RADIOS:GetVec3(radio) + return radio.vec3 +end + +--- Get COORDINATE of a specific radio. +-- @param #RADIOS self +-- @param #RADIOS.Radio radio The radio data structure. +-- @return Core.Point#COORDINATE The coordinate. +function RADIOS:GetCoordinate(radio) + return radio.coordinate +end + + +--- Find closest radio to a given coordinate. +-- @param #RADIOS self +-- @param Core.Point#COORDINATE Coordinate The reference coordinate. +-- @param #number DistMax (Optional) Max search distance in meters. +-- @param #table ExcludeList (Optional) List of radios to exclude. +-- @return #RADIOS.Radio The closest radio. +function RADIOS:GetClosestRadio(Coordinate, DistMax, ExcludeList) + + local radio=nil --#RADIOS.Radio + local distmin=math.huge + + ExcludeList=ExcludeList or {} + + for _,_radio in pairs(self.radios) do + local ra=_radio --#RADIOS.Radio + + if (not UTILS.IsInTable(ExcludeList, ra, "radioId")) then + + local dist=Coordinate:Get2DDistance(ra.coordinate) + + if dist=1e6 then + freq=freq/1e6 + unit="MHz" + elseif freq>=1e3 then + freq=freq/1e3 + unit="kHz" + end + + return freq, unit +end + +--- Get name of frequency band. +-- @param #RADIOS self +-- @param #number BandNumber Band as number. +-- @return #string Band name. +function RADIOS:_GetBandName(BandNumber) + + if BandNumber~=nil then + for bandName,bandNumber in pairs(ENUMS.FrequencyBand) do + if bandNumber==BandNumber then + return bandName + end + end + end + + return "Unknown" +end + +--- Get name of frequency band. +-- @param #RADIOS self +-- @param #table airbasenames Names of all airbases. +-- @param #string name Name of airbase. +-- @return #string Name of airbase +function RADIOS:_GetAirbaseName(airbasenames, name) + + local airbase=AIRBASE:FindByName(name) + + if airbase then + return name + else + for _,airbasename in pairs(airbasenames) do + if string.find(airbasename, name) then + return airbasename + end + end + end + + return "Unknown" +end + +--- Get name of frequency band. +-- @param #RADIOS self +-- @param #table airbases Table of airbases. +-- @param #number aid Airbase ID. +-- @return Wrapper.Airbase#AIRBASE Airbase matching the ID or nil. +function RADIOS:_GetAirbaseByID(airbases, aid) + + for _,_airbase in pairs(airbases) do + local airbase=_airbase --Wrapper.Airbase#AIRBASE + local id=airbase:GetID(true) + if id==aid then + return airbase + end + end + + return nil +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Navigation/Towns.lua b/Moose Development/Moose/Navigation/Towns.lua new file mode 100644 index 000000000..c5f67efbd --- /dev/null +++ b/Moose Development/Moose/Navigation/Towns.lua @@ -0,0 +1,331 @@ +--- **NAVIGATION** - Towns of the map/theatre. +-- +-- **Main Features:** +-- +-- * Find towns of map +-- * Road and rail connections +-- * Find closest town to a given coordinate +-- +-- === +-- +-- ## Example Missions: +-- +-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Navigation%20-%20Towns). +-- +-- === +-- +-- ### Author: **funkyfranky** +-- +-- === +-- @module Navigation.Towns +-- @image NAVIGATION_Towns.png + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- TOWNS class. +-- @type TOWNS +-- +-- @field #string ClassName Name of the class. +-- @field #number verbose Verbosity of output. +-- @field #table towns Towns. +-- +-- @extends Core.Base#BASE + +--- *Hope is the beacon that guides lost ships back to the shore.* +-- +-- === +-- +-- # The TOWNS Concept +-- +-- This class is desinged to make information about towns of a map/theatre easier accessible. The information contains location and road/rail connections of the towns. +-- +-- **Note** that try to avoid hard coding stuff in Moose since DCS is updated frequently and things change. Therefore, the main source of information is either a file `towns.lua` that can be +-- found in the installation directory of DCS for each map or a table that the user needs to provide. +-- +-- # Basic Setup +-- +-- A new `TOWNS` object can be created with the @{#TOWNS.NewFromFile}(*towns_lua_file*) function. +-- +-- local towns=TOWNS:NewFromFile("\Mods\terrains\\towns.lua") +-- towns:MarkerShow() +-- +-- This will load the towns from the `` for the specific map and place markers on the F10 map. This is the first step you should do to ensure that the file +-- you provided is correct and all relevant towns are present. +-- +-- # User Functions +-- +-- ## F10 Map Markers +-- +-- ## Position +-- +-- ## Get Closest Town +-- +-- +-- @field #TOWNS +TOWNS = { + ClassName = "TOWNS", + verbose = 0, + towns = {}, +} + +--- Town data. +-- @type TOWNS.Town +-- @field #string display_name Displayed name. +-- @field #string name Name of the town. +-- @field #number latitude Latitude. +-- @field #number longitude Longitude +-- @field DCS#Vec3 vec3 Position vector 3D. +-- @field Core.Point#COORDINATE coordinate The coordinate. +-- @field Core.Point#COORDINATE coordRoad The coordinate of the closest road. +-- @field Core.Point#COORDINATE coordRail The coordinate of the closest railway. +-- @field #number markerID ID for the F10 marker. + +--- TOWNS class version. +-- @field #string version +TOWNS.version="0.1.0" + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- ToDo list +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +-- TODO: A lot... +-- DONE: Road connection +-- DONE: Rail connection +-- DONE: Connection between towns + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Constructor(s) +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Create a new TOWNS class instance from a given table. +-- @param #TOWNS self +-- @param #table TownTable Table with all towns data. +-- @return #TOWNS self +function TOWNS:NewFromTable(TownTable) + + -- Inherit everything from BASE class. + self=BASE:Inherit(self, BASE:New()) -- #TOWNS + + for TownName,_town in pairs(TownTable) do + local town=_town --#TOWNS.Town + + town.name=TownName + + -- Get coordinate + town.coordinate=COORDINATE:NewFromLLDD(town.latitude, town.longitude) + + -- Get coordinate of closest road + town.coordRoad=town.coordinate:GetClosestPointToRoad() + + -- Get coordinate of closest rail + town.coordRail=town.coordinate:GetClosestPointToRoad(true) + + -- Add to table + table.insert(self.towns, town) + end + + -- Debug output + self:I(string.format("Added %d towns", #self.towns)) + + return self +end + + +--- Create a new TOWNS class instance from a given file. +-- @param #TOWNS self +-- @param #string FileName Full path to the file containing the towns data. +-- @return #TOWNS self +function TOWNS:NewFromFile(FileName) + + -- Inherit everything from BASE class. + self=BASE:Inherit(self, BASE:New()) -- #TOWNS + + local exists=UTILS.FileExists(FileName) + + if exists==false then + self:E(string.format("ERROR: file with towns info does not exist!")) + return nil + end + + -- This will create a global table `towns` + dofile(FileName) + + -- Get towns from table. + self=self:NewFromTable(towns) + + return self +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- User Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Get 3D position vector of a specific town. +-- @param #TOWNS self +-- @param #TOWNS.Town town The town data structure. +-- @return DCS#Vec3 Position vector. +function TOWNS:GetVec3(town) + return town.vec3 +end + +--- Get COORDINATE of a specific town. +-- @param #TOWNS self +-- @param #TOWNS.Town town The town data structure. +-- @return Core.Point#COORDINATE The coordinate. +function TOWNS:GetCoordinate(town) + return town.coordinate +end + +--- Get closest road coordinate of a town. +-- @param #TOWNS self +-- @param #TOWNS.Town town The town data structure. +-- @return Core.Point#COORDINATE The closest road coordinate. +function TOWNS:GetCoordRoad(town) + return town.coordRoad +end + +--- Get closest rail coordinate of a town. +-- @param #TOWNS self +-- @param #TOWNS.Town town The town data structure. +-- @return Core.Point#COORDINATE The closest rail coordinate. +function TOWNS:GetCoordRail(town) + return town.coordRail +end + +--- Get road or rail connection between two towns. +-- @param #TOWNS self +-- @param #TOWNS.Town townA The town data structure. +-- @param #TOWNS.Town townB The town data structure. +-- @param #boolean Railroad If `true`, find rail road connection +-- @return Core.Pathline#PATHLINE Pathline connecting the two towns on road. +function TOWNS:GetConnectionRoad(townA, townB, Railroad) + + local path=townA.coordRoad:GetPathlineOnRoad(townB.coordRoad, false, Railroad) + + return path +end + +--- Find closest town to a given coordinate. +-- @param #TOWNS self +-- @param Core.Point#COORDINATE Coordinate The reference coordinate. +-- @param #number DistMax (Optional) Max search distance in meters. +-- @param #table ExcludeList (Optional) List of towns excluded from the search. +-- @return #TOWNS.Town The closest town. +function TOWNS:GetClosestTown(Coordinate, DistMax, ExcludeList) + + local Town=nil --#TOWNS.Town + local distmin=math.huge + ExcludeList=ExcludeList or {} + + for _,_town in pairs(self.towns) do + local town=_town --#TOWNS.Town + + if (not UTILS.IsInTable(ExcludeList, town, "name")) then + + local dist=Coordinate:Get2DDistance(town.coordinate) + + if dist100 or los==false then - --env.info("FF Update Engage Target Moved "..self.engage.Target:GetName()) - -- Update new position. self.engage.Coordinate:UpdateFromVec3(vec3) @@ -1852,13 +1850,14 @@ function ARMYGROUP:_UpdateEngageTarget() -- Remove current waypoint self:RemoveWaypointByID(self.engage.Waypoint.uid) - local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.9) + -- Get new coordinate where to go. + local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.95) -- Add waypoint after current. self.engage.Waypoint=self:AddWaypoint(intercoord, self.engage.Speed, uid, self.engage.Formation, true) -- Set if we want to resume route after reaching the detour waypoint. - self.engage.Waypoint.detour=0 + self.engage.Waypoint.detour=1 end diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index a425eacae..cdad223f1 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -324,6 +324,10 @@ -- -- A ground attack mission can be created with the @{#AUFTRAG.NewGROUNDATTACK}() function. -- +-- ## NAVALENGAGEMENT +-- +-- A naval engagement mission can be created with the @{#AUFTRAG.NewNAVALENGAGEMENT}() function. +-- -- # Assigning Missions -- -- An AUFTRAG can be assigned to groups (FLIGHTGROUP, ARMYGROUP, NAVYGROUP), legions (AIRWING, BRIGADE, FLEET) or to a COMMANDER. @@ -443,6 +447,7 @@ _AUFTRAGSNR=0 -- @field #string HOVER Hover. -- @field #string LANDATCOORDINATE Land at coordinate. -- @field #string GROUNDATTACK Ground attack. +-- @field #string NAVALENGAGEMENT Naval engagement (similar to GROUNDATTACK). -- @field #string CARGOTRANSPORT Cargo transport. -- @field #string RELOCATECOHORT Relocate a cohort from one legion to another. -- @field #string AIRDEFENSE Air defense. @@ -491,6 +496,7 @@ AUFTRAG.Type={ HOVER="Hover", LANDATCOORDINATE="Land at Coordinate", GROUNDATTACK="Ground Attack", + NAVALENGAGEMENT="Naval Engagement", CARGOTRANSPORT="Cargo Transport", RELOCATECOHORT="Relocate Cohort", AIRDEFENSE="Air Defence", @@ -515,6 +521,7 @@ AUFTRAG.Type={ -- @field #string BARRAGE Barrage. -- @field #string HOVER Hover. -- @field #string GROUNDATTACK Ground attack. +-- @field #string NAVALENGAGEMENT Naval engagement. -- @field #string FERRY Ferry mission. -- @field #string RELOCATECOHORT Relocate cohort. -- @field #string AIRDEFENSE Air defense. @@ -537,6 +544,7 @@ AUFTRAG.SpecialTask={ ARMORATTACK="AmorAttack", HOVER="Hover", GROUNDATTACK="Ground Attack", + NAVALENGAGEMENT="Naval Engagement", FERRY="Ferry", RELOCATECOHORT="Relocate Cohort", AIRDEFENSE="Air Defense", @@ -666,7 +674,7 @@ AUFTRAG.Category={ --- AUFTRAG class version. -- @field #string version -AUFTRAG.version="1.2.1" +AUFTRAG.version="1.3.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -2210,7 +2218,7 @@ end -- **Note** that it is recommended to set the weapon range via the `OPSGROUP:AddWeaponRange()` function as this cannot be retrieved from the DCS API. -- @param #AUFTRAG self -- @param Core.Point#COORDINATE Target Center of the firing solution. --- @param #number Nshots Number of shots to be fired. Default `#nil`. +-- @param #number Nshots Number of shots to be fired. Default `#nil`. If value is in (0,1), it is interpreted as per cent of available ammo. -- @param #number Radius Radius of the shells in meters. Default 100 meters. -- @param #number Altitude Altitude in meters. Can be used to setup a Barrage. Default `#nil`. -- @return #AUFTRAG self @@ -2382,11 +2390,13 @@ function AUFTRAG:NewARMORATTACK(Target, Speed, Formation) return mission end ---- **[GROUND]** Create a GROUNDATTACK mission. Ground group(s) will go to a target object and attack. +--- **[GROUND]** Create a GROUNDATTACK mission. Ground group(s) will go to a target object and attack at their own discretion. +-- Unfortunately, the "Attack Group" and "Attack Unit" tasks do not work for ground and naval groups (only for aircraft). +-- Therefore, we resort to this workaround, which guides the attacking group to the vicinity of the target. Then they start shooting on their own, once they detect the target. -- @param #AUFTRAG self -- @param Wrapper.Positionable#POSITIONABLE Target The target to attack. Can be a GROUP, UNIT or STATIC object. -- @param #number Speed Speed in knots. Default max. --- @param #string Formation The attack formation, e.g. "Wedge", "Vee" etc. Default `ENUMS.Formation.Vehicle.Vee`. +-- @param #string Formation The attack formation, e.g. "Wedge", "Vee" etc. Default `ENUMS.Formation.Vehicle.Vee`. Only working for ground, not naval! -- @return #AUFTRAG self function AUFTRAG:NewGROUNDATTACK(Target, Speed, Formation) @@ -2413,6 +2423,38 @@ function AUFTRAG:NewGROUNDATTACK(Target, Speed, Formation) return mission end +--- **[NAVAL]** Create a NAVALENGAGEMENT mission. Naval group(s) will go to a target object and attack at their own discretion. +-- Unfortunately, the "Attack Group" and "Attack Unit" tasks do not work for ground and naval groups (only for aircraft). +-- Therefore, we resort to this workaround, which guides the attacking group to the vicinity of the target. Then they start shooting on their own, once they detect the target. +-- @param #AUFTRAG self +-- @param Wrapper.Positionable#POSITIONABLE Target The target to attack. Can be a GROUP, UNIT or STATIC object. +-- @param #number Speed Speed in knots. Default max. +-- @param #number Depth The attack depth in meters. Only for submarines! +-- @return #AUFTRAG self +function AUFTRAG:NewNAVALENGAGEMENT(Target, Speed, Depth) + + local mission=AUFTRAG:New(AUFTRAG.Type.NAVALENGAGEMENT) + + mission:_TargetFromObject(Target) + + mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.NAVALENGAGEMENT) + + -- Defaults. + mission.optionROE=ENUMS.ROE.OpenFire + mission.optionAlarm=ENUMS.AlarmState.Auto + mission.missionFraction=0.70 + mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed) or nil + mission.missionAltitude=Depth or 0 + + mission.categories={AUFTRAG.Category.NAVAL} + + mission.DCStask=mission:GetDCSMissionTask() + + mission.DCStask.params.speed=mission.missionSpeed and UTILS.KmphToMps(mission.missionSpeed) or nil + + return mission +end + --- **[AIR, GROUND, NAVAL]** Create a RECON mission. -- @param #AUFTRAG self -- @param Core.Set#SET_ZONE ZoneSet The recon zones. @@ -2705,33 +2747,35 @@ function AUFTRAG:NewFromTarget(Target, MissionType) local mission=nil --#AUFTRAG if MissionType==AUFTRAG.Type.ANTISHIP then - mission=self:NewANTISHIP(Target, Altitude) + mission=self:NewANTISHIP(Target) elseif MissionType==AUFTRAG.Type.ARTY then - mission=self:NewARTY(Target, Nshots, Radius) + mission=self:NewARTY(Target, 0.3) -- use 30% of the available ammo elseif MissionType==AUFTRAG.Type.BAI then - mission=self:NewBAI(Target, Altitude) + mission=self:NewBAI(Target) elseif MissionType==AUFTRAG.Type.BOMBCARPET then - mission=self:NewBOMBCARPET(Target, Altitude, CarpetLength) + mission=self:NewBOMBCARPET(Target) elseif MissionType==AUFTRAG.Type.BOMBING then - mission=self:NewBOMBING(Target, Altitude) + mission=self:NewBOMBING(Target) elseif MissionType==AUFTRAG.Type.BOMBRUNWAY then - mission=self:NewBOMBRUNWAY(Target, Altitude) + mission=self:NewBOMBRUNWAY(Target) elseif MissionType==AUFTRAG.Type.STRAFING then - mission=self:NewSTRAFING(Target, Altitude) + mission=self:NewSTRAFING(Target) elseif MissionType==AUFTRAG.Type.CAS then - mission=self:NewCAS(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000), Altitude, Speed, Target:GetAverageCoordinate(), Heading, Leg, TargetTypes) + mission=self:NewCAS(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000), nil, nil, Target:GetAverageCoordinate()) elseif MissionType==AUFTRAG.Type.CASENHANCED then - mission=self:NewCASENHANCED(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000), Altitude, Speed, RangeMax, NoEngageZoneSet, TargetTypes) + mission=self:NewCASENHANCED(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000)) elseif MissionType==AUFTRAG.Type.INTERCEPT then mission=self:NewINTERCEPT(Target) elseif MissionType==AUFTRAG.Type.SEAD then - mission=self:NewSEAD(Target, Altitude) + mission=self:NewSEAD(Target) elseif MissionType==AUFTRAG.Type.STRIKE then - mission=self:NewSTRIKE(Target, Altitude) + mission=self:NewSTRIKE(Target) elseif MissionType==AUFTRAG.Type.ARMORATTACK then - mission=self:NewARMORATTACK(Target, Speed) + mission=self:NewARMORATTACK(Target) elseif MissionType==AUFTRAG.Type.GROUNDATTACK then - mission=self:NewGROUNDATTACK(Target, Speed, Formation) + mission=self:NewGROUNDATTACK(Target) + elseif MissionType==AUFTRAG.Type.NAVALENGAGEMENT then + mission=self:NewNAVALENGAGEMENT(Target) else return nil end @@ -2848,7 +2892,7 @@ function AUFTRAG:NewAUTO(EngageGroup) if auftrag==AUFTRAG.Type.ANTISHIP then mission=AUFTRAG:NewANTISHIP(Target) elseif auftrag==AUFTRAG.Type.ARTY then - mission=AUFTRAG:NewARTY(Target) + mission=AUFTRAG:NewARTY(Target, 0.2) elseif auftrag==AUFTRAG.Type.AWACS then mission=AUFTRAG:NewAWACS(Coordinate, Altitude,Speed,Heading,Leg) elseif auftrag==AUFTRAG.Type.BAI then @@ -4399,7 +4443,7 @@ function AUFTRAG:onafterStatus(From, Event, To) -- Group info. if self.verbose>=3 then -- Data on assigned groups. - local text=string.format("Assets [N=%d,Nassigned=%s, Ndead=%s]:", self.Nassets or 0, self.Nassigned or 0, self.Ndead or 0) + local text=string.format("Assets [N=%d, Nassigned=%s, Ndead=%s]:", self.Nassets or 0, self.Nassigned or 0, self.Ndead or 0) for i,_asset in pairs(self.assets or {}) do local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem text=text..string.format("\n[%d] %s: spawned=%s, requested=%s, reserved=%s", i, asset.spawngroupname, tostring(asset.spawned), tostring(asset.requested), tostring(asset.reserved)) @@ -4440,7 +4484,7 @@ function AUFTRAG:Evaluate() local owndamage=self.Ncasualties/self.Nelements*100 -- Current number of mission targets. - local Ntargets=self:CountMissionTargets() + local Ntargets=self:CountMissionTargets(true) local Ntargets0=self:GetTargetInitialNumber() local Life=self:GetTargetLife() @@ -4891,16 +4935,25 @@ function AUFTRAG:CheckGroupsDone() self:T2(self.lid..string.format("CheckGroupsDone: Mission is still in state %s [FSM=%s] and reinfoce=%d. Mission NOT DONE!", self.status, self:GetState(), self.reinforce)) return false end + + local NopsgroupsAlive=self:CountOpsGroups() + local NopsgroupsDone=self:CountOpsGroupsInStatus(AUFTRAG.GroupStatus.DONE)+self:CountOpsGroupsInStatus(AUFTRAG.GroupStatus.CANCELLED) -- It could be that all groups were destroyed on the way to the mission execution waypoint. -- TODO: would be better to check if everybody is dead by now. - if self:IsStarted() and self:CountOpsGroups()==0 then + if self:IsStarted() and NopsgroupsAlive==0 then self:T(self.lid..string.format("CheckGroupsDone: Mission is STARTED state %s [FSM=%s] but count of alive OPSGROUP is zero. Mission DONE!", self.status, self:GetState())) return true end - if (self:IsStarted() or self:IsExecuting()) and (fsmState == AUFTRAG.Status.STARTED or fsmState == AUFTRAG.Status.EXECUTING) and self:CountOpsGroups()>0 then - self:T(self.lid..string.format("CheckGroupsDone: Mission is STARTED state %s [FSM=%s] and count of alive OPSGROUP > zero. Mission NOT DONE!", self.status, self:GetState())) + -- Every group alive is done or cancelled + if NopsgroupsAlive==NopsgroupsDone then + self:T(self.lid..string.format("CheckGroupsDone: Mission is in state %s [FSM=%s] but all groups [=%d] are done or cancelled. Mission DONE!", self.status, self:GetState(), NopsgroupsAlive)) + return true + end + + if (self:IsStarted() or self:IsExecuting()) and (fsmState == AUFTRAG.Status.STARTED or fsmState == AUFTRAG.Status.EXECUTING) and NopsgroupsAlive>0 then + self:T(self.lid..string.format("CheckGroupsDone: Mission is in state %s [FSM=%s] and count of alive OPSGROUP > zero. Mission NOT DONE!", self.status, self:GetState())) return false end @@ -5494,8 +5547,9 @@ end --- Count alive mission targets. -- @param #AUFTRAG self +-- @param #boolean OnlyReallyAlive (Optional) If `true`, count only really alive targets (units, groups) but not coordinates or zones. -- @return #number Number of alive target units. -function AUFTRAG:CountMissionTargets() +function AUFTRAG:CountMissionTargets(OnlyReallyAlive) local N=0 @@ -5503,7 +5557,7 @@ function AUFTRAG:CountMissionTargets() local Coalitions=self.coalition and UTILS.GetCoalitionEnemy(self.coalition, true) or nil if self.engageTarget then - N=self.engageTarget:CountTargets(Coalitions) + N=self.engageTarget:CountTargets(Coalitions, OnlyReallyAlive) end return N @@ -6642,6 +6696,26 @@ function AUFTRAG:GetDCSMissionTask() table.insert(DCStasks, DCStask) + elseif self.type==AUFTRAG.Type.NAVALENGAGEMENT then + + --------------------------- + -- NAVAL ENGAGEMENT Mission -- + --------------------------- + + local DCStask={} + + DCStask.id=AUFTRAG.SpecialTask.NAVALENGAGEMENT + + -- We create a "fake" DCS task and pass the parameters to the NAVYGROUP. + local param={} + param.target=self:GetTargetData() + param.speed=self.missionSpeed and UTILS.KmphToMps(self.missionSpeed) or nil + param.altitude=self.missionAltitude or 0 + + DCStask.params=param + + table.insert(DCStasks, DCStask) + elseif self.type==AUFTRAG.Type.AMMOSUPPLY then ------------------------- diff --git a/Moose Development/Moose/Ops/Brigade.lua b/Moose Development/Moose/Ops/Brigade.lua index b8382f136..5734e0f9c 100644 --- a/Moose Development/Moose/Ops/Brigade.lua +++ b/Moose Development/Moose/Ops/Brigade.lua @@ -599,7 +599,80 @@ function BRIGADE:onafterStatus(From, Event, To) text=text..string.format("\n* %s: spawned=%s", asset.spawngroupname, tostring(asset.spawned)) end self:I(self.lid..text) - end + end + + if self.verbose>=3 then + + -- Count numbers + local Ntotal=0 + local Nspawned=0 + local Nrequested=0 + local Nreserved=0 + local Nstock=0 + + local text="\n===========================================\n" + text=text.."Assets:" + local legion=self --Ops.Legion#LEGION + + for _,_cohort in pairs(legion.cohorts) do + local cohort=_cohort --Ops.Cohort#COHORT + + for _,_asset in pairs(cohort.assets) do + local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem + + local state="In Stock" + if asset.flightgroup then + state=asset.flightgroup:GetState() + local mission=legion:GetAssetCurrentMission(asset) + if mission then + state=state..string.format(", Mission \"%s\" [%s]", mission:GetName(), mission:GetType()) + end + else + if asset.spawned then + env.info("FF ERROR: asset has opsgroup but is NOT spawned!") + end + if asset.requested and asset.isReserved then + env.info("FF ERROR: asset is requested and reserved. Should not be both!") + state="Reserved+Requested!" + elseif asset.isReserved then + state="Reserved" + elseif asset.requested then + state="Requested" + end + end + + -- Text. + text=text..string.format("\n[UID=%03d] %s Legion=%s [%s]: State=%s [RID=%s]", + asset.uid, asset.spawngroupname, legion.alias, cohort.name, state, tostring(asset.rid)) + + + if asset.spawned then + Nspawned=Nspawned+1 + end + if asset.requested then + Nrequested=Nrequested+1 + end + if asset.isReserved then + Nreserved=Nreserved+1 + end + if not (asset.spawned or asset.requested or asset.isReserved) then + Nstock=Nstock+1 + end + + Ntotal=Ntotal+1 + + end + + end + text=text.."\n-------------------------------------------" + text=text..string.format("\nNstock = %d", Nstock) + text=text..string.format("\nNreserved = %d", Nreserved) + text=text..string.format("\nNrequested = %d", Nrequested) + text=text..string.format("\nNspawned = %d", Nspawned) + text=text..string.format("\nNtotal = %d (=%d)", Ntotal, Nstock+Nspawned+Nrequested+Nreserved) + text=text.."\n===========================================" + self:I(self.lid..text) + end end diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 372d32afc..42e8d4e03 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -913,7 +913,7 @@ do -- -- Make a Gazelle into a heavy truck, this type can load both crates and troops and eight of each type, up to 4000 kgs: -- my_ctld:SetUnitCapabilities("SA342L", true, true, 8, 8, 12, 4000) -- --- -- Default unit type capabilities are: +-- -- Default unit type capabilities are e.g. (list might be incomplete) -- ["SA342Mistral"] = {type="SA342Mistral", crates=false, troops=true, cratelimit = 0, trooplimit = 4, length = 12, cargoweightlimit = 400}, -- ["SA342L"] = {type="SA342L", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 12, cargoweightlimit = 400}, -- ["SA342M"] = {type="SA342M", crates=false, troops=true, cratelimit = 0, trooplimit = 4, length = 12, cargoweightlimit = 400}, @@ -1396,7 +1396,8 @@ CTLD.UnitTypeCapabilities = { ["Ka-50_3"] = {type="Ka-50_3", crates=false, troops=false, cratelimit = 0, trooplimit = 0, length = 15, cargoweightlimit = 0}, ["Mi-24P"] = {type="Mi-24P", crates=true, troops=true, cratelimit = 2, trooplimit = 8, length = 18, cargoweightlimit = 700}, ["Mi-24V"] = {type="Mi-24V", crates=true, troops=true, cratelimit = 2, trooplimit = 8, length = 18, cargoweightlimit = 700}, - ["Hercules"] = {type="Hercules", crates=true, troops=true, cratelimit = 7, trooplimit = 64, length = 25, cargoweightlimit = 19000}, -- 19t cargo, 64 paratroopers. + ["Hercules"] = {type="Hercules", crates=true, troops=true, cratelimit = 7, trooplimit = 64, length = 25, cargoweightlimit = 19000}, -- 19t cargo, 64 paratroopers. + ["C-130J-30"] = {type="C-130J-30", crates=true, troops=true, cratelimit = 7, trooplimit = 64, length = 35, cargoweightlimit = 21500}, -- 19t cargo, 64 paratroopers. --Actually it's longer, but the center coord is off-center of the model. ["UH-60L"] = {type="UH-60L", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- 4t cargo, 20 (unsec) seats ["UH-60L_DAP"] = {type="UH-60L_DAP", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 16, cargoweightlimit = 500}, -- UH-60L DAP is an attack helo but can do limited CSAR and CTLD @@ -1417,11 +1418,12 @@ CTLD.FixedWingTypes = { ["Hercules"] = "Hercules", ["Bronco"] = "Bronco", ["Mosquito"] = "Mosquito", + ["C-130J-30"] = "C-130J-30", } --- CTLD class version. -- @field #string version -CTLD.version="1.3.38" +CTLD.version="1.3.39" --- Instantiate a new CTLD. -- @param #CTLD self @@ -1561,9 +1563,8 @@ function CTLD:New(Coalition, Prefixes, Alias) self.movetroopsdistance = 5000 self.returntroopstobase = true -- if set to false, troops would stay after deployment inside a load zone. self.troopdropzoneradius = 100 - + self.buildPairSeparation = 25 self.loadSavedCrates = true - self.VehicleMoveFormation = AI.Task.VehicleFormation.VEE -- added support Hercules Mod @@ -4235,23 +4236,62 @@ function CTLD:_BuildCrates(Group, Unit,Engineering,MultiDrop) end -- let\'s get going if canbuild then + local notified=false -- loop again for _,_build in pairs(buildables) do local build = _build -- #CTLD.Buildable if build.CanBuild then - self:_CleanUpCrates(crates,build,number) - self:_RefreshLoadCratesMenu(Group,Unit) - if self.buildtime and self.buildtime > 0 then + local required = build.Required or 1 + if required < 1 then required = 1 end + local full = math.floor((build.Found or 0)/required) + if full < 1 then full = 1 end + + local sep = self.buildPairSeparation or 25 + local hdg = (Unit:GetHeading()+180)%360 + local lat = (hdg+90)%360 + local base = Unit:GetCoordinate():Translate(20,hdg) + + if full == 1 then + local cratesNow, numberNow = self:_FindCratesNearby(Group,Unit, finddist,true,true) + self:_CleanUpCrates(cratesNow,build,numberNow) + self:_RefreshLoadCratesMenu(Group,Unit) + if self.buildtime and self.buildtime > 0 then local buildtimer = TIMER:New(self._BuildObjectFromCrates,self,Group,Unit,build,false,Group:GetCoordinate(),MultiDrop) buildtimer:Start(self.buildtime) - self:_SendMessage(string.format("Build started, ready in %d seconds!",self.buildtime),15,false,Group) + if not notified then + self:_SendMessage(string.format("Build started, ready in %d seconds!",self.buildtime),15,false,Group) + notified=true + end self:__CratesBuildStarted(1,Group,Unit,build.Name) + else + self:_BuildObjectFromCrates(Group,Unit,build,false,nil,MultiDrop) + end else - self:_BuildObjectFromCrates(Group,Unit,build,false,nil,MultiDrop) + local start = -((full-1)*sep)/2 + for n=1,full do + local cratesNow, numberNow = self:_FindCratesNearby(Group,Unit, finddist,true,true) + self:_CleanUpCrates(cratesNow,build,numberNow) + self:_RefreshLoadCratesMenu(Group,Unit) + local off = start + (n-1)*sep + local coord = base:Translate(off,lat):GetVec2() + local b = { Name=build.Name, Required=build.Required, Template=build.Template, CanBuild=true, Type=build.Type, Coord=coord } + if self.buildtime and self.buildtime > 0 then + local buildtimer = TIMER:New(self._BuildObjectFromCrates,self,Group,Unit,b,false,Group:GetCoordinate(),MultiDrop) + buildtimer:Start(self.buildtime) + if not notified then + self:_SendMessage(string.format("Build started, ready in %d seconds!",self.buildtime),15,false,Group) + notified=true + end + self:__CratesBuildStarted(1,Group,Unit,build.Name) + else + self:_BuildObjectFromCrates(Group,Unit,b,false,nil,MultiDrop) + end + end end 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 @@ -4273,28 +4313,37 @@ function CTLD:_PackCratesNearby(Group, Unit) -- get nearby vehicles local location = Group:GetCoordinate() -- get coordinate of group using function - local nearestGroups = SET_GROUP:New():FilterCoalitions("blue"):FilterZones({ZONE_RADIUS:New("TempZone", location:GetVec2(), self.PackDistance, false)}):FilterOnce() -- get all groups withing PackDistance from group using function - -- get template name of all vehicles in zone + local nearestGroups = SET_GROUP:New():FilterCoalitions("blue"):FilterZones({ZONE_RADIUS:New("TempZone", location:GetVec2(), self.PackDistance, false)}):FilterOnce() + + local packedAny = false -- determine if group is packable for _, _Group in pairs(nearestGroups.Set) do -- convert #SET_GROUP to a list of Wrapper.Group#GROUP + local didPackThisGroup = false for _, _Template in pairs(_DATABASE.Templates.Groups) do -- iterate through the database of templates - if (string.match(_Group:GetName(), _Template.GroupName)) then -- check if the Wrapper.Group#GROUP near the player is in the list of templates by name - -- generate crates and destroy group + if string.match(_Group:GetName(), _Template.GroupName) then -- check if the Wrapper.Group#GROUP near the player is in the list of templates by name for _, _entry in pairs(self.Cargo_Crates) do -- iterate through #CTLD_CARGO - if (_entry.Templates[1] == _Template.GroupName) then -- check if the #CTLD_CARGO matches the template name - _Group:Destroy() -- if a match is found destroy the Wrapper.Group#GROUP near the player + if _entry.Templates[1] == _Template.GroupName then -- check if the #CTLD_CARGO matches the template name + _Group:Destroy() self:_GetCrates(Group, Unit, _entry, nil, false, true) -- spawn the appropriate crates near the player self:_RefreshLoadCratesMenu(Group,Unit) -- call the refresher to show the crates in the menu self:__CratesPacked(1,Group,Unit,_entry) - return true + packedAny = true + didPackThisGroup = true + break end end end + if didPackThisGroup then break end end end + + if not packedAny then self:_SendMessage("Nothing to pack at this distance pilot!",10,false,Group) return false + end + + return true end --- (Internal) Function to repair nearby vehicles / FOBs @@ -4513,7 +4562,8 @@ function CTLD:_CleanUpCrates(Crates,Build,Number) if name == nametype then -- matching crate type table.insert(destIDs,thisID) found = found + 1 - nowcrate:GetPositionable():Destroy(false) + local pos = nowcrate:GetPositionable() + if pos then pos:Destroy(false) end nowcrate.Positionable = nil nowcrate.HasBeenDropped = false end @@ -5597,29 +5647,62 @@ function CTLD:_RefreshDropCratesMenu(Group, Unit) local needed=list[1]:GetCratesNeeded() or 1 table.sort(list,function(a,b)return a:GetID()=needed then + local sets=math.floor(#list/(needed>0 and needed or 1)) + if sets>0 then + local parentLabel=string.format("%d. %s (%d SET)",lineIndex,cName,sets) + local parentMenu=MENU_GROUP:New(Group,parentLabel,dropCratesMenu) + for s=1,sets do local chunk={} - for n=i,i+needed-1 do - table.insert(chunk,list[n]) - end - local label=string.format("%d. %s",lineIndex,cName) + for n=i,i+needed-1 do table.insert(chunk,list[n]) end table.insert(self.CrateGroupList[Unit:GetName()],chunk) - local setIndex=#self.CrateGroupList[Unit:GetName()] - MENU_GROUP_COMMAND:New(Group,label,dropCratesMenu,self._UnloadSingleCrateSet,self,Group,Unit,setIndex) i=i+needed - else - local chunk={} - for n=i,#list do - table.insert(chunk,list[n]) - end - local label=string.format("%d. %s %d/%d",lineIndex,cName,left,needed) - table.insert(self.CrateGroupList[Unit:GetName()],chunk) - local setIndex=#self.CrateGroupList[Unit:GetName()] - MENU_GROUP_COMMAND:New(Group,label,dropCratesMenu,self._UnloadSingleCrateSet,self,Group,Unit,setIndex) - i=#list+1 end + if sets==1 then + MENU_GROUP_COMMAND:New(Group,"Drop",parentMenu,function(selfArg,GroupArg,UnitArg,cNameArg,neededArg,qty) + local uName=UnitArg:GetName() + for k=1,qty do + local lst=selfArg.CrateGroupList and selfArg.CrateGroupList[uName] + if not lst then break end + local idx=nil + for j=1,#lst do + local ch=lst[j] + local first=ch and ch[1] + if first and (not first:WasDropped()) and first:GetName()==cNameArg and #ch>=neededArg then idx=j break end + end + if not idx then break end + selfArg:_UnloadSingleCrateSet(GroupArg,UnitArg,idx) + end + end,self,Group,Unit,cName,needed,1) + else + for q=1,sets do + local qm=MENU_GROUP:New(Group,string.format("Drop %d Set%s",q,q>1 and "s" or ""),parentMenu) + MENU_GROUP_COMMAND:New(Group,"Drop",qm,function(selfArg,GroupArg,UnitArg,cNameArg,neededArg,qty) + local uName=UnitArg:GetName() + for k=1,qty do + local lst=selfArg.CrateGroupList and selfArg.CrateGroupList[uName] + if not lst then break end + local idx=nil + for j=1,#lst do + local ch=lst[j] + local first=ch and ch[1] + if first and (not first:WasDropped()) and first:GetName()==cNameArg and #ch>=neededArg then idx=j break end + end + if not idx then break end + selfArg:_UnloadSingleCrateSet(GroupArg,UnitArg,idx) + end + end,self,Group,Unit,cName,needed,q) + end + end + lineIndex=lineIndex+1 + end + if i<=#list then + local left=#list-i+1 + local chunk={} + for n=i,#list do table.insert(chunk,list[n]) end + table.insert(self.CrateGroupList[Unit:GetName()],chunk) + local setIndex=#self.CrateGroupList[Unit:GetName()] + local label=string.format("%d. %s %d/%d",lineIndex,cName,left,needed) + MENU_GROUP_COMMAND:New(Group,label,dropCratesMenu,self._UnloadSingleCrateSet,self,Group,Unit,setIndex) lineIndex=lineIndex+1 end end @@ -5642,39 +5725,105 @@ function CTLD:_RefreshDropCratesMenu(Group, Unit) local needed=list[1]:GetCratesNeeded() or 1 table.sort(list,function(a,b)return a:GetID()=needed then + local sets=math.floor(#list/(needed>0 and needed or 1)) + if sets>0 then + local parentLabel=string.format("%d. %s (%d SET)",lineIndex,cName,sets) + local parentMenu=MENU_GROUP:New(Group,parentLabel,dropCratesMenu) + for s=1,sets do local chunk={} - for n=i,i+needed-1 do - table.insert(chunk,list[n]) - end - local label=string.format("%d. %s",lineIndex,cName) + for n=i,i+needed-1 do table.insert(chunk,list[n]) end table.insert(self.CrateGroupList[Unit:GetName()],chunk) - local setIndex=#self.CrateGroupList[Unit:GetName()] - local mSet=MENU_GROUP:New(Group,label,dropCratesMenu) - MENU_GROUP_COMMAND:New(Group,"Drop",mSet,self._UnloadSingleCrateSet,self,Group,Unit,setIndex) - if not ( self:IsUnitInAir(Unit) and self:IsFixedWing(Unit) ) then - MENU_GROUP_COMMAND:New(Group,"Drop and build",mSet,self._DropSingleAndBuild,self,Group,Unit,setIndex) - end i=i+needed - else - local chunk={} - for n=i,#list do - table.insert(chunk,list[n]) - end - local label=string.format("%d. %s %d/%d",lineIndex,cName,left,needed) - table.insert(self.CrateGroupList[Unit:GetName()],chunk) - local setIndex=#self.CrateGroupList[Unit:GetName()] - MENU_GROUP_COMMAND:New(Group,label,dropCratesMenu,self._UnloadSingleCrateSet,self,Group,Unit,setIndex) - i=#list+1 end + if sets==1 then + MENU_GROUP_COMMAND:New(Group,"Drop",parentMenu,function(selfArg,GroupArg,UnitArg,cNameArg,neededArg,qty) + local uName=UnitArg:GetName() + for k=1,qty do + local lst=selfArg.CrateGroupList and selfArg.CrateGroupList[uName] + if not lst then break end + local idx=nil + for j=1,#lst do + local ch=lst[j] + local first=ch and ch[1] + if first and (not first:WasDropped()) and first:GetName()==cNameArg and #ch>=neededArg then idx=j break end + end + if not idx then break end + selfArg:_UnloadSingleCrateSet(GroupArg,UnitArg,idx) + end + end,self,Group,Unit,cName,needed,1) + if not ( self:IsUnitInAir(Unit) and self:IsFixedWing(Unit) ) then + MENU_GROUP_COMMAND:New(Group,"Drop and build",parentMenu,function(selfArg,GroupArg,UnitArg,cNameArg,neededArg,qty) + local uName=UnitArg:GetName() + for k=1,qty do + local lst=selfArg.CrateGroupList and selfArg.CrateGroupList[uName] + if not lst then break end + local idx=nil + for j=1,#lst do + local ch=lst[j] + local first=ch and ch[1] + if first and (not first:WasDropped()) and first:GetName()==cNameArg and #ch>=neededArg then idx=j break end + end + if not idx then break end + selfArg:_UnloadSingleCrateSet(GroupArg,UnitArg,idx) + end + selfArg:_BuildCrates(GroupArg,UnitArg) + end,self,Group,Unit,cName,needed,1) + end + else + for q=1,sets do + local qm=MENU_GROUP:New(Group,string.format("Drop %d Set%s",q,q>1 and "s" or ""),parentMenu) + MENU_GROUP_COMMAND:New(Group,"Drop",qm,function(selfArg,GroupArg,UnitArg,cNameArg,neededArg,qty) + local uName=UnitArg:GetName() + for k=1,qty do + local lst=selfArg.CrateGroupList and selfArg.CrateGroupList[uName] + if not lst then break end + local idx=nil + for j=1,#lst do + local ch=lst[j] + local first=ch and ch[1] + if first and (not first:WasDropped()) and first:GetName()==cNameArg and #ch>=neededArg then idx=j break end + end + if not idx then break end + selfArg:_UnloadSingleCrateSet(GroupArg,UnitArg,idx) + end + end,self,Group,Unit,cName,needed,q) + if not ( self:IsUnitInAir(Unit) and self:IsFixedWing(Unit) ) then + MENU_GROUP_COMMAND:New(Group,"Drop and build",qm,function(selfArg,GroupArg,UnitArg,cNameArg,neededArg,qty) + local uName=UnitArg:GetName() + for k=1,qty do + local lst=selfArg.CrateGroupList and selfArg.CrateGroupList[uName] + if not lst then break end + local idx=nil + for j=1,#lst do + local ch=lst[j] + local first=ch and ch[1] + if first and (not first:WasDropped()) and first:GetName()==cNameArg and #ch>=neededArg then idx=j break end + end + if not idx then break end + selfArg:_UnloadSingleCrateSet(GroupArg,UnitArg,idx) + end + selfArg:_BuildCrates(GroupArg,UnitArg) + end,self,Group,Unit,cName,needed,q) + end + end + end + lineIndex=lineIndex+1 + end + if i<=#list then + local left=#list-i+1 + local chunk={} + for n=i,#list do table.insert(chunk,list[n]) end + table.insert(self.CrateGroupList[Unit:GetName()],chunk) + local setIndex=#self.CrateGroupList[Unit:GetName()] + local label=string.format("%d. %s %d/%d",lineIndex,cName,left,needed) + MENU_GROUP_COMMAND:New(Group,label,dropCratesMenu,self._UnloadSingleCrateSet,self,Group,Unit,setIndex) lineIndex=lineIndex+1 end end end end + --- (Internal) Function to unload a single Troop group by ID. -- @param #CTLD self -- @param Wrapper.Group#GROUP Group The calling group. @@ -8472,7 +8621,7 @@ end local injecttroops = CTLD_CARGO:New(nil,cargoname,cargotemplates,cargotype,true,true,size,nil,true,mass) self:InjectTroops(dropzone,injecttroops,self.surfacetypes,self.useprecisecoordloads,structure,timestamp) end - elseif self.loadSavedCrates and ((type(groupname) == "string" and groupname == "STATIC") or cargotype == CTLD_CARGO.Enum.REPAIR) then + elseif self.loadSavedCrates and (type(groupname) == "string" and groupname == "STATIC") or cargotype == CTLD_CARGO.Enum.REPAIR then local dropzone = ZONE_RADIUS:New("DropZone",vec2,20) local injectstatic = nil if cargotype == CTLD_CARGO.Enum.VEHICLE or cargotype == CTLD_CARGO.Enum.FOB then diff --git a/Moose Development/Moose/Ops/Chief.lua b/Moose Development/Moose/Ops/Chief.lua index 3e9f46561..9500ae313 100644 --- a/Moose Development/Moose/Ops/Chief.lua +++ b/Moose Development/Moose/Ops/Chief.lua @@ -35,7 +35,7 @@ -- @field #number Nsuccess Number of successful missions. -- @field #number Nfailure Number of failed mission. -- @field #table assetNumbers Asset numbers. Each entry is a table of data type `#CHIEF.AssetNumber`. --- @extends Ops.Intel#INTEL +-- @extends Ops.Intelligence#INTEL --- *In preparing for battle I have always found that plans are useless, but planning is indispensable* -- Dwight D Eisenhower -- @@ -48,7 +48,7 @@ -- -- # Territory -- --- The chief class allows you to define boarder zones, conflict zones and attack zones. +-- The chief class allows you to define border zones, conflict zones and attack zones. -- -- ## Border Zones -- @@ -332,7 +332,7 @@ CHIEF.Strategy = { --- CHIEF class version. -- @field #string version -CHIEF.version="0.6.1" +CHIEF.version="0.7.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -2897,6 +2897,8 @@ function CHIEF:_GetMissionPerformanceFromTarget(Target) table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ARTY, 30)) else + + -- Everything else table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.BAI, 100)) table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.GROUNDATTACK, 50)) @@ -2912,6 +2914,8 @@ function CHIEF:_GetMissionPerformanceFromTarget(Target) --- table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ANTISHIP, 100)) + table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.NAVALENGAGEMENT, 50)) + table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ARTY, 30)) else self:E(self.lid.."ERROR: Unknown Group category!") diff --git a/Moose Development/Moose/Ops/Cohort.lua b/Moose Development/Moose/Ops/Cohort.lua index c18d838d6..48f63112c 100644 --- a/Moose Development/Moose/Ops/Cohort.lua +++ b/Moose Development/Moose/Ops/Cohort.lua @@ -629,32 +629,42 @@ end --- Remove assets from pool. Not that assets must not be spawned or already reserved or requested. -- @param #COHORT self -- @param #number N Number of assets to be removed. Default 1. +-- @param #number Delay Delay in seconds before assets are removed. -- @return #COHORT self -function COHORT:RemoveAssets(N) +function COHORT:RemoveAssets(N, Delay) self:T2(self.lid..string.format("Remove %d assets of Cohort", N)) - - N=N or 1 - local n=0 - for i=#self.assets,1,-1 do - local asset=self.assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem + if Delay and Delay>0 then + -- Delayed call + self:ScheduleOnce(Delay, COHORT.RemoveAssets, self, N, 0) + else - self:T2(self.lid..string.format("Checking removing asset %s", asset.spawngroupname)) - if not (asset.requested or asset.spawned or asset.isReserved) then - self:T2(self.lid..string.format("Removing asset %s", asset.spawngroupname)) - table.remove(self.assets, i) - n=n+1 - else - self:T2(self.lid..string.format("Could NOT Remove asset %s", asset.spawngroupname)) + N=N or 1 + + local n=0 + for i=#self.assets,1,-1 do + local asset=self.assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem + + self:T2(self.lid..string.format("Checking removing asset %s", asset.spawngroupname)) + if not (asset.requested or asset.spawned or asset.isReserved) then + self:T2(self.lid..string.format("Removing asset %s", asset.spawngroupname)) + -- Remove from warehouse and warehouse DB + asset.legion:_DeleteStockItem(asset) + table.remove(self.assets, i) + n=n+1 + else + self:T2(self.lid..string.format("Could NOT Remove asset %s", asset.spawngroupname)) + end + + if n>=N then + break + end + end - if n>=N then - break - end + self:T(self.lid..string.format("Removed %d/%d assets. New asset count=%d", n, N, #self.assets)) end - - self:T(self.lid..string.format("Removed %d/%d assets. New asset count=%d", n, N, #self.assets)) return self end diff --git a/Moose Development/Moose/Ops/Legion.lua b/Moose Development/Moose/Ops/Legion.lua index 8e3a3f10c..513ed07f0 100644 --- a/Moose Development/Moose/Ops/Legion.lua +++ b/Moose Development/Moose/Ops/Legion.lua @@ -1901,7 +1901,7 @@ function LEGION:_TacticalOverview() for _,mtype in pairs(AUFTRAG.Type) do local n=self:CountMissionsInQueue(mtype) if n>0 then - local N=self:CountMissionsInQueue(mtype) + local N=self:CountMissionsInQueue(mtype, true) text=text..string.format(" - %s: %d [Running=%d]\n", mtype, n, N) end end @@ -2057,18 +2057,23 @@ end --- Count missions in mission queue. -- @param #LEGION self -- @param #table MissionTypes Types on mission to be checked. Default *all* possible types `AUFTRAG.Type`. +-- @param #boolean OnlyRunning If `true`, only count running missions. -- @return #number Number of missions that are not over yet. -function LEGION:CountMissionsInQueue(MissionTypes) +function LEGION:CountMissionsInQueue(MissionTypes, OnlyRunning) MissionTypes=MissionTypes or AUFTRAG.Type local N=0 for _,_mission in pairs(self.missionqueue) do local mission=_mission --Ops.Auftrag#AUFTRAG + + if (not OnlyRunning) or (mission.statusLegion~=AUFTRAG.Status.PLANNED) then - -- Check if this mission type is requested. - if mission:IsNotOver() and AUFTRAG.CheckMissionType(mission.type, MissionTypes) then - N=N+1 + -- Check if this mission type is requested. + if mission:IsNotOver() and AUFTRAG.CheckMissionType(mission.type, MissionTypes) then + N=N+1 + end + end end diff --git a/Moose Development/Moose/Ops/NavyGroup.lua b/Moose Development/Moose/Ops/NavyGroup.lua index 85f03853d..5f2869d03 100644 --- a/Moose Development/Moose/Ops/NavyGroup.lua +++ b/Moose Development/Moose/Ops/NavyGroup.lua @@ -86,12 +86,14 @@ NAVYGROUP = { -- @field Ops.Target#TARGET Target The target. -- @field Core.Point#COORDINATE Coordinate Last known coordinate of the target. -- @field Ops.OpsGroup#OPSGROUP.Waypoint Waypoint the waypoint created to go to the target. +-- @field #number Speed Speed in knots. +-- @field #number Depth Depth of the engagement (submarines). -- @field #number roe ROE backup. -- @field #number alarmstate Alarm state backup. --- NavyGroup version. -- @field #string version -NAVYGROUP.version="1.0.3" +NAVYGROUP.version="1.0.4" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -1081,7 +1083,7 @@ function NAVYGROUP:onafterSpawned(From, Event, To) text=text..string.format("Elements = %d\n", #self.elements) text=text..string.format("Waypoints = %d\n", #self.waypoints) text=text..string.format("Radio = %.1f MHz %s %s\n", self.radio.Freq, UTILS.GetModulationName(self.radio.Modu), tostring(self.radio.On)) - text=text..string.format("Ammo = %d (G=%d/R=%d/M=%d/T=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Rockets, self.ammo.Missiles, self.ammo.Torpedos) + text=text..string.format("Ammo = %d (G=%d/C=%d/R=%d/M=%d/T=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Cannons, self.ammo.Rockets, self.ammo.Missiles, self.ammo.Torpedos) text=text..string.format("FSM state = %s\n", self:GetState()) text=text..string.format("Is alive = %s\n", tostring(self:IsAlive())) text=text..string.format("LateActivate = %s\n", tostring(self:IsLateActivated())) @@ -1602,8 +1604,10 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param Wrapper.Group#GROUP Group the group to be engaged. -function NAVYGROUP:onafterEngageTarget(From, Event, To, Target) +-- @param Ops.Target#TARGET Target The target to be engaged. Can also be a GROUP or UNIT object. +-- @param #number Speed Attack speed in knots. +-- @param #number Depth The depth in meters. Only for submarins. +function NAVYGROUP:onafterEngageTarget(From, Event, To, Target, Speed, Depth) self:T(self.lid.."Engaging Target") if Target:IsInstanceOf("TARGET") then @@ -1615,11 +1619,9 @@ function NAVYGROUP:onafterEngageTarget(From, Event, To, Target) -- Target coordinate. self.engage.Coordinate=UTILS.DeepCopy(self.engage.Target:GetCoordinate()) - - local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.9) + -- Get a coordinate close to the target. + local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.8) - - -- Backup ROE and alarm state. self.engage.roe=self:GetROE() self.engage.alarmstate=self:GetAlarmstate() @@ -1629,11 +1631,17 @@ function NAVYGROUP:onafterEngageTarget(From, Event, To, Target) self:SwitchROE(ENUMS.ROE.OpenFire) -- ID of current waypoint. - local uid=self:GetWaypointCurrent().uid + local uid=self:GetWaypointCurrentUID() + + -- Set formation. + self.engage.Depth=Depth or 0 + + -- Set speed. + self.engage.Speed=Speed -- Add waypoint after current. - self.engage.Waypoint=self:AddWaypoint(intercoord, nil, uid, Formation, true) - + self.engage.Waypoint=self:AddWaypoint(intercoord, Speed, uid, Depth, true) + -- Set if we want to resume route after reaching the detour waypoint. self.engage.Waypoint.detour=1 @@ -1656,24 +1664,22 @@ function NAVYGROUP:_UpdateEngageTarget() -- Check if target moved more than 100 meters. if dist>100 then - --env.info("FF Update Engage Target Moved "..self.engage.Target:GetName()) - -- Update new position. self.engage.Coordinate:UpdateFromVec3(vec3) -- ID of current waypoint. - local uid=self:GetWaypointCurrent().uid + local uid=self:GetWaypointCurrentUID() -- Remove current waypoint self:RemoveWaypointByID(self.engage.Waypoint.uid) - local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.9) + local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.8) -- Add waypoint after current. - self.engage.Waypoint=self:AddWaypoint(intercoord, nil, uid, Formation, true) + self.engage.Waypoint=self:AddWaypoint(intercoord, self.engage.Speed, uid, self.engage.Depth, true) -- Set if we want to resume route after reaching the detour waypoint. - self.engage.Waypoint.detour=0 + self.engage.Waypoint.detour=1 end @@ -1709,8 +1715,8 @@ function NAVYGROUP:onafterDisengage(From, Event, To) local task=self:GetTaskCurrent() -- Get if current task is ground attack. - if task and task.dcstask.id==AUFTRAG.SpecialTask.GROUNDATTACK then - self:T(self.lid.."Disengage with current task GROUNDATTACK ==> Task Done!") + if task and (task.dcstask.id==AUFTRAG.SpecialTask.GROUNDATTACK or task.dcstask.id==AUFTRAG.SpecialTask.NAVALENGAGEMENT) then + self:T(self.lid.."Disengage with current task GROUNDATTACK/NAVALENGAGEMENT ==> Task Done!") self:TaskDone(task) end diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 24fc98f2d..08539aa5c 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -405,7 +405,9 @@ OPSGROUP.TaskType={ --- Ammo data. -- @type OPSGROUP.Ammo -- @field #number Total Total amount of ammo. --- @field #number Guns Amount of gun shells. +-- @field #number Shells Amount of shells (guns + cannons). +-- @field #number Guns Amount of gun shells (caliber < 25). +-- @field #number Cannons Amount of cannon shells (caliber >= 25). -- @field #number Bombs Amount of bombs. -- @field #number Rockets Amount of rockets. -- @field #number Torpedos Amount of torpedos. @@ -1189,8 +1191,8 @@ function OPSGROUP:GetDCSObject() return self.dcsgroup end ---- Set detection on or off. --- If detection is on, detected targets of the group will be evaluated and FSM events triggered. +--- Make a target (unit, group, opsgroup) known to this group. +-- This is useing the DCS function `controller.knowTarget`. -- @param #OPSGROUP self -- @param Wrapper.Positionable#POSITIONABLE TargetObject The target object. -- @param #boolean KnowType Make type known. @@ -4513,6 +4515,25 @@ function OPSGROUP:_UpdateTask(Task, Mission) if target then self:EngageTarget(target, speed, Task.dcstask.params.formation) end + + elseif Task.dcstask.id==AUFTRAG.SpecialTask.NAVALENGAGEMENT then + + --- + -- Task "Naval Engagement" Mission. + --- + + -- Engage target. + local target=Task.dcstask.params.target --Ops.Target#TARGET + + -- Set speed. Default max. + local speed=self.speedMax and UTILS.KmphToKnots(self.speedMax) or nil + if Task.dcstask.params.speed then + speed=UTILS.MpsToKnots(Task.dcstask.params.speed) + end + + if target then + self:EngageTarget(target, speed, Task.dcstask.params.altitude) + end elseif Task.dcstask.id==AUFTRAG.SpecialTask.PATROLRACETRACK then @@ -4739,7 +4760,7 @@ function OPSGROUP:_UpdateTask(Task, Mission) elseif weaponType==ENUMS.WeaponFlag.AnyRocket then nAmmo=ammo.Rockets elseif weaponType==ENUMS.WeaponFlag.Cannons then - nAmmo=ammo.Guns + nAmmo=ammo.Cannons end --TODO: Update target location while we're at it anyway. @@ -4886,7 +4907,7 @@ function OPSGROUP:onafterTaskCancel(From, Event, To, Task) done=true elseif Task.dcstask.id==AUFTRAG.SpecialTask.ONGUARD or Task.dcstask.id==AUFTRAG.SpecialTask.ARMOREDGUARD then done=true - elseif Task.dcstask.id==AUFTRAG.SpecialTask.GROUNDATTACK or Task.dcstask.id==AUFTRAG.SpecialTask.ARMORATTACK then + elseif Task.dcstask.id==AUFTRAG.SpecialTask.GROUNDATTACK or Task.dcstask.id==AUFTRAG.SpecialTask.ARMORATTACK or Task.dcstask.id==AUFTRAG.SpecialTask.NAVALENGAGEMENT then done=true elseif Task.dcstask.id==AUFTRAG.SpecialTask.NOTHING then done=true @@ -5718,7 +5739,7 @@ end function OPSGROUP:onafterMissionDone(From, Event, To, Mission) -- Debug info. - local text=string.format("Mission %s DONE!", Mission.name) + local text=string.format("Mission DONE %s!", Mission.name) self:T(self.lid..text) -- Set group status. @@ -6231,11 +6252,12 @@ function OPSGROUP:RouteToMission(mission, delay) self:T(self.lid.."Already in mission zone ==> TaskExecute()") self:TaskExecute(waypointtask) -- TODO: Calling PassingWaypoint here is probably better as it marks the mission waypoint as passed! - --self:PassingWaypoint(waypoint) + self:PassingWaypoint(waypoint) return elseif d<25 then self:T(self.lid.."Already within 25 meters of mission waypoint ==> TaskExecute()") self:TaskExecute(waypointtask) + self:PassingWaypoint(waypoint) return end @@ -7850,7 +7872,7 @@ function OPSGROUP:_Spawn(Delay, Template) self:ScheduleOnce(Delay, OPSGROUP._Spawn, self, 0, Template) else -- Debug output. - self:T2({Template=Template}) + --self:T2({Template=Template}) if self:IsArmygroup() and self.ValidateAndRepositionGroundUnits then UTILS.ValidateAndRepositionGroundUnits(Template.units) @@ -11190,11 +11212,11 @@ function OPSGROUP:_CheckAmmoStatus() self:OutOfAmmo() end - -- Guns. - if self.outofGuns and ammo.Guns>0 then + -- Guns (changed to shells) + if self.outofGuns and ammo.Shells>0 then self.outofGuns=false end - if ammo.Guns==0 and self.ammo.Guns>0 and not self.outofGuns then + if ammo.Shells==0 and self.ammo.Shells>0 and not self.outofGuns then self.outofGuns=true self:OutOfGuns() end @@ -13370,7 +13392,9 @@ function OPSGROUP:GetAmmoTot() local Ammo={} --#OPSGROUP.Ammo Ammo.Total=0 + Ammo.Shells=0 Ammo.Guns=0 + Ammo.Cannons=0 Ammo.Rockets=0 Ammo.Bombs=0 Ammo.Torpedos=0 @@ -13391,7 +13415,9 @@ function OPSGROUP:GetAmmoTot() -- Add up total. Ammo.Total=Ammo.Total+ammo.Total + Ammo.Shells=Ammo.Shells+ammo.Shells Ammo.Guns=Ammo.Guns+ammo.Guns + Ammo.Cannons=Ammo.Cannons+ammo.Cannons Ammo.Rockets=Ammo.Rockets+ammo.Rockets Ammo.Bombs=Ammo.Bombs+ammo.Bombs Ammo.Torpedos=Ammo.Torpedos+ammo.Torpedos @@ -13424,6 +13450,8 @@ function OPSGROUP:GetAmmoUnit(unit, display) -- Init counter. local nammo=0 local nshells=0 + local nguns=0 + local ncannons=0 local nrockets=0 local nmissiles=0 local nmissilesAA=0 @@ -13446,10 +13474,10 @@ function OPSGROUP:GetAmmoUnit(unit, display) local ammotable=unit:GetAmmo() if ammotable then - local weapons=#ammotable - --self:I(ammotable) + --self:I(ammotable) + --UTILS.PrintTableToLog(ammotable) -- Loop over all weapons. for w=1,weapons do @@ -13457,9 +13485,9 @@ function OPSGROUP:GetAmmoUnit(unit, display) -- Number of current weapon. local Nammo=ammotable[w]["count"] - -- Range in meters. Seems only to exist for missiles (not shells). - local rmin=ammotable[w]["desc"]["rangeMin"] or 0 - local rmax=ammotable[w]["desc"]["rangeMaxAltMin"] or 0 + -- Range in meters. Seems only to exist for missiles (not shells). + local rmin=ammotable[w]["desc"]["rangeMin"] or 0 + local rmax=ammotable[w]["desc"]["rangeMaxAltMin"] or 0 -- Type name of current weapon. local Tammo=ammotable[w]["desc"]["typeName"] @@ -13481,6 +13509,16 @@ function OPSGROUP:GetAmmoUnit(unit, display) -- Add up all shells. nshells=nshells+Nammo + + -- Add small and large caliber shells for guns and cannons + if ammotable[w]["desc"]["warhead"] and ammotable[w]["desc"]["warhead"]["caliber"] then + local caliber=ammotable[w]["desc"]["warhead"]["caliber"] + if caliber<25 then + nguns=nguns+Nammo + else + ncannons=ncannons+Nammo + end + end -- Debug info. text=text..string.format("- %d shells of type %s, range=%d - %d meters\n", Nammo, _weaponName, rmin, rmax) @@ -13559,7 +13597,9 @@ function OPSGROUP:GetAmmoUnit(unit, display) local ammo={} --#OPSGROUP.Ammo ammo.Total=nammo - ammo.Guns=nshells + ammo.Shells=nshells + ammo.Guns=nguns + ammo.Cannons=ncannons ammo.Rockets=nrockets ammo.Bombs=nbombs ammo.Torpedos=ntorps diff --git a/Moose Development/Moose/Ops/Target.lua b/Moose Development/Moose/Ops/Target.lua index 4863f5202..5a3b75464 100644 --- a/Moose Development/Moose/Ops/Target.lua +++ b/Moose Development/Moose/Ops/Target.lua @@ -2006,9 +2006,10 @@ end --- Count alive objects. -- @param #TARGET self -- @param #TARGET.Object Target Target objective. --- @param #table Coalitions (Optional) Only count targets of the given coalition(s). +-- @param #table Coalitions (Optional) Only count targets of the given coalition(s). +-- @param #boolean OnlyReallyAlive (Optional) If `true`, count only really alive targets (units, groups) but not coordinates or zones. -- @return #number Number of alive target objects. -function TARGET:CountObjectives(Target, Coalitions) +function TARGET:CountObjectives(Target, Coalitions, OnlyReallyAlive) local N=0 @@ -2067,13 +2068,17 @@ function TARGET:CountObjectives(Target, Coalitions) -- No target, where we can check the alive status, so we assume it is alive. Changed this because otherwise target count is 0 if we pass a coordinate. -- This is also more consitent with the life and is alive status. - N=N+1 + if not OnlyReallyAlive then + N=N+1 + end elseif Target.Type==TARGET.ObjectType.ZONE then -- No target, where we can check the alive status, so we assume it is alive. Changed this because otherwise target count is 0 if we pass a coordinate. -- This is also more consitent with the life and is alive status. - N=N+1 + if not OnlyReallyAlive then + N=N+1 + end elseif Target.Type==TARGET.ObjectType.OPSZONE then @@ -2093,15 +2098,16 @@ end --- Count alive targets. -- @param #TARGET self -- @param #table Coalitions (Optional) Only count targets of the given coalition(s). +-- @param #boolean OnlyReallyAlive (Optional) If `true`, count only really alive targets (units, groups) but not coordinates or zones. -- @return #number Number of alive target objects. -function TARGET:CountTargets(Coalitions) +function TARGET:CountTargets(Coalitions, OnlyReallyAlive) local N=0 for _,_target in pairs(self.targets) do local Target=_target --#TARGET.Object - N=N+self:CountObjectives(Target, Coalitions) + N=N+self:CountObjectives(Target, Coalitions, OnlyReallyAlive) end diff --git a/Moose Development/Moose/Utilities/Enums.lua b/Moose Development/Moose/Utilities/Enums.lua index 17e70099d..adcb5f862 100644 --- a/Moose Development/Moose/Utilities/Enums.lua +++ b/Moose Development/Moose/Utilities/Enums.lua @@ -1816,3 +1816,28 @@ ENUMS.FARPObjectTypeNamesAndShape ={ [ENUMS.FARPType.PADSINGLE] = { TypeName="FARP_SINGLE_01", ShapeName="FARP_SINGLE_01"}, } +--- Radio frequency bands (HF, VHF, UHF) +-- @type ENUMS.FrequencyBand +-- @field #number HF High frequency +-- @field #number VHF_LOW Very high frequency +-- @field #number VHF_HI Very high frequency +-- @field #number UHF Ultra high frequency +ENUMS.FrequencyBand = { + HF = 0, + VHF_LOW = 1, + VHF_HI = 2, + UHF = 3, +} + +--- Radio modulation types (AM, FM) +-- @type ENUMS.ModulationType +-- @field #number AM Amplitude modulation +-- @field #number FM Frequency modulation +-- @field #number AMFM Amplitude and frequency modulation +-- @field #number DISCARD Discard modulation +ENUMS.ModulationType = { + AM = 0, + FM = 1, + AMFM = 2, + DISCARD = -1, +} diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index fa4ba7233..3b9e62e69 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -3227,6 +3227,22 @@ function UTILS.BearingToCardinal(Heading) end end +--- Adjust given heading so that is is in [0, 360). +-- @param #number Heading The heading in degrees. +-- @return #number Adjust heading in [0,360). +function UTILS.AdjustHeading360(Heading) + + while Heading>=360 or Heading<0 do + if Heading>=360 then + Heading=Heading-360 + elseif Heading<0 then + Heading=Heading+360 + end + end + + return Heading +end + --- Create a BRAA NATO call string BRAA between two GROUP objects -- @param Wrapper.Group#GROUP FromGrp GROUP object -- @param Wrapper.Group#GROUP ToGrp GROUP object diff --git a/Moose Development/Moose/Wrapper/DynamicCargo.lua b/Moose Development/Moose/Wrapper/DynamicCargo.lua index eb033972e..8c4987f72 100644 --- a/Moose Development/Moose/Wrapper/DynamicCargo.lua +++ b/Moose Development/Moose/Wrapper/DynamicCargo.lua @@ -108,8 +108,9 @@ DYNAMICCARGO.State = { -- @type DYNAMICCARGO.AircraftTypes DYNAMICCARGO.AircraftTypes = { ["CH-47Fbl1"] = "CH-47Fbl1", - ["Mi-8MTV2"] = "CH-47Fbl1", - ["Mi-8MT"] = "CH-47Fbl1", + ["Mi-8MTV2"] = "Mi-8MTV2", + ["Mi-8MT"] = "Mi-8MT", + ["C-130J-30"] = "C-130J-30", } --- Helo types possible. @@ -122,23 +123,29 @@ DYNAMICCARGO.AircraftDimensions = { ["length"] = 11, ["ropelength"] = 30, }, - ["Mi-8MTV2"] = { + ["Mi-8MTV2"] = { ["width"] = 6, ["height"] = 6, ["length"] = 15, ["ropelength"] = 30, }, - ["Mi-8MT"] = { + ["Mi-8MT"] = { ["width"] = 6, ["height"] = 6, ["length"] = 15, ["ropelength"] = 30, }, + ["C-130J-30"] = { + ["width"] = 4, + ["height"] = 12, + ["length"] = 35, + ["ropelength"] = 0, + }, } --- DYNAMICCARGO class version. -- @field #string version -DYNAMICCARGO.version="0.0.9" +DYNAMICCARGO.version="0.1.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 1c38db672..ad7afeef1 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -1058,9 +1058,12 @@ function GROUP:GetTypeName() local DCSGroup = self:GetDCSObject() if DCSGroup then - local GroupTypeName = DCSGroup:getUnit(1):getTypeName() - --self:T3( GroupTypeName ) - return( GroupTypeName ) + local unit = DCSGroup:getUnit(1) + if unit then + local GroupTypeName = unit:getTypeName() + --self:T3( GroupTypeName ) + return( GroupTypeName ) + end end return nil @@ -1075,9 +1078,12 @@ function GROUP:GetNatoReportingName() local DCSGroup = self:GetDCSObject() if DCSGroup then - local GroupTypeName = DCSGroup:getUnit(1):getTypeName() - --self:T3( GroupTypeName ) - return UTILS.GetReportingName(GroupTypeName) + local unit = DCSGroup:getUnit(1) + if unit then + local GroupTypeName = unit:getTypeName() + --self:T3( GroupTypeName ) + return UTILS.GetReportingName(GroupTypeName) + end end return "Bogey" @@ -1093,9 +1099,12 @@ function GROUP:GetPlayerName() local DCSGroup = self:GetDCSObject() if DCSGroup then - local PlayerName = DCSGroup:getUnit(1):getPlayerName() - --self:T3( PlayerName ) - return( PlayerName ) + local unit = DCSGroup:getUnit(1) + if unit then + local PlayerName = unit:getPlayerName() + --self:T3( PlayerName ) + return( PlayerName ) + end end return nil @@ -1111,9 +1120,12 @@ function GROUP:GetCallsign() local DCSGroup = self:GetDCSObject() if DCSGroup then - local GroupCallSign = DCSGroup:getUnit(1):getCallsign() - --self:T3( GroupCallSign ) - return GroupCallSign + local unit = DCSGroup:getUnit(1) + if unit then + local GroupCallSign = unit:getCallsign() + --self:T3( GroupCallSign ) + return GroupCallSign + end end BASE:E( { "Cannot GetCallsign", Positionable = self, Alive = self:IsAlive() } ) @@ -1152,6 +1164,23 @@ function GROUP:GetVec3() return nil end +--- Returns the current {@Core.Vector#VECTOR} of the first Unit in the GROUP. +-- @param #GROUP self +-- @return Core.Vector#VECTOR Vector of the first Unit of the GROUP or nil if cannot be found. +function GROUP:GetVector() + + -- Get first unit. + local unit=self:GetUnit(1) + + if unit then + local vector=unit:GetVector() + return vector + end + + self:E("ERROR: Cannot get Vector of group "..tostring(self.GroupName)) + return nil +end + --- Returns the average Vec3 vector of the Units in the GROUP. -- @param #GROUP self -- @return DCS#Vec3 Current Vec3 of the GROUP or nil if cannot be found. diff --git a/Moose Development/Moose/Wrapper/Identifiable.lua b/Moose Development/Moose/Wrapper/Identifiable.lua index 8561d5b2b..4f2928cb5 100644 --- a/Moose Development/Moose/Wrapper/Identifiable.lua +++ b/Moose Development/Moose/Wrapper/Identifiable.lua @@ -170,6 +170,27 @@ function IDENTIFIABLE:GetCoalition() return nil end +--- Returns true if identifiable is of RED coalition. +-- @param #IDENTIFIABLE self +-- @return #boolean If the identifiable is red. +function IDENTIFIABLE:IsRed() + return self:GetCoalition() == coalition.side.RED +end + +--- Returns true if identifiable is of BLUE coalition. +-- @param #IDENTIFIABLE self +-- @return #boolean If the identifiable is blue. +function IDENTIFIABLE:IsBlue() + return self:GetCoalition() == coalition.side.BLUE +end + +--- Returns true if identifiable is of NEUTRAL coalition. +-- @param #IDENTIFIABLE self +-- @return #boolean If the identifiable is neutral. +function IDENTIFIABLE:IsNeutral() + return self:GetCoalition() == coalition.side.NEUTRAL +end + --- Returns the name of the coalition of the Identifiable. -- @param #IDENTIFIABLE self -- @return #string The name of the coalition. diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 7be7ed135..69c949429 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -219,6 +219,25 @@ function POSITIONABLE:GetOrientationZ() end end +--- Returns the vectors of the orientation of the object: +-- X is the orientation parallel to the movement of the object, Z perpendicular and Y vertical orientation. +-- @param #POSITIONABLE self +-- @return Core.Vector#VECTOR X orientation, i.e. parallel to the direction of movement. +-- @return Core.Vector#VECTOR Y orientation, i.e. vertical. +-- @return Core.Vector#VECTOR Z orientation, i.e. perpendicular to the direction of movement. +function POSITIONABLE:GetOrientationVectors() + local position = self:GetPosition() + if position then + local vecx=VECTOR:NewFromVec(position.x) + local vecy=VECTOR:NewFromVec(position.y) + local vecz=VECTOR:NewFromVec(position.z) + return vecx, vecy, vecz + else + BASE:E( { "Cannot GetOrientation", Positionable = self, Alive = self:IsAlive() } ) + return nil, nil, nil + end +end + --- Returns the @{DCS#Position3} position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission. -- @param #POSITIONABLE self -- @return DCS#Position The 3D position vectors of the POSITIONABLE. @@ -286,6 +305,27 @@ function POSITIONABLE:GetVec2() return nil end +--- Returns the @{Core.Vector#VECTOR} indicating the 3D position of the object on the map. +-- @param #POSITIONABLE self +-- @return Core.Vector#VECTOR The 3D vector. +function POSITIONABLE:GetVector() + + local DCSPositionable = self:GetDCSObject() + + if DCSPositionable then + + local Vec3 = DCSPositionable:getPoint() -- DCS#Vec3 + + local vector=VECTOR:NewFromVec(Vec3) + + return vector + end + + self:E( { "Cannot GetVec2", Positionable = self, Alive = self:IsAlive() } ) + + return nil +end + --- Returns a COORDINATE object indicating the point in 2D of the POSITIONABLE within the mission. -- @param #POSITIONABLE self -- @return Core.Point#COORDINATE The 3D point vector of the POSITIONABLE. @@ -914,6 +954,24 @@ function POSITIONABLE:GetVelocityVec3() return nil end +--- Returns the velocity vector. +-- @param #POSITIONABLE self +-- @return Core.Vector#VECTOR The velocity vector or `nil` if the object does not exist. +function POSITIONABLE:GetVelocityVector() + + local DCSPositionable = self:GetDCSObject() + + if DCSPositionable and DCSPositionable:isExist() then + local vec3 = DCSPositionable:getVelocity() + local vector=VECTOR:NewFromVec(vec3) + return vector + end + + BASE:E( { "Cannot GetVelocityVector", Positionable = self, Alive = self:IsAlive() } ) + + return nil +end + --- Get relative velocity with respect to another POSITIONABLE. -- @param #POSITIONABLE self -- @param #POSITIONABLE Positionable Other POSITIONABLE.