diff --git a/Moose Development/LDT External Tools/Moose DOCUMENTATION Generate.launch b/Moose Development/LDT External Tools/Moose DOCUMENTATION Generate.launch index 85f0a3b46..43f710ca8 100644 --- a/Moose Development/LDT External Tools/Moose DOCUMENTATION Generate.launch +++ b/Moose Development/LDT External Tools/Moose DOCUMENTATION Generate.launch @@ -3,6 +3,6 @@ - + diff --git a/Moose Development/Moose/Actions/Act_Assign.lua b/Moose Development/Moose/Actions/Act_Assign.lua index 7d45ad21a..e04139698 100644 --- a/Moose Development/Moose/Actions/Act_Assign.lua +++ b/Moose Development/Moose/Actions/Act_Assign.lua @@ -173,8 +173,6 @@ do -- ACT_ASSIGN_ACCEPT local ProcessGroup = ProcessUnit:GetGroup() - self:Message( "You are assigned to the task " .. self.Task:GetName() ) - self.Task:Assign( ProcessUnit, ProcessUnit:GetPlayerName() ) end diff --git a/Moose Development/Moose/Core/Cargo.lua b/Moose Development/Moose/Core/Cargo.lua index 825bb5084..88635422f 100644 --- a/Moose Development/Moose/Core/Cargo.lua +++ b/Moose Development/Moose/Core/Cargo.lua @@ -239,12 +239,15 @@ function CARGO:New( Type, Name, Weight ) --R2.1 self:SetStartState( "UnLoaded" ) self:AddTransition( { "UnLoaded", "Boarding" }, "Board", "Boarding" ) self:AddTransition( "Boarding" , "Boarding", "Boarding" ) + self:AddTransition( "Boarding", "CancelBoarding", "UnLoaded" ) self:AddTransition( "Boarding", "Load", "Loaded" ) self:AddTransition( "UnLoaded", "Load", "Loaded" ) self:AddTransition( "Loaded", "UnBoard", "UnBoarding" ) self:AddTransition( "UnBoarding", "UnBoarding", "UnBoarding" ) self:AddTransition( "UnBoarding", "UnLoad", "UnLoaded" ) self:AddTransition( "Loaded", "UnLoad", "UnLoaded" ) + self:AddTransition( "*", "Destroyed", "Destroyed" ) + self:AddTransition( "*", "Respawn", "UnLoaded" ) self.Type = Type @@ -262,6 +265,7 @@ function CARGO:New( Type, Name, Weight ) --R2.1 CARGOS[self.Name] = self self:SetEventPriority( 5 ) + return self end @@ -273,6 +277,17 @@ function CARGO:GetName() --R2.1 return self.Name end +--- Get the object name of the Cargo. +-- @param #CARGO self +-- @return #string The object name of the Cargo. +function CARGO:GetObjectName() --R2.1 + if self:IsLoaded() then + return self.CargoCarrier:GetName() + else + return self.CargoObject:GetName() + end +end + --- Get the type of the Cargo. -- @param #CARGO self -- @return #string The type of the Cargo. @@ -280,6 +295,14 @@ function CARGO:GetType() return self.Type end +--- Get the current coordinates of the Cargo. +-- @param #CARGO self +-- @return Core.Point#COORDINATE The coordinates of the Cargo. +function CARGO:GetCoordinate() + return self.CargoObject:GetCoordinate() +end + + --- Check if cargo is loaded. -- @param #CARGO self -- @return #boolean true if loaded @@ -294,6 +317,20 @@ function CARGO:IsUnLoaded() return self:Is( "UnLoaded" ) end +--- Check if cargo is alive. +-- @param #CARGO self +-- @return #boolean true if unloaded +function CARGO:IsAlive() + + if self:IsLoaded() then + return self.CargoCarrier:IsAlive() + else + return self.CargoObject:IsAlive() + end +end + + + --- Template method to spawn a new representation of the CARGO in the simulator. -- @param #CARGO self @@ -329,7 +366,7 @@ end -- @param #number NearRadius The radius when the cargo will board the Carrier (to avoid collision). -- @return #boolean function CARGO:IsNear( PointVec2, NearRadius ) - self:F( { PointVec2 } ) + self:F( { PointVec2, NearRadius } ) local Distance = PointVec2:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) self:T( Distance ) @@ -520,6 +557,16 @@ function CARGO_UNIT:New( CargoUnit, Type, Name, Weight, NearRadius ) self:T( self.ClassName ) + self:HandleEvent( EVENTS.Dead, + --- @param #CARGO Cargo + -- @param Core.Event#EVENTDATA EventData + function( Cargo, EventData ) + if Cargo:GetObjectName() == EventData.IniUnit:GetName() then + self:E( { "Cargo destroyed", Cargo } ) + Cargo:Destroyed() + end + end + ) return self end @@ -542,7 +589,7 @@ end -- @param #string To -- @param Core.Point#POINT_VEC2 ToPointVec2 function CARGO_UNIT:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius ) - self:F() + self:F( { From, Event, To, ToPointVec2, NearRadius } ) NearRadius = NearRadius or 25 @@ -575,7 +622,7 @@ function CARGO_UNIT:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius -- Respawn the group... if self.CargoObject then self.CargoObject:ReSpawn( CargoDeployPointVec2:GetVec3(), CargoDeployHeading ) - self:E( { "CargoUnits:", self.CargoObject:GetGroup():GetName() } ) + self:F( { "CargoUnits:", self.CargoObject:GetGroup():GetName() } ) self.CargoCarrier = nil local Points = {} @@ -586,7 +633,8 @@ function CARGO_UNIT:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius local TaskRoute = self.CargoObject:TaskRoute( Points ) self.CargoObject:SetTask( TaskRoute, 1 ) - self:__UnBoarding( -1, ToPointVec2, NearRadius ) + + self:__UnBoarding( 1, ToPointVec2, NearRadius ) end end @@ -599,7 +647,7 @@ end -- @param #string To -- @param Core.Point#POINT_VEC2 ToPointVec2 function CARGO_UNIT:onleaveUnBoarding( From, Event, To, ToPointVec2, NearRadius ) - self:F( { ToPointVec2, From, Event, To } ) + self:F( { From, Event, To, ToPointVec2, NearRadius } ) NearRadius = NearRadius or 25 @@ -611,6 +659,7 @@ function CARGO_UNIT:onleaveUnBoarding( From, Event, To, ToPointVec2, NearRadius if self:IsNear( ToPointVec2, NearRadius ) then return true else + self:__UnBoarding( 1, ToPointVec2, NearRadius ) end return false @@ -625,7 +674,7 @@ end -- @param #string To -- @param Core.Point#POINT_VEC2 ToPointVec2 function CARGO_UNIT:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius ) - self:F( { ToPointVec2, From, Event, To } ) + self:F( { From, Event, To, ToPointVec2, NearRadius } ) NearRadius = NearRadius or 25 @@ -639,7 +688,7 @@ function CARGO_UNIT:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius end - self:__UnLoad( 1, ToPointVec2 ) + self:__UnLoad( 1, ToPointVec2, NearRadius ) end @@ -687,9 +736,9 @@ end -- @param #string From -- @param #string To function CARGO_UNIT:onafterBoard( From, Event, To, CargoCarrier, NearRadius, ... ) - self:F() + self:F( { From, Event, To, CargoCarrier, NearRadius } ) - NearRadius = NearRadius or 25 + local NearRadius = NearRadius or 25 self.CargoInAir = self.CargoObject:InAir() @@ -722,32 +771,13 @@ function CARGO_UNIT:onafterBoard( From, Event, To, CargoCarrier, NearRadius, ... local TaskRoute = self.CargoObject:TaskRoute( Points ) self.CargoObject:SetTask( TaskRoute, 2 ) self:__Boarding( -1, CargoCarrier, NearRadius ) + self.RunCount = 0 end end end ---- Leave Boarding State. --- @param #CARGO_UNIT self --- @param #string Event --- @param #string From --- @param #string To --- @param Wrapper.Unit#UNIT CargoCarrier -function CARGO_UNIT:onleaveBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) - self:F( { From, Event, To, CargoCarrier.UnitName, NearRadius } ) - - NearRadius = NearRadius or 25 - - if self:IsNear( CargoCarrier:GetPointVec2(), NearRadius ) then - self:__Load( 1, CargoCarrier, ... ) - return true - end - - return true -end - - --- Boarding Event. -- @param #CARGO_UNIT self -- @param #string Event @@ -759,8 +789,45 @@ function CARGO_UNIT:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, self:F( { From, Event, To, CargoCarrier.UnitName, NearRadius } ) - self:__Boarding( -1, CargoCarrier, NearRadius, ... ) - self:__Board( -15, CargoCarrier, NearRadius, ... ) + if CargoCarrier and CargoCarrier:IsAlive() then + if CargoCarrier:InAir() == false then + if self:IsNear( CargoCarrier:GetPointVec2(), NearRadius ) then + self:__Load( 1, CargoCarrier, ... ) + else + self:__Boarding( -1, CargoCarrier, NearRadius, ... ) + self.RunCount = self.RunCount + 1 + if self.RunCount >= 20 then + self.RunCount = 0 + local Speed = 90 + local Angle = 180 + local Distance = 5 + + NearRadius = NearRadius or 25 + + local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2() + local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees. + local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) + local CargoDeployPointVec2 = CargoCarrierPointVec2:Translate( Distance, CargoDeployHeading ) + + local Points = {} + + local PointStartVec2 = self.CargoObject:GetPointVec2() + + Points[#Points+1] = PointStartVec2:RoutePointGround( Speed ) + Points[#Points+1] = CargoDeployPointVec2:RoutePointGround( Speed ) + + local TaskRoute = self.CargoObject:TaskRoute( Points ) + self.CargoObject:SetTask( TaskRoute, 0.2 ) + end + end + else + self.CargoObject:MessageToGroup( "Cancelling Boarding... Get back on the ground!", 5, CargoCarrier:GetGroup(), self:GetName() ) + self:CancelBoarding( CargoCarrier, NearRadius, ... ) + self.CargoObject:SetCommand( self.CargoObject:CommandStopRoute( true ) ) + end + else + self:E("Something is wrong") + end end @@ -778,7 +845,7 @@ function CARGO_UNIT:onenterBoarding( From, Event, To, CargoCarrier, NearRadius, local Angle = 180 local Distance = 5 - NearRadius = NearRadius or 25 + local NearRadius = NearRadius or 25 if From == "UnLoaded" or From == "Boarding" then @@ -793,7 +860,7 @@ end -- @param #string To -- @param Wrapper.Unit#UNIT CargoCarrier function CARGO_UNIT:onenterLoaded( From, Event, To, CargoCarrier ) - self:F() + self:F( { From, Event, To, CargoCarrier } ) self.CargoCarrier = CargoCarrier @@ -808,6 +875,263 @@ end end + +do -- CARGO_GROUP + + --- @type CARGO_GROUP + -- @extends #CARGO_REPORTABLE + + --- # CARGO\_GROUP class + -- + -- The CARGO\_GROUP class defines a cargo that is represented by a @{Group} object within the simulator, and can be transported by a carrier. + -- Use the event functions as described above to Load, UnLoad, Board, UnBoard the CARGO\_GROUP to and from carrier. + -- + -- @field #CARGO_GROUP CARGO_GROUP + -- + CARGO_GROUP = { + ClassName = "CARGO_GROUP", + } + +--- CARGO_GROUP constructor. +-- @param #CARGO_GROUP self +-- @param Wrapper.Group#GROUP CargoGroup +-- @param #string Type +-- @param #string Name +-- @param #number ReportRadius (optional) +-- @param #number NearRadius (optional) +-- @return #CARGO_GROUP +function CARGO_GROUP:New( CargoGroup, Type, Name, ReportRadius ) + local self = BASE:Inherit( self, CARGO_REPORTABLE:New( CargoGroup, Type, Name, 0, ReportRadius ) ) -- #CARGO_GROUP + self:F( { Type, Name, ReportRadius } ) + + self.CargoSet = SET_CARGO:New() + + self.CargoObject = CargoGroup + + local WeightGroup = 0 + + for UnitID, UnitData in pairs( CargoGroup:GetUnits() ) do + local Unit = UnitData -- Wrapper.Unit#UNIT + local WeightUnit = Unit:GetDesc().massEmpty + WeightGroup = WeightGroup + WeightUnit + local CargoUnit = CARGO_UNIT:New( Unit, Type, Unit:GetName(), WeightUnit ) + self.CargoSet:Add( CargoUnit:GetName(), CargoUnit ) + end + + self:SetWeight( WeightGroup ) + + self:T( { "Weight Cargo", WeightGroup } ) + + -- Cargo objects are added to the _DATABASE and SET_CARGO objects. + _EVENTDISPATCHER:CreateEventNewCargo( self ) + + return self +end + +--- Enter Boarding State. +-- @param #CARGO_GROUP self +-- @param Wrapper.Unit#UNIT CargoCarrier +-- @param #string Event +-- @param #string From +-- @param #string To +function CARGO_GROUP:onenterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) + self:F( { CargoCarrier.UnitName, From, Event, To } ) + + local NearRadius = NearRadius or 25 + + if From == "UnLoaded" then + + -- For each Cargo object within the CARGO_GROUPED, route each object to the CargoLoadPointVec2 + self.CargoSet:ForEach( + function( Cargo, ... ) + Cargo:__Board( 1, CargoCarrier, NearRadius, ... ) + end, ... + ) + + self:__Boarding( 1, CargoCarrier, NearRadius, ... ) + end + +end + +--- Enter Loaded State. +-- @param #CARGO_GROUP self +-- @param Wrapper.Unit#UNIT CargoCarrier +-- @param #string Event +-- @param #string From +-- @param #string To +function CARGO_GROUP:onenterLoaded( From, Event, To, CargoCarrier, ... ) + self:F( { From, Event, To, CargoCarrier, ...} ) + + if From == "UnLoaded" then + -- For each Cargo object within the CARGO_GROUP, load each cargo to the CargoCarrier. + for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do + Cargo:Load( CargoCarrier ) + end + end + + self.CargoObject:Destroy() + self.CargoCarrier = CargoCarrier + +end + +--- Leave Boarding State. +-- @param #CARGO_GROUP self +-- @param Wrapper.Unit#UNIT CargoCarrier +-- @param #string Event +-- @param #string From +-- @param #string To +function CARGO_GROUP:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) + self:F( { CargoCarrier.UnitName, From, Event, To } ) + + local NearRadius = NearRadius or 25 + + local Boarded = true + local Cancelled = false + local Dead = true + + self.CargoSet:Flush() + + -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 + for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do + self:T( { Cargo:GetName(), Cargo.current } ) + if not Cargo:is( "Loaded" ) then + Boarded = false + end + + if Cargo:is( "UnLoaded" ) then + Cancelled = true + end + + if not Cargo:is( "Destroyed" ) then + Dead = false + end + + end + + if not Dead then + + if not Cancelled then + if not Boarded then + self:__Boarding( 1, CargoCarrier, NearRadius, ... ) + else + self:__Load( 1, CargoCarrier, ... ) + end + else + self:__CancelBoarding( 1, CargoCarrier, NearRadius, ... ) + end + else + self:__Destroyed( 1, CargoCarrier, NearRadius, ... ) + end + +end + +--- Enter UnBoarding State. +-- @param #CARGO_GROUP self +-- @param Core.Point#POINT_VEC2 ToPointVec2 +-- @param #string Event +-- @param #string From +-- @param #string To +function CARGO_GROUP:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) + self:F( {From, Event, To, ToPointVec2, NearRadius } ) + + NearRadius = NearRadius or 25 + + local Timer = 1 + + if From == "Loaded" then + + -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 + self.CargoSet:ForEach( + function( Cargo, NearRadius ) + + Cargo:__UnBoard( Timer, ToPointVec2, NearRadius ) + Timer = Timer + 10 + end, { NearRadius } + ) + + + self:__UnBoarding( 1, ToPointVec2, NearRadius, ... ) + end + +end + +--- Leave UnBoarding State. +-- @param #CARGO_GROUP self +-- @param Core.Point#POINT_VEC2 ToPointVec2 +-- @param #string Event +-- @param #string From +-- @param #string To +function CARGO_GROUP:onleaveUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) + self:F( { From, Event, To, ToPointVec2, NearRadius } ) + + --local NearRadius = NearRadius or 25 + + local Angle = 180 + local Speed = 10 + local Distance = 5 + + if From == "UnBoarding" then + local UnBoarded = true + + -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 + for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do + self:T( Cargo.current ) + if not Cargo:is( "UnLoaded" ) then + UnBoarded = false + end + end + + if UnBoarded then + return true + else + self:__UnBoarding( 1, ToPointVec2, NearRadius, ... ) + end + + return false + end + +end + +--- UnBoard Event. +-- @param #CARGO_GROUP self +-- @param Core.Point#POINT_VEC2 ToPointVec2 +-- @param #string Event +-- @param #string From +-- @param #string To +function CARGO_GROUP:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) + self:F( { From, Event, To, ToPointVec2, NearRadius } ) + + --local NearRadius = NearRadius or 25 + + self:__UnLoad( 1, ToPointVec2, ... ) +end + + + +--- Enter UnLoaded State. +-- @param #CARGO_GROUP self +-- @param Core.Point#POINT_VEC2 +-- @param #string Event +-- @param #string From +-- @param #string To +function CARGO_GROUP:onenterUnLoaded( From, Event, To, ToPointVec2, ... ) + self:F( { From, Event, To, ToPointVec2 } ) + + if From == "Loaded" then + + -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 + self.CargoSet:ForEach( + function( Cargo ) + Cargo:UnLoad( ToPointVec2 ) + end + ) + + end + +end + +end -- CARGO_GROUP + do -- CARGO_PACKAGE --- @type CARGO_PACKAGE @@ -1022,238 +1346,3 @@ end end - -do -- CARGO_GROUP - - --- @type CARGO_GROUP - -- @extends #CARGO_REPORTABLE - - --- # CARGO\_GROUP class - -- - -- The CARGO\_GROUP class defines a cargo that is represented by a @{Group} object within the simulator, and can be transported by a carrier. - -- Use the event functions as described above to Load, UnLoad, Board, UnBoard the CARGO\_GROUP to and from carrier. - -- - -- @field #CARGO_GROUP CARGO_GROUP - -- - CARGO_GROUP = { - ClassName = "CARGO_GROUP", - } - ---- CARGO_GROUP constructor. --- @param #CARGO_GROUP self --- @param Wrapper.Group#GROUP CargoGroup --- @param #string Type --- @param #string Name --- @param #number ReportRadius (optional) --- @param #number NearRadius (optional) --- @return #CARGO_GROUP -function CARGO_GROUP:New( CargoGroup, Type, Name, ReportRadius ) - local self = BASE:Inherit( self, CARGO_REPORTABLE:New( CargoGroup, Type, Name, 0, ReportRadius ) ) -- #CARGO_GROUP - self:F( { Type, Name, ReportRadius } ) - - self.CargoSet = SET_CARGO:New() - - self.CargoObject = CargoGroup - - local WeightGroup = 0 - - for UnitID, UnitData in pairs( CargoGroup:GetUnits() ) do - local Unit = UnitData -- Wrapper.Unit#UNIT - local WeightUnit = Unit:GetDesc().massEmpty - WeightGroup = WeightGroup + WeightUnit - local CargoUnit = CARGO_UNIT:New( Unit, Type, Unit:GetName(), WeightUnit ) - self.CargoSet:Add( CargoUnit:GetName(), CargoUnit ) - end - - self:SetWeight( WeightGroup ) - - self:T( { "Weight Cargo", WeightGroup } ) - - -- Cargo objects are added to the _DATABASE and SET_CARGO objects. - _EVENTDISPATCHER:CreateEventNewCargo( self ) - - return self -end - ---- Enter Boarding State. --- @param #CARGO_GROUP self --- @param Wrapper.Unit#UNIT CargoCarrier --- @param #string Event --- @param #string From --- @param #string To -function CARGO_GROUP:onenterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) - self:F( { CargoCarrier.UnitName, From, Event, To } ) - - NearRadius = NearRadius or 25 - - if From == "UnLoaded" then - - -- For each Cargo object within the CARGO_GROUPED, route each object to the CargoLoadPointVec2 - self.CargoSet:ForEach( - function( Cargo ) - Cargo:__Board( 1, CargoCarrier, NearRadius ) - end - ) - - self:__Boarding( 1, CargoCarrier, NearRadius, ... ) - end - -end - ---- Enter Loaded State. --- @param #CARGO_GROUP self --- @param Wrapper.Unit#UNIT CargoCarrier --- @param #string Event --- @param #string From --- @param #string To -function CARGO_GROUP:onenterLoaded( From, Event, To, CargoCarrier, ... ) - self:F( { CargoCarrier.UnitName, From, Event, To } ) - - if From == "UnLoaded" then - -- For each Cargo object within the CARGO_GROUP, load each cargo to the CargoCarrier. - for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do - Cargo:Load( CargoCarrier ) - end - end - - self.CargoObject:Destroy() - self.CargoCarrier = CargoCarrier - -end - ---- Leave Boarding State. --- @param #CARGO_GROUP self --- @param Wrapper.Unit#UNIT CargoCarrier --- @param #string Event --- @param #string From --- @param #string To -function CARGO_GROUP:onleaveBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) - self:F( { CargoCarrier.UnitName, From, Event, To } ) - - NearRadius = NearRadius or 25 - - local Boarded = true - - self.CargoSet:Flush() - - -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 - for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do - self:T( { Cargo:GetName(), Cargo.current } ) - if not Cargo:is( "Loaded" ) then - Boarded = false - end - end - - if not Boarded then - self:__Boarding( 1, CargoCarrier, NearRadius, ... ) - else - self:__Load( 1, CargoCarrier, ... ) - end - return Boarded -end - ---- Enter UnBoarding State. --- @param #CARGO_GROUP self --- @param Core.Point#POINT_VEC2 ToPointVec2 --- @param #string Event --- @param #string From --- @param #string To -function CARGO_GROUP:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) - self:F({From, Event, To, ToPointVec2, NearRadius}) - - NearRadius = NearRadius or 25 - - local Timer = 1 - - if From == "Loaded" then - - -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 - self.CargoSet:ForEach( - function( Cargo ) - Cargo:__UnBoard( Timer, ToPointVec2, NearRadius ) - Timer = Timer + 10 - end - ) - - self:__UnBoarding( 1, ToPointVec2, NearRadius, ... ) - end - -end - ---- Leave UnBoarding State. --- @param #CARGO_GROUP self --- @param Core.Point#POINT_VEC2 ToPointVec2 --- @param #string Event --- @param #string From --- @param #string To -function CARGO_GROUP:onleaveUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) - self:F( { From, Event, To, ToPointVec2, NearRadius } ) - - NearRadius = NearRadius or 25 - - local Angle = 180 - local Speed = 10 - local Distance = 5 - - if From == "UnBoarding" then - local UnBoarded = true - - -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 - for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do - self:T( Cargo.current ) - if not Cargo:is( "UnLoaded" ) then - UnBoarded = false - end - end - - if UnBoarded then - return true - else - self:__UnBoarding( 1, ToPointVec2, NearRadius, ... ) - end - - return false - end - -end - ---- UnBoard Event. --- @param #CARGO_GROUP self --- @param Core.Point#POINT_VEC2 ToPointVec2 --- @param #string Event --- @param #string From --- @param #string To -function CARGO_GROUP:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) - self:F( { From, Event, To, ToPointVec2, NearRadius } ) - - NearRadius = NearRadius or 25 - - self:__UnLoad( 1, ToPointVec2, ... ) -end - - - ---- Enter UnLoaded State. --- @param #CARGO_GROUP self --- @param Core.Point#POINT_VEC2 --- @param #string Event --- @param #string From --- @param #string To -function CARGO_GROUP:onenterUnLoaded( From, Event, To, ToPointVec2, ... ) - self:F( { From, Event, To, ToPointVec2 } ) - - if From == "Loaded" then - - -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 - self.CargoSet:ForEach( - function( Cargo ) - Cargo:UnLoad( ToPointVec2 ) - end - ) - - end - -end - -end -- CARGO_GROUP - diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index eebce2873..c9f7ed174 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -51,6 +51,7 @@ DATABASE = { ClientsByID = {}, }, UNITS = {}, + UNITS_Index = {}, STATICS = {}, GROUPS = {}, PLAYERS = {}, @@ -99,7 +100,7 @@ function DATABASE:New() self:HandleEvent( EVENTS.DeleteCargo ) -- Follow alive players and clients - self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit ) +-- self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit ) self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventOnPlayerLeaveUnit ) self:_RegisterTemplates() @@ -108,6 +109,33 @@ function DATABASE:New() self:_RegisterStatics() self:_RegisterPlayers() self:_RegisterAirbases() + + self.UNITS_Position = 0 + + --- @param #DATABASE self + local function CheckPlayers( self ) + + local UNITS_Count = #self.UNITS_Index + if UNITS_Count > 0 then + self.UNITS_Position = ( ( self.UNITS_Position <= UNITS_Count ) and self.UNITS_Position + 1 ) or 1 + local PlayerUnit = self.UNITS[self.UNITS_Index[self.UNITS_Position]] + if PlayerUnit then + local UnitName = PlayerUnit:GetName() + local PlayerName = PlayerUnit:GetPlayerName() + --self:E( { UNITS_Count, self.UNITS_Position, UnitName, PlayerName } ) + if PlayerName and PlayerName ~= "" then + if self.PLAYERS[PlayerName] == nil or self.PLAYERS[PlayerName] ~= UnitName then + self:E( { "Add player for unit:", UnitName, PlayerName } ) + self:AddPlayer( UnitName, PlayerName ) + --_EVENTDISPATCHER:CreateEventPlayerEnterUnit( PlayerUnit ) + end + end + end + end + end + + self:E( "Scheduling" ) + --local PlayerCheckSchedule = SCHEDULER:New( nil, CheckPlayers, { self }, 2, 0.1 ) return self end @@ -130,6 +158,8 @@ function DATABASE:AddUnit( DCSUnitName ) if not self.UNITS[DCSUnitName] then local UnitRegister = UNIT:Register( DCSUnitName ) self.UNITS[DCSUnitName] = UNIT:Register( DCSUnitName ) + + table.insert( self.UNITS_Index, DCSUnitName ) end return self.UNITS[DCSUnitName] @@ -140,7 +170,7 @@ end -- @param #DATABASE self function DATABASE:DeleteUnit( DCSUnitName ) - --self.UNITS[DCSUnitName] = nil + self.UNITS[DCSUnitName] = nil end --- Adds a Static based on the Static Name in the DATABASE. @@ -281,7 +311,7 @@ function DATABASE:AddPlayer( UnitName, PlayerName ) if PlayerName then self:E( { "Add player for unit:", UnitName, PlayerName } ) - self.PLAYERS[PlayerName] = self:FindUnit( UnitName ) + self.PLAYERS[PlayerName] = UnitName self.PLAYERSJOINED[PlayerName] = PlayerName end end diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index 8bbaeade6..f9da8ec67 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -730,6 +730,21 @@ do -- Event Creation world.onEvent( Event ) end + --- Creation of a S_EVENT_PLAYER_ENTER_UNIT Event. + -- @param #EVENT self + -- @param Wrapper.Unit#UNIT PlayerUnit. + function EVENT:CreateEventPlayerEnterUnit( PlayerUnit ) + self:F( { PlayerUnit } ) + + local Event = { + id = EVENTS.PlayerEnterUnit, + time = timer.getTime(), + initiator = PlayerUnit:GetDCSObject() + } + + world.onEvent( Event ) + end + end --- @param #EVENT self @@ -748,7 +763,7 @@ function EVENT:onEvent( Event ) local EventMeta = _EVENTMETA[Event.id] - + if self and self.Events and self.Events[Event.id] then if Event.initiator then @@ -863,7 +878,7 @@ function EVENT:onEvent( Event ) local PriorityBegin = PriorityOrder == -1 and 5 or 1 local PriorityEnd = PriorityOrder == -1 and 1 or 5 - if Event.IniObjectCategory ~= 3 then + if Event.IniObjectCategory ~= Object.Category.STATIC then self:E( { EventMeta.Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } ) end @@ -874,8 +889,10 @@ function EVENT:onEvent( Event ) -- Okay, we got the event from DCS. Now loop the SORTED self.EventSorted[] table for the received Event.id, and for each EventData registered, check if a function needs to be called. for EventClass, EventData in pairs( self.Events[Event.id][EventPriority] ) do - self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } ) - + if Event.IniObjectCategory ~= Object.Category.STATIC then + --self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } ) + end + Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName ) Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName ) diff --git a/Moose Development/Moose/Core/Fsm.lua b/Moose Development/Moose/Core/Fsm.lua index 972071634..51adf9b86 100644 --- a/Moose Development/Moose/Core/Fsm.lua +++ b/Moose Development/Moose/Core/Fsm.lua @@ -1003,15 +1003,20 @@ do -- FSM_PROCESS -- @param #FSM_PROCESS self -- @return #FSM_PROCESS function FSM_PROCESS:Remove() - self:T( { self:GetClassNameAndID() } ) - + self:F( { self:GetClassNameAndID() } ) + + self:F( "Clearing Schedules" ) + self.CallScheduler:Clear() + -- Copy Processes for ProcessID, Process in pairs( self:GetProcesses() ) do self:E( { Process} ) - Process.fsm:Remove() - Process.fsm = nil + if Process.fsm then + Process.fsm:Remove() + Process.fsm = nil + end end - + return self end @@ -1116,7 +1121,7 @@ end self:T( { ProcessUnit, From, Event, To, Dummy, self:IsTrace() } ) if self:IsTrace() then - MESSAGE:New( "@ Process " .. self:GetClassNameAndID() .. " : " .. Event .. " changed to state " .. To, 2 ):ToAll() + --MESSAGE:New( "@ Process " .. self:GetClassNameAndID() .. " : " .. Event .. " changed to state " .. To, 2 ):ToAll() end self:T( { Scores = self._Scores, To = To } ) diff --git a/Moose Development/Moose/Core/Menu.lua b/Moose Development/Moose/Core/Menu.lua index 8e361f41f..b33d8ef2c 100644 --- a/Moose Development/Moose/Core/Menu.lua +++ b/Moose Development/Moose/Core/Menu.lua @@ -82,7 +82,7 @@ do -- MENU_BASE -- @param #string MenuText The text of the child menu. -- @return #MENU_BASE function MENU_BASE:GetMenu( MenuText ) - self:F( { self.Menus, MenuText } ) + self:F2( { Menu = self.Menus[MenuText] } ) return self.Menus[MenuText] end @@ -91,7 +91,7 @@ do -- MENU_BASE -- @param #boolean RemoveParent If true, the parent menu is automatically removed when this menu is the last child menu of that parent @{Menu}. -- @return #MENU_BASE function MENU_BASE:SetRemoveParent( RemoveParent ) - self:F( { RemoveParent } ) + self:F2( { RemoveParent } ) self.MenuRemoveParent = RemoveParent return self end @@ -780,7 +780,9 @@ do self = MenuGroup._Menus[Path] else self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) - MenuGroup._Menus[Path] = self + --if MenuGroup:IsAlive() then + MenuGroup._Menus[Path] = self + --end self.MenuGroup = MenuGroup self.Path = Path @@ -808,9 +810,9 @@ do -- @param MenuTime -- @return #MENU_GROUP self function MENU_GROUP:RemoveSubMenus( MenuTime ) - self:F2( { self.MenuPath, MenuTime, self.MenuTime } ) + --self:F2( { self.MenuPath, MenuTime, self.MenuTime } ) - self:T( { "Removing Group SubMenus:", self.MenuGroup:GetName(), self.MenuPath } ) + --self:T( { "Removing Group SubMenus:", self.MenuGroup:GetName(), self.MenuPath } ) for MenuText, Menu in pairs( self.Menus ) do Menu:Remove( MenuTime ) end @@ -823,7 +825,7 @@ do -- @param MenuTime -- @return #nil function MENU_GROUP:Remove( MenuTime ) - self:F( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } ) + --self:F2( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } ) self:RemoveSubMenus( MenuTime ) @@ -837,12 +839,12 @@ do self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1 if self.ParentMenu.MenuCount == 0 then if self.MenuRemoveParent == true then - self:T( "Removing Parent Menu " ) + self:T2( "Removing Parent Menu " ) self.ParentMenu:Remove() end end end - self:T( { "Removing Group Menu:", self.MenuGroup:GetName(), self.MenuGroup._Menus[self.Path].Path } ) + self:T( { "Removing Group Menu:", MenuGroup = self.MenuGroup:GetName(), MenuPath = self.MenuGroup._Menus[self.Path].Path } ) self.MenuGroup._Menus[self.Path] = nil self = nil end @@ -880,10 +882,13 @@ do local Path = ( ParentMenu and ( table.concat( ParentMenu.MenuPath or {}, "@" ) .. "@" .. MenuText ) ) or MenuText if MenuGroup._Menus[Path] then self = MenuGroup._Menus[Path] - self:T( { "Re-using Group Command Menu:", MenuGroup:GetName(), MenuText } ) + self:F2( { "Re-using Group Command Menu:", MenuGroup:GetName(), MenuText } ) else self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) ) - MenuGroup._Menus[Path] = self + + --if MenuGroup:IsAlive() then + MenuGroup._Menus[Path] = self + --end self.Path = Path self.MenuGroup = MenuGroup @@ -891,13 +896,13 @@ do self.MenuText = MenuText self.ParentMenu = ParentMenu - self:T( { "Adding Group Command Menu:", MenuGroup:GetName(), MenuText, self.MenuParentPath } ) + self:F( { "Adding Group Command Menu:", MenuGroup = MenuGroup:GetName(), MenuText = MenuText, MenuPath = self.MenuParentPath } ) self.MenuPath = missionCommands.addCommandForGroup( self.MenuGroupID, MenuText, self.MenuParentPath, self.MenuCallHandler, arg ) if self.ParentMenu and self.ParentMenu.Menus then self.ParentMenu.Menus[MenuText] = self self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1 - self:F( { ParentMenu.Menus, MenuText } ) + self:F2( { ParentMenu.Menus, MenuText } ) end end @@ -909,20 +914,20 @@ do -- @param MenuTime -- @return #nil function MENU_GROUP_COMMAND:Remove( MenuTime ) - self:F( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } ) + --self:F2( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } ) if not MenuTime or self.MenuTime ~= MenuTime then if self.MenuGroup._Menus[self.Path] then self = self.MenuGroup._Menus[self.Path] missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) - self:T( { "Removing Group Command Menu:", self.MenuGroup:GetName(), self.MenuText, self.Path, self.MenuGroup._Menus[self.Path].Path } ) + self:T( { "Removing Group Command Menu:", MenuGroup = self.MenuGroup:GetName(), MenuText = self.MenuText, MenuPath = self.Path } ) self.ParentMenu.Menus[self.MenuText] = nil self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1 if self.ParentMenu.MenuCount == 0 then if self.MenuRemoveParent == true then - self:T( "Removing Parent Menu " ) + self:T2( "Removing Parent Menu " ) self.ParentMenu:Remove() end end diff --git a/Moose Development/Moose/Core/ScheduleDispatcher.lua b/Moose Development/Moose/Core/ScheduleDispatcher.lua index a7962e677..9db739ae1 100644 --- a/Moose Development/Moose/Core/ScheduleDispatcher.lua +++ b/Moose Development/Moose/Core/ScheduleDispatcher.lua @@ -191,7 +191,7 @@ function SCHEDULEDISPATCHER:Start( Scheduler, CallID ) ) end else - for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do + for CallID, Schedule in pairs( self.Schedule[Scheduler] or {} ) do self:Start( Scheduler, CallID ) -- Recursive end end @@ -209,7 +209,7 @@ function SCHEDULEDISPATCHER:Stop( Scheduler, CallID ) Schedule[CallID].ScheduleID = nil end else - for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do + for CallID, Schedule in pairs( self.Schedule[Scheduler] or {} ) do self:Stop( Scheduler, CallID ) -- Recursive end end @@ -218,7 +218,7 @@ end function SCHEDULEDISPATCHER:Clear( Scheduler ) self:F2( { Scheduler = Scheduler } ) - for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do + for CallID, Schedule in pairs( self.Schedule[Scheduler] or {} ) do self:Stop( Scheduler, CallID ) -- Recursive end end diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 196982ffb..6ca7f6d5d 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -165,11 +165,10 @@ end -- @param #SET_BASE self -- @param #string ObjectName function SET_BASE:Remove( ObjectName ) - self:F( ObjectName ) local t = self.Set[ObjectName] - self:E( { ObjectName, t } ) + self:F3( { ObjectName, t } ) if t then if t._next then @@ -1337,6 +1336,11 @@ SET_UNIT = { } +--- Get the first unit from the set. +-- @function [parent=#SET_UNIT] GetFirst +-- @param #SET_UNIT self +-- @return Wrapper.Unit#UNIT The UNIT object. + --- Creates a new SET_UNIT object, building a set of units belonging to a coalitions, categories, countries, types or with defined prefix names. -- @param #SET_UNIT self -- @return #SET_UNIT @@ -1568,7 +1572,7 @@ end -- @return #string The name of the UNIT -- @return #table The UNIT function SET_UNIT:FindInDatabase( Event ) - self:E( { Event.IniDCSUnitName, self.Set[Event.IniDCSUnitName], Event } ) + self:F2( { Event.IniDCSUnitName, self.Set[Event.IniDCSUnitName], Event } ) return Event.IniDCSUnitName, self.Set[Event.IniDCSUnitName] diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 998bd07fa..5f41cf733 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -731,7 +731,7 @@ end -- @param #ZONE_UNIT self -- @return Dcs.DCSTypes#Vec2 The location of the zone based on the @{Unit#UNIT}location. function ZONE_UNIT:GetVec2() - self:F( self.ZoneName ) + self:F2( self.ZoneName ) local ZoneVec2 = self.ZoneUNIT:GetVec2() if ZoneVec2 then @@ -741,7 +741,7 @@ function ZONE_UNIT:GetVec2() return self.LastVec2 end - self:T( { ZoneVec2 } ) + self:T2( { ZoneVec2 } ) return nil end diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 418767218..f6eb2dc97 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -190,7 +190,7 @@ do -- DETECTION_BASE -- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. -- -- -- Build a detect object. - -- local Detection = DETECTION_BASE:New( SetGroup ) + -- local Detection = DETECTION_UNITS:New( SetGroup ) -- -- -- This will accept detected units if the range is below 5000 meters. -- Detection:SetAcceptRange( 5000 ) @@ -211,7 +211,7 @@ do -- DETECTION_BASE -- local ZoneAccept2 = ZONE:New( "AcceptZone2" ) -- -- -- Build a detect object. - -- local Detection = DETECTION_BASE:New( SetGroup ) + -- local Detection = DETECTION_UNITS:New( SetGroup ) -- -- -- This will accept detected units by Detection when the unit is within ZoneAccept1 OR ZoneAccept2. -- Detection:SetAcceptZones( { ZoneAccept1, ZoneAccept2 } ) @@ -232,7 +232,7 @@ do -- DETECTION_BASE -- local ZoneReject2 = ZONE:New( "RejectZone2" ) -- -- -- Build a detect object. - -- local Detection = DETECTION_BASE:New( SetGroup ) + -- local Detection = DETECTION_UNITS:New( SetGroup ) -- -- -- This will reject detected units by Detection when the unit is within ZoneReject1 OR ZoneReject2. -- Detection:SetRejectZones( { ZoneReject1, ZoneReject2 } ) @@ -273,10 +273,16 @@ do -- DETECTION_BASE --- @type DETECTION_BASE.DetectedObject -- @field #string Name - -- @field #boolean Visible + -- @field #boolean IsVisible + -- @field #boolean KnowType + -- @field #boolean KnowDistance -- @field #string Type -- @field #number Distance -- @field #boolean Identified + -- @field #number LastTime + -- @field #boolean LastPos + -- @field #number LastVelocity + --- @type DETECTION_BASE.DetectedItems -- @list <#DETECTION_BASE.DetectedItem> @@ -309,12 +315,12 @@ do -- DETECTION_BASE self.DetectionInterval = 30 - self:InitDetectVisual( false ) - self:InitDetectOptical( false ) - self:InitDetectRadar( false ) - self:InitDetectRWR( false ) - self:InitDetectIRST( false ) - self:InitDetectDLINK( false ) + self:InitDetectVisual( nil ) + self:InitDetectOptical( nil ) + self:InitDetectRadar( nil ) + self:InitDetectRWR( nil ) + self:InitDetectIRST( nil ) + self:InitDetectDLINK( nil ) self:FilterCategories( { Unit.Category.AIRPLANE, @@ -531,6 +537,7 @@ do -- DETECTION_BASE self:T( { "DetectionGroup is Alive", DetectionGroup:GetName() } ) local DetectionGroupName = DetectionGroup:GetName() + local DetectionUnit = DetectionGroup:GetUnit(1) local DetectedUnits = {} @@ -550,6 +557,18 @@ do -- DETECTION_BASE if DetectedObject and DetectedObject:isExist() and DetectedObject.id_ < 50000000 then -- and ( DetectedObject:getCategory() == Object.Category.UNIT or DetectedObject:getCategory() == Object.Category.STATIC ) then + local TargetIsDetected, TargetIsVisible, TargetLastTime, TargetKnowType, TargetKnowDistance, TargetLastPos, TargetLastVelocity = DetectionUnit:IsTargetDetected( + DetectedObject, + self.DetectVisual, + self.DetectOptical, + self.DetectRadar, + self.DetectIRST, + self.DetectRWR, + self.DetectDLINK + ) + + self:T2( { TargetIsDetected = TargetIsDetected, TargetIsVisible = TargetIsVisible, TargetLastTime = TargetLastTime, TargetKnowType = TargetKnowType, TargetKnowDistance = TargetKnowDistance, TargetLastPos = TargetLastPos, TargetLastVelocity = TargetLastVelocity } ) + local DetectionAccepted = true local DetectedObjectName = DetectedObject:getName() @@ -566,7 +585,7 @@ do -- DETECTION_BASE local DetectedUnitCategory = DetectedObject:getDesc().category - self:T( { "Detected Target:", DetectionGroupName, DetectedObjectName, Distance, DetectedUnitCategory } ) + self:T2( { "Detected Target:", DetectionGroupName, DetectedObjectName, Distance, DetectedUnitCategory } ) -- Calculate Acceptance @@ -648,12 +667,14 @@ do -- DETECTION_BASE HasDetectedObjects = true - if not self.DetectedObjects[DetectedObjectName] then - self.DetectedObjects[DetectedObjectName] = {} - end + self.DetectedObjects[DetectedObjectName] = self.DetectedObjects[DetectedObjectName] or {} self.DetectedObjects[DetectedObjectName].Name = DetectedObjectName - self.DetectedObjects[DetectedObjectName].Visible = Detection.visible - self.DetectedObjects[DetectedObjectName].Type = Detection.type + self.DetectedObjects[DetectedObjectName].IsVisible = TargetIsVisible + self.DetectedObjects[DetectedObjectName].LastTime = TargetLastTime + self.DetectedObjects[DetectedObjectName].LastPos = TargetLastPos + self.DetectedObjects[DetectedObjectName].LastVelocity = TargetLastVelocity + self.DetectedObjects[DetectedObjectName].KnowType = TargetKnowType + self.DetectedObjects[DetectedObjectName].KnowDistance = Detection.distance -- TargetKnowDistance self.DetectedObjects[DetectedObjectName].Distance = Distance local DetectedUnit = UNIT:FindByName( DetectedObjectName ) @@ -707,7 +728,7 @@ do -- DETECTION_BASE local DetectedSet = DetectedItem.Set if DetectedSet:Count() == 0 then - self.DetectedItems[DetectedItemID] = nil + self:RemoveDetectedItem(DetectedItemID) end end @@ -1133,7 +1154,7 @@ do -- DETECTION_BASE -- @param #string ObjectName -- @return #DETECTION_BASE.DetectedObject function DETECTION_BASE:GetDetectedObject( ObjectName ) - self:F( ObjectName ) + self:F2( ObjectName ) if ObjectName then local DetectedObject = self.DetectedObjects[ObjectName] @@ -1200,8 +1221,10 @@ do -- DETECTION_BASE -- @param #number DetectedItemIndex The index or position in the DetectedItems list where the item needs to be removed. function DETECTION_BASE:RemoveDetectedItem( DetectedItemIndex ) - self.DetectedItemCount = self.DetectedItemCount - 1 - self.DetectedItems[DetectedItemIndex] = nil + if self.DetectedItems[DetectedItemIndex] then + self.DetectedItemCount = self.DetectedItemCount - 1 + self.DetectedItems[DetectedItemIndex] = nil + end end @@ -1215,7 +1238,7 @@ do -- DETECTION_BASE --- Get the amount of SETs with detected objects. -- @param #DETECTION_BASE self - -- @return #number Count + -- @return #number The amount of detected items. Note that the amount of detected items can differ with the reality, because detections are not real-time but doen in intervals! function DETECTION_BASE:GetDetectedItemsCount() local DetectedCount = self.DetectedItemCount @@ -1435,7 +1458,6 @@ do -- DETECTION_UNITS for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do local DetectedItemSet = DetectedItem.Set -- Core.Set#SET_UNIT - local DetectedTypeName = DetectedItem.Type for DetectedUnitName, DetectedUnitData in pairs( DetectedItemSet:GetSet() ) do local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT @@ -1450,6 +1472,17 @@ do -- DETECTION_UNITS -- Yes, the DetectedUnit is still detected or exists. Flag as identified. self:IdentifyDetectedObject( DetectedObject ) + + -- Update the detection with the new data provided. + DetectedItem.TypeName = DetectedUnit:GetTypeName() + DetectedItem.Name = DetectedObject.Name + DetectedItem.IsVisible = DetectedObject.IsVisible + DetectedItem.LastTime = DetectedObject.LastTime + DetectedItem.LastPos = DetectedObject.LastPos + DetectedItem.LastVelocity = DetectedObject.LastVelocity + DetectedItem.KnowType = DetectedObject.KnowType + DetectedItem.KnowDistance = DetectedObject.KnowDistance + DetectedItem.Distance = DetectedObject.Distance else -- There was no DetectedObject, remove DetectedUnit from the Set. self:AddChangeUnit( DetectedItem, "RU", DetectedUnitName ) @@ -1474,10 +1507,15 @@ do -- DETECTION_UNITS if not DetectedItem then self:T( "Added new DetectedItem" ) DetectedItem = self:AddDetectedItem( "UNIT", DetectedUnitName ) - DetectedItem.Type = DetectedUnit:GetTypeName() - DetectedItem.Name = DetectedObjectData.Name - DetectedItem.Visible = DetectedObjectData.Visible - DetectedItem.Distance = DetectedObjectData.Distance + DetectedItem.TypeName = DetectedUnit:GetTypeName() + DetectedItem.Name = DetectedObject.Name + DetectedItem.IsVisible = DetectedObject.IsVisible + DetectedItem.LastTime = DetectedObject.LastTime + DetectedItem.LastPos = DetectedObject.LastPos + DetectedItem.LastVelocity = DetectedObject.LastVelocity + DetectedItem.KnowType = DetectedObject.KnowType + DetectedItem.KnowDistance = DetectedObject.KnowDistance + DetectedItem.Distance = DetectedObject.Distance end DetectedItem.Set:AddUnit( DetectedUnit ) @@ -1557,19 +1595,27 @@ do -- DETECTION_UNITS if DetectedItemUnit and DetectedItemUnit:IsAlive() then self:T(DetectedItemUnit) - local UnitCategoryName = DetectedItemUnit:GetCategoryName() or "" - local UnitCategoryType = DetectedItemUnit:GetTypeName() or "" - if DetectedItem.Type and UnitCategoryName and UnitCategoryType then - UnitCategoryText = UnitCategoryName .. " (" .. UnitCategoryType .. ") at " + if DetectedItem.KnowType then + local UnitCategoryName = DetectedItemUnit:GetCategoryName() + if UnitCategoryName then + UnitCategoryText = UnitCategoryName + end + if DetectedItem.TypeName then + UnitCategoryText = UnitCategoryText .. " (" .. DetectedItem.TypeName .. ")" + end else - UnitCategoryText = "Unknown target at " + UnitCategoryText = "Unknown" end - if DetectedItem.Visible == false then - UnitDistanceText = string.format( "%.2f", DetectedItem.Distance ) .. " km, estimated" + if DetectedItem.KnowDistance then + if DetectedItem.IsVisible then + UnitDistanceText = " at " .. string.format( "%.2f", DetectedItem.Distance ) .. " km" + end else - UnitDistanceText = string.format( "%.2f", DetectedItem.Distance ) .. " km, visual" + if DetectedItem.IsVisible then + UnitDistanceText = " at +/- " .. string.format( "%.0f", DetectedItem.Distance ) .. " km" + end end local DetectedItemCoordinate = DetectedItemUnit:GetCoordinate() @@ -1699,7 +1745,7 @@ do -- DETECTION_TYPES for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do local DetectedItemSet = DetectedItem.Set -- Core.Set#SET_UNIT - local DetectedTypeName = DetectedItem.Type + local DetectedTypeName = DetectedItem.TypeName for DetectedUnitName, DetectedUnitData in pairs( DetectedItemSet:GetSet() ) do local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT @@ -1736,7 +1782,7 @@ do -- DETECTION_TYPES local DetectedItem = self:GetDetectedItem( DetectedTypeName ) if not DetectedItem then DetectedItem = self:AddDetectedItem( "TYPE", DetectedTypeName ) - DetectedItem.Type = DetectedUnit:GetTypeName() + DetectedItem.TypeName = DetectedUnit:GetTypeName() end DetectedItem.Set:AddUnit( DetectedUnit ) @@ -1808,7 +1854,7 @@ do -- DETECTION_TYPES local ThreatLevelA2G = DetectedSet:CalculateThreatLevelA2G() local DetectedItemsCount = DetectedSet:Count() - local DetectedItemType = DetectedItem.Type + local DetectedItemType = DetectedItem.TypeName local DetectedItemUnit = DetectedSet:GetFirst() @@ -1967,7 +2013,7 @@ do -- DETECTION_AREAS local ThreatLevelA2G = self:GetTreatLevelA2G( DetectedItem ) local DetectedItemsCount = DetectedSet:Count() local DetectedItemsTypes = DetectedSet:GetTypeNames() - + local ReportSummary = string.format( "%s - %s - Threat:[%s](%2d)\n %2d of %s", DetectedItemID, @@ -1983,6 +2029,25 @@ do -- DETECTION_AREAS return nil end + + --- Report detailed of a detection result. + -- @param #DETECTION_AREAS self + -- @return #string + function DETECTION_AREAS:DetectedReportDetailed() --R2.1 Fixed missing report + self:F() + + local Report = REPORT:New( "Detected areas:" ) + for DetectedItemIndex, DetectedItem in pairs( self.DetectedItems ) do + local DetectedItem = DetectedItem -- #DETECTION_BASE.DetectedItem + local ReportSummary = self:DetectedItemReportSummary( DetectedItemIndex ) + Report:Add( ReportSummary ) + end + + local ReportText = Report:Text() + + return ReportText + end + --- Returns if there are friendlies nearby the FAC units ... diff --git a/Moose Development/Moose/Functional/MissileTrainer.lua b/Moose Development/Moose/Functional/MissileTrainer.lua index 511cbca44..38b2008ae 100644 --- a/Moose Development/Moose/Functional/MissileTrainer.lua +++ b/Moose Development/Moose/Functional/MissileTrainer.lua @@ -442,7 +442,7 @@ function MISSILETRAINER._MenuMessages( MenuParameters ) if MenuParameters.Distance ~= nil then self.Distance = MenuParameters.Distance - MESSAGE:New( "Hit detection distance set to " .. self.Distance .. " meters", 15, "Menu" ):ToAll() + MESSAGE:New( "Hit detection distance set to " .. self.Distance * 1000 .. " meters", 15, "Menu" ):ToAll() end end diff --git a/Moose Development/Moose/Functional/Spawn.lua b/Moose Development/Moose/Functional/Spawn.lua index c6faf2ee8..6e9386a7d 100644 --- a/Moose Development/Moose/Functional/Spawn.lua +++ b/Moose Development/Moose/Functional/Spawn.lua @@ -1595,11 +1595,13 @@ function SPAWN:_OnBirth( EventData ) if SpawnGroup then local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) - self:T( { "Birth Event:", EventPrefix, self.SpawnTemplatePrefix } ) - if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then - self.AliveUnits = self.AliveUnits + 1 - self:T( "Alive Units: " .. self.AliveUnits ) - end + if EventPrefix then -- EventPrefix can be nil if no # is found, which means, no spawnable group! + self:T( { "Birth Event:", EventPrefix, self.SpawnTemplatePrefix } ) + if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then + self.AliveUnits = self.AliveUnits + 1 + self:T( "Alive Units: " .. self.AliveUnits ) + end + end end end @@ -1616,11 +1618,13 @@ function SPAWN:_OnDeadOrCrash( EventData ) if SpawnGroup then local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) - self:T( { "Dead event: " .. EventPrefix } ) - if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then - self.AliveUnits = self.AliveUnits - 1 - self:T( "Alive Units: " .. self.AliveUnits ) - end + if EventPrefix then -- EventPrefix can be nil if no # is found, which means, no spawnable group! + self:T( { "Dead event: " .. EventPrefix } ) + if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then + self.AliveUnits = self.AliveUnits - 1 + self:T( "Alive Units: " .. self.AliveUnits ) + end + end end end @@ -1634,10 +1638,12 @@ function SPAWN:_OnTakeOff( EventData ) local SpawnGroup = EventData.IniGroup if SpawnGroup then local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) - self:T( { "TakeOff event: " .. EventPrefix } ) - if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then - self:T( "self.Landed = false" ) - SpawnGroup:SetState( SpawnGroup, "Spawn_Landed", false ) + if EventPrefix then -- EventPrefix can be nil if no # is found, which means, no spawnable group! + self:T( { "TakeOff event: " .. EventPrefix } ) + if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then + self:T( "self.Landed = false" ) + SpawnGroup:SetState( SpawnGroup, "Spawn_Landed", false ) + end end end end @@ -1652,16 +1658,18 @@ function SPAWN:_OnLand( EventData ) local SpawnGroup = EventData.IniGroup if SpawnGroup then local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) - self:T( { "Land event: " .. EventPrefix } ) - if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then - -- TODO: Check if this is the last unit of the group that lands. - SpawnGroup:SetState( SpawnGroup, "Spawn_Landed", true ) - if self.RepeatOnLanding then - local SpawnGroupIndex = self:GetSpawnIndexFromGroup( SpawnGroup ) - self:T( { "Landed:", "ReSpawn:", SpawnGroup:GetName(), SpawnGroupIndex } ) - self:ReSpawn( SpawnGroupIndex ) - end - end + if EventPrefix then -- EventPrefix can be nil if no # is found, which means, no spawnable group! + self:T( { "Land event: " .. EventPrefix } ) + if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then + -- TODO: Check if this is the last unit of the group that lands. + SpawnGroup:SetState( SpawnGroup, "Spawn_Landed", true ) + if self.RepeatOnLanding then + local SpawnGroupIndex = self:GetSpawnIndexFromGroup( SpawnGroup ) + self:T( { "Landed:", "ReSpawn:", SpawnGroup:GetName(), SpawnGroupIndex } ) + self:ReSpawn( SpawnGroupIndex ) + end + end + end end end @@ -1676,16 +1684,18 @@ function SPAWN:_OnEngineShutDown( EventData ) local SpawnGroup = EventData.IniGroup if SpawnGroup then local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) - self:T( { "EngineShutdown event: " .. EventPrefix } ) - if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then - -- todo: test if on the runway - local Landed = SpawnGroup:GetState( SpawnGroup, "Spawn_Landed" ) - if Landed and self.RepeatOnEngineShutDown then - local SpawnGroupIndex = self:GetSpawnIndexFromGroup( SpawnGroup ) - self:T( { "EngineShutDown: ", "ReSpawn:", SpawnGroup:GetName(), SpawnGroupIndex } ) - self:ReSpawn( SpawnGroupIndex ) - end - end + if EventPrefix then -- EventPrefix can be nil if no # is found, which means, no spawnable group! + self:T( { "EngineShutdown event: " .. EventPrefix } ) + if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then + -- todo: test if on the runway + local Landed = SpawnGroup:GetState( SpawnGroup, "Spawn_Landed" ) + if Landed and self.RepeatOnEngineShutDown then + local SpawnGroupIndex = self:GetSpawnIndexFromGroup( SpawnGroup ) + self:T( { "EngineShutDown: ", "ReSpawn:", SpawnGroup:GetName(), SpawnGroupIndex } ) + self:ReSpawn( SpawnGroupIndex ) + end + end + end end end diff --git a/Moose Development/Moose/Moose.lua b/Moose Development/Moose/Moose.lua index 4c87af909..46fdaa17c 100644 --- a/Moose Development/Moose/Moose.lua +++ b/Moose Development/Moose/Moose.lua @@ -9,4 +9,4 @@ _SCHEDULEDISPATCHER = SCHEDULEDISPATCHER:New() -- Core.Timer#SCHEDULEDISPATCHER --- Declare the main database object, which is used internally by the MOOSE classes. _DATABASE = DATABASE:New() -- Database#DATABASE -COORDINATE:CoordinateMenu() +--COORDINATE:CoordinateMenu() diff --git a/Moose Development/Moose/Tasking/CommandCenter.lua b/Moose Development/Moose/Tasking/CommandCenter.lua index f24527cbb..93fd01162 100644 --- a/Moose Development/Moose/Tasking/CommandCenter.lua +++ b/Moose Development/Moose/Tasking/CommandCenter.lua @@ -9,6 +9,7 @@ -- @extends Core.Base#BASE REPORT = { ClassName = "REPORT", + Title = "", } --- Create a new REPORT. @@ -20,13 +21,24 @@ function REPORT:New( Title ) local self = BASE:Inherit( self, BASE:New() ) -- #REPORT self.Report = {} + + Title = Title or "" if Title then - self.Report[#self.Report+1] = Title + self.Title = Title end return self end +--- Has the REPORT Text? +-- @param #REPORT self +-- @return #boolean +function REPORT:HasText() --R2.1 + + return #self.Report > 0 +end + + --- Set indent of a REPORT. -- @param #REPORT self -- @param #number Indent @@ -61,7 +73,7 @@ end -- @return #string The report text. function REPORT:Text( Delimiter ) Delimiter = Delimiter or "\n" - local ReportText = table.concat( self.Report, Delimiter ) or "" + local ReportText = ( self.Title ~= "" and self.Title .. Delimiter or self.Title ) .. table.concat( self.Report, Delimiter ) or "" return ReportText end @@ -100,9 +112,9 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName ) if EventData.IniObjectCategory == 1 then local EventGroup = GROUP:Find( EventData.IniDCSGroup ) if EventGroup and self:HasGroup( EventGroup ) then - local MenuReporting = MENU_GROUP:New( EventGroup, "Reporting", self.CommandCenterMenu ) - local MenuMissionsSummary = MENU_GROUP_COMMAND:New( EventGroup, "Missions Summary Report", MenuReporting, self.ReportSummary, self, EventGroup ) - local MenuMissionsDetails = MENU_GROUP_COMMAND:New( EventGroup, "Missions Details Report", MenuReporting, self.ReportDetails, self, EventGroup ) + local MenuReporting = MENU_GROUP:New( EventGroup, "Missions Reports", self.CommandCenterMenu ) + local MenuMissionsSummary = MENU_GROUP_COMMAND:New( EventGroup, "Missions Status Report", MenuReporting, self.ReportMissionsStatus, self, EventGroup ) + local MenuMissionsDetails = MENU_GROUP_COMMAND:New( EventGroup, "Missions Players Report", MenuReporting, self.ReportMissionsPlayers, self, EventGroup ) self:ReportSummary( EventGroup ) end local PlayerUnit = EventData.IniUnit @@ -161,7 +173,7 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName ) local PlayerUnit = EventData.IniUnit for MissionID, Mission in pairs( self:GetMissions() ) do local Mission = Mission -- Tasking.Mission#MISSION - if Mission:IsOngoing() then + if Mission:IsENGAGED() then Mission:AbortUnit( PlayerUnit ) end end @@ -177,7 +189,10 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName ) function( self, EventData ) local PlayerUnit = EventData.IniUnit for MissionID, Mission in pairs( self:GetMissions() ) do - Mission:CrashUnit( PlayerUnit ) + local Mission = Mission -- Tasking.Mission#MISSION + if Mission:IsENGAGED() then + Mission:CrashUnit( PlayerUnit ) + end end end ) @@ -257,7 +272,6 @@ end -- @param #COMMANDCENTER self -- @return Core.Menu#MENU_COALITION function COMMANDCENTER:GetMenu() - self:F() return self.CommandCenterMenu end @@ -293,11 +307,9 @@ end -- @param #string Message -- @param Wrapper.Group#GROUP TaskGroup -- @param #sring Name (optional) The name of the Group used as a prefix for the message to the Group. If not provided, there will be nothing shown. -function COMMANDCENTER:MessageToGroup( Message, TaskGroup, Name ) +function COMMANDCENTER:MessageToGroup( Message, TaskGroup ) - local Prefix = Name and "@ " .. Name .. ": " or "@ " .. TaskGroup:GetCallsign() .. ": " - Message = Prefix .. Message - self:GetPositionable():MessageToGroup( Message , 20, TaskGroup, self:GetName() ) + self:GetPositionable():MessageToGroup( Message , 15, TaskGroup, self:GetName() ) end @@ -307,7 +319,8 @@ function COMMANDCENTER:MessageToCoalition( Message ) local CCCoalition = self:GetPositionable():GetCoalition() --TODO: Fix coalition bug! - self:GetPositionable():MessageToCoalition( Message, 20, CCCoalition, self:GetName() ) + + self:GetPositionable():MessageToCoalition( Message, 15, CCCoalition ) end @@ -315,18 +328,37 @@ end --- Report the status of all MISSIONs to a GROUP. -- Each Mission is listed, with an indication how many Tasks are still to be completed. -- @param #COMMANDCENTER self -function COMMANDCENTER:ReportSummary( ReportGroup ) +function COMMANDCENTER:ReportMissionsStatus( ReportGroup ) + self:E( ReportGroup ) + + local Report = REPORT:New() + + Report:Add( "Status report of all missions." ) + + for MissionID, Mission in pairs( self.Missions ) do + local Mission = Mission -- Tasking.Mission#MISSION + Report:Add( " - " .. Mission:ReportStatus() ) + end + + self:MessageToGroup( Report:Text(), ReportGroup ) +end + +--- Report the players of all MISSIONs to a GROUP. +-- Each Mission is listed, with an indication how many Tasks are still to be completed. +-- @param #COMMANDCENTER self +function COMMANDCENTER:ReportMissionsPlayers( ReportGroup ) self:E( ReportGroup ) local Report = REPORT:New() + Report:Add( "Players active in all missions." ) + for MissionID, Mission in pairs( self.Missions ) do local Mission = Mission -- Tasking.Mission#MISSION - Report:Add( " - " .. Mission:ReportOverview() ) + Report:Add( " - " .. Mission:ReportPlayers() ) end - self:GetPositionable():MessageToGroup( Report:Text(), 30, ReportGroup ) - + self:MessageToGroup( Report:Text(), ReportGroup ) end --- Report the status of a Task to a Group. @@ -342,6 +374,6 @@ function COMMANDCENTER:ReportDetails( ReportGroup, Task ) Report:Add( " - " .. Mission:ReportDetails() ) end - self:GetPositionable():MessageToGroup( Report:Text(), 30, ReportGroup ) + self:MessageToGroup( Report:Text(), ReportGroup ) end diff --git a/Moose Development/Moose/Tasking/DetectionManager.lua b/Moose Development/Moose/Tasking/DetectionManager.lua index 7601701a0..7283d50d9 100644 --- a/Moose Development/Moose/Tasking/DetectionManager.lua +++ b/Moose Development/Moose/Tasking/DetectionManager.lua @@ -76,6 +76,7 @@ do -- DETECTION MANAGER self:SetReportInterval( 30 ) self:SetReportDisplayTime( 25 ) + self:E( { Detection = Detection } ) Detection:__Start( 1 ) return self diff --git a/Moose Development/Moose/Tasking/Mission.lua b/Moose Development/Moose/Tasking/Mission.lua index 177376bfd..47563623e 100644 --- a/Moose Development/Moose/Tasking/Mission.lua +++ b/Moose Development/Moose/Tasking/Mission.lua @@ -12,6 +12,7 @@ MISSION = { ClassName = "MISSION", Name = "", MissionStatus = "PENDING", + AssignedGroups = {}, } --- This is the main MISSION declaration method. Each Mission is like the master or a Mission orchestration between, Clients, Tasks, Stages etc. @@ -26,35 +27,47 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi local self = BASE:Inherit( self, FSM:New() ) -- Core.Fsm#FSM - self:SetStartState( "Idle" ) + self:T( { MissionName, MissionPriority, MissionBriefing, MissionCoalition } ) - self:AddTransition( "Idle", "Start", "Ongoing" ) + self.CommandCenter = CommandCenter + CommandCenter:AddMission( self ) - --- OnLeave Transition Handler for State Idle. - -- @function [parent=#MISSION] OnLeaveIdle + self.Name = MissionName + self.MissionPriority = MissionPriority + self.MissionBriefing = MissionBriefing + self.MissionCoalition = MissionCoalition + + self.Tasks = {} + + self:SetStartState( "IDLE" ) + + self:AddTransition( "IDLE", "Start", "ENGAGED" ) + + --- OnLeave Transition Handler for State IDLE. + -- @function [parent=#MISSION] OnLeaveIDLE -- @param #MISSION self -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. -- @return #boolean Return false to cancel Transition. - --- OnEnter Transition Handler for State Idle. - -- @function [parent=#MISSION] OnEnterIdle + --- OnEnter Transition Handler for State IDLE. + -- @function [parent=#MISSION] OnEnterIDLE -- @param #MISSION self -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. - --- OnLeave Transition Handler for State Ongoing. - -- @function [parent=#MISSION] OnLeaveOngoing + --- OnLeave Transition Handler for State ENGAGED. + -- @function [parent=#MISSION] OnLeaveENGAGED -- @param #MISSION self -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. -- @return #boolean Return false to cancel Transition. - --- OnEnter Transition Handler for State Ongoing. - -- @function [parent=#MISSION] OnEnterOngoing + --- OnEnter Transition Handler for State ENGAGED. + -- @function [parent=#MISSION] OnEnterENGAGED -- @param #MISSION self -- @param #string From The From State string. -- @param #string Event The Event string. @@ -84,18 +97,18 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi -- @param #MISSION self -- @param #number Delay The delay in seconds. - self:AddTransition( "Ongoing", "Stop", "Idle" ) + self:AddTransition( "ENGAGED", "Stop", "IDLE" ) - --- OnLeave Transition Handler for State Idle. - -- @function [parent=#MISSION] OnLeaveIdle + --- OnLeave Transition Handler for State IDLE. + -- @function [parent=#MISSION] OnLeaveIDLE -- @param #MISSION self -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. -- @return #boolean Return false to cancel Transition. - --- OnEnter Transition Handler for State Idle. - -- @function [parent=#MISSION] OnEnterIdle + --- OnEnter Transition Handler for State IDLE. + -- @function [parent=#MISSION] OnEnterIDLE -- @param #MISSION self -- @param #string From The From State string. -- @param #string Event The Event string. @@ -125,18 +138,18 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi -- @param #MISSION self -- @param #number Delay The delay in seconds. - self:AddTransition( "Ongoing", "Complete", "Completed" ) + self:AddTransition( "ENGAGED", "Complete", "COMPLETED" ) - --- OnLeave Transition Handler for State Completed. - -- @function [parent=#MISSION] OnLeaveCompleted + --- OnLeave Transition Handler for State COMPLETED. + -- @function [parent=#MISSION] OnLeaveCOMPLETED -- @param #MISSION self -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. -- @return #boolean Return false to cancel Transition. - --- OnEnter Transition Handler for State Completed. - -- @function [parent=#MISSION] OnEnterCompleted + --- OnEnter Transition Handler for State COMPLETED. + -- @function [parent=#MISSION] OnEnterCOMPLETED -- @param #MISSION self -- @param #string From The From State string. -- @param #string Event The Event string. @@ -166,18 +179,18 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi -- @param #MISSION self -- @param #number Delay The delay in seconds. - self:AddTransition( "*", "Fail", "Failed" ) + self:AddTransition( "*", "Fail", "FAILED" ) - --- OnLeave Transition Handler for State Failed. - -- @function [parent=#MISSION] OnLeaveFailed + --- OnLeave Transition Handler for State FAILED. + -- @function [parent=#MISSION] OnLeaveFAILED -- @param #MISSION self -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. -- @return #boolean Return false to cancel Transition. - --- OnEnter Transition Handler for State Failed. - -- @function [parent=#MISSION] OnEnterFailed + --- OnEnter Transition Handler for State FAILED. + -- @function [parent=#MISSION] OnEnterFAILED -- @param #MISSION self -- @param #string From The From State string. -- @param #string Event The Event string. @@ -207,17 +220,6 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi -- @param #MISSION self -- @param #number Delay The delay in seconds. - self:T( { MissionName, MissionPriority, MissionBriefing, MissionCoalition } ) - - self.CommandCenter = CommandCenter - CommandCenter:AddMission( self ) - - self.Name = MissionName - self.MissionPriority = MissionPriority - self.MissionBriefing = MissionBriefing - self.MissionCoalition = MissionCoalition - - self.Tasks = {} -- Private implementations @@ -235,7 +237,7 @@ function MISSION:onbeforeComplete( From, Event, To ) for TaskID, Task in pairs( self:GetTasks() ) do local Task = Task -- Tasking.Task#TASK - if not Task:IsStateSuccess() and not Task:IsStateFailed() and not Task:IsStateAborted() and not Task:IsStateCancelled() then + if not Task:IsStateSuccess() and not Task:IsStateFAILED() and not Task:IsStateAborted() and not Task:IsStateCancelled() then return false -- Mission cannot be completed. Other Tasks are still active. end end @@ -247,7 +249,7 @@ end -- @param #string From -- @param #string Event -- @param #string To -function MISSION:onenterCompleted( From, Event, To ) +function MISSION:onenterCOMPLETED( From, Event, To ) self:GetCommandCenter():MessageToCoalition( "Mission " .. self:GetName() .. " has been completed! Good job guys!" ) end @@ -256,7 +258,7 @@ end -- @param #MISSION self -- @return #MISSION self function MISSION:GetName() - return self.Name + return string.format( 'Mission "%s (%s)"', self.Name, self.MissionPriority ) end --- Add a Unit to join the Mission. @@ -288,19 +290,17 @@ end -- If the Unit is part of a Task in the Mission, true is returned. -- @param #MISSION self -- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player joining the Mission. --- @return #boolean true if Unit is part of a Task in the Mission. +-- @return #MISSION function MISSION:AbortUnit( PlayerUnit ) self:F( { PlayerUnit = PlayerUnit } ) - local PlayerUnitRemoved = false - for TaskID, Task in pairs( self:GetTasks() ) do - if Task:AbortUnit( PlayerUnit ) then - PlayerUnitRemoved = true - end + local Task = Task -- Tasking.Task#TASK + local PlayerGroup = PlayerUnit:GetGroup() + Task:AbortGroup( PlayerGroup ) end - return PlayerUnitRemoved + return self end --- Handles a crash of a PlayerUnit from the Mission. @@ -309,19 +309,17 @@ end -- If the Unit is part of a Task in the Mission, true is returned. -- @param #MISSION self -- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player crashing. --- @return #boolean true if Unit is part of a Task in the Mission. +-- @return #MISSION function MISSION:CrashUnit( PlayerUnit ) self:F( { PlayerUnit = PlayerUnit } ) - local PlayerUnitRemoved = false - for TaskID, Task in pairs( self:GetTasks() ) do - if Task:CrashUnit( PlayerUnit ) then - PlayerUnitRemoved = true - end + local Task = Task -- Tasking.Task#TASK + local PlayerGroup = PlayerUnit:GetGroup() + Task:CrashGroup( PlayerGroup ) end - return PlayerUnitRemoved + return self end --- Add a scoring to the mission. @@ -365,7 +363,7 @@ end -- @param #MISSION self -- @param #number MenuTime function MISSION:SetMenu( MenuTime ) - self:F() + self:F( { self:GetName(), MenuTime } ) for _, TaskData in pairs( self:GetTasks() ) do local Task = TaskData -- Tasking.Task#TASK @@ -377,7 +375,7 @@ end -- @param #MISSION self -- @param #number MenuTime function MISSION:RemoveMenu( MenuTime ) - self:F() + self:F( { self:GetName(), MenuTime } ) for _, Task in pairs( self:GetTasks() ) do local Task = Task -- Tasking.Task#TASK @@ -386,6 +384,58 @@ function MISSION:RemoveMenu( MenuTime ) end +do -- Group Assignment + + --- Returns if the @{Mission} is assigned to the Group. + -- @param #MISSION self + -- @param Wrapper.Group#GROUP MissionGroup + -- @return #boolean + function MISSION:IsGroupAssigned( MissionGroup ) + + local MissionGroupName = MissionGroup:GetName() + + if self.AssignedGroups[MissionGroupName] == MissionGroup then + self:T( { "Mission is assigned to:", MissionGroup:GetName() } ) + return true + end + + self:T( { "Mission is not assigned to:", MissionGroup:GetName() } ) + return false + end + + + --- Set @{Group} assigned to the @{Mission}. + -- @param #MISSION self + -- @param Wrapper.Group#GROUP MissionGroup + -- @return #MISSION + function MISSION:SetGroupAssigned( MissionGroup ) + + local MissionName = self:GetName() + local MissionGroupName = MissionGroup:GetName() + + self.AssignedGroups[MissionGroupName] = MissionGroup + self:E( string.format( "Mission %s is assigned to %s", MissionName, MissionGroupName ) ) + + return self + end + + --- Clear the @{Group} assignment from the @{Mission}. + -- @param #MISSION self + -- @param Wrapper.Group#GROUP MissionGroup + -- @return #MISSION + function MISSION:ClearGroupAssignment( MissionGroup ) + + local MissionName = self:GetName() + local MissionGroupName = MissionGroup:GetName() + + self.AssignedGroups[MissionGroupName] = nil + self:E( string.format( "Mission %s is unassigned to %s", MissionName, MissionGroupName ) ) + + return self + end + +end + --- Gets the COMMANDCENTER. -- @param #MISSION self -- @return Tasking.CommandCenter#COMMANDCENTER @@ -489,39 +539,39 @@ function MISSION:GetNextTaskID( Task ) return self.Tasks[TaskName].n end ---- Is the @{Mission} **Completed**. +--- Is the @{Mission} **COMPLETED**. -- @param #MISSION self -- @return #boolean -function MISSION:IsCompleted() - return self:Is( "Completed" ) +function MISSION:IsCOMPLETED() + return self:Is( "COMPLETED" ) end ---- Is the @{Mission} **Idle**. +--- Is the @{Mission} **IDLE**. -- @param #MISSION self -- @return #boolean -function MISSION:IsIdle() - return self:Is( "Idle" ) +function MISSION:IsIDLE() + return self:Is( "IDLE" ) end ---- Is the @{Mission} **Ongoing**. +--- Is the @{Mission} **ENGAGED**. -- @param #MISSION self -- @return #boolean -function MISSION:IsOngoing() - return self:Is( "Ongoing" ) +function MISSION:IsENGAGED() + return self:Is( "ENGAGED" ) end ---- Is the @{Mission} **Failed**. +--- Is the @{Mission} **FAILED**. -- @param #MISSION self -- @return #boolean -function MISSION:IsFailed() - return self:Is( "Failed" ) +function MISSION:IsFAILED() + return self:Is( "FAILED" ) end ---- Is the @{Mission} **Hold**. +--- Is the @{Mission} **HOLD**. -- @param #MISSION self -- @return #boolean -function MISSION:IsHold() - return self:Is( "Hold" ) +function MISSION:IsHOLD() + return self:Is( "HOLD" ) end --- Validates if the Mission has a Group @@ -556,6 +606,117 @@ function MISSION:GetTasksRemaining() return TasksRemaining end +--- @param #MISSION self +-- @return #number +function MISSION:GetTaskTypes() + -- Determine how many tasks are remaining. + local TaskTypeList = {} + local TasksRemaining = 0 + for TaskID, Task in pairs( self:GetTasks() ) do + local Task = Task -- Tasking.Task#TASK + local TaskType = Task:GetType() + TaskTypeList[TaskType] = TaskType + end + return TaskTypeList +end + + +--- Create a status report of the Mission. +-- This reports provides a one liner of the mission status. It indicates how many players and how many Tasks. +-- +-- Mission "" - Status "" +-- - Task Types: , +-- - Planned Tasks (xp) +-- - Assigned Tasks(xp) +-- - Success Tasks (xp) +-- - Hold Tasks (xp) +-- - Cancelled Tasks (xp) +-- - Aborted Tasks (xp) +-- - Failed Tasks (xp) +-- +-- @param #MISSION self +-- @return #string +function MISSION:ReportStatus() + + local Report = REPORT:New() + + -- List the name of the mission. + local Name = self:GetName() + + -- Determine the status of the mission. + local Status = self:GetState() + local TasksRemaining = self:GetTasksRemaining() + + Report:Add( string.format( '%s - Status "%s"', Name, Status ) ) + + local TaskTypes = self:GetTaskTypes() + + Report:Add( string.format( " - Task Types: %s", table.concat(TaskTypes, ", " ) ) ) + + local TaskStatusList = { "Planned", "Assigned", "Success", "Hold", "Cancelled", "Aborted", "Failed" } + + for TaskStatusID, TaskStatus in pairs( TaskStatusList ) do + local TaskCount = 0 + local TaskPlayerCount = 0 + -- Determine how many tasks are remaining. + for TaskID, Task in pairs( self:GetTasks() ) do + local Task = Task -- Tasking.Task#TASK + if Task:Is( TaskStatus ) then + TaskCount = TaskCount + 1 + TaskPlayerCount = TaskPlayerCount + Task:GetPlayerCount() + end + end + if TaskCount > 0 then + Report:Add( string.format( " - %02d %s Tasks (%dp)", TaskCount, TaskStatus, TaskPlayerCount ) ) + end + end + + return Report:Text() +end + +--- Create a player report of the Mission. +-- This reports provides a one liner of the mission status. It indicates how many players and how many Tasks. +-- +-- Mission "" - Status "" +-- - Player ": Task , Task +-- - Player : Task , Task +-- - .. +-- +-- @param #MISSION self +-- @return #string +function MISSION:ReportPlayers() + + local Report = REPORT:New() + + -- List the name of the mission. + local Name = self:GetName() + + -- Determine the status of the mission. + local Status = self:GetState() + local TasksRemaining = self:GetTasksRemaining() + + Report:Add( string.format( '%s - Status "%s"', Name, Status ) ) + + local PlayerList = {} + + -- Determine how many tasks are remaining. + for TaskID, Task in pairs( self:GetTasks() ) do + local Task = Task -- Tasking.Task#TASK + local PlayerNames = Task:GetPlayerNames() + for PlayerID, PlayerName in pairs( PlayerNames ) do + PlayerList[PlayerName] = Task:GetName() + end + + end + + for PlayerName, TaskName in pairs( PlayerList ) do + Report:Add( string.format( ' - Player (%s): Task "%s"', PlayerName, TaskName ) ) + end + + return Report:Text() +end + + --- Create a summary report of the Mission (one line). -- @param #MISSION self -- @return #string @@ -595,7 +756,7 @@ function MISSION:ReportOverview( TaskStatus ) local Status = self:GetState() local TasksRemaining = self:GetTasksRemaining() - Report:Add( "Mission " .. Name .. " - " .. Status .. " Task Report ") + Report:Add( string.format( '%s - Status "%s"', Name, Status ) ) -- Determine how many tasks are remaining. local TasksRemaining = 0 @@ -622,7 +783,7 @@ function MISSION:ReportDetails() -- Determine the status of the mission. local Status = self:GetState() - Report:Add( "Mission " .. Name .. " - " .. Status ) + Report:Add( string.format( '%s - Status "%s"', Name, Status ) ) -- Determine how many tasks are remaining. local TasksRemaining = 0 @@ -641,7 +802,6 @@ end -- Tasks = Mission:GetTasks() -- env.info( "Task 2 Completion = " .. Tasks[2]:GetGoalPercentage() .. "%" ) function MISSION:GetTasks() - self:F() return self.Tasks end diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index e9519290e..e2ae85c64 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -78,6 +78,7 @@ TASK = { Mission = nil, CommandCenter = nil, TimeOut = 0, + AssignedGroups = {}, } --- FSM PlayerAborted event handler prototype for TASK. @@ -156,14 +157,15 @@ TASK = { -- @param #string TaskName The name of the Task -- @param #string TaskType The type of the Task -- @return #TASK self -function TASK:New( Mission, SetGroupAssign, TaskName, TaskType ) +function TASK:New( Mission, SetGroupAssign, TaskName, TaskType, TaskBriefing ) - local self = BASE:Inherit( self, FSM_TASK:New() ) -- Core.Fsm#FSM_TASK + local self = BASE:Inherit( self, FSM_TASK:New() ) -- Tasking.Task#TASK self:SetStartState( "Planned" ) self:AddTransition( "Planned", "Assign", "Assigned" ) self:AddTransition( "Assigned", "AssignUnit", "Assigned" ) self:AddTransition( "Assigned", "Success", "Success" ) + self:AddTransition( "Assigned", "Hold", "Hold" ) self:AddTransition( "Assigned", "Fail", "Failed" ) self:AddTransition( "Assigned", "Abort", "Aborted" ) self:AddTransition( "Assigned", "Cancel", "Cancelled" ) @@ -187,7 +189,7 @@ function TASK:New( Mission, SetGroupAssign, TaskName, TaskType ) self:SetName( TaskName ) self:SetID( Mission:GetNextTaskID( self ) ) -- The Mission orchestrates the task sequences .. - self.TaskBriefing = "You are invited for the task: " .. self.TaskName .. "." + self:SetBriefing( TaskBriefing ) self.FsmTemplate = self.FsmTemplate or FSM_PROCESS:New() @@ -241,9 +243,9 @@ function TASK:JoinUnit( PlayerUnit, PlayerGroup ) --self:MessageToGroups( PlayerUnit:GetPlayerName() .. " is planning to join Task " .. self:GetName() ) end if self:IsStateAssigned() then - local IsAssignedToGroup = self:IsAssignedToGroup( PlayerGroup ) - self:E( { IsAssignedToGroup = IsAssignedToGroup } ) - if IsAssignedToGroup then + local IsGroupAssigned = self:IsGroupAssigned( PlayerGroup ) + self:E( { IsGroupAssigned = IsGroupAssigned } ) + if IsGroupAssigned then self:AssignToUnit( PlayerUnit ) self:MessageToGroups( PlayerUnit:GetPlayerName() .. " joined Task " .. self:GetName() ) end @@ -258,14 +260,11 @@ end -- If the Unit is part of the Task, true is returned. -- @param #TASK self -- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player aborting the Task. --- @return #boolean true if Unit is part of the Task. -function TASK:AbortUnit( PlayerUnit ) - self:F( { PlayerUnit = PlayerUnit } ) - - local PlayerUnitAborted = false +-- @return #TASK +function TASK:AbortGroup( PlayerGroup ) + self:F( { PlayerGroup = PlayerGroup } ) local PlayerGroups = self:GetGroups() - local PlayerGroup = PlayerUnit:GetGroup() -- Is the PlayerGroup part of the PlayerGroups? if PlayerGroups:IsIncludeObject( PlayerGroup ) then @@ -273,25 +272,39 @@ function TASK:AbortUnit( PlayerUnit ) -- Check if the PlayerGroup is already assigned to the Task. If yes, the PlayerGroup is aborted from the Task. -- If the PlayerUnit was the last unit of the PlayerGroup, the menu needs to be removed from the Group. if self:IsStateAssigned() then - local IsAssignedToGroup = self:IsAssignedToGroup( PlayerGroup ) - self:E( { IsAssignedToGroup = IsAssignedToGroup } ) - if IsAssignedToGroup then - local PlayerName = PlayerUnit:GetPlayerName() - self:UnAssignFromUnit( PlayerUnit ) + local IsGroupAssigned = self:IsGroupAssigned( PlayerGroup ) + self:E( { IsGroupAssigned = IsGroupAssigned } ) + if IsGroupAssigned then + local PlayerName = PlayerGroup:GetUnit(1):GetPlayerName() self:MessageToGroups( PlayerName .. " aborted Task " .. self:GetName() ) - self:E( { TaskGroup = PlayerGroup:GetName(), GetUnits = PlayerGroup:GetUnits() } ) - if #PlayerGroup:GetUnits() == 1 then - self:UnAssignFromGroup( PlayerGroup ) - PlayerGroup:SetState( PlayerGroup, "Assigned", nil ) - self:RemoveMenuForGroup( PlayerGroup ) + self:UnAssignFromGroup( PlayerGroup ) + --self:Abort() + + -- Now check if the task needs to go to hold... + -- It will go to hold, if there are no players in the mission... + + PlayerGroups:Flush() + local IsRemaining = false + for GroupName, AssignedGroup in pairs( PlayerGroups:GetSet() or {} ) do + if self:IsGroupAssigned( AssignedGroup ) == true then + IsRemaining = true + self:F( { Task = self:GetName(), IsRemaining = IsRemaining } ) + break + end end - self:Abort() - self:PlayerAborted( PlayerUnit ) + + self:F( { Task = self:GetName(), IsRemaining = IsRemaining } ) + if IsRemaining == false then + self:Abort() + end + + self:PlayerAborted( PlayerGroup:GetUnit(1) ) end + end end - return PlayerUnitAborted + return self end --- A PlayerUnit crashed in a Task. Abort the Player. @@ -299,14 +312,11 @@ end -- If the Unit is part of the Task, true is returned. -- @param #TASK self -- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player aborting the Task. --- @return #boolean true if Unit is part of the Task. -function TASK:CrashUnit( PlayerUnit ) - self:F( { PlayerUnit = PlayerUnit } ) - - local PlayerUnitCrashed = false +-- @return #TASK +function TASK:CrashGroup( PlayerGroup ) + self:F( { PlayerGroup = PlayerGroup } ) local PlayerGroups = self:GetGroups() - local PlayerGroup = PlayerUnit:GetGroup() -- Is the PlayerGroup part of the PlayerGroups? if PlayerGroups:IsIncludeObject( PlayerGroup ) then @@ -314,22 +324,38 @@ function TASK:CrashUnit( PlayerUnit ) -- Check if the PlayerGroup is already assigned to the Task. If yes, the PlayerGroup is aborted from the Task. -- If the PlayerUnit was the last unit of the PlayerGroup, the menu needs to be removed from the Group. if self:IsStateAssigned() then - local IsAssignedToGroup = self:IsAssignedToGroup( PlayerGroup ) - self:E( { IsAssignedToGroup = IsAssignedToGroup } ) - if IsAssignedToGroup then - self:UnAssignFromUnit( PlayerUnit ) - self:MessageToGroups( PlayerUnit:GetPlayerName() .. " crashed in Task " .. self:GetName() ) - self:E( { TaskGroup = PlayerGroup:GetName(), GetUnits = PlayerGroup:GetUnits() } ) - if #PlayerGroup:GetUnits() == 1 then - PlayerGroup:SetState( PlayerGroup, "Assigned", nil ) - self:RemoveMenuForGroup( PlayerGroup ) + local IsGroupAssigned = self:IsGroupAssigned( PlayerGroup ) + self:E( { IsGroupAssigned = IsGroupAssigned } ) + if IsGroupAssigned then + local PlayerName = PlayerGroup:GetUnit(1):GetPlayerName() + self:MessageToGroups( PlayerName .. " crashed! " ) + self:UnAssignFromGroup( PlayerGroup ) + + -- Now check if the task needs to go to hold... + -- It will go to hold, if there are no players in the mission... + + PlayerGroups:Flush() + local IsRemaining = false + for GroupName, AssignedGroup in pairs( PlayerGroups:GetSet() or {} ) do + if self:IsGroupAssigned( AssignedGroup ) == true then + IsRemaining = true + self:F( { Task = self:GetName(), IsRemaining = IsRemaining } ) + break + end end - self:PlayerCrashed( PlayerUnit ) + + self:F( { Task = self:GetName(), IsRemaining = IsRemaining } ) + if IsRemaining == false then + self:Abort() + end + + self:PlayerCrashed( PlayerGroup:GetUnit(1) ) end + end end - return PlayerUnitCrashed + return self end @@ -350,39 +376,148 @@ function TASK:GetGroups() return self.SetGroup end +do -- Group Assignment - ---- Assign the @{Task} to a @{Group}. --- @param #TASK self --- @param Wrapper.Group#GROUP TaskGroup --- @return #TASK -function TASK:AssignToGroup( TaskGroup ) - self:F2( TaskGroup:GetName() ) + --- Returns if the @{Task} is assigned to the Group. + -- @param #TASK self + -- @param Wrapper.Group#GROUP TaskGroup + -- @return #boolean + function TASK:IsGroupAssigned( TaskGroup ) - local TaskGroupName = TaskGroup:GetName() - - TaskGroup:SetState( TaskGroup, "Assigned", self ) - - local Mission = self:GetMission() - local MissionMenu = Mission:GetMenu( TaskGroup ) - MissionMenu:RemoveSubMenus() - - --self:RemoveMenuForGroup( TaskGroup ) - self:SetAssignedMenuForGroup( TaskGroup ) - - local TaskUnits = TaskGroup:GetUnits() - for UnitID, UnitData in pairs( TaskUnits ) do - local TaskUnit = UnitData -- Wrapper.Unit#UNIT - local PlayerName = TaskUnit:GetPlayerName() - self:E(PlayerName) - if PlayerName ~= nil or PlayerName ~= "" then - self:AssignToUnit( TaskUnit ) + local TaskGroupName = TaskGroup:GetName() + + if self.AssignedGroups[TaskGroupName] then + self:T( { "Task is assigned to:", TaskGroup:GetName() } ) + return true end + + self:T( { "Task is not assigned to:", TaskGroup:GetName() } ) + return false + end + + + --- Set @{Group} assigned to the @{Task}. + -- @param #TASK self + -- @param Wrapper.Group#GROUP TaskGroup + -- @return #TASK + function TASK:SetGroupAssigned( TaskGroup ) + + local TaskName = self:GetName() + local TaskGroupName = TaskGroup:GetName() + + self.AssignedGroups[TaskGroupName] = TaskGroup + self:E( string.format( "Task %s is assigned to %s", TaskName, TaskGroupName ) ) + + -- Set the group to be assigned at mission level. This allows to decide the menu options on mission level for this group. + self:GetMission():SetGroupAssigned( TaskGroup ) + + local SetAssignedGroups = self:GetGroups() + +-- SetAssignedGroups:ForEachGroup( +-- function( AssignedGroup ) +-- if self:IsGroupAssigned(AssignedGroup) then +-- self:GetMission():GetCommandCenter():MessageToGroup( string.format( "Task %s is assigned to group %s.", TaskName, TaskGroupName ), AssignedGroup ) +-- else +-- self:GetMission():GetCommandCenter():MessageToGroup( string.format( "Task %s is assigned to your group.", TaskName ), AssignedGroup ) +-- end +-- end +-- ) + + return self + end + + --- Clear the @{Group} assignment from the @{Task}. + -- @param #TASK self + -- @param Wrapper.Group#GROUP TaskGroup + -- @return #TASK + function TASK:ClearGroupAssignment( TaskGroup ) + + local TaskName = self:GetName() + local TaskGroupName = TaskGroup:GetName() + + self.AssignedGroups[TaskGroupName] = nil + self:E( string.format( "Task %s is unassigned to %s", TaskName, TaskGroupName ) ) + + -- Set the group to be assigned at mission level. This allows to decide the menu options on mission level for this group. + self:GetMission():ClearGroupAssignment( TaskGroup ) + + local SetAssignedGroups = self:GetGroups() + + SetAssignedGroups:ForEachGroup( + function( AssignedGroup ) + if self:IsGroupAssigned(AssignedGroup) then + self:GetMission():GetCommandCenter():MessageToGroup( string.format( "Task %s is unassigned from group %s.", TaskName, TaskGroupName ), AssignedGroup ) + else + self:GetMission():GetCommandCenter():MessageToGroup( string.format( "Task %s is unassigned from your group.", TaskName ), AssignedGroup ) + end + end + ) + + return self end - return self end +do -- Group Assignment + + --- Assign the @{Task} to a @{Group}. + -- @param #TASK self + -- @param Wrapper.Group#GROUP TaskGroup + -- @return #TASK + function TASK:AssignToGroup( TaskGroup ) + self:F( TaskGroup:GetName() ) + + local TaskGroupName = TaskGroup:GetName() + local Mission = self:GetMission() + local CommandCenter = Mission:GetCommandCenter() + + self:SetGroupAssigned( TaskGroup ) + + local TaskUnits = TaskGroup:GetUnits() + for UnitID, UnitData in pairs( TaskUnits ) do + local TaskUnit = UnitData -- Wrapper.Unit#UNIT + local PlayerName = TaskUnit:GetPlayerName() + self:E(PlayerName) + if PlayerName ~= nil or PlayerName ~= "" then + self:AssignToUnit( TaskUnit ) + CommandCenter:MessageToGroup( + string.format( 'Task "%s": Briefing for player (%s):\n%s', + self:GetName(), + PlayerName, + self:GetBriefing() + ), TaskGroup + ) + end + end + + CommandCenter:SetMenu() + + return self + end + + --- UnAssign the @{Task} from a @{Group}. + -- @param #TASK self + function TASK:UnAssignFromGroup( TaskGroup ) + self:F2( { TaskGroup } ) + + self:ClearGroupAssignment( TaskGroup ) + + local TaskUnits = TaskGroup:GetUnits() + for UnitID, UnitData in pairs( TaskUnits ) do + local TaskUnit = UnitData -- Wrapper.Unit#UNIT + local PlayerName = TaskUnit:GetPlayerName() + if PlayerName ~= nil or PlayerName ~= "" then + self:UnAssignFromUnit( TaskUnit ) + end + end + + local Mission = self:GetMission() + local CommandCenter = Mission:GetCommandCenter() + CommandCenter:SetMenu() + end +end + + --- -- @param #TASK self -- @param Wrapper.Group#GROUP FindGroup @@ -458,7 +593,7 @@ function TASK:SendBriefingToAssignedGroups() for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if self:IsAssignedToGroup( TaskGroup ) then + if self:IsGroupAssigned( TaskGroup ) then TaskGroup:Message( self.TaskBriefing, 60 ) end end @@ -471,50 +606,14 @@ function TASK:UnAssignFromGroups() self:F2() for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do - self:UnAssignFromGroup( TaskGroup ) - end -end - ---- UnAssign the @{Task} from a @{Group}. --- @param #TASK self -function TASK:UnAssignFromGroup( TaskGroup ) - self:F2( { TaskGroup } ) - - TaskGroup:SetState( TaskGroup, "Assigned", nil ) - - self:RemoveAssignedMenuForGroup( TaskGroup ) - - local TaskUnits = TaskGroup:GetUnits() - for UnitID, UnitData in pairs( TaskUnits ) do - local TaskUnit = UnitData -- Wrapper.Unit#UNIT - local PlayerName = TaskUnit:GetPlayerName() - if PlayerName ~= nil or PlayerName ~= "" then - self:UnAssignFromUnit( TaskUnit ) + if self:IsGroupAssigned(TaskGroup) then + self:UnAssignFromGroup( TaskGroup ) end end end ---- Returns if the @{Task} is assigned to the Group. --- @param #TASK self --- @param Wrapper.Group#GROUP TaskGroup --- @return #boolean -function TASK:IsAssignedToGroup( TaskGroup ) - - local TaskGroupName = TaskGroup:GetName() - - if self:IsStateAssigned() then - if TaskGroup:GetState( TaskGroup, "Assigned" ) == self then - self:T( { "Task is assigned to:", TaskGroup:GetName() } ) - return true - end - end - - self:T( { "Task is not assigned to:", TaskGroup:GetName() } ) - return false -end - --- Returns if the @{Task} has still alive and assigned Units. -- @param #TASK self -- @return #boolean @@ -523,7 +622,7 @@ function TASK:HasAliveUnits() for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do if self:IsStateAssigned() then - if self:IsAssignedToGroup( TaskGroup ) then + if self:IsGroupAssigned( TaskGroup ) then for TaskUnitID, TaskUnit in pairs( TaskGroup:GetUnits() ) do if TaskUnit:IsAlive() then self:T( { HasAliveUnits = true } ) @@ -543,9 +642,9 @@ end -- @param #number MenuTime -- @return #TASK function TASK:SetMenu( MenuTime ) --R2.1 Mission Reports and Task Reports added. Fixes issue #424. - self:F() + self:F( { self:GetName(), MenuTime } ) - self.SetGroup:Flush() + --self.SetGroup:Flush() for TaskGroupID, TaskGroupData in pairs( self.SetGroup:GetSet() ) do local TaskGroup = TaskGroupData -- Wrapper.Group#GROUP if TaskGroup:IsAlive() and TaskGroup:GetPlayerNames() then @@ -558,15 +657,15 @@ function TASK:SetMenu( MenuTime ) --R2.1 Mission Reports and Task Reports added. TaskGroup.MenuReports = MENU_GROUP:New( TaskGroup, "Reports", MissionMenu ) MENU_GROUP_COMMAND:New( TaskGroup, "Report Tasks", TaskGroup.MenuReports, Mission.MenuReportSummary, Mission, TaskGroup ) MENU_GROUP_COMMAND:New( TaskGroup, "Report Planned Tasks", TaskGroup.MenuReports, Mission.MenuReportOverview, Mission, TaskGroup, "Planned" ) - MENU_GROUP_COMMAND:New( TaskGroup, "Report Ongoing Tasks", TaskGroup.MenuReports, Mission.MenuReportOverview, Mission, TaskGroup, "Ongoing" ) + MENU_GROUP_COMMAND:New( TaskGroup, "Report Assigned Tasks", TaskGroup.MenuReports, Mission.MenuReportOverview, Mission, TaskGroup, "Assigned" ) MENU_GROUP_COMMAND:New( TaskGroup, "Report Successful Tasks", TaskGroup.MenuReports, Mission.MenuReportOverview, Mission, TaskGroup, "Success" ) MENU_GROUP_COMMAND:New( TaskGroup, "Report Failed Tasks", TaskGroup.MenuReports, Mission.MenuReportOverview, Mission, TaskGroup, "Failed" ) MENU_GROUP_COMMAND:New( TaskGroup, "Report Held Tasks", TaskGroup.MenuReports, Mission.MenuReportOverview, Mission, TaskGroup, "Hold" ) end - if self:IsStatePlanned() or self:IsStateReplanned() then +-- if self:IsStatePlanned() or self:IsStateReplanned() then self:SetMenuForGroup( TaskGroup, MenuTime ) - end +-- end end end end @@ -579,12 +678,9 @@ end -- @return #TASK function TASK:SetMenuForGroup( TaskGroup, MenuTime ) - if not TaskGroup:GetState( TaskGroup, "Assigned" ) then - self:SetPlannedMenuForGroup( TaskGroup, self:GetTaskName(), MenuTime ) - else - if not self:IsAssignedToGroup( TaskGroup ) then - self:SetAssignedMenuForGroup( TaskGroup, MenuTime ) - end + self:SetPlannedMenuForGroup( TaskGroup, MenuTime ) + if self:IsGroupAssigned( TaskGroup ) then + self:SetAssignedMenuForGroup( TaskGroup, MenuTime ) end end @@ -595,7 +691,7 @@ end -- @param #string MenuText The menu text. -- @param #number MenuTime -- @return #TASK self -function TASK:SetPlannedMenuForGroup( TaskGroup, MenuText, MenuTime ) +function TASK:SetPlannedMenuForGroup( TaskGroup, MenuTime ) self:E( TaskGroup:GetName() ) local Mission = self:GetMission() @@ -603,14 +699,26 @@ function TASK:SetPlannedMenuForGroup( TaskGroup, MenuText, MenuTime ) local CommandCenter = Mission:GetCommandCenter() local CommandCenterMenu = CommandCenter:GetMenu() + local TaskType = self:GetType() +-- local TaskThreatLevel = self.TaskInfo["ThreatLevel"] +-- local TaskThreatLevelString = TaskThreatLevel and " [" .. string.rep( "â– ", TaskThreatLevel ) .. "]" or " []" + local TaskPlayerCount = self:GetPlayerCount() + local TaskPlayerString = string.format( " (%dp)", TaskPlayerCount ) + local TaskText = string.format( "%s%s", self:GetName(), TaskPlayerString ) --, TaskThreatLevelString ) + local TaskName = string.format( "%s", self:GetName() ) + local MissionMenu = MENU_GROUP:New( TaskGroup, MissionName, CommandCenterMenu ):SetTime( MenuTime ) - local MissionMenu = Mission:GetMenu( TaskGroup ) - local TaskType = self:GetType() - local TaskTypeMenu = MENU_GROUP:New( TaskGroup, TaskType, MissionMenu ):SetTime( MenuTime ) - local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, MenuText, TaskTypeMenu, self.MenuAssignToGroup, { self = self, TaskGroup = TaskGroup } ):SetTime( MenuTime ):SetRemoveParent( true ) + local TaskPlannedMenu = MENU_GROUP:New( TaskGroup, "Planned Tasks", MissionMenu ):SetTime( MenuTime ) + local TaskTypeMenu = MENU_GROUP:New( TaskGroup, TaskType, TaskPlannedMenu ):SetTime( MenuTime ):SetRemoveParent( true ) + local TaskTypeMenu = MENU_GROUP:New( TaskGroup, TaskText, TaskTypeMenu ):SetTime( MenuTime ):SetRemoveParent( true ) + local ReportTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Report Task Status" ), TaskTypeMenu, self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ):SetRemoveParent( true ) + + if not Mission:IsGroupAssigned( TaskGroup ) then + local JoinTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Join Task" ), TaskTypeMenu, self.MenuAssignToGroup, { self = self, TaskGroup = TaskGroup } ):SetTime( MenuTime ):SetRemoveParent( true ) + end return self end @@ -621,15 +729,27 @@ end -- @param #number MenuTime -- @return #TASK self function TASK:SetAssignedMenuForGroup( TaskGroup, MenuTime ) - self:E( TaskGroup:GetName() ) + self:F( { TaskGroup:GetName(), MenuTime } ) local Mission = self:GetMission() + local MissionName = Mission:GetName() + local CommandCenter = Mission:GetCommandCenter() + local CommandCenterMenu = CommandCenter:GetMenu() + + local TaskType = self:GetType() +-- local TaskThreatLevel = self.TaskInfo["ThreatLevel"] +-- local TaskThreatLevelString = TaskThreatLevel and " [" .. string.rep( "â– ", TaskThreatLevel ) .. "]" or " []" + local TaskPlayerCount = self:GetPlayerCount() + local TaskPlayerString = string.format( " (%dp)", TaskPlayerCount ) + local TaskText = string.format( "%s%s", self:GetName(), TaskPlayerString ) --, TaskThreatLevelString ) + local TaskName = string.format( "%s", self:GetName() ) + + local MissionMenu = MENU_GROUP:New( TaskGroup, MissionName, CommandCenterMenu ):SetTime( MenuTime ) local MissionMenu = Mission:GetMenu( TaskGroup ) - self:E( { MissionMenu = MissionMenu } ) - - local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Task Status", MissionMenu, self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ) - local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Abort Task", MissionMenu, self.MenuTaskAbort, self, TaskGroup ):SetTime( MenuTime ) + local TaskAssignedMenu = MENU_GROUP:New( TaskGroup, string.format( "Assigned Task %s", TaskName ), MissionMenu ):SetTime( MenuTime ) + local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Report Task Status" ), TaskAssignedMenu, self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ):SetRemoveParent( true ) + local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Abort Group from Task" ), TaskAssignedMenu, self.MenuTaskAbort, self, TaskGroup ):SetTime( MenuTime ):SetRemoveParent( true ) return self end @@ -639,14 +759,12 @@ end -- @param #number MenuTime -- @return #TASK function TASK:RemoveMenu( MenuTime ) - self:F() + self:F( { self:GetName(), MenuTime } ) for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do local TaskGroup = TaskGroup -- Wrapper.Group#GROUP if TaskGroup:IsAlive() and TaskGroup:GetPlayerNames() then - if not self:IsAssignedToGroup( TaskGroup ) then - self:RemovePlannedMenuForGroup( TaskGroup, MenuTime ) - end + self:RefreshMenus( TaskGroup, MenuTime ) end end end @@ -657,24 +775,26 @@ end -- @param Wrapper.Group#GROUP TaskGroup -- @param #number MenuTime -- @return #TASK self -function TASK:RemovePlannedMenuForGroup( TaskGroup, MenuTime ) - self:F() +function TASK:RefreshMenus( TaskGroup, MenuTime ) + self:F( { TaskGroup:GetName(), MenuTime } ) local Mission = self:GetMission() local MissionName = Mission:GetName() - + local CommandCenter = Mission:GetCommandCenter() + local CommandCenterMenu = CommandCenter:GetMenu() + local MissionMenu = Mission:GetMenu( TaskGroup ) + + local TaskName = self:GetName() + local PlannedMenu = MissionMenu:GetMenu( "Planned Tasks" ) + local AssignedMenu = MissionMenu:GetMenu( string.format( "Assigned Task %s", TaskName ) ) - if MissionMenu then - local TaskType = self:GetType() - local TypeMenu = MissionMenu:GetMenu( TaskType ) - - if TypeMenu then - local TaskMenu = TypeMenu:GetMenu( self:GetTaskName() ) - if TaskMenu then - TaskMenu:Remove( MenuTime ) - end - end + if PlannedMenu then + PlannedMenu:Remove( MenuTime ) + end + + if AssignedMenu then + AssignedMenu:Remove( MenuTime ) end end @@ -723,7 +843,7 @@ end -- @param #TASK self function TASK:MenuTaskAbort( TaskGroup ) - self:Abort() + self:AbortGroup( TaskGroup ) end @@ -735,6 +855,13 @@ function TASK:GetTaskName() return self.TaskName end +--- Returns the @{Task} briefing. +-- @param #TASK self +-- @return #string Task briefing. +function TASK:GetTaskBriefing() + return self.TaskBriefing +end + @@ -794,11 +921,14 @@ end function TASK:RemoveStateMachine( TaskUnit ) self:F( { TaskUnit, self.Fsm[TaskUnit] ~= nil } ) - self:E( self.Fsm ) - for TaskUnitT, Fsm in pairs( self.Fsm ) do - self:E( TaskUnitT ) - end + --self:E( self.Fsm ) + --for TaskUnitT, Fsm in pairs( self.Fsm ) do + --local Fsm = Fsm -- Core.Fsm#FSM_PROCESS + --self:E( TaskUnitT ) + --self.Fsm[TaskUnit] = nil + --end + self.Fsm[TaskUnit]:Remove() self.Fsm[TaskUnit] = nil collectgarbage() @@ -1001,10 +1131,18 @@ end -- @param #string TaskBriefing -- @return #TASK self function TASK:SetBriefing( TaskBriefing ) + self:E(TaskBriefing) self.TaskBriefing = TaskBriefing return self end +--- Gets the @{Task} briefing. +-- @param #TASK self +-- @return #string The briefing text. +function TASK:GetBriefing() + return self.TaskBriefing +end + @@ -1017,14 +1155,17 @@ function TASK:onenterAssigned( From, Event, To, PlayerUnit, PlayerName ) self:E( { "Task Assigned", self.Dispatcher } ) - self:MessageToGroups( "Task " .. self:GetName() .. " has been assigned to your group." ) - - if self.Dispatcher then - self:E( "Firing Assign event " ) - self.Dispatcher:Assign( self, PlayerUnit, PlayerName ) + if From ~= "Assigned" then + self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " is assigned." ) + if self.Dispatcher then + self:E( "Firing Assign event " ) + self.Dispatcher:Assign( self, PlayerUnit, PlayerName ) + end + + self:GetMission():__Start( 1 ) + + self:SetMenu() end - - self:GetMission():__Start( 1 ) end @@ -1053,12 +1194,13 @@ end function TASK:onenterAborted( From, Event, To ) self:E( "Task Aborted" ) - - self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " has been aborted! Task may be replanned." ) - self:UnAssignFromGroups() + if From ~= "Aborted" then + self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " has been aborted! Task may be replanned." ) + self:__Replan( 5 ) + self:SetMenu() + end - self:__Replan( 5 ) end --- FSM function for a TASK @@ -1098,7 +1240,7 @@ end function TASK:onstatechange( From, Event, To ) if self:IsTrace() then - MESSAGE:New( "@ Task " .. self.TaskName .. " : " .. Event .. " changed to state " .. To, 2 ):ToAll() + --MESSAGE:New( "@ Task " .. self.TaskName .. " : " .. From .. " changed to " .. To .. " by " .. Event, 2 ):ToAll() end if self.Scores[To] then @@ -1189,6 +1331,47 @@ function TASK:ReportOverview() --R2.1 fixed report. Now nicely formatted and con return Report:Text() end +--- Create a count of the players in the Task. +-- @param #TASK self +-- @return #number The total number of players in the task. +function TASK:GetPlayerCount() --R2.1 Get a count of the players. + + local PlayerCount = 0 + + -- Loop each Unit active in the Task, and find Player Names. + for TaskGroupID, PlayerGroup in pairs( self:GetGroups():GetSet() ) do + local PlayerGroup = PlayerGroup -- Wrapper.Group#GROUP + if self:IsGroupAssigned( PlayerGroup ) then + local PlayerNames = PlayerGroup:GetPlayerNames() + PlayerCount = PlayerCount + #PlayerNames + end + end + + return PlayerCount +end + + +--- Create a list of the players in the Task. +-- @param #TASK self +-- @return #map<#string,Wrapper.Group#GROUP> A map of the players +function TASK:GetPlayerNames() --R2.1 Get a map of the players. + + local PlayerNameMap = {} + + -- Loop each Unit active in the Task, and find Player Names. + for TaskGroupID, PlayerGroup in pairs( self:GetGroups():GetSet() ) do + local PlayerGroup = PlayerGroup -- Wrapper.Group#GROUP + if self:IsGroupAssigned( PlayerGroup ) then + local PlayerNames = PlayerGroup:GetPlayerNames() + for PlayerNameID, PlayerName in pairs( PlayerNames ) do + PlayerNameMap[PlayerName] = PlayerGroup + end + end + end + + return PlayerNameMap +end + --- Create a detailed report of the Task. -- List the Task Status, and the Players assigned to the Task. @@ -1203,18 +1386,13 @@ function TASK:ReportDetails() --R2.1 fixed report. Now nicely formatted and cont -- Determine the status of the Task. local State = self:GetState() - + -- Loop each Unit active in the Task, and find Player Names. - local PlayerNames = {} + local PlayerNames = self:GetPlayerNames() + local PlayerReport = REPORT:New() - for PlayerGroupID, PlayerGroupData in pairs( self:GetGroups():GetSet() ) do - - local PlayerGroup = PlayerGroupData -- Wrapper.Group#GROUP - - PlayerNames = PlayerGroup:GetPlayerNames() - if PlayerNames then - PlayerReport:Add( "Group " .. PlayerGroup:GetCallsign() .. ": " .. table.concat( PlayerNames, ", " ) ) - end + for PlayerName, PlayerGroup in pairs( PlayerNames ) do + PlayerReport:Add( "Group " .. PlayerGroup:GetCallsign() .. ": " .. PlayerName ) end local Players = PlayerReport:Text() diff --git a/Moose Development/Moose/Tasking/Task_A2G.lua b/Moose Development/Moose/Tasking/Task_A2G.lua index 6814847bd..8c99d85f1 100644 --- a/Moose Development/Moose/Tasking/Task_A2G.lua +++ b/Moose Development/Moose/Tasking/Task_A2G.lua @@ -86,15 +86,13 @@ do -- TASK_A2G -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. -- @return #TASK_A2G self - function TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskType ) - local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType ) ) -- Tasking.Task#TASK_A2G + function TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskType, TaskBriefing ) + local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType, TaskBriefing ) ) -- Tasking.Task#TASK_A2G self:F() self.TargetSetUnit = TargetSetUnit self.TaskType = TaskType - Mission:AddTask( self ) - local Fsm = self:GetUnitProcess() @@ -366,14 +364,28 @@ do -- TASK_SEAD --- Instantiates a new TASK_SEAD. -- @param #TASK_SEAD self -- @param Tasking.Mission#MISSION Mission - -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. + -- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. -- @param #string TaskName The name of the Task. - -- @param Set#SET_UNIT TargetSetUnit + -- @param Core.Set#SET_UNIT TargetSetUnit + -- @param #string TaskBriefing The briefing of the task. -- @return #TASK_SEAD self - function TASK_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit ) - local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "SEAD" ) ) -- #TASK_SEAD + function TASK_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing ) + local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "SEAD", TaskBriefing ) ) -- #TASK_SEAD self:F() + Mission:AddTask( self ) + + local TargetCoord = TargetSetUnit:GetFirst():GetCoordinate() + local TargetPositionText = TargetCoord:ToString() + local TargetThreatLevel = TargetSetUnit:CalculateThreatLevelA2G() + + self:SetBriefing( + TaskBriefing or + "Execute a Suppression of Enemy Air Defenses.\n" .. + "Initial Coordinates: " .. TargetPositionText .. "\n" .. + "Threat Level: [" .. string.rep( "â– ", TargetThreatLevel ) .. "]" + ) + return self end @@ -392,17 +404,28 @@ do -- TASK_BAI --- Instantiates a new TASK_BAI. -- @param #TASK_BAI self -- @param Tasking.Mission#MISSION Mission - -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. + -- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. -- @param #string TaskName The name of the Task. - -- @param Set#SET_UNIT UnitSetTargets - -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. - -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. - -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. + -- @param Core.Set#SET_UNIT TargetSetUnit + -- @param #string TaskBriefing The briefing of the task. -- @return #TASK_BAI self - function TASK_BAI:New( Mission, SetGroup, TaskName, TargetSetUnit ) - local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "BAI" ) ) -- #TASK_BAI + function TASK_BAI:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing ) + local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "BAI", TaskBriefing ) ) -- #TASK_BAI self:F() + Mission:AddTask( self ) + + local TargetCoord = TargetSetUnit:GetFirst():GetCoordinate() + local TargetPositionText = TargetCoord:ToString() + local TargetThreatLevel = TargetSetUnit:CalculateThreatLevelA2G() + + self:SetBriefing( + TaskBriefing or + "Execute a Battleground Air Interdiction of a group of enemy targets.\n" .. + "Initial Coordinates: " .. TargetPositionText .. "\n" .. + "Threat Level: [" .. string.rep( "â– ", TargetThreatLevel ) .. "]" + ) + return self end @@ -421,17 +444,29 @@ do -- TASK_CAS --- Instantiates a new TASK_CAS. -- @param #TASK_CAS self -- @param Tasking.Mission#MISSION Mission - -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. + -- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. -- @param #string TaskName The name of the Task. - -- @param Set#SET_UNIT UnitSetTargets - -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. - -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. - -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. + -- @param Core.Set#SET_UNIT TargetSetUnit + -- @param #string TaskBriefing The briefing of the task. -- @return #TASK_CAS self - function TASK_CAS:New( Mission, SetGroup, TaskName, TargetSetUnit ) - local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "CAS" ) ) -- #TASK_CAS + function TASK_CAS:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing ) + local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "CAS", TaskBriefing ) ) -- #TASK_CAS self:F() + Mission:AddTask( self ) + + local TargetCoord = TargetSetUnit:GetFirst():GetCoordinate() + local TargetPositionText = TargetCoord:ToString() + local TargetThreatLevel = TargetSetUnit:CalculateThreatLevelA2G() + + self:SetBriefing( + TaskBriefing or + "Execute a Close Air Support for a group of enemy targets.\n" .. + "Beware of friendlies at the vicinity!\n" .. + "Initial Coordinates: " .. TargetPositionText .. "\n" .. + "Threat Level: [" .. string.rep( "â– ", TargetThreatLevel ) .. "]" + ) + return self end diff --git a/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua index db260cfb0..f7101eea4 100644 --- a/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua @@ -55,6 +55,7 @@ do -- TASK_A2G_DISPATCHER ClassName = "TASK_A2G_DISPATCHER", Mission = nil, Detection = nil, + Tasks = {}, } @@ -181,14 +182,16 @@ do -- TASK_A2G_DISPATCHER -- @param #TASK_A2G_DISPATCHER self -- @param Tasking.Mission#MISSION Mission -- @param Tasking.Task#TASK Task - -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem + -- @param #boolean DetectedItemID + -- @param #boolean DetectedItemChange -- @return Tasking.Task#TASK - function TASK_A2G_DISPATCHER:EvaluateRemoveTask( Mission, Task, DetectedItem ) + function TASK_A2G_DISPATCHER:EvaluateRemoveTask( Mission, Task, DetectedItemID, DetectedItemChanged ) if Task then - if Task:IsStatePlanned() and DetectedItem.Changed == true then + if Task:IsStatePlanned() and DetectedItemChanged == true then self:E( "Removing Tasking: " .. Task:GetTaskName() ) - Task = Mission:RemoveTask( Task ) + Mission:RemoveTask( Task ) + self.Tasks[DetectedItemID] = nil end end @@ -208,82 +211,78 @@ do -- TASK_A2G_DISPATCHER local ChangeMsg = {} local Mission = self.Mission - - --- First we need to the detected targets. - for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do - local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem - local DetectedSet = DetectedItem.Set -- Functional.Detection#DETECTION_BASE.DetectedSet - local DetectedZone = DetectedItem.Zone - self:E( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } ) - DetectedSet:Flush() - - local ItemID = DetectedItem.ID - - -- Evaluate SEAD Tasking - local SEADTask = Mission:GetTask( string.format( "SEAD.%03d", ItemID ) ) - SEADTask = self:EvaluateRemoveTask( Mission, SEADTask, DetectedItem ) - if not SEADTask then - local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed... - if TargetSetUnit then - local Task = TASK_SEAD:New( Mission, self.SetGroup, string.format( "SEAD.%03d", ItemID ), TargetSetUnit ) - Task:SetTargetZone( DetectedZone ) - Task:SetDispatcher( self ) - Task:SetInfo( "Detection", Detection:DetectedItemReportSummary( DetectedItemID ) ) - Task:SetInfo( "Changes", Detection:GetChangeText( DetectedItem ) ) - SEADTask = Mission:AddTask( Task ) - end - end - - -- Evaluate CAS Tasking - local CASTask = Mission:GetTask( string.format( "CAS.%03d", ItemID ) ) - CASTask = self:EvaluateRemoveTask( Mission, CASTask, DetectedItem ) - if not CASTask then - local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed... - if TargetSetUnit then - local Task = TASK_CAS:New( Mission, self.SetGroup, string.format( "CAS.%03d", ItemID ), TargetSetUnit ) - Task:SetTargetZone( DetectedZone ) - Task:SetDispatcher( self ) - Task:SetInfo( "Detection", Detection:DetectedItemReportSummary( DetectedItemID ) ) - Task:SetInfo( "Changes", Detection:GetChangeText( DetectedItem ) ) - CASTask = Mission:AddTask( Task ) - end - end - - -- Evaluate BAI Tasking - local BAITask = Mission:GetTask( string.format( "BAI.%03d", ItemID ) ) - BAITask = self:EvaluateRemoveTask( Mission, BAITask, DetectedItem ) - if not BAITask then - local TargetSetUnit = self:EvaluateBAI( DetectedItem, self.Mission:GetCommandCenter():GetPositionable():GetCoalition() ) -- Returns a SetUnit if there are targets to be SEADed... - if TargetSetUnit then - local Task = TASK_BAI:New( Mission, self.SetGroup, string.format( "BAI.%03d", ItemID ), TargetSetUnit ) - Task:SetTargetZone( DetectedZone ) - Task:SetDispatcher( self ) - Task:SetInfo( "Detection", Detection:DetectedItemReportSummary( DetectedItemID ) ) - Task:SetInfo( "Changes", Detection:GetChangeText( DetectedItem ) ) - BAITask = Mission:AddTask( Task ) - end - end - - -- OK, so the tasking has been done, now delete the changes reported for the area. - Detection:AcceptChanges( DetectedItem ) - - end + if Mission:IsIDLE() or Mission:IsENGAGED() then - -- TODO set menus using the HQ coordinator - Mission:GetCommandCenter():SetMenu() - - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - Mission:GetCommandCenter():MessageToGroup( string.format( "There are %d tasks remaining for mission *%s*. Subscribe to a task using the menu.", Mission:GetTasksRemaining(), Mission:GetName() ), TaskGroup ) - if not TaskGroup:GetState( TaskGroup, "Assigned" ) then --- Mission:GetCommandCenter():MessageToGroup( --- string.format( "HQ Reporting - Planned tasks for mission '%s':\n\n%s\n", --- self.Mission:GetName(), --- string.format( "%s\n\n%s\n\n%s\n\n%s", ReportSEAD:Text(), ReportCAS:Text(), ReportBAI:Text(), ReportChanges:Text() --- ) --- ), TaskGroup --- ) + local TaskReport = REPORT:New() + + --- First we need to the detected targets. + for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do + + local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem + local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT + local DetectedZone = DetectedItem.Zone + self:E( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } ) + DetectedSet:Flush() + + local DetectedItemID = DetectedItem.ID + local DetectedItemChanged = DetectedItem.Changed + + local Task = self.Tasks[DetectedItemID] + Task = self:EvaluateRemoveTask( Mission, Task, DetectedItemID, DetectedItemChanged ) -- Task will be removed if it is planned and changed. + + -- Evaluate SEAD + if not Task then + local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed... + if TargetSetUnit then + Task = TASK_SEAD:New( Mission, self.SetGroup, string.format( "SEAD.%03d", DetectedItemID ), TargetSetUnit ) + end + + -- Evaluate CAS + if not Task then + local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be CASed... + if TargetSetUnit then + Task = TASK_CAS:New( Mission, self.SetGroup, string.format( "CAS.%03d", DetectedItemID ), TargetSetUnit ) + end + + -- Evaluate BAI + if not Task then + local TargetSetUnit = self:EvaluateBAI( DetectedItem, self.Mission:GetCommandCenter():GetPositionable():GetCoalition() ) -- Returns a SetUnit if there are targets to be BAIed... + if TargetSetUnit then + Task = TASK_BAI:New( Mission, self.SetGroup, string.format( "BAI.%03d", DetectedItemID ), TargetSetUnit ) + end + end + end + + if Task then + self.Tasks[DetectedItemID] = Task + Task:SetTargetZone( DetectedZone ) + Task:SetDispatcher( self ) + Task:SetInfo( "ThreatLevel", DetectedSet:CalculateThreatLevelA2G() ) + Task:SetInfo( "Detection", Detection:DetectedItemReportSummary( DetectedItemID ) ) + Task:SetInfo( "Changes", Detection:GetChangeText( DetectedItem ) ) + Mission:AddTask( Task ) + else + self:E("This should not happen") + end + + end + + TaskReport:Add( Task:GetName() ) + + -- OK, so the tasking has been done, now delete the changes reported for the area. + Detection:AcceptChanges( DetectedItem ) end + + -- TODO set menus using the HQ coordinator + Mission:GetCommandCenter():SetMenu() + + for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do + if not Mission:IsGroupAssigned(TaskGroup) then + Mission:GetCommandCenter():MessageToGroup( string.format( "Mission *%s* has tasks %s. Subscribe to a task using the Mission *Overlord* radio menu.", Mission:GetName(), TaskReport:Text(", ") ), TaskGroup ) + end + end + end return true diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index 7ad7013e9..776fe499b 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -153,7 +153,7 @@ do -- TASK_CARGO -- -- === -- - -- @field #TASK_CARGO TASK_CARGO + -- @field #TASK_CARGO -- TASK_CARGO = { ClassName = "TASK_CARGO", @@ -166,9 +166,10 @@ do -- TASK_CARGO -- @param #string TaskName The name of the Task. -- @param Core.Set#SET_CARGO SetCargo The scope of the cargo to be transported. -- @param #string TaskType The type of Cargo task. + -- @param #string TaskBriefing The Cargo Task briefing. -- @return #TASK_CARGO self - function TASK_CARGO:New( Mission, SetGroup, TaskName, SetCargo, TaskType ) - local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType ) ) -- #TASK_CARGO + function TASK_CARGO:New( Mission, SetGroup, TaskName, SetCargo, TaskType, TaskBriefing ) + local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType, TaskBriefing ) ) -- #TASK_CARGO self:F( {Mission, SetGroup, TaskName, SetCargo, TaskType}) self.SetCargo = SetCargo @@ -181,24 +182,24 @@ do -- TASK_CARGO Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "SelectAction", Rejected = "Reject" } ) - Fsm:AddTransition( { "Assigned", "WaitingForCommand", "ArrivedAtPickup", "ArrivedAtDeploy", "Boarded", "UnBoarded", "Landed" }, "SelectAction", "WaitingForCommand" ) + Fsm:AddTransition( { "Assigned", "WaitingForCommand", "ArrivedAtPickup", "ArrivedAtDeploy", "Boarded", "UnBoarded", "Landed", "Boarding" }, "SelectAction", "*" ) - Fsm:AddTransition( "WaitingForCommand", "RouteToPickup", "RoutingToPickup" ) + Fsm:AddTransition( "*", "RouteToPickup", "RoutingToPickup" ) Fsm:AddProcess ( "RoutingToPickup", "RouteToPickupPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtPickup" } ) Fsm:AddTransition( "Arrived", "ArriveAtPickup", "ArrivedAtPickup" ) - Fsm:AddTransition( "WaitingForCommand", "RouteToDeploy", "RoutingToDeploy" ) + Fsm:AddTransition( "*", "RouteToDeploy", "RoutingToDeploy" ) Fsm:AddProcess ( "RoutingToDeploy", "RouteToDeployZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtDeploy" } ) Fsm:AddTransition( "Arrived", "ArriveAtDeploy", "ArrivedAtDeploy" ) Fsm:AddTransition( { "ArrivedAtPickup", "ArrivedAtDeploy", "Landing" }, "Land", "Landing" ) Fsm:AddTransition( "Landing", "Landed", "Landed" ) - Fsm:AddTransition( "WaitingForCommand", "PrepareBoarding", "AwaitBoarding" ) + Fsm:AddTransition( "*", "PrepareBoarding", "AwaitBoarding" ) Fsm:AddTransition( "AwaitBoarding", "Board", "Boarding" ) Fsm:AddTransition( "Boarding", "Boarded", "Boarded" ) - Fsm:AddTransition( "WaitingForCommand", "PrepareUnBoarding", "AwaitUnBoarding" ) + Fsm:AddTransition( "*", "PrepareUnBoarding", "AwaitUnBoarding" ) Fsm:AddTransition( "AwaitUnBoarding", "UnBoard", "UnBoarding" ) Fsm:AddTransition( "UnBoarding", "UnBoarded", "UnBoarded" ) @@ -211,71 +212,79 @@ do -- TASK_CARGO --- -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task_Cargo#TASK_CARGO Task - function Fsm:onenterWaitingForCommand( TaskUnit, Task ) + -- @param Tasking.Task_CARGO#TASK_CARGO Task + function Fsm:onafterSelectAction( TaskUnit, Task ) self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - if TaskUnit.Menu then - TaskUnit.Menu:Remove() - end + local MenuTime = timer.getTime() - TaskUnit.Menu = MENU_GROUP:New( TaskUnit:GetGroup(), Task:GetName() .. " @ " .. TaskUnit:GetName() ) + TaskUnit.Menu = MENU_GROUP:New( TaskUnit:GetGroup(), Task:GetName() .. " @ " .. TaskUnit:GetName() ):SetTime( MenuTime ) Task.SetCargo:ForEachCargo( --- @param Core.Cargo#CARGO Cargo function( Cargo ) - if Cargo:IsUnLoaded() then - if Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then - MENU_GROUP_COMMAND:New( - TaskUnit:GetGroup(), - "Board cargo " .. Cargo.Name, - TaskUnit.Menu, - self.MenuBoardCargo, - self, - Cargo - ) - else - MENU_GROUP_COMMAND:New( - TaskUnit:GetGroup(), - "Route to Pickup cargo " .. Cargo.Name, - TaskUnit.Menu, - self.MenuRouteToPickup, - self, - Cargo - ) - end - end - - if Cargo:IsLoaded() then - for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do - if Cargo:IsInZone( DeployZone ) then + + if Cargo:IsAlive() then + + if Cargo:IsUnLoaded() then + if Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), - "Unboard cargo " .. Cargo.Name, + "Board cargo " .. Cargo.Name, TaskUnit.Menu, - self.MenuUnBoardCargo, + self.MenuBoardCargo, self, - Cargo, - DeployZone - ) + Cargo + ):SetTime(MenuTime) else MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), - "Route to Deploy cargo at " .. DeployZoneName, + "Route to Pickup cargo " .. Cargo.Name, TaskUnit.Menu, - self.MenuRouteToDeploy, + self.MenuRouteToPickup, self, - DeployZone - ) + Cargo + ):SetTime(MenuTime) + end + end + + if Cargo:IsLoaded() then + + MENU_GROUP_COMMAND:New( + TaskUnit:GetGroup(), + "Unboard cargo " .. Cargo.Name, + TaskUnit.Menu, + self.MenuUnBoardCargo, + self, + Cargo + ):SetTime(MenuTime) + + -- Deployzones are optional zones that can be selected to request routing information. + for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do + if not Cargo:IsInZone( DeployZone ) then + MENU_GROUP_COMMAND:New( + TaskUnit:GetGroup(), + "Route to Deploy cargo at " .. DeployZoneName, + TaskUnit.Menu, + self.MenuRouteToDeploy, + self, + DeployZone + ):SetTime(MenuTime) + end end end end end ) + + TaskUnit.Menu:Remove( MenuTime ) + self:__SelectAction( -15 ) + + Task:GetMission():GetCommandCenter():MessageToGroup("Cargo menu is ready ...", TaskUnit:GetGroup() ) end --- @@ -308,13 +317,19 @@ do -- TASK_CARGO -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task_Cargo#TASK_CARGO Task + -- @param From + -- @param Event + -- @param To + -- @param Core.Cargo#CARGO Cargo function Fsm:onafterRouteToPickup( TaskUnit, Task, From, Event, To, Cargo ) self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + if Cargo:IsAlive() then + self.Cargo = Cargo -- Core.Cargo#CARGO + Task:SetCargoPickup( self.Cargo, TaskUnit ) + self:__RouteToPickupPoint( -0.1 ) + end - self.Cargo = Cargo - Task:SetCargoPickup( self.Cargo, TaskUnit ) - self:__RouteToPickupPoint( -0.1 ) end @@ -325,10 +340,13 @@ do -- TASK_CARGO function Fsm:onafterArriveAtPickup( TaskUnit, Task ) self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - if TaskUnit:IsAir() then - self:__Land( -0.1, "Pickup" ) - else - self:__SelectAction( -0.1 ) + if self.Cargo:IsAlive() then + if TaskUnit:IsAir() then + self.Cargo.CargoObject:GetUnit(1):SmokeRed() + self:__Land( -0.1, "Pickup" ) + else + self:__SelectAction( -0.1 ) + end end end @@ -369,19 +387,21 @@ do -- TASK_CARGO function Fsm:onafterLand( TaskUnit, Task, From, Event, To, Action ) self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - if self.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then - if TaskUnit:InAir() then - Task:GetMission():GetCommandCenter():MessageToGroup( "Land", TaskUnit:GetGroup() ) - self:__Land( -10, Action ) + if self.Cargo:IsAlive() then + if self.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then + if TaskUnit:InAir() then + Task:GetMission():GetCommandCenter():MessageToGroup( "Land", TaskUnit:GetGroup() ) + self:__Land( -10, Action ) + else + Task:GetMission():GetCommandCenter():MessageToGroup( "Landed ...", TaskUnit:GetGroup() ) + self:__Landed( -0.1, Action ) + end else - Task:GetMission():GetCommandCenter():MessageToGroup( "Landed ...", TaskUnit:GetGroup() ) - self:__Landed( -0.1, Action ) - end - else - if Action == "Pickup" then - self:__RouteToPickupZone( -0.1 ) - else - self:__RouteToDeployZone( -0.1 ) + if Action == "Pickup" then + self:__RouteToPickupZone( -0.1 ) + else + self:__RouteToDeployZone( -0.1 ) + end end end end @@ -393,17 +413,19 @@ do -- TASK_CARGO function Fsm:onafterLanded( TaskUnit, Task, From, Event, To, Action ) self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - if self.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then - if TaskUnit:InAir() then - self:__Land( -0.1, Action ) + if self.Cargo:IsAlive() then + if self.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then + if TaskUnit:InAir() then + self:__Land( -0.1, Action ) + else + self:__SelectAction( -0.1 ) + end else - self:__SelectAction( -0.1 ) - end - else - if Action == "Pickup" then - self:__RouteToPickupZone( -0.1 ) - else - self:__RouteToDeployZone( -0.1 ) + if Action == "Pickup" then + self:__RouteToPickupZone( -0.1 ) + else + self:__RouteToDeployZone( -0.1 ) + end end end end @@ -415,8 +437,10 @@ do -- TASK_CARGO function Fsm:onafterPrepareBoarding( TaskUnit, Task, From, Event, To, Cargo ) self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - self.Cargo = Cargo -- Core.Cargo#CARGO_GROUP - self:__Board( -0.1 ) + if Cargo and Cargo:IsAlive() then + self.Cargo = Cargo -- Core.Cargo#CARGO_GROUP + self:__Board( -0.1 ) + end end --- @@ -427,23 +451,21 @@ do -- TASK_CARGO self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) function self.Cargo:OnEnterLoaded( From, Event, To, TaskUnit, TaskProcess ) - self:E({From, Event, To, TaskUnit, TaskProcess }) - TaskProcess:__Boarded( 0.1 ) - end - - if self.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then - if TaskUnit:InAir() then - --- ABORT the boarding. Split group if any and go back to select action. + if self.Cargo:IsAlive() then + if self.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then + if TaskUnit:InAir() then + --- ABORT the boarding. Split group if any and go back to select action. + else + self.Cargo:MessageToGroup( "Boarding ...", TaskUnit:GetGroup() ) + self.Cargo:Board( TaskUnit, 20, self ) + end else - self.Cargo:MessageToGroup( "Boarding ...", TaskUnit:GetGroup() ) - self.Cargo:Board( TaskUnit, 20, self ) + --self:__ArriveAtCargo( -0.1 ) end - else - --self:__ArriveAtCargo( -0.1 ) end end @@ -460,8 +482,10 @@ do -- TASK_CARGO -- TODO:I need to find a more decent solution for this. Task:E( { CargoPickedUp = Task.CargoPickedUp } ) - if Task.CargoPickedUp then - Task:CargoPickedUp( TaskUnit, self.Cargo ) + if self.Cargo:IsAlive() then + if Task.CargoPickedUp then + Task:CargoPickedUp( TaskUnit, self.Cargo ) + end end end @@ -471,31 +495,50 @@ do -- TASK_CARGO -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task_Cargo#TASK_CARGO Task - function Fsm:onafterPrepareUnBoarding( TaskUnit, Task, From, Event, To, Cargo, DeployZone ) - self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + -- @param From + -- @param Event + -- @param To + -- @param Cargo + -- @param Core.Zone#ZONE_BASE DeployZone + function Fsm:onafterPrepareUnBoarding( TaskUnit, Task, From, Event, To, Cargo ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID(), From, Event, To, Cargo } ) self.Cargo = Cargo - self.DeployZone = DeployZone - self:__UnBoard( -0.1 ) + self.DeployZone = nil + + -- Check if the Cargo is at a deployzone... If it is, provide it as a parameter! + if Cargo:IsAlive() then + for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do + if Cargo:IsInZone( DeployZone ) then + self.DeployZone = DeployZone -- Core.Zone#ZONE_BASE + break + end + end + self:__UnBoard( -0.1, Cargo, self.DeployZone ) + end end --- -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task_Cargo#TASK_CARGO Task - function Fsm:onafterUnBoard( TaskUnit, Task ) - self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + -- @param From + -- @param Event + -- @param To + -- @param Cargo + -- @param Core.Zone#ZONE_BASE DeployZone + function Fsm:onafterUnBoard( TaskUnit, Task, From, Event, To, Cargo, DeployZone ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID(), From, Event, To, Cargo, DeployZone } ) function self.Cargo:OnEnterUnLoaded( From, Event, To, DeployZone, TaskProcess ) - - self:E({From, Event, To, TaskUnit, TaskProcess }) - + self:E({From, Event, To, DeployZone, TaskProcess }) TaskProcess:__UnBoarded( -0.1 ) - end - self.Cargo:MessageToGroup( "UnBoarding ...", TaskUnit:GetGroup() ) - self.Cargo:UnBoard( self.DeployZone:GetPointVec2(), 20, self ) + if self.Cargo:IsAlive() then + self.Cargo:MessageToGroup( "UnBoarding ...", TaskUnit:GetGroup() ) + self.Cargo:UnBoard( DeployZone:GetPointVec2(), 400, self ) + end end @@ -510,8 +553,10 @@ do -- TASK_CARGO -- TODO:I need to find a more decent solution for this. Task:E( { CargoDeployed = Task.CargoDeployed } ) - if Task.CargoDeployed then - Task:CargoDeployed( TaskUnit, self.Cargo, self.DeployZone ) + if self.Cargo:IsAlive() then + if Task.CargoDeployed then + Task:CargoDeployed( TaskUnit, self.Cargo, self.DeployZone ) + end end self:__SelectAction( 1 ) @@ -685,9 +730,10 @@ do -- TASK_CARGO_TRANSPORT -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. -- @param #string TaskName The name of the Task. -- @param Core.Set#SET_CARGO SetCargo The scope of the cargo to be transported. + -- @param #string TaskBriefing The Cargo Task briefing. -- @return #TASK_CARGO_TRANSPORT self - function TASK_CARGO_TRANSPORT:New( Mission, SetGroup, TaskName, SetCargo ) - local self = BASE:Inherit( self, TASK_CARGO:New( Mission, SetGroup, TaskName, SetCargo, "Transport" ) ) -- #TASK_CARGO_TRANSPORT + function TASK_CARGO_TRANSPORT:New( Mission, SetGroup, TaskName, SetCargo, TaskBriefing ) + local self = BASE:Inherit( self, TASK_CARGO:New( Mission, SetGroup, TaskName, SetCargo, "Transport", TaskBriefing ) ) -- #TASK_CARGO_TRANSPORT self:F() Mission:AddTask( self ) @@ -698,8 +744,6 @@ do -- TASK_CARGO_TRANSPORT self:AddTransition( "*", "CargoPickedUp", "*" ) self:AddTransition( "*", "CargoDeployed", "*" ) - do - --- OnBefore Transition Handler for Event CargoPickedUp. -- @function [parent=#TASK_CARGO_TRANSPORT] OnBeforeCargoPickedUp -- @param #TASK_CARGO_TRANSPORT self @@ -731,9 +775,7 @@ do -- TASK_CARGO_TRANSPORT -- @param #number Delay The delay in seconds. -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - end - do --- OnBefore Transition Handler for Event CargoDeployed. -- @function [parent=#TASK_CARGO_TRANSPORT] OnBeforeCargoDeployed -- @param #TASK_CARGO_TRANSPORT self @@ -769,10 +811,26 @@ do -- TASK_CARGO_TRANSPORT -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. - end local Fsm = self:GetUnitProcess() + local CargoReport = REPORT:New( "Transport Cargo. The following cargo needs to be transported including initial positions:") + + SetCargo:ForEachCargo( + --- @param Core.Cargo#CARGO Cargo + function( Cargo ) + local CargoType = Cargo:GetType() + local CargoName = Cargo:GetName() + local CargoCoordinate = Cargo:GetCoordinate() + CargoReport:Add( string.format( '- "%s" (%s) at %s', CargoName, CargoType, CargoCoordinate:ToString() ) ) + end + ) + + self:SetBriefing( + TaskBriefing or + CargoReport:Text() + ) + return self end @@ -804,7 +862,7 @@ do -- TASK_CARGO_TRANSPORT end end end - + return CargoDeployed end diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 21a67981b..ec62872f1 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -1782,7 +1782,7 @@ function CONTROLLABLE:GetDetectedTargets( DetectVisual, DetectOptical, DetectRad return nil end -function CONTROLLABLE:IsTargetDetected( DCSObject ) +function CONTROLLABLE:IsTargetDetected( DCSObject, DetectVisual, DetectOptical, DetectRadar, DetectIRST, DetectRWR, DetectDLINK ) self:F2( self.ControllableName ) local DCSControllable = self:GetDCSObject() diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 3964388f1..3be9bc187 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -156,7 +156,6 @@ end -- @param #GROUP self -- @return Dcs.DCSWrapper.Group#Group The DCS Group. function GROUP:GetDCSObject() - self:F(self.GroupName) local DCSGroup = Group.getByName( self.GroupName ) if DCSGroup then @@ -1142,7 +1141,7 @@ do -- Players end end - self:F( PlayerNames ) + self:F2( PlayerNames ) return PlayerNames end diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 7a192ba64..c815b7bfb 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -334,7 +334,7 @@ function POSITIONABLE:GetMessageText( Message, Name ) --R2.1 added local DCSObject = self:GetDCSObject() if DCSObject then Name = Name and ( " (" .. Name .. ")" ) or "" - local Callsign = self:GetCallsign() ~= "" and self:GetCallsign() or self:GetName() + local Callsign = string.format( "[%s]", self:GetCallsign() ~= "" and self:GetCallsign() or self:GetName() ) local MessageText = Callsign .. Name .. ": " .. Message return MessageText end @@ -383,12 +383,19 @@ end -- @param #string Message The message text -- @param Dcs.DCSTYpes#Duration Duration The duration of the message. -- @param Dcs.DCScoalition#coalition MessageCoalition The Coalition receiving the message. --- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -function POSITIONABLE:MessageToCoalition( Message, Duration, MessageCoalition, Name ) +function POSITIONABLE:MessageToCoalition( Message, Duration, MessageCoalition ) self:F2( { Message, Duration } ) + local Name = "" + local DCSObject = self:GetDCSObject() if DCSObject then + if MessageCoalition == coalition.side.BLUE then + Name = "Blue coalition" + end + if MessageCoalition == coalition.side.RED then + Name = "Red coalition" + end self:GetMessage( Message, Duration, Name ):ToCoalition( MessageCoalition ) end diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 0107839b5..c99c2a7a4 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -724,7 +724,7 @@ function UNIT:IsInZone( Zone ) if self:IsAlive() then local IsInZone = Zone:IsVec3InZone( self:GetVec3() ) - self:T( { IsInZone } ) + self:T2( { IsInZone } ) return IsInZone end diff --git a/Moose Mission Setup/Moose.lua b/Moose Mission Setup/Moose.lua index bd6df9c23..c384faa86 100644 --- a/Moose Mission Setup/Moose.lua +++ b/Moose Mission Setup/Moose.lua @@ -1,5 +1,5 @@ env.info( '*** MOOSE DYNAMIC INCLUDE START *** ' ) -env.info( 'Moose Generation Timestamp: 20170424_1257' ) +env.info( 'Moose Generation Timestamp: 20170426_1017' ) local base = _G diff --git a/Utils/Deploy.sh b/Utils/Deploy.sh new file mode 100644 index 000000000..5527631f0 --- /dev/null +++ b/Utils/Deploy.sh @@ -0,0 +1,5 @@ +cd slate +git add -A +git commit -a -m "Doc Update" +git push +./deploy.sh \ No newline at end of file diff --git a/Utils/GenerateDocumentations.bat b/Utils/GenerateDocumentations.bat new file mode 100644 index 000000000..d106f5189 --- /dev/null +++ b/Utils/GenerateDocumentations.bat @@ -0,0 +1,13 @@ +@echo off + +:: Generate Luadocumentor documentation +echo Generating LuaDocumentor Documentation +echo -------------------------------------- +call luadocumentor.bat + +:: Generate Slate documentation +echo Generating Slate Documentation +echo ------------------------------ +cd "Slate Documentation Generator" +call Generate.bat +cd .. \ No newline at end of file diff --git a/Utils/Slate Documentation Generator/Generate.bat b/Utils/Slate Documentation Generator/Generate.bat new file mode 100644 index 000000000..bcd2fd145 --- /dev/null +++ b/Utils/Slate Documentation Generator/Generate.bat @@ -0,0 +1,16 @@ +@echo off +:: Generate the Markdown doc +"./bin/SlateDocGenerator2.exe" "../../../MOOSE/Moose Development/Moose" ../../../slate/source + +:: Do some cleanup +del /s /q "TreeHierarchySorted.csv" +del /s /q "FuctionList.txt" +rmdir /s /q "./bin/TEMP" + +:: Copy the Images that go with the documentation +robocopy ../../docs/Presentations ../../../slate/source/includes/Pictures /MIR /NJH /NJS + +:: Deploy the Slate documentation +echo A shell will open. To deploy the Slate website, please run the following command. Otherwise, simply close the shell. +echo $ Moose/Utils/Deploy.sh +%localappdata%\GitHub\GitHub.appref-ms --open-shell \ No newline at end of file diff --git a/Utils/Slate Documentation Generator/bin/SlateDocGenerator2.exe b/Utils/Slate Documentation Generator/bin/SlateDocGenerator2.exe new file mode 100644 index 000000000..ad688e4b7 Binary files /dev/null and b/Utils/Slate Documentation Generator/bin/SlateDocGenerator2.exe differ diff --git a/Utils/Slate Documentation Generator/bin/TreeHierarchy.csv b/Utils/Slate Documentation Generator/bin/TreeHierarchy.csv new file mode 100644 index 000000000..41eb85a6a --- /dev/null +++ b/Utils/Slate Documentation Generator/bin/TreeHierarchy.csv @@ -0,0 +1,35 @@ +@K=function, @M=Task_A2G, @N=onafterRouteToRendezVous, @P=Fsm, @F=../../../MOOSE/Moose Development/Moose\Tasking\Task_A2G.lua, @C=5129, +@K=function, @M=Task_A2G, @N=OnAfterArriveAtRendezVous, @P=Fsm, @F=../../../MOOSE/Moose Development/Moose\Tasking\Task_A2G.lua, @C=5789, +@K=function, @M=Task_A2G, @N=onafterEngage, @P=Fsm, @F=../../../MOOSE/Moose Development/Moose\Tasking\Task_A2G.lua, @C=6186, +@K=function, @M=Task_A2G, @N=onafterRouteToTarget, @P=Fsm, @F=../../../MOOSE/Moose Development/Moose\Tasking\Task_A2G.lua, @C=6508, +@K=function, @M=Task_A2G, @N=onafterRouteToTargets, @P=Fsm, @F=../../../MOOSE/Moose Development/Moose\Tasking\Task_A2G.lua, @C=7380, +@K=function, @M=Task_Cargo, @N=onenterWaitingForCommand, @P=Fsm, @F=../../../MOOSE/Moose Development/Moose\Tasking\Task_CARGO.lua, @C=10431, +@K=function, @M=Task_Cargo, @N=OnLeaveWaitingForCommand, @P=Fsm, @F=../../../MOOSE/Moose Development/Moose\Tasking\Task_CARGO.lua, @C=12652, +@K=function, @M=Task_Cargo, @N=onafterRouteToPickup, @P=Fsm, @F=../../../MOOSE/Moose Development/Moose\Tasking\Task_CARGO.lua, @C=13428, +@K=function, @M=Task_Cargo, @N=onafterArriveAtPickup, @P=Fsm, @F=../../../MOOSE/Moose Development/Moose\Tasking\Task_CARGO.lua, @C=13882, +@K=function, @M=Task_Cargo, @N=onafterRouteToDeploy, @P=Fsm, @F=../../../MOOSE/Moose Development/Moose\Tasking\Task_CARGO.lua, @C=14310, +@K=function, @M=Task_Cargo, @N=onafterArriveAtDeploy, @P=Fsm, @F=../../../MOOSE/Moose Development/Moose\Tasking\Task_CARGO.lua, @C=14757, +@K=function, @M=Task_Cargo, @N=onafterLand, @P=Fsm, @F=../../../MOOSE/Moose Development/Moose\Tasking\Task_CARGO.lua, @C=15187, +@K=function, @M=Task_Cargo, @N=onafterLanded, @P=Fsm, @F=../../../MOOSE/Moose Development/Moose\Tasking\Task_CARGO.lua, @C=16069, +@K=function, @M=Task_Cargo, @N=onafterPrepareBoarding, @P=Fsm, @F=../../../MOOSE/Moose Development/Moose\Tasking\Task_CARGO.lua, @C=16762, +@K=function, @M=Task_Cargo, @N=onafterBoard, @P=Fsm, @F=../../../MOOSE/Moose Development/Moose\Tasking\Task_CARGO.lua, @C=17166, +@K=function, @M=Task_Cargo, @N=onafterBoarded, @P=Fsm, @F=../../../MOOSE/Moose Development/Moose\Tasking\Task_CARGO.lua, @C=18074, +@K=function, @M=Task_Cargo, @N=onafterPrepareUnBoarding, @P=Fsm, @F=../../../MOOSE/Moose Development/Moose\Tasking\Task_CARGO.lua, @C=18704, +@K=function, @M=Task_Cargo, @N=onafterUnBoard, @P=Fsm, @F=../../../MOOSE/Moose Development/Moose\Tasking\Task_CARGO.lua, @C=19134, +@K=function, @M=Task_Cargo, @N=onafterUnBoarded, @P=Fsm, @F=../../../MOOSE/Moose Development/Moose\Tasking\Task_CARGO.lua, @C=19788, +@K=function, @M=Designate, @N=OnBeforeLaseOn, @P=DESIGNATE , @F=../../../MOOSE/Moose Development/Moose\Functional\Designate.lua, @C=12232, +@K=function, @M=Designate, @N=OnAfterLaseOn, @P=DESIGNATE , @F=../../../MOOSE/Moose Development/Moose\Functional\Designate.lua, @C=12480, +@K=function, @M=Designate, @N=LaseOn, @P=DESIGNATE , @F=../../../MOOSE/Moose Development/Moose\Functional\Designate.lua, @C=12701, +@K=function, @M=Designate, @N=__LaseOn, @P=DESIGNATE , @F=../../../MOOSE/Moose Development/Moose\Functional\Designate.lua, @C=12824, +@K=function, @M=Designate, @N=OnBeforeLaseOff, @P=DESIGNATE , @F=../../../MOOSE/Moose Development/Moose\Functional\Designate.lua, @C=13114, +@K=function, @M=Designate, @N=OnAfterLaseOff, @P=DESIGNATE , @F=../../../MOOSE/Moose Development/Moose\Functional\Designate.lua, @C=13364, +@K=function, @M=Designate, @N=LaseOff, @P=DESIGNATE , @F=../../../MOOSE/Moose Development/Moose\Functional\Designate.lua, @C=13587, +@K=function, @M=Designate, @N=__LaseOff, @P=DESIGNATE , @F=../../../MOOSE/Moose Development/Moose\Functional\Designate.lua, @C=13712, +@K=function, @M=Designate, @N=OnBeforeSmoke, @P=DESIGNATE , @F=../../../MOOSE/Moose Development/Moose\Functional\Designate.lua, @C=13944, +@K=function, @M=Designate, @N=OnAfterSmoke, @P=DESIGNATE , @F=../../../MOOSE/Moose Development/Moose\Functional\Designate.lua, @C=14190, +@K=function, @M=Designate, @N=Smoke, @P=DESIGNATE , @F=../../../MOOSE/Moose Development/Moose\Functional\Designate.lua, @C=14409, +@K=function, @M=Designate, @N=__Smoke, @P=DESIGNATE , @F=../../../MOOSE/Moose Development/Moose\Functional\Designate.lua, @C=14530, +@K=function, @M=Designate, @N=OnBeforeStatus, @P=DESIGNATE , @F=../../../MOOSE/Moose Development/Moose\Functional\Designate.lua, @C=15628, +@K=function, @M=Designate, @N=OnAfterStatus, @P=DESIGNATE , @F=../../../MOOSE/Moose Development/Moose\Functional\Designate.lua, @C=15876, +@K=function, @M=Designate, @N=Status, @P=DESIGNATE , @F=../../../MOOSE/Moose Development/Moose\Functional\Designate.lua, @C=16097, +@K=function, @M=Designate, @N=__Status, @P=DESIGNATE , @F=../../../MOOSE/Moose Development/Moose\Functional\Designate.lua, @C=16220, diff --git a/Utils/Slate Documentation Generator/src/DataStorer.au3 b/Utils/Slate Documentation Generator/src/DataStorer.au3 new file mode 100644 index 000000000..b5903009b --- /dev/null +++ b/Utils/Slate Documentation Generator/src/DataStorer.au3 @@ -0,0 +1,174 @@ +; This file only constains function related to storing the hierarchy in a tree-like tructure + +Func AddNode($Kind, $Module ,$Node, $Parent, $File, $CarretPos) + FileSetPos($DataFile, 0, $FILE_END) + + If $Parent == "" And $Kind == "type" Then + $Parent = "ROOT" + ElseIf $Kind == "module" Then + $Module = " " + $Parent = " " + EndIf + FileWrite($DataFile, "@K="&$Kind&", @M="&$Module&", @N="&$Node&", @P="&$Parent&", @F="&$File&", @C="&$CarretPos&","&@CRLF) +EndFunc + +; Search node by name and returns one data +Func GetData($Node, $Data) + FileSetPos($DataFile, 0, $FILE_BEGIN) + Local $CurrentLine = "" + Local $CurrentData + Local $RegexResult + Local $Regex + Switch $Data + Case "kind" + $Regex = "\@K=(.+?)," + Case "parent" + $Regex = "\@P=(.+?)," + Case "file" + $Regex = "\@F=(.+?)," + Case "carretpos" + $Regex = "\@C=(.+?)," + EndSwitch + FileSetPos($DataFile, 0, $FILE_BEGIN) + Do + $CurrentLine = FileReadLine($DataFile) + If @error == -1 Then + Return "" + ExitLoop + EndIf + $CurrentData = StringRegExp($CurrentLine, "\@N=(.+?),", $STR_REGEXPARRAYMATCH) + + Until $Node == $CurrentData[0] + $CurrentData = StringRegExp($CurrentLine, $Regex, $STR_REGEXPARRAYMATCH) + Return $CurrentData[0] +EndFunc + + +; Returns an array of parent nodes, up to the root, starting with the root +Func GetParents($Node) + Local $CurrentParent = $Node + Local $ParentsArray[0] + Local $NbOfParents = 1 + + While $CurrentParent <> "ROOT" + ReDim $ParentsArray[$NbOfParents] + $ParentsArray[$NbOfParents-1] = $CurrentParent + + $CurrentParent = GetData($CurrentParent, "parent") + If $CurrentParent == "" Then + FileWrite($Log, "ERROR : Couldn't find "&$ParentsArray[$NbOfParents-1]&"'s parent !") + $CurrentParent = "ERROR !" + ReDim $ParentsArray[$NbOfParents] + $ParentsArray[$NbOfParents-1] = $CurrentParent + ExitLoop + EndIf + $NbOfParents += 1 + WEnd + + _ArrayReverse($ParentsArray) + _ArrayDelete($ParentsArray, $NbOfParents) + Return $ParentsArray +EndFunc + + + +Func DataSort() + Local $SortedDataFile = FileOpen(@ScriptDir & "\TreeHierarchySorted.csv", $FO_OVERWRITE) + Local $Line = "" + Local $LineNb = 1 + Local $RegexResults + Local $CurrentModule + Local $CurrentType + + FileSetPos($DataFile, 0, $FILE_BEGIN) + + While True + $Line = FileReadLine($DataFile) + If @error then ExitLoop + + $RegexResults = StringRegExp($Line, "\@K=(.+?),", $STR_REGEXPARRAYMATCH) + If $RegexResults[0] == "module" Then + ConsoleWrite(".") + $RegexResults = StringRegExp($Line, "\@N=(.+?),", $STR_REGEXPARRAYMATCH) + $CurrentModule = $RegexResults[0] + FileWriteLine($SortedDataFile, $Line) + FileClose($DataFile) + _FileWriteToLine(@ScriptDir & "\TreeHierarchy.csv", $LineNb, "", True) + $DataFile = FileOpen(@ScriptDir & "\TreeHierarchy.csv", 1) + FileSetPos($DataFile, 0, $FILE_BEGIN) + $LineNb = 1 + + While True + $Line = FileReadLine($DataFile) + If @error then ExitLoop + + $RegexResults = StringRegExp($Line, "\@K=(.+?),", $STR_REGEXPARRAYMATCH) + If $RegexResults[0] == "type" Then + $RegexResults = StringRegExp($Line, "\@M=(.+?),", $STR_REGEXPARRAYMATCH) + If $RegexResults[0] == $CurrentModule Then + $RegexResults = StringRegExp($Line, "\@N=(.+?),", $STR_REGEXPARRAYMATCH) + $CurrentType = $RegexResults[0] + FileWriteLine($SortedDataFile, $Line) + FileClose($DataFile) + _FileWriteToLine(@ScriptDir & "\TreeHierarchy.csv", $LineNb, "", True) + $DataFile = FileOpen(@ScriptDir & "\TreeHierarchy.csv", 1) + FileSetPos($DataFile, 0, $FILE_BEGIN) + $LineNb = 1 + + While True + $Line = FileReadLine($DataFile) + If @error then ExitLoop + + $RegexResults = StringRegExp($Line, "\@K=(.+?),", $STR_REGEXPARRAYMATCH) + If $RegexResults[0] == "function" Then + $RegexResults = StringRegExp($Line, "\@P=(.+?),", $STR_REGEXPARRAYMATCH) + If $RegexResults[0] == $CurrentType Then + FileWriteLine($SortedDataFile, $Line) + FileClose($DataFile) + _FileWriteToLine(@ScriptDir & "\TreeHierarchy.csv", $LineNb, "", True) + $DataFile = FileOpen(@ScriptDir & "\TreeHierarchy.csv", 1) + FileSetPos($DataFile, 0, $FILE_BEGIN) + $LineNb = 0 + EndIf + EndIf + $LineNb += 1 + WEnd + FileSetPos($DataFile, 0, $FILE_BEGIN) + $LineNb = 0 + EndIf + EndIf + $LineNb += 1 + WEnd + FileSetPos($DataFile, 0, $FILE_BEGIN) + $LineNb = 0 + EndIf + $LineNb += 1 + Wend + If FileGetSize(@ScriptDir & "\TreeHierarchy.csv") <> 0 Then + FileWrite($Log, "ERROR : Some items couldn't be sorted. Verify them in the file TreeHierarchy.csv"&@CRLF) + ConsoleWrite(@CRLF&"INFO : Some items couldn't be sorted. Verify them in the file TreeHierarchy.csv"&@CRLF) + EndIf + FileClose($DataFile) + $DataFile = $SortedDataFile +EndFunc + + +Func FindInFunctionList($String) + Local $Line = "" + Local $TempStringArray + FileSetPos($FunctionList, 0, $FILE_BEGIN) + ;FileWrite($Log, 'Trying to find the function prototype for : ' & $String & @CRLF) + While 1 + $Line = FileReadLine($FunctionList) + If @error = -1 Then + SetError(0) + FileWrite($Log, "ERROR : Couldn't find " & $String & " in file. Does this method exitsts ?" & @CRLF) + Return $String + EndIf + If StringInStr($Line, $String) Then + $TempStringArray = StringSplit($Line, "-") + $Line = "[" & $TempStringArray[1] & ":" & $TempStringArray[2] & "()]" & '(#' & StringLower($Line) & ')' + Return $Line + EndIf + WEnd +EndFunc \ No newline at end of file diff --git a/Utils/Slate Documentation Generator/src/Parser.au3 b/Utils/Slate Documentation Generator/src/Parser.au3 new file mode 100644 index 000000000..307b98d20 --- /dev/null +++ b/Utils/Slate Documentation Generator/src/Parser.au3 @@ -0,0 +1,460 @@ +; This file include every function strictly related to the parsing of data in .lua files + +; Get the first comment block after $CarretPos +; We will also grab function declaration if possible/applicable +; The return is a Array : CarretPosition|BlockContent|Declaration|CarretPositionStart +Func ReadNextBlock($File, $CarretPos) + local $CommentBlock = "" ; This is where we'll store the comment block + local $Declaration = "" ; This is the next line after the comment block : usually the declaration statement + local $CurrentLine = "" + local $CurrentCarretPos = 0 + + local $IsCommentBlock = False + + local $RegExResult + local $RegexPos + + ; Start reading from $CarretPos + FileSetPos($File, $CarretPos, $FILE_BEGIN) + + ; Read till we find a comment block + Do + $CurrentLine = FileReadLine($File) + If @error Then ; We probably reached the eof + Local $ReturnArray[3] = [$CurrentCarretPos, "", ""] + Return $ReturnArray + ElseIf StringInStr($CurrentLine, "---") Then + $IsCommentBlock = True + EndIf + Until $IsCommentBlock + + Local $CarretPosStart = FileGetPos($File) - StringLen($CurrentLine) - 2 + + ; Add the first line to our comment block + $RegExResult = StringRegExp($CurrentLine, "---(.*)", $STR_REGEXPARRAYMATCH) + If Not @error Then ; The first line of the comment could be empty ! + $CommentBlock &= $RegExResult[0]&@CRLF + EndIf + + ; Read the comment block + Do + $CurrentCarretPos = FileGetPos($File) + $CurrentLine = FileReadLine($File) + If StringInStr($CurrentLine, "--") Then ; If we can't find any "--" in the line, then it's not the comment block anymore + $RegExResult = StringRegExp($CurrentLine, "--(.*)", $STR_REGEXPARRAYMATCH) + If Not @error Then; The line of the comment could be empty ! + $CommentBlock &= $RegExResult[0]&@CRLF + EndIf + Else + $IsCommentBlock = False + EndIf + Until Not $IsCommentBlock + + ; Ok, so now this is strange. If the comment block is class', we're going to have to check the + ; next comment block. If this next comment block contains a field, that is the same name as the class, then this + ; new comment block contains the whole informtion for the class. This is very shitty, but it's a workaround to + ; make intellisense show classes info while programing + If ParseForOneTag($CommentBlock, "@type") Then + Local $CommentBlock2 = "" + Do + $CurrentLine = FileReadLine($File) + If @error Then + Local $ReturnArray[3] = [$CurrentCarretPos, "", ""] + Return $ReturnArray + ElseIf StringInStr($CurrentLine, "---") Then + $IsCommentBlock = True + EndIf + Until $IsCommentBlock + + $RegExResult = StringRegExp($CurrentLine, "---(.*)", $STR_REGEXPARRAYMATCH) + If Not @error Then + $CommentBlock2 &= $RegExResult[0]&@CRLF + EndIf + + ; Yep, the next comment is the description of the class, let's read on ! + If StringInStr($CurrentLine, ParseForOneTag($CommentBlock, "@type")) And StringInStr($CurrentLine, "extend") Then + + Do + $CurrentLine = FileReadLine($File) + If StringInStr($CurrentLine, "--") Then + $RegExResult = StringRegExp($CurrentLine, "--(.*)", $STR_REGEXPARRAYMATCH) + If Not @error Then + $CommentBlock2 &= $RegExResult[0]&@CRLF + EndIf + Else + $IsCommentBlock = False + EndIf + Until Not $IsCommentBlock + + ; remove the line(s) with "@field" in the comment block. They are only needed for the intellisense hack + While 1 + $RegexResult = StringRegExp($CommentBlock2, "(.*)@field(.*)", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $CommentBlock2 = StringRegExpReplace($CommentBlock2, "(.*)@field(.*)", "", 1) + WEnd + + ; We also don't need the first line of the first comment block anymore... + ; $CommentBlock = StringRegExpReplace($CommentBlock, "(.*)", "", 1) + + ; append the description at the start of the comment block + $CommentBlock = $CommentBlock2&$CommentBlock + EndIf + + + ; We also need to check if the type is a list or a map. If so, the comment block does not describe a class, but a simple list / map. + ; It will have the formatting of a class, though, because it's closer closer to the actual code, even though it is highly confusing. + ; But it will only have 1 field : the list or map. + If StringInStr($CommentBlock, "@list") Then + $RegExResult = StringRegExp($CommentBlock, "@list\h<(.*?)>\h(.*)", $STR_REGEXPARRAYMATCH) + if not @error Then + $CommentBlock &= "@field #table["&$RegExResult[0]&"] "&$RegExResult[1] + EndIf + EndIf + ; TODO : Add support for @map the same way... + EndIf + + + + + ; We'll take the next line, as it might be the declaration statement + $Declaration = $CurrentLine + + + + ; let's do some cleanup + $CommentBlock = StringRegExpReplace($CommentBlock, "(?m)^\h+", "") ;remove leading whitespaces + $CommentBlock = StringRegExpReplace($CommentBlock, "(?m)\h+$", "") ;remove trailing whitespaces + $CommentBlock = StringRegExpReplace($CommentBlock, "(?m)^[#]+", "##### ") + $CommentBlock = StringRegExpReplace($CommentBlock, "(?m)^\h+", "") ;remove leading whitespaces again now that we removed the "#"s + $CommentBlock = StringRegExpReplace($CommentBlock, "(?m)-{3,}", "") ;remove sequences of at least 3 "-" which will mess up markdown + $CommentBlock = StringRegExpReplace($CommentBlock, "(?m)={3,}", "") ; remove sequences of at least 3 "=" which will mess up markdown + + Local $ReturnArray[4] = [$CurrentCarretPos, $CommentBlock, $Declaration, $CarretPosStart] + Return $ReturnArray +EndFunc + +; Parses the block and returns the data for one tag +; don't use it to find the function tag ! +Func ParseForOneTag($Block, $Tag) + Local $i = 1 + Local $DataArray[1] + Local $RegexResult[1] + Local $RegexPos = 1 + Local $Regex + + ; If we look for @usage, then it's a multiline data, the regex is different + If $Tag == "@usage" Then + $Regex = "(?s)@usage(.*)" + $RegexResult = StringRegExp($Block, $Regex, $STR_REGEXPARRAYMATCH, $RegexPos) + Else + $Regex = $Tag&"\h(.*)\s" + $RegexResult = StringRegExp($Block, $Regex, $STR_REGEXPARRAYMATCH, $RegexPos) + Endif + + If @error Then + Return "" + Else + Return $RegexResult[0] + EndIf + +EndFunc ;==>ReadOneTag + +; Parses the block and returns the data for multiple tags in an array +; Don't use it for @param ! +Func ParseForTags($Block, $Tag) + Local $i = 1 + Local $DataArray[1] + Local $RegexResult[1] + Local $RegexPos = 1 + + Local $Regex = $Tag&"(?m)\h([^\s]*)(?:\h)?([^\s]*)?(?:\h)?(.*)?$" + ; For each tag + While True + $RegexResult = StringRegExp($Block, $Regex, $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If $RegexPos == 0 Then ; We couldn't find any tag + If Not $DataArray[0] Then + Return "" + Else + Return $DataArray + EndIf + EndIf + + ; Add the tag to the array.The array looks like this : type1|param1|description1|type2... + ReDim $DataArray[$i * 3] + $DataArray[($i * 3) - 3] = $RegexResult[0] + If $RegexResult[1] == "" Then + $DataArray[($i * 3) - 2] = "self" ; if the first param doesn't have a name, then it's self + Else + $DataArray[($i * 3) - 2] = $RegexResult[1] + EndIf + $DataArray[($i * 3) - 1] = $RegexResult[2] + $i += 1 + WEnd +EndFunc + +; Parses both the comment block and the declaration to find the function name and it's type +; Compares both of them if possible, but will always return the one in the comment block if possible +Func ParseFunctionName($CommentBlock, $Declaration) + local $RegExResult + local $FunctionNameFromDec + local $FunctionNameFromComment + local $ReturnArray[2] + + ; Parse for function name in both the comment block and the desclaration + $RegExResult = StringRegExp($CommentBlock, "\@function\h(?:(\[.*\]\h))?(.*)", $STR_REGEXPARRAYMATCH) + If Not @error Then + $FunctionNameFromComment = $RegExResult[1] + EndIf + $RegExResult = StringRegExp($Declaration, "function\h(?:.*\:)?(.*)\(.*\)", $STR_REGEXPARRAYMATCH) + If Not @error Then + $FunctionNameFromDec = $RegExResult[0] + EndIf + + ; compare them to each other + If $FunctionNameFromComment Then + If $FunctionNameFromDec <> $FunctionNameFromComment Then + FileWrite($Log,"CAUTION : The commented function doesn't match its declaration : "&$FunctionNameFromComment& " -> "&$Declaration&@CRLF) + EndIf + $ReturnArray[0] = $FunctionNameFromComment + ElseIf $FunctionNameFromDec Then + ;FileWrite($Log, "CAUTION: No data matching @function found in block, inferring the function name from its declaration : "& $FunctionNameFromDec & @CRLF) + $ReturnArray[0] = $FunctionNameFromDec + Else + $ReturnArray[0] = "" + $ReturnArray[1] = "" + return $ReturnArray + EndIf + + ;parses for function type in both the comment block and the desclaration + local $TypeFromComment + local $TypeFromDec + + $RegExResult = StringRegExp($Declaration, "function\h(.*):", $STR_REGEXPARRAYMATCH) + If Not @error Then + $TypeFromDec = $RegExResult[0] + EndIf + $RegExResult = StringRegExp($CommentBlock, "function\h\[parent=#(.*)\]", $STR_REGEXPARRAYMATCH) + If Not @error Then + $TypeFromComment = $RegExResult[0] + EndIf + + ; compare them to each other + If $TypeFromComment Then + If $TypeFromDec <> $TypeFromComment Then + FileWrite($Log,"CAUTION : The commented function type doesn't match its declaration : "&$TypeFromComment& " -> "&$Declaration&@CRLF) + EndIf + $ReturnArray[1] = $TypeFromComment + ElseIf $TypeFromDec Then + ;FileWrite($Log, "CAUTION: No function type found in block, inferring the function type from its declaration : "& $TypeFromDec & @CRLF) + $ReturnArray[1] = $TypeFromDec + Else + $ReturnArray[0] = "" + $ReturnArray[1] = "" + return $ReturnArray + EndIf + + Return $ReturnArray +EndFunc + +; Specifically designed to parse for @param tags +; will verify the comment by matching with the declaration (theoretically, I'm pretty sure it's bugged) +Func ParseParams($CommentBlock, $Declaration) + Local $ParamsFromComment = ParseForTags($CommentBlock, "@param") + Local $RegExResult + Local $RegexPos = StringInStr($Declaration, "(") + Local $ParamsFromDec[0] + Local $NbParam = 0 + + If StringInStr($Declaration, ":") Then + $NbParam = 1 + ReDim $ParamsFromDec[1] + $ParamsFromDec[0] = "self" + EndIf + + ; extract params from function decaration + While True + $RegExResult = StringRegExp($Declaration, "([^,\(\)\h]+)", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $NbParam += 1 + Redim $ParamsFromDec[$NbParam] + $ParamsFromDec[$NbParam-1] = $RegExResult[0] + WEnd + + ; compare these parameters with those found in the comment block + If UBound($ParamsFromComment) <> UBound($ParamsFromDec)*3 Then + FileWrite($Log, "CAUTION: The number of parameters don't match between the comment block and declaration "& @CRLF) + Else + + For $i=0 To $NbParam-1 + If $ParamsFromDec[$i] <> $ParamsFromComment[($i*3)+1] Then + FileWrite($Log, "CAUTION: Parameters missmatch between the comment block and declaration "& @CRLF) + FileWrite($Log, $ParamsFromComment[($i*3)+1]& " -> " & $ParamsFromDec[$i]&@CRLF) + ExitLoop + EndIf + Next + EndIf + + Return $ParamsFromComment +EndFunc + +; This does 3 things : +; - Replace the hyperlinks with new ones +; - change the stuff starting with # (#nil -> Nil) +; - Replace pictures paths +Func ReplaceHyperlinks($TempFile) + Local $StringFile = "" + Local $RegexResult + Local $RegexPos = 1 + Local $NewURL = "" + Local $i = 0 + FileSetPos($TempFile, 0, $FILE_BEGIN) + + $StringFile = FileRead($TempFile) + + ; Replace HyperLinks Using Regexs + ; --------------------------------------------------------- + While 1 ; @{File.Module} + $RegexResult = StringRegExp($StringFile, "\@{([A-Z][^\.#}]+)\.([^\.#}]+)}", $STR_REGEXPARRAYMATCH, $RegexPos) ; + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $NewURL = "[" & $RegexResult[1] & "](#" & StringLower($RegexResult[1]) & "-module-)" + ;FileWrite($Log, "Module : " & $RegexPos & " : " & _ArrayToString($RegexResult) & " -> " & $NewURL & @CRLF) + $StringFile = StringRegExpReplace($StringFile, "\@{([A-Z][^\.#}]+)\.([^\.#}]+)}", $NewURL, 1) + WEnd + While 1 ; @{Module} + $RegexResult = StringRegExp($StringFile, "\@{([A-Z][^\.#}]+)}", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $NewURL = "[" & $RegexResult[0] & "](#" & StringLower($RegexResult[0]) & "-module-)" + ;FileWrite($Log, "Module : " & $RegexPos & " : " & _ArrayToString($RegexResult) & " -> " & $NewURL & @CRLF) + $StringFile = StringRegExpReplace($StringFile, "\@{([A-Z][^\.#}]+)}", $NewURL, 1) + WEnd + While 1 ; @{File.Module#TYPE} + $RegexResult = StringRegExp($StringFile, "\@{([A-Z][^\.#}]+)\.([A-Z][^\.#}]+)#([A-Z,_]+)}", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $NewURL = "[" & $RegexResult[2] & "](#" & StringLower($RegexResult[2]) & "-class-)" + ;FileWrite($Log, "Class : " & $RegexPos & " : " & _ArrayToString($RegexResult) & " -> " & $NewURL & @CRLF) + $StringFile = StringRegExpReplace($StringFile, "\@{([A-Z][^\.#}]+)\.([A-Z][^\.#}]+)#([A-Z,_]+)}", $NewURL, 1) + WEnd + While 1 ; @{Module#TYPE} + $RegexResult = StringRegExp($StringFile, "\@{([A-Z][^\.#}]+)#([A-Z,_]+)}", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $NewURL = "[" & $RegexResult[1] & "](#" & StringLower($RegexResult[1]) & "-class-)" + ;FileWrite($Log, "Class : " & $RegexPos & " : " & _ArrayToString($RegexResult) & " -> " & $NewURL & @CRLF) + $StringFile = StringRegExpReplace($StringFile, "\@{([A-Z][^\.#}]+)#([A-Z,_]+)}", $NewURL, 1) + WEnd + While 1 ; @{#TYPE} + $RegexResult = StringRegExp($StringFile, "\@{#([A-Z,_]+)}", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $NewURL = "[" & $RegexResult[0] & "](#" & StringLower($RegexResult[0]) & "-class-)" + ;FileWrite($Log, "Class : " & $RegexPos & " : " & _ArrayToString($RegexResult) & " -> " & $NewURL & @CRLF) + $StringFile = StringRegExpReplace($StringFile, "\@{#([A-Z,_]+)}", $NewURL, 1) + WEnd + While 1 ; #TYPE&@CR + $RegexResult = StringRegExp($StringFile, "\h#([A-Z,_]+)\s", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $NewURL = " [" & $RegexResult[0] & "](#" & StringLower($RegexResult[0]) & "-class-)"&@CRLF + ;FileWrite($Log, "Class : " & $RegexPos & " : " & _ArrayToString($RegexResult) & " -> " & $NewURL & @CRLF) + $StringFile = StringRegExpReplace($StringFile, "\h#([A-Z,_]+)\s", $NewURL, 1) + WEnd + While 1 ; @{File.Module#TYPE.Function}(), catches the parenthesis + $RegexResult = StringRegExp($StringFile, "\@{([A-Z][^#}\.]+)\.([A-Z][^#}\.]+)#([A-Z,_]+)\.([^#\.]+)}[\(]?[\)]?", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $NewURL = FindInFunctionList($RegexResult[2] & "-" & $RegexResult[3]&"-") + ;FileWrite($Log, "Class : " & $RegexPos & " : " & _ArrayToString($RegexResult) & " -> " & $NewURL & @CRLF) + $StringFile = StringRegExpReplace($StringFile, "\@{([A-Z][^#}\.]+)\.([A-Z][^#}\.]+)#([A-Z,_]+)\.([^#\.]+)}[\(]?[\)]?", $NewURL, 1) + WEnd + While 1 ; @{Module#TYPE.Function}(), catches the parenthesis + $RegexResult = StringRegExp($StringFile, "\@{([A-Z][^#}\.]+)#([A-Z,_]+)\.([^#}\.]+)}[\(]?[\)]?", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $NewURL = FindInFunctionList($RegexResult[1] & "-" & $RegexResult[2]&"-") + ;FileWrite($Log, "Class : " & $RegexPos & " : " & _ArrayToString($RegexResult) & " -> " & $NewURL & @CRLF) + $StringFile = StringRegExpReplace($StringFile, "\@{([A-Z][^#}\.]+)#([A-Z,_]+)\.([^#}\.]+)}[\(]?[\)]?", $NewURL, 1) + WEnd + While 1 ; @{#TYPE.Function}(), catches the parenthesis + $RegexResult = StringRegExp($StringFile, "\@{#([A-Z,_]+)\.([^#}\.]+)}[\(]?[\)]?", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $NewURL = FindInFunctionList($RegexResult[0] & "-" & $RegexResult[1]&"-") + ;FileWrite($Log, "Class : " & $RegexPos & " : " & _ArrayToString($RegexResult) & " -> " & $NewURL & @CRLF) + $StringFile = StringRegExpReplace($StringFile, "\@{#([A-Z,_]+)\.([^#}\.]+)}[\(]?[\)]?", $NewURL, 1) + WEnd + While 1 ; Module#TYPE + $RegexResult = StringRegExp($StringFile, "\h(\w+[^\h\_])#(.*?)\h", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $NewURL = " [" & $RegexResult[1] & "](#" & StringLower($RegexResult[1]) & "-class-) " + ;FileWrite($Log, "Class : " & $RegexPos & " : " & _ArrayToString($RegexResult) & " -> " & $NewURL & @CRLF) + $StringFile = StringRegExpReplace($StringFile, "\h(\w+[^\h\_])#(.*?)\h", $NewURL, 1) + WEnd + While 1 ; File.Module#TYPE + $RegexResult = StringRegExp($StringFile, "\h(\w+)\.(\w+)#(.*?)\h", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $NewURL = " [" & $RegexResult[2] & "](#" & StringLower($RegexResult[2]) & "-class-) " + ;FileWrite($Log, "Class : " & $RegexPos & " : " & _ArrayToString($RegexResult) & " -> " & $NewURL & @CRLF) + $StringFile = StringRegExpReplace($StringFile, "\h(\w+)\.(\w+)#(.*?)\h", $NewURL, 1) + WEnd + While 1 ; #TYPE.type (nested type... really annoying and confusing lua stuff) + $RegexResult = StringRegExp($StringFile, "\h#([A-Z,_]+)\.(\w+)\h", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $NewURL = " [" & $RegexResult[1] & "](#" &StringLower($RegexResult[0])& "-"& StringLower($RegexResult[1]) & "-class-)" + ;FileWrite($Log, "Class : " & $RegexPos & " : " & _ArrayToString($RegexResult) & " -> " & $NewURL & @CRLF) + $StringFile = StringRegExpReplace($StringFile, "\h#([A-Z,_]+)\.(\w+)\h", $NewURL, 1) + WEnd + + ; Clean stuff with # + ; --------------------------------------------------------- + $StringFile = StringReplace($StringFile, "#nil", "Nil") + $StringFile = StringReplace($StringFile, "#number", "Number") + $StringFile = StringReplace($StringFile, "#boolean", "Boolean") + $StringFile = StringReplace($StringFile, "#string", "String") + $StringFile = StringReplace($StringFile, "#table", "List[]") + $StringFile = StringReplace($StringFile, "#function", "Function()") + + ; And replace the pictures Path if any + ; --------------------------------------------------------- + While 1 + $RegexResult = StringRegExp($StringFile, "!\[(.*)\]\(.*\\(.*)\\(.*)\)", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $NewPic = "![" & $RegexResult[0] & "](/includes/Pictures/" & $RegexResult[1] & "/"& $RegexResult[2]&")" + $StringFile = StringRegExpReplace($StringFile, "!\[(.*)\]\(.*\\(.*)\\(.*)\)", $NewPic, 1) + WEnd + + While 1 + $RegexResult = StringRegExp($StringFile, "(?m)^(\d(?:(\.\d))*\)(.*))$", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + ;$StringFile = StringRegExpReplace($StringFile, "(?m)^(\d(?:(\.\d))*\)(.*))$", "

"&$RegExResult[0]&"

", 1) + $StringFile = StringRegExpReplace($StringFile, "(?m)^(\d(?:(\.\d))*\)(.*))$", "##### "&$RegExResult[0], 1) + WEnd + + Return $StringFile +EndFunc \ No newline at end of file diff --git a/Utils/Slate Documentation Generator/src/SlateGenerator2.au3 b/Utils/Slate Documentation Generator/src/SlateGenerator2.au3 new file mode 100644 index 000000000..75f8ea703 --- /dev/null +++ b/Utils/Slate Documentation Generator/src/SlateGenerator2.au3 @@ -0,0 +1,310 @@ +#Region ;**** Directives created by AutoIt3Wrapper_GUI **** +#AutoIt3Wrapper_Outfile=..\bin\SlateDocGenerator2.exe +#AutoIt3Wrapper_Change2CUI=y +#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI **** +#cs +This is the main script + +The script goal is to read .lua file, extract the documentation in comment blocks, and write .md files to be converted to html by Slate : https://github.com/lord/slate +It works in 5 steps : + +First, it reads the .lua files one bt one, indentifying the comment blocks. for each comment block, it determines the kind of content the comment block describes (module, class/type or function), +find some usefull stuff (for exemple in the declaration...) and writes all of this info in the creatively named TreeHierarchy.csv, with this format : +@K=kind, @M=ParentModule, @N=Name, @P=Parent, @F=FileWhereTheCommentBlockIsLocated, @C=CarretPositionOfTheCommentBlock +The functions used to do this step are mostly found in Parser.au3 + +Then the second step is the longest : we sort the TreeHiearchy.csv, and put the result into TreeHierarchySorted.csv +The idea is to have the data in this order : +Module A +Class A (belongs to Module A) +Function A (belongs to Class A) +Function B (belongs to Class A) +Class B Class A (belongs to Module A) +Function C (belongs to Class B) +Function D (belongs to Class B) +Module B ... +The functions used to do this step are found in DataStorer.au3 + +Then, step 3 : We read each line of TreeHierarchySorted.csv, read the appropriate comment block in the .lua source files, +and write the appropriate Markdown documentation for it in a temporary folder +This is where the markdown documentation is actually written for the first time. +The functions used to do this step are found in Writer.au3 + +Step 4 ! We read the newly created Markdown files, trying to find hyperlinks/picture paths... and we replace them with the new ones. +We copy each processed file into it's final destination. +The functions used to do this step are mostly found in Parser.au3 + +And finally Step 5 : We add the new markdown files to Slate's index and delete temporary files and folder +#ce + +#include +#include +#include +#include + +; Those are the arguments that need to be passed at the start +Global $SourceFolder = $CmdLine[1] ;"./Results" +Global $OutputFolder = $CmdLine[2] ;"@ScriptDir&"/source/index.html.md" + +Global $Log = FileOpen(@ScriptDir & "\SlateGenerator2.log", 2) +Global $DataFile = FileOpen(@ScriptDir & "\TreeHierarchy.csv", 2) +Global $FunctionList = FileOpen(@ScriptDir & "\FuctionList.txt", 2) + +#include "Parser.au3" +#include "DataStorer.au3" +#include "Writer.au3" + + +Func ExitCleanly() + FileClose($DataFile) + FileClose($FunctionList) + FileWrite($Log, "SlateGenerator2 exited cleanly") + FileClose($Log) +EndFunc + + +; Small function to determine if a comment block is describing a module, a type or a function +Func IdentifyBlock($Block, $Declaration) + Local $Kind + Local $KindFunction + + $Kind = ParseForOneTag($Block, "@module") + If $Kind Then + Return "module" + EndIf + + $Kind = ParseForOneTag($Block, "@type") + If $Kind Then + Return "type" + EndIf + + + $KindFunction = ParseFunctionName($Block, $Declaration) + If $KindFunction[0] Then + Return "function" + EndIf + + Return "" +EndFunc + + + + +; ----------------------------------------------------------------- +; Main +; ----------------------------------------------------------------- + +; Step 1 ! +; ----------------------------------------------------------------- + +Local $SourceList = _FileListToArrayRec($SourceFolder, "*", $FLTAR_FILES, $FLTAR_RECUR, $FLTAR_NOSORT, $FLTAR_FULLPATH) +Local $CurrentFile +Local $CarretPos = 0 +Local $CommentBlock +Local $CommentKind +Local $CommentInfo[2] +Local $CurrentModule + +ConsoleWrite("1. Parsing Source Files... ") +FileWrite($Log, @CRLF&@CRLF&@TAB&"INFO : Building Hierarchy" & @CRLF) +For $i=1 To $SourceList[0] ; for each .lua source file + + + FileWrite($Log, "DEBUG : "&$SourceList[$i]) + + ; let's read the next .lua source file + $CurrentFile = FileOpen($SourceList[$i], $FO_READ) + FileWrite($Log, @CRLF&"INFO : Reading File "&$SourceList[$i] & @CRLF) + While True ; for each comment block in the current .lua source file + + ; We read the next comment block. If we could not, it's probably eof, time to open the next .lua file + $CommentBlock = ReadNextBlock($CurrentFile, $CarretPos) + If Not $CommentBlock[1] Then + ExitLoop + EndIf + + $CarretPos = $CommentBlock[0] + $CommentKind = IdentifyBlock($CommentBlock[1], $CommentBlock[2]) + ; Depending on the kind of comment block it is, we write the appropriate line in TreeHierarchy.csv + Switch $CommentKind + Case "function" + $CommentInfo = ParseFunctionName($CommentBlock[1], $CommentBlock[2]) + AddNode("function", $CurrentModule, $CommentInfo[0], $CommentInfo[1], $SourceList[$i], $CommentBlock[3]) + FileWrite($Log, "INFO : Added function "&$CommentInfo[0]&" to hierarchy" & @CRLF) + Case "type" + $CommentInfo[0] = ParseForOneTag($CommentBlock[1], "@type") + $CommentInfo[1] = ParseForOneTag($CommentBlock[1], "@extends") + $CommentInfo[1] = StringRegExpReplace($CommentInfo[1], "(.*#)", "") + AddNode("type", $CurrentModule, $CommentInfo[0], $CommentInfo[1], $SourceList[$i], $CommentBlock[3]) + FileWrite($Log, "INFO : Added type "&$CommentInfo[0]&" to hierarchy" & @CRLF) + Case "module" + $CurrentModule = ParseForOneTag($CommentBlock[1], "@module") + AddNode("module", "", $CurrentModule, "", $SourceList[$i], $CommentBlock[3]) + FileWrite($Log, "INFO : Added module "&$CurrentModule&" to hierarchy" & @CRLF) + EndSwitch + + WEnd + $CarretPos = 0 + FileClose($CurrentFile) + +Next +ConsoleWrite("Done"&@CRLF) + + +; Step 2 ! +; ----------------------------------------------------------------- +ConsoleWrite("2. Sorting Hierarchy") +FileWrite($Log, @CRLF&@CRLF&@TAB&"INFO : Sorting Hierarchy" & @CRLF) +; The magic happens in DataStorer.au3 +DataSort() +ConsoleWrite("Done"&@CRLF) + + + +; Step 3 ! +; ----------------------------------------------------------------- +ConsoleWrite("3. Writing Markdown Documentation") +FileWrite($Log, @CRLF&@CRLF&@TAB&"INFO : Writing Markdown Documentation" & @CRLF) + +Local $CurrentOutput +Local $CurrentFolder +Local $RegexResult +Local $Line +Local $CarretPos = 0 +Local $Results +Local $Output +Local $Declaration + +FileSetPos($DataFile, 0, $FILE_BEGIN) +While True ; For each line in TreeHierarchySorted.csv + + ; read the next line until eof + FileSetPos($DataFile, $CarretPos, $FILE_BEGIN) + $Line = FileReadLine($DataFile) + If @error Then ; eof + ExitLoop + Endif + + $CarretPos = FileGetPos($DataFile) + + ; find the file/position of the next comment block referenced in the line + $RegexResult = StringRegExp($Line, "\@F=(.+?),", $STR_REGEXPARRAYMATCH) + $CurrentFile = FileOpen($RegexResult[0], $FO_READ) + + $RegexResult = StringRegExp($Line, "\@C=(.+?),", $STR_REGEXPARRAYMATCH) + $DataPos = $RegexResult[0] + + ; get the comment block itself + $Results = ReadNextBlock($CurrentFile, $DataPos) + $Block = $Results[1] + $Declaration = $Results[2] + + + ; choose the right function to write mardown depending on the type of comment block + $RegexResult = StringRegExp($Line, "\@K=(.+?),", $STR_REGEXPARRAYMATCH) + + If $RegexResult[0] == "module" Then + ConsoleWrite(".") + ; We need the name of the folder containing this particular source file + $RegexResult = StringRegExp($Line, "\@F=(.+?),", $STR_REGEXPARRAYMATCH) + $RegexResult = StringRegExp($RegexResult[0], "\\(.*)\\.*\.lua", $STR_REGEXPARRAYMATCH) + If @error Then + $CurrentFolder = "" + Else + $CurrentFolder = $RegexResult[0] + Endif + + ; Now we can write the markdown for this module + $CurrentOutput = WriteModule($Block, $CurrentFolder) + EndIf + + If $RegexResult[0] == "type" Then + ; We need the name of the Module containing the type + $RegexResult = StringRegExp($Line, "\@M=(.+?),", $STR_REGEXPARRAYMATCH) + + ; Now we can write the markdown for this type + WriteType($Block, $RegexResult[0], $CurrentOutput) + EndIf + + If $RegexResult[0] == "function" Then + ; We can write the markdown for this function + WriteFunction($Block, $Declaration, $CurrentOutput) + EndIf + + FileClose($CurrentFile) +Wend +ConsoleWrite("Done"&@CRLF) + + +; Step 4 ! +; ----------------------------------------------------------------- +ConsoleWrite("4. Processing Hyperlinks...") +FileWrite($Log, @CRLF&@CRLF&@TAB&"INFO : Processing Hyperlinks" & @CRLF) +Local $i=1 +Local $TempFilesArray = _FileListToArray(@ScriptDir & "/TEMP") +Local $CurrentFile +Local $FinalFile +While $i <= $TempFilesArray[0] ; For each markdown file in the temporary folder + + ;read the file + $CurrentFile = FileOpen(@ScriptDir & "/TEMP/" & $TempFilesArray[$i], 0) + ; The magic happens in Parser.au3 + $FinalString = ReplaceHyperlinks($CurrentFile) + + ; copy the result to the final file location + $FinalFile = FileOpen($OutputFolder & "/includes/" & $TempFilesArray[$i], 2) + FileWrite($FinalFile, $FinalString) + + FileClose($FinalFile) + FileClose($CurrentFile) + $i += 1 +WEnd +ConsoleWrite("Done"&@CRLF) + + +; Step 5 ! +; ----------------------------------------------------------------- +ConsoleWrite("5. Adding new documentation to index...") +FileWrite($Log, @CRLF&@CRLF&@TAB&"INFO : Adding new documentation to index" & @CRLF) + +; Now this is a bit annoying : there is no way to insert a line in a document. +; So we need to read the first half of it, read the second half, and the wipe the whole document +; This way, in the new doc, we can write the first half, what we wanted to insert, and then the second half ! + +; Let's store the index file in $IndexString +Local $IndexFile = $OutputFolder&"/index.html.md" +Local $IndexFileHandle = FileOpen($IndexFile, 0) +Local $IndexString = FileRead($IndexFileHandle) +$IndexString = StringRegExpReplace($IndexString, "-\h[A-Z][a-z]+\.[A-Z][a-z]+\s", "") + +; Now we slpit it into and store the results in $BeforeString and $AfterString +Local $SearchPos = StringInStr($IndexString, "search:") +local $BeforeString = StringTrimRight($IndexString, StringLen($IndexString) - $SearchPos + 5) +local $AfterString = StringTrimLeft($IndexString, $SearchPos - 1) + +; reopening the index file wiping everything +FileClose($IndexFileHandle) +$IndexFileHandle = FileOpen($IndexFile, 2) + +; write the first half +FileWrite($IndexFileHandle, $BeforeString) +Local $IncludePos = StringInStr($IndexString, "includes:") +FileSetPos($IndexFileHandle, $IncludePos + 10, $FILE_BEGIN) + +; add the new markdown files to the index +$i = 1 +While $i <= $TempFilesArray[0] + FileWrite($Log, StringTrimRight($TempFilesArray[$i], 3)&@CRLF) + + FileWrite($IndexFileHandle, " - "&StringTrimRight($TempFilesArray[$i], 3)&@CRLF) + $i+=1 +WEnd +FileWrite($IndexFileHandle, @CRLF) + +; append the second half of the file +FileWrite($IndexFileHandle, $AfterString) +FileClose($IndexFileHandle) +ConsoleWrite("Done"&@CRLF) + +; WE ARE DONE ! +ExitCleanly() diff --git a/Utils/Slate Documentation Generator/src/Writer.au3 b/Utils/Slate Documentation Generator/src/Writer.au3 new file mode 100644 index 000000000..b6a249540 --- /dev/null +++ b/Utils/Slate Documentation Generator/src/Writer.au3 @@ -0,0 +1,209 @@ +; Takes an array and returns it in a markdown flavored list +; If the list is a retun, then, there is no variable name... +Func ArrayToList($Array, $Return) + $String = "" + $i = 0 + do + $String &= "* " + $String &= $Array[$i] & " " + + If $Return Then + If $Array[$i + 2] == "" or $Array[$i + 2] == " " Then + $String &= @CRLF + Else + $String &= " " & $Array[$i + 1] & " " & $Array[$i + 2] & @CRLF + EndIf + Else + + $String &= $Array[$i + 1] + If $Array[$i + 2] == "" or $Array[$i + 2] == " " Then + $String &= @CRLF + Else + $String &= " : " & $Array[$i + 2] & @CRLF + EndIf + EndIf + $i += 3 + Until $i >= UBound($Array) + Return $String +EndFunc + + +Func WriteModule($Block, $Group) + Local $ModuleName = ParseForOneTag($Block, "@module") + DirCreate(@ScriptDir & "\TEMP") + Local $Output = FileOpen(@ScriptDir & "\TEMP\" & $Group & "." & $ModuleName & ".md", $FO_OVERWRITE) + Local $Data = "" + Local $DataPos = 1 + + FileWrite($Log, @CRLF&@TAB&"Writing "&$Group & "." & $ModuleName & ".md" &@CRLF) + FileWrite($Log, "Writing Module "&$ModuleName&@CRLF) + + ; Add title of Module + FileWrite($Output, "# " & $Group & "." & $ModuleName & " Module" & @CRLF) + + ; Copy the short description + While StringRight($Data, 1) <> @CRLF And StringRight($Data, 1) <> @CR + If StringRight($Data, 7) == "@module" Then ; If there is no comment in the module block + Return $Output + EndIf + $Data &= StringMid($Block, $DataPos, 1) + $DataPos += 1 + WEnd + $Data = StringTrimRight($Data, 1) + $Block = StringTrimLeft($Block, $DataPos) + FileWrite($Output, $Data & @CRLF) + + ; copy the long description + $DataPos = 1 + $Data = "" + $Omit = False + While StringRight($Data, 7) <> "@module" + $Data &= StringMid($Block, $DataPos, 1) + $DataPos += 1 + WEnd + $Data = StringTrimRight($Data, 8) + FileWrite($Output, $Data & @CRLF) + Return $Output +EndFunc + + +Func WriteType($Block, $ModuleName, $Output) + Local $TypeName = ParseForOneTag($Block, "@type") + Local $ParentClass = GetData($TypeName, "parent") + Local $Fields = ParseForTags($Block, "@field") + + FileWrite($Log, "Writing Type "&$TypeName&@CRLF) + + ; Add title of Type + FileWrite($Output, "## " & $TypeName & " Class" & @CRLF) + + ; Add hierearchy info if necessary. Some cool ASCII drawing is going on ! + If $ParentClass <> "ROOT" Then + FileWrite($Output, "
" & @CRLF)
+		FileWrite($Output, "Inheritance : The " & $TypeName & " Class inherits from the following parents :" & @CRLF)
+		Local $Hierarchy = GetParents($TypeName)
+		Local $String = ""
+		Local $TabBuffer = @TAB
+		$String &= $Hierarchy[0]&@CRLF
+		For $i=1 to UBound($Hierarchy)-1
+			$String &= $TabBuffer&"`-- "&$Hierarchy[$i]&@CRLF
+			$TabBuffer &= @TAB
+		Next
+		FileWrite($Output, $String)
+		FileWrite($Output, "
" & @CRLF) + Else + FileWrite($Output, "
" & @CRLF)
+		FileWrite($Output, "The " & $TypeName & " class does not inherit" & @CRLF)
+		FileWrite($Output, "
" & @CRLF) + EndIf + + ; Copy the long description + Local $DataPos = 1 + Local $Data = "" + Local $Omit = False + + While StringRight($Data, 1) <> @CR ; We discard the first line + $Data &= StringMid($Block, $DataPos, 1) + $DataPos += 1 + WEnd + ; If there is a tag in the first line, there is no description + if StringInStr($Data, "@type") == 0 and StringInStr($Data, "@extends") == 0 and StringInStr($Data, "@field") == 0 Then + $Data = "" + $DataPos += 1 + + While StringRight($Data, 5) <> "@type" + $Data &= StringMid($Block, $DataPos, 1) + $DataPos += 1 + WEnd + $Data = StringTrimRight($Data, 5) + FileWrite($Output, $Data & @CRLF) + EndIf + + ; Add the Attributes + If IsArray($Fields) Then + FileWrite($Output, "

Attributes

" & @CRLF & @CRLF) + FileWrite($Output, ArrayToList($Fields, False) & @CRLF) + EndIf + FileWrite($Output, @CRLF) + Return $TypeName +EndFunc + + + +Func WriteFunction($Block, $Declaration, $Output) + Local $RegexResult = ParseFunctionName($Block, $Declaration) + Local $FunctionName = $RegexResult[0] + Local $TypeName = $RegexResult[1] + Local $Parameters = ParseParams($Block, $Declaration) + Local $Returns = ParseForTags($Block, "@return") + Local $Usage = ParseForOneTag($Block, "@usage") + Local $RegexResult + + FileWrite($Log, "Writing Function "&$FunctionName&@CRLF) + + If StringLeft($FunctionName, 1) == "_" Then + _FileWriteLog($Log, @TAB&@Tab&"Function is private. Ignored." & @CRLF) + Return $FunctionName + EndIf + ; Add the class before the function name + If IsArray($Parameters) Then + If $Parameters[1] == "self" Then + $FunctionName = $TypeName & ":" & $FunctionName + EndIf + Else + $FunctionName = $TypeName & "." & $FunctionName + EndIf + + ; add the parameters in parenthesis + $FunctionName &= "(" + If IsArray($Parameters) Then + For $i = 3 To UBound($Parameters) - 3 Step 3 + $FunctionName &= $Parameters[$i + 1] & ", " + Next + If UBound($Parameters) > 3 Then + $FunctionName = StringTrimRight($FunctionName, 2) + EndIf + EndIf + $FunctionName &= ")" + + ;write the file name + FileWrite($Output, "### " & $FunctionName & @CRLF) + + ;Write the exemple if any + If $Usage <> "" Then + FileWrite($Output, "``` lua") + FileWrite($Output, $Usage) + FileWrite($Output, "```" & @CRLF) + EndIf + + ;Write the description + FileWrite($Log, $Block) + FileWrite($Log, StringTrimRight($Block, StringLen($Block) - StringInStr($Block, "@param") + 1) & @CRLF) + FileWrite($Output, StringTrimRight($Block, StringLen($Block) - StringInStr($Block, "@param") + 1) & @CRLF) + + ; Write the parameters + FileWrite($Output, "

Parameters

" & @CRLF) + If IsArray($Parameters) Then + FileWrite($Output, ArrayToList($Parameters, False) & @CRLF) + EndIf + + ; Write the returns + FileWrite($Output, "

Returns

" & @CRLF) + If IsArray($Returns) Then + FileWrite($Output, ArrayToList($Returns, True) & @CRLF) + EndIf + + FileWrite($Output, @CRLF) + + ; add to the list of function balises (useful for hyperlinks) + $RegexResult = ParseFunctionName($Block, $Declaration) + Local $URLBalise = $TypeName & "-" & $RegexResult[0] & "-" + If IsArray($Parameters) Then + For $i = 3 To UBound($Parameters) - 3 Step 3 + $URLBalise &= StringLower($Parameters[$i + 1]) & "-" + Next + EndIf + $URLBalise = StringTrimRight($URLBalise, 1) + FileWrite($FunctionList, $URLBalise & @CRLF) + return $FunctionName +EndFunc \ No newline at end of file diff --git a/docs/Documentation/Cargo.html b/docs/Documentation/Cargo.html index e2bcae7e2..a9b110ac2 100644 --- a/docs/Documentation/Cargo.html +++ b/docs/Documentation/Cargo.html @@ -2847,6 +2847,7 @@ The range till cargo will board.

+ CARGO_UNIT.CargoCarrier diff --git a/docs/Documentation/CommandCenter.html b/docs/Documentation/CommandCenter.html index 99e1f3291..7b2d84919 100644 --- a/docs/Documentation/CommandCenter.html +++ b/docs/Documentation/CommandCenter.html @@ -251,24 +251,60 @@ REPORT:Add(Text)

Add a new line to a REPORT.

+ + + + REPORT:AddIndent(Text) + +

Add a new line to a REPORT.

REPORT.ClassName + + + + REPORT:HasText() + +

Has the REPORT Text?

+ + + + REPORT.Indent + + REPORT:New(Title)

Create a new REPORT.

+ + + + REPORT.Report + + + + + + REPORT:SetIndent(Indent) + +

Set indent of a REPORT.

REPORT:Text(Delimiter)

Produces the text of the report, taking into account an optional delimeter, which is \n by default.

+ + + + REPORT.Title + + @@ -765,6 +801,32 @@ Group#GROUP

#REPORT:

+ +
+
+
+ + +REPORT:AddIndent(Text) + +
+
+ +

Add a new line to a REPORT.

+ +

Parameter

+
    +
  • + +

    #string Text :

    + +
  • +
+

Return value

+ +

#REPORT:

+ +
@@ -779,6 +841,38 @@ Group#GROUP

+ +
+
+
+ + +REPORT:HasText() + +
+
+ +

Has the REPORT Text?

+ +

Return value

+ +

#boolean:

+ + +
+
+
+
+ + + +REPORT.Indent + +
+
+ + +
@@ -805,6 +899,46 @@ Group#GROUP

#REPORT:

+ +
+
+
+ + + +REPORT.Report + +
+
+ + + +
+
+
+
+ + +REPORT:SetIndent(Indent) + +
+
+ +

Set indent of a REPORT.

+ +

Parameter

+
    +
  • + +

    #number Indent :

    + +
  • +
+

Return value

+ +

#REPORT:

+ +
@@ -832,6 +966,20 @@ Group#GROUP

#string: The report text.

+ +
+
+
+ + + +REPORT.Title + +
+
+ + +
diff --git a/docs/Documentation/Controllable.html b/docs/Documentation/Controllable.html index 5b025cdc7..ad27bec5c 100644 --- a/docs/Documentation/Controllable.html +++ b/docs/Documentation/Controllable.html @@ -382,7 +382,7 @@ This is different from the EnRoute tasks, where the targets of the task need to - CONTROLLABLE:IsTargetDetected(DCSObject) + CONTROLLABLE:IsTargetDetected(DCSObject, DetectVisual, DetectOptical, DetectRadar, DetectIRST, DetectRWR, DetectDLINK) @@ -1466,19 +1466,49 @@ WayPoints If WayPoints is given, then return the WayPoints structure.

-CONTROLLABLE:IsTargetDetected(DCSObject) +CONTROLLABLE:IsTargetDetected(DCSObject, DetectVisual, DetectOptical, DetectRadar, DetectIRST, DetectRWR, DetectDLINK)
-

Parameter

+

Parameters

  • DCSObject :

    +
  • +
  • + +

    DetectVisual :

    + +
  • +
  • + +

    DetectOptical :

    + +
  • +
  • + +

    DetectRadar :

    + +
  • +
  • + +

    DetectIRST :

    + +
  • +
  • + +

    DetectRWR :

    + +
  • +
  • + +

    DetectDLINK :

    +
diff --git a/docs/Documentation/Database.html b/docs/Documentation/Database.html index e43251af7..bdaa3f280 100644 --- a/docs/Documentation/Database.html +++ b/docs/Documentation/Database.html @@ -326,6 +326,12 @@ The following iterator methods are currently available within the DATABASE:

DATABASE:ForEachPlayerJoined(IteratorFunction, ...)

Iterate the DATABASE and call an iterator function for each player who has joined the mission, providing the Unit of the player and optional parameters.

+ + + + DATABASE:ForEachStatic(IteratorFunction, FinalizeFunction, ...) + +

Iterate the DATABASE and call an iterator function for each alive STATIC, providing the STATIC and optional parameters.

@@ -1279,6 +1285,43 @@ self

+ +DATABASE:ForEachStatic(IteratorFunction, FinalizeFunction, ...) + +
+
+ +

Iterate the DATABASE and call an iterator function for each alive STATIC, providing the STATIC and optional parameters.

+ +

Parameters

+
    +
  • + +

    #function IteratorFunction : +The function that will be called for each object in the database. The function needs to accept a STATIC parameter.

    + +
  • +
  • + +

    FinalizeFunction :

    + +
  • +
  • + +

    ... :

    + +
  • +
+

Return value

+ +

#DATABASE: +self

+ +
+
+
+
+ DATABASE:ForEachUnit(IteratorFunction, FinalizeFunction, ...) diff --git a/docs/Documentation/Designate.html b/docs/Documentation/Designate.html index 9854fc628..0b38068a2 100644 --- a/docs/Documentation/Designate.html +++ b/docs/Documentation/Designate.html @@ -279,7 +279,7 @@ each detected set of potential targets can be lased or smoked...

- DESIGNATE:MenuStatus(AttackGroup) + DESIGNATE:MenuStatus(AttackGroup, Duration) @@ -375,7 +375,7 @@ each detected set of potential targets can be lased or smoked...

- DESIGNATE:SendStatus(AttackGroup, MenuAttackGroup) + DESIGNATE:SendStatus(AttackGroup, Duration, MenuAttackGroup)

Sends the status to the Attack Groups.

@@ -1065,19 +1065,24 @@ function below will use the range 1-7 just in case

-DESIGNATE:MenuStatus(AttackGroup) +DESIGNATE:MenuStatus(AttackGroup, Duration)
-

Parameter

+

Parameters

  • AttackGroup :

    +
  • +
  • + +

    Duration :

    +
@@ -1556,7 +1561,7 @@ The Attack collection of GROUP objects to designate and report for.

-DESIGNATE:SendStatus(AttackGroup, MenuAttackGroup) +DESIGNATE:SendStatus(AttackGroup, Duration, MenuAttackGroup)
@@ -1572,6 +1577,12 @@ The Attack collection of GROUP objects to designate and report for.

  • +

    #number Duration : +The time in seconds the report should be visible.

    + +
  • +
  • +

    MenuAttackGroup :

  • diff --git a/docs/Documentation/Detection.html b/docs/Documentation/Detection.html index af236b70d..4514b29c5 100644 --- a/docs/Documentation/Detection.html +++ b/docs/Documentation/Detection.html @@ -192,6 +192,12 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu DETECTION_AREAS:CreateDetectionItems()

    Make a DetectionSet table.

    + + + + DETECTION_AREAS:DetectedItemMenu(Index) + +

    Menu of a detected item using a given numeric index.

    @@ -204,6 +210,12 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu DETECTION_AREAS.DetectedItems

    A list of areas containing the set of Units, Zones, the center Unit within the zone, and ID of each area that was detected within a DetectionZoneRange.

    + + + + DETECTION_AREAS:DetectedReportDetailed() + +

    Report detailed of a detection result.

    @@ -331,7 +343,7 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu - DETECTION_BASE:AddDetectedItem(DetectedItemIndex, Set) + DETECTION_BASE:AddDetectedItem(ItemPrefix, DetectedItemIndex, Set)

    Adds a new DetectedItem to the DetectedItems list.

    @@ -424,6 +436,12 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu DETECTION_BASE.DetectedItemMax + + + + DETECTION_BASE:DetectedItemMenu(Index) + +

    Menu of a detected item using a given numeric index.

    @@ -513,12 +531,24 @@ The different values of Unit.Category can be:

    Multiple Unit.Category entries can be given as a table and then these will be evaluated as an OR expression.

    + + + + DETECTION_BASE:GetDetectedID(Index) + +

    Get a detected ID using a given numeric index.

    DETECTION_BASE:GetDetectedItem(Index)

    Get a detected item using a given numeric index.

    + + + + DETECTION_BASE:GetDetectedItemID(Index) + +

    Get a detected ItemID using a given numeric index.

    @@ -905,6 +935,42 @@ The different values of Unit.Category can be:

    DETECTION_BASE.DetectedObject.Identified + + + + DETECTION_BASE.DetectedObject.IsVisible + + + + + + DETECTION_BASE.DetectedObject.KnowDistance + + + + + + DETECTION_BASE.DetectedObject.KnowType + + + + + + DETECTION_BASE.DetectedObject.LastPos + + + + + + DETECTION_BASE.DetectedObject.LastTime + + + + + + DETECTION_BASE.DetectedObject.LastVelocity + + @@ -917,12 +983,6 @@ The different values of Unit.Category can be:

    DETECTION_BASE.DetectedObject.Type - - - - DETECTION_BASE.DetectedObject.Visible - - @@ -939,6 +999,12 @@ The different values of Unit.Category can be:

    DETECTION_TYPES:CreateDetectionItems()

    Create the DetectedItems list from the DetectedObjects table.

    + + + + DETECTION_TYPES:DetectedItemMenu(Index, DetectedTypeName) + +

    Menu of a DetectedItem using a given numeric index.

    @@ -1015,6 +1081,12 @@ The different values of Unit.Category can be:

    DETECTION_UNITS:CreateDetectionItems()

    Create the DetectedItems list from the DetectedObjects table.

    + + + + DETECTION_UNITS:DetectedItemMenu(Index) + +

    Menu of a DetectedItem using a given numeric index.

    @@ -1258,7 +1330,7 @@ Use the method Detecti
     local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers.
     
      -- Build a detect object.
    - local Detection = DETECTION_BASE:New( SetGroup )
    + local Detection = DETECTION_UNITS:New( SetGroup )
     
      -- This will accept detected units if the range is below 5000 meters.
      Detection:SetAcceptRange( 5000 ) 
    @@ -1280,7 +1352,7 @@ Use the method Detecti
      local ZoneAccept2 = ZONE:New( "AcceptZone2" )
     
      -- Build a detect object.
    - local Detection = DETECTION_BASE:New( SetGroup )
    + local Detection = DETECTION_UNITS:New( SetGroup )
     
      -- This will accept detected units by Detection when the unit is within ZoneAccept1 OR ZoneAccept2.
      Detection:SetAcceptZones( { ZoneAccept1, ZoneAccept2 } ) 
    @@ -1302,7 +1374,7 @@ An example of how to use the method is shown below.

    local ZoneReject2 = ZONE:New( "RejectZone2" ) -- Build a detect object. - local Detection = DETECTION_BASE:New( SetGroup ) + local Detection = DETECTION_UNITS:New( SetGroup ) -- This will reject detected units by Detection when the unit is within ZoneReject1 OR ZoneReject2. Detection:SetRejectZones( { ZoneReject1, ZoneReject2 } ) @@ -1476,6 +1548,32 @@ self

    #DETECTION_AREAS: self

    +
    +
    +
    +
    + + +DETECTION_AREAS:DetectedItemMenu(Index) + +
    +
    + +

    Menu of a detected item using a given numeric index.

    + +

    Parameter

    +
      +
    • + +

      Index :

      + +
    • +
    +

    Return value

    + +

    #string:

    + +
    @@ -1516,6 +1614,24 @@ self

    A list of areas containing the set of Units, Zones, the center Unit within the zone, and ID of each area that was detected within a DetectionZoneRange.

    + +
    +
    +
    + + +DETECTION_AREAS:DetectedReportDetailed() + +
    +
    + +

    Report detailed of a detection result.

    + +

    Return value

    + +

    #string:

    + +
    @@ -1944,7 +2060,7 @@ self

    -DETECTION_BASE:AddDetectedItem(DetectedItemIndex, Set) +DETECTION_BASE:AddDetectedItem(ItemPrefix, DetectedItemIndex, Set)
    @@ -1958,6 +2074,11 @@ self

    • +

      ItemPrefix :

      + +
    • +
    • +

      #string DetectedItemIndex : The index of the DetectedItem.

      @@ -2201,6 +2322,7 @@ The index of the DetectedItem.

      + #number DETECTION_BASE.DetectedItemCount @@ -2223,6 +2345,32 @@ The index of the DetectedItem.

      +
    +
    +
    +
    + + +DETECTION_BASE:DetectedItemMenu(Index) + +
    +
    + +

    Menu of a detected item using a given numeric index.

    + +

    Parameter

    +
      +
    • + +

      Index :

      + +
    • +
    +

    Return value

    + +

    #string:

    + +
    @@ -2328,7 +2476,7 @@ The index of the DetectedItem.

    - #number + DETECTION_BASE.DetectionInterval @@ -2468,6 +2616,32 @@ self

    + +DETECTION_BASE:GetDetectedID(Index) + +
    +
    + +

    Get a detected ID using a given numeric index.

    + +

    Parameter

    +
      +
    • + +

      #number Index :

      + +
    • +
    +

    Return value

    + +

    #string: +DetectedItemID

    + +
    +
    +
    +
    + DETECTION_BASE:GetDetectedItem(Index) @@ -2489,6 +2663,32 @@ self

    #DETECTION_BASE.DetectedItem:

    + +
    +
    +
    + + +DETECTION_BASE:GetDetectedItemID(Index) + +
    +
    + +

    Get a detected ItemID using a given numeric index.

    + +

    Parameter

    +
      +
    • + +

      #number Index :

      + +
    • +
    +

    Return value

    + +

    #string: +DetectedItemID

    +
    @@ -2523,7 +2723,7 @@ self

    Return value

    #number: -Count

    +The amount of detected items. Note that the amount of detected items can differ with the reality, because detections are not real-time but doen in intervals!

    @@ -4061,6 +4261,90 @@ The To State string.

    + +
    +
    +
    + + #boolean + +DETECTION_BASE.DetectedObject.IsVisible + +
    +
    + + + +
    +
    +
    +
    + + #boolean + +DETECTION_BASE.DetectedObject.KnowDistance + +
    +
    + + + +
    +
    +
    +
    + + #boolean + +DETECTION_BASE.DetectedObject.KnowType + +
    +
    + + + +
    +
    +
    +
    + + #boolean + +DETECTION_BASE.DetectedObject.LastPos + +
    +
    + + + +
    +
    +
    +
    + + #number + +DETECTION_BASE.DetectedObject.LastTime + +
    +
    + + + +
    +
    +
    +
    + + #number + +DETECTION_BASE.DetectedObject.LastVelocity + +
    +
    + + +
    @@ -4089,20 +4373,6 @@ The To State string.

    - -
    -
    -
    - - #boolean - -DETECTION_BASE.DetectedObject.Visible - -
    -
    - - -
    @@ -4154,6 +4424,37 @@ Beware that when the amount of different types detected is large, the DetectedIt

    #DETECTION_TYPES: self

    + +
    +
    +
    + + +DETECTION_TYPES:DetectedItemMenu(Index, DetectedTypeName) + +
    +
    + +

    Menu of a DetectedItem using a given numeric index.

    + +

    Parameters

    +
      +
    • + +

      Index :

      + +
    • +
    • + +

      DetectedTypeName :

      + +
    • +
    +

    Return value

    + +

    #string:

    + +
    @@ -4389,6 +4690,32 @@ Beware that when the amount of units detected is large, the DetectedItems list w

    #DETECTION_UNITS: self

    + +
    +
    +
    + + +DETECTION_UNITS:DetectedItemMenu(Index) + +
    +
    + +

    Menu of a DetectedItem using a given numeric index.

    + +

    Parameter

    +
      +
    • + +

      Index :

      + +
    • +
    +

    Return value

    + +

    #string:

    + +
    diff --git a/docs/Documentation/Mission.html b/docs/Documentation/Mission.html index 5cf8ca5d8..96f09fc56 100644 --- a/docs/Documentation/Mission.html +++ b/docs/Documentation/Mission.html @@ -167,7 +167,7 @@ A CLIENT needs to be registered within the MISSION:GetMenu(TaskGroup) + MISSION:GetMenu()

    Gets the mission menu for the coalition.

    @@ -200,6 +200,12 @@ A CLIENT needs to be registered within the MISSION:GetTasks()

    Get all the TASKs from the Mission.

    + + + + MISSION:GetTasksRemaining() + + @@ -209,39 +215,51 @@ A CLIENT needs to be registered within the MISSION:IsCompleted() + MISSION:IsCOMPLETED() -

    Is the Mission Completed.

    +

    Is the Mission COMPLETED.

    - MISSION:IsFailed() + MISSION:IsENGAGED() -

    Is the Mission Failed.

    +

    Is the Mission ENGAGED.

    - MISSION:IsHold() + MISSION:IsFAILED() -

    Is the Mission Hold.

    +

    Is the Mission FAILED.

    - MISSION:IsIdle() + MISSION:IsHOLD() -

    Is the Mission Idle.

    +

    Is the Mission HOLD.

    - MISSION:IsOngoing() + MISSION:IsIDLE() -

    Is the Mission Ongoing.

    +

    Is the Mission IDLE.

    MISSION:JoinUnit(PlayerUnit, PlayerGroup)

    Add a Unit to join the Mission.

    + + + + MISSION:MenuReportOverview(TaskStatus, ReportGroup) + + + + + + MISSION:MenuReportSummary(ReportGroup) + + @@ -323,51 +341,51 @@ A CLIENT needs to be registered within the MISSION:OnEnterCompleted(From, Event, To) + MISSION:OnEnterCOMPLETED(From, Event, To) -

    OnEnter Transition Handler for State Completed.

    +

    OnEnter Transition Handler for State COMPLETED.

    - MISSION:OnEnterFailed(From, Event, To) + MISSION:OnEnterENGAGED(From, Event, To) -

    OnEnter Transition Handler for State Failed.

    +

    OnEnter Transition Handler for State ENGAGED.

    - MISSION:OnEnterIdle(From, Event, To) + MISSION:OnEnterFAILED(From, Event, To) -

    OnEnter Transition Handler for State Idle.

    +

    OnEnter Transition Handler for State FAILED.

    - MISSION:OnEnterOngoing(From, Event, To) + MISSION:OnEnterIDLE(From, Event, To) -

    OnEnter Transition Handler for State Ongoing.

    +

    OnEnter Transition Handler for State IDLE.

    - MISSION:OnLeaveCompleted(From, Event, To) + MISSION:OnLeaveCOMPLETED(From, Event, To) -

    OnLeave Transition Handler for State Completed.

    +

    OnLeave Transition Handler for State COMPLETED.

    - MISSION:OnLeaveFailed(From, Event, To) + MISSION:OnLeaveENGAGED(From, Event, To) -

    OnLeave Transition Handler for State Failed.

    +

    OnLeave Transition Handler for State ENGAGED.

    - MISSION:OnLeaveIdle(From, Event, To) + MISSION:OnLeaveFAILED(From, Event, To) -

    OnLeave Transition Handler for State Idle.

    +

    OnLeave Transition Handler for State FAILED.

    - MISSION:OnLeaveOngoing(From, Event, To) + MISSION:OnLeaveIDLE(From, Event, To) -

    OnLeave Transition Handler for State Ongoing.

    +

    OnLeave Transition Handler for State IDLE.

    @@ -395,7 +413,7 @@ A CLIENT needs to be registered within the MISSION:ReportOverview() + MISSION:ReportOverview(TaskStatus)

    Create a overview report of the Mission (multiple lines).

    @@ -467,7 +485,7 @@ A CLIENT needs to be registered within the MISSION:onenterCompleted(From, Event, To) + MISSION:onenterCOMPLETED(From, Event, To) @@ -697,21 +715,13 @@ true if Unit is part of a Task in the Mission.

    -MISSION:GetMenu(TaskGroup) +MISSION:GetMenu()

    Gets the mission menu for the coalition.

    -

    Parameter

    -

    Return value

    Core.Menu#MENU_COALITION: @@ -851,6 +861,24 @@ Returns nil if no task was found.

    Tasks = Mission:GetTasks() env.info( "Task 2 Completion = " .. Tasks[2]:GetGoalPercentage() .. "%" )
    +
    +
    +
    +
    + + +MISSION:GetTasksRemaining() + +
    +
    + + + +

    Return value

    + +

    #number:

    + +
    @@ -893,13 +921,13 @@ true if the Mission has a Group.

    - -MISSION:IsCompleted() + +MISSION:IsCOMPLETED()
    -

    Is the Mission Completed.

    +

    Is the Mission COMPLETED.

    Return value

    @@ -911,13 +939,13 @@ true if the Mission has a Group.

    - -MISSION:IsFailed() + +MISSION:IsENGAGED()
    -

    Is the Mission Failed.

    +

    Is the Mission ENGAGED.

    Return value

    @@ -929,13 +957,13 @@ true if the Mission has a Group.

    - -MISSION:IsHold() + +MISSION:IsFAILED()
    -

    Is the Mission Hold.

    +

    Is the Mission FAILED.

    Return value

    @@ -947,13 +975,13 @@ true if the Mission has a Group.

    - -MISSION:IsIdle() + +MISSION:IsHOLD()
    -

    Is the Mission Idle.

    +

    Is the Mission HOLD.

    Return value

    @@ -965,13 +993,13 @@ true if the Mission has a Group.

    - -MISSION:IsOngoing() + +MISSION:IsIDLE()
    -

    Is the Mission Ongoing.

    +

    Is the Mission IDLE.

    Return value

    @@ -1016,6 +1044,54 @@ The GROUP of the player joining the Mission.

    #boolean: true if Unit is part of a Task in the Mission.

    +
    +
    +
    +
    + + +MISSION:MenuReportOverview(TaskStatus, ReportGroup) + +
    +
    + + + +

    Parameters

    + +
    +
    +
    +
    + + +MISSION:MenuReportSummary(ReportGroup) + +
    +
    + + + +

    Parameter

    +
    @@ -1422,13 +1498,13 @@ Return false to cancel Transition.

    - -MISSION:OnEnterCompleted(From, Event, To) + +MISSION:OnEnterCOMPLETED(From, Event, To)
    -

    OnEnter Transition Handler for State Completed.

    +

    OnEnter Transition Handler for State COMPLETED.

    Parameters